calling a c++ code from C# in x64 all arguments shift by one

浪尽此生 提交于 2020-01-02 08:12:07

问题


my c++ dll:

int test2::CallMe(int y) {
return y;
}

c# code:

[DllImport("test2.dll",CharSet = CharSet.Anci)]
private static extern int CallMe(int y);

Console.WriteLine(CallMe(7));

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

Any suggestion?

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

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

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


回答1:


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

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.

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.

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 :-)

An example:

C++-side:

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


来源:https://stackoverflow.com/questions/42483796/calling-a-c-code-from-c-sharp-in-x64-all-arguments-shift-by-one

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!