Jump to content

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


Recommended Posts

2 hours ago, swoop said:

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

I  can see that you've been doing a lot of searching for and reading topics on how to do API calls and define structures in AutoIt.  I'm sure that you've come across several good ones.  In addition to those topics, there's a wealth of examples in the WinApi* UDF libs that come with AutoIt as well as many UDF libs submitted by users.  If you would like a suggestion for a relatively well-formatted, well-written, and well-documented UDF lib with numerous DllCalls/Structs/Constants/Enums, may I suggest my CryptoNG UDF lib (CryptoNG.au3)?  Of course I'm biased as to its quality :) but I also suggest it because I wrote it.  So I know how it works and why any particular piece of code was written the way it was.  Therefore, I can easily answer any questions that you may have about it.  Not only will it give you many DllCall and DllStruct* examples, but it also has examples of how error checking can be done as well as other general formatting and coding techniques.  Hopefully, it can help you quickly flatten your overall AutoIt leaning curve.

If after looking through the UDF you have general AutoIt-related questions, then I would suggest you create a new topic for those questions.  If you have CryptoNG-specific questions in which you think the answers may benefit other users of CryptoNG, then I suggest you post them in the CryptoNG topic.  Of course if you have ZeroMQ-related questions, then continue posting them here.  That way, you keep this or other topics on-point and uncluttered.  ;)

Edited by TheXman
Link to comment
Share on other sites

3 minutes ago, TheXman said:

I  can see that you've been doing a lot of searching for and reading topics on how to do API calls and define structures in AutoIt.  I'm sure that you've come across several good ones.  In addition to those topics, there's a wealth of examples in the WinApi* UDF libs that come with AutoIt as well as many UDF libs submitted by users.  If you would like a suggestion for a relatively well-formatted, well-written, and well-documented UDF lib with numerous DllCalls/Structs/Constants/Enums, may I suggest my CryptoNG UDF lib?  Of course I'm biased as to its quality :) but I also suggest it because I wrote it.  So I know how it works and why any particular piece of code was written the way it was.  Therefore, I can easily answer any questions that you may have about it.  Not only will it give you many DllCall and DllStruct* examples, but it also has examples of how error checking can be done as well as other general formatting and coding techniques.  Hopefully, it can help you quickly flatten your overall AutoIt leaning curve.

 

Excellent! Thanks @TheXman for the great suggestion.

I will take some time out now to study your CyrptoNG UDF code to learn about how to work with the structs and DllCall. Seeing examples like this does help a lot. This is just what I needed.

Link to comment
Share on other sites

@TheXman

I have been studying the CryptoNG UDF and the HttpApi UDF. Thanks again for the suggestion and help.

I want to clarify if I understand how DllStructs are used in AutoIt for working with external DLLs.

 

In the help file about DllStructCreatehttps://www.autoitscript.com/autoit3/docs/functions/DllStructCreate.htm the description reads as follows:

DllStructCreate
Creates a C/C++ style structure to be used in DllCall.

DllStructCreate ( Struct [, Pointer] )

Parameters

Struct A string representing the structure to create (See Remarks).
Pointer [optional] If supplied the struct will not allocate memory but use the pointer supplied.

Return Value

Success: a variable for use with DllStruct calls.
Failure: sets the @error flag to non-zero.
@error: 1 = Variable passed to DllStructCreate was not a string.
2 = There is an unknown Data Type in the string passed.
3 = Failed to allocate the memory needed for the struct, or Pointer = 0.
4 = Error allocating memory for the passed string.

 

When we have an existing struct created by another DLL, we don't need to create that struct again in memory. But it is important to use the DllStructCreate with the optional pointer parameter in order to work with this "external" struct in AutoIt.

The variable returned from DllStructCreate is used by DllStruct and other DllCall related functions to deal with an unknown pre-existing struct. Such functions as: DllCall, DllStructGetData, DllStructGetPtr, DllStructGetSize, DllStructSetData, IsDllStruct.


I refer to specific DllStructCreate examples in your UDF's for my current understanding:

From the HTTPAPI.au3:

Func _HTTPAPI_HttpSendHttpResponse($hRequestQueue, $iRequestId, $iStatusCode, $sReason, $sBody)

    _DebugOut(@CRLF & "Function: HttpSendHttpResponse")

    Local $aResult[0]
    Local $pKnownHeader = Null
    Local $iFlags = 0

    Local $tResponse      = DllStructCreate($__HTTPAPI_gtagHTTP_RESPONSE_V2), _
          $tCachePolicy   = DllStructCreate($__HTTPAPI_gtagHTTP_CACHE_POLICY), _
          $tLoggedData    = DllStructCreate($__HTTPAPI_gtagHTTP_LOG_FIELDS_DATA), _
          $tDataChunk     = DllStructCreate($__HTTPAPI_gtagHTTP_DATA_CHUNK_FROM_MEMORY), _
          $tReason        = __HTTPAPI_CreateStringBuffer($sReason), _
          $tBody          = __HTTPAPI_CreateStringBuffer($sBody), _
          $tContentType   = __HTTPAPI_CreateStringBuffer("text/html"), _
          $tContentLength = __HTTPAPI_CreateStringBuffer(String(DllStructGetSize($tBody))), _
          $tHeader        = ""


    ;Init response
    $tResponse.MajorVersion  = 1
    $tResponse.MinorVersion  = 1
    $tResponse.StatusCode    = $iStatusCode
    $tResponse.pReason       = DllStructGetPtr($tReason)
    $tResponse.ReasonLength  = DllStructGetSize($tReason)
    $tResponse.LogDataType   = $__HTTPAPI_HttpLogDataTypeFields

    ;Add known header(s) to response
    $pKnownHeader           = DllStructGetPtr($tResponse, "KnownHeaders") + ($__HTPPAPI_HttpHeaderContentType * $__HTTPAPI_HTTP_KNOWN_HEADER_SIZE)
    $tHeader                = DllStructCreate($__HTTPAPI_gtagHTTP_KNOWN_HEADER, $pKnownHeader)
    $tHeader.pRawValue      = DllStructGetPtr($tContentType)
    $tHeader.RawValueLength = DllStructGetSize($tContentType)

The specific line 1240:

