C# 自定义结构的PInvoke访问冲突异常

C# 自定义结构的PInvoke访问冲突异常,c#,c++,pinvoke,access-violation,C#,C++,Pinvoke,Access Violation,我正在尝试使用p/Invoke来填充一个POD结构。C++中的POD结构看起来像这样: struct GraphicsAdapterDesc { const wchar_t* AdapterName; int32_t AdapterIndex; const wchar_t* HardwareHash; int64_t DedicatedVMEM; int64_t DedicatedSMEM; int64_t SharedSMEM; in

我正在尝试使用p/Invoke来填充一个POD结构。C++中的POD结构看起来像这样:

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;
};
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;
    }
}
public struct GraphicsAdapterDesc {
    public IntPtr AdapterName;
    public int AdapterIndex;
    public IntPtr HardwareHash;

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

    public int NumOutputs;
};
我已经尝试过在明确指定所有字段的宽度时要小心。 C#中的“镜像”结构定义如下:

[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;
};
[DllImport(InteropUtils.RUNTIME_DLL, EntryPoint = "GetGraphicsAdapter", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool _GetGraphicsAdapter(int adapterIndex, out GraphicsAdapterDesc adapterDesc);
其中,
WCharStringPtr
如下所示:

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;
};
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;
    }
}
public struct GraphicsAdapterDesc {
    public IntPtr AdapterName;
    public int AdapterIndex;
    public IntPtr HardwareHash;

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

    public int NumOutputs;
};

我在C++中定义了这样一个方法:

extern "C" __declspec(dllexport) bool GetGraphicsAdapter(int32_t adapterIndex, GraphicsAdapterDesc& outAdapterDesc) {
        outAdapterDesc = RENDER_COMPONENT.GetGraphicsAdapter(adapterIndex);
    return true;
}
p/Invoke extern方法声明如下:

[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;
};
[DllImport(InteropUtils.RUNTIME_DLL, EntryPoint = "GetGraphicsAdapter", CallingConvention = CallingConvention.Cdecl)]
internal static extern bool _GetGraphicsAdapter(int adapterIndex, out GraphicsAdapterDesc adapterDesc);

无论何时调用
\u GetGraphicsAdapter
,我都会收到一个访问冲突错误(不是AccessViolationException)。当我从外部C++程序中中断程序时,一切看起来都很好;但一旦我从该方法返回,访问冲突就会发生。所以,我猜一旦该方法存在,就会有一些内存被清除,但我不知道是什么,或者为什么

再说一次,我对p/Invoke还不熟悉,也许这与我对字符串的处理有关;我不确定我所做的是否正确

提前感谢。

您的C#struct
WCharStringPtr
包含两个数据成员。它们都被编组为指针。另一方面,您将此字段映射为C++类型:代码> WCARGET**/COD>,这只是一个指针。所以这是一个明显的不匹配

不清楚的是如何处理这些字符串的内存分配。调用者分配内存,还是被调用者分配内存

假设被调用方分配内存。在这种情况下,
GraphicsAdapterDesc
struct应该这样声明:

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;
};
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;
    }
}
public struct GraphicsAdapterDesc {
    public IntPtr AdapterName;
    public int AdapterIndex;
    public IntPtr HardwareHash;

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

    public int NumOutputs;
};
函数返回后,调用
Marshal.PtrToStringUni()
AdapterName
HardwareHash
转换为字符串。至于解除分配,这超出了问题的范围


这似乎是最合理的选择。显然,被调用方不能为这两个字符串分配内存,因为缓冲区的长度没有在接口中传递。当然,字符串被声明为
const wchar\u t*
,这是进一步的证据

你在这两方面都是正确的;预计在调用时已分配内存。