EternalWindows
GINA / セキュリティUI

ユーザーがログオンしている状態でSASが入力された場合、 デスクトップはwinlogonデスクトップに切り替わり、WlxLoggedOnSASが呼ばれます。 WlxInitializeでWlxUseCtrlAltDelを呼び出していた場合は、 単純にdefaultデスクトップでCtrl + Alt + Delを押下するだけで、 WlxLoggedOnSASを呼び出すことができるでしょう。

int WlxLoggedOnSAS(
  PVOID pWlxContext,
  DWORD dwSasType,
  PVOID pReserved
);

pWlxContextは、コンテキストのアドレスです。 dwSasTypeは、押下されたSASのタイプです。 pReservedは、予約されています。 戻り値として指定可能な定数の一部を次に示します。

定数 意味
WLX_SAS_ACTION_NONE デフォルトデスクトップに戻る。
WLX_SAS_ACTION_LOCK_WKSTA ワークステーションをロックする。
WLX_SAS_ACTION_LOGOFF ユーザーをログオフさせる。
WLX_SAS_ACTION_SHUTDOWN ユーザーをログオフさせ、コンピュータをシャットダウンする。
WLX_SAS_ACTION_SHUTDOWN_SLEEP スタンバイ状態に入る。
WLX_SAS_ACTION_SHUTDOWN_HIBERNATE 休止状態に入る。
WLX_SAS_ACTION_TASKLIST タスクマネージャを起動する。

これらの定数が示す動作を実際に行うのは、Winlogonだということを忘れないでください。 たとえば、WLX_SAS_ACTION_LOGOFFを返すとWinlogonはユーザーをログオフさせますから、 GINAが明示的にExitWindowを呼び出してユーザーをログオフさせる必要はありません。 つまり、WlxLoggedOnSASの主な処理は数多くのセキュリティ操作をUIで提供するだけでよく、 後はその操作に対応する定数を返せばそれで十分なのです。 事実、msgina.dllはWlxLoggedOnSASでダイアログを表示し、 そこに配置されているボタンは、関数が行えるセキュリティ操作を反映しています。 次に、WlxLoggedOnSASのコード例を示します。

int WINAPI WlxLoggedOnSAS(PVOID pWlxContext, DWORD dwSasType, PVOID pReserved)
{
	int           nId;
	int           nResult;
	LPGINACONTEXT lpgc = (LPGINACONTEXT)pWlxContext;

	if (dwSasType != WLX_SAS_TYPE_CTRL_ALT_DEL)
		return WLX_SAS_ACTION_NONE;
	
	nId = lpgc->pDispatchTable->WlxMessageBox(lpgc->hWlx, NULL, L"「はい」を押すとログオフ、「いいえ」をタスクマネージャを起動します。", L"Windowsのセキュリティ", MB_YESNO);
	if (nId == IDYES)
		nResult = WLX_SAS_ACTION_LOGOFF;
	else if (nId == IDNO)
		nResult = WLX_SAS_ACTION_TASKLIST;
	else
		nResult = WLX_SAS_ACTION_NONE;

	return nResult;
}

この例ではダイアログを表示するのを避け、メッセージボックスで済ましていますが、 それでも十分な役割を果たしているといえます。 WlxMessageBoxに指定している定数がMB_YESNOであることから、 IDYESとIDNO以外の値が返ることはないと思われますが、 万が一そのようなときは単純にdefaultデスクトップに戻るようにします。

ShellShutdownDialogについて

WlxLoggedOnSASで返すことのできる値には、シャットダウンや再起動、 休止状態など同じような意味合いを持つものが多いことから、 それらをユーザーに選択させたいような場合はダイアログが便利です。 できれば、msgina.dllが提供するような項目をコンボボックスで選択できる ダイアログを表示したいところですが、実はこれは可能です。 msgina.dllは、ShellShutdownDialogという関数をエクスポートしており、 この関数を呼び出せばシャットダウン系の項目をリストから選択することができます。 ShellShutdownDialogはリファレンス化されていませんが、 次のようなプロトタイプを持つようです。

DWORD WINAPI ShellShutdownDialog(HWND, WCHAR *, BOOL)

第1引数は、ダイアログの親としたいウインドウハンドルですが、NULLを指定することもできます。 第2引数は、現在ログオンしているユーザー名ですが、NULLを指定することもできます。 第3引数は、FALSEのときに「(ログオンしているユーザー名)のログオフ」という 項目が表示され、TRUEのときはログオフ項目は非表示になります。

WlxLoggedOnSASで表示するダイアログにシャットダウンボタンがあると仮定して、 以下にコード例を示します。

typedef DWORD (WINAPI *LPFNSHELLSHUTDOWNDIALOG)(HWND, LPWSTR, BOOL);

if (nId == ID_SHUTDOWN) {
	DWORD                   dwResult;
	HMODULE                 hmod;
	LPFNSHELLSHUTDOWNDIALOG lpfnShellShutdownDialog;
	
	hmod = LoadLibrary(L"msgina.dll");
	if (hmod == NULL)
		return 0;
	
	lpfnShellShutdownDialog = (LPFNSHELLSHUTDOWNDIALOG)GetProcAddress(hmod, "ShellShutdownDialog");
	if (lpfnShellShutdownDialog == NULL)
		return 0;

	dwResult = lpfnShellShutdownDialog(NULL, NULL, TRUE);

	switch (dwResult) {
	case 1:
		nResult = WLX_SAS_ACTION_LOGOFF;
		break;
	case 2:
		nResult = WLX_SAS_ACTION_SHUTDOWN;
		break;
	case 4:
		nResult = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
		break;
	case 16:
		nResult = WLX_SAS_ACTION_SHUTDOWN_SLEEP;
		break;
	case 64:
		nResult = WLX_SAS_ACTION_SHUTDOWN_HIBERNATE;
		break;
	default:
		nResult = WLX_SAS_ACTION_NONE;
		break;
	}
	
	FreeLibrary(hmod);
}

ShellShutdownDialogの戻り値がWlxLoggedOnSASのどの戻り値と適合するかは見ての通りです。 ShellShutdownDialogはダイアログを表示するだけですから、 本来ならば戻り値を基にntdll.dllがエクスポートするNtShutdownSystemという関数を 呼び出して、明示的にシャットダウンを行う必要があるのですが、 GINAのWlxLoggedOnSASではその必要がないのは幸運なことだといえるでしょう。 なお、ShellShutdownDialogの第3引数をFALSEにした場合はログオフの項目が 表示されるわけですが、GINAにおいての利用では常にユーザー名が空白で 表示されるように思えます。この問題は、第2引数にユーザー名を指定しても、 ログオンしたユーザーのコンテキストで関数を実行しても、 解決することはできませんでした。



戻る