デバイスの追加・削除の通知をアプリケーションで受け取る方法


皆さん、こんにちは。A寿です。

 

突然ですが、皆さんは、かつお節を使った和食の「本物の味」を味わったことはありますか?・・・このお話にご興味のある方は本文の最後の【閑話休題】までどうぞ。

 

さて、今回は、デバイスの追加・削除の通知をアプリケーションで受け取る方法をご紹介しようと思います。前回の私の記事で、ToasterサンプルのNotify.exeの使い方をご紹介しました。その記事で、Notify.exeToasterデバイスを追加・削除した場合に、Notify.exeのウィンドウに、対応するメッセージが表示されていました。今回は、Toasterデバイスが追加・削除されて、Notify.exeのウィンドウに、対応するメッセージが表示されるまでに、Notify.exe(アプリ側)Toaster.sys(ドライバ側)それぞれに必要となる実装について見ていきたいと思います。

 

まず、Notify.exeToaster.sysから受け取る通知には以下の2種類があります。

(A) デバイスインターフェイスに関する通知

(B) デバイスハンドルに関する通知

 

それぞれについて、以下の3つの処理があります。

(1) 通知の受け取りの開始(登録)

(2) 通知の受け取り

(3) 通知の受け取りの終了(登録解除)

 

Notify.exeの操作と、(A),(B)それぞれの(1)(3)の処理は、以下の表のような関係になります。

 

Notify.exe

の操作

起動

(ウィンドウの作成)

デバイス

追加

デバイス

削除

終了

(ウィンドウのクローズ)

(A)

(1)

(2)

(2)

(3)

(B)

((1)())

(1)

(2),(3)

 

(※:Notify.exe起動前に、すでにToasterデバイスが追加されていた場合。)

 

それでは、(A)から順番に、(1)(3)の通知についてNotify.exeToaster.sysの処理を見ていきましょう。(以下の説明は、可能であれば、ソースコードを見たり、実機やデバッガを動かしたりしながら、お読みいただければ幸いです。)

 

(A) デバイスインターフェイスの場合

 

(1) 通知の受け取りの開始(登録)

 

アプリケーション側で、通知の受け取りを開始するためには、RegisterDeviceNotification()APIを使います。後程説明しますが、デバイスインターフェイスだけでなく、デバイスハンドルもこの関数を使います。

 

Notify.exeでは、ウィンドウが作成されるタイミングで、toaster.sysのデバイスインターフェイスに対して、RegisterDeviceNotification()を呼びます。

 

RegisterDeviceNotification()周辺の具体的な処理を説明する前に、Notify.exeの最初の処理を簡単に説明しておきます。Notify.exeが実行されると、まずnotify.c133行目から始まるWinMain()が実行されます。このWinMain()で、CreateWindow()(notify.c166176行目)が実行されることで、「Toaster Package Test Application」というタイトルのウィンドウが作成されます。この時、このウィンドウのウィンドウプロシージャ(WNDCLASS構造体のlpfnWndProc)であるWndProc()で、WM_CREATEというメッセージを受け取ります。このWM_CREATEの処理(notify.c211240行目)で、デバイスインターフェイスに対して、通知の受け取りの開始の処理を行います。(本当は、上表のように、既存のデバイスに対するデバイスハンドルについての通知の登録も、ここで行いますが、今回はこの部分についての話は割愛します。)

 

デバイスインターフェイスに関する通知の登録は、以下の抜粋(notify.c232235行目)のように行われます。

 

232   filter.dbcc_size = sizeof(filter);

233   filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

234   filter.dbcc_classguid = InterfaceGuid;

235   hInterfaceNotification = RegisterDeviceNotification(hWnd, &filter, 0);

 

232234行目は、DEV_BROADCAST_DEVICEINTERFACE構造体の初期化です。

 

ポイントは、233行目の、dbcc_devicetypeDBT_DEVTYP_DEVICEINTERFACEをセットしているところです。デバイスインターフェイスもデバイスハンドルも同じRegisterDeviceNotification()を使いますが、DBT_DEVTYP_DEVICEINTERFACEによってデバイスインターフェイスに関する通知であることを指定しています。また、RegisterDeviceNotification()は、以下のドキュメントの通り、2nd parameterLPVOID型ですが、DBT_DEVTYP_DEVICEINTERFACEを指定していることによって、DEV_BROADCAST_DEVICEINTERFACE構造体を使用することができます。

 

RegisterDeviceNotification Function

http://msdn.microsoft.com/en-us/library/aa363431(VS.85).aspx

 

HDEVNOTIFY WINAPI RegisterDeviceNotification(

  __in  HANDLE hRecipient,

  __in  LPVOID NotificationFilter,

  __in  DWORD Flags

);

 

 

【補足】RegisterDeviceNotification2nd parameterについて

 

2nd parameterは、LPVOID型といっても、上記ドキュメントの以下の記述の通り、「DEV_BROADCAST_HDR構造体で始まる構造体」へのポインタを使うことになっています。

 

NotificationFilter [in]

A pointer to a block of data that specifies the type of device for which notifications should be sent. This block always begins with the DEV_BROADCAST_HDR structure. The data following this header is dependent on the value of the dbch_devicetype member, which can be DBT_DEVTYP_DEVICEINTERFACE or DBT_DEVTYP_HANDLE. For more information, see Remarks.

 

DEV_BROADCAST_HDR構造体は、以下のドキュメントのように定義されています。

 

DEV_BROADCAST_HDR Structure

http://msdn.microsoft.com/en-us/library/aa363246(VS.85).aspx

 

typedef struct _DEV_BROADCAST_HDR {

  DWORD dbch_size;

  DWORD dbch_devicetype;

  DWORD dbch_reserved;

} DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;

 

これに対して、今回使用しているDEV_BROADCAST_DEVICEINTERFACE構造体は、以下のドキュメントのように定義されています。

 

DEV_BROADCAST_DEVICEINTERFACE Structure

http://msdn.microsoft.com/ja-jp/library/aa363244(en-us,VS.85).aspx

 

typedef struct _DEV_BROADCAST_DEVICEINTERFACE {

 DWORD dbcc_size;

  DWORD dbcc_devicetype;

  DWORD dbcc_reserved;

  GUID  dbcc_classguid;

  TCHAR dbcc_name[1];

} DEV_BROADCAST_DEVICEINTERFACE, *PDEV_BROADCAST_DEVICEINTERFACE;

 

つまり、DEV_BROADCAST_HDR構造体は、DEV_BROADCAST_DEVICEINTERFACE構造体や後述のDEV_BROADCAST_HANDLE構造体の先頭(ヘッダ)部分を表す構造体になります。

 

234行目では、DEV_BROADCAST_DEVICEINTERFACE構造体のdbcc_classguidに、デバイスインターフェイスクラスのGUIDを指定します。今回は、Toaster.sysというファンクションドライバのデバイスインターフェイスクラスとして、InterfaceGuidが指定されています。InterfaceGuidは、WinMain()(notify.c147行目)で、

    InterfaceGuid = GUID_DEVINTERFACE_TOASTER;

という値が指定されています。この、GUID_DEVINTERFACE_TOASTERは、toaster\wdm\inc\public.h43行目あたりに以下のように定義されています。

 

37 // Define an Interface Guid for toaster device class.

38 // This GUID is used to register (IoRegisterDeviceInterface)

39 // an instance of an interface so that user application

40 // can control the toaster device.

41 //

42

43 DEFINE_GUID (GUID_DEVINTERFACE_TOASTER,

44         0x781EF630, 0x72B2, 0x11d2, 0xB8, 0x52, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71);

45 //{781EF630-72B2-11d2-B852-00C04FAD5171}

 

このGUIDToaster.sysのデバイスインターフェイスクラスとして使えるようにしているのは、Toaster.sysToasterAddDevice()の以下の処理(toaster.c290294行目)です。

 

290    status = IoRegisterDeviceInterface (

291                PhysicalDeviceObject,

292                (LPGUID) &GUID_DEVINTERFACE_TOASTER,

293                NULL,

294                &fdoData->InterfaceName);

 

【補足】デバイスインターフェイスクラスについて

 

デバイスインターフェイスクラスについては、英語では、下記ドキュメントをご参照ください。

 

Device Interface Classes

http://msdn.microsoft.com/en-us/library/ff541339(VS.85).aspx

 

日本語では、まさかたさんの下記の記事が参考になると思います。

 

SetupDi API DevCon SetupDi API の使い方編 ~

http://blogs.msdn.com/b/jpwdkblog/archive/2009/07/15/setupdi-api-devcon-setupdi-api.aspx

 

なお、ご自身でドライバを開発されない場合でも、システム(OS)が提供しているデバイスインターフェイスクラスを使って通知を受け取りたい場合があると思います。そのような場合は、以下のドキュメントから、ご希望のデバイスのカテゴリへのリンクをたどっていただき、該当するGUIDをご利用いただくのがよいかと思います。

 

System-Defined Device Interface Classes

http://msdn.microsoft.com/en-us/library/ff553412(VS.85).aspx

 

 

以上のように初期化したDEV_BROADCAST_DEVICEINTERFACE構造体を、notify.c235行目のRegisterDeviceNotification()で登録します。通知の受け取り手は、RegisterDeviceNotification()3rd parameter0(=DEVICE_NOTIFY_WINDOW_HANDLE)と指定しているので、ウィンドウのハンドルです。どのウィンドウのハンドルが通知を受け取るかを1st parameterhWndで指定しています。

 

RegisterDeviceNotification()の戻り値は、hInterfaceNotificationに代入されています。これは以下のようにnotify.c64行目に定義されているグローバル変数です。

 

64  HDEVNOTIFY  hInterfaceNotification;

 

この値は、後程、(3)で登録解除の際に使われます。

 

 

(2) 通知の受け取り

 

デバイスインターフェイスの場合、前述の図の通り、Toasterデバイスの追加と削除のタイミングで、通知を受け取ります。デバイスインターフェイスの通知は、ドライバ側でIoSetDeviceInterfaceState()を呼ぶことにより有効化・無効化します。IoSetDeviceInterfaceState()によるデバイスインターフェイスの有効化・無効化のタイミングで、RegisterDeviceNotification()で「通知を受け取りたい」という意思表明をしたコンポーネント(ここではnotify.exe)に、通知をします。

IoSetDeviceInterfaceState()の定義は、以下のドキュメントのようになっています。

 

IoSetDeviceInterfaceState

http://msdn.microsoft.com/en-us/library/ff549700(VS.85).aspx

 

NTSTATUS IoSetDeviceInterfaceState(

  __in  PUNICODE_STRING SymbolicLinkName,

  __in  BOOLEAN Enable

);

 

この2nd parameterTRUEにすれば有効(enable)FALSEにすれば無効(disable)になります。

 

IoSetDeviceInterfaceState()は、同ドキュメントの以下の記述にもあるように、通常IRP_MN_START_DEVICEで有効、IRP_MN_SURPRISE_REMOVEIRP_MN_REMOVE_DEVICEで無効にします。

 

A function or a filter driver typically calls this routine with Enable set to TRUE after it successfully starts a device in response to an IRP_MN_START_DEVICE IRP. Such a driver should disable the device interface instance (that is, call IoSetDeviceInterfaceState and set Enable to FALSE) when it removes the device in response to an IRP_MN_REMOVE_DEVICE IRP or an IRP_MN_SURPRISE_REMOVAL IRP.

 

それでは、デバイスの追加・削除それぞれの処理を説明しながら、IoSetDeviceInterfaceState()が呼ばれるタイミングを確認しましょう。

 

 

■デバイスの追加の場合

 

[ユーザーモード]

Toasterデバイスの追加は、Notify.exeのメニューの[Bus]-[PlugIn]で行います。

これを実施しますと、Notify.exeWM_COMMANDのウィンドウメッセージを受け取ります。

これにより、WndProc()HandleCommands()を呼びます(notify.c208行目)[PlugIn]に該当する処理は、IDM_PLUGINが該当します。(notify.rcをご参照ください。)

HandleCommands()IDM_PLUGINの処理で、OpenBusInterface()を呼び出します(notify.c366行目)

OpenBusInterface()では、busenum.sysIOCTL_BUSENUM_PLUGIN_HARDWAREI/O Controlを送ります(notify.c10821086行目)

 

[カーネルモード]

busenum.sysBus_IoCtl()は、IOCTL_BUSENUM_PLUGIN_HARDWAREを受け取ったら、Bus_PlugInDevice()を呼びます(busenum.c260283行目)

Bus_PlugInDevice()は、IoCreateDeviceSecure()PDOを作り(pnp.c12051213行目)Bus_InitializePdo()で初期化し(pnp.c1234行目)IoInvalidateDeviceRelations()PnPのクエリを発生させます(pnp.c1242行目)

busenum.sys側でのPnPの処理が終わると、Toaster.sysToasterAddDevice()が呼ばれます(toaster.c180行目)

そこでは、Toaster.sysのデバイスオブジェクトが作られ(toaster.c205211行目)busenum.sysのデバイスオブジェクトにアタッチされます(toaster.c282行目)

そしてToaster.sysPnPの処理が進み、ToasterStartDevice()が呼ばれます(toaster.c410行目)