$tHeader                = DllStructCreate($__HTTPAPI_gtagHTTP_KNOWN_HEADER, $pKnownHeader)

I think that means the $tHeader struct is not created nor allocated in memory. We're just creating a struct variable to work with the struct.

$pKnownHeader is a pointer to a pre-existing struct (in memory).

We are creating $tHeader DLLStruct variable that contains information about the existing struct, which we know has the structure defined by $__HTTPAPI_gtagHTTP_KNOWN_HEADER.

This $tHeader variable allows for us to use DllStruct functions to call and manipulate data in the memory location referred by $pKnownHeader.

 

To apply the same idea to my ZeroMQ Dll struct code, if the ZeroMQ DLL functions create existing message structs and gives me back pointers, then I can use the DllStructCreate to do the same as you've done above. To make the message structs available for use in AutoIt via DllStrut and DllCall functions.

That's also what's happening in the CryptoNG when DllStructCreate is used in the __CryptoNG_BCryptDecrypt function.

 

From the CryptoNG.au3:

Func __CryptoNG_BCryptDecrypt($sAlgorithmId, $xData, $hEncryptionKey, $bResultIsText = True)

    Local $tInputBuffer  = "", _
          $tOutputBuffer = "", _
          $tIVBuffer     = "", _
          $tByteBuffer   = "", _
          $tUlong        = ""

    Local $iBlockLength  = 0, _
          $iStatusCode   = 0, _
          $iError        = 0, _
          $iOutputLength = 0

    Local $aResult[0]

    Local $vDecryptedData = ""

    Local $xIV = Binary("")


    _DebugOut(@CRLF & "Function: __CryptoNG_BCryptDecrypt()")

    _DebugOut("$sAlgorithmId = " & $sAlgorithmId)
    _DebugOut("$xData        = " & $xData)

    ;Get length of key
    $tByteBuffer = __CryptoNG_BCryptGetProperty($hEncryptionKey, $CNG_BCRYPT_KEY_LENGTH)
    If @error Then Return SetError(1, 0, "")
    $tUlong     = _WinAPI_CopyStruct($tByteBuffer, "ulong value")

    ;If this is a block cipher (not a stream cipher)
    If $sAlgorithmId <> $CNG_BCRYPT_RC4_ALGORITHM Then
        ;Get length of block
        $tByteBuffer = __CryptoNG_BCryptGetProperty($hEncryptionKey, $CNG_BCRYPT_BLOCK_LENGTH)
        If @error Then Return SetError(1, 0, "")
        $tUlong       = _WinAPI_CopyStruct($tByteBuffer, "ulong value")
        $iBlockLength = $tUlong.value
        _DebugOut("$iBlockLength = " & $iBlockLength)

        ;Create initialization vector (IV) buffer and set its default value (0x000102...)
        $xIV = Binary("")
        For $i = 0 To $iBlockLength - 1
            $xIV &= Binary(Chr($i))
        Next
        $tIVBuffer      = DllStructCreate(StringFormat("byte data[%i]", $iBlockLength))
        $tIVBuffer.data = $xIV
        _DebugOut("IV = " & $tIVBuffer.data)
    EndIf

The specific line 3422, the DllStructCreate is creating a new DllStruct variable $tIVBuffer:

$tIVBuffer      = DllStructCreate(StringFormat("byte data[%i]", $iBlockLength))

A DllStruct variable $tByteBuffer is returned from __CryptoNG_BCryptGetProperty:

$tByteBuffer = __CryptoNG_BCryptGetProperty($hEncryptionKey, $CNG_BCRYPT_BLOCK_LENGTH)

A copy of that struct a created as $tUlong. I'm not entirely sure why _WinAPI_CopyStruct is used here. Does this function turn the DllStruct into a WinAPI struct format? I think CopyStruct is just a WinAPI call to copy the stuct at the memory location specified by $tByteBuffer. But isn't $tByteBuffer a DllStruct variable?

$tUlong       = _WinAPI_CopyStruct($tByteBuffer, "ulong value")

Then $iBlockLength uses the ulong value of $tUlong.

$iBlockLength = $tUlong.value

It appears that DllStructCreate for $tIVBuffer uses $iBlockLength as a length for data[?].

$tIVBuffer      = DllStructCreate(StringFormat("byte data[%i]", $iBlockLength))
$tIVBuffer.data = $xIV

 

Also I see this reference in __CryptoNG_BCryptGetProperty on line 4880:

_DebugOut("$tBuffer = " & $tBuffer.data)

There is a .data property used for the DllStruct variable. What is contained in .data? The actual binary data of the struct in memory?

Is there a list of other properties that can be accessed from a DllStruct directly?

 

Generally, is my understanding of both examples above correct?

I'm trying to understand what's going on, rather than mimicking the code.

Again thanks for the help. I'm still studying both UDF's. They are so well organized and written. I especially like how the HTTPAPI API organizes the structs in a global header section.

;========================
; STRUCTURES
;========================
;Global HHTP Version Structure
Global $__HTTPAPI_gtagHTTP_VERSION = _
           "struct;"               & _
           "ushort MajorVersion;"  & _
           "ushort MinorVersion;"  & _
           "endstruct;"

;Global HHTPAPI Version Structure and Constants
Global $__HTTPAPI_gtagHTTPAPI_VERSION    = _
           "struct;"                     & _
           "ushort HttpApiMajorVersion;" & _
           "ushort HttpApiMinorVersion;" & _
           "endstruct;"

And structs within structs:

Global $__HTTPAPI_gtagHTTP_DATA_CHUNK_FROM_FRAGMENT_CACHE_EX = _
           $__HTTPAPI_gtagHTTP_DATA_CHUNK                    & _
           "struct;"                                         & _
           "ushort  FragmentNameLength;"                     & _ ;in bytes not including the NUL
           "ptr     pFragmentName;"                          & _ ;wstr
           "endstruct;"

Also, I saw the data alignment techniques here:

Global $__HTTPAPI_gtagHTTP_DATA_CHUNK = _
           "struct;"                  & _
           "int  DataChunkType;"      & _
           "endstruct;"               & _
           (@AutoItX64 ? "" : "ptr;")      ;Union alignment issue found & fixed by Danyfirex

 

Edited by swoop
Caught my error in understanding line 3422
Link to comment
Share on other sites

Sorry, my bad. I just caught my error in the second example for __CryptoNG_BCryptGetProperty.

$tIVBuffer      = DllStructCreate(StringFormat("byte data[%i]", $iBlockLength))
$tIVBuffer.data = $xIV

This is actually creating the struct variable defined by StringFormat("byte data[%i]", $iBlockLength)

So the struct is something like "byte data[10]"

In this case, it is not an optional pointer.

Edited by swoop
Link to comment
Share on other sites

43 minutes ago, swoop said:

I think that means the $tHeader struct is not created nor allocated in memory. We're just creating a struct variable to work with the struct.

Correct

43 minutes ago, swoop said:

To apply the same idea to my ZeroMQ Dll struct code, if the ZeroMQ DLL functions create existing message structs and gives me back pointers, then I can use the DllStructCreate to do the same as you've done above.

Correct again

43 minutes ago, swoop said:

It appears that DllStructCreate for $tIVBuffer uses $iBlockLength as a length for data[?].

Correct

43 minutes ago, swoop said:

There is a .data property used for the DllStruct variable. What is contained in .data? The actual binary data of the struct in memory?

Sort of.  "data" is a named member of the struct (not really a property as such - but it can be thought of that way when using dot-notation to access struct members.  The same can be done using DllStructGetData/DllStructSetData, but I prefer the dot-notation when possible). 

 

Edited by TheXman
Link to comment
Share on other sites

By the way, you should not get caught up with creating or using the callback function in the zmq_msg_init_data() API.  It is optional.  Since you are creating the data struct, you can either free the resources yourself, or if it is a local variable, it will be freed when the function it is in is done.  In AutoIt, most local resources are freed when there are no more references to them.  Therefore, when the function that the API call is in is finished, the data struct's memory will be freed also.

I actually created a script to test it, along with about 4 or 5 other ZeroMQ APIs and it works as described and expected.  I'm off to the gym right now but if you would like to see the test script, then show me what you have done and I will show you the test script later this afternoon (in a few hours) when I return.  ;)

Edited by TheXman
Link to comment
Share on other sites

2 hours ago, TheXman said:

By the way, you should not get caught up with creating or using the callback function in the zmq_msg_init_data() API.  It is optional. 

Good suggestion. I'll get the rest of the functions written and tested first.

  

2 hours ago, TheXman said:

In AutoIt, most local resources are freed when there are no more references to them.

So, this usually occurs when scope of a function ends. If the data struct is in the global scope, the resources are freed upon exit of the script. Is that correct? I can just let AutoIt handle that garbage collection.

Just curious, is there a way to free or destroy that data struct? Something like a _WinAPI_CopyStruct (Creates a duplicate of a specified structure), but for destroying a Struct?

Nine said my somevarA = 0 doesn't actually free up that data.

Quote

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

 

Edited by swoop
Link to comment
Share on other sites

@swoop

Since it appears that you are really taking the time to learn, I will put my little sample script below (with its output).  Ordinarily, I would wait to see some actual effort or for an explicit request.  ;)  Keep in mind that this is just one way, my way.  As with any language, there's usually many ways to say the same thing.  The sample script has terse documentation, but hopefully enough to help understand what each block of code is doing. 

I am hiding it behind the "Show Hidden Contents" banner in case you would rather not be tempted to see it while browsing the topic.  :D

Spoiler
#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d
#AutoIt3Wrapper_UseX64=Y

#cs
    0MQ LibZMQ Github:    https://github.com/zeromq/libzmq
    0MQ LibZMQ Releases:  https://github.com/zeromq/libzmq/releases
    0MQ API Reference:    http://api.zeromq.org/master:_start
#ce

#include <Constants.au3>

;Global Constant(s)
Const $DEBUGGING       = True
Const $ZEROMG_DLL_FILE = @ScriptDir & "\libzmq-v142-mt-4_3_4.dll"
Const $TAG_ZMQ_MSG_T   = "byte msg[64];"

;Global Handle(s)
Global $ghZeroMQDll = -1

;Context Options
Const $ZMQ_IO_THREADS                 = 1, _
      $ZMQ_MAX_SOCKETS                = 2, _
      $ZMQ_SOCKET_LIMIT               = 3, _
      $ZMQ_THREAD_PRIORITY            = 3, _
      $ZMQ_THREAD_SCHED_POLICY        = 4, _
      $ZMQ_MAX_MSGSZ                  = 5, _
      $ZMQ_MSG_T_SIZE                 = 6, _
      $ZMQ_THREAD_AFFINITY_CPU_ADD    = 7, _
      $ZMQ_THREAD_AFFINITY_CPU_REMOVE = 8, _
      $ZMQ_THREAD_NAME_PREFIX         = 9


zeromq_test()

Func zeromq_test()
    Local $sVersion = ""
    Local $vData = ""
    Local $hContext = -1
    Local $tMsg = "", $tData = ""
    Local $pMsg = 0
    Local $iOption = 0, $iOptionValue = 0

    ConsoleWrite("====================" & @CRLF)
    ConsoleWrite("ZeroMQ API Test" & @CRLF)
    ConsoleWrite("====================" & @CRLF)

    ;Get and display 0MQ version
    $sVersion = zmq_version()
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_version ERROR", "DllCall failed with @error = " & @extended)
    ConsoleWrite("ZeroMQ: Version = " & $sVersion & @CRLF)

    ;Create a message struct
    $tMsg       = DllStructCreate($TAG_ZMQ_MSG_T)
    $pMsg       = DllStructGetPtr($tMsg)

    ;Create a msg data struct with data
    $vData      = "ABCDEF"
    $tData      = DllStructCreate(StringFormat("byte data[%i]", BinaryLen($vData)))
    $tData.data = Binary($vData)

    ;Initialize a data message
    zmq_msg_init_data($pMsg, $tData)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_msg_init_data ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: New data msg successfuly initialized" & @CRLF)
;~  $tData = 0 ;Explicitly free data struct memory (not necessary; local resources will be freed when func ends)

    ;Get a new 0MQ context handle
    $hContext = zmq_ctx_new()
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_new ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: New context handle successfuly created = " & $hContext & @CRLF)

    ;Set a context option
    $iOption      = $ZMQ_IO_THREADS
    $iOptionValue = 3
    zmq_ctx_set($hContext, $iOption, $iOptionValue)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_set ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: Context option ZMQ_IO_THREADS successfuly set to " & $iOptionValue & @CRLF)

    ;Get a context option
    $iOptionValue = zmq_ctx_get($hContext, $iOption)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_get ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: Context option ZMQ_IO_THREADS successfuly retrieved = " & $iOptionValue & @CRLF)

    ;Get a context option
    $iOptionValue = zmq_ctx_get($hContext, $ZMQ_MAX_MSGSZ)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_get ERROR", zmq_strerror(@extended))
    ConsoleWrite(StringFormat("ZeroMQ: Context option ZMQ_MAX_MSGSZ successfuly retrieved = %i (0x%x)", $iOptionValue, $iOptionValue) & @CRLF)

    ;Terminate a 0MQ context handle
    zmq_ctx_term($hContext)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "Error terminating new context handle. - errno = " & @extended)
    ConsoleWrite("ZeroMQ: Context handle successfuly terminated  = " & $hContext & @CRLF)
EndFunc

;==================================
; Internal/Helper Functions
;==================================

Func get_zmq_dll_handle()
    ;If no dll handle exists yet
    If $ghZeroMQDll = -1 Then
        ;Get global handle to dll
        $ghZeroMQDll = DllOpen($ZEROMG_DLL_FILE)
        If @error Then Exit MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "DllOpen failed")

        ;Register function to close dll handle on exit
        OnAutoItExitRegister("close_zmq_dll_handle")

        If $DEBUGGING Then ConsoleWrite("(DEBUG) ZeroMQ: DLL handle opened." & @CRLF)
    EndIf

    ;Return global handle to dll
    Return $ghZeroMQDll
EndFunc

Func close_zmq_dll_handle()
    ;Close global handle
    DllClose($ghZeroMQDll)

    If $DEBUGGING Then ConsoleWrite("(DEBUG) ZeroMQ: DLL handle closed" & @CRLF)
EndFunc

;====================================
; ZeroMQ API Function Examples
; - zmq_ctx_new()
; - zmq_ctx_set($hContext, $iOption, $iOptionValue)
; - zmq_ctx_get($hContext, $iOption)
; - zmq_ctx_term($hContext)
; - zmq_errno()
; - zmq_msg_init_data($pMsg, $tData)
; - zmq_strerror($iErrNo)
; - zmq_version()
;====================================

Func zmq_ctx_new()
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "handle", "zmq_ctx_new")
    If @error Then
        Return SetError(1, @error, 0)      ;DllCall failed
    ElseIf $aResult[0] = 0 Then
        Return SetError(2, zmq_errno(), 0) ;API call failed
    EndIf

    ;Return new 0MQ context handle
    Return $aResult[0]
EndFunc

Func zmq_ctx_set($hContext, $iOption, $iOptionValue)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_ctx_set", _
                       "handle", $hContext, _
                       "int",    $iOption, _
                       "int",    $iOptionValue)
    If @error Then
        Return SetError(1, @error, False)      ;DllCall failed
    ElseIf $aResult[0] = -1 Then
        Return SetError(2, zmq_errno(), False) ;API call failed
    EndIf

    ;All is good
    Return True
EndFunc

Func zmq_ctx_get($hContext, $iOption)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_ctx_get", _
                       "handle", $hContext, _
                       "int",    $iOption)
    If @error Then
        Return SetError(1, @error, -1)      ;DllCall failed
    ElseIf $aResult[0] = -1 Then
        Return SetError(2, zmq_errno(), -1) ;API call failed
    EndIf

    ;All is good
    Return $aResult[0]
EndFunc

Func zmq_ctx_term($hContext)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_ctx_term", "handle", $hContext)
    If @error Then
        Return SetError(1, @error, False)      ;DllCall failed
    ElseIf $aResult[0] <> 0 Then
        Return SetError(2, zmq_errno(), False) ;API call failed
    EndIf

    ;All is good
    Return True
EndFunc

Func zmq_errno()
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_errno")
    If @error Then
        Return SetError(1, @error, -1) ;DllCall failed
    EndIf

    ;All is good
    Return $aResult[0]
EndFunc

Func zmq_msg_init_data($pMsg, $tData)
    Local $aResult

    If DllStructGetSize($tData) = 0 Then Return SetError(-1, 0, False) ;No data

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_msg_init_data", _
                       "ptr",       $pMsg, _                    ;msg
                       "ptr",       DllStructGetPtr($tData), _  ;data
                       "ulong_ptr", DllStructGetSize($tData), _ ;size
                       "ptr",       Null, _                     ;ffn
                       "ptr",       Null)                       ;hint
    If @error Then
        Return SetError(1, @error, False)      ;DllCall failed
    ElseIf $aResult[0] <> 0 Then
        Return SetError(2, zmq_errno(), False) ;API call failed
    EndIf

    ;All is good
    Return True
EndFunc

Func zmq_strerror($iErrNo)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "str", "zmq_strerror", _
                       "int", $iErrNo)
    If @error Then Return SetError(1, @error, "")

    ;Return version
    Return $aResult[0]
EndFunc

Func zmq_version()
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "none", "zmq_version", _
                       "int*", Null, _  ;Major
                       "int*", Null, _  ;Minor
                       "int*", Null)    ;Patch
    If @error Then Return SetError(1, @error, "?.?.?")

    ;Return version
    Return StringFormat("%s.%s.%s", $aResult[1], $aResult[2], $aResult[3])
EndFunc

Console Output:

====================
ZeroMQ API Test
====================
(DEBUG) ZeroMQ: DLL handle opened.
ZeroMQ: Version = 4.3.4
ZeroMQ: New data msg successfuly initialized
ZeroMQ: New context handle successfuly created = 0x00000000007B8750
ZeroMQ: Context option ZMQ_IO_THREADS successfuly set to 3
ZeroMQ: Context option ZMQ_IO_THREADS successfuly retrieved = 3
ZeroMQ: Context option ZMQ_MAX_MSGSZ successfuly retrieved = 2147483647 (0x7fffffff)
ZeroMQ: Context handle successfuly terminated  = 0x00000000007B8750
(DEBUG) ZeroMQ: DLL handle closed

 

 

Edited by TheXman
Link to comment
Share on other sites

3 minutes ago, TheXman said:

I am hiding it behind the "Show Hidden Contents" banner in case you would rather not be tempted to see it while browsing the topic.  :D

I peeked! Thanks for the guidance. I am very grateful.

I'm going to apply your way. It's so much better than my first attempt.

I see this particular use of DllOpen and DllClose to get a DLL Handle instead of using the DLL filename string in the helper functions.

DllOpen($ZEROMG_DLL_FILE)

and

DllClose($ghZeroMQDll)

That's more flexible than just using a global string constant for the filename. Getting the handle can also give an error message if the DLL is missing.

Is the following code just added assurance that the DLL handle is closed upon exit? 

;Register function to close dll handle on exit
        OnAutoItExitRegister("close_zmq_dll_handle")

I see you also used handle instead of ptr for the $hContext. The handle type appears to be equivalent to void*. I made that mistake as well.

Again, thanks for the help!

Link to comment
Share on other sites

1 hour ago, swoop said:

I see this particular use of DllOpen and DllClose to get a DLL Handle instead of using the DLL filename string in the helper functions.

From the help file for the DllCall function:

If a dll filename is given then the DLL is automatically loaded and then closed at the end of the call.

In other words, if you use a DLL handle in your DllCalls, it doesn't need to open/close the DLL file for each DllCall.  So if you are going to be doing multiple DllCalls, it's better to use a handle.  Both ways work but one is more efficient if you are going to be doing multiple DllCalls using any given DLL file.

 

1 hour ago, swoop said:

Is the following code just added assurance that the DLL handle is closed upon exit?

Yes, I like explicitly tidying up as much as possible.  The get_zmq_dll_handle() function, which is used by all of the DllCalls, gets and saves a global DLL handle upon its first successful invocation.  Also upon the first successful invocation, it registers a function to close the handle upon exit of the script.  That way, even if the script is prematurely ended, like with an EXIT statement, the DLL handle will still be explicitly closed.  I do this for a lot of handles, like when I'm using SQLite or if I want to make sure that other global resources are freed before exiting the script.  All subsequent invocation of get_zmq_dll_handle() just returns the saved global DLL handle.  You are correct that it isn't required to close the handle because it will be closed anyway, but I always prefer explicit actions over implicit actions.  ;)

 

1 hour ago, swoop said:

I see you also used handle instead of ptr for the $hContext.

Internally, there's no difference between a pointer and a handle.  Symantically, there's a difference.  A pointer is just that, it points to a specific address in memory.  A handle is a pointer that points to a resource.  So the way I think of it: All handles are pointers, but not all pointers are handles.  :)

Edited by TheXman
Link to comment
Share on other sites

54 minutes ago, TheXman said:

Internally, there's no difference between a pointer and a handle.  Symantically, there's a difference.  A pointer is just that, it points to a specific address in memory.  A handle is a pointer that points to a resource.  So the way I think of it, all handles are pointers, but not all pointers are handles. 

Agree with your overall sentiment, but would just mention some handles are not direct pointers to “a specific address in memory”.

Like a file handle from FileOpen() will return an small integer which (AFAIK) is a relative offset into another table which has pointers to memory structures.   

Code hard, but don’t hard code...

Link to comment
Share on other sites

10 minutes ago, JockoDundee said:

Agree with your overall sentiment, but would just mention some handles are not direct pointers to “a specific address in memory”.

I guess I should have been more clear that I was referring to DllCalls & DllStructs, which is the main subject of this topic.  In other words, I was speaking at a lower level than AutoIt.  But you are absolutely correct.  AutoIt uses the term "handle" to point to resources and some of those "handles" are not actually memory pointers - they are more like resource IDs.

Edited by TheXman
Link to comment
Share on other sites

  • 2 years later...

Try ZeroMQ to connect between server & client using tcp & it work :D

Server.au3

#include "ZeroMQ Udf.au3"

__zeromq_Server()
Func __zeromq_Server()
    __zmq_Start()
    Local $h_Context = 0
    Local $h_Socket = 0

    $h_Context = __zmq_ctx_new()
    If @error Then __Server_OnExit($h_Context, $h_Socket, "__zmq_ctx_new ERROR: " & __zmq_strerror(@extended))

    Local $ZMQ_REP = 4
    $h_Socket = __zmq_socket($h_Context, $ZMQ_REP)
    If @error Then Return __Server_OnExit($h_Context, $h_Socket, "__zmq_socket ERROR: " & __zmq_strerror(@extended))

    Local $rc
    $rc = __zmq_bind($h_Socket, "tcp://*:5555")
    If @error Then __Server_OnExit($h_Context, $h_Socket, "__zmq_bind ERROR: " & __zmq_strerror(@extended))

    RunWait( @AutoItExe & " /AutoIt3ExecuteScript " & '"Client.au3"')

    Local $t_Msg, $p_Msg
    __zmq_msg_t($t_Msg, $p_Msg)
    __zmq_msg_init($p_Msg)

    $rc = __zmq_msg_recv($p_Msg, $h_Socket, 0)
    If @error Then __Server_OnExit($h_Context, $h_Socket, "__zmq_msg_send ERROR: " & __zmq_strerror(@extended))
    ConsoleWrite('__zmq_msg_recv[byte]: ' & $rc & @crlf)

    __Server_OnExit($h_Context, $h_Socket, "The End....")
EndFunc

Func __Server_OnExit(Byref $h_Context, Byref $h_Socket, $o_msg)
    ConsoleWrite('-> ' & $o_msg & @crlf)
    If $h_Socket <> 0 Then
        __zmq_close($h_Socket)
         If @error Then ConsoleWrite("Socket on close[zmq_close] ERROR: " & __zmq_strerror(@extended) & @crlf)
    Endif

    If $h_Context <> 0 Then
        __zmq_ctx_shutdown($h_Context)
         If @error Then ConsoleWrite("Context on close[zmq_ctx_shutdown] ERROR: " & __zmq_strerror(@extended) & @crlf)

        __zmq_ctx_term($h_Context)
         If @error Then ConsoleWrite("Context on close[zmq_ctx_term] ERROR: " & __zmq_strerror(@extended) & @crlf)
    EndIf

    $h_Socket = 0
    $h_Context = 0
    Exit
