Исключение нарушения прав доступа в Marshal.StructureToPtr в Windows 7 + .NET 4.0 (Windows XP + .NET 3.5 работает нормально) - PullRequest
4 голосов
/ 26 января 2012

Вот мой код:

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);
}

А вот и структура:

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

Почему это прекрасно работает в Windows XP + .NET 3.5 и вызывает исключения в Windows 7 + .NET 4.0? Может быть, это проблема CharSet?

===================== решаемые =================== ====

Решение и объяснение

Как видите, Marshal.StructureToPtr (ebt, ptrStruct, true ); для третий параметр имеет значение true. Это означает, что система попытается освободить последнюю выделенную память для ptrStruct. Но когда метод Show() вызывается впервые, для этой структуры не было выделено памяти (ptrStruct = IntPtr.Zero). Таким образом, система попытается освободить память, расположенную в нулевом указателе. И, конечно, это вызовет исключение. Windows XP просто игнорирует это, а Windows 7 - нет.

И вот лучшее решение ИМХО:

   Marshal.StructureToPtr(ebt, ptrStruct, false);
   //Working...
   //Free resources
   Marshal.FreeHGlobal(ptrStruct);

1 Ответ

1 голос
/ 29 января 2012

Я не хотел добавлять ответ здесь, так как вы уже решили свою проблему, и то, что я скажу, не даст никакого ответа на вашу проблему, но оно не подходит каккомментарий, как я предоставляю код.Поэтому я обязан опубликовать это здесь в качестве ответа.

Вы, возможно, уже знаете это (и не написали так, поэтому код в вашем вопросе проще), но я просто хотелСказать, что лучший метод, который следует использовать везде при выделении неуправляемой памяти, - это инкапсулировать код в блок try / finally, чтобы гарантировать, что память всегда освобождается , даже еслиисключение:

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();
}
...