C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[9]: [COM]ITypeInfo からのインターフェイス取得方法


(過去ログ 131 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■77593 / inTopicNo.1)  [COM]ITypeInfo からのインターフェイス取得方法
  
□投稿者/ kurage (4回)-(2015/11/06(Fri) 01:21:48)

分類:[C#] 

こんにちは。
質問のタイトルがわかりづらくてすみません。(文字数制限により、上手いタイトルを付けられませんでした。)

【質問の概要】
[COM] ITypeInfo オブジェクトを元に、IConnectionPointContainer インターフェイスが実装されているかどうか確認したい。

【開発環境】
Visual Studio 2013 Community

----
C# にて、COM の型情報 (メソッドやプロパティ、イベントの情報など) を取得するプログラムを作成するべく、試行錯誤中です。

ある COM オブジェクトがイベントをサポートしている場合、そのオブジェクトは IConnectionPointContainer インターフェイスを実装している、ということを知りました。
(この解釈自体、間違っていたらすみません。)

そこで、IConnectionPointContainer インターフェイスを実装しているかどうかを調べる方法が知りたいのですが、

COM オブジェクト自体を取得できている場合は、

 object comObj = <<何らかの COM オブジェクト>>;
 IConnectionPointContainer c = comObj as IConnectionPointContainer;

とし、変数 c が null でなければ (つまりキャストできれば) IConnectionPointContainer インターフェイスが実装されている、と判定することはできました。

しかし、上記のように具体的な COM オブジェクトが無い状態で、COM の型情報を示す ITypeInfo オブジェクトのみが取得できている場合、
そこから IConnectionPointContainer インターフェイスが実装されているかどうかを調べる方法がわかりません。

このような事は可能なのでしょうか?また、もし可能なのであれば、その方法を (ヒントだけでも) 併せてご教示いただけませんでしょうか。

COM の知識が乏しく、的はずれな質問になっていたら申し訳ございません。
不足等ございましたら、追記致しますので、お手数ですがご指摘ください。

よろしくお願い致します。

引用返信 編集キー/
■77594 / inTopicNo.2)  Re[1]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ ito (3回)-(2015/11/06(Fri) 09:16:24)
COM自身に問い合わせを行うためのQueryInterfaceというメソッドがあります。
https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.marshal.queryinterface(v=vs.110).aspx

引用返信 編集キー/
■77595 / inTopicNo.3)  Re[2]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ kurage (5回)-(2015/11/06(Fri) 09:31:42)
No77594 (ito さん) に返信
> COM自身に問い合わせを行うためのQueryInterfaceというメソッドがあります。
> https://msdn.microsoft.com/ja-jp/library/system.runtime.interopservices.marshal.queryinterface(v=vs.110).aspx
>

ito さん、ご回答有り難うございます。

QueryInterface は、COM オブジェクト自体を取得できている場合は使用できると思います。
(質問文内ソースコードのように、キャストする事と同じと認識しています。)

今回は、COM オブジェクトは取得されておらず、型情報 (ITypeInfo オブジェクト) だけが取得できている場合の方法を知りたく存じます。

※もし ITypeInfo を使用しての QueryInterface を呼び出す方法があるのであれば、お手数ですがご教示いただけると有難いです。
引用返信 編集キー/
■77598 / inTopicNo.4)  Re[3]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ ito (4回)-(2015/11/06(Fri) 13:34:47)
No77595 (kurage さん) に返信
> ■No77594 (ito さん) に返信
> 今回は、COM オブジェクトは取得されておらず、型情報 (ITypeInfo オブジェクト) だけが取得できている場合の方法を知りたく存じます。
>
> ※もし ITypeInfo を使用しての QueryInterface を呼び出す方法があるのであれば、お手数ですがご教示いただけると有難いです。
少し勘違いしていました。
ITypeInfoが有り、オブジェクトインスタンスが無いと考えてよろしいですか?

ITypeInfoメンバーを見ると、InvokeメソッドでQueryInterfaceを呼び出せばるように見えますが、オブジェクトインスタンスが必要なんですよね...
ITypeInfo.CreateInstanceでオブジェクトインスタンスを作ってしまうぐらいしか思いつきません。


引用返信 編集キー/
■77599 / inTopicNo.5)  Re[4]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ kurage (6回)-(2015/11/06(Fri) 15:04:20)
No77598 (ito さん) に返信
> ITypeInfoが有り、オブジェクトインスタンスが無いと考えてよろしいですか?

