C#調用C++動態庫(dll)


在實際軟件開發過程中,由於公司使用了多種語言開發,在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網絡不通!");
            }

        }
復制代碼

 

生活中的不便,用軟件解決,學而不用,白學了-----紅馬車


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM