在C#中运行C++ dll来调用函数

问题描述 投票:0回答:1

我在

C++
中有代码,我想将其打包为 dll 并在
C#
代码中使用。我制作了 dll,但是当我加载它时,我得到了
System.AccessViolationException
。 (首先,dll 无法加载,并且在开始加载后,访问冲突开始出现)。

我在

C++
中编写了一段代码,我团队的其他成员希望将其合并到 Windows 窗体应用程序中,我认为这是一个了解
C++
dll 项目并将其合并到 C#/Windows 窗体中的机会。我按照 youtube 上的教程,为我的 dll 编写了这段代码:


extern "C" _declspec(dllexport) Communication* CreateCommunicationObject(const char* ip, unsigned int p_out, unsigned int p_in)
{
    return &Communication::Communication(ip, p_out, p_in);
}

extern "C" _declspec(dllexport) st_rtioout getReceivedData(Communication *comm)
{
    return comm->getData();
}

extern "C" _declspec(dllexport) void sendNewData(Communication *comm, st_rtioin *data)
{
    return comm->sendData(*data);
}

extern "C" _declspec(dllexport) char* getReceptionStatus(Communication *comm)
{
    return comm->getReceptionStatus();
}

extern "C" _declspec(dllexport) char* getSendingStatus(Communication *comm)
{
    return comm->getSendingStatus();
}

dll 文件已构建,然后我进入 C#,我尝试将 dll 导入为 Forms 应用程序中的引用,但收到错误“无法添加对 project2.dll 的引用。请确保该文件可访问并且是一个有效的程序集或 com 组件”,搜索了一段时间后,我遇到了这个问题。然后我在 C# 应用程序中尝试了这个:


        [DllImport("Project2.dll", EntryPoint = "CreateCommunicationObject", CallingConvention = CallingConvention.StdCall)]
        static extern IntPtr CreateCommunicationObject(string ip, int port_out, int port_in);

        [DllImport("Project2.dll", EntryPoint = "getReceivedData", CallingConvention = CallingConvention.StdCall)]
        static extern st_rtioout getReceivedData(IntPtr ptr);

        [DllImport("Project2.dll", EntryPoint = "sendNewData", CallingConvention = CallingConvention.StdCall)]
        static extern void sendNewData(IntPtr ptr, st_rtioin sendingdata);

        [DllImport("Project2.dll", EntryPoint = "getReceptionStatus", CallingConvention = CallingConvention.StdCall)]
        static extern string getReceptionStatus(IntPtr ptr);

        [DllImport("Project2.dll", EntryPoint = "getSendingStatus", CallingConvention = CallingConvention.StdCall)]
        static extern string getSendingStatus(IntPtr ptr);

但现在我在调用类的构造函数时遇到错误

'System.AccessViolationException'(Attempted to read or write protected memory. This is often an indication that other memory is corrupt.)
createCommunicationObject
)。

我做错了什么以及如何纠正?提前致谢。 (我使用的是VS2013)

编辑: 在查看了评论和答案之后,我尝试了你们建议的所有内容,但仍然遇到相同的错误。新的

C++
代码是:

extern "C" _declspec(dllexport) void Initialize(std::string ip, unsigned int p_out, unsigned int p_in)
{
    const char* ipaddress = ip.c_str();
    Communication comms = Communication(ipaddress, p_out, p_in);
    Comms = &comms;
}

extern "C" _declspec(dllexport) st_rtioout getReceivedData()
{
    return Comms->getData();
}

extern "C" _declspec(dllexport) void sendNewData(st_rtioin data)
{
    Comms->sendData(data);
}

extern "C" _declspec(dllexport) std::string getReceptionStatus()
{
    std::string str = Comms->getReceptionStatus();
    return str;
}

extern "C" _declspec(dllexport) std::string getSendingStatus()
{
    std::string str = Comms->getSendingStatus();
    return str;
}

新的

C#
代码是:

[DllImport("Project2.dll", EntryPoint = "Initialize", CallingConvention = CallingConvention.Cdecl)]
    static extern void Initialize(string ip, int port_out, int port_in);

    [DllImport("Project2.dll", EntryPoint = "getReceivedData", CallingConvention = CallingConvention.Cdecl)]
    static extern st_rtioout getReceivedData();

    [DllImport("Project2.dll", EntryPoint = "sendNewData", CallingConvention = CallingConvention.Cdecl)]
    static extern void sendNewData(st_rtioin sendingdata);

    [DllImport("Project2.dll", EntryPoint = "getReceptionStatus", CallingConvention = CallingConvention.Cdecl)]
    static extern string getReceptionStatus();

    [DllImport("Project2.dll", EntryPoint = "getSendingStatus", CallingConvention = CallingConvention.Cdecl)]
    static extern string getSendingStatus();
c# c++ dll visual-studio-2013
1个回答
2
投票

您将在此处返回指向堆栈变量的指针:

extern "C" _declspec(dllexport) Communication* CreateCommunicationObject(const char* ip, unsigned int p_out, unsigned int p_in)
{
    return &Communication::Communication(ip, p_out, p_in);
}

这会导致崩溃,因为堆栈变量在函数结束后无法继续存在。如果您查看编译器消息,编译器可能还会向您发出警告。

您想要做的是在堆上分配对象:

extern "C" _declspec(dllexport) Communication* CreateCommunicationObject(const char* ip, unsigned int p_out, unsigned int p_in)
{
    return new Communication::Communication(ip, p_out, p_in);
}

一旦您不再需要该对象,您可能还需要一个析构函数方法:

extern "C" _declspec(dllexport) void DestroyCommunicationObject(Communication* object)
{
    delete object;
}

这可能会让您使用 P/Invoke 工作。

验证此方法有效后,C# 中的最佳实践是创建一个正确实现

IDisposable
的包装类。

© www.soinside.com 2019 - 2024. All rights reserved.