none
プリンタドライバでLineToなどのDraw系DDI関数をフックする方法 RRS feed

  • 質問

  • はじめましてジャガビーと申します。

    現在以下の環境にてプリンタドライバを試作しています。

    OS:Windows7 x64

    WinDDK7.1.0(7600.16385.1)

    print/oemdllフォルダ配下のサンプルにある「oemuni」をもとに

    DDI関数(DrvXXXX)をフックして、情報を得たいと考えています。

    Draw系については、DrvTextOutとDrvBitBltはフックできているのですが、

    他のDrvLineToやDrvFillPathなどが渡ってきません。

    こちらはGPDファイルの設定もしくはEnableDriver、EnablePDEV側での

    設定が必要なのでしょうか?

    他に必要な情報がありましたら、ご指摘ください。

    お願いいたします。

    • 移動 Mike Wang (MSCS) 2012年10月2日 13:03 (移動元:Windows デバイスドライバー開発)
    2012年9月14日 16:18

回答

  • > NT EMFはマイクロソフト社から仕様が公開されていましたので、それをもとにファイルの内容を解析しました。
    こんな情報が公開されていたんですね。
    全く知りませんでした、ご教授いただきありがとうございます。
    (もっとも、プリンタ関連の仕事から離れてかなりの年月がたっており、今後もそっちの仕事に携わる予定はないのですが。)


    > 確かにドライバ側の描画能力が影響しているのだと思います。他のサンプルソースも参考にして色々調べたのですが、
    > DrvEnableSurface内でEngAssociateSurfaceを呼び出している部分がありました。(参照ソースファイル:msplot/plotter/enable.c)
    > http://msdn.microsoft.com/en-us/library/windows/hardware/ff564183(v=vs.85).aspx
    > この部分でフックできる(描画処理能力)を定義するのかなと思いました。
    MSPlot と OEMUni ドライバでは、その性質が異なるので、OEMUni をベースに開発するのであれば、MSPlot はあまり参考にならないような気がします。
    OEMUni はマイクロソフトが提供する UniDrv とセットで動作する Uni-Mini 形式のプリンタ ドライバですが、MSPlot は UniDrv を必要としないモノシリック ドライバです。
    この二者では扱う Surface が異なっていて、Uni-Mini 形式プリンタ ドライバでは GDI-managed Suface を使用しますが、MSPlot では Device-managed Surface を使用するはずだったと記憶しています。
    Uni-Mini 形式プリンタ ドライバでも Device-managed Surface を使用することが可能なのかはよく分かりませんが、仮に可能であった場合、UniDrv に肩代わりしてもらっているほぼすべての機能を Mini Driver (OEMUni) 側で自力で処理する必要が生じてくると思うので、個人的にはそのような実装にするメリットはない。。。と思っています。
    (要するに、Device-managed Surface として扱いたいのであれば、Uni-Mini 形式ではなく、はじめから MSPlot のようにモノリシック ドライバとして開発したほうが良い。。。と思います。)
    ちなみに、プリンタ ドライバが扱う Surface については下記サイト、あるいは WDK ヘルプに詳細が記載されていますので、そちらをご参照ください。
    (既に参照済みでしたらご容赦ください。)

    --------------------------------------
    Surface Types
    http://msdn.microsoft.com/en-us/library/windows/hardware/ff569900(v=vs.85).aspx

    Handling Device-Managed Surfaces
    http://msdn.microsoft.com/en-us/library/windows/hardware/ff550608(v=vs.85).aspx
    --------------------------------------


    > 今回の要件としましては、既存のアプリ(Office製品、PDF、ブラウザなどなど)からの印刷に対して、
    > ドライバ側で描画命令をハンドリングすることを目的としていました。
    「ドライバ側で描画命令をハンドリングすること」が何を意味するのかよく分かりませんが。。。
    基本的には、モノリシック ドライバで実現できて、Uni-Mini 形式では実現できない。。。ということは無いと思います。
    もちろん Mini Driver (Rendering Plug-in) の開発も簡単ではないと思いますが、MSPlot などのモノリシック ドライバの開発に比べれば、はるかに楽だと思います。
    当初の質問にあった、「DrvLineTo や DrvFillPath (Rendering  Plug-in の場合: OEMLineTo や OEMFillPath) のフック」に関してのみ言及すれば、現状の OEMUni でもフックはできていると思います。
    それら描画ルーチンが呼び出されないのは、単に、それら描画命令がビットマップとして処理されているため。。。ということではないかと思っています。
    DrvLineTo や DrvFillPath (OEMLineTo や OEMFillPath) はプリンタ ドライバの機能として必須ではなくオプション扱いなので、それら描画ルーチンが呼びされなくても、ぜんぜん不思議なことではない。。。と思います。


    > OSやバージョンによって描画命令は異なるのですね。
    グラフィックス エンジンの中枢である win32k.sys 等のバージョンにも依存するのかも知れませんが、OS のバージョンといよりは、アプリケーションの実装方法に依存するところが大きいと思います。


    以上、参考になりましたら幸いに存じます。

    2012年9月20日 5:07