その通りです。

> ITypeInfoメンバーを見ると、InvokeメソッドでQueryInterfaceを呼び出せばるように見えますが、オブジェクトインスタンスが必要なんですよね...
> ITypeInfo.CreateInstanceでオブジェクトインスタンスを作ってしまうぐらいしか思いつきません。

現在、題材としているのが IE 上のオブジェクト (ボタン要素やプルダウン要素など) であり、
これらは単独でインスタンスを作成することができないようなのです。

(たとえば、mshtml.HTMLOptionElement などです。「OLE/COM Object Viewer」なるもので当該のクラスを見てみたところ、
「noncreatable」と記述されているので、これらは単独でインスタンスを作成できない型なのかと解釈しています。)
引用返信 編集キー/
■77630 / inTopicNo.6)  Re[5]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ (こ) (2回)-(2015/11/09(Mon) 18:16:08)
2015/11/09(Mon) 18:32:06 編集(投稿者)
coclass の ITypeInfo から GetRefTypeOfImplType(), GetRefTypeInfo() を順に呼ぶことで、
coclass が実装しているインターフェースの ITypeInfo を取得することができます。

※以下、coclass の ITypeInfo がある(TYPEATTR.typekind == TKIND_COCLASS)という前提の話です。

C++ が読めるなら、
http://yanaware.com/com4me/typeinfo2.php-author=Sean%20BAXTER&mail=spec@ript.net&url=http---ript.net-~spec-&idTute=13.htm
の一番下のサンプルコードが参考になります。

上記 URL のサンプルコードを C# に変換すると以下のような感じかと思います。
一応動作確認済みです。
(C# には不慣れなので変なところがあるのはご容赦ください)

using System;
using System.Runtime.InteropServices;
using ComTypes = System.Runtime.InteropServices.ComTypes;

namespace typetest
{
    class Program
    {
        [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
        static extern int LoadTypeLib(string fileName, out ComTypes.ITypeLib typeLib);

        static void Main(string[] args)
        {
            ComTypes.ITypeLib pTypeLib;
            int hr = LoadTypeLib(@"C:\Windows\System32\Shdocvw.dll", out pTypeLib);
            if (hr != 0)
            {
                throw new ExternalException("LoadTypeLib", hr);
            }

            int infoCount = pTypeLib.GetTypeInfoCount();
            for (int curCoClass = 0; curCoClass < infoCount; ++curCoClass)
            {
                ComTypes.TYPEKIND typeKind;
                pTypeLib.GetTypeInfoType(curCoClass, out typeKind);
                if (typeKind != ComTypes.TYPEKIND.TKIND_COCLASS)
                {
                    continue;
                }
                string coClassName, docString, helpFile;
                int helpContext;
                pTypeLib.GetDocumentation(curCoClass, out coClassName, out docString, out helpContext, out helpFile);
                Console.WriteLine("{0}:", coClassName);

                ComTypes.ITypeInfo pTypeInfo;
                pTypeLib.GetTypeInfo(curCoClass, out pTypeInfo);
                IntPtr typeAttr_;
                pTypeInfo.GetTypeAttr(out typeAttr_);
                try
                {
                    ComTypes.TYPEATTR typeAttr =
                        (ComTypes.TYPEATTR)Marshal.PtrToStructure(typeAttr_, typeof(ComTypes.TYPEATTR));

                    // ここからがポイント
                    for (int curInterface = 0; curInterface < typeAttr.cImplTypes; ++curInterface)
                    {
                        int hRefType;
                        pTypeInfo.GetRefTypeOfImplType(curInterface, out hRefType);
                        ComTypes.ITypeInfo pInterfaceInfo;
                        pTypeInfo.GetRefTypeInfo(hRefType, out pInterfaceInfo);
                        string interfaceName;
                        pInterfaceInfo.GetDocumentation(-1, out interfaceName, out docString, out helpContext, out helpFile);
                        Console.WriteLine("\t{0}", interfaceName);
                    }
                    // ここまでがポイント
                }
                finally
                {
                    pTypeInfo.ReleaseTypeAttr(typeAttr_);
                }
            }
        }
    }
}

ポイントと書いた箇所のようなループを作り、ループ内で pInterfaceInfo が
目的のインターフェースを示しているかを何かしらの方法でチェックすればよいかと思います。
(例えば TYPEATTR を取得して guid が目的のインターフェースの GUID と一致するかを調べる、とか)

引用返信 編集キー/
■77655 / inTopicNo.7)  Re[6]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ kurage (7回)-(2015/11/11(Wed) 13:50:31)
No77630 ((こ) さん) に返信

(こ)さん、こんにちは。丁寧なソース付きでご回答下さいまして、誠に有難うございます。
返信が遅くなり、申し訳ございませんでした。

現時点で、コクラスの ITypeInfo については、取得できています。

ご提示くださったソースの方法を試してみましたところ、幾つかのインターフェイスについては列挙されました。
しかし、今回目的としている IConnectionPointContainer インターフェイスについては、取得することができませんでした。

※念のため、取得されたそれぞれのインターフェイスの ITypeInfo に対しても、同様に GetRefTypeInfo() を実行してみて確認しましたが、ダメでした。
 (IDispatch が取得されるだけでした。)

IConnectionPointContainer インターフェイスは、「OLE/COM Object Viewer (oleview.exe)」でも表示されないようです。
ただ、COM オブジェクトのインスタンスを IConnectionPointContainer 型にキャストする事はできるため、確実に実装されてはいるようなのです。
何か、特別な検索方法を用いないとダメとか、あるのでしょうか…。
引用返信 編集キー/
■77668 / inTopicNo.8)  Re[7]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ Azulean (538回)-(2015/11/13(Fri) 00:05:08)
No77655 (kurage さん) に返信
> IConnectionPointContainer インターフェイスは、「OLE/COM Object Viewer (oleview.exe)」でも表示されないようです。
> ただ、COM オブジェクトのインスタンスを IConnectionPointContainer 型にキャストする事はできるため、確実に実装されてはいるようなのです。
> 何か、特別な検索方法を用いないとダメとか、あるのでしょうか…。

