Pedja

Delphi Plugin API

13 posts in this topic

Anyone ported API to Delphi?

It's not possible, and I am uncertain if it even will be, though in the long run I am aiming towards that possibility...

Share this post


Link to post
Share on other sites

I've done a lot of work with Delphi (15+ years of programming experience) and I've created even dlls (plugins) for Winamp, which, I was told, was also impossible, so, why not give it a try. I can try to make one empty Delphi DLL example plugin. All I need is a current source code or any specification of plugin if such exists right now?

P.S.

I've tried to look into ApexDC source code, folder "plugins/SamplePlugin" and I have analized those files there and it seems to me that it is pretty simple. So, the only thing I need is that someone tells me if that code is the current actual code for a blank plugin or there is some new stuff, and if it is, where is it located, so I can take a look?

Share this post


Link to post
Share on other sites

I've done a lot of work with Delphi (15+ years of programming experience) and I've created even dlls (plugins) for Winamp, which, I was told, was also impossible, so, why not give it a try. I can try to make one empty Delphi DLL example plugin. All I need is a current source code or any specification of plugin if such exists right now?

P.S.

I've tried to look into ApexDC source code, folder "plugins/SamplePlugin" and I have analized those files there and it seems to me that it is pretty simple. So, the only thing I need is that someone tells me if that code is the current actual code for a blank plugin or there is some new stuff, and if it is, where is it located, so I can take a look?

There is quite a lot of changes... and I really don't know if you will be able to pull it off with these changes, but I hope so... for I too like the simplicity of Delphi as opposed to C++. (meaning I can create more complex plugins faster with delphi :P)-

These two are probably the biggest changes structure wise, though there a bunch of others as well...

Change: Plugins should be typesafe now (StrData, instead of std::string)

Change: Plugins a bit more RSX like now... (uses exported functions, ie. PluginAPI.lib)

I think it's good to mention here that I would have preferred to use plain char pointers had it only worked well... (this is about the first listed change, which might be by far the most problematic).

In any case I have attached some stuff regarding the new way the plugins are done... including the updated sample plugin.

If you can spot any incompatibilities with delphi give a shout and I'll see if there is something to be done about it.

Unfortunately my knowledge regarding the finer points of delphi is inferior to my C++ skills, if it were otherwise I would have made sure to preserve compatibility...

PS: Also if you or others are wondering the big changes... this is because only after these changes can I consider the plugin api usable and truly useful to some. after next version there probably won't be any big changes in regards to plugins.

Share this post


Link to post
Share on other sites

thanx for this. can you please additionaly provide a compiled dll file, so I can check for naming conventions and declarations, to see if they match with dll created with delphi?

P.S.

What exactly was a problem using plain char pointers? Can you describe it a little bit more, please?

Share this post


Link to post
Share on other sites

thanx for this. can you please additionaly provide a compiled dll file, so I can check for naming conventions and declarations, to see if they match with dll created with delphi?

P.S.

What exactly was a problem using plain char pointers? Can you describe it a little bit more, please?

Well we use cdecl calling convention, and afaik Delphi supports stdcall and cdecl... so no problems there, I think.

As for using plain char pointers, when I did that the strings got mangled when passing from exe to dll... or vice versa, even though I was careful to make sure that the contents of those pointers can't be directly edited (ie. const pointer to const object... in this case char), because allowing that would naturally be risky and cause myriad of issues.

Even though I answered the question about naming, here is a compiled dll attached anyhow, if it's of any help...

Share this post


Link to post
Share on other sites

I've created all those exports with cdecl, but I wanted to make sure it's the same thing :P

Anyway, I'm looking into the source code, especially PluginManager.cpp, to see if i can see anything odd there.

I would like to see the part of code that was dealing with passing char pointers to and from a plugin dll, so I can take a look in the code to analyze it a bit.. Can you please tell me where to look?

My first guess was it has maybe something to do with mixing unicode and ansii string pointers, but I'd like to first see the code, so I can make sure what am I talking about :)

Share this post


Link to post
Share on other sites

I've created all those exports with cdecl, but I wanted to make sure it's the same thing :)

Anyway, I'm looking into the source code, especially PluginManager.cpp, to see if i can see anything odd there.

I would like to see the part of code that was dealing with passing char pointers to and from a plugin dll, so I can take a look in the code to analyze it a bit.. Can you please tell me where to look?

My first guess was it has maybe something to do with mixing unicode and ansii string pointers, but I'd like to first see the code, so I can make sure what am I talking about :)

Well I don't have that version anywhere anymore, unfortunately, but it was pretty much simply replacing std::string with char* in the function declarations (adding const, where not present) and then using .c_str() to get the const char* pointer when calling them... and again if there was need to pass a "result string" from dll back to exe ie. a modified string we would make the function return another const char* and then on exe side make const std::string out of that one (of course here we assume the plugin dll would make these returned chars anew with the PluginAPI::AllocFunc(...), since modifying the passed params would be, not impossible, but foolish).

