首先包一個C++的DLL
#pragma once
//求和
extern "C" __declspec(dllexport) int Add(int &a,int &b);
//求和1
extern "C" __declspec(dllexport) int Add1(int a, int b);
#include "MyDLL.h"
using namespace std;
//求和
int Add(int &a,int &b)
{
return a+b;
}
//求和1
int Add1(int a, int b)
{
return a + b;
}
然後編譯生成得到一個MyDLL.dll文件
C++代碼調用
#include <iostream>
#include <Windows.h>
#include <string>
using std::string;
using std::cin;
using std::cout;
using std::endl;
LPCWSTR StringToLPCWSTR(string orig);
int main()
{
//申明
typedef int(*Add)(int &a,int &b);
//加載程序集
HMODULE hDllmy = LoadLibraryW(StringToLPCWSTR("MyDLL.dll"));
//獲得錯誤
auto err1 = GetLastError();
if (hDllmy != NULL)
{
//得到方法
Add AddHandle = (Add)GetProcAddress(hDllmy, "Add");
if (AddHandle != NULL)
{
int a = 1;
int b = 1;
//執行
int he = AddHandle(a, b);
cout << "和:" << he << endl;
}
FreeLibrary(hDllmy);
}
/*typedef string(*ReadMagCard)(string type);
HMODULE hDll = LoadLibraryW(StringToLPCWSTR("WQReadPersonInfo.dll"));
if (hDll != NULL)
{
auto aa= GetProcAddress(hDll, "ReadMagCard");
auto err=GetLastError();
ReadMagCard ReadHandle = (ReadMagCard)GetProcAddress(hDll, "ReadMagCard");
if (ReadHandle != NULL)
{
cout<< "讀到卡號:"<<ReadHandle("RType")<< endl;
}
FreeLibrary(hDll);
}*/
string in;
cin >> in;
return 0;
}
//String轉LPCWSTR
LPCWSTR StringToLPCWSTR(string orig)
{
size_t origsize = orig.length() + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
wchar_t* wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (orig.length() - 1));
mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
return wcstring;
}
效果
如果加載程序集和方法不成功可以用depends.exe查看動態庫的依賴,然後補全依賴就行
C#代碼調用
/// <summary>
/// 測試C++
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTestC_Click(object sender, EventArgs e)
{
object[] para = new object[2];
int a =1;
int b =1;
para[0] = a;
para[1] = b;
object ret = InvokeDllUtil.CommonDllInvoke(Application.StartupPath + "\\MyDLL.dll", "Add1", para, typeof(int));
MessageBox.Show(ret.ToString());
}
用反射發出動態構造C#調用方法代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;
namespace DllInvokTest
{
///<summary NoteObject="Class">
/// [功能描述:動態調用dll的工具類] <para/>
/// [創建者:zlz] <para/>
/// [創建時間:2020年5月12日] <para/>
///<說明>
/// [說明:動態調用dll的工具類]<para/>
///</說明>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///<修改記錄>
/// [修改時間:本次修改時間]<para/>
/// [修改內容:本次修改內容]<para/>
///</修改記錄>
///</summary>
public class InvokeDllUtil
{
/// <summary>
/// 加載程序集
/// </summary>
/// <param name="lpLibFileName"></param>
/// <returns></returns>
[System.Runtime.InteropServices.DllImport("kernel32")]
private static extern long GetLastError();
/// <summary>
/// 加載程序集
/// </summary>
/// <param name="lpLibFileName"></param>
/// <returns></returns>
[System.Runtime.InteropServices.DllImport("kernel32")]
private static extern IntPtr LoadLibrary(string lpLibFileName);
/// <summary>
/// 得到方法地址
/// </summary>
/// <param name="hModule"></param>
/// <param name="lpProcName"></param>
/// <returns></returns>
[System.Runtime.InteropServices.DllImport("kernel32")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
/// <summary>
/// 釋放程序集
/// </summary>
/// <param name="hLibModule"></param>
/// <returns></returns>
[System.Runtime.InteropServices.DllImport("kernel32")]
private static extern IntPtr FreeLibrary(IntPtr hLibModule);
/// <summary>
/// 動態調用Windows DLL
/// </summary>
/// <param name="fileName">Dll文件名</param>
/// <param name="funName">待調用的函數名</param>
/// <param name="objParams">函數參數</param>
/// <param name="returnType">返回值</param>
/// <returns>調用結果</returns>
public static object CommonDllInvoke(string fileName, string funName, object[] objParams, Type returnType)
{
IntPtr libHandle = IntPtr.Zero;
try
{
//獲取函數地址
libHandle = LoadLibrary(fileName);
if (libHandle == IntPtr.Zero)
{
long err=GetLastError();
return null;
}
IntPtr procAddres = GetProcAddress(libHandle, funName);
if (procAddres == IntPtr.Zero)
{
return null;
}
//獲取參數類型
Type[] paramTypes = new Type[objParams.Length];
for (int i = 0; i < objParams.Length; ++i)
{
paramTypes[i] = objParams[i].GetType();
}
//構建調用方法模型
AssemblyName asembyName = new AssemblyName();
asembyName.Name = "WinDllInvoke_Assembly";
AssemblyBuilder asembyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asembyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = asembyBuilder.DefineDynamicModule("WinDllInvoke");
MethodBuilder methodBuilder = moduleBuilder.DefineGlobalMethod("InvokeFun", MethodAttributes.Public | MethodAttributes.Static, returnType, paramTypes);
//獲取一個 ILGenerator ,用於發送所需的 IL
ILGenerator IL = methodBuilder.GetILGenerator();
for (int j = 0; j < paramTypes.Length; ++j)
{
//將參數壓入堆棧
if (paramTypes[j].IsValueType)
{
//By Value
IL.Emit(OpCodes.Ldarg, j);
}
else
{
//By Addrsss
IL.Emit(OpCodes.Ldarga, j);
}
}
// 判斷處理器類型
if (IntPtr.Size == 4)
{
IL.Emit(OpCodes.Ldc_I4, procAddres.ToInt32());
}
else if (IntPtr.Size == 8)
{
IL.Emit(OpCodes.Ldc_I8, procAddres.ToInt64());
}
else
{
throw new PlatformNotSupportedException("不支持的處理器類型!");
}
IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, returnType, paramTypes);
// 返回值
IL.Emit(OpCodes.Ret);
moduleBuilder.CreateGlobalFunctions();
// 取得方法信息
MethodInfo methodInfo = moduleBuilder.GetMethod("InvokeFun");
object retObj= methodInfo.Invoke(null, objParams);
// 調用方法,並返回其值
return retObj;
}
catch
{
return null;
}
finally
{
if (libHandle != IntPtr.Zero)
{
//釋放資源
FreeLibrary(libHandle);
}
}
}
}
}
效果
參數帶引用的一直返回的結果不對,還得再學習啊。用C++直接調用更接近,用C#調用經過了P/Invoke後有的時候莫名其名調用效果不對很費勁。