你能告訴我如何從 C# 中調用 Visual C++ 類,對此我需要什麼樣的語法?
Sunil Peddi 我有一個用 C#(用戶界面)和經典的 C++(業務邏輯)寫的應用程序。現在我需要從某 個用 C++ 寫的 DLL中調用一個函數(或方法),該函數在一個用 Visual C++ .NET 編寫的 DLL 中。而這個 Visual C++ .NET DLL 又要調用 另一個用 C# 寫的 DLL。Visual C++ .NET DLL 相當於一個代理。這樣做可行嗎?我能用 LoadLibrary 調用 Visual C++ .NET DLL 輸出的函 數,可以得到返回值,但當我試圖向 Visual C++ .Net DLL 中的函數傳遞參數時,我遇到如下錯誤:Run-Time Error Check Failure #0—The value of ESP was not properly saved
across a function call. This is usually a result of calling a function
declared with one calling convention with a function pointer declared
with a different calling convention.
我如何解決這個問題?
Giuseppe Dattilo
我得到許多關於 .Net 框架和本機 C++ 之間的互操作問題, 所以我不介意再次復習這個(well-covered)主題。有兩條路可走:從 C++ 中調用框架;或者從框架調用 C++。我不打算在此涉及 COM 的互 用性,我把它放在以後單獨的一期專欄裡討論。
讓我先從最簡單的一種開始:從 C++ 調用框架。從 C++ 程序中調用框架最簡單,最輕 松的方法是使用托管擴展(Managed Extensions)。這是微軟專用的 C++ 語言擴展,它被設計專門用來調用框架,只要包含兩個頭文件即可, 然後象使用 C++ 類一樣來使用它們。下面是一個非常簡單的調用框架 Console 類的 C++ 程序:
#using <mscorlib.dll>
#using <System.dll> // implIEd
using namespace System;
void main()
{
Console::WriteLine("Hello, world");
}
為了使用托管擴展,你只需引入 <mscorlib.dll> 和你打算使用的框架類所附著的程序集。不要忘了用 /clr 編譯。
cl /clr hello.cpp
你的 C++ 代碼可以或多或少地使用托管類,就像普通的 C++ 類一樣。例如,你可以用操作符 new 創建框架對象,並用 C++ 指針語法存取它們,象下面這樣:
DateTime d = DateTime::Now;
String* s = String::Format("The date is {0}\n", d.ToString());
Console::WriteLine(s);
Console::WriteLine(s->Length);
這裡,String s 被聲明為 String 指針,因為 String::Format 返回一個新的 String 對象。
“Hello,world”和日期/時間程序似乎很簡單——它們確實簡單 ——不過要記住不管你的程序多復雜,使用的類和 .Net 程序集有多少,其基本思路是一樣的:用 <mscorlib.dll> 以及其 它所需的程序集,然後用 new 創建托管對象,並使用指針語法來存取它們。
以上討論的是如何從 C++ 調用框架。那麼反過來從框架調 用 C++ 該如何做呢?根據你是否想調用外部 C函數或 C++ 類成員函數,有兩個選擇。我們還是首先從最簡單的開始:從 .Net 調用 C 函數。 最輕松的方法是使用 P/Invoke。使用 P/Invoke,你將外部函數聲明為某個類的靜態成員,用 DLLImport 來指定外部 DLL 中的函數。在 C# 是這樣做的:
public class Win32 {
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, String text, String caption, int type);
}
這段代碼告訴編譯器 MessageBox 是 user32.dll 中的一個函數,參數是 IntPtr (HWND),兩個 String 和一個 int。這樣你便可以在 C# 程序中調用:
Win32.MessageBox(0, "Hello World", "Platform Invoke Sample", 0);