Jump to content

3rd party DLL access difficulty


marekp
 Share

Go to solution Solved by Danyfirex,

Recommended Posts

I'm trying to use a 3rd party DLL, but with limited success. I can open the DLL and initialise it, but am having trouble with the following DLL function.

It is defined in the documentation as:

2.2.1. LMX_func_api_Get_PnPDeviceInfo
Gets information on devices that can be connected.

[Format]

UINT8 LMX_func_api_Get_PnPDeviceInfo ( PLMX_CONNECT_DEVICE_INFO plmxPnpDevInfo )
[Parameters]

PLMX_CONNECT_DEVICE_INFO plmxPnpDevInfo
Pointer of LMX_CONNECT_DEVICE_INFO structure for storing information on devices that can be connected.

[Return values]

UINT8
LMX_BOOL_TRUE is returned when information on devices that can be connected is got successfully.

The relevant structures are (from the .h files):

typedef struct _tag_LMX_DEVINFO{
    UINT32 dev_Index;
    WCHAR dev_MakerName[DEVINFO_DEF_STRING_MAX];    // Maker Name
    UINT32 dev_MakerName_Length;
    WCHAR dev_ModelName[DEVINFO_DEF_STRING_MAX];    // Model Name
    UINT32 dev_ModelName_Length;
}LMX_DEV_INFO,*PLMX_DEV_INFO;

typedef struct _tag_LMX_CONNECT_DEVICE_INFO{
    UINT32            find_PnpDevice_Count;                            // Number of devices detected //
    PWSTR            find_PnpDevice_IDs[DEVINFO_DEF_ARRAY_MAX];
    LMX_DEV_INFO    find_PnpDevice_Info[DEVINFO_DEF_ARRAY_MAX];        // Detected device information: Maximum DEVINFO_DEF_STRING_MAX //
}LMX_CONNECT_DEVICE_INFO,*PLMX_CONNECT_DEVICE_INFO;

Seemed simple enough - there's one parameter: a structure with some uints, an array [len=512] of strings [len=256], and an array [len=512] of LMX_DEV_INFO structures.

