CsWin32: When not targeting specific CPU archetecture the ExceptionPointers member of MINIDUMP_EXCEPTION_INFORMATION can be IntPtr (or nint).

I use AnyCPU and I have it generate MiniDumpWriteDump and use the GetExceptionPointers() function on the runtime to get the exception pointers as an nint (IntPtr) and as such I wanted to avoid the cast entirely (I hate casts from 1 pointer type to another as it requires unsafe).

I even tried doing:

namespace Windows.Win32.System.Diagnostics.Debug
{
    using global::System;
    using Windows.Win32.Foundation;

    /// <summary>Contains the exception information written to the minidump file by the MiniDumpWriteDump function.</summary>
    /// <remarks>
    /// <para><see href = "https://docs.microsoft.com/windows/win32/api//minidumpapiset/ns-minidumpapiset-minidump_exception_information">Learn more about this API from docs.microsoft.com</see>.</para>
    /// </remarks>
    internal struct MINIDUMP_EXCEPTION_INFORMATION : IEquatable<MINIDUMP_EXCEPTION_INFORMATION>
    {
        /// <summary>The identifier of the thread throwing the exception.</summary>
        internal uint ThreadId;

        /// <summary>
        /// <para>A pointer to an <a href = "https://docs.microsoft.com/windows/desktop/api/winnt/ns-winnt-exception_pointers">EXCEPTION_POINTERS</a> structure specifying a computer-independent description of the exception and the processor context at the time of the exception.</para>
        /// <para><see href = "https://docs.microsoft.com/windows/win32/api//minidumpapiset/ns-minidumpapiset-minidump_exception_information#members">Read more on docs.microsoft.com</see>.</para>
        /// </summary>
        internal nint ExceptionPointers;

        /// <summary>Determines where to get the memory regions pointed to by the <b>ExceptionPointers</b> member. Set to <b>TRUE</b> if the memory resides in the process being debugged (the target process of the debugger). Otherwise, set to <b>FALSE</b> if the memory resides in the address space of the calling program (the debugger process). If you are accessing local memory (in the calling process) you should not set this member to <b>TRUE</b>.</summary>
        internal BOOL ClientPointers;

        public bool Equals(MINIDUMP_EXCEPTION_INFORMATION other)
            => throw new NotImplementedException();

        public override bool Equals(object obj)
            => obj is MINIDUMP_EXCEPTION_INFORMATION minidumpExceptionInformation
               && this.Equals(minidumpExceptionInformation);

        public override int GetHashCode()
            => throw new NotImplementedException();
    }
}

But it resulted in it trying to generate that type anyway despite it being present in the project manually, as such it seems the only option is to have a version in the metadata that uses nint or IntPtr for that member instead. This is because it’s safe for me to target AnyCPU as I just get that data directly from the runtime and I do not mess with it further.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 16 (16 by maintainers)

Most upvoted comments

And I still say IntPtr is the real “unsafe.” It’s just as risky but also opts out of type checking.

Generated files usually have a .g.cs extension.

In particular, is the Pack both required for correct 64-bit function and toxic for x86? Or could these two declarations be somehow expressed in harmony so that one “AnyCPU”-compatible declaration would work on any architecture?

@AArnott Pack = 4 is fine for both architectures in this case. It is basically a no-op for x86 since the default Pack is 4 but is required for 64-bit. On 64-bit, without Pack = 4 the ExceptionPointers field will be aligned on an 8 byte boundary and thus an extra 4 bytes inserted between ThreadId and ExceptionPointers. Unfortunately this does make the ExceptionPointers field unnaturally aligned for a 64-bit machine but that is how it is defined in the DbgHelp.h. The definition above should be compatible with AnyCPU.

(I hate casts from 1 pointer type to another as it requires unsafe).

A quick note on the unsafe keyword in .NET Core 3.1 and .NET 5+. Using any API that returns or accepts an IntPtr is inherently “unsafe”. The unsafe keyword is a C# thing and has no impact on the generated code in any way so it really is a matter of preference. I personally prefer specifying the type precisely but there are many companies with constraints on putting AllowUnsafeBlocks in projects so occasionally it is easier to use IntPtr but it is all window dressing at this point in the .NET world.