英文:
Running C++ dll in C# for calling functions
问题
以下是要翻译的内容:
我有一段 `C++` 代码,我想将其打包成一个 dll 并在 `C#` 代码中使用。我创建了 dll,但当我加载它时,出现了 `System.AccessViolationException` 错误。(首先,dll 无法加载,然后开始加载后出现了访问冲突错误)。
我在 `C++` 中编写了一段代码,我的团队其他成员希望将其纳入到一个 Windows Forms 应用程序中,我认为这是学习关于 `C++` dll 项目并将其纳入到 C#/Windows Forms 的机会。我在 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 导入为窗体应用程序的引用,但出现错误:“无法添加对 project2.dll 的引用。请确保文件可访问并且是有效的程序集或 com 组件”,在搜索一段时间后,我找到了这个[问题](https://stackoverflow.com/questions/8366590/how-to-create-dll-in-c-for-using-in-c-sharp)。然后我在 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);
但现在在调用类的构造函数 (`createCommunicationObject`) 时出现错误 `System.AccessViolationException`(尝试读取或写入受保护的内存。这通常表明其他内存已损坏)。我做错了什么,如何纠正它?提前感谢您。(我使用的是 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();
<details>
<summary>英文:</summary>
I have code in `C++` that I want to package as a dll and use in `C#` code. I made the dll, but when I am loading it, I am getting `System.AccessViolationException`. (First, the dll wouldn't load and after it has started loading, Access Violation has started to appear).
I wrote a piece of code in `C++` that other members of my team want to incorporate into a windows forms application, I saw this as an opportunity to learn about `C++` dll projects and incorporating them into C#/Windows Forms. I followed a tutorial on youtube and came up with this code for my 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();
}
The dll file was built and then I came into C#, I tried importing the dll as a reference in the Forms application but got an error "a reference to project2.dll could not be added. please make sure that the file is accessible and is a valid assembly or com component", after searching for a while, I came across this [question](https://stackoverflow.com/questions/8366590/how-to-create-dll-in-c-for-using-in-c-sharp). I then tried this in the C# application:
[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);
But now I am getting an error `'System.AccessViolationException'(Attempted to read or write protected memory. This is often an indication that other memory is corrupt.)` where I am calling the constructor of the class (`createCommunicationObject`).
What am I doing wrong and how can I rectify it? thanks in advance. (I am using VS2013)
Edit:
After going through the comments and the answers, I tried all that you people suggested, but am still getting the same error. The New `C++` code is:
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;
}
and the new `C#` code is:
[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();
</details>
# 答案1
**得分**: 2
你在这里返回了一个指向堆栈变量的指针:
```c++
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
的包装类。
英文:
You are returning a pointer to a stack variable here:
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);
}
This causes the crash you have, because the stack variable doesn't survive the end of the function. The compiler probably also warns you about it, if you look at your compiler messages.
What you want to do is allocate the object on the heap:
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);
}
And you'd probably also need a destructor method once you don't need the object anymore:
extern "C" _declspec(dllexport) void DestroyCommunicationObject(Communication* object)
{
delete object;
}
That will probably make your use of P/Invoke work.
After you've verified this works, the best practice in C# would be to create a wrapper class that implements IDisposable
correctly.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论