Jump to content

DLLCall and Arrays question


JRowe
 Share

Recommended Posts

To get an array from a dllcall, you need to create the array first from dllStructCreate, like so

$myArray = dllStructCreate("int[10]")

Then pass the array struct by pointer in the dllCall

$dllCall = DLLCall($dll, "return type", "_some_function_name", "array type", DLLStructGetPtr($myArray))

Now the array is filled with whatever DLLCall returned, giving you the opportunity to get the values of the array by using DLLStructGetData.

I'm running a For/Next loop, filling in the values of an AutoIt array. I'm getting the datatypes straight from the source of the DLL I'm using, and everything is working with no errors.

I just want to make sure that this isn't going to blow up someone'scomputer when I release a UDF, lol. I also wanted to write it out tomake it more concrete in my own mind.

Is this the correct way to interact with arrays when using DLLCall? Thanks for your time.

Edited by JRowe
Link to comment
Share on other sites

What I'm doing is interacting with an array that the dll/c code generates. The dll function creates an array, and I access it by creating a DllStruct with the appropriate parameters and then using DLLStructGetData and DLLStructSetData to access the elements of the array. I've successfully interacted with a 3 dimensional array created by a DLLCall.

Specifically, the function I wrote returns a 3 dimensional array generated by a dll:

Func _ANNGetConnectionArray($dll, ByRef $hAnn)
    $numConnections = _ANNGetTotalConnections($dll, $hAnn)
    $ConnectionArrayStruct = DllStructCreate("int[" & $numConnections & "];" & "int[" & $numConnections & "];" & "int[" & $numConnections & "]")
    $dllCall = DllCall($dll, "none", "_fann_get_connection_array@8", "hwnd", $hAnn, "int", DllStructGetPtr($ConnectionArrayStruct))
    Local $ConnectionArray[$numConnections][3]
    For $i = 1 to $numConnections Step 1
        $ConnectionArray[$i-1][0] = DllStructGetData($ConnectionArrayStruct, 1, $i)
        $ConnectionArray[$i-1][1] = DllStructGetData($ConnectionArrayStruct, 2, $i)
        $ConnectionArray[$i-1][2] = DllStructGetData($ConnectionArrayStruct, 3, $i)
    Next
    Return $ConnectionArray
EndFunc

Took me awhile to get there, but @error is showing clean, the function is returning the expected data, and there's no crashing going on, so I'm gonna assume all is well. :)

These last few weeks have been very enlightening for me, I'm finally grasping some major concepts. The next fun thing is inverting the function I just posted, to create a 3D array from an AutoIt array and pass it to the dll as a parameter. Once I get past this stage, I think I'm going to start being a helluva lot more productive in my contributions to the community.

Thanks, trancexx, I appreciate the input!

Edited by JRowe
Link to comment
Share on other sites

Link to comment
Share on other sites

And, I'm back for more.

I have a problem, I'm not sure how to go about this. How do I recreate this struct using DLLStructCreate?

struct fann_train_data
{
    enum fann_errno_enum errno_f;
    FILE *error_log;
    char *errstr;

    unsigned int num_data;
    unsigned int num_input;
    unsigned int num_output;
    fann_type **input;
    fann_type **output;
};

The enum_fann_errno is just an int. Do I need to create the file struct and pass it to this struct by ptr?

#ifndef _FILE_DEFINED
struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif

The char *errstr I can deal with, it's just a pointer to an array of characters. The **input and **outputs I can generate as an array of pointers to arrays. The only thing I'm stuck on is how to deal with the FILE *error_log

Sorry, forgot my actual AutoIt code:

;Initialize data struct to pass to _fann_train_on_data
    $dataStruct = DllStructCreate("int;ptr;ptr;int;int;int;ptr[" & $numSets & "];ptr[" & $numSets & "]")
    
    DllStructSetData($dataStruct, 1, 0)
    DllStructSetData($dataStruct, 2, 0)
    DllStructSetData($dataStruct, 3, 0)
    DllStructSetData($dataStruct, 4, $numSets)
    DllStructSetData($dataStruct, 5, $numInputs)
    DllStructSetData($dataStruct, 6, $numInputs)

