Search code examples
c#exceldllimportstringbuilderaccess-violation

Getting AccessViolationException when passing a fixed length string from C# to a dll


I have the following (VB6?) code working perfectly in VBA for Excel. The dll that I am using (x.dll) is a "black box" to me. I don't know what it is written in, whether it is unmanaged or not. I know very little about it from an historical technical point of view. I only know that this particular function works when called from Excel VBA and I cannot similarly get it to function when I call it from C# and I think I should be able to. Again the value of c.b128 is used by and changed by the dll.

'In VBA for excel the value inside "c.b128" is changed from
'" 1234567890123456789012345678901234567890123456789012345678901234567890123456789                                                 "
'to
'" 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 123456789                                          "

VBA for Excel Code

Private Type k128
    b128 As String * 128
End Type

Private Declare Function dllSpace Lib "x.dll" (aInfo As Long, _
   bVec As Long, cQry As k128, dErr As k128) As Long    

Function Space() As Long

    Dim c  As k128
    Dim d  As k128

    c.b128 = Left(" 1234567890123456789012345678901234567890123456789012345678901234567890123456789", 128)
    d.b128 = Left("", 128)


    Space = dllSpace(-1, -1, c, d)

 End Function

I tried to implement the same in .NET and got an error when it gets to "return dllSpace(-1, -1, c, d);" An unhandled exception of type system.accessviolationexception occured in [...] attempted to read or write protected memory. other memory is corrupt

I need to convert this to .NET and I get an AccessViolationException. Everywhere I read that the memory reserved by StringBuilders are accessible to dlls in C#. I have tried “ref StringBuilder” I have tried using byte[], I have tried using the unsafe descriptor, I don’t understand. Also if there is a way for me to see more of what is going on in memory using the IDE that would also be helpful to me. I can see all my variables in the local and watch windows, but I can’t see and don’t know how to see more details about the exception that is thrown. I am using Visual Studio Express 2013 for Windows Desktop on a Win7 32 bit OS machine.

This is a snippet from my c# code


C# Code Snippet

[DllImport(@"x.dll")]
private static extern int dllSpace(int aInfo,
                  int bVec,
                  StringBuilder cQry,
                  StringBuilder dErr);


public int StartTheDataSpace()
{


    StringBuilder c = new StringBuilder(128);

    StringBuilder d = new StringBuilder(128);

    c.Append(" 1234567890123456789012345678901234567890123456789012345678901234567890123456789                                                 ");

    return dllSpace(-1, -1, c, d);
}


Solution

  • The VBA code makes it clear that what you have is actually a structure containing a fixed length string. That means that StringBuilder is the wrong type. You should instead use:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct k128
    {
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string b128;
    }
    

    Also, as I understand it, VBA uses ByRef as default. So the C# declaration should be:

    [DllImport(@"x.dll")]
    private static extern int dllSpace(
        ref int aInfo,
        ref int bVec,
        ref k128 cQry,
        ref k128 dErr
    );
    

    Now, it so happens that ref k128 has the same memory layout when marshalled as StringBuilder with capacity 128 so you may find it more convenient to switch back to:

    [DllImport(@"x.dll")]
    private static extern int dllSpace(
        ref int aInfo,
        ref int bVec,
        StringBuilder cQry,
        StringBuilder dErr
    );