diff --git a/modules/rostests/win32/winlogon/wlntfytests/minitest.h b/modules/rostests/win32/winlogon/wlntfytests/minitest.h new file mode 100644 index 00000000000..e2ec5f9f69d --- /dev/null +++ b/modules/rostests/win32/winlogon/wlntfytests/minitest.h @@ -0,0 +1,589 @@ +/* + * PROJECT: ReactOS Tests + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: Lightweight testing routines, based on an updated version of wine/test.h + * COPYRIGHT: Copyright 2025 Hermès Bélusca-Maïto + */ + +#ifndef __MINI_WINE_TEST_H +#define __MINI_WINE_TEST_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* debug level */ +extern int winetest_debug; + +extern int report_success; + +/* running in interactive mode? */ +extern int winetest_interactive; + +/* current platform */ +extern const char *winetest_platform; + +extern void winetest_set_location( const char* file, int line ); +extern void winetest_subtest(const char* name); +extern void winetest_start_todo( int is_todo ); +extern int winetest_loop_todo(void); +extern void winetest_end_todo(void); +extern void winetest_start_nocount(unsigned int flags); +extern int winetest_loop_nocount(void); +extern void winetest_end_nocount(void); +#if 0 +extern int winetest_get_mainargs( char*** pargv ); +#endif +extern LONG winetest_get_failures(void); +extern LONG winetest_get_successes(void); +extern void winetest_add_failures( LONG new_failures ); + +extern int broken( int condition ); +extern int winetest_vok( int condition, const char *msg, va_list ap ); +extern void winetest_vskip( const char *msg, va_list ap ); + +extern void __cdecl winetest_ok( int condition, const char *msg, ... ) __WINE_PRINTF_ATTR(2,3); +extern void __cdecl winetest_skip( const char *msg, ... ) __WINE_PRINTF_ATTR(1,2); +extern void __cdecl winetest_win_skip( const char *msg, ... ) __WINE_PRINTF_ATTR(1,2); +extern void __cdecl winetest_trace( const char *msg, ... ) __WINE_PRINTF_ATTR(1,2); +extern void __cdecl winetest_push_context( const char *fmt, ... ) __WINE_PRINTF_ATTR(1,2); +extern void winetest_pop_context(void); + +#define subtest_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_subtest +#define ok_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_ok +#define skip_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_skip +#define win_skip_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_win_skip +#define trace_(file, line) (winetest_set_location(file, line), 0) ? (void)0 : winetest_trace + +#define subtest subtest_(__RELFILE__, __LINE__) +#define ok ok_(__RELFILE__, __LINE__) +#define skip skip_(__RELFILE__, __LINE__) +#define win_skip win_skip_(__RELFILE__, __LINE__) +#define trace trace_(__RELFILE__, __LINE__) + +#define todo_if(is_todo) for (winetest_start_todo(is_todo); \ + winetest_loop_todo(); \ + winetest_end_todo()) + +#define todo_ros todo_if(!strcmp(winetest_platform, "reactos")) +#define todo_ros_if(is_todo) todo_if((is_todo) && !strcmp(winetest_platform, "reactos")) +#ifdef USE_WINE_TODOS +#define todo_wine todo_ros +#define todo_wine_if todo_ros_if +#else +#define todo_wine todo_if(!strcmp(winetest_platform, "wine")) +#define todo_wine_if(is_todo) todo_if((is_todo) && !strcmp(winetest_platform, "wine")) +#endif + +#define ros_skip_flaky for (winetest_start_nocount(3); \ + winetest_loop_nocount(); \ + winetest_end_nocount()) + +#define disable_success_count for (winetest_start_nocount(1); \ + winetest_loop_nocount(); \ + winetest_end_nocount()) + +#define skip_2k3_crash if (_winver < 0x600) skip("Test skipped, because it crashes on win 2003\n"); else +#define skip_2k3_fail if (_winver < 0x600) skip("Test skipped, because it fails on win 2003\n"); else + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + + +/************************************************************************/ +/* Below is the implementation of the various functions, to be included + * directly into the generated testlist.c file. + * It is done that way so that the dlls can build the test routines with + * different includes or flags if needed. + */ + +#ifdef STANDALONE + +#include + +/* Define WINETEST_MSVC_IDE_FORMATTING to alter the output format winetest will use for file/line numbers. + This alternate format makes the file/line numbers clickable in visual studio, to directly jump to them. */ +#if defined(WINETEST_MSVC_IDE_FORMATTING) +# define __winetest_file_line_prefix "%s(%d)" +#else +# define __winetest_file_line_prefix "%s:%d" +#endif + +struct test +{ + const char *name; + void (*func)(void); +}; + +extern const struct test winetest_testlist[]; + +/* debug level */ +int winetest_debug = 1; + +/* interactive mode? */ +int winetest_interactive = 0; + +/* current platform */ +const char *winetest_platform = "windows"; + +/* report successful tests (BOOL) */ +int report_success = 0; + +#if 0 +/* passing arguments around */ +static int winetest_argc; +static char** winetest_argv; +#endif + +static const struct test *current_test; /* test currently being run */ + +static LONG winetest_successes; /* number of successful tests */ +static LONG winetest_failures; /* number of failures */ +static LONG winetest_skipped; /* number of skipped test chunks */ +static LONG winetest_todo_successes; /* number of successful tests inside todo block */ +static LONG winetest_todo_failures; /* number of failures inside todo block */ + +/* The following data must be kept track of on a per-thread basis */ +struct winetest_thread_data +{ + const char* current_file; /* file of current check */ + int current_line; /* line of current check */ + unsigned int todo_level; /* current todo nesting level */ + unsigned int nocount_level; + int todo_do_loop; +#if 0 + char *str_pos; /* position in debug buffer */ + char strings[2000]; /* buffer for debug strings */ +#endif + char context[8][128]; /* data to print before messages */ + unsigned int context_count; /* number of context prefixes */ +} tls_data; + +static DWORD tls_index; + +static struct winetest_thread_data *winetest_get_thread_data(void) +{ + struct winetest_thread_data *data; + DWORD last_error; + + last_error = GetLastError(); + data = TlsGetValue( tls_index ); + if (!data) + { + data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) ); +#if 0 + data->str_pos = data->strings; +#endif + TlsSetValue( tls_index, data ); + } + SetLastError( last_error ); + return data; +} + +static int winetest_vprintf( const char *msg, va_list args ) +{ + static struct __wine_debug_functions s_Debug = {NULL}; + if (!s_Debug.dbg_vprintf) + __wine_dbg_set_functions(NULL, &s_Debug, sizeof(s_Debug)); + + return s_Debug.dbg_vprintf( msg, args ); +} + +void winetest_set_location( const char* file, int line ) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); +#if 1 /*|| defined(WINETEST_MSVC_IDE_FORMATTING)*/ + data->current_file = file; +#else + data->current_file=strrchr(file,'/'); + if (data->current_file==NULL) + data->current_file=strrchr(file,'\\'); + if (data->current_file==NULL) + data->current_file=file; + else + data->current_file++; +#endif + data->current_line=line; +} + +static int __cdecl winetest_printf( const char *msg, ... ) __WINE_PRINTF_ATTR(1,2); +static int __cdecl winetest_printf( const char *msg, ... ) +{ + va_list valist; + int ret; + + va_start( valist, msg ); + ret = winetest_vprintf( msg, valist ); + va_end( valist ); + + return ret; +} + +static void __cdecl winetest_print_location( const char *msg, ... ) __WINE_PRINTF_ATTR(1,2); +static void __cdecl winetest_print_location( const char *msg, ... ) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + // char elapsed[64]; + va_list valist; + + // winetest_printf( "%s:%d:%s ", data->current_file, data->current_line, winetest_elapsed( elapsed ) ); + winetest_printf( __winetest_file_line_prefix ": ", data->current_file, data->current_line ); + va_start( valist, msg ); + winetest_vprintf( msg, valist ); + va_end( valist ); +} + +static void __cdecl winetest_print_context( const char *msgtype ) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + unsigned int i; + + winetest_print_location( "%s", msgtype ); + for (i = 0; i < data->context_count; ++i) + winetest_printf( "%s: ", data->context[i] ); +} + +void winetest_subtest(const char* name) +{ + winetest_print_location( "Subtest %s\n", name ); +} + +int broken( int condition ) +{ + return ((strcmp(winetest_platform, "windows") == 0) +#ifndef USE_WINE_TODOS + || (strcmp(winetest_platform, "reactos") == 0) +#endif + ) && condition; +} + +/* + * Checks condition. + * Parameters: + * - condition - condition to check; + * - msg test description; + * - file - test application source code file name of the check + * - line - test application source code file line number of the check + * Return: + * 0 if condition does not have the expected value, 1 otherwise + */ +int winetest_vok( int condition, const char *msg, va_list args ) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + + if (data->todo_level) + { + if (condition) + { + winetest_print_context( "Test succeeded inside todo block: " ); + winetest_vprintf( msg, args ); + if ((data->nocount_level & 2) == 0) + InterlockedIncrement( &winetest_todo_failures ); + return 0; + } + else + { + /* show todos even if traces are disabled*/ + /*if (winetest_debug > 0)*/ + { + winetest_print_context( "Test marked todo: " ); + winetest_vprintf( msg, args ); + } + if ((data->nocount_level & 1) == 0) + InterlockedIncrement( &winetest_todo_successes ); + return 1; + } + } + else + { + if (!condition) + { + winetest_print_context( "Test failed: " ); + winetest_vprintf( msg, args ); + if ((data->nocount_level & 2) == 0) + InterlockedIncrement( &winetest_failures ); + return 0; + } + else + { + if (report_success && (data->nocount_level & 1) == 0) + { + winetest_print_location("Test succeeded\n"); + } + if ((data->nocount_level & 1) == 0) + InterlockedIncrement( &winetest_successes ); + return 1; + } + } +} + +void __cdecl winetest_ok( int condition, const char *msg, ... ) +{ + va_list valist; + + va_start(valist, msg); + winetest_vok(condition, msg, valist); + va_end(valist); +} + +void __cdecl winetest_trace( const char *msg, ... ) +{ + va_list valist; + + if (winetest_debug > 0) + { + winetest_print_context( "" ); + va_start(valist, msg); + winetest_vprintf( msg, valist ); + va_end(valist); + } +} + +void winetest_vskip( const char *msg, va_list args ) +{ + winetest_print_context( "Tests skipped: " ); + winetest_vprintf( msg, args ); + InterlockedIncrement( &winetest_skipped ); +} + +void __cdecl winetest_skip( const char *msg, ... ) +{ + va_list valist; + va_start(valist, msg); + winetest_vskip(msg, valist); + va_end(valist); +} + +void __cdecl winetest_win_skip( const char *msg, ... ) +{ + va_list valist; + va_start(valist, msg); + if ((strcmp(winetest_platform, "windows") == 0) +#if !defined(USE_WINE_TODOS) || defined(USE_WIN_SKIP) + || (strcmp(winetest_platform, "reactos") == 0) +#endif + ) + winetest_vskip(msg, valist); + else + winetest_vok(0, msg, valist); + va_end(valist); +} + +void winetest_start_todo( int is_todo ) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + data->todo_level = (data->todo_level << 1) | (is_todo != 0); + data->todo_do_loop=1; +} + +int winetest_loop_todo(void) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + int do_loop=data->todo_do_loop; + data->todo_do_loop=0; + return do_loop; +} + +void winetest_end_todo(void) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + data->todo_level >>= 1; +} + +void winetest_start_nocount(unsigned int flags) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + + /* The lowest 2 bits of nocount_level specify whether counting of successes + and/or failures is disabled. For each nested level the bits are shifted + left, the new lowest 2 bits are copied from the previous state and ored + with the new mask. This allows nested handling of both states up tp a + level of 16. */ + flags |= data->nocount_level & 3; + data->nocount_level = (data->nocount_level << 2) | flags; + data->todo_do_loop = 1; +} + +int winetest_loop_nocount(void) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + int do_loop = data->todo_do_loop; + data->todo_do_loop = 0; + return do_loop; +} + +void winetest_end_nocount(void) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + data->nocount_level >>= 2; +} + +void __cdecl winetest_push_context(const char* fmt, ...) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + va_list valist; + + if (data->context_count < ARRAY_SIZE(data->context)) + { + va_start(valist, fmt); + vsnprintf(data->context[data->context_count], sizeof(data->context[data->context_count]), fmt, valist); + va_end(valist); + data->context[data->context_count][sizeof(data->context[data->context_count]) - 1] = 0; + } + ++data->context_count; +} + +void winetest_pop_context(void) +{ + struct winetest_thread_data *data = winetest_get_thread_data(); + + if (data->context_count) + --data->context_count; +} + +#if 0 +int winetest_get_mainargs( char*** pargv ) +{ + *pargv = winetest_argv; + return winetest_argc; +} +#endif + +LONG winetest_get_failures(void) +{ + return winetest_failures; +} + +LONG winetest_get_successes(void) +{ + return winetest_successes; +} + +void winetest_add_failures( LONG new_failures ) +{ + while (new_failures-- > 0) + InterlockedIncrement( &winetest_failures ); +} + +#if 0 +static char *_strdup(const char *str) +{ + char* ptr = malloc((strlen(str)+1)*sizeof(char)); + if (ptr) + strcpy(ptr, str); + return ptr; +} +#endif + +/* Initialize testing support */ +static void init_test( const struct test *test ) +{ + char p[128]; + +#if 0 + winetest_argc = __argc; + winetest_argv = __argv; +#endif + + // if (GetEnvironmentVariableA( "WINETEST_PLATFORM", p, sizeof(p) )) winetest_platform = _strdup(p); + if (GetEnvironmentVariableA( "WINETEST_DEBUG", p, sizeof(p) )) winetest_debug = atoi(p); + if (GetEnvironmentVariableA( "WINETEST_INTERACTIVE", p, sizeof(p) )) winetest_interactive = atoi(p); + if (GetEnvironmentVariableA( "WINETEST_REPORT_SUCCESS", p, sizeof(p) )) report_success = atoi(p); + + if (!winetest_interactive) SetErrorMode( SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX ); + + current_test = test; + winetest_successes = winetest_failures = winetest_skipped = winetest_todo_successes = winetest_todo_failures = 0; + tls_index = TlsAlloc(); +} + +/* Executed once test is finished: shows results and returns exit status */ +static int fini_test(void) +{ + int status; + const struct test *test = current_test; + + /* show test results even if traces are disabled */ + /*if (winetest_debug)*/ + { + //winetest_printf + winetest_print_location( "\n%s: %d tests executed (%d marked as todo, %d %s), %d skipped.\n", + test->name, (int)(winetest_successes + winetest_failures + winetest_todo_successes + winetest_todo_failures), + (int)winetest_todo_successes, (int)(winetest_failures + winetest_todo_failures), + (winetest_failures + winetest_todo_failures != 1) ? "failures" : "failure", + (int)winetest_skipped ); + } + status = (winetest_failures + winetest_todo_failures < 255) ? winetest_failures + winetest_todo_failures : 255; + return status; +} + +#endif /* STANDALONE */ + +// Some helpful definitions + +#define ok_hex_(file, line, expression, result) \ + do { \ + int _value = (expression); \ + int _result = (result); \ + ok_(file, line)(_value == _result, "Wrong value for '%s', expected: " #result " (0x%x), got: 0x%x\n", \ + #expression, _result, _value); \ + } while (0) +#define ok_hex(expression, result) ok_hex_(__RELFILE__, __LINE__, expression, result) + +#define ok_dec_(file, line, expression, result) \ + do { \ + int _value = (expression); \ + int _result = (result); \ + ok_(file, line)(_value == _result, "Wrong value for '%s', expected: " #result " (%d), got: %d\n", \ + #expression, _result, _value); \ + } while (0) +#define ok_dec(expression, result) ok_dec_(__RELFILE__, __LINE__, expression, result) + +#define ok_ptr_(file, line, expression, result) \ + do { \ + const void *_value = (expression); \ + const void *_result = (result); \ + ok_(file, line)(_value == _result, "Wrong value for '%s', expected: " #result " (%p), got: %p\n", \ + #expression, _result, _value); \ + } while (0) +#define ok_ptr(expression, result) ok_ptr_(__RELFILE__, __LINE__, expression, result) + +#define ok_size_t_(file, line, expression, result) \ + do { \ + size_t _value = (expression); \ + size_t _result = (result); \ + ok_(file, line)(_value == _result, "Wrong value for '%s', expected: " #result " (%Ix), got: %Ix\n", \ + #expression, _result, _value); \ + } while (0) +#define ok_size_t(expression, result) ok_size_t_(__RELFILE__, __LINE__, expression, result) + +#define ok_char(expression, result) ok_hex(expression, result) + +#define ok_err_(file, line, error) \ + ok_(file, line)(GetLastError() == (error), "Wrong last error. Expected " #error ", got 0x%lx\n", GetLastError()) +#define ok_err(error) ok_err_(__RELFILE__, __LINE__, error) + +#define ok_str_(file, line, x, y) \ + ok_(file, line)(strcmp(x, y) == 0, "Wrong string. Expected '%s', got '%s'\n", y, x) +#define ok_str(x, y) ok_str_(__RELFILE__, __LINE__, x, y) + +#define ok_wstr_(file, line, x, y) \ + ok_(file, line)(wcscmp(x, y) == 0, "Wrong string. Expected '%S', got '%S'\n", y, x) +#define ok_wstr(x, y) ok_wstr_(__RELFILE__, __LINE__, x, y) + +#define ok_long(expression, result) ok_hex(expression, result) +#define ok_int(expression, result) ok_dec(expression, result) +#define ok_int_(file, line, expression, result) ok_dec_(file, line, expression, result) +#define ok_ntstatus(status, expected) ok_hex(status, expected) +#define ok_hdl ok_ptr + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __MINI_WINE_TEST_H */ diff --git a/modules/rostests/win32/winlogon/wlntfytests/wlntfytests.c b/modules/rostests/win32/winlogon/wlntfytests/wlntfytests.c index 68684b42555..24d1f9a5729 100644 --- a/modules/rostests/win32/winlogon/wlntfytests/wlntfytests.c +++ b/modules/rostests/win32/winlogon/wlntfytests/wlntfytests.c @@ -10,6 +10,8 @@ * - https://rsdn.org/article/baseserv/winlogon.xml */ +/* HEADERS *******************************************************************/ + /* C Headers */ #include @@ -27,11 +29,28 @@ #include -#include -WINE_DEFAULT_DEBUG_CHANNEL(wlnotify_tests); +#define STANDALONE +#include "minitest.h" // Includes also -#define NOTIFY_PKG_NAME L"WLNotifyTests" -#define NOTIFY_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\" NOTIFY_PKG_NAME +#define WINE_DEFAULT_DEBUG_CHANNEL_EX(ch, flags) \ + static struct __wine_debug_channel __wine_dbch_##ch = { (unsigned char)(flags), #ch }; \ + static struct __wine_debug_channel * const __wine_dbch___default = &__wine_dbch_##ch + +// WINE_DEFAULT_DEBUG_CHANNEL(wlnotify_tests); +WINE_DEFAULT_DEBUG_CHANNEL_EX(wlnotify_tests, + (1 << __WINE_DBCL_TRACE) | (1 << __WINE_DBCL_WARN) | + (1 << __WINE_DBCL_ERR) | (1 << __WINE_DBCL_FIXME)); + + +/* GLOBALS *******************************************************************/ + +#define NOTIFY_PKG_NAME L"WLNotifyTests" +#define NOTIFY_REG_PATH L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\" NOTIFY_PKG_NAME + +#define DEFAULT_WINSTA0 L"WinSta0" +#define DESKTOP_WINLOGON L"Winlogon" +#define DESKTOP_DEFAULT L"Default" +#define DESKTOP_SCRSAVE L"Screen-saver" typedef enum _WLNOTIFY_STATE { @@ -70,7 +89,40 @@ static const PCSTR NotifyStateName[] = static WLNOTIFY_STATE g_CurrentState = WLNotify_NonInitialized; static WLNOTIFY_STATE g_PreviousState = WLNotify_NonInitialized; +static ULONG g_fLogoffShutdownFlags = EWX_LOGOFF; static HMODULE g_hModule = NULL; +static BOOL g_bInitialized = FALSE; ///< TRUE when the flags have been initialized. +static struct { + UINT8 bAsync : 1; + UINT8 bImpersonate : 1; + UINT8 Reserved : 6; +} g_fFlags = {0}; + +FORCEINLINE +BOOL +IsUserLoggedIn( + _In_ WLNOTIFY_STATE State) +{ + /* + * - The user is not logged in when State is either of: + * WLNotify_NonInitialized, WLNotify_Startup, WLNotify_Shutdown, + * WLNotify_Logoff, and perhaps (TODO TBD.): WLNotify_Disconnect, + * WLNotify_Reconnect. + * + * - The user is logged in when State is either of: + * WLNotify_Logon, WLNotify_StartShell, WLNotify_PostShell, + * WLNotify_Lock, WLNotify_Unlock. + * + * Note that WLNotify_StartScreenSaver and WLNotify_StopScreenSaver + * are not reliable for this test, because they can be invoked from + * either a logged-off or a logged-on state. + */ + return ((State == WLNotify_Logon) || + (State >= WLNotify_StartShell && State <= WLNotify_Unlock)); +} + + +/* DEBUGGING HELPERS *********************************************************/ #if 0 void DPRINTF_FN(PCSTR FunctionName, PCWSTR Format, ...) @@ -122,6 +174,71 @@ NotifyStateToName( return NotifyStateName[i]; } + +/* SYSTEM INFORMATION HELPERS ************************************************/ + +/** + * @brief + * Retrieves the notification settings from the registry. + **/ +static VOID +GetSettings(VOID) +{ + HKEY hNotifyKey; + LSTATUS lError; + DWORD dwValue, dwSize, dwType; + + if (InterlockedCompareExchange((PLONG)&g_bInitialized, TRUE, FALSE)) + return; + + lError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + NOTIFY_REG_PATH, + 0, + KEY_QUERY_VALUE, + &hNotifyKey); + if (lError != ERROR_SUCCESS) + { + ERR("RegOpenKeyExW() failed; error %lu\n", lError); + return; // HRESULT_FROM_WIN32(lError); + } + + dwSize = sizeof(dwValue); + lError = RegQueryValueExW(hNotifyKey, + L"Asynchronous", + NULL, + &dwType, + (PBYTE)&dwValue, + &dwSize); + if ((lError == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwSize == sizeof(dwValue))) + g_fFlags.bAsync = !!dwValue; + + dwSize = sizeof(dwValue); + lError = RegQueryValueExW(hNotifyKey, + L"Impersonate", + NULL, + &dwType, + (PBYTE)&dwValue, + &dwSize); + if ((lError == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwSize == sizeof(dwValue))) + g_fFlags.bImpersonate = !!dwValue; + +#if 0 + dwSize = sizeof(dwValue); + lError = RegQueryValueExW(hNotifyKey, + L"MaxWait", + NULL, + &dwType, + (PBYTE)&dwValue, + &dwSize); + if ((lError == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwSize == sizeof(dwValue))) + g_dwMaxWait = !!dwValue; +#endif + + RegCloseKey(hNotifyKey); + + return; // S_OK; +} + /** * @brief * Retrieves the name of the specified window station or desktop object. @@ -190,75 +307,349 @@ GetUserObjectName( return pszBuffer; } +/** + * @brief + * Retrieves the user and domain names corresponding to the given token. + * + * @param[in] hToken + * Handle to the user token, for which to retrieve the user and domain names. + * + * @param[out] UserName + * Receives in output, a pointer to an allocated string representing + * the user name. The string is allocated with LocalAlloc(). After usage, + * free the pointer with LocalFree(). + * + * @param[out] DomainName + * Receives in output, a pointer to an allocated string representing + * the domain name. The string is allocated with LocalAlloc(). After usage, + * free the pointer with LocalFree(). + * + * @return + * TRUE if the information has been retrieved successfully; FALSE if not. + * + * @see + * Copied and adapted from dll/win32/userenv/environment.c!GetUserAndDomainName + * See also base/shell/progman/main.c!GetUserAndDomainName + **/ +static BOOL +GetUserAndDomainName( + _In_ HANDLE hToken, + _Out_ PWSTR* UserName, + _Out_ PWSTR* DomainName) +{ + BOOL bRet = FALSE; + DWORD cbTokenBuffer = 0; + PTOKEN_USER pUserToken; + + PWSTR pUserName = NULL; + PWSTR pDomainName = NULL; + DWORD cbUserName = 0; + DWORD cbDomainName = 0; + + SID_NAME_USE SidNameUse; + + /* Retrieve token's information */ + if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &cbTokenBuffer)) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return FALSE; + } + + pUserToken = LocalAlloc(LMEM_FIXED, cbTokenBuffer); + if (!pUserToken) + return FALSE; + + if (!GetTokenInformation(hToken, TokenUser, pUserToken, cbTokenBuffer, &cbTokenBuffer)) + { + LocalFree(pUserToken); + return FALSE; + } + + /* Retrieve the domain and user name */ + if (!LookupAccountSidW(NULL, + pUserToken->User.Sid, + NULL, + &cbUserName, + NULL, + &cbDomainName, + &SidNameUse)) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto done; + } + + pUserName = LocalAlloc(LPTR, cbUserName * sizeof(WCHAR)); + if (!pUserName) + goto done; + + pDomainName = LocalAlloc(LPTR, cbDomainName * sizeof(WCHAR)); + if (!pDomainName) + goto done; + + if (!LookupAccountSidW(NULL, + pUserToken->User.Sid, + pUserName, + &cbUserName, + pDomainName, + &cbDomainName, + &SidNameUse)) + { + goto done; + } + + *UserName = pUserName; + *DomainName = pDomainName; + bRet = TRUE; + +done: + if (bRet == FALSE) + { + if (pUserName) + LocalFree(pUserName); + if (pDomainName) + LocalFree(pDomainName); + } + LocalFree(pUserToken); + + return bRet; +} + + +typedef struct _SYSTEM_USER_INFO +{ + HANDLE hProcToken, hThrdToken; + PWSTR pProcUserName, pProcDomainName; + PWSTR pThrdUserName, pThrdDomainName; + PWSTR pNotifUserName, pNotifDomainName; + HWINSTA hWinSta; + HDESK hThreadDesk, hInputDesk; + PWSTR pszWinSta, pszThreadDesk, pszInputDesk, pszNotifDesk; + // BYTE Data[ANYSIZE_ARRAY]; +} SYSTEM_USER_INFO, *PSYSTEM_USER_INFO; + +static PSYSTEM_USER_INFO +GetSystemUserInfo( + _In_ HANDLE hNotifToken, + _In_ HDESK hNotifDesktop) +{ + SYSTEM_USER_INFO LocalInfo = {NULL}, *pSysUserInfo; + PWSTR pString; + SIZE_T BufSize; + WCHAR szBuffer0[MAX_PATH], szBuffer1[MAX_PATH], szBuffer2[MAX_PATH], szBuffer3[MAX_PATH]; + + /* Retrieve the process' token and the corresponding user/domain */ + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &LocalInfo.hProcToken)) + { + GetUserAndDomainName(LocalInfo.hProcToken, &LocalInfo.pProcUserName, &LocalInfo.pProcDomainName); + // CloseHandle(LocalInfo.hProcToken); + } + + /* Retrieve the current thread's impersonation token, if any (otherwise + * no impersonation occurs), and the corresponding user/domain */ + if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &LocalInfo.hThrdToken)) + { + GetUserAndDomainName(LocalInfo.hThrdToken, &LocalInfo.pThrdUserName, &LocalInfo.pThrdDomainName); + // CloseHandle(LocalInfo.hThrdToken); + } + + /* Retrieve the notification's corresponding user/domain */ + GetUserAndDomainName(hNotifToken, &LocalInfo.pNotifUserName, &LocalInfo.pNotifDomainName); + + /* Retrieve the process' window station name */ + LocalInfo.hWinSta = GetProcessWindowStation(); + LocalInfo.pszWinSta = GetUserObjectName(LocalInfo.hWinSta, szBuffer0, _countof(szBuffer0)); + + /* Retrieve the thread desktop name */ + LocalInfo.hThreadDesk = GetThreadDesktop(GetCurrentThreadId()); + LocalInfo.pszThreadDesk = GetUserObjectName(LocalInfo.hThreadDesk, szBuffer1, _countof(szBuffer1)); + + /* Retrieve the input desktop name */ + LocalInfo.hInputDesk = OpenInputDesktop(0, FALSE, STANDARD_RIGHTS_READ); + LocalInfo.pszInputDesk = GetUserObjectName(LocalInfo.hInputDesk, szBuffer2, _countof(szBuffer2)); + // CloseDesktop(LocalInfo.hInputDesk); + + /* Retrieve the notification desktop name */ + LocalInfo.pszNotifDesk = GetUserObjectName(hNotifDesktop, szBuffer3, _countof(szBuffer3)); + + + /* Allocate a single buffer to hold all the retrieved information */ + BufSize = sizeof(*pSysUserInfo) + + ((LocalInfo.pProcUserName ? wcslen(LocalInfo.pProcUserName) + 1 : 0) + + (LocalInfo.pProcDomainName ? wcslen(LocalInfo.pProcDomainName) + 1 : 0) + + (LocalInfo.pThrdUserName ? wcslen(LocalInfo.pThrdUserName) + 1 : 0) + + (LocalInfo.pThrdDomainName ? wcslen(LocalInfo.pThrdDomainName) + 1 : 0) + + (LocalInfo.pNotifUserName ? wcslen(LocalInfo.pNotifUserName) + 1 : 0) + + (LocalInfo.pNotifDomainName ? wcslen(LocalInfo.pNotifDomainName) + 1 : 0) + + (LocalInfo.pszWinSta ? wcslen(LocalInfo.pszWinSta) + 1 : 0) + + (LocalInfo.pszThreadDesk ? wcslen(LocalInfo.pszThreadDesk) + 1 : 0) + + (LocalInfo.pszInputDesk ? wcslen(LocalInfo.pszInputDesk) + 1 : 0) + + (LocalInfo.pszNotifDesk ? wcslen(LocalInfo.pszNotifDesk) + 1 : 0)) * sizeof(WCHAR); + pSysUserInfo = LocalAlloc(LMEM_FIXED, BufSize); + if (!pSysUserInfo) + goto Done; + + /* Copy the fixed part of the structure */ + *pSysUserInfo = LocalInfo; + + /* Copy the strings and adjust the pointers */ + pString = (PWSTR)(pSysUserInfo + 1); + if (LocalInfo.pProcUserName) + { + wcscpy(pString, LocalInfo.pProcUserName); + pSysUserInfo->pProcUserName = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pProcDomainName) + { + wcscpy(pString, LocalInfo.pProcDomainName); + pSysUserInfo->pProcDomainName = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pThrdUserName) + { + wcscpy(pString, LocalInfo.pThrdUserName); + pSysUserInfo->pThrdUserName = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pThrdDomainName) + { + wcscpy(pString, LocalInfo.pThrdDomainName); + pSysUserInfo->pThrdDomainName = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pNotifUserName) + { + wcscpy(pString, LocalInfo.pNotifUserName); + pSysUserInfo->pNotifUserName = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pNotifDomainName) + { + wcscpy(pString, LocalInfo.pNotifDomainName); + pSysUserInfo->pNotifDomainName = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pszWinSta) + { + wcscpy(pString, LocalInfo.pszWinSta); + pSysUserInfo->pszWinSta = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pszThreadDesk) + { + wcscpy(pString, LocalInfo.pszThreadDesk); + pSysUserInfo->pszThreadDesk = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pszInputDesk) + { + wcscpy(pString, LocalInfo.pszInputDesk); + pSysUserInfo->pszInputDesk = pString; + pString += wcslen(pString) + 1; + } + if (LocalInfo.pszNotifDesk) + { + wcscpy(pString, LocalInfo.pszNotifDesk); + pSysUserInfo->pszNotifDesk = pString; + } + +Done: + if (LocalInfo.pszNotifDesk != szBuffer3) + LocalFree(LocalInfo.pszNotifDesk); + if (LocalInfo.pszInputDesk != szBuffer2) + LocalFree(LocalInfo.pszInputDesk); + if (LocalInfo.pszThreadDesk != szBuffer1) + LocalFree(LocalInfo.pszThreadDesk); + if (LocalInfo.pszWinSta != szBuffer0) + LocalFree(LocalInfo.pszWinSta); + + if (LocalInfo.pNotifUserName) + LocalFree(LocalInfo.pNotifUserName); + if (LocalInfo.pNotifDomainName) + LocalFree(LocalInfo.pNotifDomainName); + if (LocalInfo.pThrdUserName) + LocalFree(LocalInfo.pThrdUserName); + if (LocalInfo.pThrdDomainName) + LocalFree(LocalInfo.pThrdDomainName); + if (LocalInfo.pProcUserName) + LocalFree(LocalInfo.pProcUserName); + if (LocalInfo.pProcDomainName) + LocalFree(LocalInfo.pProcDomainName); + + /* If we failed to allocate the buffer, close the opened handles */ + if (!pSysUserInfo) + { + CloseDesktop(LocalInfo.hInputDesk); + CloseHandle(LocalInfo.hThrdToken); + CloseHandle(LocalInfo.hProcToken); + } + + return pSysUserInfo; +} + +static VOID +FreeSystemUserInfo( + _Inout_ PSYSTEM_USER_INFO SysUserInfo) +{ + CloseHandle(SysUserInfo->hProcToken); + CloseHandle(SysUserInfo->hThrdToken); + CloseDesktop(SysUserInfo->hInputDesk); + LocalFree(SysUserInfo); +} + + static VOID DumpNotificationState( _In_ PCSTR FileName, _In_ INT LineNum, _In_ PCSTR FuncName, + _In_ PSYSTEM_USER_INFO SysUserInfo, _In_ PWLX_NOTIFICATION_INFO pInfo) { - HWINSTA hWinSta; - HDESK hThreadDesk, hInputDesk; - PWSTR pszWinSta, pszNotifDesk, pszThreadDesk, pszInputDesk; - WCHAR szBuffer0[MAX_PATH], szBuffer1[MAX_PATH], szBuffer2[MAX_PATH], szBuffer3[MAX_PATH]; - - /* Retrieve the process' window station name */ - hWinSta = GetProcessWindowStation(); - pszWinSta = GetUserObjectName(hWinSta, szBuffer0, _countof(szBuffer0)); - - /* Retrieve the notification desktop name */ - pszNotifDesk = GetUserObjectName(pInfo->hDesktop, szBuffer1, _countof(szBuffer1)); - - /* Retrieve the thread desktop name */ - hThreadDesk = GetThreadDesktop(GetCurrentThreadId()); - pszThreadDesk = GetUserObjectName(hThreadDesk, szBuffer2, _countof(szBuffer2)); - - /* Retrieve the input desktop name */ - hInputDesk = OpenInputDesktop(0, FALSE, STANDARD_RIGHTS_READ); - pszInputDesk = GetUserObjectName(hInputDesk, szBuffer3, _countof(szBuffer3)); - CloseDesktop(hInputDesk); - /* * Dump the information and the WLX_NOTIFICATION_INFO structure. */ + // TRACE( ros_dbg_log(__WINE_DBCL_ERR, __wine_dbch___default, FileName, FuncName, LineNum, - /*TRACE(*/"\nWLNOTIFY(%lx.%lx): Entering `%s`\n" - "\tProcess WinSta : 0x%p '%S'\n" - "\tNotif Desktop : 0x%p '%S'\n" - "\tThread Desktop : 0x%p '%S'\n" - "\tInput Desktop : 0x%p '%S'\n" - "\tInfo.Size : %lu\n" - "\tInfo.Flags : 0x%lx\n" - "\tInfo.UserName : '%S'\n" - "\tInfo.Domain : '%S'\n" - "\tInfo.WindowStation : '%S'\n" - "\tInfo.hToken : 0x%p\n" - "\tInfo.hDesktop : 0x%p\n" - "\tInfo.pStatusCallback: 0x%p\n", - GetCurrentProcessId(), // NtCurrentTeb()->ClientId.UniqueProcess - GetCurrentThreadId(), // NtCurrentTeb()->ClientId.UniqueThread - FuncName, - hWinSta, pszWinSta, - pInfo->hDesktop, pszNotifDesk, - hThreadDesk, pszThreadDesk, - hInputDesk, pszInputDesk, - pInfo->Size, pInfo->Flags, pInfo->UserName, - pInfo->Domain, pInfo->WindowStation, pInfo->hToken, - pInfo->hDesktop, pInfo->pStatusCallback); - - if (pszInputDesk != szBuffer3) - LocalFree(pszInputDesk); - if (pszThreadDesk != szBuffer2) - LocalFree(pszThreadDesk); - if (pszNotifDesk != szBuffer1) - LocalFree(pszNotifDesk); - if (pszWinSta != szBuffer0) - LocalFree(pszWinSta); + "\nWLNOTIFY(%lx.%lx) [Async: %s, Impers: %s]: Entering `%s`\n" + "\tProcess Token : 0x%p - User: '%S\\%S'\n" + "\tThread Token : 0x%p - User: '%S\\%S'\n" + "\tNotif Token : 0x%p - User: '%S\\%S'\n" + "\tProcess WinSta : 0x%p '%S'\n" + "\tThread Desktop : 0x%p '%S'\n" + "\tInput Desktop : 0x%p '%S'\n" + "\tNotif Desktop : 0x%p '%S'\n", + GetCurrentProcessId(), // NtCurrentTeb()->ClientId.UniqueProcess + GetCurrentThreadId(), // NtCurrentTeb()->ClientId.UniqueThread + g_fFlags.bAsync ? "TRUE" : "FALSE", + g_fFlags.bImpersonate ? "TRUE" : "FALSE", + FuncName, + SysUserInfo->hProcToken, SysUserInfo->pProcDomainName, SysUserInfo->pProcUserName, + SysUserInfo->hThrdToken, SysUserInfo->pThrdDomainName, SysUserInfo->pThrdUserName, + pInfo->hToken, SysUserInfo->pNotifDomainName, SysUserInfo->pNotifUserName, + SysUserInfo->hWinSta, SysUserInfo->pszWinSta, + SysUserInfo->hThreadDesk, SysUserInfo->pszThreadDesk, + SysUserInfo->hInputDesk, SysUserInfo->pszInputDesk, + pInfo->hDesktop, SysUserInfo->pszNotifDesk); + // if (__WINE_IS_DEBUG_ON(_ERR, __wine_dbch___default)) + DPRINTF( + "\tInfo.Size : %lu\n" + "\tInfo.Flags : 0x%lx\n" + "\tInfo.UserName : '%S'\n" + "\tInfo.Domain : '%S'\n" + "\tInfo.WindowStation : '%S'\n" + "\tInfo.hToken : 0x%p\n" + "\tInfo.hDesktop : 0x%p\n" + "\tInfo.pStatusCallback: 0x%p\n", + pInfo->Size, pInfo->Flags, pInfo->UserName, + pInfo->Domain, pInfo->WindowStation, pInfo->hToken, + pInfo->hDesktop, pInfo->pStatusCallback); } -#define DUMP_WLX_NOTIFICATION(pInfo) \ - DumpNotificationState(__RELFILE__, __LINE__, __FUNCTION__, pInfo) +#define DUMP_WLX_NOTIFICATION(SysUserInfo, pInfo) \ + DumpNotificationState(__RELFILE__, __LINE__, __FUNCTION__, (SysUserInfo), (pInfo)) + static VOID ChangeNotificationState( @@ -268,9 +659,10 @@ ChangeNotificationState( _In_ BOOL bChange, _In_ WLNOTIFY_STATE NewState) { + // TRACE( ros_dbg_log(__WINE_DBCL_ERR, __wine_dbch___default, FileName, FuncName, LineNum, - /*TRACE(*/"**** %s: %s state %s %s %s\n", + "**** %s: %s state %s %s %s\n", FuncName, bChange ? "Changing" : "Restoring", NotifyStateToName(g_CurrentState), @@ -283,6 +675,7 @@ ChangeNotificationState( #define CHANGE_STATE(bChange, NewState) \ ChangeNotificationState(__RELFILE__, __LINE__, __FUNCTION__, (bChange), (NewState)) + static DWORD DisplayWlxMessageW( _In_opt_ PFNMSGECALLBACK pStatusCallback, @@ -329,328 +722,558 @@ DisplayWlxMessageA( } -/* NOTIFICATION CALLBACKS ****************************************************/ +/* TESTING SUPPORT HELPERS ***************************************************/ -/** - * @brief Invoked on system startup. - **/ -VOID -WINAPI -WLEventStartup( - _In_ PWLX_NOTIFICATION_INFO pInfo) -{ - DUMP_WLX_NOTIFICATION(pInfo); - - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from the non-initialized state */ - if (g_CurrentState != WLNotify_NonInitialized) +#define BEGIN_TEST \ +do { \ + static const struct test winetest_testlist[] = { { __FUNCTION__, NULL } }; \ + init_test( &winetest_testlist[0] ); \ + winetest_push_context("**** %s", __FUNCTION__); \ { - ERR("**** %s: ERROR: Wrong state %s, expected %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_NonInitialized)); + +#define END_TEST \ + } \ + winetest_pop_context(); \ + (void)fini_test(); \ +} while(0); + +#define ok_state_1(state1) \ + ok(g_CurrentState == (state1), \ + "ERROR: Wrong state %s, expected %s\n", \ + NotifyStateToName(g_CurrentState), \ + NotifyStateToName(state1)) + +#define ok_state_2(state1, state2) \ + ok(g_CurrentState == (state1) || g_CurrentState == (state2), \ + "ERROR: Wrong state %s, expected %s or %s\n", \ + NotifyStateToName(g_CurrentState), \ + NotifyStateToName(state1), \ + NotifyStateToName(state2)) + +#define ok_state_3(state1, state2, state3) \ + ok(g_CurrentState == (state1) || g_CurrentState == (state2) || g_CurrentState == (state3), \ + "ERROR: Wrong state %s, expected %s or %s or %s\n", \ + NotifyStateToName(g_CurrentState), \ + NotifyStateToName(state1), \ + NotifyStateToName(state2), \ + NotifyStateToName(state3)) + +typedef struct _TEST_ENTRY +{ + _Field_size_(NumStates) WLNOTIFY_STATE* AllowedStates; + ULONG NumStates; ///< Number of elements in the AllowedStates array. + // BOOL bProcToken; + // PCWSTR pProcUserName, pProcDomainName; + BOOL bThrdToken, bUserLoggedIn; ///< When bUserLoggedIn is TRUE we expect a valid notification user token. + PCWSTR pszWinSta, pszThreadDesk; + _Maybenull_ PCWSTR pszInputDesk; + PCWSTR pszNotifDesk; +} TEST_ENTRY, *PTEST_ENTRY; + +static void +DoTest(const char* file, int line, const char* funcname, + _In_ PSYSTEM_USER_INFO SysUserInfo, + _In_ PWLX_NOTIFICATION_INFO pInfo, + _In_ PTEST_ENTRY pTest) +{ + UNREFERENCED_PARAMETER(file); + UNREFERENCED_PARAMETER(line); + UNREFERENCED_PARAMETER(funcname); // Displayed via winetest_push_context() + + if (pTest->NumStates == 1) + ok_state_1(pTest->AllowedStates[0]); + else if (pTest->NumStates == 2) + ok_state_2(pTest->AllowedStates[0], pTest->AllowedStates[1]); + else if (pTest->NumStates == 3) + ok_state_3(pTest->AllowedStates[0], pTest->AllowedStates[1], pTest->AllowedStates[2]); + else + skip("Unsupported number of allowed notify states: %lu\n", pTest->NumStates); + + ok(SysUserInfo->hProcToken != NULL, "Expected hProcToken != NULL\n"); + ok(SysUserInfo->pProcDomainName != NULL, "Expected pProcDomainName != NULL\n"); + ok(SysUserInfo->pProcUserName != NULL, "Expected pProcUserName != NULL\n"); + + if (pTest->bThrdToken) + { + ok(SysUserInfo->hThrdToken != NULL, "Expected hThrdToken != NULL\n"); + + ok(SysUserInfo->pThrdDomainName != NULL, "Expected pThrdDomainName != NULL\n"); + if (!SysUserInfo->pThrdDomainName || !SysUserInfo->pNotifDomainName) + skip("Skipped pThrdDomainName/pNotifDomainName test\n"); + else + ok_wstr(SysUserInfo->pThrdDomainName, SysUserInfo->pNotifDomainName); + + ok(SysUserInfo->pThrdUserName != NULL, "Expected pThrdUserName != NULL\n"); + if (!SysUserInfo->pThrdUserName || !SysUserInfo->pNotifUserName) + skip("Skipped pThrdUserName/pNotifUserName test\n"); + else + ok_wstr(SysUserInfo->pThrdUserName, SysUserInfo->pNotifUserName); } + else + { + ok(SysUserInfo->hThrdToken == NULL, "Expected hThrdToken == NULL\n"); + ok(SysUserInfo->pThrdDomainName == NULL, "Expected pThrdDomainName == NULL\n"); + ok(SysUserInfo->pThrdUserName == NULL, "Expected pThrdUserName == NULL\n"); + } + + if (pTest->bUserLoggedIn) + { + /* See also pInfo->UserName and pInfo->Domain below */ + ok(pInfo->hToken != NULL, "Expected pInfo->hToken != NULL\n"); + ok(SysUserInfo->pNotifDomainName != NULL, "Expected pNotifDomainName != NULL\n"); + ok(SysUserInfo->pNotifUserName != NULL, "Expected pNotifUserName != NULL\n"); + } + else + { + ok(pInfo->hToken == NULL, "Expected pInfo->hToken == NULL\n"); + ok(SysUserInfo->pNotifDomainName == NULL, "Expected pNotifDomainName == NULL\n"); + ok(SysUserInfo->pNotifUserName == NULL, "Expected pNotifUserName == NULL\n"); + } + + // if (pTest->pszWinSta) // Expected winsta -- DEFAULT_WINSTA0 + ok(SysUserInfo->hWinSta != NULL, "Expected hProcWinSta != NULL\n"); + ok(SysUserInfo->pszWinSta != NULL, "Expected pszWinSta != NULL\n"); + if (!SysUserInfo->pszWinSta) + skip("Skipped pszWinSta test\n"); + else + ok_wstr(SysUserInfo->pszWinSta, /*pTest->pszWinSta*/ DEFAULT_WINSTA0); + + // if (pTest->pszThreadDesk) // Expected thread desk -- DESKTOP_WINLOGON + ok(SysUserInfo->hThreadDesk != NULL, "Expected hThreadDesk != NULL\n"); + ok(SysUserInfo->pszThreadDesk != NULL, "Expected pszThreadDesk != NULL\n"); + if (!SysUserInfo->pszThreadDesk) + skip("Skipped pszThreadDesk test\n"); + else + ok_wstr(SysUserInfo->pszThreadDesk, DESKTOP_WINLOGON); + + if (pTest->pszInputDesk) + { + ok(SysUserInfo->hInputDesk != NULL, "Expected hInputDesk != NULL\n"); + ok(SysUserInfo->pszInputDesk != NULL, "Expected pszInputDesk != NULL\n"); + if (!SysUserInfo->pszInputDesk) + skip("Skipped pszInputDesk test\n"); + else + ok_wstr(SysUserInfo->pszInputDesk, pTest->pszInputDesk); + } + else + { + ok(SysUserInfo->hInputDesk == NULL, "Expected hInputDesk == NULL\n"); + ok(SysUserInfo->pszInputDesk == NULL, "Expected pszInputDesk == NULL\n"); + } + + // if (pTest->pszNotifDesk) + ok(pInfo->hDesktop != NULL, "Expected pInfo->hDesktop != NULL\n"); + ok(SysUserInfo->pszNotifDesk != NULL, "Expected pszNotifDesk != NULL\n"); + if (!SysUserInfo->pszNotifDesk) + skip("Skipped pszNotifDesk test\n"); + else + ok_wstr(SysUserInfo->pszNotifDesk, pTest->pszNotifDesk); + + ok_int(pInfo->Size, sizeof(*pInfo)); // == sizeof(WLX_NOTIFICATION_INFO); + // ok_int(pInfo->Flags, 0); // This is tested separately: more than one flag is possible. + + if (pTest->bUserLoggedIn) + { + ok(pInfo->UserName != NULL, "Expected pInfo->UserName != NULL\n"); + ok(pInfo->Domain != NULL, "Expected pInfo->Domain != NULL\n"); + // ok(pInfo->hToken != NULL, "Expected pInfo->hToken != NULL\n"); + + if (!pInfo->UserName || !SysUserInfo->pNotifUserName) + skip("Skipped UserName/pNotifUserName test\n"); + else + ok_wstr(pInfo->UserName, SysUserInfo->pNotifUserName); + + if (!pInfo->Domain || !SysUserInfo->pNotifDomainName) + skip("Skipped Domain/pNotifDomainName test\n"); + else + ok_wstr(pInfo->Domain, SysUserInfo->pNotifDomainName); + } + else + { + ok(pInfo->UserName == NULL, "Expected pInfo->UserName == NULL\n"); + ok(pInfo->Domain == NULL, "Expected pInfo->Domain == NULL\n"); + // ok(pInfo->hToken == NULL, "Expected pInfo->hToken == NULL\n"); + } + + ok(pInfo->WindowStation != NULL, "Expected pInfo->WindowStation != NULL\n"); + if (!pInfo->WindowStation || !SysUserInfo->pszWinSta) + skip("Skipped WindowStation/pszWinSta test\n"); + else + ok_wstr(pInfo->WindowStation, SysUserInfo->pszWinSta); + + if (wcscmp(pTest->pszNotifDesk, /*pTest->pszThreadDesk*/ DESKTOP_WINLOGON) == 0) + ok(pInfo->hDesktop == SysUserInfo->hThreadDesk, "Expected pInfo->hDesktop == hThreadDesk\n"); + else + ok(pInfo->hDesktop != SysUserInfo->hThreadDesk, "Expected pInfo->hDesktop != hThreadDesk\n"); + + if (g_fFlags.bAsync) + ok(pInfo->pStatusCallback == NULL, "Expected pStatusCallback == NULL, was: 0x%p\n", pInfo->pStatusCallback); + else + ok(pInfo->pStatusCallback != NULL, "Expected pStatusCallback != NULL\n"); +} + + +/* NOTIFICATION HANDLERS *****************************************************/ + +#define __HANDLER_PARAM(pNotifInfo)(_In_ PWLX_NOTIFICATION_INFO pNotifInfo) +#define WLNOTIFY_HANDLER(name) \ + VOID WINAPI name __HANDLER_PARAM + +#define BEGIN_HANDLER /*(SysUserInfo, pInfo)*/ \ + PSYSTEM_USER_INFO SysUserInfo; \ + SysUserInfo = GetSystemUserInfo(pInfo->hToken, pInfo->hDesktop); \ + DUMP_WLX_NOTIFICATION(SysUserInfo, pInfo); \ + DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); \ DbgBreakOnEvent(); +#define END_HANDLER(bChangeState, NewState) \ + FreeSystemUserInfo(SysUserInfo); \ + /* Change (bChangeState == TRUE) or Restore previous (FALSE) state */ \ + CHANGE_STATE(bChangeState, NewState); + + +/** + * @brief Invoked at system startup. + **/ +WLNOTIFY_HANDLER(WLEventStartup)(pInfo) +{ + /* Initially retrieve the notification registry settings */ + GetSettings(); + + BEGIN_HANDLER; + + BEGIN_TEST + { + /* We must be called from the non-initialized state */ + WLNOTIFY_STATE States[] = {WLNotify_NonInitialized}; + TEST_ENTRY StartupTest = + { States, _countof(States), FALSE, FALSE, DEFAULT_WINSTA0, + DESKTOP_WINLOGON, DESKTOP_WINLOGON, DESKTOP_WINLOGON }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &StartupTest); + + ok_int(pInfo->Flags, 0); + } + END_TEST + /* Change state */ - CHANGE_STATE(TRUE, WLNotify_Startup); + END_HANDLER(TRUE, WLNotify_Startup); } /** - * @brief Invoked on system shutdown. + * @brief Invoked at system shutdown. **/ -VOID -WINAPI -WLEventShutdown( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventShutdown)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from either Startup or previous Logoff */ - if (g_CurrentState != WLNotify_Startup && - g_CurrentState != WLNotify_Logoff) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s or %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_Startup), - NotifyStateToName(WLNotify_Logoff)); + /* We must be called from either Startup or previous Logoff */ + WLNOTIFY_STATE States[] = {WLNotify_Startup, WLNotify_Logoff}; + TEST_ENTRY ShutdownTest = + { States, _countof(States), FALSE, FALSE, DEFAULT_WINSTA0, + DESKTOP_WINLOGON, DESKTOP_WINLOGON, DESKTOP_WINLOGON }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &ShutdownTest); + + /* Compare these flags with those from WLEventLogoff */ + ok_int(pInfo->Flags, g_fLogoffShutdownFlags); + /* + * - If the logged-on user chooses to shutdown or reboot the computer, + * WLEventLogoff() is first invoked with pInfo->Flags == EWX_SHUTDOWN + * or EWX_SHUTDOWN | EWX_REBOOT (stored in g_fLogoffShutdownFlags), + * then WLEventShutdown() is invoked with the same values. + * + * - If the logged-on user chooses to log off only, WLEventLogoff() + * is invoked with pInfo->Flags == 0 (== EWX_LOGOFF). + * + * - Independently of whether the user chose to shutdown or reboot + * the computer at the SAS dialog before logging in first (thus, + * g_CurrentState == WLNotify_Startup), or when coming back at the + * SAS dialog after logged off (g_CurrentState == WLNotify_Logoff), + * WLEventShutdown() is invoked with pInfo->Flags == 0. + * (In both these cases, g_fLogoffShutdownFlags == 0 too.) + */ + ok(pInfo->Flags == EWX_LOGOFF || + pInfo->Flags == EWX_SHUTDOWN || + pInfo->Flags == (EWX_SHUTDOWN | EWX_REBOOT), + "Expected pInfo->Flags == EWX_LOGOFF (0) or EWX_SHUTDOWN (1) or EWX_SHUTDOWN|EWX_REBOOT (3), got %lu\n", + pInfo->Flags); } - DbgBreakOnEvent(); + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_Shutdown); + END_HANDLER(TRUE, WLNotify_Shutdown); } /** - * @brief Invoked on user logon. + * @brief Invoked at user logon. **/ -VOID -WINAPI -WLEventLogon( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventLogon)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from either Startup or previous Logoff */ - if (g_CurrentState != WLNotify_Startup && - g_CurrentState != WLNotify_Logoff) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s or %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_Startup), - NotifyStateToName(WLNotify_Logoff)); + /* We must be called from either Startup or previous Logoff */ + WLNOTIFY_STATE States[] = {WLNotify_Startup, WLNotify_Logoff}; + PWSTR pszInputDesk = (g_fFlags.bImpersonate ? NULL : DESKTOP_WINLOGON); + TEST_ENTRY LogonTest = + { States, _countof(States), g_fFlags.bImpersonate, TRUE, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, pszInputDesk, DESKTOP_DEFAULT }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &LogonTest); + + ok_int(pInfo->Flags, 0); } - DbgBreakOnEvent(); + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_Logon); + END_HANDLER(TRUE, WLNotify_Logon); } /** - * @brief Invoked on user logoff. + * @brief Invoked at user logoff. **/ -VOID -WINAPI -WLEventLogoff( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventLogoff)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); + /* Save the flags for comparison with those in WLEventShutdown */ + InterlockedExchange((PLONG)&g_fLogoffShutdownFlags, pInfo->Flags); - /* We must be called from PostShell, or StartShell if we failed to start - * the shell (the PostShell notification isn't emitted in this case) */ - if (g_CurrentState != WLNotify_PostShell && - g_CurrentState != WLNotify_StartShell) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s or %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_PostShell), - NotifyStateToName(WLNotify_StartShell)); + /* We must be called from PostShell, or StartShell if we failed to start + * the shell (the PostShell notification isn't emitted in this case) */ + WLNOTIFY_STATE States[] = {WLNotify_PostShell, WLNotify_StartShell}; + TEST_ENTRY LogoffTest = + { States, _countof(States), g_fFlags.bImpersonate, TRUE, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, DESKTOP_DEFAULT, DESKTOP_WINLOGON }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &LogoffTest); + + ok(pInfo->Flags == EWX_LOGOFF || + pInfo->Flags == EWX_SHUTDOWN || + pInfo->Flags == (EWX_SHUTDOWN | EWX_REBOOT), + "Expected pInfo->Flags == EWX_LOGOFF (0) or EWX_SHUTDOWN (1) or EWX_SHUTDOWN|EWX_REBOOT (3), got %lu\n", + pInfo->Flags); } - DbgBreakOnEvent(); + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_Logoff); + END_HANDLER(TRUE, WLNotify_Logoff); } /** * @brief Invoked just before starting the user shell. **/ -VOID -WINAPI -WLEventStartShell( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventStartShell)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from Logon */ - if (g_CurrentState != WLNotify_Logon) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_Logon)); + /* We must be called from Logon */ + WLNOTIFY_STATE States[] = {WLNotify_Logon}; + PWSTR pszInputDesk = (g_fFlags.bImpersonate ? NULL : DESKTOP_WINLOGON); + TEST_ENTRY StartShellTest = + { States, _countof(States), g_fFlags.bImpersonate, TRUE, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, pszInputDesk, DESKTOP_DEFAULT }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &StartShellTest); + + ok_int(pInfo->Flags, 0); } - DbgBreakOnEvent(); + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_StartShell); + END_HANDLER(TRUE, WLNotify_StartShell); } /** * @brief Invoked just after starting the user shell. **/ -VOID -WINAPI -WLEventPostShell( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventPostShell)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from StartShell */ - if (g_CurrentState != WLNotify_StartShell) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_StartShell)); + /* We must be called from StartShell */ + WLNOTIFY_STATE States[] = {WLNotify_StartShell}; + PWSTR pszInputDesk = + (g_fFlags.bAsync ? DESKTOP_DEFAULT + : (g_fFlags.bImpersonate ? NULL : DESKTOP_WINLOGON)); + TEST_ENTRY PostShellTest = + { States, _countof(States), g_fFlags.bImpersonate, TRUE, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, pszInputDesk, DESKTOP_WINLOGON }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &PostShellTest); + + ok_int(pInfo->Flags, 0); } - DbgBreakOnEvent(); + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_PostShell); + END_HANDLER(TRUE, WLNotify_PostShell); } /** - * @brief Invoked on workstation locking. + * @brief Invoked at workstation locking. **/ -VOID -WINAPI -WLEventLock( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventLock)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from PostShell */ - if (g_CurrentState != WLNotify_PostShell) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_PostShell)); + /* We must be called from PostShell */ + WLNOTIFY_STATE States[] = {WLNotify_PostShell}; + PWSTR pszInputDesk = (g_fFlags.bImpersonate ? NULL : DESKTOP_WINLOGON); + TEST_ENTRY LockTest = + { States, _countof(States), g_fFlags.bImpersonate, TRUE, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, pszInputDesk, DESKTOP_WINLOGON }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &LockTest); + + ok_int(pInfo->Flags, 0); } - DbgBreakOnEvent(); + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_Lock); + END_HANDLER(TRUE, WLNotify_Lock); } /** - * @brief Invoked on workstation unlocking. + * @brief Invoked at workstation unlocking. **/ -VOID -WINAPI -WLEventUnlock( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventUnlock)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from Lock */ - if (g_CurrentState != WLNotify_Lock) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_Lock)); + /* We must be called from Lock */ + WLNOTIFY_STATE States[] = {WLNotify_Lock}; + PWSTR pszInputDesk = + (g_fFlags.bAsync ? DESKTOP_DEFAULT + : (g_fFlags.bImpersonate ? NULL : DESKTOP_WINLOGON)); + TEST_ENTRY UnlockTest = + { States, _countof(States), g_fFlags.bImpersonate, TRUE, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, pszInputDesk, DESKTOP_WINLOGON }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &UnlockTest); + + ok_int(pInfo->Flags, 0); } - DbgBreakOnEvent(); + END_TEST /* Restore previous state */ - CHANGE_STATE(FALSE, g_PreviousState); + END_HANDLER(FALSE, g_PreviousState); } /** - * @brief Invoked on screensaver start. + * @brief Invoked at screensaver start. **/ -VOID -WINAPI -WLEventStartScreenSaver( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventStartScreenSaver)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from Startup, Logoff, PostShell */ - if (g_CurrentState != WLNotify_Startup && - g_CurrentState != WLNotify_Logoff && - g_CurrentState != WLNotify_PostShell) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s or %s or %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_Startup), - NotifyStateToName(WLNotify_Logoff), - NotifyStateToName(WLNotify_PostShell)); + /* We must be called from Startup, Logoff, PostShell. + * In case of Startup or Logoff, no user is logged in, + * therefore no notification user token is present. */ + WLNOTIFY_STATE States[] = {WLNotify_Startup, WLNotify_Logoff, WLNotify_PostShell}; + BOOL bUserLoggedIn = IsUserLoggedIn(g_CurrentState); + BOOL bImpersonate = (g_fFlags.bImpersonate && bUserLoggedIn); + TEST_ENTRY StartScreenSaverTest = + { States, _countof(States), bImpersonate, bUserLoggedIn, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, DESKTOP_SCRSAVE, DESKTOP_SCRSAVE }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &StartScreenSaverTest); + + ok_int(pInfo->Flags, 0); } - DbgBreakOnEvent(); + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_StartScreenSaver); + END_HANDLER(TRUE, WLNotify_StartScreenSaver); } /** - * @brief Invoked on screensaver stop. + * @brief Invoked at screensaver stop. **/ -VOID -WINAPI -WLEventStopScreenSaver( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventStopScreenSaver)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from StartScreenSaver */ - if (g_CurrentState != WLNotify_StartScreenSaver) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_StartScreenSaver)); + /* We must be called from StartScreenSaver. + * In case StartScreenSaver was called originally from Startup or Logoff, + * no user is logged in, therefore no notification user token is present. */ + WLNOTIFY_STATE States[] = {WLNotify_StartScreenSaver}; + BOOL bUserLoggedIn = IsUserLoggedIn(g_PreviousState); + BOOL bImpersonate = (g_fFlags.bImpersonate && bUserLoggedIn); + PWSTR pszInputDesk = + (g_fFlags.bAsync ? (bImpersonate ? NULL : DESKTOP_WINLOGON) + : DESKTOP_SCRSAVE); + TEST_ENTRY StopScreenSaverTest = + { States, _countof(States), bImpersonate, bUserLoggedIn, + DEFAULT_WINSTA0, DESKTOP_WINLOGON, pszInputDesk, DESKTOP_WINLOGON }; + + DoTest(__RELFILE__, __LINE__, __FUNCTION__, SysUserInfo, pInfo, &StopScreenSaverTest); + + ok_int(pInfo->Flags, 0); } - DbgBreakOnEvent(); + END_TEST /* Restore previous state */ - CHANGE_STATE(FALSE, g_PreviousState); + END_HANDLER(FALSE, g_PreviousState); } /** - * @brief Invoked on workstation disconnect (Terminal Services). + * @brief Invoked at workstation disconnect (Terminal Services). **/ -VOID -WINAPI -WLEventDisconnect( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventDisconnect)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - ERR("**** %s: Previous state %s\n", - __FUNCTION__, NotifyStateToName(g_CurrentState)); - DbgBreakOnEvent(); + BEGIN_TEST + { + ERR("**** %s: Previous state %s\n", + __FUNCTION__, NotifyStateToName(g_CurrentState)); + } + END_TEST /* Change state */ - CHANGE_STATE(TRUE, WLNotify_Disconnect); + END_HANDLER(TRUE, WLNotify_Disconnect); } /** - * @brief Invoked on workstation reconnect (Terminal Services). + * @brief Invoked at workstation reconnect (Terminal Services). **/ -VOID -WINAPI -WLEventReconnect( - _In_ PWLX_NOTIFICATION_INFO pInfo) +WLNOTIFY_HANDLER(WLEventReconnect)(pInfo) { - DUMP_WLX_NOTIFICATION(pInfo); + BEGIN_HANDLER; - DisplayWlxMessageA(pInfo->pStatusCallback, FALSE, __FUNCTION__); - - /* We must be called from Disconnect */ - if (g_CurrentState != WLNotify_Disconnect) + BEGIN_TEST { - ERR("**** %s: ERROR: Wrong state %s, expected %s\n", - __FUNCTION__, - NotifyStateToName(g_CurrentState), - NotifyStateToName(WLNotify_Disconnect)); + /* We must be called from Disconnect */ + ok_state_1(WLNotify_Disconnect); } - DbgBreakOnEvent(); + END_TEST /* Restore previous state */ - CHANGE_STATE(FALSE, g_PreviousState); + END_HANDLER(FALSE, g_PreviousState); } @@ -663,7 +1286,7 @@ DllMain( _In_ DWORD dwReason, _In_ PVOID pReserved) { - // MESSAGE // DPRINTF + // DPRINTF TRACE("\nWLNOTIFY(%lx.%lx): Entering `%s`(hInst: 0x%p, dwReason: 0x%x, pReserved: 0x%p)\n", GetCurrentProcessId(), // NtCurrentTeb()->ClientId.UniqueProcess GetCurrentThreadId(), // NtCurrentTeb()->ClientId.UniqueThread @@ -673,16 +1296,16 @@ DllMain( { case DLL_PROCESS_ATTACH: g_hModule = GetInstanceModule(hInstance); - // DisableThreadLibraryCalls(g_hModule); + DisableThreadLibraryCalls(g_hModule); __fallthrough; case DLL_PROCESS_DETACH: { - // MESSAGE // DPRINTF + // DPRINTF ERR("\nWLNOTIFY(%lx.%lx): Entering `%s`(hInst: 0x%p, dwReason: 0x%x, pReserved: 0x%p)\n", - GetCurrentProcessId(), // NtCurrentTeb()->ClientId.UniqueProcess - GetCurrentThreadId(), // NtCurrentTeb()->ClientId.UniqueThread - __FUNCTION__, hInstance, dwReason, pReserved); + GetCurrentProcessId(), // NtCurrentTeb()->ClientId.UniqueProcess + GetCurrentThreadId(), // NtCurrentTeb()->ClientId.UniqueThread + __FUNCTION__, hInstance, dwReason, pReserved); /* Reset to detect any unwanted reloads */ g_PreviousState = g_CurrentState = WLNotify_NonInitialized; @@ -753,7 +1376,7 @@ HRESULT WINAPI DllRegisterServer(VOID) } RegSetValueExW(hNotifyKey, L"DllName", 0, REG_EXPAND_SZ, - (PBYTE)szModule, (wcslen(szModule) + 1) * sizeof(WCHAR)); + (PBYTE)szModule, (DWORD)(wcslen(szModule) + 1) * sizeof(WCHAR)); /* Make the notifications synchronous by default */ dwValue = 0; @@ -782,7 +1405,7 @@ HRESULT WINAPI DllRegisterServer(VOID) RegSetValueExW(hNotifyKey, NotifyEvents[i].ValueName, 0, REG_SZ, (PBYTE)NotifyEvents[i].Value, - (wcslen(NotifyEvents[i].Value) + 1) * sizeof(WCHAR)); + (DWORD)(wcslen(NotifyEvents[i].Value) + 1) * sizeof(WCHAR)); } RegCloseKey(hNotifyKey); diff --git a/sdk/tools/gen_baseaddress.py b/sdk/tools/gen_baseaddress.py index 0d850ef7652..3210687f63a 100644 --- a/sdk/tools/gen_baseaddress.py +++ b/sdk/tools/gen_baseaddress.py @@ -211,10 +211,11 @@ EXCLUDE = ( 'MyEventProvider.dll', 'redirtest1.dll', 'redirtest2.dll', + 'testvdd.dll', 'win32u_2k3sp2.dll', 'win32u_vista.dll', 'win32u_xpsp2.dll', - 'testvdd.dll', + 'wlntfytests.dll', ) IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b