在實際軟件開發過程中,由于公司使用了多種語言開發,在C#中可能需要實現某個功能,而該功能可能用其他語言已經實現了,那么我們可以調用其他語言寫好的模塊嗎?還有就是,由于C#開發好的項目,我們可以利用reflector等反編譯工具反編譯出其源代碼,所以對于一些核心算法,我們不希望被別人知道,因此為了增強代碼的安全性,我們需要將一些核心算法用C或C++來編寫,然后用C#來調用這些已經寫好的接口。在面對以上情況時,我們該怎么做呢?
?方案一:重新實現
??????? 針對第一種情況,我們可以將C或者C++功能用C#來重新實現,這樣的話代碼比較統一,維護比較方便,但是這樣的話增加了軟件開發的成本,把C++的代碼功能改成C#涉及到指針和內存的操作比較繁瑣,況且有開發好的模塊為什么不重復利用呢?針對第二種情況就不能得到有效解決,雖然可以使用混淆器對代碼進行混淆,但是任然不是很安全。
?方案二:封裝COM組件
??????? 我們可以將C或者C++的函數封裝成COM組件,在C#中調用時比較方便,但是COM組件需要注冊,而且多次注冊可能也會導致一些問題,同時在處理C或者C++的類型與COM組件的類型轉換的時候也可能有些麻煩。
?方案三:使用動態鏈接庫
??????? 我們可以直接調用C或者C++已經寫好的動態鏈接庫,這樣比較方便,這樣很好的解決了上述問題。
?
??????? 在實際項目中,我們需要使用C#調用C++的一些接口,因此我使用的是方案三采用動態庫,下面我就在實際中怎么處理的進行說明。
?????? ?在調用動態庫的過程中我也遇到了以下一些問題:
????????1、C++中有指針,C#中需要使用指針嗎?
????????由于C++中的動態庫中有指針參數,因此我也是用.NET的不安全代碼,使用了C#的指針,但是最后也還是出現了一些問題,如在C#中傳入的參數是一個二維數組時就出現了問題,這個問題我在網上找了好多資料也沒有解決,最后和c++程序員商量了下改變了傳入參數的參數類型。最后也沒有使用指針。
????????2、C#和C++中的類型如何轉換呢?
??????? 雖然C#和C++比較類似,但是其給我們的參數類型我們要與C#的參數類型一一對應起來,因此我找了一些資料把其類型一一對應了,具體看后續說明。
????????3、C++寫好的動態庫放到那個位置呢?
????????關于C++動態庫的位置也是個問題,在應用中我們使用了相對路徑和絕對路徑進行測試,有的發現在VS中可以調用到,但是發布后發現無法調用到動態庫,最后只要把動態的dll放到系統的目錄system32下面才解決了改問題,目前還沒找到其他的方法,如有其他的更好方法還請大家指點。
????????4、如何反編譯C++的dll的名稱,端口?
??????? 可以通過Dependency Walker工具進行反編譯查看別人寫的動態庫的信息
????????5、還有其他的一些細節,如C#調用動態庫需要指定其編碼、代碼寫法等等
c#調用c++動態庫一般我們這樣寫
[DllImport("UCamer.dll", CallingConvention = CallingConvention.Winapi)]
public extern static void Disp_Destroy(IntPtr hShow);
DllImport的第一個參數UCamer.dll是動態庫dll的路徑,此dll放在程序運行的根目錄或者c:windows/sytem32下
??CallingConvention 參數是c#調用c++的方式 是個枚舉 msdn解釋如下
Cdecl | 調用方清理堆棧。這使您能夠調用具有?varargs?的函數(如?Printf),使之可用于接受可變數目的參數的方法。? |
FastCall | 不支持此調用約定。 |
StdCall | 被調用方清理堆棧。這是使用平臺 invoke 調用非托管函數的默認約定。? |
ThisCall | 第一個參數是?this?指針,它存儲在寄存器 ECX 中。其他參數被推送到堆棧上。此調用約定用于對從非托管 DLL 導出的類調用方法。? |
Winapi | 此成員實際上不是調用約定,而是使用了默認平臺調用約定。例如,在 Windows 上默認為?StdCall,在 Windows CE.NET 上默認為?Cdecl。? |
?從上面來看Winapi方式是根據系統自動選擇調用規約的。 而thisCall是對c++類的調用方法。 所以 一般情況下我們選擇Winapi就可以了。
例子:
#region 無標題窗體右鍵任務欄彈出菜單代碼[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]public static extern int GetWindowLong(HandleRef hWnd, int nIndex);[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]public static extern IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong);protected override CreateParams CreateParams{get{const int WS_MINIMIZEBOX = 0x00020000; // Winuser.h中定義 CreateParams cp = base.CreateParams;cp.Style = cp.Style | WS_MINIMIZEBOX; // 允許最小化操作 return cp;}}#endregion#region 窗體拖動代碼[DllImport("user32.dll")]public static extern bool ReleaseCapture();[DllImport("user32.dll")]public static extern bool SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);public const int WM_SYSCOMMAND = 0x0112;public const int SC_MOVE = 0xF010;public const int HTCAPTION = 0x0002;private void Login_MouseDown(object sender, MouseEventArgs e){ReleaseCapture();SendMessage(this.Handle, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);}#endregion[DllImport("wininet.dll")]private extern static bool InternetGetConnectedState(out int conn, int val);private void btnNetTest_Click(object sender, EventArgs e){int Out;if (InternetGetConnectedState(out Out, 0) == true){MessageDxUtil.ShowTips("Internet網絡連通!");}else{MessageDxUtil.ShowTips("Internet網絡不通!");}}
?