Running C++ dll in C# for calling functions

155 views Asked by At

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. 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();
1

There are 1 answers

2
chris_se On

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.