1

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();
  • You have declared your c++ methods `_declspec` but in `C#` you have set `CallingConvention = CallingConvention.StdCall` Try using `CallingConvention=CallingConvention.Cdecl` instead. or change both to use Stdcall. – user20716902 Jul 10 '23 at 10:32
  • returning `char*` from C/C++ is not a good idea ... how you will free this? – Selvin Jul 10 '23 at 10:48
  • what is `st_rtioin` ? if its `struct` then C/C++ side is expecting to get the pointer ... and you are passing this as value on C# side – Selvin Jul 10 '23 at 10:50
  • @user20716902 `_declspec` is not a calling convention ... but you are right ... by default MVC++ use `cdecl` calling convention – Selvin Jul 10 '23 at 10:53
  • 2
    And just to get this off the table: does this _have to_ be in C++ or does it just happen to be? Would it be much of a hassle to port it to managed C#? I know that's a completely different path. I am trying to be pragmatic, here. – Fildor Jul 10 '23 at 10:54
  • Passing around C++ classes to C# is pretty difficult to do correctly. It's much easier to just pass more basic variables back and forwards. – Charlieface Jul 10 '23 at 11:54
  • comm = new Communication::Communication(ip, p_out, p_in); return comm; – Hans Passant Jul 10 '23 at 12:11
  • @user20716902, I changed to cdecl but it didn't work. – Flight Dynamic Model Jul 12 '23 at 04:46
  • @Selvin, I modified it to return a string but still no good. st_rtioin is a struct, I saw my mistake and changed it to receive the value, it still didn't work. I also changed my code so that I don't have to move classes around, would be sharing the updated code in an edit. Still the same error. – Flight Dynamic Model Jul 12 '23 at 04:49

1 Answers1

2

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.

chris_se
  • 1,006
  • 1
  • 7
  • Note that this error does not relate to this being a dll in any way, it would occur just the same in single exe. – nick Jul 10 '23 at 11:37
  • I tried this as well as removing the use of classes completely in my code, I am just passing parameters now and the c++ part is doing the work within but still the same error, am sharing the code as an edit. – Flight Dynamic Model Jul 12 '23 at 04:51