Direct funtion call works - CALLFUNCTION() does not
Posted: Mon Feb 02, 2015 5:31 pm
Hi,
A weird problem that makes me think of a bug ... Or - lack of knowlegde
Some background to start with ...
I'm trying to write a program where different functional blocks are run from different external DLLs. Think of it as e.g. an object oriented drafting program.
By definition, all these external DLLs will have the same interface format/functions, I only attach one as a demo (ccDll1.pbi) as a sort of template - I stripped the code as much as possible. So by definition, they all have to use the same common structure definitions (ccCommon.pbi).
The approach is an object oriented approach with virtual tables. Every DLL is supposed to represent one type of an object (think square, circle, box, sphere ...). Object types are simply named after the DLL name.
Every object and it's related gadgets are hooked to an *Objects()-item and go inside a containergadget so that they're isolated from the rest. Apart from the interface-definitions and some conventions, the calling program (cc.pb) is not supposed to know anything about the internals of the DLLs. Everyone who knows the ccCommon.pbi and sticks to the conventions, should be able to write a DLL without knowing about the main program (cc.pb). The goal is that DLLs can be added without having to touch/recompile the main program.
I'm running PB 5.31-x86 on 64 bit Windows 7.
Now, the problem ...
In the example, I'm creating 4 instances/objects from the same DLL/type with the AddObject() procedure.
When running the program so that it calls the Create() procedure directly from the DLL (tweak the constant #CallDirect to do so) then the 4 containers with their gadgets show. Also, when exiting the program, all is nicely cleaned and the program terminates normal.
When running the program so that CallFunction() is used, or CallFunctionFast(), with or without prototypes, then the first topleft container is not showing but the 3 others do. When exiting this program, the program crashes.
Even though only 3 objects actually shown on screen, the 4 objects can be seen in the debugger.
Calling the Create() procedure directly, is not an option as by definition, the Create() will have to be in all DLLs - it needs to be called in some "library-oriented" manner ...
I can't explain - hopefully, somebody else can. Maybe I'm just doing something wrong.
Any suggestions on coding style and/or approach are welcomed.
Thanks,
Ludo
Main program (cc.pb)
Main include file (ccCommon.pbi) :
DLL-file (ccDll1.pbi) :
A weird problem that makes me think of a bug ... Or - lack of knowlegde

Some background to start with ...
I'm trying to write a program where different functional blocks are run from different external DLLs. Think of it as e.g. an object oriented drafting program.
By definition, all these external DLLs will have the same interface format/functions, I only attach one as a demo (ccDll1.pbi) as a sort of template - I stripped the code as much as possible. So by definition, they all have to use the same common structure definitions (ccCommon.pbi).
The approach is an object oriented approach with virtual tables. Every DLL is supposed to represent one type of an object (think square, circle, box, sphere ...). Object types are simply named after the DLL name.
Every object and it's related gadgets are hooked to an *Objects()-item and go inside a containergadget so that they're isolated from the rest. Apart from the interface-definitions and some conventions, the calling program (cc.pb) is not supposed to know anything about the internals of the DLLs. Everyone who knows the ccCommon.pbi and sticks to the conventions, should be able to write a DLL without knowing about the main program (cc.pb). The goal is that DLLs can be added without having to touch/recompile the main program.
I'm running PB 5.31-x86 on 64 bit Windows 7.
Now, the problem ...
In the example, I'm creating 4 instances/objects from the same DLL/type with the AddObject() procedure.
When running the program so that it calls the Create() procedure directly from the DLL (tweak the constant #CallDirect to do so) then the 4 containers with their gadgets show. Also, when exiting the program, all is nicely cleaned and the program terminates normal.
When running the program so that CallFunction() is used, or CallFunctionFast(), with or without prototypes, then the first topleft container is not showing but the 3 others do. When exiting this program, the program crashes.
Even though only 3 objects actually shown on screen, the 4 objects can be seen in the debugger.
Calling the Create() procedure directly, is not an option as by definition, the Create() will have to be in all DLLs - it needs to be called in some "library-oriented" manner ...

I can't explain - hopefully, somebody else can. Maybe I'm just doing something wrong.
Any suggestions on coding style and/or approach are welcomed.
Thanks,
Ludo
Main program (cc.pb)
Code: Select all
EnableExplicit
XIncludeFile("ccCommon.pbi")
#Calldirect=#True ; set to true for calling the function directly, set to false fo using the library-calls
CompilerIf #Calldirect
XIncludeFile("ccDll1.pbi") ; include only when trying to call directly
CompilerEndIf
Enumeration
#MainWindow
#G_Objects
#G_Libraries
#G_Container
EndEnumeration
Global MainEvent
Global MainGadget
Global MainEventType
Global NewList *Objects.ObjectInterface()
Global NewMap Libraries.L()
Global NewMap CreateAddresses.L()
Global cntObject=-1
Global libName.s
Prototype.i MyCreatePrototype(WindowID.L,pX.L,pY.L,lW.L,lH.L,lID.L,lType.S)
Procedure LoadDLLs()
Protected tmpDir,libName.S,libNum
tmpDir=ExamineDirectory(#PB_Any,"","cc*.dll")
If tmpDir
ClearGadgetItems(#G_Libraries)
While NextDirectoryEntry(tmpDir)
libName.S=ReplaceString(DirectoryEntryName(tmpDir),".dll","")
AddGadgetItem(#G_Libraries,-1,libName)
libNum=OpenLibrary(#PB_Any,libName)
Libraries(libName)=libNum
CreateAddresses(libName)=GetFunction(libNum,"Create")
Wend
FinishDirectory(tmpDir)
EndIf
EndProcedure
Procedure AddObject(libName.s)
Protected MyCreate.MyCreatePrototype=CreateAddresses(libName)
cntObject=cntObject+1
AddElement(*Objects())
CompilerIf #Calldirect
; when calling the create function directly, all containers show
*Objects()=Create(WindowID(#MainWindow),10+Int(cntObject/3)*150,100+150*Mod(cntObject,3),140,140,cntObject,libName)
CompilerElse
; when calling the create function with any of these methods, the first "container" does not show.
*Objects()=MyCreate(WindowID(#MainWindow),10+Int(cntObject/3)*150,100+150*Mod(cntObject,3),140,140,cntObject,libName)
;*Objects()=CallFunction(Libraries(libName),"Create",WindowID(#MainWindow),10+Int(cntObject/3)*150,100+150*Mod(cntObject,3),140,140,cntObject,libName)
;*Objects()=CallFunctionFast(CreateAddresses(libName),WindowID(#MainWindow),10+Int(cntObject/3)*150,100+150*Mod(cntObject,3),140,140,cntObject,libName)
CompilerEndIf
EndProcedure
If OpenWindow(#MainWindow,10,10,500,600,"CC")
ComboBoxGadget(#G_Libraries,5,5,200,22)
UseGadgetList(WindowID(#MainWindow))
LoadDLLs()
AddObject("ccDll1")
AddObject("ccDll1")
AddObject("ccDll1")
AddObject("ccDll1")
Repeat
MainEvent=WaitWindowEvent(0)
If MainEvent=#PB_Event_Gadget
MainGadget=EventGadget()
MainEventType=EventType()
Select MainGadget
Case #G_Libraries
libName.s=GetGadgetText(#G_Libraries)
Debug libName
AddObject(libName)
EndSelect
EndIf
Until MainEvent=#PB_Event_CloseWindow
EndIf
ForEach *Objects()
*Objects()\Free()
Next
Main include file (ccCommon.pbi) :
Code: Select all
; definitions
Interface ObjectInterface
Initialize()
Free()
Set()
Get()
Gadgets()
Update()
EndInterface
Structure Buffer ; output buffers
Address.L
Type.L
Size.L
Name.s
EndStructure
Structure VTableStructure ; The *VTable structure
Initialize.L
Free.L
Set.L
Get.L
Gadgets.L
Update.L
EndStructure
Structure ObjectStructure
*VTable.VTableStructure
Type.s{32}
Name.L
Container.L
x.L
y.L
w.L
h.L
EndStructure
Code: Select all
EnableExplicit
XIncludeFile "ccCommon.pbi"
Structure Dll1Structure Extends ObjectStructure
Dll1Gadget.L
EndStructure
ProcedureDLL Initialize()
MessageRequester("euuh","init")
EndProcedure
ProcedureDLL Free(*This.DLL1Structure)
OpenGadgetList(*This\Container)
FreeGadget(*This\Name)
FreeGadget(*This\Dll1Gadget)
CloseGadgetList()
FreeGadget(*This\Container)
FreeMemory(*This)
EndProcedure
ProcedureDLL Set()
MessageRequester("euuh","set")
EndProcedure
ProcedureDLL Get()
MessageRequester("euuh","get")
EndProcedure
ProcedureDLL Gadgets()
MessageRequester("euuh","gadgets")
EndProcedure
ProcedureDLL Update()
MessageRequester("euuh","update")
EndProcedure
ProcedureDLL.L Create(WindowID.L,pX.L,pY.L,lW.L,lH.L,lID.L,lType.S)
Protected tmpGadget
Protected tmpStr.s=lType
Protected ttt.s=Str(windowid)+Chr(10)+"x:"+Str(px)+" y:"+Str(py)+" w:"+Str(lw)+" h:"+Str(lh)+Chr(10)+Str(lid)+" "+tmpStr
MessageRequester("parameter",ttt)
Protected *Ptr.Dll1Structure=AllocateMemory(SizeOf(Dll1Structure))
*Ptr\Vtable=?FunctionTable
tmpGadget=ContainerGadget(#PB_Any,pX,pY,lW,lH,#PB_Container_Flat)
*Ptr\Container=tmpGadget
*Ptr\Type=tmpStr
*Ptr\w=lW
*Ptr\h=lH
*Ptr\Name=TextGadget(#PB_Any,0,0,100,20,tmpStr+" "+Str(lID))
*Ptr\Dll1Gadget=StringGadget(#PB_Any,10,20,50,20,Str(lID))
CloseGadgetList()
UseGadgetList(WindowID)
ProcedureReturn *Ptr
EndProcedure
DataSection
FunctionTable:
Data.L @Initialize()
Data.L @Free()
Data.L @Set()
Data.L @Get()
Data.L @Gadgets()
Data.L @Update()
EndDataSection