Difficult Marshalling DirectX array of structures from C ++ to C #

Purpose : Marshal C ++ (pointer to?) An array of structures in C #.

C ++: CreateVertexDeclaration ()

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

FROM#

I use this to define the structure of D3DVERTEXELEMENT9 . SharpDX is a DirectX managed library generated directly from the C ++ DirectX SDK headers , so it is supposedly quite compatible for COM interoperability. In fact, I already have 10 other hooks that work fine, but this is the first with a pointer to an array of structures.

Just in case, the SharpDX structure definition does not work, I also tried to replace it with my own definition:

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

I tried the following C # method signature methods:

Note: having IntPtr devicePointer as the first parameter should not be a problem. I have 10 other hooks that work successfully.

Note. These are the Direct3D API hooks. Thus, the data that I need for the marshal from the unmanaged C ++ environment to the managed C # environment is transferred to my program.

one:

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

Result: the array has one element, but its default values ​​(does not make sense). This means that short Stream , short Offset , Type , Method , Usage and UsageIndex all 0 (enumerations take the first value).

2:

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

Result: the array itself is null.

3:

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

Same as 1. No benefit.

4:

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

The same as 1 does not change anything. I get the default structure. Same thing if I called new D3DVERTEXELEMENT9() .

5:

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

I forgot the result, but it didn’t work. I think he actually broke the hooks.

6:

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

I think that either this # 6 or # 1 / # 2 should be correct. I probably messed up the implementation of this method?

I tried using this code with it:

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

But that will not work! This is the same as # 1. My ordered structure pointer is the default structure, just as if I called new D3DVERTEXELEMENT9() .

7:

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

Zero or zero; not valid.


For method # 6 (using IntPtr), I tried to view the memory area in Visual Studio. The next 50 bytes or so were all zeros. Perhaps there was one byte 2 in a field of 50 zeros. But it was almost 49 bytes of zeros, starting with IntPtr passed to the method.

Isn't that a problem? Does this mean that the supplied pointer is already incorrect? Therefore, I took it for the fact that I had the wrong method signature. The problem is that I tried to use all kinds of combinations, and they either break the program or give me the default array in the same way as if I were calling new D3DVERTEXELEMENT9() .

Question: What is the solution to correctly sort an array of pVertexElements structures from C ++ to C # and why are my current signatures incorrect?

Additional note. In C ++, the length of this particular array is determined by the suffix D3DDECL_END . I do not know how I should get the corresponding array length in C # without any parameter of the traversed length.


Other work hooks:

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) 

...

FROM#

Note. I use SharpDX enumerations and structures for all of these delegates, and they work fine. They also start with 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); ... 

Log of passed parameters for other hooks:

 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) 

The method signature, most similar to this CreateVertexDeclaration() , looks like Clear() . Here is my implementation of 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; } 
+7
source share
1 answer

First, an ad in SharpDX uses Pack = 2 (not Pack = 1):

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

Also, if you want to be sure of a marshal, prefer to use unsafe than Marshal.StructureToPtr, or any other marshal method whenever possible (when the structure maps directly to the C # equivalent). You will avoid marshal performance issues and you will be sure of a memory layout (assuming the structure has been correctly declared in 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)
     ...
 }
+3
source

All Articles