klausgunther Posted December 27, 2012 Posted December 27, 2012 Hello,I'm a newcomer to AutoIt. I wish to use AutoIt as quick test bank for Delphi DLLs which I'm developing quite heavily, as supplements to a freeware Basic clone. My development environment is MS Windows XP Pros SP3, with Delphi 6 Personal Edition.Now, some of my DLL functions need a string pointer as inpur parameter. The function than modifies the string in-memory, using de passed string pointer as address to the first data bye of the string. I made certain that enough space is reserved where the pointer points to, so there is no risk of data corruption. All my functions return a single 32 bit signed integer value.The first check I tried, tries to call a function defined in Delphi as follows:function KGFdllVersion(pout: ptruint):integer; stdcall; export;Thus, my function accepts a pointer pout to the first byte of a string (25 bytes fixed length). A 25 byte Ascii identification string will be returned ad that address. Ands the function will return a numeric integer value (it will actually be 190).So, after looking at the documentation, I came up with the following script:Local $dll, $ver, $sver $sver = "1234567890123456789012345"+chr(0) Local $stString = DllStructCreate("char v[26]") DllStructSetData($stString, 1, $sver) $dll = DllOpen("AutoIt_test.dll") $ver = DllCall($dll, "int", "KGFdllVersion", "ptr", DllStructGetPtr($stString)) DllClose($dll) MsgBox(0,"",$ver[0]) MsgBox(0,"",DllStructGetData($stString,1))The initial value of $sver is just a space holder, with a security string stop at the end. I discovered that I fave to use the DllStruct commands to produce a real pointer. But problem: I only get nice memory violations. Calling the same function with the target Basic langage works just file, so the DLL isn't the problem. Just for information, here is the calling code in the Basic program:dim ver%, sver$ sver$ = "1234567890123456789012345" dll_on "AutoIt_test.dll" ver% = dll_call1("KGFdllVersion",adr(sver$)) message str$(ver%)+" = " + sver$When I replace line 6 with$ver = DllCall($dll, "int", "KGFdllVersion", "str*", $stString)there is no more crash, but the reserved space is unchanged. Obviously, the text string written by the DLL function goes somewhere, but not into my string variable.Now, the question is: what have I missed ? I checked within the DLL function: both version do crash before I even can display the firct check message in the dll function. replacing "str*" with "ptr* (nonsens, I know), there is no more crash, the returned integer value is correct, but obviously the value of $stString hansn't be changed.
funkey Posted December 27, 2012 Posted December 27, 2012 Try this: $ver = DllCall($dll, "int", "KGFdllVersion", "str", "") ConsoleWrite($ver[1] & @CRLF) Programming today is a race between software engineers striving tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
klausgunther Posted December 27, 2012 Author Posted December 27, 2012 Result: the fonction is called, there is no crash, the numerical value returns fine, but I don't have the string modified through the pointer. This is obvious, because in your proposition, you didn't pass anything as pointer - just an empty string.I uploaded a ZIP file with all relevent information here: http://www.4shared.com/zip/A2A9K4oz/AutoIt_test.htmlThis file contains: http://www.4shared.com/photo/phOKfVyb/AutoIt_test_content.htmlAutoIt_test.dbr and TextToPanoramic.pas are the Delphi sources of a working test version of my DLL.Auto_It_test.dll is the corresponding DLL.Use_AutoIt_test.bas and Use_AutoIt_test.exe are source and executable of a working basic program showing what the result should be.test.au3 is the source of my AutoIt script. There are 3 tenatives to call the DLL function. The first two are commented out, the third one is your suggestionWhat it should do: pass a pointer (the memory address) of the first data byte os $sver as parameter to the function. The function will override 25 bytes starting at that memory address.
funkey Posted December 27, 2012 Posted December 27, 2012 From helpfile: str an ANSI string (a minimum of 65536 chars is allocated). If you pass an empty string, an pointer is generated. You receive the result of the 'out' parameter in the returning array. Programming today is a race between software engineers striving tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
klausgunther Posted December 27, 2012 Author Posted December 27, 2012 It doesn't behave so. Here is the result:http://www.4shared.com/photo/ppmkB5XM/AutoIt_-_test_empty_string_as_.htmlIn this picture, you see the actual source and the output of the first MsgBox. Nothing is in $ver[1].
funkey Posted December 27, 2012 Posted December 27, 2012 I can not open your links on this PC. But this method should work! Maybe your DLL doesn't work fine. Here an working ByRef example for strings and dword in DllCall: Global $aRet = DllCall("kernel32.dll", "BOOL", "GetComputerName", "str", "", "DWORD*", 65535) MsgBox(64, "GetComputerName", "ComputerName: " & $aRet[1] & @CRLF & "Length: " & $aRet[2] & " characters") Programming today is a race between software engineers striving tobuild bigger and better idiot-proof programs, and the Universetrying to produce bigger and better idiots.So far, the Universe is winning.
klausgunther Posted December 27, 2012 Author Posted December 27, 2012 Sorry, the DLL is working fine. Have a look at this picture. It shows the Basic program using the DLL, and the output of both the returned integer value AND the string modified through its address specified via adr(sver$):http://www.4shared.com/photo/57zKjAyY/Result_In_Basic.htmlI intercepted, within the DLL, the value passed as parameter and which is supposed to be a string address. Yes, there is some non-zero value, but this value does NOT point to the first byte of the string ! I don't know where it points, but not where it's supposed to.
Andreik Posted December 27, 2012 Posted December 27, 2012 Can you post the delphi code of that function?
klausgunther Posted December 27, 2012 Author Posted December 27, 2012 This will be my last post until 28 December 2012 - 01:05 AM. The forum is blocking me !So, I'll try to be as clear as possible.I stopped the DLL execution right un entry into the function. And I dumped the memory starting at the address passed as first (and only) parameter.Now, have a look on the following AutoIt source:Local $dll, $ver, $sver $sver = "1234567890123456789012345"+chr(0) Local $stString = DllStructCreate("char v[26]") DllStructSetData($stString, 1, $sver) $dll = DllOpen("AutoIt_test.dll") $ver = DllCall($dll, "int", "KGFdllVersion", "ptr", DllStructGetPtr($stString)) ;$ver = DllCall($dll, "int", "KGFdllVersion", "str*", $stString) ;$ver = DllCall($dll, "int", "KGFdllVersion", "str", "") DllClose($dll) MsgBox(0,"","result=" & $ver[0] & @crlf & "param1=" & $ver[1]) MsgBox(0,"",DllStructGetData("returned string=" & $stString,1))When I use one of the two lines commented out, the address points to a random memory area, There is no meaningfull data. And in neither case, the data I write into memory starting at this address will be returned in $ver[1].With the shown version (using the DllStruct construction tu build a pointer, the situation seems slightly better, because the address points effectively to the first byte ou my data loaded into $sver. So, this seems quite nice. The pointer is OK.But (and unfortunately there are 2 BUT's):1. after the first byte of my data, a byte width déciumal value 46 is inserted. My other data bytes follow normally. So, I get 1 . 2 3 4 5 etc instead of 1 2 3 4 5 etc. Strange, isn't it ?2. while I cand read the data, I can't write into it. AutoIt crashes, and the information is in this picture:http://www.4shared.com/photo/egX3Zk98/AutoIt_crash.html
Andreik Posted December 27, 2012 Posted December 27, 2012 So, I get 1 . 2 3 4 5 etc instead of 1 2 3 4 5 etc. Strange, isn't it ?Well, normally this would be strange but as long we cannot see how this function really work I wouldn't say this is strange. Actually for some reason after calling this function in that way you have no longer a string but a number in scientific notation.If you say the parameter is a pointer to a string your first example call should work. But returning no meaningfull data if I were you I would check the function, probably something is wrong.
AdmiralAlkex Posted December 27, 2012 Posted December 27, 2012 These two works for me: $ver = DllCall($dll, "int", "KGFdllVersion", "ptr*", DllStructGetPtr($stString)) or $ver = DllCall($dll, "int", "KGFdllVersion", "str*", $sver) Also remove the Chr(0). I think you meant to concatenate it (...45" & Chr(0)), not add it (math is a harsh mistress), but it doesn't seem to be needed anyway. .Some of my scripts: ShiftER, Codec-Control, Resolution switcher for HTC ShiftSome of my UDFs: SDL UDF, SetDefaultDllDirectories, Converting GDI+ Bitmap/Image to SDL Surface
Moderators Melba23 Posted December 27, 2012 Moderators Posted December 27, 2012 klausgunther,This will be my last post until 28 December 2012 - 01:05 AM. The forum is blocking me !Not any more - I have lifted the restriction for you. M23 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 columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area
klausgunther Posted December 27, 2012 Author Posted December 27, 2012 @AdmiralAlkex:your hint was decisif - the "+chr(0)" was clearly a newbe error. At least, I should have coded "& chr(0), just as I did it in my MsgBox statements.With that modification, and$ver = DllCall($dll, "int", "KGFdllVersion", "str*", $sver)It works well. I recover the result string in $ver[1]. But$ver = [url="http://www.autoitscript.com/autoit3/docs/functions/DllCall.htm"]DllCall[/url]($dll, "int", "KGFdllVersion", "ptr*", [url="http://www.autoitscript.com/autoit3/docs/functions/DllStructGetPtr.htm"]DllStructGetPtr[/url]($stString))doesn't work - the data of the first element os stString is NOT overwritten !Here is my new AutoIt code:Local $dll, $ver, $sver $sver = "1234567890123456789012345" Local $stString = DllStructCreate("char v[25]") DllStructSetData($stString, 1, $sver) $dll = DllOpen("AutoIt_test.dll") ; *** working code $ver = DllCall($dll, "int", "KGFdllVersion", "str*", $sver) $sver = $ver[1] MsgBox(0,"","A: result=" & $ver[0] & @crlf & "param1=" & $ver[1] & " $sver=" & $sver) ; *** NOT working code - the data element 1 of $stString is NOT overwritten ! $ver = DllCall($dll, "int", "KGFdllVersion", "ptr*", DllStructGetPtr($stString)) MsgBox(0,"","B: result=" & $ver[0] & @crlf & "$stString=" & DllStructGetData($stString, 1, $sver)) DllClose($dll)@Andreik:here is the Delhi code. First the AutoIt_test.dpr:expandcollapse popuplibrary AutoIt_test; { Remarque importante concernant la gestion de mémoire de DLL : ShareMem doit être la première unité de la clause USES de votre bibliothèque ET de votre projet (sélectionnez Projet-Voir source) si votre DLL exporte des procédures ou des fonctions qui passent des chaînes en tant que paramètres ou résultats de fonction. Cela s'applique à toutes les chaînes passées de et vers votre DLL --même celles qui sont imbriquées dans des enregistrements et classes. ShareMem est l'unité d'interface pour le gestionnaire de mémoire partagée BORLNDMM.DLL, qui doit être déployé avec vos DLL. Pour éviter d'utiliser BORLNDMM.DLL, passez les informations de chaînes avec des paramètres PChar ou ShortString. }uses SysUtils, Classes, Dialogs, // just used for debugging purposes TextToPanoramic in 'TextToPanoramic.pas' ; {$R *.res}function KGFdllVersion(pout: integer):integer; stdcall; export; var ver: string; pver: pointer; i: array [0..8] of byte; pi: pbyte; begin { start of memory dump starting at the address passed as parameter } pi := pointer(pout); i[0] := pi^; inc(pi); i[1] := pi^; inc(pi); i[2] := pi^; inc(pi); i[3] := pi^; inc(pi); i[4] := pi^; inc(pi); i[5] := pi^; inc(pi); i[6] := pi^; inc(pi); i[7] := pi^; inc(pi); i[8] := pi^; showmessage('hello '+inttostr(i[0])+' '+inttostr(i[1])+' '+inttostr(i[2])+' '+inttostr(i[3])+' '+inttostr(i[4]) +' '+inttostr(i[5])+' '+inttostr(i[6])+' '+inttostr(i[7])+' '+inttostr(i[8])); { end of dump - section to be suppressed }{ this is the real function code: } ver := 'KGF.dll V01.90 22/12/2012'; { taille: 25 caractères} pver := @ver; CopyTextToPanoramic(integer(pver),0,pout); result := 190; { et la valeur numérique, à diviser par 100} end; exports KGFdllVersion;begin end.And the internal TextToPanoramic.pas unit:unit TextToPanoramic; { Cette unité contient la procédure CopyTextToPanoramic(source,offset,destination); source et destination sont des pointeurs sur des strings Panoramic ou des PChar en Delphi. offset est le décalage à partir duquel on écrit dans la destination ( 0 signifie depuis le début). La copie s'arrêtre automatiquement dès que la fin d'un des strings est rencontré. } interface procedure CopyTextToPanoramic(inp, offs, out:integer); stdcall;implementation procedure CopyTextToPanoramic(inp, offs, out:integer); stdcall; var xout,yinp: pchar; n: integer; begin xout := pchar(pstring(out)^); yinp := pchar(pstring(inp)^); if offs>0 then begin for n:=1 to offs do begin inc(xout); if (xout^ = #0) then exit { sortir si l'offset va à la fin} end end; while (xout^ <> #0) and (yinp^ <> #0) do begin xout^ := yinp^; inc(xout); inc(yinp); end; end; end.@Melba23I'm very gratefull to you for your intervention !
klausgunther Posted December 27, 2012 Author Posted December 27, 2012 Oups... the second code citation should be: $ver = DllCall($dll, "int", "KGFdllVersion", "ptr*", DllStructGetPtr($stString)) How can I modify a posted message, for instance to correct an error like that ?
Moderators Melba23 Posted December 28, 2012 Moderators Posted December 28, 2012 klausgunther,How can I modify a posted messageBy using the "Edit" button that appears at the bottom right of the post alongside the "Quote" buttons when you place your mouse in that area. M23 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 columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area
AdmiralAlkex Posted December 28, 2012 Posted December 28, 2012 ... With that modification, and $ver = DllCall($dll, "int", "KGFdllVersion", "str*", $sver) It works well. I recover the result string in $ver[1]. But $ver = [url="http://www.autoitscript.com/autoit3/docs/functions/DllCall.htm"]DllCall[/url]($dll, "int", "KGFdllVersion", "ptr*", [url="http://www.autoitscript.com/autoit3/docs/functions/DllStructGetPtr.htm"]DllStructGetPtr[/url]($stString)) doesn't work - the data of the first element os stString is NOT overwritten ! ... I am not sure what you are expecting from that DllStructGetData() line so I can only suggest to remove that $sver. The third parameter is the index of an array to get, so passing the string "KGF.dll V01.90 22/12/2012" is not really going to do much good. This: MsgBox(0,"","B: result=" & $ver[0] & @crlf & "$stString=" & DllStructGetData($stString, 1)) Will show this: KGF.dll V01.90 22/12/2012 If that's not what you want, then I don't know. I don't speak Delphi, nor French. .Some of my scripts: ShiftER, Codec-Control, Resolution switcher for HTC ShiftSome of my UDFs: SDL UDF, SetDefaultDllDirectories, Converting GDI+ Bitmap/Image to SDL Surface
klausgunther Posted December 28, 2012 Author Posted December 28, 2012 (edited) How do you get this result ? For clarity, here is my complete code: Local $dll, $ver, $sver $sver = "1234567890123456789012345" Local $stString = DllStructCreate("char v[25]") DllStructSetData($stString, 1, $sver) $dll = DllOpen("AutoIt_test.dll") ; *** working code $ver = DllCall($dll, "int", "KGFdllVersion", "str*", $sver) $sver = $ver[1] MsgBox(0,"","A: result=" & $ver[0] & @crlf & "param1=" & $ver[1] & " $sver=" & $sver) ; *** NOT working code - the data element 1 of $stString is NOT overwritten ! $ver = DllCall($dll, "int", "KGFdllVersion", "ptr*", DllStructGetPtr($stString)) MsgBox(0,"","B: result=" & $ver[0] & @crlf & "$stString=" & DllStructGetData($stString, 1)) MsgBox(0,"","B1: " & $ver[1]) DllClose($dll) I used the line you suggested. The first element of ste structure is still unchanged, abd $ver[1] does not contain something meaningfull. I always thought that writing bytes to a memory address will change these bytes. What I need, is replacing a string partially or fully in-memory, not create a new copy as in the "working" example. That's the reason I used the DllStruc stuff because there was a way to retrieve a pointer. But maybe I'm completely blind, not seeing the obvious... Edited December 28, 2012 by klausgunther
Andreik Posted December 28, 2012 Posted December 28, 2012 (edited) AdmiralAlkex pointed you in right direction, check my comments in code. Local $dll, $ver, $sver $sver = "1234567890123456789012345" Local $stString = DllStructCreate("char v[25]") DllStructSetData($stString, 1, $sver) $dll = DllOpen("AutoIt_test.dll") $ver = DllCall($dll, "int", "KGFdllVersion", "ptr*", DllStructGetPtr($stString)) #cs $ver[0] - contain returned int value of your function $ver[1] - is the pointer of the structure (so cannot be something meaningfull for you but for application yes) Your data is located in the structure that starts where this pointer say. Here is not changed $sver variable. #ce MsgBox(0,"","Return value: " & $ver[0] & @CRLF & _ "Structure pointer: " & $ver[1] & @CRLF & _ "Data from structure: " & DllStructGetData($stString,"v")) DllClose($dll) Edited December 28, 2012 by Andreik
martin Posted December 28, 2012 Posted December 28, 2012 (edited) Maybe I have missed the point of what you want to do, but to send string to a dll in Delphi, and get a string back is fairly simple. This function in Delphi for example function sometest(rrr: pchar): pchar; stdcall; var ff: string; begin ff := 'Here is some text to send back in answer to ' + rrr; Result := pchar(ff); end; and this call in AutoIt $ver = DllCall($dll,'str','sometest','str','abcdef') MsgBox(0,"","Returned value: " & $ver[0]) If you use a structure in AutoIt and send the pointer you need to make sure that the returned string is no longer than the structure allowed for, so it might be wise to add a parameter for the length. Returning a string from Delphi doesn't require that the string must be a certain length. Edited December 28, 2012 by martin Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
klausgunther Posted December 28, 2012 Author Posted December 28, 2012 @Andreik: Ok. I see. But there remains a question: Where is my data going ? In my DLL function, y write the string "KGF.dll V01.90 28/12/2012", byte by byte, into the memory pointed by the first (and only) parameter of the function. I intuitively thought thatafter return, the correspondig memory area would show this data. @Martin: Thank you for your hint with Delphi.. I'll tell you a bit more about the usage of my DLLs and functions. I'm using a very nice Basic clone named Panoramic. This Basic can call DLL functions, but only with a very restrictive syntax: a. all parameters are 32/64 bit integer values b; all parameters are passed "by value" c. every funtion returns a single 32/64 bit integer value So, you see the consequences: 1. © implies that no string result can be returned 2. ( implies that no parameter can be modified by any function 3. when a string is to be passed to a function, one must pass the address of the string as integer value (consequence of (a) Its up to the function to interprete that address correctly. 4. when a value other then a single integer function result must be returned, one has to pass the address of the receiving memory area as input parameter (consequences of (a) and ( ). It's up to the function to know how many data bytes to write at that address. So, that's the reason why i must return my result string using the address of the receiving memory area (a pre-charged string in this case). The initial string value is just a place holder, a memory reservation. This all works fine from my Delphi DLL to the Panoramic language. I just wanted to have a quick check method throughout AutoIt for checking new or modified functions. The small DLL I published here, is just a test case. In reality,n there are several DLL's with several hundred functions.
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