Программа на C # должна вызывать dll, но похоже, что параметр out совпадает со значением in. Поэтому я сделаю этот короткий пример для проверки.
Вот код C ++ Dll:
.час
#define EtrpDll extern "C" __declspec(dllexport)
EtrpDll void __fastcall WhyWrong(int &m);
.CPP
#include"Header.h"#include<iostream>
void __fastcall WhyWrong(int &m) {
m++;
}
int main() {
int m = 0;
WhyWrong(m);
std::cout << m;
int y;
std::cin >> y;
}
И код C #:
.cs
using System;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Reflection.Emit;
namespace DllTest
{
class Class1 {
[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("Kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("Kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("Kernel32.dll")]
private static extern int GetLastError();
private static IntPtr hModule = IntPtr.Zero;
private static AssemblyName GbsAssemblyName;
private static AssemblyBuilder GbsAssemblyBuilder;
private static ModuleBuilder GbsModuleBuilder;
public enum ModePass {
ByValue = 0x0001,
ByRef = 0x0002
}
private IntPtr GFuncPtr = IntPtr.Zero;
private Type[] GParaType;
private ILGenerator GIL;
private ModePass[] GMdPass;
public object[] GObject;
private string Mname;
private static Type GReturn = typeof(void);static void Main() {
Class1.DllIni();
Class1 G_WhyW = new Class1(new Type[1] { typeof(int) }, new Class1.ModePass[1] { Class1.ModePass.ByRef }, new object[1] { (int)0 });
G_WhyW.LoadFunc("WhyWrong");
Class1.GCreateGlbFunc();
G_WhyW.GObject[0] = (int)1;//Input
G_WhyW.InvokeDllFunc();
Console.WriteLine((int)G_WhyW.GObject[0]);
Console.ReadLine();
Class1.UnLoadDll();
}public static void DllIni(){
hModule = LoadLibrary("DllTest.dll");
if (hModule == IntPtr.Zero) {
Console.WriteLine("DLL Not Loaded");
int e = GetLastError();
Console.WriteLine("Error Code: " + e);
}
GbsAssemblyName = new AssemblyName();
GbsAssemblyName.Name = "GbsCore";
GbsAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(GbsAssemblyName, AssemblyBuilderAccess.Run);
GbsModuleBuilder = GbsAssemblyBuilder.DefineDynamicModule("FuncGrp");
}
public static void GCreateGlbFunc() {
GbsModuleBuilder.CreateGlobalFunctions();
}
public void LoadFunc(string lpProcName) {
GFuncPtr = GetProcAddress(hModule, lpProcName);
if (GFuncPtr == IntPtr.Zero) {
Console.WriteLine("Function " + lpProcName + " Not Loaded");
}
Mname = lpProcName + "_T";
MethodBuilder GbsMethodBuilder = GbsModuleBuilder.DefineGlobalMethod(Mname, MethodAttributes.Public | MethodAttributes.Static, GReturn, GParaType);
GIL = GbsMethodBuilder.GetILGenerator();
if (GObject != null) {
for (int i = 0; i < GObject.Length; i++) {
switch (GMdPass[i]) {
case ModePass.ByValue:
GIL.Emit(OpCodes.Ldarg, i);
break;
case ModePass.ByRef:
GIL.Emit(OpCodes.Ldarga, i);
break;
default:
Console.WriteLine("Pass Mode Error");
break;
}
}
}
if (IntPtr.Size == 4) { GIL.Emit(OpCodes.Ldc_I4, GFuncPtr.ToInt32()); } //Platform
else if (IntPtr.Size == 8) { GIL.Emit(OpCodes.Ldc_I8, GFuncPtr.ToInt64()); }
else { throw new PlatformNotSupportedException(); }
GIL.EmitCalli(OpCodes.Calli, CallingConvention.FastCall, GReturn, GParaType);
GIL.Emit(OpCodes.Ret);
}
public void InvokeDllFunc() {
MethodInfo GbsMethodInfo;
if (GParaType == null) {
GbsMethodInfo = GbsModuleBuilder.GetMethod(Mname);
}
else {
GbsMethodInfo = GbsModuleBuilder.GetMethod(Mname, GParaType);
}
GbsMethodInfo.Invoke(null, GObject);//return void
}
public static void UnLoadDll() {
FreeLibrary(hModule);
hModule = IntPtr.Zero;
}
public Class1(Type[] T, ModePass[] MP, object[] OB) {
GParaType = T;
GMdPass = MP;
GObject = OB;
}
}
}
Перед вызовом функции WhyWrong значение параметра равно 1, однако результат, который я получаю при попытке, тоже равен 1. Должно быть 2, не так ли?
Эта сигнатура динамического метода void(int)
не void(ref int)
как вы намерены, так как он инициализируется с new Type[]{typeof(int)}
,
Использование отражения для вызова методов со ссылочными параметрами поддерживается (и изменяет массив аргументов), поэтому избавьтесь от ModePass перечислить в целом и использовать typeof(int).MakeByRefType()
при определении метода.
Целый switch (GMdPass[i])
не нужно Поскольку аргумент является ссылкой, он будет просто предоставлен вызываемой функции как сама ссылка (ldarg
достаточно).
Кстати, почему бы вам просто не использовать Marshal.GetDelegateForFunctionPointer? Он почти так же, как динамический метод, но должен быть быстрее. Вам нужно только определить тип делегата (не универсальный).
Кроме того, не азЬсаИ не поддерживается?
Других решений пока нет …