極端な話、タイプライブラリなどで宣言しなくても、QueryInterface でそのインターフェースに対するオブジェクトを返せるように実装すれば、キャスト可能なはずです。
よって、オブジェクトを生成せずにすべての型を網羅できるとは限らないですし、無理なことがあると思われます。
引用返信 編集キー/
■77681 / inTopicNo.9)  Re[7]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ とっちゃん (307回)-(2015/11/16(Mon) 11:39:49)
No77655 (kurage さん) に返信
> ■No77630 ((こ) さん) に返信
>
> (こ)さん、こんにちは。丁寧なソース付きでご回答下さいまして、誠に有難うございます。
> 返信が遅くなり、申し訳ございませんでした。
>
> 現時点で、コクラスの ITypeInfo については、取得できています。
>
> ご提示くださったソースの方法を試してみましたところ、幾つかのインターフェイスについては列挙されました。
> しかし、今回目的としている IConnectionPointContainer インターフェイスについては、取得することができませんでした。
>
> ※念のため、取得されたそれぞれのインターフェイスの ITypeInfo に対しても、同様に GetRefTypeInfo() を実行してみて確認しましたが、ダメでした。
>  (IDispatch が取得されるだけでした。)
>
一応...
No77630 で、(こ) さんが提示したソースは、あくまでも ITypeInfo オブジェクトに対する問合せです。

ITypeInfo は、タイプライブラリに対するアクセスをサポートするオブジェクトであって、実際に使いたいインターフェースの実体とは異なります。

多種多様な問い合わせができますが、返答の範囲はあくまでも該当するタイプライブラリに記載されたものの範囲でしかありません。

このあたりは、(こ)さんのサンプルを「ネイティブコードのデバッグを有効にする」のチェックを入れてデバッグ実行し
プロセス内にロードされたDLLを見てみるとわかります。

実装を持つ ShDocVw.dll がロードされることなく終了しているのが見れると思います。



> IConnectionPointContainer インターフェイスは、「OLE/COM Object Viewer (oleview.exe)」でも表示されないようです。
> ただ、COM オブジェクトのインスタンスを IConnectionPointContainer 型にキャストする事はできるため、確実に実装されてはいるようなのです。
> 何か、特別な検索方法を用いないとダメとか、あるのでしょうか…。

IConnectionPointContainer がOLE/COM Object Viewer に出てこないのは、単にタイプライブラリに記述がなされていないからです。

