Search code examples
c#c++pinvokemarshalling

C# P/Invoke AccessViolationException when attepting to use string parameters


I'm trying to wrap a C++ dll for which I only have the header file for. The function I am trying to get working at the moment is giving me an AccessViolationException:

"Attempted to read or write protected memory. 
This is often an indication that other memory is corrupt."

The C++ function prototype is:

RunSimulation( LPCSTR, LPSTR);

Meanwhile, my C# wrapper is:

[DllImport("thedll.dll", EntryPoint = "RunSimulation")]
public static extern uint RunSimulation(string simID, ref string outputID);

I suspect the problem is with the C# function, specifically with how the strings are implemented. Since I'm relatively new to platform invocation and such, I'm not sure how to proceed.

Should the string parameters be pointers to where the strings are? Or is there something else wrong with the wrapper maybe?

Edit:

This is how the function is called on the managed end of things:

string outputID = "";

try
{
    RunSimulation(id, ref outputID);
}
catch (Exception e)
{
    Logging.Log_Warning.SendException("Threw an exception", e);
}

Edit 2:

Upon changing the second parameter to a StringBuilder, the same exception occurs. The only difference is that the exception break doesn't stop at the line where the function is called, Visual Studio opens a new "Break" tab saying the exception happened. The function documentation recommends setting aside 16 bytes or more, so I used the capacity constructor with a values of 1024 and 4096 to test.

Edit 3:

After doing a clean and rebuild, the problem presented itself as a driver error. Since this shows that the API is functioning, the solution was indeed to change my ref string parameter to a StringBuilder as suggested in the comments.


Solution

  • The solution to my problem ended up being to use StringBuilder instead of String to make sure the space in memory was allocated ahead of time. So my signature ended up looking like:

    [DllImport("thedll.dll", EntryPoint = "RunSimulation")]
    public static extern uint RunSimulation(string simID, StringBuilder outputID);
    

    And to use it:

    string id = "someID";
    int requiredSize = 512;
    StringBuilder outputID = new StringBuilder(requiredSize);
    
    RunSimulation(id, outputID);
    

    Hope that helps!