If you need to look at PluginManager code it might be wise of me to share the new code with you, so you can see the up to date stuff in its entirety (and in full context) rather than just bits and pieces.

PS: Check you PM's :P

Share this post


Link to post
Share on other sites

I thought I'd bump this since with next version a Delphi compatibility might be possible to accomplish thus opening new doors for this feature.

Share this post


Link to post
Share on other sites

Hi,

I'm back again :) I was busy with my work in the past months, so I couldn't afford some free time for this thing :/

Anyway, do you have any specifications about dlls, what exports should they have, which data structures should be used, etc. Do you have any documentations about that, or should I just analyze the SamplePlugin folder and see for myself? :)

Share this post


Link to post
Share on other sites

Well we use cdecl calling convention, and afaik Delphi supports stdcall and cdecl... so no problems there, I think.

stdcall would open you up to VB plugins as well.

Share this post


Link to post
Share on other sites

I suppose it can be done... though in that case decorated names come to play, although I suppose def file can resolve that easily enough for us.

Share this post


Link to post
Share on other sites

I hope I can help a bit - actually I'm using Delphi 7, not C++, so I just rewrite header file a bit for my needs.

1) Here is a code for very simple plugin.

2) I want to warn you that I don't test all functions and you may crash ApexDC++ when using it.

3) It is not a complete port for header file - only things what I need.

4) This code may a bit rough for someone, but, as I say, I did that for myself and put it here only to help anyone who don't know how to start.

5) The code below is legal only for version 1.3.6 and may be not compatible with later version of ApexDC++ - don't forget to check PluginDefs.h from latest source code package.

ApexTest.pas


library ApexTest;

uses Windows, PluginDefs;


const

  PLUGIN_GUID = '{c0dec0de-c0de-c0de-c0de-c0dec0dec0de}'; // UUID/GUID for this plugin project

  PLUGIN_NAME = 'ApexTest'; // Name of the plugin

  PLUGIN_AUTHOR = 'Anonymous'; // Author of the plugin

  PLUGIN_DESC = 'A simple Delphi test plugin with every minute message.'; // Short description about the plugin

  PLUGIN_VERSION = 1.00; // Version of the plugin (note: not api version)

  PLUGIN_WEB = 'N/A'; // Plugin website, set to "N/A" if none


var

   dcpp: PDCCore = nil;

   hook: pointer = nil;



// forward declaration for onLoad function

function pluginProc(eventID: cardinal; pData: pointer): longbool; stdcall; forward;



function onLoad(eventID: cardinal; pData: pointer): longbool;

begin

  dcpp:=pData;

  // set hook

  If hook = nil Then hook:=dcpp^.set_hook(HOOK_TIMER, @pluginProc, nil);

  result:=True;

end;


function onUnload(eventID: cardinal): longbool;

begin

  // remove hook

  if hook <> nil then

  begin

    dcpp^.un_hook(hook);

    hook:=nil;

  end;

  result:=True;

end;


function TimerHandle: longbool;

begin

  result:=True;

  MessageBox(0, 'To disable this message boxes - uninstall plugin!', 'ApexTest plugin', MB_ICONINFORMATION);

end;


function pluginProc(eventID: cardinal; pData: pointer): longbool; stdcall;

begin

  result:=False;

  case eventId of

    ON_INSTALL, ON_LOAD:

      result:=onLoad(eventId, pData);

    ON_UNINSTALL, ON_UNLOAD:

      result:=onUnload(eventId);

    TIMER_MINUTE:

      result:=TimerHandle;

  end;

end;


function pluginInit(info: PMetaData): pointer; stdcall;

begin

  info^.name := PLUGIN_NAME;

  info^.author := PLUGIN_AUTHOR;

  info^.description := PLUGIN_DESC;

  info^.version := PLUGIN_VERSION;

  info^.web := PLUGIN_WEB;

  info^.apiVersion := DCAPI_VER;

  info^.compatibleVersion := DCAPI_COMPATIBLE_VER;

  info^.guid := PLUGIN_GUID;

  result:=@pluginProc;

end;


exports

  pluginInit;


end.

PluginDefs.pas

unit PluginDefs;


interface


