none
IViewObjectの使い方 RRS feed

  • 質問

  • WinFormアプリにWebBrowserコントロールを配置し、表示中のHTMLコンテンツに含まれるFlashの内容を画像保存しようとしています。

    DOMを辿ってFlashのオブジェクトを取得し、OleDraw()を使ってHDCに描画したところ真っ黒な画像しか得られませんでした。ドキュメントには内部でIViewObject::Draw()を呼び出すとあり引数も書かれていますので、直接、IViewObject::Draw()を呼び出してみたところ、意図通りの画像が得られました。
    # ということはOleDraw()のバグでしょうか。

    今度はそのプログラムをx86ターゲットでビルドし、WOW64で動作させたところ、また真っ黒な画像しか得られませんでした。
    確認したOSはWindows 8.1 Preview / Windows 8 / Windows 7 SP1辺りでダメでした。32bit版はまだ確認できていません。

    そこで質問としては、WOW64上でIViewObject::Draw()が動作しない原因等わかりますでしょうか?

    参考までに、次のような定義をしています。64bitでは正常動作している以上、間違ってはいないと思います。

    [DllImport("ole32.dll")]
    static extern int OleDraw([MarshalAs(UnmanagedType.IUnknown)]object pUnk, int dwAspect, IntPtr hdcDraw, RECT lprcBounds);
    
    [ComImport(), Guid("0000010d-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IViewObject
    {
      [PreserveSig]
      int Draw([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] object ptd,
        IntPtr hdcTargetDev, IntPtr hdcDraw, [In] RECT lprcBounds, [In] RECT lprcWBounds, IntPtr pfnContinue, [In] ulong dwContinue);
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public class RECT
    {
      public int left;
      public int top;
      public int with;
      public int height;
    }
    2013年9月3日 7:55

回答

  • 32bit版Win7で、http://www.onsen.ag/に対してあれこれした見た結果、第四引数ptdにnullを渡すと失敗します。

    [追記] 失敗じゃないですね、成功が返るが描画されない、です。[/追記]

    LayoutKind.Sequentialなclass DVTARGETDEVICEを定義して、tdSizeに0を設定してやるとうまく取れるようになりました。tdSize含む各メンバに指定する値の検証はパス。

    引数指定は(1, -1, info, dev, IntPtr.Zero, dc, rect, null, IntPtr.Zero, IntPtr.Zero)な感じ。

    Flashは11.8.800.94。


    • 編集済み Hongliang 2013年9月3日 10:16 表現修正のために追記
    • 回答としてマーク 佐祐理 2013年9月4日 0:16
    2013年9月3日 10:15

すべての返信

  • 初見で見つけた問題点としては、IViewObject::Drawの最後の引数はULONG_PTRなのでIntPtrで宣言しないといけないってところですね。64bitだとサイズちょうどなのでうまくいくでしょうけど。

    // あとptdはobjectじゃなくてIntPtrか具体的なclass型にした方が良いような。多分問題にはならないと思いますが。

    2013年9月3日 8:06
  • ありがとうございます。IViewObjectは正直言うとSystem.Windows.Forms.UnsafeNativeMethodsで最後の引数はC#のintになっています。safeint.hを見たらC++で「typedef unsigned __int64 ULONG_PTR」とあったので64bit値かと勘違いしてC#のulongと訂正していました。
    これに関しては、最後の引数でなおかつ未使用なので int / IntPtr / ulong いずれであっても64bitで動作しWOW64で動作しないという結果に影響ありませんでした。

    またもう1点指摘されているptdですが、objectをIntptrに変更しIntPtr.Zeroを引数にしたところ64bitでも動作しなくなりました。nullを渡すだけなのですが、具体的なclass型にすべきでしょうか?

    2013年9月3日 8:26
  • 門外漢なので、単なる杞憂かもしれませんが、
    一般論として、描画の転写で失敗するケースでは

    「HDCのモードが一致していない」
    「ウインドウの論理座標が一致していない」

    というのが多いようです。

    1.ウインドウエクステント
    2.ビューポートエクステント
    3.HDCのマッピングモード

    などを揃える手段が用意されているのであれば、
    試してみる価値があるかもしれません。

    2013年9月3日 9:16
  • 32bit版Win7で、http://www.onsen.ag/に対してあれこれした見た結果、第四引数ptdにnullを渡すと失敗します。

    [追記] 失敗じゃないですね、成功が返るが描画されない、です。[/追記]

    LayoutKind.Sequentialなclass DVTARGETDEVICEを定義して、tdSizeに0を設定してやるとうまく取れるようになりました。tdSize含む各メンバに指定する値の検証はパス。

    引数指定は(1, -1, info, dev, IntPtr.Zero, dc, rect, null, IntPtr.Zero, IntPtr.Zero)な感じ。

    Flashは11.8.800.94。


    • 編集済み Hongliang 2013年9月3日 10:16 表現修正のために追記
    • 回答としてマーク 佐祐理 2013年9月4日 0:16
    2013年9月3日 10:15
  • ありがとうございます。1環境で動作を確認できましたので、残りの環境でも試してみたいと思います。

    仲澤@失業者さんが書かれているようにいろいろなパラメータを一致させる必要があるという記述を見つけたものの指定してもしなくても64bitで動作しWOW64で動作しない状況は変わらないため、ひとまず外していました。無駄なクラスとかDllImportとか乱立させたくないですし…。
    # .NET 4を使えよという話もありますが、それでもDllImportはなくならない。

    しかしどうしてこのような差になったのか、それとOleDraw、気になるところです。

    追記:
    各環境で動作することが確認できました。
    • 編集済み 佐祐理 2013年9月4日 0:59 動作結果の報告
    2013年9月3日 10:38