How to call a dll function that returns a structure?

Just starting out? Need help? Post your questions and find answers here.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

How to call a dll function that returns a structure?

Post 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
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post 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?
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post 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
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post 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.
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post 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?!
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post 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
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post 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;
}
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post 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 :)
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post 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
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post 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).
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post 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.
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Post 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.
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post 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.
Post Reply