How ever Milkshape 3D is written in Visual Studio C++ and more important for this topic it's plugin interface also.
This is the third time I use Microsoft Visual Studio C++ classes with FreeBASIC.
One was the real time audio interface ASIO from the company Steinberg I wrote in FreeBASIC and published years ago.
The others I wrote many plugins for Basic4GL also written in VS C++ in FreeBASIC.
Here are the simple Milkshape 3D plugin C++ class with virtual members.
file "msPlugin.h":
Code: Select all
struct msModel;
class cMsPlugIn {
public:
cMsPlugIn () {};
virtual ~cMsPlugIn () {};
public:
virtual int GetType () = 0;
virtual const char * GetTitle () = 0;
virtual int Execute (msModel* pModel) = 0;
};
typedef cMsPlugIn* (*FN_CREATE_PLUGIN)();
typedef void (*FN_DESTROY_PLUGIN)(cMsPlugIn*);
cMsPlugIn *CreatePlugIn ();
void DestroyPlugIn (cMsPlugIn *plugin);
GetType() GetTitle() and Execute().
Only __stdcall CreatePlugIn() must be visible exported from a C++ plugin dll.
As a first test I used a well tested Milkshape plugin "msTextExporter.dll" (32-bit) and call it's virtual class members from a FreeBASIC *.exe successful.
file "interfacetest.bas"
Code: Select all
#inclib "msTextExporter"
type msModel as any
type MsPlugIn
declare constructor (byval pCPPClass as any ptr)
declare function GetType as long
declare function GetTitle as const zstring ptr
declare function Execute(byval pModel as msModel ptr) as long
private:
as any ptr m_pCPPClass
end type
constructor MsPlugIn (byval pCPPClass as any ptr)
m_pCPPClass=pCPPClass
end constructor
function MsPlugIn.GetType as long
if m_pCPPClass=0 then beep:return -1
var pThis=m_pCPPClass
dim as long result
asm
mov ecx,[pThis] ' put the hidden this pointer in ecx
mov eax,[ecx] ' get address from virtual table
call [eax + 4] ' call __stdcall vtable[1]
mov [result],eax
end asm
return result
end function
function MsPlugIn.GetTitle as const zstring ptr
if m_pCPPClass=0 then beep:return 0
var pThis=m_pCPPClass
dim as const zstring ptr result
asm
mov ecx,[pThis] ' put the hidden this pointer in ecx
mov eax,[ecx] ' get address from vtable
call [eax + 8] ' call __stdcall vtable[2]
mov [result],eax
end asm
return result
end function
function MsPlugIn.Execute(byval pMilkShapeModel as msModel ptr) as long
if m_pCPPClass=0 then beep:return -1
if msModel=0 then beep:return -1
var pThis = m_pCPPClass
var pModel = pMilkShapeModel
dim as long result
asm
push dword [pModel]
mov ecx, [pThis] ' put the hidden this pointer in ecx
mov eax, [ecx] ' get address from vtable
call [eax + 12] ' 12 call __stdcall vtable[3]
mov [result],eax
end asm
return result
end function
extern "Windows-MS"
' !!! all stdcall without @# decoration !!!
declare function CreatePlugIn() as any ptr
declare sub DestroyPlugIn(byval plugin as any ptr)
end extern
var pPlugin = CreatePlugIn()
dim as MsPlugIn plugin = MsPlugIn(pPlugin)
print "plugin.GetType() : " & plugin.GetType()
print "plugin.GetTitle(): " & *plugin.GetTitle()
print "plugin.Execute(0): " & plugin.Execute(123)
print "..."
sleep
mov ecx, [pThis] ' put the hidden this pointer in ecx
Another important fact are the pointer returned by the C++ pseudo code :
auto pClasspoiter = new myPlugin()
Is in real a pointer on a table of other pointers. (I name it VTABLE but I don't mean the interface table from MS COM interfaces)
mov eax, [ecx] ' get address from vtable
call [eax + function_number * sizeof(pointer)]
the memory layout are:
call vtable[1] = GetType()
call vtable[2] = GetTitle()
call vtable[3] = Execute()
after the call I never restore the stack frame so it must stdcall (the functions self restore the stack pointer)
I'm not sure but for me looks like:
call vtable[0] is the address of the class () {} constructor.
Normally I would think if I can call VS C++ classes from FreeBASIC it should be possible to implement such (simple) classes in FreeBASIC also.
With other words I will try to write a Milkshape 3D plugin as a FreeBASIC dll.
Before I give it a try I recall what we have so far:
Code: Select all
class msPlugin {
a(){};
virtual ~a() = 0;
virtual int GetType () = 0;
virtual const char * GetTitle () = 0;
virtual int Execute (msModel* pModel) = 0;
}
class myPlugin public msPlugin {
myPlugin();
int GetType () ;
const char * GetTitle ();
int Execute (msModel* pModel);
}
myPlugin::myPlugin() { /* code removed */}
myPlugin::GetType () { /* code removed */}
myPlugin::GetTitle () { /* code removed */}
myPlugin::Execute (msModel* pModel) { /* code removed */}
// export it from dll
msPlugIn *CreatePlugIn () {
return new myPlugin;
}
May be naked class members in FreeBASIC are a kind of solution.
Does any fbc guru can explain the memory layout of FreeBASIC classes please ?
How do you would create a faked VS C++ class in FreeBASIC ?
Joshy
edit:A bad solution looks simpler as excepted
getType() as long
getTitle() as const zstring ptr
Execute(pModel as any ptr) as long
Does not need the THIS pointer so it can be ignored
pseudo code:
Code: Select all
var vtable new any ptr [5]
vtable[0] = @myDummyContructor
vtable[1] = @myGetType
vtable[2] = @myGetTitle
vtable[3] = @myExecute
vtable[4] = NULL
' export only the entry point of the plugin
function CreatePlugIn () as any ptr export
return @vtable
end function