Handling out param in delphi DLL

Started by HPW, September 05, 2011, 04:49:55 AM

Previous topic - Next topic

HPW

Hello,



For the pdScript-DLL calling from newLISP.exe I ran into problem to get back a delphi PAnsiChar out buffer.



delphi-definition:

function pdScriptInlineExLibA(ScriptCode:PAnsiChar; ScriptDFM: PAnsiChar; Params:PAnsiChar; SyntaxCheckOnly:Bool; out pStdOut: PAnsiChar; LibraryPath:PAnsiChar): Integer; stdcall;


I do:

(import "pdScriptE.dll" "pdScriptInlineExLibA")
(import "pdScriptE.dll" "pdsFreeMem")
(setq DpasText ....)
(setq DfmText ...)
(setq DpasParam "")
(setq DpasOutPutBuffer (dup "." 2000)) ;allocating buffer but this is nonsens because out-buffer only
(setq DpasLibPath "")
(setq scriptresult(pdScriptInlineExLibA DpasText DfmText DpasParam 0 DpasOutPutBuffer DpasLibPath))
(setq OutPutBuffer (get-string DpasOutPutBuffer))
(MessageBoxA 0 (string "This is pdScript return-value: " scriptresult) "pdScript Test" 0)
(MessageBoxA 0 (string "This is pdScript output buffer: " OutPutBuffer) "pdScript Test" 0)
(pdsFreeMem DpasOutPutBuffer)

All other params are passed fine and work as expected.

The OutPutBuffer shows nothing or strange chars.

The pdsFreeMem crashes newLISP.



Here the comment from the pdScript developer:
Quote
Hi,



I don't think there is something non-standard, because we are using only pointers (PAnsiChar in this case) and "stdcall" directive.

Please, do not create a new wrapper - let's try to solve it in an easy way.



In short, you have to use an equivalent of PAnsiChar for DpasOutPutBuffer, pass it to the pdScriptInlineExLibA function and release it with pdsFreeMem on finish. Nothing more. I guess the problem is in the variable type, because it seems, that your DpasOutPutBuffer is a simple string, not a pointer to chars. But I don't know how to define such a variable in newLISP, if it is possible at all.



About pdsFreeMem - you should call it with parameter: pdsFreeMem(DpasOutPutBuffer), but when you simply remove the line with DpasOutPutBuffer declaration, it is clear, that it will crash, because when you do not pass any PAnsiChar pointer to pdScriptInlineExLibA, then the output buffer is not allocated at all.



Roman


Any idea how to call such function with out-parameter?



Regards



Hans-Peter
Hans-Peter

Lutz

#1
May be this is a pointer-to-pointer situation? In C this would be something like char ** pptr. The function called allocates the memory and also frees it. But the function does not return a pointer to the memory allocated but writes the address of the pointer to a space passed to it. In newLISP you would allocate space only for the 32-bit pointer, not the whole string:


(import "thelib.dll" "foo") ; foo(char * * pptr) allocating
(inport "thelib.dll" "freemem") ; support function

(set 'pptr (dup "00" 4)) ; allocate space for a 32-bit pointer

(foo pptr) ; the function in thelib.dll will write the address to pptr

(set 'ptr (unpack "lu" pptr)) ; get the pointer to the string content

(get-string ptr) ; -> the content memory allocated by foo

(freemem ptr) ; free the memory

HPW

#2
Hello Lutz,



Thanks for the hint.

I tried with no luck:

;Lutz advise from the forum
....
(setq pptr (dup "00" 4)) ; allocate space for a 32-bit pointer
(setq DpasLibPath "")
;call pdScript Inline
(setq scriptresult(pdScriptInlineExLibA DpasText DfmText DpasParam 0 pptr DpasLibPath))
;Lutz advise from the forum
(MessageBoxA 0 "Test1" "pdScript Test" 0)
(setq ptr (unpack "lu" pptr))        ; get the pointer to the string content
(MessageBoxA 0 "Test2" "pdScript Test" 0)
(setq OutPutBuffer (get-string ptr)) ; -> the content memory allocated by foo
(MessageBoxA 0 "Test3" "pdScript Test" 0)
;(freemem ptr)                        ; free the memory
;scriptresult is set to 0 when successfull
(MessageBoxA 0 (string "This is pdScript return-value: " scriptresult) "pdScript Test" 0)
;OutPutBuffer shows a few crypted nonsens chars instead of writeln-text from dialog, not sure why this happens
(MessageBoxA 0 (string "This is pdScript output buffer: " OutPutBuffer) "pdScript Test" 0)


The freemem crashes the process.

OutPutBuffer still shows some strange strings.



Hans-Peter
Hans-Peter

HPW

#3
Roman wrote this on his forum:


Quote
So, if it can help Lutz to understand the common logic, then



Code:out pStdOut: PAnsiChar



is equivalent to



Code:__out char *pStdOut



in C convention.
Hans-Peter

Lutz

#4
Are Delphi strings not related to Pascal strings? Pascal strings store the length of the string as leading characters in the buffer. If a binary 0 is part of it and occurs before the relevant information then get-string would not work.



Buffers in newLISP are both, they can be seen as 0-terminated C-strings, but also store the full length of the buffer in an extra internal field inside the Lisp cell. That is the size you get when using (length buff). This way newLISP can see strings as buffers with binary content too.



See the following to understand the difference between get-string and unpack and how to skip characters when using get-string.




(set 'buff "ABC000000DEF")  → "ABC000000DEF"

(length buff)  → 9

(get-string buff)  → "ABC"

(length (get-string buff))  → 3

; get a string from offset into a buffer
(get-string (+ (address buff) 6)) → "DEF"

; use unpack to get the whole buffer
(unpack "s9" buff)  → ("ABC000000DEF")


Perhaps this could offer another hint what is going on in that pdScript code.



ps: the above example will also be put in the manual.

beprecision

#5
Hi!



In pdScript DLL interface, there are not used Delphi strings, but buffers. PAnsiChar (and PChar, PWideChar, etc.) is declared in Delphi to mimic the C convention and also to allow transferring data between non Delphi application and Delphi DLL (which is our case).



So, let me briefly explain how should it work:



1) newLisp should declare a variable, that will be full-filled with output data (a pointer to character data)

2) this variable is passed to the pdScript.dll (without any preparation - the dll needs only to know the pointer, where to store data)

3) the buffer is allocated and full-filled inside the pdScript.dll (or set to nil if no output is returned)

