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

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

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

Re[3]: void * を取るDLL呼び出し時のマーシャリング


(過去ログ 30 を表示中)

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

■14125 / inTopicNo.1)  void * を取るDLL呼び出し時のマーシャリング
  
□投稿者/ ぜぜ (1回)-(2008/02/09(Sat) 16:08:30)

分類:[C#] 

開発環境 VisualStudio2005/Windows Vista
使用言語 C#

C#からwmvcore.dllのCOMインターフェースの呼び出しをしようとしているのですが、
マーシャリングが正しくできていないようで、例外が発生してしまいます。
「void *」を引数に取るため、その部分が問題になっているのではないかと
考え、MSDNの
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/cpguide/html/cpconVoidSample.asp
にあるような方法を試しているのですが、うまく行きません。
同様の経験のある方や、識者の方教えていただけますでしょうか?

以下のようなコードを書いています。

・呼び出そうとしているCOMのインターフェース ⇒ IWMIndexer2
・呼び出しで例外が上がるメソッドのC++のコード(Windows Media Format SDK)

HRESULT Configure(
  WORD  wStreamNum,
  WMT_INDEXER_TYPE  nIndexerType,
  void*  pvInterval,
  void*  pvIndexType
);

なお、Configureの注意書きとしてpvIntervalはDWORDのポインタを渡し、
pvIndexTypeはWORDのポインタを渡すとあります。

---------------- C# のコード --------------------
public class WMFWrapIndexer
{
    [DllImport("wmvcore.dll",
       EntryPoint = "WMCreateIndexer",
       SetLastError=true,
       CharSet=CharSet.Unicode,
       ExactSpelling=true,
       CallingConvention=CallingConvention.StdCall)]
        public static extern uint WMCreateIndexer(
            [Out, MarshalAs(UnmanagedType.Interface)]	out IWMIndexer ppMetadataEditor);

        public WMFWrapIndexer()
        {
        }
    }

    [Guid("6d7cdc71-9888-11d3-8edc-00c04f6109cf"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IWMIndexer
    {
        uint Cancel();
        uint StartIndexing(
            [In, MarshalAs(UnmanagedType.LPWStr)]   string pwszFilename,
            [In]                                    IntPtr pCallback,
            [In]                                    IntPtr pvContext);
    }

    [Guid("b70f1e42-6255-4df0-a6b9-02b212d9e2bb"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IWMIndexer2
    {
        uint Configure(
           [In]                                    ushort wStreamNum,
           [In]                                    WMT_INDEXER_TYPE nIndexerType,
           [In, MarshalAs(UnmanagedType.AsAny)]    Object pvInterval,
           [In, MarshalAs(UnmanagedType.AsAny)]    Object pvIndexType);
    }

    public enum WMT_INDEXER_TYPE
    {
        WMT_IT_PRESENTATION_TIME = 0,
        WMT_IT_FRAME_NUMBERS = 1,
        WMT_IT_TIMECODE = 2
    }
============ 実際の呼び出し =========================

IWMIndexer m_wmIndexer;
IWMIndexer2 m_wmIndexer2;
uint hr;

try {
    hr = WMFWrapIndexer.WMCreateIndexer(out m_wmIndexer);
    if (hr != 0) {
        return -1;
    }

    m_wmIndexer2 = (IWMIndexer2)m_wmIndexer;
                
    uint ui_interval = 3000;
    ushort us_index = 1;

    hr = m_wmIndexer2.Configure(0, WMT_INDEXER_TYPE.WMT_IT_PRESENTATION_TIME, ui_interval, us_index);  <==== ここでArgmentExceptionが発生(戻り値は0)

} catch (Exception e) {
    return -1;
} finally {
}

============ ここまで =========================

Configureの引数を全部0にするなども試してみたのですが、
やはりArgmentExceptionが発生してしまいます。
(ref uint/ref ushortを使う方法も同様でした。)

よろしくお願いします。

引用返信 編集キー/
■14126 / inTopicNo.2)  Re[1]: void * を取るDLL呼び出し時のマーシャリング
□投稿者/ Hongliang (238回)-(2008/02/09(Sat) 17:34:41)
Hongliang さんの Web サイト
IUnknown から派生するインターフェイスを再定義する場合、メソッドの宣言順が重要になります。呼び出すメソッドの特定は宣言順によるからです。SDK などを入れて該当インターフェイスの Vtbl をチェックするのが多分確実でしょう(Vtbl は難儀な記述ですがメソッドの宣言順を見るだけならどってことありません)。
また COM インターフェイスが継承される場合、派生元のインターフェイスのメソッドも書かねばなりません。.NET でのインターフェイスの再定義では、マネージインターフェイスの継承は無視されます。必ず一つのインターフェイス内に全てのメンバを宣言しなければなりません。

IWMIndexer2 は IWMIndexer から派生するため、IWMIndexer のメンバも全て含む必要があります。
IWMIndexer が持つメンバは IUnknown が持つのを除き(IUnknown が持つメンバは .NET が暗黙に用意してくれます)、StartIndexing と Cancel があります。これらはこの順で定義されているので、再宣言もこの順で行います。Configure はその後です。
つまり IWMIndexer2 は 3 つのメソッドを持たす必要があるわけですね。それも StartIndexing、Cancel、Configure の順で。
ちなみに、IWMIndexer を再定義して StartIndexing と Cancel を持たせるのは問題ないですが、IWMIndexer2 を IWMIndexer から派生させてもほとんど意味はありません。

AsAny で Object はいけません。ドキュメントに
> プラットフォーム呼び出しのメソッドでだけ有効です。
とあるので、COM 呼び出しでは使えません。
メソッドの解説を調べて何を渡すのか具体的に決めるのが基本です。たとえば pvInterval は解説によると
> point to a DWORD
となっているので、ref int 辺りが妥当でしょう。ただ、ref/out だと null を渡せないので、代わりに IntPtr を使う場合もあるかもしれません(この場合、今度は null 以外を渡す場合に Marshal.AllocCoTaskMem でメモリ確保して、呼び出し終わったら Marshal.FreeCoTaskMem で解放してって手間がかかるので面倒ですが。いっそ unsafe にして int* とか言う手も無きにしも非ず)。
引用返信 編集キー/
■14132 / inTopicNo.3)  Re[2]: void * を取るDLL呼び出し時のマーシャリング
□投稿者/ ぜぜ (3回)-(2008/02/09(Sat) 22:54:38)
Hongliang さんありがとうございます。

> IUnknown から派生するインターフェイスを再定義する場合、メソッドの宣言順が重要になります。呼び出すメソッドの特定は宣言順によるからです。SDK などを入れて該当インターフェイスの Vtbl をチェックするのが多分確実でしょう(Vtbl は難儀な記述ですがメソッドの宣言順を見るだけならどってことありません)。
> また COM インターフェイスが継承される場合、派生元のインターフェイスのメソッドも書かねばなりません。.NET でのインターフェイスの再定義では、マネージインターフェイスの継承は無視されます。必ず一つのインターフェイス内に全てのメンバを宣言しなければなりません。
お恥ずかしながら、この辺りについては全然理解していませんでした。。。
(COMについては、IDLなどを書いてVCで作成してVC/VBで使える・・・といった程度ぐらいしかできません。)
VTBLを調べようと思ったのですが、これはどのようにして調べるものなのでしょうか?

> IWMIndexer が持つメンバは・・・
この辺りは最初の段落の具体例だったので、とりあえず理解できました。


> AsAny で Object はいけません。ドキュメントに
>>プラットフォーム呼び出しのメソッドでだけ有効です。
> とあるので、COM 呼び出しでは使えません。
見直しました。確かに「プラットフォーム呼び出し」と書いてありました。

> メソッドの解説を調べて何を渡すのか具体的に決めるのが基本です。たとえば pvInterval は解説によると
>>point to a DWORD
> となっているので、ref int 辺りが妥当でしょう。ただ、ref/out だと null を渡せないので、代わりに IntPtr を使う場合もあるかもしれません(この場合、今度は null 以外を渡す場合に Marshal.AllocCoTaskMem でメモリ確保して、呼び出し終わったら Marshal.FreeCoTaskMem で解放してって手間がかかるので面倒ですが。いっそ unsafe にして int* とか言う手も無きにしも非ず)。

なるほど。。。nullを渡す場合のことは全く考えていませんでした。
unsafeにしてint*を使うという手はあれこれと試しているときに一瞬考えましたが、
無理やりな感じがしたので試していませんでした。
それ以前のVtblの辺りが分かっていなかったので、結局NGだったと思いますが。。。

引用返信 編集キー/
■14133 / inTopicNo.4)  Re[3]: void * を取るDLL呼び出し時のマーシャリング
□投稿者/ Azulean (22回)-(2008/02/09(Sat) 23:13:23)
> VTBLを調べようと思ったのですが、これはどのようにして調べるものなのでしょうか?
VTBLを見ると言うよりは、SDKのヘッダーファイルを見るようなイメージですね。
そこに書かれているメソッドの順番に書かないと、正しく呼び出されません。

Todo
・メソッドの順番を確認して修正する
・継承関係を設定する
・マーシャリングを適切に設定する
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -