LoginSignup
1
2

More than 3 years have passed since last update.

COMオブジェクトを含んだネイティブ関数のP/Invokeメソッド変種

Last updated at Posted at 2019-07-10

COMオブジェクトを生成する、ファクトリー関数をP/Invokeメソッドとして宣言する場合、(COMオブジェクトを含まなくてもそうだけど)いろいろな宣言が可能なので、そのメモ。

ネイティブ関数

SHCreateDefaultContextMenu を例にする。

SHSTDAPI SHCreateDefaultContextMenu(_In_ const DEFCONTEXTMENU *pdcm, _In_ REFIID riid, _Outptr_ void **ppv);

メソッドいろいろ

素直なP/Invokeメソッド

C# 7.2から使用できるin引数を使用している。

[DllImport("shell32.dll", PreserveSig = false)]
void SHCreateDefaultContextMenu(in DEFCONTEXTMENU pdcm, in Guid riid, out IContextMenu ppv);

C# 7.2より前

Guid構造体には、UnmanagedType.LPStructが使えるらしいので、ref を付けずにポインタを渡せる。

[DllImport("shell32.dll", PreserveSig = false)]
void SHCreateDefaultContextMenu(ref DEFCONTEXTMENU pdcm, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IContextMenu ppv);

COMオブジェクトをinterfaceではなく、objectで受け取る

[DllImport("shell32.dll", PreserveSig = false)]
void SHCreateDefaultContextMenu(ref DEFCONTEXTMENU pdcm, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.IUnknown)]out object ppv);

戻り値をHRESULTのまま受け取る

DllImport 属性のPreserveSigtrue(デフォルト)にしておけば、ネイティブ関数のHRESULTの値をそのまま受け取れる。
その代わり、エラーチェックは自分でやる必要がある。

[DllImport("shell32.dll", PreserveSig = true)]
int SHCreateDefaultContextMenu(in DEFCONTEXTMENU pdcm, in Guid riid, out IContextMenu ppv);

最後の引数を戻り値として受け取る

COMのIDLには retval属性があり、メソッドの戻り値があるかどうかは最後の引数の属性で決まる。
単なる属性なので、P/Invokeでもシグニチャ次第となる。
PreserveSig = falseが付与されている前提で、

  • 戻り値をvoidにすれば戻り値なしになる
  • 最後の引数を戻り値に移動させればそれが戻り値になる
[DllImport("shell32.dll", PreserveSig = false)]
IContextMenu SHCreateDefaultContextMenu(in DEFCONTEXTMENU pdcm, in Guid riid);

付録:上記で使用している型の宣言

struct DEFCONTEXTMENU
{
    public IntPtr hwnd;
    public IContextMenuCB pcmcb;
    public PCIDLIST_ABSOLUTE pidlFolder;
    public IShellFolder psf;
    public uint cidl;
    public PCUITEMID_CHILD_ARRAY apidl;
    [MarshalAs(UnmanagedType.IUnknown)]
    public object punkAssociationInfo;
    public uint cKeys;
    // const HKEY            *aKeys;
    public IntPtr[] aKeys;
}

[ComImport]
[SuppressUnmanagedCodeSecurity]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214e4-0000-0000-c000-000000000046")]
internal interface IContextMenu
{
    void QueryContextMenu(
        /* [annotation][in] */
        IntPtr hmenu,
        /* [annotation][in] */
        uint indexMenu,
        /* [annotation][in] */
        uint idCmdFirst,
        /* [annotation][in] */
        uint idCmdLast,
        /* [annotation][in] */
        uint uFlags);

    void InvokeCommand(
        /* [annotation][in] */
        in CMINVOKECOMMANDINFO pici);

    void GetCommandString(
        /* [annotation][in] */
        UIntPtr idCmd,
        /* [annotation][in] */
        uint uType,
        /* [annotation][in] */
        UIntPtr pReserved,
        /* [annotation][out] */ 
        StringBuilder pszName,
        /* [annotation][in] */
        uint cchMax);
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2