Jump to content

Working with DllCall and DllStruct on a ZeroMQ UDF (Questions)


Recommended Posts

Posted (edited)

I am working on a ZeroMQ UDF and have gotten most of the AutoIt wrapper functions and constants finished.

But I am encountering some difficulties with a few of the ZMQ exported functions and would kindly ask if anyone can guide me on the best way to accomplish it in AutoIt.

1) Am I creating the right struct for zmq_msg_t ?
2) Is my zmq_free_fn usage in AutoIt similar to the my_free function in the ZMQ C++ example?

The usage of the exported ZMQ function zmq_msg_init_data in C++ is this:

typedef void (zmq_free_fn) (void *data, void *hint);

typedef struct {unsigned char _ [32];} zmq_msg_t;

void ffn (void *data_, void *hint_)
{
    // Signal that ffn has been called by writing "freed" to hint
    (void) data_; //  Suppress 'unused' warnings at compile time
    memcpy (hint_, (void *) "freed", 5);
}

void my_free (void *data, void *hint)
{
 free (data);
}

 /* ... */

void *data = malloc (6);
assert (data);
memcpy (data, "ABCDEF", 6);
zmq_msg_t msg;
rc = zmq_msg_init_data (&msg, data, 6, my_free, NULL); assert (rc == 0);

My AutoIt wrapper function for the DllCall is currently this:

Global Const $tag_zmq_msg_t = "struct;BYTE var1"
Global $tzmq_msg_t = DllStructCreate($tag_zmq_msg_t)    ; this step would actually use zmq_msg_init

Func zmq_msg_init_data($pMsg,$pData,$iSize,$pHint)
    Local $aDllCallReturn = DllCall("libzmq.dll","LONG","zmq_msg_init_data","STRUCT*",$pMsg,"PTR",$pData,
       "UINT",$iSize,"PTR",zmq_free_fn($pData,$pHint),"PTR",$pHint)
    Return $aDllCallReturn[0]
EndFunc

Func zmq_free_fn($pData=0,$pHint=0)
    $pData = 0        
EndFunc

Local $tzmq_msg = DllStructSetData($tzmq_msg_t, 1, "ABCDEF")
Local $tzmq_data = DllStructGetPtr($tzmq_msg)

Local $rc = zmq_msg_init_data($tzmq_msg,$tzmq_data,6,null)

The export of the C++ function call looks like this for zmq_msq_init_data:

int zmq_msg_init_data (zmq_msg_t *msg, void *data, size_t size, zmq_free_fn *ffn, void *hint);

And a description for the zmq_msg_init_data function is here: http://api.zeromq.org/master:zmq-msg-init-data

Also, there is some code in the ZeroMQ library that handles different OS arch during compilation of the libzmq.dll for the struct memory alignment:

typedef struct zmq_msg_t
{
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
    __declspec(align (8)) unsigned char _[64];
#elif defined(_MSC_VER)                                                        \
&& (defined(_M_IX86) || defined(_M_ARM_ARMV7VE) || defined(_M_ARM))
    __declspec(align (4)) unsigned char _[64];
#elif defined(__GNUC__) || defined(__INTEL_COMPILER)                           \
|| (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590)                              \
|| (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590)
    unsigned char _[64] __attribute__ ((aligned (sizeof (void *))));
#else
    unsigned char _[64];
#endif
} zmq_msg_t;

References:
http://api.zeromq.org/master:zmq-msg-init-data
https://travlr.github.io/libzmq/structzmq__msg__t.html
https://github.com/zeromq/libzmq/blob/2ac9755ee9df54e453958136bd17fee5819dbdba/include/zmq.h#L236
https://github.com/zeromq/libzmq/blob/3070a4b2461ec64129062907d915ed665d2ac126/src/msg.hpp#L46
https://github.com/zeromq/libzmq/blob/3070a4b2461ec64129062907d915ed665d2ac126/src/msg.hpp#L45
https://github.com/zeromq/libzmq/blob/3070a4b2461ec64129062907d915ed665d2ac126/src/zmq.cpp#L624

 

