none
IRP_MJ_CLOSEハンドラでの情報取得について RRS feed

  • 質問

  • お世話になります。

    flgMgr.sysを使用した
    フィルタードライバを作成しています。

    自作のフィルタドライバを
    ずっと起動させていると、
    落ちる現象がありまして、
    Windbgでダンプを参照すると、
    どうやら
    FltIsDirectory
    で落ちているようです

    以下の
    // ★★★
    のところです。

    この関数は
    IRP_MJ_CLOSE
    のハンドラになっているのですが
    渡す引数は
    Data->Iopb->TargetFileObject
    ではだめなのですかね?
    (ダメだとしてもすぐ落ちないのは気になる。。)
    fltInstanceは、
    FltGetVolumeFromInstance関数が
    成功のようなので問題ないと思うのですが。

    以上、よろしくお願いします。

    /****************************************************************************
     * ファイルクローズのハンドラ
     *---------------------------------------------------------------------------
     * [RET] ハンドラの結果
     */
    FLT_PREOP_CALLBACK_STATUS SpyPreCloseOperationCallback (
      __inout PFLT_CALLBACK_DATA Data,
      __in PCFLT_RELATED_OBJECTS FltObjects,
      __deref_out_opt PVOID *CompletionContext
      )
    {
      FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
      PFLT_VOLUME    fltVolume;
      UNICODE_STRING fltVolumeName;
      WCHAR          fltVolumeTemp[TEMP_MAX_PATH] = {0};
      UNICODE_STRING fileName;
      WCHAR          fileNameTemp[TEMP_MAX_PATH] = {0};
      LONG           fltVolumeSize = 0;
      PFLT_INSTANCE  fltInstance;
      NTSTATUS       status;
      PFILE_OBJECT   pFileObject;
      BOOLEAN        isDirectory;
      fltInstance = Data->Iopb->TargetInstance;
    
      // インスタンスからボリューム情報取得
      status = FltGetVolumeFromInstance(fltInstance, &fltVolume);
      if (status != S_OK)
      {
        DBG_PRINT("Error(%s,%d):Failed FltGetVolumeFromInstance, 0x%08X\n", __FILE__, __LINE__, status);
      }
    
      // UNICODE_STRING初期化
      RtlInitEmptyUnicodeString(&fltVolumeName, fltVolumeTemp, TEMP_MAX_PATH);
      RtlInitEmptyUnicodeString(&fileName, fileNameTemp, TEMP_MAX_PATH);
    
      // ボリューム名取得
      status = FltGetVolumeName(fltVolume, &fltVolumeName, NULL);
      if (status != S_OK)
      {
        DBG_PRINT("Error(%s,%d):Failed FltGetVolumeName, 0x%08X\n", __FILE__, __LINE__, status);
      }
      pFileObject = Data->Iopb->TargetFileObject;
      isDirectory = FALSE;
    
      // ★★★ ここでどうやら落ちる。。
      status = FltIsDirectory(pFileObject, fltInstance, &isDirectory);
      if (status != S_OK && status != STATUS_NOT_SUPPORTED )
      {
        // エラーの場合は無視
        DBG_PRINT("Error(%s,%d):Failed FltIsDirectory, 0x%08X\n", __FILE__, __LINE__, status);
        return returnStatus;
      }
    
      if ( isDirectory )
      {
        // ディレクトリの場合は無視
        return returnStatus;
      }
    
      // ファイル名作成
      status = RtlUnicodeStringCopy(&fileName, &fltVolumeName);
      if (status != S_OK)
      {
        DBG_PRINT("Error(%s,%d):Failed RtlUnicodeStringCopy, 0x%08X\n", __FILE__, __LINE__, status);
      }
    
      status = RtlUnicodeStringCat(&fileName, &pFileObject->FileName);
      if (status != S_OK)
      {
        DBG_PRINT("Error(%s,%d):Failed RtlUnicodeStringCat, 0x%08X\n", __FILE__, __LINE__, status);
      }
    
      DBG_PRINT("CheckClose (%s,%d):%08p %ws\n", __FILE__, __LINE__, FltObjects->FileObject, fileNameTemp);
      return returnStatus;
    }
    

    • 編集済み phoenix343 2010年1月9日 1:59 ソースコードのフォントを等幅に変更
    • 移動 Mike Wang (MSCS) 2012年10月2日 12:24 (移動元:Windows デバイスドライバー開発)
    2010年1月8日 4:17

