其實不知道這個文章到底要分類再C#還是C++呢....
麻~C++的部分含量較重,那就分在C++好了
這篇會使用到extern "C"和DllImport,建議先行了解
以下皆在Visual Studio 2015上完成!
剛開始時先檔案→新增→專案,選擇Visual C++的Win32專案,命名為ForCSharpCall
應用程式設定選擇DLL和空專案
完成後是只有資料夾而已
此時對ForCSharpCall點右鍵→屬性(property),組態屬性→紅框處選擇支援,若不支援則無法給C#呼叫
然後新增一些檔案
先撰寫ForCSharpCallDefine.h
#pragma once
class Calculate
{
public:
Calculate();//預設的空建構子
int Addition(int a, int b);//加法
int Subtraction(int a, int b);//減法
int Multiplication(int a, int b);//乘法
float Division(int a, int b);//除法
};
再來是ForCSharpCallImplement.cpp,很簡單的加減乘除而已
#include "ForCSharpCallDefine.h"
Calculate::Calculate()
{
}
int Calculate::Addition(int a, int b)
{
return a + b;
}
int Calculate::Subtraction(int a, int b)
{
return a - b;
}
int Calculate::Multiplication(int a, int b)
{
return a * b;
}
float Calculate::Division(int a, int b)
{
return a / b;
}
最後是最重要的!連接C#和C++的部分
ForExternCall.cpp
#ifdef FORCSHARPCALL_EXPORTS //同專案名稱,只是後面固定為_EXPORTS
#define FORCSHARPCALL_API __declspec(dllexport) //請注意!正確的是Export要亮起
#else
#define FORCSHARPCALL_API __declspec(dllimport)
#endif
#include "ForCSharpCallDefine.h"
//可以將class的物件宣告在外面變成全域
//Calculate cal;
extern "C" FORCSHARPCALL_API int Add(int a, int b)
{
Calculate cal;//宣告在function內,只有該function能使用
return cal.Addition(a, b);
}
extern "C" FORCSHARPCALL_API int Sub(int a, int b)
{
Calculate cal;//宣告在function內,只有該function能使用
return cal.Subtraction(a, b);
}
extern "C" FORCSHARPCALL_API int Multi(int a, int b)
{
Calculate cal;//宣告在function內,只有該function能使用
return cal.Multiplication(a, b);
}
extern "C" FORCSHARPCALL_API float Div(int a, int b)
{
Calculate cal;//宣告在function內,只有該function能使用
return cal.Division(a, b);
}
以上都完成後,按下建置→建置方案(Ctrl+Shift+B)
之後就是建立C#專案,從C#呼叫啦!
為了方便,所以建立了一個主控台應用程式(Console Application),命名為TestCall
using System;
using System.Runtime.InteropServices;
namespace TestCall
{
class Program
{
//傳入和傳出的型態格式必須同ForExternCall.cpp內的設置!!
//我將dll檔放到D:,然後設置進入點
[DllImport("D:/ForCSharpCall.dll", EntryPoint="Add", CallingConvention = CallingConvention.Cdecl)]
private static extern int add(int a, int b);
//也可以不設置進入點,但函數名稱必須相同
[DllImport("D:/ForCSharpCall.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int Sub(int a, int b);
[DllImport("D:/ForCSharpCall.dll", EntryPoint = "Multi", CallingConvention = CallingConvention.Cdecl)]
private static extern int mul(int a, int b);
[DllImport("D:/ForCSharpCall.dll", EntryPoint = "Div", CallingConvention = CallingConvention.Cdecl)]
private static extern float div(int a, int b);
static void Main(string[] args)
{
Console.WriteLine(add(10, 20));
Console.WriteLine(Sub(10, 20));
Console.WriteLine(mul(10, 20));
Console.WriteLine(div(10, 20));
Console.ReadKey();
}
}
}
執行結果如下(第四個是0沒錯,因為兩個int相除一定是int,只是完成後才突然想到。反正大家知道就好,我懶得改了)
如果出現這種錯誤訊息:System.BadImageFormatException: 試圖載入格式錯誤的程式。 (發生例外狀況於 HRESULT: 0x8007000B)
原因就是Visual Studio編譯環境的問題,將Any CPU或x64改成x86即可解決。

我學習了,謝謝分享 =)
選CLR支援再建置的話會跳出一個警告是 C4199: 不支援 C++/CLI、C++/CX 或 OpenMP 的兩階段名稱查閱; 請使用 /Zc:twoPhase- 請問這個要怎麼處理? 不管他會有影響嗎?
警告的話就直接無視吧,除非編譯不了或編出來錯誤再改
本人試run發現這個問題 對 PInvoke 函式 'ConsoleApplication1!ConsoleApplication1.Program::add' 的呼叫已使堆疊失去平衡。這可能是因為 Managed PInvoke 簽章和 Unmanaged 目標簽章不相符。請確認 PInvoke 簽章的呼叫慣例及參數與目標 Unmanaged 簽章是否相符。 建議在 [DllImport("D:/ForCSharpCall.dll", EntryPoint="Add")] 改成 [DllImport("D:/ForCSharpCall.dll", EntryPoint = "Add", CallingConvention = CallingConvention.Cdecl)] 就可以排除這個問題
感謝改正!
好用 且直接不用在C#中參考過來過去
謝謝分享
*****
原本在搜尋引擎找出一堆 Blog 文章,不知哪幾篇值得花時間一看, 後來用 PTT搜尋引擎,輾轉看到您這的好文而有緣來到這, 謝謝您用心分享有價值的內容, 也回饋給您這實用的主題排名網站資訊,可查看與您 Blog 內容相關的排名好文,應該對寫 Blog 也有所幫助,期待您持續產出好文章 ^^ https://searchptt.cc/