すべての返信

  • oemuni Rendering Plug-in における EnableDriver ルーチンでの実装を見る限り、OEMFillPath や OEMLineTo 等の DDI Hook Routine はちゃんと定義されているので、このサンプルと通りに実装できているのであれば、ちゃんとフックできると思うのですが。。。

    ちなみに、Rendering Plug-in の OEMFillPath や OEMLineTo ルーチンの呼び出しは、アプリ側が FillPath() や LineTo() などの Win32 API コールを行っていることが前提となると思うのですが、動作確認に使用したアプリでも、これら Win32 API をコールしていることは既に確認済みなんですよね?

    2012年9月18日 4:41
  • お馬鹿さん、レスありがとうございます。

    動作確認に使用したアプリはWordです。簡単な矩形や罫線を引いた1ページものを印刷した動作確認です。

    WordがWindows Spoolを介してNT EMFをスプールファイルに落として、これをプリンタプロセッサが解釈して、ドライバ側に渡ってくると認識しています。

    すみません。WordがFillPath()やLineTo()をコールしているかは分かりません。

    NT EMFを少し解析したところ、EMFのコマンドでは、LineToが呼ばれています。これはそのままOEMLineToには置き換わらない?プロセッサの解釈による?

    OEMFillPathやOEMLinToはアプリ側が明示的に呼び出さないとフックできないものでしょうか。

    2012年9月19日 6:05
  • > WordがWindows Spoolを介してNT EMFをスプールファイルに落として、これをプリンタプロセッサが解釈して、ドライバ側に渡ってくると認識しています。
    プリンタ プロパティの [詳細設定] → [プリント プロセッサ] で表示されるダイアログで、[プリント プロセッサ] に "WinPrint"、 [既定のデータ型] に "NT EMF 1.00x" が設定されているのであれば、その認識で正しいと思います。


    > NT EMFを少し解析したところ、EMFのコマンドでは、LineToが呼ばれています。これはそのままOEMLineToには置き換わらない?プロセッサの解釈による?
    > OEMFillPathやOEMLinToはアプリ側が明示的に呼び出さないとフックできないものでしょうか。
    どのような解析方法で、「EMFのコマンドでは、LineToが呼ばれている」ことを確認されたのか、よく分からないのですが。。。

    プリンタ ドライバのどのルーチンが呼び出されて描画が行われるかは、"アプリケーション側が使用する描画関数" と "プリンタ ドライバが定義するデバイスの描画能力" に依存して決定されるんだと思います。
    つまり、アプリが要求した描画命令に対して、GDI エンジンがターゲット デバイスの描画能力を加味して、最終的にプリンタ ドライバのどの描画機能を利用するかを決定している。。。というのが私の認識です。
    ですので、アプリがこの描画関数をコールしたから、必ずプリンタ ドライバ側のこの関数が呼び出される。。。という単純な話ではないのだと思います。
    たとえば、プリンタ ドライバ側でサポートするバンド幅を変えただけでも、プリンタ ドライバへの描画命令は劇的に変化すると思いますし、ウィンドウ上では単なる線の描画に見えても、アプリ側がその描画オブジェクトを "Line" ではなく "Bitmap" としてハンドリングしているのであれば、プリンタ ドライバに対してライン描画命令として渡されてくることはないと思います。
    今回の場合も、OEMFillPath や OEMLinTo がフックできていないのではなく、単に 「GDI エンジンがプリンタ ドライバの DrvBitBlt (OEMBitBlt) で描画するのが望ましい」と判断したためだと思います。
    このような理由から、ご自身で作成されたプリンタ ドライバの各フック ルーチンが適切に呼び出されているかのチェックを、既存のアプリで判断するのは難しいと思います。
    (ご自身で FillPath や LineTo Win32 API を使用したテスト アプリを作成するのが一番手っ取り早いと思います。)

    ちなみに、プリンタ ドライバの DrvLineTo (OEMLineTo) が LineTo Win32 API から呼び出されることは、下記サイトに情報がありますので、ご確認ください。

    -----------------------------------
    DrvLineTo function
    http://msdn.microsoft.com/en-us/library/windows/hardware/ff556245(v=vs.85).aspx

    Remarks
    DrvLineTo is an optional entry point that a driver can supply as an optimization for application calls to the Win32 LineTo function.
    If the driver doesn't hook DrvLineTo, or if the driver returns FALSE from a call to this function, GDI will automatically call DrvStrokePath instead.
    A driver that has hooked DrvLineTo can call EngLineTo when the rendering surface is a DIB.
    -----------------------------------

    P.S.
    余談ですが。。。
    oemuni.dll ドライバを使用して、Win XP SP3 + Word 2007 の環境で直線を一本引いて、その描画処理をデバッグしてみましたが、Word プロセスでは LineTo() では描画していませんでした。
    Word のバージョンあるいは操作手順の違いが影響しているのかも知れないので、断言は出来ませんけど。。。
    私の環境では、Word プロセスは Polyline() API で直線を描画させ、EMF データのデスプーリング処理で、GDI エンジンはその直線のビットマップを作成し、最終的に UniDrv ドライバの DrvBitBlt から oemuni ドライバの OEMBitBlt で描画されているようでした。

    2012年9月19日 10:38
  • 詳細なご説明ありがとうございます。

    NT EMFはマイクロソフト社から仕様が公開されていましたので、それをもとにファイルの内容を解析しました。

    http://msdn.microsoft.com/en-us/library/cc231034(v=prot.13).aspx

    確かにドライバ側の描画能力が影響しているのだと思います。他のサンプルソースも参考にして色々調べたのですが、

    DrvEnableSurface内でEngAssociateSurfaceを呼び出している部分がありました。(参照ソースファイル:msplot/plotter/enable.c)

    http://msdn.microsoft.com/en-us/library/windows/hardware/ff564183(v=vs.85).aspx

    この部分でフックできる(描画処理能力)を定義するのかなと思いました。

    この点は当たってますでしょうか・・・?

    ちなみにoemuniドライバで変更することは可能でしょうか。IPrintOemUni2インタフェースではメソッドが提供されていなかったので。

    今回の要件としましては、既存のアプリ(Office製品、PDF、ブラウザなどなど)からの印刷に対して、

    ドライバ側で描画命令をハンドリングすることを目的としていました。

    oemuniドライバを使用すれば比較的簡単に行えると思ったのですが。。。。甘かったですね。

    OSやバージョンによって描画命令は異なるのですね。これはとても参考になりました。ありがとうございます。

    2012年9月19日 11:14
  • > NT EMFはマイクロソフト社から仕様が公開されていましたので、それをもとにファイルの内容を解析しました。
    こんな情報が公開されていたんですね。
    全く知りませんでした、ご教授いただきありがとうございます。
    (もっとも、プリンタ関連の仕事から離れてかなりの年月がたっており、今後もそっちの仕事に携わる予定はないのですが。)


    > 確かにドライバ側の描画能力が影響しているのだと思います。他のサンプルソースも参考にして色々調べたのですが、
    > DrvEnableSurface内でEngAssociateSurfaceを呼び出している部分がありました。(参照ソースファイル:msplot/plotter/enable.c)
    > http://msdn.microsoft.com/en-us/library/windows/hardware/ff564183(v=vs.85).aspx
    > この部分でフックできる(描画処理能力)を定義するのかなと思いました。
    MSPlot と OEMUni ドライバでは、その性質が異なるので、OEMUni をベースに開発するのであれば、MSPlot はあまり参考にならないような気がします。
    OEMUni はマイクロソフトが提供する UniDrv とセットで動作する Uni-Mini 形式のプリンタ ドライバですが、MSPlot は UniDrv を必要としないモノシリック ドライバです。
    この二者では扱う Surface が異なっていて、Uni-Mini 形式プリンタ ドライバでは GDI-managed Suface を使用しますが、MSPlot では Device-managed Surface を使用するはずだったと記憶しています。
    Uni-Mini 形式プリンタ ドライバでも Device-managed Surface を使用することが可能なのかはよく分かりませんが、仮に可能であった場合、UniDrv に肩代わりしてもらっているほぼすべての機能を Mini Driver (OEMUni) 側で自力で処理する必要が生じてくると思うので、個人的にはそのような実装にするメリットはない。。。と思っています。
    (要するに、Device-managed Surface として扱いたいのであれば、Uni-Mini 形式ではなく、はじめから MSPlot のようにモノリシック ドライバとして開発したほうが良い。。。と思います。)
    ちなみに、プリンタ ドライバが扱う Surface については下記サイト、あるいは WDK ヘルプに詳細が記載されていますので、そちらをご参照ください。
    (既に参照済みでしたらご容赦ください。)

    --------------------------------------
    Surface Types
    http://msdn.microsoft.com/en-us/library/windows/hardware/ff569900(v=vs.85).aspx

    Handling Device-Managed Surfaces
    http://msdn.microsoft.com/en-us/library/windows/hardware/ff550608(v=vs.85).aspx
    --------------------------------------


    > 今回の要件としましては、既存のアプリ(Office製品、PDF、ブラウザなどなど)からの印刷に対して、
    > ドライバ側で描画命令をハンドリングすることを目的としていました。
    「ドライバ側で描画命令をハンドリングすること」が何を意味するのかよく分かりませんが。。。
    基本的には、モノリシック ドライバで実現できて、Uni-Mini 形式では実現できない。。。ということは無いと思います。
    もちろん Mini Driver (Rendering Plug-in) の開発も簡単ではないと思いますが、MSPlot などのモノリシック ドライバの開発に比べれば、はるかに楽だと思います。
    当初の質問にあった、「DrvLineTo や DrvFillPath (Rendering  Plug-in の場合: OEMLineTo や OEMFillPath) のフック」に関してのみ言及すれば、現状の OEMUni でもフックはできていると思います。
    それら描画ルーチンが呼び出されないのは、単に、それら描画命令がビットマップとして処理されているため。。。ということではないかと思っています。
    DrvLineTo や DrvFillPath (OEMLineTo や OEMFillPath) はプリンタ ドライバの機能として必須ではなくオプション扱いなので、それら描画ルーチンが呼びされなくても、ぜんぜん不思議なことではない。。。と思います。


    > OSやバージョンによって描画命令は異なるのですね。
    グラフィックス エンジンの中枢である win32k.sys 等のバージョンにも依存するのかも知れませんが、OS のバージョンといよりは、アプリケーションの実装方法に依存するところが大きいと思います。


    以上、参考になりましたら幸いに存じます。

    2012年9月20日 5:07