从C ++到C#的结构很难整理DirectX数组

目标 :Marshal C ++(指向一个?)C#数组的结构体。

C ++: CreateVertexDeclaration()

   HRESULT CreateVertexDeclaration(
     [in]           const D3DVERTEXELEMENT9 *pVertexElements,
     [out, retval]  IDirect3DVertexDeclaration9 **ppDecl
   );

C#:

我使用它来定义D3DVERTEXELEMENT9结构。 SharpDX是一个由DirectX SDK C ++头文件直接生成的托管DirectX库,因此它可以很好地兼容COM互操作。 实际上,我已经有10个其他钩子可以完美工作,但这是第一个带有指向结构数组的指针。

以防万一SharpDX的结构定义不起作用,我也试着用我自己的定义替换它:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class D3DVERTEXELEMENT9
    {
        public short Stream;
        public short Offset;
        public DeclarationType Type;      // Enum
        public DeclarationMethod Method;  // Enum
        public DeclarationUsage Usage;    // Enum
        public byte UsageIndex;
    }

我已经尝试了以下C#方法签名:

注意:将IntPtr devicePointer作为第一个参数不应该是问题。 我有10个其他钩子可以成功运行。

注意:这些是Direct3D API挂钩。 所以我的程序正在将我需要的数据从非托管C ++环境传递到托管C#环境。

1:

CreateVertexDeclaration(IntPtr devicePointer, D3DVERTEXELEMENT9[] vertexElements, out IntPtr vertexDeclaration)

结果:数组有一个元素,但它的值是默认的(没有意义)。 这意味着short Streamshort OffsetTypeMethodUsageUsage UsageIndex都是0(枚举取第一个值)。

2:

CreateVertexDeclaration(IntPtr devicePointer, ref D3DVERTEXELEMENT9[] vertexElements, out IntPtr vertexDeclaration)

结果:数组本身为空。

3:

CreateVertexDeclaration(IntPtr devicePointer, [In, Out] D3DVERTEXELEMENT9[] vertexElements, out IntPtr vertexDeclaration)

与1.一样没有好处。

4:

CreateVertexDeclaration(IntPtr devicePointer, D3DVERTEXELEMENT9 vertexElements, out IntPtr vertexDeclaration)

与1相同,不会改变任何内容。 我得到一个默认的结构。 如果我调用new D3DVERTEXELEMENT9()

5:

CreateVertexDeclaration(IntPtr devicePointer, ref D3DVERTEXELEMENT9 vertexElements, out IntPtr vertexDeclaration)

我忘了结果,但没有奏效。 我认为它实际上是撞上了钩子。

6:

CreateVertexDeclaration(IntPtr devicePointer, IntPtr vertexElements, out IntPtr vertexDeclaration)

我认为这个#6或#1 /#2应该是正确的。 我可能搞砸了这个方法的实现吗?

我试图用这个代码:

var vertex = (D3DVERTEXELEMENT9)Marshal.PtrToStructure(vertexElements, typeof(D3DVERTEXELEMENT9));

但它不起作用! 这和#1是一样的。 我编组的指针结构是一个默认的结构,就像我调用new D3DVERTEXELEMENT9()

7:

CreateVertexDeclaration(IntPtr devicePointer, ref IntPtr vertexElements, out IntPtr vertexDeclaration)

零或零; 无效。


对于方法#6(使用IntPtr),我尝试在Visual Studio中查看内存区域。 接下来的50个字节左右都是零。 在50个零的字段中可能有一个2字节。 但是从提供给该方法的IntPtr开始,它几乎有49个字节的零。

这不是问题吗? 这不是说提供的指针已经不正确吗? 所以我认为这意味着我有错误的方法签名。 问题是我一直在尝试各种可能的组合,并且它们要么崩溃程序,要么给我一个默认数组,就像我调用new D3DVERTEXELEMENT9()

问题:将C ++结构的pVertexElements数组正确编组为C#的解决方案是什么,以及为什么我的当前签名不正确?

附加说明:在C ++中,此特定数组的长度由后缀D3DDECL_END定义。 我不知道如何在C#中获得相应的数组长度,而不需要某种传递的长度参数。


其他工作钩:

C ++:

BeginScene(LPDIRECT3DDEVICE9 pDevice)
BeginStateBlock(LPDIRECT3DDEVICE9 pDevice)
Clear(LPDIRECT3DDEVICE9 pDevice, DWORD Count,CONST D3DRECT* pRects,DWORD Flags,D3DCOLOR Color,float Z,DWORD Stencil)
ColorFill(LPDIRECT3DDEVICE9 pDevice, IDirect3DSurface9* pSurface,CONST RECT* pRect,D3DCOLOR color)
CreateAdditionalSwapChain(LPDIRECT3DDEVICE9 pDevice, D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DSwapChain9** pSwapChain)
CreateCubeTexture(LPDIRECT3DDEVICE9 pDevice, UINT EdgeLength,UINT Levels,DWORD Usage,D3DFORMAT Format,D3DPOOL Pool,IDirect3DCubeTexture9** ppCubeTexture,HANDLE* pSharedHandle)

...

C#:

