AccessViolationException when sharing a pointer between C# and C++ DLLimport problem.

Hi all,

I am facing an AccessViolationException and I can't seem to pinpoint where have I gone wrong. I hope the experts here are able to provide me with an answer.

Scenario:

A C# WinForms (.net 2.0) program that creates a background/worker thread to call a C++ function via Interop (static extern method). The C++ function will internally do a loop, that will "break" out of the loop once a flag in the C# program is triggered.

Now, issue is that it works for a few times before the AccessViolationException kicks in. The VS debugger points to the C++ function call as the culprit.

C++ Function:

_declspec(dllexport) bool StartCall( unsigned int *position, unsigned int *maxLength,
unsigned int *flag, char* fileName )
{
while( (*position) < (*maxLength) && (*flag)==1 )
{
//do processing, e.g. play sound, calculations

(*position)++;
}
return true;

}

C# Function:

[DllImport( "MYCPP.dll" , ExactSpelling = true , CallingConvention = CallingConvention.Cdecl )]private static extern bool StartCall( IntPtr ptrPosition , IntPtr ptrMaxLength, IntPtr ptrFlag,
[In] string fileName );

private const int FLAG_ON = 1;
private const int FLAG_STOP = 2;
private const int FLAG_NEXT = 3;


private Thread tThread = null;
private int iCurrentIndex = 0;
private string[] arrFiles = new string[] { "file1.txt", "file2.txt", "file3.txt", "file4.txt" };

private IntPtr ptrPosition = IntPtr.Zero;
private IntPtr ptrMaxLength = IntPtr.Zero;
private IntPtr ptrFlag = IntPtr.Zero;

private void Form_Load( object sender , EventArgs e )
{
}

private void btnStop_Click( object sender , EventArgs e )
{
Marshal.WriteInt32( this.ptrFlag , FLAG_STOP );
}

private void btnStart_Click( object sender , EventArgs e )
{
btnStart.Enabled = false;
tThread = new Thread( new ThreadStart( MyThread_Run ) );
tThread.IsBackground = true;
tThread.Start();

}

private void btnNext_Click( object sender , EventArgs e )
{
Marshal.WriteInt32( this.ptrFlag , FLAG_NEXT );
}

private void MyThread_Run()
{
GCHandle hGCPosition = GCHandle.Alloc( 0 , GCHandleType.Pinned );
GCHandle hGCMaxLength = GCHandle.Alloc( 100 , GCHandleType.Pinned );
GCHandle hGCFlag = GCHandle.Alloc( FLAG_ON , GCHandleType.Pinned );

this.ptrPosition = hGCPosition.AddrOfPinnedObject();
this.ptrMaxLength = hGCMaxLength.AddrOfPinnedObject();
this.ptrFlag = hGCFlag.AddrOfPinnedObject();

while ( Marshal.ReadInt32( ptrFlag ) != FLAG_STOP )
{

StartCall( ptrPosition, ptrMaxLength, ptrFlag, arrFiles[iCurrentIndex] );

if ( Marshal.ReadInt32( ptrFlag ) == FLAG_NEXT )
{
iCurrentIndex++;
if ( iCurrentIndex >= arrFiles.Length )
iCurrentIndex = 0;

Marshal.WriteInt32( ptrPosition, 0 );
Marshal.WriteInt32( this.ptrFlag , FLAG_ON );
}

}

ptrPosition = IntPtr.Zero;
ptrMaxLength = IntPtr.Zero;
ptrFlag = IntPtr.Zero;

hGCPosition.Free();
hGCMaxLength.Free();
hGCFlag.Free();

}

I am making use of a Pointer (integer value) to act as a flag to alert the C++ function to stop. Apparently after a random number of times of clicking Next, it will throw the exception.

Anyone

knave



Answer this question

