View Full Version : API Service + Borland
thinktink
29th July 2011, 16:55
I'm still hitting a brick wall with trying to get the api service. It seems so odd to me that it's so problematic to use the functions from what is still relatively very popular compiler. It's really frustrating. Is there any possible way I can emulate what the MSVC code is doing? Even under a kludge? Is there some piece of information that I'm not researching myself that is causing me not to realize how MSVC handles that code that I can try to emulate? I will go to hell and back if I can get this to work. Hell, I'll make my own Borland compatible headers of all the services if I have to.
Any help or pointers or research links would be appreciated.
thinktink
8th December 2011, 15:47
/bump
DrO
8th December 2011, 17:26
in brutal honesty i just don't think it's going to work as what i remember is Borland's abi isn't the same as MSVC's (but that's basing things on years ago when i did use Borland and just went to MSVC since i knew it'd work and was told to do so inorder to access things by people on the original dev team).
-daz
thinktink
30th June 2012, 21:47
I think I've managed a kludge:
//---------------------------------------------------------------------------
int __fastcall ___callWASABI(void *AThis,int Msg,void *RET_VAL,void **params,int NumParams)
{
int Res;
typedef int __cdecl (*AWASABIDispatch)(int Msg,void *RET_VAL,void **params,int NumParams);
#include "pshpack4.h"
typedef struct tagWASABIVTablePointer{
union
{
AWASABIDispatch Dispatch[1];
tagWASABIVTablePointer *b[1];
};
}WASABIVTablePointer;
#include "poppack.h"
WASABIVTablePointer *a = (WASABIVTablePointer *)AThis;
a=a->b[0];
register DWORD OldECX;
__asm { MOV OldECX, ECX
MOV ECX, AThis };
Res=a->Dispatch[0](Msg,RET_VAL,params,NumParams);
__asm { MOV ECX, OldECX };
return Res;
};
//---------------------------------------------------------------------------
Example to retrieve the memmngr api class pointer using the above kludge:
if(serviceApi)
{
DWORD RetVal=0;
void *p[1];
GUID st=memMgrApiServiceGuid;
p[0]=&st;
___callWASABI(serviceApi,API_SERVICE_SERVICE_GETSERVICEBYGUID,&RetVal,p,1);
//the variable "RetVal" now contains the pointer to the instance of the api_memmgr service class.
}
Thus far I've had absolutely no crashes. And yes, the asm code is absolutely necessary since (at least from what I read) MSVC classes use the ECX register for the this pointer for class member function calls. If I took out the asm code it crashes Winamp.
thinktink
16th July 2012, 16:08
Revision:
Change the calling convention of the typedef AWASABIDispatch in the above example code from __cdecl to __stdcall. I discovered the error while creating a reverse of the kludge for callbacks.
And, of course, the obviously wrong reference to RetVal being a pointer to the api_memmgr service when it's actually a pointer to the api_memmgr's waServiceFactory object at the end of execution.
thinktink
19th July 2012, 03:09
k, I think I've created a solid and stable way for Borland plugin writers to use the API services. I've attached a zip file containing two C++ files (1 whole unit.) You can't put it all in the header or it will make compile times longer and create more headaches with size, recompiles, what-not. It means you will need to add the CallWasabi.cpp file to your project or you'll get linker errors.
Example code to allocate 24 bytes of memory (for no particular reason):
#include "CallWasabi.h"
void *SomeMemory=NULL;
/* Never call the the functions of the base class directly.
** Always call the functions from the macro generated classes */
void *apiService=(void *)SendMessage(plugin.parent,WM_WA_IPC,0,IPC_GET_API_SERVICE);
if((int)apiService==1)apiService=NULL;
if(apiService)
{
TApiService ApiService(apiService);
void *mmsf=ApiService.service_getServiceByGuid(memMgrApiServiceGuid);
if(mmsf)
{
TServiceFactory mmServiceFactory(mmsf);
void *memmgrApi = mmServiceFactory.getInterface(FALSE);
if(memmgrApi)
{
TMemMgr MemMgr(memmgrApi);
SomeMemory=MemMgr.sysMalloc(24);
}
}
}
Example code to create a callback class (this example will use playlist enumeration.):
#include "CallWasabi.h"
#include <playlist\ifc_playlistloadercallback.h>
#include <vcl.h>
/* aWASABICALLBACKCLASS is a class generation macro from CallWasabi.h */
aWASABICALLBACKCLASS(TPlaylistLoaderCallback,ifc_playlistloadercallback);
typedef void __fastcall (__closure *TMyPlaylistLoaderCallbackOnFile)(void *Sender,void *UserData,AnsiString Filename,AnsiString Title,int MSLen,bool &Continue);
class TMyPlaylistLoaderCallback : public TPlaylistLoaderCallback
{
private: typedef TPlaylistLoaderCallback baseclass;
void *UserData;
public:
TMyPlaylistLoaderCallbackOnFile OnRecvFile;
TMyPlaylistLoaderCallback(void *AUserData)
: baseclass()
{
OnRecvFile=NULL;
UserData=AUserData;
};
RECVS_DISPATCH;
int OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info);
void OnFileOld(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info);
int OnPlaylistInfo(const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info);
void OnPlaylistInfoOld(const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info);
const wchar_t *GetBasePath();
};
int TMyPlaylistLoaderCallback::OnFile(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
{
bool Continue=true;
if(OnRecvFile)
{
OnRecvFile(this,UserData,filename,title,lengthInMS,Continue);
}
if(Continue)return LOAD_CONTINUE;
else return LOAD_ABORT;
};
void TMyPlaylistLoaderCallback::OnFileOld(const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info)
{
bool Continue;
if(OnRecvFile)
{
OnRecvFile(this,UserData,filename,title,lengthInMS,Continue);
}
};
int TMyPlaylistLoaderCallback::OnPlaylistInfo(const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info)
{
return LOAD_CONTINUE;
};
void TMyPlaylistLoaderCallback::OnPlaylistInfoOld(const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info)
{
};
const wchar_t *TMyPlaylistLoaderCallback::GetBasePath()
{
return NULL;
};
#define CBCLASS TMyPlaylistLoaderCallback
START_DISPATCH
CB(IFC_PLAYLISTLOADERCALLBACK_ONFILE_RET,OnFile);
VCB(IFC_PLAYLISTLOADERCALLBACK_ONFILE,OnFileOld);
CB(IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO_RET,OnPlaylistInfo);
VCB(IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO,OnPlaylistInfoOld);
CB(IFC_PLAYLISTLOADERCALLBACK_GETBASEPATH,GetBasePath);
FORWARD_DISPATCH(baseclass);
#undef CBCLASS
//---------------------------------------------------------------------------
void *apiService=(void *)SendMessage(plugin.parent,WM_WA_IPC,0,IPC_GET_API_SERVICE);
if((int)apiService==1)apiService=NULL;
if(apiService)
{
TApiService ApiService(apiService);
void *sf=ApiService.service_getServiceByGuid(api_playlistmanagerGUID);
if(sf)
{
TServiceFactory ServiceFactory(sf);
void *aplm=ServiceFactory.getInterface(FALSE);
if(aplm)
{
TPlaylistManager PlaylistManager(aplm);
TMyPlaylistLoaderCallback MyPlaylistLoaderCallback(/*UserData pointer*/);
MyPlaylistLoaderCallback.OnRecvFile=/*Some class instance method function pointer that knows what to do with "UserData pointer"*/;
WideString PlayListFileName=L"C:\\Documents and Settings\\User\\My Documents\\SomePlayListFile.???";
if(PlaylistManager.CanLoad(PlayListFileName.c_bstr()))
{
PlaylistManager.Load(PlayListFileName.c_bstr(),MyPlaylistLoaderCallback.GetWASABIObject());
}
}
}
}
Any thoughts DrO?
thinktink
20th July 2012, 18:22
I found a flaw in the callback creator macro that could potentially cause problems if the developer selects a different project default calling convention than that of the standard default. Attached corrected version.
vBulletin® v3.8.6, Copyright ©2000-2013, Jelsoft Enterprises Ltd.