Since I can get everything to work by just passing a file to a different function, I might skip this and make some AutoIt based array/file functions that mimic it's functionality.

Edited by JRowe
Link to comment
Share on other sites

I think that you can presume it's just a pointer to an object. Not exactly but quite like a pointer to a FONT or log font. What should be accurate is it's structure size + any padding necessary for alignment. Use sizeof() or calculate it yourself and round to the next 4 bytes multiplication. 12 -> 16, etc. Not sure though.

Link to comment
Share on other sites

Well, I'm at it again, I think I'm close, but this code is giving me fits. It hard crashes on the dllCall:

$inputArrayStruct = DllStructCreate("ptr[4]")
$inputArrayStruct1 = DllStructCreate("float[2]")
$inputArrayStruct2 = DllStructCreate("float[2]")
$inputArrayStruct3 = DllStructCreate("float[2]")
$inputArrayStruct4 = DllStructCreate("float[2]")

$outputArrayStruct = DllStructCreate("ptr[4]")
$outputArrayStruct1 = DllStructCreate("float")
$outputArrayStruct2 = DllStructCreate("float")
$outputArrayStruct3 = DllStructCreate("float")
$outputArrayStruct4 = DllStructCreate("float")


DllStructSetData($inputArrayStruct1, 1, -1, 1)
DllStructSetData($inputArrayStruct1, 1, -1, 2)
DllStructSetData($inputArrayStruct2, 1, -1, 1)
DllStructSetData($inputArrayStruct2, 1, 1, 2)
DllStructSetData($inputArrayStruct3, 1, 1, 1)
DllStructSetData($inputArrayStruct3, 1, -1, 2)
DllStructSetData($inputArrayStruct4, 1, 1, 1)
DllStructSetData($inputArrayStruct4, 1, 1, 2)
DllStructSetData($outputArrayStruct1, 1, -1)
DllStructSetData($outputArrayStruct2, 1, 1)
DllStructSetData($outputArrayStruct3, 1, 1)
DllStructSetData($outputArrayStruct4, 1, -1)


DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct1), 1)
DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct2), 2)
DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct3), 3)
DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct4), 4)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct1), 1)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct2), 2)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct3), 3)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct4), 4)

_InitializeANN()
$Ann = _CreateAnn($num_layers, $ANNLayers)
_ANNSetActivationFunctionHidden($Ann, $FANN_SIGMOID_SYMMETRIC)
_ANNSetActivationFunctionOutput($Ann, $FANN_SIGMOID_SYMMETRIC)

$trainDataStruct = DllStructCreate("uint num_data;uint num_input;uint num_output;ptr inputs;ptr outputs")

DllStructSetData($trainDataStruct, "num_data", 4)
DllStructSetData($trainDataStruct, "num_input", 2)
DllStructSetData($trainDataStruct, "num_output", 1)

DllStructSetData($trainDataStruct, "inputs", DllStructGetPtr($inputArrayStruct))

DllStructSetData($trainDataStruct, "outputs", DllStructGetPtr($outputArrayStruct))

DllCall($hFannDll, "none", "_fann_train_on_data@20", "hwnd", $Ann, _
            "ptr", DllStructGetPtr($trainDataStruct), _
            "dword", $max_epochs, _
            "dword", $epochs_between_reports, _
            "float", $desired_error)

The pieces are all here, but I can't seem to figure out how to put them together. Any help at all is greatly appreciated, if more is needed from me, please let me know.

Context of what I'm trying to do:

The function requires a "data" struct. The "data" struct is put together as shown above in the c code. I'm choking on **input and **output. I'm reading them as pointers to arrays of data, and attempting to build those arrays using dllStructSetData. I'm convinced I'm almost there, but I can't see how it falls together.

