mirror of
https://github.com/reactos/reactos.git
synced 2025-02-24 17:34:57 +00:00
Cleanup shutdown code path. Now, the user can logoff and logon again.
svn path=/trunk/; revision=23501
This commit is contained in:
parent
b229f6fcb2
commit
63e6cdc364
2 changed files with 188 additions and 113 deletions
|
@ -149,7 +149,10 @@ DoGenericAction(
|
||||||
Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
|
Session->Gina.Functions.WlxDisplaySASNotice(Session->Gina.Context);
|
||||||
}
|
}
|
||||||
if (WLX_SHUTTINGDOWN(wlxAction))
|
if (WLX_SHUTTINGDOWN(wlxAction))
|
||||||
|
{
|
||||||
|
Session->Gina.Functions.WlxShutdown(Session->Gina.Context, wlxAction);
|
||||||
HandleShutdown(Session, wlxAction);
|
HandleShutdown(Session, wlxAction);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WLX_SAS_ACTION_TASKLIST: /* 0x07 */
|
case WLX_SAS_ACTION_TASKLIST: /* 0x07 */
|
||||||
SwitchDesktop(WLSession->ApplicationDesktop);
|
SwitchDesktop(WLSession->ApplicationDesktop);
|
||||||
|
@ -256,128 +259,164 @@ typedef struct tagLOGOFF_SHUTDOWN_DATA
|
||||||
static DWORD WINAPI
|
static DWORD WINAPI
|
||||||
LogoffShutdownThread(LPVOID Parameter)
|
LogoffShutdownThread(LPVOID Parameter)
|
||||||
{
|
{
|
||||||
PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA) Parameter;
|
PLOGOFF_SHUTDOWN_DATA LSData = (PLOGOFF_SHUTDOWN_DATA) Parameter;
|
||||||
|
|
||||||
if (! ImpersonateLoggedOnUser(LSData->Session->UserToken))
|
if (!ImpersonateLoggedOnUser(LSData->Session->UserToken))
|
||||||
{
|
{
|
||||||
DPRINT1("ImpersonateLoggedOnUser failed with error %d\n", GetLastError());
|
ERR("ImpersonateLoggedOnUser failed with error %lu\n", GetLastError());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (! ExitWindowsEx(EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK)
|
|
||||||
| (EWX_LOGOFF == (LSData->Flags & EWX_ACTION_MASK) ? EWX_INTERNAL_FLAG_LOGOFF : 0),
|
|
||||||
0))
|
|
||||||
{
|
|
||||||
DPRINT1("Unable to kill user apps, error %d\n", GetLastError());
|
|
||||||
RevertToSelf();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
RevertToSelf();
|
|
||||||
|
|
||||||
/* This is not right (see top of reactos/dll/win32/user32/misc/exit.c),
|
/* Close processes of the interactive user */
|
||||||
* but this should be enough atm */
|
if (!ExitWindowsEx(
|
||||||
switch (LSData->Flags & EWX_ACTION_MASK)
|
EWX_INTERNAL_KILL_USER_APPS | (LSData->Flags & EWX_FLAGS_MASK) |
|
||||||
{
|
(EWX_LOGOFF == (LSData->Flags & EWX_ACTION_MASK) ? EWX_INTERNAL_FLAG_LOGOFF : 0),
|
||||||
case EWX_SHUTDOWN:
|
0))
|
||||||
NtShutdownSystem(ShutdownNoReboot);
|
{
|
||||||
break;
|
ERR("Unable to kill user apps, error %lu\n", GetLastError());
|
||||||
case EWX_REBOOT:
|
RevertToSelf();
|
||||||
NtShutdownSystem(ShutdownReboot);
|
return 0;
|
||||||
break;
|
}
|
||||||
default:
|
|
||||||
UNIMPLEMENTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
HeapFree(GetProcessHeap(), 0, LSData);
|
/* FIXME: Call ExitWindowsEx() to terminate COM processes */
|
||||||
|
|
||||||
return 1;
|
RevertToSelf();
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NTSTATUS
|
||||||
|
CheckPrivilegeForRequestedAction(
|
||||||
|
IN UINT Action,
|
||||||
|
IN DWORD RequestingProcessId)
|
||||||
|
{
|
||||||
|
HANDLE Process;
|
||||||
|
HANDLE Token;
|
||||||
|
BOOL CheckResult;
|
||||||
|
PPRIVILEGE_SET PrivSet;
|
||||||
|
|
||||||
|
TRACE("CheckPrivilegeForRequestedAction(%u)\n", Action);
|
||||||
|
|
||||||
|
if (Action == EWX_LOGOFF)
|
||||||
|
/* No privilege needed to log off */
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
|
||||||
|
/* Check for invalid arguments */
|
||||||
|
if (Action != EWX_SHUTDOWN && Action != EWX_REBOOT && Action != EWX_POWEROFF)
|
||||||
|
{
|
||||||
|
ERR("Invalid exit action %u\n", Action);
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, RequestingProcessId);
|
||||||
|
if (!Process)
|
||||||
|
{
|
||||||
|
WARN("OpenProcess() failed with error %lu\n", GetLastError());
|
||||||
|
return STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
if (!OpenProcessToken(Process, TOKEN_QUERY, &Token))
|
||||||
|
{
|
||||||
|
WARN("OpenProcessToken() failed with error %lu\n", GetLastError());
|
||||||
|
CloseHandle(Process);
|
||||||
|
return STATUS_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
CloseHandle(Process);
|
||||||
|
PrivSet = HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES));
|
||||||
|
if (!PrivSet)
|
||||||
|
{
|
||||||
|
ERR("Failed to allocate mem for privilege set\n");
|
||||||
|
CloseHandle(Token);
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
PrivSet->PrivilegeCount = 1;
|
||||||
|
PrivSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
|
||||||
|
if (!LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &PrivSet->Privilege[0].Luid))
|
||||||
|
{
|
||||||
|
WARN("LookupPrivilegeValue() failed with error %lu\n", GetLastError());
|
||||||
|
HeapFree(GetProcessHeap(), 0, PrivSet);
|
||||||
|
CloseHandle(Token);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
if (!PrivilegeCheck(Token, PrivSet, &CheckResult))
|
||||||
|
{
|
||||||
|
WARN("PrivilegeCheck() failed with error %lu\n", GetLastError());
|
||||||
|
HeapFree(GetProcessHeap(), 0, PrivSet);
|
||||||
|
CloseHandle(Token);
|
||||||
|
return STATUS_ACCESS_DENIED;
|
||||||
|
}
|
||||||
|
HeapFree(GetProcessHeap(), 0, PrivSet);
|
||||||
|
CloseHandle(Token);
|
||||||
|
|
||||||
|
if (!CheckResult)
|
||||||
|
{
|
||||||
|
WARN("SE_SHUTDOWN privilege not enabled\n");
|
||||||
|
return STATUS_ACCESS_DENIED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static LRESULT
|
static LRESULT
|
||||||
HandleExitWindows(PWLSESSION Session, DWORD RequestingProcessId, UINT Flags)
|
HandleExitWindows(
|
||||||
|
IN OUT PWLSESSION Session,
|
||||||
|
IN DWORD RequestingProcessId,
|
||||||
|
IN UINT Flags)
|
||||||
{
|
{
|
||||||
UINT Action;
|
UINT Action;
|
||||||
HANDLE Process;
|
PLOGOFF_SHUTDOWN_DATA LSData;
|
||||||
HANDLE Token;
|
HANDLE hThread;
|
||||||
HANDLE Thread;
|
NTSTATUS Status;
|
||||||
BOOL CheckResult;
|
|
||||||
PPRIVILEGE_SET PrivSet;
|
|
||||||
PLOGOFF_SHUTDOWN_DATA LSData;
|
|
||||||
|
|
||||||
/* Check parameters */
|
/* Check parameters */
|
||||||
Action = Flags & EWX_ACTION_MASK;
|
Action = Flags & EWX_ACTION_MASK;
|
||||||
if (EWX_LOGOFF != Action && EWX_SHUTDOWN != Action && EWX_REBOOT != Action
|
if (Action != EWX_LOGOFF &&
|
||||||
&& EWX_POWEROFF != Action)
|
Action != EWX_SHUTDOWN &&
|
||||||
{
|
Action != EWX_REBOOT &&
|
||||||
DPRINT1("Invalid ExitWindows action 0x%x\n", Action);
|
Action != EWX_POWEROFF)
|
||||||
return STATUS_INVALID_PARAMETER;
|
{
|
||||||
}
|
ERR("Invalid ExitWindows action 0x%x\n", Action);
|
||||||
|
return STATUS_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check privilege */
|
/* Check privilege */
|
||||||
if (EWX_LOGOFF != Action)
|
Status = CheckPrivilegeForRequestedAction(Action, RequestingProcessId);
|
||||||
{
|
if (!NT_SUCCESS(Status))
|
||||||
Process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, RequestingProcessId);
|
return Status;
|
||||||
if (NULL == Process)
|
|
||||||
{
|
|
||||||
DPRINT1("OpenProcess failed with error %d\n", GetLastError());
|
|
||||||
return STATUS_INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
if (! OpenProcessToken(Process, TOKEN_QUERY, &Token))
|
|
||||||
{
|
|
||||||
DPRINT1("OpenProcessToken failed with error %d\n", GetLastError());
|
|
||||||
CloseHandle(Process);
|
|
||||||
return STATUS_INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
CloseHandle(Process);
|
|
||||||
PrivSet = HeapAlloc(GetProcessHeap(), 0, sizeof(PRIVILEGE_SET) + sizeof(LUID_AND_ATTRIBUTES));
|
|
||||||
if (NULL == PrivSet)
|
|
||||||
{
|
|
||||||
DPRINT1("Failed to allocate mem for privilege set\n");
|
|
||||||
CloseHandle(Token);
|
|
||||||
return STATUS_NO_MEMORY;
|
|
||||||
}
|
|
||||||
PrivSet->PrivilegeCount = 1;
|
|
||||||
PrivSet->Control = PRIVILEGE_SET_ALL_NECESSARY;
|
|
||||||
if (! LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &PrivSet->Privilege[0].Luid))
|
|
||||||
{
|
|
||||||
DPRINT1("LookupPrivilegeValue failed with error %d\n", GetLastError());
|
|
||||||
HeapFree(GetProcessHeap(), 0, PrivSet);
|
|
||||||
CloseHandle(Token);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
if (! PrivilegeCheck(Token, PrivSet, &CheckResult))
|
|
||||||
{
|
|
||||||
DPRINT1("PrivilegeCheck failed with error %d\n", GetLastError());
|
|
||||||
HeapFree(GetProcessHeap(), 0, PrivSet);
|
|
||||||
CloseHandle(Token);
|
|
||||||
return STATUS_ACCESS_DENIED;
|
|
||||||
}
|
|
||||||
HeapFree(GetProcessHeap(), 0, PrivSet);
|
|
||||||
CloseHandle(Token);
|
|
||||||
if (! CheckResult)
|
|
||||||
{
|
|
||||||
DPRINT1("SE_SHUTDOWN privilege not enabled\n");
|
|
||||||
return STATUS_ACCESS_DENIED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
|
/* Prepare data for logoff/shutdown thread */
|
||||||
if (NULL == LSData)
|
LSData = HeapAlloc(GetProcessHeap(), 0, sizeof(LOGOFF_SHUTDOWN_DATA));
|
||||||
{
|
if (!LSData)
|
||||||
DPRINT1("Failed to allocate mem for thread data\n");
|
{
|
||||||
return STATUS_NO_MEMORY;
|
ERR("Failed to allocate mem for thread data\n");
|
||||||
}
|
return STATUS_NO_MEMORY;
|
||||||
LSData->Flags = Flags;
|
}
|
||||||
LSData->Session = Session;
|
LSData->Flags = Flags;
|
||||||
Thread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID) LSData, 0, NULL);
|
LSData->Session = Session;
|
||||||
if (NULL == Thread)
|
|
||||||
{
|
|
||||||
DPRINT1("Unable to create shutdown thread, error %d\n", GetLastError());
|
|
||||||
HeapFree(GetProcessHeap(), 0, LSData);
|
|
||||||
return STATUS_UNSUCCESSFUL;
|
|
||||||
}
|
|
||||||
CloseHandle(Thread);
|
|
||||||
|
|
||||||
return 1;
|
/* Run logoff/shutdown thread */
|
||||||
|
hThread = CreateThread(NULL, 0, LogoffShutdownThread, (LPVOID)LSData, 0, NULL);
|
||||||
|
if (!hThread)
|
||||||
|
{
|
||||||
|
ERR("Unable to create shutdown thread, error %lu\n", GetLastError());
|
||||||
|
HeapFree(GetProcessHeap(), 0, LSData);
|
||||||
|
return STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
WaitForSingleObject(hThread, INFINITE);
|
||||||
|
CloseHandle(hThread);
|
||||||
|
HeapFree(GetProcessHeap(), 0, LSData);
|
||||||
|
|
||||||
|
Session->LogonStatus = WKSTA_IS_LOGGED_OFF;
|
||||||
|
if (Action == EWX_LOGOFF)
|
||||||
|
{
|
||||||
|
DispatchSAS(Session, WLX_SAS_TYPE_TIMEOUT);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle shutdown */
|
||||||
|
FIXME("FIXME: Call ExitWindowEx in SYSTEM process context\n");
|
||||||
|
|
||||||
|
FIXME("FIXME: Call SMSS API #1\n");
|
||||||
|
NtShutdownSystem(ShutdownNoReboot); /* FIXME: should go in smss */
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LRESULT CALLBACK
|
static LRESULT CALLBACK
|
||||||
|
|
|
@ -57,6 +57,9 @@ WlxUseCtrlAltDel(
|
||||||
HANDLE hWlx)
|
HANDLE hWlx)
|
||||||
{
|
{
|
||||||
ULONG_PTR OldValue;
|
ULONG_PTR OldValue;
|
||||||
|
|
||||||
|
TRACE("WlxUseCtrlAltDel()\n");
|
||||||
|
|
||||||
WlxSetOption(hWlx, WLX_OPTION_USE_CTRL_ALT_DEL, TRUE, &OldValue);
|
WlxSetOption(hWlx, WLX_OPTION_USE_CTRL_ALT_DEL, TRUE, &OldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +72,9 @@ WlxSetContextPointer(
|
||||||
PVOID pWlxContext)
|
PVOID pWlxContext)
|
||||||
{
|
{
|
||||||
ULONG_PTR OldValue;
|
ULONG_PTR OldValue;
|
||||||
|
|
||||||
|
TRACE("WlxSetContextPointer(%p)\n", pWlxContext);
|
||||||
|
|
||||||
WlxSetOption(hWlx, WLX_OPTION_CONTEXT_POINTER, (ULONG_PTR)pWlxContext, &OldValue);
|
WlxSetOption(hWlx, WLX_OPTION_CONTEXT_POINTER, (ULONG_PTR)pWlxContext, &OldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +86,7 @@ WlxSasNotify(
|
||||||
HANDLE hWlx,
|
HANDLE hWlx,
|
||||||
DWORD dwSasType)
|
DWORD dwSasType)
|
||||||
{
|
{
|
||||||
|
TRACE("WlxSasNotify(0x%lx)\n", dwSasType);
|
||||||
DispatchSAS((PWLSESSION)hWlx, dwSasType);
|
DispatchSAS((PWLSESSION)hWlx, dwSasType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +99,9 @@ WlxSetTimeout(
|
||||||
DWORD Timeout)
|
DWORD Timeout)
|
||||||
{
|
{
|
||||||
PWLSESSION Session = (PWLSESSION)hWlx;
|
PWLSESSION Session = (PWLSESSION)hWlx;
|
||||||
|
|
||||||
|
TRACE("WlxSetTimeout(%lu)\n", Timeout);
|
||||||
|
|
||||||
Session->DialogTimeout = Timeout;
|
Session->DialogTimeout = Timeout;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -121,6 +131,7 @@ WlxMessageBox(
|
||||||
LPWSTR lpszTitle,
|
LPWSTR lpszTitle,
|
||||||
UINT fuStyle)
|
UINT fuStyle)
|
||||||
{
|
{
|
||||||
|
TRACE("WlxMessageBox()\n");
|
||||||
/* FIXME: Provide a custom window proc to be able to handle timeout */
|
/* FIXME: Provide a custom window proc to be able to handle timeout */
|
||||||
return MessageBoxW(hwndOwner, lpszText, lpszTitle, fuStyle);
|
return MessageBoxW(hwndOwner, lpszText, lpszTitle, fuStyle);
|
||||||
}
|
}
|
||||||
|
@ -136,6 +147,8 @@ WlxDialogBox(
|
||||||
HWND hwndOwner,
|
HWND hwndOwner,
|
||||||
DLGPROC dlgprc)
|
DLGPROC dlgprc)
|
||||||
{
|
{
|
||||||
|
TRACE("WlxDialogBox()\n");
|
||||||
|
|
||||||
if (PreviousWindowProc != NULL)
|
if (PreviousWindowProc != NULL)
|
||||||
return -1;
|
return -1;
|
||||||
PreviousWindowProc = dlgprc;
|
PreviousWindowProc = dlgprc;
|
||||||
|
@ -154,6 +167,8 @@ WlxDialogBoxParam(
|
||||||
DLGPROC dlgprc,
|
DLGPROC dlgprc,
|
||||||
LPARAM dwInitParam)
|
LPARAM dwInitParam)
|
||||||
{
|
{
|
||||||
|
TRACE("WlxDialogBoxParam()\n");
|
||||||
|
|
||||||
if (PreviousWindowProc != NULL)
|
if (PreviousWindowProc != NULL)
|
||||||
return -1;
|
return -1;
|
||||||
PreviousWindowProc = dlgprc;
|
PreviousWindowProc = dlgprc;
|
||||||
|
@ -171,6 +186,8 @@ WlxDialogBoxIndirect(
|
||||||
HWND hwndOwner,
|
HWND hwndOwner,
|
||||||
DLGPROC dlgprc)
|
DLGPROC dlgprc)
|
||||||
{
|
{
|
||||||
|
TRACE("WlxDialogBoxIndirect()\n");
|
||||||
|
|
||||||
if (PreviousWindowProc != NULL)
|
if (PreviousWindowProc != NULL)
|
||||||
return -1;
|
return -1;
|
||||||
PreviousWindowProc = dlgprc;
|
PreviousWindowProc = dlgprc;
|
||||||
|
@ -189,6 +206,8 @@ WlxDialogBoxIndirectParam(
|
||||||
DLGPROC dlgprc,
|
DLGPROC dlgprc,
|
||||||
LPARAM dwInitParam)
|
LPARAM dwInitParam)
|
||||||
{
|
{
|
||||||
|
TRACE("WlxDialogBoxIndirectParam()\n");
|
||||||
|
|
||||||
if (PreviousWindowProc != NULL)
|
if (PreviousWindowProc != NULL)
|
||||||
return -1;
|
return -1;
|
||||||
PreviousWindowProc = dlgprc;
|
PreviousWindowProc = dlgprc;
|
||||||
|
@ -203,6 +222,9 @@ WlxSwitchDesktopToUser(
|
||||||
HANDLE hWlx)
|
HANDLE hWlx)
|
||||||
{
|
{
|
||||||
PWLSESSION Session = (PWLSESSION)hWlx;
|
PWLSESSION Session = (PWLSESSION)hWlx;
|
||||||
|
|
||||||
|
TRACE("WlxSwitchDesktopToUser()\n");
|
||||||
|
|
||||||
return (int)SwitchDesktop(Session->ApplicationDesktop);
|
return (int)SwitchDesktop(Session->ApplicationDesktop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,6 +236,9 @@ WlxSwitchDesktopToWinlogon(
|
||||||
HANDLE hWlx)
|
HANDLE hWlx)
|
||||||
{
|
{
|
||||||
PWLSESSION Session = (PWLSESSION)hWlx;
|
PWLSESSION Session = (PWLSESSION)hWlx;
|
||||||
|
|
||||||
|
TRACE("WlxSwitchDesktopToWinlogon()\n");
|
||||||
|
|
||||||
return (int)SwitchDesktop(Session->WinlogonDesktop);
|
return (int)SwitchDesktop(Session->WinlogonDesktop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +334,8 @@ WlxSetOption(
|
||||||
{
|
{
|
||||||
PWLSESSION Session = (PWLSESSION)hWlx;
|
PWLSESSION Session = (PWLSESSION)hWlx;
|
||||||
|
|
||||||
|
TRACE("WlxSetOption(%lu)\n", Option);
|
||||||
|
|
||||||
switch (Option)
|
switch (Option)
|
||||||
{
|
{
|
||||||
case WLX_OPTION_USE_CTRL_ALT_DEL:
|
case WLX_OPTION_USE_CTRL_ALT_DEL:
|
||||||
|
@ -338,6 +365,8 @@ WlxGetOption(
|
||||||
{
|
{
|
||||||
PWLSESSION Session = (PWLSESSION)hWlx;
|
PWLSESSION Session = (PWLSESSION)hWlx;
|
||||||
|
|
||||||
|
TRACE("WlxGetOption(%lu)\n", Option);
|
||||||
|
|
||||||
switch (Option)
|
switch (Option)
|
||||||
{
|
{
|
||||||
case WLX_OPTION_USE_CTRL_ALT_DEL:
|
case WLX_OPTION_USE_CTRL_ALT_DEL:
|
||||||
|
@ -460,7 +489,7 @@ WlxQueryTsLogonCredentials(
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const
|
static
|
||||||
WLX_DISPATCH_VERSION_1_4 FunctionTable = {
|
WLX_DISPATCH_VERSION_1_4 FunctionTable = {
|
||||||
WlxUseCtrlAltDel,
|
WlxUseCtrlAltDel,
|
||||||
WlxSetContextPointer,
|
WlxSetContextPointer,
|
||||||
|
@ -559,8 +588,15 @@ LoadGina(
|
||||||
/* Assume current version */
|
/* Assume current version */
|
||||||
*DllVersion = WLX_CURRENT_VERSION;
|
*DllVersion = WLX_CURRENT_VERSION;
|
||||||
}
|
}
|
||||||
else if (!Functions->WlxNegotiate(WLX_CURRENT_VERSION, DllVersion))
|
else
|
||||||
goto cleanup;
|
{
|
||||||
|
TRACE("About to negociate with Gina %S. Winlogon uses version %lx\n",
|
||||||
|
GinaDll, WLX_CURRENT_VERSION);
|
||||||
|
if (!Functions->WlxNegotiate(WLX_CURRENT_VERSION, DllVersion))
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("Gina uses WLX_VERSION %lx\n", *DllVersion);
|
||||||
|
|
||||||
if (*DllVersion >= WLX_VERSION_1_0)
|
if (*DllVersion >= WLX_VERSION_1_0)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue