none
アプリケーションからのデバイスドライバの呼び出し、制御方法について RRS feed

  • 質問

  • お世話になります。

    アプリケーションから動作中のデバイスドライバを呼び出して特定の処理をさせる方法について教えてください。

    やりたいことは、Visual C++環境で作成したアプリケーション(ドライバのプロパティ画面)からボタンなどのイベントで、
    動作中のデバイスドライバを呼び出して特定の処理を行わせるというようなことがしたいです。

    アプリケーション側でDeviceIoControl関数を使ってIoControlCodeを送信して、
    ドライバ側で定義されたIoControlCodeごとに対応した処理を行うというところまでは分かったのですが、
    デバイスのハンドル取得がうまくいかず、DeviceIoControlの実行がうまくいきません。

    CreateFile関数等を使って、デバイスのハンドルを取得する場合、引数などはどのようにすればよいでしょうか?
    CreateFile関数のlpFileNameの引数は、ドライバのファイルのパスを指定すればよいのでしょうか?

    下記のようにコーディングして試してみたのですが、間違いがありましたらご指摘お願いします。
    例:mydriver.sysというドライバをインストール、動作している場合。

    #define IOCTL_MYDRIVER 0x01
    HANDLE hDevice;
    char InputBuffer[100];
    char OutputBuffer[100];
    DWORD dwReturned;

    memset(InputBuffer,0,sizeof(InputBuffer));
    memset(OutputBuffer,0,sizeof(OutputBuffer));

    hDevice = CreateFile("C:\\WINDOWS\\system32\\drivers\\mydriver.sys",GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    status = DeviceIoControl(hDevice, IOCTL_MYDRIVER, InputBuffer, sizeof(InputBuffer), OutputBuffer, sizeof(OutputBuffer), &dwReturned, NULL);

     


    ちなみにドライバ側では、EvtIoInternalDeviceControlのcallbackが定義されていて、
    既存のIoControlCodeに対応した処理を行うようになっています。

    もしくは、このDeviceIoControlを使う以外に、アプリケーションからドライバ制御する方法がありましたら、教えて頂きたいと思います。

    よろしくお願いします。

    • 移動 Mike Wang (MSCS) 2012年10月2日 12:40 (移動元:Windows デバイスドライバー開発)
    2010年3月29日 8:07

すべての返信

  • こんばんわです。

    デバイス (デバイス ドライバ) に対して DeviceIoControl() API で I/O Control Code を発行したい場合、
    「デバイス ハンドル」が必要となります。
    yoshi0987654 さんが CreateFile() API 1st パラメータで渡しているのは、ドライバ バイナリ ファイル名なので、
    デバイス ハンドルは取得できません。
    (これだと、"C:\\WINDOWS\\system32\\drivers\\mydriver.sys" の「ファイル」がオープンされてしまいます。)

    CreateFile() API でデバイスをオープンしたい場合は、1st パラメータに「デバイス名」を指定しなければなりません。
    デバイス名の指定方法に関しては、下記 MSDN サイトの CreateFile() の説明にもありますが、通常以下のような感じで
    指定します。

    --------------------------------------------------------
    ☆ CreateFile() API 1st パラメータでのデバイス名の指定方法
    "\\\\.\\<Device Name>"
    --------------------------------------------------------
    CreateFile()
    http://msdn.microsoft.com/ja-jp/library/cc429198.aspx

    以下、抜粋...
    The lpFileName string should be of the form \\.\PHYSICALDRIVEx to open the hard disk x. Hard disk numbers start at zero.
    For example:  ハードディスク x を開くには、lpFileName パラメータで \\.\PHYSICALDRIVEx と指定します。ハードディスクの番号は、
                  0 から始まるオフセットで指定します。例を示します。
    --------------------------------------------------------

    ここで注意しなければならないのは、"\\\\.\\<Device Name>" の <Device Name> 部分は、デバイス オブジェクト名ではなく、
    デバイス オブジェクト名に付けられたシンボリック リンクであるとい点だと思います。
    例えば、個々のドライブを示す "C:" とか "D:" などのドライブ レターも、カーネル モード側の Volume Manager が作成した Volume Device Object に
    付けられたシンボリック リンクで、実際の Volume Device Object Name は "\Device\HarddiskVolumeX" とかの名前になってます。
    この辺は、実際に WinObj ツールなどを使って、自分で確かめてみると、理解しやすいと思います。

    あと、カーネル モード ドライバがユーザ モード側に対してインターフェイスを提供する方法は、
    上記でもふれたシンボリック リンクを使う方法以外に、デバイス インターフェイスを定義する方法もあります。
    デバイス インターフェイスの登録方法については、WDK ドキュメントや Web で "IoRegisterDeviceInterface" をキーワードに
    検索すればいっぱい出てきますので、そちらを参考にされれば良いかと思います。

    もし yoshi0987654 さんの作られたドライバが IoRegisterDeviceInterface() でデバイス インターフェイスを提供している場合、
    ユーザ モード側では SetupDiGetDeviceInterfaceDetail() API を使ってインターフェイス情報を取得しなければならないので、
    ちょっと面倒もしれません。

    いずれにしましても、まずはドライバ側がどのような方法でインターフェイスを提供しているのかを確認されるのが良いかと思います。

    参考になりましたら幸いです。

    2010年3月30日 11:28
  • ニコチャン大王さん

    ありがとうございます。

    CreateFile() API 1st パラメータが、デバイス オブジェクトのシンボリックリンク名を指定する必要があること理解できました。
    ただ、WDKサンプルから作成したドライバのシンボリックリンク名が何と定義されているのか、どこで定義されているのかがよく分かりません。
    何かインストールしたドライバのシンボリックリンク名を確認する方法等はありますでしょうか?

    WinObj ツールを使ってみたところ、\Driver\mydriverのようにインストールしたドライバ名はあるのですが、\Device下にそれらしきシンボリックリンク名が見つかりませんでした。

    作成したドライバは、最新のWDKのサンプルの\src\input\hiddigi\EloMTのタッチパネルのサンプルコードをベースとしているのですが、シンボリック名を定義しているところや、IoRegisterDeviceInterfaceを使ってデバイスインタフェースを定義している箇所がよく分かりませんでした。
    シンボリック名やデバイスインタフェースを定義していない場合もあるのでしょうか?

    ベースのサンプルコード \src\input\hiddigi\EloMT\elotouch.c より抜粋

    NTSTATUS
    DriverEntry (
        __in PDRIVER_OBJECT  DriverObject,
        __in PUNICODE_STRING RegistryPath
        )
    /*++

    Routine Description:

        Installable driver initialization entry point.
        This entry point is called directly by the I/O system.

    Arguments:

        DriverObject - pointer to the driver object

        RegistryPath - pointer to a unicode string representing the path,
                       to driver-specific key in the registry.

    Return Value:

        STATUS_SUCCESS if successful,
        STATUS_UNSUCCESSFUL otherwise.

    --*/
    {
        NTSTATUS               status = STATUS_SUCCESS;
        WDF_DRIVER_CONFIG      config = {0};
        WDF_OBJECT_ATTRIBUTES  attributes = {0};
       
        KdPrint(("ELO MT driver entry!\n"));

        WDF_DRIVER_CONFIG_INIT(&config, NInputDeviceAdd);

        //
        // Register a cleanup callback so that we can call WPP_CLEANUP when
        // the framework driver object is deleted during driver unload.
        //
        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

        //
        // Create a framework driver object to represent our driver.
        //
        status = WdfDriverCreate(DriverObject,
                                 RegistryPath,
                                 &attributes, // Driver Attributes
                                 &config,          // Driver Config Info
                                 WDF_NO_HANDLE
                                 );

        if (NT_SUCCESS(status)) {

        }
        else{
            KdPrint(("failed to create driver object status=%x", status));
        }

        return status;
    }

    NTSTATUS
    NInputDeviceAdd(
        IN WDFDRIVER       Driver,
        IN PWDFDEVICE_INIT DeviceInit
        )
    /*++
    Routine Description:

        EloTouchDeviceAdd is called by the framework in response to AddDevice
        call from the PnP manager. We create and initialize a WDF device object to
        represent a new instance of toaster device.

    Arguments:

        Driver - Handle to a framework driver object created in DriverEntry

        DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

    Return Value:

        STATUS_SUCCESS if successful,
        NTSTATUS of failure otherwise.

    --*/
    {
        NTSTATUS                      status = STATUS_SUCCESS;
        WDF_IO_QUEUE_CONFIG           queueConfig = {0};
        WDF_OBJECT_ATTRIBUTES         attributes = {0};
        WDFDEVICE                     hDevice = {0};
        PDEVICE_EXTENSION             devContext = NULL;
        WDFQUEUE                      queue = {0};
        WDF_PNPPOWER_EVENT_CALLBACKS  pnpPowerCallbacks = {0};
       
        UNREFERENCED_PARAMETER(Driver);

        PAGED_CODE();

        KdPrint(("ELO PMT add device!\n"));
        //
        // Relinquish power policy ownership because HIDCLASS acts a power
        // policy owner for ther HID stack.
        //
        WdfDeviceInitSetPowerPolicyOwnership(DeviceInit, FALSE);


        //
        // Initialize pnp-power callbacks, attributes and a context area for the device object.
        //
        //
        WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

        pnpPowerCallbacks.EvtDeviceD0Entry = NInputEvtDeviceD0Entry;
        pnpPowerCallbacks.EvtDeviceD0Exit  = NInputEvtDeviceD0Exit;
        pnpPowerCallbacks.EvtDeviceQueryStop = NInputEvtDeviceStop;
        pnpPowerCallbacks.EvtDeviceQueryRemove = NInputEvtDeviceQueryRemove;

        WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
      
        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION);

        //
        // Create a framework device object.This call will in turn create
        // a WDM device object, attach to the lower stack, and set the
        // appropriate flags and attributes.
        //
        status = WdfDeviceCreate(&DeviceInit, &attributes, &hDevice);
        if (!NT_SUCCESS(status)) {
            TErr(("WdfDeviceCreate failed status:0x%x",status));
            return status;
        }

        devContext = GetDeviceContext(hDevice);
        devContext->Device = hDevice;
        devContext->InputMode = MODE_MOUSE;
        //initialize spinlock
        WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
        attributes.ParentObject = hDevice;
        status = WdfSpinLockCreate(
                               &attributes,
                               &devContext->SpinLock
                               );
        if(!NT_SUCCESS(status))
        {
            KdPrint(("Spink lock creation failed\n"));
            return status;
        }
       
        //get the lower device.
        devContext->IoTarget = WdfDeviceGetIoTarget(hDevice);
    #ifdef PRIORITY_UPDATE
        devContext->LastState = TOUCH_NONE;
    #endif
           
        WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);

        queueConfig.EvtIoInternalDeviceControl = NInputInternalDeviceControl;
       
        queueConfig.PowerManaged = WdfFalse;

        status = WdfIoQueueCreate(hDevice,
                                  &queueConfig,
                                  WDF_NO_OBJECT_ATTRIBUTES,
                                  &queue
                                  );
        if (!NT_SUCCESS (status)) {
            TErr(("WdfIoQueueCreate failed status:0x%x",status));
            return status;
        }

        //
        // Register a manual I/O queue for Read Requests.
        // This queue will be used for storing Requests that need to wait for data
        //  from the serial driver before they can be completed.
        //
        WDF_IO_QUEUE_CONFIG_INIT(&queueConfig, WdfIoQueueDispatchManual);

        //
        // This queue is used for requests that dont directly access the device. The
        // requests in this queue are serviced only when the device is in a fully
        // powered state and sends an interrupt. So we can use a non-power managed
        // queue to park the requests since we dont care whether the device is idle
        // or fully powered up.
        //
         queueConfig.PowerManaged = WdfFalse;

        status = WdfIoQueueCreate(hDevice,
                                  &queueConfig,
                                  WDF_NO_OBJECT_ATTRIBUTES,
                                  &devContext->PingPongQueue
                                  );

        if (!NT_SUCCESS(status)) {
            KdPrint(("failed to create queue\n"));      
        }
        TExit(Func, ("=%x", status));
        return status;
    }


    お手数ですが、もし何か分かるようでしたら、ご指摘頂ければと思います。

    2010年3月31日 8:00
  • こんにちは

    詳しいことはちょっと見ていないのですが・・・

    参考にならなかったらごめんなさい。

     

    > シンボリック名やデバイスインタフェースを定義していない場合もあるのでしょうか?

    ドライバによっては、定義していない場合もあると思います。

    ユーザーモード側に独自のインターフェースを提供しないなら、必要ないと思います。

     

    ELOMTって、シリアルのデバイスのようにあつかうんですよね??(↓yoshi0987654 さんの 以前の質問より)

    http://social.msdn.microsoft.com/Forums/ja-JP/windowsdevicedriverja/thread/3f6a9c18-2bc0-4c59-810b-5474a75ff245

    "COM1"とか"COM2"でオープンして、Read、Writeすることでドライバにアクセスすることを想定しているんじゃないでしょうか?

     

    独自の動作をさせたいなら、インターフェースを定義する必要がある気がします。

     

    2010年3月31日 9:39
  • 皆様、再びこんばんわです。
    (書き込みをしてくれる方がちょっとづつ増えて、ちょっと嬉しい今日この頃です。
      とは言っても、私は MS 社員ではありませんのであしからず...)

    yoshi0987654 さんの作成されているドライバは elotouch サンプル ベースの WDF ドライバでしたね。。。
    すっかり忘れておりました。 申し訳ありません。。。。

    すでに kinmoku さんからのお返事にもありますように、ドライバ側でシンボリック名やデバイスインタフェースを
    定義するかは、ドライバ開発者の任意です。
    つまり、独自に User Mode 側との通信を行いたい場合には、シンボリック名かデバイスインタフェースのどちらか
    (両方でも構いませんが)を定義する必要がありますし、そのような必要性が無いのであれば、逆に定義すべきでは
    ありません。

    今回の yoshi0987654 さんの要求仕様は、
    「自分達の Touch Panel Device に対して独自の I/O 処理をさせたい!!」
    ということだと思いますので、シンボリックかデバイスインタフェースによる定義は必要になると思います。
    (ちなみに、"COM1" "COM2" とかのシンボリック リンクのハンドルで DeviceIoControl() API を発行してしまうと、
      I/O マネージャは elotouch ドライバをスルーして、その親デバイスである Serial Port Device の Device Stack
      に対して直接 I/O Control を発行してしまうと思うので、yoshi0987654 さんの要求仕様は満たせないと思います。)

    前回の返信は WDM ベースのドライバを前提にしましたが、WDF ベースのドライバの場合、使用する関数(およびメソッド)が
    ちょっと異なってきます。

    (かなり大雑把ですが...)
    WDM ドライバの場合、IoCreateDevice() の 3rd パラメータ DeviceName にデバイス オブジェクト名をセットし、
    名前付きのデバイス オブジェクトを作成して、そのあと IoCreateSymbolicLink() でデバイス オブジェクトに割り当てた
    デバイス オブジェクト名に対して任意のシンボリック リンク名を付ける...という流れになるかと思います。

    で、WDF の場合、IoCreateDevice() に相当するのが WdfDeviceCreate() になるかと思いますが、WdfDeviceCreate() の
    パラメータにはデバイス オブジェクト名をセットするフィールドはありませんので、直接デバイス オブジェクト名を
    セットすることは出来ないようです。
    つまり名前付きのデバイス オブジェクトを作成するには、予め WdfDeviceInitAssignName() で WDFDEVICE_INIT 構造体
    データにデバイス オブジェクト名をセットして、その WDFDEVICE_INIT 構造体をパラメータとして WdfDeviceCreate()
    コールを行う必要あるようです。
    で、WdfDeviceCreate()  コールで Framework Device Object が取得出来たら、そのオブジェクトをパラメータに
    WdfDeviceCreateSymbolicLink() コールを行えば、シンボリック リンク名が付けられる...ということだと思います。
    ちなみに IoRegisterDeviceInterface() 対応する WDF の関数は WdfDeviceCreateDeviceInterface() のようです。

    WDF ドライバも Frame Work と一緒に機能することによって WDM ドライバと同等の処理を実現している訳で、
    結局のところは最終的な動きとしては同じなると思いますが、Frame Work が介在する分その手続きが煩雑になる
    場合もある...ということだと思います。

    ちょっとだけ elotouch ドライバの実装を見てみましたが、WdfDeviceInitAssignName() や WdfDeviceCreateDeviceInterface()
    を使っているコードは見当たらなかったので、おそらく User Mode 側への Interface は提供していないようです。
    なので、独自にシンボリックかデバイスインタフェースの定義が必要になると思います。

    WdfDeviceInitAssignName() や WdfDeviceCreateDeviceInterface() の使用方法に関しては、WDK サンプルの
    wdfserial.sys (serial.sys の KMDF バージョン) にその実装例がありますので、こちらが参考になると思います。

    参考になりましたら幸いです。

    2010年3月31日 13:19
  • ニコチャン大王さん、kinmokuさん

    ありがとうございます。

    シンボリックリンク名の定義方法について、丁寧に教えて頂きありがとうございます。
    wdfserial.sys のサンプルコードを参考にして、WdfDeviceInitAssignNameでデバイスオブジェクト名を定義し、
    WdfDeviceCreate後に、WdfDeviceCreateSymbolicLinkでシンボリックリンク名を定義するように、
    下記のような処理を追加したとことろ、デバイスオブジェクト名とシンボリックリンク名を作成することができました。

    WinObjツールで確認したところ、\Device\Touchpanelのデバイスオブジェクト名と、\GLOBAL??\Touchpanelのシンボリックリンク名が作成されていることを確認しました。

     

    #define DEVICE_OBJECT_NAME_LENGTH 128 //追加
    #define SYMBOLICK_LINK_NAME_LENGTH 128 //追加

    NTSTATUS
    NInputDeviceAdd(
        IN WDFDRIVER       Driver,
        IN PWDFDEVICE_INIT DeviceInit
        )
    {

     DECLARE_UNICODE_STRING_SIZE(deviceName, DEVICE_OBJECT_NAME_LENGTH);  //追加
     DECLARE_UNICODE_STRING_SIZE(symbolicLinkName, SYMBOLICK_LINK_NAME_LENGTH); //追加

    ========省略========

     status = RtlUnicodeStringPrintf(&deviceName,L"\\Device\\Touchpanel"); //追加
     status = WdfDeviceInitAssignName(DeviceInit,&deviceName);  //追加

     status = WdfDeviceCreate(&DeviceInit, &attributes, &hDevice);
     if (!NT_SUCCESS(status)) {
      TErr(("WdfDeviceCreate failed status:0x%x",status));
      return status;
     }

     status = RtlUnicodeStringPrintf(&symbolicLinkName,L"\\DosDevices\\Touchpanel"); //追加
     status = WdfDeviceCreateSymbolicLink(hDevice,&symbolicLinkName);  //追加

    ========省略========

    }

     

    ただ、アプリケーション側から、下記のコードのように、CreateFileで"\\\\.\\Touchpanel"のシンボリックリンク名を指定して実行してみたのですが、CreateFileの戻り値がINVALID_HANDLE_VALUEでエラーになってしまいます。
    GetLastError関数で、エラーコードを確認すると、エラーコード:0x1F(システムに接続されたデバイスが機能していません)となっていました。
    ドライバは正常にインストールされ、動作している状態で、アプリケーションからCreateFile, DeviceIoControlを実行しています


    アプリケーション側で、CreateFile,DeviceIoControlを実行するコード

    #define IOCTL_MYDRIVER 0x01
    HANDLE hDevice;
    char InputBuffer[100];
    char OutputBuffer[100];
    DWORD dwReturned;

    memset(InputBuffer,0,sizeof(InputBuffer));
    memset(OutputBuffer,0,sizeof(OutputBuffer));

    hDevice = CreateFile("\\\\.\\Touchpanel",GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    status = DeviceIoControl(hDevice, IOCTL_MYDRIVER, InputBuffer, sizeof(InputBuffer), OutputBuffer, sizeof(OutputBuffer), &dwReturned, NULL);

     

    このようなエラーでCreateFileが失敗する原因は、何が考えられますでしょうか?
    何か間違いがありましたら、ご指摘お願いします。

    ちなみにkinmokuさんのご指摘のように、サンプルのドライバは、シリアルポートにインストールして動作しているので、
    シンボリックリンク名の定義の修正を入れていない元のドライバをCOM1にインストールして、CreateFileに"COM1"を指定して実行してみたのですが、同じエラーで、CreateFileが成功しませんでした。

    お手数ですが、何か分かりましたら宜しくお願いします。

    2010年4月1日 6:14
  • 皆様、再びこんばんわです。

    実際に試した訳ではないのでわかりませんけど、もしかしたら CreateFile() API 3rd パラメータ dwShareMode での
    共有モード設定に問題があるのかも...。
    以下の感じのパラメータで試してみたらどうでしょうか?

    -----------------------------------------------------------------

            :
    hDevice = CreateFile( TEXT("\\\\.\\Touchpanel"),  // or "COM1"
                          GENERIC_READ | GENERIC_WRITE,
                          0,
                          NULL,
                          OPEN_EXISTING,
                          FILE_ATTRIBUTE_NORMAL,
                          NULL );

    if ( hDevice == INVALID_HANDLE_VALUE )
    {
        _tprintf( TEXT(" CretaFile API returns false .... Error Code : %x \n"), GetLastError() );
    }
            :

    -----------------------------------------------------------------

    まずは、CreateFile() で失敗した時の Last Error Code をチェックした方が良いと思います。
    もっとも、WinDBG を接続しているのであれば、デバッガ上で確認するのが一番手っ取り早いですが...

    ちなみに、WinDBG はカーネル モード ドライバ開発者にとって必須のデバッグ ツールです。
    これがあれば、ダンプ解析も自力でなんとかなる場合が結構あります。
    なので是非 WinDBG を使用されることをお勧めします。

    参考になりましたら幸いです。

    2010年4月1日 10:30
  • ニコチャン大王さん

    ありがとうございます。

    ご指摘頂いたように、3rd パラメータ dwShareModeの設定を変更して試してみましたが、やはりINVALID_HANDLE_VALUEが返り、CreateFile() 後のLast Error Codeは、0x1F(システムに接続されたデバイスが機能していません)になります。

    WinDBGを使ったデバッグや、WdfDeviceCreateDeviceInterface() でデバイスインタフェースを定義する方法なども試して、いろいろやってみます。

    WinDBGを使いこなすのになかなか苦労してますが。。。

    いろいろ丁寧に教えて頂き、ありがとうございました。

     

    2010年4月5日 9:02
  • とりあえず、COM ポートや Touch Panel デバイスが、Device Manager 上で正しく表示されているかを確認した方が良いかも...

    2010年4月5日 10:53
  • デバイスドライバファイル名をUNICODE文字にしてください。

    同僚が同じところではまってました。

    http://msdn.microsoft.com/ja-jp/library/dybsewaf(VS.80).aspx

    >status = RtlUnicodeStringPrintf(&deviceName,L"\\Device\\Touchpanel"); //追加

    を追加したということですが

    L"\\Device\\Touchpanel"の”L”は「文字列をUNICODEにしまっせ」というリテラル文字列らしいです。

    ここで書いてるのに、

    hDevice = CreateFile("\\\\.\\Touchpanel",GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

    ここで書いてないの理由がよくわかりませんが・・・。

     

    試してないんで、根拠のない情報です。

    根拠は、同僚が「デバイスドライバファイル名をUNICODEにしたらCreateFileを正常に実行できた。」という話を聞いた、ということだけですわー。

    解決したらフィードバックください。


    以上です。

    よろしくおねがいします。

    2010年7月9日 4:49