Edited by JRowe
Link to comment
Share on other sites

Fixed the naming issue, I think. Sorry about that.

$inputArrayStruct = DllStructCreate("ptr[4]")
$inputArrayStruct1 = DllStructCreate("float[2]")
$inputArrayStruct2 = DllStructCreate("float[2]")
$inputArrayStruct3 = DllStructCreate("float[2]")
$inputArrayStruct4 = DllStructCreate("float[2]")

$outputArrayStruct = DllStructCreate("ptr[4]")
$outputArrayStruct1 = DllStructCreate("float")
$outputArrayStruct2 = DllStructCreate("float")
$outputArrayStruct3 = DllStructCreate("float")
$outputArrayStruct4 = DllStructCreate("float")


DllStructSetData($inputArrayStruct1, 1, -1, 1)
DllStructSetData($inputArrayStruct1, 1, -1, 2)
DllStructSetData($inputArrayStruct2, 1, -1, 1)
DllStructSetData($inputArrayStruct2, 1, 1, 2)
DllStructSetData($inputArrayStruct3, 1, 1, 1)
DllStructSetData($inputArrayStruct3, 1, -1, 2)
DllStructSetData($inputArrayStruct4, 1, 1, 1)
DllStructSetData($inputArrayStruct4, 1, 1, 2)
DllStructSetData($outputArrayStruct1, 1, -1)
DllStructSetData($outputArrayStruct2, 1, 1)
DllStructSetData($outputArrayStruct3, 1, 1)
DllStructSetData($outputArrayStruct4, 1, -1)


DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct1), 1)
DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct2), 2)
DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct3), 3)
DllStructSetData($inputArrayStruct, 1, DllStructGetPtr($inputArrayStruct4), 4)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct1), 1)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct2), 2)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct3), 3)
DllStructSetData($outputArrayStruct, 1, DllStructGetPtr($outputArrayStruct4), 4)

_InitializeANN()
$Ann = _CreateAnn($num_layers, $ANNLayers)
_ANNSetActivationFunctionHidden($Ann, $FANN_SIGMOID_SYMMETRIC)
_ANNSetActivationFunctionOutput($Ann, $FANN_SIGMOID_SYMMETRIC)

$trainDataStruct = DllStructCreate("uint numdata;uint numinput;uint numoutput;ptr inputs;ptr outputs")

DllStructSetData($trainDataStruct, "numdata", 4)
DllStructSetData($trainDataStruct, "numinput", 2)
DllStructSetData($trainDataStruct, "numoutput", 1)

DllStructSetData($trainDataStruct, "inputs", DllStructGetPtr($inputArrayStruct))

DllStructSetData($trainDataStruct, "outputs", DllStructGetPtr($outputArrayStruct))

DllCall($hFannDll, "none", "_fann_train_on_data@20", "hwnd", $Ann, _
            "ptr", DllStructGetPtr($trainDataStruct), _
            "dword", $max_epochs, _
            "dword", $epochs_between_reports, _
            "float", $desired_error)

numdata = number of data sets

numinput = number of input values in each data set

numoutput = number of output values in each data set

$inputStruct represents a 2d array of values, like so:

inputStruct[0][0] = -1
inputStruct[0][1] = -1

inputStruct[1][0] = -1
inputStruct[1][1] = 1

inputStruct[2][0] = 1
inputStruct[2][1] = -1

inputStruct[3][0] = 1
inputStruct[3][1] = 1

$outputStruct is a 1d array, like so:

outputStruct[0] = -1
outputStruct[1] = 1
outputStruct[2] = 1
outputStruct[3] = -1

I'm pretty sure that my major issue is coming from correctly presenting the input struct to dllCall, or "ptr inputs" in the DLLStructCreate. It should be a 2d array of floats.

Edited by JRowe
Link to comment
Share on other sites

omg, it's correct. After I cleaned up the underscores and added the correct struct parameters it worked.

THANK YOU TRANCEXX!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...