7

目標: 構造体の 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)

結果: 配列には 1 つの要素がありますが、その値はデフォルトです (意味がありません)。これはshort Streamshort OffsetTypeMethodUsage、および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));

しかし、うまくいきません!#1と同じです。整列化された構造体へのポインターは、呼び出した場合と同じように、デフォルトの構造体ですnew D3DVERTEXELEMENT9()

7:

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

null またはゼロ。有効ではありません。


方法 6 (IntPtr を使用) では、Visual Studio でメモリ領域を表示してみました。次の 50 バイト程度はすべてゼロでした。250 個のゼロのフィールドには、おそらく 1バイトがありました。しかし、メソッドに提供された 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)

...

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

1 に答える 1

3

まず、SharpDX での宣言は Pack = 2 を使用しています (Pack = 1 ではありません)。

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

また、マーシャルについて確認したい場合は、Marshal.StructureToPtr や他のマーシャル メソッドよりも unsafe を使用することをお勧めします (構造が C# の同等物に直接マップされている場合)。Marshal のパフォーマンスの問題を回避し、メモリ レイアウトについて確信を持つことができます (構造が C# で正しく宣言されていると仮定します)。

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

    // すべての VertexElement へのアクセス (最後の要素は d3d9types.h の Cmacro D3DDECL_END() によって指定されます)
    ...
}
于 2012-11-16T00:53:46.223 に答える