EndFunc

 

Client.au3

#include "ZeroMQ Udf.au3"

__zeromq_Client()
Func __zeromq_Client()
    MsgBox(0, 'client', '[Client started]')
    __zmq_Start()
    Local $h_Context = 0
    Local $h_Socket = 0

    $h_Context = __zmq_ctx_new()
    If @error Then __Client_OnExit($h_Context, $h_Socket, "__zmq_ctx_new ERROR: " & __zmq_strerror(@extended))

    Local $ZMQ_REQ = 3
    $h_Socket = __zmq_socket($h_Context, $ZMQ_REQ)
    If @error Then Return __Client_OnExit($h_Context, $h_Socket, "__zmq_socket ERROR: " & __zmq_strerror(@extended))

    Local $rc
    $rc = __zmq_connect($h_Socket, "tcp://localhost:5555")
    If @error Then __Client_OnExit($h_Context, $h_Socket, "__zmq_connect ERROR: " & __zmq_strerror(@extended))

    Local $t_Msg, $p_Msg
    __zmq_msg_t($t_Msg, $p_Msg)
    $rc = __zmq_msg_init_data($p_Msg, 'Hello')
    If @error Then __Client_OnExit($h_Context, $h_Socket, "__zmq_msg_init_data ERROR: " & __zmq_strerror(@extended))

    $rc = __zmq_msg_send($p_Msg, $h_Socket, 0)
    If @error Then __Client_OnExit($h_Context, $h_Socket, "__zmq_msg_send ERROR: " & __zmq_strerror(@extended))
    MsgBox(0, 'Client', '__zmq_msg_send[byte]: ' & $rc) 

    $rc = __zmq_disconnect($h_Socket, "tcp://localhost:5555")
    If @error Then __Client_OnExit($h_Context, $h_Socket, "__zmq_disconnect ERROR: " & __zmq_strerror(@extended))
    
    __Client_OnExit($h_Context, $h_Socket, "From Client The End....")
EndFunc

Func __Client_OnExit(Byref $h_Context, Byref $h_Socket, $o_msg)
    MsgBox(0, 'Client', '-> ' & $o_msg)
    If $h_Socket <> 0 Then
        __zmq_close($h_Socket)
         If @error Then MsgBox(0, 'Client', "Socket on close[zmq_close] ERROR: " & __zmq_strerror(@extended) & @crlf)
    Endif

    If $h_Context <> 0 Then
        __zmq_ctx_shutdown($h_Context)
         If @error Then MsgBox(0, 'Client', "Context on close[zmq_ctx_shutdown] ERROR: " & __zmq_strerror(@extended) & @crlf)

        __zmq_ctx_term($h_Context)
         If @error Then MsgBox(0, 'Client', "Context on close[zmq_ctx_term] ERROR: " & __zmq_strerror(@extended) & @crlf)
    EndIf

    $h_Socket = 0
    $h_Context = 0
    Exit
EndFunc

 

Link to comment
Share on other sites

@TheXman little query, how to read data using zmq_msg_data pointer?
I use your below code....

 

zmq_msg_init_data($pMsg, $tData)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_msg_init_data ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: New data msg successfuly initialized" & @CRLF)
;~  $tData = 0 ;Explicitly free data struct memory (not necessary; local resources will be freed when func ends)

    Local $iSize = zmq_msg_size($pMsg)
    ConsoleWrite("ZeroMQ: byte: " & $iSize & @CRLF)
    
    Local $pData = zmq_msg_data($pMsg)
    ConsoleWrite("ZeroMQ: pointer: " & $pData & @CRLF)

How i retrieve the raw data using this given pointer & size?
Try DllStruct using the given pointer ($pData) but failed.

Link to comment
Share on other sites

@jugador

1 hour ago, jugador said:

How i retrieve the raw data using this given pointer & size?

I would create a struct using DllStructCreate() function and pass it the pointer parameter of $pData.  Then reference the struct data as you normally would, using DllstructGetData or with dot-notation.  How your reference the data depends on the expected data (binary, string, int, struct, etc).  Without more details, it's hard to be more specific.

 

Create struct something like:

Reference data as raw binary data
$tStruct = DllStructCreate("byte data[" & $iSize & "]", $pData)

Reference data as CHAR string:
$tStruct = DllStructCreate("char data[" & $iSize & "]", $pData)

Reference data as WCHAR string:
$tStruct = DllStructCreate("wchar data[" & $iSize & "]", $pData)

Reference data as INT:
$tStruct = DllStructCreate("int data", $pData)

etc...

=====================

Reference the "data" of the struct like:

DllstructGetData($tStruct, 1)
or
DllstructGetData($tStruct, "data")
or
$tStruct.data

 

Edited by TheXman
Link to comment
Share on other sites

@TheXman I use your code as it is.
just add zmq_msg_size & zmq_msg_data to get the binary data.

;Create a msg data struct with data
$vData      = "ABCDEF"
$tData      = DllStructCreate(StringFormat("byte data[%i]", BinaryLen($vData)))
$tData.data = Binary($vData)
ConsoleWrite('+ ' & DllStructGetData($tData, 1) & @crlf)    ;~ 0x414243444546

I want to retrieve the Binary data that is ( 0x414243444546 ) using zmq_msg_data pointer.

try:

$tBinaryBuffer = DllStructCreate("byte data[" & $iSize & "]", $pData)
ConsoleWrite('+ ' & DllStructGetData($tBinaryBuffer, 1) & @crlf)    
;~ but it return 0x010000000000

https://libzmq.readthedocs.io/en/latest/zmq_msg_data.html

Upon successful completion, zmq_msg_data() shall return a pointer to the message content

.

Link to comment
Share on other sites

@jugador

52 minutes ago, jugador said:

I use your code as it is

You may be using my previous example as a guide, but my previous example did not include functions to retrieve the message size or pointer.  So you are asking for help with code that I have not seen and cannot possibly know if it is working correctly.  Therefore, I provided you a theoretical answer to a theoretical problem.  If you'd like a more specific answer as to why your code is not producing the expected results, then providing an actual example, that I can run, would be most helpful.  I don't need a huge example script.  I just need the smallest script that I can run that shows the issue you are having.

example()