AccessViolationException when sharing a pointer between C# and C++ DLLimport problem.

  • Jason Beck

    On a side note, when I enabled Unmanaged Debugging mode, at every startup of the program, VS2005 will throw up an alert message on some Marshalling problem. (something like "Problem found in Marshalling XXX from UInt32 to UInt32" kind of messages).

    Not sure if it may be the culprit, but it throws the same message for every marshalling parameter.

    Knave


  • mcmcom

    The problem is that it's not very clear where it crashes. I really doubt that it crashes in the function call itself, there are only "normal" arguments, there is no special marshaling to be done that could crash. So it probably crash inside the function. Look in the stack frame, sometimes the debugger does not show the current function in the stack frame but its caller.
  • Malay Roy

    nobugz:

    According to the docs (for GCHandleType) this should not be a problem:

    Pinned

    This handle type is similar to Normal

    Normal This handle type represents an opaque handle, meaning you cannot resolve the address of the pinned object through the handle. You can use this type to track an object and prevent its collection by the garbage collector. This enumeration member is useful when an unmanaged client holds the only reference, which is undetectable from the garbage collector, to a managed object.

  • michaelleewebb

    Your GCHandle.Alloc() calls are not kosher. You are passing it a constant instead of a reference to an object. That forces the CLR to box the constant. You pin that boxed object but that doesn't prevent it from being garbage collected. Once the collector makes a pass, you're very like to get an AccessViolation.

    Try something like this instead:
    object flag = new int();
    (int)flag = FLAG_ON;
    GCHandle hGCFlag = GCHandle.Alloc(flag, GCHandleType.Pinned );
    this.ptrFlag = hGCFlag.AddrOfPinnedObject();

    Same pattern for the other two arguments.


  • Jb4e

    Sad to say, its extremely hard to crash in debug mode shielded by vshost process.

    The release build crashes much faster and easier. Perhaps it is the method that I using that is causing such problems Or does any one have a better solution to my targeted output instead of using a pointer like this

    knave.


  • zille

    One possible problem is that you click Next while that thread is not running. When the thread is not running ptrFlag points to random memory and if you write to it anything can happen.

    If this is not problem it would be useful to tell us where in the C++ function the debugger shows that the exception is thrown (if possible).


  • Regmo

    Nope. that is not the issue.

    The debugger only stops at StartCall(). I can't dive in.

    while ( Marshal.ReadInt32( ptrFlag ) != FLAG_STOP )
    {

    --> StartCall( ptrPosition, ptrMaxLength, ptrFlag, arrFiles[iCurrentIndex] );

    knave


  • gumtoo

    Hi Nobugz,

    I had did something similar to your suggestion before (minus the boxing to an object part) as I had suspected the same thing as you have, and it still didn't work. :( However, I will give the boxing idea a try.

    Knave.


  • Ian Jorgensen

    Hmm... you should be able to dive in StartCall. I suppose you copied the mycpp.dll file to the same dir where the C# exe is. Did you also copied the pdb file


  • A kid

    I tried setting "SetLastError=true" at the declaration portion and the relevent Win32 error message was "operation saved successfully".

    If I do a Try/Catch statement over the StartCall(..) interop call, it will prevent the crashing, but it causes memory lockage (as the C++ function is using some File IO before it throws the exception). Anyone has any idea how to release the lock + the memory

    Knave.


  • Pradeep Gupta

    nobugz is correct. GCHandles are not meant to be used with constants.

    I have a blog post that describes this issue here:
    http://blogs.msdn.com/clyon/archive/2004/09/17/230985.aspx

    From the article:


    the int is] boxed into a newly heap-allocated Object. So the new GCHandle obediently pins this new Object in memory. Then when you pass gch2.AddrOfPinnedObject to your unmanaged code… trouble.

    Consider an unmanaged method takes an array of ints, and increments each element. You’ve passed it an address, and it dutifully increments each value it finds for the length of the array. Congratulations, you’ve just corrupted memory.

    If you’re lucky, this crashes the runtime right away. If you’re unlucky, your app may continue to run and crash sometime in the future or your app’s data may be messed up.

    Hope that helps

    -Chris


  • AccessViolationException when sharing a pointer between C# and C++ DLLimport problem.