1. 程式人生 > >C#開發奇技淫巧二:根據dll文件加載C++或者Delphi插件

C#開發奇技淫巧二:根據dll文件加載C++或者Delphi插件

man int cnblogs 需要 pat method msg initial 函數指針

原文:C#開發奇技淫巧二:根據dll文件加載C++或者Delphi插件

這兩天忙著把框架改為支持加載C++和Delphi的插件,來不及更新blog了。
原來的寫的框架只支持c#插件,這個好做,直接用c#的反射功能便可。但是公司不是所有人都搞C#,也不是所有的程序C#都能很好的完成,又或者其他公司提供的API不是C#的,這個時候,就需要這個框架能夠支持多種語言了。
廢話不多說,進入正題。
上網一搜,C#加載非托管的dll,無非就是使用 DllImportAttribute 。然而,這個屬性裏面要指明dll所在的路徑,因為又是寫在屬性中,因此是在編譯的時候就已經把路徑寫死了,不能動態指定路徑加載。
於是又找了下,終於發現了c#中的一個函數:Marshal.GetDelegateForFunctionPointer。這個函數的功能就是將非托管的函數指針轉換為委托。至此,任務完成。Dll的功能無非提提供各種函數,組成所謂的API,有了上述的方法之後,在C#中定義相關的委托(方法的參數列表和參數類型要跟非托管的Dll的參數類型和參數列表對應,具體的對應請google),然後調用上述方法,將非托管的dll轉換為相應的委托,這樣就能調用非托管的dll了。
在C#中,我們定義相關的接口,在方法實現中調用相應的委托,這樣,一個插件對象就完成了。下面給出相應的類庫和使用實例。

1 public class LoadDll
2 {
3 #region Win32 API : Load dll
4 [DllImport("kernel32.dll")]
5 public static extern IntPtr LoadLibrary(string path);
6
7 [DllImport("kernel32.dll")]
8 public static extern IntPtr GetProcAddress(IntPtr lib, string funcName);
9
10 [DllImport("kernel32.dll")]
11 public static extern bool FreeLibrary(IntPtr lib);
12
13 [DllImport("kernel32.dll")]
14 public static extern IntPtr GetStdHandle(int nStdHandle);
15
16 [DllImport("user32", EntryPoint = "CallWindowProc")]
17 public static
extern int CallWindowProc(IntPtr lpPreWndFunc, int hwnd, int msg, int wParam, int lParam);
18 #endregion
19
20 private IntPtr _dllLib;
21
22 /// <summary>
23 /// Initializes a new instance of the <see cref="LoadDll"/> class.
24 /// </summary>
25 public LoadDll()
26 {
27
28 }
29
30 /// <summary>
31 /// Initializes a new instance of the <see cref="LoadDll"/> class.
32 /// </summary>
33 /// <param name="path">The path.</param>
34 public LoadDll(string path)
35 {
36 _dllLib = LoadLibrary(path);
37 }
38
39 /// <summary>
40 /// 註銷對象時釋放資源
41 /// <see cref="LoadDll"/> is reclaimed by garbage collection.
42 /// </summary>
43 ~LoadDll()
44 {
45 FreeLibrary(_dllLib);
46 }
47
48 /// <summary>
49 /// 初始化dll的路徑
50 /// </summary>
51 /// <param name="path">The path.</param>
52 public void InitPath(string path)
53 {
54 if (_dllLib == IntPtr.Zero)
55 _dllLib = LoadLibrary(path);
56 }
57
58 /// <summary>
59 /// 根據非托管的dll中的方法名稱映射成托管的委托類型
60 /// </summary>
61 /// <param name="methodName">非托管的dll中的方法名稱</param>
62 /// <param name="methodType">托管的委托類型</param>
63 /// <returns></returns>
64 public Delegate InvokeMethod(string methodName, Type methodType)
65 {
66 //獲取非托管的dll中方法的地址
67 var methodPtr = GetProcAddress(_dllLib, methodName);
68 //將非托管的方法轉換為委托
69 return Marshal.GetDelegateForFunctionPointer(methodPtr, methodType);
70 }
71 }

調用:

1 loadDll = new LoadDll(path);
2 stop = (StopHandler)loadDll.InvokeMethod("stop", typeof(StopHandler));
3 start = (StartHandler)loadDll.InvokeMethod("start", typeof(StartHandler));
4 init = (InitHandler)loadDll.InvokeMethod("init", typeof(InitHandler));
5 query = (QueryHandler)loadDll.InvokeMethod("query", typeof(QueryHandler));
6 setDatabaseInfo = (SetDatabaseInfoHandler)loadDll.InvokeMethod("setDatabaseInfo", typeof(SetDatabaseInfoHandler));
7 setMonitorInfo = (SetMonitorInfoHandler)loadDll.InvokeMethod("setMonitorInfo", typeof(SetMonitorInfoHandler));


c++中的導出方法:

1 /// 插件導出方法
2 extern "C" __declspec(dllexport) void setDatabaseInfo(LPCTSTR dbServer, LPCTSTR dbUser, LPCTSTR dbPass, LPCTSTR dbName);
3 extern "C" __declspec(dllexport) void setMonitorInfo(LPCTSTR _agentBm, LPCTSTR _com);
4 extern "C" __declspec(dllexport) void init();
5 extern "C" __declspec(dllexport) void start();
6 extern "C" __declspec(dllexport) void stop();
7 extern "C" __declspec(dllexport) LPCTSTR query(LPCTSTR devname, LPCTSTR id);

C#中的委托

1 ///<summary>
2 /// 處理Stop事件
3 ///</summary>
4 publicdelegatevoid StopHandler();
5 ///<summary>
6 /// 處理Start事件
7 ///</summary>
8 publicdelegatevoid StartHandler();
9 ///<summary>
10 /// 處理Init事件
11 ///</summary>
12 publicdelegatevoid InitHandler();
13 ///<summary>
14 /// 處理Query事件
15 ///</summary>
16 publicdelegatestring QueryHandler(string devName, string id);
17 ///<summary>
18 /// 處理SetDataBaseInfo事件
19 ///</summary>
20 publicdelegatevoid SetDatabaseInfoHandler(string server,string user,string password,string dbName);
21 ///<summary>
22 /// 處理SetMonitorInfo事件
23 ///</summary>
24 publicdelegatevoid SetMonitorInfoHandler(string agentBm, string com);

接下來怎麽搞,你們都懂的

C#開發奇技淫巧二:根據dll文件加載C++或者Delphi插件