从x64中的C#调用C ++代码,所有参数都移位一个 [英] calling a c++ code from C# in x64 all arguments shift by one

查看:111
本文介绍了从x64中的C#调用C ++代码,所有参数都移位一个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的c ++ dll:

my 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
,但是如果我在C64和X64的X64或C#的任何CPU上编译它们,则打印结果是:
0

if the dll and the test program are compiled in x86 i get a print: 7 but if i compile them at X64 for c++ and X64 or any CPU for c# the print is: 0

有什么建议吗?

编辑:问题出在调用,因为在调试器中我看到CPP收到0,如果是struct则返回null。

edit: the problem is the call, because in debugger i see that the CPP receives 0 , or null in case of struct.

edit 2:使用def文件导出功能。如果我使用extern C导出函数,则可以正常工作,但是我无法导出calss的函数,或者我不知道如何

edit 2: the functions are exported using a def file. if i export a function using extern "C" it works fine but i cant export a function of a calss, or i dont know how

编辑3:显然,参数是实际上不是零,只有最后一个参数为零,所有参数都已移位,第二个参数设置为第一个,依此类推

edit 3: apparently the arguments are not actually zero, only the last argument is zero, all arguments are shifted, the second param is set to the first one and so on

推荐答案

不完全/完全/透明地支持从C#调用C ++函数。您可以尝试在 DllImport 中使用 CallingConvention = CallingConvention.ThisCall ,但这并不是一门精确的科学。假设它应该适用于简单的情况……

Calling a C++ function from C# isn't exactly/completely/transparently supported. You can try using the CallingConvention = CallingConvention.ThisCall in the DllImport, but it isn't an exact science. Let's say that it should work for simple cases...

[DllImport("test2.dll", CallingConvention = CallingConvention.ThisCall)]
private static extern int CallMe(IntPtr obj, int y);

其中 obj 是对C ++的引用对象(在C ++中称为 this )。之所以会出现32位和64位之间的差异,是因为指针的放置位置在32位和64位之间变化。

where obj is a reference to the C++ object (what in C++ is called this). The difference you get between 32 and 64 bits happens because the place where the this pointer is placed changes between 32 and 64 bits.

对于非常简单的情况,实际上C ++方法不使用 this (并且它不使用虚函数)时,您可以传递 IntPtr.Zero 作为指针。通常,您会有一个 extern C 方法,该方法创建C ++对象 C ++端并返回 IntPtr ( code> void * )传递给C#,然后C#调用C ++方法,并传递此 IntPtr

For very simple cases, when in truth the this isn't used by the C++ method (and it doesn't use virtual functions), you can pass IntPtr.Zero as the pointer. Normally you would have an extern C method that creates the C++ object "C++ side" and returns an IntPtr (a void*) to C#, and then C# calls the C++ methods passing this IntPtr.

脆弱之处在于C ++编译器修改了C ++方法的名称(不在 extern C 块中的方法) ,因此您必须手动发现它们(例如,使用 DUMPBIN / exports )...为了使事情更容易,整齐的名称在32到32之间变化。 64位:-)

The "brittle" point is that the C++ compiler mangles the name of C++ methods (methods that aren't in an extern "C" block), so you have to "manually" discover them (for example using the DUMPBIN /exports)... And to make things "easier", the mangled names change between 32 and 64 bits :-)

示例:

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);
    }
}

这篇关于从x64中的C#调用C ++代码,所有参数都移位一个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