c++ stdcall 方式与 C# 传递 struct 数据类型?

发布时间 2023-04-10 15:04:07作者: 空明流光
using namespace std;
#include <iostream>
#include <thread>
#include <mutex>

struct MyStruct {
    char* myString;
    int length;
    // other members
};

#ifdef __cplusplus
extern "C" {
#endif

    __declspec(dllexport) int __stdcall MyFunction(MyStruct* myStruct);
    __declspec(dllexport) void __stdcall ClearMyFunction(int handle);

#ifdef __cplusplus
}
#endif

static map <int, intptr_t> pointerList;
static std::mutex pointerListLocker;
static int pointerHandleIndex = 0;

int addToPointerList(intptr_t buffer)
{
    pointerListLocker.lock();

    pointerHandleIndex++;
    pointerList.insert(pair<int, intptr_t>(pointerHandleIndex, buffer));

    int ret = pointerHandleIndex;

    pointerListLocker.unlock();

    return ret;
}

intptr_t getFromPointerList(int handle) 
{
    pointerListLocker.lock();

    intptr_t ret = pointerList[handle];

    pointerListLocker.unlock();

    return ret;
}

void removeFromPointerList(int handle)
{
    pointerListLocker.lock();

    map <int, intptr_t>::iterator finder = pointerList.find(handle);
    if (finder != pointerList.end())
        pointerList.erase(handle);

    pointerListLocker.unlock();
}

__declspec(dllexport) int __stdcall MyFunction(MyStruct* myStruct)
{
    char* buffer = new char[myStruct->length + 1];
    strncpy(buffer, myStruct->myString, myStruct->length);
    buffer[myStruct->length] = '\0';

    cout << "c++ from c#:" << buffer << endl;

    delete[] buffer;

    char* buffer2 = new char[] {"Nice to meet you! 幸会"};
    myStruct->myString = buffer2;
    myStruct->length = strlen(buffer2);

    //buffer2使用new声明,需要释放,缓存到pointerList,可调用ClearMyFunction释放
    int handle = addToPointerList(reinterpret_cast<intptr_t>(buffer2));
    return handle;
}

__declspec(dllexport) void __stdcall ClearMyFunction(int handle)
{
    intptr_t pointer = getFromPointerList(handle);
    char* buffer2 = reinterpret_cast<char*>(pointer);
    delete buffer2;
    removeFromPointerList(handle);
}

// 定义 DllMain 函数,用于在 DLL 加载和卸载时执行初始化和清理工作
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        cout << "DLL_PROCESS_ATTACH" << endl;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace ConsoleApp1
{
    internal class Program
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct MyStruct
        {
            public IntPtr myString;
            public int length;
        }

        [DllImport(@"windows\x64\Debug\OpenCVDemo.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern int MyFunction(ref MyStruct myStruct);

        [DllImport(@"windows\x64\Debug\OpenCVDemo.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern void ClearMyFunction(int handle);

        static void Main(string[] args)
        {
            string inputStr = "Hello, World! 你好!";

            MyStruct myStruct = new MyStruct();
            myStruct.length = inputStr.Length;
            myStruct.myString = Marshal.StringToHGlobalAnsi(inputStr);
            IntPtr pointer = myStruct.myString;

            int handle = MyFunction(ref myStruct);

            string outputStr = Marshal.PtrToStringAnsi(myStruct.myString, myStruct.length);

            Console.WriteLine("Input string: " + inputStr);
            Console.WriteLine("Output string: " + outputStr);

            Marshal.FreeHGlobal(pointer);

            ClearMyFunction(handle);

            Console.ReadLine();
        }
    }
}