Edited by swoop
Link to post
Share on other sites
Posted (edited)

Since I don't have a copy of the DLL and can't run your script, can you say what happens when you run your script?

The first thing I notice is that you are using a stdcall calling convention.  Although I'm not sure because I haven't dug into the headers, but I'm pretty sure you should be using cdecl (for 32bit execution).  If it is 64bit, it's not necessary.

Edited by TheXman
Link to post
Share on other sites
40 minutes ago, TheXman said:

Since I don't have a copy of the DLL and can't run your script, can you say what happens when you run your script?

The first thing I notice is that you are using a stdcall calling convention.  Although I'm not sure because I haven't dug into the headers, but I'm pretty sure you should be using cdecl (for 32bit execution).  If it is 64bit, it's not necessary.

I'm not sure if my knowledge of AutoIt DllStruct and the C++ typedef is correct.

Is the following:

typedef struct {unsigned char _ [32];} zmq_msg_t;

equivalent to:

Global Const $tag_zmq_msg_t = "struct;BYTE var1"

When I run the AutoIt snippet, I get the following attached error. Error parsing function call.

The DLL can be found here in the latest release from ZeroMQhttps://github.com/zeromq/libzmq/releases

 

Error.png

Link to post
Share on other sites
Posted (edited)

The code you have posted is full of errors.  You need to read the basics of AutoIt in help file.  For that particular error, you cannot split statement between multiple lines without using _ at the end of a line.  For the tag, it is badly formatted.   And you need to add error handling so you know if the statements are successful or not.

Please take a moment to read and understand the statements from help file.

Edited by Nine
Link to post
Share on other sites
Posted (edited)
20 minutes ago, swoop said:

Global Const $tag_zmq_msg_t = "struct;BYTE var1"

Close, but I think it would look something like this:

Const $tag_zmq_msg_t = "struct; BYTE var1[32]; endstruct;"

Or just:

Const $tag_zmq_msg_t = "BYTE var1[32];"

since the struct/endstruct is implied in this case.

Edited by TheXman
Link to post
Share on other sites

@Nine You're right, I didn't check the line split. My bad.

You're also right about the error checking.

I tried to check if the zeromq dll call is not working for a simpler function using the DllCall generator to get the ZMQ version information, and I get back: "DllCall error (libzmq.dll/zmq_version): Unable to use the DLL file.  Possibly a problem with the parameters."

$aDllCallReturn = DllCall("libzmq-v142-mt-4_3_4.dll","ptr","zmq_version","LONG_PTR","$major","LONG_PTR","$minor","LONG_PTR","$patch")
If @error Then
   Switch @error
      Case 1
         MsgBox(0,"DllCall Code Generator","DllCall error (libzmq.dll/zmq_version): Unable to use the DLL file.  Possibly a problem with the parameters.")
      Case 2
         MsgBox(0,"DllCall Code Generator","DllCall error (libzmq.dll/zmq_version): Unknown return type.")
      Case 3
         MsgBox(0,"DllCall Code Generator","DllCall error (libzmq.dll/zmq_version): Function not found in DLL file.  Remember that function names are case sensitive.")
      Case 4
         MsgBox(0,"DllCall Code Generator","DllCall error (libzmq.dll/zmq_version): Incorrect number of parameters.")
      Case 5
         MsgBox(0,"DllCall Code Generator","DllCall error (libzmq.dll/zmq_version): Bad parameter.")
      Case Else
         MsgBox(0,"DllCall Code Generator","DllCall error (libzmq.dll/zmq_version): Unknown/unspecified error.")
   EndSwitch
   $vDllCallReturn = ""
Else
   $vDllCallReturn = $aDllCallReturn[0]
   $major = $aDllCallReturn[1]
   $minor = $aDllCallReturn[2]
   $patch = $aDllCallReturn[3]
   MsgBox(0,"DllCall Code Generator","$major = " & $major & @CRLF & "$minor = " & $minor & @CRLF & "$patch = " & $patch & @CRLF & "DllCall return value: " & $vDllCallReturn)
EndIf

http://api.zeromq.org/master:zmq-version

void zmq_version (int *major, int *minor, int *patch);

@TheXman Thank you for the correction.

Link to post
Share on other sites
Posted (edited)

As your link points out, the declaration of the function is:

void zmq_version (int *major, int *minor, int *patch);

 

The test script below works for me:

#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d

#include <Constants.au3>


zeromq_test()

Func zeromq_test()
    Local $aResult

    $aResult = DllCall("libzmq-v142-mt-4_3_4.dll", "none", "zmq_version", _
                        "int*", Null, _  ;Major
                        "int*", Null, _  ;Minor
                        "int*", Null)    ;Patch
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "DllCall Error - @error = " & @error)

    ConsoleWrite(StringFormat("zeroMQ Version = %s.%s.%s", $aResult[1], $aResult[2], $aResult[3]) & @CRLF)
EndFunc

Console Output:

zeroMQ Version = 4.3.4

 

Edited by TheXman
Link to post
Share on other sites

Perfect. Thanks a bunch @TheXman for the correction, and trying it out. Very much appreciated.

I will try to go through more of the functions, and make sure I compile for 64-bit when using that DLL. 

Link to post
Share on other sites
1 hour ago, JockoDundee said:

I coulda sworn you had said something about undeclared variables

Mirage ?  Excessive use of prohibited substance ? Magical mystery ?

Link to post
Share on other sites

@TheXman I tried to get further with the ZeroMQ initialization functions. The zmq_msg_init and zmq_msg_close appear to run fine, returning 0.

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

Const $tag_zmq_msg_t = "BYTE var1[32];"
Const $tag_zmq_msg_t64 = "BYTE var1[64];"

Func zmq_msg_init($pMsg)
    ; zmq_msg_init - initialise empty ØMQ message : int zmq_msg_init (zmq_msg_t *msg);
    ; http://api.zeromq.org/master:zmq-msg-init

    Local $aDllCallReturn = DllCall("libzmq-v142-mt-4_3_4.dll","int","zmq_msg_init","struct*",$pMsg)
    Return $aDllCallReturn[0]
EndFunc

Func zmq_msg_close($pMsg)
    ; zmq_msg_close - release ØMQ message : int zmq_msg_close (zmq_msg_t *msg);
    ; http://api.zeromq.org/master:zmq-msg-close

    Local $aDllCallReturn = DllCall("libzmq-v142-mt-4_3_4.dll","int","zmq_msg_close","struct*",$pMsg)
    Return $aDllCallReturn[0]
EndFunc

ConsoleWrite(@CRLF & @CRLF & @CRLF)

Global $msg = DllStructCreate($tag_zmq_msg_t)

; Test Simple Msg Init and Close
ConsoleWrite(StringFormat("Init Message Result = %s", zmq_msg_init($msg)) & @CRLF)
ConsoleWrite(StringFormat("Closing Message Result = %s", zmq_msg_close($msg)) & @CRLF)

Although I am getting the zmq_msg_init_data to run, it sometimes does not get to the end of the script and terminate. Am I missing something simple?

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

Const $tag_zmq_msg_t = "BYTE var1[32];"
Const $tag_zmq_msg_t64 = "BYTE var1[64];"

Func zmq_free_fn(ByRef $pData, ByRef $pHint)
    $pData = 0
    $pHint = 0
EndFunc

Func zmq_msg_init_data($pMsg,$pData,$iSize,$hHandle,$pHint)
    ; zmq_msg_init_data - initialise ØMQ message from a supplied buffer
        ; typedef void (zmq_free_fn) (void *data, void *hint);
        ; int zmq_msg_init_data (zmq_msg_t *msg, void *data, size_t size, zmq_free_fn *ffn, void *hint);
    ; http://api.zeromq.org/master:zmq-msg-init-data

    Local $aDllCallReturn = DllCall("libzmq-v142-mt-4_3_4.dll","int","zmq_msg_init_data","struct*",$pMsg,"ptr",$pData,"uint",$iSize,"ptr",DllCallbackGetPtr($hHandle),"ptr",$pHint)

    Return $aDllCallReturn[0]
EndFunc

Func zmq_msg_close($pMsg)
    ; zmq_msg_close - release ØMQ message : int zmq_msg_close (zmq_msg_t *msg);
    ; http://api.zeromq.org/master:zmq-msg-close

    Local $aDllCallReturn = DllCall("libzmq-v142-mt-4_3_4.dll","int","zmq_msg_close","struct*",$pMsg)
    Return $aDllCallReturn[0]
EndFunc

ConsoleWrite(@CRLF & @CRLF & @CRLF)

Global $msg = DllStructCreate($tag_zmq_msg_t)

; Show Message Pointer/Size
ConsoleWrite(StringFormat("Message Ptr = %s", DllStructGetPtr($msg)) & @CRLF)
ConsoleWrite(StringFormat("Message Size = %s", DllStructGetSize($msg)) & @CRLF)

; Create and Set Data
Const $tag_data = "BYTE var1[6];"
Global $data = DllStructCreate($tag_data)
DllStructSetData($data, 1, "ABCDEF")

; Show Data Size
ConsoleWrite(StringFormat("Data Ptr = %s", DllStructGetPtr($data)) & @CRLF)
ConsoleWrite(StringFormat("Data Size = %s", DllStructGetSize($data)) & @CRLF)

; Create callback function.
Global $hHandle = DllCallbackRegister("zmq_free_fn", "none", "pData;pHint")

; Init message data
ConsoleWrite(StringFormat("zmq_msg_init_data = %s", zmq_msg_init_data(DllStructGetPtr($msg),DllStructGetPtr($data),6,$hHandle,null)) & @CRLF)

; Msg Close
ConsoleWrite(StringFormat("Closing Message = %s", zmq_msg_close($msg)) & @CRLF)

; Delete callback function.
DllCallbackFree($hHandle)

I'm trying to mimic the C++ example given by the documentation: http://api.zeromq.org/master:zmq-msg-init-data

void my_free (void *data, void *hint)
{
 free (data);
}

 /* ... */

void *data = malloc (6);
assert (data);
memcpy (data, "ABCDEF", 6);
zmq_msg_t msg;
rc = zmq_msg_init_data (&msg, data, 6, my_free, NULL); assert (rc == 0);

 

Link to post
Share on other sites

Your DllCallbackRegister is badly formatted.  The parameters must follow DllCall types.  As I already told you, put some error handling...

Link to post
Share on other sites

Added some error messages, and there is an error code = 2 for the DllCallbackRegister shown as "ERROR: Registering ZMQ_FREE_F Error - @error = 2"

Message Ptr = 0x000001D72E9D0FC0
Message Size = 32
Success: DllStructCreate($tag_data)
Success: DllStructSetData($data...
Data Ptr = 0x000001D72E9CB350
Data Size = 6
FuncName = ZMQ_FREE_FN
ERROR: Registering ZMQ_FREE_F Error - @error = 2
zmq_msg_init_data = 0
Success: zmq_msg_init_data(...
Closing Message = 0
Success: zmq_msg_close($msg)
Success: DllCallbackFree($hHandle)
!>07:32:56 AutoIt3.exe ended.rc:-1073740940
+>07:32:56 AutoIt3Wrapper Finished.
>Exit code: 3221226356    Time: 2.114

I didn't find any error code references in the help file. If it is the same as the DllCall error code, then does that mean the "unknown error?"

Here's the code:

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include <constants.au3>

Const $tag_zmq_msg_t = "BYTE var1[32];"
Const $tag_zmq_msg_t64 = "BYTE var1[64];"

ConsoleWrite(@CRLF & @CRLF & @CRLF)

Global $msg = DllStructCreate($tag_zmq_msg_t)

; Show Message Pointer/Size
ConsoleWrite(StringFormat("Message Ptr = %s", DllStructGetPtr($msg)) & @CRLF)
ConsoleWrite(StringFormat("Message Size = %s", DllStructGetSize($msg)) & @CRLF)

; Create and Set Data
Const $tag_data = "BYTE var1[6];"
Global $data = DllStructCreate($tag_data)
console_error (@error, "DllStructCreate($tag_data)")

DllStructSetData($data, 1, "ABCDEF")
console_error (@error, "DllStructSetData($data...")


; Show Data Size
ConsoleWrite(StringFormat("Data Ptr = %s", DllStructGetPtr($data)) & @CRLF)
ConsoleWrite(StringFormat("Data Size = %s", DllStructGetSize($data)) & @CRLF)

; Check to see what's the function's name
ConsoleWrite("FuncName = " & FuncName(zmq_free_fn) & @CRLF)

; Create callback function.
Global $hHandle = DllCallbackRegister("ZMQ_FREE_FN", "none", "pData;ptr;pHint;ptr")
console_error (@error, "Registering ZMQ_FREE_F")

; Init message data
ConsoleWrite(StringFormat("zmq_msg_init_data = %s", zmq_msg_init_data(DllStructGetPtr($msg),DllStructGetPtr($data),6,$hHandle,null)) & @CRLF)
console_error (@error, "zmq_msg_init_data(...")

; Msg Close
ConsoleWrite(StringFormat("Closing Message = %s", zmq_msg_close($msg)) & @CRLF)
console_error (@error, "zmq_msg_close($msg)")

; Delete callback function.
DllCallbackFree($hHandle)
console_error (@error, "DllCallbackFree($hHandle)")


; Functions

Func zmq_free_fn($pData, $pHint)
;Func zmq_free_fn(ByRef $pData, ByRef $pHint)
    $pData = 0 ; free $pData memory
    $pHint = 0 ; free $pHint memory
EndFunc

Func zmq_msg_init_data($pMsg,$pData,$iSize,$hHandle,$pHint)
    ; zmq_msg_init_data - initialise ØMQ message from a supplied buffer
        ; typedef void (zmq_free_fn) (void *data, void *hint);
        ; int zmq_msg_init_data (zmq_msg_t *msg, void *data, size_t size, zmq_free_fn *ffn, void *hint);
    ; http://api.zeromq.org/master:zmq-msg-init-data

    Local $aDllCallReturn = DllCall("libzmq-v142-mt-4_3_4.dll","int","zmq_msg_init_data","struct*",$pMsg,"ptr",$pData,"uint",$iSize,"ptr",DllCallbackGetPtr($hHandle),"ptr",$pHint)

    Return $aDllCallReturn[0]
EndFunc

Func zmq_msg_close($pMsg)
    ; zmq_msg_close - release ØMQ message : int zmq_msg_close (zmq_msg_t *msg);
    ; http://api.zeromq.org/master:zmq-msg-close

    Local $aDllCallReturn = DllCall("libzmq-v142-mt-4_3_4.dll","int","zmq_msg_close","struct*",$pMsg)
    Return $aDllCallReturn[0]
EndFunc

Func console_error ($errno, $tag)
    If $errno Then
        ConsoleWrite("ERROR: " & $tag & " Error - @error = " & $errno & @CRLF)
    Else
        ConsoleWrite("Success: " & $tag & @CRLF)
    EndIf
EndFunc

If I use the following for the zmq_free_fn function with ByRef instead:

Func zmq_free_fn(ByRef $pData, ByRef $pHint)
    $pData = 0 ; free data from memory
    $pHint = 0 ; free hint from memory
EndFunc
Message Ptr = 0x000001B81A7CC220
Message Size = 32
Success: DllStructCreate($tag_data)
Success: DllStructSetData($data...
Data Ptr = 0x000001B81A7CBCC0
Data Size = 6
FuncName = ZMQ_FREE_FN
ERROR: Registering ZMQ_FREE_F Error - @error = 3
!>07:31:46 AutoIt3.exe ended.rc:-1073740940
+>07:31:46 AutoIt3Wrapper Finished.
>Exit code: 3221226356    Time: 2.261

I get an error 3 (I don't know, Function not found?) from the DllCallbackRegister function, and the rest of the script doesn't execute.

Should the data type for the call back parameters be written as:

; Create callback function.
Global $hHandle = DllCallbackRegister("zmq_free_fn", "none", "pData;ptr;pHint;ptr")

or should it be:

; Create callback function.
Global $hHandle = DllCallbackRegister("zmq_free_fn", "none", "pData;none;pHint;none")

 

Link to post
Share on other sites
Global $hHandle = DllCallbackRegister(zmq_free_fn, "none", "ptr;ptr")

You can remove double quotes around any reference to functions.  It will make it detectable for au3check if the function is wrong.

Link to post
Share on other sites

@Nine Much appreciation for the help. Can't thank you enough for the guidance. Doing that allows the zmq_msg_init_data and the callback function to register and run without error.

I now need to track down why the zmq_free_fn doesn't seem to clean up or free that memory.

Does "ptr;ptr" for DllCallbackRegister params mean that for AutoIt functions being called back, one doesn't need to use the parameter names of the function?

Global $hHandle = DllCallbackRegister(zmq_free_fn, "none", "ptr;ptr")

Again, thanks for the help as I'm still a beginner with AutoIt.

Link to post
Share on other sites
Posted (edited)

Another issue from your last post:

You define the following 2 constants.  Where did you get the understanding that a zmq msg type could be 32 bytes?  Your definitions:

Const $tag_zmq_msg_t = "BYTE var1[32];"
Const $tag_zmq_msg_t64 = "BYTE var1[64];"

The header file defines zmq_msg_t as:

/* Some architectures, like sparc64 and some variants of aarch64, enforce pointer
 * alignment and raise sigbus on violations. Make sure applications allocate
 * zmq_msg_t on addresses aligned on a pointer-size boundary to avoid this issue.
 */
typedef struct zmq_msg_t
{
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
    __declspec(align (8)) unsigned char _[64];
#elif defined(_MSC_VER)                                                        \
  && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE) || defined(_M_ARM))
    __declspec(align (4)) unsigned char _[64];
#elif defined(__GNUC__) || defined(__INTEL_COMPILER)                           \
  || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590)                              \
  || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590)
    unsigned char _[64] __attribute__ ((aligned (sizeof (void *))));
#else
    unsigned char _[64];
#endif
} zmq_msg_t;