回答

  • phoenix343 さん、再びこんにちは。

    ファイル システム フィルタ ドライバが IRP_MJ_CLOSE を受け取ったタイミングで、
    削除対象となっている File Object にアクセスするのは、やってはいけない実装だと思います。
    なぜなら、IRP_MJ_CLOSE を受け取った時点において、対象となる File Object の参照カウンタと
    オープン ハンドル カウンタが既に 0 となっている状態、つまり破棄される直前だからです。

    これらのカウンタは、IRP_MJ_CREATE によってインクリメントされていき、IRP_MJ_CLEANUP が呼び出されるごとに
    デクリメントされて、最終的にこれらカウンタの値が0になった時に、オブジェクト マネージャや I/O マネージャを経由して
    IRP_MJ_CLOSE が発行され、その File Object が破棄される。。。という流れだったと思います。

    (ただしファイル システム フィルタ ドライバ側から見た場合、常に IRP_MJ_CREATE と IRP_MJ_CLEANUP の数が
      一致するわけではありません。 この点に関しては、WDK ドキュメント "IRP_MJ_CLOSE" トピックに記載されています。)

    つまり、IRP_MJ_CLOSE がファイル システム フィルタ ドライバに発行された時点で、I/O マネージャなどの
    NT カーネルはその File Object の削除を前提に処理を行っている訳で、今回の場合まさに削除しようとしている
    File Object に対して、削除処理中にアクセスしようとしている訳ですから、BSOD になっても不思議ではないと思います。

    「なぜすぐに落ちないのか?」に関しては、I/O マネージャあるいはファイル システム ドライバ側がたまたま運よく
    思いとどまっているだけが、たまたま別のスレッド コンテキストに切り替わっただけで、それ自体に意味はないと思います。
    (少なくとも、正常に処理されている訳では無いと思います。)
    どうしてもその理由を明確にしたいのであれば、WinDbg 等のデバッガで丹念にトレースしていけばわかるのでは?

    ちなみに、参照カウンタやオープン ハンドル カウンタは FILE_OBJECT 構造体 FsContext メンバの先に
    FCB (File Control Block) 構造体として保持されているようですが、FsContext や FCB のデータ フォーマット
    は非公開のようです。
    (FCB は WinDbg を使って、ちゃんとシンボルを合わせれば、ある程度の情報は見えるようです。)

     

    • 回答としてマーク phoenix343 2010年1月9日 0:53
    2010年1月8日 6:50
  • >のFltObjects->FileObject
    >でも同様ですよね?
    はい。
    デバッガで確認すればすぐにわかると思いますが、FLT_RELATED_OBJECTS 構造体 FileObject メンバと
    FLT_IO_PARAMETER_BLOCK 構造体  TargetFileObject メンバは、同じアドレスになっているはずだと思います。
    ですので、同じ結果になると思います。

    >とすると、IRP_MJ_CLOSEがきたときはファイル名を取得することは不可能なんでしょうか?
    そうだと思います。
    (そもそも IRP_MJ_CLOSE で削除対象の File Object にアクセスを行う実装を行ったことはありませんが。。。)

    >渡されたFileObjectが使えないとなると、、、
    >OpenしたときのFileObjectのアドレス値でも覚えておいて、
    >IRP_MJ_CLOSEがきたときにFileObjectのアドレスで検索…かな?
    一般的な実装かどうかはわかりませんが、私の場合、Create IRP が来たタイミングで FILE_OBJECT 構造体 FileName メンバに
    セットされている名前を保持し、ファイル システムでの Create IRP が成功した場合に、FsContext にセットされたアドレスを
    ベースに名前やその他の属性を管理するようにしています。
    FsContext で管理している理由は、FILE_OBJECT だと、同一ファイルに対して複数の FILE_OBJECT が生成されてしまうからです。

    追伸
    下記 Filter Manager Service Function を組み合わせれば、もっとスマートに解決できるかもしれません。

    FltAllocateContext
    http://msdn.microsoft.com/en-us/library/aa488574.aspx

    FltSetStreamContext
    http://msdn.microsoft.com/en-us/library/aa488717.aspx

    FltGetStreamContext
    http://msdn.microsoft.com/en-us/library/aa488667.aspx

    FltDeleteStreamContext
    http://msdn.microsoft.com/en-us/library/aa488562.aspx

    WDK サンプルの ctx ドライバでこれら関数を使用しているので、参考になると思います。

    • 回答としてマーク phoenix343 2010年1月8日 23:59
    2010年1月8日 8:23