toaster.c1318行目で、以下のように、IoSetDeviceInterfaceState()が呼ばれます。

 

1318  status = IoSetDeviceInterfaceState(&FdoData->InterfaceName, TRUE);

 

IoSetDeviceInterfaceState()によるユーザーモードへの通知は非同期的に行われます。

 

[ユーザーモード]

Notify.exeは、WM_DEVICECHANGEのウィンドウメッセージを受け取ります。

この時、WndProc()は、3rd parameterWPARAM wParamDBT_DEVICEARRIVAL(0x8000)を受け取ります。

そして、4th parameterLPARAM lParamは、notify.c201行目で、DEV_BROADCAST_HDR構造体のポインタにキャストされますが、このdbch_devicetypeには、DBT_DEVTYP_DEVICEINTERFACEが入っています。

これにより、WndProc()は、以下のように、notify.c277行目で、HandleDeviceInterfaceChange()を呼びます。

 

277  HandleDeviceInterfaceChange(hWnd, nEventType, (PDEV_BROADCAST_DEVICEINTERFACE) p);

 

この中で、前回の記事の「(8) notify.exetoasterデバイスを追加」の一番下の図のように、「New device Arrived (Interface Change Notification): ToasterDevice01」と「Opened handled to the device: ToasterDevice01」のメッセージが表示されます。これらは、それぞれ、notify.cの以下の場所で出力されます。

 

503        Display(TEXT("New device Arrived (Interface Change Notification): %ws"),

504                    deviceInfo->DeviceName);

 

520        Display(TEXT("Opened handled to the device: %ws"),  deviceInfo->DeviceName);

 

 

■デバイスの削除の場合

 

[ユーザーモード]

Toasterデバイスの削除は、Notify.exeのメニューの[Bus]-[UnPlug(Surprise Removal)]で行います。

これを実施しますと、Notify.exeWM_COMMANDのウィンドウメッセージを受け取ります。

これにより、WndProc()HandleCommands()を呼びます(notify.c208行目)

[UnPlug(Surprise Removal)]に該当する処理は、IDM_UNPLUGが該当します。(notify.rcをご参照ください。)

HandleCommands()IDM_UNPLUGの処理で、OpenBusInterface()を呼び出します(notify.c375行目)

OpenBusInterface()では、busenum.sysIOCTL_BUSENUM_UNPLUG_HARDWAREI/O Controlを送ります(notify.c11031107行目)

 

[カーネルモード]

busenum.sysBus_IoCtl()は、IOCTL_BUSENUM_UNPLUG_HARDWAREを受け取ったら、Bus_UnPlugDevice()を呼びます(busenum.c285296行目)

Bus_UnPlugDevice()は、ユーザーが入力したSerialNoと一致するデバイスを探し(pnp.c12991318行目)、見つかったらIoInvalidateDeviceRelations()PnPのクエリを発生させます(pnp.c13221325行目)

PnPの処理が進むと、Toaster.sysToasterDispatchPnp()IRP_MN_SURPRISE_REMOVALの処理が行われます(toaster.c561597行目)

その時、toaster.c568行目で、以下のようにIoSetDeviceInterfaceState()を呼び出します。

 

568        status = IoSetDeviceInterfaceState(&fdoData->InterfaceName, FALSE);

 

[ユーザーモード]

Notify.exeは、WM_DEVICECHANGEのウィンドウメッセージを受け取ります。

この時、WndProc()は、3rd parameterWPARAM wParamDBT_DEVICEREMOVECOMPLETE(0x8004)を受け取ります。

そして、4th parameterLPARAM lParamは、notify.c201行目で、DEV_BROADCAST_HDR構造体のポインタにキャストされますが、このdbch_devicetypeには、DBT_DEVTYP_DEVICEINTERFACEが入っています。

これにより、WndProc()は、以下のように、notify.c277行目で、HandleDeviceInterfaceChange()を呼びます。

 

277  HandleDeviceInterfaceChange(hWnd, nEventType, (PDEV_BROADCAST_DEVICEINTERFACE) p);

 

この中で、前回の記事の「(9) notify.exetoasterデバイスを削除」の一番下の図の一番下の行のように、「Remove Complete (Interface Change Notification)」のメッセージが表示されます。これは、notify.cの以下の場所で出力されます。

 

531        Display(TEXT("Remove Complete (Interface Change Notification)"));

 

 

(3) 通知の受け取りの終了(登録解除)

 

