Page 1 of 1

Interfaces to regular functions

Posted: Tue Sep 07, 2004 11:12 pm
by Moonshine
I wrote a quick DLL today to test something. Heres the code for the DLL:

Code: Select all

ProcedureDLL TestFunction(Value.l)
  MessageRequester("DLL Message", "The value is: " + Str(Value), #PB_MessageRequester_Ok)
EndProcedure
And the test program:

Code: Select all

; InterfaceTest

Interface I_FunctionInterface
  TestFunction(a.l)
EndInterface

Global *Object.I_FunctionInterface

*Object = OpenLibrary(0, "TestLib.dll")

  *Object\TestFunction(17)
  CloseLibrary(0)
  End
What I wanted to do is to create an interface to a regular DLL function, just so the calls stay more organised, rather than lots of CallFunction's dotted through the source. Also, some of the functions need to be called many times per loop so speed is everything and I thought this would be a better alternative to keeping a big list of function pointers and using CallFunctionFast... anyways, running the above code causes a regular XP "This program has encountered a problem and needs to close etc" dialog. So either Im doing something wrong (most likely), it's not possible, or it just cant be done. Does anyone know whats going wrong? Thanks :)

Posted: Wed Sep 08, 2004 9:11 am
by carolight
I could be wrong, but I believe Interfaces for DLLs are really for interfacing to COM / ActiveX.

You can imitate OOP using Interfaces, but I can't think why you'd want to in this case, as I don't believe it would be very fast.

Can you just create a procedure to call the DLL procedure (although this probably won't be as fast as using CallFunctionFast directly in your code)? This would be effectively the same as using Interface, which has to redirect anyway.

Code: Select all

Global *f.l

Procedure TestFunction(Value.l)
  CallFunctionFast(*f, Value)
EndProcedure

OpenLibrary(0, "TestLib.dll") 
;Get the address of the function
*f = IsFunction(0, "TestFunction")

;TestFunction is the procedure you would call throughout your code.
TestFunction(17)
CloseLibrary(0) 
End

Posted: Wed Sep 08, 2004 4:42 pm
by Moonshine
Thanks carolight...so my pointer list would seem the best would seem the best option....

I started a procedure that basically checks all the functions exist then assigns a pointer to each function so it can be called fast....only this might get hard to manage with a few hundred pointers...

this is how it looks:

Code: Select all

procedure getpointerlist()

*f_Function1 = IsFunction(#lib, "Function1")
  *f_Function2 = IsFunction(#lib, "Function2")
...
All I can think of is to have an array, but then Id have to keep a written list or something or which index number is which function....

anyways, thanks for the help :)

Posted: Wed Sep 08, 2004 8:57 pm
by GedB
Moonshine,

Do you know about the DLL importer? It allows you to call DLL functions by adding an underscore after the name.

Like this:
Function1_()
Function2_(param1, param2)

The same method is used to import all the API commands.

It's very easy to do. Just look at the ReadMe in "PureBasic\Library SDK\DLL Importer"

Posted: Wed Sep 08, 2004 9:31 pm
by Moonshine
Ah, I forgot about that...what's it like for speed?

Posted: Wed Sep 08, 2004 9:41 pm
by GedB
It should be the fastest available method.

Posted: Wed Sep 08, 2004 9:42 pm
by GedB
If you simply must have an array of function pointers, here's how to do it using an interface:

Code: Select all

;An interface is actuall a specialised form of structure.  
;The first element of an interface always points to a vTable.
;A vtable is a list of Function pointers.
;Notice that the paramater list is different
;to the functions that they are going to point to.
Interface Functions
  First()
  Second(long.l)
  Third(string.s)
  Fourth()
  Fifth(fzero, *fzero)
EndInterface

;When a function is called via an interface
;The rest of the interfaces structure after 
;the vtable pointer is passsed as the first 
;paramater.  Here we are just ignoreing it.
Procedure One(ignore)
  Debug ignore
  Debug "1"
EndProcedure

;Failure To add ignore to your paramater list
;would mess up any paramaters passed.
Procedure Two(param.l)
  Debug "2 " + Str(param)
EndProcedure

Procedure Three(ignore, param.s)
  Debug "3" + param
EndProcedure

;If there are no paramaters you can ignore them,
;but it's a good idea to leave the ignore
;paramater there, to remind yourself.
Procedure Four()
  Debug "4"
EndProcedure

;Since our vtable follows imediatly after
;the pointer in this example, ignore
;actually points the the vtable.
;Ie. ignore will hold a pointer to 
;f(1), which is the same as the content
;of f(0).  This is just a coincidence
;because of the way the array has been 
;used.
Procedure Five(ignore, fzero, *fzero)
  Debug Str(PeekL(ignore)) + " = " + Str(fzero)
  Debug Str(ignore) + " = " + Str(*fzero)
EndProcedure

Dim F.l(5)

;f(0) is going to be the pointer to the vtable, 
;which is the rest of the array.
F(0) = @F(1)

;The rest of the array is the vTable.  Each element
;is pointing to a function.
F(1) = @One()
F(2) = @Two()
F(3) = @Three()
F(4) = @Four()
F(5) = @Five()

do.Functions = @F(0)

do\First()
do\Second(2)
do\Third("Three")
do\Fourth()
do\Fifth(F(0), @F(0))

Posted: Thu Sep 09, 2004 4:19 pm
by Moonshine
Thanks for the code GedB - youve really established yourself as quite an expert on this :)