That says that a zmq_msg_t is 64 bytes.  Since the default byte boundary for a struct created with DllStructCreate is 8, you don't need to explicitly set the alignment but it wouldn't hurt for the sake of readability and explicit translation.  You don't have to because anything on an 8 byte boundary is also on a 4 byte boundary.  You could do something like:

Global $tag_zmq_msg_t = (@AutoItX64 ? "align 8;" : "align 4;") & "byte var[64];"  ;Explicit boundaries

or

Global $tag_zmq_msg_t = "byte var[64];"  ; Implicit 8 byte boundary

In your script, you used the 32 byte definition.  If an API function is expecting 64 bytes, and it only is given 32 bytes, bad things can happen.  At the least, you can have unexpected results.  You used:

Global $msg = DllStructCreate($tag_zmq_msg_t)

 

One more word of advice, in the functions that call the API's, after each DllCall, you should check both the @error and the API's return code (if one exists) to make sure that the API call was successful.  If the API call is not successful, you should signal to the caller that the function failed.  Many scripts use SetError() for this purpose.  That also means that it might be better not to call those functions within other functions, like you are currently doing.  By calling the functions within other functions, you don't have the ability to make sure that the function was successful.

For example you do this:

ConsoleWrite(StringFormat("zmq_msg_init_data = %s", zmq_msg_init_data(DllStructGetPtr($msg),DllStructGetPtr($data),6,$hHandle,null)) & @CRLF)

When it might be better to do something like this:

$someVar = zmq_msg_init_data(DllStructGetPtr($msg),DllStructGetPtr($data),6,$hHandle,null)
If @error Then Return MsgBox($MB_ICONERROR, "ERROR", "Some error occured.  @error = " & @error)

ConsoleWrite("zmq_msg_init_data = " & $someVar & @CRLF)

