marekp Posted November 27, 2022 Share Posted November 27, 2022 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 More sharing options...
abberration Posted November 28, 2022 Share Posted November 28, 2022 I would start by seeing if an @error returns a code when querying devmakername0 and devmodelname0. Easy MP3 | Software Installer | Password Manager Link to comment Share on other sites More sharing options...
marekp Posted November 28, 2022 Author Share Posted November 28, 2022 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 More sharing options...
RTFC Posted November 28, 2022 Share Posted November 28, 2022 (edited) 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 November 28, 2022 by RTFC My Contributions and Wrappers Spoiler BitMaskSudokuSolver BuildPartitionTable CodeCrypter CodeScanner DigitalDisplay Eigen4AutoIt FAT Suite HighMem MetaCodeFileLibrary OSgrid Pool RdRand SecondDesktop SimulatedAnnealing Xbase I/O Link to comment Share on other sites More sharing options...
marekp Posted November 28, 2022 Author Share Posted November 28, 2022 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 More sharing options...
Danyfirex Posted November 29, 2022 Share Posted November 29, 2022 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 Danysys.com AutoIt... UDFs: VirusTotal API 2.0 UDF - libZPlay UDF - Apps: Guitar Tab Tester - VirusTotal Hash Checker Examples: Text-to-Speech ISpVoice Interface - Get installed applications - Enable/Disable Network connection PrintHookProc - WINTRUST - Mute Microphone Level - Get Connected NetWorks - Create NetWork Connection ShortCut Link to comment Share on other sites More sharing options...
marekp Posted November 29, 2022 Author Share Posted November 29, 2022 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 More sharing options...
marekp Posted November 29, 2022 Author Share Posted November 29, 2022 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 More sharing options...
marekp Posted November 29, 2022 Author Share Posted November 29, 2022 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 More sharing options...
Danyfirex Posted November 29, 2022 Share Posted November 29, 2022 Learn about AutoIt first. You are walking with your hand in your eyes. Saludos mLipok 1 Danysys.com AutoIt... UDFs: VirusTotal API 2.0 UDF - libZPlay UDF - Apps: Guitar Tab Tester - VirusTotal Hash Checker Examples: Text-to-Speech ISpVoice Interface - Get installed applications - Enable/Disable Network connection PrintHookProc - WINTRUST - Mute Microphone Level - Get Connected NetWorks - Create NetWork Connection ShortCut Link to comment Share on other sites More sharing options...
marekp Posted November 29, 2022 Author Share Posted November 29, 2022 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 More sharing options...
Danyfirex Posted November 29, 2022 Share Posted November 29, 2022 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 Danysys.com AutoIt... UDFs: VirusTotal API 2.0 UDF - libZPlay UDF - Apps: Guitar Tab Tester - VirusTotal Hash Checker Examples: Text-to-Speech ISpVoice Interface - Get installed applications - Enable/Disable Network connection PrintHookProc - WINTRUST - Mute Microphone Level - Get Connected NetWorks - Create NetWork Connection ShortCut Link to comment Share on other sites More sharing options...
Danyfirex Posted November 29, 2022 Share Posted November 29, 2022 (edited) show me how is defined DEVINFO_DEF_STRING_MAX and DEVINFO_DEF_ARRAY_MAX. Saludos Edited November 29, 2022 by Danyfirex Danysys.com AutoIt... UDFs: VirusTotal API 2.0 UDF - libZPlay UDF - Apps: Guitar Tab Tester - VirusTotal Hash Checker Examples: Text-to-Speech ISpVoice Interface - Get installed applications - Enable/Disable Network connection PrintHookProc - WINTRUST - Mute Microphone Level - Get Connected NetWorks - Create NetWork Connection ShortCut Link to comment Share on other sites More sharing options...
marekp Posted November 29, 2022 Author Share Posted November 29, 2022 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. Danyfirex 1 Link to comment Share on other sites More sharing options...
marekp Posted November 29, 2022 Author Share Posted November 29, 2022 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 More sharing options...
Solution Danyfirex Posted November 29, 2022 Solution Share Posted November 29, 2022 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 Danysys.com AutoIt... UDFs: VirusTotal API 2.0 UDF - libZPlay UDF - Apps: Guitar Tab Tester - VirusTotal Hash Checker Examples: Text-to-Speech ISpVoice Interface - Get installed applications - Enable/Disable Network connection PrintHookProc - WINTRUST - Mute Microphone Level - Get Connected NetWorks - Create NetWork Connection ShortCut Link to comment Share on other sites More sharing options...
marekp Posted November 29, 2022 Author Share Posted November 29, 2022 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 More sharing options...
marekp Posted November 30, 2022 Author Share Posted November 30, 2022 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... expandcollapse popup#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 More sharing options...
marekp Posted November 30, 2022 Author Share Posted November 30, 2022 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 More sharing options...
marekp Posted November 30, 2022 Author Share Posted November 30, 2022 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 More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now