すべての返信

  • phoenix343 さん、再びこんにちは。

    ファイル システム フィルタ ドライバが IRP_MJ_CLOSE を受け取ったタイミングで、
    削除対象となっている File Object にアクセスするのは、やってはいけない実装だと思います。
    なぜなら、IRP_MJ_CLOSE を受け取った時点において、対象となる File Object の参照カウンタと
    オープン ハンドル カウンタが既に 0 となっている状態、つまり破棄される直前だからです。

    これらのカウンタは、IRP_MJ_CREATE によってインクリメントされていき、IRP_MJ_CLEANUP が呼び出されるごとに
    デクリメントされて、最終的にこれらカウンタの値が0になった時に、オブジェクト マネージャや I/O マネージャを経由して
    IRP_MJ_CLOSE が発行され、その File Object が破棄される。。。という流れだったと思います。

    (ただしファイル システム フィルタ ドライバ側から見た場合、常に IRP_MJ_CREATE と IRP_MJ_CLEANUP の数が
      一致するわけではありません。 この点に関しては、WDK ドキュメント "IRP_MJ_CLOSE" トピックに記載されています。)

    つまり、IRP_MJ_CLOSE がファイル システム フィルタ ドライバに発行された時点で、I/O マネージャなどの
    NT カーネルはその File Object の削除を前提に処理を行っている訳で、今回の場合まさに削除しようとしている
    File Object に対して、削除処理中にアクセスしようとしている訳ですから、BSOD になっても不思議ではないと思います。

    「なぜすぐに落ちないのか?」に関しては、I/O マネージャあるいはファイル システム ドライバ側がたまたま運よく
    思いとどまっているだけが、たまたま別のスレッド コンテキストに切り替わっただけで、それ自体に意味はないと思います。
    (少なくとも、正常に処理されている訳では無いと思います。)
    どうしてもその理由を明確にしたいのであれば、WinDbg 等のデバッガで丹念にトレースしていけばわかるのでは?

    ちなみに、参照カウンタやオープン ハンドル カウンタは FILE_OBJECT 構造体 FsContext メンバの先に
    FCB (File Control Block) 構造体として保持されているようですが、FsContext や FCB のデータ フォーマット
    は非公開のようです。
    (FCB は WinDbg を使って、ちゃんとシンボルを合わせれば、ある程度の情報は見えるようです。)

     

    • 回答としてマーク phoenix343 2010年1月9日 0:53
    2010年1月8日 6:50
  • かぴるんるんさん、再び返答ありがとうございます。

    ファイル システム フィルタ ドライバが IRP_MJ_CLOSE を受け取ったタイミングで、
    削除対象となっている File Object にアクセスするのは、やってはいけない実装だと思います。

    納得しました。
    2番目に渡される
    __in PCFLT_RELATED_OBJECTS FltObjects
    のFltObjects->FileObject
    でも同様ですよね?

    とすると、IRP_MJ_CLOSEがきたときはファイル名を取得することは不可能なんでしょうか?
    渡されたFileObjectが使えないとなると、、、
    OpenしたときのFileObjectのアドレス値でも覚えておいて、
    IRP_MJ_CLOSEがきたときにFileObjectのアドレスで検索…かな?

    何か知恵があれば教えてください、、
    2010年1月8日 7:14

  • http://msdn.microsoft.com/en-us/library/aa488639.aspx を見ると

    NTSTATUS
      FltIsDirectory(
        __in PFILE_OBJECT  FileObject,
        __in PFLT_INSTANCE  Instance,
        __out PBOOLEAN  IsDirectory
        );

    Parameters

    Instance
    Opaque instance pointer for the instance associated with this file object.
      → このファイルオブジェクトに割り当てられたインスタン用の不定なインスタンスポインター

    と記載されているように見えます。

    Data->Iopb->TargetInstance を渡してみたらどうなりますか???
    2010年1月8日 8:15
  • >のFltObjects->FileObject
    >でも同様ですよね?
    はい。
    デバッガで確認すればすぐにわかると思いますが、FLT_RELATED_OBJECTS 構造体 FileObject メンバと
    FLT_IO_PARAMETER_BLOCK 構造体  TargetFileObject メンバは、同じアドレスになっているはずだと思います。
    ですので、同じ結果になると思います。

    >とすると、IRP_MJ_CLOSEがきたときはファイル名を取得することは不可能なんでしょうか?
    そうだと思います。
    (そもそも IRP_MJ_CLOSE で削除対象の File Object にアクセスを行う実装を行ったことはありませんが。。。)

    >渡されたFileObjectが使えないとなると、、、
    >OpenしたときのFileObjectのアドレス値でも覚えておいて、
    >IRP_MJ_CLOSEがきたときにFileObjectのアドレスで検索…かな?
    一般的な実装かどうかはわかりませんが、私の場合、Create IRP が来たタイミングで FILE_OBJECT 構造体 FileName メンバに
    セットされている名前を保持し、ファイル システムでの Create IRP が成功した場合に、FsContext にセットされたアドレスを
    ベースに名前やその他の属性を管理するようにしています。
    FsContext で管理している理由は、FILE_OBJECT だと、同一ファイルに対して複数の FILE_OBJECT が生成されてしまうからです。

    追伸
    下記 Filter Manager Service Function を組み合わせれば、もっとスマートに解決できるかもしれません。

    FltAllocateContext
    http://msdn.microsoft.com/en-us/library/aa488574.aspx

    FltSetStreamContext
    http://msdn.microsoft.com/en-us/library/aa488717.aspx

    FltGetStreamContext
    http://msdn.microsoft.com/en-us/library/aa488667.aspx

    FltDeleteStreamContext
    http://msdn.microsoft.com/en-us/library/aa488562.aspx

    WDK サンプルの ctx ドライバでこれら関数を使用しているので、参考になると思います。

    • 回答としてマーク phoenix343 2010年1月8日 23:59
    2010年1月8日 8:23
  • mitu6251さん、返信ありがとうございます。

    Data->Iopb->TargetInstance を渡してみたらどうなりますか???

    えっと、、ソース見てますか?直に渡すかの違いだけで、同じですよ。
    2010年1月8日 10:03
  • かぴるんるんさん、
    返信ありがとうございます。

    読み返して気づきましたが
    IRP_MJ_CREATEがくる分、
    IRP_MJ_CLOSEでなく
    IRP_MJ_CLEANUPがくるんですね。

    うーむ、、
    悩ましい。
    IRP_MJ_CLEANUPも見るようにしますかね。
    IRP_MJ_CLOSEは無視しようかな。実装めんどうだし 笑

    (ただしファイル システム フィルタ ドライバ側から見た場合、常に IRP_MJ_CREATE と IRP_MJ_CLEANUP の数が
      一致するわけではありません。 この点に関しては、WDK ドキュメント "IRP_MJ_CLOSE" トピックに記載されています。)

    これはもちろん承知の上で。
    とにかくクローズのログが出ればいいので、、
    2010年1月8日 10:47
  • とりあえず、気になった点をいくつか。。。

    >IRP_MJ_CLEANUPも見るようにしますかね。

    少なくとも、今の SpyPreCloseOperationCallback ルーチンの実装には
    いくつかの問題が潜在ししているように思います。
    ですので、今のままの実装で IRP_MJ_CLEANUP に対応させても、
    目的の機能の実現は難しいと思います。

    まず第1の問題としては、FILE_OBJECT 構造体 FileName メンバを
    参照するタイミングです。
    このメンバにファイル名がセットされていることが保証されるタイミングは、
    IRP_MJ_CREATE が渡されてきたとき、つまりミニフィルタ ドライバの場合、
    PreCreate Operation が呼び出されたときだけです。
    PostCreate Operation を含むそれ以外のタイミングでは、ファイル システム
    ドライバによって別の名前に書き換えたれていたり、あるいは NULL ポインタ
    に置き換わっていたりする場合があるので、そのような場合だと、正しいファイル名
    は表示できません。
    ただ、ミニフィルタ ドライバの場合、
    FltGetFileNameInformation()
    というサービス関数が用意されているので、FILE_OBJECT 構造体 FileName メンバ
    の直接参照ではなく、この関数を使用してファイル名を取得する実装に変更すれば、
    IRP_MJ_CLEANUP のタイミングでも大丈夫かもしれません。
    (私は試したことありませんけど。)

    もう1つの問題は、FltGetVolumeFromInstance() コールに対応する
    FltObjectDereference() がコールが無いことです。
    下記サイトでの説明にもありますが、FltGetVolumeFromInstance() コールで
    取得した FLT_VOLUME 構造体データがいらなくなったら、FltObjectDereference()
    をコールしてあげないといけないと思います。

    FltGetVolumeFromInstance
    http://msdn.microsoft.com/en-us/library/aa488606.aspx


    とりあえず私の方は、ctxドライバのような Stream Context ベースで
    管理する実装の方がはるかにスマートであることに気が付いたので、
    こっちの実装に変更することにしました。

     

    2010年1月10日 5:32
  • かびるんるんさん、再び返信ありがとうございます。。
    ただ、ミニフィルタ ドライバの場合、
    FltGetFileNameInformation()
    というサービス関数が用意されているので、FILE_OBJECT 構造体 FileName メンバ
    の直接参照ではなく、この関数を使用してファイル名を取得する実装に変更すれば、
    IRP_MJ_CLEANUP のタイミングでも大丈夫かもしれません。
    (私は試したことありませんけど。)
    実装を検討してみます。 IRP_MJ_CLEANUPにすることで落ちなくはなりましたが、その方がよいかな。。
    もう1つの問題は、FltGetVolumeFromInstance() コールに対応する
    FltObjectDereference() がコールが無いことです。
    指摘ありがとうございます。
    ここはすでに修正済みです。(一言書いとけば良かったですね…m(__;)m
    2010年1月12日 1:36
  • FltGetFileNameInformation()
    をIRP_MJ_CLEANUPで使用した場合、動作がおかしくなりますね。。

    うーみゅ
    2010年1月12日 8:10
  • 具体的に、どのようにおかしくなるのでしょうか?
    ちゃんとは読んでいないのですが、MiniSpy のコードを眺めた限りでは、Cleanup IRP でも
    FltGetFileNameInformation() は機能しそうに見えたのですが。。。
    2010年1月12日 9:31
  • かびるんるんさん
    再び気にしてくださり有難うございます

    具体的に、どのようにおかしくなるのでしょうか?
    ちゃんとは読んでいないのですが、MiniSpy のコードを眺めた限りでは、Cleanup IRP でも
    FltGetFileNameInformation() は機能しそうに見えたのですが。。。
    落ちたりはしないんですけどね。
    コンパイルや沢山のファイルにアクセスしたりすると、目に見えてパフォーマンスが落ちるんです。
    で、しまいにはエクスプローラを開こうとしても応答なしになるんで…

    FltGetFileNameInformationの処理を外すと問題ないので、
    この関数の中で、何か処理しちゃってそうですね。
    2010年1月12日 13:31
  • ちなみに、FltGetFileNameInformation() で使い終わった後に、
    ちゃんと FltReleaseFileNameInformation()  をコールしてるんですよね?

    2010年1月12日 16:11
  • ちゃんと FltReleaseFileNameInformation() をコールしてるんですよね?
    もちろんですとも。
    2010年1月12日 23:04
  • MiniSpy を使ってちょっと試してみましたけど、私の環境ではちゃんと表示されて、サクサク動きましたよ。
    参考になるかわかりませんが、調査用に追加したコードを以下に示しておきます。

    filter\minispy.c
    ------------------------------------------------------------
    #define PRIVATE_DEBUG_ENABLE  TRUE

    BOOLEAN     g_OutputPrivateLogs = TRUE;

    ....
    ....


    VOID
    SpyOutputFileNameInformation
    (
        PFLT_FILE_NAME_INFORMATION  fltFileNameInformation
    )
    {
        if ( fltFileNameInformation )
        {
            if ( g_OutputPrivateLogs )
            {
                DbgPrint( ("<FLT_FILE_NAME_INFORMATION Structure> \n"
                           "Size           = %d \n"
                           "NamesParsed    = 0x%08X \n"
                           "Format         = 0x%08X \n"
                           "Name           = %wZ \n"
                           "Volume         = %wZ \n"
                           "Share          = %wZ \n"
                           "Extension      = %wZ \n"
                           "Stream         = %wZ \n"
                           "FinalComponent = %wZ \n"
                           "ParentDir      = %wZ \n\n"),
                          fltFileNameInformation->Size,
                          fltFileNameInformation->NamesParsed,
                          fltFileNameInformation->Format,
                          &(fltFileNameInformation->Name),
                          &(fltFileNameInformation->Volume),
                          &(fltFileNameInformation->Share),
                          &(fltFileNameInformation->Extension),
                          &(fltFileNameInformation->Stream),
                          &(fltFileNameInformation->FinalComponent),
                          &(fltFileNameInformation->ParentDir) );
            }
        }
    }


    ....
    ....

    ....
    ....

    FLT_PREOP_CALLBACK_STATUS
    SpyPreOperationCallback (
        __inout PFLT_CALLBACK_DATA Data,
        __in PCFLT_RELATED_OBJECTS FltObjects,
        __deref_out_opt PVOID *CompletionContext
        )
    {
        FLT_PREOP_CALLBACK_STATUS returnStatus = FLT_PREOP_SUCCESS_NO_CALLBACK; //assume we are NOT going to call our completion routine
        PRECORD_LIST recordList;
        PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
        UNICODE_STRING defaultName;
        PUNICODE_STRING nameToUse;
        NTSTATUS status;
    #if MINISPY_NOT_W2K
        WCHAR name[MAX_NAME_SPACE/sizeof(WCHAR)];
    #endif

    #if PRIVATE_DEBUG_ENABLE
        PFLT_IO_PARAMETER_BLOCK fltIoParameterBlock = Data->Iopb;
        UCHAR                   MajorFunction = fltIoParameterBlock->MajorFunction;
        UCHAR                   MinorFunction = fltIoParameterBlock->MinorFunction;
    #endif PRIVATE_DEBUG_ENABLE

    ....
    ....


                status = FltGetFileNameInformation( Data,
                                                    FLT_FILE_NAME_NORMALIZED |
                                                        MiniSpyData.NameQueryMethod,
                                                    &nameInfo );

    #if PRIVATE_DEBUG_ENABLE
                if( MajorFunction == IRP_MJ_CLEANUP )
                {
                    if ( NT_SUCCESS( status ) )
                    {
                        SpyOutputFileNameInformation( nameInfo );
                    }
                }
    #endif PRIVATE_DEBUG_ENABLE

    ....
    ....

                    lstatus = FltGetFileNameInformation( Data,
                                                         FLT_FILE_NAME_OPENED |
                                                                FLT_FILE_NAME_QUERY_ALWAYS_ALLOW_CACHE_LOOKUP,
                                                         &lnameInfo );


                    if (NT_SUCCESS(lstatus)) {

    #if PRIVATE_DEBUG_ENABLE
                        if( MajorFunction == IRP_MJ_CLEANUP )
                        {
                            SpyOutputFileNameInformation( nameInfo );
                        }
    #endif PRIVATE_DEBUG_ENABLE

    #pragma prefast(suppress:__WARNING_BANNED_API_USAGE, "reviewed and safe usage")
                        (VOID)_snwprintf( name,
                                          sizeof(name)/sizeof(WCHAR),
                                          L"<%08x> %wZ",
                                          status,
                                          &lnameInfo->Name );

                        FltReleaseFileNameInformation( lnameInfo );

                    } else {

    ....
    ....

    ....
    ....

                    retryStatus = FltGetFileNameInformation( Data,
                                                             FLT_FILE_NAME_NORMALIZED |
                                                                 MiniSpyData.NameQueryMethod,
                                                             &nameInfo );

                    if (!NT_SUCCESS( retryStatus )) {

                        //
                        //  We always release nameInfo, so ignore return value.
                        //

                        NOTHING;
                    }
    #if PRIVATE_DEBUG_ENABLE
                    else
                    {
                        if( MajorFunction == IRP_MJ_CLEANUP )
                        {
                            SpyOutputFileNameInformation( nameInfo );
                        }
                    }
    #endif PRIVATE_DEBUG_ENABLE

    ....
    ....
    ....
    ....

    }
    ------------------------------------------------------------

    2010年1月13日 2:38
  • かびるんるんさん
    コードまで載せていただいて、お世話になります。。

    今はもう
    落ちることなく動いているので、
    このまま行っちゃいます。

    何かあったら
    参考にさせていただきますね。

    ではでは。
    2010年1月13日 10:48
  • かびるんるんさん
    WDK サンプルの ctx ドライバでこれら関数を使用しているので、参考になると思います。
    結局、参考にさせていただきまして、Contextを使った実装にしました。。
    2010年1月19日 10:34