Page 1 of 1
How to call a dll function that returns a structure?
Posted: Sat Jan 12, 2008 4:47 am
by Mistrel
I'm trying to call a function from a dll that returns a structure and not a structure pointer. Is this possible to do natively in PureBasic?
If I try to call the function without a return parameter the dll crashes with an access violation. The same occurs if I try to assign the result to a structure pointer. How can this be done if PureBasic doesn't support structures as a return parameter?
I'm wrapping these commands in C to return a structure pointer. Is there any way to make this work in just PureBasic?
From what I've read here it looks like this is not possible.
http://www.purebasic.fr/english/viewtopic.php?t=29155
Posted: Sat Jan 12, 2008 5:55 am
by pdwyer
how big is the structure? can you return it in a different type? Would it fit inside a quad? you could pull the data out afterwards with a union or something...
I heard once that fixed strings are passed on the stack and not a copied data pointer like dynamic strings, can you pass it back in a fixed string hard-set to the size of the structure?
Posted: Sat Jan 12, 2008 6:22 am
by Hroudtwolf
I'm trying to call a function from a dll that returns a structure and not a structure pointer. Is this possible to do natively in PureBasic?
A function returns a pointer if the return-variable was a dataset.
For reading datasets from functionresult, use following way.
Code: Select all
Procedure Blub ()
*dataset.point = AllocateMemory (SizeOf (POINT))
*dataset\x = 100
*dataset\y = 150
ProcedureReturn *dataset
EndProcedure
*dataset.point = Blub ()
Debug *dataset\x
Debug *dataset\y
Best regards
Wolf
Posted: Sat Jan 12, 2008 6:23 am
by Mistrel
I have three different structures that I need to return. There is one function that returns an 8-byte structure but the dll still crashed when I tried to return it to a double.
Code: Select all
PrototypeC.d ProtoFunction(VectorID)
lib=LoadLibrary_("Library.dll")
ptr=GetProcAddress_(lib,"FunctionName")
Function.ProtoFunction=ptr
this.d=Function(1)
Maybe PureBasic is incorrectly handling returning C structures? I should create my own dll in C to test this theory.
Posted: Sat Jan 12, 2008 6:27 am
by Hroudtwolf
Maybe PureBasic is incorrectly handling returning C structures?
Structures aren't datamediums.
It's the memoryblock which uses the structure what contains the data.
Maybe, your this memoryblock is protected?!
Posted: Sat Jan 12, 2008 7:26 am
by Mistrel
Ok. Here is an example to work with.
Code: Select all
/* Return_Struct.c */
#define ExportC __declspec(dllexport)
typedef struct MyStruct {
float a;
float b;
float c;
} MyStruct;
ExportC MyStruct ReturnStruct(int Param1) {
MyStruct Local;
Local.a=1;
Local.b=3;
Local.c=5;
return Local;
}
Code: Select all
lib=LoadLibrary_("Return_Struct.dll")
ptr=GetProcAddress_(lib,"ReturnStruct")
Structure MyStruct
a.f
b.f
c.f
EndStructure
; you can't do this
*this.MyStruct=CallCFunctionFast(ptr,1)
Debug *this\a
Debug *this\b
Debug *this\c
This gives us a starting point to work with (again, if this is even possible).
Here is the dll compiled:
http://3dfolio.com/files/Return_Struct.dll
Posted: Sat Jan 12, 2008 9:15 am
by Hroudtwolf
Maybe, you have to allocate a unprotected memoryblock for that.
You could try this.
Code: Select all
ExportC MyStruct ReturnStruct(int Param1) {
MyStruct* Local;
Local = malloc(sizeof(MyStruct));
Local->a=1;
Local->b=3;
Local->c=5;
return Local;
}
Posted: Sat Jan 12, 2008 12:06 pm
by tinman
Mistrel wrote:Ok. Here is an example to work with.
I don't know if this kind of thing is compiler dependant, but I've coded a test in C and the assembly generated looks like this:
Code: Select all
; 9 : MyStruct foo;
; 10 :
; 11 : foo = ReturnStruct(6);
mov esi, esp
push 6
lea eax, DWORD PTR $T78404[ebp]
push eax
call DWORD PTR __imp__ReturnStruct
add esp, 8
cmp esi, esp
call __RTC_CheckEsp
mov ecx, DWORD PTR [eax]
mov DWORD PTR _foo$[ebp], ecx
mov edx, DWORD PTR [eax+4]
mov DWORD PTR _foo$[ebp+4], edx
mov eax, DWORD PTR [eax+8]
mov DWORD PTR _foo$[ebp+8], eax
So you can see that the value passed on the stack seems to be a temporary variable, whcih then gets copied into my "foo" variable. So in PB you'd need to do something like this (I think - I can't get it to work for some reason, it will not find the exported function in the DLL):
Code: Select all
lib.l=LoadLibrary_("debug\ReturnStruct.dll")
Structure MyStruct
a.f
b.f
c.f
EndStructure
Define.MyStruct foo
; you can't do this
;*this.MyStruct=CallCFunctionFast(ptr,1)
PrototypeC ReturnStruct(*struct.MyStruct, Param1.l)
*ptr.ReturnStruct=GetProcAddress_(lib, "ReturnStruct")
If *ptr=0
Debug GetLastError_()
Else
*ptr(@foo, 6)
Debug foo\a
Debug foo\b
Debug foo\c
EndIf
Edit: It does work if you replace the "ReturnStruct" in the GetProcAddress_() call with the ordinal. So at least it shows that it should work in PB :)
Posted: Sat Jan 12, 2008 7:52 pm
by Mistrel
Hey! It works! That's some hack, tinman. Thank you!
I don't understand what the errors are from. I added a new function:
Code: Select all
ExportC int ReturnVal() {
return 7;
}
This function calls fine but GetLastError_() reports that the file could not be found
Code: Select all
Procedure.s ShowAPIError(CheckReturnValue) ; forum code, unsure of original author
Buffer.s=Space (4096)
NumberOfChars=FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM,0,CheckReturnValue,0,Buffer.s,Len(Buffer.s),0)
ProcedureReturn Left(Buffer.s,NumberOfChars-2)
EndProcedure
lib=LoadLibrary_("Return_Struct.dll")
ptr=GetProcAddress_(lib,"ReturnVal")
Debug ShowAPIError(GetLastError_())
Debug CallCFunctionFast(ptr)
End
Posted: Sat Jan 12, 2008 8:11 pm
by tinman
Mistrel wrote:This function calls fine but GetLastError_() reports that the file could not be found
Are you sure that the error code is up-to-date? Usually you only call GetLastError_() if there was an error so a function may not change it if there has been no error. You can check by calling SetLastError_(0) before your LoadLibrary_() call (or somewhere else).
Posted: Sat Jan 12, 2008 8:14 pm
by Mistrel
I'm positive. You should see the same result if you run that code.
I don't understand how your trick works. How does the external procedure know to pass the structure to the first parameter and not to the return value? I know that's how you wrote the prototype but it doesn't match the actual function.
Posted: Sat Jan 12, 2008 9:00 pm
by tinman
Mistrel wrote:I'm positive. You should see the same result if you run that code.
No, I get "The operation completed successfully" in the debug window using your original code.
Mistrel wrote:I don't understand how your trick works. How does the external procedure know to pass the structure to the first parameter and not to the return value? I know that's how you wrote the prototype but it doesn't match the actual function.
With this (MSVC2005) compiler it looks like the behaviour for returning a structure by value is to require an additional hidden parameter that the compiler inserts automatically and then uses that as the destination for the return statement. But I think this behaviour is compiler dependant. I found a draft C standard on the net and I could not find anything that said how values should be returned.
IMO returning by value is fine within an application (or perhaps even an application private DLL) but if you have some control over it then do it differently for DLL functions.
Posted: Sat Jan 12, 2008 9:18 pm
by Mistrel
Thank you for the explanation. It seems to work fine from a gcc compiled dll as well. But you're right that it may be compiler dependent.