上述したように ITypeInfo は「タイプライブラリ」にアクセスして情報を得るためのものなので、それ以上でもそれ以下でもありません。
そのため、取得しうる情報はあくまでも「タイプライブラリ」の中にある情報となります。
そこに記述されていない範疇のものについては、それがどのようなものか?も含めて詳細を知ることはできません。

さて、イベントですが、タイプライブラリの中では coclass(CoCreateInsatnceに渡すCLSIDの型定義)にある程度推測できる情報があります。

サンプルとしては coclass WebBrowser のタイプライブラリ情報を抜粋してみました。
[
 uuid(8856F961-340A-11D0-A96B-00C04FD705A2),
 helpstring("WebBrowser Control"),
 control
]
coclass WebBrowser {
 [default] interface IWebBrowser2;
 interface IWebBrowser;
 [default, source] dispinterface DWebBrowserEvents2;
 [source] dispinterface DWebBrowserEvents;
};

coclass WebBrowser は、[default] のインターフェースとして IWebBrowser2 を定義しており、そのほかに IWebBrowser を定義していることがここから読み取れます。
また、[default]の[source]すなわち、イベントインターフェースとして、dispinterface DWebBrowserEvents2(DualInterface)を規定しており、合わせて
dispinterface DWebBrowserEvents もイベントインターフェースとして規定している
ということがわかります。

COMの「汎用イベント」は、IConnectionPointContainer を使ってイベントインターフェースを登録する仕組みを提供する規約があります。
そのため、イベントインターフェースを規定している coclass は、そのオブジェクトがイベントを通知可能であるすなわち
IConnectionPointContainer を通じて、あらかじめ規定したインターフェースを接続できることを意味しています。

と、coclass から読み取れるのはここまで。

ここからは、類推が働くことになります。
COMのオブジェクトは、CoCreateInstance 以外の手段でも構築が可能です。
ブラウザのDOMインターフェースと同じように。。。

そのため、間接的にクリエイトされ、coclass を持たないオブジェクトの場合、タイプライブラリにインターフェースだけあれば
それ以上は記述しなくてもいいというオプション的な扱いがあります。

では、そういったインターフェースはどのように規定を行うのか?ですが、こちらは「プログラマに対するリファレンス情報」を
通じて、規定が知らされます。

例えば、マネージメントオブジェクトがあり、そこから個別のインターフェースが取得できるような場合、
マネージメントオブジェクトは、coclass としてタイプライブラリに登録されていますが
個別のインターフェースは、インターフェースが定義されているだけで、coclass が定義されていないことが大半です。

要するにどういうことか?というと、「マニュアル読め!」ということになります。

不親切に聞こえるかもしれませんが、タイプライブラリ自身も一種のマニュアル(リファレンス)です。
そこに記載がないなら、さらに詳しいことはほかの情報を頼るしかないということになります。
具体的には、利用者向けのリファレンスマニュアル、あるいは作者本人、場合によってはソースコードそのものというところ。

このあたりが、COMが一般的になり切れなかった理由の一つであり、タイプライブラリが中途半端と呼ばれる所以でもあったりします。

これらにアクセスせず、なおかつ実際のオブジェクトもない状態で、そのオブジェクトにイベントを接続できるかを
調査する方法があるか?という問いに対しては「そんなものはこの地球上のどこにも存在しない」という冷たい答えを返しておくことにします。

引用返信 編集キー/
■77702 / inTopicNo.10)  Re[8]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ kurage (8回)-(2015/11/17(Tue) 15:57:54)
No77668 (Azulean さん) に返信
No77681 (とっちゃん さん) に返信

Azulean さん、とっちゃんさん、ご回答くださいまして、有難うございます。

お二人の回答内容を見て、私は基本的なことが理解できていなかった事がわかりました。

つまり、ITypeInfo はあくまでタイプライブラリの情報を提供するに過ぎず、
またタイプライブラリを参照するだけでは知り得ない情報もある、ということですね。

> 不親切に聞こえるかもしれませんが、タイプライブラリ自身も一種のマニュアル(リファレンス)です。
> そこに記載がないなら、さらに詳しいことはほかの情報を頼るしかないということになります。
> 具体的には、利用者向けのリファレンスマニュアル、あるいは作者本人、場合によってはソースコードそのものというところ。

なるほど。
タイプライブラリに記載がないということは、ある意味「利用者は知らなくて良い情報」なのですね…。

