Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access violation exception at 'Marshal.StructureToPtr' in Windows 7 + .NET 4.0 (Windows XP + .NET 3.5 works fine)

Here is my code:

internal void Show()
{
    if (Parent == null)
        throw new NullReferenceException();
    EDITBALLOONTIP ebt = new EDITBALLOONTIP();
    ebt.cbStruct = Marshal.SizeOf(ebt);
    ebt.pszText = Text;
    ebt.pszTitle = Caption;
    ebt.ttiIcon = (int)Icon;
    IntPtr ptrStruct = Marshal.AllocHGlobal(Marshal.SizeOf(ebt));
    Marshal.StructureToPtr(ebt, ptrStruct, true); // Here we go.
    // Access violation exception in Windows 7 + .NET 4.0
    // In Windows XP + .NET 3.5, it works just fine.

    // ... Some other code ...

    Marshal.FreeHGlobal(ptrStruct);
}

And here is the structure:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct EDITBALLOONTIP
{
    internal int cbStruct;
    internal string pszTitle;
    internal string pszText;
    internal int ttiIcon;
}

Why does this work fine in Windows XP + .NET 3.5 and raises exceptions in Windows 7 + .NET 4.0? May be it's CharSet trouble?

=====================Solved=======================

Solution and explanation

As you can see Marshal.StructureToPtr(ebt, ptrStruct, true); has third parameter set to true. It means that the system will try to free the last allocated memory for ptrStruct. But when method Show() is invoked for the first time, there was not allocated memory for that structure (ptrStruct = IntPtr.Zero). So system will try to free memory located at zero pointer. And of course it will raise an exception. Windows XP just ignores this, but Windows 7 doesn't.

And here is best solution IMHO:

   Marshal.StructureToPtr(ebt, ptrStruct, false);
   //Working...
   //Free resources
   Marshal.FreeHGlobal(ptrStruct);
like image 724
Coffka Avatar asked Nov 05 '22 07:11

Coffka


1 Answers

I didn't want to add an answer here as you already have solved your problem and what I'll say won't provide any answer to the problem you had, but it wouldn't fit well as a comment as I provide some code. So I'm obliged to post it here as an answer.

You may already know it (and didn't wrote it that way so the code in your question is simpler), but I just wanted to say that a best practice that should be used everywhere when unmanaged memory is allocated, is to encapsulate the code in a try/finally block to ensure the memory is always freed, even if an exception is thrown:

private static void Test()
{
    IntPtr ptrStruct = IntPtr.Zero;

    try
    {
        Marshal.AllocHGlobal(0x100);

        // Some code here
        // At some point, an exception is thrown
        throw new IndexOutOfRangeException();
    }
    finally
    {
        // Even if the exception is thrown and catch
        // from the code that calls the Test() method,
        // the memory will be freed.
        if (ptrStruct != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(ptrStruct);
        }
    }
}

try
{
    Test();
}
catch (IndexOutOfRangeException)
{
    // Catches the exception, so the program won't crash
    // but it'll exit the Test() method, so the try/finally
    // ensures it won't leave without freeing the memory
    Debugger.Break();
}
like image 170
ken2k Avatar answered Nov 09 '22 04:11

ken2k