从x64中的C#调用C 代码,所有参数都会偏移一个
calling a c++ code from C# in x64 all arguments shift by one
我的C dll:
int test2::CallMe(int y) {
return y;
}
c#代码:
[DllImport("test2.dll",CharSet = CharSet.Anci)]
private static extern int CallMe(int y);
Console.WriteLine(CallMe(7));
如果DLL和测试程序是在X86中编译的,则我会得到一个打印:7但是,如果我以C 和X64的X64进行编译,或C#的任何CPU,则打印是:0
有任何建议?
编辑:问题是呼叫,因为在调试器中,我看到CPP接收0,或者在struct的情况下为null。
编辑2:使用DEF文件导出功能。如果我使用Extern" C"导出功能,它可以正常工作,但我不能导出CALS的功能,或者我不知道
如何编辑3:显然参数实际上不是零,只有最后一个参数为零,所有参数已移动,第二个参数设置为第一个参数,依此类推,依此类推。
从C#调用C 函数并不完全/透明地支持。您可以尝试在DllImport
中使用CallingConvention = CallingConvention.ThisCall
,但这不是确切的科学。假设它应该适用于简单的情况...
[DllImport("test2.dll", CallingConvention = CallingConvention.ThisCall)]
private static extern int CallMe(IntPtr obj, int y);
其中obj
是对C 对象的引用(C 中的内容称为this
)。您得到32和64位之间的区别发生了,因为将this
指针放置在32和64位之间的位置。
对于非常简单的情况,当实际上this
不使用C 方法(并且不使用虚拟功能)时,您可以将IntPtr.Zero
作为指针传递。通常,您将拥有一个extern C
方法,该方法会创建C 对象" C 侧",然后将IntPtr
(A void*
)返回到C#,然后C#调用CC 方法传递此IntPtr
。
"脆性"点是C 编译器人员操作C 方法的名称(不在extern "C"
块中的方法),因此您必须"手动"发现它们(例如,使用DUMPBIN /exports
)...为了使事情变得"更容易",被弄糊的名称在32至64位之间变化: - )
一个例子:
C - 侧:
class Store
{
private:
int value;
public:
__declspec(dllexport) void Put(int value)
{
this->value = value;
}
__declspec(dllexport) int Get()
{
return this->value;
}
__declspec(dllexport) void Increment()
{
this->value++;
}
};
extern "C"
{
__declspec(dllexport) int PlusOne(int x)
{
return x + 1;
}
__declspec(dllexport) Store* NewStore()
{
return new Store;
}
__declspec(dllexport) void DeleteStore(Store* store)
{
delete store;
}
}
class Program
{
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
extern static int PlusOne(int x);
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
extern static IntPtr NewStore();
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.Cdecl)]
extern static void DeleteStore(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Put@Store@@QAEXH@Z")]
extern static void Put32(IntPtr store, int value);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Get@Store@@QAEHXZ")]
extern static int Get32(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Increment@Store@@QAEXXZ")]
extern static void Increment32(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Put@Store@@QEAAXH@Z")]
extern static void Put64(IntPtr store, int value);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Get@Store@@QEAAHXZ")]
extern static int Get64(IntPtr store);
// EntryPoint generated with DUMPBIN /exports dllname.dll
[DllImport("CplusPlusSide.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint = "?Increment@Store@@QEAAXXZ")]
extern static void Increment64(IntPtr store);
static void Main(string[] args)
{
int x = PlusOne(1);
Console.WriteLine(x);
IntPtr store = NewStore();
int ret;
if (IntPtr.Size == 8)
{
Put64(store, 5);
Increment64(store);
ret = Get64(store);
}
else
{
Put32(store, 5);
Increment32(store);
ret = Get32(store);
}
Console.WriteLine(ret);
DeleteStore(store);
}
}
- 构造函数在退出函数时无法初始化一个参数
- 运算符重载:"operator+"必须采用零个或一个参数
- 如果需要转换,我可以在读取参数的同时将其移动到另一个参数吗?
- 两个函数模板候选项.将一个参数作为引用后,选择不太专业的模板
- 如何设置默认参数以防用户不输入另一个参数
- 使用一个参数的模板函数时出错(适用于 2)
- 仅当一个参数中未使用 std::function 时,模板函数替换才有效
- 如何在两个树通用的函数中创建一个参数?
- 使用带有一个参数函数的递归找到数字的平方
- 专门处理一个参数(C++模板)的两个模板参数
- 模板函数,其中一个参数需要专门化,而另一个不需要
- 如何更改参数包中的最后一个参数
- C++ 如何使用一个参数从派生类构造函数中调用具有两个参数的超类构造函数
- 模板参数重载,最后一个参数为非类型名
- 根据一个参数对数组进行排序
- 是否可以将指针传递给运算符,作为一个参数,例如指向函数的指针
- 用一个参数计数圆形链接列表中的节点
- 将数组作为一个参数从一个函数传递到C 中的另一个函数
- 将另一个参数传递给函子的库
- 分叉:使用一个参数运行 shell 时资源暂时不可用