注意:我为所有这些代表使用SharpDX枚举和结构,并且它们工作正常。 他们也都以IntPtr devicePointer

BeginSceneDelegate(IntPtr devicePointer);
BeginStateBlocKDelegate(IntPtr devicePointer);
ClearDelegate(IntPtr devicePointer, int count, IntPtr rects, ClearFlags flags, ColorBGRA color, float z, int stencil);
ColorFillDelegate(IntPtr devicePointer, IntPtr surface, IntPtr rect, ColorBGRA color);
CreateAdditionalSwapChainDelegate(IntPtr devicePointer, [In, Out] PresentParameters presentParameters, out SwapChain swapChain);
CreateCubeTextureDelegate(IntPtr devicePointer, int edgeLength, int levels, Usage usage, Format format, Pool pool, out IntPtr cubeTexture, IntPtr sharedHandle);
...

其他钩子传递参数的日志:

DLL injection suceeded.
Setting up Direct3D 9 hooks...
Activating Direct3D 9 hooks...
CreateDepthStencilSurface(IntPtr devicePointer: 147414976, Int32 width: 1346, Int32 height: 827, Format format: D24S8, MultisampleType multiSampleType: None, Int32 multiSampleQuality: 0, Boolean discard: False, IntPtr& surface: (out), IntPtr sharedHandle: 0)
CreateDepthStencilSurface(IntPtr devicePointer: 147414976, Int32 width: 1346, Int32 height: 827, Format format: D24S8, MultisampleType multiSampleType: None, Int32 multiSampleQuality: 0, Boolean discard: False, IntPtr& surface: (out), IntPtr sharedHandle: 0)
Clear(IntPtr devicePointer: 147414976, Int32 count: 0, IntPtr rects: (Empty), ClearFlags flags: Target, ColorBGRA color: A:0 R:0 G:0 B:0, Single z: 1, Int32 stencil: 0)
Clear(IntPtr devicePointer: 147414976, Int32 count: 0, IntPtr rects: (Empty), ClearFlags flags: Target, ColorBGRA color: A:0 R:0 G:0 B:0, Single z: 1, Int32 stencil: 0)
BeginScene(IntPtr devicePointer: 147414976)
Clear(IntPtr devicePointer: 147414976, Int32 count: 0, IntPtr rects: (Empty), ClearFlags flags: Target, ColorBGRA color: A:0 R:0 G:0 B:0, Single z: 1, Int32 stencil: 0)
Clear(IntPtr devicePointer: 147414976, Int32 count: 0, IntPtr rects: (Empty), ClearFlags flags: Target, ColorBGRA color: A:0 R:0 G:0 B:0, Single z: 1, Int32 stencil: 0)
Clear(IntPtr devicePointer: 147414976, Int32 count: 0, IntPtr rects: (Empty), ClearFlags flags: ZBuffer, ColorBGRA color: A:0 R:0 G:0 B:0, Single z: 1, Int32 stencil: 0)
BeginScene(IntPtr devicePointer: 147414976)

与此CreateVertexDeclaration()最相似的方法签名似乎是Clear() 。 这里是我的Clear()

private Result Clear(IntPtr devicePointer, int count, IntPtr rects, ClearFlags flags, ColorBGRA color, float z, int stencil)
        {
            try
            {
                var structSize = Marshal.SizeOf(typeof (Rectangle));
                var structs = new Rectangle[count];
                for (int i = 0; i < count; i++)
                {
                    structs[i] = (Rectangle) Marshal.PtrToStructure(rects, typeof (Rectangle));
                }
                // Seems to work fine, not sure why it doesn't work for CreateVertexDeclaration

                var rectangles = structs;
                Log.LogMethodSignatureTypesAndValues(devicePointer, count, rectangles.PrintTypesNamesValues(), flags,
                                                     color, z, stencil);
                GetOrCreateDevice(devicePointer);
                if (rectangles.Length == 0)
                    Device.Clear(flags, color, z, stencil);
                else
                    Device.Clear(flags, color, z, stencil, rectangles);
            }
            catch (Exception ex)
            {
                Log.Warn(ex.ToString());
            }

            return Result.Ok;
        }

首先,SharpDX中的声明使用Pack = 2(而不是Pack = 1):

    [StructLayout(LayoutKind.Sequential, Pack = 2 )]
    public  partial struct VertexElement {  
     ...
    }

此外,如果你想确定元帅的身份,那么只要有可能(当结构直接映射到C#等价物时),最好使用比Marshal.StructureToPtr或任何其他编组方法都不安全的元素。 您将避免Marshal性能问题,并且您将确定内存布局(假设该结构已在C#中正确声明)

public unsafe static void CreateVertexDeclaration(IntPtr devicePointer, IntPtr vertexElementsPtr, out IntPtr vertexDeclaration)
{
    var vertexElements = (VertexElement*)vertexElementsPtr;

    // Access to all VertexElement (last element is specified by Cmacro D3DDECL_END() in d3d9types.h)
    ...
}
链接地址: http://www.djcxy.com/p/11421.html

上一篇: Difficult Marshalling DirectX array of structs from C++ to C#

下一篇: Why does this constexpr code cause GCC to eat all my RAM?