アプリケーション側で、通知の受け取りの終了(登録解除)を行うためには、UnregisterDeviceNotification()APIを使います。デバイスインターフェイスだけでなく、デバイスハンドルもこの関数を使います。(RegisterDeviceNotification()と同様です。)

 

Notify.exeでは、ウィンドウがクローズされるタイミングで、toaster.sysのデバイスインターフェイスに関連したデバイス通知ハンドルに対して、UnregisterDeviceNotification()を呼びます。Notify.exeのウィンドウをクローズすると、WndProc()で、WM_CLOSEというメッセージを受け取ります。このWM_CLOSEの処理(notify.c289292行目)で、以下のようにデバイスインターフェイスに対して、通知の受け取りの終了(登録解除)の処理を行います。

 

291            UnregisterDeviceNotification(hInterfaceNotification);

 

UnregisterDeviceNotification()の引数は、以下のドキュメントのように、RegisterDeviceNotification()の戻り値であるデバイス通知ハンドルです。(1)の記述を読み返していただくと、グローバル変数hInterfaceNotificationに、RegisterDeviceNotification()の戻り値をセットしていたことがお分かりいただけると思います。

 

UnregisterDeviceNotification Function

http://msdn.microsoft.com/en-us/library/aa363475(VS.85).aspx

 

BOOL WINAPI UnregisterDeviceNotification(

  __in  HDEVNOTIFY Handle

);

 

Handle [in]

Device notification handle returned by the RegisterDeviceNotification function.

 

 

(B) デバイスハンドルの場合

 

(1) 通知の受け取りの開始(登録)

 

デバイスハンドルの通知についての登録は、デバイスインターフェイスの場合と同様、RegisterDeviceNotification()を使います。ただ、呼び出すタイミングやRegisterDeviceNotification()2nd parameterにセットするデータが、デバイスインターフェイスの場合とは異なります。

 

デバイスハンドルの通知についての登録の場合、RegisterDeviceNotification()を呼ぶタイミングは、デバイスが追加された後になります。これは、デバイスが追加された後でないと、デバイスをオープンして、デバイスハンドルを取得することができないからです。そのため、前述の表のように、ウィンドウを作成した時にすでにデバイスがある場合と、デバイスが追加された場合のぞれぞれでデバイスハンドルを取得し、RegisterDeviceNotification()を呼んでいます。

 

具体的なコードの場所として、デバイスが追加された場合を見てみましょう。前述のとおり、デバイスが追加され、デバイスインターフェイスが有効化された通知をnotify.exeが受けると、HandleDeviceInterfaceChange()が呼ばれます。notify.c482528行目が、HandleDeviceInterfaceChange()の「case DBT_DEVICEARRIVAL」の処理が行われているところです。この部分のうち、以下の処理が、デバイスハンドルの通知についての登録の処理になります。

 

521        memset (&filter, 0, sizeof(filter)); //zero the structure

522        filter.dbch_size = sizeof(filter);

523        filter.dbch_devicetype = DBT_DEVTYP_HANDLE;

524        filter.dbch_handle = deviceInfo->hDevice;

525

526        deviceInfo->hHandleNotification =

527                            RegisterDeviceNotification(hWnd, &filter, 0);

 

521524行目は、DEV_BROADCAST_HANDLE構造体の初期化です。

 

ポイントは、523行目の、dbch_devicetypeDBT_DEVTYP_HANDLEをセットしているところです。これを指定することで、RegisterDeviceNotification()2nd parameterDEV_BROADCAST_HANDLE構造体をセットすることができます。

 

524行目では、DEV_BROADCAST_HANDLE構造体のdbch_handleに、デバイスのハンドルを指定します。今回は、この処理の直前で以下のようにデバイスハンドルを取得しています。

 

512        deviceInfo->hDevice = CreateFile(dip->dbcc_name,

513                                        GENERIC_READ |GENERIC_WRITE, 0, NULL,

514                                        OPEN_EXISTING, 0, NULL);

 

dip->dbcc_nameには、例えば、以下のようなデバイス名が入っています。

  \\?\{B85B7C50-6A01-11d2-B841-00C04FAD5171}#MsToaster#1&79f5d87&0&01#{781ef630-72b2-11d2-b852-00c04fad5171}

 

以上のように初期化したDEV_BROADCAST_HANDLE構造体を、527行目のRegisterDeviceNotification()2nd parameterにセットして、登録します。RegisterDeviceNotification()のその他の引数については、デバイスインターフェイスのところ((A)(1))で述べたとおりです。

 