const


  // Version of the plugin api (must change every time the API has changed)

  DCAPI_VER = 0.50;

  // The earliest version of the API that this version is backwards compatible with

  DCAPI_COMPATIBLE_VER = 0.50;


  // Hook IDs


  // Mandatory callback hook

  CALLBACK_BASE = 0;

  // Common hooks (none manadatory, however, one required)

  HOOK_PROTOCOL = 500;

  HOOK_CHAT     = 501;

  HOOK_HUBS     = 502;

  HOOK_TIMER    = 503;

  HOOK_QUEUE    = 504;

  HOOK_UI       = 505;


  // Plugin created hooks and callbacks (HOOK_USER + n)

  HOOK_USER = 1000;


  // Main hook events (returned by pluginInit)

  ON_INSTALL      = 0; // Replaces ON_LOAD for the very first loading of the plugin

  ON_UNINSTALL    = 1; // Replaces ON_UNLOAD when plugin is being uninstalled

  ON_LOAD         = 2; // Sent after successful call to pluginInit

  ON_UNLOAD       = 3; // Sent right before plugin is unloaded (no params)

  ON_CONFIGURE    = 4; // Sent when user wants to configure the plugin (obj: obj: impl. dependant or NULL)


  // Chat hook events (HOOK_CHAT)

  CHAT_IN         = 500; // Incoming chat from hub (obj: ClientData)

  CHAT_OUT        = 501; // Outgoing chat (obj: ClientData)

  CHAT_PM_IN      = 502; // Incoming private message (obj: UserData)

  CHAT_PM_OUT     = 503; // Outgoing private message (obj: UserData)


  // Timer hook events (HOOK_TIMER)

  TIMER_SECOND    = 1000; // Timer event fired once per second (tick value)

  TIMER_MINUTE    = 1001; // Timer event fired once per minute (tick value)


  // Hubs hook events (HOOK_HUBS)

  HUB_OFFLINE     = 1500; // Hub has just gone offline (obj: ClientData)

  HUB_ONLINE      = 1501; // (New) hub has just gone online (obj: ClientData)


  // Connections hook events (HOOK_PROTOCOL)

  HUB_IN          = 2000; // Incoming protocol messages from hub (obj: ClientData)

  HUB_OUT         = 2001; // Outgoing protocol message to hub (obj: ClientData)

  CONN_IN         = 2002; // Incoming client<->client protocol message (obj: ConnectionData)

  CONN_OUT        = 2003; // Outgoing client<->client protocol message (obj: ConnectionData)


  // Queue hook events (HOOK_QUEUE)

  QUEUE_ADD       = 2500; // (New) item has been added to download queue (obj: QueueData)

  QUEUE_MOVE      = 2501; // Download queue item has been moved to new location (obj: QueueData)

  QUEUE_REMOVE    = 2502; // Item has just been removed from download queue (obj: QueueData)

  QUEUE_FINISHED  = 2503; // Item has just finished downloading (obj: QueueData)


  // UI hook events (HOOK_UI)

  UI_CREATED      = 3000; // Host node UI has been created (if any, obj: impl. dependant)

  UI_CHAT_DISPLAY = 3001; // Chat messages before displayed to user (obj: StringData)


  // Plugin created hook events (EVENT_USER + n)

  EVENT_USER      = 3500;


type


  TMetaData = record

                 name: pchar;    // Name of the plugin

               author: pchar;    // Name/Nick of the plugin author

          description: pchar;    // *Short* description of plugin functionality (may be multiple lines)

                  web: pchar;    // Authors website if any

                 guid: pchar;    // Plugins unique GUID

         dependencies: ^pchar;   // Array of plugin dependencies

      numDependencies: cardinal; // Number of plugin GUIDs in dependencies array

              version: double;   // Plugin version

           apiVersion: double;   // API version the plugin was compiled against

    compatibleVersion: double;   // Earliest API version the plugin can be used with

  End;

  PMetaData = ^TMetaData;


  TDCCore = record

    // Core API version

    apiVersion: double;


    // Hook creation

    create_hook: function(hookID, hookType: cardinal; defProc: pointer): pointer; stdcall;

    destroy_hook: procedure(hHook: cardinal); stdcall;


    // Hook interaction

    set_hook: function(hookID: cardinal; hookProc, pCommon: pointer): pointer; stdcall;

    call_hook: function(hookID, eventID: cardinal; pData: pointer): LongBool; stdcall;

    un_hook: function(hHook: pointer): cardinal; stdcall;


    // Message regitster

    register_message: function(hookType: cardinal; const name: pchar): cardinal; stdcall;

    register_range: function(hookType: cardinal; const name: pchar; count: cardinal): cardinal; stdcall;

    seek_message: function(const name: pchar): cardinal; stdcall;


    // Settings management

    set_cfg: procedure(const guid, settings: pchar; cval: pointer); stdcall;

    get_cfg: function(const guid, settings: pchar; cval: pointer): LongBool; stdcall;


    // General

    memalloc: function(P: pointer; bytes: cardinal): pointer; stdcall;

    strconv: function(ctype: cardinal; dst, src: pointer; len: cardinal): cardinal; stdcall;

  end;

  PDCCore = ^TDCCore;


implementation


end.

Share this post


Link to post
Share on other sites