Func example()
    Local $tData   = "",  _
          $tStruct = ""
    Local $pData   = 0
    Local $vData   = ""
    Local $iSize   = 0

    ;Create a msg data struct with data
    $vData      = "ABCDEF"
    $tData      = DllStructCreate(StringFormat("byte data[%i]", BinaryLen($vData)))
    $tData.data = Binary($vData)
    ConsoleWrite('+ ' & DllStructGetData($tData, 1) & @crlf)    ;~ 0x414243444546

    ;Get pointer to data struct and size of struct
    $pData   = DllStructGetPtr($tData)
    $iSize   = DllStructGetSize($tData)

    ;Get data as binary
    $tStruct = DllStructCreate(StringFormat("byte data[%i]", $iSize), $pData)
    ConsoleWrite('+ $tStruct.data = ' & $tStruct.data & @crlf) ; 0x414243444546

    ;Get data as a string
    $tStruct = DllStructCreate(StringFormat("char data[%i]", $iSize), $pData)
    ConsoleWrite('+ $tStruct.data = ' & $tStruct.data & @CRLF) ;ABCDEF
EndFunc

Console output:

+ 0x414243444546
+ $tStruct.data = 0x414243444546
+ $tStruct.data = ABCDEF

 

Edited by TheXman
Link to comment
Share on other sites

@TheXman  added zmq_msg_size and zmq_msg_data

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

#cs
    0MQ LibZMQ Github:    https://github.com/zeromq/libzmq
    0MQ LibZMQ Releases:  https://github.com/zeromq/libzmq/releases
    0MQ API Reference:    http://api.zeromq.org/master:_start
#ce

#include <Constants.au3>

;Global Constant(s)
Const $DEBUGGING       = True
Const $ZEROMG_DLL_FILE = @ScriptDir & "\libzmq-v142-mt-4_3_4.dll"
Const $TAG_ZMQ_MSG_T   = "byte msg[64];"

;Global Handle(s)
Global $ghZeroMQDll = -1

;Context Options
Const $ZMQ_IO_THREADS                 = 1, _
      $ZMQ_MAX_SOCKETS                = 2, _
      $ZMQ_SOCKET_LIMIT               = 3, _
      $ZMQ_THREAD_PRIORITY            = 3, _
      $ZMQ_THREAD_SCHED_POLICY        = 4, _
      $ZMQ_MAX_MSGSZ                  = 5, _
      $ZMQ_MSG_T_SIZE                 = 6, _
      $ZMQ_THREAD_AFFINITY_CPU_ADD    = 7, _
      $ZMQ_THREAD_AFFINITY_CPU_REMOVE = 8, _
      $ZMQ_THREAD_NAME_PREFIX         = 9


zeromq_test()

Func zeromq_test()
    Local $sVersion = ""
    Local $vData = ""
    Local $hContext = -1
    Local $tMsg = "", $tData = ""
    Local $pMsg = 0
    Local $iOption = 0, $iOptionValue = 0

    ConsoleWrite("====================" & @CRLF)
    ConsoleWrite("ZeroMQ API Test" & @CRLF)
    ConsoleWrite("====================" & @CRLF)

    ;Get and display 0MQ version
    $sVersion = zmq_version()
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_version ERROR", "DllCall failed with @error = " & @extended)
    ConsoleWrite("ZeroMQ: Version = " & $sVersion & @CRLF)

    ;Create a message struct
    $tMsg       = DllStructCreate($TAG_ZMQ_MSG_T)
    $pMsg       = DllStructGetPtr($tMsg)

    ;Create a msg data struct with data
    $vData      = "ABCDEF"
    $tData      = DllStructCreate(StringFormat("byte data[%i]", BinaryLen($vData)))
    $tData.data = Binary($vData)

    ;Initialize a data message
    zmq_msg_init_data($pMsg, $tData)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_msg_init_data ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: New data msg successfuly initialized" & @CRLF)
;~  $tData = 0 ;Explicitly free data struct memory (not necessary; local resources will be freed when func ends)

    ;~~~~~~~~~~
    Local $iSize = zmq_msg_size($pMsg)
    ConsoleWrite("ZeroMQ: byte: " & $iSize & @CRLF)

    Local $pData = zmq_msg_data($pMsg)
    ConsoleWrite("ZeroMQ: pointer: " & $pData & @CRLF)

    Local $tBinaryBuffer = DllStructCreate("byte data[" & $iSize & "]", $pData)
    ConsoleWrite('+ ' & DllStructGetData($tBinaryBuffer, 1) & @crlf)
    ;~~~~~~~~~~

    ;Get a new 0MQ context handle
    $hContext = zmq_ctx_new()
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_new ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: New context handle successfuly created = " & $hContext & @CRLF)

    ;Set a context option
    $iOption      = $ZMQ_IO_THREADS
    $iOptionValue = 3
    zmq_ctx_set($hContext, $iOption, $iOptionValue)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_set ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: Context option ZMQ_IO_THREADS successfuly set to " & $iOptionValue & @CRLF)

    ;Get a context option
    $iOptionValue = zmq_ctx_get($hContext, $iOption)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_get ERROR", zmq_strerror(@extended))
    ConsoleWrite("ZeroMQ: Context option ZMQ_IO_THREADS successfuly retrieved = " & $iOptionValue & @CRLF)

    ;Get a context option
    $iOptionValue = zmq_ctx_get($hContext, $ZMQ_MAX_MSGSZ)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "zmq_ctx_get ERROR", zmq_strerror(@extended))
    ConsoleWrite(StringFormat("ZeroMQ: Context option ZMQ_MAX_MSGSZ successfuly retrieved = %i (0x%x)", $iOptionValue, $iOptionValue) & @CRLF)

    ;Terminate a 0MQ context handle
    zmq_ctx_term($hContext)
    If @error Then Return MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "Error terminating new context handle. - errno = " & @extended)
    ConsoleWrite("ZeroMQ: Context handle successfuly terminated  = " & $hContext & @CRLF)
EndFunc

;==================================
; Internal/Helper Functions
;==================================