(2) 通知の受け取り

 

デバイスハンドルの場合、前述の図の通り、Toasterデバイスの削除のタイミングで、通知を受け取ります。Notify.exeのメニューの[Bus]-[UnPlug(Surprise Removal)]Toasterデバイスを削除すると、結果的に、WndProc()WM_DEVICECHANGEを受け取ります。この時、WndProc()は、3rd parameterWPARAM wParamDBT_DEVICEREMOVECOMPLETE(0x8004)を受け取ります。そして、4th parameterLPARAM lParamは、notify.c201行目で、DEV_BROADCAST_HDR構造体のポインタにキャストされますが、このdbch_devicetypeには、DBT_DEVTYP_HANDLEが入っています。これにより、WndProc()は、以下のように、notify.c280行目で、HandleDeviceChange()を呼びます。

 

280  HandleDeviceChange(hWnd, nEventType, (PDEV_BROADCAST_HANDLE) p);

 

この中で、前回の記事の「(9) notify.exetoasterデバイスを削除」の一番下の図の最初の2行のように、「Remove Complete (Handle Notification):ToasterDevice01」と「Closed handle to device ToasterDevice01」のメッセージが表示されます。これは、notify.cの以下の場所で出力されます。

 

599        Display(TEXT("Remove Complete (Handle Notification):%ws"),

600                    deviceInfo->DeviceName);

 

614            Display(TEXT("Closed handle to device %ws"), deviceInfo->DeviceName );

 

後者(614行目)の方のメッセージの前に、以下のように、このデバイスハンドルをクローズしています。

 

612            CloseHandle(deviceInfo->hDevice);

 

つまり、デバイスの削除の通知をデバイスハンドルについての通知として受け取ることで、そのデバイスハンドルをクローズするタイミングがわかる、というわけです。

 

 

(3) 通知の受け取りの終了(登録解除)

 

デバイスハンドルの場合も、UnregisterDeviceNotification()で通知の受け取りの終了(登録解除)を行います。Notify.exeでは、デバイスハンドルについてデバイスの削除が通知されたタイミングで、そのデバイスハンドルに関連したデバイス通知ハンドルに対して、UnregisterDeviceNotification()を呼びます。具体的なコードの場所は、以下です。

 

607            UnregisterDeviceNotification(deviceInfo->hHandleNotification);

 

 

以上が、デバイスの追加・削除の通知をアプリケーションで受け取る方法になります。お役に立てれば幸いです。

 

それでは、皆さん、よいお年をお迎えください。

 

――――――――――――――――

 

【閑話休題】突然ですが、皆さんは、かつお節を使った和食の「本物の味」を味わったことはありますか?

 

突然ですが、皆さんは、かつお節を使った和食の「本物の味」を味わったことはありますか?

 

結論から言うと、私はありません。正確には、味わえるはずだったのですが、味わったのか味わってないのかよくわかりませんでした。

 

数年前、カルチャーセンター主催のかつお節の講座に参加した時の話です。この講座は、大人向けの「食育」を目的に、かつお節が題材に選ばれたものです。講座の内容自体は、某水産加工品メーカーの方が、かつお節ができるまでの工程を開設してくださったり、女性の料理研究家の方が、「本物の味」として、かつお節をメインにだしをとった味噌汁や、かつお節と醤油のかかったご飯などをふるまってくれたり、自分でかつお節を削る体験コーナーもあったりするなど、非常に充実したものでした。

 

その料理研究家の先生が、講義中に料理を出してくれた時のことです。先生はできたての料理を出すために、受講生の目の前で調理されるのですが、お弟子さんの一人が、どうやら分量を間違えたらしいのです。先生は、激しく怒って、授業中にもかかわらず、ヒステリックに弟子を叱り始めました。そして、やっと叱り終えたかと思うと、時間の都合上、料理をやり直すわけにもいかないので、そのまま料理は受講生に配られました。が、意外にも、私や他の受講生の方たちは、誰も文句を言う必要なく、おいしくいただきました。にもかかわらず、先生の怒りはおさまらず、「こんなはずじゃなかったんです。ごめんなさいね。」という受講生への優しい言葉を皮切りに、また弟子を叱り始めたのです。結局、受講生は、先生のおっしゃる「本物の味」はわからずじまいな上に、先生から弟子への壮絶な説教シーンを延々と見て、帰ったのでした。皆様も、場所を選ばずにブチ切れる先生にはご注意ください。

 

 

Skip to main content