4a) newLisp should check if the variable is nil (it is not the same as empty buffer, I think it is a checking of 0 in C)

4b) if variable is not a nil, then newLisp can read the variable as a string and it should free the buffer by calling the function pdsFreeMem(variable) from pdScript.dll (because the dll should free the memory)



The point of using this kind of declaration (and processing) is, that the caller don't know the resulting size of output buffer and it is not possible to call the DLL twice (as it is often using in a lot of Win API functions) to get the size of the buffer first, allocate the buffer and to get the result then (of course the reason is, that the script would run twice, which is a nonsense in most cases).



Hope this helps you a little to declare a right variable for calling the function from pdScript.dll.



Best regards,



Roman

HPW

#6
Hello,



Based on the hints and diskussion here I finally get it:



(get-string(setq ptr(first(unpack "lu" pptr))))



(setq pptr (dup "." 2000)) ; allocate space for test but not really required
(MessageBoxA 0 (string(address pptr)) "pdScript Test" 0) ;shows a memory address
(setq DpasLibPath "")
;call pdScript Inline
(setq scriptresult(pdScriptInlineExLibA DpasText DfmText DpasParam 0 pptr DpasLibPath))

(MessageBoxA 0 (string(address pptr)) "pdScript Test" 0) ;shows still the memory address

;Lutz advise from the forum
(MessageBoxA 0 "Test1" "pdScript Test" 0) ;Debug step

(MessageBoxA 0 (string(length(get-string(+ (address pptr)4)))) "pdScript Test" 0) ;shows the length of the rest of the buffer

(MessageBoxA 0 "Test2" "pdScript Test" 0) ;Debug step

(MessageBoxA 0 (string(get-string(setq ptr(first(unpack "lu" pptr))))) "pdScript Test" 0) ;the pointer is the first in the returned list and is the address of pdScript output buffer

(setq OutPutBuffer (get-string (+ (address pptr)4))) ; -> the content memory allocated
(MessageBoxA 0 OutPutBuffer "pdScript Test" 0)


(MessageBoxA 0 "Test3" "pdScript Test" 0)
(pdsFreeMem ptr)                        ; free the memory
Hans-Peter

HPW

#7
Hello,



After solving the problem it is now possible to call newLISP.exe with a lsp-file and create a GUI on the fly with pdScript.

Together with Peter's run.exe tool there is even no cmd-window.

And with pdScript's /PDST param you have control if the GUI should have a visible taskbar button.



A question: Is there any option to do a direct callback into the newLISP.exe instance?

(Like a pointer to a newlispEvalStr in the EXE)

(But maybe it is better in this situation to use a pdScript mainloop and use the newLISP.dll)
Hans-Peter

Lutz

#8
QuoteA question: Is there any option to do a direct callback into the newLISP.exe instance?


Yes, the callback function returns a memory address, and assigns a newLISP function to it. That address is than used by the DLL to call into newLISP. There are two working examples in the newlisp-10.x.x/examples directory: opengl-demo.lsp and win32demo.lsp. The second does a complete Windows event loop in newLISP (great example by Cyril).



See also http://www.newlisp.org/downloads/newlisp_manual.html#callback">http://www.newlisp.org/downloads/newlis ... l#callback">http://www.newlisp.org/downloads/newlisp_manual.html#callback