C#で実行時に得たCOMオブジェクトのインタフェース名を取得する

# やれること

インスタンス化したComObject(※)が特定のインタフェース(IDispatch)を実装している場合に

+ インタフェース名 と そのGUID
+ TypeLibrary名

を取得します。

※簡単な判別方法： ` .GetType().ToString()` が ` "System.__ComObject" `になるやつ

# ソースコード

<details><summary>ソースコード</summary><div>

```csharp

using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.InteropServices;

// ↓これを消すと、System.Runtime.InteropServicesにある同名の古い定義をうっかり引き当ててしまうおそれがあるので残す
using System.Runtime.InteropServices.ComTypes;

using ComTypes = System.Runtime.InteropServices.ComTypes;


public static class ShowComInfo
{
    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00020400-0000-0000-c000-000000000046")]
    private interface IDispatch
    {
        [PreserveSig]
        int GetTypeInfoCount(out int count);
        [PreserveSig]
        int GetTypeInfo([In] int itinfo, [In] int lcid, out IntPtr typeinfo);
        [PreserveSig]
        int GetIDsOfNames();//dummy. don't call this method
        [PreserveSig]
        int Invoke();//dummy. don't call this method
    }
    
    static Guid? GetGuidFromIType(ITypeInfo typ)
    {
        Guid? guid = null;
        IntPtr ptr;
        try {
            typ.GetTypeAttr(out ptr);
        }
        catch ( COMException ) { return null; }
        
        try {
            var attr = Marshal.PtrToStructure<ComTypes.TYPEATTR>(ptr);
            guid = attr.guid;
        }
        finally {
            typ.ReleaseTypeAttr(ptr);
        }
        
        return guid;
    }
    
    static void ShowNamesOfTypeInfo(ITypeInfo typ)
    {
        string strName;
        string strDocString;
        int dwHelpContext;
        string strHelpFile;
        
        typ.GetDocumentation(-1, out strName, out strDocString, out dwHelpContext, out strHelpFile);
        Console.Write("InterfaceName: ");
        Console.WriteLine(strName??"<<NULL>>");
        Console.Write("InterfaceDocString: ");
        Console.WriteLine(strDocString??"<<NULL>>");
        Console.Write("InterfaceHelpFile: ");
        Console.WriteLine(strHelpFile??"<<NULL>>");
    }
    
    static void ShowNamesOfTypeLib(ITypeLib typ)
    {
        string strName;
        string strDocString;
        int dwHelpContext;
        string strHelpFile;
        
        typ.GetDocumentation(-1, out strName, out strDocString, out dwHelpContext, out strHelpFile);
        Console.Write("TypeLibName: ");
        Console.WriteLine(strName??"<<NULL>>");
        Console.Write("TypeLibDocString: ");
        Console.WriteLine(strDocString??"<<NULL>>");
        Console.Write("TypeLibHelpFile: ");
        Console.WriteLine(strHelpFile??"<<NULL>>");
    }
    
    public static void ShowInterfaceIdFromComObject(object obj)
    {
        Guid? guid = null;
        ComTypes.ITypeInfo typeInfo = null;
        IntPtr ptr = IntPtr.Zero;
        
        try {
            if ( obj is IDispatch ) {
                var idisp = (IDispatch)obj;
                int count;
                int hResult = idisp.GetTypeInfoCount(out count);
                if ( hResult >= 0 ) { // Succeeded
                    if ( count < 1 ) { // no type info
                        return;
                    }
                    
                    idisp.GetTypeInfo(0, 0, out ptr);
                    if ( ptr == IntPtr.Zero ) {
                        return;
                    }
                    
                    typeInfo = (ComTypes.ITypeInfo)
                       (Marshal.GetTypedObjectForIUnknown(ptr, typeof(ComTypes.ITypeInfo)));
                    
                    if ( typeInfo == null ) {
                        return;
                    }
                    ShowNamesOfTypeInfo(typeInfo);
                    guid = GetGuidFromIType(typeInfo);
                    Console.Write("InterfaceGUID: ");
                    Console.WriteLine(guid);
                    
                    ITypeLib typLib;
                    int indexOfLib;
                    typeInfo.GetContainingTypeLib(out typLib, out indexOfLib);
                    ShowNamesOfTypeLib(typLib);
                }
            }
            else { // unknown type
                return;
            }
        }
        finally {
            if ( ptr != IntPtr.Zero ) {
                Marshal.Release(ptr);
                ptr = IntPtr.Zero;
            }
        }
    }
}

class Test
{
    [STAThread]
    static void Main(string[] args)
    {
        Type comType = Type.GetTypeFromProgID("Shell.Application");
        dynamic o = Activator.CreateInstance(comType);
        
        try {
            ShowComInfo.ShowInterfaceIdFromComObject(o);
        }
        finally {
            Marshal.ReleaseComObject(o);
        }
    }
}

```

</div></details>

# 実行結果

```
InterfaceName: IShellDispatch6
InterfaceDocString: Updated IShellDispatch
InterfaceHelpFile: <<NULL>>
InterfaceGUID: 34936ba1-67ad-4c41-99b8-8c12dff1e974
TypeLibName: Shell32
TypeLibDocString: Microsoft Shell Controls And Automation
TypeLibHelpFile: <<NULL>>
```

ちなみにCOMオブジェクトのインタフェースは、レジストリの `HKEY_CLASSES_ROOT\Interface` にいます。
OleView.exeでTypeLibraryを探すと情報がみれる。

# 参考サイト
- http://eternalwindows.jp/com/auto/auto02.html
- http://goungoun.dip.jp/app/fswiki/wiki.cgi/devnotebook?page=Visual+Studio+%2ENET+2003+C%23%A1%A2COM%A5%AA%A5%D6%A5%B8%A5%A7%A5%AF%A5%C8%A4%CE%BC%EF%CE%E0%A4%F2%C6%C0%A4%EB%A1%A3
- https://msdn.microsoft.com/ja-jp/magazine/dd347981.aspx

# OleView.exeの使い方

OleView.exeでインタフェースのメソッド、プロパティを調べる方法。
※そのPCにインストールされているものしか出ません。例：ExcelがインストールされていないPCでExcelのCOM Objectは多分でてこない。

■Step0. 入手する ・・・ oleview.exe microsoft あたりのキーワードでググるべし。
■Step1. 起動する ・・・ C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin らへんにいるはず。。環境依存。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438183/7fb0c384-e793-dbc3-c394-f564d4702f14.png)
■Step2. タイプライブラリ名を探す 
「Type Libraries」から、TypeLib名を探す(例：Microsoft Shell Controls And Automation)
■Step3. タイプライブラリ名をダブルクリックする
・・・別ウィンドウが立ち上がります。
■Step4. インタフェース名を探す。
・・・dispinterface インタフェース名


# その後
[C#でITypeInfoクラスを使い倒してCOMオブジェクトの関数名とパラメータを表示させる](https://qiita.com/kob58im/items/5570c79a8f1f4c5347c2)