Как написать плагин на C # для программы с Delphi-зависимой инфраструктурой плагинов - PullRequest
0 голосов
/ 27 июня 2019

Как ни странно, я хочу написать плагин на c # для программы, написанной на Delphi (Download Master https://westbyte.com/dm/ или Internet Download Accelerator https://westbyte.com/ida/ для пользователей за пределами СНГ).Довольно странно, я знаю.Разработчик программы предоставил плагин шаблона, написанный на Delphi, который я сейчас пытаюсь переписать на c # с помощью библиотеки Unmanaged Exports (DllExport), но пока не повезло.

Ниже приведен код для шаблона плагина Delphi и онработает хорошо, как и ожидалось.

Программный код

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.

Определение интерфейсов

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.

Реализация класса

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',
    '<url>http://www.westbyte.com/plugin</url> <sectionslimit>2</sectionslimit>');
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.

И ниже моей реализации c #, которая не работает, как я ожидал.RegisterPlugIn вызывается один раз при загрузке программы, но после этого ничего не происходит.Из тестового плагина я выяснил, какая следующая функция будет вызываться getID() (свойство ID, похоже, не используется программой).

Определение интерфейсов

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; }
    }
}

Реализация класса

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();
        }
    }
}

Я также пытался переписать RegisterPlugIn как процедуру, но не повезло:

        [DllExport(CallingConvention = CallingConvention.StdCall)]
        public static void RegisterPlugIn([MarshalAs(UnmanagedType.Interface)] out DMCSharpPlugin result)
        {
            result = new DMCSharpPlugin();
        }

Iпредположим, что я где-то ошибся в RegisterPlugIn возвращаемом параметре функции или объявлении интерфейса, но не знаю, что еще проверить.

Буду признателен за любой совет и помощь в написании этого плагина.

...