Jas*_*son 7 c# c++ marshalling unmarshalling sharpdx
目标:Marshal C++(指向?)的结构数组到C#.
C++: CreateVertexDeclaration()
HRESULT CreateVertexDeclaration(
[in] const D3DVERTEXELEMENT9 *pVertexElements,
[out, retval] IDirect3DVertexDeclaration9 **ppDecl
);
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
我尝试了以下C#方法签名:
注意:IntPtr devicePointer作为第一个参数应该不是问题.我有10个其他成功工作的钩子.
注意:这些是Direct3D API挂钩.所以我的程序正在传递我需要从非托管C++环境编组到托管C#环境的数据.
1:
CreateVertexDeclaration(IntPtr devicePointer, D3DVERTEXELEMENT9[] vertexElements, out IntPtr vertexDeclaration)
结果:数组有一个元素,但其值是默认值(没有意义).这意味着short Stream,short Offset,Type,Method,Usage,和UsageIndex均为0(枚举取他们的第一值).
2:
CreateVertexDeclaration(IntPtr devicePointer, ref D3DVERTEXELEMENT9[] vertexElements, out IntPtr vertexDeclaration)
结果:数组本身为null.
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));
Run Code Online (Sandbox Code Playgroud)
但它不起作用!它与#1相同.我编组的指针结构是一个默认的结构,就像我打电话一样new D3DVERTEXELEMENT9().
7:
CreateVertexDeclaration(IntPtr devicePointer, ref IntPtr vertexElements, out IntPtr vertexDeclaration)
空或零; 无效.
对于方法#6(使用IntPtr),我尝试在Visual Studio中查看内存区域.接下来的50个字节左右都是零.在250个零的字段中可能有一个字节.但是从提供给该方法的IntPtr开始,它几乎是49个字节的零.
这不是问题吗?这是不是意味着提供的指针已经不正确?所以我认为这意味着我有错误的方法签名.问题是我一直在尝试各种可能的组合,他们要么崩溃程序,要么给我一个默认数组,就像我打电话一样new D3DVERTEXELEMENT9().
问题:正确编组pVertexElements从C++到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)
Run Code Online (Sandbox Code Playgroud)
...
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);
...
Run Code Online (Sandbox Code Playgroud)
其他挂钩的传递参数的日志:
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)
Run Code Online (Sandbox Code Playgroud)
与此类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;
}
Run Code Online (Sandbox Code Playgroud)
首先,SharpDX 中的声明使用 Pack = 2(而不是 Pack = 1):
[StructLayout(LayoutKind.Sequential, Pack = 2 )]
公共部分结构 VertexElement {
...
}
此外,如果您想确定编组,请尽可能使用 unsafe,而不是 Marshal.StructureToPtr 或任何其他编组方法(当结构直接映射到 C# 等效项时)。您将避免 Marshal 性能问题,并且您将确定内存布局(假设在 C# 中正确声明了该结构)
公共不安全静态无效CreateVertexDeclaration(IntPtr devicePointer,IntPtr vertexElementsPtr,out IntPtr vertexDeclaration)
{
var vertexElements = (VertexElement*)vertexElementsPtr;
// 访问所有VertexElement(最后一个元素由d3d9types.h中的Cmacro D3DDECL_END()指定)
...
}