title: How to write dll plugin on c# for program with delphi-reliant plugin infrastructure
body: <p>Oddly enough, I want to write a plugin on c# for a program written on Delphi (Download Master <a href="https://westbyte.com/dm/" rel="nofollow noreferrer">https://westbyte.com/dm/</a> or Internet Download Accelerator <a href="https://westbyte.com/ida/" rel="nofollow noreferrer">https://westbyte.com/ida/</a> for non-CIS users). Quite odd, I know. The program developer has supplied template plugin written on Delphi which I now trying to rewrite on c# using Unmanaged Exports library (<code>DllExport</code>), but no luck so far.</p>
  
  <p>Below the code for Delphi-plugin template and it's working well as expected.</p>
  
  <p><em>Program code</em></p>
  
  <pre class="lang-pascal prettyprint-override"><code>library dm_delphi_plugin;
  
  uses
    System.SysUtils, System.Classes, DMPluginIntf in 'DMPluginIntf.pas', dmtest_pluginImpl in 'dmtest_pluginImpl.pas';
  
  {$R *.res}
  
  function RegisterPlugIn: IDMPlugIn; stdcall;
  begin
    try
      Result := TDMTestPlugIn.Create;
    except
      Result := nil;
    end;
  end;
  
  exports RegisterPlugIn;
  
  begin
  end.
  </code></pre>
  
  <p><em>Interfaces definition</em></p>
  
  <pre class="lang-pascal prettyprint-override"><code>unit DMPluginIntf;
  
  interface
  
  type
    IDMInterface = interface(IUnknown)
      ['{B412B405-0578-4B99-BB06-368CDA0B2F8C}']
      function DoAction(action: WideString; parameters: WideString)
        : WideString; stdcall;
    end;
  
    IDMPlugIn = interface(IUnknown)
      ['{959CD0D3-83FD-40F7-A75A-E5C6500B58DF}']
      function getID: WideString; stdcall;
      function GetName: WideString; stdcall;
      function GetVersion: WideString; stdcall;
      function GetDescription(language: WideString): WideString; stdcall;
      function GetEmail: WideString; stdcall;
      function GetHomepage: WideString; stdcall;
      function GetCopyright: WideString; stdcall;
      function GetMinAppVersion: WideString; stdcall;
      procedure PluginInit(_IDmInterface: IDMInterface); stdcall;
      procedure PluginConfigure(params: WideString); stdcall;
      procedure BeforeUnload; stdcall;
      function EventRaised(eventType: WideString; eventData: WideString)
        : WideString; stdcall;
      property ID: WideString read getID;
    end;
  
  implementation
  end.
  </code></pre>
  
  <p><em>Class implementation</em></p>
  
  <pre class="lang-pascal prettyprint-override"><code>unit dmtest_pluginImpl;
  
  interface
  
  uses DMPluginIntf, Classes, Vcl.Dialogs;
  
  const
    myPluginID = '{5857F431-C0D8-487C-9A4E-9C2E5A9005FA}';
    myPluginName = 'DM Delphi Template Plugin';
    myPluginVersion = '0.1.1.1';
    myPluginEmail = 'e@mail.com';
    myPluginHomePage = 'https://google.com/';
    myPluginCopyRight = chr(169) + '2019 Developer';
    myMinNeedAppVersion = '6.17.1';
    myPluginDescription = 'Test Plug-in for show how create plugins for DM';
  
  type
    TDMTestPlugIn = class(TInterfacedObject, IDMPlugIn)
    private
      myIDmInterface: IDmInterface;
    protected
  
    public
      function getID: WideString; stdcall;
      function GetName: WideString; stdcall;
      function GetVersion: WideString; stdcall;
      function GetDescription(language: WideString): WideString; stdcall;
      function GetEmail: WideString; stdcall;
      function GetHomepage: WideString; stdcall;
      function GetCopyright: WideString; stdcall;
      function GetMinAppVersion: WideString; stdcall;
      procedure PluginInit(_IDmInterface: IDmInterface); stdcall;
      procedure PluginConfigure(params: WideString); stdcall;
      procedure BeforeUnload; stdcall;
      function EventRaised(eventType: WideString; eventData: WideString)
        : WideString; stdcall;
      property ID: WideString read getID;
    end;
  
  implementation
  
  function TDMTestPlugIn.GetName: WideString;
  begin
    Result := myPluginName;
  end;
  
  function TDMTestPlugIn.GetVersion: WideString;
  begin
    Result := myPluginVersion;
  end;
  
  function TDMTestPlugIn.GetDescription(language: WideString): WideString;
  begin
    { code is simplified }
    Result := myPluginDescription;
  end;
  
  function TDMTestPlugIn.getID: WideString; stdcall;
  begin
    Result := myPluginID;
  end;
  
  procedure TDMTestPlugIn.PluginInit(_IDmInterface: IDmInterface);
  begin
    myIDmInterface := _IDmInterface;
  end;
  
  procedure TDMTestPlugIn.BeforeUnload;
  begin
    myIDmInterface := nil;
  end;
  
  procedure TDMTestPlugIn.PluginConfigure(params: WideString);
  begin
    ShowMessage('Showing config form');
    myIDmInterface.DoAction('AddingURL',
      '&lt;url&gt;http://www.westbyte.com/plugin&lt;/url&gt; &lt;sectionslimit&gt;2&lt;/sectionslimit&gt;');
  end;
  
  function TDMTestPlugIn.EventRaised(eventType: WideString; eventData: WideString)
    : WideString;
  begin
    if eventType = 'dm_download_added' then
      ShowMessage('Added ID=' + eventData);
  end;
  
  function TDMTestPlugIn.GetEmail: WideString;
  begin
    Result := myPluginEmail;
  end;
  
  function TDMTestPlugIn.GetHomepage: WideString;
  begin
    Result := myPluginHomePage;
  end;
  
  function TDMTestPlugIn.GetCopyright: WideString;
  begin
    Result := myPluginCopyRight;
  end;
  
  function TDMTestPlugIn.GetMinAppVersion: WideString;
  begin
    Result := myMinNeedAppVersion;
  end;
  
  end.
  </code></pre>
  
  <p>And below my c# realization, which doesn't work as expected by me. <code>RegisterPlugIn</code> called once when the program loading, but nothing happened after. From the test plugin, I've figured out what next function to be called is <code>getID()</code> (property <code>ID</code> seems to be unused by the program).</p>
  
  <p><em>Interfaces definition</em></p>
  
  <pre class="lang-cs prettyprint-override"><code>namespace dm_csharp_plugin
  {
      [ComImport]
      [ComVisible(true)]
      [Guid("B412B405-0578-4B99-BB06-368CDA0B2F8C"),
          InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
      public interface IDMInterface
      {
          string DoAction([MarshalAs(UnmanagedType.BStr)]string action, [MarshalAs(UnmanagedType.BStr)]string parameters);
      }
  
      [ComVisible(true)]
      [Guid("959CD0D3-83FD-40F7-A75A-E5C6500B58DF")]
      [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
      interface IDMPlugin
      {
          [return: MarshalAs(UnmanagedType.BStr)]
          string getID();
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string GetName();
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string GetVersion();
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string GetDescription([MarshalAs(UnmanagedType.BStr)]string language);
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string GetEmail();
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string GetHomepage();
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string GetCopyright();
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string GetMinAppVersion();
  
          void PluginInit([MarshalAs(UnmanagedType.Interface)]IDMInterface _IDmInterface);
  
          void PluginConfigure();
  
          void BeforeUnload();
  
          [return: MarshalAs(UnmanagedType.BStr)]
          string EventRaised([MarshalAs(UnmanagedType.BStr)]string eventType, [MarshalAs(UnmanagedType.BStr)]string eventData);
  
          string ID { [return: MarshalAs(UnmanagedType.BStr)]get; }
      }
  }
  </code></pre>
  
  <p><em>Class implementation</em></p>
  
  <pre class="lang-cs prettyprint-override"><code>namespace dm_csharp_plugin
  {
      public class Win32MsgBox
      {
          [DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "MessageBox")]
          private static extern int _MessageBox(IntPtr hWnd, String text, String caption, uint type);
  
          public static void MessageBox(string text, string caption)
          {
              _MessageBox(new IntPtr(0), text, caption, 0);
          }
      }
  
      public class DMCSharpPlugin : Object, IDMPlugin
      {
          [return: MarshalAs(UnmanagedType.BStr)]
          public string GetCopyright()
          {
              return "";
          }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string GetDescription(string language)
          {
              return "";
          }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string GetEmail()
          {
              return "";
          }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string GetHomepage()
          {
              return "";
          }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string getID()
          {
              Win32MsgBox.MessageBox("getID()", "Method called");
              return "{5857F431-C0D8-487C-9A4E-9C2E5A9005FA}";
          }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string GetMinAppVersion()
          {
              return "6.17.1";
          }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string GetName()
          {
              return "";
          }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string GetVersion()
          {
              return "";
          }
  
          public void PluginInit([MarshalAs(UnmanagedType.Interface)]IDMInterface dmInstance) { }
  
          public void PluginConfigure() { }
  
          [return: MarshalAs(UnmanagedType.BStr)]
          public string EventRaised([MarshalAs(UnmanagedType.BStr)]string eventType, [MarshalAs(UnmanagedType.BStr)]string eventData)
          {
              return "";
          }
  
          public void BeforeUnload() { }
  
          public string ID
          {
              [return: MarshalAs(UnmanagedType.BStr)]
              get
              {
                  Win32MsgBox.MessageBox("ID property readed", "Method called");
  
                  return "";
              }
          }
  
      }
  
      public static class Exports
      {
          [DllExport]
          [return: MarshalAs(UnmanagedType.Interface)]
          public static DMCSharpPlugin RegisterPlugIn()
          {
              Win32MsgBox.MessageBox("RegisterPlugIn begin", "Hello Dialog");
              return new DMCSharpPlugin();
          }
      }
  }
  
  </code></pre>
  
  <p>I also tried to rewrite <code>RegisterPlugIn</code> as a procedure, but no luck:</p>
  
  <pre class="lang-cs prettyprint-override"><code>        [DllExport(CallingConvention = CallingConvention.StdCall)]
          public static void RegisterPlugIn([MarshalAs(UnmanagedType.Interface)] out DMCSharpPlugin result)
          {
              result = new DMCSharpPlugin();
          }
  </code></pre>
  
  <p>I suppose I've mistaken somewhere in <code>RegisterPlugIn</code> function return parameter or interface declaration, but don't know what else to check.</p>
  
  <p>I would appreciate any piece of advice and help with writing this plugin.</p>