Question

I'm trying to use P/Invoke to populate a POD struct. The POD struct in C++ looks like this:

struct GraphicsAdapterDesc {
    const wchar_t* AdapterName;
    int32_t AdapterIndex;
    const wchar_t* HardwareHash;

    int64_t DedicatedVMEM;
    int64_t DedicatedSMEM;
    int64_t SharedSMEM;

    int32_t NumOutputs;
};

I've tried to be careful with explicitly specifying the widths of all my fields. The 'mirror' struct, in C#, is defined as follows:

[StructLayout(LayoutKind.Sequential)]
public struct GraphicsAdapterDesc {
    public WCharStringPtr AdapterName;
    public int AdapterIndex;
    public WCharStringPtr HardwareHash;

    public long DedicatedVMEM;
    public long DedicatedSMEM;
    public long SharedSMEM;

    public int NumOutputs;
};

Where WCharStringPtr looks like this:

public struct WCharStringPtr {
    internal IntPtr charArrayPtr;

    private string asString;
    public string AsString {
        get {
            return asString ?? (asString = Marshal.PtrToStringUni(charArrayPtr));
        }
    }

    public static implicit operator string(WCharStringPtr operand) {
        return operand.AsString;
    }

    public override string ToString() {
        return AsString;
    }
}

I have a method defined as such in C++:

extern "C" __declspec(dllexport) bool GetGraphicsAdapter(int32_t adapterIndex, GraphicsAdapterDesc& outAdapterDesc) {
        outAdapterDesc = RENDER_COMPONENT.GetGraphicsAdapter(adapterIndex);
    return true;
}

And the P/Invoke extern method declaration is as follows:

[DllImport(InteropUtils.RUNTIME_DLL, EntryPoint = "GetGraphicsAdapter", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool _GetGraphicsAdapter(int adapterIndex, out GraphicsAdapterDesc adapterDesc);

Whenever I call _GetGraphicsAdapter, I get an access violation error (not an AccessViolationException). When I break the program from within the extern C++ method, everything appears to be well-formed; but as soon as I return from that method, the access violation occurs. So, I'm guessing that there's some memory being cleaned up as soon as the method exists, but I can't see what, or why.

Then again, I'm new to P/Invoke, and maybe it's something to do with my handling of strings; I'm not exactly sure if what I'm doing is correct.

Thank you in advance.

Was it helpful?

Solution

Your C# struct WCharStringPtr contains two data members. These are both marshalled as pointers. On the other hand the C++ fields you map this to are of type wchar_t*, which is just a single pointer. So that's a clear mismatch.

What is not clear is how the memory allocation for these strings is meant to be handled. Does the caller allocate the memory, or does the callee?

Let is suppose that the callee allocates the memory. In that case the GraphicsAdapterDesc struct should be declared like this:

public struct GraphicsAdapterDesc {
    public IntPtr AdapterName;
    public int AdapterIndex;
    public IntPtr HardwareHash;

    public long DedicatedVMEM;
    public long DedicatedSMEM;
    public long SharedSMEM;

    public int NumOutputs;
};

You call Marshal.PtrToStringUni() to convert AdapterName and HardwareHash to strings once the function returns. As for deallocation, that's beyond the scope of the question.

This seems the most plausible option. It seems rather clear that the callee cannot be expected to allocate the memory for the two strings since the length of the buffers is not passed in the interface. And of course the strings are declared const wchar_t* which is further evidence.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top