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! :D

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.