So I tried the following code (I'm only interested in the 1st device, so the other 511 are 'nameless')...

            Local $raw_LMX_DEV_INFO =     "struct;uint;wchar[256];uint;wchar[256];uint;endstruct;"                ; 'nameless' substring for structure

            Local $str_LMX_CONNECT_DEVICE_INFO = ""
            $str_LMX_CONNECT_DEVICE_INFO &= "struct;uint devCount;wchar[512];"                             ; First section...
            $str_LMX_CONNECT_DEVICE_INFO &= "struct;uint devIndex;wchar devMakerName0[256];uint;wchar devModelName0[256];uint;endstruct;"
            $str_LMX_CONNECT_DEVICE_INFO &= _StringRepeat($raw_LMX_DEV_INFO, 511)                        ; ... rest of array of structures...
            $str_LMX_CONNECT_DEVICE_INFO &= "endstruct"                                                    ; ... and terminate.

            Local $s1 = DllStructCreate($str_LMX_CONNECT_DEVICE_INFO)
            Local $r = DllCall($hDll, "bool", "LMX_func_api_Get_PnPDeviceInfo", "ptr", DllStructGetPtr($s1))

            If @error == 0 Then
               ConsoleWrite("Return[0]=" & $r[0] & @CRLF)
               ConsoleWrite("Return[1]=" & $r[1] & @CRLF)

               ConsoleWrite("Device info: Count=" & DllStructGetData($s1, "devCount") & @CRLF)
               ConsoleWrite("Device info: Maker=" & DllStructGetData($s1, "devMakerName0") & @CRLF)
               ConsoleWrite("Device info: Model=" & DllStructGetData($s1, "devModelName0") & @CRLF)

... and I get the result that devCount correctly identifies when I have a device plugged in, but the rest of the structure $s1 appears to be empty/zero. I expected to see devMakerName0 and devModelName0 to contain meaningful strings, but they are empty. When calling the function from a C++ program, the names are correct. So I'm assuming I've done something wrong in the nested structure definition. I've tried a number of variations:

  • I've tried it without the "struct;endstruct" bracketing of the nested structures. No difference.
  • I've tried stdcall and cdecl calling conventions. No difference.
  • I've tried DllCall parameters...
    "struct*", $s1
    ... instead of ...
     "ptr", DllStructGetPtr($s1)

    ... but then $r[1]  is blank rather than a big hex number and devMakerName0 and devModelName0 return zeros. The devCount value is still returned correctly.

Out of ideas now. Can anyone point me in the right direction?

 

 

Link to comment
Share on other sites

1 hour ago, abberration said:

I would start by seeing if an @error returns a code when querying devmakername0 and devmodelname0. 

No errors reported via @error.

I have also scanned the whole structure for values <> 0 and the devCount is the only one.

Link to comment
Share on other sites

Since you're reading struct info from an .h file I'm assuming your dll is written in C, in which case the calling convention is cdecl, not stdcall (the default assumption if omitted) so append :cdecl to the return type ("bool:cdecl"); see DllCall for details. Maybe also check potential struct member alignment issues (see DllstructCreate).

Edited by RTFC
Link to comment
Share on other sites

21 minutes ago, RTFC said:

Since you're reading struct info from an .h file I'm assuming your dll is written in C, in which case the calling convention is cdecl, not stdcall (the default assumption if omitted) so append :cdecl to the return type ("bool:cdecl"); see DllCall for details. Maybe also check potential struct member alignment issues (see DllstructCreate).

My second bullet point was that I've already tried cdecl with no (obvious) difference 😉. Is cdecl always the case for C/C++ functions?

As for the alignment idea: Yes, it did occur to me that it might be an issue, but not being versed in C++ (haven't used it in years) I wasn't sure what I was going to check anyway. That's why I tried the 'scan' of the whole structure hoping to find some 'wrong values in the wrong places', but again, everything apart from the devCount value is zero. So I assumed alignment wasn't the problem.

Link to comment
Share on other sites

Try with the structure like this:

Local Const $sTag_LMX_CONNECT_DEVICE_INFO = "uint find_PnpDevice_Count;ptr find_PnpDevice_IDs[256];byte find_PnpDevice_Info[" & 2060 * 256 & "];"

Then you can cast LMX_DEV_INFO (find_PnpDevice_Info).

Saludos

 

Link to comment
Share on other sites

9 hours ago, Danyfirex said:

Try with the structure like this:

Local Const $sTag_LMX_CONNECT_DEVICE_INFO = "uint find_PnpDevice_Count;ptr find_PnpDevice_IDs[256];byte find_PnpDevice_Info[" & 2060 * 256 & "];"

Then you can cast LMX_DEV_INFO (find_PnpDevice_Info).

Saludos

 

That certainly is on the right track for me, in that a scan of the $sTag_LMX_CONNECT_DEVICE_INFO described structure now returns two more location with non-zero data. However I have two questions:

1. What does byte find_PnpDevice_Info[" & 2060 * 256 & "] mean? I couldn't find any reference to such syntax. A string where I would expect a uint (length)?

2. How do I do the casting of the find_PnpDevice_Count array into a structure to get the devMakerName and devModelName of the first entry? I assume I can get a byte array with ...

Local $s2 = DllStructGetData($s1, "find_PnpDevice_Info")

... but how do I cast $s2 to LMX_DEV_INFO? All I have is a string descriptor not a typedef. Again, I think I'm missing some AutoIt syntax.

Link to comment
Share on other sites

27 minutes ago, marekp said:

 

1. What does byte find_PnpDevice_Info[" & 2060 * 256 & "] mean? I couldn't find any reference to such syntax. A string where I would expect a uint (length)?

 

I'm now doubly confused. I tried to extract the find_PnpDevice_Info byte array (in anticipation of casting it) ...

Local $s2 = DllStructGetData($s1, "find_PnpDevice_Info")

consolewrite("Extracted byte array length = " & Ubound($s2))

... and the length appears to be zero!

Link to comment
Share on other sites

53 minutes ago, marekp said:

1. What does byte find_PnpDevice_Info[" & 2060 * 256 & "] mean? I couldn't find any reference to such syntax. A string where I would expect a uint (length)?

Well, that was embarrassing! I should read more carefully! However, the number appear incorrect: The LMX_DEV_INFO structure has 3xUINTs and 2xWCHAR arrays(length=256) which I make to be 1036 bytes. And then the main structure has 512 of the LMX_DEV_INFO structures, so I think it should be  find_PnpDevice_Info[" & 1036 * 512 & "].

Anyway, I tried that, but Ubound(DllStructGetData($s1, "find_PnpDevice_Info")) still returns zero, so I'm still stuck.

Link to comment
Share on other sites

Learn about AutoIt first. You are walking with your hand in your eyes.

 

 

Saludos

Link to comment
Share on other sites

1 hour ago, Danyfirex said:

Learn about AutoIt first. You are walking with your hand in your eyes.

 

Where? I've looked through the documentation and it's pretty rudimentary at best (particularly around DLLs and debugging) - so I started asking (aka trying to learn) here. I'm not asking anyone to write my code, I'm just looking for useful hints as to where to look. And "Learn about AutoIt first" is NOT a useful hint, sorry. I'm not here for fun, it's not a game, I'm just trying to get a job done as efficiently as possible. I though AutoIt might be a good way, but perhaps not.

Link to comment
Share on other sites

You're mixing Ubound with Dll structure. They are not same data type. 

So make sure to understand how structure works. then It would be easy to handle it.

once you have it use find_PnpDevice_Count reference to cast each LMX_DEV_INFO.

I'll write a sample when electric power energy is back here 😬

Saludos

 

 

 

 

Link to comment
Share on other sites

show me how is defined DEVINFO_DEF_STRING_MAX and DEVINFO_DEF_ARRAY_MAX.

 

Saludos

Edited by Danyfirex
Link to comment
Share on other sites

28 minutes ago, Danyfirex said:

You're mixing Ubound with Dll structure. They are not same data type. 

According to the original structure definition DllStructGetData($s1, "find_PnpDevice_Info") should return a byte array (length=1036*512) so Ubound should return 530432.

 

Link to comment
Share on other sites

27 minutes ago, Danyfirex said:

show me how is defined DEVINFO_DEF_STRING_MAX and DEVINFO_DEF_ARRAY_MAX.

They are defined in a .h file associated with the 3rd party DLL:

#define DEVINFO_DEF_ARRAY_MAX    512        // The number of ARRAY (other than String) is represented by ULONG, but in this application it is possible to represent up to 512 //
#define DEVINFO_DEF_STRING_MAX    256        // Since ARRAY (String only) can be represented by UCHAR, we can express up to 256 expressed in UCHAR //

I just stated those values in the original post for simplicity.

Link to comment
Share on other sites

  • Solution

This is what I meant:

_Test()

Func _Test()
    Local Const $sTag_LMX_DEV_INFO = "struct;uint dev_Index;wchar dev_MakerName[256];uint dev_MakerName_Length;wchar dev_ModelName[256];uint dev_ModelName_Length;endstruct;"                       ; 'nameless' substring for structure
    Local Const $sTag_LMX_CONNECT_DEVICE_INFO = "uint find_PnpDevice_Count;ptr find_PnpDevice_IDs[512];byte find_PnpDevice_Info[" & 1036 * 512 & "];"
    
    
    ;structure where I have the array of device info structure
    Local $tLMX_CONNECT_DEVICE_INFO = DllStructCreate($sTag_LMX_CONNECT_DEVICE_INFO)
    ;your dllcall here
    ;then cast the LMX_DEV_INFO

    ;get number of devices
    Local $iDevicesCount = DllStructGetData($tLMX_CONNECT_DEVICE_INFO, "find_PnpDevice_Count")
    ConsoleWrite("$iDevicesCount: " & $iDevicesCount & @CRLF)

    ;casting each device info
    Local $tDevInfo = 0
    For $i = 0 To $iDevicesCount - 1
        $tDevInfo = DllStructCreate($sTag_LMX_DEV_INFO, DllStructGetPtr($tLMX_CONNECT_DEVICE_INFO, "find_PnpDevice_Info") + ($i * 1036))
        ;print information
        ConsoleWrite("dev_Index: " & DllStructGetData($tDevInfo, "dev_Index") & @CRLF)
        ConsoleWrite("dev_MakerName: " & DllStructGetData($tDevInfo, "dev_MakerName") & @CRLF)
    Next

EndFunc   ;==>_Test

Saludos

Link to comment
Share on other sites

5 hours ago, Danyfirex said:

This is what I meant: ...

Thanks for your effort, I've now isolated my mistake: I'd translated the C/C++ PWSTR type ' to 'uint' instead of 'ptr'. Probably would have got away with it on a 32b system, but not on my 64b one.

Changed that and not only does your version works, but my original version works too.

Thanks again.

Link to comment
Share on other sites

Looks like I still have a problem 😞

The original DLL call now works OK, but on the next one DllCall crashes (kills application). This is the current code based on the input above...
 

#AutoIt3Wrapper_UseX64=Y

$hDll = DllOpen("Lmxptpif.dll")
if @error then ConsoleWrite("ERROR: " & @error)
ConsoleWrite("DLL handle: " & $hDll & @CRLF)

Local $r = DllCall($hDll, "none", "LMX_func_api_Init")
ConsoleWrite("API initialised" & @CRLF)

Local Const $sTag_LMX_DEV_INFO = "struct;uint dev_Index;wchar dev_MakerName[256];uint dev_MakerName_Length;wchar dev_ModelName[256];uint dev_ModelName_Length;endstruct;"                       ; 'nameless' substring for structure
Local Const $sTag_LMX_CONNECT_DEVICE_INFO = "uint find_PnpDevice_Count;ptr find_PnpDevice_IDs[512];byte find_PnpDevice_Info[" & 1036 * 512 & "];"


;structure where I have the array of device info structure
Local $tLMX_CONNECT_DEVICE_INFO = DllStructCreate($sTag_LMX_CONNECT_DEVICE_INFO)


;DllCall------------------------------------------------------------------------------------------------------------------------------
Local $r = DllCall($hDll, "byte:cdecl", "LMX_func_api_Get_PnPDeviceInfo", "ptr", DllStructGetPtr($tLMX_CONNECT_DEVICE_INFO))


;then cast the LMX_DEV_INFO
;get number of devices
Local $iDevicesCount = DllStructGetData($tLMX_CONNECT_DEVICE_INFO, "find_PnpDevice_Count")
ConsoleWrite("$iDevicesCount: " & $iDevicesCount & @CRLF)

;casting each device info
Local $tDevInfo = 0
For $i = 0 To $iDevicesCount - 1
    $tDevInfo = DllStructCreate($sTag_LMX_DEV_INFO, DllStructGetPtr($tLMX_CONNECT_DEVICE_INFO, "find_PnpDevice_Info") + ($i * 1036))
    ;print information
    ConsoleWrite("dev_Index: " & DllStructGetData($tDevInfo, "dev_Index") & @CRLF)
    ConsoleWrite("dev_MakerName: " & DllStructGetData($tDevInfo, "dev_MakerName") & @CRLF)
Next

ConsoleWrite("Selecting device..." & @CRLF)
; Select the first (and only) device...


;DllCall------------------------------------------------------------------------------------------------------------------------------
Local $index = 0
Local $r = DllCall($hDll, "byte:cdecl", "LMX_func_api_Select_PnPDevice", "uint", $index, "ptr", DllStructGetPtr($tLMX_CONNECT_DEVICE_INFO))


ConsoleWrite("Device selected" & @CRLF)

I've highlighted the two DllCall invocation - the first one now works, the second does not.

The C/C++ definition for the two DLL function highlighted above are:

/////////////////////////////////////////////////////////////////////
// 
// Func     :LMX_func_api_Get_PnPDeviceInfo
// 
// Summ.    :Get PnP connected device information (for WPD)
// Input    :
//             PLMX_CONNECT_DEVICE_INFO        plmxPnpDevInfo;
//
// Output   :
//          UINT8       LMX_BOOL_TRUE :Acquisition success
//                      LMX_BOOL_FALSE:Acquisition failure
// 
UINT8 LMX_API LMX_func_api_Get_PnPDeviceInfo(
    PLMX_CONNECT_DEVICE_INFO plmxPnpDevInfo,
    UINT32                *retError = NULL
);

/////////////////////////////////////////////////////////////////////
// 
// Func     :LMX_func_api_Select_PnPDevice
// 
// Summ.    :Select the specified device from the PnP connected device (for WPD)
// Input    :
//          UINT32 dwTargetIndex
//             PLMX_CONNECT_DEVICE_INFO        plmxPnpDevInfo;
//
// Output   :
//          UINT8       LMX_BOOL_TRUE :Acquisition success
//                      LMX_BOOL_FALSE:Acquisition failure
// 
UINT8 LMX_API LMX_func_api_Select_PnPDevice(
    UINT32 dwDevIndex,
    PLMX_CONNECT_DEVICE_INFO plmxPnpDevInfo,
    UINT32                *retError = NULL
);

... and the two structures are:

#define DEVINFO_DEF_ARRAY_MAX    512        // The number of ARRAY (other than String) is represented by ULONG, but in this application it is possible to represent up to 512 //
#define DEVINFO_DEF_STRING_MAX    256        // Since ARRAY (String only) can be represented by UCHAR, we can express up to 256 expressed in UCHAR //


typedef struct _tag_LMX_DEVINFO{
    UINT32 dev_Index;
    WCHAR dev_MakerName[DEVINFO_DEF_STRING_MAX];    // Maker Name
    UINT32 dev_MakerName_Length;
    WCHAR dev_ModelName[DEVINFO_DEF_STRING_MAX];    // Model Name
    UINT32 dev_ModelName_Length;
}LMX_DEV_INFO,*PLMX_DEV_INFO;

typedef struct _tag_LMX_CONNECT_DEVICE_INFO{
    UINT32            find_PnpDevice_Count;                            // Number of devices detected //
    PWSTR            find_PnpDevice_IDs[DEVINFO_DEF_ARRAY_MAX];
    LMX_DEV_INFO    find_PnpDevice_Info[DEVINFO_DEF_ARRAY_MAX];        // Detected device information: Maximum DEVINFO_DEF_STRING_MAX //
}LMX_CONNECT_DEVICE_INFO,*PLMX_CONNECT_DEVICE_INFO;

The call/return parameters only differ in that the second function expects an extra UINT32 index as the first argument. The first one return the correct data, the second one never returns and crashes.

Any hints as to what might be wrong here?

I've tried variations on return type (byte/boolean), calling convention (stdcall/cdecl), different ways of defining the structure definition string. All to no avail

TIA...

 

 

 

Link to comment
Share on other sites

Just for completeness, here the C/C++ code that uses the DLL and runs OK...

int SelectDevice(void)
{
    UINT8 ret;
    UINT32 retError;

    // Get device info
    LMX_CONNECT_DEVICE_INFO devInfo;
    ret = LMX_func_api_Get_PnPDeviceInfo(&devInfo, &retError);

    std::string key;

    std::cout << std::endl;
    std::cout << "Select Device:" << std::endl;
    for (UINT32 i = 0; i < devInfo.find_PnpDevice_Count; i++) {
        std::wcout << "  " << i + 1 << ") "
            << devInfo.find_PnpDevice_Info[i].dev_ModelName
            << std::endl;
    }
    std::cout << "  q) quit" << std::endl;
    std::cout << "? ";
    std::cin >> key;
    if (key.c_str()[0] == 'q') {
        return -1;
    }

    int index = std::stoi(key) - 1;

    // Select device
    ret = LMX_func_api_Select_PnPDevice(index, &devInfo, &retError);

    return index;
}

 

 

 

Link to comment
Share on other sites

As a further data point, I've replaced the complex structure definition with one big anonymous byte array (and removed later references to named elements) and the issue is still the same: The first DllCall works fine and the second one crashes.

The 1 vs 2 argument difference made me suspect calling convention issues, but as I understand it Windows x64 has only one, so that's unlikely. Correct? Seems to be support by the fact that the presence or absence of the cdecl declaration make no difference to the code behaviour?

 

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...