在既有 MFC (C++) 專案中整合 C# 組件,需啟用 `/clr` 混合模式編譯、正確引用組件、處理 Managed/Unmanaged 邊界。本文提供完整設定步驟、程式碼範例、常見問題解決方案。
Common Language Runtime Support (/clr)Use MFC in a Shared DLLProgram Database (/Zi) (非 /ZI)Multi-threaded DLL (/MD) 或 Multi-threaded Debug DLL (/MDd)// C# 類別庫專案
using System;
using System.Runtime.InteropServices;
namespace CSharpLib
{
[ComVisible(true)]
[Guid("YOUR-GUID-HERE")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Calculator
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
public double Multiply(double a, double b) => a * b;
public string GetMessage(string name) => $"Hello, {name} from C#!";
}
}
// AssemblyInfo.cs
[assembly: ComVisible(true)]
[assembly: Guid("ASSEMBLY-GUID-HERE")]
// 專案屬性 → Build Events → Post-build event command line
"$(FrameworkSDKDir)bin\gacutil.exe" /i "$(TargetPath)"
"$(FrameworkSDKDir)bin\regasm.exe" "$(TargetPath)" /tlb /codebase
// stdafx.h 或專用標頭檔
#using
// 使用相對路徑或完整路徑引用 C# DLL
#using "..\CSharpLib\bin\Debug\CSharpLib.dll"
using namespace System;
using namespace CSharpLib; // C# 命名空間
// 在需要呼叫的 .cpp 檔案
#pragma managed // 啟用 Managed 程式碼區塊
void CMFCAppDlg::OnBnClickedCallCSharp()
{
try
{
// 實例化 C# 類別
Calculator^ calc = gcnew Calculator();
// 呼叫方法
int sum = calc->Add(10, 20);
int diff = calc->Subtract(50, 30);
double product = calc->Multiply(3.14, 2.0);
String^ msg = calc->GetMessage("MFC User");
// 轉換為 MFC 字串顯示
CString strResult;
strResult.Format(_T("Add: %d\nSubtract: %d\nMultiply: %.2f\nMessage: %s"),
sum, diff, product, (LPCTSTR)Marshal::StringToHGlobalAuto(msg));
MessageBox(strResult, _T("C# DLL 呼叫結果"), MB_OK);
// 釋放 Marshal 記憶體
Marshal::FreeHGlobal(Marshal::StringToHGlobalAuto(msg));
}
catch (Exception^ ex)
{
CString errMsg;
errMsg.Format(_T("呼叫 C# DLL 失敗:%s"), (LPCTSTR)Marshal::StringToHGlobalAuto(ex->Message));
MessageBox(errMsg, _T("錯誤"), MB_ICONERROR);
}
}
#pragma unmanaged // 回復 Unmanaged 程式碼區塊
// C# String^ → MFC CString
String^ managedStr = ...;
CString mfcStr = (LPCTSTR)Marshal::StringToHGlobalAuto(managedStr);
// 使用後必須釋放
Marshal::FreeHGlobal((IntPtr)(LPCTSTR)mfcStr); // 或儲存 IntPtr 稍後釋放
// MFC CString → C# String^
CString mfcStr = _T("Hello");
String^ managedStr = gcnew String(mfcStr); // 自動轉換
try
{
// Managed 程式碼
}
catch (System::Exception^ ex) // 捕獲 Managed 例外
{
// 處理
}
catch (...) // 捕獲 Unmanaged 例外 (SEH)
{
// 處理
}
// C# 定義委派介面
public delegate void ProgressCallback(int percent, string message);
[ComVisible(true)]
public interface IProgressReporter
{
void Report(int percent, string message);
}
// MFC 實作介面
#pragma managed
public ref class MfcProgressReporter : public IProgressReporter
{
HWND _hWnd;
public:
MfcProgressReporter(HWND hWnd) : _hWnd(hWnd) {}
virtual void Report(int percent, String^ message)
{
// 發送訊息到 MFC 視窗
::PostMessage(_hWnd, WM_USER_PROGRESS, percent,
(LPARAM)Marshal::StringToHGlobalAuto(message));
}
};
#pragma unmanaged
// C# 使用
public void DoWork(IProgressReporter reporter)
{
for (int i = 0; i <= 100; i += 10)
{
reporter.Report(i, $"Processing {i}%");
Thread.Sleep(100);
}
}
MFC 呼叫 C# DLL 的關鍵在於:
此技術適用於漸進式現代化既有 MFC 系統,但新專案建議考慮進程隔離架構降低耦合複雜度。