Func get_zmq_dll_handle()
    ;If no dll handle exists yet
    If $ghZeroMQDll = -1 Then
        ;Get global handle to dll
        $ghZeroMQDll = DllOpen($ZEROMG_DLL_FILE)
        If @error Then Exit MsgBox($MB_ICONERROR + $MB_TOPMOST, "ERROR", "DllOpen failed")

        ;Register function to close dll handle on exit
        OnAutoItExitRegister("close_zmq_dll_handle")

        If $DEBUGGING Then ConsoleWrite("(DEBUG) ZeroMQ: DLL handle opened." & @CRLF)
    EndIf

    ;Return global handle to dll
    Return $ghZeroMQDll
EndFunc

Func close_zmq_dll_handle()
    ;Close global handle
    DllClose($ghZeroMQDll)

    If $DEBUGGING Then ConsoleWrite("(DEBUG) ZeroMQ: DLL handle closed" & @CRLF)
EndFunc

;====================================
; ZeroMQ API Function Examples
; - zmq_ctx_new()
; - zmq_ctx_set($hContext, $iOption, $iOptionValue)
; - zmq_ctx_get($hContext, $iOption)
; - zmq_ctx_term($hContext)
; - zmq_errno()
; - zmq_msg_init_data($pMsg, $tData)
; - zmq_strerror($iErrNo)
; - zmq_version()
;====================================

Func zmq_ctx_new()
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "handle", "zmq_ctx_new")
    If @error Then
        Return SetError(1, @error, 0)      ;DllCall failed
    ElseIf $aResult[0] = 0 Then
        Return SetError(2, zmq_errno(), 0) ;API call failed
    EndIf

    ;Return new 0MQ context handle
    Return $aResult[0]
EndFunc

Func zmq_ctx_set($hContext, $iOption, $iOptionValue)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_ctx_set", _
                       "handle", $hContext, _
                       "int",    $iOption, _
                       "int",    $iOptionValue)
    If @error Then
        Return SetError(1, @error, False)      ;DllCall failed
    ElseIf $aResult[0] = -1 Then
        Return SetError(2, zmq_errno(), False) ;API call failed
    EndIf

    ;All is good
    Return True
EndFunc

Func zmq_ctx_get($hContext, $iOption)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_ctx_get", _
                       "handle", $hContext, _
                       "int",    $iOption)
    If @error Then
        Return SetError(1, @error, -1)      ;DllCall failed
    ElseIf $aResult[0] = -1 Then
        Return SetError(2, zmq_errno(), -1) ;API call failed
    EndIf

    ;All is good
    Return $aResult[0]
EndFunc

Func zmq_ctx_term($hContext)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_ctx_term", "handle", $hContext)
    If @error Then
        Return SetError(1, @error, False)      ;DllCall failed
    ElseIf $aResult[0] <> 0 Then
        Return SetError(2, zmq_errno(), False) ;API call failed
    EndIf

    ;All is good
    Return True
EndFunc

Func zmq_errno()
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_errno")
    If @error Then
        Return SetError(1, @error, -1) ;DllCall failed
    EndIf

    ;All is good
    Return $aResult[0]
EndFunc

Func zmq_msg_init_data($pMsg, $tData)
    Local $aResult

    If DllStructGetSize($tData) = 0 Then Return SetError(-1, 0, False) ;No data

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "int", "zmq_msg_init_data", _
                       "ptr",       $pMsg, _                    ;msg
                       "ptr",       DllStructGetPtr($tData), _  ;data
                       "ulong_ptr", DllStructGetSize($tData), _ ;size
                       "ptr",       Null, _                     ;ffn
                       "ptr",       Null)                       ;hint
    If @error Then
        Return SetError(1, @error, False)      ;DllCall failed
    ElseIf $aResult[0] <> 0 Then
        Return SetError(2, zmq_errno(), False) ;API call failed
    EndIf

    ;All is good
    Return True
EndFunc

Func zmq_strerror($iErrNo)
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "str", "zmq_strerror", _
                       "int", $iErrNo)
    If @error Then Return SetError(1, @error, "")

    ;Return version
    Return $aResult[0]
EndFunc

Func zmq_version()
    Local $aResult

    ;Call API
    $aResult = DllCall(get_zmq_dll_handle(), "none", "zmq_version", _
                       "int*", Null, _  ;Major
                       "int*", Null, _  ;Minor
                       "int*", Null)    ;Patch
    If @error Then Return SetError(1, @error, "?.?.?")

    ;Return version
    Return StringFormat("%s.%s.%s", $aResult[1], $aResult[2], $aResult[3])
EndFunc

Func zmq_msg_data($pMsg)
    Local $aResult = DllCall(get_zmq_dll_handle(), "ptr", "zmq_msg_data", "ptr", $pMsg)
    If @error Then Return SetError(1, @error, -1)                       ;~ DllCall failed
    Return $aResult[0]  ;~ return a pointer to the message content.
EndFunc

Func zmq_msg_size($pMsg)
    Local $aResult = DllCall(get_zmq_dll_handle(), "ulong_ptr", "zmq_msg_size", "ptr", $pMsg)
    If @error Then Return SetError(1, @error, -1)                       ;~ DllCall failed
    Return $aResult[0]  ;~ return the size of the message content in bytes.
EndFunc

 

Link to comment
Share on other sites

@jugador

So if you add the lines below to the script that you posted, you get the data as a string, correct?

$tBinaryBuffer = DllStructCreate("char data[" & $iSize & "]", $pData)
ConsoleWrite('+ ' & DllStructGetData($tBinaryBuffer, 1) & @crlf)

This is the output I get:

====================
ZeroMQ API Test
====================
(DEBUG) ZeroMQ: DLL handle opened.
ZeroMQ: Version = 4.3.4
ZeroMQ: New data msg successfuly initialized
ZeroMQ: byte: 6
ZeroMQ: pointer: 0x0000000000752F70
+ 0x414243444546
+ ABCDEF
ZeroMQ: New context handle successfuly created = 0x0000000000755D90
ZeroMQ: Context option ZMQ_IO_THREADS successfuly set to 3
ZeroMQ: Context option ZMQ_IO_THREADS successfuly retrieved = 3
ZeroMQ: Context option ZMQ_MAX_MSGSZ successfuly retrieved = 2147483647 (0x7fffffff)
ZeroMQ: Context handle successfuly terminated  = 0x0000000000755D90
(DEBUG) ZeroMQ: DLL handle closed

 

Edited by TheXman
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...