ZBrushCentral

Question: FileExecute Args and Ouput values

It seems as though many of the args and the return value don’t work for this function yet. Can someone verify if this is true. Here are what I have working so far.
Arguments:
1. FileExecute (OK)
2. File Name (OK)
3. Routine Name (OK)
4. Optional text input (OK)
5. Optional Numeric input ( Not Successfull )
6. Optional MemoryBlock input ( Not Successfull )
7. Optional MemoryBlock ouput ( Not Successfull )

It also states that the Output value of this function is: >>>>>Output: The result value defined by the specified file/routine. ( Not Successfull )

I have already got some amazing results with windows and zbrush, but this is hindering the progress of some of my scripts…Please help inform me on this…

(I have tried recieving the data to my dll as pointers and as variables - nothing is working except the text input) Thanks, Chris Reid

The lack of infos (many infos :cry: ) is the worst thing…i don’t develop zscript for some time. I don’t work on reinventing the wheel.
Best luck to all.

cameyo

hi Chris

see my repy to your previous post.

we await the sdk.

aminuts:
Thanks for the reply. I too then await the sdk.

But for now ... It always seems like the less things work, the more creative you must be. I am feeling very :idea: (hope I didn't use it all up on that Icon) I will take your advice and talk to support. Thanks again, Chris Reid

Yeah, there are things that just don’t work (or the info needed to make them work is missing). dlls are beyond my capabilities at the moment so I’ve no suggestions. I hope your inspiration finds some workarounds though and that you share some of the results… Good luck! :+1:

I hope your inspiration finds some workarounds though and that you share some of the results… Good luck! :+1:
Well to share some so far.
The text input works great. So you can always [StrMerge] your strings together like so:
“OpenWindow, 500, 250, SHOW, …” Then you just have your dll function parse the file. And then use atoi(), atol(), etc… to extract the data you need.
I also tried using different multiple [FileExecute]'s to call different dll funcs
Like so:
[FileExecute,“WinLink.dll”,SizeWindowHorz, “250”]
[FileExecute,“WinLink.dll”,SizeWindowVert, “500”]
[FileExecute,“WinLink.dll”,ShowWindow, “Show”]
[FileExecute,“WinLink.dll”,OpenWindow, “true”]

Each will set up the data for the OpenWindow Function to do what it needs. I often prefer the 1st way just because different scripting languages do different things, and Some that I have used will go the the next line even before the previous returns…:red_circle:So Be Carefull:red_circle: (not with zscripts…yet:D)

Also since I couldn’t access memoryBlocks I wrote data to files and got it from there, but this is always dangerous for the same reasons as above. You need to do lots of checks.

One other thing I mentioned in another post is that if you need to call a specific function from a System dll that takes specific data arguments, Lets say a windows handle . You cannot get this with zscript. So you need to pass the values you can(again I suggest my first method) to your dll function. And then have your Dll do the things your script cannot, and then Call to the System dll.

I hope this helps someone or proves usefull on some planet out there…there…there…ther…the…th…t

Chris Reid (C8

It also states that the Output value of this function is: >>>>>Output: The result value defined by the specified file/routine. ( Not Successfull )

OK ( Successfull ) (not a return value, but an argument pointer adjustment) Here is one way I have found so far:

  1. Define a Var. in your ZScript. This is the variable you will pass in your [FileExecute…] call. As the Input Text Argument.
    [VarDef, StrPass, “Arg_1 ,Arg_2 ,Arg_3 …”]
    then call this in your script:
    [FileExecute, DllName, func, [Var, StrPass]]

    2. Change the The stringPointer in the dll. ( I havn't tested it yet, but You are probably limited by the size of the initial string passed: [StrLength, String to evaluate]

    in your dll it may look something like this…
    void func(char * str)
    {
    strcpy(str, “New Value”);
    }

    1. Then The Variable for StrPass will be whatever you changed the pointer into from the .dll function (my example would be “New Value”.
      Thought I tried this before, but I must be doing something different.
      Then hmmmm,hopefully memoryBlocks are just pointers too. This would be Grand…

Wheeww, glad that works. It is much better than writing to files. I will post a nice example in time…gotta get some work done now. :wink:

Hi Chris. Great to see you charging into the largely unexplored [FileExecute,…] command, and making progress.

I will probably repeat some things you already know, but just to make sure anybody reading starts on the same page I will explain some fundamentals of calling DLL functions. At least as how I understand them. Note that I know very little about windows programming and what I will show is simply what has worked for me. There is most likely a proper procedure on how to write and manage the DLL functions so do not take my word as gospel.

First of all, the main purpose of the [FileExecute,…] command is to call a function within a DLL specifically written for Zbrush. Although you can call various functions from DLLs already on your system the arguments of the [FileExecute,…] command cannot be extended or reordered as they are locked to the zscripting language. That means only DLLs written for Zbrush or DLLs with functions that fit the limited argument space, can be used.

In order to use DLL functions with more input arguments than [FileExecute,…] allows you must write your own DLL which in return calls your desired DLL function.

The [FileExecute,…] command itself is simple enough. You need to know the path where the DLL file is stored, what the name of the function is and the required input arguments.

[[color=Lime]FileExecute, Filename , Routine to call, Optional text input, Optional number input, Optional memory block input, Optional memory block output] Filename is simply the name of the DLL file including the .dll extension. For help on determining the path, read through the [controlling Subdirectory paths in ZScript](http://www.zbrushcentral.com/zbc/showthread.php?t=20494&page=1&pp=15) thread. Routine to call is the exact name of the DLL function. You must use the exact UPPER and lower case of the function name. 'MessageBoxA' , for example. [color=Blue][color=Navy]Optional text input Is passed to the function as a pointer to the first character in the line of text. If you are calling a DLL function that does not require a text argument do not give it one. Usually used for passing a path and/or filename, but as Chris showed you can use it to pass multiple arguments to a specially written DLL. Optional number input Is passed to the function as the value itself, not a pointer. Zscript variables are double precision real numbers (Floating points) by default. If you are calling a DLL function that does not require a number argument do not give it one. [color=Blue][color=Navy]Optional memory block input Is passed to the function as a pointer to the first byte. Is only used with DLL functions specifically written for Zbrush. Although it is labelled an input argument I found it is perfectly acceptable to write to the memory block address from the DLL function. [color=Blue][color=Navy]Optional memory block output Due to my lack of windows programming experience I cannot write to the memory block without crashing Zbrush. Maybe it is passed as a handle of a memory allocation? But as the memory block input apparently can serve a dual purpose it is not a big deal right now. Or maybe it is an output from zbrush only memory block. So to answer your initial question on how to use the Numerical and Memory Block input parameters. Assuming we have the following [FileExecute,...] command:
[[color=Lime]FileExecute, "example1.dll", TestFunctionG, "Hello Zbrush", 3, TV_TestMemblock]
your DLL function definition should read the following (compiler requirements aside):

DLLIMPORT void TestFunctionG ( unsigned char *message, double repeat, void *memblock)

Hope that helps. For others wanting to dig into DLL programming I can highly recommend the [BloodShed Dev-C++](http://www.bloodshed.net/) suite which includes the MinGW compiler system and uses GCC 3.4.2. MinGW is a collection of "Windows specific header files and import libraries combined with GNU toolsets that allow one to produce native Windows programs that do not rely on any 3rd-party C runtime DLLs.". Dev-C++ is [Free Software](http://www.gnu.org/philosophy/free-sw.html) distributed under the [GNU General Public License](http://www.gnu.org/licenses/gpl.txt).

W:grimacing:w Thanks,
Great Post

The thing that was setting me back is that the arguments, although <b>optional in the script</b>, are <b>*</b><b>NOT* optional in </b><b>the </b><b>dll</b>. If you just want a number value, then you still have to have the char* argument in the dll function. Just want this to be clear for future readers.

In the following example I only want a numeric value passed to my dll.
So the ZScript is missing the text parameter, but the DLL must have it in its argument list.

ZScript:
   [FileExecute,  "example1.dll",  TestFunctionG<b> ,</b> <b>,   3</b>]

[color=Navy]

</span></span></b> 
DLL:
   DLLIMPORT void TestFunctionG ( <b>unsigned char *message</b>, <b>double  repeat</b>)

:red_circle:small_orange_diamond:large_orange_diamond: This is Very important to have in the Docs … :large_orange_diamond: :red_circle:

</b>Hmmm... this said, I may know how to fix the mysterious memoryOutput.
Back to the dungeon.

Thanks, I forgot to mention the argument dependency between the [FileExecute,…] command and the DLL function. I had forgotten about it since I needed all the arguments I could get my hands on :slight_smile:

I just reread your previous post. You mean you can use the Text inputs memory address to change the contents from within the DLL? And it is reflected within the zscript? Interesting and very useful. Thanks.

I have left the Output Memory block argument alone. It is not really needed right now as you can modify the Input Memory block from within the DLL function. However I would be interested to hear what you work out. It might be reserved for the functionality the SDK will offer us, so don’t spend to much time on it.

Nope, my idea didn’t work.
I thought that if my dll func would return void*, that this pointer would be the OutputMemoryBlock. I couldn’t get this to work. I am pretty sure that it is not passed to the function though, As you always get a hard crash.

Can’t wait for the sdk.

Chris

For people that are using the Visual Studio compiler for the Windows platform. Here is how to create a DLL containing a function that can be called from a ZScript:

1) Create a solution.
2) Create a Win32 DLL project.
3) Create a new C++ file containing the entry point function that is going to be called from the ZScript. It should have the following declaration:

    #define DLLEXPORT __declspec(dllexport)
  
    extern "C" int DLLEXPORT FunctionName(unsigned char* message, double number, void* memblock, void* memblock2)
     {
   // function body
     }
    

The calling ZScript has the following FileExecute command. “MemBlock” refers to a memory block handle created earlier in the ZScript.


   [FileExecute, "DllName.dll", "FunctionName", "StringArgument", [MemGetSize, "MemBlock"], "MemBlock",]
    

For example, to read the first integer written into the memory block, you could just write the following code in the FunctionName body:


   int mbSize = (int)number; // the second argument have to be a double
   
   // Read from the memory block via a strstream
   std:small_orange_diamond:strstream mbStream((char*)memblock, mbSize);
   
   // Read the first integer written into the memory block by the ZScript
   int val = 0;
   mbStream.read((char*)&val, 4);
   
   

Thanks, that’s very useful.

Here is another method,
I have been using it for over a year now and no problems.

// Script code [MemCreate, ZSampleMemPass, 4 + 4 + 4 + 4 + 4 , 0]// I add them like this so I can keep track easier // ech 4 corresponds to the following memSpaces// you can easily add more here... //SampleVar_1-SampleVar_5 are precreated variable in the script. They should already have values assigned to them. // Write the variables to memory [MemWrite, ZSampleMemPass, SampleVar_1 ,5, 4*0] [MemWrite, ZSampleMemPass, SampleVar_2 ,5, 4*1] [MemWrite, ZSampleMemPass, SampleVar_3 ,5, 4*2] [MemWrite, ZSampleMemPass, SampleVar_4 ,5, 4*3] [MemWrite, ZSampleMemPass, SampleVar_5 ,0, 4*4] [VarDef, SampleMessageVar] [VarSet, SampleMessageVar, "Hello From ZBrush"] // Call the ZDllCall function in MyDll.dll [FileExecute, "MyDll.dll", ZDllCall,[Var,SampleMessageVar],,ZSampleMemPass] [NoteBar, SampleMessageVar]// this will show "Thanks ZBrush, Hello from MyDll" if you look at :ex: below // C/C++ code // Declare your var pointers // these long * var_1 = NULL; long * var_2 = NULL; long * var_3 = NULL; long * var_4 = NULL; long * var_5 = NULL; char *MessageOut = NULL; extern "C" __declspec(dllexport) void ZDllCall(char *msg, double num, void* data) {// in this example we don't even try to access the number... messageOut = msg;// ZBrush can get anything that you set MessageOut To. // :ex: ie: strcopy( messageOut, "Thanks ZBrush, Hello from MyDll"); // Now all of these variable will be able to access the data passed.

// to explain: cast ptr to char for ptr addition, then cast to data type
var_1 = (long*)data;
var_2 = (long*)((char*)data+4);
var_3 = (long*)((char*)data+8);
var_4 = (long*)((char*)data+12);
var_5 = (float*)((char*)data+16);

// to access the data just do something like this. long sum = *var_1 + *var_2; // its that easy. } // DLLMain Proc code.... // Also I would recommend setting all pointers to NULL if unloading dll if(Reason == DLL_PROCESS_DETACH) { // Free Global Pointers var_1 = NULL; var_2 = NULL; var_3 = NULL; var_4 = NULL; var_5 = NULL; } [color=black]Enjoy Chris Reid