Like I said, this assumes that zmq_msg_init_data() appropriately sets @error if an error was encountered.  The other benefit to using similar logic is that you don't continue when an error has occurred.  Obviously if you were not successful in initializing your msg data, you shouldn't continue.  :)

Edited by TheXman
Reworded last bit of advice.
Link to post
Share on other sites
  • Moderators

TheXman,

Just as an aside, thanks for the clear explanations. I learn a lot from your posts on DLLcalls and structs generally.

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to post
Share on other sites
Quote

You define the following 2 constants.  Where did you get the understanding that a zmq msg type could be 32 bytes?

I tried to look at how different languages have implemented ZMQ bindings, and also did a search through Github for more information at the ZMQ repository.

I saw references at https://travlr.github.io/libzmq/structzmq__msg__t.html that show:

typedef struct {unsigned char _ [32];} zmq_msg_t;

I've seen some people use this instead for 64-bit and similarly for 32-bit:

type zmq_msg_t = ubyte unsigned char 8
        __(0 to 63) as ubyte

They all "seem" to work for those language binding implementations.

In the msg.cpp, on line 44, there is code that shows:

//  Check whether the sizes of public representation of the message (zmq_msg_t)
//  and private representation of the message (zmq::msg_t) match.

typedef char
  zmq_msg_size_check[2 * ((sizeof (zmq::msg_t) == sizeof (zmq_msg_t)) != 0)
                     - 1];

bool zmq::msg_t::check () const
{
    return _u.base.type >= type_min && _u.base.type <= type_max;
}

Reference: https://github.com/zeromq/libzmq/blob/master/src/msg.cpp#L44

The msg.hpp, https://github.com/zeromq/libzmq/blob/2ac9755ee9df54e453958136bd17fee5819dbdba/include/zmq.h#L229 has the definition that you refer to:

/******************************************************************************/
/*  0MQ message definition.                                                   */
/******************************************************************************/

/* Some architectures, like sparc64 and some variants of aarch64, enforce pointer
 * alignment and raise sigbus on violations. Make sure applications allocate
 * zmq_msg_t on addresses aligned on a pointer-size boundary to avoid this issue.
 */
typedef struct zmq_msg_t
{
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
    __declspec(align (8)) unsigned char _[64];
#elif defined(_MSC_VER)                                                        \
  && (defined(_M_IX86) || defined(_M_ARM_ARMV7VE) || defined(_M_ARM))
    __declspec(align (4)) unsigned char _[64];
#elif defined(__GNUC__) || defined(__INTEL_COMPILER)                           \
  || (defined(__SUNPRO_C) && __SUNPRO_C >= 0x590)                              \
  || (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x590)
    unsigned char _[64] __attribute__ ((aligned (sizeof (void *))));
#else
    unsigned char _[64];
#endif
} zmq_msg_t;

That's the definition of zmq_msg_t within the ZMQ code for the DLL.

Quote

In your script, you used the 32 byte definition.  If an API function is expecting 64 bytes, and it only is given 32 bytes, bad things can happen.  At the least, you can have unexpected results.

That's what I'm experiencing running the functions. They do run and no errors generated, but there are unexpected issues. That's probably because of the memory alignment you refer to.

@TheXman Thanks again for the suggestions and you're absolutely right. It really helps me muddle through learn how to implement this. 

Link to post
Share on other sites
53 minutes ago, swoop said:

I now need to track down why the zmq_free_fn doesn't seem to clean up or free that memory.

It is because you are zeroing the pointer not the struct.

54 minutes ago, swoop said:

one doesn't need to use the parameter names of the function?

Exactly

Link to post
Share on other sites
Posted (edited)
1 hour ago, swoop said:

That's the definition of zmq_msg_t within the ZMQ code for the DLL.

Yes, I used the definition in the zmq.h header file, from the latest version of the ZeroMQ DLL for Windows (libzmq-v142-x64-4_3_4.zip), on the page that you referred to in the 3rd post. That's probably the one that you should rely on since you are using the DLL built from that source -- at least I hope that's what you are using.  :)

Edited by TheXman
Corrected the name of the latest DLL file
Link to post
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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...