我在
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();
您将在此处返回指向堆栈变量的指针:
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
的包装类。