Как ни странно, я хочу написать плагин на 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
возвращаемом параметре функции или объявлении интерфейса, но не знаю, что еще проверить.
Буду признателен за любой совет и помощь в написании этого плагина.