> これらにアクセスせず、なおかつ実際のオブジェクトもない状態で、そのオブジェクトにイベントを接続できるかを
> 調査する方法があるか?という問いに対しては「そんなものはこの地球上のどこにも存在しない」という冷たい答えを返しておくことにします。

ITypeInfo があくまでもタイプライブラリの情報を元にしているものなのであれば、これは当然の事となりますね。
どうしても知りたければ何とか COM オブジェクトを取得して、QueryInterface するしかないということですね。
もやっとしていたものが、すっきり致しました。

今回質問してみて、いろいろな事が勉強になりました。
皆様、本当に有難うございました。
解決済み
引用返信 編集キー/
■77708 / inTopicNo.11)  Re[9]: [COM]ITypeInfo からのインターフェイス取得方法
□投稿者/ とっちゃん (308回)-(2015/11/17(Tue) 18:44:10)
No77702 (kurage さん) に返信
> つまり、ITypeInfo はあくまでタイプライブラリの情報を提供するに過ぎず、
> またタイプライブラリを参照するだけでは知り得ない情報もある、ということですね。
>
はい。そうです。
タイプライブラリは、その時代的な背景と当時の要件から、すべての情報が記載されているわけではありません。
具体的な例を挙げると、coclass を実装しているものがなにか?はタイプライブラリからはわかるようにはなっていないなど
もあります。


>>不親切に聞こえるかもしれませんが、タイプライブラリ自身も一種のマニュアル(リファレンス)です。
>>そこに記載がないなら、さらに詳しいことはほかの情報を頼るしかないということになります。
>>具体的には、利用者向けのリファレンスマニュアル、あるいは作者本人、場合によってはソースコードそのものというところ。
>
> なるほど。
> タイプライブラリに記載がないということは、ある意味「利用者は知らなくて良い情報」なのですね…。
>
いえ。違います。「利用者が知らなくてもいい」情報なのではないのです。
タイプライブラリは、「型情報」を提供するためのライブラリです。
その型情報を定義するために必要な情報は出ていますがそうではない情報は出てきません。
そのため、型情報としては出てこないのです。

もう少し具体的に書きますね。

COMのインターフェースは多重継承できない規約なので、オブジェクトの型の定義に出てこないインターフェースは定義されていません。

例えば、先に出ていた coclass WebBrowser2 のプライマリインターフェース IWebBrowser2 は
IWebBrowserApp から派生しています。
IWebBrowserApp は、IWebBrowser から派生しており、それは IDispatch から派生しています。

coclass WebBrowser2 は、source 属性を持つインターフェースを持っているため、
イベントを接続することができます。
汎用のイベント接続は、COMの規約でIConnectionPointContainer を使う決まりなので
インターフェースを定義していなくても、このインターフェースを取得することができるということがわかります。

というように、間接的に定義してあるだけという場合もあります。

さらに言えば、coclass は CoCreateInstance をする場合には必要ですが、それ以外では必要ないので
用意しなくても問題ありません。

そのため、そこで作られるオブジェクトがイベント接続できるか?できるとしたらどういうイベントを
定義すればいいのか?という情報は、タイプライブラリには出てきません。

定義がなくてもプログラムは作れますからね。

これらの、定義がなくてもいい部分で必要な情報は、
世間一般でいうところのプログラマーズマニュアルあるいはリファレンスと呼ばれる自然言語で提供するしかないのです。

ですので、決して必要がないというわけではありません。
提供手段がないので異なる方法で提供しているだけです。


>>これらにアクセスせず、なおかつ実際のオブジェクトもない状態で、そのオブジェクトにイベントを接続できるかを
>>調査する方法があるか?という問いに対しては「そんなものはこの地球上のどこにも存在しない」という冷たい答えを返しておくことにします。
>
> ITypeInfo があくまでもタイプライブラリの情報を元にしているものなのであれば、これは当然の事となりますね。
> どうしても知りたければ何とか COM オブジェクトを取得して、QueryInterface するしかないということですね。
> もやっとしていたものが、すっきり致しました。
>

異なる提供方法がない場合は、オブジェクトを作ってQueryInterfaceするしかありません。
ですが、この場合イベントをつなぐことはできるとわかってもそれがどんなイベントか?についてはわかりません。

Advise 接続するオブジェクトのIIDは、Queryできませんからね。

なので、どこかに必ずそれを知らせる情報があるはずです。
それがなにか?については、実装詳細がわからないので何とも言えませんが。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -