<directory name="dxtest">\r
<xi:include href="dxtest/directory.rbuild" />\r
</directory>\r
+<directory name="regtests_by_casper">\r
+ <xi:include href="regtests_by_casper/directory.rbuild" />\r
+</directory>\r
<directory name="tests">\r
<xi:include href="tests/directory.rbuild" />\r
</directory>\r
<directory name="win32">\r
<xi:include href="win32/testsets.rbuild" />\r
</directory>\r
+<directory name="winetests">\r
+ <xi:include href="winetests/directory.rbuild" />\r
+</directory>\r
--- /dev/null
+<directory name="regtests">\r
+ <xi:include href="regtests/regtests.rbuild" />\r
+</directory>\r
+<directory name="shared">\r
+ <xi:include href="shared/rtshared.rbuild" />\r
+</directory>\r
--- /dev/null
+/*
+ * PROJECT: ReactOS kernel
+ * FILE: regtests/regtests/regtests.c
+ * PURPOSE: Regression testing framework
+ * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * UPDATE HISTORY:
+ * 23-10-2004 CSH Created
+ */
+#include <windows.h>
+
+HMODULE STDCALL
+_GetModuleHandleA(LPCSTR lpModuleName)
+{
+ return GetModuleHandleA(lpModuleName);
+}
+
+FARPROC STDCALL
+_GetProcAddress(HMODULE hModule,
+ LPCSTR lpProcName)
+{
+ return GetProcAddress(hModule, lpProcName);
+}
+
+HINSTANCE STDCALL
+_LoadLibraryA(LPCSTR lpLibFileName)
+{
+ return LoadLibraryA(lpLibFileName);
+}
+
+VOID STDCALL
+_ExitProcess(UINT uExitCode)
+{
+ ExitProcess(uExitCode);
+}
+
+HANDLE STDCALL
+_CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize,
+ LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
+ DWORD dwCreationFlags, LPDWORD lpThreadId)
+{
+ return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress,
+ lpParameter, dwCreationFlags, lpThreadId);
+}
+
+WINBOOL STDCALL
+_TerminateThread(HANDLE hThread, DWORD dwExitCode)
+{
+ return TerminateThread(hThread, dwExitCode);
+}
+
+DWORD STDCALL
+_WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
+{
+ return WaitForSingleObject(hHandle, dwMilliseconds);
+}
+
+DWORD STDCALL
+_GetLastError()
+{
+ return GetLastError();
+}
+
+VOID STDCALL
+_CloseHandle(HANDLE handle)
+{
+ CloseHandle (handle);
+}
+
+BOOL STDCALL
+_GetThreadTimes(HANDLE hThread, LPFILETIME lpCreationTime,
+ LPFILETIME lpExitTime, LPFILETIME lpKernelTime,
+ LPFILETIME lpUserTime)
+{
+ return GetThreadTimes(hThread, lpCreationTime, lpExitTime,
+ lpKernelTime, lpUserTime);
+}
+
+BOOL STDCALL
+_SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass)
+{
+ return SetPriorityClass(hProcess, dwPriorityClass);
+}
+
+BOOL STDCALL
+_SetThreadPriority(HANDLE hThread, int nPriority)
+{
+ return SetThreadPriority(hThread, nPriority);
+}
+
+HANDLE STDCALL
+_GetCurrentProcess()
+{
+ return GetCurrentProcess();
+}
+
+HANDLE STDCALL
+_GetCurrentThread()
+{
+ return GetCurrentThread();
+}
+
+VOID STDCALL
+_Sleep(DWORD dwMilliseconds)
+{
+ return Sleep(dwMilliseconds);
+}
--- /dev/null
+LIBRARY REGTESTS.DLL
+EXPORTS
+_ExitProcess@4
+_GetModuleHandleA@4
+_GetProcAddress@8
+_LoadLibraryA@4
+_CreateThread@24
+_TerminateThread@8
+_WaitForSingleObject@8
+_GetLastError@0
+_CloseHandle@4
+_GetThreadTimes@20
+_SetPriorityClass@8
+_SetThreadPriority@8
+_GetCurrentProcess@0
+_GetCurrentThread@0
+_Sleep@4
--- /dev/null
+<module name="regtests" type="win32dll" baseaddress="${BASEADDRESS_REGTESTS}">
+ <importlibrary definition="regtests.def" />
+ <include base="regtests">.</include>
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <file>regtests.c</file>
+</module>
--- /dev/null
+/*
+ * PROJECT: ReactOS kernel
+ * FILE: regtests/shared/regtests.c
+ * PURPOSE: Regression testing framework
+ * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * UPDATE HISTORY:
+ * 06-07-2003 CSH Created
+ */
+#define WIN32_NO_STATUS
+#include <windows.h>
+#define NTOS_MODE_USER
+#include <ndk/ntndk.h>
+#include <pseh/pseh.h>
+#include "regtests.h"
+
+#define NDEBUG
+#include <debug.h>
+
+typedef struct _PERFORM_TEST_ARGS
+{
+ TestOutputRoutine OutputRoutine;
+ _PTEST Test;
+ LPSTR TestName;
+ DWORD Result;
+ char Buffer[5000];
+ DWORD Time;
+} PERFORM_TEST_ARGS;
+
+int _Result;
+char *_Buffer;
+
+static LIST_ENTRY AllTests;
+
+VOID
+InitializeTests()
+{
+ InitializeListHead(&AllTests);
+}
+
+char*
+FormatExecutionTime(char *buffer, ULONG milliseconds)
+{
+ sprintf(buffer,
+ "%ldms",
+ milliseconds);
+ return buffer;
+}
+
+DWORD WINAPI
+PerformTest(PVOID _arg)
+{
+ PERFORM_TEST_ARGS *Args = (PERFORM_TEST_ARGS *)_arg;
+ _PTEST Test = Args->Test;
+
+ _SetThreadPriority(_GetCurrentThread(), THREAD_PRIORITY_IDLE);
+
+ memset(Args->Buffer, 0, sizeof(Args->Buffer));
+
+ _SEH_TRY {
+ _Result = TS_OK;
+ _Buffer = Args->Buffer;
+ (Test->Routine)(TESTCMD_RUN);
+ Args->Result = _Result;
+ } _SEH_HANDLE {
+ Args->Result = TS_FAILED;
+ sprintf(Args->Buffer, "due to exception 0x%lx", _SEH_GetExceptionCode());
+ } _SEH_END;
+ return 1;
+}
+
+VOID
+ControlNormalTest(HANDLE hThread,
+ PERFORM_TEST_ARGS *Args,
+ DWORD TimeOut)
+{
+ _FILETIME time;
+ _FILETIME executionTime;
+ DWORD status;
+
+ status = _WaitForSingleObject(hThread, TimeOut);
+ if (status == WAIT_TIMEOUT)
+ {
+ _TerminateThread(hThread, 0);
+ Args->Result = TS_TIMEDOUT;
+ }
+ status = _GetThreadTimes(hThread,
+ &time,
+ &time,
+ &time,
+ &executionTime);
+ Args->Time = executionTime.dwLowDateTime / 10000;
+}
+
+VOID
+DisplayResult(PERFORM_TEST_ARGS* Args,
+ LPSTR OutputBuffer)
+{
+ char Format[100];
+
+ if (Args->Result == TS_OK)
+ {
+ sprintf(OutputBuffer,
+ "[%s] Success [%s]\n",
+ Args->TestName,
+ FormatExecutionTime(Format,
+ Args->Time));
+ }
+ else if (Args->Result == TS_TIMEDOUT)
+ {
+ sprintf(OutputBuffer,
+ "[%s] Timed out [%s]\n",
+ Args->TestName,
+ FormatExecutionTime(Format,
+ Args->Time));
+ }
+ else
+ sprintf(OutputBuffer, "[%s] Failed (%s)\n", Args->TestName, Args->Buffer);
+
+ if (Args->OutputRoutine != NULL)
+ (*Args->OutputRoutine)(OutputBuffer);
+ else
+ DbgPrint(OutputBuffer);
+}
+
+VOID
+ControlTest(HANDLE hThread,
+ PERFORM_TEST_ARGS *Args,
+ DWORD TestType,
+ DWORD TimeOut)
+{
+ switch (TestType)
+ {
+ case TT_NORMAL:
+ ControlNormalTest(hThread, Args, TimeOut);
+ break;
+ default:
+ printf("Unknown test type %ld\n", TestType);
+ break;
+ }
+}
+
+VOID
+PerformTests(TestOutputRoutine OutputRoutine, LPSTR TestName)
+{
+ PLIST_ENTRY CurrentEntry;
+ PLIST_ENTRY NextEntry;
+ _PTEST Current;
+ PERFORM_TEST_ARGS Args;
+ HANDLE hThread;
+ char OutputBuffer[1024];
+ char Name[200];
+ DWORD TestType;
+ DWORD TimeOut;
+
+ Args.OutputRoutine = OutputRoutine;
+ Args.TestName = Name;
+ Args.Time = 0;
+
+ CurrentEntry = AllTests.Flink;
+ for (; CurrentEntry != &AllTests; CurrentEntry = NextEntry)
+ {
+ NextEntry = CurrentEntry->Flink;
+ Current = CONTAINING_RECORD(CurrentEntry, _TEST, ListEntry);
+ Args.Test = Current;
+
+ /* Get name of test */
+ memset(Name, 0, sizeof(Name));
+
+ _Result = TS_OK;
+ _Buffer = Name;
+ (Current->Routine)(TESTCMD_TESTNAME);
+ if (_Result != TS_OK)
+ {
+ if (TestName != NULL)
+ continue;
+ strcpy(Name, "Unnamed");
+ }
+
+ if ((TestName != NULL) && (_stricmp(Name, TestName) != 0))
+ continue;
+
+ TestType = TT_NORMAL;
+ _Result = TS_OK;
+ _Buffer = (char *)&TestType;
+ (Current->Routine)(TESTCMD_TESTTYPE);
+ if (_Result != TS_OK)
+ TestType = TT_NORMAL;
+
+ /* Get timeout for test */
+ TimeOut = 0;
+ _Result = TS_OK;
+ _Buffer = (char *)&TimeOut;
+ (Current->Routine)(TESTCMD_TIMEOUT);
+ if (_Result != TS_OK || TimeOut == INFINITE)
+ TimeOut = 5000;
+
+ /* Run test in a separate thread */
+ hThread = _CreateThread(NULL, 0, PerformTest, (PVOID)&Args, 0, NULL);
+ if (hThread == NULL)
+ {
+ printf("[%s] Failed (CreateThread() failed: %ld)\n",
+ Name,
+ _GetLastError());
+ Args.Result = TS_FAILED;
+ }
+ else
+ ControlTest(hThread, &Args, TestType, TimeOut);
+
+ DisplayResult(&Args, OutputBuffer);
+ }
+}
+
+VOID
+AddTest(TestRoutine Routine)
+{
+ _PTEST Test;
+
+ Test = (_PTEST) malloc(sizeof(_TEST));
+ if (Test == NULL)
+ {
+ DbgPrint("Out of memory");
+ return;
+ }
+
+ Test->Routine = Routine;
+
+ InsertTailList(&AllTests, &Test->ListEntry);
+}
+
+PVOID STDCALL
+FrameworkGetHook(ULONG index)
+{
+ return FrameworkGetHookInternal(index);
+}
--- /dev/null
+/*
+ * PROJECT: ReactOS kernel
+ * FILE: regtests/shared/regtests.h
+ * PURPOSE: Regression testing
+ * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * UPDATE HISTORY:
+ * 06-07-2003 CSH Created
+ */
+#ifndef __REGTESTS_H
+#define __REGTESTS_H
+#include <stdio.h>
+#include <string.h>
+
+typedef DWORD (STDCALL _LPTHREAD_START_ROUTINE)(LPVOID lpParameter);
+
+typedef struct __FILETIME
+{
+ DWORD dwLowDateTime;
+ DWORD dwHighDateTime;
+} _FILETIME, *_PFILETIME, *_LPFILETIME;
+
+extern void SetupOnce();
+
+#define _SetupOnce() \
+void SetupOnce()
+
+/* Valid values for Command parameter of TestRoutine */
+#define TESTCMD_RUN 0 /* Buffer contains information about what failed */
+#define TESTCMD_TESTTYPE 1 /* Buffer contains type of test */
+#define TESTCMD_TESTNAME 2 /* Buffer contains description of test */
+#define TESTCMD_TIMEOUT 3 /* Buffer contains timeout for test (DWORD, default is 5000 ms) */
+
+/* Test types */
+#define TT_NORMAL 0
+
+/* Valid values for return values of TestRoutine */
+#define TS_TIMEDOUT ((DWORD)-2)
+#define TS_EXCEPTION ((DWORD)-1)
+#define TS_OK 0
+#define TS_FAILED 1
+
+extern int _Result;
+extern char *_Buffer;
+
+/* Macros to simplify tests */
+#define _DispatcherTypeTimeout(FunctionName, TestName, TestType, TimeOut) \
+void \
+FunctionName(int Command) \
+{ \
+ switch (Command) \
+ { \
+ case TESTCMD_RUN: \
+ RunTest(); \
+ break; \
+ case TESTCMD_TESTTYPE: \
+ *(PDWORD)_Buffer = (DWORD)TestType; \
+ break; \
+ case TESTCMD_TESTNAME: \
+ strcpy(_Buffer, TestName); \
+ break; \
+ case TESTCMD_TIMEOUT: \
+ *(PDWORD)_Buffer = (DWORD)TimeOut; \
+ break; \
+ default: \
+ _Result = TS_FAILED; \
+ break; \
+ } \
+}
+
+#define _DispatcherTimeout(FunctionName, TestName, TimeOut) \
+ _DispatcherTypeTimeout(FunctionName, TestName, TT_NORMAL, TimeOut)
+
+#define _DispatcherType(FunctionName, TestName, TestType) \
+ _DispatcherTypeTimeout(FunctionName, TestName, TestType, 5000)
+
+#define _Dispatcher(FunctionName, TestName) \
+ _DispatcherTimeout(FunctionName, TestName, 5000)
+
+static __inline void
+AppendAssertion(char *message)
+{
+ if (strlen(_Buffer) != 0)
+ strcat(_Buffer, "\n");
+ strcat(_Buffer, message);
+ _Result = TS_FAILED;
+}
+
+#define _AssertTrue(_Condition) \
+{ \
+ if (!(_Condition)) \
+ { \
+ char _message[100]; \
+ sprintf(_message, "Condition was not true at %s:%d", \
+ __FILE__, __LINE__); \
+ AppendAssertion(_message); \
+ } \
+}
+
+#define _AssertFalse(_Condition) \
+{ \
+ if (_Condition) \
+ { \
+ char _message[100]; \
+ sprintf(_message, "Condition was not false at %s:%d", \
+ __FILE__, __LINE__); \
+ AppendAssertion(_message); \
+ } \
+}
+
+#define _AssertEqualValue(_Expected, _Actual) \
+{ \
+ ULONG __Expected = (ULONG) (_Expected); \
+ ULONG __Actual = (ULONG) (_Actual); \
+ if ((__Expected) != (__Actual)) \
+ { \
+ char _message[100]; \
+ sprintf(_message, "Expected %ld/0x%.08lx was %ld/0x%.08lx at %s:%d", \
+ (__Expected), (__Expected), (__Actual), (__Actual), __FILE__, __LINE__); \
+ AppendAssertion(_message); \
+ } \
+}
+
+#define _AssertEqualWideString(_Expected, _Actual) \
+{ \
+ LPWSTR __Expected = (LPWSTR) (_Expected); \
+ LPWSTR __Actual = (LPWSTR) (_Actual); \
+ if (wcscmp((__Expected), (__Actual)) != 0) \
+ { \
+ char _message[100]; \
+ sprintf(_message, "Expected %S was %S at %s:%d", \
+ (__Expected), (__Actual), __FILE__, __LINE__); \
+ AppendAssertion(_message); \
+ } \
+}
+
+#define _AssertNotEqualValue(_Expected, _Actual) \
+{ \
+ ULONG __Expected = (ULONG) (_Expected); \
+ ULONG __Actual = (ULONG) (_Actual); \
+ if ((__Expected) == (__Actual)) \
+ { \
+ char _message[100]; \
+ sprintf(_message, "Actual value expected to be different from %ld/0x%.08lx at %s:%d", \
+ (__Expected), (__Expected), __FILE__, __LINE__); \
+ AppendAssertion(_message); \
+ } \
+}
+
+
+/*
+ * Test routine prototype
+ * Command - The command to process
+ */
+typedef void (*TestRoutine)(int Command);
+
+/*
+ * Test output routine prototype
+ * Buffer - Address of buffer with text to output
+ */
+typedef void (*TestOutputRoutine)(char *Buffer);
+
+/*
+ * Test driver entry routine.
+* OutputRoutine - Output routine.
+ * TestName - If NULL all tests are run. If non-NULL specifies the test to be run
+ */
+typedef void (STDCALL *TestDriverMain)(TestOutputRoutine OutputRoutine, char *TestName);
+
+typedef struct __TEST
+{
+ LIST_ENTRY ListEntry;
+ TestRoutine Routine;
+} _TEST, *_PTEST;
+
+extern VOID InitializeTests();
+extern VOID RegisterTests();
+extern VOID PerformTests(TestOutputRoutine OutputRoutine, LPSTR TestName);
+
+
+typedef struct __API_DESCRIPTION
+{
+ PCHAR FileName;
+ PCHAR FunctionName;
+ PCHAR ForwardedFunctionName;
+ PVOID FunctionAddress;
+ PVOID MockFunctionAddress;
+} _API_DESCRIPTION, *_PAPI_DESCRIPTION;
+
+extern _API_DESCRIPTION ExternalDependencies[];
+extern ULONG MaxExternalDependency;
+
+HANDLE STDCALL
+_GetModuleHandleA(LPCSTR lpModuleName);
+
+PVOID STDCALL
+_GetProcAddress(HANDLE hModule,
+ LPCSTR lpProcName);
+
+HANDLE STDCALL
+_LoadLibraryA(LPCSTR lpLibFileName);
+
+VOID STDCALL
+_ExitProcess(UINT uExitCode);
+
+HANDLE STDCALL
+_CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, DWORD dwStackSize,
+ _LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter,
+ DWORD dwCreationFlags, LPDWORD lpThreadId);
+
+BOOL STDCALL
+_TerminateThread(HANDLE hThread, DWORD dwExitCode);
+
+DWORD STDCALL
+_WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
+
+DWORD STDCALL
+_GetLastError();
+
+VOID STDCALL
+_CloseHandle(HANDLE handle);
+
+BOOL STDCALL
+_GetThreadTimes(HANDLE hThread,
+ _LPFILETIME lpCreationTime,
+ _LPFILETIME lpExitTime,
+ _LPFILETIME lpKernelTime,
+ _LPFILETIME lpUserTime);
+
+BOOL STDCALL
+_SetPriorityClass(HANDLE hProcess, DWORD dwPriorityClass);
+
+BOOL STDCALL
+_SetThreadPriority(HANDLE hThread, int nPriority);
+
+HANDLE STDCALL
+_GetCurrentProcess();
+
+HANDLE STDCALL
+_GetCurrentThread();
+
+VOID STDCALL
+_Sleep(DWORD dwMilliseconds);
+
+
+static __inline PCHAR
+FrameworkGetExportedFunctionNameInternal(_PAPI_DESCRIPTION ApiDescription)
+{
+ if (ApiDescription->ForwardedFunctionName != NULL)
+ {
+ return ApiDescription->ForwardedFunctionName;
+ }
+ else
+ {
+ return ApiDescription->FunctionName;
+ }
+}
+
+static __inline PVOID
+FrameworkGetFunction(_PAPI_DESCRIPTION ApiDescription)
+{
+ HANDLE hModule;
+ PVOID function = NULL;
+ PCHAR exportedFunctionName;
+
+ exportedFunctionName = FrameworkGetExportedFunctionNameInternal(ApiDescription);
+
+ hModule = _GetModuleHandleA(ApiDescription->FileName);
+ if (hModule != NULL)
+ {
+ function = _GetProcAddress(hModule, exportedFunctionName);
+ }
+ else
+ {
+ hModule = _LoadLibraryA(ApiDescription->FileName);
+ if (hModule != NULL)
+ {
+ function = _GetProcAddress(hModule, exportedFunctionName);
+ //FreeLibrary(hModule);
+ }
+ }
+ return function;
+}
+
+static __inline PVOID
+FrameworkGetHookInternal(ULONG index)
+{
+ PVOID address;
+ PCHAR exportedFunctionName;
+
+ if (index > MaxExternalDependency)
+ return NULL;
+
+ if (ExternalDependencies[index].MockFunctionAddress != NULL)
+ return ExternalDependencies[index].MockFunctionAddress;
+
+ if (ExternalDependencies[index].FunctionAddress != NULL)
+ return ExternalDependencies[index].FunctionAddress;
+
+ exportedFunctionName = FrameworkGetExportedFunctionNameInternal(&ExternalDependencies[index]);
+
+ printf("Calling function '%s' in DLL '%s'.\n",
+ exportedFunctionName,
+ ExternalDependencies[index].FileName);
+
+ address = FrameworkGetFunction(&ExternalDependencies[index]);
+
+ if (address == NULL)
+ {
+ printf("Function '%s' not found in DLL '%s'.\n",
+ exportedFunctionName,
+ ExternalDependencies[index].FileName);
+ }
+ ExternalDependencies[index].FunctionAddress = address;
+
+ return address;
+}
+
+
+static __inline VOID
+_SetHook(PCHAR name,
+ PVOID address)
+{
+ _PAPI_DESCRIPTION api;
+ ULONG index;
+
+ for (index = 0; index <= MaxExternalDependency; index++)
+ {
+ api = &ExternalDependencies[index];
+ if (strcmp(api->FunctionName, name) == 0)
+ {
+ api->MockFunctionAddress = address;
+ return;
+ }
+ }
+}
+
+typedef struct __HOOK
+{
+ PCHAR FunctionName;
+ PVOID FunctionAddress;
+} _HOOK, *_PHOOK;
+
+static __inline VOID
+_SetHooks(_PHOOK hookTable)
+{
+ _PHOOK hook;
+
+ hook = &hookTable[0];
+ while (hook->FunctionName != NULL)
+ {
+ _SetHook(hook->FunctionName,
+ hook->FunctionAddress);
+ hook++;
+ }
+}
+
+static __inline VOID
+_UnsetHooks(_PHOOK hookTable)
+{
+ _PHOOK hook;
+
+ hook = &hookTable[0];
+ while (hook->FunctionName != NULL)
+ {
+ _SetHook(hook->FunctionName,
+ NULL);
+ hook++;
+ }
+}
+
+static __inline VOID
+_UnsetAllHooks()
+{
+ _PAPI_DESCRIPTION api;
+ ULONG index;
+
+ for (index = 0; index <= MaxExternalDependency; index++)
+ {
+ api = &ExternalDependencies[index];
+ api->MockFunctionAddress = NULL;
+ }
+}
+
+#endif /* __REGTESTS_H */
--- /dev/null
+<module name="rtshared" type="staticlibrary">
+ <include base="rtshared">.</include>
+ <define name="__USE_W32API" />
+ <file>regtests.c</file>
+</module>
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE project SYSTEM "tools/rbuild/project.dtd">
+<group>
+<directory name="queuetest">
+ <xi:include href="queuetest/queuetest.rbuild" />
+</directory>
+</group>
--- /dev/null
+/*
+ * PROJECT: ReactOS Tests
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: queuetest.c
+ * PURPOSE: Usermode QueueUserWorkItem() testing
+ * PROGRAMMERS: Thomas Weidenmueller (w3seek@reactos.org)
+ */
+
+#include <windows.h>
+#include <stdio.h>
+
+#define WT_EXECUTEINPERSISTENTIOTHREAD 0x00000040
+BOOL WINAPI QueueUserWorkItem(LPTHREAD_START_ROUTINE,PVOID,ULONG);
+
+#define TestProc(n) \
+DWORD CALLBACK TestProc##n(void *ctx)\
+{\
+ printf("TestProc%d thread 0x%x context 0x%p\n", n, GetCurrentThreadId(), ctx);\
+ return 0;\
+}
+
+TestProc(1)
+TestProc(2)
+TestProc(3)
+TestProc(4)
+TestProc(5)
+TestProc(6)
+
+int __cdecl
+main(int argc, char* argv[])
+{
+ PVOID x = (PVOID)0x12345;
+ QueueUserWorkItem(TestProc1, x, 0);
+ QueueUserWorkItem(TestProc2, x, WT_EXECUTELONGFUNCTION);
+ QueueUserWorkItem(TestProc3, x, WT_EXECUTEINIOTHREAD);
+ QueueUserWorkItem(TestProc4, x, WT_EXECUTEINIOTHREAD | WT_EXECUTELONGFUNCTION);
+ QueueUserWorkItem(TestProc5, x, WT_EXECUTEINPERSISTENTTHREAD);
+ QueueUserWorkItem(TestProc6, x, WT_EXECUTEINPERSISTENTIOTHREAD);
+ Sleep(INFINITE);
+ return 0;
+}
--- /dev/null
+<module name="queuetest" type="win32cui" installbase="system32" installname="queuetest.exe" allowwarnings="true">
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <file>queuetest.c</file>
+</module>
--- /dev/null
+/*
+ * Kernel-Mode Tests Loader (based on PnP Test Driver Loader by Filip Navara)
+ *
+ * Copyright 2004 Filip Navara <xnavara@volny.cz>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; see the file COPYING.LIB.
+ * If not, write to the Free Software Foundation,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* INCLUDES *******************************************************************/
+
+#include <windows.h>
+#include <stdio.h>
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+int main()
+{
+ SC_HANDLE schSCManager;
+ SC_HANDLE schService;
+ PWCHAR DriverName = L"KMTEST";
+ WCHAR ServiceExe[MAX_PATH];
+
+ printf("Kernel Mode Tests loader\n\n");
+ GetCurrentDirectoryW(MAX_PATH, ServiceExe);
+ wcscat(ServiceExe, L"\\kmtest.sys");
+
+ printf("Opening SC Manager...\n");
+ schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+
+ if (schSCManager == NULL)
+ {
+ DWORD Err = GetLastError();
+ printf("OpenSCManager failed with error 0x%lx\n", Err);
+ return 0;
+ }
+
+ printf("Creating service...\n");
+ schService = CreateServiceW(
+ schSCManager,
+ DriverName,
+ DriverName,
+ SERVICE_ALL_ACCESS,
+ SERVICE_KERNEL_DRIVER,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ ServiceExe,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (schService == NULL)
+ {
+ printf("Opening service...\n");
+ schService = OpenServiceW(schSCManager, DriverName, SERVICE_ALL_ACCESS);
+ }
+
+ if (schService == NULL)
+ {
+ DWORD Err = GetLastError();
+ printf("Create/OpenService failed with error 0x%lx\n", Err);
+ CloseServiceHandle(schSCManager);
+ return 0;
+ }
+
+ //for (;;) ;
+
+ printf("Starting service...\n");
+ StartService(schService, 0, NULL);
+
+ printf("Cleaning up and exiting\n");
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+
+ return 0;
+}
--- /dev/null
+<module name="kmtloader" type="win32cui" installbase="system32" installname="kmtloader.exe">
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <library>advapi32</library>
+ <file>kmtloader.c</file>
+</module>
<?xml version="1.0"?>
<!DOCTYPE project SYSTEM "tools/rbuild/project.dtd">
<group>
+<directory name="kernel32">
+ <xi:include href="kernel32/directory.rbuild" />
+</directory>
+<directory name="kmtloader">
+ <xi:include href="kmtloader/kmtloader.rbuild" />
+</directory>
<directory name="smss">
<xi:include href="smss/smss.rbuild" />
</directory>
--- /dev/null
+<module name="advapi32_winetest" type="win32cui" installbase="bin" installname="advapi32_winetest.exe" allowwarnings="true">
+ <include base="advapi32_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>advapi32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>registry.c</file>
+ <file>security.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * Unit tests for crypt functions
+ *
+ * Copyright (c) 2004 Michael Jung
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wincrypt.h"
+#include "winerror.h"
+#include "winreg.h"
+
+#include "wine/test.h"
+
+static const char szRsaBaseProv[] = MS_DEF_PROV_A;
+static const char szNonExistentProv[] = "Wine Nonexistent Cryptographic Provider v11.2";
+static const char szKeySet[] = "wine_test_keyset";
+static const char szBadKeySet[] = "wine_test_bad_keyset";
+#define NON_DEF_PROV_TYPE 999
+
+static HMODULE hadvapi32;
+static BOOL (WINAPI *pCryptAcquireContextA)(HCRYPTPROV*,LPCSTR,LPCSTR,DWORD,DWORD);
+static BOOL (WINAPI *pCryptEnumProviderTypesA)(DWORD, DWORD*, DWORD, DWORD*, LPSTR, DWORD*);
+static BOOL (WINAPI *pCryptEnumProvidersA)(DWORD, DWORD*, DWORD, DWORD*, LPSTR, DWORD*);
+static BOOL (WINAPI *pCryptGetDefaultProviderA)(DWORD, DWORD*, DWORD, LPSTR, DWORD*);
+static BOOL (WINAPI *pCryptReleaseContext)(HCRYPTPROV, DWORD);
+static BOOL (WINAPI *pCryptSetProviderExA)(LPCSTR, DWORD, DWORD*, DWORD);
+static BOOL (WINAPI *pCryptCreateHash)(HCRYPTPROV, ALG_ID, HCRYPTKEY, DWORD, HCRYPTHASH*);
+static BOOL (WINAPI *pCryptDestroyHash)(HCRYPTHASH);
+static BOOL (WINAPI *pCryptGenRandom)(HCRYPTPROV, DWORD, BYTE*);
+static BOOL (WINAPI *pCryptContextAddRef)(HCRYPTPROV, DWORD*, DWORD dwFlags);
+static BOOL (WINAPI *pCryptGenKey)(HCRYPTPROV, ALG_ID, DWORD, HCRYPTKEY*);
+static BOOL (WINAPI *pCryptDestroyKey)(HCRYPTKEY);
+static BOOL (WINAPI *pCryptDecrypt)(HCRYPTKEY, HCRYPTHASH, BOOL, DWORD, BYTE*, DWORD*);
+static BOOL (WINAPI *pCryptDeriveKey)(HCRYPTPROV, ALG_ID, HCRYPTHASH, DWORD, HCRYPTKEY*);
+static BOOL (WINAPI *pCryptDuplicateHash)(HCRYPTHASH, DWORD*, DWORD, HCRYPTHASH*);
+static BOOL (WINAPI *pCryptDuplicateKey)(HCRYPTKEY, DWORD*, DWORD, HCRYPTKEY*);
+static BOOL (WINAPI *pCryptEncrypt)(HCRYPTKEY, HCRYPTHASH, BOOL, DWORD, BYTE*, DWORD*, DWORD);
+static BOOL (WINAPI *pCryptExportKey)(HCRYPTKEY, HCRYPTKEY, DWORD, DWORD, BYTE*, DWORD*);
+static BOOL (WINAPI *pCryptGetHashParam)(HCRYPTHASH, DWORD, BYTE*, DWORD*, DWORD);
+static BOOL (WINAPI *pCryptGetKeyParam)(HCRYPTKEY, DWORD, BYTE*, DWORD*, DWORD);
+static BOOL (WINAPI *pCryptGetProvParam)(HCRYPTPROV, DWORD, BYTE*, DWORD*, DWORD);
+static BOOL (WINAPI *pCryptGetUserKey)(HCRYPTPROV, DWORD, HCRYPTKEY*);
+static BOOL (WINAPI *pCryptHashData)(HCRYPTHASH, BYTE*, DWORD, DWORD);
+static BOOL (WINAPI *pCryptHashSessionKey)(HCRYPTHASH, HCRYPTKEY, DWORD);
+static BOOL (WINAPI *pCryptImportKey)(HCRYPTPROV, BYTE*, DWORD, HCRYPTKEY, DWORD, HCRYPTKEY*);
+static BOOL (WINAPI *pCryptSignHashW)(HCRYPTHASH, DWORD, LPCWSTR, DWORD, BYTE*, DWORD*);
+static BOOL (WINAPI *pCryptSetHashParam)(HCRYPTKEY, DWORD, BYTE*, DWORD);
+static BOOL (WINAPI *pCryptSetKeyParam)(HCRYPTKEY, DWORD, BYTE*, DWORD);
+static BOOL (WINAPI *pCryptSetProvParam)(HCRYPTPROV, DWORD, BYTE*, DWORD);
+static BOOL (WINAPI *pCryptVerifySignatureW)(HCRYPTHASH, BYTE*, DWORD, HCRYPTKEY, LPCWSTR, DWORD);
+
+static void init_function_pointers(void)
+{
+ hadvapi32 = GetModuleHandleA("advapi32.dll");
+
+ if(hadvapi32)
+ {
+ pCryptAcquireContextA = (void*)GetProcAddress(hadvapi32, "CryptAcquireContextA");
+ pCryptEnumProviderTypesA = (void*)GetProcAddress(hadvapi32, "CryptEnumProviderTypesA");
+ pCryptEnumProvidersA = (void*)GetProcAddress(hadvapi32, "CryptEnumProvidersA");
+ pCryptGetDefaultProviderA = (void*)GetProcAddress(hadvapi32, "CryptGetDefaultProviderA");
+ pCryptReleaseContext = (void*)GetProcAddress(hadvapi32, "CryptReleaseContext");
+ pCryptSetProviderExA = (void*)GetProcAddress(hadvapi32, "CryptSetProviderExA");
+ pCryptCreateHash = (void*)GetProcAddress(hadvapi32, "CryptCreateHash");
+ pCryptDestroyHash = (void*)GetProcAddress(hadvapi32, "CryptDestroyHash");
+ pCryptGenRandom = (void*)GetProcAddress(hadvapi32, "CryptGenRandom");
+ pCryptContextAddRef = (void*)GetProcAddress(hadvapi32, "CryptContextAddRef");
+ pCryptGenKey = (void*)GetProcAddress(hadvapi32, "CryptGenKey");
+ pCryptDestroyKey = (void*)GetProcAddress(hadvapi32, "CryptDestroyKey");
+ pCryptDecrypt = (void*)GetProcAddress(hadvapi32, "CryptDecrypt");
+ pCryptDeriveKey = (void*)GetProcAddress(hadvapi32, "CryptDeriveKey");
+ pCryptDuplicateHash = (void*)GetProcAddress(hadvapi32, "CryptDuplicateHash");
+ pCryptDuplicateKey = (void*)GetProcAddress(hadvapi32, "CryptDuplicateKey");
+ pCryptEncrypt = (void*)GetProcAddress(hadvapi32, "CryptEncrypt");
+ pCryptExportKey = (void*)GetProcAddress(hadvapi32, "CryptExportKey");
+ pCryptGetHashParam = (void*)GetProcAddress(hadvapi32, "CryptGetHashParam");
+ pCryptGetKeyParam = (void*)GetProcAddress(hadvapi32, "CryptGetKeyParam");
+ pCryptGetProvParam = (void*)GetProcAddress(hadvapi32, "CryptGetProvParam");
+ pCryptGetUserKey = (void*)GetProcAddress(hadvapi32, "CryptGetUserKey");
+ pCryptHashData = (void*)GetProcAddress(hadvapi32, "CryptHashData");
+ pCryptHashSessionKey = (void*)GetProcAddress(hadvapi32, "CryptHashSessionKey");
+ pCryptImportKey = (void*)GetProcAddress(hadvapi32, "CryptImportKey");
+ pCryptSignHashW = (void*)GetProcAddress(hadvapi32, "CryptSignHashW");
+ pCryptSetHashParam = (void*)GetProcAddress(hadvapi32, "CryptSetHashParam");
+ pCryptSetKeyParam = (void*)GetProcAddress(hadvapi32, "CryptSetKeyParam");
+ pCryptSetProvParam = (void*)GetProcAddress(hadvapi32, "CryptSetProvParam");
+ pCryptVerifySignatureW = (void*)GetProcAddress(hadvapi32, "CryptVerifySignatureW");
+ }
+}
+
+static void init_environment(void)
+{
+ HCRYPTPROV hProv;
+
+ /* Ensure that container "wine_test_keyset" does exist */
+ if (!pCryptAcquireContextA(&hProv, szKeySet, szRsaBaseProv, PROV_RSA_FULL, 0))
+ {
+ pCryptAcquireContextA(&hProv, szKeySet, szRsaBaseProv, PROV_RSA_FULL, CRYPT_NEWKEYSET);
+ }
+ pCryptReleaseContext(hProv, 0);
+
+ /* Ensure that container "wine_test_keyset" does exist in default PROV_RSA_FULL type provider */
+ if (!pCryptAcquireContextA(&hProv, szKeySet, NULL, PROV_RSA_FULL, 0))
+ {
+ pCryptAcquireContextA(&hProv, szKeySet, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET);
+ }
+ pCryptReleaseContext(hProv, 0);
+
+ /* Ensure that container "wine_test_bad_keyset" does not exist. */
+ if (pCryptAcquireContextA(&hProv, szBadKeySet, szRsaBaseProv, PROV_RSA_FULL, 0))
+ {
+ pCryptReleaseContext(hProv, 0);
+ pCryptAcquireContextA(&hProv, szBadKeySet, szRsaBaseProv, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
+ }
+}
+
+static void clean_up_environment(void)
+{
+ HCRYPTPROV hProv;
+
+ /* Remove container "wine_test_keyset" */
+ if (pCryptAcquireContextA(&hProv, szKeySet, szRsaBaseProv, PROV_RSA_FULL, 0))
+ {
+ pCryptReleaseContext(hProv, 0);
+ pCryptAcquireContextA(&hProv, szKeySet, szRsaBaseProv, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
+ }
+
+ /* Remove container "wine_test_keyset" from default PROV_RSA_FULL type provider */
+ if (pCryptAcquireContextA(&hProv, szKeySet, NULL, PROV_RSA_FULL, 0))
+ {
+ pCryptReleaseContext(hProv, 0);
+ pCryptAcquireContextA(&hProv, szKeySet, NULL, PROV_RSA_FULL, CRYPT_DELETEKEYSET);
+ }
+}
+
+static void test_acquire_context(void)
+{
+ BOOL result;
+ HCRYPTPROV hProv;
+
+ /* Provoke all kinds of error conditions (which are easy to provoke).
+ * The order of the error tests seems to match Windows XP's rsaenh.dll CSP,
+ * but since this is likely to change between CSP versions, we don't check
+ * this. Please don't change the order of tests. */
+ result = pCryptAcquireContextA(&hProv, NULL, NULL, 0, 0);
+ ok(!result && GetLastError()==NTE_BAD_PROV_TYPE, "%ld\n", GetLastError());
+
+ result = pCryptAcquireContextA(&hProv, NULL, NULL, 1000, 0);
+ ok(!result && GetLastError()==NTE_BAD_PROV_TYPE, "%ld\n", GetLastError());
+
+ result = pCryptAcquireContextA(&hProv, NULL, NULL, NON_DEF_PROV_TYPE, 0);
+ ok(!result && GetLastError()==NTE_PROV_TYPE_NOT_DEF, "%ld\n", GetLastError());
+
+ result = pCryptAcquireContextA(&hProv, szKeySet, szNonExistentProv, PROV_RSA_FULL, 0);
+ ok(!result && GetLastError()==NTE_KEYSET_NOT_DEF, "%ld\n", GetLastError());
+
+ result = pCryptAcquireContextA(&hProv, szKeySet, szRsaBaseProv, NON_DEF_PROV_TYPE, 0);
+ ok(!result && GetLastError()==NTE_PROV_TYPE_NO_MATCH, "%ld\n", GetLastError());
+
+ /* This test fails under Win2k SP4:
+ result = TRUE, GetLastError() == ERROR_INVALID_PARAMETER
+ SetLastError(0xdeadbeef);
+ result = pCryptAcquireContextA(NULL, szKeySet, szRsaBaseProv, PROV_RSA_FULL, 0);
+ ok(!result && GetLastError()==ERROR_INVALID_PARAMETER, "%d/%ld\n", result, GetLastError());
+ */
+
+ /* Last not least, try to really acquire a context. */
+ hProv = 0;
+ SetLastError(0xdeadbeef);
+ result = pCryptAcquireContextA(&hProv, szKeySet, szRsaBaseProv, PROV_RSA_FULL, 0);
+ ok(result && (GetLastError() == ERROR_ENVVAR_NOT_FOUND || GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_RING2_STACK_IN_USE || GetLastError() == NTE_FAIL), "%d/%ld\n", result, GetLastError());
+
+ if (hProv)
+ pCryptReleaseContext(hProv, 0);
+
+ /* Try again, witch an empty ("\0") szProvider parameter */
+ hProv = 0;
+ SetLastError(0xdeadbeef);
+ result = pCryptAcquireContextA(&hProv, szKeySet, "", PROV_RSA_FULL, 0);
+ ok(result && (GetLastError() == ERROR_ENVVAR_NOT_FOUND || GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_RING2_STACK_IN_USE || GetLastError() == NTE_FAIL), "%d/%ld\n", result, GetLastError());
+
+ if (hProv)
+ pCryptReleaseContext(hProv, 0);
+}
+
+static void test_incorrect_api_usage(void)
+{
+ BOOL result;
+ HCRYPTPROV hProv, hProv2;
+ HCRYPTHASH hHash, hHash2;
+ HCRYPTKEY hKey, hKey2;
+ BYTE temp;
+ DWORD dwLen, dwTemp;
+
+ /* This is to document incorrect api usage in the
+ * "Uru - Ages beyond Myst Demo" installer as reported by Paul Vriens.
+ *
+ * The installer destroys a hash object after having released the context
+ * with which the hash was created. This is not allowed according to MSDN,
+ * since CryptReleaseContext destroys all hash and key objects belonging to
+ * the respective context. However, while wine used to crash, Windows is more
+ * robust here and returns an ERROR_INVALID_PARAMETER code.
+ */
+
+ result = pCryptAcquireContextA(&hProv, szBadKeySet, szRsaBaseProv,
+ PROV_RSA_FULL, CRYPT_NEWKEYSET);
+ ok (result, "%08lx\n", GetLastError());
+ if (!result) return;
+
+ result = pCryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash);
+ ok (result, "%ld\n", GetLastError());
+ if (!result) return;
+
+ result = pCryptGenKey(hProv, CALG_RC4, 0, &hKey);
+ ok (result, "%ld\n", GetLastError());
+ if (!result) return;
+
+ result = pCryptGenKey(hProv, CALG_RC4, 0, &hKey2);
+ ok (result, "%ld\n", GetLastError());
+ if (!result) return;
+
+ result = pCryptDestroyKey(hKey2);
+ ok (result, "%ld\n", GetLastError());
+
+ dwTemp = CRYPT_MODE_ECB;
+ result = pCryptSetKeyParam(hKey2, KP_MODE, (BYTE*)&dwTemp, sizeof(DWORD));
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptAcquireContextA(&hProv2, szBadKeySet, NULL, PROV_RSA_FULL,
+ CRYPT_DELETEKEYSET);
+ ok (result, "%ld\n", GetLastError());
+ if (!result) return;
+
+ result = pCryptReleaseContext(hProv, 0);
+ ok (result, "%ld\n", GetLastError());
+ if (!result) return;
+
+ result = pCryptReleaseContext(hProv, 0);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptGenRandom(hProv, 1, &temp);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+#ifdef CRASHES_ON_NT40
+ result = pCryptContextAddRef(hProv, NULL, 0);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+#endif
+
+ result = pCryptCreateHash(hProv, CALG_SHA, 0, 0, &hHash2);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ dwLen = 1;
+ result = pCryptDecrypt(hKey, (HCRYPTHASH)NULL, TRUE, 0, &temp, &dwLen);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ dwLen = 1;
+ result = pCryptEncrypt(hKey, (HCRYPTHASH)NULL, TRUE, 0, &temp, &dwLen, 1);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptDeriveKey(hProv, CALG_RC4, hHash, 0, &hKey2);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+#ifdef CRASHES_ON_NT40
+ result = pCryptDuplicateHash(hHash, NULL, 0, &hHash2);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptDuplicateKey(hKey, NULL, 0, &hKey2);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+#endif
+
+ dwLen = 1;
+ result = pCryptExportKey(hKey, (HCRYPTPROV)NULL, 0, 0, &temp, &dwLen);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptGenKey(hProv, CALG_RC4, 0, &hKey2);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ dwLen = 1;
+ result = pCryptGetHashParam(hHash, 0, &temp, &dwLen, 0);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ dwLen = 1;
+ result = pCryptGetKeyParam(hKey, 0, &temp, &dwLen, 0);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ dwLen = 1;
+ result = pCryptGetProvParam(hProv, 0, &temp, &dwLen, 0);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptGetUserKey(hProv, 0, &hKey2);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptHashData(hHash, &temp, 1, 0);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptHashSessionKey(hHash, hKey, 0);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptImportKey(hProv, &temp, 1, (HCRYPTKEY)NULL, 0, &hKey2);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ dwLen = 1;
+ result = pCryptSignHashW(hHash, 0, NULL, 0, &temp, &dwLen);
+ ok (!result && (GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), "%ld\n", GetLastError());
+
+ result = pCryptSetKeyParam(hKey, 0, &temp, 1);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptSetHashParam(hHash, 0, &temp, 1);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptSetProvParam(hProv, 0, &temp, 1);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptVerifySignatureW(hHash, &temp, 1, hKey, NULL, 0);
+ ok (!result && (GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_CALL_NOT_IMPLEMENTED), "%ld\n", GetLastError());
+
+ result = pCryptDestroyHash(hHash);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ result = pCryptDestroyKey(hKey);
+ ok (!result && GetLastError() == ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+}
+
+static BOOL FindProvRegVals(DWORD dwIndex, DWORD *pdwProvType, LPSTR *pszProvName,
+ DWORD *pcbProvName, DWORD *pdwProvCount)
+{
+ HKEY hKey;
+ HKEY subkey;
+ DWORD size = sizeof(DWORD);
+
+ if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\Defaults\\Provider", &hKey))
+ return FALSE;
+
+ RegQueryInfoKey(hKey, NULL, NULL, NULL, pdwProvCount, pcbProvName,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ (*pcbProvName)++;
+
+ if (!(*pszProvName = ((LPSTR)LocalAlloc(LMEM_ZEROINIT, *pcbProvName))))
+ return FALSE;
+
+ RegEnumKeyEx(hKey, dwIndex, *pszProvName, pcbProvName, NULL, NULL, NULL, NULL);
+ (*pcbProvName)++;
+
+ RegOpenKey(hKey, *pszProvName, &subkey);
+ RegQueryValueEx(subkey, "Type", NULL, NULL, (BYTE*)pdwProvType, &size);
+
+ RegCloseKey(subkey);
+ RegCloseKey(hKey);
+
+ return TRUE;
+}
+
+static void test_enum_providers(void)
+{
+ /* expected results */
+ CHAR *pszProvName = NULL;
+ DWORD cbName;
+ DWORD dwType;
+ DWORD provCount;
+ DWORD dwIndex = 0;
+
+ /* actual results */
+ CHAR *provider = NULL;
+ DWORD providerLen;
+ DWORD type;
+ DWORD count;
+ BOOL result;
+ DWORD notNull = 5;
+ DWORD notZeroFlags = 5;
+
+ if(!pCryptEnumProvidersA)
+ {
+ trace("skipping CryptEnumProviders tests\n");
+ return;
+ }
+
+ if (!FindProvRegVals(dwIndex, &dwType, &pszProvName, &cbName, &provCount))
+ return;
+
+ /* check pdwReserved flag for NULL */
+ result = pCryptEnumProvidersA(dwIndex, ¬Null, 0, &type, NULL, &providerLen);
+ ok(!result && GetLastError()==ERROR_INVALID_PARAMETER, "%ld\n", GetLastError());
+
+ /* check dwFlags == 0 */
+ result = pCryptEnumProvidersA(dwIndex, NULL, notZeroFlags, &type, NULL, &providerLen);
+ ok(!result && GetLastError()==NTE_BAD_FLAGS, "%ld\n", GetLastError());
+
+ /* alloc provider to half the size required
+ * cbName holds the size required */
+ providerLen = cbName / 2;
+ if (!(provider = ((LPSTR)LocalAlloc(LMEM_ZEROINIT, providerLen))))
+ return;
+
+ result = pCryptEnumProvidersA(dwIndex, NULL, 0, &type, provider, &providerLen);
+ ok(!result && GetLastError()==ERROR_MORE_DATA, "expected %i, got %ld\n",
+ ERROR_MORE_DATA, GetLastError());
+
+ LocalFree(provider);
+
+ /* loop through the providers to get the number of providers
+ * after loop ends, count should be provCount + 1 so subtract 1
+ * to get actual number of providers */
+ count = 0;
+ while(pCryptEnumProvidersA(count++, NULL, 0, &type, NULL, &providerLen))
+ ;
+ count--;
+ ok(count==provCount, "expected %i, got %i\n", (int)provCount, (int)count);
+
+ /* loop past the actual number of providers to get the error
+ * ERROR_NO_MORE_ITEMS */
+ for (count = 0; count < provCount + 1; count++)
+ result = pCryptEnumProvidersA(count, NULL, 0, &type, NULL, &providerLen);
+ ok(!result && GetLastError()==ERROR_NO_MORE_ITEMS, "expected %i, got %ld\n",
+ ERROR_NO_MORE_ITEMS, GetLastError());
+
+ /* check expected versus actual values returned */
+ result = pCryptEnumProvidersA(dwIndex, NULL, 0, &type, NULL, &providerLen);
+ ok(result && providerLen==cbName, "expected %i, got %i\n", (int)cbName, (int)providerLen);
+ if (!(provider = ((LPSTR)LocalAlloc(LMEM_ZEROINIT, providerLen))))
+ return;
+
+ result = pCryptEnumProvidersA(dwIndex, NULL, 0, &type, provider, &providerLen);
+ ok(result && type==dwType, "expected %ld, got %ld\n",
+ dwType, type);
+ ok(result && !strcmp(pszProvName, provider), "expected %s, got %s\n", pszProvName, provider);
+ ok(result && cbName==providerLen, "expected %ld, got %ld\n",
+ cbName, providerLen);
+
+ LocalFree(provider);
+}
+
+static BOOL FindProvTypesRegVals(DWORD dwIndex, DWORD *pdwProvType, LPSTR *pszTypeName,
+ DWORD *pcbTypeName, DWORD *pdwTypeCount)
+{
+ HKEY hKey;
+ HKEY hSubKey;
+ PSTR ch;
+
+ if (RegOpenKey(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Cryptography\\Defaults\\Provider Types", &hKey))
+ return FALSE;
+
+ if (RegQueryInfoKey(hKey, NULL, NULL, NULL, pdwTypeCount, pcbTypeName, NULL,
+ NULL, NULL, NULL, NULL, NULL))
+ return FALSE;
+ (*pcbTypeName)++;
+
+ if (!(*pszTypeName = ((LPSTR)LocalAlloc(LMEM_ZEROINIT, *pcbTypeName))))
+ return FALSE;
+
+ if (RegEnumKeyEx(hKey, dwIndex, *pszTypeName, pcbTypeName, NULL, NULL, NULL, NULL))
+ return FALSE;
+ (*pcbTypeName)++;
+ ch = *pszTypeName + strlen(*pszTypeName);
+ /* Convert "Type 000" to 0, etc/ */
+ *pdwProvType = *(--ch) - '0';
+ *pdwProvType += (*(--ch) - '0') * 10;
+ *pdwProvType += (*(--ch) - '0') * 100;
+
+ if (RegOpenKey(hKey, *pszTypeName, &hSubKey))
+ return FALSE;
+
+ if (RegQueryValueEx(hSubKey, "TypeName", NULL, NULL, NULL, pcbTypeName))
+ return FALSE;
+
+ if (!(*pszTypeName = ((LPSTR)LocalAlloc(LMEM_ZEROINIT, *pcbTypeName))))
+ return FALSE;
+
+ if (RegQueryValueEx(hSubKey, "TypeName", NULL, NULL, *pszTypeName, pcbTypeName))
+ return FALSE;
+
+ RegCloseKey(hSubKey);
+ RegCloseKey(hKey);
+
+ return TRUE;
+}
+
+static void test_enum_provider_types()
+{
+ /* expected values */
+ DWORD dwProvType;
+ LPSTR pszTypeName = NULL;
+ DWORD cbTypeName;
+ DWORD dwTypeCount;
+
+ /* actual values */
+ DWORD index = 0;
+ DWORD provType;
+ LPSTR typeName = NULL;
+ DWORD typeNameSize;
+ DWORD typeCount;
+ DWORD result;
+ DWORD notNull = 5;
+ DWORD notZeroFlags = 5;
+
+ if(!pCryptEnumProviderTypesA)
+ {
+ trace("skipping CryptEnumProviderTypes tests\n");
+ return;
+ }
+
+ if (!FindProvTypesRegVals(index, &dwProvType, &pszTypeName, &cbTypeName, &dwTypeCount))
+ {
+ trace("could not find provider types in registry, skipping the test\n");
+ return;
+ }
+
+ /* check pdwReserved for NULL */
+ result = pCryptEnumProviderTypesA(index, ¬Null, 0, &provType, typeName, &typeNameSize);
+ ok(!result && GetLastError()==ERROR_INVALID_PARAMETER, "expected %i, got %ld\n",
+ ERROR_INVALID_PARAMETER, GetLastError());
+
+ /* check dwFlags == zero */
+ result = pCryptEnumProviderTypesA(index, NULL, notZeroFlags, &provType, typeName, &typeNameSize);
+ ok(!result && GetLastError()==NTE_BAD_FLAGS, "expected %i, got %ld\n",
+ ERROR_INVALID_PARAMETER, GetLastError());
+
+ /* alloc provider type to half the size required
+ * cbTypeName holds the size required */
+ typeNameSize = cbTypeName / 2;
+ if (!(typeName = ((LPSTR)LocalAlloc(LMEM_ZEROINIT, typeNameSize))))
+ return;
+
+ /* This test fails under Win2k SP4:
+ result = TRUE, GetLastError() == 0xdeadbeef
+ SetLastError(0xdeadbeef);
+ result = pCryptEnumProviderTypesA(index, NULL, 0, &provType, typeName, &typeNameSize);
+ ok(!result && GetLastError()==ERROR_MORE_DATA, "expected 0/ERROR_MORE_DATA, got %d/%08lx\n",
+ result, GetLastError());
+ */
+
+ LocalFree(typeName);
+
+ /* loop through the provider types to get the number of provider types
+ * after loop ends, count should be dwTypeCount + 1 so subtract 1
+ * to get actual number of provider types */
+ typeCount = 0;
+ while(pCryptEnumProviderTypesA(typeCount++, NULL, 0, &provType, NULL, &typeNameSize))
+ ;
+ typeCount--;
+ ok(typeCount==dwTypeCount, "expected %ld, got %ld\n", dwTypeCount, typeCount);
+
+ /* loop past the actual number of provider types to get the error
+ * ERROR_NO_MORE_ITEMS */
+ for (typeCount = 0; typeCount < dwTypeCount + 1; typeCount++)
+ result = pCryptEnumProviderTypesA(typeCount, NULL, 0, &provType, NULL, &typeNameSize);
+ ok(!result && GetLastError()==ERROR_NO_MORE_ITEMS, "expected %i, got %ld\n",
+ ERROR_NO_MORE_ITEMS, GetLastError());
+
+
+ /* check expected versus actual values returned */
+ result = pCryptEnumProviderTypesA(index, NULL, 0, &provType, NULL, &typeNameSize);
+ ok(result && typeNameSize==cbTypeName, "expected %ld, got %ld\n", cbTypeName, typeNameSize);
+ if (!(typeName = ((LPSTR)LocalAlloc(LMEM_ZEROINIT, typeNameSize))))
+ return;
+
+ typeNameSize = 0xdeadbeef;
+ result = pCryptEnumProviderTypesA(index, NULL, 0, &provType, typeName, &typeNameSize);
+ ok(result, "expected TRUE, got %ld\n", result);
+ ok(provType==dwProvType, "expected %ld, got %ld\n", dwProvType, provType);
+ if (pszTypeName)
+ ok(!strcmp(pszTypeName, typeName), "expected %s, got %s\n", pszTypeName, typeName);
+ ok(typeNameSize==cbTypeName, "expected %ld, got %ld\n", cbTypeName, typeNameSize);
+
+ LocalFree(typeName);
+}
+
+static BOOL FindDfltProvRegVals(DWORD dwProvType, DWORD dwFlags, LPSTR *pszProvName, DWORD *pcbProvName)
+{
+ HKEY hKey;
+ PSTR keyname;
+ PSTR ptr;
+ DWORD user = dwFlags & CRYPT_USER_DEFAULT;
+
+ LPSTR MACHINESTR = "Software\\Microsoft\\Cryptography\\Defaults\\Provider Types\\Type XXX";
+ LPSTR USERSTR = "Software\\Microsoft\\Cryptography\\Provider Type XXX";
+
+ keyname = LocalAlloc(LMEM_ZEROINIT, (user ? strlen(USERSTR) : strlen(MACHINESTR)) + 1);
+ if (keyname)
+ {
+ user ? strcpy(keyname, USERSTR) : strcpy(keyname, MACHINESTR);
+ ptr = keyname + strlen(keyname);
+ *(--ptr) = (dwProvType % 10) + '0';
+ *(--ptr) = ((dwProvType / 10) % 10) + '0';
+ *(--ptr) = (dwProvType / 100) + '0';
+ } else
+ return FALSE;
+
+ if (RegOpenKey((dwFlags & CRYPT_USER_DEFAULT) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE ,keyname, &hKey))
+ {
+ LocalFree(keyname);
+ return FALSE;
+ }
+ LocalFree(keyname);
+
+ if (RegQueryValueEx(hKey, "Name", NULL, NULL, *pszProvName, pcbProvName))
+ {
+ if (GetLastError() != ERROR_MORE_DATA)
+ SetLastError(NTE_PROV_TYPE_ENTRY_BAD);
+ return FALSE;
+ }
+
+ if (!(*pszProvName = LocalAlloc(LMEM_ZEROINIT, *pcbProvName)))
+ return FALSE;
+
+ if (RegQueryValueEx(hKey, "Name", NULL, NULL, *pszProvName, pcbProvName))
+ {
+ if (GetLastError() != ERROR_MORE_DATA)
+ SetLastError(NTE_PROV_TYPE_ENTRY_BAD);
+ return FALSE;
+ }
+
+ RegCloseKey(hKey);
+
+ return TRUE;
+}
+
+static void test_get_default_provider()
+{
+ /* expected results */
+ DWORD dwProvType = PROV_RSA_FULL;
+ DWORD dwFlags = CRYPT_MACHINE_DEFAULT;
+ LPSTR pszProvName = NULL;
+ DWORD cbProvName;
+
+ /* actual results */
+ DWORD provType = PROV_RSA_FULL;
+ DWORD flags = CRYPT_MACHINE_DEFAULT;
+ LPSTR provName = NULL;
+ DWORD provNameSize;
+ DWORD result;
+ DWORD notNull = 5;
+
+ if(!pCryptGetDefaultProviderA)
+ {
+ trace("skipping CryptGetDefaultProvider tests\n");
+ return;
+ }
+
+ FindDfltProvRegVals(dwProvType, dwFlags, &pszProvName, &cbProvName);
+
+ /* check pdwReserved for NULL */
+ result = pCryptGetDefaultProviderA(provType, ¬Null, flags, provName, &provNameSize);
+ ok(!result && GetLastError()==ERROR_INVALID_PARAMETER, "expected %i, got %ld\n",
+ ERROR_INVALID_PARAMETER, GetLastError());
+
+ /* check for invalid flag */
+ flags = 0xdeadbeef;
+ result = pCryptGetDefaultProviderA(provType, NULL, flags, provName, &provNameSize);
+ ok(!result && GetLastError()==NTE_BAD_FLAGS, "expected %ld, got %ld\n",
+ NTE_BAD_FLAGS, GetLastError());
+ flags = CRYPT_MACHINE_DEFAULT;
+
+ /* check for invalid prov type */
+ provType = 0xdeadbeef;
+ result = pCryptGetDefaultProviderA(provType, NULL, flags, provName, &provNameSize);
+ ok(!result && (GetLastError() == NTE_BAD_PROV_TYPE ||
+ GetLastError() == ERROR_INVALID_PARAMETER),
+ "expected NTE_BAD_PROV_TYPE or ERROR_INVALID_PARAMETER, got %ld/%ld\n",
+ result, GetLastError());
+ provType = PROV_RSA_FULL;
+
+ SetLastError(0);
+
+ /* alloc provName to half the size required
+ * cbProvName holds the size required */
+ provNameSize = cbProvName / 2;
+ if (!(provName = LocalAlloc(LMEM_ZEROINIT, provNameSize)))
+ return;
+
+ result = pCryptGetDefaultProviderA(provType, NULL, flags, provName, &provNameSize);
+ ok(!result && GetLastError()==ERROR_MORE_DATA, "expected %i, got %ld\n",
+ ERROR_MORE_DATA, GetLastError());
+
+ LocalFree(provName);
+
+ /* check expected versus actual values returned */
+ result = pCryptGetDefaultProviderA(provType, NULL, flags, NULL, &provNameSize);
+ ok(result && provNameSize==cbProvName, "expected %ld, got %ld\n", cbProvName, provNameSize);
+ provNameSize = cbProvName;
+
+ if (!(provName = LocalAlloc(LMEM_ZEROINIT, provNameSize)))
+ return;
+
+ result = pCryptGetDefaultProviderA(provType, NULL, flags, provName, &provNameSize);
+ ok(result && !strcmp(pszProvName, provName), "expected %s, got %s\n", pszProvName, provName);
+ ok(result && provNameSize==cbProvName, "expected %ld, got %ld\n", cbProvName, provNameSize);
+
+ LocalFree(provName);
+}
+
+static void test_set_provider_ex()
+{
+ DWORD result;
+ DWORD notNull = 5;
+
+ /* results */
+ LPSTR pszProvName = NULL;
+ DWORD cbProvName;
+
+ if(!pCryptGetDefaultProviderA || !pCryptSetProviderExA)
+ {
+ trace("skipping CryptSetProviderEx tests\n");
+ return;
+ }
+
+ /* check pdwReserved for NULL */
+ result = pCryptSetProviderExA(MS_DEF_PROV, PROV_RSA_FULL, ¬Null, CRYPT_MACHINE_DEFAULT);
+ ok(!result && GetLastError()==ERROR_INVALID_PARAMETER, "expected %i, got %ld\n",
+ ERROR_INVALID_PARAMETER, GetLastError());
+
+ /* remove the default provider and then set it to MS_DEF_PROV/PROV_RSA_FULL */
+ result = pCryptSetProviderExA(MS_DEF_PROV, PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT | CRYPT_DELETE_DEFAULT);
+ ok(result, "%ld\n", GetLastError());
+
+ result = pCryptSetProviderExA(MS_DEF_PROV, PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT);
+ ok(result, "%ld\n", GetLastError());
+
+ /* call CryptGetDefaultProvider to see if they match */
+ result = pCryptGetDefaultProviderA(PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT, NULL, &cbProvName);
+ if (!(pszProvName = LocalAlloc(LMEM_ZEROINIT, cbProvName)))
+ return;
+
+ result = pCryptGetDefaultProviderA(PROV_RSA_FULL, NULL, CRYPT_MACHINE_DEFAULT, pszProvName, &cbProvName);
+ ok(result && !strcmp(MS_DEF_PROV, pszProvName), "expected %s, got %s\n", MS_DEF_PROV, pszProvName);
+ ok(result && cbProvName==(strlen(MS_DEF_PROV) + 1), "expected %i, got %ld\n", (strlen(MS_DEF_PROV) + 1), cbProvName);
+
+ LocalFree(pszProvName);
+}
+
+START_TEST(crypt)
+{
+ init_function_pointers();
+ if(pCryptAcquireContextA && pCryptReleaseContext) {
+ init_environment();
+ test_acquire_context();
+ test_incorrect_api_usage();
+ clean_up_environment();
+ }
+
+ test_enum_providers();
+ test_enum_provider_types();
+ test_get_default_provider();
+ test_set_provider_ex();
+}
--- /dev/null
+/*
+ * Unit tests for SystemFunction006 (LMHash?)
+ *
+ * Copyright 2004 Hans Leidekker
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+
+typedef VOID (WINAPI *fnSystemFunction006)( PCSTR passwd, PSTR lmhash );
+fnSystemFunction006 pSystemFunction006;
+
+static void test_SystemFunction006()
+{
+ static unsigned char lmhash[16 + 1];
+
+ unsigned char passwd[] = { 's','e','c','r','e','t', 0, 0, 0, 0, 0, 0, 0, 0 };
+ unsigned char expect[] =
+ { 0x85, 0xf5, 0x28, 0x9f, 0x09, 0xdc, 0xa7, 0xeb,
+ 0xaa, 0xd3, 0xb4, 0x35, 0xb5, 0x14, 0x04, 0xee };
+
+ pSystemFunction006( passwd, lmhash );
+
+ ok( !memcmp( lmhash, expect, sizeof(expect) ),
+ "lmhash: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ lmhash[0], lmhash[1], lmhash[2], lmhash[3], lmhash[4], lmhash[5],
+ lmhash[6], lmhash[7], lmhash[8], lmhash[9], lmhash[10], lmhash[11],
+ lmhash[12], lmhash[13], lmhash[14], lmhash[15] );
+}
+
+START_TEST(crypt_lmhash)
+{
+ HMODULE module;
+
+ if (!(module = LoadLibrary("advapi32.dll"))) return;
+
+ pSystemFunction006 = (fnSystemFunction006)GetProcAddress( module, "SystemFunction006" );
+
+ if (!pSystemFunction006) goto out;
+
+ if (pSystemFunction006)
+ test_SystemFunction006();
+
+out:
+ FreeLibrary( module );
+}
--- /dev/null
+/*
+ * Unit tests for MD4 functions
+ *
+ * Copyright 2004 Hans Leidekker
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+typedef struct
+{
+ unsigned int buf[4];
+ unsigned int i[2];
+ unsigned char in[64];
+ unsigned char digest[16];
+} MD4_CTX;
+
+typedef VOID (WINAPI *fnMD4Init)( MD4_CTX *ctx );
+typedef VOID (WINAPI *fnMD4Update)( MD4_CTX *ctx, const unsigned char *src, const int len );
+typedef VOID (WINAPI *fnMD4Final)( MD4_CTX *ctx );
+
+fnMD4Init pMD4Init;
+fnMD4Update pMD4Update;
+fnMD4Final pMD4Final;
+
+#define ctxcmp( a, b ) memcmp( (char*)a, (char*)b, FIELD_OFFSET( MD4_CTX, in ) )
+
+void test_md4_ctx()
+{
+ static unsigned char message[] =
+ "In our Life there's If"
+ "In our beliefs there's Lie"
+ "In our business there is Sin"
+ "In our bodies, there is Die";
+
+ int size = strlen( message );
+ HMODULE module;
+
+ MD4_CTX ctx;
+ MD4_CTX ctx_initialized =
+ {
+ { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 },
+ { 0, 0 }
+ };
+
+ MD4_CTX ctx_update1 =
+ {
+ { 0x5e592ef7, 0xbdcb1567, 0x2b626d17, 0x7d1198bd },
+ { 0x00000338, 0 }
+ };
+
+ MD4_CTX ctx_update2 =
+ {
+ { 0x05dcfd65, 0xb3711c0d, 0x9e3369c2, 0x903ead11 },
+ { 0x00000670, 0 }
+ };
+
+ unsigned char expect[16] =
+ { 0x5f, 0xd3, 0x9b, 0x29, 0x47, 0x53, 0x47, 0xaf,
+ 0xa5, 0xba, 0x0c, 0x05, 0xff, 0xc0, 0xc7, 0xda };
+
+ if (!(module = LoadLibrary( "advapi32.dll" ))) return;
+
+ pMD4Init = (fnMD4Init)GetProcAddress( module, "MD4Init" );
+ pMD4Update = (fnMD4Update)GetProcAddress( module, "MD4Update" );
+ pMD4Final = (fnMD4Final)GetProcAddress( module, "MD4Final" );
+
+ if (!pMD4Init || !pMD4Update || !pMD4Final) goto out;
+
+ memset( &ctx, 0, sizeof(ctx) );
+ pMD4Init( &ctx );
+ ok( !ctxcmp( &ctx, &ctx_initialized ), "invalid initialization\n" );
+
+ pMD4Update( &ctx, message, size );
+ ok( !ctxcmp( &ctx, &ctx_update1 ), "update doesn't work correctly\n" );
+
+ pMD4Update( &ctx, message, size );
+ ok( !ctxcmp( &ctx, &ctx_update2 ), "update doesn't work correctly\n" );
+
+ pMD4Final( &ctx );
+ ok( ctxcmp( &ctx, &ctx_initialized ), "context has changed\n" );
+ ok( !memcmp( ctx.digest, expect, sizeof(expect) ), "incorrect result\n" );
+
+out:
+ FreeLibrary( module );
+}
+
+START_TEST(crypt_md4)
+{
+ test_md4_ctx();
+}
--- /dev/null
+/*
+ * Unit tests for MD5 functions
+ *
+ * Copyright 2004 Hans Leidekker
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+typedef struct
+{
+ unsigned int i[2];
+ unsigned int buf[4];
+ unsigned char in[64];
+ unsigned char digest[16];
+} MD5_CTX;
+
+typedef VOID (WINAPI *fnMD5Init)( MD5_CTX *ctx );
+typedef VOID (WINAPI *fnMD5Update)( MD5_CTX *ctx, const unsigned char *src, const int len );
+typedef VOID (WINAPI *fnMD5Final)( MD5_CTX *ctx );
+
+fnMD5Init pMD5Init;
+fnMD5Update pMD5Update;
+fnMD5Final pMD5Final;
+
+#define ctxcmp( a, b ) memcmp( (char*)a, (char*)b, FIELD_OFFSET( MD5_CTX, in ) )
+
+void test_md5_ctx()
+{
+ static unsigned char message[] =
+ "In our Life there's If"
+ "In our beliefs there's Lie"
+ "In our business there is Sin"
+ "In our bodies, there is Die";
+
+ int size = strlen( message );
+ HMODULE module;
+
+ MD5_CTX ctx;
+ MD5_CTX ctx_initialized =
+ {
+ { 0, 0 },
+ { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 }
+ };
+
+ MD5_CTX ctx_update1 =
+ {
+ { 0x00000338, 0 },
+ { 0x068cb64d, 0xb7a05790, 0x426979ee, 0xed67e221 }
+ };
+
+ MD5_CTX ctx_update2 =
+ {
+ { 0x00000670, 0 },
+ { 0x2f7afe58, 0xcc3e9315, 0x709c465c, 0xbf6414c8 }
+ };
+
+ unsigned char expect[16] =
+ { 0x43, 0x03, 0xdd, 0x8c, 0x60, 0xd9, 0x3a, 0x22,
+ 0x0b, 0x28, 0xd0, 0xb2, 0x65, 0x93, 0xd0, 0x36 };
+
+ if (!(module = LoadLibrary( "advapi32.dll" ))) return;
+
+ pMD5Init = (fnMD5Init)GetProcAddress( module, "MD5Init" );
+ pMD5Update = (fnMD5Update)GetProcAddress( module, "MD5Update" );
+ pMD5Final = (fnMD5Final)GetProcAddress( module, "MD5Final" );
+
+ if (!pMD5Init || !pMD5Update || !pMD5Final) goto out;
+
+ memset( &ctx, 0, sizeof(ctx) );
+ pMD5Init( &ctx );
+ ok( !ctxcmp( &ctx, &ctx_initialized ), "invalid initialization\n" );
+
+ pMD5Update( &ctx, message, size );
+ ok( !ctxcmp( &ctx, &ctx_update1 ), "update doesn't work correctly\n" );
+
+ pMD5Update( &ctx, message, size );
+ ok( !ctxcmp( &ctx, &ctx_update2 ), "update doesn't work correctly\n" );
+
+ pMD5Final( &ctx );
+ ok( ctxcmp( &ctx, &ctx_initialized ), "context has changed\n" );
+ ok( !memcmp( ctx.digest, expect, sizeof(expect) ), "incorrect result\n" );
+
+out:
+ FreeLibrary( module );
+}
+
+START_TEST(crypt_md5)
+{
+ test_md5_ctx();
+}
--- /dev/null
+/*
+ * Unit tests for SHA functions
+ *
+ * Copyright (c) 2004 Filip Navara
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+#include "wine/test.h"
+
+typedef struct {
+ ULONG Unknown[6];
+ ULONG State[5];
+ ULONG Count[2];
+ UCHAR Buffer[64];
+} SHA_CTX, *PSHA_CTX;
+
+#define ctxcmp(a,b) memcmp((char*)a, (char*)b, FIELD_OFFSET(SHA_CTX, Buffer))
+
+static void test_sha_ctx(void)
+{
+ FARPROC pA_SHAInit, pA_SHAUpdate, pA_SHAFinal;
+ static const char test_buffer[] = "In our Life there's If"
+ "In our beliefs there's Lie"
+ "In our business there is Sin"
+ "In our bodies, there is Die";
+ ULONG test_buffer_size = strlen(test_buffer);
+ HMODULE hmod;
+ SHA_CTX ctx;
+ SHA_CTX ctx_initialized = {{0, 0, 0, 0, 0}, {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0}, {0, 0}};
+ SHA_CTX ctx_update1 = {{0, 0, 0, 0, 0}, {0xdbe5eba8, 0x6b4335ca, 0xf7c94abe, 0xc9f34e31, 0x311023f0}, {0, 0x67}};
+ SHA_CTX ctx_update2 = {{0, 0, 0, 0, 0}, {0x5ecc818d, 0x52498169, 0xf6758559, 0xd035a164, 0x871dd125}, {0, 0xce}};
+ ULONG result[5];
+ ULONG result_correct[5] = {0xe014f93, 0xe09791ec, 0x6dcf96c8, 0x8e9385fc, 0x1611c1bb};
+
+ hmod = LoadLibrary("advapi32.dll");
+ pA_SHAInit = GetProcAddress(hmod, "A_SHAInit");
+ pA_SHAUpdate = GetProcAddress(hmod, "A_SHAUpdate");
+ pA_SHAFinal = GetProcAddress(hmod, "A_SHAFinal");
+
+ if (!pA_SHAInit || !pA_SHAUpdate || !pA_SHAFinal) return;
+
+ RtlZeroMemory(&ctx, sizeof(ctx));
+ pA_SHAInit(&ctx);
+ ok(!ctxcmp(&ctx, &ctx_initialized), "invalid initialization\n");
+
+ pA_SHAUpdate(&ctx, test_buffer, test_buffer_size);
+ ok(!ctxcmp(&ctx, &ctx_update1), "update doesn't work correctly\n");
+
+ pA_SHAUpdate(&ctx, test_buffer, test_buffer_size);
+ ok(!ctxcmp(&ctx, &ctx_update2), "update doesn't work correctly\n");
+
+ pA_SHAFinal(&ctx, result);
+ ok(!ctxcmp(&ctx, &ctx_initialized), "context hasn't been reinitialized\n");
+ ok(!memcmp(result, result_correct, sizeof(result)), "incorrect result\n");
+
+ FreeLibrary(hmod);
+}
+
+START_TEST(crypt_sha)
+{
+ test_sha_ctx();
+}
--- /dev/null
+/*
+ * Unit tests for registry functions
+ *
+ * Copyright (c) 2002 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winerror.h"
+
+static HKEY hkey_main;
+
+static const char * sTestpath1 = "%LONGSYSTEMVAR%\\subdir1";
+static const char * sTestpath2 = "%FOO%\\subdir1";
+
+/* delete key and all its subkeys */
+static DWORD delete_key( HKEY hkey )
+{
+ char name[MAX_PATH];
+ DWORD ret;
+
+ while (!(ret = RegEnumKeyA(hkey, 0, name, sizeof(name))))
+ {
+ HKEY tmp;
+ if (!(ret = RegOpenKeyExA( hkey, name, 0, KEY_ENUMERATE_SUB_KEYS, &tmp )))
+ {
+ ret = delete_key( tmp );
+ RegCloseKey( tmp );
+ }
+ if (ret) break;
+ }
+ if (ret != ERROR_NO_MORE_ITEMS) return ret;
+ RegDeleteKeyA( hkey, "" );
+ return 0;
+}
+
+static void setup_main_key(void)
+{
+ if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkey_main )) delete_key( hkey_main );
+
+ assert (!RegCreateKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkey_main ));
+}
+
+static void create_test_entries(void)
+{
+ SetEnvironmentVariableA("LONGSYSTEMVAR", "bar");
+ SetEnvironmentVariableA("FOO", "ImARatherLongButIndeedNeededString");
+
+ ok(!RegSetValueExA(hkey_main,"Test1",0,REG_EXPAND_SZ, sTestpath1, strlen(sTestpath1)+1),
+ "RegSetValueExA failed\n");
+ ok(!RegSetValueExA(hkey_main,"Test2",0,REG_SZ, sTestpath1, strlen(sTestpath1)+1),
+ "RegSetValueExA failed\n");
+ ok(!RegSetValueExA(hkey_main,"Test3",0,REG_EXPAND_SZ, sTestpath2, strlen(sTestpath2)+1),
+ "RegSetValueExA failed\n");
+}
+
+static void test_enum_value(void)
+{
+ DWORD res;
+ HKEY test_key;
+ char value[20], data[20];
+ WCHAR valueW[20], dataW[20];
+ DWORD val_count, data_count, type;
+ static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
+ static const WCHAR testW[] = {'T','e','s','t',0};
+ static const WCHAR xxxW[] = {'x','x','x','x','x','x','x','x',0};
+
+ /* create the working key for new 'Test' value */
+ res = RegCreateKeyA( hkey_main, "TestKey", &test_key );
+ ok( res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", res);
+
+ /* check NULL data with zero length */
+ res = RegSetValueExA( test_key, "Test", 0, REG_SZ, NULL, 0 );
+ if (GetVersion() & 0x80000000)
+ ok( res == ERROR_INVALID_PARAMETER, "RegSetValueExA returned %ld\n", res );
+ else
+ ok( !res, "RegSetValueExA returned %ld\n", res );
+ res = RegSetValueExA( test_key, "Test", 0, REG_EXPAND_SZ, NULL, 0 );
+ ok( ERROR_SUCCESS == res || ERROR_INVALID_PARAMETER == res, "RegSetValueExA returned %ld\n", res );
+ res = RegSetValueExA( test_key, "Test", 0, REG_BINARY, NULL, 0 );
+ ok( ERROR_SUCCESS == res || ERROR_INVALID_PARAMETER == res, "RegSetValueExA returned %ld\n", res );
+
+ res = RegSetValueExA( test_key, "Test", 0, REG_SZ, (BYTE *)"foobar", 7 );
+ ok( res == 0, "RegSetValueExA failed error %ld\n", res );
+
+ /* overflow both name and data */
+ val_count = 2;
+ data_count = 2;
+ type = 1234;
+ strcpy( value, "xxxxxxxxxx" );
+ strcpy( data, "xxxxxxxxxx" );
+ res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, data, &data_count );
+ ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %ld\n", res );
+ ok( val_count == 2, "val_count set to %ld\n", val_count );
+ ok( data_count == 7, "data_count set to %ld instead of 7\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !strcmp( value, "xxxxxxxxxx" ), "value set to '%s'\n", value );
+ ok( !strcmp( data, "xxxxxxxxxx" ), "data set to '%s'\n", data );
+
+ /* overflow name */
+ val_count = 3;
+ data_count = 20;
+ type = 1234;
+ strcpy( value, "xxxxxxxxxx" );
+ strcpy( data, "xxxxxxxxxx" );
+ res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, data, &data_count );
+ ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %ld\n", res );
+ /* Win9x returns 2 as specified by MSDN but NT returns 3... */
+ ok( val_count == 2 || val_count == 3, "val_count set to %ld\n", val_count );
+ ok( data_count == 7, "data_count set to %ld instead of 7\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+#if 0
+ /* v5.1.2600.0 (XP Home) does not touch value or data in this case */
+ ok( !strcmp( value, "Te" ), "value set to '%s' instead of 'Te'\n", value );
+ ok( !strcmp( data, "foobar" ), "data set to '%s' instead of 'foobar'\n", data );
+#endif
+
+ /* overflow empty name */
+ val_count = 0;
+ data_count = 20;
+ type = 1234;
+ strcpy( value, "xxxxxxxxxx" );
+ strcpy( data, "xxxxxxxxxx" );
+ res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, data, &data_count );
+ ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %ld\n", res );
+ ok( val_count == 0, "val_count set to %ld\n", val_count );
+ ok( data_count == 7, "data_count set to %ld instead of 7\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !strcmp( value, "xxxxxxxxxx" ), "value set to '%s'\n", value );
+#if 0
+ /* v5.1.2600.0 (XP Home) does not touch data in this case */
+ ok( !strcmp( data, "foobar" ), "data set to '%s' instead of 'foobar'\n", data );
+#endif
+
+ /* overflow data */
+ val_count = 20;
+ data_count = 2;
+ type = 1234;
+ strcpy( value, "xxxxxxxxxx" );
+ strcpy( data, "xxxxxxxxxx" );
+ res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, data, &data_count );
+ ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %ld\n", res );
+ ok( val_count == 20, "val_count set to %ld\n", val_count );
+ ok( data_count == 7, "data_count set to %ld instead of 7\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !strcmp( value, "xxxxxxxxxx" ), "value set to '%s'\n", value );
+ ok( !strcmp( data, "xxxxxxxxxx" ), "data set to '%s'\n", data );
+
+ /* no overflow */
+ val_count = 20;
+ data_count = 20;
+ type = 1234;
+ strcpy( value, "xxxxxxxxxx" );
+ strcpy( data, "xxxxxxxxxx" );
+ res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, data, &data_count );
+ ok( res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", res );
+ ok( val_count == 4, "val_count set to %ld instead of 4\n", val_count );
+ ok( data_count == 7, "data_count set to %ld instead of 7\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !strcmp( value, "Test" ), "value is '%s' instead of Test\n", value );
+ ok( !strcmp( data, "foobar" ), "data is '%s' instead of foobar\n", data );
+
+ /* Unicode tests */
+
+ SetLastError(0);
+ res = RegSetValueExW( test_key, testW, 0, REG_SZ, (const BYTE *)foobarW, 7*sizeof(WCHAR) );
+ if (res==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok( res == 0, "RegSetValueExW failed error %ld\n", res );
+
+ /* overflow both name and data */
+ val_count = 2;
+ data_count = 2;
+ type = 1234;
+ memcpy( valueW, xxxW, sizeof(xxxW) );
+ memcpy( dataW, xxxW, sizeof(xxxW) );
+ res = RegEnumValueW( test_key, 0, valueW, &val_count, NULL, &type, (BYTE*)dataW, &data_count );
+ ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %ld\n", res );
+ ok( val_count == 2, "val_count set to %ld\n", val_count );
+ ok( data_count == 7*sizeof(WCHAR), "data_count set to %ld instead of 7*sizeof(WCHAR)\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !memcmp( valueW, xxxW, sizeof(xxxW) ), "value modified\n" );
+ ok( !memcmp( dataW, xxxW, sizeof(xxxW) ), "data modified\n" );
+
+ /* overflow name */
+ val_count = 3;
+ data_count = 20;
+ type = 1234;
+ memcpy( valueW, xxxW, sizeof(xxxW) );
+ memcpy( dataW, xxxW, sizeof(xxxW) );
+ res = RegEnumValueW( test_key, 0, valueW, &val_count, NULL, &type, (BYTE*)dataW, &data_count );
+ ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %ld\n", res );
+ ok( val_count == 3, "val_count set to %ld\n", val_count );
+ ok( data_count == 7*sizeof(WCHAR), "data_count set to %ld instead of 7*sizeof(WCHAR)\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !memcmp( valueW, xxxW, sizeof(xxxW) ), "value modified\n" );
+ ok( !memcmp( dataW, xxxW, sizeof(xxxW) ), "data modified\n" );
+
+ /* overflow data */
+ val_count = 20;
+ data_count = 2;
+ type = 1234;
+ memcpy( valueW, xxxW, sizeof(xxxW) );
+ memcpy( dataW, xxxW, sizeof(xxxW) );
+ res = RegEnumValueW( test_key, 0, valueW, &val_count, NULL, &type, (BYTE*)dataW, &data_count );
+ ok( res == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %ld\n", res );
+ ok( val_count == 4, "val_count set to %ld instead of 4\n", val_count );
+ ok( data_count == 7*sizeof(WCHAR), "data_count set to %ld instead of 7*sizeof(WCHAR)\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !memcmp( valueW, testW, sizeof(testW) ), "value is not 'Test'\n" );
+ ok( !memcmp( dataW, xxxW, sizeof(xxxW) ), "data modified\n" );
+
+ /* no overflow */
+ val_count = 20;
+ data_count = 20;
+ type = 1234;
+ memcpy( valueW, xxxW, sizeof(xxxW) );
+ memcpy( dataW, xxxW, sizeof(xxxW) );
+ res = RegEnumValueW( test_key, 0, valueW, &val_count, NULL, &type, (BYTE*)dataW, &data_count );
+ ok( res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", res );
+ ok( val_count == 4, "val_count set to %ld instead of 4\n", val_count );
+ ok( data_count == 7*sizeof(WCHAR), "data_count set to %ld instead of 7*sizeof(WCHAR)\n", data_count );
+ ok( type == REG_SZ, "type %ld is not REG_SZ\n", type );
+ ok( !memcmp( valueW, testW, sizeof(testW) ), "value is not 'Test'\n" );
+ ok( !memcmp( dataW, foobarW, sizeof(foobarW) ), "data is not 'foobar'\n" );
+}
+
+static void test_query_value_ex()
+{
+ DWORD ret;
+ DWORD size;
+ DWORD type;
+
+ ret = RegQueryValueExA(hkey_main, "Test2", NULL, &type, NULL, &size);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+ ok(size == strlen(sTestpath1) + 1, "(%ld,%ld)\n", (DWORD)strlen(sTestpath1) + 1, size);
+ ok(type == REG_SZ, "type %ld is not REG_SZ\n", type);
+}
+
+static void test_reg_open_key()
+{
+ DWORD ret = 0;
+ HKEY hkResult = NULL;
+ HKEY hkPreserve = NULL;
+
+ /* successful open */
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkResult);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+ ok(hkResult != NULL, "expected hkResult != NULL\n");
+ hkPreserve = hkResult;
+
+ /* open same key twice */
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkResult);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+ ok(hkResult != hkPreserve, "epxected hkResult != hkPreserve\n");
+ ok(hkResult != NULL, "hkResult != NULL\n");
+ RegCloseKey(hkResult);
+
+ /* open nonexistent key
+ * check that hkResult is set to NULL
+ */
+ hkResult = hkPreserve;
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Nonexistent", &hkResult);
+ ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %ld\n", ret);
+ ok(hkResult == NULL, "expected hkResult == NULL\n");
+
+ /* open the same nonexistent key again to make sure the key wasn't created */
+ hkResult = hkPreserve;
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Nonexistent", &hkResult);
+ ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %ld\n", ret);
+ ok(hkResult == NULL, "expected hkResult == NULL\n");
+
+ /* send in NULL lpSubKey
+ * check that hkResult receives the value of hKey
+ */
+ hkResult = hkPreserve;
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, NULL, &hkResult);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+ ok(hkResult == HKEY_CURRENT_USER, "expected hkResult == HKEY_CURRENT_USER\n");
+
+ /* send empty-string in lpSubKey */
+ hkResult = hkPreserve;
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, "", &hkResult);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+ ok(hkResult == HKEY_CURRENT_USER, "expected hkResult == HKEY_CURRENT_USER\n");
+
+ /* send in NULL lpSubKey and NULL hKey
+ * hkResult is set to NULL
+ */
+ hkResult = hkPreserve;
+ ret = RegOpenKeyA(NULL, NULL, &hkResult);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+ ok(hkResult == NULL, "expected hkResult == NULL\n");
+
+ /* only send NULL hKey
+ * the value of hkResult remains unchanged
+ */
+ hkResult = hkPreserve;
+ ret = RegOpenKeyA(NULL, "Software\\Wine\\Test", &hkResult);
+ ok(ret == ERROR_INVALID_HANDLE || ret == ERROR_BADKEY, /* Windows 95 returns BADKEY */
+ "expected ERROR_INVALID_HANDLE or ERROR_BADKEY, got %ld\n", ret);
+ ok(hkResult == hkPreserve, "expected hkResult == hkPreserve\n");
+ RegCloseKey(hkResult);
+
+ /* send in NULL hkResult */
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", NULL);
+ ok(ret == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %ld\n", ret);
+}
+
+static void test_reg_close_key()
+{
+ DWORD ret = 0;
+ HKEY hkHandle;
+
+ /* successfully close key
+ * hkHandle remains changed after call to RegCloseKey
+ */
+ ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkHandle);
+ ret = RegCloseKey(hkHandle);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+
+ /* try to close the key twice */
+ ret = RegCloseKey(hkHandle); /* Windows 95 doesn't mind. */
+ ok(ret == ERROR_INVALID_HANDLE || ret == ERROR_SUCCESS,
+ "expected ERROR_INVALID_HANDLE or ERROR_SUCCESS, got %ld\n", ret);
+
+ /* try to close a NULL handle */
+ ret = RegCloseKey(NULL);
+ ok(ret == ERROR_INVALID_HANDLE || ret == ERROR_BADKEY, /* Windows 95 returns BADKEY */
+ "expected ERROR_INVALID_HANDLE or ERROR_BADKEY, got %ld\n", ret);
+}
+
+static void test_reg_delete_key()
+{
+ DWORD ret;
+ HKEY hkResult = NULL;
+
+ ret = RegDeleteKey(hkey_main, NULL);
+ ok(ret == ERROR_INVALID_PARAMETER || ret == ERROR_ACCESS_DENIED,
+ "expected ERROR_INVALID_PARAMETER or ERROR_ACCESS_DENIED, got %ld\n", ret);
+
+ ret = RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Tost", &hkResult);
+
+ ret = RegDeleteValue(hkResult, "noExists");
+ ok(ret == ERROR_FILE_NOT_FOUND, "expected ERROR_FILE_NOT_FOUND, got %ld\n", ret);
+
+ ret = RegDeleteKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Tost");
+
+ ret = RegCloseKey(hkResult);
+
+}
+
+static void test_reg_save_key()
+{
+ DWORD ret;
+
+ ret = RegSaveKey(hkey_main, "saved_key", NULL);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+}
+
+static void test_reg_load_key()
+{
+ DWORD ret;
+ HKEY hkHandle;
+
+ ret = RegLoadKey(HKEY_LOCAL_MACHINE, "Test", "saved_key");
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+
+ ret = RegOpenKey(HKEY_LOCAL_MACHINE, "Test", &hkHandle);
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+
+ RegCloseKey(hkHandle);
+}
+
+static void test_reg_unload_key()
+{
+ DWORD ret;
+
+ ret = RegUnLoadKey(HKEY_LOCAL_MACHINE, "Test");
+ ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %ld\n", ret);
+
+ DeleteFile("saved_key");
+}
+
+static BOOL set_privileges(LPCSTR privilege, BOOL set)
+{
+ TOKEN_PRIVILEGES tp;
+ HANDLE hToken;
+ LUID luid;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
+ return FALSE;
+
+ if(!LookupPrivilegeValue(NULL, privilege, &luid))
+ {
+ CloseHandle(hToken);
+ return FALSE;
+ }
+
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Luid = luid;
+
+ if (set)
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ else
+ tp.Privileges[0].Attributes = 0;
+
+ AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
+ if (GetLastError() != ERROR_SUCCESS)
+ {
+ CloseHandle(hToken);
+ return FALSE;
+ }
+
+ CloseHandle(hToken);
+ return TRUE;
+}
+
+START_TEST(registry)
+{
+ setup_main_key();
+ create_test_entries();
+ test_enum_value();
+ test_query_value_ex();
+ test_reg_open_key();
+ test_reg_close_key();
+ test_reg_delete_key();
+
+ /* SaveKey/LoadKey require the SE_BACKUP_NAME privilege to be set */
+ if (set_privileges(SE_BACKUP_NAME, TRUE) &&
+ set_privileges(SE_RESTORE_NAME, TRUE))
+ {
+ test_reg_save_key();
+ test_reg_load_key();
+ test_reg_unload_key();
+
+ set_privileges(SE_BACKUP_NAME, FALSE);
+ set_privileges(SE_RESTORE_NAME, FALSE);
+ }
+ /* cleanup */
+ delete_key( hkey_main );
+}
--- /dev/null
+/*
+ * Unit tests for security functions
+ *
+ * Copyright (c) 2004 Mike McCormack
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _WIN32_WINNT 0x0501
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "aclapi.h"
+#include "winnt.h"
+
+typedef BOOL (WINAPI *fnBuildTrusteeWithSidA)( TRUSTEE *trustee, PSID psid );
+typedef BOOL (WINAPI *fnBuildTrusteeWithNameA)( TRUSTEE *trustee, LPSTR str );
+typedef BOOL (WINAPI *fnConvertSidToStringSidA)( PSID pSid, LPSTR *str );
+typedef BOOL (WINAPI *fnConvertStringSidToSidA)( LPCSTR str, PSID pSid );
+typedef BOOL (WINAPI *fnGetFileSecurityA)(LPCSTR, SECURITY_INFORMATION,
+ PSECURITY_DESCRIPTOR, DWORD, LPDWORD);
+
+static HMODULE hmod;
+
+fnBuildTrusteeWithSidA pBuildTrusteeWithSidA;
+fnBuildTrusteeWithNameA pBuildTrusteeWithNameA;
+fnConvertSidToStringSidA pConvertSidToStringSidA;
+fnConvertStringSidToSidA pConvertStringSidToSidA;
+fnGetFileSecurityA pGetFileSecurityA;
+
+struct sidRef
+{
+ SID_IDENTIFIER_AUTHORITY auth;
+ const char *refStr;
+};
+
+static void init(void)
+{
+ hmod = GetModuleHandle("advapi32.dll");
+}
+
+void test_sid()
+{
+ struct sidRef refs[] = {
+ { { {0x00,0x00,0x33,0x44,0x55,0x66} }, "S-1-860116326-1" },
+ { { {0x00,0x00,0x01,0x02,0x03,0x04} }, "S-1-16909060-1" },
+ { { {0x00,0x00,0x00,0x01,0x02,0x03} }, "S-1-66051-1" },
+ { { {0x00,0x00,0x00,0x00,0x01,0x02} }, "S-1-258-1" },
+ { { {0x00,0x00,0x00,0x00,0x00,0x02} }, "S-1-2-1" },
+ { { {0x00,0x00,0x00,0x00,0x00,0x0c} }, "S-1-12-1" },
+ };
+ const char noSubAuthStr[] = "S-1-5";
+ unsigned int i;
+ PSID psid = NULL;
+ BOOL r;
+ LPSTR str = NULL;
+
+ pConvertSidToStringSidA = (fnConvertSidToStringSidA)
+ GetProcAddress( hmod, "ConvertSidToStringSidA" );
+ if( !pConvertSidToStringSidA )
+ return;
+ pConvertStringSidToSidA = (fnConvertStringSidToSidA)
+ GetProcAddress( hmod, "ConvertStringSidToSidA" );
+ if( !pConvertStringSidToSidA )
+ return;
+
+ r = pConvertStringSidToSidA( NULL, NULL );
+ ok( !r, "expected failure with NULL parameters\n" );
+ if( GetLastError() == ERROR_CALL_NOT_IMPLEMENTED )
+ return;
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "expected GetLastError() is ERROR_INVALID_PARAMETER, got %ld\n",
+ GetLastError() );
+
+ r = pConvertStringSidToSidA( refs[0].refStr, NULL );
+ ok( !r && GetLastError() == ERROR_INVALID_PARAMETER,
+ "expected GetLastError() is ERROR_INVALID_PARAMETER, got %ld\n",
+ GetLastError() );
+
+ r = pConvertStringSidToSidA( NULL, &str );
+ ok( !r && GetLastError() == ERROR_INVALID_PARAMETER,
+ "expected GetLastError() is ERROR_INVALID_PARAMETER, got %ld\n",
+ GetLastError() );
+
+ r = pConvertStringSidToSidA( noSubAuthStr, &psid );
+ ok( !r,
+ "expected failure with no sub authorities\n" );
+ ok( GetLastError() == ERROR_INVALID_SID,
+ "expected GetLastError() is ERROR_INVALID_SID, got %ld\n",
+ GetLastError() );
+
+ for( i = 0; i < sizeof(refs) / sizeof(refs[0]); i++ )
+ {
+ PISID pisid;
+
+ r = AllocateAndInitializeSid( &refs[i].auth, 1,1,0,0,0,0,0,0,0,
+ &psid );
+ ok( r, "failed to allocate sid\n" );
+ r = pConvertSidToStringSidA( psid, &str );
+ ok( r, "failed to convert sid\n" );
+ if (r)
+ {
+ ok( !strcmp( str, refs[i].refStr ),
+ "incorrect sid, expected %s, got %s\n", refs[i].refStr, str );
+ LocalFree( str );
+ }
+ if( psid )
+ FreeSid( psid );
+
+ r = pConvertStringSidToSidA( refs[i].refStr, &psid );
+ ok( r, "failed to parse sid string\n" );
+ pisid = (PISID)psid;
+ ok( pisid &&
+ !memcmp( pisid->IdentifierAuthority.Value, refs[i].auth.Value,
+ sizeof(refs[i].auth) ),
+ "string sid %s didn't parse to expected value\n"
+ "(got 0x%04x%08lx, expected 0x%04x%08lx)\n",
+ refs[i].refStr,
+ MAKEWORD( pisid->IdentifierAuthority.Value[1],
+ pisid->IdentifierAuthority.Value[0] ),
+ MAKELONG( MAKEWORD( pisid->IdentifierAuthority.Value[5],
+ pisid->IdentifierAuthority.Value[4] ),
+ MAKEWORD( pisid->IdentifierAuthority.Value[3],
+ pisid->IdentifierAuthority.Value[2] ) ),
+ MAKEWORD( refs[i].auth.Value[1], refs[i].auth.Value[0] ),
+ MAKELONG( MAKEWORD( refs[i].auth.Value[5], refs[i].auth.Value[4] ),
+ MAKEWORD( refs[i].auth.Value[3], refs[i].auth.Value[2] ) ) );
+ if( psid )
+ LocalFree( psid );
+ }
+}
+
+void test_trustee()
+{
+ TRUSTEE trustee;
+ PSID psid;
+ LPSTR str = "2jjj";
+
+ SID_IDENTIFIER_AUTHORITY auth = { {0x11,0x22,0,0,0, 0} };
+
+ pBuildTrusteeWithSidA = (fnBuildTrusteeWithSidA)
+ GetProcAddress( hmod, "BuildTrusteeWithSidA" );
+ pBuildTrusteeWithNameA = (fnBuildTrusteeWithNameA)
+ GetProcAddress( hmod, "BuildTrusteeWithNameA" );
+ if( !pBuildTrusteeWithSidA || !pBuildTrusteeWithNameA)
+ return;
+
+ if ( ! AllocateAndInitializeSid( &auth, 1, 42, 0,0,0,0,0,0,0,&psid ) )
+ {
+ trace( "failed to init SID\n" );
+ return;
+ }
+
+ memset( &trustee, 0xff, sizeof trustee );
+ pBuildTrusteeWithSidA( &trustee, psid );
+
+ ok( trustee.pMultipleTrustee == NULL, "pMultipleTrustee wrong\n");
+ ok( trustee.MultipleTrusteeOperation == NO_MULTIPLE_TRUSTEE,
+ "MultipleTrusteeOperation wrong\n");
+ ok( trustee.TrusteeForm == TRUSTEE_IS_SID, "TrusteeForm wrong\n");
+ ok( trustee.TrusteeType == TRUSTEE_IS_UNKNOWN, "TrusteeType wrong\n");
+ ok( trustee.ptstrName == (LPSTR) psid, "ptstrName wrong\n" );
+ FreeSid( psid );
+
+ /* test BuildTrusteeWithNameA */
+ memset( &trustee, 0xff, sizeof trustee );
+ pBuildTrusteeWithNameA( &trustee, str );
+
+ ok( trustee.pMultipleTrustee == NULL, "pMultipleTrustee wrong\n");
+ ok( trustee.MultipleTrusteeOperation == NO_MULTIPLE_TRUSTEE,
+ "MultipleTrusteeOperation wrong\n");
+ ok( trustee.TrusteeForm == TRUSTEE_IS_NAME, "TrusteeForm wrong\n");
+ ok( trustee.TrusteeType == TRUSTEE_IS_UNKNOWN, "TrusteeType wrong\n");
+ ok( trustee.ptstrName == str, "ptstrName wrong\n" );
+}
+
+/* If the first isn't defined, assume none is */
+#ifndef SE_MIN_WELL_KNOWN_PRIVILEGE
+#define SE_MIN_WELL_KNOWN_PRIVILEGE 2L
+#define SE_CREATE_TOKEN_PRIVILEGE 2L
+#define SE_ASSIGNPRIMARYTOKEN_PRIVILEGE 3L
+#define SE_LOCK_MEMORY_PRIVILEGE 4L
+#define SE_INCREASE_QUOTA_PRIVILEGE 5L
+#define SE_MACHINE_ACCOUNT_PRIVILEGE 6L
+#define SE_TCB_PRIVILEGE 7L
+#define SE_SECURITY_PRIVILEGE 8L
+#define SE_TAKE_OWNERSHIP_PRIVILEGE 9L
+#define SE_LOAD_DRIVER_PRIVILEGE 10L
+#define SE_SYSTEM_PROFILE_PRIVILEGE 11L
+#define SE_SYSTEMTIME_PRIVILEGE 12L
+#define SE_PROF_SINGLE_PROCESS_PRIVILEGE 13L
+#define SE_INC_BASE_PRIORITY_PRIVILEGE 14L
+#define SE_CREATE_PAGEFILE_PRIVILEGE 15L
+#define SE_CREATE_PERMANENT_PRIVILEGE 16L
+#define SE_BACKUP_PRIVILEGE 17L
+#define SE_RESTORE_PRIVILEGE 18L
+#define SE_SHUTDOWN_PRIVILEGE 19L
+#define SE_DEBUG_PRIVILEGE 20L
+#define SE_AUDIT_PRIVILEGE 21L
+#define SE_SYSTEM_ENVIRONMENT_PRIVILEGE 22L
+#define SE_CHANGE_NOTIFY_PRIVILLEGE 23L
+#define SE_REMOTE_SHUTDOWN_PRIVILEGE 24L
+#define SE_UNDOCK_PRIVILEGE 25L
+#define SE_SYNC_AGENT_PRIVILEGE 26L
+#define SE_ENABLE_DELEGATION_PRIVILEGE 27L
+#define SE_MANAGE_VOLUME_PRIVILEGE 28L
+#define SE_IMPERSONATE_PRIVILEGE 29L
+#define SE_CREATE_GLOBAL_PRIVILEGE 30L
+#define SE_MAX_WELL_KNOWN_PRIVILEGE SE_CREATE_GLOBAL_PRIVILEGE
+#endif /* ndef SE_MIN_WELL_KNOWN_PRIVILEGE */
+
+static void test_allocateLuid(void)
+{
+ BOOL (WINAPI *pAllocateLocallyUniqueId)(PLUID);
+ LUID luid1, luid2;
+ BOOL ret;
+
+ pAllocateLocallyUniqueId = (void*)GetProcAddress(hmod, "AllocateLocallyUniqueId");
+ if (!pAllocateLocallyUniqueId) return;
+
+ ret = pAllocateLocallyUniqueId(&luid1);
+ if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+
+ ok(ret,
+ "AllocateLocallyUniqueId failed: %ld\n", GetLastError());
+ ret = pAllocateLocallyUniqueId(&luid2);
+ ok( ret,
+ "AllocateLocallyUniqueId failed: %ld\n", GetLastError());
+ ok(luid1.LowPart > SE_MAX_WELL_KNOWN_PRIVILEGE || luid1.HighPart != 0,
+ "AllocateLocallyUniqueId returned a well-known LUID\n");
+ ok(luid1.LowPart != luid2.LowPart || luid1.HighPart != luid2.HighPart,
+ "AllocateLocallyUniqueId returned non-unique LUIDs\n");
+ ret = pAllocateLocallyUniqueId(NULL);
+ ok( !ret && GetLastError() == ERROR_NOACCESS,
+ "AllocateLocallyUniqueId(NULL) didn't return ERROR_NOACCESS: %ld\n",
+ GetLastError());
+}
+
+static void test_lookupPrivilegeName(void)
+{
+ BOOL (WINAPI *pLookupPrivilegeNameA)(LPSTR, PLUID, LPSTR, LPDWORD);
+ char buf[MAX_PATH]; /* arbitrary, seems long enough */
+ DWORD cchName = sizeof(buf);
+ LUID luid = { 0, 0 };
+ LONG i;
+ BOOL ret;
+
+ /* check whether it's available first */
+ pLookupPrivilegeNameA = (void*)GetProcAddress(hmod, "LookupPrivilegeNameA");
+ if (!pLookupPrivilegeNameA) return;
+ luid.LowPart = SE_CREATE_TOKEN_PRIVILEGE;
+ ret = pLookupPrivilegeNameA(NULL, &luid, buf, &cchName);
+ if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+
+ /* check with a short buffer */
+ cchName = 0;
+ luid.LowPart = SE_CREATE_TOKEN_PRIVILEGE;
+ ret = pLookupPrivilegeNameA(NULL, &luid, NULL, &cchName);
+ ok( !ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "LookupPrivilegeNameA didn't fail with ERROR_INSUFFICIENT_BUFFER: %ld\n",
+ GetLastError());
+ ok(cchName == strlen("SeCreateTokenPrivilege") + 1,
+ "LookupPrivilegeNameA returned an incorrect required length for\n"
+ "SeCreateTokenPrivilege (got %ld, expected %d)\n", cchName,
+ strlen("SeCreateTokenPrivilege") + 1);
+ /* check a known value and its returned length on success */
+ cchName = sizeof(buf);
+ ok(pLookupPrivilegeNameA(NULL, &luid, buf, &cchName) &&
+ cchName == strlen("SeCreateTokenPrivilege"),
+ "LookupPrivilegeNameA returned an incorrect output length for\n"
+ "SeCreateTokenPrivilege (got %ld, expected %d)\n", cchName,
+ (int)strlen("SeCreateTokenPrivilege"));
+ /* check known values */
+ for (i = SE_MIN_WELL_KNOWN_PRIVILEGE; i < SE_MAX_WELL_KNOWN_PRIVILEGE; i++)
+ {
+ luid.LowPart = i;
+ cchName = sizeof(buf);
+ ret = pLookupPrivilegeNameA(NULL, &luid, buf, &cchName);
+ ok( ret || GetLastError() == ERROR_NO_SUCH_PRIVILEGE,
+ "LookupPrivilegeNameA(0.%ld) failed: %ld\n", i, GetLastError());
+ }
+ /* check a bogus LUID */
+ luid.LowPart = 0xdeadbeef;
+ cchName = sizeof(buf);
+ ret = pLookupPrivilegeNameA(NULL, &luid, buf, &cchName);
+ ok( !ret && GetLastError() == ERROR_NO_SUCH_PRIVILEGE,
+ "LookupPrivilegeNameA didn't fail with ERROR_NO_SUCH_PRIVILEGE: %ld\n",
+ GetLastError());
+ /* check on a bogus system */
+ luid.LowPart = SE_CREATE_TOKEN_PRIVILEGE;
+ cchName = sizeof(buf);
+ ret = pLookupPrivilegeNameA("b0gu5.Nam3", &luid, buf, &cchName);
+ ok( !ret && GetLastError() == RPC_S_SERVER_UNAVAILABLE,
+ "LookupPrivilegeNameA didn't fail with RPC_S_SERVER_UNAVAILABLE: %ld\n",
+ GetLastError());
+}
+
+struct NameToLUID
+{
+ const char *name;
+ DWORD lowPart;
+};
+
+static void test_lookupPrivilegeValue(void)
+{
+ static const struct NameToLUID privs[] = {
+ { "SeCreateTokenPrivilege", SE_CREATE_TOKEN_PRIVILEGE },
+ { "SeAssignPrimaryTokenPrivilege", SE_ASSIGNPRIMARYTOKEN_PRIVILEGE },
+ { "SeLockMemoryPrivilege", SE_LOCK_MEMORY_PRIVILEGE },
+ { "SeIncreaseQuotaPrivilege", SE_INCREASE_QUOTA_PRIVILEGE },
+ { "SeMachineAccountPrivilege", SE_MACHINE_ACCOUNT_PRIVILEGE },
+ { "SeTcbPrivilege", SE_TCB_PRIVILEGE },
+ { "SeSecurityPrivilege", SE_SECURITY_PRIVILEGE },
+ { "SeTakeOwnershipPrivilege", SE_TAKE_OWNERSHIP_PRIVILEGE },
+ { "SeLoadDriverPrivilege", SE_LOAD_DRIVER_PRIVILEGE },
+ { "SeSystemProfilePrivilege", SE_SYSTEM_PROFILE_PRIVILEGE },
+ { "SeSystemtimePrivilege", SE_SYSTEMTIME_PRIVILEGE },
+ { "SeProfileSingleProcessPrivilege", SE_PROF_SINGLE_PROCESS_PRIVILEGE },
+ { "SeIncreaseBasePriorityPrivilege", SE_INC_BASE_PRIORITY_PRIVILEGE },
+ { "SeCreatePagefilePrivilege", SE_CREATE_PAGEFILE_PRIVILEGE },
+ { "SeCreatePermanentPrivilege", SE_CREATE_PERMANENT_PRIVILEGE },
+ { "SeBackupPrivilege", SE_BACKUP_PRIVILEGE },
+ { "SeRestorePrivilege", SE_RESTORE_PRIVILEGE },
+ { "SeShutdownPrivilege", SE_SHUTDOWN_PRIVILEGE },
+ { "SeDebugPrivilege", SE_DEBUG_PRIVILEGE },
+ { "SeAuditPrivilege", SE_AUDIT_PRIVILEGE },
+ { "SeSystemEnvironmentPrivilege", SE_SYSTEM_ENVIRONMENT_PRIVILEGE },
+ { "SeChangeNotifyPrivilege", SE_CHANGE_NOTIFY_PRIVILLEGE },
+ { "SeRemoteShutdownPrivilege", SE_REMOTE_SHUTDOWN_PRIVILEGE },
+ { "SeUndockPrivilege", SE_UNDOCK_PRIVILEGE },
+ { "SeSyncAgentPrivilege", SE_SYNC_AGENT_PRIVILEGE },
+ { "SeEnableDelegationPrivilege", SE_ENABLE_DELEGATION_PRIVILEGE },
+ { "SeManageVolumePrivilege", SE_MANAGE_VOLUME_PRIVILEGE },
+ { "SeImpersonatePrivilege", SE_IMPERSONATE_PRIVILEGE },
+ { "SeCreateGlobalPrivilege", SE_CREATE_GLOBAL_PRIVILEGE },
+ };
+ BOOL (WINAPI *pLookupPrivilegeValueA)(LPCSTR, LPCSTR, PLUID);
+ int i;
+ LUID luid;
+ BOOL ret;
+
+ /* check whether it's available first */
+ pLookupPrivilegeValueA = (void*)GetProcAddress(hmod, "LookupPrivilegeValueA");
+ if (!pLookupPrivilegeValueA) return;
+ ret = pLookupPrivilegeValueA(NULL, "SeCreateTokenPrivilege", &luid);
+ if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+
+ /* check a bogus system name */
+ ret = pLookupPrivilegeValueA("b0gu5.Nam3", "SeCreateTokenPrivilege", &luid);
+ ok( !ret && GetLastError() == RPC_S_SERVER_UNAVAILABLE,
+ "LookupPrivilegeValueA didn't fail with RPC_S_SERVER_UNAVAILABLE: %ld\n",
+ GetLastError());
+ /* check a NULL string */
+ ret = pLookupPrivilegeValueA(NULL, 0, &luid);
+ ok( !ret && GetLastError() == ERROR_NO_SUCH_PRIVILEGE,
+ "LookupPrivilegeValueA didn't fail with ERROR_NO_SUCH_PRIVILEGE: %ld\n",
+ GetLastError());
+ /* check a bogus privilege name */
+ ret = pLookupPrivilegeValueA(NULL, "SeBogusPrivilege", &luid);
+ ok( !ret && GetLastError() == ERROR_NO_SUCH_PRIVILEGE,
+ "LookupPrivilegeValueA didn't fail with ERROR_NO_SUCH_PRIVILEGE: %ld\n",
+ GetLastError());
+ /* check case insensitive */
+ ret = pLookupPrivilegeValueA(NULL, "sEcREATEtOKENpRIVILEGE", &luid);
+ ok( ret,
+ "LookupPrivilegeValueA(NULL, sEcREATEtOKENpRIVILEGE, &luid) failed: %ld\n",
+ GetLastError());
+ for (i = 0; i < sizeof(privs) / sizeof(privs[0]); i++)
+ {
+ /* Not all privileges are implemented on all Windows versions, so
+ * don't worry if the call fails
+ */
+ if (pLookupPrivilegeValueA(NULL, privs[i].name, &luid))
+ {
+ ok(luid.LowPart == privs[i].lowPart,
+ "LookupPrivilegeValueA returned an invalid LUID for %s\n",
+ privs[i].name);
+ }
+ }
+}
+
+static void test_luid(void)
+{
+ test_allocateLuid();
+ test_lookupPrivilegeName();
+ test_lookupPrivilegeValue();
+}
+
+static void test_FileSecurity(void)
+{
+ char directory[MAX_PATH];
+ DWORD retval, outSize;
+ BOOL result;
+ BYTE buffer[0x40];
+
+ pGetFileSecurityA = (fnGetFileSecurityA)
+ GetProcAddress( hmod, "GetFileSecurityA" );
+ if( !pGetFileSecurityA )
+ return;
+
+ retval = GetTempPathA(sizeof(directory), directory);
+ if (!retval) {
+ trace("GetTempPathA failed\n");
+ return;
+ }
+
+ strcpy(directory, "\\Should not exist");
+
+ SetLastError(NO_ERROR);
+ result = GetFileSecurityA( directory,OWNER_SECURITY_INFORMATION,
+ (PSECURITY_DESCRIPTOR)buffer,0x40,&outSize);
+ ok(!result, "GetFileSecurityA should fail for not existing directories/files\n");
+ ok( (GetLastError() == ERROR_FILE_NOT_FOUND ) ||
+ (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) ,
+ "last error ERROR_FILE_NOT_FOUND / ERROR_CALL_NOT_IMPLEMENTED (98) "
+ "expected, got %ld\n", GetLastError());
+}
+
+START_TEST(security)
+{
+ init();
+ if (!hmod) return;
+ test_sid();
+ test_trustee();
+ test_luid();
+ test_FileSecurity();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_crypt(void);
+extern void func_crypt_lmhash(void);
+extern void func_crypt_md4(void);
+extern void func_crypt_md5(void);
+extern void func_crypt_sha(void);
+extern void func_lsa(void);
+extern void func_registry(void);
+extern void func_security(void);
+
+const struct test winetest_testlist[] =
+{
+/* { "crypt", func_crypt },
+ { "crypt_lmhash", func_crypt_lmhash },
+ { "crypt_md4", func_crypt_md4 },
+ { "crypt_md5", func_crypt_md5 },
+ { "crypt_sha", func_crypt_sha },
+ { "lsa", func_lsa },
+*/ { "registry", func_registry },
+ { "security", func_security },
+ { 0, 0 }
+};
--- /dev/null
+<module name="cabinet_winetest" type="win32cui" installbase="bin" installname="cabinet_winetest.exe" allowwarnings="true">
+ <include base="cabinet_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>cabinet</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>extract.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * Unit tests for cabinet.dll extract functions
+ *
+ * Copyright (C) 2006 James Hawkins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <fci.h>
+#include "wine/test.h"
+
+/* make the max size large so there is only one cab file */
+#define MEDIA_SIZE 999999999
+#define FOLDER_THRESHOLD 900000
+
+/* The following defintions were copied from dlls/cabinet/cabinet.h
+ * because they are undocumented in windows.
+ */
+
+/* EXTRACTdest flags */
+#define EXTRACT_FILLFILELIST 0x00000001
+#define EXTRACT_EXTRACTFILES 0x00000002
+
+struct ExtractFileList {
+ LPSTR filename;
+ struct ExtractFileList *next;
+ BOOL unknown; /* always 1L */
+};
+
+/* the first parameter of the function extract */
+typedef struct {
+ long result1; /* 0x000 */
+ long unknown1[3]; /* 0x004 */
+ struct ExtractFileList *filelist; /* 0x010 */
+ long filecount; /* 0x014 */
+ long flags; /* 0x018 */
+ char directory[0x104]; /* 0x01c */
+ char lastfile[0x20c]; /* 0x120 */
+} EXTRACTDEST;
+
+/* function pointers */
+HMODULE hCabinet;
+static HRESULT (WINAPI *pExtract)(EXTRACTDEST*, LPCSTR);
+
+CHAR CURR_DIR[MAX_PATH];
+
+static void init_function_pointers(void)
+{
+ hCabinet = LoadLibraryA("cabinet.dll");
+
+ if (hCabinet)
+ {
+ pExtract = (void *)GetProcAddress(hCabinet, "Extract");
+ }
+}
+
+/* creates a file with the specified name for tests */
+static void createTestFile(const CHAR *name)
+{
+ HANDLE file;
+ DWORD written;
+
+ file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
+ WriteFile(file, name, strlen(name), &written, NULL);
+ WriteFile(file, "\n", strlen("\n"), &written, NULL);
+ CloseHandle(file);
+}
+
+static void create_test_files(void)
+{
+ int len;
+
+ GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+ len = lstrlenA(CURR_DIR);
+
+ if(len && (CURR_DIR[len-1] == '\\'))
+ CURR_DIR[len-1] = 0;
+
+ createTestFile("a.txt");
+ createTestFile("b.txt");
+ CreateDirectoryA("testdir", NULL);
+ createTestFile("testdir\\c.txt");
+ createTestFile("testdir\\d.txt");
+ CreateDirectoryA("dest", NULL);
+}
+
+static void delete_test_files(void)
+{
+ DeleteFileA("a.txt");
+ DeleteFileA("b.txt");
+ DeleteFileA("testdir\\c.txt");
+ DeleteFileA("testdir\\d.txt");
+ RemoveDirectoryA("testdir");
+
+ DeleteFileA("extract.cab");
+}
+
+/* the FCI callbacks */
+
+static void *mem_alloc(ULONG cb)
+{
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+static void mem_free(void *memory)
+{
+ HeapFree(GetProcessHeap(), 0, memory);
+}
+
+static BOOL get_next_cabinet(PCCAB pccab, ULONG cbPrevCab, void *pv)
+{
+ return TRUE;
+}
+
+static long progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
+{
+ return 0;
+}
+
+static int file_placed(PCCAB pccab, char *pszFile, long cbFile,
+ BOOL fContinuation, void *pv)
+{
+ return 0;
+}
+
+static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
+{
+ HANDLE handle;
+ DWORD dwAccess = 0;
+ DWORD dwShareMode = 0;
+ DWORD dwCreateDisposition = OPEN_EXISTING;
+
+ dwAccess = GENERIC_READ | GENERIC_WRITE;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+
+ if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
+ dwCreateDisposition = OPEN_EXISTING;
+ else
+ dwCreateDisposition = CREATE_NEW;
+
+ handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
+ dwCreateDisposition, 0, NULL);
+
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);
+
+ return (INT_PTR)handle;
+}
+
+static UINT fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD dwRead;
+ BOOL res;
+
+ res = ReadFile(handle, memory, cb, &dwRead, NULL);
+ ok(res, "Failed to ReadFile\n");
+
+ return dwRead;
+}
+
+static UINT fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD dwWritten;
+ BOOL res;
+
+ res = WriteFile(handle, memory, cb, &dwWritten, NULL);
+ ok(res, "Failed to WriteFile\n");
+
+ return dwWritten;
+}
+
+static int fci_close(INT_PTR hf, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ ok(CloseHandle(handle), "Failed to CloseHandle\n");
+
+ return 0;
+}
+
+static long fci_seek(INT_PTR hf, long dist, int seektype, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD ret;
+
+ ret = SetFilePointer(handle, dist, NULL, seektype);
+ ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
+
+ return ret;
+}
+
+static int fci_delete(char *pszFile, int *err, void *pv)
+{
+ BOOL ret = DeleteFileA(pszFile);
+ ok(ret, "Failed to DeleteFile %s\n", pszFile);
+
+ return 0;
+}
+
+static BOOL get_temp_file(char *pszTempName, int cbTempName, void *pv)
+{
+ LPSTR tempname;
+
+ tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
+ GetTempFileNameA(".", "xx", 0, tempname);
+
+ if (tempname && (strlen(tempname) < (unsigned)cbTempName))
+ {
+ lstrcpyA(pszTempName, tempname);
+ HeapFree(GetProcessHeap(), 0, tempname);
+ return TRUE;
+ }
+
+ HeapFree(GetProcessHeap(), 0, tempname);
+
+ return FALSE;
+}
+
+static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
+ USHORT *pattribs, int *err, void *pv)
+{
+ BY_HANDLE_FILE_INFORMATION finfo;
+ FILETIME filetime;
+ HANDLE handle;
+ DWORD attrs;
+ BOOL res;
+
+ handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);
+
+ res = GetFileInformationByHandle(handle, &finfo);
+ ok(res, "Expected GetFileInformationByHandle to succeed\n");
+
+ FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
+ FileTimeToDosDateTime(&filetime, pdate, ptime);
+
+ attrs = GetFileAttributes(pszName);
+ ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");
+
+ return (INT_PTR)handle;
+}
+
+static void add_file(HFCI hfci, char *file)
+{
+ char path[MAX_PATH];
+ BOOL res;
+
+ lstrcpyA(path, CURR_DIR);
+ lstrcatA(path, "\\");
+ lstrcatA(path, file);
+
+ res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
+ get_open_info, tcompTYPE_MSZIP);
+ ok(res, "Expected FCIAddFile to succeed\n");
+}
+
+static void set_cab_parameters(PCCAB pCabParams)
+{
+ ZeroMemory(pCabParams, sizeof(CCAB));
+
+ pCabParams->cb = MEDIA_SIZE;
+ pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
+ pCabParams->setID = 0xbeef;
+ lstrcpyA(pCabParams->szCabPath, CURR_DIR);
+ lstrcatA(pCabParams->szCabPath, "\\");
+ lstrcpyA(pCabParams->szCab, "extract.cab");
+}
+
+static void create_cab_file(void)
+{
+ CCAB cabParams;
+ HFCI hfci;
+ ERF erf;
+ BOOL res;
+
+ set_cab_parameters(&cabParams);
+
+ hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
+ fci_read, fci_write, fci_close, fci_seek, fci_delete,
+ get_temp_file, &cabParams, NULL);
+
+ ok(hfci != NULL, "Failed to create an FCI context\n");
+
+ add_file(hfci, "a.txt");
+ add_file(hfci, "b.txt");
+ add_file(hfci, "testdir\\c.txt");
+ add_file(hfci, "testdir\\d.txt");
+
+ res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
+ ok(res, "Failed to flush the cabinet\n");
+
+ res = FCIDestroy(hfci);
+ ok(res, "Failed to destroy the cabinet\n");
+}
+
+static void test_Extract(void)
+{
+ EXTRACTDEST extractDest;
+ HRESULT res;
+
+ /* native windows crashes if
+ * - invalid parameters are sent in
+ * - you call EXTRACT_EXTRACTFILES without calling
+ * EXTRACT_FILLFILELIST first or at the same time
+ */
+
+ /* try to extract all files */
+ ZeroMemory(&extractDest, sizeof(EXTRACTDEST));
+ lstrcpyA(extractDest.directory, "dest");
+ extractDest.flags = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
+ res = pExtract(&extractDest, "extract.cab");
+ ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+ ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
+ ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
+ ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
+ ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
+ ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
+
+ /* try fill file list operation */
+ ZeroMemory(&extractDest, sizeof(EXTRACTDEST));
+ lstrcpyA(extractDest.directory, "dest");
+ extractDest.flags = EXTRACT_FILLFILELIST;
+ res = pExtract(&extractDest, "extract.cab");
+ ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+ ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
+ ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
+ ok(extractDest.filecount == 4, "Expected 4 files, got %ld\n", extractDest.filecount);
+ ok(!lstrcmpA(extractDest.lastfile, "dest\\testdir\\d.txt"),
+ "Expected last file to be dest\\testdir\\d.txt, got %s\n", extractDest.lastfile);
+
+ /* try extract files operation once file list is filled */
+ extractDest.flags = EXTRACT_EXTRACTFILES;
+ res = pExtract(&extractDest, "extract.cab");
+ ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+ ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
+ ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
+ ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
+ ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
+ ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
+ ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
+
+ /* Extract does not extract files if the dest dir does not exist */
+ res = pExtract(&extractDest, "extract.cab");
+ ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+ ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
+ ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
+
+ /* remove two of the files in the list */
+ extractDest.filelist->next = extractDest.filelist->next->next;
+ extractDest.filelist->next->next = NULL;
+ CreateDirectoryA("dest", NULL);
+ res = pExtract(&extractDest, "extract.cab");
+ ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+ ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
+ ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
+ ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
+ ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
+ ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
+ ok(RemoveDirectoryA("dest"), "Expected dest\\testdir to exist\n");
+}
+
+START_TEST(extract)
+{
+ init_function_pointers();
+ create_test_files();
+ create_cab_file();
+
+ test_Extract();
+
+ delete_test_files();
+}
--- /dev/null
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_extract(void);
+
+const struct test winetest_testlist[] =
+{
+ { "extract", func_extract },
+ { 0, 0 }
+};
--- /dev/null
+/* Unit test suite for comboex control.
+ *
+ * Copyright 2005 Jason Edmeades
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <commctrl.h>
+
+#include "wine/test.h"
+
+static HWND hComboExParentWnd;
+static HINSTANCE hMainHinst;
+static const char ComboExTestClass[] = "ComboExTestClass";
+
+#define MAX_CHARS 100
+static char *textBuffer = NULL;
+
+static HWND createComboEx(DWORD style) {
+ return CreateWindowExA(0, WC_COMBOBOXEXA, NULL, style, 0, 0, 300, 300,
+ hComboExParentWnd, NULL, hMainHinst, NULL);
+}
+
+static LONG addItem(HWND cbex, int idx, LPTSTR text) {
+ COMBOBOXEXITEM cbexItem;
+ memset(&cbexItem, 0x00, sizeof(cbexItem));
+ cbexItem.mask = CBEIF_TEXT;
+ cbexItem.iItem = idx;
+ cbexItem.pszText = text;
+ cbexItem.cchTextMax = 0;
+ return (LONG)SendMessage(cbex, CBEM_INSERTITEM, 0,(LPARAM)&cbexItem);
+}
+
+static LONG setItem(HWND cbex, int idx, LPTSTR text) {
+ COMBOBOXEXITEM cbexItem;
+ memset(&cbexItem, 0x00, sizeof(cbexItem));
+ cbexItem.mask = CBEIF_TEXT;
+ cbexItem.iItem = idx;
+ cbexItem.pszText = text;
+ cbexItem.cchTextMax = 0;
+ return (LONG)SendMessage(cbex, CBEM_SETITEM, 0,(LPARAM)&cbexItem);
+}
+
+static LONG delItem(HWND cbex, int idx) {
+ return (LONG)SendMessage(cbex, CBEM_DELETEITEM, (LPARAM)idx, 0);
+}
+
+static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEM *cbItem) {
+ memset(cbItem, 0x00, sizeof(COMBOBOXEXITEM));
+ cbItem->mask = CBEIF_TEXT;
+ cbItem->pszText = textBuffer;
+ cbItem->iItem = idx;
+ cbItem->cchTextMax = 100;
+ return (LONG)SendMessage(cbex, CBEM_GETITEM, 0, (LPARAM)cbItem);
+}
+
+static void test_comboboxex(void) {
+ HWND myHwnd = 0;
+ LONG res = -1;
+ COMBOBOXEXITEM cbexItem;
+ static TCHAR first_item[] = {'F','i','r','s','t',' ','I','t','e','m',0},
+ second_item[] = {'S','e','c','o','n','d',' ','I','t','e','m',0},
+ third_item[] = {'T','h','i','r','d',' ','I','t','e','m',0},
+ middle_item[] = {'B','e','t','w','e','e','n',' ','F','i','r','s','t',' ','a','n','d',' ',
+ 'S','e','c','o','n','d',' ','I','t','e','m','s',0},
+ replacement_item[] = {'B','e','t','w','e','e','n',' ','F','i','r','s','t',' ','a','n','d',' ',
+ 'S','e','c','o','n','d',' ','I','t','e','m','s',0},
+ out_of_range_item[] = {'O','u','t',' ','o','f',' ','R','a','n','g','e',' ','I','t','e','m',0};
+
+ /* Allocate space for result */
+ textBuffer = malloc(MAX_CHARS);
+
+ /* Basic comboboxex test */
+ myHwnd = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+ /* Add items onto the end of the combobox */
+ res = addItem(myHwnd, -1, first_item);
+ ok(res == 0, "Adding simple item failed (%ld)\n", res);
+ res = addItem(myHwnd, -1, second_item);
+ ok(res == 1, "Adding simple item failed (%ld)\n", res);
+ res = addItem(myHwnd, 2, third_item);
+ ok(res == 2, "Adding simple item failed (%ld)\n", res);
+ res = addItem(myHwnd, 1, middle_item);
+ ok(res == 1, "Inserting simple item failed (%ld)\n", res);
+
+ /* Add an item completely out of range */
+ res = addItem(myHwnd, 99, out_of_range_item);
+ ok(res == -1, "Adding using out of range index worked unexpectedly (%ld)\n", res);
+ res = addItem(myHwnd, 5, out_of_range_item);
+ ok(res == -1, "Adding using out of range index worked unexpectedly (%ld)\n", res);
+ /* Removed: Causes traps on Windows XP
+ res = addItem(myHwnd, -2, "Out Of Range Item");
+ ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res);
+ */
+
+ /* Get an item completely out of range */
+ res = getItem(myHwnd, 99, &cbexItem);
+ ok(res == 0, "Getting item using out of range index worked unexpectedly (%ld, %s)\n", res, cbexItem.pszText);
+ res = getItem(myHwnd, 4, &cbexItem);
+ ok(res == 0, "Getting item using out of range index worked unexpectedly (%ld, %s)\n", res, cbexItem.pszText);
+ res = getItem(myHwnd, -2, &cbexItem);
+ ok(res == 0, "Getting item using out of range index worked unexpectedly (%ld, %s)\n", res, cbexItem.pszText);
+
+ /* Get an item in range */
+ res = getItem(myHwnd, 0, &cbexItem);
+ ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
+ ok(strcmp(first_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+ res = getItem(myHwnd, 1, &cbexItem);
+ ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
+ ok(strcmp(middle_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+ res = getItem(myHwnd, 2, &cbexItem);
+ ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
+ ok(strcmp(second_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+ res = getItem(myHwnd, 3, &cbexItem);
+ ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
+ ok(strcmp(third_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
+
+ /* Set an item completely out of range */
+ res = setItem(myHwnd, 99, replacement_item);
+ ok(res == 0, "Setting item using out of range index worked unexpectedly (%ld)\n", res);
+ res = setItem(myHwnd, 4, replacement_item);
+ ok(res == 0, "Setting item using out of range index worked unexpectedly (%ld)\n", res);
+ res = setItem(myHwnd, -2, replacement_item);
+ ok(res == 0, "Setting item using out of range index worked unexpectedly (%ld)\n", res);
+
+ /* Set an item in range */
+ res = setItem(myHwnd, 0, replacement_item);
+ ok(res != 0, "Setting first item failed (%ld)\n", res);
+ res = setItem(myHwnd, 3, replacement_item);
+ ok(res != 0, "Setting last item failed (%ld)\n", res);
+
+ /* Remove items completely out of range (4 items in control at this point) */
+ res = delItem(myHwnd, -1);
+ ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%ld)\n", res);
+ res = delItem(myHwnd, 4);
+ ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%ld)\n", res);
+
+ /* Remove items in range (4 items in control at this point) */
+ res = delItem(myHwnd, 3);
+ ok(res == 3, "Deleting using out of range index failed (%ld)\n", res);
+ res = delItem(myHwnd, 0);
+ ok(res == 2, "Deleting using out of range index failed (%ld)\n", res);
+ res = delItem(myHwnd, 0);
+ ok(res == 1, "Deleting using out of range index failed (%ld)\n", res);
+ res = delItem(myHwnd, 0);
+ ok(res == 0, "Deleting using out of range index failed (%ld)\n", res);
+
+ /* Remove from an empty box */
+ res = delItem(myHwnd, 0);
+ ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%ld)\n", res);
+
+
+ /* Cleanup */
+ free(textBuffer);
+
+}
+
+LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProcA(hWnd, msg, wParam, lParam);
+ }
+
+ return 0L;
+}
+
+static void init(void) {
+ WNDCLASSA wc;
+ INITCOMMONCONTROLSEX icex;
+
+ icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ icex.dwICC = ICC_USEREX_CLASSES;
+ InitCommonControlsEx(&icex);
+
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandleA(NULL);
+ wc.hIcon = NULL;
+ wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_ARROW));
+ wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = ComboExTestClass;
+ wc.lpfnWndProc = ComboExTestWndProc;
+ RegisterClassA(&wc);
+
+ hComboExParentWnd = CreateWindowExA(0, ComboExTestClass, "ComboEx test", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
+ assert(hComboExParentWnd != NULL);
+
+ hMainHinst = GetModuleHandleA(NULL);
+
+}
+
+static void cleanup(void)
+{
+ MSG msg;
+
+ PostMessageA(hComboExParentWnd, WM_CLOSE, 0, 0);
+ while (GetMessageA(&msg,0,0,0)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+
+ UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL));
+}
+
+START_TEST(comboex)
+{
+ init();
+
+ test_comboboxex();
+
+ cleanup();
+}
--- /dev/null
+<module name="comctl32_winetest" type="win32cui" installbase="bin" installname="comctl32_winetest.exe" allowwarnings="true">
+ <include base="comctl32_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>shlwapi</library>
+ <library>ole32</library>
+ <library>comctl32</library>
+ <library>ntdll</library>
+ <library>gdi32</library>
+ <library>user32</library>
+ <library>kernel32</library>
+ <library>advapi32</library>
+ <file>comboex.c</file>
+ <file>dpa.c</file>
+ <file>header.c</file>
+ <file>imagelist.c</file>
+ <file>listview.c</file>
+ <file>monthcal.c</file>
+ <file>mru.c</file>
+ <file>progress.c</file>
+ <file>propsheet.c</file>
+ <file>subclass.c</file>
+ <file>tab.c</file>
+ <file>testlist.c</file>
+ <file>toolbar.c</file>
+ <file>tooltips.c</file>
+ <file>treeview.c</file>
+ <file>updown.c</file>
+ <file>propsheet.rc</file>
+</module>
--- /dev/null
+/*
+ * Unit tests for DPA functions
+ *
+ * Copyright 2003 Uwe Bonnes
+ * Copyright 2005 Felix Nawothnig
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <stdarg.h>
+
+#include "windows.h"
+#include "commctrl.h"
+#include "objidl.h"
+
+#include "wine/test.h"
+
+#define DPAM_NOSORT 0x1
+#define DPAM_INSERT 0x4
+#define DPAM_DELETE 0x8
+
+typedef struct _ITEMDATA
+{
+ INT iPos;
+ PVOID pvData;
+} ITEMDATA, *LPITEMDATA;
+
+typedef PVOID (CALLBACK *PFNDPAMERGE)(UINT,PVOID,PVOID,LPARAM);
+typedef HRESULT (CALLBACK *PFNDPASTM)(LPITEMDATA,IStream*,LPARAM);
+
+static HDPA (WINAPI *pDPA_Clone)(const HDPA,const HDPA);
+static HDPA (WINAPI *pDPA_Create)(INT);
+static HDPA (WINAPI *pDPA_CreateEx)(INT,HANDLE);
+static PVOID (WINAPI *pDPA_DeleteAllPtrs)(const HDPA);
+static PVOID (WINAPI *pDPA_DeletePtr)(const HDPA,INT);
+static BOOL (WINAPI *pDPA_Destroy)(const HDPA);
+static VOID (WINAPI *pDPA_DestroyCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
+static VOID (WINAPI *pDPA_EnumCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
+static INT (WINAPI *pDPA_GetPtr)(const HDPA,INT);
+static INT (WINAPI *pDPA_GetPtrIndex)(const HDPA,PVOID);
+static BOOL (WINAPI *pDPA_Grow)(HDPA,INT);
+static INT (WINAPI *pDPA_InsertPtr)(const HDPA,INT,PVOID);
+static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTM,IStream*,LPARAM);
+static BOOL (WINAPI *pDPA_Merge)(const HDPA,const HDPA,DWORD,PFNDPACOMPARE,PFNDPAMERGE,LPARAM);
+static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTM,IStream*,LPARAM);
+static INT (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT);
+static BOOL (WINAPI *pDPA_SetPtr)(const HDPA,INT,PVOID);
+static BOOL (WINAPI *pDPA_Sort)(const HDPA,PFNDPACOMPARE,LPARAM);
+
+#define COMCTL32_GET_PROC(func, ord) \
+ ((p ## func = (PVOID)GetProcAddress(hcomctl32,(LPCSTR)ord)) ? 1 \
+ : (trace( #func " not exported\n"), 0))
+
+static BOOL InitFunctionPtrs(HMODULE hcomctl32)
+{
+ /* 4.00+ */
+ if(COMCTL32_GET_PROC(DPA_Clone, 331) &&
+ COMCTL32_GET_PROC(DPA_Create, 328) &&
+ COMCTL32_GET_PROC(DPA_CreateEx, 340) &&
+ COMCTL32_GET_PROC(DPA_DeleteAllPtrs, 337) &&
+ COMCTL32_GET_PROC(DPA_DeletePtr, 336) &&
+ COMCTL32_GET_PROC(DPA_Destroy, 329) &&
+ COMCTL32_GET_PROC(DPA_GetPtr, 332) &&
+ COMCTL32_GET_PROC(DPA_GetPtrIndex, 333) &&
+ COMCTL32_GET_PROC(DPA_Grow, 330) &&
+ COMCTL32_GET_PROC(DPA_InsertPtr, 334) &&
+ COMCTL32_GET_PROC(DPA_Search, 339) &&
+ COMCTL32_GET_PROC(DPA_SetPtr, 335) &&
+ COMCTL32_GET_PROC(DPA_Sort, 338))
+ {
+ /* 4.71+ */
+ COMCTL32_GET_PROC(DPA_DestroyCallback, 386) &&
+ COMCTL32_GET_PROC(DPA_EnumCallback, 385) &&
+ COMCTL32_GET_PROC(DPA_LoadStream, 9) &&
+ COMCTL32_GET_PROC(DPA_Merge, 11) &&
+ COMCTL32_GET_PROC(DPA_SaveStream, 10);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Callbacks */
+static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp)
+{
+ ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+ return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
+}
+
+static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
+{
+ ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+ return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
+}
+
+static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
+{
+ ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+ return p1;
+}
+
+static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
+{
+ ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+ return ((PCHAR)p2)+1;
+}
+
+static INT nEnum;
+
+static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
+{
+ INT i;
+
+ i = pDPA_GetPtrIndex(lp, pItem);
+ ok(i == nEnum, "i=%d nEnum=%d\n", i, nEnum);
+ nEnum++;
+ pDPA_SetPtr(lp, i, (PVOID)7);
+ return pItem != (PVOID)3;
+}
+
+static HRESULT CALLBACK CB_Save(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
+{
+ HRESULT hRes;
+
+ ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+ hRes = IStream_Write(pStm, &pInfo->iPos, sizeof(INT), NULL);
+ ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+ hRes = IStream_Write(pStm, &pInfo->pvData, sizeof(PVOID), NULL);
+ ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+ return S_OK;
+}
+
+static HRESULT CALLBACK CB_Load(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
+{
+ HRESULT hRes;
+ INT iOldPos;
+
+ iOldPos = pInfo->iPos;
+ ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+ hRes = IStream_Read(pStm, &pInfo->iPos, sizeof(INT), NULL);
+ ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+ ok(pInfo->iPos == iOldPos, "iPos=%d iOldPos=%d\n", pInfo->iPos, iOldPos);
+ hRes = IStream_Read(pStm, &pInfo->pvData, sizeof(PVOID), NULL);
+ ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+ return S_OK;
+}
+
+static BOOL CheckDPA(HDPA dpa, DWORD dwIn, PDWORD pdwOut)
+{
+ DWORD dwOut = 0;
+ INT i;
+
+ for(i = 0; i < 8;)
+ {
+ ULONG_PTR ulItem = (ULONG_PTR)pDPA_GetPtr(dpa, i++);
+ if(!ulItem) break;
+ dwOut = dwOut << 4 | (ulItem & 0xf);
+ }
+
+ *pdwOut = dwOut;
+
+ if(dwOut != dwIn)
+ {
+ pDPA_DeleteAllPtrs(dpa);
+
+ do
+ {
+ pDPA_InsertPtr(dpa, 0, (PVOID)(dwIn & 0xf));
+ dwIn >>= 4;
+ }
+ while(dwIn);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void test_dpa(void)
+{
+ SYSTEM_INFO si;
+ HANDLE hHeap;
+ HDPA dpa, dpa2, dpa3;
+ INT ret, i;
+ PVOID p;
+ DWORD dw, dw2, dw3;
+ HRESULT hRes;
+
+ GetSystemInfo(&si);
+ hHeap = HeapCreate(0, 1, 2);
+ ok(hHeap != NULL, "error=%ld\n", GetLastError());
+ dpa3 = pDPA_CreateEx(0, hHeap);
+ ok(dpa3 != NULL, "\n");
+ ret = pDPA_Grow(dpa3, si.dwPageSize + 1);
+ todo_wine ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY,
+ "ret=%d error=%ld\n", ret, GetLastError());
+
+ dpa = pDPA_Create(0);
+ ok(dpa != NULL, "\n");
+
+ /* Set item with out of bound index */
+ ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
+ /* Fill the greated gap */
+ ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
+ ok(CheckDPA(dpa, 0x56, &dw), "dw=0x%lx\n", dw);
+
+ /* Prepend item */
+ ret = pDPA_InsertPtr(dpa, 1, (PVOID)1);
+ ok(ret == 1, "ret=%d\n", ret);
+ /* Append item using correct index */
+ ret = pDPA_InsertPtr(dpa, 3, (PVOID)3);
+ ok(ret == 3, "ret=%d\n", ret);
+ /* Append item using out of bound index */
+ ret = pDPA_InsertPtr(dpa, 5, (PVOID)2);
+ ok(ret == 4, "ret=%d\n", ret);
+ /* Append item using DPA_APPEND */
+ ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4);
+ ok(ret == 5, "ret=%d\n", ret);
+
+ ok(CheckDPA(dpa, 0x516324, &dw), "dw=0x%lx\n", dw);
+
+ for(i = 1; i <= 6; i++)
+ {
+ INT j, k;
+ k = pDPA_GetPtrIndex(dpa, (PVOID)i);
+ /* Linear searches should work on unsorted DPAs */
+ j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpLT, 0xdeadbeef, 0);
+ ok(j == k, "j=%d k=%d\n", j, k);
+ }
+
+ /* Sort DPA */
+ ok(pDPA_Sort(dpa, CB_CmpGT, 0xdeadbeef), "\n");
+ ok(CheckDPA(dpa, 0x654321, &dw), "dw=0x%lx\n", dw);
+
+ /* Clone into a new DPA */
+ dpa2 = pDPA_Clone(dpa, NULL);
+ ok(dpa2 != NULL, "\n");
+ /* The old data should have been preserved */
+ ok(CheckDPA(dpa2, 0x654321, &dw2), "dw=0x%lx\n", dw2);
+ ok(pDPA_Sort(dpa, CB_CmpLT, 0xdeadbeef), "\n");
+
+ /* Test if the DPA itself was really copied */
+ ok(CheckDPA(dpa, 0x123456, &dw), "dw=0x%lx\n", dw );
+ ok(CheckDPA(dpa2, 0x654321, &dw2), "dw2=0x%lx\n", dw2);
+
+ /* Clone into an old DPA */
+ p = NULL; SetLastError(ERROR_SUCCESS);
+ p = pDPA_Clone(dpa, dpa3);
+ ok(p == dpa3, "p=%p\n", p);
+ ok(CheckDPA(dpa3, 0x123456, &dw3), "dw3=0x%lx\n", dw3);
+
+ for(i = 1; i <= 6; i++)
+ {
+ INT j;
+
+ /* The array is in order so ptr == index+1 */
+ j = pDPA_GetPtrIndex(dpa, (PVOID)i);
+ ok(j+1 == i, "j=%d i=%d\n", j, i);
+ j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
+ ok(j+1 == i, "j=%d i=%d\n", j, i);
+
+ /* Linear searches respect iStart ... */
+ j = pDPA_Search(dpa, (PVOID)i, i+1, CB_CmpLT, 0xdeadbeef, 0);
+ ok(j == DPA_ERR, "j=%d\n", j);
+ /* ... but for a binary search it's ignored */
+ j = pDPA_Search(dpa, (PVOID)i, i+1, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
+ todo_wine ok(j+1 == i, "j=%d i=%d\n", j, i);
+ }
+
+ /* Try to get the index of a nonexistent item */
+ i = pDPA_GetPtrIndex(dpa, (PVOID)7);
+ ok(i == DPA_ERR, "i=%d\n", i);
+
+ /* Try to delete out of bound indexes */
+ p = pDPA_DeletePtr(dpa, -1);
+ ok(p == NULL, "p=%p\n", p);
+ p = pDPA_DeletePtr(dpa, 6);
+ ok(p == NULL, "p=%p\n", p);
+
+ /* Delete the third item */
+ p = pDPA_DeletePtr(dpa, 2);
+ ok(p == (PVOID)3, "p=%p\n", p);
+ ok(CheckDPA(dpa, 0x12456, &dw), "dw=0x%lx\n", dw);
+
+ /* Check where to re-insert the deleted item */
+ i = pDPA_Search(dpa, (PVOID)3, 0,
+ CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTAFTER);
+ ok(i == 2, "i=%d\n", i);
+ /* DPAS_INSERTBEFORE works just like DPAS_INSERTAFTER */
+ i = pDPA_Search(dpa, (PVOID)3, 0,
+ CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTBEFORE);
+ ok(i == 2, "i=%d\n", i);
+ /* without DPAS_INSERTBEFORE/AFTER */
+ i = pDPA_Search(dpa, (PVOID)3, 0,
+ CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
+ ok(i == -1, "i=%d\n", i);
+
+ /* Re-insert the item */
+ ret = pDPA_InsertPtr(dpa, 2, (PVOID)3);
+ ok(ret == 2, "ret=%d i=%d\n", ret, 2);
+ ok(CheckDPA(dpa, 0x123456, &dw), "dw=0x%lx\n", dw);
+
+ /* When doing a binary search while claiming reverse order all indexes
+ * should be bogus */
+ for(i = 0; i < 6; i++)
+ {
+ INT j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpGT, 0xdeadbeef,
+ DPAS_SORTED|DPAS_INSERTBEFORE);
+ ok(j != i, "i=%d\n", i);
+ }
+
+ if(pDPA_Merge)
+ {
+ /* Delete all even entries from dpa */
+ p = pDPA_DeletePtr(dpa, 1);
+ p = pDPA_DeletePtr(dpa, 2);
+ p = pDPA_DeletePtr(dpa, 3);
+ ok(CheckDPA(dpa, 0x135, &dw), "dw=0x%lx\n", dw);
+
+ /* Delete all odd entries from dpa2 */
+ pDPA_Merge(dpa2, dpa, DPAM_DELETE,
+ CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
+ todo_wine ok(CheckDPA(dpa2, 0x246, &dw2), "dw=0x%lx\n", dw2);
+
+ /* Merge dpa3 into dpa2 and dpa */
+ pDPA_Merge(dpa, dpa3, DPAM_INSERT|DPAM_NOSORT,
+ CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
+ pDPA_Merge(dpa2, dpa3, DPAM_INSERT|DPAM_NOSORT,
+ CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
+
+ ok(CheckDPA(dpa, 0x123456, &dw ), "dw=0x%lx\n", dw);
+ ok(CheckDPA(dpa2, 0x123456, &dw2), "dw2=0x%lx\n", dw2);
+ ok(CheckDPA(dpa3, 0x123456, &dw3), "dw3=0x%lx\n", dw3);
+ }
+
+ if(pDPA_EnumCallback)
+ {
+ nEnum = 0;
+ pDPA_EnumCallback(dpa2, CB_EnumFirstThree, (PVOID)dpa2);
+ ok(CheckDPA(dpa2, 0x777456, &dw2), "dw=0x%lx\n", dw2);
+ ok(nEnum == 3, "nEnum=%d\n", nEnum);
+ }
+
+ /* Setting item with huge index should work */
+ ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
+ ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
+ ok(ret == 0x12345, "ret=%d\n", ret);
+
+ pDPA_DeleteAllPtrs(dpa2);
+ ok(CheckDPA(dpa2, 0, &dw2), "dw2=0x%lx\n", dw2);
+ pDPA_Destroy(dpa2);
+
+ if(pDPA_DestroyCallback)
+ {
+ nEnum = 0;
+ pDPA_DestroyCallback(dpa3, CB_EnumFirstThree, dpa3);
+ ok(nEnum == 3, "nEnum=%d\n", nEnum);
+ }
+ else pDPA_Destroy(dpa3);
+
+ if(!pDPA_SaveStream)
+ goto skip_stream_tests;
+
+ hRes = CoInitialize(NULL);
+ if(hRes == S_OK)
+ {
+ static const WCHAR szStg[] = { 'S','t','g',0 };
+ IStorage* pStg = NULL;
+ IStream* pStm = NULL;
+ LARGE_INTEGER liZero;
+ DWORD dwMode;
+ liZero.QuadPart = 0;
+
+ dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
+ hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
+ ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+
+ hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
+ ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+
+ hRes = pDPA_SaveStream(dpa, CB_Save, pStm, 0xdeadbeef);
+ todo_wine ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+ pDPA_Destroy(dpa);
+
+ hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
+ ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+ hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, 0xdeadbeef);
+ todo_wine ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
+ todo_wine ok(CheckDPA(dpa, 0x123456, &dw), "dw=0x%lx\n", dw);
+ pDPA_Destroy(dpa);
+
+ ret = IStream_Release(pStm);
+ ok(!ret, "ret=%d\n", ret);
+
+ ret = IStorage_Release(pStg);
+ ok(!ret, "ret=%d\n", ret);
+
+ CoUninitialize();
+ }
+ else ok(0, "hResult: %ld\n", hRes);
+
+skip_stream_tests:
+ pDPA_Destroy(dpa);
+}
+
+START_TEST(dpa)
+{
+ HMODULE hcomctl32;
+
+ hcomctl32 = GetModuleHandleA("comctl32.dll");
+
+ if(!hcomctl32)
+ {
+ ok(0, "error=%ld\n", GetLastError());
+ return;
+ }
+
+ if(InitFunctionPtrs(hcomctl32))
+ test_dpa();
+ else
+ trace("skipping tests\n");
+
+ FreeLibrary(hcomctl32);
+}
--- /dev/null
+/* Unit test suite for header control.
+ *
+ * Copyright 2005 Vijay Kiran Kamuju
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#include <windows.h>
+#include <commctrl.h>
+#include <assert.h>
+
+#include "wine/test.h"
+
+typedef struct tagEXPECTEDNOTIFY
+{
+ INT iCode;
+ BOOL fUnicode;
+ HDITEMA hdItem;
+} EXPECTEDNOTIFY;
+
+EXPECTEDNOTIFY expectedNotify[10];
+INT nExpectedNotify = 0;
+INT nReceivedNotify = 0;
+INT unexpectedNotify[10];
+INT nUnexpectedNotify = 0;
+
+static HWND hHeaderParentWnd;
+static HWND hWndHeader;
+#define MAX_CHARS 100
+
+static void expect_notify(INT iCode, BOOL fUnicode, HDITEMA *lpItem)
+{
+ assert(nExpectedNotify < 10);
+ expectedNotify[nExpectedNotify].iCode = iCode;
+ expectedNotify[nExpectedNotify].fUnicode = fUnicode;
+ expectedNotify[nExpectedNotify].hdItem = *lpItem;
+ nExpectedNotify++;
+}
+
+static void dont_expect_notify(INT iCode)
+{
+ assert(nUnexpectedNotify < 10);
+ unexpectedNotify[nUnexpectedNotify++] = iCode;
+}
+
+static BOOL notifies_received(void)
+{
+ BOOL fRet = (nExpectedNotify == nReceivedNotify);
+ nExpectedNotify = nReceivedNotify = 0;
+ nUnexpectedNotify = 0;
+ return fRet;
+}
+
+static LONG addItem(HWND hdex, int idx, LPCSTR text)
+{
+ HDITEMA hdItem;
+ hdItem.mask = HDI_TEXT | HDI_WIDTH;
+ hdItem.cxy = 100;
+ hdItem.pszText = (LPSTR)text;
+ hdItem.cchTextMax = 0;
+ return (LONG)SendMessage(hdex, HDM_INSERTITEMA, (WPARAM)idx, (LPARAM)&hdItem);
+}
+
+static LONG setItem(HWND hdex, int idx, LPCSTR text, BOOL fCheckNotifies)
+{
+ LONG ret;
+ HDITEMA hdexItem;
+ hdexItem.mask = HDI_TEXT;
+ hdexItem.pszText = (LPSTR)text;
+ hdexItem.cchTextMax = 0;
+ if (fCheckNotifies)
+ {
+ expect_notify(HDN_ITEMCHANGINGA, FALSE, &hdexItem);
+ expect_notify(HDN_ITEMCHANGEDA, FALSE, &hdexItem);
+ }
+ ret = (LONG)SendMessage(hdex, HDM_SETITEMA, (WPARAM)idx, (LPARAM)&hdexItem);
+ if (fCheckNotifies)
+ ok(notifies_received(), "setItem(): not all expected notifies were received\n");
+ return ret;
+}
+
+static LONG setItemUnicodeNotify(HWND hdex, int idx, LPCSTR text, LPCWSTR wText)
+{
+ LONG ret;
+ HDITEMA hdexItem;
+ HDITEMW hdexNotify;
+ hdexItem.mask = HDI_TEXT;
+ hdexItem.pszText = (LPSTR)text;
+ hdexItem.cchTextMax = 0;
+
+ hdexNotify.mask = HDI_TEXT;
+ hdexNotify.pszText = (LPWSTR)wText;
+
+ expect_notify(HDN_ITEMCHANGINGW, TRUE, (HDITEMA*)&hdexNotify);
+ expect_notify(HDN_ITEMCHANGEDW, TRUE, (HDITEMA*)&hdexNotify);
+ ret = (LONG)SendMessage(hdex, HDM_SETITEMA, (WPARAM)idx, (LPARAM)&hdexItem);
+ ok(notifies_received(), "setItemUnicodeNotify(): not all expected notifies were received\n");
+ return ret;
+}
+
+static LONG delItem(HWND hdex, int idx)
+{
+ return (LONG)SendMessage(hdex, HDM_DELETEITEM, (WPARAM)idx, 0);
+}
+
+static LONG getItemCount(HWND hdex)
+{
+ return (LONG)SendMessage(hdex, HDM_GETITEMCOUNT, 0, 0);
+}
+
+static LONG getItem(HWND hdex, int idx, LPSTR textBuffer)
+{
+ HDITEMA hdItem;
+ hdItem.mask = HDI_TEXT;
+ hdItem.pszText = textBuffer;
+ hdItem.cchTextMax = MAX_CHARS;
+ return (LONG)SendMessage(hdex, HDM_GETITEMA, (WPARAM)idx, (LPARAM)&hdItem);
+}
+
+static void addReadDelItem(HWND hdex, HDITEMA *phdiCreate, int maskRead, HDITEMA *phdiRead)
+{
+ ok(SendMessage(hdex, HDM_INSERTITEMA, (WPARAM)0, (LPARAM)phdiCreate)!=-1, "Adding item failed\n");
+ ZeroMemory(phdiRead, sizeof(HDITEMA));
+ phdiRead->mask = maskRead;
+ ok(SendMessage(hdex, HDM_GETITEMA, (WPARAM)0, (LPARAM)phdiRead)!=0, "Getting item data failed\n");
+ ok(SendMessage(hdex, HDM_DELETEITEM, (WPARAM)0, (LPARAM)0)!=0, "Deleteing item failed\n");
+}
+
+static HWND create_header_control (void)
+{
+ HWND handle;
+ HDLAYOUT hlayout;
+ RECT rectwin;
+ WINDOWPOS winpos;
+
+ handle = CreateWindowEx(0, WC_HEADER, NULL,
+ WS_CHILD|WS_BORDER|WS_VISIBLE|HDS_BUTTONS|HDS_HORZ,
+ 0, 0, 0, 0,
+ hHeaderParentWnd, NULL, NULL, NULL);
+ assert(handle);
+
+ if (winetest_interactive)
+ ShowWindow (hHeaderParentWnd, SW_SHOW);
+
+ GetClientRect(hHeaderParentWnd,&rectwin);
+ hlayout.prc = &rectwin;
+ hlayout.pwpos = &winpos;
+ SendMessageA(handle,HDM_LAYOUT,0,(LPARAM) &hlayout);
+ SetWindowPos(handle, winpos.hwndInsertAfter, winpos.x, winpos.y,
+ winpos.cx, winpos.cy, 0);
+
+ return handle;
+}
+
+static void compare_items(INT iCode, HDITEMA *hdi1, HDITEMA *hdi2, BOOL fUnicode)
+{
+ ok(hdi1->mask == hdi2->mask, "Notify %d mask mismatch (%08x != %08x)\n", iCode, hdi1->mask, hdi2->mask);
+ if (hdi1->mask & HDI_WIDTH)
+ {
+ ok(hdi1->cxy == hdi2->cxy, "Notify %d cxy mismatch (%08x != %08x)\n", iCode, hdi1->cxy, hdi2->cxy);
+ }
+ if (hdi1->mask & HDI_TEXT)
+ {
+ if (hdi1->pszText == LPSTR_TEXTCALLBACKA)
+ {
+ ok(hdi1->pszText == LPSTR_TEXTCALLBACKA, "Notify %d - only one item is LPSTR_TEXTCALLBACK\n", iCode);
+ }
+ else
+ if (fUnicode)
+ {
+ char buf1[260];
+ char buf2[260];
+ WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)hdi1->pszText, -1, buf1, 260, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)hdi2->pszText, -1, buf2, 260, NULL, NULL);
+ ok(lstrcmpW((LPWSTR)hdi1->pszText, (LPWSTR)hdi2->pszText)==0,
+ "Notify %d text mismatch (L\"%s\" vs L\"%s\")\n",
+ iCode, buf1, buf2);
+ }
+ else
+ {
+ ok(strcmp(hdi1->pszText, hdi2->pszText)==0,
+ "Notify %d text mismatch (\"%s\" vs \"%s\")\n",
+ iCode, hdi1->pszText, hdi2->pszText);
+ }
+ }
+}
+
+static const char *str_items[] =
+ {"First Item", "Second Item", "Third Item", "Fourth Item", "Replace Item", "Out Of Range Item"};
+
+static const char pszUniTestA[] = "TST";
+static const WCHAR pszUniTestW[] = {'T','S','T',0};
+
+
+#define TEST_GET_ITEM(i,c)\
+{ res = getItem(hWndHeader, i, buffer);\
+ ok(res != 0, "Getting item[%d] using valid index failed unexpectedly (%ld)\n", i, res);\
+ ok(strcmp(str_items[c], buffer) == 0, "Getting item[%d] returned \"%s\" expecting \"%s\"\n", i, buffer, str_items[c]);\
+}
+
+#define TEST_GET_ITEMCOUNT(i)\
+{ res = getItemCount(hWndHeader);\
+ ok(res == i, "Got Item Count as %ld\n", res);\
+}
+
+static void check_auto_format(void)
+{
+ HDITEMA hdiCreate;
+ HDITEMA hdiRead;
+ static CHAR text[] = "Test";
+ ZeroMemory(&hdiCreate, sizeof(HDITEMA));
+
+ /* Windows implicitly sets some format bits in INSERTITEM */
+
+ /* HDF_STRING is automatically set and cleared for no text */
+ hdiCreate.mask = HDI_TEXT|HDI_WIDTH|HDI_FORMAT;
+ hdiCreate.pszText = text;
+ hdiCreate.cxy = 100;
+ hdiCreate.fmt=HDF_CENTER;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ ok(hdiRead.fmt == (HDF_STRING|HDF_CENTER), "HDF_STRING not set automatically (fmt=%x)\n", hdiRead.fmt);
+
+ hdiCreate.mask = HDI_WIDTH|HDI_FORMAT;
+ hdiCreate.pszText = text;
+ hdiCreate.fmt = HDF_CENTER|HDF_STRING;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ ok(hdiRead.fmt == (HDF_CENTER), "HDF_STRING should be automatically cleared (fmt=%x)\n", hdiRead.fmt);
+
+ /* HDF_BITMAP is automatically set and cleared for a NULL bitmap or no bitmap */
+ hdiCreate.mask = HDI_BITMAP|HDI_WIDTH|HDI_FORMAT;
+ hdiCreate.hbm = CreateBitmap(16, 16, 1, 8, NULL);
+ hdiCreate.fmt = HDF_CENTER;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ ok(hdiRead.fmt == (HDF_BITMAP|HDF_CENTER), "HDF_BITMAP not set automatically (fmt=%x)\n", hdiRead.fmt);
+ DeleteObject(hdiCreate.hbm);
+
+ hdiCreate.hbm = NULL;
+ hdiCreate.fmt = HDF_CENTER|HDF_BITMAP;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ ok(hdiRead.fmt == HDF_CENTER, "HDF_BITMAP not cleared automatically for NULL bitmap (fmt=%x)\n", hdiRead.fmt);
+
+ hdiCreate.mask = HDI_WIDTH|HDI_FORMAT;
+ hdiCreate.fmt = HDF_CENTER|HDF_BITMAP;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ ok(hdiRead.fmt == HDF_CENTER, "HDF_BITMAP not cleared automatically for no bitmap (fmt=%x)\n", hdiRead.fmt);
+
+ /* HDF_IMAGE is automatically set but not cleared */
+ hdiCreate.mask = HDI_IMAGE|HDI_WIDTH|HDI_FORMAT;
+ hdiCreate.iImage = 17;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ ok(hdiRead.fmt == (HDF_IMAGE|HDF_CENTER), "HDF_IMAGE not set automatically (fmt=%x)\n", hdiRead.fmt);
+
+ hdiCreate.mask = HDI_WIDTH|HDI_FORMAT;
+ hdiCreate.fmt = HDF_CENTER|HDF_IMAGE;
+ hdiCreate.iImage = 0;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ ok(hdiRead.fmt == (HDF_CENTER|HDF_IMAGE), "HDF_IMAGE shouldn't be cleared automatically (fmt=%x)\n", hdiRead.fmt);
+}
+
+static void check_auto_fields(void)
+{
+ HDITEMA hdiCreate;
+ HDITEMA hdiRead;
+ static CHAR text[] = "Test";
+ LRESULT res;
+
+ /* Windows stores the format, width, lparam even if they are not in the item's mask */
+ ZeroMemory(&hdiCreate, sizeof(HDITEMA));
+ hdiCreate.mask = HDI_TEXT;
+ hdiCreate.cxy = 100;
+ hdiCreate.pszText = text;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_WIDTH, &hdiRead);
+ TEST_GET_ITEMCOUNT(6);
+ ok(hdiRead.cxy == hdiCreate.cxy, "cxy should be automatically set\n");
+
+ ZeroMemory(&hdiCreate, sizeof(HDITEMA));
+ hdiCreate.mask = HDI_TEXT;
+ hdiCreate.pszText = text;
+ hdiCreate.lParam = 0x12345678;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_LPARAM, &hdiRead);
+ TEST_GET_ITEMCOUNT(6);
+ ok(hdiRead.lParam == hdiCreate.lParam, "lParam should be automatically set\n");
+
+ ZeroMemory(&hdiCreate, sizeof(HDITEMA));
+ hdiCreate.mask = HDI_TEXT;
+ hdiCreate.pszText = text;
+ hdiCreate.fmt = HDF_STRING|HDF_CENTER;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_FORMAT, &hdiRead);
+ TEST_GET_ITEMCOUNT(6);
+ ok(hdiRead.fmt == hdiCreate.fmt, "fmt should be automatically set\n");
+
+ /* others fields are not set */
+ ZeroMemory(&hdiCreate, sizeof(HDITEMA));
+ hdiCreate.mask = HDI_TEXT;
+ hdiCreate.pszText = text;
+ hdiCreate.hbm = CreateBitmap(16, 16, 1, 8, NULL);
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_BITMAP, &hdiRead);
+ TEST_GET_ITEMCOUNT(6);
+ ok(hdiRead.hbm == NULL, "hbm should not be automatically set\n");
+ DeleteObject(hdiCreate.hbm);
+
+ ZeroMemory(&hdiCreate, sizeof(HDITEMA));
+ hdiCreate.mask = HDI_IMAGE;
+ hdiCreate.iImage = 17;
+ hdiCreate.pszText = text;
+ addReadDelItem(hWndHeader, &hdiCreate, HDI_TEXT, &hdiRead);
+ TEST_GET_ITEMCOUNT(6);
+ ok(hdiRead.pszText==NULL, "pszText shouldn't be automatically set\n");
+
+ /* field from comctl >4.0 not tested as the system probably won't touch them */
+}
+
+static void check_mask(void)
+{
+ HDITEMA hdi;
+ static CHAR text[] = "ABC";
+ LRESULT ret;
+
+ /* don't create items if the mask is zero */
+ ZeroMemory(&hdi, sizeof(hdi));
+ hdi.mask = 0;
+ hdi.cxy = 200;
+ hdi.pszText = text;
+ hdi.fmt = 0;
+ hdi.iOrder = 0;
+ hdi.lParam = 17;
+ hdi.cchTextMax = 260;
+ ret = SendMessage(hWndHeader, HDM_INSERTITEM, (WPARAM)0, (LPARAM)&hdi);
+ ok(ret == -1, "Creating an item with a zero mask should have failed\n");
+ if (ret != -1) SendMessage(hWndHeader, HDM_DELETEITEM, (WPARAM)0, (LPARAM)0);
+
+ /* with a non-zero mask creation will succeed */
+ ZeroMemory(&hdi, sizeof(hdi));
+ hdi.mask = HDI_LPARAM;
+ ret = SendMessage(hWndHeader, HDM_INSERTITEM, (WPARAM)0, (LPARAM)&hdi);
+ ok(ret != -1, "Adding item with non-zero mask failed\n");
+ if (ret != -1)
+ SendMessage(hWndHeader, HDM_DELETEITEM, (WPARAM)0, (LPARAM)0);
+
+ /* in SETITEM if the mask contains a unknown bit, it is ignored */
+ ZeroMemory(&hdi, sizeof(hdi));
+ hdi.mask = 0x08000000 | HDI_LPARAM | HDI_IMAGE;
+ hdi.lParam = 133;
+ hdi.iImage = 17;
+ ret = SendMessage(hWndHeader, HDM_INSERTITEM, (WPARAM)0, (LPARAM)&hdi);
+ ok(ret != -1, "Adding item failed\n");
+
+ if (ret != -1)
+ {
+ /* check result */
+ ZeroMemory(&hdi, sizeof(hdi));
+ hdi.mask = HDI_LPARAM | HDI_IMAGE;
+ SendMessage(hWndHeader, HDM_GETITEM, (WPARAM)0, (LPARAM)&hdi);
+ ok(hdi.lParam == 133, "comctl32 4.0 field not set\n");
+ ok(hdi.iImage == 17, "comctl32 >4.0 field not set\n");
+
+ /* but in GETITEM if an unknown bit is set, comctl32 uses only version 4.0 fields */
+ ZeroMemory(&hdi, sizeof(hdi));
+ hdi.mask = 0x08000000 | HDI_LPARAM | HDI_IMAGE;
+ SendMessage(hWndHeader, HDM_GETITEM, (WPARAM)0, (LPARAM)&hdi);
+ ok(hdi.lParam == 133, "comctl32 4.0 field not read\n");
+ ok(hdi.iImage == 0, "comctl32 >4.0 field shouldn't be read\n");
+
+ SendMessage(hWndHeader, HDM_DELETEITEM, (WPARAM)0, (LPARAM)0);
+ }
+}
+
+static void test_header_control (void)
+{
+ LONG res;
+ static char buffer[MAX_CHARS];
+ int i;
+
+ hWndHeader = create_header_control ();
+
+ for (i = 3; i >= 0; i--)
+ {
+ TEST_GET_ITEMCOUNT(3-i);
+ res = addItem(hWndHeader, 0, str_items[i]);
+ ok(res == 0, "Adding simple item failed (%ld)\n", res);
+ }
+
+ TEST_GET_ITEMCOUNT(4);
+ res = addItem(hWndHeader, 99, str_items[i+1]);
+ ok(res != -1, "Adding Out of Range item should fail with -1 got (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(5);
+ res = addItem(hWndHeader, 5, str_items[i+1]);
+ ok(res != -1, "Adding Out of Range item should fail with -1 got (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(6);
+
+ for (i = 0; i < 4; i++) { TEST_GET_ITEM(i,i); TEST_GET_ITEMCOUNT(6); }
+
+ res=getItem(hWndHeader, 99, buffer);
+ ok(res == 0, "Getting Out of Range item should fail with 0 (%ld), got %s\n", res,buffer);
+ res=getItem(hWndHeader, 5, buffer);
+ ok(res == 1, "Getting Out of Range item should fail with 1 (%ld), got %s\n", res,buffer);
+ res=getItem(hWndHeader, -2, buffer);
+ ok(res == 0, "Getting Out of Range item should fail with 0 (%ld), got %s\n", res,buffer);
+
+ if (winetest_interactive)
+ {
+ UpdateWindow(hHeaderParentWnd);
+ UpdateWindow(hWndHeader);
+ }
+
+ TEST_GET_ITEMCOUNT(6);
+ res=setItem(hWndHeader, 99, str_items[5], FALSE);
+ ok(res == 0, "Setting Out of Range item should fail with 0 (%ld)\n", res);
+ res=setItem(hWndHeader, 5, str_items[5], TRUE);
+ ok(res == 1, "Setting Out of Range item should fail with 1 (%ld)\n", res);
+ res=setItem(hWndHeader, -2, str_items[5], FALSE);
+ ok(res == 0, "Setting Out of Range item should fail with 0 (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(6);
+
+ for (i = 0; i < 4; i++)
+ {
+ res = setItem(hWndHeader, i, str_items[4], TRUE);
+ ok(res != 0, "Setting %d item failed (%ld)\n", i+1, res);
+ TEST_GET_ITEM(i, 4);
+ TEST_GET_ITEMCOUNT(6);
+ }
+
+ SendMessageA(hWndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, 0);
+ setItemUnicodeNotify(hWndHeader, 3, pszUniTestA, pszUniTestW);
+ SendMessageA(hWndHeader, WM_NOTIFYFORMAT, (WPARAM)hHeaderParentWnd, (LPARAM)NF_REQUERY);
+ setItem(hWndHeader, 3, str_items[4], TRUE);
+
+ dont_expect_notify(HDN_GETDISPINFOA);
+ dont_expect_notify(HDN_GETDISPINFOW);
+ addItem(hWndHeader, 0, LPSTR_TEXTCALLBACKA);
+ setItem(hWndHeader, 0, str_items[4], TRUE);
+ /* unexpected notifies cleared by notifies_received in setItem */
+ dont_expect_notify(HDN_GETDISPINFOA);
+ dont_expect_notify(HDN_GETDISPINFOW);
+ setItem(hWndHeader, 0, LPSTR_TEXTCALLBACKA, TRUE);
+ /* unexpected notifies cleared by notifies_received in setItem */
+ delItem(hWndHeader, 0);
+
+ check_auto_format();
+ TEST_GET_ITEMCOUNT(6);
+ check_auto_fields();
+ TEST_GET_ITEMCOUNT(6);
+ check_mask();
+ TEST_GET_ITEMCOUNT(6);
+
+ res = delItem(hWndHeader, 5);
+ ok(res == 1, "Deleting Out of Range item should fail with 1 (%ld)\n", res);
+ res = delItem(hWndHeader, -2);
+ ok(res == 0, "Deleting Out of Range item should fail with 0 (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(5);
+
+ res = delItem(hWndHeader, 3);
+ ok(res != 0, "Deleting using out of range index failed (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(4);
+ res = delItem(hWndHeader, 0);
+ ok(res != 0, "Deleting using out of range index failed (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(3);
+ res = delItem(hWndHeader, 0);
+ ok(res != 0, "Deleting using out of range index failed (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(2);
+ res = delItem(hWndHeader, 0);
+ ok(res != 0, "Deleting using out of range index failed (%ld)\n", res);
+ TEST_GET_ITEMCOUNT(1);
+
+ DestroyWindow(hWndHeader);
+}
+
+
+LRESULT CALLBACK HeaderTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+
+ case WM_NOTIFY:
+ {
+ NMHEADERA *hdr = (NMHEADER *)lParam;
+ EXPECTEDNOTIFY *expected;
+ int i;
+
+ for (i=0; i<nUnexpectedNotify; i++)
+ ok(hdr->hdr.code != unexpectedNotify[i], "Received invalid notify %d\n", hdr->hdr.code);
+
+ if (nReceivedNotify >= nExpectedNotify || hdr->hdr.hwndFrom != hWndHeader )
+ break;
+
+ expected = &expectedNotify[nReceivedNotify];
+ if (hdr->hdr.code != expected->iCode)
+ break;
+
+ nReceivedNotify++;
+ compare_items(hdr->hdr.code, &expected->hdItem, hdr->pitem, expected->fUnicode);
+ break;
+ }
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProcA(hWnd, msg, wParam, lParam);
+ }
+
+ return 0L;
+}
+
+static void init(void) {
+ WNDCLASSA wc;
+ INITCOMMONCONTROLSEX icex;
+
+ icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ icex.dwICC = ICC_USEREX_CLASSES;
+ InitCommonControlsEx(&icex);
+
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandleA(NULL);
+ wc.hIcon = NULL;
+ wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_ARROW));
+ wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = "HeaderTestClass";
+ wc.lpfnWndProc = HeaderTestWndProc;
+ RegisterClassA(&wc);
+
+ hHeaderParentWnd = CreateWindowExA(0, "HeaderTestClass", "Header test", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
+ assert(hHeaderParentWnd != NULL);
+}
+
+START_TEST(header)
+{
+ init();
+
+ test_header_control();
+
+ DestroyWindow(hHeaderParentWnd);
+}
--- /dev/null
+/* Unit test suite for imagelist control.
+ *
+ * Copyright 2004 Michael Stefaniuc
+ * Copyright 2002 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+
+#undef VISIBLE
+
+#ifdef VISIBLE
+#define WAIT Sleep (1000)
+#define REDRAW(hwnd) RedrawWindow (hwnd, NULL, 0, RDW_UPDATENOW)
+#else
+#define WAIT
+#define REDRAW(hwnd)
+#endif
+
+
+static BOOL (WINAPI *pImageList_DrawIndirect)(IMAGELISTDRAWPARAMS*) = NULL;
+
+static HDC desktopDC;
+static HINSTANCE hinst;
+
+/* These macros build cursor/bitmap data in 4x4 pixel blocks */
+#define B(x,y) ((x?0xf0:0)|(y?0xf:0))
+#define ROW1(a,b,c,d,e,f,g,h) B(a,b),B(c,d),B(e,f),B(g,h)
+#define ROW32(a,b,c,d,e,f,g,h) ROW1(a,b,c,d,e,f,g,h), ROW1(a,b,c,d,e,f,g,h), \
+ ROW1(a,b,c,d,e,f,g,h), ROW1(a,b,c,d,e,f,g,h)
+#define ROW2(a,b,c,d,e,f,g,h,i,j,k,l) ROW1(a,b,c,d,e,f,g,h),B(i,j),B(k,l)
+#define ROW48(a,b,c,d,e,f,g,h,i,j,k,l) ROW2(a,b,c,d,e,f,g,h,i,j,k,l), \
+ ROW2(a,b,c,d,e,f,g,h,i,j,k,l), ROW2(a,b,c,d,e,f,g,h,i,j,k,l), \
+ ROW2(a,b,c,d,e,f,g,h,i,j,k,l)
+
+static const BYTE empty_bits[48*48/8];
+
+static const BYTE icon_bits[32*32/8] =
+{
+ ROW32(0,0,0,0,0,0,0,0),
+ ROW32(0,0,1,1,1,1,0,0),
+ ROW32(0,1,1,1,1,1,1,0),
+ ROW32(0,1,1,0,0,1,1,0),
+ ROW32(0,1,1,0,0,1,1,0),
+ ROW32(0,1,1,1,1,1,1,0),
+ ROW32(0,0,1,1,1,1,0,0),
+ ROW32(0,0,0,0,0,0,0,0)
+};
+
+static const BYTE bitmap_bits[48*48/8] =
+{
+ ROW48(0,0,0,0,0,0,0,0,0,0,0,0),
+ ROW48(0,1,1,1,1,1,1,1,1,1,1,0),
+ ROW48(0,1,1,0,0,0,0,0,0,1,1,0),
+ ROW48(0,1,0,0,0,0,0,0,1,0,1,0),
+ ROW48(0,1,0,0,0,0,0,1,0,0,1,0),
+ ROW48(0,1,0,0,0,0,1,0,0,0,1,0),
+ ROW48(0,1,0,0,0,1,0,0,0,0,1,0),
+ ROW48(0,1,0,0,1,0,0,0,0,0,1,0),
+ ROW48(0,1,0,1,0,0,0,0,0,0,1,0),
+ ROW48(0,1,1,0,0,0,0,0,0,1,1,0),
+ ROW48(0,1,1,1,1,1,1,1,1,1,1,0),
+ ROW48(0,0,0,0,0,0,0,0,0,0,0,0)
+};
+
+static HIMAGELIST createImageList(int cx, int cy)
+{
+ /* Create an ImageList and put an image into it */
+ HIMAGELIST himl = ImageList_Create(cx, cy, ILC_COLOR, 1, 1);
+ HBITMAP hbm = CreateBitmap(48, 48, 1, 1, bitmap_bits);
+ ImageList_Add(himl, hbm, NULL);
+ return himl;
+}
+
+static HWND create_a_window(void)
+{
+ char className[] = "bmwnd";
+ char winName[] = "Test Bitmap";
+ HWND hWnd;
+ static int registered = 0;
+
+ if (!registered)
+ {
+ WNDCLASSA cls;
+
+ cls.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS;
+ cls.lpfnWndProc = DefWindowProcA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = 0;
+ cls.hIcon = LoadIconA (0, (LPSTR)IDI_APPLICATION);
+ cls.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
+ cls.lpszMenuName = 0;
+ cls.lpszClassName = className;
+
+ RegisterClassA (&cls);
+ registered = 1;
+ }
+
+ /* Setup window */
+ hWnd = CreateWindowA (className, winName,
+ WS_OVERLAPPEDWINDOW ,
+ CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, 0,
+ 0, hinst, 0);
+
+#ifdef VISIBLE
+ ShowWindow (hWnd, SW_SHOW);
+#endif
+ REDRAW(hWnd);
+ WAIT;
+
+ return hWnd;
+}
+
+static HDC show_image(HWND hwnd, HIMAGELIST himl, int idx, int size,
+ LPCSTR loc, BOOL clear)
+{
+ HDC hdc = NULL;
+#ifdef VISIBLE
+ if (!himl) return NULL;
+
+ SetWindowText(hwnd, loc);
+ hdc = GetDC(hwnd);
+ ImageList_Draw(himl, idx, hdc, 0, 0, ILD_TRANSPARENT);
+
+ REDRAW(hwnd);
+ WAIT;
+
+ if (clear)
+ {
+ BitBlt(hdc, 0, 0, size, size, hdc, size+1, size+1, SRCCOPY);
+ ReleaseDC(hwnd, hdc);
+ hdc = NULL;
+ }
+#endif /* VISIBLE */
+ return hdc;
+}
+
+/* Useful for checking differences */
+#if 0
+static void dump_bits(const BYTE *p, const BYTE *q, int size)
+{
+ int i, j;
+
+ size /= 8;
+
+ for (i = 0; i < size * 2; i++)
+ {
+ printf("|");
+ for (j = 0; j < size; j++)
+ printf("%c%c", p[j] & 0xf0 ? 'X' : ' ', p[j] & 0xf ? 'X' : ' ');
+ printf(" -- ");
+ for (j = 0; j < size; j++)
+ printf("%c%c", q[j] & 0xf0 ? 'X' : ' ', q[j] & 0xf ? 'X' : ' ');
+ printf("|\n");
+ p += size * 4;
+ q += size * 4;
+ }
+ printf("\n");
+}
+#endif
+
+static void check_bits(HWND hwnd, HIMAGELIST himl, int idx, int size,
+ const BYTE *checkbits, LPCSTR loc)
+{
+#ifdef VISIBLE
+ BYTE bits[100*100/8];
+ COLORREF c;
+ HDC hdc;
+ int x, y, i = -1;
+
+ if (!himl) return;
+
+ memset(bits, 0, sizeof(bits));
+ hdc = show_image(hwnd, himl, idx, size, loc, FALSE);
+
+ c = GetPixel(hdc, 0, 0);
+
+ for (y = 0; y < size; y ++)
+ {
+ for (x = 0; x < size; x++)
+ {
+ if (!(x & 0x7)) i++;
+ if (GetPixel(hdc, x, y) != c) bits[i] |= (0x80 >> (x & 0x7));
+ }
+ }
+
+ BitBlt(hdc, 0, 0, size, size, hdc, size+1, size+1, SRCCOPY);
+ ReleaseDC(hwnd, hdc);
+
+ ok (memcmp(bits, checkbits, (size * size)/8) == 0,
+ "%s: bits different\n", loc);
+ if (memcmp(bits, checkbits, (size * size)/8))
+ dump_bits(bits, checkbits, size);
+#endif /* VISIBLE */
+}
+
+static void testHotspot (void)
+{
+ struct hotspot {
+ int dx;
+ int dy;
+ };
+
+#define SIZEX1 47
+#define SIZEY1 31
+#define SIZEX2 11
+#define SIZEY2 17
+#define HOTSPOTS_MAX 4 /* Number of entries in hotspots */
+ static const struct hotspot hotspots[HOTSPOTS_MAX] = {
+ { 10, 7 },
+ { SIZEX1, SIZEY1 },
+ { -9, -8 },
+ { -7, 35 }
+ };
+ int i, j, ret;
+ HIMAGELIST himl1 = createImageList(SIZEX1, SIZEY1);
+ HIMAGELIST himl2 = createImageList(SIZEX2, SIZEY2);
+ HWND hwnd = create_a_window();
+
+
+ for (i = 0; i < HOTSPOTS_MAX; i++) {
+ for (j = 0; j < HOTSPOTS_MAX; j++) {
+ int dx1 = hotspots[i].dx;
+ int dy1 = hotspots[i].dy;
+ int dx2 = hotspots[j].dx;
+ int dy2 = hotspots[j].dy;
+ int correctx, correcty, newx, newy;
+ char loc[256];
+ HIMAGELIST himlNew;
+ POINT ppt;
+
+ ret = ImageList_BeginDrag(himl1, 0, dx1, dy1);
+ ok(ret != 0, "BeginDrag failed for { %d, %d }\n", dx1, dy1);
+ sprintf(loc, "BeginDrag (%d,%d)\n", i, j);
+ show_image(hwnd, himl1, 0, max(SIZEX1, SIZEY1), loc, TRUE);
+
+ /* check merging the dragged image with a second image */
+ ret = ImageList_SetDragCursorImage(himl2, 0, dx2, dy2);
+ ok(ret != 0, "SetDragCursorImage failed for {%d, %d}{%d, %d}\n",
+ dx1, dy1, dx2, dy2);
+ sprintf(loc, "SetDragCursorImage (%d,%d)\n", i, j);
+ show_image(hwnd, himl2, 0, max(SIZEX2, SIZEY2), loc, TRUE);
+
+ /* check new hotspot, it should be the same like the old one */
+ himlNew = ImageList_GetDragImage(NULL, &ppt);
+ ok(ppt.x == dx1 && ppt.y == dy1,
+ "Expected drag hotspot [%d,%d] got [%ld,%ld]\n",
+ dx1, dy1, ppt.x, ppt.y);
+ /* check size of new dragged image */
+ ImageList_GetIconSize(himlNew, &newx, &newy);
+ correctx = max(SIZEX1, max(SIZEX2 + dx2, SIZEX1 - dx2));
+ correcty = max(SIZEY1, max(SIZEY2 + dy2, SIZEY1 - dy2));
+ ok(newx == correctx && newy == correcty,
+ "Expected drag image size [%d,%d] got [%d,%d]\n",
+ correctx, correcty, newx, newy);
+ sprintf(loc, "GetDragImage (%d,%d)\n", i, j);
+ show_image(hwnd, himlNew, 0, max(correctx, correcty), loc, TRUE);
+ ImageList_EndDrag();
+ }
+ }
+#undef SIZEX1
+#undef SIZEY1
+#undef SIZEX2
+#undef SIZEY2
+#undef HOTSPOTS_MAX
+ DestroyWindow(hwnd);
+}
+
+static BOOL DoTest1(void)
+{
+ HIMAGELIST himl ;
+
+ HICON hicon1 ;
+ HICON hicon2 ;
+ HICON hicon3 ;
+
+ /* create an imagelist to play with */
+ himl = ImageList_Create(84,84,0x10,0,3);
+ ok(himl!=0,"failed to create imagelist\n");
+
+ /* load the icons to add to the image list */
+ hicon1 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+ ok(hicon1 != 0, "no hicon1\n");
+ hicon2 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+ ok(hicon2 != 0, "no hicon2\n");
+ hicon3 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+ ok(hicon3 != 0, "no hicon3\n");
+
+ /* remove when nothing exists */
+ ok(!ImageList_Remove(himl,0),"removed nonexistent icon\n");
+ /* removing everything from an empty imagelist should succeed */
+ ok(ImageList_RemoveAll(himl),"removed nonexistent icon\n");
+
+ /* add three */
+ ok(0==ImageList_AddIcon(himl, hicon1),"failed to add icon1\n");
+ ok(1==ImageList_AddIcon(himl, hicon2),"failed to add icon2\n");
+ ok(2==ImageList_AddIcon(himl, hicon3),"failed to add icon3\n");
+
+ /* remove an index out of range */
+ ok(!ImageList_Remove(himl,4711),"removed nonexistent icon\n");
+
+ /* remove three */
+ ok(ImageList_Remove(himl,0),"can't remove 0\n");
+ ok(ImageList_Remove(himl,0),"can't remove 0\n");
+ ok(ImageList_Remove(himl,0),"can't remove 0\n");
+
+ /* remove one extra */
+ ok(!ImageList_Remove(himl,0),"removed nonexistent icon\n");
+
+ /* destroy it */
+ ok(ImageList_Destroy(himl),"destroy imagelist failed\n");
+
+ /* icons should be deleted by the imagelist */
+ ok(!DeleteObject(hicon1),"icon 1 wasn't deleted\n");
+ ok(!DeleteObject(hicon2),"icon 2 wasn't deleted\n");
+ ok(!DeleteObject(hicon3),"icon 3 wasn't deleted\n");
+
+ return TRUE;
+}
+
+static BOOL DoTest2(void)
+{
+ HIMAGELIST himl ;
+
+ HICON hicon1 ;
+ HICON hicon2 ;
+ HICON hicon3 ;
+
+ /* create an imagelist to play with */
+ himl = ImageList_Create(84,84,0x10,0,3);
+ ok(himl!=0,"failed to create imagelist\n");
+
+ /* load the icons to add to the image list */
+ hicon1 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+ ok(hicon1 != 0, "no hicon1\n");
+ hicon2 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+ ok(hicon2 != 0, "no hicon2\n");
+ hicon3 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+ ok(hicon3 != 0, "no hicon3\n");
+
+ /* add three */
+ ok(0==ImageList_AddIcon(himl, hicon1),"failed to add icon1\n");
+ ok(1==ImageList_AddIcon(himl, hicon2),"failed to add icon2\n");
+ ok(2==ImageList_AddIcon(himl, hicon3),"failed to add icon3\n");
+
+ /* destroy it */
+ ok(ImageList_Destroy(himl),"destroy imagelist failed\n");
+
+ /* icons should be deleted by the imagelist */
+ ok(!DeleteObject(hicon1),"icon 1 wasn't deleted\n");
+ ok(!DeleteObject(hicon2),"icon 2 wasn't deleted\n");
+ ok(!DeleteObject(hicon3),"icon 3 wasn't deleted\n");
+
+ return TRUE;
+}
+
+static BOOL DoTest3(void)
+{
+ HIMAGELIST himl;
+
+ HBITMAP hbm1;
+ HBITMAP hbm2;
+ HBITMAP hbm3;
+
+ IMAGELISTDRAWPARAMS imldp;
+ HDC hdc;
+ HWND hwndfortest;
+
+ if (!pImageList_DrawIndirect)
+ {
+ HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
+ pImageList_DrawIndirect = (void*)GetProcAddress(hComCtl32, "ImageList_DrawIndirect");
+ if (!pImageList_DrawIndirect)
+ {
+ trace("ImageList_DrawIndirect not available, skipping test\n");
+ return TRUE;
+ }
+ }
+
+ hwndfortest = create_a_window();
+ hdc = GetDC(hwndfortest);
+ ok(hdc!=NULL, "couldn't get DC\n");
+
+ /* create an imagelist to play with */
+ himl = ImageList_Create(48,48,0x10,0,3);
+ ok(himl!=0,"failed to create imagelist\n");
+
+ /* load the icons to add to the image list */
+ hbm1 = CreateBitmap(48, 48, 1, 1, bitmap_bits);
+ ok(hbm1 != 0, "no bitmap 1\n");
+ hbm2 = CreateBitmap(48, 48, 1, 1, bitmap_bits);
+ ok(hbm2 != 0, "no bitmap 2\n");
+ hbm3 = CreateBitmap(48, 48, 1, 1, bitmap_bits);
+ ok(hbm3 != 0, "no bitmap 3\n");
+
+ /* add three */
+ ok(0==ImageList_Add(himl, hbm1, 0),"failed to add bitmap 1\n");
+ ok(1==ImageList_Add(himl, hbm2, 0),"failed to add bitmap 2\n");
+
+ ok(ImageList_SetImageCount(himl,3),"Setimage count failed\n");
+ /*ok(2==ImageList_Add(himl, hbm3, NULL),"failed to add bitmap 3\n"); */
+ ok(ImageList_Replace(himl, 2, hbm3, 0),"failed to replace bitmap 3\n");
+
+ memset(&imldp, 0, sizeof (imldp));
+ ok(!pImageList_DrawIndirect(&imldp), "zero data succeeded!\n");
+ imldp.cbSize = sizeof (imldp);
+ ok(!pImageList_DrawIndirect(&imldp), "zero hdc succeeded!\n");
+ imldp.hdcDst = hdc;
+ ok(!pImageList_DrawIndirect(&imldp),"zero himl succeeded!\n");
+ imldp.himl = himl;
+ if (!pImageList_DrawIndirect(&imldp))
+ {
+ /* Earlier versions of native comctl32 use a smaller structure */
+ imldp.cbSize -= 3 * sizeof(DWORD);
+ ok(pImageList_DrawIndirect(&imldp),"DrawIndirect should succeed\n");
+ }
+ REDRAW(hwndfortest);
+ WAIT;
+
+ imldp.fStyle = SRCCOPY;
+ imldp.rgbBk = CLR_DEFAULT;
+ imldp.rgbFg = CLR_DEFAULT;
+ imldp.y = 100;
+ imldp.x = 100;
+ ok(pImageList_DrawIndirect(&imldp),"should succeed\n");
+ imldp.i ++;
+ ok(pImageList_DrawIndirect(&imldp),"should succeed\n");
+ imldp.i ++;
+ ok(pImageList_DrawIndirect(&imldp),"should succeed\n");
+ imldp.i ++;
+ ok(!pImageList_DrawIndirect(&imldp),"should fail\n");
+
+ /* remove three */
+ ok(ImageList_Remove(himl, 0), "removing 1st bitmap\n");
+ ok(ImageList_Remove(himl, 0), "removing 2nd bitmap\n");
+ ok(ImageList_Remove(himl, 0), "removing 3rd bitmap\n");
+
+ /* destroy it */
+ ok(ImageList_Destroy(himl),"destroy imagelist failed\n");
+
+ /* bitmaps should not be deleted by the imagelist */
+ ok(DeleteObject(hbm1),"bitmap 1 can't be deleted\n");
+ ok(DeleteObject(hbm2),"bitmap 2 can't be deleted\n");
+ ok(DeleteObject(hbm3),"bitmap 3 can't be deleted\n");
+
+ ReleaseDC(hwndfortest, hdc);
+ DestroyWindow(hwndfortest);
+
+ return TRUE;
+}
+
+static void testMerge(void)
+{
+ HIMAGELIST himl1, himl2, hmerge;
+ HICON hicon1;
+ HWND hwnd = create_a_window();
+
+ himl1 = ImageList_Create(32,32,0,0,3);
+ ok(himl1 != NULL,"failed to create himl1\n");
+
+ himl2 = ImageList_Create(32,32,0,0,3);
+ ok(himl2 != NULL,"failed to create himl2\n");
+
+ hicon1 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+ ok(hicon1 != NULL, "failed to create hicon1\n");
+
+ if (!himl1 || !himl2 || !hicon1)
+ return;
+
+ ok(0==ImageList_AddIcon(himl2, hicon1),"add icon1 to himl2 failed\n");
+ check_bits(hwnd, himl2, 0, 32, icon_bits, "add icon1 to himl2");
+
+ /* If himl1 has no images, merge still succeeds */
+ hmerge = ImageList_Merge(himl1, -1, himl2, 0, 0, 0);
+ ok(hmerge != NULL, "merge himl1,-1 failed\n");
+ check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1,-1");
+ if (hmerge) ImageList_Destroy(hmerge);
+
+ hmerge = ImageList_Merge(himl1, 0, himl2, 0, 0, 0);
+ ok(hmerge != NULL,"merge himl1,0 failed\n");
+ check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1,0");
+ if (hmerge) ImageList_Destroy(hmerge);
+
+ /* Same happens if himl2 is empty */
+ ImageList_Destroy(himl2);
+ himl2 = ImageList_Create(32,32,0,0,3);
+ ok(himl2 != NULL,"failed to recreate himl2\n");
+ if (!himl2)
+ return;
+
+ hmerge = ImageList_Merge(himl1, -1, himl2, -1, 0, 0);
+ ok(hmerge != NULL, "merge himl2,-1 failed\n");
+ check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2,-1");
+ if (hmerge) ImageList_Destroy(hmerge);
+
+ hmerge = ImageList_Merge(himl1, -1, himl2, 0, 0, 0);
+ ok(hmerge != NULL, "merge himl2,0 failed\n");
+ check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2,0");
+ if (hmerge) ImageList_Destroy(hmerge);
+
+ /* Now try merging an image with itself */
+ ok(0==ImageList_AddIcon(himl2, hicon1),"re-add icon1 to himl2 failed\n");
+
+ hmerge = ImageList_Merge(himl2, 0, himl2, 0, 0, 0);
+ ok(hmerge != NULL, "merge himl2 with itself failed\n");
+ check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2 with itself");
+ if (hmerge) ImageList_Destroy(hmerge);
+
+ /* Try merging 2 different image lists */
+ ok(0==ImageList_AddIcon(himl1, hicon1),"add icon1 to himl1 failed\n");
+
+ hmerge = ImageList_Merge(himl1, 0, himl2, 0, 0, 0);
+ ok(hmerge != NULL, "merge himl1 with himl2 failed\n");
+ check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1 with himl2");
+ if (hmerge) ImageList_Destroy(hmerge);
+
+ hmerge = ImageList_Merge(himl1, 0, himl2, 0, 8, 16);
+ ok(hmerge != NULL, "merge himl1 with himl2 8,16 failed\n");
+ check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1 with himl2, 8,16");
+ if (hmerge) ImageList_Destroy(hmerge);
+
+ ImageList_Destroy(himl1);
+ ImageList_Destroy(himl2);
+ DeleteObject(hicon1);
+ DestroyWindow(hwnd);
+}
+
+START_TEST(imagelist)
+{
+ desktopDC=GetDC(NULL);
+ hinst = GetModuleHandleA(NULL);
+
+ InitCommonControls();
+
+ testHotspot();
+ DoTest1();
+ DoTest2();
+ DoTest3();
+ testMerge();
+}
--- /dev/null
+/*
+ * ListView tests
+ *
+ * Copyright 2006 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <commctrl.h>
+
+#include "wine/test.h"
+
+static void test_images(void)
+{
+ HWND hwnd, hwndparent = 0;
+ DWORD r;
+ LVITEM item;
+ HIMAGELIST himl;
+ HBITMAP hbmp;
+ RECT r1, r2;
+ static CHAR hello[] = "hello";
+
+ himl = ImageList_Create(40, 40, 0, 4, 4);
+ ok(himl != NULL, "failed to create imagelist\n");
+
+ hbmp = CreateBitmap(40, 40, 1, 1, NULL);
+ ok(hbmp != NULL, "failed to create bitmap\n");
+
+ r = ImageList_Add(himl, hbmp, 0);
+ ok(r == 0, "should be zero\n");
+
+ hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_OWNERDRAWFIXED,
+ 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
+ ok(hwnd != NULL, "failed to create listview window\n");
+
+ r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, 0x940);
+ ok(r == 0, "should return zero\n");
+
+ r = SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)himl);
+ ok(r == 0, "should return zero\n");
+
+ r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELONG(100,50));
+ /* returns dimensions */
+
+ r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
+ ok(r == 0, "should be zero items\n");
+
+ item.mask = LVIF_IMAGE | LVIF_TEXT;
+ item.iItem = 0;
+ item.iSubItem = 1;
+ item.iImage = 0;
+ r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
+ ok(r == -1, "should fail\n");
+
+ item.iSubItem = 0;
+ item.pszText = hello;
+ r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
+ ok(r == 0, "should not fail\n");
+
+ memset(&r1, 0, sizeof r1);
+ r1.left = LVIR_ICON;
+ r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r1);
+
+ r = SendMessage(hwnd, LVM_DELETEALLITEMS, 0, 0);
+ ok(r == TRUE, "should not fail\n");
+
+ item.iSubItem = 0;
+ item.pszText = hello;
+ r = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM) &item);
+ ok(r == 0, "should not fail\n");
+
+ memset(&r2, 0, sizeof r2);
+ r2.left = LVIR_ICON;
+ r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM) &r2);
+
+ ok(!memcmp(&r1, &r2, sizeof r1), "rectangle should be the same\n");
+
+ DestroyWindow(hwnd);
+}
+
+static void test_checkboxes(void)
+{
+ HWND hwnd, hwndparent = 0;
+ LVITEMA item;
+ DWORD r;
+ static CHAR text[] = "Text",
+ text2[] = "Text2",
+ text3[] = "Text3";
+
+ hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
+ 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
+ ok(hwnd != NULL, "failed to create listview window\n");
+
+ /* first without LVS_EX_CHECKBOXES set and an item and check that state is preserved */
+ item.mask = LVIF_TEXT | LVIF_STATE;
+ item.stateMask = 0xffff;
+ item.state = 0xfccc;
+ item.iItem = 0;
+ item.iSubItem = 0;
+ item.pszText = text;
+ r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
+ ok(r == 0, "ret %ld\n", r);
+
+ item.iItem = 0;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0xfccc, "state %x\n", item.state);
+
+ /* Don't set LVIF_STATE */
+ item.mask = LVIF_TEXT;
+ item.stateMask = 0xffff;
+ item.state = 0xfccc;
+ item.iItem = 1;
+ item.iSubItem = 0;
+ item.pszText = text;
+ r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
+ ok(r == 1, "ret %ld\n", r);
+
+ item.iItem = 1;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0, "state %x\n", item.state);
+
+ r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
+ ok(r == 0, "should return zero\n");
+
+ /* Having turned on checkboxes, check that all existing items are set to 0x1000 (unchecked) */
+ item.iItem = 0;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0x1ccc, "state %x\n", item.state);
+
+ /* Now add an item without specifying a state and check that it's state goes to 0x1000 */
+ item.iItem = 2;
+ item.mask = LVIF_TEXT;
+ item.state = 0;
+ item.pszText = text2;
+ r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
+ ok(r == 2, "ret %ld\n", r);
+
+ item.iItem = 2;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0x1000, "state %x\n", item.state);
+
+ /* Add a further item this time specifying a state and still it's state goes to 0x1000 */
+ item.iItem = 3;
+ item.mask = LVIF_TEXT | LVIF_STATE;
+ item.stateMask = 0xffff;
+ item.state = 0x2aaa;
+ item.pszText = text3;
+ r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
+ ok(r == 3, "ret %ld\n", r);
+
+ item.iItem = 3;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0x1aaa, "state %x\n", item.state);
+
+ /* Set an item's state to checked */
+ item.iItem = 3;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xf000;
+ item.state = 0x2000;
+ r = SendMessage(hwnd, LVM_SETITEMA, 0, (LPARAM) &item);
+
+ item.iItem = 3;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0x2aaa, "state %x\n", item.state);
+
+ /* Set the style again and check that doesn't change an item's state */
+ r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
+ ok(r == LVS_EX_CHECKBOXES, "ret %lx\n", r);
+
+ item.iItem = 3;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0x2aaa, "state %x\n", item.state);
+
+ /* Unsetting the checkbox extended style doesn't change an item's state */
+ r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, 0);
+ ok(r == LVS_EX_CHECKBOXES, "ret %lx\n", r);
+
+ item.iItem = 3;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0x2aaa, "state %x\n", item.state);
+
+ /* Now setting the style again will change an item's state */
+ r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES);
+ ok(r == 0, "ret %lx\n", r);
+
+ item.iItem = 3;
+ item.mask = LVIF_STATE;
+ item.stateMask = 0xffff;
+ r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
+ ok(item.state == 0x1aaa, "state %x\n", item.state);
+
+ DestroyWindow(hwnd);
+}
+
+START_TEST(listview)
+{
+ INITCOMMONCONTROLSEX icc;
+
+ icc.dwICC = 0;
+ icc.dwSize = sizeof icc;
+ InitCommonControlsEx(&icc);
+
+ test_images();
+ test_checkboxes();
+}
--- /dev/null
+/*
+ * comctl32 month calendar unit tests
+ *
+ * Copyright (C) 2006 Vitaliy Margolen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "windows.h"
+
+#include "commctrl.h"
+
+#include "wine/test.h"
+
+void test_monthcal(void)
+{
+ HWND hwnd;
+ SYSTEMTIME st[2], st1[2];
+ INITCOMMONCONTROLSEX ic = {sizeof(INITCOMMONCONTROLSEX), ICC_DATE_CLASSES};
+ int res, month_range;
+
+ InitCommonControlsEx(&ic);
+ hwnd = CreateWindowA(MONTHCAL_CLASSA, "MonthCal", WS_POPUP | WS_VISIBLE, CW_USEDEFAULT,
+ 0, 300, 300, 0, 0, NULL, NULL);
+ ok(hwnd != NULL, "Failed to create MonthCal\n");
+ GetSystemTime(&st[0]);
+ st[1] = st[0];
+
+ /* Invalid date/time */
+ st[0].wYear = 2000;
+ /* Time should not matter */
+ st[1].wHour = st[1].wMinute = st[1].wSecond = 70;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set MAX limit\n");
+ ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "No limits should be set\n");
+ ok(st1[0].wYear != 2000, "Lover limit changed\n");
+
+ st[1].wMonth = 0;
+ ok(!SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st), "Should have failed to set limits\n");
+ ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "No limits should be set\n");
+ ok(st1[0].wYear != 2000, "Lover limit changed\n");
+ ok(!SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Should have failed to set MAX limit\n");
+ ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "No limits should be set\n");
+ ok(st1[0].wYear != 2000, "Lover limit changed\n");
+
+ GetSystemTime(&st[0]);
+ st[0].wDay = 20;
+ st[0].wMonth = 5;
+ st[1] = st[0];
+
+ month_range = SendMessage(hwnd, MCM_GETMONTHRANGE, GMR_VISIBLE, (LPARAM)st1);
+ st[1].wMonth--;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st), "Failed to set both min and max limits\n");
+ res = SendMessage(hwnd, MCM_GETMONTHRANGE, GMR_VISIBLE, (LPARAM)st1);
+ ok(res == month_range, "Invalid month range (%d)\n", res);
+ ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == (GDTR_MIN|GDTR_MAX), "Limits should be set\n");
+
+ st[1].wMonth += 2;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st), "Failed to set both min and max limits\n");
+ res = SendMessage(hwnd, MCM_GETMONTHRANGE, GMR_VISIBLE, (LPARAM)st1);
+ ok(res == month_range, "Invalid month range (%d)\n", res);
+
+ st[1].wYear --;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st), "Failed to set both min and max limits\n");
+ st[1].wYear += 1;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st), "Failed to set both min and max limits\n");
+
+ st[1].wMonth -= 3;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set max limit\n");
+ ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "Only MAX limit should be set\n");
+ st[1].wMonth += 4;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set max limit\n");
+ st[1].wYear -= 3;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set max limit\n");
+ st[1].wYear += 4;
+ ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set max limit\n");
+ ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "Only MAX limit should be set\n");
+}
+
+START_TEST(monthcal)
+{
+ test_monthcal();
+}
--- /dev/null
+/*
+ * comctl32 MRU unit tests
+ *
+ * Copyright (C) 2004 Jon Griffiths <jon_p_griffiths@yahoo.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "commctrl.h"
+#include "shlwapi.h"
+
+#include "wine/test.h"
+
+/* Keys for testing MRU functions */
+#define REG_TEST_BASEKEYA "Software\\Wine"
+#define REG_TEST_BASESUBKEYA "Test"
+#define REG_TEST_KEYA REG_TEST_BASEKEYA "\\" REG_TEST_BASESUBKEYA
+#define REG_TEST_SUBKEYA "MRUTest"
+#define REG_TEST_FULLKEY REG_TEST_KEYA "\\" REG_TEST_SUBKEYA
+
+/* Undocumented MRU structures & functions */
+typedef struct tagCREATEMRULISTA
+{
+ DWORD cbSize;
+ DWORD nMaxItems;
+ DWORD dwFlags;
+ HKEY hKey;
+ LPCSTR lpszSubKey;
+ PROC lpfnCompare;
+} CREATEMRULISTA, *LPCREATEMRULISTA;
+
+#define MRUF_STRING_LIST 0
+#define MRUF_BINARY_LIST 1
+#define MRUF_DELAYED_SAVE 2
+
+#define LIST_SIZE 3 /* Max entries for each mru */
+
+static CREATEMRULISTA mruA =
+{
+ sizeof(CREATEMRULISTA),
+ LIST_SIZE,
+ 0,
+ NULL,
+ REG_TEST_SUBKEYA,
+ NULL
+};
+
+static HMODULE hComctl32;
+static HANDLE (WINAPI *pCreateMRUListA)(LPCREATEMRULISTA);
+static void (WINAPI *pFreeMRUList)(HANDLE);
+static INT (WINAPI *pAddMRUStringA)(HANDLE,LPCSTR);
+/*
+static INT (WINAPI *pFindMRUStringA)(HANDLE,LPCSTR,LPINT);
+static INT (WINAPI *pEnumMRUList)(HANDLE,INT,LPVOID,DWORD);
+*/
+
+static BOOL create_reg_entries(void)
+{
+ HKEY hKey = NULL;
+
+ ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
+ "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
+ if (!hKey) return FALSE;
+ RegCloseKey(hKey);
+ return TRUE;
+}
+
+static void delete_reg_entries(void)
+{
+ HKEY hKey;
+
+ if (RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_BASEKEYA, 0, KEY_ALL_ACCESS,
+ &hKey))
+ return;
+ SHDeleteKeyA(hKey, REG_TEST_BASESUBKEYA);
+ RegCloseKey(hKey);
+}
+
+static void check_reg_entries(const char *mrulist, const char**items)
+{
+ char buff[128];
+ HKEY hKey = NULL;
+ DWORD type, size, ret;
+ unsigned int i;
+
+ ok(!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
+ "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY);
+ if (!hKey) return;
+
+ type = REG_SZ;
+ size = sizeof(buff);
+ buff[0] = '\0';
+ ret = RegQueryValueExA(hKey, "MRUList", NULL, &type, (LPBYTE)buff, &size);
+
+ ok(!ret && buff[0], "Checking MRU: got %ld from RegQueryValueExW\n", ret);
+ if(ret || !buff[0]) return;
+
+ ok(strcmp(buff, mrulist) == 0, "Checking MRU: Expected list %s, got %s\n",
+ mrulist, buff);
+ if(strcmp(buff, mrulist)) return;
+
+ for (i = 0; i < strlen(mrulist); i++)
+ {
+ char name[2];
+ name[0] = mrulist[i];
+ name[1] = '\0';
+ type = REG_SZ;
+ size = sizeof(buff);
+ buff[0] = '\0';
+ ret = RegQueryValueExA(hKey, name, NULL, &type, (LPBYTE)buff, &size);
+ ok(!ret && buff[0],
+ "Checking MRU item %d ('%c'): got %ld from RegQueryValueExW\n",
+ i, mrulist[i], ret);
+ if(ret || !buff[0]) return;
+ ok(!strcmp(buff, items[mrulist[i]-'a']),
+ "Checking MRU item %d ('%c'): expected \"%s\", got \"%s\"\n",
+ i, mrulist[i], buff, items[mrulist[i] - 'a']);
+ }
+}
+
+static INT CALLBACK cmp_mru_strA(LPCVOID data1, LPCVOID data2)
+{
+ return lstrcmpiA(data1, data2);
+}
+
+static HANDLE create_mruA(HKEY hKey, DWORD flags, PROC cmp)
+{
+ mruA.dwFlags = flags;
+ mruA.lpfnCompare = cmp;
+ mruA.hKey = hKey;
+
+ SetLastError(0);
+ return pCreateMRUListA(&mruA);
+}
+
+static void test_MRUListA(void)
+{
+ const char *checks[LIST_SIZE+1];
+ HANDLE hMRU;
+ HKEY hKey;
+ INT iRet;
+
+ pCreateMRUListA = (void*)GetProcAddress(hComctl32,(LPCSTR)151);
+ pFreeMRUList = (void*)GetProcAddress(hComctl32,(LPCSTR)152);
+ pAddMRUStringA = (void*)GetProcAddress(hComctl32,(LPCSTR)153);
+ if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA)
+ return;
+
+#if 0 /* Create (NULL) - crashes native */
+ hMRU = pCreateMRUListA(NULL);
+#endif
+
+ /* Create (size too small) */
+ mruA.cbSize = sizeof(mruA) - 2;
+ hMRU = create_mruA(NULL, MRUF_STRING_LIST, cmp_mru_strA);
+ ok (!hMRU && !GetLastError(),
+ "CreateMRUListA(too small) expected NULL,0 got %p,%ld\n",
+ hMRU, GetLastError());
+ mruA.cbSize = sizeof(mruA);
+
+ /* Create (size too big) */
+ mruA.cbSize = sizeof(mruA) + 2;
+ hMRU = create_mruA(NULL, MRUF_STRING_LIST, cmp_mru_strA);
+ ok (!hMRU && !GetLastError(),
+ "CreateMRUListA(too big) expected NULL,0 got %p,%ld\n",
+ hMRU, GetLastError());
+ mruA.cbSize = sizeof(mruA);
+
+ /* Create (NULL hKey) */
+ hMRU = create_mruA(NULL, MRUF_STRING_LIST, cmp_mru_strA);
+ ok (!hMRU && !GetLastError(),
+ "CreateMRUListA(NULL key) expected NULL,0 got %p,%ld\n",
+ hMRU, GetLastError());
+
+ /* Create (NULL name) */
+ mruA.lpszSubKey = NULL;
+ hMRU = create_mruA(NULL, MRUF_STRING_LIST, cmp_mru_strA);
+ ok (!hMRU && !GetLastError(),
+ "CreateMRUListA(NULL name) expected NULL,0 got %p,%ld\n",
+ hMRU, GetLastError());
+ mruA.lpszSubKey = REG_TEST_SUBKEYA;
+
+ /* Create a string MRU */
+ ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
+ "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
+ if (!hKey)
+ return;
+ hMRU = create_mruA(hKey, MRUF_STRING_LIST, cmp_mru_strA);
+ ok(hMRU && !GetLastError(),
+ "CreateMRUListA(string) expected non-NULL,0 got %p,%ld\n",
+ hMRU, GetLastError());
+
+ if (hMRU)
+ {
+ checks[0] = "Test 1";
+ checks[1] = "Test 2";
+ checks[2] = "Test 3";
+ checks[3] = "Test 4";
+
+ /* Add (NULL list) */
+ SetLastError(0);
+ iRet = pAddMRUStringA(NULL, checks[0]);
+ ok(iRet == -1 && !GetLastError(),
+ "AddMRUStringA(NULL list) expected -1,0 got %d,%ld\n",
+ iRet, GetLastError());
+
+ /* Add (NULL string) */
+#if 0
+ /* Some native versions crash when passed NULL or fail to SetLastError() */
+ SetLastError(0);
+ iRet = pAddMRUStringA(hMRU, NULL);
+ ok(iRet == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
+ "AddMRUStringA(NULL str) expected 0,ERROR_INVALID_PARAMETER got %d,%ld\n",
+ iRet, GetLastError());
+#endif
+
+ /* Add 3 strings. Check the registry is correct after each add */
+ SetLastError(0);
+ iRet = pAddMRUStringA(hMRU, checks[0]);
+ ok(iRet == 0 && !GetLastError(),
+ "AddMRUStringA(1) expected 0,0 got %d,%ld\n",
+ iRet, GetLastError());
+ check_reg_entries("a", checks);
+
+ SetLastError(0);
+ iRet = pAddMRUStringA(hMRU, checks[1]);
+ ok(iRet == 1 && !GetLastError(),
+ "AddMRUStringA(2) expected 1,0 got %d,%ld\n",
+ iRet, GetLastError());
+ check_reg_entries("ba", checks);
+
+ SetLastError(0);
+ iRet = pAddMRUStringA(hMRU, checks[2]);
+ ok(iRet == 2 && !GetLastError(),
+ "AddMRUStringA(2) expected 2,0 got %d,%ld\n",
+ iRet, GetLastError());
+ check_reg_entries("cba", checks);
+
+ /* Add a duplicate of the 2nd string - it should move to the front,
+ * but keep the same index in the registry.
+ */
+ SetLastError(0);
+ iRet = pAddMRUStringA(hMRU, checks[1]);
+ ok(iRet == 1 && !GetLastError(),
+ "AddMRUStringA(re-add 1) expected 1,0 got %d,%ld\n",
+ iRet, GetLastError());
+ check_reg_entries("bca", checks);
+
+ /* Add a new string - replaces the oldest string + moves to the front */
+ SetLastError(0);
+ iRet = pAddMRUStringA(hMRU, checks[3]);
+ ok(iRet == 0 && !GetLastError(),
+ "AddMRUStringA(add new) expected 0,0 got %d,%ld\n",
+ iRet, GetLastError());
+ checks[0] = checks[3];
+ check_reg_entries("abc", checks);
+
+ /* Finished with this MRU */
+ pFreeMRUList(hMRU);
+ }
+
+ /* Free (NULL list) - Doesn't crash */
+ pFreeMRUList(NULL);
+}
+
+START_TEST(mru)
+{
+ hComctl32 = GetModuleHandleA("comctl32.dll");
+ if (!hComctl32)
+ return;
+
+ delete_reg_entries();
+ if (!create_reg_entries())
+ return;
+
+ test_MRUListA();
+
+ delete_reg_entries();
+}
--- /dev/null
+/* Unit tests for the progress bar control.
+ *
+ * Copyright 2005 Michael Kaufmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "commctrl.h"
+
+#include "wine/test.h"
+
+
+HWND hProgressParentWnd, hProgressWnd;
+static const char progressTestClass[] = "ProgressBarTestClass";
+
+
+LRESULT CALLBACK ProgressTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProcA(hWnd, msg, wParam, lParam);
+ }
+
+ return 0L;
+}
+
+static WNDPROC progress_wndproc;
+static BOOL erased;
+static RECT last_paint_rect;
+
+LRESULT CALLBACK ProgressSubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ if (msg == WM_PAINT)
+ {
+ GetUpdateRect(hWnd, &last_paint_rect, FALSE);
+ }
+ else if (msg == WM_ERASEBKGND)
+ {
+ erased = TRUE;
+ }
+ return CallWindowProc(progress_wndproc, hWnd, msg, wParam, lParam);
+}
+
+
+static void update_window(HWND hWnd)
+{
+ UpdateWindow(hWnd);
+ ok(!GetUpdateRect(hWnd, NULL, FALSE), "GetUpdateRect must return zero after UpdateWindow\n");
+}
+
+
+static void init(void)
+{
+ WNDCLASSA wc;
+ INITCOMMONCONTROLSEX icex;
+ RECT rect;
+
+ icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ icex.dwICC = ICC_PROGRESS_CLASS;
+ InitCommonControlsEx(&icex);
+
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandleA(NULL);
+ wc.hIcon = NULL;
+ wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_ARROW));
+ wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = progressTestClass;
+ wc.lpfnWndProc = ProgressTestWndProc;
+ RegisterClassA(&wc);
+
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = 400;
+ rect.bottom = 20;
+ assert(AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE));
+
+ hProgressParentWnd = CreateWindowExA(0, progressTestClass, "Progress Bar Test", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, GetModuleHandleA(NULL), 0);
+ assert(hProgressParentWnd != NULL);
+
+ GetClientRect(hProgressParentWnd, &rect);
+ hProgressWnd = CreateWindowEx(0, PROGRESS_CLASS, "", WS_CHILD | WS_VISIBLE,
+ 0, 0, rect.right, rect.bottom, hProgressParentWnd, NULL, GetModuleHandleA(NULL), 0);
+ assert(hProgressWnd != NULL);
+ progress_wndproc = (WNDPROC)SetWindowLongPtr(hProgressWnd, GWLP_WNDPROC, (LPARAM)ProgressSubclassProc);
+
+ ShowWindow(hProgressParentWnd, SW_SHOWNORMAL);
+ ok(GetUpdateRect(hProgressParentWnd, NULL, FALSE), "GetUpdateRect: There should be a region that needs to be updated\n");
+ update_window(hProgressParentWnd);
+}
+
+
+static void cleanup(void)
+{
+ MSG msg;
+
+ PostMessageA(hProgressParentWnd, WM_CLOSE, 0, 0);
+ while (GetMessageA(&msg,0,0,0)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+
+ UnregisterClassA(progressTestClass, GetModuleHandleA(NULL));
+}
+
+
+/*
+ * Tests if a progress bar repaints itself immediately when it receives
+ * some specific messages.
+ */
+static void test_redraw(void)
+{
+ RECT client_rect;
+
+ SendMessageA(hProgressWnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
+ SendMessageA(hProgressWnd, PBM_SETPOS, 10, 0);
+ SendMessageA(hProgressWnd, PBM_SETSTEP, 20, 0);
+ update_window(hProgressWnd);
+
+ /* PBM_SETPOS */
+ ok(SendMessageA(hProgressWnd, PBM_SETPOS, 50, 0) == 10, "PBM_SETPOS must return the previous position\n");
+ ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_SETPOS: The progress bar should be redrawn immediately\n");
+
+ /* PBM_DELTAPOS */
+ ok(SendMessageA(hProgressWnd, PBM_DELTAPOS, 15, 0) == 50, "PBM_DELTAPOS must return the previous position\n");
+ ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_DELTAPOS: The progress bar should be redrawn immediately\n");
+
+ /* PBM_SETPOS */
+ ok(SendMessageA(hProgressWnd, PBM_SETPOS, 80, 0) == 65, "PBM_SETPOS must return the previous position\n");
+ ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_SETPOS: The progress bar should be redrawn immediately\n");
+
+ /* PBM_STEPIT */
+ ok(SendMessageA(hProgressWnd, PBM_STEPIT, 0, 0) == 80, "PBM_STEPIT must return the previous position\n");
+ ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_STEPIT: The progress bar should be redrawn immediately\n");
+ ok((UINT)SendMessageA(hProgressWnd, PBM_GETPOS, 0, 0) == 100, "PBM_GETPOS returned a wrong position\n");
+
+ /* PBM_SETRANGE and PBM_SETRANGE32:
+ Usually the progress bar doesn't repaint itself immediately. If the
+ position is not in the new range, it does.
+ Don't test this, it may change in future Windows versions. */
+
+ SendMessage(hProgressWnd, PBM_SETPOS, 0, 0);
+ update_window(hProgressWnd);
+
+ /* increase to 10 - no background erase required */
+ erased = FALSE;
+ SetRectEmpty(&last_paint_rect);
+ SendMessage(hProgressWnd, PBM_SETPOS, 10, 0);
+ GetClientRect(hProgressWnd, &client_rect);
+ ok(EqualRect(&last_paint_rect, &client_rect),
+ "last_paint_rect was { %ld, %ld, %ld, %ld } instead of { %ld, %ld, %ld, %ld }\n",
+ last_paint_rect.left, last_paint_rect.top, last_paint_rect.right, last_paint_rect.bottom,
+ client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+ update_window(hProgressWnd);
+ ok(!erased, "Progress bar shouldn't have erased the background\n");
+
+ /* decrease to 0 - background erase will be required */
+ erased = FALSE;
+ SetRectEmpty(&last_paint_rect);
+ SendMessage(hProgressWnd, PBM_SETPOS, 0, 0);
+ GetClientRect(hProgressWnd, &client_rect);
+ ok(EqualRect(&last_paint_rect, &client_rect),
+ "last_paint_rect was { %ld, %ld, %ld, %ld } instead of { %ld, %ld, %ld, %ld }\n",
+ last_paint_rect.left, last_paint_rect.top, last_paint_rect.right, last_paint_rect.bottom,
+ client_rect.left, client_rect.top, client_rect.right, client_rect.bottom);
+ update_window(hProgressWnd);
+ ok(erased, "Progress bar should have erased the background\n");
+}
+
+
+START_TEST(progress)
+{
+ init();
+
+ test_redraw();
+
+ cleanup();
+}
--- /dev/null
+/* Unit test suite for property sheet control.
+ *
+ * Copyright 2006 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "wine/test.h"
+
+static int CALLBACK sheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
+{
+ switch(msg)
+ {
+ case PSCB_INITIALIZED:
+ {
+ char caption[256];
+ GetWindowTextA(hwnd, caption, sizeof(caption));
+ ok(!strcmp(caption,"test caption"), "caption: %s\n", caption);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static INT_PTR CALLBACK page_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
+ LPARAM lparam)
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ HWND sheet = GetParent(hwnd);
+ char caption[256];
+ GetWindowTextA(sheet, caption, sizeof(caption));
+ ok(!strcmp(caption,"test caption"), "caption: %s\n", caption);
+ return TRUE;
+ }
+
+ case WM_NOTIFY:
+ {
+ NMHDR *nmhdr = (NMHDR *)lparam;
+ switch(nmhdr->code)
+ {
+ case PSN_APPLY:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+ default:
+ return FALSE;
+ }
+}
+
+static void test_title(void)
+{
+ HPROPSHEETPAGE hpsp[1];
+ PROPSHEETPAGEA psp;
+ PROPSHEETHEADERA psh;
+ HWND hdlg;
+
+ memset(&psp, 0, sizeof(psp));
+ psp.dwSize = sizeof(psp);
+ psp.dwFlags = 0;
+ psp.hInstance = GetModuleHandleW(NULL);
+ psp.u.pszTemplate = "prop_page1";
+ psp.u2.pszIcon = NULL;
+ psp.pfnDlgProc = page_dlg_proc;
+ psp.lParam = 0;
+
+ hpsp[0] = CreatePropertySheetPageA(&psp);
+
+ memset(&psh, 0, sizeof(psh));
+ psh.dwSize = sizeof(psh);
+ psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
+ psh.pszCaption = "test caption";
+ psh.nPages = 1;
+ psh.hwndParent = GetDesktopWindow();
+ psh.u3.phpage = hpsp;
+ psh.pfnCallback = sheet_callback;
+
+ hdlg = (HWND)PropertySheetA(&psh);
+ DestroyWindow(hdlg);
+}
+
+START_TEST(propsheet)
+{
+ test_title();
+}
--- /dev/null
+/* Resources for the unit test suite for property sheet control.
+ *
+ * Copyright 2006 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windef.h"
+#include "winuser.h"
+
+PROP_PAGE1 DIALOG LOADONCALL MOVEABLE DISCARDABLE 5, 43, 227, 215
+STYLE WS_POPUP | WS_CAPTION | WS_CLIPSIBLINGS | WS_VISIBLE
+CAPTION "Page1"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Test", -1, 10, 6, 100, 8
+}
--- /dev/null
+/* Unit tests for subclassed windows.
+ *
+ * Copyright 2004 Kevin Koltzau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#define _WIN32_WINNT 0x0501 /* For SetWindowSubclass/etc */
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "commctrl.h"
+
+#include "wine/test.h"
+
+static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
+static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
+static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
+
+#define SEND_NEST 0x01
+#define DELETE_SELF 0x02
+#define DELETE_PREV 0x04
+
+struct message {
+ int procnum; /* WndProc id message is expected from */
+ WPARAM wParam; /* expected value of wParam */
+};
+
+static int sequence_cnt, sequence_size;
+static struct message* sequence;
+
+static const struct message Sub_BasicTest[] = {
+ { 2, 1 },
+ { 1, 1 },
+ { 2, 2 },
+ { 1, 2 },
+ { 0 }
+};
+
+static const struct message Sub_DeletedTest[] = {
+ { 2, 1 },
+ { 1, 1 },
+ { 0 }
+};
+
+static const struct message Sub_AfterDeletedTest[] = {
+ { 1, 1 },
+ { 0 }
+};
+
+static const struct message Sub_OldAfterNewTest[] = {
+ { 3, 1 },
+ { 2, 1 },
+ { 1, 1 },
+ { 3, 2 },
+ { 2, 2 },
+ { 1, 2 },
+ { 0 }
+};
+
+static const struct message Sub_MixTest[] = {
+ { 3, 1 },
+ { 4, 1 },
+ { 2, 1 },
+ { 1, 1 },
+ { 0 }
+};
+
+static const struct message Sub_MixAndNestTest[] = {
+ { 3, 1 },
+ { 4, 1 },
+ { 3, 2 },
+ { 4, 2 },
+ { 2, 2 },
+ { 1, 2 },
+ { 2, 1 },
+ { 1, 1 },
+ { 0 }
+};
+
+static const struct message Sub_MixNestDelTest[] = {
+ { 3, 1 },
+ { 4, 1 },
+ { 3, 2 },
+ { 2, 2 },
+ { 1, 2 },
+ { 2, 1 },
+ { 1, 1 },
+ { 0 }
+};
+
+static const struct message Sub_MixDelPrevTest[] = {
+ { 3, 1 },
+ { 5, 1 },
+ { 2, 1 },
+ { 1, 1 },
+ { 0 }
+};
+
+static void add_message(const struct message *msg)
+{
+ if (!sequence)
+ {
+ sequence_size = 10;
+ sequence = HeapAlloc( GetProcessHeap(), 0, sequence_size * sizeof (struct message) );
+ }
+ if (sequence_cnt == sequence_size)
+ {
+ sequence_size *= 2;
+ sequence = HeapReAlloc( GetProcessHeap(), 0, sequence, sequence_size * sizeof (struct message) );
+ }
+ assert(sequence);
+
+ sequence[sequence_cnt].wParam = msg->wParam;
+ sequence[sequence_cnt].procnum = msg->procnum;
+
+ sequence_cnt++;
+}
+
+static void flush_sequence(void)
+{
+ HeapFree(GetProcessHeap(), 0, sequence);
+ sequence = 0;
+ sequence_cnt = sequence_size = 0;
+}
+
+static void ok_sequence(const struct message *expected, const char *context)
+{
+ static const struct message end_of_sequence = { 0, 0 };
+ const struct message *actual;
+
+ add_message(&end_of_sequence);
+
+ actual = sequence;
+
+ while(expected->procnum && actual->procnum)
+ {
+ ok(expected->procnum == actual->procnum,
+ "%s: the procnum %d was expected, but got procnum %d instead\n",
+ context, expected->procnum, actual->procnum);
+ ok(expected->wParam == actual->wParam,
+ "%s: in procnum %d expecting wParam 0x%x got 0x%x\n",
+ context, expected->procnum, expected->wParam, actual->wParam);
+ expected++;
+ actual++;
+ }
+ ok(!expected->procnum, "Received fewer messages than expected\n");
+ ok(!actual->procnum, "Received more messages than expected\n");
+ flush_sequence();
+}
+
+static LRESULT WINAPI WndProc1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ struct message msg;
+
+ if(message == WM_USER) {
+ msg.wParam = wParam;
+ msg.procnum = 1;
+ add_message(&msg);
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+static WNDPROC origProc3;
+static LRESULT WINAPI WndProc3(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ struct message msg;
+
+ if(message == WM_USER) {
+ msg.wParam = wParam;
+ msg.procnum = 3;
+ add_message(&msg);
+ }
+ return CallWindowProc(origProc3, hwnd, message, wParam, lParam);
+}
+
+static LRESULT WINAPI WndProcSub(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uldSubclass, DWORD_PTR dwRefData)
+{
+ struct message msg;
+
+ if(message == WM_USER) {
+ msg.wParam = wParam;
+ msg.procnum = uldSubclass;
+ add_message(&msg);
+
+ if(lParam) {
+ if(dwRefData & DELETE_SELF) {
+ pRemoveWindowSubclass(hwnd, WndProcSub, uldSubclass);
+ pRemoveWindowSubclass(hwnd, WndProcSub, uldSubclass);
+ }
+ if(dwRefData & DELETE_PREV)
+ pRemoveWindowSubclass(hwnd, WndProcSub, uldSubclass-1);
+ if(dwRefData & SEND_NEST)
+ SendMessage(hwnd, WM_USER, wParam+1, 0);
+ }
+ }
+ return pDefSubclassProc(hwnd, message, wParam, lParam);
+}
+
+static void test_subclass(void)
+{
+ HWND hwnd = CreateWindowExA(0, "TestSubclass", "Test subclass", WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ assert(hwnd);
+
+ pSetWindowSubclass(hwnd, WndProcSub, 2, 0);
+ SendMessage(hwnd, WM_USER, 1, 0);
+ SendMessage(hwnd, WM_USER, 2, 0);
+ ok_sequence(Sub_BasicTest, "Basic");
+
+ pSetWindowSubclass(hwnd, WndProcSub, 2, DELETE_SELF);
+ SendMessage(hwnd, WM_USER, 1, 1);
+ ok_sequence(Sub_DeletedTest, "Deleted");
+
+ SendMessage(hwnd, WM_USER, 1, 0);
+ ok_sequence(Sub_AfterDeletedTest, "After Deleted");
+
+ pSetWindowSubclass(hwnd, WndProcSub, 2, 0);
+ origProc3 = (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG)WndProc3);
+ SendMessage(hwnd, WM_USER, 1, 0);
+ SendMessage(hwnd, WM_USER, 2, 0);
+ ok_sequence(Sub_OldAfterNewTest, "Old after New");
+
+ pSetWindowSubclass(hwnd, WndProcSub, 4, 0);
+ SendMessage(hwnd, WM_USER, 1, 0);
+ ok_sequence(Sub_MixTest, "Mix");
+
+ /* Now the fun starts */
+ pSetWindowSubclass(hwnd, WndProcSub, 4, SEND_NEST);
+ SendMessage(hwnd, WM_USER, 1, 1);
+ ok_sequence(Sub_MixAndNestTest, "Mix and nest");
+
+ pSetWindowSubclass(hwnd, WndProcSub, 4, SEND_NEST | DELETE_SELF);
+ SendMessage(hwnd, WM_USER, 1, 1);
+ ok_sequence(Sub_MixNestDelTest, "Mix, nest, del");
+
+ pSetWindowSubclass(hwnd, WndProcSub, 4, 0);
+ pSetWindowSubclass(hwnd, WndProcSub, 5, DELETE_PREV);
+ SendMessage(hwnd, WM_USER, 1, 1);
+ ok_sequence(Sub_MixDelPrevTest, "Mix and del prev");
+
+ DestroyWindow(hwnd);
+}
+
+static BOOL RegisterWindowClasses(void)
+{
+ WNDCLASSA cls;
+
+ cls.style = 0;
+ cls.lpfnWndProc = WndProc1;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = NULL;
+ cls.hbrBackground = NULL;
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "TestSubclass";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ return TRUE;
+}
+
+START_TEST(subclass)
+{
+ HMODULE hdll;
+
+ hdll = GetModuleHandleA("comctl32.dll");
+ assert(hdll);
+ pSetWindowSubclass = (void*)GetProcAddress(hdll, "SetWindowSubclass");
+ pRemoveWindowSubclass = (void*)GetProcAddress(hdll, "RemoveWindowSubclass");
+ pDefSubclassProc = (void*)GetProcAddress(hdll, "DefSubclassProc");
+
+ if(!pSetWindowSubclass || !pRemoveWindowSubclass || !pDefSubclassProc)
+ return;
+
+ if(!RegisterWindowClasses()) assert(0);
+
+ test_subclass();
+}
--- /dev/null
+/* Unit test suite for tab control.
+ *
+ * Copyright 2003 Vitaliy Margolen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <commctrl.h>
+
+#include "wine/test.h"
+
+#define DEFAULT_MIN_TAB_WIDTH 54
+#define TAB_DEFAULT_WIDTH 96
+#define TAB_PADDING_X 6
+#define EXTRA_ICON_PADDING 3
+
+#define TabWidthPadded(padd_x, num) (DEFAULT_MIN_TAB_WIDTH - (TAB_PADDING_X - (padd_x)) * num)
+
+#define TabCheckSetSize(hwnd, SetWidth, SetHeight, ExpWidth, ExpHeight, Msg)\
+ SendMessage (hwnd, TCM_SETITEMSIZE, 0,\
+ (LPARAM) MAKELPARAM((SetWidth >= 0) ? SetWidth:0, (SetHeight >= 0) ? SetHeight:0));\
+ if (winetest_interactive) RedrawWindow (hwnd, NULL, 0, RDW_UPDATENOW);\
+ CheckSize(hwnd, ExpWidth, ExpHeight, Msg);
+
+#define CheckSize(hwnd,width,height,msg)\
+ SendMessage (hwnd, TCM_GETITEMRECT, 0, (LPARAM) &rTab);\
+ if ((width >= 0) && (height < 0))\
+ ok (width == rTab.right - rTab.left, "%s: Expected width [%d] got [%ld]\n",\
+ msg, (int)width, rTab.right - rTab.left);\
+ else if ((height >= 0) && (width < 0))\
+ ok (height == rTab.bottom - rTab.top, "%s: Expected height [%d] got [%ld]\n",\
+ msg, (int)height, rTab.bottom - rTab.top);\
+ else\
+ ok ((width == rTab.right - rTab.left) &&\
+ (height == rTab.bottom - rTab.top ),\
+ "%s: Expected [%d,%d] got [%ld,%ld]\n", msg, (int)width, (int)height,\
+ rTab.right - rTab.left, rTab.bottom - rTab.top);
+
+static HFONT hFont = 0;
+
+static HWND
+create_tabcontrol (DWORD style, DWORD mask)
+{
+ HWND handle;
+ TCITEM tcNewTab;
+ static char text1[] = "Tab 1",
+ text2[] = "Wide Tab 2",
+ text3[] = "T 3";
+
+ handle = CreateWindow (
+ WC_TABCONTROLA,
+ "TestTab",
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | style,
+ 10, 10, 300, 100,
+ NULL, NULL, NULL, 0);
+
+ assert (handle);
+
+ SetWindowLong(handle, GWL_STYLE, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_FOCUSNEVER | style);
+ SendMessage (handle, WM_SETFONT, 0, (LPARAM) hFont);
+
+ tcNewTab.mask = mask;
+ tcNewTab.pszText = text1;
+ tcNewTab.iImage = 0;
+ SendMessage (handle, TCM_INSERTITEM, 0, (LPARAM) &tcNewTab);
+ tcNewTab.pszText = text2;
+ tcNewTab.iImage = 1;
+ SendMessage (handle, TCM_INSERTITEM, 1, (LPARAM) &tcNewTab);
+ tcNewTab.pszText = text3;
+ tcNewTab.iImage = 2;
+ SendMessage (handle, TCM_INSERTITEM, 2, (LPARAM) &tcNewTab);
+
+ if (winetest_interactive)
+ {
+ ShowWindow (handle, SW_SHOW);
+ RedrawWindow (handle, NULL, 0, RDW_UPDATENOW);
+ Sleep (1000);
+ }
+
+ return handle;
+}
+
+static void test_tab(INT nMinTabWidth)
+{
+ HWND hwTab;
+ RECT rTab;
+ HIMAGELIST himl = ImageList_Create(21, 21, ILC_COLOR, 3, 4);
+ SIZE size;
+ HDC hdc;
+ HFONT hOldFont;
+ INT i;
+
+ hwTab = create_tabcontrol(TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE);
+ SendMessage(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);
+
+ hdc = GetDC(hwTab);
+ hOldFont = SelectObject(hdc, (HFONT)SendMessage(hwTab, WM_GETFONT, 0, 0));
+ GetTextExtentPoint32A(hdc, "Tab 1", strlen("Tab 1"), &size);
+ trace("Tab1 text size: size.cx=%ld size.cy=%ld\n", size.cx, size.cy);
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(hwTab, hdc);
+
+ trace (" TCS_FIXEDWIDTH tabs no icon...\n");
+ CheckSize(hwTab, TAB_DEFAULT_WIDTH, -1, "default width");
+ TabCheckSetSize(hwTab, 50, 20, 50, 20, "set size");
+ TabCheckSetSize(hwTab, 0, 1, 0, 1, "min size");
+
+ SendMessage(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);
+
+ trace (" TCS_FIXEDWIDTH tabs with icon...\n");
+ TabCheckSetSize(hwTab, 50, 30, 50, 30, "set size > icon");
+ TabCheckSetSize(hwTab, 20, 20, 25, 20, "set size < icon");
+ TabCheckSetSize(hwTab, 0, 1, 25, 1, "min size");
+
+ DestroyWindow (hwTab);
+
+ hwTab = create_tabcontrol(TCS_FIXEDWIDTH | TCS_BUTTONS, TCIF_TEXT|TCIF_IMAGE);
+ SendMessage(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);
+
+ trace (" TCS_FIXEDWIDTH buttons no icon...\n");
+ CheckSize(hwTab, TAB_DEFAULT_WIDTH, -1, "default width");
+ TabCheckSetSize(hwTab, 20, 20, 20, 20, "set size 1");
+ TabCheckSetSize(hwTab, 10, 50, 10, 50, "set size 2");
+ TabCheckSetSize(hwTab, 0, 1, 0, 1, "min size");
+
+ SendMessage(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);
+
+ trace (" TCS_FIXEDWIDTH buttons with icon...\n");
+ TabCheckSetSize(hwTab, 50, 30, 50, 30, "set size > icon");
+ TabCheckSetSize(hwTab, 20, 20, 25, 20, "set size < icon");
+ TabCheckSetSize(hwTab, 0, 1, 25, 1, "min size");
+ SendMessage(hwTab, TCM_SETPADDING, 0, MAKELPARAM(4,4));
+ TabCheckSetSize(hwTab, 0, 1, 25, 1, "set padding, min size");
+
+ DestroyWindow (hwTab);
+
+ hwTab = create_tabcontrol(TCS_FIXEDWIDTH | TCS_BOTTOM, TCIF_TEXT|TCIF_IMAGE);
+ SendMessage(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);
+
+ trace (" TCS_FIXEDWIDTH | TCS_BOTTOM tabs...\n");
+ CheckSize(hwTab, TAB_DEFAULT_WIDTH, -1, "no icon, default width");
+
+ TabCheckSetSize(hwTab, 20, 20, 20, 20, "no icon, set size 1");
+ TabCheckSetSize(hwTab, 10, 50, 10, 50, "no icon, set size 2");
+ TabCheckSetSize(hwTab, 0, 1, 0, 1, "no icon, min size");
+
+ SendMessage(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);
+
+ TabCheckSetSize(hwTab, 50, 30, 50, 30, "with icon, set size > icon");
+ TabCheckSetSize(hwTab, 20, 20, 25, 20, "with icon, set size < icon");
+ TabCheckSetSize(hwTab, 0, 1, 25, 1, "with icon, min size");
+ SendMessage(hwTab, TCM_SETPADDING, 0, MAKELPARAM(4,4));
+ TabCheckSetSize(hwTab, 0, 1, 25, 1, "set padding, min size");
+
+ DestroyWindow (hwTab);
+
+ hwTab = create_tabcontrol(0, TCIF_TEXT|TCIF_IMAGE);
+ SendMessage(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);
+
+ trace (" non fixed width, with text...\n");
+ CheckSize(hwTab, max(size.cx +TAB_PADDING_X*2, (nMinTabWidth < 0) ? DEFAULT_MIN_TAB_WIDTH : nMinTabWidth), -1,
+ "no icon, default width");
+ for (i=0; i<8; i++)
+ {
+ INT nTabWidth = (nMinTabWidth < 0) ? TabWidthPadded(i, 2) : nMinTabWidth;
+
+ SendMessage(hwTab, TCM_SETIMAGELIST, 0, 0);
+ SendMessage(hwTab, TCM_SETPADDING, 0, MAKELPARAM(i,i));
+
+ TabCheckSetSize(hwTab, 50, 20, max(size.cx + i*2, nTabWidth), 20, "no icon, set size");
+ TabCheckSetSize(hwTab, 0, 1, max(size.cx + i*2, nTabWidth), 1, "no icon, min size");
+
+ SendMessage(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);
+ nTabWidth = (nMinTabWidth < 0) ? TabWidthPadded(i, 3) : nMinTabWidth;
+
+ TabCheckSetSize(hwTab, 50, 30, max(size.cx + 21 + i*3, nTabWidth), 30, "with icon, set size > icon");
+ TabCheckSetSize(hwTab, 20, 20, max(size.cx + 21 + i*3, nTabWidth), 20, "with icon, set size < icon");
+ TabCheckSetSize(hwTab, 0, 1, max(size.cx + 21 + i*3, nTabWidth), 1, "with icon, min size");
+ }
+ DestroyWindow (hwTab);
+
+ hwTab = create_tabcontrol(0, TCIF_IMAGE);
+ SendMessage(hwTab, TCM_SETMINTABWIDTH, 0, nMinTabWidth);
+
+ trace (" non fixed width, no text...\n");
+ CheckSize(hwTab, (nMinTabWidth < 0) ? DEFAULT_MIN_TAB_WIDTH : nMinTabWidth, -1, "no icon, default width");
+ for (i=0; i<8; i++)
+ {
+ INT nTabWidth = (nMinTabWidth < 0) ? TabWidthPadded(i, 2) : nMinTabWidth;
+
+ SendMessage(hwTab, TCM_SETIMAGELIST, 0, 0);
+ SendMessage(hwTab, TCM_SETPADDING, 0, MAKELPARAM(i,i));
+
+ TabCheckSetSize(hwTab, 50, 20, nTabWidth, 20, "no icon, set size");
+ TabCheckSetSize(hwTab, 0, 1, nTabWidth, 1, "no icon, min size");
+
+ SendMessage(hwTab, TCM_SETIMAGELIST, 0, (LPARAM)himl);
+ if (i > 1 && nMinTabWidth > 0 && nMinTabWidth < DEFAULT_MIN_TAB_WIDTH)
+ nTabWidth += EXTRA_ICON_PADDING *(i-1);
+
+ TabCheckSetSize(hwTab, 50, 30, nTabWidth, 30, "with icon, set size > icon");
+ TabCheckSetSize(hwTab, 20, 20, nTabWidth, 20, "with icon, set size < icon");
+ TabCheckSetSize(hwTab, 0, 1, nTabWidth, 1, "with icon, min size");
+ }
+
+ DestroyWindow (hwTab);
+
+ ImageList_Destroy(himl);
+ DeleteObject(hFont);
+}
+
+START_TEST(tab)
+{
+ LOGFONTA logfont;
+
+ lstrcpyA(logfont.lfFaceName, "Arial");
+ memset(&logfont, 0, sizeof(logfont));
+ logfont.lfHeight = -12;
+ logfont.lfWeight = FW_NORMAL;
+ logfont.lfCharSet = ANSI_CHARSET;
+ hFont = CreateFontIndirectA(&logfont);
+
+ InitCommonControls();
+
+ trace ("Testing with default MinWidth\n");
+ test_tab(-1);
+ trace ("Testing with MinWidth set to -3\n");
+ test_tab(-3);
+ trace ("Testing with MinWidth set to 24\n");
+ test_tab(24);
+ trace ("Testing with MinWidth set to 54\n");
+ test_tab(54);
+ trace ("Testing with MinWidth set to 94\n");
+ test_tab(94);
+}
--- /dev/null
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_comboex(void);
+extern void func_dpa(void);
+extern void func_header(void);
+extern void func_imagelist(void);
+extern void func_listview(void);
+extern void func_monthcal(void);
+extern void func_mru(void);
+extern void func_progress(void);
+extern void func_propsheet(void);
+extern void func_subclass(void);
+extern void func_tab(void);
+extern void func_toolbar(void);
+extern void func_tooltips(void);
+extern void func_treeview(void);
+extern void func_updown(void);
+
+const struct test winetest_testlist[] =
+{
+ { "comboex", func_comboex },
+ { "dpa", func_dpa },
+ { "header", func_header },
+ { "imagelist", func_imagelist },
+ { "listview", func_listview },
+ { "monthcal", func_monthcal },
+ { "mru", func_mru },
+ { "progress", func_progress },
+ { "propsheet", func_propsheet },
+ { "subclass", func_subclass },
+ { "tab", func_tab },
+ { "toolbar", func_toolbar },
+ { "tooltips", func_tooltips },
+ { "treeview", func_treeview },
+ { "updown", func_updown },
+ { 0, 0 }
+};
--- /dev/null
+/* Unit tests for treeview.
+ *
+ * Copyright 2005 Krzysztof Foltman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "commctrl.h"
+
+#include "wine/test.h"
+
+static void MakeButton(TBBUTTON *p, int idCommand, int fsStyle, int nString) {
+ p->iBitmap = -2;
+ p->idCommand = idCommand;
+ p->fsState = TBSTATE_ENABLED;
+ p->fsStyle = fsStyle;
+ p->iString = nString;
+}
+
+static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+
+ case WM_CREATE:
+ {
+ TBBUTTON buttons[9];
+ int i;
+ HWND hToolbar;
+ for (i=0; i<9; i++)
+ MakeButton(buttons+i, 1000+i, TBSTYLE_CHECKGROUP, 0);
+ MakeButton(buttons+3, 1003, TBSTYLE_SEP|TBSTYLE_GROUP, 0);
+ MakeButton(buttons+6, 1006, TBSTYLE_SEP, 0);
+
+ hToolbar = CreateToolbarEx(hWnd,
+ WS_VISIBLE | WS_CLIPCHILDREN | CCS_TOP |
+ WS_CHILD | TBSTYLE_LIST,
+ 100,
+ 0, NULL, (UINT)0,
+ buttons, sizeof(buttons)/sizeof(buttons[0]),
+ 0, 0, 20, 16, sizeof(TBBUTTON));
+ ok(hToolbar != NULL, "Toolbar creation\n");
+
+ SendMessage(hToolbar, TB_ADDSTRINGA, 0, (LPARAM)"test\000");
+
+ /* test for exclusion working inside a separator-separated :-) group */
+ SendMessage(hToolbar, TB_CHECKBUTTON, 1000, 1); /* press A1 */
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1000, 0), "A1 pressed\n");
+ ok(!SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1001, 0), "A2 not pressed\n");
+
+ SendMessage(hToolbar, TB_CHECKBUTTON, 1004, 1); /* press A5, release A1 */
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1004, 0), "A5 pressed\n");
+ ok(!SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1000, 0), "A1 not pressed anymore\n");
+
+ SendMessage(hToolbar, TB_CHECKBUTTON, 1005, 1); /* press A6, release A5 */
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1005, 0), "A6 pressed\n");
+ ok(!SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1004, 0), "A5 not pressed anymore\n");
+
+ /* test for inter-group crosstalk, ie. two radio groups interfering with each other */
+ SendMessage(hToolbar, TB_CHECKBUTTON, 1007, 1); /* press B2 */
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1005, 0), "A6 still pressed, no inter-group crosstalk\n");
+ ok(!SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1000, 0), "A1 still not pressed\n");
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1007, 0), "B2 pressed\n");
+
+ SendMessage(hToolbar, TB_CHECKBUTTON, 1000, 1); /* press A1 and ensure B group didn't suffer */
+ ok(!SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1005, 0), "A6 not pressed anymore\n");
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1000, 0), "A1 pressed\n");
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1007, 0), "B2 still pressed\n");
+
+ SendMessage(hToolbar, TB_CHECKBUTTON, 1008, 1); /* press B3, and ensure A group didn't suffer */
+ ok(!SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1005, 0), "A6 pressed\n");
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1000, 0), "A1 pressed\n");
+ ok(!SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1007, 0), "B2 not pressed\n");
+ ok(SendMessage(hToolbar, TB_ISBUTTONCHECKED, 1008, 0), "B3 pressed\n");
+ PostMessage(hWnd, WM_CLOSE, 0, 0);
+ return 0;
+ }
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProcA(hWnd, msg, wParam, lParam);
+ }
+ return 0L;
+}
+
+START_TEST(toolbar)
+{
+ WNDCLASSA wc;
+ MSG msg;
+ RECT rc;
+ HWND hMainWnd;
+
+ InitCommonControls();
+
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandleA(NULL);
+ wc.hIcon = NULL;
+ wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_IBEAM));
+ wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = "MyTestWnd";
+ wc.lpfnWndProc = MyWndProc;
+ RegisterClassA(&wc);
+
+ hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
+ GetClientRect(hMainWnd, &rc);
+
+ while(GetMessageA(&msg,0,0,0)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+}
--- /dev/null
+/*
+ * Copyright 2005 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <commctrl.h>
+
+#include "wine/test.h"
+
+static void test_create_tooltip(void)
+{
+ HWND parent, hwnd;
+ DWORD style, exp_style;
+
+ parent = CreateWindowEx(0, "static", NULL, WS_POPUP,
+ 0, 0, 0, 0,
+ NULL, NULL, NULL, 0);
+ assert(parent);
+
+ hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, 0x7fffffff | WS_POPUP,
+ 10, 10, 300, 100,
+ parent, NULL, NULL, 0);
+ assert(hwnd);
+
+ style = GetWindowLong(hwnd, GWL_STYLE);
+ trace("style = %08lx\n", style);
+ exp_style = 0x7fffffff | WS_POPUP;
+ exp_style &= ~(WS_CHILD | WS_MAXIMIZE | WS_BORDER | WS_DLGFRAME);
+ ok(style == exp_style,"wrong style %08lx/%08lx\n", style, exp_style);
+
+ DestroyWindow(hwnd);
+
+ hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, 0,
+ 10, 10, 300, 100,
+ parent, NULL, NULL, 0);
+ assert(hwnd);
+
+ style = GetWindowLong(hwnd, GWL_STYLE);
+ trace("style = %08lx\n", style);
+ ok(style == (WS_POPUP | WS_CLIPSIBLINGS | WS_BORDER),
+ "wrong style %08lx\n", style);
+
+ DestroyWindow(hwnd);
+
+ DestroyWindow(parent);
+}
+
+START_TEST(tooltips)
+{
+ InitCommonControls();
+
+ test_create_tooltip();
+}
--- /dev/null
+/* Unit tests for treeview.
+ *
+ * Copyright 2005 Krzysztof Foltman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "commctrl.h"
+
+#include "wine/test.h"
+
+static HWND hMainWnd;
+
+static HWND hTree;
+static HTREEITEM hRoot, hChild;
+
+static int pos = 0;
+static char sequence[256];
+
+static void Clear(void)
+{
+ pos = 0;
+ sequence[0] = '\0';
+}
+
+static void AddItem(char ch)
+{
+ sequence[pos++] = ch;
+ sequence[pos] = '\0';
+}
+
+static void IdentifyItem(HTREEITEM hItem)
+{
+ if (hItem == hRoot) {
+ AddItem('R');
+ return;
+ }
+ if (hItem == hChild) {
+ AddItem('C');
+ return;
+ }
+ if (hItem == NULL) {
+ AddItem('n');
+ return;
+ }
+ AddItem('?');
+}
+
+static void FillRoot(void)
+{
+ TVINSERTSTRUCTA ins;
+ static CHAR root[] = "Root",
+ child[] = "Child";
+
+ Clear();
+ AddItem('A');
+ ins.hParent = TVI_ROOT;
+ ins.hInsertAfter = TVI_ROOT;
+ U(ins).item.mask = TVIF_TEXT;
+ U(ins).item.pszText = root;
+ hRoot = TreeView_InsertItem(hTree, &ins);
+ assert(hRoot);
+
+ AddItem('B');
+ ins.hParent = hRoot;
+ ins.hInsertAfter = TVI_FIRST;
+ U(ins).item.mask = TVIF_TEXT;
+ U(ins).item.pszText = child;
+ hChild = TreeView_InsertItem(hTree, &ins);
+ assert(hChild);
+ AddItem('.');
+
+ ok(!strcmp(sequence, "AB."), "Item creation\n");
+}
+
+static void DoTest1(void)
+{
+ BOOL r;
+ r = TreeView_SelectItem(hTree, NULL);
+ Clear();
+ AddItem('1');
+ r = TreeView_SelectItem(hTree, hRoot);
+ AddItem('2');
+ r = TreeView_SelectItem(hTree, hRoot);
+ AddItem('3');
+ r = TreeView_SelectItem(hTree, NULL);
+ AddItem('4');
+ r = TreeView_SelectItem(hTree, NULL);
+ AddItem('5');
+ r = TreeView_SelectItem(hTree, hRoot);
+ AddItem('.');
+ ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
+}
+
+static void DoTest2(void)
+{
+ BOOL r;
+ r = TreeView_SelectItem(hTree, NULL);
+ Clear();
+ AddItem('1');
+ r = TreeView_SelectItem(hTree, hRoot);
+ AddItem('2');
+ r = TreeView_SelectItem(hTree, hRoot);
+ AddItem('3');
+ r = TreeView_SelectItem(hTree, hChild);
+ AddItem('4');
+ r = TreeView_SelectItem(hTree, hChild);
+ AddItem('5');
+ r = TreeView_SelectItem(hTree, hRoot);
+ AddItem('.');
+ ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
+}
+
+LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch(msg) {
+
+ case WM_CREATE:
+ {
+ hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
+ TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS,
+ 0, 0, 300, 50, hWnd, (HMENU)100, GetModuleHandleA(0), 0);
+
+ SetFocus(hTree);
+ return 0;
+ }
+ case WM_NOTIFY:
+ {
+ NMHDR *pHdr = (NMHDR *)lParam;
+
+ if (pHdr->idFrom == 100) {
+ NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
+ switch(pHdr->code) {
+ case TVN_SELCHANGINGA:
+ AddItem('(');
+ IdentifyItem(pTreeView->itemOld.hItem);
+ IdentifyItem(pTreeView->itemNew.hItem);
+ return 0;
+ case TVN_SELCHANGEDA:
+ AddItem(')');
+ IdentifyItem(pTreeView->itemOld.hItem);
+ IdentifyItem(pTreeView->itemNew.hItem);
+ return 0;
+ }
+ }
+ return 0;
+ }
+
+ case WM_SIZE:
+ MoveWindow(hTree, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ default:
+ return DefWindowProcA(hWnd, msg, wParam, lParam);
+ }
+ return 0L;
+}
+
+START_TEST(treeview)
+{
+ WNDCLASSA wc;
+ MSG msg;
+ INITCOMMONCONTROLSEX icex;
+ RECT rc;
+
+ icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ icex.dwICC = ICC_TREEVIEW_CLASSES;
+ InitCommonControlsEx(&icex);
+
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = GetModuleHandleA(NULL);
+ wc.hIcon = NULL;
+ wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_IBEAM));
+ wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = "MyTestWnd";
+ wc.lpfnWndProc = MyWndProc;
+ RegisterClassA(&wc);
+
+
+ hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
+ GetClientRect(hMainWnd, &rc);
+
+ FillRoot();
+ DoTest1();
+ DoTest2();
+
+ PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
+ while(GetMessageA(&msg,0,0,0)) {
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+}
--- /dev/null
+/* Unit test suite for updown control.
+ *
+ * Copyright 2005 C. Scott Ananian
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+
+static HDC desktopDC;
+static HINSTANCE hinst;
+
+static HWND create_edit_control (DWORD style, DWORD exstyle)
+{
+ HWND handle;
+
+ handle = CreateWindowEx(exstyle,
+ "EDIT",
+ NULL,
+ ES_AUTOHSCROLL | ES_AUTOVSCROLL | style,
+ 10, 10, 300, 300,
+ NULL, NULL, hinst, NULL);
+ assert (handle);
+ if (winetest_interactive)
+ ShowWindow (handle, SW_SHOW);
+ return handle;
+}
+
+static HWND create_updown_control (HWND hWndEdit)
+{
+ HWND hWndUpDown;
+
+ /* make the control */
+ hWndUpDown = CreateWindowEx
+ (0L, UPDOWN_CLASS, NULL,
+ /* window styles */
+ UDS_SETBUDDYINT | UDS_ALIGNRIGHT |
+ UDS_ARROWKEYS | UDS_NOTHOUSANDS,
+ /* placement */
+ 0, 0, 8, 8,
+ /* parent, etc */
+ NULL, NULL, hinst, NULL);
+ assert (hWndUpDown);
+ /* set the buddy. */
+ SendMessage (hWndUpDown, UDM_SETBUDDY, (WPARAM)hWndEdit, 0L );
+ /* set the range. */
+ SendMessage (hWndUpDown, UDM_SETRANGE, 0L, (LPARAM) MAKELONG(32000, 0));
+ /* maybe show it. */
+ if (winetest_interactive)
+ ShowWindow (hWndUpDown, SW_SHOW);
+ return hWndUpDown;
+}
+
+static void test_updown_control (void)
+{
+ HWND hWndUpDown, hWndEdit;
+ int num;
+
+ hWndEdit = create_edit_control (ES_AUTOHSCROLL | ES_NUMBER, 0);
+ hWndUpDown = create_updown_control (hWndEdit);
+ /* before we set a value, it should be '0' */
+ num = SendMessage(hWndUpDown, UDM_GETPOS, 0, 0L);
+ ok(num == 0, "Expected 0 got %d\n", num);
+ /* set a value, check it. */
+ SendMessage(hWndUpDown, UDM_SETPOS, 0L, MAKELONG( 1, 0));
+ num = SendMessage(hWndUpDown, UDM_GETPOS, 0, 0L);
+ ok(num == 1, "Expected 1 got %d\n", num);
+ /* okay, done (short set of tests!) */
+ DestroyWindow(hWndUpDown);
+ DestroyWindow(hWndEdit);
+}
+
+START_TEST(updown)
+{
+ desktopDC=GetDC(NULL);
+ hinst = GetModuleHandleA(NULL);
+
+ InitCommonControls();
+
+ test_updown_control();
+}
--- /dev/null
+<group>
+<directory name="advapi32">
+ <xi:include href="advapi32/advapi32.rbuild" />
+</directory>
+<directory name="cabinet">
+ <xi:include href="cabinet/cabinet.rbuild" />
+</directory>
+<directory name="comctl32">
+ <xi:include href="comctl32/comctl32.rbuild" />
+</directory>
+<directory name="gdi32">
+ <xi:include href="gdi32/gdi32.rbuild" />
+</directory>
+<directory name="icmp">
+ <xi:include href="icmp/icmp.rbuild" />
+</directory>
+<directory name="kernel32">
+ <xi:include href="kernel32/kernel32.rbuild" />
+</directory>
+<directory name="lz32">
+ <xi:include href="lz32/lz32.rbuild" />
+</directory>
+<directory name="msi">
+ <xi:include href="msi/msi.rbuild" />
+</directory>
+<directory name="msvcrt">
+ <xi:include href="msvcrt/msvcrt.rbuild" />
+</directory>
+<directory name="ntdll">
+ <xi:include href="ntdll/ntdll.rbuild" />
+</directory>
+<directory name="psapi">
+ <xi:include href="psapi/psapi.rbuild" />
+</directory>
+<directory name="powrprof">
+ <xi:include href="powrprof/powrprof.rbuild" />
+</directory>
+<directory name="setupapi">
+ <xi:include href="setupapi/setupapi.rbuild" />
+</directory>
+<directory name="shell32">
+ <xi:include href="shell32/shell32.rbuild" />
+</directory>
+<directory name="shlwapi">
+ <xi:include href="shlwapi/shlwapi.rbuild" />
+</directory>
+<directory name="user32">
+ <xi:include href="user32/user32.rbuild" />
+</directory>
+<directory name="usp10">
+ <xi:include href="usp10/usp10.rbuild" />
+</directory>
+<directory name="version">
+ <xi:include href="version/version.rbuild" />
+</directory>
+</group>
--- /dev/null
+/*
+ * Unit test suite for bitmaps
+ *
+ * Copyright 2004 Huw Davies
+ * Copyright 2006 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmsystem.h"
+
+#include "wine/test.h"
+
+static BOOL is_win9x;
+
+static INT BITMAP_GetWidthBytes( INT bmWidth, INT bpp )
+{
+ switch(bpp)
+ {
+ case 1:
+ return 2 * ((bmWidth+15) >> 4);
+
+ case 24:
+ bmWidth *= 3; /* fall through */
+ case 8:
+ return bmWidth + (bmWidth & 1);
+
+ case 32:
+ return bmWidth * 4;
+
+ case 16:
+ case 15:
+ return bmWidth * 2;
+
+ case 4:
+ return 2 * ((bmWidth+3) >> 2);
+
+ default:
+ trace("Unknown depth %d, please report.\n", bpp );
+ assert(0);
+ }
+ return -1;
+}
+
+static void test_bitmap_info(HBITMAP hbm, INT expected_depth, const BITMAPINFOHEADER *bmih)
+{
+ BITMAP bm;
+ INT ret, width_bytes;
+ char buf[512], buf_cmp[512];
+
+ ret = GetObject(hbm, sizeof(bm), &bm);
+ ok(ret == sizeof(bm), "GetObject returned %d\n", ret);
+
+ ok(bm.bmType == 0, "wrong bm.bmType %d\n", bm.bmType);
+ ok(bm.bmWidth == bmih->biWidth, "wrong bm.bmWidth %d\n", bm.bmWidth);
+ ok(bm.bmHeight == bmih->biHeight, "wrong bm.bmHeight %d\n", bm.bmHeight);
+ width_bytes = BITMAP_GetWidthBytes(bm.bmWidth, bm.bmBitsPixel);
+ ok(bm.bmWidthBytes == width_bytes, "wrong bm.bmWidthBytes %d != %d\n", bm.bmWidthBytes, width_bytes);
+ ok(bm.bmPlanes == bmih->biPlanes, "wrong bm.bmPlanes %d\n", bm.bmPlanes);
+ ok(bm.bmBitsPixel == expected_depth, "wrong bm.bmBitsPixel %d != %d\n", bm.bmBitsPixel, expected_depth);
+ ok(bm.bmBits == NULL, "wrong bm.bmBits %p\n", bm.bmBits);
+
+ assert(sizeof(buf) >= bm.bmWidthBytes * bm.bmHeight);
+ assert(sizeof(buf) == sizeof(buf_cmp));
+
+ ret = GetBitmapBits(hbm, 0, NULL);
+ ok(ret == bm.bmWidthBytes * bm.bmHeight, "%d != %d\n", ret, bm.bmWidthBytes * bm.bmHeight);
+
+ memset(buf_cmp, 0xAA, sizeof(buf_cmp));
+ memset(buf_cmp, 0, bm.bmWidthBytes * bm.bmHeight);
+
+ memset(buf, 0xAA, sizeof(buf));
+ ret = GetBitmapBits(hbm, sizeof(buf), buf);
+ ok(ret == bm.bmWidthBytes * bm.bmHeight, "%d != %d\n", ret, bm.bmWidthBytes * bm.bmHeight);
+ ok(!memcmp(buf, buf_cmp, sizeof(buf)), "buffers do not match\n");
+
+ /* test various buffer sizes for GetObject */
+ ret = GetObject(hbm, 0, NULL);
+ ok(ret == sizeof(bm), "wrong size %d\n", ret);
+
+ ret = GetObject(hbm, sizeof(bm) * 2, &bm);
+ ok(ret == sizeof(bm), "wrong size %d\n", ret);
+
+ ret = GetObject(hbm, sizeof(bm) / 2, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ ret = GetObject(hbm, 0, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ ret = GetObject(hbm, 1, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+}
+
+static void test_createdibitmap(void)
+{
+ HDC hdc, hdcmem;
+ BITMAPINFOHEADER bmih;
+ HBITMAP hbm, hbm_colour, hbm_old;
+ INT screen_depth;
+
+ hdc = GetDC(0);
+ screen_depth = GetDeviceCaps(hdc, BITSPIXEL);
+ memset(&bmih, 0, sizeof(bmih));
+ bmih.biSize = sizeof(bmih);
+ bmih.biWidth = 10;
+ bmih.biHeight = 10;
+ bmih.biPlanes = 1;
+ bmih.biBitCount = 32;
+ bmih.biCompression = BI_RGB;
+
+ /* First create an un-initialised bitmap. The depth of the bitmap
+ should match that of the hdc and not that supplied in bmih.
+ */
+
+ /* First try 32 bits */
+ hbm = CreateDIBitmap(hdc, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, screen_depth, &bmih);
+ DeleteObject(hbm);
+
+ /* Then 16 */
+ bmih.biBitCount = 16;
+ hbm = CreateDIBitmap(hdc, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, screen_depth, &bmih);
+ DeleteObject(hbm);
+
+ /* Then 1 */
+ bmih.biBitCount = 1;
+ hbm = CreateDIBitmap(hdc, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, screen_depth, &bmih);
+ DeleteObject(hbm);
+
+ /* Now with a monochrome dc we expect a monochrome bitmap */
+ hdcmem = CreateCompatibleDC(hdc);
+
+ /* First try 32 bits */
+ bmih.biBitCount = 32;
+ hbm = CreateDIBitmap(hdcmem, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, 1, &bmih);
+ DeleteObject(hbm);
+
+ /* Then 16 */
+ bmih.biBitCount = 16;
+ hbm = CreateDIBitmap(hdcmem, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, 1, &bmih);
+ DeleteObject(hbm);
+
+ /* Then 1 */
+ bmih.biBitCount = 1;
+ hbm = CreateDIBitmap(hdcmem, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, 1, &bmih);
+ DeleteObject(hbm);
+
+ /* Now select a polychrome bitmap into the dc and we expect
+ screen_depth bitmaps again */
+ hbm_colour = CreateCompatibleBitmap(hdc, 1, 1);
+ hbm_old = SelectObject(hdcmem, hbm_colour);
+
+ /* First try 32 bits */
+ bmih.biBitCount = 32;
+ hbm = CreateDIBitmap(hdcmem, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, screen_depth, &bmih);
+ DeleteObject(hbm);
+
+ /* Then 16 */
+ bmih.biBitCount = 16;
+ hbm = CreateDIBitmap(hdcmem, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, screen_depth, &bmih);
+ DeleteObject(hbm);
+
+ /* Then 1 */
+ bmih.biBitCount = 1;
+ hbm = CreateDIBitmap(hdcmem, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, screen_depth, &bmih);
+ DeleteObject(hbm);
+
+ SelectObject(hdcmem, hbm_old);
+ DeleteObject(hbm_colour);
+ DeleteDC(hdcmem);
+
+ /* If hdc == 0 then we get a 1 bpp bitmap */
+ if (!is_win9x) {
+ bmih.biBitCount = 32;
+ hbm = CreateDIBitmap(0, &bmih, 0, NULL, NULL, 0);
+ ok(hbm != NULL, "CreateDIBitmap failed\n");
+ test_bitmap_info(hbm, 1, &bmih);
+ DeleteObject(hbm);
+ }
+
+ ReleaseDC(0, hdc);
+}
+
+static INT DIB_GetWidthBytes( int width, int bpp )
+{
+ int words;
+
+ switch (bpp)
+ {
+ case 1: words = (width + 31) / 32; break;
+ case 4: words = (width + 7) / 8; break;
+ case 8: words = (width + 3) / 4; break;
+ case 15:
+ case 16: words = (width + 1) / 2; break;
+ case 24: words = (width * 3 + 3)/4; break;
+ case 32: words = width; break;
+
+ default:
+ words=0;
+ trace("Unknown depth %d, please report.\n", bpp );
+ assert(0);
+ break;
+ }
+ return 4 * words;
+}
+
+static void test_dib_info(HBITMAP hbm, const void *bits, const BITMAPINFOHEADER *bmih)
+{
+ BITMAP bm;
+ DIBSECTION ds;
+ INT ret, width_bytes;
+ BYTE *buf;
+
+ ret = GetObject(hbm, sizeof(bm), &bm);
+ ok(ret == sizeof(bm), "GetObject returned %d\n", ret);
+
+ ok(bm.bmType == 0, "wrong bm.bmType %d\n", bm.bmType);
+ ok(bm.bmWidth == bmih->biWidth, "wrong bm.bmWidth %d\n", bm.bmWidth);
+ ok(bm.bmHeight == bmih->biHeight, "wrong bm.bmHeight %d\n", bm.bmHeight);
+ width_bytes = DIB_GetWidthBytes(bm.bmWidth, bm.bmBitsPixel);
+ ok(bm.bmWidthBytes == width_bytes, "wrong bm.bmWidthBytes %d != %d\n", bm.bmWidthBytes, width_bytes);
+ ok(bm.bmPlanes == bmih->biPlanes, "wrong bm.bmPlanes %d\n", bm.bmPlanes);
+ ok(bm.bmBitsPixel == bmih->biBitCount, "bm.bmBitsPixel %d != %d\n", bm.bmBitsPixel, bmih->biBitCount);
+ ok(bm.bmBits == bits, "wrong bm.bmBits %p != %p\n", bm.bmBits, bits);
+
+ buf = HeapAlloc(GetProcessHeap(), 0, bm.bmWidthBytes * bm.bmHeight + 4096);
+
+ width_bytes = BITMAP_GetWidthBytes(bm.bmWidth, bm.bmBitsPixel);
+
+ /* GetBitmapBits returns not 32-bit aligned data */
+ ret = GetBitmapBits(hbm, 0, NULL);
+ ok(ret == width_bytes * bm.bmHeight, "%d != %d\n", ret, width_bytes * bm.bmHeight);
+
+ memset(buf, 0xAA, bm.bmWidthBytes * bm.bmHeight + 4096);
+ ret = GetBitmapBits(hbm, bm.bmWidthBytes * bm.bmHeight + 4096, buf);
+ ok(ret == width_bytes * bm.bmHeight, "%d != %d\n", ret, width_bytes * bm.bmHeight);
+
+ HeapFree(GetProcessHeap(), 0, buf);
+
+ /* test various buffer sizes for GetObject */
+ memset(&ds, 0xAA, sizeof(ds));
+ ret = GetObject(hbm, sizeof(bm) * 2, &bm);
+ ok(ret == sizeof(bm), "wrong size %d\n", ret);
+ ok(bm.bmWidth == bmih->biWidth, "wrong bm.bmWidth %d\n", bm.bmWidth);
+ ok(bm.bmHeight == bmih->biHeight, "wrong bm.bmHeight %d\n", bm.bmHeight);
+ ok(bm.bmBits == bits, "wrong bm.bmBits %p != %p\n", bm.bmBits, bits);
+
+ ret = GetObject(hbm, sizeof(bm) / 2, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ ret = GetObject(hbm, 0, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ ret = GetObject(hbm, 1, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ /* test various buffer sizes for GetObject */
+ ret = GetObject(hbm, 0, NULL);
+ ok(ret == sizeof(bm), "wrong size %d\n", ret);
+
+ memset(&ds, 0xAA, sizeof(ds));
+ ret = GetObject(hbm, sizeof(ds) * 2, &ds);
+ ok(ret == sizeof(ds), "wrong size %d\n", ret);
+
+ ok(ds.dsBm.bmBits == bits, "wrong bm.bmBits %p != %p\n", ds.dsBm.bmBits, bits);
+ ok(ds.dsBmih.biSizeImage == ds.dsBm.bmWidthBytes * ds.dsBm.bmHeight, "%lu != %u\n",
+ ds.dsBmih.biSizeImage, ds.dsBm.bmWidthBytes * ds.dsBm.bmHeight);
+ ok(bmih->biSizeImage == 0, "%lu != 0\n", bmih->biSizeImage);
+ ds.dsBmih.biSizeImage = 0;
+
+ ok(ds.dsBmih.biSize == bmih->biSize, "%lu != %lu\n", ds.dsBmih.biSize, bmih->biSize);
+ ok(ds.dsBmih.biWidth == bmih->biWidth, "%lu != %lu\n", ds.dsBmih.biWidth, bmih->biWidth);
+ ok(ds.dsBmih.biHeight == bmih->biHeight, "%lu != %lu\n", ds.dsBmih.biHeight, bmih->biHeight);
+ ok(ds.dsBmih.biPlanes == bmih->biPlanes, "%u != %u\n", ds.dsBmih.biPlanes, bmih->biPlanes);
+ ok(ds.dsBmih.biBitCount == bmih->biBitCount, "%u != %u\n", ds.dsBmih.biBitCount, bmih->biBitCount);
+ ok(ds.dsBmih.biCompression == bmih->biCompression, "%lu != %lu\n", ds.dsBmih.biCompression, bmih->biCompression);
+ ok(ds.dsBmih.biSizeImage == bmih->biSizeImage, "%lu != %lu\n", ds.dsBmih.biSizeImage, bmih->biSizeImage);
+ ok(ds.dsBmih.biXPelsPerMeter == bmih->biXPelsPerMeter, "%lu != %lu\n", ds.dsBmih.biXPelsPerMeter, bmih->biXPelsPerMeter);
+ ok(ds.dsBmih.biYPelsPerMeter == bmih->biYPelsPerMeter, "%lu != %lu\n", ds.dsBmih.biYPelsPerMeter, bmih->biYPelsPerMeter);
+
+ memset(&ds, 0xAA, sizeof(ds));
+ ret = GetObject(hbm, sizeof(ds) - 4, &ds);
+ ok(ret == sizeof(ds.dsBm), "wrong size %d\n", ret);
+ ok(ds.dsBm.bmWidth == bmih->biWidth, "%lu != %lu\n", ds.dsBmih.biWidth, bmih->biWidth);
+ ok(ds.dsBm.bmHeight == bmih->biHeight, "%lu != %lu\n", ds.dsBmih.biHeight, bmih->biHeight);
+ ok(ds.dsBm.bmBits == bits, "%p != %p\n", ds.dsBm.bmBits, bits);
+
+ ret = GetObject(hbm, 0, &ds);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ ret = GetObject(hbm, 1, &ds);
+ ok(ret == 0, "%d != 0\n", ret);
+}
+
+#define test_color_todo(got, exp, txt, todo) \
+ if (!todo && got != exp && screen_depth < 24) { \
+ todo_wine ok(0, #txt " failed at %d-bit screen depth: got 0x%06x expected 0x%06x - skipping DIB tests\n", \
+ screen_depth, (UINT)got, (UINT)exp); \
+ return; \
+ } else if (todo) todo_wine { ok(got == exp, #txt " failed: got 0x%06x expected 0x%06x\n", (UINT)got, (UINT)exp); } \
+ else ok(got == exp, #txt " failed: got 0x%06x expected 0x%06x\n", (UINT)got, (UINT)exp) \
+
+#define test_color(hdc, color, exp, todo_setp, todo_getp) \
+{ \
+ COLORREF c; \
+ c = SetPixel(hdc, 0, 0, color); \
+ if (!is_win9x) { test_color_todo(c, exp, SetPixel, todo_setp); } \
+ c = GetPixel(hdc, 0, 0); \
+ test_color_todo(c, exp, GetPixel, todo_getp); \
+}
+
+static void test_dibsections(void)
+{
+ HDC hdc, hdcmem, hdcmem2;
+ HBITMAP hdib, oldbm, hdib2, oldbm2;
+ char bmibuf[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)];
+ char bcibuf[sizeof(BITMAPCOREINFO) + 256 * sizeof(RGBTRIPLE)];
+ BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf;
+ BITMAPCOREINFO *pbci = (BITMAPCOREINFO *)bcibuf;
+ HBITMAP hcoredib;
+ char coreBits[256];
+ BYTE *bits;
+ RGBQUAD rgb[256];
+ int ret;
+ char logpalbuf[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)];
+ LOGPALETTE *plogpal = (LOGPALETTE*)logpalbuf;
+ WORD *index;
+ DWORD *bits32;
+ HPALETTE hpal, oldpal;
+ DIBSECTION dibsec;
+ COLORREF c0, c1;
+ int i;
+ int screen_depth;
+ MEMORY_BASIC_INFORMATION info;
+
+ hdc = GetDC(0);
+ screen_depth = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES);
+
+ memset(pbmi, 0, sizeof(bmibuf));
+ pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader);
+ pbmi->bmiHeader.biHeight = 100;
+ pbmi->bmiHeader.biWidth = 512;
+ pbmi->bmiHeader.biBitCount = 24;
+ pbmi->bmiHeader.biPlanes = 1;
+ pbmi->bmiHeader.biCompression = BI_RGB;
+
+ SetLastError(0xdeadbeef);
+ hdib = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection error %ld\n", GetLastError());
+ ok(GetObject(hdib, sizeof(DIBSECTION), &dibsec) != 0, "GetObject failed for DIBSection\n");
+ ok(dibsec.dsBm.bmBits == bits, "dibsec.dsBits %p != bits %p\n", dibsec.dsBm.bmBits, bits);
+
+ /* test the DIB memory */
+ ok(VirtualQuery(bits, &info, sizeof(info)) == sizeof(info),
+ "VirtualQuery failed\n");
+ ok(info.BaseAddress == bits, "%p != %p\n", info.BaseAddress, bits);
+ ok(info.AllocationBase == bits, "%p != %p\n", info.AllocationBase, bits);
+ ok(info.AllocationProtect == PAGE_READWRITE, "%lx != PAGE_READWRITE\n", info.AllocationProtect);
+ ok(info.RegionSize == 0x26000, "0x%lx != 0x26000\n", info.RegionSize);
+ ok(info.State == MEM_COMMIT, "%lx != MEM_COMMIT\n", info.State);
+ ok(info.Protect == PAGE_READWRITE, "%lx != PAGE_READWRITE\n", info.Protect);
+ ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+ DeleteObject(hdib);
+
+ pbmi->bmiHeader.biBitCount = 8;
+ pbmi->bmiHeader.biCompression = BI_RLE8;
+ SetLastError(0xdeadbeef);
+ hdib = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib == NULL, "CreateDIBSection should fail when asked to create a compressed DIB section\n");
+ ok(GetLastError() == 0xdeadbeef, "wrong error %ld\n", GetLastError());
+
+ pbmi->bmiHeader.biBitCount = 16;
+ pbmi->bmiHeader.biCompression = BI_BITFIELDS;
+ ((PDWORD)pbmi->bmiColors)[0] = 0xf800;
+ ((PDWORD)pbmi->bmiColors)[1] = 0x07e0;
+ ((PDWORD)pbmi->bmiColors)[2] = 0x001f;
+ SetLastError(0xdeadbeef);
+ hdib = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection error %ld\n", GetLastError());
+
+ /* test the DIB memory */
+ ok(VirtualQuery(bits, &info, sizeof(info)) == sizeof(info),
+ "VirtualQuery failed\n");
+ ok(info.BaseAddress == bits, "%p != %p\n", info.BaseAddress, bits);
+ ok(info.AllocationBase == bits, "%p != %p\n", info.AllocationBase, bits);
+ ok(info.AllocationProtect == PAGE_READWRITE, "%lx != PAGE_READWRITE\n", info.AllocationProtect);
+ ok(info.RegionSize == 0x19000, "0x%lx != 0x19000\n", info.RegionSize);
+ ok(info.State == MEM_COMMIT, "%lx != MEM_COMMIT\n", info.State);
+ ok(info.Protect == PAGE_READWRITE, "%lx != PAGE_READWRITE\n", info.Protect);
+ ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+ DeleteObject(hdib);
+
+ memset(pbmi, 0, sizeof(bmibuf));
+ pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader);
+ pbmi->bmiHeader.biHeight = 16;
+ pbmi->bmiHeader.biWidth = 16;
+ pbmi->bmiHeader.biBitCount = 1;
+ pbmi->bmiHeader.biPlanes = 1;
+ pbmi->bmiHeader.biCompression = BI_RGB;
+ pbmi->bmiColors[0].rgbRed = 0xff;
+ pbmi->bmiColors[0].rgbGreen = 0;
+ pbmi->bmiColors[0].rgbBlue = 0;
+ pbmi->bmiColors[1].rgbRed = 0;
+ pbmi->bmiColors[1].rgbGreen = 0;
+ pbmi->bmiColors[1].rgbBlue = 0xff;
+
+ hdib = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection failed\n");
+ ok(GetObject(hdib, sizeof(DIBSECTION), &dibsec) != 0, "GetObject failed for DIBSection\n");
+ ok(dibsec.dsBmih.biClrUsed == 2,
+ "created DIBSection: wrong biClrUsed field: %lu, should be: %u\n", dibsec.dsBmih.biClrUsed, 2);
+
+ /* Test if the old BITMAPCOREINFO structure is supported */
+
+ pbci->bmciHeader.bcSize = sizeof(BITMAPCOREHEADER);
+ pbci->bmciHeader.bcBitCount = 0;
+
+ if (!is_win9x) {
+ ret = GetDIBits(hdc, hdib, 0, 16, NULL, (BITMAPINFO*) pbci, DIB_RGB_COLORS);
+ ok(ret, "GetDIBits doesn't work with a BITMAPCOREHEADER\n");
+ ok((pbci->bmciHeader.bcWidth == 16) && (pbci->bmciHeader.bcHeight == 16)
+ && (pbci->bmciHeader.bcBitCount == 1) && (pbci->bmciHeader.bcPlanes == 1),
+ "GetDIBits did't fill in the BITMAPCOREHEADER structure properly\n");
+
+ ret = GetDIBits(hdc, hdib, 0, 16, &coreBits, (BITMAPINFO*) pbci, DIB_RGB_COLORS);
+ ok(ret, "GetDIBits doesn't work with a BITMAPCOREHEADER\n");
+ ok((pbci->bmciColors[0].rgbtRed == 0xff) && (pbci->bmciColors[0].rgbtGreen == 0) &&
+ (pbci->bmciColors[0].rgbtBlue == 0) && (pbci->bmciColors[1].rgbtRed == 0) &&
+ (pbci->bmciColors[1].rgbtGreen == 0) && (pbci->bmciColors[1].rgbtBlue == 0xff),
+ "The color table has not been translated to the old BITMAPCOREINFO format\n");
+
+ hcoredib = CreateDIBSection(hdc, (BITMAPINFO*) pbci, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hcoredib != NULL, "CreateDIBSection failed with a BITMAPCOREINFO\n");
+
+ ZeroMemory(pbci->bmciColors, 256 * sizeof(RGBTRIPLE));
+ ret = GetDIBits(hdc, hcoredib, 0, 16, &coreBits, (BITMAPINFO*) pbci, DIB_RGB_COLORS);
+ ok(ret, "GetDIBits doesn't work with a BITMAPCOREHEADER\n");
+ ok((pbci->bmciColors[0].rgbtRed == 0xff) && (pbci->bmciColors[0].rgbtGreen == 0) &&
+ (pbci->bmciColors[0].rgbtBlue == 0) && (pbci->bmciColors[1].rgbtRed == 0) &&
+ (pbci->bmciColors[1].rgbtGreen == 0) && (pbci->bmciColors[1].rgbtBlue == 0xff),
+ "The color table has not been translated to the old BITMAPCOREINFO format\n");
+
+ DeleteObject(hcoredib);
+ }
+
+ hdcmem = CreateCompatibleDC(hdc);
+ oldbm = SelectObject(hdcmem, hdib);
+
+ ret = GetDIBColorTable(hdcmem, 0, 2, rgb);
+ ok(ret == 2, "GetDIBColorTable returned %d\n", ret);
+ ok(!memcmp(rgb, pbmi->bmiColors, 2 * sizeof(RGBQUAD)),
+ "GetDIBColorTable returns table 0: r%02x g%02x b%02x res%02x 1: r%02x g%02x b%02x res%02x\n",
+ rgb[0].rgbRed, rgb[0].rgbGreen, rgb[0].rgbBlue, rgb[0].rgbReserved,
+ rgb[1].rgbRed, rgb[1].rgbGreen, rgb[1].rgbBlue, rgb[1].rgbReserved);
+
+ c0 = RGB(pbmi->bmiColors[0].rgbRed, pbmi->bmiColors[0].rgbGreen, pbmi->bmiColors[0].rgbBlue);
+ c1 = RGB(pbmi->bmiColors[1].rgbRed, pbmi->bmiColors[1].rgbGreen, pbmi->bmiColors[1].rgbBlue);
+
+ test_color(hdcmem, DIBINDEX(0), c0, 0, 1);
+ test_color(hdcmem, DIBINDEX(1), c1, 0, 1);
+ test_color(hdcmem, DIBINDEX(2), c0, 1, 1);
+ test_color(hdcmem, PALETTEINDEX(0), c0, 1, 1);
+ test_color(hdcmem, PALETTEINDEX(1), c0, 1, 1);
+ test_color(hdcmem, PALETTEINDEX(2), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(pbmi->bmiColors[0].rgbRed, pbmi->bmiColors[0].rgbGreen,
+ pbmi->bmiColors[0].rgbBlue), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(pbmi->bmiColors[1].rgbRed, pbmi->bmiColors[1].rgbGreen,
+ pbmi->bmiColors[1].rgbBlue), c1, 1, 1);
+ test_color(hdcmem, PALETTERGB(0, 0, 0), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(0xff, 0xff, 0xff), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(0, 0, 0xfe), c1, 1, 1);
+
+ SelectObject(hdcmem, oldbm);
+ DeleteObject(hdib);
+
+ pbmi->bmiColors[0].rgbRed = 0xff;
+ pbmi->bmiColors[0].rgbGreen = 0xff;
+ pbmi->bmiColors[0].rgbBlue = 0xff;
+ pbmi->bmiColors[1].rgbRed = 0;
+ pbmi->bmiColors[1].rgbGreen = 0;
+ pbmi->bmiColors[1].rgbBlue = 0;
+
+ hdib = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection failed\n");
+
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+
+ oldbm = SelectObject(hdcmem, hdib);
+
+ ret = GetDIBColorTable(hdcmem, 0, 2, rgb);
+ ok(ret == 2, "GetDIBColorTable returned %d\n", ret);
+ ok(!memcmp(rgb, pbmi->bmiColors, 2 * sizeof(RGBQUAD)),
+ "GetDIBColorTable returns table 0: r%02x g%02x b%02x res%02x 1: r%02x g%02x b%02x res%02x\n",
+ rgb[0].rgbRed, rgb[0].rgbGreen, rgb[0].rgbBlue, rgb[0].rgbReserved,
+ rgb[1].rgbRed, rgb[1].rgbGreen, rgb[1].rgbBlue, rgb[1].rgbReserved);
+
+ SelectObject(hdcmem, oldbm);
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+ DeleteObject(hdib);
+
+ pbmi->bmiHeader.biBitCount = 4;
+ for (i = 0; i < 16; i++) {
+ pbmi->bmiColors[i].rgbRed = i;
+ pbmi->bmiColors[i].rgbGreen = 16-i;
+ pbmi->bmiColors[i].rgbBlue = 0;
+ }
+ hdib = CreateDIBSection(hdcmem, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection failed\n");
+ ok(GetObject(hdib, sizeof(DIBSECTION), &dibsec) != 0, "GetObject failed for DIB Section\n");
+ ok(dibsec.dsBmih.biClrUsed == 16,
+ "created DIBSection: wrong biClrUsed field: %lu, should be: %u\n", dibsec.dsBmih.biClrUsed, 16);
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+ DeleteObject(hdib);
+
+ pbmi->bmiHeader.biBitCount = 8;
+
+ for (i = 0; i < 128; i++) {
+ pbmi->bmiColors[i].rgbRed = 255 - i * 2;
+ pbmi->bmiColors[i].rgbGreen = i * 2;
+ pbmi->bmiColors[i].rgbBlue = 0;
+ pbmi->bmiColors[255 - i].rgbRed = 0;
+ pbmi->bmiColors[255 - i].rgbGreen = i * 2;
+ pbmi->bmiColors[255 - i].rgbBlue = 255 - i * 2;
+ }
+ hdib = CreateDIBSection(hdcmem, pbmi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection failed\n");
+ ok(GetObject(hdib, sizeof(DIBSECTION), &dibsec) != 0, "GetObject failed for DIB Section\n");
+ ok(dibsec.dsBmih.biClrUsed == 256,
+ "created DIBSection: wrong biClrUsed field: %lu, should be: %u\n", dibsec.dsBmih.biClrUsed, 256);
+
+ oldbm = SelectObject(hdcmem, hdib);
+
+ for (i = 0; i < 256; i++) {
+ test_color(hdcmem, DIBINDEX(i),
+ RGB(pbmi->bmiColors[i].rgbRed, pbmi->bmiColors[i].rgbGreen, pbmi->bmiColors[i].rgbBlue), 0, 0);
+ test_color(hdcmem, PALETTERGB(pbmi->bmiColors[i].rgbRed, pbmi->bmiColors[i].rgbGreen, pbmi->bmiColors[i].rgbBlue),
+ RGB(pbmi->bmiColors[i].rgbRed, pbmi->bmiColors[i].rgbGreen, pbmi->bmiColors[i].rgbBlue), 0, 0);
+ }
+
+ SelectObject(hdcmem, oldbm);
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+ DeleteObject(hdib);
+
+ pbmi->bmiHeader.biBitCount = 1;
+
+ /* Now create a palette and a palette indexed dib section */
+ memset(plogpal, 0, sizeof(logpalbuf));
+ plogpal->palVersion = 0x300;
+ plogpal->palNumEntries = 2;
+ plogpal->palPalEntry[0].peRed = 0xff;
+ plogpal->palPalEntry[0].peBlue = 0xff;
+ plogpal->palPalEntry[1].peGreen = 0xff;
+
+ index = (WORD*)pbmi->bmiColors;
+ *index++ = 0;
+ *index = 1;
+ hpal = CreatePalette(plogpal);
+ ok(hpal != NULL, "CreatePalette failed\n");
+ oldpal = SelectPalette(hdc, hpal, TRUE);
+ hdib = CreateDIBSection(hdc, pbmi, DIB_PAL_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection failed\n");
+ ok(GetObject(hdib, sizeof(DIBSECTION), &dibsec) != 0, "GetObject failed for DIB Section\n");
+ ok(dibsec.dsBmih.biClrUsed == 2,
+ "created DIBSection: wrong biClrUsed field: %lu, should be: %u\n", dibsec.dsBmih.biClrUsed, 2);
+
+ /* The colour table has already been grabbed from the dc, so we select back the
+ old palette */
+
+ SelectPalette(hdc, oldpal, TRUE);
+ oldbm = SelectObject(hdcmem, hdib);
+ oldpal = SelectPalette(hdcmem, hpal, TRUE);
+
+ ret = GetDIBColorTable(hdcmem, 0, 2, rgb);
+ ok(ret == 2, "GetDIBColorTable returned %d\n", ret);
+ ok(rgb[0].rgbRed == 0xff && rgb[0].rgbBlue == 0xff && rgb[0].rgbGreen == 0 &&
+ rgb[1].rgbRed == 0 && rgb[1].rgbBlue == 0 && rgb[1].rgbGreen == 0xff,
+ "GetDIBColorTable returns table 0: r%02x g%02x b%02x res%02x 1: r%02x g%02x b%02x res%02x\n",
+ rgb[0].rgbRed, rgb[0].rgbGreen, rgb[0].rgbBlue, rgb[0].rgbReserved,
+ rgb[1].rgbRed, rgb[1].rgbGreen, rgb[1].rgbBlue, rgb[1].rgbReserved);
+
+ c0 = RGB(plogpal->palPalEntry[0].peRed, plogpal->palPalEntry[0].peGreen, plogpal->palPalEntry[0].peBlue);
+ c1 = RGB(plogpal->palPalEntry[1].peRed, plogpal->palPalEntry[1].peGreen, plogpal->palPalEntry[1].peBlue);
+
+ test_color(hdcmem, DIBINDEX(0), c0, 0, 1);
+ test_color(hdcmem, DIBINDEX(1), c1, 0, 1);
+ test_color(hdcmem, DIBINDEX(2), c0, 1, 1);
+ test_color(hdcmem, PALETTEINDEX(0), c0, 0, 1);
+ test_color(hdcmem, PALETTEINDEX(1), c1, 0, 1);
+ test_color(hdcmem, PALETTEINDEX(2), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(plogpal->palPalEntry[0].peRed, plogpal->palPalEntry[0].peGreen,
+ plogpal->palPalEntry[0].peBlue), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(plogpal->palPalEntry[1].peRed, plogpal->palPalEntry[1].peGreen,
+ plogpal->palPalEntry[1].peBlue), c1, 1, 1);
+ test_color(hdcmem, PALETTERGB(0, 0, 0), c1, 1, 1);
+ test_color(hdcmem, PALETTERGB(0xff, 0xff, 0xff), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(0, 0, 0xfe), c0, 1, 1);
+ test_color(hdcmem, PALETTERGB(0, 1, 0), c1, 1, 1);
+ test_color(hdcmem, PALETTERGB(0x3f, 0, 0x3f), c1, 1, 1);
+ test_color(hdcmem, PALETTERGB(0x40, 0, 0x40), c0, 1, 1);
+
+ /* Bottom and 2nd row from top green, everything else magenta */
+ bits[0] = bits[1] = 0xff;
+ bits[13 * 4] = bits[13*4 + 1] = 0xff;
+
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+
+ pbmi->bmiHeader.biBitCount = 32;
+
+ hdib2 = CreateDIBSection(NULL, pbmi, DIB_RGB_COLORS, (void **)&bits32, NULL, 0);
+ ok(hdib2 != NULL, "CreateDIBSection failed\n");
+ hdcmem2 = CreateCompatibleDC(hdc);
+ oldbm2 = SelectObject(hdcmem2, hdib2);
+
+ BitBlt(hdcmem2, 0, 0, 16,16, hdcmem, 0, 0, SRCCOPY);
+
+ ok(bits32[0] == 0xff00, "lower left pixel is %08lx\n", bits32[0]);
+ ok(bits32[17] == 0xff00ff, "bottom but one, left pixel is %08lx\n", bits32[17]);
+
+ SelectObject(hdcmem2, oldbm2);
+ test_dib_info(hdib2, bits32, &pbmi->bmiHeader);
+ DeleteObject(hdib2);
+
+ SelectObject(hdcmem, oldbm);
+ SelectObject(hdcmem, oldpal);
+ DeleteObject(hdib);
+ DeleteObject(hpal);
+
+
+ pbmi->bmiHeader.biBitCount = 8;
+
+ memset(plogpal, 0, sizeof(logpalbuf));
+ plogpal->palVersion = 0x300;
+ plogpal->palNumEntries = 256;
+
+ for (i = 0; i < 128; i++) {
+ plogpal->palPalEntry[i].peRed = 255 - i * 2;
+ plogpal->palPalEntry[i].peBlue = i * 2;
+ plogpal->palPalEntry[i].peGreen = 0;
+ plogpal->palPalEntry[255 - i].peRed = 0;
+ plogpal->palPalEntry[255 - i].peGreen = i * 2;
+ plogpal->palPalEntry[255 - i].peBlue = 255 - i * 2;
+ }
+
+ index = (WORD*)pbmi->bmiColors;
+ for (i = 0; i < 256; i++) {
+ *index++ = i;
+ }
+
+ hpal = CreatePalette(plogpal);
+ ok(hpal != NULL, "CreatePalette failed\n");
+ oldpal = SelectPalette(hdc, hpal, TRUE);
+ hdib = CreateDIBSection(hdc, pbmi, DIB_PAL_COLORS, (void**)&bits, NULL, 0);
+ ok(hdib != NULL, "CreateDIBSection failed\n");
+ ok(GetObject(hdib, sizeof(DIBSECTION), &dibsec) != 0, "GetObject failed for DIB Section\n");
+ ok(dibsec.dsBmih.biClrUsed == 256,
+ "created DIBSection: wrong biClrUsed field: %lu, should be: %u\n", dibsec.dsBmih.biClrUsed, 256);
+
+ test_dib_info(hdib, bits, &pbmi->bmiHeader);
+
+ SelectPalette(hdc, oldpal, TRUE);
+ oldbm = SelectObject(hdcmem, hdib);
+ oldpal = SelectPalette(hdcmem, hpal, TRUE);
+
+ ret = GetDIBColorTable(hdcmem, 0, 256, rgb);
+ ok(ret == 256, "GetDIBColorTable returned %d\n", ret);
+ for (i = 0; i < 256; i++) {
+ ok(rgb[i].rgbRed == plogpal->palPalEntry[i].peRed &&
+ rgb[i].rgbBlue == plogpal->palPalEntry[i].peBlue &&
+ rgb[i].rgbGreen == plogpal->palPalEntry[i].peGreen,
+ "GetDIBColorTable returns table %d: r%02x g%02x b%02x res%02x\n",
+ i, rgb[i].rgbRed, rgb[i].rgbGreen, rgb[i].rgbBlue, rgb[i].rgbReserved);
+ }
+
+ for (i = 0; i < 256; i++) {
+ test_color(hdcmem, DIBINDEX(i),
+ RGB(plogpal->palPalEntry[i].peRed, plogpal->palPalEntry[i].peGreen, plogpal->palPalEntry[i].peBlue), 0, 0);
+ test_color(hdcmem, PALETTEINDEX(i),
+ RGB(plogpal->palPalEntry[i].peRed, plogpal->palPalEntry[i].peGreen, plogpal->palPalEntry[i].peBlue), 0, 0);
+ test_color(hdcmem, PALETTERGB(plogpal->palPalEntry[i].peRed, plogpal->palPalEntry[i].peGreen, plogpal->palPalEntry[i].peBlue),
+ RGB(plogpal->palPalEntry[i].peRed, plogpal->palPalEntry[i].peGreen, plogpal->palPalEntry[i].peBlue), 0, 0);
+ }
+
+ SelectPalette(hdcmem, oldpal, TRUE);
+ SelectObject(hdcmem, oldbm);
+ DeleteObject(hdib);
+ DeleteObject(hpal);
+
+
+ DeleteDC(hdcmem);
+ ReleaseDC(0, hdc);
+}
+
+void test_mono_dibsection(void)
+{
+ HDC hdc, memdc;
+ HBITMAP old_bm, mono_ds;
+ char bmibuf[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)];
+ BITMAPINFO *pbmi = (BITMAPINFO *)bmibuf;
+ BYTE bits[10 * 4];
+ BYTE *ds_bits;
+ int num;
+
+ hdc = GetDC(0);
+
+ memdc = CreateCompatibleDC(hdc);
+
+ memset(pbmi, 0, sizeof(bmibuf));
+ pbmi->bmiHeader.biSize = sizeof(pbmi->bmiHeader);
+ pbmi->bmiHeader.biHeight = 10;
+ pbmi->bmiHeader.biWidth = 10;
+ pbmi->bmiHeader.biBitCount = 1;
+ pbmi->bmiHeader.biPlanes = 1;
+ pbmi->bmiHeader.biCompression = BI_RGB;
+ pbmi->bmiColors[0].rgbRed = 0xff;
+ pbmi->bmiColors[0].rgbGreen = 0xff;
+ pbmi->bmiColors[0].rgbBlue = 0xff;
+ pbmi->bmiColors[1].rgbRed = 0x0;
+ pbmi->bmiColors[1].rgbGreen = 0x0;
+ pbmi->bmiColors[1].rgbBlue = 0x0;
+
+ /*
+ * First dib section is 'inverted' ie color[0] is white, color[1] is black
+ */
+
+ mono_ds = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&ds_bits, NULL, 0);
+ ok(mono_ds != NULL, "CreateDIBSection rets NULL\n");
+ old_bm = SelectObject(memdc, mono_ds);
+
+ /* black border, white interior */
+ Rectangle(memdc, 0, 0, 10, 10);
+ ok(ds_bits[0] == 0xff, "out_bits %02x\n", ds_bits[0]);
+ ok(ds_bits[4] == 0x80, "out_bits %02x\n", ds_bits[4]);
+
+ /* SetDIBitsToDevice with an inverted bmi -> inverted dib section */
+
+ memset(bits, 0, sizeof(bits));
+ bits[0] = 0xaa;
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0xaa, "out_bits %02x\n", ds_bits[0]);
+
+ /* SetDIBitsToDevice with a normal bmi -> inverted dib section */
+
+ pbmi->bmiColors[0].rgbRed = 0x0;
+ pbmi->bmiColors[0].rgbGreen = 0x0;
+ pbmi->bmiColors[0].rgbBlue = 0x0;
+ pbmi->bmiColors[1].rgbRed = 0xff;
+ pbmi->bmiColors[1].rgbGreen = 0xff;
+ pbmi->bmiColors[1].rgbBlue = 0xff;
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0x55, "out_bits %02x\n", ds_bits[0]);
+
+ SelectObject(memdc, old_bm);
+ DeleteObject(mono_ds);
+
+ /*
+ * Next dib section is 'normal' ie color[0] is black, color[1] is white
+ */
+
+ pbmi->bmiColors[0].rgbRed = 0x0;
+ pbmi->bmiColors[0].rgbGreen = 0x0;
+ pbmi->bmiColors[0].rgbBlue = 0x0;
+ pbmi->bmiColors[1].rgbRed = 0xff;
+ pbmi->bmiColors[1].rgbGreen = 0xff;
+ pbmi->bmiColors[1].rgbBlue = 0xff;
+
+ mono_ds = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&ds_bits, NULL, 0);
+ ok(mono_ds != NULL, "CreateDIBSection rets NULL\n");
+ old_bm = SelectObject(memdc, mono_ds);
+
+ /* black border, white interior */
+ Rectangle(memdc, 0, 0, 10, 10);
+ ok(ds_bits[0] == 0x00, "out_bits %02x\n", ds_bits[0]);
+ ok(ds_bits[4] == 0x7f, "out_bits %02x\n", ds_bits[4]);
+
+ /* SetDIBitsToDevice with a normal bmi -> normal dib section */
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0xaa, "out_bits %02x\n", ds_bits[0]);
+
+ /* SetDIBitsToDevice with a inverted bmi -> normal dib section */
+
+ pbmi->bmiColors[0].rgbRed = 0xff;
+ pbmi->bmiColors[0].rgbGreen = 0xff;
+ pbmi->bmiColors[0].rgbBlue = 0xff;
+ pbmi->bmiColors[1].rgbRed = 0x0;
+ pbmi->bmiColors[1].rgbGreen = 0x0;
+ pbmi->bmiColors[1].rgbBlue = 0x0;
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0x55, "out_bits %02x\n", ds_bits[0]);
+
+ /*
+ * Take that 'normal' dibsection and change its colour table to an 'inverted' one
+ */
+
+ pbmi->bmiColors[0].rgbRed = 0xff;
+ pbmi->bmiColors[0].rgbGreen = 0xff;
+ pbmi->bmiColors[0].rgbBlue = 0xff;
+ pbmi->bmiColors[1].rgbRed = 0x0;
+ pbmi->bmiColors[1].rgbGreen = 0x0;
+ pbmi->bmiColors[1].rgbBlue = 0x0;
+ num = SetDIBColorTable(memdc, 0, 2, pbmi->bmiColors);
+ ok(num == 2, "num = %d\n", num);
+
+ /* black border, white interior */
+ Rectangle(memdc, 0, 0, 10, 10);
+todo_wine {
+ ok(ds_bits[0] == 0xff, "out_bits %02x\n", ds_bits[0]);
+ ok(ds_bits[4] == 0x80, "out_bits %02x\n", ds_bits[4]);
+ }
+ /* SetDIBitsToDevice with an inverted bmi -> inverted dib section */
+
+ memset(bits, 0, sizeof(bits));
+ bits[0] = 0xaa;
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0xaa, "out_bits %02x\n", ds_bits[0]);
+
+ /* SetDIBitsToDevice with a normal bmi -> inverted dib section */
+
+ pbmi->bmiColors[0].rgbRed = 0x0;
+ pbmi->bmiColors[0].rgbGreen = 0x0;
+ pbmi->bmiColors[0].rgbBlue = 0x0;
+ pbmi->bmiColors[1].rgbRed = 0xff;
+ pbmi->bmiColors[1].rgbGreen = 0xff;
+ pbmi->bmiColors[1].rgbBlue = 0xff;
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0x55, "out_bits %02x\n", ds_bits[0]);
+
+ SelectObject(memdc, old_bm);
+ DeleteObject(mono_ds);
+
+ /*
+ * Now a dib section with a strange colour map just for fun. This behaves just like an inverted one.
+ */
+
+ pbmi->bmiColors[0].rgbRed = 0xff;
+ pbmi->bmiColors[0].rgbGreen = 0x0;
+ pbmi->bmiColors[0].rgbBlue = 0x0;
+ pbmi->bmiColors[1].rgbRed = 0xfe;
+ pbmi->bmiColors[1].rgbGreen = 0x0;
+ pbmi->bmiColors[1].rgbBlue = 0x0;
+
+ mono_ds = CreateDIBSection(hdc, pbmi, DIB_RGB_COLORS, (void**)&ds_bits, NULL, 0);
+ ok(mono_ds != NULL, "CreateDIBSection rets NULL\n");
+ old_bm = SelectObject(memdc, mono_ds);
+
+ /* black border, white interior */
+ Rectangle(memdc, 0, 0, 10, 10);
+ ok(ds_bits[0] == 0xff, "out_bits %02x\n", ds_bits[0]);
+ ok(ds_bits[4] == 0x80, "out_bits %02x\n", ds_bits[4]);
+
+ /* SetDIBitsToDevice with a normal bmi -> inverted dib section */
+
+ pbmi->bmiColors[0].rgbRed = 0x0;
+ pbmi->bmiColors[0].rgbGreen = 0x0;
+ pbmi->bmiColors[0].rgbBlue = 0x0;
+ pbmi->bmiColors[1].rgbRed = 0xff;
+ pbmi->bmiColors[1].rgbGreen = 0xff;
+ pbmi->bmiColors[1].rgbBlue = 0xff;
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0x55, "out_bits %02x\n", ds_bits[0]);
+
+ /* SetDIBitsToDevice with a inverted bmi -> inverted dib section */
+
+ pbmi->bmiColors[0].rgbRed = 0xff;
+ pbmi->bmiColors[0].rgbGreen = 0xff;
+ pbmi->bmiColors[0].rgbBlue = 0xff;
+ pbmi->bmiColors[1].rgbRed = 0x0;
+ pbmi->bmiColors[1].rgbGreen = 0x0;
+ pbmi->bmiColors[1].rgbBlue = 0x0;
+
+ SetDIBitsToDevice(memdc, 0, 0, 10, 10, 0, 0, 0, 10, bits, pbmi, DIB_RGB_COLORS);
+ ok(ds_bits[0] == 0xaa, "out_bits %02x\n", ds_bits[0]);
+
+ SelectObject(memdc, old_bm);
+ DeleteObject(mono_ds);
+
+ DeleteDC(memdc);
+ ReleaseDC(0, hdc);
+}
+
+static void test_bitmap(void)
+{
+ char buf[256], buf_cmp[256];
+ HBITMAP hbmp, hbmp_old;
+ HDC hdc;
+ BITMAP bm;
+ INT ret;
+
+ hdc = CreateCompatibleDC(0);
+ assert(hdc != 0);
+
+ hbmp = CreateBitmap(15, 15, 1, 1, NULL);
+ assert(hbmp != NULL);
+
+ ret = GetObject(hbmp, sizeof(bm), &bm);
+ ok(ret == sizeof(bm), "wrong size %d\n", ret);
+
+ ok(bm.bmType == 0, "wrong bm.bmType %d\n", bm.bmType);
+ ok(bm.bmWidth == 15, "wrong bm.bmWidth %d\n", bm.bmWidth);
+ ok(bm.bmHeight == 15, "wrong bm.bmHeight %d\n", bm.bmHeight);
+ ok(bm.bmWidthBytes == 2, "wrong bm.bmWidthBytes %d\n", bm.bmWidthBytes);
+ ok(bm.bmPlanes == 1, "wrong bm.bmPlanes %d\n", bm.bmPlanes);
+ ok(bm.bmBitsPixel == 1, "wrong bm.bmBitsPixel %d\n", bm.bmBitsPixel);
+ ok(bm.bmBits == NULL, "wrong bm.bmBits %p\n", bm.bmBits);
+
+ assert(sizeof(buf) >= bm.bmWidthBytes * bm.bmHeight);
+ assert(sizeof(buf) == sizeof(buf_cmp));
+
+ ret = GetBitmapBits(hbmp, 0, NULL);
+ ok(ret == bm.bmWidthBytes * bm.bmHeight, "%d != %d\n", ret, bm.bmWidthBytes * bm.bmHeight);
+
+ memset(buf_cmp, 0xAA, sizeof(buf_cmp));
+ memset(buf_cmp, 0, bm.bmWidthBytes * bm.bmHeight);
+
+ memset(buf, 0xAA, sizeof(buf));
+ ret = GetBitmapBits(hbmp, sizeof(buf), buf);
+ ok(ret == bm.bmWidthBytes * bm.bmHeight, "%d != %d\n", ret, bm.bmWidthBytes * bm.bmHeight);
+ ok(!memcmp(buf, buf_cmp, sizeof(buf)), "buffers do not match\n");
+
+ hbmp_old = SelectObject(hdc, hbmp);
+
+ ret = GetObject(hbmp, sizeof(bm), &bm);
+ ok(ret == sizeof(bm), "wrong size %d\n", ret);
+
+ ok(bm.bmType == 0, "wrong bm.bmType %d\n", bm.bmType);
+ ok(bm.bmWidth == 15, "wrong bm.bmWidth %d\n", bm.bmWidth);
+ ok(bm.bmHeight == 15, "wrong bm.bmHeight %d\n", bm.bmHeight);
+ ok(bm.bmWidthBytes == 2, "wrong bm.bmWidthBytes %d\n", bm.bmWidthBytes);
+ ok(bm.bmPlanes == 1, "wrong bm.bmPlanes %d\n", bm.bmPlanes);
+ ok(bm.bmBitsPixel == 1, "wrong bm.bmBitsPixel %d\n", bm.bmBitsPixel);
+ ok(bm.bmBits == NULL, "wrong bm.bmBits %p\n", bm.bmBits);
+
+ memset(buf, 0xAA, sizeof(buf));
+ ret = GetBitmapBits(hbmp, sizeof(buf), buf);
+ ok(ret == bm.bmWidthBytes * bm.bmHeight, "%d != %d\n", ret, bm.bmWidthBytes * bm.bmHeight);
+ ok(!memcmp(buf, buf_cmp, sizeof(buf)), "buffers do not match\n");
+
+ hbmp_old = SelectObject(hdc, hbmp_old);
+ ok(hbmp_old == hbmp, "wrong old bitmap %p\n", hbmp_old);
+
+ /* test various buffer sizes for GetObject */
+ ret = GetObject(hbmp, sizeof(bm) * 2, &bm);
+ ok(ret == sizeof(bm), "wrong size %d\n", ret);
+
+ ret = GetObject(hbmp, sizeof(bm) / 2, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ ret = GetObject(hbmp, 0, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ ret = GetObject(hbmp, 1, &bm);
+ ok(ret == 0, "%d != 0\n", ret);
+
+ DeleteObject(hbmp);
+ DeleteDC(hdc);
+}
+
+static void test_bmBits(void)
+{
+ BYTE bits[4];
+ HBITMAP hbmp;
+ BITMAP bmp;
+
+ memset(bits, 0, sizeof(bits));
+ hbmp = CreateBitmap(2, 2, 1, 4, bits);
+ ok(hbmp != NULL, "CreateBitmap failed\n");
+
+ memset(&bmp, 0xFF, sizeof(bmp));
+ ok(GetObject(hbmp, sizeof(bmp), &bmp) == sizeof(bmp),
+ "GetObject failed or returned a wrong structure size\n");
+ ok(!bmp.bmBits, "bmBits must be NULL for device-dependent bitmaps\n");
+
+ DeleteObject(hbmp);
+}
+
+static void test_GetDIBits_selected_DIB(UINT bpp)
+{
+ HBITMAP dib;
+ BITMAPINFO * info;
+ BITMAPINFO * info2;
+ void * bits;
+ void * bits2;
+ UINT dib_size;
+ HDC dib_dc, dc;
+ HBITMAP old_bmp;
+ BOOL equalContents;
+ UINT i;
+ int res;
+
+ /* Create a DIB section with a color table */
+
+ info = (BITMAPINFO *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + (1 << bpp) * sizeof(RGBQUAD));
+ info2 = (BITMAPINFO *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + (1 << bpp) * sizeof(RGBQUAD));
+ assert(info);
+ assert(info2);
+
+ info->bmiHeader.biSize = sizeof(info->bmiHeader);
+
+ /* Choose width and height such that the row length (in bytes)
+ is a multiple of 4 (makes things easier) */
+ info->bmiHeader.biWidth = 32;
+ info->bmiHeader.biHeight = 32;
+ info->bmiHeader.biPlanes = 1;
+ info->bmiHeader.biBitCount = bpp;
+ info->bmiHeader.biCompression = BI_RGB;
+
+ for (i=0; i < (1 << bpp); i++)
+ {
+ BYTE c = i * (1 << (8 - bpp));
+ info->bmiColors[i].rgbRed = c;
+ info->bmiColors[i].rgbGreen = c;
+ info->bmiColors[i].rgbBlue = c;
+ info->bmiColors[i].rgbReserved = 0;
+ }
+
+ dib = CreateDIBSection(NULL, info, DIB_RGB_COLORS, &bits, NULL, 0);
+ assert(dib);
+ dib_size = bpp * (info->bmiHeader.biWidth * info->bmiHeader.biHeight) / 8;
+
+ /* Set the bits of the DIB section */
+ for (i=0; i < dib_size; i++)
+ {
+ ((BYTE *)bits)[i] = i % 256;
+ }
+
+ /* Select the DIB into a DC */
+ dib_dc = CreateCompatibleDC(NULL);
+ old_bmp = (HBITMAP) SelectObject(dib_dc, dib);
+ dc = CreateCompatibleDC(NULL);
+ bits2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dib_size);
+ assert(bits2);
+
+ /* Copy the DIB attributes but not the color table */
+ memcpy(info2, info, sizeof(BITMAPINFOHEADER));
+
+ res = GetDIBits(dc, dib, 0, info->bmiHeader.biHeight, bits2, info2, DIB_RGB_COLORS);
+ ok(res, "GetDIBits failed\n");
+
+ /* Compare the color table and the bits */
+ equalContents = TRUE;
+ for (i=0; i < (1 << bpp); i++)
+ {
+ if ((info->bmiColors[i].rgbRed != info2->bmiColors[i].rgbRed)
+ || (info->bmiColors[i].rgbGreen != info2->bmiColors[i].rgbGreen)
+ || (info->bmiColors[i].rgbBlue != info2->bmiColors[i].rgbBlue)
+ || (info->bmiColors[i].rgbReserved != info2->bmiColors[i].rgbReserved))
+ {
+ equalContents = FALSE;
+ break;
+ }
+ }
+ todo_wine
+ {
+ ok(equalContents, "GetDIBits with DIB selected in DC: Invalid DIB color table\n");
+ }
+
+ equalContents = TRUE;
+ for (i=0; i < dib_size / sizeof(DWORD); i++)
+ {
+ if (((DWORD *)bits)[i] != ((DWORD *)bits2)[i])
+ {
+ equalContents = FALSE;
+ break;
+ }
+ }
+ todo_wine
+ {
+ ok(equalContents, "GetDIBits with DIB selected in DC: Invalid DIB bits\n");
+ }
+
+ HeapFree(GetProcessHeap(), 0, bits2);
+ DeleteDC(dc);
+
+ SelectObject(dib_dc, old_bmp);
+ DeleteDC(dib_dc);
+ DeleteObject(dib);
+
+ HeapFree(GetProcessHeap(), 0, info2);
+ HeapFree(GetProcessHeap(), 0, info);
+}
+
+static void test_GetDIBits_selected_DDB(BOOL monochrome)
+{
+ HBITMAP ddb;
+ BITMAPINFO * info;
+ BITMAPINFO * info2;
+ void * bits;
+ void * bits2;
+ HDC ddb_dc, dc;
+ HBITMAP old_bmp;
+ BOOL equalContents;
+ UINT width, height;
+ UINT bpp;
+ UINT i, j;
+ int res;
+
+ width = height = 16;
+
+ /* Create a DDB (device-dependent bitmap) */
+ if (monochrome)
+ {
+ bpp = 1;
+ ddb = CreateBitmap(width, height, 1, 1, NULL);
+ }
+ else
+ {
+ HDC screen_dc = GetDC(NULL);
+ bpp = GetDeviceCaps(screen_dc, BITSPIXEL) * GetDeviceCaps(screen_dc, PLANES);
+ ddb = CreateCompatibleBitmap(screen_dc, width, height);
+ ReleaseDC(NULL, screen_dc);
+ }
+
+ /* Set the pixels */
+ ddb_dc = CreateCompatibleDC(NULL);
+ old_bmp = (HBITMAP) SelectObject(ddb_dc, ddb);
+ for (i = 0; i < width; i++)
+ {
+ for (j=0; j < height; j++)
+ {
+ BYTE c = (i * width + j) % 256;
+ SetPixelV(ddb_dc, i, j, RGB(c, c, c));
+ }
+ }
+ SelectObject(ddb_dc, old_bmp);
+
+ info = (BITMAPINFO *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
+ info2 = (BITMAPINFO *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
+ assert(info);
+ assert(info2);
+
+ info->bmiHeader.biSize = sizeof(info->bmiHeader);
+ info->bmiHeader.biWidth = width;
+ info->bmiHeader.biHeight = height;
+ info->bmiHeader.biPlanes = 1;
+ info->bmiHeader.biBitCount = bpp;
+ info->bmiHeader.biCompression = BI_RGB;
+
+ dc = CreateCompatibleDC(NULL);
+
+ /* Fill in biSizeImage */
+ GetDIBits(dc, ddb, 0, height, NULL, info, DIB_RGB_COLORS);
+ ok(info->bmiHeader.biSizeImage != 0, "GetDIBits failed to get the DIB attributes\n");
+
+ bits = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, info->bmiHeader.biSizeImage);
+ bits2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, info->bmiHeader.biSizeImage);
+ assert(bits);
+ assert(bits2);
+
+ /* Get the bits */
+ res = GetDIBits(dc, ddb, 0, height, bits, info, DIB_RGB_COLORS);
+ ok(res, "GetDIBits failed\n");
+
+ /* Copy the DIB attributes but not the color table */
+ memcpy(info2, info, sizeof(BITMAPINFOHEADER));
+
+ /* Select the DDB into another DC */
+ old_bmp = (HBITMAP) SelectObject(ddb_dc, ddb);
+
+ /* Get the bits */
+ res = GetDIBits(dc, ddb, 0, height, bits2, info2, DIB_RGB_COLORS);
+ ok(res, "GetDIBits failed\n");
+
+ /* Compare the color table and the bits */
+ if (bpp <= 8)
+ {
+ equalContents = TRUE;
+ for (i=0; i < (1 << bpp); i++)
+ {
+ if ((info->bmiColors[i].rgbRed != info2->bmiColors[i].rgbRed)
+ || (info->bmiColors[i].rgbGreen != info2->bmiColors[i].rgbGreen)
+ || (info->bmiColors[i].rgbBlue != info2->bmiColors[i].rgbBlue)
+ || (info->bmiColors[i].rgbReserved != info2->bmiColors[i].rgbReserved))
+ {
+ equalContents = FALSE;
+ break;
+ }
+ }
+ ok(equalContents, "GetDIBits with DDB selected in DC: Got a different color table\n");
+ }
+
+ equalContents = TRUE;
+ for (i=0; i < info->bmiHeader.biSizeImage / sizeof(DWORD); i++)
+ {
+ if (((DWORD *)bits)[i] != ((DWORD *)bits2)[i])
+ {
+ equalContents = FALSE;
+ }
+ }
+ ok(equalContents, "GetDIBits with DDB selected in DC: Got different DIB bits\n");
+
+ HeapFree(GetProcessHeap(), 0, bits2);
+ HeapFree(GetProcessHeap(), 0, bits);
+ DeleteDC(dc);
+
+ SelectObject(ddb_dc, old_bmp);
+ DeleteDC(ddb_dc);
+ DeleteObject(ddb);
+
+ HeapFree(GetProcessHeap(), 0, info2);
+ HeapFree(GetProcessHeap(), 0, info);
+}
+
+START_TEST(bitmap)
+{
+ is_win9x = GetWindowLongPtrW(GetDesktopWindow(), GWLP_WNDPROC) == 0;
+
+ test_createdibitmap();
+ test_dibsections();
+ test_mono_dibsection();
+ test_bitmap();
+ test_bmBits();
+ test_GetDIBits_selected_DIB(1);
+ test_GetDIBits_selected_DIB(4);
+ test_GetDIBits_selected_DIB(8);
+ test_GetDIBits_selected_DDB(TRUE);
+ test_GetDIBits_selected_DDB(FALSE);
+}
--- /dev/null
+/*
+ * Unit test suite for brushes
+ *
+ * Copyright 2004 Kevin Koltzau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+
+#include "wine/test.h"
+
+typedef struct _STOCK_BRUSH {
+ COLORREF color;
+ int stockobj;
+ const char *name;
+} STOCK_BRUSH;
+
+static void test_solidbrush(void)
+{
+ static const STOCK_BRUSH stock[] = {
+ {RGB(255,255,255), WHITE_BRUSH, "white"},
+ {RGB(192,192,192), LTGRAY_BRUSH, "ltgray"},
+ {RGB(128,128,128), GRAY_BRUSH, "gray"},
+ {RGB(0,0,0), BLACK_BRUSH, "black"},
+ {RGB(0,0,255), -1, "blue"}
+ };
+ HBRUSH solidBrush;
+ HBRUSH stockBrush;
+ LOGBRUSH br;
+ size_t i;
+ INT ret;
+
+ for(i=0; i<sizeof(stock)/sizeof(stock[0]); i++) {
+ solidBrush = CreateSolidBrush(stock[i].color);
+
+ if(stock[i].stockobj != -1) {
+ stockBrush = (HBRUSH)GetStockObject(stock[i].stockobj);
+ ok(stockBrush!=solidBrush, "Stock %s brush equals solid %s brush\n", stock[i].name, stock[i].name);
+ }
+ else
+ stockBrush = NULL;
+ memset(&br, 0, sizeof(br));
+ ret = GetObject(solidBrush, sizeof(br), &br);
+ ok( ret !=0, "GetObject on solid %s brush failed, error=%ld\n", stock[i].name, GetLastError());
+ ok(br.lbStyle==BS_SOLID, "%s brush has wrong style, got %d expected %d\n", stock[i].name, br.lbStyle, BS_SOLID);
+ ok(br.lbColor==stock[i].color, "%s brush has wrong color, got 0x%08lx expected 0x%08lx\n", stock[i].name, br.lbColor, stock[i].color);
+
+ if(stockBrush) {
+ /* Sanity check, make sure the colors being compared do in fact have a stock brush */
+ ret = GetObject(stockBrush, sizeof(br), &br);
+ ok( ret !=0, "GetObject on stock %s brush failed, error=%ld\n", stock[i].name, GetLastError());
+ ok(br.lbColor==stock[i].color, "stock %s brush unexpected color, got 0x%08lx expected 0x%08lx\n", stock[i].name, br.lbColor, stock[i].color);
+ }
+
+ DeleteObject(solidBrush);
+ ok(GetObject(solidBrush, sizeof(br), &br)==0, "GetObject succeeded on a deleted %s brush\n", stock[i].name);
+ }
+}
+
+START_TEST(brush)
+{
+ test_solidbrush();
+}
--- /dev/null
+/*
+ * Unit test suite for clipping
+ *
+ * Copyright 2005 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+static void test_GetRandomRgn(void)
+{
+ HWND hwnd = CreateWindowExA(0,"BUTTON","test",WS_VISIBLE|WS_POPUP,0,0,100,100,GetDesktopWindow(),0,0,0);
+ HDC hdc;
+ HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
+ int ret;
+ RECT rc, rc2;
+ RECT ret_rc, window_rc;
+
+ ok( hwnd != 0, "CreateWindow failed\n" );
+
+ SetRect(&window_rc, 400, 300, 500, 400);
+ MoveWindow(hwnd, window_rc.left, window_rc.top, window_rc.right - window_rc.left, window_rc.bottom - window_rc.top, FALSE);
+ hdc = GetDC(hwnd);
+
+ ret = GetRandomRgn(hdc, hrgn, 1);
+ ok(ret == 0, "GetRandomRgn rets %d\n", ret);
+ ret = GetRandomRgn(hdc, hrgn, 2);
+ ok(ret == 0, "GetRandomRgn rets %d\n", ret);
+ ret = GetRandomRgn(hdc, hrgn, 3);
+ ok(ret == 0, "GetRandomRgn rets %d\n", ret);
+
+ /* Set a clip region */
+ SetRect(&rc, 20, 20, 80, 80);
+ IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
+
+ ret = GetRandomRgn(hdc, hrgn, 1);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ ok(EqualRect(&rc, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+ ret = GetRandomRgn(hdc, hrgn, 2);
+ ok(ret == 0, "GetRandomRgn rets %d\n", ret);
+
+ ret = GetRandomRgn(hdc, hrgn, 3);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ ok(EqualRect(&rc, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+ /* Move the clip to the meta and clear the clip */
+ SetMetaRgn(hdc);
+
+ ret = GetRandomRgn(hdc, hrgn, 1);
+ ok(ret == 0, "GetRandomRgn rets %d\n", ret);
+ ret = GetRandomRgn(hdc, hrgn, 2);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ ok(EqualRect(&rc, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+ ret = GetRandomRgn(hdc, hrgn, 3);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ ok(EqualRect(&rc, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+ /* Set a new clip (still got the meta) */
+ SetRect(&rc2, 10, 30, 70, 90);
+ IntersectClipRect(hdc, rc2.left, rc2.top, rc2.right, rc2.bottom);
+
+ ret = GetRandomRgn(hdc, hrgn, 1);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ ok(EqualRect(&rc2, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+ ret = GetRandomRgn(hdc, hrgn, 2);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ ok(EqualRect(&rc, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+ IntersectRect(&rc2, &rc, &rc2);
+
+ ret = GetRandomRgn(hdc, hrgn, 3);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ ok(EqualRect(&rc2, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+
+ ret = GetRandomRgn(hdc, hrgn, SYSRGN);
+ ok(ret != 0, "GetRandomRgn rets %d\n", ret);
+ GetRgnBox(hrgn, &ret_rc);
+ if(GetVersion() & 0x80000000)
+ OffsetRect(&window_rc, -window_rc.left, -window_rc.top);
+ ok(EqualRect(&window_rc, &ret_rc), "GetRandomRgn %ld,%ld - %ld,%ld\n",
+ ret_rc.left, ret_rc.top, ret_rc.right, ret_rc.bottom);
+
+ DeleteObject(hrgn);
+ ReleaseDC(hwnd, hdc);
+ DestroyWindow(hwnd);
+}
+
+START_TEST(clipping)
+{
+ test_GetRandomRgn();
+}
--- /dev/null
+/*
+ * Unit tests for dc functions
+ *
+ * Copyright (c) 2005 Huw Davies
+ * Copyright (c) 2005 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+
+static void dump_region(HRGN hrgn)
+{
+ DWORD i, size;
+ RGNDATA *data = NULL;
+ RECT *rect;
+
+ if (!hrgn)
+ {
+ printf( "(null) region\n" );
+ return;
+ }
+ if (!(size = GetRegionData( hrgn, 0, NULL ))) return;
+ if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return;
+ GetRegionData( hrgn, size, data );
+ printf( "%ld rects:", data->rdh.nCount );
+ for (i = 0, rect = (RECT *)data->Buffer; i < data->rdh.nCount; i++, rect++)
+ printf( " (%ld,%ld)-(%ld,%ld)", rect->left, rect->top, rect->right, rect->bottom );
+ printf( "\n" );
+ HeapFree( GetProcessHeap(), 0, data );
+}
+
+static void test_savedc_2(void)
+{
+ HWND hwnd;
+ HDC hdc;
+ HRGN hrgn;
+ RECT rc, rc_clip;
+ int ret;
+
+ hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,
+ 0, 0, 0, NULL);
+ assert(hwnd != 0);
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow(hwnd);
+
+ hrgn = CreateRectRgn(0, 0, 0, 0);
+ assert(hrgn != 0);
+
+ hdc = GetDC(hwnd);
+ ok(hdc != NULL, "CreateDC rets %p\n", hdc);
+
+ ret = GetClipBox(hdc, &rc_clip);
+ ok(ret == SIMPLEREGION, "GetClipBox returned %d instead of SIMPLEREGION\n", ret);
+ ret = GetClipRgn(hdc, hrgn);
+ ok(ret == 0, "GetClipRgn returned %d instead of 0\n", ret);
+ ret = GetRgnBox(hrgn, &rc);
+ ok(ret == NULLREGION, "GetRgnBox returned %d (%ld,%ld-%ld,%ld) instead of NULLREGION\n",
+ ret, rc.left, rc.top, rc.right, rc.bottom);
+ /*dump_region(hrgn);*/
+ SetRect(&rc, 0, 0, 100, 100);
+ ok(EqualRect(&rc, &rc_clip),
+ "rects are not equal: (%ld,%ld-%ld,%ld) - (%ld,%ld-%ld,%ld)\n",
+ rc.left, rc.top, rc.right, rc.bottom,
+ rc_clip.left, rc_clip.top, rc_clip.right, rc_clip.bottom);
+
+ ret = SaveDC(hdc);
+todo_wine
+{
+ ok(ret == 1, "ret = %d\n", ret);
+}
+
+ ret = IntersectClipRect(hdc, 0, 0, 50, 50);
+#if 0 /* XP returns COMPLEXREGION although dump_region reports only 1 rect */
+ ok(ret == SIMPLEREGION, "IntersectClipRect returned %d instead of SIMPLEREGION\n", ret);
+#endif
+ if (ret != SIMPLEREGION)
+ {
+ trace("Windows BUG: IntersectClipRect returned %d instead of SIMPLEREGION\n", ret);
+ /* let's make sure that it's a simple region */
+ ret = GetClipRgn(hdc, hrgn);
+ ok(ret == 1, "GetClipRgn returned %d instead of 1\n", ret);
+ dump_region(hrgn);
+ }
+
+ ret = GetClipBox(hdc, &rc_clip);
+ ok(ret == SIMPLEREGION, "GetClipBox returned %d instead of SIMPLEREGION\n", ret);
+ SetRect(&rc, 0, 0, 50, 50);
+ ok(EqualRect(&rc, &rc_clip), "rects are not equal\n");
+
+ ret = RestoreDC(hdc, 1);
+ ok(ret, "ret = %d\n", ret);
+
+ ret = GetClipBox(hdc, &rc_clip);
+ ok(ret == SIMPLEREGION, "GetClipBox returned %d instead of SIMPLEREGION\n", ret);
+ SetRect(&rc, 0, 0, 100, 100);
+ ok(EqualRect(&rc, &rc_clip), "rects are not equal\n");
+
+ DeleteObject(hrgn);
+ ReleaseDC(hwnd, hdc);
+ DestroyWindow(hwnd);
+}
+
+static void test_savedc(void)
+{
+ HDC hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);
+ int ret;
+
+ ok(hdc != NULL, "CreateDC rets %p\n", hdc);
+
+ ret = SaveDC(hdc);
+ ok(ret == 1, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 2, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 3, "ret = %d\n", ret);
+ ret = RestoreDC(hdc, -1);
+ ok(ret, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 3, "ret = %d\n", ret);
+ ret = RestoreDC(hdc, 1);
+ ok(ret, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 1, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 2, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 3, "ret = %d\n", ret);
+ ret = RestoreDC(hdc, -2);
+ ok(ret, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 2, "ret = %d\n", ret);
+ ret = RestoreDC(hdc, -2);
+ ok(ret, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 1, "ret = %d\n", ret);
+ ret = SaveDC(hdc);
+ ok(ret == 2, "ret = %d\n", ret);
+ ret = RestoreDC(hdc, -4);
+ ok(!ret, "ret = %d\n", ret);
+ ret = RestoreDC(hdc, 3);
+ ok(!ret, "ret = %d\n", ret);
+
+ /* Under win98 the following two succeed and both clear the save stack
+ ret = RestoreDC(hdc, -3);
+ ok(!ret, "ret = %d\n", ret);
+ ret = RestoreDC(hdc, 0);
+ ok(!ret, "ret = %d\n", ret);
+ */
+
+ ret = RestoreDC(hdc, 1);
+ ok(ret, "ret = %d\n", ret);
+
+ DeleteDC(hdc);
+}
+
+START_TEST(dc)
+{
+ test_savedc();
+ test_savedc_2();
+}
--- /dev/null
+/*
+ * Unit test suite for fonts
+ *
+ * Copyright 2002 Mike McCormack
+ * Copyright 2004 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <assert.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/test.h"
+
+static void check_font(const char* test, const LOGFONTA* lf, HFONT hfont)
+{
+ LOGFONTA getobj_lf;
+ int ret, minlen = 0;
+
+ if (!hfont)
+ return;
+
+ ret = GetObject(hfont, sizeof(getobj_lf), &getobj_lf);
+ /* NT4 tries to be clever and only returns the minimum length */
+ while (lf->lfFaceName[minlen] && minlen < LF_FACESIZE-1)
+ minlen++;
+ minlen += FIELD_OFFSET(LOGFONTA, lfFaceName) + 1;
+ ok(ret == sizeof(LOGFONTA) || ret == minlen, "%s: GetObject returned %d\n", test, ret);
+ ok(!memcmp(&lf, &lf, FIELD_OFFSET(LOGFONTA, lfFaceName)), "%s: fonts don't match\n", test);
+ ok(!lstrcmpA(lf->lfFaceName, getobj_lf.lfFaceName),
+ "%s: font names don't match: %s != %s\n", test, lf->lfFaceName, getobj_lf.lfFaceName);
+}
+
+static HFONT create_font(const char* test, const LOGFONTA* lf)
+{
+ HFONT hfont = CreateFontIndirectA(lf);
+ ok(hfont != 0, "%s: CreateFontIndirect failed\n", test);
+ if (hfont)
+ check_font(test, lf, hfont);
+ return hfont;
+}
+
+static void test_logfont(void)
+{
+ LOGFONTA lf;
+ HFONT hfont;
+
+ memset(&lf, 0, sizeof lf);
+
+ lf.lfCharSet = ANSI_CHARSET;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfWeight = FW_DONTCARE;
+ lf.lfHeight = 16;
+ lf.lfWidth = 16;
+ lf.lfQuality = DEFAULT_QUALITY;
+
+ lstrcpyA(lf.lfFaceName, "Arial");
+ hfont = create_font("Arial", &lf);
+ DeleteObject(hfont);
+
+ memset(&lf, 'A', sizeof(lf));
+ hfont = CreateFontIndirectA(&lf);
+ ok(hfont != 0, "CreateFontIndirectA with strange LOGFONT failed\n");
+
+ lf.lfFaceName[LF_FACESIZE - 1] = 0;
+ check_font("AAA...", &lf, hfont);
+ DeleteObject(hfont);
+}
+
+static INT CALLBACK font_enum_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
+{
+ if (type & RASTER_FONTTYPE)
+ {
+ LOGFONT *lf = (LOGFONT *)lParam;
+ *lf = *elf;
+ return 0; /* stop enumeration */
+ }
+
+ return 1; /* continue enumeration */
+}
+
+static void test_font_metrics(HDC hdc, HFONT hfont, const char *test_str,
+ INT test_str_len, const TEXTMETRICA *tm_orig,
+ const SIZE *size_orig, INT width_orig,
+ INT scale_x, INT scale_y)
+{
+ HFONT old_hfont;
+ TEXTMETRICA tm;
+ SIZE size;
+ INT width;
+
+ if (!hfont)
+ return;
+
+ old_hfont = SelectObject(hdc, hfont);
+
+ GetTextMetricsA(hdc, &tm);
+
+ ok(tm.tmHeight == tm_orig->tmHeight * scale_y, "%ld != %ld\n", tm.tmHeight, tm_orig->tmHeight * scale_y);
+ ok(tm.tmAscent == tm_orig->tmAscent * scale_y, "%ld != %ld\n", tm.tmAscent, tm_orig->tmAscent * scale_y);
+ ok(tm.tmDescent == tm_orig->tmDescent * scale_y, "%ld != %ld\n", tm.tmDescent, tm_orig->tmDescent * scale_y);
+ ok(tm.tmAveCharWidth == tm_orig->tmAveCharWidth * scale_x, "%ld != %ld\n", tm.tmAveCharWidth, tm_orig->tmAveCharWidth * scale_x);
+
+ GetTextExtentPoint32A(hdc, test_str, test_str_len, &size);
+
+ ok(size.cx == size_orig->cx * scale_x, "%ld != %ld\n", size.cx, size_orig->cx * scale_x);
+ ok(size.cy == size_orig->cy * scale_y, "%ld != %ld\n", size.cy, size_orig->cy * scale_y);
+
+ GetCharWidthA(hdc, 'A', 'A', &width);
+
+ ok(width == width_orig * scale_x, "%d != %d\n", width, width_orig * scale_x);
+
+ SelectObject(hdc, old_hfont);
+}
+
+/* see whether GDI scales bitmap font metrics */
+static void test_bitmap_font(void)
+{
+ static const char test_str[11] = "Test String";
+ HDC hdc;
+ LOGFONTA bitmap_lf;
+ HFONT hfont, old_hfont;
+ TEXTMETRICA tm_orig;
+ SIZE size_orig;
+ INT ret, i, width_orig, height_orig;
+
+ hdc = GetDC(0);
+
+ /* "System" has only 1 pixel size defined, otherwise the test breaks */
+ ret = EnumFontFamiliesA(hdc, "System", font_enum_proc, (LPARAM)&bitmap_lf);
+ if (ret)
+ {
+ ReleaseDC(0, hdc);
+ trace("no bitmap fonts were found, skipping the test\n");
+ return;
+ }
+
+ trace("found bitmap font %s, height %ld\n", bitmap_lf.lfFaceName, bitmap_lf.lfHeight);
+
+ height_orig = bitmap_lf.lfHeight;
+ hfont = create_font("bitmap", &bitmap_lf);
+
+ old_hfont = SelectObject(hdc, hfont);
+ ok(GetTextMetricsA(hdc, &tm_orig), "GetTextMetricsA failed\n");
+ ok(GetTextExtentPoint32A(hdc, test_str, sizeof(test_str), &size_orig), "GetTextExtentPoint32A failed\n");
+ ok(GetCharWidthA(hdc, 'A', 'A', &width_orig), "GetCharWidthA failed\n");
+ SelectObject(hdc, old_hfont);
+ DeleteObject(hfont);
+
+ /* test fractional scaling */
+ for (i = 1; i < height_orig; i++)
+ {
+ hfont = create_font("fractional", &bitmap_lf);
+ test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 1, 1);
+ DeleteObject(hfont);
+ }
+
+ /* test integer scaling 3x2 */
+ bitmap_lf.lfHeight = height_orig * 2;
+ bitmap_lf.lfWidth *= 3;
+ hfont = create_font("3x2", &bitmap_lf);
+todo_wine
+{
+ test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 2);
+}
+ DeleteObject(hfont);
+
+ /* test integer scaling 3x3 */
+ bitmap_lf.lfHeight = height_orig * 3;
+ bitmap_lf.lfWidth = 0;
+ hfont = create_font("3x3", &bitmap_lf);
+
+todo_wine
+{
+ test_font_metrics(hdc, hfont, test_str, sizeof(test_str), &tm_orig, &size_orig, width_orig, 3, 3);
+}
+ DeleteObject(hfont);
+
+ ReleaseDC(0, hdc);
+}
+
+static INT CALLBACK find_font_proc(const LOGFONT *elf, const TEXTMETRIC *ntm, DWORD type, LPARAM lParam)
+{
+ LOGFONT *lf = (LOGFONT *)lParam;
+
+ if (elf->lfHeight == lf->lfHeight && !strcmp(elf->lfFaceName, lf->lfFaceName))
+ {
+ *lf = *elf;
+ return 0; /* stop enumeration */
+ }
+ return 1; /* continue enumeration */
+}
+
+static void test_bitmap_font_metrics(void)
+{
+ static const struct font_data
+ {
+ const char face_name[LF_FACESIZE];
+ int weight, height, ascent, descent, int_leading, ext_leading;
+ int ave_char_width, max_char_width;
+ } fd[] =
+ {
+ { "MS Sans Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 11 },
+ { "MS Sans Serif", FW_NORMAL, 16, 13, 3, 3, 0, 7, 14 },
+ { "MS Sans Serif", FW_NORMAL, 20, 16, 4, 4, 0, 8, 16 },
+ { "MS Sans Serif", FW_NORMAL, 24, 19, 5, 6, 0, 9, 20 },
+ { "MS Sans Serif", FW_NORMAL, 29, 23, 6, 5, 0, 12, 25 },
+ { "MS Sans Serif", FW_NORMAL, 37, 29, 8, 5, 0, 16, 32 },
+ { "MS Serif", FW_NORMAL, 10, 8, 2, 2, 0, 5, 8 },
+ { "MS Serif", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9 },
+ { "MS Serif", FW_NORMAL, 13, 11, 2, 2, 0, 5, 12 },
+ { "MS Serif", FW_NORMAL, 16, 13, 3, 3, 0, 6, 16 },
+ { "MS Serif", FW_NORMAL, 19, 15, 4, 3, 0, 8, 19 },
+ { "MS Serif", FW_NORMAL, 21, 16, 5, 3, 0, 9, 23 },
+ { "MS Serif", FW_NORMAL, 27, 21, 6, 3, 0, 12, 27 },
+ { "MS Serif", FW_NORMAL, 35, 27, 8, 3, 0, 16, 34 },
+ { "Courier", FW_NORMAL, 13, 11, 2, 0, 0, 8, 8 },
+ { "Courier", FW_NORMAL, 16, 13, 3, 0, 0, 9, 9 },
+ { "Courier", FW_NORMAL, 20, 16, 4, 0, 0, 12, 12 },
+ { "System", FW_BOLD, 16, 13, 3, 3, 0, 7, 15 },
+ { "Small Fonts", FW_NORMAL, 3, 2, 1, 0, 0, 1, 2 },
+ { "Small Fonts", FW_NORMAL, 5, 4, 1, 1, 0, 3, 4 },
+ { "Small Fonts", FW_NORMAL, 6, 5, 1, 1, 0, 3, 13 },
+ { "Small Fonts", FW_NORMAL, 8, 7, 1, 1, 0, 4, 7 },
+ { "Small Fonts", FW_NORMAL, 10, 8, 2, 2, 0, 4, 8 },
+ { "Small Fonts", FW_NORMAL, 11, 9, 2, 2, 0, 5, 9 }
+ /* FIXME: add "Fixedsys", "Terminal" */
+ };
+ HDC hdc;
+ LOGFONT lf;
+ HFONT hfont, old_hfont;
+ TEXTMETRIC tm;
+ INT ret, i;
+
+ hdc = CreateCompatibleDC(0);
+ assert(hdc);
+
+ for (i = 0; i < sizeof(fd)/sizeof(fd[0]); i++)
+ {
+ memset(&lf, 0, sizeof(lf));
+
+ lf.lfHeight = fd[i].height;
+ strcpy(lf.lfFaceName, fd[i].face_name);
+ ret = EnumFontFamilies(hdc, fd[i].face_name, find_font_proc, (LPARAM)&lf);
+ if (ret)
+ {
+ trace("font %s height %d not found\n", fd[i].face_name, fd[i].height);
+ continue;
+ }
+
+ trace("found font %s, height %ld\n", lf.lfFaceName, lf.lfHeight);
+
+ hfont = create_font(lf.lfFaceName, &lf);
+ old_hfont = SelectObject(hdc, hfont);
+ ok(GetTextMetrics(hdc, &tm), "GetTextMetrics error %ld\n", GetLastError());
+
+ ok(tm.tmWeight == fd[i].weight, "%s(%d): tm.tmWeight %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmWeight, fd[i].weight);
+ ok(tm.tmHeight == fd[i].height, "%s(%d): tm.tmHeight %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmHeight, fd[i].height);
+ ok(tm.tmAscent == fd[i].ascent, "%s(%d): tm.tmAscent %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmAscent, fd[i].ascent);
+ ok(tm.tmDescent == fd[i].descent, "%s(%d): tm.tmDescent %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmDescent, fd[i].descent);
+ ok(tm.tmInternalLeading == fd[i].int_leading, "%s(%d): tm.tmInternalLeading %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmInternalLeading, fd[i].int_leading);
+ ok(tm.tmExternalLeading == fd[i].ext_leading, "%s(%d): tm.tmExternalLeading %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmExternalLeading, fd[i].ext_leading);
+ ok(tm.tmAveCharWidth == fd[i].ave_char_width, "%s(%d): tm.tmAveCharWidth %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmAveCharWidth, fd[i].ave_char_width);
+ ok(tm.tmMaxCharWidth == fd[i].max_char_width, "%s(%d): tm.tmMaxCharWidth %ld != %d\n", fd[i].face_name, fd[i].height, tm.tmMaxCharWidth, fd[i].max_char_width);
+
+ SelectObject(hdc, old_hfont);
+ DeleteObject(hfont);
+ }
+
+ DeleteDC(hdc);
+}
+
+static void test_GdiGetCharDimensions(void)
+{
+ HDC hdc;
+ TEXTMETRICW tm;
+ LONG ret;
+ SIZE size;
+ LONG avgwidth, height;
+ static const char szAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ typedef LONG (WINAPI *fnGdiGetCharDimensions)(HDC hdc, LPTEXTMETRICW lptm, LONG *height);
+ fnGdiGetCharDimensions GdiGetCharDimensions = (fnGdiGetCharDimensions)GetProcAddress(LoadLibrary("gdi32"), "GdiGetCharDimensions");
+ if (!GdiGetCharDimensions) return;
+
+ hdc = CreateCompatibleDC(NULL);
+
+ GetTextExtentPoint(hdc, szAlphabet, strlen(szAlphabet), &size);
+ avgwidth = ((size.cx / 26) + 1) / 2;
+
+ ret = GdiGetCharDimensions(hdc, &tm, &height);
+ ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth, ret);
+ ok(height == tm.tmHeight, "GdiGetCharDimensions should have set height to %ld instead of %ld\n", tm.tmHeight, height);
+
+ ret = GdiGetCharDimensions(hdc, &tm, NULL);
+ ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth, ret);
+
+ ret = GdiGetCharDimensions(hdc, NULL, NULL);
+ ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth, ret);
+
+ height = 0;
+ ret = GdiGetCharDimensions(hdc, NULL, &height);
+ ok(ret == avgwidth, "GdiGetCharDimensions should have returned width of %ld instead of %ld\n", avgwidth, ret);
+ ok(height == size.cy, "GdiGetCharDimensions should have set height to %ld instead of %ld\n", size.cy, height);
+
+ DeleteDC(hdc);
+}
+
+static void test_GetCharABCWidthsW(void)
+{
+ BOOL ret;
+ ABC abc[1];
+ typedef BOOL (WINAPI *fnGetCharABCWidthsW)(HDC hdc, UINT first, UINT last, LPABC abc);
+ fnGetCharABCWidthsW GetCharABCWidthsW = (fnGetCharABCWidthsW)GetProcAddress(LoadLibrary("gdi32"), "GetCharABCWidthsW");
+ if (!GetCharABCWidthsW) return;
+
+ ret = GetCharABCWidthsW(NULL, 'a', 'a', abc);
+ ok(!ret, "GetCharABCWidthsW should have returned FALSE\n");
+}
+
+static void test_text_extents(void)
+{
+ static const WCHAR wt[] = {'O','n','e','\n','t','w','o',' ','3',0};
+ LPINT extents;
+ INT i, len, fit1, fit2;
+ LOGFONTA lf;
+ TEXTMETRICA tm;
+ HDC hdc;
+ HFONT hfont;
+ SIZE sz;
+ SIZE sz1, sz2;
+
+ memset(&lf, 0, sizeof(lf));
+ strcpy(lf.lfFaceName, "Arial");
+ lf.lfHeight = 20;
+
+ hfont = CreateFontIndirectA(&lf);
+ hdc = GetDC(0);
+ hfont = SelectObject(hdc, hfont);
+ GetTextMetricsA(hdc, &tm);
+ GetTextExtentPointA(hdc, "o", 1, &sz);
+ ok(sz.cy == tm.tmHeight, "cy %ld tmHeight %ld\n", sz.cy, tm.tmHeight);
+
+ len = lstrlenW(wt);
+ extents = HeapAlloc(GetProcessHeap(), 0, len * sizeof extents[0]);
+ memset(extents, 0, len * sizeof extents[0]);
+ extents[0] = 1; /* So that the increasing sequence test will fail
+ if the extents array is untouched. */
+ GetTextExtentExPointW(hdc, wt, len, 32767, &fit1, extents, &sz1);
+ GetTextExtentPointW(hdc, wt, len, &sz2);
+ ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
+ "results from GetTextExtentExPointW and GetTextExtentPointW differ\n");
+ for (i = 1; i < len; ++i)
+ ok(extents[i-1] <= extents[i],
+ "GetTextExtentExPointW generated a non-increasing sequence of partial extents (at position %d)\n",
+ i);
+ ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n");
+ ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1);
+ ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n");
+ GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2);
+ ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n");
+ ok(fit2 == 3, "GetTextExtentExPointW extents isn't consistent with fit\n");
+ GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2);
+ ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n");
+ GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2);
+ ok(extents[0] == extents[2] && extents[1] == extents[3],
+ "GetTextExtentExPointW with lpnFit == NULL returns incorrect results\n");
+ GetTextExtentExPointW(hdc, wt, 2, 0, NULL, NULL, &sz1);
+ ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy,
+ "GetTextExtentExPointW with lpnFit and alpDx both NULL returns incorrect results\n");
+ HeapFree(GetProcessHeap(), 0, extents);
+
+ SelectObject(hdc, hfont);
+ DeleteObject(hfont);
+ ReleaseDC(NULL, hdc);
+}
+
+static void test_GetGlyphIndices()
+{
+ HDC hdc;
+ HFONT hfont;
+ DWORD charcount;
+ LOGFONTA lf;
+ DWORD flags = 0;
+ WCHAR testtext[] = {'T','e','s','t',0xffff,0};
+ WORD glyphs[(sizeof(testtext)/2)-1];
+ TEXTMETRIC textm;
+
+ typedef BOOL (WINAPI *fnGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
+ fnGetGlyphIndicesW GetGlyphIndicesW = (fnGetGlyphIndicesW)GetProcAddress(LoadLibrary("gdi32"),
+ "GetGlyphIndicesW");
+ if (!GetGlyphIndicesW) {
+ trace("GetGlyphIndices not available on platform\n");
+ return;
+ }
+
+ memset(&lf, 0, sizeof(lf));
+ strcpy(lf.lfFaceName, "Symbol");
+ lf.lfHeight = 20;
+
+ hfont = CreateFontIndirectA(&lf);
+ hdc = GetDC(0);
+
+ ok(GetTextMetrics(hdc, &textm), "GetTextMetric failed\n");
+ flags |= GGI_MARK_NONEXISTING_GLYPHS;
+ charcount = GetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
+ ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %ld\n", charcount);
+ ok(glyphs[4] == 0x001f, "GetGlyphIndices should have returned a nonexistent char not %04x\n", glyphs[4]);
+ flags = 0;
+ charcount = GetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
+ ok(charcount == 5, "GetGlyphIndices count of glyphs should = 5 not %ld\n", charcount);
+ ok(glyphs[4] == textm.tmDefaultChar, "GetGlyphIndices should have returned a %04x not %04x\n",
+ textm.tmDefaultChar, glyphs[4]);
+}
+
+START_TEST(font)
+{
+ test_logfont();
+ test_bitmap_font();
+ test_bitmap_font_metrics();
+ test_GdiGetCharDimensions();
+ test_GetCharABCWidthsW();
+ test_text_extents();
+ test_GetGlyphIndices();
+}
--- /dev/null
+<module name="gdi32_winetest" type="win32cui" installbase="bin" installname="gdi32_winetest.exe" allowwarnings="true">
+ <include base="gdi32_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>ntdll</library>
+ <library>gdi32</library>
+ <library>kernel32</library>
+ <library>user32</library>
+ <library>advapi32</library>
+ <file>bitmap.c</file>
+ <file>brush.c</file>
+ <file>clipping.c</file>
+ <file>dc.c</file>
+ <file>gdiobj.c</file>
+ <file>font.c</file>
+ <file>mapping.c</file>
+ <file>metafile.c</file>
+ <file>palette.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * Unit test suite for GDI objects
+ *
+ * Copyright 2002 Mike McCormack
+ * Copyright 2004 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <assert.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/test.h"
+
+static void test_gdi_objects(void)
+{
+ BYTE buff[256];
+ HDC hdc = GetDC(NULL);
+ HPEN hp;
+ int i;
+ BOOL ret;
+
+ /* SelectObject() with a NULL DC returns 0 and sets ERROR_INVALID_HANDLE.
+ * Note: Under XP at least invalid ptrs can also be passed, not just NULL;
+ * Don't test that here in case it crashes earlier win versions.
+ */
+ SetLastError(0);
+ hp = SelectObject(NULL, GetStockObject(BLACK_PEN));
+ ok(!hp && GetLastError() == ERROR_INVALID_HANDLE,
+ "SelectObject(NULL DC) expected 0, ERROR_INVALID_HANDLE, got %p, 0x%08lx\n",
+ hp, GetLastError());
+
+ /* With a valid DC and a NULL object, the call returns 0 but does not SetLastError() */
+ SetLastError(0);
+ hp = SelectObject(hdc, NULL);
+ ok(!hp && !GetLastError(),
+ "SelectObject(NULL obj) expected 0, NO_ERROR, got %p, 0x%08lx\n",
+ hp, GetLastError());
+
+ /* The DC is unaffected by the NULL SelectObject */
+ SetLastError(0);
+ hp = SelectObject(hdc, GetStockObject(BLACK_PEN));
+ ok(hp && !GetLastError(),
+ "SelectObject(post NULL) expected non-null, NO_ERROR, got %p, 0x%08lx\n",
+ hp, GetLastError());
+
+ /* GetCurrentObject does not SetLastError() on a null object */
+ SetLastError(0);
+ hp = GetCurrentObject(NULL, OBJ_PEN);
+ ok(!hp && !GetLastError(),
+ "GetCurrentObject(NULL DC) expected 0, NO_ERROR, got %p, 0x%08lx\n",
+ hp, GetLastError());
+
+ /* DeleteObject does not SetLastError() on a null object */
+ ret = DeleteObject(NULL);
+ ok( !ret && !GetLastError(),
+ "DeleteObject(NULL obj), expected 0, NO_ERROR, got %d, 0x%08lx\n",
+ ret, GetLastError());
+
+ /* GetObject does not SetLastError() on a null object */
+ SetLastError(0);
+ i = GetObjectA(NULL, sizeof(buff), buff);
+ ok (!i && !GetLastError(),
+ "GetObject(NULL obj), expected 0, NO_ERROR, got %d, 0x%08lx\n",
+ i, GetLastError());
+
+ /* GetObjectType does SetLastError() on a null object */
+ SetLastError(0);
+ i = GetObjectType(NULL);
+ ok (!i && GetLastError() == ERROR_INVALID_HANDLE,
+ "GetObjectType(NULL obj), expected 0, ERROR_INVALID_HANDLE, got %d, 0x%08lx\n",
+ i, GetLastError());
+
+ /* UnrealizeObject does not SetLastError() on a null object */
+ SetLastError(0);
+ i = UnrealizeObject(NULL);
+ ok (!i && !GetLastError(),
+ "UnrealizeObject(NULL obj), expected 0, NO_ERROR, got %d, 0x%08lx\n",
+ i, GetLastError());
+
+ ReleaseDC(NULL, hdc);
+}
+
+struct hgdiobj_event
+{
+ HDC hdc;
+ HGDIOBJ hgdiobj1;
+ HGDIOBJ hgdiobj2;
+ HANDLE stop_event;
+ HANDLE ready_event;
+};
+
+static DWORD WINAPI thread_proc(void *param)
+{
+ LOGPEN lp;
+ struct hgdiobj_event *hgdiobj_event = (struct hgdiobj_event *)param;
+
+ hgdiobj_event->hdc = CreateDC("display", NULL, NULL, NULL);
+ ok(hgdiobj_event->hdc != NULL, "CreateDC error %ld\n", GetLastError());
+
+ hgdiobj_event->hgdiobj1 = CreatePen(PS_DASHDOTDOT, 17, RGB(1, 2, 3));
+ ok(hgdiobj_event->hgdiobj1 != 0, "Failed to create pen\n");
+
+ hgdiobj_event->hgdiobj2 = CreateRectRgn(0, 1, 12, 17);
+ ok(hgdiobj_event->hgdiobj2 != 0, "Failed to create pen\n");
+
+ SetEvent(hgdiobj_event->ready_event);
+ ok(WaitForSingleObject(hgdiobj_event->stop_event, INFINITE) == WAIT_OBJECT_0,
+ "WaitForSingleObject error %ld\n", GetLastError());
+
+ ok(!GetObject(hgdiobj_event->hgdiobj1, sizeof(lp), &lp), "GetObject should fail\n");
+
+ ok(!GetDeviceCaps(hgdiobj_event->hdc, TECHNOLOGY), "GetDeviceCaps(TECHNOLOGY) should fail\n");
+
+ return 0;
+}
+
+static void test_thread_objects(void)
+{
+ LOGPEN lp;
+ DWORD tid, type;
+ HANDLE hthread;
+ struct hgdiobj_event hgdiobj_event;
+ INT ret;
+
+ hgdiobj_event.stop_event = CreateEvent(NULL, 0, 0, NULL);
+ ok(hgdiobj_event.stop_event != NULL, "CreateEvent error %ld\n", GetLastError());
+ hgdiobj_event.ready_event = CreateEvent(NULL, 0, 0, NULL);
+ ok(hgdiobj_event.ready_event != NULL, "CreateEvent error %ld\n", GetLastError());
+
+ hthread = CreateThread(NULL, 0, thread_proc, &hgdiobj_event, 0, &tid);
+ ok(hthread != NULL, "CreateThread error %ld\n", GetLastError());
+
+ ok(WaitForSingleObject(hgdiobj_event.ready_event, INFINITE) == WAIT_OBJECT_0,
+ "WaitForSingleObject error %ld\n", GetLastError());
+
+ ok(GetObject(hgdiobj_event.hgdiobj1, sizeof(lp), &lp) == sizeof(lp),
+ "GetObject error %ld\n", GetLastError());
+ ok(lp.lopnStyle == PS_DASHDOTDOT, "wrong pen style %d\n", lp.lopnStyle);
+ ok(lp.lopnWidth.x == 17, "wrong pen width.y %ld\n", lp.lopnWidth.x);
+ ok(lp.lopnWidth.y == 0, "wrong pen width.y %ld\n", lp.lopnWidth.y);
+ ok(lp.lopnColor == RGB(1, 2, 3), "wrong pen width.y %08lx\n", lp.lopnColor);
+
+ ret = GetDeviceCaps(hgdiobj_event.hdc, TECHNOLOGY);
+ ok(ret == DT_RASDISPLAY, "GetDeviceCaps(TECHNOLOGY) should return DT_RASDISPLAY not %d\n", ret);
+
+ ok(DeleteObject(hgdiobj_event.hgdiobj1), "DeleteObject error %ld\n", GetLastError());
+ ok(DeleteDC(hgdiobj_event.hdc), "DeleteDC error %ld\n", GetLastError());
+
+ type = GetObjectType(hgdiobj_event.hgdiobj2);
+ ok(type == OBJ_REGION, "GetObjectType returned %lu\n", type);
+
+ SetEvent(hgdiobj_event.stop_event);
+ ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0,
+ "WaitForSingleObject error %ld\n", GetLastError());
+ CloseHandle(hthread);
+
+ type = GetObjectType(hgdiobj_event.hgdiobj2);
+ ok(type == OBJ_REGION, "GetObjectType returned %lu\n", type);
+ ok(DeleteObject(hgdiobj_event.hgdiobj2), "DeleteObject error %ld\n", GetLastError());
+
+ CloseHandle(hgdiobj_event.stop_event);
+ CloseHandle(hgdiobj_event.ready_event);
+}
+
+static void test_GetCurrentObject(void)
+{
+ DWORD type;
+ HPEN hpen;
+ HBRUSH hbrush;
+ HPALETTE hpal;
+ HFONT hfont;
+ HBITMAP hbmp;
+ HRGN hrgn;
+ HDC hdc;
+ HCOLORSPACE hcs;
+ HGDIOBJ hobj;
+ LOGBRUSH lb;
+ LOGCOLORSPACEA lcs;
+
+ hdc = CreateCompatibleDC(0);
+ assert(hdc != 0);
+
+ type = GetObjectType(hdc);
+ ok(type == OBJ_MEMDC, "GetObjectType returned %lu\n", type);
+
+ hpen = CreatePen(PS_SOLID, 10, RGB(10, 20, 30));
+ assert(hpen != 0);
+ SelectObject(hdc, hpen);
+ hobj = GetCurrentObject(hdc, OBJ_PEN);
+ ok(hobj == hpen, "OBJ_PEN is wrong: %p\n", hobj);
+ hobj = GetCurrentObject(hdc, OBJ_EXTPEN);
+ ok(hobj == hpen, "OBJ_EXTPEN is wrong: %p\n", hobj);
+
+ hbrush = CreateSolidBrush(RGB(10, 20, 30));
+ assert(hbrush != 0);
+ SelectObject(hdc, hbrush);
+ hobj = GetCurrentObject(hdc, OBJ_BRUSH);
+ ok(hobj == hbrush, "OBJ_BRUSH is wrong: %p\n", hobj);
+
+ hpal = CreateHalftonePalette(hdc);
+ assert(hpal != 0);
+ SelectPalette(hdc, hpal, FALSE);
+ hobj = GetCurrentObject(hdc, OBJ_PAL);
+ ok(hobj == hpal, "OBJ_PAL is wrong: %p\n", hobj);
+
+ hfont = CreateFontA(10, 5, 0, 0, FW_DONTCARE, 0, 0, 0, ANSI_CHARSET,
+ OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ DEFAULT_PITCH, "MS Sans Serif");
+ assert(hfont != 0);
+ SelectObject(hdc, hfont);
+ hobj = GetCurrentObject(hdc, OBJ_FONT);
+ ok(hobj == hfont, "OBJ_FONT is wrong: %p\n", hobj);
+
+ hbmp = CreateBitmap(100, 100, 1, 1, NULL);
+ assert(hbmp != 0);
+ SelectObject(hdc, hbmp);
+ hobj = GetCurrentObject(hdc, OBJ_BITMAP);
+ ok(hobj == hbmp, "OBJ_BITMAP is wrong: %p\n", hobj);
+
+ assert(GetObject(hbrush, sizeof(lb), &lb) == sizeof(lb));
+ hpen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_SQUARE | PS_JOIN_BEVEL,
+ 10, &lb, 0, NULL);
+ assert(hpen != 0);
+ SelectObject(hdc, hpen);
+ hobj = GetCurrentObject(hdc, OBJ_PEN);
+ ok(hobj == hpen, "OBJ_PEN is wrong: %p\n", hobj);
+ hobj = GetCurrentObject(hdc, OBJ_EXTPEN);
+ ok(hobj == hpen, "OBJ_EXTPEN is wrong: %p\n", hobj);
+
+ hcs = GetColorSpace(hdc);
+ if (hcs)
+ {
+ trace("current color space is not NULL\n");
+ ok(GetLogColorSpaceA(hcs, &lcs, sizeof(lcs)), "GetLogColorSpace failed\n");
+ hcs = CreateColorSpaceA(&lcs);
+ ok(hcs != 0, "CreateColorSpace failed\n");
+ SelectObject(hdc, hcs);
+ hobj = GetCurrentObject(hdc, OBJ_COLORSPACE);
+ ok(hobj == hcs, "OBJ_COLORSPACE is wrong: %p\n", hobj);
+ }
+
+ hrgn = CreateRectRgn(1, 1, 100, 100);
+ assert(hrgn != 0);
+ SelectObject(hdc, hrgn);
+ hobj = GetCurrentObject(hdc, OBJ_REGION);
+ ok(!hobj, "OBJ_REGION is wrong: %p\n", hobj);
+
+ DeleteDC(hdc);
+}
+
+START_TEST(gdiobj)
+{
+ test_gdi_objects();
+ test_thread_objects();
+ test_GetCurrentObject();
+}
--- /dev/null
+/*
+ * Unit tests for mapping functions
+ *
+ * Copyright (c) 2005 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+
+
+void test_modify_world_transform(void)
+{
+ HDC hdc = GetDC(0);
+ int ret;
+
+ ret = SetGraphicsMode(hdc, GM_ADVANCED);
+ if(!ret) /* running in win9x so quit */
+ {
+ ReleaseDC(0, hdc);
+ return;
+ }
+
+ ret = ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
+ ok(ret, "ret = %d\n", ret);
+
+ ret = ModifyWorldTransform(hdc, NULL, MWT_LEFTMULTIPLY);
+ ok(!ret, "ret = %d\n", ret);
+
+ ret = ModifyWorldTransform(hdc, NULL, MWT_RIGHTMULTIPLY);
+ ok(!ret, "ret = %d\n", ret);
+
+ ReleaseDC(0, hdc);
+}
+
+void test_SetWindowExt(HDC hdc, LONG cx, LONG cy, LONG expected_vp_cx, LONG expected_vp_cy)
+{
+ SIZE windowExt, viewportExt;
+ POINT windowOrg, windowOrgAfter, viewportOrg, viewportOrgAfter;
+
+ GetWindowOrgEx(hdc, &windowOrg);
+ GetViewportOrgEx(hdc, &viewportOrg);
+
+ SetWindowExtEx(hdc, cx, cy, NULL);
+ GetWindowExtEx(hdc, &windowExt);
+ ok(windowExt.cx == cx && windowExt.cy == cy,
+ "Window extension: Expected %ldx%ld, got %ldx%ld\n",
+ cx, cy, windowExt.cx, windowExt.cy);
+
+ GetViewportExtEx(hdc, &viewportExt);
+ ok(viewportExt.cx == expected_vp_cx && viewportExt.cy == expected_vp_cy,
+ "Viewport extents have not been properly adjusted: Expected %ldx%ld, got %ldx%ld\n",
+ expected_vp_cx, expected_vp_cy, viewportExt.cx, viewportExt.cy);
+
+ GetWindowOrgEx(hdc, &windowOrgAfter);
+ ok(windowOrg.x == windowOrgAfter.x && windowOrg.y == windowOrgAfter.y,
+ "Window origin changed from (%ld,%ld) to (%ld,%ld)\n",
+ windowOrg.x, windowOrg.y, windowOrgAfter.x, windowOrgAfter.y);
+
+ GetViewportOrgEx(hdc, &viewportOrgAfter);
+ ok(viewportOrg.x == viewportOrgAfter.x && viewportOrg.y == viewportOrgAfter.y,
+ "Viewport origin changed from (%ld,%ld) to (%ld,%ld)\n",
+ viewportOrg.x, viewportOrg.y, viewportOrgAfter.x, viewportOrgAfter.y);
+}
+
+void test_SetViewportExt(HDC hdc, LONG cx, LONG cy, LONG expected_vp_cx, LONG expected_vp_cy)
+{
+ SIZE windowExt, windowExtAfter, viewportExt;
+ POINT windowOrg, windowOrgAfter, viewportOrg, viewportOrgAfter;
+
+ GetWindowOrgEx(hdc, &windowOrg);
+ GetViewportOrgEx(hdc, &viewportOrg);
+ GetWindowExtEx(hdc, &windowExt);
+
+ SetViewportExtEx(hdc, cx, cy, NULL);
+ GetViewportExtEx(hdc, &viewportExt);
+ ok(viewportExt.cx == expected_vp_cx && viewportExt.cy == expected_vp_cy,
+ "Viewport extents have not been properly adjusted: Expected %ldx%ld, got %ldx%ld\n",
+ expected_vp_cx, expected_vp_cy, viewportExt.cx, viewportExt.cy);
+
+ GetWindowExtEx(hdc, &windowExtAfter);
+ ok(windowExt.cx == windowExtAfter.cx && windowExt.cy == windowExtAfter.cy,
+ "Window extension changed from %ldx%ld to %ldx%ld\n",
+ windowExt.cx, windowExt.cy, windowExtAfter.cx, windowExtAfter.cy);
+
+ GetWindowOrgEx(hdc, &windowOrgAfter);
+ ok(windowOrg.x == windowOrgAfter.x && windowOrg.y == windowOrgAfter.y,
+ "Window origin changed from (%ld,%ld) to (%ld,%ld)\n",
+ windowOrg.x, windowOrg.y, windowOrgAfter.x, windowOrgAfter.y);
+
+ GetViewportOrgEx(hdc, &viewportOrgAfter);
+ ok(viewportOrg.x == viewportOrgAfter.x && viewportOrg.y == viewportOrgAfter.y,
+ "Viewport origin changed from (%ld,%ld) to (%ld,%ld)\n",
+ viewportOrg.x, viewportOrg.y, viewportOrgAfter.x, viewportOrgAfter.y);
+}
+
+void test_isotropic_mapping(void)
+{
+ SIZE win, vp;
+ HDC hdc = GetDC(0);
+
+ SetMapMode(hdc, MM_ISOTROPIC);
+
+ /* MM_ISOTROPIC is set up like MM_LOMETRIC.
+ Initial values after SetMapMode():
+ (1 inch = 25.4 mm)
+
+ Windows 9x: Windows NT:
+ Window Ext: 254 x -254 HORZSIZE*10 x VERTSIZE*10
+ Viewport Ext: LOGPIXELSX x LOGPIXELSY HORZRES x -VERTRES
+
+ To test without rounding errors, we have to use multiples of
+ these values!
+ */
+
+ GetWindowExtEx(hdc, &win);
+ GetViewportExtEx(hdc, &vp);
+
+ test_SetViewportExt(hdc, 10 * vp.cx, 10 * vp.cy, 10 * vp.cx, 10 * vp.cy);
+ test_SetWindowExt(hdc, win.cx, win.cy, 10 * vp.cx, 10 * vp.cy);
+ test_SetWindowExt(hdc, 2 * win.cx, win.cy, 10 * vp.cx, 5 * vp.cy);
+ test_SetWindowExt(hdc, win.cx, win.cy, 5 * vp.cx, 5 * vp.cy);
+ test_SetViewportExt(hdc, 4 * vp.cx, 2 * vp.cy, 2 * vp.cx, 2 * vp.cy);
+ test_SetViewportExt(hdc, vp.cx, 2 * vp.cy, vp.cx, vp.cy);
+ test_SetViewportExt(hdc, 2 * vp.cx, 2 * vp.cy, 2 * vp.cx, 2 * vp.cy);
+ test_SetViewportExt(hdc, 4 * vp.cx, 2 * vp.cy, 2 * vp.cx, 2 * vp.cy);
+ test_SetWindowExt(hdc, 4 * win.cx, 2 * win.cy, 2 * vp.cx, vp.cy);
+ test_SetViewportExt(hdc, -2 * vp.cx, -4 * vp.cy, -2 * vp.cx, -vp.cy);
+ test_SetViewportExt(hdc, -2 * vp.cx, -1 * vp.cy, -2 * vp.cx, -vp.cy);
+ test_SetWindowExt(hdc, -4 * win.cx, -2 * win.cy, -2 * vp.cx, -vp.cy);
+ test_SetWindowExt(hdc, 4 * win.cx, -4 * win.cy, -vp.cx, -vp.cy);
+
+ ReleaseDC(0, hdc);
+}
+
+START_TEST(mapping)
+{
+ test_modify_world_transform();
+ test_isotropic_mapping();
+}
--- /dev/null
+/*
+ * Unit tests for metafile functions
+ *
+ * Copyright (c) 2002 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+
+static LOGFONTA orig_lf;
+static BOOL emr_processed = FALSE;
+
+/* Arbitrarily chosen values for the second co-ordinate of a metafile line */
+#define LINE_X 55.0f
+#define LINE_Y 15.0f
+
+static INT (WINAPI * pGetRelAbs)(HDC, DWORD);
+static INT (WINAPI * pSetRelAbs)(HDC, INT);
+
+#define GDI_GET_PROC(func) \
+ p ## func = (void *)GetProcAddress(hGDI, #func); \
+ if(!p ## func) \
+ trace("GetProcAddress(hGDI, \"%s\") failed\n", #func); \
+
+static void init_function_pointers(void)
+{
+ HMODULE hGDI;
+
+ pGetRelAbs = NULL;
+ pSetRelAbs = NULL;
+
+ hGDI = GetModuleHandleA("gdi32.dll");
+ assert(hGDI);
+ GDI_GET_PROC(GetRelAbs);
+ GDI_GET_PROC(SetRelAbs);
+}
+
+static int CALLBACK eto_emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
+ const ENHMETARECORD *emr, int n_objs, LPARAM param)
+{
+ static int n_record;
+ DWORD i;
+ const INT *dx;
+ INT *orig_dx = (INT *)param;
+ LOGFONTA device_lf;
+ INT ret;
+
+ trace("hdc %p, emr->iType %ld, emr->nSize %ld, param %p\n",
+ hdc, emr->iType, emr->nSize, (void *)param);
+
+ if(!hdc) return 1;
+
+ PlayEnhMetaFileRecord(hdc, handle_table, emr, n_objs);
+
+ switch (emr->iType)
+ {
+ case EMR_HEADER:
+ ok(GetTextAlign(hdc) == 0, "text align %08x\n", GetTextAlign(hdc));
+ ok(GetBkColor(hdc) == RGB(0xff, 0xff, 0xff), "bk color %08lx\n", GetBkColor(hdc));
+ ok(GetTextColor(hdc) == RGB(0x0, 0x0, 0x0), "text color %08lx\n", GetTextColor(hdc));
+ ok(GetROP2(hdc) == R2_COPYPEN, "rop %d\n", GetROP2(hdc));
+ ok(GetArcDirection(hdc) == AD_COUNTERCLOCKWISE, "arc dir %d\n", GetArcDirection(hdc));
+ ok(GetPolyFillMode(hdc) == ALTERNATE, "poly fill %d\n", GetPolyFillMode(hdc));
+ ok(GetStretchBltMode(hdc) == BLACKONWHITE, "stretchblt mode %d\n", GetStretchBltMode(hdc));
+
+ /* GetBkMode, GetRelAbs do not get reset to the default value */
+ ok(GetBkMode(hdc) == OPAQUE, "bk mode %d\n", GetBkMode(hdc));
+ if(pSetRelAbs && pGetRelAbs)
+ ok(pGetRelAbs(hdc, 0) == RELATIVE, "relabs %d\n", pGetRelAbs(hdc, 0));
+
+ n_record = 0;
+ break;
+
+ case EMR_EXTTEXTOUTA:
+ {
+ const EMREXTTEXTOUTA *emr_ExtTextOutA = (const EMREXTTEXTOUTA *)emr;
+ dx = (const INT *)((const char *)emr + emr_ExtTextOutA->emrtext.offDx);
+
+ ret = GetObjectA(GetCurrentObject(hdc, OBJ_FONT), sizeof(device_lf), &device_lf);
+ ok( ret == sizeof(device_lf), "GetObjectA error %ld\n", GetLastError());
+
+ /* compare up to lfOutPrecision, other values are not interesting,
+ * and in fact sometimes arbitrary adapted by Win9x.
+ */
+ ok(!memcmp(&orig_lf, &device_lf, FIELD_OFFSET(LOGFONTA, lfOutPrecision)), "fonts don't match\n");
+ ok(!lstrcmpA(orig_lf.lfFaceName, device_lf.lfFaceName), "font names don't match\n");
+
+ for(i = 0; i < emr_ExtTextOutA->emrtext.nChars; i++)
+ {
+ ok(orig_dx[i] == dx[i], "pass %d: dx[%ld] (%d) didn't match %d\n",
+ n_record, i, dx[i], orig_dx[i]);
+ }
+ n_record++;
+ emr_processed = TRUE;
+ break;
+ }
+
+ case EMR_EXTTEXTOUTW:
+ {
+ const EMREXTTEXTOUTW *emr_ExtTextOutW = (const EMREXTTEXTOUTW *)emr;
+ dx = (const INT *)((const char *)emr + emr_ExtTextOutW->emrtext.offDx);
+
+ ret = GetObjectA(GetCurrentObject(hdc, OBJ_FONT), sizeof(device_lf), &device_lf);
+ ok( ret == sizeof(device_lf), "GetObjectA error %ld\n", GetLastError());
+
+ /* compare up to lfOutPrecision, other values are not interesting,
+ * and in fact sometimes arbitrary adapted by Win9x.
+ */
+ ok(!memcmp(&orig_lf, &device_lf, FIELD_OFFSET(LOGFONTA, lfOutPrecision)), "fonts don't match\n");
+ ok(!lstrcmpA(orig_lf.lfFaceName, device_lf.lfFaceName), "font names don't match\n");
+
+ for(i = 0; i < emr_ExtTextOutW->emrtext.nChars; i++)
+ {
+ ok(orig_dx[i] == dx[i], "pass %d: dx[%ld] (%d) didn't match %d\n",
+ n_record, i, dx[i], orig_dx[i]);
+ }
+ n_record++;
+ emr_processed = TRUE;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+static void test_ExtTextOut(void)
+{
+ HWND hwnd;
+ HDC hdcDisplay, hdcMetafile;
+ HENHMETAFILE hMetafile;
+ HFONT hFont;
+ static const char text[] = "Simple text to test ExtTextOut on metafiles";
+ INT i, len, dx[256];
+ static const RECT rc = { 0, 0, 100, 100 };
+ BOOL ret;
+
+ assert(sizeof(dx)/sizeof(dx[0]) >= lstrlenA(text));
+
+ /* Win9x doesn't play EMFs on invisible windows */
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
+ 0, 0, 200, 200, 0, 0, 0, NULL);
+ ok(hwnd != 0, "CreateWindowExA error %ld\n", GetLastError());
+
+ hdcDisplay = GetDC(hwnd);
+ ok(hdcDisplay != 0, "GetDC error %ld\n", GetLastError());
+
+ trace("hdcDisplay %p\n", hdcDisplay);
+
+ SetMapMode(hdcDisplay, MM_TEXT);
+
+ memset(&orig_lf, 0, sizeof(orig_lf));
+
+ orig_lf.lfCharSet = ANSI_CHARSET;
+ orig_lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ orig_lf.lfWeight = FW_DONTCARE;
+ orig_lf.lfHeight = 7;
+ orig_lf.lfQuality = DEFAULT_QUALITY;
+ lstrcpyA(orig_lf.lfFaceName, "Arial");
+ hFont = CreateFontIndirectA(&orig_lf);
+ ok(hFont != 0, "CreateFontIndirectA error %ld\n", GetLastError());
+
+ hFont = SelectObject(hdcDisplay, hFont);
+
+ len = lstrlenA(text);
+ for (i = 0; i < len; i++)
+ {
+ ret = GetCharWidthA(hdcDisplay, text[i], text[i], &dx[i]);
+ ok( ret, "GetCharWidthA error %ld\n", GetLastError());
+ }
+ hFont = SelectObject(hdcDisplay, hFont);
+
+ hdcMetafile = CreateEnhMetaFileA(hdcDisplay, NULL, NULL, NULL);
+ ok(hdcMetafile != 0, "CreateEnhMetaFileA error %ld\n", GetLastError());
+
+ trace("hdcMetafile %p\n", hdcMetafile);
+
+ ok(GetDeviceCaps(hdcMetafile, TECHNOLOGY) == DT_RASDISPLAY,
+ "GetDeviceCaps(TECHNOLOGY) has to return DT_RASDISPLAY for a display based EMF\n");
+
+ hFont = SelectObject(hdcMetafile, hFont);
+
+ /* 1. pass NULL lpDx */
+ ret = ExtTextOutA(hdcMetafile, 0, 0, 0, &rc, text, lstrlenA(text), NULL);
+ ok( ret, "ExtTextOutA error %ld\n", GetLastError());
+
+ /* 2. pass custom lpDx */
+ ret = ExtTextOutA(hdcMetafile, 0, 20, 0, &rc, text, lstrlenA(text), dx);
+ ok( ret, "ExtTextOutA error %ld\n", GetLastError());
+
+ hFont = SelectObject(hdcMetafile, hFont);
+ ret = DeleteObject(hFont);
+ ok( ret, "DeleteObject error %ld\n", GetLastError());
+
+ hMetafile = CloseEnhMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseEnhMetaFile error %ld\n", GetLastError());
+
+ ok(!GetObjectType(hdcMetafile), "CloseEnhMetaFile has to destroy metafile hdc\n");
+
+ ret = PlayEnhMetaFile(hdcDisplay, hMetafile, &rc);
+ ok( ret, "PlayEnhMetaFile error %ld\n", GetLastError());
+
+ SetTextAlign(hdcDisplay, TA_UPDATECP | TA_CENTER | TA_BASELINE | TA_RTLREADING );
+ SetBkColor(hdcDisplay, RGB(0xff, 0, 0));
+ SetTextColor(hdcDisplay, RGB(0, 0xff, 0));
+ SetROP2(hdcDisplay, R2_NOT);
+ SetArcDirection(hdcDisplay, AD_CLOCKWISE);
+ SetPolyFillMode(hdcDisplay, WINDING);
+ SetStretchBltMode(hdcDisplay, HALFTONE);
+
+ if(pSetRelAbs) pSetRelAbs(hdcDisplay, RELATIVE);
+ SetBkMode(hdcDisplay, OPAQUE);
+
+ ret = EnumEnhMetaFile(hdcDisplay, hMetafile, eto_emf_enum_proc, dx, &rc);
+ ok( ret, "EnumEnhMetaFile error %ld\n", GetLastError());
+
+ ok( GetTextAlign(hdcDisplay) == (TA_UPDATECP | TA_CENTER | TA_BASELINE | TA_RTLREADING),
+ "text align %08x\n", GetTextAlign(hdcDisplay));
+ ok( GetBkColor(hdcDisplay) == RGB(0xff, 0, 0), "bk color %08lx\n", GetBkColor(hdcDisplay));
+ ok( GetTextColor(hdcDisplay) == RGB(0, 0xff, 0), "text color %08lx\n", GetTextColor(hdcDisplay));
+ ok( GetROP2(hdcDisplay) == R2_NOT, "rop2 %d\n", GetROP2(hdcDisplay));
+ ok( GetArcDirection(hdcDisplay) == AD_CLOCKWISE, "arc dir %d\n", GetArcDirection(hdcDisplay));
+ ok( GetPolyFillMode(hdcDisplay) == WINDING, "poly fill %d\n", GetPolyFillMode(hdcDisplay));
+ ok( GetStretchBltMode(hdcDisplay) == HALFTONE, "stretchblt mode %d\n", GetStretchBltMode(hdcDisplay));
+
+ ok(emr_processed, "EnumEnhMetaFile couldn't find EMR_EXTTEXTOUTA or EMR_EXTTEXTOUTW record\n");
+
+ ok(!EnumEnhMetaFile(hdcDisplay, hMetafile, eto_emf_enum_proc, dx, NULL),
+ "A valid hdc has to require a valid rc\n");
+
+ ok(EnumEnhMetaFile(NULL, hMetafile, eto_emf_enum_proc, dx, NULL),
+ "A null hdc does not require a valid rc\n");
+
+ ret = DeleteEnhMetaFile(hMetafile);
+ ok( ret, "DeleteEnhMetaFile error %ld\n", GetLastError());
+ ret = ReleaseDC(hwnd, hdcDisplay);
+ ok( ret, "ReleaseDC error %ld\n", GetLastError());
+ DestroyWindow(hwnd);
+}
+
+static int CALLBACK savedc_emf_enum_proc(HDC hdc, HANDLETABLE *handle_table,
+ const ENHMETARECORD *emr, int n_objs, LPARAM param)
+{
+ static int save_state;
+ static int restore_no;
+
+ switch (emr->iType)
+ {
+ case EMR_HEADER:
+ save_state = 0;
+ restore_no = 0;
+ break;
+
+ case EMR_SAVEDC:
+ save_state++;
+ break;
+
+ case EMR_RESTOREDC:
+ {
+ EMRRESTOREDC *restoredc = (EMRRESTOREDC *)emr;
+ switch(++restore_no)
+ {
+ case 1:
+ ok(restoredc->iRelative == -1, "first restore %ld\n", restoredc->iRelative);
+ break;
+
+ case 2:
+ ok(restoredc->iRelative == -3, "second restore %ld\n", restoredc->iRelative);
+ break;
+ case 3:
+ ok(restoredc->iRelative == -2, "third restore %ld\n", restoredc->iRelative);
+ break;
+ }
+ ok(restore_no <= 3, "restore_no %d\n", restore_no);
+ save_state += restoredc->iRelative;
+ break;
+ }
+ case EMR_EOF:
+ ok(save_state == 0, "EOF save_state %d\n", save_state);
+ break;
+ }
+
+
+ return 1;
+}
+
+void test_SaveDC(void)
+{
+ HDC hdcMetafile, hdcDisplay;
+ HENHMETAFILE hMetafile;
+ HWND hwnd;
+ int ret;
+ static const RECT rc = { 0, 0, 100, 100 };
+
+ /* Win9x doesn't play EMFs on invisible windows */
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
+ 0, 0, 200, 200, 0, 0, 0, NULL);
+ ok(hwnd != 0, "CreateWindowExA error %ld\n", GetLastError());
+
+ hdcDisplay = GetDC(hwnd);
+ ok(hdcDisplay != 0, "GetDC error %ld\n", GetLastError());
+
+ hdcMetafile = CreateEnhMetaFileA(hdcDisplay, NULL, NULL, NULL);
+ ok(hdcMetafile != 0, "CreateEnhMetaFileA error %ld\n", GetLastError());
+
+ /* Need to write something to the emf, otherwise Windows won't play it back */
+ LineTo(hdcMetafile, 100, 100);
+
+ ret = SaveDC(hdcMetafile);
+ ok(ret == 1, "ret = %d\n", ret);
+
+ ret = SaveDC(hdcMetafile);
+ ok(ret == 2, "ret = %d\n", ret);
+
+ ret = SaveDC(hdcMetafile);
+ ok(ret == 3, "ret = %d\n", ret);
+
+ ret = RestoreDC(hdcMetafile, -1);
+ ok(ret, "ret = %d\n", ret);
+
+ ret = SaveDC(hdcMetafile);
+ ok(ret == 3, "ret = %d\n", ret);
+
+ ret = RestoreDC(hdcMetafile, 1);
+ ok(ret, "ret = %d\n", ret);
+
+ ret = SaveDC(hdcMetafile);
+ ok(ret == 1, "ret = %d\n", ret);
+
+ ret = SaveDC(hdcMetafile);
+ ok(ret == 2, "ret = %d\n", ret);
+
+ hMetafile = CloseEnhMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseEnhMetaFile error %ld\n", GetLastError());
+
+ ret = EnumEnhMetaFile(hdcDisplay, hMetafile, savedc_emf_enum_proc, 0, &rc);
+ ok( ret == 1, "EnumEnhMetaFile rets %d\n", ret);
+
+ ret = DeleteEnhMetaFile(hMetafile);
+ ok( ret, "DeleteEnhMetaFile error %ld\n", GetLastError());
+ ret = ReleaseDC(hwnd, hdcDisplay);
+ ok( ret, "ReleaseDC error %ld\n", GetLastError());
+ DestroyWindow(hwnd);
+}
+
+/* Win-format metafile (mfdrv) tests */
+/* These tests compare the generated metafiles byte-by-byte */
+/* with the nominal results. */
+
+/* Maximum size of sample metafiles in bytes. */
+#define MF_BUFSIZE 512
+
+/* 8x8 bitmap data for a pattern brush */
+static const unsigned char SAMPLE_PATTERN_BRUSH[] = {
+ 0x01, 0x00, 0x02, 0x00,
+ 0x03, 0x00, 0x04, 0x00,
+ 0x05, 0x00, 0x06, 0x00,
+ 0x07, 0x00, 0x08, 0x00
+};
+
+/* Sample metafiles to be compared to the outputs of the
+ * test functions.
+ */
+
+static const unsigned char MF_BLANK_BITS[] = {
+ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x0c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const unsigned char MF_GRAPHICS_BITS[] = {
+ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x22, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, 0x02,
+ 0x01, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x13, 0x02, 0x02, 0x00, 0x02, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x14, 0x02, 0x01, 0x00, 0x01, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x18, 0x04, 0x02, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static const unsigned char MF_PATTERN_BRUSH_BITS[] = {
+ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x3d, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x2d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x42, 0x01,
+ 0x03, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x2d, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+static const unsigned char MF_TEXTOUT_ON_PATH_BITS[] =
+{
+ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x19, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x32, 0x0a,
+ 0x16, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x54, 0x65, 0x73, 0x74, 0x03, 0x00, 0x05, 0x00,
+ 0x08, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+static const unsigned char EMF_TEXTOUT_ON_PATH_BITS[] =
+{
+ 0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe7, 0xff, 0xff, 0xff, 0xe9, 0xff, 0xff, 0xff,
+ 0x20, 0x45, 0x4d, 0x46, 0x00, 0x00, 0x01, 0x00,
+ 0xf4, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x40, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x04, 0x00,
+ 0x80, 0xa9, 0x03, 0x00, 0x3b, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xc8, 0x41, 0x00, 0x80, 0xbb, 0x41,
+ 0x0b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x54, 0x00, 0x00, 0x00,
+ 0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00
+};
+
+static const unsigned char MF_LINETO_BITS[] = {
+ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x11, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x13, 0x02,
+ 0x0f, 0x00, 0x37, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+};
+
+static const unsigned char EMF_LINETO_BITS[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x06, 0x00, 0x00, 0xb7, 0x01, 0x00, 0x00,
+ 0x20, 0x45, 0x4d, 0x46, 0x00, 0x00, 0x01, 0x00,
+ 0x38, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x7c, 0x01, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0xcc, 0x05, 0x00,
+ 0xe0, 0x93, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+ 0x47, 0x44, 0x49, 0x43, 0x01, 0x00, 0x00, 0x80,
+ 0x00, 0x03, 0x00, 0x00, 0x60, 0xe5, 0xf4, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x11, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x13, 0x02,
+ 0x0f, 0x00, 0x37, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x25, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x80, 0x4b, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00
+};
+
+static const unsigned char EMF_LINETO_MM_ANISOTROPIC_BITS[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x20, 0x45, 0x4d, 0x46, 0x00, 0x00, 0x01, 0x00,
+ 0x38, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x7c, 0x01, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0xcc, 0x05, 0x00,
+ 0xe0, 0x93, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+ 0x47, 0x44, 0x49, 0x43, 0x01, 0x00, 0x00, 0x80,
+ 0x00, 0x03, 0x00, 0x00, 0xa4, 0xfe, 0xf4, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x11, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x13, 0x02,
+ 0x0f, 0x00, 0x37, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x25, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x30, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x80, 0x4b, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00
+};
+
+static const unsigned char EMF_LINETO_MM_TEXT_BITS[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x37, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x06, 0x00, 0x00, 0xb7, 0x01, 0x00, 0x00,
+ 0x20, 0x45, 0x4d, 0x46, 0x00, 0x00, 0x01, 0x00,
+ 0xe4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x7c, 0x01, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0xcc, 0x05, 0x00,
+ 0xe0, 0x93, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80,
+ 0x25, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0x30, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x80,
+ 0x4b, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00
+};
+
+/* For debugging or dumping the raw metafiles produced by
+ * new test functions.
+ */
+static INT CALLBACK mf_enum_proc(HDC hdc, HANDLETABLE *ht, METARECORD *mr,
+ INT nobj, LPARAM param)
+{
+ trace("hdc %p, mr->rdFunction %04x, mr->rdSize %lu, param %p\n",
+ hdc, mr->rdFunction, mr->rdSize, (void *)param);
+ return TRUE;
+}
+
+/* For debugging or dumping the raw metafiles produced by
+ * new test functions.
+ */
+
+static void dump_mf_bits (const HMETAFILE mf, const char *desc)
+{
+ BYTE buf[MF_BUFSIZE];
+ UINT mfsize, i;
+
+ if (!winetest_debug) return;
+
+ mfsize = GetMetaFileBitsEx (mf, MF_BUFSIZE, buf);
+ ok (mfsize > 0, "%s: GetMetaFileBitsEx failed.\n", desc);
+
+ printf ("MetaFile %s has bits:\n{\n ", desc);
+ for (i=0; i<mfsize; i++)
+ {
+ printf ("0x%02x", buf[i]);
+ if (i == mfsize-1)
+ printf ("\n");
+ else if (i % 8 == 7)
+ printf (",\n ");
+ else
+ printf (", ");
+ }
+ printf ("};\n");
+}
+
+/* Compare the metafile produced by a test function with the
+ * expected raw metafile data in "bits".
+ * Return value is 0 for a perfect match,
+ * -1 if lengths aren't equal,
+ * otherwise returns the number of non-matching bytes.
+ */
+
+static int compare_mf_bits (const HMETAFILE mf, const unsigned char *bits, UINT bsize,
+ const char *desc)
+{
+ unsigned char buf[MF_BUFSIZE];
+ UINT mfsize, i;
+ int diff;
+
+ mfsize = GetMetaFileBitsEx (mf, MF_BUFSIZE, buf);
+ ok (mfsize > 0, "%s: GetMetaFileBitsEx failed.\n", desc);
+ if (mfsize < MF_BUFSIZE)
+ ok (mfsize == bsize, "%s: mfsize=%d, bsize=%d.\n",
+ desc, mfsize, bsize);
+ else
+ ok (bsize >= MF_BUFSIZE, "%s: mfsize > bufsize (%d bytes), bsize=%d.\n",
+ desc, mfsize, bsize);
+ if (mfsize != bsize)
+ return -1;
+
+ diff = 0;
+ for (i=0; i<bsize; i++)
+ {
+ if (buf[i] != bits[i])
+ diff++;
+ }
+ ok (diff == 0, "%s: mfsize=%d, bsize=%d, diff=%d\n",
+ desc, mfsize, bsize, diff);
+
+ return diff;
+}
+
+static int compare_mf_disk_bits(LPCSTR name, const BYTE *bits, UINT bsize, const char *desc)
+{
+ unsigned char buf[MF_BUFSIZE];
+ DWORD mfsize, rd_size, i;
+ int diff;
+ HANDLE hfile;
+ BOOL ret;
+
+ hfile = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ assert(hfile != INVALID_HANDLE_VALUE);
+
+ mfsize = GetFileSize(hfile, NULL);
+ assert(mfsize <= MF_BUFSIZE);
+
+ ret = ReadFile(hfile, buf, sizeof(buf), &rd_size, NULL);
+ ok( ret && rd_size == mfsize, "ReadFile: error %ld\n", GetLastError());
+
+ CloseHandle(hfile);
+
+ ok(mfsize == bsize, "%s: mfsize=%ld, bsize=%d.\n", desc, mfsize, bsize);
+
+ if (mfsize != bsize)
+ return -1;
+
+ diff = 0;
+ for (i=0; i<bsize; i++)
+ {
+ if (buf[i] != bits[i])
+ diff++;
+ }
+ ok(diff == 0, "%s: mfsize=%ld, bsize=%d, diff=%d\n",
+ desc, mfsize, bsize, diff);
+
+ return diff;
+}
+
+/* For debugging or dumping the raw EMFs produced by
+ * new test functions.
+ */
+static void dump_emf_bits(const HENHMETAFILE mf, const char *desc)
+{
+ BYTE buf[MF_BUFSIZE];
+ UINT mfsize, i;
+
+ if (!winetest_debug) return;
+
+ mfsize = GetEnhMetaFileBits(mf, MF_BUFSIZE, buf);
+ ok (mfsize > 0, "%s: GetEnhMetaFileBits failed\n", desc);
+
+ printf("EMF %s has bits:\n{\n ", desc);
+ for (i = 0; i < mfsize; i++)
+ {
+ printf ("0x%02x", buf[i]);
+ if (i == mfsize-1)
+ printf ("\n");
+ else if (i % 8 == 7)
+ printf (",\n ");
+ else
+ printf (", ");
+ }
+ printf ("};\n");
+}
+
+static void dump_emf_records(const HENHMETAFILE mf, const char *desc)
+{
+ BYTE *emf;
+ BYTE buf[MF_BUFSIZE];
+ UINT mfsize, offset;
+
+ if (!winetest_debug) return;
+
+ mfsize = GetEnhMetaFileBits(mf, MF_BUFSIZE, buf);
+ ok (mfsize > 0, "%s: GetEnhMetaFileBits error %ld\n", desc, GetLastError());
+
+ printf("EMF %s has records:\n", desc);
+
+ emf = buf;
+ offset = 0;
+ while(offset < mfsize)
+ {
+ EMR *emr = (EMR *)(emf + offset);
+ printf("emr->iType %ld, emr->nSize %lu\n", emr->iType, emr->nSize);
+ /*trace("emr->iType 0x%04lx, emr->nSize 0x%04lx\n", emr->iType, emr->nSize);*/
+ offset += emr->nSize;
+ }
+}
+
+/* Compare the EMF produced by a test function with the
+ * expected raw EMF data in "bits".
+ * Return value is 0 for a perfect match,
+ * -1 if lengths aren't equal,
+ * otherwise returns the number of non-matching bytes.
+ */
+static int compare_emf_bits(const HENHMETAFILE mf, const unsigned char *bits,
+ UINT bsize, const char *desc, BOOL todo)
+{
+ unsigned char buf[MF_BUFSIZE];
+ UINT mfsize, i;
+ int diff;
+
+ mfsize = GetEnhMetaFileBits(mf, MF_BUFSIZE, buf);
+ ok (mfsize > 0, "%s: GetEnhMetaFileBits error %ld\n", desc, GetLastError());
+
+ if (mfsize < MF_BUFSIZE)
+ {
+ if (mfsize != bsize && todo)
+ {
+ todo_wine
+ ok(mfsize == bsize, "%s: mfsize=%d, bsize=%d\n", desc, mfsize, bsize);
+ }
+ else
+ ok(mfsize == bsize, "%s: mfsize=%d, bsize=%d\n", desc, mfsize, bsize);
+ }
+ else
+ ok(bsize >= MF_BUFSIZE, "%s: mfsize > bufsize (%d bytes), bsize=%d\n",
+ desc, mfsize, bsize);
+
+ if (mfsize != bsize)
+ return -1;
+
+ diff = 0;
+ for (i = 0; i < bsize; i++)
+ {
+ if (buf[i] != bits[i])
+ diff++;
+ }
+ if (diff != 0 && todo)
+ {
+ todo_wine
+ {
+ ok(diff == 0, "%s: mfsize=%d, bsize=%d, diff=%d\n",
+ desc, mfsize, bsize, diff);
+ }
+ return diff;
+ }
+ else
+ {
+ ok(diff == 0, "%s: mfsize=%d, bsize=%d, diff=%d\n",
+ desc, mfsize, bsize, diff);
+
+ return diff;
+ }
+}
+
+/* Test a blank metafile. May be used as a template for new tests. */
+
+static void test_mf_Blank(void)
+{
+ HDC hdcMetafile;
+ HMETAFILE hMetafile;
+ INT caps;
+ BOOL ret;
+ INT type;
+
+ hdcMetafile = CreateMetaFileA(NULL);
+ ok(hdcMetafile != 0, "CreateMetaFileA(NULL) error %ld\n", GetLastError());
+ trace("hdcMetafile %p\n", hdcMetafile);
+
+/* Tests on metafile initialization */
+ caps = GetDeviceCaps (hdcMetafile, TECHNOLOGY);
+ ok (caps == DT_METAFILE,
+ "GetDeviceCaps: TECHNOLOGY=%d != DT_METAFILE.\n", caps);
+
+ hMetafile = CloseMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseMetaFile error %ld\n", GetLastError());
+ type = GetObjectType(hMetafile);
+ ok(type == OBJ_METAFILE, "CloseMetaFile created object with type %d\n", type);
+ ok(!GetObjectType(hdcMetafile), "CloseMetaFile has to destroy metafile hdc\n");
+
+ if (compare_mf_bits (hMetafile, MF_BLANK_BITS, sizeof(MF_BLANK_BITS),
+ "mf_blank") != 0)
+ {
+ dump_mf_bits(hMetafile, "mf_Blank");
+ EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hMetafile);
+ ok( ret, "DeleteMetaFile(%p) error %ld\n", hMetafile, GetLastError());
+}
+
+static void test_CopyMetaFile(void)
+{
+ HDC hdcMetafile;
+ HMETAFILE hMetafile, hmf_copy;
+ BOOL ret;
+ char temp_path[MAX_PATH];
+ char mf_name[MAX_PATH];
+ INT type;
+
+ hdcMetafile = CreateMetaFileA(NULL);
+ ok(hdcMetafile != 0, "CreateMetaFileA(NULL) error %ld\n", GetLastError());
+ trace("hdcMetafile %p\n", hdcMetafile);
+
+ hMetafile = CloseMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseMetaFile error %ld\n", GetLastError());
+ type = GetObjectType(hMetafile);
+ ok(type == OBJ_METAFILE, "CloseMetaFile created object with type %d\n", type);
+
+ if (compare_mf_bits (hMetafile, MF_BLANK_BITS, sizeof(MF_BLANK_BITS),
+ "mf_blank") != 0)
+ {
+ dump_mf_bits(hMetafile, "mf_Blank");
+ EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+ }
+
+ GetTempPathA(MAX_PATH, temp_path);
+ GetTempFileNameA(temp_path, "wmf", 0, mf_name);
+
+ hmf_copy = CopyMetaFileA(hMetafile, mf_name);
+ ok(hmf_copy != 0, "CopyMetaFile error %ld\n", GetLastError());
+
+ type = GetObjectType(hmf_copy);
+ ok(type == OBJ_METAFILE, "CopyMetaFile created object with type %d\n", type);
+
+ ret = DeleteMetaFile(hMetafile);
+ ok( ret, "DeleteMetaFile(%p) error %ld\n", hMetafile, GetLastError());
+
+ if (compare_mf_disk_bits(mf_name, MF_BLANK_BITS, sizeof(MF_BLANK_BITS), "mf_blank") != 0)
+ {
+ dump_mf_bits(hMetafile, "mf_Blank");
+ EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hmf_copy);
+ ok( ret, "DeleteMetaFile(%p) error %ld\n", hmf_copy, GetLastError());
+
+ DeleteFileA(mf_name);
+}
+
+static void test_SetMetaFileBits(void)
+{
+ HMETAFILE hmf;
+ INT type;
+ BOOL ret;
+ BYTE buf[256];
+ METAHEADER *mh;
+
+ hmf = SetMetaFileBitsEx(sizeof(MF_GRAPHICS_BITS), MF_GRAPHICS_BITS);
+ ok(hmf != 0, "SetMetaFileBitsEx error %ld\n", GetLastError());
+ type = GetObjectType(hmf);
+ ok(type == OBJ_METAFILE, "SetMetaFileBitsEx created object with type %d\n", type);
+
+ if (compare_mf_bits(hmf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS), "mf_Graphics") != 0)
+ {
+ dump_mf_bits(hmf, "mf_Graphics");
+ EnumMetaFile(0, hmf, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hmf);
+ ok(ret, "DeleteMetaFile(%p) error %ld\n", hmf, GetLastError());
+
+ /* NULL data crashes XP SP1 */
+ /*hmf = SetMetaFileBitsEx(sizeof(MF_GRAPHICS_BITS), NULL);*/
+
+ /* Now with not zero size */
+ SetLastError(0xdeadbeef);
+ hmf = SetMetaFileBitsEx(0, MF_GRAPHICS_BITS);
+ ok(!hmf, "SetMetaFileBitsEx should fail\n");
+ ok(GetLastError() == ERROR_INVALID_DATA, "wrong error %ld\n", GetLastError());
+
+ /* Now with not even size */
+ SetLastError(0xdeadbeef);
+ hmf = SetMetaFileBitsEx(sizeof(MF_GRAPHICS_BITS) - 1, MF_GRAPHICS_BITS);
+ ok(!hmf, "SetMetaFileBitsEx should fail\n");
+ ok(GetLastError() == 0xdeadbeef /* XP SP1 */, "wrong error %ld\n", GetLastError());
+
+ /* Now with zeroed out or faked some header fields */
+ assert(sizeof(buf) >= sizeof(MF_GRAPHICS_BITS));
+ memcpy(buf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS));
+ mh = (METAHEADER *)buf;
+ /* corruption of any of the below fields leads to a failure */
+ mh->mtType = 0;
+ mh->mtVersion = 0;
+ mh->mtHeaderSize = 0;
+ SetLastError(0xdeadbeef);
+ hmf = SetMetaFileBitsEx(sizeof(MF_GRAPHICS_BITS), buf);
+ ok(!hmf, "SetMetaFileBitsEx should fail\n");
+ ok(GetLastError() == ERROR_INVALID_DATA, "wrong error %ld\n", GetLastError());
+
+ /* Now with corrupted mtSize field */
+ memcpy(buf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS));
+ mh = (METAHEADER *)buf;
+ /* corruption of mtSize doesn't lead to a failure */
+ mh->mtSize *= 2;
+ hmf = SetMetaFileBitsEx(sizeof(MF_GRAPHICS_BITS), buf);
+ ok(hmf != 0, "SetMetaFileBitsEx error %ld\n", GetLastError());
+
+ if (compare_mf_bits(hmf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS), "mf_Graphics") != 0)
+ {
+ dump_mf_bits(hmf, "mf_Graphics");
+ EnumMetaFile(0, hmf, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hmf);
+ ok(ret, "DeleteMetaFile(%p) error %ld\n", hmf, GetLastError());
+
+ /* Now with zeroed out mtSize field */
+ memcpy(buf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS));
+ mh = (METAHEADER *)buf;
+ /* zeroing mtSize doesn't lead to a failure */
+ mh->mtSize = 0;
+ hmf = SetMetaFileBitsEx(sizeof(MF_GRAPHICS_BITS), buf);
+ ok(hmf != 0, "SetMetaFileBitsEx error %ld\n", GetLastError());
+
+ if (compare_mf_bits(hmf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS), "mf_Graphics") != 0)
+ {
+ dump_mf_bits(hmf, "mf_Graphics");
+ EnumMetaFile(0, hmf, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hmf);
+ ok(ret, "DeleteMetaFile(%p) error %ld\n", hmf, GetLastError());
+}
+
+/* Simple APIs from mfdrv/graphics.c
+ */
+
+static void test_mf_Graphics(void)
+{
+ HDC hdcMetafile;
+ HMETAFILE hMetafile;
+ POINT oldpoint;
+ BOOL ret;
+
+ hdcMetafile = CreateMetaFileA(NULL);
+ ok(hdcMetafile != 0, "CreateMetaFileA(NULL) error %ld\n", GetLastError());
+ trace("hdcMetafile %p\n", hdcMetafile);
+
+ ret = MoveToEx(hdcMetafile, 1, 1, NULL);
+ ok( ret, "MoveToEx error %ld.\n", GetLastError());
+ ret = LineTo(hdcMetafile, 2, 2);
+ ok( ret, "LineTo error %ld.\n", GetLastError());
+ ret = MoveToEx(hdcMetafile, 1, 1, &oldpoint);
+ ok( ret, "MoveToEx error %ld.\n", GetLastError());
+
+/* oldpoint gets garbage under Win XP, so the following test would
+ * work under Wine but fails under Windows:
+ *
+ * ok((oldpoint.x == 2) && (oldpoint.y == 2),
+ * "MoveToEx: (x, y) = (%ld, %ld), should be (2, 2).\n",
+ * oldpoint.x, oldpoint.y);
+ */
+
+ ret = Ellipse(hdcMetafile, 0, 0, 2, 2);
+ ok( ret, "Ellipse error %ld.\n", GetLastError());
+
+ hMetafile = CloseMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseMetaFile error %ld\n", GetLastError());
+ ok(!GetObjectType(hdcMetafile), "CloseMetaFile has to destroy metafile hdc\n");
+
+ if (compare_mf_bits (hMetafile, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS),
+ "mf_Graphics") != 0)
+ {
+ dump_mf_bits(hMetafile, "mf_Graphics");
+ EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hMetafile);
+ ok( ret, "DeleteMetaFile(%p) error %ld\n",
+ hMetafile, GetLastError());
+}
+
+static void test_mf_PatternBrush(void)
+{
+ HDC hdcMetafile;
+ HMETAFILE hMetafile;
+ LOGBRUSH *orig_lb;
+ HBRUSH hBrush;
+ BOOL ret;
+
+ orig_lb = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LOGBRUSH));
+
+ orig_lb->lbStyle = BS_PATTERN;
+ orig_lb->lbColor = RGB(0, 0, 0);
+ orig_lb->lbHatch = (ULONG_PTR)CreateBitmap (8, 8, 1, 1, SAMPLE_PATTERN_BRUSH);
+ ok((HBITMAP)orig_lb->lbHatch != NULL, "CreateBitmap error %ld.\n", GetLastError());
+
+ hBrush = CreateBrushIndirect (orig_lb);
+ ok(hBrush != 0, "CreateBrushIndirect error %ld\n", GetLastError());
+
+ hdcMetafile = CreateMetaFileA(NULL);
+ ok(hdcMetafile != 0, "CreateMetaFileA error %ld\n", GetLastError());
+ trace("hdcMetafile %p\n", hdcMetafile);
+
+ hBrush = SelectObject(hdcMetafile, hBrush);
+ ok(hBrush != 0, "SelectObject error %ld.\n", GetLastError());
+
+ hMetafile = CloseMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseMetaFile error %ld\n", GetLastError());
+ ok(!GetObjectType(hdcMetafile), "CloseMetaFile has to destroy metafile hdc\n");
+
+ if (compare_mf_bits (hMetafile, MF_PATTERN_BRUSH_BITS, sizeof(MF_PATTERN_BRUSH_BITS),
+ "mf_Pattern_Brush") != 0)
+ {
+ dump_mf_bits(hMetafile, "mf_Pattern_Brush");
+ EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hMetafile);
+ ok( ret, "DeleteMetaFile error %ld\n", GetLastError());
+ ret = DeleteObject(hBrush);
+ ok( ret, "DeleteObject(HBRUSH) error %ld\n", GetLastError());
+ ret = DeleteObject((HBITMAP)orig_lb->lbHatch);
+ ok( ret, "DeleteObject(HBITMAP) error %ld\n",
+ GetLastError());
+ HeapFree (GetProcessHeap(), 0, orig_lb);
+}
+
+static void test_mf_ExtTextOut_on_path(void)
+{
+ HDC hdcMetafile;
+ HMETAFILE hMetafile;
+ BOOL ret;
+ static const INT dx[4] = { 3, 5, 8, 12 };
+
+ hdcMetafile = CreateMetaFileA(NULL);
+ ok(hdcMetafile != 0, "CreateMetaFileA(NULL) error %ld\n", GetLastError());
+ trace("hdcMetafile %p\n", hdcMetafile);
+
+ ret = BeginPath(hdcMetafile);
+ ok(!ret, "BeginPath on metafile DC should fail\n");
+
+ ret = ExtTextOutA(hdcMetafile, 11, 22, 0, NULL, "Test", 4, dx);
+ ok(ret, "ExtTextOut error %ld\n", GetLastError());
+
+ ret = EndPath(hdcMetafile);
+ ok(!ret, "EndPath on metafile DC should fail\n");
+
+ hMetafile = CloseMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseMetaFile error %ld\n", GetLastError());
+
+ if (compare_mf_bits(hMetafile, MF_TEXTOUT_ON_PATH_BITS, sizeof(MF_TEXTOUT_ON_PATH_BITS),
+ "mf_TextOut_on_path") != 0)
+ {
+ dump_mf_bits(hMetafile, "mf_TextOut_on_path");
+ EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+ }
+
+ ret = DeleteMetaFile(hMetafile);
+ ok(ret, "DeleteMetaFile(%p) error %ld\n", hMetafile, GetLastError());
+}
+
+static void test_emf_ExtTextOut_on_path(void)
+{
+ HWND hwnd;
+ HDC hdcDisplay, hdcMetafile;
+ HENHMETAFILE hMetafile;
+ BOOL ret;
+ static const INT dx[4] = { 3, 5, 8, 12 };
+
+ /* Win9x doesn't play EMFs on invisible windows */
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
+ 0, 0, 200, 200, 0, 0, 0, NULL);
+ ok(hwnd != 0, "CreateWindowExA error %ld\n", GetLastError());
+
+ hdcDisplay = GetDC(hwnd);
+ ok(hdcDisplay != 0, "GetDC error %ld\n", GetLastError());
+
+ hdcMetafile = CreateEnhMetaFileA(hdcDisplay, NULL, NULL, NULL);
+ ok(hdcMetafile != 0, "CreateEnhMetaFileA error %ld\n", GetLastError());
+
+ ret = BeginPath(hdcMetafile);
+ ok(ret, "BeginPath error %ld\n", GetLastError());
+
+ ret = ExtTextOutA(hdcMetafile, 11, 22, 0, NULL, "Test", 4, dx);
+ ok(ret, "ExtTextOut error %ld\n", GetLastError());
+
+ ret = EndPath(hdcMetafile);
+ ok(ret, "EndPath error %ld\n", GetLastError());
+
+ hMetafile = CloseEnhMetaFile(hdcMetafile);
+ ok(hMetafile != 0, "CloseEnhMetaFile error %ld\n", GetLastError());
+
+ /* this doesn't succeed yet: EMF has correct size, all EMF records
+ * are there, but their contents don't match for different reasons.
+ */
+ if (compare_emf_bits(hMetafile, EMF_TEXTOUT_ON_PATH_BITS, sizeof(EMF_TEXTOUT_ON_PATH_BITS),
+ "emf_TextOut_on_path", TRUE) != 0)
+ {
+ dump_emf_bits(hMetafile, "emf_TextOut_on_path");
+ dump_emf_records(hMetafile, "emf_TextOut_on_path");
+ }
+
+ ret = DeleteEnhMetaFile(hMetafile);
+ ok(ret, "DeleteEnhMetaFile error %ld\n", GetLastError());
+ ret = ReleaseDC(hwnd, hdcDisplay);
+ ok(ret, "ReleaseDC error %ld\n", GetLastError());
+ DestroyWindow(hwnd);
+}
+
+static INT CALLBACK EmfEnumProc(HDC hdc, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, INT nObj, LPARAM lpData)
+{
+ LPMETAFILEPICT lpMFP = (LPMETAFILEPICT)lpData;
+ POINT mapping[2] = { { 0, 0 }, { 10, 10 } };
+ /* When using MM_TEXT Win9x does not update the mapping mode
+ * until a record is played which actually outputs something */
+ PlayEnhMetaFileRecord(hdc, lpHTable, lpEMFR, nObj);
+ LPtoDP(hdc, mapping, 2);
+ trace("Meta record: iType %ld, nSize %ld, (%ld,%ld)-(%ld,%ld)\n",
+ lpEMFR->iType, lpEMFR->nSize,
+ mapping[0].x, mapping[0].y, mapping[1].x, mapping[1].y);
+
+ if (lpEMFR->iType == EMR_LINETO)
+ {
+ INT x0, y0, x1, y1;
+ if (!lpMFP || lpMFP->mm == MM_TEXT)
+ {
+ x0 = 0;
+ y0 = 0;
+ x1 = (INT)floor(10 * 100.0 / LINE_X + 0.5);
+ y1 = (INT)floor(10 * 100.0 / LINE_Y + 0.5);
+ }
+ else
+ {
+ ok(lpMFP->mm == MM_ANISOTROPIC, "mm=%ld\n", lpMFP->mm);
+
+ x0 = MulDiv(0, GetDeviceCaps(hdc, HORZSIZE) * 100, GetDeviceCaps(hdc, HORZRES));
+ y0 = MulDiv(0, GetDeviceCaps(hdc, VERTSIZE) * 100, GetDeviceCaps(hdc, VERTRES));
+ x1 = MulDiv(10, GetDeviceCaps(hdc, HORZSIZE) * 100, GetDeviceCaps(hdc, HORZRES));
+ y1 = MulDiv(10, GetDeviceCaps(hdc, VERTSIZE) * 100, GetDeviceCaps(hdc, VERTRES));
+ }
+ ok(mapping[0].x == x0 && mapping[0].y == y0 && mapping[1].x == x1 && mapping[1].y == y1,
+ "(%ld,%ld)->(%ld,%ld), expected (%d,%d)->(%d,%d)\n",
+ mapping[0].x, mapping[0].y, mapping[1].x, mapping[1].y,
+ x0, y0, x1, y1);
+ }
+ return TRUE;
+}
+
+static HENHMETAFILE create_converted_emf(const METAFILEPICT *mfp)
+{
+ HDC hdcMf;
+ HMETAFILE hmf;
+ BOOL ret;
+ UINT size;
+ LPBYTE pBits;
+
+ hdcMf = CreateMetaFile(NULL);
+ ok(hdcMf != NULL, "CreateMetaFile failed with error %ld\n", GetLastError());
+ ret = LineTo(hdcMf, (INT)LINE_X, (INT)LINE_Y);
+ ok(ret, "LineTo failed with error %ld\n", GetLastError());
+ hmf = CloseMetaFile(hdcMf);
+ ok(hmf != NULL, "CloseMetaFile failed with error %ld\n", GetLastError());
+
+ if (compare_mf_bits (hmf, MF_LINETO_BITS, sizeof(MF_LINETO_BITS), "mf_LineTo") != 0)
+ {
+ dump_mf_bits(hmf, "mf_LineTo");
+ EnumMetaFile(0, hmf, mf_enum_proc, 0);
+ }
+
+ size = GetMetaFileBitsEx(hmf, 0, NULL);
+ ok(size, "GetMetaFileBitsEx failed with error %ld\n", GetLastError());
+ pBits = HeapAlloc(GetProcessHeap(), 0, size);
+ GetMetaFileBitsEx(hmf, size, pBits);
+ DeleteMetaFile(hmf);
+ return SetWinMetaFileBits(size, pBits, NULL, mfp);
+}
+
+static void test_mf_conversions(void)
+{
+ trace("Testing MF->EMF conversion (MM_ANISOTROPIC)\n");
+ {
+ HDC hdcOffscreen = CreateCompatibleDC(NULL);
+ HENHMETAFILE hemf;
+ METAFILEPICT mfp;
+ RECT rect = { 0, 0, 100, 100 };
+ mfp.mm = MM_ANISOTROPIC;
+ mfp.xExt = 100;
+ mfp.yExt = 100;
+ mfp.hMF = NULL;
+ hemf = create_converted_emf(&mfp);
+
+ if (compare_emf_bits(hemf, EMF_LINETO_MM_ANISOTROPIC_BITS, sizeof(EMF_LINETO_MM_ANISOTROPIC_BITS),
+ "emf_LineTo MM_ANISOTROPIC", TRUE) != 0)
+ {
+ dump_emf_bits(hemf, "emf_LineTo MM_ANISOTROPIC");
+ dump_emf_records(hemf, "emf_LineTo MM_ANISOTROPIC");
+ }
+
+ EnumEnhMetaFile(hdcOffscreen, hemf, EmfEnumProc, &mfp, &rect);
+
+ DeleteEnhMetaFile(hemf);
+ DeleteDC(hdcOffscreen);
+ }
+
+ trace("Testing MF->EMF conversion (MM_TEXT)\n");
+ {
+ HDC hdcOffscreen = CreateCompatibleDC(NULL);
+ HENHMETAFILE hemf;
+ METAFILEPICT mfp;
+ RECT rect = { 0, 0, 100, 100 };
+ mfp.mm = MM_TEXT;
+ mfp.xExt = 0;
+ mfp.yExt = 0;
+ mfp.hMF = NULL;
+ hemf = create_converted_emf(&mfp);
+
+ if (compare_emf_bits(hemf, EMF_LINETO_MM_TEXT_BITS, sizeof(EMF_LINETO_MM_TEXT_BITS),
+ "emf_LineTo MM_TEXT", TRUE) != 0)
+ {
+ dump_emf_bits(hemf, "emf_LineTo MM_TEXT");
+ dump_emf_records(hemf, "emf_LineTo MM_TEXT");
+ }
+
+ EnumEnhMetaFile(hdcOffscreen, hemf, EmfEnumProc, &mfp, &rect);
+
+ DeleteEnhMetaFile(hemf);
+ DeleteDC(hdcOffscreen);
+ }
+
+ trace("Testing MF->EMF conversion (NULL mfp)\n");
+ {
+ HDC hdcOffscreen = CreateCompatibleDC(NULL);
+ HENHMETAFILE hemf;
+ RECT rect = { 0, 0, 100, 100 };
+ hemf = create_converted_emf(NULL);
+
+ if (compare_emf_bits(hemf, EMF_LINETO_BITS, sizeof(EMF_LINETO_BITS),
+ "emf_LineTo NULL", TRUE) != 0)
+ {
+ dump_emf_bits(hemf, "emf_LineTo NULL");
+ dump_emf_records(hemf, "emf_LineTo NULL");
+ }
+
+ EnumEnhMetaFile(hdcOffscreen, hemf, EmfEnumProc, NULL, &rect);
+
+ DeleteEnhMetaFile(hemf);
+ DeleteDC(hdcOffscreen);
+ }
+}
+
+static BOOL getConvertedFrameAndBounds(UINT buffer_size, BYTE * buffer, BOOL mfpIsNull,
+ LONG mm, LONG xExt, LONG yExt,
+ RECTL * rclBounds, RECTL * rclFrame)
+{
+ METAFILEPICT mfp;
+ METAFILEPICT * mfpPtr = NULL;
+ HENHMETAFILE emf;
+ ENHMETAHEADER header;
+ UINT res;
+
+ if (!mfpIsNull)
+ {
+ mfp.mm = mm;
+ mfp.xExt = xExt;
+ mfp.yExt = yExt;
+ mfpPtr = &mfp;
+ }
+
+ emf = SetWinMetaFileBits(buffer_size, buffer, NULL, mfpPtr);
+ ok(emf != NULL, "SetWinMetaFileBits failed\n");
+ if (!emf) return FALSE;
+ res = GetEnhMetaFileHeader(emf, sizeof(header), &header);
+ ok(res != 0, "GetEnhMetaHeader failed\n");
+ DeleteEnhMetaFile(emf);
+ if (!res) return FALSE;
+
+ *rclBounds = header.rclBounds;
+ *rclFrame = header.rclFrame;
+ return TRUE;
+}
+
+static void checkConvertedFrameAndBounds(UINT buffer_size, BYTE * buffer, BOOL mfpIsNull,
+ LONG mm, LONG xExt, LONG yExt,
+ RECTL * rclBoundsExpected, RECTL * rclFrameExpected)
+{
+ RECTL rclBounds, rclFrame;
+
+ if (getConvertedFrameAndBounds(buffer_size, buffer, mfpIsNull, mm, xExt, yExt, &rclBounds, &rclFrame))
+ {
+ const char * msg;
+ char buf[64];
+
+ if (mfpIsNull)
+ {
+ msg = "mfp == NULL";
+ }
+ else
+ {
+ const char * mm_str;
+ switch (mm)
+ {
+ case MM_ANISOTROPIC: mm_str = "MM_ANISOTROPIC"; break;
+ case MM_ISOTROPIC: mm_str = "MM_ISOTROPIC"; break;
+ default: mm_str = "Unexpected";
+ }
+ sprintf(buf, "mm=%s, xExt=%ld, yExt=%ld", mm_str, xExt, yExt);
+ msg = buf;
+ }
+
+ ok(rclBounds.left == rclBoundsExpected->left, "rclBounds.left: Expected %ld, got %ld (%s)\n", rclBoundsExpected->left, rclBounds.left, msg);
+ ok(rclBounds.top == rclBoundsExpected->top, "rclBounds.top: Expected %ld, got %ld (%s)\n", rclBoundsExpected->top, rclBounds.top, msg);
+ ok(rclBounds.right == rclBoundsExpected->right, "rclBounds.right: Expected %ld, got %ld (%s)\n", rclBoundsExpected->right, rclBounds.right, msg);
+ ok(rclBounds.bottom == rclBoundsExpected->bottom, "rclBounds.bottom: Expected %ld, got %ld (%s)\n", rclBoundsExpected->bottom, rclBounds.bottom, msg);
+ ok(rclFrame.left == rclFrameExpected->left, "rclFrame.left: Expected %ld, got %ld (%s)\n", rclFrameExpected->left, rclFrame.left, msg);
+ ok(rclFrame.top == rclFrameExpected->top, "rclFrame.top: Expected %ld, got %ld (%s)\n", rclFrameExpected->top, rclFrame.top, msg);
+ ok(rclFrame.right == rclFrameExpected->right, "rclFrame.right: Expected %ld, got %ld (%s)\n", rclFrameExpected->right, rclFrame.right, msg);
+ ok(rclFrame.bottom == rclFrameExpected->bottom, "rclFrame.bottom: Expected %ld, got %ld (%s)\n", rclFrameExpected->bottom, rclFrame.bottom, msg);
+ }
+}
+
+static void test_SetWinMetaFileBits(void)
+{
+ HMETAFILE wmf;
+ HDC wmfDC;
+ BYTE * buffer;
+ UINT buffer_size;
+ RECT rect;
+ UINT res;
+ RECTL rclBoundsAnisotropic, rclFrameAnisotropic;
+ RECTL rclBoundsIsotropic, rclFrameIsotropic;
+ RECTL rclBounds, rclFrame;
+ HDC dc;
+ LONG diffx, diffy;
+
+ wmfDC = CreateMetaFile(NULL);
+ ok(wmfDC != NULL, "CreateMetaFile failed\n");
+ if (!wmfDC) return;
+
+ SetWindowExtEx(wmfDC, 100, 100, NULL);
+ rect.left = rect.top = 0;
+ rect.right = rect.bottom = 50;
+ FillRect(wmfDC, &rect, GetStockObject(BLACK_BRUSH));
+ wmf = CloseMetaFile(wmfDC);
+ ok(wmf != NULL, "Metafile creation failed\n");
+ if (!wmf) return;
+
+ buffer_size = GetMetaFileBitsEx(wmf, 0, NULL);
+ ok(buffer_size != 0, "GetMetaFileBitsEx failed\n");
+ if (buffer_size == 0)
+ {
+ DeleteMetaFile(wmf);
+ return;
+ }
+
+ buffer = (BYTE *)HeapAlloc(GetProcessHeap(), 0, buffer_size);
+ ok(buffer != NULL, "HeapAlloc failed\n");
+ if (!buffer)
+ {
+ DeleteMetaFile(wmf);
+ return;
+ }
+
+ res = GetMetaFileBitsEx(wmf, buffer_size, buffer);
+ ok(res == buffer_size, "GetMetaFileBitsEx failed\n");
+ DeleteMetaFile(wmf);
+ if (res != buffer_size)
+ {
+ HeapFree(GetProcessHeap(), 0, buffer);
+ return;
+ }
+
+ /* Get the reference bounds and frame */
+ getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 0, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 0, 0, &rclBoundsIsotropic, &rclFrameIsotropic);
+
+ ok(rclBoundsAnisotropic.left == 0 && rclBoundsAnisotropic.top == 0 &&
+ rclBoundsIsotropic.left == 0 && rclBoundsIsotropic.top == 0,
+ "SetWinMetaFileBits: Reference bounds: Left and top bound must be zero\n");
+
+ ok(rclBoundsAnisotropic.right >= rclBoundsIsotropic.right, "SetWinMetaFileBits: Reference bounds: Invalid right bound\n");
+ ok(rclBoundsAnisotropic.bottom >= rclBoundsIsotropic.bottom, "SetWinMetaFileBits: Reference bounds: Invalid bottom bound\n");
+ diffx = rclBoundsIsotropic.right - rclBoundsIsotropic.bottom;
+ if (diffx < 0) diffx = -diffx;
+ ok(diffx <= 1, "SetWinMetaFileBits (MM_ISOTROPIC): Reference bounds are not isotropic\n");
+
+ dc = CreateCompatibleDC(NULL);
+ todo_wine
+ {
+ ok(rclBoundsAnisotropic.right == GetDeviceCaps(dc, HORZRES) / 2 - 1 &&
+ rclBoundsAnisotropic.bottom == GetDeviceCaps(dc, VERTRES) / 2 - 1,
+ "SetWinMetaFileBits (MM_ANISOTROPIC): Reference bounds: The whole device surface must be used (%dx%d), but got (%ldx%ld)\n",
+ GetDeviceCaps(dc, HORZRES) / 2 - 1, GetDeviceCaps(dc, VERTRES) / 2 - 1, rclBoundsAnisotropic.right, rclBoundsAnisotropic.bottom);
+ }
+
+ /* Allow 1 mm difference (rounding errors) */
+ diffx = rclFrameAnisotropic.right / 100 - GetDeviceCaps(dc, HORZSIZE) / 2;
+ diffy = rclFrameAnisotropic.bottom / 100 - GetDeviceCaps(dc, VERTSIZE) / 2;
+ if (diffx < 0) diffx = -diffx;
+ if (diffy < 0) diffy = -diffy;
+ todo_wine
+ {
+ ok(diffx <= 1 && diffy <= 1,
+ "SetWinMetaFileBits (MM_ANISOTROPIC): Reference frame: The whole device surface must be used (%dx%d), but got (%ldx%ld)\n",
+ GetDeviceCaps(dc, HORZSIZE) / 2, GetDeviceCaps(dc, VERTSIZE) / 2, rclFrameAnisotropic.right / 100, rclFrameAnisotropic.bottom / 100);
+ }
+ DeleteDC(dc);
+
+ /* If the METAFILEPICT pointer is NULL, the MM_ANISOTROPIC mapping mode and the whole device surface are used */
+ checkConvertedFrameAndBounds(buffer_size, buffer, TRUE, 0, 0, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+
+ /* If xExt or yExt is zero or negative, the whole device surface is used */
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 10000, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 10000, 0, &rclBoundsIsotropic, &rclFrameIsotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 0, 10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 0, 10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, -10000, 0, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, -10000, 0, &rclBoundsIsotropic, &rclFrameIsotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 0, -10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 0, -10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, -10000, 10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, -10000, 10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 10000, -10000, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 10000, -10000, &rclBoundsIsotropic, &rclFrameIsotropic);
+
+ /* MSDN says that negative xExt and yExt values specify a ratio.
+ Check that this is wrong and the whole device surface is used */
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, -1000, -100, &rclBoundsAnisotropic, &rclFrameAnisotropic);
+ checkConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, -1000, -100, &rclBoundsIsotropic, &rclFrameIsotropic);
+
+ /* Ordinary conversions */
+
+ if (getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ANISOTROPIC, 30000, 20000, &rclBounds, &rclFrame))
+ {
+ ok(rclFrame.left == 0 && rclFrame.top == 0 && rclFrame.right == 30000 && rclFrame.bottom == 20000,
+ "SetWinMetaFileBits (MM_ANISOTROPIC): rclFrame contains invalid values\n");
+ ok(rclBounds.left == 0 && rclBounds.top == 0 && rclBounds.right > rclBounds.bottom,
+ "SetWinMetaFileBits (MM_ANISOTROPIC): rclBounds contains invalid values\n");
+ }
+
+ if (getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_ISOTROPIC, 30000, 20000, &rclBounds, &rclFrame))
+ {
+ ok(rclFrame.left == 0 && rclFrame.top == 0 && rclFrame.right == 30000 && rclFrame.bottom == 20000,
+ "SetWinMetaFileBits (MM_ISOTROPIC): rclFrame contains invalid values\n");
+ ok(rclBounds.left == 0 && rclBounds.top == 0,
+ "SetWinMetaFileBits (MM_ISOTROPIC): rclBounds contains invalid values\n");
+
+ /* Wine has a rounding error */
+ diffx = rclBounds.right - rclBounds.bottom;
+ if (diffx < 0) diffx = -diffx;
+ ok(diffx <= 1, "SetWinMetaFileBits (MM_ISOTROPIC): rclBounds is not isotropic\n");
+ }
+
+ if (getConvertedFrameAndBounds(buffer_size, buffer, FALSE, MM_HIMETRIC, 30000, 20000, &rclBounds, &rclFrame))
+ {
+ ok(rclFrame.right - rclFrame.left != 30000 && rclFrame.bottom - rclFrame.top != 20000,
+ "SetWinMetaFileBits: xExt and yExt must be ignored for mapping modes other than MM_ANISOTROPIC and MM_ISOTROPIC\n");
+ }
+
+ HeapFree(GetProcessHeap(), 0, buffer);
+}
+
+static BOOL (WINAPI *pGdiIsMetaPrintDC)(HDC);
+static BOOL (WINAPI *pGdiIsMetaFileDC)(HDC);
+static BOOL (WINAPI *pGdiIsPlayMetafileDC)(HDC);
+
+static void test_gdiis(void)
+{
+ RECT rect = {0,0,100,100};
+ HDC hdc, hemfDC, hmfDC;
+ HENHMETAFILE hemf;
+ HMODULE hgdi32;
+
+ /* resolve all the functions */
+ hgdi32 = GetModuleHandle("gdi32");
+ pGdiIsMetaPrintDC = (void*) GetProcAddress(hgdi32, "GdiIsMetaPrintDC");
+ pGdiIsMetaFileDC = (void*) GetProcAddress(hgdi32, "GdiIsMetaFileDC");
+ pGdiIsPlayMetafileDC = (void*) GetProcAddress(hgdi32, "GdiIsPlayMetafileDC");
+
+ /* they should all exist or none should exist */
+ if(!pGdiIsMetaPrintDC)
+ return;
+
+ /* try with nothing */
+ ok(!pGdiIsMetaPrintDC(NULL), "ismetaprint with NULL parameter\n");
+ ok(!pGdiIsMetaFileDC(NULL), "ismetafile with NULL parameter\n");
+ ok(!pGdiIsPlayMetafileDC(NULL), "isplaymetafile with NULL parameter\n");
+
+ /* try with a metafile */
+ hmfDC = CreateMetaFile(NULL);
+ ok(!pGdiIsMetaPrintDC(hmfDC), "ismetaprint on metafile\n");
+ ok(pGdiIsMetaFileDC(hmfDC), "ismetafile on metafile\n");
+ ok(!pGdiIsPlayMetafileDC(hmfDC), "isplaymetafile on metafile\n");
+ DeleteObject(CloseMetaFile(hmfDC));
+
+ /* try with an enhanced metafile */
+ hdc = GetDC(NULL);
+ hemfDC = CreateEnhMetaFileW(hdc, NULL, &rect, NULL);
+ ok(hemfDC != NULL, "failed to create emf\n");
+
+ ok(!pGdiIsMetaPrintDC(hemfDC), "ismetaprint on emf\n");
+ ok(pGdiIsMetaFileDC(hemfDC), "ismetafile on emf\n");
+ ok(!pGdiIsPlayMetafileDC(hemfDC), "isplaymetafile on emf\n");
+
+ hemf = CloseEnhMetaFile(hemfDC);
+ ok(hemf != NULL, "failed to close EMF\n");
+ DeleteObject(hemf);
+ ReleaseDC(NULL,hdc);
+}
+
+START_TEST(metafile)
+{
+ init_function_pointers();
+
+ /* For enhanced metafiles (enhmfdrv) */
+ test_ExtTextOut();
+ test_SaveDC();
+
+ /* For win-format metafiles (mfdrv) */
+ test_mf_Blank();
+ test_mf_Graphics();
+ test_mf_PatternBrush();
+ test_CopyMetaFile();
+ test_SetMetaFileBits();
+ test_mf_ExtTextOut_on_path();
+ test_emf_ExtTextOut_on_path();
+
+ /* For metafile conversions */
+ test_mf_conversions();
+ test_SetWinMetaFileBits();
+
+ test_gdiis();
+}
--- /dev/null
+/*
+ * Unit test suite for palettes
+ *
+ * Copyright 2005 Glenn Wurster
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <assert.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmsystem.h"
+
+#include "wine/test.h"
+
+static const PALETTEENTRY logpalettedata[8] = {
+ { 0x10, 0x20, 0x30, PC_NOCOLLAPSE },
+ { 0x20, 0x30, 0x40, PC_NOCOLLAPSE },
+ { 0x30, 0x40, 0x50, PC_NOCOLLAPSE },
+ { 0x40, 0x50, 0x60, PC_NOCOLLAPSE },
+ { 0x50, 0x60, 0x70, PC_NOCOLLAPSE },
+ { 0x60, 0x70, 0x80, PC_NOCOLLAPSE },
+ { 0x70, 0x80, 0x90, PC_NOCOLLAPSE },
+ { 0x80, 0x90, 0xA0, PC_NOCOLLAPSE },
+};
+
+static void test_DIB_PAL_COLORS(void) {
+ HDC hdc = GetDC( NULL );
+ HDC memhdc = CreateCompatibleDC( hdc );
+ HBITMAP hbmp, hbmpOld;
+ char bmpbuf[sizeof(BITMAPINFO) + 10 * sizeof(WORD)];
+ PBITMAPINFO bmp = (PBITMAPINFO)bmpbuf;
+ WORD * bmpPalPtr;
+ char logpalettebuf[sizeof(LOGPALETTE) + sizeof(logpalettedata)];
+ PLOGPALETTE logpalette = (PLOGPALETTE)logpalettebuf;
+ HPALETTE hpal, hpalOld;
+ COLORREF setColor, chkColor, getColor;
+ int i;
+
+ /* Initalize the logical palette with a few colours */
+ logpalette->palVersion = 0x300;
+ logpalette->palNumEntries = 8;
+ memcpy( logpalette->palPalEntry, logpalettedata, sizeof(logpalettedata) );
+ hpal = CreatePalette( logpalette );
+ hpalOld = SelectPalette( memhdc, hpal, FALSE );
+ ok( hpalOld != NULL, "error=%ld\n", GetLastError() );
+
+ /* Create a DIB BMP which references colours in the logical palette */
+ memset( bmp, 0x00, sizeof(BITMAPINFO) );
+ bmp->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmp->bmiHeader.biWidth = 1;
+ bmp->bmiHeader.biHeight = 1;
+ bmp->bmiHeader.biPlanes = 1;
+ bmp->bmiHeader.biBitCount = 8;
+ bmp->bmiHeader.biCompression = BI_RGB;
+ bmp->bmiHeader.biClrUsed = 10;
+ bmp->bmiHeader.biClrImportant = 0;
+ bmpPalPtr = (WORD *)&bmp->bmiColors;
+ for( i = 0; i < 8; i++ ) {
+ *bmpPalPtr++ = i;
+ }
+ *bmpPalPtr++ = 8; /* Pointer to logical palette index just outside range */
+ *bmpPalPtr++ = 19; /* Pointer to bad logical palette index */
+
+ hbmp = CreateDIBSection( memhdc, bmp, DIB_PAL_COLORS, 0, 0, 0 );
+ ok( hbmp != NULL, "error=%ld\n", GetLastError() );
+ hbmpOld = SelectObject( memhdc, hbmp );
+ ok( hbmpOld != NULL, "error=%ld\n", GetLastError() );
+
+ /* Test with a RGB to DIB_PAL_COLORS */
+ setColor = RGB( logpalettedata[1].peRed, logpalettedata[1].peGreen, logpalettedata[1].peBlue );
+ SetPixel( memhdc, 0, 0, setColor );
+ chkColor = RGB( logpalettedata[1].peRed, logpalettedata[1].peGreen, logpalettedata[1].peBlue );
+ getColor = GetPixel( memhdc, 0, 0 );
+ ok( getColor == chkColor, "getColor=%08X\n", (UINT)getColor );
+
+ /* Test with a valid DIBINDEX to DIB_PAL_COLORS */
+ setColor = DIBINDEX( 2 );
+ SetPixel( memhdc, 0, 0, setColor );
+ chkColor = RGB( logpalettedata[2].peRed, logpalettedata[2].peGreen, logpalettedata[2].peBlue );
+ getColor = GetPixel( memhdc, 0, 0 );
+ ok( getColor == chkColor, "getColor=%08X\n", (UINT)getColor );
+
+ /* Test with a invalid DIBINDEX to DIB_PAL_COLORS */
+ setColor = DIBINDEX( 12 );
+ SetPixel( memhdc, 0, 0, setColor );
+ chkColor = RGB( 0, 0, 0 );
+ getColor = GetPixel( memhdc, 0, 0 );
+ ok( getColor == chkColor, "getColor=%08X\n", (UINT)getColor );
+
+ /* Test for double wraparound on logical palette references from */
+ /* DIBINDEX by DIB_PAL_COLORS. */
+ setColor = DIBINDEX( 9 );
+ SetPixel( memhdc, 0, 0, setColor );
+ chkColor = RGB( logpalettedata[3].peRed, logpalettedata[3].peGreen, logpalettedata[3].peBlue );
+ getColor = GetPixel( memhdc, 0, 0 );
+ ok( getColor == chkColor, "getColor=%08X\n", (UINT)getColor );
+
+ SelectPalette( memhdc, hpalOld, FALSE );
+ DeleteObject( hpal );
+ SelectObject( memhdc, hbmpOld );
+ DeleteObject( hbmp );
+ DeleteDC( memhdc );
+ ReleaseDC( NULL, hdc );
+}
+
+START_TEST(palette)
+{
+ test_DIB_PAL_COLORS();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_bitmap(void);
+extern void func_brush(void);
+extern void func_clipping(void);
+extern void func_dc(void);
+extern void func_font(void);
+extern void func_gdiobj(void);
+extern void func_generated(void);
+extern void func_mapping(void);
+extern void func_metafile(void);
+extern void func_palette(void);
+extern void func_pen(void);
+
+const struct test winetest_testlist[] =
+{
+ { "bitmap", func_bitmap },
+ { "brush", func_brush },
+ { "clipping", func_clipping },
+ { "dc", func_dc },
+ { "font", func_font },
+ { "gdiobj", func_gdiobj },
+// { "generated", func_generated },
+ { "mapping", func_mapping },
+ { "metafile", func_metafile },
+ { "palette", func_palette },
+// { "pen", func_pen },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * Unit test suite for Icmp.dll functions
+ *
+ * Copyright 2006 Steven Edwards
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * TODO:
+ * It seems under Windows XP, 2003 and Vista these functions are not implemented
+ * in iphlpapi. Move the implementation and tests there.
+ */
+
+#include <windows.h>
+#include "wine/test.h"
+
+HANDLE WINAPI IcmpCreateFile(void);
+BOOL WINAPI IcmpCloseHandle(HANDLE handle);
+
+HANDLE handle;
+
+static void test_IcmpCreateFile(void)
+{
+ handle=IcmpCreateFile();
+ ok(handle!=INVALID_HANDLE_VALUE,"Failed to create icmp file handle\n");
+}
+
+static void test_IcmpCloseHandle(void)
+{
+ BOOL result;
+ result=IcmpCloseHandle(handle);
+ ok(result!=FALSE,"Failed to close icmp file handle\n");
+}
+
+START_TEST(icmp)
+{
+ test_IcmpCreateFile();
+ test_IcmpCloseHandle();
+}
--- /dev/null
+<module name="icmp_winetest" type="win32cui" installbase="bin" installname="icmp_winetest.exe" allowwarnings="true">
+ <include base="icmp_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <library>icmp</library>
+ <file>icmp.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_icmp(void);
+
+const struct test winetest_testlist[] =
+{
+ { "icmp", func_icmp },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * Unit test suite for memory allocation functions.
+ *
+ * Copyright 2002 Geoffrey Hausheer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+
+ /* The following functions don't have tests, because either I don't know how
+ to test them, or they are WinNT only, or require multiple threads.
+ Since the last two issues shouldn't really stop the tests from being
+ written, assume for now that it is all due to the first case
+ HeapCompact
+ HeapLock
+ HeapQueryInformation
+ HeapSetInformation
+ HeapUnlock
+ HeapValidate
+ HeapWalk
+*/
+/* In addition, these features aren't being tested
+ HEAP_NO_SERIALIZE
+ HEAP_GENERATE_EXCEPTIONS
+ STATUS_ACCESS_VIOLATION (error code from HeapAlloc)
+*/
+
+static void test_Heap(void)
+{
+ SYSTEM_INFO sysInfo;
+ ULONG memchunk;
+ HANDLE heap;
+ LPVOID mem1,mem1a,mem3;
+ UCHAR *mem2,*mem2a;
+ UINT error,i;
+ DWORD dwSize;
+
+/* Retrieve the page size for this system */
+ sysInfo.dwPageSize=0;
+ GetSystemInfo(&sysInfo);
+ ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
+
+/* Create a Heap with a minimum and maximum size */
+/* Note that Windows and Wine seem to behave a bit differently with respect
+ to memory allocation. In Windows, you can't access all the memory
+ specified in the heap (due to overhead), so choosing a reasonable maximum
+ size for the heap was done mostly by trial-and-error on Win2k. It may need
+ more tweaking for otherWindows variants.
+*/
+ memchunk=10*sysInfo.dwPageSize;
+ heap=HeapCreate(0,2*memchunk,5*memchunk);
+
+/* Check that HeapCreate allocated the right amount of ram */
+ todo_wine {
+ /* Today HeapCreate seems to return a memory block larger than specified.
+ MSDN says the maximum heap size should be dwMaximumSize rounded up to the
+ nearest page boundary
+ */
+ mem1=HeapAlloc(heap,0,5*memchunk+1);
+ ok(mem1==NULL,"HeapCreate allocated more Ram than it should have\n");
+ HeapFree(heap,0,mem1);
+ }
+
+/* Check that a normal alloc works */
+ mem1=HeapAlloc(heap,0,memchunk);
+ ok(mem1!=NULL,"HeapAlloc failed\n");
+ if(mem1) {
+ ok(HeapSize(heap,0,mem1)>=memchunk, "HeapAlloc should return a big enough memory block\n");
+ }
+
+/* Check that a 'zeroing' alloc works */
+ mem2=HeapAlloc(heap,HEAP_ZERO_MEMORY,memchunk);
+ ok(mem2!=NULL,"HeapAlloc failed\n");
+ if(mem2) {
+ ok(HeapSize(heap,0,mem2)>=memchunk,"HeapAlloc should return a big enough memory block\n");
+ error=0;
+ for(i=0;i<memchunk;i++) {
+ if(mem2[i]!=0) {
+ error=1;
+ }
+ }
+ ok(!error,"HeapAlloc should have zeroed out it's allocated memory\n");
+ }
+
+/* Check that HeapAlloc returns NULL when requested way too much memory */
+ mem3=HeapAlloc(heap,0,5*memchunk);
+ ok(mem3==NULL,"HeapAlloc should return NULL\n");
+ if(mem3) {
+ ok(HeapFree(heap,0,mem3),"HeapFree didn't pass successfully\n");
+ }
+
+/* Check that HeapRealloc works */
+ mem2a=HeapReAlloc(heap,HEAP_ZERO_MEMORY,mem2,memchunk+5*sysInfo.dwPageSize);
+ ok(mem2a!=NULL,"HeapReAlloc failed\n");
+ if(mem2a) {
+ ok(HeapSize(heap,0,mem2a)>=memchunk+5*sysInfo.dwPageSize,"HeapReAlloc failed\n");
+ error=0;
+ for(i=0;i<5*sysInfo.dwPageSize;i++) {
+ if(mem2a[memchunk+i]!=0) {
+ error=1;
+ }
+ }
+ ok(!error,"HeapReAlloc should have zeroed out it's allocated memory\n");
+ }
+
+/* Check that HeapRealloc honours HEAP_REALLOC_IN_PLACE_ONLY */
+ error=0;
+ mem1a=HeapReAlloc(heap,HEAP_REALLOC_IN_PLACE_ONLY,mem1,memchunk+sysInfo.dwPageSize);
+ if(mem1a!=NULL) {
+ if(mem1a!=mem1) {
+ error=1;
+ }
+ }
+ ok(mem1a==NULL || error==0,"HeapReAlloc didn't honour HEAP_REALLOC_IN_PLACE_ONLY\n");
+
+/* Check that HeapFree works correctly */
+ if(mem1a) {
+ ok(HeapFree(heap,0,mem1a),"HeapFree failed\n");
+ } else {
+ ok(HeapFree(heap,0,mem1),"HeapFree failed\n");
+ }
+ if(mem2a) {
+ ok(HeapFree(heap,0,mem2a),"HeapFree failed\n");
+ } else {
+ ok(HeapFree(heap,0,mem2),"HeapFree failed\n");
+ }
+
+ /* 0-length buffer */
+ mem1 = HeapAlloc(heap, 0, 0);
+ ok(mem1 != NULL, "Reserved memory\n");
+
+ dwSize = HeapSize(heap, 0, mem1);
+ /* should work with 0-length buffer */
+ ok((dwSize >= 0) && (dwSize < 0xFFFFFFFF),
+ "The size of the 0-length buffer\n");
+ ok(HeapFree(heap, 0, mem1), "Freed the 0-length buffer\n");
+
+/* Check that HeapDestry works */
+ ok(HeapDestroy(heap),"HeapDestroy failed\n");
+}
+
+/* The following functions don't have tests, because either I don't know how
+ to test them, or they are WinNT only, or require multiple threads.
+ Since the last two issues shouldn't really stop the tests from being
+ written, assume for now that it is all due to the first case
+ GlobalFlags
+ GlobalMemoryStatus
+ GlobalMemoryStatusEx
+*/
+/* In addition, these features aren't being tested
+ GMEM_DISCADABLE
+ GMEM_NOCOMPACT
+*/
+static void test_Global(void)
+{
+ ULONG memchunk;
+ HGLOBAL mem1,mem2,mem2a,mem2b;
+ UCHAR *mem2ptr;
+ UINT error,i;
+ memchunk=100000;
+
+ SetLastError(NO_ERROR);
+/* Check that a normal alloc works */
+ mem1=GlobalAlloc(0,memchunk);
+ ok(mem1!=NULL,"GlobalAlloc failed\n");
+ if(mem1) {
+ ok(GlobalSize(mem1)>=memchunk, "GlobalAlloc should return a big enough memory block\n");
+ }
+
+/* Check that a 'zeroing' alloc works */
+ mem2=GlobalAlloc(GMEM_ZEROINIT,memchunk);
+ ok(mem2!=NULL,"GlobalAlloc failed: error=%ld\n",GetLastError());
+ if(mem2) {
+ ok(GlobalSize(mem2)>=memchunk,"GlobalAlloc should return a big enough memory block\n");
+ mem2ptr=GlobalLock(mem2);
+ ok(mem2ptr==mem2,"GlobalLock should have returned the same memory as was allocated\n");
+ if(mem2ptr) {
+ error=0;
+ for(i=0;i<memchunk;i++) {
+ if(mem2ptr[i]!=0) {
+ error=1;
+ }
+ }
+ ok(!error,"GlobalAlloc should have zeroed out it's allocated memory\n");
+ }
+ }
+/* Check that GlobalReAlloc works */
+/* Check that we can change GMEM_FIXED to GMEM_MOVEABLE */
+ mem2a=GlobalReAlloc(mem2,0,GMEM_MODIFY | GMEM_MOVEABLE);
+ if(mem2a!=NULL) {
+ mem2=mem2a;
+ mem2ptr=GlobalLock(mem2a);
+ ok(mem2ptr!=NULL && !GlobalUnlock(mem2a)&&GetLastError()==NO_ERROR,
+ "Converting from FIXED to MOVEABLE didn't REALLY work\n");
+ }
+
+/* Check that ReAllocing memory works as expected */
+ mem2a=GlobalReAlloc(mem2,2*memchunk,GMEM_MOVEABLE | GMEM_ZEROINIT);
+ ok(mem2a!=NULL,"GlobalReAlloc failed\n");
+ if(mem2a) {
+ ok(GlobalSize(mem2a)>=2*memchunk,"GlobalReAlloc failed\n");
+ mem2ptr=GlobalLock(mem2a);
+ ok(mem2ptr!=NULL,"GlobalLock Failed\n");
+ if(mem2ptr) {
+ error=0;
+ for(i=0;i<memchunk;i++) {
+ if(mem2ptr[memchunk+i]!=0) {
+ error=1;
+ }
+ }
+ ok(!error,"GlobalReAlloc should have zeroed out it's allocated memory\n");
+
+/* Check that GlobalHandle works */
+ mem2b=GlobalHandle(mem2ptr);
+ ok(mem2b==mem2a,"GlobalHandle didn't return the correct memory handle\n");
+
+/* Check that we can't discard locked memory */
+ mem2b=GlobalDiscard(mem2a);
+ if(mem2b==NULL) {
+ ok(!GlobalUnlock(mem2a) && GetLastError()==NO_ERROR,"GlobalUnlock Failed\n");
+ }
+ }
+ }
+ if(mem1) {
+ ok(GlobalFree(mem1)==NULL,"GlobalFree failed\n");
+ }
+ if(mem2a) {
+ ok(GlobalFree(mem2a)==NULL,"GlobalFree failed\n");
+ } else {
+ ok(GlobalFree(mem2)==NULL,"GlobalFree failed\n");
+ }
+}
+
+
+/* The following functions don't have tests, because either I don't know how
+ to test them, or they are WinNT only, or require multiple threads.
+ Since the last two issues shouldn't really stop the tests from being
+ written, assume for now that it is all due to the first case
+ LocalDiscard
+ LocalFlags
+*/
+/* In addition, these features aren't being tested
+ LMEM_DISCADABLE
+ LMEM_NOCOMPACT
+*/
+static void test_Local(void)
+{
+ ULONG memchunk;
+ HLOCAL mem1,mem2,mem2a,mem2b;
+ UCHAR *mem2ptr;
+ UINT error,i;
+ memchunk=100000;
+
+/* Check that a normal alloc works */
+ mem1=LocalAlloc(0,memchunk);
+ ok(mem1!=NULL,"LocalAlloc failed: error=%ld\n",GetLastError());
+ if(mem1) {
+ ok(LocalSize(mem1)>=memchunk, "LocalAlloc should return a big enough memory block\n");
+ }
+
+/* Check that a 'zeroing' and lock alloc works */
+ mem2=LocalAlloc(LMEM_ZEROINIT|LMEM_MOVEABLE,memchunk);
+ ok(mem2!=NULL,"LocalAlloc failed: error=%ld\n",GetLastError());
+ if(mem2) {
+ ok(LocalSize(mem2)>=memchunk,"LocalAlloc should return a big enough memory block\n");
+ mem2ptr=LocalLock(mem2);
+ ok(mem2ptr!=NULL,"LocalLock: error=%ld\n",GetLastError());
+ if(mem2ptr) {
+ error=0;
+ for(i=0;i<memchunk;i++) {
+ if(mem2ptr[i]!=0) {
+ error=1;
+ }
+ }
+ ok(!error,"LocalAlloc should have zeroed out it's allocated memory\n");
+ SetLastError(0);
+ error=LocalUnlock(mem2);
+ ok(error==0 && GetLastError()==NO_ERROR,
+ "LocalUnlock Failed: rc=%d err=%ld\n",error,GetLastError());
+ }
+ }
+ mem2a=LocalFree(mem2);
+ ok(mem2a==NULL, "LocalFree failed: %p\n",mem2a);
+
+/* Reallocate mem2 as moveable memory */
+ mem2=LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,memchunk);
+ ok(mem2!=NULL, "LocalAlloc failed to create moveable memory, error=%ld\n",GetLastError());
+
+/* Check that ReAllocing memory works as expected */
+ mem2a=LocalReAlloc(mem2,2*memchunk,LMEM_MOVEABLE | LMEM_ZEROINIT);
+ ok(mem2a!=NULL,"LocalReAlloc failed, error=%ld\n",GetLastError());
+ if(mem2a) {
+ ok(LocalSize(mem2a)>=2*memchunk,"LocalReAlloc failed\n");
+ mem2ptr=LocalLock(mem2a);
+ ok(mem2ptr!=NULL,"LocalLock Failed\n");
+ if(mem2ptr) {
+ error=0;
+ for(i=0;i<memchunk;i++) {
+ if(mem2ptr[memchunk+i]!=0) {
+ error=1;
+ }
+ }
+ ok(!error,"LocalReAlloc should have zeroed out it's allocated memory\n");
+/* Check that LocalHandle works */
+ mem2b=LocalHandle(mem2ptr);
+ ok(mem2b==mem2a,"LocalHandle didn't return the correct memory handle\n");
+/* Check that we can't discard locked memory */
+ mem2b=LocalDiscard(mem2a);
+ ok(mem2b==NULL,"Discarded memory we shouldn't have\n");
+ SetLastError(NO_ERROR);
+ ok(!LocalUnlock(mem2a) && GetLastError()==NO_ERROR, "LocalUnlock Failed\n");
+ }
+ }
+ if(mem1) {
+ ok(LocalFree(mem1)==NULL,"LocalFree failed\n");
+ }
+ if(mem2a) {
+ ok(LocalFree(mem2a)==NULL,"LocalFree failed\n");
+ } else {
+ ok(LocalFree(mem2)==NULL,"LocalFree failed\n");
+ }
+}
+
+/* The Virtual* routines are not tested as thoroughly,
+ since I don't really understand how to use them correctly :)
+ The following routines are not tested at all
+ VirtualAllocEx
+ VirtualFreeEx
+ VirtualLock
+ VirtualProtect
+ VirtualProtectEx
+ VirtualQuery
+ VirtualQueryEx
+ VirtualUnlock
+ And the only features (flags) being tested are
+ MEM_COMMIT
+ MEM_RELEASE
+ PAGE_READWRITE
+ Testing the rest requires using exceptions, which I really don't
+ understand well
+*/
+static void test_Virtual(void)
+{
+ SYSTEM_INFO sysInfo;
+ ULONG memchunk;
+ UCHAR *mem1;
+ UINT error,i;
+
+/* Retrieve the page size for this system */
+ sysInfo.dwPageSize=0;
+ GetSystemInfo(&sysInfo);
+ ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
+
+/* Choose a reasonable allocation size */
+ memchunk=10*sysInfo.dwPageSize;
+
+/* Check that a normal alloc works */
+ mem1=VirtualAlloc(NULL,memchunk,MEM_COMMIT,PAGE_READWRITE);
+ ok(mem1!=NULL,"VirtualAlloc failed\n");
+ if(mem1) {
+/* check that memory is initialized to 0 */
+ error=0;
+ for(i=0;i<memchunk;i++) {
+ if(mem1[i]!=0) {
+ error=1;
+ }
+ }
+ ok(!error,"VirtualAlloc did not initialize memory to '0's\n");
+/* Check that we can read/write to memory */
+ error=0;
+ for(i=0;i<memchunk;i+=100) {
+ mem1[i]='a';
+ if(mem1[i]!='a') {
+ error=1;
+ }
+ }
+ ok(!error,"Virtual memory was not writable\n");
+ }
+ ok(VirtualFree(mem1,0,MEM_RELEASE),"VirtualFree failed\n");
+}
+START_TEST(alloc)
+{
+ test_Heap();
+ test_Global();
+ test_Local();
+ test_Virtual();
+}
--- /dev/null
+/*
+ * Unit tests for atom functions
+ *
+ * Copyright (c) 2002 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winuser.h"
+
+#define DOUBLE(x) (WCHAR)((x<<8)|(x))
+
+static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
+static const WCHAR FOOBARW[] = {'F','O','O','B','A','R',0};
+static const WCHAR _foobarW[] = {'_','f','o','o','b','a','r',0};
+
+static void do_initA(char* tmp, const char* pattern, int len)
+{
+ const char* p = pattern;
+
+ while (len--)
+ {
+ *tmp++ = *p++;
+ if (!*p) p = pattern;
+ }
+ *tmp = '\0';
+}
+
+static void do_initW(WCHAR* tmp, const char* pattern, int len)
+{
+ const char* p = pattern;
+
+ while (len--)
+ {
+ *tmp++ = *p++;
+ if (!*p) p = pattern;
+ }
+ *tmp = '\0';
+}
+
+static void print_integral( WCHAR* buffer, int atom )
+{
+ BOOL first = TRUE;
+
+#define X(v) { if (atom >= v) {*buffer++ = '0' + atom / v; first = FALSE; } else if (!first || v == 1) *buffer++ = '0'; atom %= v; }
+ *buffer++ = '#';
+ X(10000);
+ X(1000);
+ X(100);
+ X(10);
+ X(1);
+ *buffer = '\0';
+#undef X
+}
+
+static BOOL unicode_OS;
+
+static void test_add_atom(void)
+{
+ ATOM atom, w_atom;
+ int i;
+
+ SetLastError( 0xdeadbeef );
+ atom = GlobalAddAtomA( "foobar" );
+ ok( atom >= 0xc000, "bad atom id %x\n", atom );
+ ok( GetLastError() == 0xdeadbeef, "GlobalAddAtomA set last error\n" );
+
+ /* Verify that it can be found (or not) appropriately */
+ ok( GlobalFindAtomA( "foobar" ) == atom, "could not find atom foobar\n" );
+ ok( GlobalFindAtomA( "FOOBAR" ) == atom, "could not find atom FOOBAR\n" );
+ ok( !GlobalFindAtomA( "_foobar" ), "found _foobar\n" );
+
+ /* Add the same atom, specifying string as unicode; should
+ * find the first one, not add a new one */
+ SetLastError( 0xdeadbeef );
+ w_atom = GlobalAddAtomW( foobarW );
+ if (w_atom && GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+ unicode_OS = TRUE;
+ else
+ trace("WARNING: Unicode atom APIs are not supported on this platform\n");
+
+ if (unicode_OS)
+ {
+ ok( w_atom == atom, "Unicode atom does not match ASCII\n" );
+ ok( GetLastError() == 0xdeadbeef, "GlobalAddAtomW set last error\n" );
+ }
+
+ /* Verify that it can be found (or not) appropriately via unicode name */
+ if (unicode_OS)
+ {
+ ok( GlobalFindAtomW( foobarW ) == atom, "could not find atom foobar\n" );
+ ok( GlobalFindAtomW( FOOBARW ) == atom, "could not find atom FOOBAR\n" );
+ ok( !GlobalFindAtomW( _foobarW ), "found _foobar\n" );
+ }
+
+ /* Test integer atoms
+ * (0x0001 .. 0xbfff) should be valid;
+ * (0xc000 .. 0xffff) should be invalid */
+
+ SetLastError( 0xdeadbeef );
+ ok( GlobalAddAtomA(0) == 0 && GetLastError() == 0xdeadbeef, "succeeded to add atom 0\n" );
+ if (unicode_OS)
+ {
+ SetLastError( 0xdeadbeef );
+ ok( GlobalAddAtomW(0) == 0 && GetLastError() == 0xdeadbeef, "succeeded to add atom 0\n" );
+ }
+
+ SetLastError( 0xdeadbeef );
+ for (i = 1; i <= 0xbfff; i++)
+ {
+ SetLastError( 0xdeadbeef );
+ ok( GlobalAddAtomA((LPCSTR)i) == i && GetLastError() == 0xdeadbeef,
+ "failed to add atom %x\n", i );
+ if (unicode_OS)
+ {
+ SetLastError( 0xdeadbeef );
+ ok( GlobalAddAtomW((LPCWSTR)i) == i && GetLastError() == 0xdeadbeef,
+ "failed to add atom %x\n", i );
+ }
+ }
+
+ for (i = 0xc000; i <= 0xffff; i++)
+ {
+ ok( !GlobalAddAtomA((LPCSTR)i), "succeeded adding %x\n", i );
+ if (unicode_OS)
+ ok( !GlobalAddAtomW((LPCWSTR)i), "succeeded adding %x\n", i );
+ }
+}
+
+static void test_get_atom_name(void)
+{
+ char buf[10];
+ WCHAR bufW[10];
+ int i;
+ UINT len;
+ static const WCHAR resultW[] = {'f','o','o','b','a','r',0,'.','.','.'};
+ char in[257], out[257];
+ WCHAR inW[257], outW[257];
+
+ ATOM atom = GlobalAddAtomA( "foobar" );
+
+ /* Get the name of the atom we added above */
+ memset( buf, '.', sizeof(buf) );
+ len = GlobalGetAtomNameA( atom, buf, 10 );
+ ok( len == strlen("foobar"), "bad length %d\n", len );
+ ok( !memcmp( buf, "foobar\0...", 10 ), "bad buffer contents\n" );
+
+ /* Repeat, unicode-style */
+ if (unicode_OS)
+ {
+ for (i = 0; i < 10; i++) bufW[i] = '.';
+ SetLastError( 0xdeadbeef );
+ len = GlobalGetAtomNameW( atom, bufW, 10 );
+ ok( len && GetLastError() == 0xdeadbeef, "GlobalGetAtomNameW failed\n" );
+ ok( len == lstrlenW(foobarW), "bad length %d\n", len );
+ ok( !memcmp( bufW, resultW, 10*sizeof(WCHAR) ), "bad buffer contents\n" );
+ }
+
+ /* Check error code returns */
+ memset(buf, '.', 10);
+ ok( !GlobalGetAtomNameA( atom, buf, 0 ), "succeeded\n" );
+ ok( !memcmp( buf, "..........", 10 ), "should not touch buffer\n" );
+
+ if (unicode_OS)
+ {
+ static const WCHAR sampleW[10] = {'.','.','.','.','.','.','.','.','.','.'};
+
+ for (i = 0; i < 10; i++) bufW[i] = '.';
+ ok( !GlobalGetAtomNameW( atom, bufW, 0 ), "succeeded\n" );
+ ok( !memcmp( bufW, sampleW, 10 * sizeof(WCHAR) ), "should not touch buffer\n" );
+ }
+
+ /* Test integer atoms */
+ for (i = 0; i <= 0xbfff; i++)
+ {
+ memset( buf, 'a', 10 );
+ len = GlobalGetAtomNameA( (ATOM)i, buf, 10 );
+ if (i)
+ {
+ char res[20];
+ ok( (len > 1) && (len < 7), "bad length %d\n", len );
+ sprintf( res, "#%d", i );
+ memset( res + strlen(res) + 1, 'a', 10 );
+ ok( !memcmp( res, buf, 10 ), "bad buffer contents %s\n", buf );
+ }
+ else
+ ok( !len, "bad length %d\n", len );
+
+ SetLastError(0xdeadbeef);
+ len = GlobalGetAtomNameA( (ATOM)i, buf, 2);
+ if (!len) /* the NT way */
+ {
+ ok(GetLastError() == (i ? ERROR_MORE_DATA : ERROR_INVALID_PARAMETER) ||
+ GetLastError() == 0xdeadbeef, /* the Win 9x way */
+ "wrong error conditions %lu for %u\n", GetLastError(), i);
+ }
+ else /* the Win 9x way */
+ {
+ ok(GetLastError() == 0xdeadbeef,
+ "wrong error conditions %lu for %u\n", GetLastError(), i);
+ }
+ }
+
+ memset( buf, '.', sizeof(buf) );
+ len = GlobalGetAtomNameA( atom, buf, 6 );
+ ok( len == 0, "bad length %d\n", len );
+ ok( !memcmp( buf, "fooba\0....", 10 ), "bad buffer contents\n");
+ if (unicode_OS)
+ {
+ static const WCHAR resW[] = {'f','o','o','b','a','r','.','.','.','.'};
+ for (len = 0; len < 10; len++) bufW[len] = '.';
+ SetLastError(0xdeadbeef);
+ len = GlobalGetAtomNameW( atom, bufW, 6 );
+ ok( len && GetLastError() == 0xdeadbeef, "GlobalGetAtomNameW failed\n" );
+ ok( len == lstrlenW(foobarW), "bad length %d\n", len );
+ ok( !memcmp( bufW, resW, 10*sizeof(WCHAR) ), "bad buffer contents\n" );
+ }
+
+ /* test string limits & overflow */
+ do_initA(in, "abcdefghij", 255);
+ atom = GlobalAddAtomA(in);
+ ok(atom, "couldn't add atom for %s\n", in);
+ len = GlobalGetAtomNameA(atom, out, sizeof(out));
+ ok(len == 255, "length mismatch (%u instead of 255)\n", len);
+ for (i = 0; i < 255; i++)
+ {
+ ok(out[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, out[i], "abcdefghij"[i % 10]);
+ }
+ ok(out[255] == '\0', "wrong end of string\n");
+ memset(out, '.', sizeof(out));
+ SetLastError(0xdeadbeef);
+ len = GlobalGetAtomNameA(atom, out, 10);
+ if (!len) /* the NT way */
+ {
+ ok(GetLastError() == ERROR_MORE_DATA, "wrong error code (%lu instead of %lu)\n", GetLastError(), (DWORD)ERROR_MORE_DATA);
+ }
+ else /* the Win9x way */
+ {
+ ok(GetLastError() == 0xdeadbeef, "wrong error code (%lu instead of %u)\n", GetLastError(), 0xdeadbeef);
+ }
+ for (i = 0; i < 9; i++)
+ {
+ ok(out[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, out[i], "abcdefghij"[i % 10]);
+ }
+ ok(out[9] == '\0', "wrong end of string\n");
+ ok(out[10] == '.', "wrote after end of buf\n");
+ do_initA(in, "abcdefghij", 256);
+ atom = GlobalAddAtomA(in);
+ ok(!atom, "succeeded\n");
+ if (unicode_OS)
+ {
+ /* test integral atoms */
+ for (i = 0; i <= 0xbfff; i++)
+ {
+ memset(outW, 'a', sizeof(outW));
+ len = GlobalGetAtomNameW( (ATOM)i, outW, 10 );
+ if (i)
+ {
+ WCHAR res[20];
+
+ ok( (len > 1) && (len < 7), "bad length %d\n", len );
+ print_integral( res, i );
+ memset( res + lstrlenW(res) + 1, 'a', 10 * sizeof(WCHAR));
+ ok( !memcmp( res, outW, 10 * sizeof(WCHAR) ), "bad buffer contents for %d\n", i );
+ }
+ else
+ ok( !len, "bad length %d\n", len );
+
+ memset(outW, '.', sizeof(outW));
+ SetLastError(0xdeadbeef);
+ len = GlobalGetAtomNameW( (ATOM)i, outW, 1);
+ if (i)
+ {
+ /* len == 0 with ERROR_MORE_DATA is on NT3.51 */
+ ok(len == 1 || (len == 0 && GetLastError() == ERROR_MORE_DATA),
+ "0x%04x: got %u with %ld (excepted '1' or '0' with " \
+ "ERROR_MORE_DATA)\n", i, len, GetLastError());
+ ok(outW[1] == DOUBLE('.'), "buffer overwrite\n");
+ }
+ else ok(len == 0 && GetLastError() == ERROR_INVALID_PARAMETER, "0 badly handled\n");
+ }
+
+ do_initW(inW, "abcdefghij", 255);
+ atom = GlobalAddAtomW(inW);
+ ok(atom, "couldn't add atom for %s\n", in);
+ len = GlobalGetAtomNameW(atom, outW, sizeof(outW));
+ ok(len == 255, "length mismatch (%u instead of 255)\n", len);
+ for (i = 0; i < 255; i++)
+ {
+ ok(outW[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, outW[i], "abcdefghij"[i % 10]);
+ }
+ ok(outW[255] == '\0', "wrong end of string\n");
+ memset(outW, '.', sizeof(outW));
+ len = GlobalGetAtomNameW(atom, outW, 10);
+ ok(len == 10, "succeeded\n");
+ for (i = 0; i < 10; i++)
+ {
+ ok(outW[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, outW[i], "abcdefghij"[i % 10]);
+ }
+ ok(outW[10] == DOUBLE('.'), "wrote after end of buf\n");
+ do_initW(inW, "abcdefghij", 256);
+ atom = GlobalAddAtomW(inW);
+ ok(!atom, "succeeded\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error code\n");
+ }
+}
+
+static void test_error_handling(void)
+{
+ char buffer[260];
+ WCHAR bufferW[260];
+ int i;
+
+ memset( buffer, 'a', 256 );
+ buffer[256] = 0;
+ ok( !GlobalAddAtomA(buffer), "add succeeded\n" );
+ ok( !GlobalFindAtomA(buffer), "find succeeded\n" );
+
+ if (unicode_OS)
+ {
+ for (i = 0; i < 256; i++) bufferW[i] = 'b';
+ bufferW[256] = 0;
+ ok( !GlobalAddAtomW(bufferW), "add succeeded\n" );
+ ok( !GlobalFindAtomW(bufferW), "find succeeded\n" );
+ }
+}
+
+static void test_local_add_atom(void)
+{
+ ATOM atom, w_atom;
+ int i;
+
+ SetLastError( 0xdeadbeef );
+ atom = AddAtomA( "foobar" );
+ ok( atom >= 0xc000, "bad atom id %x\n", atom );
+ ok( GetLastError() == 0xdeadbeef, "AddAtomA set last error\n" );
+
+ /* Verify that it can be found (or not) appropriately */
+ ok( FindAtomA( "foobar" ) == atom, "could not find atom foobar\n" );
+ ok( FindAtomA( "FOOBAR" ) == atom, "could not find atom FOOBAR\n" );
+ ok( !FindAtomA( "_foobar" ), "found _foobar\n" );
+
+ /* Add the same atom, specifying string as unicode; should
+ * find the first one, not add a new one */
+ SetLastError( 0xdeadbeef );
+ w_atom = AddAtomW( foobarW );
+ if (w_atom && GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+ unicode_OS = TRUE;
+ else
+ trace("WARNING: Unicode atom APIs are not supported on this platform\n");
+
+ if (unicode_OS)
+ {
+ ok( w_atom == atom, "Unicode atom does not match ASCII\n" );
+ ok( GetLastError() == 0xdeadbeef, "AddAtomW set last error\n" );
+ }
+
+ /* Verify that it can be found (or not) appropriately via unicode name */
+ if (unicode_OS)
+ {
+ ok( FindAtomW( foobarW ) == atom, "could not find atom foobar\n" );
+ ok( FindAtomW( FOOBARW ) == atom, "could not find atom FOOBAR\n" );
+ ok( !FindAtomW( _foobarW ), "found _foobar\n" );
+ }
+
+ /* Test integer atoms
+ * (0x0001 .. 0xbfff) should be valid;
+ * (0xc000 .. 0xffff) should be invalid */
+
+ SetLastError( 0xdeadbeef );
+ ok( AddAtomA(0) == 0 && GetLastError() == 0xdeadbeef, "succeeded to add atom 0\n" );
+ if (unicode_OS)
+ {
+ SetLastError( 0xdeadbeef );
+ ok( AddAtomW(0) == 0 && GetLastError() == 0xdeadbeef, "succeeded to add atom 0\n" );
+ }
+
+ SetLastError( 0xdeadbeef );
+ for (i = 1; i <= 0xbfff; i++)
+ {
+ SetLastError( 0xdeadbeef );
+ ok( AddAtomA((LPCSTR)i) == i && GetLastError() == 0xdeadbeef,
+ "failed to add atom %x\n", i );
+ if (unicode_OS)
+ {
+ SetLastError( 0xdeadbeef );
+ ok( AddAtomW((LPCWSTR)i) == i && GetLastError() == 0xdeadbeef,
+ "failed to add atom %x\n", i );
+ }
+ }
+
+ for (i = 0xc000; i <= 0xffff; i++)
+ {
+ ok( !AddAtomA((LPCSTR)i), "succeeded adding %x\n", i );
+ if (unicode_OS)
+ ok( !AddAtomW((LPCWSTR)i), "succeeded adding %x\n", i );
+ }
+}
+
+static void test_local_get_atom_name(void)
+{
+ char buf[10], in[257], out[257];
+ WCHAR bufW[10], inW[257], outW[257];
+ int i;
+ UINT len;
+ static const WCHAR resultW[] = {'f','o','o','b','a','r',0,'.','.','.'};
+
+ ATOM atom = AddAtomA( "foobar" );
+
+ /* Get the name of the atom we added above */
+ memset( buf, '.', sizeof(buf) );
+ len = GetAtomNameA( atom, buf, 10 );
+ ok( len == strlen("foobar"), "bad length %d\n", len );
+ ok( !memcmp( buf, "foobar\0...", 10 ), "bad buffer contents\n" );
+
+ /* Repeat, unicode-style */
+ if (unicode_OS)
+ {
+ for (i = 0; i < 10; i++) bufW[i] = '.';
+ SetLastError( 0xdeadbeef );
+ len = GetAtomNameW( atom, bufW, 10 );
+ ok( len && GetLastError() == 0xdeadbeef, "GetAtomNameW failed\n" );
+ ok( len == lstrlenW(foobarW), "bad length %d\n", len );
+ ok( !memcmp( bufW, resultW, 10*sizeof(WCHAR) ), "bad buffer contents\n" );
+ }
+
+ /* Get the name of the atom we added above */
+ memset( buf, '.', sizeof(buf) );
+ len = GetAtomNameA( atom, buf, 6 );
+ ok( len == 5, "bad length %d\n", len );
+ ok( !memcmp( buf, "fooba\0....", 10 ), "bad buffer contents\n" );
+
+ /* Repeat, unicode-style */
+ if (unicode_OS)
+ {
+ WCHAR resW[] = {'f','o','o','b','a','\0','.','.','.','.'};
+ for (i = 0; i < 10; i++) bufW[i] = '.';
+ SetLastError( 0xdeadbeef );
+ len = GetAtomNameW( atom, bufW, 6 );
+ ok( len && GetLastError() == 0xdeadbeef, "GlobalGetAtomNameW failed\n" );
+ ok( len == 5, "bad length %d\n", len );
+ ok( !memcmp( bufW, resW, 10*sizeof(WCHAR) ), "bad buffer contents\n" );
+ }
+
+ /* Check error code returns */
+ memset(buf, '.', 10);
+ ok( !GetAtomNameA( atom, buf, 0 ), "succeeded\n" );
+ ok( !memcmp( buf, "..........", 10 ), "should not touch buffer\n" );
+
+ if (unicode_OS)
+ {
+ static const WCHAR sampleW[10] = {'.','.','.','.','.','.','.','.','.','.'};
+
+ for (i = 0; i < 10; i++) bufW[i] = '.';
+ ok( !GetAtomNameW( atom, bufW, 0 ), "succeeded\n" );
+ ok( !memcmp( bufW, sampleW, 10 * sizeof(WCHAR) ), "should not touch buffer\n" );
+ }
+
+ /* Test integer atoms */
+ for (i = 0; i <= 0xbfff; i++)
+ {
+ memset( buf, 'a', 10 );
+ len = GetAtomNameA( (ATOM)i, buf, 10 );
+ if (i)
+ {
+ char res[20];
+ ok( (len > 1) && (len < 7), "bad length %d for %s\n", len, buf );
+ sprintf( res, "#%d", i );
+ memset( res + strlen(res) + 1, 'a', 10 );
+ ok( !memcmp( res, buf, 10 ), "bad buffer contents %s\n", buf );
+ }
+ else
+ ok( !len, "bad length %d\n", len );
+
+ len = GetAtomNameA( (ATOM)i, buf, 1);
+ ok(!len, "succeed with %u for %u\n", len, i);
+
+ /* ERROR_MORE_DATA is on nt3.51 sp5 */
+ if (i)
+ ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER ||
+ GetLastError() == ERROR_MORE_DATA ||
+ GetLastError() == 0xdeadbeef, /* the Win 9x way */
+ "wrong error conditions %lu for %u\n", GetLastError(), i);
+ else
+ ok(GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_MORE_DATA ||
+ GetLastError() == 0xdeadbeef, /* the Win 9x way */
+ "wrong error conditions %lu for %u\n", GetLastError(), i);
+ }
+ /* test string limits & overflow */
+ do_initA(in, "abcdefghij", 255);
+ atom = AddAtomA(in);
+ ok(atom, "couldn't add atom for %s\n", in);
+ len = GetAtomNameA(atom, out, sizeof(out));
+ ok(len == 255, "length mismatch (%u instead of 255)\n", len);
+ for (i = 0; i < 255; i++)
+ {
+ ok(out[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, out[i], "abcdefghij"[i % 10]);
+ }
+ ok(out[255] == '\0', "wrong end of string\n");
+ memset(out, '.', sizeof(out));
+ len = GetAtomNameA(atom, out, 10);
+ ok(len == 9, "succeeded %d\n", len);
+ for (i = 0; i < 9; i++)
+ {
+ ok(out[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, out[i], "abcdefghij"[i % 10]);
+ }
+ ok(out[9] == '\0', "wrong end of string\n");
+ ok(out[10] == '.', "buffer overwrite\n");
+ do_initA(in, "abcdefghij", 256);
+ atom = AddAtomA(in);
+ ok(!atom, "succeeded\n");
+
+ /* ERROR_MORE_DATA is on nt3.51 sp5 */
+ ok(GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_MORE_DATA ||
+ GetLastError() == 0xdeadbeef, /* the Win 9x way */
+ "wrong error code (%lu)\n", GetLastError());
+
+ if (unicode_OS)
+ {
+ /* test integral atoms */
+ for (i = 0; i <= 0xbfff; i++)
+ {
+ memset(outW, 'a', sizeof(outW));
+ len = GetAtomNameW( (ATOM)i, outW, 10 );
+ if (i)
+ {
+ WCHAR res[20];
+
+ ok( (len > 1) && (len < 7), "bad length %d\n", len );
+ print_integral( res, i );
+ memset( res + lstrlenW(res) + 1, 'a', 10 * sizeof(WCHAR));
+ ok( !memcmp( res, outW, 10 * sizeof(WCHAR) ), "bad buffer contents for %d\n", i );
+ }
+ else
+ ok( !len, "bad length %d\n", len );
+
+ len = GetAtomNameW( (ATOM)i, outW, 1);
+ ok(!len, "succeed with %u for %u\n", len, i);
+
+ /* ERROR_MORE_DATA is on nt3.51 sp5 */
+ ok(GetLastError() == ERROR_MORE_DATA ||
+ GetLastError() == (i ? ERROR_INSUFFICIENT_BUFFER : ERROR_INVALID_PARAMETER),
+ "wrong error conditions %lu for %u\n", GetLastError(), i);
+ }
+ do_initW(inW, "abcdefghij", 255);
+ atom = AddAtomW(inW);
+ ok(atom, "couldn't add atom for %s\n", in);
+ len = GetAtomNameW(atom, outW, sizeof(outW));
+ ok(len == 255, "length mismatch (%u instead of 255)\n", len);
+ for (i = 0; i < 255; i++)
+ {
+ ok(outW[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, outW[i], "abcdefghij"[i % 10]);
+ }
+ ok(outW[255] == '\0', "wrong end of string\n");
+ memset(outW, '.', sizeof(outW));
+ len = GetAtomNameW(atom, outW, 10);
+ ok(len == 9, "succeeded %d\n", len);
+ for (i = 0; i < 9; i++)
+ {
+ ok(outW[i] == "abcdefghij"[i % 10], "wrong string at %i (%c instead of %c)\n", i, outW[i], "abcdefghij"[i % 10]);
+ }
+ ok(outW[9] == '\0', "wrong end of string\n");
+ ok(outW[10] == DOUBLE('.'), "buffer overwrite\n");
+ do_initW(inW, "abcdefghij", 256);
+ atom = AddAtomW(inW);
+ ok(!atom, "succeeded\n");
+
+ /* ERROR_MORE_DATA is on nt3.51 sp5 */
+ ok(GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_MORE_DATA,
+ "wrong error code (%lu)\n", GetLastError());
+ }
+}
+
+static void test_local_error_handling(void)
+{
+ char buffer[260];
+ WCHAR bufferW[260];
+ int i;
+
+ memset( buffer, 'a', 256 );
+ buffer[256] = 0;
+ ok( !AddAtomA(buffer), "add succeeded\n" );
+ ok( !FindAtomA(buffer), "find succeeded\n" );
+
+ if (unicode_OS)
+ {
+ for (i = 0; i < 256; i++) bufferW[i] = 'b';
+ bufferW[256] = 0;
+ ok( !AddAtomW(bufferW), "add succeeded\n" );
+ ok( !FindAtomW(bufferW), "find succeeded\n" );
+ }
+}
+
+START_TEST(atom)
+{
+ test_add_atom();
+ test_get_atom_name();
+ test_error_handling();
+ test_local_add_atom();
+ test_local_get_atom_name();
+ test_local_error_handling();
+}
--- /dev/null
+/*
+ * Tests for file change notification functions
+ *
+ * Copyright (c) 2004 Hans Leidekker
+ * Copyright 2006 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* TODO: - security attribute changes
+ * - compound filter and multiple notifications
+ * - subtree notifications
+ * - non-documented flags FILE_NOTIFY_CHANGE_LAST_ACCESS and
+ * FILE_NOTIFY_CHANGE_CREATION
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "wine/test.h"
+#include <windef.h>
+#include <winbase.h>
+#include <winternl.h>
+
+static DWORD CALLBACK NotificationThread(LPVOID arg)
+{
+ HANDLE change = (HANDLE) arg;
+ BOOL ret = FALSE;
+ DWORD status;
+
+ status = WaitForSingleObject(change, 100);
+
+ if (status == WAIT_OBJECT_0 ) {
+ ret = FindNextChangeNotification(change);
+ }
+
+ ret = FindCloseChangeNotification(change);
+ ok( ret, "FindCloseChangeNotification error: %ld\n",
+ GetLastError());
+
+ ExitThread((DWORD)ret);
+}
+
+static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
+{
+ HANDLE change, thread;
+ DWORD threadId;
+
+ change = FindFirstChangeNotificationA(path, subtree, flags);
+ ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError());
+
+ thread = CreateThread(NULL, 0, NotificationThread, (LPVOID)change,
+ 0, &threadId);
+ ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %ld\n", GetLastError());
+
+ return thread;
+}
+
+static DWORD FinishNotificationThread(HANDLE thread)
+{
+ DWORD status, exitcode;
+
+ status = WaitForSingleObject(thread, 5000);
+ ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %ld error %ld\n", status, GetLastError());
+
+ ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
+
+ return exitcode;
+}
+
+static void test_FindFirstChangeNotification(void)
+{
+ HANDLE change, file, thread;
+ DWORD attributes, count;
+ BOOL ret;
+
+ char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
+ char filename1[MAX_PATH], filename2[MAX_PATH];
+ static const char prefix[] = "FCN";
+ char buffer[2048];
+
+ /* pathetic checks */
+
+ change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+ ok(change == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
+ "FindFirstChangeNotification error: %ld\n", GetLastError());
+
+ if (0) /* This documents win2k behavior. It crashes on win98. */
+ {
+ change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+ ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "FindFirstChangeNotification error: %ld\n", GetLastError());
+ }
+
+ ret = FindNextChangeNotification(NULL);
+ ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n",
+ GetLastError());
+
+ ret = FindCloseChangeNotification(NULL);
+ ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
+ GetLastError());
+
+ ret = GetTempPathA(MAX_PATH, workdir);
+ ok(ret, "GetTempPathA error: %ld\n", GetLastError());
+
+ lstrcatA(workdir, "testFileChangeNotification");
+
+ ret = CreateDirectoryA(workdir, NULL);
+ ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
+
+ ret = GetTempFileNameA(workdir, prefix, 0, filename1);
+ ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
+
+ file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+ ret = CloseHandle(file);
+ ok( ret, "CloseHandle error: %ld\n", GetLastError());
+
+ /* Try to register notification for a file. win98 and win2k behave differently here */
+ change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+ ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
+ GetLastError() == ERROR_FILE_NOT_FOUND),
+ "FindFirstChangeNotification error: %ld\n", GetLastError());
+
+ lstrcpyA(dirname1, filename1);
+ lstrcatA(dirname1, "dir");
+
+ lstrcpyA(dirname2, dirname1);
+ lstrcatA(dirname2, "new");
+
+ ret = CreateDirectoryA(dirname1, NULL);
+ ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
+
+ /* What if we move the directory we registered notification for? */
+ thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+ ret = MoveFileA(dirname1, dirname2);
+ ok(ret, "MoveFileA error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* What if we remove the directory we registered notification for? */
+ thread = StartNotificationThread(dirname2, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+ ret = RemoveDirectoryA(dirname2);
+ ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
+
+ /* win98 and win2k behave differently here */
+ ret = FinishNotificationThread(thread);
+ ok(ret || !ret, "You'll never read this\n");
+
+ /* functional checks */
+
+ /* Create a directory */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+ ret = CreateDirectoryA(dirname1, NULL);
+ ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* Rename a directory */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+ ret = MoveFileA(dirname1, dirname2);
+ ok(ret, "MoveFileA error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* Delete a directory */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+ ret = RemoveDirectoryA(dirname2);
+ ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ lstrcpyA(filename2, filename1);
+ lstrcatA(filename2, "new");
+
+ /* Rename a file */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+ ret = MoveFileA(filename1, filename2);
+ ok(ret, "MoveFileA error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* Delete a file */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+ ret = DeleteFileA(filename2);
+ ok(ret, "DeleteFileA error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* Create a file */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+ file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+ ret = CloseHandle(file);
+ ok( ret, "CloseHandle error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ attributes = GetFileAttributesA(filename2);
+ ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\n", GetLastError());
+ attributes &= FILE_ATTRIBUTE_READONLY;
+
+ /* Change file attributes */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
+ ret = SetFileAttributesA(filename2, attributes);
+ ok(ret, "SetFileAttributesA error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* Change last write time by writing to a file */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
+ file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+ ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
+ ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError());
+ ret = CloseHandle(file);
+ ok( ret, "CloseHandle error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* Change file size by truncating a file */
+ thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
+ file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+ ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
+ ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError());
+ ret = CloseHandle(file);
+ ok( ret, "CloseHandle error: %ld\n", GetLastError());
+ ok(FinishNotificationThread(thread), "Missed notification\n");
+
+ /* clean up */
+
+ ret = DeleteFileA(filename2);
+ ok(ret, "DeleteFileA error: %ld\n", GetLastError());
+
+ ret = RemoveDirectoryA(workdir);
+ ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
+}
+
+/* this test concentrates more on the wait behaviour of the handle */
+static void test_ffcn(void)
+{
+ DWORD filter;
+ HANDLE handle;
+ LONG r;
+ WCHAR path[MAX_PATH], subdir[MAX_PATH];
+ static const WCHAR szBoo[] = { '\\','b','o','o',0 };
+ static const WCHAR szHoo[] = { '\\','h','o','o',0 };
+
+ r = GetTempPathW( MAX_PATH, path );
+ ok( r != 0, "temp path failed\n");
+ if (!r)
+ return;
+
+ lstrcatW( path, szBoo );
+ lstrcpyW( subdir, path );
+ lstrcatW( subdir, szHoo );
+
+ RemoveDirectoryW( subdir );
+ RemoveDirectoryW( path );
+
+ r = CreateDirectoryW(path, NULL);
+ ok( r == TRUE, "failed to create directory\n");
+
+ filter = FILE_NOTIFY_CHANGE_FILE_NAME;
+ filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
+
+ handle = FindFirstChangeNotificationW( path, 1, filter);
+ ok( handle != INVALID_HANDLE_VALUE, "invalid handle\n");
+
+ r = WaitForSingleObject( handle, 0 );
+ ok( r == STATUS_TIMEOUT, "should time out\n");
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create subdir\n");
+
+ r = WaitForSingleObject( handle, 0 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n");
+
+ r = WaitForSingleObject( handle, 0 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n");
+
+ r = FindNextChangeNotification(handle);
+ ok( r == TRUE, "find next failed\n");
+
+ r = WaitForSingleObject( handle, 0 );
+ ok( r == STATUS_TIMEOUT, "should time out\n");
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove subdir\n");
+
+ r = WaitForSingleObject( handle, 0 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n");
+
+ r = WaitForSingleObject( handle, 0 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n");
+
+ r = FindNextChangeNotification(handle);
+ ok( r == TRUE, "find next failed\n");
+
+ r = FindNextChangeNotification(handle);
+ ok( r == TRUE, "find next failed\n");
+
+ r = FindCloseChangeNotification(handle);
+ ok( r == TRUE, "should succeed\n");
+
+ r = RemoveDirectoryW( path );
+ ok( r == TRUE, "failed to remove dir\n");
+}
+
+typedef BOOL (WINAPI *fnReadDirectoryChangesW)(HANDLE,LPVOID,DWORD,BOOL,DWORD,
+ LPDWORD,LPOVERLAPPED,LPOVERLAPPED_COMPLETION_ROUTINE);
+fnReadDirectoryChangesW pReadDirectoryChangesW;
+
+static void test_readdirectorychanges(void)
+{
+ HANDLE hdir;
+ char buffer[0x1000];
+ DWORD fflags, filter = 0, r, dwCount;
+ OVERLAPPED ov;
+ WCHAR path[MAX_PATH], subdir[MAX_PATH], subsubdir[MAX_PATH];
+ static const WCHAR szBoo[] = { '\\','b','o','o',0 };
+ static const WCHAR szHoo[] = { '\\','h','o','o',0 };
+ static const WCHAR szGa[] = { '\\','h','o','o','\\','g','a',0 };
+ PFILE_NOTIFY_INFORMATION pfni;
+
+ if (!pReadDirectoryChangesW)
+ return;
+
+ r = GetTempPathW( MAX_PATH, path );
+ ok( r != 0, "temp path failed\n");
+ if (!r)
+ return;
+
+ lstrcatW( path, szBoo );
+ lstrcpyW( subdir, path );
+ lstrcatW( subdir, szHoo );
+
+ lstrcpyW( subsubdir, path );
+ lstrcatW( subsubdir, szGa );
+
+ RemoveDirectoryW( subsubdir );
+ RemoveDirectoryW( subdir );
+ RemoveDirectoryW( path );
+
+ r = CreateDirectoryW(path, NULL);
+ ok( r == TRUE, "failed to create directory\n");
+
+ SetLastError(0xd0b00b00);
+ r = pReadDirectoryChangesW(NULL,NULL,0,FALSE,0,NULL,NULL,NULL);
+ ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
+ ok(r==FALSE, "should return false\n");
+
+ fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
+ hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, fflags, NULL);
+ ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+ ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
+
+ SetLastError(0xd0b00b00);
+ r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,NULL,NULL);
+ ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
+ ok(r==FALSE, "should return false\n");
+
+ SetLastError(0xd0b00b00);
+ r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,0,NULL,&ov,NULL);
+ ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
+ ok(r==FALSE, "should return false\n");
+
+ filter = FILE_NOTIFY_CHANGE_FILE_NAME;
+ filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
+ filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ filter |= FILE_NOTIFY_CHANGE_SIZE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ filter |= FILE_NOTIFY_CHANGE_CREATION;
+ filter |= FILE_NOTIFY_CHANGE_SECURITY;
+
+ SetLastError(0xd0b00b00);
+ ov.Internal = 0;
+ ov.InternalHigh = 0;
+ memset( buffer, 0, sizeof buffer );
+
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,-1,NULL,&ov,NULL);
+ ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
+ ok(r==FALSE, "should return false\n");
+
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
+ ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
+ ok(r==FALSE, "should return false\n");
+
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ r = WaitForSingleObject( ov.hEvent, 10 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = WaitForSingleObject( ov.hEvent, INFINITE );
+ ok( r == WAIT_OBJECT_0, "event should be ready\n" );
+
+ ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
+ ok( pfni->FileNameLength == 6, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
+
+ ResetEvent(ov.hEvent);
+ SetLastError(0xd0b00b00);
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,NULL,NULL);
+ ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
+ ok(r==FALSE, "should return false\n");
+
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,0,NULL,&ov,NULL);
+ ok(GetLastError()==ERROR_INVALID_PARAMETER,"last error wrong\n");
+ ok(r==FALSE, "should return false\n");
+
+ filter = FILE_NOTIFY_CHANGE_SIZE;
+
+ SetEvent(ov.hEvent);
+ ov.Internal = 1;
+ ov.InternalHigh = 1;
+ S(U(ov)).Offset = 0;
+ S(U(ov)).OffsetHigh = 0;
+ memset( buffer, 0, sizeof buffer );
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ ok( ov.Internal == STATUS_PENDING, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 1, "ov.InternalHigh wrong\n");
+
+ r = WaitForSingleObject( ov.hEvent, 0 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ r = WaitForSingleObject( ov.hEvent, INFINITE );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
+
+ r = GetOverlappedResult( hdir, &ov, &dwCount, TRUE );
+ ok( r == TRUE, "getoverlappedresult failed\n");
+ ok( dwCount == 0x12, "count wrong\n");
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
+ ok( pfni->FileNameLength == 6, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
+
+ /* what happens if the buffer is too small? */
+ r = pReadDirectoryChangesW(hdir,buffer,0x10,FALSE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = WaitForSingleObject( ov.hEvent, INFINITE );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
+
+ /* test the recursive watch */
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ r = CreateDirectoryW( subsubdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = WaitForSingleObject( ov.hEvent, INFINITE );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
+ ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
+
+ r = RemoveDirectoryW( subsubdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ ov.Internal = 1;
+ ov.InternalHigh = 1;
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ r = WaitForSingleObject( ov.hEvent, INFINITE );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
+ ok( pfni->FileNameLength == 0x0c, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szGa[1],6), "name wrong\n" );
+
+ ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0x18, "ov.InternalHigh wrong\n");
+
+ CloseHandle(hdir);
+
+ r = RemoveDirectoryW( path );
+ ok( r == TRUE, "failed to remove directory\n");
+}
+
+/* show the behaviour when a null buffer is passed */
+static void test_readdirectorychanges_null(void)
+{
+ NTSTATUS r;
+ HANDLE hdir;
+ char buffer[0x1000];
+ DWORD fflags, filter = 0;
+ OVERLAPPED ov;
+ WCHAR path[MAX_PATH], subdir[MAX_PATH];
+ static const WCHAR szBoo[] = { '\\','b','o','o',0 };
+ static const WCHAR szHoo[] = { '\\','h','o','o',0 };
+ PFILE_NOTIFY_INFORMATION pfni;
+
+ if (!pReadDirectoryChangesW)
+ return;
+
+ r = GetTempPathW( MAX_PATH, path );
+ ok( r != 0, "temp path failed\n");
+ if (!r)
+ return;
+
+ lstrcatW( path, szBoo );
+ lstrcpyW( subdir, path );
+ lstrcatW( subdir, szHoo );
+
+ RemoveDirectoryW( subdir );
+ RemoveDirectoryW( path );
+
+ r = CreateDirectoryW(path, NULL);
+ ok( r == TRUE, "failed to create directory\n");
+
+ fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
+ hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, fflags, NULL);
+ ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+ ov.hEvent = CreateEvent( NULL, 1, 0, NULL );
+
+ filter = FILE_NOTIFY_CHANGE_FILE_NAME;
+ filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
+
+ SetLastError(0xd0b00b00);
+ ov.Internal = 0;
+ ov.InternalHigh = 0;
+ memset( buffer, 0, sizeof buffer );
+
+ r = pReadDirectoryChangesW(hdir,NULL,0,FALSE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ r = WaitForSingleObject( ov.hEvent, 0 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = WaitForSingleObject( ov.hEvent, 0 );
+ ok( r == WAIT_OBJECT_0, "event should be ready\n" );
+
+ ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
+
+ ov.Internal = 0;
+ ov.InternalHigh = 0;
+ S(U(ov)).Offset = 0;
+ S(U(ov)).OffsetHigh = 0;
+ memset( buffer, 0, sizeof buffer );
+
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,FALSE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ r = WaitForSingleObject( ov.hEvent, 0 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ r = WaitForSingleObject( ov.hEvent, INFINITE );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok( ov.Internal == STATUS_NOTIFY_ENUM_DIR, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0, "ov.InternalHigh wrong\n");
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+
+ CloseHandle(hdir);
+
+ r = RemoveDirectoryW( path );
+ ok( r == TRUE, "failed to remove directory\n");
+}
+
+static void test_readdirectorychanges_filedir(void)
+{
+ NTSTATUS r;
+ HANDLE hdir, hfile;
+ char buffer[0x1000];
+ DWORD fflags, filter = 0;
+ OVERLAPPED ov;
+ WCHAR path[MAX_PATH], subdir[MAX_PATH], file[MAX_PATH];
+ static const WCHAR szBoo[] = { '\\','b','o','o',0 };
+ static const WCHAR szHoo[] = { '\\','h','o','o',0 };
+ static const WCHAR szFoo[] = { '\\','f','o','o',0 };
+ PFILE_NOTIFY_INFORMATION pfni;
+
+ r = GetTempPathW( MAX_PATH, path );
+ ok( r != 0, "temp path failed\n");
+ if (!r)
+ return;
+
+ lstrcatW( path, szBoo );
+ lstrcpyW( subdir, path );
+ lstrcatW( subdir, szHoo );
+
+ lstrcpyW( file, path );
+ lstrcatW( file, szFoo );
+
+ DeleteFileW( file );
+ RemoveDirectoryW( subdir );
+ RemoveDirectoryW( path );
+
+ r = CreateDirectoryW(path, NULL);
+ ok( r == TRUE, "failed to create directory\n");
+
+ fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
+ hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE|FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, fflags, NULL);
+ ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+ ov.hEvent = CreateEvent( NULL, 0, 0, NULL );
+
+ filter = FILE_NOTIFY_CHANGE_FILE_NAME;
+
+ r = pReadDirectoryChangesW(hdir,buffer,sizeof buffer,TRUE,filter,NULL,&ov,NULL);
+ ok(r==TRUE, "should return true\n");
+
+ r = WaitForSingleObject( ov.hEvent, 10 );
+ ok( r == WAIT_TIMEOUT, "should timeout\n" );
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ hfile = CreateFileW( file, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
+ ok( hfile != INVALID_HANDLE_VALUE, "failed to create file\n");
+ ok( CloseHandle(hfile), "failed toc lose file\n");
+
+ r = WaitForSingleObject( ov.hEvent, INFINITE );
+ ok( r == WAIT_OBJECT_0, "event should be ready\n" );
+
+ ok( ov.Internal == STATUS_SUCCESS, "ov.Internal wrong\n");
+ ok( ov.InternalHigh == 0x12, "ov.InternalHigh wrong\n");
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
+ ok( pfni->FileNameLength == 6, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szFoo[1],6), "name wrong\n" );
+
+ r = DeleteFileW( file );
+ ok( r == TRUE, "failed to delete file\n");
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ CloseHandle(hdir);
+
+ r = RemoveDirectoryW( path );
+ ok( r == TRUE, "failed to remove directory\n");
+}
+
+START_TEST(change)
+{
+ HMODULE hkernel32 = GetModuleHandle("kernel32");
+ pReadDirectoryChangesW = (fnReadDirectoryChangesW)
+ GetProcAddress(hkernel32, "ReadDirectoryChangesW");
+
+ test_FindFirstChangeNotification();
+ test_ffcn();
+ test_readdirectorychanges();
+ test_readdirectorychanges_null();
+ test_readdirectorychanges_filedir();
+}
--- /dev/null
+/*
+ * Unit tests for code page to/from unicode translations
+ *
+ * Copyright (c) 2002 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+
+static void test_null_source(void)
+{
+ int len;
+ DWORD GLE;
+
+ SetLastError(0);
+ len = WideCharToMultiByte(CP_ACP, 0, NULL, 0, NULL, 0, NULL, NULL);
+ GLE = GetLastError();
+ ok(!len && GLE == ERROR_INVALID_PARAMETER,
+ "WideCharToMultiByte returned %d with GLE=%ld (expected 0 with ERROR_INVALID_PARAMETER)\n",
+ len, GLE);
+}
+
+/* lstrcmpW is not supported on Win9x! */
+static int mylstrcmpW(const WCHAR* str1, const WCHAR* str2)
+{
+ while (*str1 && *str1==*str2) {
+ str1++;
+ str2++;
+ }
+ return *str1-*str2;
+}
+
+static void test_negative_source_length(void)
+{
+ int len;
+ char buf[10];
+ WCHAR bufW[10];
+ static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
+
+ /* Test, whether any negative source length works as strlen() + 1 */
+ SetLastError( 0xdeadbeef );
+ memset(buf,'x',sizeof(buf));
+ len = WideCharToMultiByte(CP_ACP, 0, foobarW, -2002, buf, 10, NULL, NULL);
+ ok(len == 7 && !lstrcmpA(buf, "foobar") && GetLastError() == 0xdeadbeef,
+ "WideCharToMultiByte(-2002): len=%d error=%ld\n",len,GetLastError());
+
+ SetLastError( 0xdeadbeef );
+ memset(bufW,'x',sizeof(bufW));
+ len = MultiByteToWideChar(CP_ACP, 0, "foobar", -2002, bufW, 10);
+ ok(len == 7 && !mylstrcmpW(bufW, foobarW) && GetLastError() == 0xdeadbeef,
+ "MultiByteToWideChar(-2002): len=%d error=%ld\n",len,GetLastError());
+}
+
+static void test_overlapped_buffers(void)
+{
+ static const WCHAR strW[] = {'j','u','s','t',' ','a',' ','t','e','s','t',0};
+ static const char strA[] = "just a test";
+ char buf[256];
+ int ret;
+
+ lstrcpyW((WCHAR *)(buf + 1), strW);
+ ret = WideCharToMultiByte(CP_ACP, 0, (WCHAR *)(buf + 1), -1, buf, sizeof(buf), NULL, NULL);
+ ok(ret == sizeof(strA), "unexpected ret %d != %d\n", ret, sizeof(strA));
+ ok(!memcmp(buf, strA, sizeof(strA)), "conversion failed: %s\n", buf);
+}
+
+START_TEST(codepage)
+{
+ test_null_source();
+ test_negative_source_length();
+ test_overlapped_buffers();
+}
--- /dev/null
+/* Unit test suite for comm functions
+ *
+ * Copyright 2003 Kevin Groeneveld
+ * Copyright 2005 Uwe Bonnes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "winnls.h"
+
+#define TIMEOUT 1000 /* one second for Timeouts*/
+#define SLOWBAUD 150
+#define FASTBAUD 115200
+#define TIMEDELTA 100 /* 100 ms uncertainty allowed */
+
+/* Define the appropriate LOOPBACK(s) TRUE if you have a Loopback cable with
+ * the mentioned shorts connected to your Serial port
+ */
+#define LOOPBACK_TXD_RXD FALSE /* Sub-D 9: Short 2-3 */
+#define LOOPBACK_CTS_RTS FALSE /* Sub-D 9: Short 7-8 */
+#define LOOPBACK_DTR_DSR FALSE /* Sub-D 9: Short 4-6 */
+#define LOOPBACK_DTR_RING FALSE /* Sub-D 9: Short 4-9 */
+#define LOOPBACK_DTR_DCD FALSE /* Sub-D 9: Short 4-1 */
+/* Many Linux serial drivers have the TIOCM_LOOP flag in the TIOCM_SET ioctl
+ * available. For the 8250 this is equivalent to TXD->RXD, OUT2->DCD,
+ * OUT1->RI, RTS->CTS and DTR->DSR
+ */
+
+typedef struct
+{
+ char string[100];
+ BOOL result;
+ BOOL old_style;
+ DCB dcb1, dcb2;
+ COMMTIMEOUTS timeouts1, timeouts2;
+} TEST;
+
+static TEST test[] =
+{
+ {
+ "baud=9600 parity=e data=5 stop=1 xon=on odsr=off octs=off dtr=on rts=on idsr=on",
+ TRUE, FALSE,
+ { 0x00000000, 0x00002580, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x05, 0x02, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00002580, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x05, 0x02, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "baud=0 parity=M data=6 stop=1.5 xon=off odsr=on octs=ON dtr=off rts=off idsr=OFF",
+ TRUE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x06, 0x03, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00000000, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x06, 0x03, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "BAUD=4000000000 parity=n data=7 stop=2 to=off",
+ TRUE, FALSE,
+ { 0x00000000, 0xee6b2800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x00, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xee6b2800, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x00, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }
+ },
+ {
+ "Baud=115200 Parity=O Data=8 To=On",
+ TRUE, FALSE,
+ { 0x00000000, 0x0001c200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x01, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x0001c200, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x01, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 }
+ },
+ {
+ "PaRiTy=s Data=7 DTR=on",
+ TRUE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x04, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x04, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "data=4",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "data=9",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "parity=no",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "stop=0",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "stop=1.501",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "stop=3",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "to=foobar",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ " baud=9600",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "baud= 9600",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "baud=9600,data=8",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "11,n,8,1",
+ TRUE, TRUE,
+ { 0x00000000, 0x0000006e, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x0000006e, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "30 ,E, 5,1.5",
+ TRUE, TRUE,
+ { 0x00000000, 0x0000012c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x05, 0x02, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x0000012c, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x05, 0x02, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "60, m, 6, 2 ",
+ TRUE, TRUE,
+ { 0x00000000, 0x00000258, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x06, 0x03, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00000258, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x06, 0x03, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "12 , o , 7 , 1",
+ TRUE, TRUE,
+ { 0x00000000, 0x000004b0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x01, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x000004b0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x01, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "24,s,8,1.5",
+ TRUE, TRUE,
+ { 0x00000000, 0x00000960, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x04, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00000960, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x04, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "48,n,8,1,p",
+ TRUE, TRUE,
+ { 0x00000000, 0x000012c0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x000012c0, 1, 1, 1, 1, 2, 1, 1, 0, 0, 1, 1, 2, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,N,8,1 , x ",
+ TRUE, TRUE,
+ { 0x00000000, 0x00002580, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00002580, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "19, e, 7, 1, x",
+ TRUE, TRUE,
+ { 0x00000000, 0x00004b00, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x02, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00004b00, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x02, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "0,M,7,1,P",
+ TRUE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x03, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00000000, 1, 1, 1, 1, 2, 1, 1, 0, 0, 1, 1, 2, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x03, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "4000000000,O,7,1.5,X",
+ TRUE, TRUE,
+ { 0x00000000, 0xee6b2800, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x01, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xee6b2800, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x01, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,N,8,1 to=on",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,NO,8,1",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,N,4,1",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,N,9,1",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,N,8,0",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,N,8,3",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "96,N,8,1,K",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COM0:baud=115200",
+ FALSE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COMx:baud=38400 data=8",
+ TRUE, FALSE,
+ { 0x00000000, 0x00009600, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00009600, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0xff, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COMx :to=on stop=1.5",
+ TRUE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 }
+ },
+ {
+ "COMx: baud=12345 data=7",
+ TRUE, FALSE,
+ { 0x00000000, 0x00003039, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00003039, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0xff, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COMx : xon=on odsr=off",
+ TRUE, FALSE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 0, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COM0:9600,N,8,1",
+ FALSE, TRUE,
+ { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COMx:9600,N,8,1",
+ TRUE, TRUE,
+ { 0x00000000, 0x00002580, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00002580, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COMx: 11,E,7,2",
+ TRUE, TRUE,
+ { 0x00000000, 0x0000006e, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x02, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x0000006e, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x02, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COMx :19,M,5,1",
+ TRUE, TRUE,
+ { 0x00000000, 0x00004b00, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x05, 0x03, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x00004b00, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x05, 0x03, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+ {
+ "COMx : 57600,S,6,2,x",
+ TRUE, TRUE,
+ { 0x00000000, 0x0000e100, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x06, 0x04, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+ { 0xffffffff, 0x0000e100, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x06, 0x04, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+ { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+ },
+};
+
+#define TEST_COUNT (sizeof(test) / sizeof(TEST))
+
+/* This function can be useful if you are modifiying the test cases and want to
+ output the contents of a DCB structure. */
+/*static print_dcb(DCB *pdcb)
+{
+ printf("0x%08x, 0x%08x, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, 0x%05x, 0x%04x, 0x%04x, 0x%04x, 0x%02x, 0x%02x, 0x%02x, (char)0x%02x, (char)0x%02x, (char)0x%02x, (char)0x%02x, (char)0x%02x, 0x%04x\n",
+ pdcb->DCBlength,
+ pdcb->BaudRate,
+ pdcb->fBinary,
+ pdcb->fParity,
+ pdcb->fOutxCtsFlow,
+ pdcb->fOutxDsrFlow,
+ pdcb->fDtrControl,
+ pdcb->fDsrSensitivity,
+ pdcb->fTXContinueOnXoff,
+ pdcb->fOutX,
+ pdcb->fInX,
+ pdcb->fErrorChar,
+ pdcb->fNull,
+ pdcb->fRtsControl,
+ pdcb->fAbortOnError,
+ pdcb->fDummy2,
+ pdcb->wReserved,
+ pdcb->XonLim,
+ pdcb->XoffLim,
+ pdcb->ByteSize,
+ pdcb->Parity,
+ pdcb->StopBits,
+ pdcb->XonChar & 0xff,
+ pdcb->XoffChar & 0xff,
+ pdcb->ErrorChar & 0xff,
+ pdcb->EofChar & 0xff,
+ pdcb->EvtChar & 0xff,
+ pdcb->wReserved1 & 0xffff );
+} */
+
+static void check_result(const char *function, TEST *ptest, int initial_value, BOOL result)
+{
+ DWORD LastError = GetLastError();
+ DWORD CorrectError = (ptest->result ? 0xdeadbeef : ERROR_INVALID_PARAMETER);
+
+ ok(LastError == CorrectError, "%s(\"%s\"), 0x%02x: GetLastError() returned 0x%08lx, should be 0x%08lx\n", function, ptest->string, initial_value, LastError, CorrectError);
+ ok(result == ptest->result, "%s(\"%s\"), 0x%02x: return value should be %s\n", function, ptest->string, initial_value, ptest->result ? "TRUE" : "FALSE");
+}
+
+#define check_dcb_member(a,b) ok(pdcb1->a == pdcb2->a, "%s(\"%s\"), 0x%02x: "#a" is "b", should be "b"\n", function, ptest->string, initial_value, pdcb1->a, pdcb2->a)
+#define check_dcb_member2(a,c,b) if(pdcb2->a == c) { check_dcb_member(a,b); } else { ok(pdcb1->a == pdcb2->a || pdcb1->a == c, "%s(\"%s\"), 0x%02x: "#a" is "b", should be "b" or "b"\n", function, ptest->string, initial_value, pdcb1->a, pdcb2->a, c); }
+
+static void check_dcb(const char *function, TEST *ptest, int initial_value, DCB *pdcb1, DCB *pdcb2)
+{
+ /* DCBlength is a special case since Win 9x sets it but NT does not.
+ We will accept either as correct. */
+ check_dcb_member2(DCBlength, (DWORD)sizeof(DCB), "%lu");
+
+ /* For old style control strings Win 9x does not set the next five members, NT does. */
+ if(ptest->old_style && ptest->result)
+ {
+ check_dcb_member2(fOutxCtsFlow, ((unsigned int)initial_value & 1), "%u");
+ check_dcb_member2(fDtrControl, ((unsigned int)initial_value & 3), "%u");
+ check_dcb_member2(fOutX, ((unsigned int)initial_value & 1), "%u");
+ check_dcb_member2(fInX, ((unsigned)initial_value & 1), "%u");
+ check_dcb_member2(fRtsControl, ((unsigned)initial_value & 3), "%u");
+ }
+ else
+ {
+ check_dcb_member(fOutxCtsFlow, "%u");
+ check_dcb_member(fDtrControl, "%u");
+ check_dcb_member(fOutX, "%u");
+ check_dcb_member(fInX, "%u");
+ check_dcb_member(fRtsControl, "%u");
+ }
+
+ if(ptest->result)
+ {
+ /* For the idsr=xxx parameter, NT sets fDsrSensitivity, 9x sets
+ fOutxDsrFlow. */
+ if(!ptest->old_style)
+ {
+ check_dcb_member2(fOutxDsrFlow, pdcb2->fDsrSensitivity, "%u");
+ check_dcb_member2(fDsrSensitivity, pdcb2->fOutxDsrFlow, "%u");
+ }
+ else
+ {
+ /* For old style control strings Win 9x does not set the
+ fOutxDsrFlow member, NT does. */
+ check_dcb_member2(fOutxDsrFlow, ((unsigned int)initial_value & 1), "%u");
+ check_dcb_member(fDsrSensitivity, "%u");
+ }
+ }
+ else
+ {
+ check_dcb_member(fOutxDsrFlow, "%u");
+ check_dcb_member(fDsrSensitivity, "%u");
+ }
+
+ /* Check the result of the DCB members. */
+ check_dcb_member(BaudRate, "%lu");
+ check_dcb_member(fBinary, "%u");
+ check_dcb_member(fParity, "%u");
+ check_dcb_member(fTXContinueOnXoff, "%u");
+ check_dcb_member(fErrorChar, "%u");
+ check_dcb_member(fNull, "%u");
+ check_dcb_member(fAbortOnError, "%u");
+ check_dcb_member(fDummy2, "%u");
+ check_dcb_member(wReserved, "%u");
+ check_dcb_member(XonLim, "%u");
+ check_dcb_member(XoffLim, "%u");
+ check_dcb_member(ByteSize, "%u");
+ check_dcb_member(Parity, "%u");
+ check_dcb_member(StopBits, "%u");
+ check_dcb_member(XonChar, "%d");
+ check_dcb_member(XoffChar, "%d");
+ check_dcb_member(ErrorChar, "%d");
+ check_dcb_member(EofChar, "%d");
+ check_dcb_member(EvtChar, "%d");
+ check_dcb_member(wReserved1, "%u");
+}
+
+#define check_timeouts_member(a) ok(ptimeouts1->a == ptimeouts2->a, "%s(\"%s\"), 0x%02x: "#a" is %lu, should be %lu\n", function, ptest->string, initial_value, ptimeouts1->a, ptimeouts2->a);
+
+static void check_timeouts(const char *function, TEST *ptest, int initial_value, COMMTIMEOUTS *ptimeouts1, COMMTIMEOUTS *ptimeouts2)
+{
+ check_timeouts_member(ReadIntervalTimeout);
+ check_timeouts_member(ReadTotalTimeoutMultiplier);
+ check_timeouts_member(ReadTotalTimeoutConstant);
+ check_timeouts_member(WriteTotalTimeoutMultiplier);
+ check_timeouts_member(WriteTotalTimeoutConstant);
+}
+
+static void test_BuildCommDCBA(TEST *ptest, int initial_value, DCB *pexpected_dcb)
+{
+ BOOL result;
+ DCB dcb;
+
+ /* set initial conditions */
+ memset(&dcb, initial_value, sizeof(DCB));
+ SetLastError(0xdeadbeef);
+
+ result = BuildCommDCBA(ptest->string, &dcb);
+
+ /* check results */
+ check_result("BuildCommDCBA", ptest, initial_value, result);
+ check_dcb("BuildCommDCBA", ptest, initial_value, &dcb, pexpected_dcb);
+}
+
+static void test_BuildCommDCBAndTimeoutsA(TEST *ptest, int initial_value, DCB *pexpected_dcb, COMMTIMEOUTS *pexpected_timeouts)
+{
+ BOOL result;
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+
+ /* set initial conditions */
+ memset(&dcb, initial_value, sizeof(DCB));
+ memset(&timeouts, initial_value, sizeof(COMMTIMEOUTS));
+ SetLastError(0xdeadbeef);
+
+ result = BuildCommDCBAndTimeoutsA(ptest->string, &dcb, &timeouts);
+
+ /* check results */
+ check_result("BuildCommDCBAndTimeoutsA", ptest, initial_value, result);
+ check_dcb("BuildCommDCBAndTimeoutsA", ptest, initial_value, &dcb, pexpected_dcb);
+ check_timeouts("BuildCommDCBAndTimeoutsA", ptest, initial_value, &timeouts, pexpected_timeouts);
+}
+
+static void test_BuildCommDCBW(TEST *ptest, int initial_value, DCB *pexpected_dcb)
+{
+ BOOL result;
+ DCB dcb;
+ WCHAR wide_string[sizeof(ptest->string)];
+
+ MultiByteToWideChar(CP_ACP, 0, ptest->string, -1, wide_string, sizeof(wide_string) / sizeof(WCHAR));
+
+ /* set initial conditions */
+ memset(&dcb, initial_value, sizeof(DCB));
+ SetLastError(0xdeadbeef);
+
+ result = BuildCommDCBW(wide_string, &dcb);
+
+ if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+
+ /* check results */
+ check_result("BuildCommDCBW", ptest, initial_value, result);
+ check_dcb("BuildCommDCBW", ptest, initial_value, &dcb, pexpected_dcb);
+}
+
+static void test_BuildCommDCBAndTimeoutsW(TEST *ptest, int initial_value, DCB *pexpected_dcb, COMMTIMEOUTS *pexpected_timeouts)
+{
+ BOOL result;
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+ WCHAR wide_string[sizeof(ptest->string)];
+
+ MultiByteToWideChar(CP_ACP, 0, ptest->string, -1, wide_string, sizeof(wide_string) / sizeof(WCHAR));
+
+ /* set initial conditions */
+ memset(&dcb, initial_value, sizeof(DCB));
+ memset(&timeouts, initial_value, sizeof(COMMTIMEOUTS));
+ SetLastError(0xdeadbeef);
+
+ result = BuildCommDCBAndTimeoutsW(wide_string, &dcb, &timeouts);
+
+ if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+
+ /* check results */
+ check_result("BuildCommDCBAndTimeoutsA", ptest, initial_value, result);
+ check_dcb("BuildCommDCBAndTimeoutsA", ptest, initial_value, &dcb, pexpected_dcb);
+ check_timeouts("BuildCommDCBAndTimeoutsA", ptest, initial_value, &timeouts, pexpected_timeouts);
+}
+
+static void test_BuildCommDCB(void)
+{
+ char port_name[] = "COMx";
+ char port = 0;
+ unsigned int i;
+ char *ptr;
+
+ /* Some of these tests require a valid COM port. This loop will try to find
+ a valid port. */
+ for(port_name[3] = '1'; port_name[3] <= '9'; port_name[3]++)
+ {
+ COMMCONFIG commconfig;
+ DWORD size = sizeof(COMMCONFIG);
+
+ if(GetDefaultCommConfig(port_name, &commconfig, &size))
+ {
+ port = port_name[3];
+ break;
+ }
+ }
+
+ if(!port)
+ trace("Could not find a valid COM port. Some tests will be skipped.\n");
+
+ for(i = 0; i < TEST_COUNT; i++)
+ {
+ /* Check if this test case needs a valid COM port. */
+ ptr = strstr(test[i].string, "COMx");
+
+ /* If required, substitute valid port number into device control string. */
+ if(ptr)
+ {
+ if(port)
+ ptr[3] = port;
+ else
+ continue;
+ }
+
+ test_BuildCommDCBA(&test[i], 0x00, &test[i].dcb1);
+ test_BuildCommDCBA(&test[i], 0xff, &test[i].dcb2);
+ test_BuildCommDCBAndTimeoutsA(&test[i], 0x00, &test[i].dcb1, &test[i].timeouts1);
+ test_BuildCommDCBAndTimeoutsA(&test[i], 0xff, &test[i].dcb2, &test[i].timeouts2);
+
+ test_BuildCommDCBW(&test[i], 0x00, &test[i].dcb1);
+ test_BuildCommDCBW(&test[i], 0xff, &test[i].dcb2);
+ test_BuildCommDCBAndTimeoutsW(&test[i], 0x00, &test[i].dcb1, &test[i].timeouts1);
+ test_BuildCommDCBAndTimeoutsW(&test[i], 0xff, &test[i].dcb2, &test[i].timeouts2);
+ }
+}
+
+static HANDLE test_OpenComm(BOOL doOverlap)
+{
+ HANDLE hcom = INVALID_HANDLE_VALUE;
+ char port_name[] = "COMx";
+ static BOOL shown = FALSE;
+ DWORD errors;
+ COMSTAT comstat;
+
+ /* Try to find a port */
+ for(port_name[3] = '1'; port_name[3] <= '9'; port_name[3]++)
+ {
+ hcom = CreateFile( port_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ (doOverlap)?FILE_FLAG_OVERLAPPED:0, NULL );
+ if (hcom != INVALID_HANDLE_VALUE)
+ break;
+ }
+ if(!shown)
+ {
+ if (hcom == INVALID_HANDLE_VALUE)
+ trace("Could not find a valid COM port. Skipping test_ReadTimeOut\n");
+ else
+ trace("Found Com port %s. Connected devices may disturbe results\n", port_name);
+ /*shown = TRUE; */
+ }
+ if (hcom != INVALID_HANDLE_VALUE)
+ {
+ ok(ClearCommError(hcom,&errors,&comstat), "Unexpected errors on open\n");
+ ok(comstat.cbInQue == 0, "Unexpected %ld chars in InQueue\n",comstat.cbInQue);
+ ok(comstat.cbOutQue == 0, "Still pending %ld charcters in OutQueue\n", comstat.cbOutQue);
+ ok(errors == 0, "Unexpected errors 0x%08lx\n", errors);
+ }
+ return hcom;
+}
+
+static void test_GetModemStatus(HANDLE hcom)
+{
+ DWORD ModemStat;
+
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ trace("GetCommModemStatus returned 0x%08lx->%s%s%s%s\n", ModemStat,
+ (ModemStat &MS_RLSD_ON)?"MS_RLSD_ON ":"",
+ (ModemStat &MS_RING_ON)?"MS_RING_ON ":"",
+ (ModemStat &MS_DSR_ON)?"MS_DSR_ON ":"",
+ (ModemStat &MS_CTS_ON)?"MS_CTS_ON ":"");
+}
+
+/* When we don't write anything, Read should time out even on a loopbacked port */
+static void test_ReadTimeOut(HANDLE hcom)
+{
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+ char rbuf[32];
+ DWORD before, after, read, timediff, LastError;
+ BOOL res;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = FASTBAUD;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.fRtsControl=RTS_CONTROL_ENABLE;
+ dcb.fDtrControl=DTR_CONTROL_ENABLE;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ZeroMemory( &timeouts, sizeof(timeouts));
+ timeouts.ReadTotalTimeoutConstant = TIMEOUT;
+ ok(SetCommTimeouts(hcom, &timeouts),"SetCommTimeouts failed\n");
+
+ before = GetTickCount();
+ SetLastError(0xdeadbeef);
+ res = ReadFile(hcom, rbuf, sizeof(rbuf), &read, NULL);
+ LastError = GetLastError();
+ after = GetTickCount();
+ ok( res == TRUE, "A timed-out read should return TRUE\n");
+ ok( LastError == 0xdeadbeef, "err=%ld\n", LastError);
+ timediff = after - before;
+ ok( timediff > TIMEOUT>>2 && timediff < TIMEOUT *2,
+ "Unexpected TimeOut %ld, expected %d\n", timediff, TIMEOUT);
+}
+
+static void test_waittxempty(HANDLE hcom)
+{
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+ char tbuf[]="test_waittxempty";
+ DWORD before, after, written, timediff, evtmask = 0;
+ BOOL res_write, res;
+ DWORD baud = SLOWBAUD;
+
+ trace("test_waittxempty\n");
+ /* set a low baud rate to have ample time*/
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = baud;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.fRtsControl=RTS_CONTROL_ENABLE;
+ dcb.fDtrControl=DTR_CONTROL_ENABLE;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ZeroMemory( &timeouts, sizeof(timeouts));
+ timeouts.ReadTotalTimeoutConstant = TIMEOUT;
+ ok(SetCommTimeouts(hcom, &timeouts),"SetCommTimeouts failed\n");
+
+ ok(SetupComm(hcom,1024,1024),"SetUpComm failed\n");
+ ok(SetCommMask(hcom, EV_TXEMPTY), "SetCommMask failed\n");
+
+ before = GetTickCount();
+ res_write=WriteFile(hcom, tbuf, sizeof(tbuf), &written, NULL);
+ after = GetTickCount();
+ ok(res_write == TRUE, "WriteFile failed\n");
+ ok(written == sizeof(tbuf),
+ "WriteFile: Unexpected write_size %ld , expected %d\n", written, sizeof(tbuf));
+
+ trace("WriteFile succeeded, took %ld ms to write %d Bytes at %ld Baud\n",
+ after - before, sizeof(tbuf), baud);
+
+ before = GetTickCount();
+ res = WaitCommEvent(hcom, &evtmask, NULL);
+ after = GetTickCount();
+
+ ok(res == TRUE, "WaitCommEvent failed\n");
+ ok((evtmask & EV_TXEMPTY),
+ "WaitCommEvent: Unexpected EvtMask 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_TXEMPTY);
+
+ timediff = after - before;
+
+ trace("WaitCommEvent for EV_TXEMPTY took %ld ms\n", timediff);
+ /* 050604: This shows a difference between XP (tested with mingw compiled crosstest):
+ XP returns Writefile only after everything went out of the Serial port,
+ while wine returns immedate.
+ Thus on XP, WaintCommEvent after setting the CommMask for EV_TXEMPTY
+ nearly return immediate,
+ while on wine the most time is spent here
+ */
+}
+
+/* A new open handle should not return error or have bytes in the Queues */
+static void test_ClearCommErrors(HANDLE hcom)
+{
+ DWORD errors;
+ COMSTAT lpStat;
+
+ ok(ClearCommError(hcom, &errors, &lpStat), "ClearCommError failed\n");
+ ok(lpStat.cbInQue == 0, "Unexpected %ld chars in InQueue\n", lpStat.cbInQue);
+ ok(lpStat.cbOutQue == 0, "Unexpected %ld chars in OutQueue\n", lpStat.cbOutQue);
+ ok(errors == 0, "ClearCommErrors: Unexpected error 0x%08lx\n", errors);
+ trace("test_ClearCommErrors done\n");
+}
+
+static void test_non_pending_errors(HANDLE hcom)
+{
+ DCB dcb;
+ DWORD err;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.ByteSize = 255; /* likely bogus */
+ ok(!SetCommState(hcom, &dcb), "SetCommState should have failed\n");
+ ok(ClearCommError(hcom, &err, NULL), "ClearCommError should succeed\n");
+ ok(!(err & CE_MODE), "ClearCommError shouldn't set CE_MODE byte in this case (%lx)\n", err);
+}
+
+/**/
+static void test_LoopbackRead(HANDLE hcom)
+{
+ DCB dcb;
+ COMMTIMEOUTS timeouts;
+ char rbuf[32];
+ DWORD before, after, diff, read, read1, written, evtmask=0, i;
+ BOOL res;
+ char tbuf[]="test_LoopbackRead";
+
+ trace("Starting test_LoopbackRead\n");
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = FASTBAUD;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.fRtsControl=RTS_CONTROL_ENABLE;
+ dcb.fDtrControl=DTR_CONTROL_ENABLE;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ZeroMemory( &timeouts, sizeof(timeouts));
+ timeouts.ReadTotalTimeoutConstant = TIMEOUT;
+ ok(SetCommTimeouts(hcom, &timeouts),"SetCommTimeouts failed\n");
+
+ ok(SetCommMask(hcom, EV_TXEMPTY), "SetCommMask failed\n");
+
+ before = GetTickCount();
+ ok(WriteFile(hcom,tbuf,sizeof(tbuf),&written, NULL), "WriteFile failed\n");
+ after = GetTickCount();
+ ok(written == sizeof(tbuf),"WriteFile %ld bytes written, expected %d\n",
+ written, sizeof(tbuf));
+ diff = after -before;
+
+ /* make sure all bytes are written, so Readfile will succeed in one call*/
+ ok(WaitCommEvent(hcom, &evtmask, NULL), "WaitCommEvent failed\n");
+ before = GetTickCount();
+ ok(evtmask == EV_TXEMPTY,
+ "WaitCommEvent: Unexpected EvtMask 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_TXEMPTY);
+ trace("Write %ld ms WaitCommEvent EV_TXEMPTY %ld ms\n", diff, before- after);
+
+ read=0;
+ ok(ReadFile(hcom, rbuf, sizeof(rbuf), &read, NULL), "Readfile failed\n");
+ ok(read == sizeof(tbuf),"ReadFile read %ld bytes, expected %d \"%s\"\n", read, sizeof(tbuf),rbuf);
+
+ /* Now do the same withe a slower Baud rate.
+ As we request more characters then written, we will hit the timeout
+ */
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.BaudRate = 9600;
+ dcb.ByteSize = 8;
+ dcb.Parity = NOPARITY;
+ dcb.fRtsControl=RTS_CONTROL_ENABLE;
+ dcb.fDtrControl=DTR_CONTROL_ENABLE;
+ dcb.StopBits = ONESTOPBIT;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+
+ ok(SetCommMask(hcom, EV_RXCHAR), "SetCommMask failed\n");
+ ok(WriteFile(hcom,tbuf,sizeof(tbuf),&written, NULL), "WriteFile failed\n");
+ ok(written == sizeof(tbuf),"WriteFile %ld bytes written, expected %d\n",
+ written, sizeof(tbuf));
+
+ trace("WaitCommEventEV_RXCHAR\n");
+ ok(WaitCommEvent(hcom, &evtmask, NULL), "WaitCommEvent failed\n");
+ ok(evtmask == EV_RXCHAR, "WaitCommEvent: Unexpected EvtMask 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_RXCHAR);
+
+ before = GetTickCount();
+ res = ReadFile(hcom, rbuf, sizeof(rbuf), &read, NULL);
+ after = GetTickCount();
+ ok(res, "Readfile failed\n");
+ ok(read == sizeof(tbuf),"ReadFile read %ld bytes, expected %d\n", read, sizeof(tbuf));
+ diff = after - before;
+ trace("Readfile for %d chars with %d avail took %ld ms\n",
+ sizeof(rbuf), sizeof(tbuf), diff);
+ ok( (diff > TIMEOUT - TIMEDELTA) && (diff < TIMEOUT + TIMEDELTA),
+ "Timedout Wait took %ld ms, expected around %d\n", diff, TIMEOUT);
+
+ /* now do a plain read with slow speed
+ * This will result in several low level reads and a timeout to happen
+ */
+ dcb.BaudRate = SLOWBAUD;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+ ok(WriteFile(hcom,tbuf,sizeof(tbuf),&written, NULL), "WriteFile failed\n");
+ before = GetTickCount();
+ read = 0;
+ read1 =0;
+ i=0;
+ do
+ {
+ res = ReadFile(hcom, rbuf+read, sizeof(rbuf-read), &read1, NULL);
+ ok(res, "Readfile failed\n");
+ read += read1;
+ i++;
+ }
+ while ((read < sizeof(tbuf)) && (i <10));
+ after = GetTickCount();
+ ok( read == sizeof(tbuf),"ReadFile read %ld bytes, expected %d\n", read, sizeof(tbuf));
+ trace("Plain Read for %d char at %d baud took %ld ms\n", sizeof(tbuf), SLOWBAUD, after-before);
+
+}
+
+static void test_LoopbackCtsRts(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fRtsControl == RTS_CONTROL_HANDSHAKE)
+ {
+ trace("RTS_CONTROL_HANDSHAKE is set, so don't manipulate RTS\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_CTS_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRRTS), "EscapeCommFunction failed to clear RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_CTS_ON) == 0, "CTS didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_CTS_ON));
+ ok(EscapeCommFunction(hcom, SETRTS), "EscapeCommFunction failed to clear RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore CTS: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETRTS), "EscapeCommFunction failed to set RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_CTS_ON) == MS_CTS_ON,
+ "CTS didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_CTS_ON));
+ ok(EscapeCommFunction(hcom, CLRRTS), "EscapeCommFunction failed to clear RTS\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore CTS: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+static void test_LoopbackDtrDcd(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_HANDSHAKE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_RLSD_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_RLSD_ON) == 0, "RLSD didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_RLSD_ON));
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RLSD: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_RLSD_ON) == MS_RLSD_ON,
+ "RLSD didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_RLSD_ON));
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RLSD: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+static void test_LoopbackDtrDsr(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_DSR_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_DSR_ON) == 0, "CTS didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_DSR_ON));
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore DSR: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_DSR_ON) == MS_DSR_ON,
+ "CTS didn't react: 0x%04lx,expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_DSR_ON));
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore DSR: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+static void test_LoopbackDtrRing(HANDLE hcom)
+{
+ DWORD ModemStat, defaultStat;
+ DCB dcb;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_HANDSHAKE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ /* XP returns some values in the low nibble, so mask them out*/
+ defaultStat &= MS_CTS_ON|MS_DSR_ON|MS_RING_ON|MS_RLSD_ON;
+ if(defaultStat & MS_RING_ON)
+ {
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_RING_ON) == 0, "RING didn't react: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, (defaultStat & ~MS_RING_ON));
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RING: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+ else
+ {
+ ok(EscapeCommFunction(hcom, SETDTR), "EscapeCommFunction failed to set DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok ((ModemStat & MS_RING_ON) == MS_RING_ON,
+ "RING didn't react: 0x%04lx,expected 0x%04lx\n",
+ ModemStat, (defaultStat | MS_RING_ON));
+ ok(EscapeCommFunction(hcom, CLRDTR), "EscapeCommFunction failed to clear DTR\n");
+ ok(GetCommModemStatus(hcom, &ModemStat), "GetCommModemStatus failed\n");
+ ok (ModemStat == defaultStat, "Failed to restore RING: 0x%04lx, expected 0x%04lx\n",
+ ModemStat, defaultStat);
+ }
+}
+
+/*
+ * Set up a WaitCommEvent for anything in the receive buffer,
+ * then write to TX to put a character
+ * into the RX buffer
+ * Need Loopback TX->RX
+*/
+
+static void test_WaitRx(HANDLE hcom)
+{
+ OVERLAPPED overlapped, overlapped_w;
+ HANDLE hComPortEvent, hComWriteEvent;
+ DWORD before, after, after1, diff, success_wait = FALSE, success_write;
+ DWORD err_wait, err_write, written, evtmask=0;
+
+ ok(SetCommMask(hcom, EV_RXCHAR), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+
+ ok((hComWriteEvent = CreateEvent( NULL, TRUE, FALSE, NULL )) !=0,
+ "CreateEvent res 0x%08lx\n",
+ GetLastError());
+ ZeroMemory( &overlapped_w, sizeof(overlapped_w));
+ overlapped_w.hEvent = hComWriteEvent;
+
+ before = GetTickCount();
+ {success_wait = WaitCommEvent(hcom, &evtmask, &overlapped);}
+ err_wait = GetLastError();
+ after = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success_wait, err_wait, evtmask);
+ ok(success_wait || err_wait == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+
+ success_write= WriteFile(hcom, "X", 1, &written, &overlapped_w);
+ err_write = GetLastError();
+ ok(success_write || err_write == ERROR_IO_PENDING,
+ "overlapped WriteFile failed, err 0x%08lx\n",
+ err_write);
+
+ if (!success_write && (err_write == ERROR_IO_PENDING)) {
+ success_write = WaitForSingleObjectEx(hComWriteEvent, TIMEOUT, TRUE);
+ err_write = GetLastError();
+ ok(success_write == WAIT_OBJECT_0, "WaitForSingleObjectEx, res 0x%08lx, err 0x%08lx\n",
+ success_write, err_write);
+ }
+ Sleep(TIMEOUT >>1);
+ success_write = GetOverlappedResult(hcom, &overlapped_w, &written, FALSE);
+ err_write = GetLastError();
+
+ trace("Write after Wait res 0x%08lx err 0x%08lx\n",success_write, err_write);
+ ok(success_write && written ==1, "Write after Wait res 0x%08lx err 0x%08lx\n",
+ success_write, err_write);
+
+ if (!success_wait && (err_wait == ERROR_IO_PENDING)) {
+ success_wait = WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE);
+ err_wait = GetLastError();
+ ok(success_wait == WAIT_OBJECT_0, "wait hComPortEvent, res 0x%08lx, err 0x%08lx\n",
+ success_wait, err_wait);
+ }
+ success_wait = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err_wait = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success_wait, err_wait, evtmask, after-before, after1-before);
+
+ ok(evtmask & EV_RXCHAR, "Detect EV_RXCHAR: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_RXCHAR);
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+}
+
+/* Change the controling line after the given timeout to the given state
+ By the loopback, this should trigger the WaitCommEvent
+*/
+static DWORD CALLBACK toggle_ctlLine(LPVOID arg)
+{
+ DWORD *args = (DWORD *) arg;
+ DWORD timeout = args[0];
+ DWORD ctl = args[1];
+ HANDLE hcom = (HANDLE) args[2];
+ HANDLE hComPortEvent = (HANDLE) args[3];
+ DWORD success, err;
+
+ trace("toggle_ctlLine timeout %ld clt 0x%08lx handle 0x%08lx\n",
+ args[0], args[1], args[2]);
+ Sleep(timeout);
+ ok(EscapeCommFunction(hcom, ctl),"EscapeCommFunction 0x%08lx failed\n", ctl);
+ trace("toggle_ctline done\n");
+ success = WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE);
+ err = GetLastError();
+ trace("toggle_ctline WaitForSingleObjectEx res 0x%08lx err 0x%08lx\n",
+ success, err);
+ return 0;
+}
+
+/*
+ * Wait for a change in CTS
+ * Needs Loopback from DTR to CTS
+ */
+static void test_WaitCts(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[4], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ dcb.fRtsControl=RTS_CONTROL_ENABLE;
+ dcb.fDtrControl=DTR_CONTROL_ENABLE;
+ ok(SetCommState(hcom, &dcb), "SetCommState failed\n");
+ if (dcb.fDtrControl == RTS_CONTROL_DISABLE)
+ {
+ trace("RTS_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_CTS_ON)
+ args[1] = CLRRTS;
+ else
+ args[1] = SETRTS;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitCts timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_CTS), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ args[3] = (DWORD) hComPortEvent;
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ /* Wait a minimum to let the thread start up */
+ Sleep(10);
+ trace("Thread created\n");
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "WaitCts hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ ok(evtmask & EV_CTS, "Failed to detect EV_CTS: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_CTS);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_CTS_ON)
+ ok((evtmask & MS_CTS_ON) == 0,"CTS didn't change state!\n");
+ else
+ ok((evtmask & MS_CTS_ON), "CTS didn't change state!\n");
+
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_CTS_ON)
+ args[1] = SETRTS;
+ else
+ args[1] = CLRRTS;
+}
+
+/* Change the Comm Mask while a Wait is going on
+ WaitCommevent should return with a EVTMASK set to zero
+*/
+static DWORD CALLBACK reset_CommMask(LPVOID arg)
+{
+ DWORD *args = (DWORD *) arg;
+ DWORD timeout = args[0];
+ HANDLE hcom = (HANDLE) args[1];
+
+ trace(" Changing CommMask on the fly for handle %p after timeout %ld\n",
+ hcom, timeout);
+ Sleep(timeout);
+ ok(SetCommMask(hcom, 0),"SetCommMask %p failed\n", hcom);
+ trace("SetCommMask changed\n");
+ return 0;
+}
+
+/* Set up a Wait for a change on CTS. We don't toggle any line, but we
+ reset the CommMask and expect the wait to return with a mask of 0
+ No special port connections needed
+*/
+static void test_AbortWaitCts(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[2];
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == RTS_CONTROL_DISABLE)
+ {
+ trace("RTS_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ args[1]=(DWORD) hcom;
+
+ trace("test_AbortWaitCts timeout %ld handle 0x%08lx\n",args[0], args[1]);
+
+ ok(SetCommMask(hcom, EV_CTS), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ alarmThread = CreateThread(NULL, 0, reset_CommMask, (void *) &args, 0, &alarmThreadId);
+ /* Wait a minimum to let the thread start up */
+ Sleep(10);
+ trace("Thread created\n");
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "AbortWaitCts hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ ok(evtmask == 0, "Incorect EventMask 0x%08lx returned on Wait aborted bu SetCommMask, expected 0x%08x\n",
+ evtmask, 0);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+}
+
+/*
+ * Wait for a change in DSR
+ * Needs Loopback from DTR to DSR
+ */
+static void test_WaitDsr(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[3], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_DSR_ON)
+ args[1] = CLRDTR;
+ else
+ args[1] = SETDTR;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitDsr timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_DSR), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ ok(evtmask & EV_DSR, "Failed to detect EV_DSR: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_DSR);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_DSR_ON)
+ ok((evtmask & MS_DSR_ON) == 0,"DTR didn't change state!\n");
+ else
+ ok((evtmask & MS_DSR_ON), "DTR didn't change state!\n");
+
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_DSR_ON)
+ args[1] = SETDTR;
+ else
+ args[1] = CLRDTR;
+}
+
+/*
+ * Wait for a Ring
+ * Needs Loopback from DTR to RING
+ */
+static void test_WaitRing(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[3], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RING_ON)
+ args[1] = CLRDTR;
+ else
+ args[1] = SETDTR;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitRing timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_RING), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ ok(evtmask & EV_RING, "Failed to detect EV_RING: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_CTS);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RING_ON)
+ ok((evtmask & MS_RING_ON) == 0,"DTR didn't change state!\n");
+ else
+ ok((evtmask & MS_RING_ON), "DTR didn't change state!\n");
+
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_RING_ON)
+ args[1] = SETDTR;
+ else
+ args[1] = CLRDTR;
+}
+/*
+ * Wait for a change in DCD
+ * Needs Loopback from DTR to DCD
+ */
+static void test_WaitDcd(HANDLE hcom)
+{
+ DCB dcb;
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[3], defaultStat;
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(GetCommState(hcom, &dcb), "GetCommState failed\n");
+ if (dcb.fDtrControl == DTR_CONTROL_DISABLE)
+ {
+ trace("DTR_CONTROL_HANDSHAKE is set, so don't manipulate DTR\n");
+ return;
+ }
+ args[0]= TIMEOUT >>1;
+ ok(GetCommModemStatus(hcom, &defaultStat), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RLSD_ON)
+ args[1] = CLRDTR;
+ else
+ args[1] = SETDTR;
+ args[2]=(DWORD) hcom;
+
+ trace("test_WaitDcd timeout %ld clt 0x%08lx handle 0x%08lx\n",args[0], args[1], args[2]);
+
+ ok(SetCommMask(hcom, EV_RLSD), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+ alarmThread = CreateThread(NULL, 0, toggle_ctlLine, (void *) &args, 0, &alarmThreadId);
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+ if (!success && (err == ERROR_IO_PENDING))
+ ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent failed\n");
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ ok(evtmask & EV_RLSD, "Failed to detect EV_RLSD: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_CTS);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+ if(defaultStat & MS_RLSD_ON)
+ ok((evtmask & MS_RLSD_ON) == 0,"DTR didn't change state!\n");
+ else
+ ok((evtmask & MS_RLSD_ON), "DTR didn't change state!\n");
+
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ /*restore RTS Settings*/
+ if(defaultStat & MS_RLSD_ON)
+ args[1] = SETDTR;
+ else
+ args[1] = CLRDTR;
+}
+
+/*
+ Set Break after timeout
+*/
+static DWORD CALLBACK set_CommBreak(LPVOID arg)
+{
+ DWORD *args = (DWORD *) arg;
+ DWORD timeout = args[0];
+ HANDLE hcom = (HANDLE) args[1];
+
+ trace("SetCommBreak for handle %p after timeout %ld\n",
+ hcom, timeout);
+ Sleep(timeout);
+ ok(SetCommBreak(hcom),"SetCommBreak %p failed\n", hcom);
+ trace("SetCommBreak done\n");
+ return 0;
+}
+
+/*
+ Wait for the Break condition (TX resp. RX active)
+ Needs Loopback TX-RX
+*/
+static void test_WaitBreak(HANDLE hcom)
+{
+ OVERLAPPED overlapped;
+ HANDLE hComPortEvent;
+ HANDLE alarmThread;
+ DWORD args[2];
+ DWORD alarmThreadId, before, after, after1, diff, success, err, written, evtmask=0;
+
+ ok(SetCommMask(hcom, EV_BREAK), "SetCommMask failed\n");
+ hComPortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ ok(hComPortEvent != 0, "CreateEvent failed\n");
+
+ trace("test_WaitBreak\n");
+ args[0]= TIMEOUT >>1;
+ args[1]=(DWORD) hcom;
+ alarmThread = CreateThread(NULL, 0, set_CommBreak, (void *) &args, 0, &alarmThreadId);
+ /* Wait a minimum to let the thread start up */
+ Sleep(10);
+ trace("Thread created\n");
+ ok(alarmThread !=0 , "CreateThread Failed\n");
+
+ ZeroMemory( &overlapped, sizeof(overlapped));
+ overlapped.hEvent = hComPortEvent;
+ before = GetTickCount();
+ success = WaitCommEvent(hcom, &evtmask, &overlapped);
+ err = GetLastError();
+ after = GetTickCount();
+
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx\n", success, err, evtmask);
+ ok(success || err == ERROR_IO_PENDING, "overlapped WaitCommEvent failed\n");
+ trace("overlapped WriteCommEvent returned.\n");
+
+ if (!success && (err == ERROR_IO_PENDING))
+ ok(WaitForSingleObjectEx(hComPortEvent, TIMEOUT, TRUE) == 0,
+ "wait hComPortEvent res 0x%08lx\n", GetLastError());
+ success = GetOverlappedResult(hcom, &overlapped, &written, FALSE);
+ err = GetLastError();
+ after1 = GetTickCount();
+ trace("Success 0x%08lx err 0x%08lx evtmask 0x%08lx diff1 %ld, diff2 %ld\n",
+ success, err, evtmask, after-before, after1-before);
+
+ ok(evtmask & EV_BREAK, "Failed to detect EV_BREAK: 0x%08lx, expected 0x%08x\n",
+ evtmask, EV_BREAK);
+ ok(GetCommModemStatus(hcom, &evtmask), "GetCommModemStatus failed\n");
+
+ diff = after1 - before;
+ ok ((diff > (TIMEOUT>>1) -TIMEDELTA) && (diff < (TIMEOUT>>1) + TIMEDELTA),
+ "Unexpected time %ld, expected around %d\n", diff, TIMEOUT>>1);
+
+ ok(ClearCommBreak(hcom), "ClearCommBreak failed\n");
+}
+
+START_TEST(comm)
+{
+ HANDLE hcom;
+ /* use variables and not #define to compile the code */
+ BOOL loopback_txd_rxd = LOOPBACK_TXD_RXD;
+ BOOL loopback_rts_cts = LOOPBACK_CTS_RTS;
+ BOOL loopback_dtr_dsr = LOOPBACK_DTR_DSR;
+ BOOL loopback_dtr_ring = LOOPBACK_DTR_RING;
+ BOOL loopback_dtr_dcd = LOOPBACK_DTR_DCD;
+
+ test_BuildCommDCB();
+ hcom = test_OpenComm(FALSE);
+ if (hcom != INVALID_HANDLE_VALUE)
+ {
+ test_GetModemStatus(hcom);
+ test_ReadTimeOut(hcom);
+ test_waittxempty(hcom);
+ CloseHandle(hcom);
+ }
+ hcom = test_OpenComm(FALSE);
+ if (hcom != INVALID_HANDLE_VALUE)
+ {
+ Sleep(200); /* Give the laster character of test_waittxempty to drop into the receiver */
+ test_ClearCommErrors(hcom);
+ CloseHandle(hcom);
+ }
+ hcom = test_OpenComm(FALSE);
+ if (hcom != INVALID_HANDLE_VALUE)
+ {
+ test_non_pending_errors(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_txd_rxd) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackRead(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_rts_cts) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackCtsRts(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dsr) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackDtrDsr(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_ring) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackDtrRing(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dcd) && ((hcom = test_OpenComm(FALSE))!=INVALID_HANDLE_VALUE))
+ {
+ test_LoopbackDtrDcd(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_txd_rxd) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitRx(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_rts_cts) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitCts(hcom);
+ CloseHandle(hcom);
+ }
+ if((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE)
+ {
+ test_AbortWaitCts(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dsr) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitDsr(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_ring) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitRing(hcom);
+ CloseHandle(hcom);
+ }
+ if((loopback_dtr_dcd) && ((hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE))
+ {
+ test_WaitDcd(hcom);
+ CloseHandle(hcom);
+ }
+ if(loopback_txd_rxd && (hcom = test_OpenComm(TRUE))!=INVALID_HANDLE_VALUE)
+ {
+ test_WaitBreak(hcom);
+ CloseHandle(hcom);
+ }
+}
--- /dev/null
+/*
+ * Unit tests for console API
+ *
+ * Copyright (c) 2003,2004 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include <windows.h>
+#include <stdio.h>
+
+/* DEFAULT_ATTRIB is used for all initial filling of the console.
+ * all modifications are made with TEST_ATTRIB so that we could check
+ * what has to be modified or not
+ */
+#define TEST_ATTRIB (BACKGROUND_BLUE | FOREGROUND_GREEN)
+#define DEFAULT_ATTRIB (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED)
+/* when filling the screen with non-blank chars, this macro defines
+ * what character should be at position 'c'
+ */
+#define CONTENT(c) ('A' + (((c).Y * 17 + (c).X) % 23))
+
+#define okCURSOR(hCon, c) do { \
+ CONSOLE_SCREEN_BUFFER_INFO __sbi; \
+ BOOL expect = GetConsoleScreenBufferInfo((hCon), &__sbi) && \
+ __sbi.dwCursorPosition.X == (c).X && __sbi.dwCursorPosition.Y == (c).Y; \
+ ok(expect, "Expected cursor at (%d,%d), got (%d,%d)\n", \
+ (c).X, (c).Y, __sbi.dwCursorPosition.X, __sbi.dwCursorPosition.Y); \
+} while (0)
+
+#define okCHAR(hCon, c, ch, attr) do { \
+ char __ch; WORD __attr; DWORD __len; BOOL expect; \
+ expect = ReadConsoleOutputCharacter((hCon), &__ch, 1, (c), &__len) == 1 && __len == 1 && __ch == (ch); \
+ ok(expect, "At (%d,%d): expecting char '%c'/%02x got '%c'/%02x\n", (c).X, (c).Y, (ch), (ch), __ch, __ch); \
+ expect = ReadConsoleOutputAttribute((hCon), &__attr, 1, (c), &__len) == 1 && __len == 1 && __attr == (attr); \
+ ok(expect, "At (%d,%d): expecting attr %04x got %04x\n", (c).X, (c).Y, (attr), __attr); \
+} while (0)
+
+/* FIXME: this could be optimized on a speed point of view */
+static void resetContent(HANDLE hCon, COORD sbSize, BOOL content)
+{
+ COORD c;
+ WORD attr = DEFAULT_ATTRIB;
+ char ch;
+ DWORD len;
+
+ for (c.X = 0; c.X < sbSize.X; c.X++)
+ {
+ for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+ {
+ ch = (content) ? CONTENT(c) : ' ';
+ WriteConsoleOutputAttribute(hCon, &attr, 1, c, &len);
+ WriteConsoleOutputCharacterA(hCon, &ch, 1, c, &len);
+ }
+ }
+}
+
+static void testCursor(HANDLE hCon, COORD sbSize)
+{
+ COORD c;
+
+ c.X = c.Y = 0;
+ ok(SetConsoleCursorPosition(0, c) == 0, "No handle\n");
+ ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError: expecting %lu got %lu\n",
+ (DWORD) ERROR_INVALID_HANDLE, (DWORD) GetLastError());
+
+ c.X = c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
+ okCURSOR(hCon, c);
+
+ c.X = sbSize.X - 1;
+ c.Y = sbSize.Y - 1;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in lower-right\n");
+ okCURSOR(hCon, c);
+
+ c.X = sbSize.X;
+ c.Y = sbSize.Y - 1;
+ ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %lu got %lu\n",
+ (DWORD) ERROR_INVALID_PARAMETER, (DWORD) GetLastError());
+
+ c.X = sbSize.X - 1;
+ c.Y = sbSize.Y;
+ ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %lu got %lu\n",
+ (DWORD) ERROR_INVALID_PARAMETER, (DWORD) GetLastError());
+
+ c.X = -1;
+ c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %lu got %lu\n",
+ (DWORD) ERROR_INVALID_PARAMETER, (DWORD) GetLastError());
+
+ c.X = 0;
+ c.Y = -1;
+ ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %lu got %lu\n",
+ (DWORD) ERROR_INVALID_PARAMETER, (DWORD) GetLastError());
+}
+
+static void testWriteSimple(HANDLE hCon, COORD sbSize)
+{
+ COORD c;
+ DWORD len;
+ const char* mytest = "abcdefg";
+ const int mylen = strlen(mytest);
+
+ /* single line write */
+ c.X = c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+ c.Y = 0;
+ for (c.X = 0; c.X < mylen; c.X++)
+ {
+ okCHAR(hCon, c, mytest[c.X], TEST_ATTRIB);
+ }
+
+ okCURSOR(hCon, c);
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+}
+
+static void testWriteNotWrappedNotProcessed(HANDLE hCon, COORD sbSize)
+{
+ COORD c;
+ DWORD len, mode;
+ const char* mytest = "123";
+ const int mylen = strlen(mytest);
+ int ret;
+ int p;
+
+ ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode & ~(ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT)),
+ "clearing wrap at EOL & processed output\n");
+
+ /* write line, wrapping disabled, buffer exceeds sb width */
+ c.X = sbSize.X - 3; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+ ret = WriteConsole(hCon, mytest, mylen, &len, NULL);
+ ok(ret != 0 && len == mylen, "Couldn't write, ret = %d, len = %ld\n", ret, len);
+ c.Y = 0;
+ for (p = mylen - 3; p < mylen; p++)
+ {
+ c.X = sbSize.X - 3 + p % 3;
+ okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+ }
+
+ c.X = 0; c.Y = 1;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+ p = sbSize.X - 3 + mylen % 3;
+ c.X = p; c.Y = 0;
+
+ /* write line, wrapping disabled, strings end on end of line */
+ c.X = sbSize.X - mylen; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+}
+
+static void testWriteNotWrappedProcessed(HANDLE hCon, COORD sbSize)
+{
+ COORD c;
+ DWORD len, mode;
+ const char* mytest = "abcd\nf\tg";
+ const int mylen = strlen(mytest);
+ const int mylen2 = strchr(mytest, '\n') - mytest;
+ int p;
+
+ ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, (mode | ENABLE_PROCESSED_OUTPUT) & ~ENABLE_WRAP_AT_EOL_OUTPUT),
+ "clearing wrap at EOL & setting processed output\n");
+
+ /* write line, wrapping disabled, buffer exceeds sb width */
+ c.X = sbSize.X - 5; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-5\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+ c.Y = 0;
+ for (c.X = sbSize.X - 5; c.X < sbSize.X - 1; c.X++)
+ {
+ okCHAR(hCon, c, mytest[c.X - sbSize.X + 5], TEST_ATTRIB);
+ }
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+ c.X = 0; c.Y++;
+ okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
+ for (c.X = 1; c.X < 8; c.X++)
+ okCHAR(hCon, c, ' ', TEST_ATTRIB);
+ okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+ c.X++;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+ okCURSOR(hCon, c);
+
+ /* write line, wrapping disabled, strings end on end of line */
+ c.X = sbSize.X - 4; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+ c.Y = 0;
+ for (c.X = sbSize.X - 4; c.X < sbSize.X; c.X++)
+ {
+ okCHAR(hCon, c, mytest[c.X - sbSize.X + 4], TEST_ATTRIB);
+ }
+ c.X = 0; c.Y++;
+ okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
+ for (c.X = 1; c.X < 8; c.X++)
+ okCHAR(hCon, c, ' ', TEST_ATTRIB);
+ okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+ c.X++;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+ okCURSOR(hCon, c);
+
+ /* write line, wrapping disabled, strings end after end of line */
+ c.X = sbSize.X - 3; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+ c.Y = 0;
+ for (p = mylen2 - 3; p < mylen2; p++)
+ {
+ c.X = sbSize.X - 3 + p % 3;
+ okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+ }
+ c.X = 0; c.Y = 1;
+ okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
+ for (c.X = 1; c.X < 8; c.X++)
+ okCHAR(hCon, c, ' ', TEST_ATTRIB);
+ okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+ c.X++;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+ okCURSOR(hCon, c);
+}
+
+static void testWriteWrappedNotProcessed(HANDLE hCon, COORD sbSize)
+{
+ COORD c;
+ DWORD len, mode;
+ const char* mytest = "abcd\nf\tg";
+ const int mylen = strlen(mytest);
+ int p;
+
+ ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon,(mode | ENABLE_WRAP_AT_EOL_OUTPUT) & ~(ENABLE_PROCESSED_OUTPUT)),
+ "setting wrap at EOL & clearing processed output\n");
+
+ /* write line, wrapping enabled, buffer doesn't exceed sb width */
+ c.X = sbSize.X - 9; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+ c.Y = 0;
+ for (p = 0; p < mylen; p++)
+ {
+ c.X = sbSize.X - 9 + p;
+ okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+ }
+ c.X = sbSize.X - 9 + mylen;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+ c.X = 0; c.Y = 1;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+ /* write line, wrapping enabled, buffer does exceed sb width */
+ c.X = sbSize.X - 3; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+ c.Y = 1;
+ c.X = mylen - 3;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+}
+
+static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
+{
+ COORD c;
+ DWORD len, mode;
+ const char* mytest = "abcd\nf\tg";
+ const int mylen = strlen(mytest);
+ int p;
+
+ ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode | (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)),
+ "setting wrap at EOL & processed output\n");
+
+ /* write line, wrapping enabled, buffer doesn't exceed sb width */
+ c.X = sbSize.X - 9; c.Y = 0;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+ for (p = 0; p < 4; p++)
+ {
+ c.X = sbSize.X - 9 + p;
+ okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+ }
+ c.X = sbSize.X - 9 + p;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+ c.X = 0; c.Y++;
+ okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
+ for (c.X = 1; c.X < 8; c.X++)
+ okCHAR(hCon, c, ' ', TEST_ATTRIB);
+ okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+ c.X++;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+ okCURSOR(hCon, c);
+
+ /* write line, wrapping enabled, buffer does exceed sb width */
+ c.X = sbSize.X - 3; c.Y = 2;
+ ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+ ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+ for (p = 0; p < 3; p++)
+ {
+ c.X = sbSize.X - 3 + p;
+ okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+ }
+ c.X = 0; c.Y++;
+ okCHAR(hCon, c, mytest[3], TEST_ATTRIB);
+ c.X++;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+ c.X = 0; c.Y++;
+ okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
+ for (c.X = 1; c.X < 8; c.X++)
+ okCHAR(hCon, c, ' ', TEST_ATTRIB);
+ okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+ c.X++;
+ okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+ okCURSOR(hCon, c);
+}
+
+static void testWrite(HANDLE hCon, COORD sbSize)
+{
+ /* FIXME: should in fact insure that the sb is at least 10 character wide */
+ ok(SetConsoleTextAttribute(hCon, TEST_ATTRIB), "Setting default text color\n");
+ resetContent(hCon, sbSize, FALSE);
+ testWriteSimple(hCon, sbSize);
+ resetContent(hCon, sbSize, FALSE);
+ testWriteNotWrappedNotProcessed(hCon, sbSize);
+ resetContent(hCon, sbSize, FALSE);
+ testWriteNotWrappedProcessed(hCon, sbSize);
+ resetContent(hCon, sbSize, FALSE);
+ testWriteWrappedNotProcessed(hCon, sbSize);
+ resetContent(hCon, sbSize, FALSE);
+ testWriteWrappedProcessed(hCon, sbSize);
+}
+
+static void testScroll(HANDLE hCon, COORD sbSize)
+{
+ SMALL_RECT scroll, clip;
+ COORD dst, c, tc;
+ CHAR_INFO ci;
+
+#define W 11
+#define H 7
+
+#define IN_SRECT(r,c) ((r).Left <= (c).X && (c).X <= (r).Right && (r).Top <= (c).Y && (c).Y <= (r).Bottom)
+#define IN_SRECT2(r,d,c) ((d).X <= (c).X && (c).X <= (d).X + (r).Right - (r).Left && (d).Y <= (c).Y && (c).Y <= (d).Y + (r).Bottom - (r).Top)
+
+ /* no clipping, src & dst rect don't overlap */
+ resetContent(hCon, sbSize, TRUE);
+
+ scroll.Left = 0;
+ scroll.Right = W - 1;
+ scroll.Top = 0;
+ scroll.Bottom = H - 1;
+ dst.X = W + 3;
+ dst.Y = H + 3;
+ ci.Char.UnicodeChar = '#';
+ ci.Attributes = TEST_ATTRIB;
+
+ clip.Left = 0;
+ clip.Right = sbSize.X - 1;
+ clip.Top = 0;
+ clip.Bottom = sbSize.Y - 1;
+
+ ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
+
+ for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+ {
+ for (c.X = 0; c.X < sbSize.X; c.X++)
+ {
+ if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
+ {
+ tc.X = c.X - dst.X;
+ tc.Y = c.Y - dst.Y;
+ okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+ }
+ else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
+ okCHAR(hCon, c, '#', TEST_ATTRIB);
+ else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+ }
+ }
+
+ /* no clipping, src & dst rect do overlap */
+ resetContent(hCon, sbSize, TRUE);
+
+ scroll.Left = 0;
+ scroll.Right = W - 1;
+ scroll.Top = 0;
+ scroll.Bottom = H - 1;
+ dst.X = W /2;
+ dst.Y = H / 2;
+ ci.Char.UnicodeChar = '#';
+ ci.Attributes = TEST_ATTRIB;
+
+ clip.Left = 0;
+ clip.Right = sbSize.X - 1;
+ clip.Top = 0;
+ clip.Bottom = sbSize.Y - 1;
+
+ ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
+
+ for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+ {
+ for (c.X = 0; c.X < sbSize.X; c.X++)
+ {
+ if (dst.X <= c.X && c.X < dst.X + W && dst.Y <= c.Y && c.Y < dst.Y + H)
+ {
+ tc.X = c.X - dst.X;
+ tc.Y = c.Y - dst.Y;
+ okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+ }
+ else if (c.X < W && c.Y < H) okCHAR(hCon, c, '#', TEST_ATTRIB);
+ else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+ }
+ }
+
+ /* clipping, src & dst rect don't overlap */
+ resetContent(hCon, sbSize, TRUE);
+
+ scroll.Left = 0;
+ scroll.Right = W - 1;
+ scroll.Top = 0;
+ scroll.Bottom = H - 1;
+ dst.X = W + 3;
+ dst.Y = H + 3;
+ ci.Char.UnicodeChar = '#';
+ ci.Attributes = TEST_ATTRIB;
+
+ clip.Left = W / 2;
+ clip.Right = min(W + W / 2, sbSize.X - 1);
+ clip.Top = H / 2;
+ clip.Bottom = min(H + H / 2, sbSize.Y - 1);
+
+ ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
+
+ for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+ {
+ for (c.X = 0; c.X < sbSize.X; c.X++)
+ {
+ if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
+ {
+ tc.X = c.X - dst.X;
+ tc.Y = c.Y - dst.Y;
+ okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+ }
+ else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
+ okCHAR(hCon, c, '#', TEST_ATTRIB);
+ else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+ }
+ }
+
+ /* clipping, src & dst rect do overlap */
+ resetContent(hCon, sbSize, TRUE);
+
+ scroll.Left = 0;
+ scroll.Right = W - 1;
+ scroll.Top = 0;
+ scroll.Bottom = H - 1;
+ dst.X = W / 2 - 3;
+ dst.Y = H / 2 - 3;
+ ci.Char.UnicodeChar = '#';
+ ci.Attributes = TEST_ATTRIB;
+
+ clip.Left = W / 2;
+ clip.Right = min(W + W / 2, sbSize.X - 1);
+ clip.Top = H / 2;
+ clip.Bottom = min(H + H / 2, sbSize.Y - 1);
+
+ ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
+
+ for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+ {
+ for (c.X = 0; c.X < sbSize.X; c.X++)
+ {
+ if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
+ {
+ tc.X = c.X - dst.X;
+ tc.Y = c.Y - dst.Y;
+ okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+ }
+ else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
+ okCHAR(hCon, c, '#', TEST_ATTRIB);
+ else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+ }
+ }
+}
+
+static int mch_count;
+/* we need the event as Wine console event generation isn't synchronous
+ * (ie GenerateConsoleCtrlEvent returns before all ctrl-handlers in all
+ * processes have been called).
+ */
+static HANDLE mch_event;
+static BOOL WINAPI mch(DWORD event)
+{
+ mch_count++;
+ SetEvent(mch_event);
+ return TRUE;
+}
+
+static void testCtrlHandler(void)
+{
+ ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %lu\n", GetLastError());
+ ok(SetConsoleCtrlHandler(mch, TRUE), "Couldn't set handler\n");
+ /* wine requires the event for the test, as we cannot insure, so far, that event
+ * are processed synchronously in GenerateConsoleCtrlEvent()
+ */
+ mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
+ mch_count = 0;
+ ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
+#if 0 /* FIXME: it isn't synchronous on wine but it can still happen before we test */
+ todo_wine ok(mch_count == 1, "Event isn't synchronous\n");
+#endif
+ ok(WaitForSingleObject(mch_event, 3000) == WAIT_OBJECT_0, "event sending didn't work\n");
+ CloseHandle(mch_event);
+
+ /* Turning off ctrl-c handling doesn't work on win9x such way ... */
+ ok(SetConsoleCtrlHandler(NULL, TRUE), "Couldn't turn off ctrl-c handling\n");
+ mch_event = CreateEventA(NULL, TRUE, FALSE, NULL);
+ mch_count = 0;
+ if(!(GetVersion() & 0x80000000))
+ /* ... and next line leads to an unhandled exception on 9x. Avoid it on 9x. */
+ ok(GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0), "Couldn't send ctrl-c event\n");
+ ok(WaitForSingleObject(mch_event, 3000) == WAIT_TIMEOUT && mch_count == 0, "Event shouldn't have been sent\n");
+ CloseHandle(mch_event);
+ ok(SetConsoleCtrlHandler(mch, FALSE), "Couldn't remove handler\n");
+ ok(!SetConsoleCtrlHandler(mch, FALSE), "Shouldn't succeed\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "Bad error %lu\n", GetLastError());
+}
+
+START_TEST(console)
+{
+ HANDLE hConIn, hConOut;
+ BOOL ret;
+ CONSOLE_SCREEN_BUFFER_INFO sbi;
+
+ /* be sure we have a clean console (and that's our own)
+ * FIXME: this will make the test fail (currently) if we don't run
+ * under X11
+ * Another solution would be to rerun the test under wineconsole with
+ * the curses backend
+ */
+
+ /* first, we detach and open a fresh console to play with */
+ FreeConsole();
+ ok(AllocConsole(), "Couldn't alloc console\n");
+ hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+ hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+
+ /* now verify everything's ok */
+ ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
+ ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
+
+ ok(ret = GetConsoleScreenBufferInfo(hConOut, &sbi), "Getting sb info\n");
+ if (!ret) return;
+
+ /* Non interactive tests */
+ testCursor(hConOut, sbi.dwSize);
+ /* will test wrapped (on/off) & processed (on/off) strings output */
+ testWrite(hConOut, sbi.dwSize);
+ /* will test line scrolling at the bottom of the screen */
+ /* testBottomScroll(); */
+ /* will test all the scrolling operations */
+ testScroll(hConOut, sbi.dwSize);
+ /* will test sb creation / modification... */
+ /* testScreenBuffer() */
+ testCtrlHandler();
+ /* still to be done: access rights & access on objects */
+}
--- /dev/null
+/*
+ * Unit test suite for directory functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+/* If you change something in these tests, please do the same
+ * for GetSystemDirectory tests.
+ */
+static void test_GetWindowsDirectoryA(void)
+{
+ UINT len, len_with_null;
+ char buf[MAX_PATH];
+
+ len_with_null = GetWindowsDirectoryA(NULL, 0);
+ ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+ lstrcpyA(buf, "foo");
+ len_with_null = GetWindowsDirectoryA(buf, 1);
+ ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+
+ lstrcpyA(buf, "foo");
+ len = GetWindowsDirectoryA(buf, len_with_null - 1);
+ ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+ ok(len == len_with_null, "GetWindowsDirectoryW returned %d, expected %d\n",
+ len, len_with_null);
+
+ lstrcpyA(buf, "foo");
+ len = GetWindowsDirectoryA(buf, len_with_null);
+ ok(lstrcmpA(buf, "foo") != 0, "should touch the buffer\n");
+ ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+ ok(len == len_with_null-1, "GetWindowsDirectoryA returned %d, expected %d\n",
+ len, len_with_null-1);
+}
+
+static void test_GetWindowsDirectoryW(void)
+{
+ UINT len, len_with_null;
+ WCHAR buf[MAX_PATH];
+ static const WCHAR fooW[] = {'f','o','o',0};
+
+ len_with_null = GetWindowsDirectoryW(NULL, 0);
+ if (len_with_null==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+ lstrcpyW(buf, fooW);
+ len = GetWindowsDirectoryW(buf, 1);
+ ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+ ok(len == len_with_null, "GetWindowsDirectoryW returned %d, expected %d\n",
+ len, len_with_null);
+
+ lstrcpyW(buf, fooW);
+ len = GetWindowsDirectoryW(buf, len_with_null - 1);
+ ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+ ok(len == len_with_null, "GetWindowsDirectoryW returned %d, expected %d\n",
+ len, len_with_null);
+
+ lstrcpyW(buf, fooW);
+ len = GetWindowsDirectoryW(buf, len_with_null);
+ ok(lstrcmpW(buf, fooW) != 0, "should touch the buffer\n");
+ ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+ ok(len == len_with_null-1, "GetWindowsDirectoryW returned %d, expected %d\n",
+ len, len_with_null-1);
+}
+
+
+/* If you change something in these tests, please do the same
+ * for GetWindowsDirectory tests.
+ */
+static void test_GetSystemDirectoryA(void)
+{
+ UINT len, len_with_null;
+ char buf[MAX_PATH];
+
+ len_with_null = GetSystemDirectoryA(NULL, 0);
+ ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+ lstrcpyA(buf, "foo");
+ len = GetSystemDirectoryA(buf, 1);
+ ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+ ok(len == len_with_null, "GetSystemDirectoryA returned %d, expected %d\n",
+ len, len_with_null);
+
+ lstrcpyA(buf, "foo");
+ len = GetSystemDirectoryA(buf, len_with_null - 1);
+ ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+ ok(len == len_with_null, "GetSystemDirectoryA returned %d, expected %d\n",
+ len, len_with_null);
+
+ lstrcpyA(buf, "foo");
+ len = GetSystemDirectoryA(buf, len_with_null);
+ ok(lstrcmpA(buf, "foo") != 0, "should touch the buffer\n");
+ ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+ ok(len == len_with_null-1, "GetSystemDirectoryW returned %d, expected %d\n",
+ len, len_with_null-1);
+}
+
+static void test_GetSystemDirectoryW(void)
+{
+ UINT len, len_with_null;
+ WCHAR buf[MAX_PATH];
+ static const WCHAR fooW[] = {'f','o','o',0};
+
+ len_with_null = GetSystemDirectoryW(NULL, 0);
+ if (len_with_null==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+ lstrcpyW(buf, fooW);
+ len = GetSystemDirectoryW(buf, 1);
+ ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+ ok(len == len_with_null, "GetSystemDirectoryW returned %d, expected %d\n",
+ len, len_with_null);
+
+ lstrcpyW(buf, fooW);
+ len = GetSystemDirectoryW(buf, len_with_null - 1);
+ ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+ ok(len == len_with_null, "GetSystemDirectoryW returned %d, expected %d\n",
+ len, len_with_null);
+
+ lstrcpyW(buf, fooW);
+ len = GetSystemDirectoryW(buf, len_with_null);
+ ok(lstrcmpW(buf, fooW) != 0, "should touch the buffer\n");
+ ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+ ok(len == len_with_null-1, "GetSystemDirectoryW returned %d, expected %d\n",
+ len, len_with_null-1);
+}
+
+static void test_CreateDirectoryA(void)
+{
+ char tmpdir[MAX_PATH];
+ BOOL ret;
+
+ ret = CreateDirectoryA(NULL, NULL);
+ ok(ret == FALSE && (GetLastError() == ERROR_PATH_NOT_FOUND ||
+ GetLastError() == ERROR_INVALID_PARAMETER),
+ "CreateDirectoryA(NULL): ret=%d err=%ld\n", ret, GetLastError());
+
+ ret = CreateDirectoryA("", NULL);
+ ok(ret == FALSE && (GetLastError() == ERROR_BAD_PATHNAME ||
+ GetLastError() == ERROR_PATH_NOT_FOUND),
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+
+ ret = GetSystemDirectoryA(tmpdir, MAX_PATH);
+ ok(ret < MAX_PATH, "System directory should fit into MAX_PATH\n");
+
+ ret = SetCurrentDirectoryA(tmpdir);
+ ok(ret == TRUE, "could not chdir to the System directory\n");
+
+ ret = CreateDirectoryA(".", NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS,
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+
+
+ ret = CreateDirectoryA("..", NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS,
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ tmpdir[3] = 0; /* truncate the path */
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == FALSE && (GetLastError() == ERROR_ALREADY_EXISTS ||
+ GetLastError() == ERROR_ACCESS_DENIED),
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE, "CreateDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS,
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+
+ lstrcatA(tmpdir, "?");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == FALSE && (GetLastError() == ERROR_INVALID_NAME ||
+ GetLastError() == ERROR_PATH_NOT_FOUND),
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+ RemoveDirectoryA(tmpdir);
+
+ tmpdir[lstrlenA(tmpdir) - 1] = '*';
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == FALSE && (GetLastError() == ERROR_INVALID_NAME ||
+ GetLastError() == ERROR_PATH_NOT_FOUND),
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+ RemoveDirectoryA(tmpdir);
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me/Please Remove Me");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+ RemoveDirectoryA(tmpdir);
+
+ /* Test behavior with a trailing dot.
+ * The directory should be created without the dot.
+ */
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me.");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE,
+ "CreateDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ lstrcatA(tmpdir, "/Please Remove Me");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE,
+ "CreateDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me");
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ /* Test behavior with two trailing dots.
+ * The directory should be created without the trailing dots.
+ */
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me..");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE,
+ "CreateDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ lstrcatA(tmpdir, "/Please Remove Me");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE || /* On Win98 */
+ (ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND), /* On NT! */
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+ if (ret == TRUE)
+ {
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+ }
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me");
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ /* Test behavior with a trailing space.
+ * The directory should be created without the trailing space.
+ */
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me ");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE,
+ "CreateDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ lstrcatA(tmpdir, "/Please Remove Me");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE || /* On Win98 */
+ (ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND), /* On NT! */
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+ if (ret == TRUE)
+ {
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+ }
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me");
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ /* Test behavior with a trailing space.
+ * The directory should be created without the trailing spaces.
+ */
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me ");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE,
+ "CreateDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+
+ lstrcatA(tmpdir, "/Please Remove Me");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE || /* On Win98 */
+ (ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND), /* On NT! */
+ "CreateDirectoryA(%s): ret=%d err=%ld\n", tmpdir, ret, GetLastError());
+ if (ret == TRUE)
+ {
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+ }
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me");
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE,
+ "RemoveDirectoryA(%s) failed err=%ld\n", tmpdir, GetLastError());
+}
+
+static void test_CreateDirectoryW(void)
+{
+ WCHAR tmpdir[MAX_PATH];
+ BOOL ret;
+ static const WCHAR empty_strW[] = { 0 };
+ static const WCHAR tmp_dir_name[] = {'P','l','e','a','s','e',' ','R','e','m','o','v','e',' ','M','e',0};
+ static const WCHAR dotW[] = {'.',0};
+ static const WCHAR slashW[] = {'/',0};
+ static const WCHAR dotdotW[] = {'.','.',0};
+ static const WCHAR questionW[] = {'?',0};
+
+ ret = CreateDirectoryW(NULL, NULL);
+ if (!ret && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND, "should not create NULL path\n");
+
+ ret = CreateDirectoryW(empty_strW, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND, "should not create empty path\n");
+
+ ret = GetSystemDirectoryW(tmpdir, MAX_PATH);
+ ok(ret < MAX_PATH, "System directory should fit into MAX_PATH\n");
+
+ ret = SetCurrentDirectoryW(tmpdir);
+ ok(ret == TRUE, "could not chdir to the System directory\n");
+
+ ret = CreateDirectoryW(dotW, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+ ret = CreateDirectoryW(dotdotW, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+ GetTempPathW(MAX_PATH, tmpdir);
+ tmpdir[3] = 0; /* truncate the path */
+ ret = CreateDirectoryW(tmpdir, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_ACCESS_DENIED, "should deny access to the drive root\n");
+
+ GetTempPathW(MAX_PATH, tmpdir);
+ lstrcatW(tmpdir, tmp_dir_name);
+ ret = CreateDirectoryW(tmpdir, NULL);
+ ok(ret == TRUE, "CreateDirectoryW should always succeed\n");
+
+ ret = CreateDirectoryW(tmpdir, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+ ret = RemoveDirectoryW(tmpdir);
+ ok(ret == TRUE, "RemoveDirectoryW should always succeed\n");
+
+ lstrcatW(tmpdir, questionW);
+ ret = CreateDirectoryW(tmpdir, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+ "CreateDirectoryW with ? wildcard name should fail with error 183, ret=%s error=%ld\n",
+ ret ? " True" : "False", GetLastError());
+ ret = RemoveDirectoryW(tmpdir);
+
+ tmpdir[lstrlenW(tmpdir) - 1] = '*';
+ ret = CreateDirectoryW(tmpdir, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+ "CreateDirectoryW with * wildcard name should fail with error 183, ret=%s error=%ld\n",
+ ret ? " True" : "False", GetLastError());
+ ret = RemoveDirectoryW(tmpdir);
+
+ GetTempPathW(MAX_PATH, tmpdir);
+ lstrcatW(tmpdir, tmp_dir_name);
+ lstrcatW(tmpdir, slashW);
+ lstrcatW(tmpdir, tmp_dir_name);
+ ret = CreateDirectoryW(tmpdir, NULL);
+ ok(ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "CreateDirectoryW with multiple nonexistent directories in path should fail\n");
+ ret = RemoveDirectoryW(tmpdir);
+}
+
+static void test_RemoveDirectoryA(void)
+{
+ char tmpdir[MAX_PATH];
+ BOOL ret;
+
+ GetTempPathA(MAX_PATH, tmpdir);
+ lstrcatA(tmpdir, "Please Remove Me");
+ ret = CreateDirectoryA(tmpdir, NULL);
+ ok(ret == TRUE, "CreateDirectoryA should always succeed\n");
+
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == TRUE, "RemoveDirectoryA should always succeed\n");
+
+ lstrcatA(tmpdir, "?");
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == FALSE && (GetLastError() == ERROR_INVALID_NAME ||
+ GetLastError() == ERROR_PATH_NOT_FOUND),
+ "RemoveDirectoryA with ? wildcard name should fail, ret=%s error=%ld\n",
+ ret ? " True" : "False", GetLastError());
+
+ tmpdir[lstrlenA(tmpdir) - 1] = '*';
+ ret = RemoveDirectoryA(tmpdir);
+ ok(ret == FALSE && (GetLastError() == ERROR_INVALID_NAME ||
+ GetLastError() == ERROR_PATH_NOT_FOUND),
+ "RemoveDirectoryA with * wildcard name should fail, ret=%s error=%ld\n",
+ ret ? " True" : "False", GetLastError());
+}
+
+static void test_RemoveDirectoryW(void)
+{
+ WCHAR tmpdir[MAX_PATH];
+ BOOL ret;
+ static const WCHAR tmp_dir_name[] = {'P','l','e','a','s','e',' ','R','e','m','o','v','e',' ','M','e',0};
+ static const WCHAR questionW[] = {'?',0};
+
+ GetTempPathW(MAX_PATH, tmpdir);
+ lstrcatW(tmpdir, tmp_dir_name);
+ ret = CreateDirectoryW(tmpdir, NULL);
+ if (!ret && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+
+ ok(ret == TRUE, "CreateDirectoryW should always succeed\n");
+
+ ret = RemoveDirectoryW(tmpdir);
+ ok(ret == TRUE, "RemoveDirectoryW should always succeed\n");
+
+ lstrcatW(tmpdir, questionW);
+ ret = RemoveDirectoryW(tmpdir);
+ ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+ "RemoveDirectoryW with wildcard should fail with error 183, ret=%s error=%ld\n",
+ ret ? " True" : "False", GetLastError());
+
+ tmpdir[lstrlenW(tmpdir) - 1] = '*';
+ ret = RemoveDirectoryW(tmpdir);
+ ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+ "RemoveDirectoryW with * wildcard name should fail with error 183, ret=%s error=%ld\n",
+ ret ? " True" : "False", GetLastError());
+}
+
+static void test_SetCurrentDirectoryA(void)
+{
+ SetLastError(0);
+ ok( !SetCurrentDirectoryA( "\\some_dummy_dir" ), "SetCurrentDirectoryA succeeded\n" );
+ ok( GetLastError() == ERROR_FILE_NOT_FOUND, "wrong error %ld\n", GetLastError() );
+ ok( !SetCurrentDirectoryA( "\\some_dummy\\subdir" ), "SetCurrentDirectoryA succeeded\n" );
+ ok( GetLastError() == ERROR_PATH_NOT_FOUND, "wrong error %ld\n", GetLastError() );
+}
+
+START_TEST(directory)
+{
+ test_GetWindowsDirectoryA();
+ test_GetWindowsDirectoryW();
+
+ test_GetSystemDirectoryA();
+ test_GetSystemDirectoryW();
+
+ test_CreateDirectoryA();
+ test_CreateDirectoryW();
+
+ test_RemoveDirectoryA();
+ test_RemoveDirectoryW();
+
+ test_SetCurrentDirectoryA();
+}
--- /dev/null
+/*
+ * Unit test suite for drive functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+static DWORD (WINAPI *pGetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
+
+static void test_GetDriveTypeA(void)
+{
+ char drive[] = "?:\\";
+ DWORD logical_drives;
+ UINT type;
+
+ logical_drives = GetLogicalDrives();
+ ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+ for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+ {
+ type = GetDriveTypeA(drive);
+ ok(type > 0 && type <= 6, "not a valid drive %c: type %u\n", drive[0], type);
+
+ if (!(logical_drives & 1))
+ ok(type == DRIVE_NO_ROOT_DIR,
+ "GetDriveTypeA should return DRIVE_NO_ROOT_DIR for inexistant drive %c: but not %u\n",
+ drive[0], type);
+
+ logical_drives >>= 1;
+ }
+}
+
+static void test_GetDriveTypeW(void)
+{
+ WCHAR drive[] = {'?',':','\\',0};
+ DWORD logical_drives;
+ UINT type;
+
+ logical_drives = GetLogicalDrives();
+ ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+ for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+ {
+ type = GetDriveTypeW(drive);
+ if (type == DRIVE_UNKNOWN && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ /* Must be Win9x which doesn't support the Unicode functions */
+ return;
+ }
+ ok(type > 0 && type <= 6, "not a valid drive %c: type %u\n", drive[0], type);
+
+ if (!(logical_drives & 1))
+ ok(type == DRIVE_NO_ROOT_DIR,
+ "GetDriveTypeW should return DRIVE_NO_ROOT_DIR for inexistant drive %c: but not %u\n",
+ drive[0], type);
+
+ logical_drives >>= 1;
+ }
+}
+
+static void test_GetDiskFreeSpaceA(void)
+{
+ BOOL ret;
+ DWORD sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters;
+ char drive[] = "?:\\";
+ DWORD logical_drives;
+
+ ret = GetDiskFreeSpaceA(NULL, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ ok(ret, "GetDiskFreeSpaceA error %ld\n", GetLastError());
+
+ ret = GetDiskFreeSpaceA("", §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ ok(!ret && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_INVALID_NAME),
+ "GetDiskFreeSpaceA(\"\"): ret=%d GetLastError=%ld\n",
+ ret, GetLastError());
+
+ ret = GetDiskFreeSpaceA("\\", §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ ok(ret, "GetDiskFreeSpaceA error %ld\n", GetLastError());
+
+ ret = GetDiskFreeSpaceA("/", §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ ok(ret, "GetDiskFreeSpaceA error %ld\n", GetLastError());
+
+ logical_drives = GetLogicalDrives();
+ ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+ for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+ {
+ UINT drivetype = GetDriveTypeA(drive);
+ /* Skip floppy drives because NT pops up a MessageBox if no
+ * floppy is present
+ */
+ if (drivetype != DRIVE_REMOVABLE && drivetype != DRIVE_NO_ROOT_DIR)
+ {
+ ret = GetDiskFreeSpaceA(drive, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ if (!(logical_drives & 1))
+ ok(!ret && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_INVALID_DRIVE),
+ "GetDiskFreeSpaceA(%s): ret=%d GetLastError=%ld\n",
+ drive, ret, GetLastError());
+ else
+ {
+ ok(ret ||
+ (!ret && (GetLastError() == ERROR_NOT_READY || GetLastError() == ERROR_INVALID_DRIVE)),
+ "GetDiskFreeSpaceA(%s): ret=%d GetLastError=%ld\n",
+ drive, ret, GetLastError());
+ if( GetVersion() & 0x80000000)
+ /* win3.0 thru winME */
+ ok( total_clusters <= 65535,
+ "total clusters is %ld > 65535\n", total_clusters);
+ else if (pGetDiskFreeSpaceExA) {
+ /* NT, 2k, XP : GetDiskFreeSpace shoud be accurate */
+ ULARGE_INTEGER totEx, tot, d;
+
+ tot.QuadPart = sectors_per_cluster;
+ tot.QuadPart = (tot.QuadPart * bytes_per_sector) * total_clusters;
+ ret = pGetDiskFreeSpaceExA( drive, &d, &totEx, NULL);
+ ok( ret || (!ret && ERROR_NOT_READY == GetLastError()),
+ "GetDiskFreeSpaceExA( %s ) failed. GetLastError=%ld\n", drive, GetLastError());
+ ok( bytes_per_sector == 0 || /* empty cd rom drive */
+ totEx.QuadPart <= tot.QuadPart,
+ "GetDiskFreeSpaceA should report at least as much bytes on disk %s as GetDiskFreeSpaceExA\n", drive);
+ }
+ }
+ }
+ logical_drives >>= 1;
+ }
+}
+
+static void test_GetDiskFreeSpaceW(void)
+{
+ BOOL ret;
+ DWORD sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters;
+ WCHAR drive[] = {'?',':','\\',0};
+ DWORD logical_drives;
+ static const WCHAR empty_pathW[] = { 0 };
+ static const WCHAR root_pathW[] = { '\\', 0 };
+ static const WCHAR unix_style_root_pathW[] = { '/', 0 };
+
+ ret = GetDiskFreeSpaceW(NULL, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ if (ret == 0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ /* Must be Win9x which doesn't support the Unicode functions */
+ return;
+ }
+ ok(ret, "GetDiskFreeSpaceW error %ld\n", GetLastError());
+
+ ret = GetDiskFreeSpaceW(empty_pathW, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "GetDiskFreeSpaceW(\"\"): ret=%d GetLastError=%ld\n",
+ ret, GetLastError());
+
+ ret = GetDiskFreeSpaceW(root_pathW, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ ok(ret, "GetDiskFreeSpaceW(\"\") error %ld\n", GetLastError());
+
+ ret = GetDiskFreeSpaceW(unix_style_root_pathW, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ ok(ret, "GetDiskFreeSpaceW error %ld\n", GetLastError());
+
+ logical_drives = GetLogicalDrives();
+ ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+ for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+ {
+ UINT drivetype = GetDriveTypeW(drive);
+ /* Skip floppy drives because NT4 pops up a MessageBox if no floppy is present */
+ if (drivetype != DRIVE_REMOVABLE && drivetype != DRIVE_NO_ROOT_DIR)
+ {
+ ret = GetDiskFreeSpaceW(drive, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+ if (!(logical_drives & 1))
+ ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "GetDiskFreeSpaceW(%c): ret=%d GetLastError=%ld\n",
+ drive[0], ret, GetLastError());
+ else
+ ok(ret || GetLastError() == ERROR_NOT_READY,
+ "GetDiskFreeSpaceW(%c): ret=%d GetLastError=%ld\n",
+ drive[0], ret, GetLastError());
+ }
+ logical_drives >>= 1;
+ }
+}
+
+START_TEST(drive)
+{
+ HANDLE hkernel32 = GetModuleHandleA("kernel32");
+ pGetDiskFreeSpaceExA = (void *) GetProcAddress(hkernel32, "GetDiskFreeSpaceExA");
+
+ test_GetDriveTypeA();
+ test_GetDriveTypeW();
+
+ test_GetDiskFreeSpaceA();
+ test_GetDiskFreeSpaceW();
+}
--- /dev/null
+/*
+ * Unit test suite for environment functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+static void test_GetSetEnvironmentVariableA(void)
+{
+ char buf[256];
+ BOOL ret;
+ DWORD ret_size;
+ static const char name[] = "SomeWildName";
+ static const char name_cased[] = "sOMEwILDnAME";
+ static const char value[] = "SomeWildValue";
+
+ ret = SetEnvironmentVariableA(name, value);
+ ok(ret == TRUE,
+ "unexpected error in SetEnvironmentVariableA, GetLastError=%ld\n",
+ GetLastError());
+
+ /* Try to retrieve the environment variable we just set */
+ ret_size = GetEnvironmentVariableA(name, NULL, 0);
+ ok(ret_size == strlen(value) + 1,
+ "should return length with terminating 0 ret_size=%ld\n", ret_size);
+
+ lstrcpyA(buf, "foo");
+ ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value));
+ ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+ ok(ret_size == strlen(value) + 1,
+ "should return length with terminating 0 ret_size=%ld\n", ret_size);
+
+ lstrcpyA(buf, "foo");
+ ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value) + 1);
+ ok(lstrcmpA(buf, value) == 0, "should touch the buffer\n");
+ ok(ret_size == strlen(value),
+ "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+ lstrcpyA(buf, "foo");
+ ret_size = GetEnvironmentVariableA(name_cased, buf, lstrlenA(value) + 1);
+ ok(lstrcmpA(buf, value) == 0, "should touch the buffer\n");
+ ok(ret_size == strlen(value),
+ "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+ /* Remove that environment variable */
+ ret = SetEnvironmentVariableA(name_cased, NULL);
+ ok(ret == TRUE, "should erase existing variable\n");
+
+ lstrcpyA(buf, "foo");
+ ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value) + 1);
+ ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+ ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+
+ /* Check behavior of SetEnvironmentVariableA(name, "") */
+ ret = SetEnvironmentVariableA(name, value);
+ ok(ret == TRUE,
+ "unexpected error in SetEnvironmentVariableA, GetLastError=%ld\n",
+ GetLastError());
+
+ lstrcpyA(buf, "foo");
+ ret_size = GetEnvironmentVariableA(name_cased, buf, lstrlenA(value) + 1);
+ ok(lstrcmpA(buf, value) == 0, "should touch the buffer\n");
+ ok(ret_size == strlen(value),
+ "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+ ret = SetEnvironmentVariableA(name_cased, "");
+ ok(ret == TRUE,
+ "should not fail with empty value but GetLastError=%ld\n", GetLastError());
+
+ lstrcpyA(buf, "foo");
+ SetLastError(0);
+ ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value) + 1);
+ ok(ret_size == 0 &&
+ ((GetLastError() == 0 && lstrcmpA(buf, "") == 0) ||
+ (GetLastError() == ERROR_ENVVAR_NOT_FOUND)),
+ "%s should be set to \"\" (NT) or removed (Win9x) but ret_size=%ld GetLastError=%ld and buf=%s\n",
+ name, ret_size, GetLastError(), buf);
+
+ /* Test the limits */
+ ret_size = GetEnvironmentVariableA(NULL, NULL, 0);
+ ok(ret_size == 0 && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_ENVVAR_NOT_FOUND),
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+
+ ret_size = GetEnvironmentVariableA(NULL, buf, lstrlenA(value) + 1);
+ ok(ret_size == 0 && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_ENVVAR_NOT_FOUND),
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+
+ ret_size = GetEnvironmentVariableA("", buf, lstrlenA(value) + 1);
+ ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+}
+
+static void test_GetSetEnvironmentVariableW(void)
+{
+ WCHAR buf[256];
+ BOOL ret;
+ DWORD ret_size;
+ static const WCHAR name[] = {'S','o','m','e','W','i','l','d','N','a','m','e',0};
+ static const WCHAR value[] = {'S','o','m','e','W','i','l','d','V','a','l','u','e',0};
+ static const WCHAR name_cased[] = {'s','O','M','E','w','I','L','D','n','A','M','E',0};
+ static const WCHAR empty_strW[] = { 0 };
+ static const WCHAR fooW[] = {'f','o','o',0};
+
+ ret = SetEnvironmentVariableW(name, value);
+ if (ret == FALSE && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ /* Must be Win9x which doesn't support the Unicode functions */
+ return;
+ }
+ ok(ret == TRUE,
+ "unexpected error in SetEnvironmentVariableW, GetLastError=%ld\n",
+ GetLastError());
+
+ /* Try to retrieve the environment variable we just set */
+ ret_size = GetEnvironmentVariableW(name, NULL, 0);
+ ok(ret_size == lstrlenW(value) + 1,
+ "should return length with terminating 0 ret_size=%ld\n",
+ ret_size);
+
+ lstrcpyW(buf, fooW);
+ ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value));
+ ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+
+ ok(ret_size == lstrlenW(value) + 1,
+ "should return length with terminating 0 ret_size=%ld\n", ret_size);
+
+ lstrcpyW(buf, fooW);
+ ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+ ok(lstrcmpW(buf, value) == 0, "should touch the buffer\n");
+ ok(ret_size == lstrlenW(value),
+ "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+ lstrcpyW(buf, fooW);
+ ret_size = GetEnvironmentVariableW(name_cased, buf, lstrlenW(value) + 1);
+ ok(lstrcmpW(buf, value) == 0, "should touch the buffer\n");
+ ok(ret_size == lstrlenW(value),
+ "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+ /* Remove that environment variable */
+ ret = SetEnvironmentVariableW(name_cased, NULL);
+ ok(ret == TRUE, "should erase existing variable\n");
+
+ lstrcpyW(buf, fooW);
+ ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+ ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+ ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+
+ /* Check behavior of SetEnvironmentVariableW(name, "") */
+ ret = SetEnvironmentVariableW(name, value);
+ ok(ret == TRUE,
+ "unexpected error in SetEnvironmentVariableW, GetLastError=%ld\n",
+ GetLastError());
+
+ lstrcpyW(buf, fooW);
+ ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+ ok(lstrcmpW(buf, value) == 0, "should touch the buffer\n");
+ ok(ret_size == lstrlenW(value),
+ "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+ ret = SetEnvironmentVariableW(name_cased, empty_strW);
+ ok(ret == TRUE, "should not fail with empty value but GetLastError=%ld\n", GetLastError());
+
+ lstrcpyW(buf, fooW);
+ ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+ ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+ ok(lstrcmpW(buf, empty_strW) == 0, "should copy an empty string\n");
+
+ /* Test the limits */
+ ret_size = GetEnvironmentVariableW(NULL, NULL, 0);
+ ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+
+ ret_size = GetEnvironmentVariableW(NULL, buf, lstrlenW(value) + 1);
+ ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+ "should not find variable but ret_size=%ld GetLastError=%ld\n",
+ ret_size, GetLastError());
+
+ ret = SetEnvironmentVariableW(NULL, NULL);
+ ok(ret == FALSE && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_ENVVAR_NOT_FOUND),
+ "should fail with NULL, NULL but ret=%d and GetLastError=%ld\n",
+ ret, GetLastError());
+}
+
+static void test_ExpandEnvironmentStringsA(void)
+{
+ char buf[256], buf1[256];
+ DWORD ret_size, ret_size1;
+
+ ret_size1 = GetWindowsDirectoryA(buf1,256);
+ ok ((ret_size1 >0) && (ret_size1<256), "GetWindowsDirectory Failed\n");
+ ret_size = ExpandEnvironmentStringsA("%SystemRoot%",buf,sizeof(buf));
+ if (ERROR_ENVVAR_NOT_FOUND == GetLastError())
+ return;
+ ok(!strcmp(buf, buf1), "ExpandEnvironmentStrings failed %s vs %s. ret_size = %ld\n", buf, buf1, ret_size);
+}
+
+START_TEST(environ)
+{
+ test_GetSetEnvironmentVariableA();
+ test_GetSetEnvironmentVariableW();
+ test_ExpandEnvironmentStringsA();
+}
--- /dev/null
+/*
+ * Unit tests for file functions in Wine
+ *
+ * Copyright (c) 2002, 2004 Jakob Eriksson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+static int dll_capable(const char *dll, const char *function)
+{
+ HMODULE module = GetModuleHandleA(dll);
+ if (!module) return 0;
+
+ return (GetProcAddress(module, function) != NULL);
+}
+
+
+LPCSTR filename = "testfile.xxx";
+LPCSTR sillytext =
+"en larvig liten text dx \033 gx hej 84 hej 4484 ! \001\033 bla bl\na.. bla bla."
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34 3############# 33 3 3 3 # 3## 3"
+"sdlkfjasdlkfj a dslkj adsklf \n \nasdklf askldfa sdlkf \nsadklf asdklf asdf ";
+
+
+static void test__hread( void )
+{
+ HFILE filehandle;
+ char buffer[10000];
+ long bytes_read;
+ long bytes_wanted;
+ long i;
+ BOOL ret;
+
+ SetFileAttributesA(filename,FILE_ATTRIBUTE_NORMAL); /* be sure to remove stale files */
+ DeleteFileA( filename );
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READ );
+
+ ok( HFILE_ERROR != filehandle, "couldn't open file \"%s\" again (err=%ld)\n", filename, GetLastError( ) );
+
+ bytes_read = _hread( filehandle, buffer, 2 * strlen( sillytext ) );
+
+ ok( lstrlenA( sillytext ) == bytes_read, "file read size error\n" );
+
+ for (bytes_wanted = 0; bytes_wanted < lstrlenA( sillytext ); bytes_wanted++)
+ {
+ ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+ ok( _hread( filehandle, buffer, bytes_wanted ) == bytes_wanted, "erratic _hread return value\n" );
+ for (i = 0; i < bytes_wanted; i++)
+ {
+ ok( buffer[i] == sillytext[i], "that's not what's written\n" );
+ }
+ }
+
+ ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret != 0, "DeleteFile failed (%ld)\n", GetLastError( ) );
+}
+
+
+static void test__hwrite( void )
+{
+ HFILE filehandle;
+ char buffer[10000];
+ long bytes_read;
+ long bytes_written;
+ long blocks;
+ long i;
+ char *contents;
+ HLOCAL memory_object;
+ char checksum[1];
+ BOOL ret;
+
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ ok( HFILE_ERROR != _hwrite( filehandle, "", 0 ), "_hwrite complains\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READ );
+
+ bytes_read = _hread( filehandle, buffer, 1);
+
+ ok( 0 == bytes_read, "file read size error\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READWRITE );
+
+ bytes_written = 0;
+ checksum[0] = '\0';
+ srand( (unsigned)time( NULL ) );
+ for (blocks = 0; blocks < 100; blocks++)
+ {
+ for (i = 0; i < (long)sizeof( buffer ); i++)
+ {
+ buffer[i] = rand( );
+ checksum[0] = checksum[0] + buffer[i];
+ }
+ ok( HFILE_ERROR != _hwrite( filehandle, buffer, sizeof( buffer ) ), "_hwrite complains\n" );
+ bytes_written = bytes_written + sizeof( buffer );
+ }
+
+ ok( HFILE_ERROR != _hwrite( filehandle, checksum, 1 ), "_hwrite complains\n" );
+ bytes_written++;
+
+ ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+ memory_object = LocalAlloc( LPTR, bytes_written );
+
+ ok( 0 != memory_object, "LocalAlloc fails. (Could be out of memory.)\n" );
+
+ contents = LocalLock( memory_object );
+
+ filehandle = _lopen( filename, OF_READ );
+
+ contents = LocalLock( memory_object );
+
+ ok( NULL != contents, "LocalLock whines\n" );
+
+ ok( bytes_written == _hread( filehandle, contents, bytes_written), "read length differ from write length\n" );
+
+ checksum[0] = '\0';
+ i = 0;
+ do
+ {
+ checksum[0] = checksum[0] + contents[i];
+ i++;
+ }
+ while (i < bytes_written - 1);
+
+ ok( checksum[0] == contents[i], "stored checksum differ from computed checksum\n" );
+
+ ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret != 0, "DeleteFile failed (%ld)\n", GetLastError( ) );
+}
+
+
+static void test__lclose( void )
+{
+ HFILE filehandle;
+ BOOL ret;
+
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret != 0, "DeleteFile failed (%ld)\n", GetLastError( ) );
+}
+
+
+static void test__lcreat( void )
+{
+ HFILE filehandle;
+ char buffer[10000];
+ WIN32_FIND_DATAA search_results;
+ char slashname[] = "testfi/";
+ int err;
+ HANDLE find;
+ BOOL ret;
+
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+ ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+
+ ok( _hread( filehandle, buffer, strlen( sillytext ) ) == lstrlenA( sillytext ), "erratic _hread return value\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should be able to find file\n" );
+
+ ret = DeleteFileA(filename);
+ ok( ret != 0, "DeleteFile failed (%ld)\n", GetLastError());
+
+ filehandle = _lcreat( filename, 1 ); /* readonly */
+ ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%ld)\n", filename, GetLastError( ) );
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite shouldn't be able to write never the less\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should be able to find file\n" );
+
+ ok( 0 == DeleteFileA( filename ), "shouldn't be able to delete a readonly file\n" );
+
+ ok( SetFileAttributesA(filename, FILE_ATTRIBUTE_NORMAL ) != 0, "couldn't change attributes on file\n" );
+
+ ok( DeleteFileA( filename ) != 0, "now it should be possible to delete the file!\n" );
+
+ filehandle = _lcreat( filename, 2 );
+ ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%ld)\n", filename, GetLastError( ) );
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+ ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+
+ ok( _hread( filehandle, buffer, strlen( sillytext ) ) == lstrlenA( sillytext ), "erratic _hread return value\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should STILL be able to find file\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+
+ filehandle = _lcreat( filename, 4 ); /* SYSTEM file */
+ ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%ld)\n", filename, GetLastError( ) );
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+ ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+
+ ok( _hread( filehandle, buffer, strlen( sillytext ) ) == lstrlenA( sillytext ), "erratic _hread return value\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should STILL be able to find file\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+
+ filehandle=_lcreat (slashname, 0); /* illegal name */
+ if (HFILE_ERROR==filehandle) {
+ err=GetLastError ();
+ ok (err==ERROR_INVALID_NAME || err==ERROR_PATH_NOT_FOUND,
+ "creating file \"%s\" failed with error %d\n", slashname, err);
+ } else { /* only NT succeeds */
+ _lclose(filehandle);
+ find=FindFirstFileA (slashname, &search_results);
+ if (INVALID_HANDLE_VALUE!=find)
+ {
+ ret = FindClose (find);
+ ok (0 != ret, "FindClose complains (%ld)\n", GetLastError ());
+ slashname[strlen(slashname)-1]=0;
+ ok (!strcmp (slashname, search_results.cFileName),
+ "found unexpected name \"%s\"\n", search_results.cFileName);
+ ok (FILE_ATTRIBUTE_ARCHIVE==search_results.dwFileAttributes,
+ "attributes of file \"%s\" are 0x%04lx\n", search_results.cFileName,
+ search_results.dwFileAttributes);
+ }
+ ret = DeleteFileA( slashname );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+ }
+
+ filehandle=_lcreat (filename, 8); /* illegal attribute */
+ if (HFILE_ERROR==filehandle)
+ ok (0, "couldn't create volume label \"%s\"\n", filename);
+ else {
+ _lclose(filehandle);
+ find=FindFirstFileA (filename, &search_results);
+ if (INVALID_HANDLE_VALUE==find)
+ ok (0, "file \"%s\" not found\n", filename);
+ else {
+ ret = FindClose(find);
+ ok ( 0 != ret, "FindClose complains (%ld)\n", GetLastError ());
+ ok (!strcmp (filename, search_results.cFileName),
+ "found unexpected name \"%s\"\n", search_results.cFileName);
+ ok (FILE_ATTRIBUTE_ARCHIVE==search_results.dwFileAttributes,
+ "attributes of file \"%s\" are 0x%04lx\n", search_results.cFileName,
+ search_results.dwFileAttributes);
+ }
+ ret = DeleteFileA( filename );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+ }
+}
+
+
+static void test__llseek( void )
+{
+ INT i;
+ HFILE filehandle;
+ char buffer[1];
+ long bytes_read;
+ BOOL ret;
+
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ for (i = 0; i < 400; i++)
+ {
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+ }
+ ok( HFILE_ERROR != _llseek( filehandle, 400 * strlen( sillytext ), FILE_CURRENT ), "should be able to seek\n" );
+ ok( HFILE_ERROR != _llseek( filehandle, 27 + 35 * strlen( sillytext ), FILE_BEGIN ), "should be able to seek\n" );
+
+ bytes_read = _hread( filehandle, buffer, 1);
+ ok( 1 == bytes_read, "file read size error\n" );
+ ok( buffer[0] == sillytext[27], "_llseek error, it got lost seeking\n" );
+ ok( HFILE_ERROR != _llseek( filehandle, -400 * strlen( sillytext ), FILE_END ), "should be able to seek\n" );
+
+ bytes_read = _hread( filehandle, buffer, 1);
+ ok( 1 == bytes_read, "file read size error\n" );
+ ok( buffer[0] == sillytext[0], "_llseek error, it got lost seeking\n" );
+ ok( HFILE_ERROR != _llseek( filehandle, 1000000, FILE_END ), "should be able to seek past file; poor, poor Windows programmers\n" );
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+}
+
+
+static void test__llopen( void )
+{
+ HFILE filehandle;
+ UINT bytes_read;
+ char buffer[10000];
+ BOOL ret;
+
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READ );
+ ok( HFILE_ERROR == _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite shouldn't be able to write!\n" );
+ bytes_read = _hread( filehandle, buffer, strlen( sillytext ) );
+ ok( strlen( sillytext ) == bytes_read, "file read size error\n" );
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READWRITE );
+ bytes_read = _hread( filehandle, buffer, 2 * strlen( sillytext ) );
+ ok( strlen( sillytext ) == bytes_read, "file read size error\n" );
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite should write just fine\n" );
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_WRITE );
+ ok( HFILE_ERROR == _hread( filehandle, buffer, 1 ), "you should only be able to write this file\n" );
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite should write just fine\n" );
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+ /* TODO - add tests for the SHARE modes - use two processes to pull this one off */
+}
+
+
+static void test__lread( void )
+{
+ HFILE filehandle;
+ char buffer[10000];
+ long bytes_read;
+ UINT bytes_wanted;
+ UINT i;
+ BOOL ret;
+
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READ );
+
+ ok( HFILE_ERROR != filehandle, "couldn't open file \"%s\" again (err=%ld)\n", filename, GetLastError());
+
+ bytes_read = _lread( filehandle, buffer, 2 * strlen( sillytext ) );
+
+ ok( lstrlenA( sillytext ) == bytes_read, "file read size error\n" );
+
+ for (bytes_wanted = 0; bytes_wanted < strlen( sillytext ); bytes_wanted++)
+ {
+ ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+ ok( _lread( filehandle, buffer, bytes_wanted ) == bytes_wanted, "erratic _hread return value\n" );
+ for (i = 0; i < bytes_wanted; i++)
+ {
+ ok( buffer[i] == sillytext[i], "that's not what's written\n" );
+ }
+ }
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+}
+
+
+static void test__lwrite( void )
+{
+ HFILE filehandle;
+ char buffer[10000];
+ long bytes_read;
+ long bytes_written;
+ long blocks;
+ long i;
+ char *contents;
+ HLOCAL memory_object;
+ char checksum[1];
+ BOOL ret;
+
+ filehandle = _lcreat( filename, 0 );
+ if (filehandle == HFILE_ERROR)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+
+ ok( HFILE_ERROR != _lwrite( filehandle, "", 0 ), "_hwrite complains\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READ );
+
+ bytes_read = _hread( filehandle, buffer, 1);
+
+ ok( 0 == bytes_read, "file read size error\n" );
+
+ ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+ filehandle = _lopen( filename, OF_READWRITE );
+
+ bytes_written = 0;
+ checksum[0] = '\0';
+ srand( (unsigned)time( NULL ) );
+ for (blocks = 0; blocks < 100; blocks++)
+ {
+ for (i = 0; i < (long)sizeof( buffer ); i++)
+ {
+ buffer[i] = rand( );
+ checksum[0] = checksum[0] + buffer[i];
+ }
+ ok( HFILE_ERROR != _lwrite( filehandle, buffer, sizeof( buffer ) ), "_hwrite complains\n" );
+ bytes_written = bytes_written + sizeof( buffer );
+ }
+
+ ok( HFILE_ERROR != _lwrite( filehandle, checksum, 1 ), "_hwrite complains\n" );
+ bytes_written++;
+
+ ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+ memory_object = LocalAlloc( LPTR, bytes_written );
+
+ ok( 0 != memory_object, "LocalAlloc fails, could be out of memory\n" );
+
+ contents = LocalLock( memory_object );
+
+ filehandle = _lopen( filename, OF_READ );
+
+ contents = LocalLock( memory_object );
+
+ ok( NULL != contents, "LocalLock whines\n" );
+
+ ok( bytes_written == _hread( filehandle, contents, bytes_written), "read length differ from write length\n" );
+
+ checksum[0] = '\0';
+ i = 0;
+ do
+ {
+ checksum[0] += contents[i];
+ i++;
+ }
+ while (i < bytes_written - 1);
+
+ ok( checksum[0] == contents[i], "stored checksum differ from computed checksum\n" );
+
+ ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+ ret = DeleteFileA( filename );
+ ok( ret, "DeleteFile failed (%ld)\n", GetLastError( ) );
+}
+
+static void test_CopyFileA(void)
+{
+ char temp_path[MAX_PATH];
+ char source[MAX_PATH], dest[MAX_PATH];
+ static const char prefix[] = "pfx";
+ HANDLE hfile;
+ FILETIME ft1, ft2;
+ char buf[10];
+ DWORD ret;
+ BOOL retok;
+
+ ret = GetTempPathA(MAX_PATH, temp_path);
+ ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameA(temp_path, prefix, 0, source);
+ ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+ /* make the source have not zero size */
+ hfile = CreateFileA(source, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok(hfile != INVALID_HANDLE_VALUE, "failed to open source file\n");
+ retok = WriteFile(hfile, prefix, sizeof(prefix), &ret, NULL );
+ ok( retok && ret == sizeof(prefix),
+ "WriteFile error %ld\n", GetLastError());
+ ok(GetFileSize(hfile, NULL) == sizeof(prefix), "source file has wrong size\n");
+ /* get the file time and change it to prove the difference */
+ ret = GetFileTime(hfile, NULL, NULL, &ft1);
+ ok( ret, "GetFileTime error %ld\n", GetLastError());
+ ft1.dwLowDateTime -= 600000000; /* 60 second */
+ ret = SetFileTime(hfile, NULL, NULL, &ft1);
+ ok( ret, "SetFileTime error %ld\n", GetLastError());
+ GetFileTime(hfile, NULL, NULL, &ft1); /* get the actual time back */
+ CloseHandle(hfile);
+
+ ret = GetTempFileNameA(temp_path, prefix, 0, dest);
+ ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+ SetLastError(0xdeadbeef);
+ ret = CopyFileA(source, dest, TRUE);
+ ok(!ret && GetLastError() == ERROR_FILE_EXISTS,
+ "CopyFileA: unexpected error %ld\n", GetLastError());
+
+ ret = CopyFileA(source, dest, FALSE);
+ ok(ret, "CopyFileA: error %ld\n", GetLastError());
+
+ /* make sure that destination has correct size */
+ hfile = CreateFileA(dest, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
+ ok(hfile != INVALID_HANDLE_VALUE, "failed to open destination file\n");
+ ret = GetFileSize(hfile, NULL);
+ ok(ret == sizeof(prefix), "destination file has wrong size %ld\n", ret);
+
+ /* make sure that destination has the same filetime */
+ ret = GetFileTime(hfile, NULL, NULL, &ft2);
+ ok( ret, "GetFileTime error %ld\n", GetLastError());
+ ok(CompareFileTime(&ft1, &ft2) == 0, "destination file has wrong filetime\n");
+
+ SetLastError(0xdeadbeef);
+ ret = CopyFileA(source, dest, FALSE);
+ ok(!ret && GetLastError() == ERROR_SHARING_VIOLATION,
+ "CopyFileA: ret = %ld, unexpected error %ld\n", ret, GetLastError());
+
+ /* make sure that destination still has correct size */
+ ret = GetFileSize(hfile, NULL);
+ ok(ret == sizeof(prefix), "destination file has wrong size %ld\n", ret);
+ retok = ReadFile(hfile, buf, sizeof(buf), &ret, NULL);
+ ok( retok && ret == sizeof(prefix),
+ "ReadFile: error %ld\n", GetLastError());
+ ok(!memcmp(prefix, buf, sizeof(prefix)), "buffer contents mismatch\n");
+ CloseHandle(hfile);
+
+ ret = DeleteFileA(source);
+ ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+ ret = DeleteFileA(dest);
+ ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+}
+
+static void test_CopyFileW(void)
+{
+ WCHAR temp_path[MAX_PATH];
+ WCHAR source[MAX_PATH], dest[MAX_PATH];
+ static const WCHAR prefix[] = {'p','f','x',0};
+ DWORD ret;
+
+ ret = GetTempPathW(MAX_PATH, temp_path);
+ if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(ret != 0, "GetTempPathW error %ld\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, source);
+ ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, dest);
+ ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+ ret = CopyFileW(source, dest, TRUE);
+ ok(!ret && GetLastError() == ERROR_FILE_EXISTS,
+ "CopyFileW: unexpected error %ld\n", GetLastError());
+
+ ret = CopyFileW(source, dest, FALSE);
+ ok(ret, "CopyFileW: error %ld\n", GetLastError());
+
+ ret = DeleteFileW(source);
+ ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+ ret = DeleteFileW(dest);
+ ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+}
+
+static void test_CreateFileA(void)
+{
+ HANDLE hFile;
+ char temp_path[MAX_PATH];
+ char filename[MAX_PATH];
+ static const char prefix[] = "pfx";
+ DWORD ret;
+
+ ret = GetTempPathA(MAX_PATH, temp_path);
+ ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameA(temp_path, prefix, 0, filename);
+ ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+ hFile = CreateFileA(filename, GENERIC_READ, 0, NULL,
+ CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0);
+ ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_EXISTS,
+ "CREATE_NEW should fail if file exists and last error value should be ERROR_FILE_EXISTS\n");
+
+ ret = DeleteFileA(filename);
+ ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+}
+
+static void test_CreateFileW(void)
+{
+ HANDLE hFile;
+ WCHAR temp_path[MAX_PATH];
+ WCHAR filename[MAX_PATH];
+ static const WCHAR emptyW[]={'\0'};
+ static const WCHAR prefix[] = {'p','f','x',0};
+ static const WCHAR bogus[] = { '\\', '\\', '.', '\\', 'B', 'O', 'G', 'U', 'S', 0 };
+ DWORD ret;
+
+ ret = GetTempPathW(MAX_PATH, temp_path);
+ if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(ret != 0, "GetTempPathW error %ld\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, filename);
+ ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+ hFile = CreateFileW(filename, GENERIC_READ, 0, NULL,
+ CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0);
+ ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_EXISTS,
+ "CREATE_NEW should fail if file exists and last error value should be ERROR_FILE_EXISTS\n");
+
+ ret = DeleteFileW(filename);
+ ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+
+#if 0 /* this test crashes on NT4.0 */
+ hFile = CreateFileW(NULL, GENERIC_READ, 0, NULL,
+ CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0);
+ ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "CreateFileW(NULL) returned ret=%p error=%ld\n",hFile,GetLastError());
+#endif
+
+ hFile = CreateFileW(emptyW, GENERIC_READ, 0, NULL,
+ CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0);
+ ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "CreateFileW(\"\") returned ret=%p error=%ld\n",hFile,GetLastError());
+
+ /* test the result of opening a nonexistent driver name */
+ hFile = CreateFileW(bogus, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
+ "CreateFileW on invalid VxD name returned ret=%p error=%ld\n",hFile,GetLastError());
+}
+
+static void test_GetTempFileNameA(void)
+{
+ UINT result;
+ char out[MAX_PATH];
+ char expected[MAX_PATH + 10];
+ char windowsdir[MAX_PATH + 10];
+ char windowsdrive[3];
+
+ result = GetWindowsDirectory(windowsdir, sizeof(windowsdir));
+ ok(result < sizeof(windowsdir), "windowsdir is abnormally long!\n");
+ ok(result != 0, "GetWindowsDirectory: error %ld\n", GetLastError());
+
+ /* If the Windows directory is the root directory, it ends in backslash, not else. */
+ if (strlen(windowsdir) != 3) /* As in "C:\" or "F:\" */
+ {
+ strcat(windowsdir, "\\");
+ }
+
+ windowsdrive[0] = windowsdir[0];
+ windowsdrive[1] = windowsdir[1];
+ windowsdrive[2] = '\0';
+
+ result = GetTempFileNameA(windowsdrive, "abc", 1, out);
+ ok(result != 0, "GetTempFileNameA: error %ld\n", GetLastError());
+ ok(((out[0] == windowsdrive[0]) && (out[1] == ':')) && (out[2] == '\\'),
+ "GetTempFileNameA: first three characters should be %c:\\, string was actually %s\n",
+ windowsdrive[0], out);
+
+ result = GetTempFileNameA(windowsdir, "abc", 2, out);
+ ok(result != 0, "GetTempFileNameA: error %ld\n", GetLastError());
+ expected[0] = '\0';
+ strcat(expected, windowsdir);
+ strcat(expected, "abc2.tmp");
+ ok(lstrcmpiA(out, expected) == 0, "GetTempFileNameA: Unexpected output \"%s\" vs \"%s\"\n",
+ out, expected);
+}
+
+static void test_DeleteFileA( void )
+{
+ BOOL ret;
+
+ ret = DeleteFileA(NULL);
+ ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_PATH_NOT_FOUND),
+ "DeleteFileA(NULL) returned ret=%d error=%ld\n",ret,GetLastError());
+
+ ret = DeleteFileA("");
+ ok(!ret && (GetLastError() == ERROR_PATH_NOT_FOUND ||
+ GetLastError() == ERROR_BAD_PATHNAME),
+ "DeleteFileA(\"\") returned ret=%d error=%ld\n",ret,GetLastError());
+
+ ret = DeleteFileA("nul");
+ ok(!ret && (GetLastError() == ERROR_FILE_NOT_FOUND ||
+ GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_ACCESS_DENIED ||
+ GetLastError() == ERROR_INVALID_FUNCTION),
+ "DeleteFileA(\"nul\") returned ret=%d error=%ld\n",ret,GetLastError());
+}
+
+static void test_DeleteFileW( void )
+{
+ BOOL ret;
+ static const WCHAR emptyW[]={'\0'};
+
+ ret = DeleteFileW(NULL);
+ if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "DeleteFileW(NULL) returned ret=%d error=%ld\n",ret,GetLastError());
+
+ ret = DeleteFileW(emptyW);
+ ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "DeleteFileW(\"\") returned ret=%d error=%ld\n",ret,GetLastError());
+}
+
+#define IsDotDir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
+
+static void test_MoveFileA(void)
+{
+ char tempdir[MAX_PATH];
+ char source[MAX_PATH], dest[MAX_PATH];
+ static const char prefix[] = "pfx";
+ DWORD ret;
+
+ ret = GetTempPathA(MAX_PATH, tempdir);
+ ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameA(tempdir, prefix, 0, source);
+ ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+ ret = GetTempFileNameA(tempdir, prefix, 0, dest);
+ ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+ ret = MoveFileA(source, dest);
+ ok(!ret && GetLastError() == ERROR_ALREADY_EXISTS,
+ "MoveFileA: unexpected error %ld\n", GetLastError());
+
+ ret = DeleteFileA(dest);
+ ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+
+ ret = MoveFileA(source, dest);
+ ok(ret, "MoveFileA: failed, error %ld\n", GetLastError());
+
+ lstrcatA(tempdir, "Remove Me");
+ ret = CreateDirectoryA(tempdir, NULL);
+ ok(ret == TRUE, "CreateDirectoryA failed\n");
+
+ lstrcpyA(source, dest);
+ lstrcpyA(dest, tempdir);
+ lstrcatA(dest, "\\wild?.*");
+ /* FIXME: if we create a file with wildcards we can't delete it now that DeleteFile works correctly */
+ ret = MoveFileA(source, dest);
+ ok(!ret, "MoveFileA: shouldn't move to wildcard file\n");
+ ok(GetLastError() == ERROR_INVALID_NAME || /* NT */
+ GetLastError() == ERROR_FILE_NOT_FOUND, /* Win9x */
+ "MoveFileA: with wildcards, unexpected error %ld\n", GetLastError());
+ if (ret || (GetLastError() != ERROR_INVALID_NAME))
+ {
+ WIN32_FIND_DATAA fd;
+ char temppath[MAX_PATH];
+ HANDLE hFind;
+
+ lstrcpyA(temppath, tempdir);
+ lstrcatA(temppath, "\\*.*");
+ hFind = FindFirstFileA(temppath, &fd);
+ if (INVALID_HANDLE_VALUE != hFind)
+ {
+ LPSTR lpName;
+ do
+ {
+ lpName = fd.cAlternateFileName;
+ if (!lpName[0])
+ lpName = fd.cFileName;
+ ok(IsDotDir(lpName), "MoveFileA: wildcards file created!\n");
+ }
+ while (FindNextFileA(hFind, &fd));
+ FindClose(hFind);
+ }
+ }
+ ret = DeleteFileA(source);
+ ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+ ret = DeleteFileA(dest);
+ ok(!ret, "DeleteFileA: error %ld\n", GetLastError());
+ ret = RemoveDirectoryA(tempdir);
+ ok(ret, "DeleteDirectoryA: error %ld\n", GetLastError());
+}
+
+static void test_MoveFileW(void)
+{
+ WCHAR temp_path[MAX_PATH];
+ WCHAR source[MAX_PATH], dest[MAX_PATH];
+ static const WCHAR prefix[] = {'p','f','x',0};
+ DWORD ret;
+
+ ret = GetTempPathW(MAX_PATH, temp_path);
+ if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(ret != 0, "GetTempPathW error %ld\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, source);
+ ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+ ret = GetTempFileNameW(temp_path, prefix, 0, dest);
+ ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+ ret = MoveFileW(source, dest);
+ ok(!ret && GetLastError() == ERROR_ALREADY_EXISTS,
+ "CopyFileW: unexpected error %ld\n", GetLastError());
+
+ ret = DeleteFileW(source);
+ ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+ ret = DeleteFileW(dest);
+ ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+}
+
+#define PATTERN_OFFSET 0x10
+
+static void test_offset_in_overlapped_structure(void)
+{
+ HANDLE hFile;
+ OVERLAPPED ov;
+ DWORD done;
+ BOOL rc;
+ BYTE buf[256], pattern[] = "TeSt";
+ UINT i;
+ char temp_path[MAX_PATH], temp_fname[MAX_PATH];
+ BOOL ret;
+
+ ret =GetTempPathA(MAX_PATH, temp_path);
+ ok( ret, "GetTempPathA error %ld\n", GetLastError());
+ ret =GetTempFileNameA(temp_path, "pfx", 0, temp_fname);
+ ok( ret, "GetTempFileNameA error %ld\n", GetLastError());
+
+ /*** Write File *****************************************************/
+
+ hFile = CreateFileA(temp_fname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+ ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA error %ld\n", GetLastError());
+
+ for(i = 0; i < sizeof(buf); i++) buf[i] = i;
+ ret = WriteFile(hFile, buf, sizeof(buf), &done, NULL);
+ ok( ret, "WriteFile error %ld\n", GetLastError());
+ ok(done == sizeof(buf), "expected number of bytes written %lu\n", done);
+
+ memset(&ov, 0, sizeof(ov));
+ S(U(ov)).Offset = PATTERN_OFFSET;
+ S(U(ov)).OffsetHigh = 0;
+ rc=WriteFile(hFile, pattern, sizeof(pattern), &done, &ov);
+ /* Win 9x does not support the overlapped I/O on files */
+ if (rc || GetLastError()!=ERROR_INVALID_PARAMETER) {
+ ok(rc, "WriteFile error %ld\n", GetLastError());
+ ok(done == sizeof(pattern), "expected number of bytes written %lu\n", done);
+ /*trace("Current offset = %04lx\n", SetFilePointer(hFile, 0, NULL, FILE_CURRENT));*/
+ ok(SetFilePointer(hFile, 0, NULL, FILE_CURRENT) == (PATTERN_OFFSET + sizeof(pattern)),
+ "expected file offset %d\n", PATTERN_OFFSET + sizeof(pattern));
+
+ S(U(ov)).Offset = sizeof(buf) * 2;
+ S(U(ov)).OffsetHigh = 0;
+ ret = WriteFile(hFile, pattern, sizeof(pattern), &done, &ov);
+ ok( ret, "WriteFile error %ld\n", GetLastError());
+ ok(done == sizeof(pattern), "expected number of bytes written %lu\n", done);
+ /*trace("Current offset = %04lx\n", SetFilePointer(hFile, 0, NULL, FILE_CURRENT));*/
+ ok(SetFilePointer(hFile, 0, NULL, FILE_CURRENT) == (sizeof(buf) * 2 + sizeof(pattern)),
+ "expected file offset %d\n", sizeof(buf) * 2 + sizeof(pattern));
+ }
+
+ CloseHandle(hFile);
+
+ /*** Read File *****************************************************/
+
+ hFile = CreateFileA(temp_fname, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
+ ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA error %ld\n", GetLastError());
+
+ memset(buf, 0, sizeof(buf));
+ memset(&ov, 0, sizeof(ov));
+ S(U(ov)).Offset = PATTERN_OFFSET;
+ S(U(ov)).OffsetHigh = 0;
+ rc=ReadFile(hFile, buf, sizeof(pattern), &done, &ov);
+ /* Win 9x does not support the overlapped I/O on files */
+ if (rc || GetLastError()!=ERROR_INVALID_PARAMETER) {
+ ok(rc, "ReadFile error %ld\n", GetLastError());
+ ok(done == sizeof(pattern), "expected number of bytes read %lu\n", done);
+ /*trace("Current offset = %04lx\n", SetFilePointer(hFile, 0, NULL, FILE_CURRENT));*/
+ ok(SetFilePointer(hFile, 0, NULL, FILE_CURRENT) == (PATTERN_OFFSET + sizeof(pattern)),
+ "expected file offset %d\n", PATTERN_OFFSET + sizeof(pattern));
+ ok(!memcmp(buf, pattern, sizeof(pattern)), "pattern match failed\n");
+ }
+
+ CloseHandle(hFile);
+
+ ret = DeleteFileA(temp_fname);
+ ok( ret, "DeleteFileA error %ld\n", GetLastError());
+}
+
+static void test_LockFile(void)
+{
+ HANDLE handle;
+ DWORD written;
+ OVERLAPPED overlapped;
+ int limited_LockFile;
+ int limited_UnLockFile;
+ int lockfileex_capable;
+
+ handle = CreateFileA( filename, GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, 0, 0 );
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+ ok( WriteFile( handle, sillytext, strlen(sillytext), &written, NULL ), "write failed\n" );
+
+ ok( LockFile( handle, 0, 0, 0, 0 ), "LockFile failed\n" );
+ ok( UnlockFile( handle, 0, 0, 0, 0 ), "UnlockFile failed\n" );
+
+ limited_UnLockFile = 0;
+ if (UnlockFile( handle, 0, 0, 0, 0 ))
+ {
+ limited_UnLockFile = 1;
+ }
+
+ ok( LockFile( handle, 10, 0, 20, 0 ), "LockFile 10,20 failed\n" );
+ /* overlapping locks must fail */
+ ok( !LockFile( handle, 12, 0, 10, 0 ), "LockFile 12,10 succeeded\n" );
+ ok( !LockFile( handle, 5, 0, 6, 0 ), "LockFile 5,6 succeeded\n" );
+ /* non-overlapping locks must succeed */
+ ok( LockFile( handle, 5, 0, 5, 0 ), "LockFile 5,5 failed\n" );
+
+ ok( !UnlockFile( handle, 10, 0, 10, 0 ), "UnlockFile 10,10 succeeded\n" );
+ ok( UnlockFile( handle, 10, 0, 20, 0 ), "UnlockFile 10,20 failed\n" );
+ ok( !UnlockFile( handle, 10, 0, 20, 0 ), "UnlockFile 10,20 again succeeded\n" );
+ ok( UnlockFile( handle, 5, 0, 5, 0 ), "UnlockFile 5,5 failed\n" );
+
+ S(U(overlapped)).Offset = 100;
+ S(U(overlapped)).OffsetHigh = 0;
+ overlapped.hEvent = 0;
+
+ lockfileex_capable = dll_capable("kernel32", "LockFileEx");
+ if (lockfileex_capable)
+ {
+ /* Test for broken LockFileEx a la Windows 95 OSR2. */
+ if (LockFileEx( handle, 0, 0, 100, 0, &overlapped ))
+ {
+ /* LockFileEx is probably OK, test it more. */
+ ok( LockFileEx( handle, 0, 0, 100, 0, &overlapped ),
+ "LockFileEx 100,100 failed\n" );
+ }
+ }
+
+ /* overlapping shared locks are OK */
+ S(U(overlapped)).Offset = 150;
+ limited_UnLockFile || ok( LockFileEx( handle, 0, 0, 100, 0, &overlapped ), "LockFileEx 150,100 failed\n" );
+
+ /* but exclusive is not */
+ if (lockfileex_capable)
+ {
+ ok( !LockFileEx( handle, LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY,
+ 0, 50, 0, &overlapped ),
+ "LockFileEx exclusive 150,50 succeeded\n" );
+ if (dll_capable("kernel32.dll", "UnlockFileEx"))
+ {
+ if (!UnlockFileEx( handle, 0, 100, 0, &overlapped ))
+ { /* UnLockFile is capable. */
+ S(U(overlapped)).Offset = 100;
+ ok( !UnlockFileEx( handle, 0, 100, 0, &overlapped ),
+ "UnlockFileEx 150,100 again succeeded\n" );
+ }
+ }
+ }
+
+ ok( LockFile( handle, 0, 0x10000000, 0, 0xf0000000 ), "LockFile failed\n" );
+ ok( !LockFile( handle, ~0, ~0, 1, 0 ), "LockFile ~0,1 succeeded\n" );
+ ok( !LockFile( handle, 0, 0x20000000, 20, 0 ), "LockFile 0x20000000,20 succeeded\n" );
+ ok( UnlockFile( handle, 0, 0x10000000, 0, 0xf0000000 ), "UnlockFile failed\n" );
+
+ /* wrap-around lock should not do anything */
+ /* (but still succeeds on NT4 so we don't check result) */
+ LockFile( handle, 0, 0x10000000, 0, 0xf0000001 );
+
+ limited_LockFile = 0;
+ if (!LockFile( handle, ~0, ~0, 1, 0 ))
+ {
+ limited_LockFile = 1;
+ }
+
+ limited_UnLockFile || ok( UnlockFile( handle, ~0, ~0, 1, 0 ), "Unlockfile ~0,1 failed\n" );
+
+ /* zero-byte lock */
+ ok( LockFile( handle, 100, 0, 0, 0 ), "LockFile 100,0 failed\n" );
+ limited_LockFile || ok( !LockFile( handle, 98, 0, 4, 0 ), "LockFile 98,4 succeeded\n" );
+ ok( LockFile( handle, 90, 0, 10, 0 ), "LockFile 90,10 failed\n" );
+ limited_LockFile || ok( !LockFile( handle, 100, 0, 10, 0 ), "LockFile 100,10 failed\n" );
+
+ ok( UnlockFile( handle, 90, 0, 10, 0 ), "UnlockFile 90,10 failed\n" );
+ !ok( UnlockFile( handle, 100, 0, 10, 0 ), "UnlockFile 100,10 failed\n" );
+
+ ok( UnlockFile( handle, 100, 0, 0, 0 ), "UnlockFile 100,0 failed\n" );
+
+ CloseHandle( handle );
+ DeleteFileA( filename );
+}
+
+static inline int is_sharing_compatible( DWORD access1, DWORD sharing1, DWORD access2, DWORD sharing2 )
+{
+ if (!access1 || !access2) return 1;
+ if ((access1 & GENERIC_READ) && !(sharing2 & FILE_SHARE_READ)) return 0;
+ if ((access1 & GENERIC_WRITE) && !(sharing2 & FILE_SHARE_WRITE)) return 0;
+ if ((access1 & DELETE) && !(sharing2 & FILE_SHARE_DELETE)) return 0;
+ if ((access2 & GENERIC_READ) && !(sharing1 & FILE_SHARE_READ)) return 0;
+ if ((access2 & GENERIC_WRITE) && !(sharing1 & FILE_SHARE_WRITE)) return 0;
+ if ((access2 & DELETE) && !(sharing1 & FILE_SHARE_DELETE)) return 0;
+ return 1;
+}
+
+static void test_file_sharing(void)
+{
+ static const DWORD access_modes[] =
+ { 0, GENERIC_READ, GENERIC_WRITE, GENERIC_READ|GENERIC_WRITE,
+ DELETE, GENERIC_READ|DELETE, GENERIC_WRITE|DELETE, GENERIC_READ|GENERIC_WRITE|DELETE };
+ static const DWORD sharing_modes[] =
+ { 0, FILE_SHARE_READ,
+ FILE_SHARE_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_SHARE_DELETE, FILE_SHARE_READ|FILE_SHARE_DELETE,
+ FILE_SHARE_WRITE|FILE_SHARE_DELETE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE };
+ int a1, s1, a2, s2;
+ int ret;
+ HANDLE h, h2;
+
+ /* make sure the file exists */
+ h = CreateFileA( filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ ok(0, "couldn't create file \"%s\" (err=%ld)\n", filename, GetLastError());
+ return;
+ }
+ CloseHandle( h );
+
+ for (a1 = 0; a1 < sizeof(access_modes)/sizeof(access_modes[0]); a1++)
+ {
+ for (s1 = 0; s1 < sizeof(sharing_modes)/sizeof(sharing_modes[0]); s1++)
+ {
+ h = CreateFileA( filename, access_modes[a1], sharing_modes[s1],
+ NULL, OPEN_EXISTING, 0, 0 );
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+ return;
+ }
+ for (a2 = 0; a2 < sizeof(access_modes)/sizeof(access_modes[0]); a2++)
+ {
+ for (s2 = 0; s2 < sizeof(sharing_modes)/sizeof(sharing_modes[0]); s2++)
+ {
+ SetLastError(0xdeadbeef);
+ h2 = CreateFileA( filename, access_modes[a2], sharing_modes[s2],
+ NULL, OPEN_EXISTING, 0, 0 );
+ if (is_sharing_compatible( access_modes[a1], sharing_modes[s1],
+ access_modes[a2], sharing_modes[s2] ))
+ {
+ ret = GetLastError();
+ ok( ERROR_SHARING_VIOLATION == ret || 0 == ret,
+ "Windows 95 sets GetLastError() = ERROR_SHARING_VIOLATION and\n"
+ " Windows XP GetLastError() = 0, but now it is %d.\n"
+ " indexes = %d, %d, %d, %d\n"
+ " modes =\n %lx/%lx/%lx/%lx\n",
+ ret,
+ a1, s1, a2, s2,
+ access_modes[a1], sharing_modes[s1],
+ access_modes[a2], sharing_modes[s2]
+ );
+ }
+ else
+ {
+ ok( h2 == INVALID_HANDLE_VALUE,
+ "open succeeded for modes %lx/%lx/%lx/%lx\n",
+ access_modes[a1], sharing_modes[s1],
+ access_modes[a2], sharing_modes[s2] );
+ if (h2 == INVALID_HANDLE_VALUE)
+ ok( GetLastError() == ERROR_SHARING_VIOLATION,
+ "wrong error code %ld\n", GetLastError() );
+ }
+ if (h2 != INVALID_HANDLE_VALUE) CloseHandle( h2 );
+ }
+ }
+ CloseHandle( h );
+ }
+ }
+
+ SetLastError(0xdeadbeef);
+ h = CreateFileA( filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, 0 );
+ ok( h != INVALID_HANDLE_VALUE, "CreateFileA error %ld\n", GetLastError() );
+
+ SetLastError(0xdeadbeef);
+ h2 = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
+ ok( h2 == INVALID_HANDLE_VALUE, "CreateFileA should fail\n");
+ ok( GetLastError() == ERROR_SHARING_VIOLATION, "wrong error code %ld\n", GetLastError() );
+
+ h2 = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
+ ok( h2 != INVALID_HANDLE_VALUE, "CreateFileA error %ld\n", GetLastError() );
+
+ CloseHandle(h);
+ CloseHandle(h2);
+
+ DeleteFileA( filename );
+}
+
+static char get_windows_drive(void)
+{
+ char windowsdir[MAX_PATH];
+ GetWindowsDirectory(windowsdir, sizeof(windowsdir));
+ return windowsdir[0];
+}
+
+static void test_FindFirstFileA(void)
+{
+ HANDLE handle;
+ WIN32_FIND_DATAA search_results;
+ int err;
+ char buffer[5] = "C:\\";
+
+ /* try FindFirstFileA on "C:\" */
+ buffer[0] = get_windows_drive();
+ handle = FindFirstFileA(buffer,&search_results);
+ err = GetLastError();
+ ok ( handle == INVALID_HANDLE_VALUE , "FindFirstFile on root directory should Fail\n");
+ if (handle == INVALID_HANDLE_VALUE)
+ ok ( err == ERROR_FILE_NOT_FOUND, "Bad Error number %d\n", err);
+
+ /* try FindFirstFileA on "C:\*" */
+ strcat(buffer, "*");
+ handle = FindFirstFileA(buffer,&search_results);
+ ok ( handle != INVALID_HANDLE_VALUE, "FindFirstFile on %s should succeed\n", buffer );
+ ok ( FindClose(handle) == TRUE, "Failed to close handle\n");
+}
+
+static void test_FindNextFileA(void)
+{
+ HANDLE handle;
+ WIN32_FIND_DATAA search_results;
+ int err;
+ char buffer[5] = "C:\\*";
+
+ buffer[0] = get_windows_drive();
+ handle = FindFirstFileA(buffer,&search_results);
+ ok ( handle != INVALID_HANDLE_VALUE, "FindFirstFile on C:\\* should succeed\n" );
+ while (FindNextFile(handle, &search_results))
+ {
+ /* get to the end of the files */
+ }
+ ok ( FindClose(handle) == TRUE, "Failed to close handle\n");
+ err = GetLastError();
+ ok ( err == ERROR_NO_MORE_FILES, "GetLastError should return ERROR_NO_MORE_FILES\n");
+}
+
+static int test_Mapfile_createtemp(HANDLE *handle)
+{
+ SetFileAttributesA(filename,FILE_ATTRIBUTE_NORMAL);
+ DeleteFile(filename);
+ *handle = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, 0,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (*handle != INVALID_HANDLE_VALUE) {
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void test_MapFile(void)
+{
+ HANDLE handle;
+ HANDLE hmap;
+
+ ok(test_Mapfile_createtemp(&handle), "Couldn't create test file.\n");
+
+ hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0, 0x1000, "named_file_map" );
+ ok( hmap != NULL, "mapping should work, I named it!\n" );
+
+ ok( CloseHandle( hmap ), "can't close mapping handle\n");
+
+ /* We have to close file before we try new stuff with mapping again.
+ Else we would always succeed on XP or block descriptors on 95. */
+ hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0, 0, NULL );
+ ok( hmap != NULL, "We should still be able to map!\n" );
+ ok( CloseHandle( hmap ), "can't close mapping handle\n");
+ ok( CloseHandle( handle ), "can't close file handle\n");
+ handle = NULL;
+
+ ok(test_Mapfile_createtemp(&handle), "Couldn't create test file.\n");
+
+ hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0, 0, NULL );
+ ok( hmap == NULL, "mapped zero size file\n");
+ ok( GetLastError() == ERROR_FILE_INVALID, "not ERROR_FILE_INVALID\n");
+
+ hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0x1000, 0, NULL );
+ ok( hmap == NULL, "mapping should fail\n");
+ /* GetLastError() varies between win9x and WinNT and also depends on the filesystem */
+
+ hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0x1000, 0x10000, NULL );
+ ok( hmap == NULL, "mapping should fail\n");
+ /* GetLastError() varies between win9x and WinNT and also depends on the filesystem */
+
+ /* On XP you can now map again, on Win 95 you cannot. */
+
+ ok( CloseHandle( handle ), "can't close file handle\n");
+ ok( DeleteFileA( filename ), "DeleteFile failed after map\n" );
+}
+
+static void test_GetFileType(void)
+{
+ DWORD type;
+ HANDLE h = CreateFileA( filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( h != INVALID_HANDLE_VALUE, "open %s failed\n", filename );
+ type = GetFileType(h);
+ ok( type == FILE_TYPE_DISK, "expected type disk got %ld\n", type );
+ CloseHandle( h );
+ h = CreateFileA( "nul", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok( h != INVALID_HANDLE_VALUE, "open nul failed\n" );
+ type = GetFileType(h);
+ ok( type == FILE_TYPE_CHAR, "expected type char for nul got %ld\n", type );
+ CloseHandle( h );
+ DeleteFileA( filename );
+}
+
+static int completion_count;
+
+static void CALLBACK FileIOComplete(DWORD dwError, DWORD dwBytes, LPOVERLAPPED ovl)
+{
+/* printf("(%ld, %ld, %p { %ld, %ld, %ld, %ld, %p })\n", dwError, dwBytes, ovl, ovl->Internal, ovl->InternalHigh, ovl->Offset, ovl->OffsetHigh, ovl->hEvent);*/
+ ReleaseSemaphore(ovl->hEvent, 1, NULL);
+ completion_count++;
+}
+
+static void test_async_file_errors(void)
+{
+ char szFile[MAX_PATH];
+ HANDLE hSem = CreateSemaphoreW(NULL, 1, 1, NULL);
+ HANDLE hFile;
+ LPVOID lpBuffer = HeapAlloc(GetProcessHeap(), 0, 4096);
+ OVERLAPPED ovl;
+ S(U(ovl)).Offset = 0;
+ S(U(ovl)).OffsetHigh = 0;
+ ovl.hEvent = hSem;
+ completion_count = 0;
+ szFile[0] = '\0';
+ GetWindowsDirectoryA(szFile, sizeof(szFile)/sizeof(szFile[0])-1-strlen("\\win.ini"));
+ strcat(szFile, "\\win.ini");
+ hFile = CreateFileA(szFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
+ ok(hFile != NULL, "CreateFileA(%s ...) failed\n", szFile);
+ while (TRUE)
+ {
+ BOOL res;
+ while (WaitForSingleObjectEx(hSem, INFINITE, TRUE) == WAIT_IO_COMPLETION)
+ ;
+ res = ReadFileEx(hFile, lpBuffer, 4096, &ovl, FileIOComplete);
+ /*printf("Offset = %ld, result = %s\n", ovl.Offset, res ? "TRUE" : "FALSE");*/
+ if (!res)
+ break;
+ S(U(ovl)).Offset += 4096;
+ /* i/o completion routine only called if ReadFileEx returned success.
+ * we only care about violations of this rule so undo what should have
+ * been done */
+ completion_count--;
+ }
+ ok(completion_count == 0, "completion routine should only be called when ReadFileEx succeeds (this rule was violated %d times)\n", completion_count);
+ /*printf("Error = %ld\n", GetLastError());*/
+}
+
+static void test_read_write(void)
+{
+ DWORD bytes, ret;
+ HANDLE hFile;
+ char temp_path[MAX_PATH];
+ char filename[MAX_PATH];
+ static const char prefix[] = "pfx";
+
+ ret = GetTempPathA(MAX_PATH, temp_path);
+ ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+ ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+ ret = GetTempFileNameA(temp_path, prefix, 0, filename);
+ ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+ hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_FLAG_RANDOM_ACCESS, 0);
+ ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA: error %ld\n", GetLastError());
+
+ SetLastError(12345678);
+ bytes = 12345678;
+ ret = WriteFile(hFile, NULL, 0, &bytes, NULL);
+ ok(ret && GetLastError() == 12345678,
+ "ret = %ld, error %ld\n", ret, GetLastError());
+ ok(!bytes, "bytes = %ld\n", bytes);
+
+ SetLastError(12345678);
+ bytes = 12345678;
+ ret = WriteFile(hFile, NULL, 10, &bytes, NULL);
+ ok((!ret && GetLastError() == ERROR_INVALID_USER_BUFFER) || /* Win2k */
+ (ret && GetLastError() == 12345678), /* Win9x */
+ "ret = %ld, error %ld\n", ret, GetLastError());
+ ok(!bytes || /* Win2k */
+ bytes == 10, /* Win9x */
+ "bytes = %ld\n", bytes);
+
+ /* make sure the file contains data */
+ WriteFile(hFile, "this is the test data", 21, &bytes, NULL);
+ SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
+
+ SetLastError(12345678);
+ bytes = 12345678;
+ ret = ReadFile(hFile, NULL, 0, &bytes, NULL);
+ ok(ret && GetLastError() == 12345678,
+ "ret = %ld, error %ld\n", ret, GetLastError());
+ ok(!bytes, "bytes = %ld\n", bytes);
+
+ SetLastError(12345678);
+ bytes = 12345678;
+ ret = ReadFile(hFile, NULL, 10, &bytes, NULL);
+ ok(!ret && (GetLastError() == ERROR_NOACCESS || /* Win2k */
+ GetLastError() == ERROR_INVALID_PARAMETER), /* Win9x */
+ "ret = %ld, error %ld\n", ret, GetLastError());
+ ok(!bytes, "bytes = %ld\n", bytes);
+
+ ret = CloseHandle(hFile);
+ ok( ret, "CloseHandle: error %ld\n", GetLastError());
+ ret = DeleteFileA(filename);
+ ok( ret, "DeleteFileA: error %ld\n", GetLastError());
+}
+
+static void test_OpenFile_exists(void)
+{
+ HFILE hFile;
+ OFSTRUCT ofs;
+
+ static const char *file = "\\winver.exe";
+ char buff[MAX_PATH];
+ UINT length;
+
+ length = GetSystemDirectoryA(buff, MAX_PATH);
+
+ if ((length + lstrlen(file) < MAX_PATH))
+ {
+ lstrcat(buff, file);
+
+ hFile = OpenFile(buff, &ofs, OF_EXIST);
+ ok( hFile == TRUE, "%s not found : %ld\n", buff, GetLastError());
+ }
+
+ hFile = OpenFile(".\\foo-bar-foo.baz", &ofs, OF_EXIST);
+ ok( hFile == HFILE_ERROR, "hFile != HFILE_ERROR : %ld\n", GetLastError());
+}
+
+static void test_overlapped(void)
+{
+ OVERLAPPED ov;
+ DWORD r, result;
+
+ /* GetOverlappedResult crashes if the 2nd or 3rd param are NULL */
+
+ memset( &ov, 0, sizeof ov );
+ result = 1;
+ r = GetOverlappedResult(0, &ov, &result, 0);
+ ok( r == TRUE, "should return false\n");
+ ok( result == 0, "result wrong\n");
+
+ result = 0;
+ ov.Internal = 0;
+ ov.InternalHigh = 0xabcd;
+ r = GetOverlappedResult(0, &ov, &result, 0);
+ ok( r == TRUE, "should return false\n");
+ ok( result == 0xabcd, "result wrong\n");
+
+ SetLastError( 0xb00 );
+ result = 0;
+ ov.Internal = STATUS_INVALID_HANDLE;
+ ov.InternalHigh = 0xabcd;
+ r = GetOverlappedResult(0, &ov, &result, 0);
+ ok (GetLastError() == ERROR_INVALID_HANDLE, "error wrong\n");
+ ok( r == FALSE, "should return false\n");
+ ok( result == 0xabcd, "result wrong\n");
+
+ result = 0;
+ ov.Internal = STATUS_PENDING;
+ ov.InternalHigh = 0xabcd;
+ r = GetOverlappedResult(0, &ov, &result, 0);
+ todo_wine {
+ ok (GetLastError() == ERROR_IO_INCOMPLETE, "error wrong\n");
+ }
+ ok( r == FALSE, "should return false\n");
+ ok( result == 0, "result wrong\n");
+
+ ov.hEvent = CreateEvent( NULL, 1, 1, NULL );
+ ov.Internal = STATUS_PENDING;
+ ov.InternalHigh = 0xabcd;
+ r = GetOverlappedResult(0, &ov, &result, 0);
+ ok (GetLastError() == ERROR_IO_INCOMPLETE, "error wrong\n");
+ ok( r == FALSE, "should return false\n");
+
+ ResetEvent( ov.hEvent );
+
+ ov.Internal = STATUS_PENDING;
+ ov.InternalHigh = 0;
+ r = GetOverlappedResult(0, &ov, &result, 0);
+ ok (GetLastError() == ERROR_IO_INCOMPLETE, "error wrong\n");
+ ok( r == FALSE, "should return false\n");
+
+ r = CloseHandle( ov.hEvent );
+ ok( r == TRUE, "close handle failed\n");
+}
+
+START_TEST(file)
+{
+ test__hread( );
+ test__hwrite( );
+ test__lclose( );
+ test__lcreat( );
+ test__llseek( );
+ test__llopen( );
+ test__lread( );
+ test__lwrite( );
+ test_GetTempFileNameA();
+ test_CopyFileA();
+ test_CopyFileW();
+ test_CreateFileA();
+ test_CreateFileW();
+ test_DeleteFileA();
+ test_DeleteFileW();
+ test_MoveFileA();
+ test_MoveFileW();
+ test_FindFirstFileA();
+ test_FindNextFileA();
+ test_LockFile();
+ test_file_sharing();
+ test_offset_in_overlapped_structure();
+ test_MapFile();
+ test_GetFileType();
+ test_async_file_errors();
+ test_read_write();
+ test_OpenFile_exists();
+ test_overlapped();
+}
--- /dev/null
+/* Unit test suite for FormatMessageA
+ *
+ * Copyright 2002 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+
+/* #define ok(cond,failstr) if(!(cond)) {printf("line %d : %s\n",__LINE__,failstr);exit(1);} */
+
+static DWORD doit(DWORD flags, LPCVOID src, DWORD msg_id, DWORD lang_id,
+ LPSTR out, DWORD outsize, ... )
+{
+ va_list list;
+ DWORD r;
+
+ va_start(list, outsize);
+ r = FormatMessageA(flags, src, msg_id,
+ lang_id, out, outsize, &list);
+ va_end(list);
+ return r;
+}
+
+static void test_message_from_string(void)
+{
+ CHAR out[0x100] = {0};
+ DWORD r;
+ static const WCHAR szwTest[] = { 't','e','s','t',0};
+
+ /* the basics */
+ r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "test", 0,
+ 0, out, sizeof(out)/sizeof(CHAR),NULL);
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* using the format feature */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!s!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), "test");
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* no format */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), "test");
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* two pieces */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1%2", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), "te","st");
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* three pieces */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1%3%2%1", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), "t","s","e");
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* s doesn't seem to work in format strings */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%!s!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), "test");
+ ok(!strcmp("!s!", out),"failed out=[%s]\n",out);
+ ok(r==3,"failed: r=%ld\n",r);
+
+ /* S is unicode */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!S!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), szwTest);
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* as characters */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!c!%2!c!%3!c!%1!c!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 't','e','s');
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* some numbers */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!d!%2!d!%3!d!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 1,2,3);
+ ok(!strcmp("123", out),"failed out=[%s]\n",out);
+ ok(r==3,"failed: r=%ld\n",r);
+
+ /* a single digit with some spacing */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4d!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 1);
+ ok(!strcmp(" 1", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* a single digit, left justified */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!-4d!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 1);
+ ok(!strcmp("1 ", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* two digit decimal number */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4d!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 11);
+ ok(!strcmp(" 11", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* a hex number */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4x!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 11);
+ ok(!strcmp(" b", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* a hex number, upper case */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4X!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 11);
+ ok(!strcmp(" B", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* a hex number, upper case, left justified */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!-4X!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 11);
+ ok(!strcmp("B ", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* a long hex number, upper case */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4X!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 0x1ab);
+ ok(!strcmp(" 1AB", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* two percent... */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, " %%%% ", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp(" %% ", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* periods are special cases */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, " %.%. %1!d!", 0,
+ 0, out, sizeof(out)/sizeof(CHAR), 0x1ab);
+ ok(!strcmp(" .. 427", out),"failed out=[%s]\n",out);
+ ok(r==7,"failed: r=%ld\n",r);
+
+ /* %0 ends the line */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "test%0test", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("test", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* %! prints an exclaimation */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "yah%!%0 ", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("yah!", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* %space */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "% % ", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp(" ", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "hi\n", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("hi\r\n", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* carriage return line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "hi\r\n", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("hi\r\n", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* carriage return line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "\r", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("\r\n", out),"failed out=[%s]\n",out);
+ ok(r==2,"failed: r=%ld\n",r);
+
+ /* carriage return line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING, "\r\r\n", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("\r\n\r\n", out),"failed out=[%s]\n",out);
+ ok(r==4,"failed: r=%ld\n",r);
+
+ /* change of pace... test the low byte of dwflags */
+ /* line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\n", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("hi ", out) || !strcmp("hi\r\n", out),"failed out=[%s]\n",out);
+ ok(r==3 || r==4,"failed: r=%ld\n",r);
+
+ /* carriage return line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\r\n", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp("hi ", out),"failed out=[%s]\n",out);
+ ok(r==3,"failed: r=%ld\n",r);
+
+ /* carriage return line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "\r", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp(" ", out),"failed out=[%s]\n",out);
+ ok(r==1,"failed: r=%ld\n",r);
+
+ /* carriage return line feed */
+ r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "\r\r\n", 0,
+ 0, out, sizeof(out)/sizeof(CHAR));
+ ok(!strcmp(" ", out),"failed out=[%s]\n",out);
+ ok(r==2,"failed: r=%ld\n",r);
+}
+
+START_TEST(format_msg)
+{
+ test_message_from_string();
+}
--- /dev/null
+/*
+ * Unit test suite for heap functions
+ *
+ * Copyright 2003 Dimitrie O. Paun
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/test.h"
+
+static SIZE_T resize_9x(SIZE_T size)
+{
+ DWORD dwSizeAligned = (size + 3) & ~3;
+ return max(dwSizeAligned, 12); /* at least 12 bytes */
+}
+
+START_TEST(heap)
+{
+ void *mem;
+ HGLOBAL gbl;
+ SIZE_T size;
+
+ /* Heap*() functions */
+ mem = HeapAlloc(GetProcessHeap(), 0, 0);
+ ok(mem != NULL, "memory not allocated for size 0\n");
+
+ mem = HeapReAlloc(GetProcessHeap(), 0, NULL, 10);
+ ok(mem == NULL, "memory allocated by HeapReAlloc\n");
+
+ for (size = 0; size <= 256; size++)
+ {
+ SIZE_T heap_size;
+ mem = HeapAlloc(GetProcessHeap(), 0, size);
+ heap_size = HeapSize(GetProcessHeap(), 0, mem);
+ ok(heap_size == size || heap_size == resize_9x(size),
+ "HeapSize returned %lu instead of %lu or %lu\n", heap_size, size, resize_9x(size));
+ HeapFree(GetProcessHeap(), 0, mem);
+ }
+
+ /* Global*() functions */
+ gbl = GlobalAlloc(GMEM_MOVEABLE, 0);
+ ok(gbl != NULL, "global memory not allocated for size 0\n");
+
+ gbl = GlobalReAlloc(gbl, 10, GMEM_MOVEABLE);
+ ok(gbl != NULL, "Can't realloc global memory\n");
+ size = GlobalSize(gbl);
+ ok(size >= 10 && size <= 16, "Memory not resized to size 10, instead size=%ld\n", size);
+
+ gbl = GlobalReAlloc(gbl, 0, GMEM_MOVEABLE);
+ ok(gbl != NULL, "GlobalReAlloc should not fail on size 0\n");
+
+ size = GlobalSize(gbl);
+ ok(size == 0, "Memory not resized to size 0, instead size=%ld\n", size);
+ ok(GlobalFree(gbl) == NULL, "Memory not freed\n");
+ size = GlobalSize(gbl);
+ ok(size == 0, "Memory should have been freed, size=%ld\n", size);
+
+ gbl = GlobalReAlloc(0, 10, GMEM_MOVEABLE);
+ ok(gbl == NULL, "global realloc allocated memory\n");
+
+ /* Local*() functions */
+ gbl = LocalAlloc(LMEM_MOVEABLE, 0);
+ ok(gbl != NULL, "local memory not allocated for size 0\n");
+
+ gbl = LocalReAlloc(gbl, 10, LMEM_MOVEABLE);
+ ok(gbl != NULL, "Can't realloc local memory\n");
+ size = LocalSize(gbl);
+ ok(size >= 10 && size <= 16, "Memory not resized to size 10, instead size=%ld\n", size);
+
+ gbl = LocalReAlloc(gbl, 0, LMEM_MOVEABLE);
+ ok(gbl != NULL, "LocalReAlloc should not fail on size 0\n");
+
+ size = LocalSize(gbl);
+ ok(size == 0, "Memory not resized to size 0, instead size=%ld\n", size);
+ ok(LocalFree(gbl) == NULL, "Memory not freed\n");
+ size = LocalSize(gbl);
+ ok(size == 0, "Memory should have been freed, size=%ld\n", size);
+
+ gbl = LocalReAlloc(0, 10, LMEM_MOVEABLE);
+ ok(gbl == NULL, "local realloc allocated memory\n");
+
+ /* trying to lock empty memory should give an error */
+ gbl = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,0);
+ ok(gbl != NULL, "returned NULL\n");
+ SetLastError(0xdeadbeef);
+ mem = GlobalLock(gbl);
+ ok( GetLastError() == ERROR_DISCARDED, "should return an error\n");
+ ok( mem == NULL, "should return NULL\n");
+ GlobalFree(gbl);
+}
--- /dev/null
+/*
+ * Unit test suite for interlocked functions.
+ *
+ * Copyright 2006 Hervé Poussineau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+static void test_InterlockedCompareExchange(void)
+{
+ LONG dest, res;
+
+ dest = 0;
+ res = InterlockedCompareExchange( &dest, 1, 0 );
+ ok( res == 0 && dest == 1,
+ "Expected 0 and 1, got %ld and %ld", res, dest );
+
+ dest = 1;
+ res = InterlockedCompareExchange( &dest, 2, 0 );
+ ok( res == 1 && dest == 1,
+ "Expected 1 and 1, got %ld and %ld", res, dest );
+}
+
+static void test_InterlockedDecrement(void)
+{
+ LONG dest, res;
+
+ dest = 1;
+ res = InterlockedDecrement( &dest );
+ ok( res == 0 && dest == 0,
+ "Expected 0 and 0, got %ld and %ld", res, dest );
+
+ dest = 0;
+ res = InterlockedDecrement( &dest );
+ ok( res == -1 && dest == -1,
+ "Expected -1 and -1, got %ld and %ld", res, dest );
+
+ dest = -1;
+ res = InterlockedDecrement( &dest );
+ ok( res == -2 && dest == -2,
+ "Expected -2 and -2, got %ld and %ld", res, dest );
+}
+
+static void test_InterlockedExchange(void)
+{
+ LONG dest, res;
+
+ dest = 0;
+ res = InterlockedExchange( &dest, 1 );
+ ok( res == 0 && dest == 1,
+ "Expected 0 and 1, got %ld and %ld", res, dest );
+
+ dest = 1;
+ res = InterlockedExchange( &dest, 2 );
+ ok( res == 1 && dest == 2,
+ "Expected 1 and 2, got %ld and %ld", res, dest );
+
+ dest = 1;
+ res = InterlockedExchange( &dest, -1 );
+ ok( res == 1 && dest == -1,
+ "Expected 1 and -1, got %ld and %ld", res, dest );
+}
+
+static void test_InterlockedExchangeAdd(void)
+{
+ LONG dest, res;
+
+ dest = 0;
+ res = InterlockedExchangeAdd( &dest, 1 );
+ ok( res == 0 && dest == 1,
+ "Expected 0 and 1, got %ld and %ld", res, dest );
+
+ dest = 1;
+ res = InterlockedExchangeAdd( &dest, 2 );
+ ok( res == 1 && dest == 3,
+ "Expected 1 and 3, got %ld and %ld", res, dest );
+
+ dest = 1;
+ res = InterlockedExchangeAdd( &dest, -1 );
+ ok( res == 1 && dest == 0,
+ "Expected 1 and 0, got %ld and %ld", res, dest );
+}
+
+static void test_InterlockedIncrement(void)
+{
+ LONG dest, res;
+
+ dest = -2;
+ res = InterlockedIncrement( &dest );
+ ok( res == -1 && dest == -1,
+ "Expected -1 and -1, got %ld and %ld", res, dest );
+
+ dest = -1;
+ res = InterlockedIncrement( &dest );
+ ok( res == 0 && dest == 0,
+ "Expected 0 and 0, got %ld and %ld", res, dest );
+
+ dest = 0;
+ res = InterlockedIncrement( &dest );
+ ok( res == 1 && dest == 1,
+ "Expected 1 and 1, got %ld and %ld", res, dest );
+}
+
+START_TEST(interlck)
+{
+ test_InterlockedCompareExchange();
+ test_InterlockedDecrement();
+ test_InterlockedExchange();
+ test_InterlockedExchangeAdd();
+ test_InterlockedIncrement();
+}
--- /dev/null
+<module name="kernel32_winetest" type="win32cui" installbase="bin" installname="kernel32_winetest.exe" allowwarnings="true">
+ <include base="kernel32_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>ntdll</library>
+ <library>kernel32</library>
+ <file>alloc.c</file>
+ <file>atom.c</file>
+ <file>change.c</file>
+ <file>codepage.c</file>
+ <file>comm.c</file>
+ <file>console.c</file>
+ <file>directory.c</file>
+ <file>drive.c</file>
+ <file>environ.c</file>
+ <file>file.c</file>
+ <file>format_msg.c</file>
+ <file>heap.c</file>
+ <file>interlck.c</file>
+ <file>locale.c</file>
+ <file>mailslot.c</file>
+ <file>module.c</file>
+ <file>path.c</file>
+ <file>pipe.c</file>
+ <file>process.c</file>
+ <file>profile.c</file>
+ <file>sync.c</file>
+ <file>thread.c</file>
+ <file>time.c</file>
+ <file>timer.c</file>
+ <file>virtual.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * Unit tests for locale functions
+ *
+ * Copyright 2002 YASAR Mehmet
+ * Copyright 2003 Dmitry Timoshkov
+ * Copyright 2003 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NOTES
+ * We must pass LOCALE_NOUSEROVERRIDE (NUO) to formatting functions so that
+ * even when the user has overridden their default i8n settings (e.g. in
+ * the control panel i8n page), we will still get the expected results.
+ */
+
+#define WINVER 0x0500
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winnls.h"
+
+static inline unsigned int strlenW( const WCHAR *str )
+{
+ const WCHAR *s = str;
+ while (*s) s++;
+ return s - str;
+}
+
+static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
+{
+ if (n <= 0) return 0;
+ while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
+ return *str1 - *str2;
+}
+
+static inline WCHAR *strchrW( const WCHAR *str, WCHAR ch )
+{
+ do { if (*str == ch) return (WCHAR *)str; } while (*str++);
+ return NULL;
+}
+
+inline static int isdigitW( WCHAR wc )
+{
+ WORD type;
+ GetStringTypeW( CT_CTYPE1, &wc, 1, &type );
+ return type & C1_DIGIT;
+}
+
+/* Some functions are only in later versions of kernel32.dll */
+static HMODULE hKernel32;
+static WORD enumCount;
+
+typedef BOOL (WINAPI *EnumSystemLanguageGroupsAFn)(LANGUAGEGROUP_ENUMPROC,
+ DWORD, LONG_PTR);
+static EnumSystemLanguageGroupsAFn pEnumSystemLanguageGroupsA;
+typedef BOOL (WINAPI *EnumLanguageGroupLocalesAFn)(LANGGROUPLOCALE_ENUMPROC,
+ LGRPID, DWORD, LONG_PTR);
+static EnumLanguageGroupLocalesAFn pEnumLanguageGroupLocalesA;
+typedef BOOL (WINAPI *EnumUILanguagesAFn)(UILANGUAGE_ENUMPROC,
+ DWORD, LONG_PTR);
+static EnumUILanguagesAFn pEnumUILanguagesA;
+
+typedef INT (WINAPI *FoldStringAFn)(DWORD, LPCSTR, INT, LPSTR, INT);
+static FoldStringAFn pFoldStringA;
+typedef INT (WINAPI *FoldStringWFn)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+static FoldStringWFn pFoldStringW;
+
+typedef BOOL (WINAPI *IsValidLanguageGroupFn)(LGRPID, DWORD);
+static IsValidLanguageGroupFn pIsValidLanguageGroup;
+
+static void InitFunctionPointers(void)
+{
+ hKernel32 = GetModuleHandleA("kernel32");
+
+ if (hKernel32)
+ {
+ pEnumSystemLanguageGroupsA = (void*)GetProcAddress(hKernel32, "EnumSystemLanguageGroupsA");
+ pEnumLanguageGroupLocalesA = (void*)GetProcAddress(hKernel32, "EnumLanguageGroupLocalesA");
+ pFoldStringA = (void*)GetProcAddress(hKernel32, "FoldStringA");
+ pFoldStringW = (void*)GetProcAddress(hKernel32, "FoldStringW");
+ pIsValidLanguageGroup = (void*)GetProcAddress(hKernel32, "IsValidLanguageGroup");
+ pEnumUILanguagesA = (void*)GetProcAddress(hKernel32, "EnumUILanguagesA");
+ }
+}
+
+#define eq(received, expected, label, type) \
+ ok((received) == (expected), "%s: got " type " instead of " type "\n", \
+ (label), (received), (expected))
+
+#define BUFFER_SIZE 128
+#define COUNTOF(x) (sizeof(x)/sizeof(x)[0])
+
+#define EXPECT_LEN(len) ok(ret == (len), "Expected Len %d, got %d\n", (len), ret)
+#define EXPECT_INVALID ok(GetLastError() == ERROR_INVALID_PARAMETER, \
+ "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError())
+#define EXPECT_BUFFER ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, \
+ "Expected ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError())
+#define EXPECT_FLAGS ok(GetLastError() == ERROR_INVALID_FLAGS, \
+ "Expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError())
+#define EXPECT_INVALIDFLAGS ok(GetLastError() == ERROR_INVALID_FLAGS || \
+ GetLastError() == ERROR_INVALID_PARAMETER, \
+ "Expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError())
+#define EXPECT_VALID ok(GetLastError() == 0, \
+ "Expected GetLastError() == 0, got %ld\n", GetLastError())
+
+#define STRINGSA(x,y) strcpy(input, x); strcpy(Expected, y); SetLastError(0); buffer[0] = '\0'
+#define EXPECT_LENA EXPECT_LEN((int)strlen(Expected)+1)
+#define EXPECT_EQA ok(strncmp(buffer, Expected, strlen(Expected)) == 0, \
+ "Expected '%s', got '%s'\n", Expected, buffer)
+
+#define STRINGSW(x,y) MultiByteToWideChar(CP_ACP,0,x,-1,input,COUNTOF(input)); \
+ MultiByteToWideChar(CP_ACP,0,y,-1,Expected,COUNTOF(Expected)); \
+ SetLastError(0); buffer[0] = '\0'
+#define EXPECT_LENW EXPECT_LEN((int)strlenW(Expected)+1)
+#define EXPECT_EQW ok(strncmpW(buffer, Expected, strlenW(Expected)) == 0, "Bad conversion\n")
+#define EXPECT_FALSE ok(FALSE == ret, "Expected return value FALSE, got TRUE\n")
+#define EXPECT_TRUE ok(FALSE != ret, "Expected return value TRUE, got FALSE\n")
+
+#define NUO LOCALE_NOUSEROVERRIDE
+
+static void test_GetLocaleInfoA(void)
+{
+ int ret;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ char buffer[BUFFER_SIZE];
+
+ ok(lcid == 0x409, "wrong LCID calculated - %ld\n", lcid);
+
+ /* HTMLKit and "Font xplorer lite" expect GetLocaleInfoA to
+ * partially fill the buffer even if it is too short. See bug 637.
+ */
+ SetLastError(0); memset(buffer, 0, COUNTOF(buffer));
+ ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 0);
+ ok(ret == 7 && !buffer[0], "Expected len=7, got %d\n", ret);
+
+ SetLastError(0); memset(buffer, 0, COUNTOF(buffer));
+ ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 3);
+ EXPECT_BUFFER; EXPECT_LEN(0);
+ ok(!strcmp(buffer, "Mon"), "Expected 'Mon', got '%s'\n", buffer);
+
+ SetLastError(0); memset(buffer, 0, COUNTOF(buffer));
+ ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 10);
+ EXPECT_VALID; EXPECT_LEN(7);
+ ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer);
+}
+
+static void test_GetTimeFormatA(void)
+{
+ int ret;
+ SYSTEMTIME curtime;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
+
+ memset(&curtime, 2, sizeof(SYSTEMTIME));
+ STRINGSA("tt HH':'mm'@'ss", ""); /* Invalid time */
+ ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ curtime.wHour = 8;
+ curtime.wMinute = 56;
+ curtime.wSecond = 13;
+ curtime.wMilliseconds = 22;
+ STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
+ ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("tt HH':'mm'@'ss", "A"); /* Insufficent buffer */
+ ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
+ EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
+ ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
+ EXPECT_VALID; EXPECT_LENA;
+
+ STRINGSA("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
+ ret = GetTimeFormatA(lcid, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
+ ret = GetTimeFormatA(lcid, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
+ ret = GetTimeFormatA(lcid, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
+ strcpy(Expected, "8:56 AM");
+ ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
+ ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("s1s2s3", ""); /* Duplicate tokens */
+ ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("t/tt", "A/AM"); /* AM time marker */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ curtime.wHour = 13;
+ STRINGSA("t/tt", "P/PM"); /* PM time marker */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
+ ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
+ ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
+ ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ curtime.wHour = 14; /* change this to 14 or 2pm */
+ curtime.wMinute = 5;
+ curtime.wSecond = 3;
+ STRINGSA("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ curtime.wHour = 0;
+ STRINGSA("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ /* try to convert formatting strings with more than two letters
+ * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
+ * NOTE: We expect any letter for which there is an upper case value
+ * we should see a replacement. For letters that DO NOT have
+ * upper case values we should see NO REPLACEMENT.
+ */
+ curtime.wHour = 8;
+ curtime.wMinute = 56;
+ curtime.wSecond = 13;
+ curtime.wMilliseconds = 22;
+ STRINGSA("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
+ "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("h", "text"); /* Don't write to buffer if len is 0 */
+ strcpy(buffer, "text");
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, 0);
+ EXPECT_VALID; EXPECT_LEN(2); EXPECT_EQA;
+
+ STRINGSA("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
+ "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("'''", "'"); /* invalid quoted string */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ /* test that msdn suggested single quotation usage works as expected */
+ STRINGSA("''''", "'"); /* single quote mark */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("''HHHHHH", "08"); /* Normal use */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ /* and test for normal use of the single quotation mark */
+ STRINGSA("'''HHHHHH'", "'HHHHHH"); /* Normal use */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("'''HHHHHH", "'HHHHHH"); /* Odd use */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
+ ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ curtime.wHour = 25;
+ STRINGSA("'123'tt", ""); /* Invalid time */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ curtime.wHour = 12;
+ curtime.wMonth = 60; /* Invalid */
+ STRINGSA("h:m:s", "12:56:13"); /* Invalid date */
+ ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+}
+
+static void test_GetDateFormatA(void)
+{
+ int ret;
+ SYSTEMTIME curtime;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
+
+ memset(&curtime, 2, sizeof(SYSTEMTIME)); /* Invalid time */
+ STRINGSA("ddd',' MMM dd yy","");
+ ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ curtime.wYear = 2002;
+ curtime.wMonth = 5;
+ curtime.wDay = 4;
+ curtime.wDayOfWeek = 3;
+ STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Simple case */
+ ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Format containing "'" */
+ ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ curtime.wHour = 36; /* Invalid */
+ STRINGSA("ddd',' MMM dd ''''yy","Sat, May 04 '02"); /* Invalid time */
+ ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("ddd',' MMM dd ''''yy",""); /* Get size only */
+ ret = GetDateFormatA(lcid, 0, &curtime, input, NULL, 0);
+ EXPECT_VALID; EXPECT_LEN(16); EXPECT_EQA;
+
+ STRINGSA("ddd',' MMM dd ''''yy",""); /* Buffer too small */
+ ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, 2);
+ EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("ddd',' MMM dd ''''yy","5/4/2002"); /* Default to DATE_SHORTDATE */
+ ret = GetDateFormat(lcid, NUO, &curtime, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA;
+ if (strncmp(buffer, Expected, strlen(Expected)) && strncmp(buffer, "5/4/02", strlen(Expected)) != 0)
+ ok (0, "Expected '%s' or '5/4/02', got '%s'\n", Expected, buffer);
+
+ STRINGSA("ddd',' MMM dd ''''yy", "Saturday, May 04, 2002"); /* DATE_LONGDATE */
+ ret = GetDateFormat(lcid, NUO|DATE_LONGDATE, &curtime, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ /* test for expected DATE_YEARMONTH behavior with null format */
+ /* NT4 returns ERROR_INVALID_FLAGS for DATE_YEARMONTH */
+ STRINGSA("ddd',' MMM dd ''''yy", ""); /* DATE_YEARMONTH */
+ ret = GetDateFormat(lcid, NUO|DATE_YEARMONTH, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_FLAGS; EXPECT_LEN(0); EXPECT_EQA;
+
+ /* Test that using invalid DATE_* flags results in the correct error */
+ /* and return values */
+ STRINGSA("m/d/y", ""); /* Invalid flags */
+ ret = GetDateFormat(lcid, DATE_YEARMONTH|DATE_SHORTDATE|DATE_LONGDATE,
+ &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_FLAGS; EXPECT_LEN(0); EXPECT_EQA;
+}
+
+static void test_GetDateFormatW(void)
+{
+ int ret;
+ SYSTEMTIME curtime;
+ WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+ STRINGSW("",""); /* If flags is not zero then format must be NULL */
+ ret = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, NULL,
+ input, buffer, COUNTOF(buffer));
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ EXPECT_FLAGS; EXPECT_LEN(0); EXPECT_EQW;
+
+ STRINGSW("",""); /* NULL buffer, len > 0 */
+ ret = GetDateFormatW (lcid, 0, NULL, input, NULL, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQW;
+
+ STRINGSW("",""); /* NULL buffer, len == 0 */
+ ret = GetDateFormatW (lcid, 0, NULL, input, NULL, 0);
+ EXPECT_VALID; EXPECT_LENW; EXPECT_EQW;
+
+ curtime.wYear = 2002;
+ curtime.wMonth = 10;
+ curtime.wDay = 23;
+ curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
+ curtime.wHour = 65432; /* Invalid */
+ curtime.wMinute = 34512; /* Invalid */
+ curtime.wSecond = 65535; /* Invalid */
+ curtime.wMilliseconds = 12345;
+ STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
+ ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENW; EXPECT_EQW;
+}
+
+
+#define CY_POS_LEFT 0
+#define CY_POS_RIGHT 1
+#define CY_POS_LEFT_SPACE 2
+#define CY_POS_RIGHT_SPACE 3
+
+static void test_GetCurrencyFormatA(void)
+{
+ static char szDot[] = { '.', '\0' };
+ static char szComma[] = { ',', '\0' };
+ static char szDollar[] = { '$', '\0' };
+ int ret;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
+ CURRENCYFMTA format;
+
+ memset(&format, 0, sizeof(format));
+
+ STRINGSA("23",""); /* NULL output, length > 0 --> Error */
+ ret = GetCurrencyFormatA(lcid, 0, input, NULL, NULL, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("23,53",""); /* Invalid character --> Error */
+ ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("--",""); /* Double '-' --> Error */
+ ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("0-",""); /* Trailing '-' --> Error */
+ ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("0..",""); /* Double '.' --> Error */
+ ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA(" 0.1",""); /* Leading space --> Error */
+ ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("1234","$"); /* Length too small --> Write up to length chars */
+ ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, 2);
+ EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("2353",""); /* Format and flags given --> Error */
+ ret = GetCurrencyFormatA(lcid, NUO, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_INVALIDFLAGS; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("2353",""); /* Invalid format --> Error */
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("2353","$2,353.00"); /* Valid number */
+ ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("-2353","($2,353.00)"); /* Valid negative number */
+ ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("2353.1","$2,353.10"); /* Valid real number */
+ ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("2353.111","$2,353.11"); /* Too many DP --> Truncated */
+ ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("2353.119","$2,353.12"); /* Too many DP --> Rounded */
+ ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NumDigits = 0; /* No decimal separator */
+ format.LeadingZero = 0;
+ format.Grouping = 0; /* No grouping char */
+ format.NegativeOrder = 0;
+ format.PositiveOrder = CY_POS_LEFT;
+ format.lpDecimalSep = szDot;
+ format.lpThousandSep = szComma;
+ format.lpCurrencySymbol = szDollar;
+
+ STRINGSA("2353","$2353"); /* No decimal or grouping chars expected */
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
+ STRINGSA("2353","$2353.0");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.Grouping = 2; /* Group by 100's */
+ STRINGSA("2353","$23,53.0");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.LeadingZero = 1; /* Always provide leading zero */
+ STRINGSA(".5","$0.5");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.PositiveOrder = CY_POS_RIGHT;
+ STRINGSA("1","1.0$");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.PositiveOrder = CY_POS_LEFT_SPACE;
+ STRINGSA("1","$ 1.0");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.PositiveOrder = CY_POS_RIGHT_SPACE;
+ STRINGSA("1","1.0 $");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 0;
+ STRINGSA("-1","($1.0)");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 1;
+ STRINGSA("-1","-$1.0");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 2;
+ STRINGSA("-1","$-1.0");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 3;
+ STRINGSA("-1","$1.0-");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 4;
+ STRINGSA("-1","(1.0$)");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 5;
+ STRINGSA("-1","-1.0$");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 6;
+ STRINGSA("-1","1.0-$");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 7;
+ STRINGSA("-1","1.0$-");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 8;
+ STRINGSA("-1","-1.0 $");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 9;
+ STRINGSA("-1","-$ 1.0");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 10;
+ STRINGSA("-1","1.0 $-");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 11;
+ STRINGSA("-1","$ 1.0-");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 12;
+ STRINGSA("-1","$ -1.0");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 13;
+ STRINGSA("-1","1.0- $");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 14;
+ STRINGSA("-1","($ 1.0)");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = 15;
+ STRINGSA("-1","(1.0 $)");
+ ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+}
+
+#define NEG_PARENS 0 /* "(1.1)" */
+#define NEG_LEFT 1 /* "-1.1" */
+#define NEG_LEFT_SPACE 2 /* "- 1.1" */
+#define NEG_RIGHT 3 /* "1.1-" */
+#define NEG_RIGHT_SPACE 4 /* "1.1 -" */
+
+static void test_GetNumberFormatA(void)
+{
+ static char szDot[] = { '.', '\0' };
+ static char szComma[] = { ',', '\0' };
+ int ret;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+ char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
+ NUMBERFMTA format;
+
+ memset(&format, 0, sizeof(format));
+
+ STRINGSA("23",""); /* NULL output, length > 0 --> Error */
+ ret = GetNumberFormatA(lcid, 0, input, NULL, NULL, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("23,53",""); /* Invalid character --> Error */
+ ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("--",""); /* Double '-' --> Error */
+ ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("0-",""); /* Trailing '-' --> Error */
+ ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("0..",""); /* Double '.' --> Error */
+ ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA(" 0.1",""); /* Leading space --> Error */
+ ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("1234","1"); /* Length too small --> Write up to length chars */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, 2);
+ EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("2353",""); /* Format and flags given --> Error */
+ ret = GetNumberFormatA(lcid, NUO, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_INVALIDFLAGS; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("2353",""); /* Invalid format --> Error */
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+ STRINGSA("2353","2,353.00"); /* Valid number */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("-2353","-2,353.00"); /* Valid negative number */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("-353","-353.00"); /* test for off by one error in grouping */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("2353.1","2,353.10"); /* Valid real number */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("2353.111","2,353.11"); /* Too many DP --> Truncated */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ STRINGSA("2353.119","2,353.12"); /* Too many DP --> Rounded */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NumDigits = 0; /* No decimal separator */
+ format.LeadingZero = 0;
+ format.Grouping = 0; /* No grouping char */
+ format.NegativeOrder = 0;
+ format.lpDecimalSep = szDot;
+ format.lpThousandSep = szComma;
+
+ STRINGSA("2353","2353"); /* No decimal or grouping chars expected */
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
+ STRINGSA("2353","2353.0");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.Grouping = 2; /* Group by 100's */
+ STRINGSA("2353","23,53.0");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.LeadingZero = 1; /* Always provide leading zero */
+ STRINGSA(".5","0.5");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = NEG_PARENS;
+ STRINGSA("-1","(1.0)");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = NEG_LEFT;
+ STRINGSA("-1","-1.0");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = NEG_LEFT_SPACE;
+ STRINGSA("-1","- 1.0");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = NEG_RIGHT;
+ STRINGSA("-1","1.0-");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ format.NegativeOrder = NEG_RIGHT_SPACE;
+ STRINGSA("-1","1.0 -");
+ ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+ lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
+
+ if (IsValidLocale(lcid, 0))
+ {
+ STRINGSA("-12345","-12 345,00"); /* Try French formatting */
+ Expected[3] = 160; /* Non breaking space */
+ ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+ EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+ }
+}
+
+
+static void test_CompareStringA(void)
+{
+ int ret;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
+
+ ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
+ ok (ret== 1, "(Salut/Salute) Expected 1, got %d\n", ret);
+
+ ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
+ ok (ret== 2, "(Salut/SaLuT) Expected 2, got %d\n", ret);
+
+ ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
+ ok (ret== 3, "(Salut/hola) Expected 3, got %d\n", ret);
+
+ ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
+ ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+
+ lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+ ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
+ ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+
+ ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
+ ok (ret== 3, "(haha/hoho) Expected 3, got %d\n", ret);
+
+ ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1);
+ ok (ret == 2, "(Salut/saLuT) Expected 2, got %d\n", ret);
+
+ /* test for CompareStringA flags */
+ SetLastError(0xdeadbeef);
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0x10, "NULL", -1, "NULL", -1);
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+ ok(!ret, "CompareStringA must fail with invalid flag\n");
+
+ SetLastError(0xdeadbeef);
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, LOCALE_USE_CP_ACP, "NULL", -1, "NULL", -1);
+ ok(GetLastError() == 0xdeadbeef, "unexpected error code %ld\n", GetLastError());
+ ok(ret == CSTR_EQUAL, "CompareStringA error: %d != CSTR_EQUAL\n", ret);
+ /* end of test for CompareStringA flags */
+
+ ret = lstrcmpA("", "");
+ ok (ret == 0, "lstrcmpA(\"\", \"\") should return 0, got %d\n", ret);
+
+ ret = lstrcmpA(NULL, NULL);
+ ok (ret == 0, "lstrcmpA(NULL, NULL) should return 0, got %d\n", ret);
+
+ ret = lstrcmpA("", NULL);
+ ok (ret == 1, "lstrcmpA(\"\", NULL) should return 1, got %d\n", ret);
+
+ ret = lstrcmpA(NULL, "");
+ ok (ret == -1, "lstrcmpA(NULL, \"\") should return -1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"EndDialog",-1,"_Property",-1);
+ ok( ret == 3, "EndDialog vs _Property ... expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"osp_vba.sreg0070",-1,"_IEWWBrowserComp",-1);
+ ok( ret == 3, "osp_vba.sreg0070 vs _IEWWBrowserComp ... expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"r",-1,"\\",-1);
+ ok( ret == 3, "r vs \\ ... expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"osp_vba.sreg0031", -1, "OriginalDatabase", -1 );
+ ok( ret == 3, "osp_vba.sreg0031 vs OriginalDatabase ... expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1 );
+ ok( ret == 3, "AAA vs aaa expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1 );
+ ok( ret == 1, "AAA vs aab expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1 );
+ ok( ret == 1, "AAA vs Aab expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1 );
+ ok( ret == 1, ".AAA vs Aab expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1 );
+ ok( ret == 1, ".AAA vs A.ab expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1 );
+ ok( ret == 1, "aa vs AB expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1 );
+ ok( ret == 1, "aa vs Aab expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1 );
+ ok( ret == 3, "aB vs Aab expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1 );
+ ok( ret == 1, "Ba vs bab expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1 );
+ ok( ret == 1, "{100}{83}{71}{71}{71} vs Global_DataAccess_JRO expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1 );
+ ok( ret == 3, "a vs { expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1 );
+ ok( ret == 3, "A vs { expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1 );
+ ok(ret == 1, "3.5/0 vs 4.0/-1 expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1 );
+ ok(ret == 1, "3.5 vs 4.0 expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1 );
+ ok(ret == 1, "3.520.4403.2 vs 4.0.2927.10 expected 1, got %d\n", ret);
+
+ /* hyphen and apostrophe are treated differently depending on
+ * whether SORT_STRINGSORT specified or not
+ */
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1 );
+ ok(ret == 3, "-o vs /m expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1 );
+ ok(ret == 1, "/m vs -o expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1 );
+ ok(ret == 1, "-o vs /m expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1 );
+ ok(ret == 3, "/m vs -o expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1 );
+ ok(ret == 3, "'o vs /m expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1 );
+ ok(ret == 1, "/m vs 'o expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1 );
+ ok(ret == 1, "'o vs /m expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1 );
+ ok(ret == 3, "/m vs 'o expected 3, got %d\n", ret);
+
+ if (0) { /* this requires collation table patch to make it MS compatible */
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
+ ok(ret == 1, "'o vs -o expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 );
+ ok(ret == 1, "'o vs -o expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 );
+ ok(ret == 1, "' vs - expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 );
+ ok(ret == 1, "' vs - expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 );
+ ok(ret == 3, "`o vs /m expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 );
+ ok(ret == 1, "/m vs `o expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 );
+ ok(ret == 3, "`o vs /m expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 );
+ ok(ret == 1, "/m vs `o expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 );
+ ok(ret == 1, "`o vs -m expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 );
+ ok(ret == 3, "-m vs `o expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 );
+ ok(ret == 3, "`o vs -m expected 3, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 );
+ ok(ret == 1, "-m vs `o expected 1, got %d\n", ret);
+ }
+
+ ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9);
+ ok(ret == 2, "aLuZkUtZ vs aLuZkUtZ\\0 expected 2, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10);
+ ok(ret == 1, "aLuZkUtZ vs aLuZkUtZ\\0A expected 1, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
+ ok(ret == 2, "aLuZkUtZ vs aLuZkUtZ\\0A expected 2, got %d\n", ret);
+
+ ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10);
+ ok(ret == 2, "aLu\\0ZkUtZ vs aLu\\0ZkUtZ\\0A expected 2, got %d\n", ret);
+}
+
+static void test_LCMapStringA(void)
+{
+ int ret, ret2;
+ char buf[256], buf2[256];
+ static const char upper_case[] = "\tJUST! A, TEST; STRING 1/*+-.\r\n";
+ static const char lower_case[] = "\tjust! a, test; string 1/*+-.\r\n";
+ static const char symbols_stripped[] = "justateststring1";
+
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP | LCMAP_LOWERCASE,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret == lstrlenA(lower_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenA(lower_case) + 1);
+ ok(!memcmp(buf, lower_case, ret), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
+ upper_case, -1, buf, sizeof(buf));
+ ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA,
+ upper_case, -1, buf, sizeof(buf));
+ ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
+ upper_case, -1, buf, sizeof(buf));
+ ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
+ upper_case, -1, buf, sizeof(buf));
+ ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT,
+ upper_case, -1, buf, sizeof(buf));
+ ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
+ ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
+
+ /* test LCMAP_LOWERCASE */
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+ upper_case, -1, buf, sizeof(buf));
+ ok(ret == lstrlenA(upper_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenA(upper_case) + 1);
+ ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+
+ /* test LCMAP_UPPERCASE */
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret == lstrlenA(lower_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenA(lower_case) + 1);
+ ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
+
+ /* test buffer overflow */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+ lower_case, -1, buf, 4);
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
+
+ /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
+ lstrcpyA(buf, lower_case);
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+ buf, -1, buf, sizeof(buf));
+ if (!ret) /* Win9x */
+ trace("Ignoring LCMapStringA(LCMAP_UPPERCASE, buf, buf) error on Win9x\n");
+ else
+ {
+ ok(ret == lstrlenA(lower_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenA(lower_case) + 1);
+ ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
+ }
+ lstrcpyA(buf, upper_case);
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+ buf, -1, buf, sizeof(buf));
+ if (!ret) /* Win9x */
+ trace("Ignoring LCMapStringA(LCMAP_LOWERCASE, buf, buf) error on Win9x\n");
+ else
+ {
+ ok(ret == lstrlenA(upper_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenA(lower_case) + 1);
+ ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+ }
+
+ /* otherwise src == dst should fail */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
+ buf, 10, buf, sizeof(buf));
+ ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
+ GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
+ "unexpected error code %ld\n", GetLastError());
+ ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
+
+ /* test whether '\0' is always appended */
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ upper_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringA must succeed\n");
+ ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ upper_case, lstrlenA(upper_case), buf2, sizeof(buf2));
+ ok(ret, "LCMapStringA must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+ /* test LCMAP_SORTKEY | NORM_IGNORECASE */
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
+ upper_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringA must succeed\n");
+ ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ lower_case, -1, buf2, sizeof(buf2));
+ ok(ret2, "LCMapStringA must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+ /* test LCMAP_SORTKEY | NORM_IGNORENONSPACE */
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORENONSPACE,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringA must succeed\n");
+ ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ lower_case, -1, buf2, sizeof(buf2));
+ ok(ret2, "LCMapStringA must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+ /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringA must succeed\n");
+ ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ symbols_stripped, -1, buf2, sizeof(buf2));
+ ok(ret2, "LCMapStringA must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+ /* test NORM_IGNORENONSPACE */
+ lstrcpyA(buf, "foo");
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret == lstrlenA(lower_case) + 1, "LCMapStringA should return %d, ret = %d\n",
+ lstrlenA(lower_case) + 1, ret);
+ ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+
+ /* test NORM_IGNORESYMBOLS */
+ lstrcpyA(buf, "foo");
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
+ lstrlenA(symbols_stripped) + 1, ret);
+ ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+
+ /* test srclen = 0 */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringA(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
+ ok(!ret, "LCMapStringA should fail with srclen = 0\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "unexpected error code %ld\n", GetLastError());
+}
+
+static void test_LCMapStringW(void)
+{
+ int ret, ret2;
+ WCHAR buf[256], buf2[256];
+ char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
+ static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0};
+ static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
+ static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
+ static const WCHAR fooW[] = {'f','o','o',0};
+
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
+ upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ trace("Skipping LCMapStringW tests on Win9x\n");
+ return;
+ }
+ ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA,
+ upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
+ upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
+ upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
+ ok(GetLastError() == ERROR_INVALID_FLAGS,
+ "unexpected error code %ld\n", GetLastError());
+
+ /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT,
+ upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
+ ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
+
+ /* test LCMAP_LOWERCASE */
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+ upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(ret == lstrlenW(upper_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenW(upper_case) + 1);
+ ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+ /* test LCMAP_UPPERCASE */
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+ lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(ret == lstrlenW(lower_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenW(lower_case) + 1);
+ ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+
+ /* test buffer overflow */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+ lower_case, -1, buf, 4);
+ ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
+
+ /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
+ lstrcpyW(buf, lower_case);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+ buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(ret == lstrlenW(lower_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenW(lower_case) + 1);
+ ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+
+ lstrcpyW(buf, upper_case);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+ buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(ret == lstrlenW(upper_case) + 1,
+ "ret %d, error %ld, expected value %d\n",
+ ret, GetLastError(), lstrlenW(lower_case) + 1);
+ ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+ /* otherwise src == dst should fail */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
+ buf, 10, buf, sizeof(buf));
+ ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
+ GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
+ "unexpected error code %ld\n", GetLastError());
+ ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
+
+ /* test whether '\0' is always appended */
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ upper_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringW must succeed\n");
+ ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
+ ok(ret, "LCMapStringW must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+ /* test LCMAP_SORTKEY | NORM_IGNORECASE */
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
+ upper_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringW must succeed\n");
+ ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ lower_case, -1, buf2, sizeof(buf2));
+ ok(ret2, "LCMapStringW must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+ /* test LCMAP_SORTKEY | NORM_IGNORENONSPACE */
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORENONSPACE,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringW must succeed\n");
+ ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ lower_case, -1, buf2, sizeof(buf2));
+ ok(ret2, "LCMapStringW must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+ /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
+ lower_case, -1, buf, sizeof(buf));
+ ok(ret, "LCMapStringW must succeed\n");
+ ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+ symbols_stripped, -1, buf2, sizeof(buf2));
+ ok(ret2, "LCMapStringW must succeed\n");
+ ok(ret == ret2, "lengths of sort keys must be equal\n");
+ ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+ /* test NORM_IGNORENONSPACE */
+ lstrcpyW(buf, fooW);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
+ lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(ret == lstrlenW(lower_case) + 1, "LCMapStringW should return %d, ret = %d\n",
+ lstrlenW(lower_case) + 1, ret);
+ ok(!lstrcmpW(buf, lower_case), "string comparison mismatch\n");
+
+ /* test NORM_IGNORESYMBOLS */
+ lstrcpyW(buf, fooW);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
+ lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+ ok(ret == lstrlenW(symbols_stripped) + 1, "LCMapStringW should return %d, ret = %d\n",
+ lstrlenW(symbols_stripped) + 1, ret);
+ ok(!lstrcmpW(buf, symbols_stripped), "string comparison mismatch\n");
+
+ /* test srclen = 0 */
+ SetLastError(0xdeadbeef);
+ ret = LCMapStringW(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
+ ok(!ret, "LCMapStringW should fail with srclen = 0\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "unexpected error code %ld\n", GetLastError());
+}
+
+/* this requires collation table patch to make it MS compatible */
+const char *strings_sorted[] =
+{
+"'",
+"-",
+"!",
+"\"",
+".",
+":",
+"\\",
+"_",
+"`",
+"{",
+"}",
+"+",
+"0",
+"1",
+"2",
+"3",
+"4",
+"5",
+"6",
+"7",
+"8",
+"9",
+"a",
+"A",
+"b",
+"B",
+"c",
+"C"
+};
+
+const char *strings[] =
+{
+"C",
+"\"",
+"9",
+"'",
+"}",
+"-",
+"7",
+"+",
+"`",
+"1",
+"a",
+"5",
+"\\",
+"8",
+"B",
+"3",
+"_",
+"6",
+"{",
+"2",
+"c",
+"4",
+"!",
+"0",
+"A",
+":",
+"b",
+"."
+};
+
+static int compare_string1(const void *e1, const void *e2)
+{
+ const char *s1 = *(const char **)e1;
+ const char *s2 = *(const char **)e2;
+
+ return lstrcmpA(s1, s2);
+}
+
+static int compare_string2(const void *e1, const void *e2)
+{
+ const char *s1 = *(const char **)e1;
+ const char *s2 = *(const char **)e2;
+
+ return CompareStringA(0, 0, s1, -1, s2, -1) - 2;
+}
+
+static int compare_string3(const void *e1, const void *e2)
+{
+ const char *s1 = *(const char **)e1;
+ const char *s2 = *(const char **)e2;
+ char key1[256], key2[256];
+
+ LCMapStringA(0, LCMAP_SORTKEY, s1, -1, key1, sizeof(key1));
+ LCMapStringA(0, LCMAP_SORTKEY, s2, -1, key2, sizeof(key2));
+ return strcmp(key1, key2);
+}
+
+static void test_sorting(void)
+{
+ char buf[256];
+ char **str_buf = (char **)buf;
+ int i;
+
+ assert(sizeof(buf) >= sizeof(strings));
+
+ /* 1. sort using lstrcmpA */
+ memcpy(buf, strings, sizeof(strings));
+ qsort(buf, sizeof(strings)/sizeof(strings[0]), sizeof(strings[0]), compare_string1);
+ for (i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
+ {
+ ok(!strcmp(strings_sorted[i], str_buf[i]),
+ "qsort using lstrcmpA failed for element %d\n", i);
+ }
+ /* 2. sort using CompareStringA */
+ memcpy(buf, strings, sizeof(strings));
+ qsort(buf, sizeof(strings)/sizeof(strings[0]), sizeof(strings[0]), compare_string2);
+ for (i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
+ {
+ ok(!strcmp(strings_sorted[i], str_buf[i]),
+ "qsort using CompareStringA failed for element %d\n", i);
+ }
+ /* 3. sort using sort keys */
+ memcpy(buf, strings, sizeof(strings));
+ qsort(buf, sizeof(strings)/sizeof(strings[0]), sizeof(strings[0]), compare_string3);
+ for (i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
+ {
+ ok(!strcmp(strings_sorted[i], str_buf[i]),
+ "qsort using sort keys failed for element %d\n", i);
+ }
+}
+
+static void test_FoldStringA(void)
+{
+ int ret, i;
+ char src[256], dst[256];
+ static const char digits_src[] = { 0xB9,0xB2,0xB3,'\0' };
+ static const char digits_dst[] = { '1','2','3','\0' };
+ static const char composite_src[] =
+ {
+ 0x8a,0x8e,0x9a,0x9e,0x9f,0xc0,0xc1,0xc2,
+ 0xc3,0xc4,0xc5,0xc7,0xc8,0xc9,0xca,0xcb,
+ 0xcc,0xcd,0xce,0xcf,0xd1,0xd2,0xd3,0xd4,
+ 0xd5,0xd6,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,
+ 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe7,0xe8,
+ 0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf1,
+ 0xf2,0xf3,0xf4,0xf5,0xf6,0xf8,0xf9,0xfa,
+ 0xfb,0xfc,0xfd,0xff,'\0'
+ };
+ static const char composite_dst[] =
+ {
+ 0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
+ 0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
+ 0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
+ 0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
+ 0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
+ 0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
+ 0x4f,0x7e,0x4f,0xa8,0x4f,0x3f,0x55,0x60,
+ 0x55,0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,
+ 0x61,0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,
+ 0x61,0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,
+ 0x65,0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,
+ 0x69,0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,
+ 0x6f,0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,
+ 0x6f,0xa8,0x6f,0x3f,0x75,0x60,0x75,0xb4,
+ 0x75,0x5e,0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
+ };
+ static const char ligatures_src[] =
+ {
+ 0x8C,0x9C,0xC6,0xDE,0xDF,0xE6,0xFE,'\0'
+ };
+ static const char ligatures_dst[] =
+ {
+ 'O','E','o','e','A','E','T','H','s','s','a','e','t','h','\0'
+ };
+
+ if (!pFoldStringA)
+ return; /* FoldString is present in NT v3.1+, but not 95/98/Me */
+
+ /* these tests are locale specific */
+ if (GetACP() != 1252)
+ {
+ trace("Skipping FoldStringA tests for a not Latin 1 locale\n");
+ return;
+ }
+
+ /* MAP_FOLDDIGITS */
+ SetLastError(0);
+ ret = pFoldStringA(MAP_FOLDDIGITS, digits_src, -1, dst, 256);
+ if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ EXPECT_LEN(4); EXPECT_VALID;
+ ok(strcmp(dst, digits_dst) == 0,
+ "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", digits_dst, dst);
+ for (i = 1; i < 256; i++)
+ {
+ if (!strchr(digits_src, i))
+ {
+ src[0] = i;
+ src[1] = '\0';
+ SetLastError(0);
+ ret = pFoldStringA(MAP_FOLDDIGITS, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(dst[0] == src[0],
+ "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", src, dst);
+ }
+ }
+
+ /* MAP_EXPAND_LIGATURES */
+ SetLastError(0);
+ ret = pFoldStringA(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
+ /* NT 4.0 doesn't support MAP_EXPAND_LIGATURES */
+ if (!(ret == 0 && GetLastError() == ERROR_INVALID_FLAGS)) {
+ EXPECT_LEN(sizeof(ligatures_dst)); EXPECT_VALID;
+ ok(strcmp(dst, ligatures_dst) == 0,
+ "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", ligatures_dst, dst);
+ for (i = 1; i < 256; i++)
+ {
+ if (!strchr(ligatures_src, i))
+ {
+ src[0] = i;
+ src[1] = '\0';
+ SetLastError(0);
+ ret = pFoldStringA(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(dst[0] == src[0],
+ "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", src, dst);
+ }
+ }
+ }
+
+ /* MAP_COMPOSITE */
+ SetLastError(0);
+ ret = pFoldStringA(MAP_COMPOSITE, composite_src, -1, dst, 256);
+ EXPECT_VALID;
+ todo_wine
+ {
+ /* Wine gets close, but doesn't produce quite the same result as native */
+ EXPECT_LEN(121);
+ ok(strcmp(dst, composite_dst) == 0,
+ "MAP_COMPOSITE: Expected '%s', got '%s'\n", composite_dst, dst);
+ }
+
+ for (i = 1; i < 256; i++)
+ {
+ if (!strchr(composite_src, i))
+ {
+ src[0] = i;
+ src[1] = '\0';
+ SetLastError(0);
+ ret = pFoldStringA(MAP_COMPOSITE, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(dst[0] == src[0],
+ "0x%02x, 0x%02x,0x%02x,0x%02x,\n", (unsigned char)src[0],
+ (unsigned char)dst[0],(unsigned char)dst[1],(unsigned char)dst[2]);
+ }
+ }
+
+ /* MAP_FOLDCZONE */
+ for (i = 1; i < 256; i++)
+ {
+ src[0] = i;
+ src[1] = '\0';
+ SetLastError(0);
+ ret = pFoldStringA(MAP_FOLDCZONE, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(src[0] == dst[0],
+ "MAP_FOLDCZONE: Expected 0x%02x, got 0x%02x\n",
+ (unsigned char)src[0], (unsigned char)dst[0]);
+ }
+
+ /* MAP_PRECOMPOSED */
+ for (i = 1; i < 256; i++)
+ {
+ src[0] = i;
+ src[1] = '\0';
+ SetLastError(0);
+ ret = pFoldStringA(MAP_PRECOMPOSED, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(src[0] == dst[0],
+ "MAP_PRECOMPOSED: Expected 0x%02x, got 0x%02x\n",
+ (unsigned char)src[0], (unsigned char)dst[0]);
+ }
+}
+
+static void test_FoldStringW(void)
+{
+ int ret;
+ size_t i, j;
+ WCHAR src[256], dst[256], ch, prev_ch = 1;
+ static const DWORD badFlags[] =
+ {
+ 0,
+ MAP_PRECOMPOSED|MAP_COMPOSITE,
+ MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES,
+ MAP_COMPOSITE|MAP_EXPAND_LIGATURES
+ };
+ /* Ranges of digits 0-9 : Must be sorted! */
+ static const WCHAR digitRanges[] =
+ {
+ 0x0030, /* '0'-'9' */
+ 0x0660, /* Eastern Arabic */
+ 0x06F0, /* Arabic - Hindu */
+ 0x0966, /* Devengari */
+ 0x09E6, /* Bengalii */
+ 0x0A66, /* Gurmukhi */
+ 0x0AE6, /* Gujarati */
+ 0x0B66, /* Oriya */
+ 0x0BE6, /* Tamil - No 0 */
+ 0x0C66, /* Telugu */
+ 0x0CE6, /* Kannada */
+ 0x0D66, /* Maylayalam */
+ 0x0E50, /* Thai */
+ 0x0ED0, /* Laos */
+ 0x2070, /* Superscript - 1, 2, 3 are out of sequence */
+ 0x2080, /* Subscript */
+ 0x245F, /* Circled - 0 is out of sequence */
+ 0x2473, /* Bracketed */
+ 0x2487, /* Full stop */
+ 0x2775, /* Inverted circled - No 0 */
+ 0x277F, /* Patterned circled - No 0 */
+ 0x2789, /* Inverted Patterned circled - No 0 */
+ 0xff10, /* Pliene chasse (?) */
+ 0xffff /* Terminator */
+ };
+ /* Digits which are represented, but out of sequence */
+ static const WCHAR outOfSequenceDigits[] =
+ {
+ 0xB9, /* Superscript 1 */
+ 0xB2, /* Superscript 2 */
+ 0xB3, /* Superscript 3 */
+ 0x24EA, /* Circled 0 */
+ '\0' /* Terminator */
+ };
+ /* Digits in digitRanges for which no representation is available */
+ static const WCHAR noDigitAvailable[] =
+ {
+ 0x0BE6, /* No Tamil 0 */
+ 0x2473, /* No Bracketed 0 */
+ 0x2487, /* No 0 Full stop */
+ 0x2775, /* No inverted circled 0 */
+ 0x277F, /* No patterned circled */
+ 0x2789, /* No inverted Patterned circled */
+ '\0' /* Terminator */
+ };
+ /* Compatibility conversion results */
+ static const WCHAR compat_F900_FA2F[256+48] =
+ {
+ 0x8c48, 0x66f4, 0x8eca, 0x8cc8, 0x6ed1, 0x4e32, 0x53e5, 0x9f9c,
+ 0x9f9c, 0x5951, 0x91d1, 0x5587, 0x5948, 0x61f6, 0x7669, 0x7f85,
+ 0x863f, 0x87ba, 0x88f8, 0x908f, 0x6a02, 0x6d1b, 0x70d9, 0x73de,
+ 0x843d, 0x916a, 0x99f1, 0x4e82, 0x5375, 0x6b04, 0x721b, 0x862d,
+ 0x9e1e, 0x5d50, 0x6feb, 0x85cd, 0x8964, 0x62c9, 0x81d8, 0x881f,
+ 0x5eca, 0x6717, 0x6d6a, 0x72fc, 0x0000, 0x4f86, 0x51b7, 0x52de,
+ 0x64c4, 0x6ad3, 0x7210, 0x76e7, 0x8001, 0x8606, 0x865c, 0x8def,
+ 0x9732, 0x9b6f, 0x9dfa, 0x788c, 0x797f, 0x7da0, 0x83c9, 0x9304,
+ 0x9e7f, 0x8ad6, 0x58df, 0x5f04, 0x7c60, 0x807e, 0x7262, 0x78ca,
+ 0x8cc2, 0x96f7, 0x58d8, 0x5c62, 0x6a13, 0x6dda, 0x6f0f, 0x7d2f,
+ 0x7e37, 0x964b, 0x52d2, 0x808b, 0x51dc, 0x51cc, 0x7a1c, 0x7dbe,
+ 0x83f1, 0x9675, 0x8b80, 0x62cf, 0x6a02, 0x8afe, 0x4e39, 0x5be7,
+ 0x6012, 0x7387, 0x7570, 0x5317, 0x78fb, 0x4fbf, 0x5fa9, 0x4e0d,
+ 0x6ccc, 0x6578, 0x7d22, 0x53c3, 0x585e, 0x7701, 0x8449, 0x8aaa,
+ 0x6bba, 0x8fb0, 0x6c88, 0x62fe, 0x82e5, 0x63a0, 0x7565, 0x4eae,
+ 0x5169, 0x0000, 0x6881, 0x7ce7, 0x826f, 0x8ad2, 0x91cf, 0x52f5,
+ 0x5442, 0x5973, 0x5eec, 0x65c5, 0x6ffe, 0x792a, 0x95ad, 0x9a6a,
+ 0x9e97, 0x9ece, 0x529b, 0x66c6, 0x6b77, 0x8f62, 0x5e74, 0x6190,
+ 0x6200, 0x649a, 0x6f23, 0x7149, 0x7489, 0x0000, 0x7df4, 0x806f,
+ 0x8f26, 0x84ee, 0x9023, 0x934a, 0x5217, 0x52a3, 0x54bd, 0x70c8,
+ 0x88c2, 0x8aaa, 0x5ec9, 0x5ff5, 0x637b, 0x6bae, 0x7c3e, 0x7375,
+ 0x4ee4, 0x56f9, 0x5be7, 0x5dba, 0x601c, 0x73b2, 0x7469, 0x7f9a,
+ 0x8046, 0x9234, 0x96f6, 0x9748, 0x9818, 0x4f8b, 0x79ae, 0x91b4,
+ 0x96b8, 0x60e1, 0x4e86, 0x50da, 0x5bee, 0x5c3f, 0x6599, 0x6a02,
+ 0x71ce, 0x7642, 0x84fc, 0x907c, 0x9f8d, 0x6688, 0x962e, 0x5289,
+ 0x677b, 0x67f3, 0x6d41, 0x6e9c, 0x7409, 0x7559, 0x786b, 0x7d10,
+ 0x985e, 0x516d, 0x622e, 0x9678, 0x502b, 0x5d19, 0x6dea, 0x8f2a,
+ 0x5f8b, 0x6144, 0x6817, 0x7387, 0x9686, 0x5229, 0x540f, 0x5c65,
+ 0x6613, 0x674e, 0x68a8, 0x6ce5, 0x7406, 0x75e2, 0x7f79, 0x0000,
+ 0x88e1, 0x91cc, 0x96e2, 0x533f, 0x6eba, 0x541d, 0x71d0, 0x7498,
+ 0x85fa, 0x0000, 0x9c57, 0x9e9f, 0x6797, 0x6dcb, 0x81e8, 0x7acb,
+ 0x7b20, 0x7c92, 0x72c0, 0x7099, 0x8b58, 0x4ec0, 0x8336, 0x523a,
+ 0x5207, 0x5ea6, 0x62d3, 0x7cd6, 0x5b85, 0x6d1e, 0x66b4, 0x8f3b,
+ 0x884c, 0x964d, 0x898b, 0x5ed3, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x585a, 0x0000, 0x6674, 0x0000, 0x0000, 0x51de, 0x8c6c, 0x76ca,
+ 0x0000, 0x795e, 0x7965, 0x798f, 0x9756, 0x7cbe, 0x7fbd, 0x0000,
+ 0x0000, 0x0000, 0x8af8, 0x0000, 0x0000, 0x9038, 0x90fd, 0x0000,
+ 0x0000, 0x0000, 0x98ef, 0x98fc, 0x9928, 0x9db4, 0x0000, 0x0000
+ };
+ static const WCHAR compat_FE30_FEF7[200] =
+ {
+ 0x2025, 0x2014, 0x2013, 0x005f, 0x005f, 0x0028, 0x0029, 0x007b,
+ 0x007d, 0x3014, 0x3015, 0x3010, 0x3011, 0x300a, 0x300b, 0x3008,
+ 0x3009, 0x300c, 0x300d, 0x300e, 0x300f, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x203e, 0x203e, 0x203e, 0x203e, 0x005f, 0x005f, 0x005f,
+ 0x002c, 0x3001, 0x002e, 0x0000, 0x003b, 0x003a, 0x003f, 0x0021,
+ 0x2014, 0x0028, 0x0029, 0x007b, 0x007d, 0x3014, 0x3015, 0x0023,
+ 0x0026, 0x002a, 0x002b, 0x002d, 0x003c, 0x003e, 0x003d, 0x0000,
+ 0x0000, 0x0024, 0x0025, 0x0040, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x064b, 0x064b, 0x064c, 0x0000, 0x064d, 0x0000, 0x064e, 0x064e,
+ 0x064f, 0x064f, 0x0650, 0x0650, 0x0651, 0x0651, 0x0652, 0x0652,
+ 0x0621, 0x0622, 0x0622, 0x0623, 0x0623, 0x0624, 0x0624, 0x0625,
+ 0x0625, 0x0626, 0x0626, 0x0626, 0x0626, 0x0627, 0x0627, 0x0628,
+ 0x0628, 0x0628, 0x0628, 0x0629, 0x0629, 0x062a, 0x062a, 0x062a,
+ 0x062a, 0x062b, 0x062b, 0x062b, 0x062b, 0x062c, 0x062c, 0x062c,
+ 0x062c, 0x062d, 0x062d, 0x062d, 0x062d, 0x062e, 0x062e, 0x062e,
+ 0x062e, 0x062f, 0x062f, 0x0630, 0x0630, 0x0631, 0x0631, 0x0632,
+ 0x0632, 0x0633, 0x0633, 0x0633, 0x0633, 0x0634, 0x0634, 0x0634,
+ 0x0634, 0x0635, 0x0635, 0x0635, 0x0635, 0x0636, 0x0636, 0x0636,
+ 0x0636, 0x0637, 0x0637, 0x0637, 0x0637, 0x0638, 0x0638, 0x0638,
+ 0x0638, 0x0639, 0x0639, 0x0639, 0x0639, 0x063a, 0x063a, 0x063a,
+ 0x063a, 0x0641, 0x0641, 0x0641, 0x0641, 0x0642, 0x0642, 0x0642,
+ 0x0642, 0x0643, 0x0643, 0x0643, 0x0643, 0x0644, 0x0644, 0x0644,
+ 0x0644, 0x0645, 0x0645, 0x0645, 0x0645, 0x0646, 0x0646, 0x0646,
+ 0x0646, 0x0647, 0x0647, 0x0647, 0x0647, 0x0648, 0x0648, 0x0649,
+ 0x0649, 0x064a, 0x064a, 0x064a, 0x064a, 0x0000, 0x0000, 0x0000
+ };
+ static const WCHAR compat_FF00_FFEF[240] =
+ {
+ 0x0000, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x0000, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
+ 0x0000, 0x3002, 0x300c, 0x300d, 0x3001, 0x30fb, 0x30f2, 0x30a1,
+ 0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, 0x30e7, 0x30c3,
+ 0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad,
+ 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb, 0x30bd,
+ 0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, 0x30cb, 0x30cc,
+ 0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de,
+ 0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9,
+ 0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, 0x309b, 0x309c,
+ 0x3164, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137,
+ 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f,
+ 0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147,
+ 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x0000,
+ 0x0000, 0x0000, 0x314f, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154,
+ 0x0000, 0x0000, 0x3155, 0x3156, 0x3157, 0x3158, 0x3159, 0x315a,
+ 0x0000, 0x0000, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160,
+ 0x0000, 0x0000, 0x3161, 0x3162, 0x3163, 0x0000, 0x0000, 0x0000,
+ 0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9, 0x0000,
+ 0x2502, 0x2190, 0x2191, 0x2192, 0x2193, 0x25a0, 0x25cb, 0x0000
+ };
+ static const WCHAR ligatures_src[] =
+ {
+ 0x00c6, 0x00de, 0x00df, 0x00e6, 0x00fe, 0x0132, 0x0133, 0x0152,
+ 0x0153, 0x01c4, 0x01c5, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01ca,
+ 0x01cb, 0x01cc, 0x01e2, 0x01e3, 0x01f1, 0x01f2, 0x01f3, 0x01fc,
+ 0x01fd, 0x05f0, 0x05f1, 0x05f2, 0xfb00, 0xfb01, 0xfb02, 0xfb03,
+ 0xfb04, 0xfb05, 0xfb06, '\0'
+ };
+ static const WCHAR ligatures_dst[] =
+ {
+ 'A','E','T','H','s','s','a','e','t','h','I','J','i','j','O','E','o','e',
+ 'D',0x017d,'D',0x017e,'d',0x017e,'L','J','L','j','l','j','N','J','N','j',
+ 'n','j',0x0100,0x0112,0x0101,0x0113,'D','Z','D','z','d','z',0x00c1,0x00c9,
+ 0x00e1,0x00e9,0x05d5,0x05d5,0x05d5,0x05d9,0x05d9,0x05d9,'f','f','f','i',
+ 'f','l','f','f','i','f','f','l',0x017f,'t','s','t','\0'
+ };
+
+ if (!pFoldStringW)
+ return; /* FoldString is present in NT v3.1+, but not 95/98/Me */
+
+ /* Invalid flag combinations */
+ for (i = 0; i < sizeof(badFlags)/sizeof(badFlags[0]); i++)
+ {
+ src[0] = dst[0] = '\0';
+ SetLastError(0);
+ ret = pFoldStringW(badFlags[i], src, 256, dst, 256);
+ if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ EXPECT_LEN(0); EXPECT_FLAGS;
+ }
+
+ /* src & dst cannot be the same */
+ SetLastError(0);
+ ret = pFoldStringW(MAP_FOLDCZONE, src, -1, src, 256);
+ EXPECT_LEN(0); EXPECT_INVALID;
+
+ /* src can't be NULL */
+ SetLastError(0);
+ ret = pFoldStringW(MAP_FOLDCZONE, NULL, -1, dst, 256);
+ EXPECT_LEN(0); EXPECT_INVALID;
+
+ /* srclen can't be 0 */
+ SetLastError(0);
+ ret = pFoldStringW(MAP_FOLDCZONE, src, 0, dst, 256);
+ EXPECT_LEN(0); EXPECT_INVALID;
+
+ /* dstlen can't be < 0 */
+ SetLastError(0);
+ ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, -1);
+ EXPECT_LEN(0); EXPECT_INVALID;
+
+ /* Ret includes terminating NUL which is appended if srclen = -1 */
+ SetLastError(0);
+ src[0] = 'A';
+ src[1] = '\0';
+ dst[0] = '\0';
+ ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(dst[0] == 'A' && dst[1] == '\0',
+ "srclen=-1: Expected ret=2 [%d,%d], got ret=%d [%d,%d], err=%ld\n",
+ 'A', '\0', ret, dst[0], dst[1], GetLastError());
+
+ /* If size is given, result is not NUL terminated */
+ SetLastError(0);
+ src[0] = 'A';
+ src[1] = 'A';
+ dst[0] = 'X';
+ dst[1] = 'X';
+ ret = pFoldStringW(MAP_FOLDCZONE, src, 1, dst, 256);
+ EXPECT_LEN(1); EXPECT_VALID;
+ ok(dst[0] == 'A' && dst[1] == 'X',
+ "srclen=1: Expected ret=1, [%d,%d], got ret=%d,[%d,%d], err=%ld\n",
+ 'A','X', ret, dst[0], dst[1], GetLastError());
+
+ /* MAP_FOLDDIGITS */
+ for (j = 0; j < sizeof(digitRanges)/sizeof(digitRanges[0]); j++)
+ {
+ /* Check everything before this range */
+ for (ch = prev_ch; ch < digitRanges[j]; ch++)
+ {
+ SetLastError(0);
+ src[0] = ch;
+ src[1] = dst[0] = '\0';
+ ret = pFoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+
+ ok(dst[0] == ch || strchrW(outOfSequenceDigits, ch) ||
+ /* Wine (correctly) maps all Unicode 4.0+ digits */
+ isdigitW(ch) || (ch >= 0x24F5 && ch <= 0x24FD) || ch == 0x24FF ||
+ (ch >= 0x1369 && ch <= 0x1371),
+ "MAP_FOLDDIGITS: ch %d 0x%04x Expected unchanged got %d\n", ch, ch, dst[0]);
+ }
+
+ if (digitRanges[j] == 0xffff)
+ break; /* Finished the whole code point space */
+
+ for (ch = digitRanges[j]; ch < digitRanges[j] + 10; ch++)
+ {
+ WCHAR c;
+
+ /* Map out of sequence characters */
+ if (ch == 0x2071) c = 0x00B9; /* Superscript 1 */
+ else if (ch == 0x2072) c = 0x00B2; /* Superscript 2 */
+ else if (ch == 0x2073) c = 0x00B3; /* Superscript 3 */
+ else if (ch == 0x245F) c = 0x24EA; /* Circled 0 */
+ else c = ch;
+ SetLastError(0);
+ src[0] = c;
+ src[1] = dst[0] = '\0';
+ ret = pFoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+
+ ok((dst[0] == '0' + ch - digitRanges[j] && dst[1] == '\0') ||
+ strchrW(noDigitAvailable, c),
+ "MAP_FOLDDIGITS: ch %d Expected %d got %d\n",
+ ch, '0' + digitRanges[j] - ch, dst[0]);
+ }
+ prev_ch = ch;
+ }
+
+ /* MAP_FOLDCZONE */
+ for (ch = 1; ch <0xffff; ch++)
+ {
+ WCHAR expected = 0;
+
+ if (ch >= 0xF900 && ch <= 0xFA2F)
+ expected = compat_F900_FA2F[ch - 0xF900];
+ else if (ch >= 0xFE30 && ch <= 0xFEF7)
+ expected = compat_FE30_FEF7[ch - 0xFE30];
+ else if (ch >= 0xFF00 && ch <= 0xFFEF)
+ expected = compat_FF00_FFEF[ch - 0xFF00];
+
+ if (!expected)
+ expected = ch;
+
+ SetLastError(0);
+ src[0] = ch;
+ src[1] = dst[0] = '\0';
+ ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(dst[0] == expected ||
+ /* Wine (correctly) uses updated mappings for some Unicode 4.0 chars */
+ (ch >= 0xFA0D && ch <= 0xFA47) ||
+ 0xf92c || ch == 0xf979 || ch == 0xf995 || ch == 0xf9e7 || ch == 0xf9f1,
+ "MAP_FOLDCZONE: ch %d 0x%04x Expected 0x%04x got 0x%04x\n",
+ ch, ch, expected, dst[0]);
+ }
+
+ /* MAP_EXPAND_LIGATURES */
+ SetLastError(0);
+ ret = pFoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
+ /* NT 4.0 doesn't support MAP_EXPAND_LIGATURES */
+ if (!(ret == 0 && GetLastError() == ERROR_INVALID_FLAGS)) {
+ EXPECT_LEN(sizeof(ligatures_dst)/sizeof(ligatures_dst[0])); EXPECT_VALID;
+ ok(!memcmp(dst, ligatures_dst, sizeof(ligatures_dst)),
+ "MAP_EXPAND_LIGATURES: Expanded incorrectly\n");
+ for (i = 1; i <= 0xffff; i++)
+ {
+ if (!strchrW(ligatures_src, i))
+ {
+ src[0] = i;
+ src[1] = '\0';
+ SetLastError(0);
+ ret = pFoldStringW(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
+ EXPECT_LEN(2); EXPECT_VALID;
+ ok(dst[0] == src[0],
+ "MAP_EXPAND_LIGATURES: 0x%02x : Expected 0x%02x, got 0x%02x\n",
+ i, src[0], dst[0]);
+ }
+ }
+ }
+
+ /* FIXME: MAP_PRECOMPOSED : MAP_COMPOSITE */
+}
+
+
+
+#define LCID_OK(l) \
+ ok(lcid == l, "Expected lcid = %08lx, got %08lx\n", l, lcid)
+#define MKLCID(x,y,z) MAKELCID(MAKELANGID(x, y), z)
+#define LCID_RES(src, res) lcid = ConvertDefaultLocale(src); LCID_OK(res)
+#define TEST_LCIDLANG(a,b) LCID_RES(MAKELCID(a,b), MAKELCID(a,b))
+#define TEST_LCID(a,b,c) LCID_RES(MKLCID(a,b,c), MKLCID(a,b,c))
+
+static void test_ConvertDefaultLocale(void)
+{
+ LCID lcid;
+
+ /* Doesn't change lcid, even if non default sublang/sort used */
+ TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_US, SORT_DEFAULT);
+ TEST_LCID(LANG_ENGLISH, SUBLANG_ENGLISH_UK, SORT_DEFAULT);
+ TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT);
+ TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE);
+
+ /* SUBLANG_NEUTRAL -> SUBLANG_DEFAULT */
+ LCID_RES(MKLCID(LANG_ENGLISH, SUBLANG_NEUTRAL, SORT_DEFAULT),
+ MKLCID(LANG_ENGLISH, SUBLANG_DEFAULT, SORT_DEFAULT));
+ LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_DEFAULT),
+ MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT));
+ LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE),
+ MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE));
+
+ /* Invariant language is not treated specially */
+ TEST_LCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT);
+ LCID_RES(MKLCID(LANG_INVARIANT, SUBLANG_NEUTRAL, SORT_DEFAULT),
+ MKLCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT));
+
+ /* User/system default languages alone are not mapped */
+ TEST_LCIDLANG(LANG_SYSTEM_DEFAULT, SORT_JAPANESE_UNICODE);
+ TEST_LCIDLANG(LANG_USER_DEFAULT, SORT_JAPANESE_UNICODE);
+
+ /* Default lcids */
+ LCID_RES(LOCALE_SYSTEM_DEFAULT, GetSystemDefaultLCID());
+ LCID_RES(LOCALE_USER_DEFAULT, GetUserDefaultLCID());
+ LCID_RES(LOCALE_NEUTRAL, GetUserDefaultLCID());
+}
+
+static BOOL CALLBACK langgrp_procA(LGRPID lgrpid, LPSTR lpszNum, LPSTR lpszName,
+ DWORD dwFlags, LONG_PTR lParam)
+{
+ trace("%08lx, %s, %s, %08lx, %08lx\n",
+ lgrpid, lpszNum, lpszName, dwFlags, lParam);
+
+ ok(pIsValidLanguageGroup(lgrpid, dwFlags) == TRUE,
+ "Enumerated grp %ld not valid (flags %ld)\n", lgrpid, dwFlags);
+
+ /* If lParam is one, we are calling with flags defaulted from 0 */
+ ok(!lParam || (dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED),
+ "Expected dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED, got %ld\n", dwFlags);
+
+ return TRUE;
+}
+
+static void test_EnumSystemLanguageGroupsA(void)
+{
+ if (!pEnumSystemLanguageGroupsA || !pIsValidLanguageGroup)
+ return;
+
+ /* No enumeration proc */
+ SetLastError(0);
+ pEnumSystemLanguageGroupsA(0, LGRPID_INSTALLED, 0);
+ EXPECT_INVALID;
+
+ /* Invalid flags */
+ SetLastError(0);
+ pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED|LGRPID_SUPPORTED, 0);
+ EXPECT_FLAGS;
+
+ /* No flags - defaults to LGRPID_INSTALLED */
+ SetLastError(0);
+ pEnumSystemLanguageGroupsA(langgrp_procA, 0, 1);
+ EXPECT_VALID;
+
+ pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED, 0);
+ pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
+}
+
+
+static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
+ LONG_PTR lParam)
+{
+ trace("%08lx, %08lx, %s, %08lx\n", lgrpid, lcid, lpszNum, lParam);
+
+ ok(pIsValidLanguageGroup(lgrpid, LGRPID_SUPPORTED) == TRUE,
+ "Enumerated grp %ld not valid\n", lgrpid);
+ ok(IsValidLocale(lcid, LCID_SUPPORTED) == TRUE,
+ "Enumerated grp locale %ld not valid\n", lcid);
+ return TRUE;
+}
+
+static void test_EnumLanguageGroupLocalesA(void)
+{
+ if (!pEnumLanguageGroupLocalesA || !pIsValidLanguageGroup)
+ return;
+
+ /* No enumeration proc */
+ SetLastError(0);
+ pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0, 0);
+ EXPECT_INVALID;
+
+ /* lgrpid too small */
+ SetLastError(0);
+ pEnumLanguageGroupLocalesA(lgrplocale_procA, 0, 0, 0);
+ EXPECT_INVALID;
+
+ /* lgrpid too big */
+ SetLastError(0);
+ pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_ARMENIAN + 1, 0, 0);
+ EXPECT_INVALID;
+
+ /* dwFlags is reserved */
+ SetLastError(0);
+ pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0x1, 0);
+ EXPECT_INVALID;
+
+ pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_WESTERN_EUROPE, 0, 0);
+}
+
+static void test_SetLocaleInfoA(void)
+{
+ BOOL bRet;
+ LCID lcid = GetUserDefaultLCID();
+
+ /* Null data */
+ SetLastError(0);
+ bRet = SetLocaleInfoA(lcid, LOCALE_SDATE, 0);
+ EXPECT_INVALID;
+
+ /* IDATE */
+ SetLastError(0);
+ bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, (LPSTR)test_SetLocaleInfoA);
+ EXPECT_FLAGS;
+
+ /* ILDATE */
+ SetLastError(0);
+ bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, (LPSTR)test_SetLocaleInfoA);
+ EXPECT_FLAGS;
+}
+
+static BOOL CALLBACK luilocale_proc1A(LPSTR value, LONG_PTR lParam)
+{
+ trace("%s %08lx\n", value, lParam);
+ return(TRUE);
+}
+
+static BOOL CALLBACK luilocale_proc2A(LPSTR value, LONG_PTR lParam)
+{
+ ok(!enumCount, "callback called again unexpected\n");
+ enumCount++;
+ return(FALSE);
+}
+
+static BOOL CALLBACK luilocale_proc3A(LPSTR value, LONG_PTR lParam)
+{
+ ok(0,"callback called unexpected\n");
+ return(FALSE);
+}
+
+static void test_EnumUILanguageA(void)
+{
+ BOOL ret;
+ if (!pEnumUILanguagesA) {
+ trace("EnumUILanguagesA is not available on Win9x\n");
+ return;
+ }
+
+ SetLastError(ERROR_SUCCESS);
+ ret = pEnumUILanguagesA(luilocale_proc1A, 0, 0);
+ EXPECT_TRUE; EXPECT_VALID;
+
+ enumCount = 0;
+ SetLastError(ERROR_SUCCESS);
+ ret = pEnumUILanguagesA(luilocale_proc2A, 0, 0);
+ EXPECT_TRUE; EXPECT_VALID;
+
+ SetLastError(ERROR_SUCCESS);
+ ret = pEnumUILanguagesA(NULL, 0, 0);
+ EXPECT_FALSE; EXPECT_INVALID;
+
+ SetLastError(ERROR_SUCCESS);
+ ret = pEnumUILanguagesA(luilocale_proc3A, 0x5a5a5a5a, 0);
+ EXPECT_FALSE; EXPECT_FLAGS;
+
+ SetLastError(ERROR_SUCCESS);
+ ret = pEnumUILanguagesA(NULL, 0x5a5a5a5a, 0);
+ EXPECT_FALSE; EXPECT_INVALID;
+}
+
+static char date_fmt_buf[1024];
+
+static BOOL CALLBACK enum_datetime_procA(LPSTR fmt)
+{
+ lstrcatA(date_fmt_buf, fmt);
+ lstrcatA(date_fmt_buf, "\n");
+ return TRUE;
+}
+
+static void test_EnumDateFormatsA(void)
+{
+ char *p, buf[256];
+ BOOL ret;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+ trace("EnumDateFormatsA 0\n");
+ date_fmt_buf[0] = 0;
+ ret = EnumDateFormatsA(enum_datetime_procA, lcid, 0);
+ ok(ret, "EnumDateFormatsA(0) error %ld\n", GetLastError());
+ trace("%s\n", date_fmt_buf);
+ /* test the 1st enumerated format */
+ if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
+ ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
+ ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %ld\n", GetLastError());
+ ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
+
+ trace("EnumDateFormatsA LOCALE_USE_CP_ACP\n");
+ date_fmt_buf[0] = 0;
+ ret = EnumDateFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
+ ok(ret, "EnumDateFormatsA(LOCALE_USE_CP_ACP) error %ld\n", GetLastError());
+ trace("%s\n", date_fmt_buf);
+ /* test the 1st enumerated format */
+ if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
+ ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
+ ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %ld\n", GetLastError());
+ ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
+
+ trace("EnumDateFormatsA DATE_SHORTDATE\n");
+ date_fmt_buf[0] = 0;
+ ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_SHORTDATE);
+ ok(ret, "EnumDateFormatsA(DATE_SHORTDATE) error %ld\n", GetLastError());
+ trace("%s\n", date_fmt_buf);
+ /* test the 1st enumerated format */
+ if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
+ ret = GetLocaleInfoA(lcid, LOCALE_SSHORTDATE, buf, sizeof(buf));
+ ok(ret, "GetLocaleInfoA(LOCALE_SSHORTDATE) error %ld\n", GetLastError());
+ ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
+
+ trace("EnumDateFormatsA DATE_LONGDATE\n");
+ date_fmt_buf[0] = 0;
+ ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_LONGDATE);
+ ok(ret, "EnumDateFormatsA(DATE_LONGDATE) error %ld\n", GetLastError());
+ trace("%s\n", date_fmt_buf);
+ /* test the 1st enumerated format */
+ if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
+ ret = GetLocaleInfoA(lcid, LOCALE_SLONGDATE, buf, sizeof(buf));
+ ok(ret, "GetLocaleInfoA(LOCALE_SLONGDATE) error %ld\n", GetLastError());
+ ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
+
+ trace("EnumDateFormatsA DATE_YEARMONTH\n");
+ date_fmt_buf[0] = 0;
+ ret = EnumDateFormatsA(enum_datetime_procA, lcid, DATE_YEARMONTH);
+ ok(ret, "EnumDateFormatsA(DATE_YEARMONTH) error %ld\n", GetLastError());
+ trace("%s\n", date_fmt_buf);
+ /* test the 1st enumerated format */
+ if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
+ ret = GetLocaleInfoA(lcid, LOCALE_SYEARMONTH, buf, sizeof(buf));
+ ok(ret, "GetLocaleInfoA(LOCALE_SYEARMONTH) error %ld\n", GetLastError());
+ ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
+}
+
+static void test_EnumTimeFormatsA(void)
+{
+ char *p, buf[256];
+ BOOL ret;
+ LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+ trace("EnumTimeFormatsA 0\n");
+ date_fmt_buf[0] = 0;
+ ret = EnumTimeFormatsA(enum_datetime_procA, lcid, 0);
+ ok(ret, "EnumTimeFormatsA(0) error %ld\n", GetLastError());
+ trace("%s\n", date_fmt_buf);
+ /* test the 1st enumerated format */
+ if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
+ ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
+ ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %ld\n", GetLastError());
+ ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
+
+ trace("EnumTimeFormatsA LOCALE_USE_CP_ACP\n");
+ date_fmt_buf[0] = 0;
+ ret = EnumTimeFormatsA(enum_datetime_procA, lcid, LOCALE_USE_CP_ACP);
+ ok(ret, "EnumTimeFormatsA(LOCALE_USE_CP_ACP) error %ld\n", GetLastError());
+ trace("%s\n", date_fmt_buf);
+ /* test the 1st enumerated format */
+ if ((p = strchr(date_fmt_buf, '\n'))) *p = 0;
+ ret = GetLocaleInfoA(lcid, LOCALE_STIMEFORMAT, buf, sizeof(buf));
+ ok(ret, "GetLocaleInfoA(LOCALE_STIMEFORMAT) error %ld\n", GetLastError());
+ ok(!lstrcmpA(date_fmt_buf, buf), "expected \"%s\" got \"%s\"\n", date_fmt_buf, buf);
+}
+
+START_TEST(locale)
+{
+ InitFunctionPointers();
+
+ test_EnumTimeFormatsA();
+ test_EnumDateFormatsA();
+
+ test_GetLocaleInfoA();
+ test_GetTimeFormatA();
+ test_GetDateFormatA();
+ test_GetDateFormatW();
+ test_GetCurrencyFormatA(); /* Also tests the W version */
+ test_GetNumberFormatA(); /* Also tests the W version */
+ test_CompareStringA();
+ test_LCMapStringA();
+ test_LCMapStringW();
+ test_FoldStringA();
+ test_FoldStringW();
+ test_ConvertDefaultLocale();
+ test_EnumSystemLanguageGroupsA();
+ test_EnumLanguageGroupLocalesA();
+ test_SetLocaleInfoA();
+ test_EnumUILanguageA();
+
+ /* this requires collation table patch to make it MS compatible */
+ if (0) test_sorting();
+}
--- /dev/null
+/*
+ * Mailslot regression test
+ *
+ * Copyright 2003 Mike McCormack
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <windef.h>
+#include <winbase.h>
+
+#include "wine/test.h"
+
+static const char szmspath[] = "\\\\.\\mailslot\\wine_mailslot_test";
+
+static int mailslot_test(void)
+{
+ HANDLE hSlot, hSlot2, hWriter, hWriter2;
+ unsigned char buffer[16];
+ DWORD count, dwMax, dwNext, dwMsgCount, dwTimeout;
+
+ /* sanity check on GetMailslotInfo */
+ dwMax = dwNext = dwMsgCount = dwTimeout = 0;
+ ok( !GetMailslotInfo( INVALID_HANDLE_VALUE, &dwMax, &dwNext,
+ &dwMsgCount, &dwTimeout ), "getmailslotinfo succeeded\n");
+
+ /* open a mailslot that doesn't exist */
+ hWriter = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter == INVALID_HANDLE_VALUE, "nonexistent mailslot\n");
+
+ /* open a mailslot without the right name */
+ hSlot = CreateMailslot( "blah", 0, 0, NULL );
+ ok( hSlot == INVALID_HANDLE_VALUE,
+ "Created mailslot with invalid name\n");
+ ok( GetLastError() == ERROR_INVALID_NAME,
+ "error should be ERROR_INVALID_NAME\n");
+
+ /* open a mailslot with a null name */
+ hSlot = CreateMailslot( NULL, 0, 0, NULL );
+ ok( hSlot == INVALID_HANDLE_VALUE,
+ "Created mailslot with invalid name\n");
+ ok( GetLastError() == ERROR_PATH_NOT_FOUND,
+ "error should be ERROR_PATH_NOT_FOUND\n");
+
+ /* valid open, but with wacky parameters ... then check them */
+ hSlot = CreateMailslot( szmspath, -1, -1, NULL );
+ ok( hSlot != INVALID_HANDLE_VALUE , "mailslot with valid name failed\n");
+ dwMax = dwNext = dwMsgCount = dwTimeout = 0;
+ ok( GetMailslotInfo( hSlot, &dwMax, &dwNext, &dwMsgCount, &dwTimeout ),
+ "getmailslotinfo failed\n");
+ ok( dwMax == ~0UL, "dwMax incorrect\n");
+ ok( dwNext == MAILSLOT_NO_MESSAGE, "dwNext incorrect\n");
+ ok( dwMsgCount == 0, "dwMsgCount incorrect\n");
+ ok( dwTimeout == ~0UL, "dwTimeout incorrect\n");
+ ok( GetMailslotInfo( hSlot, NULL, NULL, NULL, NULL ),
+ "getmailslotinfo failed\n");
+ ok( CloseHandle(hSlot), "failed to close mailslot\n");
+
+ /* now open it for real */
+ hSlot = CreateMailslot( szmspath, 0, 0, NULL );
+ ok( hSlot != INVALID_HANDLE_VALUE , "valid mailslot failed\n");
+
+ /* try and read/write to it */
+ count = 0;
+ memset(buffer, 0, sizeof buffer);
+ ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "slot read\n");
+ ok( !WriteFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "slot write\n");
+
+ /* now try and openthe client, but with the wrong sharing mode */
+ hWriter = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter == INVALID_HANDLE_VALUE, "bad sharing mode\n");
+ ok( GetLastError() == ERROR_SHARING_VIOLATION,
+ "error should be ERROR_SHARING_VIOLATION\n");
+
+ /* now open the client with the correct sharing mode */
+ hWriter = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter != INVALID_HANDLE_VALUE, "existing mailslot\n");
+
+ /*
+ * opening a client should make no difference to
+ * whether we can read or write the mailslot
+ */
+ ok( !ReadFile( hSlot, buffer, sizeof buffer/2, &count, NULL),
+ "slot read\n");
+ ok( !WriteFile( hSlot, buffer, sizeof buffer/2, &count, NULL),
+ "slot write\n");
+
+ /*
+ * we can't read from this client,
+ * but we should be able to write to it
+ */
+ ok( !ReadFile( hWriter, buffer, sizeof buffer/2, &count, NULL),
+ "can read client\n");
+ ok( WriteFile( hWriter, buffer, sizeof buffer/2, &count, NULL),
+ "can't write client\n");
+ ok( !ReadFile( hWriter, buffer, sizeof buffer/2, &count, NULL),
+ "can read client\n");
+
+ /*
+ * seeing as there's something in the slot,
+ * we should be able to read it once
+ */
+ ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "slot read\n");
+ ok( count == (sizeof buffer/2), "short read\n" );
+
+ /* but not again */
+ ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "slot read\n");
+
+ /* now try open another writer... should fail */
+ hWriter2 = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter2 == INVALID_HANDLE_VALUE, "two writers\n");
+
+ /* now try open another as a reader ... also fails */
+ hWriter2 = CreateFile(szmspath, GENERIC_READ,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter2 == INVALID_HANDLE_VALUE, "writer + reader\n");
+
+ /* now try open another as a writer ... still fails */
+ hWriter2 = CreateFile(szmspath, GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter2 == INVALID_HANDLE_VALUE, "writer\n");
+
+ /* now open another one */
+ hSlot2 = CreateMailslot( szmspath, 0, 0, NULL );
+ ok( hSlot2 == INVALID_HANDLE_VALUE , "opened two mailslots\n");
+
+ /* close the client again */
+ ok( CloseHandle( hWriter ), "closing the client\n");
+
+ /*
+ * now try reopen it with slightly different permissions ...
+ * shared writing
+ */
+ hWriter = CreateFile(szmspath, GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter != INVALID_HANDLE_VALUE, "sharing writer\n");
+
+ /*
+ * now try open another as a writer ...
+ * but don't share with the first ... fail
+ */
+ hWriter2 = CreateFile(szmspath, GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter2 == INVALID_HANDLE_VALUE, "greedy writer succeeded\n");
+
+ /* now try open another as a writer ... and share with the first */
+ hWriter2 = CreateFile(szmspath, GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ ok( hWriter2 != INVALID_HANDLE_VALUE, "2nd sharing writer\n");
+
+ /* check the mailslot info */
+ dwMax = dwNext = dwMsgCount = dwTimeout = 0;
+ ok( GetMailslotInfo( hSlot, &dwMax, &dwNext, &dwMsgCount, &dwTimeout ),
+ "getmailslotinfo failed\n");
+ ok( dwNext == MAILSLOT_NO_MESSAGE, "dwNext incorrect\n");
+ ok( dwMax == 0, "dwMax incorrect\n");
+ ok( dwMsgCount == 0, "dwMsgCount incorrect\n");
+ ok( dwTimeout == 0, "dwTimeout incorrect\n");
+
+ /* check there's still no data */
+ ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL), "slot read\n");
+
+ /* write two messages */
+ buffer[0] = 'a';
+ ok( WriteFile( hWriter, buffer, 1, &count, NULL), "1st write failed\n");
+
+ /* check the mailslot info */
+ dwNext = dwMsgCount = 0;
+ ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+ "getmailslotinfo failed\n");
+ ok( dwNext == 1, "dwNext incorrect\n");
+ ok( dwMsgCount == 1, "dwMsgCount incorrect\n");
+
+ buffer[0] = 'b';
+ buffer[1] = 'c';
+ ok( WriteFile( hWriter2, buffer, 2, &count, NULL), "2nd write failed\n");
+
+ /* check the mailslot info */
+ dwNext = dwMsgCount = 0;
+ ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+ "getmailslotinfo failed\n");
+ ok( dwNext == 1, "dwNext incorrect\n");
+ todo_wine {
+ ok( dwMsgCount == 2, "dwMsgCount incorrect\n");
+ }
+
+ /* write a 3rd message with zero size */
+ ok( WriteFile( hWriter2, buffer, 0, &count, NULL), "3rd write failed\n");
+
+ /* check the mailslot info */
+ dwNext = dwMsgCount = 0;
+ ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+ "getmailslotinfo failed\n");
+ ok( dwNext == 1, "dwNext incorrect\n");
+ todo_wine {
+ ok( dwMsgCount == 3, "dwMsgCount incorrect\n");
+ }
+
+ buffer[0]=buffer[1]=0;
+
+ /*
+ * then check that they come out with the correct order and size,
+ * then the slot is empty
+ */
+ ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "1st slot read failed\n");
+ ok( count == 1, "failed to get 1st message\n");
+ ok( buffer[0] == 'a', "1st message wrong\n");
+
+ /* check the mailslot info */
+ dwNext = dwMsgCount = 0;
+ ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+ "getmailslotinfo failed\n");
+ ok( dwNext == 2, "dwNext incorrect\n");
+ todo_wine {
+ ok( dwMsgCount == 2, "dwMsgCount incorrect\n");
+ }
+
+ /* read the second message */
+ ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "2nd slot read failed\n");
+ ok( count == 2, "failed to get 2nd message\n");
+ ok( ( buffer[0] == 'b' ) && ( buffer[1] == 'c' ), "2nd message wrong\n");
+
+ /* check the mailslot info */
+ dwNext = dwMsgCount = 0;
+ ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+ "getmailslotinfo failed\n");
+ todo_wine {
+ ok( dwNext == 0, "dwNext incorrect\n");
+ ok( dwMsgCount == 1, "dwMsgCount incorrect\n");
+ }
+
+ /* read the 3rd (zero length) message */
+ todo_wine {
+ ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "3rd slot read failed\n");
+ }
+ ok( count == 0, "failed to get 3rd message\n");
+
+ /*
+ * now there should be no more messages
+ * check the mailslot info
+ */
+ dwNext = dwMsgCount = 0;
+ ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+ "getmailslotinfo failed\n");
+ ok( dwNext == MAILSLOT_NO_MESSAGE, "dwNext incorrect\n");
+ ok( dwMsgCount == 0, "dwMsgCount incorrect\n");
+
+ /* check that reads fail */
+ ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+ "3rd slot read succeeded\n");
+
+ /* finally close the mailslot and its client */
+ ok( CloseHandle( hWriter2 ), "closing 2nd client\n");
+ ok( CloseHandle( hWriter ), "closing the client\n");
+ ok( CloseHandle( hSlot ), "closing the mailslot\n");
+
+ return 0;
+}
+
+START_TEST(mailslot)
+{
+ mailslot_test();
+}
--- /dev/null
+/*
+ * Unit tests for module/DLL/library API
+ *
+ * Copyright (c) 2004 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include <windows.h>
+
+static BOOL is_unicode_enabled = TRUE;
+
+static BOOL cmpStrAW(const char* a, const WCHAR* b, DWORD lenA, DWORD lenB)
+{
+ WCHAR aw[1024];
+
+ DWORD len = MultiByteToWideChar( AreFileApisANSI() ? CP_ACP : CP_OEMCP, 0,
+ a, lenA, aw, sizeof(aw) / sizeof(aw[0]) );
+ if (len != lenB) return FALSE;
+ return memcmp(aw, b, len * sizeof(WCHAR)) == 0;
+}
+
+static void testGetModuleFileName(const char* name)
+{
+ HMODULE hMod;
+ char bufA[MAX_PATH];
+ WCHAR bufW[MAX_PATH];
+ DWORD len1A, len1W = 0, len2A, len2W = 0;
+
+ hMod = (name) ? GetModuleHandle(name) : NULL;
+
+ /* first test, with enough space in buffer */
+ memset(bufA, '-', sizeof(bufA));
+ len1A = GetModuleFileNameA(hMod, bufA, sizeof(bufA));
+ ok(len1A > 0, "Getting module filename for handle %p\n", hMod);
+
+ if (is_unicode_enabled)
+ {
+ memset(bufW, '-', sizeof(bufW));
+ len1W = GetModuleFileNameW(hMod, bufW, sizeof(bufW) / sizeof(WCHAR));
+ ok(len1W > 0, "Getting module filename for handle %p\n", hMod);
+ }
+
+ ok(len1A == strlen(bufA), "Unexpected length of GetModuleFilenameA (%ld/%d)\n", len1A, strlen(bufA));
+
+ if (is_unicode_enabled)
+ {
+ ok(len1W == lstrlenW(bufW), "Unexpected length of GetModuleFilenameW (%ld/%d)\n", len1W, lstrlenW(bufW));
+ ok(cmpStrAW(bufA, bufW, len1A, len1W), "Comparing GetModuleFilenameAW results\n");
+ }
+
+ /* second test with a buffer too small */
+ memset(bufA, '-', sizeof(bufA));
+ len2A = GetModuleFileNameA(hMod, bufA, len1A / 2);
+ ok(len2A > 0, "Getting module filename for handle %p\n", hMod);
+
+ if (is_unicode_enabled)
+ {
+ memset(bufW, '-', sizeof(bufW));
+ len2W = GetModuleFileNameW(hMod, bufW, len1W / 2);
+ ok(len2W > 0, "Getting module filename for handle %p\n", hMod);
+ ok(cmpStrAW(bufA, bufW, len2A, len2W), "Comparing GetModuleFilenameAW results with buffer too small\n" );
+ ok(len1W / 2 == len2W, "Correct length in GetModuleFilenameW with buffer too small (%ld/%ld)\n", len1W / 2, len2W);
+ }
+
+ ok(len1A / 2 == len2A ||
+ len1A / 2 == len2A + 1, /* Win9x */
+ "Correct length in GetModuleFilenameA with buffer too small (%ld/%ld)\n", len1A / 2, len2A);
+}
+
+static void testGetModuleFileName_Wrong(void)
+{
+ char bufA[MAX_PATH];
+ WCHAR bufW[MAX_PATH];
+
+ /* test wrong handle */
+ if (is_unicode_enabled)
+ {
+ bufW[0] = '*';
+ ok(GetModuleFileNameW((void*)0xffffffff, bufW, sizeof(bufW) / sizeof(WCHAR)) == 0, "Unexpected success in module handle\n");
+ ok(bufW[0] == '*', "When failing, buffer shouldn't be written to\n");
+ }
+
+ bufA[0] = '*';
+ ok(GetModuleFileNameA((void*)0xffffffff, bufA, sizeof(bufA)) == 0, "Unexpected success in module handle\n");
+ ok(bufA[0] == '*' ||
+ bufA[0] == 0 /* Win9x */,
+ "When failing, buffer shouldn't be written to\n");
+}
+
+static void testLoadLibraryA(void)
+{
+ HMODULE hModule, hModule1;
+ FARPROC fp;
+
+ SetLastError(0xdeadbeef);
+ hModule = LoadLibraryA("kernel32.dll");
+ ok( hModule != NULL, "kernel32.dll should be loadable\n");
+ ok( GetLastError() == 0xdeadbeef, "GetLastError should be 0xdeadbeef but is %08lx\n", GetLastError());
+
+ fp = GetProcAddress(hModule, "CreateFileA");
+ ok( fp != NULL, "CreateFileA should be there\n");
+ ok( GetLastError() == 0xdeadbeef, "GetLastError should be 0xdeadbeef but is %08lx\n", GetLastError());
+
+ SetLastError(0xdeadbeef);
+ hModule1 = LoadLibraryA("kernel32 ");
+ /* Only winNT does this */
+ if (GetLastError() != ERROR_DLL_NOT_FOUND)
+ {
+ ok( hModule1 != NULL, "\"kernel32 \" should be loadable\n");
+ ok( GetLastError() == 0xdeadbeef, "GetLastError should be 0xdeadbeef but is %08lx\n", GetLastError());
+ ok( hModule == hModule1, "Loaded wrong module\n");
+ FreeLibrary(hModule1);
+ }
+ FreeLibrary(hModule);
+}
+
+static void testLoadLibraryA_Wrong(void)
+{
+ HMODULE hModule;
+
+ /* Try to load a nonexistent dll */
+ SetLastError(0xdeadbeef);
+ hModule = LoadLibraryA("non_ex_pv.dll");
+ ok( !hModule, "non_ex_pv.dll should be not loadable\n");
+ ok( GetLastError() == ERROR_MOD_NOT_FOUND || GetLastError() == ERROR_DLL_NOT_FOUND,
+ "Expected ERROR_MOD_NOT_FOUND or ERROR_DLL_NOT_FOUND (win9x), got %08lx\n", GetLastError());
+
+ /* Just in case */
+ FreeLibrary(hModule);
+}
+
+static void testGetProcAddress_Wrong(void)
+{
+ FARPROC fp;
+
+ SetLastError(0xdeadbeef);
+ fp = GetProcAddress(NULL, "non_ex_call");
+ ok( !fp, "non_ex_call should not be found\n");
+ ok( GetLastError() == ERROR_PROC_NOT_FOUND || GetLastError() == ERROR_INVALID_HANDLE,
+ "Expected ERROR_PROC_NOT_FOUND or ERROR_INVALID_HANDLE(win9x), got %08lx\n", GetLastError());
+
+ SetLastError(0xdeadbeef);
+ fp = GetProcAddress((HMODULE)0xdeadbeef, "non_ex_call");
+ ok( !fp, "non_ex_call should not be found\n");
+ ok( GetLastError() == ERROR_MOD_NOT_FOUND || GetLastError() == ERROR_INVALID_HANDLE,
+ "Expected ERROR_MOD_NOT_FOUND or ERROR_INVALID_HANDLE(win9x), got %08lx\n", GetLastError());
+}
+
+START_TEST(module)
+{
+ WCHAR filenameW[MAX_PATH];
+
+ /* Test if we can use GetModuleFileNameW */
+
+ SetLastError(0xdeadbeef);
+ GetModuleFileNameW(NULL, filenameW, MAX_PATH);
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ trace("GetModuleFileNameW not existing on this platform, skipping W-calls\n");
+ is_unicode_enabled = FALSE;
+ }
+
+ testGetModuleFileName(NULL);
+ testGetModuleFileName("kernel32.dll");
+ testGetModuleFileName_Wrong();
+
+ testLoadLibraryA();
+ testLoadLibraryA_Wrong();
+ testGetProcAddress_Wrong();
+}
--- /dev/null
+/*
+ * Unit test suite for Get*PathNamesA and (Get|Set)CurrentDirectoryA.
+ *
+ * Copyright 2002 Geoffrey Hausheer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winnls.h"
+
+#define HAS_TRAIL_SLASH_A(string) (string[lstrlenA(string)-1]=='\\')
+
+#define LONGFILE "Long File test.path"
+#define SHORTFILE "pathtest.pth"
+#define SHORTDIR "shortdir"
+#define LONGDIR "Long Directory"
+#define NONFILE_SHORT "noexist.pth"
+#define NONFILE_LONG "NonExistent File"
+#define NONDIR_SHORT "notadir"
+#define NONDIR_LONG "NonExistent Directory"
+
+#define NOT_A_VALID_DRIVE '@'
+
+/* the following characters don't work well with GetFullPathNameA
+ in Win98. I don't know if this is a FAT thing, or if it is an OS thing
+ but I don't test these characters now.
+ NOTE: Win2k allows GetFullPathNameA to work with them though
+ |<>"
+*/
+static const CHAR funny_chars[]="!@#$%^&*()=+{}[],?'`";
+static const CHAR is_char_ok[] ="11111110111111111011";
+
+static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR,LPSTR,DWORD);
+
+/* a structure to deal with wine todos somewhat cleanly */
+typedef struct {
+ DWORD shortlen;
+ DWORD shorterror;
+ DWORD s2llen;
+ DWORD s2lerror;
+ DWORD longlen;
+ DWORD longerror;
+} SLpassfail;
+
+/* function that tests GetFullPathNameA, GetShortPathNameA,GetLongPathNameA */
+/* NOTE: the passfail structure is used to allow cutomizeable todo checking
+ for wine. It is not very pretty, but it sure beats duplicating this
+ function lots of times
+*/
+static void test_ValidPathA(const CHAR *curdir, const CHAR *subdir, const CHAR *filename,
+ CHAR *shortstr, SLpassfail *passfail, const CHAR *errstr)
+{
+ CHAR tmpstr[MAX_PATH],
+ fullpath[MAX_PATH], /*full path to the file (not short/long) */
+ subpath[MAX_PATH], /*relative path to the file */
+ fullpathshort[MAX_PATH], /*absolue path to the file (short format) */
+ fullpathlong[MAX_PATH], /*absolute path to the file (long format) */
+ curdirshort[MAX_PATH], /*absolute path to the current dir (short) */
+ curdirlong[MAX_PATH]; /*absolute path to the current dir (long) */
+ LPSTR strptr; /*ptr to the filename portion of the path */
+ DWORD len;
+/* if passfail is NULL, we can perform all checks within this function,
+ otherwise, we will return the relevant data in the passfail struct, so
+ we must initialize it first
+*/
+ if(passfail!=NULL) {
+ passfail->shortlen=-1;passfail->s2llen=-1;passfail->longlen=-1;
+ passfail->shorterror=0;passfail->s2lerror=0;passfail->longerror=0;
+ }
+/* GetLongPathNameA is only supported on Win2k+ and Win98+ */
+ if(pGetLongPathNameA) {
+ ok((len=pGetLongPathNameA(curdir,curdirlong,MAX_PATH)),
+ "%s: GetLongPathNameA failed\n",errstr);
+/*GetLongPathNameA can return a trailing '\\' but shouldn't do so here */
+ ok(! HAS_TRAIL_SLASH_A(curdirlong),
+ "%s: GetLongPathNameA should not have a trailing \\\n",errstr);
+ }
+ ok((len=GetShortPathNameA(curdir,curdirshort,MAX_PATH)),
+ "%s: GetShortPathNameA failed\n",errstr);
+/*GetShortPathNameA can return a trailing '\\' but shouldn't do so here */
+ ok(! HAS_TRAIL_SLASH_A(curdirshort),
+ "%s: GetShortPathNameA should not have a trailing \\\n",errstr);
+/* build relative and absolute paths from inputs */
+ if(lstrlenA(subdir)) {
+ sprintf(subpath,"%s\\%s",subdir,filename);
+ } else {
+ lstrcpyA(subpath,filename);
+ }
+ sprintf(fullpath,"%s\\%s",curdir,subpath);
+ sprintf(fullpathshort,"%s\\%s",curdirshort,subpath);
+ sprintf(fullpathlong,"%s\\%s",curdirlong,subpath);
+/* Test GetFullPathNameA functionality */
+ len=GetFullPathNameA(subpath,MAX_PATH,tmpstr,&strptr);
+ ok(len, "GetFullPathNameA failed for: '%s'\n",subpath);
+ if(HAS_TRAIL_SLASH_A(subpath)) {
+ ok(strptr==NULL,
+ "%s: GetFullPathNameA should not return a filename ptr\n",errstr);
+ ok(lstrcmpiA(fullpath,tmpstr)==0,
+ "%s: GetFullPathNameA returned '%s' instead of '%s'\n",
+ errstr,tmpstr,fullpath);
+ } else {
+ ok(lstrcmpiA(strptr,filename)==0,
+ "%s: GetFullPathNameA returned '%s' instead of '%s'\n",
+ errstr,strptr,filename);
+ ok(lstrcmpiA(fullpath,tmpstr)==0,
+ "%s: GetFullPathNameA returned '%s' instead of '%s'\n",
+ errstr,tmpstr,fullpath);
+ }
+/* Test GetShortPathNameA functionality */
+ SetLastError(0);
+ len=GetShortPathNameA(fullpathshort,shortstr,MAX_PATH);
+ if(passfail==NULL) {
+ ok(len, "%s: GetShortPathNameA failed\n",errstr);
+ } else {
+ passfail->shortlen=len;
+ passfail->shorterror=GetLastError();
+ }
+/* Test GetLongPathNameA functionality
+ We test both conversion from GetFullPathNameA and from GetShortPathNameA
+*/
+ if(pGetLongPathNameA) {
+ if(len!=0) {
+ SetLastError(0);
+ len=pGetLongPathNameA(shortstr,tmpstr,MAX_PATH);
+ if(passfail==NULL) {
+ ok(len,
+ "%s: GetLongPathNameA failed during Short->Long conversion\n", errstr);
+ ok(lstrcmpiA(fullpathlong,tmpstr)==0,
+ "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
+ errstr,tmpstr,fullpathlong);
+ } else {
+ passfail->s2llen=len;
+ passfail->s2lerror=GetLastError();
+ }
+ }
+ SetLastError(0);
+ len=pGetLongPathNameA(fullpath,tmpstr,MAX_PATH);
+ if(passfail==NULL) {
+ ok(len, "%s: GetLongPathNameA failed\n",errstr);
+ if(HAS_TRAIL_SLASH_A(fullpath)) {
+ ok(lstrcmpiA(fullpathlong,tmpstr)==0,
+ "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
+ errstr,tmpstr,fullpathlong);
+ } else {
+ ok(lstrcmpiA(fullpathlong,tmpstr)==0,
+ "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
+ errstr,tmpstr,fullpathlong);
+ }
+ } else {
+ passfail->longlen=len;
+ passfail->longerror=GetLastError();
+ }
+ }
+}
+
+/* split path into leading directory, and 8.3 filename */
+static void test_SplitShortPathA(CHAR *path,CHAR *dir,CHAR *eight,CHAR *three) {
+ int done,error;
+ int ext,fil;
+ int len,i;
+ len=lstrlenA(path);
+ ext=len; fil=len; done=0; error=0;
+/* walk backwards over path looking for '.' or '\\' separators */
+ for(i=len-1;(i>=0) && (!done);i--) {
+ if(path[i]=='.')
+ if(ext!=len) error=1; else ext=i;
+ else if(path[i]=='\\') {
+ if(i==len-1) {
+ error=1;
+ } else {
+ fil=i;
+ done=1;
+ }
+ }
+ }
+/* Check that we didn't find a trailing '\\' or multiple '.' */
+ ok(!error,"Illegal file found in 8.3 path '%s'\n",path);
+/* Separate dir, root, and extension */
+ if(ext!=len) lstrcpyA(three,path+ext+1); else lstrcpyA(three,"");
+ if(fil!=len) {
+ lstrcpynA(eight,path+fil+1,ext-fil);
+ lstrcpynA(dir,path,fil+1);
+ } else {
+ lstrcpynA(eight,path,ext+1);
+ lstrcpyA(dir,"");
+ }
+/* Validate that root and extension really are 8.3 */
+ ok(lstrlenA(eight)<=8 && lstrlenA(three)<=3,
+ "GetShortPathNAmeA did not return an 8.3 path\n");
+}
+
+/* Check that GetShortPathNameA returns a valid 8.3 path */
+static void test_LongtoShortA(CHAR *teststr,const CHAR *goodstr,
+ const CHAR *ext,const CHAR *errstr) {
+ CHAR dir[MAX_PATH],eight[MAX_PATH],three[MAX_PATH];
+
+ test_SplitShortPathA(teststr,dir,eight,three);
+ ok(lstrcmpiA(dir,goodstr)==0,
+ "GetShortPathNameA returned '%s' instead of '%s'\n",dir,goodstr);
+ ok(lstrcmpiA(three,ext)==0,
+ "GetShortPathNameA returned '%s' with incorrect extension\n",three);
+}
+
+/* Test that Get(Short|Long|Full)PathNameA work correctly with interesting
+ characters in the filename.
+ 'valid' indicates whether this would be an allowed filename
+ 'todo' indicates that wine doesn't get this right yet.
+ NOTE: We always call this routine with a nonexistent filename, so
+ Get(Short|Long)PathNameA should never pass, but GetFullPathNameA
+ should.
+*/
+static void test_FunnyChars(CHAR *curdir,CHAR *curdir_short,CHAR *filename, INT valid,CHAR *errstr)
+{
+ CHAR tmpstr[MAX_PATH],tmpstr1[MAX_PATH];
+ SLpassfail passfail;
+
+ test_ValidPathA(curdir,"",filename,tmpstr,&passfail,errstr);
+ if(valid) {
+ sprintf(tmpstr1,"%s\\%s",curdir_short,filename);
+ ok((passfail.shortlen==0 &&
+ (passfail.shorterror==ERROR_FILE_NOT_FOUND || passfail.shorterror==ERROR_PATH_NOT_FOUND || !passfail.shorterror)) ||
+ (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+ "%s: GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+ errstr,passfail.shortlen,passfail.shorterror,tmpstr);
+ } else {
+ ok(passfail.shortlen==0 &&
+ (passfail.shorterror==ERROR_INVALID_NAME || passfail.shorterror==ERROR_FILE_NOT_FOUND || !passfail.shorterror),
+ "%s: GetShortPathA should have failed len=%ld, error=%ld\n",
+ errstr,passfail.shortlen,passfail.shorterror);
+ }
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ if(valid) {
+ ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "%s: GetLongPathA returned %ld and not %ld\n",
+ errstr,passfail.longerror,(DWORD) ERROR_FILE_NOT_FOUND);
+ } else {
+ ok(passfail.longerror==ERROR_INVALID_NAME ||
+ passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "%s: GetLongPathA returned %ld and not %ld or %ld'\n",
+ errstr, passfail.longerror,(DWORD) ERROR_INVALID_NAME,(DWORD)ERROR_FILE_NOT_FOUND);
+ }
+ }
+}
+
+/* Routine to test that SetCurrentDirectory behaves as expected. */
+static void test_setdir(CHAR *olddir,CHAR *newdir,
+ CHAR *cmprstr, INT pass, const CHAR *errstr)
+{
+ CHAR tmppath[MAX_PATH], *dirptr;
+ DWORD val,len,chklen;
+
+ val=SetCurrentDirectoryA(newdir);
+ len=GetCurrentDirectoryA(MAX_PATH,tmppath);
+/* if 'pass' then the SetDirectoryA was supposed to pass */
+ if(pass) {
+ dirptr=(cmprstr==NULL) ? newdir : cmprstr;
+ chklen=lstrlenA(dirptr);
+ ok(val,"%s: SetCurrentDirectoryA failed\n",errstr);
+ ok(len==chklen,
+ "%s: SetCurrentDirectory did not change the directory, though it passed\n",
+ errstr);
+ ok(lstrcmpiA(dirptr,tmppath)==0,
+ "%s: SetCurrentDirectory did not change the directory, though it passed\n",
+ errstr);
+ ok(SetCurrentDirectoryA(olddir),
+ "%s: Couldn't set directory to it's original value\n",errstr);
+ } else {
+/* else thest that it fails correctly */
+ chklen=lstrlenA(olddir);
+ ok(val==0,
+ "%s: SetCurrentDirectoryA passed when it should have failed\n",errstr);
+ ok(len==chklen,
+ "%s: SetCurrentDirectory changed the directory, though it failed\n",
+ errstr);
+ ok(lstrcmpiA(olddir,tmppath)==0,
+ "%s: SetCurrentDirectory changed the directory, though it failed\n",
+ errstr);
+ }
+}
+static void test_InitPathA(CHAR *newdir, CHAR *curDrive, CHAR *otherDrive)
+{
+ CHAR tmppath[MAX_PATH], /*path to TEMP */
+ tmpstr[MAX_PATH],
+ tmpstr1[MAX_PATH];
+ DWORD len,len1,drives;
+ INT id;
+ HANDLE hndl;
+ BOOL bRes;
+
+ *curDrive = *otherDrive = NOT_A_VALID_DRIVE;
+
+/* Get the current drive letter */
+ if( GetCurrentDirectoryA( MAX_PATH, tmpstr))
+ *curDrive = tmpstr[0];
+ else
+ trace( "Unable to discover current drive, some tests will not be conducted.\n");
+
+/* Test GetTempPathA */
+ len=GetTempPathA(MAX_PATH,tmppath);
+ ok(len!=0 && len < MAX_PATH,"GetTempPathA failed\n");
+ ok(HAS_TRAIL_SLASH_A(tmppath),
+ "GetTempPathA returned a path that did not end in '\\'\n");
+ lstrcpyA(tmpstr,"aaaaaaaa");
+ len1=GetTempPathA(len,tmpstr);
+ ok(len1==len+1,
+ "GetTempPathA should return string length %ld instead of %ld\n",len+1,len1);
+
+/* Test GetTmpFileNameA
+ The only test we do here is whether GetTempFileNameA passes or not.
+ We do not thoroughly test this function yet (specifically, whether
+ it behaves correctly when 'unique' is non zero)
+*/
+ ok((id=GetTempFileNameA(tmppath,"path",0,newdir)),"GetTempFileNameA failed\n");
+ sprintf(tmpstr,"pat%.4x.tmp",id & 0xffff);
+ sprintf(tmpstr1,"pat%x.tmp",id & 0xffff);
+ ok(lstrcmpiA(newdir+lstrlenA(tmppath),tmpstr)==0 ||
+ lstrcmpiA(newdir+lstrlenA(tmppath),tmpstr1)==0,
+ "GetTempFileNameA returned '%s' which doesn't match '%s' or '%s'. id=%x\n",
+ newdir,tmpstr,tmpstr1,id);
+
+ ok((id=GetTempFileNameA(tmppath,NULL,0,newdir)),"GetTempFileNameA failed\n");
+ sprintf(tmpstr,"%.4x.tmp",id & 0xffff);
+ sprintf(tmpstr1,"%x.tmp",id & 0xffff);
+ ok(lstrcmpiA(newdir+lstrlenA(tmppath),tmpstr)==0 ||
+ lstrcmpiA(newdir+lstrlenA(tmppath),tmpstr1)==0,
+ "GetTempFileNameA returned '%s' which doesn't match '%s' or '%s'. id=%x\n",
+ newdir,tmpstr,tmpstr1,id);
+
+
+/* Find first valid drive letter that is neither newdir[0] nor curDrive */
+ drives = GetLogicalDrives() & ~(1<<(newdir[0]-'A'));
+ if( *curDrive != NOT_A_VALID_DRIVE)
+ drives &= ~(1<<(*curDrive-'A'));
+ if( drives)
+ for( *otherDrive='A'; (drives & 1) == 0; drives>>=1, (*otherDrive)++);
+ else
+ trace( "Could not find alternative drive, some tests will not be conducted.\n");
+
+/* Do some CreateDirectoryA tests */
+/* It would be nice to do test the SECURITY_ATTRIBUTES, but I don't
+ really understand how they work.
+ More formal tests should be done along with CreateFile tests
+*/
+ ok(CreateDirectoryA(newdir,NULL)==0,
+ "CreateDirectoryA succeeded even though a file of the same name exists\n");
+ ok(DeleteFileA(newdir),"Couldn't delete the temporary file we just created\n");
+ ok(CreateDirectoryA(newdir,NULL),"CreateDirectoryA failed\n");
+/* Create some files to test other functions. Note, we will test CreateFileA
+ at some later point
+*/
+ sprintf(tmpstr,"%s\\%s",newdir,SHORTDIR);
+ ok(CreateDirectoryA(tmpstr,NULL),"CreateDirectoryA failed\n");
+ sprintf(tmpstr,"%s\\%s",newdir,LONGDIR);
+ ok(CreateDirectoryA(tmpstr,NULL),"CreateDirectoryA failed\n");
+ bRes = CreateDirectoryA("c:",NULL);
+ ok(!bRes && (GetLastError() == ERROR_ACCESS_DENIED ||
+ GetLastError() == ERROR_ALREADY_EXISTS),
+ "CreateDirectoryA(\"c:\" should have failed (%ld)\n", GetLastError());
+ bRes = CreateDirectoryA("c:\\",NULL);
+ ok(!bRes && (GetLastError() == ERROR_ACCESS_DENIED ||
+ GetLastError() == ERROR_ALREADY_EXISTS),
+ "CreateDirectoryA(\"c:\\\" should have failed (%ld)\n", GetLastError());
+ sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,SHORTFILE);
+ hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+ CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+ ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+ ok(CloseHandle(hndl),"CloseHandle failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,LONGFILE);
+ hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+ CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+ ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+ ok(CloseHandle(hndl),"CloseHandle failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",newdir,LONGDIR,SHORTFILE);
+ hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+ CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+ ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+ ok(CloseHandle(hndl),"CloseHandle failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",newdir,LONGDIR,LONGFILE);
+ hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+ CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+ ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+ ok(CloseHandle(hndl),"CloseHandle failed\n");
+}
+
+/* Test GetCurrentDirectory & SetCurrentDirectory */
+static void test_CurrentDirectoryA(CHAR *origdir, CHAR *newdir)
+{
+ CHAR tmpstr[MAX_PATH],tmpstr1[MAX_PATH];
+ DWORD len,len1;
+/* Save the original directory, so that we can return to it at the end
+ of the test
+*/
+ len=GetCurrentDirectoryA(MAX_PATH,origdir);
+ ok(len!=0 && len < MAX_PATH,"GetCurrentDirectoryA failed\n");
+/* Make sure that CetCurrentDirectoryA doesn't overwrite the buffer when the
+ buffer size is too small to hold the current directory
+*/
+ lstrcpyA(tmpstr,"aaaaaaa");
+ len1=GetCurrentDirectoryA(len,tmpstr);
+ ok(len1==len+1, "GetCurrentDirectoryA returned %ld instead of %ld\n",len1,len+1);
+ ok(lstrcmpiA(tmpstr,"aaaaaaa")==0,
+ "GetCurrentDirectoryA should not have modified the buffer\n");
+/* SetCurrentDirectoryA shouldn't care whether the string has a
+ trailing '\\' or not
+*/
+ sprintf(tmpstr,"%s\\",newdir);
+ test_setdir(origdir,tmpstr,newdir,1,"check 1");
+ test_setdir(origdir,newdir,NULL,1,"check 2");
+/* Set the directory to the working area. We just tested that this works,
+ so why check it again.
+*/
+ SetCurrentDirectoryA(newdir);
+/* Check that SetCurrentDirectory fails when a nonexistent dir is specified */
+ sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,NONDIR_SHORT);
+ test_setdir(newdir,tmpstr,NULL,0,"check 3");
+/* Check that SetCurrentDirectory fails for a nonexistent lond directory */
+ sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,NONDIR_LONG);
+ test_setdir(newdir,tmpstr,NULL,0,"check 4");
+/* Check that SetCurrentDirectory passes with a long directory */
+ sprintf(tmpstr,"%s\\%s",newdir,LONGDIR);
+ test_setdir(newdir,tmpstr,NULL,1,"check 5");
+/* Check that SetCurrentDirectory passes with a short relative directory */
+ sprintf(tmpstr,"%s",SHORTDIR);
+ sprintf(tmpstr1,"%s\\%s",newdir,SHORTDIR);
+ test_setdir(newdir,tmpstr,tmpstr1,1,"check 6");
+/* starting with a '.' */
+ sprintf(tmpstr,".\\%s",SHORTDIR);
+ test_setdir(newdir,tmpstr,tmpstr1,1,"check 7");
+/* Check that SetCurrentDirectory passes with a short relative directory */
+ sprintf(tmpstr,"%s",LONGDIR);
+ sprintf(tmpstr1,"%s\\%s",newdir,LONGDIR);
+ test_setdir(newdir,tmpstr,tmpstr1,1,"check 8");
+/* starting with a '.' */
+ sprintf(tmpstr,".\\%s",LONGDIR);
+ test_setdir(newdir,tmpstr,tmpstr1,1,"check 9");
+}
+
+/* Cleanup the mess we made while executing these tests */
+static void test_CleanupPathA(CHAR *origdir, CHAR *curdir)
+{
+ CHAR tmpstr[MAX_PATH];
+ sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,SHORTFILE);
+ ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,LONGFILE);
+ ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",curdir,LONGDIR,SHORTFILE);
+ ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",curdir,LONGDIR,LONGFILE);
+ ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+ sprintf(tmpstr,"%s\\%s",curdir,SHORTDIR);
+ ok(RemoveDirectoryA(tmpstr),"RemoveDirectoryA failed\n");
+ sprintf(tmpstr,"%s\\%s",curdir,LONGDIR);
+ ok(RemoveDirectoryA(tmpstr),"RemoveDirectoryA failed\n");
+ ok(SetCurrentDirectoryA(origdir),"SetCurrentDirectoryA failed\n");
+ ok(RemoveDirectoryA(curdir),"RemoveDirectoryA failed\n");
+}
+
+/* This routine will test Get(Full|Short|Long)PathNameA */
+static void test_PathNameA(CHAR *curdir, CHAR curDrive, CHAR otherDrive)
+{
+ CHAR curdir_short[MAX_PATH],
+ longdir_short[MAX_PATH];
+ CHAR tmpstr[MAX_PATH],tmpstr1[MAX_PATH],tmpstr2[MAX_PATH];
+ LPSTR strptr; /*ptr to the filename portion of the path */
+ DWORD len;
+ INT i;
+ CHAR dir[MAX_PATH],eight[MAX_PATH],three[MAX_PATH];
+ SLpassfail passfail;
+
+/* Get the short form of the current directory */
+ ok((len=GetShortPathNameA(curdir,curdir_short,MAX_PATH)),
+ "GetShortPathNameA failed\n");
+ ok(!HAS_TRAIL_SLASH_A(curdir_short),
+ "GetShortPathNameA should not have a trailing \\\n");
+/* Get the short form of the absolute-path to LONGDIR */
+ sprintf(tmpstr,"%s\\%s",curdir_short,LONGDIR);
+ ok((len=GetShortPathNameA(tmpstr,longdir_short,MAX_PATH)),
+ "GetShortPathNameA failed\n");
+ ok(lstrcmpiA(longdir_short+(len-1),"\\")!=0,
+ "GetShortPathNameA should not have a trailing \\\n");
+
+ if (pGetLongPathNameA) {
+ DWORD rc1,rc2;
+ sprintf(tmpstr,"%s\\%s\\%s",curdir,LONGDIR,LONGFILE);
+ rc1=(*pGetLongPathNameA)(tmpstr,NULL,0);
+ rc2=(*pGetLongPathNameA)(curdir,NULL,0);
+ ok((rc1-strlen(tmpstr))==(rc2-strlen(curdir)),
+ "GetLongPathNameA: wrong return code, %ld instead of %d\n",
+ rc1, strlen(tmpstr)+1);
+
+ sprintf(dir,"%c:",curDrive);
+ rc1=(*pGetLongPathNameA)(dir,tmpstr,sizeof(tmpstr));
+ ok(strcmp(dir,tmpstr)==0,
+ "GetLongPathNameA: returned '%s' instead of '%s' (rc=%ld)\n",
+ tmpstr,dir,rc1);
+ }
+
+/* Check the cases where both file and directory exist first */
+/* Start with a 8.3 directory, 8.3 filename */
+ test_ValidPathA(curdir,SHORTDIR,SHORTFILE,tmpstr,NULL,"test1");
+ sprintf(tmpstr1,"%s\\%s\\%s",curdir_short,SHORTDIR,SHORTFILE);
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetShortPathNameA returned '%s' instead of '%s'\n",tmpstr,tmpstr1);
+/* Now try a 8.3 directory, long file name */
+ test_ValidPathA(curdir,SHORTDIR,LONGFILE,tmpstr,NULL,"test2");
+ sprintf(tmpstr1,"%s\\%s",curdir_short,SHORTDIR);
+ test_LongtoShortA(tmpstr,tmpstr1,"PAT","test2");
+/* Next is a long directory, 8.3 file */
+ test_ValidPathA(curdir,LONGDIR,SHORTFILE,tmpstr,NULL,"test3");
+ sprintf(tmpstr1,"%s\\%s",longdir_short,SHORTFILE);
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetShortPathNameA returned '%s' instead of '%s'\n",tmpstr,tmpstr1);
+/*Lastly a long directory, long file */
+ test_ValidPathA(curdir,LONGDIR,LONGFILE,tmpstr,NULL,"test4");
+ test_LongtoShortA(tmpstr,longdir_short,"PAT","test4");
+
+/* Now check all of the invalid file w/ valid directory combinations */
+/* Start with a 8.3 directory, 8.3 filename */
+ test_ValidPathA(curdir,SHORTDIR,NONFILE_SHORT,tmpstr,&passfail,"test5");
+ sprintf(tmpstr1,"%s\\%s\\%s",curdir_short,SHORTDIR,NONFILE_SHORT);
+ ok((passfail.shortlen==0 &&
+ (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+ (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+ "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+ passfail.shortlen,passfail.shorterror,tmpstr);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+ }
+/* Now try a 8.3 directory, long file name */
+ test_ValidPathA(curdir,SHORTDIR,NONFILE_LONG,tmpstr,&passfail,"test6");
+ ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+ ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+ !passfail.shorterror,
+ "GetShortPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+ }
+/* Next is a long directory, 8.3 file */
+ test_ValidPathA(curdir,LONGDIR,NONFILE_SHORT,tmpstr,&passfail,"test7");
+ sprintf(tmpstr2,"%s\\%s",curdir_short,LONGDIR);
+ GetShortPathNameA(tmpstr2,tmpstr1,MAX_PATH);
+ strcat(tmpstr1,"\\" NONFILE_SHORT);
+ ok((passfail.shortlen==0 &&
+ (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+ (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+ "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+ passfail.shortlen,passfail.shorterror,tmpstr);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+ }
+/*Lastly a long directory, long file */
+ test_ValidPathA(curdir,LONGDIR,NONFILE_LONG,tmpstr,&passfail,"test8");
+ ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+ ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+ !passfail.shorterror,
+ "GetShortPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+ }
+/* Now try again with directories that don't exist */
+/* 8.3 directory, 8.3 filename */
+ test_ValidPathA(curdir,NONDIR_SHORT,SHORTFILE,tmpstr,&passfail,"test9");
+ sprintf(tmpstr1,"%s\\%s\\%s",curdir_short,NONDIR_SHORT,SHORTFILE);
+ ok((passfail.shortlen==0 &&
+ (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+ (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+ "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+ passfail.shortlen,passfail.shorterror,tmpstr);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+ passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+ passfail.longerror);
+ }
+/* Now try a 8.3 directory, long file name */
+ test_ValidPathA(curdir,NONDIR_SHORT,LONGFILE,tmpstr,&passfail,"test10");
+ ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+ ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+ !passfail.shorterror,
+ "GetShortPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+ passfail.shorterror);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+ passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+ passfail.longerror);
+ }
+/* Next is a long directory, 8.3 file */
+ test_ValidPathA(curdir,NONDIR_LONG,SHORTFILE,tmpstr,&passfail,"test11");
+ ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+ ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+ !passfail.shorterror,
+ "GetShortPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+ passfail.shorterror);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+ passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+ passfail.longerror);
+ }
+/*Lastly a long directory, long file */
+ test_ValidPathA(curdir,NONDIR_LONG,LONGFILE,tmpstr,&passfail,"test12");
+ ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+ ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+ !passfail.shorterror,
+ "GetShortPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+ passfail.shorterror);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+ passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+ passfail.longerror);
+ }
+/* Next try directories ending with '\\' */
+/* Existing Directories */
+ sprintf(tmpstr,"%s\\",SHORTDIR);
+ test_ValidPathA(curdir,"",tmpstr,tmpstr1,NULL,"test13");
+ sprintf(tmpstr,"%s\\",LONGDIR);
+ test_ValidPathA(curdir,"",tmpstr,tmpstr1,NULL,"test14");
+/* Nonexistent directories */
+ sprintf(tmpstr,"%s\\",NONDIR_SHORT);
+ test_ValidPathA(curdir,"",tmpstr,tmpstr1,&passfail,"test15");
+ sprintf(tmpstr2,"%s\\%s",curdir_short,tmpstr);
+ ok((passfail.shortlen==0 &&
+ (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+ (passfail.shortlen==strlen(tmpstr2) && lstrcmpiA(tmpstr1,tmpstr2)==0),
+ "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+ passfail.shortlen,passfail.shorterror,tmpstr);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetLongPathA returned %ld and not 'ERROR_FILE_NOT_FOUND'\n",
+ passfail.longerror);
+ }
+ sprintf(tmpstr,"%s\\",NONDIR_LONG);
+ test_ValidPathA(curdir,"",tmpstr,tmpstr1,&passfail,"test16");
+ ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+ ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+ passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+ !passfail.shorterror,
+ "GetShortPathA returned %ld and not 'ERROR_FILE_NOT_FOUND'\n",
+ passfail.shorterror);
+ if(pGetLongPathNameA) {
+ ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+ ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+ "GetLongPathA returned %ld and not 'ERROR_FILE_NOT_FOUND'\n",
+ passfail.longerror);
+ }
+/* Test GetFullPathNameA with drive letters */
+ if( curDrive != NOT_A_VALID_DRIVE) {
+ sprintf(tmpstr,"%c:",curdir[0]);
+ ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr2,&strptr),
+ "GetFullPathNameA(%c:) failed\n", curdir[0]);
+ GetCurrentDirectoryA(MAX_PATH,tmpstr);
+ sprintf(tmpstr1,"%s\\",tmpstr);
+ ok(lstrcmpiA(tmpstr,tmpstr2)==0 || lstrcmpiA(tmpstr1,tmpstr2)==0,
+ "GetFullPathNameA(%c:) returned '%s' instead of '%s' or '%s'\n",
+ curdir[0],tmpstr2,tmpstr,tmpstr1);
+
+ sprintf(tmpstr,"%c:\\%s\\%s",curDrive,SHORTDIR,SHORTFILE);
+ ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+ ok(lstrcmpiA(SHORTFILE,strptr)==0,
+ "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+ }
+/* Without a leading slash, insert the current directory if on the current drive */
+ sprintf(tmpstr,"%c:%s\\%s",curdir[0],SHORTDIR,SHORTFILE);
+ ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,SHORTFILE);
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+ ok(lstrcmpiA(SHORTFILE,strptr)==0,
+ "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+/* Otherwise insert the missing leading slash */
+ if( otherDrive != NOT_A_VALID_DRIVE) {
+ sprintf(tmpstr,"%c:%s\\%s",otherDrive,SHORTDIR,SHORTFILE);
+ ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed for %s\n", tmpstr);
+ sprintf(tmpstr,"%c:\\%s\\%s",otherDrive,SHORTDIR,SHORTFILE);
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+ ok(lstrcmpiA(SHORTFILE,strptr)==0,
+ "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+ }
+/* Xilinx tools like to mix Unix and DOS formats, which Windows handles fine.
+ So test for them. */
+ if( curDrive != NOT_A_VALID_DRIVE) {
+ sprintf(tmpstr,"%c:/%s\\%s",curDrive,SHORTDIR,SHORTFILE);
+ ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+ sprintf(tmpstr,"%c:\\%s\\%s",curDrive,SHORTDIR,SHORTFILE);
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+ ok(lstrcmpiA(SHORTFILE,strptr)==0,
+ "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+ }
+/**/
+ sprintf(tmpstr,"%c:%s/%s",curdir[0],SHORTDIR,SHORTFILE);
+ ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+ sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,SHORTFILE);
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+ ok(lstrcmpiA(SHORTFILE,strptr)==0,
+ "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+/* Windows will insert a drive letter in front of an absolute UNIX path */
+ sprintf(tmpstr,"/%s/%s",SHORTDIR,SHORTFILE);
+ ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+ sprintf(tmpstr,"%c:\\%s\\%s",*tmpstr1,SHORTDIR,SHORTFILE);
+ ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+ "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+/* This passes in Wine because it still contains the pointer from the previous test */
+ ok(lstrcmpiA(SHORTFILE,strptr)==0,
+ "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+
+/* Now try some relative paths */
+ ok(GetShortPathNameA(LONGDIR,tmpstr,MAX_PATH),"GetShortPathNameA failed\n");
+ test_SplitShortPathA(tmpstr,dir,eight,three);
+ if(pGetLongPathNameA) {
+ ok(pGetLongPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetLongPathNameA failed\n");
+ ok(lstrcmpiA(tmpstr1,LONGDIR)==0,
+ "GetLongPathNameA returned '%s' instead of '%s'\n",tmpstr1,LONGDIR);
+ }
+ sprintf(tmpstr,".\\%s",LONGDIR);
+ ok(GetShortPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetShortPathNameA failed\n");
+ test_SplitShortPathA(tmpstr1,dir,eight,three);
+ ok(lstrcmpiA(dir,".")==0 || dir[0]=='\0',
+ "GetShortPathNameA did not keep relative directory [%s]\n",tmpstr1);
+ if(pGetLongPathNameA) {
+ ok(pGetLongPathNameA(tmpstr1,tmpstr1,MAX_PATH),"GetLongPathNameA failed %s\n",
+ tmpstr);
+ ok(lstrcmpiA(tmpstr1,tmpstr)==0,
+ "GetLongPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+ }
+/* Check out Get*PathNameA on some funny characters */
+ for(i=0;i<lstrlenA(funny_chars);i++) {
+ INT valid;
+ valid=(is_char_ok[i]=='0') ? 0 : 1;
+ sprintf(tmpstr1,"check%d-1",i);
+ sprintf(tmpstr,"file%c000.ext",funny_chars[i]);
+ test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
+ sprintf(tmpstr1,"check%d-2",i);
+ sprintf(tmpstr,"file000.e%ct",funny_chars[i]);
+ test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
+ sprintf(tmpstr1,"check%d-3",i);
+ sprintf(tmpstr,"%cfile000.ext",funny_chars[i]);
+ test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
+ sprintf(tmpstr1,"check%d-4",i);
+ sprintf(tmpstr,"file000%c.ext",funny_chars[i]);
+ test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
+ sprintf(tmpstr1,"check%d-5",i);
+ sprintf(tmpstr,"Long %c File",funny_chars[i]);
+ test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
+ sprintf(tmpstr1,"check%d-6",i);
+ sprintf(tmpstr,"%c Long File",funny_chars[i]);
+ test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
+ sprintf(tmpstr1,"check%d-7",i);
+ sprintf(tmpstr,"Long File %c",funny_chars[i]);
+ test_FunnyChars(curdir,curdir_short,tmpstr,valid,tmpstr1);
+ }
+}
+
+static void test_GetTempPathA(char* tmp_dir)
+{
+ DWORD len, len_with_null;
+ char buf[MAX_PATH];
+
+ len_with_null = strlen(tmp_dir) + 1;
+
+ lstrcpyA(buf, "foo");
+ len = GetTempPathA(MAX_PATH, buf);
+ ok(len <= MAX_PATH, "should fit into MAX_PATH\n");
+ ok(lstrcmpiA(buf, tmp_dir) == 0, "expected [%s], got [%s]\n",tmp_dir,buf);
+ ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+
+ /* Some versions of Windows touch the buffer, some don't so we don't
+ * test that. Also, NT sometimes exagerates the required buffer size
+ * so we cannot test for an exact match. Finally, the
+ * 'len_with_null - 1' case is so buggy on Windows it's not testable.
+ * For instance in some cases Win98 returns len_with_null - 1 instead
+ * of len_with_null.
+ */
+ len = GetTempPathA(1, buf);
+ ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+ len = GetTempPathA(0, NULL);
+ ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+ /* The call above gave us the buffer size that Windows thinks is needed
+ * so the next call should work
+ */
+ lstrcpyA(buf, "foo");
+ len = GetTempPathA(len, buf);
+ ok(lstrcmpiA(buf, tmp_dir) == 0, "expected [%s], got [%s]\n",tmp_dir,buf);
+ ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+}
+
+static void test_GetTempPathW(char* tmp_dir)
+{
+ DWORD len, len_with_null;
+ WCHAR buf[MAX_PATH];
+ WCHAR tmp_dirW[MAX_PATH];
+ static const WCHAR fooW[] = {'f','o','o',0};
+
+ MultiByteToWideChar(CP_ACP,0,tmp_dir,-1,tmp_dirW,sizeof(tmp_dirW)/sizeof(*tmp_dirW));
+ len_with_null = lstrlenW(tmp_dirW) + 1;
+
+ /* This one is different from ANSI version: ANSI version doesn't
+ * touch the buffer, unicode version usually truncates the buffer
+ * to zero size. NT still exagerates the required buffer size
+ * sometimes so we cannot test for an exact match. Finally, the
+ * 'len_with_null - 1' case is so buggy on Windows it's not testable.
+ * For instance on NT4 it will sometimes return a path without the
+ * trailing '\\' and sometimes return an error.
+ */
+
+ lstrcpyW(buf, fooW);
+ len = GetTempPathW(MAX_PATH, buf);
+ if (len==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(lstrcmpiW(buf, tmp_dirW) == 0, "GetTempPathW returned an incorrect temporary path\n");
+ ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+
+ lstrcpyW(buf, fooW);
+ len = GetTempPathW(1, buf);
+ ok(buf[0] == 0, "unicode version should truncate the buffer to zero size\n");
+ ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+ len = GetTempPathW(0, NULL);
+ ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+ lstrcpyW(buf, fooW);
+ len = GetTempPathW(len, buf);
+ ok(lstrcmpiW(buf, tmp_dirW) == 0, "GetTempPathW returned an incorrect temporary path\n");
+ ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+}
+
+static void test_GetTempPath(void)
+{
+ char save_TMP[MAX_PATH];
+ char windir[MAX_PATH];
+ char buf[MAX_PATH];
+
+ GetEnvironmentVariableA("TMP", save_TMP, sizeof(save_TMP));
+
+ /* test default configuration */
+ trace("TMP=%s\n", save_TMP);
+ strcpy(buf,save_TMP);
+ if (buf[strlen(buf)-1]!='\\')
+ strcat(buf,"\\");
+ test_GetTempPathA(buf);
+ test_GetTempPathW(buf);
+
+ /* TMP=C:\WINDOWS */
+ GetWindowsDirectoryA(windir, sizeof(windir));
+ SetEnvironmentVariableA("TMP", windir);
+ GetEnvironmentVariableA("TMP", buf, sizeof(buf));
+ trace("TMP=%s\n", buf);
+ strcat(windir,"\\");
+ test_GetTempPathA(windir);
+ test_GetTempPathW(windir);
+
+ /* TMP=C:\ */
+ GetWindowsDirectoryA(windir, sizeof(windir));
+ windir[3] = 0;
+ SetEnvironmentVariableA("TMP", windir);
+ GetEnvironmentVariableA("TMP", buf, sizeof(buf));
+ trace("TMP=%s\n", buf);
+ test_GetTempPathA(windir);
+ test_GetTempPathW(windir);
+
+ /* TMP=C: i.e. use current working directory of the specified drive */
+ GetWindowsDirectoryA(windir, sizeof(windir));
+ SetCurrentDirectoryA(windir);
+ windir[2] = 0;
+ SetEnvironmentVariableA("TMP", windir);
+ GetEnvironmentVariableA("TMP", buf, sizeof(buf));
+ trace("TMP=%s\n", buf);
+ GetWindowsDirectoryA(windir, sizeof(windir));
+ strcat(windir,"\\");
+ test_GetTempPathA(windir);
+ test_GetTempPathW(windir);
+
+ SetEnvironmentVariableA("TMP", save_TMP);
+}
+
+START_TEST(path)
+{
+ CHAR origdir[MAX_PATH],curdir[MAX_PATH], curDrive, otherDrive;
+ pGetLongPathNameA = (void*)GetProcAddress( GetModuleHandleA("kernel32.dll"),
+ "GetLongPathNameA" );
+ test_InitPathA(curdir, &curDrive, &otherDrive);
+ test_CurrentDirectoryA(origdir,curdir);
+ test_PathNameA(curdir, curDrive, otherDrive);
+ test_CleanupPathA(origdir,curdir);
+ test_GetTempPath();
+}
--- /dev/null
+/*
+ * Unit tests for named pipe functions in Wine
+ *
+ * Copyright (c) 2002 Dan Kegel
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winsock.h>
+#include <wtypes.h>
+#include <winerror.h>
+
+#include "wine/test.h"
+
+#define PIPENAME "\\\\.\\PiPe\\tests_pipe.c"
+
+#define NB_SERVER_LOOPS 8
+
+static HANDLE alarm_event;
+
+static void test_CreateNamedPipe(int pipemode)
+{
+ HANDLE hnp;
+ HANDLE hFile;
+ static const char obuf[] = "Bit Bucket";
+ static const char obuf2[] = "More bits";
+ char ibuf[32], *pbuf;
+ DWORD written;
+ DWORD readden;
+ DWORD avail;
+ DWORD lpmode;
+
+ if (pipemode == PIPE_TYPE_BYTE)
+ trace("test_CreateNamedPipe starting in byte mode\n");
+ else
+ trace("test_CreateNamedPipe starting in message mode\n");
+ /* Bad parameter checks */
+ hnp = CreateNamedPipe("not a named pipe", PIPE_ACCESS_DUPLEX, pipemode | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+
+ if (hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
+ /* Is this the right way to notify user of skipped tests? */
+ ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
+ "CreateNamedPipe not supported on this platform, skipping tests.\n");
+ return;
+ }
+ ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME,
+ "CreateNamedPipe should fail if name doesn't start with \\\\.\\pipe\n");
+
+ hnp = CreateNamedPipe(NULL,
+ PIPE_ACCESS_DUPLEX, pipemode | PIPE_WAIT,
+ 1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, NULL);
+ ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PATH_NOT_FOUND,
+ "CreateNamedPipe should fail if name is NULL\n");
+
+ hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+ ok(hFile == INVALID_HANDLE_VALUE
+ && GetLastError() == ERROR_FILE_NOT_FOUND,
+ "connecting to nonexistent named pipe should fail with ERROR_FILE_NOT_FOUND\n");
+
+ /* Functional checks */
+
+ hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, pipemode | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ ok(WaitNamedPipeA(PIPENAME, 2000), "WaitNamedPipe failed (%08lx)\n", GetLastError());
+
+ hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+ ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed (%08lx)\n", GetLastError());
+
+ /* don't try to do i/o if one side couldn't be opened, as it hangs */
+ if (hFile != INVALID_HANDLE_VALUE) {
+ HANDLE hFile2;
+
+ /* Make sure we can read and write a few bytes in both directions */
+ memset(ibuf, 0, sizeof(ibuf));
+ ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile\n");
+ ok(written == sizeof(obuf), "write file len 1\n");
+ ok(PeekNamedPipe(hFile, NULL, 0, NULL, &readden, NULL), "Peek\n");
+ ok(readden == sizeof(obuf), "peek 1 got %ld bytes\n", readden);
+ ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n");
+ ok(readden == sizeof(obuf), "read 1 got %ld bytes\n", readden);
+ ok(memcmp(obuf, ibuf, written) == 0, "content 1 check\n");
+
+ memset(ibuf, 0, sizeof(ibuf));
+ ok(WriteFile(hFile, obuf2, sizeof(obuf2), &written, NULL), "WriteFile\n");
+ ok(written == sizeof(obuf2), "write file len 2\n");
+ ok(PeekNamedPipe(hnp, NULL, 0, NULL, &readden, NULL), "Peek\n");
+ ok(readden == sizeof(obuf2), "peek 2 got %ld bytes\n", readden);
+ ok(PeekNamedPipe(hnp, (LPVOID)1, 0, NULL, &readden, NULL), "Peek\n");
+ ok(readden == sizeof(obuf2), "peek 2 got %ld bytes\n", readden);
+ ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n");
+ ok(readden == sizeof(obuf2), "read 2 got %ld bytes\n", readden);
+ ok(memcmp(obuf2, ibuf, written) == 0, "content 2 check\n");
+
+ /* Test reading of multiple writes */
+ memset(ibuf, 0, sizeof(ibuf));
+ ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile3a\n");
+ ok(written == sizeof(obuf), "write file len 3a\n");
+ ok(WriteFile(hnp, obuf2, sizeof(obuf2), &written, NULL), " WriteFile3b\n");
+ ok(written == sizeof(obuf2), "write file len 3b\n");
+ ok(PeekNamedPipe(hFile, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek3\n");
+ if (pipemode == PIPE_TYPE_BYTE) {
+ todo_wine {
+ /* should return all 23 bytes */
+ ok(readden == sizeof(obuf) + sizeof(obuf2), "peek3 got %ld bytes\n", readden);
+ }
+ }
+ else
+ ok(readden == sizeof(obuf), "peek3 got %ld bytes\n", readden);
+ if (avail != sizeof(obuf)) /* older Linux kernels only return the first write here */
+ ok(avail == sizeof(obuf) + sizeof(obuf2), "peek3 got %ld bytes available\n", avail);
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "pipe content 3a check\n");
+ if (pipemode == PIPE_TYPE_BYTE) {
+ todo_wine {
+ pbuf += sizeof(obuf);
+ ok(memcmp(obuf2, pbuf, sizeof(obuf2)) == 0, "pipe content 3b check\n");
+ }
+ }
+ ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n");
+ ok(readden == sizeof(obuf) + sizeof(obuf2), "read 3 got %ld bytes\n", readden);
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 3a check\n");
+ pbuf += sizeof(obuf);
+ ok(memcmp(obuf2, pbuf, sizeof(obuf2)) == 0, "content 3b check\n");
+
+ /* Multiple writes in the reverse direction */
+ memset(ibuf, 0, sizeof(ibuf));
+ ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile4a\n");
+ ok(written == sizeof(obuf), "write file len 4a\n");
+ ok(WriteFile(hFile, obuf2, sizeof(obuf2), &written, NULL), " WriteFile4b\n");
+ ok(written == sizeof(obuf2), "write file len 4b\n");
+ ok(PeekNamedPipe(hnp, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek4\n");
+ if (pipemode == PIPE_TYPE_BYTE) {
+ todo_wine {
+ /* should return all 23 bytes */
+ ok(readden == sizeof(obuf) + sizeof(obuf2), "peek4 got %ld bytes\n", readden);
+ }
+ }
+ else
+ ok(readden == sizeof(obuf), "peek4 got %ld bytes\n", readden);
+ if (avail != sizeof(obuf)) /* older Linux kernels only return the first write here */
+ ok(avail == sizeof(obuf) + sizeof(obuf2), "peek4 got %ld bytes available\n", avail);
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "pipe content 4a check\n");
+ if (pipemode == PIPE_TYPE_BYTE) {
+ todo_wine {
+ pbuf += sizeof(obuf);
+ ok(memcmp(obuf2, pbuf, sizeof(obuf2)) == 0, "pipe content 4b check\n");
+ }
+ }
+ ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n");
+ if (pipemode == PIPE_TYPE_BYTE) {
+ ok(readden == sizeof(obuf) + sizeof(obuf2), "read 4 got %ld bytes\n", readden);
+ }
+ else {
+ todo_wine {
+ ok(readden == sizeof(obuf), "read 4 got %ld bytes\n", readden);
+ }
+ }
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 4a check\n");
+ if (pipemode == PIPE_TYPE_BYTE) {
+ pbuf += sizeof(obuf);
+ ok(memcmp(obuf2, pbuf, sizeof(obuf2)) == 0, "content 4b check\n");
+ }
+
+ /* Test reading of multiple writes after a mode change
+ (CreateFile always creates a byte mode pipe) */
+ lpmode = PIPE_READMODE_MESSAGE;
+ if (pipemode == PIPE_TYPE_BYTE) {
+ /* trying to change the client end of a byte pipe to message mode should fail */
+ ok(!SetNamedPipeHandleState(hFile, &lpmode, NULL, NULL), "Change mode\n");
+ }
+ else {
+ todo_wine {
+ ok(SetNamedPipeHandleState(hFile, &lpmode, NULL, NULL), "Change mode\n");
+ }
+
+ memset(ibuf, 0, sizeof(ibuf));
+ ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile5a\n");
+ ok(written == sizeof(obuf), "write file len 3a\n");
+ ok(WriteFile(hnp, obuf2, sizeof(obuf2), &written, NULL), " WriteFile5b\n");
+ ok(written == sizeof(obuf2), "write file len 3b\n");
+ ok(PeekNamedPipe(hFile, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek5\n");
+ ok(readden == sizeof(obuf), "peek5 got %ld bytes\n", readden);
+ if (avail != sizeof(obuf)) /* older Linux kernels only return the first write here */
+ ok(avail == sizeof(obuf) + sizeof(obuf2), "peek5 got %ld bytes available\n", avail);
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 5a check\n");
+ ok(ReadFile(hFile, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n");
+ todo_wine {
+ ok(readden == sizeof(obuf), "read 5 got %ld bytes\n", readden);
+ }
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 5a check\n");
+
+ /* Multiple writes in the reverse direction */
+ /* the write of obuf2 from write4 should still be in the buffer */
+ ok(PeekNamedPipe(hnp, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek6a\n");
+ todo_wine {
+ ok(readden == sizeof(obuf2), "peek6a got %ld bytes\n", readden);
+ ok(avail == sizeof(obuf2), "peek6a got %ld bytes available\n", avail);
+ }
+ if (avail > 0) {
+ ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n");
+ ok(readden == sizeof(obuf2), "read 6a got %ld bytes\n", readden);
+ pbuf = ibuf;
+ ok(memcmp(obuf2, pbuf, sizeof(obuf2)) == 0, "content 6a check\n");
+ }
+ memset(ibuf, 0, sizeof(ibuf));
+ ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile6a\n");
+ ok(written == sizeof(obuf), "write file len 6a\n");
+ ok(WriteFile(hFile, obuf2, sizeof(obuf2), &written, NULL), " WriteFile6b\n");
+ ok(written == sizeof(obuf2), "write file len 6b\n");
+ ok(PeekNamedPipe(hnp, ibuf, sizeof(ibuf), &readden, &avail, NULL), "Peek6\n");
+ ok(readden == sizeof(obuf), "peek6 got %ld bytes\n", readden);
+ if (avail != sizeof(obuf)) /* older Linux kernels only return the first write here */
+ ok(avail == sizeof(obuf) + sizeof(obuf2), "peek6b got %ld bytes available\n", avail);
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 6a check\n");
+ ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL), "ReadFile\n");
+ todo_wine {
+ ok(readden == sizeof(obuf), "read 6b got %ld bytes\n", readden);
+ }
+ pbuf = ibuf;
+ ok(memcmp(obuf, pbuf, sizeof(obuf)) == 0, "content 6a check\n");
+ }
+
+ /* Picky conformance tests */
+
+ /* Verify that you can't connect to pipe again
+ * until server calls DisconnectNamedPipe+ConnectNamedPipe
+ * or creates a new pipe
+ * case 1: other client not yet closed
+ */
+ hFile2 = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+ ok(hFile2 == INVALID_HANDLE_VALUE,
+ "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail\n");
+ ok(GetLastError() == ERROR_PIPE_BUSY,
+ "connecting to named pipe before other client closes should fail with ERROR_PIPE_BUSY\n");
+
+ ok(CloseHandle(hFile), "CloseHandle\n");
+
+ /* case 2: other client already closed */
+ hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+ ok(hFile == INVALID_HANDLE_VALUE,
+ "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail\n");
+ ok(GetLastError() == ERROR_PIPE_BUSY,
+ "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail with ERROR_PIPE_BUSY\n");
+
+ ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+
+ /* case 3: server has called DisconnectNamedPipe but not ConnectNamed Pipe */
+ hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+ ok(hFile == INVALID_HANDLE_VALUE,
+ "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail\n");
+ ok(GetLastError() == ERROR_PIPE_BUSY,
+ "connecting to named pipe after other client closes but before ConnectNamedPipe should fail with ERROR_PIPE_BUSY\n");
+
+ /* to be complete, we'd call ConnectNamedPipe here and loop,
+ * but by default that's blocking, so we'd either have
+ * to turn on the uncommon nonblocking mode, or
+ * use another thread.
+ */
+ }
+
+ ok(CloseHandle(hnp), "CloseHandle\n");
+
+ trace("test_CreateNamedPipe returning\n");
+}
+
+static void test_CreateNamedPipe_instances_must_match(void)
+{
+ HANDLE hnp, hnp2;
+
+ /* Check no mismatch */
+ hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 2,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ hnp2 = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 2,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp2 != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ ok(CloseHandle(hnp), "CloseHandle\n");
+ ok(CloseHandle(hnp2), "CloseHandle\n");
+
+ /* Check nMaxInstances */
+ hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ hnp2 = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp2 == INVALID_HANDLE_VALUE
+ && GetLastError() == ERROR_PIPE_BUSY, "nMaxInstances not obeyed\n");
+
+ ok(CloseHandle(hnp), "CloseHandle\n");
+
+ /* Check PIPE_ACCESS_* */
+ hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 2,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ hnp2 = CreateNamedPipe(PIPENAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp2 == INVALID_HANDLE_VALUE
+ && GetLastError() == ERROR_ACCESS_DENIED, "PIPE_ACCESS_* mismatch allowed\n");
+
+ ok(CloseHandle(hnp), "CloseHandle\n");
+
+ /* etc, etc */
+}
+
+/** implementation of alarm() */
+static DWORD CALLBACK alarmThreadMain(LPVOID arg)
+{
+ DWORD timeout = (DWORD) arg;
+ trace("alarmThreadMain\n");
+ if (WaitForSingleObject( alarm_event, timeout ) == WAIT_TIMEOUT)
+ {
+ ok(FALSE, "alarm\n");
+ ExitProcess(1);
+ }
+ return 1;
+}
+
+HANDLE hnp = INVALID_HANDLE_VALUE;
+
+/** Trivial byte echo server - disconnects after each session */
+static DWORD CALLBACK serverThreadMain1(LPVOID arg)
+{
+ int i;
+
+ trace("serverThreadMain1 start\n");
+ /* Set up a simple echo server */
+ hnp = CreateNamedPipe(PIPENAME "serverThreadMain1", PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+
+ ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+ for (i = 0; i < NB_SERVER_LOOPS; i++) {
+ char buf[512];
+ DWORD written;
+ DWORD readden;
+ DWORD success;
+
+ /* Wait for client to connect */
+ trace("Server calling ConnectNamedPipe...\n");
+ ok(ConnectNamedPipe(hnp, NULL)
+ || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe\n");
+ trace("ConnectNamedPipe returned.\n");
+
+ /* Echo bytes once */
+ memset(buf, 0, sizeof(buf));
+
+ trace("Server reading...\n");
+ success = ReadFile(hnp, buf, sizeof(buf), &readden, NULL);
+ trace("Server done reading.\n");
+ ok(success, "ReadFile\n");
+ ok(readden, "short read\n");
+
+ trace("Server writing...\n");
+ ok(WriteFile(hnp, buf, readden, &written, NULL), "WriteFile\n");
+ trace("Server done writing.\n");
+ ok(written == readden, "write file len\n");
+
+ /* finish this connection, wait for next one */
+ ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
+ trace("Server done flushing.\n");
+ ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+ trace("Server done disconnecting.\n");
+ }
+ return 0;
+}
+
+/** Trivial byte echo server - closes after each connection */
+static DWORD CALLBACK serverThreadMain2(LPVOID arg)
+{
+ int i;
+ HANDLE hnpNext = 0;
+
+ trace("serverThreadMain2\n");
+ /* Set up a simple echo server */
+ hnp = CreateNamedPipe(PIPENAME "serverThreadMain2", PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 2,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ for (i = 0; i < NB_SERVER_LOOPS; i++) {
+ char buf[512];
+ DWORD written;
+ DWORD readden;
+ DWORD success;
+
+ /* Wait for client to connect */
+ trace("Server calling ConnectNamedPipe...\n");
+ ok(ConnectNamedPipe(hnp, NULL)
+ || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe\n");
+ trace("ConnectNamedPipe returned.\n");
+
+ /* Echo bytes once */
+ memset(buf, 0, sizeof(buf));
+
+ trace("Server reading...\n");
+ success = ReadFile(hnp, buf, sizeof(buf), &readden, NULL);
+ trace("Server done reading.\n");
+ ok(success, "ReadFile\n");
+
+ trace("Server writing...\n");
+ ok(WriteFile(hnp, buf, readden, &written, NULL), "WriteFile\n");
+ trace("Server done writing.\n");
+ ok(written == readden, "write file len\n");
+
+ /* finish this connection, wait for next one */
+ ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
+ ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+
+ /* Set up next echo server */
+ hnpNext =
+ CreateNamedPipe(PIPENAME "serverThreadMain2", PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 2,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+
+ ok(hnpNext != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ ok(CloseHandle(hnp), "CloseHandle\n");
+ hnp = hnpNext;
+ }
+ return 0;
+}
+
+/** Trivial byte echo server - uses overlapped named pipe calls */
+static DWORD CALLBACK serverThreadMain3(LPVOID arg)
+{
+ int i;
+ HANDLE hEvent;
+
+ trace("serverThreadMain3\n");
+ /* Set up a simple echo server */
+ hnp = CreateNamedPipe(PIPENAME "serverThreadMain3", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+ hEvent = CreateEvent(NULL, /* security attribute */
+ TRUE, /* manual reset event */
+ FALSE, /* initial state */
+ NULL); /* name */
+ ok(hEvent != NULL, "CreateEvent\n");
+
+ for (i = 0; i < NB_SERVER_LOOPS; i++) {
+ char buf[512];
+ DWORD written;
+ DWORD readden;
+ DWORD dummy;
+ DWORD success;
+ OVERLAPPED oOverlap;
+ int letWFSOEwait = (i & 2);
+ int letGORwait = (i & 1);
+ DWORD err;
+
+ memset(&oOverlap, 0, sizeof(oOverlap));
+ oOverlap.hEvent = hEvent;
+
+ /* Wait for client to connect */
+ trace("Server calling overlapped ConnectNamedPipe...\n");
+ success = ConnectNamedPipe(hnp, &oOverlap);
+ err = GetLastError();
+ ok(success || err == ERROR_IO_PENDING
+ || err == ERROR_PIPE_CONNECTED, "overlapped ConnectNamedPipe\n");
+ trace("overlapped ConnectNamedPipe returned.\n");
+ if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+ ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait ConnectNamedPipe\n");
+ success = GetOverlappedResult(hnp, &oOverlap, &dummy, letGORwait);
+ if (!letGORwait && !letWFSOEwait && !success) {
+ ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult\n");
+ success = GetOverlappedResult(hnp, &oOverlap, &dummy, TRUE);
+ }
+ ok(success, "GetOverlappedResult ConnectNamedPipe\n");
+ trace("overlapped ConnectNamedPipe operation complete.\n");
+
+ /* Echo bytes once */
+ memset(buf, 0, sizeof(buf));
+
+ trace("Server reading...\n");
+ success = ReadFile(hnp, buf, sizeof(buf), NULL, &oOverlap);
+ trace("Server ReadFile returned...\n");
+ err = GetLastError();
+ ok(success || err == ERROR_IO_PENDING, "overlapped ReadFile\n");
+ trace("overlapped ReadFile returned.\n");
+ if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+ ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait ReadFile\n");
+ success = GetOverlappedResult(hnp, &oOverlap, &readden, letGORwait);
+ if (!letGORwait && !letWFSOEwait && !success) {
+ ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult\n");
+ success = GetOverlappedResult(hnp, &oOverlap, &readden, TRUE);
+ }
+ trace("Server done reading.\n");
+ ok(success, "overlapped ReadFile\n");
+
+ trace("Server writing...\n");
+ success = WriteFile(hnp, buf, readden, NULL, &oOverlap);
+ trace("Server WriteFile returned...\n");
+ err = GetLastError();
+ ok(success || err == ERROR_IO_PENDING, "overlapped WriteFile\n");
+ trace("overlapped WriteFile returned.\n");
+ if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+ ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait WriteFile\n");
+ success = GetOverlappedResult(hnp, &oOverlap, &written, letGORwait);
+ if (!letGORwait && !letWFSOEwait && !success) {
+ ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult\n");
+ success = GetOverlappedResult(hnp, &oOverlap, &written, TRUE);
+ }
+ trace("Server done writing.\n");
+ ok(success, "overlapped WriteFile\n");
+ ok(written == readden, "write file len\n");
+
+ /* finish this connection, wait for next one */
+ ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
+ ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+ }
+ return 0;
+}
+
+static void exercizeServer(const char *pipename, HANDLE serverThread)
+{
+ int i;
+
+ trace("exercizeServer starting\n");
+ for (i = 0; i < NB_SERVER_LOOPS; i++) {
+ HANDLE hFile=INVALID_HANDLE_VALUE;
+ static const char obuf[] = "Bit Bucket";
+ char ibuf[32];
+ DWORD written;
+ DWORD readden;
+ int loop;
+
+ for (loop = 0; loop < 3; loop++) {
+ DWORD err;
+ trace("Client connecting...\n");
+ /* Connect to the server */
+ hFile = CreateFileA(pipename, GENERIC_READ | GENERIC_WRITE, 0,
+ NULL, OPEN_EXISTING, 0, 0);
+ if (hFile != INVALID_HANDLE_VALUE)
+ break;
+ err = GetLastError();
+ if (loop == 0)
+ ok(err == ERROR_PIPE_BUSY || err == ERROR_FILE_NOT_FOUND, "connecting to pipe\n");
+ else
+ ok(err == ERROR_PIPE_BUSY, "connecting to pipe\n");
+ trace("connect failed, retrying\n");
+ Sleep(200);
+ }
+ ok(hFile != INVALID_HANDLE_VALUE, "client opening named pipe\n");
+
+ /* Make sure it can echo */
+ memset(ibuf, 0, sizeof(ibuf));
+ trace("Client writing...\n");
+ ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile to client end of pipe\n");
+ ok(written == sizeof(obuf), "write file len\n");
+ trace("Client reading...\n");
+ ok(ReadFile(hFile, ibuf, sizeof(obuf), &readden, NULL), "ReadFile from client end of pipe\n");
+ ok(readden == sizeof(obuf), "read file len\n");
+ ok(memcmp(obuf, ibuf, written) == 0, "content check\n");
+
+ trace("Client closing...\n");
+ ok(CloseHandle(hFile), "CloseHandle\n");
+ }
+
+ ok(WaitForSingleObject(serverThread,INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject\n");
+ CloseHandle(hnp);
+ trace("exercizeServer returning\n");
+}
+
+static void test_NamedPipe_2(void)
+{
+ HANDLE serverThread;
+ DWORD serverThreadId;
+ HANDLE alarmThread;
+ DWORD alarmThreadId;
+
+ trace("test_NamedPipe_2 starting\n");
+ /* Set up a ten second timeout */
+ alarm_event = CreateEvent( NULL, TRUE, FALSE, NULL );
+ alarmThread = CreateThread(NULL, 0, alarmThreadMain, (void *) 10000, 0, &alarmThreadId);
+
+ /* The servers we're about to exercize do try to clean up carefully,
+ * but to reduce the change of a test failure due to a pipe handle
+ * leak in the test code, we'll use a different pipe name for each server.
+ */
+
+ /* Try server #1 */
+ serverThread = CreateThread(NULL, 0, serverThreadMain1, (void *)8, 0, &serverThreadId);
+ ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread\n");
+ exercizeServer(PIPENAME "serverThreadMain1", serverThread);
+
+ /* Try server #2 */
+ serverThread = CreateThread(NULL, 0, serverThreadMain2, 0, 0, &serverThreadId);
+ ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread\n");
+ exercizeServer(PIPENAME "serverThreadMain2", serverThread);
+
+ if( 0 ) /* overlapped pipe server doesn't work yet - it randomly fails */
+ {
+ /* Try server #3 */
+ serverThread = CreateThread(NULL, 0, serverThreadMain3, 0, 0, &serverThreadId);
+ ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread\n");
+ exercizeServer(PIPENAME "serverThreadMain3", serverThread);
+ }
+
+ ok(SetEvent( alarm_event ), "SetEvent\n");
+ CloseHandle( alarm_event );
+ trace("test_NamedPipe_2 returning\n");
+}
+
+static int test_DisconnectNamedPipe(void)
+{
+ HANDLE hnp;
+ HANDLE hFile;
+ static const char obuf[] = "Bit Bucket";
+ char ibuf[32];
+ DWORD written;
+ DWORD readden;
+
+ hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+ /* nMaxInstances */ 1,
+ /* nOutBufSize */ 1024,
+ /* nInBufSize */ 1024,
+ /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+ /* lpSecurityAttrib */ NULL);
+ if (INVALID_HANDLE_VALUE == hnp) {
+ trace ("Seems we have no named pipes.\n");
+ return 1;
+ }
+
+ ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL) == 0
+ && GetLastError() == ERROR_PIPE_LISTENING, "WriteFile to not-yet-connected pipe\n");
+ ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL) == 0
+ && GetLastError() == ERROR_PIPE_LISTENING, "ReadFile from not-yet-connected pipe\n");
+
+ hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+ ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed\n");
+
+ /* don't try to do i/o if one side couldn't be opened, as it hangs */
+ if (hFile != INVALID_HANDLE_VALUE) {
+
+ /* see what happens if server calls DisconnectNamedPipe
+ * when there are bytes in the pipe
+ */
+
+ ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile\n");
+ ok(written == sizeof(obuf), "write file len\n");
+ ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe while messages waiting\n");
+ ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL) == 0
+ && GetLastError() == ERROR_PIPE_NOT_CONNECTED, "WriteFile to disconnected pipe\n");
+ ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL) == 0
+ && GetLastError() == ERROR_PIPE_NOT_CONNECTED,
+ "ReadFile from disconnected pipe with bytes waiting\n");
+ ok(CloseHandle(hFile), "CloseHandle\n");
+ }
+
+ ok(CloseHandle(hnp), "CloseHandle\n");
+
+ return 0;
+}
+static void test_CreatePipe(void)
+{
+ SECURITY_ATTRIBUTES pipe_attr;
+ HANDLE piperead, pipewrite;
+ DWORD written;
+ DWORD read;
+ char readbuf[32];
+
+ pipe_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ pipe_attr.bInheritHandle = TRUE;
+ pipe_attr.lpSecurityDescriptor = NULL;
+ ok(CreatePipe(&piperead, &pipewrite, &pipe_attr, 0) != 0, "CreatePipe failed\n");
+ ok(WriteFile(pipewrite,PIPENAME,sizeof(PIPENAME), &written, NULL), "Write to anonymous pipe failed\n");
+ ok(written == sizeof(PIPENAME), "Write to anonymous pipe wrote %ld bytes instead of %d\n", written,sizeof(PIPENAME));
+ ok(ReadFile(piperead,readbuf,sizeof(readbuf),&read, NULL), "Read from non empty pipe failed\n");
+ ok(read == sizeof(PIPENAME), "Read from anonymous pipe got %ld bytes instead of %d\n", read, sizeof(PIPENAME));
+
+ /* Now write another chunk*/
+ ok(CreatePipe(&piperead, &pipewrite, &pipe_attr, 0) != 0, "CreatePipe failed\n");
+ ok(WriteFile(pipewrite,PIPENAME,sizeof(PIPENAME), &written, NULL), "Write to anonymous pipe failed\n");
+ ok(written == sizeof(PIPENAME), "Write to anonymous pipe wrote %ld bytes instead of %d\n", written,sizeof(PIPENAME));
+ /* and close the write end, read should still succeed*/
+ ok(CloseHandle(pipewrite), "CloseHandle for the Write Pipe failed\n");
+ ok(ReadFile(piperead,readbuf,sizeof(readbuf),&read, NULL), "Read from broken pipe withe with pending data failed\n");
+ ok(read == sizeof(PIPENAME), "Read from anonymous pipe got %ld bytes instead of %d\n", read, sizeof(PIPENAME));
+ /* But now we need to get informed that the pipe is closed */
+ ok(ReadFile(piperead,readbuf,sizeof(readbuf),&read, NULL) == 0, "Broken pipe not detected\n");
+}
+
+START_TEST(pipe)
+{
+ trace("test 1 of 6:\n");
+ if (test_DisconnectNamedPipe())
+ return;
+ trace("test 2 of 6:\n");
+ test_CreateNamedPipe_instances_must_match();
+ trace("test 3 of 6:\n");
+ test_NamedPipe_2();
+ trace("test 4 of 6:\n");
+ test_CreateNamedPipe(PIPE_TYPE_BYTE);
+ trace("test 5 of 6\n");
+ test_CreateNamedPipe(PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE);
+ trace("test 6 of 6\n");
+ test_CreatePipe();
+ trace("all tests done\n");
+}
--- /dev/null
+/*
+ * Unit test suite for CreateProcess function.
+ *
+ * Copyright 2002 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "wincon.h"
+#include "winnls.h"
+
+static char base[MAX_PATH];
+static char selfname[MAX_PATH];
+static char resfile[MAX_PATH];
+
+static int myARGC;
+static char** myARGV;
+
+/* As some environment variables get very long on Unix, we only test for
+ * the first 127 bytes.
+ * Note that increasing this value past 256 may exceed the buffer size
+ * limitations of the *Profile functions (at least on Wine).
+ */
+#define MAX_LISTED_ENV_VAR 128
+
+/* ---------------- portable memory allocation thingie */
+
+static char memory[1024*32];
+static char* memory_index = memory;
+
+static char* grab_memory(size_t len)
+{
+ char* ret = memory_index;
+ /* align on dword */
+ len = (len + 3) & ~3;
+ memory_index += len;
+ assert(memory_index <= memory + sizeof(memory));
+ return ret;
+}
+
+static void release_memory(void)
+{
+ memory_index = memory;
+}
+
+/* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */
+
+static const char* encodeA(const char* str)
+{
+ char* ptr;
+ size_t len,i;
+
+ if (!str) return "";
+ len = strlen(str) + 1;
+ ptr = grab_memory(len * 2 + 1);
+ for (i = 0; i < len; i++)
+ sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
+ ptr[2 * len] = '\0';
+ return ptr;
+}
+
+static const char* encodeW(const WCHAR* str)
+{
+ char* ptr;
+ size_t len,i;
+
+ if (!str) return "";
+ len = lstrlenW(str) + 1;
+ ptr = grab_memory(len * 4 + 1);
+ assert(ptr);
+ for (i = 0; i < len; i++)
+ sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]);
+ ptr[4 * len] = '\0';
+ return ptr;
+}
+
+static unsigned decode_char(char c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ assert(c >= 'A' && c <= 'F');
+ return c - 'A' + 10;
+}
+
+static char* decodeA(const char* str)
+{
+ char* ptr;
+ size_t len,i;
+
+ len = strlen(str) / 2;
+ if (!len--) return NULL;
+ ptr = grab_memory(len + 1);
+ for (i = 0; i < len; i++)
+ ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
+ ptr[len] = '\0';
+ return ptr;
+}
+
+#if 0
+/* This will be needed to decode Unicode strings saved by the child process
+ * when we test Unicode functions.
+ */
+static WCHAR* decodeW(const char* str)
+{
+ size_t len;
+ WCHAR* ptr;
+ int i;
+
+ len = strlen(str) / 4;
+ if (!len--) return NULL;
+ ptr = (WCHAR*)grab_memory(len * 2 + 1);
+ for (i = 0; i < len; i++)
+ ptr[i] = (decode_char(str[4 * i]) << 12) |
+ (decode_char(str[4 * i + 1]) << 8) |
+ (decode_char(str[4 * i + 2]) << 4) |
+ (decode_char(str[4 * i + 3]) << 0);
+ ptr[len] = '\0';
+ return ptr;
+}
+#endif
+
+/******************************************************************
+ * init
+ *
+ * generates basic information like:
+ * base: absolute path to curr dir
+ * selfname: the way to reinvoke ourselves
+ */
+static int init(void)
+{
+ myARGC = winetest_get_mainargs( &myARGV );
+ if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
+ strcpy(selfname, myARGV[0]);
+ return 1;
+}
+
+/******************************************************************
+ * get_file_name
+ *
+ * generates an absolute file_name for temporary file
+ *
+ */
+static void get_file_name(char* buf)
+{
+ char path[MAX_PATH];
+
+ buf[0] = '\0';
+ GetTempPathA(sizeof(path), path);
+ GetTempFileNameA(path, "wt", 0, buf);
+}
+
+/******************************************************************
+ * static void childPrintf
+ *
+ */
+static void childPrintf(HANDLE h, const char* fmt, ...)
+{
+ va_list valist;
+ char buffer[1024+4*MAX_LISTED_ENV_VAR];
+ DWORD w;
+
+ va_start(valist, fmt);
+ vsprintf(buffer, fmt, valist);
+ va_end(valist);
+ WriteFile(h, buffer, strlen(buffer), &w, NULL);
+}
+
+
+/******************************************************************
+ * doChild
+ *
+ * output most of the information in the child process
+ */
+static void doChild(const char* file, const char* option)
+{
+ STARTUPINFOA siA;
+ STARTUPINFOW siW;
+ int i;
+ char* ptrA;
+ WCHAR* ptrW;
+ char bufA[MAX_PATH];
+ WCHAR bufW[MAX_PATH];
+ HANDLE hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+ BOOL ret;
+
+ if (hFile == INVALID_HANDLE_VALUE) return;
+
+ /* output of startup info (Ansi) */
+ GetStartupInfoA(&siA);
+ childPrintf(hFile,
+ "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
+ "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
+ "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
+ "dwFlags=%lu\nwShowWindow=%u\n"
+ "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
+ siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle),
+ siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize,
+ siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute,
+ siA.dwFlags, siA.wShowWindow,
+ (DWORD)siA.hStdInput, (DWORD)siA.hStdOutput, (DWORD)siA.hStdError);
+
+ /* since GetStartupInfoW is only implemented in win2k,
+ * zero out before calling so we can notice the difference
+ */
+ memset(&siW, 0, sizeof(siW));
+ GetStartupInfoW(&siW);
+ childPrintf(hFile,
+ "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
+ "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
+ "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
+ "dwFlags=%lu\nwShowWindow=%u\n"
+ "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
+ siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle),
+ siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize,
+ siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute,
+ siW.dwFlags, siW.wShowWindow,
+ (DWORD)siW.hStdInput, (DWORD)siW.hStdOutput, (DWORD)siW.hStdError);
+
+ /* Arguments */
+ childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC);
+ for (i = 0; i < myARGC; i++)
+ {
+ childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
+ }
+ childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
+
+#if 0
+ int argcW;
+ WCHAR** argvW;
+
+ /* this is part of shell32... and should be tested there */
+ argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
+ for (i = 0; i < argcW; i++)
+ {
+ childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
+ }
+#endif
+ childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
+
+ /* output of environment (Ansi) */
+ ptrA = GetEnvironmentStringsA();
+ if (ptrA)
+ {
+ char env_var[MAX_LISTED_ENV_VAR];
+
+ childPrintf(hFile, "[EnvironmentA]\n");
+ i = 0;
+ while (*ptrA)
+ {
+ lstrcpynA(env_var, ptrA, MAX_LISTED_ENV_VAR);
+ childPrintf(hFile, "env%d=%s\n", i, encodeA(env_var));
+ i++;
+ ptrA += strlen(ptrA) + 1;
+ }
+ childPrintf(hFile, "len=%d\n\n", i);
+ }
+
+ /* output of environment (Unicode) */
+ ptrW = GetEnvironmentStringsW();
+ if (ptrW)
+ {
+ WCHAR env_var[MAX_LISTED_ENV_VAR];
+
+ childPrintf(hFile, "[EnvironmentW]\n");
+ i = 0;
+ while (*ptrW)
+ {
+ lstrcpynW(env_var, ptrW, MAX_LISTED_ENV_VAR - 1);
+ env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
+ childPrintf(hFile, "env%d=%s\n", i, encodeW(env_var));
+ i++;
+ ptrW += lstrlenW(ptrW) + 1;
+ }
+ childPrintf(hFile, "len=%d\n\n", i);
+ }
+
+ childPrintf(hFile, "[Misc]\n");
+ if (GetCurrentDirectoryA(sizeof(bufA), bufA))
+ childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA));
+ if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW))
+ childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW));
+ childPrintf(hFile, "\n");
+
+ if (option && strcmp(option, "console") == 0)
+ {
+ CONSOLE_SCREEN_BUFFER_INFO sbi;
+ HANDLE hConIn = GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ DWORD modeIn, modeOut;
+
+ childPrintf(hFile, "[Console]\n");
+ if (GetConsoleScreenBufferInfo(hConOut, &sbi))
+ {
+ childPrintf(hFile, "SizeX=%d\nSizeY=%d\nCursorX=%d\nCursorY=%d\nAttributes=%d\n",
+ sbi.dwSize.X, sbi.dwSize.Y, sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y, sbi.wAttributes);
+ childPrintf(hFile, "winLeft=%d\nwinTop=%d\nwinRight=%d\nwinBottom=%d\n",
+ sbi.srWindow.Left, sbi.srWindow.Top, sbi.srWindow.Right, sbi.srWindow.Bottom);
+ childPrintf(hFile, "maxWinWidth=%d\nmaxWinHeight=%d\n",
+ sbi.dwMaximumWindowSize.X, sbi.dwMaximumWindowSize.Y);
+ }
+ childPrintf(hFile, "InputCP=%d\nOutputCP=%d\n",
+ GetConsoleCP(), GetConsoleOutputCP());
+ if (GetConsoleMode(hConIn, &modeIn))
+ childPrintf(hFile, "InputMode=%ld\n", modeIn);
+ if (GetConsoleMode(hConOut, &modeOut))
+ childPrintf(hFile, "OutputMode=%ld\n", modeOut);
+
+ /* now that we have written all relevant information, let's change it */
+ ok(SetConsoleCP(1252), "Setting CP\n");
+ ok(SetConsoleOutputCP(1252), "Setting SB CP\n");
+ ret = SetConsoleMode(hConIn, modeIn ^ 1);
+ ok( ret, "Setting mode (%ld)\n", GetLastError());
+ ret = SetConsoleMode(hConOut, modeOut ^ 1);
+ ok( ret, "Setting mode (%ld)\n", GetLastError());
+ sbi.dwCursorPosition.X ^= 1;
+ sbi.dwCursorPosition.Y ^= 1;
+ ret = SetConsoleCursorPosition(hConOut, sbi.dwCursorPosition);
+ ok( ret, "Setting cursor position (%ld)\n", GetLastError());
+ }
+ if (option && strcmp(option, "stdhandle") == 0)
+ {
+ HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
+ HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ if (hStdIn != INVALID_HANDLE_VALUE || hStdOut != INVALID_HANDLE_VALUE)
+ {
+ char buf[1024];
+ DWORD r, w;
+
+ ok(ReadFile(hStdIn, buf, sizeof(buf), &r, NULL) && r > 0, "Reading message from input pipe\n");
+ childPrintf(hFile, "[StdHandle]\nmsg=%s\n\n", encodeA(buf));
+ ok(WriteFile(hStdOut, buf, r, &w, NULL) && w == r, "Writing message to output pipe\n");
+ }
+ }
+
+ if (option && strcmp(option, "exit_code") == 0)
+ {
+ childPrintf(hFile, "[ExitCode]\nvalue=%d\n\n", 123);
+ CloseHandle(hFile);
+ ExitProcess(123);
+ }
+
+ CloseHandle(hFile);
+}
+
+static char* getChildString(const char* sect, const char* key)
+{
+ char buf[1024+4*MAX_LISTED_ENV_VAR];
+ char* ret;
+
+ GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
+ if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
+ assert(!(strlen(buf) & 1));
+ ret = decodeA(buf);
+ return ret;
+}
+
+/* FIXME: this may be moved to the wtmain.c file, because it may be needed by
+ * others... (windows uses stricmp while Un*x uses strcasecmp...)
+ */
+static int wtstrcasecmp(const char* p1, const char* p2)
+{
+ char c1, c2;
+
+ c1 = c2 = '@';
+ while (c1 == c2 && c1)
+ {
+ c1 = *p1++; c2 = *p2++;
+ if (c1 != c2)
+ {
+ c1 = toupper(c1); c2 = toupper(c2);
+ }
+ }
+ return c1 - c2;
+}
+
+static int strCmp(const char* s1, const char* s2, BOOL sensitive)
+{
+ if (!s1 && !s2) return 0;
+ if (!s2) return -1;
+ if (!s1) return 1;
+ return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2);
+}
+
+#define okChildString(sect, key, expect) \
+ do { \
+ char* result = getChildString((sect), (key)); \
+ ok(strCmp(result, expect, 1) == 0, "%s:%s expected '%s', got '%s'\n", (sect), (key), (expect)?(expect):"(null)", result); \
+ } while (0)
+
+#define okChildIString(sect, key, expect) \
+ do { \
+ char* result = getChildString(sect, key); \
+ ok(strCmp(result, expect, 0) == 0, "%s:%s expected '%s', got '%s'\n", sect, key, expect, result); \
+ } while (0)
+
+/* using !expect ensures that the test will fail if the sect/key isn't present
+ * in result file
+ */
+#define okChildInt(sect, key, expect) \
+ do { \
+ UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \
+ ok(result == expect, "%s:%s expected %d, but got %d\n", (sect), (key), (int)(expect), result); \
+ } while (0)
+
+static void test_Startup(void)
+{
+ char buffer[MAX_PATH];
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup,si;
+
+ /* let's start simplistic */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ GetStartupInfoA(&si);
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", si.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* not so simplistic now */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ startup.lpTitle = "I'm the title string";
+ startup.lpDesktop = "I'm the desktop string";
+ startup.dwXCountChars = 0x12121212;
+ startup.dwYCountChars = 0x23232323;
+ startup.dwX = 0x34343434;
+ startup.dwY = 0x45454545;
+ startup.dwXSize = 0x56565656;
+ startup.dwYSize = 0x67676767;
+ startup.dwFillAttribute = 0xA55A;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* not so simplistic now */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ startup.lpTitle = "I'm the title string";
+ startup.lpDesktop = NULL;
+ startup.dwXCountChars = 0x12121212;
+ startup.dwYCountChars = 0x23232323;
+ startup.dwX = 0x34343434;
+ startup.dwY = 0x45454545;
+ startup.dwXSize = 0x56565656;
+ startup.dwYSize = 0x67676767;
+ startup.dwFillAttribute = 0xA55A;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* not so simplistic now */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ startup.lpTitle = "I'm the title string";
+ startup.lpDesktop = "";
+ startup.dwXCountChars = 0x12121212;
+ startup.dwYCountChars = 0x23232323;
+ startup.dwX = 0x34343434;
+ startup.dwY = 0x45454545;
+ startup.dwXSize = 0x56565656;
+ startup.dwYSize = 0x67676767;
+ startup.dwFillAttribute = 0xA55A;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* not so simplistic now */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ startup.lpTitle = NULL;
+ startup.lpDesktop = "I'm the desktop string";
+ startup.dwXCountChars = 0x12121212;
+ startup.dwYCountChars = 0x23232323;
+ startup.dwX = 0x34343434;
+ startup.dwY = 0x45454545;
+ startup.dwXSize = 0x56565656;
+ startup.dwYSize = 0x67676767;
+ startup.dwFillAttribute = 0xA55A;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", si.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* not so simplistic now */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ startup.lpTitle = "";
+ startup.lpDesktop = "I'm the desktop string";
+ startup.dwXCountChars = 0x12121212;
+ startup.dwYCountChars = 0x23232323;
+ startup.dwX = 0x34343434;
+ startup.dwY = 0x45454545;
+ startup.dwXSize = 0x56565656;
+ startup.dwYSize = 0x67676767;
+ startup.dwFillAttribute = 0xA55A;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+ todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* not so simplistic now */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ startup.lpTitle = "";
+ startup.lpDesktop = "";
+ startup.dwXCountChars = 0x12121212;
+ startup.dwYCountChars = 0x23232323;
+ startup.dwX = 0x34343434;
+ startup.dwY = 0x45454545;
+ startup.dwXSize = 0x56565656;
+ startup.dwYSize = 0x67676767;
+ startup.dwFillAttribute = 0xA55A;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+ todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* TODO: test for A/W and W/A and W/W */
+}
+
+static void test_CommandLine(void)
+{
+ char buffer[MAX_PATH], fullpath[MAX_PATH], *lpFilePart, *p;
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup;
+ DWORD len;
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ /* the basics */
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("Arguments", "argcA", 4);
+ okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
+ okChildString("Arguments", "argvA4", NULL);
+ okChildString("Arguments", "CommandLineA", buffer);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ /* from Frangois */
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildInt("Arguments", "argcA", 6);
+ okChildString("Arguments", "argvA3", "a\"b\\");
+ okChildString("Arguments", "argvA4", "c\"");
+ okChildString("Arguments", "argvA5", "d");
+ okChildString("Arguments", "argvA6", NULL);
+ okChildString("Arguments", "CommandLineA", buffer);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ /* Test for Bug1330 to show that XP doesn't change '/' to '\\' in argv[0]*/
+ get_file_name(resfile);
+ sprintf(buffer, "./%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+ sprintf(buffer, "./%s", selfname);
+ okChildString("Arguments", "argvA0", buffer);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ get_file_name(resfile);
+ sprintf(buffer, ".\\%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+ sprintf(buffer, ".\\%s", selfname);
+ okChildString("Arguments", "argvA0", buffer);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ get_file_name(resfile);
+ len = GetFullPathNameA(selfname, MAX_PATH, fullpath, &lpFilePart);
+ assert ( lpFilePart != 0);
+ *(lpFilePart -1 ) = 0;
+ p = strrchr(fullpath, '\\');
+ assert (p);
+ sprintf(buffer, "..%s/%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", p, selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+ sprintf(buffer, "..%s/%s", p, selfname);
+ okChildString("Arguments", "argvA0", buffer);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+}
+
+static void test_Directory(void)
+{
+ char buffer[MAX_PATH];
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup;
+ char windir[MAX_PATH];
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ /* the basics */
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ GetWindowsDirectoryA( windir, sizeof(windir) );
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildIString("Misc", "CurrDirA", windir);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+}
+
+static BOOL is_str_env_drive_dir(const char* str)
+{
+ return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' &&
+ str[3] == '=' && str[4] == str[1];
+}
+
+/* compared expected child's environment (in gesA) from actual
+ * environment our child got
+ */
+static void cmpEnvironment(const char* gesA)
+{
+ int i, clen;
+ const char* ptrA;
+ char* res;
+ char key[32];
+ BOOL found;
+
+ clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile);
+
+ /* now look each parent env in child */
+ if ((ptrA = gesA) != NULL)
+ {
+ while (*ptrA)
+ {
+ for (i = 0; i < clen; i++)
+ {
+ sprintf(key, "env%d", i);
+ res = getChildString("EnvironmentA", key);
+ if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0)
+ break;
+ }
+ found = i < clen;
+ ok(found, "Parent-env string %s isn't in child process\n", ptrA);
+
+ ptrA += strlen(ptrA) + 1;
+ release_memory();
+ }
+ }
+ /* and each child env in parent */
+ for (i = 0; i < clen; i++)
+ {
+ sprintf(key, "env%d", i);
+ res = getChildString("EnvironmentA", key);
+ if ((ptrA = gesA) != NULL)
+ {
+ while (*ptrA)
+ {
+ if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0)
+ break;
+ ptrA += strlen(ptrA) + 1;
+ }
+ if (!*ptrA) ptrA = NULL;
+ }
+
+ if (!is_str_env_drive_dir(res))
+ {
+ found = ptrA != NULL;
+ ok(found, "Child-env string %s isn't in parent process\n", res);
+ }
+ /* else => should also test we get the right per drive default directory here... */
+ }
+}
+
+static void test_Environment(void)
+{
+ char buffer[MAX_PATH];
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup;
+ char* child_env;
+ int child_env_len;
+ char* ptr;
+ char* env;
+ int slen;
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ /* the basics */
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ cmpEnvironment(GetEnvironmentStringsA());
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ /* the basics */
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+
+ child_env_len = 0;
+ ptr = GetEnvironmentStringsA();
+ while(*ptr)
+ {
+ slen = strlen(ptr)+1;
+ child_env_len += slen;
+ ptr += slen;
+ }
+ /* Add space for additional environment variables */
+ child_env_len += 256;
+ child_env = HeapAlloc(GetProcessHeap(), 0, child_env_len);
+
+ ptr = child_env;
+ sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
+ ptr += strlen(ptr) + 1;
+ strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR");
+ ptr += strlen(ptr) + 1;
+ strcpy(ptr, "FOO=BAR");
+ ptr += strlen(ptr) + 1;
+ strcpy(ptr, "BAR=FOOBAR");
+ ptr += strlen(ptr) + 1;
+ /* copy all existing variables except:
+ * - WINELOADER
+ * - PATH (already set above)
+ * - the directory definitions (=[A-Z]:=)
+ */
+ for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
+ {
+ if (strncmp(env, "PATH=", 5) != 0 &&
+ strncmp(env, "WINELOADER=", 11) != 0 &&
+ !is_str_env_drive_dir(env))
+ {
+ strcpy(ptr, env);
+ ptr += strlen(ptr) + 1;
+ }
+ }
+ *ptr = '\0';
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ cmpEnvironment(child_env);
+
+ HeapFree(GetProcessHeap(), 0, child_env);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+}
+
+static void test_SuspendFlag(void)
+{
+ char buffer[MAX_PATH];
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup, us;
+ DWORD exit_status;
+
+ /* let's start simplistic */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+ ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
+ Sleep(8000);
+ ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
+ ok(ResumeThread(info.hThread) == 1, "Resuming thread\n");
+
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ GetStartupInfoA(&us);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+}
+
+static void test_DebuggingFlag(void)
+{
+ char buffer[MAX_PATH];
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup, us;
+ DEBUG_EVENT de;
+ unsigned dbg = 0;
+
+ /* let's start simplistic */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+ /* get all startup events up to the entry point break exception */
+ do
+ {
+ ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
+ ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
+ if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++;
+ } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
+
+ ok(dbg, "I have seen a debug event\n");
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ GetStartupInfoA(&us);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+}
+
+static BOOL is_console(HANDLE h)
+{
+ return h != INVALID_HANDLE_VALUE && ((ULONG_PTR)h & 3) == 3;
+}
+
+static void test_Console(void)
+{
+ char buffer[MAX_PATH];
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup, us;
+ SECURITY_ATTRIBUTES sa;
+ CONSOLE_SCREEN_BUFFER_INFO sbi, sbiC;
+ DWORD modeIn, modeOut, modeInC, modeOutC;
+ DWORD cpIn, cpOut, cpInC, cpOutC;
+ DWORD w;
+ HANDLE hChildIn, hChildInInh, hChildOut, hChildOutInh, hParentIn, hParentOut;
+ const char* msg = "This is a std-handle inheritance test.";
+ unsigned msg_len;
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+ startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+
+ /* first, we need to be sure we're attached to a console */
+ if (!is_console(startup.hStdInput) || !is_console(startup.hStdOutput))
+ {
+ /* we're not attached to a console, let's do it */
+ AllocConsole();
+ startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+ startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+ }
+ /* now verify everything's ok */
+ ok(startup.hStdInput != INVALID_HANDLE_VALUE, "Opening ConIn\n");
+ ok(startup.hStdOutput != INVALID_HANDLE_VALUE, "Opening ConOut\n");
+ startup.hStdError = startup.hStdOutput;
+
+ ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbi), "Getting sb info\n");
+ ok(GetConsoleMode(startup.hStdInput, &modeIn) &&
+ GetConsoleMode(startup.hStdOutput, &modeOut), "Getting console modes\n");
+ cpIn = GetConsoleCP();
+ cpOut = GetConsoleOutputCP();
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s console", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ /* now get the modification the child has made, and resets parents expected values */
+ ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbiC), "Getting sb info\n");
+ ok(GetConsoleMode(startup.hStdInput, &modeInC) &&
+ GetConsoleMode(startup.hStdOutput, &modeOutC), "Getting console modes\n");
+
+ SetConsoleMode(startup.hStdInput, modeIn);
+ SetConsoleMode(startup.hStdOutput, modeOut);
+
+ cpInC = GetConsoleCP();
+ cpOutC = GetConsoleOutputCP();
+ SetConsoleCP(cpIn);
+ SetConsoleOutputCP(cpOut);
+
+ GetStartupInfoA(&us);
+
+ okChildInt("StartupInfoA", "cb", startup.cb);
+ okChildString("StartupInfoA", "lpDesktop", us.lpDesktop);
+ okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+ okChildInt("StartupInfoA", "dwX", startup.dwX);
+ okChildInt("StartupInfoA", "dwY", startup.dwY);
+ okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+ okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+ okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+ okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+ okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+ okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+ okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+
+ /* check child correctly inherited the console */
+ okChildInt("StartupInfoA", "hStdInput", (DWORD)startup.hStdInput);
+ okChildInt("StartupInfoA", "hStdOutput", (DWORD)startup.hStdOutput);
+ okChildInt("StartupInfoA", "hStdError", (DWORD)startup.hStdError);
+ okChildInt("Console", "SizeX", (DWORD)sbi.dwSize.X);
+ okChildInt("Console", "SizeY", (DWORD)sbi.dwSize.Y);
+ okChildInt("Console", "CursorX", (DWORD)sbi.dwCursorPosition.X);
+ okChildInt("Console", "CursorY", (DWORD)sbi.dwCursorPosition.Y);
+ okChildInt("Console", "Attributes", sbi.wAttributes);
+ okChildInt("Console", "winLeft", (DWORD)sbi.srWindow.Left);
+ okChildInt("Console", "winTop", (DWORD)sbi.srWindow.Top);
+ okChildInt("Console", "winRight", (DWORD)sbi.srWindow.Right);
+ okChildInt("Console", "winBottom", (DWORD)sbi.srWindow.Bottom);
+ okChildInt("Console", "maxWinWidth", (DWORD)sbi.dwMaximumWindowSize.X);
+ okChildInt("Console", "maxWinHeight", (DWORD)sbi.dwMaximumWindowSize.Y);
+ okChildInt("Console", "InputCP", cpIn);
+ okChildInt("Console", "OutputCP", cpOut);
+ okChildInt("Console", "InputMode", modeIn);
+ okChildInt("Console", "OutputMode", modeOut);
+
+ todo_wine ok(cpInC == 1252, "Wrong console CP (expected 1252 got %ld/%ld)\n", cpInC, cpIn);
+ todo_wine ok(cpOutC == 1252, "Wrong console-SB CP (expected 1252 got %ld/%ld)\n", cpOutC, cpOut);
+ ok(modeInC == (modeIn ^ 1), "Wrong console mode\n");
+ ok(modeOutC == (modeOut ^ 1), "Wrong console-SB mode\n");
+ ok(sbiC.dwCursorPosition.X == (sbi.dwCursorPosition.X ^ 1), "Wrong cursor position\n");
+ ok(sbiC.dwCursorPosition.Y == (sbi.dwCursorPosition.Y ^ 1), "Wrong cursor position\n");
+
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+
+ ok(CreatePipe(&hParentIn, &hChildOut, NULL, 0), "Creating parent-input pipe\n");
+ ok(DuplicateHandle(GetCurrentProcess(), hChildOut, GetCurrentProcess(),
+ &hChildOutInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
+ "Duplicating as inheritable child-output pipe\n");
+ CloseHandle(hChildOut);
+
+ ok(CreatePipe(&hChildIn, &hParentOut, NULL, 0), "Creating parent-output pipe\n");
+ ok(DuplicateHandle(GetCurrentProcess(), hChildIn, GetCurrentProcess(),
+ &hChildInInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
+ "Duplicating as inheritable child-input pipe\n");
+ CloseHandle(hChildIn);
+
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+ startup.wShowWindow = SW_SHOWNORMAL;
+ startup.hStdInput = hChildInInh;
+ startup.hStdOutput = hChildOutInh;
+ startup.hStdError = hChildOutInh;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
+ ok(CloseHandle(hChildInInh), "Closing handle\n");
+ ok(CloseHandle(hChildOutInh), "Closing handle\n");
+
+ msg_len = strlen(msg) + 1;
+ ok(WriteFile(hParentOut, msg, msg_len, &w, NULL), "Writing to child\n");
+ ok(w == msg_len, "Should have written %u bytes, actually wrote %lu\n", msg_len, w);
+ memset(buffer, 0, sizeof(buffer));
+ ok(ReadFile(hParentIn, buffer, sizeof(buffer), &w, NULL), "Reading from child\n");
+ ok(strcmp(buffer, msg) == 0, "Should have received '%s'\n", msg);
+
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ okChildString("StdHandle", "msg", msg);
+
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+}
+
+static void test_ExitCode(void)
+{
+ char buffer[MAX_PATH];
+ PROCESS_INFORMATION info;
+ STARTUPINFOA startup;
+ DWORD code;
+
+ /* let's start simplistic */
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = SW_SHOWNORMAL;
+
+ get_file_name(resfile);
+ sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile);
+ ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+ /* wait for child to terminate */
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+ /* child process has changed result file, so let profile functions know about it */
+ WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+ ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n");
+ okChildInt("ExitCode", "value", code);
+
+ release_memory();
+ assert(DeleteFileA(resfile) != 0);
+}
+
+START_TEST(process)
+{
+ int b = init();
+ ok(b, "Basic init of CreateProcess test\n");
+ if (!b) return;
+
+ if (myARGC >= 3)
+ {
+ doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]);
+ return;
+ }
+ test_Startup();
+ test_CommandLine();
+ test_Directory();
+ test_Environment();
+ test_SuspendFlag();
+ test_DebuggingFlag();
+ test_Console();
+ test_ExitCode();
+ /* things that can be tested:
+ * lookup: check the way program to be executed is searched
+ * handles: check the handle inheritance stuff (+sec options)
+ * console: check if console creation parameters work
+ */
+}
--- /dev/null
+/*
+ * Unit tests for profile functions
+ *
+ * Copyright (c) 2003 Stefan Leichter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "windows.h"
+
+#define KEY "ProfileInt"
+#define SECTION "Test"
+#define TESTFILE ".\\testwine.ini"
+#define TESTFILE2 ".\\testwine2.ini"
+
+struct _profileInt {
+ LPCSTR section;
+ LPCSTR key;
+ LPCSTR value;
+ LPCSTR iniFile;
+ INT defaultVal;
+ UINT result;
+ UINT result9x;
+};
+
+static void test_profile_int(void)
+{
+ struct _profileInt profileInt[]={
+ { NULL, NULL, NULL, NULL, 70, 0 , 0}, /* 0 */
+ { NULL, NULL, NULL, TESTFILE, -1, 4294967295U, 0},
+ { NULL, NULL, NULL, TESTFILE, 1, 1 , 0},
+ { SECTION, NULL, NULL, TESTFILE, -1, 4294967295U, 0},
+ { SECTION, NULL, NULL, TESTFILE, 1, 1 , 0},
+ { NULL, KEY, NULL, TESTFILE, -1, 4294967295U, 0}, /* 5 */
+ { NULL, KEY, NULL, TESTFILE, 1, 1 , 0},
+ { SECTION, KEY, NULL, TESTFILE, -1, 4294967295U, 4294967295U},
+ { SECTION, KEY, NULL, TESTFILE, 1, 1 , 1},
+ { SECTION, KEY, "-1", TESTFILE, -1, 4294967295U, 4294967295U},
+ { SECTION, KEY, "-1", TESTFILE, 1, 4294967295U, 4294967295U}, /* 10 */
+ { SECTION, KEY, "1", TESTFILE, -1, 1 , 1},
+ { SECTION, KEY, "1", TESTFILE, 1, 1 , 1},
+ { SECTION, KEY, "+1", TESTFILE, -1, 1 , 0},
+ { SECTION, KEY, "+1", TESTFILE, 1, 1 , 0},
+ { SECTION, KEY, "4294967296", TESTFILE, -1, 0 , 0}, /* 15 */
+ { SECTION, KEY, "4294967296", TESTFILE, 1, 0 , 0},
+ { SECTION, KEY, "4294967297", TESTFILE, -1, 1 , 1},
+ { SECTION, KEY, "4294967297", TESTFILE, 1, 1 , 1},
+ { SECTION, KEY, "-4294967297", TESTFILE, -1, 4294967295U, 4294967295U},
+ { SECTION, KEY, "-4294967297", TESTFILE, 1, 4294967295U, 4294967295U}, /* 20 */
+ { SECTION, KEY, "42A94967297", TESTFILE, -1, 42 , 42},
+ { SECTION, KEY, "42A94967297", TESTFILE, 1, 42 , 42},
+ { SECTION, KEY, "B4294967297", TESTFILE, -1, 0 , 0},
+ { SECTION, KEY, "B4294967297", TESTFILE, 1, 0 , 0},
+ };
+ int i, num_test = (sizeof(profileInt)/sizeof(struct _profileInt));
+ UINT res;
+
+ DeleteFileA( TESTFILE);
+
+ for (i=0; i < num_test; i++) {
+ if (profileInt[i].value)
+ WritePrivateProfileStringA(SECTION, KEY, profileInt[i].value,
+ profileInt[i].iniFile);
+
+ res = GetPrivateProfileIntA(profileInt[i].section, profileInt[i].key,
+ profileInt[i].defaultVal, profileInt[i].iniFile);
+ ok((res == profileInt[i].result) || (res == profileInt[i].result9x),
+ "test<%02d>: ret<%010u> exp<%010u><%010u>\n",
+ i, res, profileInt[i].result, profileInt[i].result9x);
+ }
+
+ DeleteFileA( TESTFILE);
+}
+
+static void test_profile_string(void)
+{
+ HANDLE h;
+ int ret;
+ DWORD count;
+ char buf[100];
+ char *p;
+ /* test that lines without an '=' will not be enumerated */
+ /* in the case below, name2 is a key while name3 is not. */
+ char content[]="[s]\r\nname1=val1\r\nname2=\r\nname3\r\nname4=val4\r\n";
+ DeleteFileA( TESTFILE2);
+ h = CreateFileA( TESTFILE2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ ok( h != INVALID_HANDLE_VALUE, " cannot create %s\n", TESTFILE2);
+ if( h == INVALID_HANDLE_VALUE) return;
+ WriteFile( h, content, sizeof(content), &count, NULL);
+ CloseHandle( h);
+
+ /* enumerate the keys */
+ ret=GetPrivateProfileStringA( "s", NULL, "", buf, sizeof(buf),
+ TESTFILE2);
+ for( p = buf + strlen(buf) + 1; *p;p += strlen(p)+1)
+ p[-1] = ',';
+ /* and test */
+ ok( ret == 18 && !strcmp( buf, "name1,name2,name4"), "wrong keys returned(%d): %s\n", ret,
+ buf);
+
+ ret=GetPrivateProfileSectionA("s", buf, sizeof(buf), TESTFILE2);
+ for( p = buf + strlen(buf) + 1; *p;p += strlen(p)+1)
+ p[-1] = ',';
+ /* and test */
+ ok( ret == 35 && !strcmp( buf, "name1=val1,name2=,name3,name4=val4"), "wrong section returned(%d): %s\n",
+ ret, buf);
+
+ /* add a new key to test that the file is quite usable */
+ WritePrivateProfileStringA( "s", "name5", "val5", TESTFILE2);
+ ret=GetPrivateProfileStringA( "s", NULL, "", buf, sizeof(buf),
+ TESTFILE2);
+ for( p = buf + strlen(buf) + 1; *p;p += strlen(p)+1)
+ p[-1] = ',';
+ ok( ret == 24 && !strcmp( buf, "name1,name2,name4,name5"), "wrong keys returned(%d): %s\n",
+ ret, buf);
+
+ DeleteFileA( TESTFILE2);
+}
+
+START_TEST(profile)
+{
+ test_profile_int();
+ test_profile_string();
+}
--- /dev/null
+/*
+ * Synchronization tests
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <windef.h>
+#include <winbase.h>
+
+#include "wine/test.h"
+
+static void test_signalandwait(void)
+{
+ DWORD (WINAPI *pSignalObjectAndWait)(HANDLE, HANDLE, DWORD, BOOL);
+ HMODULE kernel32;
+ DWORD r;
+ HANDLE event[2], semaphore[2], file;
+
+ kernel32 = GetModuleHandle("kernel32");
+ pSignalObjectAndWait = (void*) GetProcAddress(kernel32, "SignalObjectAndWait");
+
+ if (!pSignalObjectAndWait)
+ return;
+
+ /* invalid parameters */
+ r = pSignalObjectAndWait(NULL, NULL, 0, 0);
+ if (r == ERROR_INVALID_FUNCTION)
+ {
+ trace("SignalObjectAndWait not implemented, skipping tests\n");
+ return; /* Win98/ME */
+ }
+ ok( r == WAIT_FAILED, "should fail\n");
+
+ event[0] = CreateEvent(NULL, 0, 0, NULL);
+ event[1] = CreateEvent(NULL, 1, 1, NULL);
+
+ ok( event[0] && event[1], "failed to create event flags\n");
+
+ r = pSignalObjectAndWait(event[0], NULL, 0, FALSE);
+ ok( r == WAIT_FAILED, "should fail\n");
+
+ r = pSignalObjectAndWait(NULL, event[0], 0, FALSE);
+ ok( r == WAIT_FAILED, "should fail\n");
+
+
+ /* valid parameters */
+ r = pSignalObjectAndWait(event[0], event[1], 0, FALSE);
+ ok( r == WAIT_OBJECT_0, "should succeed\n");
+
+ /* event[0] is now signalled */
+ r = pSignalObjectAndWait(event[0], event[0], 0, FALSE);
+ ok( r == WAIT_OBJECT_0, "should succeed\n");
+
+ /* event[0] is not signalled */
+ r = WaitForSingleObject(event[0], 0);
+ ok( r == WAIT_TIMEOUT, "event was signalled\n");
+
+ r = pSignalObjectAndWait(event[0], event[0], 0, FALSE);
+ ok( r == WAIT_OBJECT_0, "should succeed\n");
+
+ /* clear event[1] and check for a timeout */
+ ok(ResetEvent(event[1]), "failed to clear event[1]\n");
+ r = pSignalObjectAndWait(event[0], event[1], 0, FALSE);
+ ok( r == WAIT_TIMEOUT, "should timeout\n");
+
+ CloseHandle(event[0]);
+ CloseHandle(event[1]);
+
+
+ /* semaphores */
+ semaphore[0] = CreateSemaphore( NULL, 0, 1, NULL );
+ semaphore[1] = CreateSemaphore( NULL, 1, 1, NULL );
+ ok( semaphore[0] && semaphore[1], "failed to create semaphore\n");
+
+ r = pSignalObjectAndWait(semaphore[0], semaphore[1], 0, FALSE);
+ ok( r == WAIT_OBJECT_0, "should succeed\n");
+
+ r = pSignalObjectAndWait(semaphore[0], semaphore[1], 0, FALSE);
+ ok( r == WAIT_FAILED, "should fail\n");
+
+ r = ReleaseSemaphore(semaphore[0],1,NULL);
+ ok( r == FALSE, "should fail\n");
+
+ r = ReleaseSemaphore(semaphore[1],1,NULL);
+ ok( r == TRUE, "should succeed\n");
+
+ CloseHandle(semaphore[0]);
+ CloseHandle(semaphore[1]);
+
+ /* try a registry key */
+ file = CreateFile("x", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+ r = pSignalObjectAndWait(file, file, 0, FALSE);
+ ok( r == WAIT_FAILED, "should fail\n");
+ ok( ERROR_INVALID_HANDLE == GetLastError(), "should return invalid handle error\n");
+ CloseHandle(file);
+}
+
+START_TEST(sync)
+{
+ test_signalandwait();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define STANDALONE
+#include "wine/test.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "windef.h"
+#include "winbase.h"
+#include <windows.h>
+
+extern void func_alloc(void);
+extern void func_atom(void);
+extern void func_change(void);
+extern void func_codepage(void);
+extern void func_comm(void);
+extern void func_console(void);
+extern void func_directory(void);
+extern void func_drive(void);
+extern void func_environ(void);
+extern void func_file(void);
+extern void func_format_msg(void);
+extern void func_heap(void);
+extern void func_interlck(void);
+extern void func_locale(void);
+extern void func_module(void);
+extern void func_mailslot(void);
+extern void func_path(void);
+extern void func_pipe(void);
+extern void func_process(void);
+extern void func_profile(void);
+extern void func_sync(void);
+extern void func_thread(void);
+extern void func_time(void);
+extern void func_timer(void);
+extern void func_virtual(void);
+
+const struct test winetest_testlist[] =
+{
+ { "alloc", func_alloc },
+ { "atom", func_atom },
+ { "change", func_change },
+ { "codepage", func_codepage },
+ { "comm", func_comm },
+ { "console", func_console },
+ { "directory", func_directory },
+ { "drive", func_drive },
+ { "environ", func_environ },
+ { "file", func_file },
+ { "format_msg", func_format_msg },
+ { "heap", func_heap },
+ { "interlck", func_interlck },
+ { "locale", func_locale },
+ { "module", func_module },
+ { "mailslot", func_mailslot },
+ { "path", func_path },
+ { "pipe", func_pipe },
+ { "process", func_process },
+ { "profile", func_profile },
+ { "sync", func_sync },
+ { "thread", func_thread },
+ { "time", func_time },
+ { "timer", func_timer },
+ { "virtual", func_virtual },
+ { 0, 0 }
+};
+
--- /dev/null
+/*
+ * Unit test suite for directory functions.
+ *
+ * Copyright 2002 Geoffrey Hausheer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Define _WIN32_WINNT to get SetThreadIdealProcessor on Windows */
+#define _WIN32_WINNT 0x0500
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include <windef.h>
+#include <winbase.h>
+#include <winnt.h>
+#include <winerror.h>
+
+/* Specify the number of simultaneous threads to test */
+#define NUM_THREADS 4
+/* Specify whether to test the extended priorities for Win2k/XP */
+#define USE_EXTENDED_PRIORITIES 0
+/* Specify whether to test the stack allocation in CreateThread */
+#define CHECK_STACK 0
+
+/* Set CHECK_STACK to 1 if you want to try to test the stack-limit from
+ CreateThread. So far I have been unable to make this work, and
+ I am in doubt as to how portable it is. Also, according to MSDN,
+ you shouldn't mix C-run-time-libraries (i.e. alloca) with CreateThread.
+ Anyhow, the check is currently commented out
+*/
+#if CHECK_STACK
+# ifdef __try
+# define __TRY __try
+# define __EXCEPT __except
+# define __ENDTRY
+# else
+# include "wine/exception.h"
+# endif
+#endif
+
+typedef BOOL (WINAPI *GetThreadPriorityBoost_t)(HANDLE,PBOOL);
+static GetThreadPriorityBoost_t pGetThreadPriorityBoost=NULL;
+
+typedef HANDLE (WINAPI *OpenThread_t)(DWORD,BOOL,DWORD);
+static OpenThread_t pOpenThread=NULL;
+
+typedef DWORD (WINAPI *SetThreadIdealProcessor_t)(HANDLE,DWORD);
+static SetThreadIdealProcessor_t pSetThreadIdealProcessor=NULL;
+
+typedef BOOL (WINAPI *SetThreadPriorityBoost_t)(HANDLE,BOOL);
+static SetThreadPriorityBoost_t pSetThreadPriorityBoost=NULL;
+
+/* Functions not tested yet:
+ AttachThreadInput
+ CreateRemoteThread
+ SetThreadContext
+ SwitchToThread
+
+In addition there are no checks that the inheritance works properly in
+CreateThread
+*/
+
+DWORD tlsIndex;
+
+typedef struct {
+ int threadnum;
+ HANDLE *event;
+ DWORD *threadmem;
+} t1Struct;
+
+/* WinME supports OpenThread but doesn't know about access restrictions so
+ we require them to be either completely ignored or always obeyed.
+*/
+INT obeying_ars = 0; /* -1 == no, 0 == dunno yet, 1 == yes */
+#define obey_ar(x) \
+ (obeying_ars == 0 \
+ ? ((x) \
+ ? (obeying_ars = +1) \
+ : ((obeying_ars = -1), \
+ trace("not restricted, assuming consistent behaviour\n"))) \
+ : (obeying_ars < 0) \
+ ? ok(!(x), "access restrictions obeyed\n") \
+ : ok( (x), "access restrictions not obeyed\n"))
+
+/* Basic test that simultaneous threads can access shared memory,
+ that the thread local storage routines work correctly, and that
+ threads actually run concurrently
+*/
+static DWORD WINAPI threadFunc1(LPVOID p)
+{
+ t1Struct *tstruct = (t1Struct *)p;
+ int i;
+/* write our thread # into shared memory */
+ tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId();
+ ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0,
+ "TlsSetValue failed\n");
+/* The threads synchronize before terminating. This is done by
+ Signaling an event, and waiting for all events to occur
+*/
+ SetEvent(tstruct->event[tstruct->threadnum]);
+ WaitForMultipleObjects(NUM_THREADS,tstruct->event,TRUE,INFINITE);
+/* Double check that all threads really did run by validating that
+ they have all written to the shared memory. There should be no race
+ here, since all threads were synchronized after the write.*/
+ for(i=0;i<NUM_THREADS;i++) {
+ while(tstruct->threadmem[i]==0) ;
+ }
+
+ /* lstrlenA contains an exception handler so this makes sure exceptions work in threads */
+ //ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" );
+
+/* Check that noone changed our tls memory */
+ ok((int)TlsGetValue(tlsIndex)-1==tstruct->threadnum,
+ "TlsGetValue failed\n");
+ return NUM_THREADS+tstruct->threadnum;
+}
+
+static DWORD WINAPI threadFunc2(LPVOID p)
+{
+ return 99;
+}
+
+static DWORD WINAPI threadFunc3(LPVOID p)
+{
+ HANDLE thread;
+ thread=GetCurrentThread();
+ SuspendThread(thread);
+ return 99;
+}
+
+static DWORD WINAPI threadFunc4(LPVOID p)
+{
+ HANDLE event = (HANDLE)p;
+ if(event != NULL) {
+ SetEvent(event);
+ }
+ Sleep(99000);
+ return 0;
+}
+
+#if CHECK_STACK
+static DWORD WINAPI threadFunc5(LPVOID p)
+{
+ DWORD *exitCode = (DWORD *)p;
+ SYSTEM_INFO sysInfo;
+ sysInfo.dwPageSize=0;
+ GetSystemInfo(&sysInfo);
+ *exitCode=0;
+ __TRY
+ {
+ alloca(2*sysInfo.dwPageSize);
+ }
+ __EXCEPT(1) {
+ *exitCode=1;
+ }
+ __ENDTRY
+ return 0;
+}
+#endif
+
+/* Check basic funcationality of CreateThread and Tls* functions */
+static VOID test_CreateThread_basic(void)
+{
+ HANDLE thread[NUM_THREADS],event[NUM_THREADS];
+ DWORD threadid[NUM_THREADS],curthreadId;
+ DWORD threadmem[NUM_THREADS];
+ DWORD exitCode;
+ t1Struct tstruct[NUM_THREADS];
+ int error;
+ DWORD i,j;
+ DWORD GLE, ret;
+
+ /* lstrlenA contains an exception handler so this makes sure exceptions work in the main thread */
+ //ok( lstrlenA( (char *)0xdeadbeef ) == 0, "lstrlenA: unexpected success\n" );
+
+/* Retrieve current Thread ID for later comparisons */
+ curthreadId=GetCurrentThreadId();
+/* Allocate some local storage */
+ ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed\n");
+/* Create events for thread synchronization */
+ for(i=0;i<NUM_THREADS;i++) {
+ threadmem[i]=0;
+/* Note that it doesn't matter what type of event we chose here. This
+ test isn't trying to thoroughly test events
+*/
+ event[i]=CreateEventA(NULL,TRUE,FALSE,NULL);
+ tstruct[i].threadnum=i;
+ tstruct[i].threadmem=threadmem;
+ tstruct[i].event=event;
+ }
+
+/* Test that passing arguments to threads works okay */
+ for(i=0;i<NUM_THREADS;i++) {
+ thread[i] = CreateThread(NULL,0,threadFunc1,
+ &tstruct[i],0,&threadid[i]);
+ ok(thread[i]!=NULL,"Create Thread failed\n");
+ }
+/* Test that the threads actually complete */
+ for(i=0;i<NUM_THREADS;i++) {
+ error=WaitForSingleObject(thread[i],5000);
+ ok(error==WAIT_OBJECT_0, "Thread did not complete within timelimit\n");
+ if(error!=WAIT_OBJECT_0) {
+ TerminateThread(thread[i],i+NUM_THREADS);
+ }
+ ok(GetExitCodeThread(thread[i],&exitCode),"Could not retrieve ext code\n");
+ ok(exitCode==i+NUM_THREADS,"Thread returned an incorrect exit code\n");
+ }
+/* Test that each thread executed in its parent's address space
+ (it was able to change threadmem and pass that change back to its parent)
+ and that each thread id was independant). Note that we prove that the
+ threads actually execute concurrently by having them block on each other
+ in threadFunc1
+*/
+ for(i=0;i<NUM_THREADS;i++) {
+ error=0;
+ for(j=i+1;j<NUM_THREADS;j++) {
+ if (threadmem[i]==threadmem[j]) {
+ error=1;
+ }
+ }
+ ok(!error && threadmem[i]==threadid[i] && threadmem[i]!=curthreadId,
+ "Thread did not execute successfully\n");
+ ok(CloseHandle(thread[i])!=0,"CloseHandle failed\n");
+ }
+ ok(TlsFree(tlsIndex)!=0,"TlsFree failed\n");
+
+ /* Test how passing NULL as a pointer to threadid works */
+ SetLastError(0xFACEaBAD);
+ thread[0] = CreateThread(NULL,0,threadFunc2,NULL,0,NULL);
+ GLE = GetLastError();
+ if (thread[0]) { /* NT */
+ ok(GLE==0xFACEaBAD, "CreateThread set last error to %ld, expected 4207848365\n", GLE);
+ ret = WaitForSingleObject(thread[0],100);
+ ok(ret==WAIT_OBJECT_0, "threadFunc2 did not exit during 100 ms\n");
+ ret = GetExitCodeThread(thread[0],&exitCode);
+ ok(ret!=0, "GetExitCodeThread returned %ld (expected nonzero)\n", ret);
+ ok(exitCode==99, "threadFunc2 exited with code: %ld (expected 99)\n", exitCode);
+ ok(CloseHandle(thread[0])!=0,"Error closing thread handle\n");
+ }
+ else { /* 9x */
+ ok(GLE==ERROR_INVALID_PARAMETER, "CreateThread set last error to %ld, expected 87\n", GLE);
+ }
+}
+
+/* Check that using the CREATE_SUSPENDED flag works */
+static VOID test_CreateThread_suspended(void)
+{
+ HANDLE thread;
+ DWORD threadId;
+ int error;
+
+ thread = CreateThread(NULL,0,threadFunc2,NULL,
+ CREATE_SUSPENDED,&threadId);
+ ok(thread!=NULL,"Create Thread failed\n");
+/* Check that the thread is suspended */
+ ok(SuspendThread(thread)==1,"Thread did not start suspended\n");
+ ok(ResumeThread(thread)==2,"Resume thread returned an invalid value\n");
+/* Check that resume thread didn't actually start the thread. I can't think
+ of a better way of checking this than just waiting. I am not sure if this
+ will work on slow computers.
+*/
+ ok(WaitForSingleObject(thread,1000)==WAIT_TIMEOUT,
+ "ResumeThread should not have actually started the thread\n");
+/* Now actually resume the thread and make sure that it actually completes*/
+ ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
+ ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
+ "Thread did not resume\n");
+ if(error!=WAIT_OBJECT_0) {
+ TerminateThread(thread,1);
+ }
+ ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
+}
+
+/* Check that SuspendThread and ResumeThread work */
+static VOID test_SuspendThread(void)
+{
+ HANDLE thread,access_thread;
+ DWORD threadId,exitCode,error;
+ int i;
+
+ thread = CreateThread(NULL,0,threadFunc3,NULL,
+ 0,&threadId);
+ ok(thread!=NULL,"Create Thread failed\n");
+/* Check that the thread is suspended */
+/* Note that this is a polling method, and there is a race between
+ SuspendThread being called (in the child, and the loop below timing out,
+ so the test could fail on a heavily loaded or slow computer.
+*/
+ error=0;
+ for(i=0;error==0 && i<100;i++) {
+ error=SuspendThread(thread);
+ ResumeThread(thread);
+ if(error==0) {
+ Sleep(50);
+ i++;
+ }
+ }
+ ok(error==1,"SuspendThread did not work\n");
+/* check that access restrictions are obeyed */
+ if (pOpenThread) {
+ access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_SUSPEND_RESUME),
+ 0,threadId);
+ ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
+ if (access_thread!=NULL) {
+ obey_ar(SuspendThread(access_thread)==~0UL);
+ obey_ar(ResumeThread(access_thread)==~0UL);
+ ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
+ }
+ }
+/* Double check that the thread really is suspended */
+ ok((error=GetExitCodeThread(thread,&exitCode))!=0 && exitCode==STILL_ACTIVE,
+ "Thread did not really suspend\n");
+/* Resume the thread, and make sure it actually completes */
+ ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
+ ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
+ "Thread did not resume\n");
+ if(error!=WAIT_OBJECT_0) {
+ TerminateThread(thread,1);
+ }
+ /* Trying to suspend a terminated thread should fail */
+ error=SuspendThread(thread);
+ ok(error==~0UL, "wrong return code: %ld\n", error);
+ ok(GetLastError()==ERROR_ACCESS_DENIED || GetLastError()==ERROR_NO_MORE_ITEMS, "unexpected error code: %ld\n", GetLastError());
+
+ ok(CloseHandle(thread)!=0,"CloseHandle Failed\n");
+}
+
+/* Check that TerminateThread works properly
+*/
+static VOID test_TerminateThread(void)
+{
+ HANDLE thread,access_thread,event;
+ DWORD threadId,exitCode;
+ event=CreateEventA(NULL,TRUE,FALSE,NULL);
+ thread = CreateThread(NULL,0,threadFunc4,
+ (LPVOID)event, 0,&threadId);
+ ok(thread!=NULL,"Create Thread failed\n");
+/* TerminateThread has a race condition in Wine. If the thread is terminated
+ before it starts, it leaves a process behind. Therefore, we wait for the
+ thread to signal that it has started. There is no easy way to force the
+ race to occur, so we don't try to find it.
+*/
+ ok(WaitForSingleObject(event,5000)==WAIT_OBJECT_0,
+ "TerminateThread didn't work\n");
+/* check that access restrictions are obeyed */
+ if (pOpenThread) {
+ access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_TERMINATE),
+ 0,threadId);
+ ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
+ if (access_thread!=NULL) {
+ obey_ar(TerminateThread(access_thread,99)==0);
+ ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
+ }
+ }
+/* terminate a job and make sure it terminates */
+ ok(TerminateThread(thread,99)!=0,"TerminateThread failed\n");
+ ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+ "TerminateThread didn't work\n");
+ ok(GetExitCodeThread(thread,&exitCode)!=STILL_ACTIVE,
+ "TerminateThread should not leave the thread 'STILL_ACTIVE'\n");
+ ok(exitCode==99, "TerminateThread returned invalid exit code\n");
+ ok(CloseHandle(thread)!=0,"Error Closing thread handle\n");
+}
+
+/* Check if CreateThread obeys the specified stack size. This code does
+ not work properly, and is currently disabled
+*/
+static VOID test_CreateThread_stack(void)
+{
+#if CHECK_STACK
+/* The only way I know of to test the stack size is to use alloca
+ and __try/__except. However, this is probably not portable,
+ and I couldn't get it to work under Wine anyhow. However, here
+ is the code which should allow for testing that CreateThread
+ respects the stack-size limit
+*/
+ HANDLE thread;
+ DWORD threadId,exitCode;
+
+ SYSTEM_INFO sysInfo;
+ sysInfo.dwPageSize=0;
+ GetSystemInfo(&sysInfo);
+ ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
+ thread = CreateThread(NULL,sysInfo.dwPageSize,
+ threadFunc5,&exitCode,
+ 0,&threadId);
+ ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+ "TerminateThread didn't work\n");
+ ok(exitCode==1,"CreateThread did not obey stack-size-limit\n");
+ ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
+#endif
+}
+
+/* Check whether setting/retrieving thread priorities works */
+static VOID test_thread_priority(void)
+{
+ HANDLE curthread,access_thread;
+ DWORD curthreadId,exitCode;
+ int min_priority=-2,max_priority=2;
+ BOOL disabled,rc;
+ int i;
+
+ curthread=GetCurrentThread();
+ curthreadId=GetCurrentThreadId();
+/* Check thread priority */
+/* NOTE: on Win2k/XP priority can be from -7 to 6. All other platforms it
+ is -2 to 2. However, even on a real Win2k system, using thread
+ priorities beyond the -2 to 2 range does not work. If you want to try
+ anyway, enable USE_EXTENDED_PRIORITIES
+*/
+ ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL,
+ "GetThreadPriority Failed\n");
+
+ if (pOpenThread) {
+/* check that access control is obeyed */
+ access_thread=pOpenThread(THREAD_ALL_ACCESS &
+ (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
+ 0,curthreadId);
+ ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
+ if (access_thread!=NULL) {
+ obey_ar(SetThreadPriority(access_thread,1)==0);
+ obey_ar(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN);
+ obey_ar(GetExitCodeThread(access_thread,&exitCode)==0);
+ ok(CloseHandle(access_thread),"Error Closing thread handle\n");
+ }
+#if USE_EXTENDED_PRIORITIES
+ min_priority=-7; max_priority=6;
+#endif
+ }
+ for(i=min_priority;i<=max_priority;i++) {
+ ok(SetThreadPriority(curthread,i)!=0,
+ "SetThreadPriority Failed for priority: %d\n",i);
+ ok(GetThreadPriority(curthread)==i,
+ "GetThreadPriority Failed for priority: %d\n",i);
+ }
+ ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0,
+ "SetThreadPriority Failed\n");
+ ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL,
+ "GetThreadPriority Failed\n");
+ ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0,
+ "SetThreadPriority Failed\n");
+ ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE,
+ "GetThreadPriority Failed\n");
+ ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed\n");
+
+/* Check thread priority boost */
+ if (!pGetThreadPriorityBoost || !pSetThreadPriorityBoost)
+ return; /* Win9x */
+
+ SetLastError(0xdeadbeef);
+ rc=pGetThreadPriorityBoost(curthread,&disabled);
+ if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return; /* WinME */
+
+/* check that access control is obeyed */
+ access_thread=pOpenThread(THREAD_ALL_ACCESS &
+ (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
+ 0,curthreadId);
+ ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
+ if (access_thread!=NULL) {
+ obey_ar(pSetThreadPriorityBoost(access_thread,1)==0);
+ obey_ar(pGetThreadPriorityBoost(access_thread,&disabled)==0);
+ ok(CloseHandle(access_thread),"Error Closing thread handle\n");
+ }
+
+ todo_wine {
+ ok(rc!=0,"error=%ld\n",GetLastError());
+
+ rc = pSetThreadPriorityBoost(curthread,1);
+ ok( rc != 0, "error=%ld\n",GetLastError());
+ rc=pGetThreadPriorityBoost(curthread,&disabled);
+ ok(rc!=0 && disabled==1,
+ "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
+
+ rc = pSetThreadPriorityBoost(curthread,0);
+ ok( rc != 0, "error=%ld\n",GetLastError());
+ rc=pGetThreadPriorityBoost(curthread,&disabled);
+ ok(rc!=0 && disabled==0,
+ "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
+ }
+}
+
+/* check the GetThreadTimes function */
+static VOID test_GetThreadTimes(void)
+{
+ HANDLE thread,access_thread=NULL;
+ FILETIME creationTime,exitTime,kernelTime,userTime;
+ DWORD threadId;
+ int error;
+
+ thread = CreateThread(NULL,0,threadFunc2,NULL,
+ CREATE_SUSPENDED,&threadId);
+
+ ok(thread!=NULL,"Create Thread failed\n");
+/* check that access control is obeyed */
+ if (pOpenThread) {
+ access_thread=pOpenThread(THREAD_ALL_ACCESS &
+ (~THREAD_QUERY_INFORMATION), 0,threadId);
+ ok(access_thread!=NULL,
+ "OpenThread returned an invalid handle\n");
+ }
+ ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
+ ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+ "ResumeThread didn't work\n");
+ creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99;
+ exitTime.dwLowDateTime=99; exitTime.dwHighDateTime=99;
+ kernelTime.dwLowDateTime=99; kernelTime.dwHighDateTime=99;
+ userTime.dwLowDateTime=99; userTime.dwHighDateTime=99;
+/* GetThreadTimes should set all of the parameters passed to it */
+ error=GetThreadTimes(thread,&creationTime,&exitTime,
+ &kernelTime,&userTime);
+ if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+ ok(error!=0,"GetThreadTimes failed\n");
+ ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99,
+ "creationTime was invalid\n");
+ ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99,
+ "exitTime was invalid\n");
+ ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99,
+ "kernelTimewas invalid\n");
+ ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99,
+ "userTime was invalid\n");
+ ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
+ if(access_thread!=NULL)
+ {
+ error=GetThreadTimes(access_thread,&creationTime,&exitTime,
+ &kernelTime,&userTime);
+ obey_ar(error==0);
+ }
+ }
+ if(access_thread!=NULL) {
+ ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
+ }
+}
+
+/* Check the processor affinity functions */
+/* NOTE: These functions should also be checked that they obey access control
+*/
+static VOID test_thread_processor(void)
+{
+ HANDLE curthread,curproc;
+ DWORD processMask,systemMask;
+ SYSTEM_INFO sysInfo;
+ int error=0;
+
+ sysInfo.dwNumberOfProcessors=0;
+ GetSystemInfo(&sysInfo);
+ ok(sysInfo.dwNumberOfProcessors>0,
+ "GetSystemInfo failed to return a valid # of processors\n");
+/* Use the current Thread/process for all tests */
+ curthread=GetCurrentThread();
+ ok(curthread!=NULL,"GetCurrentThread failed\n");
+ curproc=GetCurrentProcess();
+ ok(curproc!=NULL,"GetCurrentProcess failed\n");
+/* Check the Affinity Mask functions */
+ ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0,
+ "GetProcessAffinityMask failed\n");
+ ok(SetThreadAffinityMask(curthread,processMask)==processMask,
+ "SetThreadAffinityMask failed\n");
+ ok(SetThreadAffinityMask(curthread,processMask+1)==0,
+ "SetThreadAffinityMask passed for an illegal processor\n");
+/* NOTE: This only works on WinNT/2000/XP) */
+ if (pSetThreadIdealProcessor) {
+ todo_wine {
+ SetLastError(0);
+ error=pSetThreadIdealProcessor(curthread,0);
+ if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+ ok(error!=-1, "SetThreadIdealProcessor failed\n");
+ }
+ }
+ if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+ error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
+ ok(error==-1,
+ "SetThreadIdealProcessor succeeded with an illegal processor #\n");
+ todo_wine {
+ error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
+ ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n");
+ }
+ }
+ }
+}
+
+static VOID test_GetThreadExitCode(void)
+{
+ DWORD exitCode, threadid;
+ DWORD GLE, ret;
+ HANDLE thread;
+
+ ret = GetExitCodeThread((HANDLE)0x2bad2bad,&exitCode);
+ ok(ret==0, "GetExitCodeThread returned non zero value: %ld\n", ret);
+ GLE = GetLastError();
+ ok(GLE==ERROR_INVALID_HANDLE, "GetLastError returned %ld (expected 6)\n", GLE);
+
+ thread = CreateThread(NULL,0,threadFunc2,NULL,0,&threadid);
+ ret = WaitForSingleObject(thread,100);
+ ok(ret==WAIT_OBJECT_0, "threadFunc2 did not exit during 100 ms\n");
+ ret = GetExitCodeThread(thread,&exitCode);
+ ok(ret==exitCode || ret==1,
+ "GetExitCodeThread returned %ld (expected 1 or %ld)\n", ret, exitCode);
+ ok(exitCode==99, "threadFunc2 exited with code %ld (expected 99)\n", exitCode);
+ ok(CloseHandle(thread)!=0,"Error closing thread handle\n");
+}
+
+#ifdef __i386__
+
+static int test_value = 0;
+static HANDLE event;
+
+static void WINAPI set_test_val( int val )
+{
+ test_value += val;
+}
+
+static DWORD WINAPI threadFunc6(LPVOID p)
+{
+ SetEvent( event );
+ Sleep( 1000 );
+ test_value *= (int)p;
+ return 0;
+}
+
+static void test_SetThreadContext(void)
+{
+ CONTEXT ctx;
+ int *stack;
+ HANDLE thread;
+ DWORD threadid;
+ DWORD prevcount;
+
+ SetLastError(0xdeadbeef);
+ event = CreateEvent( NULL, TRUE, FALSE, NULL );
+ thread = CreateThread( NULL, 0, threadFunc6, (void *)2, 0, &threadid );
+ ok( thread != NULL, "CreateThread failed : (%ld)\n", GetLastError() );
+ if (!thread)
+ {
+ trace("Thread creation failed, skipping rest of test\n");
+ return;
+ }
+ WaitForSingleObject( event, INFINITE );
+ SuspendThread( thread );
+ CloseHandle( event );
+
+ ctx.ContextFlags = CONTEXT_FULL;
+ SetLastError(0xdeadbeef);
+ ok( GetThreadContext( thread, &ctx ), "GetThreadContext failed : (%ld)\n", GetLastError() );
+
+ /* simulate a call to set_test_val(10) */
+ stack = (int *)ctx.Esp;
+ stack[-1] = 10;
+ stack[-2] = ctx.Eip;
+ ctx.Esp -= 2 * sizeof(int *);
+ ctx.Eip = (DWORD)set_test_val;
+ SetLastError(0xdeadbeef);
+ ok( SetThreadContext( thread, &ctx ), "SetThreadContext failed : (%ld)\n", GetLastError() );
+
+ SetLastError(0xdeadbeef);
+ prevcount = ResumeThread( thread );
+ ok ( prevcount == 1, "Previous suspend count (%ld) instead of 1, last error : (%ld)\n",
+ prevcount, GetLastError() );
+
+ WaitForSingleObject( thread, INFINITE );
+ ok( test_value == 20, "test_value %d instead of 20\n", test_value );
+}
+
+#endif /* __i386__ */
+
+
+START_TEST(thread)
+{
+ HINSTANCE lib;
+/* Neither Cygwin nor mingW export OpenThread, so do a dynamic check
+ so that the compile passes
+*/
+ lib=GetModuleHandleA("kernel32.dll");
+ ok(lib!=NULL,"Couldn't get a handle for kernel32.dll\n");
+ pGetThreadPriorityBoost=(GetThreadPriorityBoost_t)GetProcAddress(lib,"GetThreadPriorityBoost");
+ pOpenThread=(OpenThread_t)GetProcAddress(lib,"OpenThread");
+ pSetThreadIdealProcessor=(SetThreadIdealProcessor_t)GetProcAddress(lib,"SetThreadIdealProcessor");
+ pSetThreadPriorityBoost=(SetThreadPriorityBoost_t)GetProcAddress(lib,"SetThreadPriorityBoost");
+ test_CreateThread_basic();
+ test_CreateThread_suspended();
+ test_SuspendThread();
+ test_TerminateThread();
+ test_CreateThread_stack();
+ test_thread_priority();
+ test_GetThreadTimes();
+ test_thread_processor();
+ test_GetThreadExitCode();
+#ifdef __i386__
+ test_SetThreadContext();
+#endif
+}
--- /dev/null
+/*
+ * Unit test suite for time functions
+ *
+ * Copyright 2004 Uwe Bonnes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include "winbase.h"
+
+#define SECSPERMIN 60
+#define SECSPERDAY 86400
+/* 1601 to 1970 is 369 years plus 89 leap days */
+#define SECS_1601_TO_1970 ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
+#define TICKSPERSEC 10000000
+#define TICKSPERMSEC 10000
+#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
+
+
+#define NEWYEAR_1980_HI 0x01a8e79f
+#define NEWYEAR_1980_LO 0xe1d58000
+
+#define MAYDAY_2002_HI 0x01c1f107
+#define MAYDAY_2002_LO 0xb82b6000
+
+#define ATIME_HI 0x1c2349b
+#define ATIME_LOW 0x580716b0
+
+#define LOCAL_ATIME_HI 0x01c23471
+#define LOCAL_ATIME_LOW 0x6f310eb0
+
+#define DOS_DATE(y,m,d) ( (((y)-1980)<<9) | ((m)<<5) | (d) )
+#define DOS_TIME(h,m,s) ( ((h)<<11) | ((m)<<5) | ((s)>>1) )
+
+
+#define SETUP_1980(st) \
+ (st).wYear = 1980; \
+ (st).wMonth = 1; \
+ (st).wDay = 1; \
+ (st).wHour = 0; \
+ (st).wMinute = 0; \
+ (st).wSecond = 0; \
+ (st).wMilliseconds = 0;
+
+#define SETUP_2002(st) \
+ (st).wYear = 2002; \
+ (st).wMonth = 5; \
+ (st).wDay = 1; \
+ (st).wHour = 12; \
+ (st).wMinute = 0; \
+ (st).wSecond = 0; \
+ (st).wMilliseconds = 0;
+
+#define SETUP_ATIME(st) \
+ (st).wYear = 2002; \
+ (st).wMonth = 7; \
+ (st).wDay = 26; \
+ (st).wHour = 11; \
+ (st).wMinute = 55; \
+ (st).wSecond = 32; \
+ (st).wMilliseconds = 123;
+
+
+
+static void test_conversions(void)
+{
+ FILETIME ft;
+ SYSTEMTIME st;
+
+ memset(&ft,0,sizeof ft);
+
+ SETUP_ATIME(st)
+ ok (SystemTimeToFileTime(&st,&ft), "Conversion Failed ATIME\n");
+ ok( (!((ft.dwHighDateTime != ATIME_HI) || (ft.dwLowDateTime!=ATIME_LOW))),
+ "Wrong time for ATIME: %08lx %08lx (correct %08x %08x)\n",
+ ft.dwLowDateTime, ft.dwHighDateTime, ATIME_LOW, ATIME_HI);
+
+
+ SETUP_2002(st)
+ ok (SystemTimeToFileTime(&st, &ft), "Conversion failed 2002\n");
+
+ ok( (!((ft.dwHighDateTime != MAYDAY_2002_HI) ||
+ (ft.dwLowDateTime!=MAYDAY_2002_LO))),
+ "Wrong time for 2002 %08lx %08lx (correct %08x %08x)\n", ft.dwLowDateTime,
+ ft.dwHighDateTime, MAYDAY_2002_LO, MAYDAY_2002_HI);
+
+
+ SETUP_1980(st)
+ ok((SystemTimeToFileTime(&st, &ft)), "Conversion failed 1980\n");
+
+ ok( (!((ft.dwHighDateTime!=NEWYEAR_1980_HI) ||
+ (ft.dwLowDateTime!=NEWYEAR_1980_LO))) ,
+ "Wrong time for 1980 %08lx %08lx (correct %08x %08x)\n", ft.dwLowDateTime,
+ ft.dwHighDateTime, NEWYEAR_1980_LO,NEWYEAR_1980_HI );
+
+ ok(DosDateTimeToFileTime(DOS_DATE(1980,1,1),DOS_TIME(0,0,0),&ft),
+ "DosDateTimeToFileTime() failed\n");
+
+ ok( (!((ft.dwHighDateTime!=NEWYEAR_1980_HI) ||
+ (ft.dwLowDateTime!=NEWYEAR_1980_LO))),
+ "Wrong time DosDateTimeToFileTime %08lx %08lx (correct %08x %08x)\n",
+ ft.dwHighDateTime, ft.dwLowDateTime, NEWYEAR_1980_HI, NEWYEAR_1980_LO);
+
+}
+
+static void test_invalid_arg(void)
+{
+ FILETIME ft;
+ SYSTEMTIME st;
+
+
+ /* Invalid argument checks */
+
+ memset(&ft,0,sizeof ft);
+ ok( DosDateTimeToFileTime(DOS_DATE(1980,1,1),DOS_TIME(0,0,0),&ft), /* this is 1 Jan 1980 00:00:00 */
+ "DosDateTimeToFileTime() failed\n");
+
+ ok( (ft.dwHighDateTime==NEWYEAR_1980_HI) && (ft.dwLowDateTime==NEWYEAR_1980_LO),
+ "filetime for 1/1/80 00:00:00 was %08lx %08lx\n", ft.dwHighDateTime, ft.dwLowDateTime);
+
+ /* now check SystemTimeToFileTime */
+ memset(&ft,0,sizeof ft);
+
+
+ /* try with a bad month */
+ SETUP_1980(st)
+ st.wMonth = 0;
+
+ ok( !SystemTimeToFileTime(&st, &ft), "bad month\n");
+
+ /* with a bad hour */
+ SETUP_1980(st)
+ st.wHour = 24;
+
+ ok( !SystemTimeToFileTime(&st, &ft), "bad hour\n");
+
+ /* with a bad minute */
+ SETUP_1980(st)
+ st.wMinute = 60;
+
+ ok( !SystemTimeToFileTime(&st, &ft), "bad minute\n");
+}
+
+static void test_GetTimeZoneInformation(void)
+{
+ TIME_ZONE_INFORMATION tzinfo, tzinfo1;
+ DWORD res = GetTimeZoneInformation(&tzinfo);
+ ok(res != TIME_ZONE_ID_INVALID, "GetTimeZoneInformation failed\n");
+ ok(SetEnvironmentVariableA("TZ","GMT0") != 0,
+ "SetEnvironmentVariableA failed\n");
+ res = GetTimeZoneInformation(&tzinfo1);
+ ok(res != TIME_ZONE_ID_INVALID, "GetTimeZoneInformation failed\n");
+
+ ok(((tzinfo.Bias == tzinfo1.Bias) &&
+ (tzinfo.StandardBias == tzinfo1.StandardBias) &&
+ (tzinfo.DaylightBias == tzinfo1.DaylightBias)),
+ "Bias influenced by TZ variable\n");
+ ok(SetEnvironmentVariableA("TZ",NULL) != 0,
+ "SetEnvironmentVariableA failed\n");
+
+}
+
+static void test_FileTimeToSystemTime(void)
+{
+ FILETIME ft;
+ SYSTEMTIME st;
+ ULONGLONG time = (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970;
+ BOOL ret;
+
+ ft.dwHighDateTime = 0;
+ ft.dwLowDateTime = 0;
+ ret = FileTimeToSystemTime(&ft, &st);
+ ok( ret,
+ "FileTimeToSystemTime() failed with Error 0x%08lx\n",GetLastError());
+ ok(((st.wYear == 1601) && (st.wMonth == 1) && (st.wDay == 1) &&
+ (st.wHour == 0) && (st.wMinute == 0) && (st.wSecond == 0) &&
+ (st.wMilliseconds == 0)),
+ "Got Year %4d Month %2d Day %2d\n", st.wYear, st.wMonth, st.wDay);
+
+ ft.dwHighDateTime = (UINT)(time >> 32);
+ ft.dwLowDateTime = (UINT)time;
+ ret = FileTimeToSystemTime(&ft, &st);
+ ok( ret,
+ "FileTimeToSystemTime() failed with Error 0x%08lx\n",GetLastError());
+ ok(((st.wYear == 1970) && (st.wMonth == 1) && (st.wDay == 1) &&
+ (st.wHour == 0) && (st.wMinute == 0) && (st.wSecond == 1) &&
+ (st.wMilliseconds == 0)),
+ "Got Year %4d Month %2d Day %2d Hour %2d Min %2d Sec %2d mSec %3d\n",
+ st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
+ st.wMilliseconds);
+}
+
+static void test_FileTimeToLocalFileTime(void)
+{
+ FILETIME ft, lft;
+ SYSTEMTIME st;
+ TIME_ZONE_INFORMATION tzinfo;
+ DWORD res = GetTimeZoneInformation(&tzinfo);
+ ULONGLONG time = (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970 +
+ (LONGLONG)(tzinfo.Bias +
+ ( res == TIME_ZONE_ID_STANDARD ? tzinfo.StandardBias :
+ ( res == TIME_ZONE_ID_DAYLIGHT ? tzinfo.DaylightBias : 0 ))) *
+ SECSPERMIN *TICKSPERSEC;
+ BOOL ret;
+
+ ok( res != TIME_ZONE_ID_INVALID , "GetTimeZoneInformation failed\n");
+ ft.dwHighDateTime = (UINT)(time >> 32);
+ ft.dwLowDateTime = (UINT)time;
+ ret = FileTimeToLocalFileTime(&ft, &lft);
+ ok( ret,
+ "FileTimeToLocalFileTime() failed with Error 0x%08lx\n",GetLastError());
+ FileTimeToSystemTime(&lft, &st);
+ ok(((st.wYear == 1970) && (st.wMonth == 1) && (st.wDay == 1) &&
+ (st.wHour == 0) && (st.wMinute == 0) && (st.wSecond == 1) &&
+ (st.wMilliseconds == 0)),
+ "Got Year %4d Month %2d Day %2d Hour %2d Min %2d Sec %2d mSec %3d\n",
+ st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
+ st.wMilliseconds);
+
+ ok(SetEnvironmentVariableA("TZ","GMT") != 0,
+ "SetEnvironmentVariableA failed\n");
+ ok(res != TIME_ZONE_ID_INVALID, "GetTimeZoneInformation failed\n");
+ ret = FileTimeToLocalFileTime(&ft, &lft);
+ ok( ret,
+ "FileTimeToLocalFileTime() failed with Error 0x%08lx\n",GetLastError());
+ FileTimeToSystemTime(&lft, &st);
+ ok(((st.wYear == 1970) && (st.wMonth == 1) && (st.wDay == 1) &&
+ (st.wHour == 0) && (st.wMinute == 0) && (st.wSecond == 1) &&
+ (st.wMilliseconds == 0)),
+ "Got Year %4d Month %2d Day %2d Hour %2d Min %2d Sec %2d mSec %3d\n",
+ st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
+ st.wMilliseconds);
+ ok(SetEnvironmentVariableA("TZ",NULL) != 0,
+ "SetEnvironmentVariableA failed\n");
+}
+
+
+/* test TzSpecificLocalTimeToSystemTime and SystemTimeToTzSpecificLocalTime
+ * these are in winXP and later */
+typedef HANDLE (WINAPI *fnTzSpecificLocalTimeToSystemTime)( LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME);
+typedef HANDLE (WINAPI *fnSystemTimeToTzSpecificLocalTime)( LPTIME_ZONE_INFORMATION, LPSYSTEMTIME, LPSYSTEMTIME);
+
+typedef struct {
+ int nr; /* test case number for easier lookup */
+ TIME_ZONE_INFORMATION *ptz; /* ptr to timezone */
+ SYSTEMTIME slt; /* system/local time to convert */
+ WORD ehour; /* expected hour */
+} TZLT2ST_case;
+
+static void test_TzSpecificLocalTimeToSystemTime(void)
+{
+ HMODULE hKernel = GetModuleHandle("kernel32");
+ fnTzSpecificLocalTimeToSystemTime pTzSpecificLocalTimeToSystemTime;
+ fnSystemTimeToTzSpecificLocalTime pSystemTimeToTzSpecificLocalTime = NULL;
+ TIME_ZONE_INFORMATION tzE, tzW, tzS;
+ SYSTEMTIME result;
+ int i, j, year;
+ pTzSpecificLocalTimeToSystemTime = (fnTzSpecificLocalTimeToSystemTime) GetProcAddress( hKernel, "TzSpecificLocalTimeToSystemTime");
+ if(pTzSpecificLocalTimeToSystemTime)
+ pSystemTimeToTzSpecificLocalTime = (fnTzSpecificLocalTimeToSystemTime) GetProcAddress( hKernel, "SystemTimeToTzSpecificLocalTime");
+ if( !pSystemTimeToTzSpecificLocalTime)
+ return;
+ ZeroMemory( &tzE, sizeof(tzE));
+ ZeroMemory( &tzW, sizeof(tzW));
+ ZeroMemory( &tzS, sizeof(tzS));
+ /* timezone Eastern hemisphere */
+ tzE.Bias=-600;
+ tzE.StandardBias=0;
+ tzE.DaylightBias=-60;
+ tzE.StandardDate.wMonth=10;
+ tzE.StandardDate.wDayOfWeek=0; /*sunday */
+ tzE.StandardDate.wDay=5; /* last (sunday) of the month */
+ tzE.StandardDate.wHour=3;
+ tzE.DaylightDate.wMonth=3;
+ tzE.DaylightDate.wDay=5;
+ tzE.DaylightDate.wHour=2;
+ /* timezone Western hemisphere */
+ tzW.Bias=240;
+ tzW.StandardBias=0;
+ tzW.DaylightBias=-60;
+ tzW.StandardDate.wMonth=10;
+ tzW.StandardDate.wDayOfWeek=0; /*sunday */
+ tzW.StandardDate.wDay=4; /* 4th (sunday) of the month */
+ tzW.StandardDate.wHour=2;
+ tzW.DaylightDate.wMonth=4;
+ tzW.DaylightDate.wDay=1;
+ tzW.DaylightDate.wHour=2;
+ /* timezone Eastern hemisphere */
+ tzS.Bias=240;
+ tzS.StandardBias=0;
+ tzS.DaylightBias=-60;
+ tzS.StandardDate.wMonth=4;
+ tzS.StandardDate.wDayOfWeek=0; /*sunday */
+ tzS.StandardDate.wDay=1; /* 1st (sunday) of the month */
+ tzS.StandardDate.wHour=2;
+ tzS.DaylightDate.wMonth=10;
+ tzS.DaylightDate.wDay=4;
+ tzS.DaylightDate.wHour=2;
+ /*tests*/
+ /* TzSpecificLocalTimeToSystemTime */
+ { TZLT2ST_case cases[] = {
+ /* standard->daylight transition */
+ { 1, &tzE, {2004,3,-1,28,1,0,0,0}, 15 },
+ { 2, &tzE, {2004,3,-1,28,1,59,59,999}, 15},
+ { 3, &tzE, {2004,3,-1,28,2,0,0,0}, 15},
+ /* daylight->standard transition */
+ { 4, &tzE, {2004,10,-1,31,2,0,0,0} , 15 },
+ { 5, &tzE, {2004,10,-1,31,2,59,59,999}, 15 },
+ { 6, &tzE, {2004,10,-1,31,3,0,0,0}, 17 },
+ /* West and with fixed weekday of the month */
+ { 7, &tzW, {2004,4,-1,4,1,0,0,0}, 5},
+ { 8, &tzW, {2004,4,-1,4,1,59,59,999}, 5},
+ { 9, &tzW, {2004,4,-1,4,2,0,0,0}, 5},
+ { 10, &tzW, {2004,10,-1,24,1,0,0,0}, 4},
+ { 11, &tzW, {2004,10,-1,24,1,59,59,999}, 4},
+ { 12, &tzW, {2004,10,-1,24,2,0,0,0 }, 6},
+ /* and now south */
+ { 13, &tzS, {2004,4,-1,4,1,0,0,0}, 4},
+ { 14, &tzS, {2004,4,-1,4,1,59,59,999}, 4},
+ { 15, &tzS, {2004,4,-1,4,2,0,0,0}, 6},
+ { 16, &tzS, {2004,10,-1,24,1,0,0,0}, 5},
+ { 17, &tzS, {2004,10,-1,24,1,59,59,999}, 5},
+ { 18, &tzS, {2004,10,-1,24,2,0,0,0}, 5},
+ {0}
+ };
+ /* days of transitions to put into the cases array */
+ int yeardays[][6]=
+ {
+ {28,31,4,24,4,24} /* 1999 */
+ , {26,29,2,22,2,22} /* 2000 */
+ , {25,28,1,28,1,28} /* 2001 */
+ , {31,27,7,27,7,27} /* 2002 */
+ , {30,26,6,26,6,26} /* 2003 */
+ , {28,31,4,24,4,24} /* 2004 */
+ , {27,30,3,23,3,23} /* 2005 */
+ , {26,29,2,22,2,22} /* 2006 */
+ , {25,28,1,28,1,28} /* 2007 */
+ , {30,26,6,26,6,26} /* 2008 */
+ , {29,25,5,25,5,25} /* 2009 */
+ , {28,31,4,24,4,24} /* 2010 */
+ , {27,30,3,23,3,23} /* 2011 */
+ , {25,28,1,28,1,28} /* 2012 */
+ , {31,27,7,27,7,27} /* 2013 */
+ , {30,26,6,26,6,26} /* 2014 */
+ , {29,25,5,25,5,25} /* 2015 */
+ , {27,30,3,23,3,23} /* 2016 */
+ , {26,29,2,22,2,22} /* 2017 */
+ , {25,28,1,28,1,28} /* 2018 */
+ , {31,27,7,27,7,27} /* 2019 */
+ ,{0}
+ };
+ for( j=0 , year = 1999; yeardays[j][0] ; j++, year++) {
+ for (i=0; cases[i].nr; i++) {
+ if(i) cases[i].nr += 18;
+ cases[i].slt.wYear = year;
+ cases[i].slt.wDay = yeardays[j][i/3];
+ pTzSpecificLocalTimeToSystemTime( cases[i].ptz, &(cases[i].slt), &result);
+ ok( result.wHour == cases[i].ehour,
+ "Test TzSpecificLocalTimeToSystemTime #%d. Got %4d-%02d-%02d %02d:%02d. Expect hour = %02d\n",
+ cases[i].nr, result.wYear, result.wMonth, result.wDay,
+ result.wHour, result.wMinute, cases[i].ehour);
+ }
+ }
+ }
+ /* SystemTimeToTzSpecificLocalTime */
+ { TZLT2ST_case cases[] = {
+ /* standard->daylight transition */
+ { 1, &tzE, {2004,3,-1,27,15,0,0,0}, 1 },
+ { 2, &tzE, {2004,3,-1,27,15,59,59,999}, 1},
+ { 3, &tzE, {2004,3,-1,27,16,0,0,0}, 3},
+ /* daylight->standard transition */
+ { 4, &tzE, {2004,10,-1,30,15,0,0,0}, 2 },
+ { 5, &tzE, {2004,10,-1,30,15,59,59,999}, 2 },
+ { 6, &tzE, {2004,10,-1,30,16,0,0,0}, 2 },
+ /* West and with fixed weekday of the month */
+ { 7, &tzW, {2004,4,-1,4,5,0,0,0}, 1},
+ { 8, &tzW, {2004,4,-1,4,5,59,59,999}, 1},
+ { 9, &tzW, {2004,4,-1,4,6,0,0,0}, 3},
+ { 10, &tzW, {2004,10,-1,24,4,0,0,0}, 1},
+ { 11, &tzW, {2004,10,-1,24,4,59,59,999}, 1},
+ { 12, &tzW, {2004,10,-1,24,5,0,0,0 }, 1},
+ /* and now south */
+ { 13, &tzS, {2004,4,-1,4,4,0,0,0}, 1},
+ { 14, &tzS, {2004,4,-1,4,4,59,59,999}, 1},
+ { 15, &tzS, {2004,4,-1,4,5,0,0,0}, 1},
+ { 16, &tzS, {2004,10,-1,24,5,0,0,0}, 1},
+ { 17, &tzS, {2004,10,-1,24,5,59,59,999}, 1},
+ { 18, &tzS, {2004,10,-1,24,6,0,0,0}, 3},
+
+ {0}
+ };
+ /* days of transitions to put into the cases array */
+ int yeardays[][6]=
+ {
+ {27,30,4,24,4,24} /* 1999 */
+ , {25,28,2,22,2,22} /* 2000 */
+ , {24,27,1,28,1,28} /* 2001 */
+ , {30,26,7,27,7,27} /* 2002 */
+ , {29,25,6,26,6,26} /* 2003 */
+ , {27,30,4,24,4,24} /* 2004 */
+ , {26,29,3,23,3,23} /* 2005 */
+ , {25,28,2,22,2,22} /* 2006 */
+ , {24,27,1,28,1,28} /* 2007 */
+ , {29,25,6,26,6,26} /* 2008 */
+ , {28,24,5,25,5,25} /* 2009 */
+ , {27,30,4,24,4,24} /* 2010 */
+ , {26,29,3,23,3,23} /* 2011 */
+ , {24,27,1,28,1,28} /* 2012 */
+ , {30,26,7,27,7,27} /* 2013 */
+ , {29,25,6,26,6,26} /* 2014 */
+ , {28,24,5,25,5,25} /* 2015 */
+ , {26,29,3,23,3,23} /* 2016 */
+ , {25,28,2,22,2,22} /* 2017 */
+ , {24,27,1,28,1,28} /* 2018 */
+ , {30,26,7,27,7,27} /* 2019 */
+ };
+ for( j=0 , year = 1999; yeardays[j][0] ; j++, year++) {
+ for (i=0; cases[i].nr; i++) {
+ if(i) cases[i].nr += 18;
+ cases[i].slt.wYear = year;
+ cases[i].slt.wDay = yeardays[j][i/3];
+ pSystemTimeToTzSpecificLocalTime( cases[i].ptz, &(cases[i].slt), &result);
+ ok( result.wHour == cases[i].ehour,
+ "Test SystemTimeToTzSpecificLocalTime #%d. Got %4d-%02d-%02d %02d:%02d. Expect hour = %02d\n",
+ cases[i].nr, result.wYear, result.wMonth, result.wDay,
+ result.wHour, result.wMinute, cases[i].ehour);
+ }
+ }
+
+ }
+}
+
+START_TEST(time)
+{
+ test_conversions();
+ test_invalid_arg();
+ test_GetTimeZoneInformation();
+ test_FileTimeToSystemTime();
+ test_FileTimeToLocalFileTime();
+ test_TzSpecificLocalTimeToSystemTime();
+}
--- /dev/null
+/*
+ * Unit test suite for time functions
+ *
+ * Copyright 2004 Mike McCormack
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _WIN32_WINNT 0x0501
+
+#include "wine/test.h"
+#include "winbase.h"
+
+typedef HANDLE (WINAPI *fnCreateWaitableTimerA)( SECURITY_ATTRIBUTES*, BOOL, LPSTR );
+typedef BOOL (WINAPI *fnSetWaitableTimer)(HANDLE, LARGE_INTEGER*, LONG, PTIMERAPCROUTINE, LPVOID, BOOL);
+
+
+static void test_timer(void)
+{
+ HMODULE hker = GetModuleHandle("kernel32");
+ fnCreateWaitableTimerA pCreateWaitableTimerA;
+ fnSetWaitableTimer pSetWaitableTimer;
+ HANDLE handle;
+ BOOL r;
+ LARGE_INTEGER due;
+
+ pCreateWaitableTimerA = (fnCreateWaitableTimerA) GetProcAddress( hker, "CreateWaitableTimerA");
+ if( !pCreateWaitableTimerA )
+ return;
+
+ pSetWaitableTimer = (fnSetWaitableTimer) GetProcAddress( hker, "SetWaitableTimer");
+ if( !pSetWaitableTimer )
+ return;
+
+ /* try once with a positive number */
+ handle = pCreateWaitableTimerA( NULL, 0, NULL );
+ ok( handle != NULL, "failed to create waitable timer with no name\n" );
+
+ due.QuadPart = 10000;
+ r = pSetWaitableTimer( handle, &due, 0x1f4, NULL, NULL, FALSE );
+ ok( r, "failed to set timer\n");
+
+ CloseHandle( handle );
+
+ /* try once with a negative number */
+ handle = pCreateWaitableTimerA( NULL, 0, NULL );
+ ok( handle != NULL, "failed to create waitable timer with no name\n" );
+
+ due.QuadPart = -10000;
+ r = pSetWaitableTimer( handle, &due, 0x1f4, NULL, NULL, FALSE );
+ ok( r, "failed to set timer\n");
+
+ CloseHandle( handle );
+}
+
+START_TEST(timer)
+{
+ test_timer();
+}
--- /dev/null
+/*
+ * Unit test suite for Virtual* family of APIs.
+ *
+ * Copyright 2004 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/test.h"
+
+static void test_VirtualAlloc(void)
+{
+ void *addr1, *addr2;
+ DWORD old_prot;
+ MEMORY_BASIC_INFORMATION info;
+
+ SetLastError(0xdeadbeef);
+ addr1 = VirtualAlloc(0, 0, MEM_RESERVE, PAGE_NOACCESS);
+ ok(addr1 == NULL, "VirtualAlloc should fail on zero-sized allocation\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER /* NT */ ||
+ GetLastError() == ERROR_NOT_ENOUGH_MEMORY, /* Win9x */
+ "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+
+ addr1 = VirtualAlloc(0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
+ ok(addr1 != NULL, "VirtualAlloc failed\n");
+
+ /* test a not committed memory */
+ ok(VirtualQuery(addr1, &info, sizeof(info)) == sizeof(info),
+ "VirtualQuery failed\n");
+ ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
+ ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
+ ok(info.AllocationProtect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n", info.AllocationProtect);
+ ok(info.RegionSize == 0x10000, "%lx != 0x10000\n", info.RegionSize);
+ ok(info.State == MEM_RESERVE, "%lx != MEM_RESERVE\n", info.State);
+ /* NT reports Protect == 0 for a not committed memory block */
+ ok(info.Protect == 0 /* NT */ ||
+ info.Protect == PAGE_NOACCESS, /* Win9x */
+ "%lx != PAGE_NOACCESS\n", info.Protect);
+ ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+
+ SetLastError(0xdeadbeef);
+ ok(!VirtualProtect(addr1, 0xFFFC, PAGE_READONLY, &old_prot),
+ "VirtualProtect should fail on a not committed memory\n");
+ ok(GetLastError() == ERROR_INVALID_ADDRESS /* NT */ ||
+ GetLastError() == ERROR_INVALID_PARAMETER, /* Win9x */
+ "got %ld, expected ERROR_INVALID_ADDRESS\n", GetLastError());
+
+ addr2 = VirtualAlloc(addr1, 0x1000, MEM_COMMIT, PAGE_NOACCESS);
+ ok(addr1 == addr2, "VirtualAlloc failed\n");
+
+ /* test a committed memory */
+ ok(VirtualQuery(addr1, &info, sizeof(info)) == sizeof(info),
+ "VirtualQuery failed\n");
+ ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
+ ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
+ ok(info.AllocationProtect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n", info.AllocationProtect);
+ ok(info.RegionSize == 0x1000, "%lx != 0x1000\n", info.RegionSize);
+ ok(info.State == MEM_COMMIT, "%lx != MEM_COMMIT\n", info.State);
+ /* this time NT reports PAGE_NOACCESS as well */
+ ok(info.Protect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n", info.Protect);
+ ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+
+ /* this should fail, since not the whole range is committed yet */
+ SetLastError(0xdeadbeef);
+ ok(!VirtualProtect(addr1, 0xFFFC, PAGE_READONLY, &old_prot),
+ "VirtualProtect should fail on a not committed memory\n");
+ ok(GetLastError() == ERROR_INVALID_ADDRESS /* NT */ ||
+ GetLastError() == ERROR_INVALID_PARAMETER, /* Win9x */
+ "got %ld, expected ERROR_INVALID_ADDRESS\n", GetLastError());
+
+ ok(VirtualProtect(addr1, 0x1000, PAGE_READONLY, &old_prot), "VirtualProtect failed\n");
+ ok(old_prot == PAGE_NOACCESS,
+ "wrong old protection: got %04lx instead of PAGE_NOACCESS\n", old_prot);
+
+ ok(VirtualProtect(addr1, 0x1000, PAGE_READWRITE, &old_prot), "VirtualProtect failed\n");
+ ok(old_prot == PAGE_READONLY,
+ "wrong old protection: got %04lx instead of PAGE_READONLY\n", old_prot);
+
+ ok(!VirtualFree(addr1, 0x10000, 0), "VirtualFree should fail with type 0\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+
+ ok(VirtualFree(addr1, 0x10000, MEM_DECOMMIT), "VirtualFree failed\n");
+
+ /* if the type is MEM_RELEASE, size must be 0 */
+ ok(!VirtualFree(addr1, 1, MEM_RELEASE), "VirtualFree should fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+
+ ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
+}
+
+static void test_MapViewOfFile(void)
+{
+ static const char testfile[] = "testfile.xxx";
+ HANDLE file, mapping;
+ void *ptr;
+
+ file = CreateFileA( testfile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0 );
+ ok( file != INVALID_HANDLE_VALUE, "Failed to create test file\n" );
+ SetFilePointer( file, 4096, NULL, FILE_BEGIN );
+ SetEndOfFile( file );
+
+ /* read/write mapping */
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_READWRITE, 0, 4096, NULL );
+ ok( mapping != 0, "CreateFileMapping failed\n" );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile FILE_MAPE_READ failed\n" );
+ UnmapViewOfFile( ptr );
+
+ /* this fails on win9x but succeeds on NT */
+ ptr = MapViewOfFile( mapping, FILE_MAP_COPY, 0, 0, 4096 );
+ if (ptr) UnmapViewOfFile( ptr );
+ else ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error %lx\n", GetLastError() );
+
+ ptr = MapViewOfFile( mapping, 0, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile 0 failed\n" );
+ UnmapViewOfFile( ptr );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile FILE_MAP_WRITE failed\n" );
+ UnmapViewOfFile( ptr );
+ CloseHandle( mapping );
+
+ /* read-only mapping */
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_READONLY, 0, 4096, NULL );
+ ok( mapping != 0, "CreateFileMapping failed\n" );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ failed\n" );
+ UnmapViewOfFile( ptr );
+
+ /* this fails on win9x but succeeds on NT */
+ ptr = MapViewOfFile( mapping, FILE_MAP_COPY, 0, 0, 4096 );
+ if (ptr) UnmapViewOfFile( ptr );
+ else ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error %lx\n", GetLastError() );
+
+ ptr = MapViewOfFile( mapping, 0, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile 0 failed\n" );
+ UnmapViewOfFile( ptr );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 4096 );
+ ok( !ptr, "MapViewOfFile FILE_MAP_WRITE succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %lx\n", GetLastError() );
+ CloseHandle( mapping );
+
+ /* copy-on-write mapping */
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_WRITECOPY, 0, 4096, NULL );
+ ok( mapping != 0, "CreateFileMapping failed\n" );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ failed\n" );
+ UnmapViewOfFile( ptr );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_COPY, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile FILE_MAP_COPY failed\n" );
+ UnmapViewOfFile( ptr );
+
+ ptr = MapViewOfFile( mapping, 0, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile 0 failed\n" );
+ UnmapViewOfFile( ptr );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 4096 );
+ ok( !ptr, "MapViewOfFile FILE_MAP_WRITE succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %lx\n", GetLastError() );
+ CloseHandle( mapping );
+
+ /* no access mapping */
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_NOACCESS, 0, 4096, NULL );
+ /* fails on NT but succeeds on win9x */
+ if (!mapping) ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error %lx\n", GetLastError() );
+ else
+ {
+ ptr = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile FILE_MAP_READ failed\n" );
+ UnmapViewOfFile( ptr );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_COPY, 0, 0, 4096 );
+ ok( !ptr, "MapViewOfFile FILE_MAP_COPY succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error %lx\n", GetLastError() );
+
+ ptr = MapViewOfFile( mapping, 0, 0, 0, 4096 );
+ ok( ptr != NULL, "MapViewOfFile 0 failed\n" );
+ UnmapViewOfFile( ptr );
+
+ ptr = MapViewOfFile( mapping, FILE_MAP_WRITE, 0, 0, 4096 );
+ ok( !ptr, "MapViewOfFile FILE_MAP_WRITE succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error %lx\n", GetLastError() );
+
+ CloseHandle( mapping );
+ }
+
+ CloseHandle( file );
+
+ /* now try read-only file */
+
+ file = CreateFileA( testfile, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok( file != INVALID_HANDLE_VALUE, "Failed to create test file\n" );
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_READWRITE, 0, 4096, NULL );
+ ok( !mapping, "CreateFileMapping PAGE_READWRITE succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %lx\n", GetLastError() );
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_WRITECOPY, 0, 4096, NULL );
+ ok( mapping != 0, "CreateFileMapping PAGE_WRITECOPY failed\n" );
+ CloseHandle( mapping );
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_READONLY, 0, 4096, NULL );
+ ok( mapping != 0, "CreateFileMapping PAGE_READONLY failed\n" );
+ CloseHandle( mapping );
+ CloseHandle( file );
+
+ /* now try no access file */
+
+ file = CreateFileA( testfile, 0, 0, NULL, OPEN_EXISTING, 0, 0 );
+ ok( file != INVALID_HANDLE_VALUE, "Failed to create test file\n" );
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_READWRITE, 0, 4096, NULL );
+ ok( !mapping, "CreateFileMapping PAGE_READWRITE succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %lx\n", GetLastError() );
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_WRITECOPY, 0, 4096, NULL );
+ ok( !mapping, "CreateFileMapping PAGE_WRITECOPY succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %lx\n", GetLastError() );
+
+ mapping = CreateFileMappingA( file, NULL, PAGE_READONLY, 0, 4096, NULL );
+ ok( !mapping, "CreateFileMapping PAGE_READONLY succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER ||
+ GetLastError() == ERROR_ACCESS_DENIED, "Wrong error %lx\n", GetLastError() );
+
+ CloseHandle( file );
+
+ CloseHandle( file );
+ DeleteFileA( testfile );
+}
+
+START_TEST(virtual)
+{
+ test_VirtualAlloc();
+ test_MapViewOfFile();
+}
--- /dev/null
+<module name="lz32_winetest" type="win32cui" installbase="bin" installname="lz32_winetest.exe" allowwarnings="true">
+ <include base="lz32_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <library>lz32</library>
+ <file>testlist.c</file>
+ <file>lzexpand_main.c</file>
+</module>
--- /dev/null
+/*
+ * Unit test suite for lz32 functions
+ *
+ * Copyright 2004 Evan Parry, Daniel Kegel
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <windef.h>
+#include <winbase.h>
+#include <stdlib.h>
+#include <winerror.h>
+#include <lzexpand.h>
+
+#include "wine/test.h"
+
+static char filename_[] = "testfile.xx_";
+#if 0
+static char filename[] = "testfile.xxx";
+#endif
+static char filename2[] = "testfile.yyy";
+
+/* This is the hex string representation of the file created by compressing
+ a simple text file with the contents "This is a test file."
+
+ The file was created using COMPRESS.EXE from the Windows Server 2003
+ Resource Kit from Microsoft. The resource kit was retrieved from the
+ following URL:
+
+ http://www.microsoft.com/downloads/details.aspx?FamilyID=9d467a69-57ff-4ae7-96ee-b18c4790cffd&displaylang=en
+ */
+static const unsigned char compressed_file[] =
+ {0x53,0x5A,0x44,0x44,0x88,0xF0,0x27,0x33,0x41,
+ 0x74,0x75,0x14,0x00,0x00,0xDF,0x54,0x68,0x69,
+ 0x73,0x20,0xF2,0xF0,0x61,0x20,0xFF,0x74,0x65,
+ 0x73,0x74,0x20,0x66,0x69,0x6C,0x03,0x65,0x2E};
+static const DWORD compressed_file_size = sizeof(compressed_file);
+
+static const char uncompressed_data[] = "This is a test file.";
+static const DWORD uncompressed_data_size = sizeof(uncompressed_data) - 1;
+
+static char *buf;
+
+static void test_lzopenfile(void)
+{
+ OFSTRUCT test;
+ DWORD retval;
+ INT file;
+
+ /* Check for nonexistent file. */
+ file = LZOpenFile("badfilename_", &test, OF_READ);
+ ok(file == LZERROR_BADINHANDLE,
+ "LZOpenFile succeeded on nonexistent file\n");
+ LZClose(file);
+
+ /* Create an empty file. */
+ file = LZOpenFile(filename_, &test, OF_CREATE);
+ ok(file >= 0, "LZOpenFile failed on creation\n");
+ LZClose(file);
+ retval = GetFileAttributes(filename_);
+ ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributes: error %ld\n",
+ GetLastError());
+
+ /* Check various opening options. */
+ file = LZOpenFile(filename_, &test, OF_READ);
+ ok(file >= 0, "LZOpenFile failed on read\n");
+ LZClose(file);
+ file = LZOpenFile(filename_, &test, OF_WRITE);
+ ok(file >= 0, "LZOpenFile failed on write\n");
+ LZClose(file);
+ file = LZOpenFile(filename_, &test, OF_READWRITE);
+ ok(file >= 0, "LZOpenFile failed on read/write\n");
+ LZClose(file);
+ file = LZOpenFile(filename_, &test, OF_EXIST);
+ ok(file >= 0, "LZOpenFile failed on read/write\n");
+ LZClose(file);
+
+
+ /* If the file "foo.xxx" does not exist, LZOpenFile should then
+ check for the file "foo.xx_" and open that -- at least on some
+ operating systems. Doesn't seem to on my copy of Win98.
+ The Wine testing guidelines say we should accept the behavior of
+ any valid version of Windows. Thus it seems we cannot check this?!
+ Revisit this at some point to see if this can be tested somehow.
+ */
+#if 0
+ file = LZOpenFile(filename, &test, OF_EXIST);
+ ok(file != LZERROR_BADINHANDLE,
+ "LZOpenFile \"filename_\" check failed\n");
+ LZClose(file);
+#endif
+
+ /* Delete the file then make sure it doesn't exist anymore. */
+ file = LZOpenFile(filename_, &test, OF_DELETE);
+ ok(file >= 0, "LZOpenFile failed on delete\n");
+ LZClose(file);
+
+ retval = GetFileAttributes(filename_);
+ ok(retval == INVALID_FILE_ATTRIBUTES,
+ "GetFileAttributes succeeded on deleted file\n");
+
+}
+
+static void test_lzread(void)
+{
+ HANDLE file;
+ DWORD ret;
+ int cfile;
+ OFSTRUCT test;
+ BOOL retok;
+
+ /* Create the compressed file. */
+ file = CreateFile(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
+ ok(file != INVALID_HANDLE_VALUE, "Could not create test file\n");
+ retok = WriteFile(file, compressed_file, compressed_file_size, &ret, 0);
+ ok( retok, "WriteFile: error %ld\n", GetLastError());
+ ok(ret == compressed_file_size, "Wrote wrong number of bytes with WriteFile?\n");
+ CloseHandle(file);
+
+ cfile = LZOpenFile(filename_, &test, OF_READ);
+ ok(cfile > 0, "LZOpenFile failed\n");
+
+ ret = LZRead(cfile, buf, uncompressed_data_size);
+ ok(ret == uncompressed_data_size, "Read wrong number of bytes\n");
+
+ /* Compare what we read with what we think we should read. */
+ ok(memcmp(buf, uncompressed_data, uncompressed_data_size) == 0,
+ "buffer contents mismatch\n");
+
+ todo_wine {
+ /* Wine returns the number of bytes actually read instead of an error */
+ ret = LZRead(cfile, buf, uncompressed_data_size);
+ ok(ret == LZERROR_READ, "Expected read-past-EOF to return LZERROR_READ\n");
+ }
+
+ LZClose(cfile);
+
+ ret = DeleteFile(filename_);
+ ok(ret, "DeleteFile: error %ld\n", GetLastError());
+}
+
+static void test_lzcopy(void)
+{
+ HANDLE file;
+ DWORD ret;
+ int source, dest;
+ OFSTRUCT stest, dtest;
+ BOOL retok;
+
+ /* Create the compressed file. */
+ file = CreateFile(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
+ ok(file != INVALID_HANDLE_VALUE,
+ "CreateFile: error %ld\n", GetLastError());
+ retok = WriteFile(file, compressed_file, compressed_file_size, &ret, 0);
+ ok( retok, "WriteFile error %ld\n", GetLastError());
+ ok(ret == compressed_file_size, "Wrote wrong number of bytes\n");
+ CloseHandle(file);
+
+ source = LZOpenFile(filename_, &stest, OF_READ);
+ ok(source >= 0, "LZOpenFile failed on compressed file\n");
+ dest = LZOpenFile(filename2, &dtest, OF_CREATE);
+ ok(dest >= 0, "LZOpenFile failed on creating new file %d\n", dest);
+
+ ret = LZCopy(source, dest);
+ ok(ret > 0, "LZCopy error\n");
+
+ LZClose(source);
+ LZClose(dest);
+
+ file = CreateFile(filename2, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ 0, 0);
+ ok(file != INVALID_HANDLE_VALUE,
+ "CreateFile: error %ld\n", GetLastError());
+
+ retok = ReadFile(file, buf, uncompressed_data_size*2, &ret, 0);
+ ok( retok && ret == uncompressed_data_size, "ReadFile: error %ld\n", GetLastError());
+ /* Compare what we read with what we think we should read. */
+ ok(!memcmp(buf, uncompressed_data, uncompressed_data_size),
+ "buffer contents mismatch\n");
+ CloseHandle(file);
+
+ ret = DeleteFile(filename_);
+ ok(ret, "DeleteFile: error %ld\n", GetLastError());
+ ret = DeleteFile(filename2);
+ ok(ret, "DeleteFile: error %ld\n", GetLastError());
+}
+
+START_TEST(lzexpand_main)
+{
+ buf = malloc(uncompressed_data_size * 2);
+ test_lzopenfile();
+ test_lzread();
+ test_lzcopy();
+ free(buf);
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_lzexpand_main(void);
+
+const struct test winetest_testlist[] =
+{
+ { "lzexpand_main", func_lzexpand_main },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ *
+ * A test program for MSI database files.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#include <stdio.h>
+
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static const char *msifile = "winetest.msi";
+
+static void test_msidatabase(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+
+ DeleteFile(msifile);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ res = MsiCloseHandle( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to close database\n" );
+
+ res = DeleteFile( msifile );
+ ok( res == TRUE, "Failed to delete database\n" );
+}
+
+static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec)
+{
+ MSIHANDLE hview = 0;
+ UINT r, ret;
+
+ /* open a select query */
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if (r != ERROR_SUCCESS)
+ return r;
+ r = MsiViewExecute(hview, 0);
+ if (r != ERROR_SUCCESS)
+ return r;
+ ret = MsiViewFetch(hview, phrec);
+ r = MsiViewClose(hview);
+ if (r != ERROR_SUCCESS)
+ return r;
+ r = MsiCloseHandle(hview);
+ if (r != ERROR_SUCCESS)
+ return r;
+ return ret;
+}
+
+static void test_msiinsert(void)
+{
+ MSIHANDLE hdb = 0, hview = 0, hrec = 0;
+ UINT r;
+ const char *query;
+ char buf[80];
+ DWORD sz;
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ /* create a table */
+ query = "CREATE TABLE `phone` ( "
+ "`id` INT, `name` CHAR(32), `number` CHAR(32) "
+ "PRIMARY KEY `id`)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* insert a value into it */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES('1', 'Abe', '8675309')";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` = 1";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
+
+ /* check the record contains what we put in it */
+ r = MsiRecordGetFieldCount(hrec);
+ ok(r == 3, "record count wrong\n");
+
+ todo_wine {
+ r = MsiRecordIsNull(hrec, 0);
+ ok(r == FALSE, "field 0 not null\n");
+ }
+
+ r = MsiRecordGetInteger(hrec, 1);
+ ok(r == 1, "field 1 contents wrong\n");
+ sz = sizeof buf;
+ r = MsiRecordGetString(hrec, 2, buf, &sz);
+ ok(r == ERROR_SUCCESS, "field 2 content fetch failed\n");
+ ok(!strcmp(buf,"Abe"), "field 2 content incorrect\n");
+ sz = sizeof buf;
+ r = MsiRecordGetString(hrec, 3, buf, &sz);
+ ok(r == ERROR_SUCCESS, "field 3 content fetch failed\n");
+ ok(!strcmp(buf,"8675309"), "field 3 content incorrect\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* open a select query */
+ hrec = 100;
+ query = "SELECT * FROM `phone` WHERE `id` >= 10";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+ ok(hrec == 0, "hrec should be null\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` < 0";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` <= 0";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` <> 1";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ query = "SELECT * FROM `phone` WHERE `id` > 10";
+ r = do_query(hdb, query, &hrec);
+ ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
+
+ todo_wine {
+ /* now try a few bad INSERT xqueries */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES(?, ?)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n");
+
+ if (r == ERROR_SUCCESS)
+ r = MsiCloseHandle(hview);
+ }
+
+ /* construct a record to insert */
+ hrec = MsiCreateRecord(4);
+ r = MsiRecordSetInteger(hrec, 1, 2);
+ ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n");
+ r = MsiRecordSetString(hrec, 2, "Adam");
+ ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
+ r = MsiRecordSetString(hrec, 3, "96905305");
+ ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
+
+ /* insert another value, using a record and wildcards */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES(?, ?, ?)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ if (r == ERROR_SUCCESS)
+ {
+ r = MsiViewExecute(hview, hrec);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+ }
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = MsiViewFetch(0, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiViewFetch failed\n");
+
+ r = MsiDatabaseCommit(hdb);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
+
+ r = MsiCloseHandle(hdb);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = DeleteFile(msifile);
+ ok(r == TRUE, "file didn't exist after commit\n");
+}
+
+typedef UINT (WINAPI *fnMsiDecomposeDescriptorA)(LPCSTR, LPCSTR, LPSTR, LPSTR, DWORD *);
+static fnMsiDecomposeDescriptorA pMsiDecomposeDescriptorA;
+
+static void test_msidecomposedesc(void)
+{
+ char prod[MAX_FEATURE_CHARS+1], comp[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1];
+ const char *desc;
+ UINT r;
+ DWORD len;
+ HMODULE hmod;
+
+ hmod = GetModuleHandle("msi.dll");
+ if (!hmod)
+ return;
+ pMsiDecomposeDescriptorA = (fnMsiDecomposeDescriptorA)
+ GetProcAddress(hmod, "MsiDecomposeDescriptorA");
+ if (!pMsiDecomposeDescriptorA)
+ return;
+
+ /* test a valid feature descriptor */
+ desc = "']gAVn-}f(ZXfeAR6.jiFollowTheWhiteRabbit>3w2x^IGfe?CxI5heAvk.";
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
+ ok(r == ERROR_SUCCESS, "returned an error\n");
+ ok(len == strlen(desc), "length was wrong\n");
+ ok(strcmp(prod,"{90110409-6000-11D3-8CFE-0150048383C9}")==0, "product wrong\n");
+ ok(strcmp(feature,"FollowTheWhiteRabbit")==0, "feature wrong\n");
+ ok(strcmp(comp,"{A7CD68DB-EF74-49C8-FBB2-A7C463B2AC24}")==0,"component wrong\n");
+
+ /* test an invalid feature descriptor with too many characters */
+ desc = "']gAVn-}f(ZXfeAR6.ji"
+ "ThisWillFailIfTheresMoreThanAGuidsChars>"
+ "3w2x^IGfe?CxI5heAvk.";
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
+ ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
+
+ /*
+ * Test a valid feature descriptor with the
+ * maximum number of characters and some trailing characters.
+ */
+ desc = "']gAVn-}f(ZXfeAR6.ji"
+ "ThisWillWorkIfTheresLTEThanAGuidsChars>"
+ "3w2x^IGfe?CxI5heAvk."
+ "extra";
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, feature, NULL, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, prod, NULL, NULL, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(desc, NULL, NULL, NULL, &len);
+ ok(r == ERROR_SUCCESS, "returned wrong error\n");
+ ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
+
+ len = 0;
+ r = pMsiDecomposeDescriptorA(NULL, NULL, NULL, NULL, &len);
+ ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
+ ok(len == 0, "length wrong\n");
+}
+
+static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
+{
+ MSIHANDLE htab = 0;
+ UINT res;
+
+ res = MsiDatabaseOpenView( hdb, szQuery, &htab );
+ if(res == ERROR_SUCCESS )
+ {
+ UINT r;
+
+ r = MsiViewExecute( htab, hrec );
+ if(r != ERROR_SUCCESS )
+ res = r;
+
+ r = MsiViewClose( htab );
+ if(r != ERROR_SUCCESS )
+ res = r;
+
+ r = MsiCloseHandle( htab );
+ if(r != ERROR_SUCCESS )
+ res = r;
+ }
+ return res;
+}
+
+static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
+{
+ return try_query_param( hdb, szQuery, 0 );
+}
+
+static UINT try_insert_query( MSIHANDLE hdb, LPCSTR szQuery )
+{
+ MSIHANDLE hrec = 0;
+ UINT r;
+
+ hrec = MsiCreateRecord( 1 );
+ MsiRecordSetString( hrec, 1, "Hello");
+
+ r = try_query_param( hdb, szQuery, hrec );
+
+ MsiCloseHandle( hrec );
+ return r;
+}
+
+static void test_msibadqueries(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT r;
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ r = MsiDatabaseCommit( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to commit database\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to close database\n");
+
+ /* open it readonly */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb );
+ ok(r == ERROR_SUCCESS , "Failed to open database r/o\n");
+
+ /* add a table to it */
+ r = try_query( hdb, "select * from _Tables");
+ ok(r == ERROR_SUCCESS , "query 1 failed\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to close database r/o\n");
+
+ /* open it read/write */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb );
+ ok(r == ERROR_SUCCESS , "Failed to open database r/w\n");
+
+ /* a bunch of test queries that fail with the native MSI */
+
+ r = try_query( hdb, "CREATE TABLE");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2a return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a`");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2b return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` ()");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2c return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2d return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) )");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2e return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2f return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2g return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2h return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2i return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY 'b')");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2j return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2k return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2l return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHA(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2m return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(-1) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2n return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(720) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2o return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`` CHAR(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_SUCCESS , "valid query 2z failed\n");
+
+ r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_BAD_QUERY_SYNTAX , "created same table again\n");
+
+ r = try_query( hdb, "CREATE TABLE `aa` (`b` CHAR(72) NOT NULL, `c` "
+ "CHAR(72), `d` CHAR(255) NOT NULL LOCALIZABLE PRIMARY KEY `b`)");
+ ok(r == ERROR_SUCCESS , "query 4 failed\n");
+
+ r = MsiDatabaseCommit( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
+
+ r = try_query( hdb, "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL "
+ "PRIMARY KEY `foo`)");
+ ok(r == ERROR_SUCCESS , "query 4 failed\n");
+
+ r = try_insert_query( hdb, "insert into a ( `b` ) VALUES ( ? )");
+ ok(r == ERROR_SUCCESS , "failed to insert record in db\n");
+
+ r = MsiDatabaseCommit( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
+
+ r = try_query( hdb, "CREATE TABLE `boo` (`foo` CHAR(72) NOT NULL "
+ "PRIMARY KEY `ba`)");
+ ok(r != ERROR_SUCCESS , "query 5 succeeded\n");
+
+ r = try_query( hdb,"CREATE TABLE `bee` (`foo` CHAR(72) NOT NULL )");
+ ok(r != ERROR_SUCCESS , "query 6 succeeded\n");
+
+ r = try_query( hdb, "CREATE TABLE `temp` (`t` CHAR(72) NOT NULL "
+ "PRIMARY KEY `t`)");
+ ok(r == ERROR_SUCCESS , "query 7 failed\n");
+
+ r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)");
+ ok(r == ERROR_SUCCESS , "query 8 failed\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS , "Failed to close database transact\n");
+
+ r = DeleteFile( msifile );
+ ok(r == TRUE, "file didn't exist after commit\n");
+}
+
+static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query )
+{
+ MSIHANDLE hview = 0;
+ UINT r;
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, hrec);
+ if( r == ERROR_SUCCESS )
+ r = MsiViewClose(hview);
+ MsiCloseHandle(hview);
+ return r;
+}
+
+static void test_viewmodify(void)
+{
+ MSIHANDLE hdb = 0, hview = 0, hrec = 0;
+ UINT r;
+ const char *query;
+ char buffer[0x100];
+ DWORD sz;
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ query = "CREATE TABLE `phone` ( "
+ "`id` INT, `name` CHAR(32), `number` CHAR(32) "
+ "PRIMARY KEY `id`)";
+ r = run_query( hdb, 0, query );
+ ok(r == ERROR_SUCCESS, "query failed\n");
+
+ /* check what the error function reports without doing anything */
+ sz = 0;
+ /* passing NULL as the 3rd param make function to crash on older platforms */
+ r = MsiViewGetError( 0, NULL, &sz );
+ ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
+
+ /* open a view */
+ query = "SELECT * FROM `phone`";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ /* see what happens with a good hview and bad args */
+ r = MsiViewGetError( hview, NULL, NULL );
+ ok(r == MSIDBERROR_INVALIDARG || r == MSIDBERROR_NOERROR,
+ "MsiViewGetError returns %u (expected -3)\n", r);
+ r = MsiViewGetError( hview, buffer, NULL );
+ ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
+
+ /* see what happens with a zero length buffer */
+ sz = 0;
+ buffer[0] = 'x';
+ r = MsiViewGetError( hview, buffer, &sz );
+ ok(r == MSIDBERROR_MOREDATA, "MsiViewGetError return\n");
+ ok(buffer[0] == 'x', "buffer cleared\n");
+ ok(sz == 0, "size not zero\n");
+
+ /* ok this one is strange */
+ sz = 0;
+ r = MsiViewGetError( hview, NULL, &sz );
+ ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
+ ok(sz == 0, "size not zero\n");
+
+ /* see if it really has an error */
+ sz = sizeof buffer;
+ buffer[0] = 'x';
+ r = MsiViewGetError( hview, buffer, &sz );
+ ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
+ ok(buffer[0] == 0, "buffer not cleared\n");
+ ok(sz == 0, "size not zero\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+
+ /* try some invalid records */
+ r = MsiViewModify(hview, MSIMODIFY_INSERT, 0 );
+ ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
+ r = MsiViewModify(hview, -1, 0 );
+ ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
+
+ /* try an small record */
+ hrec = MsiCreateRecord(1);
+ r = MsiViewModify(hview, -1, hrec );
+ ok(r == ERROR_INVALID_DATA, "MsiViewModify failed\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "failed to close record\n");
+
+ /* insert a valid record */
+ hrec = MsiCreateRecord(3);
+
+ r = MsiRecordSetInteger(hrec, 2, 1);
+ ok(r == ERROR_SUCCESS, "failed to set integer\n");
+ r = MsiRecordSetString(hrec, 2, "bob");
+ ok(r == ERROR_SUCCESS, "failed to set integer\n");
+ r = MsiRecordSetString(hrec, 3, "7654321");
+ ok(r == ERROR_SUCCESS, "failed to set integer\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
+ ok(r == ERROR_SUCCESS, "MsiViewModify failed\n");
+
+ /* insert the same thing again */
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+
+ /* should fail ... */
+ todo_wine {
+ r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
+ ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n");
+ }
+
+ r = MsiCloseHandle(hrec);
+ ok(r == ERROR_SUCCESS, "failed to close record\n");
+
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = MsiCloseHandle( hdb );
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
+}
+
+static MSIHANDLE create_db(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+
+ DeleteFile(msifile);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+ if( res != ERROR_SUCCESS )
+ return hdb;
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ return hdb;
+}
+
+static void test_getcolinfo(void)
+{
+ MSIHANDLE hdb, hview = 0, rec = 0;
+ UINT r;
+ DWORD sz;
+ char buffer[0x20];
+
+ /* create an empty db */
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ /* tables should be present */
+ r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview);
+ ok( r == ERROR_SUCCESS, "failed to open query\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok( r == ERROR_SUCCESS, "failed to execute query\n");
+
+ /* check that NAMES works */
+ rec = 0;
+ r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec );
+ ok( r == ERROR_SUCCESS, "failed to get names\n");
+ sz = sizeof buffer;
+ r = MsiRecordGetString(rec, 1, buffer, &sz );
+ ok( r == ERROR_SUCCESS, "failed to get string\n");
+ ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n");
+ r = MsiCloseHandle( rec );
+ ok( r == ERROR_SUCCESS, "failed to close record handle\n");
+
+ /* check that TYPES works */
+ rec = 0;
+ r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec );
+ ok( r == ERROR_SUCCESS, "failed to get names\n");
+ sz = sizeof buffer;
+ r = MsiRecordGetString(rec, 1, buffer, &sz );
+ ok( r == ERROR_SUCCESS, "failed to get string\n");
+ ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n");
+ r = MsiCloseHandle( rec );
+ ok( r == ERROR_SUCCESS, "failed to close record handle\n");
+
+ /* check that invalid values fail */
+ rec = 0;
+ r = MsiViewGetColumnInfo( hview, 100, &rec );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
+ ok( rec == 0, "returned a record\n");
+
+ r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
+
+ r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec );
+ ok( r == ERROR_INVALID_HANDLE, "wrong error code\n");
+
+ r = MsiViewClose(hview);
+ ok( r == ERROR_SUCCESS, "failed to close view\n");
+ r = MsiCloseHandle(hview);
+ ok( r == ERROR_SUCCESS, "failed to close view handle\n");
+ r = MsiCloseHandle(hdb);
+ ok( r == ERROR_SUCCESS, "failed to close database\n");
+}
+
+static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type)
+{
+ MSIHANDLE hview = 0, rec = 0;
+ UINT r;
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, 0);
+ if( r == ERROR_SUCCESS )
+ {
+ MsiViewGetColumnInfo( hview, type, &rec );
+ MsiViewClose(hview);
+ }
+ MsiCloseHandle(hview);
+ return rec;
+}
+
+static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field)
+{
+ MSIHANDLE hview = 0, rec = 0;
+ UINT r, type = 0;
+ char query[0x100];
+
+ sprintf(query, "select * from `_Columns` where `Table` = '%s'", table );
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, 0);
+ if( r == ERROR_SUCCESS )
+ {
+ while (1)
+ {
+ r = MsiViewFetch( hview, &rec );
+ if( r != ERROR_SUCCESS)
+ break;
+ r = MsiRecordGetInteger( rec, 2 );
+ if (r == field)
+ type = MsiRecordGetInteger( rec, 4 );
+ MsiCloseHandle( rec );
+ }
+
+ MsiViewClose(hview);
+ }
+ MsiCloseHandle(hview);
+ return type;
+}
+
+static BOOL check_record( MSIHANDLE rec, UINT field, LPCSTR val )
+{
+ CHAR buffer[0x20];
+ UINT r;
+ DWORD sz;
+
+ sz = sizeof buffer;
+ r = MsiRecordGetString( rec, field, buffer, &sz );
+ return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
+}
+
+static void test_viewgetcolumninfo(void)
+{
+ MSIHANDLE hdb = 0, rec;
+ UINT r;
+
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ r = run_query( hdb, 0,
+ "CREATE TABLE `Properties` "
+ "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ /* check the column types */
+ rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "S255"), "wrong record type\n");
+ ok( check_record( rec, 2, "S1"), "wrong record type\n");
+
+ MsiCloseHandle( rec );
+
+ /* check the type in _Columns */
+ ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n");
+ ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n");
+
+ /* now try the names */
+ rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "Property"), "wrong record type\n");
+ ok( check_record( rec, 2, "Value"), "wrong record type\n");
+
+ MsiCloseHandle( rec );
+
+ r = run_query( hdb, 0,
+ "CREATE TABLE `Binary` "
+ "( `Name` CHAR(255), `Data` OBJECT PRIMARY KEY `Name`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ /* check the column types */
+ rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "S255"), "wrong record type\n");
+ ok( check_record( rec, 2, "V0"), "wrong record type\n");
+
+ MsiCloseHandle( rec );
+
+ /* check the type in _Columns */
+ ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n");
+ ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n");
+
+ /* now try the names */
+ rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+
+ ok( check_record( rec, 1, "Name"), "wrong record type\n");
+ ok( check_record( rec, 2, "Data"), "wrong record type\n");
+ MsiCloseHandle( rec );
+
+ r = run_query( hdb, 0,
+ "CREATE TABLE `UIText` "
+ "( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n");
+ ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n");
+
+ rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+ ok( check_record( rec, 1, "Key"), "wrong record type\n");
+ ok( check_record( rec, 2, "Text"), "wrong record type\n");
+ MsiCloseHandle( rec );
+
+ rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+ ok( check_record( rec, 1, "s72"), "wrong record type\n");
+ ok( check_record( rec, 2, "L255"), "wrong record type\n");
+ MsiCloseHandle( rec );
+
+ MsiCloseHandle( hdb );
+}
+
+static void test_msiexport(void)
+{
+ MSIHANDLE hdb = 0, hview = 0;
+ UINT r;
+ const char *query;
+ char path[MAX_PATH];
+ const char file[] = "phone.txt";
+ HANDLE handle;
+ char buffer[0x100];
+ DWORD length;
+ const char expected[] =
+ "id\tname\tnumber\r\n"
+ "I2\tS32\tS32\r\n"
+ "phone\tid\r\n"
+ "1\tAbe\t8675309\r\n";
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ /* create a table */
+ query = "CREATE TABLE `phone` ( "
+ "`id` INT, `name` CHAR(32), `number` CHAR(32) "
+ "PRIMARY KEY `id`)";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* insert a value into it */
+ query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
+ "VALUES('1', 'Abe', '8675309')";
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+ r = MsiViewClose(hview);
+ ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+ r = MsiCloseHandle(hview);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ GetCurrentDirectory(MAX_PATH, path);
+
+ todo_wine {
+ r = MsiDatabaseExport(hdb, "phone", path, file);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n");
+
+ MsiCloseHandle(hdb);
+
+ lstrcat(path, "\\");
+ lstrcat(path, file);
+
+ /* check the data that was written */
+ length = 0;
+ memset(buffer, 0, sizeof buffer);
+ handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ ReadFile(handle, buffer, sizeof buffer, &length, NULL);
+ CloseHandle(handle);
+ DeleteFile(path);
+ }
+ else
+ ok(0, "failed to open file %s\n", path);
+
+ ok( length == strlen(expected), "length of data wrong\n");
+ ok( !lstrcmp(buffer, expected), "data doesn't match\n");
+ }
+ DeleteFile(msifile);
+}
+
+static void test_longstrings(void)
+{
+ const char insert_query[] =
+ "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')";
+ char *str;
+ MSIHANDLE hdb = 0, hview = 0, hrec = 0;
+ DWORD len;
+ UINT r;
+ const DWORD STRING_LENGTH = 0x10005;
+
+ DeleteFile(msifile);
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ /* create a table */
+ r = try_query( hdb,
+ "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)");
+ ok(r == ERROR_SUCCESS, "query failed\n");
+
+ /* try a insert a very long string */
+ str = HeapAlloc(GetProcessHeap(), 0, STRING_LENGTH+sizeof insert_query);
+ len = strchr(insert_query, 'Z') - insert_query;
+ strcpy(str, insert_query);
+ memset(str+len, 'Z', STRING_LENGTH);
+ strcpy(str+len+STRING_LENGTH, insert_query+len+1);
+ r = try_query( hdb, str );
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ HeapFree(GetProcessHeap(), 0, str);
+
+ MsiDatabaseCommit(hdb);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
+ MsiCloseHandle(hdb);
+
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ r = MsiDatabaseOpenView(hdb, "select * from `strings` where `id` = 1", &hview);
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+
+ r = MsiViewExecute(hview, 0);
+ ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
+
+ r = MsiViewFetch(hview, &hrec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
+
+ MsiCloseHandle(hview);
+
+ r = MsiRecordGetString(hrec, 2, NULL, &len);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
+ todo_wine {
+ ok(len == STRING_LENGTH, "string length wrong\n");
+ }
+
+ MsiCloseHandle(hrec);
+ MsiCloseHandle(hdb);
+ DeleteFile(msifile);
+}
+
+static void test_streamtable(void)
+{
+ MSIHANDLE hdb = 0, rec;
+ UINT r;
+
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ r = run_query( hdb, 0,
+ "CREATE TABLE `Properties` "
+ "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" );
+ ok( r == ERROR_SUCCESS , "Failed to create table\n" );
+
+ /* check the column types */
+ rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES );
+ ok( rec, "failed to get column info record\n" );
+
+ todo_wine {
+ ok( check_record( rec, 1, "s62"), "wrong record type\n");
+ ok( check_record( rec, 2, "V0"), "wrong record type\n");
+ }
+
+ MsiCloseHandle( rec );
+
+ /* now try the names */
+ rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES );
+ ok( rec, "failed to get column info record\n" );
+
+ todo_wine {
+ ok( check_record( rec, 1, "Name"), "wrong record type\n");
+ ok( check_record( rec, 2, "Data"), "wrong record type\n");
+ }
+
+ MsiCloseHandle( rec );
+ MsiCloseHandle( hdb );
+ DeleteFile(msifile);
+}
+
+static void test_where(void)
+{
+ MSIHANDLE hdb = 0, rec;
+ LPCSTR query;
+ UINT r;
+
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ r = run_query( hdb, 0,
+ "CREATE TABLE `Media` ("
+ "`DiskId` SHORT NOT NULL, "
+ "`LastSequence` LONG, "
+ "`DiskPrompt` CHAR(64) LOCALIZABLE, "
+ "`Cabinet` CHAR(255), "
+ "`VolumeLabel` CHAR(32), "
+ "`Source` CHAR(72) "
+ "PRIMARY KEY `DiskId`)" );
+ ok( r == S_OK, "cannot create Media table: %d\n", r );
+
+ r = run_query( hdb, 0, "INSERT INTO `Media` "
+ "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
+ "VALUES ( 1, 0, '', 'zero.cab', '', '' )" );
+ ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
+
+ r = run_query( hdb, 0, "INSERT INTO `Media` "
+ "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
+ "VALUES ( 2, 1, '', 'one.cab', '', '' )" );
+ ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
+
+ r = run_query( hdb, 0, "INSERT INTO `Media` "
+ "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
+ "VALUES ( 3, 2, '', 'two.cab', '', '' )" );
+ ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
+
+ query = "SELECT * FROM `Media`";
+ r = do_query(hdb, query, &rec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
+ ok( check_record( rec, 4, "zero.cab"), "wrong cabinet\n");
+ MsiCloseHandle( rec );
+
+ query = "SELECT * FROM `Media` WHERE `LastSequence` >= 1";
+ r = do_query(hdb, query, &rec);
+ ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
+ ok( check_record( rec, 4, "one.cab"), "wrong cabinet\n");
+
+ r = MsiRecordGetInteger(rec, 1);
+ ok( 2 == r, "field wrong\n");
+ r = MsiRecordGetInteger(rec, 2);
+ ok( 1 == r, "field wrong\n");
+
+ MsiCloseHandle( rec );
+
+ MsiCloseHandle( hdb );
+ DeleteFile(msifile);
+}
+
+static CHAR CURR_DIR[MAX_PATH];
+
+static const CHAR test_data[] = "FirstPrimaryColumn\tSecondPrimaryColumn\tShortInt\tShortIntNullable\tLongInt\tLongIntNullable\tString\tLocalizableString\tLocalizableStringNullable\n"
+ "s255\ti2\ti2\tI2\ti4\tI4\tS255\tS0\ts0\n"
+ "TestTable\tFirstPrimaryColumn\n"
+ "stringage\t5\t2\t\t2147483640\t-2147483640\tanother string\tlocalizable\tduh\n";
+
+static void write_file(const CHAR *filename, const char *data, int data_size)
+{
+ DWORD size;
+
+ HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ WriteFile(hf, data, data_size, &size, NULL);
+ CloseHandle(hf);
+}
+
+static UINT add_table_to_db(MSIHANDLE hdb, LPCSTR table_data)
+{
+ UINT r;
+
+ write_file("temp_file", table_data, (lstrlen(table_data) - 1) * sizeof(char));
+ r = MsiDatabaseImportA(hdb, CURR_DIR, "temp_file");
+ DeleteFileA("temp_file");
+
+ return r;
+}
+
+static void test_msiimport(void)
+{
+ MSIHANDLE hdb, view, rec;
+ LPCSTR query;
+ UINT r, count;
+ signed int i;
+
+ GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+
+ r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+ r = add_table_to_db(hdb, test_data);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ }
+
+ query = "SELECT * FROM `TestTable`";
+ r = MsiDatabaseOpenView(hdb, query, &view);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ }
+
+ r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
+ count = MsiRecordGetFieldCount(rec);
+ todo_wine
+ {
+ ok(count == 9, "Expected 9, got %d\n", count);
+ ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n");
+ ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n");
+ ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n");
+ ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n");
+ ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n");
+ ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n");
+ ok(check_record(rec, 7, "String"), "Expected String\n");
+ ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n");
+ ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n");
+ }
+
+ r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
+ count = MsiRecordGetFieldCount(rec);
+ todo_wine
+ {
+ ok(count == 9, "Expected 9, got %d\n", count);
+ ok(check_record(rec, 1, "s255"), "Expected s255\n");
+ ok(check_record(rec, 2, "i2"), "Expected i2\n");
+ ok(check_record(rec, 3, "i2"), "Expected i2\n");
+ ok(check_record(rec, 4, "I2"), "Expected I2\n");
+ ok(check_record(rec, 5, "i4"), "Expected i4\n");
+ ok(check_record(rec, 6, "I4"), "Expected I4\n");
+ ok(check_record(rec, 7, "S255"), "Expected S255\n");
+ ok(check_record(rec, 8, "S0"), "Expected S0\n");
+ ok(check_record(rec, 9, "s0"), "Expected s0\n");
+ }
+
+ query = "SELECT * FROM `TestTable`";
+ r = do_query(hdb, query, &rec);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ }
+
+ todo_wine
+ {
+ ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n");
+ ok(check_record(rec, 7, "another string"), "Expected 'another string'\n");
+ ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n");
+ ok(check_record(rec, 9, "duh"), "Expected 'duh'\n");
+ }
+
+ i = MsiRecordGetInteger(rec, 2);
+ todo_wine
+ {
+ ok(i == 5, "Expected 5, got %d\n", i);
+ }
+
+ i = MsiRecordGetInteger(rec, 3);
+ todo_wine
+ {
+ ok(i == 2, "Expected 2, got %d\n", i);
+ }
+
+ i = MsiRecordGetInteger(rec, 4);
+ ok(i == 0x80000000, "Expected 0x80000000, got %d\n", i);
+
+ i = MsiRecordGetInteger(rec, 5);
+ todo_wine
+ {
+ ok(i == 2147483640, "Expected 2147483640, got %d\n", i);
+ }
+
+ i = MsiRecordGetInteger(rec, 6);
+ todo_wine
+ {
+ ok(i == -2147483640, "Expected -2147483640, got %d\n", i);
+ }
+
+ MsiCloseHandle(rec);
+ MsiCloseHandle(view);
+ MsiCloseHandle(hdb);
+ DeleteFileA(msifile);
+}
+
+static void test_markers(void)
+{
+ MSIHANDLE hdb, rec;
+ LPCSTR query;
+ UINT r;
+
+ hdb = create_db();
+ ok( hdb, "failed to create db\n");
+
+ rec = MsiCreateRecord(3);
+ MsiRecordSetString(rec, 1, "Table");
+ MsiRecordSetString(rec, 2, "Apples");
+ MsiRecordSetString(rec, 3, "Oranges");
+
+ /* try a legit create */
+ query = "CREATE TABLE `Table` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
+ r = run_query(hdb, 0, query);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ MsiCloseHandle(rec);
+
+ /* try table name as marker */
+ rec = MsiCreateRecord(1);
+ MsiRecordSetString(rec, 1, "Fable");
+ query = "CREATE TABLE `?` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+ /* try table name as marker without backticks */
+ MsiRecordSetString(rec, 1, "Mable");
+ query = "CREATE TABLE ? ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try one column name as marker */
+ MsiRecordSetString(rec, 1, "One");
+ query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try column names as markers */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(2);
+ MsiRecordSetString(rec, 1, "One");
+ MsiRecordSetString(rec, 2, "Two");
+ query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `One`)";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try names with backticks */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(3);
+ MsiRecordSetString(rec, 1, "One");
+ MsiRecordSetString(rec, 2, "Two");
+ MsiRecordSetString(rec, 3, "One");
+ query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
+ r = run_query(hdb, rec, query);
+ todo_wine
+ {
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+ }
+
+ /* try names with backticks, minus definitions */
+ query = "CREATE TABLE `Mable` ( `?`, `?` PRIMARY KEY `?`)";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try names without backticks */
+ query = "CREATE TABLE `Mable` ( ? SHORT NOT NULL, ? CHAR(255) PRIMARY KEY ?)";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try one long marker */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(1);
+ MsiRecordSetString(rec, 1, "`One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`");
+ query = "CREATE TABLE `Mable` ( ? )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try all names as markers */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(4);
+ MsiRecordSetString(rec, 1, "Mable");
+ MsiRecordSetString(rec, 2, "One");
+ MsiRecordSetString(rec, 3, "Two");
+ MsiRecordSetString(rec, 4, "One");
+ query = "CREATE TABLE `?` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try a legit insert */
+ query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( 5, 'hello' )";
+ r = run_query(hdb, 0, query);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+ /* try values as markers */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(2);
+ MsiRecordSetInteger(rec, 1, 4);
+ MsiRecordSetString(rec, 2, "hi");
+ query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+ /* try column names and values as markers */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(4);
+ MsiRecordSetString(rec, 1, "One");
+ MsiRecordSetString(rec, 2, "Two");
+ MsiRecordSetInteger(rec, 3, 5);
+ MsiRecordSetString(rec, 4, "hi");
+ query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( ?, '?' )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try column names as markers */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(2);
+ MsiRecordSetString(rec, 1, "One");
+ MsiRecordSetString(rec, 2, "Two");
+ query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( 3, 'yellow' )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* try table name as a marker */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(1);
+ MsiRecordSetString(rec, 1, "Table");
+ query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( 2, 'green' )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+ /* try table name and values as markers */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(3);
+ MsiRecordSetString(rec, 1, "Table");
+ MsiRecordSetInteger(rec, 2, 10);
+ MsiRecordSetString(rec, 3, "haha");
+ query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( ?, '?' )";
+ r = run_query(hdb, rec, query);
+ todo_wine
+ {
+ ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
+ }
+
+ /* try all markers */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(5);
+ MsiRecordSetString(rec, 1, "Table");
+ MsiRecordSetString(rec, 1, "One");
+ MsiRecordSetString(rec, 1, "Two");
+ MsiRecordSetInteger(rec, 2, 10);
+ MsiRecordSetString(rec, 3, "haha");
+ query = "INSERT INTO `?` ( `?`, `?` ) VALUES ( ?, '?' )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
+
+ /* insert an integer as a string */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(2);
+ MsiRecordSetString(rec, 1, "11");
+ MsiRecordSetString(rec, 2, "hi");
+ query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+ /* leave off the '' for the string */
+ MsiCloseHandle(rec);
+ rec = MsiCreateRecord(2);
+ MsiRecordSetInteger(rec, 1, 12);
+ MsiRecordSetString(rec, 2, "hi");
+ query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, ? )";
+ r = run_query(hdb, rec, query);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ MsiCloseHandle(rec);
+
+ MsiCloseHandle(hdb);
+ DeleteFileA(msifile);
+}
+
+START_TEST(db)
+{
+ test_msidatabase();
+ test_msiinsert();
+ test_msidecomposedesc();
+ test_msibadqueries();
+ test_viewmodify();
+ test_viewgetcolumninfo();
+ test_getcolinfo();
+ test_msiexport();
+ test_longstrings();
+ test_streamtable();
+ test_where();
+ test_msiimport();
+ test_markers();
+}
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ * Copyright (C) 2005 Aric Stewart for CodeWeavers
+ *
+ * A test program for MSI record formatting
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static MSIHANDLE helper_createpackage( const char *szName )
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+ CHAR szPackage[10];
+ MSIHANDLE hPackage;
+ MSIHANDLE suminfo;
+
+ DeleteFile(szName);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(szName, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ /* build summmary info */
+ res = MsiGetSummaryInformation(hdb, NULL, 7, &suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to open summaryinfo\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,2, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,3, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,4, VT_LPSTR, 0,NULL,
+ "Wine Hackers");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,7, VT_LPSTR, 0,NULL,
+ ";1033");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,9, VT_LPSTR, 0,NULL,
+ "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 14, VT_I4, 100, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 15, VT_I4, 0, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoPersist(suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to make summary info persist\n" );
+
+ res = MsiCloseHandle( suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to close suminfo\n" );
+
+ sprintf(szPackage,"#%li",(DWORD)hdb);
+ res = MsiOpenPackage(szPackage,&hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to open package\n" );
+
+ res = MsiCloseHandle( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to close database\n" );
+
+ return hPackage;
+}
+
+static void test_createpackage(void)
+{
+ static const CHAR filename[] = "winetest.msi";
+ MSIHANDLE hPackage = 0;
+ UINT res;
+
+ hPackage = helper_createpackage( filename );
+ ok ( hPackage != 0, " Failed to create package\n");
+
+ res = MsiCloseHandle( hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to close package\n" );
+
+ DeleteFile( filename );
+}
+
+static void test_formatrecord(void)
+{
+ char buffer[100];
+ MSIHANDLE hrec;
+ UINT r;
+ DWORD sz;
+
+ r = MsiFormatRecord(0, 0, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong error\n");
+
+ hrec = MsiCreateRecord(4);
+ ok( hrec, "failed to create record\n");
+
+ /* format an empty record */
+ r = MsiFormatRecord(0, hrec, NULL, NULL );
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ sz=0;
+ r = MsiFormatRecord(0, hrec, buffer+1, &sz);
+ ok( r == ERROR_MORE_DATA && buffer[0] == 'x', "format failed measuring with buffer\n");
+ ok( sz == 16, "size wrong\n");
+ sz=100;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 16, "size wrong\n");
+ ok( 0 == strcmp(buffer,"1: 2: 3: 4: "), "wrong output\n");
+
+ r = MsiCloseHandle(hrec);
+ ok(r==ERROR_SUCCESS, "Unable to close record\n");
+
+ hrec = MsiCreateRecord(6);
+ ok( hrec, "failed to create record\n");
+
+ sz = 100;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 24, "size wrong\n");
+ ok( 0 == strcmp(buffer,"1: 2: 3: 4: 5: 6: "), "wrong output\n");
+
+
+ /* format a format string with everything else empty */
+ r = MsiRecordSetString(hrec, 0, "%1");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ r = MsiFormatRecord(0, hrec, NULL, NULL );
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ r = MsiFormatRecord(0, hrec, buffer, NULL);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
+
+ sz = 123;
+ r = MsiFormatRecord(0, hrec, NULL, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong (%li)\n",sz);
+ sz = sizeof buffer;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"%1"), "wrong output\n");
+
+ /* make the buffer too small */
+ sz = 0;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_MORE_DATA, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"x"), "wrong output\n");
+
+ /* make the buffer a little bit bigger */
+ sz = 1;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_MORE_DATA, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output (%s)\n",buffer);
+
+ /* and again */
+ sz = 2;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_MORE_DATA, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"%"), "wrong output\n");
+
+ /* and again */
+ sz = 3;
+ buffer[0] = 'x';
+ buffer[1] = 0;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer\n");
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"%1"), "wrong output\n");
+
+ /* now try a real format string */
+ r = MsiRecordSetString(hrec, 0, "[1]");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+
+ /* now put something in the first field */
+ r = MsiRecordSetString(hrec, 1, "boo hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+
+ /* now put something in the first field */
+ r = MsiRecordSetString(hrec, 0, "[1] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+
+
+ /* empty string */
+ r = MsiRecordSetString(hrec, 0, "");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 30, "size wrong %li\n",sz);
+ ok( 0 == strcmp(buffer,"1: boo 2: hoo 3: 4: 5: 6: "),
+ "wrong output(%s)\n",buffer);
+
+ /* play games with recursive lookups */
+ r = MsiRecordSetString(hrec, 0, "[[1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"hey hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "[2]");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[[2]] hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[[3]]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"hey hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiCloseHandle(hrec);
+ ok(r==ERROR_SUCCESS, "Unable to close record\n");
+ hrec = MsiCreateRecord(12);
+ ok( hrec, "failed to create record\n");
+
+ r = MsiRecordSetString(hrec, 0, "[[3][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"big hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[3][4][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 7, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"big hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[3][[4]][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 10, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[1[]2] hey"), "wrong output (%s)\n",buffer);
+
+ /* incorrect formats */
+ r = MsiRecordSetString(hrec, 0, "[[[3][[4]][1]] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[[[3][[4]][1]] [2]"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[[3][[4]][1]] [2]]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 11, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[1[]2] hey]"), "wrong output (%s)\n",buffer);
+
+
+ /* play games with {} */
+
+ r = MsiRecordSetString(hrec, 0, "{[3][1]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 6, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"12 hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[{[3][1]}] [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 8, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"[12] hey"), "wrong output (%s)\n",buffer);
+
+
+ r = MsiRecordSetString(hrec, 0, "{test} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 10, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{test} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[test]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{[test]} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2][3][4]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 4, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer," hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2][3][dummy]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{2hey1[dummy]} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2][3][4][dummy]} [2]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{2hey1[dummy]} hey"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "{{[1][2]}[3][4][dummy]}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine{
+ ok( sz == 16, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{{2hey}1[dummy]}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1][2]}[3]{[4][dummy]}}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "hey");
+ r = MsiRecordSetString(hrec, 3, "1");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 0, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,""), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 12, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{[1][2]} {{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 15, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"12 {{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{[4]}{[1][2]} {{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 15, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"12 {{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+
+ r = MsiRecordSetString(hrec, 0, "{blah} {[4]}{[1][2]} {{[1][2]}[3]} {[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 22, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{blah} 12 {{12}3} {12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1]}[2]} {[4]}{[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 13, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"{{1}2} {}{12}"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{[1]}} {[4]}{[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 3, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer," 12"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "{{{[1]}} {[4]}{[1][2]}");
+ r = MsiRecordSetString(hrec, 1, "1");
+ r = MsiRecordSetString(hrec, 2, "2");
+ r = MsiRecordSetString(hrec, 3, "3");
+ r = MsiRecordSetString(hrec, 4, NULL);
+ r = MsiRecordSetString(hrec, 12, "big");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 3, "size wrong,(%li)\n",sz);
+ ok( 0 == strcmp(buffer," 12"), "wrong output (%s)\n",buffer);
+ }
+
+ /* now put play games with escaping */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\3asdf]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 16, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo [\\3asdf]"), "wrong output\n");
+
+ /* now put play games with escaping */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\x]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo [\\x]"), "wrong output\n");
+
+ /* now try other formats without a package */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [property]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 18, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo [property]"), "wrong output\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1] [~] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 11, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo [~] hoo"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1]");
+ r = MsiRecordSetInteger(hrec, 1, 123456);
+ ok( r == ERROR_SUCCESS, "set integer failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ todo_wine{
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"123456"), "wrong output (%s)\n",buffer);
+ }
+
+ r = MsiRecordSetString(hrec, 0, "[~]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[~]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ /* MsiFormatRecord doesn't seem to handle a negative too well */
+ r = MsiRecordSetString(hrec, 0, "[-1]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[-1]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[]}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[]}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[0]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[0]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[100]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1] [2]}");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{foo}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{foo}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{boo [1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]}}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{ {[1]}}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( 0 == strcmp(buffer," {hoo}"), "wrong output\n");
+ ok( sz == 6, "size wrong\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]} }");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{hoo} }"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ [1]}}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1] }}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a}{b}{c }{ d}{any text}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{{a} }");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{a} }"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a} {b}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a} b}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{a b}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{ }");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{ }"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, " {{a}}}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer," }"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ almost {{ any }} text }}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer," text }}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ } { hidden ][ [ }}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 0, "size wrong\n");
+ ok( 0 == strcmp(buffer,""), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[ 1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[ 1]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[01]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{test}} [01");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer," [01"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[\\[]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[\\[]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[foo]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[foo]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[01.]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[01.]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ SetEnvironmentVariable("FOO", "BAR");
+ r = MsiRecordSetString(hrec, 0, "[%FOO]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[%FOO]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ {[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ {[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{ {{[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {{hoo}"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{{ {{a}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {{a}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{{ {{a}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 7, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{{ {{a}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "0{1{2{3{4[1]5}6}7}8}9");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 19, "size wrong\n");
+ ok( 0 == strcmp(buffer,"01{2{3{4hoo56}7}8}9"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "0{1{2[1]3}4");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"01{2hoo34"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "0{1{2[1]3}4");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"01{2hoo34"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1.} [1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[1.} hoo"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[{[1]}]}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[{[1]}]}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1][}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ r = MsiRecordSetString(hrec, 2, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"2["), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1]");
+ r = MsiRecordSetString(hrec, 1, "[2]");
+ r = MsiRecordSetString(hrec, 2, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[2]"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "[{{boo}}1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[1]"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "[{{boo}}1]");
+ r = MsiRecordSetString(hrec, 0, "[1{{boo}}]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[1]"), "wrong output\n");
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1]{{boo} }}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 11, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo{{boo }}"), "wrong output\n");
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1{{boo}}]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 12, "size wrong: got %lu, expected 12\n", sz);
+ ok( 0 == strcmp(buffer,"{[1{{boo}}]}"), "wrong output: got %s, expected [1]\n", buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ todo_wine {
+ r = MsiRecordSetString(hrec, 0, "{{[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong: got %lu, expected 3\n", sz);
+ ok( 0 == strcmp(buffer,"{{hoo}"), "wrong output: got %s, expected [1]\n", buffer);
+ }
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1{{bo}o}}]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 13, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[1{{bo}o}}]}"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1{{b{o}o}}]}");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 14, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{[1{{b{o}o}}]}"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{ {[1]}");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer," {hoo"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ /* {} inside a substitution does strange things... */
+ r = MsiRecordSetString(hrec, 0, "[[1]{}]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[[1]]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[[1]{}[1]]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[[1]2]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[a[1]b[1]c{}d[1]e]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 14, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[a[1]b[1]cd2e]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[a[1]b");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 6, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[a[1]b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "a[1]b]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"a2b]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "]a[1]b");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"]a2b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "]a[1]b");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 4, "size wrong\n");
+ ok( 0 == strcmp(buffer,"]a2b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "\\[1]");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"\\2"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "\\{[1]}");
+ r = MsiRecordSetString(hrec, 1, "2");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"\\2"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "a{b[1]c}d");
+ r = MsiRecordSetString(hrec, 1, NULL);
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 2, "size wrong\n");
+ ok( 0 == strcmp(buffer,"ad"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{a[0]b}");
+ r = MsiRecordSetString(hrec, 1, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"a{a[0]b}b"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[foo]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 5, "size wrong\n");
+ ok( 0 == strcmp(buffer,"[foo]"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "{[1][-1][1]}");
+ r = MsiRecordSetString(hrec, 1, "foo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(0, hrec, buffer, &sz);
+ ok( sz == 12, "size wrong\n");
+ ok( 0 == strcmp(buffer,"{foo[-1]foo}"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ MsiCloseHandle( hrec );
+}
+
+static void test_formatrecord_package(void)
+{
+ static const CHAR filename[] = "winetest.msi";
+ char buffer[100];
+ MSIHANDLE hrec;
+ MSIHANDLE package;
+ UINT r;
+ DWORD sz=100;
+
+ package = helper_createpackage( filename );
+ ok(package!=0, "Unable to create package\n");
+
+ hrec = MsiCreateRecord(12);
+ ok( hrec, "failed to create record\n");
+
+ r = MsiFormatRecord(package, 0, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong error\n");
+
+ r = MsiFormatRecord(package, hrec, NULL, NULL );
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec,0,NULL);
+ r = MsiRecordSetString(hrec,1,NULL);
+ r = MsiRecordSetString(hrec,2,NULL);
+ r = MsiRecordSetString(hrec,3,NULL);
+ r = MsiRecordSetString(hrec,4,NULL);
+ r = MsiRecordSetString(hrec,5,NULL);
+ r = MsiRecordSetString(hrec,6,NULL);
+ r = MsiRecordSetString(hrec,7,NULL);
+ r = MsiRecordSetString(hrec,8,NULL);
+ r = MsiRecordSetString(hrec,9,NULL);
+ r = MsiRecordSetString(hrec,10,NULL);
+ r = MsiRecordSetString(hrec,11,NULL);
+ r = MsiRecordSetString(hrec,12,NULL);
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed with empty buffer (%i)\n",r);
+ ok( sz == 51, "size wrong (%li)\n",sz);
+ ok( 0 == strcmp(buffer,"1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: "), "wrong output(%s)\n",buffer);
+
+ /* now put play games with escaping */
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\3asdf]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo 3"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [\\x]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo x"), "wrong output (%s)\n",buffer);
+
+ /* null characters */
+ r = MsiRecordSetString(hrec, 0, "[1] [~] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo "), "wrong output\n");
+
+ /* properties */
+ r = MsiSetProperty(package,"dummy","Bork");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[1] [dummy] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo Bork hoo"), "wrong output\n");
+
+ r = MsiRecordSetString(hrec, 0, "[1] [invalid] [2]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 8, "size wrong\n");
+ ok( 0 == strcmp(buffer,"boo hoo"), "wrong output\n");
+
+
+ /* nesting tests */
+ r = MsiSetProperty(package,"dummya","foo");
+ r = MsiSetProperty(package,"dummyb","baa");
+ r = MsiSetProperty(package,"adummyc","whoa");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[dummy[1]] [dummy[2]] [[1]dummy[3]]");
+ r = MsiRecordSetString(hrec, 1, "a");
+ r = MsiRecordSetString(hrec, 2, "b");
+ r = MsiRecordSetString(hrec, 3, "c");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 12, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"foo baa whoa"), "wrong output (%s)\n",buffer);
+
+
+ r = MsiSetProperty(package,"dummya","1");
+ r = MsiSetProperty(package,"dummyb","[2]");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[dummya] [[dummya]] [dummyb]");
+ r = MsiRecordSetString(hrec, 1, "aaa");
+ r = MsiRecordSetString(hrec, 2, "bbb");
+ r = MsiRecordSetString(hrec, 3, "ccc");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"1 [1] [2]"), "wrong output (%s)\n",buffer);
+
+ r = MsiSetProperty(package,"dummya","1");
+ r = MsiSetProperty(package,"dummyb","a");
+ r = MsiSetProperty(package,"dummyc","\\blath");
+ r = MsiSetProperty(package,"dummyd","[\\blath]");
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+ r = MsiRecordSetString(hrec, 0, "[dummyc] [[dummyc]] [dummy[dummyb]]");
+ r = MsiRecordSetString(hrec, 1, "aaa");
+ r = MsiRecordSetString(hrec, 2, "bbb");
+ r = MsiRecordSetString(hrec, 3, "ccc");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 10, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"\\blath b 1"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [[\\3asdf]]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ r = MsiRecordSetString(hrec, 3, "yeah");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 11, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo [3]"), "wrong output (%s)\n",buffer);
+
+ r = MsiRecordSetString(hrec, 0, "[1] [2] [[3]]");
+ r = MsiRecordSetString(hrec, 1, "boo");
+ r = MsiRecordSetString(hrec, 2, "hoo");
+ r = MsiRecordSetString(hrec, 3, "\\help");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(package, hrec, buffer, &sz);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+ ok( sz == 9, "size wrong(%li)\n",sz);
+ ok( 0 == strcmp(buffer,"boo hoo h"), "wrong output (%s)\n",buffer);
+
+ MsiCloseHandle(hrec);
+
+ r = MsiCloseHandle(package);
+ ok(r==ERROR_SUCCESS, "Unable to close package\n");
+
+ DeleteFile( filename );
+}
+
+static void test_processmessage(void)
+{
+ static const CHAR filename[] = "winetest.msi";
+ MSIHANDLE hrec;
+ MSIHANDLE package;
+ int r;
+
+ package = helper_createpackage( filename );
+ ok(package!=0, "Unable to create package\n");
+
+ hrec = MsiCreateRecord(3);
+ ok( hrec, "failed to create record\n");
+
+ r = MsiRecordSetString(hrec, 1, "");
+ ok( r == ERROR_SUCCESS, "set string failed\n");
+
+ r = MsiProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, hrec);
+ ok( r == IDOK, "expected IDOK, got %i\n", r);
+
+ MsiCloseHandle(hrec);
+ MsiCloseHandle(package);
+
+ DeleteFile(filename);
+}
+
+START_TEST(format)
+{
+ test_createpackage();
+ test_formatrecord();
+ test_formatrecord_package();
+ test_processmessage();
+}
--- /dev/null
+/*
+ * Copyright (C) 2006 James Hawkins
+ *
+ * A test program for installing MSI products.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+
+#include <windows.h>
+#include <msiquery.h>
+#include <msidefs.h>
+#include <msi.h>
+#include <fci.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static const char *msifile = "winetest.msi";
+CHAR CURR_DIR[MAX_PATH];
+CHAR PROG_FILES_DIR[MAX_PATH];
+
+/* msi database data */
+
+static const CHAR admin_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+ "s72\tS255\tI2\n"
+ "AdminExecuteSequence\tAction\n"
+ "CostFinalize\t\t1000\n"
+ "CostInitialize\t\t800\n"
+ "FileCost\t\t900\n"
+ "InstallAdminPackage\t\t3900\n"
+ "InstallFiles\t\t4000\n"
+ "InstallFinalize\t\t6600\n"
+ "InstallInitialize\t\t1500\n"
+ "InstallValidate\t\t1400";
+
+static const CHAR advt_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+ "s72\tS255\tI2\n"
+ "AdvtExecuteSequence\tAction\n"
+ "CostFinalize\t\t1000\n"
+ "CostInitialize\t\t800\n"
+ "CreateShortcuts\t\t4500\n"
+ "InstallFinalize\t\t6600\n"
+ "InstallInitialize\t\t1500\n"
+ "InstallValidate\t\t1400\n"
+ "PublishComponents\t\t6200\n"
+ "PublishFeatures\t\t6300\n"
+ "PublishProduct\t\t6400\n"
+ "RegisterClassInfo\t\t4600\n"
+ "RegisterExtensionInfo\t\t4700\n"
+ "RegisterMIMEInfo\t\t4900\n"
+ "RegisterProgIdInfo\t\t4800";
+
+static const CHAR component_dat[] = "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
+ "s72\tS38\ts72\ti2\tS255\tS72\n"
+ "Component\tComponent\n"
+ "Five\t{8CC92E9D-14B2-4CA4-B2AA-B11D02078087}\tNEWDIR\t2\t\tfive.txt\n"
+ "Four\t{FD37B4EA-7209-45C0-8917-535F35A2F080}\tCABOUTDIR\t2\t\tfour.txt\n"
+ "One\t{783B242E-E185-4A56-AF86-C09815EC053C}\tMSITESTDIR\t2\t\tone.txt\n"
+ "Three\t{010B6ADD-B27D-4EDD-9B3D-34C4F7D61684}\tCHANGEDDIR\t2\t\tthree.txt\n"
+ "Two\t{BF03D1A6-20DA-4A65-82F3-6CAC995915CE}\tFIRSTDIR\t2\t\ttwo.txt\n"
+ "dangler\t{6091DF25-EF96-45F1-B8E9-A9B1420C7A3C}\tTARGETDIR\t4\t\tregdata";
+
+static const CHAR directory_dat[] = "Directory\tDirectory_Parent\tDefaultDir\n"
+ "s72\tS72\tl255\n"
+ "Directory\tDirectory\n"
+ "CABOUTDIR\tMSITESTDIR\tcabout\n"
+ "CHANGEDDIR\tMSITESTDIR\tchanged:second\n"
+ "FIRSTDIR\tMSITESTDIR\tfirst\n"
+ "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
+ "NEWDIR\tCABOUTDIR\tnew\n"
+ "ProgramFilesFolder\tTARGETDIR\t.\n"
+ "TARGETDIR\t\tSourceDir";
+
+static const CHAR feature_dat[] = "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
+ "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
+ "Feature\tFeature\n"
+ "Five\t\tFive\tThe Five Feature\t5\t3\tNEWDIR\t0\n"
+ "Four\t\tFour\tThe Four Feature\t4\t3\tCABOUTDIR\t0\n"
+ "One\t\tOne\tThe One Feature\t1\t3\tMSITESTDIR\t0\n"
+ "Three\t\tThree\tThe Three Feature\t3\t3\tCHANGEDDIR\t0\n"
+ "Two\t\tTwo\tThe Two Feature\t2\t3\tFIRSTDIR\t0";
+
+static const CHAR feature_comp_dat[] = "Feature_\tComponent_\n"
+ "s38\ts72\n"
+ "FeatureComponents\tFeature_\tComponent_\n"
+ "Five\tFive\n"
+ "Four\tFour\n"
+ "One\tOne\n"
+ "Three\tThree\n"
+ "Two\tTwo";
+
+static const CHAR file_dat[] = "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
+ "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
+ "File\tFile\n"
+ "five.txt\tFive\tfive.txt\t1000\t\t\t16384\t5\n"
+ "four.txt\tFour\tfour.txt\t1000\t\t\t16384\t4\n"
+ "one.txt\tOne\tone.txt\t1000\t\t\t0\t1\n"
+ "three.txt\tThree\tthree.txt\t1000\t\t\t0\t3\n"
+ "two.txt\tTwo\ttwo.txt\t1000\t\t\t0\t2";
+
+static const CHAR install_exec_seq_dat[] = "Action\tCondition\tSequence\n"
+ "s72\tS255\tI2\n"
+ "InstallExecuteSequence\tAction\n"
+ "AllocateRegistrySpace\tNOT Installed\t1550\n"
+ "CostFinalize\t\t1000\n"
+ "CostInitialize\t\t800\n"
+ "FileCost\t\t900\n"
+ "InstallFiles\t\t4000\n"
+ "InstallFinalize\t\t6600\n"
+ "InstallInitialize\t\t1500\n"
+ "InstallValidate\t\t1400\n"
+ "LaunchConditions\t\t100\n"
+ "WriteRegistryValues\t\t5000";
+
+static const CHAR media_dat[] = "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
+ "i2\ti4\tL64\tS255\tS32\tS72\n"
+ "Media\tDiskId\n"
+ "1\t3\t\t\tDISK1\t\n"
+ "2\t5\t\tmsitest.cab\tDISK2\t\n";
+
+static const CHAR property_dat[] = "Property\tValue\n"
+ "s72\tl0\n"
+ "Property\tProperty\n"
+ "DefaultUIFont\tDlgFont8\n"
+ "INSTALLLEVEL\t3\n"
+ "InstallMode\tTypical\n"
+ "Manufacturer\tWine\n"
+ "PIDTemplate\t12345<###-%%%%%%%>@@@@@\n"
+ "ProductCode\t{F1C3AF50-8B56-4A69-A00C-00773FE42F30}\n"
+ "ProductID\tnone\n"
+ "ProductLanguage\t1033\n"
+ "ProductName\tMSITEST\n"
+ "ProductVersion\t1.1.1\n"
+ "PROMPTROLLBACKCOST\tP\n"
+ "Setup\tSetup\n"
+ "UpgradeCode\t{CE067E8D-2E1A-4367-B734-4EB2BDAD6565}";
+
+static const CHAR registry_dat[] = "Registry\tRoot\tKey\tName\tValue\tComponent_\n"
+ "s72\ti2\tl255\tL255\tL0\ts72\n"
+ "Registry\tRegistry\n"
+ "Apples\t2\tSOFTWARE\\Wine\\msitest\tName\timaname\tOne\n"
+ "Oranges\t2\tSOFTWARE\\Wine\\msitest\tnumber\t#314\tTwo\n"
+ "regdata\t2\tSOFTWARE\\Wine\\msitest\tblah\tbad\tdangler";
+
+typedef struct _msi_table
+{
+ const CHAR *filename;
+ const CHAR *data;
+ int size;
+} msi_table;
+
+#define ADD_TABLE(x) {#x".idt", x##_dat, sizeof(x##_dat)}
+
+static const msi_table tables[] =
+{
+ ADD_TABLE(admin_exec_seq),
+ ADD_TABLE(advt_exec_seq),
+ ADD_TABLE(component),
+ ADD_TABLE(directory),
+ ADD_TABLE(feature),
+ ADD_TABLE(feature_comp),
+ ADD_TABLE(file),
+ ADD_TABLE(install_exec_seq),
+ ADD_TABLE(media),
+ ADD_TABLE(property),
+ ADD_TABLE(registry)
+};
+
+/* cabinet definitions */
+
+/* make the max size large so there is only one cab file */
+#define MEDIA_SIZE 999999999
+#define FOLDER_THRESHOLD 900000
+
+/* The following defintions were copied from dlls/cabinet/cabinet.h
+ * because they are undocumented in windows.
+ */
+
+/* EXTRACTdest flags */
+#define EXTRACT_FILLFILELIST 0x00000001
+#define EXTRACT_EXTRACTFILES 0x00000002
+
+struct ExtractFileList {
+ LPSTR filename;
+ struct ExtractFileList *next;
+ BOOL unknown; /* always 1L */
+};
+
+/* the first parameter of the function extract */
+typedef struct {
+ long result1; /* 0x000 */
+ long unknown1[3]; /* 0x004 */
+ struct ExtractFileList *filelist; /* 0x010 */
+ long filecount; /* 0x014 */
+ long flags; /* 0x018 */
+ char directory[0x104]; /* 0x01c */
+ char lastfile[0x20c]; /* 0x120 */
+} EXTRACTDEST;
+
+/* cabinet function pointers */
+HMODULE hCabinet;
+static HRESULT (WINAPI *pExtract)(EXTRACTDEST*, LPCSTR);
+
+/* the FCI callbacks */
+
+static void *mem_alloc(ULONG cb)
+{
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+static void mem_free(void *memory)
+{
+ HeapFree(GetProcessHeap(), 0, memory);
+}
+
+static BOOL get_next_cabinet(PCCAB pccab, ULONG cbPrevCab, void *pv)
+{
+ return TRUE;
+}
+
+static long progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
+{
+ return 0;
+}
+
+static int file_placed(PCCAB pccab, char *pszFile, long cbFile,
+ BOOL fContinuation, void *pv)
+{
+ return 0;
+}
+
+static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
+{
+ HANDLE handle;
+ DWORD dwAccess = 0;
+ DWORD dwShareMode = 0;
+ DWORD dwCreateDisposition = OPEN_EXISTING;
+
+ dwAccess = GENERIC_READ | GENERIC_WRITE;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+
+ if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
+ dwCreateDisposition = OPEN_EXISTING;
+ else
+ dwCreateDisposition = CREATE_NEW;
+
+ handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
+ dwCreateDisposition, 0, NULL);
+
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);
+
+ return (INT_PTR)handle;
+}
+
+static UINT fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD dwRead;
+ BOOL res;
+
+ res = ReadFile(handle, memory, cb, &dwRead, NULL);
+ ok(res, "Failed to ReadFile\n");
+
+ return dwRead;
+}
+
+static UINT fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD dwWritten;
+ BOOL res;
+
+ res = WriteFile(handle, memory, cb, &dwWritten, NULL);
+ ok(res, "Failed to WriteFile\n");
+
+ return dwWritten;
+}
+
+static int fci_close(INT_PTR hf, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ ok(CloseHandle(handle), "Failed to CloseHandle\n");
+
+ return 0;
+}
+
+static long fci_seek(INT_PTR hf, long dist, int seektype, int *err, void *pv)
+{
+ HANDLE handle = (HANDLE)hf;
+ DWORD ret;
+
+ ret = SetFilePointer(handle, dist, NULL, seektype);
+ ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
+
+ return ret;
+}
+
+static int fci_delete(char *pszFile, int *err, void *pv)
+{
+ BOOL ret = DeleteFileA(pszFile);
+ ok(ret, "Failed to DeleteFile %s\n", pszFile);
+
+ return 0;
+}
+
+static BOOL check_record(MSIHANDLE rec, UINT field, LPCSTR val)
+{
+ CHAR buffer[0x20];
+ UINT r;
+ DWORD sz;
+
+ sz = sizeof buffer;
+ r = MsiRecordGetString(rec, field, buffer, &sz);
+ return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
+}
+
+static BOOL get_temp_file(char *pszTempName, int cbTempName, void *pv)
+{
+ LPSTR tempname;
+
+ tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
+ GetTempFileNameA(".", "xx", 0, tempname);
+
+ if (tempname && (strlen(tempname) < (unsigned)cbTempName))
+ {
+ lstrcpyA(pszTempName, tempname);
+ HeapFree(GetProcessHeap(), 0, tempname);
+ return TRUE;
+ }
+
+ HeapFree(GetProcessHeap(), 0, tempname);
+
+ return FALSE;
+}
+
+static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
+ USHORT *pattribs, int *err, void *pv)
+{
+ BY_HANDLE_FILE_INFORMATION finfo;
+ FILETIME filetime;
+ HANDLE handle;
+ DWORD attrs;
+ BOOL res;
+
+ handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+
+ ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);
+
+ res = GetFileInformationByHandle(handle, &finfo);
+ ok(res, "Expected GetFileInformationByHandle to succeed\n");
+
+ FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
+ FileTimeToDosDateTime(&filetime, pdate, ptime);
+
+ attrs = GetFileAttributes(pszName);
+ ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");
+
+ return (INT_PTR)handle;
+}
+
+static void add_file(HFCI hfci, char *file)
+{
+ char path[MAX_PATH];
+ BOOL res;
+
+ lstrcpyA(path, CURR_DIR);
+ lstrcatA(path, "\\");
+ lstrcatA(path, file);
+
+ res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
+ get_open_info, tcompTYPE_MSZIP);
+ ok(res, "Expected FCIAddFile to succeed\n");
+}
+
+static void set_cab_parameters(PCCAB pCabParams, const CHAR *name)
+{
+ ZeroMemory(pCabParams, sizeof(CCAB));
+
+ pCabParams->cb = MEDIA_SIZE;
+ pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
+ pCabParams->setID = 0xbeef;
+ lstrcpyA(pCabParams->szCabPath, CURR_DIR);
+ lstrcatA(pCabParams->szCabPath, "\\");
+ lstrcpyA(pCabParams->szCab, name);
+}
+
+static void create_cab_file(const CHAR *name)
+{
+ CCAB cabParams;
+ HFCI hfci;
+ ERF erf;
+ static CHAR four_txt[] = "four.txt",
+ five_txt[] = "five.txt";
+ BOOL res;
+
+ set_cab_parameters(&cabParams, name);
+
+ hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
+ fci_read, fci_write, fci_close, fci_seek, fci_delete,
+ get_temp_file, &cabParams, NULL);
+
+ ok(hfci != NULL, "Failed to create an FCI context\n");
+
+ add_file(hfci, four_txt);
+ add_file(hfci, five_txt);
+
+ res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
+ ok(res, "Failed to flush the cabinet\n");
+
+ res = FCIDestroy(hfci);
+ ok(res, "Failed to destroy the cabinet\n");
+}
+
+static BOOL init_function_pointers(void)
+{
+ hCabinet = LoadLibraryA("cabinet.dll");
+ if (!hCabinet)
+ return FALSE;
+
+ pExtract = (void *)GetProcAddress(hCabinet, "Extract");
+ if (!pExtract)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL get_program_files_dir(LPSTR buf)
+{
+ HKEY hkey;
+ CHAR temp[MAX_PATH];
+ DWORD type = REG_EXPAND_SZ, size;
+
+ if (RegOpenKey(HKEY_LOCAL_MACHINE,
+ "Software\\Microsoft\\Windows\\CurrentVersion", &hkey))
+ return FALSE;
+
+ size = MAX_PATH;
+ if (RegQueryValueEx(hkey, "ProgramFilesPath", 0, &type, (LPBYTE)temp, &size))
+ return FALSE;
+
+ ExpandEnvironmentStrings(temp, buf, MAX_PATH);
+
+ RegCloseKey(hkey);
+ return TRUE;
+}
+
+static void create_file(const CHAR *name)
+{
+ HANDLE file;
+ DWORD written;
+
+ file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
+ WriteFile(file, name, strlen(name), &written, NULL);
+ WriteFile(file, "\n", strlen("\n"), &written, NULL);
+ CloseHandle(file);
+}
+
+static void create_test_files(void)
+{
+ int len;
+
+ GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+ len = lstrlenA(CURR_DIR);
+
+ if(len && (CURR_DIR[len-1] == '\\'))
+ CURR_DIR[len - 1] = 0;
+
+ get_program_files_dir(PROG_FILES_DIR);
+
+ CreateDirectoryA("msitest", NULL);
+ create_file("msitest\\one.txt");
+ CreateDirectoryA("msitest\\first", NULL);
+ create_file("msitest\\first\\two.txt");
+ CreateDirectoryA("msitest\\second", NULL);
+ create_file("msitest\\second\\three.txt");
+
+ create_file("four.txt");
+ create_file("five.txt");
+ create_cab_file("msitest.cab");
+
+ DeleteFileA("four.txt");
+ DeleteFileA("five.txt");
+}
+
+static BOOL delete_pf(const CHAR *rel_path, BOOL is_file)
+{
+ CHAR path[MAX_PATH];
+
+ lstrcpyA(path, PROG_FILES_DIR);
+ lstrcatA(path, "\\");
+ lstrcatA(path, rel_path);
+
+ if (is_file)
+ return DeleteFileA(path);
+ else
+ return RemoveDirectoryA(path);
+}
+
+static void delete_test_files(void)
+{
+ DeleteFileA("msitest.msi");
+ DeleteFileA("msitest.cab");
+ DeleteFileA("msitest\\second\\three.txt");
+ DeleteFileA("msitest\\first\\two.txt");
+ DeleteFileA("msitest\\one.txt");
+ RemoveDirectoryA("msitest\\second");
+ RemoveDirectoryA("msitest\\first");
+ RemoveDirectoryA("msitest");
+}
+
+static void write_file(const CHAR *filename, const char *data, int data_size)
+{
+ DWORD size;
+
+ HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ WriteFile(hf, data, data_size, &size, NULL);
+ CloseHandle(hf);
+}
+
+static void write_msi_summary_info(MSIHANDLE db)
+{
+ MSIHANDLE summary;
+ UINT r;
+
+ r = MsiGetSummaryInformationA(db, NULL, 4, &summary);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_TEMPLATE, VT_LPSTR, 0, NULL, ";1033");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_REVNUMBER, VT_LPSTR, 0, NULL,
+ "{004757CA-5092-49c2-AD20-28E1CE0DF5F2}");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_PAGECOUNT, VT_I4, 100, NULL, NULL);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSummaryInfoSetPropertyA(summary, PID_WORDCOUNT, VT_I4, 0, NULL, NULL);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ /* write the summary changes back to the stream */
+ r = MsiSummaryInfoPersist(summary);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ MsiCloseHandle(summary);
+}
+
+static void create_database(const CHAR *name, const msi_table *tables, int num_tables)
+{
+ MSIHANDLE db;
+ UINT r;
+ int j;
+
+ r = MsiOpenDatabaseA(name, MSIDBOPEN_CREATE, &db);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ /* import the tables into the database */
+ for (j = 0; j < num_tables; j++)
+ {
+ const msi_table *table = &tables[j];
+
+ write_file(table->filename, table->data, (table->size - 1) * sizeof(char));
+
+ r = MsiDatabaseImportA(db, CURR_DIR, table->filename);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+ }
+
+ DeleteFileA(table->filename);
+ }
+
+ write_msi_summary_info(db);
+
+ r = MsiDatabaseCommit(db);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ MsiCloseHandle(db);
+}
+
+static void test_MsiInstallProduct(void)
+{
+ UINT r;
+ CHAR path[MAX_PATH];
+ LONG res;
+ HKEY hkey;
+ DWORD num, size, type;
+
+ r = MsiInstallProductA(msifile, NULL);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+ }
+
+ todo_wine
+ {
+ ok(delete_pf("msitest\\cabout\\new\\five.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\cabout\\new", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\cabout\\four.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\cabout", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\changed\\three.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\changed", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\first\\two.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest\\first", FALSE), "File not installed\n");
+ ok(delete_pf("msitest\\one.txt", TRUE), "File not installed\n");
+ ok(delete_pf("msitest", FALSE), "File not installed\n");
+ }
+
+ res = RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest", &hkey);
+ todo_wine
+ {
+ ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res);
+ }
+
+ size = MAX_PATH;
+ type = REG_SZ;
+ res = RegQueryValueExA(hkey, "Name", NULL, &type, (LPBYTE)path, &size);
+ todo_wine
+ {
+ ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res);
+ ok(!lstrcmpA(path, "imaname"), "Expected imaname, got %s\n", path);
+ }
+
+ size = MAX_PATH;
+ type = REG_SZ;
+ res = RegQueryValueExA(hkey, "blah", NULL, &type, (LPBYTE)path, &size);
+ todo_wine
+ {
+ ok(res == ERROR_FILE_NOT_FOUND, "Expected ERROR_FILE_NOT_FOUND, got %ld\n", res);
+ }
+
+ size = sizeof(num);
+ type = REG_DWORD;
+ res = RegQueryValueExA(hkey, "number", NULL, &type, (LPBYTE)&num, &size);
+ todo_wine
+ {
+ ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %ld\n", res);
+ ok(num == 314, "Expected 314, got %ld\n", num);
+ }
+
+ RegDeleteKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine\\msitest");
+}
+
+static void test_MsiSetComponentState(void)
+{
+ MSIHANDLE package;
+ char path[MAX_PATH];
+ UINT r;
+
+ CoInitialize(NULL);
+
+ lstrcpy(path, CURR_DIR);
+ lstrcat(path, "\\");
+ lstrcat(path, msifile);
+
+ r = MsiOpenPackage(path, &package);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiDoAction(package, "CostInitialize");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiDoAction(package, "FileCost");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiDoAction(package, "CostFinalize");
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ r = MsiSetComponentState(package, "dangler", INSTALLSTATE_SOURCE);
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+ }
+
+ MsiCloseHandle(package);
+ CoUninitialize();
+}
+
+static void test_packagecoltypes(void)
+{
+ MSIHANDLE hdb, view, rec;
+ char path[MAX_PATH];
+ LPCSTR query;
+ UINT r, count;
+
+ CoInitialize(NULL);
+
+ lstrcpy(path, CURR_DIR);
+ lstrcat(path, "\\");
+ lstrcat(path, msifile);
+
+ r = MsiOpenDatabase(path, MSIDBOPEN_READONLY, &hdb);
+ ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r);
+
+ query = "SELECT * FROM `Media`";
+ r = MsiDatabaseOpenView( hdb, query, &view );
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
+ }
+
+ r = MsiViewGetColumnInfo( view, MSICOLINFO_NAMES, &rec );
+ count = MsiRecordGetFieldCount( rec );
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n");
+ ok(count == 6, "Expected 6, got %d\n", count);
+ ok(check_record(rec, 1, "DiskId"), "wrong column label\n");
+ ok(check_record(rec, 2, "LastSequence"), "wrong column label\n");
+ ok(check_record(rec, 3, "DiskPrompt"), "wrong column label\n");
+ ok(check_record(rec, 4, "Cabinet"), "wrong column label\n");
+ ok(check_record(rec, 5, "VolumeLabel"), "wrong column label\n");
+ ok(check_record(rec, 6, "Source"), "wrong column label\n");
+ }
+
+ r = MsiViewGetColumnInfo( view, MSICOLINFO_TYPES, &rec );
+ count = MsiRecordGetFieldCount( rec );
+ todo_wine
+ {
+ ok(r == ERROR_SUCCESS, "MsiViewGetColumnInfo failed\n");
+ ok(count == 6, "Expected 6, got %d\n", count);
+ ok(check_record(rec, 1, "i2"), "wrong column label\n");
+ ok(check_record(rec, 2, "i4"), "wrong column label\n");
+ ok(check_record(rec, 3, "L64"), "wrong column label\n");
+ ok(check_record(rec, 4, "S255"), "wrong column label\n");
+ ok(check_record(rec, 5, "S32"), "wrong column label\n");
+ ok(check_record(rec, 6, "S72"), "wrong column label\n");
+ }
+
+ MsiCloseHandle(hdb);
+ DeleteFile(msifile);
+}
+
+START_TEST(install)
+{
+ if (!init_function_pointers())
+ return;
+
+ create_test_files();
+ create_database(msifile, tables, sizeof(tables) / sizeof(msi_table));
+
+ test_MsiInstallProduct();
+ test_MsiSetComponentState();
+ test_packagecoltypes();
+
+ delete_test_files();
+}
--- /dev/null
+/*
+ * tests for Microsoft Installer functionality
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+typedef struct test_MSIFILEHASHINFO {
+ ULONG dwFileHashInfoSize;
+ ULONG dwData[4];
+} test_MSIFILEHASHINFO, *test_PMSIFILEHASHINFO;
+
+typedef INSTALLSTATE (WINAPI *fnMsiUseFeatureExA)(LPCSTR, LPCSTR ,DWORD, DWORD );
+fnMsiUseFeatureExA pMsiUseFeatureExA;
+typedef UINT (WINAPI *fnMsiOpenPackageExA)(LPCSTR, DWORD, MSIHANDLE*);
+fnMsiOpenPackageExA pMsiOpenPackageExA;
+typedef UINT (WINAPI *fnMsiOpenPackageExW)(LPCWSTR, DWORD, MSIHANDLE*);
+fnMsiOpenPackageExW pMsiOpenPackageExW;
+typedef INSTALLSTATE (WINAPI *fnMsiGetComponentPathA)(LPCSTR, LPCSTR, LPSTR, DWORD*);
+fnMsiGetComponentPathA pMsiGetComponentPathA;
+typedef UINT (WINAPI *fnMsiGetFileHashA)(LPCSTR, DWORD, test_PMSIFILEHASHINFO);
+fnMsiGetFileHashA pMsiGetFileHashA;
+
+static void test_usefeature(void)
+{
+ UINT r;
+
+ if (!pMsiUseFeatureExA)
+ return;
+
+ r = MsiQueryFeatureState(NULL,NULL);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = MsiQueryFeatureState("{9085040-6000-11d3-8cfe-0150048383c9}" ,NULL);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA(NULL,NULL,0,0);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA(NULL, "WORDVIEWFiles", -2, 1 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}",
+ NULL, -2, 0 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{9085040-6000-11d3-8cfe-0150048383c9}",
+ "WORDVIEWFiles", -2, 0 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{0085040-6000-11d3-8cfe-0150048383c9}",
+ "WORDVIEWFiles", -2, 0 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+
+ r = pMsiUseFeatureExA("{90850409-6000-11d3-8cfe-0150048383c9}",
+ "WORDVIEWFiles", -2, 1 );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return val\n");
+}
+
+static void test_null(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+
+ r = pMsiOpenPackageExW(NULL, 0, &hpkg);
+ ok( r == ERROR_INVALID_PARAMETER,"wrong error\n");
+
+ r = MsiQueryProductStateW(NULL);
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return\n");
+
+ r = MsiEnumFeaturesW(NULL,0,NULL,NULL);
+ ok( r == ERROR_INVALID_PARAMETER,"wrong error\n");
+
+ r = MsiConfigureFeatureW(NULL, NULL, 0);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
+
+ r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", NULL, 0);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error\n");
+
+ r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", "foo", 0);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ r = MsiConfigureFeatureA("{00000000-0000-0000-0000-000000000000}", "foo", INSTALLSTATE_DEFAULT);
+ ok( r == ERROR_UNKNOWN_PRODUCT, "wrong error %d\n", r);
+}
+
+static void test_getcomponentpath(void)
+{
+ INSTALLSTATE r;
+ char buffer[0x100];
+ DWORD sz;
+
+ if(!pMsiGetComponentPathA)
+ return;
+
+ r = pMsiGetComponentPathA( NULL, NULL, NULL, NULL );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "bogus", "bogus", NULL, NULL );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "bogus", "{00000000-0000-0000-000000000000}", NULL, NULL );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ sz = sizeof buffer;
+ buffer[0]=0;
+ r = pMsiGetComponentPathA( "bogus", "{00000000-0000-0000-000000000000}", buffer, &sz );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000000-78E1-11D2-B60F-006097C998E7}",
+ "{00000000-0000-0000-0000-000000000000}", buffer, &sz );
+ ok( r == INSTALLSTATE_UNKNOWN, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000409-78E1-11D2-B60F-006097C998E7}",
+ "{00000000-0000-0000-0000-00000000}", buffer, &sz );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000409-78E1-11D2-B60F-006097C998E7}",
+ "{029E403D-A86A-1D11-5B5B0006799C897E}", buffer, &sz );
+ ok( r == INSTALLSTATE_INVALIDARG, "wrong return value\n");
+
+ r = pMsiGetComponentPathA( "{00000000-78E1-11D2-B60F-006097C9987e}",
+ "{00000000-A68A-11d1-5B5B-0006799C897E}", buffer, &sz );
+ ok( r == INSTALLSTATE_UNKNOWN, "wrong return value\n");
+}
+
+static void test_filehash(void)
+{
+ const char name[] = "msitest.bin";
+ const char data[] = {'a','b','c'};
+ HANDLE handle;
+ UINT r;
+ test_MSIFILEHASHINFO hash;
+ DWORD count = 0;
+
+ if (!pMsiGetFileHashA)
+ return;
+
+ DeleteFile(name);
+
+ memset(&hash, 0, sizeof hash);
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ r = pMsiGetFileHashA(name, 0, NULL);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ memset(&hash, 0, sizeof hash);
+ hash.dwFileHashInfoSize = sizeof hash;
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_FILE_NOT_FOUND, "wrong error %d\n", r);
+
+ handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL);
+ ok(handle != INVALID_HANDLE_VALUE, "failed to create file\n");
+
+ WriteFile(handle, data, sizeof data, &count, NULL);
+ CloseHandle(handle);
+
+ memset(&hash, 0, sizeof hash);
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ memset(&hash, 0, sizeof hash);
+ hash.dwFileHashInfoSize = sizeof hash;
+ r = pMsiGetFileHashA(name, 1, &hash);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong error %d\n", r);
+
+ r = pMsiGetFileHashA(name, 0, &hash);
+ ok( r == ERROR_SUCCESS, "wrong error %d\n", r);
+
+ ok(hash.dwFileHashInfoSize == sizeof hash, "hash size changed\n");
+ ok(hash.dwData[0] == 0x98500190 &&
+ hash.dwData[1] == 0xb04fd23c &&
+ hash.dwData[2] == 0x7d3f96d6 &&
+ hash.dwData[3] == 0x727fe128, "hash of abc incorrect\n");
+
+ DeleteFile(name);
+}
+
+START_TEST(msi)
+{
+ HMODULE hmod = GetModuleHandle("msi.dll");
+ pMsiUseFeatureExA = (fnMsiUseFeatureExA)
+ GetProcAddress(hmod, "MsiUseFeatureExA");
+ pMsiOpenPackageExA = (fnMsiOpenPackageExA)
+ GetProcAddress(hmod, "MsiOpenPackageExA");
+ pMsiOpenPackageExW = (fnMsiOpenPackageExW)
+ GetProcAddress(hmod, "MsiOpenPackageExW");
+ pMsiGetComponentPathA = (fnMsiGetComponentPathA)
+ GetProcAddress(hmod, "MsiGetComponentPathA" );
+ pMsiGetFileHashA = (fnMsiGetFileHashA)
+ GetProcAddress(hmod, "MsiGetFileHashA" );
+
+ test_usefeature();
+ test_null();
+ test_getcomponentpath();
+ test_filehash();
+}
--- /dev/null
+<module name="msi_winetest" type="win32cui" installbase="bin" installname="msi_winetest.exe" allowwarnings="true">
+ <include base="msi_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>cabinet</library>
+ <library>msi</library>
+ <library>ole32</library>
+ <library>advapi32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>db.c</file>
+ <file>format.c</file>
+ <file>install.c</file>
+ <file>msi.c</file>
+ <file>package.c</file>
+ <file>record.c</file>
+ <file>suminfo.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * tests for Microsoft Installer functionality
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static const char msifile[] = "winetest.msi";
+
+static UINT run_query( MSIHANDLE hdb, const char *query )
+{
+ MSIHANDLE hview = 0;
+ UINT r;
+
+ r = MsiDatabaseOpenView(hdb, query, &hview);
+ if( r != ERROR_SUCCESS )
+ return r;
+
+ r = MsiViewExecute(hview, 0);
+ if( r == ERROR_SUCCESS )
+ r = MsiViewClose(hview);
+ MsiCloseHandle(hview);
+ return r;
+}
+
+static UINT create_component_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `Component` ( "
+ "`Component` CHAR(72) NOT NULL, "
+ "`ComponentId` CHAR(38), "
+ "`Directory_` CHAR(72) NOT NULL, "
+ "`Attributes` SHORT NOT NULL, "
+ "`Condition` CHAR(255), "
+ "`KeyPath` CHAR(72) "
+ "PRIMARY KEY `Component`)" );
+}
+
+static UINT create_feature_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `Feature` ( "
+ "`Feature` CHAR(38) NOT NULL, "
+ "`Feature_Parent` CHAR(38), "
+ "`Title` CHAR(64), "
+ "`Description` CHAR(255), "
+ "`Display` SHORT NOT NULL, "
+ "`Level` SHORT NOT NULL, "
+ "`Directory_` CHAR(72), "
+ "`Attributes` SHORT NOT NULL "
+ "PRIMARY KEY `Feature`)" );
+}
+
+static UINT create_feature_components_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `FeatureComponents` ( "
+ "`Feature_` CHAR(38) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL "
+ "PRIMARY KEY `Feature_`, `Component_` )" );
+}
+
+static UINT create_file_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `File` ("
+ "`File` CHAR(72) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL, "
+ "`FileName` CHAR(255) NOT NULL, "
+ "`FileSize` LONG NOT NULL, "
+ "`Version` CHAR(72), "
+ "`Language` CHAR(20), "
+ "`Attributes` SHORT, "
+ "`Sequence` SHORT NOT NULL "
+ "PRIMARY KEY `File`)" );
+}
+
+static UINT create_remove_file_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `RemoveFile` ("
+ "`FileKey` CHAR(72) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL, "
+ "`FileName` CHAR(255) LOCALIZABLE, "
+ "`DirProperty` CHAR(72) NOT NULL, "
+ "`InstallMode` SHORT NOT NULL "
+ "PRIMARY KEY `FileKey`)" );
+}
+
+static UINT create_appsearch_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `AppSearch` ("
+ "`Property` CHAR(72) NOT NULL, "
+ "`Signature_` CHAR(72) NOT NULL "
+ "PRIMARY KEY `Property`, `Signature_`)" );
+}
+
+static UINT create_reglocator_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `RegLocator` ("
+ "`Signature_` CHAR(72) NOT NULL, "
+ "`Root` SHORT NOT NULL, "
+ "`Key` CHAR(255) NOT NULL, "
+ "`Name` CHAR(255), "
+ "`Type` SHORT "
+ "PRIMARY KEY `Signature_`)" );
+}
+
+static UINT create_signature_table( MSIHANDLE hdb )
+{
+ return run_query( hdb,
+ "CREATE TABLE `Signature` ("
+ "`Signature` CHAR(72) NOT NULL, "
+ "`FileName` CHAR(255) NOT NULL, "
+ "`MinVersion` CHAR(20), "
+ "`MaxVersion` CHAR(20), "
+ "`MinSize` LONG, "
+ "`MaxSize` LONG, "
+ "`MinDate` LONG, "
+ "`MaxDate` LONG, "
+ "`Languages` CHAR(255) "
+ "PRIMARY KEY `Signature`)" );
+}
+
+static UINT add_component_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `Component` "
+ "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_feature_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `Feature` (`Feature`, `Feature_Parent`, "
+ "`Title`, `Description`, `Display`, `Level`, `Directory_`, `Attributes`) VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_feature_components_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `FeatureComponents` "
+ "(`Feature_`, `Component_`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_file_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `File` "
+ "(`File`, `Component_`, `FileName`, `FileSize`, `Version`, `Language`, `Attributes`, `Sequence`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_appsearch_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `AppSearch` "
+ "(`Property`, `Signature_`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_reglocator_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `RegLocator` "
+ "(`Signature_`, `Root`, `Key`, `Name`, `Type`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT add_signature_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `Signature` "
+ "(`Signature`, `FileName`, `MinVersion`, `MaxVersion`,"
+ " `MinSize`, `MaxSize`, `MinDate`, `MaxDate`, `Languages`) "
+ "VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static UINT set_summary_info(MSIHANDLE hdb)
+{
+ UINT res;
+ MSIHANDLE suminfo;
+
+ /* build summmary info */
+ res = MsiGetSummaryInformation(hdb, NULL, 7, &suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to open summaryinfo\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,2, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,3, VT_LPSTR, 0,NULL,
+ "Installation Database");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,4, VT_LPSTR, 0,NULL,
+ "Wine Hackers");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,7, VT_LPSTR, 0,NULL,
+ ";1033");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo,9, VT_LPSTR, 0,NULL,
+ "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}");
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 14, VT_I4, 100, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoSetProperty(suminfo, 15, VT_I4, 0, NULL, NULL);
+ ok( res == ERROR_SUCCESS , "Failed to set summary info\n" );
+
+ res = MsiSummaryInfoPersist(suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to make summary info persist\n" );
+
+ res = MsiCloseHandle( suminfo);
+ ok( res == ERROR_SUCCESS , "Failed to close suminfo\n" );
+
+ return res;
+}
+
+
+MSIHANDLE create_package_db(void)
+{
+ MSIHANDLE hdb = 0;
+ UINT res;
+
+ DeleteFile(msifile);
+
+ /* create an empty database */
+ res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
+ ok( res == ERROR_SUCCESS , "Failed to create database\n" );
+ if( res != ERROR_SUCCESS )
+ return hdb;
+
+ res = MsiDatabaseCommit( hdb );
+ ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
+
+ res = set_summary_info(hdb);
+
+ res = run_query( hdb,
+ "CREATE TABLE `Directory` ( "
+ "`Directory` CHAR(255) NOT NULL, "
+ "`Directory_Parent` CHAR(255), "
+ "`DefaultDir` CHAR(255) NOT NULL "
+ "PRIMARY KEY `Directory`)" );
+ ok( res == ERROR_SUCCESS , "Failed to create directory table\n" );
+
+ return hdb;
+}
+
+MSIHANDLE package_from_db(MSIHANDLE hdb)
+{
+ UINT res;
+ CHAR szPackage[10];
+ MSIHANDLE hPackage;
+
+ sprintf(szPackage,"#%li",hdb);
+ res = MsiOpenPackage(szPackage,&hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to open package\n" );
+
+ res = MsiCloseHandle(hdb);
+ ok( res == ERROR_SUCCESS , "Failed to close db handle\n" );
+
+ return hPackage;
+}
+
+static void create_test_file(const CHAR *name)
+{
+ HANDLE file;
+ DWORD written;
+
+ file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
+ WriteFile(file, name, strlen(name), &written, NULL);
+ WriteFile(file, "\n", strlen("\n"), &written, NULL);
+ CloseHandle(file);
+}
+
+static void test_createpackage(void)
+{
+ MSIHANDLE hPackage = 0;
+ UINT res;
+
+ hPackage = package_from_db(create_package_db());
+ ok( hPackage != 0, " Failed to create package\n");
+
+ res = MsiCloseHandle( hPackage);
+ ok( res == ERROR_SUCCESS , "Failed to close package\n" );
+ DeleteFile(msifile);
+}
+
+static void test_getsourcepath_bad( void )
+{
+ static const char str[] = { 0 };
+ char buffer[0x80];
+ DWORD sz;
+ UINT r;
+
+ r = MsiGetSourcePath( -1, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, NULL, buffer, &sz );
+ ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, str, NULL, &sz );
+ ok( r == ERROR_INVALID_HANDLE, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, str, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "return value wrong\n");
+
+ sz = 0;
+ r = MsiGetSourcePath( -1, str, buffer, &sz );
+ ok( r == ERROR_INVALID_HANDLE, "return value wrong\n");
+}
+
+static UINT add_directory_entry( MSIHANDLE hdb, const char *values )
+{
+ char insert[] = "INSERT INTO `Directory` (`Directory`,`Directory_Parent`,`DefaultDir`) VALUES( %s )";
+ char *query;
+ UINT sz, r;
+
+ sz = strlen(values) + sizeof insert;
+ query = HeapAlloc(GetProcessHeap(),0,sz);
+ sprintf(query,insert,values);
+ r = run_query( hdb, query );
+ HeapFree(GetProcessHeap(), 0, query);
+ return r;
+}
+
+static void test_getsourcepath( void )
+{
+ static const char str[] = { 0 };
+ char buffer[0x80];
+ DWORD sz;
+ UINT r;
+ MSIHANDLE hpkg, hdb;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ sz = 0;
+ buffer[0] = 'x';
+ r = MsiGetSourcePath( hpkg, str, buffer, &sz );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+ ok( buffer[0] == 'x', "buffer modified\n");
+
+ sz = 1;
+ buffer[0] = 'x';
+ r = MsiGetSourcePath( hpkg, str, buffer, &sz );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+ ok( buffer[0] == 'x', "buffer modified\n");
+
+ MsiCloseHandle( hpkg );
+
+
+ /* another test but try create a directory this time */
+ hdb = create_package_db();
+ ok( hdb, "failed to create database\n");
+
+ r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'");
+ ok( r == S_OK, "failed\n");
+
+ hpkg = package_from_db(hdb);
+ ok( hpkg, "failed to create package\n");
+
+ sz = sizeof buffer -1;
+ strcpy(buffer,"x bad");
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+
+ r = MsiDoAction( hpkg, "CostInitialize");
+ ok( r == ERROR_SUCCESS, "cost init failed\n");
+ r = MsiDoAction( hpkg, "CostFinalize");
+ ok( r == ERROR_SUCCESS, "cost finalize failed\n");
+
+ todo_wine {
+ sz = sizeof buffer -1;
+ buffer[0] = 'x';
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "return value wrong\n");
+ ok( sz == strlen(buffer), "returned length wrong\n");
+
+ sz = 0;
+ strcpy(buffer,"x bad");
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "return value wrong\n");
+ }
+ ok( buffer[0] == 'x', "buffer modified\n");
+
+ todo_wine {
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", NULL, NULL );
+ ok( r == ERROR_SUCCESS, "return value wrong\n");
+ }
+
+ r = MsiGetSourcePath( hpkg, "TARGETDIR ", NULL, NULL );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+
+ r = MsiGetSourcePath( hpkg, "targetdir", NULL, NULL );
+ ok( r == ERROR_DIRECTORY, "return value wrong\n");
+
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", buffer, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "return value wrong\n");
+
+ todo_wine {
+ r = MsiGetSourcePath( hpkg, "TARGETDIR", NULL, &sz );
+ ok( r == ERROR_SUCCESS, "return value wrong\n");
+ }
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void test_doaction( void )
+{
+ MSIHANDLE hpkg;
+ UINT r;
+
+ r = MsiDoAction( -1, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiDoAction(hpkg, NULL);
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiDoAction(0, "boo");
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiDoAction(hpkg, "boo");
+ ok( r == ERROR_FUNCTION_NOT_CALLED, "wrong return val\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void test_gettargetpath_bad(void)
+{
+ char buffer[0x80];
+ MSIHANDLE hpkg;
+ DWORD sz;
+ UINT r;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiGetTargetPath( 0, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetTargetPath( 0, NULL, NULL, &sz );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetTargetPath( 0, "boo", NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiGetTargetPath( 0, "boo", NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiGetTargetPath( hpkg, "boo", NULL, NULL );
+ ok( r == ERROR_DIRECTORY, "wrong return val\n");
+
+ r = MsiGetTargetPath( hpkg, "boo", buffer, NULL );
+ ok( r == ERROR_DIRECTORY, "wrong return val\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void query_file_path(MSIHANDLE hpkg, LPCSTR file, LPSTR buff)
+{
+ UINT r;
+ DWORD size;
+ MSIHANDLE rec;
+
+ rec = MsiCreateRecord( 1 );
+ ok(rec, "MsiCreate record failed\n");
+
+ r = MsiRecordSetString( rec, 0, file );
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r );
+
+ size = MAX_PATH;
+ r = MsiFormatRecord( hpkg, rec, buff, &size );
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r );
+
+ MsiCloseHandle( rec );
+}
+
+static void test_settargetpath(void)
+{
+ char tempdir[MAX_PATH+8], buffer[MAX_PATH];
+ DWORD sz;
+ MSIHANDLE hpkg;
+ UINT r;
+ MSIHANDLE hdb;
+
+ hdb = create_package_db();
+ ok ( hdb, "failed to create package database\n" );
+
+ r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'" );
+ ok( r == S_OK, "failed to add directory entry: %d\n" , r );
+
+ r = run_query( hdb, /* these tables required by Windows Installer for MsiSetTargetPath */
+ "CREATE TABLE `Component` ( "
+ "`Component` CHAR(72) NOT NULL, "
+ "`ComponentId` CHAR(38), "
+ "`Directory_` CHAR(72) NOT NULL, "
+ "`Attributes` SHORT NOT NULL, "
+ "`Condition` CHAR(255), "
+ "`KeyPath` CHAR(72) "
+ "PRIMARY KEY `Component`)" );
+ ok( r == S_OK, "cannot create Component table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `Component` "
+ "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
+ "VALUES( 'WinWorkAround', '{83e2694d-0864-4124-9323-6d37630912a1}', 'TARGETDIR', 8, '', 'FL_dummycomponent')" );
+ ok( r == S_OK, "cannot add dummy component: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `Component` "
+ "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
+ "VALUES( 'TestComp', '{A3FB59C8-C293-4F7E-B8C5-F0E1D8EEE4E5}', 'TestDir', 0, '', 'TestFile')" );
+ ok( r == S_OK, "cannot add test component: %d\n", r );
+
+ r = run_query( hdb,
+ "CREATE TABLE `Feature` ( "
+ "`Feature` CHAR(38) NOT NULL, "
+ "`Feature_Parent` CHAR(38), "
+ "`Title` CHAR(64), "
+ "`Description` CHAR(255), "
+ "`Display` SHORT NOT NULL, "
+ "`Level` SHORT NOT NULL, "
+ "`Directory_` CHAR(72), "
+ "`Attributes` SHORT NOT NULL "
+ "PRIMARY KEY `Feature`)" );
+ ok( r == S_OK, "cannot create Feature table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `Feature` "
+ "(`Feature`, `Feature_Parent`, `Display`, `Level`, `Attributes`) "
+ "VALUES( 'TestFeature', '', 0, 1, 0 )" );
+ ok( r == ERROR_SUCCESS, "cannot add TestFeature to Feature table: %d\n", r );
+
+ r = run_query( hdb,
+ "CREATE TABLE `FeatureComponents` ( "
+ "`Feature_` CHAR(38) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL "
+ "PRIMARY KEY `Feature_` )" );
+ ok( r == S_OK, "cannot create FeatureComponents table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `FeatureComponents` "
+ "(`Feature_`, `Component_`) "
+ "VALUES( 'TestFeature', 'TestComp' )" );
+ ok( r == S_OK, "cannot insert component into FeatureComponents table: %d\n", r );
+
+ add_directory_entry( hdb, "'TestParent', 'TARGETDIR', 'TestParent'" );
+ add_directory_entry( hdb, "'TestDir', 'TestParent', 'TestDir'" );
+
+ r = run_query( hdb,
+ "CREATE TABLE `File` ("
+ "`File` CHAR(72) NOT NULL, "
+ "`Component_` CHAR(72) NOT NULL, "
+ "`FileName` CHAR(255) NOT NULL, "
+ "`FileSize` LONG NOT NULL, "
+ "`Version` CHAR(72), "
+ "`Language` CHAR(20), "
+ "`Attributes` SHORT, "
+ "`Sequence` SHORT NOT NULL "
+ "PRIMARY KEY `File`)" );
+ ok( r == S_OK, "cannot create File table: %d\n", r );
+
+ r = run_query( hdb,
+ "INSERT INTO `File` "
+ "(`File`, `Component_`, `FileName`, `FileSize`, `Version`, `Language`, `Attributes`, `Sequence`) "
+ "VALUES( 'TestFile', 'TestComp', 'testfile.txt', 0, '', '1033', 8192, 1 )" );
+ ok( r == S_OK, "cannot add file to the File table: %d\n", r );
+
+ hpkg = package_from_db( hdb );
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiDoAction( hpkg, "CostInitialize");
+ ok( r == ERROR_SUCCESS, "cost init failed\n");
+
+ r = MsiDoAction( hpkg, "FileCost");
+ ok( r == ERROR_SUCCESS, "file cost failed\n");
+
+ r = MsiDoAction( hpkg, "CostFinalize");
+ ok( r == ERROR_SUCCESS, "cost finalize failed\n");
+
+ r = MsiSetTargetPath( 0, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiSetTargetPath( 0, "boo", "C:\\bogusx" );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiSetTargetPath( hpkg, "boo", NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiSetTargetPath( hpkg, "boo", "c:\\bogusx" );
+ ok( r == ERROR_DIRECTORY, "wrong return val\n");
+
+ sz = sizeof tempdir - 1;
+ r = MsiGetTargetPath( hpkg, "TARGETDIR", tempdir, &sz );
+ if ( r == S_OK )
+ {
+ if ( GetTempFileName( tempdir, "_wt", 0, buffer ) )
+ {
+ sprintf( tempdir, "%s\\subdir", buffer );
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath on file returned %d\n", r );
+
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath on 'subdir' of file returned %d\n", r );
+
+ DeleteFile( buffer );
+
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", buffer );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r );
+
+ r = GetFileAttributes( buffer );
+ ok ( r == INVALID_FILE_ATTRIBUTES, "file/directory exists after MsiSetTargetPath. Attributes: %08X\n", r );
+
+ r = MsiSetTargetPath( hpkg, "TARGETDIR", tempdir );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath on subsubdir returned %d\n", r );
+ } else {
+ trace("GetTempFileName failed, cannot do some tests\n");
+ }
+ } else {
+ trace( "MsiGetTargetPath failed: %d\n", r );
+ }
+
+ r = MsiSetTargetPath( hpkg, "TestParent", "C:\\one\\two" );
+ ok( r == ERROR_SUCCESS, "MsiSetTargetPath returned %d\n", r );
+
+ query_file_path( hpkg, "[#TestFile]", buffer );
+ ok( !lstrcmp(buffer, "C:\\one\\two\\TestDir\\testfile.txt"),
+ "Expected C:\\one\\two\\TestDir\\testfile.txt, got %s\n", buffer );
+
+ MsiCloseHandle( hpkg );
+}
+
+static void test_condition(void)
+{
+ MSICONDITION r;
+ MSIHANDLE hpkg;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiEvaluateCondition(0, NULL);
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, NULL);
+ ok( r == MSICONDITION_NONE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "");
+ ok( r == MSICONDITION_NONE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 = 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 <> 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 = 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 > 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~> 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 > 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~> 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 >= 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~>= 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 >= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~>= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 < 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~< 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 < 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~< 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 <= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 ~<= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 <= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 ~<= 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 >=");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " ");
+ ok( r == MSICONDITION_NONE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "LicView <> \"1\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "LicView <> \"0\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "LicView <> LicView");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not LicView");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not \"A\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "~not \"A\"");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"0\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 and 2");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0 and 3");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0 and 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "not 0 or 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(0)");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(((((1))))))");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(((((1)))))");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" < \"B\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" > \"B\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"1\" > \"12\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"100\" < \"21\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 < > 0");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "(1<<1) == 2");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" = \"a\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" ~ = \"a\" ");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" ~= \"a\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" ~= 1 ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " \"A\" = 1 ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 1 ~= 1 ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 1 ~= \"1\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 1 = \"1\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 0 = \"1\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 0 < \"100\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, " 100 > \"0\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 XOR 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMP 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMP 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 IMP 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 EQV 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 EQV 1");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMP 1 OR 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 IMPL 1");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" >< \"S\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" ~>< \"s\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" ~>< \"\" ");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "\"ASFD\" ~>< \"sss\" ");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "mm", "5" );
+
+ r = MsiEvaluateCondition(hpkg, "mm = 5");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm < 6");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm <= 5");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm > 4");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm < 12");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "mm = \"5\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 = \"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 AND \"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 AND \"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "1 AND \"1\"");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "3 >< 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "3 >< 4");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 0 AND 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 0 AND 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 1 OR 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 AND 1 OR 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "0 AND 0 OR 1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 0 AND 1 OR 0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "_1 = _1");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "( 1 AND 1 ) = 2");
+ ok( r == MSICONDITION_ERROR, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT ( 1 AND 1 )");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT A AND (BBBBBBBBBB=2 OR CCC=1) AND Ddddddddd");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "Installed<>\"\"");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "NOT 1 AND 0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael<>0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael<0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael>0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael>=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael<=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ r = MsiEvaluateCondition(hpkg, "bandalmael~<>0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "asdf" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0asdf" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0 " );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "-0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0000000000000" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "--0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0x00" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "-" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "+0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "bandalmael", "0.0" );
+ r = MsiEvaluateCondition(hpkg, "bandalmael=0");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+ r = MsiEvaluateCondition(hpkg, "bandalmael<>0");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hi");
+ MsiSetProperty(hpkg, "two", "hithere");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hithere");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hello");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hellohithere");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hi");
+ MsiSetProperty(hpkg, "two", "");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "");
+ MsiSetProperty(hpkg, "two", "");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "1234");
+ MsiSetProperty(hpkg, "two", "1");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "one 1234");
+ MsiSetProperty(hpkg, "two", "1");
+ r = MsiEvaluateCondition(hpkg, "one >< two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hithere");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hi");
+ MsiSetProperty(hpkg, "two", "hithere");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hi");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "abcdhithere");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hithere");
+ MsiSetProperty(hpkg, "two", "");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "");
+ MsiSetProperty(hpkg, "two", "");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "1234");
+ MsiSetProperty(hpkg, "two", "1");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "1234 one");
+ MsiSetProperty(hpkg, "two", "1");
+ r = MsiEvaluateCondition(hpkg, "one << two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hithere");
+ MsiSetProperty(hpkg, "two", "there");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "hithere");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "there");
+ MsiSetProperty(hpkg, "two", "hithere");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "there");
+ MsiSetProperty(hpkg, "two", "there");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "abcdhithere");
+ MsiSetProperty(hpkg, "two", "hi");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "");
+ MsiSetProperty(hpkg, "two", "there");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "there");
+ MsiSetProperty(hpkg, "two", "");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "");
+ MsiSetProperty(hpkg, "two", "");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "1234");
+ MsiSetProperty(hpkg, "two", "4");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_FALSE, "wrong return val\n");
+
+ MsiSetProperty(hpkg, "one", "one 1234");
+ MsiSetProperty(hpkg, "two", "4");
+ r = MsiEvaluateCondition(hpkg, "one >> two");
+ ok( r == MSICONDITION_TRUE, "wrong return val\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static BOOL check_prop_empty( MSIHANDLE hpkg, const char * prop)
+{
+ UINT r;
+ DWORD sz;
+ char buffer[2];
+
+ sz = sizeof buffer;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, prop, buffer, &sz );
+ return r == ERROR_SUCCESS && buffer[0] == 0 && sz == 0;
+}
+
+static void test_props(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+ DWORD sz;
+ char buffer[0x100];
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ /* test invalid values */
+ r = MsiGetProperty( 0, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetProperty( hpkg, NULL, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiGetProperty( hpkg, "boo", NULL, NULL );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ r = MsiGetProperty( hpkg, "boo", buffer, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ /* test retrieving an empty/nonexistent property */
+ sz = sizeof buffer;
+ r = MsiGetProperty( hpkg, "boo", NULL, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( sz == 0, "wrong size returned\n");
+
+ check_prop_empty( hpkg, "boo");
+ sz = 0;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "wrong return val\n");
+ ok( !strcmp(buffer,"x"), "buffer was changed\n");
+ ok( sz == 0, "wrong size returned\n");
+
+ sz = 1;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( buffer[0] == 0, "buffer was not changed\n");
+ ok( sz == 0, "wrong size returned\n");
+
+ /* set the property to something */
+ r = MsiSetProperty( 0, NULL, NULL );
+ ok( r == ERROR_INVALID_HANDLE, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, NULL, NULL );
+ ok( r == ERROR_INVALID_PARAMETER, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, "", NULL );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ /* try set and get some illegal property identifiers */
+ r = MsiSetProperty( hpkg, "", "asdf" );
+ ok( r == ERROR_FUNCTION_FAILED, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, "=", "asdf" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, " ", "asdf" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ r = MsiSetProperty( hpkg, "'", "asdf" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ sz = sizeof buffer;
+ buffer[0]=0;
+ r = MsiGetProperty( hpkg, "'", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( !strcmp(buffer,"asdf"), "buffer was not changed\n");
+
+ /* set empty values */
+ r = MsiSetProperty( hpkg, "boo", NULL );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( check_prop_empty( hpkg, "boo"), "prop wasn't empty\n");
+
+ r = MsiSetProperty( hpkg, "boo", "" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( check_prop_empty( hpkg, "boo"), "prop wasn't empty\n");
+
+ /* set a non-empty value */
+ r = MsiSetProperty( hpkg, "boo", "xyz" );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+
+ sz = 1;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "wrong return val\n");
+ ok( buffer[0] == 0, "buffer was not changed\n");
+ ok( sz == 3, "wrong size returned\n");
+
+ sz = 4;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_SUCCESS, "wrong return val\n");
+ ok( !strcmp(buffer,"xyz"), "buffer was not changed\n");
+ ok( sz == 3, "wrong size returned\n");
+
+ sz = 3;
+ strcpy(buffer,"x");
+ r = MsiGetProperty( hpkg, "boo", buffer, &sz );
+ ok( r == ERROR_MORE_DATA, "wrong return val\n");
+ ok( !strcmp(buffer,"xy"), "buffer was not changed\n");
+ ok( sz == 3, "wrong size returned\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
+{
+ MSIHANDLE htab = 0;
+ UINT res;
+
+ res = MsiDatabaseOpenView( hdb, szQuery, &htab );
+ if( res == ERROR_SUCCESS )
+ {
+ UINT r;
+
+ r = MsiViewExecute( htab, hrec );
+ if( r != ERROR_SUCCESS )
+ {
+ res = r;
+ fprintf(stderr,"MsiViewExecute failed %08x\n", res);
+ }
+
+ r = MsiViewClose( htab );
+ if( r != ERROR_SUCCESS )
+ res = r;
+
+ r = MsiCloseHandle( htab );
+ if( r != ERROR_SUCCESS )
+ res = r;
+ }
+ return res;
+}
+
+static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
+{
+ return try_query_param( hdb, szQuery, 0 );
+}
+
+static void test_msipackage(void)
+{
+ MSIHANDLE hdb = 0, hpack = 100;
+ UINT r;
+ const char *query;
+ char name[10];
+
+ DeleteFile(msifile);
+
+ todo_wine {
+ name[0] = 0;
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_SUCCESS, "failed to open package with no name\n");
+ r = MsiCloseHandle(hpack);
+ ok(r == ERROR_SUCCESS, "failed to close package\n");
+ }
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ name[0]='#';
+ name[1]=0;
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_INVALID_HANDLE, "MsiOpenPackage returned wrong code\n");
+
+ todo_wine {
+ /* now try again with our empty database */
+ sprintf(name, "#%ld", hdb);
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_INSTALL_PACKAGE_INVALID, "MsiOpenPackage returned wrong code\n");
+ if (!r) MsiCloseHandle(hpack);
+ }
+
+ /* create a table */
+ query = "CREATE TABLE `Property` ( "
+ "`Property` CHAR(72), `Value` CHAR(0) "
+ "PRIMARY KEY `Property`)";
+ r = try_query(hdb, query);
+ ok(r == ERROR_SUCCESS, "failed to create Properties table\n");
+
+ todo_wine {
+ query = "CREATE TABLE `InstallExecuteSequence` ("
+ "`Action` CHAR(72), `Condition` CHAR(0), `Sequence` INTEGER "
+ "PRIMARY KEY `Action`)";
+ r = try_query(hdb, query);
+ ok(r == ERROR_SUCCESS, "failed to create InstallExecuteSequence table\n");
+
+ sprintf(name, "#%ld", hdb);
+ r = MsiOpenPackage(name, &hpack);
+ ok(r == ERROR_INSTALL_PACKAGE_INVALID, "MsiOpenPackage returned wrong code\n");
+ if (!r) MsiCloseHandle(hpack);
+ }
+
+ r = MsiCloseHandle(hdb);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle(database) failed\n");
+ DeleteFile(msifile);
+}
+
+static void test_formatrecord2(void)
+{
+ MSIHANDLE hpkg, hrec ;
+ char buffer[0x100];
+ DWORD sz;
+ UINT r;
+
+ hpkg = package_from_db(create_package_db());
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiSetProperty(hpkg, "Manufacturer", " " );
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+
+ hrec = MsiCreateRecord(2);
+ ok(hrec, "create record failed\n");
+
+ r = MsiRecordSetString( hrec, 0, "[ProgramFilesFolder][Manufacturer]\\asdf");
+ ok( r == ERROR_SUCCESS, "format record failed\n");
+
+ buffer[0] = 0;
+ sz = sizeof buffer;
+ r = MsiFormatRecord( hpkg, hrec, buffer, &sz );
+
+ r = MsiRecordSetString(hrec, 0, "[foo][1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "x[~]x");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"x"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[foo.$%}][1]");
+ r = MsiRecordSetString(hrec, 1, "hoo");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"hoo"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[\\[]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 1, "size wrong\n");
+ ok( 0 == strcmp(buffer,"["), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ SetEnvironmentVariable("FOO", "BAR");
+ r = MsiRecordSetString(hrec, 0, "[%FOO]");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"BAR"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ r = MsiRecordSetString(hrec, 0, "[[1]]");
+ r = MsiRecordSetString(hrec, 1, "%FOO");
+ sz = sizeof buffer;
+ r = MsiFormatRecord(hpkg, hrec, buffer, &sz);
+ ok( sz == 3, "size wrong\n");
+ ok( 0 == strcmp(buffer,"BAR"), "wrong output %s\n",buffer);
+ ok( r == ERROR_SUCCESS, "format failed\n");
+
+ MsiCloseHandle( hrec );
+ MsiCloseHandle( hpkg );
+ DeleteFile(msifile);
+}
+
+static void test_states(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+ MSIHANDLE hdb;
+ INSTALLSTATE state, action;
+
+ hdb = create_package_db();
+ ok ( hdb, "failed to create package database\n" );
+
+ r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'");
+ ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r );
+
+ r = create_feature_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Feature table: %d\n", r );
+
+ r = create_component_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal */
+ r = add_feature_entry( hdb, "'one', '', '', '', 2, 1, '', 0" );
+ ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesLocalOnly */
+ r = add_component_entry( hdb, "'alpha', '{467EC132-739D-4784-A37B-677AA43DBC94}', 'TARGETDIR', 0, '', 'alpha_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesSourceOnly */
+ r = add_component_entry( hdb, "'beta', '{2C1F189C-24A6-4C34-B26B-994A6C026506}', 'TARGETDIR', 1, '', 'beta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesOptional */
+ r = add_component_entry( hdb, "'gamma', '{C271E2A4-DE2E-4F70-86D1-6984AF7DE2CA}', 'TARGETDIR', 2, '', 'gamma_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorLocal:msidbComponentAttributesSharedDllRefCount */
+ r = add_component_entry( hdb, "'theta', '{4EB3129D-81A8-48D5-9801-75600FED3DD9}', 'TARGETDIR', 8, '', 'theta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource */
+ r = add_feature_entry( hdb, "'two', '', '', '', 2, 1, '', 1" );
+ ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesLocalOnly */
+ r = add_component_entry( hdb, "'delta', '{938FD4F2-C648-4259-A03C-7AA3B45643F3}', 'TARGETDIR', 0, '', 'delta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesSourceOnly */
+ r = add_component_entry( hdb, "'epsilon', '{D59713B6-C11D-47F2-A395-1E5321781190}', 'TARGETDIR', 1, '', 'epsilon_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesOptional */
+ r = add_component_entry( hdb, "'zeta', '{377D33AB-2FAA-42B9-A629-0C0DAE9B9C7A}', 'TARGETDIR', 2, '', 'zeta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesSharedDllRefCount */
+ r = add_component_entry( hdb, "'iota', '{5D36F871-B5ED-4801-9E0F-C46B9E5C9669}', 'TARGETDIR', 8, '', 'iota_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource */
+ r = add_feature_entry( hdb, "'three', '', '', '', 2, 1, '', 1" );
+ ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r );
+
+ /* msidbFeatureAttributesFavorSource:msidbComponentAttributesSourceOnly */
+ r = add_component_entry( hdb, "'eta', '{DD89003F-0DD4-41B8-81C0-3411A7DA2695}', 'TARGETDIR', 1, '', 'eta_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = create_feature_components_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'alpha'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'beta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'gamma'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'theta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'two', 'delta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'two', 'epsilon'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'two', 'zeta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'two', 'iota'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'three', 'eta'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = create_file_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create File table: %d\n", r );
+
+ r = add_file_entry( hdb, "'alpha_file', 'alpha', 'alpha.txt', 100, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'beta_file', 'beta', 'beta.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'gamma_file', 'gamma', 'gamma.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'theta_file', 'theta', 'theta.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'delta_file', 'delta', 'delta.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'epsilon_file', 'epsilon', 'epsilon.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'zeta_file', 'zeta', 'zeta.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'iota_file', 'iota', 'iota.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ /* compressed file */
+ r = add_file_entry( hdb, "'eta_file', 'eta', 'eta.txt', 0, '', '1033', 16384, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ hpkg = package_from_db( hdb );
+ ok( hpkg, "failed to create package\n");
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_UNKNOWN_FEATURE, "Expected ERROR_UNKNOWN_FEATURE, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "theta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "iota", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_UNKNOWN_COMPONENT, "Expected ERROR_UNKNOWN_COMPONENT, got %d\n", r );
+ ok( state == 0xdeadbee, "Expected 0xdeadbee, got %d\n", state);
+ ok( action == 0xdeadbee, "Expected 0xdeadbee, got %d\n", action);
+
+ r = MsiDoAction( hpkg, "CostInitialize");
+ ok( r == ERROR_SUCCESS, "cost init failed\n");
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "theta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "iota", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ r = MsiDoAction( hpkg, "FileCost");
+ ok( r == ERROR_SUCCESS, "file cost failed\n");
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ }
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "theta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "iota", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ todo_wine
+ {
+ ok( state == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", state);
+ ok( action == INSTALLSTATE_UNKNOWN, "Expected INSTALLSTATE_UNKNOWN, got %d\n", action);
+ }
+
+ r = MsiDoAction( hpkg, "CostFinalize");
+ ok( r == ERROR_SUCCESS, "cost finalize failed: %d\n", r);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "one", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "two", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetFeatureState(hpkg, "three", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "alpha", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "beta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "gamma", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "theta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "delta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "epsilon", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "zeta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_SOURCE, "Expected INSTALLSTATE_SOURCE, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "iota", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ state = 0xdeadbee;
+ action = 0xdeadbee;
+ r = MsiGetComponentState(hpkg, "eta", &state, &action);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r );
+ ok( state == INSTALLSTATE_ABSENT, "Expected INSTALLSTATE_ABSENT, got %d\n", state);
+ ok( action == INSTALLSTATE_LOCAL, "Expected INSTALLSTATE_LOCAL, got %d\n", action);
+
+ MsiCloseHandle( hpkg );
+}
+
+static void test_getproperty(void)
+{
+ MSIHANDLE hPackage = 0;
+ char prop[100];
+ static CHAR empty[] = "";
+ DWORD size;
+ UINT r;
+
+ hPackage = package_from_db(create_package_db());
+ ok( hPackage != 0, " Failed to create package\n");
+
+ /* set the property */
+ r = MsiSetProperty(hPackage, "Name", "Value");
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+
+ /* retrieve the size, NULL pointer */
+ size = 0;
+ r = MsiGetProperty(hPackage, "Name", NULL, &size);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ ok( size == 5, "Expected 5, got %ld\n", size);
+
+ /* retrieve the size, empty string */
+ size = 0;
+ r = MsiGetProperty(hPackage, "Name", empty, &size);
+ ok( r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r);
+ ok( size == 5, "Expected 5, got %ld\n", size);
+
+ /* don't change size */
+ r = MsiGetProperty(hPackage, "Name", prop, &size);
+ ok( r == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", r);
+ ok( size == 5, "Expected 5, got %ld\n", size);
+ ok( !lstrcmp(prop, "Valu"), "Expected Valu, got %s\n", prop);
+
+ /* increase the size by 1 */
+ size++;
+ r = MsiGetProperty(hPackage, "Name", prop, &size);
+ ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+ ok( size == 5, "Expected 5, got %ld\n", size);
+ ok( !lstrcmp(prop, "Value"), "Expected Value, got %s\n", prop);
+
+ r = MsiCloseHandle( hPackage);
+ ok( r == ERROR_SUCCESS , "Failed to close package\n" );
+ DeleteFile(msifile);
+}
+
+static void test_removefiles(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+ MSIHANDLE hdb;
+ char CURR_DIR[MAX_PATH];
+
+ GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+
+ hdb = create_package_db();
+ ok ( hdb, "failed to create package database\n" );
+
+ r = add_directory_entry( hdb, "'TARGETDIR', '', 'SourceDir'");
+ ok( r == ERROR_SUCCESS, "cannot add directory: %d\n", r );
+
+ r = create_feature_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Feature table: %d\n", r );
+
+ r = create_component_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r );
+
+ r = add_feature_entry( hdb, "'one', '', '', '', 2, 1, '', 0" );
+ ok( r == ERROR_SUCCESS, "cannot add feature: %d\n", r );
+
+ r = add_component_entry( hdb, "'hydrogen', '', 'TARGETDIR', 0, '', 'hydrogen_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = add_component_entry( hdb, "'helium', '', 'TARGETDIR', 0, '', 'helium_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = add_component_entry( hdb, "'lithium', '', 'TARGETDIR', 0, '', 'lithium_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = add_component_entry( hdb, "'beryllium', '', 'TARGETDIR', 0, '', 'beryllium_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = add_component_entry( hdb, "'boron', '', 'TARGETDIR', 0, '', 'boron_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = add_component_entry( hdb, "'carbon', '', 'TARGETDIR', 0, '', 'carbon_file'" );
+ ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
+
+ r = create_feature_components_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'hydrogen'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'helium'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'lithium'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'beryllium'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'boron'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = add_feature_components_entry( hdb, "'one', 'carbon'" );
+ ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
+
+ r = create_file_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create File table: %d\n", r );
+
+ r = add_file_entry( hdb, "'hydrogen_file', 'hydrogen', 'hydrogen.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'helium_file', 'helium', 'helium.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'lithium_file', 'lithium', 'lithium.txt', 0, '', '1033', 8192, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'beryllium_file', 'beryllium', 'beryllium.txt', 0, '', '1033', 16384, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'boron_file', 'boron', 'boron.txt', 0, '', '1033', 16384, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = add_file_entry( hdb, "'carbon_file', 'carbon', 'carbon.txt', 0, '', '1033', 16384, 1" );
+ ok( r == ERROR_SUCCESS, "cannot add file: %d\n", r);
+
+ r = create_remove_file_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Remove File table: %d\n", r);
+
+ hpkg = package_from_db( hdb );
+ ok( hpkg, "failed to create package\n");
+
+ create_test_file( "hydrogen.txt" );
+ create_test_file( "helium.txt" );
+ create_test_file( "lithium.txt" );
+ create_test_file( "beryllium.txt" );
+ create_test_file( "boron.txt" );
+ create_test_file( "carbon.txt" );
+
+ r = MsiSetProperty( hpkg, "TARGETDIR", CURR_DIR );
+ ok( r == ERROR_SUCCESS, "set property failed\n");
+
+ r = MsiDoAction( hpkg, "CostInitialize");
+ ok( r == ERROR_SUCCESS, "cost init failed\n");
+
+ r = MsiDoAction( hpkg, "FileCost");
+ ok( r == ERROR_SUCCESS, "cost finalize failed\n");
+
+ r = MsiDoAction( hpkg, "CostFinalize");
+ ok( r == ERROR_SUCCESS, "cost finalize failed\n");
+
+ r = MsiDoAction( hpkg, "InstallValidate");
+ ok( r == ERROR_SUCCESS, "cost finalize failed\n");
+
+ r = MsiSetComponentState( hpkg, "hydrogen", INSTALLSTATE_ABSENT );
+ ok( r == ERROR_SUCCESS, "failed to set component state: %d\n", r);
+
+ r = MsiSetComponentState( hpkg, "helium", INSTALLSTATE_LOCAL );
+ ok( r == ERROR_SUCCESS, "failed to set component state: %d\n", r);
+
+ r = MsiSetComponentState( hpkg, "lithium", INSTALLSTATE_SOURCE );
+ ok( r == ERROR_SUCCESS, "failed to set component state: %d\n", r);
+
+ r = MsiSetComponentState( hpkg, "beryllium", INSTALLSTATE_ABSENT );
+ ok( r == ERROR_SUCCESS, "failed to set component state: %d\n", r);
+
+ r = MsiSetComponentState( hpkg, "boron", INSTALLSTATE_LOCAL );
+ ok( r == ERROR_SUCCESS, "failed to set component state: %d\n", r);
+
+ r = MsiSetComponentState( hpkg, "carbon", INSTALLSTATE_SOURCE );
+ ok( r == ERROR_SUCCESS, "failed to set component state: %d\n", r);
+
+ r = MsiDoAction( hpkg, "RemoveFiles");
+ ok( r == ERROR_SUCCESS, "remove files failed\n");
+
+ ok(DeleteFileA("hydrogen.txt"), "Expected hydrogen.txt to exist\n");
+ ok(DeleteFileA("lithium.txt"), "Expected lithium.txt to exist\n");
+ ok(DeleteFileA("beryllium.txt"), "Expected beryllium.txt to exist\n");
+ ok(DeleteFileA("carbon.txt"), "Expected carbon.txt to exist\n");
+ ok(DeleteFileA("helium.txt"), "Expected helium.txt to exist\n");
+ ok(DeleteFileA("boron.txt"), "Expected boron.txt to exist\n");
+
+ MsiCloseHandle( hpkg );
+ DeleteFileA(msifile);
+}
+
+static void test_appsearch(void)
+{
+ MSIHANDLE hpkg;
+ UINT r;
+ MSIHANDLE hdb;
+ CHAR prop[MAX_PATH];
+ DWORD size = MAX_PATH;
+
+ hdb = create_package_db();
+ ok ( hdb, "failed to create package database\n" );
+
+ r = create_appsearch_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create AppSearch table: %d\n", r );
+
+ r = add_appsearch_entry( hdb, "'WEBBROWSERPROG', 'NewSignature1'" );
+ ok( r == ERROR_SUCCESS, "cannot add entry: %d\n", r );
+
+ r = create_reglocator_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create RegLocator table: %d\n", r );
+
+ r = add_reglocator_entry( hdb, "'NewSignature1', 0, 'htmlfile\\shell\\open\\command', '', 1" );
+ ok( r == ERROR_SUCCESS, "cannot create RegLocator table: %d\n", r );
+
+ r = create_signature_table( hdb );
+ ok( r == ERROR_SUCCESS, "cannot create Signature table: %d\n", r );
+
+ r = add_signature_entry( hdb, "'NewSignature1', 'FileName', '', '', '', '', '', '', ''" );
+ ok( r == ERROR_SUCCESS, "cannot create Signature table: %d\n", r );
+
+ hpkg = package_from_db( hdb );
+ ok( hpkg, "failed to create package\n");
+
+ r = MsiDoAction( hpkg, "AppSearch" );
+ ok( r == ERROR_SUCCESS, "AppSearch failed: %d\n", r);
+
+ r = MsiGetPropertyA( hpkg, "WEBBROWSERPROG", prop, &size );
+ ok( r == ERROR_SUCCESS, "get property failed: %d\n", r);
+ todo_wine
+ {
+ ok( lstrlenA(prop) != 0, "Expected non-zero length\n");
+ }
+
+ MsiCloseHandle( hpkg );
+ DeleteFileA(msifile);
+}
+
+START_TEST(package)
+{
+ test_createpackage();
+ test_getsourcepath_bad();
+ test_getsourcepath();
+ test_doaction();
+ test_gettargetpath_bad();
+ test_settargetpath();
+ test_props();
+ test_condition();
+ test_msipackage();
+ test_formatrecord2();
+ test_states();
+ test_getproperty();
+ test_removefiles();
+ test_appsearch();
+}
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ *
+ * A test program for MSI records
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+static BOOL create_temp_file(char *name)
+{
+ UINT r;
+ unsigned char buffer[26], i;
+ DWORD sz;
+ HANDLE handle;
+
+ r = GetTempFileName(".", "msitest",0,name);
+ if(!r)
+ return r;
+ handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE,
+ 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if(handle==INVALID_HANDLE_VALUE)
+ return 0;
+ for(i=0; i<26; i++)
+ buffer[i]=i+'a';
+ r = WriteFile(handle,buffer,sizeof buffer,&sz,NULL);
+ CloseHandle(handle);
+ return r;
+}
+
+static void test_msirecord(void)
+{
+ DWORD r, sz;
+ INT i;
+ MSIHANDLE h;
+ char buf[10];
+ WCHAR bufW[10];
+ const char str[] = "hello";
+ const WCHAR strW[] = { 'h','e','l','l','o',0};
+ char filename[MAX_PATH];
+
+ /* check behaviour with an invalid record */
+ r = MsiRecordGetFieldCount(0);
+ ok(r==-1, "field count for invalid record not -1\n");
+ SetLastError(0);
+ r = MsiRecordIsNull(0, 0);
+ ok(r==0, "invalid handle not considered to be non-null...\n");
+ ok(GetLastError()==0, "MsiRecordIsNull set LastError\n");
+ r = MsiRecordGetInteger(0,0);
+ ok(r == MSI_NULL_INTEGER, "got integer from invalid record\n");
+ r = MsiRecordSetInteger(0,0,0);
+ ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
+ r = MsiRecordSetInteger(0,-1,0);
+ ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
+ SetLastError(0);
+ h = MsiCreateRecord(-1);
+ ok(h==0, "created record with -1 elements\n");
+ h = MsiCreateRecord(0x10000);
+ ok(h==0, "created record with 0x10000 elements\n");
+ /* doesn't set LastError */
+ ok(GetLastError()==0, "MsiCreateRecord set last error\n");
+ r = MsiRecordClearData(0);
+ ok(r == ERROR_INVALID_HANDLE, "MsiRecordClearData returned wrong error\n");
+ r = MsiRecordDataSize(0,0);
+ ok(r == 0, "MsiRecordDataSize returned wrong error\n");
+
+
+ /* check behaviour of a record with 0 elements */
+ h = MsiCreateRecord(0);
+ ok(h!=0, "couldn't create record with zero elements\n");
+ r = MsiRecordGetFieldCount(h);
+ ok(r==0, "field count should be zero\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r, "new record wasn't null\n");
+ r = MsiRecordIsNull(h,1);
+ ok(r, "out of range record wasn't null\n");
+ r = MsiRecordIsNull(h,-1);
+ ok(r, "out of range record wasn't null\n");
+ r = MsiRecordDataSize(h,0);
+ ok(r==0, "size of null record is 0\n");
+ sz = sizeof buf;
+ strcpy(buf,"x");
+ r = MsiRecordGetString(h, 0, buf, &sz);
+ ok(r==ERROR_SUCCESS, "failed to get null string\n");
+ ok(sz==0, "null string too long\n");
+ ok(buf[0]==0, "null string not set\n");
+
+ /* same record, but add an integer to it */
+ r = MsiRecordSetInteger(h, 0, 0);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 0\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r==0, "new record is null after setting an integer\n");
+ r = MsiRecordDataSize(h,0);
+ ok(r==sizeof(DWORD), "size of integer record is 4\n");
+ r = MsiRecordSetInteger(h, 0, 1);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 1\n");
+ r = MsiRecordSetInteger(h, 1, 1);
+ ok(r == ERROR_INVALID_PARAMETER, "set integer at 1\n");
+ r = MsiRecordSetInteger(h, -1, 0);
+ ok(r == ERROR_INVALID_PARAMETER, "set integer at -1\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r==0, "new record is null after setting an integer\n");
+ r = MsiRecordGetInteger(h, 0);
+ ok(r == 1, "failed to get integer\n");
+
+ /* same record, but add a string to it */
+ r = MsiRecordSetString(h, 0, NULL);
+ ok(r == ERROR_SUCCESS, "Failed to set null string at 0\n");
+ r = MsiRecordIsNull(h, 0);
+ ok(r == TRUE, "null string not null field\n");
+ r = MsiRecordSetString(h, 0, "");
+ ok(r == ERROR_SUCCESS, "Failed to set empty string at 0\n");
+ r = MsiRecordIsNull(h, 0);
+ ok(r == TRUE, "null string not null field\n");
+ r = MsiRecordSetString(h,0,str);
+ ok(r == ERROR_SUCCESS, "Failed to set string at 0\n");
+ r = MsiRecordGetInteger(h, 0);
+ ok(r == MSI_NULL_INTEGER, "should get invalid integer\n");
+ r = MsiRecordDataSize(h,0);
+ ok(r==sizeof str-1, "size of string record is strlen\n");
+ buf[0]=0;
+ sz = sizeof buf;
+ r = MsiRecordGetString(h,0,buf,&sz);
+ ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
+ ok(0==strcmp(buf,str), "MsiRecordGetString returned the wrong string\n");
+ ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n");
+ buf[0]=0;
+ sz = sizeof str - 2;
+ r = MsiRecordGetString(h,0,buf,&sz);
+ ok(r == ERROR_MORE_DATA, "small buffer should yield ERROR_MORE_DATA\n");
+ ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n");
+ ok(0==strncmp(buf,str,sizeof str-3), "MsiRecordGetString returned the wrong string\n");
+ ok(buf[sizeof str - 3]==0, "string wasn't nul terminated\n");
+
+ buf[0]=0;
+ sz = sizeof str;
+ r = MsiRecordGetString(h,0,buf,&sz);
+ ok(r == ERROR_SUCCESS, "wrong error\n");
+ ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n");
+ ok(0==strcmp(buf,str), "MsiRecordGetString returned the wrong string\n");
+
+
+ memset(bufW, 0, sizeof bufW);
+ sz = 5;
+ r = MsiRecordGetStringW(h,0,bufW,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok(0==memcmp(bufW,strW,8), "MsiRecordGetString returned the wrong string\n");
+
+ sz = 0;
+ bufW[0] = 'x';
+ r = MsiRecordGetStringW(h,0,bufW,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok('x'==bufW[0], "MsiRecordGetString returned the wrong string\n");
+
+ memset(buf, 0, sizeof buf);
+ sz = 5;
+ r = MsiRecordGetStringA(h,0,buf,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok(0==memcmp(buf,str,4), "MsiRecordGetString returned the wrong string\n");
+
+ sz = 0;
+ buf[0] = 'x';
+ r = MsiRecordGetStringA(h,0,buf,&sz);
+ ok(r == ERROR_MORE_DATA, "wrong error\n");
+ ok(sz == 5, "MsiRecordGetString returned the wrong length\n");
+ ok('x'==buf[0], "MsiRecordGetString returned the wrong string\n");
+
+ /* same record, check we can wipe all the data */
+ r = MsiRecordClearData(h);
+ ok(r == ERROR_SUCCESS, "Failed to clear record\n");
+ r = MsiRecordClearData(h);
+ ok(r == ERROR_SUCCESS, "Failed to clear record again\n");
+ r = MsiRecordIsNull(h,0);
+ ok(r, "cleared record wasn't null\n");
+
+ /* same record, try converting strings to integers */
+ i = MsiRecordSetString(h,0,"42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == 42, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"-42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == -42, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0," 42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"42 ");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"42.0");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"0x42");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
+ i = MsiRecordSetString(h,0,"1000000000000000");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == -1530494976, "should get truncated integer\n");
+ i = MsiRecordSetString(h,0,"2147483647");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == 2147483647, "should get maxint\n");
+ i = MsiRecordSetString(h,0,"-2147483647");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == -2147483647, "should get -maxint-1\n");
+ i = MsiRecordSetString(h,0,"4294967297");
+ ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
+ i = MsiRecordGetInteger(h, 0);
+ ok(i == 1, "should get one\n");
+
+ /* same record, try converting integers to strings */
+ r = MsiRecordSetInteger(h, 0, 32);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
+ buf[0]=0;
+ sz = sizeof buf;
+ r = MsiRecordGetString(h, 0, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
+ ok(0==strcmp(buf,"32"), "failed to get string from integer\n");
+ r = MsiRecordSetInteger(h, 0, -32);
+ ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
+ buf[0]=0;
+ sz = sizeof buf;
+ r = MsiRecordGetString(h, 0, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
+ ok(0==strcmp(buf,"-32"), "failed to get string from integer\n");
+
+ /* same record, now try streams */
+ r = MsiRecordSetStream(h, 0, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "set NULL stream\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 0, buf, &sz);
+ ok(r == ERROR_INVALID_DATATYPE, "read non-stream type\n");
+ ok(sz == sizeof buf, "set sz\n");
+ r = MsiRecordDataSize( h, -1);
+ ok(r == 0,"MsiRecordDataSize returned wrong size\n");
+ r = MsiRecordDataSize( h, 0);
+ ok(r == 4,"MsiRecordDataSize returned wrong size\n");
+
+ /* same record, now close it */
+ r = MsiCloseHandle(h);
+ ok(r == ERROR_SUCCESS, "Failed to close handle\n");
+
+ /* now try streams in a new record - need to create a file to play with */
+ r = create_temp_file(filename);
+ if(!r)
+ return;
+
+ /* streams can't be inserted in field 0 for some reason */
+ h = MsiCreateRecord(2);
+ ok(h, "couldn't create a two field record\n");
+ r = MsiRecordSetStream(h, 0, filename);
+ ok(r == ERROR_INVALID_PARAMETER, "added stream to field 0\n");
+ r = MsiRecordSetStream(h, 1, filename);
+ ok(r == ERROR_SUCCESS, "failed to add stream to record\n");
+ r = MsiRecordReadStream(h, 1, buf, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
+ /* http://test.winehq.org/data/200503181000/98_jmelgarejo98casa/msi:record.txt */
+ DeleteFile(filename); /* Windows 98 doesn't like this at all, so don't check return. */
+ r = MsiRecordReadStream(h, 1, NULL, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, NULL, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==26,"couldn't get size of stream\n");
+ sz = 0;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==0,"short read\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==sizeof buf,"short read\n");
+ ok(!strncmp(buf,"abcdefghij",10), "read the wrong thing\n");
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==sizeof buf,"short read\n");
+ ok(!strncmp(buf,"klmnopqrst",10), "read the wrong thing\n");
+ memset(buf,0,sizeof buf);
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==6,"short read\n");
+ ok(!strcmp(buf,"uvwxyz"), "read the wrong thing\n");
+ memset(buf,0,sizeof buf);
+ sz = sizeof buf;
+ r = MsiRecordReadStream(h, 1, buf, &sz);
+ ok(r == ERROR_SUCCESS, "failed to read stream\n");
+ ok(sz==0,"size non-zero at end of stream\n");
+ ok(buf[0]==0, "read something at end of the stream\n");
+ r = MsiRecordSetStream(h, 1, NULL);
+ ok(r == ERROR_SUCCESS, "failed to reset stream\n");
+ sz = 0;
+ r = MsiRecordReadStream(h, 1, NULL, &sz);
+ ok(r == ERROR_SUCCESS, "bytes left wrong after reset\n");
+ ok(sz==26,"couldn't get size of stream\n");
+ r = MsiRecordDataSize(h,1);
+ ok(r == 26,"MsiRecordDataSize returned wrong size\n");
+
+ /* now close the stream record */
+ r = MsiCloseHandle(h);
+ ok(r == ERROR_SUCCESS, "Failed to close handle\n");
+ DeleteFile(filename); /* Delete it for sure, when everything else is closed. */
+}
+
+START_TEST(record)
+{
+ test_msirecord();
+}
--- /dev/null
+/*
+ * Copyright (C) 2005 Mike McCormack for CodeWeavers
+ *
+ * A test program for MSI database files.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <stdio.h>
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include "wine/test.h"
+#include "wine/windef.h"
+
+/*
+ * The following are defined in Windows SDK's msidefs.h
+ * but that file doesn't exist in the msvc6 header set.
+ *
+ * Some are already defined in PropIdl.h - undefine them
+ */
+#undef PID_DICTIONARY
+#undef PID_CODEPAGE
+#undef PID_SUBJECT
+#undef PID_SECURITY
+
+#define PID_DICTIONARY 0
+#define PID_CODEPAGE 1
+#define PID_TITLE 2
+#define PID_SUBJECT 3
+#define PID_AUTHOR 4
+#define PID_KEYWORDS 5
+#define PID_COMMENTS 6
+#define PID_TEMPLATE 7
+#define PID_LASTAUTHOR 8
+#define PID_REVNUMBER 9
+#define PID_EDITTINE 10
+#define PID_LASTPRINTED 11
+#define PID_CREATE_DTM 12
+#define PID_LASTSAVE_DTM 13
+#define PID_PAGECOUNT 14
+#define PID_WORDCOUNT 15
+#define PID_CHARCOUNT 16
+#define PID_THUMBNAIL 17
+#define PID_APPNAME 18
+#define PID_SECURITY 19
+#define PID_MSIVERSION PID_PAGECOUNT
+#define PID_MSISOURCE PID_WORDCOUNT
+#define PID_MSIRESTRICT PID_CHARCOUNT
+
+START_TEST(suminfo)
+{
+ const char *msifile = "winetest.msi";
+ MSIHANDLE hdb = 0, hsuminfo;
+ UINT r, count, type;
+ DWORD sz;
+ INT val;
+ FILETIME ft;
+ char buf[0x10];
+
+ DeleteFile(msifile);
+
+ /* just MsiOpenDatabase should not create a file */
+ r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+ ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
+
+ r = MsiGetSummaryInformation(hdb, NULL, 0, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiGetSummaryInformation wrong error\n");
+
+ r = MsiGetSummaryInformation(hdb, NULL, 0, &hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n");
+
+ r = MsiSummaryInfoGetPropertyCount(0, NULL);
+ ok(r == ERROR_INVALID_HANDLE, "getpropcount failed\n");
+
+ r = MsiSummaryInfoGetPropertyCount(hsuminfo, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+
+ count = -1;
+ r = MsiSummaryInfoGetPropertyCount(hsuminfo, &count);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(count == 0, "count should be zero\n");
+
+ r = MsiSummaryInfoGetProperty(hsuminfo, 0, NULL, NULL, NULL, 0, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+
+ type = -1;
+ r = MsiSummaryInfoGetProperty(hsuminfo, 0, &type, NULL, NULL, 0, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(type == 0, "wrong type\n");
+
+ type = -1;
+ val = 1234;
+ r = MsiSummaryInfoGetProperty(hsuminfo, 0, &type, &val, NULL, 0, NULL);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(type == 0, "wrong type\n");
+ ok(val == 1234, "wrong val\n");
+
+ buf[0]='x';
+ buf[1]=0;
+ sz = 0x10;
+ r = MsiSummaryInfoGetProperty(hsuminfo, PID_REVNUMBER, &type, &val, NULL, buf, &sz);
+ ok(r == ERROR_SUCCESS, "getpropcount failed\n");
+ ok(buf[0]=='x', "cleared buffer\n");
+ ok(sz == 0x10, "count wasn't zero\n");
+ ok(type == VT_EMPTY, "should be empty\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "Mike");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 1, NULL, "JungAh");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 1, &ft, "Mike");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, &ft, "JungAh");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiCloseHandle(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* try again with the update count set */
+ r = MsiGetSummaryInformation(hdb, NULL, 1, &hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, 0, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_I4, 0, NULL, "Mike");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_AUTHOR, VT_I4, 0, NULL, "JungAh");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_KEYWORDS, VT_I2, 0, NULL, "Mike");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_COMMENTS, VT_FILETIME, 0, NULL, "JungAh");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TEMPLATE, VT_I2, 0, NULL, "Mike");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_LASTAUTHOR, VT_LPSTR, 0, NULL, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_LASTSAVE_DTM, VT_FILETIME, 0, NULL, NULL);
+ ok(r == ERROR_INVALID_PARAMETER, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_LASTAUTHOR, VT_LPWSTR, 0, NULL, "h\0i\0\0");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_REVNUMBER, VT_I4, 0, NULL, "Jungah");
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_PAGECOUNT, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "Mike");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n");
+
+ sz = 2;
+ strcpy(buf,"x");
+ r = MsiSummaryInfoGetProperty(hsuminfo, PID_TITLE, &type, NULL, NULL, buf, &sz );
+ ok(r == ERROR_MORE_DATA, "MsiSummaryInfoSetProperty failed\n");
+ ok(sz == 4, "count was wrong\n");
+ ok(type == VT_LPSTR, "type was wrong\n");
+ ok(!strcmp(buf,"M"), "buffer was wrong\n");
+
+ sz = 4;
+ strcpy(buf,"x");
+ r = MsiSummaryInfoGetProperty(hsuminfo, PID_TITLE, &type, NULL, NULL, buf, &sz );
+ ok(r == ERROR_MORE_DATA, "MsiSummaryInfoSetProperty failed\n");
+ ok(sz == 4, "count was wrong\n");
+ ok(type == VT_LPSTR, "type was wrong\n");
+ ok(!strcmp(buf,"Mik"), "buffer was wrong\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "JungAh");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, &ft, "Mike");
+ ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiCloseHandle(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ /* try again with a higher update count */
+ r = MsiGetSummaryInformation(hdb, NULL, 10, &hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_TITLE, VT_LPSTR, 0, NULL, "JungAh");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_LPSTR, 1, NULL, NULL);
+ ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, NULL, NULL);
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_CODEPAGE, VT_I2, 1, &ft, "Mike");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoSetProperty(hsuminfo, PID_AUTHOR, VT_LPSTR, 1, &ft, "Mike");
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n");
+
+ r = MsiSummaryInfoPersist(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiSummaryInfoPersist failed\n");
+
+ MsiDatabaseCommit(hdb);
+
+ r = MsiCloseHandle(hsuminfo);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = MsiCloseHandle(hdb);
+ ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
+
+ r = DeleteFile(msifile);
+ ok(r, "DeleteFile failed\n");
+}
--- /dev/null
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_db(void);
+extern void func_format(void);
+extern void func_install(void);
+extern void func_msi(void);
+extern void func_package(void);
+extern void func_record(void);
+extern void func_suminfo(void);
+
+const struct test winetest_testlist[] =
+{
+ { "db", func_db },
+ { "format", func_format },
+ { "install", func_install },
+ { "msi", func_msi },
+ { "package", func_package },
+ { "record", func_record },
+ { "suminfo", func_suminfo },
+ { 0, 0 }
+};
--- /dev/null
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+TESTDLL = msvcrt.dll
+IMPORTS = msvcrt
+EXTRAINCL = -I$(TOPSRCDIR)/include/msvcrt -I$(SRCDIR)/..
+
+CTESTS = \
+ cpp.c \
+ environ.c \
+ file.c \
+ heap.c \
+ printf.c \
+ scanf.c \
+ string.c \
+ time.c
+
+@MAKE_TEST_RULES@
+
+### Dependencies:
--- /dev/null
+/* Unit test suite for msvcrt C++ objects
+ *
+ * Copyright 2003 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * NOTES
+ * This tests is only valid for ix86 platforms, on others it's a no-op.
+ * Some tests cannot be checked with ok(), for example the dtors. We simply
+ * call them to ensure we don't crash ;-)
+ *
+ * If we build this test with VC++ in debug mode, we will fail in _chkstk()
+ * or at program exit malloc() checking if these methods haven't been
+ * implemented correctly (they have).
+ *
+ * Tested with a range of native msvcrt's from v4 -> v7.
+ */
+#include "wine/test.h"
+#include "winbase.h"
+#include "winnt.h"
+
+#ifndef __i386__
+/* Skip these tests for non x86 platforms */
+START_TEST(cpp)
+{
+}
+#else
+
+typedef struct __exception
+{
+ void *vtable;
+ char *name;
+ int do_free;
+} exception;
+
+typedef struct __type_info
+{
+ void *vtable;
+ char *name;
+ char mangled[16];
+} type_info;
+
+/* Function pointers. We need to use these to call these funcs as __thiscall */
+static HMODULE hMsvcrt;
+
+static void* (*poperator_new)(unsigned int);
+static void (*poperator_delete)(void*);
+static void* (*pmalloc)(unsigned int);
+static void (*pfree)(void*);
+
+/* exception */
+static void (WINAPI *pexception_ctor)(exception*,LPCSTR*);
+static void (WINAPI *pexception_copy_ctor)(exception*,exception*);
+static void (WINAPI *pexception_default_ctor)(exception*);
+static void (WINAPI *pexception_dtor)(exception*);
+static exception* (WINAPI *pexception_opequals)(exception*,exception*);
+static char* (WINAPI *pexception_what)(exception*);
+static void* (WINAPI *pexception_vtable)(exception*);
+static void (WINAPI *pexception_vector_dtor)(exception*,unsigned int);
+static void (WINAPI *pexception_scalar_dtor)(exception*,unsigned int);
+
+/* bad_typeid */
+static void (WINAPI *pbad_typeid_ctor)(exception*,LPCSTR);
+static void (WINAPI *pbad_typeid_ctor_closure)(exception*);
+static void (WINAPI *pbad_typeid_copy_ctor)(exception*,exception*);
+static void (WINAPI *pbad_typeid_dtor)(exception*);
+static exception* (WINAPI *pbad_typeid_opequals)(exception*,exception*);
+static char* (WINAPI *pbad_typeid_what)(exception*);
+static void* (WINAPI *pbad_typeid_vtable)(exception*);
+static void (WINAPI *pbad_typeid_vector_dtor)(exception*,unsigned int);
+static void (WINAPI *pbad_typeid_scalar_dtor)(exception*,unsigned int);
+
+/* bad_cast */
+static void (WINAPI *pbad_cast_ctor)(exception*,LPCSTR*);
+static void (WINAPI *pbad_cast_ctor2)(exception*,LPCSTR);
+static void (WINAPI *pbad_cast_ctor_closure)(exception*);
+static void (WINAPI *pbad_cast_copy_ctor)(exception*,exception*);
+static void (WINAPI *pbad_cast_dtor)(exception*);
+static exception* (WINAPI *pbad_cast_opequals)(exception*,exception*);
+static char* (WINAPI *pbad_cast_what)(exception*);
+static void* (WINAPI *pbad_cast_vtable)(exception*);
+static void (WINAPI *pbad_cast_vector_dtor)(exception*,unsigned int);
+static void (WINAPI *pbad_cast_scalar_dtor)(exception*,unsigned int);
+
+/* __non_rtti_object */
+static void (WINAPI *p__non_rtti_object_ctor)(exception*,LPCSTR);
+static void (WINAPI *p__non_rtti_object_copy_ctor)(exception*,exception*);
+static void (WINAPI *p__non_rtti_object_dtor)(exception*);
+static exception* (WINAPI *p__non_rtti_object_opequals)(exception*,exception*);
+static char* (WINAPI *p__non_rtti_object_what)(exception*);
+static void* (WINAPI *p__non_rtti_object_vtable)(exception*);
+static void (WINAPI *p__non_rtti_object_vector_dtor)(exception*,unsigned int);
+static void (WINAPI *p__non_rtti_object_scalar_dtor)(exception*,unsigned int);
+
+/* type_info */
+static void (WINAPI *ptype_info_dtor)(type_info*);
+static char* (WINAPI *ptype_info_raw_name)(type_info*);
+static char* (WINAPI *ptype_info_name)(type_info*);
+static int (WINAPI *ptype_info_before)(type_info*,type_info*);
+static int (WINAPI *ptype_info_opequals_equals)(type_info*,type_info*);
+static int (WINAPI *ptype_info_opnot_equals)(type_info*,type_info*);
+
+/* RTTI */
+static type_info* (*p__RTtypeid)(void*);
+static void* (*p__RTCastToVoid)(void*);
+static void* (*p__RTDynamicCast)(void*,int,void*,void*,int);
+
+/*Demangle*/
+static char* (*p__unDName)(char*,const char*,int,void*,void*,unsigned short int);
+
+
+/* _very_ early native versions have serious RTTI bugs, so we check */
+static void* bAncientVersion;
+
+/* Emulate a __thiscall */
+#ifdef _MSC_VER
+_inline static void* do_call_func1(void *func, void *_this)
+{
+ volatile void* retval = 0;
+ __asm
+ {
+ push ecx
+ mov ecx, _this
+ call func
+ mov retval, eax
+ pop ecx
+ }
+ return (void*)retval;
+}
+
+_inline static void* do_call_func2(void *func, void *_this, void* arg)
+{
+ volatile void* retval = 0;
+ __asm
+ {
+ push ecx
+ push arg
+ mov ecx, _this
+ call func
+ mov retval, eax
+ pop ecx
+ }
+ return (void*)retval;
+}
+#else
+static void* do_call_func1(void *func, void *_this)
+{
+ void* ret;
+ __asm__ __volatile__ ("call *%1"
+ : "=a" (ret)
+ : "g" (func), "c" (_this)
+ : "memory" );
+ return ret;
+}
+static void* do_call_func2(void *func, void *_this, void* arg)
+{
+ void* ret;
+ __asm__ __volatile__ ("pushl %2\n\tcall *%1"
+ : "=a" (ret)
+ : "r" (func), "g" (arg), "c" (_this)
+ : "memory" );
+ return ret;
+}
+#endif
+
+#define call_func1(x,y) do_call_func1((void*)x,(void*)y)
+#define call_func2(x,y,z) do_call_func2((void*)x,(void*)y,(void*)z)
+
+/* Some exports are only available in later versions */
+#define SETNOFAIL(x,y) x = (void*)GetProcAddress(hMsvcrt,y)
+#define SET(x,y) SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y)
+
+static void InitFunctionPtrs(void)
+{
+ hMsvcrt = LoadLibraryA("msvcrt.dll");
+ ok(hMsvcrt != 0, "LoadLibraryA failed\n");
+ if (hMsvcrt)
+ {
+ SETNOFAIL(poperator_new, "??_U@YAPAXI@Z");
+ SETNOFAIL(poperator_delete, "??_V@YAXPAX@Z");
+ SET(pmalloc, "malloc");
+ SET(pfree, "free");
+
+ if (!poperator_new)
+ poperator_new = pmalloc;
+ if (!poperator_delete)
+ poperator_delete = pfree;
+
+ SET(pexception_ctor, "??0exception@@QAE@ABQBD@Z");
+ SET(pexception_copy_ctor, "??0exception@@QAE@ABV0@@Z");
+ SET(pexception_default_ctor, "??0exception@@QAE@XZ");
+ SET(pexception_dtor, "??1exception@@UAE@XZ");
+ SET(pexception_opequals, "??4exception@@QAEAAV0@ABV0@@Z");
+ SET(pexception_what, "?what@exception@@UBEPBDXZ");
+ SET(pexception_vtable, "??_7exception@@6B@");
+ SET(pexception_vector_dtor, "??_Eexception@@UAEPAXI@Z");
+ SET(pexception_scalar_dtor, "??_Gexception@@UAEPAXI@Z");
+
+ SET(pbad_typeid_ctor, "??0bad_typeid@@QAE@PBD@Z");
+ SETNOFAIL(pbad_typeid_ctor_closure, "??_Fbad_typeid@@QAEXXZ");
+ SET(pbad_typeid_copy_ctor, "??0bad_typeid@@QAE@ABV0@@Z");
+ SET(pbad_typeid_dtor, "??1bad_typeid@@UAE@XZ");
+ SET(pbad_typeid_opequals, "??4bad_typeid@@QAEAAV0@ABV0@@Z");
+ SET(pbad_typeid_what, "?what@exception@@UBEPBDXZ");
+ SET(pbad_typeid_vtable, "??_7bad_typeid@@6B@");
+ SET(pbad_typeid_vector_dtor, "??_Ebad_typeid@@UAEPAXI@Z");
+ SET(pbad_typeid_scalar_dtor, "??_Gbad_typeid@@UAEPAXI@Z");
+
+ SETNOFAIL(pbad_cast_ctor, "??0bad_cast@@QAE@ABQBD@Z");
+ if (!pbad_cast_ctor)
+ SET(pbad_cast_ctor, "??0bad_cast@@AAE@PBQBD@Z");
+ SETNOFAIL(pbad_cast_ctor2, "??0bad_cast@@QAE@PBD@Z");
+ SETNOFAIL(pbad_cast_ctor_closure, "??_Fbad_cast@@QAEXXZ");
+ SET(pbad_cast_copy_ctor, "??0bad_cast@@QAE@ABV0@@Z");
+ SET(pbad_cast_dtor, "??1bad_cast@@UAE@XZ");
+ SET(pbad_cast_opequals, "??4bad_cast@@QAEAAV0@ABV0@@Z");
+ SET(pbad_cast_what, "?what@exception@@UBEPBDXZ");
+ SET(pbad_cast_vtable, "??_7bad_cast@@6B@");
+ SET(pbad_cast_vector_dtor, "??_Ebad_cast@@UAEPAXI@Z");
+ SET(pbad_cast_scalar_dtor, "??_Gbad_cast@@UAEPAXI@Z");
+
+ SET(p__non_rtti_object_ctor, "??0__non_rtti_object@@QAE@PBD@Z");
+ SET(p__non_rtti_object_copy_ctor, "??0__non_rtti_object@@QAE@ABV0@@Z");
+ SET(p__non_rtti_object_dtor, "??1__non_rtti_object@@UAE@XZ");
+ SET(p__non_rtti_object_opequals, "??4__non_rtti_object@@QAEAAV0@ABV0@@Z");
+ SET(p__non_rtti_object_what, "?what@exception@@UBEPBDXZ");
+ SET(p__non_rtti_object_vtable, "??_7__non_rtti_object@@6B@");
+ SET(p__non_rtti_object_vector_dtor, "??_E__non_rtti_object@@UAEPAXI@Z");
+ SET(p__non_rtti_object_scalar_dtor, "??_G__non_rtti_object@@UAEPAXI@Z");
+
+ SET(ptype_info_dtor, "??1type_info@@UAE@XZ");
+ SET(ptype_info_raw_name, "?raw_name@type_info@@QBEPBDXZ");
+ SET(ptype_info_name, "?name@type_info@@QBEPBDXZ");
+ SET(ptype_info_before, "?before@type_info@@QBEHABV1@@Z");
+ SET(ptype_info_opequals_equals, "??8type_info@@QBEHABV0@@Z");
+ SET(ptype_info_opnot_equals, "??9type_info@@QBEHABV0@@Z");
+
+ SET(p__RTtypeid, "__RTtypeid");
+ SET(p__RTCastToVoid, "__RTCastToVoid");
+ SET(p__RTDynamicCast, "__RTDynamicCast");
+
+ SET(p__unDName,"__unDName");
+
+ /* Extremely early versions export logic_error, and crash in RTTI */
+ SETNOFAIL(bAncientVersion, "??0logic_error@@QAE@ABQBD@Z");
+ }
+}
+
+static void test_exception(void)
+{
+ static const char* e_name = "An exception name";
+ char* name;
+ exception e, e2, e3, *pe;
+
+ if (!poperator_new || !poperator_delete ||
+ !pexception_ctor || !pexception_copy_ctor || !pexception_default_ctor ||
+ !pexception_dtor || !pexception_opequals || !pexception_what ||
+ !pexception_vtable || !pexception_vector_dtor || !pexception_scalar_dtor)
+ return;
+
+ /* 'const char*&' ctor */
+ memset(&e, 0, sizeof(e));
+ call_func2(pexception_ctor, &e, &e_name);
+ ok(e.vtable != NULL, "Null exception vtable for e\n");
+ ok(e.name && e.name != e_name && !strcmp(e.name, "An exception name"), "Bad name '%s' for e\n", e.name);
+ ok(e.do_free == 1, "do_free set to %d for e\n", e.do_free);
+
+ /* Copy ctor */
+ memset(&e2, 0, sizeof(e2));
+ call_func2(pexception_copy_ctor, &e2, &e);
+ ok(e2.vtable != NULL, "Null exception vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "An exception name"), "Bad exception name for e2\n");
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+
+ /* Default ctor */
+ memset(&e3, 1, sizeof(e3));
+ call_func1(pexception_default_ctor, &e3);
+ ok(e3.vtable != NULL, "Null exception vtable for e3\n");
+ ok(e3.name == NULL, "Bad exception name for e3\n");
+ ok(e3.do_free == 0, "do_free set to %d for e3\n", e3.do_free);
+
+ ok(e.vtable == e2.vtable && e.vtable == e3.vtable, "exception vtables differ!\n");
+
+ /* Test calling the dtors */
+ call_func1(pexception_dtor, &e2);
+ call_func1(pexception_dtor, &e3);
+
+ /* Operator equals */
+ memset(&e2, 0, sizeof(e2));
+ pe = call_func2(pexception_opequals, &e2, &e);
+ ok(e2.vtable != NULL, "Null exception vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "An exception name"), "Bad exception name for e2\n");
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+ ok(pe == &e2, "opequals didn't return e2\n");
+
+ /* what() */
+ name = call_func1(pexception_what, &e2);
+ ok(e2.name == name, "Bad exception name from e2::what()\n");
+
+ /* vtable ptr */
+ ok(e2.vtable == pexception_vtable, "Bad vtable for e2\n");
+ call_func1(pexception_dtor, &e2);
+
+ /* new() */
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ call_func2(pexception_ctor, pe, &e_name);
+ /* scalar dtor */
+ call_func2(pexception_scalar_dtor, pe, 0); /* Shouldn't delete pe */
+ pe->name = NULL;
+ pe->do_free = 0;
+ call_func2(pexception_scalar_dtor, pe, 1); /* Should delete pe */
+ }
+
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, single element */
+ call_func2(pexception_ctor, pe, &e_name);
+ call_func2(pexception_vector_dtor, pe, 1); /* Should delete pe as single element*/
+ }
+
+ pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, multiple elements */
+ char name[] = "a constant";
+ *((int*)pe) = 3;
+ pe = (exception*)((int*)pe + 1);
+ call_func2(pexception_ctor, &pe[0], &e_name);
+ call_func2(pexception_ctor, &pe[1], &e_name);
+ call_func2(pexception_ctor, &pe[2], &e_name);
+ pe[3].name = name;
+ pe[3].do_free = 1; /* Crash if we try to free this */
+ call_func2(pexception_vector_dtor, pe, 3); /* Should delete all 3 and then pe block */
+ }
+
+ /* test our exported vtable is kosher */
+ pe = (void*)pexception_vtable; /* Use the exception struct to get vtable ptrs */
+ pexception_vector_dtor = (void*)pe->vtable;
+ pexception_what = (void*)pe->name;
+
+ name = call_func1(pexception_what, &e);
+ ok(e.name == name, "Bad exception name from vtable e::what()\n");
+
+ if (p__RTtypeid && !bAncientVersion)
+ {
+ /* Check the rtti */
+ type_info *ti = p__RTtypeid(&e);
+ ok (ti && ti->mangled &&
+ !strcmp(ti->mangled, ".?AVexception@@"), "bad rtti for e\n");
+
+ if (ti)
+ {
+ /* Check the returned type_info has rtti too */
+ type_info *ti2 = p__RTtypeid(ti);
+ ok (ti2 != NULL && !strcmp(ti2->mangled, ".?AVtype_info@@"), "bad rtti for e's type_info\n");
+ }
+ }
+
+ call_func2(pexception_vector_dtor, &e, 0); /* Should delete e.name, but not e */
+}
+
+/* This test is basically a cut 'n' paste of the exception test. but it verifies that
+ * bad_typeid works the exact same way... */
+static void test_bad_typeid(void)
+{
+ static const char* e_name = "A bad_typeid name";
+ char* name;
+ exception e, e2, e3, *pe;
+
+ if (!poperator_new || !poperator_delete ||
+ !pbad_typeid_ctor || !pbad_typeid_copy_ctor ||
+ !pbad_typeid_dtor || !pbad_typeid_opequals || !pbad_typeid_what ||
+ !pbad_typeid_vtable || !pbad_typeid_vector_dtor || !pbad_typeid_scalar_dtor)
+ return;
+
+ /* 'const char*' ctor */
+ memset(&e, 0, sizeof(e));
+ call_func2(pbad_typeid_ctor, &e, e_name);
+ ok(e.vtable != NULL, "Null bad_typeid vtable for e\n");
+ ok(e.name && e.name != e_name && !strcmp(e.name, "A bad_typeid name"), "Bad name '%s' for e\n", e.name);
+ ok(e.do_free == 1, "do_free set to %d for e\n", e.do_free);
+
+ /* Copy ctor */
+ memset(&e2, 0, sizeof(e2));
+ call_func2(pbad_typeid_copy_ctor, &e2, &e);
+ ok(e2.vtable != NULL, "Null bad_typeid vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "A bad_typeid name"), "Bad name '%s' for e2\n", e2.name);
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+
+ /* Ctor closure */
+ if (pbad_typeid_ctor_closure)
+ {
+ memset(&e3, 1, sizeof(e3));
+ call_func1(pbad_typeid_ctor_closure, &e3);
+ ok(e3.vtable != NULL, "Null bad_typeid vtable for e3\n");
+ ok(e3.name && !strcmp(e3.name, "bad typeid"), "Bad bad_typeid name for e3\n");
+ ok(e3.do_free == 1, "do_free set to %d for e3\n", e3.do_free);
+ ok(e.vtable == e3.vtable, "bad_typeid closure vtables differ!\n");
+ call_func1(pbad_typeid_dtor, &e3);
+ }
+ ok(e.vtable == e2.vtable, "bad_typeid vtables differ!\n");
+
+ /* Test calling the dtors */
+ call_func1(pbad_typeid_dtor, &e2);
+
+ /* Operator equals */
+ memset(&e2, 1, sizeof(e2));
+ pe = call_func2(pbad_typeid_opequals, &e2, &e);
+ ok(e2.vtable != NULL, "Null bad_typeid vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "A bad_typeid name"), "Bad bad_typeid name for e2\n");
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+ ok(pe == &e2, "opequals didn't return e2\n");
+
+ /* what() */
+ name = call_func1(pbad_typeid_what, &e2);
+ ok(e2.name == name, "Bad bad_typeid name from e2::what()\n");
+
+ /* vtable ptr */
+ ok(e2.vtable == pexception_vtable, "Bad vtable for e2\n");
+ call_func1(pbad_typeid_dtor, &e2);
+
+ /* new() */
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ call_func2(pbad_typeid_ctor, pe, e_name);
+ /* scalar dtor */
+ call_func2(pbad_typeid_scalar_dtor, pe, 0); /* Shouldn't delete pe */
+ pe->name = NULL;
+ pe->do_free = 0;
+ call_func2(pbad_typeid_scalar_dtor, pe, 1); /* Should delete pe */
+ }
+
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, single element */
+ call_func2(pbad_typeid_ctor, pe, e_name);
+ call_func2(pbad_typeid_vector_dtor, pe, 1); /* Should delete pe as single element*/
+ }
+
+ pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, multiple elements */
+ *((int*)pe) = 3;
+ pe = (exception*)((int*)pe + 1);
+ call_func2(pbad_typeid_ctor, &pe[0], e_name);
+ call_func2(pbad_typeid_ctor, &pe[1], e_name);
+ call_func2(pbad_typeid_ctor, &pe[2], e_name);
+ pe[3].name = 0;
+ pe[3].do_free = 1; /* Crash if we try to free this element */
+ call_func2(pbad_typeid_vector_dtor, pe, 3); /* Should delete all 3 and then pe block */
+ }
+
+ /* test our exported vtable is kosher */
+ pe = (void*)pbad_typeid_vtable; /* Use the exception struct to get vtable ptrs */
+ pbad_typeid_vector_dtor = (void*)pe->vtable;
+ pbad_typeid_what = (void*)pe->name;
+
+ name = call_func1(pbad_typeid_what, &e);
+ ok(e.name == name, "Bad bad_typeid name from vtable e::what()\n");
+
+ if (p__RTtypeid && !bAncientVersion)
+ {
+ /* Check the rtti */
+ type_info *ti = p__RTtypeid(&e);
+ ok (ti != NULL && !strcmp(ti->mangled, ".?AVbad_typeid@@"), "bad rtti for e (%s)\n",
+ !ti ? "null" : ti->mangled);
+ }
+
+ call_func2(pbad_typeid_vector_dtor, &e, 0); /* Should delete e.name, but not e */
+}
+
+
+/* Ditto for this test... */
+static void test_bad_cast(void)
+{
+ static const char* e_name = "A bad_cast name";
+ char* name;
+ exception e, e2, e3, *pe;
+
+ if (!poperator_new || !poperator_delete ||
+ !pbad_cast_ctor || !pbad_cast_copy_ctor ||
+ !pbad_cast_dtor || !pbad_cast_opequals || !pbad_cast_what ||
+ !pbad_cast_vtable || !pbad_cast_vector_dtor || !pbad_cast_scalar_dtor)
+ return;
+
+ if (pbad_cast_ctor2)
+ {
+ /* 'const char*' ctor */
+ memset(&e, 0, sizeof(e));
+ call_func2(pbad_cast_ctor2, &e, e_name);
+ ok(e.vtable != NULL, "Null bad_cast vtable for e\n");
+ ok(e.name && e.name != e_name && !strcmp(e.name, "A bad_cast name"), "Bad name '%s' for e\n", e.name);
+ ok(e.do_free == 1, "do_free set to %d for e\n", e.do_free);
+ call_func1(pbad_cast_dtor, &e);
+ }
+
+ /* 'const char*&' ctor */
+ memset(&e, 0, sizeof(e));
+ call_func2(pbad_cast_ctor, &e, &e_name);
+ ok(e.vtable != NULL, "Null bad_cast vtable for e\n");
+ ok(e.name && e.name != e_name && !strcmp(e.name, "A bad_cast name"), "Bad name '%s' for e\n", e.name);
+ ok(e.do_free == 1, "do_free set to %d for e\n", e.do_free);
+
+ /* Copy ctor */
+ memset(&e2, 0, sizeof(e2));
+ call_func2(pbad_cast_copy_ctor, &e2, &e);
+ ok(e2.vtable != NULL, "Null bad_cast vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "A bad_cast name"), "Bad name '%s' for e2\n", e2.name);
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+
+ /* Ctor closure */
+ if (pbad_cast_ctor_closure)
+ {
+ memset(&e3, 1, sizeof(e3));
+ call_func1(pbad_cast_ctor_closure, &e3);
+ ok(e3.vtable != NULL, "Null bad_cast vtable for e3\n");
+ ok(e3.name && !strcmp(e3.name, "bad cast"), "Bad bad_cast name for e3\n");
+ ok(e3.do_free == 1, "do_free set to %d for e3\n", e3.do_free);
+ ok(e.vtable == e3.vtable, "bad_cast closure vtables differ!\n");
+ call_func1(pbad_cast_dtor, &e3);
+ }
+ ok(e.vtable == e2.vtable, "bad_cast vtables differ!\n");
+
+ /* Test calling the dtors */
+ call_func1(pbad_cast_dtor, &e2);
+
+ /* Operator equals */
+ memset(&e2, 1, sizeof(e2));
+ pe = call_func2(pbad_cast_opequals, &e2, &e);
+ ok(e2.vtable != NULL, "Null bad_cast vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "A bad_cast name"), "Bad bad_cast name for e2\n");
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+ ok(pe == &e2, "opequals didn't return e2\n");
+
+ /* what() */
+ name = call_func1(pbad_cast_what, &e2);
+ ok(e2.name == name, "Bad bad_cast name from e2::what()\n");
+
+ /* vtable ptr */
+ ok(e2.vtable == pexception_vtable, "Bad vtable for e2\n");
+ call_func1(pbad_cast_dtor, &e2);
+
+ /* new() */
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ call_func2(pbad_cast_ctor, pe, &e_name);
+ /* scalar dtor */
+ call_func2(pbad_cast_scalar_dtor, pe, 0); /* Shouldn't delete pe */
+ pe->name = NULL;
+ pe->do_free = 0;
+ call_func2(pbad_cast_scalar_dtor, pe, 1); /* Should delete pe */
+ }
+
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, single element */
+ call_func2(pbad_cast_ctor, pe, &e_name);
+ call_func2(pbad_cast_vector_dtor, pe, 1); /* Should delete pe as single element*/
+ }
+
+ pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, multiple elements */
+ *((int*)pe) = 3;
+ pe = (exception*)((int*)pe + 1);
+ call_func2(pbad_cast_ctor, &pe[0], &e_name);
+ call_func2(pbad_cast_ctor, &pe[1], &e_name);
+ call_func2(pbad_cast_ctor, &pe[2], &e_name);
+ pe[3].name = 0;
+ pe[3].do_free = 1; /* Crash if we try to free this element */
+ call_func2(pbad_cast_vector_dtor, pe, 3); /* Should delete all 3 and then pe block */
+ }
+
+ /* test our exported vtable is kosher */
+ pe = (void*)pbad_cast_vtable; /* Use the exception struct to get vtable ptrs */
+ pbad_cast_vector_dtor = (void*)pe->vtable;
+ pbad_cast_what = (void*)pe->name;
+
+ name = call_func1(pbad_cast_what, &e);
+ ok(e.name == name, "Bad bad_cast name from vtable e::what()\n");
+
+ if (p__RTtypeid && !bAncientVersion)
+ {
+ /* Check the rtti */
+ type_info *ti = p__RTtypeid(&e);
+ ok (ti != NULL && !strcmp(ti->mangled, ".?AVbad_cast@@"), "bad rtti for e\n");
+ }
+ call_func2(pbad_cast_vector_dtor, &e, 0); /* Should delete e.name, but not e */
+}
+
+/* ... and this one */
+static void test___non_rtti_object(void)
+{
+ static const char* e_name = "A __non_rtti_object name";
+ char* name;
+ exception e, e2, *pe;
+
+ if (!poperator_new || !poperator_delete ||
+ !p__non_rtti_object_ctor || !p__non_rtti_object_copy_ctor ||
+ !p__non_rtti_object_dtor || !p__non_rtti_object_opequals || !p__non_rtti_object_what ||
+ !p__non_rtti_object_vtable || !p__non_rtti_object_vector_dtor || !p__non_rtti_object_scalar_dtor)
+ return;
+
+ /* 'const char*' ctor */
+ memset(&e, 0, sizeof(e));
+ call_func2(p__non_rtti_object_ctor, &e, e_name);
+ ok(e.vtable != NULL, "Null __non_rtti_object vtable for e\n");
+ ok(e.name && e.name != e_name && !strcmp(e.name, "A __non_rtti_object name"), "Bad name '%s' for e\n", e.name);
+ ok(e.do_free == 1, "do_free set to %d for e\n", e.do_free);
+
+ /* Copy ctor */
+ memset(&e2, 0, sizeof(e2));
+ call_func2(p__non_rtti_object_copy_ctor, &e2, &e);
+ ok(e2.vtable != NULL, "Null __non_rtti_object vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "A __non_rtti_object name"), "Bad name '%s' for e2\n", e2.name);
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+ ok(e.vtable == e2.vtable, "__non_rtti_object vtables differ!\n");
+
+ /* Test calling the dtors */
+ call_func1(p__non_rtti_object_dtor, &e2);
+
+ /* Operator equals */
+ memset(&e2, 1, sizeof(e2));
+ pe = call_func2(p__non_rtti_object_opequals, &e2, &e);
+ ok(e2.vtable != NULL, "Null __non_rtti_object vtable for e2\n");
+ ok(e2.name && e2.name != e.name && !strcmp(e2.name, "A __non_rtti_object name"), "Bad __non_rtti_object name for e2\n");
+ ok(e2.do_free == 1, "do_free set to %d for e2\n", e2.do_free);
+ ok(pe == &e2, "opequals didn't return e2\n");
+
+ /* what() */
+ name = call_func1(p__non_rtti_object_what, &e2);
+ ok(e2.name == name, "Bad __non_rtti_object name from e2::what()\n");
+
+ /* vtable ptr */
+ ok(e2.vtable == pexception_vtable, "Bad vtable for e2\n");
+ call_func1(p__non_rtti_object_dtor, &e2);
+
+ /* new() */
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ call_func2(p__non_rtti_object_ctor, pe, e_name);
+ /* scalar dtor */
+ call_func2(p__non_rtti_object_scalar_dtor, pe, 0); /* Shouldn't delete pe */
+ pe->name = NULL;
+ pe->do_free = 0;
+ call_func2(p__non_rtti_object_scalar_dtor, pe, 1); /* Should delete pe */
+ }
+
+ pe = poperator_new(sizeof(exception));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, single element */
+ call_func2(p__non_rtti_object_ctor, pe, e_name);
+ call_func2(p__non_rtti_object_vector_dtor, pe, 1); /* Should delete pe as single element*/
+ }
+
+ pe = poperator_new(sizeof(exception) * 4 + sizeof(int));
+ ok(pe != NULL, "new() failed\n");
+ if (pe)
+ {
+ /* vector dtor, multiple elements */
+ *((int*)pe) = 3;
+ pe = (exception*)((int*)pe + 1);
+ call_func2(p__non_rtti_object_ctor, &pe[0], e_name);
+ call_func2(p__non_rtti_object_ctor, &pe[1], e_name);
+ call_func2(p__non_rtti_object_ctor, &pe[2], e_name);
+ pe[3].name = 0;
+ pe[3].do_free = 1; /* Crash if we try to free this element */
+ call_func2(p__non_rtti_object_vector_dtor, pe, 3); /* Should delete all 3 and then pe block */
+ }
+
+ /* test our exported vtable is kosher */
+ pe = (void*)p__non_rtti_object_vtable; /* Use the exception struct to get vtable ptrs */
+ p__non_rtti_object_vector_dtor = (void*)pe->vtable;
+ p__non_rtti_object_what = (void*)pe->name;
+
+ name = call_func1(p__non_rtti_object_what, &e);
+ ok(e.name == name, "Bad __non_rtti_object name from vtable e::what()\n");
+
+ if (p__RTtypeid && !bAncientVersion)
+ {
+ /* Check the rtti */
+ type_info *ti = p__RTtypeid(&e);
+ ok (ti != NULL && !strcmp(ti->mangled, ".?AV__non_rtti_object@@"), "bad rtti for e\n");
+ }
+ call_func2(p__non_rtti_object_vector_dtor, &e, 0); /* Should delete e.name, but not e */
+}
+
+
+static void test_type_info(void)
+{
+ static type_info t1 = { NULL, NULL,{'.','?','A','V','t','e','s','t','1','@','@',0,0,0,0,0 } };
+ static type_info t1_1 = { NULL, NULL,{'?','?','A','V','t','e','s','t','1','@','@',0,0,0,0,0 } };
+ static type_info t2 = { NULL, NULL, {'.','?','A','V','t','e','s','t','2','@','@',0,0,0,0,0 } };
+ char* name;
+ int res;
+
+ if (!pmalloc || !pfree || !ptype_info_dtor || !ptype_info_raw_name ||
+ !ptype_info_name || !ptype_info_before ||
+ !ptype_info_opequals_equals || !ptype_info_opnot_equals)
+ return;
+
+ /* Test calling the dtors */
+ call_func1(ptype_info_dtor, &t1); /* No effect, since name is NULL */
+ t1.name = pmalloc(64);
+ strcpy(t1.name, "foo");
+ call_func1(ptype_info_dtor, &t1); /* Frees t1.name using 'free' */
+
+ /* raw_name */
+ t1.name = NULL;
+ name = call_func1(ptype_info_raw_name, &t1);
+
+ /* FIXME: This fails on native; it shouldn't though - native bug?
+ * ok(name && !strcmp(name, t1.mangled), "bad raw_name '%s' for t1 (expected '%s')\n", name, t1.mangled);
+ */
+ ok(t1.name == NULL, "raw_name() set name for t1\n");
+
+ /* name */
+ t1.name = NULL;
+ name = call_func1(ptype_info_name, &t1);
+ ok(name && t1.name && !strcmp(name, t1.name), "bad name '%s' for t1\n", name);
+
+ ok(t1.name && !strcmp(t1.name, "class test1"), "demangled to '%s' for t1\n", t1.name);
+ call_func1(ptype_info_dtor, &t1);
+
+ /* before */
+ t1.name = NULL;
+ res = (int)call_func2(ptype_info_before, &t1, &t1);
+ ok(res == 0, "expected 0, got %d\n", res);
+ res = (int)call_func2(ptype_info_before, &t2, &t1);
+ ok(res == 0, "expected 0, got %d\n", res);
+ res = (int)call_func2(ptype_info_before, &t1, &t2);
+ ok(res == 1, "expected 1, got %d\n", res);
+ /* Doesn't check first char */
+ res = (int)call_func2(ptype_info_before, &t1, &t1_1);
+ ok(res == 0, "expected 0, got %d\n", res);
+
+ /* opequals_equals */
+ t1.name = NULL;
+ res = (int)call_func2(ptype_info_opequals_equals, &t1, &t1);
+ ok(res == 1, "expected 1, got %d\n", res);
+ res = (int)call_func2(ptype_info_opequals_equals, &t1, &t2);
+ ok(res == 0, "expected 0, got %d\n", res);
+ res = (int)call_func2(ptype_info_opequals_equals, &t2, &t1);
+ ok(res == 0, "expected 0, got %d\n", res);
+
+ /* opnot_equals */
+ t1.name = NULL;
+ res = (int)call_func2(ptype_info_opnot_equals, &t1, &t1);
+ ok(res == 0, "expected 0, got %d\n", res);
+ res = (int)call_func2(ptype_info_opnot_equals, &t1, &t2);
+ ok(res == 1, "expected 1, got %d\n", res);
+ res = (int)call_func2(ptype_info_opnot_equals, &t2, &t1);
+ ok(res == 1, "expected 1, got %d\n", res);
+}
+
+/* Test RTTI functions */
+static void test_rtti(void)
+{
+ static const char* e_name = "name";
+ type_info *ti,*bti;
+ exception e,b;
+ void *casted;
+
+ if (bAncientVersion ||
+ !p__RTCastToVoid || !p__RTtypeid || !pexception_ctor || !pbad_typeid_ctor || !p__RTDynamicCast)
+ return;
+
+ call_func2(pexception_ctor, &e, &e_name);
+ call_func2(pbad_typeid_ctor, &b, e_name);
+
+ /* dynamic_cast to void* */
+ casted = p__RTCastToVoid(&e);
+ ok (casted == (void*)&e, "failed cast to void\n");
+
+ /* dynamic_cast up */
+ ti = p__RTtypeid(&e);
+ bti = p__RTtypeid(&b);
+
+ casted = p__RTDynamicCast(&b, 0, NULL, ti, 0);
+ ok (casted == (void*)&b, "failed cast from bad_cast to exception\n");
+
+ /* dynamic_cast down */
+ casted = p__RTDynamicCast(&e, 0, NULL, bti, 0);
+ ok (casted == NULL, "Cast succeeded\n");
+}
+
+struct _demangle {
+ LPCSTR mangled;
+ LPCSTR result;
+ BOOL test_in_wine;
+};
+
+static void test_demangle_datatype(void)
+{
+ char * name;
+ struct _demangle demangle[]={
+/* { "BlaBla"," ?? ::Bla", FALSE}, */
+ { "ABVVec4@ref2@dice@@","class dice::ref2::Vec4 const &",TRUE},
+ { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$0H@@@", "class CDB_GEN_BIG_ENUM_FLAG<enum CDB_WYSIWYG_BITS_ENUM,7>", TRUE},
+ { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$0HO@@@", "class CDB_GEN_BIG_ENUM_FLAG<enum CDB_WYSIWYG_BITS_ENUM,126>",TRUE},
+ { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$0HOA@@@", "class CDB_GEN_BIG_ENUM_FLAG<enum CDB_WYSIWYG_BITS_ENUM,2016>",TRUE},
+ { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$0HOAA@@@", "class CDB_GEN_BIG_ENUM_FLAG<enum CDB_WYSIWYG_BITS_ENUM,32256>",TRUE},
+ { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$01@@@", "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$01@@@", FALSE},
+/* { "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$011@@@", "?AV?$CDB_GEN_BIG_ENUM_FLAG@W4CDB_WYSIWYG_BITS_ENUM@@$011@@@",FALSE}, */
+ };
+ int i, num_test = (sizeof(demangle)/sizeof(struct _demangle));
+
+ for (i = 0; i < num_test; i++)
+ {
+ name = p__unDName(0, demangle[i].mangled, 0, pmalloc, pfree, 0x2800);
+ if (demangle[i].test_in_wine)
+ ok(name != NULL && !strcmp(name,demangle[i].result), "Got name \"%s\" for %d\n", name, i);
+ else
+ todo_wine ok(name != NULL && !strcmp(name,demangle[i].result), "Got name %s for %d\n", name, i);
+
+ }
+}
+
+/* Compare two strings treating multiple spaces (' ', ascii 0x20) in s2
+ as single space. Needed for test_demangle as __unDName() returns sometimes
+ two spaces instead of one in some older native msvcrt dlls. */
+static int strcmp_space(const char *s1, const char *s2)
+{
+ const char* s2start = s2;
+ do {
+ while (*s1 == *s2 && *s1) {
+ s1++;
+ s2++;
+ }
+ if (*s2 == ' ' && s2 > s2start && *(s2 - 1) == ' ')
+ s2++;
+ else
+ break;
+ } while (*s1 && *s2);
+ return *s1 - *s2;
+}
+
+static void test_demangle(void)
+{
+ static struct {const char* in; const char* out;} test[] = {
+{"??0bad_alloc@std@@QAE@ABV01@@Z", "public: __thiscall std::bad_alloc::bad_alloc(class std::bad_alloc const &)"},
+{"??0bad_alloc@std@@QAE@PBD@Z", "public: __thiscall std::bad_alloc::bad_alloc(char const *)"},
+{"??0bad_cast@@AAE@PBQBD@Z", "private: __thiscall bad_cast::bad_cast(char const * const *)"},
+{"??0bad_cast@@QAE@ABQBD@Z", "public: __thiscall bad_cast::bad_cast(char const * const &)"},
+{"??0bad_cast@@QAE@ABV0@@Z", "public: __thiscall bad_cast::bad_cast(class bad_cast const &)"},
+{"??0bad_exception@std@@QAE@ABV01@@Z", "public: __thiscall std::bad_exception::bad_exception(class std::bad_exception const &)"},
+{"??0bad_exception@std@@QAE@PBD@Z", "public: __thiscall std::bad_exception::bad_exception(char const *)"},
+{"??0?$basic_filebuf@DU?$char_traits@D@std@@@std@@QAE@ABV01@@Z", "public: __thiscall std::basic_filebuf<char,struct std::char_traits<char> >::basic_filebuf<char,struct std::char_traits<char> >(class std::basic_filebuf<char,struct std::char_traits<char> > const &)"},
+{"??0?$basic_filebuf@DU?$char_traits@D@std@@@std@@QAE@PAU_iobuf@@@Z", "public: __thiscall std::basic_filebuf<char,struct std::char_traits<char> >::basic_filebuf<char,struct std::char_traits<char> >(struct _iobuf *)"},
+{"??0?$basic_filebuf@DU?$char_traits@D@std@@@std@@QAE@W4_Uninitialized@1@@Z", "public: __thiscall std::basic_filebuf<char,struct std::char_traits<char> >::basic_filebuf<char,struct std::char_traits<char> >(enum std::_Uninitialized)"},
+{"??0?$basic_filebuf@GU?$char_traits@G@std@@@std@@QAE@ABV01@@Z", "public: __thiscall std::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> >::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> >(class std::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> > const &)"},
+{"??0?$basic_filebuf@GU?$char_traits@G@std@@@std@@QAE@PAU_iobuf@@@Z", "public: __thiscall std::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> >::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> >(struct _iobuf *)"},
+{"??0?$basic_filebuf@GU?$char_traits@G@std@@@std@@QAE@W4_Uninitialized@1@@Z", "public: __thiscall std::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> >::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> >(enum std::_Uninitialized)"},
+{"??0?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@ABV01@@Z", "public: __thiscall std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >(class std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> > const &)"},
+{"??0?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@1@H@Z", "public: __thiscall std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int)"},
+{"??0?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@H@Z", "public: __thiscall std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >(int)"},
+{"??0?$basic_stringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAE@ABV01@@Z", "public: __thiscall std::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >(class std::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > const &)"},
+{"??0?$basic_stringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAE@ABV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@1@H@Z", "public: __thiscall std::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >(class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > const &,int)"},
+{"??0?$basic_stringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAE@H@Z", "public: __thiscall std::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >(int)"},
+{"??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z", "public: __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >(class std::_Locinfo const &,unsigned int)"},
+{"??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z", "public: __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >(unsigned int)"},
+{"??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z", "public: __thiscall std::num_get<unsigned short,class std::istreambuf_iterator<unsigned short,struct std::char_traits<unsigned short> > >::num_get<unsigned short,class std::istreambuf_iterator<unsigned short,struct std::char_traits<unsigned short> > >(class std::_Locinfo const &,unsigned int)"},
+{"??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z", "public: __thiscall std::num_get<unsigned short,class std::istreambuf_iterator<unsigned short,struct std::char_traits<unsigned short> > >::num_get<unsigned short,class std::istreambuf_iterator<unsigned short,struct std::char_traits<unsigned short> > >(unsigned int)"},
+{"??0streambuf@@QAE@ABV0@@Z", "public: __thiscall streambuf::streambuf(class streambuf const &)"},
+{"??0strstreambuf@@QAE@ABV0@@Z", "public: __thiscall strstreambuf::strstreambuf(class strstreambuf const &)"},
+{"??0strstreambuf@@QAE@H@Z", "public: __thiscall strstreambuf::strstreambuf(int)"},
+{"??0strstreambuf@@QAE@P6APAXJ@ZP6AXPAX@Z@Z", "public: __thiscall strstreambuf::strstreambuf(void * (__cdecl*)(long),void (__cdecl*)(void *))"},
+{"??0strstreambuf@@QAE@PADH0@Z", "public: __thiscall strstreambuf::strstreambuf(char *,int,char *)"},
+{"??0strstreambuf@@QAE@PAEH0@Z", "public: __thiscall strstreambuf::strstreambuf(unsigned char *,int,unsigned char *)"},
+{"??0strstreambuf@@QAE@XZ", "public: __thiscall strstreambuf::strstreambuf(void)"},
+{"??1__non_rtti_object@std@@UAE@XZ", "public: virtual __thiscall std::__non_rtti_object::~__non_rtti_object(void)"},
+{"??1__non_rtti_object@@UAE@XZ", "public: virtual __thiscall __non_rtti_object::~__non_rtti_object(void)"},
+{"??1?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@UAE@XZ", "public: virtual __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::~num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >(void)"},
+{"??1?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@UAE@XZ", "public: virtual __thiscall std::num_get<unsigned short,class std::istreambuf_iterator<unsigned short,struct std::char_traits<unsigned short> > >::~num_get<unsigned short,class std::istreambuf_iterator<unsigned short,struct std::char_traits<unsigned short> > >(void)"},
+{"??4istream_withassign@@QAEAAV0@ABV0@@Z", "public: class istream_withassign & __thiscall istream_withassign::operator=(class istream_withassign const &)"},
+{"??4istream_withassign@@QAEAAVistream@@ABV1@@Z", "public: class istream & __thiscall istream_withassign::operator=(class istream const &)"},
+{"??4istream_withassign@@QAEAAVistream@@PAVstreambuf@@@Z", "public: class istream & __thiscall istream_withassign::operator=(class streambuf *)"},
+{"??5std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@0@AAV10@AAC@Z", "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl std::operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,signed char &)"},
+{"??5std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@0@AAV10@AAD@Z", "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl std::operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,char &)"},
+{"??5std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@0@AAV10@AAE@Z", "class std::basic_istream<char,struct std::char_traits<char> > & __cdecl std::operator>>(class std::basic_istream<char,struct std::char_traits<char> > &,unsigned char &)"},
+{"??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z", "public: class std::basic_ostream<unsigned short,struct std::char_traits<unsigned short> > & __thiscall std::basic_ostream<unsigned short,struct std::char_traits<unsigned short> >::operator<<(class std::ios_base & (__cdecl*)(class std::ios_base &))"},
+{"??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z", "public: class std::basic_ostream<unsigned short,struct std::char_traits<unsigned short> > & __thiscall std::basic_ostream<unsigned short,struct std::char_traits<unsigned short> >::operator<<(class std::basic_streambuf<unsigned short,struct std::char_traits<unsigned short> > *)"},
+{"??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PBX@Z", "public: class std::basic_ostream<unsigned short,struct std::char_traits<unsigned short> > & __thiscall std::basic_ostream<unsigned short,struct std::char_traits<unsigned short> >::operator<<(void const *)"},
+{"??_8?$basic_fstream@DU?$char_traits@D@std@@@std@@7B?$basic_ostream@DU?$char_traits@D@std@@@1@@", "const std::basic_fstream<char,struct std::char_traits<char> >::`vbtable'{for `std::basic_ostream<char,struct std::char_traits<char> >'}"},
+{"??_8?$basic_fstream@GU?$char_traits@G@std@@@std@@7B?$basic_istream@GU?$char_traits@G@std@@@1@@", "const std::basic_fstream<unsigned short,struct std::char_traits<unsigned short> >::`vbtable'{for `std::basic_istream<unsigned short,struct std::char_traits<unsigned short> >'}"},
+{"??_8?$basic_fstream@GU?$char_traits@G@std@@@std@@7B?$basic_ostream@GU?$char_traits@G@std@@@1@@", "const std::basic_fstream<unsigned short,struct std::char_traits<unsigned short> >::`vbtable'{for `std::basic_ostream<unsigned short,struct std::char_traits<unsigned short> >'}"},
+{"??9std@@YA_NPBDABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@0@@Z", "bool __cdecl std::operator!=(char const *,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)"},
+{"??9std@@YA_NPBGABV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@0@@Z", "bool __cdecl std::operator!=(unsigned short const *,class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > const &)"},
+{"??A?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEAADI@Z", "public: char & __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::operator[](unsigned int)"},
+{"??A?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEABDI@Z", "public: char const & __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::operator[](unsigned int)const "},
+{"??A?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEAAGI@Z", "public: unsigned short & __thiscall std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::operator[](unsigned int)"},
+{"??A?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QBEABGI@Z", "public: unsigned short const & __thiscall std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::operator[](unsigned int)const "},
+{"?abs@std@@YAMABV?$complex@M@1@@Z", "float __cdecl std::abs(class std::complex<float> const &)"},
+{"?abs@std@@YANABV?$complex@N@1@@Z", "double __cdecl std::abs(class std::complex<double> const &)"},
+{"?abs@std@@YAOABV?$complex@O@1@@Z", "long double __cdecl std::abs(class std::complex<long double> const &)"},
+{"?cin@std@@3V?$basic_istream@DU?$char_traits@D@std@@@1@A", "class std::basic_istream<char,struct std::char_traits<char> > std::cin"},
+{"?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAG@Z", "protected: virtual class std::istreambuf_iterator<char,struct std::char_traits<char> > __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::do_get(class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::ios_base &,int &,unsigned short &)const "},
+{"?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAI@Z", "protected: virtual class std::istreambuf_iterator<char,struct std::char_traits<char> > __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::do_get(class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::ios_base &,int &,unsigned int &)const "},
+{"?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z", "protected: virtual class std::istreambuf_iterator<char,struct std::char_traits<char> > __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::do_get(class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::ios_base &,int &,long &)const "},
+{"?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAK@Z", "protected: virtual class std::istreambuf_iterator<char,struct std::char_traits<char> > __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::do_get(class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::ios_base &,int &,unsigned long &)const "},
+{"?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAM@Z", "protected: virtual class std::istreambuf_iterator<char,struct std::char_traits<char> > __thiscall std::num_get<char,class std::istreambuf_iterator<char,struct std::char_traits<char> > >::do_get(class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::istreambuf_iterator<char,struct std::char_traits<char> >,class std::ios_base &,int &,float &)const "},
+{"?_query_new_handler@@YAP6AHI@ZXZ", "int (__cdecl*__cdecl _query_new_handler(void))(unsigned int)"},
+{"?register_callback@ios_base@std@@QAEXP6AXW4event@12@AAV12@H@ZH@Z", "public: void __thiscall std::ios_base::register_callback(void (__cdecl*)(enum std::ios_base::event,class std::ios_base &,int),int)"},
+{"?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@JW4seekdir@ios_base@2@@Z", "public: class std::basic_istream<char,struct std::char_traits<char> > & __thiscall std::basic_istream<char,struct std::char_traits<char> >::seekg(long,enum std::ios_base::seekdir)"},
+{"?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z", "public: class std::basic_istream<char,struct std::char_traits<char> > & __thiscall std::basic_istream<char,struct std::char_traits<char> >::seekg(class std::fpos<int>)"},
+{"?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@JW4seekdir@ios_base@2@@Z", "public: class std::basic_istream<unsigned short,struct std::char_traits<unsigned short> > & __thiscall std::basic_istream<unsigned short,struct std::char_traits<unsigned short> >::seekg(long,enum std::ios_base::seekdir)"},
+{"?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z", "public: class std::basic_istream<unsigned short,struct std::char_traits<unsigned short> > & __thiscall std::basic_istream<unsigned short,struct std::char_traits<unsigned short> >::seekg(class std::fpos<int>)"},
+{"?seekoff@?$basic_filebuf@DU?$char_traits@D@std@@@std@@MAE?AV?$fpos@H@2@JW4seekdir@ios_base@2@H@Z", "protected: virtual class std::fpos<int> __thiscall std::basic_filebuf<char,struct std::char_traits<char> >::seekoff(long,enum std::ios_base::seekdir,int)"},
+{"?seekoff@?$basic_filebuf@GU?$char_traits@G@std@@@std@@MAE?AV?$fpos@H@2@JW4seekdir@ios_base@2@H@Z", "protected: virtual class std::fpos<int> __thiscall std::basic_filebuf<unsigned short,struct std::char_traits<unsigned short> >::seekoff(long,enum std::ios_base::seekdir,int)"},
+{"?set_new_handler@@YAP6AXXZP6AXXZ@Z", "void (__cdecl*__cdecl set_new_handler(void (__cdecl*)(void)))(void)"},
+{"?str@?$basic_istringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z", "public: void __thiscall std::basic_istringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)"},
+{"?str@?$basic_istringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ", "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall std::basic_istringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(void)const "},
+{"?str@?$basic_istringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXABV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@@Z", "public: void __thiscall std::basic_istringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > const &)"},
+{"?str@?$basic_istringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QBE?AV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@XZ", "public: class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > __thiscall std::basic_istringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(void)const "},
+{"?str@?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z", "public: void __thiscall std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)"},
+{"?str@?$basic_ostringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ", "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall std::basic_ostringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(void)const "},
+{"?str@?$basic_ostringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXABV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@@Z", "public: void __thiscall std::basic_ostringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > const &)"},
+{"?str@?$basic_ostringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QBE?AV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@XZ", "public: class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > __thiscall std::basic_ostringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(void)const "},
+{"?str@?$basic_stringbuf@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z", "public: void __thiscall std::basic_stringbuf<char,struct std::char_traits<char>,class std::allocator<char> >::str(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)"},
+{"?str@?$basic_stringbuf@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ", "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall std::basic_stringbuf<char,struct std::char_traits<char>,class std::allocator<char> >::str(void)const "},
+{"?str@?$basic_stringbuf@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXABV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@@Z", "public: void __thiscall std::basic_stringbuf<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > const &)"},
+{"?str@?$basic_stringbuf@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QBE?AV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@XZ", "public: class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > __thiscall std::basic_stringbuf<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(void)const "},
+{"?str@?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAEXABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@@Z", "public: void __thiscall std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)"},
+{"?str@?$basic_stringstream@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@XZ", "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> >::str(void)const "},
+{"?str@?$basic_stringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QAEXABV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@@Z", "public: void __thiscall std::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > const &)"},
+{"?str@?$basic_stringstream@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@QBE?AV?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@2@XZ", "public: class std::basic_string<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> > __thiscall std::basic_stringstream<unsigned short,struct std::char_traits<unsigned short>,class std::allocator<unsigned short> >::str(void)const "},
+{"?_Sync@ios_base@std@@0_NA", "private: static bool std::ios_base::_Sync"},
+{"??_U@YAPAXI@Z", "void * __cdecl operator new[](unsigned int)"},
+{"??_V@YAXPAX@Z", "void __cdecl operator delete[](void *)"},
+{"??X?$_Complex_base@M@std@@QAEAAV01@ABM@Z", "public: class std::_Complex_base<float> & __thiscall std::_Complex_base<float>::operator*=(float const &)"},
+{"??Xstd@@YAAAV?$complex@M@0@AAV10@ABV10@@Z", "class std::complex<float> & __cdecl std::operator*=(class std::complex<float> &,class std::complex<float> const &)"},
+{"?aaa@@YAHAAUbbb@@@Z", "int __cdecl aaa(struct bbb &)"},
+{"?aaa@@YAHBAUbbb@@@Z", "int __cdecl aaa(struct bbb & volatile)"},
+{"?aaa@@YAHPAUbbb@@@Z", "int __cdecl aaa(struct bbb *)"},
+{"?aaa@@YAHQAUbbb@@@Z", "int __cdecl aaa(struct bbb * const)"},
+{"?aaa@@YAHRAUbbb@@@Z", "int __cdecl aaa(struct bbb * volatile)"},
+{"?aaa@@YAHSAUbbb@@@Z", "int __cdecl aaa(struct bbb * const volatile)"},
+{"??0aa.a@@QAE@XZ", "??0aa.a@@QAE@XZ"},
+{"??0aa$_3a@@QAE@XZ", "public: __thiscall aa$_3a::aa$_3a(void)"},
+{"??2?$aaa@AAUbbb@@AAUccc@@AAU2@@ddd@1eee@2@QAEHXZ", "public: int __thiscall eee::eee::ddd::ddd::aaa<struct bbb &,struct ccc &,struct ccc &>::operator new(void)"},
+{"?pSW@@3P6GHKPAX0PAU_tagSTACKFRAME@@0P6GH0K0KPAK@ZP6GPAX0K@ZP6GK0K@ZP6GK00PAU_tagADDRESS@@@Z@ZA", "int (__stdcall* pSW)(unsigned long,void *,void *,struct _tagSTACKFRAME *,void *,int (__stdcall*)(void *,unsigned long,void *,unsigned long,unsigned long *),void * (__stdcall*)(void *,unsigned long),unsigned long (__stdcall*)(void *,unsigned long),unsigned long (__stdcall*)(void *,void *,struct _tagADDRESS *))"},
+ };
+ int i, num_test = (sizeof(test)/sizeof(test[0]));
+ char* name;
+
+ for (i = 0; i < num_test; i++)
+ {
+ name = p__unDName(0, test[i].in, 0, pmalloc, pfree, 0);
+ ok(name != NULL && !strcmp_space(test[i].out, name),
+ "Got name \"%s\" for %d\n", name, i);
+ pfree(name);
+ }
+}
+
+START_TEST(cpp)
+{
+ InitFunctionPtrs();
+
+ test_exception();
+ test_bad_typeid();
+ test_bad_cast();
+ test___non_rtti_object();
+ test_type_info();
+ test_rtti();
+ test_demangle_datatype();
+ test_demangle();
+
+ if (hMsvcrt)
+ FreeLibrary(hMsvcrt);
+}
+#endif /* __i386__ */
--- /dev/null
+/*
+ * Unit test suite for dir functions
+ *
+ * Copyright 2006 CodeWeavers, Aric Stewart
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winnls.h>
+#include <process.h>
+#include <errno.h>
+
+void test_fullpath(void)
+{
+ char full[MAX_PATH];
+ char tmppath[MAX_PATH];
+ char level1[MAX_PATH];
+ char level2[MAX_PATH];
+ char teststring[MAX_PATH];
+ char *freeme;
+ BOOL rc,free1,free2;
+
+ free1=free2=TRUE;
+ GetTempPath(MAX_PATH,tmppath);
+ strcpy(level1,tmppath);
+ strcat(level1,"msvcrt-test\\");
+
+ rc = CreateDirectory(level1,NULL);
+ if (!rc && GetLastError()==ERROR_ALREADY_EXISTS)
+ free1=FALSE;
+
+ strcpy(level2,level1);
+ strcat(level2,"nextlevel\\");
+ rc = CreateDirectory(level2,NULL);
+ if (!rc && GetLastError()==ERROR_ALREADY_EXISTS)
+ free2=FALSE;
+ SetCurrentDirectory(level2);
+
+ ok(_fullpath(full,"test", MAX_PATH)!=NULL,"_fullpath failed\n");
+ strcpy(teststring,level2);
+ strcat(teststring,"test");
+ ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
+ ok(_fullpath(full,"\\test", MAX_PATH)!=NULL,"_fullpath failed\n");
+ strncpy(teststring,level2,3);
+ teststring[3]=0;
+ strcat(teststring,"test");
+ ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
+ ok(_fullpath(full,"..\\test", MAX_PATH)!=NULL,"_fullpath failed\n");
+ strcpy(teststring,level1);
+ strcat(teststring,"test");
+ ok(strcmp(full,teststring)==0,"Invalid Path returned %s\n",full);
+ ok(_fullpath(full,"..\\test", 10)==NULL,"_fullpath failed to generate error\n");
+
+ freeme = _fullpath(NULL,"test", 0);
+ ok(freeme!=NULL,"No path returned\n");
+ strcpy(teststring,level2);
+ strcat(teststring,"test");
+ ok(strcmp(freeme,teststring)==0,"Invalid Path returned %s\n",freeme);
+ free(freeme);
+
+ if (free2)
+ RemoveDirectory(level2);
+ if (free1)
+ RemoveDirectory(level1);
+}
+
+START_TEST(dir)
+{
+ test_fullpath();
+}
--- /dev/null
+/*
+ * Unit tests for C library environment routines
+ *
+ * Copyright 2004 Mike Hearn <mh@codeweavers.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include <stdlib.h>
+
+START_TEST(environ)
+{
+ ok( _putenv("cat=") == 0, "_putenv failed on deletion of nonexistent environment variable\n" );
+ ok( _putenv("cat=dog") == 0, "failed setting cat=dog\n" );
+ ok( strcmp(getenv("cat"), "dog") == 0, "getenv did not return 'dog'\n" );
+ ok( _putenv("cat=") == 0, "failed deleting cat\n" );
+
+ ok( _putenv("=") == -1, "should not accept '=' as input\n" );
+ ok( _putenv("=dog") == -1, "should not accept '=dog' as input\n" );
+
+ ok( getenv("nonexistent") == NULL, "getenv should fail with nonexistent var name\n" );
+}
--- /dev/null
+/*
+ * Unit test suite for file functions
+ *
+ * Copyright 2002 Bill Currie
+ * Copyright 2005 Paul Rupe
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winnls.h>
+#include <process.h>
+#include <errno.h>
+
+static void test_fdopen( void )
+{
+ static const char buffer[] = {0,1,2,3,4,5,6,7,8,9};
+ char ibuf[10];
+ int fd;
+ FILE *file;
+
+ fd = open ("fdopen.tst", O_WRONLY | O_CREAT | O_BINARY, _S_IREAD |_S_IWRITE);
+ write (fd, buffer, sizeof (buffer));
+ close (fd);
+
+ fd = open ("fdopen.tst", O_RDONLY | O_BINARY);
+ lseek (fd, 5, SEEK_SET);
+ file = fdopen (fd, "rb");
+ ok (fread (ibuf, 1, sizeof (buffer), file) == 5, "read wrong byte count\n");
+ ok (memcmp (ibuf, buffer + 5, 5) == 0, "read wrong bytes\n");
+ fclose (file);
+ unlink ("fdopen.tst");
+}
+
+static void test_fileops( void )
+{
+ static const char outbuffer[] = "0,1,2,3,4,5,6,7,8,9";
+ char buffer[256];
+ WCHAR wbuffer[256];
+ int fd;
+ FILE *file;
+ fpos_t pos;
+ int i, c;
+
+ fd = open ("fdopen.tst", O_WRONLY | O_CREAT | O_BINARY, _S_IREAD |_S_IWRITE);
+ write (fd, outbuffer, sizeof (outbuffer));
+ close (fd);
+
+ fd = open ("fdopen.tst", O_RDONLY | O_BINARY);
+ file = fdopen (fd, "rb");
+ ok(strlen(outbuffer) == (sizeof(outbuffer)-1),"strlen/sizeof error\n");
+ ok(fgets(buffer,sizeof(buffer),file) !=0,"fgets failed unexpected\n");
+ ok(fgets(buffer,sizeof(buffer),file) ==0,"fgets didn't signal EOF\n");
+ ok(feof(file) !=0,"feof doesn't signal EOF\n");
+ rewind(file);
+ ok(fgets(buffer,strlen(outbuffer),file) !=0,"fgets failed unexpected\n");
+ ok(lstrlenA(buffer) == lstrlenA(outbuffer) -1,"fgets didn't read right size\n");
+ ok(fgets(buffer,sizeof(outbuffer),file) !=0,"fgets failed unexpected\n");
+ ok(strlen(buffer) == 1,"fgets dropped chars\n");
+ ok(buffer[0] == outbuffer[strlen(outbuffer)-1],"fgets exchanged chars\n");
+
+ rewind(file);
+ for (i = 0, c = EOF; i < sizeof(outbuffer); i++)
+ {
+ ok((c = fgetc(file)) == outbuffer[i], "fgetc returned wrong data\n");
+ }
+ ok((c = fgetc(file)) == EOF, "getc did not return EOF\n");
+ ok(feof(file), "feof did not return EOF\n");
+ ok(ungetc(c, file) == EOF, "ungetc(EOF) did not return EOF\n");
+ ok(feof(file), "feof after ungetc(EOF) did not return EOF\n");
+ ok((c = fgetc(file)) == EOF, "getc did not return EOF\n");
+ c = outbuffer[sizeof(outbuffer) - 1];
+ ok(ungetc(c, file) == c, "ungetc did not return its input\n");
+ ok(!feof(file), "feof after ungetc returned EOF\n");
+ ok((c = fgetc(file)) != EOF, "getc after ungetc returned EOF\n");
+ ok(c == outbuffer[sizeof(outbuffer) - 1],
+ "getc did not return ungetc'd data\n");
+ ok(!feof(file), "feof after getc returned EOF prematurely\n");
+ ok((c = fgetc(file)) == EOF, "getc did not return EOF\n");
+ ok(feof(file), "feof after getc did not return EOF\n");
+
+ rewind(file);
+ ok(fgetpos(file,&pos) == 0, "fgetpos failed unexpected\n");
+ ok(pos == 0, "Unexpected result of fgetpos 0x%Lx\n", pos);
+ pos = (ULONGLONG)sizeof (outbuffer);
+ ok(fsetpos(file, &pos) == 0, "fsetpos failed unexpected\n");
+ ok(fgetpos(file,&pos) == 0, "fgetpos failed unexpected\n");
+ ok(pos == (ULONGLONG)sizeof (outbuffer), "Unexpected result of fgetpos 0x%Lx\n", pos);
+
+ fclose (file);
+ fd = open ("fdopen.tst", O_RDONLY | O_TEXT);
+ file = fdopen (fd, "rt"); /* open in TEXT mode */
+ ok(fgetws(wbuffer,sizeof(wbuffer),file) !=0,"fgetws failed unexpected\n");
+ ok(fgetws(wbuffer,sizeof(wbuffer),file) ==0,"fgetws didn't signal EOF\n");
+ ok(feof(file) !=0,"feof doesn't signal EOF\n");
+ rewind(file);
+ ok(fgetws(wbuffer,strlen(outbuffer),file) !=0,"fgetws failed unexpected\n");
+ ok(lstrlenW(wbuffer) == (lstrlenA(outbuffer) -1),"fgetws didn't read right size\n");
+ ok(fgetws(wbuffer,sizeof(outbuffer),file) !=0,"fgets failed unexpected\n");
+ ok(lstrlenW(wbuffer) == 1,"fgets dropped chars\n");
+ fclose (file);
+
+ file = fopen("fdopen.tst", "rb");
+ ok( file != NULL, "fopen failed\n");
+ /* sizeof(buffer) > content of file */
+ ok(fread(buffer, sizeof(buffer), 1, file) == 0, "fread test failed\n");
+ /* feof should be set now */
+ ok(feof(file), "feof after fread failed\n");
+ fclose (file);
+
+ unlink ("fdopen.tst");
+}
+
+static WCHAR* AtoW( char* p )
+{
+ WCHAR* buffer;
+ DWORD len = MultiByteToWideChar( CP_ACP, 0, p, -1, NULL, 0 );
+ buffer = malloc( len * sizeof(WCHAR) );
+ MultiByteToWideChar( CP_ACP, 0, p, -1, buffer, len );
+ return buffer;
+}
+
+static void test_fgetwc( void )
+{
+#define LLEN 512
+
+ char* tempf;
+ FILE *tempfh;
+ static const char mytext[]= "This is test_fgetwc\n";
+ WCHAR wtextW[LLEN+1];
+ WCHAR *mytextW = NULL, *aptr, *wptr;
+ BOOL diff_found = FALSE;
+ unsigned int i;
+
+ tempf=_tempnam(".","wne");
+ tempfh = fopen(tempf,"wt"); /* open in TEXT mode */
+ fputs(mytext,tempfh);
+ fclose(tempfh);
+ tempfh = fopen(tempf,"rt");
+ fgetws(wtextW,LLEN,tempfh);
+ mytextW = AtoW ((char*)mytext);
+ aptr = mytextW;
+ wptr = wtextW;
+
+ for (i=0; i<strlen(mytext); i++, aptr++, wptr++)
+ {
+ diff_found |= (*aptr != *wptr);
+ }
+ ok(!(diff_found), "fgetwc difference found in TEXT mode\n");
+ if(mytextW) free (mytextW);
+ fclose(tempfh);
+ unlink(tempf);
+}
+
+static void test_file_put_get( void )
+{
+ char* tempf;
+ FILE *tempfh;
+ static const char mytext[]= "This is a test_file_put_get\n";
+ static const char dostext[]= "This is a test_file_put_get\r\n";
+ char btext[LLEN];
+ WCHAR wtextW[LLEN+1];
+ WCHAR *mytextW = NULL, *aptr, *wptr;
+ BOOL diff_found = FALSE;
+ unsigned int i;
+
+ tempf=_tempnam(".","wne");
+ tempfh = fopen(tempf,"wt"); /* open in TEXT mode */
+ fputs(mytext,tempfh);
+ fclose(tempfh);
+ tempfh = fopen(tempf,"rb"); /* open in TEXT mode */
+ fgets(btext,LLEN,tempfh);
+ ok( strlen(mytext) + 1 == strlen(btext),"TEXT/BINARY mode not handled for write\n");
+ ok( btext[strlen(mytext)-1] == '\r', "CR not written\n");
+ fclose(tempfh);
+ tempfh = fopen(tempf,"wb"); /* open in BINARY mode */
+ fputs(dostext,tempfh);
+ fclose(tempfh);
+ tempfh = fopen(tempf,"rt"); /* open in TEXT mode */
+ fgets(btext,LLEN,tempfh);
+ ok(strcmp(btext, mytext) == 0,"_O_TEXT read doesn't strip CR\n");
+ fclose(tempfh);
+ tempfh = fopen(tempf,"rb"); /* open in TEXT mode */
+ fgets(btext,LLEN,tempfh);
+ ok(strcmp(btext, dostext) == 0,"_O_BINARY read doesn't preserve CR\n");
+
+ fclose(tempfh);
+ tempfh = fopen(tempf,"rt"); /* open in TEXT mode */
+ fgetws(wtextW,LLEN,tempfh);
+ mytextW = AtoW ((char*)mytext);
+ aptr = mytextW;
+ wptr = wtextW;
+
+ for (i=0; i<strlen(mytext); i++, aptr++, wptr++)
+ {
+ diff_found |= (*aptr != *wptr);
+ }
+ ok(!(diff_found), "fgetwc doesn't strip CR in TEXT mode\n");
+ if(mytextW) free (mytextW);
+ fclose(tempfh);
+ unlink(tempf);
+}
+
+static void test_file_write_read( void )
+{
+ char* tempf;
+ int tempfd;
+ static const char mytext[]= "This is test_file_write_read\nsecond line\n";
+ static const char dostext[]= "This is test_file_write_read\r\nsecond line\r\n";
+ char btext[LLEN];
+ int ret;
+
+ tempf=_tempnam(".","wne");
+ tempfd = _open(tempf,_O_CREAT|_O_TRUNC|_O_TEXT|_O_RDWR,
+ _S_IREAD | _S_IWRITE);
+ ok( tempfd != -1,
+ "Can't open '%s': %d\n", tempf, errno); /* open in TEXT mode */
+ ok(_write(tempfd,mytext,strlen(mytext)) == lstrlenA(mytext),
+ "_write _O_TEXT bad return value\n");
+ _close(tempfd);
+ tempfd = _open(tempf,_O_RDONLY|_O_BINARY,0); /* open in BINARY mode */
+ ok(_read(tempfd,btext,LLEN) == lstrlenA(dostext),
+ "_read _O_BINARY got bad length\n");
+ ok( memcmp(dostext,btext,strlen(dostext)) == 0,
+ "problems with _O_TEXT _write / _O_BINARY _read\n");
+ ok( btext[strlen(dostext)-2] == '\r', "CR not written or read\n");
+ _close(tempfd);
+ tempfd = _open(tempf,_O_RDONLY|_O_TEXT); /* open in TEXT mode */
+ ok(_read(tempfd,btext,LLEN) == lstrlenA(mytext),
+ "_read _O_TEXT got bad length\n");
+ ok( memcmp(mytext,btext,strlen(mytext)) == 0,
+ "problems with _O_TEXT _write / _read\n");
+ _close(tempfd);
+
+ memset(btext, 0, LLEN);
+ tempfd = _open(tempf,_O_APPEND|_O_RDWR); /* open for APPEND in default mode */
+ ok(tell(tempfd) == 0, "bad position %lu expecting 0\n", tell(tempfd));
+ ok(_read(tempfd,btext,LLEN) == lstrlenA(mytext), "_read _O_APPEND got bad length\n");
+ ok( memcmp(mytext,btext,strlen(mytext)) == 0, "problems with _O_APPEND _read\n");
+ _close(tempfd);
+
+ /* Test reading only \n or \r */
+ tempfd = _open(tempf,_O_RDONLY|_O_TEXT); /* open in TEXT mode */
+ _lseek(tempfd, -1, FILE_END);
+ ret = _read(tempfd,btext,LLEN);
+ ok(ret == 1, "_read expected 1 got bad length: %d\n", ret);
+ _lseek(tempfd, -2, FILE_END);
+ ret = _read(tempfd,btext,LLEN);
+ ok(ret == 1 && *btext == '\n', "_read expected '\\n' got bad length: %d\n", ret);
+ _lseek(tempfd, -3, FILE_END);
+ ret = _read(tempfd,btext,2);
+ todo_wine ok(ret == 1 && *btext == 'e', "_read expected 'e' got \"%.*s\" bad length: %d\n", ret, btext, ret);
+ todo_wine ok(tell(tempfd) == 42, "bad position %lu expecting 42\n", tell(tempfd));
+ _close(tempfd);
+
+ ret = unlink(tempf);
+ ok( ret == 0 ,"Can't unlink '%s': %d\n", tempf, errno);
+
+ tempf=_tempnam(".","wne");
+ tempfd = _open(tempf,_O_CREAT|_O_TRUNC|_O_BINARY|_O_RDWR,0);
+ ok( tempfd != -1,
+ "Can't open '%s': %d\n", tempf, errno); /* open in BINARY mode */
+ ok(_write(tempfd,dostext,strlen(dostext)) == lstrlenA(dostext),
+ "_write _O_BINARY bad return value\n");
+ _close(tempfd);
+ tempfd = _open(tempf,_O_RDONLY|_O_BINARY,0); /* open in BINARY mode */
+ ok(_read(tempfd,btext,LLEN) == lstrlenA(dostext),
+ "_read _O_BINARY got bad length\n");
+ ok( memcmp(dostext,btext,strlen(dostext)) == 0,
+ "problems with _O_BINARY _write / _read\n");
+ ok( btext[strlen(dostext)-2] == '\r', "CR not written or read\n");
+ _close(tempfd);
+ tempfd = _open(tempf,_O_RDONLY|_O_TEXT); /* open in TEXT mode */
+ ok(_read(tempfd,btext,LLEN) == lstrlenA(mytext),
+ "_read _O_TEXT got bad length\n");
+ ok( memcmp(mytext,btext,strlen(mytext)) == 0,
+ "problems with _O_BINARY _write / _O_TEXT _read\n");
+ _close(tempfd);
+
+ ret =_chmod (tempf, _S_IREAD | _S_IWRITE);
+ ok( ret == 0,
+ "Can't chmod '%s' to read-write: %d\n", tempf, errno);
+ ret = unlink(tempf);
+ ok( ret == 0 ,"Can't unlink '%s': %d\n", tempf, errno);
+}
+
+static void test_file_inherit_child(const char* fd_s)
+{
+ int fd = atoi(fd_s);
+ char buffer[32];
+ int ret;
+
+ ret =write(fd, "Success", 8);
+ ok( ret == 8, "Couldn't write in child process on %d (%s)\n", fd, strerror(errno));
+ lseek(fd, 0, SEEK_SET);
+ ok(read(fd, buffer, sizeof (buffer)) == 8, "Couldn't read back the data\n");
+ ok(memcmp(buffer, "Success", 8) == 0, "Couldn't read back the data\n");
+}
+
+static void test_file_inherit_child_no(const char* fd_s)
+{
+ int fd = atoi(fd_s);
+ int ret;
+
+ ret = write(fd, "Success", 8);
+ ok( ret == -1 && errno == EBADF,
+ "Wrong write result in child process on %d (%s)\n", fd, strerror(errno));
+}
+
+static void test_file_inherit( const char* selfname )
+{
+ int fd;
+ const char* arg_v[5];
+ char buffer[16];
+
+ fd = open ("fdopen.tst", O_CREAT | O_RDWR | O_BINARY, _S_IREAD |_S_IWRITE);
+ ok(fd != -1, "Couldn't create test file\n");
+ arg_v[0] = selfname;
+ arg_v[1] = "tests/file.c";
+ arg_v[2] = buffer; sprintf(buffer, "%d", fd);
+ arg_v[3] = 0;
+ _spawnvp(_P_WAIT, selfname, arg_v);
+ ok(tell(fd) == 8, "bad position %lu expecting 8\n", tell(fd));
+ lseek(fd, 0, SEEK_SET);
+ ok(read(fd, buffer, sizeof (buffer)) == 8 && memcmp(buffer, "Success", 8) == 0, "Couldn't read back the data\n");
+ close (fd);
+ ok(unlink("fdopen.tst") == 0, "Couldn't unlink\n");
+
+ fd = open ("fdopen.tst", O_CREAT | O_RDWR | O_BINARY | O_NOINHERIT, _S_IREAD |_S_IWRITE);
+ ok(fd != -1, "Couldn't create test file\n");
+ arg_v[0] = selfname;
+ arg_v[1] = "tests/file.c";
+ arg_v[2] = buffer; sprintf(buffer, "%d", fd);
+ arg_v[3] = buffer;
+ arg_v[4] = 0;
+ _spawnvp(_P_WAIT, selfname, arg_v);
+ ok(tell(fd) == 0, "bad position %lu expecting 0\n", tell(fd));
+ ok(read(fd, buffer, sizeof (buffer)) == 0, "Found unexpected data (%s)\n", buffer);
+ close (fd);
+ ok(unlink("fdopen.tst") == 0, "Couldn't unlink\n");
+}
+
+static void test_tmpnam( void )
+{
+ char name[MAX_PATH] = "abc";
+ char *res;
+
+ res = tmpnam(NULL);
+ ok(res != NULL, "tmpnam returned NULL\n");
+ ok(res[0] == '\\', "first character is not a backslash\n");
+ ok(strchr(res+1, '\\') == 0, "file not in the root directory\n");
+ ok(res[strlen(res)-1] == '.', "first call - last character is not a dot\n");
+
+ res = tmpnam(name);
+ ok(res != NULL, "tmpnam returned NULL\n");
+ ok(res == name, "supplied buffer was not used\n");
+ ok(res[0] == '\\', "first character is not a backslash\n");
+ ok(strchr(res+1, '\\') == 0, "file not in the root directory\n");
+ ok(res[strlen(res)-1] != '.', "second call - last character is a dot\n");
+}
+
+static void test_chsize( void )
+{
+ int fd;
+ long cur, pos, count;
+ char temptext[] = "012345678";
+ char *tempfile = _tempnam( ".", "tst" );
+
+ ok( tempfile != NULL, "Couldn't create test file: %s\n", tempfile );
+
+ fd = _open( tempfile, _O_CREAT|_O_TRUNC|_O_RDWR, _S_IREAD|_S_IWRITE );
+ ok( fd > 0, "Couldn't open test file\n" );
+
+ count = _write( fd, temptext, sizeof(temptext) );
+ ok( count > 0, "Couldn't write to test file\n" );
+
+ /* get current file pointer */
+ cur = _lseek( fd, 0, SEEK_CUR );
+
+ /* make the file smaller */
+ ok( _chsize( fd, sizeof(temptext) / 2 ) == 0, "_chsize() failed\n" );
+
+ pos = _lseek( fd, 0, SEEK_CUR );
+ ok( cur == pos, "File pointer changed from: %ld to: %ld\n", cur, pos );
+ ok( _filelength( fd ) == sizeof(temptext) / 2, "Wrong file size\n" );
+
+ /* enlarge the file */
+ ok( _chsize( fd, sizeof(temptext) * 2 ) == 0, "_chsize() failed\n" );
+
+ pos = _lseek( fd, 0, SEEK_CUR );
+ ok( cur == pos, "File pointer changed from: %ld to: %ld\n", cur, pos );
+ ok( _filelength( fd ) == sizeof(temptext) * 2, "Wrong file size\n" );
+
+ _close( fd );
+ _unlink( tempfile );
+}
+
+static void test_fopen_fclose_fcloseall( void )
+{
+ char fname1[] = "empty1";
+ char fname2[] = "empty2";
+ char fname3[] = "empty3";
+ FILE *stream1, *stream2, *stream3, *stream4;
+ int ret, numclosed;
+
+ /* testing fopen() */
+ stream1 = fopen(fname1, "w+");
+ ok(stream1 != NULL, "The file '%s' was not opened\n", fname1);
+ stream2 = fopen(fname2, "w ");
+ ok(stream2 != NULL, "The file '%s' was not opened\n", fname2 );
+ _unlink(fname3);
+ stream3 = fopen(fname3, "r");
+ ok(stream3 == NULL, "The file '%s' shouldn't exist before\n", fname3 );
+ stream3 = fopen(fname3, "w+");
+ ok(stream3 != NULL, "The file '%s' should be opened now\n", fname3 );
+ errno = 0xfaceabad;
+ stream4 = fopen("", "w+");
+ ok(stream4 == NULL && errno == ENOENT,
+ "filename is empty, errno = %d (expected 2)\n", errno);
+ errno = 0xfaceabad;
+ stream4 = fopen(NULL, "w+");
+ ok(stream4 == NULL && (errno == EINVAL || errno == ENOENT),
+ "filename is NULL, errno = %d (expected 2 or 22)\n", errno);
+
+ /* testing fclose() */
+ ret = fclose(stream2);
+ ok(ret == 0, "The file '%s' was not closed\n", fname2);
+ ret = fclose(stream3);
+ ok(ret == 0, "The file '%s' was not closed\n", fname3);
+ ret = fclose(stream2);
+ ok(ret == EOF, "Closing file '%s' returned %d\n", fname2, ret);
+ ret = fclose(stream3);
+ ok(ret == EOF, "Closing file '%s' returned %d\n", fname3, ret);
+
+ /* testing fcloseall() */
+ numclosed = _fcloseall();
+ /* fname1 should be closed here */
+ ok(numclosed == 1, "Number of files closed by fcloseall(): %u\n", numclosed);
+ numclosed = _fcloseall();
+ ok(numclosed == 0, "Number of files closed by fcloseall(): %u\n", numclosed);
+
+ ok(_unlink(fname1) == 0, "Couldn't unlink file named '%s'\n", fname1);
+ ok(_unlink(fname2) == 0, "Couldn't unlink file named '%s'\n", fname2);
+ ok(_unlink(fname3) == 0, "Couldn't unlink file named '%s'\n", fname3);
+}
+
+START_TEST(file)
+{
+ int arg_c;
+ char** arg_v;
+
+ arg_c = winetest_get_mainargs( &arg_v );
+
+ /* testing low-level I/O */
+ if (arg_c >= 3)
+ {
+ if (arg_c == 3) test_file_inherit_child(arg_v[2]);
+ else test_file_inherit_child_no(arg_v[2]);
+ return;
+ }
+ test_file_inherit(arg_v[0]);
+ test_file_write_read();
+ test_chsize();
+
+ /* testing stream I/O */
+ test_fdopen();
+ test_fopen_fclose_fcloseall();
+ test_fileops();
+ test_fgetwc();
+ test_file_put_get();
+ test_tmpnam();
+}
--- /dev/null
+/*
+ * Unit test suite for memory functions
+ *
+ * Copyright 2003 Dimitrie O. Paun
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include "wine/test.h"
+
+START_TEST(heap)
+{
+ void *mem;
+
+ mem = malloc(0);
+ ok(mem != NULL, "memory not allocated for size 0\n");
+
+ mem = realloc(NULL, 10);
+ ok(mem != NULL, "memory not allocated\n");
+
+ mem = realloc(mem, 20);
+ ok(mem != NULL, "memory not reallocated\n");
+
+ mem = realloc(mem, 0);
+ ok(mem == NULL, "memory not freed\n");
+
+ mem = realloc(NULL, 0);
+ ok(mem != NULL, "memory not (re)allocated for size 0\n");
+}
--- /dev/null
+<module name="msvcrt_winetest" type="win32cui" installbase="bin" installname="msvcrt_winetest.exe" allowwarnings="true">
+ <include base="msvcrt_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>cpp.c</file>
+ <file>dir.c</file>
+ <file>environ.c</file>
+ <file>file.c</file>
+ <file>heap.c</file>
+ <file>printf.c</file>
+ <file>scanf.c</file>
+ <file>string.c</file>
+ <file>testlist.c</file>
+ <file>time.c</file>
+</module>
--- /dev/null
+# Microsoft Developer Studio Project File - Name="msvcrt_test" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=msvcrt_test - Win32 Wine Headers\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "msvcrt_test.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "msvcrt_test.mak" CFG="msvcrt_test - Win32 Wine Headers"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "msvcrt_test - Win32 MSVC Headers" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "msvcrt_test - Win32 Wine Headers" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+!IF "$(CFG)" == "msvcrt_test - Win32 MSVC Headers"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Output\Win32_MSVC_Headers"\r
+# PROP BASE Intermediate_Dir "Output\Win32_MSVC_Headers"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Output\Win32_MSVC_Headers"\r
+# PROP Intermediate_Dir "Output\Win32_MSVC_Headers"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WINVER=0x0501" /D "_WIN32_WINNT=0x0501" /D "_WIN32_IE=0x0600" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ..\..\..\Output\\Win32_MSVC_Headers /D "WINVER=0x0501" /D "_WIN32_WINNT=0x0501" /D "_WIN32_IE=0x0600" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "_MSVCRT_TEST_" /D "__WINE_USE_NATIVE_HEADERS" /D __WINETEST_OUTPUT_DIR=\"Output\\Win32_MSVC_Headers\" /D "__i386__" /D "_X86_" /D inline=__inline /FR /FD /GZ /c\r
+# ADD BASE RSC /l 0x41d /d "_DEBUG"\r
+# ADD RSC /l 0x41d /i "..\..\..\Output\\Win32_MSVC_Headers" /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+\r
+!ELSEIF "$(CFG)" == "msvcrt_test - Win32 Wine Headers"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Output\Win32_Wine_Headers"\r
+# PROP BASE Intermediate_Dir "Output\Win32_Wine_Headers"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Output\Win32_Wine_Headers"\r
+# PROP Intermediate_Dir "Output\Win32_Wine_Headers"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WINVER=0x0501" /D "_WIN32_WINNT=0x0501" /D "_WIN32_IE=0x0600" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /I ..\..\..\Output\\Win32_Wine_Headers /I ..\..\..\include /D "WINVER=0x0501" /D "_WIN32_WINNT=0x0501" /D "_WIN32_IE=0x0600" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "_MSVCRT_TEST_" /D __WINETEST_OUTPUT_DIR=\"Output\\Win32_Wine_Headers\" /D "__i386__" /D "_X86_" /D inline=__inline /FR /FD /GZ /c\r
+# ADD BASE RSC /l 0x41d /d "_DEBUG"\r
+# ADD RSC /l 0x41d /i "..\..\..\Output\\Win32_Wine_Headers" /i "..\..\..\include" /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "msvcrt_test - Win32 MSVC Headers"\r
+# Name "msvcrt_test - Win32 Wine Headers"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=.\cpp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\\\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\testlist.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# End Group\r
+# End Target\r
+# End Project\r
--- /dev/null
+/*
+ * Conformance tests for *printf functions.
+ *
+ * Copyright 2002 Uwe Bonnes
+ * Copyright 2004 Aneurin Price
+ * Copyright 2005 Mike McCormack
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+
+static void test_sprintf( void )
+{
+ char buffer[100];
+ const char *format;
+ double pnumber=789456123;
+ int x, r;
+ WCHAR wide[] = { 'w','i','d','e',0};
+
+ format = "%+#23.15e";
+ r = sprintf(buffer,format,pnumber);
+ todo_wine {
+ ok(!strcmp(buffer,"+7.894561230000000e+008"),"exponent format incorrect\n");
+ }
+ ok( r==23, "return count wrong\n");
+
+ format = "%I64d";
+ r = sprintf(buffer,format,((ULONGLONG)0xffffffff)*0xffffffff);
+ ok(!strcmp(buffer,"-8589934591"),"Problem with long long\n");
+ ok( r==11, "return count wrong\n");
+
+ format = "%+8I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer," +100") && r==8,"+8I64d failed: '%s'\n", buffer);
+
+ format = "%+.8I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"+00000100") && r==9,"+.8I64d failed: '%s'\n", buffer);
+
+ format = "%+10.8I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer," +00000100") && r==10,"+10.8I64d failed: '%s'\n", buffer);
+ format = "%_1I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"_1I64d") && r==6,"_1I64d failed\n");
+
+ format = "%-1.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"-00100") && r==6,"-1.5I64d failed: '%s'\n", buffer);
+
+ format = "%5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer," 100") && r==5,"5I64d failed: '%s'\n", buffer);
+
+ format = "%5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer," -100") && r==5,"5I64d failed: '%s'\n", buffer);
+
+ format = "%-5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"100 ") && r==5,"-5I64d failed: '%s'\n", buffer);
+
+ format = "%-5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"-100 ") && r==5,"-5I64d failed: '%s'\n", buffer);
+
+ format = "%-.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"00100") && r==5,"-.5I64d failed: '%s'\n", buffer);
+
+ format = "%-.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"-00100") && r==6,"-.5I64d failed: '%s'\n", buffer);
+
+ format = "%-8.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"00100 ") && r==8,"-8.5I64d failed: '%s'\n", buffer);
+
+ format = "%-8.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"-00100 ") && r==8,"-8.5I64d failed: '%s'\n", buffer);
+
+ format = "%05I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"00100") && r==5,"05I64d failed: '%s'\n", buffer);
+
+ format = "%05I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"-0100") && r==5,"05I64d failed: '%s'\n", buffer);
+
+ format = "% I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer," 100") && r==4,"' I64d' failed: '%s'\n", buffer);
+
+ format = "% I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"-100") && r==4,"' I64d' failed: '%s'\n", buffer);
+
+ format = "% 5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer," 100") && r==5,"' 5I64d' failed: '%s'\n", buffer);
+
+ format = "% 5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer," -100") && r==5,"' 5I64d' failed: '%s'\n", buffer);
+
+ format = "% .5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer," 00100") && r==6,"' .5I64d' failed: '%s'\n", buffer);
+
+ format = "% .5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"-00100") && r==6,"' .5I64d' failed: '%s'\n", buffer);
+
+ format = "% 8.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer," 00100") && r==8,"' 8.5I64d' failed: '%s'\n", buffer);
+
+ format = "% 8.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer," -00100") && r==8,"' 8.5I64d' failed: '%s'\n", buffer);
+
+ format = "%.0I64d";
+ r = sprintf(buffer,format,(LONGLONG)0);
+ ok(r==0,".0I64d failed: '%s'\n", buffer);
+
+ format = "%#+21.18I64x";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer," 0x00ffffffffffffff9c") && r==21,"#+21.18I64x failed: '%s'\n", buffer);
+
+ format = "%#.25I64o";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"0001777777777777777777634") && r==25,"#.25I64o failed: '%s'\n", buffer);
+
+ format = "%#+24.20I64o";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer," 01777777777777777777634") && r==24,"#+24.20I64o failed: '%s'\n", buffer);
+
+ format = "%#+18.21I64X";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"0X00000FFFFFFFFFFFFFF9C") && r==23,"#+18.21I64X failed: '%s '\n", buffer);
+
+ format = "%#+20.24I64o";
+ r = sprintf(buffer,format,(LONGLONG)-100);
+ ok(!strcmp(buffer,"001777777777777777777634") && r==24,"#+20.24I64o failed: '%s'\n", buffer);
+
+ format = "%#+25.22I64u";
+ r = sprintf(buffer,format,(LONGLONG)-1);
+ ok(!strcmp(buffer," 0018446744073709551615") && r==25,"#+25.22I64u conversion failed: '%s'\n", buffer);
+
+ format = "%#+25.22I64u";
+ r = sprintf(buffer,format,(LONGLONG)-1);
+ ok(!strcmp(buffer," 0018446744073709551615") && r==25,"#+25.22I64u failed: '%s'\n", buffer);
+
+ format = "%#+30.25I64u";
+ r = sprintf(buffer,format,(LONGLONG)-1);
+ ok(!strcmp(buffer," 0000018446744073709551615") && r==30,"#+30.25I64u failed: '%s'\n", buffer);
+
+ format = "%+#25.22I64d";
+ r = sprintf(buffer,format,(LONGLONG)-1);
+ ok(!strcmp(buffer," -0000000000000000000001") && r==25,"+#25.22I64d failed: '%s'\n", buffer);
+
+ format = "%#-8.5I64o";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"00144 ") && r==8,"-8.5I64o failed: '%s'\n", buffer);
+
+ format = "%#-+ 08.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"+00100 ") && r==8,"'#-+ 08.5I64d failed: '%s'\n", buffer);
+
+ format = "%#-+ 08.5I64d";
+ r = sprintf(buffer,format,(LONGLONG)100);
+ ok(!strcmp(buffer,"+00100 ") && r==8,"#-+ 08.5I64d failed: '%s'\n", buffer);
+
+ format = "%lld";
+ r = sprintf(buffer,format,((ULONGLONG)0xffffffff)*0xffffffff);
+ ok(!strcmp(buffer, "1"), "Problem with \"ll\" interpretation\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%I";
+ r = sprintf(buffer,format,1);
+ ok(!strcmp(buffer, "I"), "Problem with \"I\" interpretation\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%I0d";
+ r = sprintf(buffer,format,1);
+ ok(!strcmp(buffer,"I0d"),"I0d failed\n");
+ ok( r==3, "return count wrong\n");
+
+ format = "%I32d";
+ r = sprintf(buffer,format,1);
+ ok(!strcmp(buffer,"1"),"I32d failed\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%I64D";
+ r = sprintf(buffer,format,(LONGLONG)-1);
+ ok(!strcmp(buffer,"D"),"I64D failed: %s\n",buffer);
+ ok( r==1, "return count wrong\n");
+
+ format = "% d";
+ r = sprintf(buffer,format,1);
+ ok(!strcmp(buffer, " 1"),"Problem with sign place-holder: '%s'\n",buffer);
+ ok( r==2, "return count wrong\n");
+
+ format = "%+ d";
+ r = sprintf(buffer,format,1);
+ ok(!strcmp(buffer, "+1"),"Problem with sign flags: '%s'\n",buffer);
+ ok( r==2, "return count wrong\n");
+
+ format = "%S";
+ r = sprintf(buffer,format,wide);
+ ok(!strcmp(buffer,"wide"),"Problem with wide string format\n");
+ ok( r==4, "return count wrong\n");
+
+ format = "%04c";
+ r = sprintf(buffer,format,'1');
+ ok(!strcmp(buffer,"0001"),"Character not zero-prefixed \"%s\"\n",buffer);
+ ok( r==4, "return count wrong\n");
+
+ format = "%-04c";
+ r = sprintf(buffer,format,'1');
+ ok(!strcmp(buffer,"1 "),"Character zero-padded and/or not left-adjusted \"%s\"\n",buffer);
+ ok( r==4, "return count wrong\n");
+
+ format = "%p";
+ r = sprintf(buffer,format,(void *)57);
+ ok(!strcmp(buffer,"00000039"),"Pointer formatted incorrectly \"%s\"\n",buffer);
+ ok( r==8, "return count wrong\n");
+
+ format = "%#012p";
+ r = sprintf(buffer,format,(void *)57);
+ ok(!strcmp(buffer," 0X00000039"),"Pointer formatted incorrectly\n");
+ ok( r==12, "return count wrong\n");
+
+ format = "%Fp";
+ r = sprintf(buffer,format,(void *)57);
+ ok(!strcmp(buffer,"00000039"),"Pointer formatted incorrectly \"%s\"\n",buffer);
+ ok( r==8, "return count wrong\n");
+
+ format = "%04s";
+ r = sprintf(buffer,format,"foo");
+ ok(!strcmp(buffer,"0foo"),"String not zero-prefixed \"%s\"\n",buffer);
+ ok( r==4, "return count wrong\n");
+
+ format = "%.1s";
+ r = sprintf(buffer,format,"foo");
+ ok(!strcmp(buffer,"f"),"Precision ignored \"%s\"\n",buffer);
+ ok( r==1, "return count wrong\n");
+
+ format = "%.*s";
+ r = sprintf(buffer,format,1,"foo");
+ ok(!strcmp(buffer,"f"),"Precision ignored \"%s\"\n",buffer);
+ ok( r==1, "return count wrong\n");
+
+ format = "%#-012p";
+ r = sprintf(buffer,format,(void *)57);
+ ok(!strcmp(buffer,"0X00000039 "),"Pointer formatted incorrectly\n");
+ ok( r==12, "return count wrong\n");
+
+ format = "hello";
+ r = sprintf(buffer, format);
+ ok(!strcmp(buffer,"hello"), "failed\n");
+ ok( r==5, "return count wrong\n");
+
+ format = "%ws";
+ r = sprintf(buffer, format, wide);
+ ok(!strcmp(buffer,"wide"), "failed\n");
+ ok( r==4, "return count wrong\n");
+
+ format = "%-10ws";
+ r = sprintf(buffer, format, wide );
+ ok(!strcmp(buffer,"wide "), "failed\n");
+ ok( r==10, "return count wrong\n");
+
+ format = "%10ws";
+ r = sprintf(buffer, format, wide );
+ ok(!strcmp(buffer," wide"), "failed\n");
+ ok( r==10, "return count wrong\n");
+
+ format = "%#+ -03whlls";
+ r = sprintf(buffer, format, wide );
+ ok(!strcmp(buffer,"wide"), "failed\n");
+ ok( r==4, "return count wrong\n");
+
+ format = "%w0s";
+ r = sprintf(buffer, format, wide );
+ ok(!strcmp(buffer,"0s"), "failed\n");
+ ok( r==2, "return count wrong\n");
+
+ format = "%w-s";
+ r = sprintf(buffer, format, wide );
+ ok(!strcmp(buffer,"-s"), "failed\n");
+ ok( r==2, "return count wrong\n");
+
+ format = "%ls";
+ r = sprintf(buffer, format, wide );
+ ok(!strcmp(buffer,"wide"), "failed\n");
+ ok( r==4, "return count wrong\n");
+
+ format = "%Ls";
+ r = sprintf(buffer, format, "not wide" );
+ ok(!strcmp(buffer,"not wide"), "failed\n");
+ ok( r==8, "return count wrong\n");
+
+ format = "%b";
+ r = sprintf(buffer, format);
+ ok(!strcmp(buffer,"b"), "failed\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%3c";
+ r = sprintf(buffer, format,'a');
+ ok(!strcmp(buffer," a"), "failed\n");
+ ok( r==3, "return count wrong\n");
+
+ format = "%3d";
+ r = sprintf(buffer, format,1234);
+ ok(!strcmp(buffer,"1234"), "failed\n");
+ ok( r==4, "return count wrong\n");
+
+ format = "%3h";
+ r = sprintf(buffer, format);
+ ok(!strcmp(buffer,""), "failed\n");
+ ok( r==0, "return count wrong\n");
+
+ format = "%j%k%m%q%r%t%v%y%z";
+ r = sprintf(buffer, format);
+ ok(!strcmp(buffer,"jkmqrtvyz"), "failed\n");
+ ok( r==9, "return count wrong\n");
+
+ format = "asdf%n";
+ x = 0;
+ r = sprintf(buffer, format, &x );
+ ok(x == 4, "should write to x\n");
+ ok(!strcmp(buffer,"asdf"), "failed\n");
+ ok( r==4, "return count wrong\n");
+
+ format = "%-1d";
+ r = sprintf(buffer, format,2);
+ ok(!strcmp(buffer,"2"), "failed\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%2.4f";
+ r = sprintf(buffer, format,8.6);
+ ok(!strcmp(buffer,"8.6000"), "failed\n");
+ ok( r==6, "return count wrong\n");
+
+ format = "%0f";
+ r = sprintf(buffer, format,0.6);
+ ok(!strcmp(buffer,"0.600000"), "failed\n");
+ ok( r==8, "return count wrong\n");
+
+ format = "%.0f";
+ r = sprintf(buffer, format,0.6);
+ ok(!strcmp(buffer,"1"), "failed\n");
+ ok( r==1, "return count wrong\n");
+
+ todo_wine {
+ format = "%2.4e";
+ r = sprintf(buffer, format,8.6);
+ ok(!strcmp(buffer,"8.6000e+000"), "failed\n");
+ ok( r==11, "return count wrong\n");
+ }
+
+ format = "%2.4g";
+ r = sprintf(buffer, format,8.6);
+ ok(!strcmp(buffer,"8.6"), "failed\n");
+ ok( r==3, "return count wrong\n");
+
+ format = "%-i";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,"-1"), "failed\n");
+ ok( r==2, "return count wrong\n");
+
+ format = "%-i";
+ r = sprintf(buffer, format,1);
+ ok(!strcmp(buffer,"1"), "failed\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%+i";
+ r = sprintf(buffer, format,1);
+ ok(!strcmp(buffer,"+1"), "failed\n");
+ ok( r==2, "return count wrong\n");
+
+ format = "%o";
+ r = sprintf(buffer, format,10);
+ ok(!strcmp(buffer,"12"), "failed\n");
+ ok( r==2, "return count wrong\n");
+
+ format = "%p";
+ r = sprintf(buffer, format,0);
+ ok(!strcmp(buffer,"00000000"), "failed\n");
+ ok( r==8, "return count wrong\n");
+
+ format = "%s";
+ r = sprintf(buffer, format,0);
+ ok(!strcmp(buffer,"(null)"), "failed\n");
+ ok( r==6, "return count wrong\n");
+
+ format = "%s";
+ r = sprintf(buffer, format,"%%%%");
+ ok(!strcmp(buffer,"%%%%"), "failed\n");
+ ok( r==4, "return count wrong\n");
+
+ format = "%u";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,"4294967295"), "failed\n");
+ ok( r==10, "return count wrong\n");
+
+ format = "%w";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,""), "failed\n");
+ ok( r==0, "return count wrong\n");
+
+ format = "%h";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,""), "failed\n");
+ ok( r==0, "return count wrong\n");
+
+ format = "%z";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,"z"), "failed\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%j";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,"j"), "failed\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "%F";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,""), "failed\n");
+ ok( r==0, "return count wrong\n");
+
+ format = "%H";
+ r = sprintf(buffer, format,-1);
+ ok(!strcmp(buffer,"H"), "failed\n");
+ ok( r==1, "return count wrong\n");
+
+ format = "x%cx";
+ r = sprintf(buffer, format, 0x100+'X');
+ ok(!strcmp(buffer,"xXx"), "failed\n");
+ ok( r==3, "return count wrong\n");
+
+ format = "%%0";
+ r = sprintf(buffer, format);
+ ok(!strcmp(buffer,"%0"), "failed: \"%s\"\n", buffer);
+ ok( r==2, "return count wrong\n");
+}
+
+static void test_swprintf( void )
+{
+ wchar_t buffer[100];
+ const wchar_t I64d[] = {'%','I','6','4','d',0};
+ double pnumber=789456123;
+ const wchar_t TwentyThreePoint15e[]= {'%','+','#','2','3','.','1','5','e',0};
+ const wchar_t e008[] = {'e','+','0','0','8',0};
+ const char string[]={'s','t','r','i','n','g',0};
+ const wchar_t S[]={'%','S',0};
+ swprintf(buffer,TwentyThreePoint15e,pnumber);
+ todo_wine
+ {
+ ok(wcsstr(buffer,e008) != 0,"Sprintf different\n");
+ }
+ swprintf(buffer,I64d,((ULONGLONG)0xffffffff)*0xffffffff);
+ ok(wcslen(buffer) == 11,"Problem with long long\n");
+ swprintf(buffer,S,string);
+ ok(wcslen(buffer) == 6,"Problem with \"%%S\" interpretation\n");
+}
+
+static void test_fwprintf( void )
+{
+ const char *string="not a wide string";
+ todo_wine
+ {
+ ok(fwprintf(fopen("nul","r+"),(wchar_t *)string) == -1,"Non-wide string should not be printed by fwprintf\n");
+ }
+}
+
+static void test_snprintf (void)
+{
+ struct snprintf_test {
+ const char *format;
+ int expected;
+ };
+ /* Pre-2.1 libc behaviour, not C99 compliant. */
+ const struct snprintf_test tests[] = {{"short", 5},
+ {"justfit", 7},
+ {"justfits", 8},
+ {"muchlonger", -1}};
+ char buffer[8];
+ const int bufsiz = sizeof buffer;
+ unsigned int i;
+
+ for (i = 0; i < sizeof tests / sizeof tests[0]; i++) {
+ const char *fmt = tests[i].format;
+ const int expect = tests[i].expected;
+ const int n = _snprintf (buffer, bufsiz, fmt);
+ const int valid = n < 0 ? bufsiz : (n == bufsiz ? n : n+1);
+
+ ok (n == expect, "\"%s\": expected %d, returned %d\n",
+ fmt, expect, n);
+ ok (!memcmp (fmt, buffer, valid),
+ "\"%s\": rendered \"%.*s\"\n", fmt, valid, buffer);
+ };
+}
+
+static void test_fcvt(void)
+{
+ char *str;
+ int dec=100, sign=100;
+
+ str = _fcvt(0.0001, 1, &dec, &sign );
+ todo_wine {
+ ok( 0 == strcmp(str,""), "bad return\n");
+ ok( -3 == dec, "dec wrong\n");
+ }
+ ok( 0 == sign, "dec wrong\n");
+
+ str = _fcvt(0.0001, -10, &dec, &sign );
+ todo_wine {
+ ok( 0 == strcmp(str,""), "bad return\n");
+ ok( -3 == dec, "dec wrong\n");
+ }
+ ok( 0 == sign, "dec wrong\n");
+
+ str = _fcvt(0.0001, 10, &dec, &sign );
+ todo_wine {
+ ok( 0 == strcmp(str,"1000000"), "bad return\n");
+ ok( -3 == dec, "dec wrong\n");
+ }
+ ok( 0 == sign, "dec wrong\n");
+
+ str = _fcvt(-111.0001, 5, &dec, &sign );
+ todo_wine {
+ ok( 0 == strcmp(str,"11100010"), "bad return\n");
+ ok( 3 == dec, "dec wrong\n");
+ }
+ ok( 1 == sign, "dec wrong\n");
+
+ str = _fcvt(111.0001, 5, &dec, &sign );
+ todo_wine {
+ ok( 0 == strcmp(str,"11100010"), "bad return\n");
+ ok( 3 == dec, "dec wrong\n");
+ }
+ ok( 0 == sign, "dec wrong\n");
+
+ str = _fcvt(0.0, 5, &dec, &sign );
+ todo_wine {
+ ok( 0 == strcmp(str,"00000"), "bad return\n");
+ ok( 0 == dec, "dec wrong\n");
+ }
+ ok( 0 == sign, "dec wrong\n");
+}
+
+START_TEST(printf)
+{
+ test_sprintf();
+ test_swprintf();
+ test_fwprintf();
+ test_snprintf();
+ test_fcvt();
+}
--- /dev/null
+/*
+ * Unit test suite for *scanf functions.
+ *
+ * Copyright 2002 Uwe Bonnes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+
+static void test_sscanf( void )
+{
+ char buffer[100], buffer1[100];
+ char format[20];
+ int result, ret;
+ char c;
+ float res1= -82.6267f, res2= 27.76f, res11, res12;
+ static const char pname[]=" St. Petersburg, Florida\n";
+ int hour=21,min=59,sec=20;
+ int number,number_so_far;
+
+
+ /* check EOF */
+ strcpy(buffer,"");
+ ret = sscanf(buffer, "%d", &result);
+ ok( ret == EOF,"sscanf returns %x instead of %x\n", ret, EOF );
+
+ /* check %x */
+ strcpy(buffer,"0x519");
+ ok( sscanf(buffer, "%x", &result) == 1, "sscanf failed\n" );
+ ok( result == 0x519,"sscanf reads %x instead of %x\n", result, 0x519 );
+
+ strcpy(buffer,"0x51a");
+ ok( sscanf(buffer, "%x", &result) == 1, "sscanf failed\n" );
+ ok( result == 0x51a ,"sscanf reads %x instead of %x\n", result, 0x51a );
+
+ strcpy(buffer,"0x51g");
+ ok( sscanf(buffer, "%x", &result) == 1, "sscanf failed\n" );
+ ok( result == 0x51, "sscanf reads %x instead of %x\n", result, 0x51 );
+
+ /* check % followed by any char */
+ strcpy(buffer,"\"%12@");
+ strcpy(format,"%\"%%%d%@"); /* work around gcc format check */
+ ok( sscanf(buffer, format, &result) == 1, "sscanf failed\n" );
+ ok( result == 12, "sscanf reads %x instead of %x\n", result, 12 );
+
+ /* Check float */
+ ret = sprintf(buffer,"%f %f",res1, res2);
+ ret = sscanf(buffer,"%f%f",&res11, &res12);
+ ok( (res11 == res1) && (res12 == res2), "Error reading floats\n");
+
+ /* check strings */
+ ret = sprintf(buffer," %s", pname);
+ ret = sscanf(buffer,"%*c%[^\n]",buffer1);
+ ok( ret == 1, "Error with format \"%s\"\n","%*c%[^\n]");
+ ok( strncmp(pname,buffer1,strlen(buffer1)) == 0, "Error with \"%s\" \"%s\"\n",pname, buffer1);
+
+ ret = sscanf("abcefgdh","%*[a-cg-e]%c",&buffer[0]);
+ ok( ret == 1, "Error with format \"%s\"\n","%*[a-cg-e]%c");
+ ok( buffer[0] == 'd', "Error with \"abcefgdh\" \"%c\"\n", buffer[0]);
+
+ ret = sscanf("abcefgdh","%*[a-cd-dg-e]%c",&buffer[0]);
+ ok( ret == 1, "Error with format \"%s\"\n","%*[a-cd-dg-e]%c");
+ ok( buffer[0] == 'h', "Error with \"abcefgdh\" \"%c\"\n", buffer[0]);
+
+ /* check digits */
+ ret = sprintf(buffer,"%d:%d:%d",hour,min,sec);
+ ret = sscanf(buffer,"%d%n",&number,&number_so_far);
+ ok(ret == 1 , "problem with format arg \"%%d%%n\"\n");
+ ok(number == hour,"Read wrong arg %d instead of %d\n",number, hour);
+ ok(number_so_far == 2,"Read wrong arg for \"%%n\" %d instead of 2\n",number_so_far);
+
+ ret = sscanf(buffer+2,"%*c%n",&number_so_far);
+ ok(ret == 0 , "problem with format arg \"%%*c%%n\"\n");
+ ok(number_so_far == 1,"Read wrong arg for \"%%n\" %d instead of 2\n",number_so_far);
+
+ /* Check %i according to bug 1878 */
+ strcpy(buffer,"123");
+ ret = sscanf(buffer, "%i", &result);
+ ok(ret == 1, "Wrong number of arguments read: %d\n", ret);
+ ok(result == 123, "Wrong number read\n");
+ ret = sscanf(buffer, "%d", &result);
+ ok(ret == 1, "Wrong number of arguments read: %d\n", ret);
+ ok(result == 123, "Wrong number read\n");
+
+ /* Check %c */
+ strcpy(buffer,"a");
+ c = 0x55;
+ ret = sscanf(buffer, "%c", &c);
+ ok(ret == 1, "Wrong number of arguments read: %d\n", ret);
+ ok(c == 'a', "Field incorrect: '%c'\n", c);
+
+ strcpy(buffer," a");
+ c = 0x55;
+ ret = sscanf(buffer, "%c", &c);
+ ok(ret == 1, "Wrong number of arguments read: %d\n", ret);
+ ok(c == ' ', "Field incorrect: '%c'\n", c);
+
+ strcpy(buffer,"18:59");
+ c = 0x55;
+ ret = sscanf(buffer, "%d:%d%c", &hour, &min, &c);
+ ok(ret == 2, "Wrong number of arguments read: %d\n", ret);
+ ok(hour == 18, "Field 1 incorrect: %d\n", hour);
+ ok(min == 59, "Field 2 incorrect: %d\n", min);
+ ok(c == 0x55, "Field 3 incorrect: 0x%02x\n", c);
+
+ /* Check %n (also whitespace in format strings and %s) */
+ buffer[0]=0; buffer1[0]=0;
+ ret = sscanf("abc def", "%s %n%s", buffer, &number_so_far, buffer1);
+ ok(strcmp(buffer, "abc")==0, "First %%s read incorrectly: %s\n", buffer);
+ ok(strcmp(buffer1,"def")==0, "Second %%s read incorrectly: %s\n", buffer1);
+ ok(number_so_far==6, "%%n yielded wrong result: %d\n", number_so_far);
+ ok(ret == 2, "%%n shouldn't count as a conversion: %d\n", ret);
+}
+
+START_TEST(scanf)
+{
+ test_sscanf();
+}
--- /dev/null
+/*
+ * Unit test suite for string functions.
+ *
+ * Copyright 2004 Uwe Bonnes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include "winbase.h"
+#include <string.h>
+#include <mbstring.h>
+#include <stdlib.h>
+#include <mbctype.h>
+
+static void* (*pmemcpy)(void *, const void *, size_t n);
+static int* (*pmemcmp)(void *, const void *, size_t n);
+
+#define SETNOFAIL(x,y) x = (void*)GetProcAddress(hMsvcrt,y)
+#define SET(x,y) SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y)
+
+static void test_swab( void ) {
+ char original[] = "BADCFEHGJILKNMPORQTSVUXWZY@#";
+ char expected1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ@#";
+ char expected2[] = "ABCDEFGHIJKLMNOPQRSTUVWX$";
+ char expected3[] = "$";
+
+ char from[30];
+ char to[30];
+
+ int testsize;
+
+ /* Test 1 - normal even case */
+ memset(to,'$', sizeof(to));
+ memset(from,'@', sizeof(from));
+ testsize = 26;
+ memcpy(from, original, testsize);
+ _swab( from, to, testsize );
+ ok(memcmp(to,expected1,testsize) == 0, "Testing even size %d returned '%*.*s'\n", testsize, testsize, testsize, to);
+
+ /* Test 2 - uneven case */
+ memset(to,'$', sizeof(to));
+ memset(from,'@', sizeof(from));
+ testsize = 25;
+ memcpy(from, original, testsize);
+ _swab( from, to, testsize );
+ ok(memcmp(to,expected2,testsize) == 0, "Testing odd size %d returned '%*.*s'\n", testsize, testsize, testsize, to);
+
+ /* Test 3 - from = to */
+ memset(to,'$', sizeof(to));
+ memset(from,'@', sizeof(from));
+ testsize = 26;
+ memcpy(to, original, testsize);
+ _swab( to, to, testsize );
+ ok(memcmp(to,expected1,testsize) == 0, "Testing overlapped size %d returned '%*.*s'\n", testsize, testsize, testsize, to);
+
+ /* Test 4 - 1 bytes */
+ memset(to,'$', sizeof(to));
+ memset(from,'@', sizeof(from));
+ testsize = 1;
+ memcpy(from, original, testsize);
+ _swab( from, to, testsize );
+ ok(memcmp(to,expected3,testsize) == 0, "Testing small size %d returned '%*.*s'\n", testsize, testsize, testsize, to);
+}
+
+void test_ismbblead(void)
+{
+ unsigned int s = '\354';
+
+ _setmbcp(936);
+ todo_wine ok(_ismbblead(s), "got result %d\n", _ismbblead(s));
+ _setmbcp(1252);
+}
+
+static void test_mbsspn( void)
+{
+ unsigned char str1[]="cabernet";
+ unsigned char str2[]="shiraz";
+ unsigned char set[]="abc";
+ unsigned char empty[]="";
+ int ret;
+ ret=_mbsspn( str1, set);
+ ok( ret==3, "_mbsspn returns %d should be 3\n", ret);
+ ret=_mbsspn( str2, set);
+ ok( ret==0, "_mbsspn returns %d should be 0\n", ret);
+ ret=_mbsspn( str1, empty);
+ ok( ret==0, "_mbsspn returns %d should be 0\n", ret);
+}
+
+START_TEST(string)
+{
+ void *mem;
+ static const char xilstring[]="c:/xilinx";
+ int nLen=strlen(xilstring);
+ HMODULE hMsvcrt = LoadLibraryA("msvcrt.dll");
+ ok(hMsvcrt != 0, "LoadLibraryA failed\n");
+ SET(pmemcpy,"memcpy");
+ SET(pmemcmp,"memcmp");
+
+ /* MSVCRT memcpy behaves like memmove for overlapping moves,
+ MFC42 CString::Insert seems to rely on that behaviour */
+ mem = malloc(100);
+ ok(mem != NULL, "memory not allocated for size 0\n");
+ strcpy((char*)mem,xilstring);
+ pmemcpy((char*)mem+5, mem,nLen+1);
+ ok(pmemcmp((char*)mem+5,xilstring, nLen) == 0,
+ "Got result %s\n",(char*)mem+5);
+
+ /* Test _swab function */
+ test_swab();
+
+ /* Test ismbblead*/
+ test_ismbblead();
+ /* test _mbsspn */
+ test_mbsspn();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_cpp(void);
+extern void func_dir(void);
+extern void func_environ(void);
+extern void func_file(void);
+extern void func_headers(void);
+extern void func_heap(void);
+extern void func_printf(void);
+extern void func_scanf(void);
+extern void func_string(void);
+extern void func_time(void);
+
+const struct test winetest_testlist[] =
+{
+ { "cpp", func_cpp },
+ { "dir", func_dir },
+ { "environ", func_environ },
+ { "file", func_file },
+// { "headers", func_headers },
+ { "heap", func_heap },
+ { "printf", func_printf },
+ { "scanf", func_scanf },
+ { "string", func_string },
+ { "time", func_time },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * Unit test suite for time functions.
+ *
+ * Copyright 2004 Uwe Bonnes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "time.h"
+
+#include <stdlib.h> /*setenv*/
+#include <stdio.h> /*printf*/
+
+#define SECSPERDAY 86400
+#define SECSPERHOUR 3600
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+
+static void test_gmtime(void)
+{
+ time_t gmt = (time_t)NULL;
+ struct tm* gmt_tm = gmtime(&gmt);
+ if(gmt_tm == 0)
+ {
+ ok(0,"gmtime() error\n");
+ return;
+ }
+ ok(((gmt_tm->tm_year == 70) && (gmt_tm->tm_mon == 0) && (gmt_tm->tm_yday == 0) &&
+ (gmt_tm->tm_mday == 1) && (gmt_tm->tm_wday == 4) && (gmt_tm->tm_hour == 0) &&
+ (gmt_tm->tm_min == 0) && (gmt_tm->tm_sec == 0) && (gmt_tm->tm_isdst == 0)),
+ "Wrong date:Year %4d mon %2d yday %3d mday %2d wday %1d hour%2d min %2d sec %2d dst %2d\n",
+ gmt_tm->tm_year, gmt_tm->tm_mon, gmt_tm->tm_yday, gmt_tm->tm_mday, gmt_tm->tm_wday,
+ gmt_tm->tm_hour, gmt_tm->tm_min, gmt_tm->tm_sec, gmt_tm->tm_isdst);
+
+}
+static void test_mktime(void)
+{
+ TIME_ZONE_INFORMATION tzinfo;
+ DWORD res = GetTimeZoneInformation(&tzinfo);
+ struct tm my_tm, sav_tm;
+ time_t nulltime, local_time;
+ char TZ_env[256];
+ int secs;
+
+ ok (res != TIME_ZONE_ID_INVALID, "GetTimeZoneInformation failed\n");
+ /* Bias may be positive or negative, to use offset of one day */
+ secs= SECSPERDAY - (tzinfo.Bias +
+ ( res == TIME_ZONE_ID_STANDARD ? tzinfo.StandardBias :
+ ( res == TIME_ZONE_ID_DAYLIGHT ? tzinfo.DaylightBias : 0 ))) * SECSPERMIN;
+ my_tm.tm_mday = 1 + secs/SECSPERDAY;
+ secs = secs % SECSPERDAY;
+ my_tm.tm_hour = secs / SECSPERHOUR;
+ secs = secs % SECSPERHOUR;
+ my_tm.tm_min = secs / SECSPERMIN;
+ secs = secs % SECSPERMIN;
+ my_tm.tm_sec = secs;
+
+ my_tm.tm_year = 70;
+ my_tm.tm_mon = 0;
+ my_tm.tm_isdst= 0;
+
+ sav_tm = my_tm;
+
+ local_time = mktime(&my_tm);
+ ok(((DWORD)local_time == SECSPERDAY), "mktime returned 0x%08lx\n",(DWORD)local_time);
+ /* now test some unnormalized struct tm's */
+ my_tm = sav_tm;
+ my_tm.tm_sec += 60;
+ my_tm.tm_min -= 1;
+ local_time = mktime(&my_tm);
+ ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
+ ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
+ my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
+ my_tm.tm_sec == sav_tm.tm_sec
+ , "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
+ my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
+ my_tm.tm_hour,my_tm.tm_sec,
+ sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
+ sav_tm.tm_hour,sav_tm.tm_sec);
+ my_tm = sav_tm;
+ my_tm.tm_min -= 60;
+ my_tm.tm_hour += 1;
+ local_time = mktime(&my_tm);
+ ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
+ ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
+ my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
+ my_tm.tm_sec == sav_tm.tm_sec
+ , "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
+ my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
+ my_tm.tm_hour,my_tm.tm_sec,
+ sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
+ sav_tm.tm_hour,sav_tm.tm_sec);
+ my_tm = sav_tm;
+ my_tm.tm_mon -= 12;
+ my_tm.tm_year += 1;
+ local_time = mktime(&my_tm);
+ ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
+ ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
+ my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
+ my_tm.tm_sec == sav_tm.tm_sec
+ , "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
+ my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
+ my_tm.tm_hour,my_tm.tm_sec,
+ sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
+ sav_tm.tm_hour,sav_tm.tm_sec);
+ my_tm = sav_tm;
+ my_tm.tm_mon += 12;
+ my_tm.tm_year -= 1;
+ local_time = mktime(&my_tm);
+ ok(((DWORD)local_time == SECSPERDAY), "Unnormalized mktime returned 0x%08lx\n",(DWORD)local_time);
+ ok( my_tm.tm_year == sav_tm.tm_year && my_tm.tm_mon == sav_tm.tm_mon &&
+ my_tm.tm_mday == sav_tm.tm_mday && my_tm.tm_hour == sav_tm.tm_hour &&
+ my_tm.tm_sec == sav_tm.tm_sec
+ , "mktime returned %3d-%02d-%02d %02d:%02d expected %3d-%02d-%02d %02d:%02d.\n",
+ my_tm.tm_year,my_tm.tm_mon,my_tm.tm_mday,
+ my_tm.tm_hour,my_tm.tm_sec,
+ sav_tm.tm_year,sav_tm.tm_mon,sav_tm.tm_mday,
+ sav_tm.tm_hour,sav_tm.tm_sec);
+ /* now a bad time example */
+ my_tm = sav_tm;
+ my_tm.tm_year -= 1;
+ local_time = mktime(&my_tm);
+ ok((local_time == -1), "(bad time) mktime returned 0x%08lx\n",(DWORD)local_time);
+
+ my_tm = sav_tm;
+ /* TEST that we are independent from the TZ variable */
+ /*Argh, msvcrt doesn't have setenv() */
+ _snprintf(TZ_env,255,"TZ=%s",(getenv("TZ")?getenv("TZ"):""));
+ putenv("TZ=GMT");
+ nulltime = mktime(&my_tm);
+ ok(((DWORD)nulltime == SECSPERDAY),"mktime returned 0x%08lx\n",(DWORD)nulltime);
+ putenv(TZ_env);
+}
+static void test_localtime(void)
+{
+ TIME_ZONE_INFORMATION tzinfo;
+ DWORD res = GetTimeZoneInformation(&tzinfo);
+ time_t gmt = (time_t)(SECSPERDAY + (tzinfo.Bias +
+ ( res == TIME_ZONE_ID_STANDARD ? tzinfo.StandardBias :
+ ( res == TIME_ZONE_ID_DAYLIGHT ? tzinfo.DaylightBias : 0 ))) * SECSPERMIN);
+
+ char TZ_env[256];
+ struct tm* lt;
+
+ ok (res != TIME_ZONE_ID_INVALID, "GetTimeZoneInformation failed\n");
+ lt = localtime(&gmt);
+ ok(((lt->tm_year == 70) && (lt->tm_mon == 0) && (lt->tm_yday == 1) &&
+ (lt->tm_mday == 2) && (lt->tm_wday == 5) && (lt->tm_hour == 0) &&
+ (lt->tm_min == 0) && (lt->tm_sec == 0) && (lt->tm_isdst ==
+ (res == TIME_ZONE_ID_DAYLIGHT))),
+ "Wrong date:Year %4d mon %2d yday %3d mday %2d wday %1d hour%2d min %2d sec %2d dst %2d\n",
+ lt->tm_year, lt->tm_mon, lt->tm_yday, lt->tm_mday, lt->tm_wday, lt->tm_hour,
+ lt->tm_min, lt->tm_sec, lt->tm_isdst);
+
+ _snprintf(TZ_env,255,"TZ=%s",(getenv("TZ")?getenv("TZ"):""));
+ putenv("TZ=GMT");
+ lt = localtime(&gmt);
+ ok(((lt->tm_year == 70) && (lt->tm_mon == 0) && (lt->tm_yday == 1) &&
+ (lt->tm_mday == 2) && (lt->tm_wday == 5) && (lt->tm_hour == 0) &&
+ (lt->tm_min == 0) && (lt->tm_sec == 0) && (lt->tm_isdst ==
+ (res == TIME_ZONE_ID_DAYLIGHT))),
+ "Wrong date:Year %4d mon %2d yday %3d mday %2d wday %1d hour%2d min %2d sec %2d dst %2d\n",
+ lt->tm_year, lt->tm_mon, lt->tm_yday, lt->tm_mday, lt->tm_wday, lt->tm_hour,
+ lt->tm_min, lt->tm_sec, lt->tm_isdst);
+ putenv(TZ_env);
+}
+static void test_strdate(void)
+{
+ char date[16], * result;
+ int month, day, year, count, len;
+
+ result = _strdate(date);
+ ok(result == date, "Wrong return value\n");
+ len = strlen(date);
+ ok(len == 8, "Wrong length: returned %d, should be 8\n", len);
+ count = sscanf(date, "%02d/%02d/%02d", &month, &day, &year);
+ ok(count == 3, "Wrong format: count = %d, should be 3\n", count);
+}
+static void test_strtime(void)
+{
+ char time[16], * result;
+ int hour, minute, second, count, len;
+
+ result = _strtime(time);
+ ok(result == time, "Wrong return value\n");
+ len = strlen(time);
+ ok(len == 8, "Wrong length: returned %d, should be 8\n", len);
+ count = sscanf(time, "%02d:%02d:%02d", &hour, &minute, &second);
+ ok(count == 3, "Wrong format: count = %d, should be 3\n", count);
+}
+static void test_wstrdate(void)
+{
+ wchar_t date[16], * result;
+ int month, day, year, count, len;
+ wchar_t format[] = { '%','0','2','d','/','%','0','2','d','/','%','0','2','d',0 };
+
+ result = _wstrdate(date);
+ ok(result == date, "Wrong return value\n");
+ len = wcslen(date);
+ ok(len == 8, "Wrong length: returned %d, should be 8\n", len);
+ count = swscanf(date, format, &month, &day, &year);
+ ok(count == 3, "Wrong format: count = %d, should be 3\n", count);
+}
+static void test_wstrtime(void)
+{
+ wchar_t time[16], * result;
+ int hour, minute, second, count, len;
+ wchar_t format[] = { '%','0','2','d',':','%','0','2','d',':','%','0','2','d',0 };
+
+ result = _wstrtime(time);
+ ok(result == time, "Wrong return value\n");
+ len = wcslen(time);
+ ok(len == 8, "Wrong length: returned %d, should be 8\n", len);
+ count = swscanf(time, format, &hour, &minute, &second);
+ ok(count == 3, "Wrong format: count = %d, should be 3\n", count);
+}
+
+START_TEST(time)
+{
+ test_gmtime();
+ test_mktime();
+ test_localtime();
+ test_strdate();
+ test_strtime();
+ test_wstrdate();
+ test_wstrtime();
+}
--- /dev/null
+/* Unit test suite for Ntdll atom API functions
+ *
+ * Copyright 2003 Gyorgy 'Nog' Jeney
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as there is no import library for NTDLL on
+ * windows.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "ntstatus.h"
+/* Define WIN32_NO_STATUS so MSVC does not give us duplicate macro
+ * definition errors when we get to winnt.h
+ */
+#define WIN32_NO_STATUS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "wine/test.h"
+#include "winternl.h"
+
+#ifndef __WINE_WINTERNL_H
+typedef unsigned short RTL_ATOM, *PRTL_ATOM;
+typedef struct atom_table *RTL_ATOM_TABLE, **PRTL_ATOM_TABLE;
+#endif
+
+/* Function pointers for ntdll calls */
+static HMODULE hntdll = 0;
+static NTSTATUS (WINAPI *pRtlCreateAtomTable)(ULONG,PRTL_ATOM_TABLE);
+static NTSTATUS (WINAPI *pRtlDestroyAtomTable)(RTL_ATOM_TABLE);
+static NTSTATUS (WINAPI *pRtlEmptyAtomTable)(RTL_ATOM_TABLE,BOOLEAN);
+static NTSTATUS (WINAPI *pRtlAddAtomToAtomTable)(RTL_ATOM_TABLE,PCWSTR,PRTL_ATOM);
+static NTSTATUS (WINAPI *pRtlDeleteAtomFromAtomTable)(RTL_ATOM_TABLE,RTL_ATOM);
+static NTSTATUS (WINAPI *pRtlLookupAtomInAtomTable)(RTL_ATOM_TABLE,PCWSTR,PRTL_ATOM);
+static NTSTATUS (WINAPI *pRtlPinAtomInAtomTable)(RTL_ATOM_TABLE,RTL_ATOM);
+static NTSTATUS (WINAPI *pRtlQueryAtomInAtomTable)(RTL_ATOM_TABLE,RTL_ATOM,PULONG,PULONG,PWSTR,PULONG);
+
+static NTSTATUS (WINAPI* pNtAddAtom)(LPCWSTR,ULONG,RTL_ATOM*);
+static NTSTATUS (WINAPI* pNtQueryInformationAtom)(RTL_ATOM,DWORD,void*,ULONG,PULONG);
+
+static const WCHAR EmptyAtom[] = {0};
+static const WCHAR testAtom1[] = {'H','e','l','l','o',' ','W','o','r','l','d',0};
+static const WCHAR testAtom2[] = {'H','e','l','l','o',' ','W','o','r','l','d','2',0};
+static const WCHAR testAtom3[] = {'H','e','l','l','o',' ','W','o','r','l','d','3',0};
+
+static const WCHAR testAtom1Cap[] = {'H','E','L','L','O',' ','W','O','R','L','D',0};
+static const WCHAR testAtom1Low[] = {'h','e','l','l','o',' ','w','o','r','l','d',0};
+
+static const WCHAR testAtomInt[] = {'#','1','3','2',0};
+static const WCHAR testAtomIntInv[] = {'#','2','3','4','z',0};
+static const WCHAR testAtomOTT[] = {'#','1','2','3',0};
+
+static void InitFunctionPtr(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ ok(hntdll != 0, "Unable to load ntdll.dll\n");
+
+ if (hntdll)
+ {
+ pRtlCreateAtomTable = (void *)GetProcAddress(hntdll, "RtlCreateAtomTable");
+ pRtlDestroyAtomTable = (void *)GetProcAddress(hntdll, "RtlDestroyAtomTable");
+ pRtlEmptyAtomTable = (void *)GetProcAddress(hntdll, "RtlEmptyAtomTable");
+ pRtlAddAtomToAtomTable = (void *)GetProcAddress(hntdll, "RtlAddAtomToAtomTable");
+ pRtlDeleteAtomFromAtomTable = (void *)GetProcAddress(hntdll, "RtlDeleteAtomFromAtomTable");
+ pRtlLookupAtomInAtomTable = (void *)GetProcAddress(hntdll, "RtlLookupAtomInAtomTable");
+ pRtlPinAtomInAtomTable = (void *)GetProcAddress(hntdll, "RtlPinAtomInAtomTable");
+ pRtlQueryAtomInAtomTable = (void *)GetProcAddress(hntdll, "RtlQueryAtomInAtomTable");
+
+ pNtAddAtom = (void *)GetProcAddress(hntdll, "NtAddAtom");
+ pNtQueryInformationAtom = (void *)GetProcAddress(hntdll, "NtQueryInformationAtom");
+ }
+}
+
+static DWORD RtlAtomTestThread(LPVOID Table)
+{
+ RTL_ATOM_TABLE AtomTable = *(PRTL_ATOM_TABLE)Table;
+ RTL_ATOM Atom;
+ NTSTATUS res;
+ ULONG RefCount = 0, PinCount = 0, Len = 0;
+ WCHAR Name[64];
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &Atom);
+ ok(!res, "Unable to find atom from another thread, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom2, &Atom);
+ ok(!res, "Unable to lookup pinned atom in table, retval: %lx\n", res);
+
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, Name, &Len);
+ ok(res == STATUS_BUFFER_TOO_SMALL, "We got wrong retval: %lx\n", res);
+
+ Len = 64;
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, Name, &Len);
+ ok(!res, "Failed with longenough buffer, retval: %lx\n", res);
+ ok(RefCount == 1, "Refcount was not 1 but %lx\n", RefCount);
+ ok(PinCount == 1, "Pincount was not 1 but %lx\n", PinCount);
+ ok(!lstrcmpW(Name, testAtom2), "We found wrong atom!!\n");
+ ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len);
+
+ Len = 64;
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom, NULL, NULL, Name, &Len);
+ ok(!res, "RtlQueryAtomInAtomTable with optional args invalid failed, retval: %lx\n", res);
+ ok(!lstrcmpW(Name, testAtom2), "Found Wrong atom!\n");
+ ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, Atom);
+ ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res);
+
+ return 0;
+}
+
+static void test_NtAtom(void)
+{
+ RTL_ATOM_TABLE AtomTable = NULL;
+ NTSTATUS res;
+ RTL_ATOM Atom1, Atom2, Atom3, testEAtom, testAtom;
+ HANDLE testThread;
+ ULONG RefCount = 0, PinCount = 0, Len = 0;
+ WCHAR Name[64];
+
+ /* If we pass a non-null string to create atom table, then it thinks that we
+ * have passed it an already allocated atom table */
+ res = pRtlCreateAtomTable(0, &AtomTable);
+ ok(!res, "RtlCreateAtomTable should succeed with an atom table size of 0\n");
+
+ if (!res)
+ {
+ res = pRtlDestroyAtomTable(AtomTable);
+ ok(!res, "We could create the atom table, but we couldn't destroy it! retval: %lx\n", res);
+ }
+
+ AtomTable = NULL;
+ res = pRtlCreateAtomTable(37, &AtomTable);
+ ok(!res, "We're unable to create an atom table with a valid table size retval: %lx\n", res);
+ if (!res)
+ {
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom1);
+ ok(!res, "We were unable to add a simple atom to the atom table, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1Cap, &testAtom);
+ ok(!res, "We were unable to find capital version of the atom, retval: %lx\n", res);
+ ok(Atom1 == testAtom, "Found wrong atom in table when querying capital atom\n");
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1Low, &testAtom);
+ ok(!res, "Unable to find lowercase version of the atom, retval: %lx\n", res);
+ ok(testAtom == Atom1, "Found wrong atom when querying lowercase atom\n");
+
+ res = pRtlAddAtomToAtomTable(AtomTable, EmptyAtom, &testEAtom);
+ ok(res == STATUS_OBJECT_NAME_INVALID, "Got wrong retval, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom);
+ ok(!res, "Failed to find totally legitimate atom, retval: %lx\n", res);
+ ok(testAtom == Atom1, "Found wrong atom!\n");
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom2, &Atom2);
+ ok(!res, "Unable to add other legitimate atom to table, retval: %lx\n", res);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, Atom2);
+ ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res);
+
+ testThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RtlAtomTestThread, &AtomTable, 0, NULL);
+ WaitForSingleObject(testThread, INFINITE);
+
+ Len = 64;
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom2, &RefCount, &PinCount, Name, &Len);
+ ok(!res, "Unable to query atom in atom table, retval: %lx\n", res);
+ ok(RefCount == 1, "RefCount is not 1 but %lx\n", RefCount);
+ ok(PinCount == 1, "PinCount is not 1 but %lx\n", PinCount);
+ ok(!lstrcmpW(Name, testAtom2), "We found wrong atom\n");
+ ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len);
+
+ res = pRtlEmptyAtomTable(AtomTable, FALSE);
+ ok(!res, "Unable to empty atom table, retval %lx\n", res);
+
+ Len = 64;
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom2, &RefCount, &PinCount, Name, &Len);
+ ok(!res, "It seems RtlEmptyAtomTable deleted our pinned atom eaven though we asked it not to, retval: %lx\n", res);
+ ok(RefCount == 1, "RefCount is not 1 but %lx\n", RefCount);
+ ok(PinCount == 1, "PinCount is not 1 but %lx\n", PinCount);
+ ok(!lstrcmpW(Name, testAtom2), "We found wrong atom\n");
+ ok((lstrlenW(testAtom2) * sizeof(WCHAR)) == Len, "Returned wrong length %ld\n", Len);
+
+ Len = 8;
+ Name[0] = Name[1] = Name[2] = Name[3] = Name[4] = 0x55AA;
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom2, NULL, NULL, Name, &Len);
+ ok(!res, "query atom %lx\n", res);
+ ok(Len == 6, "wrong length %lu\n", Len);
+ ok(!memcmp(Name, testAtom2, Len), "wrong atom string\n");
+ ok(!Name[3], "wrong string termination\n");
+ ok(Name[4] == 0x55AA, "buffer overwrite\n");
+
+ Len = lstrlenW(testAtom2) * sizeof(WCHAR);
+ memset(Name, '.', sizeof(Name));
+ res = pRtlQueryAtomInAtomTable( AtomTable, Atom2, NULL, NULL, Name, &Len );
+ ok(!res, "query atom %lx\n", res);
+ ok(Len == (lstrlenW(testAtom2) - 1) * sizeof(WCHAR), "wrong length %lu\n", Len);
+ ok(!memcmp(testAtom2, Name, (lstrlenW(testAtom2) - 1) * sizeof(WCHAR)), "wrong atom name\n");
+ ok(Name[lstrlenW(testAtom2) - 1] == '\0', "wrong char\n");
+ ok(Name[lstrlenW(testAtom2)] == ('.' << 8) + '.', "wrong char\n");
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom2, &testAtom);
+ ok(!res, "We can't find our pinned atom!! retval: %lx\n", res);
+ ok(testAtom == Atom2, "We found wrong atom!!!\n");
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom);
+ ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "We found the atom in our table eaven though we asked RtlEmptyAtomTable to remove it, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom3, &Atom3);
+ ok(!res, "Unable to add atom to table, retval: %lx\n", res);
+
+ res = pRtlEmptyAtomTable(AtomTable, TRUE);
+ ok(!res, "Unable to empty atom table, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom2, &testAtom);
+ ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "The pinned atom should be removed, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom3, &testAtom);
+ ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Non pinned atom should also be removed, retval: %lx\n", res);
+
+ res = pRtlDestroyAtomTable(AtomTable);
+ ok(!res, "Can't destroy atom table, retval: %lx\n", res);
+ }
+
+ AtomTable = NULL;
+ res = pRtlCreateAtomTable(37, &AtomTable);
+ ok(!res, "Unable to create atom table, retval: %lx\n", res);
+
+ if (!res)
+ {
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom);
+ ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Didn't get expected retval with querying an empty atom table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom1);
+ ok(!res, "Unable to add atom to atom table, retval %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom);
+ ok(!res, "Can't find previously added atom in table, retval: %lx\n", res);
+ ok(testAtom == Atom1, "Found wrong atom! retval: %lx\n", res);
+
+ res = pRtlDeleteAtomFromAtomTable(AtomTable, Atom1);
+ ok(!res, "Unable to delete atom from table, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom);
+ ok(res == STATUS_OBJECT_NAME_NOT_FOUND, "Able to find previously deleted atom in table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom1);
+ ok(!res, "Unable to add atom to atom table, retval: %lx\n", res);
+
+ Len = 0;
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom1, NULL, NULL, Name, &Len);
+ ok(res == STATUS_BUFFER_TOO_SMALL, "Got wrong retval, retval: %lx\n", res);
+ ok((lstrlenW(testAtom1) * sizeof(WCHAR)) == Len, "Got wrong length %lx\n", Len);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, Atom1);
+ ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom);
+ ok(!res, "Unable to find atom in atom table, retval: %lx\n", res);
+ ok(testAtom == Atom1, "Wrong atom found\n");
+
+ res = pRtlDeleteAtomFromAtomTable(AtomTable, Atom1);
+ ok(res == STATUS_WAS_LOCKED, "Unable to delete atom from table, retval: %lx\n", res);
+
+ res = pRtlLookupAtomInAtomTable(AtomTable, testAtom1, &testAtom);
+ ok(!res, "Able to find deleted atom in table\n");
+
+ res = pRtlDestroyAtomTable(AtomTable);
+ ok(!res, "Unable to destroy atom table\n");
+ }
+}
+
+/* Test Adding integer atoms to atom table */
+static void test_NtIntAtom(void)
+{
+ NTSTATUS res;
+ RTL_ATOM_TABLE AtomTable;
+ RTL_ATOM testAtom;
+ ULONG RefCount = 0, PinCount = 0;
+ int i;
+ WCHAR Name[64];
+ ULONG Len;
+
+ AtomTable = NULL;
+ res = pRtlCreateAtomTable(37, &AtomTable);
+ ok(!res, "Unable to create atom table, %lx\n", res);
+
+ if (!res)
+ {
+ /* According to the kernel32 functions, integer atoms are only allowd from
+ * 0x0001 to 0xbfff and not 0xc000 to 0xffff, which is correct */
+ res = pRtlAddAtomToAtomTable(AtomTable, (PWSTR)0, &testAtom);
+ ok(res == STATUS_INVALID_PARAMETER, "Didn't get expected result from adding 0 int atom, retval: %lx\n", res);
+ for (i = 1; i <= 0xbfff; i++)
+ {
+ res = pRtlAddAtomToAtomTable(AtomTable, (PWSTR)i, &testAtom);
+ ok(!res, "Unable to add valid integer atom %i, retval: %lx\n", i, res);
+ }
+
+ for (i = 1; i <= 0xbfff; i++)
+ {
+ res = pRtlLookupAtomInAtomTable(AtomTable, (PWSTR)i, &testAtom);
+ ok(!res, "Unable to find int atom %i, retval: %lx\n", i, res);
+ if (!res)
+ {
+ res = pRtlPinAtomInAtomTable(AtomTable, testAtom);
+ ok(!res, "Unable to pin int atom %i, retval: %lx\n", i, res);
+ }
+ }
+
+ for (i = 0xc000; i <= 0xffff; i++)
+ {
+ res = pRtlAddAtomToAtomTable(AtomTable, (PWSTR)i, &testAtom);
+ ok(res, "Able to illeageal integer atom %i, retval: %lx\n", i, res);
+ }
+
+ res = pRtlDestroyAtomTable(AtomTable);
+ ok(!res, "Unable to destroy atom table, retval: %lx\n", res);
+ }
+
+ AtomTable = NULL;
+ res = pRtlCreateAtomTable(37, &AtomTable);
+ ok(!res, "Unable to create atom table, %lx\n", res);
+ if (!res)
+ {
+ res = pRtlLookupAtomInAtomTable(AtomTable, (PWSTR)123, &testAtom);
+ ok(!res, "Unable to query atom in atom table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtomInt, &testAtom);
+ ok(!res, "Unable to add int atom to table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtomIntInv, &testAtom);
+ ok(!res, "Unable to add int atom to table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, (PWSTR)123, &testAtom);
+ ok(!res, "Unable to add int atom to table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, (PWSTR)123, &testAtom);
+ ok(!res, "Unable to re-add int atom to table, retval: %lx\n", res);
+
+ Len = 64;
+ res = pRtlQueryAtomInAtomTable(AtomTable, testAtom, &RefCount, &PinCount, Name, &Len);
+ ok(!res, "Unable to query atom in atom table, retval: %lx\n", res);
+ ok(PinCount == 1, "Expected pincount 1 but got %lx\n", PinCount);
+ ok(RefCount == 1, "Expected refcount 1 but got %lx\n", RefCount);
+ ok(!lstrcmpW(testAtomOTT, Name), "Got wrong atom name\n");
+ ok((lstrlenW(testAtomOTT) * sizeof(WCHAR)) == Len, "Got wrong len %ld\n", Len);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, testAtom);
+ ok(!res, "Unable to pin int atom, retval: %lx\n", res);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, testAtom);
+ ok(!res, "Unable to pin int atom, retval: %lx\n", res);
+
+ res = pRtlQueryAtomInAtomTable(AtomTable, testAtom, &RefCount, &PinCount, NULL, NULL);
+ ok(!res, "Unable to query atom in atom table, retval: %lx\n", res);
+ ok(PinCount == 1, "Expected pincount 1 but got %lx\n", PinCount);
+ ok(RefCount == 1, "Expected refcount 1 but got %lx\n", RefCount);
+
+ res = pRtlDestroyAtomTable(AtomTable);
+ ok(!res, "Unable to destroy atom table, retval: %lx\n", res);
+ }
+}
+
+/* Tests to see how the pincount and refcount actually works */
+static void test_NtRefPinAtom(void)
+{
+ RTL_ATOM_TABLE AtomTable;
+ RTL_ATOM Atom;
+ ULONG PinCount = 0, RefCount = 0;
+ NTSTATUS res;
+
+ AtomTable = NULL;
+ res = pRtlCreateAtomTable(37, &AtomTable);
+ ok(!res, "Unable to create atom table, %lx\n", res);
+
+ if (!res)
+ {
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom);
+ ok(!res, "Unable to add our atom to the atom table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom);
+ ok(!res, "Unable to add our atom to the atom table, retval: %lx\n", res);
+
+ res = pRtlAddAtomToAtomTable(AtomTable, testAtom1, &Atom);
+ ok(!res, "Unable to add our atom to the atom table, retval: %lx\n", res);
+
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, NULL, NULL);
+ ok(!res, "Unable to query atom in atom table, retval: %lx\n", res);
+ ok(PinCount == 0, "Expected pincount 0 but got %lx\n", PinCount);
+ ok(RefCount == 3, "Expected refcount 3 but got %lx\n", RefCount);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, Atom);
+ ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, Atom);
+ ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res);
+
+ res = pRtlPinAtomInAtomTable(AtomTable, Atom);
+ ok(!res, "Unable to pin atom in atom table, retval: %lx\n", res);
+
+ res = pRtlQueryAtomInAtomTable(AtomTable, Atom, &RefCount, &PinCount, NULL, NULL);
+ ok(!res, "Unable to query atom in atom table, retval: %lx\n", res);
+ ok(PinCount == 1, "Expected pincount 1 but got %lx\n", PinCount);
+ ok(RefCount == 3, "Expected refcount 3 but got %lx\n", RefCount);
+
+ res = pRtlDestroyAtomTable(AtomTable);
+ ok(!res, "Unable to destroy atom table, retval: %lx\n", res);
+ }
+}
+
+static void test_Global(void)
+{
+ NTSTATUS res;
+ RTL_ATOM atom;
+ char ptr[sizeof(ATOM_BASIC_INFORMATION) + 255 * sizeof(WCHAR)];
+ ATOM_BASIC_INFORMATION* abi = (ATOM_BASIC_INFORMATION*)ptr;
+ ULONG ptr_size = sizeof(ATOM_BASIC_INFORMATION) + 255 * sizeof(WCHAR);
+
+ res = pNtAddAtom(testAtom1, lstrlenW(testAtom1) * sizeof(WCHAR), &atom);
+ ok(!res, "Added atom (%lx)\n", res);
+
+ memset(abi->Name, 0x55, 255 * sizeof(WCHAR));
+ res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL );
+ ok(!res, "atom lookup\n");
+ ok(!lstrcmpW(abi->Name, testAtom1), "ok strings\n");
+ ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "wrong string length\n");
+ ok(abi->Name[lstrlenW(testAtom1)] == 0, "wrong string termination %x\n", abi->Name[lstrlenW(testAtom1)]);
+ ok(abi->Name[lstrlenW(testAtom1) + 1] == 0x5555, "buffer overwrite %x\n", abi->Name[lstrlenW(testAtom1) + 1]);
+
+ ptr_size = sizeof(ATOM_BASIC_INFORMATION);
+ res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL );
+ ok(res == STATUS_BUFFER_TOO_SMALL, "wrong return status (%lx)\n", res);
+ ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "ok string length\n");
+
+ memset(abi->Name, 0x55, lstrlenW(testAtom1) * sizeof(WCHAR));
+ ptr_size = sizeof(ATOM_BASIC_INFORMATION) + lstrlenW(testAtom1) * sizeof(WCHAR);
+ res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL );
+ ok(!res, "atom lookup %lx\n", res);
+ ok(!lstrcmpW(abi->Name, testAtom1), "strings don't match\n");
+ ok(abi->NameLength == lstrlenW(testAtom1) * sizeof(WCHAR), "wrong string length\n");
+ ok(abi->Name[lstrlenW(testAtom1)] == 0, "buffer overwrite %x\n", abi->Name[lstrlenW(testAtom1)]);
+ ok(abi->Name[lstrlenW(testAtom1) + 1] == 0x5555, "buffer overwrite %x\n", abi->Name[lstrlenW(testAtom1) + 1]);
+
+ ptr_size = sizeof(ATOM_BASIC_INFORMATION) + 4 * sizeof(WCHAR);
+ abi->Name[0] = abi->Name[1] = abi->Name[2] = abi->Name[3] = '\0';
+ res = pNtQueryInformationAtom( atom, AtomBasicInformation, (void*)ptr, ptr_size, NULL );
+ ok(!res, "couldn't find atom\n");
+ ok(abi->NameLength == 8, "wrong string length %u\n", abi->NameLength);
+ ok(!memcmp(abi->Name, testAtom1, 8), "strings don't match\n");
+}
+
+START_TEST(atom)
+{
+ InitFunctionPtr();
+ if (pRtlCreateAtomTable)
+ {
+ test_NtAtom();
+ test_NtIntAtom();
+ test_NtRefPinAtom();
+ test_Global();
+ }
+}
--- /dev/null
+/*
+ * File change notification tests
+ *
+ * Copyright 2006 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <ntstatus.h>
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <winnt.h>
+#include <winternl.h>
+#include <winerror.h>
+#include <stdio.h>
+#include "wine/test.h"
+
+typedef NTSTATUS (WINAPI *fnNtNotifyChangeDirectoryFile)(
+ HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,
+ PIO_STATUS_BLOCK,PVOID,ULONG,ULONG,BOOLEAN);
+fnNtNotifyChangeDirectoryFile pNtNotifyChangeDirectoryFile;
+
+typedef NTSTATUS (WINAPI *fnNtCancelIoFile)(HANDLE,PIO_STATUS_BLOCK);
+fnNtCancelIoFile pNtCancelIoFile;
+
+
+static void test_ntncdf(void)
+{
+ NTSTATUS r;
+ HANDLE hdir, hEvent;
+ char buffer[0x1000];
+ DWORD fflags, filter = 0;
+ IO_STATUS_BLOCK iosb;
+ WCHAR path[MAX_PATH], subdir[MAX_PATH];
+ static const WCHAR szBoo[] = { '\\','b','o','o',0 };
+ static const WCHAR szHoo[] = { '\\','h','o','o',0 };
+ PFILE_NOTIFY_INFORMATION pfni;
+
+ r = GetTempPathW( MAX_PATH, path );
+ ok( r != 0, "temp path failed\n");
+ if (!r)
+ return;
+
+ lstrcatW( path, szBoo );
+ lstrcpyW( subdir, path );
+ lstrcatW( subdir, szHoo );
+
+ RemoveDirectoryW( subdir );
+ RemoveDirectoryW( path );
+
+ r = CreateDirectoryW(path, NULL);
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = pNtNotifyChangeDirectoryFile(NULL,NULL,NULL,NULL,NULL,NULL,0,0,0);
+ ok(r==STATUS_ACCESS_VIOLATION, "should return access violation\n");
+
+ fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
+ hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, fflags, NULL);
+ ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+ hEvent = CreateEvent( NULL, 0, 0, NULL );
+
+ r = pNtNotifyChangeDirectoryFile(hdir,NULL,NULL,NULL,&iosb,NULL,0,0,0);
+ ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
+
+ r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,NULL,0,0,0);
+ ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
+
+ filter = FILE_NOTIFY_CHANGE_FILE_NAME;
+ filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
+ filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ filter |= FILE_NOTIFY_CHANGE_SIZE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ filter |= FILE_NOTIFY_CHANGE_CREATION;
+ filter |= FILE_NOTIFY_CHANGE_SECURITY;
+
+ U(iosb).Status = 1;
+ iosb.Information = 1;
+ r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,-1,0);
+ ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
+
+ ok( U(iosb).Status == 1, "information wrong\n");
+ ok( iosb.Information == 1, "information wrong\n");
+
+ U(iosb).Status = 1;
+ iosb.Information = 0;
+ r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
+ ok(r==STATUS_PENDING, "should return status pending\n");
+
+ r = WaitForSingleObject( hEvent, 0 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = WaitForSingleObject( hdir, 0 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = WaitForSingleObject( hdir, 0 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = WaitForSingleObject( hEvent, 0 );
+ ok( r == WAIT_OBJECT_0, "event should be ready\n" );
+
+ ok( U(iosb).Status == STATUS_SUCCESS, "information wrong\n");
+ ok( iosb.Information == 0x12, "information wrong\n");
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
+ ok( pfni->FileNameLength == 6, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
+
+ r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,0,0);
+ ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
+
+ r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,0,0);
+ ok(r==STATUS_INVALID_PARAMETER, "should return invalid parameter\n");
+
+ filter = FILE_NOTIFY_CHANGE_SIZE;
+
+ U(iosb).Status = 1;
+ iosb.Information = 1;
+ r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0);
+ ok(r==STATUS_PENDING, "should status pending\n");
+
+ ok( U(iosb).Status == 1, "information wrong\n");
+ ok( iosb.Information == 1, "information wrong\n");
+
+ r = WaitForSingleObject( hdir, 0 );
+ ok( r == STATUS_TIMEOUT, "should timeout\n" );
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ r = WaitForSingleObject( hdir, 100 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ r = WaitForSingleObject( hdir, 100 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok( U(iosb).Status == STATUS_NOTIFY_ENUM_DIR, "information wrong\n");
+ ok( iosb.Information == 0, "information wrong\n");
+
+ CloseHandle(hdir);
+ CloseHandle(hEvent);
+
+ r = RemoveDirectoryW( path );
+ ok( r == TRUE, "failed to remove directory\n");
+}
+
+
+static void test_ntncdf_async(void)
+{
+ NTSTATUS r;
+ HANDLE hdir, hEvent;
+ char buffer[0x1000];
+ DWORD fflags, filter = 0;
+ IO_STATUS_BLOCK iosb, iosb2;
+ WCHAR path[MAX_PATH], subdir[MAX_PATH];
+ static const WCHAR szBoo[] = { '\\','b','o','o',0 };
+ static const WCHAR szHoo[] = { '\\','h','o','o',0 };
+ PFILE_NOTIFY_INFORMATION pfni;
+
+ r = GetTempPathW( MAX_PATH, path );
+ ok( r != 0, "temp path failed\n");
+ if (!r)
+ return;
+
+ lstrcatW( path, szBoo );
+ lstrcpyW( subdir, path );
+ lstrcatW( subdir, szHoo );
+
+ RemoveDirectoryW( subdir );
+ RemoveDirectoryW( path );
+
+ r = CreateDirectoryW(path, NULL);
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = pNtNotifyChangeDirectoryFile(NULL,NULL,NULL,NULL,NULL,NULL,0,0,0);
+ ok(r==STATUS_ACCESS_VIOLATION, "should return access violation\n");
+
+ fflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
+ hdir = CreateFileW(path, GENERIC_READ|SYNCHRONIZE, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, fflags, NULL);
+ ok( hdir != INVALID_HANDLE_VALUE, "failed to open directory\n");
+
+ hEvent = CreateEvent( NULL, 0, 0, NULL );
+
+ filter = FILE_NOTIFY_CHANGE_FILE_NAME;
+ filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
+ filter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ filter |= FILE_NOTIFY_CHANGE_SIZE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ filter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ filter |= FILE_NOTIFY_CHANGE_CREATION;
+ filter |= FILE_NOTIFY_CHANGE_SECURITY;
+
+
+ U(iosb).Status = 0x01234567;
+ iosb.Information = 0x12345678;
+ r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
+ ok(r==STATUS_PENDING, "should status pending\n");
+ ok(U(iosb).Status == 0x01234567, "status set too soon\n");
+ ok(iosb.Information == 0x12345678, "info set too soon\n");
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = WaitForSingleObject( hdir, 100 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n");
+ ok(iosb.Information == 0x12, "info not set\n");
+
+ pfni = (PFILE_NOTIFY_INFORMATION) buffer;
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_ADDED, "action wrong\n" );
+ ok( pfni->FileNameLength == 6, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
+
+ r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
+ ok(r==STATUS_PENDING, "should status pending\n");
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ r = WaitForSingleObject( hdir, 0 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n");
+ ok(iosb.Information == 0x12, "info not set\n");
+
+ ok( pfni->NextEntryOffset == 0, "offset wrong\n" );
+ ok( pfni->Action == FILE_ACTION_REMOVED, "action wrong\n" );
+ ok( pfni->FileNameLength == 6, "len wrong\n" );
+ ok( !memcmp(pfni->FileName,&szHoo[1],6), "name wrong\n" );
+
+ /* check APCs */
+ U(iosb).Status = 0;
+ iosb.Information = 0;
+
+ r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,NULL,0,filter,0);
+ ok(r==STATUS_PENDING, "should status pending\n");
+
+ r = CreateDirectoryW( subdir, NULL );
+ ok( r == TRUE, "failed to create directory\n");
+
+ r = WaitForSingleObject( hdir, 0 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok(U(iosb).Status == STATUS_NOTIFY_ENUM_DIR, "status not successful\n");
+ ok(iosb.Information == 0, "info not set\n");
+
+ U(iosb).Status = 0;
+ iosb.Information = 0;
+
+ r = pNtNotifyChangeDirectoryFile(hdir,hEvent,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
+ ok(r==STATUS_PENDING, "should status pending\n");
+
+ r = RemoveDirectoryW( subdir );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ r = WaitForSingleObject( hEvent, 0 );
+ ok( r == WAIT_OBJECT_0, "should be ready\n" );
+
+ ok(U(iosb).Status == STATUS_SUCCESS, "status not successful\n");
+ ok(iosb.Information == 0x12, "info not set\n");
+
+
+ U(iosb).Status = 0x01234567;
+ iosb.Information = 0x12345678;
+ r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb,buffer,sizeof buffer,filter,0);
+ ok(r==STATUS_PENDING, "should status pending\n");
+
+ U(iosb2).Status = 0x01234567;
+ iosb2.Information = 0x12345678;
+ r = pNtNotifyChangeDirectoryFile(hdir,0,NULL,NULL,&iosb2,buffer,sizeof buffer,filter,0);
+ ok(r==STATUS_PENDING, "should status pending\n");
+
+ ok(U(iosb).Status == 0x01234567, "status set too soon\n");
+ ok(iosb.Information == 0x12345678, "info set too soon\n");
+
+ todo_wine {
+ r = pNtCancelIoFile(hdir, &iosb);
+ ok( r == STATUS_SUCCESS, "cancel failed\n");
+
+ CloseHandle(hdir);
+
+ ok(U(iosb).Status == STATUS_SUCCESS, "status wrong\n");
+ ok(U(iosb2).Status == STATUS_CANCELLED, "status wrong\n");
+ }
+ ok(iosb.Information == 0, "info wrong\n");
+ ok(iosb2.Information == 0, "info wrong\n");
+
+ r = RemoveDirectoryW( path );
+ ok( r == TRUE, "failed to remove directory\n");
+
+ CloseHandle(hEvent);
+}
+
+START_TEST(change)
+{
+ HMODULE hntdll = GetModuleHandle("ntdll");
+
+ pNtNotifyChangeDirectoryFile = (fnNtNotifyChangeDirectoryFile)
+ GetProcAddress(hntdll, "NtNotifyChangeDirectoryFile");
+ pNtCancelIoFile = (fnNtCancelIoFile)
+ GetProcAddress(hntdll, "NtCancelIoFile");
+
+ if (!pNtNotifyChangeDirectoryFile)
+ return;
+ if (!pNtCancelIoFile)
+ return;
+
+ test_ntncdf();
+ test_ntncdf_async();
+}
--- /dev/null
+/*
+ * Unit test suite for ntdll path functions
+ *
+ * Copyright 2003 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+
+#include "ntdll_test.h"
+
+static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
+ LPCSTR src, DWORD srclen );
+static NTSTATUS (WINAPI *pRtlCreateEnvironment)(BOOLEAN, PWSTR*);
+static NTSTATUS (WINAPI *pRtlDestroyEnvironment)(PWSTR);
+static NTSTATUS (WINAPI *pRtlQueryEnvironmentVariable_U)(PWSTR, PUNICODE_STRING, PUNICODE_STRING);
+static void (WINAPI *pRtlSetCurrentEnvironment)(PWSTR, PWSTR*);
+static NTSTATUS (WINAPI *pRtlSetEnvironmentVariable)(PWSTR*, PUNICODE_STRING, PUNICODE_STRING);
+static NTSTATUS (WINAPI *pRtlExpandEnvironmentStrings_U)(LPWSTR, PUNICODE_STRING, PUNICODE_STRING, PULONG);
+
+static WCHAR small_env[] = {'f','o','o','=','t','o','t','o',0,
+ 'f','o','=','t','i','t','i',0,
+ 'f','o','o','o','=','t','u','t','u',0,
+ 's','r','=','a','n','=','o','u','o',0,
+ 'g','=','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',
+ 'a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a','a',0,
+ '=','o','O','H','=','I','I','I',0,
+ 'n','u','l','=',0,
+ 0};
+
+static void testQuery(void)
+{
+ struct test
+ {
+ const char *var;
+ int len;
+ NTSTATUS status;
+ const char *val;
+ };
+
+ static const struct test tests[] =
+ {
+ {"foo", 256, STATUS_SUCCESS, "toto"},
+ {"FoO", 256, STATUS_SUCCESS, "toto"},
+ {"foo=", 256, STATUS_VARIABLE_NOT_FOUND, NULL},
+ {"foo ", 256, STATUS_VARIABLE_NOT_FOUND, NULL},
+ {"foo", 1, STATUS_BUFFER_TOO_SMALL, "toto"},
+ {"foo", 3, STATUS_BUFFER_TOO_SMALL, "toto"},
+ {"foo", 4, STATUS_SUCCESS, "toto"},
+ {"fooo", 256, STATUS_SUCCESS, "tutu"},
+ {"f", 256, STATUS_VARIABLE_NOT_FOUND, NULL},
+ {"g", 256, STATUS_SUCCESS, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
+ {"sr=an", 256, STATUS_VARIABLE_NOT_FOUND, NULL},
+ {"sr", 256, STATUS_SUCCESS, "an=ouo"},
+ {"=oOH", 256, STATUS_SUCCESS, "III"},
+ {"", 256, STATUS_VARIABLE_NOT_FOUND, NULL},
+ {"nul", 256, STATUS_SUCCESS, ""},
+ {NULL, 0, 0, NULL}
+ };
+
+ WCHAR bn[257];
+ WCHAR bv[257];
+ UNICODE_STRING name;
+ UNICODE_STRING value;
+ const struct test* test;
+ NTSTATUS nts;
+
+ for (test = tests; test->var; test++)
+ {
+ name.Length = strlen(test->var) * 2;
+ name.MaximumLength = name.Length + 2;
+ name.Buffer = bn;
+ value.Length = 0;
+ value.MaximumLength = test->len * 2;
+ value.Buffer = bv;
+ bv[test->len] = '@';
+
+ pRtlMultiByteToUnicodeN( bn, sizeof(bn), NULL, test->var, strlen(test->var)+1 );
+ nts = pRtlQueryEnvironmentVariable_U(small_env, &name, &value);
+ ok( nts == test->status, "[%d]: Wrong status for '%s', expecting %lx got %lx\n",
+ test - tests, test->var, test->status, nts );
+ if (nts == test->status) switch (nts)
+ {
+ case STATUS_SUCCESS:
+ pRtlMultiByteToUnicodeN( bn, sizeof(bn), NULL, test->val, strlen(test->val)+1 );
+ ok( value.Length == strlen(test->val) * sizeof(WCHAR), "Wrong length %d/%d for %s\n",
+ value.Length, strlen(test->val) * sizeof(WCHAR), test->var );
+ ok((value.Length == strlen(test->val) * sizeof(WCHAR) && memcmp(bv, bn, test->len*sizeof(WCHAR)) == 0) ||
+ lstrcmpW(bv, bn) == 0,
+ "Wrong result for %s/%d\n", test->var, test->len);
+ ok(bv[test->len] == '@', "Writing too far away in the buffer for %s/%d\n", test->var, test->len);
+ break;
+ case STATUS_BUFFER_TOO_SMALL:
+ ok( value.Length == strlen(test->val) * sizeof(WCHAR),
+ "Wrong returned length %d/%d (too small buffer) for %s\n",
+ value.Length, strlen(test->val) * sizeof(WCHAR), test->var );
+ break;
+ }
+ }
+}
+
+static void testSetHelper(LPWSTR* env, const char* var, const char* val, NTSTATUS ret)
+{
+ WCHAR bvar[256], bval1[256], bval2[256];
+ UNICODE_STRING uvar;
+ UNICODE_STRING uval;
+ NTSTATUS nts;
+
+ uvar.Length = strlen(var) * sizeof(WCHAR);
+ uvar.MaximumLength = uvar.Length + sizeof(WCHAR);
+ uvar.Buffer = bvar;
+ pRtlMultiByteToUnicodeN( bvar, sizeof(bvar), NULL, var, strlen(var)+1 );
+ if (val)
+ {
+ uval.Length = strlen(val) * sizeof(WCHAR);
+ uval.MaximumLength = uval.Length + sizeof(WCHAR);
+ uval.Buffer = bval1;
+ pRtlMultiByteToUnicodeN( bval1, sizeof(bval1), NULL, val, strlen(val)+1 );
+ }
+ nts = pRtlSetEnvironmentVariable(env, &uvar, val ? &uval : NULL);
+ ok(nts == ret, "Setting var %s=%s (%lx/%lx)\n", var, val, nts, ret);
+ if (nts == STATUS_SUCCESS)
+ {
+ uval.Length = 0;
+ uval.MaximumLength = sizeof(bval2);
+ uval.Buffer = bval2;
+ nts = pRtlQueryEnvironmentVariable_U(*env, &uvar, &uval);
+ switch (nts)
+ {
+ case STATUS_SUCCESS:
+ ok(lstrcmpW(bval1, bval2) == 0, "Cannot get value written to environment\n");
+ break;
+ case STATUS_VARIABLE_NOT_FOUND:
+ ok(val == NULL, "Couldn't find variable, but didn't delete it. val = %s\n", val);
+ break;
+ default:
+ ok(0, "Wrong ret %lu for %s\n", nts, var);
+ break;
+ }
+ }
+}
+
+static void testSet(void)
+{
+ LPWSTR env;
+ char tmp[16];
+ int i;
+
+ ok(pRtlCreateEnvironment(FALSE, &env) == STATUS_SUCCESS, "Creating environment\n");
+ memmove(env, small_env, sizeof(small_env));
+
+ testSetHelper(&env, "cat", "dog", STATUS_SUCCESS);
+ testSetHelper(&env, "cat", "horse", STATUS_SUCCESS);
+ testSetHelper(&env, "cat", "zz", STATUS_SUCCESS);
+ testSetHelper(&env, "cat", NULL, STATUS_SUCCESS);
+ testSetHelper(&env, "cat", NULL, STATUS_VARIABLE_NOT_FOUND);
+ testSetHelper(&env, "foo", "meouw", STATUS_SUCCESS);
+ testSetHelper(&env, "me=too", "also", STATUS_INVALID_PARAMETER);
+ testSetHelper(&env, "me", "too=also", STATUS_SUCCESS);
+ testSetHelper(&env, "=too", "also", STATUS_SUCCESS);
+ testSetHelper(&env, "=", "also", STATUS_SUCCESS);
+
+ for (i = 0; i < 128; i++)
+ {
+ sprintf(tmp, "zork%03d", i);
+ testSetHelper(&env, tmp, "is alive", STATUS_SUCCESS);
+ }
+
+ for (i = 0; i < 128; i++)
+ {
+ sprintf(tmp, "zork%03d", i);
+ testSetHelper(&env, tmp, NULL, STATUS_SUCCESS);
+ }
+ testSetHelper(&env, "fOo", NULL, STATUS_SUCCESS);
+
+ ok(pRtlDestroyEnvironment(env) == STATUS_SUCCESS, "Destroying environment\n");
+}
+
+static void testExpand(void)
+{
+ static const struct test
+ {
+ const char *src;
+ const char *dst;
+ } tests[] =
+ {
+ {"hello%foo%world", "hellototoworld"},
+ {"hello%=oOH%world", "helloIIIworld"},
+ {"hello%foo", "hello%foo"},
+ {"hello%bar%world", "hello%bar%world"},
+ /*
+ * {"hello%foo%world%=oOH%eeck", "hellototoworldIIIeeck"},
+ * Interestingly enough, with a 8 WCHAR buffers, we get on 2k:
+ * helloIII
+ * so it seems like strings overflowing the buffer are written
+ * (troncated) but the write cursor is not advanced :-/
+ */
+ {NULL, NULL}
+ };
+
+ const struct test* test;
+ NTSTATUS nts;
+ UNICODE_STRING us_src, us_dst;
+ WCHAR src[256], dst[256], rst[256];
+ ULONG ul;
+
+ for (test = tests; test->src; test++)
+ {
+ pRtlMultiByteToUnicodeN(src, sizeof(src), NULL, test->src, strlen(test->src)+1);
+ pRtlMultiByteToUnicodeN(rst, sizeof(rst), NULL, test->dst, strlen(test->dst)+1);
+
+ us_src.Length = strlen(test->src) * sizeof(WCHAR);
+ us_src.MaximumLength = us_src.Length + 2;
+ us_src.Buffer = src;
+
+ us_dst.Length = 0;
+ us_dst.MaximumLength = 0;
+ us_dst.Buffer = NULL;
+
+ nts = pRtlExpandEnvironmentStrings_U(small_env, &us_src, &us_dst, &ul);
+ ok(ul == strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR),
+ "Wrong returned length for %s: %lu <> %u\n",
+ test->src, ul, strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR));
+
+ us_dst.Length = 0;
+ us_dst.MaximumLength = sizeof(dst);
+ us_dst.Buffer = dst;
+
+ nts = pRtlExpandEnvironmentStrings_U(small_env, &us_src, &us_dst, &ul);
+ ok(nts == STATUS_SUCCESS, "Call failed (%lu)\n", nts);
+ ok(ul == us_dst.Length + sizeof(WCHAR),
+ "Wrong returned length for %s: %lu <> %u\n",
+ test->src, ul, us_dst.Length + sizeof(WCHAR));
+ ok(ul == strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR),
+ "Wrong returned length for %s: %lu <> %u\n",
+ test->src, ul, strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR));
+ ok(lstrcmpW(dst, rst) == 0, "Wrong result for %s: expecting %s\n",
+ test->src, test->dst);
+
+ us_dst.Length = 0;
+ us_dst.MaximumLength = 8 * sizeof(WCHAR);
+ us_dst.Buffer = dst;
+ dst[8] = '-';
+ nts = pRtlExpandEnvironmentStrings_U(small_env, &us_src, &us_dst, &ul);
+ ok(nts == STATUS_BUFFER_TOO_SMALL, "Call failed (%lu)\n", nts);
+ ok(ul == strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR),
+ "Wrong returned length for %s (with buffer too small): %lu <> %u\n",
+ test->src, ul, strlen(test->dst) * sizeof(WCHAR) + sizeof(WCHAR));
+ ok(memcmp(dst, rst, 8*sizeof(WCHAR)) == 0,
+ "Wrong result for %s (with buffer too small): expecting %s\n",
+ test->src, test->dst);
+ ok(dst[8] == '-', "Writing too far in buffer (got %c/%d)\n", dst[8], dst[8]);
+ }
+
+}
+
+START_TEST(env)
+{
+ HMODULE mod = GetModuleHandleA("ntdll.dll");
+
+ pRtlMultiByteToUnicodeN = (void *)GetProcAddress(mod,"RtlMultiByteToUnicodeN");
+ pRtlCreateEnvironment = (void*)GetProcAddress(mod, "RtlCreateEnvironment");
+ pRtlDestroyEnvironment = (void*)GetProcAddress(mod, "RtlDestroyEnvironment");
+ pRtlQueryEnvironmentVariable_U = (void*)GetProcAddress(mod, "RtlQueryEnvironmentVariable_U");
+ pRtlSetCurrentEnvironment = (void*)GetProcAddress(mod, "RtlSetCurrentEnvironment");
+ pRtlSetEnvironmentVariable = (void*)GetProcAddress(mod, "RtlSetEnvironmentVariable");
+ pRtlExpandEnvironmentStrings_U = (void*)GetProcAddress(mod, "RtlExpandEnvironmentStrings_U");
+
+ if (pRtlQueryEnvironmentVariable_U)
+ testQuery();
+ if (pRtlSetEnvironmentVariable)
+ testSet();
+ if (pRtlExpandEnvironmentStrings_U)
+ testExpand();
+}
--- /dev/null
+/*
+ * Unit tests for RtlNtStatusToDosError function
+ *
+ * Copyright (c) 2002 Andriy Palamarchuk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+
+#include "wine/test.h"
+
+#include "windef.h"
+#include "winbase.h"
+#include "rpcnterr.h"
+#include "winreg.h"
+#include "winternl.h"
+
+/* FIXME!!! this test checks only mappings, defined by MSDN:
+ * http://support.microsoft.com/default.aspx?scid=KB;EN-US;q113996&
+ * It is necessary to add other mappings and to test them up to Windows XP.
+ *
+ * Some Windows platforms don't know about all the mappings, and in such
+ * cases they return somewhat strange results (Win98) or a generic error
+ * like ERROR_MR_MID_NOT_FOUND (NT4). Our tests have to know about these to
+ * not fail, but we would very much prefer Wine not to return such garbage.
+ * To you can pass the 'strict' option to this test to force it to only check
+ * results against the first listed value. This test should pass in strict
+ * mode on the latest Windows platform (currently XP) and in Wine.
+ * (of course older Windows platforms will fail to pass the strict mode)
+ */
+
+static ULONG (WINAPI *statustodoserror)(NTSTATUS Status);
+static int strict;
+
+static int prepare_test(void)
+{
+ HMODULE ntdll;
+ int argc;
+ char** argv;
+
+ ntdll = LoadLibraryA("ntdll.dll");
+ statustodoserror = (void*)GetProcAddress(ntdll, "RtlNtStatusToDosError");
+ if (!statustodoserror)
+ return 0;
+
+ argc = winetest_get_mainargs(&argv);
+ strict=(argc >= 3 && strcmp(argv[2],"strict")==0);
+ return 1;
+}
+
+static void cmp_call(NTSTATUS win_nt, ULONG win32, const char* message)
+{
+ ULONG err;
+
+ err = statustodoserror(win_nt);
+ ok(err == win32,
+ "%s (%lx): got %ld, expected %ld\n",
+ message, win_nt, err, win32);
+}
+
+static void cmp_call2(NTSTATUS win_nt, ULONG win32, const char* message)
+{
+ ULONG err;
+
+ err = statustodoserror(win_nt);
+ ok(err == win32 ||
+ (!strict && err == ERROR_MR_MID_NOT_FOUND),
+ "%s (%lx): got %ld, expected %ld (or MID_NOT_FOUND)\n",
+ message, win_nt, err, win32);
+}
+
+static void cmp_call3(NTSTATUS win_nt, ULONG win32_1, ULONG win32_2, const char* message)
+{
+ ULONG err;
+
+ err = statustodoserror(win_nt);
+ ok(err == win32_1 || (!strict && err == win32_2),
+ "%s (%lx): got %ld, expected %ld or %ld\n",
+ message, win_nt, err, win32_1, win32_2);
+}
+
+static void cmp_call4(NTSTATUS win_nt, ULONG win32_1, ULONG win32_2, const char* message)
+{
+ ULONG err;
+
+ err = statustodoserror(win_nt);
+ ok(err == win32_1 ||
+ (!strict && (err == win32_2 || err == ERROR_MR_MID_NOT_FOUND)),
+ "%s (%lx): got %ld, expected %ld or %ld\n",
+ message, win_nt, err, win32_1, win32_2);
+}
+
+#define cmp(status, error) \
+ cmp_call(status, error, #status)
+#define cmp2(status, error) \
+ cmp_call2(status, error, #status)
+#define cmp3(status, error1, error2) \
+ cmp_call3(status, error1, error2, #status)
+#define cmp4(status, error1, error2) \
+ cmp_call4(status, error1, error2, #status)
+
+static void run_error_tests(void)
+{
+ cmp(STATUS_DATATYPE_MISALIGNMENT, ERROR_NOACCESS);
+ cmp(STATUS_ACCESS_VIOLATION, ERROR_NOACCESS);
+ cmp2(STATUS_DATATYPE_MISALIGNMENT_ERROR, ERROR_NOACCESS);
+ cmp(STATUS_CTL_FILE_NOT_SUPPORTED, ERROR_NOT_SUPPORTED);
+ cmp(STATUS_PORT_ALREADY_SET, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_SECTION_NOT_IMAGE, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_BAD_WORKING_SET_LIMIT, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_WORKING_SET_LIMIT_RANGE, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INCOMPATIBLE_FILE_MAP, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_PORT_DISCONNECTED, ERROR_INVALID_HANDLE);
+ cmp(STATUS_NOT_LOCKED, ERROR_NOT_LOCKED);
+ cmp(STATUS_NOT_MAPPED_VIEW, ERROR_INVALID_ADDRESS);
+ cmp(STATUS_UNABLE_TO_FREE_VM, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_UNABLE_TO_DELETE_SECTION, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_MORE_PROCESSING_REQUIRED, ERROR_MORE_DATA);
+ cmp(STATUS_INVALID_CID, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_STACK_OVERFLOW, ERROR_STACK_OVERFLOW);
+ cmp(STATUS_BAD_INITIAL_STACK, ERROR_STACK_OVERFLOW);
+ cmp(STATUS_INVALID_VOLUME_LABEL, ERROR_LABEL_TOO_LONG);
+ cmp(STATUS_SECTION_NOT_EXTENDED, ERROR_OUTOFMEMORY);
+ cmp(STATUS_NOT_MAPPED_DATA, ERROR_INVALID_ADDRESS);
+ cmp2(STATUS_NO_LDT, ERROR_INVALID_THREAD_ID);
+ cmp(STATUS_INFO_LENGTH_MISMATCH, ERROR_BAD_LENGTH);
+ cmp(STATUS_INVALID_INFO_CLASS, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_SUSPEND_COUNT_EXCEEDED, ERROR_SIGNAL_REFUSED);
+ cmp(STATUS_NOTIFY_ENUM_DIR, ERROR_NOTIFY_ENUM_DIR);
+ cmp(STATUS_REGISTRY_RECOVERED, ERROR_REGISTRY_RECOVERED);
+ cmp(STATUS_REGISTRY_IO_FAILED, ERROR_REGISTRY_IO_FAILED);
+ cmp(STATUS_NOT_REGISTRY_FILE, ERROR_NOT_REGISTRY_FILE);
+ cmp(STATUS_KEY_DELETED, ERROR_KEY_DELETED);
+ cmp(STATUS_NO_LOG_SPACE, ERROR_NO_LOG_SPACE);
+ cmp(STATUS_KEY_HAS_CHILDREN, ERROR_KEY_HAS_CHILDREN);
+ cmp(STATUS_CHILD_MUST_BE_VOLATILE, ERROR_CHILD_MUST_BE_VOLATILE);
+ cmp(STATUS_REGISTRY_CORRUPT, ERROR_BADDB);
+ cmp(STATUS_DLL_NOT_FOUND, ERROR_MOD_NOT_FOUND);
+ cmp(STATUS_DLL_INIT_FAILED, ERROR_DLL_INIT_FAILED);
+ cmp2(STATUS_INVALID_IMPORT_OF_NON_DLL, ERROR_INVALID_IMPORT_OF_NON_DLL);
+ cmp(STATUS_ORDINAL_NOT_FOUND, ERROR_INVALID_ORDINAL);
+ cmp(STATUS_DRIVER_ORDINAL_NOT_FOUND, ERROR_INVALID_ORDINAL);
+ cmp2(STATUS_DRIVER_UNABLE_TO_LOAD, ERROR_BAD_DRIVER);
+ cmp(STATUS_ENTRYPOINT_NOT_FOUND, ERROR_PROC_NOT_FOUND);
+ cmp(STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, ERROR_PROC_NOT_FOUND);
+ cmp(STATUS_PENDING, ERROR_IO_PENDING);
+ cmp(STATUS_MORE_ENTRIES, ERROR_MORE_DATA);
+ cmp(STATUS_INTEGER_OVERFLOW, ERROR_ARITHMETIC_OVERFLOW);
+ cmp(STATUS_BUFFER_OVERFLOW, ERROR_MORE_DATA);
+ cmp(STATUS_NO_MORE_FILES, ERROR_NO_MORE_FILES);
+ cmp(STATUS_NO_INHERITANCE, ERROR_NO_INHERITANCE);
+ cmp(STATUS_NO_MORE_EAS, ERROR_NO_MORE_ITEMS);
+ cmp(STATUS_NO_MORE_ENTRIES, ERROR_NO_MORE_ITEMS);
+ cmp(STATUS_GUIDS_EXHAUSTED, ERROR_NO_MORE_ITEMS);
+ cmp(STATUS_AGENTS_EXHAUSTED, ERROR_NO_MORE_ITEMS);
+ cmp(STATUS_UNSUCCESSFUL, ERROR_GEN_FAILURE);
+ cmp(STATUS_TOO_MANY_LINKS, ERROR_TOO_MANY_LINKS);
+ cmp(STATUS_NOT_IMPLEMENTED, ERROR_INVALID_FUNCTION);
+ cmp(STATUS_ILLEGAL_FUNCTION, ERROR_INVALID_FUNCTION);
+ cmp(STATUS_IN_PAGE_ERROR, ERROR_SWAPERROR);
+ cmp(STATUS_PAGEFILE_QUOTA, ERROR_PAGEFILE_QUOTA);
+ cmp(STATUS_COMMITMENT_LIMIT, ERROR_COMMITMENT_LIMIT);
+ cmp(STATUS_SECTION_TOO_BIG, ERROR_NOT_ENOUGH_MEMORY);
+ cmp(RPC_NT_SS_IN_NULL_CONTEXT, ERROR_INVALID_HANDLE);
+ cmp(RPC_NT_INVALID_BINDING, ERROR_INVALID_HANDLE);
+ cmp(STATUS_INVALID_HANDLE, ERROR_INVALID_HANDLE);
+ cmp(STATUS_OBJECT_TYPE_MISMATCH, ERROR_INVALID_HANDLE);
+ cmp(STATUS_FILE_CLOSED, ERROR_INVALID_HANDLE);
+ cmp(STATUS_INVALID_PORT_HANDLE, ERROR_INVALID_HANDLE);
+ cmp(STATUS_HANDLE_NOT_CLOSABLE, ERROR_INVALID_HANDLE);
+ cmp(STATUS_NOT_COMMITTED, ERROR_INVALID_ADDRESS);
+ cmp(STATUS_PARTIAL_COPY, ERROR_PARTIAL_COPY);
+ cmp(STATUS_LPC_REPLY_LOST, ERROR_INTERNAL_ERROR);
+ cmp(STATUS_INVALID_PARAMETER, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_1, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_2, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_3, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_4, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_5, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_6, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_7, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_8, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_9, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_10, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_11, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_12, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PARAMETER_MIX, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_INVALID_PAGE_PROTECTION, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_SECTION_PROTECTION, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_RESOURCE_DATA_NOT_FOUND, ERROR_RESOURCE_DATA_NOT_FOUND);
+ cmp(STATUS_RESOURCE_TYPE_NOT_FOUND, ERROR_RESOURCE_TYPE_NOT_FOUND);
+ cmp(STATUS_RESOURCE_NAME_NOT_FOUND, ERROR_RESOURCE_NAME_NOT_FOUND);
+ cmp(STATUS_RESOURCE_LANG_NOT_FOUND, ERROR_RESOURCE_LANG_NOT_FOUND);
+ cmp(STATUS_NO_SUCH_DEVICE, ERROR_FILE_NOT_FOUND);
+ cmp(STATUS_NO_SUCH_FILE, ERROR_FILE_NOT_FOUND);
+ cmp(STATUS_INVALID_DEVICE_REQUEST, ERROR_INVALID_FUNCTION);
+ cmp2(STATUS_VOLUME_NOT_UPGRADED, ERROR_INVALID_FUNCTION);
+ cmp(STATUS_END_OF_FILE, ERROR_HANDLE_EOF);
+ cmp(STATUS_FILE_FORCED_CLOSED, ERROR_HANDLE_EOF);
+ cmp(STATUS_WRONG_VOLUME, ERROR_WRONG_DISK);
+ cmp(STATUS_NO_MEDIA, ERROR_NO_MEDIA_IN_DRIVE);
+ cmp(STATUS_NO_MEDIA_IN_DEVICE, ERROR_NOT_READY);
+ cmp(STATUS_VOLUME_DISMOUNTED, ERROR_NOT_READY);
+ cmp(STATUS_NONEXISTENT_SECTOR, ERROR_SECTOR_NOT_FOUND);
+ cmp(STATUS_WORKING_SET_QUOTA, ERROR_WORKING_SET_QUOTA);
+ cmp(STATUS_NO_MEMORY, ERROR_NOT_ENOUGH_MEMORY);
+ cmp(STATUS_CONFLICTING_ADDRESSES, ERROR_INVALID_ADDRESS);
+ cmp(STATUS_INVALID_SYSTEM_SERVICE, ERROR_INVALID_FUNCTION);
+ cmp(STATUS_THREAD_IS_TERMINATING, ERROR_ACCESS_DENIED);
+ cmp(STATUS_PROCESS_IS_TERMINATING, ERROR_ACCESS_DENIED);
+ cmp(STATUS_INVALID_LOCK_SEQUENCE, ERROR_ACCESS_DENIED);
+ cmp(STATUS_INVALID_VIEW_SIZE, ERROR_ACCESS_DENIED);
+ cmp(STATUS_ALREADY_COMMITTED, ERROR_ACCESS_DENIED);
+ cmp(STATUS_ACCESS_DENIED, ERROR_ACCESS_DENIED);
+ cmp(STATUS_FILE_IS_A_DIRECTORY, ERROR_ACCESS_DENIED);
+ cmp(STATUS_CANNOT_DELETE, ERROR_ACCESS_DENIED);
+ cmp(STATUS_INVALID_COMPUTER_NAME, ERROR_INVALID_COMPUTERNAME);
+ cmp(STATUS_FILE_DELETED, ERROR_ACCESS_DENIED);
+ cmp2(STATUS_FILE_RENAMED, ERROR_ACCESS_DENIED);
+ cmp(STATUS_DELETE_PENDING, ERROR_ACCESS_DENIED);
+ cmp(STATUS_PORT_CONNECTION_REFUSED, ERROR_ACCESS_DENIED);
+ cmp(STATUS_NO_SUCH_PRIVILEGE, ERROR_NO_SUCH_PRIVILEGE);
+ cmp(STATUS_PRIVILEGE_NOT_HELD, ERROR_PRIVILEGE_NOT_HELD);
+ cmp(STATUS_CANNOT_IMPERSONATE, ERROR_CANNOT_IMPERSONATE);
+ cmp(STATUS_LOGON_FAILURE, ERROR_LOGON_FAILURE);
+ cmp2(STATUS_MUTUAL_AUTHENTICATION_FAILED, ERROR_MUTUAL_AUTH_FAILED);
+ cmp2(STATUS_TIME_DIFFERENCE_AT_DC, ERROR_TIME_SKEW);
+ cmp2(STATUS_PKINIT_FAILURE, ERROR_PKINIT_FAILURE);
+ cmp2(STATUS_SMARTCARD_SUBSYSTEM_FAILURE, ERROR_SMARTCARD_SUBSYSTEM_FAILURE);
+ cmp2(STATUS_DOWNGRADE_DETECTED, ERROR_DOWNGRADE_DETECTED);
+ cmp2(STATUS_SMARTCARD_CERT_REVOKED, SEC_E_SMARTCARD_CERT_REVOKED);
+ cmp2(STATUS_ISSUING_CA_UNTRUSTED, SEC_E_ISSUING_CA_UNTRUSTED);
+ cmp2(STATUS_REVOCATION_OFFLINE_C, SEC_E_REVOCATION_OFFLINE_C);
+ cmp2(STATUS_PKINIT_CLIENT_FAILURE, SEC_E_PKINIT_CLIENT_FAILURE);
+ cmp2(STATUS_SMARTCARD_CERT_EXPIRED, SEC_E_SMARTCARD_CERT_EXPIRED);
+ cmp2(STATUS_NO_KERB_KEY, SEC_E_NO_KERB_KEY);
+ cmp2(STATUS_CURRENT_DOMAIN_NOT_ALLOWED, ERROR_CURRENT_DOMAIN_NOT_ALLOWED);
+ cmp2(STATUS_SMARTCARD_WRONG_PIN, SCARD_W_WRONG_CHV);
+ cmp2(STATUS_SMARTCARD_CARD_BLOCKED, SCARD_W_CHV_BLOCKED);
+ cmp2(STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED,SCARD_W_CARD_NOT_AUTHENTICATED);
+ cmp2(STATUS_SMARTCARD_NO_CARD, SCARD_E_NO_SMARTCARD);
+ cmp2(STATUS_SMARTCARD_NO_KEY_CONTAINER, NTE_NO_KEY);
+ cmp2(STATUS_SMARTCARD_NO_CERTIFICATE, SCARD_E_NO_SUCH_CERTIFICATE);
+ cmp2(STATUS_SMARTCARD_NO_KEYSET, NTE_BAD_KEYSET);
+ cmp2(STATUS_SMARTCARD_IO_ERROR, SCARD_E_COMM_DATA_LOST);
+ cmp(STATUS_ACCOUNT_RESTRICTION, ERROR_ACCOUNT_RESTRICTION);
+ cmp(STATUS_INVALID_LOGON_HOURS, ERROR_INVALID_LOGON_HOURS);
+ cmp(STATUS_INVALID_WORKSTATION, ERROR_INVALID_WORKSTATION);
+ cmp(STATUS_BUFFER_TOO_SMALL, ERROR_INSUFFICIENT_BUFFER);
+ cmp(STATUS_UNABLE_TO_DECOMMIT_VM, ERROR_INVALID_ADDRESS);
+ cmp(STATUS_DISK_CORRUPT_ERROR, ERROR_DISK_CORRUPT);
+ cmp(STATUS_FT_MISSING_MEMBER, ERROR_IO_DEVICE);
+ cmp(STATUS_FT_ORPHANING, ERROR_IO_DEVICE);
+ cmp(STATUS_VARIABLE_NOT_FOUND, ERROR_ENVVAR_NOT_FOUND);
+ cmp(STATUS_OBJECT_NAME_INVALID, ERROR_INVALID_NAME);
+ cmp(STATUS_OBJECT_NAME_NOT_FOUND, ERROR_FILE_NOT_FOUND);
+ cmp(STATUS_OBJECT_NAME_COLLISION, ERROR_ALREADY_EXISTS);
+ cmp(STATUS_OBJECT_PATH_INVALID, ERROR_BAD_PATHNAME);
+ cmp(STATUS_OBJECT_PATH_NOT_FOUND, ERROR_PATH_NOT_FOUND);
+ cmp(STATUS_DFS_EXIT_PATH_FOUND, ERROR_PATH_NOT_FOUND);
+ cmp2(STATUS_DFS_UNAVAILABLE, ERROR_CONNECTION_UNAVAIL);
+ cmp(STATUS_OBJECT_PATH_SYNTAX_BAD, ERROR_BAD_PATHNAME);
+ cmp(STATUS_NAME_TOO_LONG, ERROR_FILENAME_EXCED_RANGE);
+ cmp(STATUS_DATA_OVERRUN, ERROR_IO_DEVICE);
+ cmp(STATUS_DATA_LATE_ERROR, ERROR_IO_DEVICE);
+ cmp(STATUS_DATA_ERROR, ERROR_CRC);
+ cmp(STATUS_CRC_ERROR, ERROR_CRC);
+ cmp(STATUS_SHARING_VIOLATION, ERROR_SHARING_VIOLATION);
+ cmp(STATUS_QUOTA_EXCEEDED, ERROR_NOT_ENOUGH_QUOTA);
+ cmp(STATUS_MUTANT_NOT_OWNED, ERROR_NOT_OWNER);
+ cmp(STATUS_SEMAPHORE_LIMIT_EXCEEDED, ERROR_TOO_MANY_POSTS);
+ cmp(STATUS_DISK_FULL, ERROR_DISK_FULL);
+ cmp(STATUS_LOCK_NOT_GRANTED, ERROR_LOCK_VIOLATION);
+ cmp(STATUS_FILE_LOCK_CONFLICT, ERROR_LOCK_VIOLATION);
+ cmp(STATUS_NOT_A_DIRECTORY, ERROR_DIRECTORY);
+ cmp2(STATUS_CANNOT_MAKE, ERROR_CANNOT_MAKE);
+ cmp(STATUS_UNKNOWN_REVISION, ERROR_UNKNOWN_REVISION);
+ cmp(STATUS_REVISION_MISMATCH, ERROR_REVISION_MISMATCH);
+ cmp(STATUS_INVALID_OWNER, ERROR_INVALID_OWNER);
+ cmp(STATUS_INVALID_PRIMARY_GROUP, ERROR_INVALID_PRIMARY_GROUP);
+ cmp(STATUS_NO_IMPERSONATION_TOKEN, ERROR_NO_IMPERSONATION_TOKEN);
+ cmp(STATUS_CANT_DISABLE_MANDATORY, ERROR_CANT_DISABLE_MANDATORY);
+ cmp(STATUS_NO_LOGON_SERVERS, ERROR_NO_LOGON_SERVERS);
+ cmp(STATUS_DOMAIN_CONTROLLER_NOT_FOUND, ERROR_DOMAIN_CONTROLLER_NOT_FOUND);
+ cmp(STATUS_NO_SUCH_LOGON_SESSION, ERROR_NO_SUCH_LOGON_SESSION);
+ cmp(STATUS_INVALID_ACCOUNT_NAME, ERROR_INVALID_ACCOUNT_NAME);
+ cmp(STATUS_USER_EXISTS, ERROR_USER_EXISTS);
+ cmp(STATUS_NO_SUCH_USER, ERROR_NO_SUCH_USER);
+ cmp(STATUS_GROUP_EXISTS, ERROR_GROUP_EXISTS);
+ cmp(STATUS_NO_SUCH_GROUP, ERROR_NO_SUCH_GROUP);
+ cmp(STATUS_SPECIAL_GROUP, ERROR_SPECIAL_GROUP);
+ cmp(STATUS_MEMBER_IN_GROUP, ERROR_MEMBER_IN_GROUP);
+ cmp(STATUS_MEMBER_NOT_IN_GROUP, ERROR_MEMBER_NOT_IN_GROUP);
+ cmp(STATUS_LAST_ADMIN, ERROR_LAST_ADMIN);
+ cmp(STATUS_WRONG_PASSWORD, ERROR_INVALID_PASSWORD);
+ cmp(STATUS_WRONG_PASSWORD_CORE, ERROR_INVALID_PASSWORD);
+ cmp(STATUS_ILL_FORMED_PASSWORD, ERROR_ILL_FORMED_PASSWORD);
+ cmp(STATUS_PASSWORD_RESTRICTION, ERROR_PASSWORD_RESTRICTION);
+ cmp(STATUS_PASSWORD_EXPIRED, ERROR_PASSWORD_EXPIRED);
+ cmp(STATUS_PASSWORD_MUST_CHANGE, ERROR_PASSWORD_MUST_CHANGE);
+ cmp(STATUS_ACCOUNT_DISABLED, ERROR_ACCOUNT_DISABLED);
+ cmp(STATUS_ACCOUNT_LOCKED_OUT, ERROR_ACCOUNT_LOCKED_OUT);
+ cmp(STATUS_NONE_MAPPED, ERROR_NONE_MAPPED);
+ cmp(STATUS_TOO_MANY_LUIDS_REQUESTED, ERROR_TOO_MANY_LUIDS_REQUESTED);
+ cmp(STATUS_LUIDS_EXHAUSTED, ERROR_LUIDS_EXHAUSTED);
+ cmp(STATUS_INVALID_SUB_AUTHORITY, ERROR_INVALID_SUB_AUTHORITY);
+ cmp(STATUS_INVALID_ACL, ERROR_INVALID_ACL);
+ cmp(STATUS_INVALID_SID, ERROR_INVALID_SID);
+ cmp(STATUS_INVALID_SECURITY_DESCR, ERROR_INVALID_SECURITY_DESCR);
+ cmp(STATUS_PROCEDURE_NOT_FOUND, ERROR_PROC_NOT_FOUND);
+ cmp(STATUS_BAD_INITIAL_PC, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_INVALID_FILE_FOR_SECTION, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_INVALID_IMAGE_FORMAT, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_IMAGE_MP_UP_MISMATCH, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_INVALID_IMAGE_NOT_MZ, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_IMAGE_CHECKSUM_MISMATCH, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_INVALID_IMAGE_PROTECT, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_INVALID_IMAGE_LE_FORMAT, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_INVALID_IMAGE_NE_FORMAT, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_INVALID_IMAGE_WIN_16, ERROR_BAD_EXE_FORMAT);
+ cmp2(STATUS_INVALID_IMAGE_WIN_32, ERROR_BAD_EXE_FORMAT);
+ cmp2(STATUS_INVALID_IMAGE_WIN_64, ERROR_BAD_EXE_FORMAT);
+ cmp(STATUS_NO_TOKEN, ERROR_NO_TOKEN);
+ cmp(STATUS_RANGE_NOT_LOCKED, ERROR_NOT_LOCKED);
+ cmp(STATUS_SERVER_DISABLED, ERROR_SERVER_DISABLED);
+ cmp(STATUS_SERVER_NOT_DISABLED, ERROR_SERVER_NOT_DISABLED);
+ cmp(STATUS_INVALID_ID_AUTHORITY, ERROR_INVALID_ID_AUTHORITY);
+ cmp(STATUS_ALLOTTED_SPACE_EXCEEDED, ERROR_ALLOTTED_SPACE_EXCEEDED);
+ cmp(STATUS_TOO_MANY_PAGING_FILES, ERROR_NOT_ENOUGH_MEMORY);
+ cmp(STATUS_INSUFFICIENT_RESOURCES, ERROR_NO_SYSTEM_RESOURCES);
+ cmp(STATUS_INSUFF_SERVER_RESOURCES, ERROR_NOT_ENOUGH_SERVER_MEMORY);
+ cmp(STATUS_FILE_INVALID, ERROR_FILE_INVALID);
+ cmp(STATUS_MAPPED_FILE_SIZE_ZERO, ERROR_FILE_INVALID);
+ cmp(STATUS_DEVICE_PAPER_EMPTY, ERROR_OUT_OF_PAPER);
+ cmp(STATUS_DEVICE_POWERED_OFF, ERROR_NOT_READY);
+ cmp(STATUS_DEVICE_OFF_LINE, ERROR_NOT_READY);
+ cmp(STATUS_DEVICE_DATA_ERROR, ERROR_CRC);
+ cmp(STATUS_DEVICE_NOT_READY, ERROR_NOT_READY);
+ cmp3(STATUS_DEVICE_NOT_CONNECTED, ERROR_DEVICE_NOT_CONNECTED, ERROR_NOT_READY);
+ cmp(STATUS_DEVICE_POWER_FAILURE, ERROR_NOT_READY);
+ cmp2(STATUS_NOT_FOUND, ERROR_NOT_FOUND);
+ cmp2(STATUS_NO_MATCH, ERROR_NO_MATCH);
+ cmp2(STATUS_PROPSET_NOT_FOUND, ERROR_SET_NOT_FOUND);
+ cmp(STATUS_DEVICE_BUSY, ERROR_BUSY);
+ cmp(STATUS_FREE_VM_NOT_AT_BASE, ERROR_INVALID_ADDRESS);
+ cmp(STATUS_MEMORY_NOT_ALLOCATED, ERROR_INVALID_ADDRESS);
+ cmp(STATUS_NOT_SAME_DEVICE, ERROR_NOT_SAME_DEVICE);
+ cmp(STATUS_NOT_SUPPORTED, ERROR_NOT_SUPPORTED);
+ cmp(STATUS_REMOTE_NOT_LISTENING, ERROR_REM_NOT_LIST);
+ cmp(STATUS_DUPLICATE_NAME, ERROR_DUP_NAME);
+ cmp(STATUS_BAD_NETWORK_PATH, ERROR_BAD_NETPATH);
+ cmp(STATUS_NETWORK_BUSY, ERROR_NETWORK_BUSY);
+ cmp2(STATUS_ONLY_IF_CONNECTED, ERROR_ONLY_IF_CONNECTED);
+ cmp(STATUS_DEVICE_DOES_NOT_EXIST, ERROR_DEV_NOT_EXIST);
+ cmp(STATUS_TOO_MANY_COMMANDS, ERROR_TOO_MANY_CMDS);
+ cmp(STATUS_ADAPTER_HARDWARE_ERROR, ERROR_ADAP_HDW_ERR);
+ cmp(STATUS_REDIRECTOR_NOT_STARTED, ERROR_PATH_NOT_FOUND);
+ cmp(STATUS_INVALID_EA_NAME, ERROR_INVALID_EA_NAME);
+ cmp(STATUS_EA_LIST_INCONSISTENT, ERROR_EA_LIST_INCONSISTENT);
+ cmp(STATUS_EA_TOO_LARGE, ERROR_EA_LIST_INCONSISTENT);
+ cmp(STATUS_INVALID_EA_FLAG, ERROR_EA_LIST_INCONSISTENT);
+ cmp2(STATUS_EAS_NOT_SUPPORTED, ERROR_EAS_NOT_SUPPORTED);
+ cmp(STATUS_FILE_CORRUPT_ERROR, ERROR_FILE_CORRUPT);
+ cmp(STATUS_EA_CORRUPT_ERROR, ERROR_FILE_CORRUPT);
+ cmp(STATUS_NONEXISTENT_EA_ENTRY, ERROR_FILE_CORRUPT);
+ cmp(STATUS_NO_EAS_ON_FILE, ERROR_FILE_CORRUPT);
+ cmp2(STATUS_NOT_A_REPARSE_POINT, ERROR_NOT_A_REPARSE_POINT);
+ cmp4(STATUS_IO_REPARSE_TAG_INVALID, ERROR_REPARSE_TAG_INVALID, ERROR_INVALID_PARAMETER);
+ cmp4(STATUS_IO_REPARSE_TAG_MISMATCH, ERROR_REPARSE_TAG_MISMATCH, ERROR_INVALID_PARAMETER);
+ cmp2(STATUS_IO_REPARSE_TAG_NOT_HANDLED, ERROR_CANT_ACCESS_FILE);
+ cmp2(STATUS_REPARSE_POINT_NOT_RESOLVED, ERROR_CANT_RESOLVE_FILENAME);
+ cmp2(STATUS_DIRECTORY_IS_A_REPARSE_POINT, ERROR_BAD_PATHNAME);
+ cmp2(STATUS_REPARSE_ATTRIBUTE_CONFLICT, ERROR_REPARSE_ATTRIBUTE_CONFLICT);
+ cmp4(STATUS_IO_REPARSE_DATA_INVALID, ERROR_INVALID_REPARSE_DATA, ERROR_INVALID_PARAMETER);
+ cmp2(STATUS_FILE_IS_OFFLINE, ERROR_FILE_OFFLINE);
+ cmp2(STATUS_REMOTE_STORAGE_NOT_ACTIVE, ERROR_REMOTE_STORAGE_NOT_ACTIVE);
+ cmp2(STATUS_REMOTE_STORAGE_MEDIA_ERROR, ERROR_REMOTE_STORAGE_MEDIA_ERROR);
+ cmp2(STATUS_NO_TRACKING_SERVICE, ERROR_NO_TRACKING_SERVICE);
+ cmp2(STATUS_JOURNAL_DELETE_IN_PROGRESS, ERROR_JOURNAL_DELETE_IN_PROGRESS);
+ cmp2(STATUS_JOURNAL_NOT_ACTIVE, ERROR_JOURNAL_NOT_ACTIVE);
+ cmp2(STATUS_JOURNAL_ENTRY_DELETED, ERROR_JOURNAL_ENTRY_DELETED);
+ cmp(STATUS_INVALID_NETWORK_RESPONSE, ERROR_BAD_NET_RESP);
+ cmp(STATUS_USER_SESSION_DELETED, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_UNEXPECTED_NETWORK_ERROR, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_BAD_REMOTE_ADAPTER, ERROR_BAD_REM_ADAP);
+ cmp(STATUS_PRINT_QUEUE_FULL, ERROR_PRINTQ_FULL);
+ cmp(STATUS_NO_SPOOL_SPACE, ERROR_NO_SPOOL_SPACE);
+ cmp(STATUS_PRINT_CANCELLED, ERROR_PRINT_CANCELLED);
+ cmp(STATUS_NETWORK_NAME_DELETED, ERROR_NETNAME_DELETED);
+ cmp(STATUS_NETWORK_ACCESS_DENIED, ERROR_NETWORK_ACCESS_DENIED);
+ cmp(STATUS_BAD_DEVICE_TYPE, ERROR_BAD_DEV_TYPE);
+ cmp(STATUS_BAD_NETWORK_NAME, ERROR_BAD_NET_NAME);
+ cmp(STATUS_TOO_MANY_NAMES, ERROR_TOO_MANY_NAMES);
+ cmp(STATUS_TOO_MANY_GUIDS_REQUESTED, ERROR_TOO_MANY_NAMES);
+ cmp(STATUS_TOO_MANY_ADDRESSES, ERROR_TOO_MANY_NAMES);
+ cmp(STATUS_TOO_MANY_NODES, ERROR_TOO_MANY_NAMES);
+ cmp(STATUS_TOO_MANY_SESSIONS, ERROR_TOO_MANY_SESS);
+ cmp(STATUS_SHARING_PAUSED, ERROR_SHARING_PAUSED);
+ cmp(STATUS_REQUEST_NOT_ACCEPTED, ERROR_REQ_NOT_ACCEP);
+ cmp(STATUS_REDIRECTOR_PAUSED, ERROR_REDIR_PAUSED);
+ cmp(STATUS_NET_WRITE_FAULT, ERROR_NET_WRITE_FAULT);
+ cmp(STATUS_VIRTUAL_CIRCUIT_CLOSED, ERROR_VC_DISCONNECTED);
+ cmp(STATUS_INVALID_PIPE_STATE, ERROR_BAD_PIPE);
+ cmp(STATUS_INVALID_READ_MODE, ERROR_BAD_PIPE);
+ cmp(STATUS_PIPE_CLOSING, ERROR_NO_DATA);
+ cmp(STATUS_PIPE_EMPTY, ERROR_NO_DATA);
+ cmp(STATUS_PIPE_CONNECTED, ERROR_PIPE_CONNECTED);
+ cmp(STATUS_PIPE_DISCONNECTED, ERROR_PIPE_NOT_CONNECTED);
+ cmp(STATUS_PIPE_LISTENING, ERROR_PIPE_LISTENING);
+ cmp(STATUS_PIPE_NOT_AVAILABLE, ERROR_PIPE_BUSY);
+ cmp(STATUS_INSTANCE_NOT_AVAILABLE, ERROR_PIPE_BUSY);
+ cmp(STATUS_PIPE_BUSY, ERROR_PIPE_BUSY);
+ cmp(STATUS_PIPE_BROKEN, ERROR_BROKEN_PIPE);
+ cmp(STATUS_DIRECTORY_NOT_EMPTY, ERROR_DIR_NOT_EMPTY);
+ cmp(STATUS_TOO_MANY_OPENED_FILES, ERROR_TOO_MANY_OPEN_FILES);
+ cmp(STATUS_IO_TIMEOUT, ERROR_SEM_TIMEOUT);
+ cmp(STATUS_CANCELLED, ERROR_OPERATION_ABORTED);
+ cmp(STATUS_UNRECOGNIZED_MEDIA, ERROR_UNRECOGNIZED_MEDIA);
+ cmp(STATUS_INVALID_LEVEL, ERROR_INVALID_LEVEL);
+ cmp(STATUS_UNRECOGNIZED_VOLUME, ERROR_UNRECOGNIZED_VOLUME);
+ cmp(STATUS_MEDIA_WRITE_PROTECTED, ERROR_WRITE_PROTECT);
+ cmp(STATUS_TOO_LATE, ERROR_WRITE_PROTECT);
+ cmp(STATUS_SUCCESS, NO_ERROR);
+ cmp(STATUS_FULLSCREEN_MODE, ERROR_FULLSCREEN_MODE);
+ cmp(STATUS_END_OF_MEDIA, ERROR_END_OF_MEDIA);
+ cmp(STATUS_EOM_OVERFLOW, ERROR_EOM_OVERFLOW);
+ cmp(STATUS_BEGINNING_OF_MEDIA, ERROR_BEGINNING_OF_MEDIA);
+ cmp(STATUS_MEDIA_CHANGED, ERROR_MEDIA_CHANGED);
+ cmp(STATUS_BUS_RESET, ERROR_BUS_RESET);
+ cmp(STATUS_FILEMARK_DETECTED, ERROR_FILEMARK_DETECTED);
+ cmp(STATUS_SETMARK_DETECTED, ERROR_SETMARK_DETECTED);
+ cmp(STATUS_NO_DATA_DETECTED, ERROR_NO_DATA_DETECTED);
+ cmp(STATUS_PARTITION_FAILURE, ERROR_PARTITION_FAILURE);
+ cmp(STATUS_INVALID_BLOCK_LENGTH, ERROR_INVALID_BLOCK_LENGTH);
+ cmp(STATUS_DEVICE_NOT_PARTITIONED, ERROR_DEVICE_NOT_PARTITIONED);
+ cmp(STATUS_UNABLE_TO_LOCK_MEDIA, ERROR_UNABLE_TO_LOCK_MEDIA);
+ cmp(STATUS_UNABLE_TO_UNLOAD_MEDIA, ERROR_UNABLE_TO_UNLOAD_MEDIA);
+ cmp(STATUS_UNMAPPABLE_CHARACTER, ERROR_NO_UNICODE_TRANSLATION);
+ cmp(STATUS_NOT_ALL_ASSIGNED, ERROR_NOT_ALL_ASSIGNED);
+ cmp(STATUS_SOME_NOT_MAPPED, ERROR_SOME_NOT_MAPPED);
+ cmp(STATUS_NO_QUOTAS_FOR_ACCOUNT, ERROR_NO_QUOTAS_FOR_ACCOUNT);
+ cmp(STATUS_LOCAL_USER_SESSION_KEY, ERROR_LOCAL_USER_SESSION_KEY);
+ cmp(STATUS_NULL_LM_PASSWORD, ERROR_NULL_LM_PASSWORD);
+ cmp(STATUS_BAD_INHERITANCE_ACL, ERROR_BAD_INHERITANCE_ACL);
+ cmp(STATUS_INVALID_GROUP_ATTRIBUTES, ERROR_INVALID_GROUP_ATTRIBUTES);
+ cmp(STATUS_BAD_IMPERSONATION_LEVEL, ERROR_BAD_IMPERSONATION_LEVEL);
+ cmp(STATUS_CANT_OPEN_ANONYMOUS, ERROR_CANT_OPEN_ANONYMOUS);
+ cmp(STATUS_BAD_VALIDATION_CLASS, ERROR_BAD_VALIDATION_CLASS);
+ cmp(STATUS_BAD_TOKEN_TYPE, ERROR_BAD_TOKEN_TYPE);
+ cmp2(STATUS_BAD_MASTER_BOOT_RECORD, ERROR_INVALID_PARAMETER);
+ cmp(STATUS_NO_SECURITY_ON_OBJECT, ERROR_NO_SECURITY_ON_OBJECT);
+ cmp(STATUS_CANT_ACCESS_DOMAIN_INFO, ERROR_CANT_ACCESS_DOMAIN_INFO);
+ cmp(STATUS_INVALID_SERVER_STATE, ERROR_INVALID_SERVER_STATE);
+ cmp(STATUS_INVALID_DOMAIN_STATE, ERROR_INVALID_DOMAIN_STATE);
+ cmp(STATUS_INVALID_DOMAIN_ROLE, ERROR_INVALID_DOMAIN_ROLE);
+ cmp(STATUS_NO_SUCH_DOMAIN, ERROR_NO_SUCH_DOMAIN);
+ cmp(STATUS_DOMAIN_EXISTS, ERROR_DOMAIN_EXISTS);
+ cmp(STATUS_DOMAIN_LIMIT_EXCEEDED, ERROR_DOMAIN_LIMIT_EXCEEDED);
+ cmp2(STATUS_OPLOCK_NOT_GRANTED, ERROR_OPLOCK_NOT_GRANTED);
+ cmp2(STATUS_INVALID_OPLOCK_PROTOCOL, ERROR_INVALID_OPLOCK_PROTOCOL);
+ cmp(STATUS_INTERNAL_DB_CORRUPTION, ERROR_INTERNAL_DB_CORRUPTION);
+ cmp(STATUS_INTERNAL_ERROR, ERROR_INTERNAL_ERROR);
+ cmp(STATUS_GENERIC_NOT_MAPPED, ERROR_GENERIC_NOT_MAPPED);
+ cmp(STATUS_BAD_DESCRIPTOR_FORMAT, ERROR_BAD_DESCRIPTOR_FORMAT);
+ cmp(STATUS_NOT_LOGON_PROCESS, ERROR_NOT_LOGON_PROCESS);
+ cmp(STATUS_LOGON_SESSION_EXISTS, ERROR_LOGON_SESSION_EXISTS);
+ cmp(STATUS_NO_SUCH_PACKAGE, ERROR_NO_SUCH_PACKAGE);
+ cmp(STATUS_BAD_LOGON_SESSION_STATE, ERROR_BAD_LOGON_SESSION_STATE);
+ cmp(STATUS_LOGON_SESSION_COLLISION, ERROR_LOGON_SESSION_COLLISION);
+ cmp(STATUS_INVALID_LOGON_TYPE, ERROR_INVALID_LOGON_TYPE);
+ cmp(STATUS_RXACT_INVALID_STATE, ERROR_RXACT_INVALID_STATE);
+ cmp(STATUS_RXACT_COMMIT_FAILURE, ERROR_RXACT_COMMIT_FAILURE);
+ cmp(STATUS_SPECIAL_ACCOUNT, ERROR_SPECIAL_ACCOUNT);
+ cmp(STATUS_SPECIAL_USER, ERROR_SPECIAL_USER);
+ cmp(STATUS_MEMBERS_PRIMARY_GROUP, ERROR_MEMBERS_PRIMARY_GROUP);
+ cmp(STATUS_TOKEN_ALREADY_IN_USE, ERROR_TOKEN_ALREADY_IN_USE);
+ cmp(STATUS_NO_SUCH_ALIAS, ERROR_NO_SUCH_ALIAS);
+ cmp(STATUS_MEMBER_NOT_IN_ALIAS, ERROR_MEMBER_NOT_IN_ALIAS);
+ cmp(STATUS_MEMBER_IN_ALIAS, ERROR_MEMBER_IN_ALIAS);
+ cmp(STATUS_ALIAS_EXISTS, ERROR_ALIAS_EXISTS);
+ cmp(STATUS_LOGON_NOT_GRANTED, ERROR_LOGON_NOT_GRANTED);
+ cmp(STATUS_TOO_MANY_SECRETS, ERROR_TOO_MANY_SECRETS);
+ cmp(STATUS_SECRET_TOO_LONG, ERROR_SECRET_TOO_LONG);
+ cmp(STATUS_INTERNAL_DB_ERROR, ERROR_INTERNAL_DB_ERROR);
+ cmp(STATUS_TOO_MANY_CONTEXT_IDS, ERROR_TOO_MANY_CONTEXT_IDS);
+ cmp(STATUS_LOGON_TYPE_NOT_GRANTED, ERROR_LOGON_TYPE_NOT_GRANTED);
+ cmp(STATUS_NT_CROSS_ENCRYPTION_REQUIRED, ERROR_NT_CROSS_ENCRYPTION_REQUIRED);
+ cmp(STATUS_NO_SUCH_MEMBER, ERROR_NO_SUCH_MEMBER);
+ cmp(STATUS_INVALID_MEMBER, ERROR_INVALID_MEMBER);
+ cmp(STATUS_TOO_MANY_SIDS, ERROR_TOO_MANY_SIDS);
+ cmp(STATUS_LM_CROSS_ENCRYPTION_REQUIRED, ERROR_LM_CROSS_ENCRYPTION_REQUIRED);
+ cmp(STATUS_MESSAGE_NOT_FOUND, ERROR_MR_MID_NOT_FOUND);
+ cmp(STATUS_LOCAL_DISCONNECT, ERROR_NETNAME_DELETED);
+ cmp(STATUS_REMOTE_DISCONNECT, ERROR_NETNAME_DELETED);
+ cmp(STATUS_REMOTE_RESOURCES, ERROR_REM_NOT_LIST);
+ cmp(STATUS_LINK_FAILED, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_LINK_TIMEOUT, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_INVALID_CONNECTION, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_INVALID_ADDRESS, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_IO_DEVICE_ERROR, ERROR_IO_DEVICE);
+ cmp(STATUS_DEVICE_PROTOCOL_ERROR, ERROR_IO_DEVICE);
+ cmp(STATUS_DRIVER_INTERNAL_ERROR, ERROR_IO_DEVICE);
+ cmp(STATUS_INVALID_DEVICE_STATE, ERROR_BAD_COMMAND);
+ cmp(STATUS_DEVICE_CONFIGURATION_ERROR, ERROR_INVALID_PARAMETER);
+ cmp2(STATUS_SOURCE_ELEMENT_EMPTY, ERROR_SOURCE_ELEMENT_EMPTY);
+ cmp2(STATUS_DESTINATION_ELEMENT_FULL, ERROR_DESTINATION_ELEMENT_FULL);
+ cmp2(STATUS_ILLEGAL_ELEMENT_ADDRESS, ERROR_ILLEGAL_ELEMENT_ADDRESS);
+ cmp2(STATUS_MAGAZINE_NOT_PRESENT, ERROR_MAGAZINE_NOT_PRESENT);
+ cmp2(STATUS_REINITIALIZATION_NEEDED, ERROR_DEVICE_REINITIALIZATION_NEEDED);
+ cmp2(STATUS_DEVICE_REQUIRES_CLEANING, ERROR_DEVICE_REQUIRES_CLEANING);
+ cmp2(STATUS_DEVICE_DOOR_OPEN, ERROR_DEVICE_DOOR_OPEN);
+ cmp2(STATUS_TRANSPORT_FULL, ERROR_TRANSPORT_FULL);
+ cmp2(STATUS_CLEANER_CARTRIDGE_INSTALLED, ERROR_CLEANER_CARTRIDGE_INSTALLED);
+ cmp2(STATUS_REG_NAT_CONSUMPTION, ERROR_REG_NAT_CONSUMPTION);
+ cmp4(STATUS_ENCRYPTION_FAILED, ERROR_ACCESS_DENIED, ERROR_ENCRYPTION_FAILED);
+ cmp4(STATUS_DECRYPTION_FAILED, ERROR_ACCESS_DENIED, ERROR_DECRYPTION_FAILED);
+ cmp4(STATUS_NO_RECOVERY_POLICY, ERROR_ACCESS_DENIED, ERROR_NO_RECOVERY_POLICY);
+ cmp4(STATUS_NO_EFS, ERROR_ACCESS_DENIED, ERROR_NO_EFS);
+ cmp4(STATUS_WRONG_EFS, ERROR_ACCESS_DENIED, ERROR_WRONG_EFS);
+ cmp4(STATUS_NO_USER_KEYS, ERROR_ACCESS_DENIED, ERROR_NO_USER_KEYS);
+ cmp2(STATUS_FILE_NOT_ENCRYPTED, ERROR_FILE_NOT_ENCRYPTED);
+ cmp2(STATUS_NOT_EXPORT_FORMAT, ERROR_NOT_EXPORT_FORMAT);
+ cmp2(STATUS_FILE_ENCRYPTED, ERROR_FILE_ENCRYPTED);
+ cmp2(STATUS_EFS_ALG_BLOB_TOO_BIG, ERROR_EFS_ALG_BLOB_TOO_BIG);
+ cmp(STATUS_INVALID_USER_BUFFER, ERROR_INVALID_USER_BUFFER);
+ cmp(STATUS_SERIAL_NO_DEVICE_INITED, ERROR_SERIAL_NO_DEVICE);
+ cmp(STATUS_SHARED_IRQ_BUSY, ERROR_IRQ_BUSY);
+ cmp(STATUS_SERIAL_MORE_WRITES, ERROR_MORE_WRITES);
+ cmp(STATUS_SERIAL_COUNTER_TIMEOUT, ERROR_COUNTER_TIMEOUT);
+ cmp(STATUS_FLOPPY_ID_MARK_NOT_FOUND, ERROR_FLOPPY_ID_MARK_NOT_FOUND);
+ cmp(STATUS_FLOPPY_WRONG_CYLINDER, ERROR_FLOPPY_WRONG_CYLINDER);
+ cmp(STATUS_FLOPPY_UNKNOWN_ERROR, ERROR_FLOPPY_UNKNOWN_ERROR);
+ cmp(STATUS_FLOPPY_BAD_REGISTERS, ERROR_FLOPPY_BAD_REGISTERS);
+ cmp(STATUS_DISK_RECALIBRATE_FAILED, ERROR_DISK_RECALIBRATE_FAILED);
+ cmp(STATUS_DISK_OPERATION_FAILED, ERROR_DISK_OPERATION_FAILED);
+ cmp(STATUS_DISK_RESET_FAILED, ERROR_DISK_RESET_FAILED);
+ cmp(STATUS_EVENTLOG_FILE_CORRUPT, ERROR_EVENTLOG_FILE_CORRUPT);
+ cmp(STATUS_EVENTLOG_CANT_START, ERROR_EVENTLOG_CANT_START);
+ cmp(STATUS_NETLOGON_NOT_STARTED, ERROR_NETLOGON_NOT_STARTED);
+ cmp(STATUS_ACCOUNT_EXPIRED, ERROR_ACCOUNT_EXPIRED);
+ cmp(STATUS_NETWORK_CREDENTIAL_CONFLICT, ERROR_SESSION_CREDENTIAL_CONFLICT);
+ cmp(STATUS_REMOTE_SESSION_LIMIT, ERROR_REMOTE_SESSION_LIMIT_EXCEEDED);
+ cmp(STATUS_INVALID_BUFFER_SIZE, ERROR_INVALID_USER_BUFFER);
+ cmp(STATUS_INVALID_ADDRESS_COMPONENT, ERROR_INVALID_NETNAME);
+ cmp(STATUS_INVALID_ADDRESS_WILDCARD, ERROR_INVALID_NETNAME);
+ cmp(STATUS_ADDRESS_ALREADY_EXISTS, ERROR_DUP_NAME);
+ cmp(STATUS_ADDRESS_CLOSED, ERROR_NETNAME_DELETED);
+ cmp(STATUS_CONNECTION_DISCONNECTED, ERROR_NETNAME_DELETED);
+ cmp(STATUS_CONNECTION_RESET, ERROR_NETNAME_DELETED);
+ cmp(STATUS_TRANSACTION_ABORTED, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_TRANSACTION_TIMED_OUT, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_TRANSACTION_NO_RELEASE, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_TRANSACTION_NO_MATCH, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_TRANSACTION_RESPONDED, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_TRANSACTION_INVALID_ID, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_TRANSACTION_INVALID_TYPE, ERROR_UNEXP_NET_ERR);
+ cmp(STATUS_NOT_SERVER_SESSION, ERROR_NOT_SUPPORTED);
+ cmp(STATUS_NOT_CLIENT_SESSION, ERROR_NOT_SUPPORTED);
+ cmp(STATUS_USER_MAPPED_FILE, ERROR_USER_MAPPED_FILE);
+ cmp(STATUS_PLUGPLAY_NO_DEVICE, ERROR_SERVICE_DISABLED);
+ cmp2(STATUS_WMI_GUID_NOT_FOUND, ERROR_WMI_GUID_NOT_FOUND);
+ cmp2(STATUS_WMI_INSTANCE_NOT_FOUND, ERROR_WMI_INSTANCE_NOT_FOUND);
+ cmp2(STATUS_WMI_ITEMID_NOT_FOUND, ERROR_WMI_ITEMID_NOT_FOUND);
+ cmp2(STATUS_WMI_TRY_AGAIN, ERROR_WMI_TRY_AGAIN);
+ cmp2(STATUS_WMI_READ_ONLY, ERROR_WMI_READ_ONLY);
+ cmp2(STATUS_WMI_SET_FAILURE, ERROR_WMI_SET_FAILURE);
+ cmp2(STATUS_WMI_NOT_SUPPORTED, ERROR_NOT_SUPPORTED);
+ cmp2(STATUS_WMI_GUID_DISCONNECTED, ERROR_WMI_GUID_DISCONNECTED);
+ cmp2(STATUS_WMI_ALREADY_DISABLED, ERROR_WMI_ALREADY_DISABLED);
+ cmp2(STATUS_WMI_ALREADY_ENABLED, ERROR_WMI_ALREADY_ENABLED);
+ cmp2(STATUS_COPY_PROTECTION_FAILURE, STG_E_STATUS_COPY_PROTECTION_FAILURE);
+ cmp2(STATUS_CSS_AUTHENTICATION_FAILURE, STG_E_CSS_AUTHENTICATION_FAILURE);
+ cmp2(STATUS_CSS_KEY_NOT_PRESENT, STG_E_CSS_KEY_NOT_PRESENT);
+ cmp2(STATUS_CSS_KEY_NOT_ESTABLISHED, STG_E_CSS_KEY_NOT_ESTABLISHED);
+ cmp2(STATUS_CSS_SCRAMBLED_SECTOR, STG_E_CSS_SCRAMBLED_SECTOR);
+ cmp2(STATUS_CSS_REGION_MISMATCH, STG_E_CSS_REGION_MISMATCH);
+ cmp2(STATUS_CSS_RESETS_EXHAUSTED, STG_E_RESETS_EXHAUSTED);
+ cmp(RPC_NT_SERVER_UNAVAILABLE, RPC_S_SERVER_UNAVAILABLE);
+ cmp(RPC_NT_INVALID_STRING_BINDING, RPC_S_INVALID_STRING_BINDING);
+ cmp(RPC_NT_WRONG_KIND_OF_BINDING, RPC_S_WRONG_KIND_OF_BINDING);
+ cmp(RPC_NT_PROTSEQ_NOT_SUPPORTED, RPC_S_PROTSEQ_NOT_SUPPORTED);
+ cmp(RPC_NT_INVALID_RPC_PROTSEQ, RPC_S_INVALID_RPC_PROTSEQ);
+ cmp(RPC_NT_INVALID_STRING_UUID, RPC_S_INVALID_STRING_UUID);
+ cmp(RPC_NT_INVALID_ENDPOINT_FORMAT, RPC_S_INVALID_ENDPOINT_FORMAT);
+ cmp(RPC_NT_INVALID_NET_ADDR, RPC_S_INVALID_NET_ADDR);
+ cmp(RPC_NT_NO_ENDPOINT_FOUND, RPC_S_NO_ENDPOINT_FOUND);
+ cmp(RPC_NT_INVALID_TIMEOUT, RPC_S_INVALID_TIMEOUT);
+ cmp(RPC_NT_OBJECT_NOT_FOUND, RPC_S_OBJECT_NOT_FOUND);
+ cmp(RPC_NT_ALREADY_REGISTERED, RPC_S_ALREADY_REGISTERED);
+ cmp(RPC_NT_TYPE_ALREADY_REGISTERED, RPC_S_TYPE_ALREADY_REGISTERED);
+ cmp(RPC_NT_ALREADY_LISTENING, RPC_S_ALREADY_LISTENING);
+ cmp(RPC_NT_NO_PROTSEQS_REGISTERED, RPC_S_NO_PROTSEQS_REGISTERED);
+ cmp(RPC_NT_NOT_LISTENING, RPC_S_NOT_LISTENING);
+ cmp(RPC_NT_UNKNOWN_MGR_TYPE, RPC_S_UNKNOWN_MGR_TYPE);
+ cmp(RPC_NT_UNKNOWN_IF, RPC_S_UNKNOWN_IF);
+ cmp(RPC_NT_NO_BINDINGS, RPC_S_NO_BINDINGS);
+ cmp(RPC_NT_NO_MORE_BINDINGS, RPC_S_NO_MORE_BINDINGS);
+ cmp(RPC_NT_NO_PROTSEQS, RPC_S_NO_PROTSEQS);
+ cmp(RPC_NT_CANT_CREATE_ENDPOINT, RPC_S_CANT_CREATE_ENDPOINT);
+ cmp(RPC_NT_OUT_OF_RESOURCES, RPC_S_OUT_OF_RESOURCES);
+ cmp(RPC_NT_SERVER_TOO_BUSY, RPC_S_SERVER_TOO_BUSY);
+ cmp(RPC_NT_INVALID_NETWORK_OPTIONS, RPC_S_INVALID_NETWORK_OPTIONS);
+ cmp(RPC_NT_NO_CALL_ACTIVE, RPC_S_NO_CALL_ACTIVE);
+ cmp(RPC_NT_CALL_FAILED, RPC_S_CALL_FAILED);
+ cmp(RPC_NT_CALL_FAILED_DNE, RPC_S_CALL_FAILED_DNE);
+ cmp(RPC_NT_PROTOCOL_ERROR, RPC_S_PROTOCOL_ERROR);
+ cmp(RPC_NT_UNSUPPORTED_TRANS_SYN, RPC_S_UNSUPPORTED_TRANS_SYN);
+ cmp(RPC_NT_UNSUPPORTED_TYPE, RPC_S_UNSUPPORTED_TYPE);
+ cmp(RPC_NT_INVALID_TAG, RPC_S_INVALID_TAG);
+ cmp(RPC_NT_INVALID_BOUND, RPC_S_INVALID_BOUND);
+ cmp(RPC_NT_NO_ENTRY_NAME, RPC_S_NO_ENTRY_NAME);
+ cmp(RPC_NT_INVALID_NAME_SYNTAX, RPC_S_INVALID_NAME_SYNTAX);
+ cmp(RPC_NT_UNSUPPORTED_NAME_SYNTAX, RPC_S_UNSUPPORTED_NAME_SYNTAX);
+ cmp(RPC_NT_UUID_NO_ADDRESS, RPC_S_UUID_NO_ADDRESS);
+ cmp(RPC_NT_DUPLICATE_ENDPOINT, RPC_S_DUPLICATE_ENDPOINT);
+ cmp(RPC_NT_UNKNOWN_AUTHN_TYPE, RPC_S_UNKNOWN_AUTHN_TYPE);
+ cmp(RPC_NT_MAX_CALLS_TOO_SMALL, RPC_S_MAX_CALLS_TOO_SMALL);
+ cmp(RPC_NT_STRING_TOO_LONG, RPC_S_STRING_TOO_LONG);
+ cmp(RPC_NT_PROTSEQ_NOT_FOUND, RPC_S_PROTSEQ_NOT_FOUND);
+ cmp(RPC_NT_PROCNUM_OUT_OF_RANGE, RPC_S_PROCNUM_OUT_OF_RANGE);
+ cmp(RPC_NT_BINDING_HAS_NO_AUTH, RPC_S_BINDING_HAS_NO_AUTH);
+ cmp(RPC_NT_UNKNOWN_AUTHN_SERVICE, RPC_S_UNKNOWN_AUTHN_SERVICE);
+ cmp(RPC_NT_UNKNOWN_AUTHN_LEVEL, RPC_S_UNKNOWN_AUTHN_LEVEL);
+ cmp(RPC_NT_INVALID_AUTH_IDENTITY, RPC_S_INVALID_AUTH_IDENTITY);
+ cmp(RPC_NT_UNKNOWN_AUTHZ_SERVICE, RPC_S_UNKNOWN_AUTHZ_SERVICE);
+ cmp(EPT_NT_INVALID_ENTRY, EPT_S_INVALID_ENTRY);
+ cmp(EPT_NT_CANT_PERFORM_OP, EPT_S_CANT_PERFORM_OP);
+ cmp(EPT_NT_NOT_REGISTERED, EPT_S_NOT_REGISTERED);
+ cmp(RPC_NT_NOTHING_TO_EXPORT, RPC_S_NOTHING_TO_EXPORT);
+ cmp(RPC_NT_INCOMPLETE_NAME, RPC_S_INCOMPLETE_NAME);
+ cmp(RPC_NT_INVALID_VERS_OPTION, RPC_S_INVALID_VERS_OPTION);
+ cmp(RPC_NT_NO_MORE_MEMBERS, RPC_S_NO_MORE_MEMBERS);
+ cmp(RPC_NT_NOT_ALL_OBJS_UNEXPORTED, RPC_S_NOT_ALL_OBJS_UNEXPORTED);
+ cmp(RPC_NT_INTERFACE_NOT_FOUND, RPC_S_INTERFACE_NOT_FOUND);
+ cmp(RPC_NT_ENTRY_ALREADY_EXISTS, RPC_S_ENTRY_ALREADY_EXISTS);
+ cmp(RPC_NT_ENTRY_NOT_FOUND, RPC_S_ENTRY_NOT_FOUND);
+ cmp(RPC_NT_NAME_SERVICE_UNAVAILABLE, RPC_S_NAME_SERVICE_UNAVAILABLE);
+ cmp(RPC_NT_INVALID_NAF_ID, RPC_S_INVALID_NAF_ID);
+ cmp(RPC_NT_CANNOT_SUPPORT, RPC_S_CANNOT_SUPPORT);
+ cmp(RPC_NT_NO_CONTEXT_AVAILABLE, RPC_S_NO_CONTEXT_AVAILABLE);
+ cmp(RPC_NT_INTERNAL_ERROR, RPC_S_INTERNAL_ERROR);
+ cmp(RPC_NT_ZERO_DIVIDE, RPC_S_ZERO_DIVIDE);
+ cmp(RPC_NT_ADDRESS_ERROR, RPC_S_ADDRESS_ERROR);
+ cmp(RPC_NT_FP_DIV_ZERO, RPC_S_FP_DIV_ZERO);
+ cmp(RPC_NT_FP_UNDERFLOW, RPC_S_FP_UNDERFLOW);
+ cmp(RPC_NT_FP_OVERFLOW, RPC_S_FP_OVERFLOW);
+ cmp(RPC_NT_NO_MORE_ENTRIES, RPC_X_NO_MORE_ENTRIES);
+ cmp(RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, RPC_X_SS_CHAR_TRANS_OPEN_FAIL);
+ cmp(RPC_NT_SS_CHAR_TRANS_SHORT_FILE, RPC_X_SS_CHAR_TRANS_SHORT_FILE);
+ cmp(RPC_NT_SS_CONTEXT_MISMATCH, ERROR_INVALID_HANDLE);
+ cmp(RPC_NT_SS_CONTEXT_DAMAGED, RPC_X_SS_CONTEXT_DAMAGED);
+ cmp(RPC_NT_SS_HANDLES_MISMATCH, RPC_X_SS_HANDLES_MISMATCH);
+ cmp(RPC_NT_SS_CANNOT_GET_CALL_HANDLE, RPC_X_SS_CANNOT_GET_CALL_HANDLE);
+ cmp(RPC_NT_NULL_REF_POINTER, RPC_X_NULL_REF_POINTER);
+ cmp(RPC_NT_ENUM_VALUE_OUT_OF_RANGE, RPC_X_ENUM_VALUE_OUT_OF_RANGE);
+ cmp(RPC_NT_BYTE_COUNT_TOO_SMALL, RPC_X_BYTE_COUNT_TOO_SMALL);
+ cmp(RPC_NT_BAD_STUB_DATA, RPC_X_BAD_STUB_DATA);
+ cmp(RPC_NT_INVALID_OBJECT, RPC_S_INVALID_OBJECT);
+ cmp(STATUS_NO_TRUST_LSA_SECRET, ERROR_NO_TRUST_LSA_SECRET);
+ cmp(STATUS_NO_TRUST_SAM_ACCOUNT, ERROR_NO_TRUST_SAM_ACCOUNT);
+ cmp(STATUS_TRUSTED_DOMAIN_FAILURE, ERROR_TRUSTED_DOMAIN_FAILURE);
+ cmp(STATUS_TRUSTED_RELATIONSHIP_FAILURE, ERROR_TRUSTED_RELATIONSHIP_FAILURE);
+ cmp(STATUS_TRUST_FAILURE, ERROR_TRUST_FAILURE);
+ cmp(RPC_NT_CALL_IN_PROGRESS, RPC_S_CALL_IN_PROGRESS);
+ cmp(STATUS_LOG_FILE_FULL, ERROR_LOG_FILE_FULL);
+ cmp(STATUS_EVENTLOG_FILE_CHANGED, ERROR_EVENTLOG_FILE_CHANGED);
+ cmp(STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT);
+ cmp(STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT);
+ cmp(STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, ERROR_NOLOGON_SERVER_TRUST_ACCOUNT);
+ cmp(STATUS_DOMAIN_TRUST_INCONSISTENT, ERROR_DOMAIN_TRUST_INCONSISTENT);
+ cmp(STATUS_NO_USER_SESSION_KEY, ERROR_NO_USER_SESSION_KEY);
+ cmp(STATUS_POSSIBLE_DEADLOCK, ERROR_POSSIBLE_DEADLOCK);
+ cmp(STATUS_IMAGE_ALREADY_LOADED, ERROR_SERVICE_ALREADY_RUNNING);
+ cmp(RPC_NT_GROUP_MEMBER_NOT_FOUND, RPC_S_GROUP_MEMBER_NOT_FOUND);
+ cmp(RPC_NT_NO_INTERFACES, RPC_S_NO_INTERFACES);
+ cmp(RPC_NT_CALL_CANCELLED, RPC_S_CALL_CANCELLED);
+ cmp(RPC_NT_BINDING_INCOMPLETE, RPC_S_BINDING_INCOMPLETE);
+ cmp(RPC_NT_COMM_FAILURE, RPC_S_COMM_FAILURE);
+ cmp(RPC_NT_UNSUPPORTED_AUTHN_LEVEL, RPC_S_UNSUPPORTED_AUTHN_LEVEL);
+ cmp(RPC_NT_NO_PRINC_NAME, RPC_S_NO_PRINC_NAME);
+ cmp(RPC_NT_NOT_RPC_ERROR, RPC_S_NOT_RPC_ERROR);
+ cmp(RPC_NT_UUID_LOCAL_ONLY, RPC_S_UUID_LOCAL_ONLY);
+ cmp(RPC_NT_SEC_PKG_ERROR, RPC_S_SEC_PKG_ERROR);
+ cmp(RPC_NT_NOT_CANCELLED, RPC_S_NOT_CANCELLED);
+ cmp(RPC_NT_INVALID_ES_ACTION, RPC_X_INVALID_ES_ACTION);
+ cmp(RPC_NT_WRONG_ES_VERSION, RPC_X_WRONG_ES_VERSION);
+ cmp(RPC_NT_WRONG_STUB_VERSION, RPC_X_WRONG_STUB_VERSION);
+ cmp(RPC_NT_INVALID_PIPE_OBJECT, RPC_X_INVALID_PIPE_OBJECT);
+ cmp(RPC_NT_INVALID_PIPE_OPERATION, RPC_X_INVALID_PIPE_OPERATION);
+ cmp(RPC_NT_WRONG_PIPE_VERSION, RPC_X_WRONG_PIPE_VERSION);
+ cmp(EPT_NT_CANT_CREATE, EPT_S_CANT_CREATE);
+ cmp(RPC_NT_SEND_INCOMPLETE, RPC_S_SEND_INCOMPLETE);
+ cmp2(RPC_NT_INVALID_ASYNC_HANDLE, RPC_S_INVALID_ASYNC_HANDLE);
+ cmp2(RPC_NT_INVALID_ASYNC_CALL, RPC_S_INVALID_ASYNC_CALL);
+ cmp2(RPC_NT_PIPE_CLOSED, RPC_X_PIPE_CLOSED);
+ cmp2(RPC_NT_PIPE_EMPTY, RPC_X_PIPE_EMPTY);
+ cmp2(RPC_NT_PIPE_DISCIPLINE_ERROR, RPC_X_PIPE_DISCIPLINE_ERROR);
+ cmp(STATUS_NO_BROWSER_SERVERS_FOUND, ERROR_NO_BROWSER_SERVERS_FOUND);
+ cmp(STATUS_MAPPED_ALIGNMENT, ERROR_MAPPED_ALIGNMENT);
+ cmp(STATUS_CONNECTION_IN_USE, ERROR_DEVICE_IN_USE);
+ cmp(STATUS_VERIFY_REQUIRED, ERROR_MEDIA_CHANGED);
+ cmp(STATUS_ALREADY_DISCONNECTED, ERROR_ACTIVE_CONNECTIONS);
+ cmp(STATUS_CONNECTION_REFUSED, ERROR_CONNECTION_REFUSED);
+ cmp(STATUS_GRACEFUL_DISCONNECT, ERROR_GRACEFUL_DISCONNECT);
+ cmp(STATUS_ADDRESS_ALREADY_ASSOCIATED, ERROR_ADDRESS_ALREADY_ASSOCIATED);
+ cmp(STATUS_ADDRESS_NOT_ASSOCIATED, ERROR_ADDRESS_NOT_ASSOCIATED);
+ cmp(STATUS_CONNECTION_INVALID, ERROR_CONNECTION_INVALID);
+ cmp(STATUS_CONNECTION_ACTIVE, ERROR_CONNECTION_ACTIVE);
+ cmp(STATUS_NETWORK_UNREACHABLE, ERROR_NETWORK_UNREACHABLE);
+ cmp(STATUS_HOST_UNREACHABLE, ERROR_HOST_UNREACHABLE);
+ cmp2(STATUS_HOST_DOWN, ERROR_HOST_DOWN);
+ cmp(STATUS_PROTOCOL_UNREACHABLE, ERROR_PROTOCOL_UNREACHABLE);
+ cmp(STATUS_PORT_UNREACHABLE, ERROR_PORT_UNREACHABLE);
+ cmp(STATUS_REQUEST_ABORTED, ERROR_REQUEST_ABORTED);
+ cmp(STATUS_CONNECTION_ABORTED, ERROR_CONNECTION_ABORTED);
+ cmp(STATUS_CONNECTION_COUNT_LIMIT, ERROR_CONNECTION_COUNT_LIMIT);
+ cmp(STATUS_PATH_NOT_COVERED, ERROR_HOST_UNREACHABLE);
+ cmp(STATUS_LOGIN_TIME_RESTRICTION, ERROR_LOGIN_TIME_RESTRICTION);
+ cmp(STATUS_LOGIN_WKSTA_RESTRICTION, ERROR_LOGIN_WKSTA_RESTRICTION);
+ cmp(STATUS_LICENSE_QUOTA_EXCEEDED, ERROR_LICENSE_QUOTA_EXCEEDED);
+ cmp(STATUS_RESOURCE_NOT_OWNED, ERROR_NOT_OWNER);
+ cmp(STATUS_DUPLICATE_OBJECTID, STATUS_DUPLICATE_OBJECTID);
+ cmp(STATUS_OBJECTID_EXISTS, STATUS_OBJECTID_EXISTS);
+ cmp2(STATUS_OBJECTID_NOT_FOUND, ERROR_FILE_NOT_FOUND);
+ cmp2(STATUS_MFT_TOO_FRAGMENTED, ERROR_DISK_TOO_FRAGMENTED);
+ cmp(SEC_E_INSUFFICIENT_MEMORY, ERROR_NO_SYSTEM_RESOURCES);
+ cmp(SEC_E_INVALID_HANDLE, ERROR_INVALID_HANDLE);
+ cmp(SEC_E_UNSUPPORTED_FUNCTION, ERROR_INVALID_FUNCTION);
+ cmp(SEC_E_TARGET_UNKNOWN, ERROR_BAD_NETPATH);
+ cmp(SEC_E_INTERNAL_ERROR, ERROR_INTERNAL_ERROR);
+ cmp(SEC_E_SECPKG_NOT_FOUND, ERROR_NO_SUCH_PACKAGE);
+ cmp(SEC_E_NOT_OWNER, ERROR_NOT_OWNER);
+ cmp(SEC_E_CANNOT_INSTALL, ERROR_NO_SUCH_PACKAGE);
+ cmp(SEC_E_INVALID_TOKEN, ERROR_INVALID_PARAMETER);
+ cmp(SEC_E_CANNOT_PACK, ERROR_INVALID_PARAMETER);
+ cmp(SEC_E_QOP_NOT_SUPPORTED, ERROR_NOT_SUPPORTED);
+ cmp(SEC_E_NO_IMPERSONATION, ERROR_CANNOT_IMPERSONATE);
+ cmp2(SEC_E_MULTIPLE_ACCOUNTS, ERROR_CANNOT_IMPERSONATE);
+ cmp(SEC_E_LOGON_DENIED, ERROR_LOGON_FAILURE);
+ cmp(SEC_E_UNKNOWN_CREDENTIALS, ERROR_INVALID_PARAMETER);
+ cmp2(SEC_E_INCOMPLETE_CREDENTIALS, ERROR_INVALID_PARAMETER);
+ cmp(SEC_E_NO_CREDENTIALS, ERROR_NO_SUCH_LOGON_SESSION);
+ cmp(SEC_E_MESSAGE_ALTERED, ERROR_ACCESS_DENIED);
+ cmp(SEC_E_OUT_OF_SEQUENCE, ERROR_ACCESS_DENIED);
+ cmp(SEC_E_NO_AUTHENTICATING_AUTHORITY, ERROR_NO_LOGON_SERVERS);
+ cmp(SEC_E_BAD_PKGID, ERROR_NO_SUCH_PACKAGE);
+ cmp4(SEC_E_WRONG_PRINCIPAL, ERROR_WRONG_TARGET_NAME, 1462);
+ cmp2(SEC_E_INCOMPLETE_MESSAGE, ERROR_INVALID_USER_BUFFER);
+ cmp2(SEC_E_BUFFER_TOO_SMALL, ERROR_INSUFFICIENT_BUFFER);
+ cmp2(SEC_E_UNTRUSTED_ROOT, ERROR_TRUST_FAILURE);
+ cmp2(SEC_E_ILLEGAL_MESSAGE, ERROR_INVALID_PARAMETER);
+ cmp2(SEC_E_CERT_UNKNOWN, ERROR_INVALID_PARAMETER);
+ cmp2(SEC_E_CERT_EXPIRED, ERROR_PASSWORD_EXPIRED);
+ cmp2(SEC_E_ENCRYPT_FAILURE, ERROR_ENCRYPTION_FAILED);
+ cmp2(SEC_E_DECRYPT_FAILURE, ERROR_DECRYPTION_FAILED);
+ cmp2(SEC_E_ALGORITHM_MISMATCH, ERROR_INVALID_FUNCTION);
+ cmp2(SEC_E_CONTEXT_EXPIRED, ERROR_CONTEXT_EXPIRED);
+ cmp2(STATUS_BAD_BINDINGS, SEC_E_BAD_BINDINGS);
+ cmp2(TRUST_E_CERT_SIGNATURE, ERROR_MUTUAL_AUTH_FAILED);
+ cmp2(CRYPT_E_REVOKED, ERROR_MUTUAL_AUTH_FAILED);
+ cmp2(CRYPT_E_NO_REVOCATION_CHECK, ERROR_MUTUAL_AUTH_FAILED);
+ cmp2(CRYPT_E_REVOCATION_OFFLINE, ERROR_MUTUAL_AUTH_FAILED);
+ cmp2(STATUS_SHUTDOWN_IN_PROGRESS, ERROR_SHUTDOWN_IN_PROGRESS);
+ cmp2(STATUS_SERVER_SHUTDOWN_IN_PROGRESS, ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
+ cmp4(STATUS_DS_MEMBERSHIP_EVALUATED_LOCALLY, ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY, 1922);
+ cmp4(STATUS_DS_NO_ATTRIBUTE_OR_VALUE, ERROR_DS_NO_ATTRIBUTE_OR_VALUE, 1923);
+ cmp4(STATUS_DS_INVALID_ATTRIBUTE_SYNTAX, ERROR_DS_INVALID_ATTRIBUTE_SYNTAX, 1924);
+ cmp4(STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED, ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED, 1925);
+ cmp4(STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS, ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS, 1926);
+ cmp4(STATUS_DS_BUSY, ERROR_DS_BUSY, 1927);
+ cmp4(STATUS_DS_UNAVAILABLE, ERROR_DS_UNAVAILABLE, 1928);
+ cmp4(STATUS_DS_NO_RIDS_ALLOCATED, ERROR_DS_NO_RIDS_ALLOCATED, 1929);
+ cmp4(STATUS_DS_NO_MORE_RIDS, ERROR_DS_NO_MORE_RIDS, 1930);
+ cmp4(STATUS_DS_INCORRECT_ROLE_OWNER, ERROR_DS_INCORRECT_ROLE_OWNER, 1931);
+ cmp4(STATUS_DS_RIDMGR_INIT_ERROR, ERROR_DS_RIDMGR_INIT_ERROR, 1932);
+ cmp4(STATUS_DS_OBJ_CLASS_VIOLATION, ERROR_DS_OBJ_CLASS_VIOLATION, 1933);
+ cmp4(STATUS_DS_CANT_ON_NON_LEAF, ERROR_DS_CANT_ON_NON_LEAF, 1934);
+ cmp4(STATUS_DS_CANT_ON_RDN, ERROR_DS_CANT_ON_RDN, 1935);
+ cmp4(STATUS_DS_CROSS_DOM_MOVE_FAILED, ERROR_DS_CROSS_DOM_MOVE_ERROR, 1937);
+ cmp4(STATUS_DS_GC_NOT_AVAILABLE, ERROR_DS_GC_NOT_AVAILABLE, 1938);
+ cmp2(STATUS_DS_CANT_MOD_OBJ_CLASS, ERROR_DS_CANT_MOD_OBJ_CLASS);
+ cmp2(STATUS_DS_ADMIN_LIMIT_EXCEEDED, ERROR_DS_ADMIN_LIMIT_EXCEEDED);
+ cmp2(STATUS_DIRECTORY_SERVICE_REQUIRED, ERROR_DS_DS_REQUIRED);
+ cmp2(STATUS_DS_SAM_INIT_FAILURE, ERROR_DS_SAM_INIT_FAILURE);
+ cmp2(STATUS_DS_CANT_START, ERROR_DS_CANT_START);
+ cmp2(STATUS_DS_INIT_FAILURE, ERROR_DS_INIT_FAILURE);
+ cmp2(STATUS_SAM_INIT_FAILURE, ERROR_SAM_INIT_FAILURE);
+ cmp2(STATUS_DS_SENSITIVE_GROUP_VIOLATION, ERROR_DS_SENSITIVE_GROUP_VIOLATION);
+ cmp2(STATUS_DS_CANT_MOD_PRIMARYGROUPID, ERROR_DS_CANT_MOD_PRIMARYGROUPID);
+ cmp2(STATUS_DS_INVALID_GROUP_TYPE, ERROR_DS_INVALID_GROUP_TYPE);
+ cmp2(STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN, ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN);
+ cmp2(STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN, ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN);
+ cmp2(STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER, ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER);
+ cmp2(STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER, ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER);
+ cmp2(STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER, ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER);
+ cmp2(STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER, ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER);
+ cmp2(STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER, ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER);
+ cmp2(STATUS_DS_HAVE_PRIMARY_MEMBERS, ERROR_DS_HAVE_PRIMARY_MEMBERS);
+ cmp2(STATUS_DS_GC_REQUIRED, ERROR_DS_GC_REQUIRED);
+ cmp2(STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY, ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY);
+ cmp2(STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS, ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS);
+ cmp2(STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED,ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED);
+ cmp2(STATUS_SAM_NEED_BOOTKEY_PASSWORD, ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD);
+ cmp2(STATUS_SAM_NEED_BOOTKEY_FLOPPY, ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY);
+ cmp2(STATUS_DS_INIT_FAILURE_CONSOLE, ERROR_DS_INIT_FAILURE_CONSOLE);
+ cmp2(STATUS_DS_SAM_INIT_FAILURE_CONSOLE, ERROR_DS_SAM_INIT_FAILURE_CONSOLE);
+ cmp2(STATUS_UNFINISHED_CONTEXT_DELETED, SEC_E_UNFINISHED_CONTEXT_DELETED);
+ cmp2(STATUS_NO_TGT_REPLY, SEC_E_NO_TGT_REPLY);
+ cmp2(STATUS_NO_IP_ADDRESSES, SEC_E_NO_IP_ADDRESSES);
+ cmp2(STATUS_WRONG_CREDENTIAL_HANDLE, SEC_E_WRONG_CREDENTIAL_HANDLE);
+ cmp2(STATUS_CRYPTO_SYSTEM_INVALID, SEC_E_CRYPTO_SYSTEM_INVALID);
+ cmp2(STATUS_MAX_REFERRALS_EXCEEDED, SEC_E_MAX_REFERRALS_EXCEEDED);
+ cmp2(STATUS_MUST_BE_KDC, SEC_E_MUST_BE_KDC);
+ cmp2(STATUS_STRONG_CRYPTO_NOT_SUPPORTED, SEC_E_STRONG_CRYPTO_NOT_SUPPORTED);
+ cmp2(STATUS_TOO_MANY_PRINCIPALS, SEC_E_TOO_MANY_PRINCIPALS);
+ cmp2(STATUS_NO_PA_DATA, SEC_E_NO_PA_DATA);
+ cmp2(STATUS_PKINIT_NAME_MISMATCH, SEC_E_PKINIT_NAME_MISMATCH);
+ cmp2(STATUS_SMARTCARD_LOGON_REQUIRED, SEC_E_SMARTCARD_LOGON_REQUIRED);
+ cmp2(STATUS_KDC_INVALID_REQUEST, SEC_E_KDC_INVALID_REQUEST);
+ cmp2(STATUS_KDC_UNABLE_TO_REFER, SEC_E_KDC_UNABLE_TO_REFER);
+ cmp2(STATUS_KDC_UNKNOWN_ETYPE, SEC_E_KDC_UNKNOWN_ETYPE);
+ cmp2(STATUS_UNSUPPORTED_PREAUTH, SEC_E_UNSUPPORTED_PREAUTH);
+ cmp4(STATUS_SHARED_POLICY, ERROR_SHARED_POLICY, 1939);
+ cmp4(STATUS_POLICY_OBJECT_NOT_FOUND, ERROR_POLICY_OBJECT_NOT_FOUND, 1940);
+ cmp4(STATUS_POLICY_ONLY_IN_DS, ERROR_POLICY_ONLY_IN_DS, 1941);
+ cmp4(STATUS_DEVICE_REMOVED, ERROR_DEVICE_REMOVED, 617);
+ cmp2(STATUS_RETRY, ERROR_RETRY);
+ cmp2(STATUS_NOT_SUPPORTED_ON_SBS, ERROR_NOT_SUPPORTED_ON_SBS);
+ cmp2(STATUS_DRIVER_BLOCKED_CRITICAL, ERROR_DRIVER_BLOCKED);
+ cmp2(STATUS_DRIVER_BLOCKED, ERROR_DRIVER_BLOCKED);
+ cmp2(STATUS_PRENT4_MACHINE_ACCOUNT, ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4);
+ cmp2(STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER,ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER);
+ cmp2(STATUS_DS_SHUTTING_DOWN, ERROR_DS_SHUTTING_DOWN);
+ cmp2(STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT, ERROR_ACCESS_DISABLED_BY_POLICY);
+ cmp2(STATUS_ACCESS_DISABLED_BY_POLICY_PATH, ERROR_ACCESS_DISABLED_BY_POLICY);
+ cmp2(STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER, ERROR_ACCESS_DISABLED_BY_POLICY);
+ cmp2(STATUS_ACCESS_DISABLED_BY_POLICY_OTHER, ERROR_ACCESS_DISABLED_BY_POLICY);
+ cmp2(STATUS_FAIL_CHECK, ERROR_INVALID_PARAMETER);
+ cmp2(STATUS_CTX_CLOSE_PENDING, ERROR_CTX_CLOSE_PENDING);
+ cmp2(STATUS_CTX_NO_OUTBUF, ERROR_CTX_NO_OUTBUF);
+ cmp2(STATUS_CTX_MODEM_INF_NOT_FOUND, ERROR_CTX_MODEM_INF_NOT_FOUND);
+ cmp2(STATUS_CTX_INVALID_MODEMNAME, ERROR_CTX_INVALID_MODEMNAME);
+ cmp2(STATUS_CTX_RESPONSE_ERROR, ERROR_CTX_MODEM_RESPONSE_ERROR);
+ cmp2(STATUS_CTX_MODEM_RESPONSE_TIMEOUT, ERROR_CTX_MODEM_RESPONSE_TIMEOUT);
+ cmp2(STATUS_CTX_MODEM_RESPONSE_NO_CARRIER, ERROR_CTX_MODEM_RESPONSE_NO_CARRIER);
+ cmp2(STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE, ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE);
+ cmp2(STATUS_CTX_MODEM_RESPONSE_BUSY, ERROR_CTX_MODEM_RESPONSE_BUSY);
+ cmp2(STATUS_CTX_MODEM_RESPONSE_VOICE, ERROR_CTX_MODEM_RESPONSE_VOICE);
+ cmp2(STATUS_CTX_TD_ERROR, ERROR_CTX_TD_ERROR);
+ cmp2(STATUS_CTX_WINSTATION_NAME_INVALID, ERROR_CTX_WINSTATION_NAME_INVALID);
+ cmp2(STATUS_CTX_WINSTATION_NOT_FOUND, ERROR_CTX_WINSTATION_NOT_FOUND);
+ cmp2(STATUS_CTX_WINSTATION_NAME_COLLISION, ERROR_CTX_WINSTATION_ALREADY_EXISTS);
+ cmp2(STATUS_CTX_WINSTATION_BUSY, ERROR_CTX_WINSTATION_BUSY);
+ cmp2(STATUS_CTX_GRAPHICS_INVALID, ERROR_CTX_GRAPHICS_INVALID);
+ cmp2(STATUS_CTX_BAD_VIDEO_MODE, ERROR_CTX_BAD_VIDEO_MODE);
+ cmp2(STATUS_CTX_NOT_CONSOLE, ERROR_CTX_NOT_CONSOLE);
+ cmp2(STATUS_CTX_CLIENT_QUERY_TIMEOUT, ERROR_CTX_CLIENT_QUERY_TIMEOUT);
+ cmp2(STATUS_CTX_CONSOLE_DISCONNECT, ERROR_CTX_CONSOLE_DISCONNECT);
+ cmp2(STATUS_CTX_CONSOLE_CONNECT, ERROR_CTX_CONSOLE_CONNECT);
+ cmp2(STATUS_CTX_SHADOW_DENIED, ERROR_CTX_SHADOW_DENIED);
+ cmp2(STATUS_CTX_SHADOW_INVALID, ERROR_CTX_SHADOW_INVALID);
+ cmp2(STATUS_CTX_SHADOW_DISABLED, ERROR_CTX_SHADOW_DISABLED);
+ cmp2(STATUS_CTX_WINSTATION_ACCESS_DENIED, ERROR_CTX_WINSTATION_ACCESS_DENIED);
+ cmp2(STATUS_CTX_INVALID_PD, ERROR_CTX_INVALID_PD);
+ cmp2(STATUS_CTX_PD_NOT_FOUND, ERROR_CTX_PD_NOT_FOUND);
+ cmp2(STATUS_CTX_INVALID_WD, ERROR_CTX_INVALID_WD);
+ cmp2(STATUS_CTX_WD_NOT_FOUND, ERROR_CTX_WD_NOT_FOUND);
+ cmp2(STATUS_CTX_CLIENT_LICENSE_IN_USE, ERROR_CTX_CLIENT_LICENSE_IN_USE);
+ cmp2(STATUS_CTX_CLIENT_LICENSE_NOT_SET, ERROR_CTX_CLIENT_LICENSE_NOT_SET);
+ cmp2(STATUS_CTX_LICENSE_NOT_AVAILABLE, ERROR_CTX_LICENSE_NOT_AVAILABLE);
+ cmp2(STATUS_CTX_LICENSE_CLIENT_INVALID, ERROR_CTX_LICENSE_CLIENT_INVALID);
+ cmp2(STATUS_CTX_LICENSE_EXPIRED, ERROR_CTX_LICENSE_EXPIRED);
+ cmp2(STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE, ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE);
+ cmp2(STATUS_CTX_SHADOW_NOT_RUNNING, ERROR_CTX_SHADOW_NOT_RUNNING);
+ cmp2(STATUS_LICENSE_VIOLATION, ERROR_CTX_LICENSE_NOT_AVAILABLE);
+#if 0
+ /* FIXME - unknown STATUS values, see bug 1001 */
+ cmp(STATUS_ENDPOINT_CLOSED, ERROR_DEV_NOT_EXIST);
+ cmp(STATUS_DISCONNECTED, ERROR_DEV_NOT_EXIST);
+ cmp(STATUS_NONEXISTENT_NET_NAME, ERROR_DEV_NOT_EXIST);
+#endif
+ cmp2(STATUS_NETWORK_SESSION_EXPIRED, ERROR_NO_USER_SESSION_KEY);
+ cmp2(STATUS_FILES_OPEN, ERROR_OPEN_FILES);
+ cmp2(STATUS_SXS_SECTION_NOT_FOUND, ERROR_SXS_SECTION_NOT_FOUND);
+ cmp2(STATUS_SXS_CANT_GEN_ACTCTX, ERROR_SXS_CANT_GEN_ACTCTX);
+ cmp2(STATUS_SXS_INVALID_ACTCTXDATA_FORMAT, ERROR_SXS_INVALID_ACTCTXDATA_FORMAT);
+ cmp2(STATUS_SXS_ASSEMBLY_NOT_FOUND, ERROR_SXS_ASSEMBLY_NOT_FOUND);
+ cmp2(STATUS_SXS_MANIFEST_FORMAT_ERROR, ERROR_SXS_MANIFEST_FORMAT_ERROR);
+ cmp2(STATUS_SXS_MANIFEST_PARSE_ERROR, ERROR_SXS_MANIFEST_PARSE_ERROR);
+ cmp2(STATUS_SXS_ACTIVATION_CONTEXT_DISABLED, ERROR_SXS_ACTIVATION_CONTEXT_DISABLED);
+ cmp2(STATUS_SXS_KEY_NOT_FOUND, ERROR_SXS_KEY_NOT_FOUND);
+ cmp2(STATUS_SXS_WRONG_SECTION_TYPE, ERROR_SXS_WRONG_SECTION_TYPE);
+ cmp2(STATUS_SXS_THREAD_QUERIES_DISABLED, ERROR_SXS_THREAD_QUERIES_DISABLED);
+ cmp2(STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET, ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET);
+ cmp2(STATUS_REDIRECTOR_STARTED, ERROR_SERVICE_ALREADY_RUNNING);
+ cmp2(STATUS_AUDITING_DISABLED, ERROR_AUDITING_DISABLED);
+ cmp2(STATUS_CLUSTER_NODE_ALREADY_UP, ERROR_CLUSTER_NODE_ALREADY_UP);
+ cmp2(STATUS_CLUSTER_NODE_ALREADY_DOWN, ERROR_CLUSTER_NODE_ALREADY_DOWN);
+ cmp2(STATUS_CLUSTER_NETWORK_ALREADY_ONLINE, ERROR_CLUSTER_NETWORK_ALREADY_ONLINE);
+ cmp2(STATUS_CLUSTER_NETWORK_ALREADY_OFFLINE, ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE);
+ cmp2(STATUS_CLUSTER_NODE_ALREADY_MEMBER, ERROR_CLUSTER_NODE_ALREADY_MEMBER);
+ cmp2(STATUS_CLUSTER_INVALID_NODE, ERROR_CLUSTER_INVALID_NODE);
+ cmp2(STATUS_CLUSTER_NODE_EXISTS, ERROR_CLUSTER_NODE_EXISTS);
+ cmp2(STATUS_CLUSTER_JOIN_IN_PROGRESS, ERROR_CLUSTER_JOIN_IN_PROGRESS);
+ cmp2(STATUS_CLUSTER_NODE_NOT_FOUND, ERROR_CLUSTER_NODE_NOT_FOUND);
+ cmp2(STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND, ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND);
+ cmp2(STATUS_CLUSTER_NETWORK_EXISTS, ERROR_CLUSTER_NETWORK_EXISTS);
+ cmp2(STATUS_CLUSTER_NETWORK_NOT_FOUND, ERROR_CLUSTER_NETWORK_NOT_FOUND);
+ cmp2(STATUS_CLUSTER_NETINTERFACE_EXISTS, ERROR_CLUSTER_NETINTERFACE_EXISTS);
+ cmp2(STATUS_CLUSTER_NETINTERFACE_NOT_FOUND, ERROR_CLUSTER_NETINTERFACE_NOT_FOUND);
+ cmp2(STATUS_CLUSTER_INVALID_REQUEST, ERROR_CLUSTER_INVALID_REQUEST);
+ cmp2(STATUS_CLUSTER_INVALID_NETWORK_PROVIDER,ERROR_CLUSTER_INVALID_NETWORK_PROVIDER);
+ cmp2(STATUS_CLUSTER_NODE_DOWN, ERROR_CLUSTER_NODE_DOWN);
+ cmp2(STATUS_CLUSTER_NODE_UNREACHABLE, ERROR_CLUSTER_NODE_UNREACHABLE);
+ cmp2(STATUS_CLUSTER_NODE_NOT_MEMBER, ERROR_CLUSTER_NODE_NOT_MEMBER);
+ cmp2(STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS, ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS);
+ cmp2(STATUS_CLUSTER_INVALID_NETWORK, ERROR_CLUSTER_INVALID_NETWORK);
+ cmp2(STATUS_CLUSTER_NODE_UP, ERROR_CLUSTER_NODE_UP);
+ cmp2(STATUS_CLUSTER_NODE_PAUSED, ERROR_CLUSTER_NODE_PAUSED);
+ cmp2(STATUS_CLUSTER_NODE_NOT_PAUSED, ERROR_CLUSTER_NODE_NOT_PAUSED);
+ cmp2(STATUS_CLUSTER_NO_SECURITY_CONTEXT, ERROR_CLUSTER_NO_SECURITY_CONTEXT);
+ cmp2(STATUS_CLUSTER_NETWORK_NOT_INTERNAL, ERROR_CLUSTER_NETWORK_NOT_INTERNAL);
+}
+
+START_TEST(error)
+{
+ if (prepare_test())
+ run_error_tests();
+}
--- /dev/null
+/*
+ * Unit test suite for ntdll exceptions
+ *
+ * Copyright 2005 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x500 /* For NTSTATUS */
+#endif
+
+#include <stdio.h>
+#define WIN32_NO_STATUS
+#include <windows.h>
+#define NTOS_MODE_USER
+#include <ndk/ntndk.h>
+
+#include "wine/test.h"
+
+/*
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winnt.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "excpt.h"
+#include "wine/test.h"
+
+*/
+
+#ifdef __i386__
+
+static struct _TEB * (WINAPI *pNtCurrentTeb)(void);
+
+/* Test various instruction combinations that cause a protection fault on the i386,
+ * and check what the resulting exception looks like.
+ */
+
+static const struct exception
+{
+ BYTE code[18]; /* asm code */
+ BYTE offset; /* offset of faulting instruction */
+ BYTE length; /* length of faulting instruction */
+ NTSTATUS status; /* expected status code */
+ DWORD nb_params; /* expected number of parameters */
+ DWORD params[4]; /* expected parameters */
+} exceptions[] =
+{
+ /* test some privileged instructions */
+ { { 0xfb, 0xc3 }, /* sti; ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x6c, 0xc3 }, /* insb (%dx); ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x6d, 0xc3 }, /* insl (%dx); ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x6e, 0xc3 }, /* outsb (%dx); ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x6f, 0xc3 }, /* outsl (%dx); ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xe4, 0x11, 0xc3 }, /* inb $0x11,%al; ret */
+ 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xe5, 0x11, 0xc3 }, /* inl $0x11,%eax; ret */
+ 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xe6, 0x11, 0xc3 }, /* outb %al,$0x11; ret */
+ 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xe7, 0x11, 0xc3 }, /* outl %eax,$0x11; ret */
+ 0, 2, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xed, 0xc3 }, /* inl (%dx),%eax; ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xee, 0xc3 }, /* outb %al,(%dx); ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xef, 0xc3 }, /* outl %eax,(%dx); ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xf4, 0xc3 }, /* hlt; ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0xfa, 0xc3 }, /* cli; ret */
+ 0, 1, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+
+ /* test long jump to invalid selector */
+ { { 0xea, 0, 0, 0, 0, 0, 0, 0xc3 }, /* ljmp $0,$0; ret */
+ 0, 7, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+
+ /* test iret to invalid selector */
+ { { 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0xcf, 0x83, 0xc4, 0x0c, 0xc3 },
+ /* pushl $0; pushl $0; pushl $0; iret; addl $12,%esp; ret */
+ 6, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+
+ /* test loading an invalid selector */
+ { { 0xb8, 0xef, 0xbe, 0x00, 0x00, 0x8e, 0xe8, 0xc3 }, /* mov $beef,%ax; mov %ax,%gs; ret */
+ 5, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xbee8 } },
+
+ /* test accessing a zero selector */
+ { { 0x06, 0x31, 0xc0, 0x8e, 0xc0, 0x26, 0xa1, 0, 0, 0, 0, 0x07, 0xc3 },
+ /* push %es; xor %eax,%eax; mov %ax,%es; mov %es:(0),%ax; pop %es */
+ 5, 6, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+
+ /* test moving %cs -> %ss */
+ { { 0x0e, 0x17, 0x58, 0xc3 }, /* pushl %cs; popl %ss; popl %eax; ret */
+ 1, 1, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+
+ /* test overlong instruction (limit is 16 bytes) */
+ { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
+ 0, 16, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+ { { 0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0x64,0xfa,0xc3 },
+ 0, 15, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+
+ /* test invalid interrupt */
+ { { 0xcd, 0xff, 0xc3 }, /* int $0xff; ret */
+ 0, 2, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+
+ /* test moves to/from Crx */
+ { { 0x0f, 0x20, 0xc0, 0xc3 }, /* movl %cr0,%eax; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x20, 0xe0, 0xc3 }, /* movl %cr4,%eax; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x22, 0xc0, 0xc3 }, /* movl %eax,%cr0; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x22, 0xe0, 0xc3 }, /* movl %eax,%cr4; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+
+ /* test moves to/from Drx */
+ { { 0x0f, 0x21, 0xc0, 0xc3 }, /* movl %dr0,%eax; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x21, 0xc8, 0xc3 }, /* movl %dr1,%eax; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x21, 0xf8, 0xc3 }, /* movl %dr7,%eax; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x23, 0xc0, 0xc3 }, /* movl %eax,%dr0; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x23, 0xc8, 0xc3 }, /* movl %eax,%dr1; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+ { { 0x0f, 0x23, 0xf8, 0xc3 }, /* movl %eax,%dr7; ret */
+ 0, 3, STATUS_PRIVILEGED_INSTRUCTION, 0 },
+
+ /* test memory reads */
+ { { 0xa1, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffc,%eax; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xfffffffc } },
+ { { 0xa1, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffd,%eax; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+ { { 0xa1, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xfffffffe,%eax; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+ { { 0xa1, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl 0xffffffff,%eax; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+
+ /* test memory writes */
+ { { 0xa3, 0xfc, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffc; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 1, 0xfffffffc } },
+ { { 0xa3, 0xfd, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffd; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+ { { 0xa3, 0xfe, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xfffffffe; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+ { { 0xa3, 0xff, 0xff, 0xff, 0xff, 0xc3 }, /* movl %eax,0xffffffff; ret */
+ 0, 5, STATUS_ACCESS_VIOLATION, 2, { 0, 0xffffffff } },
+};
+
+static int got_exception;
+
+static DWORD handler( EXCEPTION_RECORD *rec, EXCEPTION_REGISTRATION_RECORD *frame,
+ CONTEXT *context, EXCEPTION_REGISTRATION_RECORD **dispatcher )
+{
+ const struct exception *except = *(const struct exception **)(frame + 1);
+ unsigned int i, entry = except - exceptions;
+
+ got_exception++;
+ trace( "exception: %lx flags:%lx addr:%p\n",
+ rec->ExceptionCode, rec->ExceptionFlags, rec->ExceptionAddress );
+
+ ok( rec->ExceptionCode == except->status,
+ "%u: Wrong exception code %lx/%lx\n", entry, rec->ExceptionCode, except->status );
+ ok( rec->ExceptionAddress == except->code + except->offset,
+ "%u: Wrong exception address %p/%p\n", entry,
+ rec->ExceptionAddress, except->code + except->offset );
+
+ ok( rec->NumberParameters == except->nb_params,
+ "%u: Wrong number of parameters %lu/%lu\n", entry, rec->NumberParameters, except->nb_params );
+ for (i = 0; i < rec->NumberParameters; i++)
+ ok( rec->ExceptionInformation[i] == except->params[i],
+ "%u: Wrong parameter %d: %lx/%lx\n",
+ entry, i, rec->ExceptionInformation[i], except->params[i] );
+
+ /* don't handle exception if it's not the address we expected */
+ if (rec->ExceptionAddress != except->code + except->offset) return ExceptionContinueSearch;
+
+ context->Eip += except->length;
+ return ExceptionContinueExecution;
+}
+
+static void test_prot_fault(void)
+{
+ unsigned int i;
+ struct
+ {
+ EXCEPTION_REGISTRATION_RECORD frame;
+ const struct exception *except;
+ } exc_frame;
+
+ pNtCurrentTeb = (void *)GetProcAddress( GetModuleHandleA("ntdll.dll"), "NtCurrentTeb" );
+ if (!pNtCurrentTeb)
+ {
+ trace( "NtCurrentTeb not found, skipping tests\n" );
+ return;
+ }
+
+ exc_frame.frame.Handler = handler;
+ exc_frame.frame.Next = pNtCurrentTeb()->Tib.ExceptionList;
+ pNtCurrentTeb()->Tib.ExceptionList = &exc_frame.frame;
+ for (i = 0; i < sizeof(exceptions)/sizeof(exceptions[0]); i++)
+ {
+ void (*func)(void) = (void *)exceptions[i].code;
+ exc_frame.except = &exceptions[i];
+ got_exception = 0;
+ func();
+ if (!i && !got_exception)
+ {
+ trace( "No exception, assuming win9x, no point in testing further\n" );
+ break;
+ }
+ ok( got_exception == (exceptions[i].status != 0),
+ "%u: bad exception count %d\n", i, got_exception );
+ }
+ pNtCurrentTeb()->Tib.ExceptionList = exc_frame.frame.Next;
+}
+
+#endif /* __i386__ */
+
+START_TEST(exception)
+{
+#ifdef __i386__
+ test_prot_fault();
+#endif
+}
--- /dev/null
+/* File generated automatically from tools/winapi/test.dat; do not edit! */
+/* This file can be copied, modified and distributed without restriction. */
+
+/*
+ * Unit tests for data structure packing
+ */
+
+#define WINVER 0x0501
+#define _WIN32_IE 0x0501
+#define _WIN32_WINNT 0x0501
+
+#define WINE_NOWINSOCK
+
+#include "ntdll_test.h"
+
+#include "wine/test.h"
+
+/***********************************************************************
+ * Compability macros
+ */
+
+#define DWORD_PTR UINT_PTR
+#define LONG_PTR INT_PTR
+#define ULONG_PTR UINT_PTR
+
+/***********************************************************************
+ * Windows API extension
+ */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define FIELD_ALIGNMENT(type, field) __alignof(((type*)0)->field)
+#elif defined(__GNUC__)
+# define FIELD_ALIGNMENT(type, field) __alignof__(((type*)0)->field)
+#else
+/* FIXME: Not sure if is possible to do without compiler extension */
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define _TYPE_ALIGNMENT(type) __alignof(type)
+#elif defined(__GNUC__)
+# define _TYPE_ALIGNMENT(type) __alignof__(type)
+#else
+/*
+ * FIXME: Not sure if is possible to do without compiler extension
+ * (if type is not just a name that is, if so the normal)
+ * TYPE_ALIGNMENT can be used)
+ */
+#endif
+
+#if defined(TYPE_ALIGNMENT) && defined(_MSC_VER) && _MSC_VER >= 800 && !defined(__cplusplus)
+#pragma warning(disable:4116)
+#endif
+
+#if !defined(TYPE_ALIGNMENT) && defined(_TYPE_ALIGNMENT)
+# define TYPE_ALIGNMENT _TYPE_ALIGNMENT
+#endif
+
+/***********************************************************************
+ * Test helper macros
+ */
+
+#ifdef FIELD_ALIGNMENT
+# define TEST_FIELD_ALIGNMENT(type, field, align) \
+ ok(FIELD_ALIGNMENT(type, field) == align, \
+ "FIELD_ALIGNMENT(" #type ", " #field ") == %d (expected " #align ")\n", \
+ (int)FIELD_ALIGNMENT(type, field))
+#else
+# define TEST_FIELD_ALIGNMENT(type, field, align) do { } while (0)
+#endif
+
+#define TEST_FIELD_OFFSET(type, field, offset) \
+ ok(FIELD_OFFSET(type, field) == offset, \
+ "FIELD_OFFSET(" #type ", " #field ") == %ld (expected " #offset ")\n", \
+ (long int)FIELD_OFFSET(type, field))
+
+#ifdef _TYPE_ALIGNMENT
+#define TEST__TYPE_ALIGNMENT(type, align) \
+ ok(_TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)_TYPE_ALIGNMENT(type))
+#else
+# define TEST__TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#ifdef TYPE_ALIGNMENT
+#define TEST_TYPE_ALIGNMENT(type, align) \
+ ok(TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)TYPE_ALIGNMENT(type))
+#else
+# define TEST_TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#define TEST_TYPE_SIZE(type, size) \
+ ok(sizeof(type) == size, "sizeof(" #type ") == %d (expected " #size ")\n", ((int) sizeof(type)))
+
+/***********************************************************************
+ * Test macros
+ */
+
+#define TEST_FIELD(type, field_type, field_name, field_offset, field_size, field_align) \
+ TEST_TYPE_SIZE(field_type, field_size); \
+ TEST_FIELD_ALIGNMENT(type, field_name, field_align); \
+ TEST_FIELD_OFFSET(type, field_name, field_offset); \
+
+#define TEST_TYPE(type, size, align) \
+ TEST_TYPE_ALIGNMENT(type, align); \
+ TEST_TYPE_SIZE(type, size)
+
+#define TEST_TYPE_POINTER(type, size, align) \
+ TEST__TYPE_ALIGNMENT(*(type)0, align); \
+ TEST_TYPE_SIZE(*(type)0, size)
+
+#define TEST_TYPE_SIGNED(type) \
+ ok((type) -1 < 0, "(" #type ") -1 < 0\n");
+
+#define TEST_TYPE_UNSIGNED(type) \
+ ok((type) -1 > 0, "(" #type ") -1 > 0\n");
+
+static void test_pack_DWORD32(void)
+{
+ /* DWORD32 */
+ TEST_TYPE(DWORD32, 4, 4);
+ TEST_TYPE_UNSIGNED(DWORD32);
+}
+
+static void test_pack_DWORD64(void)
+{
+ /* DWORD64 */
+ TEST_TYPE(DWORD64, 8, 8);
+ TEST_TYPE_UNSIGNED(DWORD64);
+}
+
+static void test_pack_DWORD_PTR(void)
+{
+ /* DWORD_PTR */
+ TEST_TYPE(DWORD_PTR, 4, 4);
+}
+
+static void test_pack_HALF_PTR(void)
+{
+ /* HALF_PTR */
+ TEST_TYPE(HALF_PTR, 2, 2);
+ TEST_TYPE_SIGNED(HALF_PTR);
+}
+
+static void test_pack_INT16(void)
+{
+ /* INT16 */
+ TEST_TYPE(INT16, 2, 2);
+ TEST_TYPE_SIGNED(INT16);
+}
+
+static void test_pack_INT32(void)
+{
+ /* INT32 */
+ TEST_TYPE(INT32, 4, 4);
+ TEST_TYPE_SIGNED(INT32);
+}
+
+static void test_pack_INT64(void)
+{
+ /* INT64 */
+ TEST_TYPE(INT64, 8, 8);
+ TEST_TYPE_SIGNED(INT64);
+}
+
+static void test_pack_INT8(void)
+{
+ /* INT8 */
+ TEST_TYPE(INT8, 1, 1);
+ TEST_TYPE_SIGNED(INT8);
+}
+
+static void test_pack_INT_PTR(void)
+{
+ /* INT_PTR */
+ TEST_TYPE(INT_PTR, 4, 4);
+ TEST_TYPE_SIGNED(INT_PTR);
+}
+
+static void test_pack_LONG32(void)
+{
+ /* LONG32 */
+ TEST_TYPE(LONG32, 4, 4);
+ TEST_TYPE_SIGNED(LONG32);
+}
+
+static void test_pack_LONG64(void)
+{
+ /* LONG64 */
+ TEST_TYPE(LONG64, 8, 8);
+ TEST_TYPE_SIGNED(LONG64);
+}
+
+static void test_pack_LONG_PTR(void)
+{
+ /* LONG_PTR */
+ TEST_TYPE(LONG_PTR, 4, 4);
+ TEST_TYPE_SIGNED(LONG_PTR);
+}
+
+static void test_pack_SIZE_T(void)
+{
+ /* SIZE_T */
+ TEST_TYPE(SIZE_T, 4, 4);
+}
+
+static void test_pack_SSIZE_T(void)
+{
+ /* SSIZE_T */
+ TEST_TYPE(SSIZE_T, 4, 4);
+}
+
+static void test_pack_UHALF_PTR(void)
+{
+ /* UHALF_PTR */
+ TEST_TYPE(UHALF_PTR, 2, 2);
+ TEST_TYPE_UNSIGNED(UHALF_PTR);
+}
+
+static void test_pack_UINT16(void)
+{
+ /* UINT16 */
+ TEST_TYPE(UINT16, 2, 2);
+ TEST_TYPE_UNSIGNED(UINT16);
+}
+
+static void test_pack_UINT32(void)
+{
+ /* UINT32 */
+ TEST_TYPE(UINT32, 4, 4);
+ TEST_TYPE_UNSIGNED(UINT32);
+}
+
+static void test_pack_UINT64(void)
+{
+ /* UINT64 */
+ TEST_TYPE(UINT64, 8, 8);
+ TEST_TYPE_UNSIGNED(UINT64);
+}
+
+static void test_pack_UINT8(void)
+{
+ /* UINT8 */
+ TEST_TYPE(UINT8, 1, 1);
+ TEST_TYPE_UNSIGNED(UINT8);
+}
+
+static void test_pack_UINT_PTR(void)
+{
+ /* UINT_PTR */
+ TEST_TYPE(UINT_PTR, 4, 4);
+ TEST_TYPE_UNSIGNED(UINT_PTR);
+}
+
+static void test_pack_ULONG32(void)
+{
+ /* ULONG32 */
+ TEST_TYPE(ULONG32, 4, 4);
+ TEST_TYPE_UNSIGNED(ULONG32);
+}
+
+static void test_pack_ULONG64(void)
+{
+ /* ULONG64 */
+ TEST_TYPE(ULONG64, 8, 8);
+ TEST_TYPE_UNSIGNED(ULONG64);
+}
+
+static void test_pack_ULONG_PTR(void)
+{
+ /* ULONG_PTR */
+ TEST_TYPE(ULONG_PTR, 4, 4);
+ TEST_TYPE_UNSIGNED(ULONG_PTR);
+}
+
+static void test_pack_ACCESS_ALLOWED_ACE(void)
+{
+ /* ACCESS_ALLOWED_ACE (pack 4) */
+ TEST_TYPE(ACCESS_ALLOWED_ACE, 12, 4);
+ TEST_FIELD(ACCESS_ALLOWED_ACE, ACE_HEADER, Header, 0, 4, 2);
+ TEST_FIELD(ACCESS_ALLOWED_ACE, DWORD, Mask, 4, 4, 4);
+ TEST_FIELD(ACCESS_ALLOWED_ACE, DWORD, SidStart, 8, 4, 4);
+}
+
+static void test_pack_ACCESS_DENIED_ACE(void)
+{
+ /* ACCESS_DENIED_ACE (pack 4) */
+ TEST_TYPE(ACCESS_DENIED_ACE, 12, 4);
+ TEST_FIELD(ACCESS_DENIED_ACE, ACE_HEADER, Header, 0, 4, 2);
+ TEST_FIELD(ACCESS_DENIED_ACE, DWORD, Mask, 4, 4, 4);
+ TEST_FIELD(ACCESS_DENIED_ACE, DWORD, SidStart, 8, 4, 4);
+}
+
+static void test_pack_ACCESS_MASK(void)
+{
+ /* ACCESS_MASK */
+ TEST_TYPE(ACCESS_MASK, 4, 4);
+ TEST_TYPE_UNSIGNED(ACCESS_MASK);
+}
+
+static void test_pack_ACE_HEADER(void)
+{
+ /* ACE_HEADER (pack 4) */
+ TEST_TYPE(ACE_HEADER, 4, 2);
+ TEST_FIELD(ACE_HEADER, BYTE, AceType, 0, 1, 1);
+ TEST_FIELD(ACE_HEADER, BYTE, AceFlags, 1, 1, 1);
+ TEST_FIELD(ACE_HEADER, WORD, AceSize, 2, 2, 2);
+}
+
+static void test_pack_ACL(void)
+{
+ /* ACL (pack 4) */
+ TEST_TYPE(ACL, 8, 2);
+ TEST_FIELD(ACL, BYTE, AclRevision, 0, 1, 1);
+ TEST_FIELD(ACL, BYTE, Sbz1, 1, 1, 1);
+ TEST_FIELD(ACL, WORD, AclSize, 2, 2, 2);
+ TEST_FIELD(ACL, WORD, AceCount, 4, 2, 2);
+ TEST_FIELD(ACL, WORD, Sbz2, 6, 2, 2);
+}
+
+static void test_pack_ACL_REVISION_INFORMATION(void)
+{
+ /* ACL_REVISION_INFORMATION (pack 4) */
+ TEST_TYPE(ACL_REVISION_INFORMATION, 4, 4);
+ TEST_FIELD(ACL_REVISION_INFORMATION, DWORD, AclRevision, 0, 4, 4);
+}
+
+static void test_pack_ACL_SIZE_INFORMATION(void)
+{
+ /* ACL_SIZE_INFORMATION (pack 4) */
+ TEST_TYPE(ACL_SIZE_INFORMATION, 12, 4);
+ TEST_FIELD(ACL_SIZE_INFORMATION, DWORD, AceCount, 0, 4, 4);
+ TEST_FIELD(ACL_SIZE_INFORMATION, DWORD, AclBytesInUse, 4, 4, 4);
+ TEST_FIELD(ACL_SIZE_INFORMATION, DWORD, AclBytesFree, 8, 4, 4);
+}
+
+static void test_pack_BOOLEAN(void)
+{
+ /* BOOLEAN */
+ TEST_TYPE(BOOLEAN, 1, 1);
+ TEST_TYPE_UNSIGNED(BOOLEAN);
+}
+
+static void test_pack_CCHAR(void)
+{
+ /* CCHAR */
+ TEST_TYPE(CCHAR, 1, 1);
+ TEST_TYPE_SIGNED(CCHAR);
+}
+
+static void test_pack_CHAR(void)
+{
+ /* CHAR */
+ TEST_TYPE(CHAR, 1, 1);
+ TEST_TYPE_SIGNED(CHAR);
+}
+
+static void test_pack_DWORDLONG(void)
+{
+ /* DWORDLONG */
+ TEST_TYPE(DWORDLONG, 8, 8);
+ TEST_TYPE_UNSIGNED(DWORDLONG);
+}
+
+static void test_pack_EXCEPTION_POINTERS(void)
+{
+ /* EXCEPTION_POINTERS (pack 4) */
+ TEST_TYPE(EXCEPTION_POINTERS, 8, 4);
+ TEST_FIELD(EXCEPTION_POINTERS, PEXCEPTION_RECORD, ExceptionRecord, 0, 4, 4);
+ TEST_FIELD(EXCEPTION_POINTERS, PCONTEXT, ContextRecord, 4, 4, 4);
+}
+
+static void test_pack_EXCEPTION_RECORD(void)
+{
+ /* EXCEPTION_RECORD (pack 4) */
+ TEST_TYPE(EXCEPTION_RECORD, 80, 4);
+ TEST_FIELD(EXCEPTION_RECORD, DWORD, ExceptionCode, 0, 4, 4);
+ TEST_FIELD(EXCEPTION_RECORD, DWORD, ExceptionFlags, 4, 4, 4);
+ TEST_FIELD(EXCEPTION_RECORD, struct _EXCEPTION_RECORD *, ExceptionRecord, 8, 4, 4);
+ TEST_FIELD(EXCEPTION_RECORD, PVOID, ExceptionAddress, 12, 4, 4);
+ TEST_FIELD(EXCEPTION_RECORD, DWORD, NumberParameters, 16, 4, 4);
+ TEST_FIELD(EXCEPTION_RECORD, ULONG_PTR[EXCEPTION_MAXIMUM_PARAMETERS], ExceptionInformation, 20, 60, 4);
+}
+
+static void test_pack_EXECUTION_STATE(void)
+{
+ /* EXECUTION_STATE */
+ TEST_TYPE(EXECUTION_STATE, 4, 4);
+ TEST_TYPE_UNSIGNED(EXECUTION_STATE);
+}
+
+static void test_pack_FLOATING_SAVE_AREA(void)
+{
+ /* FLOATING_SAVE_AREA (pack 4) */
+ TEST_TYPE(FLOATING_SAVE_AREA, 112, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, ControlWord, 0, 4, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, StatusWord, 4, 4, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, TagWord, 8, 4, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, ErrorOffset, 12, 4, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, ErrorSelector, 16, 4, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, DataOffset, 20, 4, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, DataSelector, 24, 4, 4);
+ TEST_FIELD(FLOATING_SAVE_AREA, BYTE[SIZE_OF_80387_REGISTERS], RegisterArea, 28, 80, 1);
+ TEST_FIELD(FLOATING_SAVE_AREA, DWORD, Cr0NpxState, 108, 4, 4);
+}
+
+static void test_pack_FPO_DATA(void)
+{
+ /* FPO_DATA (pack 4) */
+ TEST_TYPE(FPO_DATA, 16, 4);
+ TEST_FIELD(FPO_DATA, DWORD, ulOffStart, 0, 4, 4);
+ TEST_FIELD(FPO_DATA, DWORD, cbProcSize, 4, 4, 4);
+ TEST_FIELD(FPO_DATA, DWORD, cdwLocals, 8, 4, 4);
+ TEST_FIELD(FPO_DATA, WORD, cdwParams, 12, 2, 2);
+}
+
+static void test_pack_GENERIC_MAPPING(void)
+{
+ /* GENERIC_MAPPING (pack 4) */
+ TEST_TYPE(GENERIC_MAPPING, 16, 4);
+ TEST_FIELD(GENERIC_MAPPING, ACCESS_MASK, GenericRead, 0, 4, 4);
+ TEST_FIELD(GENERIC_MAPPING, ACCESS_MASK, GenericWrite, 4, 4, 4);
+ TEST_FIELD(GENERIC_MAPPING, ACCESS_MASK, GenericExecute, 8, 4, 4);
+ TEST_FIELD(GENERIC_MAPPING, ACCESS_MASK, GenericAll, 12, 4, 4);
+}
+
+static void test_pack_HANDLE(void)
+{
+ /* HANDLE */
+ TEST_TYPE(HANDLE, 4, 4);
+}
+
+static void test_pack_HRESULT(void)
+{
+ /* HRESULT */
+ TEST_TYPE(HRESULT, 4, 4);
+}
+
+static void test_pack_IMAGE_ARCHIVE_MEMBER_HEADER(void)
+{
+ /* IMAGE_ARCHIVE_MEMBER_HEADER (pack 4) */
+ TEST_TYPE(IMAGE_ARCHIVE_MEMBER_HEADER, 60, 1);
+ TEST_FIELD(IMAGE_ARCHIVE_MEMBER_HEADER, BYTE[16], Name, 0, 16, 1);
+ TEST_FIELD(IMAGE_ARCHIVE_MEMBER_HEADER, BYTE[12], Date, 16, 12, 1);
+ TEST_FIELD(IMAGE_ARCHIVE_MEMBER_HEADER, BYTE[6], UserID, 28, 6, 1);
+ TEST_FIELD(IMAGE_ARCHIVE_MEMBER_HEADER, BYTE[6], GroupID, 34, 6, 1);
+ TEST_FIELD(IMAGE_ARCHIVE_MEMBER_HEADER, BYTE[8], Mode, 40, 8, 1);
+ TEST_FIELD(IMAGE_ARCHIVE_MEMBER_HEADER, BYTE[10], Size, 48, 10, 1);
+ TEST_FIELD(IMAGE_ARCHIVE_MEMBER_HEADER, BYTE[2], EndHeader, 58, 2, 1);
+}
+
+static void test_pack_IMAGE_AUX_SYMBOL(void)
+{
+ /* IMAGE_AUX_SYMBOL (pack 2) */
+}
+
+static void test_pack_IMAGE_BASE_RELOCATION(void)
+{
+ /* IMAGE_BASE_RELOCATION (pack 4) */
+ TEST_TYPE(IMAGE_BASE_RELOCATION, 8, 4);
+ TEST_FIELD(IMAGE_BASE_RELOCATION, DWORD, VirtualAddress, 0, 4, 4);
+ TEST_FIELD(IMAGE_BASE_RELOCATION, DWORD, SizeOfBlock, 4, 4, 4);
+}
+
+static void test_pack_IMAGE_BOUND_FORWARDER_REF(void)
+{
+ /* IMAGE_BOUND_FORWARDER_REF (pack 4) */
+ TEST_TYPE(IMAGE_BOUND_FORWARDER_REF, 8, 4);
+ TEST_FIELD(IMAGE_BOUND_FORWARDER_REF, DWORD, TimeDateStamp, 0, 4, 4);
+ TEST_FIELD(IMAGE_BOUND_FORWARDER_REF, WORD, OffsetModuleName, 4, 2, 2);
+ TEST_FIELD(IMAGE_BOUND_FORWARDER_REF, WORD, Reserved, 6, 2, 2);
+}
+
+static void test_pack_IMAGE_BOUND_IMPORT_DESCRIPTOR(void)
+{
+ /* IMAGE_BOUND_IMPORT_DESCRIPTOR (pack 4) */
+ TEST_TYPE(IMAGE_BOUND_IMPORT_DESCRIPTOR, 8, 4);
+ TEST_FIELD(IMAGE_BOUND_IMPORT_DESCRIPTOR, DWORD, TimeDateStamp, 0, 4, 4);
+ TEST_FIELD(IMAGE_BOUND_IMPORT_DESCRIPTOR, WORD, OffsetModuleName, 4, 2, 2);
+ TEST_FIELD(IMAGE_BOUND_IMPORT_DESCRIPTOR, WORD, NumberOfModuleForwarderRefs, 6, 2, 2);
+}
+
+static void test_pack_IMAGE_COFF_SYMBOLS_HEADER(void)
+{
+ /* IMAGE_COFF_SYMBOLS_HEADER (pack 4) */
+ TEST_TYPE(IMAGE_COFF_SYMBOLS_HEADER, 32, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, NumberOfSymbols, 0, 4, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, LvaToFirstSymbol, 4, 4, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, NumberOfLinenumbers, 8, 4, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, LvaToFirstLinenumber, 12, 4, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, RvaToFirstByteOfCode, 16, 4, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, RvaToLastByteOfCode, 20, 4, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, RvaToFirstByteOfData, 24, 4, 4);
+ TEST_FIELD(IMAGE_COFF_SYMBOLS_HEADER, DWORD, RvaToLastByteOfData, 28, 4, 4);
+}
+
+static void test_pack_IMAGE_DATA_DIRECTORY(void)
+{
+ /* IMAGE_DATA_DIRECTORY (pack 4) */
+ TEST_TYPE(IMAGE_DATA_DIRECTORY, 8, 4);
+ TEST_FIELD(IMAGE_DATA_DIRECTORY, DWORD, VirtualAddress, 0, 4, 4);
+ TEST_FIELD(IMAGE_DATA_DIRECTORY, DWORD, Size, 4, 4, 4);
+}
+
+static void test_pack_IMAGE_DEBUG_DIRECTORY(void)
+{
+ /* IMAGE_DEBUG_DIRECTORY (pack 4) */
+ TEST_TYPE(IMAGE_DEBUG_DIRECTORY, 28, 4);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, DWORD, Characteristics, 0, 4, 4);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, DWORD, TimeDateStamp, 4, 4, 4);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, WORD, MajorVersion, 8, 2, 2);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, WORD, MinorVersion, 10, 2, 2);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, DWORD, Type, 12, 4, 4);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, DWORD, SizeOfData, 16, 4, 4);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, DWORD, AddressOfRawData, 20, 4, 4);
+ TEST_FIELD(IMAGE_DEBUG_DIRECTORY, DWORD, PointerToRawData, 24, 4, 4);
+}
+
+static void test_pack_IMAGE_DEBUG_MISC(void)
+{
+ /* IMAGE_DEBUG_MISC (pack 4) */
+ TEST_TYPE(IMAGE_DEBUG_MISC, 16, 4);
+ TEST_FIELD(IMAGE_DEBUG_MISC, DWORD, DataType, 0, 4, 4);
+ TEST_FIELD(IMAGE_DEBUG_MISC, DWORD, Length, 4, 4, 4);
+ TEST_FIELD(IMAGE_DEBUG_MISC, BYTE, Unicode, 8, 1, 1);
+ TEST_FIELD(IMAGE_DEBUG_MISC, BYTE[ 3 ], Reserved, 9, 3, 1);
+ TEST_FIELD(IMAGE_DEBUG_MISC, BYTE[ 1 ], Data, 12, 1, 1);
+}
+
+static void test_pack_IMAGE_DOS_HEADER(void)
+{
+ /* IMAGE_DOS_HEADER (pack 2) */
+ TEST_TYPE(IMAGE_DOS_HEADER, 64, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_magic, 0, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_cblp, 2, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_cp, 4, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_crlc, 6, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_cparhdr, 8, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_minalloc, 10, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_maxalloc, 12, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_ss, 14, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_sp, 16, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_csum, 18, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_ip, 20, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_cs, 22, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_lfarlc, 24, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_ovno, 26, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD[4], e_res, 28, 8, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_oemid, 36, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD, e_oeminfo, 38, 2, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, WORD[10], e_res2, 40, 20, 2);
+ TEST_FIELD(IMAGE_DOS_HEADER, DWORD, e_lfanew, 60, 4, 2);
+}
+
+static void test_pack_IMAGE_EXPORT_DIRECTORY(void)
+{
+ /* IMAGE_EXPORT_DIRECTORY (pack 4) */
+ TEST_TYPE(IMAGE_EXPORT_DIRECTORY, 40, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, Characteristics, 0, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, TimeDateStamp, 4, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, WORD, MajorVersion, 8, 2, 2);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, WORD, MinorVersion, 10, 2, 2);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, Name, 12, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, Base, 16, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, NumberOfFunctions, 20, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, NumberOfNames, 24, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, AddressOfFunctions, 28, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, AddressOfNames, 32, 4, 4);
+ TEST_FIELD(IMAGE_EXPORT_DIRECTORY, DWORD, AddressOfNameOrdinals, 36, 4, 4);
+}
+
+static void test_pack_IMAGE_FILE_HEADER(void)
+{
+ /* IMAGE_FILE_HEADER (pack 4) */
+ TEST_TYPE(IMAGE_FILE_HEADER, 20, 4);
+ TEST_FIELD(IMAGE_FILE_HEADER, WORD, Machine, 0, 2, 2);
+ TEST_FIELD(IMAGE_FILE_HEADER, WORD, NumberOfSections, 2, 2, 2);
+ TEST_FIELD(IMAGE_FILE_HEADER, DWORD, TimeDateStamp, 4, 4, 4);
+ TEST_FIELD(IMAGE_FILE_HEADER, DWORD, PointerToSymbolTable, 8, 4, 4);
+ TEST_FIELD(IMAGE_FILE_HEADER, DWORD, NumberOfSymbols, 12, 4, 4);
+ TEST_FIELD(IMAGE_FILE_HEADER, WORD, SizeOfOptionalHeader, 16, 2, 2);
+ TEST_FIELD(IMAGE_FILE_HEADER, WORD, Characteristics, 18, 2, 2);
+}
+
+static void test_pack_IMAGE_FUNCTION_ENTRY(void)
+{
+ /* IMAGE_FUNCTION_ENTRY (pack 4) */
+ TEST_TYPE(IMAGE_FUNCTION_ENTRY, 12, 4);
+ TEST_FIELD(IMAGE_FUNCTION_ENTRY, DWORD, StartingAddress, 0, 4, 4);
+ TEST_FIELD(IMAGE_FUNCTION_ENTRY, DWORD, EndingAddress, 4, 4, 4);
+ TEST_FIELD(IMAGE_FUNCTION_ENTRY, DWORD, EndOfPrologue, 8, 4, 4);
+}
+
+static void test_pack_IMAGE_IMPORT_BY_NAME(void)
+{
+ /* IMAGE_IMPORT_BY_NAME (pack 4) */
+ TEST_TYPE(IMAGE_IMPORT_BY_NAME, 4, 2);
+ TEST_FIELD(IMAGE_IMPORT_BY_NAME, WORD, Hint, 0, 2, 2);
+ TEST_FIELD(IMAGE_IMPORT_BY_NAME, BYTE[1], Name, 2, 1, 1);
+}
+
+static void test_pack_IMAGE_IMPORT_DESCRIPTOR(void)
+{
+ /* IMAGE_IMPORT_DESCRIPTOR (pack 4) */
+}
+
+static void test_pack_IMAGE_LINENUMBER(void)
+{
+ /* IMAGE_LINENUMBER (pack 2) */
+}
+
+static void test_pack_IMAGE_LOAD_CONFIG_DIRECTORY(void)
+{
+ /* IMAGE_LOAD_CONFIG_DIRECTORY (pack 4) */
+ TEST_TYPE(IMAGE_LOAD_CONFIG_DIRECTORY, 72, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, Size, 0, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, TimeDateStamp, 4, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, WORD, MajorVersion, 8, 2, 2);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, WORD, MinorVersion, 10, 2, 2);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, GlobalFlagsClear, 12, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, GlobalFlagsSet, 16, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, CriticalSectionDefaultTimeout, 20, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, DeCommitFreeBlockThreshold, 24, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, DeCommitTotalFreeThreshold, 28, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, PVOID, LockPrefixTable, 32, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, MaximumAllocationSize, 36, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, VirtualMemoryThreshold, 40, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, ProcessHeapFlags, 44, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, ProcessAffinityMask, 48, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, WORD, CSDVersion, 52, 2, 2);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, WORD, Reserved1, 54, 2, 2);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, PVOID, EditList, 56, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, SecurityCookie, 60, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, SEHandlerTable, 64, 4, 4);
+ TEST_FIELD(IMAGE_LOAD_CONFIG_DIRECTORY, DWORD, SEHandlerCount, 68, 4, 4);
+}
+
+static void test_pack_IMAGE_NT_HEADERS(void)
+{
+ /* IMAGE_NT_HEADERS (pack 4) */
+ TEST_TYPE(IMAGE_NT_HEADERS, 248, 4);
+ TEST_FIELD(IMAGE_NT_HEADERS, DWORD, Signature, 0, 4, 4);
+ TEST_FIELD(IMAGE_NT_HEADERS, IMAGE_FILE_HEADER, FileHeader, 4, 20, 4);
+ TEST_FIELD(IMAGE_NT_HEADERS, IMAGE_OPTIONAL_HEADER, OptionalHeader, 24, 224, 4);
+}
+
+static void test_pack_IMAGE_OPTIONAL_HEADER(void)
+{
+ /* IMAGE_OPTIONAL_HEADER (pack 4) */
+ TEST_TYPE(IMAGE_OPTIONAL_HEADER, 224, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, Magic, 0, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, BYTE, MajorLinkerVersion, 2, 1, 1);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, BYTE, MinorLinkerVersion, 3, 1, 1);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfCode, 4, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfInitializedData, 8, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfUninitializedData, 12, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, AddressOfEntryPoint, 16, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, BaseOfCode, 20, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, BaseOfData, 24, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, ImageBase, 28, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SectionAlignment, 32, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, FileAlignment, 36, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, MajorOperatingSystemVersion, 40, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, MinorOperatingSystemVersion, 42, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, MajorImageVersion, 44, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, MinorImageVersion, 46, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, MajorSubsystemVersion, 48, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, MinorSubsystemVersion, 50, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, Win32VersionValue, 52, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfImage, 56, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfHeaders, 60, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, CheckSum, 64, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, Subsystem, 68, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, WORD, DllCharacteristics, 70, 2, 2);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfStackReserve, 72, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfStackCommit, 76, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfHeapReserve, 80, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, SizeOfHeapCommit, 84, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, LoaderFlags, 88, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, DWORD, NumberOfRvaAndSizes, 92, 4, 4);
+ TEST_FIELD(IMAGE_OPTIONAL_HEADER, IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES], DataDirectory, 96, 128, 4);
+}
+
+static void test_pack_IMAGE_OS2_HEADER(void)
+{
+ /* IMAGE_OS2_HEADER (pack 2) */
+ TEST_TYPE(IMAGE_OS2_HEADER, 64, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_magic, 0, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, BYTE, ne_ver, 2, 1, 1);
+ TEST_FIELD(IMAGE_OS2_HEADER, BYTE, ne_rev, 3, 1, 1);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_enttab, 4, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_cbenttab, 6, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, LONG, ne_crc, 8, 4, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_flags, 12, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_autodata, 14, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_heap, 16, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_stack, 18, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, DWORD, ne_csip, 20, 4, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, DWORD, ne_sssp, 24, 4, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_cseg, 28, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_cmod, 30, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_cbnrestab, 32, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_segtab, 34, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_rsrctab, 36, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_restab, 38, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_modtab, 40, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_imptab, 42, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, DWORD, ne_nrestab, 44, 4, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_cmovent, 48, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_align, 50, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_cres, 52, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, BYTE, ne_exetyp, 54, 1, 1);
+ TEST_FIELD(IMAGE_OS2_HEADER, BYTE, ne_flagsothers, 55, 1, 1);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_pretthunks, 56, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_psegrefbytes, 58, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_swaparea, 60, 2, 2);
+ TEST_FIELD(IMAGE_OS2_HEADER, WORD, ne_expver, 62, 2, 2);
+}
+
+static void test_pack_IMAGE_RELOCATION(void)
+{
+ /* IMAGE_RELOCATION (pack 2) */
+}
+
+static void test_pack_IMAGE_RESOURCE_DATA_ENTRY(void)
+{
+ /* IMAGE_RESOURCE_DATA_ENTRY (pack 4) */
+ TEST_TYPE(IMAGE_RESOURCE_DATA_ENTRY, 16, 4);
+ TEST_FIELD(IMAGE_RESOURCE_DATA_ENTRY, DWORD, OffsetToData, 0, 4, 4);
+ TEST_FIELD(IMAGE_RESOURCE_DATA_ENTRY, DWORD, Size, 4, 4, 4);
+ TEST_FIELD(IMAGE_RESOURCE_DATA_ENTRY, DWORD, CodePage, 8, 4, 4);
+ TEST_FIELD(IMAGE_RESOURCE_DATA_ENTRY, DWORD, Reserved, 12, 4, 4);
+}
+
+static void test_pack_IMAGE_RESOURCE_DIRECTORY(void)
+{
+ /* IMAGE_RESOURCE_DIRECTORY (pack 4) */
+ TEST_TYPE(IMAGE_RESOURCE_DIRECTORY, 16, 4);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY, DWORD, Characteristics, 0, 4, 4);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY, DWORD, TimeDateStamp, 4, 4, 4);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY, WORD, MajorVersion, 8, 2, 2);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY, WORD, MinorVersion, 10, 2, 2);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY, WORD, NumberOfNamedEntries, 12, 2, 2);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY, WORD, NumberOfIdEntries, 14, 2, 2);
+}
+
+static void test_pack_IMAGE_RESOURCE_DIRECTORY_ENTRY(void)
+{
+ /* IMAGE_RESOURCE_DIRECTORY_ENTRY (pack 4) */
+}
+
+static void test_pack_IMAGE_RESOURCE_DIRECTORY_STRING(void)
+{
+ /* IMAGE_RESOURCE_DIRECTORY_STRING (pack 4) */
+ TEST_TYPE(IMAGE_RESOURCE_DIRECTORY_STRING, 4, 2);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY_STRING, WORD, Length, 0, 2, 2);
+ TEST_FIELD(IMAGE_RESOURCE_DIRECTORY_STRING, CHAR[ 1 ], NameString, 2, 1, 1);
+}
+
+static void test_pack_IMAGE_RESOURCE_DIR_STRING_U(void)
+{
+ /* IMAGE_RESOURCE_DIR_STRING_U (pack 4) */
+ TEST_TYPE(IMAGE_RESOURCE_DIR_STRING_U, 4, 2);
+ TEST_FIELD(IMAGE_RESOURCE_DIR_STRING_U, WORD, Length, 0, 2, 2);
+ TEST_FIELD(IMAGE_RESOURCE_DIR_STRING_U, WCHAR[ 1 ], NameString, 2, 2, 2);
+}
+
+static void test_pack_IMAGE_SECTION_HEADER(void)
+{
+ /* IMAGE_SECTION_HEADER (pack 4) */
+ TEST_FIELD(IMAGE_SECTION_HEADER, BYTE[IMAGE_SIZEOF_SHORT_NAME], Name, 0, 8, 1);
+}
+
+static void test_pack_IMAGE_SEPARATE_DEBUG_HEADER(void)
+{
+ /* IMAGE_SEPARATE_DEBUG_HEADER (pack 4) */
+ TEST_TYPE(IMAGE_SEPARATE_DEBUG_HEADER, 48, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, WORD, Signature, 0, 2, 2);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, WORD, Flags, 2, 2, 2);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, WORD, Machine, 4, 2, 2);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, WORD, Characteristics, 6, 2, 2);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, TimeDateStamp, 8, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, CheckSum, 12, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, ImageBase, 16, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, SizeOfImage, 20, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, NumberOfSections, 24, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, ExportedNamesSize, 28, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, DebugDirectorySize, 32, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD, SectionAlignment, 36, 4, 4);
+ TEST_FIELD(IMAGE_SEPARATE_DEBUG_HEADER, DWORD[ 2 ], Reserved, 40, 8, 4);
+}
+
+static void test_pack_IMAGE_SYMBOL(void)
+{
+ /* IMAGE_SYMBOL (pack 2) */
+}
+
+static void test_pack_IMAGE_THUNK_DATA(void)
+{
+ /* IMAGE_THUNK_DATA (pack 4) */
+}
+
+static void test_pack_IMAGE_TLS_DIRECTORY(void)
+{
+ /* IMAGE_TLS_DIRECTORY (pack 4) */
+ TEST_TYPE(IMAGE_TLS_DIRECTORY, 24, 4);
+ TEST_FIELD(IMAGE_TLS_DIRECTORY, DWORD, StartAddressOfRawData, 0, 4, 4);
+ TEST_FIELD(IMAGE_TLS_DIRECTORY, DWORD, EndAddressOfRawData, 4, 4, 4);
+ TEST_FIELD(IMAGE_TLS_DIRECTORY, LPDWORD, AddressOfIndex, 8, 4, 4);
+ TEST_FIELD(IMAGE_TLS_DIRECTORY, PIMAGE_TLS_CALLBACK *, AddressOfCallBacks, 12, 4, 4);
+ TEST_FIELD(IMAGE_TLS_DIRECTORY, DWORD, SizeOfZeroFill, 16, 4, 4);
+ TEST_FIELD(IMAGE_TLS_DIRECTORY, DWORD, Characteristics, 20, 4, 4);
+}
+
+static void test_pack_IMAGE_VXD_HEADER(void)
+{
+ /* IMAGE_VXD_HEADER (pack 2) */
+ TEST_TYPE(IMAGE_VXD_HEADER, 196, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, WORD, e32_magic, 0, 2, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, BYTE, e32_border, 2, 1, 1);
+ TEST_FIELD(IMAGE_VXD_HEADER, BYTE, e32_worder, 3, 1, 1);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_level, 4, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, WORD, e32_cpu, 8, 2, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, WORD, e32_os, 10, 2, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_ver, 12, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_mflags, 16, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_mpages, 20, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_startobj, 24, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_eip, 28, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_stackobj, 32, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_esp, 36, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_pagesize, 40, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_lastpagesize, 44, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_fixupsize, 48, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_fixupsum, 52, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_ldrsize, 56, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_ldrsum, 60, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_objtab, 64, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_objcnt, 68, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_objmap, 72, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_itermap, 76, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_rsrctab, 80, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_rsrccnt, 84, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_restab, 88, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_enttab, 92, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_dirtab, 96, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_dircnt, 100, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_fpagetab, 104, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_frectab, 108, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_impmod, 112, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_impmodcnt, 116, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_impproc, 120, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_pagesum, 124, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_datapage, 128, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_preload, 132, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_nrestab, 136, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_cbnrestab, 140, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_nressum, 144, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_autodata, 148, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_debuginfo, 152, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_debuglen, 156, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_instpreload, 160, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_instdemand, 164, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_heapsize, 168, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, BYTE[12], e32_res3, 172, 12, 1);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_winresoff, 184, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, DWORD, e32_winreslen, 188, 4, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, WORD, e32_devid, 192, 2, 2);
+ TEST_FIELD(IMAGE_VXD_HEADER, WORD, e32_ddkver, 194, 2, 2);
+}
+
+static void test_pack_IO_COUNTERS(void)
+{
+ /* IO_COUNTERS (pack 8) */
+ TEST_TYPE(IO_COUNTERS, 48, 8);
+ TEST_FIELD(IO_COUNTERS, ULONGLONG, ReadOperationCount, 0, 8, 8);
+ TEST_FIELD(IO_COUNTERS, ULONGLONG, WriteOperationCount, 8, 8, 8);
+ TEST_FIELD(IO_COUNTERS, ULONGLONG, OtherOperationCount, 16, 8, 8);
+ TEST_FIELD(IO_COUNTERS, ULONGLONG, ReadTransferCount, 24, 8, 8);
+ TEST_FIELD(IO_COUNTERS, ULONGLONG, WriteTransferCount, 32, 8, 8);
+ TEST_FIELD(IO_COUNTERS, ULONGLONG, OtherTransferCount, 40, 8, 8);
+}
+
+static void test_pack_LANGID(void)
+{
+ /* LANGID */
+ TEST_TYPE(LANGID, 2, 2);
+ TEST_TYPE_UNSIGNED(LANGID);
+}
+
+static void test_pack_LARGE_INTEGER(void)
+{
+ /* LARGE_INTEGER (pack 4) */
+}
+
+static void test_pack_LCID(void)
+{
+ /* LCID */
+ TEST_TYPE(LCID, 4, 4);
+ TEST_TYPE_UNSIGNED(LCID);
+}
+
+static void test_pack_LIST_ENTRY(void)
+{
+ /* LIST_ENTRY (pack 4) */
+ TEST_TYPE(LIST_ENTRY, 8, 4);
+ TEST_FIELD(LIST_ENTRY, struct _LIST_ENTRY *, Flink, 0, 4, 4);
+ TEST_FIELD(LIST_ENTRY, struct _LIST_ENTRY *, Blink, 4, 4, 4);
+}
+
+static void test_pack_LONG(void)
+{
+ /* LONG */
+ TEST_TYPE(LONG, 4, 4);
+ TEST_TYPE_SIGNED(LONG);
+}
+
+static void test_pack_LONGLONG(void)
+{
+ /* LONGLONG */
+ TEST_TYPE(LONGLONG, 8, 8);
+ TEST_TYPE_SIGNED(LONGLONG);
+}
+
+static void test_pack_LPTOP_LEVEL_EXCEPTION_FILTER(void)
+{
+ /* LPTOP_LEVEL_EXCEPTION_FILTER */
+ TEST_TYPE(LPTOP_LEVEL_EXCEPTION_FILTER, 4, 4);
+}
+
+static void test_pack_LUID(void)
+{
+ /* LUID (pack 4) */
+ TEST_TYPE(LUID, 8, 4);
+ TEST_FIELD(LUID, DWORD, LowPart, 0, 4, 4);
+ TEST_FIELD(LUID, LONG, HighPart, 4, 4, 4);
+}
+
+static void test_pack_LUID_AND_ATTRIBUTES(void)
+{
+ /* LUID_AND_ATTRIBUTES (pack 4) */
+ TEST_TYPE(LUID_AND_ATTRIBUTES, 12, 4);
+ TEST_FIELD(LUID_AND_ATTRIBUTES, LUID, Luid, 0, 8, 4);
+ TEST_FIELD(LUID_AND_ATTRIBUTES, DWORD, Attributes, 8, 4, 4);
+}
+
+static void test_pack_MEMORY_BASIC_INFORMATION(void)
+{
+ /* MEMORY_BASIC_INFORMATION (pack 4) */
+ TEST_TYPE(MEMORY_BASIC_INFORMATION, 28, 4);
+ TEST_FIELD(MEMORY_BASIC_INFORMATION, LPVOID, BaseAddress, 0, 4, 4);
+ TEST_FIELD(MEMORY_BASIC_INFORMATION, LPVOID, AllocationBase, 4, 4, 4);
+ TEST_FIELD(MEMORY_BASIC_INFORMATION, DWORD, AllocationProtect, 8, 4, 4);
+ TEST_FIELD(MEMORY_BASIC_INFORMATION, DWORD, RegionSize, 12, 4, 4);
+ TEST_FIELD(MEMORY_BASIC_INFORMATION, DWORD, State, 16, 4, 4);
+ TEST_FIELD(MEMORY_BASIC_INFORMATION, DWORD, Protect, 20, 4, 4);
+ TEST_FIELD(MEMORY_BASIC_INFORMATION, DWORD, Type, 24, 4, 4);
+}
+
+static void test_pack_MESSAGE_RESOURCE_BLOCK(void)
+{
+ /* MESSAGE_RESOURCE_BLOCK (pack 4) */
+ TEST_TYPE(MESSAGE_RESOURCE_BLOCK, 12, 4);
+ TEST_FIELD(MESSAGE_RESOURCE_BLOCK, DWORD, LowId, 0, 4, 4);
+ TEST_FIELD(MESSAGE_RESOURCE_BLOCK, DWORD, HighId, 4, 4, 4);
+ TEST_FIELD(MESSAGE_RESOURCE_BLOCK, DWORD, OffsetToEntries, 8, 4, 4);
+}
+
+static void test_pack_MESSAGE_RESOURCE_DATA(void)
+{
+ /* MESSAGE_RESOURCE_DATA (pack 4) */
+ TEST_TYPE(MESSAGE_RESOURCE_DATA, 16, 4);
+ TEST_FIELD(MESSAGE_RESOURCE_DATA, DWORD, NumberOfBlocks, 0, 4, 4);
+ TEST_FIELD(MESSAGE_RESOURCE_DATA, MESSAGE_RESOURCE_BLOCK[ 1 ], Blocks, 4, 12, 4);
+}
+
+static void test_pack_MESSAGE_RESOURCE_ENTRY(void)
+{
+ /* MESSAGE_RESOURCE_ENTRY (pack 4) */
+ TEST_TYPE(MESSAGE_RESOURCE_ENTRY, 6, 2);
+ TEST_FIELD(MESSAGE_RESOURCE_ENTRY, WORD, Length, 0, 2, 2);
+ TEST_FIELD(MESSAGE_RESOURCE_ENTRY, WORD, Flags, 2, 2, 2);
+ TEST_FIELD(MESSAGE_RESOURCE_ENTRY, BYTE[1], Text, 4, 1, 1);
+}
+
+static void test_pack_NT_TIB(void)
+{
+ /* NT_TIB (pack 4) */
+ TEST_FIELD(NT_TIB, struct _EXCEPTION_REGISTRATION_RECORD *, ExceptionList, 0, 4, 4);
+ TEST_FIELD(NT_TIB, PVOID, StackBase, 4, 4, 4);
+ TEST_FIELD(NT_TIB, PVOID, StackLimit, 8, 4, 4);
+ TEST_FIELD(NT_TIB, PVOID, SubSystemTib, 12, 4, 4);
+}
+
+static void test_pack_OBJECT_TYPE_LIST(void)
+{
+ /* OBJECT_TYPE_LIST (pack 4) */
+ TEST_TYPE(OBJECT_TYPE_LIST, 8, 4);
+ TEST_FIELD(OBJECT_TYPE_LIST, WORD, Level, 0, 2, 2);
+ TEST_FIELD(OBJECT_TYPE_LIST, WORD, Sbz, 2, 2, 2);
+ TEST_FIELD(OBJECT_TYPE_LIST, GUID *, ObjectType, 4, 4, 4);
+}
+
+static void test_pack_PACCESS_ALLOWED_ACE(void)
+{
+ /* PACCESS_ALLOWED_ACE */
+ TEST_TYPE(PACCESS_ALLOWED_ACE, 4, 4);
+ TEST_TYPE_POINTER(PACCESS_ALLOWED_ACE, 12, 4);
+}
+
+static void test_pack_PACCESS_DENIED_ACE(void)
+{
+ /* PACCESS_DENIED_ACE */
+ TEST_TYPE(PACCESS_DENIED_ACE, 4, 4);
+ TEST_TYPE_POINTER(PACCESS_DENIED_ACE, 12, 4);
+}
+
+static void test_pack_PACCESS_TOKEN(void)
+{
+ /* PACCESS_TOKEN */
+ TEST_TYPE(PACCESS_TOKEN, 4, 4);
+}
+
+static void test_pack_PACE_HEADER(void)
+{
+ /* PACE_HEADER */
+ TEST_TYPE(PACE_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PACE_HEADER, 4, 2);
+}
+
+static void test_pack_PACL(void)
+{
+ /* PACL */
+ TEST_TYPE(PACL, 4, 4);
+ TEST_TYPE_POINTER(PACL, 8, 2);
+}
+
+static void test_pack_PACL_REVISION_INFORMATION(void)
+{
+ /* PACL_REVISION_INFORMATION */
+ TEST_TYPE(PACL_REVISION_INFORMATION, 4, 4);
+ TEST_TYPE_POINTER(PACL_REVISION_INFORMATION, 4, 4);
+}
+
+static void test_pack_PACL_SIZE_INFORMATION(void)
+{
+ /* PACL_SIZE_INFORMATION */
+ TEST_TYPE(PACL_SIZE_INFORMATION, 4, 4);
+ TEST_TYPE_POINTER(PACL_SIZE_INFORMATION, 12, 4);
+}
+
+static void test_pack_PCCH(void)
+{
+ /* PCCH */
+ TEST_TYPE(PCCH, 4, 4);
+ TEST_TYPE_POINTER(PCCH, 1, 1);
+}
+
+static void test_pack_PCH(void)
+{
+ /* PCH */
+ TEST_TYPE(PCH, 4, 4);
+ TEST_TYPE_POINTER(PCH, 1, 1);
+}
+
+static void test_pack_PCSTR(void)
+{
+ /* PCSTR */
+ TEST_TYPE(PCSTR, 4, 4);
+ TEST_TYPE_POINTER(PCSTR, 1, 1);
+}
+
+static void test_pack_PCTSTR(void)
+{
+ /* PCTSTR */
+ TEST_TYPE(PCTSTR, 4, 4);
+}
+
+static void test_pack_PCWCH(void)
+{
+ /* PCWCH */
+ TEST_TYPE(PCWCH, 4, 4);
+ TEST_TYPE_POINTER(PCWCH, 2, 2);
+}
+
+static void test_pack_PCWSTR(void)
+{
+ /* PCWSTR */
+ TEST_TYPE(PCWSTR, 4, 4);
+ TEST_TYPE_POINTER(PCWSTR, 2, 2);
+}
+
+static void test_pack_PEXCEPTION_POINTERS(void)
+{
+ /* PEXCEPTION_POINTERS */
+ TEST_TYPE(PEXCEPTION_POINTERS, 4, 4);
+ TEST_TYPE_POINTER(PEXCEPTION_POINTERS, 8, 4);
+}
+
+static void test_pack_PEXCEPTION_RECORD(void)
+{
+ /* PEXCEPTION_RECORD */
+ TEST_TYPE(PEXCEPTION_RECORD, 4, 4);
+ TEST_TYPE_POINTER(PEXCEPTION_RECORD, 80, 4);
+}
+
+static void test_pack_PFLOATING_SAVE_AREA(void)
+{
+ /* PFLOATING_SAVE_AREA */
+ TEST_TYPE(PFLOATING_SAVE_AREA, 4, 4);
+ TEST_TYPE_POINTER(PFLOATING_SAVE_AREA, 112, 4);
+}
+
+static void test_pack_PFPO_DATA(void)
+{
+ /* PFPO_DATA */
+ TEST_TYPE(PFPO_DATA, 4, 4);
+ TEST_TYPE_POINTER(PFPO_DATA, 16, 4);
+}
+
+static void test_pack_PGENERIC_MAPPING(void)
+{
+ /* PGENERIC_MAPPING */
+ TEST_TYPE(PGENERIC_MAPPING, 4, 4);
+ TEST_TYPE_POINTER(PGENERIC_MAPPING, 16, 4);
+}
+
+static void test_pack_PHANDLE(void)
+{
+ /* PHANDLE */
+ TEST_TYPE(PHANDLE, 4, 4);
+ TEST_TYPE_POINTER(PHANDLE, 4, 4);
+}
+
+static void test_pack_PIMAGE_ARCHIVE_MEMBER_HEADER(void)
+{
+ /* PIMAGE_ARCHIVE_MEMBER_HEADER */
+ TEST_TYPE(PIMAGE_ARCHIVE_MEMBER_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_ARCHIVE_MEMBER_HEADER, 60, 1);
+}
+
+static void test_pack_PIMAGE_AUX_SYMBOL(void)
+{
+ /* PIMAGE_AUX_SYMBOL */
+ TEST_TYPE(PIMAGE_AUX_SYMBOL, 4, 4);
+}
+
+static void test_pack_PIMAGE_BASE_RELOCATION(void)
+{
+ /* PIMAGE_BASE_RELOCATION */
+ TEST_TYPE(PIMAGE_BASE_RELOCATION, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_BASE_RELOCATION, 8, 4);
+}
+
+static void test_pack_PIMAGE_BOUND_FORWARDER_REF(void)
+{
+ /* PIMAGE_BOUND_FORWARDER_REF */
+ TEST_TYPE(PIMAGE_BOUND_FORWARDER_REF, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_BOUND_FORWARDER_REF, 8, 4);
+}
+
+static void test_pack_PIMAGE_BOUND_IMPORT_DESCRIPTOR(void)
+{
+ /* PIMAGE_BOUND_IMPORT_DESCRIPTOR */
+ TEST_TYPE(PIMAGE_BOUND_IMPORT_DESCRIPTOR, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_BOUND_IMPORT_DESCRIPTOR, 8, 4);
+}
+
+static void test_pack_PIMAGE_COFF_SYMBOLS_HEADER(void)
+{
+ /* PIMAGE_COFF_SYMBOLS_HEADER */
+ TEST_TYPE(PIMAGE_COFF_SYMBOLS_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_COFF_SYMBOLS_HEADER, 32, 4);
+}
+
+static void test_pack_PIMAGE_DATA_DIRECTORY(void)
+{
+ /* PIMAGE_DATA_DIRECTORY */
+ TEST_TYPE(PIMAGE_DATA_DIRECTORY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_DATA_DIRECTORY, 8, 4);
+}
+
+static void test_pack_PIMAGE_DEBUG_DIRECTORY(void)
+{
+ /* PIMAGE_DEBUG_DIRECTORY */
+ TEST_TYPE(PIMAGE_DEBUG_DIRECTORY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_DEBUG_DIRECTORY, 28, 4);
+}
+
+static void test_pack_PIMAGE_DEBUG_MISC(void)
+{
+ /* PIMAGE_DEBUG_MISC */
+ TEST_TYPE(PIMAGE_DEBUG_MISC, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_DEBUG_MISC, 16, 4);
+}
+
+static void test_pack_PIMAGE_DOS_HEADER(void)
+{
+ /* PIMAGE_DOS_HEADER */
+ TEST_TYPE(PIMAGE_DOS_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_DOS_HEADER, 64, 2);
+}
+
+static void test_pack_PIMAGE_EXPORT_DIRECTORY(void)
+{
+ /* PIMAGE_EXPORT_DIRECTORY */
+ TEST_TYPE(PIMAGE_EXPORT_DIRECTORY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_EXPORT_DIRECTORY, 40, 4);
+}
+
+static void test_pack_PIMAGE_FILE_HEADER(void)
+{
+ /* PIMAGE_FILE_HEADER */
+ TEST_TYPE(PIMAGE_FILE_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_FILE_HEADER, 20, 4);
+}
+
+static void test_pack_PIMAGE_FUNCTION_ENTRY(void)
+{
+ /* PIMAGE_FUNCTION_ENTRY */
+ TEST_TYPE(PIMAGE_FUNCTION_ENTRY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_FUNCTION_ENTRY, 12, 4);
+}
+
+static void test_pack_PIMAGE_IMPORT_BY_NAME(void)
+{
+ /* PIMAGE_IMPORT_BY_NAME */
+ TEST_TYPE(PIMAGE_IMPORT_BY_NAME, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_IMPORT_BY_NAME, 4, 2);
+}
+
+static void test_pack_PIMAGE_IMPORT_DESCRIPTOR(void)
+{
+ /* PIMAGE_IMPORT_DESCRIPTOR */
+ TEST_TYPE(PIMAGE_IMPORT_DESCRIPTOR, 4, 4);
+}
+
+static void test_pack_PIMAGE_LINENUMBER(void)
+{
+ /* PIMAGE_LINENUMBER */
+ TEST_TYPE(PIMAGE_LINENUMBER, 4, 4);
+}
+
+static void test_pack_PIMAGE_LOAD_CONFIG_DIRECTORY(void)
+{
+ /* PIMAGE_LOAD_CONFIG_DIRECTORY */
+ TEST_TYPE(PIMAGE_LOAD_CONFIG_DIRECTORY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_LOAD_CONFIG_DIRECTORY, 72, 4);
+}
+
+static void test_pack_PIMAGE_NT_HEADERS(void)
+{
+ /* PIMAGE_NT_HEADERS */
+ TEST_TYPE(PIMAGE_NT_HEADERS, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_NT_HEADERS, 248, 4);
+}
+
+static void test_pack_PIMAGE_OPTIONAL_HEADER(void)
+{
+ /* PIMAGE_OPTIONAL_HEADER */
+ TEST_TYPE(PIMAGE_OPTIONAL_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_OPTIONAL_HEADER, 224, 4);
+}
+
+static void test_pack_PIMAGE_OS2_HEADER(void)
+{
+ /* PIMAGE_OS2_HEADER */
+ TEST_TYPE(PIMAGE_OS2_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_OS2_HEADER, 64, 2);
+}
+
+static void test_pack_PIMAGE_RELOCATION(void)
+{
+ /* PIMAGE_RELOCATION */
+ TEST_TYPE(PIMAGE_RELOCATION, 4, 4);
+}
+
+static void test_pack_PIMAGE_RESOURCE_DATA_ENTRY(void)
+{
+ /* PIMAGE_RESOURCE_DATA_ENTRY */
+ TEST_TYPE(PIMAGE_RESOURCE_DATA_ENTRY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_RESOURCE_DATA_ENTRY, 16, 4);
+}
+
+static void test_pack_PIMAGE_RESOURCE_DIRECTORY(void)
+{
+ /* PIMAGE_RESOURCE_DIRECTORY */
+ TEST_TYPE(PIMAGE_RESOURCE_DIRECTORY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_RESOURCE_DIRECTORY, 16, 4);
+}
+
+static void test_pack_PIMAGE_RESOURCE_DIRECTORY_ENTRY(void)
+{
+ /* PIMAGE_RESOURCE_DIRECTORY_ENTRY */
+ TEST_TYPE(PIMAGE_RESOURCE_DIRECTORY_ENTRY, 4, 4);
+}
+
+static void test_pack_PIMAGE_RESOURCE_DIRECTORY_STRING(void)
+{
+ /* PIMAGE_RESOURCE_DIRECTORY_STRING */
+ TEST_TYPE(PIMAGE_RESOURCE_DIRECTORY_STRING, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_RESOURCE_DIRECTORY_STRING, 4, 2);
+}
+
+static void test_pack_PIMAGE_RESOURCE_DIR_STRING_U(void)
+{
+ /* PIMAGE_RESOURCE_DIR_STRING_U */
+ TEST_TYPE(PIMAGE_RESOURCE_DIR_STRING_U, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_RESOURCE_DIR_STRING_U, 4, 2);
+}
+
+static void test_pack_PIMAGE_SECTION_HEADER(void)
+{
+ /* PIMAGE_SECTION_HEADER */
+ TEST_TYPE(PIMAGE_SECTION_HEADER, 4, 4);
+}
+
+static void test_pack_PIMAGE_SEPARATE_DEBUG_HEADER(void)
+{
+ /* PIMAGE_SEPARATE_DEBUG_HEADER */
+ TEST_TYPE(PIMAGE_SEPARATE_DEBUG_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_SEPARATE_DEBUG_HEADER, 48, 4);
+}
+
+static void test_pack_PIMAGE_SYMBOL(void)
+{
+ /* PIMAGE_SYMBOL */
+ TEST_TYPE(PIMAGE_SYMBOL, 4, 4);
+}
+
+static void test_pack_PIMAGE_THUNK_DATA(void)
+{
+ /* PIMAGE_THUNK_DATA */
+ TEST_TYPE(PIMAGE_THUNK_DATA, 4, 4);
+}
+
+static void test_pack_PIMAGE_TLS_CALLBACK(void)
+{
+ /* PIMAGE_TLS_CALLBACK */
+ TEST_TYPE(PIMAGE_TLS_CALLBACK, 4, 4);
+}
+
+static void test_pack_PIMAGE_TLS_DIRECTORY(void)
+{
+ /* PIMAGE_TLS_DIRECTORY */
+ TEST_TYPE(PIMAGE_TLS_DIRECTORY, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_TLS_DIRECTORY, 24, 4);
+}
+
+static void test_pack_PIMAGE_VXD_HEADER(void)
+{
+ /* PIMAGE_VXD_HEADER */
+ TEST_TYPE(PIMAGE_VXD_HEADER, 4, 4);
+ TEST_TYPE_POINTER(PIMAGE_VXD_HEADER, 196, 2);
+}
+
+static void test_pack_PIO_COUNTERS(void)
+{
+ /* PIO_COUNTERS */
+ TEST_TYPE(PIO_COUNTERS, 4, 4);
+ TEST_TYPE_POINTER(PIO_COUNTERS, 48, 8);
+}
+
+static void test_pack_PISECURITY_DESCRIPTOR(void)
+{
+ /* PISECURITY_DESCRIPTOR */
+ TEST_TYPE(PISECURITY_DESCRIPTOR, 4, 4);
+ TEST_TYPE_POINTER(PISECURITY_DESCRIPTOR, 20, 4);
+}
+
+static void test_pack_PISECURITY_DESCRIPTOR_RELATIVE(void)
+{
+ /* PISECURITY_DESCRIPTOR_RELATIVE */
+ TEST_TYPE(PISECURITY_DESCRIPTOR_RELATIVE, 4, 4);
+ TEST_TYPE_POINTER(PISECURITY_DESCRIPTOR_RELATIVE, 20, 4);
+}
+
+static void test_pack_PISID(void)
+{
+ /* PISID */
+ TEST_TYPE(PISID, 4, 4);
+ TEST_TYPE_POINTER(PISID, 12, 4);
+}
+
+static void test_pack_PLARGE_INTEGER(void)
+{
+ /* PLARGE_INTEGER */
+ TEST_TYPE(PLARGE_INTEGER, 4, 4);
+}
+
+static void test_pack_PLIST_ENTRY(void)
+{
+ /* PLIST_ENTRY */
+ TEST_TYPE(PLIST_ENTRY, 4, 4);
+ TEST_TYPE_POINTER(PLIST_ENTRY, 8, 4);
+}
+
+static void test_pack_PLUID(void)
+{
+ /* PLUID */
+ TEST_TYPE(PLUID, 4, 4);
+ TEST_TYPE_POINTER(PLUID, 8, 4);
+}
+
+static void test_pack_PLUID_AND_ATTRIBUTES(void)
+{
+ /* PLUID_AND_ATTRIBUTES */
+ TEST_TYPE(PLUID_AND_ATTRIBUTES, 4, 4);
+ TEST_TYPE_POINTER(PLUID_AND_ATTRIBUTES, 12, 4);
+}
+
+static void test_pack_PMEMORY_BASIC_INFORMATION(void)
+{
+ /* PMEMORY_BASIC_INFORMATION */
+ TEST_TYPE(PMEMORY_BASIC_INFORMATION, 4, 4);
+ TEST_TYPE_POINTER(PMEMORY_BASIC_INFORMATION, 28, 4);
+}
+
+static void test_pack_PMESSAGE_RESOURCE_BLOCK(void)
+{
+ /* PMESSAGE_RESOURCE_BLOCK */
+ TEST_TYPE(PMESSAGE_RESOURCE_BLOCK, 4, 4);
+ TEST_TYPE_POINTER(PMESSAGE_RESOURCE_BLOCK, 12, 4);
+}
+
+static void test_pack_PMESSAGE_RESOURCE_DATA(void)
+{
+ /* PMESSAGE_RESOURCE_DATA */
+ TEST_TYPE(PMESSAGE_RESOURCE_DATA, 4, 4);
+ TEST_TYPE_POINTER(PMESSAGE_RESOURCE_DATA, 16, 4);
+}
+
+static void test_pack_PMESSAGE_RESOURCE_ENTRY(void)
+{
+ /* PMESSAGE_RESOURCE_ENTRY */
+ TEST_TYPE(PMESSAGE_RESOURCE_ENTRY, 4, 4);
+ TEST_TYPE_POINTER(PMESSAGE_RESOURCE_ENTRY, 6, 2);
+}
+
+static void test_pack_PNT_TIB(void)
+{
+ /* PNT_TIB */
+ TEST_TYPE(PNT_TIB, 4, 4);
+}
+
+static void test_pack_POBJECT_TYPE_LIST(void)
+{
+ /* POBJECT_TYPE_LIST */
+ TEST_TYPE(POBJECT_TYPE_LIST, 4, 4);
+ TEST_TYPE_POINTER(POBJECT_TYPE_LIST, 8, 4);
+}
+
+static void test_pack_PPRIVILEGE_SET(void)
+{
+ /* PPRIVILEGE_SET */
+ TEST_TYPE(PPRIVILEGE_SET, 4, 4);
+ TEST_TYPE_POINTER(PPRIVILEGE_SET, 20, 4);
+}
+
+static void test_pack_PRIVILEGE_SET(void)
+{
+ /* PRIVILEGE_SET (pack 4) */
+ TEST_TYPE(PRIVILEGE_SET, 20, 4);
+ TEST_FIELD(PRIVILEGE_SET, DWORD, PrivilegeCount, 0, 4, 4);
+ TEST_FIELD(PRIVILEGE_SET, DWORD, Control, 4, 4, 4);
+ TEST_FIELD(PRIVILEGE_SET, LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY], Privilege, 8, 12, 4);
+}
+
+static void test_pack_PRLIST_ENTRY(void)
+{
+ /* PRLIST_ENTRY */
+ TEST_TYPE(PRLIST_ENTRY, 4, 4);
+ TEST_TYPE_POINTER(PRLIST_ENTRY, 8, 4);
+}
+
+static void test_pack_PRTL_CRITICAL_SECTION(void)
+{
+ /* PRTL_CRITICAL_SECTION */
+ TEST_TYPE(PRTL_CRITICAL_SECTION, 4, 4);
+ TEST_TYPE_POINTER(PRTL_CRITICAL_SECTION, 24, 4);
+}
+
+static void test_pack_PRTL_CRITICAL_SECTION_DEBUG(void)
+{
+ /* PRTL_CRITICAL_SECTION_DEBUG */
+ TEST_TYPE(PRTL_CRITICAL_SECTION_DEBUG, 4, 4);
+ TEST_TYPE_POINTER(PRTL_CRITICAL_SECTION_DEBUG, 32, 4);
+}
+
+static void test_pack_PRTL_OSVERSIONINFOEXW(void)
+{
+ /* PRTL_OSVERSIONINFOEXW */
+ TEST_TYPE(PRTL_OSVERSIONINFOEXW, 4, 4);
+ TEST_TYPE_POINTER(PRTL_OSVERSIONINFOEXW, 284, 4);
+}
+
+static void test_pack_PRTL_OSVERSIONINFOW(void)
+{
+ /* PRTL_OSVERSIONINFOW */
+ TEST_TYPE(PRTL_OSVERSIONINFOW, 4, 4);
+ TEST_TYPE_POINTER(PRTL_OSVERSIONINFOW, 276, 4);
+}
+
+static void test_pack_PRTL_RESOURCE_DEBUG(void)
+{
+ /* PRTL_RESOURCE_DEBUG */
+ TEST_TYPE(PRTL_RESOURCE_DEBUG, 4, 4);
+ TEST_TYPE_POINTER(PRTL_RESOURCE_DEBUG, 32, 4);
+}
+
+static void test_pack_PSECURITY_DESCRIPTOR(void)
+{
+ /* PSECURITY_DESCRIPTOR */
+ TEST_TYPE(PSECURITY_DESCRIPTOR, 4, 4);
+}
+
+static void test_pack_PSECURITY_QUALITY_OF_SERVICE(void)
+{
+ /* PSECURITY_QUALITY_OF_SERVICE */
+ TEST_TYPE(PSECURITY_QUALITY_OF_SERVICE, 4, 4);
+}
+
+static void test_pack_PSID(void)
+{
+ /* PSID */
+ TEST_TYPE(PSID, 4, 4);
+}
+
+static void test_pack_PSID_IDENTIFIER_AUTHORITY(void)
+{
+ /* PSID_IDENTIFIER_AUTHORITY */
+ TEST_TYPE(PSID_IDENTIFIER_AUTHORITY, 4, 4);
+ TEST_TYPE_POINTER(PSID_IDENTIFIER_AUTHORITY, 6, 1);
+}
+
+static void test_pack_PSINGLE_LIST_ENTRY(void)
+{
+ /* PSINGLE_LIST_ENTRY */
+ TEST_TYPE(PSINGLE_LIST_ENTRY, 4, 4);
+ TEST_TYPE_POINTER(PSINGLE_LIST_ENTRY, 4, 4);
+}
+
+static void test_pack_PSTR(void)
+{
+ /* PSTR */
+ TEST_TYPE(PSTR, 4, 4);
+ TEST_TYPE_POINTER(PSTR, 1, 1);
+}
+
+static void test_pack_PSYSTEM_ALARM_ACE(void)
+{
+ /* PSYSTEM_ALARM_ACE */
+ TEST_TYPE(PSYSTEM_ALARM_ACE, 4, 4);
+ TEST_TYPE_POINTER(PSYSTEM_ALARM_ACE, 12, 4);
+}
+
+static void test_pack_PSYSTEM_AUDIT_ACE(void)
+{
+ /* PSYSTEM_AUDIT_ACE */
+ TEST_TYPE(PSYSTEM_AUDIT_ACE, 4, 4);
+ TEST_TYPE_POINTER(PSYSTEM_AUDIT_ACE, 12, 4);
+}
+
+static void test_pack_PTOKEN_GROUPS(void)
+{
+ /* PTOKEN_GROUPS */
+ TEST_TYPE(PTOKEN_GROUPS, 4, 4);
+ TEST_TYPE_POINTER(PTOKEN_GROUPS, 12, 4);
+}
+
+static void test_pack_PTOKEN_PRIVILEGES(void)
+{
+ /* PTOKEN_PRIVILEGES */
+ TEST_TYPE(PTOKEN_PRIVILEGES, 4, 4);
+ TEST_TYPE_POINTER(PTOKEN_PRIVILEGES, 16, 4);
+}
+
+static void test_pack_PTOKEN_USER(void)
+{
+ /* PTOKEN_USER */
+ TEST_TYPE(PTOKEN_USER, 4, 4);
+ TEST_TYPE_POINTER(PTOKEN_USER, 8, 4);
+}
+
+static void test_pack_PTOP_LEVEL_EXCEPTION_FILTER(void)
+{
+ /* PTOP_LEVEL_EXCEPTION_FILTER */
+ TEST_TYPE(PTOP_LEVEL_EXCEPTION_FILTER, 4, 4);
+}
+
+static void test_pack_PTSTR(void)
+{
+ /* PTSTR */
+ TEST_TYPE(PTSTR, 4, 4);
+}
+
+static void test_pack_PULARGE_INTEGER(void)
+{
+ /* PULARGE_INTEGER */
+ TEST_TYPE(PULARGE_INTEGER, 4, 4);
+}
+
+static void test_pack_PVECTORED_EXCEPTION_HANDLER(void)
+{
+ /* PVECTORED_EXCEPTION_HANDLER */
+ TEST_TYPE(PVECTORED_EXCEPTION_HANDLER, 4, 4);
+}
+
+static void test_pack_PVOID(void)
+{
+ /* PVOID */
+ TEST_TYPE(PVOID, 4, 4);
+}
+
+static void test_pack_PWCH(void)
+{
+ /* PWCH */
+ TEST_TYPE(PWCH, 4, 4);
+ TEST_TYPE_POINTER(PWCH, 2, 2);
+}
+
+static void test_pack_PWSTR(void)
+{
+ /* PWSTR */
+ TEST_TYPE(PWSTR, 4, 4);
+ TEST_TYPE_POINTER(PWSTR, 2, 2);
+}
+
+static void test_pack_RTL_CRITICAL_SECTION(void)
+{
+ /* RTL_CRITICAL_SECTION (pack 4) */
+ TEST_TYPE(RTL_CRITICAL_SECTION, 24, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION, PRTL_CRITICAL_SECTION_DEBUG, DebugInfo, 0, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION, LONG, LockCount, 4, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION, LONG, RecursionCount, 8, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION, HANDLE, OwningThread, 12, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION, HANDLE, LockSemaphore, 16, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION, ULONG_PTR, SpinCount, 20, 4, 4);
+}
+
+static void test_pack_RTL_CRITICAL_SECTION_DEBUG(void)
+{
+ /* RTL_CRITICAL_SECTION_DEBUG (pack 4) */
+ TEST_TYPE(RTL_CRITICAL_SECTION_DEBUG, 32, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION_DEBUG, WORD, Type, 0, 2, 2);
+ TEST_FIELD(RTL_CRITICAL_SECTION_DEBUG, WORD, CreatorBackTraceIndex, 2, 2, 2);
+ TEST_FIELD(RTL_CRITICAL_SECTION_DEBUG, struct _RTL_CRITICAL_SECTION *, CriticalSection, 4, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION_DEBUG, LIST_ENTRY, ProcessLocksList, 8, 8, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION_DEBUG, DWORD, EntryCount, 16, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION_DEBUG, DWORD, ContentionCount, 20, 4, 4);
+ TEST_FIELD(RTL_CRITICAL_SECTION_DEBUG, DWORD[ 2 ], Spare, 24, 8, 4);
+}
+
+static void test_pack_RTL_OSVERSIONINFOEXW(void)
+{
+ /* RTL_OSVERSIONINFOEXW (pack 4) */
+ TEST_TYPE(RTL_OSVERSIONINFOEXW, 284, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, DWORD, dwOSVersionInfoSize, 0, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, DWORD, dwMajorVersion, 4, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, DWORD, dwMinorVersion, 8, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, DWORD, dwBuildNumber, 12, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, DWORD, dwPlatformId, 16, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, WCHAR[128], szCSDVersion, 20, 256, 2);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, WORD, wServicePackMajor, 276, 2, 2);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, WORD, wServicePackMinor, 278, 2, 2);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, WORD, wSuiteMask, 280, 2, 2);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, BYTE, wProductType, 282, 1, 1);
+ TEST_FIELD(RTL_OSVERSIONINFOEXW, BYTE, wReserved, 283, 1, 1);
+}
+
+static void test_pack_RTL_OSVERSIONINFOW(void)
+{
+ /* RTL_OSVERSIONINFOW (pack 4) */
+ TEST_TYPE(RTL_OSVERSIONINFOW, 276, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOW, DWORD, dwOSVersionInfoSize, 0, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOW, DWORD, dwMajorVersion, 4, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOW, DWORD, dwMinorVersion, 8, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOW, DWORD, dwBuildNumber, 12, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOW, DWORD, dwPlatformId, 16, 4, 4);
+ TEST_FIELD(RTL_OSVERSIONINFOW, WCHAR[128], szCSDVersion, 20, 256, 2);
+}
+
+static void test_pack_RTL_RESOURCE_DEBUG(void)
+{
+ /* RTL_RESOURCE_DEBUG (pack 4) */
+ TEST_TYPE(RTL_RESOURCE_DEBUG, 32, 4);
+ TEST_FIELD(RTL_RESOURCE_DEBUG, WORD, Type, 0, 2, 2);
+ TEST_FIELD(RTL_RESOURCE_DEBUG, WORD, CreatorBackTraceIndex, 2, 2, 2);
+ TEST_FIELD(RTL_RESOURCE_DEBUG, struct _RTL_CRITICAL_SECTION *, CriticalSection, 4, 4, 4);
+ TEST_FIELD(RTL_RESOURCE_DEBUG, LIST_ENTRY, ProcessLocksList, 8, 8, 4);
+ TEST_FIELD(RTL_RESOURCE_DEBUG, DWORD, EntryCount, 16, 4, 4);
+ TEST_FIELD(RTL_RESOURCE_DEBUG, DWORD, ContentionCount, 20, 4, 4);
+ TEST_FIELD(RTL_RESOURCE_DEBUG, DWORD[ 2 ], Spare, 24, 8, 4);
+}
+
+static void test_pack_SECURITY_CONTEXT_TRACKING_MODE(void)
+{
+ /* SECURITY_CONTEXT_TRACKING_MODE */
+ TEST_TYPE(SECURITY_CONTEXT_TRACKING_MODE, 1, 1);
+}
+
+static void test_pack_SECURITY_DESCRIPTOR(void)
+{
+ /* SECURITY_DESCRIPTOR (pack 4) */
+ TEST_TYPE(SECURITY_DESCRIPTOR, 20, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR, BYTE, Revision, 0, 1, 1);
+ TEST_FIELD(SECURITY_DESCRIPTOR, BYTE, Sbz1, 1, 1, 1);
+ TEST_FIELD(SECURITY_DESCRIPTOR, SECURITY_DESCRIPTOR_CONTROL, Control, 2, 2, 2);
+ TEST_FIELD(SECURITY_DESCRIPTOR, PSID, Owner, 4, 4, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR, PSID, Group, 8, 4, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR, PACL, Sacl, 12, 4, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR, PACL, Dacl, 16, 4, 4);
+}
+
+static void test_pack_SECURITY_DESCRIPTOR_CONTROL(void)
+{
+ /* SECURITY_DESCRIPTOR_CONTROL */
+ TEST_TYPE(SECURITY_DESCRIPTOR_CONTROL, 2, 2);
+ TEST_TYPE_UNSIGNED(SECURITY_DESCRIPTOR_CONTROL);
+}
+
+static void test_pack_SECURITY_DESCRIPTOR_RELATIVE(void)
+{
+ /* SECURITY_DESCRIPTOR_RELATIVE (pack 4) */
+ TEST_TYPE(SECURITY_DESCRIPTOR_RELATIVE, 20, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR_RELATIVE, BYTE, Revision, 0, 1, 1);
+ TEST_FIELD(SECURITY_DESCRIPTOR_RELATIVE, BYTE, Sbz1, 1, 1, 1);
+ TEST_FIELD(SECURITY_DESCRIPTOR_RELATIVE, SECURITY_DESCRIPTOR_CONTROL, Control, 2, 2, 2);
+ TEST_FIELD(SECURITY_DESCRIPTOR_RELATIVE, DWORD, Owner, 4, 4, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR_RELATIVE, DWORD, Group, 8, 4, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR_RELATIVE, DWORD, Sacl, 12, 4, 4);
+ TEST_FIELD(SECURITY_DESCRIPTOR_RELATIVE, DWORD, Dacl, 16, 4, 4);
+}
+
+static void test_pack_SECURITY_INFORMATION(void)
+{
+ /* SECURITY_INFORMATION */
+ TEST_TYPE(SECURITY_INFORMATION, 4, 4);
+ TEST_TYPE_UNSIGNED(SECURITY_INFORMATION);
+}
+
+static void test_pack_SECURITY_QUALITY_OF_SERVICE(void)
+{
+ /* SECURITY_QUALITY_OF_SERVICE (pack 4) */
+ TEST_FIELD(SECURITY_QUALITY_OF_SERVICE, DWORD, Length, 0, 4, 4);
+}
+
+static void test_pack_SHORT(void)
+{
+ /* SHORT */
+ TEST_TYPE(SHORT, 2, 2);
+ TEST_TYPE_SIGNED(SHORT);
+}
+
+static void test_pack_SID(void)
+{
+ /* SID (pack 4) */
+ TEST_TYPE(SID, 12, 4);
+ TEST_FIELD(SID, BYTE, Revision, 0, 1, 1);
+ TEST_FIELD(SID, BYTE, SubAuthorityCount, 1, 1, 1);
+ TEST_FIELD(SID, SID_IDENTIFIER_AUTHORITY, IdentifierAuthority, 2, 6, 1);
+ TEST_FIELD(SID, DWORD[1], SubAuthority, 8, 4, 4);
+}
+
+static void test_pack_SID_AND_ATTRIBUTES(void)
+{
+ /* SID_AND_ATTRIBUTES (pack 4) */
+ TEST_TYPE(SID_AND_ATTRIBUTES, 8, 4);
+ TEST_FIELD(SID_AND_ATTRIBUTES, PSID, Sid, 0, 4, 4);
+ TEST_FIELD(SID_AND_ATTRIBUTES, DWORD, Attributes, 4, 4, 4);
+}
+
+static void test_pack_SID_IDENTIFIER_AUTHORITY(void)
+{
+ /* SID_IDENTIFIER_AUTHORITY (pack 4) */
+ TEST_TYPE(SID_IDENTIFIER_AUTHORITY, 6, 1);
+ TEST_FIELD(SID_IDENTIFIER_AUTHORITY, BYTE[6], Value, 0, 6, 1);
+}
+
+static void test_pack_SINGLE_LIST_ENTRY(void)
+{
+ /* SINGLE_LIST_ENTRY (pack 4) */
+ TEST_TYPE(SINGLE_LIST_ENTRY, 4, 4);
+ TEST_FIELD(SINGLE_LIST_ENTRY, struct _SINGLE_LIST_ENTRY *, Next, 0, 4, 4);
+}
+
+static void test_pack_SYSTEM_ALARM_ACE(void)
+{
+ /* SYSTEM_ALARM_ACE (pack 4) */
+ TEST_TYPE(SYSTEM_ALARM_ACE, 12, 4);
+ TEST_FIELD(SYSTEM_ALARM_ACE, ACE_HEADER, Header, 0, 4, 2);
+ TEST_FIELD(SYSTEM_ALARM_ACE, DWORD, Mask, 4, 4, 4);
+ TEST_FIELD(SYSTEM_ALARM_ACE, DWORD, SidStart, 8, 4, 4);
+}
+
+static void test_pack_SYSTEM_AUDIT_ACE(void)
+{
+ /* SYSTEM_AUDIT_ACE (pack 4) */
+ TEST_TYPE(SYSTEM_AUDIT_ACE, 12, 4);
+ TEST_FIELD(SYSTEM_AUDIT_ACE, ACE_HEADER, Header, 0, 4, 2);
+ TEST_FIELD(SYSTEM_AUDIT_ACE, DWORD, Mask, 4, 4, 4);
+ TEST_FIELD(SYSTEM_AUDIT_ACE, DWORD, SidStart, 8, 4, 4);
+}
+
+static void test_pack_TCHAR(void)
+{
+ /* TCHAR */
+ TEST_TYPE(TCHAR, 1, 1);
+}
+
+static void test_pack_TOKEN_DEFAULT_DACL(void)
+{
+ /* TOKEN_DEFAULT_DACL (pack 4) */
+ TEST_TYPE(TOKEN_DEFAULT_DACL, 4, 4);
+ TEST_FIELD(TOKEN_DEFAULT_DACL, PACL, DefaultDacl, 0, 4, 4);
+}
+
+static void test_pack_TOKEN_GROUPS(void)
+{
+ /* TOKEN_GROUPS (pack 4) */
+ TEST_TYPE(TOKEN_GROUPS, 12, 4);
+ TEST_FIELD(TOKEN_GROUPS, DWORD, GroupCount, 0, 4, 4);
+ TEST_FIELD(TOKEN_GROUPS, SID_AND_ATTRIBUTES[ANYSIZE_ARRAY], Groups, 4, 8, 4);
+}
+
+static void test_pack_TOKEN_OWNER(void)
+{
+ /* TOKEN_OWNER (pack 4) */
+ TEST_TYPE(TOKEN_OWNER, 4, 4);
+ TEST_FIELD(TOKEN_OWNER, PSID, Owner, 0, 4, 4);
+}
+
+static void test_pack_TOKEN_PRIMARY_GROUP(void)
+{
+ /* TOKEN_PRIMARY_GROUP (pack 4) */
+ TEST_TYPE(TOKEN_PRIMARY_GROUP, 4, 4);
+ TEST_FIELD(TOKEN_PRIMARY_GROUP, PSID, PrimaryGroup, 0, 4, 4);
+}
+
+static void test_pack_TOKEN_PRIVILEGES(void)
+{
+ /* TOKEN_PRIVILEGES (pack 4) */
+ TEST_TYPE(TOKEN_PRIVILEGES, 16, 4);
+ TEST_FIELD(TOKEN_PRIVILEGES, DWORD, PrivilegeCount, 0, 4, 4);
+ TEST_FIELD(TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY], Privileges, 4, 12, 4);
+}
+
+static void test_pack_TOKEN_SOURCE(void)
+{
+ /* TOKEN_SOURCE (pack 4) */
+ TEST_TYPE(TOKEN_SOURCE, 16, 4);
+ TEST_FIELD(TOKEN_SOURCE, char[TOKEN_SOURCE_LENGTH], SourceName, 0, 8, 1);
+ TEST_FIELD(TOKEN_SOURCE, LUID, SourceIdentifier, 8, 8, 4);
+}
+
+static void test_pack_TOKEN_STATISTICS(void)
+{
+ /* TOKEN_STATISTICS (pack 4) */
+ TEST_FIELD(TOKEN_STATISTICS, LUID, TokenId, 0, 8, 4);
+ TEST_FIELD(TOKEN_STATISTICS, LUID, AuthenticationId, 8, 8, 4);
+ TEST_FIELD(TOKEN_STATISTICS, LARGE_INTEGER, ExpirationTime, 16, 8, 4);
+}
+
+static void test_pack_TOKEN_USER(void)
+{
+ /* TOKEN_USER (pack 4) */
+ TEST_TYPE(TOKEN_USER, 8, 4);
+ TEST_FIELD(TOKEN_USER, SID_AND_ATTRIBUTES, User, 0, 8, 4);
+}
+
+static void test_pack_ULARGE_INTEGER(void)
+{
+ /* ULARGE_INTEGER (pack 4) */
+}
+
+static void test_pack_ULONGLONG(void)
+{
+ /* ULONGLONG */
+ TEST_TYPE(ULONGLONG, 8, 8);
+ TEST_TYPE_UNSIGNED(ULONGLONG);
+}
+
+static void test_pack_WAITORTIMERCALLBACKFUNC(void)
+{
+ /* WAITORTIMERCALLBACKFUNC */
+ TEST_TYPE(WAITORTIMERCALLBACKFUNC, 4, 4);
+}
+
+static void test_pack_WCHAR(void)
+{
+ /* WCHAR */
+ TEST_TYPE(WCHAR, 2, 2);
+ TEST_TYPE_UNSIGNED(WCHAR);
+}
+
+static void test_pack_ATOM(void)
+{
+ /* ATOM */
+ TEST_TYPE(ATOM, 2, 2);
+ TEST_TYPE_UNSIGNED(ATOM);
+}
+
+static void test_pack_BOOL(void)
+{
+ /* BOOL */
+ TEST_TYPE(BOOL, 4, 4);
+ TEST_TYPE_SIGNED(BOOL);
+}
+
+static void test_pack_BYTE(void)
+{
+ /* BYTE */
+ TEST_TYPE(BYTE, 1, 1);
+ TEST_TYPE_UNSIGNED(BYTE);
+}
+
+static void test_pack_COLORREF(void)
+{
+ /* COLORREF */
+ TEST_TYPE(COLORREF, 4, 4);
+ TEST_TYPE_UNSIGNED(COLORREF);
+}
+
+static void test_pack_DWORD(void)
+{
+ /* DWORD */
+ TEST_TYPE(DWORD, 4, 4);
+ TEST_TYPE_UNSIGNED(DWORD);
+}
+
+static void test_pack_FARPROC(void)
+{
+ /* FARPROC */
+ TEST_TYPE(FARPROC, 4, 4);
+}
+
+static void test_pack_FLOAT(void)
+{
+ /* FLOAT */
+ TEST_TYPE(FLOAT, 4, 4);
+}
+
+static void test_pack_GLOBALHANDLE(void)
+{
+ /* GLOBALHANDLE */
+ TEST_TYPE(GLOBALHANDLE, 4, 4);
+}
+
+static void test_pack_HCURSOR(void)
+{
+ /* HCURSOR */
+ TEST_TYPE(HCURSOR, 4, 4);
+ TEST_TYPE_UNSIGNED(HCURSOR);
+}
+
+static void test_pack_HFILE(void)
+{
+ /* HFILE */
+ TEST_TYPE(HFILE, 4, 4);
+ TEST_TYPE_SIGNED(HFILE);
+}
+
+static void test_pack_HGDIOBJ(void)
+{
+ /* HGDIOBJ */
+ TEST_TYPE(HGDIOBJ, 4, 4);
+}
+
+static void test_pack_HGLOBAL(void)
+{
+ /* HGLOBAL */
+ TEST_TYPE(HGLOBAL, 4, 4);
+}
+
+static void test_pack_HLOCAL(void)
+{
+ /* HLOCAL */
+ TEST_TYPE(HLOCAL, 4, 4);
+}
+
+static void test_pack_HMODULE(void)
+{
+ /* HMODULE */
+ TEST_TYPE(HMODULE, 4, 4);
+ TEST_TYPE_UNSIGNED(HMODULE);
+}
+
+static void test_pack_INT(void)
+{
+ /* INT */
+ TEST_TYPE(INT, 4, 4);
+ TEST_TYPE_SIGNED(INT);
+}
+
+static void test_pack_LOCALHANDLE(void)
+{
+ /* LOCALHANDLE */
+ TEST_TYPE(LOCALHANDLE, 4, 4);
+}
+
+static void test_pack_LPARAM(void)
+{
+ /* LPARAM */
+ TEST_TYPE(LPARAM, 4, 4);
+}
+
+static void test_pack_LPCRECT(void)
+{
+ /* LPCRECT */
+ TEST_TYPE(LPCRECT, 4, 4);
+ TEST_TYPE_POINTER(LPCRECT, 16, 4);
+}
+
+static void test_pack_LPCRECTL(void)
+{
+ /* LPCRECTL */
+ TEST_TYPE(LPCRECTL, 4, 4);
+ TEST_TYPE_POINTER(LPCRECTL, 16, 4);
+}
+
+static void test_pack_LPCVOID(void)
+{
+ /* LPCVOID */
+ TEST_TYPE(LPCVOID, 4, 4);
+}
+
+static void test_pack_LPPOINT(void)
+{
+ /* LPPOINT */
+ TEST_TYPE(LPPOINT, 4, 4);
+ TEST_TYPE_POINTER(LPPOINT, 8, 4);
+}
+
+static void test_pack_LPPOINTS(void)
+{
+ /* LPPOINTS */
+ TEST_TYPE(LPPOINTS, 4, 4);
+ TEST_TYPE_POINTER(LPPOINTS, 4, 2);
+}
+
+static void test_pack_LPRECT(void)
+{
+ /* LPRECT */
+ TEST_TYPE(LPRECT, 4, 4);
+ TEST_TYPE_POINTER(LPRECT, 16, 4);
+}
+
+static void test_pack_LPRECTL(void)
+{
+ /* LPRECTL */
+ TEST_TYPE(LPRECTL, 4, 4);
+ TEST_TYPE_POINTER(LPRECTL, 16, 4);
+}
+
+static void test_pack_LPSIZE(void)
+{
+ /* LPSIZE */
+ TEST_TYPE(LPSIZE, 4, 4);
+ TEST_TYPE_POINTER(LPSIZE, 8, 4);
+}
+
+static void test_pack_LRESULT(void)
+{
+ /* LRESULT */
+ TEST_TYPE(LRESULT, 4, 4);
+}
+
+static void test_pack_POINT(void)
+{
+ /* POINT (pack 4) */
+ TEST_TYPE(POINT, 8, 4);
+ TEST_FIELD(POINT, LONG, x, 0, 4, 4);
+ TEST_FIELD(POINT, LONG, y, 4, 4, 4);
+}
+
+static void test_pack_POINTL(void)
+{
+ /* POINTL (pack 4) */
+ TEST_TYPE(POINTL, 8, 4);
+ TEST_FIELD(POINTL, LONG, x, 0, 4, 4);
+ TEST_FIELD(POINTL, LONG, y, 4, 4, 4);
+}
+
+static void test_pack_POINTS(void)
+{
+ /* POINTS (pack 4) */
+ TEST_TYPE(POINTS, 4, 2);
+ TEST_FIELD(POINTS, SHORT, x, 0, 2, 2);
+ TEST_FIELD(POINTS, SHORT, y, 2, 2, 2);
+}
+
+static void test_pack_PPOINT(void)
+{
+ /* PPOINT */
+ TEST_TYPE(PPOINT, 4, 4);
+ TEST_TYPE_POINTER(PPOINT, 8, 4);
+}
+
+static void test_pack_PPOINTL(void)
+{
+ /* PPOINTL */
+ TEST_TYPE(PPOINTL, 4, 4);
+ TEST_TYPE_POINTER(PPOINTL, 8, 4);
+}
+
+static void test_pack_PPOINTS(void)
+{
+ /* PPOINTS */
+ TEST_TYPE(PPOINTS, 4, 4);
+ TEST_TYPE_POINTER(PPOINTS, 4, 2);
+}
+
+static void test_pack_PRECT(void)
+{
+ /* PRECT */
+ TEST_TYPE(PRECT, 4, 4);
+ TEST_TYPE_POINTER(PRECT, 16, 4);
+}
+
+static void test_pack_PRECTL(void)
+{
+ /* PRECTL */
+ TEST_TYPE(PRECTL, 4, 4);
+ TEST_TYPE_POINTER(PRECTL, 16, 4);
+}
+
+static void test_pack_PROC(void)
+{
+ /* PROC */
+ TEST_TYPE(PROC, 4, 4);
+}
+
+static void test_pack_PSIZE(void)
+{
+ /* PSIZE */
+ TEST_TYPE(PSIZE, 4, 4);
+ TEST_TYPE_POINTER(PSIZE, 8, 4);
+}
+
+static void test_pack_PSZ(void)
+{
+ /* PSZ */
+ TEST_TYPE(PSZ, 4, 4);
+}
+
+static void test_pack_RECT(void)
+{
+ /* RECT (pack 4) */
+ TEST_TYPE(RECT, 16, 4);
+ TEST_FIELD(RECT, LONG, left, 0, 4, 4);
+ TEST_FIELD(RECT, LONG, top, 4, 4, 4);
+ TEST_FIELD(RECT, LONG, right, 8, 4, 4);
+ TEST_FIELD(RECT, LONG, bottom, 12, 4, 4);
+}
+
+static void test_pack_RECTL(void)
+{
+ /* RECTL (pack 4) */
+ TEST_TYPE(RECTL, 16, 4);
+ TEST_FIELD(RECTL, LONG, left, 0, 4, 4);
+ TEST_FIELD(RECTL, LONG, top, 4, 4, 4);
+ TEST_FIELD(RECTL, LONG, right, 8, 4, 4);
+ TEST_FIELD(RECTL, LONG, bottom, 12, 4, 4);
+}
+
+static void test_pack_SIZE(void)
+{
+ /* SIZE (pack 4) */
+ TEST_TYPE(SIZE, 8, 4);
+ TEST_FIELD(SIZE, LONG, cx, 0, 4, 4);
+ TEST_FIELD(SIZE, LONG, cy, 4, 4, 4);
+}
+
+static void test_pack_SIZEL(void)
+{
+ /* SIZEL */
+ TEST_TYPE(SIZEL, 8, 4);
+}
+
+static void test_pack_UCHAR(void)
+{
+ /* UCHAR */
+ TEST_TYPE(UCHAR, 1, 1);
+ TEST_TYPE_UNSIGNED(UCHAR);
+}
+
+static void test_pack_UINT(void)
+{
+ /* UINT */
+ TEST_TYPE(UINT, 4, 4);
+ TEST_TYPE_UNSIGNED(UINT);
+}
+
+static void test_pack_ULONG(void)
+{
+ /* ULONG */
+ TEST_TYPE(ULONG, 4, 4);
+ TEST_TYPE_UNSIGNED(ULONG);
+}
+
+static void test_pack_USHORT(void)
+{
+ /* USHORT */
+ TEST_TYPE(USHORT, 2, 2);
+ TEST_TYPE_UNSIGNED(USHORT);
+}
+
+static void test_pack_WORD(void)
+{
+ /* WORD */
+ TEST_TYPE(WORD, 2, 2);
+ TEST_TYPE_UNSIGNED(WORD);
+}
+
+static void test_pack_WPARAM(void)
+{
+ /* WPARAM */
+ TEST_TYPE(WPARAM, 4, 4);
+}
+
+static void test_pack(void)
+{
+ test_pack_ACCESS_ALLOWED_ACE();
+ test_pack_ACCESS_DENIED_ACE();
+ test_pack_ACCESS_MASK();
+ test_pack_ACE_HEADER();
+ test_pack_ACL();
+ test_pack_ACL_REVISION_INFORMATION();
+ test_pack_ACL_SIZE_INFORMATION();
+ test_pack_ATOM();
+ test_pack_BOOL();
+ test_pack_BOOLEAN();
+ test_pack_BYTE();
+ test_pack_CCHAR();
+ test_pack_CHAR();
+ test_pack_COLORREF();
+ test_pack_DWORD();
+ test_pack_DWORD32();
+ test_pack_DWORD64();
+ test_pack_DWORDLONG();
+ test_pack_DWORD_PTR();
+ test_pack_EXCEPTION_POINTERS();
+ test_pack_EXCEPTION_RECORD();
+ test_pack_EXECUTION_STATE();
+ test_pack_FARPROC();
+ test_pack_FLOAT();
+ test_pack_FLOATING_SAVE_AREA();
+ test_pack_FPO_DATA();
+ test_pack_GENERIC_MAPPING();
+ test_pack_GLOBALHANDLE();
+ test_pack_HALF_PTR();
+ test_pack_HANDLE();
+ test_pack_HCURSOR();
+ test_pack_HFILE();
+ test_pack_HGDIOBJ();
+ test_pack_HGLOBAL();
+ test_pack_HLOCAL();
+ test_pack_HMODULE();
+ test_pack_HRESULT();
+ test_pack_IMAGE_ARCHIVE_MEMBER_HEADER();
+ test_pack_IMAGE_AUX_SYMBOL();
+ test_pack_IMAGE_BASE_RELOCATION();
+ test_pack_IMAGE_BOUND_FORWARDER_REF();
+ test_pack_IMAGE_BOUND_IMPORT_DESCRIPTOR();
+ test_pack_IMAGE_COFF_SYMBOLS_HEADER();
+ test_pack_IMAGE_DATA_DIRECTORY();
+ test_pack_IMAGE_DEBUG_DIRECTORY();
+ test_pack_IMAGE_DEBUG_MISC();
+ test_pack_IMAGE_DOS_HEADER();
+ test_pack_IMAGE_EXPORT_DIRECTORY();
+ test_pack_IMAGE_FILE_HEADER();
+ test_pack_IMAGE_FUNCTION_ENTRY();
+ test_pack_IMAGE_IMPORT_BY_NAME();
+ test_pack_IMAGE_IMPORT_DESCRIPTOR();
+ test_pack_IMAGE_LINENUMBER();
+ test_pack_IMAGE_LOAD_CONFIG_DIRECTORY();
+ test_pack_IMAGE_NT_HEADERS();
+ test_pack_IMAGE_OPTIONAL_HEADER();
+ test_pack_IMAGE_OS2_HEADER();
+ test_pack_IMAGE_RELOCATION();
+ test_pack_IMAGE_RESOURCE_DATA_ENTRY();
+ test_pack_IMAGE_RESOURCE_DIRECTORY();
+ test_pack_IMAGE_RESOURCE_DIRECTORY_ENTRY();
+ test_pack_IMAGE_RESOURCE_DIRECTORY_STRING();
+ test_pack_IMAGE_RESOURCE_DIR_STRING_U();
+ test_pack_IMAGE_SECTION_HEADER();
+ test_pack_IMAGE_SEPARATE_DEBUG_HEADER();
+ test_pack_IMAGE_SYMBOL();
+ test_pack_IMAGE_THUNK_DATA();
+ test_pack_IMAGE_TLS_DIRECTORY();
+ test_pack_IMAGE_VXD_HEADER();
+ test_pack_INT();
+ test_pack_INT16();
+ test_pack_INT32();
+ test_pack_INT64();
+ test_pack_INT8();
+ test_pack_INT_PTR();
+ test_pack_IO_COUNTERS();
+ test_pack_LANGID();
+ test_pack_LARGE_INTEGER();
+ test_pack_LCID();
+ test_pack_LIST_ENTRY();
+ test_pack_LOCALHANDLE();
+ test_pack_LONG();
+ test_pack_LONG32();
+ test_pack_LONG64();
+ test_pack_LONGLONG();
+ test_pack_LONG_PTR();
+ test_pack_LPARAM();
+ test_pack_LPCRECT();
+ test_pack_LPCRECTL();
+ test_pack_LPCVOID();
+ test_pack_LPPOINT();
+ test_pack_LPPOINTS();
+ test_pack_LPRECT();
+ test_pack_LPRECTL();
+ test_pack_LPSIZE();
+ test_pack_LPTOP_LEVEL_EXCEPTION_FILTER();
+ test_pack_LRESULT();
+ test_pack_LUID();
+ test_pack_LUID_AND_ATTRIBUTES();
+ test_pack_MEMORY_BASIC_INFORMATION();
+ test_pack_MESSAGE_RESOURCE_BLOCK();
+ test_pack_MESSAGE_RESOURCE_DATA();
+ test_pack_MESSAGE_RESOURCE_ENTRY();
+ test_pack_NT_TIB();
+ test_pack_OBJECT_TYPE_LIST();
+ test_pack_PACCESS_ALLOWED_ACE();
+ test_pack_PACCESS_DENIED_ACE();
+ test_pack_PACCESS_TOKEN();
+ test_pack_PACE_HEADER();
+ test_pack_PACL();
+ test_pack_PACL_REVISION_INFORMATION();
+ test_pack_PACL_SIZE_INFORMATION();
+ test_pack_PCCH();
+ test_pack_PCH();
+ test_pack_PCSTR();
+ test_pack_PCTSTR();
+ test_pack_PCWCH();
+ test_pack_PCWSTR();
+ test_pack_PEXCEPTION_POINTERS();
+ test_pack_PEXCEPTION_RECORD();
+ test_pack_PFLOATING_SAVE_AREA();
+ test_pack_PFPO_DATA();
+ test_pack_PGENERIC_MAPPING();
+ test_pack_PHANDLE();
+ test_pack_PIMAGE_ARCHIVE_MEMBER_HEADER();
+ test_pack_PIMAGE_AUX_SYMBOL();
+ test_pack_PIMAGE_BASE_RELOCATION();
+ test_pack_PIMAGE_BOUND_FORWARDER_REF();
+ test_pack_PIMAGE_BOUND_IMPORT_DESCRIPTOR();
+ test_pack_PIMAGE_COFF_SYMBOLS_HEADER();
+ test_pack_PIMAGE_DATA_DIRECTORY();
+ test_pack_PIMAGE_DEBUG_DIRECTORY();
+ test_pack_PIMAGE_DEBUG_MISC();
+ test_pack_PIMAGE_DOS_HEADER();
+ test_pack_PIMAGE_EXPORT_DIRECTORY();
+ test_pack_PIMAGE_FILE_HEADER();
+ test_pack_PIMAGE_FUNCTION_ENTRY();
+ test_pack_PIMAGE_IMPORT_BY_NAME();
+ test_pack_PIMAGE_IMPORT_DESCRIPTOR();
+ test_pack_PIMAGE_LINENUMBER();
+ test_pack_PIMAGE_LOAD_CONFIG_DIRECTORY();
+ test_pack_PIMAGE_NT_HEADERS();
+ test_pack_PIMAGE_OPTIONAL_HEADER();
+ test_pack_PIMAGE_OS2_HEADER();
+ test_pack_PIMAGE_RELOCATION();
+ test_pack_PIMAGE_RESOURCE_DATA_ENTRY();
+ test_pack_PIMAGE_RESOURCE_DIRECTORY();
+ test_pack_PIMAGE_RESOURCE_DIRECTORY_ENTRY();
+ test_pack_PIMAGE_RESOURCE_DIRECTORY_STRING();
+ test_pack_PIMAGE_RESOURCE_DIR_STRING_U();
+ test_pack_PIMAGE_SECTION_HEADER();
+ test_pack_PIMAGE_SEPARATE_DEBUG_HEADER();
+ test_pack_PIMAGE_SYMBOL();
+ test_pack_PIMAGE_THUNK_DATA();
+ test_pack_PIMAGE_TLS_CALLBACK();
+ test_pack_PIMAGE_TLS_DIRECTORY();
+ test_pack_PIMAGE_VXD_HEADER();
+ test_pack_PIO_COUNTERS();
+ test_pack_PISECURITY_DESCRIPTOR();
+ test_pack_PISECURITY_DESCRIPTOR_RELATIVE();
+ test_pack_PISID();
+ test_pack_PLARGE_INTEGER();
+ test_pack_PLIST_ENTRY();
+ test_pack_PLUID();
+ test_pack_PLUID_AND_ATTRIBUTES();
+ test_pack_PMEMORY_BASIC_INFORMATION();
+ test_pack_PMESSAGE_RESOURCE_BLOCK();
+ test_pack_PMESSAGE_RESOURCE_DATA();
+ test_pack_PMESSAGE_RESOURCE_ENTRY();
+ test_pack_PNT_TIB();
+ test_pack_POBJECT_TYPE_LIST();
+ test_pack_POINT();
+ test_pack_POINTL();
+ test_pack_POINTS();
+ test_pack_PPOINT();
+ test_pack_PPOINTL();
+ test_pack_PPOINTS();
+ test_pack_PPRIVILEGE_SET();
+ test_pack_PRECT();
+ test_pack_PRECTL();
+ test_pack_PRIVILEGE_SET();
+ test_pack_PRLIST_ENTRY();
+ test_pack_PROC();
+ test_pack_PRTL_CRITICAL_SECTION();
+ test_pack_PRTL_CRITICAL_SECTION_DEBUG();
+ test_pack_PRTL_OSVERSIONINFOEXW();
+ test_pack_PRTL_OSVERSIONINFOW();
+ test_pack_PRTL_RESOURCE_DEBUG();
+ test_pack_PSECURITY_DESCRIPTOR();
+ test_pack_PSECURITY_QUALITY_OF_SERVICE();
+ test_pack_PSID();
+ test_pack_PSID_IDENTIFIER_AUTHORITY();
+ test_pack_PSINGLE_LIST_ENTRY();
+ test_pack_PSIZE();
+ test_pack_PSTR();
+ test_pack_PSYSTEM_ALARM_ACE();
+ test_pack_PSYSTEM_AUDIT_ACE();
+ test_pack_PSZ();
+ test_pack_PTOKEN_GROUPS();
+ test_pack_PTOKEN_PRIVILEGES();
+ test_pack_PTOKEN_USER();
+ test_pack_PTOP_LEVEL_EXCEPTION_FILTER();
+ test_pack_PTSTR();
+ test_pack_PULARGE_INTEGER();
+ test_pack_PVECTORED_EXCEPTION_HANDLER();
+ test_pack_PVOID();
+ test_pack_PWCH();
+ test_pack_PWSTR();
+ test_pack_RECT();
+ test_pack_RECTL();
+ test_pack_RTL_CRITICAL_SECTION();
+ test_pack_RTL_CRITICAL_SECTION_DEBUG();
+ test_pack_RTL_OSVERSIONINFOEXW();
+ test_pack_RTL_OSVERSIONINFOW();
+ test_pack_RTL_RESOURCE_DEBUG();
+ test_pack_SECURITY_CONTEXT_TRACKING_MODE();
+ test_pack_SECURITY_DESCRIPTOR();
+ test_pack_SECURITY_DESCRIPTOR_CONTROL();
+ test_pack_SECURITY_DESCRIPTOR_RELATIVE();
+ test_pack_SECURITY_INFORMATION();
+ test_pack_SECURITY_QUALITY_OF_SERVICE();
+ test_pack_SHORT();
+ test_pack_SID();
+ test_pack_SID_AND_ATTRIBUTES();
+ test_pack_SID_IDENTIFIER_AUTHORITY();
+ test_pack_SINGLE_LIST_ENTRY();
+ test_pack_SIZE();
+ test_pack_SIZEL();
+ test_pack_SIZE_T();
+ test_pack_SSIZE_T();
+ test_pack_SYSTEM_ALARM_ACE();
+ test_pack_SYSTEM_AUDIT_ACE();
+ test_pack_TCHAR();
+ test_pack_TOKEN_DEFAULT_DACL();
+ test_pack_TOKEN_GROUPS();
+ test_pack_TOKEN_OWNER();
+ test_pack_TOKEN_PRIMARY_GROUP();
+ test_pack_TOKEN_PRIVILEGES();
+ test_pack_TOKEN_SOURCE();
+ test_pack_TOKEN_STATISTICS();
+ test_pack_TOKEN_USER();
+ test_pack_UCHAR();
+ test_pack_UHALF_PTR();
+ test_pack_UINT();
+ test_pack_UINT16();
+ test_pack_UINT32();
+ test_pack_UINT64();
+ test_pack_UINT8();
+ test_pack_UINT_PTR();
+ test_pack_ULARGE_INTEGER();
+ test_pack_ULONG();
+ test_pack_ULONG32();
+ test_pack_ULONG64();
+ test_pack_ULONGLONG();
+ test_pack_ULONG_PTR();
+ test_pack_USHORT();
+ test_pack_WAITORTIMERCALLBACKFUNC();
+ test_pack_WCHAR();
+ test_pack_WORD();
+ test_pack_WPARAM();
+}
+
+START_TEST(generated)
+{
+ test_pack();
+}
--- /dev/null
+/* Unit test suite for *Information* Registry API functions
+ *
+ * Copyright 2005 Paul Vriens
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include "ntdll_test.h"
+
+static NTSTATUS (WINAPI * pNtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
+static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+
+static HMODULE hntdll = 0;
+
+/* one_before_last_pid is used to be able to compare values of a still running process
+ with the output of the test_query_process_times and test_query_process_handlecount tests.
+*/
+static DWORD one_before_last_pid = 0;
+
+#define NTDLL_GET_PROC(func) \
+ p ## func = (void*)GetProcAddress(hntdll, #func); \
+ if(!p ## func) { \
+ trace("GetProcAddress(%s) failed\n", #func); \
+ FreeLibrary(hntdll); \
+ return FALSE; \
+ }
+
+static BOOL InitFunctionPtrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ if(!hntdll) {
+ trace("Could not load ntdll.dll\n");
+ return FALSE;
+ }
+ if (hntdll)
+ {
+ NTDLL_GET_PROC(NtQuerySystemInformation)
+ NTDLL_GET_PROC(NtQueryInformationProcess)
+ }
+ return TRUE;
+}
+
+static void test_query_basic(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ SYSTEM_BASIC_INFORMATION sbi;
+
+ /* This test also covers some basic parameter testing that should be the same for
+ * every information class
+ */
+
+ /* Use a nonexistent info class */
+ trace("Check nonexistent info class\n");
+ status = pNtQuerySystemInformation(-1, NULL, 0, NULL);
+ ok( status == STATUS_INVALID_INFO_CLASS, "Expected STATUS_INVALID_INFO_CLASS, got %08lx\n", status);
+
+ /* Use an existing class but with a zero-length buffer */
+ trace("Check zero-length buffer\n");
+ status = pNtQuerySystemInformation(SystemBasicInformation, NULL, 0, NULL);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ /* Use an existing class, correct length but no SystemInformation buffer */
+ trace("Check no SystemInformation buffer\n");
+ status = pNtQuerySystemInformation(SystemBasicInformation, NULL, sizeof(sbi), NULL);
+ ok( status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", status);
+
+ /* Use a existing class, correct length, a pointer to a buffer but no ReturnLength pointer */
+ trace("Check no ReturnLength pointer\n");
+ status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+
+ /* Check a too large buffer size */
+ trace("Check a too large buffer size\n");
+ status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi) * 2, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ /* Finally some correct calls */
+ trace("Check with correct parameters\n");
+ status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(sbi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(sbi), ReturnLength);
+
+ /* Check if we have some return values */
+ trace("Number of Processors : %d\n", sbi.NumberOfProcessors);
+ ok( sbi.NumberOfProcessors > 0, "Expected more than 0 processors, got %d\n", sbi.NumberOfProcessors);
+}
+
+static void test_query_cpu(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ SYSTEM_CPU_INFORMATION sci;
+
+ status = pNtQuerySystemInformation(SystemCpuInformation, &sci, sizeof(sci), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(sci) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(sci), ReturnLength);
+
+ /* Check if we have some return values */
+ trace("Processor FeatureSet : %08lx\n", sci.FeatureSet);
+ ok( sci.FeatureSet != 0, "Expected some features for this processor, got %08lx\n", sci.FeatureSet);
+}
+
+static void test_query_performance(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ SYSTEM_PERFORMANCE_INFORMATION spi;
+
+ status = pNtQuerySystemInformation(SystemPerformanceInformation, &spi, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ status = pNtQuerySystemInformation(SystemPerformanceInformation, &spi, sizeof(spi), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(spi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(spi), ReturnLength);
+
+ status = pNtQuerySystemInformation(SystemPerformanceInformation, &spi, sizeof(spi) + 2, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(spi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(spi), ReturnLength);
+
+ /* Not return values yet, as struct members are unknown */
+}
+
+static void test_query_timeofday(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+
+ /* Copy of our winternl.h structure turned into a private one */
+ typedef struct _SYSTEM_TIMEOFDAY_INFORMATION_PRIVATE {
+ LARGE_INTEGER liKeBootTime;
+ LARGE_INTEGER liKeSystemTime;
+ LARGE_INTEGER liExpTimeZoneBias;
+ ULONG uCurrentTimeZoneId;
+ DWORD dwUnknown1[5];
+ } SYSTEM_TIMEOFDAY_INFORMATION_PRIVATE, *PSYSTEM_TIMEOFDAY_INFORMATION_PRIVATE;
+
+ SYSTEM_TIMEOFDAY_INFORMATION_PRIVATE sti;
+
+ /* The struct size for NT (32 bytes) and Win2K/XP (48 bytes) differ.
+ *
+ * Windows 2000 and XP return STATUS_INFO_LENGTH_MISMATCH if the given buffer size is greater
+ * then 48 and 0 otherwise
+ * Windows NT returns STATUS_INFO_LENGTH_MISMATCH when the given buffer size is not correct
+ * and 0 otherwise
+ *
+ * Windows 2000 and XP copy the given buffer size into the provided buffer, if the return code is STATUS_SUCCESS
+ * NT only fills the buffer if the return code is STATUS_SUCCESS
+ *
+ */
+
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, sizeof(sti), &ReturnLength);
+
+ if (status == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ trace("Windows version is NT, we have to cater for differences with W2K/WinXP\n");
+
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( 0 == ReturnLength, "ReturnLength should be 0, it is (%ld)\n", ReturnLength);
+
+ sti.uCurrentTimeZoneId = 0xdeadbeef;
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 28, &ReturnLength);
+ ok( 0xdeadbeef == sti.uCurrentTimeZoneId, "This part of the buffer should not have been filled\n");
+
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 32, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( 32 == ReturnLength, "ReturnLength should be 0, it is (%ld)\n", ReturnLength);
+ }
+ else
+ {
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 0, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( 0 == ReturnLength, "ReturnLength should be 0, it is (%ld)\n", ReturnLength);
+
+ sti.uCurrentTimeZoneId = 0xdeadbeef;
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 24, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( 24 == ReturnLength, "ReturnLength should be 24, it is (%ld)\n", ReturnLength);
+ ok( 0xdeadbeef == sti.uCurrentTimeZoneId, "This part of the buffer should not have been filled\n");
+
+ sti.uCurrentTimeZoneId = 0xdeadbeef;
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 32, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( 32 == ReturnLength, "ReturnLength should be 32, it is (%ld)\n", ReturnLength);
+ ok( 0xdeadbeef != sti.uCurrentTimeZoneId, "Buffer should have been partially filled\n");
+
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, 49, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( 0 == ReturnLength, "ReturnLength should be 0, it is (%ld)\n", ReturnLength);
+
+ status = pNtQuerySystemInformation(SystemTimeOfDayInformation, &sti, sizeof(sti), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(sti) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(sti), ReturnLength);
+ }
+
+ /* Check if we have some return values */
+ trace("uCurrentTimeZoneId : (%ld)\n", sti.uCurrentTimeZoneId);
+}
+
+static void test_query_process(void)
+{
+ DWORD status;
+ DWORD last_pid;
+ ULONG ReturnLength;
+ int i = 0, k = 0;
+ int is_nt = 0;
+ SYSTEM_BASIC_INFORMATION sbi;
+
+ /* Copy of our winternl.h structure turned into a private one */
+ typedef struct _SYSTEM_PROCESS_INFORMATION_PRIVATE {
+ DWORD dwOffset;
+ DWORD dwThreadCount;
+ DWORD dwUnknown1[6];
+ FILETIME ftCreationTime;
+ FILETIME ftUserTime;
+ FILETIME ftKernelTime;
+ UNICODE_STRING ProcessName;
+ DWORD dwBasePriority;
+ DWORD dwProcessID;
+ DWORD dwParentProcessID;
+ DWORD dwHandleCount;
+ DWORD dwUnknown3;
+ DWORD dwUnknown4;
+ VM_COUNTERS vmCounters;
+ IO_COUNTERS ioCounters;
+ SYSTEM_THREAD_INFORMATION ti[1];
+ } SYSTEM_PROCESS_INFORMATION_PRIVATE, *PSYSTEM_PROCESS_INFORMATION_PRIVATE;
+
+ ULONG SystemInformationLength = sizeof(SYSTEM_PROCESS_INFORMATION_PRIVATE);
+ SYSTEM_PROCESS_INFORMATION_PRIVATE* spi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength);
+
+ /* Only W2K3 returns the needed length, the rest returns 0, so we have to loop */
+
+ for (;;)
+ {
+ status = pNtQuerySystemInformation(SystemProcessInformation, spi, SystemInformationLength, &ReturnLength);
+
+ if (status != STATUS_INFO_LENGTH_MISMATCH) break;
+
+ spi = HeapReAlloc(GetProcessHeap(), 0, spi , SystemInformationLength *= 2);
+ }
+
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+
+ /* Get the first dwOffset, from this we can deduce the OS version we're running
+ *
+ * W2K/WinXP/W2K3:
+ * dwOffset for a process is 184 + (no. of threads) * sizeof(SYSTEM_THREAD_INFORMATION)
+ * NT:
+ * dwOffset for a process is 136 + (no. of threads) * sizeof(SYSTEM_THREAD_INFORMATION)
+ * Wine (with every windows version):
+ * dwOffset for a process is 0 if just this test is running
+ * dwOffset for a process is 184 + (no. of threads) * sizeof(SYSTEM_THREAD_INFORMATION) +
+ * ProcessName.MaximumLength
+ * if more wine processes are running
+ *
+ * Note : On windows the first process is in fact the Idle 'process' with a thread for every processor
+ */
+
+ pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength);
+
+ is_nt = ( spi->dwOffset - (sbi.NumberOfProcessors * sizeof(SYSTEM_THREAD_INFORMATION)) == 136);
+
+ if (is_nt) trace("Windows version is NT, we will skip thread tests\n");
+
+ /* Check if we have some return values
+ *
+ * On windows there will be several processes running (Including the always present Idle and System)
+ * On wine we only have one (if this test is the only wine process running)
+ */
+
+ /* Loop through the processes */
+
+ for (;;)
+ {
+ i++;
+
+ last_pid = spi->dwProcessID;
+
+ ok( spi->dwThreadCount > 0, "Expected some threads for this process, got 0\n");
+
+ /* Loop through the threads, skip NT4 for now */
+
+ if (!is_nt)
+ {
+ DWORD j;
+ for ( j = 0; j < spi->dwThreadCount; j++)
+ {
+ k++;
+ ok ( spi->ti[j].dwOwningPID == spi->dwProcessID,
+ "The owning pid of the thread (%ld) doesn't equal the pid (%ld) of the process\n",
+ spi->ti[j].dwOwningPID, spi->dwProcessID);
+ }
+ }
+
+ if (!spi->dwOffset) break;
+
+ one_before_last_pid = last_pid;
+
+ spi = (SYSTEM_PROCESS_INFORMATION_PRIVATE*)((char*)spi + spi->dwOffset);
+ }
+ trace("Total number of running processes : %d\n", i);
+ if (!is_nt) trace("Total number of running threads : %d\n", k);
+
+ if (one_before_last_pid == 0) one_before_last_pid = last_pid;
+
+ HeapFree( GetProcessHeap(), 0, spi);
+}
+
+static void test_query_procperf(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ ULONG NeededLength;
+ SYSTEM_BASIC_INFORMATION sbi;
+ SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi;
+
+ /* Find out the number of processors */
+ status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength);
+ NeededLength = sbi.NumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
+
+ sppi = HeapAlloc(GetProcessHeap(), 0, NeededLength);
+
+ status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ /* Try it for 1 processor */
+ status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi,
+ sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) == ReturnLength,
+ "Inconsistent length (%d) <-> (%ld)\n", sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), ReturnLength);
+
+ /* Try it for all processors */
+ status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, NeededLength, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( NeededLength == ReturnLength, "Inconsistent length (%ld) <-> (%ld)\n", NeededLength, ReturnLength);
+
+ /* A too large given buffer size */
+ sppi = HeapReAlloc(GetProcessHeap(), 0, sppi , NeededLength + 2);
+ status = pNtQuerySystemInformation(SystemProcessorPerformanceInformation, sppi, NeededLength + 2, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( NeededLength == ReturnLength, "Inconsistent length (%ld) <-> (%ld)\n", NeededLength, ReturnLength);
+
+ HeapFree( GetProcessHeap(), 0, sppi);
+}
+
+static void test_query_module(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ ULONG ModuleCount, i;
+
+ ULONG SystemInformationLength = sizeof(SYSTEM_MODULE_INFORMATION);
+ SYSTEM_MODULE_INFORMATION* smi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength);
+ SYSTEM_MODULE* sm;
+
+ /* Request the needed length */
+ status = pNtQuerySystemInformation(SystemModuleInformation, smi, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( ReturnLength > 0, "Expected a ReturnLength to show the needed length\n");
+
+ SystemInformationLength = ReturnLength;
+ smi = HeapReAlloc(GetProcessHeap(), 0, smi , SystemInformationLength);
+ status = pNtQuerySystemInformation(SystemModuleInformation, smi, SystemInformationLength, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+
+ ModuleCount = smi->ModulesCount;
+ sm = &smi->Modules[0];
+ todo_wine{
+ /* our implementation is a stub for now */
+ ok( ModuleCount > 0, "Expected some modules to be loaded\n");
+ }
+
+ /* Loop through all the modules/drivers, Wine doesn't get here (yet) */
+ for (i = 0; i < ModuleCount ; i++)
+ {
+ ok( i == sm->Id, "Id (%d) should have matched %lu\n", sm->Id, i);
+ sm++;
+ }
+
+ HeapFree( GetProcessHeap(), 0, smi);
+}
+
+static void test_query_handle(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ ULONG SystemInformationLength = sizeof(SYSTEM_HANDLE_INFORMATION);
+ SYSTEM_HANDLE_INFORMATION* shi = HeapAlloc(GetProcessHeap(), 0, SystemInformationLength);
+
+ /* Request the needed length : a SystemInformationLength greater than one struct sets ReturnLength */
+ status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength);
+
+ /* The following check assumes more than one handle on any given system */
+ todo_wine
+ {
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ }
+ ok( ReturnLength > 0, "Expected ReturnLength to be > 0, it was %ld\n", ReturnLength);
+
+ SystemInformationLength = ReturnLength;
+ shi = HeapReAlloc(GetProcessHeap(), 0, shi , SystemInformationLength);
+ status = pNtQuerySystemInformation(SystemHandleInformation, shi, SystemInformationLength, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+
+ /* Check if we have some return values */
+ trace("Number of Handles : %ld\n", shi->Count);
+ todo_wine
+ {
+ /* our implementation is a stub for now */
+ ok( shi->Count > 1, "Expected more than 1 handles, got (%ld)\n", shi->Count);
+ }
+
+ HeapFree( GetProcessHeap(), 0, shi);
+}
+
+static void test_query_cache(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ SYSTEM_CACHE_INFORMATION sci;
+
+ status = pNtQuerySystemInformation(SystemCacheInformation, &sci, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ status = pNtQuerySystemInformation(SystemCacheInformation, &sci, sizeof(sci), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(sci) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(sci), ReturnLength);
+
+ status = pNtQuerySystemInformation(SystemCacheInformation, &sci, sizeof(sci) + 2, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(sci) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(sci), ReturnLength);
+}
+
+static void test_query_interrupt(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ ULONG NeededLength;
+ SYSTEM_BASIC_INFORMATION sbi;
+ SYSTEM_INTERRUPT_INFORMATION* sii;
+
+ /* Find out the number of processors */
+ status = pNtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), &ReturnLength);
+ NeededLength = sbi.NumberOfProcessors * sizeof(SYSTEM_INTERRUPT_INFORMATION);
+
+ sii = HeapAlloc(GetProcessHeap(), 0, NeededLength);
+
+ status = pNtQuerySystemInformation(SystemInterruptInformation, sii, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ /* Try it for all processors */
+ status = pNtQuerySystemInformation(SystemInterruptInformation, sii, NeededLength, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+
+ /* Windows XP and W2K3 (and others?) always return 0 for the ReturnLength
+ * No test added for this as it's highly unlikely that an app depends on this
+ */
+
+ HeapFree( GetProcessHeap(), 0, sii);
+}
+
+static void test_query_kerndebug(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ SYSTEM_KERNEL_DEBUGGER_INFORMATION skdi;
+
+ status = pNtQuerySystemInformation(SystemKernelDebuggerInformation, &skdi, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ status = pNtQuerySystemInformation(SystemKernelDebuggerInformation, &skdi, sizeof(skdi), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(skdi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(skdi), ReturnLength);
+
+ status = pNtQuerySystemInformation(SystemKernelDebuggerInformation, &skdi, sizeof(skdi) + 2, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(skdi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(skdi), ReturnLength);
+}
+
+static void test_query_regquota(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ SYSTEM_REGISTRY_QUOTA_INFORMATION srqi;
+
+ status = pNtQuerySystemInformation(SystemRegistryQuotaInformation, &srqi, 0, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ status = pNtQuerySystemInformation(SystemRegistryQuotaInformation, &srqi, sizeof(srqi), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(srqi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(srqi), ReturnLength);
+
+ status = pNtQuerySystemInformation(SystemRegistryQuotaInformation, &srqi, sizeof(srqi) + 2, &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(srqi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(srqi), ReturnLength);
+}
+
+static void test_query_process_basic(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+
+ typedef struct _PROCESS_BASIC_INFORMATION_PRIVATE {
+ DWORD ExitStatus;
+ DWORD PebBaseAddress;
+ DWORD AffinityMask;
+ DWORD BasePriority;
+ ULONG UniqueProcessId;
+ ULONG InheritedFromUniqueProcessId;
+ } PROCESS_BASIC_INFORMATION_PRIVATE, *PPROCESS_BASIC_INFORMATION_PRIVATE;
+
+ PROCESS_BASIC_INFORMATION_PRIVATE pbi;
+
+ /* This test also covers some basic parameter testing that should be the same for
+ * every information class
+ */
+
+ /* Use a nonexistent info class */
+ trace("Check nonexistent info class\n");
+ status = pNtQueryInformationProcess(NULL, -1, NULL, 0, NULL);
+ ok( status == STATUS_INVALID_INFO_CLASS, "Expected STATUS_INVALID_INFO_CLASS, got %08lx\n", status);
+
+ /* Do not give a handle and buffer */
+ trace("Check NULL handle and buffer and zero-length buffersize\n");
+ status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, NULL, 0, NULL);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ /* Use a correct info class and buffer size, but still no handle and buffer */
+ trace("Check NULL handle and buffer\n");
+ status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, NULL, sizeof(pbi), NULL);
+ ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
+ "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status);
+
+ /* Use a correct info class and buffer size, but still no handle */
+ trace("Check NULL handle\n");
+ status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
+ ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status);
+
+ /* Use a greater buffer size */
+ trace("Check NULL handle and too large buffersize\n");
+ status = pNtQueryInformationProcess(NULL, ProcessBasicInformation, &pbi, sizeof(pbi) * 2, NULL);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ /* Use no ReturnLength */
+ trace("Check NULL ReturnLength\n");
+ status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+
+ /* Finally some correct calls */
+ trace("Check with correct parameters\n");
+ status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(pbi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(pbi), ReturnLength);
+
+ /* Everything is correct except a too large buffersize */
+ trace("Too large buffersize\n");
+ status = pNtQueryInformationProcess(GetCurrentProcess(), ProcessBasicInformation, &pbi, sizeof(pbi) * 2, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( sizeof(pbi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(pbi), ReturnLength);
+
+ /* Check if we have some return values */
+ trace("ProcessID : %ld\n", pbi.UniqueProcessId);
+ ok( pbi.UniqueProcessId > 0, "Expected a ProcessID > 0, got 0\n");
+}
+
+static void test_query_process_vm(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ VM_COUNTERS pvi;
+
+ status = pNtQueryInformationProcess(NULL, ProcessVmCounters, NULL, sizeof(pvi), NULL);
+ ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
+ "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status);
+
+ status = pNtQueryInformationProcess(NULL, ProcessVmCounters, &pvi, sizeof(pvi), NULL);
+ ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status);
+
+ /* Windows XP and W2K3 will report success for a size of 44 AND 48 !
+ Windows W2K will only report success for 44.
+ For now we only care for 44, which is sizeof(VM_COUNTERS)
+ If an app depends on it, we have to implement this in ntdll/process.c
+ */
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, 24, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, sizeof(pvi), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(pvi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(pvi), ReturnLength);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessVmCounters, &pvi, 46, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( sizeof(pvi) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(pvi), ReturnLength);
+
+ /* Check if we have some return values */
+ trace("WorkingSetSize : %ld\n", pvi.WorkingSetSize);
+ todo_wine
+ {
+ ok( pvi.WorkingSetSize > 0, "Expected a WorkingSetSize > 0\n");
+ }
+}
+
+static void test_query_process_io(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ IO_COUNTERS pii;
+
+ /* NT4 doesn't support this information class, so check for it */
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii), &ReturnLength);
+ if (status == STATUS_NOT_SUPPORTED)
+ {
+ trace("ProcessIoCounters information class not supported, skipping tests\n");
+ return;
+ }
+
+ status = pNtQueryInformationProcess(NULL, ProcessIoCounters, NULL, sizeof(pii), NULL);
+ ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
+ "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status);
+
+ status = pNtQueryInformationProcess(NULL, ProcessIoCounters, &pii, sizeof(pii), NULL);
+ ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, 24, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(pii) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(pii), ReturnLength);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessIoCounters, &pii, sizeof(pii) * 2, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( sizeof(pii) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(pii), ReturnLength);
+
+ /* Check if we have some return values */
+ trace("OtherOperationCount : %lld\n", pii.OtherOperationCount);
+ todo_wine
+ {
+ ok( pii.OtherOperationCount > 0, "Expected an OtherOperationCount > 0\n");
+ }
+}
+
+static void test_query_process_times(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ HANDLE process;
+ SYSTEMTIME UTC, Local;
+ KERNEL_USER_TIMES spti;
+
+ status = pNtQueryInformationProcess(NULL, ProcessTimes, NULL, sizeof(spti), NULL);
+ ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
+ "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status);
+
+ status = pNtQueryInformationProcess(NULL, ProcessTimes, &spti, sizeof(spti), NULL);
+ ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessTimes, &spti, 24, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, one_before_last_pid);
+ if (!process)
+ {
+ trace("Could not open process with ID : %ld, error : %08lx. Going to use current one.\n", one_before_last_pid, GetLastError());
+ process = GetCurrentProcess();
+ trace("ProcessTimes for current process\n");
+ }
+ else
+ trace("ProcessTimes for process with ID : %ld\n", one_before_last_pid);
+
+ status = pNtQueryInformationProcess( process, ProcessTimes, &spti, sizeof(spti), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(spti) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(spti), ReturnLength);
+ CloseHandle(process);
+
+ FileTimeToSystemTime((const FILETIME *)&spti.CreateTime, &UTC);
+ SystemTimeToTzSpecificLocalTime(NULL, &UTC, &Local);
+ trace("CreateTime : %02d/%02d/%04d %02d:%02d:%02d\n", Local.wMonth, Local.wDay, Local.wYear,
+ Local.wHour, Local.wMinute, Local.wSecond);
+
+ FileTimeToSystemTime((const FILETIME *)&spti.ExitTime, &UTC);
+ SystemTimeToTzSpecificLocalTime(NULL, &UTC, &Local);
+ trace("ExitTime : %02d/%02d/%04d %02d:%02d:%02d\n", Local.wMonth, Local.wDay, Local.wYear,
+ Local.wHour, Local.wMinute, Local.wSecond);
+
+ FileTimeToSystemTime((const FILETIME *)&spti.KernelTime, &Local);
+ trace("KernelTime : %02d:%02d:%02d.%03d\n", Local.wHour, Local.wMinute, Local.wSecond, Local.wMilliseconds);
+
+ FileTimeToSystemTime((const FILETIME *)&spti.UserTime, &Local);
+ trace("UserTime : %02d:%02d:%02d.%03d\n", Local.wHour, Local.wMinute, Local.wSecond, Local.wMilliseconds);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessTimes, &spti, sizeof(spti) * 2, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( sizeof(spti) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(spti), ReturnLength);
+}
+
+static void test_query_process_handlecount(void)
+{
+ DWORD status;
+ ULONG ReturnLength;
+ DWORD handlecount;
+ HANDLE process;
+
+ status = pNtQueryInformationProcess(NULL, ProcessHandleCount, NULL, sizeof(handlecount), NULL);
+ ok( status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_HANDLE,
+ "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_HANDLE(W2K3), got %08lx\n", status);
+
+ status = pNtQueryInformationProcess(NULL, ProcessHandleCount, &handlecount, sizeof(handlecount), NULL);
+ ok( status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got %08lx\n", status);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessHandleCount, &handlecount, 2, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+
+ process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, one_before_last_pid);
+ if (!process)
+ {
+ trace("Could not open process with ID : %ld, error : %08lx. Going to use current one.\n", one_before_last_pid, GetLastError());
+ process = GetCurrentProcess();
+ trace("ProcessHandleCount for current process\n");
+ }
+ else
+ trace("ProcessHandleCount for process with ID : %ld\n", one_before_last_pid);
+
+ status = pNtQueryInformationProcess( process, ProcessHandleCount, &handlecount, sizeof(handlecount), &ReturnLength);
+ ok( status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %08lx\n", status);
+ ok( sizeof(handlecount) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(handlecount), ReturnLength);
+ CloseHandle(process);
+
+ status = pNtQueryInformationProcess( GetCurrentProcess(), ProcessHandleCount, &handlecount, sizeof(handlecount) * 2, &ReturnLength);
+ ok( status == STATUS_INFO_LENGTH_MISMATCH, "Expected STATUS_INFO_LENGTH_MISMATCH, got %08lx\n", status);
+ ok( sizeof(handlecount) == ReturnLength, "Inconsistent length (%d) <-> (%ld)\n", sizeof(handlecount), ReturnLength);
+
+ /* Check if we have some return values */
+ trace("HandleCount : %ld\n", handlecount);
+ todo_wine
+ {
+ ok( handlecount > 0, "Expected some handles, got 0\n");
+ }
+}
+
+START_TEST(info)
+{
+ if(!InitFunctionPtrs())
+ return;
+
+ /* NtQuerySystemInformation */
+
+ /* 0x0 SystemBasicInformation */
+ trace("Starting test_query_basic()\n");
+ test_query_basic();
+
+ /* 0x1 SystemCpuInformation */
+ trace("Starting test_query_cpu()\n");
+ test_query_cpu();
+
+ /* 0x2 SystemPerformanceInformation */
+ trace("Starting test_query_performance()\n");
+ test_query_performance();
+
+ /* 0x3 SystemTimeOfDayInformation */
+ trace("Starting test_query_timeofday()\n");
+ test_query_timeofday();
+
+ /* 0x5 SystemProcessInformation */
+ trace("Starting test_query_process()\n");
+ test_query_process();
+
+ /* 0x8 SystemProcessorPerformanceInformation */
+ trace("Starting test_query_procperf()\n");
+ test_query_procperf();
+
+ /* 0xb SystemModuleInformation */
+ trace("Starting test_query_module()\n");
+ test_query_module();
+
+ /* 0x10 SystemHandleInformation */
+ trace("Starting test_query_handle()\n");
+ test_query_handle();
+
+ /* 0x15 SystemCacheInformation */
+ trace("Starting test_query_cache()\n");
+ test_query_cache();
+
+ /* 0x17 SystemInterruptInformation */
+ trace("Starting test_query_interrupt()\n");
+ test_query_interrupt();
+
+ /* 0x23 SystemKernelDebuggerInformation */
+ trace("Starting test_query_kerndebug()\n");
+ test_query_kerndebug();
+
+ /* 0x25 SystemRegistryQuotaInformation */
+ trace("Starting test_query_regquota()\n");
+ test_query_regquota();
+
+ /* NtQueryInformationProcess */
+
+ /* 0x0 ProcessBasicInformation */
+ trace("Starting test_query_process_basic()\n");
+ test_query_process_basic();
+
+ /* 0x2 ProcessIoCounters */
+ trace("Starting test_query_process_io()\n");
+ test_query_process_io();
+
+ /* 0x3 ProcessVmCounters */
+ trace("Starting test_query_process_vm()\n");
+ test_query_process_vm();
+
+ /* 0x4 ProcessTimes */
+ trace("Starting test_query_process_times()\n");
+ test_query_process_times();
+
+ /* 0x14 ProcessHandleCount */
+ trace("Starting test_query_process_handlecount()\n");
+ test_query_process_handlecount();
+
+ FreeLibrary(hntdll);
+}
--- /dev/null
+/* Unit test suite for Rtl large integer functions
+ *
+ * Copyright 2003 Thomas Mertes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as there is no import library for NTDLL on
+ * windows.
+ */
+
+#include <stdlib.h>
+
+#include "ntdll_test.h"
+
+
+/* Function ptrs for ntdll calls */
+static HMODULE hntdll = 0;
+static LONGLONG (WINAPI *pRtlExtendedMagicDivide)(LONGLONG, LONGLONG, INT);
+static VOID (WINAPI *pRtlFreeAnsiString)(PSTRING);
+static NTSTATUS (WINAPI *pRtlInt64ToUnicodeString)(ULONGLONG, ULONG, UNICODE_STRING *);
+static NTSTATUS (WINAPI *pRtlLargeIntegerToChar)(ULONGLONG *, ULONG, ULONG, PCHAR);
+static NTSTATUS (WINAPI *pRtlUnicodeStringToAnsiString)(STRING *, const UNICODE_STRING *, BOOLEAN);
+
+
+static void InitFunctionPtrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ ok(hntdll != 0, "LoadLibrary failed\n");
+ if (hntdll) {
+ pRtlExtendedMagicDivide = (void *)GetProcAddress(hntdll, "RtlExtendedMagicDivide");
+ pRtlFreeAnsiString = (void *)GetProcAddress(hntdll, "RtlFreeAnsiString");
+ pRtlInt64ToUnicodeString = (void *)GetProcAddress(hntdll, "RtlInt64ToUnicodeString");
+ pRtlLargeIntegerToChar = (void *)GetProcAddress(hntdll, "RtlLargeIntegerToChar");
+ pRtlUnicodeStringToAnsiString = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToAnsiString");
+ } /* if */
+}
+
+#define ULL(a,b) (((ULONGLONG)(a) << 32) | (b))
+
+typedef struct {
+ LONGLONG a;
+ LONGLONG b;
+ INT shift;
+ LONGLONG result;
+} magic_divide_t;
+
+static const magic_divide_t magic_divide[] = {
+ { 3, ULL(0x55555555,0x55555555), 0, 0}, /* 1 */
+ { 333333333, ULL(0x55555555,0x55555555), 0, 111111110}, /* 111111111 */
+ { ULL(0x7fffffff,0xffffffff), ULL(0x55555555,0x55555555), 0, ULL(0x2aaaaaaa,0xaaaaaaaa)},
+ { 3, ULL(0xaaaaaaaa,0xaaaaaaaa), 1, 0}, /* 1 */
+ { 333333333, ULL(0xaaaaaaaa,0xaaaaaaaa), 1, 111111110}, /* 111111111 */
+ { ULL(0x7fffffff,0xffffffff), ULL(0xaaaaaaaa,0xaaaaaaaa), 1, ULL(0x2aaaaaaa,0xaaaaaaaa)},
+ { -3, ULL(0x55555555,0x55555555), 0, 0}, /* -1 */
+ { -333333333, ULL(0x55555555,0x55555555), 0, -111111110}, /* -111111111 */
+ {-ULL(0x7fffffff,0xffffffff), ULL(0x55555555,0x55555555), 0, -ULL(0x2aaaaaaa,0xaaaaaaaa)},
+ { -3, ULL(0xaaaaaaaa,0xaaaaaaaa), 1, 0}, /* -1 */
+ { -333333333, ULL(0xaaaaaaaa,0xaaaaaaaa), 1, -111111110}, /* -111111111 */
+ {-ULL(0x7fffffff,0xffffffff), ULL(0xaaaaaaaa,0xaaaaaaaa), 1, -ULL(0x2aaaaaaa,0xaaaaaaaa)},
+ { -3, -ULL(0x55555555,0x55555555), 0, -2}, /* -1 */
+ { -333333333, -ULL(0x55555555,0x55555555), 0, -222222222}, /* -111111111 */
+ {-ULL(0x7fffffff,0xffffffff), -ULL(0x55555555,0x55555555), 0, -ULL(0x55555555,0x55555554)},
+ { -3, -ULL(0xaaaaaaaa,0xaaaaaaaa), 1, 0}, /* -1 */
+ { -333333333, -ULL(0xaaaaaaaa,0xaaaaaaaa), 1, -55555555}, /* -111111111 */
+ {-ULL(0x7fffffff,0xffffffff), -ULL(0xaaaaaaaa,0xaaaaaaaa), 1, -ULL(0x15555555,0x55555555)},
+ { 3, -ULL(0x55555555,0x55555555), 0, 2}, /* -1 */
+ { 333333333, -ULL(0x55555555,0x55555555), 0, 222222222}, /* -111111111 */
+ { ULL(0x7fffffff,0xffffffff), -ULL(0x55555555,0x55555555), 0, ULL(0x55555555,0x55555554)},
+ { 3, -ULL(0xaaaaaaaa,0xaaaaaaaa), 1, 0}, /* -1 */
+ { 333333333, -ULL(0xaaaaaaaa,0xaaaaaaaa), 1, 55555555}, /* -111111111 */
+ { ULL(0x7fffffff,0xffffffff), -ULL(0xaaaaaaaa,0xaaaaaaaa), 1, ULL(0x15555555,0x55555555)},
+ { 3, ULL(0xaaaaaaaa,0xaaaaa800), 1, 0}, /* 1 */
+ { 333333333, ULL(0xaaaaaaaa,0xaaaaa800), 1, 111111110}, /* 111111111 */
+ { ULL(0x7fffffff,0xffffffff), ULL(0xaaaaaaaa,0xaaaaa800), 1, ULL(0x2aaaaaaa,0xaaaaa9ff)}, /* 0x2aaaaaaaaaaaaaaa */
+ { 5, ULL(0x33333333,0x333333ff), 0, 1},
+ { 555555555, ULL(0x33333333,0x333333ff), 0, 111111111},
+ { ULL(0x7fffffff,0xffffffff), ULL(0x33333333,0x333333ff), 0, ULL(0x19999999,0x999999ff)}, /* 0x199999999999999a */
+ { 5, ULL(0x66666666,0x666667fe), 1, 1},
+ { 555555555, ULL(0x66666666,0x666667fe), 1, 111111111},
+ { ULL(0x7fffffff,0xffffffff), ULL(0x66666666,0x666667fe), 1, ULL(0x19999999,0x999999ff)}, /* 0x199999999999999a */
+ { 5, ULL(0xcccccccc,0xcccccffd), 2, 1},
+ { 555555555, ULL(0xcccccccc,0xcccccffd), 2, 111111111},
+ { ULL(0x7fffffff,0xffffffff), ULL(0xcccccccc,0xcccccffd), 2, ULL(0x19999999,0x999999ff)}, /* 0x199999999999999a */
+ { ULL(0x00000add,0xcafeface), ULL(0x002f1e28,0xfd1b5cca), 33, 1},
+ { ULL(0x081ac1b9,0xc2310a80), ULL(0x002f1e28,0xfd1b5cca), 33, 0xbeef},
+ { ULL(0x74ae3b5f,0x1558c800), ULL(0x002f1e28,0xfd1b5cca), 33, 0xabcde},
+ { ULL(0x00000add,0xcafeface), ULL(0x2f1e28fd,0x1b5cca00), 41, 1},
+ { ULL(0x081ac1b9,0xc2310a80), ULL(0x2f1e28fd,0x1b5cca00), 41, 0xbeef},
+ { ULL(0x74ae3b5f,0x1558c800), ULL(0x2f1e28fd,0x1b5cca00), 41, 0xabcde},
+
+};
+#define NB_MAGIC_DIVIDE (sizeof(magic_divide)/sizeof(*magic_divide))
+
+
+static void test_RtlExtendedMagicDivide(void)
+{
+ int i;
+ LONGLONG result;
+
+ for (i = 0; i < NB_MAGIC_DIVIDE; i++) {
+ result = pRtlExtendedMagicDivide(magic_divide[i].a, magic_divide[i].b, magic_divide[i].shift);
+ ok(result == magic_divide[i].result,
+ "call failed: RtlExtendedMagicDivide(%lld, %llu, %d) has result %llx, expected %llx\n",
+ magic_divide[i].a, magic_divide[i].b, magic_divide[i].shift, result, magic_divide[i].result);
+ }
+}
+
+
+#define LARGE_STRI_BUFFER_LENGTH 67
+
+typedef struct {
+ int base;
+ ULONGLONG value;
+ USHORT Length;
+ USHORT MaximumLength;
+ const char *Buffer;
+ NTSTATUS result;
+} largeint2str_t;
+
+/*
+ * The native DLL does produce garbage or STATUS_BUFFER_OVERFLOW for
+ * base 2, 8 and 16 when the value is larger than 0xFFFFFFFF.
+ * Therefore these testcases are commented out.
+ */
+
+static const largeint2str_t largeint2str[] = {
+ {10, 123, 3, 11, "123\0---------------------------------------------------------------", STATUS_SUCCESS},
+
+ { 0, 0x80000000U, 10, 11, "2147483648\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 0, -2147483647, 20, 21, "18446744071562067969\0----------------------------------------------", STATUS_SUCCESS},
+ { 0, -2, 20, 21, "18446744073709551614\0----------------------------------------------", STATUS_SUCCESS},
+ { 0, -1, 20, 21, "18446744073709551615\0----------------------------------------------", STATUS_SUCCESS},
+ { 0, 0, 1, 11, "0\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 1, 1, 11, "1\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 12, 2, 11, "12\0----------------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 123, 3, 11, "123\0---------------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 1234, 4, 11, "1234\0--------------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 12345, 5, 11, "12345\0-------------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 123456, 6, 11, "123456\0------------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 1234567, 7, 11, "1234567\0-----------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 12345678, 8, 11, "12345678\0----------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 123456789, 9, 11, "123456789\0---------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 2147483646, 10, 11, "2147483646\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 2147483647, 10, 11, "2147483647\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 2147483648U, 10, 11, "2147483648\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 2147483649U, 10, 11, "2147483649\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 4294967294U, 10, 11, "4294967294\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 0, 4294967295U, 10, 11, "4294967295\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0x2,0xdfdc1c35), 11, 12, "12345678901\0-------------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0xe5,0xf4c8f374), 12, 13, "987654321012\0------------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0x1c0,0xfc161e3e), 13, 14, "1928374656574\0-----------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0xbad,0xcafeface), 14, 15, "12841062955726\0----------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0x5bad,0xcafeface), 15, 16, "100801993177806\0---------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0xaface,0xbeefcafe), 16, 20, "3090515640699646\0--------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0xa5beef,0xabcdcafe), 17, 20, "46653307746110206\0-------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0x1f8cf9b,0xf2df3af1), 18, 20, "142091656963767025\0------------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0x0fffffff,0xffffffff), 19, 20, "1152921504606846975\0-----------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0xffffffff,0xfffffffe), 20, 21, "18446744073709551614\0----------------------------------------------", STATUS_SUCCESS},
+ { 0, ULL(0xffffffff,0xffffffff), 20, 21, "18446744073709551615\0----------------------------------------------", STATUS_SUCCESS},
+
+ { 2, 0x80000000U, 32, 33, "10000000000000000000000000000000\0----------------------------------", STATUS_SUCCESS},
+/*
+ * { 2, -2147483647, 64, 65, "1111111111111111111111111111111110000000000000000000000000000001\0--", STATUS_SUCCESS},
+ * { 2, -2, 64, 65, "1111111111111111111111111111111111111111111111111111111111111110\0--", STATUS_SUCCESS},
+ * { 2, -1, 64, 65, "1111111111111111111111111111111111111111111111111111111111111111\0--", STATUS_SUCCESS},
+ */
+ { 2, 0, 1, 33, "0\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ { 2, 1, 1, 33, "1\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ { 2, 10, 4, 33, "1010\0--------------------------------------------------------------", STATUS_SUCCESS},
+ { 2, 100, 7, 33, "1100100\0-----------------------------------------------------------", STATUS_SUCCESS},
+ { 2, 1000, 10, 33, "1111101000\0--------------------------------------------------------", STATUS_SUCCESS},
+ { 2, 10000, 14, 33, "10011100010000\0----------------------------------------------------", STATUS_SUCCESS},
+ { 2, 32767, 15, 33, "111111111111111\0---------------------------------------------------", STATUS_SUCCESS},
+ { 2, 32768, 16, 33, "1000000000000000\0--------------------------------------------------", STATUS_SUCCESS},
+ { 2, 65535, 16, 33, "1111111111111111\0--------------------------------------------------", STATUS_SUCCESS},
+ { 2, 100000, 17, 33, "11000011010100000\0-------------------------------------------------", STATUS_SUCCESS},
+ { 2, 1000000, 20, 33, "11110100001001000000\0----------------------------------------------", STATUS_SUCCESS},
+ { 2, 10000000, 24, 33, "100110001001011010000000\0------------------------------------------", STATUS_SUCCESS},
+ { 2, 100000000, 27, 33, "101111101011110000100000000\0---------------------------------------", STATUS_SUCCESS},
+ { 2, 1000000000, 30, 33, "111011100110101100101000000000\0------------------------------------", STATUS_SUCCESS},
+ { 2, 1073741823, 30, 33, "111111111111111111111111111111\0------------------------------------", STATUS_SUCCESS},
+ { 2, 2147483646, 31, 33, "1111111111111111111111111111110\0-----------------------------------", STATUS_SUCCESS},
+ { 2, 2147483647, 31, 33, "1111111111111111111111111111111\0-----------------------------------", STATUS_SUCCESS},
+ { 2, 2147483648U, 32, 33, "10000000000000000000000000000000\0----------------------------------", STATUS_SUCCESS},
+ { 2, 2147483649U, 32, 33, "10000000000000000000000000000001\0----------------------------------", STATUS_SUCCESS},
+ { 2, 4294967294U, 32, 33, "11111111111111111111111111111110\0----------------------------------", STATUS_SUCCESS},
+ { 2, 0xFFFFFFFF, 32, 33, "11111111111111111111111111111111\0----------------------------------", STATUS_SUCCESS},
+/*
+ * { 2, 0x1FFFFFFFF, 33, 34, "111111111111111111111111111111111\0---------------------------------", STATUS_SUCCESS},
+ * { 2, 0x3FFFFFFFF, 34, 35, "1111111111111111111111111111111111\0--------------------------------", STATUS_SUCCESS},
+ * { 2, 0x7FFFFFFFF, 35, 36, "11111111111111111111111111111111111\0-------------------------------", STATUS_SUCCESS},
+ * { 2, 0xFFFFFFFFF, 36, 37, "111111111111111111111111111111111111\0------------------------------", STATUS_SUCCESS},
+ * { 2, 0x1FFFFFFFFF, 37, 38, "1111111111111111111111111111111111111\0-----------------------------", STATUS_SUCCESS},
+ * { 2, 0x3FFFFFFFFF, 38, 39, "11111111111111111111111111111111111111\0----------------------------", STATUS_SUCCESS},
+ * { 2, 0x7FFFFFFFFF, 39, 40, "111111111111111111111111111111111111111\0---------------------------", STATUS_SUCCESS},
+ * { 2, 0xFFFFFFFFFF, 40, 41, "1111111111111111111111111111111111111111\0--------------------------", STATUS_SUCCESS},
+ */
+
+ { 8, 0x80000000U, 11, 12, "20000000000\0-------------------------------------------------------", STATUS_SUCCESS},
+/*
+ * { 8, -2147483647, 22, 23, "1777777777760000000001\0--------------------------------------------", STATUS_SUCCESS},
+ * { 8, -2, 22, 23, "1777777777777777777776\0--------------------------------------------", STATUS_SUCCESS},
+ * { 8, -1, 22, 23, "1777777777777777777777\0--------------------------------------------", STATUS_SUCCESS},
+ */
+ { 8, 0, 1, 12, "0\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ { 8, 1, 1, 12, "1\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ { 8, 2147483646, 11, 12, "17777777776\0-------------------------------------------------------", STATUS_SUCCESS},
+ { 8, 2147483647, 11, 12, "17777777777\0-------------------------------------------------------", STATUS_SUCCESS},
+ { 8, 2147483648U, 11, 12, "20000000000\0-------------------------------------------------------", STATUS_SUCCESS},
+ { 8, 2147483649U, 11, 12, "20000000001\0-------------------------------------------------------", STATUS_SUCCESS},
+ { 8, 4294967294U, 11, 12, "37777777776\0-------------------------------------------------------", STATUS_SUCCESS},
+ { 8, 4294967295U, 11, 12, "37777777777\0-------------------------------------------------------", STATUS_SUCCESS},
+
+ {10, 0x80000000U, 10, 11, "2147483648\0--------------------------------------------------------", STATUS_SUCCESS},
+ {10, -2147483647, 20, 21, "18446744071562067969\0----------------------------------------------", STATUS_SUCCESS},
+ {10, -2, 20, 21, "18446744073709551614\0----------------------------------------------", STATUS_SUCCESS},
+ {10, -1, 20, 21, "18446744073709551615\0----------------------------------------------", STATUS_SUCCESS},
+ {10, 0, 1, 11, "0\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ {10, 1, 1, 11, "1\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ {10, 2147483646, 10, 11, "2147483646\0--------------------------------------------------------", STATUS_SUCCESS},
+ {10, 2147483647, 10, 11, "2147483647\0--------------------------------------------------------", STATUS_SUCCESS},
+ {10, 2147483648U, 10, 11, "2147483648\0--------------------------------------------------------", STATUS_SUCCESS},
+ {10, 2147483649U, 10, 11, "2147483649\0--------------------------------------------------------", STATUS_SUCCESS},
+ {10, 4294967294U, 10, 11, "4294967294\0--------------------------------------------------------", STATUS_SUCCESS},
+ {10, 4294967295U, 10, 11, "4294967295\0--------------------------------------------------------", STATUS_SUCCESS},
+
+ {16, 0, 1, 9, "0\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ {16, 1, 1, 9, "1\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ {16, 2147483646, 8, 9, "7FFFFFFE\0----------------------------------------------------------", STATUS_SUCCESS},
+ {16, 2147483647, 8, 9, "7FFFFFFF\0----------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0x80000000, 8, 9, "80000000\0----------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0x80000001, 8, 9, "80000001\0----------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0xFFFFFFFE, 8, 9, "FFFFFFFE\0----------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0xFFFFFFFF, 8, 9, "FFFFFFFF\0----------------------------------------------------------", STATUS_SUCCESS},
+/*
+ * {16, 0x100000000, 9, 10, "100000000\0---------------------------------------------------------", STATUS_SUCCESS},
+ * {16, 0xBADDEADBEEF, 11, 12, "BADDEADBEEF\0-------------------------------------------------------", STATUS_SUCCESS},
+ * {16, 0x8000000000000000, 16, 17, "8000000000000000\0--------------------------------------------------", STATUS_SUCCESS},
+ * {16, 0xFEDCBA9876543210, 16, 17, "FEDCBA9876543210\0--------------------------------------------------", STATUS_SUCCESS},
+ * {16, 0xFFFFFFFF80000001, 16, 17, "FFFFFFFF80000001\0--------------------------------------------------", STATUS_SUCCESS},
+ * {16, 0xFFFFFFFFFFFFFFFE, 16, 17, "FFFFFFFFFFFFFFFE\0--------------------------------------------------", STATUS_SUCCESS},
+ * {16, 0xFFFFFFFFFFFFFFFF, 16, 17, "FFFFFFFFFFFFFFFF\0--------------------------------------------------", STATUS_SUCCESS},
+ */
+
+ { 2, 32768, 16, 17, "1000000000000000\0--------------------------------------------------", STATUS_SUCCESS},
+ { 2, 32768, 16, 16, "1000000000000000---------------------------------------------------", STATUS_SUCCESS},
+ { 2, 65536, 17, 18, "10000000000000000\0-------------------------------------------------", STATUS_SUCCESS},
+ { 2, 65536, 17, 17, "10000000000000000--------------------------------------------------", STATUS_SUCCESS},
+ { 2, 131072, 18, 19, "100000000000000000\0------------------------------------------------", STATUS_SUCCESS},
+ { 2, 131072, 18, 18, "100000000000000000-------------------------------------------------", STATUS_SUCCESS},
+ {16, 0xffffffff, 8, 9, "FFFFFFFF\0----------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0xffffffff, 8, 8, "FFFFFFFF-----------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0xffffffff, 8, 7, "-------------------------------------------------------------------", STATUS_BUFFER_OVERFLOW},
+ {16, 0xa, 1, 2, "A\0-----------------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0xa, 1, 1, "A------------------------------------------------------------------", STATUS_SUCCESS},
+ {16, 0, 1, 0, "-------------------------------------------------------------------", STATUS_BUFFER_OVERFLOW},
+ {20, 0xdeadbeef, 0, 9, "-------------------------------------------------------------------", STATUS_INVALID_PARAMETER},
+ {-8, 07654321, 0, 12, "-------------------------------------------------------------------", STATUS_INVALID_PARAMETER},
+};
+#define NB_LARGEINT2STR (sizeof(largeint2str)/sizeof(*largeint2str))
+
+
+static void one_RtlInt64ToUnicodeString_test(int test_num, const largeint2str_t *largeint2str)
+{
+ int pos;
+ WCHAR expected_str_Buffer[LARGE_STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING expected_unicode_string;
+ STRING expected_ansi_str;
+ WCHAR str_Buffer[LARGE_STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING unicode_string;
+ STRING ansi_str;
+ NTSTATUS result;
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_str_Buffer[pos] = largeint2str->Buffer[pos];
+ } /* for */
+ expected_unicode_string.Length = largeint2str->Length * sizeof(WCHAR);
+ expected_unicode_string.MaximumLength = largeint2str->MaximumLength * sizeof(WCHAR);
+ expected_unicode_string.Buffer = expected_str_Buffer;
+ pRtlUnicodeStringToAnsiString(&expected_ansi_str, &expected_unicode_string, 1);
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ str_Buffer[pos] = '-';
+ } /* for */
+ unicode_string.Length = 0;
+ unicode_string.MaximumLength = largeint2str->MaximumLength * sizeof(WCHAR);
+ unicode_string.Buffer = str_Buffer;
+
+ if (largeint2str->base == 0) {
+ result = pRtlInt64ToUnicodeString(largeint2str->value, 10, &unicode_string);
+ } else {
+ result = pRtlInt64ToUnicodeString(largeint2str->value, largeint2str->base, &unicode_string);
+ } /* if */
+ pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
+ if (result == STATUS_BUFFER_OVERFLOW) {
+ /* On BUFFER_OVERFLOW the string Buffer should be unchanged */
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_str_Buffer[pos] = '-';
+ } /* for */
+ /* w2k: The native function has two reasons for BUFFER_OVERFLOW: */
+ /* If the value is too large to convert: The Length is unchanged */
+ /* If str is too small to hold the string: Set str->Length to the length */
+ /* the string would have (which can be larger than the MaximumLength). */
+ /* To allow all this in the tests we do the following: */
+ if (expected_unicode_string.Length >= 64) {
+ /* The value is too large to convert only triggerd when testing native */
+ /* Length is not filled with the expected string length (garbage?) */
+ expected_unicode_string.Length = unicode_string.Length;
+ } /* if */
+ } else {
+ ok(result == largeint2str->result,
+ "(test %d): RtlInt64ToUnicodeString(%llu, %d, [out]) has result %lx, expected: %lx\n",
+ test_num, largeint2str->value, largeint2str->base, result, largeint2str->result);
+ if (result == STATUS_SUCCESS) {
+ ok(unicode_string.Buffer[unicode_string.Length/sizeof(WCHAR)] == '\0',
+ "(test %d): RtlInt64ToUnicodeString(%llu, %d, [out]) string \"%s\" is not NULL terminated\n",
+ test_num, largeint2str->value, largeint2str->base, ansi_str.Buffer);
+ } /* if */
+ } /* if */
+ ok(memcmp(unicode_string.Buffer, expected_unicode_string.Buffer, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): RtlInt64ToUnicodeString(%llu, %d, [out]) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, largeint2str->value, largeint2str->base, ansi_str.Buffer, expected_ansi_str.Buffer);
+ ok(unicode_string.Length == expected_unicode_string.Length,
+ "(test %d): RtlInt64ToUnicodeString(%llu, %d, [out]) string has Length %d, expected: %d\n",
+ test_num, largeint2str->value, largeint2str->base, unicode_string.Length, expected_unicode_string.Length);
+ ok(unicode_string.MaximumLength == expected_unicode_string.MaximumLength,
+ "(test %d): RtlInt64ToUnicodeString(%llu, %d, [out]) string has MaximumLength %d, expected: %d\n",
+ test_num, largeint2str->value, largeint2str->base, unicode_string.MaximumLength, expected_unicode_string.MaximumLength);
+ pRtlFreeAnsiString(&expected_ansi_str);
+ pRtlFreeAnsiString(&ansi_str);
+}
+
+
+static void test_RtlInt64ToUnicodeString(void)
+{
+ int test_num;
+
+ for (test_num = 0; test_num < NB_LARGEINT2STR; test_num++) {
+ one_RtlInt64ToUnicodeString_test(test_num, &largeint2str[test_num]);
+ } /* for */
+}
+
+
+static void one_RtlLargeIntegerToChar_test(int test_num, const largeint2str_t *largeint2str)
+{
+ NTSTATUS result;
+ char dest_str[LARGE_STRI_BUFFER_LENGTH + 1];
+ ULONGLONG value;
+
+ memset(dest_str, '-', LARGE_STRI_BUFFER_LENGTH);
+ dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ value = largeint2str->value;
+ if (largeint2str->base == 0) {
+ result = pRtlLargeIntegerToChar(&value, 10, largeint2str->MaximumLength, dest_str);
+ } else {
+ result = pRtlLargeIntegerToChar(&value, largeint2str->base, largeint2str->MaximumLength, dest_str);
+ } /* if */
+ ok(result == largeint2str->result,
+ "(test %d): RtlLargeIntegerToChar(%llu, %d, %d, [out]) has result %lx, expected: %lx\n",
+ test_num, largeint2str->value, largeint2str->base, largeint2str->MaximumLength, result, largeint2str->result);
+ ok(memcmp(dest_str, largeint2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
+ "(test %d): RtlLargeIntegerToChar(%llu, %d, %d, [out]) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, largeint2str->value, largeint2str->base, largeint2str->MaximumLength, dest_str, largeint2str->Buffer);
+}
+
+
+static void test_RtlLargeIntegerToChar(void)
+{
+ NTSTATUS result;
+ int test_num;
+ ULONGLONG value;
+
+ for (test_num = 0; test_num < NB_LARGEINT2STR; test_num++) {
+ one_RtlLargeIntegerToChar_test(test_num, &largeint2str[test_num]);
+ } /* for */
+
+ value = largeint2str[0].value;
+ result = pRtlLargeIntegerToChar(&value, 20, largeint2str[0].MaximumLength, NULL);
+ ok(result == STATUS_INVALID_PARAMETER,
+ "(test a): RtlLargeIntegerToChar(%llu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ largeint2str[0].value, 20, largeint2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER);
+
+ result = pRtlLargeIntegerToChar(&value, 20, 0, NULL);
+ ok(result == STATUS_INVALID_PARAMETER,
+ "(test b): RtlLargeIntegerToChar(%llu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ largeint2str[0].value, 20, largeint2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER);
+
+ result = pRtlLargeIntegerToChar(&value, largeint2str[0].base, 0, NULL);
+ ok(result == STATUS_BUFFER_OVERFLOW,
+ "(test c): RtlLargeIntegerToChar(%llu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ largeint2str[0].value, largeint2str[0].base, 0, result, STATUS_BUFFER_OVERFLOW);
+
+ result = pRtlLargeIntegerToChar(&value, largeint2str[0].base, largeint2str[0].MaximumLength, NULL);
+ ok(result == STATUS_ACCESS_VIOLATION,
+ "(test d): RtlLargeIntegerToChar(%llu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ largeint2str[0].value, largeint2str[0].base, largeint2str[0].MaximumLength, result, STATUS_ACCESS_VIOLATION);
+}
+
+
+START_TEST(large_int)
+{
+ InitFunctionPtrs();
+
+ if (pRtlExtendedMagicDivide)
+ test_RtlExtendedMagicDivide();
+ if (pRtlInt64ToUnicodeString)
+ test_RtlInt64ToUnicodeString();
+ if (pRtlLargeIntegerToChar)
+ test_RtlLargeIntegerToChar();
+}
--- /dev/null
+<module name="ntdll_winetest" type="win32cui" installbase="bin" installname="ntdll_winetest.exe" allowwarnings="true">
+ <include base="ntdll_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>atom.c</file>
+ <file>change.c</file>
+ <file>env.c</file>
+ <file>error.c</file>
+ <file>exception.c</file>
+ <file>info.c</file>
+ <file>large_int.c</file>
+ <file>om.c</file>
+ <file>path.c</file>
+ <file>port.c</file>
+ <file>reg.c</file>
+ <file>rtlbitmap.c</file>
+ <file>rtl.c</file>
+ <file>rtlstr.c</file>
+ <file>string.c</file>
+ <file>time.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * Unit test suite for ntdll path functions
+ *
+ * Copyright 2003 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x500 /* For NTSTATUS */
+#endif
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winnt.h"
+#include "winreg.h"
+#include "winternl.h"
+
+#include "wine/test.h"
--- /dev/null
+/*
+ * Unit test suite for object manager functions
+ *
+ * Copyright 2005 Robert Shearman
+ * Copyright 2005 Vitaliy Margolen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "ntdll_test.h"
+#include "winternl.h"
+#include "stdio.h"
+#include "winnt.h"
+#include "stdlib.h"
+
+static NTSTATUS (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
+static VOID (WINAPI *pRtlInitUnicodeString)( PUNICODE_STRING, LPCWSTR );
+static VOID (WINAPI *pRtlFreeUnicodeString)(PUNICODE_STRING);
+static NTSTATUS (WINAPI *pNtCreateEvent) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN, BOOLEAN);
+static NTSTATUS (WINAPI *pNtCreateMutant)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, BOOLEAN );
+static NTSTATUS (WINAPI *pNtOpenMutant) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES );
+static NTSTATUS (WINAPI *pNtCreateSemaphore)( PHANDLE, ACCESS_MASK,const POBJECT_ATTRIBUTES,LONG,LONG );
+static NTSTATUS (WINAPI *pNtCreateTimer) ( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, TIMER_TYPE );
+static NTSTATUS (WINAPI *pNtCreateSection)( PHANDLE, ACCESS_MASK, const POBJECT_ATTRIBUTES, const PLARGE_INTEGER,
+ ULONG, ULONG, HANDLE );
+static NTSTATUS (WINAPI *pNtOpenFile) ( PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG );
+static NTSTATUS (WINAPI *pNtClose) ( HANDLE );
+static NTSTATUS (WINAPI *pNtCreateNamedPipeFile)( PHANDLE, ULONG, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK,
+ ULONG, ULONG, ULONG, ULONG, ULONG, ULONG, ULONG, ULONG, ULONG, PLARGE_INTEGER );
+static NTSTATUS (WINAPI *pNtOpenDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
+static NTSTATUS (WINAPI *pNtCreateDirectoryObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
+static NTSTATUS (WINAPI *pNtOpenSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
+static NTSTATUS (WINAPI *pNtCreateSymbolicLinkObject)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PUNICODE_STRING);
+
+
+void test_case_sensitive (void)
+{
+ static const WCHAR buffer1[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','t','e','s','t',0};
+ static const WCHAR buffer2[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','T','e','s','t',0};
+ static const WCHAR buffer3[] = {'\\','B','a','s','e','N','a','m','e','d','O','b','j','e','c','t','s','\\','T','E','s','t',0};
+ static const WCHAR buffer4[] = {'\\','B','A','S','E','N','a','m','e','d','O','b','j','e','c','t','s','\\','t','e','s','t',0};
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING str;
+ HANDLE Event, Mutant, h;
+
+ pRtlInitUnicodeString(&str, buffer1);
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ status = pNtCreateMutant(&Mutant, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_SUCCESS, "Failed to create Mutant(%08lx)\n", status);
+
+ status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, FALSE, FALSE);
+ ok(status == STATUS_OBJECT_NAME_COLLISION,
+ "NtCreateEvent should have failed with STATUS_OBJECT_NAME_COLLISION got(%08lx)\n", status);
+
+ pRtlInitUnicodeString(&str, buffer2);
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ status = pNtCreateEvent(&Event, GENERIC_ALL, &attr, FALSE, FALSE);
+ ok(status == STATUS_SUCCESS, "Failed to create Event(%08lx)\n", status);
+
+ pRtlInitUnicodeString(&str, buffer3);
+ InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL);
+ status = pNtOpenMutant(&h, GENERIC_ALL, &attr);
+ ok(status == STATUS_OBJECT_TYPE_MISMATCH,
+ "NtOpenMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08lx)\n", status);
+
+ pNtClose(Mutant);
+
+ pRtlInitUnicodeString(&str, buffer4);
+ InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL);
+ status = pNtCreateMutant(&Mutant, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_NAME_COLLISION,
+ "NtCreateMutant should have failed with STATUS_OBJECT_NAME_COLLISION got(%08lx)\n", status);
+
+ status = pNtCreateEvent(&h, GENERIC_ALL, &attr, FALSE, FALSE);
+ ok(status == STATUS_OBJECT_NAME_COLLISION,
+ "NtCreateEvent should have failed with STATUS_OBJECT_NAME_COLLISION got(%08lx)\n", status);
+
+ attr.Attributes = 0;
+ status = pNtCreateMutant(&Mutant, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_PATH_NOT_FOUND,
+ "NtCreateMutant should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08lx)\n", status);
+
+ pNtClose(Event);
+}
+
+void test_namespace_pipe(void)
+{
+ static const WCHAR buffer1[] = {'\\','?','?','\\','P','I','P','E','\\','t','e','s','t','\\','p','i','p','e',0};
+ static const WCHAR buffer2[] = {'\\','?','?','\\','P','I','P','E','\\','T','E','S','T','\\','P','I','P','E',0};
+ static const WCHAR buffer3[] = {'\\','?','?','\\','p','i','p','e','\\','t','e','s','t','\\','p','i','p','e',0};
+ static const WCHAR buffer4[] = {'\\','?','?','\\','p','i','p','e','\\','t','e','s','t',0};
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING str;
+ IO_STATUS_BLOCK iosb;
+ NTSTATUS status;
+ LARGE_INTEGER timeout;
+ HANDLE pipe, h;
+
+ timeout.QuadPart = -10000;
+
+ pRtlInitUnicodeString(&str, buffer1);
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ status = pNtCreateNamedPipeFile(&pipe, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout);
+ ok(status == STATUS_SUCCESS, "Failed to create NamedPipe(%08lx)\n", status);
+
+ status = pNtCreateNamedPipeFile(&pipe, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout);
+ ok(status == STATUS_INSTANCE_NOT_AVAILABLE,
+ "NtCreateNamedPipeFile should have failed with STATUS_INSTANCE_NOT_AVAILABLE got(%08lx)\n", status);
+
+ pRtlInitUnicodeString(&str, buffer2);
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ status = pNtCreateNamedPipeFile(&pipe, GENERIC_READ|GENERIC_WRITE, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_CREATE, FILE_PIPE_FULL_DUPLEX, FALSE, FALSE, FALSE, 1, 256, 256, &timeout);
+ ok(status == STATUS_INSTANCE_NOT_AVAILABLE,
+ "NtCreateNamedPipeFile should have failed with STATUS_INSTANCE_NOT_AVAILABLE got(%08lx)\n", status);
+
+ attr.Attributes = OBJ_CASE_INSENSITIVE;
+ status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN);
+ ok(status == STATUS_SUCCESS, "Failed to open NamedPipe(%08lx)\n", status);
+ pNtClose(h);
+
+ pRtlInitUnicodeString(&str, buffer3);
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN);
+ ok(status == STATUS_OBJECT_PATH_NOT_FOUND || status == STATUS_PIPE_NOT_AVAILABLE,
+ "pNtOpenFile should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08lx)\n", status);
+
+ pRtlInitUnicodeString(&str, buffer4);
+ InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, 0, NULL);
+ status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN);
+ ok(status == STATUS_OBJECT_NAME_NOT_FOUND,
+ "pNtOpenFile should have failed with STATUS_OBJECT_NAME_NOT_FOUND got(%08lx)\n", status);
+
+ pNtClose(pipe);
+}
+
+#define DIRECTORY_QUERY (0x0001)
+#define SYMBOLIC_LINK_QUERY 0x0001
+
+#define DIR_TEST_CREATE_FAILURE(h,e) \
+ status = pNtCreateDirectoryObject(h, DIRECTORY_QUERY, &attr);\
+ ok(status == e,"NtCreateDirectoryObject should have failed with %s got(%08lx)\n", #e, status);
+#define DIR_TEST_OPEN_FAILURE(h,e) \
+ status = pNtOpenDirectoryObject(h, DIRECTORY_QUERY, &attr);\
+ ok(status == e,"NtOpenDirectoryObject should have failed with %s got(%08lx)\n", #e, status);
+#define DIR_TEST_CREATE_OPEN_FAILURE(h,n,e) \
+ pRtlCreateUnicodeStringFromAsciiz(&str, n);\
+ DIR_TEST_CREATE_FAILURE(h,e) DIR_TEST_OPEN_FAILURE(h,e)\
+ pRtlFreeUnicodeString(&str);
+
+#define DIR_TEST_CREATE_SUCCESS(h) \
+ status = pNtCreateDirectoryObject(h, DIRECTORY_QUERY, &attr); \
+ ok(status == STATUS_SUCCESS, "Failed to create Directory(%08lx)\n", status);
+#define DIR_TEST_OPEN_SUCCESS(h) \
+ status = pNtOpenDirectoryObject(h, DIRECTORY_QUERY, &attr); \
+ ok(status == STATUS_SUCCESS, "Failed to open Directory(%08lx)\n", status);
+#define DIR_TEST_CREATE_OPEN_SUCCESS(h,n) \
+ pRtlCreateUnicodeStringFromAsciiz(&str, n);\
+ DIR_TEST_CREATE_SUCCESS(h) pNtClose(h); DIR_TEST_OPEN_SUCCESS(h) pNtClose(h); \
+ pRtlFreeUnicodeString(&str);
+
+static void test_name_collisions(void)
+{
+ NTSTATUS status;
+ UNICODE_STRING str;
+ OBJECT_ATTRIBUTES attr;
+ HANDLE dir, h, h1, h2;
+ DWORD winerr;
+ LARGE_INTEGER size;
+
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\");
+ DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_NAME_COLLISION)
+ InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, 0, NULL);
+
+ DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_NAME_EXISTS)
+ pNtClose(h);
+ status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_TYPE_MISMATCH,
+ "NtCreateMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\??\\PIPE\\om.c-mutant");
+ status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_OBJECT_PATH_NOT_FOUND,
+ "NtCreateMutant should have failed with STATUS_OBJECT_TYPE_MISMATCH got(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects");
+ DIR_TEST_OPEN_SUCCESS(&dir)
+ pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-test");
+ InitializeObjectAttributes(&attr, &str, OBJ_OPENIF, dir, NULL);
+
+ h = CreateMutexA(NULL, FALSE, "om.c-test");
+ ok(h != 0, "CreateMutexA failed got ret=%p (%ld)\n", h, GetLastError());
+ status = pNtCreateMutant(&h1, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL,
+ "NtCreateMutant should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status);
+ h2 = CreateMutexA(NULL, FALSE, "om.c-test");
+ winerr = GetLastError();
+ ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS,
+ "CreateMutexA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr);
+ pNtClose(h);
+ pNtClose(h1);
+ pNtClose(h2);
+
+ h = CreateEventA(NULL, FALSE, FALSE, "om.c-test");
+ ok(h != 0, "CreateEventA failed got ret=%p (%ld)\n", h, GetLastError());
+ status = pNtCreateEvent(&h1, GENERIC_ALL, &attr, FALSE, FALSE);
+ ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL,
+ "NtCreateEvent should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status);
+ h2 = CreateEventA(NULL, FALSE, FALSE, "om.c-test");
+ winerr = GetLastError();
+ ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS,
+ "CreateEventA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr);
+ pNtClose(h);
+ pNtClose(h1);
+ pNtClose(h2);
+
+ h = CreateSemaphoreA(NULL, 1, 2, "om.c-test");
+ ok(h != 0, "CreateSemaphoreA failed got ret=%p (%ld)\n", h, GetLastError());
+ status = pNtCreateSemaphore(&h1, GENERIC_ALL, &attr, 1, 2);
+ ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL,
+ "NtCreateSemaphore should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status);
+ h2 = CreateSemaphoreA(NULL, 1, 2, "om.c-test");
+ winerr = GetLastError();
+ ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS,
+ "CreateSemaphoreA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr);
+ pNtClose(h);
+ pNtClose(h1);
+ pNtClose(h2);
+
+ h = CreateWaitableTimerA(NULL, TRUE, "om.c-test");
+ ok(h != 0, "CreateWaitableTimerA failed got ret=%p (%ld)\n", h, GetLastError());
+ status = pNtCreateTimer(&h1, GENERIC_ALL, &attr, NotificationTimer);
+ ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL,
+ "NtCreateTimer should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status);
+ h2 = CreateWaitableTimerA(NULL, TRUE, "om.c-test");
+ winerr = GetLastError();
+ ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS,
+ "CreateWaitableTimerA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr);
+ pNtClose(h);
+ pNtClose(h1);
+ pNtClose(h2);
+
+ h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, "om.c-test");
+ ok(h != 0, "CreateFileMappingA failed got ret=%p (%ld)\n", h, GetLastError());
+ size.u.LowPart = 256;
+ size.u.HighPart = 0;
+ status = pNtCreateSection(&h1, SECTION_MAP_WRITE, &attr, &size, PAGE_READWRITE, SEC_COMMIT, 0);
+ ok(status == STATUS_OBJECT_NAME_EXISTS && h1 != NULL,
+ "NtCreateSection should have succeeded with STATUS_OBJECT_NAME_EXISTS got(%08lx)\n", status);
+ h2 = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, "om.c-test");
+ winerr = GetLastError();
+ ok(h2 != 0 && winerr == ERROR_ALREADY_EXISTS,
+ "CreateFileMappingA should have succeeded with ERROR_ALREADY_EXISTS got ret=%p (%ld)\n", h2, winerr);
+ pNtClose(h);
+ pNtClose(h1);
+ pNtClose(h2);
+
+ pRtlFreeUnicodeString(&str);
+ pNtClose(dir);
+}
+
+void test_directory(void)
+{
+ NTSTATUS status;
+ UNICODE_STRING str;
+ OBJECT_ATTRIBUTES attr;
+ HANDLE dir, dir1, h;
+
+ /* No name and/or no attributes */
+ status = pNtCreateDirectoryObject(NULL, DIRECTORY_QUERY, &attr);
+ ok(status == STATUS_ACCESS_VIOLATION,
+ "NtCreateDirectoryObject should have failed with STATUS_ACCESS_VIOLATION got(%08lx)\n", status);
+ status = pNtOpenDirectoryObject(NULL, DIRECTORY_QUERY, &attr);
+ ok(status == STATUS_ACCESS_VIOLATION,
+ "NtOpenDirectoryObject should have failed with STATUS_ACCESS_VIOLATION got(%08lx)\n", status);
+
+ status = pNtCreateDirectoryObject(&h, DIRECTORY_QUERY, NULL);
+ ok(status == STATUS_SUCCESS, "Failed to create Directory without attributes(%08lx)\n", status);
+ pNtClose(h);
+ status = pNtOpenDirectoryObject(&h, DIRECTORY_QUERY, NULL);
+ ok(status == STATUS_INVALID_PARAMETER,
+ "NtOpenDirectoryObject should have failed with STATUS_INVALID_PARAMETER got(%08lx)\n", status);
+
+ InitializeObjectAttributes(&attr, NULL, 0, 0, NULL);
+ DIR_TEST_CREATE_SUCCESS(&dir)
+ DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+
+ /* Bad name */
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "");
+ DIR_TEST_CREATE_SUCCESS(&h)
+ pNtClose(h);
+ DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(dir);
+
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "BaseNamedObjects", STATUS_OBJECT_PATH_SYNTAX_BAD)
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\", STATUS_OBJECT_NAME_INVALID)
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\\\BaseNamedObjects", STATUS_OBJECT_NAME_INVALID)
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\\\om.c-test", STATUS_OBJECT_NAME_INVALID)
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND)
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test");
+ DIR_TEST_CREATE_SUCCESS(&h)
+ DIR_TEST_OPEN_SUCCESS(&dir1)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(h);
+ pNtClose(dir1);
+
+
+ /* Use of root directory */
+
+ /* Can't use symlinks as a directory */
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local");
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ status = pNtOpenSymbolicLinkObject(&dir, SYMBOLIC_LINK_QUERY, &attr);\
+ ok(status == STATUS_SUCCESS, "Failed to open SymbolicLink(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+ InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "one more level");
+ DIR_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_TYPE_MISMATCH)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(h);
+ pNtClose(dir);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects");
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ DIR_TEST_OPEN_SUCCESS(&dir)
+ pRtlFreeUnicodeString(&str);
+
+ InitializeObjectAttributes(&attr, NULL, 0, dir, NULL);
+ DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_NAME_INVALID)
+
+ InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
+ DIR_TEST_CREATE_OPEN_SUCCESS(&h, "")
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\", STATUS_OBJECT_PATH_SYNTAX_BAD)
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\om.c-test", STATUS_OBJECT_PATH_SYNTAX_BAD)
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "\\om.c-test\\", STATUS_OBJECT_PATH_SYNTAX_BAD)
+ DIR_TEST_CREATE_OPEN_FAILURE(&h, "om.c-test\\", STATUS_OBJECT_PATH_NOT_FOUND)
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-test");
+ DIR_TEST_CREATE_SUCCESS(&dir1)
+ DIR_TEST_OPEN_SUCCESS(&h)
+ pRtlFreeUnicodeString(&str);
+
+ pNtClose(h);
+ pNtClose(dir1);
+ pNtClose(dir);
+
+ /* Nested directories */
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\");
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ DIR_TEST_OPEN_SUCCESS(&dir)
+ InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
+ DIR_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(dir);
+
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test");
+ DIR_TEST_CREATE_SUCCESS(&dir)
+ pRtlFreeUnicodeString(&str);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\om.c-test\\one more level");
+ DIR_TEST_CREATE_SUCCESS(&h)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(h);
+ InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "one more level");
+ DIR_TEST_CREATE_SUCCESS(&h)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(h);
+
+ pNtClose(dir);
+
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Global\\om.c-test");
+ DIR_TEST_CREATE_SUCCESS(&dir)
+ pRtlFreeUnicodeString(&str);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects\\Local\\om.c-test\\one more level");
+ DIR_TEST_CREATE_SUCCESS(&h)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(h);
+ InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "one more level");
+ DIR_TEST_CREATE_SUCCESS(&dir)
+ pRtlFreeUnicodeString(&str);
+ pNtClose(h);
+
+ pNtClose(dir);
+
+
+ /* Create other objects using RootDirectory */
+
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects");
+ DIR_TEST_OPEN_SUCCESS(&dir)
+ pRtlFreeUnicodeString(&str);
+ InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
+
+ /* Test inavalid paths */
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\om.c-mutant");
+ status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD,
+ "NtCreateMutant should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\om.c-mutant\\");
+ status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD,
+ "NtCreateMutant should have failed with STATUS_OBJECT_PATH_SYNTAX_BAD got(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "om.c\\-mutant");
+ status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_OBJECT_PATH_NOT_FOUND,
+ "NtCreateMutant should have failed with STATUS_OBJECT_PATH_NOT_FOUND got(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "om.c-mutant");
+ status = pNtCreateMutant(&h, GENERIC_ALL, &attr, FALSE);
+ ok(status == STATUS_SUCCESS, "Failed to create Mutant(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+ pNtClose(h);
+
+ pNtClose(dir);
+}
+
+#define SYMLNK_TEST_CREATE_FAILURE(h,e) \
+ status = pNtCreateSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr, &target);\
+ ok(status == e,"NtCreateSymbolicLinkObject should have failed with %s got(%08lx)\n", #e, status);
+#define SYMLNK_TEST_OPEN_FAILURE(h,e) \
+ status = pNtOpenSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr);\
+ ok(status == e,"NtOpenSymbolicLinkObject should have failed with %s got(%08lx)\n", #e, status);
+#define SYMLNK_TEST_CREATE_OPEN_FAILURE(h,n,t,e) \
+ pRtlCreateUnicodeStringFromAsciiz(&str, n);\
+ pRtlCreateUnicodeStringFromAsciiz(&target, t);\
+ SYMLNK_TEST_CREATE_FAILURE(h,e)\
+ SYMLNK_TEST_OPEN_FAILURE(h,e)\
+ pRtlFreeUnicodeString(&target);\
+ pRtlFreeUnicodeString(&str);
+
+#define SYMLNK_TEST_CREATE_SUCCESS(h) \
+ status = pNtCreateSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr, &target); \
+ ok(status == STATUS_SUCCESS, "Failed to create SymbolicLink(%08lx)\n", status);
+#define SYMLNK_TEST_OPEN_SUCCESS(h) \
+ status = pNtOpenSymbolicLinkObject(h, SYMBOLIC_LINK_QUERY, &attr); \
+ ok(status == STATUS_SUCCESS, "Failed to open SymbolicLink(%08lx)\n", status);
+
+void test_symboliclink(void)
+{
+ NTSTATUS status;
+ UNICODE_STRING str, target;
+ OBJECT_ATTRIBUTES attr;
+ HANDLE dir, link, h;
+ IO_STATUS_BLOCK iosb;
+
+ /* No name and/or no attributes */
+ SYMLNK_TEST_CREATE_OPEN_FAILURE(NULL, "", "", STATUS_ACCESS_VIOLATION)
+
+ status = pNtCreateSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL, NULL);
+ ok(status == STATUS_ACCESS_VIOLATION,
+ "NtCreateSymbolicLinkObject should have failed with STATUS_ACCESS_VIOLATION got(%08lx)\n", status);
+ status = pNtOpenSymbolicLinkObject(&h, SYMBOLIC_LINK_QUERY, NULL);
+ ok(status == STATUS_INVALID_PARAMETER,
+ "NtOpenSymbolicLinkObject should have failed with STATUS_INVALID_PARAMETER got(%08lx)\n", status);
+
+ InitializeObjectAttributes(&attr, NULL, 0, 0, NULL);
+ SYMLNK_TEST_CREATE_FAILURE(&link, STATUS_INVALID_PARAMETER)
+ SYMLNK_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+
+ /* Bad name */
+ pRtlCreateUnicodeStringFromAsciiz(&target, "anywhere");
+ InitializeObjectAttributes(&attr, &str, 0, 0, NULL);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "");
+ SYMLNK_TEST_CREATE_SUCCESS(&link)
+ SYMLNK_TEST_OPEN_FAILURE(&h, STATUS_OBJECT_PATH_SYNTAX_BAD)
+ pNtClose(link);
+ pRtlFreeUnicodeString(&str);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\");
+ todo_wine {SYMLNK_TEST_CREATE_FAILURE(&h, STATUS_OBJECT_TYPE_MISMATCH)}
+ pRtlFreeUnicodeString(&str);
+ pRtlFreeUnicodeString(&target);
+
+ SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "BaseNamedObjects", "->Somewhere", STATUS_OBJECT_PATH_SYNTAX_BAD)
+ SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\", "->Somewhere", STATUS_OBJECT_NAME_INVALID)
+ SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\\\BaseNamedObjects", "->Somewhere", STATUS_OBJECT_NAME_INVALID)
+ SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\\\om.c-test", "->Somewhere", STATUS_OBJECT_NAME_INVALID)
+ SYMLNK_TEST_CREATE_OPEN_FAILURE(&h, "\\BaseNamedObjects\\om.c-test\\", "->Somewhere", STATUS_OBJECT_NAME_INVALID)
+
+
+ /* Compaund test */
+ pRtlCreateUnicodeStringFromAsciiz(&str, "\\BaseNamedObjects");
+ DIR_TEST_OPEN_SUCCESS(&dir)
+ pRtlFreeUnicodeString(&str);
+
+ InitializeObjectAttributes(&attr, &str, 0, dir, NULL);
+ pRtlCreateUnicodeStringFromAsciiz(&str, "Local\\test-link");
+ pRtlCreateUnicodeStringFromAsciiz(&target, "\\DosDevices");
+ SYMLNK_TEST_CREATE_SUCCESS(&link)
+ pRtlFreeUnicodeString(&str);
+ pRtlFreeUnicodeString(&target);
+
+ pRtlCreateUnicodeStringFromAsciiz(&str, "Local\\test-link\\PIPE");
+ status = pNtOpenFile(&h, GENERIC_READ, &attr, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN);
+ todo_wine ok(status == STATUS_SUCCESS, "Failed to open NamedPipe(%08lx)\n", status);
+ pRtlFreeUnicodeString(&str);
+
+ pNtClose(h);
+ pNtClose(link);
+ pNtClose(dir);
+}
+
+START_TEST(om)
+{
+ HMODULE hntdll = GetModuleHandleA("ntdll.dll");
+ if (hntdll)
+ {
+ pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
+ pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
+ pNtCreateEvent = (void *)GetProcAddress(hntdll, "NtCreateEvent");
+ pNtCreateMutant = (void *)GetProcAddress(hntdll, "NtCreateMutant");
+ pNtOpenMutant = (void *)GetProcAddress(hntdll, "NtOpenMutant");
+ pNtOpenFile = (void *)GetProcAddress(hntdll, "NtOpenFile");
+ pNtClose = (void *)GetProcAddress(hntdll, "NtClose");
+ pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
+ pNtCreateNamedPipeFile = (void *)GetProcAddress(hntdll, "NtCreateNamedPipeFile");
+ pNtOpenDirectoryObject = (void *)GetProcAddress(hntdll, "NtOpenDirectoryObject");
+ pNtCreateDirectoryObject= (void *)GetProcAddress(hntdll, "NtCreateDirectoryObject");
+ pNtOpenSymbolicLinkObject = (void *)GetProcAddress(hntdll, "NtOpenSymbolicLinkObject");
+ pNtCreateSymbolicLinkObject = (void *)GetProcAddress(hntdll, "NtCreateSymbolicLinkObject");
+ pNtCreateSemaphore = (void *)GetProcAddress(hntdll, "NtCreateSemaphore");
+ pNtCreateTimer = (void *)GetProcAddress(hntdll, "NtCreateTimer");
+ pNtCreateSection = (void *)GetProcAddress(hntdll, "NtCreateSection");
+
+ test_case_sensitive();
+ test_namespace_pipe();
+ test_name_collisions();
+ test_directory();
+ test_symboliclink();
+ }
+}
--- /dev/null
+/*
+ * Unit test suite for ntdll path functions
+ *
+ * Copyright 2002 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "ntdll_test.h"
+
+static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)( LPWSTR dst, DWORD dstlen, LPDWORD reslen,
+ LPCSTR src, DWORD srclen );
+static NTSTATUS (WINAPI *pRtlUnicodeToMultiByteN)(LPSTR,DWORD,LPDWORD,LPCWSTR,DWORD);
+static UINT (WINAPI *pRtlDetermineDosPathNameType_U)( PCWSTR path );
+static ULONG (WINAPI *pRtlIsDosDeviceName_U)( PCWSTR dos_name );
+static NTSTATUS (WINAPI *pRtlOemStringToUnicodeString)(UNICODE_STRING *, const STRING *, BOOLEAN );
+static BOOLEAN (WINAPI *pRtlIsNameLegalDOS8Dot3)(const UNICODE_STRING*,POEM_STRING,PBOOLEAN);
+static DWORD (WINAPI *pRtlGetFullPathName_U)(const WCHAR*,ULONG,WCHAR*,WCHAR**);
+
+
+static void test_RtlDetermineDosPathNameType(void)
+{
+ struct test
+ {
+ const char *path;
+ UINT ret;
+ };
+
+ static const struct test tests[] =
+ {
+ { "\\\\foo", 1 },
+ { "//foo", 1 },
+ { "\\/foo", 1 },
+ { "/\\foo", 1 },
+ { "\\\\", 1 },
+ { "//", 1 },
+ { "c:\\foo", 2 },
+ { "c:/foo", 2 },
+ { "c://foo", 2 },
+ { "c:\\", 2 },
+ { "c:/", 2 },
+ { "c:foo", 3 },
+ { "c:f\\oo", 3 },
+ { "c:foo/bar", 3 },
+ { "\\foo", 4 },
+ { "/foo", 4 },
+ { "\\", 4 },
+ { "/", 4 },
+ { "foo", 5 },
+ { "", 5 },
+ { "\0:foo", 5 },
+ { "\\\\.\\foo", 6 },
+ { "//./foo", 6 },
+ { "/\\./foo", 6 },
+ { "\\\\.foo", 1 },
+ { "//.foo", 1 },
+ { "\\\\.", 7 },
+ { "//.", 7 },
+ { NULL, 0 }
+ };
+
+ const struct test *test;
+ WCHAR buffer[MAX_PATH];
+ UINT ret;
+
+ for (test = tests; test->path; test++)
+ {
+ pRtlMultiByteToUnicodeN( buffer, sizeof(buffer), NULL, test->path, strlen(test->path)+1 );
+ ret = pRtlDetermineDosPathNameType_U( buffer );
+ ok( ret == test->ret, "Wrong result %d/%d for %s\n", ret, test->ret, test->path );
+ }
+}
+
+
+static void test_RtlIsDosDeviceName(void)
+{
+ struct test
+ {
+ const char *path;
+ WORD pos;
+ WORD len;
+ };
+
+ static const struct test tests[] =
+ {
+ { "\\\\.\\CON", 8, 6 },
+ { "\\\\.\\con", 8, 6 },
+ { "\\\\.\\CON2", 0, 0 },
+ { "", 0, 0 },
+ { "\\\\foo\\nul", 0, 0 },
+ { "c:\\nul:", 6, 6 },
+ { "c:\\nul::", 0, 0 },
+ { "c:prn ", 4, 6 },
+ { "c:prn.......", 4, 6 },
+ { "c:prn... ...", 4, 6 },
+ { "c:NUL .... ", 0, 0 },
+ { "c: . . .", 0, 0 },
+ { "c:", 0, 0 },
+ { " . . . :", 0, 0 },
+ { ":", 0, 0 },
+ { "c:nul. . . :", 4, 6 },
+ { "c:nul . . :", 0, 0 },
+ { "c:nul0", 0, 0 },
+ { "c:prn:aaa", 0, 0 },
+ { "c:PRN:.txt", 4, 6 },
+ { "c:aux:.txt...", 4, 6 },
+ { "c:prn:.txt:", 4, 6 },
+ { "c:nul:aaa", 0, 0 },
+ { "con:", 0, 6 },
+ { "lpt1:", 0, 8 },
+ { "c:com5:", 4, 8 },
+ { "CoM4:", 0, 8 },
+ { "lpt9:", 0, 8 },
+ { "c:\\lpt0.txt", 0, 0 },
+ { "c:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\nul.txt", 1000, 6 },
+ { NULL, 0 }
+ };
+
+ const struct test *test;
+ WCHAR buffer[2000];
+ ULONG ret;
+
+ for (test = tests; test->path; test++)
+ {
+ pRtlMultiByteToUnicodeN( buffer, sizeof(buffer), NULL, test->path, strlen(test->path)+1 );
+ ret = pRtlIsDosDeviceName_U( buffer );
+ ok( ret == MAKELONG( test->len, test->pos ),
+ "Wrong result (%d,%d)/(%d,%d) for %s\n",
+ HIWORD(ret), LOWORD(ret), test->pos, test->len, test->path );
+ }
+}
+
+static void test_RtlIsNameLegalDOS8Dot3(void)
+{
+ struct test
+ {
+ const char *path;
+ BOOLEAN result;
+ BOOLEAN spaces;
+ };
+
+ static const struct test tests[] =
+ {
+ { "12345678", TRUE, FALSE },
+ { "123 5678", TRUE, TRUE },
+ { "12345678.", FALSE, 2 /*not set*/ },
+ { "1234 678.", FALSE, 2 /*not set*/ },
+ { "12345678.a", TRUE, FALSE },
+ { "12345678.a ", FALSE, 2 /*not set*/ },
+ { "12345678.a c", TRUE, TRUE },
+ { " 2345678.a ", FALSE, 2 /*not set*/ },
+ { "1 345678.abc", TRUE, TRUE },
+ { "1 8.a c", TRUE, TRUE },
+ { "1 3 5 7 .abc", FALSE, 2 /*not set*/ },
+ { "12345678. c", TRUE, TRUE },
+ { "123456789.a", FALSE, 2 /*not set*/ },
+ { "12345.abcd", FALSE, 2 /*not set*/ },
+ { "12345.ab d", FALSE, 2 /*not set*/ },
+ { ".abc", FALSE, 2 /*not set*/ },
+ { "12.abc.d", FALSE, 2 /*not set*/ },
+ { ".", TRUE, FALSE },
+ { "..", TRUE, FALSE },
+ { "...", FALSE, 2 /*not set*/ },
+ { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", FALSE, 2 /*not set*/ },
+ { NULL, 0 }
+ };
+
+ const struct test *test;
+ UNICODE_STRING ustr;
+ OEM_STRING oem, oem_ret;
+ WCHAR buffer[200];
+ char buff2[12];
+ BOOLEAN ret, spaces;
+
+ ustr.MaximumLength = sizeof(buffer);
+ ustr.Buffer = buffer;
+ for (test = tests; test->path; test++)
+ {
+ char path[100];
+ strcpy(path, test->path);
+ oem.Buffer = path;
+ oem.Length = strlen(test->path);
+ oem.MaximumLength = oem.Length + 1;
+ pRtlOemStringToUnicodeString( &ustr, &oem, FALSE );
+ spaces = 2;
+ oem_ret.Length = oem_ret.MaximumLength = sizeof(buff2);
+ oem_ret.Buffer = buff2;
+ ret = pRtlIsNameLegalDOS8Dot3( &ustr, &oem_ret, &spaces );
+ ok( ret == test->result, "Wrong result %d/%d for '%s'\n", ret, test->result, test->path );
+ ok( spaces == test->spaces, "Wrong spaces value %d/%d for '%s'\n", spaces, test->spaces, test->path );
+ if (strlen(test->path) <= 12)
+ {
+ char str[13];
+ int i;
+ strcpy( str, test->path );
+ for (i = 0; str[i]; i++) str[i] = toupper(str[i]);
+ ok( oem_ret.Length == strlen(test->path), "Wrong length %d/%d for '%s'\n",
+ oem_ret.Length, strlen(test->path), test->path );
+ ok( !memcmp( oem_ret.Buffer, str, oem_ret.Length ),
+ "Wrong string '%.*s'/'%s'\n", oem_ret.Length, oem_ret.Buffer, str );
+ }
+ }
+}
+static void test_RtlGetFullPathName_U(void)
+{
+ struct test
+ {
+ const char *path;
+ const char *rname;
+ const char *rfile;
+ };
+
+ static const struct test tests[] =
+ {
+ { "c:/test", "c:\\test", "test"},
+ { "c:/test ", "c:\\test", "test"},
+ { "c:/test.", "c:\\test", "test"},
+ { "c:/test .... .. ", "c:\\test", "test"},
+ { "c:/test/ .... .. ", "c:\\test\\", NULL},
+ { "c:/test/..", "c:\\", NULL},
+ { "c:/test/.. ", "c:\\test\\", NULL},
+ { "c:/TEST", "c:\\test", "test"},
+ { "c:/test/file", "c:\\test\\file", "file"},
+ { "c:/test./file", "c:\\test\\file", "file"},
+ { "c:/test../file", "c:\\test.\\file", "file"},
+ { "c:/test.. /file", "c:\\test.. \\file","file"},
+ { "c:/test/././file", "c:\\test\\file", "file"},
+ { "c:/test\\.\\.\\file", "c:\\test\\file", "file"},
+ { "c:/test/\\.\\.\\file", "c:\\test\\file", "file"},
+ { "c:/test\\\\.\\.\\file", "c:\\test\\file", "file"},
+ { "c:/test\\test1\\..\\.\\file", "c:\\test\\file", "file"},
+ { "c:///test\\.\\.\\file//", "c:\\test\\file\\", NULL},
+ { "c:///test\\..\\file\\..\\//", "c:\\", NULL},
+ { NULL, NULL, NULL}
+ };
+
+ const struct test *test;
+ WCHAR pathbufW[2*MAX_PATH], rbufferW[MAX_PATH];
+ CHAR rbufferA[MAX_PATH], rfileA[MAX_PATH];
+ ULONG ret;
+ WCHAR *file_part;
+ DWORD reslen;
+ UINT len;
+
+ for (test = tests; test->path; test++)
+ {
+ len= strlen(test->rname) * sizeof(WCHAR);
+ pRtlMultiByteToUnicodeN(pathbufW , sizeof(pathbufW), NULL, test->path, strlen(test->path)+1 );
+ ret = pRtlGetFullPathName_U( pathbufW,MAX_PATH, rbufferW, &file_part);
+ ok( ret == len, "Wrong result %ld/%d for \"%s\"\n", ret, len, test->path );
+ ok(pRtlUnicodeToMultiByteN(rbufferA,MAX_PATH,&reslen,rbufferW,(lstrlenW(rbufferW) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS,
+ "RtlUnicodeToMultiByteN failed\n");
+ ok(lstrcmpiA(rbufferA,test->rname) == 0, "Got \"%s\" expected \"%s\"\n",rbufferA,test->rname);
+ if (file_part)
+ {
+ ok(pRtlUnicodeToMultiByteN(rfileA,MAX_PATH,&reslen,file_part,(lstrlenW(file_part) + 1) * sizeof(WCHAR)) == STATUS_SUCCESS,
+ "RtlUnicodeToMultiByteN failed\n");
+ ok(test->rfile && !lstrcmpiA(rfileA,test->rfile), "Got \"%s\" expected \"%s\"\n",rfileA,test->rfile);
+ }
+ else
+ {
+ ok( !test->rfile, "Got NULL expected \"%s\"\n", test->rfile );
+ }
+ }
+
+}
+
+START_TEST(path)
+{
+ HMODULE mod = GetModuleHandleA("ntdll.dll");
+ pRtlMultiByteToUnicodeN = (void *)GetProcAddress(mod,"RtlMultiByteToUnicodeN");
+ pRtlUnicodeToMultiByteN = (void *)GetProcAddress(mod,"RtlUnicodeToMultiByteN");
+ pRtlDetermineDosPathNameType_U = (void *)GetProcAddress(mod,"RtlDetermineDosPathNameType_U");
+ pRtlIsDosDeviceName_U = (void *)GetProcAddress(mod,"RtlIsDosDeviceName_U");
+ pRtlOemStringToUnicodeString = (void *)GetProcAddress(mod,"RtlOemStringToUnicodeString");
+ pRtlIsNameLegalDOS8Dot3 = (void *)GetProcAddress(mod,"RtlIsNameLegalDOS8Dot3");
+ pRtlGetFullPathName_U = (void *)GetProcAddress(mod,"RtlGetFullPathName_U");
+ if (pRtlDetermineDosPathNameType_U)
+ test_RtlDetermineDosPathNameType();
+ if (pRtlIsDosDeviceName_U)
+ test_RtlIsDosDeviceName();
+ if (pRtlIsNameLegalDOS8Dot3)
+ test_RtlIsNameLegalDOS8Dot3();
+ if (pRtlGetFullPathName_U && pRtlMultiByteToUnicodeN)
+ test_RtlGetFullPathName_U();
+}
--- /dev/null
+/* Unit test suite for Ntdll Port API functions
+ *
+ * Copyright 2006 James Hawkins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "ntdll_test.h"
+#include "windows.h"
+
+#ifndef __WINE_WINTERNL_H
+
+typedef struct _CLIENT_ID
+{
+ HANDLE UniqueProcess;
+ HANDLE UniqueThread;
+} CLIENT_ID, *PCLIENT_ID;
+
+typedef struct _LPC_SECTION_WRITE
+{
+ ULONG Length;
+ HANDLE SectionHandle;
+ ULONG SectionOffset;
+ ULONG ViewSize;
+ PVOID ViewBase;
+ PVOID TargetViewBase;
+} LPC_SECTION_WRITE, *PLPC_SECTION_WRITE;
+
+typedef struct _LPC_SECTION_READ
+{
+ ULONG Length;
+ ULONG ViewSize;
+ PVOID ViewBase;
+} LPC_SECTION_READ, *PLPC_SECTION_READ;
+
+typedef struct _LPC_MESSAGE
+{
+ USHORT DataSize;
+ USHORT MessageSize;
+ USHORT MessageType;
+ USHORT VirtualRangesOffset;
+ CLIENT_ID ClientId;
+ ULONG MessageId;
+ ULONG SectionSize;
+ UCHAR Data[ANYSIZE_ARRAY];
+} LPC_MESSAGE, *PLPC_MESSAGE;
+
+#endif
+
+/* Types of LPC messages */
+#define UNUSED_MSG_TYPE 0
+#define LPC_REQUEST 1
+#define LPC_REPLY 2
+#define LPC_DATAGRAM 3
+#define LPC_LOST_REPLY 4
+#define LPC_PORT_CLOSED 5
+#define LPC_CLIENT_DIED 6
+#define LPC_EXCEPTION 7
+#define LPC_DEBUG_EVENT 8
+#define LPC_ERROR_EVENT 9
+#define LPC_CONNECTION_REQUEST 10
+
+static const WCHAR PORTNAME[] = {'\\','M','y','P','o','r','t',0};
+
+#define REQUEST1 "Request1"
+#define REQUEST2 "Request2"
+#define REPLY "Reply"
+
+#define MAX_MESSAGE_LEN 30
+
+UNICODE_STRING port;
+static char selfname[MAX_PATH];
+static int myARGC;
+static char** myARGV;
+
+/* Function pointers for ntdll calls */
+static HMODULE hntdll = 0;
+static NTSTATUS (WINAPI *pNtCompleteConnectPort)(HANDLE);
+static NTSTATUS (WINAPI *pNtAcceptConnectPort)(PHANDLE,ULONG,PLPC_MESSAGE,ULONG,
+ ULONG,PLPC_SECTION_READ);
+static NTSTATUS (WINAPI *pNtReplyPort)(HANDLE,PLPC_MESSAGE);
+static NTSTATUS (WINAPI *pNtReplyWaitReceivePort)(PHANDLE,PULONG,PLPC_MESSAGE,
+ PLPC_MESSAGE);
+static NTSTATUS (WINAPI *pNtCreatePort)(PHANDLE,POBJECT_ATTRIBUTES,ULONG,ULONG,ULONG);
+static NTSTATUS (WINAPI *pNtRequestWaitReplyPort)(HANDLE,PLPC_MESSAGE,PLPC_MESSAGE);
+static NTSTATUS (WINAPI *pNtRequestPort)(HANDLE,PLPC_MESSAGE);
+static NTSTATUS (WINAPI *pNtRegisterThreadTerminatePort)(HANDLE);
+static NTSTATUS (WINAPI *pNtConnectPort)(PHANDLE,PUNICODE_STRING,
+ PSECURITY_QUALITY_OF_SERVICE,
+ PLPC_SECTION_WRITE,PLPC_SECTION_READ,
+ PVOID,PVOID,PULONG);
+static NTSTATUS (WINAPI *pRtlInitUnicodeString)(PUNICODE_STRING,LPCWSTR);
+static NTSTATUS (WINAPI *pNtWaitForSingleObject)(HANDLE,BOOLEAN,PLARGE_INTEGER);
+
+static BOOL init_function_ptrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+
+ if (hntdll)
+ {
+ pNtCompleteConnectPort = (void *)GetProcAddress(hntdll, "NtCompleteConnectPort");
+ pNtAcceptConnectPort = (void *)GetProcAddress(hntdll, "NtAcceptConnectPort");
+ pNtReplyPort = (void *)GetProcAddress(hntdll, "NtReplyPort");
+ pNtReplyWaitReceivePort = (void *)GetProcAddress(hntdll, "NtReplyWaitReceivePort");
+ pNtCreatePort = (void *)GetProcAddress(hntdll, "NtCreatePort");
+ pNtRequestWaitReplyPort = (void *)GetProcAddress(hntdll, "NtRequestWaitReplyPort");
+ pNtRequestPort = (void *)GetProcAddress(hntdll, "NtRequestPort");
+ pNtRegisterThreadTerminatePort = (void *)GetProcAddress(hntdll, "NtRegisterThreadTerminatePort");
+ pNtConnectPort = (void *)GetProcAddress(hntdll, "NtConnectPort");
+ pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
+ pNtWaitForSingleObject = (void *)GetProcAddress(hntdll, "NtWaitForSingleObject");
+ }
+
+ if (!pNtCompleteConnectPort || !pNtAcceptConnectPort ||
+ !pNtReplyWaitReceivePort || !pNtCreatePort || !pNtRequestWaitReplyPort ||
+ !pNtRequestPort || !pNtRegisterThreadTerminatePort ||
+ !pNtConnectPort || !pRtlInitUnicodeString)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void ProcessConnectionRequest(PLPC_MESSAGE LpcMessage, PHANDLE pAcceptPortHandle)
+{
+ NTSTATUS status;
+
+ ok(LpcMessage->MessageType == LPC_CONNECTION_REQUEST,
+ "Expected LPC_CONNECTION_REQUEST, got %d\n", LpcMessage->MessageType);
+ ok(!*LpcMessage->Data, "Expected empty string!\n");
+
+ status = pNtAcceptConnectPort(pAcceptPortHandle, 0, LpcMessage, 1, 0, NULL);
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+
+ status = pNtCompleteConnectPort(*pAcceptPortHandle);
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+}
+
+static void ProcessLpcRequest(HANDLE PortHandle, PLPC_MESSAGE LpcMessage)
+{
+ NTSTATUS status;
+
+ ok(LpcMessage->MessageType == LPC_REQUEST,
+ "Expected LPC_REQUEST, got %d\n", LpcMessage->MessageType);
+ ok(!lstrcmp((LPSTR)LpcMessage->Data, REQUEST2),
+ "Expected %s, got %s\n", REQUEST2, LpcMessage->Data);
+
+ lstrcpy((LPSTR)LpcMessage->Data, REPLY);
+
+ status = pNtReplyPort(PortHandle, LpcMessage);
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+ ok(LpcMessage->MessageType == LPC_REQUEST,
+ "Expected LPC_REQUEST, got %d\n", LpcMessage->MessageType);
+ ok(!lstrcmp((LPSTR)LpcMessage->Data, REPLY),
+ "Expected %s, got %s\n", REPLY, LpcMessage->Data);
+}
+
+static DWORD WINAPI test_ports_client(LPVOID arg)
+{
+ SECURITY_QUALITY_OF_SERVICE sqos;
+ LPC_MESSAGE *LpcMessage, *out;
+ HANDLE PortHandle;
+ ULONG len, size;
+ NTSTATUS status;
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_STATIC_TRACKING;
+ sqos.EffectiveOnly = TRUE;
+
+ status = pNtConnectPort(&PortHandle, &port, &sqos, 0, 0, &len, NULL, NULL);
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+
+ status = pNtRegisterThreadTerminatePort(PortHandle);
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+
+ size = FIELD_OFFSET(LPC_MESSAGE, Data) + MAX_MESSAGE_LEN;
+ LpcMessage = HeapAlloc(GetProcessHeap(), 0, size);
+ out = HeapAlloc(GetProcessHeap(), 0, size);
+
+ memset(LpcMessage, 0, size);
+ LpcMessage->DataSize = lstrlen(REQUEST1) + 1;
+ LpcMessage->MessageSize = FIELD_OFFSET(LPC_MESSAGE, Data) + LpcMessage->DataSize;
+ lstrcpy((LPSTR)LpcMessage->Data, REQUEST1);
+
+ status = pNtRequestPort(PortHandle, LpcMessage);
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+ ok(LpcMessage->MessageType == 0, "Expected 0, got %d\n", LpcMessage->MessageType);
+ ok(!lstrcmp((LPSTR)LpcMessage->Data, REQUEST1),
+ "Expected %s, got %s\n", REQUEST1, LpcMessage->Data);
+
+ /* Fill in the message */
+ memset(LpcMessage, 0, size);
+ LpcMessage->DataSize = lstrlen(REQUEST2) + 1;
+ LpcMessage->MessageSize = FIELD_OFFSET(LPC_MESSAGE, Data) + LpcMessage->DataSize;
+ lstrcpy((LPSTR)LpcMessage->Data, REQUEST2);
+
+ /* Send the message and wait for the reply */
+ status = pNtRequestWaitReplyPort(PortHandle, LpcMessage, out);
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+ ok(!lstrcmp((LPSTR)out->Data, REPLY), "Expected %s, got %s\n", REPLY, out->Data);
+ ok(out->MessageType == LPC_REPLY, "Expected LPC_REPLY, got %d\n", out->MessageType);
+
+ return 0;
+}
+
+static void test_ports_server(void)
+{
+ OBJECT_ATTRIBUTES obj;
+ HANDLE PortHandle;
+ HANDLE AcceptPortHandle;
+ PLPC_MESSAGE LpcMessage;
+ ULONG size;
+ NTSTATUS status;
+ BOOL done = FALSE;
+
+ pRtlInitUnicodeString(&port, PORTNAME);
+
+ memset(&obj, 0, sizeof(OBJECT_ATTRIBUTES));
+ obj.Length = sizeof(OBJECT_ATTRIBUTES);
+ obj.ObjectName = &port;
+
+ status = pNtCreatePort(&PortHandle, &obj, 100, 100, 0);
+ todo_wine
+ {
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld\n", status);
+ }
+
+ size = FIELD_OFFSET(LPC_MESSAGE, Data) + MAX_MESSAGE_LEN;
+ LpcMessage = HeapAlloc(GetProcessHeap(), 0, size);
+ memset(LpcMessage, 0, size);
+
+ while (TRUE)
+ {
+ status = pNtReplyWaitReceivePort(PortHandle, NULL, NULL, LpcMessage);
+ todo_wine
+ {
+ ok(status == STATUS_SUCCESS, "Expected STATUS_SUCCESS, got %ld(%lx)\n", status, status);
+ }
+ /* STATUS_INVALID_HANDLE: win2k without admin rights will perform an
+ * endless loop here
+ */
+ if ((status == STATUS_NOT_IMPLEMENTED) ||
+ (status == STATUS_INVALID_HANDLE)) return;
+
+ switch (LpcMessage->MessageType)
+ {
+ case LPC_CONNECTION_REQUEST:
+ ProcessConnectionRequest(LpcMessage, &AcceptPortHandle);
+ break;
+
+ case LPC_REQUEST:
+ ProcessLpcRequest(PortHandle, LpcMessage);
+ done = TRUE;
+ break;
+
+ case LPC_DATAGRAM:
+ ok(!lstrcmp((LPSTR)LpcMessage->Data, REQUEST1),
+ "Expected %s, got %s\n", REQUEST1, LpcMessage->Data);
+ break;
+
+ case LPC_CLIENT_DIED:
+ ok(done, "Expected LPC request to be completed!\n");
+ return;
+
+ default:
+ ok(FALSE, "Unexpected message: %d\n", LpcMessage->MessageType);
+ break;
+ }
+ }
+}
+
+START_TEST(port)
+{
+ HANDLE thread;
+ DWORD id;
+
+ if (!init_function_ptrs())
+ return;
+
+ myARGC = winetest_get_mainargs(&myARGV);
+ strcpy(selfname, myARGV[0]);
+
+ thread = CreateThread(NULL, 0, test_ports_client, NULL, 0, &id);
+ ok(thread != NULL, "Expected non-NULL thread handle!\n");
+
+ test_ports_server();
+ CloseHandle(thread);
+}
--- /dev/null
+/* Unit test suite for Rtl* Registry API functions
+ *
+ * Copyright 2003 Thomas Mertes
+ * Copyright 2005 Brad DeMorrow
+ * Copyright 2006 Dmitry Philippov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTE: I don't test every RelativeTo value because it would be redundant, all calls go through
+ * helper function RTL_GetKeyHandle().--Brad DeMorrow
+ *
+ */
+
+#include "ntdll_test.h"
+#include "winternl.h"
+#include "stdio.h"
+#include "winnt.h"
+#include "winnls.h"
+#include "stdlib.h"
+
+#ifndef __WINE_WINTERNL_H
+
+/* RtlQueryRegistryValues structs and defines */
+#define RTL_REGISTRY_ABSOLUTE 0
+#define RTL_REGISTRY_SERVICES 1
+#define RTL_REGISTRY_CONTROL 2
+#define RTL_REGISTRY_WINDOWS_NT 3
+#define RTL_REGISTRY_DEVICEMAP 4
+#define RTL_REGISTRY_USER 5
+
+#define RTL_REGISTRY_HANDLE 0x40000000
+#define RTL_REGISTRY_OPTIONAL 0x80000000
+
+#define RTL_QUERY_REGISTRY_SUBKEY 0x00000001
+#define RTL_QUERY_REGISTRY_TOPKEY 0x00000002
+#define RTL_QUERY_REGISTRY_REQUIRED 0x00000004
+#define RTL_QUERY_REGISTRY_NOVALUE 0x00000008
+#define RTL_QUERY_REGISTRY_NOEXPAND 0x00000010
+#define RTL_QUERY_REGISTRY_DIRECT 0x00000020
+#define RTL_QUERY_REGISTRY_DELETE 0x00000040
+
+typedef NTSTATUS (WINAPI *PRTL_QUERY_REGISTRY_ROUTINE)( PCWSTR ValueName,
+ ULONG ValueType,
+ PVOID ValueData,
+ ULONG ValueLength,
+ PVOID Context,
+ PVOID EntryContext);
+
+typedef struct _RTL_QUERY_REGISTRY_TABLE {
+ PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
+ ULONG Flags;
+ PWSTR Name;
+ PVOID EntryContext;
+ ULONG DefaultType;
+ PVOID DefaultData;
+ ULONG DefaultLength;
+} RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;
+
+#define InitializeObjectAttributes(p,n,a,r,s) \
+ do { \
+ (p)->Length = sizeof(OBJECT_ATTRIBUTES); \
+ (p)->RootDirectory = r; \
+ (p)->Attributes = a; \
+ (p)->ObjectName = n; \
+ (p)->SecurityDescriptor = s; \
+ (p)->SecurityQualityOfService = NULL; \
+ } while (0)
+
+#endif
+
+static NTSTATUS (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
+static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING);
+static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
+static NTSTATUS (WINAPI * pRtlQueryRegistryValues)(IN ULONG, IN PCWSTR,IN PRTL_QUERY_REGISTRY_TABLE, IN PVOID,IN PVOID);
+static NTSTATUS (WINAPI * pRtlCheckRegistryKey)(IN ULONG,IN PWSTR);
+static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, OUT PHANDLE);
+static NTSTATUS (WINAPI * pNtOpenKey)(PHANDLE, IN ACCESS_MASK, IN POBJECT_ATTRIBUTES);
+static NTSTATUS (WINAPI * pNtClose)(IN HANDLE);
+static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
+static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE);
+static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
+ ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
+ PULONG dispos );
+static NTSTATUS (WINAPI * pNtSetValueKey)( HANDLE, const PUNICODE_STRING, ULONG,
+ ULONG, const PVOID, ULONG );
+static NTSTATUS (WINAPI * pNtQueryValueKey)( HANDLE,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,
+ void *,DWORD,DWORD * );
+static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(PUNICODE_STRING);
+static NTSTATUS (WINAPI * pRtlCreateUnicodeString)( PUNICODE_STRING, LPCWSTR);
+static NTSTATUS (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG);
+static NTSTATUS (WINAPI * pRtlAppendUnicodeToString)(PUNICODE_STRING, PCWSTR);
+static NTSTATUS (WINAPI * pRtlUnicodeStringToAnsiString)(PSTRING, PUNICODE_STRING, BOOL);
+static NTSTATUS (WINAPI * pRtlFreeHeap)(PVOID, ULONG, PVOID);
+static NTSTATUS (WINAPI * pRtlAllocateHeap)(PVOID,ULONG,ULONG);
+static NTSTATUS (WINAPI * pRtlZeroMemory)(PVOID, ULONG);
+
+static HMODULE hntdll = 0;
+static int CurrentTest = 0;
+static UNICODE_STRING winetestpath;
+
+#define NTDLL_GET_PROC(func) \
+ p ## func = (void*)GetProcAddress(hntdll, #func); \
+ if(!p ## func) { \
+ trace("GetProcAddress(%s) failed\n", #func); \
+ FreeLibrary(hntdll); \
+ return FALSE; \
+ }
+
+static BOOL InitFunctionPtrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ if(!hntdll) {
+ trace("Could not load ntdll.dll\n");
+ return FALSE;
+ }
+ if (hntdll)
+ {
+ NTDLL_GET_PROC(RtlCreateUnicodeStringFromAsciiz)
+ NTDLL_GET_PROC(RtlCreateUnicodeString)
+ NTDLL_GET_PROC(RtlFreeUnicodeString)
+ NTDLL_GET_PROC(NtDeleteValueKey)
+ NTDLL_GET_PROC(RtlQueryRegistryValues)
+ NTDLL_GET_PROC(RtlCheckRegistryKey)
+ NTDLL_GET_PROC(RtlOpenCurrentUser)
+ NTDLL_GET_PROC(NtClose)
+ NTDLL_GET_PROC(NtDeleteValueKey)
+ NTDLL_GET_PROC(NtCreateKey)
+ NTDLL_GET_PROC(NtDeleteKey)
+ NTDLL_GET_PROC(NtSetValueKey)
+ NTDLL_GET_PROC(NtQueryValueKey)
+ NTDLL_GET_PROC(NtOpenKey)
+ NTDLL_GET_PROC(RtlFormatCurrentUserKeyPath)
+ NTDLL_GET_PROC(RtlReAllocateHeap)
+ NTDLL_GET_PROC(RtlAppendUnicodeToString)
+ NTDLL_GET_PROC(RtlUnicodeStringToAnsiString)
+ NTDLL_GET_PROC(RtlFreeHeap)
+ NTDLL_GET_PROC(RtlAllocateHeap)
+ NTDLL_GET_PROC(RtlZeroMemory)
+ }
+ return TRUE;
+}
+#undef NTDLL_GET_PROC
+
+static NTSTATUS WINAPI QueryRoutine (IN PCWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData,
+ IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext)
+{
+ NTSTATUS ret = STATUS_SUCCESS;
+ LPSTR ValName = 0;
+ LPSTR ValData = 0;
+ int ValueNameLength = 0;
+ trace("**Test %d**\n", CurrentTest);
+
+ if(ValueName)
+ {
+ ValueNameLength = lstrlenW(ValueName);
+
+ ValName = (LPSTR)pRtlAllocateHeap(GetProcessHeap(), 0, ValueNameLength);
+ WideCharToMultiByte(0, 0, ValueName, ValueNameLength+1,ValName, ValueNameLength, 0, 0);
+
+ trace("ValueName: %s\n", ValName);
+ }
+ else
+ trace("ValueName: (null)\n");
+
+ if( ValueType == REG_SZ ||
+ ValueType == REG_MULTI_SZ ||
+ ValueType == REG_EXPAND_SZ )
+ {
+ ValData = (LPSTR)pRtlAllocateHeap(GetProcessHeap(), 0, ValueLength);
+ WideCharToMultiByte(0, 0, ValueData, ValueLength, ValData, ValueLength, 0, 0);
+ }
+
+ switch(ValueType)
+ {
+ case REG_NONE:
+ trace("ValueType: REG_NONE\n");
+ trace("ValueData: %d\n", (int)ValueData);
+ break;
+
+ case REG_BINARY:
+ trace("ValueType: REG_BINARY\n");
+ trace("ValueData: %d\n", *(unsigned int *)ValueData);
+ break;
+
+ case REG_SZ:
+ trace("ValueType: REG_SZ\n");
+ trace("ValueData: %s\n", ValData);
+ break;
+
+ case REG_MULTI_SZ:
+ trace("ValueType: REG_MULTI_SZ\n");
+ trace("ValueData: %s", (char*)ValData);
+ break;
+
+ case REG_EXPAND_SZ:
+ trace("ValueType: REG_EXPAND_SZ\n");
+ trace("ValueData: %s\n", (char*)ValData);
+ break;
+
+ case REG_DWORD:
+ trace("ValueType: REG_DWORD\n");
+ trace("ValueData: %d\n", *(unsigned int *)ValueData);
+ break;
+ };
+ trace("ValueLength: %d\n", (int)ValueLength);
+
+ if(CurrentTest == 0)
+ ok(1, "\n"); /*checks that QueryRoutine is called*/
+ if(CurrentTest > 7)
+ ok(!1, "Invalid Test Specified!\n");
+
+ CurrentTest++;
+
+ if(ValName)
+ pRtlFreeHeap(GetProcessHeap(), 0, ValName);
+
+ if(ValData)
+ pRtlFreeHeap(GetProcessHeap(), 0, ValData);
+
+ return ret;
+}
+
+static void test_RtlQueryRegistryValues(void)
+{
+
+ /*
+ ******************************
+ * QueryTable Flags *
+ ******************************
+ *RTL_QUERY_REGISTRY_SUBKEY * Name is the name of a subkey relative to Path
+ *RTL_QUERY_REGISTRY_TOPKEY * Resets location to original RelativeTo and Path
+ *RTL_QUERY_REGISTRY_REQUIRED * Key required. returns STATUS_OBJECT_NAME_NOT_FOUND if not present
+ *RTL_QUERY_REGISTRY_NOVALUE * We just want a call-back
+ *RTL_QUERY_REGISTRY_NOEXPAND * Don't expand the variables!
+ *RTL_QUERY_REGISTRY_DIRECT * Results of query will be stored in EntryContext(QueryRoutine ignored)
+ *RTL_QUERY_REGISTRY_DELETE * Delete value key after query
+ ******************************
+
+
+ **Test layout(numbered according to CurrentTest value)**
+ 0)NOVALUE Just make sure call-back works
+ 1)Null Name See if QueryRoutine is called for every value in current key
+ 2)SUBKEY See if we can use SUBKEY to change the current path on the fly
+ 3)REQUIRED Test for value that's not there
+ 4)NOEXPAND See if it will return multiple strings(no expand should split strings up)
+ 5)DIRECT Make it store data directly in EntryContext and not call QueryRoutine
+ 6)DefaultType Test return values when key isn't present
+ 7)DefaultValue Test Default Value returned with key isn't present(and no REQUIRED flag set)
+ 8)DefaultLength Test Default Length with DefaultType = REG_SZ
+ 9)DefaultLength Test Default Length with DefaultType = REG_MULTI_SZ
+ 10)DefaultLength Test Default Length with DefaultType = REG_EXPAND_SZ
+ 11)DefaultData Test whether DefaultData is used while DefaltType = REG_NONE(shouldn't be)
+ 12)Delete Try to delete value key
+
+ */
+ NTSTATUS status;
+ ULONG RelativeTo;
+
+ PRTL_QUERY_REGISTRY_TABLE QueryTable = NULL;
+ RelativeTo = RTL_REGISTRY_ABSOLUTE;/*Only using absolute - no need to test all relativeto variables*/
+
+ QueryTable = (PRTL_QUERY_REGISTRY_TABLE)pRtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_QUERY_REGISTRY_TABLE)*26);
+
+ pRtlZeroMemory( QueryTable, sizeof(RTL_QUERY_REGISTRY_TABLE) * 26);
+
+ QueryTable[0].QueryRoutine = QueryRoutine;
+ QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOVALUE;
+ QueryTable[0].Name = NULL;
+ QueryTable[0].EntryContext = NULL;
+ QueryTable[0].DefaultType = REG_BINARY;
+ QueryTable[0].DefaultData = NULL;
+ QueryTable[0].DefaultLength = 100;
+
+ QueryTable[1].QueryRoutine = QueryRoutine;
+ QueryTable[1].Flags = RTL_QUERY_REGISTRY_DELETE;
+ QueryTable[1].Name = L"multisztest";
+ QueryTable[1].EntryContext = 0;
+ QueryTable[1].DefaultType = REG_NONE;
+ QueryTable[1].DefaultData = NULL;
+ QueryTable[1].DefaultLength = 0;
+
+ QueryTable[2].QueryRoutine = QueryRoutine;
+ QueryTable[2].Flags = 0;
+ QueryTable[2].Name = NULL;
+ QueryTable[2].EntryContext = 0;
+ QueryTable[2].DefaultType = REG_NONE;
+ QueryTable[2].DefaultData = NULL;
+ QueryTable[2].DefaultLength = 0;
+
+ status = pRtlQueryRegistryValues(RelativeTo, winetestpath.Buffer, QueryTable, 0, 0);
+ ok(status == STATUS_SUCCESS, "RtlQueryRegistryValues return: 0x%08lx\n", status);
+
+ pRtlFreeHeap(GetProcessHeap(), 0, QueryTable);
+}
+
+static void test_NtCreateKey(void)
+{
+ /*Create WineTest*/
+ OBJECT_ATTRIBUTES attr;
+ HANDLE key;
+ ACCESS_MASK am = GENERIC_ALL;
+ NTSTATUS status;
+
+ InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
+ status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
+ ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08lx\n", status);
+
+ pNtClose(key);
+}
+
+static void test_NtSetValueKey(void)
+{
+ HANDLE key;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES attr;
+ ACCESS_MASK am = KEY_WRITE;
+ UNICODE_STRING ValName;
+ UNICODE_STRING ValNameMultiSz;
+ DWORD data = 711;
+ static const WCHAR DataMultiSz[] = {'T','e','s','t','V','a','l','u','e','1',0,
+ 'T','e','s','t','V','a','l','u','e','2',0,
+ 'T','e','s','t','V','a','l','u','e','3',0,0,
+ 'T','e','s','t','V','a','l','u','e','5',0,0,0};
+
+ pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
+ InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
+ status = pNtOpenKey(&key, am, &attr);
+ ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status);
+
+ status = pNtSetValueKey(key, &ValName, 0, REG_DWORD, &data, sizeof(data));
+ ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08lx\n", status);
+
+ pRtlFreeUnicodeString(&ValName);
+
+ pRtlCreateUnicodeStringFromAsciiz(&ValNameMultiSz, "multisztest");
+
+ status = pNtSetValueKey(key, &ValNameMultiSz, 0, REG_MULTI_SZ, (PVOID)DataMultiSz, sizeof(DataMultiSz));
+ ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08lx\n", status);
+
+ pRtlFreeUnicodeString(&ValNameMultiSz);
+
+ pNtClose(key);
+}
+
+static void test_RtlOpenCurrentUser(void)
+{
+ NTSTATUS status;
+ HANDLE handle;
+ status=pRtlOpenCurrentUser(KEY_READ, &handle);
+ ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08lx\n", status);
+ pNtClose(handle);
+}
+
+static void test_RtlCheckRegistryKey(void)
+{
+ NTSTATUS status;
+
+ status = pRtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, winetestpath.Buffer);
+ ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE: 0x%08lx\n", status);
+
+ status = pRtlCheckRegistryKey((RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL), winetestpath.Buffer);
+ ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and RTL_REGISTRY_OPTIONAL: 0x%08lx\n", status);
+}
+
+static void test_RtlQueryRegistryDelete(void)
+{
+ HANDLE key;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES attr;
+ UNICODE_STRING ValNameMultiSz;
+ WCHAR sBuf[255];
+ DWORD ValueSize;
+
+ InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
+ status = pNtOpenKey(&key, KEY_READ, &attr);
+ ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08lx\n", status);
+
+ pRtlCreateUnicodeStringFromAsciiz(&ValNameMultiSz, "multisztest");
+
+ status = pNtQueryValueKey(key, &ValNameMultiSz, 0, (void*)sBuf, sizeof(sBuf), &ValueSize);
+ ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey returns: 0x%08lx instead of STATUS_OBJECT_NAME_NOT_FOUND\n", status);
+
+ pRtlFreeUnicodeString(&ValNameMultiSz);
+
+ pNtClose(key);
+}
+
+static void test_NtDeleteKey(void)
+{
+ NTSTATUS status;
+ HANDLE hkey;
+ OBJECT_ATTRIBUTES attr;
+ ACCESS_MASK am = KEY_ALL_ACCESS;
+
+ InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
+ status = pNtOpenKey(&hkey, am, &attr);
+
+ status = pNtDeleteKey(hkey);
+ ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08lx\n", status);
+
+ pNtClose(hkey);
+}
+
+START_TEST(reg)
+{
+ static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t','\\',0};
+ if(!InitFunctionPtrs())
+ return;
+ pRtlFormatCurrentUserKeyPath(&winetestpath);
+ winetestpath.Buffer = (PWSTR)pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, winetestpath.Buffer,
+ winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR));
+ winetestpath.MaximumLength = winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR);
+
+ pRtlAppendUnicodeToString(&winetestpath, winetest);
+
+ test_NtCreateKey();
+ test_NtSetValueKey();
+ test_RtlCheckRegistryKey();
+ test_RtlOpenCurrentUser();
+ test_RtlQueryRegistryValues();
+ test_RtlQueryRegistryDelete();
+ test_NtDeleteKey();
+
+ pRtlFreeUnicodeString(&winetestpath);
+
+ FreeLibrary(hntdll);
+}
--- /dev/null
+/* Unit test suite for Rtl* API functions
+ *
+ * Copyright 2003 Thomas Mertes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as there is no import library for NTDLL on
+ * windows.
+ */
+
+#include <stdlib.h>
+
+#include "ntdll_test.h"
+
+#ifndef __WINE_WINTERNL_H
+
+typedef struct _RTL_HANDLE
+{
+ struct _RTL_HANDLE * Next;
+} RTL_HANDLE;
+
+typedef struct _RTL_HANDLE_TABLE
+{
+ ULONG MaxHandleCount;
+ ULONG HandleSize;
+ ULONG Unused[2];
+ PVOID NextFree;
+ PVOID FirstHandle;
+ PVOID ReservedMemory;
+ PVOID MaxHandle;
+} RTL_HANDLE_TABLE;
+
+#endif
+
+/* Function ptrs for ntdll calls */
+static HMODULE hntdll = 0;
+static SIZE_T (WINAPI *pRtlCompareMemory)(LPCVOID,LPCVOID,SIZE_T);
+static SIZE_T (WINAPI *pRtlCompareMemoryUlong)(PULONG, SIZE_T, ULONG);
+static VOID (WINAPI *pRtlMoveMemory)(LPVOID,LPCVOID,SIZE_T);
+static VOID (WINAPI *pRtlFillMemory)(LPVOID,SIZE_T,BYTE);
+static VOID (WINAPI *pRtlFillMemoryUlong)(LPVOID,SIZE_T,ULONG);
+static VOID (WINAPI *pRtlZeroMemory)(LPVOID,SIZE_T);
+static ULONGLONG (WINAPIV *pRtlUlonglongByteSwap)(ULONGLONG source);
+static ULONG (WINAPI *pRtlUniform)(PULONG);
+static ULONG (WINAPI *pRtlRandom)(PULONG);
+static BOOLEAN (WINAPI *pRtlAreAllAccessesGranted)(ACCESS_MASK, ACCESS_MASK);
+static BOOLEAN (WINAPI *pRtlAreAnyAccessesGranted)(ACCESS_MASK, ACCESS_MASK);
+static DWORD (WINAPI *pRtlComputeCrc32)(DWORD,const BYTE*,INT);
+static void (WINAPI * pRtlInitializeHandleTable)(ULONG, ULONG, RTL_HANDLE_TABLE *);
+static BOOLEAN (WINAPI * pRtlIsValidIndexHandle)(const RTL_HANDLE_TABLE *, ULONG, RTL_HANDLE **);
+static NTSTATUS (WINAPI * pRtlDestroyHandleTable)(RTL_HANDLE_TABLE *);
+static RTL_HANDLE * (WINAPI * pRtlAllocateHandle)(RTL_HANDLE_TABLE *, ULONG *);
+static BOOLEAN (WINAPI * pRtlFreeHandle)(RTL_HANDLE_TABLE *, RTL_HANDLE *);
+static NTSTATUS (WINAPI *pRtlAllocateAndInitializeSid)(PSID_IDENTIFIER_AUTHORITY,BYTE,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,DWORD,PSID*);
+static NTSTATUS (WINAPI *pRtlFreeSid)(PSID);
+#define LEN 16
+static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */
+static ULONG src_aligned_block[4];
+static ULONG dest_aligned_block[32];
+static const char *src = (const char*)src_aligned_block;
+static char* dest = (char*)dest_aligned_block;
+
+static void InitFunctionPtrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ ok(hntdll != 0, "LoadLibrary failed\n");
+ if (hntdll) {
+ pRtlCompareMemory = (void *)GetProcAddress(hntdll, "RtlCompareMemory");
+ pRtlCompareMemoryUlong = (void *)GetProcAddress(hntdll, "RtlCompareMemoryUlong");
+ pRtlMoveMemory = (void *)GetProcAddress(hntdll, "RtlMoveMemory");
+ pRtlFillMemory = (void *)GetProcAddress(hntdll, "RtlFillMemory");
+ pRtlFillMemoryUlong = (void *)GetProcAddress(hntdll, "RtlFillMemoryUlong");
+ pRtlZeroMemory = (void *)GetProcAddress(hntdll, "RtlZeroMemory");
+ pRtlUlonglongByteSwap = (void *)GetProcAddress(hntdll, "RtlUlonglongByteSwap");
+ pRtlUniform = (void *)GetProcAddress(hntdll, "RtlUniform");
+ pRtlRandom = (void *)GetProcAddress(hntdll, "RtlRandom");
+ pRtlAreAllAccessesGranted = (void *)GetProcAddress(hntdll, "RtlAreAllAccessesGranted");
+ pRtlAreAnyAccessesGranted = (void *)GetProcAddress(hntdll, "RtlAreAnyAccessesGranted");
+ pRtlComputeCrc32 = (void *)GetProcAddress(hntdll, "RtlComputeCrc32");
+ pRtlInitializeHandleTable = (void *)GetProcAddress(hntdll, "RtlInitializeHandleTable");
+ pRtlIsValidIndexHandle = (void *)GetProcAddress(hntdll, "RtlIsValidIndexHandle");
+ pRtlDestroyHandleTable = (void *)GetProcAddress(hntdll, "RtlDestroyHandleTable");
+ pRtlAllocateHandle = (void *)GetProcAddress(hntdll, "RtlAllocateHandle");
+ pRtlFreeHandle = (void *)GetProcAddress(hntdll, "RtlFreeHandle");
+ pRtlAllocateAndInitializeSid = (void *)GetProcAddress(hntdll, "RtlAllocateAndInitializeSid");
+ pRtlFreeSid = (void *)GetProcAddress(hntdll, "RtlFreeSid");
+ }
+ strcpy((char*)src_aligned_block, src_src);
+ ok(strlen(src) == 15, "Source must be 16 bytes long!\n");
+}
+
+#define COMP(str1,str2,cmplen,len) size = pRtlCompareMemory(str1, str2, cmplen); \
+ ok(size == len, "Expected %ld, got %ld\n", size, (SIZE_T)len)
+
+static void test_RtlCompareMemory(void)
+{
+ SIZE_T size;
+
+ if (!pRtlCompareMemory)
+ return;
+
+ strcpy(dest, src);
+
+ COMP(src,src,0,0);
+ COMP(src,src,LEN,LEN);
+ dest[0] = 'x';
+ COMP(src,dest,LEN,0);
+}
+
+static void test_RtlCompareMemoryUlong(void)
+{
+ ULONG a[10];
+ ULONG result;
+
+ a[0]= 0x0123;
+ a[1]= 0x4567;
+ a[2]= 0x89ab;
+ a[3]= 0xcdef;
+ result = pRtlCompareMemoryUlong(a, 0, 0x0123);
+ ok(result == 0, "RtlCompareMemoryUlong(%p, 0, 0x0123) returns %lu, expected 0\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 3, 0x0123);
+ ok(result == 0, "RtlCompareMemoryUlong(%p, 3, 0x0123) returns %lu, expected 0\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 4, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 4, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 5, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 5, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 7, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 7, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 8, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 8, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 9, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 9, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 4, 0x0127);
+ ok(result == 0, "RtlCompareMemoryUlong(%p, 4, 0x0127) returns %lu, expected 0\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 4, 0x7123);
+ ok(result == 0, "RtlCompareMemoryUlong(%p, 4, 0x7123) returns %lu, expected 0\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 16, 0x4567);
+ ok(result == 0, "RtlCompareMemoryUlong(%p, 16, 0x4567) returns %lu, expected 0\n", a, result);
+
+ a[1]= 0x0123;
+ result = pRtlCompareMemoryUlong(a, 3, 0x0123);
+ ok(result == 0, "RtlCompareMemoryUlong(%p, 3, 0x0123) returns %lu, expected 0\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 4, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 4, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 5, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 5, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 7, 0x0123);
+ ok(result == 4, "RtlCompareMemoryUlong(%p, 7, 0x0123) returns %lu, expected 4\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 8, 0x0123);
+ ok(result == 8, "RtlCompareMemoryUlong(%p, 8, 0x0123) returns %lu, expected 8\n", a, result);
+ result = pRtlCompareMemoryUlong(a, 9, 0x0123);
+ ok(result == 8, "RtlCompareMemoryUlong(%p, 9, 0x0123) returns %lu, expected 8\n", a, result);
+}
+
+#define COPY(len) memset(dest,0,sizeof(dest_aligned_block)); pRtlMoveMemory(dest, src, len)
+#define CMP(str) ok(strcmp(dest,str) == 0, "Expected '%s', got '%s'\n", str, dest)
+
+static void test_RtlMoveMemory(void)
+{
+ if (!pRtlMoveMemory)
+ return;
+
+ /* Length should be in bytes and not rounded. Use strcmp to ensure we
+ * didn't write past the end (it checks for the final NUL left by memset)
+ */
+ COPY(0); CMP("");
+ COPY(1); CMP("T");
+ COPY(2); CMP("Th");
+ COPY(3); CMP("Thi");
+ COPY(4); CMP("This");
+ COPY(5); CMP("This ");
+ COPY(6); CMP("This i");
+ COPY(7); CMP("This is");
+ COPY(8); CMP("This is ");
+ COPY(9); CMP("This is a");
+
+ /* Overlapping */
+ strcpy(dest, src); pRtlMoveMemory(dest, dest + 1, strlen(src) - 1);
+ CMP("his is a test!!");
+ strcpy(dest, src); pRtlMoveMemory(dest + 1, dest, strlen(src));
+ CMP("TThis is a test!");
+}
+
+#define FILL(len) memset(dest,0,sizeof(dest_aligned_block)); strcpy(dest, src); pRtlFillMemory(dest,len,'x')
+
+static void test_RtlFillMemory(void)
+{
+ if (!pRtlFillMemory)
+ return;
+
+ /* Length should be in bytes and not rounded. Use strcmp to ensure we
+ * didn't write past the end (the remainder of the string should match)
+ */
+ FILL(0); CMP("This is a test!");
+ FILL(1); CMP("xhis is a test!");
+ FILL(2); CMP("xxis is a test!");
+ FILL(3); CMP("xxxs is a test!");
+ FILL(4); CMP("xxxx is a test!");
+ FILL(5); CMP("xxxxxis a test!");
+ FILL(6); CMP("xxxxxxs a test!");
+ FILL(7); CMP("xxxxxxx a test!");
+ FILL(8); CMP("xxxxxxxxa test!");
+ FILL(9); CMP("xxxxxxxxx test!");
+}
+
+#define LFILL(len) memset(dest,0,sizeof(dest_aligned_block)); strcpy(dest, src); pRtlFillMemoryUlong(dest,len,val)
+
+static void test_RtlFillMemoryUlong(void)
+{
+ ULONG val = ('x' << 24) | ('x' << 16) | ('x' << 8) | 'x';
+ if (!pRtlFillMemoryUlong)
+ return;
+
+ /* Length should be in bytes and not rounded. Use strcmp to ensure we
+ * didn't write past the end (the remainder of the string should match)
+ */
+ LFILL(0); CMP("This is a test!");
+ LFILL(1); CMP("This is a test!");
+ LFILL(2); CMP("This is a test!");
+ LFILL(3); CMP("This is a test!");
+ LFILL(4); CMP("xxxx is a test!");
+ LFILL(5); CMP("xxxx is a test!");
+ LFILL(6); CMP("xxxx is a test!");
+ LFILL(7); CMP("xxxx is a test!");
+ LFILL(8); CMP("xxxxxxxxa test!");
+ LFILL(9); CMP("xxxxxxxxa test!");
+}
+
+#define ZERO(len) memset(dest,0,sizeof(dest_aligned_block)); strcpy(dest, src); pRtlZeroMemory(dest,len)
+#define MCMP(str) ok(memcmp(dest,str,LEN) == 0, "Memcmp failed\n")
+
+static void test_RtlZeroMemory(void)
+{
+ if (!pRtlZeroMemory)
+ return;
+
+ /* Length should be in bytes and not rounded. */
+ ZERO(0); MCMP("This is a test!");
+ ZERO(1); MCMP("\0his is a test!");
+ ZERO(2); MCMP("\0\0is is a test!");
+ ZERO(3); MCMP("\0\0\0s is a test!");
+ ZERO(4); MCMP("\0\0\0\0 is a test!");
+ ZERO(5); MCMP("\0\0\0\0\0is a test!");
+ ZERO(6); MCMP("\0\0\0\0\0\0s a test!");
+ ZERO(7); MCMP("\0\0\0\0\0\0\0 a test!");
+ ZERO(8); MCMP("\0\0\0\0\0\0\0\0a test!");
+ ZERO(9); MCMP("\0\0\0\0\0\0\0\0\0 test!");
+}
+
+static void test_RtlUlonglongByteSwap(void)
+{
+ ULONGLONG result;
+
+ result = pRtlUlonglongByteSwap( ((ULONGLONG)0x76543210 << 32) | 0x87654321 );
+ ok( (((ULONGLONG)0x21436587 << 32) | 0x10325476) == result,
+ "RtlUlonglongByteSwap(0x7654321087654321) returns 0x%llx, expected 0x2143658710325476\n",
+ result);
+}
+
+
+static void test_RtlUniform(void)
+{
+ ULONGLONG num;
+ ULONG seed;
+ ULONG seed_bak;
+ ULONG expected;
+ ULONG result;
+
+/*
+ * According to the documentation RtlUniform is using D.H. Lehmer's 1948
+ * algorithm. This algorithm is:
+ *
+ * seed = (seed * const_1 + const_2) % const_3;
+ *
+ * According to the documentation the random number is distributed over
+ * [0..MAXLONG]. Therefore const_3 is MAXLONG + 1:
+ *
+ * seed = (seed * const_1 + const_2) % (MAXLONG + 1);
+ *
+ * Because MAXLONG is 0x7fffffff (and MAXLONG + 1 is 0x80000000) the
+ * algorithm can be expressed without division as:
+ *
+ * seed = (seed * const_1 + const_2) & MAXLONG;
+ *
+ * To find out const_2 we just call RtlUniform with seed set to 0:
+ */
+ seed = 0;
+ expected = 0x7fffffc3;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 0)) returns %lx, expected %lx\n",
+ result, expected);
+/*
+ * The algorithm is now:
+ *
+ * seed = (seed * const_1 + 0x7fffffc3) & MAXLONG;
+ *
+ * To find out const_1 we can use:
+ *
+ * const_1 = RtlUniform(1) - 0x7fffffc3;
+ *
+ * If that does not work a search loop can try all possible values of
+ * const_1 and compare to the result to RtlUniform(1).
+ * This way we find out that const_1 is 0xffffffed.
+ *
+ * For seed = 1 the const_2 is 0x7fffffc4:
+ */
+ seed = 1;
+ expected = seed * 0xffffffed + 0x7fffffc3 + 1;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 1)) returns %lx, expected %lx\n",
+ result, expected);
+/*
+ * For seed = 2 the const_2 is 0x7fffffc3:
+ */
+ seed = 2;
+ expected = seed * 0xffffffed + 0x7fffffc3;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 2)) returns %lx, expected %lx\n",
+ result, expected);
+/*
+ * More tests show that if seed is odd the result must be incremented by 1:
+ */
+ seed = 3;
+ expected = seed * 0xffffffed + 0x7fffffc3 + (seed & 1);
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 2)) returns %lx, expected %lx\n",
+ result, expected);
+
+ seed = 0x6bca1aa;
+ expected = seed * 0xffffffed + 0x7fffffc3;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 0x6bca1aa)) returns %lx, expected %lx\n",
+ result, expected);
+
+ seed = 0x6bca1ab;
+ expected = seed * 0xffffffed + 0x7fffffc3 + 1;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 0x6bca1ab)) returns %lx, expected %lx\n",
+ result, expected);
+/*
+ * When seed is 0x6bca1ac there is an exception:
+ */
+ seed = 0x6bca1ac;
+ expected = seed * 0xffffffed + 0x7fffffc3 + 2;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 0x6bca1ac)) returns %lx, expected %lx\n",
+ result, expected);
+/*
+ * Note that up to here const_3 is not used
+ * (the highest bit of the result is not set).
+ *
+ * Starting with 0x6bca1ad: If seed is even the result must be incremented by 1:
+ */
+ seed = 0x6bca1ad;
+ expected = (seed * 0xffffffed + 0x7fffffc3) & MAXLONG;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 0x6bca1ad)) returns %lx, expected %lx\n",
+ result, expected);
+
+ seed = 0x6bca1ae;
+ expected = (seed * 0xffffffed + 0x7fffffc3 + 1) & MAXLONG;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "RtlUniform(&seed (seed == 0x6bca1ae)) returns %lx, expected %lx\n",
+ result, expected);
+/*
+ * There are several ranges where for odd or even seed the result must be
+ * incremented by 1. You can see this ranges in the following test.
+ *
+ * For a full test use one of the following loop heads:
+ *
+ * for (num = 0; num <= 0xffffffff; num++) {
+ * seed = num;
+ * ...
+ *
+ * seed = 0;
+ * for (num = 0; num <= 0xffffffff; num++) {
+ * ...
+ */
+ seed = 0;
+ for (num = 0; num <= 100000; num++) {
+
+ expected = seed * 0xffffffed + 0x7fffffc3;
+ if (seed < 0x6bca1ac) {
+ expected = expected + (seed & 1);
+ } else if (seed == 0x6bca1ac) {
+ expected = (expected + 2) & MAXLONG;
+ } else if (seed < 0xd79435c) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x1435e50b) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x1af286ba) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x21af2869) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x286bca18) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x2f286bc7) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x35e50d77) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x3ca1af26) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x435e50d5) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x4a1af284) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x50d79433) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x579435e2) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x5e50d792) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x650d7941) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x6bca1af0) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x7286bc9f) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x79435e4e) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x7ffffffd) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x86bca1ac) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed == 0x86bca1ac) {
+ expected = (expected + 1) & MAXLONG;
+ } else if (seed < 0x8d79435c) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0x9435e50b) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0x9af286ba) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xa1af2869) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0xa86bca18) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xaf286bc7) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed == 0xaf286bc7) {
+ expected = (expected + 2) & MAXLONG;
+ } else if (seed < 0xb5e50d77) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xbca1af26) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0xc35e50d5) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xca1af284) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0xd0d79433) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xd79435e2) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0xde50d792) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xe50d7941) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0xebca1af0) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xf286bc9f) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else if (seed < 0xf9435e4e) {
+ expected = expected + (seed & 1);
+ } else if (seed < 0xfffffffd) {
+ expected = (expected + (~seed & 1)) & MAXLONG;
+ } else {
+ expected = expected + (seed & 1);
+ } /* if */
+ seed_bak = seed;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "test: %llu RtlUniform(&seed (seed == %lx)) returns %lx, expected %lx\n",
+ num, seed_bak, result, expected);
+ ok(seed == expected,
+ "test: %llu RtlUniform(&seed (seed == %lx)) sets seed to %lx, expected %lx\n",
+ num, seed_bak, seed, expected);
+ } /* for */
+/*
+ * Further investigation shows: In the different regions the highest bit
+ * is set or cleared when even or odd seeds need an increment by 1.
+ * This leads to a simplified algorithm:
+ *
+ * seed = seed * 0xffffffed + 0x7fffffc3;
+ * if (seed == 0xffffffff || seed == 0x7ffffffe) {
+ * seed = (seed + 2) & MAXLONG;
+ * } else if (seed == 0x7fffffff) {
+ * seed = 0;
+ * } else if ((seed & 0x80000000) == 0) {
+ * seed = seed + (~seed & 1);
+ * } else {
+ * seed = (seed + (seed & 1)) & MAXLONG;
+ * }
+ *
+ * This is also the algorithm used for RtlUniform of wine (see dlls/ntdll/rtl.c).
+ *
+ * Now comes the funny part:
+ * It took me one weekend, to find the complicated algorithm and one day more,
+ * to find the simplified algorithm. Several weeks later I found out: The value
+ * MAXLONG (=0x7fffffff) is never returned, neither with the native function
+ * nor with the simplified algorithm. In reality the native function and our
+ * function return a random number distributed over [0..MAXLONG-1]. Note
+ * that this is different from what native documentation states [0..MAXLONG].
+ * Expressed with D.H. Lehmer's 1948 algorithm it looks like:
+ *
+ * seed = (seed * const_1 + const_2) % MAXLONG;
+ *
+ * Further investigations show that the real algorithm is:
+ *
+ * seed = (seed * 0x7fffffed + 0x7fffffc3) % MAXLONG;
+ *
+ * This is checked with the test below:
+ */
+ seed = 0;
+ for (num = 0; num <= 100000; num++) {
+ expected = (seed * 0x7fffffed + 0x7fffffc3) % 0x7fffffff;
+ seed_bak = seed;
+ result = pRtlUniform(&seed);
+ ok(result == expected,
+ "test: %llu RtlUniform(&seed (seed == %lx)) returns %lx, expected %lx\n",
+ num, seed_bak, result, expected);
+ ok(seed == expected,
+ "test: %llu RtlUniform(&seed (seed == %lx)) sets seed to %lx, expected %lx\n",
+ num, seed_bak, seed, expected);
+ } /* for */
+/*
+ * More tests show that RtlUniform does not return 0x7ffffffd for seed values
+ * in the range [0..MAXLONG-1]. Additionally 2 is returned twice. This shows
+ * that there is more than one cycle of generated randon numbers ...
+ */
+}
+
+
+static ULONG WINAPI my_RtlRandom(PULONG seed)
+{
+ static ULONG saved_value[128] =
+ { /* 0 */ 0x4c8bc0aa, 0x4c022957, 0x2232827a, 0x2f1e7626, 0x7f8bdafb, 0x5c37d02a, 0x0ab48f72, 0x2f0c4ffa,
+ /* 8 */ 0x290e1954, 0x6b635f23, 0x5d3885c0, 0x74b49ff8, 0x5155fa54, 0x6214ad3f, 0x111e9c29, 0x242a3a09,
+ /* 16 */ 0x75932ae1, 0x40ac432e, 0x54f7ba7a, 0x585ccbd5, 0x6df5c727, 0x0374dad1, 0x7112b3f1, 0x735fc311,
+ /* 24 */ 0x404331a9, 0x74d97781, 0x64495118, 0x323e04be, 0x5974b425, 0x4862e393, 0x62389c1d, 0x28a68b82,
+ /* 32 */ 0x0f95da37, 0x7a50bbc6, 0x09b0091c, 0x22cdb7b4, 0x4faaed26, 0x66417ccd, 0x189e4bfa, 0x1ce4e8dd,
+ /* 40 */ 0x5274c742, 0x3bdcf4dc, 0x2d94e907, 0x32eac016, 0x26d33ca3, 0x60415a8a, 0x31f57880, 0x68c8aa52,
+ /* 48 */ 0x23eb16da, 0x6204f4a1, 0x373927c1, 0x0d24eb7c, 0x06dd7379, 0x2b3be507, 0x0f9c55b1, 0x2c7925eb,
+ /* 56 */ 0x36d67c9a, 0x42f831d9, 0x5e3961cb, 0x65d637a8, 0x24bb3820, 0x4d08e33d, 0x2188754f, 0x147e409e,
+ /* 64 */ 0x6a9620a0, 0x62e26657, 0x7bd8ce81, 0x11da0abb, 0x5f9e7b50, 0x23e444b6, 0x25920c78, 0x5fc894f0,
+ /* 72 */ 0x5e338cbb, 0x404237fd, 0x1d60f80f, 0x320a1743, 0x76013d2b, 0x070294ee, 0x695e243b, 0x56b177fd,
+ /* 80 */ 0x752492e1, 0x6decd52f, 0x125f5219, 0x139d2e78, 0x1898d11e, 0x2f7ee785, 0x4db405d8, 0x1a028a35,
+ /* 88 */ 0x63f6f323, 0x1f6d0078, 0x307cfd67, 0x3f32a78a, 0x6980796c, 0x462b3d83, 0x34b639f2, 0x53fce379,
+ /* 96 */ 0x74ba50f4, 0x1abc2c4b, 0x5eeaeb8d, 0x335a7a0d, 0x3973dd20, 0x0462d66b, 0x159813ff, 0x1e4643fd,
+ /* 104 */ 0x06bc5c62, 0x3115e3fc, 0x09101613, 0x47af2515, 0x4f11ec54, 0x78b99911, 0x3db8dd44, 0x1ec10b9b,
+ /* 112 */ 0x5b5506ca, 0x773ce092, 0x567be81a, 0x5475b975, 0x7a2cde1a, 0x494536f5, 0x34737bb4, 0x76d9750b,
+ /* 120 */ 0x2a1f6232, 0x2e49644d, 0x7dddcbe7, 0x500cebdb, 0x619dab9e, 0x48c626fe, 0x1cda3193, 0x52dabe9d };
+ ULONG rand;
+ int pos;
+ ULONG result;
+
+ rand = (*seed * 0x7fffffed + 0x7fffffc3) % 0x7fffffff;
+ *seed = (rand * 0x7fffffed + 0x7fffffc3) % 0x7fffffff;
+ pos = *seed & 0x7f;
+ result = saved_value[pos];
+ saved_value[pos] = rand;
+ return(result);
+}
+
+
+static void test_RtlRandom(void)
+{
+ ULONGLONG num;
+ ULONG seed;
+ ULONG seed_bak;
+ ULONG seed_expected;
+ ULONG result;
+ ULONG result_expected;
+
+/*
+ * Unlike RtlUniform, RtlRandom is not documented. We guess that for
+ * RtlRandom D.H. Lehmer's 1948 algorithm is used like stated in
+ * the documentation of the RtlUniform function. This algorithm is:
+ *
+ * seed = (seed * const_1 + const_2) % const_3;
+ *
+ * According to the RtlUniform documentation the random number is
+ * distributed over [0..MAXLONG], but in reality it is distributed
+ * over [0..MAXLONG-1]. Therefore const_3 might be MAXLONG + 1 or
+ * MAXLONG:
+ *
+ * seed = (seed * const_1 + const_2) % (MAXLONG + 1);
+ *
+ * or
+ *
+ * seed = (seed * const_1 + const_2) % MAXLONG;
+ *
+ * To find out const_2 we just call RtlRandom with seed set to 0:
+ */
+ seed = 0;
+ result_expected = 0x320a1743;
+ seed_expected =0x44b;
+ result = pRtlRandom(&seed);
+ ok(result == result_expected,
+ "pRtlRandom(&seed (seed == 0)) returns %lx, expected %lx\n",
+ result, result_expected);
+ ok(seed == seed_expected,
+ "pRtlRandom(&seed (seed == 0)) sets seed to %lx, expected %lx\n",
+ seed, seed_expected);
+/*
+ * Seed is not equal to result as with RtlUniform. To see more we
+ * call RtlRandom aggain with seed set to 0:
+ */
+ seed = 0;
+ result_expected = 0x7fffffc3;
+ seed_expected =0x44b;
+ result = pRtlRandom(&seed);
+ ok(result == result_expected,
+ "RtlRandom(&seed (seed == 0)) returns %lx, expected %lx\n",
+ result, result_expected);
+ ok(seed == seed_expected,
+ "RtlRandom(&seed (seed == 0)) sets seed to %lx, expected %lx\n",
+ seed, seed_expected);
+/*
+ * Seed is set to the same value as before but the result is different.
+ * To see more we call RtlRandom aggain with seed set to 0:
+ */
+ seed = 0;
+ result_expected = 0x7fffffc3;
+ seed_expected =0x44b;
+ result = pRtlRandom(&seed);
+ ok(result == result_expected,
+ "RtlRandom(&seed (seed == 0)) returns %lx, expected %lx\n",
+ result, result_expected);
+ ok(seed == seed_expected,
+ "RtlRandom(&seed (seed == 0)) sets seed to %lx, expected %lx\n",
+ seed, seed_expected);
+/*
+ * Seed is aggain set to the same value as before. This time we also
+ * have the same result as before. Interestingly the value of the
+ * result is 0x7fffffc3 which is the same value used in RtlUniform
+ * as const_2. If we do
+ *
+ * seed = 0;
+ * result = RtlUniform(&seed);
+ *
+ * we get the same result (0x7fffffc3) as with
+ *
+ * seed = 0;
+ * RtlRandom(&seed);
+ * seed = 0;
+ * result = RtlRandom(&seed);
+ *
+ * And there is another interesting thing. If we do
+ *
+ * seed = 0;
+ * RtlUniform(&seed);
+ * RtlUniform(&seed);
+ *
+ * seed is set to the value 0x44b which ist the same value that
+ *
+ * seed = 0;
+ * RtlRandom(&seed);
+ *
+ * assigns to seed. Putting these two findings together leads to
+ * the concluson that RtlRandom saves the value in some variable,
+ * like in the following algorithm:
+ *
+ * result = saved_value;
+ * saved_value = RtlUniform(&seed);
+ * RtlUniform(&seed);
+ * return(result);
+ *
+ * Now we do further tests with seed set to 1:
+ */
+ seed = 1;
+ result_expected = 0x7a50bbc6;
+ seed_expected =0x5a1;
+ result = pRtlRandom(&seed);
+ ok(result == result_expected,
+ "RtlRandom(&seed (seed == 1)) returns %lx, expected %lx\n",
+ result, result_expected);
+ ok(seed == seed_expected,
+ "RtlRandom(&seed (seed == 1)) sets seed to %lx, expected %lx\n",
+ seed, seed_expected);
+/*
+ * If there is just one saved_value the result now would be
+ * 0x7fffffc3. From this test we can see that there is more than
+ * one saved_value, like with this algorithm:
+ *
+ * result = saved_value[pos];
+ * saved_value[pos] = RtlUniform(&seed);
+ * RtlUniform(&seed);
+ * return(result);
+ *
+ * But how is the value of pos determined? The calls to RtlUniform
+ * create a sequence of random numbers. Every second random number
+ * is put into the saved_value array and is used in some later call
+ * of RtlRandom as result. The only reasonable source to determine
+ * pos are the random numbers generated by RtlUniform which are not
+ * put into the saved_value array. This are the values of seed
+ * between the two calls of RtlUniform as in this algorithm:
+ *
+ * rand = RtlUniform(&seed);
+ * RtlUniform(&seed);
+ * pos = position(seed);
+ * result = saved_value[pos];
+ * saved_value[pos] = rand;
+ * return(result);
+ *
+ * What remains to be determined is: The size of the saved_value array,
+ * the initial values of the saved_value array and the function
+ * position(seed). These tests are not shown here.
+ * The result of these tests is: The size of the saved_value array
+ * is 128, the initial values can be seen in the my_RtlRandom
+ * function and the position(seed) function is (seed & 0x7f).
+ *
+ * For a full test of RtlRandom use one of the following loop heads:
+ *
+ * for (num = 0; num <= 0xffffffff; num++) {
+ * seed = num;
+ * ...
+ *
+ * seed = 0;
+ * for (num = 0; num <= 0xffffffff; num++) {
+ * ...
+ */
+ seed = 0;
+ for (num = 0; num <= 100000; num++) {
+ seed_bak = seed;
+ seed_expected = seed;
+ result_expected = my_RtlRandom(&seed_expected);
+ /* The following corrections are necessary because the */
+ /* previous tests changed the saved_value array */
+ if (num == 0) {
+ result_expected = 0x7fffffc3;
+ } else if (num == 81) {
+ result_expected = 0x7fffffb1;
+ } /* if */
+ result = pRtlRandom(&seed);
+ ok(result == result_expected,
+ "test: %llu RtlUniform(&seed (seed == %lx)) returns %lx, expected %lx\n",
+ num, seed_bak, result, result_expected);
+ ok(seed == seed_expected,
+ "test: %llu RtlUniform(&seed (seed == %lx)) sets seed to %lx, expected %lx\n",
+ num, seed_bak, seed, seed_expected);
+ } /* for */
+}
+
+
+typedef struct {
+ ACCESS_MASK GrantedAccess;
+ ACCESS_MASK DesiredAccess;
+ BOOLEAN result;
+} all_accesses_t;
+
+static const all_accesses_t all_accesses[] = {
+ {0xFEDCBA76, 0xFEDCBA76, 1},
+ {0x00000000, 0xFEDCBA76, 0},
+ {0xFEDCBA76, 0x00000000, 1},
+ {0x00000000, 0x00000000, 1},
+ {0xFEDCBA76, 0xFEDCBA70, 1},
+ {0xFEDCBA70, 0xFEDCBA76, 0},
+ {0xFEDCBA76, 0xFEDC8A76, 1},
+ {0xFEDC8A76, 0xFEDCBA76, 0},
+ {0xFEDCBA76, 0xC8C4B242, 1},
+ {0xC8C4B242, 0xFEDCBA76, 0},
+};
+#define NB_ALL_ACCESSES (sizeof(all_accesses)/sizeof(*all_accesses))
+
+
+static void test_RtlAreAllAccessesGranted(void)
+{
+ size_t test_num;
+ BOOLEAN result;
+
+ for (test_num = 0; test_num < NB_ALL_ACCESSES; test_num++) {
+ result = pRtlAreAllAccessesGranted(all_accesses[test_num].GrantedAccess,
+ all_accesses[test_num].DesiredAccess);
+ ok(all_accesses[test_num].result == result,
+ "(test %d): RtlAreAllAccessesGranted(%08lx, %08lx) returns %d, expected %d\n",
+ test_num, all_accesses[test_num].GrantedAccess,
+ all_accesses[test_num].DesiredAccess,
+ result, all_accesses[test_num].result);
+ } /* for */
+}
+
+
+typedef struct {
+ ACCESS_MASK GrantedAccess;
+ ACCESS_MASK DesiredAccess;
+ BOOLEAN result;
+} any_accesses_t;
+
+static const any_accesses_t any_accesses[] = {
+ {0xFEDCBA76, 0xFEDCBA76, 1},
+ {0x00000000, 0xFEDCBA76, 0},
+ {0xFEDCBA76, 0x00000000, 0},
+ {0x00000000, 0x00000000, 0},
+ {0xFEDCBA76, 0x01234589, 0},
+ {0x00040000, 0xFEDCBA76, 1},
+ {0x00040000, 0xFED8BA76, 0},
+ {0xFEDCBA76, 0x00040000, 1},
+ {0xFED8BA76, 0x00040000, 0},
+};
+#define NB_ANY_ACCESSES (sizeof(any_accesses)/sizeof(*any_accesses))
+
+
+static void test_RtlAreAnyAccessesGranted(void)
+{
+ size_t test_num;
+ BOOLEAN result;
+
+ for (test_num = 0; test_num < NB_ANY_ACCESSES; test_num++) {
+ result = pRtlAreAnyAccessesGranted(any_accesses[test_num].GrantedAccess,
+ any_accesses[test_num].DesiredAccess);
+ ok(any_accesses[test_num].result == result,
+ "(test %d): RtlAreAnyAccessesGranted(%08lx, %08lx) returns %d, expected %d\n",
+ test_num, any_accesses[test_num].GrantedAccess,
+ any_accesses[test_num].DesiredAccess,
+ result, any_accesses[test_num].result);
+ } /* for */
+}
+
+static void test_RtlComputeCrc32(void)
+{
+ DWORD crc = 0;
+
+ if (!pRtlComputeCrc32)
+ return;
+
+ crc = pRtlComputeCrc32(crc, (LPBYTE)src, LEN);
+ ok(crc == 0x40861dc2,"Expected 0x40861dc2, got %8lx\n", crc);
+}
+
+
+typedef struct MY_HANDLE
+{
+ RTL_HANDLE RtlHandle;
+ void * MyValue;
+} MY_HANDLE;
+
+static inline void RtlpMakeHandleAllocated(RTL_HANDLE * Handle)
+{
+ ULONG_PTR *AllocatedBit = (ULONG_PTR *)(&Handle->Next);
+ *AllocatedBit = *AllocatedBit | 1;
+}
+
+static void test_HandleTables(void)
+{
+ BOOLEAN result;
+ NTSTATUS status;
+ ULONG Index;
+ MY_HANDLE * MyHandle;
+ RTL_HANDLE_TABLE HandleTable;
+
+ pRtlInitializeHandleTable(0x3FFF, sizeof(MY_HANDLE), &HandleTable);
+ MyHandle = (MY_HANDLE *)pRtlAllocateHandle(&HandleTable, &Index);
+ ok(MyHandle != NULL, "RtlAllocateHandle failed\n");
+ RtlpMakeHandleAllocated(&MyHandle->RtlHandle);
+ MyHandle = NULL;
+ result = pRtlIsValidIndexHandle(&HandleTable, Index, (RTL_HANDLE **)&MyHandle);
+ ok(result, "Handle %p wasn't valid\n", MyHandle);
+ result = pRtlFreeHandle(&HandleTable, &MyHandle->RtlHandle);
+ ok(result, "Couldn't free handle %p\n", MyHandle);
+ status = pRtlDestroyHandleTable(&HandleTable);
+ ok(status == STATUS_SUCCESS, "RtlDestroyHandleTable failed with error 0x%08lx\n", status);
+}
+
+static void test_RtlAllocateAndInitializeSid(void)
+{
+ NTSTATUS ret;
+ SID_IDENTIFIER_AUTHORITY sia = {{ 1, 2, 3, 4, 5, 6 }};
+ PSID psid;
+
+ ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
+ ok(!ret, "RtlAllocateAndInitializeSid error %08lx\n", ret);
+ ret = pRtlFreeSid(psid);
+ ok(!ret, "RtlFreeSid error %08lx\n", ret);
+
+ /* these tests crash on XP
+ ret = pRtlAllocateAndInitializeSid(NULL, 0, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
+ ret = pRtlAllocateAndInitializeSid(&sia, 0, 1, 2, 3, 4, 5, 6, 7, 8, NULL);*/
+
+ ret = pRtlAllocateAndInitializeSid(&sia, 9, 1, 2, 3, 4, 5, 6, 7, 8, &psid);
+ ok(ret == STATUS_INVALID_SID, "wrong error %08lx\n", ret);
+}
+
+START_TEST(rtl)
+{
+ InitFunctionPtrs();
+
+ if (pRtlCompareMemory)
+ test_RtlCompareMemory();
+ if (pRtlCompareMemoryUlong)
+ test_RtlCompareMemoryUlong();
+ if (pRtlMoveMemory)
+ test_RtlMoveMemory();
+ if (pRtlFillMemory)
+ test_RtlFillMemory();
+ if (pRtlFillMemoryUlong)
+ test_RtlFillMemoryUlong();
+ if (pRtlZeroMemory)
+ test_RtlZeroMemory();
+ if (pRtlUlonglongByteSwap)
+ test_RtlUlonglongByteSwap();
+ if (pRtlUniform)
+ test_RtlUniform();
+ if (pRtlRandom)
+ test_RtlRandom();
+ if (pRtlAreAllAccessesGranted)
+ test_RtlAreAllAccessesGranted();
+ if (pRtlAreAnyAccessesGranted)
+ test_RtlAreAnyAccessesGranted();
+ if (pRtlComputeCrc32)
+ test_RtlComputeCrc32();
+ if (pRtlInitializeHandleTable)
+ test_HandleTables();
+ if (pRtlAllocateAndInitializeSid)
+ test_RtlAllocateAndInitializeSid();
+}
--- /dev/null
+/* Unit test suite for Rtl bitmap functions
+ *
+ * Copyright 2002 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as some of the bitmap functions exist only
+ * in later versions of ntdll.
+ */
+
+#include "ntdll_test.h"
+
+#ifdef __WINE_WINTERNL_H
+
+/* Function ptrs for ordinal calls */
+static HMODULE hntdll = 0;
+static VOID (WINAPI *pRtlInitializeBitMap)(PRTL_BITMAP,LPBYTE,ULONG);
+static VOID (WINAPI *pRtlSetAllBits)(PRTL_BITMAP);
+static VOID (WINAPI *pRtlClearAllBits)(PRTL_BITMAP);
+static VOID (WINAPI *pRtlSetBits)(PRTL_BITMAP,ULONG,ULONG);
+static VOID (WINAPI *pRtlClearBits)(PRTL_BITMAP,ULONG,ULONG);
+static BOOLEAN (WINAPI *pRtlAreBitsSet)(PRTL_BITMAP,ULONG,ULONG);
+static BOOLEAN (WINAPI *pRtlAreBitsClear)(PRTL_BITMAP,ULONG,ULONG);
+static ULONG (WINAPI *pRtlFindSetBitsAndClear)(PRTL_BITMAP,ULONG,ULONG);
+static ULONG (WINAPI *pRtlFindClearBitsAndSet)(PRTL_BITMAP,ULONG,ULONG);
+static CCHAR (WINAPI *pRtlFindMostSignificantBit)(ULONGLONG);
+static CCHAR (WINAPI *pRtlFindLeastSignificantBit)(ULONGLONG);
+static ULONG (WINAPI *pRtlFindSetRuns)(PRTL_BITMAP,PRTL_BITMAP_RUN,ULONG,BOOLEAN);
+static ULONG (WINAPI *pRtlFindClearRuns)(PRTL_BITMAP,PRTL_BITMAP_RUN,ULONG,BOOLEAN);
+static ULONG (WINAPI *pRtlNumberOfSetBits)(PRTL_BITMAP);
+static ULONG (WINAPI *pRtlNumberOfClearBits)(PRTL_BITMAP);
+static ULONG (WINAPI *pRtlFindLongestRunSet)(PRTL_BITMAP,PULONG);
+static ULONG (WINAPI *pRtlFindLongestRunClear)(PRTL_BITMAP,PULONG);
+
+static BYTE buff[256];
+static RTL_BITMAP bm;
+
+static void InitFunctionPtrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ ok(hntdll != 0, "LoadLibrary failed\n");
+ if (hntdll)
+ {
+ pRtlInitializeBitMap = (void *)GetProcAddress(hntdll, "RtlInitializeBitMap");
+ pRtlSetAllBits = (void *)GetProcAddress(hntdll, "RtlSetAllBits");
+ pRtlClearAllBits = (void *)GetProcAddress(hntdll, "RtlClearAllBits");
+ pRtlSetBits = (void *)GetProcAddress(hntdll, "RtlSetBits");
+ pRtlClearBits = (void *)GetProcAddress(hntdll, "RtlClearBits");
+ pRtlAreBitsSet = (void *)GetProcAddress(hntdll, "RtlAreBitsSet");
+ pRtlAreBitsClear = (void *)GetProcAddress(hntdll, "RtlAreBitsClear");
+ pRtlNumberOfSetBits = (void *)GetProcAddress(hntdll, "RtlNumberOfSetBits");
+ pRtlNumberOfClearBits = (void *)GetProcAddress(hntdll, "RtlNumberOfClearBits");
+ pRtlFindSetBitsAndClear = (void *)GetProcAddress(hntdll, "RtlFindSetBitsAndClear");
+ pRtlFindClearBitsAndSet = (void *)GetProcAddress(hntdll, "RtlFindClearBitsAndSet");
+ pRtlFindMostSignificantBit = (void *)GetProcAddress(hntdll, "RtlFindMostSignificantBit");
+ pRtlFindLeastSignificantBit = (void *)GetProcAddress(hntdll, "RtlFindLeastSignificantBit");
+ pRtlFindSetRuns = (void *)GetProcAddress(hntdll, "RtlFindSetRuns");
+ pRtlFindClearRuns = (void *)GetProcAddress(hntdll, "RtlFindClearRuns");
+ pRtlFindLongestRunSet = (void *)GetProcAddress(hntdll, "RtlFindLongestRunSet");
+ pRtlFindLongestRunClear = (void *)GetProcAddress(hntdll, "RtlFindLongestRunClear");
+ }
+}
+
+static void test_RtlInitializeBitMap(void)
+{
+ bm.SizeOfBitMap = 0;
+ bm.Buffer = 0;
+
+ memset(buff, 0, sizeof(buff));
+ buff[0] = 77; /* Check buffer is not written to during init */
+ buff[79] = 77;
+
+ pRtlInitializeBitMap(&bm, buff, 800);
+ ok(bm.SizeOfBitMap == 800, "size uninitialised\n");
+ ok(bm.Buffer == (PULONG)buff,"buffer uninitialised\n");
+ ok(buff[0] == 77 && buff[79] == 77, "wrote to buffer\n");
+}
+
+static void test_RtlSetAllBits(void)
+{
+ if (!pRtlSetAllBits)
+ return;
+
+ memset(buff, 0 , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, 1);
+
+ pRtlSetAllBits(&bm);
+ ok(buff[0] == 0xff && buff[1] == 0xff && buff[2] == 0xff &&
+ buff[3] == 0xff, "didn't round up size\n");
+ ok(buff[4] == 0, "set more than rounded size\n");
+}
+
+static void test_RtlClearAllBits(void)
+{
+ if (!pRtlClearAllBits)
+ return;
+
+ memset(buff, 0xff , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, 1);
+
+ pRtlClearAllBits(&bm);
+ ok(!buff[0] && !buff[1] && !buff[2] && !buff[3], "didn't round up size\n");
+ ok(buff[4] == 0xff, "cleared more than rounded size\n");
+}
+
+static void test_RtlSetBits(void)
+{
+ if (!pRtlSetBits)
+ return;
+
+ memset(buff, 0 , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ pRtlSetBits(&bm, 0, 1);
+ ok(buff[0] == 1, "didn't set 1st bit\n");
+
+ buff[0] = 0;
+ pRtlSetBits(&bm, 7, 2);
+ ok(buff[0] == 0x80 && buff[1] == 1, "didn't span w/len < 8\n");
+
+ buff[0] = buff[1] = 0;
+ pRtlSetBits(&bm, 7, 10);
+ ok(buff[0] == 0x80 && buff[1] == 0xff && buff[2] == 1, "didn't span w/len > 8\n");
+
+ buff[0] = buff[1] = buff[2] = 0;
+ pRtlSetBits(&bm, 0, 8); /* 1st byte */
+ ok(buff[0] == 0xff, "didn't set all bits\n");
+ ok(!buff[1], "set too many bits\n");
+
+ pRtlSetBits(&bm, sizeof(buff)*8-1, 1); /* last bit */
+ ok(buff[sizeof(buff)-1] == 0x80, "didn't set last bit\n");
+}
+
+static void test_RtlClearBits(void)
+{
+ if (!pRtlClearBits)
+ return;
+
+ memset(buff, 0xff , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ pRtlClearBits(&bm, 0, 1);
+ ok(buff[0] == 0xfe, "didn't clear 1st bit\n");
+
+ buff[0] = 0xff;
+ pRtlClearBits(&bm, 7, 2);
+ ok(buff[0] == 0x7f && buff[1] == 0xfe, "didn't span w/len < 8\n");
+
+ buff[0] = buff[1] = 0xff;
+ pRtlClearBits(&bm, 7, 10);
+ ok(buff[0] == 0x7f && buff[1] == 0 && buff[2] == 0xfe, "didn't span w/len > 8\n");
+
+ buff[0] = buff[1] = buff[2] = 0xff;
+ pRtlClearBits(&bm, 0, 8); /* 1st byte */
+ ok(!buff[0], "didn't clear all bits\n");
+ ok(buff[1] == 0xff, "cleared too many bits\n");
+
+ pRtlClearBits(&bm, sizeof(buff)*8-1, 1);
+ ok(buff[sizeof(buff)-1] == 0x7f, "didn't set last bit\n");
+}
+
+static void test_RtlCheckBit(void)
+{
+ BOOLEAN bRet;
+
+ memset(buff, 0 , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+ pRtlSetBits(&bm, 0, 1);
+ pRtlSetBits(&bm, 7, 2);
+ pRtlSetBits(&bm, sizeof(buff)*8-1, 1);
+
+ bRet = RtlCheckBit(&bm, 0);
+ ok (bRet, "didn't find set bit\n");
+ bRet = RtlCheckBit(&bm, 7);
+ ok (bRet, "didn't find set bit\n");
+ bRet = RtlCheckBit(&bm, 8);
+ ok (bRet, "didn't find set bit\n");
+ bRet = RtlCheckBit(&bm, sizeof(buff)*8-1);
+ ok (bRet, "didn't find set bit\n");
+ bRet = RtlCheckBit(&bm, 1);
+ ok (!bRet, "found non set bit\n");
+ bRet = RtlCheckBit(&bm, sizeof(buff)*8-2);
+ ok (!bRet, "found non set bit\n");
+}
+
+static void test_RtlAreBitsSet(void)
+{
+ BOOLEAN bRet;
+
+ if (!pRtlAreBitsSet)
+ return;
+
+ memset(buff, 0 , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ bRet = pRtlAreBitsSet(&bm, 0, 1);
+ ok (!bRet, "found set bits after init\n");
+
+ pRtlSetBits(&bm, 0, 1);
+ bRet = pRtlAreBitsSet(&bm, 0, 1);
+ ok (bRet, "didn't find set bits\n");
+
+ buff[0] = 0;
+ pRtlSetBits(&bm, 7, 2);
+ bRet = pRtlAreBitsSet(&bm, 7, 2);
+ ok(bRet, "didn't find w/len < 8\n");
+ bRet = pRtlAreBitsSet(&bm, 6, 3);
+ ok(!bRet, "found non set bit\n");
+ bRet = pRtlAreBitsSet(&bm, 7, 3);
+ ok(!bRet, "found non set bit\n");
+
+ buff[0] = buff[1] = 0;
+ pRtlSetBits(&bm, 7, 10);
+ bRet = pRtlAreBitsSet(&bm, 7, 10);
+ ok(bRet, "didn't find w/len < 8\n");
+ bRet = pRtlAreBitsSet(&bm, 6, 11);
+ ok(!bRet, "found non set bit\n");
+ bRet = pRtlAreBitsSet(&bm, 7, 11);
+ ok(!bRet, "found non set bit\n");
+
+ buff[0] = buff[1] = buff[2] = 0;
+ pRtlSetBits(&bm, 0, 8); /* 1st byte */
+ bRet = pRtlAreBitsSet(&bm, 0, 8);
+ ok(bRet, "didn't find whole byte\n");
+
+ pRtlSetBits(&bm, sizeof(buff)*8-1, 1);
+ bRet = pRtlAreBitsSet(&bm, sizeof(buff)*8-1, 1);
+ ok(bRet, "didn't find last bit\n");
+}
+
+static void test_RtlAreBitsClear(void)
+{
+ BOOLEAN bRet;
+
+ if (!pRtlAreBitsClear)
+ return;
+
+ memset(buff, 0xff , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ bRet = pRtlAreBitsClear(&bm, 0, 1);
+ ok (!bRet, "found clear bits after init\n");
+
+ pRtlClearBits(&bm, 0, 1);
+ bRet = pRtlAreBitsClear(&bm, 0, 1);
+ ok (bRet, "didn't find set bits\n");
+
+ buff[0] = 0xff;
+ pRtlClearBits(&bm, 7, 2);
+ bRet = pRtlAreBitsClear(&bm, 7, 2);
+ ok(bRet, "didn't find w/len < 8\n");
+ bRet = pRtlAreBitsClear(&bm, 6, 3);
+ ok(!bRet, "found non clear bit\n");
+ bRet = pRtlAreBitsClear(&bm, 7, 3);
+ ok(!bRet, "found non clear bit\n");
+
+ buff[0] = buff[1] = 0xff;
+ pRtlClearBits(&bm, 7, 10);
+ bRet = pRtlAreBitsClear(&bm, 7, 10);
+ ok(bRet, "didn't find w/len < 8\n");
+ bRet = pRtlAreBitsClear(&bm, 6, 11);
+ ok(!bRet, "found non clear bit\n");
+ bRet = pRtlAreBitsClear(&bm, 7, 11);
+ ok(!bRet, "found non clear bit\n");
+
+ buff[0] = buff[1] = buff[2] = 0xff;
+ pRtlClearBits(&bm, 0, 8); /* 1st byte */
+ bRet = pRtlAreBitsClear(&bm, 0, 8);
+ ok(bRet, "didn't find whole byte\n");
+
+ pRtlClearBits(&bm, sizeof(buff)*8-1, 1);
+ bRet = pRtlAreBitsClear(&bm, sizeof(buff)*8-1, 1);
+ ok(bRet, "didn't find last bit\n");
+}
+
+static void test_RtlNumberOfSetBits(void)
+{
+ ULONG ulCount;
+
+ if (!pRtlNumberOfSetBits)
+ return;
+
+ memset(buff, 0 , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ ulCount = pRtlNumberOfSetBits(&bm);
+ ok(ulCount == 0, "set bits after init\n");
+
+ pRtlSetBits(&bm, 0, 1); /* Set 1st bit */
+ ulCount = pRtlNumberOfSetBits(&bm);
+ ok(ulCount == 1, "count wrong\n");
+
+ pRtlSetBits(&bm, 7, 8); /* 8 more, spanning bytes 1-2 */
+ ulCount = pRtlNumberOfSetBits(&bm);
+ ok(ulCount == 8+1, "count wrong\n");
+
+ pRtlSetBits(&bm, 17, 33); /* 33 more crossing ULONG boundary */
+ ulCount = pRtlNumberOfSetBits(&bm);
+ ok(ulCount == 8+1+33, "count wrong\n");
+
+ pRtlSetBits(&bm, sizeof(buff)*8-1, 1); /* Set last bit */
+ ulCount = pRtlNumberOfSetBits(&bm);
+ ok(ulCount == 8+1+33+1, "count wrong\n");
+}
+
+static void test_RtlNumberOfClearBits(void)
+{
+ ULONG ulCount;
+
+ if (!pRtlNumberOfClearBits)
+ return;
+
+ memset(buff, 0xff , sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ ulCount = pRtlNumberOfClearBits(&bm);
+ ok(ulCount == 0, "cleared bits after init\n");
+
+ pRtlClearBits(&bm, 0, 1); /* Set 1st bit */
+ ulCount = pRtlNumberOfClearBits(&bm);
+ ok(ulCount == 1, "count wrong\n");
+
+ pRtlClearBits(&bm, 7, 8); /* 8 more, spanning bytes 1-2 */
+ ulCount = pRtlNumberOfClearBits(&bm);
+ ok(ulCount == 8+1, "count wrong\n");
+
+ pRtlClearBits(&bm, 17, 33); /* 33 more crossing ULONG boundary */
+ ulCount = pRtlNumberOfClearBits(&bm);
+ ok(ulCount == 8+1+33, "count wrong\n");
+
+ pRtlClearBits(&bm, sizeof(buff)*8-1, 1); /* Set last bit */
+ ulCount = pRtlNumberOfClearBits(&bm);
+ ok(ulCount == 8+1+33+1, "count wrong\n");
+}
+
+/* Note: this tests RtlFindSetBits also */
+static void test_RtlFindSetBitsAndClear(void)
+{
+ BOOLEAN bRet;
+ ULONG ulPos;
+
+ if (!pRtlFindSetBitsAndClear)
+ return;
+
+ memset(buff, 0, sizeof(buff));
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ pRtlSetBits(&bm, 0, 32);
+ ulPos = pRtlFindSetBitsAndClear(&bm, 32, 0);
+ ok (ulPos == 0, "didn't find bits\n");
+ if(ulPos == 0)
+ {
+ bRet = pRtlAreBitsClear(&bm, 0, 32);
+ ok (bRet, "found but didn't clear\n");
+ }
+
+ memset(buff, 0 , sizeof(buff));
+ pRtlSetBits(&bm, 40, 77);
+ ulPos = pRtlFindSetBitsAndClear(&bm, 77, 0);
+ ok (ulPos == 40, "didn't find bits\n");
+ if(ulPos == 40)
+ {
+ bRet = pRtlAreBitsClear(&bm, 40, 77);
+ ok (bRet, "found but didn't clear\n");
+ }
+}
+
+/* Note: this tests RtlFindClearBits also */
+static void test_RtlFindClearBitsAndSet(void)
+{
+ BOOLEAN bRet;
+ ULONG ulPos;
+
+ if (!pRtlFindClearBitsAndSet)
+ return;
+
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ memset(buff, 0xff, sizeof(buff));
+ pRtlSetBits(&bm, 0, 32);
+ ulPos = pRtlFindSetBitsAndClear(&bm, 32, 0);
+ ok (ulPos == 0, "didn't find bits\n");
+ if(ulPos == 0)
+ {
+ bRet = pRtlAreBitsClear(&bm, 0, 32);
+ ok (bRet, "found but didn't clear\n");
+ }
+
+ memset(buff, 0xff , sizeof(buff));
+ pRtlClearBits(&bm, 40, 77);
+ ulPos = pRtlFindClearBitsAndSet(&bm, 77, 50);
+ ok (ulPos == 40, "didn't find bits\n");
+ if(ulPos == 40)
+ {
+ bRet = pRtlAreBitsSet(&bm, 40, 77);
+ ok (bRet, "found but didn't set\n");
+ }
+}
+
+static void test_RtlFindMostSignificantBit(void)
+{
+ int i;
+ CCHAR cPos;
+ ULONGLONG ulLong;
+
+ if (!pRtlFindMostSignificantBit)
+ return;
+
+ for (i = 0; i < 64; i++)
+ {
+ ulLong = 1ul;
+ ulLong <<= i;
+
+ cPos = pRtlFindMostSignificantBit(ulLong);
+ ok (cPos == i, "didn't find MSB %llx %d %d\n", ulLong, i, cPos);
+
+ /* Set all bits lower than bit i */
+ ulLong = ((ulLong - 1) << 1) | 1;
+
+ cPos = pRtlFindMostSignificantBit(ulLong);
+ ok (cPos == i, "didn't find MSB %llx %d %d\n", ulLong, i, cPos);
+ }
+ cPos = pRtlFindMostSignificantBit(0);
+ ok (cPos == -1, "found bit when not set\n");
+}
+
+static void test_RtlFindLeastSignificantBit(void)
+{
+ int i;
+ CCHAR cPos;
+ ULONGLONG ulLong;
+
+ if (!pRtlFindLeastSignificantBit)
+ return;
+
+ for (i = 0; i < 64; i++)
+ {
+ ulLong = (ULONGLONG)1 << i;
+
+ cPos = pRtlFindLeastSignificantBit(ulLong);
+ ok (cPos == i, "didn't find LSB %llx %d %d\n", ulLong, i, cPos);
+
+ ulLong = ~((ULONGLONG)0) << i;
+
+ cPos = pRtlFindLeastSignificantBit(ulLong);
+ ok (cPos == i, "didn't find LSB %llx %d %d\n", ulLong, i, cPos);
+ }
+ cPos = pRtlFindLeastSignificantBit(0);
+ ok (cPos == -1, "found bit when not set\n");
+}
+
+/* Note: Also tests RtlFindLongestRunSet() */
+static void test_RtlFindSetRuns(void)
+{
+ RTL_BITMAP_RUN runs[16];
+ ULONG ulCount;
+
+ if (!pRtlFindSetRuns)
+ return;
+
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ memset(buff, 0, sizeof(buff));
+ ulCount = pRtlFindSetRuns(&bm, runs, 16, TRUE);
+ ok (ulCount == 0, "found set bits in empty bitmap\n");
+
+ memset(runs, 0, sizeof(runs));
+ memset(buff, 0xff, sizeof(buff));
+ ulCount = pRtlFindSetRuns(&bm, runs, 16, TRUE);
+ ok (ulCount == 1, "didn't find set bits\n");
+ ok (runs[0].StartingIndex == 0,"bad start\n");
+ ok (runs[0].NumberOfBits == sizeof(buff)*8,"bad size\n");
+
+ /* Set up 3 runs */
+ memset(runs, 0, sizeof(runs));
+ memset(buff, 0, sizeof(buff));
+ pRtlSetBits(&bm, 7, 19);
+ pRtlSetBits(&bm, 101, 3);
+ pRtlSetBits(&bm, 1877, 33);
+
+ /* Get first 2 */
+ ulCount = pRtlFindSetRuns(&bm, runs, 2, FALSE);
+ ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101,"bad find\n");
+ ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101,"bad find\n");
+ ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 19 + 3,"bad size\n");
+ ok (runs[0].StartingIndex != runs[1].StartingIndex,"found run twice\n");
+ ok (runs[2].StartingIndex == 0,"found extra run\n");
+
+ /* Get longest 3 */
+ memset(runs, 0, sizeof(runs));
+ ulCount = pRtlFindSetRuns(&bm, runs, 2, TRUE);
+ ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 1877,"bad find\n");
+ ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 1877,"bad find\n");
+ ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 33 + 19,"bad size\n");
+ ok (runs[0].StartingIndex != runs[1].StartingIndex,"found run twice\n");
+ ok (runs[2].StartingIndex == 0,"found extra run\n");
+
+ /* Get all 3 */
+ memset(runs, 0, sizeof(runs));
+ ulCount = pRtlFindSetRuns(&bm, runs, 3, TRUE);
+ ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101 ||
+ runs[0].StartingIndex == 1877,"bad find\n");
+ ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101 ||
+ runs[1].StartingIndex == 1877,"bad find\n");
+ ok (runs[2].StartingIndex == 7 || runs[2].StartingIndex == 101 ||
+ runs[2].StartingIndex == 1877,"bad find\n");
+ ok (runs[0].NumberOfBits + runs[1].NumberOfBits
+ + runs[2].NumberOfBits == 19 + 3 + 33,"bad size\n");
+ ok (runs[0].StartingIndex != runs[1].StartingIndex,"found run twice\n");
+ ok (runs[1].StartingIndex != runs[2].StartingIndex,"found run twice\n");
+ ok (runs[3].StartingIndex == 0,"found extra run\n");
+
+ if (pRtlFindLongestRunSet)
+ {
+ ULONG ulStart = 0;
+
+ ulCount = pRtlFindLongestRunSet(&bm, &ulStart);
+ ok(ulCount == 33 && ulStart == 1877,"didn't find longest %ld %ld\n",ulCount,ulStart);
+
+ memset(buff, 0, sizeof(buff));
+ ulCount = pRtlFindLongestRunSet(&bm, &ulStart);
+ ok(ulCount == 0,"found longest when none set\n");
+ }
+}
+
+/* Note: Also tests RtlFindLongestRunClear() */
+static void test_RtlFindClearRuns(void)
+{
+ RTL_BITMAP_RUN runs[16];
+ ULONG ulCount;
+
+ if (!pRtlFindClearRuns)
+ return;
+
+ pRtlInitializeBitMap(&bm, buff, sizeof(buff)*8);
+
+ memset(buff, 0xff, sizeof(buff));
+ ulCount = pRtlFindClearRuns(&bm, runs, 16, TRUE);
+ ok (ulCount == 0, "found clear bits in full bitmap\n");
+
+ memset(runs, 0, sizeof(runs));
+ memset(buff, 0, sizeof(buff));
+ ulCount = pRtlFindClearRuns(&bm, runs, 16, TRUE);
+ ok (ulCount == 1, "didn't find clear bits\n");
+ ok (runs[0].StartingIndex == 0,"bad start\n");
+ ok (runs[0].NumberOfBits == sizeof(buff)*8,"bad size\n");
+
+ /* Set up 3 runs */
+ memset(runs, 0, sizeof(runs));
+ memset(buff, 0xff, sizeof(buff));
+ pRtlClearBits(&bm, 7, 19);
+ pRtlClearBits(&bm, 101, 3);
+ pRtlClearBits(&bm, 1877, 33);
+
+ /* Get first 2 */
+ ulCount = pRtlFindClearRuns(&bm, runs, 2, FALSE);
+ ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101,"bad find\n");
+ ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101,"bad find\n");
+ ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 19 + 3,"bad size\n");
+ ok (runs[0].StartingIndex != runs[1].StartingIndex,"found run twice\n");
+ ok (runs[2].StartingIndex == 0,"found extra run\n");
+
+ /* Get longest 3 */
+ memset(runs, 0, sizeof(runs));
+ ulCount = pRtlFindClearRuns(&bm, runs, 2, TRUE);
+ ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 1877,"bad find\n");
+ ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 1877,"bad find\n");
+ ok (runs[0].NumberOfBits + runs[1].NumberOfBits == 33 + 19,"bad size\n");
+ ok (runs[0].StartingIndex != runs[1].StartingIndex,"found run twice\n");
+ ok (runs[2].StartingIndex == 0,"found extra run\n");
+
+ /* Get all 3 */
+ memset(runs, 0, sizeof(runs));
+ ulCount = pRtlFindClearRuns(&bm, runs, 3, TRUE);
+ ok (runs[0].StartingIndex == 7 || runs[0].StartingIndex == 101 ||
+ runs[0].StartingIndex == 1877,"bad find\n");
+ ok (runs[1].StartingIndex == 7 || runs[1].StartingIndex == 101 ||
+ runs[1].StartingIndex == 1877,"bad find\n");
+ ok (runs[2].StartingIndex == 7 || runs[2].StartingIndex == 101 ||
+ runs[2].StartingIndex == 1877,"bad find\n");
+ ok (runs[0].NumberOfBits + runs[1].NumberOfBits
+ + runs[2].NumberOfBits == 19 + 3 + 33,"bad size\n");
+ ok (runs[0].StartingIndex != runs[1].StartingIndex,"found run twice\n");
+ ok (runs[1].StartingIndex != runs[2].StartingIndex,"found run twice\n");
+ ok (runs[3].StartingIndex == 0,"found extra run\n");
+
+ if (pRtlFindLongestRunClear)
+ {
+ ULONG ulStart = 0;
+
+ ulCount = pRtlFindLongestRunClear(&bm, &ulStart);
+ ok(ulCount == 33 && ulStart == 1877,"didn't find longest\n");
+
+ memset(buff, 0xff, sizeof(buff));
+ ulCount = pRtlFindLongestRunClear(&bm, &ulStart);
+ ok(ulCount == 0,"found longest when none clear\n");
+ }
+
+}
+#endif
+
+START_TEST(rtlbitmap)
+{
+#ifdef __WINE_WINTERNL_H
+ InitFunctionPtrs();
+
+ if (pRtlInitializeBitMap)
+ {
+ test_RtlInitializeBitMap();
+ test_RtlSetAllBits();
+ test_RtlClearAllBits();
+ test_RtlSetBits();
+ test_RtlClearBits();
+ test_RtlCheckBit();
+ test_RtlAreBitsSet();
+ test_RtlAreBitsClear();
+ test_RtlNumberOfSetBits();
+ test_RtlNumberOfClearBits();
+ test_RtlFindSetBitsAndClear();
+ test_RtlFindClearBitsAndSet();
+ test_RtlFindMostSignificantBit();
+ test_RtlFindLeastSignificantBit();
+ test_RtlFindSetRuns();
+ test_RtlFindClearRuns();
+ }
+#endif
+}
--- /dev/null
+/* Unit test suite for Rtl string functions
+ *
+ * Copyright 2002 Robert Shearman
+ * Copyright 2003 Thomas Mertes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as there is no import library for NTDLL on
+ * windows.
+ */
+
+#include <stdlib.h>
+
+#define INITGUID
+
+#include "ntdll_test.h"
+#include "winnls.h"
+#include "guiddef.h"
+
+/* Function ptrs for ntdll calls */
+static HMODULE hntdll = 0;
+static NTSTATUS (WINAPI *pRtlAnsiStringToUnicodeString)(PUNICODE_STRING, PCANSI_STRING, BOOLEAN);
+static NTSTATUS (WINAPI *pRtlAppendAsciizToString)(STRING *, LPCSTR);
+static NTSTATUS (WINAPI *pRtlAppendStringToString)(STRING *, const STRING *);
+static NTSTATUS (WINAPI *pRtlAppendUnicodeStringToString)(UNICODE_STRING *, const UNICODE_STRING *);
+static NTSTATUS (WINAPI *pRtlAppendUnicodeToString)(UNICODE_STRING *, LPCWSTR);
+static NTSTATUS (WINAPI *pRtlCharToInteger)(PCSZ, ULONG, int *);
+static VOID (WINAPI *pRtlCopyString)(STRING *, const STRING *);
+static BOOLEAN (WINAPI *pRtlCreateUnicodeString)(PUNICODE_STRING, LPCWSTR);
+static BOOLEAN (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
+static NTSTATUS (WINAPI *pRtlDowncaseUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *, BOOLEAN);
+static NTSTATUS (WINAPI *pRtlDuplicateUnicodeString)(long, UNICODE_STRING *, UNICODE_STRING *);
+static BOOLEAN (WINAPI *pRtlEqualUnicodeString)(const UNICODE_STRING *, const UNICODE_STRING *, BOOLEAN);
+static NTSTATUS (WINAPI *pRtlFindCharInUnicodeString)(int, const UNICODE_STRING *, const UNICODE_STRING *, USHORT *);
+static VOID (WINAPI *pRtlFreeAnsiString)(PSTRING);
+static VOID (WINAPI *pRtlInitAnsiString)(PSTRING, LPCSTR);
+static VOID (WINAPI *pRtlInitString)(PSTRING, LPCSTR);
+static VOID (WINAPI *pRtlInitUnicodeString)(PUNICODE_STRING, LPCWSTR);
+static NTSTATUS (WINAPI *pRtlInitUnicodeStringEx)(PUNICODE_STRING, LPCWSTR);
+static NTSTATUS (WINAPI *pRtlIntegerToChar)(ULONG, ULONG, ULONG, PCHAR);
+static NTSTATUS (WINAPI *pRtlIntegerToUnicodeString)(ULONG, ULONG, UNICODE_STRING *);
+static NTSTATUS (WINAPI *pRtlMultiAppendUnicodeStringBuffer)(UNICODE_STRING *, long, UNICODE_STRING *);
+static NTSTATUS (WINAPI *pRtlUnicodeStringToAnsiString)(STRING *, const UNICODE_STRING *, BOOLEAN);
+static NTSTATUS (WINAPI *pRtlUnicodeStringToInteger)(const UNICODE_STRING *, int, int *);
+static WCHAR (WINAPI *pRtlUpcaseUnicodeChar)(WCHAR);
+static NTSTATUS (WINAPI *pRtlUpcaseUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *, BOOLEAN);
+static CHAR (WINAPI *pRtlUpperChar)(CHAR);
+static NTSTATUS (WINAPI *pRtlUpperString)(STRING *, const STRING *);
+static NTSTATUS (WINAPI *pRtlValidateUnicodeString)(long, UNICODE_STRING *);
+static NTSTATUS (WINAPI *pRtlGUIDFromString)(const UNICODE_STRING*,GUID*);
+static NTSTATUS (WINAPI *pRtlStringFromGUID)(const GUID*, UNICODE_STRING*);
+
+/*static VOID (WINAPI *pRtlFreeOemString)(PSTRING);*/
+/*static VOID (WINAPI *pRtlFreeUnicodeString)(PUNICODE_STRING);*/
+/*static VOID (WINAPI *pRtlCopyUnicodeString)(UNICODE_STRING *, const UNICODE_STRING *);*/
+/*static VOID (WINAPI *pRtlEraseUnicodeString)(UNICODE_STRING *);*/
+/*static LONG (WINAPI *pRtlCompareString)(const STRING *,const STRING *,BOOLEAN);*/
+/*static LONG (WINAPI *pRtlCompareUnicodeString)(const UNICODE_STRING *,const UNICODE_STRING *,BOOLEAN);*/
+/*static BOOLEAN (WINAPI *pRtlEqualString)(const STRING *,const STRING *,BOOLEAN);*/
+/*static BOOLEAN (WINAPI *pRtlPrefixString)(const STRING *, const STRING *, BOOLEAN);*/
+/*static BOOLEAN (WINAPI *pRtlPrefixUnicodeString)(const UNICODE_STRING *, const UNICODE_STRING *, BOOLEAN);*/
+/*static NTSTATUS (WINAPI *pRtlOemStringToUnicodeString)(PUNICODE_STRING, const STRING *, BOOLEAN);*/
+/*static NTSTATUS (WINAPI *pRtlUnicodeStringToOemString)(STRING *, const UNICODE_STRING *, BOOLEAN);*/
+/*static NTSTATUS (WINAPI *pRtlMultiByteToUnicodeN)(LPWSTR, DWORD, LPDWORD, LPCSTR, DWORD);*/
+/*static NTSTATUS (WINAPI *pRtlOemToUnicodeN)(LPWSTR, DWORD, LPDWORD, LPCSTR, DWORD);*/
+/*static NTSTATUS (WINAPI *pRtlUpcaseUnicodeStringToAnsiString)(STRING *, const UNICODE_STRING *, BOOLEAN);*/
+/*static NTSTATUS (WINAPI *pRtlUpcaseUnicodeStringToOemString)(STRING *, const UNICODE_STRING *, BOOLEAN);*/
+/*static NTSTATUS (WINAPI *pRtlUpcaseUnicodeToMultiByteN)(LPSTR, DWORD, LPDWORD, LPCWSTR, DWORD);*/
+/*static NTSTATUS (WINAPI *pRtlUpcaseUnicodeToOemN)(LPSTR, DWORD, LPDWORD, LPCWSTR, DWORD);*/
+/*static UINT (WINAPI *pRtlOemToUnicodeSize)(const STRING *);*/
+/*static DWORD (WINAPI *pRtlAnsiStringToUnicodeSize)(const STRING *);*/
+/*static DWORD (WINAPI *pRtlIsTextUnicode)(LPVOID, DWORD, DWORD *);*/
+
+
+static WCHAR* AtoW( const char* p )
+{
+ WCHAR* buffer;
+ DWORD len = MultiByteToWideChar( CP_ACP, 0, p, -1, NULL, 0 );
+ buffer = malloc( len * sizeof(WCHAR) );
+ MultiByteToWideChar( CP_ACP, 0, p, -1, buffer, len );
+ return buffer;
+}
+
+
+static void InitFunctionPtrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ ok(hntdll != 0, "LoadLibrary failed\n");
+ if (hntdll) {
+ pRtlAnsiStringToUnicodeString = (void *)GetProcAddress(hntdll, "RtlAnsiStringToUnicodeString");
+ pRtlAppendAsciizToString = (void *)GetProcAddress(hntdll, "RtlAppendAsciizToString");
+ pRtlAppendStringToString = (void *)GetProcAddress(hntdll, "RtlAppendStringToString");
+ pRtlAppendUnicodeStringToString = (void *)GetProcAddress(hntdll, "RtlAppendUnicodeStringToString");
+ pRtlAppendUnicodeToString = (void *)GetProcAddress(hntdll, "RtlAppendUnicodeToString");
+ pRtlCharToInteger = (void *)GetProcAddress(hntdll, "RtlCharToInteger");
+ pRtlCopyString = (void *)GetProcAddress(hntdll, "RtlCopyString");
+ pRtlCreateUnicodeString = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeString");
+ pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
+ pRtlDowncaseUnicodeString = (void *)GetProcAddress(hntdll, "RtlDowncaseUnicodeString");
+ pRtlDuplicateUnicodeString = (void *)GetProcAddress(hntdll, "RtlDuplicateUnicodeString");
+ pRtlEqualUnicodeString = (void *)GetProcAddress(hntdll, "RtlEqualUnicodeString");
+ pRtlFindCharInUnicodeString = (void *)GetProcAddress(hntdll, "RtlFindCharInUnicodeString");
+ pRtlFreeAnsiString = (void *)GetProcAddress(hntdll, "RtlFreeAnsiString");
+ pRtlInitAnsiString = (void *)GetProcAddress(hntdll, "RtlInitAnsiString");
+ pRtlInitString = (void *)GetProcAddress(hntdll, "RtlInitString");
+ pRtlInitUnicodeString = (void *)GetProcAddress(hntdll, "RtlInitUnicodeString");
+ pRtlInitUnicodeStringEx = (void *)GetProcAddress(hntdll, "RtlInitUnicodeStringEx");
+ pRtlIntegerToChar = (void *)GetProcAddress(hntdll, "RtlIntegerToChar");
+ pRtlIntegerToUnicodeString = (void *)GetProcAddress(hntdll, "RtlIntegerToUnicodeString");
+ pRtlMultiAppendUnicodeStringBuffer = (void *)GetProcAddress(hntdll, "RtlMultiAppendUnicodeStringBuffer");
+ pRtlUnicodeStringToAnsiString = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToAnsiString");
+ pRtlUnicodeStringToInteger = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToInteger");
+ pRtlUpcaseUnicodeChar = (void *)GetProcAddress(hntdll, "RtlUpcaseUnicodeChar");
+ pRtlUpcaseUnicodeString = (void *)GetProcAddress(hntdll, "RtlUpcaseUnicodeString");
+ pRtlUpperChar = (void *)GetProcAddress(hntdll, "RtlUpperChar");
+ pRtlUpperString = (void *)GetProcAddress(hntdll, "RtlUpperString");
+ pRtlValidateUnicodeString = (void *)GetProcAddress(hntdll, "RtlValidateUnicodeString");
+ pRtlGUIDFromString = (void *)GetProcAddress(hntdll, "RtlGUIDFromString");
+ pRtlStringFromGUID = (void *)GetProcAddress(hntdll, "RtlStringFromGUID");
+ }
+}
+
+
+static void test_RtlInitString(void)
+{
+ static const char teststring[] = "Some Wild String";
+ STRING str;
+
+ str.Length = 0;
+ str.MaximumLength = 0;
+ str.Buffer = (void *)0xdeadbeef;
+ pRtlInitString(&str, teststring);
+ ok(str.Length == sizeof(teststring) - sizeof(char), "Length uninitialized\n");
+ ok(str.MaximumLength == sizeof(teststring), "MaximumLength uninitialized\n");
+ ok(str.Buffer == teststring, "Buffer not equal to teststring\n");
+ ok(strcmp(str.Buffer, "Some Wild String") == 0, "Buffer written to\n");
+ pRtlInitString(&str, NULL);
+ ok(str.Length == 0, "Length uninitialized\n");
+ ok(str.MaximumLength == 0, "MaximumLength uninitialized\n");
+ ok(str.Buffer == NULL, "Buffer not equal to NULL\n");
+/* pRtlInitString(NULL, teststring); */
+}
+
+
+static void test_RtlInitUnicodeString(void)
+{
+#define STRINGW {'S','o','m','e',' ','W','i','l','d',' ','S','t','r','i','n','g',0}
+ static const WCHAR teststring[] = STRINGW;
+ static const WCHAR originalstring[] = STRINGW;
+#undef STRINGW
+ UNICODE_STRING uni;
+
+ uni.Length = 0;
+ uni.MaximumLength = 0;
+ uni.Buffer = (void *)0xdeadbeef;
+ pRtlInitUnicodeString(&uni, teststring);
+ ok(uni.Length == sizeof(teststring) - sizeof(WCHAR), "Length uninitialized\n");
+ ok(uni.MaximumLength == sizeof(teststring), "MaximumLength uninitialized\n");
+ ok(uni.Buffer == teststring, "Buffer not equal to teststring\n");
+ ok(lstrcmpW(uni.Buffer, originalstring) == 0, "Buffer written to\n");
+ pRtlInitUnicodeString(&uni, NULL);
+ ok(uni.Length == 0, "Length uninitialized\n");
+ ok(uni.MaximumLength == 0, "MaximumLength uninitialized\n");
+ ok(uni.Buffer == NULL, "Buffer not equal to NULL\n");
+/* pRtlInitUnicodeString(NULL, teststring); */
+}
+
+
+#define TESTSTRING2_LEN 1000000
+/* #define TESTSTRING2_LEN 32766 */
+
+
+static void test_RtlInitUnicodeStringEx(void)
+{
+ static const WCHAR teststring[] = {'S','o','m','e',' ','W','i','l','d',' ','S','t','r','i','n','g',0};
+ WCHAR *teststring2;
+ UNICODE_STRING uni;
+ NTSTATUS result;
+
+ teststring2 = (WCHAR *) malloc((TESTSTRING2_LEN + 1) * sizeof(WCHAR));
+ memset(teststring2, 'X', TESTSTRING2_LEN * sizeof(WCHAR));
+ teststring2[TESTSTRING2_LEN] = '\0';
+
+ uni.Length = 12345;
+ uni.MaximumLength = 12345;
+ uni.Buffer = (void *) 0xdeadbeef;
+ result = pRtlInitUnicodeStringEx(&uni, teststring);
+ ok(result == STATUS_SUCCESS,
+ "pRtlInitUnicodeStringEx(&uni, 0) returns %lx, expected 0\n",
+ result);
+ ok(uni.Length == 32,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets Length to %u, expected %u\n",
+ uni.Length, 32);
+ ok(uni.MaximumLength == 34,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets MaximumLength to %u, expected %u\n",
+ uni.MaximumLength, 34);
+ ok(uni.Buffer == teststring,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets Buffer to %p, expected %p\n",
+ uni.Buffer, teststring);
+
+ uni.Length = 12345;
+ uni.MaximumLength = 12345;
+ uni.Buffer = (void *) 0xdeadbeef;
+ pRtlInitUnicodeString(&uni, teststring);
+ ok(uni.Length == 32,
+ "pRtlInitUnicodeString(&uni, 0) sets Length to %u, expected %u\n",
+ uni.Length, 32);
+ ok(uni.MaximumLength == 34,
+ "pRtlInitUnicodeString(&uni, 0) sets MaximumLength to %u, expected %u\n",
+ uni.MaximumLength, 34);
+ ok(uni.Buffer == teststring,
+ "pRtlInitUnicodeString(&uni, 0) sets Buffer to %p, expected %p\n",
+ uni.Buffer, teststring);
+
+ uni.Length = 12345;
+ uni.MaximumLength = 12345;
+ uni.Buffer = (void *) 0xdeadbeef;
+ result = pRtlInitUnicodeStringEx(&uni, teststring2);
+ ok(result == STATUS_NAME_TOO_LONG,
+ "pRtlInitUnicodeStringEx(&uni, 0) returns %lx, expected %lx\n",
+ result, STATUS_NAME_TOO_LONG);
+ ok(uni.Length == 12345,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets Length to %u, expected %u\n",
+ uni.Length, 12345);
+ ok(uni.MaximumLength == 12345,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets MaximumLength to %u, expected %u\n",
+ uni.MaximumLength, 12345);
+ ok(uni.Buffer == (void *) 0xdeadbeef,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets Buffer to %p, expected %x\n",
+ uni.Buffer, 0xdeadbeef);
+
+ uni.Length = 12345;
+ uni.MaximumLength = 12345;
+ uni.Buffer = (void *) 0xdeadbeef;
+ pRtlInitUnicodeString(&uni, teststring2);
+ ok(uni.Length == 33920,
+ "pRtlInitUnicodeString(&uni, 0) sets Length to %u, expected %u\n",
+ uni.Length, 33920);
+ ok(uni.MaximumLength == 33922,
+ "pRtlInitUnicodeString(&uni, 0) sets MaximumLength to %u, expected %u\n",
+ uni.MaximumLength, 33922);
+ ok(uni.Buffer == teststring2,
+ "pRtlInitUnicodeString(&uni, 0) sets Buffer to %p, expected %p\n",
+ uni.Buffer, teststring2);
+ ok(memcmp(uni.Buffer, teststring2, (TESTSTRING2_LEN + 1) * sizeof(WCHAR)) == 0,
+ "pRtlInitUnicodeString(&uni, 0) changes Buffer\n");
+
+ uni.Length = 12345;
+ uni.MaximumLength = 12345;
+ uni.Buffer = (void *) 0xdeadbeef;
+ result = pRtlInitUnicodeStringEx(&uni, 0);
+ ok(result == STATUS_SUCCESS,
+ "pRtlInitUnicodeStringEx(&uni, 0) returns %lx, expected 0\n",
+ result);
+ ok(uni.Length == 0,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets Length to %u, expected %u\n",
+ uni.Length, 0);
+ ok(uni.MaximumLength == 0,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets MaximumLength to %u, expected %u\n",
+ uni.MaximumLength, 0);
+ ok(uni.Buffer == NULL,
+ "pRtlInitUnicodeStringEx(&uni, 0) sets Buffer to %p, expected %p\n",
+ uni.Buffer, NULL);
+
+ uni.Length = 12345;
+ uni.MaximumLength = 12345;
+ uni.Buffer = (void *) 0xdeadbeef;
+ pRtlInitUnicodeString(&uni, 0);
+ ok(uni.Length == 0,
+ "pRtlInitUnicodeString(&uni, 0) sets Length to %u, expected %u\n",
+ uni.Length, 0);
+ ok(uni.MaximumLength == 0,
+ "pRtlInitUnicodeString(&uni, 0) sets MaximumLength to %u, expected %u\n",
+ uni.MaximumLength, 0);
+ ok(uni.Buffer == NULL,
+ "pRtlInitUnicodeString(&uni, 0) sets Buffer to %p, expected %p\n",
+ uni.Buffer, NULL);
+}
+
+
+typedef struct {
+ int add_nul;
+ int source_Length;
+ int source_MaximumLength;
+ int source_buf_size;
+ const char *source_buf;
+ int dest_Length;
+ int dest_MaximumLength;
+ int dest_buf_size;
+ const char *dest_buf;
+ int res_Length;
+ int res_MaximumLength;
+ int res_buf_size;
+ const char *res_buf;
+ NTSTATUS result;
+} dupl_ustr_t;
+
+static const dupl_ustr_t dupl_ustr[] = {
+ { 0, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 32, 32, 32, "This is a string", STATUS_SUCCESS},
+ { 0, 32, 32, 32, "This is a string", 40, 42, 42, "--------------------", 32, 32, 32, "This is a string", STATUS_SUCCESS},
+ { 0, 32, 30, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 0, 32, 34, 34, "This is a string", 40, 42, 42, NULL, 32, 32, 32, "This is a string", STATUS_SUCCESS},
+ { 0, 32, 32, 32, "This is a string", 40, 42, 42, NULL, 32, 32, 32, "This is a string", STATUS_SUCCESS},
+ { 0, 32, 30, 34, "This is a string", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 1, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 1, 32, 32, 32, "This is a string", 40, 42, 42, "--------------------", 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 1, 32, 30, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 1, 32, 34, 34, "This is a string", 40, 42, 42, NULL, 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 1, 32, 32, 32, "This is a string", 40, 42, 42, NULL, 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 1, 32, 30, 34, "This is a string", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 2, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 2, 32, 32, 32, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 2, 32, 30, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 2, 32, 34, 34, "This is a string", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 2, 32, 32, 32, "This is a string", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 2, 32, 30, 34, "This is a string", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 3, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 3, 32, 32, 32, "This is a string", 40, 42, 42, "--------------------", 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 3, 32, 30, 32, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 3, 32, 34, 34, "This is a string", 40, 42, 42, NULL, 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 3, 32, 32, 32, "This is a string", 40, 42, 42, NULL, 32, 34, 34, "This is a string", STATUS_SUCCESS},
+ { 3, 32, 30, 32, "This is a string", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 4, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 5, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 6, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 7, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 8, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 9, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {10, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {11, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {12, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {13, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {14, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {15, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {16, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {-1, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {-5, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ {-9, 32, 34, 34, "This is a string", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 0, 0, 2, 2, "", 40, 42, 42, "--------------------", 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 0, 0, 0, 0, "", 40, 42, 42, "--------------------", 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 0, 0, 2, 2, "", 40, 42, 42, NULL, 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 0, 0, 0, 0, "", 40, 42, 42, NULL, 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 0, 0, 2, 2, NULL, 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 0, 0, 0, 0, NULL, 40, 42, 42, "--------------------", 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 0, 0, 2, 2, NULL, 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 0, 0, 0, 0, NULL, 40, 42, 42, NULL, 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 1, 0, 2, 2, "", 40, 42, 42, "--------------------", 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 1, 0, 0, 0, "", 40, 42, 42, "--------------------", 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 1, 0, 2, 2, "", 40, 42, 42, NULL, 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 1, 0, 0, 0, "", 40, 42, 42, NULL, 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 1, 0, 2, 2, NULL, 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 1, 0, 0, 0, NULL, 40, 42, 42, "--------------------", 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 1, 0, 2, 2, NULL, 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 1, 0, 0, 0, NULL, 40, 42, 42, NULL, 0, 0, 0, NULL, STATUS_SUCCESS},
+ { 2, 0, 2, 2, "", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 2, 0, 0, 0, "", 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 2, 0, 2, 2, "", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 2, 0, 0, 0, "", 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 2, 0, 2, 2, NULL, 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 2, 0, 0, 0, NULL, 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 2, 0, 2, 2, NULL, 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 2, 0, 0, 0, NULL, 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 3, 0, 2, 2, "", 40, 42, 42, "--------------------", 0, 2, 2, "", STATUS_SUCCESS},
+ { 3, 0, 0, 0, "", 40, 42, 42, "--------------------", 0, 2, 2, "", STATUS_SUCCESS},
+ { 3, 0, 2, 2, "", 40, 42, 42, NULL, 0, 2, 2, "", STATUS_SUCCESS},
+ { 3, 0, 0, 0, "", 40, 42, 42, NULL, 0, 2, 2, "", STATUS_SUCCESS},
+ { 3, 0, 2, 2, NULL, 40, 42, 42, "--------------------", 40, 42, 42, "--------------------", STATUS_INVALID_PARAMETER},
+ { 3, 0, 0, 0, NULL, 40, 42, 42, "--------------------", 0, 2, 2, "", STATUS_SUCCESS},
+ { 3, 0, 2, 2, NULL, 40, 42, 42, NULL, 40, 42, 0, NULL, STATUS_INVALID_PARAMETER},
+ { 3, 0, 0, 0, NULL, 40, 42, 42, NULL, 0, 2, 2, "", STATUS_SUCCESS},
+};
+#define NB_DUPL_USTR (sizeof(dupl_ustr)/sizeof(*dupl_ustr))
+
+
+static void test_RtlDuplicateUnicodeString(void)
+{
+ size_t pos;
+ WCHAR source_buf[257];
+ WCHAR dest_buf[257];
+ WCHAR res_buf[257];
+ UNICODE_STRING source_str;
+ UNICODE_STRING dest_str;
+ UNICODE_STRING res_str;
+ CHAR dest_ansi_buf[257];
+ STRING dest_ansi_str;
+ NTSTATUS result;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_DUPL_USTR; test_num++) {
+ source_str.Length = dupl_ustr[test_num].source_Length;
+ source_str.MaximumLength = dupl_ustr[test_num].source_MaximumLength;
+ if (dupl_ustr[test_num].source_buf != NULL) {
+ for (pos = 0; pos < dupl_ustr[test_num].source_buf_size/sizeof(WCHAR); pos++) {
+ source_buf[pos] = dupl_ustr[test_num].source_buf[pos];
+ }
+ source_str.Buffer = source_buf;
+ } else {
+ source_str.Buffer = NULL;
+ }
+ dest_str.Length = dupl_ustr[test_num].dest_Length;
+ dest_str.MaximumLength = dupl_ustr[test_num].dest_MaximumLength;
+ if (dupl_ustr[test_num].dest_buf != NULL) {
+ for (pos = 0; pos < dupl_ustr[test_num].dest_buf_size/sizeof(WCHAR); pos++) {
+ dest_buf[pos] = dupl_ustr[test_num].dest_buf[pos];
+ }
+ dest_str.Buffer = dest_buf;
+ } else {
+ dest_str.Buffer = NULL;
+ }
+ res_str.Length = dupl_ustr[test_num].res_Length;
+ res_str.MaximumLength = dupl_ustr[test_num].res_MaximumLength;
+ if (dupl_ustr[test_num].res_buf != NULL) {
+ for (pos = 0; pos < dupl_ustr[test_num].res_buf_size/sizeof(WCHAR); pos++) {
+ res_buf[pos] = dupl_ustr[test_num].res_buf[pos];
+ }
+ res_str.Buffer = res_buf;
+ } else {
+ res_str.Buffer = NULL;
+ }
+ result = pRtlDuplicateUnicodeString(dupl_ustr[test_num].add_nul, &source_str, &dest_str);
+ dest_ansi_str.Length = dest_str.Length / sizeof(WCHAR);
+ dest_ansi_str.MaximumLength = dest_ansi_str.Length + 1;
+ for (pos = 0; pos < dest_ansi_str.Length; pos++) {
+ dest_ansi_buf[pos] = (char)dest_buf[pos];
+ }
+ dest_ansi_buf[dest_ansi_str.Length] = '\0';
+ dest_ansi_str.Buffer = dest_ansi_buf;
+ ok(result == dupl_ustr[test_num].result,
+ "(test %d): RtlDuplicateUnicodeString(%d, source, dest) has result %lx, expected %lx\n",
+ test_num, dupl_ustr[test_num].add_nul, result, dupl_ustr[test_num].result);
+ ok(dest_str.Length == dupl_ustr[test_num].res_Length,
+ "(test %d): RtlDuplicateUnicodeString(%d, source, dest) destination has Length %d, expected %d\n",
+ test_num, dupl_ustr[test_num].add_nul, dest_str.Length, dupl_ustr[test_num].res_Length);
+ ok(dest_str.MaximumLength == dupl_ustr[test_num].res_MaximumLength,
+ "(test %d): RtlDuplicateUnicodeString(%d, source, dest) destination has MaximumLength %d, expected %d\n",
+ test_num, dupl_ustr[test_num].add_nul, dest_str.MaximumLength, dupl_ustr[test_num].res_MaximumLength);
+ if (result == STATUS_INVALID_PARAMETER) {
+ ok((dest_str.Buffer == NULL && res_str.Buffer == NULL) ||
+ dest_str.Buffer == dest_buf,
+ "(test %d): RtlDuplicateUnicodeString(%d, source, dest) destination buffer changed %p expected %p\n",
+ test_num, dupl_ustr[test_num].add_nul, dest_str.Buffer, dest_buf);
+ } else {
+ ok(dest_str.Buffer != dest_buf,
+ "(test %d): RtlDuplicateUnicodeString(%d, source, dest) has destination buffer unchanged %p\n",
+ test_num, dupl_ustr[test_num].add_nul, dest_str.Buffer);
+ }
+ if (dest_str.Buffer != NULL && dupl_ustr[test_num].res_buf != NULL) {
+ ok(memcmp(dest_str.Buffer, res_str.Buffer, dupl_ustr[test_num].res_buf_size) == 0,
+ "(test %d): RtlDuplicateUnicodeString(%d, source, dest) has destination \"%s\" expected \"%s\"\n",
+ test_num, dupl_ustr[test_num].add_nul, dest_ansi_str.Buffer, dupl_ustr[test_num].res_buf);
+ } else {
+ ok(dest_str.Buffer == NULL && dupl_ustr[test_num].res_buf == NULL,
+ "(test %d): RtlDuplicateUnicodeString(%d, source, dest) has destination %p expected %p\n",
+ test_num, dupl_ustr[test_num].add_nul, dest_str.Buffer, dupl_ustr[test_num].res_buf);
+ }
+ }
+}
+
+
+static void test_RtlCopyString(void)
+{
+ static const char teststring[] = "Some Wild String";
+ char deststring[] = " ";
+ STRING str;
+ STRING deststr;
+
+ pRtlInitString(&str, teststring);
+ pRtlInitString(&deststr, deststring);
+ pRtlCopyString(&deststr, &str);
+ ok(strncmp(str.Buffer, deststring, str.Length) == 0, "String not copied\n");
+}
+
+
+static void test_RtlUpperChar(void)
+{
+ int ch;
+ int upper_ch;
+ int expected_upper_ch;
+ int byte_ch;
+
+ for (ch = -1; ch <= 1024; ch++) {
+ upper_ch = pRtlUpperChar(ch);
+ byte_ch = ch & 0xff;
+ if (byte_ch >= 'a' && byte_ch <= 'z') {
+ expected_upper_ch = (CHAR) (byte_ch - 'a' + 'A');
+ } else {
+ expected_upper_ch = (CHAR) byte_ch;
+ }
+ ok(upper_ch == expected_upper_ch,
+ "RtlUpperChar('%c'[=0x%x]) has result '%c'[=0x%x], expected '%c'[=0x%x]\n",
+ ch, ch, upper_ch, upper_ch, expected_upper_ch, expected_upper_ch);
+ }
+}
+
+
+static void test_RtlUpperString(void)
+{
+ int i;
+ CHAR ch;
+ CHAR upper_ch;
+ char ascii_buf[257];
+ char result_buf[257];
+ char upper_buf[257];
+ STRING ascii_str;
+ STRING result_str;
+ STRING upper_str;
+
+ for (i = 0; i <= 255; i++) {
+ ch = (CHAR) i;
+ if (ch >= 'a' && ch <= 'z') {
+ upper_ch = ch - 'a' + 'A';
+ } else {
+ upper_ch = ch;
+ }
+ ascii_buf[i] = ch;
+ result_buf[i] = '\0';
+ upper_buf[i] = upper_ch;
+ }
+ ascii_buf[i] = '\0';
+ result_buf[i] = '\0';
+ upper_buf[i] = '\0';
+ ascii_str.Length = 256;
+ ascii_str.MaximumLength = 256;
+ ascii_str.Buffer = ascii_buf;
+ result_str.Length = 256;
+ result_str.MaximumLength = 256;
+ result_str.Buffer = result_buf;
+ upper_str.Length = 256;
+ upper_str.MaximumLength = 256;
+ upper_str.Buffer = upper_buf;
+
+ pRtlUpperString(&result_str, &ascii_str);
+ ok(memcmp(result_str.Buffer, upper_str.Buffer, 256) == 0,
+ "RtlUpperString does not work as expected\n");
+}
+
+
+static void test_RtlUpcaseUnicodeChar(void)
+{
+ int i;
+ WCHAR ch;
+ WCHAR upper_ch;
+ WCHAR expected_upper_ch;
+
+ for (i = 0; i <= 255; i++) {
+ ch = (WCHAR) i;
+ upper_ch = pRtlUpcaseUnicodeChar(ch);
+ if (ch >= 'a' && ch <= 'z') {
+ expected_upper_ch = ch - 'a' + 'A';
+ } else if (ch >= 0xe0 && ch <= 0xfe && ch != 0xf7) {
+ expected_upper_ch = ch - 0x20;
+ } else if (ch == 0xff) {
+ expected_upper_ch = 0x178;
+ } else {
+ expected_upper_ch = ch;
+ }
+ ok(upper_ch == expected_upper_ch,
+ "RtlUpcaseUnicodeChar('%c'[=0x%x]) has result '%c'[=0x%x], expected: '%c'[=0x%x]\n",
+ ch, ch, upper_ch, upper_ch, expected_upper_ch, expected_upper_ch);
+ }
+}
+
+
+static void test_RtlUpcaseUnicodeString(void)
+{
+ int i;
+ WCHAR ch;
+ WCHAR upper_ch;
+ WCHAR ascii_buf[257];
+ WCHAR result_buf[257];
+ WCHAR upper_buf[257];
+ UNICODE_STRING ascii_str;
+ UNICODE_STRING result_str;
+ UNICODE_STRING upper_str;
+
+ for (i = 0; i <= 255; i++) {
+ ch = (WCHAR) i;
+ if (ch >= 'a' && ch <= 'z') {
+ upper_ch = ch - 'a' + 'A';
+ } else if (ch >= 0xe0 && ch <= 0xfe && ch != 0xf7) {
+ upper_ch = ch - 0x20;
+ } else if (ch == 0xff) {
+ upper_ch = 0x178;
+ } else {
+ upper_ch = ch;
+ }
+ ascii_buf[i] = ch;
+ result_buf[i] = '\0';
+ upper_buf[i] = upper_ch;
+ }
+ ascii_buf[i] = '\0';
+ result_buf[i] = '\0';
+ upper_buf[i] = '\0';
+ ascii_str.Length = 512;
+ ascii_str.MaximumLength = 512;
+ ascii_str.Buffer = ascii_buf;
+ result_str.Length = 512;
+ result_str.MaximumLength = 512;
+ result_str.Buffer = result_buf;
+ upper_str.Length = 512;
+ upper_str.MaximumLength = 512;
+ upper_str.Buffer = upper_buf;
+
+ pRtlUpcaseUnicodeString(&result_str, &ascii_str, 0);
+ for (i = 0; i <= 255; i++) {
+ ok(result_str.Buffer[i] == upper_str.Buffer[i],
+ "RtlUpcaseUnicodeString works wrong: '%c'[=0x%x] is converted to '%c'[=0x%x], expected: '%c'[=0x%x]\n",
+ ascii_str.Buffer[i], ascii_str.Buffer[i],
+ result_str.Buffer[i], result_str.Buffer[i],
+ upper_str.Buffer[i], upper_str.Buffer[i]);
+ }
+}
+
+
+static void test_RtlDowncaseUnicodeString(void)
+{
+ int i;
+ WCHAR ch;
+ WCHAR lower_ch;
+ WCHAR source_buf[1025];
+ WCHAR result_buf[1025];
+ WCHAR lower_buf[1025];
+ UNICODE_STRING source_str;
+ UNICODE_STRING result_str;
+ UNICODE_STRING lower_str;
+
+ for (i = 0; i <= 1024; i++) {
+ ch = (WCHAR) i;
+ if (ch >= 'A' && ch <= 'Z') {
+ lower_ch = ch - 'A' + 'a';
+ } else if (ch >= 0xc0 && ch <= 0xde && ch != 0xd7) {
+ lower_ch = ch + 0x20;
+ } else if (ch >= 0x391 && ch <= 0x3ab && ch != 0x3a2) {
+ lower_ch = ch + 0x20;
+ } else {
+ switch (ch) {
+ case 0x178: lower_ch = 0xff; break;
+ case 0x181: lower_ch = 0x253; break;
+ case 0x186: lower_ch = 0x254; break;
+ case 0x189: lower_ch = 0x256; break;
+ case 0x18a: lower_ch = 0x257; break;
+ case 0x18e: lower_ch = 0x1dd; break;
+ case 0x18f: lower_ch = 0x259; break;
+ case 0x190: lower_ch = 0x25b; break;
+ case 0x193: lower_ch = 0x260; break;
+ case 0x194: lower_ch = 0x263; break;
+ case 0x196: lower_ch = 0x269; break;
+ case 0x197: lower_ch = 0x268; break;
+ case 0x19c: lower_ch = 0x26f; break;
+ case 0x19d: lower_ch = 0x272; break;
+ case 0x19f: lower_ch = 0x275; break;
+ case 0x1a9: lower_ch = 0x283; break;
+ case 0x1ae: lower_ch = 0x288; break;
+ case 0x1b1: lower_ch = 0x28a; break;
+ case 0x1b2: lower_ch = 0x28b; break;
+ case 0x1b7: lower_ch = 0x292; break;
+ case 0x1c4: lower_ch = 0x1c6; break;
+ case 0x1c7: lower_ch = 0x1c9; break;
+ case 0x1ca: lower_ch = 0x1cc; break;
+ case 0x1f1: lower_ch = 0x1f3; break;
+ case 0x386: lower_ch = 0x3ac; break;
+ case 0x388: lower_ch = 0x3ad; break;
+ case 0x389: lower_ch = 0x3ae; break;
+ case 0x38a: lower_ch = 0x3af; break;
+ case 0x38c: lower_ch = 0x3cc; break;
+ case 0x38e: lower_ch = 0x3cd; break;
+ case 0x38f: lower_ch = 0x3ce; break;
+ case 0x400: lower_ch = 0x0; break;
+ default: lower_ch = ch; break;
+ } /* switch */
+ }
+ source_buf[i] = ch;
+ result_buf[i] = '\0';
+ lower_buf[i] = lower_ch;
+ }
+ source_buf[i] = '\0';
+ result_buf[i] = '\0';
+ lower_buf[i] = '\0';
+ source_str.Length = 2048;
+ source_str.MaximumLength = 2048;
+ source_str.Buffer = source_buf;
+ result_str.Length = 2048;
+ result_str.MaximumLength = 2048;
+ result_str.Buffer = result_buf;
+ lower_str.Length = 2048;
+ lower_str.MaximumLength = 2048;
+ lower_str.Buffer = lower_buf;
+
+ pRtlDowncaseUnicodeString(&result_str, &source_str, 0);
+ for (i = 0; i <= 1024; i++) {
+ ok(result_str.Buffer[i] == lower_str.Buffer[i] || result_str.Buffer[i] == source_str.Buffer[i] + 1,
+ "RtlDowncaseUnicodeString works wrong: '%c'[=0x%x] is converted to '%c'[=0x%x], expected: '%c'[=0x%x]\n",
+ source_str.Buffer[i], source_str.Buffer[i],
+ result_str.Buffer[i], result_str.Buffer[i],
+ lower_str.Buffer[i], lower_str.Buffer[i]);
+ }
+}
+
+
+typedef struct {
+ int ansi_Length;
+ int ansi_MaximumLength;
+ int ansi_buf_size;
+ const char *ansi_buf;
+ int uni_Length;
+ int uni_MaximumLength;
+ int uni_buf_size;
+ const char *uni_buf;
+ BOOLEAN doalloc;
+ int res_Length;
+ int res_MaximumLength;
+ int res_buf_size;
+ const char *res_buf;
+ NTSTATUS result;
+} ustr2astr_t;
+
+static const ustr2astr_t ustr2astr[] = {
+ { 10, 12, 12, "------------", 0, 0, 0, "", TRUE, 0, 1, 1, "", STATUS_SUCCESS},
+ { 10, 12, 12, "------------", 12, 12, 12, "abcdef", TRUE, 6, 7, 7, "abcdef", STATUS_SUCCESS},
+ { 0, 2, 12, "------------", 12, 12, 12, "abcdef", TRUE, 6, 7, 7, "abcdef", STATUS_SUCCESS},
+ { 10, 12, 12, NULL, 12, 12, 12, "abcdef", TRUE, 6, 7, 7, "abcdef", STATUS_SUCCESS},
+ { 0, 0, 12, "------------", 12, 12, 12, "abcdef", FALSE, 6, 0, 0, "", STATUS_BUFFER_OVERFLOW},
+ { 0, 1, 12, "------------", 12, 12, 12, "abcdef", FALSE, 0, 1, 1, "", STATUS_BUFFER_OVERFLOW},
+ { 0, 2, 12, "------------", 12, 12, 12, "abcdef", FALSE, 1, 2, 2, "a", STATUS_BUFFER_OVERFLOW},
+ { 0, 3, 12, "------------", 12, 12, 12, "abcdef", FALSE, 2, 3, 3, "ab", STATUS_BUFFER_OVERFLOW},
+ { 0, 5, 12, "------------", 12, 12, 12, "abcdef", FALSE, 4, 5, 5, "abcd", STATUS_BUFFER_OVERFLOW},
+ { 8, 5, 12, "------------", 12, 12, 12, "abcdef", FALSE, 4, 5, 5, "abcd", STATUS_BUFFER_OVERFLOW},
+ { 8, 6, 12, "------------", 12, 12, 12, "abcdef", FALSE, 5, 6, 6, "abcde", STATUS_BUFFER_OVERFLOW},
+ { 8, 7, 12, "------------", 12, 12, 12, "abcdef", FALSE, 6, 7, 7, "abcdef", STATUS_SUCCESS},
+ { 8, 7, 12, "------------", 0, 12, 12, NULL, FALSE, 0, 7, 0, "", STATUS_SUCCESS},
+ { 0, 0, 12, NULL, 10, 10, 12, NULL, FALSE, 5, 0, 0, NULL, STATUS_BUFFER_OVERFLOW},
+};
+#define NB_USTR2ASTR (sizeof(ustr2astr)/sizeof(*ustr2astr))
+
+
+static void test_RtlUnicodeStringToAnsiString(void)
+{
+ size_t pos;
+ CHAR ansi_buf[257];
+ WCHAR uni_buf[257];
+ STRING ansi_str;
+ UNICODE_STRING uni_str;
+ NTSTATUS result;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_USTR2ASTR; test_num++) {
+ ansi_str.Length = ustr2astr[test_num].ansi_Length;
+ ansi_str.MaximumLength = ustr2astr[test_num].ansi_MaximumLength;
+ if (ustr2astr[test_num].ansi_buf != NULL) {
+ memcpy(ansi_buf, ustr2astr[test_num].ansi_buf, ustr2astr[test_num].ansi_buf_size);
+ ansi_buf[ustr2astr[test_num].ansi_buf_size] = '\0';
+ ansi_str.Buffer = ansi_buf;
+ } else {
+ ansi_str.Buffer = NULL;
+ }
+ uni_str.Length = ustr2astr[test_num].uni_Length;
+ uni_str.MaximumLength = ustr2astr[test_num].uni_MaximumLength;
+ if (ustr2astr[test_num].uni_buf != NULL) {
+ for (pos = 0; pos < ustr2astr[test_num].uni_buf_size/sizeof(WCHAR); pos++) {
+ uni_buf[pos] = ustr2astr[test_num].uni_buf[pos];
+ }
+ uni_str.Buffer = uni_buf;
+ } else {
+ uni_str.Buffer = NULL;
+ }
+ result = pRtlUnicodeStringToAnsiString(&ansi_str, &uni_str, ustr2astr[test_num].doalloc);
+ ok(result == ustr2astr[test_num].result,
+ "(test %d): RtlUnicodeStringToAnsiString(ansi, uni, %d) has result %lx, expected %lx\n",
+ test_num, ustr2astr[test_num].doalloc, result, ustr2astr[test_num].result);
+ ok(ansi_str.Length == ustr2astr[test_num].res_Length,
+ "(test %d): RtlUnicodeStringToAnsiString(ansi, uni, %d) ansi has Length %d, expected %d\n",
+ test_num, ustr2astr[test_num].doalloc, ansi_str.Length, ustr2astr[test_num].res_Length);
+ ok(ansi_str.MaximumLength == ustr2astr[test_num].res_MaximumLength,
+ "(test %d): RtlUnicodeStringToAnsiString(ansi, uni, %d) ansi has MaximumLength %d, expected %d\n",
+ test_num, ustr2astr[test_num].doalloc, ansi_str.MaximumLength, ustr2astr[test_num].res_MaximumLength);
+ ok(memcmp(ansi_str.Buffer, ustr2astr[test_num].res_buf, ustr2astr[test_num].res_buf_size) == 0,
+ "(test %d): RtlUnicodeStringToAnsiString(ansi, uni, %d) has ansi \"%s\" expected \"%s\"\n",
+ test_num, ustr2astr[test_num].doalloc, ansi_str.Buffer, ustr2astr[test_num].res_buf);
+ }
+}
+
+
+typedef struct {
+ int dest_Length;
+ int dest_MaximumLength;
+ int dest_buf_size;
+ const char *dest_buf;
+ const char *src;
+ int res_Length;
+ int res_MaximumLength;
+ int res_buf_size;
+ const char *res_buf;
+ NTSTATUS result;
+} app_asc2str_t;
+
+static const app_asc2str_t app_asc2str[] = {
+ { 5, 12, 15, "TestS01234abcde", "tring", 10, 12, 15, "TestStringabcde", STATUS_SUCCESS},
+ { 5, 11, 15, "TestS01234abcde", "tring", 10, 11, 15, "TestStringabcde", STATUS_SUCCESS},
+ { 5, 10, 15, "TestS01234abcde", "tring", 10, 10, 15, "TestStringabcde", STATUS_SUCCESS},
+ { 5, 9, 15, "TestS01234abcde", "tring", 5, 9, 15, "TestS01234abcde", STATUS_BUFFER_TOO_SMALL},
+ { 5, 0, 15, "TestS01234abcde", "tring", 5, 0, 15, "TestS01234abcde", STATUS_BUFFER_TOO_SMALL},
+ { 5, 14, 15, "TestS01234abcde", "tring", 10, 14, 15, "TestStringabcde", STATUS_SUCCESS},
+ { 5, 14, 15, "TestS01234abcde", NULL, 5, 14, 15, "TestS01234abcde", STATUS_SUCCESS},
+ { 5, 14, 15, NULL, NULL, 5, 14, 15, NULL, STATUS_SUCCESS},
+ { 5, 12, 15, "Tst\0S01234abcde", "tr\0i", 7, 12, 15, "Tst\0Str234abcde", STATUS_SUCCESS},
+};
+#define NB_APP_ASC2STR (sizeof(app_asc2str)/sizeof(*app_asc2str))
+
+
+static void test_RtlAppendAsciizToString(void)
+{
+ CHAR dest_buf[257];
+ STRING dest_str;
+ NTSTATUS result;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_APP_ASC2STR; test_num++) {
+ dest_str.Length = app_asc2str[test_num].dest_Length;
+ dest_str.MaximumLength = app_asc2str[test_num].dest_MaximumLength;
+ if (app_asc2str[test_num].dest_buf != NULL) {
+ memcpy(dest_buf, app_asc2str[test_num].dest_buf, app_asc2str[test_num].dest_buf_size);
+ dest_buf[app_asc2str[test_num].dest_buf_size] = '\0';
+ dest_str.Buffer = dest_buf;
+ } else {
+ dest_str.Buffer = NULL;
+ }
+ result = pRtlAppendAsciizToString(&dest_str, app_asc2str[test_num].src);
+ ok(result == app_asc2str[test_num].result,
+ "(test %d): RtlAppendAsciizToString(dest, src) has result %lx, expected %lx\n",
+ test_num, result, app_asc2str[test_num].result);
+ ok(dest_str.Length == app_asc2str[test_num].res_Length,
+ "(test %d): RtlAppendAsciizToString(dest, src) dest has Length %d, expected %d\n",
+ test_num, dest_str.Length, app_asc2str[test_num].res_Length);
+ ok(dest_str.MaximumLength == app_asc2str[test_num].res_MaximumLength,
+ "(test %d): RtlAppendAsciizToString(dest, src) dest has MaximumLength %d, expected %d\n",
+ test_num, dest_str.MaximumLength, app_asc2str[test_num].res_MaximumLength);
+ if (dest_str.Buffer == dest_buf) {
+ ok(memcmp(dest_buf, app_asc2str[test_num].res_buf, app_asc2str[test_num].res_buf_size) == 0,
+ "(test %d): RtlAppendAsciizToString(dest, src) has dest \"%s\" expected \"%s\"\n",
+ test_num, dest_buf, app_asc2str[test_num].res_buf);
+ } else {
+ ok(dest_str.Buffer == app_asc2str[test_num].res_buf,
+ "(test %d): RtlAppendAsciizToString(dest, src) dest has Buffer %p expected %p\n",
+ test_num, dest_str.Buffer, app_asc2str[test_num].res_buf);
+ }
+ }
+}
+
+
+typedef struct {
+ int dest_Length;
+ int dest_MaximumLength;
+ int dest_buf_size;
+ const char *dest_buf;
+ int src_Length;
+ int src_MaximumLength;
+ int src_buf_size;
+ const char *src_buf;
+ int res_Length;
+ int res_MaximumLength;
+ int res_buf_size;
+ const char *res_buf;
+ NTSTATUS result;
+} app_str2str_t;
+
+static const app_str2str_t app_str2str[] = {
+ { 5, 12, 15, "TestS01234abcde", 5, 5, 7, "tringZY", 10, 12, 15, "TestStringabcde", STATUS_SUCCESS},
+ { 5, 11, 15, "TestS01234abcde", 5, 5, 7, "tringZY", 10, 11, 15, "TestStringabcde", STATUS_SUCCESS},
+ { 5, 10, 15, "TestS01234abcde", 5, 5, 7, "tringZY", 10, 10, 15, "TestStringabcde", STATUS_SUCCESS},
+ { 5, 9, 15, "TestS01234abcde", 5, 5, 7, "tringZY", 5, 9, 15, "TestS01234abcde", STATUS_BUFFER_TOO_SMALL},
+ { 5, 0, 15, "TestS01234abcde", 0, 0, 7, "tringZY", 5, 0, 15, "TestS01234abcde", STATUS_SUCCESS},
+ { 5, 14, 15, "TestS01234abcde", 0, 0, 7, "tringZY", 5, 14, 15, "TestS01234abcde", STATUS_SUCCESS},
+ { 5, 14, 15, "TestS01234abcde", 0, 0, 7, NULL, 5, 14, 15, "TestS01234abcde", STATUS_SUCCESS},
+ { 5, 14, 15, NULL, 0, 0, 7, NULL, 5, 14, 15, NULL, STATUS_SUCCESS},
+ { 5, 12, 15, "Tst\0S01234abcde", 4, 4, 7, "tr\0iZY", 9, 12, 15, "Tst\0Str\0i4abcde", STATUS_SUCCESS},
+};
+#define NB_APP_STR2STR (sizeof(app_str2str)/sizeof(*app_str2str))
+
+
+static void test_RtlAppendStringToString(void)
+{
+ CHAR dest_buf[257];
+ CHAR src_buf[257];
+ STRING dest_str;
+ STRING src_str;
+ NTSTATUS result;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_APP_STR2STR; test_num++) {
+ dest_str.Length = app_str2str[test_num].dest_Length;
+ dest_str.MaximumLength = app_str2str[test_num].dest_MaximumLength;
+ if (app_str2str[test_num].dest_buf != NULL) {
+ memcpy(dest_buf, app_str2str[test_num].dest_buf, app_str2str[test_num].dest_buf_size);
+ dest_buf[app_str2str[test_num].dest_buf_size] = '\0';
+ dest_str.Buffer = dest_buf;
+ } else {
+ dest_str.Buffer = NULL;
+ }
+ src_str.Length = app_str2str[test_num].src_Length;
+ src_str.MaximumLength = app_str2str[test_num].src_MaximumLength;
+ if (app_str2str[test_num].src_buf != NULL) {
+ memcpy(src_buf, app_str2str[test_num].src_buf, app_str2str[test_num].src_buf_size);
+ src_buf[app_str2str[test_num].src_buf_size] = '\0';
+ src_str.Buffer = src_buf;
+ } else {
+ src_str.Buffer = NULL;
+ }
+ result = pRtlAppendStringToString(&dest_str, &src_str);
+ ok(result == app_str2str[test_num].result,
+ "(test %d): RtlAppendStringToString(dest, src) has result %lx, expected %lx\n",
+ test_num, result, app_str2str[test_num].result);
+ ok(dest_str.Length == app_str2str[test_num].res_Length,
+ "(test %d): RtlAppendStringToString(dest, src) dest has Length %d, expected %d\n",
+ test_num, dest_str.Length, app_str2str[test_num].res_Length);
+ ok(dest_str.MaximumLength == app_str2str[test_num].res_MaximumLength,
+ "(test %d): RtlAppendStringToString(dest, src) dest has MaximumLength %d, expected %d\n",
+ test_num, dest_str.MaximumLength, app_str2str[test_num].res_MaximumLength);
+ if (dest_str.Buffer == dest_buf) {
+ ok(memcmp(dest_buf, app_str2str[test_num].res_buf, app_str2str[test_num].res_buf_size) == 0,
+ "(test %d): RtlAppendStringToString(dest, src) has dest \"%s\" expected \"%s\"\n",
+ test_num, dest_buf, app_str2str[test_num].res_buf);
+ } else {
+ ok(dest_str.Buffer == app_str2str[test_num].res_buf,
+ "(test %d): RtlAppendStringToString(dest, src) dest has Buffer %p expected %p\n",
+ test_num, dest_str.Buffer, app_str2str[test_num].res_buf);
+ }
+ }
+}
+
+
+typedef struct {
+ int dest_Length;
+ int dest_MaximumLength;
+ int dest_buf_size;
+ const char *dest_buf;
+ const char *src;
+ int res_Length;
+ int res_MaximumLength;
+ int res_buf_size;
+ const char *res_buf;
+ NTSTATUS result;
+} app_uni2str_t;
+
+static const app_uni2str_t app_uni2str[] = {
+ { 4, 12, 14, "Fake0123abcdef", "Ustr\0", 8, 12, 14, "FakeUstr\0\0cdef", STATUS_SUCCESS},
+ { 4, 11, 14, "Fake0123abcdef", "Ustr\0", 8, 11, 14, "FakeUstr\0\0cdef", STATUS_SUCCESS},
+ { 4, 10, 14, "Fake0123abcdef", "Ustr\0", 8, 10, 14, "FakeUstr\0\0cdef", STATUS_SUCCESS},
+/* In the following test the native function writes beyond MaximumLength
+ * { 4, 9, 14, "Fake0123abcdef", "Ustr\0", 8, 9, 14, "FakeUstrabcdef", STATUS_SUCCESS},
+ */
+ { 4, 8, 14, "Fake0123abcdef", "Ustr\0", 8, 8, 14, "FakeUstrabcdef", STATUS_SUCCESS},
+ { 4, 7, 14, "Fake0123abcdef", "Ustr\0", 4, 7, 14, "Fake0123abcdef", STATUS_BUFFER_TOO_SMALL},
+ { 4, 0, 14, "Fake0123abcdef", "Ustr\0", 4, 0, 14, "Fake0123abcdef", STATUS_BUFFER_TOO_SMALL},
+ { 4, 14, 14, "Fake0123abcdef", "Ustr\0", 8, 14, 14, "FakeUstr\0\0cdef", STATUS_SUCCESS},
+ { 4, 14, 14, "Fake0123abcdef", NULL, 4, 14, 14, "Fake0123abcdef", STATUS_SUCCESS},
+ { 4, 14, 14, NULL, NULL, 4, 14, 14, NULL, STATUS_SUCCESS},
+ { 4, 14, 14, "Fake0123abcdef", "U\0stri\0", 10, 14, 14, "FakeU\0stri\0\0ef", STATUS_SUCCESS},
+ { 6, 14, 16, "Te\0\0stabcdefghij", "St\0\0ri", 8, 14, 16, "Te\0\0stSt\0\0efghij", STATUS_SUCCESS},
+};
+#define NB_APP_UNI2STR (sizeof(app_uni2str)/sizeof(*app_uni2str))
+
+
+static void test_RtlAppendUnicodeToString(void)
+{
+ WCHAR dest_buf[257];
+ UNICODE_STRING dest_str;
+ NTSTATUS result;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_APP_UNI2STR; test_num++) {
+ dest_str.Length = app_uni2str[test_num].dest_Length;
+ dest_str.MaximumLength = app_uni2str[test_num].dest_MaximumLength;
+ if (app_uni2str[test_num].dest_buf != NULL) {
+ memcpy(dest_buf, app_uni2str[test_num].dest_buf, app_uni2str[test_num].dest_buf_size);
+ dest_buf[app_uni2str[test_num].dest_buf_size/sizeof(WCHAR)] = '\0';
+ dest_str.Buffer = dest_buf;
+ } else {
+ dest_str.Buffer = NULL;
+ }
+ result = pRtlAppendUnicodeToString(&dest_str, (LPCWSTR) app_uni2str[test_num].src);
+ ok(result == app_uni2str[test_num].result,
+ "(test %d): RtlAppendUnicodeToString(dest, src) has result %lx, expected %lx\n",
+ test_num, result, app_uni2str[test_num].result);
+ ok(dest_str.Length == app_uni2str[test_num].res_Length,
+ "(test %d): RtlAppendUnicodeToString(dest, src) dest has Length %d, expected %d\n",
+ test_num, dest_str.Length, app_uni2str[test_num].res_Length);
+ ok(dest_str.MaximumLength == app_uni2str[test_num].res_MaximumLength,
+ "(test %d): RtlAppendUnicodeToString(dest, src) dest has MaximumLength %d, expected %d\n",
+ test_num, dest_str.MaximumLength, app_uni2str[test_num].res_MaximumLength);
+ if (dest_str.Buffer == dest_buf) {
+ ok(memcmp(dest_buf, app_uni2str[test_num].res_buf, app_uni2str[test_num].res_buf_size) == 0,
+ "(test %d): RtlAppendUnicodeToString(dest, src) has dest \"%s\" expected \"%s\"\n",
+ test_num, (char *) dest_buf, app_uni2str[test_num].res_buf);
+ } else {
+ ok(dest_str.Buffer == (WCHAR *) app_uni2str[test_num].res_buf,
+ "(test %d): RtlAppendUnicodeToString(dest, src) dest has Buffer %p expected %p\n",
+ test_num, dest_str.Buffer, app_uni2str[test_num].res_buf);
+ }
+ }
+}
+
+
+typedef struct {
+ int dest_Length;
+ int dest_MaximumLength;
+ int dest_buf_size;
+ const char *dest_buf;
+ int src_Length;
+ int src_MaximumLength;
+ int src_buf_size;
+ const char *src_buf;
+ int res_Length;
+ int res_MaximumLength;
+ int res_buf_size;
+ const char *res_buf;
+ NTSTATUS result;
+} app_ustr2str_t;
+
+static const app_ustr2str_t app_ustr2str[] = {
+ { 4, 12, 14, "Fake0123abcdef", 4, 6, 8, "UstrZYXW", 8, 12, 14, "FakeUstr\0\0cdef", STATUS_SUCCESS},
+ { 4, 11, 14, "Fake0123abcdef", 4, 6, 8, "UstrZYXW", 8, 11, 14, "FakeUstr\0\0cdef", STATUS_SUCCESS},
+ { 4, 10, 14, "Fake0123abcdef", 4, 6, 8, "UstrZYXW", 8, 10, 14, "FakeUstr\0\0cdef", STATUS_SUCCESS},
+/* In the following test the native function writes beyond MaximumLength
+ * { 4, 9, 14, "Fake0123abcdef", 4, 6, 8, "UstrZYXW", 8, 9, 14, "FakeUstrabcdef", STATUS_SUCCESS},
+ */
+ { 4, 8, 14, "Fake0123abcdef", 4, 6, 8, "UstrZYXW", 8, 8, 14, "FakeUstrabcdef", STATUS_SUCCESS},
+ { 4, 7, 14, "Fake0123abcdef", 4, 6, 8, "UstrZYXW", 4, 7, 14, "Fake0123abcdef", STATUS_BUFFER_TOO_SMALL},
+ { 4, 0, 14, "Fake0123abcdef", 0, 0, 8, "UstrZYXW", 4, 0, 14, "Fake0123abcdef", STATUS_SUCCESS},
+ { 4, 14, 14, "Fake0123abcdef", 0, 0, 8, "UstrZYXW", 4, 14, 14, "Fake0123abcdef", STATUS_SUCCESS},
+ { 4, 14, 14, "Fake0123abcdef", 0, 0, 8, NULL, 4, 14, 14, "Fake0123abcdef", STATUS_SUCCESS},
+ { 4, 14, 14, NULL, 0, 0, 8, NULL, 4, 14, 14, NULL, STATUS_SUCCESS},
+ { 6, 14, 16, "Te\0\0stabcdefghij", 6, 8, 8, "St\0\0riZY", 12, 14, 16, "Te\0\0stSt\0\0ri\0\0ij", STATUS_SUCCESS},
+};
+#define NB_APP_USTR2STR (sizeof(app_ustr2str)/sizeof(*app_ustr2str))
+
+
+static void test_RtlAppendUnicodeStringToString(void)
+{
+ WCHAR dest_buf[257];
+ WCHAR src_buf[257];
+ UNICODE_STRING dest_str;
+ UNICODE_STRING src_str;
+ NTSTATUS result;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_APP_USTR2STR; test_num++) {
+ dest_str.Length = app_ustr2str[test_num].dest_Length;
+ dest_str.MaximumLength = app_ustr2str[test_num].dest_MaximumLength;
+ if (app_ustr2str[test_num].dest_buf != NULL) {
+ memcpy(dest_buf, app_ustr2str[test_num].dest_buf, app_ustr2str[test_num].dest_buf_size);
+ dest_buf[app_ustr2str[test_num].dest_buf_size/sizeof(WCHAR)] = '\0';
+ dest_str.Buffer = dest_buf;
+ } else {
+ dest_str.Buffer = NULL;
+ }
+ src_str.Length = app_ustr2str[test_num].src_Length;
+ src_str.MaximumLength = app_ustr2str[test_num].src_MaximumLength;
+ if (app_ustr2str[test_num].src_buf != NULL) {
+ memcpy(src_buf, app_ustr2str[test_num].src_buf, app_ustr2str[test_num].src_buf_size);
+ src_buf[app_ustr2str[test_num].src_buf_size/sizeof(WCHAR)] = '\0';
+ src_str.Buffer = src_buf;
+ } else {
+ src_str.Buffer = NULL;
+ }
+ result = pRtlAppendUnicodeStringToString(&dest_str, &src_str);
+ ok(result == app_ustr2str[test_num].result,
+ "(test %d): RtlAppendStringToString(dest, src) has result %lx, expected %lx\n",
+ test_num, result, app_ustr2str[test_num].result);
+ ok(dest_str.Length == app_ustr2str[test_num].res_Length,
+ "(test %d): RtlAppendStringToString(dest, src) dest has Length %d, expected %d\n",
+ test_num, dest_str.Length, app_ustr2str[test_num].res_Length);
+ ok(dest_str.MaximumLength == app_ustr2str[test_num].res_MaximumLength,
+ "(test %d): RtlAppendStringToString(dest, src) dest has MaximumLength %d, expected %d\n",
+ test_num, dest_str.MaximumLength, app_ustr2str[test_num].res_MaximumLength);
+ if (dest_str.Buffer == dest_buf) {
+ ok(memcmp(dest_buf, app_ustr2str[test_num].res_buf, app_ustr2str[test_num].res_buf_size) == 0,
+ "(test %d): RtlAppendStringToString(dest, src) has dest \"%s\" expected \"%s\"\n",
+ test_num, (char *) dest_buf, app_ustr2str[test_num].res_buf);
+ } else {
+ ok(dest_str.Buffer == (WCHAR *) app_ustr2str[test_num].res_buf,
+ "(test %d): RtlAppendStringToString(dest, src) dest has Buffer %p expected %p\n",
+ test_num, dest_str.Buffer, app_ustr2str[test_num].res_buf);
+ }
+ }
+}
+
+
+typedef struct {
+ int flags;
+ const char *main_str;
+ const char *search_chars;
+ USHORT pos;
+ NTSTATUS result;
+} find_ch_in_ustr_t;
+
+static const find_ch_in_ustr_t find_ch_in_ustr[] = {
+ { 0, "Some Wild String", "S", 2, STATUS_SUCCESS},
+ { 0, "This is a String", "String", 6, STATUS_SUCCESS},
+ { 1, "This is a String", "String", 30, STATUS_SUCCESS},
+ { 2, "This is a String", "String", 2, STATUS_SUCCESS},
+ { 3, "This is a String", "String", 18, STATUS_SUCCESS},
+ { 0, "This is a String", "Wild", 6, STATUS_SUCCESS},
+ { 1, "This is a String", "Wild", 26, STATUS_SUCCESS},
+ { 2, "This is a String", "Wild", 2, STATUS_SUCCESS},
+ { 3, "This is a String", "Wild", 30, STATUS_SUCCESS},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "", 0, STATUS_NOT_FOUND},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "123", 0, STATUS_NOT_FOUND},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "a", 2, STATUS_SUCCESS},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "12a34", 2, STATUS_SUCCESS},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "12b34", 4, STATUS_SUCCESS},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "12y34", 50, STATUS_SUCCESS},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "12z34", 52, STATUS_SUCCESS},
+ { 0, "abcdefghijklmnopqrstuvwxyz", "rvz", 36, STATUS_SUCCESS},
+ { 0, "abcdefghijklmmlkjihgfedcba", "egik", 10, STATUS_SUCCESS},
+ { 1, "abcdefghijklmnopqrstuvwxyz", "", 0, STATUS_NOT_FOUND},
+ { 1, "abcdefghijklmnopqrstuvwxyz", "rvz", 50, STATUS_SUCCESS},
+ { 1, "abcdefghijklmnopqrstuvwxyz", "ravy", 48, STATUS_SUCCESS},
+ { 1, "abcdefghijklmnopqrstuvwxyz", "raxv", 46, STATUS_SUCCESS},
+ { 2, "abcdefghijklmnopqrstuvwxyz", "", 2, STATUS_SUCCESS},
+ { 2, "abcdefghijklmnopqrstuvwxyz", "rvz", 2, STATUS_SUCCESS},
+ { 2, "abcdefghijklmnopqrstuvwxyz", "vaz", 4, STATUS_SUCCESS},
+ { 2, "abcdefghijklmnopqrstuvwxyz", "ravbz", 6, STATUS_SUCCESS},
+ { 3, "abcdefghijklmnopqrstuvwxyz", "", 50, STATUS_SUCCESS},
+ { 3, "abcdefghijklmnopqrstuvwxyz", "123", 50, STATUS_SUCCESS},
+ { 3, "abcdefghijklmnopqrstuvwxyz", "ahp", 50, STATUS_SUCCESS},
+ { 3, "abcdefghijklmnopqrstuvwxyz", "rvz", 48, STATUS_SUCCESS},
+ { 0, NULL, "abc", 0, STATUS_NOT_FOUND},
+ { 1, NULL, "abc", 0, STATUS_NOT_FOUND},
+ { 2, NULL, "abc", 0, STATUS_NOT_FOUND},
+ { 3, NULL, "abc", 0, STATUS_NOT_FOUND},
+ { 0, "abcdefghijklmnopqrstuvwxyz", NULL, 0, STATUS_NOT_FOUND},
+ { 1, "abcdefghijklmnopqrstuvwxyz", NULL, 0, STATUS_NOT_FOUND},
+ { 2, "abcdefghijklmnopqrstuvwxyz", NULL, 2, STATUS_SUCCESS},
+ { 3, "abcdefghijklmnopqrstuvwxyz", NULL, 50, STATUS_SUCCESS},
+ { 0, NULL, NULL, 0, STATUS_NOT_FOUND},
+ { 1, NULL, NULL, 0, STATUS_NOT_FOUND},
+ { 2, NULL, NULL, 0, STATUS_NOT_FOUND},
+ { 3, NULL, NULL, 0, STATUS_NOT_FOUND},
+ { 0, "abcdabcdabcdabcdabcdabcd", "abcd", 2, STATUS_SUCCESS},
+ { 1, "abcdabcdabcdabcdabcdabcd", "abcd", 46, STATUS_SUCCESS},
+ { 2, "abcdabcdabcdabcdabcdabcd", "abcd", 0, STATUS_NOT_FOUND},
+ { 3, "abcdabcdabcdabcdabcdabcd", "abcd", 0, STATUS_NOT_FOUND},
+};
+#define NB_FIND_CH_IN_USTR (sizeof(find_ch_in_ustr)/sizeof(*find_ch_in_ustr))
+
+
+static void test_RtlFindCharInUnicodeString(void)
+{
+ WCHAR main_str_buf[257];
+ WCHAR search_chars_buf[257];
+ UNICODE_STRING main_str;
+ UNICODE_STRING search_chars;
+ USHORT pos;
+ NTSTATUS result;
+ size_t idx;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_FIND_CH_IN_USTR; test_num++) {
+ if (find_ch_in_ustr[test_num].main_str != NULL) {
+ main_str.Length = strlen(find_ch_in_ustr[test_num].main_str) * sizeof(WCHAR);
+ main_str.MaximumLength = main_str.Length + sizeof(WCHAR);
+ for (idx = 0; idx < main_str.Length / sizeof(WCHAR); idx++) {
+ main_str_buf[idx] = find_ch_in_ustr[test_num].main_str[idx];
+ }
+ main_str.Buffer = main_str_buf;
+ } else {
+ main_str.Length = 0;
+ main_str.MaximumLength = 0;
+ main_str.Buffer = NULL;
+ }
+ if (find_ch_in_ustr[test_num].search_chars != NULL) {
+ search_chars.Length = strlen(find_ch_in_ustr[test_num].search_chars) * sizeof(WCHAR);
+ search_chars.MaximumLength = search_chars.Length + sizeof(WCHAR);
+ for (idx = 0; idx < search_chars.Length / sizeof(WCHAR); idx++) {
+ search_chars_buf[idx] = find_ch_in_ustr[test_num].search_chars[idx];
+ }
+ search_chars.Buffer = search_chars_buf;
+ } else {
+ search_chars.Length = 0;
+ search_chars.MaximumLength = 0;
+ search_chars.Buffer = NULL;
+ }
+ pos = 12345;
+ result = pRtlFindCharInUnicodeString(find_ch_in_ustr[test_num].flags, &main_str, &search_chars, &pos);
+ ok(result == find_ch_in_ustr[test_num].result,
+ "(test %d): RtlFindCharInUnicodeString(%d, %s, %s, [out]) has result %lx, expected %lx\n",
+ test_num, find_ch_in_ustr[test_num].flags,
+ find_ch_in_ustr[test_num].main_str, find_ch_in_ustr[test_num].search_chars,
+ result, find_ch_in_ustr[test_num].result);
+ ok(pos == find_ch_in_ustr[test_num].pos,
+ "(test %d): RtlFindCharInUnicodeString(%d, %s, %s, [out]) assigns %d to pos, expected %d\n",
+ test_num, find_ch_in_ustr[test_num].flags,
+ find_ch_in_ustr[test_num].main_str, find_ch_in_ustr[test_num].search_chars,
+ pos, find_ch_in_ustr[test_num].pos);
+ }
+}
+
+
+typedef struct {
+ int base;
+ const char *str;
+ int value;
+ NTSTATUS result;
+} str2int_t;
+
+static const str2int_t str2int[] = {
+ { 0, "1011101100", 1011101100, STATUS_SUCCESS},
+ { 0, "1234567", 1234567, STATUS_SUCCESS},
+ { 0, "-214", -214, STATUS_SUCCESS},
+ { 0, "+214", 214, STATUS_SUCCESS}, /* The + sign is allowed also */
+ { 0, "--214", 0, STATUS_SUCCESS}, /* Do not accept more than one sign */
+ { 0, "-+214", 0, STATUS_SUCCESS},
+ { 0, "++214", 0, STATUS_SUCCESS},
+ { 0, "+-214", 0, STATUS_SUCCESS},
+ { 0, "\001\002\003\00411", 11, STATUS_SUCCESS}, /* whitespace char 1 to 4 */
+ { 0, "\005\006\007\01012", 12, STATUS_SUCCESS}, /* whitespace char 5 to 8 */
+ { 0, "\011\012\013\01413", 13, STATUS_SUCCESS}, /* whitespace char 9 to 12 */
+ { 0, "\015\016\017\02014", 14, STATUS_SUCCESS}, /* whitespace char 13 to 16 */
+ { 0, "\021\022\023\02415", 15, STATUS_SUCCESS}, /* whitespace char 17 to 20 */
+ { 0, "\025\026\027\03016", 16, STATUS_SUCCESS}, /* whitespace char 21 to 24 */
+ { 0, "\031\032\033\03417", 17, STATUS_SUCCESS}, /* whitespace char 25 to 28 */
+ { 0, "\035\036\037\04018", 18, STATUS_SUCCESS}, /* whitespace char 29 to 32 */
+ { 0, " \n \r \t214", 214, STATUS_SUCCESS},
+ { 0, " \n \r \t+214", 214, STATUS_SUCCESS}, /* Signs can be used after whitespace */
+ { 0, " \n \r \t-214", -214, STATUS_SUCCESS},
+ { 0, "+214 0", 214, STATUS_SUCCESS}, /* Space terminates the number */
+ { 0, " 214.01", 214, STATUS_SUCCESS}, /* Decimal point not accepted */
+ { 0, " 214,01", 214, STATUS_SUCCESS}, /* Decimal comma not accepted */
+ { 0, "f81", 0, STATUS_SUCCESS},
+ { 0, "0x12345", 0x12345, STATUS_SUCCESS}, /* Hex */
+ { 0, "00x12345", 0, STATUS_SUCCESS},
+ { 0, "0xx12345", 0, STATUS_SUCCESS},
+ { 0, "1x34", 1, STATUS_SUCCESS},
+ { 0, "-9999999999", -1410065407, STATUS_SUCCESS}, /* Big negative integer */
+ { 0, "-2147483649", 2147483647, STATUS_SUCCESS}, /* Too small to fit in 32 Bits */
+ { 0, "-2147483648", 0x80000000L, STATUS_SUCCESS}, /* Smallest negative integer */
+ { 0, "-2147483647", -2147483647, STATUS_SUCCESS},
+ { 0, "-1", -1, STATUS_SUCCESS},
+ { 0, "0", 0, STATUS_SUCCESS},
+ { 0, "1", 1, STATUS_SUCCESS},
+ { 0, "2147483646", 2147483646, STATUS_SUCCESS},
+ { 0, "2147483647", 2147483647, STATUS_SUCCESS}, /* Largest signed positive integer */
+ { 0, "2147483648", 0x80000000L, STATUS_SUCCESS}, /* Positive int equal to smallest negative int */
+ { 0, "2147483649", -2147483647, STATUS_SUCCESS},
+ { 0, "4294967294", -2, STATUS_SUCCESS},
+ { 0, "4294967295", -1, STATUS_SUCCESS}, /* Largest unsigned integer */
+ { 0, "4294967296", 0, STATUS_SUCCESS}, /* Too big to fit in 32 Bits */
+ { 0, "9999999999", 1410065407, STATUS_SUCCESS}, /* Big positive integer */
+ { 0, "056789", 56789, STATUS_SUCCESS}, /* Leading zero and still decimal */
+ { 0, "b1011101100", 0, STATUS_SUCCESS}, /* Binary (b-notation) */
+ { 0, "-b1011101100", 0, STATUS_SUCCESS}, /* Negative Binary (b-notation) */
+ { 0, "b10123456789", 0, STATUS_SUCCESS}, /* Binary with nonbinary digits (2-9) */
+ { 0, "0b1011101100", 748, STATUS_SUCCESS}, /* Binary (0b-notation) */
+ { 0, "-0b1011101100", -748, STATUS_SUCCESS}, /* Negative binary (0b-notation) */
+ { 0, "0b10123456789", 5, STATUS_SUCCESS}, /* Binary with nonbinary digits (2-9) */
+ { 0, "-0b10123456789", -5, STATUS_SUCCESS}, /* Negative binary with nonbinary digits (2-9) */
+ { 0, "0b1", 1, STATUS_SUCCESS}, /* one digit binary */
+ { 0, "0b2", 0, STATUS_SUCCESS}, /* empty binary */
+ { 0, "0b", 0, STATUS_SUCCESS}, /* empty binary */
+ { 0, "o1234567", 0, STATUS_SUCCESS}, /* Octal (o-notation) */
+ { 0, "-o1234567", 0, STATUS_SUCCESS}, /* Negative Octal (o-notation) */
+ { 0, "o56789", 0, STATUS_SUCCESS}, /* Octal with nonoctal digits (8 and 9) */
+ { 0, "0o1234567", 01234567, STATUS_SUCCESS}, /* Octal (0o-notation) */
+ { 0, "-0o1234567", -01234567, STATUS_SUCCESS}, /* Negative octal (0o-notation) */
+ { 0, "0o56789", 0567, STATUS_SUCCESS}, /* Octal with nonoctal digits (8 and 9) */
+ { 0, "-0o56789", -0567, STATUS_SUCCESS}, /* Negative octal with nonoctal digits (8 and 9) */
+ { 0, "0o7", 7, STATUS_SUCCESS}, /* one digit octal */
+ { 0, "0o8", 0, STATUS_SUCCESS}, /* empty octal */
+ { 0, "0o", 0, STATUS_SUCCESS}, /* empty octal */
+ { 0, "0d1011101100", 0, STATUS_SUCCESS}, /* explizit decimal with 0d */
+ { 0, "x89abcdef", 0, STATUS_SUCCESS}, /* Hex with lower case digits a-f (x-notation) */
+ { 0, "xFEDCBA00", 0, STATUS_SUCCESS}, /* Hex with upper case digits A-F (x-notation) */
+ { 0, "-xFEDCBA00", 0, STATUS_SUCCESS}, /* Negative Hexadecimal (x-notation) */
+ { 0, "0x89abcdef", 0x89abcdef, STATUS_SUCCESS}, /* Hex with lower case digits a-f (0x-notation) */
+ { 0, "0xFEDCBA00", 0xFEDCBA00, STATUS_SUCCESS}, /* Hex with upper case digits A-F (0x-notation) */
+ { 0, "-0xFEDCBA00", 19088896, STATUS_SUCCESS}, /* Negative Hexadecimal (0x-notation) */
+ { 0, "0xabcdefgh", 0xabcdef, STATUS_SUCCESS}, /* Hex with illegal lower case digits (g-z) */
+ { 0, "0xABCDEFGH", 0xABCDEF, STATUS_SUCCESS}, /* Hex with illegal upper case digits (G-Z) */
+ { 0, "0xF", 0xf, STATUS_SUCCESS}, /* one digit hexadecimal */
+ { 0, "0xG", 0, STATUS_SUCCESS}, /* empty hexadecimal */
+ { 0, "0x", 0, STATUS_SUCCESS}, /* empty hexadecimal */
+ { 0, "", 0, STATUS_SUCCESS}, /* empty string */
+ { 2, "1011101100", 748, STATUS_SUCCESS},
+ { 2, "-1011101100", -748, STATUS_SUCCESS},
+ { 2, "2", 0, STATUS_SUCCESS},
+ { 2, "0b1011101100", 0, STATUS_SUCCESS},
+ { 2, "0o1011101100", 0, STATUS_SUCCESS},
+ { 2, "0d1011101100", 0, STATUS_SUCCESS},
+ { 2, "0x1011101100", 0, STATUS_SUCCESS},
+ { 2, "", 0, STATUS_SUCCESS}, /* empty string */
+ { 8, "1011101100", 136610368, STATUS_SUCCESS},
+ { 8, "-1011101100", -136610368, STATUS_SUCCESS},
+ { 8, "8", 0, STATUS_SUCCESS},
+ { 8, "0b1011101100", 0, STATUS_SUCCESS},
+ { 8, "0o1011101100", 0, STATUS_SUCCESS},
+ { 8, "0d1011101100", 0, STATUS_SUCCESS},
+ { 8, "0x1011101100", 0, STATUS_SUCCESS},
+ { 8, "", 0, STATUS_SUCCESS}, /* empty string */
+ {10, "1011101100", 1011101100, STATUS_SUCCESS},
+ {10, "-1011101100", -1011101100, STATUS_SUCCESS},
+ {10, "0b1011101100", 0, STATUS_SUCCESS},
+ {10, "0o1011101100", 0, STATUS_SUCCESS},
+ {10, "0d1011101100", 0, STATUS_SUCCESS},
+ {10, "0x1011101100", 0, STATUS_SUCCESS},
+ {10, "o12345", 0, STATUS_SUCCESS}, /* Octal although base is 10 */
+ {10, "", 0, STATUS_SUCCESS}, /* empty string */
+ {16, "1011101100", 286265600, STATUS_SUCCESS},
+ {16, "-1011101100", -286265600, STATUS_SUCCESS},
+ {16, "G", 0, STATUS_SUCCESS},
+ {16, "g", 0, STATUS_SUCCESS},
+ {16, "0b1011101100", 286265600, STATUS_SUCCESS},
+ {16, "0o1011101100", 0, STATUS_SUCCESS},
+ {16, "0d1011101100", 286265600, STATUS_SUCCESS},
+ {16, "0x1011101100", 0, STATUS_SUCCESS},
+ {16, "", 0, STATUS_SUCCESS}, /* empty string */
+ {20, "0", 0xdeadbeef, STATUS_INVALID_PARAMETER}, /* illegal base */
+ {-8, "0", 0xdeadbeef, STATUS_INVALID_PARAMETER}, /* Negative base */
+/* { 0, NULL, 0, STATUS_SUCCESS}, */ /* NULL as string */
+};
+#define NB_STR2INT (sizeof(str2int)/sizeof(*str2int))
+
+
+static void test_RtlUnicodeStringToInteger(void)
+{
+ size_t test_num;
+ int value;
+ NTSTATUS result;
+ WCHAR *wstr;
+ UNICODE_STRING uni;
+
+ for (test_num = 0; test_num < NB_STR2INT; test_num++) {
+ wstr = AtoW(str2int[test_num].str);
+ value = 0xdeadbeef;
+ pRtlInitUnicodeString(&uni, wstr);
+ result = pRtlUnicodeStringToInteger(&uni, str2int[test_num].base, &value);
+ ok(result == str2int[test_num].result,
+ "(test %d): RtlUnicodeStringToInteger(\"%s\", %d, [out]) has result %lx, expected: %lx\n",
+ test_num, str2int[test_num].str, str2int[test_num].base, result, str2int[test_num].result);
+ ok(value == str2int[test_num].value,
+ "(test %d): RtlUnicodeStringToInteger(\"%s\", %d, [out]) assigns value %d, expected: %d\n",
+ test_num, str2int[test_num].str, str2int[test_num].base, value, str2int[test_num].value);
+ free(wstr);
+ }
+
+ wstr = AtoW(str2int[1].str);
+ pRtlInitUnicodeString(&uni, wstr);
+ result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, NULL);
+ ok(result == STATUS_ACCESS_VIOLATION,
+ "call failed: RtlUnicodeStringToInteger(\"%s\", %d, NULL) has result %lx\n",
+ str2int[1].str, str2int[1].base, result);
+ result = pRtlUnicodeStringToInteger(&uni, 20, NULL);
+ ok(result == STATUS_INVALID_PARAMETER,
+ "call failed: RtlUnicodeStringToInteger(\"%s\", 20, NULL) has result %lx\n",
+ str2int[1].str, result);
+
+ uni.Length = 10; /* Make Length shorter (5 WCHARS instead of 7) */
+ result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, &value);
+ ok(result == STATUS_SUCCESS,
+ "call failed: RtlUnicodeStringToInteger(\"12345\", %d, [out]) has result %lx\n",
+ str2int[1].base, result);
+ ok(value == 12345,
+ "didn't return expected value (test a): expected: %d, got: %d\n",
+ 12345, value);
+
+ uni.Length = 5; /* Use odd Length (2.5 WCHARS) */
+ result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, &value);
+ ok(result == STATUS_SUCCESS,
+ "call failed: RtlUnicodeStringToInteger(\"12\", %d, [out]) has result %lx\n",
+ str2int[1].base, result);
+ ok(value == 12,
+ "didn't return expected value (test b): expected: %d, got: %d\n",
+ 12, value);
+
+ uni.Length = 2;
+ result = pRtlUnicodeStringToInteger(&uni, str2int[1].base, &value);
+ ok(result == STATUS_SUCCESS,
+ "call failed: RtlUnicodeStringToInteger(\"1\", %d, [out]) has result %lx\n",
+ str2int[1].base, result);
+ ok(value == 1,
+ "didn't return expected value (test c): expected: %d, got: %d\n",
+ 1, value);
+ /* w2k: uni.Length = 0 returns value 11234567 instead of 0 */
+ free(wstr);
+}
+
+
+static void test_RtlCharToInteger(void)
+{
+ size_t test_num;
+ int value;
+ NTSTATUS result;
+
+ for (test_num = 0; test_num < NB_STR2INT; test_num++) {
+ /* w2k skips a leading '\0' and processes the string after */
+ if (str2int[test_num].str[0] != '\0') {
+ value = 0xdeadbeef;
+ result = pRtlCharToInteger(str2int[test_num].str, str2int[test_num].base, &value);
+ ok(result == str2int[test_num].result,
+ "(test %d): call failed: RtlCharToInteger(\"%s\", %d, [out]) has result %lx, expected: %lx\n",
+ test_num, str2int[test_num].str, str2int[test_num].base, result, str2int[test_num].result);
+ ok(value == str2int[test_num].value,
+ "(test %d): call failed: RtlCharToInteger(\"%s\", %d, [out]) assigns value %d, expected: %d\n",
+ test_num, str2int[test_num].str, str2int[test_num].base, value, str2int[test_num].value);
+ }
+ }
+
+ result = pRtlCharToInteger(str2int[1].str, str2int[1].base, NULL);
+ ok(result == STATUS_ACCESS_VIOLATION,
+ "call failed: RtlCharToInteger(\"%s\", %d, NULL) has result %lx\n",
+ str2int[1].str, str2int[1].base, result);
+
+ result = pRtlCharToInteger(str2int[1].str, 20, NULL);
+ ok(result == STATUS_INVALID_PARAMETER,
+ "call failed: RtlCharToInteger(\"%s\", 20, NULL) has result %lx\n",
+ str2int[1].str, result);
+}
+
+
+#define STRI_BUFFER_LENGTH 35
+
+typedef struct {
+ int base;
+ ULONG value;
+ USHORT Length;
+ USHORT MaximumLength;
+ const char *Buffer;
+ NTSTATUS result;
+} int2str_t;
+
+static const int2str_t int2str[] = {
+ {10, 123, 3, 11, "123\0-------------------------------", STATUS_SUCCESS},
+
+ { 0, 0x80000000U, 10, 11, "2147483648\0------------------------", STATUS_SUCCESS}, /* min signed int */
+ { 0, -2147483647, 10, 11, "2147483649\0------------------------", STATUS_SUCCESS},
+ { 0, -2, 10, 11, "4294967294\0------------------------", STATUS_SUCCESS},
+ { 0, -1, 10, 11, "4294967295\0------------------------", STATUS_SUCCESS},
+ { 0, 0, 1, 11, "0\0---------------------------------", STATUS_SUCCESS},
+ { 0, 1, 1, 11, "1\0---------------------------------", STATUS_SUCCESS},
+ { 0, 12, 2, 11, "12\0--------------------------------", STATUS_SUCCESS},
+ { 0, 123, 3, 11, "123\0-------------------------------", STATUS_SUCCESS},
+ { 0, 1234, 4, 11, "1234\0------------------------------", STATUS_SUCCESS},
+ { 0, 12345, 5, 11, "12345\0-----------------------------", STATUS_SUCCESS},
+ { 0, 123456, 6, 11, "123456\0----------------------------", STATUS_SUCCESS},
+ { 0, 1234567, 7, 11, "1234567\0---------------------------", STATUS_SUCCESS},
+ { 0, 12345678, 8, 11, "12345678\0--------------------------", STATUS_SUCCESS},
+ { 0, 123456789, 9, 11, "123456789\0-------------------------", STATUS_SUCCESS},
+ { 0, 2147483646, 10, 11, "2147483646\0------------------------", STATUS_SUCCESS},
+ { 0, 2147483647, 10, 11, "2147483647\0------------------------", STATUS_SUCCESS}, /* max signed int */
+ { 0, 2147483648U, 10, 11, "2147483648\0------------------------", STATUS_SUCCESS}, /* uint = -max int */
+ { 0, 2147483649U, 10, 11, "2147483649\0------------------------", STATUS_SUCCESS},
+ { 0, 4294967294U, 10, 11, "4294967294\0------------------------", STATUS_SUCCESS},
+ { 0, 4294967295U, 10, 11, "4294967295\0------------------------", STATUS_SUCCESS}, /* max unsigned int */
+
+ { 2, 0x80000000U, 32, 33, "10000000000000000000000000000000\0--", STATUS_SUCCESS}, /* min signed int */
+ { 2, -2147483647, 32, 33, "10000000000000000000000000000001\0--", STATUS_SUCCESS},
+ { 2, -2, 32, 33, "11111111111111111111111111111110\0--", STATUS_SUCCESS},
+ { 2, -1, 32, 33, "11111111111111111111111111111111\0--", STATUS_SUCCESS},
+ { 2, 0, 1, 33, "0\0---------------------------------", STATUS_SUCCESS},
+ { 2, 1, 1, 33, "1\0---------------------------------", STATUS_SUCCESS},
+ { 2, 10, 4, 33, "1010\0------------------------------", STATUS_SUCCESS},
+ { 2, 100, 7, 33, "1100100\0---------------------------", STATUS_SUCCESS},
+ { 2, 1000, 10, 33, "1111101000\0------------------------", STATUS_SUCCESS},
+ { 2, 10000, 14, 33, "10011100010000\0--------------------", STATUS_SUCCESS},
+ { 2, 32767, 15, 33, "111111111111111\0-------------------", STATUS_SUCCESS},
+ { 2, 32768, 16, 33, "1000000000000000\0------------------", STATUS_SUCCESS},
+ { 2, 65535, 16, 33, "1111111111111111\0------------------", STATUS_SUCCESS},
+ { 2, 65536, 17, 33, "10000000000000000\0-----------------", STATUS_SUCCESS},
+ { 2, 100000, 17, 33, "11000011010100000\0-----------------", STATUS_SUCCESS},
+ { 2, 1000000, 20, 33, "11110100001001000000\0--------------", STATUS_SUCCESS},
+ { 2, 10000000, 24, 33, "100110001001011010000000\0----------", STATUS_SUCCESS},
+ { 2, 100000000, 27, 33, "101111101011110000100000000\0-------", STATUS_SUCCESS},
+ { 2, 1000000000, 30, 33, "111011100110101100101000000000\0----", STATUS_SUCCESS},
+ { 2, 1073741823, 30, 33, "111111111111111111111111111111\0----", STATUS_SUCCESS},
+ { 2, 2147483646, 31, 33, "1111111111111111111111111111110\0---", STATUS_SUCCESS},
+ { 2, 2147483647, 31, 33, "1111111111111111111111111111111\0---", STATUS_SUCCESS}, /* max signed int */
+ { 2, 2147483648U, 32, 33, "10000000000000000000000000000000\0--", STATUS_SUCCESS}, /* uint = -max int */
+ { 2, 2147483649U, 32, 33, "10000000000000000000000000000001\0--", STATUS_SUCCESS},
+ { 2, 4294967294U, 32, 33, "11111111111111111111111111111110\0--", STATUS_SUCCESS},
+ { 2, 4294967295U, 32, 33, "11111111111111111111111111111111\0--", STATUS_SUCCESS}, /* max unsigned int */
+
+ { 8, 0x80000000U, 11, 12, "20000000000\0-----------------------", STATUS_SUCCESS}, /* min signed int */
+ { 8, -2147483647, 11, 12, "20000000001\0-----------------------", STATUS_SUCCESS},
+ { 8, -2, 11, 12, "37777777776\0-----------------------", STATUS_SUCCESS},
+ { 8, -1, 11, 12, "37777777777\0-----------------------", STATUS_SUCCESS},
+ { 8, 0, 1, 12, "0\0---------------------------------", STATUS_SUCCESS},
+ { 8, 1, 1, 12, "1\0---------------------------------", STATUS_SUCCESS},
+ { 8, 2147483646, 11, 12, "17777777776\0-----------------------", STATUS_SUCCESS},
+ { 8, 2147483647, 11, 12, "17777777777\0-----------------------", STATUS_SUCCESS}, /* max signed int */
+ { 8, 2147483648U, 11, 12, "20000000000\0-----------------------", STATUS_SUCCESS}, /* uint = -max int */
+ { 8, 2147483649U, 11, 12, "20000000001\0-----------------------", STATUS_SUCCESS},
+ { 8, 4294967294U, 11, 12, "37777777776\0-----------------------", STATUS_SUCCESS},
+ { 8, 4294967295U, 11, 12, "37777777777\0-----------------------", STATUS_SUCCESS}, /* max unsigned int */
+
+ {10, 0x80000000U, 10, 11, "2147483648\0------------------------", STATUS_SUCCESS}, /* min signed int */
+ {10, -2147483647, 10, 11, "2147483649\0------------------------", STATUS_SUCCESS},
+ {10, -2, 10, 11, "4294967294\0------------------------", STATUS_SUCCESS},
+ {10, -1, 10, 11, "4294967295\0------------------------", STATUS_SUCCESS},
+ {10, 0, 1, 11, "0\0---------------------------------", STATUS_SUCCESS},
+ {10, 1, 1, 11, "1\0---------------------------------", STATUS_SUCCESS},
+ {10, 2147483646, 10, 11, "2147483646\0------------------------", STATUS_SUCCESS},
+ {10, 2147483647, 10, 11, "2147483647\0------------------------", STATUS_SUCCESS}, /* max signed int */
+ {10, 2147483648U, 10, 11, "2147483648\0------------------------", STATUS_SUCCESS}, /* uint = -max int */
+ {10, 2147483649U, 10, 11, "2147483649\0------------------------", STATUS_SUCCESS},
+ {10, 4294967294U, 10, 11, "4294967294\0------------------------", STATUS_SUCCESS},
+ {10, 4294967295U, 10, 11, "4294967295\0------------------------", STATUS_SUCCESS}, /* max unsigned int */
+
+ {16, 0x80000000U, 8, 9, "80000000\0--------------------------", STATUS_SUCCESS}, /* min signed int */
+ {16, -2147483647, 8, 9, "80000001\0--------------------------", STATUS_SUCCESS},
+ {16, -2, 8, 9, "FFFFFFFE\0--------------------------", STATUS_SUCCESS},
+ {16, -1, 8, 9, "FFFFFFFF\0--------------------------", STATUS_SUCCESS},
+ {16, 0, 1, 9, "0\0---------------------------------", STATUS_SUCCESS},
+ {16, 1, 1, 9, "1\0---------------------------------", STATUS_SUCCESS},
+ {16, 2147483646, 8, 9, "7FFFFFFE\0--------------------------", STATUS_SUCCESS},
+ {16, 2147483647, 8, 9, "7FFFFFFF\0--------------------------", STATUS_SUCCESS}, /* max signed int */
+ {16, 2147483648U, 8, 9, "80000000\0--------------------------", STATUS_SUCCESS}, /* uint = -max int */
+ {16, 2147483649U, 8, 9, "80000001\0--------------------------", STATUS_SUCCESS},
+ {16, 4294967294U, 8, 9, "FFFFFFFE\0--------------------------", STATUS_SUCCESS},
+ {16, 4294967295U, 8, 9, "FFFFFFFF\0--------------------------", STATUS_SUCCESS}, /* max unsigned int */
+
+ { 2, 32768, 16, 17, "1000000000000000\0------------------", STATUS_SUCCESS},
+ { 2, 32768, 16, 16, "1000000000000000-------------------", STATUS_SUCCESS},
+ { 2, 65536, 17, 18, "10000000000000000\0-----------------", STATUS_SUCCESS},
+ { 2, 65536, 17, 17, "10000000000000000------------------", STATUS_SUCCESS},
+ { 2, 131072, 18, 19, "100000000000000000\0----------------", STATUS_SUCCESS},
+ { 2, 131072, 18, 18, "100000000000000000-----------------", STATUS_SUCCESS},
+ {16, 0xffffffff, 8, 9, "FFFFFFFF\0--------------------------", STATUS_SUCCESS},
+ {16, 0xffffffff, 8, 8, "FFFFFFFF---------------------------", STATUS_SUCCESS}, /* No \0 term */
+ {16, 0xffffffff, 8, 7, "-----------------------------------", STATUS_BUFFER_OVERFLOW}, /* Too short */
+ {16, 0xa, 1, 2, "A\0---------------------------------", STATUS_SUCCESS},
+ {16, 0xa, 1, 1, "A----------------------------------", STATUS_SUCCESS}, /* No \0 term */
+ {16, 0, 1, 0, "-----------------------------------", STATUS_BUFFER_OVERFLOW},
+ {20, 0xdeadbeef, 0, 9, "-----------------------------------", STATUS_INVALID_PARAMETER}, /* ill. base */
+ {-8, 07654321, 0, 12, "-----------------------------------", STATUS_INVALID_PARAMETER}, /* neg. base */
+};
+#define NB_INT2STR (sizeof(int2str)/sizeof(*int2str))
+
+
+static void one_RtlIntegerToUnicodeString_test(int test_num, const int2str_t *int2str)
+{
+ int pos;
+ WCHAR expected_str_Buffer[STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING expected_unicode_string;
+ STRING expected_ansi_str;
+ WCHAR str_Buffer[STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING unicode_string;
+ STRING ansi_str;
+ NTSTATUS result;
+
+ for (pos = 0; pos < STRI_BUFFER_LENGTH; pos++) {
+ expected_str_Buffer[pos] = int2str->Buffer[pos];
+ }
+ expected_unicode_string.Length = int2str->Length * sizeof(WCHAR);
+ expected_unicode_string.MaximumLength = int2str->MaximumLength * sizeof(WCHAR);
+ expected_unicode_string.Buffer = expected_str_Buffer;
+ pRtlUnicodeStringToAnsiString(&expected_ansi_str, &expected_unicode_string, 1);
+
+ for (pos = 0; pos < STRI_BUFFER_LENGTH; pos++) {
+ str_Buffer[pos] = '-';
+ }
+ unicode_string.Length = 0;
+ unicode_string.MaximumLength = int2str->MaximumLength * sizeof(WCHAR);
+ unicode_string.Buffer = str_Buffer;
+
+ result = pRtlIntegerToUnicodeString(int2str->value, int2str->base, &unicode_string);
+ pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
+ if (result == STATUS_BUFFER_OVERFLOW) {
+ /* On BUFFER_OVERFLOW the string Buffer should be unchanged */
+ for (pos = 0; pos < STRI_BUFFER_LENGTH; pos++) {
+ expected_str_Buffer[pos] = '-';
+ }
+ /* w2k: The native function has two reasons for BUFFER_OVERFLOW: */
+ /* If the value is too large to convert: The Length is unchanged */
+ /* If str is too small to hold the string: Set str->Length to the length */
+ /* the string would have (which can be larger than the MaximumLength). */
+ /* To allow all this in the tests we do the following: */
+ if (expected_unicode_string.Length > 32 && unicode_string.Length == 0) {
+ /* The value is too large to convert only triggerd when testing native */
+ expected_unicode_string.Length = 0;
+ }
+ } else {
+ ok(result == int2str->result,
+ "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) has result %lx, expected: %lx\n",
+ test_num, int2str->value, int2str->base, result, int2str->result);
+ if (result == STATUS_SUCCESS) {
+ ok(unicode_string.Buffer[unicode_string.Length/sizeof(WCHAR)] == '\0',
+ "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) string \"%s\" is not NULL terminated\n",
+ test_num, int2str->value, int2str->base, ansi_str.Buffer);
+ }
+ }
+ ok(memcmp(unicode_string.Buffer, expected_unicode_string.Buffer, STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, int2str->value, int2str->base, ansi_str.Buffer, expected_ansi_str.Buffer);
+ ok(unicode_string.Length == expected_unicode_string.Length,
+ "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) string has Length %d, expected: %d\n",
+ test_num, int2str->value, int2str->base, unicode_string.Length, expected_unicode_string.Length);
+ ok(unicode_string.MaximumLength == expected_unicode_string.MaximumLength,
+ "(test %d): RtlIntegerToUnicodeString(%lu, %d, [out]) string has MaximumLength %d, expected: %d\n",
+ test_num, int2str->value, int2str->base, unicode_string.MaximumLength, expected_unicode_string.MaximumLength);
+ pRtlFreeAnsiString(&expected_ansi_str);
+ pRtlFreeAnsiString(&ansi_str);
+}
+
+
+static void test_RtlIntegerToUnicodeString(void)
+{
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_INT2STR; test_num++)
+ one_RtlIntegerToUnicodeString_test(test_num, &int2str[test_num]);
+}
+
+
+static void one_RtlIntegerToChar_test(int test_num, const int2str_t *int2str)
+{
+ NTSTATUS result;
+ char dest_str[STRI_BUFFER_LENGTH + 1];
+
+ memset(dest_str, '-', STRI_BUFFER_LENGTH);
+ dest_str[STRI_BUFFER_LENGTH] = '\0';
+ result = pRtlIntegerToChar(int2str->value, int2str->base, int2str->MaximumLength, dest_str);
+ ok(result == int2str->result,
+ "(test %d): RtlIntegerToChar(%lu, %d, %d, [out]) has result %lx, expected: %lx\n",
+ test_num, int2str->value, int2str->base, int2str->MaximumLength, result, int2str->result);
+ ok(memcmp(dest_str, int2str->Buffer, STRI_BUFFER_LENGTH) == 0,
+ "(test %d): RtlIntegerToChar(%lu, %d, %d, [out]) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, int2str->value, int2str->base, int2str->MaximumLength, dest_str, int2str->Buffer);
+}
+
+
+static void test_RtlIntegerToChar(void)
+{
+ NTSTATUS result;
+ size_t test_num;
+
+ for (test_num = 0; test_num < NB_INT2STR; test_num++)
+ one_RtlIntegerToChar_test(test_num, &int2str[test_num]);
+
+ result = pRtlIntegerToChar(int2str[0].value, 20, int2str[0].MaximumLength, NULL);
+ ok(result == STATUS_INVALID_PARAMETER,
+ "(test a): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ int2str[0].value, 20, int2str[0].MaximumLength, result, STATUS_INVALID_PARAMETER);
+
+ result = pRtlIntegerToChar(int2str[0].value, 20, 0, NULL);
+ ok(result == STATUS_INVALID_PARAMETER,
+ "(test b): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ int2str[0].value, 20, 0, result, STATUS_INVALID_PARAMETER);
+
+ result = pRtlIntegerToChar(int2str[0].value, int2str[0].base, 0, NULL);
+ ok(result == STATUS_BUFFER_OVERFLOW,
+ "(test c): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ int2str[0].value, int2str[0].base, 0, result, STATUS_BUFFER_OVERFLOW);
+
+ result = pRtlIntegerToChar(int2str[0].value, int2str[0].base, int2str[0].MaximumLength, NULL);
+ ok(result == STATUS_ACCESS_VIOLATION,
+ "(test d): RtlIntegerToChar(%lu, %d, %d, NULL) has result %lx, expected: %lx\n",
+ int2str[0].value, int2str[0].base, int2str[0].MaximumLength, result, STATUS_ACCESS_VIOLATION);
+}
+
+static const WCHAR szGuid[] = { '{','0','1','0','2','0','3','0','4','-',
+ '0','5','0','6','-' ,'0','7','0','8','-','0','9','0','A','-',
+ '0','B','0','C','0','D','0','E','0','F','0','A','}','\0' };
+static const WCHAR szGuid2[] = { '{','0','1','0','2','0','3','0','4','-',
+ '0','5','0','6','-' ,'0','7','0','8','-','0','9','0','A','-',
+ '0','B','0','C','0','D','0','E','0','F','0','A',']','\0' };
+DEFINE_GUID(IID_Endianess, 0x01020304, 0x0506, 0x0708, 0x09, 0x0A, 0x0B,
+ 0x0C, 0x0D, 0x0E, 0x0F, 0x0A);
+
+static void test_RtlGUIDFromString(void)
+{
+ GUID guid;
+ UNICODE_STRING str;
+ NTSTATUS ret;
+
+ str.Length = str.MaximumLength = (sizeof(szGuid) - 1) / sizeof(WCHAR);
+ str.Buffer = (LPWSTR)szGuid;
+
+ ret = pRtlGUIDFromString(&str, &guid);
+ ok(ret == 0, "expected ret=0, got 0x%0lx\n", ret);
+ ok(memcmp(&guid, &IID_Endianess, sizeof(guid)) == 0, "Endianess broken\n");
+
+ str.Length = str.MaximumLength = (sizeof(szGuid2) - 1) / sizeof(WCHAR);
+ str.Buffer = (LPWSTR)szGuid2;
+
+ ret = pRtlGUIDFromString(&str, &guid);
+ ok(ret, "expected ret!=0\n");
+}
+
+static void test_RtlStringFromGUID(void)
+{
+ UNICODE_STRING str;
+ NTSTATUS ret;
+
+ str.Length = str.MaximumLength = 0;
+ str.Buffer = NULL;
+
+ ret = pRtlStringFromGUID(&IID_Endianess, &str);
+ ok(ret == 0, "expected ret=0, got 0x%0lx\n", ret);
+ ok(str.Buffer && !lstrcmpW(str.Buffer, szGuid), "Endianess broken\n");
+}
+
+START_TEST(rtlstr)
+{
+ InitFunctionPtrs();
+ if (pRtlInitAnsiString) {
+ test_RtlInitString();
+ test_RtlInitUnicodeString();
+ test_RtlCopyString();
+ test_RtlUnicodeStringToInteger();
+ test_RtlCharToInteger();
+ test_RtlIntegerToUnicodeString();
+ test_RtlIntegerToChar();
+ test_RtlUpperChar();
+ test_RtlUpperString();
+ test_RtlUnicodeStringToAnsiString();
+ test_RtlAppendAsciizToString();
+ test_RtlAppendStringToString();
+ test_RtlAppendUnicodeToString();
+ test_RtlAppendUnicodeStringToString();
+ }
+
+ if (pRtlInitUnicodeStringEx)
+ test_RtlInitUnicodeStringEx();
+ if (pRtlDuplicateUnicodeString)
+ test_RtlDuplicateUnicodeString();
+ if (pRtlFindCharInUnicodeString)
+ test_RtlFindCharInUnicodeString();
+ if (pRtlGUIDFromString)
+ test_RtlGUIDFromString();
+ if (pRtlStringFromGUID)
+ test_RtlStringFromGUID();
+ if(0)
+ {
+ test_RtlUpcaseUnicodeChar();
+ test_RtlUpcaseUnicodeString();
+ test_RtlDowncaseUnicodeString();
+ }
+}
--- /dev/null
+/* Unit test suite for string functions and some wcstring functions
+ *
+ * Copyright 2003 Thomas Mertes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * NOTES
+ * We use function pointers here as there is no import library for NTDLL on
+ * windows.
+ */
+
+#include <stdlib.h>
+
+#include "ntdll_test.h"
+
+
+/* Function ptrs for ntdll calls */
+static HMODULE hntdll = 0;
+static NTSTATUS (WINAPI *pRtlUnicodeStringToAnsiString)(STRING *, const UNICODE_STRING *, BOOLEAN);
+static VOID (WINAPI *pRtlFreeAnsiString)(PSTRING);
+static BOOLEAN (WINAPI *pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING,LPCSTR);
+static VOID (WINAPI *pRtlFreeUnicodeString)(PUNICODE_STRING);
+
+static int (WINAPIV *patoi)(const char *);
+static long (WINAPIV *patol)(const char *);
+static LONGLONG (WINAPIV *p_atoi64)(const char *);
+static LPSTR (WINAPIV *p_itoa)(int, LPSTR, INT);
+static LPSTR (WINAPIV *p_ltoa)(long, LPSTR, INT);
+static LPSTR (WINAPIV *p_ultoa)(unsigned long, LPSTR, INT);
+static LPSTR (WINAPIV *p_i64toa)(LONGLONG, LPSTR, INT);
+static LPSTR (WINAPIV *p_ui64toa)(ULONGLONG, LPSTR, INT);
+
+static int (WINAPIV *p_wtoi)(LPWSTR);
+static long (WINAPIV *p_wtol)(LPWSTR);
+static LONGLONG (WINAPIV *p_wtoi64)(LPWSTR);
+static LPWSTR (WINAPIV *p_itow)(int, LPWSTR, int);
+static LPWSTR (WINAPIV *p_ltow)(long, LPWSTR, INT);
+static LPWSTR (WINAPIV *p_ultow)(unsigned long, LPWSTR, INT);
+static LPWSTR (WINAPIV *p_i64tow)(LONGLONG, LPWSTR, INT);
+static LPWSTR (WINAPIV *p_ui64tow)(ULONGLONG, LPWSTR, INT);
+
+static long (WINAPIV *pwcstol)(LPCWSTR, LPWSTR *, INT);
+static ULONG (WINAPIV *pwcstoul)(LPCWSTR, LPWSTR *, INT);
+
+static LPWSTR (WINAPIV *p_wcschr)(LPCWSTR, WCHAR);
+static LPWSTR (WINAPIV *p_wcsrchr)(LPCWSTR, WCHAR);
+
+static void InitFunctionPtrs(void)
+{
+ hntdll = LoadLibraryA("ntdll.dll");
+ ok(hntdll != 0, "LoadLibrary failed\n");
+ if (hntdll) {
+ pRtlUnicodeStringToAnsiString = (void *)GetProcAddress(hntdll, "RtlUnicodeStringToAnsiString");
+ pRtlFreeAnsiString = (void *)GetProcAddress(hntdll, "RtlFreeAnsiString");
+ pRtlCreateUnicodeStringFromAsciiz = (void *)GetProcAddress(hntdll, "RtlCreateUnicodeStringFromAsciiz");
+ pRtlFreeUnicodeString = (void *)GetProcAddress(hntdll, "RtlFreeUnicodeString");
+
+ patoi = (void *)GetProcAddress(hntdll, "atoi");
+ patol = (void *)GetProcAddress(hntdll, "atol");
+ p_atoi64 = (void *)GetProcAddress(hntdll, "_atoi64");
+ p_itoa = (void *)GetProcAddress(hntdll, "_itoa");
+ p_ltoa = (void *)GetProcAddress(hntdll, "_ltoa");
+ p_ultoa = (void *)GetProcAddress(hntdll, "_ultoa");
+ p_i64toa = (void *)GetProcAddress(hntdll, "_i64toa");
+ p_ui64toa = (void *)GetProcAddress(hntdll, "_ui64toa");
+
+ p_wtoi = (void *)GetProcAddress(hntdll, "_wtoi");
+ p_wtol = (void *)GetProcAddress(hntdll, "_wtol");
+ p_wtoi64 = (void *)GetProcAddress(hntdll, "_wtoi64");
+ p_itow = (void *)GetProcAddress(hntdll, "_itow");
+ p_ltow = (void *)GetProcAddress(hntdll, "_ltow");
+ p_ultow = (void *)GetProcAddress(hntdll, "_ultow");
+ p_i64tow = (void *)GetProcAddress(hntdll, "_i64tow");
+ p_ui64tow = (void *)GetProcAddress(hntdll, "_ui64tow");
+
+ pwcstol = (void *)GetProcAddress(hntdll, "wcstol");
+ pwcstoul = (void *)GetProcAddress(hntdll, "wcstoul");
+
+ p_wcschr= (void *)GetProcAddress(hntdll, "wcschr");
+ p_wcsrchr= (void *)GetProcAddress(hntdll, "wcsrchr");
+ } /* if */
+}
+
+
+#define LARGE_STRI_BUFFER_LENGTH 67
+
+typedef struct {
+ int base;
+ ULONG value;
+ const char *Buffer;
+ int mask; /* ntdll/msvcrt: 0x01=itoa, 0x02=ltoa, 0x04=ultoa */
+ /* 0x10=itow, 0x20=ltow, 0x40=ultow */
+} ulong2str_t;
+
+static const ulong2str_t ulong2str[] = {
+ {10, 123, "123\0---------------------------------------------------------------", 0x77},
+
+ { 2, 0x80000000U, "10000000000000000000000000000000\0----------------------------------", 0x67},
+ { 2, -2147483647, "10000000000000000000000000000001\0----------------------------------", 0x67},
+ { 2, -65537, "11111111111111101111111111111111\0----------------------------------", 0x67},
+ { 2, -65536, "11111111111111110000000000000000\0----------------------------------", 0x67},
+ { 2, -65535, "11111111111111110000000000000001\0----------------------------------", 0x67},
+ { 2, -32768, "11111111111111111000000000000000\0----------------------------------", 0x67},
+ { 2, -32767, "11111111111111111000000000000001\0----------------------------------", 0x67},
+ { 2, -2, "11111111111111111111111111111110\0----------------------------------", 0x67},
+ { 2, -1, "11111111111111111111111111111111\0----------------------------------", 0x67},
+ { 2, 0, "0\0-----------------------------------------------------------------", 0x77},
+ { 2, 1, "1\0-----------------------------------------------------------------", 0x77},
+ { 2, 10, "1010\0--------------------------------------------------------------", 0x77},
+ { 2, 100, "1100100\0-----------------------------------------------------------", 0x77},
+ { 2, 1000, "1111101000\0--------------------------------------------------------", 0x77},
+ { 2, 10000, "10011100010000\0----------------------------------------------------", 0x77},
+ { 2, 32767, "111111111111111\0---------------------------------------------------", 0x77},
+ { 2, 32768, "1000000000000000\0--------------------------------------------------", 0x77},
+ { 2, 65535, "1111111111111111\0--------------------------------------------------", 0x77},
+ { 2, 100000, "11000011010100000\0-------------------------------------------------", 0x77},
+ { 2, 234567, "111001010001000111\0------------------------------------------------", 0x77},
+ { 2, 300000, "1001001001111100000\0-----------------------------------------------", 0x77},
+ { 2, 524287, "1111111111111111111\0-----------------------------------------------", 0x77},
+ { 2, 524288, "10000000000000000000\0----------------------------------------------", 0x67},
+ { 2, 1000000, "11110100001001000000\0----------------------------------------------", 0x67},
+ { 2, 10000000, "100110001001011010000000\0------------------------------------------", 0x67},
+ { 2, 100000000, "101111101011110000100000000\0---------------------------------------", 0x67},
+ { 2, 1000000000, "111011100110101100101000000000\0------------------------------------", 0x67},
+ { 2, 1073741823, "111111111111111111111111111111\0------------------------------------", 0x67},
+ { 2, 2147483646, "1111111111111111111111111111110\0-----------------------------------", 0x67},
+ { 2, 2147483647, "1111111111111111111111111111111\0-----------------------------------", 0x67},
+ { 2, 2147483648U, "10000000000000000000000000000000\0----------------------------------", 0x67},
+ { 2, 2147483649U, "10000000000000000000000000000001\0----------------------------------", 0x67},
+ { 2, 4294967294U, "11111111111111111111111111111110\0----------------------------------", 0x67},
+ { 2, 0xFFFFFFFF, "11111111111111111111111111111111\0----------------------------------", 0x67},
+
+ { 8, 0x80000000U, "20000000000\0-------------------------------------------------------", 0x77},
+ { 8, -2147483647, "20000000001\0-------------------------------------------------------", 0x77},
+ { 8, -2, "37777777776\0-------------------------------------------------------", 0x77},
+ { 8, -1, "37777777777\0-------------------------------------------------------", 0x77},
+ { 8, 0, "0\0-----------------------------------------------------------------", 0x77},
+ { 8, 1, "1\0-----------------------------------------------------------------", 0x77},
+ { 8, 2147483646, "17777777776\0-------------------------------------------------------", 0x77},
+ { 8, 2147483647, "17777777777\0-------------------------------------------------------", 0x77},
+ { 8, 2147483648U, "20000000000\0-------------------------------------------------------", 0x77},
+ { 8, 2147483649U, "20000000001\0-------------------------------------------------------", 0x77},
+ { 8, 4294967294U, "37777777776\0-------------------------------------------------------", 0x77},
+ { 8, 4294967295U, "37777777777\0-------------------------------------------------------", 0x77},
+
+ {10, 0x80000000U, "-2147483648\0-------------------------------------------------------", 0x33},
+ {10, 0x80000000U, "2147483648\0--------------------------------------------------------", 0x44},
+ {10, -2147483647, "-2147483647\0-------------------------------------------------------", 0x33},
+ {10, -2147483647, "2147483649\0--------------------------------------------------------", 0x44},
+ {10, -2, "-2\0----------------------------------------------------------------", 0x33},
+ {10, -2, "4294967294\0--------------------------------------------------------", 0x44},
+ {10, -1, "-1\0----------------------------------------------------------------", 0x33},
+ {10, -1, "4294967295\0--------------------------------------------------------", 0x44},
+ {10, 0, "0\0-----------------------------------------------------------------", 0x77},
+ {10, 1, "1\0-----------------------------------------------------------------", 0x77},
+ {10, 12, "12\0----------------------------------------------------------------", 0x77},
+ {10, 123, "123\0---------------------------------------------------------------", 0x77},
+ {10, 1234, "1234\0--------------------------------------------------------------", 0x77},
+ {10, 12345, "12345\0-------------------------------------------------------------", 0x77},
+ {10, 123456, "123456\0------------------------------------------------------------", 0x77},
+ {10, 1234567, "1234567\0-----------------------------------------------------------", 0x77},
+ {10, 12345678, "12345678\0----------------------------------------------------------", 0x77},
+ {10, 123456789, "123456789\0---------------------------------------------------------", 0x77},
+ {10, 2147483646, "2147483646\0--------------------------------------------------------", 0x77},
+ {10, 2147483647, "2147483647\0--------------------------------------------------------", 0x77},
+ {10, 2147483648U, "-2147483648\0-------------------------------------------------------", 0x33},
+ {10, 2147483648U, "2147483648\0--------------------------------------------------------", 0x44},
+ {10, 2147483649U, "-2147483647\0-------------------------------------------------------", 0x33},
+ {10, 2147483649U, "2147483649\0--------------------------------------------------------", 0x44},
+ {10, 4294967294U, "-2\0----------------------------------------------------------------", 0x33},
+ {10, 4294967294U, "4294967294\0--------------------------------------------------------", 0x44},
+ {10, 4294967295U, "-1\0----------------------------------------------------------------", 0x33},
+ {10, 4294967295U, "4294967295\0--------------------------------------------------------", 0x44},
+
+ {16, 0, "0\0-----------------------------------------------------------------", 0x77},
+ {16, 1, "1\0-----------------------------------------------------------------", 0x77},
+ {16, 2147483646, "7ffffffe\0----------------------------------------------------------", 0x77},
+ {16, 2147483647, "7fffffff\0----------------------------------------------------------", 0x77},
+ {16, 0x80000000, "80000000\0----------------------------------------------------------", 0x77},
+ {16, 0x80000001, "80000001\0----------------------------------------------------------", 0x77},
+ {16, 0xFFFFFFFE, "fffffffe\0----------------------------------------------------------", 0x77},
+ {16, 0xFFFFFFFF, "ffffffff\0----------------------------------------------------------", 0x77},
+
+ { 2, 32768, "1000000000000000\0--------------------------------------------------", 0x77},
+ { 2, 65536, "10000000000000000\0-------------------------------------------------", 0x77},
+ { 2, 131072, "100000000000000000\0------------------------------------------------", 0x77},
+ {16, 0xffffffff, "ffffffff\0----------------------------------------------------------", 0x77},
+ {16, 0xa, "a\0-----------------------------------------------------------------", 0x77},
+ {16, 0, "0\0-----------------------------------------------------------------", 0x77},
+ {20, 3368421, "111111\0------------------------------------------------------------", 0x77},
+ {36, 62193781, "111111\0------------------------------------------------------------", 0x77},
+ {37, 71270178, "111111\0------------------------------------------------------------", 0x77},
+};
+#define NB_ULONG2STR (sizeof(ulong2str)/sizeof(*ulong2str))
+
+
+static void one_itoa_test(int test_num, const ulong2str_t *ulong2str)
+{
+ char dest_str[LARGE_STRI_BUFFER_LENGTH + 1];
+ int value;
+ LPSTR result;
+
+ memset(dest_str, '-', LARGE_STRI_BUFFER_LENGTH);
+ dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ value = ulong2str->value;
+ result = p_itoa(value, dest_str, ulong2str->base);
+ ok(result == dest_str,
+ "(test %d): _itoa(%d, [out], %d) has result %p, expected: %p\n",
+ test_num, value, ulong2str->base, result, dest_str);
+ ok(memcmp(dest_str, ulong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
+ "(test %d): _itoa(%d, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, value, ulong2str->base, dest_str, ulong2str->Buffer);
+}
+
+
+static void one_ltoa_test(int test_num, const ulong2str_t *ulong2str)
+{
+ char dest_str[LARGE_STRI_BUFFER_LENGTH + 1];
+ long value;
+ LPSTR result;
+
+ memset(dest_str, '-', LARGE_STRI_BUFFER_LENGTH);
+ dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ value = ulong2str->value;
+ result = p_ltoa(ulong2str->value, dest_str, ulong2str->base);
+ ok(result == dest_str,
+ "(test %d): _ltoa(%ld, [out], %d) has result %p, expected: %p\n",
+ test_num, value, ulong2str->base, result, dest_str);
+ ok(memcmp(dest_str, ulong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
+ "(test %d): _ltoa(%ld, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, value, ulong2str->base, dest_str, ulong2str->Buffer);
+}
+
+
+static void one_ultoa_test(int test_num, const ulong2str_t *ulong2str)
+{
+ char dest_str[LARGE_STRI_BUFFER_LENGTH + 1];
+ unsigned long value;
+ LPSTR result;
+
+ memset(dest_str, '-', LARGE_STRI_BUFFER_LENGTH);
+ dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ value = ulong2str->value;
+ result = p_ultoa(ulong2str->value, dest_str, ulong2str->base);
+ ok(result == dest_str,
+ "(test %d): _ultoa(%lu, [out], %d) has result %p, expected: %p\n",
+ test_num, value, ulong2str->base, result, dest_str);
+ ok(memcmp(dest_str, ulong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
+ "(test %d): _ultoa(%lu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, value, ulong2str->base, dest_str, ulong2str->Buffer);
+}
+
+
+static void test_ulongtoa(void)
+{
+ int test_num;
+
+ for (test_num = 0; test_num < NB_ULONG2STR; test_num++) {
+ if (ulong2str[test_num].mask & 0x01) {
+ one_itoa_test(test_num, &ulong2str[test_num]);
+ } /* if */
+ if (ulong2str[test_num].mask & 0x02) {
+ one_ltoa_test(test_num, &ulong2str[test_num]);
+ } /* if */
+ if (ulong2str[test_num].mask & 0x04) {
+ one_ultoa_test(test_num, &ulong2str[test_num]);
+ } /* if */
+ } /* for */
+}
+
+
+static void one_itow_test(int test_num, const ulong2str_t *ulong2str)
+{
+ int pos;
+ WCHAR expected_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ WCHAR dest_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING unicode_string;
+ STRING ansi_str;
+ int value;
+ LPWSTR result;
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str->Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ dest_wstr[pos] = '-';
+ } /* for */
+ dest_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ unicode_string.Length = LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR);
+ unicode_string.MaximumLength = unicode_string.Length + sizeof(WCHAR);
+ unicode_string.Buffer = dest_wstr;
+ value = ulong2str->value;
+ result = p_itow(value, dest_wstr, ulong2str->base);
+ pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
+ ok(result == dest_wstr,
+ "(test %d): _itow(%d, [out], %d) has result %p, expected: %p\n",
+ test_num, value, ulong2str->base, result, dest_wstr);
+ ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): _itow(%d, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, value, ulong2str->base, ansi_str.Buffer, ulong2str->Buffer);
+ pRtlFreeAnsiString(&ansi_str);
+}
+
+
+static void one_ltow_test(int test_num, const ulong2str_t *ulong2str)
+{
+ int pos;
+ WCHAR expected_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ WCHAR dest_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING unicode_string;
+ STRING ansi_str;
+ long value;
+ LPWSTR result;
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str->Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ dest_wstr[pos] = '-';
+ } /* for */
+ dest_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ unicode_string.Length = LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR);
+ unicode_string.MaximumLength = unicode_string.Length + sizeof(WCHAR);
+ unicode_string.Buffer = dest_wstr;
+
+ value = ulong2str->value;
+ result = p_ltow(value, dest_wstr, ulong2str->base);
+ pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
+ ok(result == dest_wstr,
+ "(test %d): _ltow(%ld, [out], %d) has result %p, expected: %p\n",
+ test_num, value, ulong2str->base, result, dest_wstr);
+ ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): _ltow(%ld, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, value, ulong2str->base, ansi_str.Buffer, ulong2str->Buffer);
+ pRtlFreeAnsiString(&ansi_str);
+}
+
+
+static void one_ultow_test(int test_num, const ulong2str_t *ulong2str)
+{
+ int pos;
+ WCHAR expected_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ WCHAR dest_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING unicode_string;
+ STRING ansi_str;
+ unsigned long value;
+ LPWSTR result;
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str->Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ dest_wstr[pos] = '-';
+ } /* for */
+ dest_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ unicode_string.Length = LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR);
+ unicode_string.MaximumLength = unicode_string.Length + sizeof(WCHAR);
+ unicode_string.Buffer = dest_wstr;
+
+ value = ulong2str->value;
+ result = p_ultow(value, dest_wstr, ulong2str->base);
+ pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
+ ok(result == dest_wstr,
+ "(test %d): _ultow(%lu, [out], %d) has result %p, expected: %p\n",
+ test_num, value, ulong2str->base, result, dest_wstr);
+ ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): _ultow(%lu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, value, ulong2str->base, ansi_str.Buffer, ulong2str->Buffer);
+ pRtlFreeAnsiString(&ansi_str);
+}
+
+
+static void test_ulongtow(void)
+{
+ int test_num;
+ int pos;
+ WCHAR expected_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ LPWSTR result;
+
+ for (test_num = 0; test_num < NB_ULONG2STR; test_num++) {
+ if (ulong2str[test_num].mask & 0x10) {
+ one_itow_test(test_num, &ulong2str[test_num]);
+ } /* if */
+ if (ulong2str[test_num].mask & 0x20) {
+ one_ltow_test(test_num, &ulong2str[test_num]);
+ } /* if */
+ if (ulong2str[test_num].mask & 0x40) {
+ one_ultow_test(test_num, &ulong2str[test_num]);
+ } /* if */
+ } /* for */
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str[0].Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ result = p_itow(ulong2str[0].value, NULL, 10);
+ ok(result == NULL,
+ "(test a): _itow(%ld, NULL, 10) has result %p, expected: NULL\n",
+ ulong2str[0].value, result);
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str[0].Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ result = p_ltow(ulong2str[0].value, NULL, 10);
+ ok(result == NULL,
+ "(test b): _ltow(%ld, NULL, 10) has result %p, expected: NULL\n",
+ ulong2str[0].value, result);
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str[0].Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ result = p_ultow(ulong2str[0].value, NULL, 10);
+ ok(result == NULL,
+ "(test c): _ultow(%ld, NULL, 10) has result %p, expected: NULL\n",
+ ulong2str[0].value, result);
+}
+
+#define ULL(a,b) (((ULONGLONG)(a) << 32) | (b))
+
+typedef struct {
+ int base;
+ ULONGLONG value;
+ const char *Buffer;
+ int mask; /* ntdll/msvcrt: 0x01=i64toa, 0x02=ui64toa, 0x04=wrong _i64toa try next example */
+ /* 0x10=i64tow, 0x20=ui64tow, 0x40=wrong _i64tow try next example */
+} ulonglong2str_t;
+
+static const ulonglong2str_t ulonglong2str[] = {
+ {10, 123, "123\0---------------------------------------------------------------", 0x33},
+
+ { 2, 0x80000000U, "10000000000000000000000000000000\0----------------------------------", 0x33},
+ { 2, -2147483647, "1111111111111111111111111111111110000000000000000000000000000001\0--", 0x33},
+ { 2, -65537, "1111111111111111111111111111111111111111111111101111111111111111\0--", 0x33},
+ { 2, -65536, "1111111111111111111111111111111111111111111111110000000000000000\0--", 0x33},
+ { 2, -65535, "1111111111111111111111111111111111111111111111110000000000000001\0--", 0x33},
+ { 2, -32768, "1111111111111111111111111111111111111111111111111000000000000000\0--", 0x33},
+ { 2, -32767, "1111111111111111111111111111111111111111111111111000000000000001\0--", 0x33},
+ { 2, -2, "1111111111111111111111111111111111111111111111111111111111111110\0--", 0x33},
+ { 2, -1, "1111111111111111111111111111111111111111111111111111111111111111\0--", 0x33},
+ { 2, 0, "0\0-----------------------------------------------------------------", 0x33},
+ { 2, 1, "1\0-----------------------------------------------------------------", 0x33},
+ { 2, 10, "1010\0--------------------------------------------------------------", 0x33},
+ { 2, 100, "1100100\0-----------------------------------------------------------", 0x33},
+ { 2, 1000, "1111101000\0--------------------------------------------------------", 0x33},
+ { 2, 10000, "10011100010000\0----------------------------------------------------", 0x33},
+ { 2, 32767, "111111111111111\0---------------------------------------------------", 0x33},
+ { 2, 32768, "1000000000000000\0--------------------------------------------------", 0x33},
+ { 2, 65535, "1111111111111111\0--------------------------------------------------", 0x33},
+ { 2, 100000, "11000011010100000\0-------------------------------------------------", 0x33},
+ { 2, 234567, "111001010001000111\0------------------------------------------------", 0x33},
+ { 2, 300000, "1001001001111100000\0-----------------------------------------------", 0x33},
+ { 2, 524287, "1111111111111111111\0-----------------------------------------------", 0x33},
+ { 2, 524288, "10000000000000000000\0----------------------------------------------", 0x33},
+ { 2, 1000000, "11110100001001000000\0----------------------------------------------", 0x33},
+ { 2, 10000000, "100110001001011010000000\0------------------------------------------", 0x33},
+ { 2, 100000000, "101111101011110000100000000\0---------------------------------------", 0x33},
+ { 2, 1000000000, "111011100110101100101000000000\0------------------------------------", 0x33},
+ { 2, 1073741823, "111111111111111111111111111111\0------------------------------------", 0x33},
+ { 2, 2147483646, "1111111111111111111111111111110\0-----------------------------------", 0x33},
+ { 2, 2147483647, "1111111111111111111111111111111\0-----------------------------------", 0x33},
+ { 2, 2147483648U, "10000000000000000000000000000000\0----------------------------------", 0x33},
+ { 2, 2147483649U, "10000000000000000000000000000001\0----------------------------------", 0x33},
+ { 2, 4294967294U, "11111111111111111111111111111110\0----------------------------------", 0x33},
+ { 2, 0xFFFFFFFF, "11111111111111111111111111111111\0----------------------------------", 0x33},
+ { 2, ULL(0x1,0xffffffff), "111111111111111111111111111111111\0---------------------------------", 0x33},
+ { 2, ((ULONGLONG)100000)*100000, "1001010100000010111110010000000000\0--------------------------------", 0x33},
+ { 2, ULL(0x3,0xffffffff), "1111111111111111111111111111111111\0--------------------------------", 0x33},
+ { 2, ULL(0x7,0xffffffff), "11111111111111111111111111111111111\0-------------------------------", 0x33},
+ { 2, ULL(0xf,0xffffffff), "111111111111111111111111111111111111\0------------------------------", 0x33},
+ { 2, ((ULONGLONG)100000)*1000000, "1011101001000011101101110100000000000\0-----------------------------", 0x33},
+ { 2, ULL(0x1f,0xffffffff), "1111111111111111111111111111111111111\0-----------------------------", 0x33},
+ { 2, ULL(0x3f,0xffffffff), "11111111111111111111111111111111111111\0----------------------------", 0x33},
+ { 2, ULL(0x7f,0xffffffff), "111111111111111111111111111111111111111\0---------------------------", 0x33},
+ { 2, ULL(0xff,0xffffffff), "1111111111111111111111111111111111111111\0--------------------------", 0x33},
+
+ { 8, 0x80000000U, "20000000000\0-------------------------------------------------------", 0x33},
+ { 8, -2147483647, "1777777777760000000001\0--------------------------------------------", 0x33},
+ { 8, -2, "1777777777777777777776\0--------------------------------------------", 0x33},
+ { 8, -1, "1777777777777777777777\0--------------------------------------------", 0x33},
+ { 8, 0, "0\0-----------------------------------------------------------------", 0x33},
+ { 8, 1, "1\0-----------------------------------------------------------------", 0x33},
+ { 8, 2147483646, "17777777776\0-------------------------------------------------------", 0x33},
+ { 8, 2147483647, "17777777777\0-------------------------------------------------------", 0x33},
+ { 8, 2147483648U, "20000000000\0-------------------------------------------------------", 0x33},
+ { 8, 2147483649U, "20000000001\0-------------------------------------------------------", 0x33},
+ { 8, 4294967294U, "37777777776\0-------------------------------------------------------", 0x33},
+ { 8, 4294967295U, "37777777777\0-------------------------------------------------------", 0x33},
+
+ {10, 0x80000000U, "2147483648\0--------------------------------------------------------", 0x33},
+ {10, -2147483647, "-2147483647\0-------------------------------------------------------", 0x55},
+ {10, -2147483647, "-18446744071562067969\0---------------------------------------------", 0x00},
+ {10, -2147483647, "18446744071562067969\0----------------------------------------------", 0x22},
+ {10, -2, "-2\0----------------------------------------------------------------", 0x55},
+ {10, -2, "-18446744073709551614\0---------------------------------------------", 0x00},
+ {10, -2, "18446744073709551614\0----------------------------------------------", 0x22},
+ {10, -1, "-1\0----------------------------------------------------------------", 0x55},
+ {10, -1, "-18446744073709551615\0---------------------------------------------", 0x00},
+ {10, -1, "18446744073709551615\0----------------------------------------------", 0x22},
+ {10, 0, "0\0-----------------------------------------------------------------", 0x33},
+ {10, 1, "1\0-----------------------------------------------------------------", 0x33},
+ {10, 12, "12\0----------------------------------------------------------------", 0x33},
+ {10, 123, "123\0---------------------------------------------------------------", 0x33},
+ {10, 1234, "1234\0--------------------------------------------------------------", 0x33},
+ {10, 12345, "12345\0-------------------------------------------------------------", 0x33},
+ {10, 123456, "123456\0------------------------------------------------------------", 0x33},
+ {10, 1234567, "1234567\0-----------------------------------------------------------", 0x33},
+ {10, 12345678, "12345678\0----------------------------------------------------------", 0x33},
+ {10, 123456789, "123456789\0---------------------------------------------------------", 0x33},
+ {10, 2147483646, "2147483646\0--------------------------------------------------------", 0x33},
+ {10, 2147483647, "2147483647\0--------------------------------------------------------", 0x33},
+ {10, 2147483648U, "2147483648\0--------------------------------------------------------", 0x33},
+ {10, 2147483649U, "2147483649\0--------------------------------------------------------", 0x33},
+ {10, 4294967294U, "4294967294\0--------------------------------------------------------", 0x33},
+ {10, 4294967295U, "4294967295\0--------------------------------------------------------", 0x33},
+ {10, ULL(0x2,0xdfdc1c35), "12345678901\0-------------------------------------------------------", 0x33},
+ {10, ULL(0xe5,0xf4c8f374), "987654321012\0------------------------------------------------------", 0x33},
+ {10, ULL(0x1c0,0xfc161e3e), "1928374656574\0-----------------------------------------------------", 0x33},
+ {10, ULL(0xbad,0xcafeface), "12841062955726\0----------------------------------------------------", 0x33},
+ {10, ULL(0x5bad,0xcafeface), "100801993177806\0---------------------------------------------------", 0x33},
+ {10, ULL(0xaface,0xbeefcafe), "3090515640699646\0--------------------------------------------------", 0x33},
+ {10, ULL(0xa5beef,0xabcdcafe), "46653307746110206\0-------------------------------------------------", 0x33},
+ {10, ULL(0x1f8cf9b,0xf2df3af1), "142091656963767025\0------------------------------------------------", 0x33},
+ {10, ULL(0x0fffffff,0xffffffff), "1152921504606846975\0-----------------------------------------------", 0x33},
+ {10, ULL(0x7fffffff,0xffffffff), "9223372036854775807\0-----------------------------------------------", 0x33},
+ {10, ULL(0x80000000,0x00000000), "-9223372036854775808\0----------------------------------------------", 0x11},
+ {10, ULL(0x80000000,0x00000000), "9223372036854775808\0-----------------------------------------------", 0x22},
+ {10, ULL(0x80000000,0x00000001), "-9223372036854775807\0----------------------------------------------", 0x55},
+ {10, ULL(0x80000000,0x00000001), "-9223372036854775809\0----------------------------------------------", 0x00},
+ {10, ULL(0x80000000,0x00000001), "9223372036854775809\0-----------------------------------------------", 0x22},
+ {10, ULL(0x80000000,0x00000002), "-9223372036854775806\0----------------------------------------------", 0x55},
+ {10, ULL(0x80000000,0x00000002), "-9223372036854775810\0----------------------------------------------", 0x00},
+ {10, ULL(0x80000000,0x00000002), "9223372036854775810\0-----------------------------------------------", 0x22},
+ {10, ULL(0xffffffff,0xfffffffe), "-2\0----------------------------------------------------------------", 0x55},
+ {10, ULL(0xffffffff,0xfffffffe), "-18446744073709551614\0---------------------------------------------", 0x00},
+ {10, ULL(0xffffffff,0xfffffffe), "18446744073709551614\0----------------------------------------------", 0x22},
+ {10, ULL(0xffffffff,0xffffffff), "-1\0----------------------------------------------------------------", 0x55},
+ {10, ULL(0xffffffff,0xffffffff), "-18446744073709551615\0---------------------------------------------", 0x00},
+ {10, ULL(0xffffffff,0xffffffff), "18446744073709551615\0----------------------------------------------", 0x22},
+
+ {16, 0, "0\0-----------------------------------------------------------------", 0x33},
+ {16, 1, "1\0-----------------------------------------------------------------", 0x33},
+ {16, 2147483646, "7ffffffe\0----------------------------------------------------------", 0x33},
+ {16, 2147483647, "7fffffff\0----------------------------------------------------------", 0x33},
+ {16, 0x80000000, "80000000\0----------------------------------------------------------", 0x33},
+ {16, 0x80000001, "80000001\0----------------------------------------------------------", 0x33},
+ {16, 0xFFFFFFFE, "fffffffe\0----------------------------------------------------------", 0x33},
+ {16, 0xFFFFFFFF, "ffffffff\0----------------------------------------------------------", 0x33},
+ {16, ULL(0x1,0x00000000), "100000000\0---------------------------------------------------------", 0x33},
+ {16, ULL(0xbad,0xdeadbeef), "baddeadbeef\0-------------------------------------------------------", 0x33},
+ {16, ULL(0x80000000,0x00000000), "8000000000000000\0--------------------------------------------------", 0x33},
+ {16, ULL(0xfedcba98,0x76543210), "fedcba9876543210\0--------------------------------------------------", 0x33},
+ {16, ULL(0xffffffff,0x80000001), "ffffffff80000001\0--------------------------------------------------", 0x33},
+ {16, ULL(0xffffffff,0xfffffffe), "fffffffffffffffe\0--------------------------------------------------", 0x33},
+ {16, ULL(0xffffffff,0xffffffff), "ffffffffffffffff\0--------------------------------------------------", 0x33},
+
+ { 2, 32768, "1000000000000000\0--------------------------------------------------", 0x33},
+ { 2, 65536, "10000000000000000\0-------------------------------------------------", 0x33},
+ { 2, 131072, "100000000000000000\0------------------------------------------------", 0x33},
+ {16, 0xffffffff, "ffffffff\0----------------------------------------------------------", 0x33},
+ {16, 0xa, "a\0-----------------------------------------------------------------", 0x33},
+ {16, 0, "0\0-----------------------------------------------------------------", 0x33},
+ {20, 3368421, "111111\0------------------------------------------------------------", 0x33},
+ {36, 62193781, "111111\0------------------------------------------------------------", 0x33},
+ {37, 71270178, "111111\0------------------------------------------------------------", 0x33},
+ {99, ULL(0x2,0x3c9e468c), "111111\0------------------------------------------------------------", 0x33},
+};
+#define NB_ULONGLONG2STR (sizeof(ulonglong2str)/sizeof(*ulonglong2str))
+
+
+static void one_i64toa_test(int test_num, const ulonglong2str_t *ulonglong2str)
+{
+ LPSTR result;
+ char dest_str[LARGE_STRI_BUFFER_LENGTH + 1];
+
+ memset(dest_str, '-', LARGE_STRI_BUFFER_LENGTH);
+ dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ result = p_i64toa(ulonglong2str->value, dest_str, ulonglong2str->base);
+ ok(result == dest_str,
+ "(test %d): _i64toa(%Lu, [out], %d) has result %p, expected: %p\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, result, dest_str);
+ if (ulonglong2str->mask & 0x04) {
+ if (memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) != 0) {
+ if (memcmp(dest_str, ulonglong2str[1].Buffer, LARGE_STRI_BUFFER_LENGTH) != 0) {
+ ok(memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
+ "(test %d): _i64toa(%Lu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, dest_str, ulonglong2str->Buffer);
+ } /* if */
+ } /* if */
+ } else {
+ ok(memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
+ "(test %d): _i64toa(%Lu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, dest_str, ulonglong2str->Buffer);
+ } /* if */
+}
+
+
+static void one_ui64toa_test(int test_num, const ulonglong2str_t *ulonglong2str)
+{
+ LPSTR result;
+ char dest_str[LARGE_STRI_BUFFER_LENGTH + 1];
+
+ memset(dest_str, '-', LARGE_STRI_BUFFER_LENGTH);
+ dest_str[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ result = p_ui64toa(ulonglong2str->value, dest_str, ulonglong2str->base);
+ ok(result == dest_str,
+ "(test %d): _ui64toa(%Lu, [out], %d) has result %p, expected: %p\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, result, dest_str);
+ ok(memcmp(dest_str, ulonglong2str->Buffer, LARGE_STRI_BUFFER_LENGTH) == 0,
+ "(test %d): _ui64toa(%Lu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, dest_str, ulonglong2str->Buffer);
+}
+
+
+static void test_ulonglongtoa(void)
+{
+ int test_num;
+
+ for (test_num = 0; test_num < NB_ULONGLONG2STR; test_num++) {
+ if (ulonglong2str[test_num].mask & 0x01) {
+ one_i64toa_test(test_num, &ulonglong2str[test_num]);
+ } /* if */
+ if (p_ui64toa != NULL) {
+ if (ulonglong2str[test_num].mask & 0x02) {
+ one_ui64toa_test(test_num, &ulonglong2str[test_num]);
+ } /* if */
+ } /* if */
+ } /* for */
+}
+
+
+static void one_i64tow_test(int test_num, const ulonglong2str_t *ulonglong2str)
+{
+ int pos;
+ WCHAR expected_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ WCHAR dest_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING unicode_string;
+ STRING ansi_str;
+ LPWSTR result;
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulonglong2str->Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ dest_wstr[pos] = '-';
+ } /* for */
+ dest_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ unicode_string.Length = LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR);
+ unicode_string.MaximumLength = unicode_string.Length + sizeof(WCHAR);
+ unicode_string.Buffer = dest_wstr;
+
+ result = p_i64tow(ulonglong2str->value, dest_wstr, ulonglong2str->base);
+ pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
+ ok(result == dest_wstr,
+ "(test %d): _i64tow(%llu, [out], %d) has result %p, expected: %p\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, result, dest_wstr);
+ if (ulonglong2str->mask & 0x04) {
+ if (memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) != 0) {
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulonglong2str[1].Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ if (memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) != 0) {
+ ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): _i64tow(%llu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, ansi_str.Buffer, ulonglong2str->Buffer);
+ } /* if */
+ } /* if */
+ } else {
+ ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): _i64tow(%llu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, ansi_str.Buffer, ulonglong2str->Buffer);
+ } /* if */
+ pRtlFreeAnsiString(&ansi_str);
+}
+
+
+static void one_ui64tow_test(int test_num, const ulonglong2str_t *ulonglong2str)
+{
+ int pos;
+ WCHAR expected_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ WCHAR dest_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ UNICODE_STRING unicode_string;
+ STRING ansi_str;
+ LPWSTR result;
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulonglong2str->Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ dest_wstr[pos] = '-';
+ } /* for */
+ dest_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ unicode_string.Length = LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR);
+ unicode_string.MaximumLength = unicode_string.Length + sizeof(WCHAR);
+ unicode_string.Buffer = dest_wstr;
+
+ result = p_ui64tow(ulonglong2str->value, dest_wstr, ulonglong2str->base);
+ pRtlUnicodeStringToAnsiString(&ansi_str, &unicode_string, 1);
+ ok(result == dest_wstr,
+ "(test %d): _ui64tow(%llu, [out], %d) has result %p, expected: %p\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, result, dest_wstr);
+ ok(memcmp(dest_wstr, expected_wstr, LARGE_STRI_BUFFER_LENGTH * sizeof(WCHAR)) == 0,
+ "(test %d): _ui64tow(%llu, [out], %d) assigns string \"%s\", expected: \"%s\"\n",
+ test_num, ulonglong2str->value, ulonglong2str->base, ansi_str.Buffer, ulonglong2str->Buffer);
+ pRtlFreeAnsiString(&ansi_str);
+}
+
+
+static void test_ulonglongtow(void)
+{
+ int test_num;
+ int pos;
+ WCHAR expected_wstr[LARGE_STRI_BUFFER_LENGTH + 1];
+ LPWSTR result;
+
+ for (test_num = 0; test_num < NB_ULONGLONG2STR; test_num++) {
+ if (ulonglong2str[test_num].mask & 0x10) {
+ one_i64tow_test(test_num, &ulonglong2str[test_num]);
+ } /* if */
+ if (p_ui64tow) {
+ if (ulonglong2str[test_num].mask & 0x20) {
+ one_ui64tow_test(test_num, &ulonglong2str[test_num]);
+ } /* if */
+ } /* if */
+ } /* for */
+
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str[0].Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ result = p_i64tow(ulong2str[0].value, NULL, 10);
+ ok(result == NULL,
+ "(test d): _i64tow(%llu, NULL, 10) has result %p, expected: NULL\n",
+ ulonglong2str[0].value, result);
+
+ if (p_ui64tow) {
+ for (pos = 0; pos < LARGE_STRI_BUFFER_LENGTH; pos++) {
+ expected_wstr[pos] = ulong2str[0].Buffer[pos];
+ } /* for */
+ expected_wstr[LARGE_STRI_BUFFER_LENGTH] = '\0';
+ result = p_ui64tow(ulong2str[0].value, NULL, 10);
+ ok(result == NULL,
+ "(test e): _ui64tow(%llu, NULL, 10) has result %p, expected: NULL\n",
+ ulonglong2str[0].value, result);
+ } /* if */
+}
+
+
+typedef struct {
+ const char *str;
+ LONG value;
+} str2long_t;
+
+static const str2long_t str2long[] = {
+ { "1011101100", 1011101100 },
+ { "1234567", 1234567 },
+ { "-214", -214 },
+ { "+214", 214 }, /* The + sign is allowed also */
+ { "--214", 0 }, /* Do not accept more than one sign */
+ { "-+214", 0 },
+ { "++214", 0 },
+ { "+-214", 0 },
+ { "\00141", 0 }, /* not whitespace char 1 */
+ { "\00242", 0 }, /* not whitespace char 2 */
+ { "\00343", 0 }, /* not whitespace char 3 */
+ { "\00444", 0 }, /* not whitespace char 4 */
+ { "\00545", 0 }, /* not whitespace char 5 */
+ { "\00646", 0 }, /* not whitespace char 6 */
+ { "\00747", 0 }, /* not whitespace char 7 */
+ { "\01050", 0 }, /* not whitespace char 8 */
+ { "\01151", 51 }, /* is whitespace char 9 (tab) */
+ { "\01252", 52 }, /* is whitespace char 10 (lf) */
+ { "\01353", 53 }, /* is whitespace char 11 (vt) */
+ { "\01454", 54 }, /* is whitespace char 12 (ff) */
+ { "\01555", 55 }, /* is whitespace char 13 (cr) */
+ { "\01656", 0 }, /* not whitespace char 14 */
+ { "\01757", 0 }, /* not whitespace char 15 */
+ { "\02060", 0 }, /* not whitespace char 16 */
+ { "\02161", 0 }, /* not whitespace char 17 */
+ { "\02262", 0 }, /* not whitespace char 18 */
+ { "\02363", 0 }, /* not whitespace char 19 */
+ { "\02464", 0 }, /* not whitespace char 20 */
+ { "\02565", 0 }, /* not whitespace char 21 */
+ { "\02666", 0 }, /* not whitespace char 22 */
+ { "\02767", 0 }, /* not whitespace char 23 */
+ { "\03070", 0 }, /* not whitespace char 24 */
+ { "\03171", 0 }, /* not whitespace char 25 */
+ { "\03272", 0 }, /* not whitespace char 26 */
+ { "\03373", 0 }, /* not whitespace char 27 */
+ { "\03474", 0 }, /* not whitespace char 28 */
+ { "\03575", 0 }, /* not whitespace char 29 */
+ { "\03676", 0 }, /* not whitespace char 30 */
+ { "\03777", 0 }, /* not whitespace char 31 */
+ { "\04080", 80 }, /* is whitespace char 32 (space) */
+ { " \n \r \t214", 214 },
+ { " \n \r \t+214", 214 }, /* Signs can be used after whitespace */
+ { " \n \r \t-214", -214 },
+ { "+214 0", 214 }, /* Space terminates the number */
+ { " 214.01", 214 }, /* Decimal point not accepted */
+ { " 214,01", 214 }, /* Decimal comma not accepted */
+ { "f81", 0 },
+ { "0x12345", 0 }, /* Hex not accepted */
+ { "00x12345", 0 },
+ { "0xx12345", 0 },
+ { "1x34", 1 },
+ { "-9999999999", -1410065407 }, /* Big negative integer */
+ { "-2147483649", 2147483647 }, /* Too small to fit in 32 Bits */
+ { "-2147483648", 0x80000000 }, /* Smallest negative integer */
+ { "-2147483647", -2147483647 },
+ { "-1", -1 },
+ { "0", 0 },
+ { "1", 1 },
+ { "2147483646", 2147483646 },
+ { "2147483647", 2147483647 }, /* Largest signed positive integer */
+ { "2147483648", 2147483648UL }, /* Positive int equal to smallest negative int */
+ { "2147483649", 2147483649UL },
+ { "4294967294", 4294967294UL },
+ { "4294967295", 4294967295UL }, /* Largest unsigned integer */
+ { "4294967296", 0 }, /* Too big to fit in 32 Bits */
+ { "9999999999", 1410065407 }, /* Big positive integer */
+ { "056789", 56789 }, /* Leading zero and still decimal */
+ { "b1011101100", 0 }, /* Binary (b-notation) */
+ { "-b1011101100", 0 }, /* Negative Binary (b-notation) */
+ { "b10123456789", 0 }, /* Binary with nonbinary digits (2-9) */
+ { "0b1011101100", 0 }, /* Binary (0b-notation) */
+ { "-0b1011101100", 0 }, /* Negative binary (0b-notation) */
+ { "0b10123456789", 0 }, /* Binary with nonbinary digits (2-9) */
+ { "-0b10123456789", 0 }, /* Negative binary with nonbinary digits (2-9) */
+ { "0b1", 0 }, /* one digit binary */
+ { "0b2", 0 }, /* empty binary */
+ { "0b", 0 }, /* empty binary */
+ { "o1234567", 0 }, /* Octal (o-notation) */
+ { "-o1234567", 0 }, /* Negative Octal (o-notation) */
+ { "o56789", 0 }, /* Octal with nonoctal digits (8 and 9) */
+ { "0o1234567", 0 }, /* Octal (0o-notation) */
+ { "-0o1234567", 0 }, /* Negative octal (0o-notation) */
+ { "0o56789", 0 }, /* Octal with nonoctal digits (8 and 9) */
+ { "-0o56789", 0 }, /* Negative octal with nonoctal digits (8 and 9) */
+ { "0o7", 0 }, /* one digit octal */
+ { "0o8", 0 }, /* empty octal */
+ { "0o", 0 }, /* empty octal */
+ { "0d1011101100", 0 }, /* explizit decimal with 0d */
+ { "x89abcdef", 0 }, /* Hex with lower case digits a-f (x-notation) */
+ { "xFEDCBA00", 0 }, /* Hex with upper case digits A-F (x-notation) */
+ { "-xFEDCBA00", 0 }, /* Negative Hexadecimal (x-notation) */
+ { "0x89abcdef", 0 }, /* Hex with lower case digits a-f (0x-notation) */
+ { "0xFEDCBA00", 0 }, /* Hex with upper case digits A-F (0x-notation) */
+ { "-0xFEDCBA00", 0 }, /* Negative Hexadecimal (0x-notation) */
+ { "0xabcdefgh", 0 }, /* Hex with illegal lower case digits (g-z) */
+ { "0xABCDEFGH", 0 }, /* Hex with illegal upper case digits (G-Z) */
+ { "0xF", 0 }, /* one digit hexadecimal */
+ { "0xG", 0 }, /* empty hexadecimal */
+ { "0x", 0 }, /* empty hexadecimal */
+ { "", 0 }, /* empty string */
+/* { NULL, 0 }, */ /* NULL as string */
+};
+#define NB_STR2LONG (sizeof(str2long)/sizeof(*str2long))
+
+
+static void test_wtoi(void)
+{
+ int test_num;
+ UNICODE_STRING uni;
+ int result;
+
+ for (test_num = 0; test_num < NB_STR2LONG; test_num++) {
+ pRtlCreateUnicodeStringFromAsciiz(&uni, str2long[test_num].str);
+ result = p_wtoi(uni.Buffer);
+ ok(result == str2long[test_num].value,
+ "(test %d): call failed: _wtoi(\"%s\") has result %d, expected: %ld\n",
+ test_num, str2long[test_num].str, result, str2long[test_num].value);
+ pRtlFreeUnicodeString(&uni);
+ } /* for */
+}
+
+
+static void test_wtol(void)
+{
+ int test_num;
+ UNICODE_STRING uni;
+ LONG result;
+
+ for (test_num = 0; test_num < NB_STR2LONG; test_num++) {
+ pRtlCreateUnicodeStringFromAsciiz(&uni, str2long[test_num].str);
+ result = p_wtol(uni.Buffer);
+ ok(result == str2long[test_num].value,
+ "(test %d): call failed: _wtol(\"%s\") has result %ld, expected: %ld\n",
+ test_num, str2long[test_num].str, result, str2long[test_num].value);
+ pRtlFreeUnicodeString(&uni);
+ } /* for */
+}
+
+
+typedef struct {
+ const char *str;
+ LONGLONG value;
+} str2longlong_t;
+
+static const str2longlong_t str2longlong[] = {
+ { "1011101100", 1011101100 },
+ { "1234567", 1234567 },
+ { "-214", -214 },
+ { "+214", 214 }, /* The + sign is allowed also */
+ { "--214", 0 }, /* Do not accept more than one sign */
+ { "-+214", 0 },
+ { "++214", 0 },
+ { "+-214", 0 },
+ { "\00141", 0 }, /* not whitespace char 1 */
+ { "\00242", 0 }, /* not whitespace char 2 */
+ { "\00343", 0 }, /* not whitespace char 3 */
+ { "\00444", 0 }, /* not whitespace char 4 */
+ { "\00545", 0 }, /* not whitespace char 5 */
+ { "\00646", 0 }, /* not whitespace char 6 */
+ { "\00747", 0 }, /* not whitespace char 7 */
+ { "\01050", 0 }, /* not whitespace char 8 */
+ { "\01151", 51 }, /* is whitespace char 9 (tab) */
+ { "\01252", 52 }, /* is whitespace char 10 (lf) */
+ { "\01353", 53 }, /* is whitespace char 11 (vt) */
+ { "\01454", 54 }, /* is whitespace char 12 (ff) */
+ { "\01555", 55 }, /* is whitespace char 13 (cr) */
+ { "\01656", 0 }, /* not whitespace char 14 */
+ { "\01757", 0 }, /* not whitespace char 15 */
+ { "\02060", 0 }, /* not whitespace char 16 */
+ { "\02161", 0 }, /* not whitespace char 17 */
+ { "\02262", 0 }, /* not whitespace char 18 */
+ { "\02363", 0 }, /* not whitespace char 19 */
+ { "\02464", 0 }, /* not whitespace char 20 */
+ { "\02565", 0 }, /* not whitespace char 21 */
+ { "\02666", 0 }, /* not whitespace char 22 */
+ { "\02767", 0 }, /* not whitespace char 23 */
+ { "\03070", 0 }, /* not whitespace char 24 */
+ { "\03171", 0 }, /* not whitespace char 25 */
+ { "\03272", 0 }, /* not whitespace char 26 */
+ { "\03373", 0 }, /* not whitespace char 27 */
+ { "\03474", 0 }, /* not whitespace char 28 */
+ { "\03575", 0 }, /* not whitespace char 29 */
+ { "\03676", 0 }, /* not whitespace char 30 */
+ { "\03777", 0 }, /* not whitespace char 31 */
+ { "\04080", 80 }, /* is whitespace char 32 (space) */
+ { " \n \r \t214", 214 },
+ { " \n \r \t+214", 214 }, /* Signs can be used after whitespace */
+ { " \n \r \t-214", -214 },
+ { "+214 0", 214 }, /* Space terminates the number */
+ { " 214.01", 214 }, /* Decimal point not accepted */
+ { " 214,01", 214 }, /* Decimal comma not accepted */
+ { "f81", 0 },
+ { "0x12345", 0 }, /* Hex not accepted */
+ { "00x12345", 0 },
+ { "0xx12345", 0 },
+ { "1x34", 1 },
+ { "-99999999999999999999", -ULL(0x6bc75e2d,0x630fffff) }, /* Big negative integer */
+ { "-9223372036854775809", ULL(0x7fffffff,0xffffffff) }, /* Too small to fit in 64 bits */
+ { "-9223372036854775808", ULL(0x80000000,0x00000000) }, /* Smallest negative 64 bit integer */
+ { "-9223372036854775807", -ULL(0x7fffffff,0xffffffff) },
+ { "-9999999999", -ULL(0x00000002,0x540be3ff) },
+ { "-2147483649", -ULL(0x00000000,0x80000001) }, /* Too small to fit in 32 bits */
+ { "-2147483648", -ULL(0x00000000,0x80000000) }, /* Smallest 32 bits negative integer */
+ { "-2147483647", -2147483647 },
+ { "-1", -1 },
+ { "0", 0 },
+ { "1", 1 },
+ { "2147483646", 2147483646 },
+ { "2147483647", 2147483647 }, /* Largest signed positive 32 bit integer */
+ { "2147483648", ULL(0x00000000,0x80000000) }, /* Pos int equal to smallest neg 32 bit int */
+ { "2147483649", ULL(0x00000000,0x80000001) },
+ { "4294967294", ULL(0x00000000,0xfffffffe) },
+ { "4294967295", ULL(0x00000000,0xffffffff) }, /* Largest unsigned 32 bit integer */
+ { "4294967296", ULL(0x00000001,0x00000000) }, /* Too big to fit in 32 Bits */
+ { "9999999999", ULL(0x00000002,0x540be3ff) },
+ { "9223372036854775806", ULL(0x7fffffff,0xfffffffe) },
+ { "9223372036854775807", ULL(0x7fffffff,0xffffffff) }, /* Largest signed positive 64 bit integer */
+ { "9223372036854775808", ULL(0x80000000,0x00000000) }, /* Pos int equal to smallest neg 64 bit int */
+ { "9223372036854775809", ULL(0x80000000,0x00000001) },
+ { "18446744073709551614", ULL(0xffffffff,0xfffffffe) },
+ { "18446744073709551615", ULL(0xffffffff,0xffffffff) }, /* Largest unsigned 64 bit integer */
+ { "18446744073709551616", 0 }, /* Too big to fit in 64 bits */
+ { "99999999999999999999", ULL(0x6bc75e2d,0x630fffff) }, /* Big positive integer */
+ { "056789", 56789 }, /* Leading zero and still decimal */
+ { "b1011101100", 0 }, /* Binary (b-notation) */
+ { "-b1011101100", 0 }, /* Negative Binary (b-notation) */
+ { "b10123456789", 0 }, /* Binary with nonbinary digits (2-9) */
+ { "0b1011101100", 0 }, /* Binary (0b-notation) */
+ { "-0b1011101100", 0 }, /* Negative binary (0b-notation) */
+ { "0b10123456789", 0 }, /* Binary with nonbinary digits (2-9) */
+ { "-0b10123456789", 0 }, /* Negative binary with nonbinary digits (2-9) */
+ { "0b1", 0 }, /* one digit binary */
+ { "0b2", 0 }, /* empty binary */
+ { "0b", 0 }, /* empty binary */
+ { "o1234567", 0 }, /* Octal (o-notation) */
+ { "-o1234567", 0 }, /* Negative Octal (o-notation) */
+ { "o56789", 0 }, /* Octal with nonoctal digits (8 and 9) */
+ { "0o1234567", 0 }, /* Octal (0o-notation) */
+ { "-0o1234567", 0 }, /* Negative octal (0o-notation) */
+ { "0o56789", 0 }, /* Octal with nonoctal digits (8 and 9) */
+ { "-0o56789", 0 }, /* Negative octal with nonoctal digits (8 and 9) */
+ { "0o7", 0 }, /* one digit octal */
+ { "0o8", 0 }, /* empty octal */
+ { "0o", 0 }, /* empty octal */
+ { "0d1011101100", 0 }, /* explizit decimal with 0d */
+ { "x89abcdef", 0 }, /* Hex with lower case digits a-f (x-notation) */
+ { "xFEDCBA00", 0 }, /* Hex with upper case digits A-F (x-notation) */
+ { "-xFEDCBA00", 0 }, /* Negative Hexadecimal (x-notation) */
+ { "0x89abcdef", 0 }, /* Hex with lower case digits a-f (0x-notation) */
+ { "0xFEDCBA00", 0 }, /* Hex with upper case digits A-F (0x-notation) */
+ { "-0xFEDCBA00", 0 }, /* Negative Hexadecimal (0x-notation) */
+ { "0xabcdefgh", 0 }, /* Hex with illegal lower case digits (g-z) */
+ { "0xABCDEFGH", 0 }, /* Hex with illegal upper case digits (G-Z) */
+ { "0xF", 0 }, /* one digit hexadecimal */
+ { "0xG", 0 }, /* empty hexadecimal */
+ { "0x", 0 }, /* empty hexadecimal */
+ { "", 0 }, /* empty string */
+/* { NULL, 0 }, */ /* NULL as string */
+};
+#define NB_STR2LONGLONG (sizeof(str2longlong)/sizeof(*str2longlong))
+
+
+static void test_atoi64(void)
+{
+ int test_num;
+ LONGLONG result;
+
+ for (test_num = 0; test_num < NB_STR2LONGLONG; test_num++) {
+ result = p_atoi64(str2longlong[test_num].str);
+ ok(result == str2longlong[test_num].value,
+ "(test %d): call failed: _atoi64(\"%s\") has result %lld, expected: %lld\n",
+ test_num, str2longlong[test_num].str, result, str2longlong[test_num].value);
+ } /* for */
+}
+
+
+static void test_wtoi64(void)
+{
+ int test_num;
+ UNICODE_STRING uni;
+ LONGLONG result;
+
+ for (test_num = 0; test_num < NB_STR2LONGLONG; test_num++) {
+ pRtlCreateUnicodeStringFromAsciiz(&uni, str2longlong[test_num].str);
+ result = p_wtoi64(uni.Buffer);
+ ok(result == str2longlong[test_num].value,
+ "(test %d): call failed: _wtoi64(\"%s\") has result %lld, expected: %lld\n",
+ test_num, str2longlong[test_num].str, result, str2longlong[test_num].value);
+ pRtlFreeUnicodeString(&uni);
+ } /* for */
+}
+
+static void test_wcsfuncs(void)
+{
+ static const WCHAR testing[] = {'T','e','s','t','i','n','g',0};
+ ok (p_wcschr(testing,0)!=NULL, "wcschr Not finding terminating character\n");
+ ok (p_wcsrchr(testing,0)!=NULL, "wcsrchr Not finding terminating character\n");
+}
+
+START_TEST(string)
+{
+ InitFunctionPtrs();
+
+ if (p_ultoa)
+ test_ulongtoa();
+ if (p_ui64toa)
+ test_ulonglongtoa();
+ if (p_atoi64)
+ test_atoi64();
+ if (p_ultow)
+ test_ulongtow();
+ if (p_ui64tow)
+ test_ulonglongtow();
+ if (p_wtoi)
+ test_wtoi();
+ if (p_wtol)
+ test_wtol();
+ if (p_wtoi64)
+ test_wtoi64();
+ if (p_wcschr && p_wcsrchr)
+ test_wcsfuncs();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_atom(void);
+extern void func_change(void);
+extern void func_env(void);
+extern void func_error(void);
+extern void func_exception(void);
+extern void func_generated(void);
+extern void func_info(void);
+extern void func_large_int(void);
+extern void func_om(void);
+extern void func_path(void);
+extern void func_port(void);
+extern void func_reg(void);
+extern void func_rtl(void);
+extern void func_rtlbitmap(void);
+extern void func_rtlstr(void);
+extern void func_string(void);
+extern void func_time(void);
+
+const struct test winetest_testlist[] =
+{
+ { "atom", func_atom },
+ { "change", func_change },
+ { "env", func_env },
+ { "error", func_error },
+ { "exception", func_exception },
+// { "generated", func_generated },
+ { "info", func_info },
+ { "large_int", func_large_int },
+ { "om", func_om },
+ { "path", func_path },
+ { "port", func_port },
+ { "reg", func_reg },
+ { "rtl", func_rtl },
+ { "rtlbitmap", func_rtlbitmap },
+ { "rtlstr", func_rtlstr },
+ { "string", func_string },
+ { "time", func_time },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * Unit test suite for ntdll time functions
+ *
+ * Copyright 2004 Rein Klazes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "ntdll_test.h"
+
+#ifdef __WINE_WINTERNL_H
+
+#define TICKSPERSEC 10000000
+#define TICKSPERMSEC 10000
+#define SECSPERDAY 86400
+
+static VOID (WINAPI *pRtlTimeToTimeFields)( const LARGE_INTEGER *liTime, PTIME_FIELDS TimeFields) ;
+static VOID (WINAPI *pRtlTimeFieldsToTime)( PTIME_FIELDS TimeFields, PLARGE_INTEGER Time) ;
+
+static const int MonthLengths[2][12] =
+{
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static inline int IsLeapYear(int Year)
+{
+ return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
+}
+
+/* start time of the tests */
+TIME_FIELDS tftest = {1889,12,31,23,59,59,0,0};
+
+static void test_pRtlTimeToTimeFields(void)
+{
+ LARGE_INTEGER litime , liresult;
+ TIME_FIELDS tfresult;
+ int i=0;
+ litime.QuadPart = ((ULONGLONG)0x0144017a << 32) | 0xf0b0a980;
+ while( tftest.Year < 2110 ) {
+ /* test at the last second of the month */
+ pRtlTimeToTimeFields( &litime, &tfresult);
+ ok( tfresult.Year == tftest.Year && tfresult.Month == tftest.Month &&
+ tfresult.Day == tftest.Day && tfresult.Hour == tftest.Hour &&
+ tfresult.Minute == tftest.Minute && tfresult.Second == tftest.Second,
+ "#%d expected: %d-%d-%d %d:%d:%d got: %d-%d-%d %d:%d:%d\n", ++i,
+ tftest.Year, tftest.Month, tftest.Day,
+ tftest.Hour, tftest.Minute,tftest.Second,
+ tfresult.Year, tfresult.Month, tfresult.Day,
+ tfresult.Hour, tfresult.Minute, tfresult.Second);
+ /* test the inverse */
+ pRtlTimeFieldsToTime( &tfresult, &liresult);
+ ok( liresult.QuadPart == litime.QuadPart," TimeFieldsToTime failed on %d-%d-%d %d:%d:%d. Error is %d ticks\n",
+ tfresult.Year, tfresult.Month, tfresult.Day,
+ tfresult.Hour, tfresult.Minute, tfresult.Second,
+ (int) (liresult.QuadPart - litime.QuadPart) );
+ /* one second later is beginning of next month */
+ litime.QuadPart += TICKSPERSEC ;
+ pRtlTimeToTimeFields( &litime, &tfresult);
+ ok( tfresult.Year == tftest.Year + (tftest.Month ==12) &&
+ tfresult.Month == tftest.Month % 12 + 1 &&
+ tfresult.Day == 1 && tfresult.Hour == 0 &&
+ tfresult.Minute == 0 && tfresult.Second == 0,
+ "#%d expected: %d-%d-%d %d:%d:%d got: %d-%d-%d %d:%d:%d\n", ++i,
+ tftest.Year + (tftest.Month ==12),
+ tftest.Month % 12 + 1, 1, 0, 0, 0,
+ tfresult.Year, tfresult.Month, tfresult.Day,
+ tfresult.Hour, tfresult.Minute, tfresult.Second);
+ /* test the inverse */
+ pRtlTimeFieldsToTime( &tfresult, &liresult);
+ ok( liresult.QuadPart == litime.QuadPart," TimeFieldsToTime failed on %d-%d-%d %d:%d:%d. Error is %d ticks\n",
+ tfresult.Year, tfresult.Month, tfresult.Day,
+ tfresult.Hour, tfresult.Minute, tfresult.Second,
+ (int) (liresult.QuadPart - litime.QuadPart) );
+ /* advance to the end of the month */
+ litime.QuadPart -= TICKSPERSEC ;
+ if( tftest.Month == 12) {
+ tftest.Month = 1;
+ tftest.Year += 1;
+ } else
+ tftest.Month += 1;
+ tftest.Day = MonthLengths[IsLeapYear(tftest.Year)][tftest.Month - 1];
+ litime.QuadPart += (LONGLONG) tftest.Day * TICKSPERSEC * SECSPERDAY;
+ }
+}
+#endif
+
+START_TEST(time)
+{
+#ifdef __WINE_WINTERNL_H
+ HMODULE mod = GetModuleHandleA("ntdll.dll");
+ pRtlTimeToTimeFields = (void *)GetProcAddress(mod,"RtlTimeToTimeFields");
+ pRtlTimeFieldsToTime = (void *)GetProcAddress(mod,"RtlTimeFieldsToTime");
+ if (pRtlTimeToTimeFields && pRtlTimeFieldsToTime)
+ test_pRtlTimeToTimeFields();
+#endif
+}
--- /dev/null
+<module name="powrprof_winetest" type="win32cui" installbase="bin" installname="powrprof_winetest.exe" allowwarnings="true">
+ <include base="powrprof_winetest">.</include>
+ <define name="__USE_W32API" />
+ <define name="UNICODE" />
+ <define name="_UNICODE" />
+ <library>powrprof</library>
+ <library>ntdll</library>
+ <library>advapi32</library>
+ <library>kernel32</library>
+ <file>testlist.c</file>
+ <file>pwrprof.c</file>
+</module>
--- /dev/null
+#include<stdarg.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#define STANDALONE
+#include "wine/test.h"
+#include "winternl.h"
+#include "windef.h"
+#include "winbase.h"
+#include "powrprof.h"
+#include "assert.h"
+
+#include "wine/unicode.h"
+/*
+ LONG WINAPI RegOpenCurrentUser(REGSAM a,PHKEY b)
+ {
+ *b = HKEY_CURRENT_USER;
+ return ERROR_SUCCESS;
+ }
+ */
+unsigned int g_NumPwrSchemes = 0;
+unsigned int g_NumPwrSchemesEnumerated = 0;
+unsigned int g_ActivePwrScheme = 3;
+unsigned int g_TempPwrScheme = 99;
+
+typedef struct _PROCESSOR_POWER_INFORMATION {
+ ULONG Number;
+ ULONG MaxMhz;
+ ULONG CurrentMhz;
+ ULONG MhzLimit;
+ ULONG MaxIdleState;
+ ULONG CurrentIdleState;
+} PROCESSOR_POWER_INFORMATION,
+*PPROCESSOR_POWER_INFORMATION;
+
+POWER_POLICY g_PowerPolicy;
+
+static const WCHAR szMachPowerPoliciesSubKey[] = { 'S', 'O', 'F', 'T', 'W', 'A', 'R',
+ 'E', '\\', 'M', 'i', 'c', 'r', 'o', 's', 'o', 'f', 't', '\\', 'W', 'i', 'n', 'd',
+ 'o', 'w', 's', '\\', 'C', 'u', 'r', 'r', 'e', 'n', 't', 'V', 'e', 'r', 's', 'i',
+ 'o', 'n', '\\', 'C', 'o', 'n', 't', 'r', 'o', 'l', 's', ' ', 'F', 'o', 'l', 'd',
+ 'e', 'r', '\\', 'P', 'o', 'w', 'e', 'r', 'C', 'f', 'g', '\\', 'P', 'o', 'w', 'e',
+ 'r', 'P', 'o', 'l', 'i', 'c', 'i', 'e', 's', 0};
+
+static const WCHAR szTempPwrScheme[] = { '9', '9', 0 };
+
+ULONG DbgPrint(PCCH X,...)
+{
+ return (ULONG)NULL;
+}
+
+void test_CallNtPowerInformation(void)
+{
+ DWORD retval;
+ ADMINISTRATOR_POWER_POLICY apolicy;
+ ULONGLONG atime, ctime;
+ PROCESSOR_POWER_INFORMATION ppi, *pppi;
+ PROCESSOR_POWER_POLICY ppp;
+ SYSTEM_BATTERY_STATE sbs;
+ SYSTEM_POWER_CAPABILITIES spc;
+ SYSTEM_POWER_INFORMATION spi;
+ SYSTEM_POWER_POLICY spp;
+ HANDLE x=NULL;
+
+ /* AdministratorPowerPolicy tests */
+ retval = CallNtPowerInformation(AdministratorPowerPolicy, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(AdministratorPowerPolicy, 0, 0, &apolicy, sizeof(ADMINISTRATOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(AdministratorPowerPolicy, &apolicy, sizeof(ADMINISTRATOR_POWER_POLICY), 0, 0);
+ ok(retval != STATUS_PRIVILEGE_NOT_HELD, "Privileg not held!!!! more errors to expect");
+ ok(retval == STATUS_SUCCESS, "function expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* LastSleepTime tests */
+ retval = CallNtPowerInformation(LastSleepTime, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(LastSleepTime, &atime, sizeof(sizeof(ULONGLONG)), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(LastSleepTime, &atime, sizeof(ULONGLONG), &ctime, sizeof(ULONGLONG));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(LastSleepTime, 0, 0, &atime, sizeof(ULONGLONG));
+ ok(retval == STATUS_SUCCESS, "function expected STATUS_SUCCESS but got %d\n",(UINT)retval);
+
+ /* LastWakeTime tests */
+ retval = CallNtPowerInformation(LastWakeTime, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(LastWakeTime, &atime, sizeof(sizeof(ULONGLONG)), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(LastWakeTime, &atime, sizeof(ULONGLONG), &ctime, sizeof(ULONGLONG));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(LastWakeTime, 0, 0, &atime, sizeof(ULONGLONG));
+ ok(retval == STATUS_SUCCESS, "function expected STATUS_SUCCESS but got %d\n",(UINT)retval);
+
+ /* ProcessorInformation tests */
+ retval = CallNtPowerInformation(ProcessorInformation, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorInformation, 0, 0, &ppi, sizeof(PROCESSOR_POWER_INFORMATION));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorInformation, &ppi, sizeof(PROCESSOR_POWER_INFORMATION), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorInformation, &ppi, sizeof(PROCESSOR_POWER_INFORMATION), &ppi, sizeof(PROCESSOR_POWER_INFORMATION));
+
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorInformation, 0, 0, &pppi, sizeof(PPROCESSOR_POWER_INFORMATION));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorInformation, &pppi, sizeof(PPROCESSOR_POWER_INFORMATION), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorInformation, &pppi, sizeof(PPROCESSOR_POWER_INFORMATION), &pppi, sizeof(PPROCESSOR_POWER_INFORMATION));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* ProcessorPowerPolicyAc tests */
+ retval = CallNtPowerInformation(ProcessorPowerPolicyAc, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyAc, 0, 0, &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyAc, &ppp, sizeof(PROCESSOR_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyAc, &ppp, sizeof(PROCESSOR_POWER_POLICY), &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* ProcessorPowerPolicyCurrent tests */
+ retval = CallNtPowerInformation(ProcessorPowerPolicyCurrent, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyCurrent, 0, 0, &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyCurrent, &ppp, sizeof(PROCESSOR_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyCurrent, &ppp, sizeof(PROCESSOR_POWER_POLICY), &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+
+ /* ProcessorPowerPolicyDc tests */
+ retval = CallNtPowerInformation(ProcessorPowerPolicyDc, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyDc, 0, 0, &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyDc, &ppp, sizeof(PROCESSOR_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorPowerPolicyDc, &ppp, sizeof(PROCESSOR_POWER_POLICY), &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* SystemBatteryState tests */
+ retval = CallNtPowerInformation(SystemBatteryState, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemBatteryState, 0, 0, &sbs, sizeof(SYSTEM_BATTERY_STATE));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemBatteryState, &sbs, sizeof(SYSTEM_BATTERY_STATE), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemBatteryState, &sbs, sizeof(SYSTEM_BATTERY_STATE), &sbs, sizeof(SYSTEM_BATTERY_STATE));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* SystemExecutionState tests */
+ retval = CallNtPowerInformation(SystemExecutionState, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* SystemPowerCapabilities tests */
+ retval = CallNtPowerInformation(SystemPowerCapabilities, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerCapabilities, 0, 0, &spc, sizeof(SYSTEM_POWER_CAPABILITIES));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerCapabilities, &spc, sizeof(SYSTEM_POWER_CAPABILITIES), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerCapabilities, &spc, sizeof(SYSTEM_POWER_CAPABILITIES), &spc, sizeof(SYSTEM_POWER_CAPABILITIES));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+
+ /* SystemPowerInformation tests */
+ retval = CallNtPowerInformation(SystemPowerInformation, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerInformation, 0, 0, &spi, sizeof(SYSTEM_POWER_INFORMATION));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerInformation, &spi, sizeof(SYSTEM_POWER_INFORMATION), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerInformation, &spi, sizeof(SYSTEM_POWER_INFORMATION), &spi, sizeof(SYSTEM_POWER_INFORMATION));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerInformation, &spp, sizeof(SYSTEM_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerInformation, &spp, sizeof(SYSTEM_POWER_POLICY), &spi, sizeof(SYSTEM_POWER_INFORMATION));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* SystemPowerPolicyAc tests */
+ retval = CallNtPowerInformation(SystemPowerPolicyAc, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyAc, 0, 0, &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyAc, &spp, sizeof(SYSTEM_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyAc, &spp, sizeof(SYSTEM_POWER_POLICY), &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* SystemPowerPolicyCurrent tests */
+ retval = CallNtPowerInformation(SystemPowerPolicyCurrent, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyCurrent, 0, 0, &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyCurrent, &spp, sizeof(SYSTEM_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyCurrent, &spp, sizeof(SYSTEM_POWER_POLICY), &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+
+ /* SystemPowerPolicyDc tests */
+ retval = CallNtPowerInformation(SystemPowerPolicyDc, 0, 0, 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyDc, 0, 0, &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyDc, &spp, sizeof(SYSTEM_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerPolicyDc, &spp, sizeof(SYSTEM_POWER_POLICY), &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* SystemReserveHiberFile tests */
+/*
+ retval = CallNtPowerInformation(SystemReserveHiberFile, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %ld\n", retval);
+ bln=TRUE;
+ retval = CallNtPowerInformation(SystemReserveHiberFile, &bln, sizeof(bln), 0, 0);
+ ok(retval == STATUS_DISK_FULL, "function result wrong expected STATUS_DISK_FULL but got %ld\n", nret);
+ bln=FALSE;
+ retval = CallNtPowerInformation(SystemReserveHiberFile, &bln, sizeof(bln), 0, 0);
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %ld\n", nret);
+
+ bln2=TRUE;
+ nret = CallNtPowerInformation(SystemReserveHiberFile, 0, 0, &bln2, sizeof(bln2));
+ ok(nret == STATUS_DATATYPE_MISALIGNMENT, "function result wrong expected STATUS_DATATYPE_MISALIGNMENT but got %ld\n", nret);
+ bln2=FALSE;
+ nret = CallNtPowerInformation(SystemReserveHiberFile, 0, 0, &bln2, sizeof(bln2));
+ ok(nret == STATUS_DATATYPE_MISALIGNMENT, "function result wrong expected STATUS_DATATYPE_MISALIGNMENT but got %ld\n", nret);
+
+ bln=TRUE;
+ bln2=TRUE;
+ nret = CallNtPowerInformation(SystemReserveHiberFile, &bln, sizeof(bln), &bln2, sizeof(bln2));
+ ok(nret == STATUS_DATATYPE_MISALIGNMENT, "function result wrong expected STATUS_DATATYPE_MISALIGNMENT but got %ld\n", nret);
+ bln2=FALSE;
+ nret = CallNtPowerInformation(SystemReserveHiberFile, &bln, sizeof(bln), &bln2, sizeof(bln2));
+ ok(nret == STATUS_DATATYPE_MISALIGNMENT, "function result wrong expected STATUS_DATATYPE_MISALIGNMENT but got %ld\n", nret);
+ bln=FALSE;
+ bln2=TRUE;
+ nret = CallNtPowerInformation(SystemReserveHiberFile, &bln, sizeof(bln), &bln2, sizeof(bln2));
+ ok(nret == STATUS_DATATYPE_MISALIGNMENT, "function result wrong expected STATUS_DATATYPE_MISALIGNMENT but got %ld\n", nret);
+ bln2=FALSE;
+ nret = CallNtPowerInformation(SystemReserveHiberFile, &bln, sizeof(bln), &bln2, sizeof(bln2));
+ ok(nret == STATUS_DATATYPE_MISALIGNMENT, "function result wrong expected STATUS_DATATYPE_MISALIGNMENT but got %ld\n", nret);
+ */
+
+ /* VerifyProcessorPowerPolicyAc tests */
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyAc, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyAc, 0, 0, &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyAc, &ppp, sizeof(PROCESSOR_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyAc, &ppp, sizeof(PROCESSOR_POWER_POLICY), &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* VerifyProcessorPowerPolicyDc tests */
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyDc, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyDc, 0, 0, &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyDc, &ppp, sizeof(PROCESSOR_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifyProcessorPowerPolicyDc, &ppp, sizeof(PROCESSOR_POWER_POLICY), &ppp, sizeof(PROCESSOR_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* VerifySystemPolicyAc tests */
+ retval = CallNtPowerInformation(VerifySystemPolicyAc, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifySystemPolicyAc, 0, 0, &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifySystemPolicyAc, &spp, sizeof(SYSTEM_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifySystemPolicyAc, &spp, sizeof(SYSTEM_POWER_POLICY), &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* VerifySystemPolicyDc tests */
+ retval = CallNtPowerInformation(VerifySystemPolicyDc, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifySystemPolicyDc, 0, 0, &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifySystemPolicyDc, &spp, sizeof(SYSTEM_POWER_POLICY), 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(VerifySystemPolicyDc, &spp, sizeof(SYSTEM_POWER_POLICY), &spp, sizeof(SYSTEM_POWER_POLICY));
+ ok(retval == STATUS_SUCCESS, "function result wrong expected STATUS_SUCCESS but got %d\n", (UINT)retval);
+
+ /* SystemPowerStateHandler tests */
+ retval = CallNtPowerInformation(SystemPowerStateHandler, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerStateHandler, 0, 0, x, sizeof(HANDLE));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+
+ /* ProcessorStateHandler tests */
+ retval = CallNtPowerInformation(ProcessorStateHandler, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorStateHandler, 0, 0, x, sizeof(HANDLE));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+
+ /* ProcessorStateHandler2 tests */
+ retval = CallNtPowerInformation(ProcessorStateHandler2, 0, 0, 0, 0);
+ ok(retval == STATUS_ACCESS_DENIED, "function result wrong expected STATUS_ACCESS_DENIED but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(ProcessorStateHandler2, 0, 0, x, sizeof(HANDLE));
+ ok(retval == STATUS_ACCESS_DENIED, "function result wrong expected STATUS_ACCESS_DENIED but got %d\n", (UINT)retval);
+
+ /* SystemPowerStateNotifyHandler tests */
+ retval = CallNtPowerInformation(SystemPowerStateNotifyHandler, 0, 0, 0, 0);
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+ retval = CallNtPowerInformation(SystemPowerStateNotifyHandler, 0, 0, x, sizeof(HANDLE));
+ ok(retval == STATUS_INVALID_PARAMETER, "function result wrong expected STATUS_INVALID_PARAMETER but got %d\n", (UINT)retval);
+
+}
+
+/*
+ @implemented
+ */
+void test_CanUserWritePwrScheme(void)
+{
+ DWORD error, retval;
+
+ retval = CanUserWritePwrScheme();
+
+ error = GetLastError();
+
+ if (retval)
+ ok(retval, "function failed?");
+ else
+ ok(error == ERROR_ACCESS_DENIED, "function last error wrong expected ERROR_ACCESS_DENIED but got %d\n", (UINT)error);
+
+}
+BOOLEAN CALLBACK test_callback_EnumPwrScheme(UINT uiIndex, DWORD dwName, LPWSTR sName, DWORD dwDesc,
+ LPWSTR sDesc, PPOWER_POLICY pp,LPARAM lParam )
+{
+ ok(uiIndex == g_NumPwrSchemes, "expected power scheme index of %d but got %d\n", g_NumPwrSchemes, uiIndex);
+ g_NumPwrSchemes++;
+
+ ok(lParam == 0xDEADBEEF, "expected function lParam to be 0xDEADBEEF but got %d\n", (UINT)lParam);
+
+ return TRUE;
+}
+
+BOOLEAN CALLBACK test_callback_stop_EnumPwrScheme(UINT uiIndex, DWORD dwName, LPWSTR sName, DWORD dwDesc,
+ LPWSTR sDesc, PPOWER_POLICY pp,LPARAM lParam )
+{
+ ok((!uiIndex || g_NumPwrSchemesEnumerated + 1 == uiIndex), "expected power scheme %d but got %d\n",g_NumPwrSchemesEnumerated+1, uiIndex);
+ g_NumPwrSchemesEnumerated = uiIndex;
+
+ ok(uiIndex <= (UINT)lParam, "enumeration should have already been stopped at index %d current index %d\n", (UINT)lParam, uiIndex);
+ if (uiIndex == (UINT)lParam)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+void test_DeletePwrScheme(void)
+{
+ DWORD retval;
+ HKEY hSubKey = NULL;
+
+
+ /*
+ * try inexistant profile number, should fail
+ */
+
+ retval = DeletePwrScheme(0xFFFFFFFF);
+ ok(!retval, "function should have failed error %x\n",(UINT)GetLastError());
+
+ /*
+ * delete active power scheme, should fail
+ */
+
+ retval = GetActivePwrScheme(&g_ActivePwrScheme);
+ ok(retval, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ retval = DeletePwrScheme(g_ActivePwrScheme);
+ ok(!retval, "function should have failed\n");
+ ok(GetLastError() == ERROR_ACCESS_DENIED, "function should have failed with ERROR_ACCESS_DENIED but got %x\n", (UINT)GetLastError());
+
+ /*
+ * delete a temporarly created power scheme
+ */
+ retval = DeletePwrScheme(g_TempPwrScheme);
+ ok(retval, "function should have succeeded\n");
+
+/*
+ * clean up, delete illegal entry, witch was created for this test
+ */
+
+ if (RegOpenKeyW(HKEY_LOCAL_MACHINE, szMachPowerPoliciesSubKey, &hSubKey) == ERROR_SUCCESS)
+ {
+ if (RegDeleteKeyW(hSubKey, szTempPwrScheme) != STATUS_SUCCESS)
+ printf("failed to delete subkey %i (testentry)\n", g_TempPwrScheme);
+ RegCloseKey(hSubKey);
+ }
+
+}
+
+void test_EnumPwrSchemes(void)
+{
+ BOOLEAN retval;
+
+ /*
+ * test EnumPwrScheme with null pointer callback
+ */
+
+ retval = EnumPwrSchemes(0, 0);
+ ok(!retval, "function was expected to fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER but got %x\n",(UINT)GetLastError());
+
+ /*
+ * enumerate power schemes, should succeed
+ */
+
+ retval = EnumPwrSchemes(test_callback_EnumPwrScheme, 0xDEADBEEF);
+ ok(retval, "function was expected to succeed %d\n",retval);
+ ok(g_NumPwrSchemes, "Warning: no power schemes available\n");
+
+ /*
+ * stop enumeration after first power scheme
+ */
+
+ retval = EnumPwrSchemes(test_callback_stop_EnumPwrScheme, (LPARAM)0);
+ ok(!retval, "function was expected to false\n");
+
+ /*
+ * enumerate half of all avalailble profiles
+ */
+
+ g_NumPwrSchemesEnumerated = 0;
+ retval = EnumPwrSchemes(test_callback_stop_EnumPwrScheme, (LPARAM)g_NumPwrSchemes / 2);
+ ok(retval, "function was expected to succeed but got %i\n", (UINT)retval);
+ ok(g_NumPwrSchemesEnumerated == g_NumPwrSchemes / 2, "function did not enumerate requested num of profiles %d enumerated %d\n", g_NumPwrSchemes / 2, g_NumPwrSchemesEnumerated);
+
+
+}
+
+void test_GetSetActivePwrScheme(void)
+{
+ DWORD retval;
+ UINT current_scheme = 2;
+ UINT temp_scheme = 0;
+
+ /*
+ * read active power scheme
+ */
+
+ retval = GetActivePwrScheme(&g_ActivePwrScheme);
+
+ ok(retval, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+ ok(retval <= g_NumPwrSchemes, "expected index lower as power scheme count %d but got %d\n", g_NumPwrSchemes, g_ActivePwrScheme);
+
+ /*
+ * sets active power scheme to inexistant profile
+ * -> corrupts power scheme enumeration on Windows XP SP2
+ */
+ //corrupts registry
+ //retval = SetActivePwrScheme(0xFFFFFFFF, 0, 0);
+ //ok(!retval, "function was expected to fail");
+ //current_scheme = min(active_scheme+1, g_NumPwrSchemes-1);
+
+ /*
+ * sets the active power scheme to profile with index 0
+ */
+
+ retval = SetActivePwrScheme(current_scheme, 0, 0);
+ ok(retval, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ /*
+ * read back the active power scheme
+ */
+
+ retval = GetActivePwrScheme(&temp_scheme);
+ ok(retval, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+ ok(temp_scheme == current_scheme, "expected %d but got %d\n", (UINT)current_scheme, (UINT)temp_scheme);
+
+ /*
+ * restore previous active power scheme
+ */
+
+ retval = SetActivePwrScheme(g_ActivePwrScheme, 0, 0);
+ ok(retval, "Warning: failed to restore old active power scheme %d\n", (UINT)g_ActivePwrScheme);
+}
+
+void test_GetCurrentPowerPolicies(void)
+{
+ GLOBAL_POWER_POLICY gpp;
+ POWER_POLICY pp;
+ BOOLEAN ret;
+ UINT current_scheme = 2;
+
+ g_ActivePwrScheme=3;
+ ret = GetActivePwrScheme(&g_ActivePwrScheme);
+
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+ ret = SetActivePwrScheme(0, &gpp, 0);
+
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(0, 0, &pp);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(0, &gpp, &pp);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(current_scheme, &gpp, 0);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(current_scheme, 0, &pp);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(current_scheme, &gpp, &pp);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(g_ActivePwrScheme, 0, 0);
+ ok(ret, "Warning: failed to restore old active power scheme %d\n", (UINT)g_ActivePwrScheme);
+
+ ret = GetCurrentPowerPolicies(0,0);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = GetCurrentPowerPolicies(&gpp,0);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = GetCurrentPowerPolicies(0,&pp);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = GetCurrentPowerPolicies(&gpp,&pp);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+ ok(gpp.mach.Revision == 1,"Global Mach Revision was expected to be 1 got %i",(UINT)gpp.mach.Revision);
+ ok(gpp.user.Revision == 1,"Global User Revision was expected to be 1 got %i",(UINT)gpp.mach.Revision);
+ ok(pp.mach.Revision == 1,"Mach Revision was expected to be 1 got %i",(UINT)gpp.mach.Revision);
+ ok(pp.user.Revision == 1,"User Revision was expected to be 1 got %i",(UINT)gpp.mach.Revision);
+
+
+ ret = GetActivePwrScheme(&g_ActivePwrScheme);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(0, &gpp, 0);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(0, 0, &pp);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(0, &gpp, &pp);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(current_scheme, &gpp, 0);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(current_scheme, 0, &pp);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(current_scheme, &gpp, &pp);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+
+ ret = SetActivePwrScheme(g_ActivePwrScheme, 0, 0);
+ ok(ret, "Warning: failed to restore old active power scheme %d\n", (UINT)g_ActivePwrScheme);
+
+}
+
+void test_GetPwrCapabilities(void)
+{
+ SYSTEM_POWER_CAPABILITIES spc;
+ BOOLEAN ret;
+
+ ret = GetPwrCapabilities(0);
+ ok(!ret, "function was expected to fail\n");
+ if (!ret)
+ {
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,"function was expectet to return ERROR_INVALID_PARAMETER, but returns: %x\n",(UINT)GetLastError());
+ }
+ ret = GetPwrCapabilities(&spc);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+}
+
+void test_GetPwrDiskSpindownRange(void)
+{
+ DWORD retval;
+ UINT min = 0;
+ UINT max = 0;
+
+ /*
+ * invalid parameter checks
+ */
+
+ retval = GetPwrDiskSpindownRange(NULL, NULL);
+ ok(!retval, "function was expected to fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error ERROR_INVALID_PARAMETER but got %x\n", (UINT)GetLastError());
+
+ retval = GetPwrDiskSpindownRange(&max, NULL);
+ ok(!retval, "function was expected to fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error ERROR_INVALID_PARAMETER but got %x\n", (UINT)GetLastError());
+
+ retval = GetPwrDiskSpindownRange(NULL, &min);
+ ok(!retval, "function was expected to fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER, "expected error ERROR_INVALID_PARAMETER but got %x\n", (UINT)GetLastError());
+
+ /*
+ * read disk spindown range
+ */
+
+ retval = GetPwrDiskSpindownRange(&max, &min);
+ ok(retval, "function was expected to succeed error %x\n",(UINT)GetLastError());
+ ok(min <= max, "range mismatch min %d max %d\n",min, max);
+}
+
+void test_IsAdminOverrideActive(void)
+{
+ ADMINISTRATOR_POWER_POLICY app;
+ BOOLEAN ret;
+
+ ret = IsAdminOverrideActive(0);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ ret = IsAdminOverrideActive(&app);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ app.MinSleep = 0;
+ app.MaxSleep = 0;
+ app.MinVideoTimeout = 0;
+ app.MaxVideoTimeout = 0;
+ app.MinSpindownTimeout = 0;
+ app.MaxSpindownTimeout = 0;
+
+ ret = IsAdminOverrideActive(&app);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+ app.MinSleep = 1;
+ app.MaxSleep = 2;
+ app.MinVideoTimeout = 3;
+ app.MaxVideoTimeout = 4;
+ app.MinSpindownTimeout = 5;
+ app.MaxSpindownTimeout = 6;
+
+ ret = IsAdminOverrideActive(&app);
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+
+}
+
+void test_IsPwrHibernateAllowed(void)
+{
+/*
+ BOOLEAN ret;
+
+ ret = IsPwrHibernateAllowed();
+ ok(!ret, "function was expected to fail, error %x\n",(UINT)GetLastError());
+ */
+}
+
+void test_IsPwrShutdownAllowed(void)
+{
+/*
+ BOOLEAN ret;
+
+ ret = IsPwrShutdownAllowed();
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+ */
+}
+
+void test_IsPwrSuspendAllowed(void)
+{
+/*
+ BOOLEAN ret;
+
+ ret = IsPwrSuspendAllowed();
+ ok(ret, "function was expected to succed, error %x\n",(UINT)GetLastError());
+ */
+}
+
+void test_ReadGlobalPwrPolicy(void)
+{
+ GLOBAL_POWER_POLICY gpp;
+ BOOLEAN ret;
+
+ ret = ReadGlobalPwrPolicy(&gpp);
+ ok(ret, "function was expected to succeed, error %x\n",(UINT)GetLastError());
+ ok(gpp.mach.Revision == 1,"Global Mach Revision was expected to be 1 got %i",(UINT)gpp.mach.Revision);
+ ok(gpp.user.Revision == 1,"Global User Revision was expected to be 1 got %i",(UINT)gpp.mach.Revision);
+
+
+
+}
+
+void test_ReadProcessorPwrScheme(void)
+{
+ MACHINE_PROCESSOR_POWER_POLICY mppp;
+ BOOLEAN ret;
+ UINT i = 0;
+ DWORD err;
+
+ do
+ {
+ RtlZeroMemory(&mppp, sizeof(MACHINE_PROCESSOR_POWER_POLICY));
+ ret = ReadProcessorPwrScheme(i,&mppp);
+ if (ret)
+ {
+ ok(mppp.Revision == 1,"Main Revision was expected to be 1 got %i",(UINT)mppp.Revision);
+ ok(mppp.ProcessorPolicyAc.Revision == 1,"PowerAC Revision was expected to be 1 got %i",(UINT)mppp.ProcessorPolicyAc.Revision);
+ ok(mppp.ProcessorPolicyDc.Revision == 1,"PowerDC Revision was expected to be 1 got %i",(UINT)mppp.ProcessorPolicyDc.Revision);
+ }
+ else
+ {
+ err = GetLastError();
+ ok(err == 0,"Failed Error %x\n",(UINT)err);
+ return;
+ }
+ i++;
+ if (i == g_NumPwrSchemes)
+ return;
+ } while (TRUE);
+
+}
+
+void test_ReadPwrScheme(void)
+{
+ DWORD retval;
+
+ /*
+ * read power scheme with null pointer -> crashs on Windows XP SP2
+ */
+ //retval = ReadPwrScheme(0, NULL);
+ //ok(!retval, "function was expected to fail\n");
+ //ok(GetLastError() == STATUS_INVALID_PARAMETER, "expected error ... but got %x\n", GetLastError());
+
+ /*
+ * read a power scheme with an invalid index, leads to the creation of the key
+ * -> corrupts power scheme enumeration
+ */
+ //retval = ReadPwrScheme(0xFFFFFFFF, &powerPolicy);
+ //ok(!retval, "function was expected to fail\n");
+ //ok(GetLastError() == ERROR_ACCESS_DENIED, "expected error ERROR_ACCESS_DENIED but got %x\n", GetLastError());
+
+ /*
+ * read current active power scheme
+ */
+
+ retval = ReadPwrScheme(g_ActivePwrScheme, &g_PowerPolicy);
+ ok(retval, "function was expected to succeed error %x\n",(UINT)GetLastError());
+
+}
+
+void test_SetSuspendState(void)
+{
+// SetSuspendState(FALSE,FALSE,FALSE)
+}
+
+void test_ValidatePowerPolicies(void)
+{
+ GLOBAL_POWER_POLICY gpp;
+ POWER_POLICY pp;
+ BOOLEAN ret;
+
+ SetLastError(0);
+ ret = ValidatePowerPolicies(0,0);
+ ok(ret, "function was expected to succeed error %i\n",(UINT)GetLastError());
+
+ ret = ValidatePowerPolicies(&gpp,0);
+ ok(!ret, "function was expected to fail return %i\n",(UINT)ret);
+ ok(GetLastError() == ERROR_REVISION_MISMATCH,"function was expected to fail with ERROR_REVISION_MISMATCH(%i,%i), but error :%i\n",(UINT)gpp.user.Revision,(UINT)gpp.mach.Revision,(UINT)GetLastError());
+
+ ret = ValidatePowerPolicies(0,&pp);
+ ok(!ret, "function was expected to fail return %i\n",(UINT)ret);
+ ok(GetLastError() == ERROR_REVISION_MISMATCH,"function was expected to fail with ERROR_REVISION_MISMATCH, but error :%i\n",(UINT)GetLastError());
+
+ ret = ValidatePowerPolicies(&gpp,&pp);
+ ok(!ret, "function was expected to fail return %i\n",(UINT)ret);
+ ok(GetLastError() == ERROR_REVISION_MISMATCH,"function was expected to fail with ERROR_REVISION_MISMATCH, but error :%i\n",(UINT)GetLastError());
+
+ gpp.user.Revision = 1;
+ gpp.mach.Revision = 1;
+
+ ret = ValidatePowerPolicies(&gpp,0);
+ ok(!ret, "function was expected to fail return %i\n",(UINT)ret);
+ ok(GetLastError() == ERROR_INVALID_DATA,"function was expected to fail with ERROR_INVALID_DATA, but error :%i\n",(UINT)GetLastError());
+
+ gpp.mach.LidOpenWakeAc = PowerSystemWorking;
+ gpp.mach.LidOpenWakeDc = PowerSystemWorking;
+
+ ret = ValidatePowerPolicies(&gpp,0);
+ ok(!ret, "function was expected to fail return %i\n",(UINT)ret);
+ ok(GetLastError() == ERROR_INVALID_DATA,"function was expected to fail with ERROR_INVALID_DATA, but error :%i\n",(UINT)GetLastError());
+
+ gpp.user.PowerButtonAc.Action = PowerActionNone;
+ gpp.user.PowerButtonDc.Action = PowerActionNone;
+ gpp.user.SleepButtonAc.Action = PowerActionNone;
+ gpp.user.SleepButtonDc.Action = PowerActionNone;
+ gpp.user.LidCloseAc.Action = PowerActionNone;
+ gpp.user.LidCloseDc.Action = PowerActionNone;
+
+ gpp.user.DischargePolicy[0].Enable=FALSE;
+ gpp.user.DischargePolicy[1].Enable=FALSE;
+ gpp.user.DischargePolicy[2].Enable=FALSE;
+ gpp.user.DischargePolicy[3].Enable=FALSE;
+ gpp.user.DischargePolicy[4].Enable=FALSE;
+ ret = ValidatePowerPolicies(&gpp,0);
+ ok(ret, "function was expected to succeed return %i\n",(UINT)GetLastError());
+ if (!ret)
+ {
+ ok(GetLastError() == ERROR_INVALID_DATA,"function was expected to fail with ERROR_INVALID_DATA, but error :%i\n",(UINT)GetLastError());
+ }
+
+ pp.user.Revision = 1;
+ pp.mach.Revision = 1;
+
+ ret = ValidatePowerPolicies(0,&pp);
+ ok(!ret, "function was expected to fail return %i\n",(UINT)ret);
+ ok(GetLastError() == ERROR_INVALID_DATA || GetLastError() == ERROR_GEN_FAILURE,"function was expected to fail with ERROR_GEN_FAILURE or ERROR_INVALID_DATA, but error :%i\n",(UINT)GetLastError());
+
+ pp.mach.MinSleepAc = PowerSystemWorking;
+ pp.mach.MinSleepDc = PowerSystemWorking;
+ pp.mach.ReducedLatencySleepAc = PowerSystemWorking;
+ pp.mach.ReducedLatencySleepDc = PowerSystemWorking;
+ pp.mach.OverThrottledAc.Action = PowerActionNone;
+ pp.mach.OverThrottledDc.Action = PowerActionNone;
+
+ pp.user.IdleAc.Action = PowerActionWarmEject+1;
+ pp.user.IdleDc.Action = PowerActionNone-1;
+ pp.user.MaxSleepAc = PowerSystemMaximum+1;
+ pp.user.MaxSleepDc = PowerSystemUnspecified;
+
+ ret = ValidatePowerPolicies(0,&pp);
+ ok(!ret, "function was expected to fail return %i\n",(UINT)GetLastError());
+ if (!ret)
+ {
+ ok(GetLastError() == ERROR_INVALID_DATA,"function was expected to fail with ERROR_INVALID_DATA, but error :%i\n",(UINT)GetLastError());
+ }
+
+ pp.user.IdleAc.Action = PowerActionNone;
+ pp.user.IdleDc.Action = PowerActionNone;
+ pp.user.MaxSleepAc = PowerSystemWorking;
+ pp.user.MaxSleepDc = PowerSystemWorking;
+
+ ret = ValidatePowerPolicies(0,&pp);
+ ok(ret, "function was expected to succeed, error %i\n",(UINT)GetLastError());
+ if (!ret)
+ {
+ ok(GetLastError() == ERROR_GEN_FAILURE,"function was expected to fail with ERROR_GEN_FAILURE, but error :%i\n",(UINT)GetLastError());
+ }
+
+ ret = ValidatePowerPolicies(&gpp,&pp);
+ ok(ret, "function was expected to succeed, error %i\n",(UINT)GetLastError());
+ if (!ret)
+ {
+// ok(GetLastError() == ERROR_GEN_FAILURE,"function was expected to fail with ERROR_GEN_FAILURE, but error :%i\n",(UINT)GetLastError());
+ }
+
+
+ ret = GetCurrentPowerPolicies(&gpp,&pp);
+ ok(ret, "function was expected to succeed error %i\n",(UINT)GetLastError());
+
+ ret = ValidatePowerPolicies(&gpp,0);
+ ok(ret, "function was expected to succeed error %i\n",(UINT)GetLastError());
+
+ ret = ValidatePowerPolicies(0,&pp);
+ ok(ret, "function was expected to succeed error %i\n",(UINT)GetLastError());
+
+ ret = ValidatePowerPolicies(&gpp,&pp);
+ ok(ret, "function was expected to succeed error %i\n",(UINT)GetLastError());
+
+}
+
+void test_WriteGlobalPwrPolicy(void)
+{
+// WriteGlobalPwrPolicy(&gpp);
+}
+
+void test_WriteProcessorPwrScheme(void)
+{
+// WriteProcessorPwrScheme(0,&mppp);
+}
+
+void test_WritePwrScheme(void)
+{
+ DWORD retval;
+ static const WCHAR szTestSchemeName[] = {'P','o','w','r','p','r','o','f',0};
+ static const WCHAR szTestSchemeDesc[] = {'P','o','w','r','p','r','o','f',' ','S','c','h','e','m','e',0};
+
+ /*
+ * create a temporarly profile, will be deleted in test_DeletePwrScheme
+ */
+
+ retval = WritePwrScheme(&g_TempPwrScheme, (LPWSTR)szTestSchemeName, (LPWSTR)szTestSchemeDesc, &g_PowerPolicy);
+ ok(retval, "Warning: function should have succeeded\n");
+}
+
+void func_power(void)
+{
+ test_CallNtPowerInformation();
+ test_CanUserWritePwrScheme();
+ test_EnumPwrSchemes();
+ test_GetSetActivePwrScheme();
+ test_ReadPwrScheme();
+ test_WritePwrScheme();
+ test_DeletePwrScheme();
+ test_GetPwrDiskSpindownRange();
+
+ test_GetCurrentPowerPolicies();
+
+ test_GetPwrCapabilities();
+ test_IsAdminOverrideActive();
+ test_IsPwrHibernateAllowed();
+ test_IsPwrShutdownAllowed();
+ test_IsPwrSuspendAllowed();
+ test_ReadGlobalPwrPolicy();
+ test_ReadProcessorPwrScheme();
+ test_SetSuspendState();
+ test_ValidatePowerPolicies();
+ test_WriteGlobalPwrPolicy();
+ test_WriteProcessorPwrScheme();
+
+}
+
+void func_ros_init(void)
+{
+ HKEY hUser, hKeyPowrCfg, hKeyGlobalPowrPol, hKeyPowerPolicies, hKeytmp;
+ DWORD err;
+ GLOBAL_USER_POWER_POLICY gupp;
+ GLOBAL_MACHINE_POWER_POLICY gmpp;
+ USER_POWER_POLICY upp;
+ MACHINE_POWER_POLICY mpp;
+ MACHINE_PROCESSOR_POWER_POLICY mppp;
+ GLOBAL_POWER_POLICY gpp;
+ POWER_POLICY pp;
+
+ int i;
+
+ static const WCHAR szUserPowrCfgKey[] = { 'C', 'o', 'n', 't', 'r', 'o', 'l', ' ',
+ 'P', 'a', 'n', 'e', 'l', '\\', 'P', 'o', 'w', 'e', 'r', 'C', 'f', 'g', 0};
+
+ static const WCHAR szCurrentPowerPolicy[] = {'C', 'u', 'r', 'r', 'e', 'n', 't', 'P',
+ 'o', 'w', 'e', 'r', 'P', 'o', 'l', 'i', 'c', 'y', 0};
+ static const WCHAR szcpp[] = {'3', 0 };
+
+ static const WCHAR szGlobalPowerPolicy[] = { 'G', 'l', 'o', 'b', 'a', 'l', 'P', 'o',
+ 'w', 'e', 'r', 'P', 'o', 'l', 'i', 'c', 'y', 0};
+ static const WCHAR szPolicies[] = {'P', 'o', 'l', 'i', 'c', 'i', 'e', 's', 0};
+ static const WCHAR szPowerPolicies[] = { 'P', 'o', 'w', 'e', 'r', 'P', 'o', 'l', 'i',
+ 'c', 'i', 'e', 's', 0};
+
+ static const WCHAR szProcessorPolicies[] = { 'P', 'r', 'o', 'c', 'e', 's', 's', 'o', 'r',
+ 'P', 'o', 'l', 'i', 'c', 'i', 'e', 's', 0};
+
+ static const WCHAR szcName[] = {'N', 'a', 'm', 'e', 0};
+ static const WCHAR szcDescription[] = {'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', 0};
+
+ static WCHAR szName[] = {'N', 'a', 'm', 'e', '(', '0', ')', 0};
+ static WCHAR szDescription[] = {'D', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '(', '0', ')', 0};
+
+ static const WCHAR szMachPowrCfgKey[] = {'S', 'O', 'F', 'T', 'W', 'A', 'R', 'E', '\\', 'M', 'i',
+ 'c', 'r', 'o', 's', 'o', 'f', 't', '\\', 'W', 'i', 'n', 'd', 'o', 'w', 's', '\\', 'C', 'u',
+ 'r', 'r', 'e', 'n', 't', 'V', 'e', 'r', 's', 'i', 'o', 'n', '\\', 'C', 'o', 'n', 't', 'r',
+ 'o', 'l', 's', ' ', 'F', 'o', 'l', 'd', 'e', 'r', '\\', 'P', 'o', 'w', 'e', 'r', 'C', 'f', 'g', 0};
+
+ static const WCHAR szLastID[] = {'L', 'a', 's', 't', 'I', 'D', 0};
+ static const WCHAR szDiskSpinDownMax[] = {'D', 'i', 's', 'k', 'S', 'p', 'i', 'n', 'D', 'o', 'w', 'n', 'M', 'a', 'x', 0};
+ static const WCHAR szDiskSpinDownMin[] = {'D', 'i', 's', 'k', 'S', 'p', 'i', 'n', 'D', 'o', 'w', 'n', 'M', 'i', 'n', 0};
+
+ static const WCHAR szLastIDValue[] = {'5', 0};
+ static const WCHAR szDiskSpinDownMaxValue[] = {'3', '6', '0', '0', 0};
+ static const WCHAR szDiskSpinDownMinValue[] = {'3', 0};
+
+ WCHAR tmp[20];
+ /*
+ * Erstelle die Registry-struktur und Daten, welche dafür erforderlich ist damit diese Tests funktionieren
+ */
+
+ /*
+ * User
+ */
+ err = RegOpenCurrentUser(KEY_ALL_ACCESS,&hUser);
+ ok(err == ERROR_SUCCESS,"Öffnen des Aktuellen Users Fehlgeschlagen\n");
+ if (err == ERROR_SUCCESS)
+ {
+ err = RegCreateKey(hUser,szUserPowrCfgKey,&hKeyPowrCfg);
+ ok(err == ERROR_SUCCESS,"Create Key UserPowrCfg failed with error %i\n",(UINT)err);
+ ok(hKeyPowrCfg != NULL,"Erstellen des Eintrages Powercfg fehlgeschalgen\n");
+ err = RegSetValueExW(hKeyPowrCfg,szCurrentPowerPolicy,(DWORD)NULL,REG_SZ,(CONST BYTE *)szcpp,strlenW(szcpp)*sizeof(WCHAR));
+ ok(err == ERROR_SUCCESS,"Set Value CurrentPowerPolicy failed with error %i\n",(UINT)err);
+ err = RegCreateKey(hKeyPowrCfg,szGlobalPowerPolicy,&hKeyGlobalPowrPol);
+ ok(err == ERROR_SUCCESS,"Create Key GlobalPowerPolicy failed with error %i\n",(UINT)err);
+ gupp.Revision = 1;
+ gupp.PowerButtonAc.Action = PowerActionNone;
+ gupp.PowerButtonDc.Action = PowerActionNone;
+ gupp.SleepButtonAc.Action = PowerActionNone;
+ gupp.SleepButtonDc.Action = PowerActionNone;
+ gupp.LidCloseAc.Action = PowerActionNone;
+ gupp.LidCloseDc.Action = PowerActionNone;
+ for (i=0; i<NUM_DISCHARGE_POLICIES; i++)
+ {
+ gupp.DischargePolicy[0].Enable=FALSE;
+ }
+
+ err = RegSetValueExW(hKeyGlobalPowrPol,szPolicies,(DWORD)NULL,REG_BINARY,(CONST BYTE *)&gupp,sizeof(GLOBAL_USER_POWER_POLICY));
+ ok(err == ERROR_SUCCESS,"Set Value GlobalPowrPol failed with error %i\n",(UINT)err);
+ err = RegCloseKey(hKeyGlobalPowrPol);
+ ok(err == ERROR_SUCCESS,"Close Key GlobalPowrPol failed with error %i\n",(UINT)err);
+ err = RegCreateKey(hKeyPowrCfg,szPowerPolicies,&hKeyPowerPolicies);
+ ok(err == ERROR_SUCCESS,"Create Key PowerPolicies failed with error %i\n",(UINT)err);
+
+ upp.Revision = 1;
+ upp.IdleAc.Action = PowerActionNone;
+ upp.IdleDc.Action = PowerActionNone;
+ upp.MaxSleepAc = PowerSystemWorking;
+ upp.MaxSleepDc = PowerSystemWorking;
+ upp.VideoTimeoutAc = 0;
+ upp.VideoTimeoutDc = 0;
+ upp.SpindownTimeoutAc = 0;
+ upp.SpindownTimeoutDc = 0;
+
+ for (i = 0; i<6; i++)
+ {
+ _itow(i,tmp,10);
+ err = RegCreateKey(hKeyPowerPolicies,tmp,&hKeytmp);
+ ok(err == ERROR_SUCCESS,"Create Key PowerPolicies(%i) failed with error %i\n",i,(UINT)err);
+ szName[5]++;
+ szDescription[12]++;
+ err = RegSetValueExW(hKeytmp,szcName,(DWORD)NULL,REG_SZ,(CONST BYTE *)szName,strlenW(szName)*sizeof(WCHAR));
+ err = RegSetValueExW(hKeytmp,szcDescription,(DWORD)NULL,REG_SZ,(CONST BYTE *)szDescription,strlenW(szDescription)*sizeof(WCHAR));
+ err = RegSetValueExW(hKeytmp,szPolicies,(DWORD)NULL,REG_BINARY,(CONST BYTE *)&upp,sizeof(USER_POWER_POLICY));
+ err = RegCloseKey(hKeytmp);
+ }
+ err = RegCloseKey(hKeyPowerPolicies);
+ err = RegCloseKey(hKeyPowrCfg);
+ err = RegCloseKey(hUser);
+ }
+
+ /*
+ * Mach
+ */
+ err = RegCreateKey(HKEY_LOCAL_MACHINE,szMachPowrCfgKey,&hKeyPowrCfg);
+ ok(err == ERROR_SUCCESS,"Create Key MachPowrCfgKey failed with error %i\n",(UINT)err);
+ err = RegSetValueExW(hKeyPowrCfg,szLastID,(DWORD)NULL,REG_SZ,(CONST BYTE *)szLastIDValue,strlenW(szLastIDValue)*sizeof(WCHAR));
+ err = RegSetValueExW(hKeyPowrCfg,szDiskSpinDownMax,(DWORD)NULL,REG_SZ,(CONST BYTE *)szDiskSpinDownMaxValue,strlenW(szDiskSpinDownMaxValue)*sizeof(WCHAR));
+ err = RegSetValueExW(hKeyPowrCfg,szDiskSpinDownMin,(DWORD)NULL,REG_SZ,(CONST BYTE *)szDiskSpinDownMinValue,strlenW(szDiskSpinDownMinValue)*sizeof(WCHAR));
+
+ err = RegCreateKey(hKeyPowrCfg,szGlobalPowerPolicy,&hKeyGlobalPowrPol);
+ ok(err == ERROR_SUCCESS,"Create Key Mach GlobalPowerPolicy failed with error %i\n",(UINT)err);
+ gmpp.Revision = 1;
+ gmpp.LidOpenWakeAc = PowerSystemWorking;
+ gmpp.LidOpenWakeDc = PowerSystemWorking;
+ gmpp.BroadcastCapacityResolution=0;
+
+ err = RegSetValueExW(hKeyGlobalPowrPol,szPolicies,(DWORD)NULL,REG_BINARY,(CONST BYTE *)&gmpp,sizeof(GLOBAL_MACHINE_POWER_POLICY));
+
+ err = RegCloseKey(hKeyGlobalPowrPol);
+ err = RegCreateKey(hKeyPowrCfg,szPowerPolicies,&hKeyPowerPolicies);
+ ok(err == ERROR_SUCCESS,"Create Key Mach PowerPolicies failed with error %i\n",(UINT)err);
+
+ mpp.Revision = 1;
+ mpp.MinSleepAc = PowerSystemWorking;
+ mpp.MinSleepDc = PowerSystemWorking;
+ mpp.ReducedLatencySleepAc = PowerSystemWorking;
+ mpp.ReducedLatencySleepDc = PowerSystemWorking;
+ mpp.OverThrottledAc.Action = PowerActionNone;
+ mpp.OverThrottledDc.Action = PowerActionNone;
+ mpp.DozeS4TimeoutAc=0;
+ mpp.DozeS4TimeoutDc=0;
+
+ for (i = 0; i<6; i++)
+ {
+ _itow(i,tmp,10);
+ err = RegCreateKey(hKeyPowerPolicies,tmp,&hKeytmp);
+ ok(err == ERROR_SUCCESS,"Create Key Mach PowerPolicies(%i) failed with error %i\n",(UINT)i,(UINT)err);
+ err = RegSetValueExW(hKeytmp,szPolicies,(DWORD)NULL,REG_BINARY,(CONST BYTE *)&mpp,sizeof(MACHINE_POWER_POLICY));
+ err = RegCloseKey(hKeytmp);
+ }
+ err = RegCloseKey(hKeyPowerPolicies);
+
+ err = RegCreateKey(hKeyPowrCfg,szProcessorPolicies,&hKeyPowerPolicies);
+ ok(err == ERROR_SUCCESS,"Create Key Mach ProcessorPolicies failed with error %i\n",(UINT)err);
+
+ mppp.Revision = 1;
+ mppp.ProcessorPolicyAc.Revision = 1;
+ mppp.ProcessorPolicyDc.Revision = 1;
+
+ for (i = 0; i<6; i++)
+ {
+ _itow(i,tmp,10);
+ err = RegCreateKey(hKeyPowerPolicies,tmp,&hKeytmp);
+ ok(err == ERROR_SUCCESS,"Create Key Mach ProcessorPolicies(%i) failed with error %i\n",i,(UINT)err);
+ err = RegSetValueExW(hKeytmp,szPolicies,(DWORD)NULL,REG_BINARY,(CONST BYTE *)&mppp,sizeof(MACHINE_PROCESSOR_POWER_POLICY));
+ err = RegCloseKey(hKeytmp);
+ }
+ err = RegCloseKey(hKeyPowerPolicies);
+
+ err = RegCloseKey(hKeyPowrCfg);
+
+ err = GetCurrentPowerPolicies(&gpp,&pp);
+ ok(err, "function was expected to succeed error %i\n",(UINT)GetLastError());
+
+ err = ValidatePowerPolicies(&gpp,&pp);
+ ok(err, "function was expected to succeed error %i\n",(UINT)GetLastError());
+
+ /*
+ [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Controls Folder\PowerCfg\GlobalPowerPolicy]
+ "CursorProperties"=...
+
+ */
+
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+/* stdarg.h is needed for Winelib */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "windef.h"
+#include "winbase.h"
+
+struct test
+{
+ const char *name;
+ void (*func)(void);
+};
+
+extern void func_power(void);
+extern void func_ros_init(void);
+
+const struct test winetest_testlist[] =
+{
+ { "power", func_power },
+ { "ros_init", func_ros_init },
+ { 0, 0 }
+};
+
+#define WINETEST_WANT_MAIN
+#include "wine/test.h"
--- /dev/null
+<module name="psapi_winetest" type="win32cui" installbase="bin" installname="psapi_winetest.exe" allowwarnings="true">
+ <include base="psapi_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <library>psapi</library>
+ <file>testlist.c</file>
+ <file>psapi_main.c</file>
+</module>
--- /dev/null
+/*
+ * Unit test suite for PSAPI
+ *
+ * Copyright (C) 2005 Felix Nawothnig
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windows.h"
+#include "psapi.h"
+
+#define PSAPI_GET_PROC(func) \
+ p ## func = (void*)GetProcAddress(hpsapi, #func); \
+ if(!p ## func) { \
+ ok(0, "GetProcAddress(%s) failed\n", #func); \
+ FreeLibrary(hpsapi); \
+ return FALSE; \
+ }
+
+/* All PSAPI functions return non-zero and call SetLastError()
+ * on failure so we can use some macros for convenience */
+
+#define w32_suc(x) \
+ (SetLastError(0xdeadbeef), \
+ (x) \
+ ? (ok(1, "succeeded\n"), 1) \
+ : GetLastError() == 0xdeadbeef \
+ ? (ok(0, "failed without error code\n"), 0) \
+ : (ok(0, "failed with %ld\n", GetLastError()), 0))
+
+#define w32_err(x, e) \
+ (SetLastError(0xdeadbeef), \
+ (x) \
+ ? (ok(0, "expected error=%d but succeeded\n", e), 0) \
+ : GetLastError() == e \
+ ? (ok(1, "failed with %d\n", e), 1) \
+ : GetLastError() == 0xdeadbeef \
+ ? (ok(0, "failed without error code\n"), 0) \
+ : (ok(0, "expected error=%d but failed with %ld\n", \
+ e, GetLastError()), 0))
+
+static BOOL (WINAPI *pEmptyWorkingSet)(HANDLE);
+static BOOL (WINAPI *pEnumProcesses)(DWORD*, DWORD, DWORD*);
+static BOOL (WINAPI *pEnumProcessModules)(HANDLE, HMODULE*, DWORD, LPDWORD);
+static DWORD (WINAPI *pGetModuleBaseNameA)(HANDLE, HMODULE, LPSTR, DWORD);
+static DWORD (WINAPI *pGetModuleFileNameExA)(HANDLE, HMODULE, LPSTR, DWORD);
+static BOOL (WINAPI *pGetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
+static DWORD (WINAPI *pGetMappedFileNameA)(HANDLE, LPVOID, LPSTR, DWORD);
+static DWORD (WINAPI *pGetProcessImageFileNameA)(HANDLE, LPSTR, DWORD);
+static BOOL (WINAPI *pGetProcessMemoryInfo)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
+static BOOL (WINAPI *pGetWsChanges)(HANDLE, PPSAPI_WS_WATCH_INFORMATION, DWORD);
+static BOOL (WINAPI *pInitializeProcessForWsWatch)(HANDLE);
+static BOOL (WINAPI *pQueryWorkingSet)(HANDLE, PVOID, DWORD);
+
+static BOOL InitFunctionPtrs(HMODULE hpsapi)
+{
+ PSAPI_GET_PROC(EmptyWorkingSet);
+ PSAPI_GET_PROC(EnumProcessModules);
+ PSAPI_GET_PROC(EnumProcesses);
+ PSAPI_GET_PROC(GetModuleBaseNameA);
+ PSAPI_GET_PROC(GetModuleFileNameExA);
+ PSAPI_GET_PROC(GetModuleInformation);
+ PSAPI_GET_PROC(GetMappedFileNameA);
+ PSAPI_GET_PROC(GetProcessMemoryInfo);
+ PSAPI_GET_PROC(GetWsChanges);
+ PSAPI_GET_PROC(InitializeProcessForWsWatch);
+ PSAPI_GET_PROC(QueryWorkingSet);
+ /* GetProcessImageFileName is not exported on NT4 */
+ pGetProcessImageFileNameA =
+ (void *)GetProcAddress(hpsapi, "GetProcessImageFileNameA");
+ return TRUE;
+}
+
+static HANDLE hpSR, hpQI, hpVR, hpQV, hpAA;
+static const HANDLE hBad = (HANDLE)0xdeadbeef;
+
+static void test_EnumProcesses(void)
+{
+ DWORD pid, cbUsed = 0xdeadbeef;
+
+ if(w32_suc(pEnumProcesses(NULL, 0, &cbUsed)))
+ ok(cbUsed == 0, "cbUsed=%ld\n", cbUsed);
+ if(w32_suc(pEnumProcesses(&pid, 4, &cbUsed)))
+ ok(cbUsed == 4, "cbUsed=%ld\n", cbUsed);
+}
+
+static void test_EnumProcessModules(void)
+{
+ HMODULE hMod = GetModuleHandle(NULL);
+ DWORD cbNeeded = 0xdeadbeef;
+
+ w32_err(pEnumProcessModules(NULL, NULL, 0, &cbNeeded), ERROR_INVALID_HANDLE);
+ w32_err(pEnumProcessModules(hpQI, NULL, 0, &cbNeeded), ERROR_ACCESS_DENIED);
+ w32_suc(pEnumProcessModules(hpQV, NULL, 0, &cbNeeded));
+ if(!w32_suc(pEnumProcessModules(hpQV, &hMod, sizeof(HMODULE), &cbNeeded)))
+ return;
+ ok(cbNeeded / sizeof(HMODULE) >= 3 && cbNeeded / sizeof(HMODULE) <= 5 * sizeof(HMODULE),
+ "cbNeeded=%ld\n", cbNeeded);
+ ok(hMod == GetModuleHandle(NULL),
+ "hMod=%p GetModuleHandle(NULL)=%p\n", hMod, GetModuleHandle(NULL));
+}
+
+static void test_GetModuleInformation(void)
+{
+ HMODULE hMod = GetModuleHandle(NULL);
+ MODULEINFO info;
+
+ w32_err(pGetModuleInformation(NULL, hMod, &info, sizeof(info)), ERROR_INVALID_HANDLE);
+ w32_err(pGetModuleInformation(hpQI, hMod, &info, sizeof(info)), ERROR_ACCESS_DENIED);
+ w32_err(pGetModuleInformation(hpQV, hBad, &info, sizeof(info)), ERROR_INVALID_HANDLE);
+ w32_err(pGetModuleInformation(hpQV, hMod, &info, sizeof(info)-1), ERROR_INSUFFICIENT_BUFFER);
+ if(w32_suc(pGetModuleInformation(hpQV, hMod, &info, sizeof(info))))
+ ok(info.lpBaseOfDll == hMod, "lpBaseOfDll=%p hMod=%p\n", info.lpBaseOfDll, hMod);
+}
+
+static void test_GetProcessMemoryInfo(void)
+{
+ PROCESS_MEMORY_COUNTERS pmc;
+
+ w32_err(pGetProcessMemoryInfo(NULL, &pmc, sizeof(pmc)), ERROR_INVALID_HANDLE);
+ todo_wine w32_err(pGetProcessMemoryInfo(hpSR, &pmc, sizeof(pmc)), ERROR_ACCESS_DENIED);
+ w32_err(pGetProcessMemoryInfo(hpQI, &pmc, sizeof(pmc)-1), ERROR_INSUFFICIENT_BUFFER);
+ w32_suc(pGetProcessMemoryInfo(hpQI, &pmc, sizeof(pmc)));
+}
+
+static void test_GetMappedFileName(void)
+{
+ HMODULE hMod = GetModuleHandle(NULL);
+ char szMapPath[MAX_PATH], szModPath[MAX_PATH], *szMapBaseName;
+ DWORD ret;
+
+ w32_err(pGetMappedFileNameA(NULL, hMod, szMapPath, sizeof(szMapPath)), ERROR_INVALID_HANDLE);
+ w32_err(pGetMappedFileNameA(hpSR, hMod, szMapPath, sizeof(szMapPath)), ERROR_ACCESS_DENIED);
+ if(!w32_suc(ret = pGetMappedFileNameA(hpQI, hMod, szMapPath, sizeof(szMapPath))))
+ return;
+ ok(ret == strlen(szMapPath), "szMapPath=\"%s\" ret=%ld\n", szMapPath, ret);
+ ok(szMapPath[0] == '\\', "szMapPath=\"%s\"\n", szMapPath);
+ szMapBaseName = strrchr(szMapPath, '\\'); /* That's close enough for us */
+ if(!szMapBaseName || !*szMapBaseName)
+ {
+ ok(0, "szMapPath=\"%s\"\n", szMapPath);
+ return;
+ }
+ GetModuleFileNameA(NULL, szModPath, sizeof(szModPath));
+ ok(!strcmp(strrchr(szModPath, '\\'), szMapBaseName),
+ "szModPath=\"%s\" szMapBaseName=\"%s\"\n", szModPath, szMapBaseName);
+}
+
+static void test_GetProcessImageFileName(void)
+{
+ HMODULE hMod = GetModuleHandle(NULL);
+ char szImgPath[MAX_PATH], szMapPath[MAX_PATH];
+ DWORD ret;
+
+ if(pGetProcessImageFileNameA == NULL)
+ return;
+
+ /* This function is available on WinXP+ only */
+ SetLastError(0xdeadbeef);
+ if(!pGetProcessImageFileNameA(hpQI, szImgPath, sizeof(szImgPath)))
+ {
+ if(GetLastError() == ERROR_INVALID_FUNCTION)
+ trace("GetProcessImageFileName not implemented\n");
+ else if(GetLastError() == 0xdeadbeef)
+ ok(0, "failed without error code\n");
+ else
+ ok(0, "failed with %ld\n", GetLastError());
+
+ return;
+ }
+
+ w32_err(pGetProcessImageFileNameA(NULL, szImgPath, sizeof(szImgPath)), ERROR_INVALID_HANDLE);
+ w32_err(pGetProcessImageFileNameA(hpSR, szImgPath, sizeof(szImgPath)), ERROR_ACCESS_DENIED);
+ w32_err(pGetProcessImageFileNameA(hpQI, szImgPath, 0), ERROR_INSUFFICIENT_BUFFER);
+ if(!w32_suc(ret = pGetProcessImageFileNameA(hpQI, szImgPath, sizeof(szImgPath))) ||
+ !w32_suc(pGetMappedFileNameA(hpQV, hMod, szMapPath, sizeof(szMapPath))))
+ return;
+ /* Windows returns 2*strlen-1 */
+ ok(ret >= strlen(szImgPath), "szImgPath=\"%s\" ret=%ld\n", szImgPath, ret);
+ ok(!strcmp(szImgPath, szMapPath),
+ "szImgPath=\"%s\" szMapPath=\"%s\"\n", szImgPath, szMapPath);
+}
+
+static void test_GetModuleFileNameEx(void)
+{
+ HMODULE hMod = GetModuleHandle(NULL);
+ char szModExPath[MAX_PATH+1], szModPath[MAX_PATH+1];
+ DWORD ret;
+
+ w32_err(pGetModuleFileNameExA(NULL, hMod, szModExPath, sizeof(szModExPath)), ERROR_INVALID_HANDLE);
+ w32_err(pGetModuleFileNameExA(hpQI, hMod, szModExPath, sizeof(szModExPath)), ERROR_ACCESS_DENIED);
+ w32_err(pGetModuleFileNameExA(hpQV, hBad, szModExPath, sizeof(szModExPath)), ERROR_INVALID_HANDLE);
+ if(!w32_suc(ret = pGetModuleFileNameExA(hpQV, NULL, szModExPath, sizeof(szModExPath))))
+ return;
+ ok(ret == strlen(szModExPath), "szModExPath=\"%s\" ret=%ld\n", szModExPath, ret);
+ GetModuleFileNameA(NULL, szModPath, sizeof(szModPath));
+ ok(!strncmp(szModExPath, szModPath, MAX_PATH),
+ "szModExPath=\"%s\" szModPath=\"%s\"\n", szModExPath, szModPath);
+}
+
+static void test_GetModuleBaseName(void)
+{
+ HMODULE hMod = GetModuleHandle(NULL);
+ char szModPath[MAX_PATH], szModBaseName[MAX_PATH];
+ DWORD ret;
+
+ w32_err(pGetModuleBaseNameA(NULL, hMod, szModBaseName, sizeof(szModBaseName)), ERROR_INVALID_HANDLE);
+ w32_err(pGetModuleBaseNameA(hpQI, hMod, szModBaseName, sizeof(szModBaseName)), ERROR_ACCESS_DENIED);
+ w32_err(pGetModuleBaseNameA(hpQV, hBad, szModBaseName, sizeof(szModBaseName)), ERROR_INVALID_HANDLE);
+ if(!w32_suc(ret = pGetModuleBaseNameA(hpQV, NULL, szModBaseName, sizeof(szModBaseName))))
+ return;
+ ok(ret == strlen(szModBaseName), "szModBaseName=\"%s\" ret=%ld\n", szModBaseName, ret);
+ GetModuleFileNameA(NULL, szModPath, sizeof(szModPath));
+ ok(!strcmp(strrchr(szModPath, '\\') + 1, szModBaseName),
+ "szModPath=\"%s\" szModBaseName=\"%s\"\n", szModPath, szModBaseName);
+}
+
+static void test_ws_functions(void)
+{
+ PSAPI_WS_WATCH_INFORMATION wswi[4096];
+ ULONG_PTR pages[4096];
+ char *addr;
+ int i;
+
+ todo_wine w32_err(pEmptyWorkingSet(NULL), ERROR_INVALID_HANDLE);
+ todo_wine w32_err(pEmptyWorkingSet(hpSR), ERROR_ACCESS_DENIED);
+ w32_suc(pEmptyWorkingSet(hpAA));
+
+ todo_wine w32_err(pInitializeProcessForWsWatch(NULL), ERROR_INVALID_HANDLE);
+ w32_suc(pInitializeProcessForWsWatch(hpAA));
+
+ if(!w32_suc(addr = VirtualAlloc(NULL, 1, MEM_COMMIT, PAGE_READWRITE)))
+ return;
+
+ if(!VirtualLock(addr, 1))
+ {
+ trace("locking failed (error=%ld) - skipping test\n", GetLastError());
+ goto free_page;
+ }
+
+ todo_wine if(w32_suc(pQueryWorkingSet(hpQI, pages, 4096 * sizeof(ULONG_PTR))))
+ {
+ for(i = 0; i < pages[0]; i++)
+ if((pages[i+1] & ~0xfffL) == (ULONG_PTR)addr)
+ {
+ ok(1, "QueryWorkingSet found our page\n");
+ goto test_gwsc;
+ }
+
+ ok(0, "QueryWorkingSet didn't find our page\n");
+ }
+
+test_gwsc:
+ todo_wine if(w32_suc(pGetWsChanges(hpQI, wswi, sizeof(wswi))))
+ {
+ for(i = 0; wswi[i].FaultingVa; i++)
+ if(((ULONG_PTR)wswi[i].FaultingVa & ~0xfffL) == (ULONG_PTR)addr)
+ {
+ ok(1, "GetWsChanges found our page\n");
+ goto free_page;
+ }
+
+ ok(0, "GetWsChanges didn't find our page\n");
+ }
+
+free_page:
+ VirtualFree(addr, 0, MEM_RELEASE);
+}
+
+START_TEST(psapi_main)
+{
+ HMODULE hpsapi = LoadLibraryA("psapi.dll");
+
+ if(!hpsapi)
+ {
+ trace("Could not load psapi.dll\n");
+ return;
+ }
+
+ if(InitFunctionPtrs(hpsapi))
+ {
+ DWORD pid = GetCurrentProcessId();
+
+ w32_suc(hpSR = OpenProcess(STANDARD_RIGHTS_REQUIRED, FALSE, pid));
+ w32_suc(hpQI = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
+ w32_suc(hpVR = OpenProcess(PROCESS_VM_READ, FALSE, pid));
+ w32_suc(hpQV = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
+ w32_suc(hpAA = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid));
+ if(hpSR && hpQI && hpVR && hpQV && hpAA)
+ {
+ test_EnumProcesses();
+ test_EnumProcessModules();
+ test_GetModuleInformation();
+ test_GetProcessMemoryInfo();
+ todo_wine test_GetMappedFileName();
+ todo_wine test_GetProcessImageFileName();
+ test_GetModuleFileNameEx();
+ test_GetModuleBaseName();
+ test_ws_functions();
+ }
+ CloseHandle(hpSR);
+ CloseHandle(hpQI);
+ CloseHandle(hpVR);
+ CloseHandle(hpQV);
+ CloseHandle(hpAA);
+ }
+
+ FreeLibrary(hpsapi);
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_psapi_main(void);
+
+const struct test winetest_testlist[] =
+{
+ { "psapi_main", func_psapi_main },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * SetupAPI device class-related functions tests
+ *
+ * Copyright 2006 Hervé Poussineau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "cfgmgr32.h"
+#include "setupapi.h"
+
+#include "wine/test.h"
+
+static GUID test_class_guid = { 0x4d36e967, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } };
+static char test_class_name[MAX_CLASS_NAME_LEN] = "DiskDrive";
+
+static const char *debugstr_guid(const GUID *guid)
+{
+ static char guidSTR1[39];
+ static char guidSTR2[39];
+ char* guidSTR;
+ static BOOL index;
+
+ if (!guid) return NULL;
+
+ index = !index;
+ guidSTR = index ? guidSTR1 : guidSTR2;
+
+ snprintf(guidSTR, sizeof(guidSTR1),
+ "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ guid->Data1, guid->Data2, guid->Data3,
+ guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+ guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+ return guidSTR;
+}
+
+static void test_SetupDiBuildClassInfoList(void)
+{
+ LPGUID guid_list = NULL;
+ DWORD required_size, size;
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiBuildClassInfoList( 0, NULL, 0, NULL ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected error %lx, got %lx", ERROR_INVALID_PARAMETER, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiBuildClassInfoList( 0, NULL, 0, &required_size ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Expected error %lx, got %lx", ERROR_INSUFFICIENT_BUFFER, GetLastError() );
+
+ guid_list = HeapAlloc( GetProcessHeap(), 0, ( required_size + 1 ) * sizeof( GUID ) );
+ if ( !guid_list )
+ return;
+
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiBuildClassInfoList( 0, guid_list, required_size, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiBuildClassInfoList( 0, guid_list, required_size + 1, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+
+ if ( size > 0 )
+ {
+ /* That's better to use the first class found, as we know for sure that it exists */
+ memcpy(&test_class_guid, &guid_list[0], sizeof( GUID ) );
+ SetupDiClassNameFromGuidA( &test_class_guid, test_class_name, sizeof( test_class_name ), NULL );
+ }
+ HeapFree( GetProcessHeap(), 0, guid_list );
+}
+
+static void test_SetupDiClassGuidsFromNameA(void)
+{
+ LPGUID guid_list = NULL;
+ DWORD required_size, size;
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiClassGuidsFromNameA( NULL, NULL, 0, NULL ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected error %lx, got %lx", ERROR_INVALID_PARAMETER, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiClassGuidsFromNameA( NULL, NULL, 0, &required_size ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected error %lx, got %lx", ERROR_INVALID_PARAMETER, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiClassGuidsFromNameA( "", NULL, 0, &required_size ),
+ "Error reported %lx", GetLastError() );
+ ok( required_size == 0, "Expected 0, got %lu", required_size );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiClassGuidsFromNameA( test_class_name, NULL, 0, &required_size ),
+ "Fail expected" );
+ SetLastError( 0xdeadbeef );
+ ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Expected error %lx, got %lx", ERROR_INSUFFICIENT_BUFFER, GetLastError() );
+ ok( required_size > 0, "Expected > 0, got %lu", required_size );
+
+ guid_list = HeapAlloc( GetProcessHeap(), 0, ( required_size + 1 ) * sizeof( GUID ) );
+ if ( !guid_list )
+ return;
+
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiClassGuidsFromNameA( test_class_name, guid_list, required_size, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+ ok( IsEqualIID( &guid_list[0], &test_class_guid ),
+ "Expected %s, got %s", debugstr_guid( &test_class_guid ), debugstr_guid( &guid_list[0] ) );
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiClassGuidsFromNameA( test_class_name, guid_list, required_size + 1, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+ ok( IsEqualIID( &guid_list[0], &test_class_guid ),
+ "Expected %s, got %s", debugstr_guid( &test_class_guid ), debugstr_guid( &guid_list[0] ) );
+
+ HeapFree( GetProcessHeap(), 0, guid_list );
+}
+
+static void test_SetupDiClassNameFromGuidA(void)
+{
+ CHAR* class_name = NULL;
+ DWORD required_size, size;
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiClassNameFromGuidA( NULL, NULL, 0, NULL ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_CLASS,
+ "Expected error %x, got %lx", ERROR_INVALID_CLASS, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiClassNameFromGuidA( NULL, NULL, 0, &required_size ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_CLASS,
+ "Expected error %x, got %lx", ERROR_INVALID_CLASS, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiClassNameFromGuidA( &test_class_guid, NULL, 0, &required_size ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Expected error %lx, got %lx", ERROR_INSUFFICIENT_BUFFER, GetLastError() );
+ ok( required_size > 0, "Expected > 0, got %lu", required_size );
+ ok( required_size < MAX_CLASS_NAME_LEN, "Expected < %u, got %lu", MAX_CLASS_NAME_LEN, required_size );
+
+ class_name = HeapAlloc( GetProcessHeap(), 0, required_size );
+ if ( !class_name )
+ return;
+
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiClassNameFromGuidA( &test_class_guid, class_name, required_size, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+ ok( !strcmp( class_name, test_class_name ),
+ "Expected %s, got %s", test_class_name, class_name );
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiClassNameFromGuidA( &test_class_guid, class_name, required_size + 1, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+ ok( !strcmp( class_name, test_class_name ),
+ "Expected %s, got %s", test_class_name, class_name );
+
+ HeapFree( GetProcessHeap(), 0, class_name );
+}
+
+static void test_SetupDiGetClassDescriptionA(void)
+{
+ CHAR* class_desc = NULL;
+ DWORD required_size, size;
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiGetClassDescriptionA( NULL, NULL, 0, NULL ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected error %lx, got %lx", ERROR_INVALID_PARAMETER, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiGetClassDescriptionA( NULL, NULL, 0, &required_size ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected error %lx, got %lx", ERROR_INVALID_PARAMETER, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetupDiGetClassDescriptionA( &test_class_guid, NULL, 0, &required_size ),
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Expected error %lx, got %lx", ERROR_INSUFFICIENT_BUFFER, GetLastError() );
+ ok( required_size > 0, "Expected > 0, got %lu", required_size );
+ ok( required_size < LINE_LEN, "Expected < %u, got %lu", LINE_LEN, required_size );
+
+ class_desc = HeapAlloc( GetProcessHeap(), 0, required_size );
+ if ( !class_desc )
+ return;
+
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiGetClassDescriptionA( &test_class_guid, class_desc, required_size, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiGetClassDescriptionA( &test_class_guid, class_desc, required_size + 1, &size ),
+ "Error reported %lx", GetLastError() );
+ ok( size == required_size, "Expected size %lu, got %lu", required_size, size );
+
+ HeapFree( GetProcessHeap(), 0, class_desc );
+}
+
+static void test_SetupDiGetClassDevsA(void)
+{
+ HDEVINFO device_info;
+
+ SetLastError( 0xdeadbeef );
+ device_info = SetupDiGetClassDevs( NULL, NULL, NULL, 0 );
+ ok( device_info == INVALID_HANDLE_VALUE,
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected error %lx, got %lx", ERROR_INVALID_PARAMETER, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ device_info = SetupDiGetClassDevs( NULL, NULL, NULL, DIGCF_ALLCLASSES );
+ ok( device_info != INVALID_HANDLE_VALUE,
+ "Error reported %lx", GetLastError() );
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiDestroyDeviceInfoList( device_info ),
+ "Error reported %lx", GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ device_info = SetupDiGetClassDevs( NULL, NULL, NULL, DIGCF_DEVICEINTERFACE );
+ ok( device_info == INVALID_HANDLE_VALUE,
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected error %lx, got %lx", ERROR_INVALID_PARAMETER, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ device_info = SetupDiGetClassDevs( &test_class_guid, NULL, NULL, 0 );
+ ok( device_info != INVALID_HANDLE_VALUE,
+ "Error reported %lx", GetLastError() );
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiDestroyDeviceInfoList( device_info ),
+ "Error reported %lx", GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ device_info = SetupDiGetClassDevs( NULL, "(invalid enumerator)", NULL, DIGCF_ALLCLASSES );
+ ok( device_info == INVALID_HANDLE_VALUE,
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_DATA,
+ "Expected error %lx, got %lx", ERROR_INVALID_DATA, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ device_info = SetupDiGetClassDevs( NULL, "Root", NULL, DIGCF_ALLCLASSES );
+ ok( device_info != INVALID_HANDLE_VALUE,
+ "Error reported %lx", GetLastError() );
+ SetLastError( 0xdeadbeef );
+ ok( SetupDiDestroyDeviceInfoList( device_info ),
+ "Error reported %lx", GetLastError() );
+}
+
+static void test_SetupDiOpenClassRegKeyExA(void)
+{
+ HKEY hkey;
+ LONG err;
+
+ SetLastError( 0xdeadbeef );
+ hkey = SetupDiOpenClassRegKeyExA( NULL, 0, 0, NULL, NULL );
+ ok( hkey == INVALID_HANDLE_VALUE,
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_FLAGS,
+ "Expected error %lx, got %lx", ERROR_INVALID_FLAGS, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ hkey = SetupDiOpenClassRegKeyExA( NULL, 0, DIOCR_INSTALLER | DIOCR_INTERFACE, NULL, NULL );
+ ok( hkey == INVALID_HANDLE_VALUE,
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_FLAGS,
+ "Expected error %lx, got %lx", ERROR_INVALID_FLAGS, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ hkey = SetupDiOpenClassRegKeyExA( NULL, 0, DIOCR_INSTALLER, NULL, NULL );
+ ok( hkey == INVALID_HANDLE_VALUE,
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_CLASS,
+ "Expected error %x, got %lx", ERROR_INVALID_CLASS, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ hkey = SetupDiOpenClassRegKeyExA( NULL, 0, DIOCR_INTERFACE, NULL, NULL );
+ ok( hkey == INVALID_HANDLE_VALUE,
+ "Fail expected" );
+ ok( GetLastError() == ERROR_INVALID_CLASS,
+ "Expected error %x, got %lx", ERROR_INVALID_CLASS, GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ hkey = SetupDiOpenClassRegKeyExA( NULL, KEY_QUERY_VALUE, DIOCR_INSTALLER, NULL, NULL );
+ ok( hkey != INVALID_HANDLE_VALUE, "Got error %lx", GetLastError() );
+ err = RegCloseKey( hkey );
+ ok( err == ERROR_SUCCESS, "Got error %lx", err );
+
+ SetLastError( 0xdeadbeef );
+ hkey = SetupDiOpenClassRegKeyExA( NULL, KEY_QUERY_VALUE, DIOCR_INTERFACE, NULL, NULL );
+ ok( hkey != INVALID_HANDLE_VALUE, "Got error %lx", GetLastError() );
+ err = RegCloseKey( hkey );
+ ok( err == ERROR_SUCCESS, "Got error %lx", err );
+
+ SetLastError( 0xdeadbeef );
+ hkey = SetupDiOpenClassRegKeyExA( &test_class_guid, KEY_QUERY_VALUE, DIOCR_INSTALLER, NULL, NULL );
+ ok( hkey != INVALID_HANDLE_VALUE, "Got error %lx", GetLastError() );
+ err = RegCloseKey( hkey );
+ ok( err == ERROR_SUCCESS, "Got error %lx", err );
+
+ err = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Class", 0, KEY_SET_VALUE, &hkey);
+ ok( err == ERROR_SUCCESS, "Got error %lx", err );
+}
+
+START_TEST(devclass)
+{
+ test_SetupDiBuildClassInfoList();
+ test_SetupDiClassGuidsFromNameA();
+ test_SetupDiClassNameFromGuidA();
+ test_SetupDiGetClassDescriptionA();
+ test_SetupDiGetClassDevsA();
+ test_SetupDiOpenClassRegKeyExA();
+}
--- /dev/null
+/*
+ * INF file parsing tests
+ *
+ * Copyright 2006 Hervé Poussineau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "setupapi.h"
+
+#include "wine/test.h"
+
+#define TMPFILE ".\\tmp.inf"
+
+#define STD_HEADER "[Version]\r\nSignature=\"$CHICAGO$\"\r\n"
+
+/* create a new file with specified contents and open it */
+static HINF test_file_contents( const char *data, UINT *err_line )
+{
+ DWORD res;
+ HANDLE handle = CreateFileA( TMPFILE, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
+ if (handle == INVALID_HANDLE_VALUE) return 0;
+ if (!WriteFile( handle, data, strlen(data), &res, NULL )) trace( "write error\n" );
+ CloseHandle( handle );
+ return SetupOpenInfFileA( TMPFILE, 0, INF_STYLE_WIN4, err_line );
+}
+
+static void test_InstallHinfSectionA(void)
+{
+ char buffer[MAX_INF_STRING_LENGTH];
+ UINT err_line;
+ HINF hinf;
+ DWORD err;
+ DWORD len;
+
+ SetLastError( 0xdeadbeef );
+ hinf = test_file_contents( STD_HEADER
+ "[s]\r\nAddReg=s.Reg\r\n[s.Reg]\r\nHKCU,,Test,,none\r\n"
+ "[s.Win]\r\nAddReg=sWin.Reg\r\n[sWin.Reg]\r\nHKCU,,Test,,win\r\n"
+ "[s.NT]\r\nAddReg=sNT.Reg\r\n[sNT.Reg]\r\nHKCU,,Test,,nt\r\n"
+ , &err_line );
+ ok( hinf != INVALID_HANDLE_VALUE, "open failed err %lx", GetLastError() );
+ if ( hinf == INVALID_HANDLE_VALUE ) return;
+
+ system( "rundll32.exe setupapi,InstallHinfSection s 128 " TMPFILE );
+
+ len = sizeof( buffer );
+ err = RegQueryValueExA( HKEY_CURRENT_USER, "Test", NULL, NULL, (LPBYTE)buffer, &len );
+ ok( err == ERROR_SUCCESS, "error %lx", err);
+
+ if (GetVersion() & 0x80000000)
+ ok( !strcmp( buffer, "win" ), "bad section %s/win\n", buffer );
+ else
+ ok( !strcmp( buffer, "nt" ), "bad section %s/nt\n", buffer );
+
+ err = RegDeleteValue( HKEY_CURRENT_USER, "Test" );
+ ok( err == ERROR_SUCCESS, "error %lx", err);
+}
+
+static void test_SetupInstallFromInfSectionA(void)
+{
+ char buffer[MAX_INF_STRING_LENGTH];
+ UINT err_line;
+ HINF hinf;
+ DWORD err;
+ DWORD len;
+
+ SetLastError( 0xdeadbeef );
+ hinf = test_file_contents( STD_HEADER
+ "[s]\r\nAddReg=s.Reg\r\n[s.Reg]\r\nHKR,,Test,,none\r\n"
+ "[s.Win]\r\nAddReg=sWin.Reg\r\n[sWin.Reg]\r\nHKR,,Test,,win\r\n"
+ "[s.NT]\r\nAddReg=sNT.Reg\r\n[sNT.Reg]\r\nHKR,,Test,,nt\r\n"
+ , &err_line );
+ ok( hinf != INVALID_HANDLE_VALUE, "open failed err %lx", GetLastError() );
+ if (hinf == INVALID_HANDLE_VALUE) return;
+
+ SetLastError( 0xdeadbeef );
+ ok ( SetupInstallFromInfSectionA( NULL, hinf, "s", SPINST_REGISTRY, HKEY_CURRENT_USER, NULL, 0, NULL, NULL, NULL, NULL ),
+ "Error code set to %lx", GetLastError() );
+
+ len = sizeof( buffer );
+ err = RegQueryValueExA( HKEY_CURRENT_USER, "Test", NULL, NULL, (LPBYTE)buffer, &len );
+ ok( err == ERROR_SUCCESS, "error %lx", err);
+ ok( !strcmp( buffer, "none" ), "bad value %s/none", buffer );
+
+ err = RegDeleteValue( HKEY_CURRENT_USER, "Test" );
+ ok( err == ERROR_SUCCESS, "error %lx", err);
+}
+
+START_TEST(install)
+{
+ test_InstallHinfSectionA();
+ test_SetupInstallFromInfSectionA();
+ DeleteFileA( TMPFILE );
+}
--- /dev/null
+/*
+ * INF file parsing tests
+ *
+ * Copyright 2002, 2005 Alexandre Julliard for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "setupapi.h"
+
+#include "wine/test.h"
+
+static const char tmpfile[] = ".\\tmp.inf";
+
+/* some large strings */
+#define A255 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+#define A256 "a" A255
+#define A400 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
+ "aaaaaaaaaaaaaaaa" A256
+#define A511 A255 A256
+#define A4097 "a" A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256 A256
+
+#define STD_HEADER "[Version]\r\nSignature=\"$CHICAGO$\"\r\n"
+
+#define STR_SECTION "[Strings]\nfoo=aaa\nbar=bbb\nloop=%loop2%\nloop2=%loop%\n" \
+ "per%%cent=abcd\nper=1\ncent=2\n22=foo\n" \
+ "big=" A400 "\n" \
+ "verybig=" A400 A400 A400 "\n"
+
+/* create a new file with specified contents and open it */
+static HINF test_file_contents( const char *data, UINT *err_line )
+{
+ DWORD res;
+ HANDLE handle = CreateFileA( tmpfile, GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0 );
+ if (handle == INVALID_HANDLE_VALUE) return 0;
+ if (!WriteFile( handle, data, strlen(data), &res, NULL )) trace( "write error\n" );
+ CloseHandle( handle );
+ return SetupOpenInfFileA( tmpfile, 0, INF_STYLE_WIN4, err_line );
+}
+
+static const char *get_string_field( INFCONTEXT *context, DWORD index )
+{
+ static char buffer[MAX_INF_STRING_LENGTH+32];
+ if (SetupGetStringFieldA( context, index, buffer, sizeof(buffer), NULL )) return buffer;
+ return NULL;
+}
+
+static const char *get_line_text( INFCONTEXT *context )
+{
+ static char buffer[MAX_INF_STRING_LENGTH+32];
+ if (SetupGetLineTextA( context, 0, 0, 0, buffer, sizeof(buffer), NULL )) return buffer;
+ return NULL;
+}
+
+
+/* Test various valid/invalid file formats */
+
+static const struct
+{
+ const char *data;
+ DWORD error;
+ UINT err_line;
+ int todo;
+} invalid_files[] =
+{
+ /* file contents expected error (or 0) errline todo */
+ { "\r\n", ERROR_WRONG_INF_STYLE, 0, 0 },
+ { "abcd\r\n", ERROR_WRONG_INF_STYLE, 0, 1 },
+ { "[Version]\r\n", ERROR_WRONG_INF_STYLE, 0, 0 },
+ { "[Version]\nSignature=", ERROR_WRONG_INF_STYLE, 0, 0 },
+ { "[Version]\nSignature=foo", ERROR_WRONG_INF_STYLE, 0, 0 },
+ { "[version]\nsignature=$chicago$", 0, 0, 0 },
+ { "[VERSION]\nSIGNATURE=$CHICAGO$", 0, 0, 0 },
+ { "[Version]\nSignature=$chicago$,abcd", 0, 0, 0 },
+ { "[Version]\nabc=def\nSignature=$chicago$", 0, 0, 0 },
+ { "[Version]\nabc=def\n[Version]\nSignature=$chicago$", 0, 0, 0 },
+ { STD_HEADER, 0, 0, 0 },
+ { STD_HEADER "[]\r\n", 0, 0, 0 },
+ { STD_HEADER "]\r\n", 0, 0, 0 },
+ { STD_HEADER "[" A255 "]\r\n", 0, 0, 0 },
+ { STD_HEADER "[ab\r\n", ERROR_BAD_SECTION_NAME_LINE, 3, 0 },
+ { STD_HEADER "\n\n[ab\x1a]\n", ERROR_BAD_SECTION_NAME_LINE, 5, 0 },
+ { STD_HEADER "[" A256 "]\r\n", ERROR_SECTION_NAME_TOO_LONG, 3, 0 },
+ { "[abc]\n" STD_HEADER, 0, 0, 0 },
+ { "abc\r\n" STD_HEADER, ERROR_EXPECTED_SECTION_NAME, 1, 0 },
+ { ";\n;\nabc\r\n" STD_HEADER, ERROR_EXPECTED_SECTION_NAME, 3, 0 },
+ { ";\n;\nab\nab\n" STD_HEADER, ERROR_EXPECTED_SECTION_NAME, 3, 0 },
+ { ";aa\n;bb\n" STD_HEADER, 0, 0, 0 },
+ { STD_HEADER " [TestSection\x00] \n", ERROR_BAD_SECTION_NAME_LINE, 3, 0 },
+ { STD_HEADER " [Test\x00Section] \n", ERROR_BAD_SECTION_NAME_LINE, 3, 0 },
+ { STD_HEADER " [TestSection\x00] \n", ERROR_BAD_SECTION_NAME_LINE, 3, 0 },
+ { STD_HEADER " [Test\x00Section] \n", ERROR_BAD_SECTION_NAME_LINE, 3, 0 },
+};
+
+static void test_invalid_files(void)
+{
+ unsigned int i;
+ UINT err_line;
+ HINF hinf;
+ DWORD err;
+
+ for (i = 0; i < sizeof(invalid_files)/sizeof(invalid_files[0]); i++)
+ {
+ SetLastError( 0xdeadbeef );
+ err_line = 0xdeadbeef;
+ hinf = test_file_contents( invalid_files[i].data, &err_line );
+ err = GetLastError();
+ trace( "hinf=%p err=%lx line=%d\n", hinf, err, err_line );
+ if (invalid_files[i].error) /* should fail */
+ {
+ ok( hinf == INVALID_HANDLE_VALUE, "file %u: Open succeeded\n", i );
+ if (invalid_files[i].todo) todo_wine
+ {
+ ok( err == invalid_files[i].error, "file %u: Bad error %lx/%lx\n",
+ i, err, invalid_files[i].error );
+ ok( err_line == invalid_files[i].err_line, "file %u: Bad error line %d/%d\n",
+ i, err_line, invalid_files[i].err_line );
+ }
+ else
+ {
+ ok( err == invalid_files[i].error, "file %u: Bad error %lx/%lx\n",
+ i, err, invalid_files[i].error );
+ ok( err_line == invalid_files[i].err_line, "file %u: Bad error line %d/%d\n",
+ i, err_line, invalid_files[i].err_line );
+ }
+ }
+ else /* should succeed */
+ {
+ ok( hinf != INVALID_HANDLE_VALUE, "file %u: Open failed\n", i );
+ ok( err == 0, "file %u: Error code set to %lx\n", i, err );
+ }
+ if (hinf != INVALID_HANDLE_VALUE) SetupCloseInfFile( hinf );
+ }
+}
+
+
+/* Test various section names */
+
+static const struct
+{
+ const char *data;
+ const char *section;
+ DWORD error;
+} section_names[] =
+{
+ /* file contents section name error code */
+ { STD_HEADER "[TestSection]", "TestSection", 0 },
+ { STD_HEADER "[TestSection]\n", "TestSection", 0 },
+ { STD_HEADER "[TESTSECTION]\r\n", "TestSection", 0 },
+ { STD_HEADER "[TestSection]\n[abc]", "testsection", 0 },
+ { STD_HEADER ";[TestSection]\n", "TestSection", ERROR_SECTION_NOT_FOUND },
+ { STD_HEADER "[TestSection]\n", "Bad name", ERROR_SECTION_NOT_FOUND },
+ /* spaces */
+ { STD_HEADER "[TestSection] \r\n", "TestSection", 0 },
+ { STD_HEADER " [TestSection]\r\n", "TestSection", 0 },
+ { STD_HEADER " [TestSection] dummy\r\n", "TestSection", 0 },
+ { STD_HEADER " [TestSection] [foo]\r\n", "TestSection", 0 },
+ { STD_HEADER " [ Test Section ] dummy\r\n", " Test Section ", 0 },
+ { STD_HEADER "[TestSection] \032\ndummy", "TestSection", 0 },
+ { STD_HEADER "[TestSection] \n\032dummy", "TestSection", 0 },
+ /* special chars in section name */
+ { STD_HEADER "[Test[Section]\r\n", "Test[Section", 0 },
+ { STD_HEADER "[Test[S]ection]\r\n", "Test[S", 0 },
+ { STD_HEADER "[Test[[[Section]\r\n", "Test[[[Section", 0 },
+ { STD_HEADER "[]\r\n", "", 0 },
+ { STD_HEADER "[[[]\n", "[[", 0 },
+ { STD_HEADER "[Test\"Section]\r\n", "Test\"Section", 0 },
+ { STD_HEADER "[Test\\Section]\r\n", "Test\\Section", 0 },
+ { STD_HEADER "[Test\\ Section]\r\n", "Test\\ Section", 0 },
+ { STD_HEADER "[Test;Section]\r\n", "Test;Section", 0 },
+ /* various control chars */
+ { STD_HEADER " [Test\r\b\tSection] \n", "Test\r\b\tSection", 0 },
+ /* nulls */
+};
+
+static void test_section_names(void)
+{
+ unsigned int i;
+ UINT err_line;
+ HINF hinf;
+ DWORD err;
+ LONG ret;
+
+ for (i = 0; i < sizeof(section_names)/sizeof(section_names[0]); i++)
+ {
+ SetLastError( 0xdeadbeef );
+ hinf = test_file_contents( section_names[i].data, &err_line );
+ ok( hinf != INVALID_HANDLE_VALUE, "line %u: open failed err %lx\n", i, GetLastError() );
+ if (hinf == INVALID_HANDLE_VALUE) continue;
+
+ ret = SetupGetLineCountA( hinf, section_names[i].section );
+ err = GetLastError();
+ trace( "hinf=%p ret=%ld err=%lx\n", hinf, ret, err );
+ if (ret != -1)
+ {
+ ok( !section_names[i].error, "line %u: section name %s found\n",
+ i, section_names[i].section );
+ ok( !err, "line %u: bad error code %lx\n", i, err );
+ }
+ else
+ {
+ ok( section_names[i].error, "line %u: section name %s not found\n",
+ i, section_names[i].section );
+ ok( err == section_names[i].error, "line %u: bad error %lx/%lx\n",
+ i, err, section_names[i].error );
+ }
+ SetupCloseInfFile( hinf );
+ }
+}
+
+
+/* Test various key and value names */
+
+static const struct
+{
+ const char *data;
+ const char *key;
+ const char *fields[10];
+} key_names[] =
+{
+/* file contents expected key expected fields */
+ { "ab=cd", "ab", { "cd" } },
+ { "ab=cd,ef,gh,ij", "ab", { "cd", "ef", "gh", "ij" } },
+ { "ab", "ab", { "ab" } },
+ { "ab,cd", NULL, { "ab", "cd" } },
+ { "ab,cd=ef", NULL, { "ab", "cd=ef" } },
+ { "=abcd,ef", "", { "abcd", "ef" } },
+ /* backslashes */
+ { "ba\\\ncd=ef", "bacd", { "ef" } },
+ { "ab \\ \ncd=ef", "abcd", { "ef" } },
+ { "ab\\\ncd,ef", NULL, { "abcd", "ef" } },
+ { "ab \\ ;cc\ncd=ef", "abcd", { "ef" } },
+ { "ab \\ \\ \ncd=ef", "abcd", { "ef" } },
+ { "ba \\ dc=xx", "ba \\ dc", { "xx" } },
+ { "ba \\\\ \nc=d", "bac", { "d" } },
+ { "a=b\\\\c", "a", { "b\\\\c" } },
+ { "ab=cd \\ ", "ab", { "cd" } },
+ { "ba=c \\ \n \\ \n a", "ba", { "ca" } },
+ { "ba=c \\ \n \\ a", "ba", { "c\\ a" } },
+ { " \\ a= \\ b", "\\ a", { "\\ b" } },
+ /* quotes */
+ { "Ab\"Cd\"=Ef", "AbCd", { "Ef" } },
+ { "Ab\"Cd=Ef\"", "AbCd=Ef", { "AbCd=Ef" } },
+ { "ab\"\"\"cd,ef=gh\"", "ab\"cd,ef=gh", { "ab\"cd,ef=gh" } },
+ { "ab\"\"cd=ef", "abcd", { "ef" } },
+ { "ab\"\"cd=ef,gh", "abcd", { "ef", "gh" } },
+ { "ab=cd\"\"ef", "ab", { "cdef" } },
+ { "ab=cd\",\"ef", "ab", { "cd,ef" } },
+ { "ab=cd\",ef", "ab", { "cd,ef" } },
+ { "ab=cd\",ef\\\nab", "ab", { "cd,ef\\" } },
+ /* spaces */
+ { " a b = c , d \n", "a b", { "c", "d" } },
+ { " a b = c ,\" d\" \n", "a b", { "c", " d" } },
+ { " a b\r = c\r\n", "a b", { "c" } },
+ /* empty fields */
+ { "a=b,,,c,,,d", "a", { "b", "", "", "c", "", "", "d" } },
+ { "a=b,\"\",c,\" \",d", "a", { "b", "", "c", " ", "d" } },
+ { "=,,b", "", { "", "", "b" } },
+ { ",=,,b", NULL, { "", "=", "", "b" } },
+ { "a=\n", "a", { "" } },
+ { "=", "", { "" } },
+ /* eof */
+ { "ab=c\032d", "ab", { "c" } },
+ { "ab\032=cd", "ab", { "ab" } },
+ /* nulls */
+ { "abcd=ef\x0gh", "abcd", { "ef" } },
+ /* multiple sections with same name */
+ { "[Test2]\nab\n[Test]\nee=ff\n", "ee", { "ff" } },
+ /* string substitution */
+ { "%foo%=%bar%\n" STR_SECTION, "aaa", { "bbb" } },
+ { "%foo%xx=%bar%yy\n" STR_SECTION, "aaaxx", { "bbbyy" } },
+ { "%% %foo%=%bar%\n" STR_SECTION, "% aaa", { "bbb" } },
+ { "%f\"o\"o%=ccc\n" STR_SECTION, "aaa", { "ccc" } },
+ { "abc=%bar;bla%\n" STR_SECTION, "abc", { "%bar" } },
+ { "loop=%loop%\n" STR_SECTION, "loop", { "%loop2%" } },
+ { "%per%%cent%=100\n" STR_SECTION, "12", { "100" } },
+ { "a=%big%\n" STR_SECTION, "a", { A400 } },
+ { "a=%verybig%\n" STR_SECTION, "a", { A511 } }, /* truncated to 511 */
+ { "a=%big%%big%%big%%big%\n" STR_SECTION, "a", { A400 A400 A400 A400 } },
+ { "a=%big%%big%%big%%big%%big%%big%%big%%big%%big%\n" STR_SECTION, "a", { A400 A400 A400 A400 A400 A400 A400 A400 A400 } },
+ { "a=%big%%big%%big%%big%%big%%big%%big%%big%%big%%big%%big%\n" STR_SECTION, "a", { A4097 /*MAX_INF_STRING_LENGTH+1*/ } },
+};
+
+/* check the key of a certain line */
+static const char *check_key( INFCONTEXT *context, const char *wanted )
+{
+ const char *key = get_string_field( context, 0 );
+ DWORD err = GetLastError();
+
+ if (!key)
+ {
+ ok( !wanted, "missing key %s\n", wanted );
+ ok( err == 0 || err == ERROR_INVALID_PARAMETER, "last error set to %lx\n", err );
+ }
+ else
+ {
+ ok( !strcmp( key, wanted ), "bad key %s/%s\n", key, wanted );
+ ok( err == 0, "last error set to %lx\n", err );
+ }
+ return key;
+}
+
+static void test_key_names(void)
+{
+ char buffer[MAX_INF_STRING_LENGTH+32];
+ const char *key, *line;
+ unsigned int i, index, count;
+ UINT err_line;
+ HINF hinf;
+ DWORD err;
+ BOOL ret;
+ INFCONTEXT context;
+
+ for (i = 0; i < sizeof(key_names)/sizeof(key_names[0]); i++)
+ {
+ strcpy( buffer, STD_HEADER "[Test]\n" );
+ strcat( buffer, key_names[i].data );
+ SetLastError( 0xdeadbeef );
+ hinf = test_file_contents( buffer, &err_line );
+ ok( hinf != INVALID_HANDLE_VALUE, "line %u: open failed err %lx\n", i, GetLastError() );
+ if (hinf == INVALID_HANDLE_VALUE) continue;
+
+ ret = SetupFindFirstLineA( hinf, "Test", 0, &context );
+ assert( ret );
+
+ key = check_key( &context, key_names[i].key );
+
+ buffer[0] = buffer[1] = 0; /* build the full line */
+ for (index = 0; ; index++)
+ {
+ const char *field = get_string_field( &context, index + 1 );
+ err = GetLastError();
+ if (field)
+ {
+ ok( err == 0, "line %u: bad error %lx\n", i, GetLastError() );
+ if (key_names[i].fields[index])
+ ok( !strcmp( field, key_names[i].fields[index] ), "line %u: bad field %s/%s\n",
+ i, field, key_names[i].fields[index] );
+ else
+ ok( 0, "line %u: got extra field %s\n", i, field );
+ strcat( buffer, "," );
+ strcat( buffer, field );
+ }
+ else
+ {
+ ok( err == 0 || err == ERROR_INVALID_PARAMETER,
+ "line %u: bad error %lx\n", i, GetLastError() );
+ if (key_names[i].fields[index])
+ ok( 0, "line %u: missing field %s\n", i, key_names[i].fields[index] );
+ }
+ if (!key_names[i].fields[index]) break;
+ }
+ count = SetupGetFieldCount( &context );
+ ok( count == index, "line %u: bad count %d/%d\n", i, index, count );
+
+ line = get_line_text( &context );
+ ok( line != NULL, "line %u: SetupGetLineText failed\n", i );
+ if (line) ok( !strcmp( line, buffer+1 ), "line %u: bad text %s/%s\n", i, line, buffer+1 );
+
+ SetupCloseInfFile( hinf );
+ }
+
+}
+
+static void test_SetupCloseInfFile(void)
+{
+ /* try to close with invalid handles */
+ SetupCloseInfFile( NULL );
+ SetupCloseInfFile( INVALID_HANDLE_VALUE );
+}
+
+START_TEST(parser)
+{
+ test_invalid_files();
+ test_section_names();
+ test_key_names();
+ test_SetupCloseInfFile();
+ DeleteFileA( tmpfile );
+}
--- /dev/null
+/*
+ * Unit tests for setupapi.dll query functions
+ *
+ * Copyright (C) 2006 James Hawkins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdio.h>
+#include <windows.h>
+#include <setupapi.h>
+#include "wine/test.h"
+
+/* function pointers */
+static HMODULE hSetupAPI;
+static void (WINAPI *pSetupCloseInfFile)(HINF);
+static BOOL (WINAPI *pSetupGetInfInformationA)(LPCVOID,DWORD,PSP_INF_INFORMATION,DWORD,PDWORD);
+static HINF (WINAPI *pSetupOpenInfFileA)(PCSTR,PCSTR,DWORD,PUINT);
+static BOOL (WINAPI *pSetupQueryInfFileInformationA)(PSP_INF_INFORMATION,UINT,PSTR,DWORD,PDWORD);
+
+CHAR CURR_DIR[MAX_PATH];
+CHAR WIN_DIR[MAX_PATH];
+
+static void init_function_pointers(void)
+{
+ hSetupAPI = LoadLibraryA("setupapi.dll");
+
+ if (hSetupAPI)
+ {
+ pSetupCloseInfFile = (void *)GetProcAddress(hSetupAPI, "SetupCloseInfFile");
+ pSetupGetInfInformationA = (void *)GetProcAddress(hSetupAPI, "SetupGetInfInformationA");
+ pSetupOpenInfFileA = (void *)GetProcAddress(hSetupAPI, "SetupOpenInfFileA");
+ pSetupQueryInfFileInformationA = (void *)GetProcAddress(hSetupAPI, "SetupQueryInfFileInformationA");
+ }
+}
+
+static void get_directories(void)
+{
+ int len;
+
+ GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+ len = lstrlenA(CURR_DIR);
+
+ if(len && (CURR_DIR[len-1] == '\\'))
+ CURR_DIR[len-1] = 0;
+
+ GetWindowsDirectoryA(WIN_DIR, MAX_PATH);
+ len = lstrlenA(WIN_DIR);
+
+ if (len && (WIN_DIR[len-1] == '\\'))
+ WIN_DIR[len-1] = 0;
+}
+
+static void append_str(char **str, const char *data)
+{
+ sprintf(*str, data);
+ *str += strlen(*str);
+}
+
+static void create_inf_file(LPSTR filename)
+{
+ char data[1024];
+ char *ptr = data;
+ DWORD dwNumberOfBytesWritten;
+ HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ append_str(&ptr, "[Version]\n");
+ append_str(&ptr, "Signature=\"$Chicago$\"\n");
+ append_str(&ptr, "AdvancedINF=2.5\n");
+
+ WriteFile(hf, data, ptr - data, &dwNumberOfBytesWritten, NULL);
+ CloseHandle(hf);
+}
+
+static BOOL check_info_filename(PSP_INF_INFORMATION info, LPSTR test)
+{
+ LPSTR filename;
+ DWORD size;
+ BOOL ret = FALSE;
+
+ if (!pSetupQueryInfFileInformationA(info, 0, NULL, 0, &size))
+ return FALSE;
+
+ filename = HeapAlloc(GetProcessHeap(), 0, size);
+ if (!filename)
+ return FALSE;
+
+ pSetupQueryInfFileInformationA(info, 0, filename, size, &size);
+
+ if (!lstrcmpiA(test, filename))
+ ret = TRUE;
+
+ HeapFree(GetProcessHeap(), 0, filename);
+ return ret;
+}
+
+static PSP_INF_INFORMATION alloc_inf_info(LPSTR filename, DWORD search, PDWORD size)
+{
+ PSP_INF_INFORMATION info;
+ BOOL ret;
+
+ ret = pSetupGetInfInformationA(filename, search, NULL, 0, size);
+ if (!ret)
+ return NULL;
+
+ info = HeapAlloc(GetProcessHeap(), 0, *size);
+ return info;
+}
+
+static void test_SetupGetInfInformation(void)
+{
+ PSP_INF_INFORMATION info;
+ CHAR inf_filename[MAX_PATH];
+ CHAR inf_one[MAX_PATH], inf_two[MAX_PATH];
+ DWORD size;
+ HINF hinf;
+ BOOL ret;
+
+ lstrcpyA(inf_filename, CURR_DIR);
+ lstrcatA(inf_filename, "\\");
+ lstrcatA(inf_filename, "test.inf");
+
+ /* try an invalid inf handle */
+ size = 0xdeadbeef;
+ SetLastError(0xbeefcafe);
+ ret = pSetupGetInfInformationA(NULL, INFINFO_INF_SPEC_IS_HINF, NULL, 0, &size);
+ ok(ret == FALSE, "Expected SetupGetInfInformation to fail\n");
+ ok(GetLastError() == ERROR_INVALID_HANDLE,
+ "Expected ERROR_INVALID_HANDLE, got %ld\n", GetLastError());
+ ok(size == 0xdeadbeef, "Expected size to remain unchanged\n");
+
+ /* try an invalid inf filename */
+ size = 0xdeadbeef;
+ SetLastError(0xbeefcafe);
+ ret = pSetupGetInfInformationA(NULL, INFINFO_INF_NAME_IS_ABSOLUTE, NULL, 0, &size);
+ ok(ret == FALSE, "Expected SetupGetInfInformation to fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
+ ok(size == 0xdeadbeef, "Expected size to remain unchanged\n");
+
+ create_inf_file(inf_filename);
+
+ /* try an invalid search flag */
+ size = 0xdeadbeef;
+ SetLastError(0xbeefcafe);
+ ret = pSetupGetInfInformationA(inf_filename, -1, NULL, 0, &size);
+ ok(ret == FALSE, "Expected SetupGetInfInformation to fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
+ ok(size == 0xdeadbeef, "Expected size to remain unchanged\n");
+
+ /* try a nonexistent inf file */
+ size = 0xdeadbeef;
+ SetLastError(0xbeefcafe);
+ ret = pSetupGetInfInformationA("idontexist", INFINFO_INF_NAME_IS_ABSOLUTE, NULL, 0, &size);
+ ok(ret == FALSE, "Expected SetupGetInfInformation to fail\n");
+ ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+ "Expected ERROR_FILE_NOT_FOUND, got %ld\n", GetLastError());
+ ok(size == 0xdeadbeef, "Expected size to remain unchanged\n");
+
+ /* successfully open the inf file */
+ size = 0xdeadbeef;
+ ret = pSetupGetInfInformationA(inf_filename, INFINFO_INF_NAME_IS_ABSOLUTE, NULL, 0, &size);
+ ok(ret == TRUE, "Expected SetupGetInfInformation to succeed\n");
+ ok(size != 0xdeadbeef, "Expected a valid size on return\n");
+
+ /* set ReturnBuffer to NULL and ReturnBufferSize to non-zero */
+ SetLastError(0xbeefcafe);
+ ret = pSetupGetInfInformationA(inf_filename, INFINFO_INF_NAME_IS_ABSOLUTE, NULL, size, &size);
+ ok(ret == FALSE, "Expected SetupGetInfInformation to fail\n");
+ ok(GetLastError() == ERROR_INVALID_PARAMETER,
+ "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError());
+
+ info = HeapAlloc(GetProcessHeap(), 0, size);
+
+ /* try valid ReturnBuffer but too small size */
+ SetLastError(0xbeefcafe);
+ ret = pSetupGetInfInformationA(inf_filename, INFINFO_INF_NAME_IS_ABSOLUTE, info, size - 1, &size);
+ ok(ret == FALSE, "Expected SetupGetInfInformation to fail\n");
+ ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+ "Expected ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError());
+
+ /* successfully get the inf information */
+ ret = pSetupGetInfInformationA(inf_filename, INFINFO_INF_NAME_IS_ABSOLUTE, info, size, &size);
+ ok(ret == TRUE, "Expected SetupGetInfInformation to succeed\n");
+ ok(check_info_filename(info, inf_filename), "Expected returned filename to be equal\n");
+
+ HeapFree(GetProcessHeap(), 0, info);
+
+ /* try the INFINFO_INF_SPEC_IS_HINF search flag */
+ hinf = pSetupOpenInfFileA(inf_filename, NULL, INF_STYLE_WIN4, NULL);
+ info = alloc_inf_info(hinf, INFINFO_INF_SPEC_IS_HINF, &size);
+ ret = pSetupGetInfInformationA(hinf, INFINFO_INF_SPEC_IS_HINF, info, size, &size);
+ ok(ret == TRUE, "Expected SetupGetInfInformation to succeed\n");
+ ok(check_info_filename(info, inf_filename), "Expected returned filename to be equal\n");
+ pSetupCloseInfFile(hinf);
+
+ lstrcpyA(inf_one, WIN_DIR);
+ lstrcatA(inf_one, "\\inf\\");
+ lstrcatA(inf_one, "test.inf");
+ create_inf_file(inf_one);
+
+ lstrcpyA(inf_two, WIN_DIR);
+ lstrcatA(inf_two, "\\system32\\");
+ lstrcatA(inf_two, "test.inf");
+ create_inf_file(inf_two);
+
+ HeapFree(GetProcessHeap(), 0, info);
+ info = alloc_inf_info("test.inf", INFINFO_DEFAULT_SEARCH, &size);
+
+ /* test the INFINFO_DEFAULT_SEARCH search flag */
+ ret = pSetupGetInfInformationA("test.inf", INFINFO_DEFAULT_SEARCH, info, size, &size);
+ ok(ret == TRUE, "Expected SetupGetInfInformation to succeed\n");
+ ok(check_info_filename(info, inf_one), "Expected returned filename to be equal\n");
+
+ HeapFree(GetProcessHeap(), 0, info);
+ info = alloc_inf_info("test.inf", INFINFO_REVERSE_DEFAULT_SEARCH, &size);
+
+ /* test the INFINFO_REVERSE_DEFAULT_SEARCH search flag */
+ ret = pSetupGetInfInformationA("test.inf", INFINFO_REVERSE_DEFAULT_SEARCH, info, size, &size);
+ ok(ret == TRUE, "Expected SetupGetInfInformation to succeed\n");
+ ok(check_info_filename(info, inf_two), "Expected returned filename to be equal\n");
+
+ DeleteFileA(inf_filename);
+ DeleteFileA(inf_one);
+ DeleteFileA(inf_two);
+}
+
+START_TEST(query)
+{
+ init_function_pointers();
+ get_directories();
+
+ test_SetupGetInfInformation();
+}
--- /dev/null
+<module name="setupapi_winetest" type="win32cui" installbase="bin" installname="setupapi_winetest.exe" allowwarnings="true">
+ <include base="setupapi_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>ntdll</library>
+ <library>kernel32</library>
+ <library>advapi32</library>
+ <library>setupapi</library>
+ <file>devclass.c</file>
+ <file>install.c</file>
+ <file>parser.c</file>
+ <file>query.c</file>
+ <file>stringtable.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * Unit test suite for StringTable functions
+ *
+ * Copyright 2005 Steven Edwards for ReactOS
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+/*
+ * TODO:
+ * Add test for StringTableStringFromIdEx
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "setupapi.h"
+
+#include "wine/test.h"
+
+
+static DWORD (WINAPI *pStringTableAddString)(HSTRING_TABLE, LPWSTR, DWORD);
+static VOID (WINAPI *pStringTableDestroy)(HSTRING_TABLE);
+static HSTRING_TABLE (WINAPI *pStringTableDuplicate)(HSTRING_TABLE hStringTable);
+static HSTRING_TABLE (WINAPI *pStringTableInitialize)(VOID);
+static DWORD (WINAPI *pStringTableLookUpString)(HSTRING_TABLE, LPWSTR, DWORD);
+static LPWSTR (WINAPI *pStringTableStringFromId)(HSTRING_TABLE, DWORD);
+#if 0
+static BOOL (WINAPI *pStringTableStringFromIdEx)(HSTRING_TABLE, DWORD, LPWSTR, LPDWORD);
+static VOID (WINAPI *pStringTableTrim)(HSTRING_TABLE);
+#endif
+
+HMODULE hdll;
+static WCHAR string[] = {'s','t','r','i','n','g',0};
+static WCHAR String[] = {'S','t','r','i','n','g',0};
+static WCHAR foo[] = {'f','o','o',0};
+DWORD hstring, hString, hfoo; /* Handles pointing to our strings */
+HANDLE table, table2; /* Handles pointing to our tables */
+
+static void load_it_up(void)
+{
+ hdll = LoadLibraryA("setupapi.dll");
+ if (!hdll)
+ return;
+
+ pStringTableInitialize = (void*)GetProcAddress(hdll, "StringTableInitialize");
+ if (!pStringTableInitialize)
+ pStringTableInitialize = (void*)GetProcAddress(hdll, "pSetupStringTableInitialize");
+
+ pStringTableAddString = (void*)GetProcAddress(hdll, "StringTableAddString");
+ if (!pStringTableAddString)
+ pStringTableAddString = (void*)GetProcAddress(hdll, "pSetupStringTableAddString");
+
+ pStringTableDuplicate = (void*)GetProcAddress(hdll, "StringTableDuplicate");
+ if (!pStringTableDuplicate)
+ pStringTableDuplicate = (void*)GetProcAddress(hdll, "pSetupStringTableDuplicate");
+
+ pStringTableDestroy = (void*)GetProcAddress(hdll, "StringTableDestroy");
+ if (!pStringTableDestroy)
+ pStringTableDestroy = (void*)GetProcAddress(hdll, "pSetupStringTableDestroy");
+
+ pStringTableLookUpString = (void*)GetProcAddress(hdll, "StringTableLookUpString");
+ if (!pStringTableLookUpString)
+ pStringTableLookUpString = (void*)GetProcAddress(hdll, "pSetupStringTableLookUpString");
+
+ pStringTableStringFromId = (void*)GetProcAddress(hdll, "StringTableStringFromId");
+ if (!pStringTableStringFromId)
+ pStringTableStringFromId = (void*)GetProcAddress(hdll, "pSetupStringTableStringFromId");
+}
+
+static void test_StringTableInitialize(void)
+{
+ table=pStringTableInitialize();
+ ok(table!=NULL,"Failed to Initialize String Table\n");
+}
+
+static void test_StringTableAddString(void)
+{
+ DWORD retval;
+
+ /* case insensitive */
+ hstring=pStringTableAddString(table,string,0);
+ ok(hstring!=-1,"Failed to add string to String Table\n");
+
+ retval=pStringTableAddString(table,String,0);
+ ok(retval!=-1,"Failed to add String to String Table\n");
+ ok(hstring==retval,"string handle %lx != String handle %lx in String Table\n", hstring, retval);
+
+ hfoo=pStringTableAddString(table,foo,0);
+ ok(hfoo!=-1,"Failed to add foo to String Table\n");
+ ok(hfoo!=hstring,"foo and string share the same ID %lx in String Table\n", hfoo);
+
+ /* case sensitive */
+ hString=pStringTableAddString(table,String,ST_CASE_SENSITIVE_COMPARE);
+ ok(hstring!=hString,"String handle and string share same ID %lx in Table\n", hstring);
+}
+
+static void test_StringTableDuplicate(void)
+{
+ table2=pStringTableDuplicate(table);
+ ok(table2!=NULL,"Failed to duplicate String Table\n");
+}
+
+static void test_StringTableLookUpString(void)
+{
+ DWORD retval, retval2;
+
+ /* case insensitive */
+ retval=pStringTableLookUpString(table,string,0);
+ ok(retval!=-1,"Failed find string in String Table 1\n");
+ ok(retval==hstring,
+ "Lookup for string (%lx) does not match previous handle (%lx) in String Table 1\n",
+ retval, hstring);
+
+ retval=pStringTableLookUpString(table2,string,0);
+ ok(retval!=-1,"Failed find string in String Table 2\n");
+
+ retval=pStringTableLookUpString(table,String,0);
+ ok(retval!=-1,"Failed find String in String Table 1\n");
+
+ retval=pStringTableLookUpString(table2,String,0);
+ ok(retval!=-1,"Failed find String in String Table 2\n");
+
+ retval=pStringTableLookUpString(table,foo,0);
+ ok(retval!=-1,"Failed find foo in String Table 1\n");
+ ok(retval==hfoo,
+ "Lookup for foo (%lx) does not match previous handle (%lx) in String Table 1\n",
+ retval, hfoo);
+
+ retval=pStringTableLookUpString(table2,foo,0);
+ ok(retval!=-1,"Failed find foo in String Table 2\n");
+
+ /* case sensitive */
+ retval=pStringTableLookUpString(table,string,ST_CASE_SENSITIVE_COMPARE);
+ retval2=pStringTableLookUpString(table,String,ST_CASE_SENSITIVE_COMPARE);
+ ok(retval!=retval2,"Lookup of string equals String in Table 1\n");
+ ok(retval2==hString,
+ "Lookup for String (%lx) does not match previous handle (%lx) in String Table 1\n",
+ retval, hString);
+}
+
+static void test_StringTableStringFromId(void)
+{
+ WCHAR *string2, *string3;
+ int result;
+
+ /* correct */
+ string2=pStringTableStringFromId(table,pStringTableLookUpString(table,string,0));
+ ok(string2!=NULL,"Failed to look up string by ID from String Table\n");
+
+ result=lstrcmpiW(string, string2);
+ ok(result==0,"StringID %p does not match requested StringID %p\n",string,string2);
+
+ /* This should never work */
+ string3=pStringTableStringFromId(table,0);
+ ok(string3!=NULL,"Failed to look up string by ID from String Table\n");
+
+ result=lstrcmpiW(string, string3);
+ ok(result!=0,"StringID %p matches requested StringID %p\n",string,string3);
+}
+
+START_TEST(stringtable)
+{
+ load_it_up();
+
+ test_StringTableInitialize();
+ test_StringTableAddString();
+ test_StringTableDuplicate();
+ test_StringTableLookUpString();
+ test_StringTableStringFromId();
+
+ /* assume we can always distroy */
+ pStringTableDestroy(table);
+ pStringTableDestroy(table2);
+
+ FreeLibrary(hdll);
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_devclass(void);
+extern void func_install(void);
+extern void func_parser(void);
+extern void func_query(void);
+extern void func_stringtable(void);
+
+const struct test winetest_testlist[] =
+{
+ { "devclass", func_devclass },
+ { "install", func_install },
+ { "parser", func_parser },
+ { "query", func_query },
+ { "stringtable", func_stringtable },
+ { 0, 0 }
+};
--- /dev/null
+/* File generated automatically from tools/winapi/test.dat; do not edit! */
+/* This file can be copied, modified and distributed without restriction. */
+
+/*
+ * Unit tests for data structure packing
+ */
+
+#define WINVER 0x0501
+#define _WIN32_IE 0x0501
+#define _WIN32_WINNT 0x0501
+
+#define WINE_NOWINSOCK
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+#include "winuser.h"
+#include "wingdi.h"
+#include "shlobj.h"
+
+#include "wine/test.h"
+
+/***********************************************************************
+ * Compability macros
+ */
+
+#define DWORD_PTR UINT_PTR
+#define LONG_PTR INT_PTR
+#define ULONG_PTR UINT_PTR
+
+/***********************************************************************
+ * Windows API extension
+ */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define FIELD_ALIGNMENT(type, field) __alignof(((type*)0)->field)
+#elif defined(__GNUC__)
+# define FIELD_ALIGNMENT(type, field) __alignof__(((type*)0)->field)
+#else
+/* FIXME: Not sure if is possible to do without compiler extension */
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define _TYPE_ALIGNMENT(type) __alignof(type)
+#elif defined(__GNUC__)
+# define _TYPE_ALIGNMENT(type) __alignof__(type)
+#else
+/*
+ * FIXME: Not sure if is possible to do without compiler extension
+ * (if type is not just a name that is, if so the normal)
+ * TYPE_ALIGNMENT can be used)
+ */
+#endif
+
+#if defined(TYPE_ALIGNMENT) && defined(_MSC_VER) && _MSC_VER >= 800 && !defined(__cplusplus)
+#pragma warning(disable:4116)
+#endif
+
+#if !defined(TYPE_ALIGNMENT) && defined(_TYPE_ALIGNMENT)
+# define TYPE_ALIGNMENT _TYPE_ALIGNMENT
+#endif
+
+/***********************************************************************
+ * Test helper macros
+ */
+
+#ifdef FIELD_ALIGNMENT
+# define TEST_FIELD_ALIGNMENT(type, field, align) \
+ ok(FIELD_ALIGNMENT(type, field) == align, \
+ "FIELD_ALIGNMENT(" #type ", " #field ") == %d (expected " #align ")\n", \
+ (int)FIELD_ALIGNMENT(type, field))
+#else
+# define TEST_FIELD_ALIGNMENT(type, field, align) do { } while (0)
+#endif
+
+#define TEST_FIELD_OFFSET(type, field, offset) \
+ ok(FIELD_OFFSET(type, field) == offset, \
+ "FIELD_OFFSET(" #type ", " #field ") == %ld (expected " #offset ")\n", \
+ (long int)FIELD_OFFSET(type, field))
+
+#ifdef _TYPE_ALIGNMENT
+#define TEST__TYPE_ALIGNMENT(type, align) \
+ ok(_TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)_TYPE_ALIGNMENT(type))
+#else
+# define TEST__TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#ifdef TYPE_ALIGNMENT
+#define TEST_TYPE_ALIGNMENT(type, align) \
+ ok(TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)TYPE_ALIGNMENT(type))
+#else
+# define TEST_TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#define TEST_TYPE_SIZE(type, size) \
+ ok(sizeof(type) == size, "sizeof(" #type ") == %d (expected " #size ")\n", ((int) sizeof(type)))
+
+/***********************************************************************
+ * Test macros
+ */
+
+#define TEST_FIELD(type, field_type, field_name, field_offset, field_size, field_align) \
+ TEST_TYPE_SIZE(field_type, field_size); \
+ TEST_FIELD_ALIGNMENT(type, field_name, field_align); \
+ TEST_FIELD_OFFSET(type, field_name, field_offset); \
+
+#define TEST_TYPE(type, size, align) \
+ TEST_TYPE_ALIGNMENT(type, align); \
+ TEST_TYPE_SIZE(type, size)
+
+#define TEST_TYPE_POINTER(type, size, align) \
+ TEST__TYPE_ALIGNMENT(*(type)0, align); \
+ TEST_TYPE_SIZE(*(type)0, size)
+
+#define TEST_TYPE_SIGNED(type) \
+ ok((type) -1 < 0, "(" #type ") -1 < 0\n");
+
+#define TEST_TYPE_UNSIGNED(type) \
+ ok((type) -1 > 0, "(" #type ") -1 > 0\n");
+
+static void test_pack_BLOB(void)
+{
+ /* BLOB (pack 4) */
+ TEST_TYPE(BLOB, 8, 4);
+ TEST_FIELD(BLOB, ULONG, cbSize, 0, 4, 4);
+ TEST_FIELD(BLOB, BYTE *, pBlobData, 4, 4, 4);
+}
+
+static void test_pack_BSTR(void)
+{
+ /* BSTR */
+ TEST_TYPE(BSTR, 4, 4);
+ TEST_TYPE_POINTER(BSTR, 2, 2);
+}
+
+static void test_pack_BSTRBLOB(void)
+{
+ /* BSTRBLOB (pack 4) */
+ TEST_TYPE(BSTRBLOB, 8, 4);
+ TEST_FIELD(BSTRBLOB, ULONG, cbSize, 0, 4, 4);
+ TEST_FIELD(BSTRBLOB, BYTE *, pData, 4, 4, 4);
+}
+
+static void test_pack_BYTE_BLOB(void)
+{
+ /* BYTE_BLOB (pack 4) */
+ TEST_TYPE(BYTE_BLOB, 8, 4);
+ TEST_FIELD(BYTE_BLOB, unsigned long, clSize, 0, 4, 4);
+ TEST_FIELD(BYTE_BLOB, byte[1], abData, 4, 1, 1);
+}
+
+static void test_pack_BYTE_SIZEDARR(void)
+{
+ /* BYTE_SIZEDARR (pack 4) */
+ TEST_TYPE(BYTE_SIZEDARR, 8, 4);
+ TEST_FIELD(BYTE_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+ TEST_FIELD(BYTE_SIZEDARR, byte *, pData, 4, 4, 4);
+}
+
+static void test_pack_CLIPDATA(void)
+{
+ /* CLIPDATA (pack 4) */
+ TEST_TYPE(CLIPDATA, 12, 4);
+ TEST_FIELD(CLIPDATA, ULONG, cbSize, 0, 4, 4);
+ TEST_FIELD(CLIPDATA, long, ulClipFmt, 4, 4, 4);
+ TEST_FIELD(CLIPDATA, BYTE *, pClipData, 8, 4, 4);
+}
+
+static void test_pack_CLIPFORMAT(void)
+{
+ /* CLIPFORMAT */
+ TEST_TYPE(CLIPFORMAT, 2, 2);
+ TEST_TYPE_UNSIGNED(CLIPFORMAT);
+}
+
+static void test_pack_COAUTHIDENTITY(void)
+{
+ /* COAUTHIDENTITY (pack 4) */
+ TEST_TYPE(COAUTHIDENTITY, 28, 4);
+ TEST_FIELD(COAUTHIDENTITY, USHORT *, User, 0, 4, 4);
+ TEST_FIELD(COAUTHIDENTITY, ULONG, UserLength, 4, 4, 4);
+ TEST_FIELD(COAUTHIDENTITY, USHORT *, Domain, 8, 4, 4);
+ TEST_FIELD(COAUTHIDENTITY, ULONG, DomainLength, 12, 4, 4);
+ TEST_FIELD(COAUTHIDENTITY, USHORT *, Password, 16, 4, 4);
+ TEST_FIELD(COAUTHIDENTITY, ULONG, PasswordLength, 20, 4, 4);
+ TEST_FIELD(COAUTHIDENTITY, ULONG, Flags, 24, 4, 4);
+}
+
+static void test_pack_COAUTHINFO(void)
+{
+ /* COAUTHINFO (pack 4) */
+ TEST_TYPE(COAUTHINFO, 28, 4);
+ TEST_FIELD(COAUTHINFO, DWORD, dwAuthnSvc, 0, 4, 4);
+ TEST_FIELD(COAUTHINFO, DWORD, dwAuthzSvc, 4, 4, 4);
+ TEST_FIELD(COAUTHINFO, LPWSTR, pwszServerPrincName, 8, 4, 4);
+ TEST_FIELD(COAUTHINFO, DWORD, dwAuthnLevel, 12, 4, 4);
+ TEST_FIELD(COAUTHINFO, DWORD, dwImpersonationLevel, 16, 4, 4);
+ TEST_FIELD(COAUTHINFO, COAUTHIDENTITY *, pAuthIdentityData, 20, 4, 4);
+ TEST_FIELD(COAUTHINFO, DWORD, dwCapabilities, 24, 4, 4);
+}
+
+static void test_pack_COSERVERINFO(void)
+{
+ /* COSERVERINFO (pack 4) */
+ TEST_TYPE(COSERVERINFO, 16, 4);
+ TEST_FIELD(COSERVERINFO, DWORD, dwReserved1, 0, 4, 4);
+ TEST_FIELD(COSERVERINFO, LPWSTR, pwszName, 4, 4, 4);
+ TEST_FIELD(COSERVERINFO, COAUTHINFO *, pAuthInfo, 8, 4, 4);
+ TEST_FIELD(COSERVERINFO, DWORD, dwReserved2, 12, 4, 4);
+}
+
+static void test_pack_DWORD_SIZEDARR(void)
+{
+ /* DWORD_SIZEDARR (pack 4) */
+ TEST_TYPE(DWORD_SIZEDARR, 8, 4);
+ TEST_FIELD(DWORD_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+ TEST_FIELD(DWORD_SIZEDARR, unsigned long *, pData, 4, 4, 4);
+}
+
+static void test_pack_FLAGGED_BYTE_BLOB(void)
+{
+ /* FLAGGED_BYTE_BLOB (pack 4) */
+ TEST_TYPE(FLAGGED_BYTE_BLOB, 12, 4);
+ TEST_FIELD(FLAGGED_BYTE_BLOB, unsigned long, fFlags, 0, 4, 4);
+ TEST_FIELD(FLAGGED_BYTE_BLOB, unsigned long, clSize, 4, 4, 4);
+ TEST_FIELD(FLAGGED_BYTE_BLOB, byte[1], abData, 8, 1, 1);
+}
+
+static void test_pack_FLAGGED_WORD_BLOB(void)
+{
+ /* FLAGGED_WORD_BLOB (pack 4) */
+ TEST_TYPE(FLAGGED_WORD_BLOB, 12, 4);
+ TEST_FIELD(FLAGGED_WORD_BLOB, unsigned long, fFlags, 0, 4, 4);
+ TEST_FIELD(FLAGGED_WORD_BLOB, unsigned long, clSize, 4, 4, 4);
+ TEST_FIELD(FLAGGED_WORD_BLOB, unsigned short[1], asData, 8, 2, 2);
+}
+
+static void test_pack_HMETAFILEPICT(void)
+{
+ /* HMETAFILEPICT */
+ TEST_TYPE(HMETAFILEPICT, 4, 4);
+}
+
+static void test_pack_HYPER_SIZEDARR(void)
+{
+ /* HYPER_SIZEDARR (pack 4) */
+ TEST_TYPE(HYPER_SIZEDARR, 8, 4);
+ TEST_FIELD(HYPER_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+ TEST_FIELD(HYPER_SIZEDARR, hyper *, pData, 4, 4, 4);
+}
+
+static void test_pack_LPBLOB(void)
+{
+ /* LPBLOB */
+ TEST_TYPE(LPBLOB, 4, 4);
+ TEST_TYPE_POINTER(LPBLOB, 8, 4);
+}
+
+static void test_pack_LPBSTR(void)
+{
+ /* LPBSTR */
+ TEST_TYPE(LPBSTR, 4, 4);
+ TEST_TYPE_POINTER(LPBSTR, 4, 4);
+}
+
+static void test_pack_LPBSTRBLOB(void)
+{
+ /* LPBSTRBLOB */
+ TEST_TYPE(LPBSTRBLOB, 4, 4);
+ TEST_TYPE_POINTER(LPBSTRBLOB, 8, 4);
+}
+
+static void test_pack_LPCOLESTR(void)
+{
+ /* LPCOLESTR */
+ TEST_TYPE(LPCOLESTR, 4, 4);
+ TEST_TYPE_POINTER(LPCOLESTR, 2, 2);
+}
+
+static void test_pack_LPCY(void)
+{
+ /* LPCY */
+ TEST_TYPE(LPCY, 4, 4);
+}
+
+static void test_pack_LPDECIMAL(void)
+{
+ /* LPDECIMAL */
+ TEST_TYPE(LPDECIMAL, 4, 4);
+}
+
+static void test_pack_LPOLESTR(void)
+{
+ /* LPOLESTR */
+ TEST_TYPE(LPOLESTR, 4, 4);
+ TEST_TYPE_POINTER(LPOLESTR, 2, 2);
+}
+
+static void test_pack_OLECHAR(void)
+{
+ /* OLECHAR */
+ TEST_TYPE(OLECHAR, 2, 2);
+}
+
+static void test_pack_PROPID(void)
+{
+ /* PROPID */
+ TEST_TYPE(PROPID, 4, 4);
+}
+
+static void test_pack_RemHBITMAP(void)
+{
+ /* RemHBITMAP (pack 4) */
+ TEST_TYPE(RemHBITMAP, 8, 4);
+ TEST_FIELD(RemHBITMAP, unsigned long, cbData, 0, 4, 4);
+ TEST_FIELD(RemHBITMAP, byte[1], data, 4, 1, 1);
+}
+
+static void test_pack_RemHENHMETAFILE(void)
+{
+ /* RemHENHMETAFILE (pack 4) */
+ TEST_TYPE(RemHENHMETAFILE, 8, 4);
+ TEST_FIELD(RemHENHMETAFILE, unsigned long, cbData, 0, 4, 4);
+ TEST_FIELD(RemHENHMETAFILE, byte[1], data, 4, 1, 1);
+}
+
+static void test_pack_RemHGLOBAL(void)
+{
+ /* RemHGLOBAL (pack 4) */
+ TEST_TYPE(RemHGLOBAL, 12, 4);
+ TEST_FIELD(RemHGLOBAL, long, fNullHGlobal, 0, 4, 4);
+ TEST_FIELD(RemHGLOBAL, unsigned long, cbData, 4, 4, 4);
+ TEST_FIELD(RemHGLOBAL, byte[1], data, 8, 1, 1);
+}
+
+static void test_pack_RemHMETAFILEPICT(void)
+{
+ /* RemHMETAFILEPICT (pack 4) */
+ TEST_TYPE(RemHMETAFILEPICT, 20, 4);
+ TEST_FIELD(RemHMETAFILEPICT, long, mm, 0, 4, 4);
+ TEST_FIELD(RemHMETAFILEPICT, long, xExt, 4, 4, 4);
+ TEST_FIELD(RemHMETAFILEPICT, long, yExt, 8, 4, 4);
+ TEST_FIELD(RemHMETAFILEPICT, unsigned long, cbData, 12, 4, 4);
+ TEST_FIELD(RemHMETAFILEPICT, byte[1], data, 16, 1, 1);
+}
+
+static void test_pack_RemHPALETTE(void)
+{
+ /* RemHPALETTE (pack 4) */
+ TEST_TYPE(RemHPALETTE, 8, 4);
+ TEST_FIELD(RemHPALETTE, unsigned long, cbData, 0, 4, 4);
+ TEST_FIELD(RemHPALETTE, byte[1], data, 4, 1, 1);
+}
+
+static void test_pack_SCODE(void)
+{
+ /* SCODE */
+ TEST_TYPE(SCODE, 4, 4);
+}
+
+static void test_pack_UP_BYTE_BLOB(void)
+{
+ /* UP_BYTE_BLOB */
+ TEST_TYPE(UP_BYTE_BLOB, 4, 4);
+ TEST_TYPE_POINTER(UP_BYTE_BLOB, 8, 4);
+}
+
+static void test_pack_UP_FLAGGED_BYTE_BLOB(void)
+{
+ /* UP_FLAGGED_BYTE_BLOB */
+ TEST_TYPE(UP_FLAGGED_BYTE_BLOB, 4, 4);
+ TEST_TYPE_POINTER(UP_FLAGGED_BYTE_BLOB, 12, 4);
+}
+
+static void test_pack_UP_FLAGGED_WORD_BLOB(void)
+{
+ /* UP_FLAGGED_WORD_BLOB */
+ TEST_TYPE(UP_FLAGGED_WORD_BLOB, 4, 4);
+ TEST_TYPE_POINTER(UP_FLAGGED_WORD_BLOB, 12, 4);
+}
+
+static void test_pack_VARIANT_BOOL(void)
+{
+ /* VARIANT_BOOL */
+ TEST_TYPE(VARIANT_BOOL, 2, 2);
+ TEST_TYPE_SIGNED(VARIANT_BOOL);
+}
+
+static void test_pack_VARTYPE(void)
+{
+ /* VARTYPE */
+ TEST_TYPE(VARTYPE, 2, 2);
+ TEST_TYPE_UNSIGNED(VARTYPE);
+}
+
+static void test_pack_WORD_SIZEDARR(void)
+{
+ /* WORD_SIZEDARR (pack 4) */
+ TEST_TYPE(WORD_SIZEDARR, 8, 4);
+ TEST_FIELD(WORD_SIZEDARR, unsigned long, clSize, 0, 4, 4);
+ TEST_FIELD(WORD_SIZEDARR, unsigned short *, pData, 4, 4, 4);
+}
+
+static void test_pack_remoteMETAFILEPICT(void)
+{
+ /* remoteMETAFILEPICT (pack 4) */
+ TEST_TYPE(remoteMETAFILEPICT, 16, 4);
+ TEST_FIELD(remoteMETAFILEPICT, long, mm, 0, 4, 4);
+ TEST_FIELD(remoteMETAFILEPICT, long, xExt, 4, 4, 4);
+ TEST_FIELD(remoteMETAFILEPICT, long, yExt, 8, 4, 4);
+ TEST_FIELD(remoteMETAFILEPICT, userHMETAFILE *, hMF, 12, 4, 4);
+}
+
+static void test_pack_userBITMAP(void)
+{
+ /* userBITMAP (pack 4) */
+ TEST_TYPE(userBITMAP, 28, 4);
+ TEST_FIELD(userBITMAP, LONG, bmType, 0, 4, 4);
+ TEST_FIELD(userBITMAP, LONG, bmWidth, 4, 4, 4);
+ TEST_FIELD(userBITMAP, LONG, bmHeight, 8, 4, 4);
+ TEST_FIELD(userBITMAP, LONG, bmWidthBytes, 12, 4, 4);
+ TEST_FIELD(userBITMAP, WORD, bmPlanes, 16, 2, 2);
+ TEST_FIELD(userBITMAP, WORD, bmBitsPixel, 18, 2, 2);
+ TEST_FIELD(userBITMAP, ULONG, cbSize, 20, 4, 4);
+ TEST_FIELD(userBITMAP, byte[1], pBuffer, 24, 1, 1);
+}
+
+static void test_pack_userCLIPFORMAT(void)
+{
+ /* userCLIPFORMAT (pack 4) */
+ TEST_FIELD(userCLIPFORMAT, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHBITMAP(void)
+{
+ /* userHBITMAP (pack 4) */
+ TEST_FIELD(userHBITMAP, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHENHMETAFILE(void)
+{
+ /* userHENHMETAFILE (pack 4) */
+ TEST_FIELD(userHENHMETAFILE, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHGLOBAL(void)
+{
+ /* userHGLOBAL (pack 4) */
+ TEST_FIELD(userHGLOBAL, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHMETAFILE(void)
+{
+ /* userHMETAFILE (pack 4) */
+ TEST_FIELD(userHMETAFILE, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHMETAFILEPICT(void)
+{
+ /* userHMETAFILEPICT (pack 4) */
+ TEST_FIELD(userHMETAFILEPICT, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_userHPALETTE(void)
+{
+ /* userHPALETTE (pack 4) */
+ TEST_FIELD(userHPALETTE, long, fContext, 0, 4, 4);
+}
+
+static void test_pack_wireBSTR(void)
+{
+ /* wireBSTR */
+ TEST_TYPE(wireBSTR, 4, 4);
+ TEST_TYPE_POINTER(wireBSTR, 12, 4);
+}
+
+static void test_pack_wireCLIPFORMAT(void)
+{
+ /* wireCLIPFORMAT */
+ TEST_TYPE(wireCLIPFORMAT, 4, 4);
+}
+
+static void test_pack_wireHBITMAP(void)
+{
+ /* wireHBITMAP */
+ TEST_TYPE(wireHBITMAP, 4, 4);
+}
+
+static void test_pack_wireHENHMETAFILE(void)
+{
+ /* wireHENHMETAFILE */
+ TEST_TYPE(wireHENHMETAFILE, 4, 4);
+}
+
+static void test_pack_wireHGLOBAL(void)
+{
+ /* wireHGLOBAL */
+ TEST_TYPE(wireHGLOBAL, 4, 4);
+}
+
+static void test_pack_wireHMETAFILE(void)
+{
+ /* wireHMETAFILE */
+ TEST_TYPE(wireHMETAFILE, 4, 4);
+}
+
+static void test_pack_wireHMETAFILEPICT(void)
+{
+ /* wireHMETAFILEPICT */
+ TEST_TYPE(wireHMETAFILEPICT, 4, 4);
+}
+
+static void test_pack_wireHPALETTE(void)
+{
+ /* wireHPALETTE */
+ TEST_TYPE(wireHPALETTE, 4, 4);
+}
+
+static void test_pack_CLSID(void)
+{
+ /* CLSID */
+ TEST_TYPE(CLSID, 16, 4);
+}
+
+static void test_pack_FMTID(void)
+{
+ /* FMTID */
+ TEST_TYPE(FMTID, 16, 4);
+}
+
+static void test_pack_GUID(void)
+{
+ /* GUID (pack 4) */
+ TEST_TYPE(GUID, 16, 4);
+ TEST_FIELD(GUID, unsigned long, Data1, 0, 4, 4);
+ TEST_FIELD(GUID, unsigned short, Data2, 4, 2, 2);
+ TEST_FIELD(GUID, unsigned short, Data3, 6, 2, 2);
+ TEST_FIELD(GUID, unsigned char[ 8 ], Data4, 8, 8, 1);
+}
+
+static void test_pack_IID(void)
+{
+ /* IID */
+ TEST_TYPE(IID, 16, 4);
+}
+
+static void test_pack_LPGUID(void)
+{
+ /* LPGUID */
+ TEST_TYPE(LPGUID, 4, 4);
+ TEST_TYPE_POINTER(LPGUID, 16, 4);
+}
+
+static void test_pack_APPBARDATA(void)
+{
+ /* APPBARDATA (pack 1) */
+ TEST_TYPE(APPBARDATA, 36, 1);
+ TEST_FIELD(APPBARDATA, DWORD, cbSize, 0, 4, 1);
+ TEST_FIELD(APPBARDATA, HWND, hWnd, 4, 4, 1);
+ TEST_FIELD(APPBARDATA, UINT, uCallbackMessage, 8, 4, 1);
+ TEST_FIELD(APPBARDATA, UINT, uEdge, 12, 4, 1);
+ TEST_FIELD(APPBARDATA, RECT, rc, 16, 16, 1);
+ TEST_FIELD(APPBARDATA, LPARAM, lParam, 32, 4, 1);
+}
+
+static void test_pack_DRAGINFOA(void)
+{
+ /* DRAGINFOA (pack 1) */
+ TEST_TYPE(DRAGINFOA, 24, 1);
+ TEST_FIELD(DRAGINFOA, UINT, uSize, 0, 4, 1);
+ TEST_FIELD(DRAGINFOA, POINT, pt, 4, 8, 1);
+ TEST_FIELD(DRAGINFOA, BOOL, fNC, 12, 4, 1);
+ TEST_FIELD(DRAGINFOA, LPSTR, lpFileList, 16, 4, 1);
+ TEST_FIELD(DRAGINFOA, DWORD, grfKeyState, 20, 4, 1);
+}
+
+static void test_pack_DRAGINFOW(void)
+{
+ /* DRAGINFOW (pack 1) */
+ TEST_TYPE(DRAGINFOW, 24, 1);
+ TEST_FIELD(DRAGINFOW, UINT, uSize, 0, 4, 1);
+ TEST_FIELD(DRAGINFOW, POINT, pt, 4, 8, 1);
+ TEST_FIELD(DRAGINFOW, BOOL, fNC, 12, 4, 1);
+ TEST_FIELD(DRAGINFOW, LPWSTR, lpFileList, 16, 4, 1);
+ TEST_FIELD(DRAGINFOW, DWORD, grfKeyState, 20, 4, 1);
+}
+
+static void test_pack_FILEOP_FLAGS(void)
+{
+ /* FILEOP_FLAGS */
+ TEST_TYPE(FILEOP_FLAGS, 2, 2);
+ TEST_TYPE_UNSIGNED(FILEOP_FLAGS);
+}
+
+static void test_pack_LPDRAGINFOA(void)
+{
+ /* LPDRAGINFOA */
+ TEST_TYPE(LPDRAGINFOA, 4, 4);
+ TEST_TYPE_POINTER(LPDRAGINFOA, 24, 1);
+}
+
+static void test_pack_LPDRAGINFOW(void)
+{
+ /* LPDRAGINFOW */
+ TEST_TYPE(LPDRAGINFOW, 4, 4);
+ TEST_TYPE_POINTER(LPDRAGINFOW, 24, 1);
+}
+
+static void test_pack_LPSHELLEXECUTEINFOA(void)
+{
+ /* LPSHELLEXECUTEINFOA */
+ TEST_TYPE(LPSHELLEXECUTEINFOA, 4, 4);
+}
+
+static void test_pack_LPSHELLEXECUTEINFOW(void)
+{
+ /* LPSHELLEXECUTEINFOW */
+ TEST_TYPE(LPSHELLEXECUTEINFOW, 4, 4);
+}
+
+static void test_pack_LPSHFILEOPSTRUCTA(void)
+{
+ /* LPSHFILEOPSTRUCTA */
+ TEST_TYPE(LPSHFILEOPSTRUCTA, 4, 4);
+ TEST_TYPE_POINTER(LPSHFILEOPSTRUCTA, 30, 1);
+}
+
+static void test_pack_LPSHFILEOPSTRUCTW(void)
+{
+ /* LPSHFILEOPSTRUCTW */
+ TEST_TYPE(LPSHFILEOPSTRUCTW, 4, 4);
+ TEST_TYPE_POINTER(LPSHFILEOPSTRUCTW, 30, 1);
+}
+
+static void test_pack_LPSHNAMEMAPPINGA(void)
+{
+ /* LPSHNAMEMAPPINGA */
+ TEST_TYPE(LPSHNAMEMAPPINGA, 4, 4);
+ TEST_TYPE_POINTER(LPSHNAMEMAPPINGA, 16, 1);
+}
+
+static void test_pack_LPSHNAMEMAPPINGW(void)
+{
+ /* LPSHNAMEMAPPINGW */
+ TEST_TYPE(LPSHNAMEMAPPINGW, 4, 4);
+ TEST_TYPE_POINTER(LPSHNAMEMAPPINGW, 16, 1);
+}
+
+static void test_pack_NOTIFYICONDATAA(void)
+{
+ /* NOTIFYICONDATAA (pack 1) */
+ TEST_FIELD(NOTIFYICONDATAA, DWORD, cbSize, 0, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, HWND, hWnd, 4, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, UINT, uID, 8, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, UINT, uFlags, 12, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, UINT, uCallbackMessage, 16, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, HICON, hIcon, 20, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, CHAR[128], szTip, 24, 128, 1);
+ TEST_FIELD(NOTIFYICONDATAA, DWORD, dwState, 152, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, DWORD, dwStateMask, 156, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAA, CHAR[256], szInfo, 160, 256, 1);
+}
+
+static void test_pack_NOTIFYICONDATAW(void)
+{
+ /* NOTIFYICONDATAW (pack 1) */
+ TEST_FIELD(NOTIFYICONDATAW, DWORD, cbSize, 0, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, HWND, hWnd, 4, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, UINT, uID, 8, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, UINT, uFlags, 12, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, UINT, uCallbackMessage, 16, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, HICON, hIcon, 20, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, WCHAR[128], szTip, 24, 256, 1);
+ TEST_FIELD(NOTIFYICONDATAW, DWORD, dwState, 280, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, DWORD, dwStateMask, 284, 4, 1);
+ TEST_FIELD(NOTIFYICONDATAW, WCHAR[256], szInfo, 288, 512, 1);
+}
+
+static void test_pack_PAPPBARDATA(void)
+{
+ /* PAPPBARDATA */
+ TEST_TYPE(PAPPBARDATA, 4, 4);
+ TEST_TYPE_POINTER(PAPPBARDATA, 36, 1);
+}
+
+static void test_pack_PNOTIFYICONDATAA(void)
+{
+ /* PNOTIFYICONDATAA */
+ TEST_TYPE(PNOTIFYICONDATAA, 4, 4);
+}
+
+static void test_pack_PNOTIFYICONDATAW(void)
+{
+ /* PNOTIFYICONDATAW */
+ TEST_TYPE(PNOTIFYICONDATAW, 4, 4);
+}
+
+static void test_pack_PRINTEROP_FLAGS(void)
+{
+ /* PRINTEROP_FLAGS */
+ TEST_TYPE(PRINTEROP_FLAGS, 2, 2);
+ TEST_TYPE_UNSIGNED(PRINTEROP_FLAGS);
+}
+
+static void test_pack_SHELLEXECUTEINFOA(void)
+{
+ /* SHELLEXECUTEINFOA (pack 1) */
+ TEST_FIELD(SHELLEXECUTEINFOA, DWORD, cbSize, 0, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, ULONG, fMask, 4, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, HWND, hwnd, 8, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpVerb, 12, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpFile, 16, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpParameters, 20, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpDirectory, 24, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, INT, nShow, 28, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, HINSTANCE, hInstApp, 32, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, LPVOID, lpIDList, 36, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, LPCSTR, lpClass, 40, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, HKEY, hkeyClass, 44, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOA, DWORD, dwHotKey, 48, 4, 1);
+}
+
+static void test_pack_SHELLEXECUTEINFOW(void)
+{
+ /* SHELLEXECUTEINFOW (pack 1) */
+ TEST_FIELD(SHELLEXECUTEINFOW, DWORD, cbSize, 0, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, ULONG, fMask, 4, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, HWND, hwnd, 8, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpVerb, 12, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpFile, 16, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpParameters, 20, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpDirectory, 24, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, INT, nShow, 28, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, HINSTANCE, hInstApp, 32, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, LPVOID, lpIDList, 36, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, LPCWSTR, lpClass, 40, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, HKEY, hkeyClass, 44, 4, 1);
+ TEST_FIELD(SHELLEXECUTEINFOW, DWORD, dwHotKey, 48, 4, 1);
+}
+
+static void test_pack_SHFILEINFOA(void)
+{
+ /* SHFILEINFOA (pack 1) */
+ TEST_TYPE(SHFILEINFOA, 352, 1);
+ TEST_FIELD(SHFILEINFOA, HICON, hIcon, 0, 4, 1);
+ TEST_FIELD(SHFILEINFOA, int, iIcon, 4, 4, 1);
+ TEST_FIELD(SHFILEINFOA, DWORD, dwAttributes, 8, 4, 1);
+ TEST_FIELD(SHFILEINFOA, CHAR[MAX_PATH], szDisplayName, 12, 260, 1);
+ TEST_FIELD(SHFILEINFOA, CHAR[80], szTypeName, 272, 80, 1);
+}
+
+static void test_pack_SHFILEINFOW(void)
+{
+ /* SHFILEINFOW (pack 1) */
+ TEST_TYPE(SHFILEINFOW, 692, 1);
+ TEST_FIELD(SHFILEINFOW, HICON, hIcon, 0, 4, 1);
+ TEST_FIELD(SHFILEINFOW, int, iIcon, 4, 4, 1);
+ TEST_FIELD(SHFILEINFOW, DWORD, dwAttributes, 8, 4, 1);
+ TEST_FIELD(SHFILEINFOW, WCHAR[MAX_PATH], szDisplayName, 12, 520, 1);
+ TEST_FIELD(SHFILEINFOW, WCHAR[80], szTypeName, 532, 160, 1);
+}
+
+static void test_pack_SHFILEOPSTRUCTA(void)
+{
+ /* SHFILEOPSTRUCTA (pack 1) */
+ TEST_TYPE(SHFILEOPSTRUCTA, 30, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, HWND, hwnd, 0, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, UINT, wFunc, 4, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, LPCSTR, pFrom, 8, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, LPCSTR, pTo, 12, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, FILEOP_FLAGS, fFlags, 16, 2, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, BOOL, fAnyOperationsAborted, 18, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, LPVOID, hNameMappings, 22, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTA, LPCSTR, lpszProgressTitle, 26, 4, 1);
+}
+
+static void test_pack_SHFILEOPSTRUCTW(void)
+{
+ /* SHFILEOPSTRUCTW (pack 1) */
+ TEST_TYPE(SHFILEOPSTRUCTW, 30, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, HWND, hwnd, 0, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, UINT, wFunc, 4, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, LPCWSTR, pFrom, 8, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, LPCWSTR, pTo, 12, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, FILEOP_FLAGS, fFlags, 16, 2, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, BOOL, fAnyOperationsAborted, 18, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, LPVOID, hNameMappings, 22, 4, 1);
+ TEST_FIELD(SHFILEOPSTRUCTW, LPCWSTR, lpszProgressTitle, 26, 4, 1);
+}
+
+static void test_pack_SHNAMEMAPPINGA(void)
+{
+ /* SHNAMEMAPPINGA (pack 1) */
+ TEST_TYPE(SHNAMEMAPPINGA, 16, 1);
+ TEST_FIELD(SHNAMEMAPPINGA, LPSTR, pszOldPath, 0, 4, 1);
+ TEST_FIELD(SHNAMEMAPPINGA, LPSTR, pszNewPath, 4, 4, 1);
+ TEST_FIELD(SHNAMEMAPPINGA, int, cchOldPath, 8, 4, 1);
+ TEST_FIELD(SHNAMEMAPPINGA, int, cchNewPath, 12, 4, 1);
+}
+
+static void test_pack_SHNAMEMAPPINGW(void)
+{
+ /* SHNAMEMAPPINGW (pack 1) */
+ TEST_TYPE(SHNAMEMAPPINGW, 16, 1);
+ TEST_FIELD(SHNAMEMAPPINGW, LPWSTR, pszOldPath, 0, 4, 1);
+ TEST_FIELD(SHNAMEMAPPINGW, LPWSTR, pszNewPath, 4, 4, 1);
+ TEST_FIELD(SHNAMEMAPPINGW, int, cchOldPath, 8, 4, 1);
+ TEST_FIELD(SHNAMEMAPPINGW, int, cchNewPath, 12, 4, 1);
+}
+
+static void test_pack_ITEMIDLIST(void)
+{
+ /* ITEMIDLIST (pack 1) */
+ TEST_TYPE(ITEMIDLIST, 3, 1);
+ TEST_FIELD(ITEMIDLIST, SHITEMID, mkid, 0, 3, 1);
+}
+
+static void test_pack_LPCITEMIDLIST(void)
+{
+ /* LPCITEMIDLIST */
+ TEST_TYPE(LPCITEMIDLIST, 4, 4);
+ TEST_TYPE_POINTER(LPCITEMIDLIST, 3, 1);
+}
+
+static void test_pack_LPCSHITEMID(void)
+{
+ /* LPCSHITEMID */
+ TEST_TYPE(LPCSHITEMID, 4, 4);
+ TEST_TYPE_POINTER(LPCSHITEMID, 3, 1);
+}
+
+static void test_pack_LPITEMIDLIST(void)
+{
+ /* LPITEMIDLIST */
+ TEST_TYPE(LPITEMIDLIST, 4, 4);
+ TEST_TYPE_POINTER(LPITEMIDLIST, 3, 1);
+}
+
+static void test_pack_LPSHELLDETAILS(void)
+{
+ /* LPSHELLDETAILS */
+ TEST_TYPE(LPSHELLDETAILS, 4, 4);
+}
+
+static void test_pack_LPSHITEMID(void)
+{
+ /* LPSHITEMID */
+ TEST_TYPE(LPSHITEMID, 4, 4);
+ TEST_TYPE_POINTER(LPSHITEMID, 3, 1);
+}
+
+static void test_pack_LPSTRRET(void)
+{
+ /* LPSTRRET */
+ TEST_TYPE(LPSTRRET, 4, 4);
+}
+
+static void test_pack_SHELLDETAILS(void)
+{
+ /* SHELLDETAILS (pack 1) */
+ TEST_FIELD(SHELLDETAILS, int, fmt, 0, 4, 1);
+ TEST_FIELD(SHELLDETAILS, int, cxChar, 4, 4, 1);
+}
+
+static void test_pack_SHITEMID(void)
+{
+ /* SHITEMID (pack 1) */
+ TEST_TYPE(SHITEMID, 3, 1);
+ TEST_FIELD(SHITEMID, WORD, cb, 0, 2, 1);
+ TEST_FIELD(SHITEMID, BYTE[1], abID, 2, 1, 1);
+}
+
+static void test_pack_STRRET(void)
+{
+ /* STRRET (pack 4) */
+ TEST_FIELD(STRRET, UINT, uType, 0, 4, 4);
+}
+
+static void test_pack_AUTO_SCROLL_DATA(void)
+{
+ /* AUTO_SCROLL_DATA (pack 1) */
+ TEST_TYPE(AUTO_SCROLL_DATA, 48, 1);
+ TEST_FIELD(AUTO_SCROLL_DATA, int, iNextSample, 0, 4, 1);
+ TEST_FIELD(AUTO_SCROLL_DATA, DWORD, dwLastScroll, 4, 4, 1);
+ TEST_FIELD(AUTO_SCROLL_DATA, BOOL, bFull, 8, 4, 1);
+ TEST_FIELD(AUTO_SCROLL_DATA, POINT[NUM_POINTS], pts, 12, 24, 1);
+ TEST_FIELD(AUTO_SCROLL_DATA, DWORD[NUM_POINTS], dwTimes, 36, 12, 1);
+}
+
+static void test_pack_BFFCALLBACK(void)
+{
+ /* BFFCALLBACK */
+ TEST_TYPE(BFFCALLBACK, 4, 4);
+}
+
+static void test_pack_BROWSEINFOA(void)
+{
+ /* BROWSEINFOA (pack 8) */
+ TEST_TYPE(BROWSEINFOA, 32, 4);
+ TEST_FIELD(BROWSEINFOA, HWND, hwndOwner, 0, 4, 4);
+ TEST_FIELD(BROWSEINFOA, LPCITEMIDLIST, pidlRoot, 4, 4, 4);
+ TEST_FIELD(BROWSEINFOA, LPSTR, pszDisplayName, 8, 4, 4);
+ TEST_FIELD(BROWSEINFOA, LPCSTR, lpszTitle, 12, 4, 4);
+ TEST_FIELD(BROWSEINFOA, UINT, ulFlags, 16, 4, 4);
+ TEST_FIELD(BROWSEINFOA, BFFCALLBACK, lpfn, 20, 4, 4);
+ TEST_FIELD(BROWSEINFOA, LPARAM, lParam, 24, 4, 4);
+ TEST_FIELD(BROWSEINFOA, INT, iImage, 28, 4, 4);
+}
+
+static void test_pack_BROWSEINFOW(void)
+{
+ /* BROWSEINFOW (pack 8) */
+ TEST_TYPE(BROWSEINFOW, 32, 4);
+ TEST_FIELD(BROWSEINFOW, HWND, hwndOwner, 0, 4, 4);
+ TEST_FIELD(BROWSEINFOW, LPCITEMIDLIST, pidlRoot, 4, 4, 4);
+ TEST_FIELD(BROWSEINFOW, LPWSTR, pszDisplayName, 8, 4, 4);
+ TEST_FIELD(BROWSEINFOW, LPCWSTR, lpszTitle, 12, 4, 4);
+ TEST_FIELD(BROWSEINFOW, UINT, ulFlags, 16, 4, 4);
+ TEST_FIELD(BROWSEINFOW, BFFCALLBACK, lpfn, 20, 4, 4);
+ TEST_FIELD(BROWSEINFOW, LPARAM, lParam, 24, 4, 4);
+ TEST_FIELD(BROWSEINFOW, INT, iImage, 28, 4, 4);
+}
+
+static void test_pack_CABINETSTATE(void)
+{
+ /* CABINETSTATE (pack 1) */
+ TEST_TYPE(CABINETSTATE, 12, 1);
+ TEST_FIELD(CABINETSTATE, WORD, cLength, 0, 2, 1);
+ TEST_FIELD(CABINETSTATE, WORD, nVersion, 2, 2, 1);
+ TEST_FIELD(CABINETSTATE, UINT, fMenuEnumFilter, 8, 4, 1);
+}
+
+static void test_pack_CIDA(void)
+{
+ /* CIDA (pack 1) */
+ TEST_TYPE(CIDA, 8, 1);
+ TEST_FIELD(CIDA, UINT, cidl, 0, 4, 1);
+ TEST_FIELD(CIDA, UINT[1], aoffset, 4, 4, 1);
+}
+
+static void test_pack_CSFV(void)
+{
+ /* CSFV (pack 1) */
+ TEST_FIELD(CSFV, UINT, cbSize, 0, 4, 1);
+ TEST_FIELD(CSFV, IShellFolder*, pshf, 4, 4, 1);
+ TEST_FIELD(CSFV, IShellView*, psvOuter, 8, 4, 1);
+ TEST_FIELD(CSFV, LPCITEMIDLIST, pidl, 12, 4, 1);
+ TEST_FIELD(CSFV, LONG, lEvents, 16, 4, 1);
+ TEST_FIELD(CSFV, LPFNVIEWCALLBACK, pfnCallback, 20, 4, 1);
+}
+
+static void test_pack_DROPFILES(void)
+{
+ /* DROPFILES (pack 1) */
+ TEST_TYPE(DROPFILES, 20, 1);
+ TEST_FIELD(DROPFILES, DWORD, pFiles, 0, 4, 1);
+ TEST_FIELD(DROPFILES, POINT, pt, 4, 8, 1);
+ TEST_FIELD(DROPFILES, BOOL, fNC, 12, 4, 1);
+ TEST_FIELD(DROPFILES, BOOL, fWide, 16, 4, 1);
+}
+
+static void test_pack_FILEDESCRIPTORA(void)
+{
+ /* FILEDESCRIPTORA (pack 1) */
+ TEST_TYPE(FILEDESCRIPTORA, 332, 1);
+ TEST_FIELD(FILEDESCRIPTORA, DWORD, dwFlags, 0, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORA, CLSID, clsid, 4, 16, 1);
+ TEST_FIELD(FILEDESCRIPTORA, SIZEL, sizel, 20, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORA, POINTL, pointl, 28, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORA, DWORD, dwFileAttributes, 36, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORA, FILETIME, ftCreationTime, 40, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORA, FILETIME, ftLastAccessTime, 48, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORA, FILETIME, ftLastWriteTime, 56, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORA, DWORD, nFileSizeHigh, 64, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORA, DWORD, nFileSizeLow, 68, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORA, CHAR[MAX_PATH], cFileName, 72, 260, 1);
+}
+
+static void test_pack_FILEDESCRIPTORW(void)
+{
+ /* FILEDESCRIPTORW (pack 1) */
+ TEST_TYPE(FILEDESCRIPTORW, 592, 1);
+ TEST_FIELD(FILEDESCRIPTORW, DWORD, dwFlags, 0, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORW, CLSID, clsid, 4, 16, 1);
+ TEST_FIELD(FILEDESCRIPTORW, SIZEL, sizel, 20, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORW, POINTL, pointl, 28, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORW, DWORD, dwFileAttributes, 36, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORW, FILETIME, ftCreationTime, 40, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORW, FILETIME, ftLastAccessTime, 48, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORW, FILETIME, ftLastWriteTime, 56, 8, 1);
+ TEST_FIELD(FILEDESCRIPTORW, DWORD, nFileSizeHigh, 64, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORW, DWORD, nFileSizeLow, 68, 4, 1);
+ TEST_FIELD(FILEDESCRIPTORW, WCHAR[MAX_PATH], cFileName, 72, 520, 1);
+}
+
+static void test_pack_FILEGROUPDESCRIPTORA(void)
+{
+ /* FILEGROUPDESCRIPTORA (pack 1) */
+ TEST_TYPE(FILEGROUPDESCRIPTORA, 336, 1);
+ TEST_FIELD(FILEGROUPDESCRIPTORA, UINT, cItems, 0, 4, 1);
+ TEST_FIELD(FILEGROUPDESCRIPTORA, FILEDESCRIPTORA[1], fgd, 4, 332, 1);
+}
+
+static void test_pack_FILEGROUPDESCRIPTORW(void)
+{
+ /* FILEGROUPDESCRIPTORW (pack 1) */
+ TEST_TYPE(FILEGROUPDESCRIPTORW, 596, 1);
+ TEST_FIELD(FILEGROUPDESCRIPTORW, UINT, cItems, 0, 4, 1);
+ TEST_FIELD(FILEGROUPDESCRIPTORW, FILEDESCRIPTORW[1], fgd, 4, 592, 1);
+}
+
+static void test_pack_IFileSystemBindData(void)
+{
+ /* IFileSystemBindData */
+}
+
+static void test_pack_IFileSystemBindDataVtbl(void)
+{
+ /* IFileSystemBindDataVtbl */
+}
+
+static void test_pack_IShellChangeNotify(void)
+{
+ /* IShellChangeNotify */
+}
+
+static void test_pack_IShellIcon(void)
+{
+ /* IShellIcon */
+}
+
+static void test_pack_LPBROWSEINFOA(void)
+{
+ /* LPBROWSEINFOA */
+ TEST_TYPE(LPBROWSEINFOA, 4, 4);
+ TEST_TYPE_POINTER(LPBROWSEINFOA, 32, 4);
+}
+
+static void test_pack_LPBROWSEINFOW(void)
+{
+ /* LPBROWSEINFOW */
+ TEST_TYPE(LPBROWSEINFOW, 4, 4);
+ TEST_TYPE_POINTER(LPBROWSEINFOW, 32, 4);
+}
+
+static void test_pack_LPCABINETSTATE(void)
+{
+ /* LPCABINETSTATE */
+ TEST_TYPE(LPCABINETSTATE, 4, 4);
+ TEST_TYPE_POINTER(LPCABINETSTATE, 12, 1);
+}
+
+static void test_pack_LPCSFV(void)
+{
+ /* LPCSFV */
+ TEST_TYPE(LPCSFV, 4, 4);
+}
+
+static void test_pack_LPDROPFILES(void)
+{
+ /* LPDROPFILES */
+ TEST_TYPE(LPDROPFILES, 4, 4);
+ TEST_TYPE_POINTER(LPDROPFILES, 20, 1);
+}
+
+static void test_pack_LPFILEDESCRIPTORA(void)
+{
+ /* LPFILEDESCRIPTORA */
+ TEST_TYPE(LPFILEDESCRIPTORA, 4, 4);
+ TEST_TYPE_POINTER(LPFILEDESCRIPTORA, 332, 1);
+}
+
+static void test_pack_LPFILEDESCRIPTORW(void)
+{
+ /* LPFILEDESCRIPTORW */
+ TEST_TYPE(LPFILEDESCRIPTORW, 4, 4);
+ TEST_TYPE_POINTER(LPFILEDESCRIPTORW, 592, 1);
+}
+
+static void test_pack_LPFILEGROUPDESCRIPTORA(void)
+{
+ /* LPFILEGROUPDESCRIPTORA */
+ TEST_TYPE(LPFILEGROUPDESCRIPTORA, 4, 4);
+ TEST_TYPE_POINTER(LPFILEGROUPDESCRIPTORA, 336, 1);
+}
+
+static void test_pack_LPFILEGROUPDESCRIPTORW(void)
+{
+ /* LPFILEGROUPDESCRIPTORW */
+ TEST_TYPE(LPFILEGROUPDESCRIPTORW, 4, 4);
+ TEST_TYPE_POINTER(LPFILEGROUPDESCRIPTORW, 596, 1);
+}
+
+static void test_pack_LPFNVIEWCALLBACK(void)
+{
+ /* LPFNVIEWCALLBACK */
+ TEST_TYPE(LPFNVIEWCALLBACK, 4, 4);
+}
+
+static void test_pack_LPIDA(void)
+{
+ /* LPIDA */
+ TEST_TYPE(LPIDA, 4, 4);
+ TEST_TYPE_POINTER(LPIDA, 8, 1);
+}
+
+static void test_pack_LPQCMINFO(void)
+{
+ /* LPQCMINFO */
+ TEST_TYPE(LPQCMINFO, 4, 4);
+ TEST_TYPE_POINTER(LPQCMINFO, 20, 4);
+}
+
+static void test_pack_LPSHChangeDWORDAsIDList(void)
+{
+ /* LPSHChangeDWORDAsIDList */
+ TEST_TYPE(LPSHChangeDWORDAsIDList, 4, 4);
+ TEST_TYPE_POINTER(LPSHChangeDWORDAsIDList, 12, 1);
+}
+
+static void test_pack_LPSHChangeProductKeyAsIDList(void)
+{
+ /* LPSHChangeProductKeyAsIDList */
+ TEST_TYPE(LPSHChangeProductKeyAsIDList, 4, 4);
+ TEST_TYPE_POINTER(LPSHChangeProductKeyAsIDList, 82, 1);
+}
+
+static void test_pack_LPSHDESCRIPTIONID(void)
+{
+ /* LPSHDESCRIPTIONID */
+ TEST_TYPE(LPSHDESCRIPTIONID, 4, 4);
+ TEST_TYPE_POINTER(LPSHDESCRIPTIONID, 20, 4);
+}
+
+static void test_pack_LPSHELLFLAGSTATE(void)
+{
+ /* LPSHELLFLAGSTATE */
+ TEST_TYPE(LPSHELLFLAGSTATE, 4, 4);
+ TEST_TYPE_POINTER(LPSHELLFLAGSTATE, 4, 1);
+}
+
+static void test_pack_LPSHELLSTATE(void)
+{
+ /* LPSHELLSTATE */
+ TEST_TYPE(LPSHELLSTATE, 4, 4);
+ TEST_TYPE_POINTER(LPSHELLSTATE, 32, 1);
+}
+
+static void test_pack_LPTBINFO(void)
+{
+ /* LPTBINFO */
+ TEST_TYPE(LPTBINFO, 4, 4);
+ TEST_TYPE_POINTER(LPTBINFO, 8, 4);
+}
+
+static void test_pack_PBROWSEINFOA(void)
+{
+ /* PBROWSEINFOA */
+ TEST_TYPE(PBROWSEINFOA, 4, 4);
+ TEST_TYPE_POINTER(PBROWSEINFOA, 32, 4);
+}
+
+static void test_pack_PBROWSEINFOW(void)
+{
+ /* PBROWSEINFOW */
+ TEST_TYPE(PBROWSEINFOW, 4, 4);
+ TEST_TYPE_POINTER(PBROWSEINFOW, 32, 4);
+}
+
+static void test_pack_QCMINFO(void)
+{
+ /* QCMINFO (pack 8) */
+ TEST_TYPE(QCMINFO, 20, 4);
+ TEST_FIELD(QCMINFO, HMENU, hmenu, 0, 4, 4);
+ TEST_FIELD(QCMINFO, UINT, indexMenu, 4, 4, 4);
+ TEST_FIELD(QCMINFO, UINT, idCmdFirst, 8, 4, 4);
+ TEST_FIELD(QCMINFO, UINT, idCmdLast, 12, 4, 4);
+ TEST_FIELD(QCMINFO, QCMINFO_IDMAP const*, pIdMap, 16, 4, 4);
+}
+
+static void test_pack_QCMINFO_IDMAP(void)
+{
+ /* QCMINFO_IDMAP (pack 8) */
+ TEST_TYPE(QCMINFO_IDMAP, 12, 4);
+ TEST_FIELD(QCMINFO_IDMAP, UINT, nMaxIds, 0, 4, 4);
+ TEST_FIELD(QCMINFO_IDMAP, QCMINFO_IDMAP_PLACEMENT[1], pIdList, 4, 8, 4);
+}
+
+static void test_pack_QCMINFO_IDMAP_PLACEMENT(void)
+{
+ /* QCMINFO_IDMAP_PLACEMENT (pack 8) */
+ TEST_TYPE(QCMINFO_IDMAP_PLACEMENT, 8, 4);
+ TEST_FIELD(QCMINFO_IDMAP_PLACEMENT, UINT, id, 0, 4, 4);
+ TEST_FIELD(QCMINFO_IDMAP_PLACEMENT, UINT, fFlags, 4, 4, 4);
+}
+
+static void test_pack_SHChangeDWORDAsIDList(void)
+{
+ /* SHChangeDWORDAsIDList (pack 1) */
+ TEST_TYPE(SHChangeDWORDAsIDList, 12, 1);
+ TEST_FIELD(SHChangeDWORDAsIDList, USHORT, cb, 0, 2, 1);
+ TEST_FIELD(SHChangeDWORDAsIDList, DWORD, dwItem1, 2, 4, 1);
+ TEST_FIELD(SHChangeDWORDAsIDList, DWORD, dwItem2, 6, 4, 1);
+ TEST_FIELD(SHChangeDWORDAsIDList, USHORT, cbZero, 10, 2, 1);
+}
+
+static void test_pack_SHChangeNotifyEntry(void)
+{
+ /* SHChangeNotifyEntry (pack 1) */
+ TEST_TYPE(SHChangeNotifyEntry, 8, 1);
+ TEST_FIELD(SHChangeNotifyEntry, LPCITEMIDLIST, pidl, 0, 4, 1);
+ TEST_FIELD(SHChangeNotifyEntry, BOOL, fRecursive, 4, 4, 1);
+}
+
+static void test_pack_SHChangeProductKeyAsIDList(void)
+{
+ /* SHChangeProductKeyAsIDList (pack 1) */
+ TEST_TYPE(SHChangeProductKeyAsIDList, 82, 1);
+ TEST_FIELD(SHChangeProductKeyAsIDList, USHORT, cb, 0, 2, 1);
+ TEST_FIELD(SHChangeProductKeyAsIDList, WCHAR[39], wszProductKey, 2, 78, 1);
+ TEST_FIELD(SHChangeProductKeyAsIDList, USHORT, cbZero, 80, 2, 1);
+}
+
+static void test_pack_SHDESCRIPTIONID(void)
+{
+ /* SHDESCRIPTIONID (pack 8) */
+ TEST_TYPE(SHDESCRIPTIONID, 20, 4);
+ TEST_FIELD(SHDESCRIPTIONID, DWORD, dwDescriptionId, 0, 4, 4);
+ TEST_FIELD(SHDESCRIPTIONID, CLSID, clsid, 4, 16, 4);
+}
+
+static void test_pack_SHELLFLAGSTATE(void)
+{
+ /* SHELLFLAGSTATE (pack 1) */
+ TEST_TYPE(SHELLFLAGSTATE, 4, 1);
+}
+
+static void test_pack_SHELLSTATE(void)
+{
+ /* SHELLSTATE (pack 1) */
+ TEST_TYPE(SHELLSTATE, 32, 1);
+ TEST_FIELD(SHELLSTATE, DWORD, dwWin95Unused, 4, 4, 1);
+ TEST_FIELD(SHELLSTATE, UINT, uWin95Unused, 8, 4, 1);
+ TEST_FIELD(SHELLSTATE, LONG, lParamSort, 12, 4, 1);
+ TEST_FIELD(SHELLSTATE, int, iSortDirection, 16, 4, 1);
+ TEST_FIELD(SHELLSTATE, UINT, version, 20, 4, 1);
+ TEST_FIELD(SHELLSTATE, UINT, uNotUsed, 24, 4, 1);
+}
+
+static void test_pack_SHELLVIEWID(void)
+{
+ /* SHELLVIEWID */
+ TEST_TYPE(SHELLVIEWID, 16, 4);
+}
+
+static void test_pack_TBINFO(void)
+{
+ /* TBINFO (pack 8) */
+ TEST_TYPE(TBINFO, 8, 4);
+ TEST_FIELD(TBINFO, UINT, cbuttons, 0, 4, 4);
+ TEST_FIELD(TBINFO, UINT, uFlags, 4, 4, 4);
+}
+
+static void test_pack(void)
+{
+ test_pack_APPBARDATA();
+ test_pack_AUTO_SCROLL_DATA();
+ test_pack_BFFCALLBACK();
+ test_pack_BLOB();
+ test_pack_BROWSEINFOA();
+ test_pack_BROWSEINFOW();
+ test_pack_BSTR();
+ test_pack_BSTRBLOB();
+ test_pack_BYTE_BLOB();
+ test_pack_BYTE_SIZEDARR();
+ test_pack_CABINETSTATE();
+ test_pack_CIDA();
+ test_pack_CLIPDATA();
+ test_pack_CLIPFORMAT();
+ test_pack_CLSID();
+ test_pack_COAUTHIDENTITY();
+ test_pack_COAUTHINFO();
+ test_pack_COSERVERINFO();
+ test_pack_CSFV();
+ test_pack_DRAGINFOA();
+ test_pack_DRAGINFOW();
+ test_pack_DROPFILES();
+ test_pack_DWORD_SIZEDARR();
+ test_pack_FILEDESCRIPTORA();
+ test_pack_FILEDESCRIPTORW();
+ test_pack_FILEGROUPDESCRIPTORA();
+ test_pack_FILEGROUPDESCRIPTORW();
+ test_pack_FILEOP_FLAGS();
+ test_pack_FLAGGED_BYTE_BLOB();
+ test_pack_FLAGGED_WORD_BLOB();
+ test_pack_FMTID();
+ test_pack_GUID();
+ test_pack_HMETAFILEPICT();
+ test_pack_HYPER_SIZEDARR();
+ test_pack_IFileSystemBindData();
+ test_pack_IFileSystemBindDataVtbl();
+ test_pack_IID();
+ test_pack_IShellChangeNotify();
+ test_pack_IShellIcon();
+ test_pack_ITEMIDLIST();
+ test_pack_LPBLOB();
+ test_pack_LPBROWSEINFOA();
+ test_pack_LPBROWSEINFOW();
+ test_pack_LPBSTR();
+ test_pack_LPBSTRBLOB();
+ test_pack_LPCABINETSTATE();
+ test_pack_LPCITEMIDLIST();
+ test_pack_LPCOLESTR();
+ test_pack_LPCSFV();
+ test_pack_LPCSHITEMID();
+ test_pack_LPCY();
+ test_pack_LPDECIMAL();
+ test_pack_LPDRAGINFOA();
+ test_pack_LPDRAGINFOW();
+ test_pack_LPDROPFILES();
+ test_pack_LPFILEDESCRIPTORA();
+ test_pack_LPFILEDESCRIPTORW();
+ test_pack_LPFILEGROUPDESCRIPTORA();
+ test_pack_LPFILEGROUPDESCRIPTORW();
+ test_pack_LPFNVIEWCALLBACK();
+ test_pack_LPGUID();
+ test_pack_LPIDA();
+ test_pack_LPITEMIDLIST();
+ test_pack_LPOLESTR();
+ test_pack_LPQCMINFO();
+ test_pack_LPSHChangeDWORDAsIDList();
+ test_pack_LPSHChangeProductKeyAsIDList();
+ test_pack_LPSHDESCRIPTIONID();
+ test_pack_LPSHELLDETAILS();
+ test_pack_LPSHELLEXECUTEINFOA();
+ test_pack_LPSHELLEXECUTEINFOW();
+ test_pack_LPSHELLFLAGSTATE();
+ test_pack_LPSHELLSTATE();
+ test_pack_LPSHFILEOPSTRUCTA();
+ test_pack_LPSHFILEOPSTRUCTW();
+ test_pack_LPSHITEMID();
+ test_pack_LPSHNAMEMAPPINGA();
+ test_pack_LPSHNAMEMAPPINGW();
+ test_pack_LPSTRRET();
+ test_pack_LPTBINFO();
+ test_pack_NOTIFYICONDATAA();
+ test_pack_NOTIFYICONDATAW();
+ test_pack_OLECHAR();
+ test_pack_PAPPBARDATA();
+ test_pack_PBROWSEINFOA();
+ test_pack_PBROWSEINFOW();
+ test_pack_PNOTIFYICONDATAA();
+ test_pack_PNOTIFYICONDATAW();
+ test_pack_PRINTEROP_FLAGS();
+ test_pack_PROPID();
+ test_pack_QCMINFO();
+ test_pack_QCMINFO_IDMAP();
+ test_pack_QCMINFO_IDMAP_PLACEMENT();
+ test_pack_RemHBITMAP();
+ test_pack_RemHENHMETAFILE();
+ test_pack_RemHGLOBAL();
+ test_pack_RemHMETAFILEPICT();
+ test_pack_RemHPALETTE();
+ test_pack_SCODE();
+ test_pack_SHChangeDWORDAsIDList();
+ test_pack_SHChangeNotifyEntry();
+ test_pack_SHChangeProductKeyAsIDList();
+ test_pack_SHDESCRIPTIONID();
+ test_pack_SHELLDETAILS();
+ test_pack_SHELLEXECUTEINFOA();
+ test_pack_SHELLEXECUTEINFOW();
+ test_pack_SHELLFLAGSTATE();
+ test_pack_SHELLSTATE();
+ test_pack_SHELLVIEWID();
+ test_pack_SHFILEINFOA();
+ test_pack_SHFILEINFOW();
+ test_pack_SHFILEOPSTRUCTA();
+ test_pack_SHFILEOPSTRUCTW();
+ test_pack_SHITEMID();
+ test_pack_SHNAMEMAPPINGA();
+ test_pack_SHNAMEMAPPINGW();
+ test_pack_STRRET();
+ test_pack_TBINFO();
+ test_pack_UP_BYTE_BLOB();
+ test_pack_UP_FLAGGED_BYTE_BLOB();
+ test_pack_UP_FLAGGED_WORD_BLOB();
+ test_pack_VARIANT_BOOL();
+ test_pack_VARTYPE();
+ test_pack_WORD_SIZEDARR();
+ test_pack_remoteMETAFILEPICT();
+ test_pack_userBITMAP();
+ test_pack_userCLIPFORMAT();
+ test_pack_userHBITMAP();
+ test_pack_userHENHMETAFILE();
+ test_pack_userHGLOBAL();
+ test_pack_userHMETAFILE();
+ test_pack_userHMETAFILEPICT();
+ test_pack_userHPALETTE();
+ test_pack_wireBSTR();
+ test_pack_wireCLIPFORMAT();
+ test_pack_wireHBITMAP();
+ test_pack_wireHENHMETAFILE();
+ test_pack_wireHGLOBAL();
+ test_pack_wireHMETAFILE();
+ test_pack_wireHMETAFILEPICT();
+ test_pack_wireHPALETTE();
+}
+
+START_TEST(generated)
+{
+ test_pack();
+}
--- /dev/null
+<module name="shell32_winetest" type="win32cui" installbase="bin" installname="shell32_winetest.exe" allowwarnings="true">
+ <include base="shell32_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>ntdll</library>
+ <library>shell32</library>
+ <library>kernel32</library>
+ <library>advapi32</library>
+ <library>shlwapi</library>
+ <library>ole32</library>
+ <file>shelllink.c</file>
+ <file>shellpath.c</file>
+ <file>shlexec.c</file>
+ <file>shlfileop.c</file>
+ <file>shlfolder.c</file>
+ <file>string.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * Unit test suite for shell32 functions
+ *
+ * Copyright 2005 Francois Gougett for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/* Helper function for creating .lnk files */
+typedef struct
+{
+ const char* description;
+ const char* workdir;
+ const char* path;
+ LPITEMIDLIST pidl;
+ const char* arguments;
+ int showcmd;
+ const char* icon;
+ int icon_id;
+ WORD hotkey;
+} lnk_desc_t;
+
+#define create_lnk(a,b,c) create_lnk_(__LINE__, (a), (b), (c))
+void create_lnk_(int,const WCHAR*,lnk_desc_t*,int);
--- /dev/null
+/*
+ * Unit tests for shelllinks
+ *
+ * Copyright 2004 Mike McCormack
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * This is a test program for the SHGet{Special}Folder{Path|Location} functions
+ * of shell32, that get either a filesytem path or a LPITEMIDLIST (shell
+ * namespace) path for a given folder (CSIDL value).
+ *
+ */
+
+#define _WIN32_IE 0x0400
+
+#define COBJMACROS
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "windef.h"
+#include "winbase.h"
+#include "basetyps.h"
+#include "shlguid.h"
+//#include "wine/shobjidl.h"
+#include "shlobj.h"
+#include "wine/test.h"
+
+#include "shell32_test.h"
+
+extern BOOL WINAPI ILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
+extern HRESULT WINAPI SHILCreateFromPath(LPCWSTR path, LPITEMIDLIST * ppidl, DWORD * attributes);
+extern void WINAPI ILFree(LPITEMIDLIST pidl);
+
+static const WCHAR lnkfile[]= { 'C',':','\\','t','e','s','t','.','l','n','k',0 };
+static const WCHAR notafile[]= { 'C',':','\\','n','o','n','e','x','i','s','t','e','n','t','\\','f','i','l','e',0 };
+
+const GUID IID_IPersistFile = { 0x0000010b, 0x0000, 0x0000, { 0xc0,0x00, 0x00,0x00,0x00,0x00,0x00,0x46 } };
+
+/* For some reason SHILCreateFromPath does not work on Win98 and
+ * SHSimpleIDListFromPathA does not work on NT4. But if we call both we
+ * get what we want on all platforms.
+ */
+static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathA)(LPCSTR)=NULL;
+
+static LPITEMIDLIST path_to_pidl(const char* path)
+{
+ LPITEMIDLIST pidl;
+
+ if (!pSHSimpleIDListFromPathA)
+ {
+ HMODULE hdll=LoadLibraryA("shell32.dll");
+ pSHSimpleIDListFromPathA=(void*)GetProcAddress(hdll, (char*)162);
+ if (!pSHSimpleIDListFromPathA)
+ trace("SHSimpleIDListFromPathA not found in shell32.dll\n");
+ }
+
+ pidl=NULL;
+ if (pSHSimpleIDListFromPathA)
+ pidl=pSHSimpleIDListFromPathA(path);
+
+ if (!pidl)
+ {
+ WCHAR* pathW;
+ HRESULT r;
+ int len;
+
+ len=MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
+ pathW=HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len);
+
+ r=SHILCreateFromPath(pathW, &pidl, NULL);
+ todo_wine {
+ ok(SUCCEEDED(r), "SHILCreateFromPath failed (0x%08lx)\n", r);
+ }
+ HeapFree(GetProcessHeap(), 0, pathW);
+ }
+ return pidl;
+}
+
+
+/*
+ * Test manipulation of an IShellLink's properties.
+ */
+
+static void test_get_set(void)
+{
+ HRESULT r;
+ IShellLinkA *sl;
+ char mypath[MAX_PATH];
+ char buffer[INFOTIPSIZE];
+ LPITEMIDLIST pidl, tmp_pidl;
+ const char * str;
+ int i;
+ WORD w;
+
+ r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkA, (LPVOID*)&sl);
+ ok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+ if (!SUCCEEDED(r))
+ return;
+
+ /* Test Getting / Setting the description */
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
+ ok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
+ ok(*buffer=='\0', "GetDescription returned '%s'\n", buffer);
+
+ str="Some description";
+ r = IShellLinkA_SetDescription(sl, str);
+ ok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
+
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
+ ok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
+ ok(lstrcmp(buffer,str)==0, "GetDescription returned '%s'\n", buffer);
+
+ /* Test Getting / Setting the work directory */
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
+ ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
+ ok(*buffer=='\0', "GetWorkingDirectory returned '%s'\n", buffer);
+
+ str="c:\\nonexistent\\directory";
+ r = IShellLinkA_SetWorkingDirectory(sl, str);
+ ok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08lx)\n", r);
+
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
+ ok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
+ ok(lstrcmpi(buffer,str)==0, "GetWorkingDirectory returned '%s'\n", buffer);
+
+ /* Test Getting / Setting the work directory */
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
+ ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+ ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
+
+ r = IShellLinkA_SetPath(sl, "");
+ ok(r==S_OK, "SetPath failed (0x%08lx)\n", r);
+
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
+ ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+ ok(*buffer=='\0', "GetPath returned '%s'\n", buffer);
+
+ str="c:\\nonexistent\\file";
+ r = IShellLinkA_SetPath(sl, str);
+ ok(r==S_FALSE, "SetPath failed (0x%08lx)\n", r);
+
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
+ ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+ ok(lstrcmpi(buffer,str)==0, "GetPath returned '%s'\n", buffer);
+
+ /* Get some a real path to play with */
+ r=GetModuleFileName(NULL, mypath, sizeof(mypath));
+ ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%ld)\n", r);
+
+ /* Test the interaction of SetPath and SetIDList */
+ tmp_pidl=NULL;
+ r = IShellLinkA_GetIDList(sl, &tmp_pidl);
+ ok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
+ if (SUCCEEDED(r))
+ {
+ strcpy(buffer,"garbage");
+ r=SHGetPathFromIDListA(tmp_pidl, buffer);
+ todo_wine {
+ ok(r, "SHGetPathFromIDListA failed\n");
+ }
+ if (r)
+ ok(lstrcmpi(buffer,str)==0, "GetIDList returned '%s'\n", buffer);
+ }
+
+ pidl=path_to_pidl(mypath);
+ todo_wine {
+ ok(pidl!=NULL, "path_to_pidl returned a NULL pidl\n");
+ }
+
+ if (pidl)
+ {
+ r = IShellLinkA_SetIDList(sl, pidl);
+ ok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\n", r);
+
+ tmp_pidl=NULL;
+ r = IShellLinkA_GetIDList(sl, &tmp_pidl);
+ ok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
+ ok(tmp_pidl && ILIsEqual(pidl, tmp_pidl),
+ "GetIDList returned an incorrect pidl\n");
+
+ /* tmp_pidl is owned by IShellLink so we don't free it */
+ ILFree(pidl);
+
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
+ ok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+ ok(lstrcmpi(buffer, mypath)==0, "GetPath returned '%s'\n", buffer);
+ }
+
+ /* Test Getting / Setting the arguments */
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
+ ok(SUCCEEDED(r), "GetArguments failed (0x%08lx)\n", r);
+ ok(*buffer=='\0', "GetArguments returned '%s'\n", buffer);
+
+ str="param1 \"spaced param2\"";
+ r = IShellLinkA_SetArguments(sl, str);
+ ok(SUCCEEDED(r), "SetArguments failed (0x%08lx)\n", r);
+
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetArguments(sl, buffer, sizeof(buffer));
+ ok(SUCCEEDED(r), "GetArguments failed (0x%08lx)\n", r);
+ ok(lstrcmp(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
+
+ /* Test Getting / Setting showcmd */
+ i=0xdeadbeef;
+ r = IShellLinkA_GetShowCmd(sl, &i);
+ ok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
+ ok(i==SW_SHOWNORMAL, "GetShowCmd returned %d\n", i);
+
+ r = IShellLinkA_SetShowCmd(sl, SW_SHOWMAXIMIZED);
+ ok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
+
+ i=0xdeadbeef;
+ r = IShellLinkA_GetShowCmd(sl, &i);
+ ok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
+ ok(i==SW_SHOWMAXIMIZED, "GetShowCmd returned %d'\n", i);
+
+ /* Test Getting / Setting the icon */
+ i=0xdeadbeef;
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
+ todo_wine {
+ ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
+ }
+ ok(*buffer=='\0', "GetIconLocation returned '%s'\n", buffer);
+ ok(i==0, "GetIconLocation returned %d\n", i);
+
+ str="c:\\nonexistent\\file";
+ r = IShellLinkA_SetIconLocation(sl, str, 0xbabecafe);
+ ok(SUCCEEDED(r), "SetIconLocation failed (0x%08lx)\n", r);
+
+ i=0xdeadbeef;
+ r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
+ ok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
+ ok(lstrcmpi(buffer,str)==0, "GetArguments returned '%s'\n", buffer);
+ ok(i==0xbabecafe, "GetIconLocation returned %d'\n", i);
+
+ /* Test Getting / Setting the hot key */
+ w=0xbeef;
+ r = IShellLinkA_GetHotkey(sl, &w);
+ ok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
+ ok(w==0, "GetHotkey returned %d\n", w);
+
+ r = IShellLinkA_SetHotkey(sl, 0x5678);
+ ok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
+
+ w=0xbeef;
+ r = IShellLinkA_GetHotkey(sl, &w);
+ ok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
+ ok(w==0x5678, "GetHotkey returned %d'\n", w);
+
+ IShellLinkA_Release(sl);
+}
+
+
+/*
+ * Test saving and loading .lnk files
+ */
+
+#define lok ok_(__FILE__, line)
+#define check_lnk(a,b) check_lnk_(__LINE__, (a), (b))
+
+void create_lnk_(int line, const WCHAR* path, lnk_desc_t* desc, int save_fails)
+{
+ HRESULT r;
+ IShellLinkA *sl;
+ IPersistFile *pf;
+
+ r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkA, (LPVOID*)&sl);
+ lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+ if (!SUCCEEDED(r))
+ return;
+
+ if (desc->description)
+ {
+ r = IShellLinkA_SetDescription(sl, desc->description);
+ lok(SUCCEEDED(r), "SetDescription failed (0x%08lx)\n", r);
+ }
+ if (desc->workdir)
+ {
+ r = IShellLinkA_SetWorkingDirectory(sl, desc->workdir);
+ lok(SUCCEEDED(r), "SetWorkingDirectory failed (0x%08lx)\n", r);
+ }
+ if (desc->path)
+ {
+ r = IShellLinkA_SetPath(sl, desc->path);
+ lok(SUCCEEDED(r), "SetPath failed (0x%08lx)\n", r);
+ }
+ if (desc->pidl)
+ {
+ r = IShellLinkA_SetIDList(sl, desc->pidl);
+ lok(SUCCEEDED(r), "SetIDList failed (0x%08lx)\n", r);
+ }
+ if (desc->arguments)
+ {
+ r = IShellLinkA_SetArguments(sl, desc->arguments);
+ lok(SUCCEEDED(r), "SetArguments failed (0x%08lx)\n", r);
+ }
+ if (desc->showcmd)
+ {
+ r = IShellLinkA_SetShowCmd(sl, desc->showcmd);
+ lok(SUCCEEDED(r), "SetShowCmd failed (0x%08lx)\n", r);
+ }
+ if (desc->icon)
+ {
+ r = IShellLinkA_SetIconLocation(sl, desc->icon, desc->icon_id);
+ lok(SUCCEEDED(r), "SetIconLocation failed (0x%08lx)\n", r);
+ }
+ if (desc->hotkey)
+ {
+ r = IShellLinkA_SetHotkey(sl, desc->hotkey);
+ lok(SUCCEEDED(r), "SetHotkey failed (0x%08lx)\n", r);
+ }
+
+ r = IShellLinkW_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
+ lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\n", r);
+ if (SUCCEEDED(r))
+ {
+ r = IPersistFile_Save(pf, path, TRUE);
+ if (save_fails)
+ {
+ todo_wine {
+ lok(SUCCEEDED(r), "save failed (0x%08lx)\n", r);
+ }
+ }
+ else
+ {
+ lok(SUCCEEDED(r), "save failed (0x%08lx)\n", r);
+ }
+ IPersistFile_Release(pf);
+ }
+
+ IShellLinkA_Release(sl);
+}
+
+static void check_lnk_(int line, const WCHAR* path, lnk_desc_t* desc)
+{
+ HRESULT r;
+ IShellLinkA *sl;
+ IPersistFile *pf;
+ char buffer[INFOTIPSIZE];
+
+ r = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IShellLinkA, (LPVOID*)&sl);
+ lok(SUCCEEDED(r), "no IID_IShellLinkA (0x%08lx)\n", r);
+ if (!SUCCEEDED(r))
+ return;
+
+ r = IShellLinkA_QueryInterface(sl, &IID_IPersistFile, (LPVOID*)&pf);
+ lok(SUCCEEDED(r), "no IID_IPersistFile (0x%08lx)\n", r);
+ if (!SUCCEEDED(r))
+ {
+ IShellLinkA_Release(sl);
+ return;
+ }
+
+ r = IPersistFile_Load(pf, path, STGM_READ);
+ lok(SUCCEEDED(r), "load failed (0x%08lx)\n", r);
+ IPersistFile_Release(pf);
+ if (!SUCCEEDED(r))
+ {
+ IShellLinkA_Release(sl);
+ return;
+ }
+
+ if (desc->description)
+ {
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetDescription(sl, buffer, sizeof(buffer));
+ lok(SUCCEEDED(r), "GetDescription failed (0x%08lx)\n", r);
+ lok(lstrcmp(buffer, desc->description)==0,
+ "GetDescription returned '%s' instead of '%s'\n",
+ buffer, desc->description);
+ }
+ if (desc->workdir)
+ {
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetWorkingDirectory(sl, buffer, sizeof(buffer));
+ lok(SUCCEEDED(r), "GetWorkingDirectory failed (0x%08lx)\n", r);
+ lok(lstrcmpi(buffer, desc->workdir)==0,
+ "GetWorkingDirectory returned '%s' instead of '%s'\n",
+ buffer, desc->workdir);
+ }
+ if (desc->path)
+ {
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetPath(sl, buffer, sizeof(buffer), NULL, SLGP_RAWPATH);
+ lok(SUCCEEDED(r), "GetPath failed (0x%08lx)\n", r);
+ lok(lstrcmpi(buffer, desc->path)==0,
+ "GetPath returned '%s' instead of '%s'\n",
+ buffer, desc->path);
+ }
+ if (desc->pidl)
+ {
+ LPITEMIDLIST pidl=NULL;
+ r = IShellLinkA_GetIDList(sl, &pidl);
+ lok(SUCCEEDED(r), "GetIDList failed (0x%08lx)\n", r);
+ lok(ILIsEqual(pidl, desc->pidl),
+ "GetIDList returned an incorrect pidl\n");
+ }
+ if (desc->showcmd)
+ {
+ int i=0xdeadbeef;
+ r = IShellLinkA_GetShowCmd(sl, &i);
+ lok(SUCCEEDED(r), "GetShowCmd failed (0x%08lx)\n", r);
+ lok(i==desc->showcmd,
+ "GetShowCmd returned 0x%0x instead of 0x%0x\n",
+ i, desc->showcmd);
+ }
+ if (desc->icon)
+ {
+ int i=0xdeadbeef;
+ strcpy(buffer,"garbage");
+ r = IShellLinkA_GetIconLocation(sl, buffer, sizeof(buffer), &i);
+ lok(SUCCEEDED(r), "GetIconLocation failed (0x%08lx)\n", r);
+ lok(lstrcmpi(buffer, desc->icon)==0,
+ "GetIconLocation returned '%s' instead of '%s'\n",
+ buffer, desc->icon);
+ lok(i==desc->icon_id,
+ "GetIconLocation returned 0x%0x instead of 0x%0x\n",
+ i, desc->icon_id);
+ }
+ if (desc->hotkey)
+ {
+ WORD i=0xbeef;
+ r = IShellLinkA_GetHotkey(sl, &i);
+ lok(SUCCEEDED(r), "GetHotkey failed (0x%08lx)\n", r);
+ lok(i==desc->hotkey,
+ "GetHotkey returned 0x%04x instead of 0x%04x\n",
+ i, desc->hotkey);
+ }
+
+ IShellLinkA_Release(sl);
+}
+
+static void test_load_save(void)
+{
+ lnk_desc_t desc;
+ char mypath[MAX_PATH];
+ char mydir[MAX_PATH];
+ char* p;
+ DWORD r;
+
+ /* Save an empty .lnk file */
+ memset(&desc, 0, sizeof(desc));
+ create_lnk(lnkfile, &desc, 0);
+
+ /* It should come back as a bunch of empty strings */
+ desc.description="";
+ desc.workdir="";
+ desc.path="";
+ desc.arguments="";
+ desc.icon="";
+ check_lnk(lnkfile, &desc);
+
+
+ /* Point a .lnk file to nonexistent files */
+ desc.description="";
+ desc.workdir="c:\\Nonexitent\\work\\directory";
+ desc.path="c:\\nonexistent\\path";
+ desc.pidl=NULL;
+ desc.arguments="";
+ desc.showcmd=0;
+ desc.icon="c:\\nonexistent\\icon\\file";
+ desc.icon_id=1234;
+ desc.hotkey=0;
+ create_lnk(lnkfile, &desc, 0);
+ check_lnk(lnkfile, &desc);
+
+ r=GetModuleFileName(NULL, mypath, sizeof(mypath));
+ ok(r>=0 && r<sizeof(mypath), "GetModuleFileName failed (%ld)\n", r);
+ strcpy(mydir, mypath);
+ p=strrchr(mydir, '\\');
+ if (p)
+ *p='\0';
+
+
+ /* Overwrite the existing lnk file and point it to existing files */
+ desc.description="test 2";
+ desc.workdir=mydir;
+ desc.path=mypath;
+ desc.pidl=NULL;
+ desc.arguments="/option1 /option2 \"Some string\"";
+ desc.showcmd=SW_SHOWNORMAL;
+ desc.icon=mypath;
+ desc.icon_id=0;
+ desc.hotkey=0x1234;
+ create_lnk(lnkfile, &desc, 0);
+ check_lnk(lnkfile, &desc);
+
+ /* FIXME: Also test saving a .lnk pointing to a pidl that cannot be
+ * represented as a path.
+ */
+
+ /* DeleteFileW is not implemented on Win9x */
+ r=DeleteFileA("c:\\test.lnk");
+ ok(r, "failed to delete link (%ld)\n", GetLastError());
+}
+
+START_TEST(shelllink)
+{
+ HRESULT r;
+
+ r = CoInitialize(NULL);
+ ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
+ if (!SUCCEEDED(r))
+ return;
+
+ test_get_set();
+ test_load_save();
+
+ CoUninitialize();
+}
--- /dev/null
+/*
+ * Unit tests for shell32 SHGet{Special}Folder{Path|Location} functions.
+ *
+ * Copyright 2004 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * This is a test program for the SHGet{Special}Folder{Path|Location} functions
+ * of shell32, that get either a filesytem path or a LPITEMIDLIST (shell
+ * namespace) path for a given folder (CSIDL value).
+ *
+ * FIXME:
+ * - Need to verify on more systems.
+ */
+
+#define COBJMACROS
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "windef.h"
+#include "winbase.h"
+#include "initguid.h"
+#include "shlguid.h"
+#include "shlobj.h"
+#include "shlwapi.h"
+#include "wine/test.h"
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) )
+#endif
+
+/* from pidl.h, not included here: */
+#ifndef PT_GUID
+#define PT_GUID 0x1f /* no path */
+#endif
+#ifndef PT_DRIVE
+#define PT_DRIVE 0x23 /* has path */
+#endif
+#ifndef PT_DRIVE2
+#define PT_DRIVE2 0x25 /* has path */
+#endif
+#ifndef PT_SHELLEXT
+#define PT_SHELLEXT 0x2e /* no path */
+#endif
+#ifndef PT_FOLDER
+#define PT_FOLDER 0x31 /* has path */
+#endif
+#ifndef PT_WORKGRP
+#define PT_WORKGRP 0x41 /* no path */
+#endif
+#ifndef PT_YAGUID
+#define PT_YAGUID 0x70 /* no path */
+#endif
+/* FIXME: this is used for history/favorites folders; what's a better name? */
+#ifndef PT_IESPECIAL2
+#define PT_IESPECIAL2 0xb1 /* has path */
+#endif
+
+static GUID CLSID_CommonDocuments = { 0x0000000c, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x1a } };
+
+struct shellExpectedValues {
+ int folder;
+ BYTE pidlType;
+};
+
+static HMODULE hShell32;
+static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR);
+static HRESULT (WINAPI *pSHGetFolderLocation)(HWND, int, HANDLE, DWORD,
+ LPITEMIDLIST *);
+static BOOL (WINAPI *pSHGetSpecialFolderPathA)(HWND, LPSTR, int, BOOL);
+static HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
+static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
+static int (WINAPI *pSHFileOperationA)(LPSHFILEOPSTRUCTA);
+static HRESULT (WINAPI *pSHGetMalloc)(LPMALLOC *);
+static DLLVERSIONINFO shellVersion = { 0 };
+static LPMALLOC pMalloc;
+static const struct shellExpectedValues requiredShellValues[] = {
+ { CSIDL_BITBUCKET, PT_GUID },
+ { CSIDL_CONTROLS, PT_SHELLEXT },
+ { CSIDL_COOKIES, PT_FOLDER },
+ { CSIDL_DESKTOPDIRECTORY, PT_FOLDER },
+ { CSIDL_DRIVES, PT_GUID },
+ { CSIDL_FAVORITES, PT_FOLDER },
+ { CSIDL_FONTS, PT_FOLDER },
+/* FIXME: the following fails in Wine, returns type PT_FOLDER
+ { CSIDL_HISTORY, PT_IESPECIAL2 },
+ */
+ { CSIDL_INTERNET, PT_GUID },
+ { CSIDL_NETHOOD, PT_FOLDER },
+ { CSIDL_NETWORK, PT_GUID },
+ { CSIDL_PRINTERS, PT_YAGUID },
+ { CSIDL_PRINTHOOD, PT_FOLDER },
+ { CSIDL_PROGRAMS, PT_FOLDER },
+ { CSIDL_RECENT, PT_FOLDER },
+ { CSIDL_SENDTO, PT_FOLDER },
+ { CSIDL_STARTMENU, PT_FOLDER },
+ { CSIDL_STARTUP, PT_FOLDER },
+ { CSIDL_TEMPLATES, PT_FOLDER },
+};
+static const struct shellExpectedValues optionalShellValues[] = {
+/* FIXME: the following only semi-succeed; they return NULL PIDLs on XP.. hmm.
+ { CSIDL_ALTSTARTUP, PT_FOLDER },
+ { CSIDL_COMMON_ALTSTARTUP, PT_FOLDER },
+ { CSIDL_COMMON_OEM_LINKS, PT_FOLDER },
+ */
+/* Windows NT-only: */
+ { CSIDL_COMMON_DESKTOPDIRECTORY, PT_FOLDER },
+ { CSIDL_COMMON_DOCUMENTS, PT_SHELLEXT },
+ { CSIDL_COMMON_FAVORITES, PT_FOLDER },
+ { CSIDL_COMMON_PROGRAMS, PT_FOLDER },
+ { CSIDL_COMMON_STARTMENU, PT_FOLDER },
+ { CSIDL_COMMON_STARTUP, PT_FOLDER },
+ { CSIDL_COMMON_TEMPLATES, PT_FOLDER },
+/* first appearing in shell32 version 4.71: */
+ { CSIDL_APPDATA, PT_FOLDER },
+/* first appearing in shell32 version 4.72: */
+ { CSIDL_INTERNET_CACHE, PT_IESPECIAL2 },
+/* first appearing in shell32 version 5.0: */
+ { CSIDL_ADMINTOOLS, PT_FOLDER },
+ { CSIDL_COMMON_APPDATA, PT_FOLDER },
+ { CSIDL_LOCAL_APPDATA, PT_FOLDER },
+ { CSIDL_MYDOCUMENTS, PT_FOLDER },
+ { CSIDL_MYMUSIC, PT_FOLDER },
+ { CSIDL_MYPICTURES, PT_FOLDER },
+ { CSIDL_MYVIDEO, PT_FOLDER },
+ { CSIDL_PROFILE, PT_FOLDER },
+ { CSIDL_PROGRAM_FILES, PT_FOLDER },
+ { CSIDL_PROGRAM_FILESX86, PT_FOLDER },
+ { CSIDL_PROGRAM_FILES_COMMON, PT_FOLDER },
+ { CSIDL_PROGRAM_FILES_COMMONX86, PT_FOLDER },
+ { CSIDL_SYSTEM, PT_FOLDER },
+ { CSIDL_WINDOWS, PT_FOLDER },
+/* first appearing in shell32 6.0: */
+ { CSIDL_CDBURN_AREA, PT_FOLDER },
+ { CSIDL_COMMON_MUSIC, PT_FOLDER },
+ { CSIDL_COMMON_PICTURES, PT_FOLDER },
+ { CSIDL_COMMON_VIDEO, PT_FOLDER },
+ { CSIDL_COMPUTERSNEARME, PT_WORKGRP },
+ { CSIDL_RESOURCES, PT_FOLDER },
+ { CSIDL_RESOURCES_LOCALIZED, PT_FOLDER },
+};
+
+static void loadShell32(void)
+{
+ hShell32 = LoadLibraryA("shell32");
+ if (hShell32)
+ {
+ HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO *);
+
+ pSHGetFolderPathA = (void *)GetProcAddress(hShell32,
+ "SHGetFolderPathA");
+ pSHGetFolderLocation = (void *)GetProcAddress(hShell32,
+ "SHGetFolderLocation");
+ pSHGetSpecialFolderPathA = (void *)GetProcAddress(hShell32,
+ "SHGetSpecialFolderPathA");
+ pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32,
+ "SHGetSpecialFolderLocation");
+ pDllGetVersion = (void *)GetProcAddress(hShell32, "DllGetVersion");
+ pILFindLastID = (void *)GetProcAddress(hShell32, "ILFindLastID");
+ if (!pILFindLastID)
+ pILFindLastID = (void *)GetProcAddress(hShell32, (LPCSTR)16);
+ pSHFileOperationA = (void *)GetProcAddress(hShell32,
+ "SHFileOperationA");
+ pSHGetMalloc = (void *)GetProcAddress(hShell32, "SHGetMalloc");
+
+ ok(pSHGetMalloc != NULL, "shell32 is missing SHGetMalloc\n");
+ if (pSHGetMalloc)
+ {
+ HRESULT hr = pSHGetMalloc(&pMalloc);
+
+ ok(SUCCEEDED(hr), "SHGetMalloc failed: 0x%08lx\n", hr);
+ ok(pMalloc != NULL, "SHGetMalloc returned a NULL IMalloc\n");
+ }
+
+ if (pDllGetVersion)
+ {
+ shellVersion.cbSize = sizeof(shellVersion);
+ pDllGetVersion(&shellVersion);
+ if (winetest_interactive)
+ printf("shell32 version is %ld.%ld\n",
+ shellVersion.dwMajorVersion, shellVersion.dwMinorVersion);
+ }
+ }
+}
+
+#ifndef CSIDL_PROFILES
+#define CSIDL_PROFILES 0x003e
+#endif
+
+/* A couple utility printing functions */
+static const char *getFolderName(int folder)
+{
+ static char unknown[17];
+
+#define CSIDL_TO_STR(x) case x: return#x;
+ switch (folder)
+ {
+ CSIDL_TO_STR(CSIDL_DESKTOP);
+ CSIDL_TO_STR(CSIDL_INTERNET);
+ CSIDL_TO_STR(CSIDL_PROGRAMS);
+ CSIDL_TO_STR(CSIDL_CONTROLS);
+ CSIDL_TO_STR(CSIDL_PRINTERS);
+ CSIDL_TO_STR(CSIDL_PERSONAL);
+ CSIDL_TO_STR(CSIDL_FAVORITES);
+ CSIDL_TO_STR(CSIDL_STARTUP);
+ CSIDL_TO_STR(CSIDL_RECENT);
+ CSIDL_TO_STR(CSIDL_SENDTO);
+ CSIDL_TO_STR(CSIDL_BITBUCKET);
+ CSIDL_TO_STR(CSIDL_STARTMENU);
+ CSIDL_TO_STR(CSIDL_MYDOCUMENTS);
+ CSIDL_TO_STR(CSIDL_MYMUSIC);
+ CSIDL_TO_STR(CSIDL_MYVIDEO);
+ CSIDL_TO_STR(CSIDL_DESKTOPDIRECTORY);
+ CSIDL_TO_STR(CSIDL_DRIVES);
+ CSIDL_TO_STR(CSIDL_NETWORK);
+ CSIDL_TO_STR(CSIDL_NETHOOD);
+ CSIDL_TO_STR(CSIDL_FONTS);
+ CSIDL_TO_STR(CSIDL_TEMPLATES);
+ CSIDL_TO_STR(CSIDL_COMMON_STARTMENU);
+ CSIDL_TO_STR(CSIDL_COMMON_PROGRAMS);
+ CSIDL_TO_STR(CSIDL_COMMON_STARTUP);
+ CSIDL_TO_STR(CSIDL_COMMON_DESKTOPDIRECTORY);
+ CSIDL_TO_STR(CSIDL_APPDATA);
+ CSIDL_TO_STR(CSIDL_PRINTHOOD);
+ CSIDL_TO_STR(CSIDL_LOCAL_APPDATA);
+ CSIDL_TO_STR(CSIDL_ALTSTARTUP);
+ CSIDL_TO_STR(CSIDL_COMMON_ALTSTARTUP);
+ CSIDL_TO_STR(CSIDL_COMMON_FAVORITES);
+ CSIDL_TO_STR(CSIDL_INTERNET_CACHE);
+ CSIDL_TO_STR(CSIDL_COOKIES);
+ CSIDL_TO_STR(CSIDL_HISTORY);
+ CSIDL_TO_STR(CSIDL_COMMON_APPDATA);
+ CSIDL_TO_STR(CSIDL_WINDOWS);
+ CSIDL_TO_STR(CSIDL_SYSTEM);
+ CSIDL_TO_STR(CSIDL_PROGRAM_FILES);
+ CSIDL_TO_STR(CSIDL_MYPICTURES);
+ CSIDL_TO_STR(CSIDL_PROFILE);
+ CSIDL_TO_STR(CSIDL_SYSTEMX86);
+ CSIDL_TO_STR(CSIDL_PROGRAM_FILESX86);
+ CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMON);
+ CSIDL_TO_STR(CSIDL_PROGRAM_FILES_COMMONX86);
+ CSIDL_TO_STR(CSIDL_COMMON_TEMPLATES);
+ CSIDL_TO_STR(CSIDL_COMMON_DOCUMENTS);
+ CSIDL_TO_STR(CSIDL_COMMON_ADMINTOOLS);
+ CSIDL_TO_STR(CSIDL_ADMINTOOLS);
+ CSIDL_TO_STR(CSIDL_CONNECTIONS);
+ CSIDL_TO_STR(CSIDL_PROFILES);
+ CSIDL_TO_STR(CSIDL_COMMON_MUSIC);
+ CSIDL_TO_STR(CSIDL_COMMON_PICTURES);
+ CSIDL_TO_STR(CSIDL_COMMON_VIDEO);
+ CSIDL_TO_STR(CSIDL_RESOURCES);
+ CSIDL_TO_STR(CSIDL_RESOURCES_LOCALIZED);
+ CSIDL_TO_STR(CSIDL_COMMON_OEM_LINKS);
+ CSIDL_TO_STR(CSIDL_CDBURN_AREA);
+ CSIDL_TO_STR(CSIDL_COMPUTERSNEARME);
+#undef CSIDL_TO_STR
+ default:
+ wnsprintfA(unknown, sizeof(unknown), "unknown (0x%04x)", folder);
+ return unknown;
+ }
+}
+
+static const char *printGUID(const GUID *guid)
+{
+ static char guidSTR[39];
+
+ if (!guid) return NULL;
+
+ wnsprintfA(guidSTR, sizeof(guidSTR),
+ "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ guid->Data1, guid->Data2, guid->Data3,
+ guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
+ guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+ return guidSTR;
+}
+
+static void testSHGetFolderLocationInvalidArgs(void)
+{
+ LPITEMIDLIST pidl;
+ HRESULT hr;
+
+ if (!pSHGetFolderLocation) return;
+
+ /* check a bogus CSIDL: */
+ pidl = NULL;
+ hr = pSHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl);
+ ok(hr == E_INVALIDARG,
+ "SHGetFolderLocation(NULL, 0xeeee, NULL, 0, &pidl)\n"
+ "returned 0x%08lx, expected E_INVALIDARG\n", hr);
+ if (SUCCEEDED(hr))
+ IMalloc_Free(pMalloc, pidl);
+ /* check a bogus user token: */
+ pidl = NULL;
+ hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, (HANDLE)2, 0, &pidl);
+ ok(hr == E_FAIL,
+ "SHGetFolderLocation(NULL, CSIDL_FAVORITES, 2, 0, &pidl)\n"
+ "returned 0x%08lx, expected E_FAIL\n", hr);
+ if (SUCCEEDED(hr))
+ IMalloc_Free(pMalloc, pidl);
+ /* check reserved is not zero: */
+ pidl = NULL;
+ hr = pSHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl);
+ ok(hr == E_INVALIDARG,
+ "SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, 1, &pidl)\n"
+ "returned 0x%08lx, expected E_INVALIDARG\n", hr);
+ if (SUCCEEDED(hr))
+ IMalloc_Free(pMalloc, pidl);
+ /* a NULL pidl pointer crashes, so don't test it */
+}
+
+static void testSHGetSpecialFolderLocationInvalidArgs(void)
+{
+ LPITEMIDLIST pidl = NULL;
+ HRESULT hr;
+
+ if (!pSHGetSpecialFolderLocation) return;
+
+ /* SHGetSpecialFolderLocation(NULL, 0, NULL) crashes */
+ hr = pSHGetSpecialFolderLocation(NULL, 0xeeee, &pidl);
+ ok(hr == E_INVALIDARG,
+ "SHGetSpecialFolderLocation(NULL, 0xeeee, &pidl) returned 0x%08lx, "
+ "expected E_INVALIDARG\n", hr);
+}
+
+static void testSHGetFolderPathInvalidArgs(void)
+{
+ char path[MAX_PATH];
+ HRESULT hr;
+
+ if (!pSHGetFolderPathA) return;
+
+ /* expect 2's a bogus handle, especially since we didn't open it */
+ hr = pSHGetFolderPathA(NULL, CSIDL_DESKTOP, (HANDLE)2,
+ SHGFP_TYPE_DEFAULT, path);
+ ok(hr == E_FAIL,
+ "SHGetFolderPathA(NULL, CSIDL_DESKTOP, 2, SHGFP_TYPE_DEFAULT, path)\n"
+ "returned 0x%08lx, expected E_FAIL\n", hr);
+ hr = pSHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path);
+ ok(hr == E_INVALIDARG,
+ "SHGetFolderPathA(NULL, 0xeeee, NULL, SHGFP_TYPE_DEFAULT, path)\n"
+ "returned 0x%08lx, expected E_INVALIDARG\n", hr);
+}
+
+static void testSHGetSpecialFolderPathInvalidArgs(void)
+{
+ char path[MAX_PATH];
+ BOOL ret;
+
+ if (!pSHGetSpecialFolderPathA) return;
+
+ ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE);
+ ok(!ret,
+ "SHGetSpecialFolderPathA(NULL, NULL, CSIDL_BITBUCKET, FALSE)\n"
+ "returned TRUE, expected FALSE\n");
+ /* odd but true: calling with a NULL path still succeeds if it's a real
+ * dir
+ */
+ ret = pSHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE);
+ ok(ret,
+ "SHGetSpecialFolderPathA(NULL, NULL, CSIDL_PROGRAMS, FALSE)\n"
+ "returned FALSE, expected TRUE\n");
+ ret = pSHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE);
+ ok(!ret,
+ "SHGetSpecialFolderPathA(NULL, path, 0xeeee, FALSE)\n"
+ "returned TRUE, expected FALSE\n");
+}
+
+static void testApiParameters(void)
+{
+ testSHGetFolderLocationInvalidArgs();
+ testSHGetSpecialFolderLocationInvalidArgs();
+ testSHGetFolderPathInvalidArgs();
+ testSHGetSpecialFolderPathInvalidArgs();
+}
+
+/* Returns the folder's PIDL type, or 0xff if one can't be found. */
+static BYTE testSHGetFolderLocation(BOOL optional, int folder)
+{
+ LPITEMIDLIST pidl;
+ HRESULT hr;
+ BYTE ret = 0xff;
+
+ /* treat absence of function as success */
+ if (!pSHGetFolderLocation) return TRUE;
+
+ pidl = NULL;
+ hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl);
+ ok(SUCCEEDED(hr) || optional,
+ "SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n"
+ "failed: 0x%08lx\n", getFolderName(folder), hr);
+ if (SUCCEEDED(hr))
+ {
+ ok(pidl != NULL,
+ "SHGetFolderLocation(NULL, %s, NULL, 0, &pidl)\n"
+ "succeeded, but returned pidl is NULL\n", getFolderName(folder));
+ if (pidl)
+ {
+ LPITEMIDLIST pidlLast = pILFindLastID(pidl);
+
+ ok(pidlLast != NULL, "%s: ILFindLastID failed\n",
+ getFolderName(folder));
+ if (pidlLast)
+ ret = pidlLast->mkid.abID[0];
+ IMalloc_Free(pMalloc, pidl);
+ }
+ }
+ return ret;
+}
+
+/* Returns the folder's PIDL type, or 0xff if one can't be found. */
+static BYTE testSHGetSpecialFolderLocation(BOOL optional, int folder)
+{
+ LPITEMIDLIST pidl;
+ HRESULT hr;
+ BYTE ret = 0xff;
+
+ /* treat absence of function as success */
+ if (!pSHGetSpecialFolderLocation) return TRUE;
+
+ pidl = NULL;
+ hr = pSHGetSpecialFolderLocation(NULL, folder, &pidl);
+ ok(SUCCEEDED(hr) || optional,
+ "SHGetSpecialFolderLocation(NULL, %s, &pidl)\n"
+ "failed: 0x%08lx\n", getFolderName(folder), hr);
+ if (SUCCEEDED(hr))
+ {
+ ok(pidl != NULL,
+ "SHGetSpecialFolderLocation(NULL, %s, &pidl)\n"
+ "succeeded, but returned pidl is NULL\n", getFolderName(folder));
+ if (pidl)
+ {
+ LPITEMIDLIST pidlLast = pILFindLastID(pidl);
+
+ ok(pidlLast != NULL,
+ "%s: ILFindLastID failed\n", getFolderName(folder));
+ if (pidlLast)
+ ret = pidlLast->mkid.abID[0];
+ IMalloc_Free(pMalloc, pidl);
+ }
+ }
+ return ret;
+}
+
+static void testSHGetFolderPath(BOOL optional, int folder)
+{
+ char path[MAX_PATH];
+ HRESULT hr;
+
+ if (!pSHGetFolderPathA) return;
+
+ hr = pSHGetFolderPathA(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path);
+ ok(SUCCEEDED(hr) || optional,
+ "SHGetFolderPathA(NULL, %s, NULL, SHGFP_TYPE_CURRENT, path)\n"
+ "failed: 0x%08lx\n", getFolderName(folder), hr);
+}
+
+static void testSHGetSpecialFolderPath(BOOL optional, int folder)
+{
+ char path[MAX_PATH];
+ BOOL ret;
+
+ if (!pSHGetSpecialFolderPathA) return;
+
+ ret = pSHGetSpecialFolderPathA(NULL, path, folder, FALSE);
+ if (ret && winetest_interactive)
+ printf("%s: %s\n", getFolderName(folder), path);
+ ok(ret || optional,
+ "SHGetSpecialFolderPathA(NULL, path, %s, FALSE) failed\n",
+ getFolderName(folder));
+}
+
+static void testShellValues(const struct shellExpectedValues testEntries[],
+ int numEntries, BOOL optional)
+{
+ int i;
+
+ for (i = 0; i < numEntries; i++)
+ {
+ BYTE type;
+
+ type = testSHGetFolderLocation(optional, testEntries[i].folder);
+ ok(type == testEntries[i].pidlType || optional,
+ "%s has type %d (0x%02x), expected %d (0x%02x)\n",
+ getFolderName(testEntries[i].folder), type, type,
+ testEntries[i].pidlType, testEntries[i].pidlType);
+ type = testSHGetSpecialFolderLocation(optional, testEntries[i].folder);
+ ok(type == testEntries[i].pidlType || optional,
+ "%s has type %d (0x%02x), expected %d (0x%02x)\n",
+ getFolderName(testEntries[i].folder), type, type,
+ testEntries[i].pidlType, testEntries[i].pidlType);
+ switch (type)
+ {
+ case PT_FOLDER:
+ case PT_DRIVE:
+ case PT_DRIVE2:
+ case PT_IESPECIAL2:
+ testSHGetFolderPath(optional, testEntries[i].folder);
+ testSHGetSpecialFolderPath(optional, testEntries[i].folder);
+ break;
+ }
+ }
+}
+
+/* Attempts to verify that the folder path corresponding to the folder CSIDL
+ * value has the same value as the environment variable with name envVar.
+ * Doesn't mind if SHGetSpecialFolderPath fails for folder or if envVar isn't
+ * set in this environment; different OS and shell version behave differently.
+ * However, if both are present, fails if envVar's value is not the same
+ * (byte-for-byte) as what SHGetSpecialFolderPath returns.
+ */
+static void matchSpecialFolderPathToEnv(int folder, const char *envVar)
+{
+ char path[MAX_PATH];
+
+ if (!pSHGetSpecialFolderPathA) return;
+
+ if (pSHGetSpecialFolderPathA(NULL, path, folder, FALSE))
+ {
+ char *envVal = getenv(envVar);
+
+ ok(!envVal || !lstrcmpiA(envVal, path),
+ "%%%s%% does not match SHGetSpecialFolderPath:\n"
+ "%%%s%% is %s\nSHGetSpecialFolderPath returns %s\n",
+ envVar, envVar, envVal, path);
+ }
+}
+
+/* Attempts to match the GUID returned by SHGetFolderLocation for folder with
+ * GUID. Assumes the type of the returned PIDL is in fact a GUID, but doesn't
+ * fail if it isn't--that check should already have been done.
+ * Fails if the returned PIDL is a GUID whose value does not match guid.
+ */
+static void matchGUID(int folder, const GUID *guid)
+{
+ LPITEMIDLIST pidl;
+ HRESULT hr;
+
+ if (!pSHGetFolderLocation) return;
+ if (!guid) return;
+
+ pidl = NULL;
+ hr = pSHGetFolderLocation(NULL, folder, NULL, 0, &pidl);
+ if (SUCCEEDED(hr))
+ {
+ LPITEMIDLIST pidlLast = pILFindLastID(pidl);
+
+ if (pidlLast && (pidlLast->mkid.abID[0] == PT_SHELLEXT ||
+ pidlLast->mkid.abID[0] == PT_GUID))
+ {
+ GUID *shellGuid = (GUID *)(pidlLast->mkid.abID + 2);
+
+ ok(IsEqualIID(shellGuid, guid),
+ "%s: got GUID %s, expected %s\n", getFolderName(folder),
+ printGUID(shellGuid), printGUID(guid));
+ }
+ IMalloc_Free(pMalloc, pidl);
+ }
+}
+
+static void testDesktop(void)
+{
+ testSHGetFolderPath(FALSE, CSIDL_DESKTOP);
+ testSHGetSpecialFolderPath(FALSE, CSIDL_DESKTOP);
+ /* Test the desktop; even though SHITEMID should always contain abID of at
+ * least one type, when cb is 0 its value is undefined. So don't check
+ * what the returned type is, just make sure it exists.
+ */
+ testSHGetFolderLocation(FALSE, CSIDL_DESKTOP);
+ testSHGetSpecialFolderLocation(FALSE, CSIDL_DESKTOP);
+}
+
+static void testPersonal(void)
+{
+ BYTE type;
+
+ /* The pidl may be a real folder, or a virtual directory, or a drive if the
+ * home directory is set to the root directory of a drive.
+ */
+ type = testSHGetFolderLocation(FALSE, CSIDL_PERSONAL);
+ ok(type == PT_FOLDER || type == PT_GUID || type == PT_DRIVE,
+ "CSIDL_PERSONAL returned invalid type 0x%02x, "
+ "expected PT_FOLDER or PT_GUID\n", type);
+ if (type == PT_FOLDER)
+ testSHGetFolderPath(FALSE, CSIDL_PERSONAL);
+ type = testSHGetSpecialFolderLocation(FALSE, CSIDL_PERSONAL);
+ ok(type == PT_FOLDER || type == PT_GUID || type == PT_DRIVE,
+ "CSIDL_PERSONAL returned invalid type 0x%02x, "
+ "expected PT_FOLDER or PT_GUID\n", type);
+ if (type == PT_FOLDER)
+ testSHGetSpecialFolderPath(FALSE, CSIDL_PERSONAL);
+}
+
+/* Checks the PIDL type of all the known values. */
+static void testPidlTypes(void)
+{
+ testDesktop();
+ testPersonal();
+ testShellValues(requiredShellValues, ARRAY_SIZE(requiredShellValues),
+ FALSE);
+ testShellValues(optionalShellValues, ARRAY_SIZE(optionalShellValues),
+ TRUE);
+}
+
+/* Verifies various shell virtual folders have the correct well-known GUIDs. */
+static void testGUIDs(void)
+{
+ matchGUID(CSIDL_BITBUCKET, &CLSID_RecycleBin);
+ matchGUID(CSIDL_CONTROLS, &CLSID_ControlPanel);
+ matchGUID(CSIDL_DRIVES, &CLSID_MyComputer);
+ matchGUID(CSIDL_INTERNET, &CLSID_Internet);
+ matchGUID(CSIDL_NETWORK, &CLSID_NetworkPlaces);
+ matchGUID(CSIDL_PERSONAL, &CLSID_MyDocuments);
+ matchGUID(CSIDL_COMMON_DOCUMENTS, &CLSID_CommonDocuments);
+}
+
+/* Verifies various shell paths match the environment variables to which they
+ * correspond.
+ */
+static void testEnvVars(void)
+{
+ matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES, "ProgramFiles");
+ matchSpecialFolderPathToEnv(CSIDL_APPDATA, "APPDATA");
+ matchSpecialFolderPathToEnv(CSIDL_PROFILE, "USERPROFILE");
+ matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "SystemRoot");
+ matchSpecialFolderPathToEnv(CSIDL_WINDOWS, "windir");
+ matchSpecialFolderPathToEnv(CSIDL_PROGRAM_FILES_COMMON,
+ "CommonProgramFiles");
+ /* this is only set on Wine, but can't hurt to verify it: */
+ matchSpecialFolderPathToEnv(CSIDL_SYSTEM, "winsysdir");
+}
+
+/* Verifies the shell path for CSIDL_WINDOWS matches the return from
+ * GetWindowsDirectory. If SHGetSpecialFolderPath fails, no harm, no foul--not
+ * every shell32 version supports CSIDL_WINDOWS.
+ */
+static void testWinDir(void)
+{
+ char windowsShellPath[MAX_PATH], windowsDir[MAX_PATH] = { 0 };
+
+ if (!pSHGetSpecialFolderPathA) return;
+
+ if (pSHGetSpecialFolderPathA(NULL, windowsShellPath, CSIDL_WINDOWS, FALSE))
+ {
+ PathRemoveBackslashA(windowsShellPath);
+ GetWindowsDirectoryA(windowsDir, sizeof(windowsDir));
+ PathRemoveBackslashA(windowsDir);
+ ok(!lstrcmpiA(windowsDir, windowsShellPath),
+ "GetWindowsDirectory does not match SHGetSpecialFolderPath:\n"
+ "GetWindowsDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
+ windowsDir, windowsShellPath);
+ }
+}
+
+/* Verifies the shell path for CSIDL_SYSTEM and CSIDL_SYSTEMX86 matches the
+ * return from GetSystemDirectory. If SHGetSpecialFolderPath fails, no harm,
+ * no foul--not every shell32 version supports CSIDL_SYSTEM.
+ */
+static void testSystemDir(void)
+{
+ char systemShellPath[MAX_PATH], systemDir[MAX_PATH] = { 0 };
+
+ if (!pSHGetSpecialFolderPathA) return;
+
+ GetSystemDirectoryA(systemDir, sizeof(systemDir));
+ PathRemoveBackslashA(systemDir);
+ if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEM, FALSE))
+ {
+ PathRemoveBackslashA(systemShellPath);
+ ok(!lstrcmpiA(systemDir, systemShellPath),
+ "GetSystemDirectory does not match SHGetSpecialFolderPath:\n"
+ "GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
+ systemDir, systemShellPath);
+ }
+ /* check CSIDL_SYSTEMX86; note that this isn't always present, so don't
+ * worry if it fails
+ */
+ if (pSHGetSpecialFolderPathA(NULL, systemShellPath, CSIDL_SYSTEMX86, FALSE))
+ {
+ PathRemoveBackslashA(systemShellPath);
+ ok(!lstrcmpiA(systemDir, systemShellPath),
+ "GetSystemDirectory does not match SHGetSpecialFolderPath:\n"
+ "GetSystemDirectory returns %s\nSHGetSpecialFolderPath returns %s\n",
+ systemDir, systemShellPath);
+ }
+}
+
+/* Globals used by subprocesses */
+static int myARGC;
+static char **myARGV;
+static char base[MAX_PATH];
+static char selfname[MAX_PATH];
+
+static int init(void)
+{
+ myARGC = winetest_get_mainargs(&myARGV);
+ if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
+ strcpy(selfname, myARGV[0]);
+ return 1;
+}
+
+/* Subprocess helper 1: test what happens when CSIDL_FAVORITES is set to a
+ * nonexistent directory.
+ */
+static void testNonExistentPath1(void)
+{
+ HRESULT hr;
+ LPITEMIDLIST pidl;
+ char path[MAX_PATH];
+
+ /* test some failure cases first: */
+ hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL,
+ SHGFP_TYPE_CURRENT, NULL);
+ ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+ "SHGetFolderPath returned 0x%08lx, expected 0x80070002\n", hr);
+ pidl = NULL;
+ hr = pSHGetFolderLocation(NULL, CSIDL_FAVORITES, NULL, 0,
+ &pidl);
+ ok(hr == E_FAIL,
+ "SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n", hr);
+ if (SUCCEEDED(hr) && pidl)
+ IMalloc_Free(pMalloc, pidl);
+ ok(!pSHGetSpecialFolderPathA(NULL, path, CSIDL_FAVORITES, FALSE),
+ "SHGetSpecialFolderPath succeeded, expected failure\n");
+ pidl = NULL;
+ hr = pSHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
+ ok(hr == E_FAIL, "SHGetFolderLocation returned 0x%08lx, expected E_FAIL\n",
+ hr);
+ if (SUCCEEDED(hr) && pidl)
+ IMalloc_Free(pMalloc, pidl);
+ /* now test success: */
+ hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL,
+ SHGFP_TYPE_CURRENT, path);
+ if (SUCCEEDED(hr))
+ {
+ BOOL ret;
+
+ if (winetest_interactive)
+ printf("CSIDL_FAVORITES was changed to %s\n", path);
+ ret = CreateDirectoryA(path, NULL);
+ ok(!ret,
+ "CreateDirectoryA succeeded but should have failed "
+ "with ERROR_ALREADY_EXISTS\n");
+ if (!ret)
+ ok(GetLastError() == ERROR_ALREADY_EXISTS,
+ "CreateDirectoryA failed with %ld, "
+ "expected ERROR_ALREADY_EXISTS\n",
+ GetLastError());
+ }
+ ok(SUCCEEDED(hr),
+ "SHGetFolderPath(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, "
+ "NULL, SHGFP_TYPE_CURRENT, path)\nfailed: 0x%08lx\n", hr);
+}
+
+/* Subprocess helper 2: make sure SHGetFolderPath still succeeds when the
+ * original value of CSIDL_FAVORITES is restored.
+ */
+static void testNonExistentPath2(void)
+{
+ HRESULT hr;
+
+ hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES | CSIDL_FLAG_CREATE, NULL,
+ SHGFP_TYPE_CURRENT, NULL);
+ ok(SUCCEEDED(hr), "SHGetFolderPath failed: 0x%08lx\n", hr);
+}
+
+static void doChild(const char *arg)
+{
+ if (arg[0] == '1')
+ testNonExistentPath1();
+ else if (arg[0] == '2')
+ testNonExistentPath2();
+}
+
+/* Tests the return values from the various shell functions both with and
+ * without the use of the CSIDL_FLAG_CREATE flag. This flag only appeared in
+ * version 5 of the shell, so don't test unless it's at least version 5.
+ * The test reads a value from the registry, modifies it, calls
+ * SHGetFolderPath once with the CSIDL_FLAG_CREATE flag, and immediately
+ * afterward without it. Then it restores the registry and deletes the folder
+ * that was created.
+ * One oddity with respect to restoration: shell32 caches somehow, so it needs
+ * to be reloaded in order to see the correct (restored) value.
+ * Some APIs unrelated to the ones under test may fail, but I expect they're
+ * covered by other unit tests; I just print out something about failure to
+ * help trace what's going on.
+ */
+static void testNonExistentPath(void)
+{
+ static const char userShellFolders[] =
+ "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders";
+ char originalPath[MAX_PATH], modifiedPath[MAX_PATH];
+ HKEY key;
+
+ if (!pSHGetFolderPathA) return;
+ if (!pSHGetFolderLocation) return;
+ if (!pSHGetSpecialFolderPathA) return;
+ if (!pSHGetSpecialFolderLocation) return;
+ if (!pSHFileOperationA) return;
+ if (shellVersion.dwMajorVersion < 5) return;
+
+ if (!RegOpenKeyExA(HKEY_CURRENT_USER, userShellFolders, 0, KEY_ALL_ACCESS,
+ &key))
+ {
+ DWORD len, type;
+
+ len = sizeof(originalPath);
+ if (!RegQueryValueExA(key, "Favorites", NULL, &type,
+ (LPBYTE)&originalPath, &len))
+ {
+ size_t len = strlen(originalPath);
+
+ memcpy(modifiedPath, originalPath, len);
+ modifiedPath[len++] = '2';
+ modifiedPath[len++] = '\0';
+ if (winetest_interactive)
+ printf("Changing CSIDL_FAVORITES to %s\n", modifiedPath);
+ if (!RegSetValueExA(key, "Favorites", 0, type, (LPBYTE) modifiedPath, len))
+ {
+ char buffer[MAX_PATH];
+ STARTUPINFOA startup;
+ PROCESS_INFORMATION info;
+ HRESULT hr;
+ BOOL ret;
+
+ wnsprintfA(buffer, sizeof(buffer), "%s tests/shellpath.c 1",
+ selfname);
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.dwFlags = SW_SHOWNORMAL;
+ CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL,
+ &startup, &info);
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0,
+ "child process termination\n");
+
+ /* Query the path to be able to delete it below */
+ hr = pSHGetFolderPathA(NULL, CSIDL_FAVORITES, NULL,
+ SHGFP_TYPE_CURRENT, modifiedPath);
+ ok(SUCCEEDED(hr), "SHGetFolderPathA failed: 0x%08lx\n", hr);
+
+ /* restore original values: */
+ if (winetest_interactive)
+ printf("Restoring CSIDL_FAVORITES to %s\n", originalPath);
+ RegSetValueExA(key, "Favorites", 0, type, (LPBYTE) originalPath,
+ strlen(originalPath) + 1);
+ RegFlushKey(key);
+
+ wnsprintfA(buffer, sizeof(buffer), "%s tests/shellpath.c 2",
+ selfname);
+ memset(&startup, 0, sizeof(startup));
+ startup.cb = sizeof(startup);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.dwFlags = SW_SHOWNORMAL;
+ CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL,
+ &startup, &info);
+ ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0,
+ "child process termination\n");
+
+ ret = RemoveDirectoryA(modifiedPath);
+ ok( ret, "RemoveDirectoryA failed: %ld\n", GetLastError());
+ }
+ }
+ else if (winetest_interactive)
+ printf("RegQueryValueExA(key, Favorites, ...) failed\n");
+ if (key)
+ RegCloseKey(key);
+ }
+ else if (winetest_interactive)
+ printf("RegOpenKeyExA(HKEY_CURRENT_USER, %s, ...) failed\n",
+ userShellFolders);
+}
+
+START_TEST(shellpath)
+{
+ if (!init()) return;
+
+ loadShell32();
+ if (!hShell32) return;
+
+ if (myARGC >= 3)
+ doChild(myARGV[2]);
+ else
+ {
+ /* first test various combinations of parameters: */
+ testApiParameters();
+
+ /* check known values: */
+ testPidlTypes();
+ testGUIDs();
+ testEnvVars();
+ testWinDir();
+ testSystemDir();
+ testNonExistentPath();
+ }
+}
--- /dev/null
+/*
+ * Unit test of the ShellExecute function.
+ *
+ * Copyright 2005 Francois Gouget for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* TODO:
+ * - test the default verb selection
+ * - test selection of an alternate class
+ * - try running executables in more ways
+ * - try passing arguments to executables
+ * - ShellExecute("foo.shlexec") with no path should work if foo.shlexec is
+ * in the PATH
+ * - test associations that use %l, %L or "%1" instead of %1
+ * - we may want to test ShellExecuteEx() instead of ShellExecute()
+ * and then we could also check its return value
+ * - ShellExecuteEx() also calls SetLastError() with meaningful values which
+ * we could check
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "wtypes.h"
+#include "winbase.h"
+#include "windef.h"
+#include "shellapi.h"
+#include "shlwapi.h"
+#include "wine/test.h"
+
+#include "shell32_test.h"
+
+
+static char argv0[MAX_PATH];
+static int myARGC;
+static char** myARGV;
+static char tmpdir[MAX_PATH];
+
+static const char* testfiles[]=
+{
+ "%s\\test file.shlexec",
+ "%s\\test file.noassoc",
+ "%s\\test file.noassoc.shlexec",
+ "%s\\test file.shlexec.noassoc",
+ "%s\\test_shortcut_shlexec.lnk",
+ NULL
+};
+
+
+static void strcat_param(char* str, const char* param)
+{
+ if (param!=NULL)
+ {
+ strcat(str, "\"");
+ strcat(str, param);
+ strcat(str, "\"");
+ }
+ else
+ {
+ strcat(str, "null");
+ }
+}
+
+static char shell_call[2048]="";
+static int shell_execute(LPCSTR operation, LPCSTR file, LPCSTR parameters, LPCSTR directory)
+{
+ strcpy(shell_call, "ShellExecute(");
+ strcat_param(shell_call, operation);
+ strcat(shell_call, ", ");
+ strcat_param(shell_call, file);
+ strcat(shell_call, ", ");
+ strcat_param(shell_call, parameters);
+ strcat(shell_call, ", ");
+ strcat_param(shell_call, directory);
+ strcat(shell_call, ")");
+ if (winetest_debug > 1)
+ trace("%s\n", shell_call);
+
+ SetLastError(0xcafebabe);
+ /* FIXME: We cannot use ShellExecuteEx() here because if there is no
+ * association it displays the 'Open With' dialog and I could not find
+ * a flag to prevent this.
+ */
+ return (int)ShellExecute(NULL, operation, file, parameters, directory,
+ SW_SHOWNORMAL);
+}
+
+static int shell_execute_ex(DWORD mask, LPCSTR operation, LPCSTR file,
+ LPCSTR parameters, LPCSTR directory)
+{
+ SHELLEXECUTEINFO sei;
+ BOOL success;
+ int rc;
+
+ strcpy(shell_call, "ShellExecuteEx(");
+ strcat_param(shell_call, operation);
+ strcat(shell_call, ", ");
+ strcat_param(shell_call, file);
+ strcat(shell_call, ", ");
+ strcat_param(shell_call, parameters);
+ strcat(shell_call, ", ");
+ strcat_param(shell_call, directory);
+ strcat(shell_call, ")");
+ if (winetest_debug > 1)
+ trace("%s\n", shell_call);
+
+ sei.cbSize=sizeof(sei);
+ sei.fMask=mask;
+ sei.hwnd=NULL;
+ sei.lpVerb=operation;
+ sei.lpFile=file;
+ sei.lpParameters=parameters;
+ sei.lpDirectory=directory;
+ sei.nShow=SW_SHOWNORMAL;
+ sei.hInstApp=NULL; /* Out */
+ sei.lpIDList=NULL;
+ sei.lpClass=NULL;
+ sei.hkeyClass=NULL;
+ sei.dwHotKey=0;
+ sei.hIcon=NULL;
+
+ SetLastError(0xcafebabe);
+ success=ShellExecuteEx(&sei);
+ rc=(int)sei.hInstApp;
+ ok((success && rc >= 32) || (!success && rc < 32),
+ "%s rc=%d and hInstApp=%d is not allowed\n", shell_call, success, rc);
+ return rc;
+}
+
+static void create_test_association(const char* extension)
+{
+ HKEY hkey, hkey_shell;
+ char class[MAX_PATH];
+ LONG rc;
+
+ sprintf(class, "shlexec%s", extension);
+ rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, extension, 0, NULL, 0, KEY_SET_VALUE,
+ NULL, &hkey, NULL);
+ assert(rc==ERROR_SUCCESS);
+ rc=RegSetValueEx(hkey, NULL, 0, REG_SZ, class, strlen(class)+1);
+ assert(rc==ERROR_SUCCESS);
+ CloseHandle(hkey);
+
+ rc=RegCreateKeyEx(HKEY_CLASSES_ROOT, class, 0, NULL, 0,
+ KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS, NULL, &hkey, NULL);
+ assert(rc==ERROR_SUCCESS);
+ rc=RegCreateKeyEx(hkey, "shell", 0, NULL, 0,
+ KEY_CREATE_SUB_KEY, NULL, &hkey_shell, NULL);
+ assert(rc==ERROR_SUCCESS);
+ CloseHandle(hkey);
+ CloseHandle(hkey_shell);
+}
+
+static void delete_test_association(const char* extension)
+{
+ char class[MAX_PATH];
+
+ sprintf(class, "shlexec%s", extension);
+ SHDeleteKey(HKEY_CLASSES_ROOT, class);
+ SHDeleteKey(HKEY_CLASSES_ROOT, extension);
+}
+
+static void create_test_verb(const char* extension, const char* verb)
+{
+ HKEY hkey_shell, hkey_verb, hkey_cmd;
+ char shell[MAX_PATH];
+ char* cmd;
+ LONG rc;
+
+ sprintf(shell, "shlexec%s\\shell", extension);
+ rc=RegOpenKeyEx(HKEY_CLASSES_ROOT, shell, 0,
+ KEY_CREATE_SUB_KEY, &hkey_shell);
+ assert(rc==ERROR_SUCCESS);
+ rc=RegCreateKeyEx(hkey_shell, verb, 0, NULL, 0, KEY_CREATE_SUB_KEY,
+ NULL, &hkey_verb, NULL);
+ assert(rc==ERROR_SUCCESS);
+ rc=RegCreateKeyEx(hkey_verb, "command", 0, NULL, 0, KEY_SET_VALUE,
+ NULL, &hkey_cmd, NULL);
+ assert(rc==ERROR_SUCCESS);
+
+ cmd=malloc(strlen(argv0)+13+1);
+ sprintf(cmd,"%s shlexec \"%%1\"", argv0);
+ rc=RegSetValueEx(hkey_cmd, NULL, 0, REG_SZ, cmd, strlen(cmd)+1);
+ assert(rc==ERROR_SUCCESS);
+
+ free(cmd);
+ CloseHandle(hkey_shell);
+ CloseHandle(hkey_verb);
+ CloseHandle(hkey_cmd);
+}
+
+
+typedef struct
+{
+ char* basename;
+ int rc;
+ int todo;
+} filename_tests_t;
+
+static filename_tests_t filename_tests[]=
+{
+ /* Test bad / nonexistent filenames */
+ {"%s\\nonexistent.shlexec", ERROR_FILE_NOT_FOUND, 1},
+ {"%s\\nonexistent.noassoc", ERROR_FILE_NOT_FOUND, 1},
+
+ /* Standard tests */
+ {"%s\\test file.shlexec", 0, 0},
+ {"%s\\test file.shlexec.", 0, 0},
+ {"%s/test file.shlexec", 0, 0},
+
+ /* Test filenames with no association */
+ {"%s\\test file.noassoc", SE_ERR_NOASSOC, 0},
+
+ /* Test double extensions */
+ {"%s\\test file.noassoc.shlexec", 0, 0},
+ {"%s\\test file.shlexec.noassoc", SE_ERR_NOASSOC, 0},
+
+ /* Test shortcuts */
+ {"%s\\test_shortcut_shlexec.lnk", 0, 0},
+
+ {NULL, 0, 0}
+};
+
+static void test_filename()
+{
+ char filename[MAX_PATH];
+ const filename_tests_t* test;
+ HMODULE hdll;
+ DLLVERSIONINFO dllver;
+ HRESULT (WINAPI *pDllGetVersion)(DLLVERSIONINFO*);
+ char* c;
+ int rc;
+
+ test=filename_tests;
+ while (test->basename)
+ {
+ sprintf(filename, test->basename, tmpdir);
+ if (strchr(filename, '/'))
+ {
+ c=filename;
+ while (*c)
+ {
+ if (*c=='\\')
+ *c='/';
+ c++;
+ }
+ }
+ rc=shell_execute(NULL, filename, NULL, NULL);
+ if (test->rc==0)
+ {
+ if (test->todo)
+ {
+ todo_wine
+ {
+ ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
+ rc, GetLastError());
+ }
+ }
+ else
+ {
+ ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call,
+ rc, GetLastError());
+ }
+ }
+ else
+ {
+ if (test->todo)
+ {
+ todo_wine
+ {
+ ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
+ }
+ }
+ else
+ {
+ ok(rc==test->rc, "%s returned %d\n", shell_call, rc);
+ }
+ }
+ test++;
+ }
+
+ hdll=GetModuleHandleA("shell32.dll");
+ pDllGetVersion=(void*)GetProcAddress(hdll, "DllGetVersion");
+ if (pDllGetVersion)
+ {
+ dllver.cbSize=sizeof(dllver);
+ pDllGetVersion(&dllver);
+ trace("major=%ld minor=%ld build=%ld platform=%ld\n",
+ dllver.dwMajorVersion, dllver.dwMinorVersion,
+ dllver.dwBuildNumber, dllver.dwPlatformID);
+
+ /* The more recent versions of shell32.dll accept quoted filenames
+ * while older ones (e.g. 4.00) don't. Still we want to test this
+ * because IE 6 depends on the new behavior.
+ * One day we may need to check the exact version of the dll but for
+ * now making sure DllGetVersion() is present is sufficient.
+ */
+ sprintf(filename, "\"%s\\test file.shlexec\"", tmpdir);
+ rc=shell_execute(NULL, filename, NULL, NULL);
+ ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
+ GetLastError());
+
+ if (dllver.dwMajorVersion>=6)
+ {
+ /* Recent versions of shell32.dll accept '/'s in shortcut paths.
+ * Older versions don't or are quite buggy in this regard.
+ */
+ sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
+ c=filename;
+ while (*c)
+ {
+ if (*c=='\\')
+ *c='/';
+ c++;
+ }
+ rc=shell_execute(NULL, filename, NULL, NULL);
+ todo_wine {
+ ok(rc>=32, "%s failed: rc=%d err=%ld\n", shell_call, rc,
+ GetLastError());
+ }
+ }
+ }
+}
+
+
+static void test_exes()
+{
+ char filename[MAX_PATH];
+ int rc;
+
+ /* We need NOZONECHECKS on Win2003 to block a dialog */
+ rc=shell_execute_ex(SEE_MASK_NOZONECHECKS, NULL, argv0, "shlexec -nop",
+ NULL);
+ ok(rc>=32, "%s returned %d\n", shell_call, rc);
+
+ sprintf(filename, "%s\\test file.noassoc", tmpdir);
+ if (CopyFile(argv0, filename, FALSE))
+ {
+ rc=shell_execute(NULL, filename, "shlexec -nop", NULL);
+ todo_wine {
+ ok(rc==SE_ERR_NOASSOC, "%s succeeded: rc=%d\n", shell_call, rc);
+ }
+ }
+}
+
+
+static void init_test()
+{
+ char filename[MAX_PATH];
+ WCHAR lnkfile[MAX_PATH];
+ const char* const * testfile;
+ lnk_desc_t desc;
+ DWORD rc;
+ HRESULT r;
+
+ r = CoInitialize(NULL);
+ ok(SUCCEEDED(r), "CoInitialize failed (0x%08lx)\n", r);
+ if (!SUCCEEDED(r))
+ exit(1);
+
+ rc=GetModuleFileName(NULL, argv0, sizeof(argv0));
+ assert(rc!=0 && rc<sizeof(argv0));
+ if (GetFileAttributes(argv0)==INVALID_FILE_ATTRIBUTES)
+ {
+ strcat(argv0, ".so");
+ ok(GetFileAttributes(argv0)!=INVALID_FILE_ATTRIBUTES,
+ "unable to find argv0!\n");
+ }
+
+ GetTempPathA(sizeof(tmpdir)/sizeof(*tmpdir), tmpdir);
+
+ /* Set up the test files */
+ testfile=testfiles;
+ while (*testfile)
+ {
+ HANDLE hfile;
+
+ sprintf(filename, *testfile, tmpdir);
+ hfile=CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hfile==INVALID_HANDLE_VALUE)
+ {
+ trace("unable to create '%s': err=%ld\n", filename, GetLastError());
+ assert(0);
+ }
+ CloseHandle(hfile);
+ testfile++;
+ }
+
+ /* Setup the test shortcuts */
+ sprintf(filename, "%s\\test_shortcut_shlexec.lnk", tmpdir);
+ MultiByteToWideChar(CP_ACP, 0, filename, -1, lnkfile, sizeof(lnkfile)/sizeof(*lnkfile));
+ desc.description=NULL;
+ desc.workdir=NULL;
+ sprintf(filename, "%s\\test file.shlexec", tmpdir);
+ desc.path=filename;
+ desc.pidl=NULL;
+ desc.arguments="";
+ desc.showcmd=0;
+ desc.icon=NULL;
+ desc.icon_id=0;
+ desc.hotkey=0;
+ create_lnk(lnkfile, &desc, 0);
+
+ /* Create a basic association suitable for most tests */
+ create_test_association(".shlexec");
+ create_test_verb(".shlexec", "Open");
+}
+
+static void cleanup_test()
+{
+ char filename[MAX_PATH];
+ const char* const * testfile;
+
+ /* Delete the test files */
+ testfile=testfiles;
+ while (*testfile)
+ {
+ sprintf(filename, *testfile, tmpdir);
+ DeleteFile(filename);
+ testfile++;
+ }
+
+ /* Delete the test association */
+ delete_test_association(".shlexec");
+
+ CoUninitialize();
+}
+
+START_TEST(shlexec)
+{
+
+ myARGC = winetest_get_mainargs(&myARGV);
+ if (myARGC>=3)
+ {
+ /* FIXME: We should dump the parameters we got
+ * and have the parent verify them
+ */
+ exit(0);
+ }
+
+ init_test();
+
+ test_filename();
+ test_exes();
+
+ cleanup_test();
+}
--- /dev/null
+/*
+ * Unit test of the SHFileOperation function.
+ *
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define WINE_NOWINSOCK
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+#include "shlobj.h"
+
+#include "wine/test.h"
+
+CHAR CURR_DIR[MAX_PATH];
+
+static HMODULE hshell32;
+static int (WINAPI *pSHCreateDirectoryExA)(HWND, LPCSTR, LPSECURITY_ATTRIBUTES);
+
+static void InitFunctionPointers(void)
+{
+ hshell32 = GetModuleHandleA("shell32.dll");
+
+ if(hshell32)
+ pSHCreateDirectoryExA = (void*)GetProcAddress(hshell32, "SHCreateDirectoryExA");
+}
+
+/* creates a file with the specified name for tests */
+static void createTestFile(const CHAR *name)
+{
+ HANDLE file;
+ DWORD written;
+
+ file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
+ WriteFile(file, name, strlen(name), &written, NULL);
+ WriteFile(file, "\n", strlen("\n"), &written, NULL);
+ CloseHandle(file);
+}
+
+static BOOL file_exists(const CHAR *name)
+{
+ return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES;
+}
+
+/* initializes the tests */
+static void init_shfo_tests(void)
+{
+ int len;
+
+ GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
+ len = lstrlenA(CURR_DIR);
+
+ if(len && (CURR_DIR[len-1] == '\\'))
+ CURR_DIR[len-1] = 0;
+
+ createTestFile(".\\test1.txt");
+ createTestFile(".\\test2.txt");
+ createTestFile(".\\test3.txt");
+ CreateDirectoryA(".\\test4.txt", NULL);
+ CreateDirectoryA(".\\testdir2", NULL);
+}
+
+/* cleans after tests */
+static void clean_after_shfo_tests(void)
+{
+ DeleteFileA(".\\test1.txt");
+ DeleteFileA(".\\test2.txt");
+ DeleteFileA(".\\test3.txt");
+ DeleteFileA(".\\test4.txt\\test1.txt");
+ DeleteFileA(".\\test4.txt\\test2.txt");
+ DeleteFileA(".\\test4.txt\\test3.txt");
+ RemoveDirectoryA(".\\test4.txt");
+ DeleteFileA(".\\testdir2\\test1.txt");
+ DeleteFileA(".\\testdir2\\test2.txt");
+ DeleteFileA(".\\testdir2\\test3.txt");
+ DeleteFileA(".\\testdir2\\test4.txt\\test1.txt");
+ RemoveDirectoryA(".\\testdir2\\test4.txt");
+ RemoveDirectoryA(".\\testdir2");
+}
+
+/*
+ puts into the specified buffer file names with current directory.
+ files - string with file names, separated by null characters. Ends on a double
+ null characters
+*/
+static void set_curr_dir_path(CHAR *buf, const CHAR* files)
+{
+ buf[0] = 0;
+ while (files[0])
+ {
+ strcpy(buf, CURR_DIR);
+ buf += strlen(buf);
+ buf[0] = '\\';
+ buf++;
+ strcpy(buf, files);
+ buf += strlen(buf) + 1;
+ files += strlen(files) + 1;
+ }
+ buf[0] = 0;
+}
+
+
+/* tests the FO_DELETE action */
+static void test_delete(void)
+{
+ SHFILEOPSTRUCTA shfo;
+ DWORD ret;
+ CHAR buf[MAX_PATH];
+
+ sprintf(buf, "%s\\%s", CURR_DIR, "test?.txt");
+ buf[strlen(buf) + 1] = '\0';
+
+ shfo.hwnd = NULL;
+ shfo.wFunc = FO_DELETE;
+ shfo.pFrom = buf;
+ shfo.pTo = "\0";
+ shfo.fFlags = FOF_FILESONLY | FOF_NOCONFIRMATION | FOF_SILENT;
+ shfo.hNameMappings = NULL;
+ shfo.lpszProgressTitle = NULL;
+
+ ok(!SHFileOperationA(&shfo), "Deletion was successful\n");
+ ok(file_exists(".\\test4.txt"), "Directory should not be removed\n");
+ ok(!file_exists(".\\test1.txt"), "File should be removed\n");
+
+ ret = SHFileOperationA(&shfo);
+ ok(!ret, "Directory exists, but is not removed, ret=%ld\n", ret);
+ ok(file_exists(".\\test4.txt"), "Directory should not be removed\n");
+
+ shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+
+ ok(!SHFileOperationA(&shfo), "Directory removed\n");
+ ok(!file_exists(".\\test4.txt"), "Directory should be removed\n");
+
+ ret = SHFileOperationA(&shfo);
+ ok(!ret, "The requested file does not exist, ret=%ld\n", ret);
+
+ init_shfo_tests();
+ sprintf(buf, "%s\\%s", CURR_DIR, "test4.txt");
+ buf[strlen(buf) + 1] = '\0';
+ ok(MoveFileA(".\\test1.txt", ".\\test4.txt\\test1.txt"), "Fill the subdirectory\n");
+ ok(!SHFileOperationA(&shfo), "Directory removed\n");
+ ok(!file_exists(".\\test4.txt"), "Directory is removed\n");
+
+ init_shfo_tests();
+ shfo.pFrom = ".\\test1.txt\0.\\test4.txt\0";
+ ok(!SHFileOperationA(&shfo), "Directory and a file removed\n");
+ ok(!file_exists(".\\test1.txt"), "The file should be removed\n");
+ ok(!file_exists(".\\test4.txt"), "Directory should be removed\n");
+ ok(file_exists(".\\test2.txt"), "This file should not be removed\n");
+}
+
+/* tests the FO_RENAME action */
+static void test_rename(void)
+{
+ SHFILEOPSTRUCTA shfo, shfo2;
+ CHAR from[MAX_PATH];
+ CHAR to[MAX_PATH];
+ DWORD retval;
+
+ shfo.hwnd = NULL;
+ shfo.wFunc = FO_RENAME;
+ shfo.pFrom = from;
+ shfo.pTo = to;
+ shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+ shfo.hNameMappings = NULL;
+ shfo.lpszProgressTitle = NULL;
+
+ set_curr_dir_path(from, "test1.txt\0");
+ set_curr_dir_path(to, "test4.txt\0");
+ ok(SHFileOperationA(&shfo), "File is not renamed moving to other directory "
+ "when specifying directory name only\n");
+ ok(file_exists(".\\test1.txt"), "The file is removed\n");
+
+ set_curr_dir_path(from, "test3.txt\0");
+ set_curr_dir_path(to, "test4.txt\\test1.txt\0");
+ ok(!SHFileOperationA(&shfo), "File is renamed moving to other directory\n");
+ ok(file_exists(".\\test4.txt\\test1.txt"), "The file is not renamed\n");
+
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
+ retval = SHFileOperationA(&shfo); /* W98 returns 0, W2K and newer returns ERROR_GEN_FAILURE, both do nothing */
+ ok(!retval || retval == ERROR_GEN_FAILURE || retval == ERROR_INVALID_TARGET_HANDLE,
+ "Can't rename many files, retval = %ld\n", retval);
+ ok(file_exists(".\\test1.txt"), "The file is renamed - many files are specified\n");
+
+ memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
+ shfo2.fFlags |= FOF_MULTIDESTFILES;
+
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
+ retval = SHFileOperationA(&shfo2); /* W98 returns 0, W2K and newer returns ERROR_GEN_FAILURE, both do nothing */
+ ok(!retval || retval == ERROR_GEN_FAILURE || retval == ERROR_INVALID_TARGET_HANDLE,
+ "Can't rename many files, retval = %ld\n", retval);
+ ok(file_exists(".\\test1.txt"), "The file is not renamed - many files are specified\n");
+
+ set_curr_dir_path(from, "test1.txt\0");
+ set_curr_dir_path(to, "test6.txt\0");
+ retval = SHFileOperationA(&shfo);
+ ok(!retval, "Rename file failed, retval = %ld\n", retval);
+ ok(!file_exists(".\\test1.txt"), "The file is not renamed\n");
+ ok(file_exists(".\\test6.txt"), "The file is not renamed\n");
+
+ set_curr_dir_path(from, "test6.txt\0");
+ set_curr_dir_path(to, "test1.txt\0");
+ retval = SHFileOperationA(&shfo);
+ ok(!retval, "Rename file back failed, retval = %ld\n", retval);
+
+ set_curr_dir_path(from, "test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0");
+ retval = SHFileOperationA(&shfo);
+ ok(!retval, "Rename dir failed, retval = %ld\n", retval);
+ ok(!file_exists(".\\test4.txt"), "The dir is not renamed\n");
+ ok(file_exists(".\\test6.txt"), "The dir is not renamed\n");
+
+ set_curr_dir_path(from, "test6.txt\0");
+ set_curr_dir_path(to, "test4.txt\0");
+ retval = SHFileOperationA(&shfo);
+ ok(!retval, "Rename dir back failed, retval = %ld\n", retval);
+}
+
+/* tests the FO_COPY action */
+static void test_copy(void)
+{
+ SHFILEOPSTRUCTA shfo, shfo2;
+ CHAR from[MAX_PATH];
+ CHAR to[MAX_PATH];
+ FILEOP_FLAGS tmp_flags;
+ DWORD retval;
+
+ shfo.hwnd = NULL;
+ shfo.wFunc = FO_COPY;
+ shfo.pFrom = from;
+ shfo.pTo = to;
+ shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+ shfo.hNameMappings = NULL;
+ shfo.lpszProgressTitle = NULL;
+
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
+ ok(SHFileOperationA(&shfo), "Can't copy many files\n");
+ ok(!file_exists(".\\test6.txt"), "The file is not copied - many files are "
+ "specified as a target\n");
+
+ memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
+ shfo2.fFlags |= FOF_MULTIDESTFILES;
+
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
+ ok(!SHFileOperationA(&shfo2), "Can't copy many files\n");
+ ok(file_exists(".\\test6.txt"), "The file is copied - many files are "
+ "specified as a target\n");
+ DeleteFileA(".\\test6.txt");
+ DeleteFileA(".\\test7.txt");
+ RemoveDirectoryA(".\\test8.txt");
+
+ /* number of sources do not correspond to number of targets */
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0");
+ ok(SHFileOperationA(&shfo2), "Can't copy many files\n");
+ ok(!file_exists(".\\test6.txt"), "The file is not copied - many files are "
+ "specified as a target\n");
+
+ set_curr_dir_path(from, "test1.txt\0");
+ set_curr_dir_path(to, "test4.txt\0");
+ ok(!SHFileOperationA(&shfo), "Prepare test to check how directories are copied recursively\n");
+ ok(file_exists(".\\test4.txt\\test1.txt"), "The file is copied\n");
+
+ set_curr_dir_path(from, "test?.txt\0");
+ set_curr_dir_path(to, "testdir2\0");
+ ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
+ ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not copied yet\n");
+ ok(!SHFileOperationA(&shfo), "Files and directories are copied to directory\n");
+ ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
+ ok(file_exists(".\\testdir2\\test4.txt"), "The directory is copied\n");
+ ok(file_exists(".\\testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is copied\n");
+ clean_after_shfo_tests();
+
+ init_shfo_tests();
+ shfo.fFlags |= FOF_FILESONLY;
+ ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
+ ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not copied yet\n");
+ ok(!SHFileOperationA(&shfo), "Files are copied to other directory\n");
+ ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
+ ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is copied\n");
+ clean_after_shfo_tests();
+
+ init_shfo_tests();
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0");
+ ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
+ ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not copied yet\n");
+ ok(!SHFileOperationA(&shfo), "Files are copied to other directory \n");
+ ok(file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
+ ok(file_exists(".\\testdir2\\test2.txt"), "The file is copied\n");
+ clean_after_shfo_tests();
+
+ /* Copying multiple files with one not existing as source, fails the
+ entire operation in Win98/ME/2K/XP, but not in 95/NT */
+ init_shfo_tests();
+ tmp_flags = shfo.fFlags;
+ set_curr_dir_path(from, "test1.txt\0test10.txt\0test2.txt\0");
+ ok(!file_exists(".\\testdir2\\test1.txt"), "The file is not copied yet\n");
+ ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not copied yet\n");
+ retval = SHFileOperationA(&shfo);
+ if (!retval)
+ /* Win 95/NT returns success but copies only the files up to the nonexistent source */
+ ok(file_exists(".\\testdir2\\test1.txt"), "The file is not copied\n");
+ else
+ {
+ /* Win 98/ME/2K/XP fail the entire operation with return code 1026 if one source file does not exist */
+ ok(retval == 1026, "Files are copied to other directory\n");
+ ok(!file_exists(".\\testdir2\\test1.txt"), "The file is copied\n");
+ }
+ ok(!file_exists(".\\testdir2\\test2.txt"), "The file is copied\n");
+ shfo.fFlags = tmp_flags;
+}
+
+/* tests the FO_MOVE action */
+static void test_move(void)
+{
+ SHFILEOPSTRUCTA shfo, shfo2;
+ CHAR from[MAX_PATH];
+ CHAR to[MAX_PATH];
+
+ shfo.hwnd = NULL;
+ shfo.wFunc = FO_MOVE;
+ shfo.pFrom = from;
+ shfo.pTo = to;
+ shfo.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+ shfo.hNameMappings = NULL;
+ shfo.lpszProgressTitle = NULL;
+
+ set_curr_dir_path(from, "test1.txt\0");
+ set_curr_dir_path(to, "test4.txt\0");
+ ok(!SHFileOperationA(&shfo), "Prepare test to check how directories are moved recursively\n");
+ ok(file_exists(".\\test4.txt\\test1.txt"), "The file is moved\n");
+
+ set_curr_dir_path(from, "test?.txt\0");
+ set_curr_dir_path(to, "testdir2\0");
+ ok(!file_exists(".\\testdir2\\test2.txt"), "The file is not moved yet\n");
+ ok(!file_exists(".\\testdir2\\test4.txt"), "The directory is not moved yet\n");
+ ok(!SHFileOperationA(&shfo), "Files and directories are moved to directory\n");
+ ok(file_exists(".\\testdir2\\test2.txt"), "The file is moved\n");
+ ok(file_exists(".\\testdir2\\test4.txt"), "The directory is moved\n");
+ ok(file_exists(".\\testdir2\\test4.txt\\test1.txt"), "The file in subdirectory is moved\n");
+
+ clean_after_shfo_tests();
+ init_shfo_tests();
+
+ memcpy(&shfo2, &shfo, sizeof(SHFILEOPSTRUCTA));
+ shfo2.fFlags |= FOF_MULTIDESTFILES;
+
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
+ ok(!SHFileOperationA(&shfo2), "Move many files\n");
+ ok(file_exists(".\\test6.txt"), "The file is moved - many files are "
+ "specified as a target\n");
+ DeleteFileA(".\\test6.txt");
+ DeleteFileA(".\\test7.txt");
+ RemoveDirectoryA(".\\test8.txt");
+
+ init_shfo_tests();
+
+ /* number of sources do not correspond to number of targets */
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0");
+ ok(SHFileOperationA(&shfo2), "Can't move many files\n");
+ ok(!file_exists(".\\test6.txt"), "The file is not moved - many files are "
+ "specified as a target\n");
+
+ init_shfo_tests();
+
+ set_curr_dir_path(from, "test3.txt\0");
+ set_curr_dir_path(to, "test4.txt\\test1.txt\0");
+ ok(!SHFileOperationA(&shfo), "File is moved moving to other directory\n");
+ ok(file_exists(".\\test4.txt\\test1.txt"), "The file is moved\n");
+
+ set_curr_dir_path(from, "test1.txt\0test2.txt\0test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0test7.txt\0test8.txt\0");
+ ok(SHFileOperationA(&shfo), "Cannot move many files\n");
+ ok(file_exists(".\\test1.txt"), "The file is not moved. Many files are specified\n");
+ ok(file_exists(".\\test4.txt"), "The directory is not moved. Many files are specified\n");
+
+ set_curr_dir_path(from, "test1.txt\0");
+ set_curr_dir_path(to, "test6.txt\0");
+ ok(!SHFileOperationA(&shfo), "Move file\n");
+ ok(!file_exists(".\\test1.txt"), "The file is moved\n");
+ ok(file_exists(".\\test6.txt"), "The file is moved\n");
+ set_curr_dir_path(from, "test6.txt\0");
+ set_curr_dir_path(to, "test1.txt\0");
+ ok(!SHFileOperationA(&shfo), "Move file back\n");
+
+ set_curr_dir_path(from, "test4.txt\0");
+ set_curr_dir_path(to, "test6.txt\0");
+ ok(!SHFileOperationA(&shfo), "Move dir\n");
+ ok(!file_exists(".\\test4.txt"), "The dir is moved\n");
+ ok(file_exists(".\\test6.txt"), "The dir is moved\n");
+ set_curr_dir_path(from, "test6.txt\0");
+ set_curr_dir_path(to, "test4.txt\0");
+ ok(!SHFileOperationA(&shfo), "Move dir back\n");
+}
+
+static void test_sh_create_dir(void)
+{
+ CHAR path[MAX_PATH];
+ int ret;
+
+ if(!pSHCreateDirectoryExA)
+ {
+ trace("skipping SHCreateDirectoryExA tests\n");
+ return;
+ }
+
+ set_curr_dir_path(path, "testdir2\\test4.txt\0");
+ ret = pSHCreateDirectoryExA(NULL, path, NULL);
+ ok(ERROR_SUCCESS == ret, "SHCreateDirectoryEx failed to create directory recursively, ret = %d\n", ret);
+ ok(file_exists(".\\testdir2"), "The first directory is not created\n");
+ ok(file_exists(".\\testdir2\\test4.txt"), "The second directory is not created\n");
+
+ ret = pSHCreateDirectoryExA(NULL, path, NULL);
+ ok(ERROR_ALREADY_EXISTS == ret, "SHCreateDirectoryEx should fail to create existing directory, ret = %d\n", ret);
+}
+
+START_TEST(shlfileop)
+{
+ InitFunctionPointers();
+
+ clean_after_shfo_tests();
+
+ init_shfo_tests();
+ test_delete();
+ clean_after_shfo_tests();
+
+ init_shfo_tests();
+ test_rename();
+ clean_after_shfo_tests();
+
+ init_shfo_tests();
+ test_copy();
+ clean_after_shfo_tests();
+
+ init_shfo_tests();
+ test_move();
+ clean_after_shfo_tests();
+
+ test_sh_create_dir();
+ clean_after_shfo_tests();
+}
--- /dev/null
+/*
+ * Unit test of the IShellFolder functions.
+ *
+ * Copyright 2004 Vitaliy Margolen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+
+
+#include "shlguid.h"
+#include "shlobj.h"
+//#include "shobjidl.h"
+#include "shlwapi.h"
+
+
+#include "wine/unicode.h"
+#include "wine/test.h"
+
+
+static IMalloc *ppM;
+
+static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
+static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
+
+static void init_function_pointers(void)
+{
+ HMODULE hmod = GetModuleHandleA("shell32.dll");
+ HRESULT hr;
+
+ if(hmod)
+ {
+ pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
+ pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
+ }
+
+ hr = SHGetMalloc(&ppM);
+ ok(hr == S_OK, "SHGetMalloc failed %08lx\n", hr);
+}
+
+static void test_ParseDisplayName(void)
+{
+ HRESULT hr;
+ IShellFolder *IDesktopFolder;
+ static const char *cNonExistDir1A = "c:\\nonexist_subdir";
+ static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
+ DWORD res;
+ WCHAR cTestDirW [MAX_PATH] = {0};
+ ITEMIDLIST *newPIDL;
+
+ hr = SHGetDesktopFolder(&IDesktopFolder);
+ if(hr != S_OK) return;
+
+ res = GetFileAttributesA(cNonExistDir1A);
+ if(res != INVALID_FILE_ATTRIBUTES) return;
+
+ MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
+ hr = IShellFolder_ParseDisplayName(IDesktopFolder,
+ NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
+ ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL),
+ "ParseDisplayName returned %08lx, expected 80070002 or E_FAIL\n", hr);
+
+ res = GetFileAttributesA(cNonExistDir2A);
+ if(res != INVALID_FILE_ATTRIBUTES) return;
+
+ MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
+ hr = IShellFolder_ParseDisplayName(IDesktopFolder,
+ NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
+ ok((hr == E_FAIL), "ParseDisplayName returned %08lx, expected E_FAIL\n", hr);
+}
+
+/* creates a file with the specified name for tests */
+static void CreateTestFile(const CHAR *name)
+{
+ HANDLE file;
+ DWORD written;
+
+ file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
+ if (file != INVALID_HANDLE_VALUE)
+ {
+ WriteFile(file, name, strlen(name), &written, NULL);
+ WriteFile(file, "\n", strlen("\n"), &written, NULL);
+ CloseHandle(file);
+ }
+}
+
+
+/* initializes the tests */
+static void CreateFilesFolders(void)
+{
+ CreateDirectoryA(".\\testdir", NULL);
+ CreateDirectoryA(".\\testdir\\test.txt", NULL);
+ CreateTestFile (".\\testdir\\test1.txt ");
+ CreateTestFile (".\\testdir\\test2.txt ");
+ CreateTestFile (".\\testdir\\test3.txt ");
+ CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
+ CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
+}
+
+/* cleans after tests */
+static void Cleanup(void)
+{
+ DeleteFileA(".\\testdir\\test1.txt");
+ DeleteFileA(".\\testdir\\test2.txt");
+ DeleteFileA(".\\testdir\\test3.txt");
+ RemoveDirectoryA(".\\testdir\\test.txt");
+ RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
+ RemoveDirectoryA(".\\testdir\\testdir2");
+ RemoveDirectoryA(".\\testdir");
+}
+
+
+/* perform test */
+static void test_EnumObjects(IShellFolder *iFolder)
+{
+ IEnumIDList *iEnumList;
+ LPITEMIDLIST newPIDL, idlArr[10];
+ ULONG NumPIDLs;
+ int i=0, j;
+ HRESULT hr;
+
+ static const WORD iResults [5][5] =
+ {
+ { 0,-1,-1,-1,-1},
+ { 1, 0,-1,-1,-1},
+ { 1, 1, 0,-1,-1},
+ { 1, 1, 1, 0,-1},
+ { 1, 1, 1, 1, 0}
+ };
+
+ /* Just test SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR for now */
+ static const ULONG attrs[5] =
+ {
+ SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
+ SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
+ SFGAO_FILESYSTEM,
+ SFGAO_FILESYSTEM,
+ SFGAO_FILESYSTEM,
+ };
+
+ hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
+ ok(hr == S_OK, "EnumObjects failed %08lx\n", hr);
+
+ /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
+ * the filesystem shellfolders return S_OK even if less than 'celt' items are
+ * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
+ * only ever returns a single entry per call. */
+ while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK)
+ i += NumPIDLs;
+ ok (i == 5, "i: %d\n", i);
+
+ hr = IEnumIDList_Release(iEnumList);
+ ok(hr == S_OK, "IEnumIDList_Release failed %08lx\n", hr);
+
+ /* Sort them first in case of wrong order from system */
+ for (i=0;i<5;i++) for (j=0;j<5;j++)
+ if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
+ {
+ newPIDL = idlArr[i];
+ idlArr[i] = idlArr[j];
+ idlArr[j] = newPIDL;
+ }
+
+ for (i=0;i<5;i++) for (j=0;j<5;j++)
+ {
+ hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
+ ok(hr == iResults[i][j], "Got %lx expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
+ }
+
+
+ for (i = 0; i < 5; i++)
+ {
+ SFGAOF flags;
+ flags = SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
+ hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
+ flags &= SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR;
+ ok(hr == S_OK, "GetAttributesOf returns %08lx\n", hr);
+ ok(flags == attrs[i], "GetAttributesOf gets attrs %08lx, expects %08lx\n", flags, attrs[i]);
+ }
+
+ for (i=0;i<5;i++)
+ IMalloc_Free(ppM, idlArr[i]);
+}
+
+static void test_BindToObject(void)
+{
+ HRESULT hr;
+ UINT cChars;
+ IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
+ SHITEMID emptyitem = { 0, { 0 } };
+ LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
+ WCHAR wszSystemDir[MAX_PATH];
+ WCHAR wszMyComputer[] = {
+ ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
+ 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
+
+ /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
+ * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
+ */
+ hr = SHGetDesktopFolder(&psfDesktop);
+ ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) return;
+
+ hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
+ ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
+
+ hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
+ ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
+
+ hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
+ ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
+ if (FAILED(hr)) {
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
+ ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
+ IShellFolder_Release(psfDesktop);
+ IMalloc_Free(ppM, pidlMyComputer);
+ if (FAILED(hr)) return;
+
+ hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
+ ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
+
+ hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
+ ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
+
+ cChars = GetSystemDirectoryW(wszSystemDir, MAX_PATH);
+ ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryW failed! LastError: %08lx\n", GetLastError());
+ if (cChars == 0 || cChars >= MAX_PATH) {
+ IShellFolder_Release(psfMyComputer);
+ return;
+ }
+
+ hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
+ ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08lx\n", hr);
+ if (FAILED(hr)) {
+ IShellFolder_Release(psfMyComputer);
+ return;
+ }
+
+ hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
+ ok (SUCCEEDED(hr), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08lx\n", hr);
+ IShellFolder_Release(psfMyComputer);
+ IMalloc_Free(ppM, pidlSystemDir);
+ if (FAILED(hr)) return;
+
+ hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
+ ok (hr == E_INVALIDARG,
+ "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08lx\n", hr);
+
+ hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
+ ok (hr == E_INVALIDARG,
+ "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08lx\n", hr);
+
+ IShellFolder_Release(psfSystemDir);
+}
+
+static void test_GetDisplayName(void)
+{
+ BOOL result;
+ HRESULT hr;
+ HANDLE hTestFile;
+ WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH], wszTestDir[MAX_PATH];
+ STRRET strret;
+ LPSHELLFOLDER psfDesktop, psfPersonal;
+ IUnknown *psfFile;
+ LPITEMIDLIST pidlTestFile;
+ LPCITEMIDLIST pidlLast;
+ static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
+ static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };
+
+ /* I'm trying to figure if there is a functional difference between calling
+ * SHGetPathFromIDList and calling GetDisplayNameOf(SHGDN_FORPARSING) after
+ * binding to the shellfolder. One thing I thought of was that perhaps
+ * SHGetPathFromIDList would be able to get the path to a file, which does
+ * not exist anymore, while the other method would'nt. It turns out there's
+ * no functional difference in this respect.
+ */
+
+ if(!pSHGetSpecialFolderPathW) return;
+
+ /* First creating a directory in MyDocuments and a file in this directory. */
+ result = pSHGetSpecialFolderPathW(NULL, wszTestDir, CSIDL_PERSONAL, FALSE);
+ ok(result, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError());
+ if (!result) return;
+
+ PathAddBackslashW(wszTestDir);
+ lstrcatW(wszTestDir, wszDirName);
+ result = CreateDirectoryW(wszTestDir, NULL);
+ ok(result, "CreateDirectoryW failed! Last error: %08lx\n", GetLastError());
+ if (!result) return;
+
+ lstrcpyW(wszTestFile, wszTestDir);
+ PathAddBackslashW(wszTestFile);
+ lstrcatW(wszTestFile, wszFileName);
+
+ hTestFile = CreateFileW(wszTestFile, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
+ ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %08lx\n", GetLastError());
+ if (hTestFile == INVALID_HANDLE_VALUE) return;
+ CloseHandle(hTestFile);
+
+ /* Getting an itemidlist for the file. */
+ hr = SHGetDesktopFolder(&psfDesktop);
+ ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) return;
+
+ hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
+ ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) {
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ /* It seems as if we cannot bind to regular files on windows, but only directories.
+ */
+ hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
+ todo_wine { ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hr = %08lx\n", hr); }
+ if (SUCCEEDED(hr)) {
+ IShellFolder_Release(psfFile);
+ }
+
+ /* Deleting the file and the directory */
+ DeleteFileW(wszTestFile);
+ RemoveDirectoryW(wszTestDir);
+
+ /* SHGetPathFromIDListW still works, although the file is not present anymore. */
+ result = SHGetPathFromIDListW(pidlTestFile, wszTestFile2);
+ ok (result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
+ ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
+
+ if(!pSHBindToParent) return;
+
+ /* Binding to the folder and querying the display name of the file also works. */
+ hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
+ ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) {
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
+ ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) {
+ IShellFolder_Release(psfDesktop);
+ IShellFolder_Release(psfPersonal);
+ return;
+ }
+
+ hr = StrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
+ ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08lx\n", hr);
+ ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
+
+ IShellFolder_Release(psfDesktop);
+ IShellFolder_Release(psfPersonal);
+}
+
+static void test_CallForAttributes(void)
+{
+ HKEY hKey;
+ LONG lResult;
+ HRESULT hr;
+ DWORD dwSize;
+ LPSHELLFOLDER psfDesktop;
+ LPITEMIDLIST pidlMyDocuments;
+ DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
+ static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
+ static const WCHAR wszCallForAttributes[] = {
+ 'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
+ static const WCHAR wszMyDocumentsKey[] = {
+ 'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
+ '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
+ '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
+ WCHAR wszMyDocuments[] = {
+ ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
+ '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
+
+ /* For the root of a namespace extension, the attributes are not queried by binding
+ * to the object and calling GetAttributesOf. Instead, the attributes are read from
+ * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
+ *
+ * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
+ * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
+ * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
+ * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
+ */
+ hr = SHGetDesktopFolder(&psfDesktop);
+ ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) return;
+
+ hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL,
+ &pidlMyDocuments, NULL);
+ ok (SUCCEEDED(hr),
+ "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08lx\n", hr);
+ if (FAILED(hr)) {
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ dwAttributes = 0xffffffff;
+ hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
+ (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
+ ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
+
+ /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
+ todo_wine{ ok (dwAttributes & SFGAO_FILESYSTEM,
+ "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n"); }
+ ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
+ ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");
+
+ /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
+ * key. So the test will return at this point, if run on wine.
+ */
+ lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
+ todo_wine { ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08lx\n", lResult); }
+ if (lResult != ERROR_SUCCESS) {
+ IMalloc_Free(ppM, pidlMyDocuments);
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ /* Query MyDocuments' Attributes value, to be able to restore it later. */
+ dwSize = sizeof(DWORD);
+ lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
+ ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
+ if (lResult != ERROR_SUCCESS) {
+ RegCloseKey(hKey);
+ IMalloc_Free(ppM, pidlMyDocuments);
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
+ dwSize = sizeof(DWORD);
+ lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL,
+ (LPBYTE)&dwOrigCallForAttributes, &dwSize);
+ ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08lx\n", lResult);
+ if (lResult != ERROR_SUCCESS) {
+ RegCloseKey(hKey);
+ IMalloc_Free(ppM, pidlMyDocuments);
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and
+ * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
+ * SFGAO_FILESYSTEM attributes. */
+ dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
+ RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
+ dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
+ RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
+ (LPBYTE)&dwCallForAttributes, sizeof(DWORD));
+
+ /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by
+ * GetAttributesOf. It seems that once there is a single attribute queried, for which
+ * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
+ * the flags in Attributes are ignored.
+ */
+ dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
+ hr = IShellFolder_GetAttributesOf(psfDesktop, 1,
+ (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
+ ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08lx\n", hr);
+ if (SUCCEEDED(hr))
+ ok (dwAttributes == SFGAO_FILESYSTEM,
+ "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08lx\n",
+ dwAttributes);
+
+ /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
+ RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
+ RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD,
+ (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
+ RegCloseKey(hKey);
+ IMalloc_Free(ppM, pidlMyDocuments);
+ IShellFolder_Release(psfDesktop);
+}
+
+static void test_GetAttributesOf(void)
+{
+ HRESULT hr;
+ LPSHELLFOLDER psfDesktop, psfMyComputer;
+ SHITEMID emptyitem = { 0, { 0 } };
+ LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
+ LPITEMIDLIST pidlMyComputer;
+ DWORD dwFlags;
+ const static DWORD dwDesktopFlags = /* As observed on WinXP SP2 */
+ SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
+ SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
+ const static DWORD dwMyComputerFlags = /* As observed on WinXP SP2 */
+ SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
+ SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
+ WCHAR wszMyComputer[] = {
+ ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
+ 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
+
+ hr = SHGetDesktopFolder(&psfDesktop);
+ ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) return;
+
+ /* The Desktop attributes can be queried with a single empty itemidlist, .. */
+ dwFlags = 0xffffffff;
+ hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
+ ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08lx\n", hr);
+ ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n",
+ dwFlags, dwDesktopFlags);
+
+ /* .. or with no itemidlist at all. */
+ dwFlags = 0xffffffff;
+ hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
+ ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08lx\n", hr);
+ ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08lx, expected: %08lx\n",
+ dwFlags, dwDesktopFlags);
+
+ /* Testing the attributes of the MyComputer shellfolder */
+ hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
+ ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
+ if (FAILED(hr)) {
+ IShellFolder_Release(psfDesktop);
+ return;
+ }
+
+ /* WinXP SP2 sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop
+ * folder object. It doesn't do this, if MyComputer is queried directly (see below).
+ * SFGAO_CANLINK is the same as DROPEFFECT_LINK, which MSDN says means: "Drag source
+ * should create a link to the original data". You can't create links on MyComputer on
+ * Windows, so this flag shouldn't be set. Seems like a bug in Windows. As long as nobody
+ * depends on this bug, we probably shouldn't imitate it.
+ */
+ dwFlags = 0xffffffff;
+ hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
+ ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08lx\n", hr);
+ todo_wine { ok ((dwFlags & ~(DWORD)SFGAO_CANLINK) == dwMyComputerFlags,
+ "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
+
+ hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
+ ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08lx\n", hr);
+ IShellFolder_Release(psfDesktop);
+ IMalloc_Free(ppM, pidlMyComputer);
+ if (FAILED(hr)) return;
+
+ hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
+ todo_wine {ok (hr == E_INVALIDARG, "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08lx\n", hr); }
+
+ dwFlags = 0xffffffff;
+ hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
+ ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08lx\n", hr);
+ todo_wine { ok (dwFlags == dwMyComputerFlags,
+ "Wrong MyComputer attributes: %08lx, expected: %08lx\n", dwFlags, dwMyComputerFlags); }
+
+ IShellFolder_Release(psfMyComputer);
+}
+
+static void test_SHGetPathFromIDList(void)
+{
+ SHITEMID emptyitem = { 0, { 0 } };
+ LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
+ LPITEMIDLIST pidlMyComputer;
+ WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
+ BOOL result;
+ HRESULT hr;
+ LPSHELLFOLDER psfDesktop;
+ WCHAR wszMyComputer[] = {
+ ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
+ 'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
+
+ if(!pSHGetSpecialFolderPathW) return;
+
+ /* Calling SHGetPathFromIDList with an empty pidl should return the desktop folder's path. */
+ result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
+ ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %08lx\n", GetLastError());
+ if (!result) return;
+
+ result = SHGetPathFromIDListW(pidlEmpty, wszPath);
+ ok(result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError());
+ if (!result) return;
+ ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDList didn't return desktop path for empty pidl!\n");
+
+ /* MyComputer does not map to a filesystem path. SHGetPathFromIDList should fail. */
+ hr = SHGetDesktopFolder(&psfDesktop);
+ ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08lx\n", hr);
+ if (FAILED(hr)) return;
+
+ hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
+ ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr);
+ IShellFolder_Release(psfDesktop);
+ if (FAILED(hr)) return;
+
+ SetLastError(0xdeadbeef);
+ result = SHGetPathFromIDListW(pidlMyComputer, wszPath);
+ ok (!result, "SHGetPathFromIDList succeeded where it shouldn't!\n");
+ ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDList shouldn't set last error! Last error: %08lx\n", GetLastError());
+
+ IMalloc_Free(ppM, pidlMyComputer);
+}
+
+static void test_EnumObjects_and_CompareIDs(void)
+{
+ ITEMIDLIST *newPIDL;
+ IShellFolder *IDesktopFolder, *testIShellFolder;
+ char cCurrDirA [MAX_PATH] = {0};
+ WCHAR cCurrDirW [MAX_PATH];
+ static const WCHAR cTestDirW[] = {'\\','t','e','s','t','d','i','r',0};
+ int len;
+ HRESULT hr;
+
+ GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
+ len = lstrlenA(cCurrDirA);
+
+ if(len == 0) {
+ trace("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
+ return;
+ }
+ if(cCurrDirA[len-1] == '\\')
+ cCurrDirA[len-1] = 0;
+
+ MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
+ strcatW(cCurrDirW, cTestDirW);
+
+ hr = SHGetDesktopFolder(&IDesktopFolder);
+ ok(hr == S_OK, "SHGetDesktopfolder failed %08lx\n", hr);
+
+ CreateFilesFolders();
+
+ hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
+ ok(hr == S_OK, "ParseDisplayName failed %08lx\n", hr);
+
+ hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
+ ok(hr == S_OK, "BindToObject failed %08lx\n", hr);
+
+ test_EnumObjects(testIShellFolder);
+
+ hr = IShellFolder_Release(testIShellFolder);
+ ok(hr == S_OK, "IShellFolder_Release failed %08lx\n", hr);
+
+ Cleanup();
+
+ IMalloc_Free(ppM, newPIDL);
+}
+
+START_TEST(shlfolder)
+{
+ init_function_pointers();
+ /* if OleInitialize doesn't get called, ParseDisplayName returns
+ CO_E_NOTINITIALIZED for malformed directory names on win2k. */
+ OleInitialize(NULL);
+
+ test_ParseDisplayName();
+ test_BindToObject();
+ test_EnumObjects_and_CompareIDs();
+ test_GetDisplayName();
+ test_GetAttributesOf();
+ test_SHGetPathFromIDList();
+ test_CallForAttributes();
+
+ OleUninitialize();
+}
--- /dev/null
+/*
+ * Unit tests for shell32 string operations
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#define WINE_NOWINSOCK
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "shellapi.h"
+#include "shtypes.h"
+#include "objbase.h"
+
+#include "wine/test.h"
+
+static HMODULE hShell32;
+static HRESULT (WINAPI *pStrRetToStrNAW)(LPVOID,DWORD,LPSTRRET,const ITEMIDLIST *);
+
+static WCHAR *CoDupStrW(const char* src)
+{
+ INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
+ WCHAR* szTemp = (WCHAR*)CoTaskMemAlloc(len * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
+ return szTemp;
+}
+
+static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
+{
+ while (*str1 && (*str1 == *str2)) { str1++; str2++; }
+ return *str1 - *str2;
+}
+
+static void test_StrRetToStringNA(void)
+{
+ trace("StrRetToStringNAW is Ascii\n");
+ /* FIXME */
+}
+
+static void test_StrRetToStringNW(void)
+{
+ static const WCHAR szTestW[] = { 'T','e','s','t','\0' };
+ ITEMIDLIST iidl[10];
+ WCHAR buff[128];
+ STRRET strret;
+ BOOL ret;
+
+ trace("StrRetToStringNAW is Unicode\n");
+
+ strret.uType = STRRET_WSTR;
+ strret.u.pOleStr = CoDupStrW("Test");
+ memset(buff, 0xff, sizeof(buff));
+ ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
+ ok(ret == TRUE && !strcmpW(buff, szTestW),
+ "STRRET_WSTR: dup failed, ret=%d\n", ret);
+
+ strret.uType = STRRET_CSTR;
+ lstrcpyA(strret.u.cStr, "Test");
+ memset(buff, 0xff, sizeof(buff));
+ ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
+ ok(ret == TRUE && !strcmpW(buff, szTestW),
+ "STRRET_CSTR: dup failed, ret=%d\n", ret);
+
+ strret.uType = STRRET_OFFSET;
+ strret.u.uOffset = 1;
+ strcpy((char*)&iidl, " Test");
+ memset(buff, 0xff, sizeof(buff));
+ ret = pStrRetToStrNAW(buff, sizeof(buff)/sizeof(WCHAR), &strret, iidl);
+ ok(ret == TRUE && !strcmpW(buff, szTestW),
+ "STRRET_OFFSET: dup failed, ret=%d\n", ret);
+
+ /* The next test crashes on W2K, WinXP and W2K3, so we don't test. */
+#if 0
+ /* Invalid dest - should return FALSE, except NT4 does not, so we don't check. */
+ strret.uType = STRRET_WSTR;
+ strret.u.pOleStr = CoDupStrW("Test");
+ pStrRetToStrNAW(NULL, sizeof(buff)/sizeof(WCHAR), &strret, NULL);
+ trace("NULL dest: ret=%d\n", ret);
+#endif
+}
+
+START_TEST(string)
+{
+ CoInitialize(0);
+
+ hShell32 = LoadLibraryA("shell32.dll");
+ if (!hShell32)
+ return;
+
+ pStrRetToStrNAW = (void*)GetProcAddress(hShell32, (LPSTR)96);
+ if (pStrRetToStrNAW)
+ {
+ if (!(GetVersion() & 0x80000000))
+ test_StrRetToStringNW();
+ else
+ test_StrRetToStringNA();
+ }
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_generated(void);
+extern void func_shelllink(void);
+extern void func_shellpath(void);
+extern void func_shlexec(void);
+extern void func_shlfileop(void);
+extern void func_shlfolder(void);
+extern void func_string(void);
+
+const struct test winetest_testlist[] =
+{
+// { "generated", func_generated },
+ { "shelllink", func_shelllink },
+ { "shellpath", func_shellpath },
+ { "shlexec", func_shlexec },
+ { "shlfileop", func_shlfileop },
+ { "shlfolder", func_shlfolder },
+ { "string", func_string },
+ { 0, 0 }
+};
--- /dev/null
+/* Unit test suite for SHLWAPI Compact List and IStream ordinal functions
+ *
+ * Copyright 2002 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "objbase.h"
+
+typedef struct tagSHLWAPI_CLIST
+{
+ ULONG ulSize;
+ ULONG ulId;
+} SHLWAPI_CLIST, *LPSHLWAPI_CLIST;
+
+typedef const SHLWAPI_CLIST* LPCSHLWAPI_CLIST;
+
+/* Items to add */
+static const SHLWAPI_CLIST SHLWAPI_CLIST_items[] =
+{
+ {4, 1},
+ {8, 3},
+ {12, 2},
+ {16, 8},
+ {20, 9},
+ {3, 11},
+ {9, 82},
+ {33, 16},
+ {32, 55},
+ {24, 100},
+ {39, 116},
+ { 0, 0}
+};
+
+/* Dummy IStream object for testing calls */
+typedef struct
+{
+ void* lpVtbl;
+ LONG ref;
+ int readcalls;
+ BOOL failreadcall;
+ BOOL failreadsize;
+ BOOL readbeyondend;
+ BOOL readreturnlarge;
+ int writecalls;
+ BOOL failwritecall;
+ BOOL failwritesize;
+ int seekcalls;
+ int statcalls;
+ BOOL failstatcall;
+ LPCSHLWAPI_CLIST item;
+ ULARGE_INTEGER pos;
+} _IDummyStream;
+
+static
+HRESULT WINAPI QueryInterface(_IDummyStream *This,REFIID riid, LPVOID *ppvObj)
+{
+ return S_OK;
+}
+
+static ULONG WINAPI AddRef(_IDummyStream *This)
+{
+ return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI Release(_IDummyStream *This)
+{
+ return InterlockedDecrement(&This->ref);
+}
+
+static HRESULT WINAPI Read(_IDummyStream* This, LPVOID lpMem, ULONG ulSize,
+ PULONG lpRead)
+{
+ HRESULT hRet = S_OK;
+ ++This->readcalls;
+
+ if (This->failreadcall)
+ {
+ return STG_E_ACCESSDENIED;
+ }
+ else if (This->failreadsize)
+ {
+ *lpRead = ulSize + 8;
+ return S_OK;
+ }
+ else if (This->readreturnlarge)
+ {
+ *((ULONG*)lpMem) = 0xffff01;
+ *lpRead = ulSize;
+ This->readreturnlarge = FALSE;
+ return S_OK;
+ }
+ if (ulSize == sizeof(ULONG))
+ {
+ /* Read size of item */
+ *((ULONG*)lpMem) = This->item->ulSize ? This->item->ulSize + sizeof(SHLWAPI_CLIST) : 0;
+ *lpRead = ulSize;
+ }
+ else
+ {
+ unsigned int i;
+ char* buff = (char*)lpMem;
+
+ /* Read item data */
+ if (!This->item->ulSize)
+ {
+ This->readbeyondend = TRUE;
+ *lpRead = 0;
+ return E_FAIL; /* Should never happen */
+ }
+ *((ULONG*)lpMem) = This->item->ulId;
+ *lpRead = ulSize;
+
+ for (i = 0; i < This->item->ulSize; i++)
+ buff[4+i] = i*2;
+
+ This->item++;
+ }
+ return hRet;
+}
+
+static HRESULT WINAPI Write(_IDummyStream* This, LPVOID lpMem, ULONG ulSize,
+ PULONG lpWritten)
+{
+ HRESULT hRet = S_OK;
+
+ ++This->writecalls;
+ if (This->failwritecall)
+ {
+ return STG_E_ACCESSDENIED;
+ }
+ else if (This->failwritesize)
+ {
+ *lpWritten = 0;
+ }
+ else
+ *lpWritten = ulSize;
+ return hRet;
+}
+
+static HRESULT WINAPI Seek(_IDummyStream* This, LARGE_INTEGER dlibMove,
+ DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
+{
+ ++This->seekcalls;
+ This->pos.QuadPart = dlibMove.QuadPart;
+ if (plibNewPosition)
+ plibNewPosition->QuadPart = dlibMove.QuadPart;
+ return S_OK;
+}
+
+static HRESULT WINAPI Stat(_IDummyStream* This, STATSTG* pstatstg,
+ DWORD grfStatFlag)
+{
+ ++This->statcalls;
+ if (This->failstatcall)
+ return E_FAIL;
+ if (pstatstg)
+ pstatstg->cbSize.QuadPart = This->pos.QuadPart;
+ return S_OK;
+}
+
+/* VTable */
+static void* iclvt[] =
+{
+ QueryInterface,
+ AddRef,
+ Release,
+ Read,
+ Write,
+ Seek,
+ NULL, /* SetSize */
+ NULL, /* CopyTo */
+ NULL, /* Commit */
+ NULL, /* Revert */
+ NULL, /* LockRegion */
+ NULL, /* UnlockRegion */
+ Stat,
+ NULL /* Clone */
+};
+
+/* Function ptrs for ordinal calls */
+static HMODULE SHLWAPI_hshlwapi = 0;
+
+static VOID (WINAPI *pSHLWAPI_19)(LPSHLWAPI_CLIST);
+static HRESULT (WINAPI *pSHLWAPI_20)(LPSHLWAPI_CLIST*,LPCSHLWAPI_CLIST);
+static BOOL (WINAPI *pSHLWAPI_21)(LPSHLWAPI_CLIST*,ULONG);
+static LPSHLWAPI_CLIST (WINAPI *pSHLWAPI_22)(LPSHLWAPI_CLIST,ULONG);
+static HRESULT (WINAPI *pSHLWAPI_17)(_IDummyStream*,LPSHLWAPI_CLIST);
+static HRESULT (WINAPI *pSHLWAPI_18)(_IDummyStream*,LPSHLWAPI_CLIST*);
+
+static BOOL (WINAPI *pSHLWAPI_166)(_IDummyStream*);
+static HRESULT (WINAPI *pSHLWAPI_184)(_IDummyStream*,LPVOID,ULONG);
+static HRESULT (WINAPI *pSHLWAPI_212)(_IDummyStream*,LPCVOID,ULONG);
+static HRESULT (WINAPI *pSHLWAPI_213)(_IDummyStream*);
+static HRESULT (WINAPI *pSHLWAPI_214)(_IDummyStream*,ULARGE_INTEGER*);
+
+
+static void InitFunctionPtrs(void)
+{
+ SHLWAPI_hshlwapi = LoadLibraryA("shlwapi.dll");
+ ok(SHLWAPI_hshlwapi != 0, "LoadLibrary failed\n");
+ if (SHLWAPI_hshlwapi)
+ {
+ pSHLWAPI_17 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)17);
+ ok(pSHLWAPI_17 != 0, "No Ordinal 17\n");
+ pSHLWAPI_18 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)18);
+ ok(pSHLWAPI_18 != 0, "No Ordinal 18\n");
+ pSHLWAPI_19 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)19);
+ ok(pSHLWAPI_19 != 0, "No Ordinal 19\n");
+ pSHLWAPI_20 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)20);
+ ok(pSHLWAPI_20 != 0, "No Ordinal 20\n");
+ pSHLWAPI_21 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)21);
+ ok(pSHLWAPI_21 != 0, "No Ordinal 21\n");
+ pSHLWAPI_22 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)22);
+ ok(pSHLWAPI_22 != 0, "No Ordinal 22\n");
+ pSHLWAPI_166 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)166);
+ ok(pSHLWAPI_166 != 0, "No Ordinal 166\n");
+ pSHLWAPI_184 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)184);
+ ok(pSHLWAPI_184 != 0, "No Ordinal 184\n");
+ pSHLWAPI_212 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)212);
+ ok(pSHLWAPI_212 != 0, "No Ordinal 212\n");
+ pSHLWAPI_213 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)213);
+ ok(pSHLWAPI_213 != 0, "No Ordinal 213\n");
+ pSHLWAPI_214 = (void *)GetProcAddress( SHLWAPI_hshlwapi, (LPSTR)214);
+ ok(pSHLWAPI_214 != 0, "No Ordinal 214\n");
+ }
+}
+
+static void InitDummyStream(_IDummyStream* iface)
+{
+ iface->lpVtbl = (void*)iclvt;
+ iface->ref = 1;
+ iface->readcalls = 0;
+ iface->failreadcall = FALSE;
+ iface->failreadsize = FALSE;
+ iface->readbeyondend = FALSE;
+ iface->readreturnlarge = FALSE;
+ iface->writecalls = 0;
+ iface->failwritecall = FALSE;
+ iface->failwritesize = FALSE;
+ iface->seekcalls = 0;
+ iface->statcalls = 0;
+ iface->failstatcall = FALSE;
+ iface->item = SHLWAPI_CLIST_items;
+ iface->pos.QuadPart = 0;
+}
+
+
+static void test_CList(void)
+{
+ _IDummyStream streamobj;
+ LPSHLWAPI_CLIST list = NULL;
+ LPCSHLWAPI_CLIST item = SHLWAPI_CLIST_items;
+ HRESULT hRet;
+ LPSHLWAPI_CLIST inserted;
+ BYTE buff[64];
+ unsigned int i;
+
+ if (!pSHLWAPI_17 || !pSHLWAPI_18 || !pSHLWAPI_19 || !pSHLWAPI_20 ||
+ !pSHLWAPI_21 || !pSHLWAPI_22)
+ return;
+
+ /* Populate a list and test the items are added correctly */
+ while (item->ulSize)
+ {
+ /* Create item and fill with data */
+ inserted = (LPSHLWAPI_CLIST)buff;
+ inserted->ulSize = item->ulSize + sizeof(SHLWAPI_CLIST);
+ inserted->ulId = item->ulId;
+ for (i = 0; i < item->ulSize; i++)
+ buff[sizeof(SHLWAPI_CLIST)+i] = i*2;
+
+ /* Add it */
+ hRet = pSHLWAPI_20(&list, inserted);
+ ok(hRet > S_OK, "failed list add\n");
+
+ if (hRet > S_OK)
+ {
+ ok(list && list->ulSize, "item not added\n");
+
+ /* Find it */
+ inserted = pSHLWAPI_22(list, item->ulId);
+ ok(inserted != NULL, "lost after adding\n");
+
+ ok(!inserted || inserted->ulId != ~0UL, "find returned a container\n");
+
+ /* Check size */
+ if (inserted && inserted->ulSize & 0x3)
+ {
+ /* Contained */
+ ok(inserted[-1].ulId == ~0UL, "invalid size is not countained\n");
+ ok(inserted[-1].ulSize > inserted->ulSize+sizeof(SHLWAPI_CLIST),
+ "container too small\n");
+ }
+ else if (inserted)
+ {
+ ok(inserted->ulSize==item->ulSize+sizeof(SHLWAPI_CLIST),
+ "id %ld size wrong (%ld!=%ld)\n", inserted->ulId, inserted->ulSize,
+ item->ulSize+sizeof(SHLWAPI_CLIST));
+ }
+ if (inserted)
+ {
+ BOOL bDataOK = TRUE;
+ LPBYTE bufftest = (LPBYTE)inserted;
+
+ for (i = 0; i < inserted->ulSize - sizeof(SHLWAPI_CLIST); i++)
+ if (bufftest[sizeof(SHLWAPI_CLIST)+i] != i*2)
+ bDataOK = FALSE;
+
+ ok(bDataOK == TRUE, "data corrupted on insert\n");
+ }
+ ok(!inserted || inserted->ulId==item->ulId, "find got wrong item\n");
+ }
+ item++;
+ }
+
+ /* Write the list */
+ InitDummyStream(&streamobj);
+
+ hRet = pSHLWAPI_17(&streamobj, list);
+ ok(hRet == S_OK, "write failed\n");
+ if (hRet == S_OK)
+ {
+ /* 1 call for each element, + 1 for OK (use our null element for this) */
+ ok(streamobj.writecalls == sizeof(SHLWAPI_CLIST_items)/sizeof(SHLWAPI_CLIST),
+ "wrong call count\n");
+ ok(streamobj.readcalls == 0,"called Read() in write\n");
+ ok(streamobj.seekcalls == 0,"called Seek() in write\n");
+ }
+
+ /* Failure cases for writing */
+ InitDummyStream(&streamobj);
+ streamobj.failwritecall = TRUE;
+ hRet = pSHLWAPI_17(&streamobj, list);
+ ok(hRet == STG_E_ACCESSDENIED, "changed object failure return\n");
+ ok(streamobj.writecalls == 1, "called object after failure\n");
+ ok(streamobj.readcalls == 0,"called Read() after failure\n");
+ ok(streamobj.seekcalls == 0,"called Seek() after failure\n");
+
+ InitDummyStream(&streamobj);
+ streamobj.failwritesize = TRUE;
+ hRet = pSHLWAPI_17(&streamobj, list);
+ ok(hRet == STG_E_MEDIUMFULL, "changed size failure return\n");
+ ok(streamobj.writecalls == 1, "called object after size failure\n");
+ ok(streamobj.readcalls == 0,"called Read() after failure\n");
+ ok(streamobj.seekcalls == 0,"called Seek() after failure\n");
+
+ /* Invalid inputs for adding */
+ inserted = (LPSHLWAPI_CLIST)buff;
+ inserted->ulSize = sizeof(SHLWAPI_CLIST) -1;
+ inserted->ulId = 33;
+ hRet = pSHLWAPI_20(&list, inserted);
+ /* The call succeeds but the item is not inserted, except on some early
+ * versions which return failure. Wine behaves like later versions.
+ */
+#if 0
+ ok(hRet == S_OK, "failed bad element size\n");
+#endif
+ inserted = pSHLWAPI_22(list, 33);
+ ok(inserted == NULL, "inserted bad element size\n");
+
+ inserted = (LPSHLWAPI_CLIST)buff;
+ inserted->ulSize = 44;
+ inserted->ulId = ~0UL;
+ hRet = pSHLWAPI_20(&list, inserted);
+ /* See comment above, some early versions fail this call */
+#if 0
+ ok(hRet == S_OK, "failed adding a container\n");
+#endif
+ item = SHLWAPI_CLIST_items;
+
+ /* Look for nonexistent item in populated list */
+ inserted = pSHLWAPI_22(list, 99999999);
+ ok(inserted == NULL, "found a nonexistent item\n");
+
+ while (item->ulSize)
+ {
+ /* Delete items */
+ BOOL bRet = pSHLWAPI_21(&list, item->ulId);
+ ok(bRet == TRUE, "couldn't find item to delete\n");
+ item++;
+ }
+
+ /* Look for nonexistent item in empty list */
+ inserted = pSHLWAPI_22(list, 99999999);
+ ok(inserted == NULL, "found an item in empty list\n");
+
+ /* Create a list by reading in data */
+ InitDummyStream(&streamobj);
+
+ hRet = pSHLWAPI_18(&streamobj, &list);
+ ok(hRet == S_OK, "failed create from Read()\n");
+ if (hRet == S_OK)
+ {
+ ok(streamobj.readbeyondend == FALSE, "read beyond end\n");
+ /* 2 calls per item, but only 1 for the terminator */
+ ok(streamobj.readcalls == sizeof(SHLWAPI_CLIST_items)/sizeof(SHLWAPI_CLIST)*2-1,
+ "wrong call count\n");
+ ok(streamobj.writecalls == 0, "called Write() from create\n");
+ ok(streamobj.seekcalls == 0,"called Seek() from create\n");
+
+ item = SHLWAPI_CLIST_items;
+
+ /* Check the items were added correctly */
+ while (item->ulSize)
+ {
+ inserted = pSHLWAPI_22(list, item->ulId);
+ ok(inserted != NULL, "lost after adding\n");
+
+ ok(!inserted || inserted->ulId != ~0UL, "find returned a container\n");
+
+ /* Check size */
+ if (inserted && inserted->ulSize & 0x3)
+ {
+ /* Contained */
+ ok(inserted[-1].ulId == ~0UL, "invalid size is not countained\n");
+ ok(inserted[-1].ulSize > inserted->ulSize+sizeof(SHLWAPI_CLIST),
+ "container too small\n");
+ }
+ else if (inserted)
+ {
+ ok(inserted->ulSize==item->ulSize+sizeof(SHLWAPI_CLIST),
+ "id %ld size wrong (%ld!=%ld)\n", inserted->ulId, inserted->ulSize,
+ item->ulSize+sizeof(SHLWAPI_CLIST));
+ }
+ ok(!inserted || inserted->ulId==item->ulId, "find got wrong item\n");
+ if (inserted)
+ {
+ BOOL bDataOK = TRUE;
+ LPBYTE bufftest = (LPBYTE)inserted;
+
+ for (i = 0; i < inserted->ulSize - sizeof(SHLWAPI_CLIST); i++)
+ if (bufftest[sizeof(SHLWAPI_CLIST)+i] != i*2)
+ bDataOK = FALSE;
+
+ ok(bDataOK == TRUE, "data corrupted on insert\n");
+ }
+ item++;
+ }
+ }
+
+ /* Failure cases for reading */
+ InitDummyStream(&streamobj);
+ streamobj.failreadcall = TRUE;
+ hRet = pSHLWAPI_18(&streamobj, &list);
+ ok(hRet == STG_E_ACCESSDENIED, "changed object failure return\n");
+ ok(streamobj.readbeyondend == FALSE, "read beyond end\n");
+ ok(streamobj.readcalls == 1, "called object after read failure\n");
+ ok(streamobj.writecalls == 0,"called Write() after read failure\n");
+ ok(streamobj.seekcalls == 0,"called Seek() after read failure\n");
+
+ /* Read returns large object */
+ InitDummyStream(&streamobj);
+ streamobj.readreturnlarge = TRUE;
+ hRet = pSHLWAPI_18(&streamobj, &list);
+ ok(hRet == S_OK, "failed create from Read() with large item\n");
+ ok(streamobj.readbeyondend == FALSE, "read beyond end\n");
+ ok(streamobj.readcalls == 1,"wrong call count\n");
+ ok(streamobj.writecalls == 0,"called Write() after read failure\n");
+ ok(streamobj.seekcalls == 2,"wrong Seek() call count (%d)\n", streamobj.seekcalls);
+
+ pSHLWAPI_19(list);
+}
+
+static BOOL test_SHLWAPI_166(void)
+{
+ _IDummyStream streamobj;
+ BOOL bRet;
+
+ if (!pSHLWAPI_166)
+ return FALSE;
+
+ InitDummyStream(&streamobj);
+ bRet = pSHLWAPI_166(&streamobj);
+
+ if (bRet != TRUE)
+ return FALSE; /* This version doesn't support stream ops on clists */
+
+ ok(streamobj.readcalls == 0, "called Read()\n");
+ ok(streamobj.writecalls == 0, "called Write()\n");
+ ok(streamobj.seekcalls == 0, "called Seek()\n");
+ ok(streamobj.statcalls == 1, "wrong call count\n");
+
+ streamobj.statcalls = 0;
+ streamobj.pos.QuadPart = 50001;
+
+ bRet = pSHLWAPI_166(&streamobj);
+
+ ok(bRet == FALSE, "failed after seek adjusted\n");
+ ok(streamobj.readcalls == 0, "called Read()\n");
+ ok(streamobj.writecalls == 0, "called Write()\n");
+ ok(streamobj.seekcalls == 0, "called Seek()\n");
+ ok(streamobj.statcalls == 1, "wrong call count\n");
+
+ /* Failure cases */
+ InitDummyStream(&streamobj);
+ streamobj.pos.QuadPart = 50001;
+ streamobj.failstatcall = TRUE; /* 1: Stat() Bad, Read() OK */
+ bRet = pSHLWAPI_166(&streamobj);
+ ok(bRet == FALSE, "should be FALSE after read is OK\n");
+ ok(streamobj.readcalls == 1, "wrong call count\n");
+ ok(streamobj.writecalls == 0, "called Write()\n");
+ ok(streamobj.seekcalls == 1, "wrong call count\n");
+ ok(streamobj.statcalls == 1, "wrong call count\n");
+ ok(streamobj.pos.QuadPart == 0, "Didn't seek to start\n");
+
+ InitDummyStream(&streamobj);
+ streamobj.pos.QuadPart = 50001;
+ streamobj.failstatcall = TRUE;
+ streamobj.failreadcall = TRUE; /* 2: Stat() Bad, Read() Bad Also */
+ bRet = pSHLWAPI_166(&streamobj);
+ ok(bRet == TRUE, "Should be true after read fails\n");
+ ok(streamobj.readcalls == 1, "wrong call count\n");
+ ok(streamobj.writecalls == 0, "called Write()\n");
+ ok(streamobj.seekcalls == 0, "Called Seek()\n");
+ ok(streamobj.statcalls == 1, "wrong call count\n");
+ ok(streamobj.pos.QuadPart == 50001, "called Seek() after read failed\n");
+ return TRUE;
+}
+
+static void test_SHLWAPI_184(void)
+{
+ _IDummyStream streamobj;
+ char buff[256];
+ HRESULT hRet;
+
+ if (!pSHLWAPI_184)
+ return;
+
+ InitDummyStream(&streamobj);
+ hRet = pSHLWAPI_184(&streamobj, buff, sizeof(buff));
+
+ ok(hRet == S_OK, "failed Read()\n");
+ ok(streamobj.readcalls == 1, "wrong call count\n");
+ ok(streamobj.writecalls == 0, "called Write()\n");
+ ok(streamobj.seekcalls == 0, "called Seek()\n");
+}
+
+static void test_SHLWAPI_212(void)
+{
+ _IDummyStream streamobj;
+ char buff[256];
+ HRESULT hRet;
+
+ if (!pSHLWAPI_212)
+ return;
+
+ InitDummyStream(&streamobj);
+ hRet = pSHLWAPI_212(&streamobj, buff, sizeof(buff));
+
+ ok(hRet == S_OK, "failed Write()\n");
+ ok(streamobj.readcalls == 0, "called Read()\n");
+ ok(streamobj.writecalls == 1, "wrong call count\n");
+ ok(streamobj.seekcalls == 0, "called Seek()\n");
+}
+
+static void test_SHLWAPI_213(void)
+{
+ _IDummyStream streamobj;
+ ULARGE_INTEGER ul;
+ LARGE_INTEGER ll;
+ HRESULT hRet;
+
+ if (!pSHLWAPI_213 || !pSHLWAPI_214)
+ return;
+
+ InitDummyStream(&streamobj);
+ ll.QuadPart = 5000l;
+ Seek(&streamobj, ll, 0, NULL); /* Seek to 5000l */
+
+ streamobj.seekcalls = 0;
+ pSHLWAPI_213(&streamobj); /* Should rewind */
+ ok(streamobj.statcalls == 0, "called Stat()\n");
+ ok(streamobj.readcalls == 0, "called Read()\n");
+ ok(streamobj.writecalls == 0, "called Write()\n");
+ ok(streamobj.seekcalls == 1, "wrong call count\n");
+
+ ul.QuadPart = 50001;
+ hRet = pSHLWAPI_214(&streamobj, &ul);
+ ok(hRet == S_OK, "failed Stat()\n");
+ ok(ul.QuadPart == 0, "213 didn't rewind stream\n");
+}
+
+static void test_SHLWAPI_214(void)
+{
+ _IDummyStream streamobj;
+ ULARGE_INTEGER ul;
+ LARGE_INTEGER ll;
+ HRESULT hRet;
+
+ if (!pSHLWAPI_214)
+ return;
+
+ InitDummyStream(&streamobj);
+ ll.QuadPart = 5000l;
+ Seek(&streamobj, ll, 0, NULL);
+ ul.QuadPart = 0;
+ streamobj.seekcalls = 0;
+ hRet = pSHLWAPI_214(&streamobj, &ul);
+
+ ok(hRet == S_OK, "failed Stat()\n");
+ ok(streamobj.statcalls == 1, "wrong call count\n");
+ ok(streamobj.readcalls == 0, "called Read()\n");
+ ok(streamobj.writecalls == 0, "called Write()\n");
+ ok(streamobj.seekcalls == 0, "called Seek()\n");
+ ok(ul.QuadPart == 5000l, "Stat gave wrong size\n");
+}
+
+START_TEST(clist)
+{
+ InitFunctionPtrs();
+
+ test_CList();
+
+ /* Test streaming if this version supports it */
+ if (test_SHLWAPI_166())
+ {
+ test_SHLWAPI_184();
+ test_SHLWAPI_212();
+ test_SHLWAPI_213();
+ test_SHLWAPI_214();
+ }
+
+ if (SHLWAPI_hshlwapi)
+ FreeLibrary(SHLWAPI_hshlwapi);
+}
--- /dev/null
+/* Unit test suite for SHLWAPI Class ID functions
+ *
+ * Copyright 2003 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#define INITGUID
+#include "wine/test.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winnls.h"
+#include "winuser.h"
+#include "shlguid.h"
+#include "wine/shobjidl.h"
+
+/* Function ptrs for ordinal calls */
+static HMODULE hShlwapi = 0;
+static BOOL (WINAPI *pSHLWAPI_269)(LPCSTR, CLSID *) = 0;
+static DWORD (WINAPI *pSHLWAPI_23)(REFGUID, LPSTR, INT) = 0;
+
+/* GUIDs to test */
+const GUID * TEST_guids[] = {
+ &CLSID_ShellDesktop,
+ &CLSID_ShellLink,
+ &CATID_BrowsableShellExt,
+ &CATID_BrowseInPlace,
+ &CATID_DeskBand,
+ &CATID_InfoBand,
+ &CATID_CommBand,
+ &FMTID_Intshcut,
+ &FMTID_InternetSite,
+ &CGID_Explorer,
+ &CGID_ShellDocView,
+ &CGID_ShellServiceObject,
+ &CGID_ExplorerBarDoc,
+ &IID_INewShortcutHookA,
+ &IID_IShellIcon,
+ &IID_IShellFolder,
+ &IID_IShellExtInit,
+ &IID_IShellPropSheetExt,
+ &IID_IPersistFolder,
+ &IID_IExtractIconA,
+ &IID_IShellDetails,
+ &IID_IDelayedRelease,
+ &IID_IShellLinkA,
+ &IID_IShellCopyHookA,
+ &IID_IFileViewerA,
+ &IID_ICommDlgBrowser,
+ &IID_IEnumIDList,
+ &IID_IFileViewerSite,
+ &IID_IContextMenu2,
+ &IID_IShellExecuteHookA,
+ &IID_IPropSheetPage,
+ &IID_INewShortcutHookW,
+ &IID_IFileViewerW,
+ &IID_IShellLinkW,
+ &IID_IExtractIconW,
+ &IID_IShellExecuteHookW,
+ &IID_IShellCopyHookW,
+ &IID_IRemoteComputer,
+ &IID_IQueryInfo,
+ &IID_IDockingWindow,
+ &IID_IDockingWindowSite,
+ &CLSID_NetworkPlaces,
+ &CLSID_NetworkDomain,
+ &CLSID_NetworkServer,
+ &CLSID_NetworkShare,
+ &CLSID_MyComputer,
+ &CLSID_Internet,
+ &CLSID_ShellFSFolder,
+ &CLSID_RecycleBin,
+ &CLSID_ControlPanel,
+ &CLSID_Printers,
+ &CLSID_MyDocuments,
+ NULL
+};
+
+DEFINE_GUID(IID_Endianess, 0x01020304, 0x0506, 0x0708, 0x09, 0x0A, 0x0B,
+ 0x0C, 0x0D, 0x0E, 0x0F, 0x0A);
+
+static void test_ClassIDs(void)
+{
+ const GUID **guids = TEST_guids;
+ char szBuff[256];
+ GUID guid;
+ DWORD dwLen;
+ BOOL bRet;
+ int i = 0;
+
+ if (!pSHLWAPI_269 || !pSHLWAPI_23)
+ return;
+
+ while (*guids)
+ {
+ dwLen = pSHLWAPI_23(*guids, szBuff, 256);
+ ok(dwLen == 39, "wrong size for id %d\n", i);
+
+ bRet = pSHLWAPI_269(szBuff, &guid);
+ ok(bRet != FALSE, "created invalid string '%s'\n", szBuff);
+
+ if (bRet)
+ ok(IsEqualGUID(*guids, &guid), "GUID created wrong %d\n", i);
+
+ guids++;
+ i++;
+ }
+
+ /* Test endianess */
+ dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 256);
+ ok(dwLen == 39, "wrong size for IID_Endianess\n");
+
+ ok(!strcmp(szBuff, "{01020304-0506-0708-090A-0B0C0D0E0F0A}"),
+ "Endianess Broken, got '%s'\n", szBuff);
+
+ /* test lengths */
+ szBuff[0] = ':';
+ dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 0);
+ ok(dwLen == 0, "accepted bad length\n");
+ ok(szBuff[0] == ':', "wrote to buffer with no length\n");
+
+ szBuff[0] = ':';
+ dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 38);
+ ok(dwLen == 0, "accepted bad length\n");
+ ok(szBuff[0] == ':', "wrote to buffer with no length\n");
+
+ szBuff[0] = ':';
+ dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 39);
+ ok(dwLen == 39, "rejected ok length\n");
+ ok(szBuff[0] == '{', "Didn't write to buffer with ok length\n");
+
+ /* Test string */
+ strcpy(szBuff, "{xxx-");
+ bRet = pSHLWAPI_269(szBuff, &guid);
+ ok(bRet == FALSE, "accepted invalid string\n");
+
+ dwLen = pSHLWAPI_23(&IID_Endianess, szBuff, 39);
+ ok(dwLen == 39, "rejected ok length\n");
+ ok(szBuff[0] == '{', "Didn't write to buffer with ok length\n");
+}
+
+
+START_TEST(clsid)
+{
+ hShlwapi = LoadLibraryA("shlwapi.dll");
+ ok(hShlwapi != 0, "LoadLibraryA failed\n");
+ if (hShlwapi)
+ {
+ pSHLWAPI_269 = (void*)GetProcAddress(hShlwapi, (LPSTR)269);
+ pSHLWAPI_23 = (void*)GetProcAddress(hShlwapi, (LPSTR)23);
+ }
+
+ test_ClassIDs();
+
+ if (hShlwapi)
+ FreeLibrary(hShlwapi);
+}
--- /dev/null
+/* File generated automatically from tools/winapi/test.dat; do not edit! */
+/* This file can be copied, modified and distributed without restriction. */
+
+/*
+ * Unit tests for data structure packing
+ */
+
+#define WINVER 0x0501
+#define _WIN32_IE 0x0501
+#define _WIN32_WINNT 0x0501
+
+#define WINE_NOWINSOCK
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wtypes.h"
+#include "winreg.h"
+#include "shlwapi.h"
+
+#include "wine/test.h"
+
+/***********************************************************************
+ * Compability macros
+ */
+
+#define DWORD_PTR UINT_PTR
+#define LONG_PTR INT_PTR
+#define ULONG_PTR UINT_PTR
+
+/***********************************************************************
+ * Windows API extension
+ */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define FIELD_ALIGNMENT(type, field) __alignof(((type*)0)->field)
+#elif defined(__GNUC__)
+# define FIELD_ALIGNMENT(type, field) __alignof__(((type*)0)->field)
+#else
+/* FIXME: Not sure if is possible to do without compiler extension */
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define _TYPE_ALIGNMENT(type) __alignof(type)
+#elif defined(__GNUC__)
+# define _TYPE_ALIGNMENT(type) __alignof__(type)
+#else
+/*
+ * FIXME: Not sure if is possible to do without compiler extension
+ * (if type is not just a name that is, if so the normal)
+ * TYPE_ALIGNMENT can be used)
+ */
+#endif
+
+#if defined(TYPE_ALIGNMENT) && defined(_MSC_VER) && _MSC_VER >= 800 && !defined(__cplusplus)
+#pragma warning(disable:4116)
+#endif
+
+#if !defined(TYPE_ALIGNMENT) && defined(_TYPE_ALIGNMENT)
+# define TYPE_ALIGNMENT _TYPE_ALIGNMENT
+#endif
+
+/***********************************************************************
+ * Test helper macros
+ */
+
+#ifdef FIELD_ALIGNMENT
+# define TEST_FIELD_ALIGNMENT(type, field, align) \
+ ok(FIELD_ALIGNMENT(type, field) == align, \
+ "FIELD_ALIGNMENT(" #type ", " #field ") == %d (expected " #align ")\n", \
+ (int)FIELD_ALIGNMENT(type, field))
+#else
+# define TEST_FIELD_ALIGNMENT(type, field, align) do { } while (0)
+#endif
+
+#define TEST_FIELD_OFFSET(type, field, offset) \
+ ok(FIELD_OFFSET(type, field) == offset, \
+ "FIELD_OFFSET(" #type ", " #field ") == %ld (expected " #offset ")\n", \
+ (long int)FIELD_OFFSET(type, field))
+
+#ifdef _TYPE_ALIGNMENT
+#define TEST__TYPE_ALIGNMENT(type, align) \
+ ok(_TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)_TYPE_ALIGNMENT(type))
+#else
+# define TEST__TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#ifdef TYPE_ALIGNMENT
+#define TEST_TYPE_ALIGNMENT(type, align) \
+ ok(TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", (int)TYPE_ALIGNMENT(type))
+#else
+# define TEST_TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#define TEST_TYPE_SIZE(type, size) \
+ ok(sizeof(type) == size, "sizeof(" #type ") == %d (expected " #size ")\n", ((int) sizeof(type)))
+
+/***********************************************************************
+ * Test macros
+ */
+
+#define TEST_FIELD(type, field_type, field_name, field_offset, field_size, field_align) \
+ TEST_TYPE_SIZE(field_type, field_size); \
+ TEST_FIELD_ALIGNMENT(type, field_name, field_align); \
+ TEST_FIELD_OFFSET(type, field_name, field_offset); \
+
+#define TEST_TYPE(type, size, align) \
+ TEST_TYPE_ALIGNMENT(type, align); \
+ TEST_TYPE_SIZE(type, size)
+
+#define TEST_TYPE_POINTER(type, size, align) \
+ TEST__TYPE_ALIGNMENT(*(type)0, align); \
+ TEST_TYPE_SIZE(*(type)0, size)
+
+#define TEST_TYPE_SIGNED(type) \
+ ok((type) -1 < 0, "(" #type ") -1 < 0\n");
+
+#define TEST_TYPE_UNSIGNED(type) \
+ ok((type) -1 > 0, "(" #type ") -1 > 0\n");
+
+static void test_pack_ASSOCF(void)
+{
+ /* ASSOCF */
+ TEST_TYPE(ASSOCF, 4, 4);
+ TEST_TYPE_UNSIGNED(ASSOCF);
+}
+
+static void test_pack_DLLGETVERSIONPROC(void)
+{
+ /* DLLGETVERSIONPROC */
+ TEST_TYPE(DLLGETVERSIONPROC, 4, 4);
+}
+
+static void test_pack_DLLVERSIONINFO(void)
+{
+ /* DLLVERSIONINFO (pack 8) */
+ TEST_TYPE(DLLVERSIONINFO, 20, 4);
+ TEST_FIELD(DLLVERSIONINFO, DWORD, cbSize, 0, 4, 4);
+ TEST_FIELD(DLLVERSIONINFO, DWORD, dwMajorVersion, 4, 4, 4);
+ TEST_FIELD(DLLVERSIONINFO, DWORD, dwMinorVersion, 8, 4, 4);
+ TEST_FIELD(DLLVERSIONINFO, DWORD, dwBuildNumber, 12, 4, 4);
+ TEST_FIELD(DLLVERSIONINFO, DWORD, dwPlatformID, 16, 4, 4);
+}
+
+static void test_pack_DLLVERSIONINFO2(void)
+{
+ /* DLLVERSIONINFO2 (pack 8) */
+ TEST_TYPE(DLLVERSIONINFO2, 32, 8);
+ TEST_FIELD(DLLVERSIONINFO2, DLLVERSIONINFO, info1, 0, 20, 4);
+ TEST_FIELD(DLLVERSIONINFO2, DWORD, dwFlags, 20, 4, 4);
+ TEST_FIELD(DLLVERSIONINFO2, ULONGLONG, ullVersion, 24, 8, 8);
+}
+
+static void test_pack_HUSKEY(void)
+{
+ /* HUSKEY */
+ TEST_TYPE(HUSKEY, 4, 4);
+}
+
+static void test_pack_IQueryAssociations(void)
+{
+ /* IQueryAssociations */
+}
+
+static void test_pack_PHUSKEY(void)
+{
+ /* PHUSKEY */
+ TEST_TYPE(PHUSKEY, 4, 4);
+ TEST_TYPE_POINTER(PHUSKEY, 4, 4);
+}
+
+static void test_pack(void)
+{
+ test_pack_ASSOCF();
+ test_pack_DLLGETVERSIONPROC();
+ test_pack_DLLVERSIONINFO();
+ test_pack_DLLVERSIONINFO2();
+ test_pack_HUSKEY();
+ test_pack_IQueryAssociations();
+ test_pack_PHUSKEY();
+}
+
+START_TEST(generated)
+{
+ test_pack();
+}
--- /dev/null
+/* Unit test suite for SHLWAPI ordinal functions
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winuser.h"
+
+/* Function ptrs for ordinal calls */
+static HMODULE hShlwapi;
+static int (WINAPI *pSHSearchMapInt)(const int*,const int*,int,int);
+static HRESULT (WINAPI *pGetAcceptLanguagesA)(LPSTR,LPDWORD);
+
+static HANDLE (WINAPI *pSHAllocShared)(LPCVOID,DWORD,DWORD);
+static LPVOID (WINAPI *pSHLockShared)(HANDLE,DWORD);
+static BOOL (WINAPI *pSHUnlockShared)(LPVOID);
+static BOOL (WINAPI *pSHFreeShared)(HANDLE,DWORD);
+
+static void test_GetAcceptLanguagesA(void)
+{ HRESULT retval;
+ DWORD buffersize, buffersize2, exactsize;
+ char buffer[100];
+
+ if (!pGetAcceptLanguagesA)
+ return;
+
+ buffersize = sizeof(buffer);
+ memset(buffer, 0, sizeof(buffer));
+ SetLastError(ERROR_SUCCESS);
+ retval = pGetAcceptLanguagesA( buffer, &buffersize);
+ trace("GetAcceptLanguagesA: retval %08lx, size %08lx, buffer (%s),"
+ " last error %ld\n", retval, buffersize, buffer, GetLastError());
+ if(retval != S_OK) {
+ trace("GetAcceptLanguagesA: skipping tests\n");
+ return;
+ }
+ ok( (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()) ||
+ (ERROR_CLASS_DOES_NOT_EXIST == GetLastError()) ||
+ (ERROR_PROC_NOT_FOUND == GetLastError()) ||
+ (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) ||
+ (ERROR_SUCCESS == GetLastError()), "last error set to %ld\n", GetLastError());
+ exactsize = strlen(buffer);
+
+ SetLastError(ERROR_SUCCESS);
+ retval = pGetAcceptLanguagesA( NULL, NULL);
+ ok(retval == E_FAIL,
+ "function result wrong: got %08lx; expected E_FAIL\n", retval);
+ ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+
+ buffersize = sizeof(buffer);
+ SetLastError(ERROR_SUCCESS);
+ retval = pGetAcceptLanguagesA( NULL, &buffersize);
+ ok(retval == E_FAIL,
+ "function result wrong: got %08lx; expected E_FAIL\n", retval);
+ ok(buffersize == sizeof(buffer),
+ "buffersize was changed (2nd parameter; not on Win2k)\n");
+ ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+
+ SetLastError(ERROR_SUCCESS);
+ retval = pGetAcceptLanguagesA( buffer, NULL);
+ ok(retval == E_FAIL,
+ "function result wrong: got %08lx; expected E_FAIL\n", retval);
+ ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+
+ buffersize = 0;
+ memset(buffer, 0, sizeof(buffer));
+ SetLastError(ERROR_SUCCESS);
+ retval = pGetAcceptLanguagesA( buffer, &buffersize);
+ ok(retval == E_FAIL,
+ "function result wrong: got %08lx; expected E_FAIL\n", retval);
+ ok(buffersize == 0,
+ "buffersize wrong(changed) got %08lx; expected 0 (2nd parameter; not on Win2k)\n", buffersize);
+ ok(ERROR_SUCCESS == GetLastError(), "last error set to %ld\n", GetLastError());
+
+ buffersize = buffersize2 = 1;
+ memset(buffer, 0, sizeof(buffer));
+ SetLastError(ERROR_SUCCESS);
+ retval = pGetAcceptLanguagesA( buffer, &buffersize);
+ switch(retval) {
+ case 0L:
+ if(buffersize == exactsize) {
+ ok( (ERROR_SUCCESS == GetLastError()) || (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) ||
+ (ERROR_PROC_NOT_FOUND == GetLastError()) || (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()),
+ "last error wrong: got %08lx; expected ERROR_SUCCESS(NT4)/ERROR_CALL_NOT_IMPLEMENTED(98/ME)/"
+ "ERROR_PROC_NOT_FOUND(NT4)/ERROR_NO_IMPERSONATION_TOKEN(XP)\n", GetLastError());
+ ok(exactsize == strlen(buffer),
+ "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), exactsize);
+ } else if((buffersize +1) == buffersize2) {
+ ok(ERROR_SUCCESS == GetLastError(),
+ "last error wrong: got %08lx; expected ERROR_SUCCESS\n", GetLastError());
+ ok(buffersize == strlen(buffer),
+ "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize);
+ } else
+ ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+ retval, buffersize, buffer, GetLastError());
+ break;
+ case E_INVALIDARG:
+ ok(buffersize == 0,
+ "buffersize wrong: got %08lx, expected 0 (2nd parameter;Win2k)\n", buffersize);
+ ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
+ "last error wrong: got %08lx; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
+ ok(buffersize2 == strlen(buffer),
+ "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize2);
+ break;
+ default:
+ ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+ retval, buffersize, buffer, GetLastError());
+ break;
+ }
+
+ buffersize = buffersize2 = exactsize;
+ memset(buffer, 0, sizeof(buffer));
+ SetLastError(ERROR_SUCCESS);
+ retval = pGetAcceptLanguagesA( buffer, &buffersize);
+ switch(retval) {
+ case 0L:
+ ok(ERROR_SUCCESS == GetLastError(),
+ "last error wrong: got %08lx; expected ERROR_SUCCESS\n", GetLastError());
+ if((buffersize == exactsize) /* XP */ ||
+ ((buffersize +1)== exactsize) /* 98 */)
+ ok(buffersize == strlen(buffer),
+ "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize);
+ else
+ ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+ retval, buffersize, buffer, GetLastError());
+ break;
+ case E_INVALIDARG:
+ ok(buffersize == 0,
+ "buffersize wrong: got %08lx, expected 0 (2nd parameter;Win2k)\n", buffersize);
+ ok(ERROR_INSUFFICIENT_BUFFER == GetLastError(),
+ "last error wrong: got %08lx; expected ERROR_INSUFFICIENT_BUFFER\n", GetLastError());
+ ok(buffersize2 == strlen(buffer),
+ "buffer content (length) wrong: got %08x, expected %08lx \n", strlen(buffer), buffersize2);
+ break;
+ default:
+ ok( 0, "retval %08lx, size %08lx, buffer (%s), last error %ld\n",
+ retval, buffersize, buffer, GetLastError());
+ break;
+ }
+}
+
+static void test_SHSearchMapInt(void)
+{
+ int keys[8], values[8];
+ int i = 0;
+
+ if (!pSHSearchMapInt)
+ return;
+
+ memset(keys, 0, sizeof(keys));
+ memset(values, 0, sizeof(values));
+ keys[0] = 99; values[0] = 101;
+
+ /* NULL key/value lists crash native, so skip testing them */
+
+ /* 1 element */
+ i = pSHSearchMapInt(keys, values, 1, keys[0]);
+ ok(i == values[0], "Len 1, expected %d, got %d\n", values[0], i);
+
+ /* Key doesn't exist */
+ i = pSHSearchMapInt(keys, values, 1, 100);
+ ok(i == -1, "Len 1 - bad key, expected -1, got %d\n", i);
+
+ /* Len = 0 => not found */
+ i = pSHSearchMapInt(keys, values, 0, keys[0]);
+ ok(i == -1, "Len 1 - passed len 0, expected -1, got %d\n", i);
+
+ /* 2 elements, len = 1 */
+ keys[1] = 98; values[1] = 102;
+ i = pSHSearchMapInt(keys, values, 1, keys[1]);
+ ok(i == -1, "Len 1 - array len 2, expected -1, got %d\n", i);
+
+ /* 2 elements, len = 2 */
+ i = pSHSearchMapInt(keys, values, 2, keys[1]);
+ ok(i == values[1], "Len 2, expected %d, got %d\n", values[1], i);
+
+ /* Searches forward */
+ keys[2] = 99; values[2] = 103;
+ i = pSHSearchMapInt(keys, values, 3, keys[0]);
+ ok(i == values[0], "Len 3, expected %d, got %d\n", values[0], i);
+}
+
+static void test_alloc_shared(void)
+{
+ DWORD procid;
+ HANDLE hmem;
+ int val;
+ int* p;
+ BOOL ret;
+
+ procid=GetCurrentProcessId();
+ hmem=pSHAllocShared(NULL,10,procid);
+ ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+ ret = pSHFreeShared(hmem, procid);
+ ok( ret, "SHFreeShared failed: %ld\n", GetLastError());
+
+ val=0x12345678;
+ hmem=pSHAllocShared(&val,4,procid);
+ ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+
+ p=(int*)pSHLockShared(hmem,procid);
+ ok(p!=NULL,"SHLockShared failed: %ld\n", GetLastError());
+ if (p!=NULL)
+ ok(*p==val,"Wrong value in shared memory: %d instead of %d\n",*p,val);
+ ret = pSHUnlockShared(p);
+ ok( ret, "SHUnlockShared failed: %ld\n", GetLastError());
+
+ ret = pSHFreeShared(hmem, procid);
+ ok( ret, "SHFreeShared failed: %ld\n", GetLastError());
+}
+
+START_TEST(ordinal)
+{
+ hShlwapi = LoadLibraryA("shlwapi.dll");
+ ok(hShlwapi != 0, "LoadLibraryA failed\n");
+ if (!hShlwapi)
+ return;
+
+ pGetAcceptLanguagesA = (void*)GetProcAddress(hShlwapi, (LPSTR)14);
+ pSHSearchMapInt = (void*)GetProcAddress(hShlwapi, (LPSTR)198);
+ pSHAllocShared=(void*)GetProcAddress(hShlwapi,(char*)7);
+ pSHLockShared=(void*)GetProcAddress(hShlwapi,(char*)8);
+ pSHUnlockShared=(void*)GetProcAddress(hShlwapi,(char*)9);
+ pSHFreeShared=(void*)GetProcAddress(hShlwapi,(char*)10);
+
+ test_GetAcceptLanguagesA();
+ test_SHSearchMapInt();
+ test_alloc_shared();
+ FreeLibrary(hShlwapi);
+}
--- /dev/null
+/* Unit test suite for Path functions
+ *
+ * Copyright 2002 Matthew Mastracci
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wine/unicode.h"
+#include "winreg.h"
+#include "shlwapi.h"
+#include "wininet.h"
+
+static HMODULE hShlwapi;
+static HRESULT (WINAPI *pPathIsValidCharA)(char,DWORD);
+static HRESULT (WINAPI *pPathIsValidCharW)(WCHAR,DWORD);
+
+const char* TEST_URL_1 = "http://www.winehq.org/tests?date=10/10/1923";
+const char* TEST_URL_2 = "http://localhost:8080/tests%2e.html?date=Mon%2010/10/1923";
+const char* TEST_URL_3 = "http://foo:bar@localhost:21/internal.php?query=x&return=y";
+
+typedef struct _TEST_URL_CANONICALIZE {
+ const char *url;
+ DWORD flags;
+ HRESULT expectret;
+ const char *expecturl;
+} TEST_URL_CANONICALIZE;
+
+const TEST_URL_CANONICALIZE TEST_CANONICALIZE[] = {
+ /*FIXME {"http://www.winehq.org/tests/../tests/../..", 0, S_OK, "http://www.winehq.org/"},*/
+ {"http://www.winehq.org/tests/../tests", 0, S_OK, "http://www.winehq.org/tests"},
+ {"http://www.winehq.org/tests\n", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, S_OK, "http://www.winehq.org/tests"},
+ {"http://www.winehq.org/tests\r", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, S_OK, "http://www.winehq.org/tests"},
+ {"http://www.winehq.org/tests\r", 0, S_OK, "http://www.winehq.org/tests"},
+ {"http://www.winehq.org/tests\r", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests"},
+ {"http://www.winehq.org/tests/../tests/", 0, S_OK, "http://www.winehq.org/tests/"},
+ {"http://www.winehq.org/tests/../tests/..", 0, S_OK, "http://www.winehq.org/"},
+ {"http://www.winehq.org/tests/../tests/../", 0, S_OK, "http://www.winehq.org/"},
+ {"http://www.winehq.org/tests/..", 0, S_OK, "http://www.winehq.org/"},
+ {"http://www.winehq.org/tests/../", 0, S_OK, "http://www.winehq.org/"},
+ {"http://www.winehq.org/tests/..?query=x&return=y", 0, S_OK, "http://www.winehq.org/?query=x&return=y"},
+ {"http://www.winehq.org/tests/../?query=x&return=y", 0, S_OK, "http://www.winehq.org/?query=x&return=y"},
+ {"http://www.winehq.org/tests/..#example", 0, S_OK, "http://www.winehq.org/#example"},
+ {"http://www.winehq.org/tests/../#example", 0, S_OK, "http://www.winehq.org/#example"},
+ {"http://www.winehq.org/tests/../#example", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests/../#example"},
+ {"http://www.winehq.org/tests/foo bar", URL_ESCAPE_SPACES_ONLY| URL_DONT_ESCAPE_EXTRA_INFO , S_OK, "http://www.winehq.org/tests/foo%20bar"},
+ {"http://www.winehq.org/tests/foo%20bar", URL_UNESCAPE , S_OK, "http://www.winehq.org/tests/foo bar"},
+ {"file:///c:/tests/foo%20bar", URL_UNESCAPE , S_OK, "file:///c:/tests/foo bar"},
+};
+
+typedef struct _TEST_URL_ESCAPE {
+ const char *url;
+ DWORD flags;
+ DWORD expectescaped;
+ HRESULT expectret;
+ const char *expecturl;
+} TEST_URL_ESCAPE;
+
+const TEST_URL_ESCAPE TEST_ESCAPE[] = {
+ {"http://www.winehq.org/tests0", 0, 0, S_OK, "http://www.winehq.org/tests0"},
+ {"http://www.winehq.org/tests1\n", 0, 0, S_OK, "http://www.winehq.org/tests1%0A"},
+ {"http://www.winehq.org/tests2\r", 0, 0, S_OK, "http://www.winehq.org/tests2%0D"},
+ {"http://www.winehq.org/tests3\r", URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, 0, S_OK, "http://www.winehq.org/tests3\r"},
+ {"http://www.winehq.org/tests4\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests4\r"},
+ {"http://www.winehq.org/tests5\r", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests5\r"},
+ {"/direct/swhelp/series6/6.2i_latestservicepack.dat\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "/direct/swhelp/series6/6.2i_latestservicepack.dat\r"},
+
+ {"file://////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+ {"file://///foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+ {"file:////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+ {"file:///localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
+ {"file:///foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+ {"file://loCalHost/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+ {"file://foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+ {"file:/localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
+ {"file:/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+ {"file:foo/bar\\baz", 0, 0, S_OK, "file:foo/bar/baz"},
+ {"file:\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+ {"file:\\\\foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
+ {"file:\\\\\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+ {"file:\\\\localhost\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
+ {"file:///f oo/b?a r\\baz", 0, 0, S_OK, "file:///f%20oo/b?a r\\baz"},
+ {"file:///foo/b#a r\\baz", 0, 0, S_OK, "file:///foo/b%23a%20r/baz"},
+ {"file:///f o^&`{}|][\"<>\\%o/b#a r\\baz", 0, 0, S_OK, "file:///f%20o%5E%26%60%7B%7D%7C%5D%5B%22%3C%3E/%o/b%23a%20r/baz"},
+ {"file:///f o%o/b?a r\\b%az", URL_ESCAPE_PERCENT, 0, S_OK, "file:///f%20o%25o/b?a r\\b%az"},
+ {"file:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "file:%2Ffoo%2Fbar%5Cbaz"},
+
+ {"foo/b%ar\\ba?z\\", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%ar%5Cba%3Fz%5C"},
+ {"foo/b%ar\\ba?z\\", URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%25ar%5Cba%3Fz%5C"},
+ {"foo/bar\\ba?z\\", 0, 0, S_OK, "foo/bar%5Cba?z\\"},
+ {"/foo/bar\\ba?z\\", 0, 0, S_OK, "/foo/bar%5Cba?z\\"},
+ {"/foo/bar\\ba#z\\", 0, 0, S_OK, "/foo/bar%5Cba#z\\"},
+ {"/foo/%5C", 0, 0, S_OK, "/foo/%5C"},
+ {"/foo/%5C", URL_ESCAPE_PERCENT, 0, S_OK, "/foo/%255C"},
+
+ {"http://////foo/bar\\baz", 0, 0, S_OK, "http://////foo/bar/baz"},
+ {"http://///foo/bar\\baz", 0, 0, S_OK, "http://///foo/bar/baz"},
+ {"http:////foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
+ {"http:///foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
+ {"http://localhost/foo/bar\\baz", 0, 0, S_OK, "http://localhost/foo/bar/baz"},
+ {"http://foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
+ {"http:/foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
+ {"http:foo/bar\\ba?z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba?z\\"},
+ {"http:foo/bar\\ba#z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba#z\\"},
+ {"http:\\foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
+ {"http:\\\\foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
+ {"http:\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
+ {"http:\\\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
+ {"http:/fo ?o/b ar\\baz", 0, 0, S_OK, "http:/fo%20?o/b ar\\baz"},
+ {"http:fo ?o/b ar\\baz", 0, 0, S_OK, "http:fo%20?o/b ar\\baz"},
+ {"http:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "http:%2Ffoo%2Fbar%5Cbaz"},
+
+ {"https://foo/bar\\baz", 0, 0, S_OK, "https://foo/bar/baz"},
+ {"https:/foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
+ {"https:\\foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
+
+ {"foo:////foo/bar\\baz", 0, 0, S_OK, "foo:////foo/bar%5Cbaz"},
+ {"foo:///foo/bar\\baz", 0, 0, S_OK, "foo:///foo/bar%5Cbaz"},
+ {"foo://localhost/foo/bar\\baz", 0, 0, S_OK, "foo://localhost/foo/bar%5Cbaz"},
+ {"foo://foo/bar\\baz", 0, 0, S_OK, "foo://foo/bar%5Cbaz"},
+ {"foo:/foo/bar\\baz", 0, 0, S_OK, "foo:/foo/bar%5Cbaz"},
+ {"foo:foo/bar\\baz", 0, 0, S_OK, "foo:foo%2Fbar%5Cbaz"},
+ {"foo:\\foo/bar\\baz", 0, 0, S_OK, "foo:%5Cfoo%2Fbar%5Cbaz"},
+ {"foo:/foo/bar\\ba?\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba?\\z"},
+ {"foo:/foo/bar\\ba#\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba#\\z"},
+
+ {"mailto:/fo/o@b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:%2Ffo%2Fo@b%5C%a%3F%5Cr.b%23%5Caz"},
+ {"mailto:fo/o@b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:fo%2Fo@b%5C%a%3F%5Cr.b%23%5Caz"},
+ {"mailto:fo/o@b\\%a?\\r.b#\\az", URL_ESCAPE_PERCENT, 0, S_OK, "mailto:fo%2Fo@b%5C%25a%3F%5Cr.b%23%5Caz"},
+
+ {"ftp:fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:fo%2Fo@bar.baz%2Ffoo%2Fbar"},
+ {"ftp:/fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:/fo/o@bar.baz/foo/bar"},
+ {"ftp://fo/o@bar.baz/fo?o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo?o\\bar"},
+ {"ftp://fo/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo#o\\bar"},
+ {"ftp://localhost/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://localhost/o@bar.baz/fo#o\\bar"},
+ {"ftp:///fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:///fo/o@bar.baz/foo/bar"},
+ {"ftp:////fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:////fo/o@bar.baz/foo/bar"}
+};
+
+typedef struct _TEST_URL_COMBINE {
+ const char *url1;
+ const char *url2;
+ DWORD flags;
+ HRESULT expectret;
+ const char *expecturl;
+} TEST_URL_COMBINE;
+
+const TEST_URL_COMBINE TEST_COMBINE[] = {
+ {"http://www.winehq.org/tests", "tests1", 0, S_OK, "http://www.winehq.org/tests1"},
+ /*FIXME {"http://www.winehq.org/tests", "../tests2", 0, S_OK, "http://www.winehq.org/tests2"},*/
+ {"http://www.winehq.org/tests/", "../tests3", 0, S_OK, "http://www.winehq.org/tests3"},
+ {"http://www.winehq.org/tests/../tests", "tests4", 0, S_OK, "http://www.winehq.org/tests4"},
+ {"http://www.winehq.org/tests/../tests/", "tests5", 0, S_OK, "http://www.winehq.org/tests/tests5"},
+ {"http://www.winehq.org/tests/../tests/", "/tests6/..", 0, S_OK, "http://www.winehq.org/"},
+ {"http://www.winehq.org/tests/../tests/..", "tests7/..", 0, S_OK, "http://www.winehq.org/"},
+ {"http://www.winehq.org/tests/?query=x&return=y", "tests8", 0, S_OK, "http://www.winehq.org/tests/tests8"},
+ {"http://www.winehq.org/tests/#example", "tests9", 0, S_OK, "http://www.winehq.org/tests/tests9"},
+ {"http://www.winehq.org/tests/../tests/", "/tests10/..", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests10/.."},
+ {"http://www.winehq.org/tests/../", "tests11", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests/../tests11"},
+};
+
+struct {
+ const char *path;
+ const char *url;
+ DWORD ret;
+} TEST_URLFROMPATH [] = {
+ {"foo", "file:foo", S_OK},
+ {"foo\\bar", "file:foo/bar", S_OK},
+ {"\\foo\\bar", "file:///foo/bar", S_OK},
+ {"c:\\foo\\bar", "file:///c:/foo/bar", S_OK},
+ {"c:foo\\bar", "file:///c:foo/bar", S_OK},
+ {"c:\\foo/b a%r", "file:///c:/foo/b%20a%25r", S_OK},
+ {"c:\\foo\\foo bar", "file:///c:/foo/foo%20bar", S_OK},
+#if 0
+ /* The following test fails on native shlwapi as distributed with Win95/98.
+ * Wine matches the behaviour of later versions.
+ */
+ {"xx:c:\\foo\\bar", "xx:c:\\foo\\bar", S_FALSE}
+#endif
+};
+
+struct {
+ const char *url;
+ const char *path;
+ DWORD ret;
+} TEST_PATHFROMURL[] = {
+ {"file:///c:/foo/ba%5Cr", "c:\\foo\\ba\\r", S_OK},
+ {"file:///c:/foo/../ba%5Cr", "c:\\foo\\..\\ba\\r", S_OK},
+ {"file:///host/c:/foo/bar", "\\host\\c:\\foo\\bar", S_OK},
+ {"file://host/c:/foo/bar", "\\\\hostc:\\foo\\bar", S_OK},
+ {"file://host/c:/foo/bar", "\\\\hostc:\\foo\\bar", S_OK},
+ {"file:\\\\host\\c:\\foo\\bar", "\\\\hostc:\\foo\\bar", S_OK},
+ {"file:\\\\host\\ca\\foo\\bar", "\\\\host\\ca\\foo\\bar", S_OK},
+ {"file:\\\\host\\c|\\foo\\bar", "\\\\hostc|\\foo\\bar", S_OK},
+ {"file:\\%5Chost\\c:\\foo\\bar", "\\\\host\\c:\\foo\\bar", S_OK},
+ {"file:\\\\host\\cx:\\foo\\bar", "\\\\host\\cx:\\foo\\bar", S_OK},
+ {"file://c:/foo/bar", "c:\\foo\\bar", S_OK},
+ {"file://c:/d:/foo/bar", "c:\\d:\\foo\\bar", S_OK},
+ {"file://c|/d|/foo/bar", "c:\\d|\\foo\\bar", S_OK},
+ {"file://host/foo/bar", "\\\\host\\foo\\bar", S_OK},
+ {"file:/foo/bar", "\\foo\\bar", S_OK},
+ {"file:/foo/bar/", "\\foo\\bar\\", S_OK},
+ {"file:foo/bar", "foo\\bar", S_OK},
+ {"file:c:/foo/bar", "c:\\foo\\bar", S_OK},
+ {"file:c|/foo/bar", "c:\\foo\\bar", S_OK},
+ {"file:cx|/foo/bar", "cx|\\foo\\bar", S_OK},
+ {"file:////c:/foo/bar", "c:\\foo\\bar", S_OK},
+/* {"file:////c:/foo/foo%20bar", "c:\\foo\\foo%20bar", S_OK},*/
+
+ {"c:\\foo\\bar", NULL, E_INVALIDARG},
+ {"foo/bar", NULL, E_INVALIDARG},
+ {"http://foo/bar", NULL, E_INVALIDARG},
+
+};
+
+struct {
+ char url[30];
+ const char *expect;
+} TEST_URL_UNESCAPE[] = {
+ {"file://foo/bar", "file://foo/bar"},
+ {"file://fo%20o%5Ca/bar", "file://fo o\\a/bar"}
+};
+
+
+struct {
+ const char *path;
+ BOOL expect;
+} TEST_PATH_IS_URL[] = {
+ {"http://foo/bar", TRUE},
+ {"c:\\foo\\bar", FALSE},
+ {"foo://foo/bar", TRUE},
+ {"foo\\bar", FALSE},
+ {"foo.bar", FALSE},
+ {"bogusscheme:", TRUE},
+ {"http:partial", TRUE}
+};
+
+struct {
+ const char *url;
+ BOOL expectOpaque;
+ BOOL expectFile;
+} TEST_URLIS_ATTRIBS[] = {
+ { "ftp:", FALSE, FALSE },
+ { "http:", FALSE, FALSE },
+ { "gopher:", FALSE, FALSE },
+ { "mailto:", TRUE, FALSE },
+ { "news:", FALSE, FALSE },
+ { "nntp:", FALSE, FALSE },
+ { "telnet:", FALSE, FALSE },
+ { "wais:", FALSE, FALSE },
+ { "file:", FALSE, TRUE },
+ { "mk:", FALSE, FALSE },
+ { "https:", FALSE, FALSE },
+ { "shell:", TRUE, FALSE },
+ { "https:", FALSE, FALSE },
+ { "snews:", FALSE, FALSE },
+ { "local:", FALSE, FALSE },
+ { "javascript:", TRUE, FALSE },
+ { "vbscript:", TRUE, FALSE },
+ { "about:", TRUE, FALSE },
+ { "res:", FALSE, FALSE },
+ { "bogusscheme:", FALSE, FALSE },
+ { "file:\\\\e:\\b\\c", FALSE, TRUE },
+ { "file://e:/b/c", FALSE, TRUE },
+ { "http:partial", FALSE, FALSE },
+ { "mailto://www.winehq.org/test.html", TRUE, FALSE },
+ { "file:partial", FALSE, TRUE }
+};
+
+
+static LPWSTR GetWideString(const char* szString)
+{
+ LPWSTR wszString = HeapAlloc(GetProcessHeap(), 0, (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
+
+ MultiByteToWideChar(0, 0, szString, -1, wszString, INTERNET_MAX_URL_LENGTH);
+
+ return wszString;
+}
+
+static void FreeWideString(LPWSTR wszString)
+{
+ HeapFree(GetProcessHeap(), 0, wszString);
+}
+
+static void hash_url(const char* szUrl)
+{
+ LPCSTR szTestUrl = szUrl;
+ LPWSTR wszTestUrl = GetWideString(szTestUrl);
+
+ DWORD cbSize = sizeof(DWORD);
+ DWORD dwHash1, dwHash2;
+ ok(UrlHashA(szTestUrl, (LPBYTE)&dwHash1, cbSize) == S_OK, "UrlHashA didn't return S_OK\n");
+ ok(UrlHashW(wszTestUrl, (LPBYTE)&dwHash2, cbSize) == S_OK, "UrlHashW didn't return S_OK\n");
+
+ FreeWideString(wszTestUrl);
+
+ ok(dwHash1 == dwHash2, "Hashes didn't compare\n");
+}
+
+static void test_UrlHash(void)
+{
+ hash_url(TEST_URL_1);
+ hash_url(TEST_URL_2);
+ hash_url(TEST_URL_3);
+}
+
+static void test_url_part(const char* szUrl, DWORD dwPart, DWORD dwFlags, const char* szExpected)
+{
+ CHAR szPart[INTERNET_MAX_URL_LENGTH];
+ WCHAR wszPart[INTERNET_MAX_URL_LENGTH];
+ LPWSTR wszUrl = GetWideString(szUrl);
+ LPWSTR wszConvertedPart;
+
+ DWORD dwSize;
+
+ dwSize = INTERNET_MAX_URL_LENGTH;
+ ok( UrlGetPartA(szUrl, szPart, &dwSize, dwPart, dwFlags) == S_OK, "UrlGetPartA for \"%s\" part 0x%08lx didn't return S_OK but \"%s\"\n", szUrl, dwPart, szPart);
+ dwSize = INTERNET_MAX_URL_LENGTH;
+ ok( UrlGetPartW(wszUrl, wszPart, &dwSize, dwPart, dwFlags) == S_OK, "UrlGetPartW didn't return S_OK\n" );
+
+ wszConvertedPart = GetWideString(szPart);
+
+ ok(strcmpW(wszPart,wszConvertedPart)==0, "Strings didn't match between ascii and unicode UrlGetPart!\n");
+
+ FreeWideString(wszUrl);
+ FreeWideString(wszConvertedPart);
+
+ /* Note that v6.0 and later don't return '?' with the query */
+ ok(strcmp(szPart,szExpected)==0 ||
+ (*szExpected=='?' && !strcmp(szPart,szExpected+1)),
+ "Expected %s, but got %s\n", szExpected, szPart);
+}
+
+static void test_UrlGetPart(void)
+{
+ test_url_part(TEST_URL_3, URL_PART_HOSTNAME, 0, "localhost");
+ test_url_part(TEST_URL_3, URL_PART_PORT, 0, "21");
+ test_url_part(TEST_URL_3, URL_PART_USERNAME, 0, "foo");
+ test_url_part(TEST_URL_3, URL_PART_PASSWORD, 0, "bar");
+ test_url_part(TEST_URL_3, URL_PART_SCHEME, 0, "http");
+ test_url_part(TEST_URL_3, URL_PART_QUERY, 0, "?query=x&return=y");
+}
+
+static void test_url_escape(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
+{
+ CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+ DWORD dwEscaped;
+ WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+ WCHAR *urlW, *expected_urlW;
+ dwEscaped=INTERNET_MAX_URL_LENGTH;
+
+ ok(UrlEscapeA(szUrl, szReturnUrl, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeA didn't return 0x%08lx from \"%s\"\n", dwExpectReturn, szUrl);
+ ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected \"%s\", but got \"%s\" from \"%s\"\n", szExpectUrl, szReturnUrl, szUrl);
+
+ dwEscaped = INTERNET_MAX_URL_LENGTH;
+ urlW = GetWideString(szUrl);
+ expected_urlW = GetWideString(szExpectUrl);
+ ok(UrlEscapeW(urlW, ret_urlW, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeW didn't return 0x%08lx from \"%s\"\n", dwExpectReturn, szUrl);
+ WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
+ ok(strcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08lx\n", szExpectUrl, szReturnUrl, szUrl, dwFlags);
+ FreeWideString(urlW);
+ FreeWideString(expected_urlW);
+
+}
+
+static void test_url_canonicalize(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
+{
+ CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+ WCHAR wszReturnUrl[INTERNET_MAX_URL_LENGTH];
+ LPWSTR wszUrl = GetWideString(szUrl);
+ LPWSTR wszExpectUrl = GetWideString(szExpectUrl);
+ LPWSTR wszConvertedUrl;
+
+ DWORD dwSize;
+
+ dwSize = INTERNET_MAX_URL_LENGTH;
+ ok(UrlCanonicalizeA(szUrl, NULL, &dwSize, dwFlags) != dwExpectReturn, "Unexpected return for NULL buffer\n");
+ ok(UrlCanonicalizeA(szUrl, szReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeA didn't return 0x%08lx\n", dwExpectReturn);
+ ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08lx Expected %s, but got %s\n", dwFlags, szExpectUrl, szReturnUrl);
+
+ dwSize = INTERNET_MAX_URL_LENGTH;
+ ok(UrlCanonicalizeW(wszUrl, NULL, &dwSize, dwFlags) != dwExpectReturn, "Unexpected return for NULL buffer\n");
+ ok(UrlCanonicalizeW(wszUrl, wszReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeW didn't return 0x%08lx\n", dwExpectReturn);
+ wszConvertedUrl = GetWideString(szReturnUrl);
+ ok(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize!\n");
+ FreeWideString(wszConvertedUrl);
+
+
+ FreeWideString(wszUrl);
+ FreeWideString(wszExpectUrl);
+}
+
+
+static void test_UrlEscape(void)
+{
+ unsigned int i;
+ for(i=0; i<sizeof(TEST_ESCAPE)/sizeof(TEST_ESCAPE[0]); i++) {
+ test_url_escape(TEST_ESCAPE[i].url, TEST_ESCAPE[i].flags,
+ TEST_ESCAPE[i].expectret, TEST_ESCAPE[i].expecturl);
+ }
+}
+
+static void test_UrlCanonicalize(void)
+{
+ unsigned int i;
+ for(i=0; i<sizeof(TEST_CANONICALIZE)/sizeof(TEST_CANONICALIZE[0]); i++) {
+ test_url_canonicalize(TEST_CANONICALIZE[i].url, TEST_CANONICALIZE[i].flags,
+ TEST_CANONICALIZE[i].expectret, TEST_CANONICALIZE[i].expecturl);
+ }
+}
+
+static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
+{
+ HRESULT hr;
+ CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+ WCHAR wszReturnUrl[INTERNET_MAX_URL_LENGTH];
+ LPWSTR wszUrl1 = GetWideString(szUrl1);
+ LPWSTR wszUrl2 = GetWideString(szUrl2);
+ LPWSTR wszExpectUrl = GetWideString(szExpectUrl);
+ LPWSTR wszConvertedUrl;
+
+ DWORD dwSize;
+ DWORD dwExpectLen = lstrlen(szExpectUrl);
+
+ hr = UrlCombineA(szUrl1, szUrl2, NULL, NULL, dwFlags);
+ ok(hr == E_INVALIDARG, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_INVALIDARG);
+
+ dwSize = 0;
+ hr = UrlCombineA(szUrl1, szUrl2, NULL, &dwSize, dwFlags);
+ ok(hr == E_POINTER, "Checking length of string, return was 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
+ ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+
+ dwSize--;
+ hr = UrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
+ ok(hr == E_POINTER, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
+ ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+
+ hr = UrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
+ ok(hr == dwExpectReturn, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, dwExpectReturn);
+ ok(dwSize == dwExpectLen, "Got length %ld, expected %ld\n", dwSize, dwExpectLen);
+ if(SUCCEEDED(hr)) {
+ ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected %s, but got %s\n", szExpectUrl, szReturnUrl);
+ }
+
+ dwSize = 0;
+ hr = UrlCombineW(wszUrl1, wszUrl2, NULL, &dwSize, dwFlags);
+ ok(hr == E_POINTER, "Checking length of string, return was 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
+ ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+
+ dwSize--;
+ hr = UrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
+ ok(hr == E_POINTER, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
+ ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
+
+ hr = UrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
+ ok(hr == dwExpectReturn, "UrlCombineW returned 0x%08lx, expected 0x%08lx\n", hr, dwExpectReturn);
+ ok(dwSize == dwExpectLen, "Got length %ld, expected %ld\n", dwSize, dwExpectLen);
+ if(SUCCEEDED(hr)) {
+ wszConvertedUrl = GetWideString(szReturnUrl);
+ ok(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
+ FreeWideString(wszConvertedUrl);
+ }
+
+ FreeWideString(wszUrl1);
+ FreeWideString(wszUrl2);
+ FreeWideString(wszExpectUrl);
+}
+
+static void test_UrlCombine(void)
+{
+ unsigned int i;
+ for(i=0; i<sizeof(TEST_COMBINE)/sizeof(TEST_COMBINE[0]); i++) {
+ test_url_combine(TEST_COMBINE[i].url1, TEST_COMBINE[i].url2, TEST_COMBINE[i].flags,
+ TEST_COMBINE[i].expectret, TEST_COMBINE[i].expecturl);
+ }
+}
+
+static void test_UrlCreateFromPath(void)
+{
+ size_t i;
+ char ret_url[INTERNET_MAX_URL_LENGTH];
+ DWORD len, ret;
+ WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+ WCHAR *pathW, *urlW;
+
+ for(i = 0; i < sizeof(TEST_URLFROMPATH) / sizeof(TEST_URLFROMPATH[0]); i++) {
+ len = INTERNET_MAX_URL_LENGTH;
+ ret = UrlCreateFromPathA(TEST_URLFROMPATH[i].path, ret_url, &len, 0);
+ ok(ret == TEST_URLFROMPATH[i].ret, "ret %08lx from path %s\n", ret, TEST_URLFROMPATH[i].path);
+ ok(!lstrcmpi(ret_url, TEST_URLFROMPATH[i].url), "url %s from path %s\n", ret_url, TEST_URLFROMPATH[i].path);
+ ok(len == strlen(ret_url), "ret len %ld from path %s\n", len, TEST_URLFROMPATH[i].path);
+
+ len = INTERNET_MAX_URL_LENGTH;
+ pathW = GetWideString(TEST_URLFROMPATH[i].path);
+ urlW = GetWideString(TEST_URLFROMPATH[i].url);
+ ret = UrlCreateFromPathW(pathW, ret_urlW, &len, 0);
+ WideCharToMultiByte(CP_ACP, 0, ret_urlW, -1, ret_url, sizeof(ret_url),0,0);
+ ok(ret == TEST_URLFROMPATH[i].ret, "ret %08lx from path L\"%s\", expected %08lx\n",
+ ret, TEST_URLFROMPATH[i].path, TEST_URLFROMPATH[i].ret);
+ ok(!lstrcmpiW(ret_urlW, urlW), "got %s expected %s from path L\"%s\"\n", ret_url, TEST_URLFROMPATH[i].url, TEST_URLFROMPATH[i].path);
+ ok(len == strlenW(ret_urlW), "ret len %ld from path L\"%s\"\n", len, TEST_URLFROMPATH[i].path);
+ FreeWideString(urlW);
+ FreeWideString(pathW);
+ }
+}
+
+static void test_UrlIs(void)
+{
+ BOOL ret;
+ size_t i;
+ WCHAR wurl[80];
+
+ for(i = 0; i < sizeof(TEST_PATH_IS_URL) / sizeof(TEST_PATH_IS_URL[0]); i++) {
+ MultiByteToWideChar(CP_ACP, 0, TEST_PATH_IS_URL[i].path, -1, wurl, 80);
+
+ ret = UrlIsA( TEST_PATH_IS_URL[i].path, URLIS_URL );
+ ok( ret == TEST_PATH_IS_URL[i].expect,
+ "returned %d from path %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
+ TEST_PATH_IS_URL[i].expect );
+
+ ret = UrlIsW( wurl, URLIS_URL );
+ ok( ret == TEST_PATH_IS_URL[i].expect,
+ "returned %d from path (UrlIsW) %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
+ TEST_PATH_IS_URL[i].expect );
+ }
+ for(i = 0; i < sizeof(TEST_URLIS_ATTRIBS) / sizeof(TEST_URLIS_ATTRIBS[0]); i++) {
+ MultiByteToWideChar(CP_ACP, 0, TEST_URLIS_ATTRIBS[i].url, -1, wurl, 80);
+
+ ret = UrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_OPAQUE);
+ ok( ret == TEST_URLIS_ATTRIBS[i].expectOpaque,
+ "returned %d for URLIS_OPAQUE, url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+ TEST_URLIS_ATTRIBS[i].expectOpaque );
+ ret = UrlIsA( TEST_URLIS_ATTRIBS[i].url, URLIS_FILEURL);
+ ok( ret == TEST_URLIS_ATTRIBS[i].expectFile,
+ "returned %d for URLIS_FILEURL, url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+ TEST_URLIS_ATTRIBS[i].expectFile );
+
+ ret = UrlIsW( wurl, URLIS_OPAQUE);
+ ok( ret == TEST_URLIS_ATTRIBS[i].expectOpaque,
+ "returned %d for URLIS_OPAQUE (UrlIsW), url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+ TEST_URLIS_ATTRIBS[i].expectOpaque );
+ ret = UrlIsW( wurl, URLIS_FILEURL);
+ ok( ret == TEST_URLIS_ATTRIBS[i].expectFile,
+ "returned %d for URLIS_FILEURL (UrlIsW), url \"%s\", expected %d\n", ret, TEST_URLIS_ATTRIBS[i].url,
+ TEST_URLIS_ATTRIBS[i].expectFile );
+ }
+}
+
+static void test_UrlUnescape(void)
+{
+ CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
+ WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+ WCHAR *urlW, *expected_urlW;
+ DWORD dwEscaped;
+ size_t i;
+
+ for(i=0; i<sizeof(TEST_URL_UNESCAPE)/sizeof(TEST_URL_UNESCAPE[0]); i++) {
+ dwEscaped=INTERNET_MAX_URL_LENGTH;
+ ok(UrlUnescapeA(TEST_URL_UNESCAPE[i].url, szReturnUrl, &dwEscaped, 0) == S_OK, "UrlEscapeA didn't return 0x%08lx from \"%s\"\n", S_OK, TEST_URL_UNESCAPE[i].url);
+ ok(strcmp(szReturnUrl,TEST_URL_UNESCAPE[i].expect)==0, "Expected \"%s\", but got \"%s\" from \"%s\"\n", TEST_URL_UNESCAPE[i].expect, szReturnUrl, TEST_URL_UNESCAPE[i].url);
+
+ dwEscaped = INTERNET_MAX_URL_LENGTH;
+ urlW = GetWideString(TEST_URL_UNESCAPE[i].url);
+ expected_urlW = GetWideString(TEST_URL_UNESCAPE[i].expect);
+ ok(UrlUnescapeW(urlW, ret_urlW, &dwEscaped, 0) == S_OK, "UrlEscapeW didn't return 0x%08lx from \"%s\"\n", S_OK, TEST_URL_UNESCAPE[i].url);
+ WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
+ ok(strcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08lx\n", TEST_URL_UNESCAPE[i].expect, szReturnUrl, TEST_URL_UNESCAPE[i].url, 0L);
+ FreeWideString(urlW);
+ FreeWideString(expected_urlW);
+ }
+
+}
+
+static void test_PathSearchAndQualify(void)
+{
+ WCHAR path1[] = {'c',':','\\','f','o','o',0};
+ WCHAR expect1[] = {'c',':','\\','f','o','o',0};
+ WCHAR path2[] = {'c',':','f','o','o',0};
+ WCHAR c_drive[] = {'c',':',0};
+ WCHAR foo[] = {'f','o','o',0};
+ WCHAR path3[] = {'\\','f','o','o',0};
+ WCHAR winini[] = {'w','i','n','.','i','n','i',0};
+ WCHAR out[MAX_PATH];
+ WCHAR cur_dir[MAX_PATH];
+ WCHAR dot[] = {'.',0};
+
+ /* c:\foo */
+ ok(PathSearchAndQualifyW(path1, out, MAX_PATH) != 0,
+ "PathSearchAndQualify rets 0\n");
+ ok(!lstrcmpiW(out, expect1), "strings don't match\n");
+
+ /* c:foo */
+ ok(PathSearchAndQualifyW(path2, out, MAX_PATH) != 0,
+ "PathSearchAndQualify rets 0\n");
+ GetFullPathNameW(c_drive, MAX_PATH, cur_dir, NULL);
+ PathAddBackslashW(cur_dir);
+ strcatW(cur_dir, foo);
+ ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
+
+ /* foo */
+ ok(PathSearchAndQualifyW(foo, out, MAX_PATH) != 0,
+ "PathSearchAndQualify rets 0\n");
+ GetFullPathNameW(dot, MAX_PATH, cur_dir, NULL);
+ PathAddBackslashW(cur_dir);
+ strcatW(cur_dir, foo);
+ ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
+
+ /* \foo */
+ ok(PathSearchAndQualifyW(path3, out, MAX_PATH) != 0,
+ "PathSearchAndQualify rets 0\n");
+ GetFullPathNameW(dot, MAX_PATH, cur_dir, NULL);
+ strcpyW(cur_dir + 2, path3);
+ ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
+
+ /* win.ini */
+ ok(PathSearchAndQualifyW(winini, out, MAX_PATH) != 0,
+ "PathSearchAndQualify rets 0\n");
+ if(!SearchPathW(NULL, winini, NULL, MAX_PATH, cur_dir, NULL))
+ GetFullPathNameW(winini, MAX_PATH, cur_dir, NULL);
+ ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
+
+}
+
+static void test_PathCreateFromUrl(void)
+{
+ size_t i;
+ char ret_path[INTERNET_MAX_URL_LENGTH];
+ DWORD len, ret;
+ WCHAR ret_pathW[INTERNET_MAX_URL_LENGTH];
+ WCHAR *pathW, *urlW;
+
+ for(i = 0; i < sizeof(TEST_PATHFROMURL) / sizeof(TEST_PATHFROMURL[0]); i++) {
+ len = INTERNET_MAX_URL_LENGTH;
+ ret = PathCreateFromUrlA(TEST_PATHFROMURL[i].url, ret_path, &len, 0);
+ ok(ret == TEST_PATHFROMURL[i].ret, "ret %08lx from url %s\n", ret, TEST_PATHFROMURL[i].url);
+ if(TEST_PATHFROMURL[i].path) {
+ ok(!lstrcmpi(ret_path, TEST_PATHFROMURL[i].path), "got %s expected %s from url %s\n", ret_path, TEST_PATHFROMURL[i].path, TEST_PATHFROMURL[i].url);
+ ok(len == strlen(ret_path), "ret len %ld from url %s\n", len, TEST_PATHFROMURL[i].url);
+ }
+ len = INTERNET_MAX_URL_LENGTH;
+ pathW = GetWideString(TEST_PATHFROMURL[i].path);
+ urlW = GetWideString(TEST_PATHFROMURL[i].url);
+ ret = PathCreateFromUrlW(urlW, ret_pathW, &len, 0);
+ WideCharToMultiByte(CP_ACP, 0, ret_pathW, -1, ret_path, sizeof(ret_path),0,0);
+ ok(ret == TEST_PATHFROMURL[i].ret, "ret %08lx from url L\"%s\"\n", ret, TEST_PATHFROMURL[i].url);
+ if(TEST_PATHFROMURL[i].path) {
+ ok(!lstrcmpiW(ret_pathW, pathW), "got %s expected %s from url L\"%s\"\n", ret_path, TEST_PATHFROMURL[i].path, TEST_PATHFROMURL[i].url);
+ ok(len == strlenW(ret_pathW), "ret len %ld from url L\"%s\"\n", len, TEST_PATHFROMURL[i].url);
+ }
+ FreeWideString(urlW);
+ FreeWideString(pathW);
+ }
+}
+
+
+static void test_PathIsUrl(void)
+{
+ size_t i;
+ BOOL ret;
+
+ for(i = 0; i < sizeof(TEST_PATH_IS_URL)/sizeof(TEST_PATH_IS_URL[0]); i++) {
+ ret = PathIsURLA(TEST_PATH_IS_URL[i].path);
+ ok(ret == TEST_PATH_IS_URL[i].expect,
+ "returned %d from path %s, expected %d\n", ret, TEST_PATH_IS_URL[i].path,
+ TEST_PATH_IS_URL[i].expect);
+ }
+}
+
+static const DWORD SHELL_charclass[] =
+{
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000080, 0x00000100, 0x00000200, 0x00000100,
+ 0x00000100, 0x00000100, 0x00000100, 0x00000100,
+ 0x00000100, 0x00000100, 0x00000002, 0x00000100,
+ 0x00000040, 0x00000100, 0x00000004, 0x00000000,
+ 0x00000100, 0x00000100, 0x00000100, 0x00000100,
+ 0x00000100, 0x00000100, 0x00000100, 0x00000100,
+ 0x00000100, 0x00000100, 0x00000010, 0x00000020,
+ 0x00000000, 0x00000100, 0x00000000, 0x00000001,
+ 0x00000100, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0x00000100,
+ 0x00000008, 0x00000100, 0x00000100, 0x00000100,
+ 0x00000100, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
+ 0xffffffff, 0xffffffff, 0xffffffff, 0x00000100,
+ 0x00000000, 0x00000100, 0x00000100
+};
+
+static void test_PathIsValidCharA(void)
+{
+ BOOL ret;
+ unsigned int c;
+
+ ret = pPathIsValidCharA( 0x7f, 0 );
+ ok ( !ret, "PathIsValidCharA succeeded: 0x%08lx\n", (DWORD)ret );
+
+ ret = pPathIsValidCharA( 0x7f, 1 );
+ ok ( !ret, "PathIsValidCharA succeeded: 0x%08lx\n", (DWORD)ret );
+
+ for (c = 0; c < 0x7f; c++)
+ {
+ ret = pPathIsValidCharA( c, ~0U );
+ ok ( ret == SHELL_charclass[c] || (ret == 1 && SHELL_charclass[c] == 0xffffffff),
+ "PathIsValidCharA failed: 0x%02x got 0x%08lx expected 0x%08lx\n",
+ c, (DWORD)ret, SHELL_charclass[c] );
+ }
+
+ for (c = 0x7f; c <= 0xff; c++)
+ {
+ ret = pPathIsValidCharA( c, ~0U );
+ ok ( ret == 0x00000100,
+ "PathIsValidCharA failed: 0x%02x got 0x%08lx expected 0x00000100\n",
+ c, (DWORD)ret );
+ }
+}
+
+static void test_PathIsValidCharW(void)
+{
+ BOOL ret;
+ unsigned int c;
+
+ ret = pPathIsValidCharW( 0x7f, 0 );
+ ok ( !ret, "PathIsValidCharW succeeded: 0x%08lx\n", (DWORD)ret );
+
+ ret = pPathIsValidCharW( 0x7f, 1 );
+ ok ( !ret, "PathIsValidCharW succeeded: 0x%08lx\n", (DWORD)ret );
+
+ for (c = 0; c < 0x7f; c++)
+ {
+ ret = pPathIsValidCharW( c, ~0U );
+ ok ( ret == SHELL_charclass[c] || (ret == 1 && SHELL_charclass[c] == 0xffffffff),
+ "PathIsValidCharW failed: 0x%02x got 0x%08lx expected 0x%08lx\n",
+ c, (DWORD)ret, SHELL_charclass[c] );
+ }
+
+ for (c = 0x007f; c <= 0xffff; c++)
+ {
+ ret = pPathIsValidCharW( c, ~0U );
+ ok ( ret == 0x00000100,
+ "PathIsValidCharW failed: 0x%02x got 0x%08lx expected 0x00000100\n",
+ c, (DWORD)ret );
+ }
+}
+
+static void test_PathMakePretty(void)
+{
+ char buff[MAX_PATH];
+
+ ok (PathMakePrettyA(NULL) == FALSE, "PathMakePretty: NULL path succeeded\n");
+ buff[0] = '\0';
+ ok (PathMakePrettyA(buff) == TRUE, "PathMakePretty: Empty path failed\n");
+
+ strcpy(buff, "C:\\A LONG FILE NAME WITH \\SPACES.TXT");
+ ok (PathMakePrettyA(buff) == TRUE, "PathMakePretty: Long UC name failed\n");
+ ok (strcmp(buff, "C:\\a long file name with \\spaces.txt") == 0,
+ "PathMakePretty: Long UC name not changed\n");
+
+ strcpy(buff, "C:\\A LONG FILE NAME WITH \\MixedCase.TXT");
+ ok (PathMakePrettyA(buff) == FALSE, "PathMakePretty: Long MC name succeeded\n");
+ ok (strcmp(buff, "C:\\A LONG FILE NAME WITH \\MixedCase.TXT") == 0,
+ "PathMakePretty: Failed but modified path\n");
+
+ strcpy(buff, "TEST");
+ ok (PathMakePrettyA(buff) == TRUE, "PathMakePretty: Short name failed\n");
+ ok (strcmp(buff, "Test") == 0, "PathMakePretty: 1st char lowercased %s\n", buff);
+}
+
+START_TEST(path)
+{
+ hShlwapi = LoadLibraryA("shlwapi.dll");
+ if (!hShlwapi) return;
+
+ test_UrlHash();
+ test_UrlGetPart();
+ test_UrlCanonicalize();
+ test_UrlEscape();
+ test_UrlCombine();
+ test_UrlCreateFromPath();
+ test_UrlIs();
+ test_UrlUnescape();
+
+ test_PathSearchAndQualify();
+ test_PathCreateFromUrl();
+ test_PathIsUrl();
+
+ test_PathMakePretty();
+
+ /* For whatever reason, PathIsValidCharA and PathAppendA share the same
+ * ordinal number in some native versions. Check this to prevent a crash.
+ */
+ pPathIsValidCharA = (void*)GetProcAddress(hShlwapi, (LPSTR)455);
+ if (pPathIsValidCharA && pPathIsValidCharA != (void*)GetProcAddress(hShlwapi, "PathAppendA"))
+ {
+ test_PathIsValidCharA();
+
+ pPathIsValidCharW = (void*)GetProcAddress(hShlwapi, (LPSTR)456);
+ if (pPathIsValidCharW) test_PathIsValidCharW();
+ }
+}
--- /dev/null
+<module name="shlwapi_winetest" type="win32cui" installbase="bin" installname="shlwapi_winetest.exe" allowwarnings="true">
+ <include base="shlwapi_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>ntdll</library>
+ <library>shlwapi</library>
+ <library>ole32</library>
+ <library>oleaut32</library>
+ <library>kernel32</library>
+ <library>advapi32</library>
+ <file>clist.c</file>
+ <file>ordinal.c</file>
+ <file>shreg.c</file>
+ <file>string.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/* Unit test suite for SHReg* functions
+ *
+ * Copyright 2002 Juergen Schmied
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "shlwapi.h"
+
+/* Keys used for testing */
+#define REG_TEST_KEY "Software\\Wine\\Test"
+#define REG_CURRENT_VERSION "Software\\Microsoft\\Windows\\CurrentVersion"
+
+static HMODULE hshlwapi;
+typedef DWORD (WINAPI *SHCopyKeyA_func)(HKEY,LPCSTR,HKEY,DWORD);
+static SHCopyKeyA_func pSHCopyKeyA;
+typedef DWORD (WINAPI *SHRegGetPathA_func)(HKEY,LPCSTR,LPCSTR,LPSTR,DWORD);
+static SHRegGetPathA_func pSHRegGetPathA;
+
+static const char * sTestpath1 = "%LONGSYSTEMVAR%\\subdir1";
+static const char * sTestpath2 = "%FOO%\\subdir1";
+
+static const char * sEnvvar1 = "bar";
+static const char * sEnvvar2 = "ImARatherLongButIndeedNeededString";
+
+static char sExpTestpath1[MAX_PATH];
+static char sExpTestpath2[MAX_PATH];
+static unsigned sExpLen1;
+static unsigned sExpLen2;
+
+static const char * sEmptyBuffer ="0123456789";
+
+/* delete key and all its subkeys */
+static DWORD delete_key( HKEY hkey, LPSTR parent, LPSTR keyname )
+{
+ HKEY parentKey;
+ DWORD ret;
+
+ RegCloseKey(hkey);
+
+ /* open the parent of the key to close */
+ ret = RegOpenKeyExA( HKEY_CURRENT_USER, parent, 0, KEY_ALL_ACCESS, &parentKey);
+ if (ret != ERROR_SUCCESS)
+ return ret;
+
+ ret = SHDeleteKeyA( parentKey, keyname );
+ RegCloseKey(parentKey);
+
+ return ret;
+}
+
+static HKEY create_test_entries(void)
+{
+ HKEY hKey;
+ DWORD ret;
+
+ SetEnvironmentVariableA("LONGSYSTEMVAR", sEnvvar1);
+ SetEnvironmentVariableA("FOO", sEnvvar2);
+
+ ret = RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEY, &hKey);
+ ok( ERROR_SUCCESS == ret, "RegCreateKeyA failed, ret=%lu\n", ret);
+
+ if (hKey)
+ {
+ ok(!RegSetValueExA(hKey,"Test1",0,REG_EXPAND_SZ, (LPBYTE) sTestpath1, strlen(sTestpath1)+1), "RegSetValueExA failed\n");
+ ok(!RegSetValueExA(hKey,"Test2",0,REG_SZ, (LPBYTE) sTestpath1, strlen(sTestpath1)+1), "RegSetValueExA failed\n");
+ ok(!RegSetValueExA(hKey,"Test3",0,REG_EXPAND_SZ, (LPBYTE) sTestpath2, strlen(sTestpath2)+1), "RegSetValueExA failed\n");
+ }
+
+ sExpLen1 = ExpandEnvironmentStringsA(sTestpath1, sExpTestpath1, sizeof(sExpTestpath1));
+ sExpLen2 = ExpandEnvironmentStringsA(sTestpath2, sExpTestpath2, sizeof(sExpTestpath2));
+
+ ok(sExpLen1 > 0, "Couldn't expand %s\n", sTestpath1);
+ trace("sExplen1 = (%d)\n", sExpLen1);
+ ok(sExpLen2 > 0, "Couldn't expand %s\n", sTestpath2);
+ trace("sExplen2 = (%d)\n", sExpLen2);
+
+ return hKey;
+}
+
+static void test_SHGetValue(void)
+{
+ DWORD dwSize;
+ DWORD dwType;
+ DWORD dwRet;
+ char buf[MAX_PATH];
+
+ strcpy(buf, sEmptyBuffer);
+ dwSize = MAX_PATH;
+ dwType = -1;
+ dwRet = SHGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", &dwType, buf, &dwSize);
+ ok( ERROR_SUCCESS == dwRet, "SHGetValueA failed, ret=%lu\n", dwRet);
+ ok( 0 == strcmp(sExpTestpath1, buf), "Comparing of (%s) with (%s) failed\n", buf, sExpTestpath1);
+ ok( REG_SZ == dwType, "Expected REG_SZ, got (%lu)\n", dwType);
+
+ strcpy(buf, sEmptyBuffer);
+ dwSize = MAX_PATH;
+ dwType = -1;
+ dwRet = SHGetValueA(HKEY_CURRENT_USER, REG_TEST_KEY, "Test2", &dwType, buf, &dwSize);
+ ok( ERROR_SUCCESS == dwRet, "SHGetValueA failed, ret=%lu\n", dwRet);
+ ok( 0 == strcmp(sTestpath1, buf) , "Comparing of (%s) with (%s) failed\n", buf, sTestpath1);
+ ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+}
+
+static void test_SHGetRegPath(void)
+{
+ char buf[MAX_PATH];
+ DWORD dwRet;
+
+ if (!pSHRegGetPathA)
+ return;
+
+ strcpy(buf, sEmptyBuffer);
+ dwRet = (*pSHRegGetPathA)(HKEY_CURRENT_USER, REG_TEST_KEY, "Test1", buf, 0);
+ ok( ERROR_SUCCESS == dwRet, "SHRegGetPathA failed, ret=%lu\n", dwRet);
+ ok( 0 == strcmp(sExpTestpath1, buf) , "Comparing (%s) with (%s) failed\n", buf, sExpTestpath1);
+}
+
+static void test_SHQUeryValueEx(void)
+{
+ HKEY hKey;
+ DWORD dwSize;
+ DWORD dwType;
+ char buf[MAX_PATH];
+ DWORD dwRet;
+ const char * sTestedFunction = "";
+ DWORD nUsedBuffer1,nUsedBuffer2;
+
+ sTestedFunction = "RegOpenKeyExA";
+ dwRet = RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_KEY, 0, KEY_QUERY_VALUE, &hKey);
+ ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+
+ /****** SHQueryValueExA ******/
+
+ sTestedFunction = "SHQueryValueExA";
+ nUsedBuffer1 = max(strlen(sExpTestpath1)+1, strlen(sTestpath1)+1);
+ nUsedBuffer2 = max(strlen(sExpTestpath2)+1, strlen(sTestpath2)+1);
+ /*
+ * Case 1.1 All arguments are NULL
+ */
+ dwRet = SHQueryValueExA( hKey, "Test1", NULL, NULL, NULL, NULL);
+ ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+
+ /*
+ * Case 1.2 dwType is set
+ */
+ dwType = -1;
+ dwRet = SHQueryValueExA( hKey, "Test1", NULL, &dwType, NULL, NULL);
+ ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+ ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+
+ /*
+ * dwSize is set
+ * dwExpanded < dwUnExpanded
+ */
+ dwSize = 6;
+ dwRet = SHQueryValueExA( hKey, "Test1", NULL, NULL, NULL, &dwSize);
+ ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+ ok( dwSize == nUsedBuffer1, "Buffer sizes (%lu) and (%lu) are not equal\n", dwSize, nUsedBuffer1);
+
+ /*
+ * dwExpanded > dwUnExpanded
+ */
+ dwSize = 6;
+ dwRet = SHQueryValueExA( hKey, "Test3", NULL, NULL, NULL, &dwSize);
+ ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+ ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
+
+ /*
+ * Case 1 string shrinks during expanding
+ */
+ strcpy(buf, sEmptyBuffer);
+ dwSize = 6;
+ dwType = -1;
+ dwRet = SHQueryValueExA( hKey, "Test1", NULL, &dwType, buf, &dwSize);
+ ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+ ok( 0 == strcmp(sEmptyBuffer, buf) , "Comparing (%s) with (%s) failed\n", buf, sEmptyBuffer);
+ ok( dwSize == nUsedBuffer1, "Buffer sizes (%lu) and (%lu) are not equal\n", dwSize, nUsedBuffer1);
+ ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+
+ /*
+ * string grows during expanding
+ * dwSize is smaller then the size of the unexpanded string
+ */
+ strcpy(buf, sEmptyBuffer);
+ dwSize = 6;
+ dwType = -1;
+ dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, buf, &dwSize);
+ ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+ ok( 0 == strcmp(sEmptyBuffer, buf) , "Comparing (%s) with (%s) failed\n", buf, sEmptyBuffer);
+ ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
+ ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+
+ /*
+ * string grows during expanding
+ * dwSize is larger then the size of the unexpanded string but smaller than the part before the backslash
+ * if the unexpanded string fits into the buffer it can get cut when expanded
+ */
+ strcpy(buf, sEmptyBuffer);
+ dwSize = strlen(sEnvvar2) - 2;
+ dwType = -1;
+ dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, buf, &dwSize);
+ ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+
+ todo_wine
+ {
+ ok( (0 == strcmp("", buf)) | (0 == strcmp(sTestpath2, buf)),
+ "Expected empty or unexpanded string (win98), got (%s)\n", buf);
+ }
+
+ ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
+ ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+
+ /*
+ * string grows during expanding
+ * dwSize is larger then the size of the part before the backslash but smaller then the expanded string
+ * if the unexpanded string fits into the buffer it can get cut when expanded
+ */
+ strcpy(buf, sEmptyBuffer);
+ dwSize = sExpLen2 - 4;
+ dwType = -1;
+ dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, buf, &dwSize);
+ ok( ERROR_MORE_DATA == dwRet, "Expected ERROR_MORE_DATA, got (%lu)\n", dwRet);
+
+ todo_wine
+ {
+ ok( (0 == strcmp("", buf)) | (0 == strcmp(sEnvvar2, buf)),
+ "Expected empty or first part of the string \"%s\", got \"%s\"\n", sEnvvar2, buf);
+ }
+
+ ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
+ ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+
+ /*
+ * The buffer is NULL but the size is set
+ */
+ strcpy(buf, sEmptyBuffer);
+ dwSize = 6;
+ dwType = -1;
+ dwRet = SHQueryValueExA( hKey, "Test3", NULL, &dwType, NULL, &dwSize);
+ ok( ERROR_SUCCESS == dwRet, "%s failed, ret=%lu\n", sTestedFunction, dwRet);
+ ok( dwSize >= nUsedBuffer2, "Buffer size (%lu) should be >= (%lu)\n", dwSize, nUsedBuffer2);
+ ok( REG_SZ == dwType , "Expected REG_SZ, got (%lu)\n", dwType);
+
+ RegCloseKey(hKey);
+}
+
+static void test_SHCopyKey(void)
+{
+ HKEY hKeySrc, hKeyDst;
+ DWORD dwRet;
+
+ /* Delete existing destination sub keys */
+ hKeyDst = NULL;
+ if (!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination", &hKeyDst) && hKeyDst)
+ {
+ SHDeleteKeyA(hKeyDst, NULL);
+ RegCloseKey(hKeyDst);
+ }
+
+ hKeyDst = NULL;
+ dwRet = RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination", &hKeyDst);
+ if (dwRet || !hKeyDst)
+ {
+ ok( 0, "Destination couldn't be created, RegCreateKeyA returned (%lu)\n", dwRet);
+ return;
+ }
+
+ hKeySrc = NULL;
+ dwRet = RegOpenKeyA(HKEY_LOCAL_MACHINE, REG_CURRENT_VERSION, &hKeySrc);
+ if (dwRet || !hKeySrc)
+ {
+ ok( 0, "Source couldn't be opened, RegOpenKeyA returned (%lu)\n", dwRet);
+ return;
+ }
+
+
+ if (pSHCopyKeyA)
+ {
+ dwRet = (*pSHCopyKeyA)(hKeySrc, NULL, hKeyDst, 0);
+ ok ( ERROR_SUCCESS == dwRet, "Copy failed, ret=(%lu)\n", dwRet);
+ }
+
+ RegCloseKey(hKeySrc);
+ RegCloseKey(hKeyDst);
+
+ /* Check we copied the sub keys, i.e. something that's on every windows system (including Wine) */
+ hKeyDst = NULL;
+ dwRet = RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\CopyDestination\\Setup", &hKeyDst);
+ if (dwRet || !hKeyDst)
+ {
+ ok ( 0, "Copy couldn't be opened, RegOpenKeyA returned (%lu)\n", dwRet);
+ return;
+ }
+
+ /* And the we copied the values too */
+ ok(!SHQueryValueExA(hKeyDst, "BootDir", NULL, NULL, NULL, NULL), "SHQueryValueExA failed\n");
+
+ RegCloseKey(hKeyDst);
+}
+
+static void test_SHDeleteKey(void)
+{
+ HKEY hKeyTest, hKeyS;
+ DWORD dwRet;
+ int sysfail = 1;
+
+ if (!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY, &hKeyTest))
+ {
+ if (!RegCreateKey(hKeyTest, "ODBC", &hKeyS))
+ {
+ HKEY hKeyO;
+
+ if (!RegCreateKey(hKeyS, "ODBC.INI", &hKeyO))
+ {
+ RegCloseKey (hKeyO);
+
+ if (!RegCreateKey(hKeyS, "ODBCINST.INI", &hKeyO))
+ {
+ RegCloseKey (hKeyO);
+ sysfail = 0;
+ }
+ }
+ RegCloseKey (hKeyS);
+ }
+ RegCloseKey (hKeyTest);
+ }
+
+ if (!sysfail)
+ {
+
+ dwRet = SHDeleteKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\ODBC");
+ ok ( ERROR_SUCCESS == dwRet, "SHDeleteKey failed, ret=(%lu)\n", dwRet);
+
+ dwRet = RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_KEY "\\ODBC", &hKeyS);
+ ok ( ERROR_FILE_NOT_FOUND == dwRet, "SHDeleteKey did not delete\n");
+
+ if (dwRet == ERROR_SUCCESS)
+ RegCloseKey (hKeyS);
+ }
+ else
+ ok( 0, "Could not set up SHDeleteKey test\n");
+}
+
+START_TEST(shreg)
+{
+ HKEY hkey = create_test_entries();
+
+ if (!hkey) return;
+
+ hshlwapi = GetModuleHandleA("shlwapi.dll");
+ if (hshlwapi)
+ {
+ pSHCopyKeyA=(SHCopyKeyA_func)GetProcAddress(hshlwapi,"SHCopyKeyA");
+ pSHRegGetPathA=(SHRegGetPathA_func)GetProcAddress(hshlwapi,"SHRegGetPathA");
+ }
+ test_SHGetValue();
+ test_SHQUeryValueEx();
+ test_SHGetRegPath();
+ test_SHCopyKey();
+ test_SHDeleteKey();
+ delete_key( hkey, "Software\\Wine", "Test" );
+}
--- /dev/null
+/* Unit test suite for SHLWAPI string functions
+ *
+ * Copyright 2003 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "wine/test.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winnls.h"
+#define NO_SHLWAPI_REG
+#define NO_SHLWAPI_PATH
+#define NO_SHLWAPI_GDI
+#define NO_SHLWAPI_STREAM
+#include "shlwapi.h"
+#include "shtypes.h"
+
+static HMODULE hShlwapi;
+static LPSTR (WINAPI *pStrCpyNXA)(LPSTR,LPCSTR,int);
+static LPWSTR (WINAPI *pStrCpyNXW)(LPWSTR,LPCWSTR,int);
+static HRESULT (WINAPI *pStrRetToBSTR)(STRRET*,void*,BSTR*);
+static DWORD (WINAPI *pSHAnsiToAnsi)(LPCSTR,LPSTR,int);
+static DWORD (WINAPI *pSHUnicodeToUnicode)(LPCWSTR,LPWSTR,int);
+static BOOL (WINAPI *pStrIsIntlEqualA)(BOOL,LPCSTR,LPCSTR,int);
+static BOOL (WINAPI *pIntlStrEqWorkerA)(BOOL,LPCSTR,LPCSTR,int);
+static BOOL (WINAPI *pStrIsIntlEqualW)(BOOL,LPCWSTR,LPCWSTR,int);
+static BOOL (WINAPI *pIntlStrEqWorkerW)(BOOL,LPCWSTR,LPCWSTR,int);
+
+static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
+{
+ while (*str1 && (*str1 == *str2)) { str1++; str2++; }
+ return *str1 - *str2;
+}
+
+/* StrToInt/StrToIntEx results */
+typedef struct tagStrToIntResult
+{
+ const char* string;
+ int str_to_int;
+ int str_to_int_ex;
+ int str_to_int_hex;
+} StrToIntResult;
+
+static const StrToIntResult StrToInt_results[] = {
+ { "1099", 1099, 1099, 1099 },
+ { "+88987", 0, 88987, 88987 },
+ { "012", 12, 12, 12 },
+ { "-55", -55, -55, -55 },
+ { "-0", 0, 0, 0 },
+ { "0x44ff", 0, 0, 0x44ff },
+ { "+0x44f4", 0, 0, 0x44f4 },
+ { "-0x44fd", 0, 0, 0x44fd },
+ { "+ 88987", 0, 0, 0 },
+ { "- 55", 0, 0, 0 },
+ { "- 0", 0, 0, 0 },
+ { "+ 0x44f4", 0, 0, 0 },
+ { "--0x44fd", 0, 0, 0 },
+ { " 1999", 0, 1999, 1999 },
+ { " +88987", 0, 88987, 88987 },
+ { " 012", 0, 12, 12 },
+ { " -55", 0, -55, -55 },
+ { " 0x44ff", 0, 0, 0x44ff },
+ { " +0x44f4", 0, 0, 0x44f4 },
+ { " -0x44fd", 0, 0, 0x44fd },
+ { NULL, 0, 0, 0 }
+};
+
+/* pStrFormatByteSize64/StrFormatKBSize results */
+typedef struct tagStrFormatSizeResult
+{
+ LONGLONG value;
+ const char* byte_size_64;
+ const char* kb_size;
+} StrFormatSizeResult;
+
+
+static const StrFormatSizeResult StrFormatSize_results[] = {
+ { -1023, "-1023 bytes", "0 KB"},
+ { -24, "-24 bytes", "0 KB"},
+ { 309, "309 bytes", "1 KB"},
+ { 10191, "9.95 KB", "10 KB"},
+ { 100353, "98.0 KB", "99 KB"},
+ { 1022286, "998 KB", "999 KB"},
+ { 1046862, "0.99 MB", "1,023 KB"},
+ { 1048574619, "999 MB", "1,023,999 KB"},
+ { 1073741775, "0.99 GB", "1,048,576 KB"},
+ { ((LONGLONG)0x000000f9 << 32) | 0xfffff94e, "999 GB", "1,048,575,999 KB"},
+ { ((LONGLONG)0x000000ff << 32) | 0xfffffa9b, "0.99 TB", "1,073,741,823 KB"},
+ { ((LONGLONG)0x0003e7ff << 32) | 0xfffffa9b, "999 TB", "1,073,741,823,999 KB"},
+ { ((LONGLONG)0x0003ffff << 32) | 0xfffffbe8, "0.99 PB", "1,099,511,627,775 KB"},
+ { ((LONGLONG)0x0f9fffff << 32) | 0xfffffd35, "999 PB", "1,099,511,627,776,000 KB"},
+ { ((LONGLONG)0x0fffffff << 32) | 0xfffffa9b, "0.99 EB", "1,125,899,906,842,623 KB"},
+ { 0, NULL, NULL }
+};
+
+/* StrFormatByteSize64/StrFormatKBSize results */
+typedef struct tagStrFromTimeIntervalResult
+{
+ DWORD ms;
+ int digits;
+ const char* time_interval;
+} StrFromTimeIntervalResult;
+
+
+static const StrFromTimeIntervalResult StrFromTimeInterval_results[] = {
+ { 1, 1, " 0 sec" },
+ { 1, 2, " 0 sec" },
+ { 1, 3, " 0 sec" },
+ { 1, 4, " 0 sec" },
+ { 1, 5, " 0 sec" },
+ { 1, 6, " 0 sec" },
+ { 1, 7, " 0 sec" },
+
+ { 1000000, 1, " 10 min" },
+ { 1000000, 2, " 16 min" },
+ { 1000000, 3, " 16 min 40 sec" },
+ { 1000000, 4, " 16 min 40 sec" },
+ { 1000000, 5, " 16 min 40 sec" },
+ { 1000000, 6, " 16 min 40 sec" },
+ { 1000000, 7, " 16 min 40 sec" },
+
+ { 1999999, 1, " 30 min" },
+ { 1999999, 2, " 33 min" },
+ { 1999999, 3, " 33 min 20 sec" },
+ { 1999999, 4, " 33 min 20 sec" },
+ { 1999999, 5, " 33 min 20 sec" },
+ { 1999999, 6, " 33 min 20 sec" },
+ { 1999999, 7, " 33 min 20 sec" },
+
+ { 3999997, 1, " 1 hr" },
+ { 3999997, 2, " 1 hr 6 min" },
+ { 3999997, 3, " 1 hr 6 min 40 sec" },
+ { 3999997, 4, " 1 hr 6 min 40 sec" },
+ { 3999997, 5, " 1 hr 6 min 40 sec" },
+ { 3999997, 6, " 1 hr 6 min 40 sec" },
+ { 3999997, 7, " 1 hr 6 min 40 sec" },
+
+ { 149999851, 7, " 41 hr 40 min 0 sec" },
+ { 150999850, 1, " 40 hr" },
+ { 150999850, 2, " 41 hr" },
+ { 150999850, 3, " 41 hr 50 min" },
+ { 150999850, 4, " 41 hr 56 min" },
+ { 150999850, 5, " 41 hr 56 min 40 sec" },
+ { 150999850, 6, " 41 hr 56 min 40 sec" },
+ { 150999850, 7, " 41 hr 56 min 40 sec" },
+
+ { 493999507, 1, " 100 hr" },
+ { 493999507, 2, " 130 hr" },
+ { 493999507, 3, " 137 hr" },
+ { 493999507, 4, " 137 hr 10 min" },
+ { 493999507, 5, " 137 hr 13 min" },
+ { 493999507, 6, " 137 hr 13 min 20 sec" },
+ { 493999507, 7, " 137 hr 13 min 20 sec" },
+
+ { 0, 0, NULL }
+};
+
+static void test_StrChrA(void)
+{
+ char string[129];
+ WORD count;
+
+ /* this test crashes on win2k SP4 */
+ /*ok(!StrChrA(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+ for (count = 32; count < 128; count++)
+ string[count] = (char)count;
+ string[128] = '\0';
+
+ for (count = 32; count < 128; count++)
+ {
+ LPSTR result = StrChrA(string+32, count);
+ ok(result - string == count,
+ "found char '%c' in wrong place: got %d, expected %d\n",
+ count, result - string, count);
+ }
+
+ for (count = 32; count < 128; count++)
+ {
+ LPSTR result = StrChrA(string+count+1, count);
+ ok(!result, "found char '%c' not in the string\n", count);
+ }
+}
+
+static void test_StrChrW(void)
+{
+ WCHAR string[16385];
+ WORD count;
+
+ /* this test crashes on win2k SP4 */
+ /*ok(!StrChrW(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+ for (count = 32; count < 16384; count++)
+ string[count] = count;
+ string[16384] = '\0';
+
+ for (count = 32; count < 16384; count++)
+ {
+ LPWSTR result = StrChrW(string+32, count);
+ ok((result - string) == count, "found char %d in wrong place\n", count);
+ }
+
+ for (count = 32; count < 16384; count++)
+ {
+ LPWSTR result = StrChrW(string+count+1, count);
+ ok(!result, "found char not in the string\n");
+ }
+}
+
+static void test_StrChrIA(void)
+{
+ char string[129];
+ WORD count;
+
+ /* this test crashes on win2k SP4 */
+ /*ok(!StrChrIA(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+ for (count = 32; count < 128; count++)
+ string[count] = (char)count;
+ string[128] = '\0';
+
+ for (count = 'A'; count <= 'X'; count++)
+ {
+ LPSTR result = StrChrIA(string+32, count);
+
+ ok(result - string == count, "found char '%c' in wrong place\n", count);
+ ok(StrChrIA(result, count)!=NULL, "didn't find lowercase '%c'\n", count);
+ }
+
+ for (count = 'a'; count < 'z'; count++)
+ {
+ LPSTR result = StrChrIA(string+count+1, count);
+ ok(!result, "found char not in the string\n");
+ }
+}
+
+static void test_StrChrIW(void)
+{
+ WCHAR string[129];
+ WORD count;
+
+ /* this test crashes on win2k SP4 */
+ /*ok(!StrChrIA(NULL,'\0'), "found a character in a NULL string!\n");*/
+
+ for (count = 32; count < 128; count++)
+ string[count] = count;
+ string[128] = '\0';
+
+ for (count = 'A'; count <= 'X'; count++)
+ {
+ LPWSTR result = StrChrIW(string+32, count);
+
+ ok(result - string == count, "found char '%c' in wrong place\n", count);
+ ok(StrChrIW(result, count)!=NULL, "didn't find lowercase '%c'\n", count);
+ }
+
+ for (count = 'a'; count < 'z'; count++)
+ {
+ LPWSTR result = StrChrIW(string+count+1, count);
+ ok(!result, "found char not in the string\n");
+ }
+}
+
+static void test_StrRChrA(void)
+{
+ char string[129];
+ WORD count;
+
+ /* this test crashes on win2k SP4 */
+ /*ok(!StrRChrA(NULL, NULL,'\0'), "found a character in a NULL string!\n");*/
+
+ for (count = 32; count < 128; count++)
+ string[count] = (char)count;
+ string[128] = '\0';
+
+ for (count = 32; count < 128; count++)
+ {
+ LPSTR result = StrRChrA(string+32, NULL, count);
+ ok(result - string == count, "found char %d in wrong place\n", count);
+ }
+
+ for (count = 32; count < 128; count++)
+ {
+ LPSTR result = StrRChrA(string+count+1, NULL, count);
+ ok(!result, "found char not in the string\n");
+ }
+
+ for (count = 32; count < 128; count++)
+ {
+ LPSTR result = StrRChrA(string+count+1, string + 127, count);
+ ok(!result, "found char not in the string\n");
+ }
+}
+
+static void test_StrRChrW(void)
+{
+ WCHAR string[129];
+ WORD count;
+
+ /* this test crashes on win2k SP4 */
+ /*ok(!StrRChrW(NULL, NULL,'\0'), "found a character in a NULL string!\n");*/
+
+ for (count = 32; count < 128; count++)
+ string[count] = count;
+ string[128] = '\0';
+
+ for (count = 32; count < 128; count++)
+ {
+ LPWSTR result = StrRChrW(string+32, NULL, count);
+ ok(result - string == count,
+ "found char %d in wrong place: got %d, expected %d\n",
+ count, result - string, count);
+ }
+
+ for (count = 32; count < 128; count++)
+ {
+ LPWSTR result = StrRChrW(string+count+1, NULL, count);
+ ok(!result, "found char %d not in the string\n", count);
+ }
+
+ for (count = 32; count < 128; count++)
+ {
+ LPWSTR result = StrRChrW(string+count+1, string + 127, count);
+ ok(!result, "found char %d not in the string\n", count);
+ }
+}
+
+static void test_StrCpyW(void)
+{
+ WCHAR szSrc[256];
+ WCHAR szBuff[256];
+ const StrFormatSizeResult* result = StrFormatSize_results;
+
+
+ while(result->value)
+ {
+ MultiByteToWideChar(0,0,result->byte_size_64,-1,szSrc,sizeof(szSrc)/sizeof(WCHAR));
+
+ StrCpyW(szBuff, szSrc);
+ ok(!StrCmpW(szSrc, szBuff), "Copied string %s wrong\n", result->byte_size_64);
+ result++;
+ }
+}
+
+
+static void test_StrToIntA(void)
+{
+ const StrToIntResult *result = StrToInt_results;
+ int return_val;
+
+ while (result->string)
+ {
+ return_val = StrToIntA(result->string);
+ ok(return_val == result->str_to_int, "converted '%s' wrong (%d)\n",
+ result->string, return_val);
+ result++;
+ }
+}
+
+static void test_StrToIntW(void)
+{
+ WCHAR szBuff[256];
+ const StrToIntResult *result = StrToInt_results;
+ int return_val;
+
+ while (result->string)
+ {
+ MultiByteToWideChar(0,0,result->string,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR));
+ return_val = StrToIntW(szBuff);
+ ok(return_val == result->str_to_int, "converted '%s' wrong (%d)\n",
+ result->string, return_val);
+ result++;
+ }
+}
+
+static void test_StrToIntExA(void)
+{
+ const StrToIntResult *result = StrToInt_results;
+ int return_val;
+ BOOL bRet;
+
+ while (result->string)
+ {
+ return_val = -1;
+ bRet = StrToIntExA(result->string,0,&return_val);
+ ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+ result->string);
+ if (bRet)
+ ok(return_val == result->str_to_int_ex, "converted '%s' wrong (%d)\n",
+ result->string, return_val);
+ result++;
+ }
+
+ result = StrToInt_results;
+ while (result->string)
+ {
+ return_val = -1;
+ bRet = StrToIntExA(result->string,STIF_SUPPORT_HEX,&return_val);
+ ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+ result->string);
+ if (bRet)
+ ok(return_val == result->str_to_int_hex, "converted '%s' wrong (%d)\n",
+ result->string, return_val);
+ result++;
+ }
+}
+
+static void test_StrToIntExW(void)
+{
+ WCHAR szBuff[256];
+ const StrToIntResult *result = StrToInt_results;
+ int return_val;
+ BOOL bRet;
+
+ while (result->string)
+ {
+ return_val = -1;
+ MultiByteToWideChar(0,0,result->string,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR));
+ bRet = StrToIntExW(szBuff, 0, &return_val);
+ ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+ result->string);
+ if (bRet)
+ ok(return_val == result->str_to_int_ex, "converted '%s' wrong (%d)\n",
+ result->string, return_val);
+ result++;
+ }
+
+ result = StrToInt_results;
+ while (result->string)
+ {
+ return_val = -1;
+ MultiByteToWideChar(0,0,result->string,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR));
+ bRet = StrToIntExW(szBuff, STIF_SUPPORT_HEX, &return_val);
+ ok(!bRet || return_val != -1, "No result returned from '%s'\n",
+ result->string);
+ if (bRet)
+ ok(return_val == result->str_to_int_hex, "converted '%s' wrong (%d)\n",
+ result->string, return_val);
+ result++;
+ }
+}
+
+static void test_StrDupA(void)
+{
+ LPSTR lpszStr;
+ const StrFormatSizeResult* result = StrFormatSize_results;
+
+ while(result->value)
+ {
+ lpszStr = StrDupA(result->byte_size_64);
+
+ ok(lpszStr != NULL, "Dup failed\n");
+ if (lpszStr)
+ {
+ ok(!strcmp(result->byte_size_64, lpszStr), "Copied string wrong\n");
+ LocalFree((HLOCAL)lpszStr);
+ }
+ result++;
+ }
+
+ /* Later versions of shlwapi return NULL for this, but earlier versions
+ * returned an empty string (as Wine does).
+ */
+ lpszStr = StrDupA(NULL);
+ ok(lpszStr == NULL || *lpszStr == '\0', "NULL string returned %p\n", lpszStr);
+}
+
+static void test_StrFormatByteSize64A(void)
+{
+ char szBuff[256];
+ const StrFormatSizeResult* result = StrFormatSize_results;
+
+ while(result->value)
+ {
+ StrFormatByteSize64A(result->value, szBuff, 256);
+
+ ok(!strcmp(result->byte_size_64, szBuff),
+ "Formatted %lx%08lx wrong: got %s, expected %s\n",
+ (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->byte_size_64);
+
+ result++;
+ }
+}
+
+static void test_StrFormatKBSizeW(void)
+{
+ WCHAR szBuffW[256];
+ char szBuff[256];
+ const StrFormatSizeResult* result = StrFormatSize_results;
+
+ while(result->value)
+ {
+ StrFormatKBSizeW(result->value, szBuffW, 256);
+ WideCharToMultiByte(0,0,szBuffW,-1,szBuff,sizeof(szBuff)/sizeof(WCHAR),0,0);
+ ok(!strcmp(result->kb_size, szBuff),
+ "Formatted %lx%08lx wrong: got %s, expected %s\n",
+ (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->kb_size);
+ result++;
+ }
+}
+
+static void test_StrFormatKBSizeA(void)
+{
+ char szBuff[256];
+ const StrFormatSizeResult* result = StrFormatSize_results;
+
+ while(result->value)
+ {
+ StrFormatKBSizeA(result->value, szBuff, 256);
+
+ ok(!strcmp(result->kb_size, szBuff),
+ "Formatted %lx%08lx wrong: got %s, expected %s\n",
+ (LONG)(result->value >> 32), (LONG)result->value, szBuff, result->kb_size);
+ result++;
+ }
+}
+
+static void test_StrFromTimeIntervalA(void)
+{
+ char szBuff[256];
+ const StrFromTimeIntervalResult* result = StrFromTimeInterval_results;
+
+ while(result->ms)
+ {
+ StrFromTimeIntervalA(szBuff, 256, result->ms, result->digits);
+
+ ok(!strcmp(result->time_interval, szBuff), "Formatted %ld %d wrong\n",
+ result->ms, result->digits);
+ result++;
+ }
+}
+
+static void test_StrCmpA(void)
+{
+ static const char str1[] = {'a','b','c','d','e','f'};
+ static const char str2[] = {'a','B','c','d','e','f'};
+ ok(0 != StrCmpNA(str1, str2, 6), "StrCmpNA is case-insensitive\n");
+ ok(0 == StrCmpNIA(str1, str2, 6), "StrCmpNIA is case-sensitive\n");
+ ok(!ChrCmpIA('a', 'a'), "ChrCmpIA doesn't work at all!\n");
+ ok(!ChrCmpIA('b', 'B'), "ChrCmpIA is not case-insensitive\n");
+ ok(ChrCmpIA('a', 'z'), "ChrCmpIA believes that a == z!\n");
+
+ pStrIsIntlEqualA = (void *)GetProcAddress(hShlwapi, "StrIsIntlEqualA");
+ pIntlStrEqWorkerA = (void *)GetProcAddress(hShlwapi, "IntlStrEqWorkerA");
+
+ if (!pStrIsIntlEqualA)
+ return;
+
+ ok(pStrIsIntlEqualA(FALSE, str1, str2, 5), "StrIsIntlEqualA(FALSE,...) isn't case-insensitive\n");
+ ok(!pStrIsIntlEqualA(TRUE, str1, str2, 5), "StrIsIntlEqualA(TRUE,...) isn't case-sensitive\n");
+
+ if (!pIntlStrEqWorkerA)
+ return;
+
+ ok(pIntlStrEqWorkerA(FALSE, str1, str2, 5), "IntlStrEqWorkerA(FALSE,...) isn't case-insensitive\n");
+ ok(!pIntlStrEqWorkerA(TRUE, str1, str2, 5), "pIntlStrEqWorkerA(TRUE,...) isn't case-sensitive\n");
+}
+
+static void test_StrCmpW(void)
+{
+ static const WCHAR str1[] = {'a','b','c','d','e','f'};
+ static const WCHAR str2[] = {'a','B','c','d','e','f'};
+ ok(0 != StrCmpNW(str1, str2, 5), "StrCmpNW is case-insensitive\n");
+ ok(0 == StrCmpNIW(str1, str2, 5), "StrCmpNIW is case-sensitive\n");
+ ok(!ChrCmpIW('a', 'a'), "ChrCmpIW doesn't work at all!\n");
+ ok(!ChrCmpIW('b', 'B'), "ChrCmpIW is not case-insensitive\n");
+ ok(ChrCmpIW('a', 'z'), "ChrCmpIW believes that a == z!\n");
+
+ pStrIsIntlEqualW = (void *)GetProcAddress(hShlwapi, "StrIsIntlEqualW");
+ pIntlStrEqWorkerW = (void *)GetProcAddress(hShlwapi, "IntlStrEqWorkerW");
+
+ if (!pStrIsIntlEqualW)
+ return;
+
+ ok(pStrIsIntlEqualW(FALSE, str1, str2, 5), "StrIsIntlEqualW(FALSE,...) isn't case-insensitive\n");
+ ok(!pStrIsIntlEqualW(TRUE, str1, str2, 5), "StrIsIntlEqualW(TRUE,...) isn't case-sensitive\n");
+
+ if (!pIntlStrEqWorkerW)
+ return;
+
+ ok(pIntlStrEqWorkerW(FALSE, str1, str2, 5), "IntlStrEqWorkerW(FALSE,...) isn't case-insensitive\n");
+ ok(!pIntlStrEqWorkerW(TRUE, str1, str2, 5), "IntlStrEqWorkerW(TRUE,...) isn't case-sensitive\n");
+}
+
+static WCHAR *CoDupStrW(const char* src)
+{
+ INT len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
+ WCHAR* szTemp = (WCHAR*)CoTaskMemAlloc(len * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, src, -1, szTemp, len);
+ return szTemp;
+}
+
+static void test_StrRetToBSTR(void)
+{
+ static const WCHAR szTestW[] = { 'T','e','s','t','\0' };
+ ITEMIDLIST iidl[10];
+ BSTR bstr;
+ STRRET strret;
+ HRESULT ret;
+
+ pStrRetToBSTR = (void *)GetProcAddress(hShlwapi, "StrRetToBSTR");
+ if (!pStrRetToBSTR) return;
+
+ strret.uType = STRRET_WSTR;
+ strret.u.pOleStr = CoDupStrW("Test");
+ bstr = 0;
+ ret = pStrRetToBSTR(&strret, NULL, &bstr);
+ ok(ret == S_OK && bstr && !strcmpW(bstr, szTestW),
+ "STRRET_WSTR: dup failed, ret=0x%08lx, bstr %p\n", ret, bstr);
+ if (bstr)
+ SysFreeString(bstr);
+
+ strret.uType = STRRET_CSTR;
+ lstrcpyA(strret.u.cStr, "Test");
+ ret = pStrRetToBSTR(&strret, NULL, &bstr);
+ ok(ret == S_OK && bstr && !strcmpW(bstr, szTestW),
+ "STRRET_CSTR: dup failed, ret=0x%08lx, bstr %p\n", ret, bstr);
+ if (bstr)
+ SysFreeString(bstr);
+
+ strret.uType = STRRET_OFFSET;
+ strret.u.uOffset = 1;
+ strcpy((char*)&iidl, " Test");
+ ret = pStrRetToBSTR(&strret, iidl, &bstr);
+ ok(ret == S_OK && bstr && !strcmpW(bstr, szTestW),
+ "STRRET_OFFSET: dup failed, ret=0x%08lx, bstr %p\n", ret, bstr);
+ if (bstr)
+ SysFreeString(bstr);
+
+ /* Native crashes if str is NULL */
+}
+
+static void test_StrCpyNXA(void)
+{
+ LPCSTR lpSrc = "hello";
+ LPSTR lpszRes;
+ char dest[8];
+
+ pStrCpyNXA = (void *)GetProcAddress(hShlwapi, (LPSTR)399);
+ if (!pStrCpyNXA)
+ return;
+
+ memset(dest, '\n', sizeof(dest));
+ lpszRes = pStrCpyNXA(dest, lpSrc, sizeof(dest)/sizeof(dest[0]));
+ ok(lpszRes == dest + 5 && !memcmp(dest, "hello\0\n\n", sizeof(dest)),
+ "StrCpyNXA: expected %p, \"hello\\0\\n\\n\", got %p, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+ dest + 5, lpszRes, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+static void test_StrCpyNXW(void)
+{
+ static const WCHAR lpInit[] = { '\n','\n','\n','\n','\n','\n','\n','\n' };
+ static const WCHAR lpSrc[] = { 'h','e','l','l','o','\0' };
+ static const WCHAR lpRes[] = { 'h','e','l','l','o','\0','\n','\n' };
+ LPWSTR lpszRes;
+ WCHAR dest[8];
+
+ pStrCpyNXW = (void *)GetProcAddress(hShlwapi, (LPSTR)400);
+ if (!pStrCpyNXW)
+ return;
+
+ memcpy(dest, lpInit, sizeof(lpInit));
+ lpszRes = pStrCpyNXW(dest, lpSrc, sizeof(dest)/sizeof(dest[0]));
+ ok(lpszRes == dest + 5 && !memcmp(dest, lpRes, sizeof(dest)),
+ "StrCpyNXA: expected %p, \"hello\\0\\n\\n\", got %p, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+ dest + 5, lpszRes, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+static void test_SHAnsiToAnsi(void)
+{
+ char dest[8];
+ DWORD dwRet;
+
+ pSHAnsiToAnsi = (void *)GetProcAddress(hShlwapi, (LPSTR)345);
+ if (!pSHAnsiToAnsi)
+ return;
+
+ memset(dest, '\n', sizeof(dest));
+ dwRet = pSHAnsiToAnsi("hello", dest, sizeof(dest)/sizeof(dest[0]));
+ ok(dwRet == 6 && !memcmp(dest, "hello\0\n\n", sizeof(dest)),
+ "SHAnsiToAnsi: expected 6, \"hello\\0\\n\\n\", got %ld, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+ dwRet, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+static void test_SHUnicodeToUnicode(void)
+{
+ static const WCHAR lpInit[] = { '\n','\n','\n','\n','\n','\n','\n','\n' };
+ static const WCHAR lpSrc[] = { 'h','e','l','l','o','\0' };
+ static const WCHAR lpRes[] = { 'h','e','l','l','o','\0','\n','\n' };
+ WCHAR dest[8];
+ DWORD dwRet;
+
+ pSHUnicodeToUnicode = (void *)GetProcAddress(hShlwapi, (LPSTR)346);
+ if (!pSHUnicodeToUnicode)
+ return;
+
+ memcpy(dest, lpInit, sizeof(lpInit));
+ dwRet = pSHUnicodeToUnicode(lpSrc, dest, sizeof(dest)/sizeof(dest[0]));
+ ok(dwRet == 6 && !memcmp(dest, lpRes, sizeof(dest)),
+ "SHUnicodeToUnicode: expected 6, \"hello\\0\\n\\n\", got %ld, \"%d,%d,%d,%d,%d,%d,%d,%d\"\n",
+ dwRet, dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]);
+}
+
+START_TEST(string)
+{
+ CoInitialize(0);
+
+ hShlwapi = GetModuleHandleA("shlwapi");
+ if (!hShlwapi)
+ return;
+
+ test_StrChrA();
+ test_StrChrW();
+ test_StrChrIA();
+ test_StrChrIW();
+ test_StrRChrA();
+ test_StrRChrW();
+ test_StrCpyW();
+ test_StrToIntA();
+ test_StrToIntW();
+ test_StrToIntExA();
+ test_StrToIntExW();
+ test_StrDupA();
+ if (0)
+ {
+ /* this test fails on locales which do not use '.' as a decimal separator */
+ test_StrFormatByteSize64A();
+
+ /* this test fails on locales which do not use '.' as a decimal separator */
+ test_StrFormatKBSizeA();
+
+ /* FIXME: Awaiting NLS fixes in kernel before these succeed */
+ test_StrFormatKBSizeW();
+ }
+ test_StrFromTimeIntervalA();
+ test_StrCmpA();
+ test_StrCmpW();
+ test_StrRetToBSTR();
+ test_StrCpyNXA();
+ test_StrCpyNXW();
+ test_SHAnsiToAnsi();
+ test_SHUnicodeToUnicode();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_clist(void);
+extern void func_clsid(void);
+extern void func_generated(void);
+extern void func_ordinal(void);
+extern void func_path(void);
+extern void func_shreg(void);
+extern void func_string(void);
+
+const struct test winetest_testlist[] =
+{
+ { "clist", func_clist },
+// { "clsid", func_clsid },
+// { "generated", func_generated },
+ { "ordinal", func_ordinal },
+// { "path", func_path },
+ { "shreg", func_shreg },
+ { "string", func_string },
+ { 0, 0 }
+};
--- /dev/null
+/* Unit test suite for window classes.
+ *
+ * Copyright 2002 Mike McCormack
+ * Copyright 2003 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* To get CS_DROPSHADOW with the MSVC headers */
+#define _WIN32_WINNT 0x0501
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#define NUMCLASSWORDS 4
+
+static LRESULT WINAPI ClassTest_WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return DefWindowProcW (hWnd, msg, wParam, lParam);
+}
+
+/***********************************************************************
+ */
+static void ClassTest(HINSTANCE hInstance, BOOL global)
+{
+ WNDCLASSW cls, wc;
+ static const WCHAR className[] = {'T','e','s','t','C','l','a','s','s',0};
+ static const WCHAR winName[] = {'W','i','n','C','l','a','s','s','T','e','s','t',0};
+ ATOM test_atom;
+ HWND hTestWnd;
+ LONG i;
+ WCHAR str[20];
+ ATOM classatom;
+
+ cls.style = CS_HREDRAW | CS_VREDRAW | (global?CS_GLOBALCLASS:0);
+ cls.lpfnWndProc = ClassTest_WndProc;
+ cls.cbClsExtra = NUMCLASSWORDS*sizeof(DWORD);
+ cls.cbWndExtra = 12;
+ cls.hInstance = hInstance;
+ cls.hIcon = LoadIconW (0, (LPWSTR)IDI_APPLICATION);
+ cls.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject (WHITE_BRUSH);
+ cls.lpszMenuName = 0;
+ cls.lpszClassName = className;
+
+ classatom=RegisterClassW(&cls);
+ if (!classatom && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(classatom, "failed to register class\n");
+
+ ok(!RegisterClassW (&cls),
+ "RegisterClass of the same class should fail for the second time\n");
+
+ /* Setup windows */
+ hTestWnd = CreateWindowW (className, winName,
+ WS_OVERLAPPEDWINDOW + WS_HSCROLL + WS_VSCROLL,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0,
+ 0, hInstance, 0);
+
+ ok(hTestWnd!=0, "Failed to create window\n");
+
+ /* test initial values of valid classwords */
+ for(i=0; i<NUMCLASSWORDS; i++)
+ {
+ SetLastError(0);
+ ok(!GetClassLongW(hTestWnd,i*sizeof (DWORD)),
+ "GetClassLongW initial value nonzero!\n");
+ ok(!GetLastError(),
+ "GetClassLongW failed!\n");
+ }
+
+#if 0
+ /*
+ * GetClassLongW(hTestWnd, NUMCLASSWORDS*sizeof(DWORD))
+ * does not fail on Win 98, though MSDN says it should
+ */
+ SetLastError(0);
+ GetClassLongW(hTestWnd, NUMCLASSWORDS*sizeof(DWORD));
+ ok(GetLastError(),
+ "GetClassLongW() with invalid offset did not fail\n");
+#endif
+
+ /* set values of valid class words */
+ for(i=0; i<NUMCLASSWORDS; i++)
+ {
+ SetLastError(0);
+ ok(!SetClassLongW(hTestWnd,i*sizeof(DWORD),i+1),
+ "GetClassLongW(%ld) initial value nonzero!\n",i*sizeof(DWORD));
+ ok(!GetLastError(),
+ "SetClassLongW(%ld) failed!\n",i*sizeof(DWORD));
+ }
+
+ /* test values of valid classwords that we set */
+ for(i=0; i<NUMCLASSWORDS; i++)
+ {
+ SetLastError(0);
+ ok( (i+1) == GetClassLongW(hTestWnd,i*sizeof (DWORD)),
+ "GetClassLongW value doesn't match what was set!\n");
+ ok(!GetLastError(),
+ "GetClassLongW failed!\n");
+ }
+
+ /* check GetClassName */
+ i = GetClassNameW(hTestWnd, str, sizeof(str));
+ ok(i == lstrlenW(className),
+ "GetClassName returned incorrect length\n");
+ ok(!lstrcmpW(className,str),
+ "GetClassName returned incorrect name for this window's class\n");
+
+ /* check GetClassInfo with our hInstance */
+ if((test_atom = GetClassInfoW(hInstance, str, &wc)))
+ {
+ ok(test_atom == classatom,
+ "class atom did not match\n");
+ ok(wc.cbClsExtra == cls.cbClsExtra,
+ "cbClsExtra did not match\n");
+ ok(wc.cbWndExtra == cls.cbWndExtra,
+ "cbWndExtra did not match\n");
+ ok(wc.hbrBackground == cls.hbrBackground,
+ "hbrBackground did not match\n");
+ ok(wc.hCursor== cls.hCursor,
+ "hCursor did not match\n");
+ ok(wc.hInstance== cls.hInstance,
+ "hInstance did not match\n");
+ }
+ else
+ ok(FALSE,"GetClassInfo (hinstance) failed!\n");
+
+ /* check GetClassInfo with zero hInstance */
+ if(global)
+ {
+ if((test_atom = GetClassInfoW(0, str, &wc)))
+ {
+ ok(test_atom == classatom,
+ "class atom did not match %x != %x\n", test_atom, classatom);
+ ok(wc.cbClsExtra == cls.cbClsExtra,
+ "cbClsExtra did not match %x!=%x\n",wc.cbClsExtra,cls.cbClsExtra);
+ ok(wc.cbWndExtra == cls.cbWndExtra,
+ "cbWndExtra did not match %x!=%x\n",wc.cbWndExtra,cls.cbWndExtra);
+ ok(wc.hbrBackground == cls.hbrBackground,
+ "hbrBackground did not match %p!=%p\n",wc.hbrBackground,cls.hbrBackground);
+ ok(wc.hCursor== cls.hCursor,
+ "hCursor did not match %p!=%p\n",wc.hCursor,cls.hCursor);
+ ok(!wc.hInstance,
+ "hInstance not zero for global class %p\n",wc.hInstance);
+ }
+ else
+ ok(FALSE,"GetClassInfo (0) failed for global class!\n");
+ }
+ else
+ {
+ ok(!GetClassInfoW(0, str, &wc),
+ "GetClassInfo (0) succeeded for local class!\n");
+ }
+
+ ok(!UnregisterClassW(className, hInstance),
+ "Unregister class succeeded with window existing\n");
+
+ ok(DestroyWindow(hTestWnd),
+ "DestroyWindow() failed!\n");
+
+ ok(UnregisterClassW(className, hInstance),
+ "UnregisterClass() failed\n");
+
+ return;
+}
+
+static void check_style( const char *name, int must_exist, UINT style, UINT ignore )
+{
+ WNDCLASS wc;
+
+ if (GetClassInfo( 0, name, &wc ))
+ {
+ ok( !(~wc.style & style & ~ignore), "System class %s is missing bits %x (%08x/%08x)\n",
+ name, ~wc.style & style, wc.style, style );
+ ok( !(wc.style & ~style), "System class %s has extra bits %x (%08x/%08x)\n",
+ name, wc.style & ~style, wc.style, style );
+ }
+ else
+ ok( !must_exist, "System class %s does not exist\n", name );
+}
+
+/* test styles of system classes */
+static void test_styles(void)
+{
+ /* check style bits */
+ check_style( "Button", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 );
+ check_style( "ComboBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 );
+ check_style( "Edit", 1, CS_PARENTDC | CS_DBLCLKS, 0 );
+ check_style( "ListBox", 1, CS_PARENTDC | CS_DBLCLKS, CS_PARENTDC /*FIXME*/ );
+ check_style( "MDIClient", 1, 0, 0 );
+ check_style( "ScrollBar", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, 0 );
+ check_style( "Static", 1, CS_PARENTDC | CS_DBLCLKS, 0 );
+ check_style( "ComboLBox", 1, CS_SAVEBITS | CS_DBLCLKS, 0 );
+ check_style( "DDEMLEvent", 0, 0, 0 );
+ check_style( "Message", 0, 0, 0 );
+ check_style( "#32768", 1, CS_DROPSHADOW | CS_SAVEBITS | CS_DBLCLKS, CS_DROPSHADOW ); /* menu */
+ check_style( "#32769", 1, CS_DBLCLKS, 0 ); /* desktop */
+ check_style( "#32770", 1, CS_SAVEBITS | CS_DBLCLKS, 0 ); /* dialog */
+ todo_wine { check_style( "#32771", 1, CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW, 0 ); } /* task switch */
+ check_style( "#32772", 1, 0, 0 ); /* icon title */
+}
+
+static void check_class(HINSTANCE inst, const char *name, const char *menu_name)
+{
+ WNDCLASS wc;
+ UINT atom = GetClassInfo(inst,name,&wc);
+ ok( atom, "Class %s %p not found\n", name, inst );
+ if (atom)
+ {
+ if (wc.lpszMenuName && menu_name)
+ ok( !strcmp( menu_name, wc.lpszMenuName ), "Wrong name %s/%s for class %s %p\n",
+ wc.lpszMenuName, menu_name, name, inst );
+ else
+ ok( !menu_name == !wc.lpszMenuName, "Wrong name %p/%p for class %s %p\n",
+ wc.lpszMenuName, menu_name, name, inst );
+ }
+}
+
+static void check_instance( const char *name, HINSTANCE inst, HINSTANCE info_inst, HINSTANCE gcl_inst )
+{
+ WNDCLASSA wc;
+ HWND hwnd;
+
+ ok( GetClassInfo( inst, name, &wc ), "Couldn't find class %s inst %p\n", name, inst );
+ ok( wc.hInstance == info_inst, "Wrong info instance %p/%p for class %s\n",
+ wc.hInstance, info_inst, name );
+ hwnd = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, inst, 0 );
+ ok( hwnd != NULL, "Couldn't create window for class %s inst %p\n", name, inst );
+ ok( (HINSTANCE)GetClassLongPtrA( hwnd, GCLP_HMODULE ) == gcl_inst,
+ "Wrong GCL instance %p/%p for class %s\n",
+ (HINSTANCE)GetClassLongPtrA( hwnd, GCLP_HMODULE ), gcl_inst, name );
+ ok( (HINSTANCE)GetWindowLongPtrA( hwnd, GWLP_HINSTANCE ) == inst,
+ "Wrong GWL instance %p/%p for window %s\n",
+ (HINSTANCE)GetWindowLongPtrA( hwnd, GWLP_HINSTANCE ), inst, name );
+ ok(!UnregisterClassA(name, inst), "UnregisterClassA should fail while exists a class window\n");
+ ok(GetLastError() == ERROR_CLASS_HAS_WINDOWS, "GetLastError() should be set to ERROR_CLASS_HAS_WINDOWS not %ld\n", GetLastError());
+ DestroyWindow(hwnd);
+}
+
+struct class_info
+{
+ const char *name;
+ HINSTANCE inst, info_inst, gcl_inst;
+};
+
+static DWORD WINAPI thread_proc(void *param)
+{
+ struct class_info *class_info = (struct class_info *)param;
+
+ check_instance(class_info->name, class_info->inst, class_info->info_inst, class_info->gcl_inst);
+
+ return 0;
+}
+
+static void check_thread_instance( const char *name, HINSTANCE inst, HINSTANCE info_inst, HINSTANCE gcl_inst )
+{
+ HANDLE hThread;
+ DWORD tid;
+ struct class_info class_info;
+
+ class_info.name = name;
+ class_info.inst = inst;
+ class_info.info_inst = info_inst;
+ class_info.gcl_inst = gcl_inst;
+
+ hThread = CreateThread(NULL, 0, thread_proc, &class_info, 0, &tid);
+ ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+ ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+ CloseHandle(hThread);
+}
+
+/* test various instance parameters */
+static void test_instances(void)
+{
+ WNDCLASSA cls, wc;
+ HWND hwnd, hwnd2;
+ const char *name = "__test__";
+ HINSTANCE kernel32 = GetModuleHandleA("kernel32");
+ HINSTANCE user32 = GetModuleHandleA("user32");
+ HINSTANCE main_module = GetModuleHandleA(NULL);
+
+ memset( &cls, 0, sizeof(cls) );
+ cls.style = CS_HREDRAW | CS_VREDRAW;
+ cls.lpfnWndProc = ClassTest_WndProc;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.lpszClassName = name;
+
+ cls.lpszMenuName = "main_module";
+ cls.hInstance = main_module;
+
+ ok( RegisterClassA( &cls ), "Failed to register local class for main module\n" );
+ check_class( main_module, name, "main_module" );
+ check_instance( name, main_module, main_module, main_module );
+ check_thread_instance( name, main_module, main_module, main_module );
+
+ cls.lpszMenuName = "kernel32";
+ cls.hInstance = kernel32;
+ ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+ check_class( kernel32, name, "kernel32" );
+ check_class( main_module, name, "main_module" );
+ check_instance( name, kernel32, kernel32, kernel32 );
+ check_thread_instance( name, kernel32, kernel32, kernel32 );
+ ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+ /* Bug 2631 - Supplying an invalid number of bytes fails */
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = -1;
+ SetLastError(0xdeadbeef);
+ ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+ "Failed with invalid number of WndExtra bytes\n");
+
+ cls.cbClsExtra = -1;
+ cls.cbWndExtra = 0;
+ SetLastError(0xdeadbeef);
+ ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+ "Failed with invalid number of ClsExtra bytes\n");
+
+ cls.cbClsExtra = -1;
+ cls.cbWndExtra = -1;
+ SetLastError(0xdeadbeef);
+ ok( ((RegisterClassA( &cls ) == 0) && (GetLastError() == ERROR_INVALID_PARAMETER)),
+ "Failed with invalid number of ClsExtra and cbWndExtra bytes\n");
+
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ SetLastError(0xdeadbeef);
+
+ /* setting global flag doesn't change status of class */
+ hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 );
+ SetClassLongA( hwnd, GCL_STYLE, CS_GLOBALCLASS );
+ cls.lpszMenuName = "kernel32";
+ cls.hInstance = kernel32;
+ ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+ check_class( kernel32, name, "kernel32" );
+ check_class( main_module, name, "main_module" );
+ check_instance( name, kernel32, kernel32, kernel32 );
+ check_instance( name, main_module, main_module, main_module );
+ check_thread_instance( name, kernel32, kernel32, kernel32 );
+ check_thread_instance( name, main_module, main_module, main_module );
+ ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+ /* changing the instance doesn't make it global */
+ SetClassLongPtrA( hwnd, GCLP_HMODULE, 0 );
+ ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+ check_class( kernel32, name, "kernel32" );
+ check_instance( name, kernel32, kernel32, kernel32 );
+ check_thread_instance( name, kernel32, kernel32, kernel32 );
+ ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" );
+ ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+ /* GetClassInfo with instance 0 finds user32 instance */
+ SetClassLongPtrA( hwnd, GCLP_HMODULE, (LONG_PTR)user32 );
+ ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+ check_class( kernel32, name, "kernel32" );
+ check_class( user32, name, "main_module" );
+ check_class( 0, name, "main_module" );
+ check_instance( name, kernel32, kernel32, kernel32 );
+ check_instance( name, user32, 0, user32 );
+ check_instance( name, 0, 0, kernel32 );
+ check_thread_instance( name, kernel32, kernel32, kernel32 );
+ check_thread_instance( name, user32, 0, user32 );
+ check_thread_instance( name, 0, 0, kernel32 );
+ ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+ SetClassLongPtrA( hwnd, GCLP_HMODULE, 0x12345678 );
+ ok( RegisterClassA( &cls ), "Failed to register local class for kernel32\n" );
+ check_class( kernel32, name, "kernel32" );
+ check_class( (HINSTANCE)0x12345678, name, "main_module" );
+ check_instance( name, kernel32, kernel32, kernel32 );
+ check_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 );
+ check_thread_instance( name, kernel32, kernel32, kernel32 );
+ check_thread_instance( name, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0x12345678 );
+ ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" );
+
+ /* creating a window with instance 0 uses the first class found */
+ cls.hInstance = (HINSTANCE)0xdeadbeef;
+ cls.lpszMenuName = "deadbeef";
+ cls.style = 3;
+ ok( RegisterClassA( &cls ), "Failed to register local class for deadbeef\n" );
+ hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 );
+ ok( (HINSTANCE)GetClassLongPtrA( hwnd2, GCLP_HMODULE ) == (HINSTANCE)0xdeadbeef,
+ "Didn't get deadbeef class for null instance\n" );
+ DestroyWindow( hwnd2 );
+ ok( UnregisterClassA( name, (HINSTANCE)0xdeadbeef ), "Unregister failed for deadbeef\n" );
+
+ hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 );
+ ok( (HINSTANCE)GetClassLongPtrA( hwnd2, GCLP_HMODULE ) == kernel32,
+ "Didn't get kernel32 class for null instance\n" );
+ DestroyWindow( hwnd2 );
+
+ ok( UnregisterClassA( name, kernel32 ), "Unregister failed for kernel32\n" );
+
+ hwnd2 = CreateWindowExA( 0, name, "test_window", 0, 0, 0, 0, 0, 0, 0, NULL, 0 );
+ ok( GetClassLongPtrA( hwnd2, GCLP_HMODULE ) == 0x12345678,
+ "Didn't get 12345678 class for null instance\n" );
+ DestroyWindow( hwnd2 );
+
+ SetClassLongPtrA( hwnd, GCLP_HMODULE, (LONG_PTR)main_module );
+ DestroyWindow( hwnd );
+
+ /* null handle means the same thing as main module */
+ cls.lpszMenuName = "null";
+ cls.hInstance = 0;
+ ok( !RegisterClassA( &cls ), "Succeeded registering local class for null instance\n" );
+ ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+ ok( UnregisterClassA( name, main_module ), "Unregister failed for main module\n" );
+
+ ok( RegisterClassA( &cls ), "Failed to register local class for null instance\n" );
+ /* must be found with main module handle */
+ check_class( main_module, name, "null" );
+ check_instance( name, main_module, main_module, main_module );
+ check_thread_instance( name, main_module, main_module, main_module );
+ ok( !GetClassInfo( 0, name, &wc ), "Class found with null instance\n" );
+ ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+ ok( UnregisterClassA( name, 0 ), "Unregister failed for null instance\n" );
+
+ /* registering for user32 always fails */
+ cls.lpszMenuName = "user32";
+ cls.hInstance = user32;
+ ok( !RegisterClassA( &cls ), "Succeeded registering local class for user32\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %ld\n", GetLastError() );
+ cls.style |= CS_GLOBALCLASS;
+ ok( !RegisterClassA( &cls ), "Succeeded registering global class for user32\n" );
+ ok( GetLastError() == ERROR_INVALID_PARAMETER, "Wrong error code %ld\n", GetLastError() );
+
+ /* unregister is OK though */
+ cls.hInstance = main_module;
+ ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" );
+ ok( UnregisterClassA( name, user32 ), "Unregister failed for user32\n" );
+
+ /* instance doesn't matter for global class */
+ cls.style |= CS_GLOBALCLASS;
+ cls.lpszMenuName = "main_module";
+ cls.hInstance = main_module;
+ ok( RegisterClassA( &cls ), "Failed to register global class for main module\n" );
+ cls.lpszMenuName = "kernel32";
+ cls.hInstance = kernel32;
+ ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" );
+ ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+ /* even if global flag is cleared */
+ hwnd = CreateWindowExA( 0, name, "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 );
+ SetClassLongA( hwnd, GCL_STYLE, 0 );
+ ok( !RegisterClassA( &cls ), "Succeeded registering local class for kernel32\n" );
+ ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+
+ check_class( main_module, name, "main_module" );
+ check_class( kernel32, name, "main_module" );
+ check_class( 0, name, "main_module" );
+ check_class( (HINSTANCE)0x12345678, name, "main_module" );
+ check_instance( name, main_module, main_module, main_module );
+ check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module );
+ check_thread_instance( name, main_module, main_module, main_module );
+ check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, main_module );
+
+ /* changing the instance for global class doesn't make much difference */
+ SetClassLongPtrA( hwnd, GCLP_HMODULE, 0xdeadbeef );
+ check_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef );
+ check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef );
+ check_thread_instance( name, main_module, main_module, (HINSTANCE)0xdeadbeef );
+ check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef );
+
+ DestroyWindow( hwnd );
+ ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" );
+ ok( !UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister succeeded the second time\n" );
+ ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+
+ cls.hInstance = (HINSTANCE)0x12345678;
+ ok( RegisterClassA( &cls ), "Failed to register global class for dummy instance\n" );
+ check_instance( name, main_module, main_module, (HINSTANCE)0x12345678 );
+ check_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 );
+ check_thread_instance( name, main_module, main_module, (HINSTANCE)0x12345678 );
+ check_thread_instance( name, (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, (HINSTANCE)0x12345678 );
+ ok( UnregisterClassA( name, (HINSTANCE)0x87654321 ), "Unregister failed for main module global\n" );
+
+ /* check system classes */
+
+ /* we cannot register a global class with the name of a system class */
+ cls.style |= CS_GLOBALCLASS;
+ cls.lpszMenuName = "button_main_module";
+ cls.lpszClassName = "BUTTON";
+ cls.hInstance = main_module;
+ ok( !RegisterClassA( &cls ), "Succeeded registering global button class for main module\n" );
+ ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+ cls.hInstance = kernel32;
+ ok( !RegisterClassA( &cls ), "Succeeded registering global button class for kernel32\n" );
+ ok( GetLastError() == ERROR_CLASS_ALREADY_EXISTS, "Wrong error code %ld\n", GetLastError() );
+
+ /* local class is OK however */
+ cls.style &= ~CS_GLOBALCLASS;
+ cls.lpszMenuName = "button_main_module";
+ cls.hInstance = main_module;
+ ok( RegisterClassA( &cls ), "Failed to register local button class for main module\n" );
+ check_class( main_module, "BUTTON", "button_main_module" );
+ cls.lpszMenuName = "button_kernel32";
+ cls.hInstance = kernel32;
+ ok( RegisterClassA( &cls ), "Failed to register local button class for kernel32\n" );
+ check_class( kernel32, "BUTTON", "button_kernel32" );
+ check_class( main_module, "BUTTON", "button_main_module" );
+ ok( UnregisterClassA( "BUTTON", kernel32 ), "Unregister failed for kernel32 button\n" );
+ ok( UnregisterClassA( "BUTTON", main_module ), "Unregister failed for main module button\n" );
+ /* GetClassInfo sets instance to passed value for global classes */
+ check_instance( "BUTTON", 0, 0, user32 );
+ check_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+ check_instance( "BUTTON", user32, 0, user32 );
+ check_thread_instance( "BUTTON", 0, 0, user32 );
+ check_thread_instance( "BUTTON", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+ check_thread_instance( "BUTTON", user32, 0, user32 );
+
+ /* we can unregister system classes */
+ ok( GetClassInfo( 0, "BUTTON", &wc ), "Button class not found with null instance\n" );
+ ok( GetClassInfo( kernel32, "BUTTON", &wc ), "Button class not found with kernel32\n" );
+ ok( UnregisterClass( "BUTTON", (HINSTANCE)0x12345678 ), "Failed to unregister button\n" );
+ ok( !UnregisterClass( "BUTTON", (HINSTANCE)0x87654321 ), "Unregistered button a second time\n" );
+ ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+ ok( !GetClassInfo( 0, "BUTTON", &wc ), "Button still exists\n" );
+ ok( GetLastError() == ERROR_CLASS_DOES_NOT_EXIST, "Wrong error code %ld\n", GetLastError() );
+
+ /* we can change the instance of a system class */
+ check_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+ check_thread_instance( "EDIT", (HINSTANCE)0xdeadbeef, (HINSTANCE)0xdeadbeef, user32 );
+ hwnd = CreateWindowExA( 0, "EDIT", "test", 0, 0, 0, 0, 0, 0, 0, main_module, 0 );
+ SetClassLongPtrA( hwnd, GCLP_HMODULE, 0xdeadbeef );
+ check_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef );
+ check_thread_instance( "EDIT", (HINSTANCE)0x12345678, (HINSTANCE)0x12345678, (HINSTANCE)0xdeadbeef );
+}
+
+static LRESULT WINAPI TestDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);
+}
+
+static BOOL RegisterTestDialog(HINSTANCE hInstance)
+{
+ WNDCLASSEX wcx;
+ ATOM atom = 0;
+
+ ZeroMemory(&wcx, sizeof(WNDCLASSEX));
+ wcx.cbSize = sizeof(wcx);
+ wcx.lpfnWndProc = TestDlgProc;
+ wcx.cbClsExtra = 0;
+ wcx.cbWndExtra = DLGWINDOWEXTRA;
+ wcx.hInstance = hInstance;
+ wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
+ wcx.lpszClassName = "TestDialog";
+ wcx.lpszMenuName = "TestDialog";
+ wcx.hIconSm = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(5),
+ IMAGE_ICON,
+ GetSystemMetrics(SM_CXSMICON),
+ GetSystemMetrics(SM_CYSMICON),
+ LR_DEFAULTCOLOR);
+
+ atom = RegisterClassEx(&wcx);
+ ok(atom != 0, "RegisterClassEx returned 0\n");
+
+ return atom;
+}
+
+/* test registering a dialog box created by using the CLASS directive in a
+ resource file, then test creating the dialog using CreateDialogParam. */
+static void WINAPI CreateDialogParamTest(HINSTANCE hInstance)
+{
+ HWND hWndMain;
+
+ if (RegisterTestDialog(hInstance))
+ {
+ hWndMain = CreateDialogParam(hInstance, "CLASS_TEST_DIALOG", NULL, 0, 0);
+ ok(hWndMain != NULL, "CreateDialogParam returned NULL\n");
+ ShowWindow(hWndMain, SW_SHOW);
+ DestroyWindow(hWndMain);
+ }
+}
+
+START_TEST(class)
+{
+ HANDLE hInstance = GetModuleHandleA( NULL );
+
+ if (!GetModuleHandleW(0))
+ {
+ trace("Class test is incompatible with Win9x implementation, skipping\n");
+ return;
+ }
+
+ ClassTest(hInstance,FALSE);
+ ClassTest(hInstance,TRUE);
+ CreateDialogParamTest(hInstance);
+ test_styles();
+ test_instances();
+}
--- /dev/null
+/*
+ * Unit test suite for clipboard functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include "windows.h"
+
+static BOOL is_win9x = FALSE;
+
+#define test_last_error(expected_error) \
+ do \
+ { \
+ if (!is_win9x) \
+ ok(GetLastError() == expected_error, \
+ "Last error should be set to %d, not %ld\n", \
+ expected_error, GetLastError()); \
+ } while (0)
+
+static void test_ClipboardOwner(void)
+{
+ HWND hWnd1, hWnd2;
+ BOOL ret;
+
+ SetLastError(0xdeadbeef);
+ ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef,
+ "could not perform clipboard test: clipboard already owned\n");
+
+ hWnd1 = CreateWindowExA(0, "static", NULL, WS_POPUP,
+ 0, 0, 10, 10, 0, 0, 0, NULL);
+ ok(hWnd1 != 0, "CreateWindowExA error %ld\n", GetLastError());
+ trace("hWnd1 = %p\n", hWnd1);
+
+ hWnd2 = CreateWindowExA(0, "static", NULL, WS_POPUP,
+ 0, 0, 10, 10, 0, 0, 0, NULL);
+ ok(hWnd2 != 0, "CreateWindowExA error %ld\n", GetLastError());
+ trace("hWnd2 = %p\n", hWnd2);
+
+ SetLastError(0xdeadbeef);
+ ok(!CloseClipboard(), "CloseClipboard should fail if clipboard wasn't open\n");
+ test_last_error(ERROR_CLIPBOARD_NOT_OPEN);
+
+ ok(OpenClipboard(0), "OpenClipboard failed\n");
+ ok(!GetClipboardOwner(), "clipboard should still be not owned\n");
+ ok(!OpenClipboard(hWnd1), "OpenClipboard should fail since clipboard already opened\n");
+ ret = CloseClipboard();
+ ok( ret, "CloseClipboard error %ld\n", GetLastError());
+
+ ok(OpenClipboard(hWnd1), "OpenClipboard failed\n");
+
+ SetLastError(0xdeadbeef);
+ ok(!OpenClipboard(hWnd2) && GetLastError() == 0xdeadbeef,
+ "OpenClipboard should fail without setting last error value\n");
+
+ SetLastError(0xdeadbeef);
+ ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef, "clipboard should still be not owned\n");
+ ret = EmptyClipboard();
+ ok( ret, "EmptyClipboard error %ld\n", GetLastError());
+ ok(GetClipboardOwner() == hWnd1, "clipboard should be owned by %p, not by %p\n", hWnd1, GetClipboardOwner());
+
+ SetLastError(0xdeadbeef);
+ ok(!OpenClipboard(hWnd2) && GetLastError() == 0xdeadbeef,
+ "OpenClipboard should fail without setting last error value\n");
+
+ ret = CloseClipboard();
+ ok( ret, "CloseClipboard error %ld\n", GetLastError());
+ ok(GetClipboardOwner() == hWnd1, "clipboard should still be owned\n");
+
+ ret = DestroyWindow(hWnd1);
+ ok( ret, "DestroyWindow error %ld\n", GetLastError());
+ ret = DestroyWindow(hWnd2);
+ ok( ret, "DestroyWindow error %ld\n", GetLastError());
+ SetLastError(0xdeadbeef);
+ ok(!GetClipboardOwner() && GetLastError() == 0xdeadbeef, "clipboard should not be owned\n");
+}
+
+static void test_RegisterClipboardFormatA(void)
+{
+ ATOM atom_id;
+ UINT format_id, format_id2;
+ char buf[256];
+ int len;
+ BOOL ret;
+
+ format_id = RegisterClipboardFormatA("my_cool_clipboard_format");
+ ok(format_id > 0xc000 && format_id < 0xffff, "invalid clipboard format id %04x\n", format_id);
+
+ format_id2 = RegisterClipboardFormatA("MY_COOL_CLIPBOARD_FORMAT");
+ ok(format_id2 == format_id, "invalid clipboard format id %04x\n", format_id2);
+
+ len = GetClipboardFormatNameA(format_id, buf, 256);
+ ok(len == lstrlenA("my_cool_clipboard_format"), "wrong format name length %d\n", len);
+ ok(!lstrcmpA(buf, "my_cool_clipboard_format"), "wrong format name \"%s\"\n", buf);
+
+ lstrcpyA(buf, "foo");
+ SetLastError(0xdeadbeef);
+ len = GetAtomNameA((ATOM)format_id, buf, 256);
+ ok(len == 0, "GetAtomNameA should fail\n");
+ test_last_error(ERROR_INVALID_HANDLE);
+
+todo_wine
+{
+ lstrcpyA(buf, "foo");
+ SetLastError(0xdeadbeef);
+ len = GlobalGetAtomNameA((ATOM)format_id, buf, 256);
+ ok(len == 0, "GlobalGetAtomNameA should fail\n");
+ test_last_error(ERROR_INVALID_HANDLE);
+}
+
+ SetLastError(0xdeadbeef);
+ atom_id = FindAtomA("my_cool_clipboard_format");
+ ok(atom_id == 0, "FindAtomA should fail\n");
+ test_last_error(ERROR_FILE_NOT_FOUND);
+
+#if 0
+ /* this relies on the clipboard and global atom table being different */
+ SetLastError(0xdeadbeef);
+ atom_id = GlobalFindAtomA("my_cool_clipboard_format");
+ ok(atom_id == 0, "GlobalFindAtomA should fail\n");
+ test_last_error(ERROR_FILE_NOT_FOUND);
+
+ for (format_id = 0; format_id < 0xffff; format_id++)
+ {
+ SetLastError(0xdeadbeef);
+ len = GetClipboardFormatNameA(format_id, buf, 256);
+
+ if (format_id < 0xc000)
+ {
+ ok(!len, "GetClipboardFormatNameA should fail, but it returned %d (%s)\n", len, buf);
+ test_last_error(ERROR_INVALID_PARAMETER);
+ }
+ else
+ {
+ if (len)
+ trace("%04x: %s\n", format_id, len ? buf : "");
+ else
+ test_last_error(ERROR_INVALID_HANDLE);
+ }
+ }
+#endif
+
+ ret = OpenClipboard(0);
+ ok( ret, "OpenClipboard error %ld\n", GetLastError());
+
+ trace("# of formats available: %d\n", CountClipboardFormats());
+
+ format_id = 0;
+ while ((format_id = EnumClipboardFormats(format_id)))
+ {
+ ok(IsClipboardFormatAvailable(format_id), "format %04x was listed as available\n", format_id);
+ len = GetClipboardFormatNameA(format_id, buf, 256);
+ trace("%04x: %s\n", format_id, len ? buf : "");
+ }
+
+ ret = EmptyClipboard();
+ ok( ret, "EmptyClipboard error %ld\n", GetLastError());
+ ret =CloseClipboard();
+ ok( ret, "CloseClipboard error %ld\n", GetLastError());
+
+ if (CountClipboardFormats())
+ {
+ SetLastError(0xdeadbeef);
+ ok(!EnumClipboardFormats(0), "EnumClipboardFormats should fail if clipboard wasn't open\n");
+ ok(GetLastError() == ERROR_CLIPBOARD_NOT_OPEN,
+ "Last error should be set to ERROR_CLIPBOARD_NOT_OPEN, not %ld\n", GetLastError());
+ }
+
+ SetLastError(0xdeadbeef);
+ ok(!EmptyClipboard(), "EmptyClipboard should fail if clipboard wasn't open\n");
+ test_last_error(ERROR_CLIPBOARD_NOT_OPEN);
+}
+
+START_TEST(clipboard)
+{
+ SetLastError(0xdeadbeef);
+ FindAtomW(NULL);
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) is_win9x = TRUE;
+
+ test_RegisterClipboardFormatA();
+ test_ClipboardOwner();
+}
--- /dev/null
+/*
+ * Unit tests for DCE support
+ *
+ * Copyright 2005 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/test.h"
+
+#ifndef DCX_USESTYLE
+#define DCX_USESTYLE 0x00010000
+#endif
+
+static HWND hwnd_cache, hwnd_owndc, hwnd_classdc, hwnd_classdc2;
+
+/* test behavior of DC attributes with various GetDC/ReleaseDC combinations */
+static void test_dc_attributes(void)
+{
+ HDC hdc, old_hdc;
+ INT rop, def_rop;
+
+ /* test cache DC */
+
+ hdc = GetDC( hwnd_cache );
+ def_rop = GetROP2( hdc );
+
+ SetROP2( hdc, R2_WHITE );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+ ReleaseDC( hwnd_cache, hdc );
+ hdc = GetDC( hwnd_cache );
+ rop = GetROP2( hdc );
+ ok( rop == def_rop, "wrong ROP2 %d after release\n", rop );
+ SetROP2( hdc, R2_WHITE );
+ ReleaseDC( hwnd_cache, hdc );
+
+ hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
+ rop = GetROP2( hdc );
+ /* Win9x seems to silently ignore DCX_NORESETATTRS */
+ ok( rop == def_rop || rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+ SetROP2( hdc, R2_WHITE );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+ ReleaseDC( hwnd_cache, hdc );
+ hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
+ rop = GetROP2( hdc );
+ ok( rop == def_rop || rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
+ ReleaseDC( hwnd_cache, hdc );
+
+ hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE );
+ rop = GetROP2( hdc );
+ ok( rop == def_rop, "wrong ROP2 %d after release\n", rop );
+ ReleaseDC( hwnd_cache, hdc );
+
+ /* test own DC */
+
+ hdc = GetDC( hwnd_owndc );
+ SetROP2( hdc, R2_WHITE );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+ old_hdc = hdc;
+ ReleaseDC( hwnd_owndc, hdc );
+ hdc = GetDC( hwnd_owndc );
+ ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
+ ReleaseDC( hwnd_owndc, hdc );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop );
+
+ /* test class DC */
+
+ hdc = GetDC( hwnd_classdc );
+ SetROP2( hdc, R2_WHITE );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d\n", rop );
+
+ old_hdc = hdc;
+ ReleaseDC( hwnd_classdc, hdc );
+ hdc = GetDC( hwnd_classdc );
+ ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d after release\n", rop );
+ ReleaseDC( hwnd_classdc, hdc );
+ rop = GetROP2( hdc );
+ ok( rop == R2_WHITE, "wrong ROP2 %d after second release\n", rop );
+
+ /* test class DC with 2 windows */
+
+ old_hdc = GetDC( hwnd_classdc );
+ SetROP2( old_hdc, R2_BLACK );
+ hdc = GetDC( hwnd_classdc2 );
+ ok( old_hdc == hdc, "didn't get same DC %p/%p\n", old_hdc, hdc );
+ rop = GetROP2( hdc );
+ ok( rop == R2_BLACK, "wrong ROP2 %d for other window\n", rop );
+ ReleaseDC( hwnd_classdc, old_hdc );
+ ReleaseDC( hwnd_classdc, hdc );
+ rop = GetROP2( hdc );
+ ok( rop == R2_BLACK, "wrong ROP2 %d after release\n", rop );
+}
+
+
+/* test behavior with various invalid parameters */
+static void test_parameters(void)
+{
+ HDC hdc;
+
+ hdc = GetDC( hwnd_cache );
+ ok( ReleaseDC( hwnd_owndc, hdc ), "ReleaseDC with wrong window should succeed\n" );
+
+ hdc = GetDC( hwnd_cache );
+ ok( !ReleaseDC( hwnd_cache, 0 ), "ReleaseDC with wrong HDC should fail\n" );
+ ok( ReleaseDC( hwnd_cache, hdc ), "correct ReleaseDC should succeed\n" );
+ ok( !ReleaseDC( hwnd_cache, hdc ), "second ReleaseDC should fail\n" );
+
+ hdc = GetDC( hwnd_owndc );
+ ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" );
+ hdc = GetDC( hwnd_owndc );
+ ok( ReleaseDC( hwnd_owndc, hdc ), "correct ReleaseDC should succeed\n" );
+ ok( ReleaseDC( hwnd_owndc, hdc ), "second ReleaseDC should succeed\n" );
+
+ hdc = GetDC( hwnd_classdc );
+ ok( ReleaseDC( hwnd_cache, hdc ), "ReleaseDC with wrong window should succeed\n" );
+ hdc = GetDC( hwnd_classdc );
+ ok( ReleaseDC( hwnd_classdc, hdc ), "correct ReleaseDC should succeed\n" );
+ ok( ReleaseDC( hwnd_classdc, hdc ), "second ReleaseDC should succeed\n" );
+}
+
+
+static void test_dc_visrgn(void)
+{
+ HDC old_hdc, hdc;
+ HRGN hrgn, hrgn2;
+ RECT rect;
+
+ /* cache DC */
+
+ SetRect( &rect, 10, 10, 20, 20 );
+ MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 );
+ hrgn = CreateRectRgnIndirect( &rect );
+ hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ ReleaseDC( hwnd_cache, hdc );
+ ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+
+ /* cache DC with NORESETATTRS */
+
+ SetRect( &rect, 10, 10, 20, 20 );
+ MapWindowPoints( hwnd_cache, 0, (POINT *)&rect, 2 );
+ hrgn = CreateRectRgnIndirect( &rect );
+ hdc = GetDCEx( hwnd_cache, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE | DCX_NORESETATTRS );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ ReleaseDC( hwnd_cache, hdc );
+ ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+ hdc = GetDCEx( hwnd_cache, 0, DCX_USESTYLE | DCX_NORESETATTRS );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+ "clip box sould have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ReleaseDC( hwnd_cache, hdc );
+
+ /* window DC */
+
+ SetRect( &rect, 10, 10, 20, 20 );
+ MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 );
+ hrgn = CreateRectRgnIndirect( &rect );
+ hdc = GetDCEx( hwnd_owndc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ ReleaseDC( hwnd_owndc, hdc );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ hdc = GetDCEx( hwnd_owndc, 0, DCX_USESTYLE );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ ReleaseDC( hwnd_owndc, hdc );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+
+ SetRect( &rect, 20, 20, 30, 30 );
+ MapWindowPoints( hwnd_owndc, 0, (POINT *)&rect, 2 );
+ hrgn2 = CreateRectRgnIndirect( &rect );
+ hdc = GetDCEx( hwnd_owndc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE );
+ ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+ ReleaseDC( hwnd_owndc, hdc );
+ ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+ hdc = GetDCEx( hwnd_owndc, 0, DCX_EXCLUDERGN | DCX_USESTYLE );
+ ok( GetRgnBox( hrgn2, &rect ) == ERROR, "region must no longer be valid\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
+ "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ReleaseDC( hwnd_owndc, hdc );
+
+ /* class DC */
+
+ SetRect( &rect, 10, 10, 20, 20 );
+ MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 );
+ hrgn = CreateRectRgnIndirect( &rect );
+ hdc = GetDCEx( hwnd_classdc, hrgn, DCX_INTERSECTRGN | DCX_USESTYLE );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ ReleaseDC( hwnd_classdc, hdc );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+ hdc = GetDCEx( hwnd_classdc, 0, DCX_USESTYLE );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+ ReleaseDC( hwnd_classdc, hdc );
+ ok( GetRgnBox( hrgn, &rect ) != ERROR, "region must still be valid\n" );
+
+ SetRect( &rect, 20, 20, 30, 30 );
+ MapWindowPoints( hwnd_classdc, 0, (POINT *)&rect, 2 );
+ hrgn2 = CreateRectRgnIndirect( &rect );
+ hdc = GetDCEx( hwnd_classdc, hrgn2, DCX_INTERSECTRGN | DCX_USESTYLE );
+ ok( GetRgnBox( hrgn, &rect ) == ERROR, "region must no longer be valid\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+
+ old_hdc = hdc;
+ hdc = GetDCEx( hwnd_classdc2, 0, DCX_USESTYLE );
+ ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc );
+ ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
+ "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ReleaseDC( hwnd_classdc2, hdc );
+ ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+ hdc = GetDCEx( hwnd_classdc2, 0, DCX_EXCLUDERGN | DCX_USESTYLE );
+ ok( GetRgnBox( hrgn2, &rect ) != ERROR, "region2 must still be valid\n" );
+ ok( !(rect.left >= 20 && rect.top >= 20 && rect.right <= 30 && rect.bottom <= 30),
+ "clip box must have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ReleaseDC( hwnd_classdc2, hdc );
+}
+
+
+/* test various BeginPaint/EndPaint behaviors */
+static void test_begin_paint(void)
+{
+ HDC old_hdc, hdc;
+ RECT rect;
+ PAINTSTRUCT ps;
+
+ /* cache DC */
+
+ /* clear update region */
+ RedrawWindow( hwnd_cache, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+ SetRect( &rect, 10, 10, 20, 20 );
+ RedrawWindow( hwnd_cache, &rect, 0, RDW_INVALIDATE );
+ hdc = BeginPaint( hwnd_cache, &ps );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ EndPaint( hwnd_cache, &ps );
+
+ /* window DC */
+
+ RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+ SetRect( &rect, 10, 10, 20, 20 );
+ RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE );
+ hdc = BeginPaint( hwnd_owndc, &ps );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ReleaseDC( hwnd_owndc, hdc );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ EndPaint( hwnd_owndc, &ps );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+ "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ RedrawWindow( hwnd_owndc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+ SetRect( &rect, 10, 10, 20, 20 );
+ RedrawWindow( hwnd_owndc, &rect, 0, RDW_INVALIDATE|RDW_ERASE );
+ ok( GetDC( hwnd_owndc ) == hdc, "got different hdc\n" );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+ "clip box should be the whole window %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ RedrawWindow( hwnd_owndc, NULL, 0, RDW_ERASENOW );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+ "clip box should still be the whole window %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+ /* class DC */
+
+ RedrawWindow( hwnd_classdc, NULL, 0, RDW_VALIDATE|RDW_NOFRAME|RDW_NOERASE );
+ SetRect( &rect, 10, 10, 20, 20 );
+ RedrawWindow( hwnd_classdc, &rect, 0, RDW_INVALIDATE );
+ hdc = BeginPaint( hwnd_classdc, &ps );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20,
+ "invalid clip box %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+ old_hdc = hdc;
+ hdc = GetDC( hwnd_classdc2 );
+ ok( old_hdc == hdc, "did not get the same hdc %p/%p\n", old_hdc, hdc );
+ SetRectEmpty( &rect );
+ GetClipBox( hdc, &rect );
+ ok( !(rect.left >= 10 && rect.top >= 10 && rect.right <= 20 && rect.bottom <= 20),
+ "clip box should have been reset %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+}
+
+
+START_TEST(dce)
+{
+ WNDCLASSA cls;
+
+ cls.style = CS_DBLCLKS;
+ cls.lpfnWndProc = DefWindowProcA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "cache_class";
+ RegisterClassA(&cls);
+ cls.style = CS_DBLCLKS | CS_OWNDC;
+ cls.lpszClassName = "owndc_class";
+ RegisterClassA(&cls);
+ cls.style = CS_DBLCLKS | CS_CLASSDC;
+ cls.lpszClassName = "classdc_class";
+ RegisterClassA(&cls);
+
+ hwnd_cache = CreateWindowA("cache_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+ 0, 0, 100, 100,
+ 0, 0, GetModuleHandleA(0), NULL );
+ hwnd_owndc = CreateWindowA("owndc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+ 0, 200, 100, 100,
+ 0, 0, GetModuleHandleA(0), NULL );
+ hwnd_classdc = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+ 200, 0, 100, 100,
+ 0, 0, GetModuleHandleA(0), NULL );
+ hwnd_classdc2 = CreateWindowA("classdc_class", NULL, WS_OVERLAPPED | WS_VISIBLE,
+ 200, 200, 100, 100,
+ 0, 0, GetModuleHandleA(0), NULL );
+ test_dc_attributes();
+ test_parameters();
+ test_dc_visrgn();
+ test_begin_paint();
+}
--- /dev/null
+/*
+ * Unit tests for DDE functions
+ *
+ * Copyright (c) 2004 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+
+#include "wine/test.h"
+#include "windows.h"
+
+static const WCHAR TEST_DDE_SERVICE[] = {'T','e','s','t','D','D','E','S','e','r','v','i','c','e',0};
+
+static const char exec_cmdA[] = "ANSI dde command";
+static const WCHAR exec_cmdW[] = {'u','n','i','c','o','d','e',' ','d','d','e',' ','c','o','m','m','a','n','d',0};
+
+static WNDPROC old_dde_client_wndproc;
+
+LRESULT WINAPI hook_dde_client_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ UINT_PTR lo, hi;
+
+ trace("hook_dde_client_wndproc: %p %04x %08x %08lx\n", hwnd, msg, wparam, lparam);
+
+ switch (msg)
+ {
+ case WM_DDE_ACK:
+ UnpackDDElParam(WM_DDE_ACK, lparam, &lo, &hi);
+ trace("WM_DDE_ACK: status %04x hglobal %p\n", lo, (HGLOBAL)hi);
+ break;
+
+ default:
+ break;
+ }
+ return CallWindowProcA(old_dde_client_wndproc, hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI dde_server_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ trace("dde_server_wndproc: %p %04x %08x %08lx\n", hwnd, msg, wparam, lparam);
+
+ switch (msg)
+ {
+ case WM_DDE_INITIATE:
+ {
+ ATOM aService = GlobalAddAtomW(TEST_DDE_SERVICE);
+
+ trace("server: got WM_DDE_INITIATE from %p with %08lx\n", (HWND)wparam, lparam);
+
+ if (LOWORD(lparam) == aService)
+ {
+ ok(!IsWindowUnicode((HWND)wparam), "client should be an ANSI window\n");
+ old_dde_client_wndproc = (WNDPROC)SetWindowLongPtrA((HWND)wparam, GWLP_WNDPROC, (ULONG_PTR)hook_dde_client_wndproc);
+ trace("server: sending WM_DDE_ACK to %p\n", (HWND)wparam);
+ SendMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, MAKELPARAM(aService, 0));
+ }
+ else
+ GlobalDeleteAtom(aService);
+ return 0;
+ }
+
+ case WM_DDE_EXECUTE:
+ {
+ DDEACK ack;
+ WORD status;
+ LPCSTR cmd;
+ UINT_PTR lo, hi;
+
+ trace("server: got WM_DDE_EXECUTE from %p with %08lx\n", (HWND)wparam, lparam);
+
+ UnpackDDElParam(WM_DDE_EXECUTE, lparam, &lo, &hi);
+ trace("%08lx => lo %04x hi %04x\n", lparam, lo, hi);
+
+ ack.bAppReturnCode = 0;
+ ack.reserved = 0;
+ ack.fBusy = 0;
+
+ cmd = GlobalLock((HGLOBAL)hi);
+
+ if (!cmd || (lstrcmpW((LPCWSTR)cmd, exec_cmdW) && lstrcmpA(cmd, exec_cmdA)))
+ {
+ trace("ignoring unknown WM_DDE_EXECUTE command\n");
+ /* We have to send a negative acknowledge even if we don't
+ * accept the command, otherwise Windows goes mad and next time
+ * we send an acknowledge DDEML drops the connection.
+ * Not sure how to call it: a bug or a feature.
+ */
+ ack.fAck = 0;
+ }
+ else
+ ack.fAck = 1;
+ GlobalUnlock((HGLOBAL)hi);
+
+ trace("server: posting %s WM_DDE_ACK to %p\n", ack.fAck ? "POSITIVE" : "NEGATIVE", (HWND)wparam);
+
+ status = *((WORD *)&ack);
+ lparam = ReuseDDElParam(lparam, WM_DDE_EXECUTE, WM_DDE_ACK, status, hi);
+
+ PostMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, lparam);
+ return 0;
+ }
+
+ case WM_DDE_TERMINATE:
+ {
+ DDEACK ack;
+ WORD status;
+
+ trace("server: got WM_DDE_TERMINATE from %p with %08lx\n", (HWND)wparam, lparam);
+
+ ack.bAppReturnCode = 0;
+ ack.reserved = 0;
+ ack.fBusy = 0;
+ ack.fAck = 1;
+
+ trace("server: posting %s WM_DDE_ACK to %p\n", ack.fAck ? "POSITIVE" : "NEGATIVE", (HWND)wparam);
+
+ status = *((WORD *)&ack);
+ lparam = PackDDElParam(WM_DDE_ACK, status, 0);
+
+ PostMessageW((HWND)wparam, WM_DDE_ACK, (WPARAM)hwnd, lparam);
+ return 0;
+ }
+
+ default:
+ break;
+ }
+
+ return DefWindowProcW(hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI dde_client_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static BOOL create_dde_windows(HWND *hwnd_client, HWND *hwnd_server)
+{
+ WNDCLASSA wcA;
+ WNDCLASSW wcW;
+ static const WCHAR server_class_name[] = {'d','d','e','_','s','e','r','v','e','r','_','w','i','n','d','o','w',0};
+ static const char client_class_name[] = "dde_client_window";
+
+ memset(&wcW, 0, sizeof(wcW));
+ wcW.lpfnWndProc = dde_server_wndproc;
+ wcW.lpszClassName = server_class_name;
+ wcW.hInstance = GetModuleHandleA(0);
+ if (!RegisterClassW(&wcW)) return FALSE;
+
+ memset(&wcA, 0, sizeof(wcA));
+ wcA.lpfnWndProc = dde_client_wndproc;
+ wcA.lpszClassName = client_class_name;
+ wcA.hInstance = GetModuleHandleA(0);
+ assert(RegisterClassA(&wcA));
+
+ *hwnd_server = CreateWindowExW(0, server_class_name, NULL,
+ WS_POPUP,
+ 100, 100, CW_USEDEFAULT, CW_USEDEFAULT,
+ GetDesktopWindow(), 0,
+ GetModuleHandleA(0), NULL);
+ assert(*hwnd_server);
+
+ *hwnd_client = CreateWindowExA(0, client_class_name, NULL,
+ WS_POPUP,
+ 100, 100, CW_USEDEFAULT, CW_USEDEFAULT,
+ GetDesktopWindow(), 0,
+ GetModuleHandleA(0), NULL);
+ assert(*hwnd_client);
+
+ trace("server hwnd %p, client hwnd %p\n", *hwnd_server, *hwnd_client);
+
+ ok(IsWindowUnicode(*hwnd_server), "server has to be a unicode window\n");
+ ok(!IsWindowUnicode(*hwnd_client), "client has to be an ANSI window\n");
+
+ return TRUE;
+}
+
+static HDDEDATA CALLBACK client_dde_callback(UINT uType, UINT uFmt, HCONV hconv,
+ HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+ ULONG_PTR dwData1, ULONG_PTR dwData2)
+{
+ static const char * const cmd_type[15] = {
+ "XTYP_ERROR", "XTYP_ADVDATA", "XTYP_ADVREQ", "XTYP_ADVSTART",
+ "XTYP_ADVSTOP", "XTYP_EXECUTE", "XTYP_CONNECT", "XTYP_CONNECT_CONFIRM",
+ "XTYP_XACT_COMPLETE", "XTYP_POKE", "XTYP_REGISTER", "XTYP_REQUEST",
+ "XTYP_DISCONNECT", "XTYP_UNREGISTER", "XTYP_WILDCONNECT" };
+ UINT type;
+ const char *cmd_name;
+
+ type = (uType & XTYP_MASK) >> XTYP_SHIFT;
+ cmd_name = (type >= 0 && type <= 14) ? cmd_type[type] : "unknown";
+
+ trace("client_dde_callback: %04x (%s) %d %p %p %p %p %08lx %08lx\n",
+ uType, cmd_name, uFmt, hconv, hsz1, hsz2, hdata, dwData1, dwData2);
+ return 0;
+}
+
+static void test_dde_transaction(void)
+{
+ HSZ hsz_server;
+ DWORD dde_inst, ret, err;
+ HCONV hconv;
+ HWND hwnd_client, hwnd_server;
+ CONVINFO info;
+ HDDEDATA hdata;
+ static const char test_cmd[] = "test dde command";
+
+ /* server: unicode, client: ansi */
+ if (!create_dde_windows(&hwnd_client, &hwnd_server)) return;
+
+ dde_inst = 0;
+ ret = DdeInitializeA(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0);
+ ok(ret == DMLERR_NO_ERROR, "DdeInitializeW failed with error %04lx (%x)\n",
+ ret, DdeGetLastError(dde_inst));
+
+ hsz_server = DdeCreateStringHandleW(dde_inst, TEST_DDE_SERVICE, CP_WINUNICODE);
+
+ hconv = DdeConnect(dde_inst, hsz_server, 0, NULL);
+ ok(hconv != 0, "DdeConnect error %x\n", DdeGetLastError(dde_inst));
+ err = DdeGetLastError(dde_inst);
+ ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err);
+
+ info.cb = sizeof(info);
+ ret = DdeQueryConvInfo(hconv, QID_SYNC, &info);
+ ok(ret, "wrong info size %ld, DdeQueryConvInfo error %x\n", ret, DdeGetLastError(dde_inst));
+ /* should be CP_WINANSI since we used DdeInitializeA */
+ ok(info.ConvCtxt.iCodePage == CP_WINANSI, "wrong iCodePage %d\n", info.ConvCtxt.iCodePage);
+ ok(!info.hConvPartner, "unexpected info.hConvPartner: %p\n", info.hConvPartner);
+todo_wine {
+ ok((info.wStatus & DDE_FACK), "unexpected info.wStatus: %04x\n", info.wStatus);
+}
+ ok((info.wStatus & (ST_CONNECTED | ST_CLIENT)) == (ST_CONNECTED | ST_CLIENT), "unexpected info.wStatus: %04x\n", info.wStatus);
+ ok(info.wConvst == XST_CONNECTED, "unexpected info.wConvst: %04x\n", info.wConvst);
+ ok(info.wType == 0, "unexpected info.wType: %04x\n", info.wType);
+
+ trace("hwnd %p, hwndPartner %p\n", info.hwnd, info.hwndPartner);
+
+ trace("sending test client transaction command\n");
+ ret = 0xdeadbeef;
+ hdata = DdeClientTransaction((LPBYTE)test_cmd, strlen(test_cmd) + 1, hconv, (HSZ)0xdead, 0xbeef, XTYP_EXECUTE, 1000, &ret);
+ ok(!hdata, "DdeClientTransaction succeeded\n");
+ ok(ret == DDE_FNOTPROCESSED, "wrong status code %04lx\n", ret);
+ err = DdeGetLastError(dde_inst);
+ ok(err == DMLERR_NOTPROCESSED, "wrong dde error %lx\n", err);
+
+ trace("sending ANSI client transaction command\n");
+ ret = 0xdeadbeef;
+ hdata = DdeClientTransaction((LPBYTE)exec_cmdA, lstrlenA(exec_cmdA) + 1, hconv, 0, 0, XTYP_EXECUTE, 1000, &ret);
+ ok(hdata != 0, "DdeClientTransaction returned %p, error %x\n", hdata, DdeGetLastError(dde_inst));
+ ok(ret == DDE_FACK, "wrong status code %04lx\n", ret);
+
+ err = DdeGetLastError(dde_inst);
+ ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err);
+
+ trace("sending unicode client transaction command\n");
+ ret = 0xdeadbeef;
+ hdata = DdeClientTransaction((LPBYTE)exec_cmdW, (lstrlenW(exec_cmdW) + 1) * sizeof(WCHAR), hconv, 0, 0, XTYP_EXECUTE, 1000, &ret);
+ ok(hdata != 0, "DdeClientTransaction returned %p, error %x\n", hdata, DdeGetLastError(dde_inst));
+ ok(ret == DDE_FACK, "wrong status code %04lx\n", ret);
+ err = DdeGetLastError(dde_inst);
+ ok(err == DMLERR_NO_ERROR, "wrong dde error %lx\n", err);
+
+ ok(DdeDisconnect(hconv), "DdeDisconnect error %x\n", DdeGetLastError(dde_inst));
+
+ info.cb = sizeof(info);
+ ret = DdeQueryConvInfo(hconv, QID_SYNC, &info);
+ ok(!ret, "DdeQueryConvInfo should fail\n");
+ err = DdeGetLastError(dde_inst);
+todo_wine {
+ ok(err == DMLERR_INVALIDPARAMETER, "wrong dde error %lx\n", err);
+}
+
+ ok(DdeFreeStringHandle(dde_inst, hsz_server), "DdeFreeStringHandle error %x\n", DdeGetLastError(dde_inst));
+
+ /* This call hangs on win2k SP4 and XP SP1.
+ DdeUninitialize(dde_inst);*/
+
+ DestroyWindow(hwnd_client);
+ DestroyWindow(hwnd_server);
+}
+
+static void test_DdeCreateStringHandleW(DWORD dde_inst, int codepage)
+{
+ static const WCHAR dde_string[] = {'D','D','E',' ','S','t','r','i','n','g',0};
+ HSZ str_handle;
+ WCHAR bufW[256];
+ char buf[256];
+ int ret;
+
+ str_handle = DdeCreateStringHandleW(dde_inst, dde_string, codepage);
+ ok(str_handle != 0, "DdeCreateStringHandleW failed with error %08x\n",
+ DdeGetLastError(dde_inst));
+
+ ret = DdeQueryStringW(dde_inst, str_handle, NULL, 0, codepage);
+ if (codepage == CP_WINANSI)
+ ok(ret == 1, "DdeQueryStringW returned wrong length %d\n", ret);
+ else
+ ok(ret == lstrlenW(dde_string), "DdeQueryStringW returned wrong length %d\n", ret);
+
+ ret = DdeQueryStringW(dde_inst, str_handle, bufW, 256, codepage);
+ if (codepage == CP_WINANSI)
+ {
+ ok(ret == 1, "DdeQueryStringW returned wrong length %d\n", ret);
+ ok(!lstrcmpA("D", (LPCSTR)bufW), "DdeQueryStringW returned wrong string\n");
+ }
+ else
+ {
+ ok(ret == lstrlenW(dde_string), "DdeQueryStringW returned wrong length %d\n", ret);
+ ok(!lstrcmpW(dde_string, bufW), "DdeQueryStringW returned wrong string\n");
+ }
+
+ ret = DdeQueryStringA(dde_inst, str_handle, buf, 256, CP_WINANSI);
+ if (codepage == CP_WINANSI)
+ {
+ ok(ret == 1, "DdeQueryStringA returned wrong length %d\n", ret);
+ ok(!lstrcmpA("D", buf), "DdeQueryStringW returned wrong string\n");
+ }
+ else
+ {
+ ok(ret == lstrlenA("DDE String"), "DdeQueryStringA returned wrong length %d\n", ret);
+ ok(!lstrcmpA("DDE String", buf), "DdeQueryStringA returned wrong string %s\n", buf);
+ }
+
+ ret = DdeQueryStringA(dde_inst, str_handle, buf, 256, CP_WINUNICODE);
+ if (codepage == CP_WINANSI)
+ {
+ ok(ret == 1, "DdeQueryStringA returned wrong length %d\n", ret);
+ ok(!lstrcmpA("D", buf), "DdeQueryStringA returned wrong string %s\n", buf);
+ }
+ else
+ {
+ ok(ret == lstrlenA("DDE String"), "DdeQueryStringA returned wrong length %d\n", ret);
+ ok(!lstrcmpW(dde_string, (LPCWSTR)buf), "DdeQueryStringW returned wrong string\n");
+ }
+
+ ok(DdeFreeStringHandle(dde_inst, str_handle), "DdeFreeStringHandle failed\n");
+}
+
+static void test_DdeCreateStringHandle(void)
+{
+ DWORD dde_inst, ret;
+
+ dde_inst = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ ret = DdeInitializeW(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0);
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ trace("Skipping the DDE test on a Win9x platform\n");
+ return;
+ }
+
+ ok(ret == DMLERR_INVALIDPARAMETER, "DdeInitializeW should fail, but got %04lx instead\n", ret);
+ ok(DdeGetLastError(dde_inst) == DMLERR_INVALIDPARAMETER, "expected DMLERR_INVALIDPARAMETER\n");
+
+ dde_inst = 0;
+ ret = DdeInitializeW(&dde_inst, client_dde_callback, APPCMD_CLIENTONLY, 0);
+ ok(ret == DMLERR_NO_ERROR, "DdeInitializeW failed with error %04lx (%08x)\n",
+ ret, DdeGetLastError(dde_inst));
+
+ test_DdeCreateStringHandleW(dde_inst, 0);
+ test_DdeCreateStringHandleW(dde_inst, CP_WINUNICODE);
+ test_DdeCreateStringHandleW(dde_inst, CP_WINANSI);
+
+ ok(DdeUninitialize(dde_inst), "DdeUninitialize failed\n");
+}
+
+START_TEST(dde)
+{
+ test_DdeCreateStringHandle();
+ test_dde_transaction();
+}
--- /dev/null
+/* Unit test suite for the dialog functions.
+ *
+ * Copyright 2004 Bill Medland
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ *
+ * This test suite currently works by building a quite complex hierarchy of
+ * objects in a variety of styles and then performs a limited number of tests
+ * for the previous and next dialog group or tab items.
+ *
+ * The test specifically does not test all possibilities at this time since
+ * there are several cases where the Windows behaviour is rather strange and
+ * significant work would be required to get the Wine code to duplicate the
+ * strangeness, especially since most are in situations that would not
+ * normally be met.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windows.h"
+
+#define MAXHWNDS 1024
+static HWND hwnd [MAXHWNDS];
+static unsigned int numwnds=1; /* 0 is reserved for null */
+
+/* Global handles */
+static HINSTANCE g_hinst; /* This application's HINSTANCE */
+static HWND g_hwndMain, g_hwndButton1, g_hwndButton2, g_hwndButtonCancel;
+static HWND g_hwndTestDlg, g_hwndTestDlgBut1, g_hwndTestDlgBut2, g_hwndTestDlgEdit;
+static HWND g_hwndInitialFocusT1, g_hwndInitialFocusT2, g_hwndInitialFocusGroupBox;
+
+static LONG g_styleInitialFocusT1, g_styleInitialFocusT2;
+static BOOL g_bInitialFocusInitDlgResult;
+
+static int g_terminated;
+
+typedef struct {
+ unsigned int id;
+ int parent;
+ DWORD style;
+ DWORD exstyle;
+} h_entry;
+
+static const h_entry hierarchy [] = {
+ /* 0 is reserved for the null window */
+ { 1, 0, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS, WS_EX_WINDOWEDGE},
+ { 20, 1, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 2, 1, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+ { 60, 2, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ /* What happens with groups when the parent is disabled */
+ { 8, 2, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, WS_EX_CONTROLPARENT},
+ { 85, 8, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_GROUP, 0},
+ { 9, 8, WS_CHILD, WS_EX_CONTROLPARENT},
+ { 86, 9, WS_CHILD | WS_VISIBLE, 0},
+ { 87, 9, WS_CHILD | WS_VISIBLE, 0},
+ { 31, 8, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 10, 2, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+ { 88, 10, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 11, 10, WS_CHILD, WS_EX_CONTROLPARENT},
+ { 89, 11, WS_CHILD | WS_VISIBLE, 0},
+ { 32, 11, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 90, 11, WS_CHILD | WS_VISIBLE, 0},
+ { 33, 10, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 21, 2, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 61, 2, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ { 3, 1, WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
+ { 22, 3, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 62, 3, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ { 7, 3, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+ { 4, 7, WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
+ { 83, 4, WS_CHILD | WS_VISIBLE, 0},
+ { 5, 4, WS_CHILD | WS_VISIBLE | DS_CONTROL, 0},
+ /* A couple of controls around the main dialog */
+ { 29, 5, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 81, 5, WS_CHILD | WS_VISIBLE, 0},
+ /* The main dialog with lots of controls */
+ { 6, 5, WS_CHILD | WS_VISIBLE, WS_EX_CONTROLPARENT},
+ /* At the start of a dialog */
+ /* Disabled controls are skipped */
+ { 63, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+ /* Invisible controls are skipped */
+ { 64, 6, WS_CHILD | WS_TABSTOP, 0},
+ /* Invisible disabled controls are skipped */
+ { 65, 6, WS_CHILD | WS_DISABLED | WS_TABSTOP, 0},
+ /* Non-tabstop controls are skipped for tabs but not for groups */
+ { 66, 6, WS_CHILD | WS_VISIBLE, 0},
+ /* End of first group, with no tabstops in it */
+ { 23, 6, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ /* At last a tabstop */
+ { 67, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ /* A group that is totally disabled or invisible */
+ { 24, 6, WS_CHILD | WS_DISABLED | WS_GROUP, 0},
+ { 68, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+ { 69, 6, WS_CHILD | WS_TABSTOP, 0},
+ /* A valid group in the middle of the dialog (not the first nor last group*/
+ { 25, 6, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ /* A non-tabstop item will be skipped for tabs */
+ { 70, 6, WS_CHILD | WS_VISIBLE, 0},
+ /* A disabled item will be skipped for tabs and groups */
+ { 71, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+ /* A valid item will be found for tabs and groups */
+ { 72, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ /* A disabled item to skip when looking for the next group item */
+ { 73, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+ /* The next group begins with an enabled visible label */
+ { 26, 6, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ { 74, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ { 75, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ /* That group is terminated by a disabled label */
+ { 27, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_GROUP, 0},
+ { 76, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ { 77, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ /* That group is terminated by an invisible label */
+ { 28, 6, WS_CHILD | WS_GROUP, 0},
+ /* The end of the dialog with item for loop and recursion testing */
+ { 78, 6, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ /* No tabstop so skipped for prev tab, but found for prev group */
+ { 79, 6, WS_CHILD | WS_VISIBLE, 0},
+ { 80, 6, WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_TABSTOP, 0},
+ /* A couple of controls after the main dialog */
+ { 82, 5, WS_CHILD | WS_VISIBLE, 0},
+ { 30, 5, WS_CHILD | WS_VISIBLE | WS_GROUP, 0},
+ /* And around them */
+ { 84, 4, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0},
+ {0, 0, 0, 0}
+};
+
+static BOOL CreateWindows (HINSTANCE hinst)
+{
+ const h_entry *p = hierarchy;
+
+ while (p->id != 0)
+ {
+ DWORD style, exstyle;
+ char ctrlname[9];
+
+ /* Basically assert that the hierarchy is valid and track the
+ * maximum control number
+ */
+ if (p->id >= numwnds)
+ {
+ if (p->id >= sizeof(hwnd)/sizeof(hwnd[0]))
+ {
+ trace ("Control %d is out of range\n", p->id);
+ return FALSE;
+ }
+ else
+ numwnds = p->id+1;
+ }
+ if (p->id <= 0)
+ {
+ trace ("Control %d is out of range\n", p->id);
+ return FALSE;
+ }
+ if (hwnd[p->id] != 0)
+ {
+ trace ("Control %d is used more than once\n", p->id);
+ return FALSE;
+ }
+
+ /* Create the control */
+ sprintf (ctrlname, "ctrl%4.4d", p->id);
+ hwnd[p->id] = CreateWindowEx (p->exstyle, TEXT(p->parent ? "static" : "GetNextDlgItemWindowClass"), TEXT(ctrlname), p->style, 10, 10, 10, 10, hwnd[p->parent], p->parent ? (HMENU) (2000 + p->id) : 0, hinst, 0);
+ if (!hwnd[p->id])
+ {
+ trace ("Failed to create control %d\n", p->id);
+ return FALSE;
+ }
+
+ /* Check that the styles are as we specified (except the main one
+ * which is quite frequently messed up). If this keeps breaking then
+ * we could mask out the bits that don't concern us.
+ */
+ if (p->parent)
+ {
+ style = GetWindowLong (hwnd[p->id], GWL_STYLE);
+ exstyle = GetWindowLong (hwnd[p->id], GWL_EXSTYLE);
+ if (style == p->style && exstyle == p->exstyle)
+ {
+ trace ("Style mismatch at %d: %8.8lx %8.8lx cf %8.8lx %8.8lx\n", p->id, style, exstyle, p->style, p->exstyle);
+ }
+ }
+ p++;
+ }
+
+ return TRUE;
+}
+
+/* Form the lParam of a WM_KEYDOWN message */
+static DWORD KeyDownData (int repeat, int scancode, int extended, int wasdown)
+{
+ return ((repeat & 0x0000FFFF) | ((scancode & 0x00FF) >> 16) |
+ (extended ? 0x01000000 : 0) | (wasdown ? 0x40000000 : 0));
+}
+
+/* Form a WM_KEYDOWN VK_TAB message to the specified window */
+static void FormTabMsg (MSG *pMsg, HWND hwnd)
+{
+ pMsg->hwnd = hwnd;
+ pMsg->message = WM_KEYDOWN;
+ pMsg->wParam = VK_TAB;
+ pMsg->lParam = KeyDownData (1, 0x0F, 0, 0);
+ /* pMsg->time is not set. It shouldn't be needed */
+ /* pMsg->pt is ignored */
+}
+
+/* Form a WM_KEYDOWN VK_RETURN message to the specified window */
+static void FormEnterMsg (MSG *pMsg, HWND hwnd)
+{
+ pMsg->hwnd = hwnd;
+ pMsg->message = WM_KEYDOWN;
+ pMsg->wParam = VK_RETURN;
+ pMsg->lParam = KeyDownData (1, 0x1C, 0, 0);
+ /* pMsg->time is not set. It shouldn't be needed */
+ /* pMsg->pt is ignored */
+}
+
+/***********************************************************************
+ *
+ * The actual tests
+ */
+
+typedef struct
+{
+ int isok; /* or is it todo */
+ int test;
+ int dlg;
+ int ctl;
+ int tab;
+ int prev;
+ int res;
+} test_record;
+
+static int id (HWND h)
+{
+ unsigned int i;
+ for (i = 0; i < numwnds; i++)
+ if (hwnd[i] == h)
+ return i;
+ return -1;
+}
+
+/* Tests
+ *
+ * Tests 1-8 test the hCtl argument of null or the dialog itself.
+ *
+ * 1. Prev Group of null is null
+ * 2. Prev Tab of null is null
+ * 3. Prev Group of hDlg in hDlg is null
+ * 4. Prev Tab of hDlg in hDlg is null
+ * 5. Next Group of null is first visible enabled child
+ * Check it skips invisible, diabled and both.
+ * 6. Next Tab of null is first visible enabled tabstop
+ * Check it skips invisible, disabled, nontabstop, and in combination.
+ * 7. Next Group of hDlg in hDlg is as of null
+ * 8. Next Tab of hDlg in hDlg is as of null
+ *
+ * Tests 9-14 test descent
+ *
+ * 9. DS_CONTROL does not result in descending the hierarchy for Tab Next
+ * 10. DS_CONTROL does not result in descending the hierarchy for Group Next
+ * 11. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Next
+ * 12. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Next
+ * 13. WS_EX_CONTROLPARENT results in descending the hierarchy for Tab Prev
+ * 14. WS_EX_CONTROLPARENT results in descending the hierarchy for Group Prev
+ *
+ * Tests 15-24 are the basic Prev/Next Group tests
+ *
+ * 15. Next Group of a visible enabled non-group control is the next visible
+ * enabled non-group control, if there is one before the next group
+ * 16. Next Group of a visible enabled non-group control wraps around to the
+ * beginning of the group on finding a control that starts another group.
+ * Note that the group is in the middle of the dialog.
+ * 17. As 16 except note that the next group is started with a disabled
+ * visible control.
+ * 18. As 16 except note that the next group is started with an invisible
+ * enabled control.
+ * 19. Next Group wraps around the controls of the dialog
+ * 20. Next Group is the same even if the initial control is disabled.
+ * 21. Next Group is the same even if the initial control is invisible.
+ * 22. Next Group is the same even if the initial control has the group style
+ * 23. Next Group returns the initial control if there is no visible enabled
+ * control in the group. (Initial control disabled and not group style).
+ * 24. Prev version of test 16.
+ * Prev Group of a visible enabled non-group control wraps around to the
+ * beginning of the group on finding a control that starts the group.
+ * Note that the group is in the middle of the dialog.
+ *
+ * In tests 25 to 28 the control is sitting under dialogs which do not have
+ * the WS_EX_CONTROLPARENT style and so cannot be reached from the top of
+ * the dialog.
+ *
+ * 25. Next Group of an inaccessible control is as if it were accessible
+ * 26. Prev Group of an inaccessible control begins searching at the highest
+ * level ancestor that did not permit recursion down the hierarchy
+ * 27. Next Tab of an inaccessible control is as if it were accessible
+ * 28. Prev Tab of an inaccessible control begins searching at the highest
+ * level ancestor that did not permit recursion down the hierarchy.
+ *
+ * Tests 29- are the basic Tab tests
+ *
+ * 29. Next Tab of a control is the next visible enabled control with the
+ * Tabstop style (N.B. skips disabled, invisible and non-tabstop)
+ * 30. Prev Tab of a control is the previous visible enabled control with the
+ * Tabstop style (N.B. skips disabled, invisible and non-tabstop)
+ * 31. Next Tab test with at least two layers of descent and finding the
+ * result not at the first control.
+ * 32. Next Tab test with at least two layers of descent with the descent and
+ * control at the start of each level.
+ * 33. Prev Tab test with at least two layers of descent and finding the
+ * result not at the last control.
+ * 34. Prev Tab test with at least two layers of descent with the descent and
+ * control at the end of each level.
+ *
+ * 35. Passing NULL may result in the first child being the one returned.
+ * (group test)
+ * 36. Passing NULL may result in the first child being the one returned.
+ * (tab test)
+ */
+
+static void GetNextDlgItemTest (void)
+{
+ static test_record test [] =
+ {
+ /* isok test dlg ctl tab prev res */
+
+ { 1, 1, 6, 0, 0, 1, 0},
+ { 1, 2, 6, 0, 1, 1, 0},
+ { 1, 3, 6, 6, 0, 1, 0},
+ { 1, 4, 6, 6, 1, 1, 0},
+ { 1, 5, 6, 0, 0, 0, 66},
+ { 1, 6, 6, 0, 1, 0, 67},
+ { 1, 7, 6, 6, 0, 0, 66},
+ { 1, 8, 6, 6, 1, 0, 67},
+
+ { 1, 9, 4, 83, 1, 0, 84},
+ { 1, 10, 4, 83, 0, 0, 5},
+ { 1, 11, 5, 81, 1, 0, 67},
+ { 1, 12, 5, 81, 0, 0, 66},
+ { 1, 13, 5, 82, 1, 1, 78},
+
+ { 1, 14, 5, 82, 0, 1, 79},
+ { 1, 15, 6, 70, 0, 0, 72},
+ { 1, 16, 6, 72, 0, 0, 25},
+ { 1, 17, 6, 75, 0, 0, 26},
+ { 1, 18, 6, 77, 0, 0, 76},
+ { 1, 19, 6, 79, 0, 0, 66},
+ { 1, 20, 6, 71, 0, 0, 72},
+ { 1, 21, 6, 64, 0, 0, 66},
+
+ { 1, 22, 6, 25, 0, 0, 70},
+ { 1, 23, 6, 68, 0, 0, 68},
+ { 1, 24, 6, 25, 0, 1, 72},
+ { 1, 25, 1, 70, 0, 0, 72},
+ /*{ 0, 26, 1, 70, 0, 1, 3}, Crashes Win95*/
+ { 1, 27, 1, 70, 1, 0, 72},
+ /*{ 0, 28, 1, 70, 1, 1, 61}, Crashes Win95*/
+
+ { 1, 29, 6, 67, 1, 0, 72},
+ { 1, 30, 6, 72, 1, 1, 67},
+
+ { 1, 35, 2, 0, 0, 0, 60},
+ { 1, 36, 2, 0, 1, 0, 60},
+
+ { 0, 0, 0, 0, 0, 0, 0} /* End of test */
+ };
+ const test_record *p = test;
+
+ ok (CreateWindows (g_hinst), "Could not create test windows\n");
+
+ while (p->dlg)
+ {
+ HWND a;
+ a = (p->tab ? GetNextDlgTabItem : GetNextDlgGroupItem) (hwnd[p->dlg], hwnd[p->ctl], p->prev);
+ if (p->isok)
+ {
+ ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was %d instead of %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res);
+ }
+ else
+ {
+ todo_wine
+ {
+ ok (a == hwnd[p->res], "Test %d: %s %s item of %d in %d was actually %d matching expected %d\n", p->test, p->prev ? "Prev" : "Next", p->tab ? "Tab" : "Group", p->ctl, p->dlg, id(a), p->res);
+ }
+ }
+ p++;
+ }
+}
+
+/*
+ * OnMainWindowCreate
+ */
+static BOOL OnMainWindowCreate (HWND hwnd, LPCREATESTRUCT lpcs)
+{
+ g_hwndButton1 = CreateWindow (TEXT("button"), TEXT("Button &1"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | BS_TEXT,
+ 10, 10, 80, 80, hwnd, (HMENU)100, g_hinst, 0);
+ if (!g_hwndButton1) return FALSE;
+
+ g_hwndButton2 = CreateWindow (TEXT("button"), TEXT("Button &2"),
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_TEXT,
+ 110, 10, 80, 80, hwnd, (HMENU)200, g_hinst, 0);
+ if (!g_hwndButton2) return FALSE;
+
+ g_hwndButtonCancel = CreateWindow (TEXT("button"), TEXT("Cancel"),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
+ 210, 10, 80, 80, hwnd, (HMENU)IDCANCEL, g_hinst, 0);
+ if (!g_hwndButtonCancel) return FALSE;
+
+ return TRUE;
+}
+
+
+/*
+ * OnTestDlgCreate
+ */
+
+static BOOL OnTestDlgCreate (HWND hwnd, LPCREATESTRUCT lpcs)
+{
+ g_hwndTestDlgEdit = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING |
+ WS_EX_RIGHTSCROLLBAR | WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE,
+ TEXT("Edit"), TEXT("Edit"),
+ WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | ES_LEFT | ES_AUTOHSCROLL,
+ 16,33,184,24, hwnd, (HMENU)101, g_hinst, 0);
+ if (!g_hwndTestDlgEdit) return FALSE;
+
+ g_hwndTestDlgBut1 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
+ | WS_EX_NOPARENTNOTIFY,
+ TEXT("button"), TEXT("Button &1"),
+ WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
+ 204,33,30,24, hwnd, (HMENU)201, g_hinst, 0);
+ if (!g_hwndTestDlgBut1) return FALSE;
+
+ g_hwndTestDlgBut2 = CreateWindowEx ( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
+ | WS_EX_NOPARENTNOTIFY, TEXT("button"),
+ TEXT("Button &2"),
+ WS_CHILDWINDOW | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON | BS_TEXT,
+ 90,102,80,24, hwnd, (HMENU)IDCANCEL, g_hinst, 0);
+ if (!g_hwndTestDlgBut2) return FALSE;
+
+ return TRUE;
+}
+
+static LRESULT CALLBACK main_window_procA (HWND hwnd, UINT uiMsg, WPARAM wParam,
+ LPARAM lParam)
+{
+ LRESULT result;
+ switch (uiMsg)
+ {
+ /* Add blank case statements for these to ensure we don't use them
+ * by mistake.
+ */
+ case DM_GETDEFID: break;
+ case DM_SETDEFID: break;
+
+ case WM_CREATE:
+ return (OnMainWindowCreate (hwnd,
+ (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1);
+ case WM_COMMAND:
+ if (wParam == IDCANCEL)
+ {
+ g_terminated = TRUE;
+ return 0;
+ }
+ break;
+ }
+
+ result=DefWindowProcA (hwnd, uiMsg, wParam, lParam);
+ return result;
+}
+
+static LRESULT CALLBACK testDlgWinProc (HWND hwnd, UINT uiMsg, WPARAM wParam,
+ LPARAM lParam)
+{
+ LRESULT result;
+ switch (uiMsg)
+ {
+ /* Add blank case statements for these to ensure we don't use them
+ * by mistake.
+ */
+ case DM_GETDEFID: break;
+ case DM_SETDEFID: break;
+
+ case WM_CREATE:
+ return (OnTestDlgCreate (hwnd,
+ (LPCREATESTRUCTA) lParam) ? 0 : (LRESULT) -1);
+ }
+
+ result=DefWindowProcA (hwnd, uiMsg, wParam, lParam);
+ return result;
+}
+
+static BOOL RegisterWindowClasses (void)
+{
+ WNDCLASSA cls;
+
+ cls.style = 0;
+ cls.lpfnWndProc = DefWindowProcA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = g_hinst;
+ cls.hIcon = NULL;
+ cls.hCursor = LoadCursorA (NULL, IDC_ARROW);
+ cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "GetNextDlgItemWindowClass";
+
+ if (!RegisterClassA (&cls)) return FALSE;
+
+ cls.lpfnWndProc = main_window_procA;
+ cls.lpszClassName = "IsDialogMessageWindowClass";
+
+ if (!RegisterClassA (&cls)) return FALSE;
+
+ GetClassInfoA(0, "#32770", &cls);
+ cls.lpfnWndProc = testDlgWinProc;
+ cls.lpszClassName = "WM_NEXTDLGCTLWndClass";
+ if (!RegisterClassA (&cls)) return FALSE;
+
+ return TRUE;
+}
+
+static void WM_NEXTDLGCTLTest(void)
+{
+ DWORD dwVal;
+
+ g_hwndTestDlg = CreateWindowEx( WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR
+ | WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT | WS_EX_APPWINDOW,
+ "WM_NEXTDLGCTLWndClass",
+ "WM_NEXTDLGCTL Message test window",
+ WS_POPUPWINDOW | WS_CLIPSIBLINGS | WS_DLGFRAME | WS_OVERLAPPED |
+ WS_MINIMIZEBOX | WS_MAXIMIZEBOX | DS_3DLOOK | DS_SETFONT | DS_MODALFRAME,
+ 0, 0, 235, 135,
+ NULL, NULL, g_hinst, 0);
+
+ assert (g_hwndTestDlg);
+ assert (g_hwndTestDlgBut1);
+ assert (g_hwndTestDlgBut2);
+ assert (g_hwndTestDlgEdit);
+
+ /*
+ * Test message DM_SETDEFID
+ */
+
+ DefDlgProcA( g_hwndTestDlg, DM_SETDEFID, IDCANCEL, 0 );
+ DefDlgProcA( g_hwndTestDlgBut1, BM_SETSTYLE, BS_DEFPUSHBUTTON, FALSE );
+ dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+
+ ok ( IDCANCEL == (LOWORD(dwVal)), "Did not set default ID\n" );
+
+ /*
+ * Check whether message WM_NEXTDLGCTL is changing the focus to next control and if
+ * the destination control is a button, style of the button should be changed to
+ * BS_DEFPUSHBUTTON with out making it default.
+ */
+
+ /*
+ * Keep the focus on Edit control.
+ */
+
+ if ( SetFocus( g_hwndTestDlgEdit ) )
+ {
+ ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't set on Edit control\n");
+
+ /*
+ * Test message WM_NEXTDLGCTL
+ */
+ DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
+ ok ((GetFocus() == g_hwndTestDlgBut1), "Focus didn't move to first button\n");
+
+ /*
+ * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
+ */
+ dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+ ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
+
+ /*
+ * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and
+ * the style of default button changed to BS_PUSHBUTTON.
+ */
+ if ( IDCANCEL == (LOWORD(dwVal)) )
+ {
+ ok ( ((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+ "Button1 style not set to BS_DEFPUSHBUTTON\n" );
+
+ ok ( !((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+ "Button2's style not chaged to BS_PUSHBUTTON\n" );
+ }
+
+ /*
+ * Move focus to Button2 using "WM_NEXTDLGCTL"
+ */
+ DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
+ ok ((GetFocus() == g_hwndTestDlgBut2), "Focus didn't move to second button\n");
+
+ /*
+ * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
+ */
+ dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+ ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
+
+ /*
+ * Check whether the style of the button which got the focus, changed to BS_DEFPUSHBUTTON and
+ * the style of button which lost the focus changed to BS_PUSHBUTTON.
+ */
+ if ( IDCANCEL == (LOWORD(dwVal)) )
+ {
+ ok ( ((GetWindowLong( g_hwndTestDlgBut2, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+ "Button2 style not set to BS_DEFPUSHBUTTON\n" );
+
+ ok ( !((GetWindowLong( g_hwndTestDlgBut1, GWL_STYLE)) & BS_DEFPUSHBUTTON),
+ "Button1's style not chaged to BS_PUSHBUTTON\n" );
+ }
+
+ /*
+ * Move focus to Edit control using "WM_NEXTDLGCTL"
+ */
+ DefDlgProcA( g_hwndTestDlg, WM_NEXTDLGCTL, 0, 0 );
+ ok ((GetFocus() == g_hwndTestDlgEdit), "Focus didn't move to Edit control\n");
+
+ /*
+ * Check whether the default button ID got changed by sending message "WM_NEXTDLGCTL"
+ */
+ dwVal = DefDlgProcA(g_hwndTestDlg, DM_GETDEFID, 0, 0);
+ ok ( IDCANCEL == (LOWORD(dwVal)), "WM_NEXTDLGCTL changed default button\n");
+ }
+ DestroyWindow(g_hwndTestDlg);
+}
+
+static void IsDialogMessageWTest (void)
+{
+ MSG msg;
+
+ g_hwndMain = CreateWindow ("IsDialogMessageWindowClass", "IsDialogMessageWindowClass",
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, g_hinst, 0);
+
+ assert (g_hwndMain);
+ assert (g_hwndButton1);
+ assert (g_hwndButtonCancel);
+
+ /* The focus should initially be nowhere. The first TAB should take it
+ * to the first button. The second TAB should take it to the Cancel
+ * button.
+ */
+ FormTabMsg (&msg, g_hwndMain);
+ ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle first TAB\n");
+ ok ((GetFocus() == g_hwndButton1), "Focus did not move to first button\n");
+ FormTabMsg (&msg, g_hwndButton1);
+ ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle second TAB\n");
+ ok ((GetFocus() == g_hwndButtonCancel),
+ "Focus did not move to cancel button\n");
+ FormEnterMsg (&msg, g_hwndButtonCancel);
+ ok (IsDialogMessage (g_hwndMain, &msg), "Did not handle the ENTER\n");
+ ok (g_terminated, "ENTER did not terminate\n");
+}
+
+
+static LRESULT CALLBACK delayFocusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uiMsg)
+ {
+ case WM_INITDIALOG:
+ g_hwndMain = hDlg;
+ g_hwndInitialFocusGroupBox = GetDlgItem(hDlg,100);
+ g_hwndButton1 = GetDlgItem(hDlg,200);
+ g_hwndButton2 = GetDlgItem(hDlg,201);
+ g_hwndButtonCancel = GetDlgItem(hDlg,IDCANCEL);
+ g_styleInitialFocusT1 = GetWindowLong(g_hwndInitialFocusGroupBox, GWL_STYLE);
+
+ /* Initially check the second radio button */
+ SendMessage(g_hwndButton1, BM_SETCHECK, BST_UNCHECKED, 0);
+ SendMessage(g_hwndButton2, BM_SETCHECK, BST_CHECKED , 0);
+ /* Continue testing after dialog initialization */
+ PostMessage(hDlg, WM_USER, 0, 0);
+ return g_bInitialFocusInitDlgResult;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hDlg, LOWORD(wParam));
+ return TRUE;
+ }
+ return FALSE;
+
+ case WM_USER:
+ g_styleInitialFocusT2 = GetWindowLong(hDlg, GWL_STYLE);
+ g_hwndInitialFocusT1 = GetFocus();
+ SetFocus(hDlg);
+ g_hwndInitialFocusT2 = GetFocus();
+ PostMessage(hDlg, WM_COMMAND, IDCANCEL, 0);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static LRESULT CALLBACK focusDlgWinProc (HWND hDlg, UINT uiMsg, WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uiMsg)
+ {
+ case WM_INITDIALOG:
+ return TRUE;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) == IDCANCEL)
+ {
+ EndDialog(hDlg, LOWORD(wParam));
+ return TRUE;
+ }
+ else if (LOWORD(wParam) == 200)
+ {
+ if (HIWORD(wParam) == EN_SETFOCUS)
+ g_hwndInitialFocusT1 = (HWND)lParam;
+ }
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/* Helper for InitialFocusTest */
+static const char * GetHwndString(HWND hw)
+{
+ if (hw == NULL)
+ return "a null handle";
+ if (hw == g_hwndMain)
+ return "the dialog handle";
+ if (hw == g_hwndInitialFocusGroupBox)
+ return "the group box control";
+ if (hw == g_hwndButton1)
+ return "the first button";
+ if (hw == g_hwndButton2)
+ return "the second button";
+ if (hw == g_hwndButtonCancel)
+ return "the cancel button";
+
+ return "unknown handle";
+}
+
+static void InitialFocusTest (void)
+{
+ /* Test 1:
+ * This test intentionally returns FALSE in response to WM_INITDIALOG
+ * without setting focus to a control. This is not allowed according to
+ * MSDN, but it is exactly what MFC's CFormView does.
+ *
+ * Since the WM_INITDIALOG handler returns FALSE without setting the focus,
+ * the focus should initially be NULL. Later, when we manually set focus to
+ * the dialog, the default handler should set focus to the first control that
+ * is "visible, not disabled, and has the WS_TABSTOP style" (MSDN). Because the
+ * second radio button has been checked, it should be the first control
+ * that meets these criteria and should receive the focus.
+ */
+
+ g_bInitialFocusInitDlgResult = FALSE;
+ g_hwndInitialFocusT1 = (HWND) -1;
+ g_hwndInitialFocusT2 = (HWND) -1;
+ g_styleInitialFocusT1 = -1;
+ g_styleInitialFocusT2 = -1;
+
+ DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, (DLGPROC)delayFocusDlgWinProc);
+
+ ok (((g_styleInitialFocusT1 & WS_TABSTOP) == 0),
+ "Error in wrc - Detected WS_TABSTOP as default style for GROUPBOX\n");
+
+ ok (((g_styleInitialFocusT2 & WS_VISIBLE) == 0),
+ "Modal dialogs should not be shown until the message queue first goes empty\n");
+
+ ok ((g_hwndInitialFocusT1 == NULL),
+ "Error in initial focus when WM_INITDIALOG returned FALSE: "
+ "Expected NULL focus, got %s (%p).\n",
+ GetHwndString(g_hwndInitialFocusT1), g_hwndInitialFocusT1);
+
+ ok ((g_hwndInitialFocusT2 == g_hwndButton2),
+ "Error after first SetFocus() when WM_INITDIALOG returned FALSE: "
+ "Expected the second button (%p), got %s (%p).\n",
+ g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
+ g_hwndInitialFocusT2);
+
+ /* Test 2:
+ * This is the same as above, except WM_INITDIALOG is made to return TRUE.
+ * This should cause the focus to go to the second radio button right away
+ * and stay there (until the user indicates otherwise).
+ */
+
+ g_bInitialFocusInitDlgResult = TRUE;
+ g_hwndInitialFocusT1 = (HWND) -1;
+ g_hwndInitialFocusT2 = (HWND) -1;
+ g_styleInitialFocusT1 = -1;
+ g_styleInitialFocusT2 = -1;
+
+ DialogBoxA(g_hinst, "RADIO_TEST_DIALOG", NULL, (DLGPROC)delayFocusDlgWinProc);
+
+ ok ((g_hwndInitialFocusT1 == g_hwndButton2),
+ "Error in initial focus when WM_INITDIALOG returned TRUE: "
+ "Expected the second button (%p), got %s (%p).\n",
+ g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
+ g_hwndInitialFocusT2);
+
+ ok ((g_hwndInitialFocusT2 == g_hwndButton2),
+ "Error after first SetFocus() when WM_INITDIALOG returned TRUE: "
+ "Expected the second button (%p), got %s (%p).\n",
+ g_hwndButton2, GetHwndString(g_hwndInitialFocusT2),
+ g_hwndInitialFocusT2);
+
+ /* Test 3:
+ * If the dialog has DS_CONTROL and it's not visible then we shouldn't change focus */
+ {
+ HWND hDlg;
+ HRSRC hResource;
+ HANDLE hTemplate;
+ DLGTEMPLATE* pTemplate;
+
+ hResource = FindResourceA(g_hinst,"FOCUS_TEST_DIALOG", (LPSTR)RT_DIALOG);
+ hTemplate = LoadResource(g_hinst, hResource);
+ pTemplate = (LPDLGTEMPLATEA)LockResource(hTemplate);
+
+ g_hwndInitialFocusT1 = 0;
+ hDlg = CreateDialogIndirectParamW(g_hinst, pTemplate, NULL, (DLGPROC)focusDlgWinProc,0);
+ ok (hDlg != 0, "Failed to create test dialog.\n");
+
+ ok ((g_hwndInitialFocusT1 == 0),
+ "Focus should not be set for an invisible DS_CONTROL dialog %p.\n", g_hwndInitialFocusT1);
+
+ DestroyWindow(hDlg);
+ }
+}
+
+static void test_GetDlgItemText(void)
+{
+ char string[64];
+ BOOL ret;
+
+ strcpy(string, "Overwrite Me");
+ ret = GetDlgItemTextA(NULL, 0, string, sizeof(string)/sizeof(string[0]));
+ ok(!ret, "GetDlgItemText(NULL) shouldn't have succeeded\n");
+
+ ok(string[0] == '\0', "string retrieved using GetDlgItemText should have been NULL terminated\n");
+}
+
+
+START_TEST(dialog)
+{
+ g_hinst = GetModuleHandleA (0);
+
+ if (!RegisterWindowClasses()) assert(0);
+
+ GetNextDlgItemTest();
+ IsDialogMessageWTest();
+ WM_NEXTDLGCTLTest();
+ InitialFocusTest();
+ test_GetDlgItemText();
+}
--- /dev/null
+/* Unit test suite for edit control.
+ *
+ * Copyright 2004 Vitaliy Margolen
+ * Copyright 2005 C. Scott Ananian
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+
+#include "wine/test.h"
+
+#ifndef ES_COMBO
+#define ES_COMBO 0x200
+#endif
+
+#define ID_EDITTEST2 99
+#define MAXLEN 200
+
+struct edit_notify {
+ int en_change, en_maxtext, en_update;
+};
+
+static struct edit_notify notifications;
+
+static HINSTANCE hinst;
+static HWND hwndET2;
+static const char szEditTest2Class[] = "EditTest2Class";
+static const char szEditTest3Class[] = "EditTest3Class";
+static const char szEditTextPositionClass[] = "EditTextPositionWindowClass";
+
+static HWND create_editcontrol (DWORD style, DWORD exstyle)
+{
+ HWND handle;
+
+ handle = CreateWindowEx(exstyle,
+ "EDIT",
+ "Test Text",
+ style,
+ 10, 10, 300, 300,
+ NULL, NULL, hinst, NULL);
+ assert (handle);
+ if (winetest_interactive)
+ ShowWindow (handle, SW_SHOW);
+ return handle;
+}
+
+static HWND create_child_editcontrol (DWORD style, DWORD exstyle)
+{
+ HWND parentWnd;
+ HWND editWnd;
+ RECT rect;
+
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = 300;
+ rect.bottom = 300;
+ assert(AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE));
+
+ parentWnd = CreateWindowEx(0,
+ szEditTextPositionClass,
+ "Edit Test",
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ rect.right - rect.left, rect.bottom - rect.top,
+ NULL, NULL, hinst, NULL);
+ assert(parentWnd);
+
+ editWnd = CreateWindowEx(exstyle,
+ "EDIT",
+ "Test Text",
+ WS_CHILD | style,
+ 0, 0, 300, 300,
+ parentWnd, NULL, hinst, NULL);
+ assert(editWnd);
+ if (winetest_interactive)
+ ShowWindow (parentWnd, SW_SHOW);
+ return editWnd;
+}
+
+static void destroy_child_editcontrol (HWND hwndEdit)
+{
+ if (GetParent(hwndEdit))
+ DestroyWindow(GetParent(hwndEdit));
+ else {
+ trace("Edit control has no parent!\n");
+ DestroyWindow(hwndEdit);
+ }
+}
+
+static LONG get_edit_style (HWND hwnd)
+{
+ return GetWindowLongA( hwnd, GWL_STYLE ) & (
+ ES_LEFT |
+/* FIXME: not implemented
+ ES_CENTER |
+ ES_RIGHT |
+ ES_OEMCONVERT |
+*/
+ ES_MULTILINE |
+ ES_UPPERCASE |
+ ES_LOWERCASE |
+ ES_PASSWORD |
+ ES_AUTOVSCROLL |
+ ES_AUTOHSCROLL |
+ ES_NOHIDESEL |
+ ES_COMBO |
+ ES_READONLY |
+ ES_WANTRETURN |
+ ES_NUMBER
+ );
+}
+
+static void set_client_height(HWND Wnd, unsigned Height)
+{
+ RECT ClientRect, WindowRect;
+
+ GetWindowRect(Wnd, &WindowRect);
+ GetClientRect(Wnd, &ClientRect);
+ SetWindowPos(Wnd, NULL, 0, 0,
+ WindowRect.right - WindowRect.left,
+ Height + (WindowRect.bottom - WindowRect.top) -
+ (ClientRect.bottom - ClientRect.top),
+ SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+
+ /* Workaround for a bug in Windows' edit control
+ (multi-line mode) */
+ GetWindowRect(Wnd, &WindowRect);
+ SetWindowPos(Wnd, NULL, 0, 0,
+ WindowRect.right - WindowRect.left + 1,
+ WindowRect.bottom - WindowRect.top + 1,
+ SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+ SetWindowPos(Wnd, NULL, 0, 0,
+ WindowRect.right - WindowRect.left,
+ WindowRect.bottom - WindowRect.top,
+ SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER);
+
+ GetClientRect(Wnd, &ClientRect);
+ ok(ClientRect.bottom - ClientRect.top == Height,
+ "The client height should be %ld, but is %ld\n",
+ (long)Height, (long)(ClientRect.bottom - ClientRect.top));
+}
+
+static void test_edit_control_1(void)
+{
+ HWND hwEdit;
+ MSG msMessage;
+ int i;
+ LONG r;
+
+ msMessage.message = WM_KEYDOWN;
+
+ trace("EDIT: Single line\n");
+ hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ r = get_edit_style(hwEdit);
+ ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL), "Wrong style expected 0xc0 got: 0x%lx\n", r);
+ for (i=0;i<65535;i++)
+ {
+ msMessage.wParam = i;
+ r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+ ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS),
+ "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %lx\n", r);
+ }
+ DestroyWindow (hwEdit);
+
+ trace("EDIT: Single line want returns\n");
+ hwEdit = create_editcontrol(ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ r = get_edit_style(hwEdit);
+ ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN), "Wrong style expected 0x10c0 got: 0x%lx\n", r);
+ for (i=0;i<65535;i++)
+ {
+ msMessage.wParam = i;
+ r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+ ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS),
+ "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %lx\n", r);
+ }
+ DestroyWindow (hwEdit);
+
+ trace("EDIT: Multiline line\n");
+ hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ r = get_edit_style(hwEdit);
+ ok(r == (ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0xc4 got: 0x%lx\n", r);
+ for (i=0;i<65535;i++)
+ {
+ msMessage.wParam = i;
+ r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+ ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS),
+ "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %lx\n", r);
+ }
+ DestroyWindow (hwEdit);
+
+ trace("EDIT: Multi line want returns\n");
+ hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ r = get_edit_style(hwEdit);
+ ok(r == (ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0x10c4 got: 0x%lx\n", r);
+ for (i=0;i<65535;i++)
+ {
+ msMessage.wParam = i;
+ r = SendMessage(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage);
+ ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS),
+ "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %lx\n", r);
+ }
+ DestroyWindow (hwEdit);
+}
+
+/* WM_SETTEXT is implemented by selecting all text, and then replacing the
+ * selection. This test checks that the first 'select all' doesn't generate
+ * an UPDATE message which can escape and (via a handler) change the
+ * selection, which would cause WM_SETTEXT to break. This old bug
+ * was fixed 18-Mar-2005; we check here to ensure it doesn't regress.
+ */
+static void test_edit_control_2(void)
+{
+ HWND hwndMain;
+ char szLocalString[MAXLEN];
+
+ /* Create main and edit windows. */
+ hwndMain = CreateWindow(szEditTest2Class, "ET2", WS_OVERLAPPEDWINDOW,
+ 0, 0, 200, 200, NULL, NULL, hinst, NULL);
+ assert(hwndMain);
+ if (winetest_interactive)
+ ShowWindow (hwndMain, SW_SHOW);
+
+ hwndET2 = CreateWindow("EDIT", NULL,
+ WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL,
+ 0, 0, 150, 50, /* important this not be 0 size. */
+ hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL);
+ assert(hwndET2);
+ if (winetest_interactive)
+ ShowWindow (hwndET2, SW_SHOW);
+
+ trace("EDIT: SETTEXT atomicity\n");
+ /* Send messages to "type" in the word 'foo'. */
+ SendMessage(hwndET2, WM_CHAR, 'f', 1);
+ SendMessage(hwndET2, WM_CHAR, 'o', 1);
+ SendMessage(hwndET2, WM_CHAR, 'o', 1);
+ /* 'foo' should have been changed to 'bar' by the UPDATE handler. */
+ GetWindowText(hwndET2, szLocalString, MAXLEN);
+ ok(lstrcmp(szLocalString, "bar")==0,
+ "Wrong contents of edit: %s\n", szLocalString);
+
+ /* OK, done! */
+ DestroyWindow (hwndET2);
+ DestroyWindow (hwndMain);
+}
+
+static void ET2_check_change(void) {
+ char szLocalString[MAXLEN];
+ /* This EN_UPDATE handler changes any 'foo' to 'bar'. */
+ GetWindowText(hwndET2, szLocalString, MAXLEN);
+ if (lstrcmp(szLocalString, "foo")==0) {
+ lstrcpy(szLocalString, "bar");
+ SendMessage(hwndET2, WM_SETTEXT, 0, (LPARAM) szLocalString);
+ }
+ /* always leave the cursor at the end. */
+ SendMessage(hwndET2, EM_SETSEL, MAXLEN - 1, MAXLEN - 1);
+}
+static void ET2_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
+{
+ if (id==ID_EDITTEST2 && codeNotify == EN_UPDATE)
+ ET2_check_change();
+}
+static LRESULT CALLBACK ET2_WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (iMsg) {
+ HANDLE_MSG(hwnd, WM_COMMAND, ET2_OnCommand);
+ }
+ return DefWindowProc(hwnd, iMsg, wParam, lParam);
+}
+
+static void zero_notify(void)
+{
+ notifications.en_change = 0;
+ notifications.en_maxtext = 0;
+ notifications.en_update = 0;
+}
+
+#define test_notify(enchange, enmaxtext, enupdate) \
+ ok(notifications.en_change == enchange, "expected %d EN_CHANGE notifications, " \
+ "got %d\n", enchange, notifications.en_change); \
+ ok(notifications.en_maxtext == enmaxtext, "expected %d EN_MAXTEXT notifications, " \
+ "got %d\n", enmaxtext, notifications.en_maxtext); \
+ ok(notifications.en_update == enupdate, "expected %d EN_UPDATE notifications, " \
+ "got %d\n", enupdate, notifications.en_update)
+
+
+static LRESULT CALLBACK edit3_wnd_procA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_COMMAND:
+ switch (HIWORD(wParam)) {
+ case EN_MAXTEXT:
+ notifications.en_maxtext++;
+ break;
+ case EN_UPDATE:
+ notifications.en_update++;
+ break;
+ case EN_CHANGE:
+ notifications.en_change++;
+ break;
+ }
+ break;
+ }
+ return DefWindowProcA(hWnd, msg, wParam, lParam);
+}
+
+/* Test behaviour of WM_SETTEXT, WM_REPLACESEL and notificatisons sent in response
+ * to these messages.
+ */
+static void test_edit_control_3(void)
+{
+ HWND hWnd;
+ HWND hParent;
+ int len;
+ static const char *str = "this is a long string.";
+ static const char *str2 = "this is a long string.\r\nthis is a long string.\r\nthis is a long string.\r\nthis is a long string.";
+
+ trace("EDIT: Test notifications\n");
+
+ hParent = CreateWindowExA(0,
+ szEditTest3Class,
+ NULL,
+ 0,
+ CW_USEDEFAULT, CW_USEDEFAULT, 10, 10,
+ NULL, NULL, NULL, NULL);
+ assert(hParent);
+
+ trace("EDIT: Single line, no ES_AUTOHSCROLL\n");
+ hWnd = CreateWindowExA(0,
+ "EDIT",
+ NULL,
+ 0,
+ 10, 10, 50, 50,
+ hParent, NULL, NULL, NULL);
+ assert(hWnd);
+
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) > len, "text should have been truncated\n");
+ test_notify(1, 1, 1);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(1 == len, "wrong text length, expected 1, got %d\n", len);
+ test_notify(1, 0, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ test_notify(1, 0, 1);
+
+ SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+ test_notify(1, 1, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ test_notify(1, 0, 1);
+
+ DestroyWindow(hWnd);
+
+ trace("EDIT: Single line, ES_AUTOHSCROLL\n");
+ hWnd = CreateWindowExA(0,
+ "EDIT",
+ NULL,
+ ES_AUTOHSCROLL,
+ 10, 10, 50, 50,
+ hParent, NULL, NULL, NULL);
+ assert(hWnd);
+
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ test_notify(1, 0, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ test_notify(1, 0, 1);
+
+ SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+ test_notify(1, 1, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ test_notify(1, 0, 1);
+
+ DestroyWindow(hWnd);
+
+ trace("EDIT: Multline, no ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n");
+ hWnd = CreateWindowExA(0,
+ "EDIT",
+ NULL,
+ ES_MULTILINE,
+ 10, 10, 50, 50,
+ hParent, NULL, NULL, NULL);
+ assert(hWnd);
+
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(0 == len, "text should have been truncated, expected 0, got %d\n", len);
+ test_notify(1, 1, 1);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len);
+ test_notify(1, 0, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ test_notify(0, 0, 0);
+
+ SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+ test_notify(1, 1, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ test_notify(0, 0, 0);
+
+ DestroyWindow(hWnd);
+
+ trace("EDIT: Multline, ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n");
+ hWnd = CreateWindowExA(0,
+ "EDIT",
+ NULL,
+ ES_MULTILINE | ES_AUTOHSCROLL,
+ 10, 10, 50, 50,
+ hParent, NULL, NULL, NULL);
+ assert(hWnd);
+
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(0 == len, "text should have been truncated, expected 0, got %d\n", len);
+ test_notify(1, 1, 1);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a");
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len);
+ test_notify(1, 0, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+ test_notify(0, 0, 0);
+
+ SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+ test_notify(1, 1, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+ test_notify(0, 0, 0);
+
+ DestroyWindow(hWnd);
+
+ trace("EDIT: Multline, ES_AUTOHSCROLL and ES_AUTOVSCROLL\n");
+ hWnd = CreateWindowExA(0,
+ "EDIT",
+ NULL,
+ ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
+ 10, 10, 50, 50,
+ hParent, NULL, NULL, NULL);
+ assert(hWnd);
+
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+ test_notify(1, 0, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+ test_notify(0, 0, 0);
+
+ SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0);
+
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)"");
+ zero_notify();
+ SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len);
+ test_notify(1, 1, 1);
+
+ zero_notify();
+ SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2);
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n");
+ test_notify(0, 0, 0);
+
+ DestroyWindow(hWnd);
+}
+
+/* Test EM_CHARFROMPOS and EM_POSFROMCHAR
+ */
+static void test_edit_control_4(void)
+{
+ HWND hwEdit;
+ int lo, hi, mid;
+ int ret;
+ int i;
+
+ trace("EDIT: Test EM_CHARFROMPOS and EM_POSFROMCHAR\n");
+ hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+ lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+ hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+ mid = lo + (hi - lo) / 2;
+
+ for (i = lo; i < mid; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(0 == ret, "expected 0 got %d\n", ret);
+ }
+ for (i = mid; i <= hi; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(1 == ret, "expected 1 got %d\n", ret);
+ }
+ ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+ ok(-1 == ret, "expected -1 got %d\n", ret);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(ES_RIGHT | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+ lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+ hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+ mid = lo + (hi - lo) / 2;
+
+ for (i = lo; i < mid; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(0 == ret, "expected 0 got %d\n", ret);
+ }
+ for (i = mid; i <= hi; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(1 == ret, "expected 1 got %d\n", ret);
+ }
+ ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+ ok(-1 == ret, "expected -1 got %d\n", ret);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(ES_CENTER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+ lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+ hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+ mid = lo + (hi - lo) / 2;
+
+ for (i = lo; i < mid; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(0 == ret, "expected 0 got %d\n", ret);
+ }
+ for (i = mid; i <= hi; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(1 == ret, "expected 1 got %d\n", ret);
+ }
+ ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+ ok(-1 == ret, "expected -1 got %d\n", ret);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+ lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+ hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+ mid = lo + (hi - lo) / 2 +1;
+
+ for (i = lo; i < mid; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(0 == ret, "expected 0 got %d\n", ret);
+ }
+ for (i = mid; i <= hi; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(1 == ret, "expected 1 got %d\n", ret);
+ }
+ ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+ ok(-1 == ret, "expected -1 got %d\n", ret);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(ES_MULTILINE | ES_RIGHT | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+ lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+ hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+ mid = lo + (hi - lo) / 2 +1;
+
+ for (i = lo; i < mid; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(0 == ret, "expected 0 got %d\n", ret);
+ }
+ for (i = mid; i <= hi; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(1 == ret, "expected 1 got %d\n", ret);
+ }
+ ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+ ok(-1 == ret, "expected -1 got %d\n", ret);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(ES_MULTILINE | ES_CENTER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+ SendMessage(hwEdit, WM_SETTEXT, 0, (LPARAM) "aa");
+ lo = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 0, 0));
+ hi = LOWORD(SendMessage(hwEdit, EM_POSFROMCHAR, 1, 0));
+ mid = lo + (hi - lo) / 2 +1;
+
+ for (i = lo; i < mid; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(0 == ret, "expected 0 got %d\n", ret);
+ }
+ for (i = mid; i <= hi; i++) {
+ ret = LOWORD(SendMessage(hwEdit, EM_CHARFROMPOS, 0, (LPARAM) i));
+ ok(1 == ret, "expected 1 got %d\n", ret);
+ }
+ ret = SendMessage(hwEdit, EM_POSFROMCHAR, 2, 0);
+ ok(-1 == ret, "expected -1 got %d\n", ret);
+ DestroyWindow(hwEdit);
+}
+
+/* Test if creating edit control without ES_AUTOHSCROLL and ES_AUTOVSCROLL
+ * truncates text that doesn't fit.
+ */
+static void test_edit_control_5(void)
+{
+ static const char *str = "test\r\ntest";
+ HWND hWnd;
+ int len;
+
+ hWnd = CreateWindowEx(0,
+ "EDIT",
+ str,
+ 0,
+ 10, 10, 1, 1,
+ NULL, NULL, NULL, NULL);
+ assert(hWnd);
+
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ DestroyWindow(hWnd);
+
+ hWnd = CreateWindowEx(0,
+ "EDIT",
+ str,
+ ES_MULTILINE,
+ 10, 10, 1, 1,
+ NULL, NULL, NULL, NULL);
+ assert(hWnd);
+
+ len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0);
+ ok(lstrlenA(str) == len, "text shouldn't have been truncated\n");
+ DestroyWindow(hWnd);
+}
+
+static void test_margins(void)
+{
+ HWND hwEdit;
+ RECT old_rect, new_rect;
+ INT old_left_margin, old_right_margin;
+ DWORD old_margins, new_margins;
+
+ hwEdit = create_editcontrol(WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+
+ old_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0);
+ old_left_margin = LOWORD(old_margins);
+ old_right_margin = HIWORD(old_margins);
+
+ /* Check if setting the margins works */
+
+ SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN, MAKELONG(10, 0));
+ new_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0);
+ ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins));
+ ok(HIWORD(new_margins) == old_right_margin, "Wrong right margin: %d\n", HIWORD(new_margins));
+
+ SendMessage(hwEdit, EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, 10));
+ new_margins = SendMessage(hwEdit, EM_GETMARGINS, 0, 0);
+ ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins));
+ ok(HIWORD(new_margins) == 10, "Wrong right margin: %d\n", HIWORD(new_margins));
+
+
+ /* The size of the rectangle must decrease if we increase the margin */
+
+ SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(5, 5));
+ SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect);
+ SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(15, 20));
+ SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+ ok(new_rect.left == old_rect.left + 10, "The left border of the rectangle is wrong\n");
+ ok(new_rect.right == old_rect.right - 15, "The right border of the rectangle is wrong\n");
+ ok(new_rect.top == old_rect.top, "The top border of the rectangle must not change\n");
+ ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle must not change\n");
+
+
+ /* If we set the margin to same value as the current margin,
+ the rectangle must not change */
+
+ SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10));
+ old_rect.left = 1;
+ old_rect.right = 99;
+ old_rect.top = 1;
+ old_rect.bottom = 99;
+ SendMessage(hwEdit, EM_SETRECT, 0, (LPARAM)&old_rect);
+ SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect);
+ SendMessage(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10));
+ SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect);
+ ok(new_rect.left == old_rect.left, "The left border of the rectangle has changed\n");
+ ok(new_rect.right == old_rect.right, "The right border of the rectangle has changed\n");
+ ok(new_rect.top == old_rect.top, "The top border of the rectangle has changed\n");
+ ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle has changed\n");
+
+ DestroyWindow (hwEdit);
+}
+
+#define edit_pos_ok(exp, got, txt) \
+ ok(exp == got, "wrong " #txt " expected %d got %ld\n", exp, got);
+
+#define check_pos(hwEdit, set_height, test_top, test_height, test_left) \
+do { \
+ RECT format_rect; \
+ int left_margin; \
+ set_client_height(hwEdit, set_height); \
+ SendMessage(hwEdit, EM_GETRECT, 0, (LPARAM) &format_rect); \
+ left_margin = LOWORD(SendMessage(hwEdit, EM_GETMARGINS, 0, 0)); \
+ edit_pos_ok(test_top, format_rect.top, vertical position); \
+ edit_pos_ok((int)test_height, format_rect.bottom - format_rect.top, height); \
+ edit_pos_ok(test_left, format_rect.left - left_margin, left); \
+} while(0)
+
+void test_text_position_style(DWORD style)
+{
+ HWND hwEdit;
+ HFONT font, oldFont;
+ HDC dc;
+ TEXTMETRIC metrics;
+ INT b, bm, b2, b3;
+ BOOL single_line = !(style & ES_MULTILINE);
+
+ b = GetSystemMetrics(SM_CYBORDER) + 1;
+ b2 = 2 * b;
+ b3 = 3 * b;
+ bm = b2 - 1;
+
+ /* Get a stock font for which we can determine the metrics */
+ assert(font = GetStockObject(SYSTEM_FONT));
+ assert(dc = GetDC(NULL));
+ oldFont = SelectObject(dc, font);
+ assert(GetTextMetrics(dc, &metrics));
+ SelectObject(dc, oldFont);
+ ReleaseDC(NULL, dc);
+
+ /* Windows' edit control has some bugs in multi-line mode:
+ * - Sometimes the format rectangle doesn't get updated
+ * (see workaround in set_client_height())
+ * - If the height of the control is smaller than the height of a text
+ * line, the format rectangle is still as high as a text line
+ * (higher than the client rectangle) and the caret is not shown
+ */
+
+ /* Edit controls that are in a parent window */
+
+ hwEdit = create_child_editcontrol(style | WS_VISIBLE, 0);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 0);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 0);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 0);
+ check_pos(hwEdit, metrics.tmHeight + 2, 0, metrics.tmHeight , 0);
+ check_pos(hwEdit, metrics.tmHeight + 10, 0, metrics.tmHeight , 0);
+ destroy_child_editcontrol(hwEdit);
+
+ hwEdit = create_child_editcontrol(style | WS_BORDER | WS_VISIBLE, 0);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, b);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + bm, 0, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + b2, b, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + b3, b, metrics.tmHeight , b);
+ destroy_child_editcontrol(hwEdit);
+
+ hwEdit = create_child_editcontrol(style | WS_VISIBLE, WS_EX_CLIENTEDGE);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1);
+ destroy_child_editcontrol(hwEdit);
+
+ hwEdit = create_child_editcontrol(style | WS_BORDER | WS_VISIBLE, WS_EX_CLIENTEDGE);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1);
+ destroy_child_editcontrol(hwEdit);
+
+
+ /* Edit controls that are popup windows */
+
+ hwEdit = create_editcontrol(style | WS_POPUP, 0);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 0);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 0);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 0);
+ check_pos(hwEdit, metrics.tmHeight + 2, 0, metrics.tmHeight , 0);
+ check_pos(hwEdit, metrics.tmHeight + 10, 0, metrics.tmHeight , 0);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(style | WS_POPUP | WS_BORDER, 0);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, b);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + bm, 0, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + b2, b, metrics.tmHeight , b);
+ check_pos(hwEdit, metrics.tmHeight + b3, b, metrics.tmHeight , b);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(style | WS_POPUP, WS_EX_CLIENTEDGE);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1);
+ DestroyWindow(hwEdit);
+
+ hwEdit = create_editcontrol(style | WS_POPUP | WS_BORDER, WS_EX_CLIENTEDGE);
+ SendMessage(hwEdit, WM_SETFONT, (WPARAM) font, (LPARAM) FALSE);
+ if (single_line)
+ check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1);
+ check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1);
+ check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1);
+ DestroyWindow(hwEdit);
+}
+
+void test_text_position(void)
+{
+ trace("EDIT: Text position (Single line)\n");
+ test_text_position_style(ES_AUTOHSCROLL | ES_AUTOVSCROLL);
+ trace("EDIT: Text position (Multi line)\n");
+ test_text_position_style(ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL);
+}
+
+static BOOL RegisterWindowClasses (void)
+{
+ WNDCLASSA test2;
+ WNDCLASSA test3;
+ WNDCLASSA text_position;
+
+ test2.style = 0;
+ test2.lpfnWndProc = ET2_WndProc;
+ test2.cbClsExtra = 0;
+ test2.cbWndExtra = 0;
+ test2.hInstance = hinst;
+ test2.hIcon = NULL;
+ test2.hCursor = LoadCursorA (NULL, IDC_ARROW);
+ test2.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ test2.lpszMenuName = NULL;
+ test2.lpszClassName = szEditTest2Class;
+ if (!RegisterClassA(&test2)) return FALSE;
+
+ test3.style = 0;
+ test3.lpfnWndProc = edit3_wnd_procA;
+ test3.cbClsExtra = 0;
+ test3.cbWndExtra = 0;
+ test3.hInstance = hinst;
+ test3.hIcon = 0;
+ test3.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ test3.hbrBackground = GetStockObject(WHITE_BRUSH);
+ test3.lpszMenuName = NULL;
+ test3.lpszClassName = szEditTest3Class;
+ if (!RegisterClassA(&test3)) return FALSE;
+
+ text_position.style = CS_HREDRAW | CS_VREDRAW;
+ text_position.cbClsExtra = 0;
+ text_position.cbWndExtra = 0;
+ text_position.hInstance = hinst;
+ text_position.hIcon = NULL;
+ text_position.hCursor = LoadCursorA(NULL, (LPCSTR) IDC_ARROW);
+ text_position.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
+ text_position.lpszMenuName = NULL;
+ text_position.lpszClassName = szEditTextPositionClass;
+ text_position.lpfnWndProc = DefWindowProc;
+ if (!RegisterClassA(&text_position)) return FALSE;
+
+ return TRUE;
+}
+
+static void UnregisterWindowClasses (void)
+{
+ UnregisterClassA(szEditTest2Class, hinst);
+ UnregisterClassA(szEditTest3Class, hinst);
+ UnregisterClassA(szEditTextPositionClass, hinst);
+}
+
+START_TEST(edit)
+{
+ hinst = GetModuleHandleA(NULL);
+ assert(RegisterWindowClasses());
+
+ test_edit_control_1();
+ test_edit_control_2();
+ test_edit_control_3();
+ test_edit_control_4();
+ test_edit_control_5();
+ test_margins();
+ test_text_position();
+
+ UnregisterWindowClasses();
+}
--- /dev/null
+/* Test Key event to Key message translation
+ *
+ * Copyright 2003 Rein Klazes
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* test whether the right type of messages:
+ * WM_KEYUP/DOWN vs WM_SYSKEYUP/DOWN are sent in case of combined
+ * keystrokes.
+ *
+ * For instance <ALT>-X can be accompished by
+ * the sequence ALT-KEY-DOWN, X-KEY-DOWN, ALT-KEY-UP, X-KEY-UP
+ * but also X-KEY-DOWN, ALT-KEY-DOWN, X-KEY-UP, ALT-KEY-UP
+ * Whether a KEY or a SYSKEY message is sent is not always clear, it is
+ * also not the same in WINNT as in WIN9X */
+
+/* NOTE that there will be test failures under WIN9X
+ * No applications are known to me that rely on this
+ * so I don't fix it */
+
+/* TODO:
+ * 1. extend it to the wm_command and wm_syscommand notifications
+ * 2. add some more tests with special cases like dead keys or right (alt) key
+ * 3. there is some adapted code from input.c in here. Should really
+ * make that code exactly the same.
+ * 4. resolve the win9x case when there is a need or the testing frame work
+ * offers a nice way.
+ * 5. The test app creates a window, the user should not take the focus
+ * away during its short existence. I could do something to prevent that
+ * if it is a problem.
+ *
+ */
+
+#define _WIN32_WINNT 0x403
+
+#include <stdarg.h>
+#include <assert.h>
+
+#include "windows.h"
+
+#include "wine/test.h"
+
+/* globals */
+static HWND hWndTest;
+static long timetag = 0x10000000;
+
+static UINT (WINAPI *ptr_SendInput) (UINT, INPUT*, size_t);
+
+#define MAXKEYEVENTS 6
+#define MAXKEYMESSAGES MAXKEYEVENTS /* assuming a key event generates one
+ and only one message */
+
+/* keyboard message names, sorted as their value */
+static const char *MSGNAME[]={"WM_KEYDOWN", "WM_KEYUP", "WM_CHAR","WM_DEADCHAR",
+ "WM_SYSKEYDOWN", "WM_SYSKEYUP", "WM_SYSCHAR", "WM_SYSDEADCHAR" ,"WM_KEYLAST"};
+
+/* keyevents, add more as needed */
+typedef enum KEVtag
+{ ALTDOWN = 1, ALTUP, XDOWN, XUP, SHIFTDOWN, SHIFTUP, CTRLDOWN, CTRLUP } KEV;
+/* matching VK's */
+static const int GETVKEY[]={0, VK_MENU, VK_MENU, 'X', 'X', VK_SHIFT, VK_SHIFT, VK_CONTROL, VK_CONTROL};
+/* matching scan codes */
+static const int GETSCAN[]={0, 0x38, 0x38, 0x2D, 0x2D, 0x2A, 0x2A, 0x1D, 0x1D };
+/* matching updown events */
+static const int GETUPDOWN[]={0, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP, 0, KEYEVENTF_KEYUP};
+/* matching descripts */
+static const char *getdesc[]={"", "+alt","-alt","+X","-X","+shift","-shift","+ctrl","-ctrl"};
+
+/* The MSVC headers ignore our NONAMELESSUNION requests so we have to define our own type */
+typedef struct
+{
+ DWORD type;
+ union
+ {
+ MOUSEINPUT mi;
+ KEYBDINPUT ki;
+ HARDWAREINPUT hi;
+ } u;
+} TEST_INPUT;
+
+#define ADDTOINPUTS(kev) \
+inputs[evtctr].type = INPUT_KEYBOARD; \
+ ((TEST_INPUT*)inputs)[evtctr].u.ki.wVk = GETVKEY[ kev]; \
+ ((TEST_INPUT*)inputs)[evtctr].u.ki.wScan = GETSCAN[ kev]; \
+ ((TEST_INPUT*)inputs)[evtctr].u.ki.dwFlags = GETUPDOWN[ kev]; \
+ ((TEST_INPUT*)inputs)[evtctr].u.ki.dwExtraInfo = 0; \
+ ((TEST_INPUT*)inputs)[evtctr].u.ki.time = ++timetag; \
+ if( kev) evtctr++;
+
+typedef struct {
+ UINT message;
+ WPARAM wParam;
+ LPARAM lParam;
+} KMSG;
+
+/*******************************************
+ * add new test sets here
+ * the software will make all combinations of the
+ * keyevent defined here
+ */
+static const struct {
+ int nrkev;
+ KEV keydwn[MAXKEYEVENTS];
+ KEV keyup[MAXKEYEVENTS];
+} testkeyset[]= {
+ { 2, { ALTDOWN, XDOWN }, { ALTUP, XUP}},
+ { 3, { ALTDOWN, XDOWN , SHIFTDOWN}, { ALTUP, XUP, SHIFTUP}},
+ { 3, { ALTDOWN, XDOWN , CTRLDOWN}, { ALTUP, XUP, CTRLUP}},
+ { 3, { SHIFTDOWN, XDOWN , CTRLDOWN}, { SHIFTUP, XUP, CTRLUP}},
+ { 0 } /* mark the end */
+};
+
+/**********************adapted from input.c **********************************/
+
+static BYTE InputKeyStateTable[256];
+static BYTE AsyncKeyStateTable[256];
+static BYTE TrackSysKey = 0; /* determine whether ALT key up will cause a WM_SYSKEYUP
+ or a WM_KEYUP message */
+typedef union
+{
+ struct
+ {
+ unsigned long count : 16;
+ unsigned long code : 8;
+ unsigned long extended : 1;
+ unsigned long unused : 2;
+ unsigned long win_internal : 2;
+ unsigned long context : 1;
+ unsigned long previous : 1;
+ unsigned long transition : 1;
+ } lp1;
+ unsigned long lp2;
+} KEYLP;
+
+static int KbdMessage( KEV kev, WPARAM *pwParam, LPARAM *plParam )
+{
+ UINT message;
+ int VKey = GETVKEY[kev];
+ KEYLP keylp;
+
+ keylp.lp2 = 0;
+
+ keylp.lp1.count = 1;
+ keylp.lp1.code = GETSCAN[kev];
+ keylp.lp1.extended = 0 ;/* FIXME (ki->dwFlags & KEYEVENTF_EXTENDEDKEY) != 0; */
+ keylp.lp1.win_internal = 0;
+
+ if (GETUPDOWN[kev] & KEYEVENTF_KEYUP )
+ {
+ message = WM_KEYUP;
+ if( (InputKeyStateTable[VK_MENU] & 0x80) && (
+ (VKey == VK_MENU) || (VKey == VK_CONTROL) ||
+ !(InputKeyStateTable[VK_CONTROL] & 0x80))) {
+ if( TrackSysKey == VK_MENU || /* <ALT>-down/<ALT>-up sequence */
+ (VKey != VK_MENU)) /* <ALT>-down...<something else>-up */
+ message = WM_SYSKEYUP;
+ TrackSysKey = 0;
+ }
+ InputKeyStateTable[VKey] &= ~0x80;
+ keylp.lp1.previous = 1;
+ keylp.lp1.transition = 1;
+ }
+ else
+ {
+ keylp.lp1.previous = (InputKeyStateTable[VKey] & 0x80) != 0;
+ keylp.lp1.transition = 0;
+ if (!(InputKeyStateTable[VKey] & 0x80)) InputKeyStateTable[VKey] ^= 0x01;
+ InputKeyStateTable[VKey] |= 0x80;
+ AsyncKeyStateTable[VKey] |= 0x80;
+
+ message = WM_KEYDOWN;
+ if( (InputKeyStateTable[VK_MENU] & 0x80) &&
+ !(InputKeyStateTable[VK_CONTROL] & 0x80)) {
+ message = WM_SYSKEYDOWN;
+ TrackSysKey = VKey;
+ }
+ }
+
+ keylp.lp1.context = (InputKeyStateTable[VK_MENU] & 0x80) != 0; /* 1 if alt */
+
+ if( plParam) *plParam = keylp.lp2;
+ if( pwParam) *pwParam = VKey;
+ return message;
+}
+
+/****************************** end copy input.c ****************************/
+
+/*
+ * . prepare the keyevents for SendInputs
+ * . calculate the "expected" messages
+ * . Send the events to our window
+ * . retrieve the messages from the input queue
+ * . verify
+ */
+static void do_test( HWND hwnd, int seqnr, const KEV td[] )
+{
+ HMODULE module;
+ INPUT inputs[MAXKEYEVENTS];
+ KMSG expmsg[MAXKEYEVENTS];
+ MSG msg;
+ char buf[100];
+ UINT evtctr=0;
+ int kmctr, i;
+
+ module = GetModuleHandleA("user32");
+ if (!module) return;
+ ptr_SendInput = (void *)GetProcAddress(module, "SendInput");
+ if (!ptr_SendInput) return;
+
+ buf[0]='\0';
+ TrackSysKey=0; /* see input.c */
+ for( i = 0; i < MAXKEYEVENTS; i++) {
+ ADDTOINPUTS(td[i])
+ strcat(buf, getdesc[td[i]]);
+ if(td[i])
+ expmsg[i].message = KbdMessage(td[i], &(expmsg[i].wParam), &(expmsg[i].lParam)); /* see queue_kbd_event() */
+ else
+ expmsg[i].message = 0;
+ }
+ for( kmctr = 0; kmctr < MAXKEYEVENTS && expmsg[kmctr].message; kmctr++)
+ ;
+ assert( evtctr <= MAXKEYEVENTS );
+ assert( evtctr == ptr_SendInput(evtctr, &inputs[0], sizeof(INPUT)));
+ i = 0;
+ trace("======== key stroke sequence #%d: %s =============\n",
+ seqnr + 1, buf);
+ while( PeekMessage(&msg,hwnd,WM_KEYFIRST,WM_KEYLAST,PM_REMOVE) ) {
+ trace("message[%d] %-15s wParam %04x lParam %08lx time %lx\n", i,
+ MSGNAME[msg.message - WM_KEYFIRST], msg.wParam, msg.lParam, msg.time);
+ if( i < kmctr ) {
+ ok( msg.message == expmsg[i].message &&
+ msg.wParam == expmsg[i].wParam &&
+ msg.lParam == expmsg[i].lParam,
+ "wrong message! expected:\n"
+ "message[%d] %-15s wParam %04x lParam %08lx\n",i,
+ MSGNAME[(expmsg[i]).message - WM_KEYFIRST],
+ expmsg[i].wParam, expmsg[i].lParam );
+ }
+ i++;
+ }
+ trace("%d messages retrieved\n", i);
+ ok( i == kmctr, "message count is wrong: got %d expected: %d\n", i, kmctr);
+}
+
+/* test all combinations of the specified key events */
+static void TestASet( HWND hWnd, int nrkev, const KEV kevdwn[], const KEV kevup[] )
+{
+ int i,j,k,l,m,n;
+ static int count=0;
+ KEV kbuf[MAXKEYEVENTS];
+ assert( nrkev==2 || nrkev==3);
+ for(i=0;i<MAXKEYEVENTS;i++) kbuf[i]=0;
+ /* two keys involved gives 4 test cases */
+ if(nrkev==2) {
+ for(i=0;i<nrkev;i++) {
+ for(j=0;j<nrkev;j++) {
+ kbuf[0] = kevdwn[i];
+ kbuf[1] = kevdwn[1-i];
+ kbuf[2] = kevup[j];
+ kbuf[3] = kevup[1-j];
+ do_test( hWnd, count++, kbuf);
+ }
+ }
+ }
+ /* three keys involved gives 36 test cases */
+ if(nrkev==3){
+ for(i=0;i<nrkev;i++){
+ for(j=0;j<nrkev;j++){
+ if(j==i) continue;
+ for(k=0;k<nrkev;k++){
+ if(k==i || k==j) continue;
+ for(l=0;l<nrkev;l++){
+ for(m=0;m<nrkev;m++){
+ if(m==l) continue;
+ for(n=0;n<nrkev;n++){
+ if(n==l ||n==m) continue;
+ kbuf[0] = kevdwn[i];
+ kbuf[1] = kevdwn[j];
+ kbuf[2] = kevdwn[k];
+ kbuf[3] = kevup[l];
+ kbuf[4] = kevup[m];
+ kbuf[5] = kevup[n];
+ do_test( hWnd, count++, kbuf);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/* test each set specified in the global testkeyset array */
+static void TestSysKeys( HWND hWnd)
+{
+ int i;
+ for(i=0; testkeyset[i].nrkev;i++)
+ TestASet( hWnd, testkeyset[i].nrkev, testkeyset[i].keydwn,
+ testkeyset[i].keyup);
+}
+
+static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam,
+ LPARAM lParam )
+{
+ switch (msg) {
+ case WM_USER:
+ SetFocus(hWnd);
+ /* window has focus, now do the test */
+ if( hWnd == hWndTest) TestSysKeys( hWnd);
+ /* finished :-) */
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage( 0 );
+ break;
+
+ default:
+ return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
+ }
+
+ return 0;
+}
+
+START_TEST(input)
+{
+ MSG msg;
+ WNDCLASSA wclass;
+ HANDLE hInstance = GetModuleHandleA( NULL );
+
+ wclass.lpszClassName = "InputSysKeyTestClass";
+ wclass.style = CS_HREDRAW | CS_VREDRAW;
+ wclass.lpfnWndProc = WndProc;
+ wclass.hInstance = hInstance;
+ wclass.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
+ wclass.hCursor = LoadCursorA( NULL, IDC_ARROW);
+ wclass.hbrBackground = (HBRUSH)( COLOR_WINDOW + 1);
+ wclass.lpszMenuName = 0;
+ wclass.cbClsExtra = 0;
+ wclass.cbWndExtra = 0;
+ assert (RegisterClassA( &wclass ));
+ /* create the test window that will receive the keystrokes */
+ assert ( hWndTest = CreateWindowA( wclass.lpszClassName, "InputSysKeyTest",
+ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 100, 100,
+ NULL, NULL, hInstance, NULL) );
+ ShowWindow( hWndTest, SW_SHOW);
+ UpdateWindow( hWndTest);
+
+ /* flush pending messages */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
+
+ SendMessageA(hWndTest, WM_USER, 0, 0);
+ DestroyWindow(hWndTest);
+}
--- /dev/null
+/* Unit test suite for list boxes.
+ *
+ * Copyright 2003 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+
+#include "wine/test.h"
+
+#ifdef VISIBLE
+#define WAIT Sleep (1000)
+#define REDRAW RedrawWindow (handle, NULL, 0, RDW_UPDATENOW)
+#else
+#define WAIT
+#define REDRAW
+#endif
+
+static const char * const strings[4] = {
+ "First added",
+ "Second added",
+ "Third added",
+ "Fourth added which is very long because at some time we only had a 256 byte character buffer and that was overflowing in one of those applications that had a common dialog file open box and tried to add a 300 characters long custom filter string which of course the code did not like and crashed. Just make sure this string is longer than 256 characters."
+};
+
+static HWND
+create_listbox (DWORD add_style, HWND parent)
+{
+ HWND handle;
+ int ctl_id=0;
+ if (parent)
+ ctl_id=1;
+ handle=CreateWindow ("LISTBOX", "TestList",
+ (LBS_STANDARD & ~LBS_SORT) | add_style,
+ 0, 0, 100, 100,
+ parent, (HMENU)ctl_id, NULL, 0);
+
+ assert (handle);
+ SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[0]);
+ SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[1]);
+ SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[2]);
+ SendMessage (handle, LB_ADDSTRING, 0, (LPARAM) (LPCTSTR) strings[3]);
+
+#ifdef VISIBLE
+ ShowWindow (handle, SW_SHOW);
+#endif
+ REDRAW;
+
+ return handle;
+}
+
+struct listbox_prop {
+ DWORD add_style;
+};
+
+struct listbox_stat {
+ int selected, anchor, caret, selcount;
+};
+
+struct listbox_test {
+ struct listbox_prop prop;
+ struct listbox_stat init, init_todo;
+ struct listbox_stat click, click_todo;
+ struct listbox_stat step, step_todo;
+ struct listbox_stat sel, sel_todo;
+};
+
+static void
+listbox_query (HWND handle, struct listbox_stat *results)
+{
+ results->selected = SendMessage (handle, LB_GETCURSEL, 0, 0);
+ results->anchor = SendMessage (handle, LB_GETANCHORINDEX, 0, 0);
+ results->caret = SendMessage (handle, LB_GETCARETINDEX, 0, 0);
+ results->selcount = SendMessage (handle, LB_GETSELCOUNT, 0, 0);
+}
+
+static void
+buttonpress (HWND handle, WORD x, WORD y)
+{
+ LPARAM lp=x+(y<<16);
+
+ WAIT;
+ SendMessage (handle, WM_LBUTTONDOWN, (WPARAM) MK_LBUTTON, lp);
+ SendMessage (handle, WM_LBUTTONUP , (WPARAM) 0 , lp);
+ REDRAW;
+}
+
+static void
+keypress (HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
+{
+ LPARAM lp=1+(scancode<<16)+(extended?KEYEVENTF_EXTENDEDKEY:0);
+
+ WAIT;
+ SendMessage (handle, WM_KEYDOWN, keycode, lp);
+ SendMessage (handle, WM_KEYUP , keycode, lp | 0xc000000);
+ REDRAW;
+}
+
+#define listbox_field_ok(t, s, f, got) \
+ ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \
+ ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \
+ t.s.f, got.f)
+
+#define listbox_todo_field_ok(t, s, f, got) \
+ if (t.s##_todo.f) todo_wine { listbox_field_ok(t, s, f, got); } \
+ else listbox_field_ok(t, s, f, got)
+
+#define listbox_ok(t, s, got) \
+ listbox_todo_field_ok(t, s, selected, got); \
+ listbox_todo_field_ok(t, s, anchor, got); \
+ listbox_todo_field_ok(t, s, caret, got); \
+ listbox_todo_field_ok(t, s, selcount, got)
+
+static void
+check (const struct listbox_test test)
+{
+ struct listbox_stat answer;
+ HWND hLB=create_listbox (test.prop.add_style, 0);
+ RECT second_item;
+ int i;
+ int res;
+
+ listbox_query (hLB, &answer);
+ listbox_ok (test, init, answer);
+
+ SendMessage (hLB, LB_GETITEMRECT, (WPARAM) 1, (LPARAM) &second_item);
+ buttonpress(hLB, (WORD)second_item.left, (WORD)second_item.top);
+
+ listbox_query (hLB, &answer);
+ listbox_ok (test, click, answer);
+
+ keypress (hLB, VK_DOWN, 0x50, TRUE);
+
+ listbox_query (hLB, &answer);
+ listbox_ok (test, step, answer);
+
+ DestroyWindow (hLB);
+ hLB=create_listbox (test.prop.add_style, 0);
+
+ SendMessage (hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
+ listbox_query (hLB, &answer);
+ listbox_ok (test, sel, answer);
+
+ for (i=0;i<4;i++) {
+ DWORD size = SendMessage (hLB, LB_GETTEXTLEN, i, 0);
+ CHAR *txt;
+ WCHAR *txtw;
+
+ txt = HeapAlloc (GetProcessHeap(), 0, size+1);
+ SendMessageA(hLB, LB_GETTEXT, i, (LPARAM)txt);
+ ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
+
+ txtw = HeapAlloc (GetProcessHeap(), 0, 2*size+2);
+ SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw);
+ WideCharToMultiByte( CP_ACP, 0, txtw, -1, txt, size, NULL, NULL );
+ ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
+
+ HeapFree (GetProcessHeap(), 0, txtw);
+ HeapFree (GetProcessHeap(), 0, txt);
+ }
+
+ /* Confirm the count of items, and that an invalid delete does not remove anything */
+ res = SendMessage (hLB, LB_GETCOUNT, 0, 0);
+ ok((res==4), "Expected 4 items, got %d\n", res);
+ res = SendMessage (hLB, LB_DELETESTRING, -1, 0);
+ ok((res==LB_ERR), "Expected LB_ERR items, got %d\n", res);
+ res = SendMessage (hLB, LB_DELETESTRING, 4, 0);
+ ok((res==LB_ERR), "Expected LB_ERR items, got %d\n", res);
+ res = SendMessage (hLB, LB_GETCOUNT, 0, 0);
+ ok((res==4), "Expected 4 items, got %d\n", res);
+
+ WAIT;
+ DestroyWindow (hLB);
+}
+
+static void check_item_height(void)
+{
+ HWND hLB;
+ HDC hdc;
+ HFONT font;
+ TEXTMETRIC tm;
+ INT itemHeight;
+
+ hLB = create_listbox (0, 0);
+ ok ((hdc = GetDCEx( hLB, 0, DCX_CACHE )) != 0, "Can't get hdc\n");
+ ok ((font = GetCurrentObject(hdc, OBJ_FONT)) != 0, "Can't get the current font\n");
+ ok (GetTextMetrics( hdc, &tm ), "Can't read font metrics\n");
+ ReleaseDC( hLB, hdc);
+
+ ok (SendMessage(hLB, WM_SETFONT, (WPARAM)font, 0) == 0, "Can't set font\n");
+
+ itemHeight = SendMessage(hLB, LB_GETITEMHEIGHT, 0, 0);
+ ok (itemHeight == tm.tmHeight, "Item height wrong, got %d, expecting %ld\n", itemHeight, tm.tmHeight);
+
+ DestroyWindow (hLB);
+}
+
+static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_DRAWITEM:
+ {
+ RECT rc_item, rc_client, rc_clip;
+ DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam;
+
+ trace("%p WM_DRAWITEM %08x %08lx\n", hwnd, wparam, lparam);
+
+ ok(wparam == dis->CtlID, "got wParam=%08x instead of %08x\n",
+ wparam, dis->CtlID);
+ ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType);
+
+ GetClientRect(dis->hwndItem, &rc_client);
+ trace("hwndItem %p client rect (%ld,%ld-%ld,%ld)\n", dis->hwndItem,
+ rc_client.left, rc_client.top, rc_client.right, rc_client.bottom);
+ GetClipBox(dis->hDC, &rc_clip);
+ trace("clip rect (%ld,%ld-%ld,%ld)\n", rc_clip.left, rc_clip.top, rc_clip.right, rc_clip.bottom);
+ ok(EqualRect(&rc_client, &rc_clip), "client rect of the listbox should be equal to the clip box\n");
+
+ trace("rcItem (%ld,%ld-%ld,%ld)\n", dis->rcItem.left, dis->rcItem.top,
+ dis->rcItem.right, dis->rcItem.bottom);
+ SendMessage(dis->hwndItem, LB_GETITEMRECT, dis->itemID, (LPARAM)&rc_item);
+ trace("item rect (%ld,%ld-%ld,%ld)\n", rc_item.left, rc_item.top, rc_item.right, rc_item.bottom);
+ ok(EqualRect(&dis->rcItem, &rc_item), "item rects are not equal\n");
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+static void test_ownerdraw(void)
+{
+ WNDCLASS cls;
+ HWND parent, hLB;
+ INT ret;
+ RECT rc;
+
+ cls.style = 0;
+ cls.lpfnWndProc = main_window_proc;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandle(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursor(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "main_window_class";
+ assert(RegisterClass(&cls));
+
+ parent = CreateWindowEx(0, "main_window_class", NULL,
+ WS_POPUP | WS_VISIBLE,
+ 100, 100, 400, 400,
+ GetDesktopWindow(), 0,
+ GetModuleHandle(0), NULL);
+ assert(parent);
+
+ hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent);
+ assert(hLB);
+
+ UpdateWindow(hLB);
+
+ /* make height short enough */
+ SendMessage(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+ SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1,
+ SWP_NOZORDER | SWP_NOMOVE);
+
+ /* make 0 item invisible */
+ SendMessage(hLB, LB_SETTOPINDEX, 1, 0);
+ ret = SendMessage(hLB, LB_GETTOPINDEX, 0, 0);
+ ok(ret == 1, "wrong top index %d\n", ret);
+
+ SendMessage(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+ trace("item 0 rect (%ld,%ld-%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ ok(!IsRectEmpty(&rc), "empty item rect\n");
+ ok(rc.top < 0, "rc.top is not negative (%ld)\n", rc.top);
+
+ DestroyWindow(hLB);
+ DestroyWindow(parent);
+}
+
+#define listbox_test_query(exp, got) \
+ ok(exp.selected == got.selected, "expected selected %d, got %d\n", exp.selected, got.selected); \
+ ok(exp.anchor == got.anchor, "expected anchor %d, got %d\n", exp.anchor, got.anchor); \
+ ok(exp.caret == got.caret, "expected caret %d, got %d\n", exp.caret, got.caret); \
+ ok(exp.selcount == got.selcount, "expected selcount %d, got %d\n", exp.selcount, got.selcount);
+
+static void test_selection(void)
+{
+ static const struct listbox_stat test_nosel = { 0, LB_ERR, 0, 0 };
+ static const struct listbox_stat test_1 = { 0, LB_ERR, 0, 2 };
+ static const struct listbox_stat test_2 = { 0, LB_ERR, 0, 3 };
+ static const struct listbox_stat test_3 = { 0, LB_ERR, 0, 4 };
+ HWND hLB;
+ struct listbox_stat answer;
+ INT ret;
+
+ trace("testing LB_SELITEMRANGE\n");
+
+ hLB = create_listbox(LBS_EXTENDEDSEL, 0);
+ assert(hLB);
+
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ ret = SendMessage(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
+ ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_1, answer);
+
+ SendMessage(hLB, LB_SETSEL, FALSE, (LPARAM)-1);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ ret = SendMessage(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, 4));
+ ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_3, answer);
+
+ SendMessage(hLB, LB_SETSEL, FALSE, (LPARAM)-1);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ ret = SendMessage(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(-5, 5));
+ ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ SendMessage(hLB, LB_SETSEL, FALSE, (LPARAM)-1);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ ret = SendMessage(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(2, 10));
+ ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_1, answer);
+
+ SendMessage(hLB, LB_SETSEL, FALSE, (LPARAM)-1);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ ret = SendMessage(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(4, 10));
+ ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ SendMessage(hLB, LB_SETSEL, FALSE, (LPARAM)-1);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ ret = SendMessage(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(10, 1));
+ ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_2, answer);
+
+ SendMessage(hLB, LB_SETSEL, FALSE, (LPARAM)-1);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_nosel, answer);
+
+ ret = SendMessage(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, -1));
+ ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret);
+ listbox_query(hLB, &answer);
+ listbox_test_query(test_2, answer);
+
+ DestroyWindow(hLB);
+}
+
+static void test_listbox_height(void)
+{
+ HWND hList;
+ int r, id;
+
+ hList = CreateWindow( "ListBox", "list test", 0,
+ 1, 1, 600, 100, NULL, NULL, NULL, NULL );
+ ok( hList != NULL, "failed to create listbox\n");
+
+ id = SendMessage( hList, LB_ADDSTRING, 0, (LPARAM) "hi");
+ ok( id == 0, "item id wrong\n");
+
+ r = SendMessage( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 20, 0 ));
+ ok( r == 0, "send message failed\n");
+
+ r = SendMessage(hList, LB_GETITEMHEIGHT, 0, 0 );
+ ok( r == 20, "height wrong\n");
+
+ r = SendMessage( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0, 30 ));
+ ok( r == -1, "send message failed\n");
+
+ r = SendMessage(hList, LB_GETITEMHEIGHT, 0, 0 );
+ ok( r == 20, "height wrong\n");
+
+ r = SendMessage( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0x100, 0 ));
+ ok( r == -1, "send message failed\n");
+
+ r = SendMessage(hList, LB_GETITEMHEIGHT, 0, 0 );
+ ok( r == 20, "height wrong\n");
+
+ r = SendMessage( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0xff, 0 ));
+ ok( r == 0, "send message failed\n");
+
+ r = SendMessage(hList, LB_GETITEMHEIGHT, 0, 0 );
+ ok( r == 0xff, "height wrong\n");
+
+ DestroyWindow( hList );
+}
+
+START_TEST(listbox)
+{
+ const struct listbox_test SS =
+/* {add_style} */
+ {{0},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0},
+ { 1, 1, 1, LB_ERR}, {0,0,0,0},
+ { 2, 2, 2, LB_ERR}, {0,0,0,0},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}};
+/* {selected, anchor, caret, selcount}{TODO fields} */
+ const struct listbox_test SS_NS =
+ {{LBS_NOSEL},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0},
+ { 1, 1, 1, LB_ERR}, {0,0,0,0},
+ { 2, 2, 2, LB_ERR}, {0,0,0,0},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}};
+ const struct listbox_test MS =
+ {{LBS_MULTIPLESEL},
+ { 0, LB_ERR, 0, 0}, {0,0,0,0},
+ { 1, 1, 1, 1}, {0,0,0,0},
+ { 2, 1, 2, 1}, {0,0,0,0},
+ { 0, LB_ERR, 0, 2}, {0,0,0,0}};
+ const struct listbox_test MS_NS =
+ {{LBS_MULTIPLESEL | LBS_NOSEL},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0},
+ { 1, 1, 1, LB_ERR}, {0,0,0,0},
+ { 2, 2, 2, LB_ERR}, {0,0,0,0},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}};
+ const struct listbox_test ES =
+ {{LBS_EXTENDEDSEL},
+ { 0, LB_ERR, 0, 0}, {0,0,0,0},
+ { 1, 1, 1, 1}, {0,0,0,0},
+ { 2, 2, 2, 1}, {0,0,0,0},
+ { 0, LB_ERR, 0, 2}, {0,0,0,0}};
+ const struct listbox_test ES_NS =
+ {{LBS_EXTENDEDSEL | LBS_NOSEL},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0},
+ { 1, 1, 1, LB_ERR}, {0,0,0,0},
+ { 2, 2, 2, LB_ERR}, {0,0,0,0},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}};
+ const struct listbox_test EMS =
+ {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL},
+ { 0, LB_ERR, 0, 0}, {0,0,0,0},
+ { 1, 1, 1, 1}, {0,0,0,0},
+ { 2, 2, 2, 1}, {0,0,0,0},
+ { 0, LB_ERR, 0, 2}, {0,0,0,0}};
+ const struct listbox_test EMS_NS =
+ {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0},
+ { 1, 1, 1, LB_ERR}, {0,0,0,0},
+ { 2, 2, 2, LB_ERR}, {0,0,0,0},
+ {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}};
+
+ trace (" Testing single selection...\n");
+ check (SS);
+ trace (" ... with NOSEL\n");
+ check (SS_NS);
+ trace (" Testing multiple selection...\n");
+ check (MS);
+ trace (" ... with NOSEL\n");
+ check (MS_NS);
+ trace (" Testing extended selection...\n");
+ check (ES);
+ trace (" ... with NOSEL\n");
+ check (ES_NS);
+ trace (" Testing extended and multiple selection...\n");
+ check (EMS);
+ trace (" ... with NOSEL\n");
+ check (EMS_NS);
+
+ check_item_height();
+ test_ownerdraw();
+ test_selection();
+ test_listbox_height();
+}
--- /dev/null
+/*
+ * Unit tests for menus
+ *
+ * Copyright 2005 Robert Shearman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#define _WIN32_WINNT 0x0500
+#define WINVER 0x0500
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/test.h"
+
+static ATOM atomMenuCheckClass;
+
+static BOOL (WINAPI *pSetMenuInfo)(HMENU,LPCMENUINFO);
+static BOOL (WINAPI *pGetMenuInfo)(HMENU,LPCMENUINFO);
+
+static LRESULT WINAPI menu_check_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_ENTERMENULOOP:
+ /* mark window as having entered menu loop */
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, TRUE);
+ /* exit menu modal loop
+ * ( A SendMessage does not work on NT3.51 here ) */
+ return PostMessage(hwnd, WM_CANCELMODE, 0, 0);
+ }
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+/* globals to communicate between test and wndproc */
+
+#define MOD_SIZE 10
+#define MOD_NRMENUS 8
+
+ /* menu texts with their sizes */
+static struct {
+ char *text;
+ SIZE size; /* size of text up to any \t */
+ SIZE sc_size; /* size of the short-cut */
+} MOD_txtsizes[] = {
+ { "Pinot &Noir" },
+ { "&Merlot\bF4" },
+ { "Shira&z\tAlt+S" },
+ { "" },
+ { NULL }
+};
+
+unsigned int MOD_maxid;
+RECT MOD_rc[MOD_NRMENUS];
+int MOD_avec, MOD_hic;
+int MOD_odheight;
+SIZE MODsizes[MOD_NRMENUS]= { {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE},
+ {MOD_SIZE, MOD_SIZE},{MOD_SIZE, MOD_SIZE}};
+int MOD_GotDrawItemMsg = FALSE;
+/* wndproc used by test_menu_ownerdraw() */
+static LRESULT WINAPI menu_ownerdraw_wnd_proc(HWND hwnd, UINT msg,
+ WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_MEASUREITEM:
+ {
+ MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)lparam;
+ if( winetest_debug)
+ trace("WM_MEASUREITEM received data %lx size %dx%d\n",
+ pmis->itemData, pmis->itemWidth, pmis->itemHeight);
+ MOD_odheight = pmis->itemHeight;
+ pmis->itemWidth = MODsizes[pmis->itemData].cx;
+ pmis->itemHeight = MODsizes[pmis->itemData].cy;
+ return TRUE;
+ }
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT * pdis;
+ TEXTMETRIC tm;
+ HPEN oldpen;
+ char chrs[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ SIZE sz;
+ int i;
+ pdis = (DRAWITEMSTRUCT *) lparam;
+ if( winetest_debug) {
+ RECT rc;
+ GetMenuItemRect( hwnd, (HMENU)pdis->hwndItem, pdis->itemData ,&rc);
+ trace("WM_DRAWITEM received hwnd %p hmenu %p itemdata %ld item %d rc %ld,%ld-%ld,%ld itemrc: %ld,%ld-%ld,%ld\n",
+ hwnd, (HMENU)pdis->hwndItem, pdis->itemData,
+ pdis->itemID, pdis->rcItem.left, pdis->rcItem.top,
+ pdis->rcItem.right,pdis->rcItem.bottom,
+ rc.left,rc.top,rc.right,rc.bottom);
+ oldpen=SelectObject( pdis->hDC, GetStockObject(
+ pdis->itemState & ODS_SELECTED ? WHITE_PEN :BLACK_PEN));
+ Rectangle( pdis->hDC, pdis->rcItem.left,pdis->rcItem.top,
+ pdis->rcItem.right,pdis->rcItem.bottom );
+ SelectObject( pdis->hDC, oldpen);
+ }
+ /* calculate widths of some menu texts */
+ if( ! MOD_txtsizes[0].size.cx)
+ for(i = 0; MOD_txtsizes[i].text; i++) {
+ char buf[100], *p;
+ RECT rc={0,0,0,0};
+ strcpy( buf, MOD_txtsizes[i].text);
+ if( ( p = strchr( buf, '\t'))) {
+ *p = '\0';
+ DrawText( pdis->hDC, p + 1, -1, &rc,
+ DT_SINGLELINE|DT_CALCRECT);
+ MOD_txtsizes[i].sc_size.cx= rc.right - rc.left;
+ MOD_txtsizes[i].sc_size.cy= rc.bottom - rc.top;
+ }
+ DrawText( pdis->hDC, buf, -1, &rc,
+ DT_SINGLELINE|DT_CALCRECT);
+ MOD_txtsizes[i].size.cx= rc.right - rc.left;
+ MOD_txtsizes[i].size.cy= rc.bottom - rc.top;
+ }
+
+ if( pdis->itemData > MOD_maxid) return TRUE;
+ /* store the rectangl */
+ MOD_rc[pdis->itemData] = pdis->rcItem;
+ /* calculate average character width */
+ GetTextExtentPoint( pdis->hDC, chrs, 52, &sz );
+ MOD_avec = (sz.cx + 26)/52;
+ GetTextMetrics( pdis->hDC, &tm);
+ MOD_hic = tm.tmHeight;
+ MOD_GotDrawItemMsg = TRUE;
+ return TRUE;
+ }
+ case WM_ENTERIDLE:
+ {
+ PostMessage(hwnd, WM_CANCELMODE, 0, 0);
+ return TRUE;
+ }
+
+ }
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+static void register_menu_check_class(void)
+{
+ WNDCLASS wc =
+ {
+ 0,
+ menu_check_wnd_proc,
+ 0,
+ 0,
+ GetModuleHandle(NULL),
+ NULL,
+ LoadCursor(NULL, IDC_ARROW),
+ (HBRUSH)(COLOR_BTNFACE+1),
+ NULL,
+ TEXT("WineMenuCheck"),
+ };
+
+ atomMenuCheckClass = RegisterClass(&wc);
+}
+
+/* demonstrates that windows locks the menu object so that it is still valid
+ * even after a client calls DestroyMenu on it */
+static void test_menu_locked_by_window(void)
+{
+ BOOL ret;
+ HMENU hmenu;
+ HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
+ WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, NULL, NULL);
+ ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
+ hmenu = CreateMenu();
+ ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
+ ret = InsertMenu(hmenu, 0, MF_STRING, 0, TEXT("&Test"));
+ ok(ret, "InsertMenu failed with error %ld\n", GetLastError());
+ ret = SetMenu(hwnd, hmenu);
+ ok(ret, "SetMenu failed with error %ld\n", GetLastError());
+ ret = DestroyMenu(hmenu);
+ ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+
+ ret = DrawMenuBar(hwnd);
+ todo_wine {
+ ok(ret, "DrawMenuBar failed with error %ld\n", GetLastError());
+ }
+ ret = IsMenu(GetMenu(hwnd));
+ ok(!ret, "Menu handle should have been destroyed\n");
+
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
+ /* did we process the WM_INITMENU message? */
+ ret = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ todo_wine {
+ ok(ret, "WM_INITMENU should have been sent\n");
+ }
+
+ DestroyWindow(hwnd);
+}
+
+static void test_menu_ownerdraw(void)
+{
+ int i,j,k;
+ BOOL ret;
+ HMENU hmenu;
+ LONG leftcol;
+ HWND hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
+ WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, NULL, NULL);
+ ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
+ if( !hwnd) return;
+ SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
+ hmenu = CreatePopupMenu();
+ ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
+ if( !hmenu) { DestroyWindow(hwnd);return;}
+ k=0;
+ for( j=0;j<2;j++) /* create columns */
+ for(i=0;i<2;i++) { /* create rows */
+ ret = AppendMenu( hmenu, MF_OWNERDRAW |
+ (i==0 ? MF_MENUBREAK : 0), k, (LPCTSTR) k);
+ k++;
+ ok( ret, "AppendMenu failed for %d\n", k-1);
+ }
+ MOD_maxid = k-1;
+ assert( k <= sizeof(MOD_rc)/sizeof(RECT));
+ /* display the menu */
+ ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+
+ /* columns have a 4 pixel gap between them */
+ ok( MOD_rc[0].right + 4 == MOD_rc[2].left,
+ "item rectangles are not separated by 4 pixels space\n");
+ /* height should be what the MEASUREITEM message has returned */
+ ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
+ "menu item has wrong height: %ld should be %d\n",
+ MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
+ /* no gaps between the rows */
+ ok( MOD_rc[0].bottom - MOD_rc[1].top == 0,
+ "There should not be a space between the rows, gap is %ld\n",
+ MOD_rc[0].bottom - MOD_rc[1].top);
+ /* test the correct value of the item height that was sent
+ * by the WM_MEASUREITEM message */
+ ok( MOD_odheight == HIWORD( GetDialogBaseUnits()) || /* WinNT,2k,XP */
+ MOD_odheight == MOD_hic, /* Win95,98,ME */
+ "Wrong height field in MEASUREITEMSTRUCT, expected %d or %d actual %d\n",
+ HIWORD( GetDialogBaseUnits()), MOD_hic, MOD_odheight);
+ /* test what MF_MENUBREAK did at the first position. Also show
+ * that an MF_SEPARATOR is ignored in the height calculation. */
+ leftcol= MOD_rc[0].left;
+ ModifyMenu( hmenu, 0, MF_BYCOMMAND| MF_OWNERDRAW| MF_SEPARATOR, 0, 0);
+ /* display the menu */
+ ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+ /* left should be 4 pixels less now */
+ ok( leftcol == MOD_rc[0].left + 4,
+ "columns should be 4 pixels to the left (actual %ld).\n",
+ leftcol - MOD_rc[0].left);
+ /* test width */
+ ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
+ "width of owner drawn menu item is wrong. Got %ld expected %d\n",
+ MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
+ /* and height */
+ ok( MOD_rc[0].bottom - MOD_rc[0].top == MOD_SIZE,
+ "Height is incorrect. Got %ld expected %d\n",
+ MOD_rc[0].bottom - MOD_rc[0].top, MOD_SIZE);
+
+ /* test width/height of an ownerdraw menu bar as well */
+ ret = DestroyMenu(hmenu);
+ ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+ hmenu = CreateMenu();
+ ok(hmenu != NULL, "CreateMenu failed with error %ld\n", GetLastError());
+ if( !hmenu) { DestroyWindow(hwnd);return;}
+ MOD_maxid=1;
+ for(i=0;i<2;i++) {
+ ret = AppendMenu( hmenu, MF_OWNERDRAW , i, 0);
+ ok( ret, "AppendMenu failed for %d\n", i);
+ }
+ ret = SetMenu( hwnd, hmenu);
+ UpdateWindow( hwnd); /* hack for wine to draw the window + menu */
+ ok(ret, "SetMenu failed with error %ld\n", GetLastError());
+ /* test width */
+ ok( MOD_rc[0].right - MOD_rc[0].left == 2 * MOD_avec + MOD_SIZE,
+ "width of owner drawn menu item is wrong. Got %ld expected %d\n",
+ MOD_rc[0].right - MOD_rc[0].left , 2*MOD_avec + MOD_SIZE);
+ /* test hight */
+ ok( MOD_rc[0].bottom - MOD_rc[0].top == GetSystemMetrics( SM_CYMENU) - 1,
+ "Height of owner drawn menu item is wrong. Got %ld expected %d\n",
+ MOD_rc[0].bottom - MOD_rc[0].top, GetSystemMetrics( SM_CYMENU) - 1);
+
+ /* clean up */
+ ret = DestroyMenu(hmenu);
+ ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+ DestroyWindow(hwnd);
+}
+
+/* helper for test_menu_bmp_and_string() */
+static void test_mbs_help( int ispop, int hassub, int mnuopt,
+ HWND hwnd, int arrowwidth, int count, HBITMAP hbmp,
+ SIZE bmpsize, char *text, SIZE size, SIZE sc_size)
+{
+ BOOL ret;
+ HMENU hmenu, submenu;
+ MENUITEMINFO mii={ sizeof( MENUITEMINFO )};
+ MENUINFO mi;
+ RECT rc;
+ int hastab, expect;
+ int failed = 0;
+
+ MOD_GotDrawItemMsg = FALSE;
+ mii.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STATE;
+ mii.fType = 0;
+ mii.fState = MF_CHECKED;
+ mii.dwItemData =0;
+ MODsizes[0] = bmpsize;
+ hastab = 0;
+ if( text ) {
+ char *p;
+ mii.fMask |= MIIM_STRING;
+ mii.dwTypeData = text;
+ if( ( p = strchr( text, '\t'))) {
+ hastab = *(p + 1) ? 2 : 1;
+ }
+ }
+ /* tabs don't make sense in menubars */
+ if(hastab && !ispop) return;
+ if( hbmp) {
+ mii.fMask |= MIIM_BITMAP;
+ mii.hbmpItem = hbmp;
+ }
+ submenu = CreateMenu();
+ ok( submenu != 0, "CreateMenu failed with error %ld\n", GetLastError());
+ if( ispop)
+ hmenu = CreatePopupMenu();
+ else
+ hmenu = CreateMenu();
+ ok( hmenu != 0, "Create{Popup}Menu failed with error %ld\n", GetLastError());
+ if( hassub) {
+ mii.fMask |= MIIM_SUBMENU;
+ mii.hSubMenu = submenu;
+ }
+ if( mnuopt) {
+ mi.cbSize = sizeof(mi);
+ mi.fMask = MIM_STYLE;
+ pGetMenuInfo( hmenu, &mi);
+ mi.dwStyle |= mnuopt == 1 ? MNS_NOCHECK : MNS_CHECKORBMP;
+ ret = pSetMenuInfo( hmenu, &mi);
+ ok( ret, "SetMenuInfo failed with error %ld\n", GetLastError());
+ }
+ ret = InsertMenuItem( hmenu, 0, FALSE, &mii);
+ ok( ret, "InsertMenuItem failed with error %ld\n", GetLastError());
+ failed = !ret;
+ if( winetest_debug) {
+ HDC hdc=GetDC(hwnd);
+ RECT rc = {100, 50, 400, 70};
+ char buf[100];
+
+ sprintf( buf,"%d text \"%s\" mnuopt %d", count, text ? text: "(nil)", mnuopt);
+ FillRect( hdc, &rc, (HBRUSH) COLOR_WINDOW);
+ TextOut( hdc, 100, 50, buf, strlen( buf));
+ ReleaseDC( hwnd, hdc);
+ }
+ if(ispop)
+ ret = TrackPopupMenu( hmenu, 0x100, 100,100, 0, hwnd, NULL);
+ else {
+ ret = SetMenu( hwnd, hmenu);
+ ok(ret, "SetMenu failed with error %ld\n", GetLastError());
+ DrawMenuBar( hwnd);
+ }
+ ret = GetMenuItemRect( hwnd, hmenu, 0, &rc);
+ /* check menu width */
+ if( ispop)
+ expect = ( text || hbmp ?
+ 4 + (mnuopt != 1 ? GetSystemMetrics(SM_CXMENUCHECK) : 0)
+ : 0) +
+ arrowwidth + MOD_avec + (hbmp ? bmpsize.cx + 2 : 0) +
+ (text && hastab ? /* TAB space */
+ MOD_avec + ( hastab==2 ? sc_size.cx : 0) : 0) +
+ (text ? 2 + (text[0] ? size.cx :0): 0) ;
+ else
+ expect = !(text || hbmp) ? 0 :
+ ( hbmp ? (text ? 2:0) + bmpsize.cx : 0 ) +
+ (text ? 2 * MOD_avec + (text[0] ? size.cx :0): 0) ;
+ ok( rc.right - rc.left == expect,
+ "menu width wrong, got %ld expected %d\n", rc.right - rc.left, expect);
+ failed = failed || !(rc.right - rc.left == expect);
+ /* check menu height */
+ if( ispop)
+ expect = max( ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 : 0),
+ max( (text ? max( 2 + size.cy, MOD_hic + 4) : 0),
+ (hbmp ? bmpsize.cy + 2 : 0)));
+ else
+ expect = ( !(text || hbmp) ? GetSystemMetrics( SM_CYMENUSIZE)/2 :
+ max( GetSystemMetrics( SM_CYMENU) - 1, (hbmp ? bmpsize.cy : 0)));
+ ok( rc.bottom - rc.top == expect,
+ "menu height wrong, got %ld expected %d (%d)\n",
+ rc.bottom - rc.top, expect, GetSystemMetrics( SM_CYMENU));
+ failed = failed || !(rc.bottom - rc.top == expect);
+ if( hbmp == HBMMENU_CALLBACK && MOD_GotDrawItemMsg) {
+ /* check the position of the bitmap */
+ /* horizontal */
+ expect = ispop ? (4 + ( mnuopt ? 0 : GetSystemMetrics(SM_CXMENUCHECK)))
+ : 3;
+ ok( expect == MOD_rc[0].left,
+ "bitmap left is %ld expected %d\n", MOD_rc[0].left, expect);
+ failed = failed || !(expect == MOD_rc[0].left);
+ /* vertical */
+ expect = (rc.bottom - rc.top - MOD_rc[0].bottom + MOD_rc[0].top) / 2;
+ ok( expect == MOD_rc[0].top,
+ "bitmap top is %ld expected %d\n", MOD_rc[0].top, expect);
+ failed = failed || !(expect == MOD_rc[0].top);
+ }
+ /* if there was a failure, report details */
+ if( failed) {
+ trace("*** count %d text \"%s\" bitmap %p bmsize %ld,%ld textsize %ld+%ld,%ld mnuopt %d hastab %d\n",
+ count, text ? text: "(nil)", hbmp, bmpsize.cx, bmpsize.cy,
+ size.cx, size.cy, sc_size.cx, mnuopt, hastab);
+ trace(" check %d,%d arrow %d avechar %d\n",
+ GetSystemMetrics(SM_CXMENUCHECK ),
+ GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
+ if( hbmp == HBMMENU_CALLBACK)
+ trace( " rc %ld,%ld-%ld,%ld bmp.rc %ld,%ld-%ld,%ld\n",
+ rc.left, rc.top, rc.top, rc.bottom, MOD_rc[0].left,
+ MOD_rc[0].top,MOD_rc[0].right, MOD_rc[0].bottom);
+ }
+ /* clean up */
+ ret = DestroyMenu(submenu);
+ ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+ ret = DestroyMenu(hmenu);
+ ok(ret, "DestroyMenu failed with error %ld\n", GetLastError());
+}
+
+
+static void test_menu_bmp_and_string(void)
+{
+ BYTE bmfill[300];
+ HBITMAP hbm_arrow;
+ BITMAP bm;
+ INT arrowwidth;
+ HWND hwnd;
+ int count, szidx, txtidx, bmpidx, hassub, mnuopt, ispop;
+
+ if( !pGetMenuInfo) return;
+
+ memset( bmfill, 0x55, sizeof( bmfill));
+ hwnd = CreateWindowEx(0, MAKEINTATOM(atomMenuCheckClass), NULL,
+ WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, NULL, NULL);
+ hbm_arrow=LoadBitmap( 0, (CHAR*)OBM_MNARROW);
+ GetObject( hbm_arrow, sizeof(bm), &bm);
+ arrowwidth = bm.bmWidth;
+
+ ok(hwnd != NULL, "CreateWindowEx failed with error %ld\n", GetLastError());
+ if( !hwnd) return;
+ SetWindowLongPtr( hwnd, GWLP_WNDPROC, (LONG)menu_ownerdraw_wnd_proc);
+
+ if( winetest_debug)
+ trace(" check %d,%d arrow %d avechar %d\n",
+ GetSystemMetrics(SM_CXMENUCHECK ),
+ GetSystemMetrics(SM_CYMENUCHECK ),arrowwidth, MOD_avec);
+ count = 0;
+ MOD_maxid = 0;
+ for( ispop=1; ispop >= 0; ispop--){
+ static SIZE bmsizes[]= {
+ {10,10},{38,38},{1,30},{55,5}};
+ for( szidx=0; szidx < sizeof( bmsizes) / sizeof( SIZE); szidx++) {
+ HBITMAP hbm = CreateBitmap( bmsizes[szidx].cx, bmsizes[szidx].cy,1,1,bmfill);
+ HBITMAP bitmaps[] = { HBMMENU_CALLBACK, hbm, NULL };
+ ok( (int)hbm, "CreateBitmap failed err %ld\n", GetLastError());
+ for( txtidx = 0; txtidx < sizeof(MOD_txtsizes)/sizeof(MOD_txtsizes[0]); txtidx++) {
+ for( hassub = 0; hassub < 2 ; hassub++) { /* add submenu item */
+ for( mnuopt = 0; mnuopt < 3 ; mnuopt++){ /* test MNS_NOCHECK/MNS_CHECKORBMP */
+ for( bmpidx = 0; bmpidx <sizeof(bitmaps)/sizeof(HBITMAP); bmpidx++) {
+ /* no need to test NULL bitmaps of several sizes */
+ if( !bitmaps[bmpidx] && szidx > 0) continue;
+ if( !ispop && hassub) continue;
+ test_mbs_help( ispop, hassub, mnuopt,
+ hwnd, arrowwidth, ++count,
+ bitmaps[bmpidx],
+ bmsizes[szidx],
+ MOD_txtsizes[txtidx].text,
+ MOD_txtsizes[txtidx].size,
+ MOD_txtsizes[txtidx].sc_size);
+ }
+ }
+ }
+ }
+ DeleteObject( hbm);
+ }
+ }
+ /* clean up */
+ DestroyWindow(hwnd);
+}
+
+static void test_menu_add_string( void )
+{
+ HMENU hmenu;
+ MENUITEMINFO info;
+ BOOL rc;
+
+ char string[0x80];
+ char string2[0x80];
+
+ char strback[0x80];
+ WCHAR strbackW[0x80];
+ static const WCHAR expectedString[] = {'D', 'u', 'm', 'm', 'y', ' ',
+ 's', 't', 'r', 'i', 'n', 'g', 0};
+
+ hmenu = CreateMenu();
+
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_ID;
+ info.dwTypeData = "blah";
+ info.cch = 6;
+ info.dwItemData = 0;
+ info.wID = 1;
+ info.fState = 0;
+ InsertMenuItem(hmenu, 0, TRUE, &info );
+
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
+ info.dwTypeData = string;
+ info.cch = sizeof string;
+ string[0] = 0;
+ GetMenuItemInfo( hmenu, 0, TRUE, &info );
+
+ ok( !strcmp( string, "blah" ), "menu item name differed\n");
+
+ /* Test combination of ownerdraw and strings with GetMenuItemString(A/W) */
+ strcpy(string, "Dummy string");
+ memset(&info, 0x00, sizeof(info));
+ info.cbSize= sizeof(MENUITEMINFO);
+ info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
+ info.fType= MFT_OWNERDRAW;
+ info.dwTypeData= string;
+ rc = InsertMenuItem( hmenu, 0, TRUE, &info );
+ ok (rc, "InsertMenuItem failed\n");
+
+ strcpy(string,"Garbage");
+ ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
+ ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
+
+ ok (GetMenuStringW( hmenu, 0, (WCHAR *)strbackW, 99, MF_BYPOSITION), "GetMenuStringW on ownerdraw entry failed\n");
+ ok (!lstrcmpW( strbackW, expectedString ), "Menu text from Unicode version incorrect\n");
+
+ /* Just change ftype to string and see what text is stored */
+ memset(&info, 0x00, sizeof(info));
+ info.cbSize= sizeof(MENUITEMINFO);
+ info.fMask= MIIM_FTYPE; /* Set string type */
+ info.fType= MFT_STRING;
+ info.dwTypeData= (char *)0xdeadbeef;
+ rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
+ ok (rc, "SetMenuItemInfo failed\n");
+
+ /* Did we keep the old dwTypeData? */
+ ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
+ ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
+
+ /* Ensure change to bitmap type fails */
+ memset(&info, 0x00, sizeof(info));
+ info.cbSize= sizeof(MENUITEMINFO);
+ info.fMask= MIIM_FTYPE; /* Set as bitmap type */
+ info.fType= MFT_BITMAP;
+ info.dwTypeData= (char *)0xdeadbee2;
+ rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
+ ok (!rc, "SetMenuItemInfo unexpectedly worked\n");
+
+ /* Just change ftype back and ensure data hasn't been freed */
+ info.fType= MFT_OWNERDRAW; /* Set as ownerdraw type */
+ info.dwTypeData= (char *)0xdeadbee3;
+ rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
+ ok (rc, "SetMenuItemInfo failed\n");
+
+ /* Did we keep the old dwTypeData? */
+ ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
+ ok (!strcmp( strback, "Dummy string" ), "Menu text from Ansi version incorrect\n");
+
+ /* Just change string value (not type) */
+ memset(&info, 0x00, sizeof(info));
+ info.cbSize= sizeof(MENUITEMINFO);
+ info.fMask= MIIM_STRING; /* Set typeData */
+ strcpy(string2, "string2");
+ info.dwTypeData= string2;
+ rc = SetMenuItemInfo( hmenu, 0, TRUE, &info );
+ ok (rc, "SetMenuItemInfo failed\n");
+
+ ok (GetMenuString( hmenu, 0, strback, 99, MF_BYPOSITION), "GetMenuString on ownerdraw entry failed\n");
+ ok (!strcmp( strback, "string2" ), "Menu text from Ansi version incorrect\n");
+
+ /* crashes with wine 0.9.5 */
+ memset(&info, 0x00, sizeof(info));
+ info.cbSize= sizeof(MENUITEMINFO);
+ info.fMask= MIIM_FTYPE | MIIM_STRING; /* Set OwnerDraw + typeData */
+ info.fType= MFT_OWNERDRAW;
+ rc = InsertMenuItem( hmenu, 0, TRUE, &info );
+ ok (rc, "InsertMenuItem failed\n");
+ ok (!GetMenuString( hmenu, 0, NULL, 0, MF_BYPOSITION),
+ "GetMenuString on ownerdraw entry succeeded.\n");
+ ok (!GetMenuStringW( hmenu, 0, NULL, 0, MF_BYPOSITION),
+ "GetMenuStringW on ownerdraw entry succeeded.\n");
+
+
+ DestroyMenu( hmenu );
+}
+
+/* define building blocks for the menu item info tests */
+static int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
+{
+ if (n <= 0) return 0;
+ while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
+ return *str1 - *str2;
+}
+
+static WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
+{
+ WCHAR *p = dst;
+ while ((*p++ = *src++));
+ return dst;
+}
+
+
+#define DMIINFF( i, e, field)\
+ ok((int)((i)->field)==(int)((e)->field) || (int)((i)->field)==(0xffff & (int)((e)->field)), \
+ "%s got 0x%x expected 0x%x\n", #field, (int)((i)->field), (int)((e)->field));
+
+#define DUMPMIINF(s,i,e)\
+{\
+ DMIINFF( i, e, fMask)\
+ DMIINFF( i, e, fType)\
+ DMIINFF( i, e, fState)\
+ DMIINFF( i, e, wID)\
+ DMIINFF( i, e, hSubMenu)\
+ DMIINFF( i, e, hbmpChecked)\
+ DMIINFF( i, e, hbmpUnchecked)\
+ DMIINFF( i, e, dwItemData)\
+ DMIINFF( i, e, dwTypeData)\
+ DMIINFF( i, e, cch)\
+ if( s==sizeof(MENUITEMINFOA)) DMIINFF( i, e, hbmpItem)\
+}
+
+/* insert menu item */
+#define TMII_INSMI( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
+ eret1)\
+{\
+ MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
+ HMENU hmenu = CreateMenu();\
+ BOOL ret, stop = FALSE;\
+ SetLastError( 0xdeadbeef);\
+ if(ansi)strcpy( string, init);\
+ else strcpyW( (WCHAR*)string, (WCHAR*)init);\
+ if( ansi) ret = InsertMenuItemA(hmenu, 0, TRUE, &info1 );\
+ else ret = InsertMenuItemW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
+ if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
+ stop = TRUE;\
+ } else ok( (eret1)==ret,"InsertMenuItem failed, err %ld\n",GetLastError());\
+
+
+/* GetMenuItemInfo + GetMenuString */
+#define TMII_GMII( a2,b2,c2,d2,e2,f2,g2,h2,i2,j2,k2,l2,m2,n2,\
+ a3,b3,c3,d3,e3,f3,g3,h3,i3,j3,k3,l3,m3,n3,\
+ expname, eret2, eret3)\
+{\
+ MENUITEMINFOA info2A=a2 b2,c2,d2,e2,f2,(void*)g2,(void*)h2,(void*)i2,j2,(void*)k2,l2,(void*)m2 n2;\
+ MENUITEMINFOA einfoA=a3 b3,c3,d3,e3,f3,(void*)g3,(void*)h3,(void*)i3,j3,(void*)k3,l3,(void*)m3 n3;\
+ MENUITEMINFOA *info2 = &info2A;\
+ MENUITEMINFOA *einfo = &einfoA;\
+ MENUITEMINFOW *info2W = (MENUITEMINFOW *)&info2A;\
+ if( !stop) {\
+ ret = ansi ? GetMenuItemInfoA( hmenu, 0, TRUE, info2 ) :\
+ GetMenuItemInfoW( hmenu, 0, TRUE, info2W );\
+ if( !(eret2)) ok( (eret2)==ret,"GetMenuItemInfo should have failed.\n");\
+ else { \
+ ok( (eret2)==ret,"GetMenuItemInfo failed, err %ld\n",GetLastError());\
+ ret = memcmp( info2, einfo, sizeof einfoA);\
+ /* ok( ret==0, "Got wrong menu item info data\n");*/\
+ if( ret) DUMPMIINF(info2A.cbSize, &info2A, &einfoA)\
+ if( einfo->dwTypeData == string) {\
+ if(ansi) ok( !strncmp( expname, info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
+ einfo->dwTypeData ? einfo->dwTypeData: "");\
+ else ok( !strncmpW( (WCHAR*)expname, (WCHAR*)info2->dwTypeData, einfo->cch ), "menu item name differed \"%s\"\n",\
+ einfo->dwTypeData ? einfo->dwTypeData: "");\
+ ret = ansi ? GetMenuStringA( hmenu, 0, string, 80, MF_BYPOSITION) :\
+ GetMenuStringW( hmenu, 0, string, 80, MF_BYPOSITION);\
+ if( (eret3)){\
+ ok( ret, "GetMenuString failed, err %ld\n",GetLastError());\
+ }else\
+ ok( !ret, "GetMenuString should have failed\n");\
+ }\
+ }\
+ }\
+}
+
+#define TMII_DONE \
+ RemoveMenu(hmenu, 0, TRUE );\
+ DestroyMenu( hmenu );\
+ DestroyMenu( submenu );\
+submenu = CreateMenu();\
+}
+/* modify menu */
+#define TMII_MODM( flags, id, data, eret )\
+if( !stop) {\
+ if(ansi)ret = ModifyMenuA( hmenu, 0, flags, (UINT_PTR)id, (char*)data);\
+ else ret = ModifyMenuW( hmenu, 0, flags, (UINT_PTR)id, (WCHAR*)data);\
+ if( !(eret)) ok( (eret)==ret,"ModifyMenuA should have failed.\n");\
+ else ok( (eret)==ret,"ModifyMenuA failed, err %ld\n",GetLastError());\
+}
+
+/* SetMenuItemInfo */
+#define TMII_SMII( a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,\
+ eret1)\
+if( !stop) {\
+ MENUITEMINFOA info1=a1 b1,c1,d1,e1,f1,(void*)g1,(void*)h1,(void*)i1,j1,(void*)k1,l1,(void*)m1 n1;\
+ SetLastError( 0xdeadbeef);\
+ if(ansi)strcpy( string, init);\
+ else strcpyW( (WCHAR*)string, (WCHAR*)init);\
+ if( ansi) ret = SetMenuItemInfoA(hmenu, 0, TRUE, &info1 );\
+ else ret = SetMenuItemInfoW(hmenu, 0, TRUE, (MENUITEMINFOW*)&info1 );\
+ if( !(eret1)) { ok( (eret1)==ret,"InsertMenuItem should have failed.\n");\
+ stop = TRUE;\
+ } else ok( (eret1)==ret,"InsertMenuItem failed, err %ld\n",GetLastError());\
+}
+
+
+
+#define OK 1
+#define ER 0
+
+
+static void test_menu_iteminfo( void )
+{
+ int S=sizeof( MENUITEMINFOA);
+ int ansi = TRUE;
+ char txtA[]="wine";
+ char initA[]="XYZ";
+ char emptyA[]="";
+ WCHAR txtW[]={'W','i','n','e',0};
+ WCHAR initW[]={'X','Y','Z',0};
+ WCHAR emptyW[]={0};
+ void *txt, *init, *empty, *string;
+ HBITMAP hbm = CreateBitmap(1,1,1,1,NULL);
+ char stringA[0x80];
+ HMENU submenu=CreateMenu();
+
+ do {
+ if( ansi) {txt=txtA;init=initA;empty=emptyA;string=stringA;}
+ else {txt=txtW;init=initW;empty=emptyW;string=stringA;}
+ trace( "%s string %p hbm %p txt %p\n", ansi ? "ANSI tests: " : "Unicode tests:", string, hbm, txt);
+ /* test all combinations of MFT_STRING, MFT_OWNERDRAW and MFT_BITMAP */
+ /* (since MFT_STRING is zero, there are four of them) */
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
+ empty, OK, ER )
+ TMII_DONE
+ /* not enough space for name*/
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
+ {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, NULL, 4, 0, },
+ empty, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 5, -9, },
+ {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 4, -9, },
+ {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 3, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, NULL, 0, -9, },
+ {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ /* cannot combine MIIM_TYPE with some other flags */
+ TMII_INSMI( {, S, MIIM_TYPE|MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
+ TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ empty, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_STRING, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ empty, ER, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, ER)
+ TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ empty, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ empty, ER, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE|MIIM_BITMAP, MFT_BITMAP, -1, -1, -1, -1, -1, -1, hbm, 6, hbm, }, ER)
+ TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ empty, OK, OK )
+ TMII_DONE
+ /* but succeeds with some others */
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE|MIIM_SUBMENU, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_STATE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE|MIIM_STATE, MFT_STRING, 0, -9, 0, -9, -9, -9, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -1, 888, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE|MIIM_ID, MFT_STRING, -9, 888, 0, -9, -9, -9, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -1, -1, -1, -1, -1, 999, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING, -9, -9, 0, -9, -9, 999, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ /* to be continued */
+ /* set text with MIIM_TYPE and retrieve with MIIM_STRING */
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, -9, },
+ txt, OK, OK )
+ TMII_DONE
+ /* set text with MIIM_TYPE and retrieve with MIIM_STRING; MFT_OWNERDRAW causes an empty string */
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, -9, },
+ init, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
+ init, OK, OK )
+ TMII_DONE
+ /* contrary to MIIM_TYPE,you can set the text for an owner draw menu */
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
+ txt, OK, OK )
+ TMII_DONE
+ /* same but retrieve with MIIM_TYPE */
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 0, -9, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
+ empty, OK, ER )
+ TMII_DONE
+
+ /* How is that with bitmaps? */
+ TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_BITMAP|MIIM_FTYPE, 0, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
+ init, OK, ER )
+ TMII_DONE
+ /* MIIM_BITMAP does not like MFT_BITMAP */
+ TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, ER)
+ TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ init, OK, OK )
+ TMII_DONE
+ /* no problem with OWNERDRAWN */
+ TMII_INSMI( {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
+ init, OK, ER )
+ TMII_DONE
+ /* setting MFT_BITMAP with MFT_FTYPE fails anyway */
+ TMII_INSMI( {, S, MIIM_FTYPE, MFT_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, ER)
+ TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ empty, OK, OK )
+ TMII_DONE
+
+ /* menu with submenu */
+ TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
+ init, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, empty, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_SUBMENU, -9, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
+ init, OK, ER )
+ TMII_DONE
+ /* menu with submenu, without MIIM_SUBMENU the submenufield is cleared */
+ TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, submenu, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 0, -9, },
+ empty, OK, ER )
+ TMII_GMII ( {, S, MIIM_SUBMENU|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, submenu, -9, -9, -9, string, 80, -9, },
+ empty, OK, ER )
+ TMII_DONE
+ /* menu with invalid submenu */
+ TMII_INSMI( {, S, MIIM_SUBMENU|MIIM_FTYPE, MFT_STRING, -1, -1, 999, -1, -1, -1, txt, 0, -1, }, ER)
+ TMII_GMII ( {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ {, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, },
+ init, OK, ER )
+ TMII_DONE
+
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_SEPARATOR, 0, 0, 0, 0, 0, 0, txt, 0, 0, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, hbm, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_BITMAP|MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, hbm, 0, hbm, },
+ empty, OK, ER )
+ TMII_DONE
+ /* SEPARATOR and STRING go well together */
+ /* BITMAP and STRING go well together */
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
+ txt, OK, OK )
+ TMII_DONE
+ /* BITMAP, SEPARATOR and STRING go well together */
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, hbm, },
+ txt, OK, OK )
+ TMII_DONE
+ /* last two tests, but use MIIM_TYPE to retrieve info */
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
+ txt, OK, OK )
+ TMII_DONE
+ /* same three with MFT_OWNERDRAW */
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_BITMAP|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, hbm, 4, hbm, },
+ txt, OK, OK )
+ TMII_DONE
+
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE|MIIM_ID, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ /* test with modifymenu: string is preserved after seting OWNERDRAW */
+ TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_MODM( MFT_OWNERDRAW, -1, 787, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 787, string, 4, -9, },
+ txt, OK, OK )
+ TMII_DONE
+ /* same with bitmap: now the text is cleared */
+ TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_MODM( MFT_BITMAP, 545, hbm, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_BITMAP, -9, 545, 0, -9, -9, -9, string, 0, hbm, },
+ empty, OK, ER )
+ TMII_DONE
+ /* start with bitmap: now setting text clears it (though he flag is raised) */
+ TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 0, 0, -9, -9, -9, string, 0, hbm, },
+ empty, OK, ER )
+ TMII_MODM( MFT_STRING, 545, txt, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ /*repeat with text NULL */
+ TMII_INSMI( {, S, MIIM_BITMAP, MFT_STRING, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_MODM( MFT_STRING, 545, NULL, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_SEPARATOR, -9, 545, 0, -9, -9, -9, string, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ /* repeat with text "" */
+ TMII_INSMI( {, S, MIIM_BITMAP, -1 , -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_MODM( MFT_STRING, 545, empty, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_ID, MFT_STRING, -9, 545, 0, -9, -9, -9, string, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ /* start with bitmap: set ownerdraw */
+ TMII_INSMI( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_MODM( MFT_OWNERDRAW, -1, 232, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP|MIIM_DATA, MFT_OWNERDRAW, -9, -9, 0, -9, -9, 232, string, 0, hbm, },
+ empty, OK, ER )
+ TMII_DONE
+ /* ask nothing */
+ TMII_INSMI( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, 0, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, 0, -9, -9, -9, 0, -9, -9, -9, string, 80, -9, },
+ init, OK, OK )
+ TMII_DONE
+ /* some tests with small cbSize: the hbmpItem is to be ignored */
+ TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 0, NULL, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S - 4, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, NULL, },
+ init, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S - 4, MIIM_STRING|MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ TMII_INSMI( {, S - 4, MIIM_FTYPE|MIIM_STRING|MIIM_BITMAP, MFT_SEPARATOR|MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, txt, 6, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_SEPARATOR|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, NULL, 4, NULL, },
+ txt, OK, OK )
+ TMII_DONE
+ /* MIIM_TYPE by itself does not get/set the dwItemData for OwnerDrawn menus */
+ TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 343, 0, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, 0, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING|MFT_OWNERDRAW, -1, -1, -1, -1, -1, 343, txt, 0, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_TYPE|MIIM_DATA, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE|MIIM_DATA, MFT_STRING|MFT_OWNERDRAW, -9, -9, 0, -9, -9, 0, 0, 0, 0, },
+ empty, OK, ER )
+ TMII_DONE
+ /* set a string menu to ownerdraw with MIIM_TYPE */
+ TMII_INSMI( {, S, MIIM_TYPE, MFT_STRING, -2, -2, -2, -2, -2, -2, txt, -2, -2, }, OK)
+ TMII_SMII( {, S, MIIM_TYPE, MFT_OWNERDRAW, -1, -1, -1, -1, -1, -1, -1, -1, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_OWNERDRAW, -9, -9, 0, -9, -9, -9, string, 4, -9, },
+ txt, OK, OK )
+ TMII_DONE
+ /* test with modifymenu add submenu */
+ TMII_INSMI( {, S, MIIM_STRING, MFT_STRING, -1, -1, -1, -1, -1, -1, txt, 0, -1, }, OK)
+ TMII_MODM( MF_POPUP, submenu, txt, OK)
+ TMII_GMII ( {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_FTYPE|MIIM_STRING|MIIM_SUBMENU, MFT_STRING, -9, -9, submenu, -9, -9, -9, string, 4, -9, },
+ txt, OK, OK )
+ TMII_GMII ( {, S, MIIM_TYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_TYPE, MFT_STRING, -9, -9, 0, -9, -9, -9, string, 4, 0, },
+ txt, OK, OK )
+ TMII_DONE
+ /* MFT_SEPARATOR bit is kept when the text is added */
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+ TMII_SMII( {, S, MIIM_STRING, -1, -1, -1, -1, -1, -1, -1, txt, -1, -1, }, OK)
+ TMII_GMII ( {, S, MIIM_STRING|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_STRING|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 4, -9, },
+ txt, OK, OK )
+ TMII_DONE
+ /* MFT_SEPARATOR bit is kept when bitmap is added */
+ TMII_INSMI( {, S, MIIM_STRING|MIIM_FTYPE, MFT_STRING, -1, -1, -1, -1, -1, -1, NULL, 0, -1, }, OK)
+ TMII_SMII( {, S, MIIM_BITMAP, -1, -1, -1, -1, -1, -1, -1, -1, -1, hbm, }, OK)
+ TMII_GMII ( {, S, MIIM_BITMAP|MIIM_FTYPE, -9, -9, -9, -9, -9, -9, -9, string, 80, -9, },
+ {, S, MIIM_BITMAP|MIIM_FTYPE, MFT_SEPARATOR, -9, -9, 0, -9, -9, -9, string, 80, hbm, },
+ init, OK, ER )
+ TMII_DONE
+
+ } while( !(ansi = !ansi) );
+ DeleteObject( hbm);
+}
+
+/*
+ The following tests try to confirm the algorithm used to return the menu items
+ when there is a collision between a menu item and a popup menu
+ */
+void test_menu_search_bycommand( void )
+{
+ HMENU hmenu, hmenuSub, hmenuSub2;
+ MENUITEMINFO info;
+ BOOL rc;
+ UINT id;
+ char strback[0x80];
+ char strIn[0x80];
+
+ /* Case 1: Menu containing a menu item */
+ hmenu = CreateMenu();
+
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
+ info.fType = MFT_STRING;
+ strcpy(strIn, "Case 1 MenuItem");
+ info.dwTypeData = strIn;
+ info.wID = (UINT) 0x1234;
+
+ rc = InsertMenuItem(hmenu, 0, TRUE, &info );
+ ok (rc, "Inserting the menuitem failed\n");
+
+ id = GetMenuItemID(hmenu, 0);
+ ok (id == 0x1234, "Getting the menuitem id failed(gave %x)\n", id);
+
+ /* Confirm the menuitem was given the id supplied (getting by position) */
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+
+ rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
+ ok (rc, "Getting the menu items info failed\n");
+ ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
+ ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
+
+ /* Search by id - Should return the item */
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+ rc = GetMenuItemInfo(hmenu, 0x1234, FALSE, &info); /* Get by ID */
+
+ ok (rc, "Getting the menu items info failed\n");
+ ok (info.wID == 0x1234, "IDs differ for the menuitem\n");
+ ok (!strcmp(info.dwTypeData, "Case 1 MenuItem"), "Returned item has wrong label\n");
+
+ DestroyMenu( hmenu );
+
+ /* Case 2: Menu containing a popup menu */
+ hmenu = CreateMenu();
+ hmenuSub = CreateMenu();
+
+ strcpy(strIn, "Case 2 SubMenu");
+ rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, strIn);
+ ok (rc, "Inserting the popup menu into the main menu failed\n");
+
+ id = GetMenuItemID(hmenu, 0);
+ ok (id == -1, "Getting the menuitem id unexpectedly worked (gave %x)\n", id);
+
+ /* Confirm the menuitem itself was given an id the same as the HMENU, (getting by position) */
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+ info.wID = 0xdeadbeef;
+
+ rc = GetMenuItemInfo(hmenu, 0, TRUE, &info); /* Get by position */
+ ok (rc, "Getting the menu items info failed\n");
+ ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the menuitem\n");
+ ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
+
+ /* Search by id - returns the popup menu itself */
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+ rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
+
+ ok (rc, "Getting the menu items info failed\n");
+ ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
+ ok (!strcmp(info.dwTypeData, "Case 2 SubMenu"), "Returned item has wrong label\n");
+
+ /*
+ Now add an item after it with the same id
+ */
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
+ info.fType = MFT_STRING;
+ strcpy(strIn, "Case 2 MenuItem 1");
+ info.dwTypeData = strIn;
+ info.wID = (UINT_PTR) hmenuSub;
+ rc = InsertMenuItem(hmenu, -1, TRUE, &info );
+ ok (rc, "Inserting the menuitem failed\n");
+
+ /* Search by id - returns the item which follows the popup menu */
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+ rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
+
+ ok (rc, "Getting the menu items info failed\n");
+ ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
+ ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 1"), "Returned item has wrong label (%s)\n", info.dwTypeData);
+
+ /*
+ Now add an item before the popup (with the same id)
+ */
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
+ info.fType = MFT_STRING;
+ strcpy(strIn, "Case 2 MenuItem 2");
+ info.dwTypeData = strIn;
+ info.wID = (UINT_PTR) hmenuSub;
+ rc = InsertMenuItem(hmenu, 0, TRUE, &info );
+ ok (rc, "Inserting the menuitem failed\n");
+
+ /* Search by id - returns the item which precedes the popup menu */
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STRING;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+ rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info); /* Get by ID */
+
+ ok (rc, "Getting the menu items info failed\n");
+ ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for the popup menu\n");
+ ok (!strcmp(info.dwTypeData, "Case 2 MenuItem 2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
+
+ DestroyMenu( hmenu );
+ DestroyMenu( hmenuSub );
+
+ /*
+ Case 3: Menu containing a popup menu which in turn
+ contains 2 items with the same id as the popup itself
+ */
+
+ hmenu = CreateMenu();
+ hmenuSub = CreateMenu();
+
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
+ info.fType = MFT_STRING;
+ info.dwTypeData = "MenuItem";
+ info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
+
+ rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
+ ok (rc, "Inserting the popup menu into the main menu failed\n");
+
+ rc = InsertMenuItem(hmenuSub, 0, TRUE, &info );
+ ok (rc, "Inserting the sub menu menuitem failed\n");
+
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
+ info.fType = MFT_STRING;
+ info.dwTypeData = "MenuItem 2";
+ info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
+
+ rc = InsertMenuItem(hmenuSub, 1, TRUE, &info );
+ ok (rc, "Inserting the sub menu menuitem 2 failed\n");
+
+ /* Prove that you can't query the id of a popup directly (By position) */
+ id = GetMenuItemID(hmenu, 0);
+ ok (id == -1, "Getting the sub menu id should have failed because its a popup (gave %x)\n", id);
+
+ /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_STRING | MIIM_ID;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+
+ rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
+ ok (rc, "Getting the menus info failed\n");
+ ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
+ ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
+ DestroyMenu( hmenu );
+ DestroyMenu( hmenuSub );
+
+ /*
+ Case 4: Menu containing 2 popup menus, the second
+ contains 2 items with the same id as the first popup menu
+ */
+ hmenu = CreateMenu();
+ hmenuSub = CreateMenu();
+ hmenuSub2 = CreateMenu();
+
+ rc = InsertMenu(hmenu, 0, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub, "Submenu");
+ ok (rc, "Inserting the popup menu into the main menu failed\n");
+
+ rc = InsertMenu(hmenu, 1, MF_BYPOSITION | MF_POPUP | MF_STRING, (UINT_PTR)hmenuSub2, "Submenu2");
+ ok (rc, "Inserting the popup menu into the main menu failed\n");
+
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
+ info.fType = MFT_STRING;
+ info.dwTypeData = "MenuItem";
+ info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
+
+ rc = InsertMenuItem(hmenuSub2, 0, TRUE, &info );
+ ok (rc, "Inserting the sub menu menuitem failed\n");
+
+ memset( &info, 0, sizeof info );
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_ID;
+ info.fType = MFT_STRING;
+ info.dwTypeData = "MenuItem 2";
+ info.wID = (UINT_PTR) hmenuSub; /* Enforce id collisions with the hmenu of the popup submenu*/
+
+ rc = InsertMenuItem(hmenuSub2, 1, TRUE, &info );
+ ok (rc, "Inserting the sub menu menuitem 2 failed\n");
+
+ /* Prove getting the item info via ID returns the first item (not the popup or 2nd item)*/
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_STRING | MIIM_ID;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+
+ rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub, FALSE, &info);
+ ok (rc, "Getting the menus info failed\n");
+ ok (info.wID == (UINT_PTR)hmenuSub, "IDs differ for popup menu\n");
+ ok (!strcmp(info.dwTypeData, "MenuItem"), "Returned item has wrong label (%s)\n", info.dwTypeData);
+
+ memset( &info, 0, sizeof info );
+ strback[0] = 0x00;
+ info.cbSize = sizeof(MENUITEMINFO);
+ info.fMask = MIIM_STRING | MIIM_ID;
+ info.dwTypeData = strback;
+ info.cch = sizeof(strback);
+
+ rc = GetMenuItemInfo(hmenu, (UINT_PTR)hmenuSub2, FALSE, &info);
+ ok (rc, "Getting the menus info failed\n");
+ ok (info.wID == (UINT)hmenuSub2, "IDs differ for popup menu\n");
+ ok (!strcmp(info.dwTypeData, "Submenu2"), "Returned item has wrong label (%s)\n", info.dwTypeData);
+}
+
+START_TEST(menu)
+{
+ pSetMenuInfo =
+ (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "SetMenuInfo" );
+ pGetMenuInfo =
+ (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetMenuInfo" );
+
+ register_menu_check_class();
+
+ test_menu_locked_by_window();
+ test_menu_ownerdraw();
+ test_menu_add_string();
+ test_menu_iteminfo();
+ test_menu_search_bycommand();
+ test_menu_bmp_and_string();
+}
--- /dev/null
+/*
+ * Unit tests for monitor APIs
+ *
+ * Copyright 2005 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#define _WIN32_WINNT 0x0500
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+static HMODULE hdll;
+static BOOL (WINAPI *pEnumDisplayDevicesA)(LPCSTR,DWORD,LPDISPLAY_DEVICEA,DWORD);
+static BOOL (WINAPI *pEnumDisplayMonitors)(HDC,LPRECT,MONITORENUMPROC,LPARAM);
+static BOOL (WINAPI *pGetMonitorInfoA)(HMONITOR,LPMONITORINFO);
+
+static void init_function_pointers(void)
+{
+ hdll = GetModuleHandleA("user32.dll");
+
+ if(hdll)
+ {
+ pEnumDisplayDevicesA = (void*)GetProcAddress(hdll, "EnumDisplayDevicesA");
+ pEnumDisplayMonitors = (void*)GetProcAddress(hdll, "EnumDisplayMonitors");
+ pGetMonitorInfoA = (void*)GetProcAddress(hdll, "GetMonitorInfoA");
+ }
+}
+
+static BOOL CALLBACK monitor_enum_proc(HMONITOR hmon, HDC hdc, LPRECT lprc,
+ LPARAM lparam)
+{
+ MONITORINFOEXA mi;
+ char *primary = (char *)lparam;
+
+ mi.cbSize = sizeof(mi);
+
+ ok(pGetMonitorInfoA(hmon, (MONITORINFO*)&mi), "GetMonitorInfo failed\n");
+ if(mi.dwFlags == MONITORINFOF_PRIMARY)
+ strcpy(primary, mi.szDevice);
+
+ return TRUE;
+}
+
+static void test_enumdisplaydevices(void)
+{
+ DISPLAY_DEVICEA dd;
+ char primary_device_name[32];
+ char primary_monitor_device_name[32];
+ DWORD primary_num = -1, num = 0;
+
+ dd.cb = sizeof(dd);
+ if(pEnumDisplayDevicesA == NULL) return;
+ while(1)
+ {
+ BOOL ret;
+ HDC dc;
+ ret = pEnumDisplayDevicesA(NULL, num, &dd, 0);
+ ok(ret || num != 0, "EnumDisplayDevices fails with num == 0\n");
+ if(!ret) break;
+ if(dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
+ {
+ strcpy(primary_device_name, dd.DeviceName);
+ primary_num = num;
+ }
+ if(dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
+ {
+ /* test creating DC */
+ dc = CreateDCA(dd.DeviceName, NULL, NULL, NULL);
+ ok(dc != NULL, "Failed to CreateDC(\"%s\") err=%ld\n", dd.DeviceName, GetLastError());
+ DeleteDC(dc);
+ }
+ num++;
+ }
+ ok(primary_num != -1, "Didn't get the primary device\n");
+
+ if(pEnumDisplayMonitors && pGetMonitorInfoA) {
+ ok(pEnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM)primary_monitor_device_name),
+ "EnumDisplayMonitors failed\n");
+
+ ok(!strcmp(primary_monitor_device_name, primary_device_name),
+ "monitor device name %s, device name %s\n", primary_monitor_device_name,
+ primary_device_name);
+ }
+}
+
+
+START_TEST(monitor)
+{
+ init_function_pointers();
+ test_enumdisplaydevices();
+}
--- /dev/null
+/*
+ * Unit tests for window message handling
+ *
+ * Copyright 1999 Ove Kaaven
+ * Copyright 2003 Dimitrie O. Paun
+ * Copyright 2004, 2005 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#define WINVER 0x0500
+#define _WIN32_WINNT 0x0501 /* For WM_CHANGEUISTATE,QS_RAWINPUT */
+
+#include "windows.h"
+
+#include "wine/test.h"
+
+#define OBJID_QUERYCLASSNAMEIDX -12
+#define OBJID_NATIVEOM -16
+
+#define MDI_FIRST_CHILD_ID 2004
+
+/* undocumented SWP flags - from SDK 3.1 */
+#define SWP_NOCLIENTSIZE 0x0800
+#define SWP_NOCLIENTMOVE 0x1000
+
+#define WND_PARENT_ID 1
+#define WND_POPUP_ID 2
+#define WND_CHILD_ID 3
+
+static BOOL test_DestroyWindow_flag;
+static HWINEVENTHOOK hEvent_hook;
+
+static HWND (WINAPI *pGetAncestor)(HWND,UINT);
+
+/*
+FIXME: add tests for these
+Window Edge Styles (Win31/Win95/98 look), in order of precedence:
+ WS_EX_DLGMODALFRAME: double border, WS_CAPTION allowed
+ WS_THICKFRAME: thick border
+ WS_DLGFRAME: double border, WS_CAPTION not allowed (but possibly shown anyway)
+ WS_BORDER (default for overlapped windows): single black border
+ none (default for child (and popup?) windows): no border
+*/
+
+typedef enum {
+ sent=0x1,
+ posted=0x2,
+ parent=0x4,
+ wparam=0x8,
+ lparam=0x10,
+ defwinproc=0x20,
+ beginpaint=0x40,
+ optional=0x80,
+ hook=0x100,
+ winevent_hook=0x200
+} msg_flags_t;
+
+struct message {
+ UINT message; /* the WM_* code */
+ msg_flags_t flags; /* message props */
+ WPARAM wParam; /* expected value of wParam */
+ LPARAM lParam; /* expected value of lParam */
+};
+
+/* Empty message sequence */
+static const struct message WmEmptySeq[] =
+{
+ { 0 }
+};
+/* CreateWindow (for overlapped window, not initially visible) (16/32) */
+static const struct message WmCreateOverlappedSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_GETMINMAXINFO, sent },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a not visible overlapped window.
+ */
+static const struct message WmSWP_ShowOverlappedSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE }, /* Win9x: SWP_NOSENDCHANGING */
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { WM_IME_NOTIFY, sent|defwinproc|optional },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ /* Win9x adds SWP_NOZORDER below */
+ { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+/* SetWindowPos(SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a visible overlapped window.
+ */
+static const struct message WmSWP_HideOverlappedSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+
+/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE)
+ * for a visible overlapped window.
+ */
+static const struct message WmSWP_ResizeSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { WM_NCPAINT, sent|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
+ { WM_NCCALCSIZE, sent|wparam|optional, TRUE },
+ { WM_NCPAINT, sent|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam|optional, 0, 0 }, /* XP sends a duplicate */
+ { 0 }
+};
+
+/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE)
+ * for a visible popup window.
+ */
+static const struct message WmSWP_ResizePopupSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { WM_NCPAINT, sent|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
+ { WM_NCCALCSIZE, sent|wparam|optional, TRUE },
+ { WM_NCPAINT, sent|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+
+/* SetWindowPos(SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE)
+ * for a visible overlapped window.
+ */
+static const struct message WmSWP_MoveSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE },
+ { WM_NCPAINT, sent|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOCLIENTSIZE },
+ { WM_MOVE, sent|defwinproc|wparam, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+
+/* SetWindowPos(SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|
+ SWP_NOZORDER|SWP_FRAMECHANGED)
+ * for a visible overlapped window with WS_CLIPCHILDREN style set.
+ */
+static const struct message WmSWP_FrameChanged_clip[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam|parent, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED },
+ { WM_NCCALCSIZE, sent|wparam|parent, 1 },
+ { WM_NCPAINT, sent|parent }, /* wparam != 1 */
+ { WM_GETTEXT, sent|parent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|parent|optional }, /* FIXME: remove optional once Wine is fixed */
+ { WM_NCPAINT, sent }, /* wparam != 1 */
+ { WM_ERASEBKGND, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam|parent, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_PAINT, sent },
+ { 0 }
+};
+/* SetWindowPos(SWP_NOSIZE|SWP_NOMOVE|SWP_DEFERERASE|SWP_NOACTIVATE|
+ SWP_NOZORDER|SWP_FRAMECHANGED)
+ * for a visible overlapped window.
+ */
+static const struct message WmSWP_FrameChangedDeferErase[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam|parent, SWP_NOSIZE|SWP_NOMOVE|SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED },
+ { WM_NCCALCSIZE, sent|wparam|parent, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam|parent, SWP_NOSIZE|SWP_NOMOVE|SWP_DEFERERASE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_PAINT, sent|parent },
+ { WM_NCPAINT, sent|beginpaint|parent }, /* wparam != 1 */
+ { WM_GETTEXT, sent|beginpaint|parent|defwinproc|optional },
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|beginpaint }, /* wparam != 1 */
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+/* SetWindowPos(SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|
+ SWP_NOZORDER|SWP_FRAMECHANGED)
+ * for a visible overlapped window without WS_CLIPCHILDREN style set.
+ */
+static const struct message WmSWP_FrameChanged_noclip[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam|parent, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED },
+ { WM_NCCALCSIZE, sent|wparam|parent, 1 },
+ { WM_NCPAINT, sent|parent }, /* wparam != 1 */
+ { WM_GETTEXT, sent|parent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|parent|optional }, /* FIXME: remove optional once Wine is fixed */
+ { WM_WINDOWPOSCHANGED, sent|wparam|parent, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|beginpaint }, /* wparam != 1 */
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+/* ShowWindow(SW_SHOW) for a not visible overlapped window */
+static const struct message WmShowOverlappedSeq[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { WM_IME_NOTIFY, sent|defwinproc|optional },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ /* Win9x adds SWP_NOZORDER below */
+ { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+#if 0 /* CreateWindow/ShowWindow(SW_SHOW) also generates WM_SIZE/WM_MOVE
+ * messages. Does that mean that CreateWindow doesn't set initial
+ * window dimensions for overlapped windows?
+ */
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+#endif
+ { 0 }
+};
+/* ShowWindow(SW_SHOWMAXIMIZED) for a not visible overlapped window */
+static const struct message WmShowMaxOverlappedSeq[] = {
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|0x8000 },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { WM_IME_NOTIFY, sent|defwinproc|optional },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ /* Win9x adds SWP_NOZORDER below */
+ { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+/* ShowWindow(SW_HIDE) for a visible overlapped window */
+static const struct message WmHideOverlappedSeq[] = {
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent|optional }, /* XP doesn't send it */
+ { WM_MOVE, sent|optional }, /* XP doesn't send it */
+ { WM_NCACTIVATE, sent|wparam, 0 },
+ { WM_ACTIVATE, sent|wparam, 0 },
+ { WM_ACTIVATEAPP, sent|wparam, 0 },
+ { WM_KILLFOCUS, sent|wparam, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_NOTIFY, sent|optional|defwinproc },
+ { 0 }
+};
+/* DestroyWindow for a visible overlapped window */
+static const struct message WmDestroyOverlappedSeq[] = {
+ { HCBT_DESTROYWND, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_NCACTIVATE, sent|wparam, 0 },
+ { WM_ACTIVATE, sent|wparam, 0 },
+ { WM_ACTIVATEAPP, sent|wparam, 0 },
+ { WM_KILLFOCUS, sent|wparam, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_NOTIFY, sent|optional|defwinproc },
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* CreateWindow(WS_MAXIMIZE|WS_VISIBLE) for popup window */
+static const struct message WmCreateMaxPopupSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent /*|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|0x8000*/ },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { WM_WINDOWPOSCHANGED, sent /*|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOREDRAW|SWP_NOZORDER|0x8000*/ },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { HCBT_ACTIVATE, hook },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_SYNCPAINT, sent|wparam|optional, 4 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOCLIENTMOVE|SWP_NOCLIENTSIZE|SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER|SWP_NOSIZE },
+ { 0 }
+};
+/* CreateWindow(WS_MAXIMIZE) for popup window, not initially visible */
+static const struct message WmCreateInvisibleMaxPopupSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent /*|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|0x8000*/ },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { WM_WINDOWPOSCHANGED, sent /*|wparam, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_NOREDRAW|SWP_NOZORDER|0x8000*/ },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { 0 }
+};
+/* ShowWindow(SW_SHOWMAXIMIZED) for a resized not visible popup window */
+static const struct message WmShowMaxPopupResizedSeq[] = {
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { HCBT_ACTIVATE, hook },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOCLIENTMOVE|SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOZORDER },
+ /* WinNT4.0 sends WM_MOVE */
+ { WM_MOVE, sent|defwinproc|optional },
+ { WM_SIZE, sent|defwinproc },
+ { 0 }
+};
+/* ShowWindow(SW_SHOWMAXIMIZED) for a not visible popup window */
+static const struct message WmShowMaxPopupSeq[] = {
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { HCBT_ACTIVATE, hook },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_SYNCPAINT, sent|wparam|optional, 4 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
+ { 0 }
+};
+/* CreateWindow(WS_VISIBLE) for popup window */
+static const struct message WmCreatePopupSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { HCBT_ACTIVATE, hook },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_SYNCPAINT, sent|wparam|optional, 4 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOCLIENTMOVE|SWP_NOCLIENTSIZE|SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER|SWP_NOSIZE },
+ { 0 }
+};
+/* ShowWindow(SW_SHOWMAXIMIZED) for a visible popup window */
+static const struct message WmShowVisMaxPopupSeq[] = {
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_GETTEXT, sent|optional },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, TRUE },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOZORDER|0x8000 },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { 0 }
+};
+/* CreateWindow (for a child popup window, not initially visible) */
+static const struct message WmCreateChildPopupSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { 0 }
+};
+/* CreateWindow (for a popup window, not initially visible,
+ * which sets WS_VISIBLE in WM_CREATE handler)
+ */
+static const struct message WmCreateInvisiblePopupSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { WM_STYLECHANGING, sent },
+ { WM_STYLECHANGED, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER)
+ * for a popup window with WS_VISIBLE style set
+ */
+static const struct message WmShowVisiblePopupSeq_2[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+ { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a popup window with WS_VISIBLE style set
+ */
+static const struct message WmShowVisiblePopupSeq_3[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent|parent },
+ { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { WM_IME_NOTIFY, sent|defwinproc|optional },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|defwinproc },
+ { 0 }
+};
+/* CreateWindow (for child window, not initially visible) */
+static const struct message WmCreateChildSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ /* child is inserted into parent's child list after WM_NCCREATE returns */
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE },
+ { 0 }
+};
+/* CreateWindow (for maximized child window, not initially visible) */
+static const struct message WmCreateMaximizedChildSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE },
+ { 0 }
+};
+/* CreateWindow (for a child window, initially visible) */
+static const struct message WmCreateVisibleChildSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ /* child is inserted into parent's child list after WM_NCCREATE returns */
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { WM_PARENTNOTIFY, sent|parent|wparam, WM_CREATE },
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 }, /* WinXP */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+/* ShowWindow(SW_SHOW) for a not visible child window */
+static const struct message WmShowChildSeq[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* ShowWindow(SW_HIDE) for a visible child window */
+static const struct message WmHideChildSeq[] = {
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a not visible child window
+ */
+static const struct message WmShowChildSeq_2[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CHILDACTIVATE, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE)
+ * for a not visible child window
+ */
+static const struct message WmShowChildSeq_3[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE)
+ * for a visible child window with a caption
+ */
+static const struct message WmShowChildSeq_4[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { WM_CHILDACTIVATE, sent },
+ { 0 }
+};
+/* ShowWindow(SW_SHOW) for child with invisible parent */
+static const struct message WmShowChildInvisibleParentSeq[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { 0 }
+};
+/* ShowWindow(SW_HIDE) for child with invisible parent */
+static const struct message WmHideChildInvisibleParentSeq[] = {
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { 0 }
+};
+/* SetWindowPos(SWP_SHOWWINDOW) for child with invisible parent */
+static const struct message WmShowChildInvisibleParentSeq_2[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* SetWindowPos(SWP_HIDEWINDOW) for child with invisible parent */
+static const struct message WmHideChildInvisibleParentSeq_2[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* DestroyWindow for a visible child window */
+static const struct message WmDestroyChildSeq[] = {
+ { HCBT_DESTROYWND, hook },
+ { WM_PARENTNOTIFY, sent|parent|wparam, WM_DESTROY },
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { HCBT_SETFOCUS, hook }, /* set focus to a parent */
+ { WM_KILLFOCUS, sent },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|parent|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|parent },
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_DESTROY, sent|optional }, /* some other (IME?) window */
+ { WM_NCDESTROY, sent|optional }, /* some other (IME?) window */
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* DestroyWindow for a visible child window with invisible parent */
+static const struct message WmDestroyInvisibleChildSeq[] = {
+ { HCBT_DESTROYWND, hook },
+ { WM_PARENTNOTIFY, sent|parent|wparam, WM_DESTROY },
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* Moving the mouse in nonclient area */
+static const struct message WmMouseMoveInNonClientAreaSeq[] = { /* FIXME: add */
+ { WM_NCHITTEST, sent },
+ { WM_SETCURSOR, sent },
+ { WM_NCMOUSEMOVE, posted },
+ { 0 }
+};
+/* Moving the mouse in client area */
+static const struct message WmMouseMoveInClientAreaSeq[] = { /* FIXME: add */
+ { WM_NCHITTEST, sent },
+ { WM_SETCURSOR, sent },
+ { WM_MOUSEMOVE, posted },
+ { 0 }
+};
+/* Moving by dragging the title bar (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
+static const struct message WmDragTitleBarSeq[] = { /* FIXME: add */
+ { WM_NCLBUTTONDOWN, sent|wparam, HTCAPTION },
+ { WM_SYSCOMMAND, sent|defwinproc|wparam, SC_MOVE+2 },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_ENTERSIZEMOVE, sent|defwinproc },
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 },
+ { WM_MOVE, sent|defwinproc },
+ { WM_EXITSIZEMOVE, sent|defwinproc },
+ { 0 }
+};
+/* Sizing by dragging the thick borders (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
+static const struct message WmDragThickBordersBarSeq[] = { /* FIXME: add */
+ { WM_NCLBUTTONDOWN, sent|wparam, 0xd },
+ { WM_SYSCOMMAND, sent|defwinproc|wparam, 0xf004 },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_ENTERSIZEMOVE, sent|defwinproc },
+ { WM_SIZING, sent|defwinproc|wparam, 4}, /* one for each mouse movement */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
+ { WM_NCPAINT, sent|defwinproc|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc },
+ { WM_ERASEBKGND, sent|defwinproc },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_EXITSIZEMOVE, sent|defwinproc },
+ { 0 }
+};
+/* Resizing child window with MoveWindow (32) */
+static const struct message WmResizingChildWithMoveWindowSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+/* Clicking on inactive button */
+static const struct message WmClickInactiveButtonSeq[] = { /* FIXME: add */
+ { WM_NCHITTEST, sent },
+ { WM_PARENTNOTIFY, sent|parent|wparam, WM_LBUTTONDOWN },
+ { WM_MOUSEACTIVATE, sent },
+ { WM_MOUSEACTIVATE, sent|parent|defwinproc },
+ { WM_SETCURSOR, sent },
+ { WM_SETCURSOR, sent|parent|defwinproc },
+ { WM_LBUTTONDOWN, posted },
+ { WM_KILLFOCUS, posted|parent },
+ { WM_SETFOCUS, posted },
+ { WM_CTLCOLORBTN, posted|parent },
+ { BM_SETSTATE, posted },
+ { WM_CTLCOLORBTN, posted|parent },
+ { WM_LBUTTONUP, posted },
+ { BM_SETSTATE, posted },
+ { WM_CTLCOLORBTN, posted|parent },
+ { WM_COMMAND, posted|parent },
+ { 0 }
+};
+/* Reparenting a button (16/32) */
+/* The last child (button) reparented gets topmost for its new parent. */
+static const struct message WmReparentButtonSeq[] = { /* FIXME: add */
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOZORDER },
+ { WM_CHILDACTIVATE, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOSIZE|SWP_NOREDRAW|SWP_NOZORDER },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { 0 }
+};
+/* Creation of a custom dialog (32) */
+static const struct message WmCreateCustomDialogSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_GETMINMAXINFO, sent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_NCCREATE, sent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { WM_KILLFOCUS, sent|parent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_IME_NOTIFY, sent|optional|defwinproc },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_GETDLGCODE, sent|defwinproc|wparam, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_NCPAINT, sent|wparam, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_ERASEBKGND, sent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_CTLCOLORDLG, sent|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_NCCALCSIZE, sent|optional },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_NCPAINT, sent|optional },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_ERASEBKGND, sent|optional },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_CTLCOLORDLG, sent|optional|defwinproc },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SIZE, sent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_MOVE, sent },
+ { 0 }
+};
+/* Calling EndDialog for a custom dialog (32) */
+static const struct message WmEndCustomDialogSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_GETTEXT, sent|optional },
+ { HCBT_ACTIVATE, hook },
+ { WM_NCACTIVATE, sent|wparam, 0 },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { WM_GETTEXT, sent|optional|defwinproc },
+ { WM_ACTIVATE, sent|wparam, 0 },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|optional, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|parent|wparam|defwinproc|optional, 1 },
+ { WM_IME_NOTIFY, sent|optional },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|parent|defwinproc },
+ { 0 }
+};
+/* ShowWindow(SW_SHOW) for a custom dialog (initially invisible) */
+static const struct message WmShowCustomDialogSeq[] = {
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_ACTIVATEAPP, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+
+ { WM_KILLFOCUS, sent|parent },
+ { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { WM_IME_NOTIFY, sent|optional|defwinproc },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam|optional, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_GETDLGCODE, sent|defwinproc|wparam, 0 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_NCPAINT, sent|wparam, 1 },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_ERASEBKGND, sent },
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_CTLCOLORDLG, sent|defwinproc },
+
+ { EVENT_OBJECT_DEFACTIONCHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* Creation and destruction of a modal dialog (32) */
+static const struct message WmModalDialogSeq[] = {
+ { WM_CANCELMODE, sent|parent },
+ { HCBT_SETFOCUS, hook },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_KILLFOCUS, sent|parent },
+ { WM_IME_SETCONTEXT, sent|parent|wparam|optional, 0 },
+ { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ENABLE, sent|parent|wparam, 0 },
+ { HCBT_CREATEWND, hook },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SETFONT, sent },
+ { WM_INITDIALOG, sent },
+ { WM_CHANGEUISTATE, sent|optional },
+ { WM_SHOWWINDOW, sent },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_GETTEXT, sent|optional },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|optional },
+ { WM_ERASEBKGND, sent },
+ { WM_CTLCOLORDLG, sent },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_GETTEXT, sent|optional },
+ { WM_NCCALCSIZE, sent|optional },
+ { WM_NCPAINT, sent|optional },
+ { WM_GETTEXT, sent|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_CTLCOLORDLG, sent|optional },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_PAINT, sent|optional },
+ { WM_CTLCOLORBTN, sent },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_ENTERIDLE, sent|parent|optional },
+ { WM_TIMER, sent },
+ { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ENABLE, sent|parent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_GETTEXT, sent|optional },
+ { HCBT_ACTIVATE, hook },
+ { WM_NCACTIVATE, sent|wparam, 0 },
+ { WM_GETTEXT, sent|optional },
+ { WM_ACTIVATE, sent|wparam, 0 },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|optional },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|parent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|parent|defwinproc },
+ { EVENT_SYSTEM_DIALOGEND, winevent_hook|wparam|lparam, 0, 0 },
+ { HCBT_DESTROYWND, hook },
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* Creation of a modal dialog that is resized inside WM_INITDIALOG (32) */
+static const struct message WmCreateModalDialogResizeSeq[] = { /* FIXME: add */
+ /* (inside dialog proc, handling WM_INITDIALOG) */
+ { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+ { WM_NCCALCSIZE, sent },
+ { WM_NCACTIVATE, sent|parent|wparam, 0 },
+ { WM_GETTEXT, sent|defwinproc },
+ { WM_ACTIVATE, sent|parent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|parent },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
+ { WM_SIZE, sent|defwinproc },
+ /* (setting focus) */
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc },
+ { WM_ERASEBKGND, sent },
+ { WM_CTLCOLORDLG, sent|defwinproc },
+ { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
+ { WM_PAINT, sent },
+ /* (bunch of WM_CTLCOLOR* for each control) */
+ { WM_PAINT, sent|parent },
+ { WM_ENTERIDLE, sent|parent|wparam, 0 },
+ { WM_SETCURSOR, sent|parent },
+ { 0 }
+};
+/* SetMenu for NonVisible windows with size change*/
+static const struct message WmSetMenuNonVisibleSizeChangeSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_NCCALCSIZE,sent|wparam|optional, 1 }, /* XP */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam|optional, 0, 0 }, /* XP sends a duplicate */
+ { WM_GETTEXT, sent|optional },
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+ { 0 }
+};
+/* SetMenu for NonVisible windows with no size change */
+static const struct message WmSetMenuNonVisibleNoSizeChangeSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+/* SetMenu for Visible windows with size change */
+static const struct message WmSetMenuVisibleSizeChangeSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_NCPAINT, sent }, /* wparam != 1 */
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_ACTIVATE, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+ { WM_NCPAINT, sent|optional }, /* wparam != 1 */
+ { WM_ERASEBKGND, sent|optional },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam|optional, 0, 0 }, /* XP sends a duplicate */
+ { 0 }
+};
+/* SetMenu for Visible windows with no size change */
+static const struct message WmSetMenuVisibleNoSizeChangeSeq[] = {
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_NCPAINT, sent }, /* wparam != 1 */
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_ACTIVATE, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+/* DrawMenuBar for a visible window */
+static const struct message WmDrawMenuBarSeq[] =
+{
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_NCPAINT, sent }, /* wparam != 1 */
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+
+static const struct message WmSetRedrawFalseSeq[] =
+{
+ { WM_SETREDRAW, sent|wparam, 0 },
+ { 0 }
+};
+
+static const struct message WmSetRedrawTrueSeq[] =
+{
+ { WM_SETREDRAW, sent|wparam, 1 },
+ { 0 }
+};
+
+static const struct message WmEnableWindowSeq_1[] =
+{
+ { WM_CANCELMODE, sent|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ENABLE, sent|wparam|lparam, FALSE, 0 },
+ { 0 }
+};
+
+static const struct message WmEnableWindowSeq_2[] =
+{
+ { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ENABLE, sent|wparam|lparam, TRUE, 0 },
+ { 0 }
+};
+
+static const struct message WmGetScrollRangeSeq[] =
+{
+ { SBM_GETRANGE, sent },
+ { 0 }
+};
+static const struct message WmGetScrollInfoSeq[] =
+{
+ { SBM_GETSCROLLINFO, sent },
+ { 0 }
+};
+static const struct message WmSetScrollRangeSeq[] =
+{
+ /* MSDN claims that Windows sends SBM_SETRANGE message, but win2k SP4
+ sends SBM_SETSCROLLINFO.
+ */
+ { SBM_SETSCROLLINFO, sent },
+ { 0 }
+};
+/* SetScrollRange for a window without a non-client area */
+static const struct message WmSetScrollRangeHSeq_empty[] =
+{
+ { EVENT_OBJECT_VALUECHANGE, winevent_hook|wparam|lparam, OBJID_HSCROLL, 0 },
+ { 0 }
+};
+static const struct message WmSetScrollRangeVSeq_empty[] =
+{
+ { EVENT_OBJECT_VALUECHANGE, winevent_hook|wparam|lparam, OBJID_VSCROLL, 0 },
+ { 0 }
+};
+static const struct message WmSetScrollRangeHVSeq[] =
+{
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_VALUECHANGE, winevent_hook|lparam|optional, 0/*OBJID_HSCROLL or OBJID_VSCROLL*/, 0 },
+ { 0 }
+};
+/* SetScrollRange for a window with a non-client area */
+static const struct message WmSetScrollRangeHV_NC_Seq[] =
+{
+ { WM_WINDOWPOSCHANGING, sent, /*|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER*/ },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_NCPAINT, sent|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_CTLCOLORDLG, sent|defwinproc|optional }, /* sent to a parent of the dialog */
+ { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|0x1000*/ },
+ { WM_SIZE, sent|defwinproc },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_VALUECHANGE, winevent_hook|lparam|optional, 0/*OBJID_HSCROLL or OBJID_VSCROLL*/, 0 },
+ { WM_GETTEXT, sent|optional },
+ { WM_GETTEXT, sent|optional },
+ { WM_GETTEXT, sent|optional },
+ { WM_GETTEXT, sent|optional },
+ { 0 }
+};
+/* test if we receive the right sequence of messages */
+/* after calling ShowWindow( SW_SHOWNA) */
+static const struct message WmSHOWNAChildInvisParInvis[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { 0 }
+};
+static const struct message WmSHOWNAChildVisParInvis[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { 0 }
+};
+static const struct message WmSHOWNAChildVisParVis[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER },
+ { 0 }
+};
+static const struct message WmSHOWNAChildInvisParVis[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER},
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+static const struct message WmSHOWNATopVisible[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE },
+ { 0 }
+};
+static const struct message WmSHOWNATopInvisible[] = {
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_NCPAINT, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 },
+ { WM_NCPAINT, sent|wparam|optional, 1 },
+ { WM_ERASEBKGND, sent|optional },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { 0 }
+};
+
+static int after_end_dialog;
+static int sequence_cnt, sequence_size;
+static struct message* sequence;
+static int log_all_parent_messages;
+
+static void add_message(const struct message *msg)
+{
+ if (!sequence)
+ {
+ sequence_size = 10;
+ sequence = HeapAlloc( GetProcessHeap(), 0, sequence_size * sizeof (struct message) );
+ }
+ if (sequence_cnt == sequence_size)
+ {
+ sequence_size *= 2;
+ sequence = HeapReAlloc( GetProcessHeap(), 0, sequence, sequence_size * sizeof (struct message) );
+ }
+ assert(sequence);
+
+ sequence[sequence_cnt].message = msg->message;
+ sequence[sequence_cnt].flags = msg->flags;
+ sequence[sequence_cnt].wParam = msg->wParam;
+ sequence[sequence_cnt].lParam = msg->lParam;
+
+ sequence_cnt++;
+}
+
+/* try to make sure pending X events have been processed before continuing */
+static void flush_events(void)
+{
+ MSG msg;
+ int diff = 100;
+ DWORD time = GetTickCount() + diff;
+
+ while (diff > 0)
+ {
+ MsgWaitForMultipleObjects( 0, NULL, FALSE, diff, QS_ALLINPUT );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ diff = time - GetTickCount();
+ }
+}
+
+static void flush_sequence(void)
+{
+ HeapFree(GetProcessHeap(), 0, sequence);
+ sequence = 0;
+ sequence_cnt = sequence_size = 0;
+}
+
+#define ok_sequence( exp, contx, todo) \
+ ok_sequence_( (exp), (contx), (todo), __FILE__, __LINE__)
+
+
+static void ok_sequence_(const struct message *expected, const char *context, int todo,
+ const char *file, int line)
+{
+ static const struct message end_of_sequence = { 0, 0, 0, 0 };
+ const struct message *actual;
+ int failcount = 0;
+
+ add_message(&end_of_sequence);
+
+ actual = sequence;
+
+ while (expected->message && actual->message)
+ {
+ trace_( file, line)("expected %04x - actual %04x\n", expected->message, actual->message);
+
+ if (expected->message == actual->message)
+ {
+ if (expected->flags & wparam)
+ {
+ if (expected->wParam != actual->wParam && todo)
+ {
+ todo_wine {
+ failcount ++;
+ ok_( file, line) (FALSE,
+ "%s: in msg 0x%04x expecting wParam 0x%x got 0x%x\n",
+ context, expected->message, expected->wParam, actual->wParam);
+ }
+ }
+ else
+ ok_( file, line) (expected->wParam == actual->wParam,
+ "%s: in msg 0x%04x expecting wParam 0x%x got 0x%x\n",
+ context, expected->message, expected->wParam, actual->wParam);
+ }
+ if (expected->flags & lparam)
+ ok_( file, line) (expected->lParam == actual->lParam,
+ "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
+ context, expected->message, expected->lParam, actual->lParam);
+ ok_( file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc),
+ "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
+ context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
+ ok_( file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint),
+ "%s: the msg 0x%04x should %shave been sent by BeginPaint\n",
+ context, expected->message, (expected->flags & beginpaint) ? "" : "NOT ");
+ ok_( file, line) ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)),
+ "%s: the msg 0x%04x should have been %s\n",
+ context, expected->message, (expected->flags & posted) ? "posted" : "sent");
+ ok_( file, line) ((expected->flags & parent) == (actual->flags & parent),
+ "%s: the msg 0x%04x was expected in %s\n",
+ context, expected->message, (expected->flags & parent) ? "parent" : "child");
+ ok_( file, line) ((expected->flags & hook) == (actual->flags & hook),
+ "%s: the msg 0x%04x should have been sent by a hook\n",
+ context, expected->message);
+ ok_( file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook),
+ "%s: the msg 0x%04x should have been sent by a winevent hook\n",
+ context, expected->message);
+ expected++;
+ actual++;
+ }
+ /* silently drop winevent messages if there is no support for them */
+ else if ((expected->flags & optional) || ((expected->flags & winevent_hook) && !hEvent_hook))
+ expected++;
+ else if (todo)
+ {
+ failcount++;
+ todo_wine {
+ ok_( file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
+ context, expected->message, actual->message);
+ }
+ flush_sequence();
+ return;
+ }
+ else
+ {
+ ok_( file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
+ context, expected->message, actual->message);
+ expected++;
+ actual++;
+ }
+ }
+
+ /* skip all optional trailing messages */
+ while (expected->message && ((expected->flags & optional) ||
+ ((expected->flags & winevent_hook) && !hEvent_hook)))
+ expected++;
+
+ if (todo)
+ {
+ todo_wine {
+ if (expected->message || actual->message) {
+ failcount++;
+ ok_( file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
+ context, expected->message, actual->message);
+ }
+ }
+ }
+ else
+ {
+ if (expected->message || actual->message)
+ ok_( file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
+ context, expected->message, actual->message);
+ }
+ if( todo && !failcount) /* succeeded yet marked todo */
+ todo_wine {
+ ok_( file, line)( TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
+ }
+
+ flush_sequence();
+}
+
+/******************************** MDI test **********************************/
+
+/* CreateWindow for MDI frame window, initially visible */
+static const struct message WmCreateMDIframeSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_GETMINMAXINFO, sent },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { HCBT_ACTIVATE, hook },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_QUERYNEWPALETTE, sent|wparam|lparam|optional, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE|SWP_NOMOVE },
+ { WM_WINDOWPOSCHANGED, sent|wparam|optional, SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE }, /* Win9x */
+ { WM_ACTIVATEAPP, sent|wparam, 1 },
+ { WM_NCACTIVATE, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ACTIVATE, sent|wparam, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ /* Win9x adds SWP_NOZORDER below */
+ { WM_WINDOWPOSCHANGED, sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 }, /* XP */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { 0 }
+};
+/* DestroyWindow for MDI frame window, initially visible */
+static const struct message WmDestroyMDIframeSeq[] = {
+ { HCBT_DESTROYWND, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_NCACTIVATE, sent|wparam, 0 },
+ { WM_ACTIVATE, sent|wparam|optional, 0 }, /* Win9x */
+ { WM_ACTIVATEAPP, sent|wparam|optional, 0 }, /* Win9x */
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* CreateWindow for MDI client window, initially visible */
+static const struct message WmCreateMDIclientSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { WM_PARENTNOTIFY, sent|wparam, WM_CREATE }, /* in MDI frame */
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { 0 }
+};
+/* DestroyWindow for MDI client window, initially visible */
+static const struct message WmDestroyMDIclientSeq[] = {
+ { HCBT_DESTROYWND, hook },
+ { WM_PARENTNOTIFY, sent|wparam, WM_DESTROY }, /* in MDI frame */
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* CreateWindow for MDI child window, initially visible */
+static const struct message WmCreateMDIchildVisibleSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+ /* Win9x: message sequence terminates here. */
+
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+ { HCBT_SETFOCUS, hook }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|defwinproc },
+ { WM_MDIACTIVATE, sent|defwinproc },
+ { 0 }
+};
+/* DestroyWindow for MDI child window, initially visible */
+static const struct message WmDestroyMDIchildVisibleSeq[] = {
+ { HCBT_DESTROYWND, hook },
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+ /* { WM_DESTROY, sent }
+ * Win9x: message sequence terminates here.
+ */
+
+ { HCBT_SETFOCUS, hook }, /* set focus to MDI client */
+ { WM_KILLFOCUS, sent },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+
+ { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+
+ { HCBT_SETFOCUS, hook }, /* set focus to MDI client */
+ { WM_KILLFOCUS, sent },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+
+ { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+
+ { WM_DESTROY, sent },
+
+ { HCBT_SETFOCUS, hook }, /* set focus to MDI client */
+ { WM_KILLFOCUS, sent },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+
+ { HCBT_SETFOCUS, hook }, /* MDI client sets focus back to MDI child */
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* CreateWindow for MDI child window, initially invisible */
+static const struct message WmCreateMDIchildInvisibleSeq[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+ { 0 }
+};
+/* DestroyWindow for MDI child window, initially invisible */
+static const struct message WmDestroyMDIchildInvisibleSeq[] = {
+ { HCBT_DESTROYWND, hook },
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* CreateWindow for the 1st MDI child window, initially visible and maximized */
+static const struct message WmCreateMDIchildVisibleMaxSeq1[] = {
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+ /* Win9x: message sequence terminates here. */
+
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+ { HCBT_SETFOCUS, hook }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|defwinproc },
+ { WM_MDIACTIVATE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { 0 }
+};
+/* CreateWindow for the 2nd MDI child window, initially visible and maximized */
+static const struct message WmCreateMDIchildVisibleMaxSeq2[] = {
+ /* restore the 1st MDI child */
+ { WM_SETREDRAW, sent|wparam, 0 },
+ { HCBT_MINMAX, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { WM_SETREDRAW, sent|wparam, 1 }, /* in the 1st MDI child */
+ /* create the 2nd MDI child */
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 0 }, /* in the 1st MDI child */
+ { WM_MDIACTIVATE, sent|defwinproc }, /* in the 1st MDI child */
+
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+ /* Win9x: message sequence terminates here. */
+
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent|defwinproc }, /* in the 1st MDI child */
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 0 }, /* in the 1st MDI child */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|defwinproc },
+
+ { WM_MDIACTIVATE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { 0 }
+};
+/* WM_MDICREATE MDI child window, initially visible and maximized */
+static const struct message WmCreateMDIchildVisibleMaxSeq3[] = {
+ { WM_MDICREATE, sent },
+ { HCBT_CREATEWND, hook },
+ { WM_NCCREATE, sent },
+ { WM_NCCALCSIZE, sent|wparam, 0 },
+ { WM_CREATE, sent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_SIZE, sent },
+ { WM_MOVE, sent },
+ { HCBT_MINMAX, hook|lparam, 0, SW_MAXIMIZE },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_CREATE, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_CREATE*/ }, /* in MDI client */
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_MDIREFRESHMENU, sent/*|wparam|lparam, 0, 0*/ },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE },
+
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+
+ /* Win9x: message sequence terminates here. */
+
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+ { HCBT_SETFOCUS, hook }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|defwinproc },
+
+ { WM_MDIACTIVATE, sent|defwinproc },
+
+ /* in MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
+
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+
+ /* in MDI client */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent },
+
+ /* in MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent|defwinproc },
+
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 }, /* XP sends it to MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* XP sends a duplicate */
+
+ { 0 }
+};
+/* WM_SYSCOMMAND/SC_CLOSE for the 2nd MDI child window, initially visible and maximized */
+static const struct message WmDestroyMDIchildVisibleMaxSeq2[] = {
+ { WM_SYSCOMMAND, sent|wparam, SC_CLOSE },
+ { HCBT_SYSCOMMAND, hook },
+ { WM_CLOSE, sent|defwinproc },
+ { WM_MDIDESTROY, sent }, /* in MDI client */
+
+ /* bring the 1st MDI child to top */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOSIZE|SWP_NOMOVE }, /* in the 1st MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE }, /* in the 2nd MDI child */
+
+ { EVENT_OBJECT_REORDER, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+
+ { WM_CHILDACTIVATE, sent|defwinproc|wparam|lparam, 0, 0 }, /* in the 1st MDI child */
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 0 }, /* in the 1st MDI child */
+ { WM_MDIACTIVATE, sent|defwinproc }, /* in the 1st MDI child */
+
+ /* maximize the 1st MDI child */
+ { HCBT_MINMAX, hook },
+ { WM_GETMINMAXINFO, sent|defwinproc },
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|0x8000 },
+ { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
+ { WM_CHILDACTIVATE, sent|defwinproc|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+
+ /* restore the 2nd MDI child */
+ { WM_SETREDRAW, sent|defwinproc|wparam, 0 },
+ { HCBT_MINMAX, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOZORDER|0x8000 },
+ { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
+
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+
+ { WM_SETREDRAW, sent|defwinproc|wparam, 1 },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+ /* bring the 1st MDI child to top */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent|defwinproc },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|defwinproc },
+ { WM_MDIACTIVATE, sent|defwinproc },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOSIZE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+ /* apparently ShowWindow(SW_SHOW) on an MDI client */
+ { WM_SHOWWINDOW, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { WM_MDIREFRESHMENU, sent },
+
+ { HCBT_DESTROYWND, hook },
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+ { WM_SHOWWINDOW, sent|defwinproc|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent|defwinproc },
+ { WM_NCDESTROY, sent|defwinproc },
+ { 0 }
+};
+/* WM_MDIDESTROY for the single MDI child window, initially visible and maximized */
+static const struct message WmDestroyMDIchildVisibleMaxSeq1[] = {
+ { WM_MDIDESTROY, sent }, /* in MDI client */
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent },
+
+ /* in MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+
+ /* in MDI client */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent },
+
+ /* in MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent|defwinproc },
+
+ /* in MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+
+ /* in MDI client */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent },
+
+ /* in MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOREDRAW|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent|defwinproc },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+
+ { WM_NCCALCSIZE, sent|wparam|defwinproc|optional, 1 }, /* XP sends it to MDI frame */
+
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* XP sends a duplicate */
+
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+
+ { WM_NCACTIVATE, sent|wparam, 0 },
+ { WM_MDIACTIVATE, sent },
+
+ { HCBT_MINMAX, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_SHOWWINDOW|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+
+ /* in MDI child */
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam|defwinproc, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_MOVE, sent|defwinproc },
+ { WM_SIZE, sent|defwinproc },
+
+ /* in MDI client */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTMOVE },
+ { WM_SIZE, sent },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { WM_NCCALCSIZE, sent|wparam|optional, 1 }, /* XP */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI client */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* XP sends a duplicate */
+
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+
+ { WM_MDIREFRESHMENU, sent }, /* in MDI client */
+
+ { HCBT_DESTROYWND, hook },
+ /* Win2k sends wparam set to
+ * MAKEWPARAM(WM_DESTROY, MDI_FIRST_CHILD_ID + nTotalCreated),
+ * while Win9x doesn't bother to set child window id according to
+ * CLIENTCREATESTRUCT.idFirstChild
+ */
+ { WM_PARENTNOTIFY, sent /*|wparam, WM_DESTROY*/ }, /* in MDI client */
+
+ { WM_SHOWWINDOW, sent|wparam, 0 },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_ERASEBKGND, sent|parent|optional },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_DESTROY, sent },
+ { WM_NCDESTROY, sent },
+ { 0 }
+};
+/* ShowWindow(SW_MAXIMIZE) for a not visible MDI child window */
+static const struct message WmMaximizeMDIchildInvisibleSeq[] = {
+ { HCBT_MINMAX, hook },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+
+ { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
+ { WM_NCACTIVATE, sent|wparam|defwinproc, 1 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, /* in MDI client */
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent }, /* in MDI client */
+ { HCBT_SETFOCUS, hook },
+ { WM_KILLFOCUS, sent }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, /* in MDI client */
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|defwinproc },
+ { WM_MDIACTIVATE, sent|defwinproc },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { 0 }
+};
+/* ShowWindow(SW_MAXIMIZE) for a visible MDI child window */
+static const struct message WmMaximizeMDIchildVisibleSeq[] = {
+ { HCBT_MINMAX, hook },
+ { WM_GETMINMAXINFO, sent },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { 0 }
+};
+/* ShowWindow(SW_RESTORE) for a visible MDI child window */
+static const struct message WmRestoreMDIchildVisibleSeq[] = {
+ { HCBT_MINMAX, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { 0 }
+};
+/* ShowWindow(SW_RESTORE) for a not visible MDI child window */
+static const struct message WmRestoreMDIchildInisibleSeq[] = {
+ { HCBT_MINMAX, hook },
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|0x8000 },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CHILDACTIVATE, sent|wparam|lparam, 0, 0 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE|0x8000 },
+ { WM_SIZE, sent|defwinproc },
+ /* in MDI frame */
+ { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER },
+ { WM_NCCALCSIZE, sent|wparam, 1 },
+ { WM_WINDOWPOSCHANGED, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI frame */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 }, /* MDI child */
+ { 0 }
+};
+
+static HWND mdi_client;
+static WNDPROC old_mdi_client_proc;
+
+static LRESULT WINAPI mdi_client_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ struct message msg;
+
+ /* do not log painting messages */
+ if (message != WM_PAINT &&
+ message != WM_ERASEBKGND &&
+ message != WM_NCPAINT &&
+ message != WM_NCHITTEST &&
+ message != WM_GETTEXT &&
+ message != WM_MDIGETACTIVE &&
+ message != WM_GETICON &&
+ message != WM_DEVICECHANGE)
+ {
+ trace("mdi client: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ switch (message)
+ {
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+ trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ /* Log only documented flags, win2k uses 0x1000 and 0x2000
+ * in the high word for internal purposes
+ */
+ wParam = winpos->flags & 0xffff;
+ break;
+ }
+ }
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+ }
+
+ return CallWindowProcA(old_mdi_client_proc, hwnd, message, wParam, lParam);
+}
+
+static LRESULT WINAPI mdi_child_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ /* do not log painting messages */
+ if (message != WM_PAINT &&
+ message != WM_ERASEBKGND &&
+ message != WM_NCPAINT &&
+ message != WM_NCHITTEST &&
+ message != WM_GETTEXT &&
+ message != WM_GETICON &&
+ message != WM_DEVICECHANGE)
+ {
+ trace("mdi child: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ switch (message)
+ {
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+ trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ /* Log only documented flags, win2k uses 0x1000 and 0x2000
+ * in the high word for internal purposes
+ */
+ wParam = winpos->flags & 0xffff;
+ break;
+ }
+
+ case WM_MDIACTIVATE:
+ {
+ HWND active, client = GetParent(hwnd);
+
+ active = (HWND)SendMessageA(client, WM_MDIGETACTIVE, 0, 0);
+
+ if (hwnd == (HWND)lParam) /* if we are being activated */
+ ok (active == (HWND)lParam, "new active %p != active %p\n", (HWND)lParam, active);
+ else
+ ok (active == (HWND)wParam, "old active %p != active %p\n", (HWND)wParam, active);
+ break;
+ }
+ }
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+ }
+
+ defwndproc_counter++;
+ ret = DefMDIChildProcA(hwnd, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static LRESULT WINAPI mdi_frame_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ /* do not log painting messages */
+ if (message != WM_PAINT &&
+ message != WM_ERASEBKGND &&
+ message != WM_NCPAINT &&
+ message != WM_NCHITTEST &&
+ message != WM_GETTEXT &&
+ message != WM_GETICON &&
+ message != WM_DEVICECHANGE)
+ {
+ trace("mdi frame: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ switch (message)
+ {
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+ trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ /* Log only documented flags, win2k uses 0x1000 and 0x2000
+ * in the high word for internal purposes
+ */
+ wParam = winpos->flags & 0xffff;
+ break;
+ }
+ }
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+ }
+
+ defwndproc_counter++;
+ ret = DefFrameProcA(hwnd, mdi_client, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static BOOL mdi_RegisterWindowClasses(void)
+{
+ WNDCLASSA cls;
+
+ cls.style = 0;
+ cls.lpfnWndProc = mdi_frame_wnd_proc;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "MDI_frame_class";
+ if (!RegisterClassA(&cls)) return FALSE;
+
+ cls.lpfnWndProc = mdi_child_wnd_proc;
+ cls.lpszClassName = "MDI_child_class";
+ if (!RegisterClassA(&cls)) return FALSE;
+
+ if (!GetClassInfoA(0, "MDIClient", &cls)) assert(0);
+ old_mdi_client_proc = cls.lpfnWndProc;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.lpfnWndProc = mdi_client_hook_proc;
+ cls.lpszClassName = "MDI_client_class";
+ if (!RegisterClassA(&cls)) assert(0);
+
+ return TRUE;
+}
+
+static void test_mdi_messages(void)
+{
+ MDICREATESTRUCTA mdi_cs;
+ CLIENTCREATESTRUCT client_cs;
+ HWND mdi_frame, mdi_child, mdi_child2, active_child;
+ BOOL zoomed;
+ HMENU hMenu = CreateMenu();
+
+ assert(mdi_RegisterWindowClasses());
+
+ flush_sequence();
+
+ trace("creating MDI frame window\n");
+ mdi_frame = CreateWindowExA(0, "MDI_frame_class", "MDI frame window",
+ WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+ WS_MAXIMIZEBOX | WS_VISIBLE,
+ 100, 100, CW_USEDEFAULT, CW_USEDEFAULT,
+ GetDesktopWindow(), hMenu,
+ GetModuleHandleA(0), NULL);
+ assert(mdi_frame);
+ ok_sequence(WmCreateMDIframeSeq, "Create MDI frame window", TRUE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_frame, "wrong focus window %p\n", GetFocus());
+
+ trace("creating MDI client window\n");
+ client_cs.hWindowMenu = 0;
+ client_cs.idFirstChild = MDI_FIRST_CHILD_ID;
+ mdi_client = CreateWindowExA(0, "MDI_client_class",
+ NULL,
+ WS_CHILD | WS_VISIBLE | MDIS_ALLCHILDSTYLES,
+ 0, 0, 0, 0,
+ mdi_frame, 0, GetModuleHandleA(0), &client_cs);
+ assert(mdi_client);
+ ok_sequence(WmCreateMDIclientSeq, "Create visible MDI client window", FALSE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_frame, "input focus should be on MDI frame not on %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(!active_child, "wrong active MDI child %p\n", active_child);
+ ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+ SetFocus(0);
+ flush_sequence();
+
+ trace("creating invisible MDI child window\n");
+ mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+ WS_CHILD,
+ 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandleA(0), NULL);
+ assert(mdi_child);
+
+ flush_sequence();
+ ShowWindow(mdi_child, SW_SHOWNORMAL);
+ ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOWNORMAL) MDI child window", FALSE);
+
+ ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+ ok(IsWindowVisible(mdi_child), "MDI child should be visible\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(!active_child, "wrong active MDI child %p\n", active_child);
+ ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+ ShowWindow(mdi_child, SW_HIDE);
+ ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE) MDI child window", FALSE);
+ flush_sequence();
+
+ ShowWindow(mdi_child, SW_SHOW);
+ ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW) MDI child window", FALSE);
+
+ ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+ ok(IsWindowVisible(mdi_child), "MDI child should be visible\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(!active_child, "wrong active MDI child %p\n", active_child);
+ ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+ DestroyWindow(mdi_child);
+ flush_sequence();
+
+ trace("creating visible MDI child window\n");
+ mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+ WS_CHILD | WS_VISIBLE,
+ 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandleA(0), NULL);
+ assert(mdi_child);
+ ok_sequence(WmCreateMDIchildVisibleSeq, "Create visible MDI child window", FALSE);
+
+ ok(GetWindowLongA(mdi_child, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+ ok(IsWindowVisible(mdi_child), "MDI child should be visible\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+ ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ DestroyWindow(mdi_child);
+ ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ /* Win2k: MDI client still returns a just destroyed child as active
+ * Win9x: MDI client returns 0
+ */
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child || /* win2k */
+ !active_child, /* win9x */
+ "wrong active MDI child %p\n", active_child);
+ ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+
+ flush_sequence();
+
+ trace("creating invisible MDI child window\n");
+ mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+ WS_CHILD,
+ 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandleA(0), NULL);
+ assert(mdi_child2);
+ ok_sequence(WmCreateMDIchildInvisibleSeq, "Create invisible MDI child window", FALSE);
+
+ ok(!(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE), "MDI child should not be visible\n");
+ ok(!IsWindowVisible(mdi_child2), "MDI child should not be visible\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ /* Win2k: MDI client still returns a just destroyed child as active
+ * Win9x: MDI client returns mdi_child2
+ */
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child || /* win2k */
+ active_child == mdi_child2, /* win9x */
+ "wrong active MDI child %p\n", active_child);
+ ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ ShowWindow(mdi_child2, SW_MAXIMIZE);
+ ok_sequence(WmMaximizeMDIchildInvisibleSeq, "ShowWindow(SW_MAXIMIZE):invisible MDI child", TRUE);
+
+ ok(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+ ok(IsWindowVisible(mdi_child2), "MDI child should be visible\n");
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+ ok(zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child2 || /* win2k */
+ GetFocus() == 0, /* win9x */
+ "wrong focus window %p\n", GetFocus());
+
+ SetFocus(0);
+ flush_sequence();
+
+ ShowWindow(mdi_child2, SW_HIDE);
+ ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE);
+
+ ShowWindow(mdi_child2, SW_RESTORE);
+ ok_sequence(WmRestoreMDIchildInisibleSeq, "ShowWindow(SW_RESTORE):invisible MDI child", TRUE);
+ flush_sequence();
+
+ ok(GetWindowLongA(mdi_child2, GWL_STYLE) & WS_VISIBLE, "MDI child should be visible\n");
+ ok(IsWindowVisible(mdi_child2), "MDI child should be visible\n");
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+ ok(!zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ SetFocus(0);
+ flush_sequence();
+
+ ShowWindow(mdi_child2, SW_HIDE);
+ ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE);
+
+ ShowWindow(mdi_child2, SW_SHOW);
+ ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):MDI child", FALSE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ ShowWindow(mdi_child2, SW_MAXIMIZE);
+ ok_sequence(WmMaximizeMDIchildVisibleSeq, "ShowWindow(SW_MAXIMIZE):MDI child", TRUE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ ShowWindow(mdi_child2, SW_RESTORE);
+ ok_sequence(WmRestoreMDIchildVisibleSeq, "ShowWindow(SW_RESTORE):MDI child", TRUE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ SetFocus(0);
+ flush_sequence();
+
+ ShowWindow(mdi_child2, SW_HIDE);
+ ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):MDI child", FALSE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ DestroyWindow(mdi_child2);
+ ok_sequence(WmDestroyMDIchildInvisibleSeq, "Destroy invisible MDI child window", FALSE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ /* test for maximized MDI children */
+ trace("creating maximized visible MDI child window 1\n");
+ mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+ WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE,
+ 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandleA(0), NULL);
+ assert(mdi_child);
+ ok_sequence(WmCreateMDIchildVisibleMaxSeq1, "Create maximized visible 1st MDI child window", TRUE);
+ ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child || /* win2k */
+ GetFocus() == 0, /* win9x */
+ "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+ ok(zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ trace("creating maximized visible MDI child window 2\n");
+ mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+ WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE,
+ 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandleA(0), NULL);
+ assert(mdi_child2);
+ ok_sequence(WmCreateMDIchildVisibleMaxSeq2, "Create maximized visible 2nd MDI child 2 window", TRUE);
+ ok(IsZoomed(mdi_child2), "2nd MDI child should be maximized\n");
+ ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child2, "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+ ok(zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ trace("destroying maximized visible MDI child window 2\n");
+ DestroyWindow(mdi_child2);
+ ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE);
+
+ ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ /* Win2k: MDI client still returns a just destroyed child as active
+ * Win9x: MDI client returns 0
+ */
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child2 || /* win2k */
+ !active_child, /* win9x */
+ "wrong active MDI child %p\n", active_child);
+ flush_sequence();
+
+ ShowWindow(mdi_child, SW_MAXIMIZE);
+ ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n");
+ flush_sequence();
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+ trace("re-creating maximized visible MDI child window 2\n");
+ mdi_child2 = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_class", "MDI child",
+ WS_CHILD | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE,
+ 0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandleA(0), NULL);
+ assert(mdi_child2);
+ ok_sequence(WmCreateMDIchildVisibleMaxSeq2, "Create maximized visible 2nd MDI child 2 window", TRUE);
+ ok(IsZoomed(mdi_child2), "2nd MDI child should be maximized\n");
+ ok(!IsZoomed(mdi_child), "1st MDI child should NOT be maximized\n");
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child2, "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child2, "wrong active MDI child %p\n", active_child);
+ ok(zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ SendMessageA(mdi_child2, WM_SYSCOMMAND, SC_CLOSE, 0);
+ ok_sequence(WmDestroyMDIchildVisibleMaxSeq2, "WM_SYSCOMMAND/SC_CLOSE on a visible maximized MDI child window", TRUE);
+ ok(!IsWindow(mdi_child2), "MDI child 2 should be destroyed\n");
+
+ ok(IsZoomed(mdi_child), "1st MDI child should be maximized\n");
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+ ok(zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ DestroyWindow(mdi_child);
+ ok_sequence(WmDestroyMDIchildVisibleSeq, "Destroy visible MDI child window", TRUE);
+
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ /* Win2k: MDI client still returns a just destroyed child as active
+ * Win9x: MDI client returns 0
+ */
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child || /* win2k */
+ !active_child, /* win9x */
+ "wrong active MDI child %p\n", active_child);
+ flush_sequence();
+ /* end of test for maximized MDI children */
+
+ mdi_cs.szClass = "MDI_child_Class";
+ mdi_cs.szTitle = "MDI child";
+ mdi_cs.hOwner = GetModuleHandleA(0);
+ mdi_cs.x = 0;
+ mdi_cs.y = 0;
+ mdi_cs.cx = CW_USEDEFAULT;
+ mdi_cs.cy = CW_USEDEFAULT;
+ mdi_cs.style = WS_CHILD | WS_SYSMENU | WS_VISIBLE | WS_MAXIMIZEBOX | WS_MAXIMIZE;
+ mdi_cs.lParam = 0;
+ mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok_sequence(WmCreateMDIchildVisibleMaxSeq3, "WM_MDICREATE for maximized visible MDI child window", TRUE);
+
+ ok(GetMenuItemID(hMenu, GetMenuItemCount(hMenu) - 1) == SC_CLOSE, "SC_CLOSE menu item not found\n");
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+
+ ok(IsZoomed(mdi_child), "MDI child should be maximized\n");
+ ok(GetActiveWindow() == mdi_frame, "wrong active window %p\n", GetActiveWindow());
+ ok(GetFocus() == mdi_child, "wrong focus window %p\n", GetFocus());
+
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(active_child == mdi_child, "wrong active MDI child %p\n", active_child);
+ ok(zoomed, "wrong zoomed state %d\n", zoomed);
+ flush_sequence();
+
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok_sequence(WmDestroyMDIchildVisibleMaxSeq1, "Destroy visible maximized MDI child window", TRUE);
+
+ ok(!IsWindow(mdi_child), "MDI child should be destroyed\n");
+ active_child = (HWND)SendMessageA(mdi_client, WM_MDIGETACTIVE, 0, (LPARAM)&zoomed);
+ ok(!active_child, "wrong active MDI child %p\n", active_child);
+
+ SetFocus(0);
+ flush_sequence();
+
+ DestroyWindow(mdi_client);
+ ok_sequence(WmDestroyMDIclientSeq, "Destroy MDI client window", FALSE);
+
+ DestroyWindow(mdi_frame);
+ ok_sequence(WmDestroyMDIframeSeq, "Destroy MDI frame window", FALSE);
+}
+/************************* End of MDI test **********************************/
+
+static void test_WM_SETREDRAW(HWND hwnd)
+{
+ DWORD style = GetWindowLongA(hwnd, GWL_STYLE);
+
+ flush_sequence();
+
+ SendMessageA(hwnd, WM_SETREDRAW, FALSE, 0);
+ ok_sequence(WmSetRedrawFalseSeq, "SetRedraw:FALSE", FALSE);
+
+ ok(!(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should NOT be set\n");
+ ok(!IsWindowVisible(hwnd), "IsWindowVisible() should return FALSE\n");
+
+ flush_sequence();
+ SendMessageA(hwnd, WM_SETREDRAW, TRUE, 0);
+ ok_sequence(WmSetRedrawTrueSeq, "SetRedraw:TRUE", FALSE);
+
+ ok(GetWindowLongA(hwnd, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+ ok(IsWindowVisible(hwnd), "IsWindowVisible() should return TRUE\n");
+
+ /* restore original WS_VISIBLE state */
+ SetWindowLongA(hwnd, GWL_STYLE, style);
+
+ flush_sequence();
+}
+
+static INT_PTR CALLBACK TestModalDlgProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ struct message msg;
+
+ trace("dialog: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ /* explicitly ignore WM_GETICON message */
+ if (message == WM_GETICON) return 0;
+
+ switch (message)
+ {
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+ trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ /* Log only documented flags, win2k uses 0x1000 and 0x2000
+ * in the high word for internal purposes
+ */
+ wParam = winpos->flags & 0xffff;
+ break;
+ }
+ }
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+
+ if (message == WM_INITDIALOG) SetTimer( hwnd, 1, 100, NULL );
+ if (message == WM_TIMER) EndDialog( hwnd, 0 );
+ return 0;
+}
+
+static void test_hv_scroll_1(HWND hwnd, INT ctl, DWORD clear, DWORD set, INT min, INT max)
+{
+ DWORD style, exstyle;
+ INT xmin, xmax;
+ BOOL ret;
+
+ exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+ style = GetWindowLongA(hwnd, GWL_STYLE);
+ /* do not be confused by WS_DLGFRAME set */
+ if ((style & WS_CAPTION) == WS_CAPTION) style &= ~WS_CAPTION;
+
+ if (clear) ok(style & clear, "style %08lx should be set\n", clear);
+ if (set) ok(!(style & set), "style %08lx should not be set\n", set);
+
+ ret = SetScrollRange(hwnd, ctl, min, max, FALSE);
+ ok( ret, "SetScrollRange(%d) error %ld\n", ctl, GetLastError());
+ if ((style & (WS_DLGFRAME | WS_BORDER | WS_THICKFRAME)) || (exstyle & WS_EX_DLGMODALFRAME))
+ ok_sequence(WmSetScrollRangeHV_NC_Seq, "SetScrollRange(SB_HORZ/SB_VERT) NC", FALSE);
+ else
+ ok_sequence(WmSetScrollRangeHVSeq, "SetScrollRange(SB_HORZ/SB_VERT)", FALSE);
+
+ style = GetWindowLongA(hwnd, GWL_STYLE);
+ if (set) ok(style & set, "style %08lx should be set\n", set);
+ if (clear) ok(!(style & clear), "style %08lx should not be set\n", clear);
+
+ /* a subsequent call should do nothing */
+ ret = SetScrollRange(hwnd, ctl, min, max, FALSE);
+ ok( ret, "SetScrollRange(%d) error %ld\n", ctl, GetLastError());
+ ok_sequence(WmEmptySeq, "SetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+ xmin = 0xdeadbeef;
+ xmax = 0xdeadbeef;
+ trace("Ignore GetScrollRange error below if you are on Win9x\n");
+ ret = GetScrollRange(hwnd, ctl, &xmin, &xmax);
+ ok( ret, "GetScrollRange(%d) error %ld\n", ctl, GetLastError());
+ ok_sequence(WmEmptySeq, "GetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE);
+ ok(xmin == min, "unexpected min scroll value %d\n", xmin);
+ ok(xmax == max, "unexpected max scroll value %d\n", xmax);
+}
+
+static void test_hv_scroll_2(HWND hwnd, INT ctl, DWORD clear, DWORD set, INT min, INT max)
+{
+ DWORD style, exstyle;
+ SCROLLINFO si;
+ BOOL ret;
+
+ exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+ style = GetWindowLongA(hwnd, GWL_STYLE);
+ /* do not be confused by WS_DLGFRAME set */
+ if ((style & WS_CAPTION) == WS_CAPTION) style &= ~WS_CAPTION;
+
+ if (clear) ok(style & clear, "style %08lx should be set\n", clear);
+ if (set) ok(!(style & set), "style %08lx should not be set\n", set);
+
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_RANGE;
+ si.nMin = min;
+ si.nMax = max;
+ SetScrollInfo(hwnd, ctl, &si, TRUE);
+ if ((style & (WS_DLGFRAME | WS_BORDER | WS_THICKFRAME)) || (exstyle & WS_EX_DLGMODALFRAME))
+ ok_sequence(WmSetScrollRangeHV_NC_Seq, "SetScrollInfo(SB_HORZ/SB_VERT) NC", FALSE);
+ else
+ ok_sequence(WmSetScrollRangeHVSeq, "SetScrollInfo(SB_HORZ/SB_VERT)", FALSE);
+
+ style = GetWindowLongA(hwnd, GWL_STYLE);
+ if (set) ok(style & set, "style %08lx should be set\n", set);
+ if (clear) ok(!(style & clear), "style %08lx should not be set\n", clear);
+
+ /* a subsequent call should do nothing */
+ SetScrollInfo(hwnd, ctl, &si, TRUE);
+ if (style & WS_HSCROLL)
+ ok_sequence(WmSetScrollRangeHSeq_empty, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+ else if (style & WS_VSCROLL)
+ ok_sequence(WmSetScrollRangeVSeq_empty, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+ else
+ ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+ si.fMask = SIF_PAGE;
+ si.nPage = 5;
+ SetScrollInfo(hwnd, ctl, &si, FALSE);
+ ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+ si.fMask = SIF_POS;
+ si.nPos = max - 1;
+ SetScrollInfo(hwnd, ctl, &si, FALSE);
+ ok_sequence(WmEmptySeq, "SetScrollInfo(SB_HORZ/SB_VERT) empty sequence", FALSE);
+
+ si.fMask = SIF_RANGE;
+ si.nMin = 0xdeadbeef;
+ si.nMax = 0xdeadbeef;
+ ret = GetScrollInfo(hwnd, ctl, &si);
+ ok( ret, "GetScrollInfo error %ld\n", GetLastError());
+ ok_sequence(WmEmptySeq, "GetScrollRange(SB_HORZ/SB_VERT) empty sequence", FALSE);
+ ok(si.nMin == min, "unexpected min scroll value %d\n", si.nMin);
+ ok(si.nMax == max, "unexpected max scroll value %d\n", si.nMax);
+}
+
+/* Win9x sends WM_USER+xxx while and NT versions send SBM_xxx messages */
+static void test_scroll_messages(HWND hwnd)
+{
+ SCROLLINFO si;
+ INT min, max;
+ BOOL ret;
+
+ min = 0xdeadbeef;
+ max = 0xdeadbeef;
+ ret = GetScrollRange(hwnd, SB_CTL, &min, &max);
+ ok( ret, "GetScrollRange error %ld\n", GetLastError());
+ if (sequence->message != WmGetScrollRangeSeq[0].message)
+ trace("GetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message);
+ /* values of min and max are undefined */
+ flush_sequence();
+
+ ret = SetScrollRange(hwnd, SB_CTL, 10, 150, FALSE);
+ ok( ret, "SetScrollRange error %ld\n", GetLastError());
+ if (sequence->message != WmSetScrollRangeSeq[0].message)
+ trace("SetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message);
+ flush_sequence();
+
+ min = 0xdeadbeef;
+ max = 0xdeadbeef;
+ ret = GetScrollRange(hwnd, SB_CTL, &min, &max);
+ ok( ret, "GetScrollRange error %ld\n", GetLastError());
+ if (sequence->message != WmGetScrollRangeSeq[0].message)
+ trace("GetScrollRange(SB_CTL) generated unknown message %04x\n", sequence->message);
+ /* values of min and max are undefined */
+ flush_sequence();
+
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_RANGE;
+ si.nMin = 20;
+ si.nMax = 160;
+ SetScrollInfo(hwnd, SB_CTL, &si, FALSE);
+ if (sequence->message != WmSetScrollRangeSeq[0].message)
+ trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+ flush_sequence();
+
+ si.fMask = SIF_PAGE;
+ si.nPage = 10;
+ SetScrollInfo(hwnd, SB_CTL, &si, FALSE);
+ if (sequence->message != WmSetScrollRangeSeq[0].message)
+ trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+ flush_sequence();
+
+ si.fMask = SIF_POS;
+ si.nPos = 20;
+ SetScrollInfo(hwnd, SB_CTL, &si, FALSE);
+ if (sequence->message != WmSetScrollRangeSeq[0].message)
+ trace("SetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+ flush_sequence();
+
+ si.fMask = SIF_RANGE;
+ si.nMin = 0xdeadbeef;
+ si.nMax = 0xdeadbeef;
+ ret = GetScrollInfo(hwnd, SB_CTL, &si);
+ ok( ret, "GetScrollInfo error %ld\n", GetLastError());
+ if (sequence->message != WmGetScrollInfoSeq[0].message)
+ trace("GetScrollInfo(SB_CTL) generated unknown message %04x\n", sequence->message);
+ /* values of min and max are undefined */
+ flush_sequence();
+
+ /* set WS_HSCROLL */
+ test_hv_scroll_1(hwnd, SB_HORZ, 0, WS_HSCROLL, 10, 150);
+ /* clear WS_HSCROLL */
+ test_hv_scroll_1(hwnd, SB_HORZ, WS_HSCROLL, 0, 0, 0);
+
+ /* set WS_HSCROLL */
+ test_hv_scroll_2(hwnd, SB_HORZ, 0, WS_HSCROLL, 10, 150);
+ /* clear WS_HSCROLL */
+ test_hv_scroll_2(hwnd, SB_HORZ, WS_HSCROLL, 0, 0, 0);
+
+ /* set WS_VSCROLL */
+ test_hv_scroll_1(hwnd, SB_VERT, 0, WS_VSCROLL, 10, 150);
+ /* clear WS_VSCROLL */
+ test_hv_scroll_1(hwnd, SB_VERT, WS_VSCROLL, 0, 0, 0);
+
+ /* set WS_VSCROLL */
+ test_hv_scroll_2(hwnd, SB_VERT, 0, WS_VSCROLL, 10, 150);
+ /* clear WS_VSCROLL */
+ test_hv_scroll_2(hwnd, SB_VERT, WS_VSCROLL, 0, 0, 0);
+}
+
+static void test_showwindow(void)
+{
+ HWND hwnd, hchild;
+ RECT rc;
+
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create overlapped window\n");
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD,
+ 0, 0, 10, 10, hwnd, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child\n");
+ flush_sequence();
+
+ /* ShowWindow( SW_SHOWNA) for invisible top level window */
+ trace("calling ShowWindow( SW_SHOWNA) for invisible top level window\n");
+ ok( ShowWindow(hwnd, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" );
+ ok_sequence(WmSHOWNATopInvisible, "ShowWindow(SW_SHOWNA) on invisible top level window", TRUE);
+ trace("done\n");
+
+ /* ShowWindow( SW_SHOWNA) for now visible top level window */
+ trace("calling ShowWindow( SW_SHOWNA) for now visible top level window\n");
+ ok( ShowWindow(hwnd, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" );
+ ok_sequence(WmSHOWNATopVisible, "ShowWindow(SW_SHOWNA) on visible top level window", FALSE);
+ trace("done\n");
+ /* back to invisible */
+ ShowWindow(hchild, SW_HIDE);
+ ShowWindow(hwnd, SW_HIDE);
+ flush_sequence();
+ /* ShowWindow(SW_SHOWNA) with child and parent invisible */
+ trace("calling ShowWindow( SW_SHOWNA) for invisible child with invisible parent\n");
+ ok( ShowWindow(hchild, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" );
+ ok_sequence(WmSHOWNAChildInvisParInvis, "ShowWindow(SW_SHOWNA) invisible child and parent", FALSE);
+ trace("done\n");
+ /* ShowWindow(SW_SHOWNA) with child visible and parent invisible */
+ ok( ShowWindow(hchild, SW_SHOW) != FALSE, "ShowWindow: window was invisible\n" );
+ flush_sequence();
+ trace("calling ShowWindow( SW_SHOWNA) for the visible child and invisible parent\n");
+ ok( ShowWindow(hchild, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" );
+ ok_sequence(WmSHOWNAChildVisParInvis, "ShowWindow(SW_SHOWNA) visible child and invisible parent", FALSE);
+ trace("done\n");
+ /* ShowWindow(SW_SHOWNA) with child visible and parent visible */
+ ShowWindow( hwnd, SW_SHOW);
+ flush_sequence();
+ trace("calling ShowWindow( SW_SHOWNA) for the visible child and parent\n");
+ ok( ShowWindow(hchild, SW_SHOWNA) != FALSE, "ShowWindow: window was invisible\n" );
+ ok_sequence(WmSHOWNAChildVisParVis, "ShowWindow(SW_SHOWNA) for the visible child and parent", FALSE);
+ trace("done\n");
+
+ /* ShowWindow(SW_SHOWNA) with child invisible and parent visible */
+ ShowWindow( hchild, SW_HIDE);
+ flush_sequence();
+ trace("calling ShowWindow( SW_SHOWNA) for the invisible child and visible parent\n");
+ ok( ShowWindow(hchild, SW_SHOWNA) == FALSE, "ShowWindow: window was visible\n" );
+ ok_sequence(WmSHOWNAChildInvisParVis, "ShowWindow(SW_SHOWNA) for the invisible child and visible parent", FALSE);
+ trace("done\n");
+
+ SetCapture(hchild);
+ ok(GetCapture() == hchild, "wrong capture window %p\n", GetCapture());
+ DestroyWindow(hchild);
+ ok(!GetCapture(), "wrong capture window %p\n", GetCapture());
+
+ DestroyWindow(hwnd);
+ flush_sequence();
+
+ /* Popup windows */
+ /* Test 1:
+ * 1. Create invisible maximized popup window.
+ * 2. Move and resize it.
+ * 3. Show it maximized.
+ */
+ trace("calling CreateWindowExA( WS_MAXIMIZE ) for invisible maximized popup window\n");
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test popup", WS_POPUP | WS_MAXIMIZE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create popup window\n");
+ ok_sequence(WmCreateInvisibleMaxPopupSeq, "CreateWindow(WS_MAXIMIZED):popup", FALSE);
+ trace("done\n");
+
+ GetWindowRect(hwnd, &rc);
+ ok( rc.right-rc.left == GetSystemMetrics(SM_CXSCREEN) &&
+ rc.bottom-rc.top == GetSystemMetrics(SM_CYSCREEN),
+ "Invalid maximized size before ShowWindow (%ld,%ld)-(%ld,%ld)\n",
+ rc.left, rc.top, rc.right, rc.bottom);
+ /* Reset window's size & position */
+ SetWindowPos(hwnd, 0, 10, 10, 200, 200, SWP_NOZORDER | SWP_NOACTIVATE);
+ flush_sequence();
+
+ trace("calling ShowWindow( SW_SHOWMAXIMIZE ) for invisible popup window\n");
+ ShowWindow(hwnd, SW_SHOWMAXIMIZED);
+ ok_sequence(WmShowMaxPopupResizedSeq, "ShowWindow(SW_SHOWMAXIMIZED):popup", FALSE);
+ trace("done\n");
+
+ GetWindowRect(hwnd, &rc);
+ ok( rc.right-rc.left == GetSystemMetrics(SM_CXSCREEN) &&
+ rc.bottom-rc.top == GetSystemMetrics(SM_CYSCREEN),
+ "Invalid maximized size after ShowWindow (%ld,%ld)-(%ld,%ld)\n",
+ rc.left, rc.top, rc.right, rc.bottom);
+ DestroyWindow(hwnd);
+ flush_sequence();
+
+ /* Test 2:
+ * 1. Create invisible maximized popup window.
+ * 2. Show it maximized.
+ */
+ trace("calling CreateWindowExA( WS_MAXIMIZE ) for invisible maximized popup window\n");
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test popup", WS_POPUP | WS_MAXIMIZE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create popup window\n");
+ ok_sequence(WmCreateInvisibleMaxPopupSeq, "CreateWindow(WS_MAXIMIZED):popup", FALSE);
+ trace("done\n");
+
+ trace("calling ShowWindow( SW_SHOWMAXIMIZE ) for invisible popup window\n");
+ ShowWindow(hwnd, SW_SHOWMAXIMIZED);
+ ok_sequence(WmShowMaxPopupSeq, "ShowWindow(SW_SHOWMAXIMIZED):popup", FALSE);
+ trace("done\n");
+ DestroyWindow(hwnd);
+ flush_sequence();
+
+ /* Test 3:
+ * 1. Create visible maximized popup window.
+ */
+ trace("calling CreateWindowExA( WS_MAXIMIZE ) for maximized popup window\n");
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test popup", WS_POPUP | WS_MAXIMIZE | WS_VISIBLE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create popup window\n");
+ ok_sequence(WmCreateMaxPopupSeq, "CreateWindow(WS_MAXIMIZED):popup", FALSE);
+ trace("done\n");
+ DestroyWindow(hwnd);
+ flush_sequence();
+
+ /* Test 4:
+ * 1. Create visible popup window.
+ * 2. Maximize it.
+ */
+ trace("calling CreateWindowExA( WS_VISIBLE ) for popup window\n");
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test popup", WS_POPUP | WS_VISIBLE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create popup window\n");
+ ok_sequence(WmCreatePopupSeq, "CreateWindow(WS_VISIBLE):popup", TRUE);
+ trace("done\n");
+
+ trace("calling ShowWindow( SW_SHOWMAXIMIZE ) for visible popup window\n");
+ ShowWindow(hwnd, SW_SHOWMAXIMIZED);
+ ok_sequence(WmShowVisMaxPopupSeq, "ShowWindow(SW_SHOWMAXIMIZED):popup", TRUE);
+ trace("done\n");
+ DestroyWindow(hwnd);
+ flush_sequence();
+}
+
+static void test_sys_menu(HWND hwnd)
+{
+ HMENU hmenu;
+ UINT state;
+
+ /* test existing window without CS_NOCLOSE style */
+ hmenu = GetSystemMenu(hwnd, FALSE);
+ ok(hmenu != 0, "GetSystemMenu error %ld\n", GetLastError());
+
+ state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+ ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+ ok(!(state & (MF_DISABLED | MF_GRAYED)), "wrong SC_CLOSE state %x\n", state);
+
+ EnableMenuItem(hmenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
+ ok_sequence(WmEmptySeq, "WmEnableMenuItem", FALSE);
+
+ state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+ ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+ ok((state & (MF_DISABLED | MF_GRAYED)) == MF_GRAYED, "wrong SC_CLOSE state %x\n", state);
+
+ EnableMenuItem(hmenu, SC_CLOSE, 0);
+ ok_sequence(WmEmptySeq, "WmEnableMenuItem", FALSE);
+
+ state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+ ok(state != 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+ ok(!(state & (MF_DISABLED | MF_GRAYED)), "wrong SC_CLOSE state %x\n", state);
+
+ /* test new window with CS_NOCLOSE style */
+ hwnd = CreateWindowExA(0, "NoCloseWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create overlapped window\n");
+
+ hmenu = GetSystemMenu(hwnd, FALSE);
+ ok(hmenu != 0, "GetSystemMenu error %ld\n", GetLastError());
+
+ state = GetMenuState(hmenu, SC_CLOSE, MF_BYCOMMAND);
+ ok(state == 0xffffffff, "wrong SC_CLOSE state %x\n", state);
+
+ DestroyWindow(hwnd);
+}
+
+/* test if we receive the right sequence of messages */
+static void test_messages(void)
+{
+ HWND hwnd, hparent, hchild;
+ HWND hchild2, hbutton;
+ HMENU hmenu;
+ MSG msg;
+ DWORD ret;
+
+ flush_sequence();
+
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create overlapped window\n");
+ ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped", FALSE);
+
+ /* test ShowWindow(SW_HIDE) on a newly created invisible window */
+ ok( ShowWindow(hwnd, SW_HIDE) == FALSE, "ShowWindow: window was visible\n" );
+ ok_sequence(WmEmptySeq, "ShowWindow(SW_HIDE):overlapped, invisible", FALSE);
+
+ /* test WM_SETREDRAW on a not visible top level window */
+ test_WM_SETREDRAW(hwnd);
+
+ SetWindowPos(hwnd, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+ ok_sequence(WmSWP_ShowOverlappedSeq, "SetWindowPos:SWP_SHOWWINDOW:overlapped", FALSE);
+ ok(IsWindowVisible(hwnd), "window should be visible at this point\n");
+
+ ok(GetActiveWindow() == hwnd, "window should be active\n");
+ ok(GetFocus() == hwnd, "window should have input focus\n");
+ ShowWindow(hwnd, SW_HIDE);
+ ok_sequence(WmHideOverlappedSeq, "ShowWindow(SW_HIDE):overlapped", TRUE);
+
+ ShowWindow(hwnd, SW_SHOW);
+ ok_sequence(WmShowOverlappedSeq, "ShowWindow(SW_SHOW):overlapped", TRUE);
+
+ ShowWindow(hwnd, SW_HIDE);
+ ok_sequence(WmHideOverlappedSeq, "ShowWindow(SW_HIDE):overlapped", FALSE);
+
+ ShowWindow(hwnd, SW_SHOWMAXIMIZED);
+ ok_sequence(WmShowMaxOverlappedSeq, "ShowWindow(SW_SHOWMAXIMIZED):overlapped", TRUE);
+
+ ShowWindow(hwnd, SW_RESTORE);
+ /* FIXME: add ok_sequence() here */
+ flush_sequence();
+
+ ShowWindow(hwnd, SW_SHOW);
+ ok_sequence(WmEmptySeq, "ShowWindow(SW_SHOW):overlapped already visible", FALSE);
+
+ ok(GetActiveWindow() == hwnd, "window should be active\n");
+ ok(GetFocus() == hwnd, "window should have input focus\n");
+ SetWindowPos(hwnd, 0,0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+ ok_sequence(WmSWP_HideOverlappedSeq, "SetWindowPos:SWP_HIDEWINDOW:overlapped", FALSE);
+ ok(!IsWindowVisible(hwnd), "window should not be visible at this point\n");
+ ok(GetActiveWindow() == hwnd, "window should still be active\n");
+
+ /* test WM_SETREDRAW on a visible top level window */
+ ShowWindow(hwnd, SW_SHOW);
+ test_WM_SETREDRAW(hwnd);
+
+ trace("testing scroll APIs on a visible top level window %p\n", hwnd);
+ test_scroll_messages(hwnd);
+
+ /* test resizing and moving */
+ SetWindowPos( hwnd, 0, 0, 0, 300, 300, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE );
+ ok_sequence(WmSWP_ResizeSeq, "SetWindowPos:Resize", FALSE );
+ SetWindowPos( hwnd, 0, 200, 200, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE );
+ ok_sequence(WmSWP_MoveSeq, "SetWindowPos:Move", FALSE );
+
+ /* popups don't get WM_GETMINMAXINFO */
+ SetWindowLongW( hwnd, GWL_STYLE, WS_VISIBLE|WS_POPUP );
+ SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_FRAMECHANGED);
+ flush_sequence();
+ SetWindowPos( hwnd, 0, 0, 0, 200, 200, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE );
+ ok_sequence(WmSWP_ResizePopupSeq, "SetWindowPos:ResizePopup", FALSE );
+
+ test_sys_menu(hwnd);
+
+ flush_sequence();
+ DestroyWindow(hwnd);
+ ok_sequence(WmDestroyOverlappedSeq, "DestroyWindow:overlapped", FALSE);
+
+ hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hparent != 0, "Failed to create parent window\n");
+ flush_sequence();
+
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_MAXIMIZE,
+ 0, 0, 10, 10, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child window\n");
+ ok_sequence(WmCreateMaximizedChildSeq, "CreateWindow:maximized child", TRUE);
+ DestroyWindow(hchild);
+ flush_sequence();
+
+ /* visible child window with a caption */
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child",
+ WS_CHILD | WS_VISIBLE | WS_CAPTION,
+ 0, 0, 10, 10, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child window\n");
+ ok_sequence(WmCreateVisibleChildSeq, "CreateWindow:visible child", FALSE);
+
+ trace("testing scroll APIs on a visible child window %p\n", hchild);
+ test_scroll_messages(hchild);
+
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+ ok_sequence(WmShowChildSeq_4, "SetWindowPos(SWP_SHOWWINDOW):child with a caption", FALSE);
+
+ DestroyWindow(hchild);
+ flush_sequence();
+
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD,
+ 0, 0, 10, 10, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child window\n");
+ ok_sequence(WmCreateChildSeq, "CreateWindow:child", FALSE);
+
+ hchild2 = CreateWindowExA(0, "SimpleWindowClass", "Test child2", WS_CHILD,
+ 100, 100, 50, 50, hparent, 0, 0, NULL);
+ ok (hchild2 != 0, "Failed to create child2 window\n");
+ flush_sequence();
+
+ hbutton = CreateWindowExA(0, "TestWindowClass", "Test button", WS_CHILD,
+ 0, 100, 50, 50, hchild, 0, 0, NULL);
+ ok (hbutton != 0, "Failed to create button window\n");
+
+ /* test WM_SETREDRAW on a not visible child window */
+ test_WM_SETREDRAW(hchild);
+
+ ShowWindow(hchild, SW_SHOW);
+ ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):child", FALSE);
+
+ ShowWindow(hchild, SW_HIDE);
+ ok_sequence(WmHideChildSeq, "ShowWindow(SW_HIDE):child", FALSE);
+
+ ShowWindow(hchild, SW_SHOW);
+ ok_sequence(WmShowChildSeq, "ShowWindow(SW_SHOW):child", FALSE);
+
+ /* test WM_SETREDRAW on a visible child window */
+ test_WM_SETREDRAW(hchild);
+
+ MoveWindow(hchild, 10, 10, 20, 20, TRUE);
+ ok_sequence(WmResizingChildWithMoveWindowSeq, "MoveWindow:child", FALSE);
+
+ ShowWindow(hchild, SW_HIDE);
+ flush_sequence();
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+ ok_sequence(WmShowChildSeq_2, "SetWindowPos:show_child_2", FALSE);
+
+ ShowWindow(hchild, SW_HIDE);
+ flush_sequence();
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
+ ok_sequence(WmShowChildSeq_3, "SetWindowPos:show_child_3", FALSE);
+
+ /* DestroyWindow sequence below expects that a child has focus */
+ SetFocus(hchild);
+ flush_sequence();
+
+ DestroyWindow(hchild);
+ ok_sequence(WmDestroyChildSeq, "DestroyWindow:child", FALSE);
+ DestroyWindow(hchild2);
+ DestroyWindow(hbutton);
+
+ flush_sequence();
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test Child Popup", WS_CHILD | WS_POPUP,
+ 0, 0, 100, 100, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child popup window\n");
+ ok_sequence(WmCreateChildPopupSeq, "CreateWindow:child_popup", FALSE);
+ DestroyWindow(hchild);
+
+ /* test what happens to a window which sets WS_VISIBLE in WM_CREATE */
+ flush_sequence();
+ hchild = CreateWindowExA(0, "TestPopupClass", "Test Popup", WS_POPUP,
+ 0, 0, 100, 100, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create popup window\n");
+ ok_sequence(WmCreateInvisiblePopupSeq, "CreateWindow:invisible_popup", FALSE);
+ ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+ ok(IsWindowVisible(hchild), "IsWindowVisible() should return TRUE\n");
+ flush_sequence();
+ ShowWindow(hchild, SW_SHOW);
+ ok_sequence(WmEmptySeq, "ShowWindow:show_visible_popup", FALSE);
+ flush_sequence();
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+ ok_sequence(WmShowVisiblePopupSeq_2, "SetWindowPos:show_visible_popup_2", FALSE);
+ flush_sequence();
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
+ ok_sequence(WmShowVisiblePopupSeq_3, "SetWindowPos:show_visible_popup_3", FALSE);
+ DestroyWindow(hchild);
+
+ /* this time add WS_VISIBLE for CreateWindowEx, but this fact actually
+ * changes nothing in message sequences.
+ */
+ flush_sequence();
+ hchild = CreateWindowExA(0, "TestPopupClass", "Test Popup", WS_POPUP | WS_VISIBLE,
+ 0, 0, 100, 100, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create popup window\n");
+ ok_sequence(WmCreateInvisiblePopupSeq, "CreateWindow:invisible_popup", FALSE);
+ ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+ ok(IsWindowVisible(hchild), "IsWindowVisible() should return TRUE\n");
+ flush_sequence();
+ ShowWindow(hchild, SW_SHOW);
+ ok_sequence(WmEmptySeq, "ShowWindow:show_visible_popup", FALSE);
+ flush_sequence();
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+ ok_sequence(WmShowVisiblePopupSeq_2, "SetWindowPos:show_visible_popup_2", FALSE);
+ DestroyWindow(hchild);
+
+ flush_sequence();
+ hwnd = CreateWindowExA(WS_EX_DLGMODALFRAME, "TestDialogClass", NULL, WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_DLGFRAME,
+ 0, 0, 100, 100, hparent, 0, 0, NULL);
+ ok(hwnd != 0, "Failed to create custom dialog window\n");
+ ok_sequence(WmCreateCustomDialogSeq, "CreateCustomDialog", TRUE);
+
+ /*
+ trace("testing scroll APIs on a visible dialog %p\n", hwnd);
+ test_scroll_messages(hwnd);
+ */
+
+ flush_sequence();
+ after_end_dialog = 1;
+ EndDialog( hwnd, 0 );
+ ok_sequence(WmEndCustomDialogSeq, "EndCustomDialog", FALSE);
+
+ DestroyWindow(hwnd);
+ after_end_dialog = 0;
+
+ hwnd = CreateWindowExA(0, "TestDialogClass", NULL, WS_POPUP,
+ 0, 0, 100, 100, 0, 0, GetModuleHandleA(0), NULL);
+ ok(hwnd != 0, "Failed to create custom dialog window\n");
+ flush_sequence();
+ trace("call ShowWindow(%p, SW_SHOW)\n", hwnd);
+ ShowWindow(hwnd, SW_SHOW);
+ ok_sequence(WmShowCustomDialogSeq, "ShowCustomDialog", TRUE);
+ DestroyWindow(hwnd);
+
+ flush_sequence();
+ DialogBoxA( 0, "TEST_DIALOG", hparent, TestModalDlgProcA );
+ ok_sequence(WmModalDialogSeq, "ModalDialog", TRUE);
+
+ /* test showing child with hidden parent */
+ ShowWindow( hparent, SW_HIDE );
+ flush_sequence();
+
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD,
+ 0, 0, 10, 10, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child window\n");
+ ok_sequence(WmCreateChildSeq, "CreateWindow:child", FALSE);
+
+ ShowWindow( hchild, SW_SHOW );
+ ok_sequence(WmShowChildInvisibleParentSeq, "ShowWindow:show child with invisible parent", FALSE);
+ ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+ ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+ ShowWindow( hchild, SW_HIDE );
+ ok_sequence(WmHideChildInvisibleParentSeq, "ShowWindow:hide child with invisible parent", FALSE);
+ ok(!(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should be not set\n");
+ ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+ ok_sequence(WmShowChildInvisibleParentSeq_2, "SetWindowPos:show child with invisible parent", FALSE);
+ ok(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE, "WS_VISIBLE should be set\n");
+ ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_HIDEWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+ ok_sequence(WmHideChildInvisibleParentSeq_2, "SetWindowPos:hide child with invisible parent", FALSE);
+ ok(!(GetWindowLongA(hchild, GWL_STYLE) & WS_VISIBLE), "WS_VISIBLE should not be set\n");
+ ok(!IsWindowVisible(hchild), "IsWindowVisible() should return FALSE\n");
+
+ SetWindowPos(hchild, 0,0,0,0,0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
+ flush_sequence();
+ DestroyWindow(hchild);
+ ok_sequence(WmDestroyInvisibleChildSeq, "DestroyInvisibleChildSeq", FALSE);
+
+ DestroyWindow(hparent);
+ flush_sequence();
+
+ /* Message sequence for SetMenu */
+ ok(!DrawMenuBar(hwnd), "DrawMenuBar should return FALSE for a window without a menu\n");
+ ok_sequence(WmEmptySeq, "DrawMenuBar for a window without a menu", FALSE);
+
+ hmenu = CreateMenu();
+ ok (hmenu != 0, "Failed to create menu\n");
+ ok (InsertMenuA(hmenu, -1, MF_BYPOSITION, 0x1000, "foo"), "InsertMenu failed\n");
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, hmenu, 0, NULL);
+ ok_sequence(WmCreateOverlappedSeq, "CreateWindow:overlapped", FALSE);
+ ok (SetMenu(hwnd, 0), "SetMenu\n");
+ ok_sequence(WmSetMenuNonVisibleSizeChangeSeq, "SetMenu:NonVisibleSizeChange", FALSE);
+ ok (SetMenu(hwnd, 0), "SetMenu\n");
+ ok_sequence(WmSetMenuNonVisibleNoSizeChangeSeq, "SetMenu:NonVisibleNoSizeChange", FALSE);
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow( hwnd );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+ ok (SetMenu(hwnd, 0), "SetMenu\n");
+ ok_sequence(WmSetMenuVisibleNoSizeChangeSeq, "SetMenu:VisibleNoSizeChange", FALSE);
+ ok (SetMenu(hwnd, hmenu), "SetMenu\n");
+ ok_sequence(WmSetMenuVisibleSizeChangeSeq, "SetMenu:VisibleSizeChange", FALSE);
+
+ UpdateWindow( hwnd );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+ ok(DrawMenuBar(hwnd), "DrawMenuBar\n");
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(WmDrawMenuBarSeq, "DrawMenuBar", FALSE);
+
+ DestroyWindow(hwnd);
+ flush_sequence();
+
+ /* Message sequence for EnableWindow */
+ hparent = CreateWindowExA(0, "TestWindowClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hparent != 0, "Failed to create parent window\n");
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_VISIBLE,
+ 0, 0, 10, 10, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child window\n");
+
+ SetFocus(hchild);
+ flush_sequence();
+
+ EnableWindow(hparent, FALSE);
+ ok_sequence(WmEnableWindowSeq_1, "EnableWindow(FALSE)", FALSE);
+
+ EnableWindow(hparent, TRUE);
+ ok_sequence(WmEnableWindowSeq_2, "EnableWindow(TRUE)", FALSE);
+
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+
+ /* MsgWaitForMultipleObjects test */
+ ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE);
+ ok(ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %lx\n", ret);
+
+ PostMessageA(hparent, WM_USER, 0, 0);
+
+ ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE);
+ ok(ret == WAIT_OBJECT_0, "MsgWaitForMultipleObjects returned %lx\n", ret);
+
+ ok(PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n");
+ ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message);
+
+ ret = MsgWaitForMultipleObjects(0, NULL, FALSE, 0, QS_POSTMESSAGE);
+ ok(ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjects returned %lx\n", ret);
+ /* end of MsgWaitForMultipleObjects test */
+
+ /* the following test causes an exception in user.exe under win9x */
+ if (!PostMessageW( hparent, WM_USER, 0, 0 )) return;
+ PostMessageW( hparent, WM_USER+1, 0, 0 );
+ /* PeekMessage(NULL) fails, but still removes the message */
+ SetLastError(0xdeadbeef);
+ ok( !PeekMessageW( NULL, 0, 0, 0, PM_REMOVE ), "PeekMessage(NULL) should fail\n" );
+ ok( GetLastError() == ERROR_NOACCESS || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* NT4 */
+ "last error is %ld\n", GetLastError() );
+ ok( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n" );
+ ok( msg.message == WM_USER+1, "got %x instead of WM_USER+1\n", msg.message );
+
+ DestroyWindow(hchild);
+ DestroyWindow(hparent);
+ flush_sequence();
+
+ test_showwindow();
+}
+
+/****************** button message test *************************/
+static const struct message WmSetFocusButtonSeq[] =
+{
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam, 0 },
+ { WM_CTLCOLORBTN, sent|defwinproc },
+ { 0 }
+};
+static const struct message WmKillFocusButtonSeq[] =
+{
+ { HCBT_SETFOCUS, hook },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_KILLFOCUS, sent|wparam, 0 },
+ { WM_CTLCOLORBTN, sent|defwinproc },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { 0 }
+};
+static const struct message WmSetFocusStaticSeq[] =
+{
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam, 0 },
+ { WM_CTLCOLORSTATIC, sent|defwinproc },
+ { 0 }
+};
+static const struct message WmKillFocusStaticSeq[] =
+{
+ { HCBT_SETFOCUS, hook },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_KILLFOCUS, sent|wparam, 0 },
+ { WM_CTLCOLORSTATIC, sent|defwinproc },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { 0 }
+};
+static const struct message WmLButtonDownSeq[] =
+{
+ { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_CTLCOLORBTN, sent|defwinproc },
+ { BM_SETSTATE, sent|wparam|defwinproc, TRUE },
+ { WM_CTLCOLORBTN, sent|defwinproc },
+ { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { 0 }
+};
+static const struct message WmLButtonUpSeq[] =
+{
+ { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 },
+ { BM_SETSTATE, sent|wparam|defwinproc, FALSE },
+ { WM_CTLCOLORBTN, sent|defwinproc },
+ { EVENT_OBJECT_STATECHANGE, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CAPTURECHANGED, sent|wparam|defwinproc, 0 },
+ { 0 }
+};
+
+static WNDPROC old_button_proc;
+
+static LRESULT CALLBACK button_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ trace("button: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ /* explicitly ignore WM_GETICON message */
+ if (message == WM_GETICON) return 0;
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+
+ if (message == BM_SETSTATE)
+ ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
+
+ defwndproc_counter++;
+ ret = CallWindowProcA(old_button_proc, hwnd, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static void subclass_button(void)
+{
+ WNDCLASSA cls;
+
+ if (!GetClassInfoA(0, "button", &cls)) assert(0);
+
+ old_button_proc = cls.lpfnWndProc;
+
+ cls.hInstance = GetModuleHandle(0);
+ cls.lpfnWndProc = button_hook_proc;
+ cls.lpszClassName = "my_button_class";
+ if (!RegisterClassA(&cls)) assert(0);
+}
+
+static void test_button_messages(void)
+{
+ static const struct
+ {
+ DWORD style;
+ DWORD dlg_code;
+ const struct message *setfocus;
+ const struct message *killfocus;
+ } button[] = {
+ { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
+ WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+ { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
+ WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+ { BS_CHECKBOX, DLGC_BUTTON,
+ WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+ { BS_AUTOCHECKBOX, DLGC_BUTTON,
+ WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+ { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
+ WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+ { BS_3STATE, DLGC_BUTTON,
+ WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+ { BS_AUTO3STATE, DLGC_BUTTON,
+ WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+ { BS_GROUPBOX, DLGC_STATIC,
+ WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+ { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
+ WmSetFocusButtonSeq, WmKillFocusButtonSeq },
+ { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
+ WmSetFocusStaticSeq, WmKillFocusStaticSeq },
+ { BS_OWNERDRAW, DLGC_BUTTON,
+ WmSetFocusButtonSeq, WmKillFocusButtonSeq }
+ };
+ unsigned int i;
+ HWND hwnd;
+ DWORD dlg_code;
+
+ subclass_button();
+
+ for (i = 0; i < sizeof(button)/sizeof(button[0]); i++)
+ {
+ hwnd = CreateWindowExA(0, "my_button_class", "test", button[i].style | WS_POPUP,
+ 0, 0, 50, 14, 0, 0, 0, NULL);
+ ok(hwnd != 0, "Failed to create button window\n");
+
+ dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
+ ok(dlg_code == button[i].dlg_code, "%d: wrong dlg_code %08lx\n", i, dlg_code);
+
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow(hwnd);
+ SetFocus(0);
+ flush_sequence();
+
+ trace("button style %08lx\n", button[i].style);
+ SetFocus(hwnd);
+ ok_sequence(button[i].setfocus, "SetFocus(hwnd) on a button", FALSE);
+
+ SetFocus(0);
+ ok_sequence(button[i].killfocus, "SetFocus(0) on a button", FALSE);
+
+ DestroyWindow(hwnd);
+ }
+
+ hwnd = CreateWindowExA(0, "my_button_class", "test", BS_PUSHBUTTON | WS_POPUP | WS_VISIBLE,
+ 0, 0, 50, 14, 0, 0, 0, NULL);
+ ok(hwnd != 0, "Failed to create button window\n");
+
+ SetFocus(0);
+ flush_sequence();
+
+ SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
+ ok_sequence(WmLButtonDownSeq, "WM_LBUTTONDOWN on a button", FALSE);
+
+ SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
+ ok_sequence(WmLButtonUpSeq, "WM_LBUTTONUP on a button", FALSE);
+ DestroyWindow(hwnd);
+}
+
+/************* painting message test ********************/
+
+void dump_region(HRGN hrgn)
+{
+ DWORD i, size;
+ RGNDATA *data = NULL;
+ RECT *rect;
+
+ if (!hrgn)
+ {
+ printf( "null region\n" );
+ return;
+ }
+ if (!(size = GetRegionData( hrgn, 0, NULL ))) return;
+ if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return;
+ GetRegionData( hrgn, size, data );
+ printf("%ld rects:", data->rdh.nCount );
+ for (i = 0, rect = (RECT *)data->Buffer; i < data->rdh.nCount; i++, rect++)
+ printf( " (%ld,%ld)-(%ld,%ld)", rect->left, rect->top, rect->right, rect->bottom );
+ printf("\n");
+ HeapFree( GetProcessHeap(), 0, data );
+}
+
+static void check_update_rgn( HWND hwnd, HRGN hrgn )
+{
+ INT ret;
+ RECT r1, r2;
+ HRGN tmp = CreateRectRgn( 0, 0, 0, 0 );
+ HRGN update = CreateRectRgn( 0, 0, 0, 0 );
+
+ ret = GetUpdateRgn( hwnd, update, FALSE );
+ ok( ret != ERROR, "GetUpdateRgn failed\n" );
+ if (ret == NULLREGION)
+ {
+ ok( !hrgn, "Update region shouldn't be empty\n" );
+ }
+ else
+ {
+ if (CombineRgn( tmp, hrgn, update, RGN_XOR ) != NULLREGION)
+ {
+ ok( 0, "Regions are different\n" );
+ if (winetest_debug > 0)
+ {
+ printf( "Update region: " );
+ dump_region( update );
+ printf( "Wanted region: " );
+ dump_region( hrgn );
+ }
+ }
+ }
+ GetRgnBox( update, &r1 );
+ GetUpdateRect( hwnd, &r2, FALSE );
+ ok( r1.left == r2.left && r1.top == r2.top && r1.right == r2.right && r1.bottom == r2.bottom,
+ "Rectangles are different: %ld,%ld-%ld,%ld / %ld,%ld-%ld,%ld\n",
+ r1.left, r1.top, r1.right, r1.bottom, r2.left, r2.top, r2.right, r2.bottom );
+
+ DeleteObject( tmp );
+ DeleteObject( update );
+}
+
+static const struct message WmInvalidateRgn[] = {
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { 0 }
+};
+
+static const struct message WmGetUpdateRect[] = {
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_PAINT, sent },
+ { 0 }
+};
+
+static const struct message WmInvalidateFull[] = {
+ { WM_NCPAINT, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { 0 }
+};
+
+static const struct message WmInvalidateErase[] = {
+ { WM_NCPAINT, sent|wparam, 1 },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { 0 }
+};
+
+static const struct message WmInvalidatePaint[] = {
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|wparam|beginpaint, 1 },
+ { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+ { 0 }
+};
+
+static const struct message WmInvalidateErasePaint[] = {
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|wparam|beginpaint, 1 },
+ { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+static const struct message WmInvalidateErasePaint2[] = {
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|beginpaint },
+ { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+static const struct message WmErase[] = {
+ { WM_ERASEBKGND, sent },
+ { 0 }
+};
+
+static const struct message WmPaint[] = {
+ { WM_PAINT, sent },
+ { 0 }
+};
+
+static const struct message WmParentOnlyPaint[] = {
+ { WM_PAINT, sent|parent },
+ { 0 }
+};
+
+static const struct message WmInvalidateParent[] = {
+ { WM_NCPAINT, sent|parent },
+ { WM_GETTEXT, sent|defwinproc|parent|optional },
+ { WM_ERASEBKGND, sent|parent },
+ { 0 }
+};
+
+static const struct message WmInvalidateParentChild[] = {
+ { WM_NCPAINT, sent|parent },
+ { WM_GETTEXT, sent|defwinproc|parent|optional },
+ { WM_ERASEBKGND, sent|parent },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { 0 }
+};
+
+static const struct message WmInvalidateParentChild2[] = {
+ { WM_ERASEBKGND, sent|parent },
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { 0 }
+};
+
+static const struct message WmParentPaint[] = {
+ { WM_PAINT, sent|parent },
+ { WM_PAINT, sent },
+ { 0 }
+};
+
+static const struct message WmParentPaintNc[] = {
+ { WM_PAINT, sent|parent },
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|beginpaint },
+ { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+static const struct message WmChildPaintNc[] = {
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|beginpaint },
+ { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+static const struct message WmParentErasePaint[] = {
+ { WM_PAINT, sent|parent },
+ { WM_NCPAINT, sent|parent|beginpaint },
+ { WM_GETTEXT, sent|parent|beginpaint|defwinproc|optional },
+ { WM_ERASEBKGND, sent|parent|beginpaint },
+ { WM_PAINT, sent },
+ { WM_NCPAINT, sent|beginpaint },
+ { WM_GETTEXT, sent|beginpaint|defwinproc|optional },
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+static const struct message WmParentOnlyNcPaint[] = {
+ { WM_PAINT, sent|parent },
+ { WM_NCPAINT, sent|parent|beginpaint },
+ { WM_GETTEXT, sent|parent|beginpaint|defwinproc|optional },
+ { 0 }
+};
+
+static const struct message WmSetParentStyle[] = {
+ { WM_STYLECHANGING, sent|parent },
+ { WM_STYLECHANGED, sent|parent },
+ { 0 }
+};
+
+static void test_paint_messages(void)
+{
+ BOOL ret;
+ RECT rect;
+ POINT pt;
+ MSG msg;
+ HWND hparent, hchild;
+ HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
+ HRGN hrgn2 = CreateRectRgn( 0, 0, 0, 0 );
+ HWND hwnd = CreateWindowExA(0, "TestWindowClass", "Test overlapped", WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create overlapped window\n");
+
+ ShowWindow( hwnd, SW_SHOW );
+ UpdateWindow( hwnd );
+ flush_events();
+
+ check_update_rgn( hwnd, 0 );
+ SetRectRgn( hrgn, 10, 10, 20, 20 );
+ ret = RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE );
+ ok(ret, "RedrawWindow returned %d instead of TRUE\n", ret);
+ check_update_rgn( hwnd, hrgn );
+ SetRectRgn( hrgn2, 20, 20, 30, 30 );
+ ret = RedrawWindow( hwnd, NULL, hrgn2, RDW_INVALIDATE );
+ ok(ret, "RedrawWindow returned %d instead of TRUE\n", ret);
+ CombineRgn( hrgn, hrgn, hrgn2, RGN_OR );
+ check_update_rgn( hwnd, hrgn );
+ /* validate everything */
+ ret = RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+ ok(ret, "RedrawWindow returned %d instead of TRUE\n", ret);
+ check_update_rgn( hwnd, 0 );
+
+ /* test empty region */
+ SetRectRgn( hrgn, 10, 10, 10, 15 );
+ ret = RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE );
+ ok(ret, "RedrawWindow returned %d instead of TRUE\n", ret);
+ check_update_rgn( hwnd, 0 );
+ /* test empty rect */
+ SetRect( &rect, 10, 10, 10, 15 );
+ ret = RedrawWindow( hwnd, &rect, NULL, RDW_INVALIDATE );
+ ok(ret, "RedrawWindow returned %d instead of TRUE\n", ret);
+ check_update_rgn( hwnd, 0 );
+
+ /* flush pending messages */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+
+ GetClientRect( hwnd, &rect );
+ SetRectRgn( hrgn, 0, 0, rect.right - rect.left, rect.bottom - rect.top );
+ /* MSDN: if hwnd parameter is NULL, InvalidateRect invalidates and redraws
+ * all windows and sends WM_ERASEBKGND and WM_NCPAINT.
+ */
+ trace("testing InvalidateRect(0, NULL, FALSE)\n");
+ SetRectEmpty( &rect );
+ ok(InvalidateRect(0, &rect, FALSE), "InvalidateRect(0, &rc, FALSE) should fail\n");
+ check_update_rgn( hwnd, hrgn );
+ ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmPaint, "Paint", FALSE );
+ RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+ check_update_rgn( hwnd, 0 );
+
+ /* MSDN: if hwnd parameter is NULL, ValidateRect invalidates and redraws
+ * all windows and sends WM_ERASEBKGND and WM_NCPAINT.
+ */
+ trace("testing ValidateRect(0, NULL)\n");
+ SetRectEmpty( &rect );
+ ok(ValidateRect(0, &rect), "ValidateRect(0, &rc) should not fail\n");
+ check_update_rgn( hwnd, hrgn );
+ ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmPaint, "Paint", FALSE );
+ RedrawWindow( hwnd, NULL, NULL, RDW_VALIDATE );
+ check_update_rgn( hwnd, 0 );
+
+ trace("testing InvalidateRgn(0, NULL, FALSE)\n");
+ SetLastError(0xdeadbeef);
+ ok(!InvalidateRgn(0, NULL, FALSE), "InvalidateRgn(0, NULL, FALSE) should fail\n");
+ ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE, "wrong error code %ld\n", GetLastError());
+ check_update_rgn( hwnd, 0 );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmEmptySeq, "WmEmptySeq", FALSE );
+
+ trace("testing ValidateRgn(0, NULL)\n");
+ SetLastError(0xdeadbeef);
+ ok(!ValidateRgn(0, NULL), "ValidateRgn(0, NULL) should fail\n");
+ ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE, "wrong error code %ld\n", GetLastError());
+ check_update_rgn( hwnd, 0 );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmEmptySeq, "WmEmptySeq", FALSE );
+
+ /* now with frame */
+ SetRectRgn( hrgn, -5, -5, 20, 20 );
+
+ /* flush pending messages */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+
+ flush_sequence();
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+ ok_sequence( WmEmptySeq, "EmptySeq", FALSE );
+
+ SetRectRgn( hrgn, 0, 0, 20, 20 ); /* GetUpdateRgn clips to client area */
+ check_update_rgn( hwnd, hrgn );
+
+ flush_sequence();
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW );
+ ok_sequence( WmInvalidateRgn, "InvalidateRgn", FALSE );
+
+ flush_sequence();
+ RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW );
+ ok_sequence( WmInvalidateFull, "InvalidateFull", FALSE );
+
+ GetClientRect( hwnd, &rect );
+ SetRectRgn( hrgn, rect.left, rect.top, rect.right, rect.bottom );
+ check_update_rgn( hwnd, hrgn );
+
+ flush_sequence();
+ RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW );
+ ok_sequence( WmInvalidateErase, "InvalidateErase", FALSE );
+
+ flush_sequence();
+ RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASENOW | RDW_UPDATENOW );
+ ok_sequence( WmInvalidatePaint, "InvalidatePaint", FALSE );
+ check_update_rgn( hwnd, 0 );
+
+ flush_sequence();
+ RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_UPDATENOW );
+ ok_sequence( WmInvalidateErasePaint, "InvalidateErasePaint", FALSE );
+ check_update_rgn( hwnd, 0 );
+
+ flush_sequence();
+ SetRectRgn( hrgn, 0, 0, 100, 100 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE );
+ SetRectRgn( hrgn, 0, 0, 50, 100 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE );
+ SetRectRgn( hrgn, 50, 0, 100, 100 );
+ check_update_rgn( hwnd, hrgn );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_ERASENOW );
+ ok_sequence( WmEmptySeq, "EmptySeq", FALSE ); /* must not generate messages, everything is valid */
+ check_update_rgn( hwnd, 0 );
+
+ flush_sequence();
+ SetRectRgn( hrgn, 0, 0, 100, 100 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE );
+ SetRectRgn( hrgn, 0, 0, 100, 50 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_ERASENOW );
+ ok_sequence( WmErase, "Erase", FALSE );
+ SetRectRgn( hrgn, 0, 50, 100, 100 );
+ check_update_rgn( hwnd, hrgn );
+
+ flush_sequence();
+ SetRectRgn( hrgn, 0, 0, 100, 100 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_ERASE );
+ SetRectRgn( hrgn, 0, 0, 50, 50 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOERASE | RDW_UPDATENOW );
+ ok_sequence( WmPaint, "Paint", FALSE );
+
+ flush_sequence();
+ SetRectRgn( hrgn, -4, -4, -2, -2 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+ SetRectRgn( hrgn, -200, -200, -198, -198 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOFRAME | RDW_ERASENOW );
+ ok_sequence( WmEmptySeq, "EmptySeq", FALSE );
+
+ flush_sequence();
+ SetRectRgn( hrgn, -4, -4, -2, -2 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+ SetRectRgn( hrgn, -4, -4, -3, -3 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_VALIDATE | RDW_NOFRAME );
+ SetRectRgn( hrgn, 0, 0, 1, 1 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_UPDATENOW );
+ ok_sequence( WmPaint, "Paint", FALSE );
+
+ flush_sequence();
+ SetRectRgn( hrgn, -4, -4, -1, -1 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+ RedrawWindow( hwnd, NULL, 0, RDW_ERASENOW );
+ /* make sure no WM_PAINT was generated */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmInvalidateRgn, "InvalidateRgn", FALSE );
+
+ flush_sequence();
+ SetRectRgn( hrgn, -4, -4, -1, -1 );
+ RedrawWindow( hwnd, NULL, hrgn, RDW_INVALIDATE | RDW_FRAME );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
+ {
+ if (msg.hwnd == hwnd && msg.message == WM_PAINT)
+ {
+ /* GetUpdateRgn must return empty region since only nonclient area is invalidated */
+ INT ret = GetUpdateRgn( hwnd, hrgn, FALSE );
+ ok( ret == NULLREGION, "Invalid GetUpdateRgn result %d\n", ret );
+ ret = GetUpdateRect( hwnd, &rect, FALSE );
+ ok( ret, "Invalid GetUpdateRect result %d\n", ret );
+ /* this will send WM_NCPAINT and validate the non client area */
+ ret = GetUpdateRect( hwnd, &rect, TRUE );
+ ok( !ret, "Invalid GetUpdateRect result %d\n", ret );
+ }
+ DispatchMessage( &msg );
+ }
+ ok_sequence( WmGetUpdateRect, "GetUpdateRect", FALSE );
+
+ DestroyWindow( hwnd );
+
+ /* now test with a child window */
+
+ hparent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hparent != 0, "Failed to create parent window\n");
+
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child", WS_CHILD | WS_VISIBLE | WS_BORDER,
+ 10, 10, 100, 100, hparent, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child window\n");
+
+ ShowWindow( hparent, SW_SHOW );
+ UpdateWindow( hparent );
+ UpdateWindow( hchild );
+ flush_events();
+ flush_sequence();
+ log_all_parent_messages++;
+
+ SetRect( &rect, 0, 0, 50, 50 );
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ RedrawWindow( hparent, NULL, 0, RDW_ERASENOW | RDW_ALLCHILDREN );
+ ok_sequence( WmInvalidateParentChild, "InvalidateParentChild", FALSE );
+
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ pt.x = pt.y = 0;
+ MapWindowPoints( hchild, hparent, &pt, 1 );
+ SetRectRgn( hrgn, 0, 0, 50 - pt.x, 50 - pt.y );
+ check_update_rgn( hchild, hrgn );
+ SetRectRgn( hrgn, 0, 0, 50, 50 );
+ check_update_rgn( hparent, hrgn );
+ RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+ ok_sequence( WmInvalidateParent, "InvalidateParent", FALSE );
+ RedrawWindow( hchild, NULL, 0, RDW_ERASENOW );
+ ok_sequence( WmEmptySeq, "EraseNow child", FALSE );
+
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentPaintNc, "WmParentPaintNc", FALSE );
+
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN );
+ RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+ ok_sequence( WmInvalidateParent, "InvalidateParent2", FALSE );
+ RedrawWindow( hchild, NULL, 0, RDW_ERASENOW );
+ ok_sequence( WmEmptySeq, "EraseNow child", FALSE );
+
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE );
+ RedrawWindow( hparent, NULL, 0, RDW_ERASENOW | RDW_ALLCHILDREN );
+ ok_sequence( WmInvalidateParentChild2, "InvalidateParentChild2", FALSE );
+
+ SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) | WS_CLIPCHILDREN );
+ flush_sequence();
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN );
+ RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+ ok_sequence( WmInvalidateParentChild, "InvalidateParentChild3", FALSE );
+
+ /* flush all paint messages */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+
+ /* RDW_UPDATENOW on child with WS_CLIPCHILDREN doesn't change corresponding parent area */
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN );
+ SetRectRgn( hrgn, 0, 0, 50, 50 );
+ check_update_rgn( hparent, hrgn );
+ RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW );
+ ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+ SetRectRgn( hrgn, 0, 0, 50, 50 );
+ check_update_rgn( hparent, hrgn );
+
+ /* flush all paint messages */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) & ~WS_CLIPCHILDREN );
+ flush_sequence();
+
+ /* RDW_UPDATENOW on child without WS_CLIPCHILDREN will validate corresponding parent area */
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetRectRgn( hrgn, 0, 0, 50, 50 );
+ check_update_rgn( hparent, hrgn );
+ RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW );
+ ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+ SetRectRgn( hrgn2, 10, 10, 50, 50 );
+ CombineRgn( hrgn, hrgn, hrgn2, RGN_DIFF );
+ check_update_rgn( hparent, hrgn );
+ /* flush all paint messages */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+
+ /* same as above but parent gets completely validated */
+ SetRect( &rect, 20, 20, 30, 30 );
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetRectRgn( hrgn, 20, 20, 30, 30 );
+ check_update_rgn( hparent, hrgn );
+ RedrawWindow( hchild, NULL, 0, RDW_UPDATENOW );
+ ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+ check_update_rgn( hparent, 0 ); /* no update region */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmEmptySeq, "WmEmpty", FALSE ); /* and no paint messages */
+
+ /* make sure RDW_VALIDATE on child doesn't have the same effect */
+ flush_sequence();
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetRectRgn( hrgn, 20, 20, 30, 30 );
+ check_update_rgn( hparent, hrgn );
+ RedrawWindow( hchild, NULL, 0, RDW_VALIDATE | RDW_NOERASE );
+ SetRectRgn( hrgn, 20, 20, 30, 30 );
+ check_update_rgn( hparent, hrgn );
+
+ /* same as above but normal WM_PAINT doesn't validate parent */
+ flush_sequence();
+ SetRect( &rect, 20, 20, 30, 30 );
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetRectRgn( hrgn, 20, 20, 30, 30 );
+ check_update_rgn( hparent, hrgn );
+ /* no WM_PAINT in child while parent still pending */
+ while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE );
+ while (PeekMessage( &msg, hparent, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentErasePaint, "WmParentErasePaint", FALSE );
+
+ flush_sequence();
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ /* no WM_PAINT in child while parent still pending */
+ while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE );
+ RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_NOERASE | RDW_NOCHILDREN );
+ /* now that parent is valid child should get WM_PAINT */
+ while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmInvalidateErasePaint2, "WmInvalidateErasePaint2", FALSE );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmEmptySeq, "No other message", FALSE );
+
+ /* same thing with WS_CLIPCHILDREN in parent */
+ flush_sequence();
+ SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) | WS_CLIPCHILDREN );
+ ok_sequence( WmSetParentStyle, "WmSetParentStyle", FALSE );
+ /* changing style invalidates non client area, but we need to invalidate something else to see it */
+ RedrawWindow( hparent, &rect, 0, RDW_UPDATENOW );
+ ok_sequence( WmEmptySeq, "No message", FALSE );
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_UPDATENOW );
+ ok_sequence( WmParentOnlyNcPaint, "WmParentOnlyNcPaint", FALSE );
+
+ flush_sequence();
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN );
+ SetRectRgn( hrgn, 20, 20, 30, 30 );
+ check_update_rgn( hparent, hrgn );
+ /* no WM_PAINT in child while parent still pending */
+ while (PeekMessage( &msg, hchild, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmEmptySeq, "No WM_PAINT", FALSE );
+ /* WM_PAINT in parent first */
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentPaintNc, "WmParentPaintNc2", FALSE );
+
+ /* no RDW_ERASE in parent still causes RDW_ERASE and RDW_FRAME in child */
+ flush_sequence();
+ SetRect( &rect, 0, 0, 30, 30 );
+ RedrawWindow( hparent, &rect, 0, RDW_INVALIDATE | RDW_ALLCHILDREN );
+ SetRectRgn( hrgn, 0, 0, 30, 30 );
+ check_update_rgn( hparent, hrgn );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentPaintNc, "WmParentPaintNc3", FALSE );
+
+ /* validate doesn't cause RDW_NOERASE or RDW_NOFRAME in child */
+ flush_sequence();
+ SetRect( &rect, -10, 0, 30, 30 );
+ RedrawWindow( hchild, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE );
+ SetRect( &rect, 0, 0, 20, 20 );
+ RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_ALLCHILDREN );
+ RedrawWindow( hparent, NULL, 0, RDW_UPDATENOW );
+ ok_sequence( WmChildPaintNc, "WmChildPaintNc", FALSE );
+
+ /* validate doesn't cause RDW_NOERASE or RDW_NOFRAME in child */
+ flush_sequence();
+ SetRect( &rect, -10, 0, 30, 30 );
+ RedrawWindow( hchild, &rect, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE );
+ SetRect( &rect, 0, 0, 100, 100 );
+ RedrawWindow( hparent, &rect, 0, RDW_VALIDATE | RDW_ALLCHILDREN );
+ RedrawWindow( hparent, NULL, 0, RDW_UPDATENOW );
+ ok_sequence( WmEmptySeq, "WmChildPaintNc2", FALSE );
+ RedrawWindow( hparent, NULL, 0, RDW_ERASENOW );
+ ok_sequence( WmEmptySeq, "WmChildPaintNc3", FALSE );
+
+ /* test RDW_INTERNALPAINT behavior */
+
+ flush_sequence();
+ RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT | RDW_NOCHILDREN );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentOnlyPaint, "WmParentOnlyPaint", FALSE );
+
+ RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT | RDW_ALLCHILDREN );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentPaint, "WmParentPaint", FALSE );
+
+ RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentOnlyPaint, "WmParentOnlyPaint", FALSE );
+
+ assert( GetWindowLong(hparent, GWL_STYLE) & WS_CLIPCHILDREN );
+ UpdateWindow( hparent );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+ trace("testing SWP_FRAMECHANGED on parent with WS_CLIPCHILDREN\n");
+ RedrawWindow( hchild, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetWindowPos( hparent, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(WmSWP_FrameChanged_clip, "SetWindowPos:FrameChanged_clip", FALSE );
+
+ UpdateWindow( hparent );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+ trace("testing SWP_FRAMECHANGED|SWP_DEFERERASE on parent with WS_CLIPCHILDREN\n");
+ RedrawWindow( hchild, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetWindowPos( hparent, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_DEFERERASE |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(WmSWP_FrameChangedDeferErase, "SetWindowPos:FrameChangedDeferErase", FALSE );
+
+ SetWindowLong( hparent, GWL_STYLE, GetWindowLong(hparent,GWL_STYLE) & ~WS_CLIPCHILDREN );
+ ok_sequence( WmSetParentStyle, "WmSetParentStyle", FALSE );
+ RedrawWindow( hparent, NULL, 0, RDW_INTERNALPAINT );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence( WmParentPaint, "WmParentPaint", FALSE );
+
+ assert( !(GetWindowLong(hparent, GWL_STYLE) & WS_CLIPCHILDREN) );
+ UpdateWindow( hparent );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+ trace("testing SWP_FRAMECHANGED on parent without WS_CLIPCHILDREN\n");
+ RedrawWindow( hchild, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetWindowPos( hparent, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(WmSWP_FrameChanged_noclip, "SetWindowPos:FrameChanged_noclip", FALSE );
+
+ UpdateWindow( hparent );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+ trace("testing SWP_FRAMECHANGED|SWP_DEFERERASE on parent without WS_CLIPCHILDREN\n");
+ RedrawWindow( hchild, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME );
+ SetWindowPos( hparent, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_DEFERERASE |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(WmSWP_FrameChangedDeferErase, "SetWindowPos:FrameChangedDeferErase", FALSE );
+
+ log_all_parent_messages--;
+ DestroyWindow( hparent );
+ ok(!IsWindow(hchild), "child must be destroyed with its parent\n");
+
+ DeleteObject( hrgn );
+ DeleteObject( hrgn2 );
+}
+
+struct wnd_event
+{
+ HWND hwnd;
+ HANDLE event;
+};
+
+static DWORD WINAPI thread_proc(void *param)
+{
+ MSG msg;
+ struct wnd_event *wnd_event = (struct wnd_event *)param;
+
+ wnd_event->hwnd = CreateWindowExA(0, "TestWindowClass", "window caption text", WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok(wnd_event->hwnd != 0, "Failed to create overlapped window\n");
+
+ SetEvent(wnd_event->event);
+
+ while (GetMessage(&msg, 0, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ ok(IsWindow(wnd_event->hwnd), "window should still exist\n");
+
+ return 0;
+}
+
+static void test_interthread_messages(void)
+{
+ HANDLE hThread;
+ DWORD tid;
+ WNDPROC proc;
+ MSG msg;
+ char buf[256];
+ int len, expected_len;
+ struct wnd_event wnd_event;
+ BOOL ret;
+
+ wnd_event.event = CreateEventW(NULL, 0, 0, NULL);
+ if (!wnd_event.event)
+ {
+ trace("skipping interthread message test under win9x\n");
+ return;
+ }
+
+ hThread = CreateThread(NULL, 0, thread_proc, &wnd_event, 0, &tid);
+ ok(hThread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+
+ ok(WaitForSingleObject(wnd_event.event, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+
+ CloseHandle(wnd_event.event);
+
+ SetLastError(0xdeadbeef);
+ ok(!DestroyWindow(wnd_event.hwnd), "DestroyWindow succeded\n");
+ ok(GetLastError() == ERROR_ACCESS_DENIED, "wrong error code %ld\n", GetLastError());
+
+ proc = (WNDPROC)GetWindowLongPtrA(wnd_event.hwnd, GWLP_WNDPROC);
+ ok(proc != NULL, "GetWindowLongPtrA(GWLP_WNDPROC) error %ld\n", GetLastError());
+
+ expected_len = lstrlenA("window caption text");
+ memset(buf, 0, sizeof(buf));
+ SetLastError(0xdeadbeef);
+ len = CallWindowProcA(proc, wnd_event.hwnd, WM_GETTEXT, sizeof(buf), (LPARAM)buf);
+ ok(len == expected_len, "CallWindowProcA(WM_GETTEXT) error %ld, len %d, expected len %d\n", GetLastError(), len, expected_len);
+ ok(!lstrcmpA(buf, "window caption text"), "window text mismatch\n");
+
+ msg.hwnd = wnd_event.hwnd;
+ msg.message = WM_GETTEXT;
+ msg.wParam = sizeof(buf);
+ msg.lParam = (LPARAM)buf;
+ memset(buf, 0, sizeof(buf));
+ SetLastError(0xdeadbeef);
+ len = DispatchMessageA(&msg);
+ ok(!len && GetLastError() == ERROR_MESSAGE_SYNC_ONLY,
+ "DispatchMessageA(WM_GETTEXT) succeded on another thread window: ret %d, error %ld\n", len, GetLastError());
+
+ /* the following test causes an exception in user.exe under win9x */
+ msg.hwnd = wnd_event.hwnd;
+ msg.message = WM_TIMER;
+ msg.wParam = 0;
+ msg.lParam = GetWindowLongPtrA(wnd_event.hwnd, GWLP_WNDPROC);
+ SetLastError(0xdeadbeef);
+ len = DispatchMessageA(&msg);
+ ok(!len && GetLastError() == 0xdeadbeef,
+ "DispatchMessageA(WM_TIMER) failed on another thread window: ret %d, error %ld\n", len, GetLastError());
+
+ ret = PostMessageA(wnd_event.hwnd, WM_QUIT, 0, 0);
+ ok( ret, "PostMessageA(WM_QUIT) error %ld\n", GetLastError());
+
+ ok(WaitForSingleObject(hThread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+ CloseHandle(hThread);
+
+ ok(!IsWindow(wnd_event.hwnd), "window should be destroyed on thread exit\n");
+}
+
+
+static const struct message WmVkN[] = {
+ { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 },
+ { WM_CHAR, wparam|lparam, 'n', 1 },
+ { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1002,1), 0 },
+ { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+ { 0 }
+};
+static const struct message WmShiftVkN[] = {
+ { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 1 },
+ { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 },
+ { WM_CHAR, wparam|lparam, 'N', 1 },
+ { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1001,1), 0 },
+ { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmCtrlVkN[] = {
+ { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, 'N', 1 },
+ { WM_CHAR, wparam|lparam, 0x000e, 1 },
+ { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1000,1), 0 },
+ { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmCtrlVkN_2[] = {
+ { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+ { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1000,1), 0 },
+ { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmAltVkN[] = {
+ { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_SYSKEYDOWN, wparam|lparam, 'N', 0x20000001 },
+ { WM_SYSKEYDOWN, sent|wparam|lparam, 'N', 0x20000001 },
+ { WM_SYSCHAR, wparam|lparam, 'n', 0x20000001 },
+ { WM_SYSCHAR, sent|wparam|lparam, 'n', 0x20000001 },
+ { WM_SYSCOMMAND, sent|defwinproc|wparam|lparam, SC_KEYMENU, 'n' },
+ { HCBT_SYSCOMMAND, hook },
+ { WM_ENTERMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 },
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+ { 0x00AE, sent|defwinproc|optional }, /* XP */
+ { WM_GETTEXT, sent|defwinproc|optional }, /* XP */
+ { WM_INITMENU, sent|defwinproc },
+ { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+ { WM_MENUCHAR, sent|defwinproc|wparam, MAKEWPARAM('n',MF_SYSMENU) },
+ { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CAPTURECHANGED, sent|defwinproc },
+ { WM_MENUSELECT, sent|defwinproc|wparam, MAKEWPARAM(0,0xffff) },
+ { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+ { WM_EXITMENULOOP, sent|defwinproc },
+ { WM_MENUSELECT, sent|defwinproc|wparam|optional, MAKEWPARAM(0,0xffff) }, /* Win95 bug */
+ { WM_EXITMENULOOP, sent|defwinproc|optional }, /* Win95 bug */
+ { WM_SYSKEYUP, wparam|lparam, 'N', 0xe0000001 },
+ { WM_SYSKEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+ { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmAltVkN_2[] = {
+ { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_SYSKEYDOWN, wparam|lparam, 'N', 0x20000001 },
+ { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1003,1), 0 },
+ { WM_SYSKEYUP, wparam|lparam, 'N', 0xe0000001 },
+ { WM_SYSKEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+ { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmCtrlAltVkN[] = {
+ { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_KEYDOWN, wparam|lparam, 'N', 0x20000001 },
+ { WM_KEYDOWN, sent|wparam|lparam, 'N', 0x20000001 },
+ { WM_KEYUP, wparam|lparam, 'N', 0xe0000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+ { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmCtrlShiftVkN[] = {
+ { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 1 },
+ { WM_KEYDOWN, wparam|lparam, 'N', 1 },
+ { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1004,1), 0 },
+ { WM_KEYUP, wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+ { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xc0000001 },
+ { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmCtrlAltShiftVkN[] = {
+ { WM_KEYDOWN, wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_CONTROL, 1 },
+ { WM_KEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_KEYDOWN, wparam|lparam, VK_SHIFT, 0x20000001 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_SHIFT, 0x20000001 },
+ { WM_KEYDOWN, wparam|lparam, 'N', 0x20000001 },
+ { WM_COMMAND, sent|wparam|lparam, MAKEWPARAM(1005,1), 0 },
+ { WM_KEYUP, wparam|lparam, 'N', 0xe0000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xe0000001 },
+ { WM_KEYUP, wparam|lparam, VK_SHIFT, 0xe0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_SHIFT, 0xe0000001 },
+ { WM_KEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_KEYUP, wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_CONTROL, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmAltPressRelease[] = {
+ { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_SYSCOMMAND, sent|defwinproc|wparam|lparam, SC_KEYMENU, 0 },
+ { HCBT_SYSCOMMAND, hook },
+ { WM_ENTERMENULOOP, sent|defwinproc|wparam|lparam, 0, 0 },
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_INITMENU, sent|defwinproc },
+ { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+ { WM_MENUSELECT, sent|defwinproc|wparam, MAKEWPARAM(0,MF_SYSMENU|MF_POPUP|MF_HILITE) },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_SYSMENU, 1 },
+
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+ { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0, },
+ { WM_CAPTURECHANGED, sent|defwinproc },
+ { WM_MENUSELECT, sent|defwinproc|wparam|optional, MAKEWPARAM(0,0xffff) },
+ { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_SYSMENU, 0 },
+ { WM_EXITMENULOOP, sent|defwinproc },
+ { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmAltMouseButton[] = {
+ { WM_SYSKEYDOWN, wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_SYSKEYDOWN, sent|wparam|lparam, VK_MENU, 0x20000001 },
+ { WM_MOUSEMOVE, wparam|optional, 0, 0 },
+ { WM_MOUSEMOVE, sent|wparam|optional, 0, 0 },
+ { WM_LBUTTONDOWN, wparam, MK_LBUTTON, 0 },
+ { WM_LBUTTONDOWN, sent|wparam, MK_LBUTTON, 0 },
+ { WM_LBUTTONUP, wparam, 0, 0 },
+ { WM_LBUTTONUP, sent|wparam, 0, 0 },
+ { WM_SYSKEYUP, wparam|lparam, VK_MENU, 0xc0000001 },
+ { WM_SYSKEYUP, sent|wparam|lparam, VK_MENU, 0xc0000001 },
+ { 0 }
+};
+static const struct message WmF1Seq[] = {
+ { WM_KEYDOWN, wparam|lparam, VK_F1, 1 },
+ { WM_KEYDOWN, sent|wparam|lparam, VK_F1, 0x00000001 },
+ { 0x4d, wparam|lparam, 0, 0 },
+ { 0x4d, sent|wparam|lparam, 0, 0 },
+ { WM_HELP, sent|defwinproc },
+ { WM_KEYUP, wparam|lparam, VK_F1, 0xc0000001 },
+ { WM_KEYUP, sent|wparam|lparam, VK_F1, 0xc0000001 },
+ { 0 }
+};
+
+static void pump_msg_loop(HWND hwnd, HACCEL hAccel)
+{
+ MSG msg;
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
+ {
+ struct message log_msg;
+
+ trace("accel: %p, %04x, %08x, %08lx\n", msg.hwnd, msg.message, msg.wParam, msg.lParam);
+
+ /* ignore some unwanted messages */
+ if (msg.message == WM_MOUSEMOVE ||
+ msg.message == WM_GETICON ||
+ msg.message == WM_DEVICECHANGE)
+ continue;
+
+ log_msg.message = msg.message;
+ log_msg.flags = wparam|lparam;
+ log_msg.wParam = msg.wParam;
+ log_msg.lParam = msg.lParam;
+ add_message(&log_msg);
+
+ if (!hAccel || !TranslateAccelerator(hwnd, hAccel, &msg))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+}
+
+static void test_accelerators(void)
+{
+ RECT rc;
+ SHORT state;
+ HACCEL hAccel;
+ HWND hwnd = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ BOOL ret;
+
+ assert(hwnd != 0);
+ UpdateWindow(hwnd);
+ flush_events();
+ SetFocus(hwnd);
+ ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus());
+
+ state = GetKeyState(VK_SHIFT);
+ ok(!(state & 0x8000), "wrong Shift state %04x\n", state);
+ state = GetKeyState(VK_CAPITAL);
+ ok(state == 0, "wrong CapsLock state %04x\n", state);
+
+ hAccel = LoadAccelerators(GetModuleHandleA(0), MAKEINTRESOURCE(1));
+ assert(hAccel != 0);
+
+ pump_msg_loop(hwnd, 0);
+ flush_sequence();
+
+ trace("testing VK_N press/release\n");
+ flush_sequence();
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmVkN, "VK_N press/release", FALSE);
+
+ trace("testing Shift+VK_N press/release\n");
+ flush_sequence();
+ keybd_event(VK_SHIFT, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmShiftVkN, "Shift+VK_N press/release", FALSE);
+
+ trace("testing Ctrl+VK_N press/release\n");
+ flush_sequence();
+ keybd_event(VK_CONTROL, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmCtrlVkN, "Ctrl+VK_N press/release", FALSE);
+
+ trace("testing Alt+VK_N press/release\n");
+ flush_sequence();
+ keybd_event(VK_MENU, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmAltVkN, "Alt+VK_N press/release", FALSE);
+
+ trace("testing Ctrl+Alt+VK_N press/release 1\n");
+ flush_sequence();
+ keybd_event(VK_CONTROL, 0, 0, 0);
+ keybd_event(VK_MENU, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmCtrlAltVkN, "Ctrl+Alt+VK_N press/release 1", FALSE);
+
+ ret = DestroyAcceleratorTable(hAccel);
+ ok( ret, "DestroyAcceleratorTable error %ld\n", GetLastError());
+
+ hAccel = LoadAccelerators(GetModuleHandleA(0), MAKEINTRESOURCE(2));
+ assert(hAccel != 0);
+
+ trace("testing VK_N press/release\n");
+ flush_sequence();
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmVkN, "VK_N press/release", FALSE);
+
+ trace("testing Shift+VK_N press/release\n");
+ flush_sequence();
+ keybd_event(VK_SHIFT, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmShiftVkN, "Shift+VK_N press/release", FALSE);
+
+ trace("testing Ctrl+VK_N press/release 2\n");
+ flush_sequence();
+ keybd_event(VK_CONTROL, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmCtrlVkN_2, "Ctrl+VK_N press/release 2", FALSE);
+
+ trace("testing Alt+VK_N press/release 2\n");
+ flush_sequence();
+ keybd_event(VK_MENU, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmAltVkN_2, "Alt+VK_N press/release 2", FALSE);
+
+ trace("testing Ctrl+Alt+VK_N press/release 2\n");
+ flush_sequence();
+ keybd_event(VK_CONTROL, 0, 0, 0);
+ keybd_event(VK_MENU, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmCtrlAltVkN, "Ctrl+Alt+VK_N press/release 2", FALSE);
+
+ trace("testing Ctrl+Shift+VK_N press/release\n");
+ flush_sequence();
+ keybd_event(VK_CONTROL, 0, 0, 0);
+ keybd_event(VK_SHIFT, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmCtrlShiftVkN, "Ctrl+Shift+VK_N press/release", FALSE);
+
+ trace("testing Ctrl+Alt+Shift+VK_N press/release\n");
+ flush_sequence();
+ keybd_event(VK_CONTROL, 0, 0, 0);
+ keybd_event(VK_MENU, 0, 0, 0);
+ keybd_event(VK_SHIFT, 0, 0, 0);
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, hAccel);
+ ok_sequence(WmCtrlAltShiftVkN, "Ctrl+Alt+Shift+VK_N press/release", FALSE);
+
+ ret = DestroyAcceleratorTable(hAccel);
+ ok( ret, "DestroyAcceleratorTable error %ld\n", GetLastError());
+
+ trace("testing Alt press/release\n");
+ flush_sequence();
+ keybd_event(VK_MENU, 0, 0, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ keybd_event(VK_MENU, 0, 0, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, 0);
+ /* this test doesn't pass in Wine for managed windows */
+ ok_sequence(WmAltPressRelease, "Alt press/release", TRUE);
+
+ trace("testing Alt+MouseButton press/release\n");
+ /* first, move mouse pointer inside of the window client area */
+ GetClientRect(hwnd, &rc);
+ MapWindowPoints(hwnd, 0, (LPPOINT)&rc, 2);
+ rc.left += (rc.right - rc.left)/2;
+ rc.top += (rc.bottom - rc.top)/2;
+ SetCursorPos(rc.left, rc.top);
+
+ pump_msg_loop(hwnd, 0);
+ flush_sequence();
+ keybd_event(VK_MENU, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+ keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, 0);
+ ok_sequence(WmAltMouseButton, "Alt+MouseButton press/release", FALSE);
+
+ keybd_event(VK_F1, 0, 0, 0);
+ keybd_event(VK_F1, 0, KEYEVENTF_KEYUP, 0);
+ pump_msg_loop(hwnd, 0);
+ ok_sequence(WmF1Seq, "F1 press/release", TRUE);
+
+ DestroyWindow(hwnd);
+}
+
+/************* window procedures ********************/
+
+static LRESULT MsgCheckProc (BOOL unicode, HWND hwnd, UINT message,
+ WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ static long beginpaint_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ trace("%p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ /* explicitly ignore WM_GETICON message */
+ if (message == WM_GETICON) return 0;
+
+ switch (message)
+ {
+ case WM_ENABLE:
+ {
+ LONG style = GetWindowLongA(hwnd, GWL_STYLE);
+ ok((BOOL)wParam == !(style & WS_DISABLED),
+ "wrong WS_DISABLED state: %d != %d\n", wParam, !(style & WS_DISABLED));
+ break;
+ }
+
+ case WM_CAPTURECHANGED:
+ if (test_DestroyWindow_flag)
+ {
+ DWORD style = GetWindowLongA(hwnd, GWL_STYLE);
+ if (style & WS_CHILD)
+ lParam = GetWindowLongPtrA(hwnd, GWLP_ID);
+ else if (style & WS_POPUP)
+ lParam = WND_POPUP_ID;
+ else
+ lParam = WND_PARENT_ID;
+ }
+ break;
+
+ case WM_NCDESTROY:
+ {
+ HWND capture;
+
+ ok(!GetWindow(hwnd, GW_CHILD), "children should be unlinked at this point\n");
+ capture = GetCapture();
+ if (capture)
+ {
+ ok(capture == hwnd, "capture should NOT be released at this point (capture %p)\n", capture);
+ trace("current capture %p, releasing...\n", capture);
+ ReleaseCapture();
+ }
+ }
+ /* fall through */
+ case WM_DESTROY:
+ if (pGetAncestor)
+ ok(pGetAncestor(hwnd, GA_PARENT) != 0, "parent should NOT be unlinked at this point\n");
+ if (test_DestroyWindow_flag)
+ {
+ DWORD style = GetWindowLongA(hwnd, GWL_STYLE);
+ if (style & WS_CHILD)
+ lParam = GetWindowLongPtrA(hwnd, GWLP_ID);
+ else if (style & WS_POPUP)
+ lParam = WND_POPUP_ID;
+ else
+ lParam = WND_PARENT_ID;
+ }
+ break;
+
+ /* test_accelerators() depends on this */
+ case WM_NCHITTEST:
+ return HTCLIENT;
+
+ /* ignore */
+ case WM_MOUSEMOVE:
+ case WM_SETCURSOR:
+ case WM_DEVICECHANGE:
+ return 0;
+
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+ trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ /* Log only documented flags, win2k uses 0x1000 and 0x2000
+ * in the high word for internal purposes
+ */
+ wParam = winpos->flags & 0xffff;
+ break;
+ }
+ }
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ if (beginpaint_counter) msg.flags |= beginpaint;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+
+ if (message == WM_GETMINMAXINFO && (GetWindowLongA(hwnd, GWL_STYLE) & WS_CHILD))
+ {
+ HWND parent = GetParent(hwnd);
+ RECT rc;
+ MINMAXINFO *minmax = (MINMAXINFO *)lParam;
+
+ GetClientRect(parent, &rc);
+ trace("parent %p client size = (%ld x %ld)\n", parent, rc.right, rc.bottom);
+
+ trace("ptReserved = (%ld,%ld)\n"
+ "ptMaxSize = (%ld,%ld)\n"
+ "ptMaxPosition = (%ld,%ld)\n"
+ "ptMinTrackSize = (%ld,%ld)\n"
+ "ptMaxTrackSize = (%ld,%ld)\n",
+ minmax->ptReserved.x, minmax->ptReserved.y,
+ minmax->ptMaxSize.x, minmax->ptMaxSize.y,
+ minmax->ptMaxPosition.x, minmax->ptMaxPosition.y,
+ minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y,
+ minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y);
+
+ ok(minmax->ptMaxSize.x == rc.right, "default width of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.x, rc.right);
+ ok(minmax->ptMaxSize.y == rc.bottom, "default height of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.y, rc.bottom);
+ }
+
+ if (message == WM_PAINT)
+ {
+ PAINTSTRUCT ps;
+ beginpaint_counter++;
+ BeginPaint( hwnd, &ps );
+ beginpaint_counter--;
+ EndPaint( hwnd, &ps );
+ return 0;
+ }
+
+ defwndproc_counter++;
+ ret = unicode ? DefWindowProcW(hwnd, message, wParam, lParam)
+ : DefWindowProcA(hwnd, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static LRESULT WINAPI MsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return MsgCheckProc (FALSE, hwnd, message, wParam, lParam);
+}
+
+static LRESULT WINAPI MsgCheckProcW(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ return MsgCheckProc (TRUE, hwnd, message, wParam, lParam);
+}
+
+static LRESULT WINAPI PopupMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ trace("popup: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ /* explicitly ignore WM_GETICON message */
+ if (message == WM_GETICON) return 0;
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+
+ if (message == WM_CREATE)
+ {
+ DWORD style = GetWindowLongA(hwnd, GWL_STYLE) | WS_VISIBLE;
+ SetWindowLongA(hwnd, GWL_STYLE, style);
+ }
+
+ defwndproc_counter++;
+ ret = DefWindowProcA(hwnd, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ static long beginpaint_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ trace("parent: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ /* explicitly ignore WM_GETICON message */
+ if (message == WM_GETICON) return 0;
+
+ if (log_all_parent_messages ||
+ message == WM_PARENTNOTIFY || message == WM_CANCELMODE ||
+ message == WM_SETFOCUS || message == WM_KILLFOCUS ||
+ message == WM_ENABLE || message == WM_ENTERIDLE ||
+ message == WM_IME_SETCONTEXT)
+ {
+ switch (message)
+ {
+ case WM_ERASEBKGND:
+ {
+ RECT rc;
+ INT ret = GetClipBox((HDC)wParam, &rc);
+
+ trace("WM_ERASEBKGND: GetClipBox()=%d, (%ld,%ld-%ld,%ld)\n",
+ ret, rc.left, rc.top, rc.right, rc.bottom);
+ break;
+ }
+
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+ trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ /* Log only documented flags, win2k uses 0x1000 and 0x2000
+ * in the high word for internal purposes
+ */
+ wParam = winpos->flags & 0xffff;
+ break;
+ }
+ }
+
+ msg.message = message;
+ msg.flags = sent|parent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ if (beginpaint_counter) msg.flags |= beginpaint;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+ }
+
+ if (message == WM_PAINT)
+ {
+ PAINTSTRUCT ps;
+ beginpaint_counter++;
+ BeginPaint( hwnd, &ps );
+ beginpaint_counter--;
+ EndPaint( hwnd, &ps );
+ return 0;
+ }
+
+ defwndproc_counter++;
+ ret = DefWindowProcA(hwnd, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static LRESULT WINAPI TestDlgProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ trace("dialog: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ /* explicitly ignore WM_GETICON message */
+ if (message == WM_GETICON) return 0;
+
+ DefDlgProcA(hwnd, DM_SETDEFID, 1, 0);
+ ret = DefDlgProcA(hwnd, DM_GETDEFID, 0, 0);
+ if (after_end_dialog)
+ ok( ret == 0, "DM_GETDEFID should return 0 after EndDialog, got %lx\n", ret );
+ else
+ ok(HIWORD(ret) == DC_HASDEFID, "DM_GETDEFID should return DC_HASDEFID, got %lx\n", ret);
+
+ switch (message)
+ {
+ case WM_WINDOWPOSCHANGING:
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lParam;
+
+ trace("%s\n", (message == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ /* Log only documented flags, win2k uses 0x1000 and 0x2000
+ * in the high word for internal purposes
+ */
+ wParam = winpos->flags & 0xffff;
+ break;
+ }
+ }
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+
+ defwndproc_counter++;
+ ret = DefDlgProcA(hwnd, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static BOOL RegisterWindowClasses(void)
+{
+ WNDCLASSA cls;
+
+ cls.style = 0;
+ cls.lpfnWndProc = MsgCheckProcA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "TestWindowClass";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ cls.lpfnWndProc = PopupMsgCheckProcA;
+ cls.lpszClassName = "TestPopupClass";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ cls.lpfnWndProc = ParentMsgCheckProcA;
+ cls.lpszClassName = "TestParentClass";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ cls.lpfnWndProc = DefWindowProcA;
+ cls.lpszClassName = "SimpleWindowClass";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ cls.style = CS_NOCLOSE;
+ cls.lpszClassName = "NoCloseWindowClass";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ ok(GetClassInfoA(0, "#32770", &cls), "GetClassInfo failed\n");
+ cls.style = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hbrBackground = 0;
+ cls.lpfnWndProc = TestDlgProcA;
+ cls.lpszClassName = "TestDialogClass";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ return TRUE;
+}
+
+static HHOOK hCBT_hook;
+static DWORD cbt_hook_thread_id;
+
+static LRESULT CALLBACK cbt_hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ static const char *CBT_code_name[10] = {
+ "HCBT_MOVESIZE",
+ "HCBT_MINMAX",
+ "HCBT_QS",
+ "HCBT_CREATEWND",
+ "HCBT_DESTROYWND",
+ "HCBT_ACTIVATE",
+ "HCBT_CLICKSKIPPED",
+ "HCBT_KEYSKIPPED",
+ "HCBT_SYSCOMMAND",
+ "HCBT_SETFOCUS" };
+ const char *code_name = (nCode >= 0 && nCode <= HCBT_SETFOCUS) ? CBT_code_name[nCode] : "Unknown";
+ HWND hwnd;
+ char buf[256];
+
+ trace("CBT: %d (%s), %08x, %08lx\n", nCode, code_name, wParam, lParam);
+
+ ok(cbt_hook_thread_id == GetCurrentThreadId(), "we didn't ask for events from other threads\n");
+
+ if (nCode == HCBT_SYSCOMMAND)
+ {
+ struct message msg;
+
+ msg.message = nCode;
+ msg.flags = hook|wparam|lparam;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+
+ return CallNextHookEx(hCBT_hook, nCode, wParam, lParam);
+ }
+
+ if (nCode == HCBT_DESTROYWND)
+ {
+ if (test_DestroyWindow_flag)
+ {
+ DWORD style = GetWindowLongA((HWND)wParam, GWL_STYLE);
+ if (style & WS_CHILD)
+ lParam = GetWindowLongPtrA((HWND)wParam, GWLP_ID);
+ else if (style & WS_POPUP)
+ lParam = WND_POPUP_ID;
+ else
+ lParam = WND_PARENT_ID;
+ }
+ }
+
+ /* Log also SetFocus(0) calls */
+ hwnd = wParam ? (HWND)wParam : (HWND)lParam;
+
+ if (GetClassNameA(hwnd, buf, sizeof(buf)))
+ {
+ if (!lstrcmpiA(buf, "TestWindowClass") ||
+ !lstrcmpiA(buf, "TestParentClass") ||
+ !lstrcmpiA(buf, "TestPopupClass") ||
+ !lstrcmpiA(buf, "SimpleWindowClass") ||
+ !lstrcmpiA(buf, "TestDialogClass") ||
+ !lstrcmpiA(buf, "MDI_frame_class") ||
+ !lstrcmpiA(buf, "MDI_client_class") ||
+ !lstrcmpiA(buf, "MDI_child_class") ||
+ !lstrcmpiA(buf, "my_button_class") ||
+ !lstrcmpiA(buf, "my_edit_class") ||
+ !lstrcmpiA(buf, "static") ||
+ !lstrcmpiA(buf, "#32770"))
+ {
+ struct message msg;
+
+ msg.message = nCode;
+ msg.flags = hook|wparam|lparam;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+ }
+ }
+ return CallNextHookEx(hCBT_hook, nCode, wParam, lParam);
+}
+
+static void CALLBACK win_event_proc(HWINEVENTHOOK hevent,
+ DWORD event,
+ HWND hwnd,
+ LONG object_id,
+ LONG child_id,
+ DWORD thread_id,
+ DWORD event_time)
+{
+ char buf[256];
+
+ trace("WEH:%p,event %08lx,hwnd %p,obj %08lx,id %08lx,thread %08lx,time %08lx\n",
+ hevent, event, hwnd, object_id, child_id, thread_id, event_time);
+
+ ok(thread_id == GetCurrentThreadId(), "we didn't ask for events from other threads\n");
+
+ /* ignore mouse cursor events */
+ if (object_id == OBJID_CURSOR) return;
+
+ if (!hwnd || GetClassNameA(hwnd, buf, sizeof(buf)))
+ {
+ if (!hwnd ||
+ !lstrcmpiA(buf, "TestWindowClass") ||
+ !lstrcmpiA(buf, "TestParentClass") ||
+ !lstrcmpiA(buf, "TestPopupClass") ||
+ !lstrcmpiA(buf, "SimpleWindowClass") ||
+ !lstrcmpiA(buf, "TestDialogClass") ||
+ !lstrcmpiA(buf, "MDI_frame_class") ||
+ !lstrcmpiA(buf, "MDI_client_class") ||
+ !lstrcmpiA(buf, "MDI_child_class") ||
+ !lstrcmpiA(buf, "my_button_class") ||
+ !lstrcmpiA(buf, "my_edit_class") ||
+ !lstrcmpiA(buf, "static") ||
+ !lstrcmpiA(buf, "#32770"))
+ {
+ struct message msg;
+
+ msg.message = event;
+ msg.flags = winevent_hook|wparam|lparam;
+ msg.wParam = object_id;
+ msg.lParam = child_id;
+ add_message(&msg);
+ }
+ }
+}
+
+static const WCHAR wszUnicode[] = {'U','n','i','c','o','d','e',0};
+static const WCHAR wszAnsi[] = {'U',0};
+
+static LRESULT CALLBACK MsgConversionProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case CB_FINDSTRINGEXACT:
+ trace("String: %p\n", (LPCWSTR)lParam);
+ if (!lstrcmpW((LPCWSTR)lParam, wszUnicode))
+ return 1;
+ if (!lstrcmpW((LPCWSTR)lParam, wszAnsi))
+ return 0;
+ return -1;
+ }
+ return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
+
+static const struct message WmGetTextLengthAfromW[] = {
+ { WM_GETTEXTLENGTH, sent },
+ { WM_GETTEXT, sent },
+ { 0 }
+};
+
+static const WCHAR testWindowClassW[] =
+{ 'T','e','s','t','W','i','n','d','o','w','C','l','a','s','s','W',0 };
+
+static const WCHAR dummy_window_text[] = {'d','u','m','m','y',' ','t','e','x','t',0};
+
+/* dummy window proc for WM_GETTEXTLENGTH test */
+static LRESULT CALLBACK get_text_len_proc( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
+{
+ switch(msg)
+ {
+ case WM_GETTEXTLENGTH:
+ return lstrlenW(dummy_window_text) + 37; /* some random length */
+ case WM_GETTEXT:
+ lstrcpynW( (LPWSTR)lp, dummy_window_text, wp );
+ return lstrlenW( (LPWSTR)lp );
+ default:
+ return DefWindowProcW( hwnd, msg, wp, lp );
+ }
+}
+
+static void test_message_conversion(void)
+{
+ static const WCHAR wszMsgConversionClass[] =
+ {'M','s','g','C','o','n','v','e','r','s','i','o','n','C','l','a','s','s',0};
+ WNDCLASSW cls;
+ LRESULT lRes;
+ HWND hwnd;
+ WNDPROC wndproc, newproc;
+ BOOL ret;
+
+ cls.style = 0;
+ cls.lpfnWndProc = MsgConversionProcW;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleW(NULL);
+ cls.hIcon = NULL;
+ cls.hCursor = LoadCursorW(NULL, (LPWSTR)IDC_ARROW);
+ cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = wszMsgConversionClass;
+ /* this call will fail on Win9x, but that doesn't matter as this test is
+ * meaningless on those platforms */
+ if(!RegisterClassW(&cls)) return;
+
+ cls.style = 0;
+ cls.lpfnWndProc = MsgCheckProcW;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleW(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = testWindowClassW;
+ if(!RegisterClassW(&cls)) return;
+
+ hwnd = CreateWindowExW(0, wszMsgConversionClass, NULL, WS_OVERLAPPED,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok(hwnd != NULL, "Window creation failed\n");
+
+ /* {W, A} -> A */
+
+ wndproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_WNDPROC);
+ lRes = CallWindowProcA(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0, "String should have been converted\n");
+ lRes = CallWindowProcW(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 1, "String shouldn't have been converted\n");
+
+ /* {W, A} -> W */
+
+ wndproc = (WNDPROC)GetWindowLongPtrW(hwnd, GWLP_WNDPROC);
+ lRes = CallWindowProcA(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 1, "String shouldn't have been converted\n");
+ lRes = CallWindowProcW(wndproc, hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 1, "String shouldn't have been converted\n");
+
+ /* Synchronous messages */
+
+ lRes = SendMessageA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0, "String should have been converted\n");
+ lRes = SendMessageW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 1, "String shouldn't have been converted\n");
+
+ /* Asynchronous messages */
+
+ SetLastError(0);
+ lRes = PostMessageA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "PostMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+ SetLastError(0);
+ lRes = PostMessageW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "PostMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+ SetLastError(0);
+ lRes = PostThreadMessageA(GetCurrentThreadId(), CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "PosThreadtMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+ SetLastError(0);
+ lRes = PostThreadMessageW(GetCurrentThreadId(), CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "PosThreadtMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+ SetLastError(0);
+ lRes = SendNotifyMessageA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "SendNotifyMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+ SetLastError(0);
+ lRes = SendNotifyMessageW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "SendNotifyMessage on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+ SetLastError(0);
+ lRes = SendMessageCallbackA(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode, NULL, 0);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "SendMessageCallback on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+ SetLastError(0);
+ lRes = SendMessageCallbackW(hwnd, CB_FINDSTRINGEXACT, 0, (LPARAM)wszUnicode, NULL, 0);
+ ok(lRes == 0 && (GetLastError() == ERROR_MESSAGE_SYNC_ONLY || GetLastError() == ERROR_INVALID_PARAMETER),
+ "SendMessageCallback on sync only message returned %ld, last error %ld\n", lRes, GetLastError());
+
+ /* Check WM_GETTEXTLENGTH A->W behaviour, whether WM_GETTEXT is also sent or not */
+
+ hwnd = CreateWindowW (testWindowClassW, wszUnicode,
+ WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ assert(hwnd);
+ flush_sequence();
+ lRes = SendMessageA (hwnd, WM_GETTEXTLENGTH, 0, 0);
+ ok_sequence(WmGetTextLengthAfromW, "ANSI WM_GETTEXTLENGTH to Unicode window", FALSE);
+ ok( lRes == WideCharToMultiByte( CP_ACP, 0, wszUnicode, lstrlenW(wszUnicode), NULL, 0, NULL, NULL ),
+ "got bad length %ld\n", lRes );
+
+ flush_sequence();
+ lRes = CallWindowProcA( (WNDPROC)GetWindowLongPtrA( hwnd, GWLP_WNDPROC ),
+ hwnd, WM_GETTEXTLENGTH, 0, 0);
+ ok_sequence(WmGetTextLengthAfromW, "ANSI WM_GETTEXTLENGTH to Unicode window", FALSE);
+ ok( lRes == WideCharToMultiByte( CP_ACP, 0, wszUnicode, lstrlenW(wszUnicode), NULL, 0, NULL, NULL ),
+ "got bad length %ld\n", lRes );
+
+ wndproc = (WNDPROC)SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)get_text_len_proc );
+ newproc = (WNDPROC)GetWindowLongPtrA( hwnd, GWLP_WNDPROC );
+ lRes = CallWindowProcA( newproc, hwnd, WM_GETTEXTLENGTH, 0, 0 );
+ ok( lRes == WideCharToMultiByte( CP_ACP, 0, dummy_window_text, lstrlenW(dummy_window_text),
+ NULL, 0, NULL, NULL ),
+ "got bad length %ld\n", lRes );
+
+ SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)wndproc ); /* restore old wnd proc */
+ lRes = CallWindowProcA( newproc, hwnd, WM_GETTEXTLENGTH, 0, 0 );
+ ok( lRes == WideCharToMultiByte( CP_ACP, 0, dummy_window_text, lstrlenW(dummy_window_text),
+ NULL, 0, NULL, NULL ),
+ "got bad length %ld\n", lRes );
+
+ ret = DestroyWindow(hwnd);
+ ok( ret, "DestroyWindow() error %ld\n", GetLastError());
+}
+
+struct timer_info
+{
+ HWND hWnd;
+ HANDLE handles[2];
+ DWORD id;
+};
+
+static VOID CALLBACK tfunc(HWND hwnd, UINT uMsg, UINT id, DWORD dwTime)
+{
+}
+
+#define TIMER_ID 0x19
+
+static DWORD WINAPI timer_thread_proc(LPVOID x)
+{
+ struct timer_info *info = x;
+ DWORD r;
+
+ r = KillTimer(info->hWnd, 0x19);
+ ok(r,"KillTimer failed in thread\n");
+ r = SetTimer(info->hWnd,TIMER_ID,10000,tfunc);
+ ok(r,"SetTimer failed in thread\n");
+ ok(r==TIMER_ID,"SetTimer id different\n");
+ r = SetEvent(info->handles[0]);
+ ok(r,"SetEvent failed in thread\n");
+ return 0;
+}
+
+static void test_timers(void)
+{
+ struct timer_info info;
+ DWORD id;
+
+ info.hWnd = CreateWindow ("TestWindowClass", NULL,
+ WS_OVERLAPPEDWINDOW ,
+ CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, 0,
+ NULL, NULL, 0);
+
+ info.id = SetTimer(info.hWnd,TIMER_ID,10000,tfunc);
+ ok(info.id, "SetTimer failed\n");
+ ok(info.id==TIMER_ID, "SetTimer timer ID different\n");
+ info.handles[0] = CreateEvent(NULL,0,0,NULL);
+ info.handles[1] = CreateThread(NULL,0,timer_thread_proc,&info,0,&id);
+
+ WaitForMultipleObjects(2, info.handles, FALSE, INFINITE);
+
+ WaitForSingleObject(info.handles[1], INFINITE);
+
+ CloseHandle(info.handles[0]);
+ CloseHandle(info.handles[1]);
+
+ ok( KillTimer(info.hWnd, TIMER_ID), "KillTimer failed\n");
+
+ ok(DestroyWindow(info.hWnd), "failed to destroy window\n");
+}
+
+/* Various win events with arbitrary parameters */
+static const struct message WmWinEventsSeq[] = {
+ { EVENT_SYSTEM_SOUND, winevent_hook|wparam|lparam, OBJID_WINDOW, 0 },
+ { EVENT_SYSTEM_ALERT, winevent_hook|wparam|lparam, OBJID_SYSMENU, 1 },
+ { EVENT_SYSTEM_FOREGROUND, winevent_hook|wparam|lparam, OBJID_TITLEBAR, 2 },
+ { EVENT_SYSTEM_MENUSTART, winevent_hook|wparam|lparam, OBJID_MENU, 3 },
+ { EVENT_SYSTEM_MENUEND, winevent_hook|wparam|lparam, OBJID_CLIENT, 4 },
+ { EVENT_SYSTEM_MENUPOPUPSTART, winevent_hook|wparam|lparam, OBJID_VSCROLL, 5 },
+ { EVENT_SYSTEM_MENUPOPUPEND, winevent_hook|wparam|lparam, OBJID_HSCROLL, 6 },
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, OBJID_SIZEGRIP, 7 },
+ { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, OBJID_CARET, 8 },
+ /* our win event hook ignores OBJID_CURSOR events */
+ /*{ EVENT_SYSTEM_MOVESIZESTART, winevent_hook|wparam|lparam, OBJID_CURSOR, 9 },*/
+ { EVENT_SYSTEM_MOVESIZEEND, winevent_hook|wparam|lparam, OBJID_ALERT, 10 },
+ { EVENT_SYSTEM_CONTEXTHELPSTART, winevent_hook|wparam|lparam, OBJID_SOUND, 11 },
+ { EVENT_SYSTEM_CONTEXTHELPEND, winevent_hook|wparam|lparam, OBJID_QUERYCLASSNAMEIDX, 12 },
+ { EVENT_SYSTEM_DRAGDROPSTART, winevent_hook|wparam|lparam, OBJID_NATIVEOM, 13 },
+ { EVENT_SYSTEM_DRAGDROPEND, winevent_hook|wparam|lparam, OBJID_WINDOW, 0 },
+ { EVENT_SYSTEM_DIALOGSTART, winevent_hook|wparam|lparam, OBJID_SYSMENU, 1 },
+ { EVENT_SYSTEM_DIALOGEND, winevent_hook|wparam|lparam, OBJID_TITLEBAR, 2 },
+ { EVENT_SYSTEM_SCROLLINGSTART, winevent_hook|wparam|lparam, OBJID_MENU, 3 },
+ { EVENT_SYSTEM_SCROLLINGEND, winevent_hook|wparam|lparam, OBJID_CLIENT, 4 },
+ { EVENT_SYSTEM_SWITCHSTART, winevent_hook|wparam|lparam, OBJID_VSCROLL, 5 },
+ { EVENT_SYSTEM_SWITCHEND, winevent_hook|wparam|lparam, OBJID_HSCROLL, 6 },
+ { EVENT_SYSTEM_MINIMIZESTART, winevent_hook|wparam|lparam, OBJID_SIZEGRIP, 7 },
+ { EVENT_SYSTEM_MINIMIZEEND, winevent_hook|wparam|lparam, OBJID_CARET, 8 },
+ { 0 }
+};
+static const struct message WmWinEventCaretSeq[] = {
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1 */
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1 */
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 2 */
+ { EVENT_OBJECT_NAMECHANGE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1 */
+ { 0 }
+};
+static const struct message WmWinEventCaretSeq_2[] = {
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1/2 */
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1/2 */
+ { EVENT_OBJECT_NAMECHANGE, winevent_hook|wparam|lparam, OBJID_CARET, 0 }, /* hook 1/2 */
+ { 0 }
+};
+static const struct message WmWinEventAlertSeq[] = {
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, OBJID_ALERT, 0 },
+ { 0 }
+};
+static const struct message WmWinEventAlertSeq_2[] = {
+ /* create window in the thread proc */
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_WINDOW, 2 },
+ /* our test event */
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, OBJID_ALERT, 2 },
+ { 0 }
+};
+static const struct message WmGlobalHookSeq_1[] = {
+ /* create window in the thread proc */
+ { HCBT_CREATEWND, hook|lparam, 0, 2 },
+ /* our test events */
+ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_PREVWINDOW, 2 },
+ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_NEXTWINDOW, 2 },
+ { 0 }
+};
+static const struct message WmGlobalHookSeq_2[] = {
+ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_NEXTWINDOW, 0 }, /* old local hook */
+ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_NEXTWINDOW, 2 }, /* new global hook */
+ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_PREVWINDOW, 0 }, /* old local hook */
+ { HCBT_SYSCOMMAND, hook|wparam|lparam, SC_PREVWINDOW, 2 }, /* new global hook */
+ { 0 }
+};
+
+static const struct message WmMouseLLHookSeq[] = {
+ { WM_MOUSEMOVE, hook },
+ { WM_LBUTTONDOWN, hook },
+ { WM_LBUTTONUP, hook },
+ { 0 }
+};
+
+static void CALLBACK win_event_global_hook_proc(HWINEVENTHOOK hevent,
+ DWORD event,
+ HWND hwnd,
+ LONG object_id,
+ LONG child_id,
+ DWORD thread_id,
+ DWORD event_time)
+{
+ char buf[256];
+
+ trace("WEH_2:%p,event %08lx,hwnd %p,obj %08lx,id %08lx,thread %08lx,time %08lx\n",
+ hevent, event, hwnd, object_id, child_id, thread_id, event_time);
+
+ if (GetClassNameA(hwnd, buf, sizeof(buf)))
+ {
+ if (!lstrcmpiA(buf, "TestWindowClass") ||
+ !lstrcmpiA(buf, "static"))
+ {
+ struct message msg;
+
+ msg.message = event;
+ msg.flags = winevent_hook|wparam|lparam;
+ msg.wParam = object_id;
+ msg.lParam = (thread_id == GetCurrentThreadId()) ? child_id : (child_id + 2);
+ add_message(&msg);
+ }
+ }
+}
+
+static HHOOK hCBT_global_hook;
+static DWORD cbt_global_hook_thread_id;
+
+static LRESULT CALLBACK cbt_global_hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ HWND hwnd;
+ char buf[256];
+
+ trace("CBT_2: %d, %08x, %08lx\n", nCode, wParam, lParam);
+
+ if (nCode == HCBT_SYSCOMMAND)
+ {
+ struct message msg;
+
+ msg.message = nCode;
+ msg.flags = hook|wparam|lparam;
+ msg.wParam = wParam;
+ msg.lParam = (cbt_global_hook_thread_id == GetCurrentThreadId()) ? 1 : 2;
+ add_message(&msg);
+
+ return CallNextHookEx(hCBT_global_hook, nCode, wParam, lParam);
+ }
+ /* WH_MOUSE_LL hook */
+ if (nCode == HC_ACTION)
+ {
+ struct message msg;
+ MSLLHOOKSTRUCT *mhll = (MSLLHOOKSTRUCT *)lParam;
+
+ /* we can't test for real mouse events */
+ if (mhll->flags & LLMHF_INJECTED)
+ {
+ msg.message = wParam;
+ msg.flags = hook;
+ add_message(&msg);
+ }
+ return CallNextHookEx(hCBT_global_hook, nCode, wParam, lParam);
+ }
+
+ /* Log also SetFocus(0) calls */
+ hwnd = wParam ? (HWND)wParam : (HWND)lParam;
+
+ if (GetClassNameA(hwnd, buf, sizeof(buf)))
+ {
+ if (!lstrcmpiA(buf, "TestWindowClass") ||
+ !lstrcmpiA(buf, "static"))
+ {
+ struct message msg;
+
+ msg.message = nCode;
+ msg.flags = hook|wparam|lparam;
+ msg.wParam = wParam;
+ msg.lParam = (cbt_global_hook_thread_id == GetCurrentThreadId()) ? 1 : 2;
+ add_message(&msg);
+ }
+ }
+ return CallNextHookEx(hCBT_global_hook, nCode, wParam, lParam);
+}
+
+static DWORD WINAPI win_event_global_thread_proc(void *param)
+{
+ HWND hwnd;
+ MSG msg;
+ HANDLE hevent = *(HANDLE *)param;
+ HMODULE user32 = GetModuleHandleA("user32.dll");
+ FARPROC pNotifyWinEvent = GetProcAddress(user32, "NotifyWinEvent");
+
+ assert(pNotifyWinEvent);
+
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0,0,0,0,0,0,0, NULL);
+ assert(hwnd);
+ trace("created thread window %p\n", hwnd);
+
+ *(HWND *)param = hwnd;
+
+ flush_sequence();
+ /* this event should be received only by our new hook proc,
+ * an old one does not expect an event from another thread.
+ */
+ pNotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, hwnd, OBJID_ALERT, 0);
+ SetEvent(hevent);
+
+ while (GetMessage(&msg, 0, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return 0;
+}
+
+static DWORD WINAPI cbt_global_hook_thread_proc(void *param)
+{
+ HWND hwnd;
+ MSG msg;
+ HANDLE hevent = *(HANDLE *)param;
+
+ flush_sequence();
+ /* these events should be received only by our new hook proc,
+ * an old one does not expect an event from another thread.
+ */
+
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0,0,0,0,0,0,0, NULL);
+ assert(hwnd);
+ trace("created thread window %p\n", hwnd);
+
+ *(HWND *)param = hwnd;
+
+ /* Windows doesn't like when a thread plays games with the focus,
+ that leads to all kinds of misbehaviours and failures to activate
+ a window. So, better keep next lines commented out.
+ SetFocus(0);
+ SetFocus(hwnd);*/
+
+ DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_PREVWINDOW, 0);
+ DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_NEXTWINDOW, 0);
+
+ SetEvent(hevent);
+
+ while (GetMessage(&msg, 0, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return 0;
+}
+
+static DWORD WINAPI mouse_ll_global_thread_proc(void *param)
+{
+ HWND hwnd;
+ MSG msg;
+ HANDLE hevent = *(HANDLE *)param;
+
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0,0,0,0,0,0,0, NULL);
+ assert(hwnd);
+ trace("created thread window %p\n", hwnd);
+
+ *(HWND *)param = hwnd;
+
+ flush_sequence();
+
+ mouse_event(MOUSEEVENTF_MOVE, 100, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+
+ SetEvent(hevent);
+ while (GetMessage(&msg, 0, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return 0;
+}
+
+static void test_winevents(void)
+{
+ BOOL ret;
+ MSG msg;
+ HWND hwnd, hwnd2;
+ UINT i;
+ HANDLE hthread, hevent;
+ DWORD tid;
+ HWINEVENTHOOK hhook;
+ const struct message *events = WmWinEventsSeq;
+ HMODULE user32 = GetModuleHandleA("user32.dll");
+ FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook");
+ FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent");
+ FARPROC pNotifyWinEvent = GetProcAddress(user32, "NotifyWinEvent");
+
+ hwnd = CreateWindowExA(0, "TestWindowClass", NULL,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, 0,
+ NULL, NULL, 0);
+ assert(hwnd);
+
+ /****** start of global hook test *************/
+ hCBT_global_hook = SetWindowsHookExA(WH_CBT, cbt_global_hook_proc, GetModuleHandleA(0), 0);
+ assert(hCBT_global_hook);
+
+ hevent = CreateEventA(NULL, 0, 0, NULL);
+ assert(hevent);
+ hwnd2 = (HWND)hevent;
+
+ hthread = CreateThread(NULL, 0, cbt_global_hook_thread_proc, &hwnd2, 0, &tid);
+ ok(hthread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+
+ ok(WaitForSingleObject(hevent, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+
+ ok_sequence(WmGlobalHookSeq_1, "global hook 1", FALSE);
+
+ flush_sequence();
+ /* this one should be received only by old hook proc */
+ DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_NEXTWINDOW, 0);
+ /* this one should be received only by old hook proc */
+ DefWindowProcA(hwnd, WM_SYSCOMMAND, SC_PREVWINDOW, 0);
+
+ ok_sequence(WmGlobalHookSeq_2, "global hook 2", FALSE);
+
+ ret = UnhookWindowsHookEx(hCBT_global_hook);
+ ok( ret, "UnhookWindowsHookEx error %ld\n", GetLastError());
+
+ PostThreadMessageA(tid, WM_QUIT, 0, 0);
+ ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+ CloseHandle(hthread);
+ CloseHandle(hevent);
+ ok(!IsWindow(hwnd2), "window should be destroyed on thread exit\n");
+ /****** end of global hook test *************/
+
+ if (!pSetWinEventHook || !pNotifyWinEvent || !pUnhookWinEvent)
+ {
+ ok(DestroyWindow(hwnd), "failed to destroy window\n");
+ return;
+ }
+
+ flush_sequence();
+
+#if 0 /* this test doesn't pass under Win9x */
+ /* win2k ignores events with hwnd == 0 */
+ SetLastError(0xdeadbeef);
+ pNotifyWinEvent(events[0].message, 0, events[0].wParam, events[0].lParam);
+ ok(GetLastError() == ERROR_INVALID_WINDOW_HANDLE || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "unexpected error %ld\n", GetLastError());
+ ok_sequence(WmEmptySeq, "empty notify winevents", FALSE);
+#endif
+
+ for (i = 0; i < sizeof(WmWinEventsSeq)/sizeof(WmWinEventsSeq[0]); i++)
+ pNotifyWinEvent(events[i].message, hwnd, events[i].wParam, events[i].lParam);
+
+ ok_sequence(WmWinEventsSeq, "notify winevents", FALSE);
+
+ /****** start of event filtering test *************/
+ hhook = (HWINEVENTHOOK)pSetWinEventHook(
+ EVENT_OBJECT_SHOW, /* 0x8002 */
+ EVENT_OBJECT_LOCATIONCHANGE, /* 0x800B */
+ GetModuleHandleA(0), win_event_global_hook_proc,
+ GetCurrentProcessId(), 0,
+ WINEVENT_INCONTEXT);
+ ok(hhook != 0, "SetWinEventHook error %ld\n", GetLastError());
+
+ hevent = CreateEventA(NULL, 0, 0, NULL);
+ assert(hevent);
+ hwnd2 = (HWND)hevent;
+
+ hthread = CreateThread(NULL, 0, win_event_global_thread_proc, &hwnd2, 0, &tid);
+ ok(hthread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+
+ ok(WaitForSingleObject(hevent, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+
+ ok_sequence(WmWinEventAlertSeq, "alert winevent", FALSE);
+
+ flush_sequence();
+ /* this one should be received only by old hook proc */
+ pNotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_CARET, 0); /* 0x8000 */
+ pNotifyWinEvent(EVENT_OBJECT_SHOW, hwnd, OBJID_CARET, 0); /* 0x8002 */
+ /* this one should be received only by old hook proc */
+ pNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_CARET, 0); /* 0x800C */
+
+ ok_sequence(WmWinEventCaretSeq, "caret winevent", FALSE);
+
+ ret = pUnhookWinEvent(hhook);
+ ok( ret, "UnhookWinEvent error %ld\n", GetLastError());
+
+ PostThreadMessageA(tid, WM_QUIT, 0, 0);
+ ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+ CloseHandle(hthread);
+ CloseHandle(hevent);
+ ok(!IsWindow(hwnd2), "window should be destroyed on thread exit\n");
+ /****** end of event filtering test *************/
+
+ /****** start of out of context event test *************/
+ hhook = (HWINEVENTHOOK)pSetWinEventHook(
+ EVENT_MIN, EVENT_MAX,
+ 0, win_event_global_hook_proc,
+ GetCurrentProcessId(), 0,
+ WINEVENT_OUTOFCONTEXT);
+ ok(hhook != 0, "SetWinEventHook error %ld\n", GetLastError());
+
+ hevent = CreateEventA(NULL, 0, 0, NULL);
+ assert(hevent);
+ hwnd2 = (HWND)hevent;
+
+ flush_sequence();
+
+ hthread = CreateThread(NULL, 0, win_event_global_thread_proc, &hwnd2, 0, &tid);
+ ok(hthread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+
+ ok(WaitForSingleObject(hevent, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+
+ ok_sequence(WmEmptySeq, "empty notify winevents", FALSE);
+ /* process pending winevent messages */
+ ok(!PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE), "msg queue should be empty\n");
+ ok_sequence(WmWinEventAlertSeq_2, "alert winevent for out of context proc", FALSE);
+
+ flush_sequence();
+ /* this one should be received only by old hook proc */
+ pNotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_CARET, 0); /* 0x8000 */
+ pNotifyWinEvent(EVENT_OBJECT_SHOW, hwnd, OBJID_CARET, 0); /* 0x8002 */
+ /* this one should be received only by old hook proc */
+ pNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_CARET, 0); /* 0x800C */
+
+ ok_sequence(WmWinEventCaretSeq_2, "caret winevent for incontext proc", FALSE);
+ /* process pending winevent messages */
+ ok(!PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE), "msg queue should be empty\n");
+ ok_sequence(WmWinEventCaretSeq_2, "caret winevent for out of context proc", FALSE);
+
+ ret = pUnhookWinEvent(hhook);
+ ok( ret, "UnhookWinEvent error %ld\n", GetLastError());
+
+ PostThreadMessageA(tid, WM_QUIT, 0, 0);
+ ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+ CloseHandle(hthread);
+ CloseHandle(hevent);
+ ok(!IsWindow(hwnd2), "window should be destroyed on thread exit\n");
+ /****** end of out of context event test *************/
+
+ /****** start of MOUSE_LL hook test *************/
+ hCBT_global_hook = SetWindowsHookExA(WH_MOUSE_LL, cbt_global_hook_proc, GetModuleHandleA(0), 0);
+ assert(hCBT_global_hook);
+
+ hevent = CreateEventA(NULL, 0, 0, NULL);
+ assert(hevent);
+ hwnd2 = (HWND)hevent;
+
+ hthread = CreateThread(NULL, 0, mouse_ll_global_thread_proc, &hwnd2, 0, &tid);
+ ok(hthread != NULL, "CreateThread failed, error %ld\n", GetLastError());
+
+ while (WaitForSingleObject(hevent, 100) == WAIT_TIMEOUT)
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+
+ ok_sequence(WmMouseLLHookSeq, "MOUSE_LL hook other thread", FALSE);
+ flush_sequence();
+
+ mouse_event(MOUSEEVENTF_MOVE, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+
+ ok_sequence(WmMouseLLHookSeq, "MOUSE_LL hook same thread", FALSE);
+
+ ret = UnhookWindowsHookEx(hCBT_global_hook);
+ ok( ret, "UnhookWindowsHookEx error %ld\n", GetLastError());
+
+ PostThreadMessageA(tid, WM_QUIT, 0, 0);
+ ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+ CloseHandle(hthread);
+ CloseHandle(hevent);
+ ok(!IsWindow(hwnd2), "window should be destroyed on thread exit\n");
+ /****** end of MOUSE_LL hook test *************/
+
+ ok(DestroyWindow(hwnd), "failed to destroy window\n");
+}
+
+static void test_set_hook(void)
+{
+ BOOL ret;
+ HHOOK hhook;
+ HWINEVENTHOOK hwinevent_hook;
+ HMODULE user32 = GetModuleHandleA("user32.dll");
+ FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook");
+ FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent");
+
+ hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, GetModuleHandleA(0), GetCurrentThreadId());
+ ok(hhook != 0, "local hook does not require hModule set to 0\n");
+ UnhookWindowsHookEx(hhook);
+
+#if 0 /* this test doesn't pass under Win9x: BUG! */
+ SetLastError(0xdeadbeef);
+ hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, 0);
+ ok(!hhook, "global hook requires hModule != 0\n");
+ ok(GetLastError() == ERROR_HOOK_NEEDS_HMOD, "unexpected error %ld\n", GetLastError());
+#endif
+
+ SetLastError(0xdeadbeef);
+ hhook = SetWindowsHookExA(WH_CBT, 0, GetModuleHandleA(0), GetCurrentThreadId());
+ ok(!hhook, "SetWinEventHook with invalid proc should fail\n");
+ ok(GetLastError() == ERROR_INVALID_FILTER_PROC || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "unexpected error %ld\n", GetLastError());
+
+ SetLastError(0xdeadbeef);
+ ok(!UnhookWindowsHookEx((HHOOK)0xdeadbeef), "UnhookWindowsHookEx succeeded\n");
+ ok(GetLastError() == ERROR_INVALID_HOOK_HANDLE || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "unexpected error %ld\n", GetLastError());
+
+ if (!pSetWinEventHook || !pUnhookWinEvent) return;
+
+ /* even process local incontext hooks require hmodule */
+ SetLastError(0xdeadbeef);
+ hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX,
+ 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
+ ok(!hwinevent_hook, "WINEVENT_INCONTEXT requires hModule != 0\n");
+ ok(GetLastError() == ERROR_HOOK_NEEDS_HMOD || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "unexpected error %ld\n", GetLastError());
+
+ /* even thread local incontext hooks require hmodule */
+ SetLastError(0xdeadbeef);
+ hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX,
+ 0, win_event_proc, GetCurrentProcessId(), GetCurrentThreadId(), WINEVENT_INCONTEXT);
+ ok(!hwinevent_hook, "WINEVENT_INCONTEXT requires hModule != 0\n");
+ ok(GetLastError() == ERROR_HOOK_NEEDS_HMOD || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "unexpected error %ld\n", GetLastError());
+
+#if 0 /* these 3 tests don't pass under Win9x */
+ SetLastError(0xdeadbeef);
+ hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(1, 0,
+ 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_OUTOFCONTEXT);
+ ok(!hwinevent_hook, "SetWinEventHook with invalid event range should fail\n");
+ ok(GetLastError() == ERROR_INVALID_HOOK_FILTER, "unexpected error %ld\n", GetLastError());
+
+ SetLastError(0xdeadbeef);
+ hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(-1, 1,
+ 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_OUTOFCONTEXT);
+ ok(!hwinevent_hook, "SetWinEventHook with invalid event range should fail\n");
+ ok(GetLastError() == ERROR_INVALID_HOOK_FILTER, "unexpected error %ld\n", GetLastError());
+
+ SetLastError(0xdeadbeef);
+ hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX,
+ 0, win_event_proc, 0, 0xdeadbeef, WINEVENT_OUTOFCONTEXT);
+ ok(!hwinevent_hook, "SetWinEventHook with invalid tid should fail\n");
+ ok(GetLastError() == ERROR_INVALID_THREAD_ID, "unexpected error %ld\n", GetLastError());
+#endif
+
+ SetLastError(0xdeadbeef);
+ hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(0, 0,
+ 0, win_event_proc, GetCurrentProcessId(), 0, WINEVENT_OUTOFCONTEXT);
+ ok(hwinevent_hook != 0, "SetWinEventHook error %ld\n", GetLastError());
+ ok(GetLastError() == 0xdeadbeef, "unexpected error %ld\n", GetLastError());
+ ret = pUnhookWinEvent(hwinevent_hook);
+ ok( ret, "UnhookWinEvent error %ld\n", GetLastError());
+
+todo_wine {
+ /* This call succeeds under win2k SP4, but fails under Wine.
+ Does win2k test/use passed process id? */
+ SetLastError(0xdeadbeef);
+ hwinevent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX,
+ 0, win_event_proc, 0xdeadbeef, 0, WINEVENT_OUTOFCONTEXT);
+ ok(hwinevent_hook != 0, "SetWinEventHook error %ld\n", GetLastError());
+ ok(GetLastError() == 0xdeadbeef, "unexpected error %ld\n", GetLastError());
+ ret = pUnhookWinEvent(hwinevent_hook);
+ ok( ret, "UnhookWinEvent error %ld\n", GetLastError());
+}
+
+ SetLastError(0xdeadbeef);
+ ok(!pUnhookWinEvent((HWINEVENTHOOK)0xdeadbeef), "UnhookWinEvent succeeded\n");
+ ok(GetLastError() == ERROR_INVALID_HANDLE || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "unexpected error %ld\n", GetLastError());
+}
+
+static const struct message ScrollWindowPaint1[] = {
+ { WM_PAINT, sent },
+ { WM_ERASEBKGND, sent|beginpaint },
+ { 0 }
+};
+
+static const struct message ScrollWindowPaint2[] = {
+ { WM_PAINT, sent },
+ { 0 }
+};
+
+static void test_scrollwindowex(void)
+{
+ HWND hwnd, hchild;
+ RECT rect={0,0,130,130};
+ MSG msg;
+
+ hwnd = CreateWindowExA(0, "TestWindowClass", "Test Scroll",
+ WS_VISIBLE|WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (hwnd != 0, "Failed to create overlapped window\n");
+ hchild = CreateWindowExA(0, "TestWindowClass", "Test child",
+ WS_VISIBLE|WS_CAPTION|WS_CHILD,
+ 10, 10, 150, 150, hwnd, 0, 0, NULL);
+ ok (hchild != 0, "Failed to create child\n");
+ UpdateWindow(hwnd);
+ flush_events();
+ flush_sequence();
+
+ /* scroll without the child window */
+ trace("start scroll\n");
+ ScrollWindowEx( hwnd, 10, 10, &rect, NULL, NULL, NULL,
+ SW_ERASE|SW_INVALIDATE);
+ ok_sequence(WmEmptySeq, "ScrollWindowEx", 0);
+ trace("end scroll\n");
+ flush_sequence();
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(ScrollWindowPaint1, "ScrollWindowEx", 0);
+
+ /* Now without the SW_ERASE flag */
+ trace("start scroll\n");
+ ScrollWindowEx( hwnd, 10, 10, &rect, NULL, NULL, NULL, SW_INVALIDATE);
+ ok_sequence(WmEmptySeq, "ScrollWindowEx", 0);
+ trace("end scroll\n");
+ flush_sequence();
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(ScrollWindowPaint2, "ScrollWindowEx", 0);
+
+ /* now scroll the child window as well */
+ trace("start scroll\n");
+ ScrollWindowEx( hwnd, 10, 10, &rect, NULL, NULL, NULL,
+ SW_SCROLLCHILDREN|SW_ERASE|SW_INVALIDATE);
+ todo_wine { /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
+ /* windows sometimes a WM_MOVE */
+ ok_sequence(WmEmptySeq, "ScrollWindowEx", 0);
+ }
+ trace("end scroll\n");
+ flush_sequence();
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(ScrollWindowPaint1, "ScrollWindowEx", 0);
+
+ /* now scroll with ScrollWindow() */
+ trace("start scroll with ScrollWindow\n");
+ ScrollWindow( hwnd, 5, 5, NULL, NULL);
+ trace("end scroll\n");
+ flush_sequence();
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ ok_sequence(ScrollWindowPaint1, "ScrollWindow", 0);
+
+ ok(DestroyWindow(hchild), "failed to destroy window\n");
+ ok(DestroyWindow(hwnd), "failed to destroy window\n");
+ flush_sequence();
+}
+
+static const struct message destroy_window_with_children[] = {
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 }, /* popup */
+ { HCBT_DESTROYWND, hook|lparam, 0, WND_PARENT_ID }, /* parent */
+ { HCBT_DESTROYWND, hook|lparam, 0, WND_POPUP_ID }, /* popup */
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, /* popup */
+ { WM_DESTROY, sent|wparam|lparam, 0, WND_POPUP_ID }, /* popup */
+ { WM_CAPTURECHANGED, sent|wparam|lparam, 0, WND_POPUP_ID }, /* popup */
+ { WM_NCDESTROY, sent|wparam|lparam, 0, WND_POPUP_ID }, /* popup */
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, 0, 0 }, /* parent */
+ { WM_DESTROY, sent|wparam|lparam, 0, WND_PARENT_ID }, /* parent */
+ { WM_DESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 2 }, /* child2 */
+ { WM_DESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 1 }, /* child1 */
+ { WM_DESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 3 }, /* child3 */
+ { WM_NCDESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 2 }, /* child2 */
+ { WM_NCDESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 3 }, /* child3 */
+ { WM_NCDESTROY, sent|wparam|lparam, 0, WND_CHILD_ID + 1 }, /* child1 */
+ { WM_NCDESTROY, sent|wparam|lparam, 0, WND_PARENT_ID }, /* parent */
+ { 0 }
+};
+
+static void test_DestroyWindow(void)
+{
+ BOOL ret;
+ HWND parent, child1, child2, child3, child4, test;
+ UINT child_id = WND_CHILD_ID + 1;
+
+ parent = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ assert(parent != 0);
+ child1 = CreateWindowExA(0, "TestWindowClass", NULL, WS_CHILD,
+ 0, 0, 50, 50, parent, (HMENU)child_id++, 0, NULL);
+ assert(child1 != 0);
+ child2 = CreateWindowExA(0, "TestWindowClass", NULL, WS_CHILD,
+ 0, 0, 50, 50, GetDesktopWindow(), (HMENU)child_id++, 0, NULL);
+ assert(child2 != 0);
+ child3 = CreateWindowExA(0, "TestWindowClass", NULL, WS_CHILD,
+ 0, 0, 50, 50, child1, (HMENU)child_id++, 0, NULL);
+ assert(child3 != 0);
+ child4 = CreateWindowExA(0, "TestWindowClass", NULL, WS_POPUP,
+ 0, 0, 50, 50, parent, 0, 0, NULL);
+ assert(child4 != 0);
+
+ /* test owner/parent of child2 */
+ test = GetParent(child2);
+ ok(test == GetDesktopWindow(), "wrong parent %p\n", test);
+ ok(!IsChild(parent, child2), "wrong parent/child %p/%p\n", parent, child2);
+ if(pGetAncestor) {
+ test = pGetAncestor(child2, GA_PARENT);
+ ok(test == GetDesktopWindow(), "wrong parent %p\n", test);
+ }
+ test = GetWindow(child2, GW_OWNER);
+ ok(!test, "wrong owner %p\n", test);
+
+ test = SetParent(child2, parent);
+ ok(test == GetDesktopWindow(), "wrong old parent %p\n", test);
+
+ /* test owner/parent of the parent */
+ test = GetParent(parent);
+ ok(!test, "wrong parent %p\n", test);
+todo_wine {
+ ok(!IsChild(GetDesktopWindow(), parent), "wrong parent/child %p/%p\n", GetDesktopWindow(), parent);
+}
+ if(pGetAncestor) {
+ test = pGetAncestor(parent, GA_PARENT);
+ ok(test == GetDesktopWindow(), "wrong parent %p\n", test);
+ }
+ test = GetWindow(parent, GW_OWNER);
+ ok(!test, "wrong owner %p\n", test);
+
+ /* test owner/parent of child1 */
+ test = GetParent(child1);
+ ok(test == parent, "wrong parent %p\n", test);
+ ok(IsChild(parent, child1), "wrong parent/child %p/%p\n", parent, child1);
+ if(pGetAncestor) {
+ test = pGetAncestor(child1, GA_PARENT);
+ ok(test == parent, "wrong parent %p\n", test);
+ }
+ test = GetWindow(child1, GW_OWNER);
+ ok(!test, "wrong owner %p\n", test);
+
+ /* test owner/parent of child2 */
+ test = GetParent(child2);
+ ok(test == parent, "wrong parent %p\n", test);
+ ok(IsChild(parent, child2), "wrong parent/child %p/%p\n", parent, child2);
+ if(pGetAncestor) {
+ test = pGetAncestor(child2, GA_PARENT);
+ ok(test == parent, "wrong parent %p\n", test);
+ }
+ test = GetWindow(child2, GW_OWNER);
+ ok(!test, "wrong owner %p\n", test);
+
+ /* test owner/parent of child3 */
+ test = GetParent(child3);
+ ok(test == child1, "wrong parent %p\n", test);
+ ok(IsChild(parent, child3), "wrong parent/child %p/%p\n", parent, child3);
+ if(pGetAncestor) {
+ test = pGetAncestor(child3, GA_PARENT);
+ ok(test == child1, "wrong parent %p\n", test);
+ }
+ test = GetWindow(child3, GW_OWNER);
+ ok(!test, "wrong owner %p\n", test);
+
+ /* test owner/parent of child4 */
+ test = GetParent(child4);
+ ok(test == parent, "wrong parent %p\n", test);
+ ok(!IsChild(parent, child4), "wrong parent/child %p/%p\n", parent, child4);
+ if(pGetAncestor) {
+ test = pGetAncestor(child4, GA_PARENT);
+ ok(test == GetDesktopWindow(), "wrong parent %p\n", test);
+ }
+ test = GetWindow(child4, GW_OWNER);
+ ok(test == parent, "wrong owner %p\n", test);
+
+ flush_sequence();
+
+ trace("parent %p, child1 %p, child2 %p, child3 %p, child4 %p\n",
+ parent, child1, child2, child3, child4);
+
+ SetCapture(child4);
+ test = GetCapture();
+ ok(test == child4, "wrong capture window %p\n", test);
+
+ test_DestroyWindow_flag = TRUE;
+ ret = DestroyWindow(parent);
+ ok( ret, "DestroyWindow() error %ld\n", GetLastError());
+ test_DestroyWindow_flag = FALSE;
+ ok_sequence(destroy_window_with_children, "destroy window with children", 0);
+
+ ok(!IsWindow(parent), "parent still exists\n");
+ ok(!IsWindow(child1), "child1 still exists\n");
+ ok(!IsWindow(child2), "child2 still exists\n");
+ ok(!IsWindow(child3), "child3 still exists\n");
+ ok(!IsWindow(child4), "child4 still exists\n");
+
+ test = GetCapture();
+ ok(!test, "wrong capture window %p\n", test);
+}
+
+
+static const struct message WmDispatchPaint[] = {
+ { WM_NCPAINT, sent },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_GETTEXT, sent|defwinproc|optional },
+ { WM_ERASEBKGND, sent },
+ { 0 }
+};
+
+static LRESULT WINAPI DispatchMessageCheckProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (message == WM_PAINT) return 0;
+ return MsgCheckProcA( hwnd, message, wParam, lParam );
+}
+
+static void test_DispatchMessage(void)
+{
+ RECT rect;
+ MSG msg;
+ int count;
+ HWND hwnd = CreateWindowA( "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ShowWindow( hwnd, SW_SHOW );
+ UpdateWindow( hwnd );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+ SetWindowLongPtrA( hwnd, GWLP_WNDPROC, (LONG_PTR)DispatchMessageCheckProc );
+
+ SetRect( &rect, -5, -5, 5, 5 );
+ RedrawWindow( hwnd, &rect, 0, RDW_INVALIDATE|RDW_ERASE|RDW_FRAME );
+ count = 0;
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
+ {
+ if (msg.message != WM_PAINT) DispatchMessage( &msg );
+ else
+ {
+ flush_sequence();
+ DispatchMessage( &msg );
+ /* DispatchMessage will send WM_NCPAINT if non client area is still invalid after WM_PAINT */
+ if (!count) ok_sequence( WmDispatchPaint, "WmDispatchPaint", FALSE );
+ else ok_sequence( WmEmptySeq, "WmEmpty", FALSE );
+ if (++count > 10) break;
+ }
+ }
+ ok( msg.message == WM_PAINT && count > 10, "WM_PAINT messages stopped\n" );
+
+ trace("now without DispatchMessage\n");
+ flush_sequence();
+ RedrawWindow( hwnd, &rect, 0, RDW_INVALIDATE|RDW_ERASE|RDW_FRAME );
+ count = 0;
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
+ {
+ if (msg.message != WM_PAINT) DispatchMessage( &msg );
+ else
+ {
+ HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
+ flush_sequence();
+ /* this will send WM_NCCPAINT just like DispatchMessage does */
+ GetUpdateRgn( hwnd, hrgn, TRUE );
+ ok_sequence( WmDispatchPaint, "WmDispatchPaint", FALSE );
+ DeleteObject( hrgn );
+ GetClientRect( hwnd, &rect );
+ ValidateRect( hwnd, &rect ); /* this will stop WM_PAINTs */
+ ok( !count, "Got multiple WM_PAINTs\n" );
+ if (++count > 10) break;
+ }
+ }
+ DestroyWindow(hwnd);
+}
+
+
+static const struct message WmUser[] = {
+ { WM_USER, sent },
+ { 0 }
+};
+
+struct sendmsg_info
+{
+ HWND hwnd;
+ DWORD timeout;
+ DWORD ret;
+};
+
+static DWORD CALLBACK send_msg_thread( LPVOID arg )
+{
+ struct sendmsg_info *info = arg;
+ info->ret = SendMessageTimeoutA( info->hwnd, WM_USER, 0, 0, 0, info->timeout, NULL );
+ if (!info->ret) ok( GetLastError() == ERROR_TIMEOUT, "unexpected error %ld\n", GetLastError());
+ return 0;
+}
+
+static void wait_for_thread( HANDLE thread )
+{
+ while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_SENDMESSAGE) != WAIT_OBJECT_0)
+ {
+ MSG msg;
+ while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage(&msg);
+ }
+}
+
+static LRESULT WINAPI send_msg_delay_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ if (message == WM_USER) Sleep(200);
+ return MsgCheckProcA( hwnd, message, wParam, lParam );
+}
+
+static void test_SendMessageTimeout(void)
+{
+ MSG msg;
+ HANDLE thread;
+ struct sendmsg_info info;
+ DWORD tid;
+
+ info.hwnd = CreateWindowA( "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ flush_sequence();
+
+ info.timeout = 1000;
+ info.ret = 0xdeadbeef;
+ thread = CreateThread( NULL, 0, send_msg_thread, &info, 0, &tid );
+ wait_for_thread( thread );
+ CloseHandle( thread );
+ ok( info.ret == 1, "SendMessageTimeout failed\n" );
+ ok_sequence( WmUser, "WmUser", FALSE );
+
+ info.timeout = 1;
+ info.ret = 0xdeadbeef;
+ thread = CreateThread( NULL, 0, send_msg_thread, &info, 0, &tid );
+ Sleep(100); /* SendMessageTimeout should timeout here */
+ wait_for_thread( thread );
+ CloseHandle( thread );
+ ok( info.ret == 0, "SendMessageTimeout succeeded\n" );
+ ok_sequence( WmEmptySeq, "WmEmptySeq", FALSE );
+
+ /* 0 means infinite timeout */
+ info.timeout = 0;
+ info.ret = 0xdeadbeef;
+ thread = CreateThread( NULL, 0, send_msg_thread, &info, 0, &tid );
+ Sleep(100);
+ wait_for_thread( thread );
+ CloseHandle( thread );
+ ok( info.ret == 1, "SendMessageTimeout failed\n" );
+ ok_sequence( WmUser, "WmUser", FALSE );
+
+ /* timeout is treated as signed despite the prototype */
+ info.timeout = 0x7fffffff;
+ info.ret = 0xdeadbeef;
+ thread = CreateThread( NULL, 0, send_msg_thread, &info, 0, &tid );
+ Sleep(100);
+ wait_for_thread( thread );
+ CloseHandle( thread );
+ ok( info.ret == 1, "SendMessageTimeout failed\n" );
+ ok_sequence( WmUser, "WmUser", FALSE );
+
+ info.timeout = 0x80000000;
+ info.ret = 0xdeadbeef;
+ thread = CreateThread( NULL, 0, send_msg_thread, &info, 0, &tid );
+ Sleep(100);
+ wait_for_thread( thread );
+ CloseHandle( thread );
+ ok( info.ret == 0, "SendMessageTimeout succeeded\n" );
+ ok_sequence( WmEmptySeq, "WmEmptySeq", FALSE );
+
+ /* now check for timeout during message processing */
+ SetWindowLongPtrA( info.hwnd, GWLP_WNDPROC, (LONG_PTR)send_msg_delay_proc );
+ info.timeout = 100;
+ info.ret = 0xdeadbeef;
+ thread = CreateThread( NULL, 0, send_msg_thread, &info, 0, &tid );
+ wait_for_thread( thread );
+ CloseHandle( thread );
+ /* we should timeout but still get the message */
+ ok( info.ret == 0, "SendMessageTimeout failed\n" );
+ ok_sequence( WmUser, "WmUser", FALSE );
+
+ DestroyWindow( info.hwnd );
+}
+
+
+/****************** edit message test *************************/
+#define ID_EDIT 0x1234
+static const struct message sl_edit_setfocus[] =
+{
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam, 0 },
+ { WM_CTLCOLOREDIT, sent|parent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { WM_COMMAND, sent|parent|wparam, MAKEWPARAM(ID_EDIT, EN_SETFOCUS) },
+ { 0 }
+};
+static const struct message ml_edit_setfocus[] =
+{
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam, 0 },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { WM_COMMAND, sent|parent|wparam, MAKEWPARAM(ID_EDIT, EN_SETFOCUS) },
+ { 0 }
+};
+static const struct message sl_edit_killfocus[] =
+{
+ { HCBT_SETFOCUS, hook },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_KILLFOCUS, sent|wparam, 0 },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_DESTROY, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { WM_COMMAND, sent|parent|wparam, MAKEWPARAM(ID_EDIT, EN_KILLFOCUS) },
+ { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
+ { 0 }
+};
+static const struct message sl_edit_lbutton_dblclk[] =
+{
+ { WM_LBUTTONDBLCLK, sent },
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+ { 0 }
+};
+static const struct message sl_edit_lbutton_down[] =
+{
+ { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { WM_CTLCOLOREDIT, sent|parent },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { WM_COMMAND, sent|parent|wparam, MAKEWPARAM(ID_EDIT, EN_SETFOCUS) },
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { 0 }
+};
+static const struct message ml_edit_lbutton_down[] =
+{
+ { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
+ { EVENT_SYSTEM_CAPTURESTART, winevent_hook|wparam|lparam, 0, 0 },
+ { HCBT_SETFOCUS, hook },
+ { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
+ { EVENT_OBJECT_FOCUS, winevent_hook|wparam|lparam, OBJID_CLIENT, 0 },
+ { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
+ { EVENT_OBJECT_CREATE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { WM_COMMAND, sent|parent|wparam, MAKEWPARAM(ID_EDIT, EN_SETFOCUS) },
+ { 0 }
+};
+static const struct message sl_edit_lbutton_up[] =
+{
+ { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 },
+ { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CAPTURECHANGED, sent|defwinproc },
+ { EVENT_OBJECT_SHOW, winevent_hook|wparam|lparam, OBJID_CARET, 0 },
+ { 0 }
+};
+static const struct message ml_edit_lbutton_up[] =
+{
+ { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 },
+ { EVENT_SYSTEM_CAPTUREEND, winevent_hook|wparam|lparam, 0, 0 },
+ { WM_CAPTURECHANGED, sent|defwinproc },
+ { 0 }
+};
+
+static WNDPROC old_edit_proc;
+
+static LRESULT CALLBACK edit_hook_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ static long defwndproc_counter = 0;
+ LRESULT ret;
+ struct message msg;
+
+ trace("edit: %p, %04x, %08x, %08lx\n", hwnd, message, wParam, lParam);
+
+ /* explicitly ignore WM_GETICON message */
+ if (message == WM_GETICON) return 0;
+
+ msg.message = message;
+ msg.flags = sent|wparam|lparam;
+ if (defwndproc_counter) msg.flags |= defwinproc;
+ msg.wParam = wParam;
+ msg.lParam = lParam;
+ add_message(&msg);
+
+ defwndproc_counter++;
+ ret = CallWindowProcA(old_edit_proc, hwnd, message, wParam, lParam);
+ defwndproc_counter--;
+
+ return ret;
+}
+
+static void subclass_edit(void)
+{
+ WNDCLASSA cls;
+
+ if (!GetClassInfoA(0, "edit", &cls)) assert(0);
+
+ old_edit_proc = cls.lpfnWndProc;
+
+ cls.hInstance = GetModuleHandle(0);
+ cls.lpfnWndProc = edit_hook_proc;
+ cls.lpszClassName = "my_edit_class";
+ if (!RegisterClassA(&cls)) assert(0);
+}
+
+static void test_edit_messages(void)
+{
+ HWND hwnd, parent;
+ DWORD dlg_code;
+
+ subclass_edit();
+ log_all_parent_messages++;
+
+ parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ok (parent != 0, "Failed to create parent window\n");
+
+ /* test single line edit */
+ hwnd = CreateWindowExA(0, "my_edit_class", "test", WS_CHILD,
+ 0, 0, 80, 20, parent, (HMENU)ID_EDIT, 0, NULL);
+ ok(hwnd != 0, "Failed to create edit window\n");
+
+ dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
+ ok(dlg_code == (DLGC_WANTCHARS|DLGC_HASSETSEL|DLGC_WANTARROWS), "wrong dlg_code %08lx\n", dlg_code);
+
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow(hwnd);
+ SetFocus(0);
+ flush_sequence();
+
+ SetFocus(hwnd);
+ ok_sequence(sl_edit_setfocus, "SetFocus(hwnd) on an edit", FALSE);
+
+ SetFocus(0);
+ ok_sequence(sl_edit_killfocus, "SetFocus(0) on an edit", FALSE);
+
+ SetFocus(0);
+ ReleaseCapture();
+ flush_sequence();
+
+ SendMessageA(hwnd, WM_LBUTTONDBLCLK, 0, 0);
+ ok_sequence(sl_edit_lbutton_dblclk, "WM_LBUTTONDBLCLK on an edit", FALSE);
+
+ SetFocus(0);
+ ReleaseCapture();
+ flush_sequence();
+
+ SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
+ ok_sequence(sl_edit_lbutton_down, "WM_LBUTTONDOWN on an edit", FALSE);
+
+ SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
+ ok_sequence(sl_edit_lbutton_up, "WM_LBUTTONUP on an edit", FALSE);
+
+ DestroyWindow(hwnd);
+
+ /* test multiline edit */
+ hwnd = CreateWindowExA(0, "my_edit_class", "test", WS_CHILD | ES_MULTILINE,
+ 0, 0, 80, 20, parent, (HMENU)ID_EDIT, 0, NULL);
+ ok(hwnd != 0, "Failed to create edit window\n");
+
+ dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
+ ok(dlg_code == (DLGC_WANTCHARS|DLGC_HASSETSEL|DLGC_WANTARROWS|DLGC_WANTALLKEYS),
+ "wrong dlg_code %08lx\n", dlg_code);
+
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow(hwnd);
+ SetFocus(0);
+ flush_sequence();
+
+ SetFocus(hwnd);
+ ok_sequence(ml_edit_setfocus, "SetFocus(hwnd) on multiline edit", FALSE);
+
+ SetFocus(0);
+ ok_sequence(sl_edit_killfocus, "SetFocus(0) on multiline edit", FALSE);
+
+ SetFocus(0);
+ ReleaseCapture();
+ flush_sequence();
+
+ SendMessageA(hwnd, WM_LBUTTONDBLCLK, 0, 0);
+ ok_sequence(sl_edit_lbutton_dblclk, "WM_LBUTTONDBLCLK on multiline edit", FALSE);
+
+ SetFocus(0);
+ ReleaseCapture();
+ flush_sequence();
+
+ SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
+ ok_sequence(ml_edit_lbutton_down, "WM_LBUTTONDOWN on multiline edit", FALSE);
+
+ SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
+ ok_sequence(ml_edit_lbutton_up, "WM_LBUTTONUP on multiline edit", FALSE);
+
+ DestroyWindow(hwnd);
+ DestroyWindow(parent);
+
+ log_all_parent_messages--;
+}
+
+/**************************** End of Edit test ******************************/
+
+static const struct message WmChar[] = {
+ { WM_CHAR, sent|wparam, 'z' },
+ { 0 }
+};
+
+static const struct message WmKeyDownUp[] = {
+ { WM_KEYDOWN, sent|wparam|lparam, 'N', 0x00000001 },
+ { WM_KEYUP, sent|wparam|lparam, 'N', 0xc0000001 },
+ { 0 }
+};
+
+static const struct message WmUserChar[] = {
+ { WM_USER, sent },
+ { WM_CHAR, sent|wparam, 'z' },
+ { 0 }
+};
+
+#define EV_START_STOP 0
+#define EV_SENDMSG 1
+#define EV_ACK 2
+
+struct peekmsg_info
+{
+ HWND hwnd;
+ HANDLE hevent[3]; /* 0 - start/stop, 1 - SendMessage, 2 - ack */
+};
+
+static DWORD CALLBACK send_msg_thread_2(void *param)
+{
+ DWORD ret;
+ struct peekmsg_info *info = param;
+
+ trace("thread: waiting for start\n");
+ WaitForSingleObject(info->hevent[EV_START_STOP], INFINITE);
+ trace("thread: looping\n");
+
+ while (1)
+ {
+ ret = WaitForMultipleObjects(2, info->hevent, FALSE, INFINITE);
+
+ switch (ret)
+ {
+ case WAIT_OBJECT_0 + EV_START_STOP:
+ trace("thread: exiting\n");
+ return 0;
+
+ case WAIT_OBJECT_0 + EV_SENDMSG:
+ trace("thread: sending message\n");
+ SendNotifyMessageA(info->hwnd, WM_USER, 0, 0);
+ SetEvent(info->hevent[EV_ACK]);
+ break;
+
+ default:
+ trace("unexpected return: %04lx\n", ret);
+ assert(0);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void test_PeekMessage(void)
+{
+ MSG msg;
+ HANDLE hthread;
+ DWORD tid, qstatus;
+ UINT qs_all_input = QS_ALLINPUT;
+ UINT qs_input = QS_INPUT;
+ struct peekmsg_info info;
+
+ info.hwnd = CreateWindowA("TestWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ ShowWindow(info.hwnd, SW_SHOW);
+ UpdateWindow(info.hwnd);
+
+ info.hevent[EV_START_STOP] = CreateEventA(NULL, 0, 0, NULL);
+ info.hevent[EV_SENDMSG] = CreateEventA(NULL, 0, 0, NULL);
+ info.hevent[EV_ACK] = CreateEventA(NULL, 0, 0, NULL);
+
+ hthread = CreateThread(NULL, 0, send_msg_thread_2, &info, 0, &tid);
+
+ trace("signalling to start looping\n");
+ SetEvent(info.hevent[EV_START_STOP]);
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ flush_sequence();
+
+ SetLastError(0xdeadbeef);
+ qstatus = GetQueueStatus(qs_all_input);
+ if (GetLastError() == ERROR_INVALID_FLAGS)
+ {
+ trace("QS_RAWINPUT not supported on this platform\n");
+ qs_all_input &= ~QS_RAWINPUT;
+ qs_input &= ~QS_RAWINPUT;
+ }
+ ok(qstatus == 0, "wrong qstatus %08lx\n", qstatus);
+
+ trace("signalling to send message\n");
+ SetEvent(info.hevent[EV_SENDMSG]);
+ WaitForSingleObject(info.hevent[EV_ACK], INFINITE);
+
+ /* pass invalid QS_xxxx flags */
+ SetLastError(0xdeadbeef);
+ qstatus = GetQueueStatus(0xffffffff);
+ ok(qstatus == 0, "GetQueueStatus should fail: %08lx\n", qstatus);
+ ok(GetLastError() == ERROR_INVALID_FLAGS, "wrong error %ld\n", GetLastError());
+
+ qstatus = GetQueueStatus(qs_all_input);
+ ok(qstatus == MAKELONG(QS_SENDMESSAGE, QS_SENDMESSAGE),
+ "wrong qstatus %08lx\n", qstatus);
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ ok_sequence(WmUser, "WmUser", FALSE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+ ok(qstatus == 0, "wrong qstatus %08lx\n", qstatus);
+
+ keybd_event('N', 0, 0, 0);
+ keybd_event('N', 0, KEYEVENTF_KEYUP, 0);
+ qstatus = GetQueueStatus(qs_all_input);
+ ok(qstatus == MAKELONG(QS_KEY, QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+
+ PostMessageA(info.hwnd, WM_CHAR, 'z', 0);
+ qstatus = GetQueueStatus(qs_all_input);
+ ok(qstatus == MAKELONG(QS_POSTMESSAGE, QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+
+ InvalidateRect(info.hwnd, NULL, FALSE);
+ qstatus = GetQueueStatus(qs_all_input);
+ ok(qstatus == MAKELONG(QS_PAINT, QS_PAINT|QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+
+ trace("signalling to send message\n");
+ SetEvent(info.hevent[EV_SENDMSG]);
+ WaitForSingleObject(info.hevent[EV_ACK], INFINITE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+ ok(qstatus == MAKELONG(QS_SENDMESSAGE, QS_SENDMESSAGE|QS_PAINT|QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | (qs_input << 16))) DispatchMessageA(&msg);
+ ok_sequence(WmUser, "WmUser", TRUE); /* todo_wine */
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_PAINT|QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ trace("signalling to send message\n");
+ SetEvent(info.hevent[EV_SENDMSG]);
+ WaitForSingleObject(info.hevent[EV_ACK], INFINITE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(QS_SENDMESSAGE, QS_SENDMESSAGE|QS_PAINT|QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_QS_POSTMESSAGE)) DispatchMessageA(&msg);
+ ok_sequence(WmUser, "WmUser", FALSE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_PAINT|QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_QS_POSTMESSAGE)) DispatchMessageA(&msg);
+ ok_sequence(WmChar, "WmChar", TRUE); /* todo_wine */
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_PAINT|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_QS_PAINT)) DispatchMessageA(&msg);
+ ok_sequence(WmPaint, "WmPaint", TRUE); /* todo_wine */
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ trace("signalling to send message\n");
+ SetEvent(info.hevent[EV_SENDMSG]);
+ WaitForSingleObject(info.hevent[EV_ACK], INFINITE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(QS_SENDMESSAGE, QS_SENDMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ PostMessageA(info.hwnd, WM_CHAR, 'z', 0);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(QS_POSTMESSAGE, QS_SENDMESSAGE|QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ while (PeekMessageA(&msg, 0, WM_CHAR, WM_CHAR, PM_REMOVE)) DispatchMessage(&msg);
+ ok_sequence(WmUserChar, "WmUserChar", FALSE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ PostMessageA(info.hwnd, WM_CHAR, 'z', 0);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(QS_POSTMESSAGE, QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ trace("signalling to send message\n");
+ SetEvent(info.hevent[EV_SENDMSG]);
+ WaitForSingleObject(info.hevent[EV_ACK], INFINITE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(QS_SENDMESSAGE, QS_SENDMESSAGE|QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | (QS_KEY << 16))) DispatchMessage(&msg);
+ ok_sequence(WmUser, "WmUser", TRUE); /* todo_wine */
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_POSTMESSAGE|QS_KEY),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | (QS_RAWINPUT << 16))) DispatchMessage(&msg);
+ ok_sequence(WmKeyDownUp, "WmKeyDownUp", TRUE); /* todo_wine */
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_POSTMESSAGE),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE)) DispatchMessage(&msg);
+ ok_sequence(WmEmptySeq, "WmEmptySeq", FALSE);
+
+ qstatus = GetQueueStatus(qs_all_input);
+todo_wine {
+ ok(qstatus == MAKELONG(0, QS_POSTMESSAGE),
+ "wrong qstatus %08lx\n", qstatus);
+}
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ ok_sequence(WmChar, "WmChar", TRUE); /* todo_wine */
+
+ qstatus = GetQueueStatus(qs_all_input);
+ ok(qstatus == 0,
+ "wrong qstatus %08lx\n", qstatus);
+
+ trace("signalling to exit\n");
+ SetEvent(info.hevent[EV_START_STOP]);
+
+ WaitForSingleObject(hthread, INFINITE);
+
+ CloseHandle(hthread);
+ CloseHandle(info.hevent[0]);
+ CloseHandle(info.hevent[1]);
+ CloseHandle(info.hevent[2]);
+
+ DestroyWindow(info.hwnd);
+}
+
+
+static void test_quit_message(void)
+{
+ MSG msg;
+ BOOL ret;
+
+ /* test using PostQuitMessage */
+ PostQuitMessage(0xbeef);
+
+ ret = PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
+ ok(ret, "PeekMessage failed with error %ld\n", GetLastError());
+ ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message);
+ ok(msg.wParam == 0xbeef, "wParam was 0x%x instead of 0xbeef\n", msg.wParam);
+
+ ret = PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0);
+ ok(ret, "PostMessage failed with error %ld\n", GetLastError());
+
+ ret = GetMessage(&msg, NULL, 0, 0);
+ ok(ret > 0, "GetMessage failed with error %ld\n", GetLastError());
+ ok(msg.message == WM_USER, "Received message 0x%04x instead of WM_USER\n", msg.message);
+
+ /* note: WM_QUIT message received after WM_USER message */
+ ret = GetMessage(&msg, NULL, 0, 0);
+ ok(!ret, "GetMessage return %d with error %ld instead of FALSE\n", ret, GetLastError());
+ ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message);
+ ok(msg.wParam == 0xbeef, "wParam was 0x%x instead of 0xbeef\n", msg.wParam);
+
+ ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
+ ok( !ret || msg.message != WM_QUIT, "Received WM_QUIT again\n" );
+
+ /* now test with PostThreadMessage - different behaviour! */
+ PostThreadMessage(GetCurrentThreadId(), WM_QUIT, 0xdead, 0);
+
+ ret = PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
+ ok(ret, "PeekMessage failed with error %ld\n", GetLastError());
+ ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message);
+ ok(msg.wParam == 0xdead, "wParam was 0x%x instead of 0xdead\n", msg.wParam);
+
+ ret = PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0);
+ ok(ret, "PostMessage failed with error %ld\n", GetLastError());
+
+ /* note: we receive the WM_QUIT message first this time */
+ ret = GetMessage(&msg, NULL, 0, 0);
+ ok(!ret, "GetMessage return %d with error %ld instead of FALSE\n", ret, GetLastError());
+ ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message);
+ ok(msg.wParam == 0xdead, "wParam was 0x%x instead of 0xdead\n", msg.wParam);
+
+ ret = GetMessage(&msg, NULL, 0, 0);
+ ok(ret > 0, "GetMessage failed with error %ld\n", GetLastError());
+ ok(msg.message == WM_USER, "Received message 0x%04x instead of WM_USER\n", msg.message);
+}
+
+START_TEST(msg)
+{
+ BOOL ret;
+ HMODULE user32 = GetModuleHandleA("user32.dll");
+ FARPROC pSetWinEventHook = GetProcAddress(user32, "SetWinEventHook");
+ FARPROC pUnhookWinEvent = GetProcAddress(user32, "UnhookWinEvent");
+ FARPROC pIsWinEventHookInstalled = 0;/*GetProcAddress(user32, "IsWinEventHookInstalled");*/
+ pGetAncestor = (void*) GetProcAddress(user32, "GetAncestor");
+
+ if (!RegisterWindowClasses()) assert(0);
+
+ if (pSetWinEventHook)
+ {
+ hEvent_hook = (HWINEVENTHOOK)pSetWinEventHook(EVENT_MIN, EVENT_MAX,
+ GetModuleHandleA(0),
+ win_event_proc,
+ 0,
+ GetCurrentThreadId(),
+ WINEVENT_INCONTEXT);
+ assert(hEvent_hook);
+
+ if (pIsWinEventHookInstalled)
+ {
+ UINT event;
+ for (event = EVENT_MIN; event <= EVENT_MAX; event++)
+ ok(pIsWinEventHookInstalled(event), "IsWinEventHookInstalled(%u) failed\n", event);
+ }
+ }
+
+ cbt_hook_thread_id = GetCurrentThreadId();
+ hCBT_hook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId());
+ assert(hCBT_hook);
+
+ test_winevents();
+
+ /* Fix message sequences before removing 4 lines below */
+#if 1
+ ret = pUnhookWinEvent(hEvent_hook);
+ ok( ret, "UnhookWinEvent error %ld\n", GetLastError());
+ pUnhookWinEvent = 0;
+ hEvent_hook = 0;
+#endif
+
+ test_PeekMessage();
+ test_scrollwindowex();
+ test_messages();
+ test_mdi_messages();
+ test_button_messages();
+ test_paint_messages();
+ test_interthread_messages();
+ test_message_conversion();
+ test_accelerators();
+ test_timers();
+ test_set_hook();
+ test_DestroyWindow();
+ test_DispatchMessage();
+ test_SendMessageTimeout();
+ test_edit_messages();
+ test_quit_message();
+
+ UnhookWindowsHookEx(hCBT_hook);
+ if (pUnhookWinEvent)
+ {
+ ret = pUnhookWinEvent(hEvent_hook);
+ ok( ret, "UnhookWinEvent error %ld\n", GetLastError());
+ SetLastError(0xdeadbeef);
+ ok(!pUnhookWinEvent(hEvent_hook), "UnhookWinEvent succeeded\n");
+ ok(GetLastError() == ERROR_INVALID_HANDLE || /* Win2k */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "unexpected error %ld\n", GetLastError());
+ }
+}
--- /dev/null
+/* Unit test suite for resources.
+ *
+ * Copyright 2004 Ferenc Wagner
+ * Copyright 2003, 2004 Mike McCormack
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <windows.h>
+
+#include "wine/test.h"
+
+static UINT (WINAPI *pPrivateExtractIconsA)(LPCTSTR, int, int, int, HICON *, UINT *, UINT, UINT) = NULL;
+
+static void init_function_pointers(void)
+{
+ HMODULE hmod = GetModuleHandleA("user32.dll");
+ if (hmod) {
+ pPrivateExtractIconsA = (void*)GetProcAddress(hmod, "PrivateExtractIconsA");
+ }
+}
+
+static void test_LoadStringA (void)
+{
+ HINSTANCE hInst = GetModuleHandle (NULL);
+ static const char str[] = "String resource"; /* same in resource.rc */
+ char buf[128];
+ struct string_test {
+ int bufsiz;
+ int expected;
+ };
+ struct string_test tests[] = {{sizeof buf, sizeof str - 1},
+ {sizeof str, sizeof str - 1},
+ {sizeof str - 1, sizeof str - 2}};
+ unsigned int i;
+
+ assert (sizeof str < sizeof buf);
+ for (i = 0; i < sizeof tests / sizeof tests[0]; i++) {
+ const int bufsiz = tests[i].bufsiz;
+ const int expected = tests[i].expected;
+ const int len = LoadStringA (hInst, 0, buf, bufsiz);
+
+ ok (len == expected, "bufsiz=%d: got %d, expected %d\n",
+ bufsiz, len, expected);
+ ok (!memcmp (buf, str, len),
+ "bufsiz=%d: got '%s', expected '%.*s'\n",
+ bufsiz, buf, len, str);
+ ok (buf[len] == 0, "bufsiz=%d: NUL termination missing\n",
+ bufsiz);
+ }
+}
+
+static void test_accel1(void)
+{
+ UINT r, n;
+ HACCEL hAccel;
+ ACCEL ac[10];
+
+ /* now create our own valid accelerator table */
+ n = 0;
+ ac[n].cmd = 1000;
+ ac[n].key = 'A';
+ ac[n++].fVirt = FVIRTKEY | FNOINVERT;
+
+ ac[n].cmd = 1001;
+ ac[n].key = 'B';
+ ac[n++].fVirt = FNOINVERT;
+
+ ac[n].cmd = 0;
+ ac[n].key = 0;
+ ac[n++].fVirt = 0;
+
+ hAccel = CreateAcceleratorTable( &ac[0], n );
+ ok( hAccel != NULL, "create accelerator table\n");
+
+ r = DestroyAcceleratorTable( hAccel );
+ ok( r, "destroy accelerator table\n");
+
+ /* now try create an invalid one */
+ n = 0;
+ ac[n].cmd = 1000;
+ ac[n].key = 'A';
+ ac[n++].fVirt = FVIRTKEY | FNOINVERT;
+
+ ac[n].cmd = 0xffff;
+ ac[n].key = 0xffff;
+ ac[n++].fVirt = (SHORT) 0xffff;
+
+ ac[n].cmd = 0xfff0;
+ ac[n].key = 0xffff;
+ ac[n++].fVirt = (SHORT) 0xfff0;
+
+ ac[n].cmd = 0xfff0;
+ ac[n].key = 0xffff;
+ ac[n++].fVirt = (SHORT) 0x0000;
+
+ ac[n].cmd = 0xfff0;
+ ac[n].key = 0xffff;
+ ac[n++].fVirt = (SHORT) 0x0001;
+
+ hAccel = CreateAcceleratorTable( &ac[0], n );
+ ok( hAccel != NULL, "create accelerator table\n");
+
+ r = CopyAcceleratorTable( hAccel, NULL, 0 );
+ ok( r == n, "two entries in table\n");
+
+ r = CopyAcceleratorTable( hAccel, &ac[0], r );
+ ok( r == n, "still should be two entries in table\n");
+
+ n=0;
+ ok( ac[n].cmd == 1000, "cmd 0 not preserved\n");
+ ok( ac[n].key == 'A', "key 0 not preserved\n");
+ ok( ac[n].fVirt == (FVIRTKEY | FNOINVERT), "fVirt 0 not preserved\n");
+
+ n++;
+ ok( ac[n].cmd == 0xffff, "cmd 1 not preserved\n");
+ ok( ac[n].key == 0xffff, "key 1 not preserved\n");
+ ok( ac[n].fVirt == 0x007f, "fVirt 1 not changed\n");
+
+ n++;
+ ok( ac[n].cmd == 0xfff0, "cmd 2 not preserved\n");
+ ok( ac[n].key == 0x00ff, "key 2 not preserved\n");
+ ok( ac[n].fVirt == 0x0070, "fVirt 2 not changed\n");
+
+ n++;
+ ok( ac[n].cmd == 0xfff0, "cmd 3 not preserved\n");
+ ok( ac[n].key == 0x00ff, "key 3 not preserved\n");
+ ok( ac[n].fVirt == 0x0000, "fVirt 3 not changed\n");
+
+ n++;
+ ok( ac[n].cmd == 0xfff0, "cmd 4 not preserved\n");
+ ok( ac[n].key == 0xffff, "key 4 not preserved\n");
+ ok( ac[n].fVirt == 0x0001, "fVirt 4 not changed\n");
+
+ r = DestroyAcceleratorTable( hAccel );
+ ok( r, "destroy accelerator table\n");
+
+ hAccel = CreateAcceleratorTable( &ac[0], 0 );
+ ok( !hAccel, "zero elements should fail\n");
+
+ /* these will on crash win2k
+ hAccel = CreateAcceleratorTable( NULL, 1 );
+ hAccel = CreateAcceleratorTable( &ac[0], -1 );
+ */
+}
+
+/*
+ * memcmp on the tables works in Windows, but does not work in wine, as
+ * there is an extra undefined and unused byte between fVirt and the key
+ */
+static void test_accel2(void)
+{
+ ACCEL ac[2], out[2];
+ HACCEL hac;
+
+ ac[0].cmd = 0;
+ ac[0].fVirt = 0;
+ ac[0].key = 0;
+
+ ac[1].cmd = 0;
+ ac[1].fVirt = 0;
+ ac[1].key = 0;
+
+ /*
+ * crashes on win2k
+ * hac = CreateAcceleratorTable( NULL, 1 );
+ */
+
+ /* try a zero count */
+ hac = CreateAcceleratorTable( &ac[0], 0 );
+ ok( !hac , "fail\n");
+ ok( !DestroyAcceleratorTable( hac ), "destroy failed\n");
+
+ /* creating one accelerator should work */
+ hac = CreateAcceleratorTable( &ac[0], 1 );
+ ok( hac != NULL , "fail\n");
+ ok( 1 == CopyAcceleratorTable( hac, out, 1 ), "copy failed\n");
+ ok( DestroyAcceleratorTable( hac ), "destroy failed\n");
+
+ /* how about two of the same type? */
+ hac = CreateAcceleratorTable( &ac[0], 2);
+ ok( hac != NULL , "fail\n");
+ ok( 2 == CopyAcceleratorTable( hac, NULL, 100 ), "copy null failed\n");
+ ok( 2 == CopyAcceleratorTable( hac, NULL, 0 ), "copy null failed\n");
+ ok( 2 == CopyAcceleratorTable( hac, NULL, 1 ), "copy null failed\n");
+ ok( 1 == CopyAcceleratorTable( hac, out, 1 ), "copy 1 failed\n");
+ ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n");
+ ok( DestroyAcceleratorTable( hac ), "destroy failed\n");
+ /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */
+
+ /* how about two of the same type with a non-zero key? */
+ ac[0].key = 0x20;
+ ac[1].key = 0x20;
+ hac = CreateAcceleratorTable( &ac[0], 2);
+ ok( hac != NULL , "fail\n");
+ ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n");
+ ok( DestroyAcceleratorTable( hac ), "destroy failed\n");
+ /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */
+
+ /* how about two of the same type with a non-zero virtual key? */
+ ac[0].fVirt = FVIRTKEY;
+ ac[0].key = 0x40;
+ ac[1].fVirt = FVIRTKEY;
+ ac[1].key = 0x40;
+ hac = CreateAcceleratorTable( &ac[0], 2);
+ ok( hac != NULL , "fail\n");
+ ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n");
+ /* ok( !memcmp( ac, out, sizeof ac ), "tables different\n"); */
+ ok( DestroyAcceleratorTable( hac ), "destroy failed\n");
+
+ /* how virtual key codes */
+ ac[0].fVirt = FVIRTKEY;
+ hac = CreateAcceleratorTable( &ac[0], 1);
+ ok( hac != NULL , "fail\n");
+ ok( 1 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n");
+ /* ok( !memcmp( ac, out, sizeof ac/2 ), "tables different\n"); */
+ ok( DestroyAcceleratorTable( hac ), "destroy failed\n");
+
+ /* how turning on all bits? */
+ ac[0].cmd = 0xffff;
+ ac[0].fVirt = 0xff;
+ ac[0].key = 0xffff;
+ hac = CreateAcceleratorTable( &ac[0], 1);
+ ok( hac != NULL , "fail\n");
+ ok( 1 == CopyAcceleratorTable( hac, out, 1 ), "copy 1 failed\n");
+ /* ok( memcmp( ac, out, sizeof ac/2 ), "tables not different\n"); */
+ ok( out[0].cmd == ac[0].cmd, "cmd modified\n");
+ ok( out[0].fVirt == (ac[0].fVirt&0x7f), "fVirt not modified\n");
+ ok( out[0].key == ac[0].key, "key modified\n");
+ ok( DestroyAcceleratorTable( hac ), "destroy failed\n");
+
+ /* how turning on all bits? */
+ memset( ac, 0xff, sizeof ac );
+ hac = CreateAcceleratorTable( &ac[0], 2);
+ ok( hac != NULL , "fail\n");
+ ok( 2 == CopyAcceleratorTable( hac, out, 2 ), "copy 2 failed\n");
+ /* ok( memcmp( ac, out, sizeof ac ), "tables not different\n"); */
+ ok( out[0].cmd == ac[0].cmd, "cmd modified\n");
+ ok( out[0].fVirt == (ac[0].fVirt&0x7f), "fVirt not modified\n");
+ ok( out[0].key == ac[0].key, "key modified\n");
+ ok( out[1].cmd == ac[1].cmd, "cmd modified\n");
+ ok( out[1].fVirt == (ac[1].fVirt&0x7f), "fVirt not modified\n");
+ ok( out[1].key == ac[1].key, "key modified\n");
+ ok( DestroyAcceleratorTable( hac ), "destroy failed\n");
+}
+
+static void test_PrivateExtractIcons(void) {
+ CONST CHAR szShell32Dll[] = "shell32.dll";
+ HICON ahIcon[256];
+ UINT aIconId[256];
+ UINT cIcons, cIcons2;
+
+ if (!pPrivateExtractIconsA) return;
+
+ cIcons = pPrivateExtractIconsA(szShell32Dll, 0, 16, 16, NULL, NULL, 0, 0);
+ cIcons2 = pPrivateExtractIconsA(szShell32Dll, 4, MAKELONG(32,16), MAKELONG(32,16),
+ NULL, NULL, 256, 0);
+ ok((cIcons == cIcons2) && (cIcons > 0),
+ "Icon count should be independent of requested icon sizes and base icon index! "
+ "(cIcons=%d, cIcons2=%d)\n", cIcons, cIcons2);
+
+ cIcons = pPrivateExtractIconsA(szShell32Dll, 0, 16, 16, ahIcon, aIconId, 0, 0);
+ ok(cIcons == 0, "Zero icons requested, got cIcons=%d\n", cIcons);
+
+ cIcons = pPrivateExtractIconsA(szShell32Dll, 0, 16, 16, ahIcon, aIconId, 3, 0);
+ ok(cIcons == 3, "Three icons requested got cIcons=%d\n", cIcons);
+
+ cIcons = pPrivateExtractIconsA(szShell32Dll, 0, MAKELONG(32,16), MAKELONG(32,16),
+ ahIcon, aIconId, 3, 0);
+ ok(cIcons == 4, "Three icons requested, four expected, got cIcons=%d\n", cIcons);
+}
+
+static void test_LoadImage(void) {
+ HBITMAP bmp;
+
+ bmp = LoadBitmapA(NULL, MAKEINTRESOURCE(OBM_CHECK));
+ ok(bmp != NULL, "Could not load the OBM_CHECK bitmap\n");
+ if (bmp) DeleteObject(bmp);
+
+ bmp = LoadBitmapA(NULL, "#32760"); /* Value of OBM_CHECK */
+ ok(bmp != NULL, "Could not load the OBM_CHECK bitmap\n");
+ if (bmp) DeleteObject(bmp);
+}
+
+START_TEST(resource)
+{
+ init_function_pointers();
+ test_LoadStringA ();
+ test_accel1();
+ test_accel2();
+ test_PrivateExtractIcons();
+ test_LoadImage();
+}
--- /dev/null
+/* Unit test suite for resources.
+ *
+ * Copyright 2004 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "windef.h"
+#include "winuser.h"
+
+1 ACCELERATORS
+{
+ "^N", 1000 /* Ctrl+'N' */
+ "N", 1001 /* Shift+'n' */
+ "n", 1002 /* 'n' */
+}
+
+2 ACCELERATORS
+{
+ 78, 1000, VIRTKEY, CONTROL /* Ctrl+'N' */
+ 78, 1001, ASCII /* 'N' */
+ 110, 1002, ASCII /* 'n' */
+ 78, 1003, VIRTKEY, ALT /* Alt+'N' */
+ 78, 1004, VIRTKEY, CONTROL, SHIFT /* Ctrl+Shift+'N' */
+ 78, 1005, VIRTKEY, CONTROL, ALT, SHIFT /* Ctrl+Alt+Shift+'N' */
+}
+
+STRINGTABLE
+{
+ 0 "String resource"
+}
+
+TEST_DIALOG DIALOG DISCARDABLE 0, 0, 60, 30
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
+CAPTION "Test dialog"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK", IDOK,4,4,50,14, WS_TABSTOP | WS_GROUP
+}
+
+RADIO_TEST_DIALOG DIALOGEX 0, 0, 160, 80
+STYLE DS_SETFONT | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
+CAPTION "Radio Button Test Dialog"
+FONT 8, "MS Shell Dlg"
+{
+ GROUPBOX "Static", 100,6,5,92,70
+ CONTROL "Radio1", 200,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,17,27,39,10
+ CONTROL "Radio2", 201,"Button",BS_AUTORADIOBUTTON,17,40,39,10
+ PUSHBUTTON "Cancel", IDCANCEL,109,20,50,14, WS_TABSTOP | WS_GROUP
+}
+
+CLASS_TEST_DIALOG DIALOG DISCARDABLE 0, 0, 91, 28
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "CreateDialogParams Test"
+CLASS "TestDialog"
+FONT 8, "MS Shell Dlg"
+{
+}
+
+FOCUS_TEST_DIALOG DIALOG DISCARDABLE 0, 0, 60, 30
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_CONTROL
+CAPTION "Test dialog"
+FONT 8, "MS Shell Dlg"
+{
+ EDITTEXT 200,4,4,50,14
+}
--- /dev/null
+/* Unit test suite for functions SystemParametersInfo and GetSystemMetrics.
+ *
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500 /* For SPI_GETMOUSEHOVERWIDTH and more */
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "winnls.h"
+
+#ifndef SPI_GETDESKWALLPAPER
+# define SPI_GETDESKWALLPAPER 0x0073
+#endif
+
+static int strict;
+static int dpi;
+static int iswin9x;
+static HDC hdc;
+
+#define eq(received, expected, label, type) \
+ ok((received) == (expected), "%s: got " type " instead of " type "\n", (label),(received),(expected))
+
+
+#define SPI_SETBEEP_REGKEY "Control Panel\\Sound"
+#define SPI_SETBEEP_VALNAME "Beep"
+#define SPI_SETMOUSE_REGKEY "Control Panel\\Mouse"
+#define SPI_SETMOUSE_VALNAME1 "MouseThreshold1"
+#define SPI_SETMOUSE_VALNAME2 "MouseThreshold2"
+#define SPI_SETMOUSE_VALNAME3 "MouseSpeed"
+#define SPI_SETBORDER_REGKEY "Control Panel\\Desktop\\WindowMetrics"
+#define SPI_SETBORDER_REGKEY2 "Control Panel\\Desktop"
+#define SPI_SETBORDER_VALNAME "BorderWidth"
+#define SPI_METRIC_REGKEY "Control Panel\\Desktop\\WindowMetrics"
+#define SPI_SCROLLWIDTH_VALNAME "ScrollWidth"
+#define SPI_SCROLLHEIGHT_VALNAME "ScrollHeight"
+#define SPI_CAPTIONWIDTH_VALNAME "CaptionWidth"
+#define SPI_CAPTIONHEIGHT_VALNAME "CaptionHeight"
+#define SPI_CAPTIONFONT_VALNAME "CaptionFont"
+#define SPI_SMCAPTIONWIDTH_VALNAME "SmCaptionWidth"
+#define SPI_SMCAPTIONHEIGHT_VALNAME "SmCaptionHeight"
+#define SPI_SMCAPTIONFONT_VALNAME "SmCaptionFont"
+#define SPI_MENUWIDTH_VALNAME "MenuWidth"
+#define SPI_MENUHEIGHT_VALNAME "MenuHeight"
+#define SPI_MENUFONT_VALNAME "MenuFont"
+#define SPI_STATUSFONT_VALNAME "StatusFont"
+#define SPI_MESSAGEFONT_VALNAME "MessageFont"
+
+#define SPI_SETKEYBOARDSPEED_REGKEY "Control Panel\\Keyboard"
+#define SPI_SETKEYBOARDSPEED_VALNAME "KeyboardSpeed"
+#define SPI_ICONHORIZONTALSPACING_REGKEY "Control Panel\\Desktop\\WindowMetrics"
+#define SPI_ICONHORIZONTALSPACING_REGKEY2 "Control Panel\\Desktop"
+#define SPI_ICONHORIZONTALSPACING_VALNAME "IconSpacing"
+#define SPI_ICONVERTICALSPACING_REGKEY "Control Panel\\Desktop\\WindowMetrics"
+#define SPI_ICONVERTICALSPACING_REGKEY2 "Control Panel\\Desktop"
+#define SPI_ICONVERTICALSPACING_VALNAME "IconVerticalSpacing"
+#define SPI_MINIMIZEDMETRICS_REGKEY "Control Panel\\Desktop\\WindowMetrics"
+#define SPI_MINWIDTH_VALNAME "MinWidth"
+#define SPI_MINHORZGAP_VALNAME "MinHorzGap"
+#define SPI_MINVERTGAP_VALNAME "MinVertGap"
+#define SPI_MINARRANGE_VALNAME "MinArrange"
+#define SPI_SETSCREENSAVETIMEOUT_REGKEY "Control Panel\\Desktop"
+#define SPI_SETSCREENSAVETIMEOUT_VALNAME "ScreenSaveTimeOut"
+#define SPI_SETSCREENSAVEACTIVE_REGKEY "Control Panel\\Desktop"
+#define SPI_SETSCREENSAVEACTIVE_VALNAME "ScreenSaveActive"
+#define SPI_SETGRIDGRANULARITY_REGKEY "Control Panel\\Desktop"
+#define SPI_SETGRIDGRANULARITY_VALNAME "GridGranularity"
+#define SPI_SETKEYBOARDDELAY_REGKEY "Control Panel\\Keyboard"
+#define SPI_SETKEYBOARDDELAY_VALNAME "KeyboardDelay"
+#define SPI_SETICONTITLEWRAP_REGKEY1 "Control Panel\\Desktop\\WindowMetrics"
+#define SPI_SETICONTITLEWRAP_REGKEY2 "Control Panel\\Desktop"
+#define SPI_SETICONTITLEWRAP_VALNAME "IconTitleWrap"
+#define SPI_SETMENUDROPALIGNMENT_REGKEY1 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows"
+#define SPI_SETMENUDROPALIGNMENT_REGKEY2 "Control Panel\\Desktop"
+#define SPI_SETMENUDROPALIGNMENT_VALNAME "MenuDropAlignment"
+#define SPI_SETDOUBLECLKWIDTH_REGKEY1 "Control Panel\\Mouse"
+#define SPI_SETDOUBLECLKWIDTH_REGKEY2 "Control Panel\\Desktop"
+#define SPI_SETDOUBLECLKWIDTH_VALNAME "DoubleClickWidth"
+#define SPI_SETDOUBLECLKHEIGHT_REGKEY1 "Control Panel\\Mouse"
+#define SPI_SETDOUBLECLKHEIGHT_REGKEY2 "Control Panel\\Desktop"
+#define SPI_SETDOUBLECLKHEIGHT_VALNAME "DoubleClickHeight"
+#define SPI_SETDOUBLECLICKTIME_REGKEY "Control Panel\\Mouse"
+#define SPI_SETDOUBLECLICKTIME_VALNAME "DoubleClickSpeed"
+#define SPI_SETMOUSEBUTTONSWAP_REGKEY "Control Panel\\Mouse"
+#define SPI_SETMOUSEBUTTONSWAP_VALNAME "SwapMouseButtons"
+#define SPI_SETWORKAREA_REGKEY "Control Panel\\Desktop"
+#define SPI_SETWORKAREA_VALNAME "WINE_WorkArea"
+#define SPI_SETSHOWSOUNDS_REGKEY "Control Panel\\Accessibility\\ShowSounds"
+#define SPI_SETSHOWSOUNDS_VALNAME "On"
+#define SPI_SETKEYBOARDPREF_REGKEY "Control Panel\\Accessibility\\Keyboard Preference"
+#define SPI_SETKEYBOARDPREF_VALNAME "On"
+#define SPI_SETKEYBOARDPREF_REGKEY_LEGACY "Control Panel\\Accessibility"
+#define SPI_SETKEYBOARDPREF_VALNAME_LEGACY "Keyboard Preference"
+#define SPI_SETSCREENREADER_REGKEY "Control Panel\\Accessibility\\Blind Access"
+#define SPI_SETSCREENREADER_VALNAME "On"
+#define SPI_SETSCREENREADER_REGKEY_LEGACY "Control Panel\\Accessibility"
+#define SPI_SETSCREENREADER_VALNAME_LEGACY "Blind Access"
+#define SPI_SETFONTSMOOTHING_REGKEY "Control Panel\\Desktop"
+#define SPI_SETFONTSMOOTHING_VALNAME "FontSmoothing"
+#define SPI_SETLOWPOWERACTIVE_REGKEY "Control Panel\\Desktop"
+#define SPI_SETLOWPOWERACTIVE_VALNAME "LowPowerActive"
+#define SPI_SETPOWEROFFACTIVE_REGKEY "Control Panel\\Desktop"
+#define SPI_SETPOWEROFFACTIVE_VALNAME "PowerOffActive"
+#define SPI_SETDRAGFULLWINDOWS_REGKEY "Control Panel\\Desktop"
+#define SPI_SETDRAGFULLWINDOWS_VALNAME "DragFullWindows"
+#define SPI_SETMOUSEHOVERWIDTH_REGKEY "Control Panel\\Mouse"
+#define SPI_SETMOUSEHOVERWIDTH_VALNAME "MouseHoverWidth"
+#define SPI_SETMOUSEHOVERHEIGHT_REGKEY "Control Panel\\Mouse"
+#define SPI_SETMOUSEHOVERHEIGHT_VALNAME "MouseHoverHeight"
+#define SPI_SETMOUSEHOVERTIME_REGKEY "Control Panel\\Mouse"
+#define SPI_SETMOUSEHOVERTIME_VALNAME "MouseHoverTime"
+#define SPI_SETMOUSESCROLLLINES_REGKEY "Control Panel\\Desktop"
+#define SPI_SETMOUSESCROLLLINES_VALNAME "WheelScrollLines"
+#define SPI_SETMENUSHOWDELAY_REGKEY "Control Panel\\Desktop"
+#define SPI_SETMENUSHOWDELAY_VALNAME "MenuShowDelay"
+#define SPI_SETDESKWALLPAPER_REGKEY "Control Panel\\Desktop"
+#define SPI_SETDESKWALLPAPER_VALNAME "Wallpaper"
+
+/* volatile registry branch under CURRENT_USER_REGKEY for temporary values storage */
+#define WINE_CURRENT_USER_REGKEY "Wine"
+
+static HWND ghTestWnd;
+
+static DWORD WINAPI SysParamsThreadFunc( LPVOID lpParam );
+static LRESULT CALLBACK SysParamsTestWndProc( HWND hWnd, UINT msg, WPARAM wParam,
+ LPARAM lParam );
+static int change_counter;
+static int change_last_param;
+
+static LRESULT CALLBACK SysParamsTestWndProc( HWND hWnd, UINT msg, WPARAM wParam,
+ LPARAM lParam )
+{
+ switch (msg) {
+
+ case WM_SETTINGCHANGE:
+ if (change_counter>0) {
+ /* ignore these messages caused by resizing of toolbars */
+ if( wParam == SPI_SETWORKAREA) break;
+ if( change_last_param == SPI_SETWORKAREA) {
+ change_last_param = wParam;
+ break;
+ }
+ ok(0,"too many changes counter=%d last change=%d\n",
+ change_counter,change_last_param);
+ }
+ change_counter++;
+ change_last_param = wParam;
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage( 0 );
+ break;
+
+ default:
+ return( DefWindowProcA( hWnd, msg, wParam, lParam ) );
+ }
+
+ return 0;
+}
+
+/*
+Performs testing for system parameters messages
+params:
+ - system parameter id
+ - supposed value of the registry key
+*/
+static void test_change_message( int action, int optional )
+{
+ if (change_counter==0 && optional==1)
+ return;
+ ok( 1 == change_counter,
+ "Missed a message: change_counter=%d\n", change_counter );
+ change_counter = 0;
+ ok( action == change_last_param,
+ "Wrong action got %d expected %d\n", change_last_param, action );
+ change_last_param = 0;
+}
+
+static BOOL test_error_msg ( int rc, const char *name )
+{
+ DWORD last_error = GetLastError();
+
+ if (rc==0)
+ {
+ if (last_error==0xdeadbeef || last_error==ERROR_INVALID_SPI_VALUE)
+ {
+ trace("%s not supported on this platform. Skipping test\n", name);
+ }
+ else if (last_error==ERROR_ACCESS_DENIED)
+ {
+ trace("%s does not have privileges to run. Skipping test\n", name);
+ }
+ else
+ {
+ trace("%s failed for reason: %ld. Indicating test failure and skipping remainder of test\n",name,last_error);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,last_error);
+ }
+ return FALSE;
+ }
+ else
+ {
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,last_error);
+ return TRUE;
+ }
+}
+
+/*
+ * Tests the HKEY_CURRENT_USER subkey value.
+ * The value should contain string value.
+ *
+ * Params:
+ * lpsSubKey - subkey name
+ * lpsRegName - registry entry name
+ * lpsTestValue - value to test
+ */
+static void _test_reg_key( LPCSTR subKey1, LPCSTR subKey2, LPCSTR valName1, LPCSTR valName2, LPCSTR testValue )
+{
+ CHAR value[MAX_PATH];
+ DWORD valueLen;
+ DWORD type;
+ HKEY hKey;
+ LONG rc;
+ int found=0;
+
+ *value='\0';
+ valueLen=sizeof(value);
+ RegOpenKeyA( HKEY_CURRENT_USER, subKey1, &hKey );
+ rc=RegQueryValueExA( hKey, valName1, NULL, &type, (LPBYTE)value, &valueLen );
+ RegCloseKey( hKey );
+ if (rc==ERROR_SUCCESS)
+ {
+ ok( !strcmp( testValue, value ),
+ "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n",
+ subKey1, valName1, testValue, value );
+ found++;
+ }
+ else if (strict)
+ {
+ ok(0,"Missing registry entry: subKey=%s, valName=%s\n",
+ subKey1, valName1);
+ }
+ if (valName2)
+ {
+ *value='\0';
+ valueLen=sizeof(value);
+ RegOpenKeyA( HKEY_CURRENT_USER, subKey1, &hKey );
+ rc=RegQueryValueExA( hKey, valName2, NULL, &type, (LPBYTE)value, &valueLen );
+ RegCloseKey( hKey );
+ if (rc==ERROR_SUCCESS)
+ {
+ ok( !strcmp( testValue, value ),
+ "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n",
+ subKey1, valName2, testValue, value );
+ found++;
+ }
+ else if (strict)
+ {
+ ok( 0,"Missing registry entry: subKey=%s, valName=%s\n",
+ subKey1, valName2 );
+ }
+ }
+ if (subKey2 && !strict)
+ {
+ *value='\0';
+ valueLen=sizeof(value);
+ RegOpenKeyA( HKEY_CURRENT_USER, subKey2, &hKey );
+ rc=RegQueryValueExA( hKey, valName1, NULL, &type, (LPBYTE)value, &valueLen );
+ RegCloseKey( hKey );
+ if (rc==ERROR_SUCCESS)
+ {
+ ok( !strcmp( testValue, value ),
+ "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n",
+ subKey2, valName1, testValue, value );
+ found++;
+ }
+ else if (strict)
+ {
+ ok( 0,"Missing registry entry: subKey=%s, valName=%s\n",
+ subKey2, valName1 );
+ }
+ if (valName2)
+ {
+ *value='\0';
+ valueLen=sizeof(value);
+ RegOpenKeyA( HKEY_CURRENT_USER, subKey2, &hKey );
+ rc=RegQueryValueExA( hKey, valName2, NULL, &type, (LPBYTE)value, &valueLen );
+ RegCloseKey( hKey );
+ if (rc==ERROR_SUCCESS)
+ {
+ ok( !strcmp( testValue, value ),
+ "Wrong value in registry: subKey=%s, valName=%s, testValue=%s, value=%s\n",
+ subKey2, valName2, testValue, value );
+ found++;
+ }
+ else if (strict)
+ {
+ ok( 0,"Missing registry entry: subKey=%s, valName=%s\n",
+ subKey2, valName2 );
+ }
+ }
+ }
+ ok(found,"Missing registry values: %s or %s in keys: %s or %s\n",
+ valName1, (valName2?valName2:"<n/a>"), subKey1, (subKey2?subKey2:"<n/a>") );
+}
+
+#define test_reg_key( subKey, valName, testValue ) \
+ _test_reg_key( subKey, NULL, valName, NULL, testValue )
+#define test_reg_key_ex( subKey1, subKey2, valName, testValue ) \
+ _test_reg_key( subKey1, subKey2, valName, NULL, testValue )
+#define test_reg_key_ex2( subKey1, subKey2, valName1, valName2, testValue ) \
+ _test_reg_key( subKey1, subKey2, valName1, valName2, testValue )
+
+/* get a metric from the registry. If the value is negative
+ * it is assumed to be in twips and converted to pixels */
+static UINT metricfromreg( char *keyname, char *valname, int dpi)
+{
+ HKEY hkey;
+ char buf[64];
+ DWORD ret;
+ DWORD size, type;
+ int value;
+
+ RegOpenKeyA( HKEY_CURRENT_USER, keyname, &hkey );
+ size = sizeof(buf);
+ ret=RegQueryValueExA( hkey, valname, NULL, &type, (LPBYTE)buf, &size );
+ RegCloseKey( hkey );
+ if( ret != ERROR_SUCCESS) return -1;
+ value = atoi( buf);
+ if( value < 0)
+ value = ( -value * dpi + 720) / 1440;
+ return value;
+}
+
+typedef struct
+{
+ INT16 lfHeight;
+ INT16 lfWidth;
+ INT16 lfEscapement;
+ INT16 lfOrientation;
+ INT16 lfWeight;
+ BYTE lfItalic;
+ BYTE lfUnderline;
+ BYTE lfStrikeOut;
+ BYTE lfCharSet;
+ BYTE lfOutPrecision;
+ BYTE lfClipPrecision;
+ BYTE lfQuality;
+ BYTE lfPitchAndFamily;
+ CHAR lfFaceName[LF_FACESIZE];
+} LOGFONT16, *LPLOGFONT16;
+
+/* get logfont from the registry */
+static int lffromreg( char *keyname, char *valname, LOGFONTA *plf)
+{
+ HKEY hkey;
+ LOGFONTW lfw;
+ DWORD ret, size, type;
+
+ RegOpenKeyA( HKEY_CURRENT_USER, keyname, &hkey );
+ size = sizeof( lfw);
+ ret=RegQueryValueExA( hkey, valname, NULL, &type, (LPBYTE)&lfw, &size );
+ RegCloseKey( hkey );
+ ok( ret == ERROR_SUCCESS, "Key \"%s\" value \"%s\" not found\n", keyname, valname);
+ if( ret != ERROR_SUCCESS)
+ return FALSE;
+ if( size <= sizeof( LOGFONT16)) {
+ LOGFONT16 *plf16 = (LOGFONT16*) &lfw;
+ plf->lfHeight = plf16->lfHeight;
+ plf->lfWidth = plf16->lfWidth;
+ plf->lfEscapement = plf16->lfEscapement;
+ plf->lfOrientation = plf16->lfOrientation;
+ plf->lfWeight = plf16->lfWeight;
+ plf->lfItalic = plf16->lfItalic;
+ plf->lfUnderline = plf16->lfUnderline;
+ plf->lfStrikeOut = plf16->lfStrikeOut;
+ plf->lfCharSet = plf16->lfCharSet;
+ plf->lfOutPrecision = plf16->lfOutPrecision;
+ plf->lfClipPrecision = plf16->lfClipPrecision;
+ plf->lfQuality = plf16->lfQuality;
+ plf->lfPitchAndFamily = plf16->lfPitchAndFamily;
+ memcpy( plf->lfFaceName, plf16->lfFaceName, LF_FACESIZE );
+ } else if( size <= sizeof( LOGFONTA)) {
+ plf = (LOGFONTA*) &lfw;
+ } else {
+ plf->lfHeight = lfw.lfHeight;
+ plf->lfWidth = lfw.lfWidth;
+ plf->lfEscapement = lfw.lfEscapement;
+ plf->lfOrientation = lfw.lfOrientation;
+ plf->lfWeight = lfw.lfWeight;
+ plf->lfItalic = lfw.lfItalic;
+ plf->lfUnderline = lfw.lfUnderline;
+ plf->lfStrikeOut = lfw.lfStrikeOut;
+ plf->lfCharSet = lfw.lfCharSet;
+ plf->lfOutPrecision = lfw.lfOutPrecision;
+ plf->lfClipPrecision = lfw.lfClipPrecision;
+ plf->lfQuality = lfw.lfQuality;
+ plf->lfPitchAndFamily = lfw.lfPitchAndFamily;
+ WideCharToMultiByte( CP_ACP, 0, lfw.lfFaceName, -1, plf->lfFaceName,
+ LF_FACESIZE, NULL, NULL);
+
+ }
+ return TRUE;
+}
+
+static void test_SPI_SETBEEP( void ) /* 2 */
+{
+ BOOL rc;
+ BOOL old_b;
+ BOOL b;
+ BOOL curr_val;
+
+ trace("testing SPI_{GET,SET}BEEP\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETBEEP, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}BEEP"))
+ return;
+
+ curr_val = TRUE;
+ rc=SystemParametersInfoA( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETBEEP, 0 );
+ test_reg_key( SPI_SETBEEP_REGKEY,
+ SPI_SETBEEP_VALNAME,
+ curr_val ? "Yes" : "No" );
+ rc=SystemParametersInfoA( SPI_GETBEEP, 0, &b, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( b, curr_val, "SPI_{GET,SET}BEEP", "%d" );
+ rc=SystemParametersInfoW( SPI_GETBEEP, 0, &b, 0 );
+ if (rc!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ ok(rc!=0,"SystemParametersInfoW: rc=%d err=%ld\n",rc,GetLastError());
+ eq( b, curr_val, "SystemParametersInfoW", "%d" );
+ }
+
+ /* is a message sent for the second change? */
+ rc=SystemParametersInfoA( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETBEEP, 0 );
+
+ curr_val = FALSE;
+ rc=SystemParametersInfoW( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ rc=SystemParametersInfoA( SPI_SETBEEP, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"SystemParametersInfo: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETBEEP, 0 );
+ test_reg_key( SPI_SETBEEP_REGKEY,
+ SPI_SETBEEP_VALNAME,
+ curr_val ? "Yes" : "No" );
+ rc=SystemParametersInfoA( SPI_GETBEEP, 0, &b, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( b, curr_val, "SPI_{GET,SET}BEEP", "%d" );
+ rc=SystemParametersInfoW( SPI_GETBEEP, 0, &b, 0 );
+ if (rc!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ ok(rc!=0,"SystemParametersInfoW: rc=%d err=%ld\n",rc,GetLastError());
+ eq( b, curr_val, "SystemParametersInfoW", "%d" );
+ }
+ ok( MessageBeep( MB_OK ), "Return value of MessageBeep when sound is disabled\n" );
+
+ rc=SystemParametersInfoA( SPI_SETBEEP, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static const char *setmouse_valuenames[3] = {
+ SPI_SETMOUSE_VALNAME1,
+ SPI_SETMOUSE_VALNAME2,
+ SPI_SETMOUSE_VALNAME3
+};
+
+/*
+ * Runs check for one setting of spi_setmouse.
+ */
+static void run_spi_setmouse_test( int curr_val[], POINT *req_change, POINT *proj_change,
+ int nchange )
+{
+ BOOL rc;
+ INT mi[3];
+ static int aw_turn = 0;
+ static BOOL w_implemented = 1;
+
+ char buf[20];
+ int i;
+
+ aw_turn++;
+ rc=0;
+ if ((aw_turn % 2!=0) && (w_implemented))
+ {
+ /* call unicode on odd (non even) calls */
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoW( SPI_SETMOUSE, 0, curr_val, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ w_implemented = 0;
+ trace("SystemParametersInfoW not supported on this platform\n");
+ }
+ }
+
+ if ((aw_turn % 2==0) || (!w_implemented))
+ {
+ /* call ascii version on even calls or if unicode is not available */
+ rc=SystemParametersInfoA( SPI_SETMOUSE, 0, curr_val, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ }
+
+ ok(rc!=0,"SystemParametersInfo: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETMOUSE, 0 );
+ for (i = 0; i < 3; i++)
+ {
+ sprintf( buf, "%d", curr_val[i] );
+ test_reg_key( SPI_SETMOUSE_REGKEY, setmouse_valuenames[i], buf );
+ }
+
+ rc=SystemParametersInfoA( SPI_GETMOUSE, 0, mi, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ for (i = 0; i < 3; i++)
+ {
+ ok(mi[i] == curr_val[i],
+ "incorrect value for %d: %d != %d\n", i, mi[i], curr_val[i]);
+ }
+
+ if (w_implemented)
+ {
+ rc=SystemParametersInfoW( SPI_GETMOUSE, 0, mi, 0 );
+ ok(rc!=0,"SystemParametersInfoW: rc=%d err=%ld\n",rc,GetLastError());
+ for (i = 0; i < 3; i++)
+ {
+ ok(mi[i] == curr_val[i],
+ "incorrect value for %d: %d != %d\n", i, mi[i], curr_val[i]);
+ }
+ }
+
+#if 0 /* FIXME: this always fails for me - AJ */
+ for (i = 0; i < nchange; i++)
+ {
+ POINT mv;
+ mouse_event( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, 0, 0, 0, 0 );
+ mouse_event( MOUSEEVENTF_MOVE, req_change[i].x, req_change[i].y, 0, 0 );
+ GetCursorPos( &mv );
+ ok( proj_change[i].x == mv.x, "Projected dx and real dx comparison. May fail under high load.\n" );
+ ok( proj_change[i].y == mv.y, "Projected dy equals real dy. May fail under high load.\n" );
+ }
+#endif
+}
+
+static void test_SPI_SETMOUSE( void ) /* 4 */
+{
+ BOOL rc;
+ INT old_mi[3];
+
+ /* win nt default values - 6, 10, 1 */
+ INT curr_val[3] = {6, 10, 1};
+
+ /* requested and projected mouse movements */
+ POINT req_change[] = { {6, 6}, { 7, 6}, { 8, 6}, {10, 10}, {11, 10}, {100, 100} };
+ POINT proj_change1[] = { {6, 6}, {14, 6}, {16, 6}, {20, 20}, {22, 20}, {200, 200} };
+ POINT proj_change2[] = { {6, 6}, {14, 6}, {16, 6}, {20, 20}, {44, 20}, {400, 400} };
+ POINT proj_change3[] = { {6, 6}, {14, 6}, {16, 6}, {20, 20}, {22, 20}, {200, 200} };
+ POINT proj_change4[] = { {6, 6}, { 7, 6}, { 8, 6}, {10, 10}, {11, 10}, {100, 100} };
+ POINT proj_change5[] = { {6, 6}, { 7, 6}, {16, 6}, {20, 20}, {22, 20}, {200, 200} };
+ POINT proj_change6[] = { {6, 6}, {28, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} };
+ POINT proj_change7[] = { {6, 6}, {14, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} };
+ POINT proj_change8[] = { {6, 6}, {28, 6}, {32, 6}, {40, 40}, {44, 40}, {400, 400} };
+
+ int nchange = sizeof( req_change ) / sizeof( POINT );
+
+ trace("testing SPI_{GET,SET}MOUSE\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETMOUSE, 0, old_mi, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}MOUSE"))
+ return;
+
+ run_spi_setmouse_test( curr_val, req_change, proj_change1, nchange );
+
+ /* acceleration change */
+ curr_val[2] = 2;
+ run_spi_setmouse_test( curr_val, req_change, proj_change2, nchange );
+
+ /* acceleration change */
+ curr_val[2] = 3;
+ run_spi_setmouse_test( curr_val, req_change, proj_change3, nchange );
+
+ /* acceleration change */
+ curr_val[2] = 0;
+ run_spi_setmouse_test( curr_val, req_change, proj_change4, nchange );
+
+ /* threshold change */
+ curr_val[2] = 1;
+ curr_val[0] = 7;
+ run_spi_setmouse_test( curr_val, req_change, proj_change5, nchange );
+
+ /* threshold change */
+ curr_val[2] = 2;
+ curr_val[0] = 6;
+ curr_val[1] = 6;
+ run_spi_setmouse_test( curr_val, req_change, proj_change6, nchange );
+
+ /* threshold change */
+ curr_val[1] = 7;
+ run_spi_setmouse_test( curr_val, req_change, proj_change7, nchange );
+
+ /* threshold change */
+ curr_val[1] = 5;
+ run_spi_setmouse_test( curr_val, req_change, proj_change8, nchange );
+
+ rc=SystemParametersInfoA( SPI_SETMOUSE, 0, old_mi, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_setborder(UINT curr_val, int usesetborder, int dpi)
+{
+ BOOL rc;
+ UINT border, regval;
+ INT frame;
+ NONCLIENTMETRICSA ncm;
+
+ ncm.cbSize = sizeof( ncm);
+ rc=SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ if( usesetborder) {
+ rc=SystemParametersInfoA( SPI_SETBORDER, curr_val, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETBORDER, 1 );
+ } else { /* set non client metrics */
+ ncm.iBorderWidth = curr_val;
+ rc=SystemParametersInfoA( SPI_SETNONCLIENTMETRICS, 0, &ncm, SPIF_UPDATEINIFILE|
+ SPIF_SENDCHANGE);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETNONCLIENTMETRICS, 1 );
+ }
+ if( curr_val) { /* skip if 0, some windows versions return 0 others 1 */
+ regval = metricfromreg( SPI_SETBORDER_REGKEY2, SPI_SETBORDER_VALNAME, dpi);
+ if( regval != curr_val)
+ regval = metricfromreg( SPI_SETBORDER_REGKEY, SPI_SETBORDER_VALNAME, dpi);
+ ok( regval==curr_val, "wrong value in registry %d, expected %d\n", regval, curr_val);
+ }
+ /* minimum border width is 1 */
+ if (curr_val == 0) curr_val = 1;
+ /* should be the same as the non client metrics */
+ rc=SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( (UINT)ncm.iBorderWidth, curr_val, "NonClientMetric.iBorderWidth", "%d");
+ /* and from SPI_GETBORDER */
+ rc=SystemParametersInfoA( SPI_GETBORDER, 0, &border, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( border, curr_val, "SPI_{GET,SET}BORDER", "%d");
+ /* test some SystemMetrics */
+ frame = curr_val + GetSystemMetrics( SM_CXDLGFRAME );
+ eq( frame, GetSystemMetrics( SM_CXFRAME ), "SM_CXFRAME", "%d" );
+ eq( frame, GetSystemMetrics( SM_CYFRAME ), "SM_CYFRAME", "%d" );
+ eq( frame, GetSystemMetrics( SM_CXSIZEFRAME ), "SM_CXSIZEFRAME", "%d" );
+ eq( frame, GetSystemMetrics( SM_CYSIZEFRAME ), "SM_CYSIZEFRAME", "%d" );
+}
+
+static void test_SPI_SETBORDER( void ) /* 6 */
+{
+ BOOL rc;
+ UINT old_border;
+ NONCLIENTMETRICSA ncmsave;
+ INT CaptionWidth;
+
+ ncmsave.cbSize = sizeof( ncmsave);
+ rc=SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncmsave, 0);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* CaptionWidth from the registry may have different value of iCaptionWidth
+ * from the non client metrics (observed on WinXP).
+ * Fix this so we can safely restore settings with the nonclientmetrics */
+ CaptionWidth = metricfromreg(
+ "Control Panel\\Desktop\\WindowMetrics","CaptionWidth", dpi);
+ ncmsave.iCaptionWidth = CaptionWidth;
+
+ /* These tests hang when XFree86 4.0 for Windows is running (tested on
+ * WinNT, SP2, Cygwin/XFree 4.1.0. Skip the test when XFree86 is
+ * running.
+ */
+ if (FindWindowA( NULL, "Cygwin/XFree86" ))
+ return;
+
+ trace("testing SPI_{GET,SET}BORDER\n");
+
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETBORDER, 0, &old_border, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}BORDER"))
+ return;
+ /* This will restore sane values if the test hang previous run. */
+ if ( old_border == 7 || old_border == 20 )
+ old_border = 1;
+
+ /* The SPI_SETBORDER seems to be buggy on Win9x/ME (looks like you need to
+ * do it twice to make the intended change). So skip parts of the tests on
+ * those platforms */
+ if( !iswin9x) {
+ test_setborder(1, 1, dpi);
+ test_setborder(0, 1, dpi);
+ test_setborder(2, 1, dpi);
+ }
+ test_setborder(1, 0, dpi);
+ test_setborder(0, 0, dpi);
+ test_setborder(3, 0, dpi);
+
+ rc=SystemParametersInfoA( SPI_SETNONCLIENTMETRICS, 0, &ncmsave,
+ SPIF_UPDATEINIFILE| SPIF_SENDCHANGE);
+ test_change_message( SPI_SETNONCLIENTMETRICS, 1 );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",
+ rc,GetLastError());
+}
+
+static void test_SPI_SETKEYBOARDSPEED( void ) /* 10 */
+{
+ BOOL rc;
+ UINT old_speed;
+ const UINT vals[]={0,20,31};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}KEYBOARDSPEED\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETKEYBOARDSPEED, 0, &old_speed, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}KEYBOARDSPEED"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETKEYBOARDSPEED, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETKEYBOARDSPEED, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETKEYBOARDSPEED_REGKEY, SPI_SETKEYBOARDSPEED_VALNAME, buf );
+
+ rc=SystemParametersInfoA( SPI_GETKEYBOARDSPEED, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}KEYBOARDSPEED", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETKEYBOARDSPEED, old_speed, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+/* test_SPI_ICONHORIZONTALSPACING helper */
+static void dotest_spi_iconhorizontalspacing( INT curr_val)
+{
+ BOOL rc;
+ INT spacing, regval;
+ ICONMETRICSA im;
+
+ rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, curr_val, 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_ICONHORIZONTALSPACING, 0 );
+ if( curr_val < 32) curr_val = 32;
+ /* The registry keys depend on the Windows version and the values too
+ * let's test (works on win95,ME,NT4,2k,XP)
+ */
+ regval = metricfromreg( SPI_ICONHORIZONTALSPACING_REGKEY2, SPI_ICONHORIZONTALSPACING_VALNAME, dpi);
+ if( regval != curr_val)
+ regval = metricfromreg( SPI_ICONHORIZONTALSPACING_REGKEY, SPI_ICONHORIZONTALSPACING_VALNAME, dpi);
+ ok( curr_val == regval,
+ "wrong value in registry %d, expected %d\n", regval, curr_val);
+ /* compare with what SPI_ICONHORIZONTALSPACING returns */
+ rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, 0, &spacing, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( spacing, curr_val, "ICONHORIZONTALSPACING", "%d");
+ /* and with a system metrics */
+ eq( GetSystemMetrics( SM_CXICONSPACING ), curr_val, "SM_CXICONSPACING", "%d" );
+ /* and with what SPI_GETICONMETRICS returns */
+ im.cbSize = sizeof(ICONMETRICSA);
+ rc=SystemParametersInfoA( SPI_GETICONMETRICS, sizeof(ICONMETRICSA), &im, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( im.iHorzSpacing, curr_val, "SPI_GETICONMETRICS", "%d" );
+}
+
+static void test_SPI_ICONHORIZONTALSPACING( void ) /* 13 */
+{
+ BOOL rc;
+ INT old_spacing;
+
+ trace("testing SPI_ICONHORIZONTALSPACING\n");
+ SetLastError(0xdeadbeef);
+ /* default value: 75 */
+ rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, 0, &old_spacing, 0 );
+ if (!test_error_msg(rc,"SPI_ICONHORIZONTALSPACING"))
+ return;
+ /* do not increase the value as it would upset the user's icon layout */
+ dotest_spi_iconhorizontalspacing( old_spacing - 1);
+ dotest_spi_iconhorizontalspacing( 10); /* minimum is 32 */
+ /* restore */
+ rc=SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, old_spacing, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETSCREENSAVETIMEOUT( void ) /* 14 */
+{
+ BOOL rc;
+ UINT old_timeout;
+ const UINT vals[]={0,32767};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}SCREENSAVETIMEOUT\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETSCREENSAVETIMEOUT, 0, &old_timeout, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}SCREENSAVETIMEOUT"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETSCREENSAVETIMEOUT, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETSCREENSAVETIMEOUT, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETSCREENSAVETIMEOUT_REGKEY,
+ SPI_SETSCREENSAVETIMEOUT_VALNAME, buf );
+
+ SystemParametersInfoA( SPI_GETSCREENSAVETIMEOUT, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}SCREENSAVETIMEOUT", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETSCREENSAVETIMEOUT, old_timeout, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETSCREENSAVEACTIVE( void ) /* 17 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}SCREENSAVEACTIVE\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETSCREENSAVEACTIVE, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}SCREENSAVEACTIVE"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+
+ rc=SystemParametersInfoA( SPI_SETSCREENSAVEACTIVE, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETSCREENSAVEACTIVE, 0 );
+ test_reg_key( SPI_SETSCREENSAVEACTIVE_REGKEY,
+ SPI_SETSCREENSAVEACTIVE_VALNAME,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETSCREENSAVEACTIVE, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}SCREENSAVEACTIVE", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETSCREENSAVEACTIVE, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETGRIDGRANULARITY( void ) /* 19 */
+{
+ /* ??? */;
+}
+
+static void test_SPI_SETKEYBOARDDELAY( void ) /* 23 */
+{
+ BOOL rc;
+ UINT old_delay;
+ const UINT vals[]={0,3};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}KEYBOARDDELAY\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETKEYBOARDDELAY, 0, &old_delay, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}KEYBOARDDELAY"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT delay;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETKEYBOARDDELAY, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETKEYBOARDDELAY, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETKEYBOARDDELAY_REGKEY,
+ SPI_SETKEYBOARDDELAY_VALNAME, buf );
+
+ rc=SystemParametersInfoA( SPI_GETKEYBOARDDELAY, 0, &delay, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( delay, vals[i], "SPI_{GET,SET}KEYBOARDDELAY", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETKEYBOARDDELAY, old_delay, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+
+/* test_SPI_ICONVERTICALSPACING helper */
+static void dotest_spi_iconverticalspacing( INT curr_val)
+{
+ BOOL rc;
+ INT spacing, regval;
+ ICONMETRICSA im;
+
+ rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, curr_val, 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_ICONVERTICALSPACING, 0 );
+ if( curr_val < 32) curr_val = 32;
+ /* The registry keys depend on the Windows version and the values too
+ * let's test (works on win95,ME,NT4,2k,XP)
+ */
+ regval = metricfromreg( SPI_ICONVERTICALSPACING_REGKEY2, SPI_ICONVERTICALSPACING_VALNAME, dpi);
+ if( regval != curr_val)
+ regval = metricfromreg( SPI_ICONVERTICALSPACING_REGKEY, SPI_ICONVERTICALSPACING_VALNAME, dpi);
+ ok( curr_val == regval,
+ "wrong value in registry %d, expected %d\n", regval, curr_val);
+ /* compare with what SPI_ICONVERTICALSPACING returns */
+ rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, 0, &spacing, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( spacing, curr_val, "ICONVERTICALSPACING", "%d" );
+ /* and with a system metrics */
+ eq( GetSystemMetrics( SM_CYICONSPACING ), curr_val, "SM_CYICONSPACING", "%d" );
+ /* and with what SPI_GETICONMETRICS returns */
+ im.cbSize = sizeof(ICONMETRICSA);
+ rc=SystemParametersInfoA( SPI_GETICONMETRICS, sizeof(ICONMETRICSA), &im, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( im.iVertSpacing, curr_val, "SPI_GETICONMETRICS", "%d" );
+}
+
+static void test_SPI_ICONVERTICALSPACING( void ) /* 24 */
+{
+ BOOL rc;
+ INT old_spacing;
+
+ trace("testing SPI_ICONVERTICALSPACING\n");
+ SetLastError(0xdeadbeef);
+ /* default value: 75 */
+ rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, 0, &old_spacing, 0 );
+ if (!test_error_msg(rc,"SPI_ICONVERTICALSPACING"))
+ return;
+ /* do not increase the value as it would upset the user's icon layout */
+ dotest_spi_iconverticalspacing( old_spacing - 1);
+ /* same tests with a value less than the minimum 32 */
+ dotest_spi_iconverticalspacing( 10);
+ /* restore */
+ rc=SystemParametersInfoA( SPI_ICONVERTICALSPACING, old_spacing, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",
+ rc,GetLastError());
+}
+
+static void test_SPI_SETICONTITLEWRAP( void ) /* 26 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+ ICONMETRICSA im;
+
+ /* These tests hang when XFree86 4.0 for Windows is running (tested on
+ * WinNT, SP2, Cygwin/XFree 4.1.0. Skip the test when XFree86 is
+ * running.
+ */
+ if (FindWindowA( NULL, "Cygwin/XFree86" ))
+ return;
+
+ trace("testing SPI_{GET,SET}ICONTITLEWRAP\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETICONTITLEWRAP, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}ICONTITLEWRAP"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ UINT regval;
+
+ rc=SystemParametersInfoA( SPI_SETICONTITLEWRAP, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETICONTITLEWRAP, 1 );
+ regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY2,
+ SPI_SETICONTITLEWRAP_VALNAME, dpi);
+ if( regval != vals[i])
+ regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY1,
+ SPI_SETICONTITLEWRAP_VALNAME, dpi);
+ ok( regval == vals[i],
+ "wrong value in registry %d, expected %d\n", regval, vals[i] );
+
+ rc=SystemParametersInfoA( SPI_GETICONTITLEWRAP, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}ICONTITLEWRAP", "%d" );
+ /* and test with what SPI_GETICONMETRICS returns */
+ im.cbSize = sizeof(ICONMETRICSA);
+ rc=SystemParametersInfoA( SPI_GETICONMETRICS, sizeof(ICONMETRICSA), &im, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( im.iTitleWrap, (BOOL)vals[i], "SPI_GETICONMETRICS", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETICONTITLEWRAP, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETMENUDROPALIGNMENT( void ) /* 28 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}MENUDROPALIGNMENT\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETMENUDROPALIGNMENT, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}MENUDROPALIGNMENT"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+
+ rc=SystemParametersInfoA( SPI_SETMENUDROPALIGNMENT, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETMENUDROPALIGNMENT, 0 );
+ test_reg_key_ex( SPI_SETMENUDROPALIGNMENT_REGKEY1,
+ SPI_SETMENUDROPALIGNMENT_REGKEY2,
+ SPI_SETMENUDROPALIGNMENT_VALNAME,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETMENUDROPALIGNMENT, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}MENUDROPALIGNMENT", "%d" );
+ eq( GetSystemMetrics( SM_MENUDROPALIGNMENT ), (int)vals[i],
+ "SM_MENUDROPALIGNMENT", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETMENUDROPALIGNMENT, old_b, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETDOUBLECLKWIDTH( void ) /* 29 */
+{
+ BOOL rc;
+ INT old_width;
+ const UINT vals[]={0,10000};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}DOUBLECLKWIDTH\n");
+ old_width = GetSystemMetrics( SM_CXDOUBLECLK );
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ char buf[10];
+
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_SETDOUBLECLKWIDTH, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ if (!test_error_msg(rc,"SPI_{GET,SET}DOUBLECLKWIDTH"))
+ return;
+
+ test_change_message( SPI_SETDOUBLECLKWIDTH, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key_ex( SPI_SETDOUBLECLKWIDTH_REGKEY1,
+ SPI_SETDOUBLECLKWIDTH_REGKEY2,
+ SPI_SETDOUBLECLKWIDTH_VALNAME, buf );
+ eq( GetSystemMetrics( SM_CXDOUBLECLK ), (int)vals[i],
+ "SM_CXDOUBLECLK", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETDOUBLECLKWIDTH, old_width, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETDOUBLECLKHEIGHT( void ) /* 30 */
+{
+ BOOL rc;
+ INT old_height;
+ const UINT vals[]={0,10000};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}DOUBLECLKHEIGHT\n");
+ old_height = GetSystemMetrics( SM_CYDOUBLECLK );
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ char buf[10];
+
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_SETDOUBLECLKHEIGHT, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ if (!test_error_msg(rc,"SPI_{GET,SET}DOUBLECLKHEIGHT"))
+ return;
+
+ test_change_message( SPI_SETDOUBLECLKHEIGHT, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key_ex( SPI_SETDOUBLECLKHEIGHT_REGKEY1,
+ SPI_SETDOUBLECLKHEIGHT_REGKEY2,
+ SPI_SETDOUBLECLKHEIGHT_VALNAME, buf );
+ eq( GetSystemMetrics( SM_CYDOUBLECLK ), (int)vals[i],
+ "SM_CYDOUBLECLK", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETDOUBLECLKHEIGHT, old_height, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETDOUBLECLICKTIME( void ) /* 32 */
+{
+ BOOL rc;
+ UINT curr_val;
+ UINT saved_val;
+ UINT old_time;
+ char buf[10];
+
+ trace("testing SPI_{GET,SET}DOUBLECLICKTIME\n");
+ old_time = GetDoubleClickTime();
+
+ curr_val = 0;
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_SETDOUBLECLICKTIME, curr_val, 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ if (!test_error_msg(rc,"SPI_{GET,SET}DOUBLECLICKTIME"))
+ return;
+
+ test_change_message( SPI_SETDOUBLECLICKTIME, 0 );
+ sprintf( buf, "%d", curr_val );
+ test_reg_key( SPI_SETDOUBLECLICKTIME_REGKEY,
+ SPI_SETDOUBLECLICKTIME_VALNAME, buf );
+ curr_val = 500; /* used value for 0 */
+ eq( GetDoubleClickTime(), curr_val, "GetDoubleClickTime", "%d" );
+
+ curr_val = 1000;
+ rc=SystemParametersInfoA( SPI_SETDOUBLECLICKTIME, curr_val, 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETDOUBLECLICKTIME, 0 );
+ sprintf( buf, "%d", curr_val );
+ test_reg_key( SPI_SETDOUBLECLICKTIME_REGKEY,
+ SPI_SETDOUBLECLICKTIME_VALNAME, buf );
+ eq( GetDoubleClickTime(), curr_val, "GetDoubleClickTime", "%d" );
+
+ saved_val = curr_val;
+
+ curr_val = 0;
+ SetDoubleClickTime( curr_val );
+ sprintf( buf, "%d", saved_val );
+ test_reg_key( SPI_SETDOUBLECLICKTIME_REGKEY,
+ SPI_SETDOUBLECLICKTIME_VALNAME, buf );
+ curr_val = 500; /* used value for 0 */
+ eq( GetDoubleClickTime(), curr_val, "GetDoubleClickTime", "%d" );
+
+ curr_val = 1000;
+ SetDoubleClickTime( curr_val );
+ sprintf( buf, "%d", saved_val );
+ test_reg_key( SPI_SETDOUBLECLICKTIME_REGKEY,
+ SPI_SETDOUBLECLICKTIME_VALNAME, buf );
+ eq( GetDoubleClickTime(), curr_val, "GetDoubleClickTime", "%d" );
+
+ rc=SystemParametersInfoA(SPI_SETDOUBLECLICKTIME, old_time, 0, SPIF_UPDATEINIFILE);
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETMOUSEBUTTONSWAP( void ) /* 33 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}MOUSEBUTTONSWAP\n");
+ old_b = GetSystemMetrics( SM_SWAPBUTTON );
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_SETMOUSEBUTTONSWAP, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ if (!test_error_msg(rc,"SPI_{GET,SET}MOUSEBUTTONSWAP"))
+ break;
+
+ test_change_message( SPI_SETMOUSEBUTTONSWAP, 0 );
+ test_reg_key( SPI_SETMOUSEBUTTONSWAP_REGKEY,
+ SPI_SETMOUSEBUTTONSWAP_VALNAME,
+ vals[i] ? "1" : "0" );
+ eq( GetSystemMetrics( SM_SWAPBUTTON ), (int)vals[i],
+ "SM_SWAPBUTTON", "%d" );
+ rc=SwapMouseButton((BOOL)vals[i^1]);
+ eq( GetSystemMetrics( SM_SWAPBUTTON ), (int)vals[i^1],
+ "SwapMouseButton", "%d" );
+ ok( rc==(BOOL)vals[i], "SwapMouseButton does not return previous state (really %d)\n", rc );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETMOUSEBUTTONSWAP, old_b, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETFASTTASKSWITCH( void ) /* 36 */
+{
+ BOOL rc;
+ BOOL v;
+
+ trace("testing SPI_GETFASTTASKSWITCH\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETFASTTASKSWITCH, 0, &v, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}FASTTASKSWITCH"))
+ return;
+
+ /* there is not a single Windows platform on which SPI_GETFASTTASKSWITCH
+ * works. That sure simplifies testing!
+ */
+}
+
+static void test_SPI_SETDRAGFULLWINDOWS( void ) /* 37 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}DRAGFULLWINDOWS\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETDRAGFULLWINDOWS, 0, &old_b, 0 );
+
+ /* SPI_{GET,SET}DRAGFULLWINDOWS is not implemented on Win95 */
+ if (!test_error_msg(rc,"SPI_{GET,SET}DRAGFULLWINDOWS"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+
+ rc=SystemParametersInfoA( SPI_SETDRAGFULLWINDOWS, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETDRAGFULLWINDOWS, 0 );
+ test_reg_key( SPI_SETDRAGFULLWINDOWS_REGKEY,
+ SPI_SETDRAGFULLWINDOWS_VALNAME,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETDRAGFULLWINDOWS, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}DRAGFULLWINDOWS", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETDRAGFULLWINDOWS, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+#define test_reg_metric( KEY, VAL, val) \
+{ INT regval;\
+ regval = metricfromreg( KEY, VAL, dpi);\
+ ok( regval==val, "wrong value \"%s\" in registry %d, expected %d\n", VAL, regval, val);\
+}
+
+#define test_reg_metric2( KEY1, KEY2, VAL, val) \
+{ INT regval;\
+ regval = metricfromreg( KEY1, VAL, dpi);\
+ if( regval != val) regval = metricfromreg( KEY2, VAL, dpi);\
+ ok( regval==val, "wrong value \"%s\" in registry %d, expected %d\n", VAL, regval, val);\
+}
+
+#define test_reg_font( KEY, VAL, LF) \
+{ LOGFONTA lfreg;\
+ lffromreg( KEY, VAL, &lfreg);\
+ ok( (lfreg.lfHeight < 0 ? (LF).lfHeight == lfreg.lfHeight :\
+ MulDiv( -(LF).lfHeight , 72, dpi) == lfreg.lfHeight )&&\
+ (LF).lfWidth == lfreg.lfWidth &&\
+ (LF).lfWeight == lfreg.lfWeight &&\
+ !strcmp( (LF).lfFaceName, lfreg.lfFaceName)\
+ , "wrong value \"%s\" in registry %ld, %ld\n", VAL, (LF).lfHeight, lfreg.lfHeight);\
+}
+
+#define TEST_NONCLIENTMETRICS_REG( ncm) \
+test_reg_metric2( SPI_SETBORDER_REGKEY2, SPI_SETBORDER_REGKEY, SPI_SETBORDER_VALNAME, (ncm).iBorderWidth);\
+test_reg_metric( SPI_METRIC_REGKEY, SPI_SCROLLWIDTH_VALNAME, (ncm).iScrollWidth);\
+test_reg_metric( SPI_METRIC_REGKEY, SPI_SCROLLHEIGHT_VALNAME, (ncm).iScrollHeight);\
+/*FIXME: test_reg_metric( SPI_METRIC_REGKEY, SPI_CAPTIONWIDTH_VALNAME, (ncm).iCaptionWidth);*/\
+test_reg_metric( SPI_METRIC_REGKEY, SPI_CAPTIONHEIGHT_VALNAME, (ncm).iCaptionHeight);\
+test_reg_metric( SPI_METRIC_REGKEY, SPI_SMCAPTIONWIDTH_VALNAME, (ncm).iSmCaptionWidth);\
+test_reg_metric( SPI_METRIC_REGKEY, SPI_SMCAPTIONHEIGHT_VALNAME, (ncm).iSmCaptionHeight);\
+test_reg_metric( SPI_METRIC_REGKEY, SPI_MENUWIDTH_VALNAME, (ncm).iMenuWidth);\
+test_reg_metric( SPI_METRIC_REGKEY, SPI_MENUHEIGHT_VALNAME, (ncm).iMenuHeight);\
+test_reg_font( SPI_METRIC_REGKEY, SPI_MENUFONT_VALNAME, (ncm).lfMenuFont);\
+test_reg_font( SPI_METRIC_REGKEY, SPI_CAPTIONFONT_VALNAME, (ncm).lfCaptionFont);\
+test_reg_font( SPI_METRIC_REGKEY, SPI_SMCAPTIONFONT_VALNAME, (ncm).lfSmCaptionFont);\
+test_reg_font( SPI_METRIC_REGKEY, SPI_STATUSFONT_VALNAME, (ncm).lfStatusFont);\
+test_reg_font( SPI_METRIC_REGKEY, SPI_MESSAGEFONT_VALNAME, (ncm).lfMessageFont);
+
+/* get text metric height value for the specified logfont */
+static int get_tmheight( LOGFONTA *plf, int flag)
+{
+ TEXTMETRICA tm;
+ HFONT hfont = CreateFontIndirectA( plf);
+ hfont = SelectObject( hdc, hfont);
+ GetTextMetricsA( hdc, &tm);
+ hfont = SelectObject( hdc, hfont);
+ return tm.tmHeight + (flag ? tm.tmExternalLeading : 0);
+}
+
+void test_GetSystemMetrics( void);
+
+static void test_SPI_SETNONCLIENTMETRICS( void ) /* 44 */
+{
+ BOOL rc;
+ INT expect;
+ NONCLIENTMETRICSA Ncmorig;
+ NONCLIENTMETRICSA Ncmnew;
+ NONCLIENTMETRICSA Ncmcur;
+ NONCLIENTMETRICSA Ncmstart;
+
+ Ncmorig.cbSize = sizeof(NONCLIENTMETRICSA);
+ Ncmnew.cbSize = sizeof(NONCLIENTMETRICSA);
+ Ncmcur.cbSize = sizeof(NONCLIENTMETRICSA);
+ Ncmstart.cbSize = sizeof(NONCLIENTMETRICSA);
+
+ trace("testing SPI_{GET,SET}NONCLIENTMETRICS\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &Ncmorig, FALSE );
+ if (!test_error_msg(rc,"SPI_{GET,SET}NONCLIENTMETRICS"))
+ return;
+ Ncmstart = Ncmorig;
+ /* SPI_GETNONCLIENTMETRICS returns some "cooked" values. For instance if
+ the caption font height is higher than the CaptionHeight field,
+ the latter is adjusted accordingly. To be able to restore these setting
+ accurately be restore the raw values. */
+ Ncmorig.iCaptionWidth = metricfromreg( SPI_METRIC_REGKEY, SPI_CAPTIONWIDTH_VALNAME, dpi);
+ Ncmorig.iCaptionHeight = metricfromreg( SPI_METRIC_REGKEY, SPI_CAPTIONHEIGHT_VALNAME, dpi);
+ Ncmorig.iSmCaptionHeight = metricfromreg( SPI_METRIC_REGKEY, SPI_SMCAPTIONHEIGHT_VALNAME, dpi);
+ Ncmorig.iMenuHeight = metricfromreg( SPI_METRIC_REGKEY, SPI_MENUHEIGHT_VALNAME, dpi);
+ /* test registry entries */
+ TEST_NONCLIENTMETRICS_REG( Ncmorig)
+ /* make small changes */
+ Ncmnew = Ncmstart;
+ Ncmnew.iBorderWidth += 1;
+ Ncmnew.iScrollWidth += 1;
+ Ncmnew.iScrollHeight -= 1;
+ Ncmnew.iCaptionWidth -= 2;
+ Ncmnew.iCaptionHeight += 2;
+ Ncmnew.lfCaptionFont.lfHeight +=1;
+ Ncmnew.lfCaptionFont.lfWidth +=2;
+ Ncmnew.lfCaptionFont.lfWeight +=1;
+ Ncmnew.iSmCaptionWidth += 1;
+ Ncmnew.iSmCaptionHeight += 2;
+ Ncmnew.lfSmCaptionFont.lfHeight +=3;
+ Ncmnew.lfSmCaptionFont.lfWidth -=1;
+ Ncmnew.lfSmCaptionFont.lfWeight +=3;
+ Ncmnew.iMenuWidth += 1;
+ Ncmnew.iMenuHeight += 2;
+ Ncmnew.lfMenuFont.lfHeight +=1;
+ Ncmnew.lfMenuFont.lfWidth +=1;
+ Ncmnew.lfMenuFont.lfWeight +=2;
+ Ncmnew.lfStatusFont.lfHeight -=1;
+ Ncmnew.lfStatusFont.lfWidth -=1;
+ Ncmnew.lfStatusFont.lfWeight +=3;
+ Ncmnew.lfMessageFont.lfHeight -=2;
+ Ncmnew.lfMessageFont.lfWidth -=1;
+ Ncmnew.lfMessageFont.lfWeight +=4;
+
+ rc=SystemParametersInfoA( SPI_SETNONCLIENTMETRICS, 0, &Ncmnew, SPIF_UPDATEINIFILE|
+ SPIF_SENDCHANGE);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETNONCLIENTMETRICS, 1 );
+ /* get them back */
+ rc=SystemParametersInfoA( SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &Ncmcur, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* test registry entries */
+ TEST_NONCLIENTMETRICS_REG( Ncmcur)
+ /* test the systemm metrics with these settings */
+ test_GetSystemMetrics();
+ /* now for something invalid: increase the {menu|caption|smcaption} fonts
+ by a large amount will increase the {menu|caption|smcaption} height*/
+ Ncmnew = Ncmstart;
+ Ncmnew.lfMenuFont.lfHeight -= 8;
+ Ncmnew.lfCaptionFont.lfHeight =-4;
+ Ncmnew.lfSmCaptionFont.lfHeight -=10;
+ /* also show that a few values are lo limited */
+ Ncmnew.iCaptionWidth = 0;
+ Ncmnew.iCaptionHeight = 0;
+ Ncmnew.iScrollHeight = 0;
+ Ncmnew.iScrollWidth = 0;
+
+ rc=SystemParametersInfoA( SPI_SETNONCLIENTMETRICS, 0, &Ncmnew, SPIF_UPDATEINIFILE|
+ SPIF_SENDCHANGE);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETNONCLIENTMETRICS, 1 );
+ /* raw values are in registry */
+ TEST_NONCLIENTMETRICS_REG( Ncmnew)
+ /* get them back */
+ rc=SystemParametersInfoA( SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &Ncmcur, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* cooked values are returned */
+ expect = max( Ncmnew.iMenuHeight, 2 + get_tmheight( &Ncmnew.lfMenuFont, 1));
+ ok( Ncmcur.iMenuHeight == expect,
+ "MenuHeight: %d expected %d\n", Ncmcur.iMenuHeight, expect);
+ expect = max( Ncmnew.iCaptionHeight, 2 + get_tmheight(&Ncmnew.lfCaptionFont, 0));
+ ok( Ncmcur.iCaptionHeight == expect,
+ "CaptionHeight: %d expected %d\n", Ncmcur.iCaptionHeight, expect);
+ expect = max( Ncmnew.iSmCaptionHeight, 2 + get_tmheight( &Ncmnew.lfSmCaptionFont, 0));
+ ok( Ncmcur.iSmCaptionHeight == expect,
+ "SmCaptionHeight: %d expected %d\n", Ncmcur.iSmCaptionHeight, expect);
+
+ ok( Ncmcur.iCaptionWidth == 8 ||
+ Ncmcur.iCaptionWidth == Ncmstart.iCaptionWidth, /* with windows XP theme, the value never changes */
+ "CaptionWidth: %d expected 8\n", Ncmcur.iCaptionWidth);
+ ok( Ncmcur.iScrollWidth == 8,
+ "ScrollWidth: %d expected 8\n", Ncmcur.iScrollWidth);
+ ok( Ncmcur.iScrollHeight == 8,
+ "ScrollHeight: %d expected 8\n", Ncmcur.iScrollHeight);
+ /* test the systemm metrics with these settings */
+ test_GetSystemMetrics();
+ /* restore */
+ rc=SystemParametersInfoA( SPI_SETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS),
+ &Ncmorig, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
+ test_change_message( SPI_SETNONCLIENTMETRICS, 0 );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETMINIMIZEDMETRICS( void ) /* 44 */
+{
+ BOOL rc;
+ INT regval;
+ MINIMIZEDMETRICS lpMm_orig;
+ MINIMIZEDMETRICS lpMm_new;
+ MINIMIZEDMETRICS lpMm_cur;
+
+ lpMm_orig.cbSize = sizeof(MINIMIZEDMETRICS);
+ lpMm_new.cbSize = sizeof(MINIMIZEDMETRICS);
+ lpMm_cur.cbSize = sizeof(MINIMIZEDMETRICS);
+
+ trace("testing SPI_{GET,SET}MINIMIZEDMETRICS\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS), &lpMm_orig, FALSE );
+ if (!test_error_msg(rc,"SPI_{GET,SET}MINIMIZEDMETRICS"))
+ return;
+ /* test registry */
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINWIDTH_VALNAME, dpi);
+ ok( regval == lpMm_orig.iWidth, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_orig.iWidth);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINHORZGAP_VALNAME, dpi);
+ ok( regval == lpMm_orig.iHorzGap, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_orig.iHorzGap);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINVERTGAP_VALNAME, dpi);
+ ok( regval == lpMm_orig.iVertGap, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_orig.iVertGap);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINARRANGE_VALNAME, dpi);
+ ok( regval == lpMm_orig.iArrange, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_orig.iArrange);
+ /* set some new values */
+ lpMm_cur.iWidth = 180;
+ lpMm_cur.iHorzGap = 1;
+ lpMm_cur.iVertGap = 1;
+ lpMm_cur.iArrange = 5;
+ rc=SystemParametersInfoA( SPI_SETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS),
+ &lpMm_cur, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* read them back */
+ rc=SystemParametersInfoA( SPI_GETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS), &lpMm_new, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* and compare */
+ eq( lpMm_new.iWidth, lpMm_cur.iWidth, "iWidth", "%d" );
+ eq( lpMm_new.iHorzGap, lpMm_cur.iHorzGap, "iHorzGap", "%d" );
+ eq( lpMm_new.iVertGap, lpMm_cur.iVertGap, "iVertGap", "%d" );
+ eq( lpMm_new.iArrange, lpMm_cur.iArrange, "iArrange", "%d" );
+ /* test registry */
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINWIDTH_VALNAME, dpi);
+ ok( regval == lpMm_new.iWidth, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iWidth);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINHORZGAP_VALNAME, dpi);
+ ok( regval == lpMm_new.iHorzGap, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iHorzGap);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINVERTGAP_VALNAME, dpi);
+ ok( regval == lpMm_new.iVertGap, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iVertGap);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINARRANGE_VALNAME, dpi);
+ ok( regval == lpMm_new.iArrange, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iArrange);
+ /* test some system metrics */
+ eq( GetSystemMetrics( SM_CXMINIMIZED ) - 6,
+ lpMm_new.iWidth, "iWidth", "%d" );
+ eq( GetSystemMetrics( SM_CXMINSPACING ) - GetSystemMetrics( SM_CXMINIMIZED ),
+ lpMm_new.iHorzGap, "iHorzGap", "%d" );
+ eq( GetSystemMetrics( SM_CYMINSPACING ) - GetSystemMetrics( SM_CYMINIMIZED ),
+ lpMm_new.iVertGap, "iVertGap", "%d" );
+ eq( GetSystemMetrics( SM_ARRANGE ),
+ lpMm_new.iArrange, "iArrange", "%d" );
+ /* now some realy invalid settings */
+ lpMm_cur.iWidth = -1;
+ lpMm_cur.iHorzGap = -1;
+ lpMm_cur.iVertGap = -1;
+ lpMm_cur.iArrange = - 1;
+ rc=SystemParametersInfoA( SPI_SETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS),
+ &lpMm_cur, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* read back */
+ rc=SystemParametersInfoA( SPI_GETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS), &lpMm_new, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* the width and H/V gaps have minimum 0, arrange is and'd with 0xf */
+ eq( lpMm_new.iWidth, 0, "iWidth", "%d" );
+ eq( lpMm_new.iHorzGap, 0, "iHorzGap", "%d" );
+ eq( lpMm_new.iVertGap, 0, "iVertGap", "%d" );
+ eq( lpMm_new.iArrange, 0xf & lpMm_cur.iArrange, "iArrange", "%d" );
+ /* test registry */
+#if 0 /* FIXME: cannot understand the results of this (11, 11, 11, 0) */
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINWIDTH_VALNAME, dpi);
+ ok( regval == lpMm_new.iWidth, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iWidth);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINHORZGAP_VALNAME, dpi);
+ ok( regval == lpMm_new.iHorzGap, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iHorzGap);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINVERTGAP_VALNAME, dpi);
+ ok( regval == lpMm_new.iVertGap, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iVertGap);
+ regval = metricfromreg( SPI_MINIMIZEDMETRICS_REGKEY, SPI_MINARRANGE_VALNAME, dpi);
+ ok( regval == lpMm_new.iArrange, "wrong value in registry %d, expected %d\n",
+ regval, lpMm_new.iArrange);
+#endif
+ /* test some system metrics */
+ eq( GetSystemMetrics( SM_CXMINIMIZED ) - 6,
+ lpMm_new.iWidth, "iWidth", "%d" );
+ eq( GetSystemMetrics( SM_CXMINSPACING ) - GetSystemMetrics( SM_CXMINIMIZED ),
+ lpMm_new.iHorzGap, "iHorzGap", "%d" );
+ eq( GetSystemMetrics( SM_CYMINSPACING ) - GetSystemMetrics( SM_CYMINIMIZED ),
+ lpMm_new.iVertGap, "iVertGap", "%d" );
+ eq( GetSystemMetrics( SM_ARRANGE ),
+ lpMm_new.iArrange, "iArrange", "%d" );
+ /* restore */
+ rc=SystemParametersInfoA( SPI_SETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS),
+ &lpMm_orig, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+ /* check that */
+ rc=SystemParametersInfoA( SPI_GETMINIMIZEDMETRICS, sizeof(MINIMIZEDMETRICS), &lpMm_new, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( lpMm_new.iWidth, lpMm_orig.iWidth, "iWidth", "%d" );
+ eq( lpMm_new.iHorzGap, lpMm_orig.iHorzGap, "iHorzGap", "%d" );
+ eq( lpMm_new.iVertGap, lpMm_orig.iVertGap, "iVertGap", "%d" );
+ eq( lpMm_new.iArrange, lpMm_orig.iArrange, "iArrange", "%d" );
+}
+
+static void test_SPI_SETICONMETRICS( void ) /* 46 */
+{
+ BOOL rc, wrap;
+ INT spacing;
+ ICONMETRICSA im_orig;
+ ICONMETRICSA im_new;
+ ICONMETRICSA im_cur;
+ INT regval;
+
+ im_orig.cbSize = sizeof(ICONMETRICSA);
+ im_new.cbSize = sizeof(ICONMETRICSA);
+ im_cur.cbSize = sizeof(ICONMETRICSA);
+
+ trace("testing SPI_{GET,SET}ICONMETRICS\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETICONMETRICS, sizeof(ICONMETRICSA), &im_orig, FALSE );
+ if (!test_error_msg(rc,"SPI_{GET,SET}ICONMETRICS"))
+ return;
+ /* check some registry values */
+ regval = metricfromreg( SPI_ICONHORIZONTALSPACING_REGKEY, SPI_ICONHORIZONTALSPACING_VALNAME, dpi);
+ ok( regval==im_orig.iHorzSpacing, "wrong value in registry %d, expected %d\n", regval, im_orig.iHorzSpacing);
+ regval = metricfromreg( SPI_ICONVERTICALSPACING_REGKEY, SPI_ICONVERTICALSPACING_VALNAME, dpi);
+ ok( regval==im_orig.iVertSpacing, "wrong value in registry %d, expected %d\n", regval, im_orig.iVertSpacing);
+ regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY2, SPI_SETICONTITLEWRAP_VALNAME, dpi);
+ if( regval != im_orig.iTitleWrap)
+ regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY1, SPI_SETICONTITLEWRAP_VALNAME, dpi);
+ ok( regval==im_orig.iTitleWrap, "wrong value in registry %d, expected %d\n", regval, im_orig.iTitleWrap);
+
+ /* change everything without creating something invalid ( Win9x would ignore
+ * an invalid font for instance) */
+ im_cur = im_orig;
+ im_cur.iHorzSpacing += 10;
+ im_cur.iVertSpacing += 6;
+ im_cur.iTitleWrap = !im_cur.iTitleWrap;
+ im_cur.lfFont.lfHeight += 1;
+ im_cur.lfFont.lfWidth += 2;
+ im_cur.lfFont.lfEscapement = 1;
+ im_cur.lfFont.lfWeight = im_cur.lfFont.lfWeight > 100 ? 1 : 314;
+ im_cur.lfFont.lfItalic = !im_cur.lfFont.lfItalic;
+ im_cur.lfFont.lfStrikeOut = !im_cur.lfFont.lfStrikeOut;
+ im_cur.lfFont.lfUnderline = !im_cur.lfFont.lfUnderline;
+ im_cur.lfFont.lfCharSet = im_cur.lfFont.lfCharSet ? 0 : 1;
+ im_cur.lfFont.lfOutPrecision = im_cur.lfFont.lfOutPrecision == OUT_DEFAULT_PRECIS ?
+ OUT_TT_PRECIS : OUT_DEFAULT_PRECIS;
+ im_cur.lfFont.lfClipPrecision ^= CLIP_LH_ANGLES;
+ im_cur.lfFont.lfPitchAndFamily = im_cur.lfFont.lfPitchAndFamily ? 0 : 1;
+ im_cur.lfFont.lfQuality = im_cur.lfFont.lfQuality == DEFAULT_QUALITY ?
+ DRAFT_QUALITY : DEFAULT_QUALITY;
+ if( strcmp( im_cur.lfFont.lfFaceName, "MS Serif"))
+ strcpy( im_cur.lfFont.lfFaceName, "MS Serif");
+ else
+ strcpy( im_cur.lfFont.lfFaceName, "MS Sans Serif");
+
+ rc=SystemParametersInfoA( SPI_SETICONMETRICS, sizeof(ICONMETRICSA), &im_cur, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+
+ rc=SystemParametersInfoA( SPI_GETICONMETRICS, sizeof(ICONMETRICSA), &im_new, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ /* test GET <-> SETICONMETRICS */
+ eq( im_new.iHorzSpacing, im_cur.iHorzSpacing, "iHorzSpacing", "%d" );
+ eq( im_new.iVertSpacing, im_cur.iVertSpacing, "iVertSpacing", "%d" );
+ eq( im_new.iTitleWrap, im_cur.iTitleWrap, "iTitleWrap", "%d" );
+ eq( im_new.lfFont.lfHeight, im_cur.lfFont.lfHeight, "lfHeight", "%ld" );
+ eq( im_new.lfFont.lfWidth, im_cur.lfFont.lfWidth, "lfWidth", "%ld" );
+ eq( im_new.lfFont.lfEscapement, im_cur.lfFont.lfEscapement, "lfEscapement", "%ld" );
+ eq( im_new.lfFont.lfWeight, im_cur.lfFont.lfWeight, "lfWeight", "%ld" );
+ eq( im_new.lfFont.lfItalic, im_cur.lfFont.lfItalic, "lfItalic", "%d" );
+ eq( im_new.lfFont.lfStrikeOut, im_cur.lfFont.lfStrikeOut, "lfStrikeOut", "%d" );
+ eq( im_new.lfFont.lfUnderline, im_cur.lfFont.lfUnderline, "lfUnderline", "%d" );
+ eq( im_new.lfFont.lfCharSet, im_cur.lfFont.lfCharSet, "lfCharSet", "%d" );
+ eq( im_new.lfFont.lfOutPrecision, im_cur.lfFont.lfOutPrecision, "lfOutPrecision", "%d" );
+ eq( im_new.lfFont.lfClipPrecision, im_cur.lfFont.lfClipPrecision, "lfClipPrecision", "%d" );
+ eq( im_new.lfFont.lfPitchAndFamily, im_cur.lfFont.lfPitchAndFamily, "lfPitchAndFamily", "%d" );
+ eq( im_new.lfFont.lfQuality, im_cur.lfFont.lfQuality, "lfQuality", "%d" );
+ ok( !strcmp( im_new.lfFont.lfFaceName, im_cur.lfFont.lfFaceName),
+ "wrong facename \"%s\", should be \"%s\"\n", im_new.lfFont.lfFaceName,
+ im_cur.lfFont.lfFaceName);
+ /* test some system metrics */
+ eq( GetSystemMetrics( SM_CXICONSPACING ),
+ im_new.iHorzSpacing, "iHorzSpacing", "%d" );
+ eq( GetSystemMetrics( SM_CYICONSPACING ),
+ im_new.iVertSpacing, "iVertSpacing", "%d" );
+ /* check some registry values */
+ regval = metricfromreg( SPI_ICONHORIZONTALSPACING_REGKEY, SPI_ICONHORIZONTALSPACING_VALNAME, dpi);
+ ok( regval==im_cur.iHorzSpacing, "wrong value in registry %d, expected %d\n", regval, im_cur.iHorzSpacing);
+ regval = metricfromreg( SPI_ICONVERTICALSPACING_REGKEY, SPI_ICONVERTICALSPACING_VALNAME, dpi);
+ ok( regval==im_cur.iVertSpacing, "wrong value in registry %d, expected %d\n", regval, im_cur.iVertSpacing);
+ regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY2, SPI_SETICONTITLEWRAP_VALNAME, dpi);
+ if( regval != im_cur.iTitleWrap)
+ regval = metricfromreg( SPI_SETICONTITLEWRAP_REGKEY1, SPI_SETICONTITLEWRAP_VALNAME, dpi);
+ ok( regval==im_cur.iTitleWrap, "wrong value in registry %d, expected %d\n", regval, im_cur.iTitleWrap);
+ /* test some values from other SPI_GETxxx calls */
+ rc = SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, 0, &spacing, 0 );
+ ok( rc && spacing == im_cur.iHorzSpacing,
+ "SystemParametersInfoA( SPI_ICONHORIZONTALSPACING...) failed or returns wrong value %d instead of %d\n",
+ spacing, im_cur.iHorzSpacing);
+ rc = SystemParametersInfoA( SPI_ICONVERTICALSPACING, 0, &spacing, 0 );
+ ok( rc && spacing == im_cur.iVertSpacing,
+ "SystemParametersInfoA( SPI_ICONVERTICALSPACING...) failed or returns wrong value %d instead of %d\n",
+ spacing, im_cur.iVertSpacing);
+ rc = SystemParametersInfoA( SPI_GETICONTITLEWRAP, 0, &wrap, 0 );
+ ok( rc && wrap == im_cur.iTitleWrap,
+ "SystemParametersInfoA( SPI_GETICONTITLEWRAP...) failed or returns wrong value %d instead of %d\n",
+ wrap, im_cur.iTitleWrap);
+ /* restore old values */
+ rc=SystemParametersInfoA( SPI_SETICONMETRICS, sizeof(ICONMETRICSA), &im_orig,SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+
+ rc=SystemParametersInfoA( SPI_GETICONMETRICS, sizeof(ICONMETRICSA), &im_new, FALSE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+
+ eq( im_new.iHorzSpacing, im_orig.iHorzSpacing, "iHorzSpacing", "%d" );
+ eq( im_new.iVertSpacing, im_orig.iVertSpacing, "iVertSpacing", "%d" );
+ eq( im_new.iTitleWrap, im_orig.iTitleWrap, "iTitleWrap", "%d" );
+}
+
+static void test_SPI_SETWORKAREA( void ) /* 47 */
+{
+ BOOL rc;
+ RECT old_area;
+ RECT area;
+ RECT curr_val;
+
+ trace("testing SPI_{GET,SET}WORKAREA\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA(SPI_GETWORKAREA, 0, &old_area, 0);
+ if (!test_error_msg(rc,"SPI_{GET,SET}WORKAREA"))
+ return;
+
+ /* Modify the work area only minimally as this causes the icons that
+ * fall outside it to be moved around thus requiring the user to
+ * reposition them manually one by one.
+ * Changing the work area by just one pixel should make this occurrence
+ * reasonably unlikely.
+ */
+ curr_val.left = old_area.left;
+ curr_val.top = old_area.top;
+ curr_val.right = old_area.right-1;
+ curr_val.bottom = old_area.bottom-1;
+ rc=SystemParametersInfoA( SPI_SETWORKAREA, 0, &curr_val,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETWORKAREA, 0 );
+ rc=SystemParametersInfoA( SPI_GETWORKAREA, 0, &area, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( area.left, curr_val.left, "left", "%ld" );
+ eq( area.top, curr_val.top, "top", "%ld" );
+ eq( area.right, curr_val.right, "right", "%ld" );
+ eq( area.bottom, curr_val.bottom, "bottom", "%ld" );
+
+ rc=SystemParametersInfoA( SPI_SETWORKAREA, 0, &old_area,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message( SPI_SETWORKAREA, 0 );
+ rc=SystemParametersInfoA( SPI_GETWORKAREA, 0, &area, 0 );
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ eq( area.left, old_area.left, "left", "%ld" );
+ eq( area.top, old_area.top, "top", "%ld" );
+ eq( area.right, old_area.right, "right", "%ld" );
+ eq( area.bottom, old_area.bottom, "bottom", "%ld" );
+}
+
+static void test_SPI_SETSHOWSOUNDS( void ) /* 57 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}SHOWSOUNDS\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETSHOWSOUNDS, 0, &old_b, 0 );
+ /* SPI_{GET,SET}SHOWSOUNDS is completely broken on Win9x */
+ if (!test_error_msg(rc,"SPI_{GET,SET}SHOWSOUNDS"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+
+ rc=SystemParametersInfoA( SPI_SETSHOWSOUNDS, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETSHOWSOUNDS, 0 );
+ test_reg_key( SPI_SETSHOWSOUNDS_REGKEY,
+ SPI_SETSHOWSOUNDS_VALNAME,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETSHOWSOUNDS, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_GETSHOWSOUNDS", "%d" );
+ eq( GetSystemMetrics( SM_SHOWSOUNDS ), (int)vals[i],
+ "SM_SHOWSOUNDS", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETSHOWSOUNDS, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETKEYBOARDPREF( void ) /* 69 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}KEYBOARDPREF\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETKEYBOARDPREF, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}KEYBOARDPREF"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ BOOL v;
+
+ rc=SystemParametersInfoA( SPI_SETKEYBOARDPREF, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETKEYBOARDPREF, 0 );
+ test_reg_key_ex2( SPI_SETKEYBOARDPREF_REGKEY, SPI_SETKEYBOARDPREF_REGKEY_LEGACY,
+ SPI_SETKEYBOARDPREF_VALNAME, SPI_SETKEYBOARDPREF_VALNAME_LEGACY,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETKEYBOARDPREF, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, (BOOL)vals[i], "SPI_GETKEYBOARDPREF", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETKEYBOARDPREF, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETSCREENREADER( void ) /* 71 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}SCREENREADER\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETSCREENREADER, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}SCREENREADER"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ BOOL v;
+
+ rc=SystemParametersInfoA( SPI_SETSCREENREADER, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETSCREENREADER, 0 );
+ test_reg_key_ex2( SPI_SETSCREENREADER_REGKEY, SPI_SETSCREENREADER_REGKEY_LEGACY,
+ SPI_SETSCREENREADER_VALNAME, SPI_SETSCREENREADER_VALNAME_LEGACY,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETSCREENREADER, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, (BOOL)vals[i], "SPI_GETSCREENREADER", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETSCREENREADER, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETFONTSMOOTHING( void ) /* 75 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={0xffffffff,0,1,2};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}FONTSMOOTHING\n");
+ if( iswin9x) return; /* 95/98/ME don't seem to implement this fully */
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETFONTSMOOTHING, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}FONTSMOOTHING"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+
+ rc=SystemParametersInfoA( SPI_SETFONTSMOOTHING, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETFONTSMOOTHING, 0 );
+ test_reg_key( SPI_SETFONTSMOOTHING_REGKEY,
+ SPI_SETFONTSMOOTHING_VALNAME,
+ vals[i] ? "2" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETFONTSMOOTHING, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i] ? 1 : 0, "SPI_GETFONTSMOOTHING", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETFONTSMOOTHING, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETLOWPOWERACTIVE( void ) /* 85 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}LOWPOWERACTIVE\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETLOWPOWERACTIVE, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}LOWPOWERACTIVE"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+
+ rc=SystemParametersInfoA( SPI_SETLOWPOWERACTIVE, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETLOWPOWERACTIVE, 0 );
+ test_reg_key( SPI_SETLOWPOWERACTIVE_REGKEY,
+ SPI_SETLOWPOWERACTIVE_VALNAME,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETLOWPOWERACTIVE, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_GETLOWPOWERACTIVE", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETLOWPOWERACTIVE, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETPOWEROFFACTIVE( void ) /* 86 */
+{
+ BOOL rc;
+ BOOL old_b;
+ const UINT vals[]={TRUE,FALSE};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}POWEROFFACTIVE\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETPOWEROFFACTIVE, 0, &old_b, 0 );
+ if (!test_error_msg(rc,"SPI_{GET,SET}POWEROFFACTIVE"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+
+ rc=SystemParametersInfoA( SPI_SETPOWEROFFACTIVE, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETPOWEROFFACTIVE, 0 );
+ test_reg_key( SPI_SETPOWEROFFACTIVE_REGKEY,
+ SPI_SETPOWEROFFACTIVE_VALNAME,
+ vals[i] ? "1" : "0" );
+
+ rc=SystemParametersInfoA( SPI_GETPOWEROFFACTIVE, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_GETPOWEROFFACTIVE", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETPOWEROFFACTIVE, old_b, 0, SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETMOUSEHOVERWIDTH( void ) /* 99 */
+{
+ BOOL rc;
+ UINT old_width;
+ const UINT vals[]={0,32767};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}MOUSEHOVERWIDTH\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETMOUSEHOVERWIDTH, 0, &old_width, 0 );
+ /* SPI_{GET,SET}MOUSEHOVERWIDTH does not seem to be supported on Win9x despite
+ * what MSDN states (Verified on Win98SE)
+ */
+ if (!test_error_msg(rc,"SPI_{GET,SET}MOUSEHOVERWIDTH"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETMOUSEHOVERWIDTH, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETMOUSEHOVERWIDTH, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETMOUSEHOVERWIDTH_REGKEY,
+ SPI_SETMOUSEHOVERWIDTH_VALNAME, buf );
+
+ SystemParametersInfoA( SPI_GETMOUSEHOVERWIDTH, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}MOUSEHOVERWIDTH", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETMOUSEHOVERWIDTH, old_width, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETMOUSEHOVERHEIGHT( void ) /* 101 */
+{
+ BOOL rc;
+ UINT old_height;
+ const UINT vals[]={0,32767};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}MOUSEHOVERHEIGHT\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETMOUSEHOVERHEIGHT, 0, &old_height, 0 );
+ /* SPI_{GET,SET}MOUSEHOVERWIDTH does not seem to be supported on Win9x despite
+ * what MSDN states (Verified on Win98SE)
+ */
+ if (!test_error_msg(rc,"SPI_{GET,SET}MOUSEHOVERHEIGHT"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETMOUSEHOVERHEIGHT, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETMOUSEHOVERHEIGHT, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETMOUSEHOVERHEIGHT_REGKEY,
+ SPI_SETMOUSEHOVERHEIGHT_VALNAME, buf );
+
+ SystemParametersInfoA( SPI_GETMOUSEHOVERHEIGHT, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}MOUSEHOVERHEIGHT", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETMOUSEHOVERHEIGHT, old_height, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETMOUSEHOVERTIME( void ) /* 103 */
+{
+ BOOL rc;
+ UINT old_time;
+
+ /* Windows XP accepts 10 as the minimum hover time. Any value below will be
+ * defaulted to a value of 10 automatically.
+ */
+ const UINT vals[]={10,32767};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}MOUSEHOVERTIME\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETMOUSEHOVERTIME, 0, &old_time, 0 );
+ /* SPI_{GET,SET}MOUSEHOVERWIDTH does not seem to be supported on Win9x despite
+ * what MSDN states (Verified on Win98SE)
+ */
+ if (!test_error_msg(rc,"SPI_{GET,SET}MOUSEHOVERTIME"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETMOUSEHOVERTIME, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETMOUSEHOVERTIME, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETMOUSEHOVERTIME_REGKEY,
+ SPI_SETMOUSEHOVERTIME_VALNAME, buf );
+
+ SystemParametersInfoA( SPI_GETMOUSEHOVERTIME, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}MOUSEHOVERTIME", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETMOUSEHOVERTIME, old_time, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETWHEELSCROLLLINES( void ) /* 105 */
+{
+ BOOL rc;
+ UINT old_lines;
+ const UINT vals[]={0,32767};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}WHEELSCROLLLINES\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETWHEELSCROLLLINES, 0, &old_lines, 0 );
+
+ /* SPI_{GET,SET}WHEELSCROLLLINES not supported on Windows 95 */
+ if (!test_error_msg(rc,"SPI_{GET,SET}WHEELSCROLLLINES"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETWHEELSCROLLLINES, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETWHEELSCROLLLINES, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETMOUSESCROLLLINES_REGKEY,
+ SPI_SETMOUSESCROLLLINES_VALNAME, buf );
+
+ SystemParametersInfoA( SPI_GETWHEELSCROLLLINES, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}WHEELSCROLLLINES", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETWHEELSCROLLLINES, old_lines, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETMENUSHOWDELAY( void ) /* 107 */
+{
+ BOOL rc;
+ UINT old_delay;
+ const UINT vals[]={0,32767};
+ unsigned int i;
+
+ trace("testing SPI_{GET,SET}MENUSHOWDELAY\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA( SPI_GETMENUSHOWDELAY, 0, &old_delay, 0 );
+
+ /* SPI_{GET,SET}MENUSHOWDELAY not supported on Windows 95 */
+ if (!test_error_msg(rc,"SPI_{GET,SET}MENUSHOWDELAY"))
+ return;
+
+ for (i=0;i<sizeof(vals)/sizeof(*vals);i++)
+ {
+ UINT v;
+ char buf[10];
+
+ rc=SystemParametersInfoA( SPI_SETMENUSHOWDELAY, vals[i], 0,
+ SPIF_UPDATEINIFILE | SPIF_SENDCHANGE );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ test_change_message( SPI_SETMENUSHOWDELAY, 0 );
+ sprintf( buf, "%d", vals[i] );
+ test_reg_key( SPI_SETMENUSHOWDELAY_REGKEY,
+ SPI_SETMENUSHOWDELAY_VALNAME, buf );
+
+ SystemParametersInfoA( SPI_GETMENUSHOWDELAY, 0, &v, 0 );
+ ok(rc!=0,"%d: rc=%d err=%ld\n",i,rc,GetLastError());
+ eq( v, vals[i], "SPI_{GET,SET}MENUSHOWDELAY", "%d" );
+ }
+
+ rc=SystemParametersInfoA( SPI_SETMENUSHOWDELAY, old_delay, 0,
+ SPIF_UPDATEINIFILE );
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+}
+
+static void test_SPI_SETWALLPAPER( void ) /* 115 */
+{
+ BOOL rc;
+ char oldval[260];
+ char newval[260];
+
+ trace("testing SPI_{GET,SET}DESKWALLPAPER\n");
+ SetLastError(0xdeadbeef);
+ rc=SystemParametersInfoA(SPI_GETDESKWALLPAPER, 260, oldval, 0);
+ /* SPI_{GET,SET}DESKWALLPAPER is completely broken on Win9x and
+ * unimplemented on NT4
+ */
+ if (!test_error_msg(rc,"SPI_{GET,SET}DESKWALLPAPER"))
+ return;
+
+ strcpy(newval, "");
+ rc=SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, newval, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
+ ok(rc!=0,"SystemParametersInfoA: rc=%d err=%ld\n",rc,GetLastError());
+ test_change_message(SPI_SETDESKWALLPAPER, 0);
+
+ rc=SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, oldval, SPIF_UPDATEINIFILE);
+ ok(rc!=0,"***warning*** failed to restore the original value: rc=%d err=%ld\n",rc,GetLastError());
+
+ test_reg_key(SPI_SETDESKWALLPAPER_REGKEY, SPI_SETDESKWALLPAPER_VALNAME, oldval);
+}
+
+/*
+ * Registry entries for the system parameters.
+ * Names are created by 'SET' flags names.
+ * We assume that corresponding 'GET' entries use the same registry keys.
+ */
+static DWORD WINAPI SysParamsThreadFunc( LPVOID lpParam )
+{
+ test_SPI_SETBEEP(); /* 1 */
+ test_SPI_SETMOUSE(); /* 4 */
+ test_SPI_SETBORDER(); /* 6 */
+ test_SPI_SETKEYBOARDSPEED(); /* 10 */
+ test_SPI_ICONHORIZONTALSPACING(); /* 13 */
+ test_SPI_SETSCREENSAVETIMEOUT(); /* 14 */
+ test_SPI_SETSCREENSAVEACTIVE(); /* 17 */
+ test_SPI_SETGRIDGRANULARITY(); /* 19 */
+ test_SPI_SETKEYBOARDDELAY(); /* 23 */
+ test_SPI_ICONVERTICALSPACING(); /* 24 */
+ test_SPI_SETICONTITLEWRAP(); /* 26 */
+ test_SPI_SETMENUDROPALIGNMENT(); /* 28 */
+ test_SPI_SETDOUBLECLKWIDTH(); /* 29 */
+ test_SPI_SETDOUBLECLKHEIGHT(); /* 30 */
+ test_SPI_SETDOUBLECLICKTIME(); /* 32 */
+ test_SPI_SETMOUSEBUTTONSWAP(); /* 33 */
+ test_SPI_SETFASTTASKSWITCH(); /* 36 */
+ test_SPI_SETDRAGFULLWINDOWS(); /* 37 */
+ test_SPI_SETNONCLIENTMETRICS(); /* 42 */
+ test_SPI_SETMINIMIZEDMETRICS(); /* 44 */
+ test_SPI_SETICONMETRICS(); /* 46 */
+ test_SPI_SETWORKAREA(); /* 47 */
+ test_SPI_SETSHOWSOUNDS(); /* 57 */
+ test_SPI_SETKEYBOARDPREF(); /* 69 */
+ test_SPI_SETSCREENREADER(); /* 71 */
+ test_SPI_SETFONTSMOOTHING(); /* 75 */
+ test_SPI_SETLOWPOWERACTIVE(); /* 85 */
+ test_SPI_SETPOWEROFFACTIVE(); /* 86 */
+ test_SPI_SETMOUSEHOVERWIDTH(); /* 99 */
+ test_SPI_SETMOUSEHOVERHEIGHT(); /* 101 */
+ test_SPI_SETMOUSEHOVERTIME(); /* 103 */
+ test_SPI_SETWHEELSCROLLLINES(); /* 105 */
+ test_SPI_SETMENUSHOWDELAY(); /* 107 */
+ test_SPI_SETWALLPAPER(); /* 115 */
+ SendMessageA( ghTestWnd, WM_DESTROY, 0, 0 );
+ return 0;
+}
+
+/* test calculation of GetSystemMetrics values (mostly) from non client metrics,
+ * icon metrics and minimized metrics.
+ */
+
+/* copied from wine's GdiGetCharDimensions, which is not available on most
+ * windows versions */
+static LONG _GdiGetCharDimensions(HDC hdc, LPTEXTMETRICA lptm, LONG *height)
+{
+ SIZE sz;
+ static const CHAR alphabet[] = {
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q',
+ 'r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H',
+ 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',0};
+
+ if(lptm && !GetTextMetricsA(hdc, lptm)) return 0;
+
+ if(!GetTextExtentPointA(hdc, alphabet, 52, &sz)) return 0;
+
+ if (height) *height = sz.cy;
+ return (sz.cx / 26 + 1) / 2;
+}
+
+/* get text metrics and/or "average" char width of the specified logfont
+ * for the specified dc */
+void get_text_metr_size( HDC hdc, LOGFONTA *plf, TEXTMETRICA * ptm, UINT *psz)
+{
+ HFONT hfont, hfontsav;
+ TEXTMETRICA tm;
+ if( !ptm) ptm = &tm;
+ hfont = CreateFontIndirectA( plf);
+ if( !hfont || ( hfontsav = SelectObject( hdc, hfont)) == NULL ) {
+ ptm->tmHeight = -1;
+ if( psz) *psz = 10;
+ if( hfont) DeleteObject( hfont);
+ return;
+ }
+ GetTextMetricsA( hdc, ptm);
+ if( psz)
+ if( !(*psz = _GdiGetCharDimensions( hdc, ptm, NULL)))
+ *psz = 10;
+ SelectObject( hdc, hfontsav);
+ DeleteObject( hfont);
+}
+
+static int gsm_error_ctr;
+static UINT smcxsmsize = 999999999;
+
+#define ok_gsm( i, e)\
+{\
+ int exp = (e);\
+ int act = GetSystemMetrics( (i));\
+ if( exp != act) gsm_error_ctr++;\
+ ok( !( exp != act),"GetSystemMetrics(%s): expected %d actual %d\n", #i, exp,act);\
+}
+#define ok_gsm_2( i, e1, e2)\
+{\
+ int exp1 = (e1);\
+ int exp2 = (e2);\
+ int act = GetSystemMetrics( (i));\
+ if( exp1 != act && exp2 != act) gsm_error_ctr++;\
+ ok( !( exp1 != act && exp2 != act), "GetSystemMetrics(%s): expected %d or %d actual %d\n", #i, exp1, exp2, act);\
+}
+#define ok_gsm_3( i, e1, e2, e3)\
+{\
+ int exp1 = (e1);\
+ int exp2 = (e2);\
+ int exp3 = (e3);\
+ int act = GetSystemMetrics( (i));\
+ if( exp1 != act && exp2 != act && exp3 != act) gsm_error_ctr++;\
+ ok( !( exp1 != act && exp2 != act && exp3 != act),"GetSystemMetrics(%s): expected %d or %d or %d actual %d\n", #i, exp1, exp2, exp3, act);\
+}
+
+void test_GetSystemMetrics( void)
+{
+ TEXTMETRICA tmMenuFont;
+ UINT IconSpacing, IconVerticalSpacing;
+
+ HDC hdc = CreateIC( "Display", 0, 0, 0);
+ UINT avcwCaption;
+ INT CaptionWidth;
+ MINIMIZEDMETRICS minim;
+ NONCLIENTMETRICS ncm;
+ minim.cbSize = sizeof( minim);
+ ncm.cbSize = sizeof( ncm);
+ SystemParametersInfo( SPI_GETMINIMIZEDMETRICS, 0, &minim, 0);
+ SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
+
+ /* CaptionWidth from the registry may have different value of iCaptionWidth
+ * from the non client metrics (observed on WinXP) */
+ CaptionWidth = metricfromreg(
+ "Control Panel\\Desktop\\WindowMetrics","CaptionWidth", dpi);
+ get_text_metr_size( hdc, &ncm.lfMenuFont, &tmMenuFont, NULL);
+ get_text_metr_size( hdc, &ncm.lfCaptionFont, NULL, &avcwCaption);
+ /* FIXME: use icon metric */
+ if( !SystemParametersInfoA( SPI_ICONVERTICALSPACING, 0, &IconVerticalSpacing, 0))
+ IconVerticalSpacing = 0;
+ if( !SystemParametersInfoA( SPI_ICONHORIZONTALSPACING, 0, &IconSpacing, 0 ))
+ IconSpacing = 0;
+ /* reset error counters */
+ gsm_error_ctr = 0;
+
+ /* the tests: */
+
+ /* SM_CXSCREEN, cannot test these two */
+ /* SM_CYSCREEN */
+ ok_gsm( SM_CXVSCROLL, ncm.iScrollWidth);
+ ok_gsm( SM_CYHSCROLL, ncm.iScrollWidth);
+ ok_gsm( SM_CYCAPTION, ncm.iCaptionHeight+1);
+ ok_gsm( SM_CXBORDER, 1);
+ ok_gsm( SM_CYBORDER, 1);
+ ok_gsm( SM_CXDLGFRAME, 3);
+ ok_gsm( SM_CYDLGFRAME, 3);
+ ok_gsm( SM_CYVTHUMB, ncm.iScrollHeight);
+ ok_gsm( SM_CXHTHUMB, ncm.iScrollHeight);
+ /* SM_CXICON */
+ /* SM_CYICON */
+ /* SM_CXCURSOR */
+ /* SM_CYCURSOR */
+ ok_gsm( SM_CYMENU, ncm.iMenuHeight + 1);
+ ok_gsm( SM_CXFULLSCREEN,
+ GetSystemMetrics( SM_CXMAXIMIZED) - 2 * GetSystemMetrics( SM_CXFRAME));
+ ok_gsm( SM_CYFULLSCREEN,
+ GetSystemMetrics( SM_CYMAXIMIZED) - GetSystemMetrics( SM_CYMIN));
+ /* SM_CYKANJIWINDOW */
+ /* SM_MOUSEPRESENT */
+ ok_gsm( SM_CYVSCROLL, ncm.iScrollHeight);
+ ok_gsm( SM_CXHSCROLL, ncm.iScrollHeight);
+ /* SM_DEBUG */
+ /* SM_SWAPBUTTON */
+ /* SM_RESERVED1 */
+ /* SM_RESERVED2 */
+ /* SM_RESERVED3 */
+ /* SM_RESERVED4 */
+ ok_gsm( SM_CXMIN, 3 * max( CaptionWidth, 8) + GetSystemMetrics( SM_CYSIZE) +
+ 4 + 4 * avcwCaption + 2 * GetSystemMetrics( SM_CXFRAME));
+ ok_gsm( SM_CYMIN, GetSystemMetrics( SM_CYCAPTION) +
+ 2 * GetSystemMetrics( SM_CYFRAME));
+ ok_gsm_2( SM_CXSIZE,
+ ncm.iCaptionWidth, /* classic/standard windows style */
+ GetSystemMetrics( SM_CYCAPTION) - 1 /* WinXP style */
+ );
+ ok_gsm( SM_CYSIZE, ncm.iCaptionHeight);
+ ok_gsm( SM_CXFRAME, ncm.iBorderWidth + 3);
+ ok_gsm( SM_CYFRAME, ncm.iBorderWidth + 3);
+ ok_gsm( SM_CXMINTRACK, GetSystemMetrics( SM_CXMIN));
+ ok_gsm( SM_CYMINTRACK, GetSystemMetrics( SM_CYMIN));
+ /* SM_CXDOUBLECLK */
+ /* SM_CYDOUBLECLK */
+ if( IconSpacing) ok_gsm( SM_CXICONSPACING, IconSpacing);
+ if( IconVerticalSpacing) ok_gsm( SM_CYICONSPACING, IconVerticalSpacing);
+ /* SM_MENUDROPALIGNMENT */
+ /* SM_PENWINDOWS */
+ /* SM_DBCSENABLED */
+ /* SM_CMOUSEBUTTONS */
+ /* SM_SECURE */
+ ok_gsm( SM_CXEDGE, 2);
+ ok_gsm( SM_CYEDGE, 2);
+ ok_gsm( SM_CXMINSPACING, GetSystemMetrics( SM_CXMINIMIZED) + minim.iHorzGap );
+ ok_gsm( SM_CYMINSPACING, GetSystemMetrics( SM_CYMINIMIZED) + minim.iVertGap );
+ /* SM_CXSMICON */
+ /* SM_CYSMICON */
+ ok_gsm( SM_CYSMCAPTION, ncm.iSmCaptionHeight + 1);
+ ok_gsm_3( SM_CXSMSIZE,
+ ncm.iSmCaptionWidth, /* classic/standard windows style */
+ GetSystemMetrics( SM_CYSMCAPTION) - 1, /* WinXP style */
+ smcxsmsize /* winXP seems to cache this value: setnonclientmetric
+ does not change it */
+ );
+ ok_gsm( SM_CYSMSIZE, GetSystemMetrics( SM_CYSMCAPTION) - 1);
+ ok_gsm( SM_CXMENUSIZE, ncm.iMenuWidth);
+ ok_gsm( SM_CYMENUSIZE, ncm.iMenuHeight);
+ /* SM_ARRANGE */
+ ok_gsm( SM_CXMINIMIZED, minim.iWidth + 6);
+ ok_gsm( SM_CYMINIMIZED, GetSystemMetrics( SM_CYCAPTION) + 5);
+ ok_gsm( SM_CXMAXTRACK, GetSystemMetrics( SM_CXSCREEN) +
+ 4 + 2 * GetSystemMetrics( SM_CXFRAME));
+ ok_gsm( SM_CYMAXTRACK, GetSystemMetrics( SM_CYSCREEN) +
+ 4 + 2 * GetSystemMetrics( SM_CYFRAME));
+ /* the next two cannot really be tested as they depend on (application)
+ * toolbars */
+ /* SM_CXMAXIMIZED */
+ /* SM_CYMAXIMIZED */
+ /* SM_NETWORK */
+ /* */
+ /* */
+ /* */
+ /* SM_CLEANBOOT */
+ /* SM_CXDRAG */
+ /* SM_CYDRAG */
+ /* SM_SHOWSOUNDS */
+ ok_gsm( SM_CXMENUCHECK,
+ ((tmMenuFont.tmHeight + tmMenuFont.tmExternalLeading+1)/2)*2-1);
+ ok_gsm( SM_CYMENUCHECK,
+ ((tmMenuFont.tmHeight + tmMenuFont.tmExternalLeading+1)/2)*2-1);
+ /* SM_SLOWMACHINE */
+ /* SM_MIDEASTENABLED */
+ /* SM_MOUSEWHEELPRESENT */
+ /* SM_XVIRTUALSCREEN */
+ /* SM_YVIRTUALSCREEN */
+ /* SM_CXVIRTUALSCREEN */
+ /* SM_CYVIRTUALSCREEN */
+ /* SM_CMONITORS */
+ /* SM_SAMEDISPLAYFORMAT */
+ /* SM_IMMENABLED */
+ /* SM_CXFOCUSBORDER */
+ /* SM_CYFOCUSBORDER */
+ /* SM_TABLETPC */
+ /* SM_MEDIACENTER */
+ /* SM_CMETRICS */
+ /* end of tests */
+ if( gsm_error_ctr ) { /* if any errors where found */
+ trace( "BorderWidth %d CaptionWidth %d CaptionHeight %d IconSpacing %d IconVerticalSpacing %d\n",
+ ncm.iBorderWidth, ncm.iCaptionWidth, ncm.iCaptionHeight, IconSpacing, IconVerticalSpacing);
+ trace( "MenuHeight %d MenuWidth %d ScrollHeight %d ScrollWidth %d SmCaptionHeight %d SmCaptionWidth %d\n",
+ ncm.iMenuHeight, ncm.iMenuWidth, ncm.iScrollHeight, ncm.iScrollWidth, ncm.iSmCaptionHeight, ncm.iSmCaptionWidth);
+ trace( "Captionfontchar width %d MenuFont %ld,%ld CaptionWidth from registry: %d\n",
+ avcwCaption, tmMenuFont.tmHeight, tmMenuFont.tmExternalLeading, CaptionWidth);
+ }
+ ReleaseDC( 0, hdc);
+}
+
+START_TEST(sysparams)
+{
+ int argc;
+ char** argv;
+ WNDCLASSA wc;
+ MSG msg;
+ HANDLE hThread;
+ DWORD dwThreadId;
+ HANDLE hInstance = GetModuleHandleA( NULL );
+
+ hdc = GetDC(0);
+ dpi = GetDeviceCaps( hdc, LOGPIXELSY);
+ iswin9x = GetVersion() & 0x80000000;
+
+ /* This test requires interactivity, if we don't have it, give up */
+ if (!SystemParametersInfoA( SPI_SETBEEP, TRUE, 0, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE ) &&
+ GetLastError()==ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION) return;
+
+ argc = winetest_get_mainargs(&argv);
+ strict=(argc >= 3 && strcmp(argv[2],"strict")==0);
+ trace("strict=%d\n",strict);
+
+ trace("testing GetSystemMetrics with your current desktop settings\n");
+ test_GetSystemMetrics( );
+
+ change_counter = 0;
+ change_last_param = 0;
+
+ wc.lpszClassName = "SysParamsTestClass";
+ wc.lpfnWndProc = SysParamsTestWndProc;
+ wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
+ wc.hInstance = hInstance;
+ wc.hIcon = LoadIconA( 0, (LPSTR)IDI_APPLICATION );
+ wc.hCursor = LoadCursorA( 0, (LPSTR)IDC_ARROW );
+ wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1 );
+ wc.lpszMenuName = 0;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ RegisterClassA( &wc );
+
+ ghTestWnd = CreateWindowA( "SysParamsTestClass", "Test System Parameters Application",
+ WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, 0, 0, hInstance, NULL );
+
+ hThread = CreateThread( NULL, 0, SysParamsThreadFunc, 0, 0, &dwThreadId );
+ assert( hThread );
+ CloseHandle( hThread );
+
+ while( GetMessageA( &msg, 0, 0, 0 )) {
+ TranslateMessage( &msg );
+ DispatchMessageA( &msg );
+ }
+ ReleaseDC( 0, hdc);
+
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_class(void);
+extern void func_clipboard(void);
+extern void func_dce(void);
+extern void func_dde(void);
+extern void func_dialog(void);
+extern void func_edit(void);
+extern void func_generated(void);
+extern void func_input(void);
+extern void func_listbox(void);
+extern void func_menu(void);
+extern void func_monitor(void);
+extern void func_msg(void);
+extern void func_resource(void);
+extern void func_sysparams(void);
+extern void func_text(void);
+extern void func_win(void);
+extern void func_winstation(void);
+extern void func_wsprintf(void);
+
+const struct test winetest_testlist[] =
+{
+ { "class", func_class },
+ { "clipboard", func_clipboard },
+ { "dce", func_dce },
+ { "dde", func_dde },
+ { "dialog", func_dialog },
+ { "edit", func_edit },
+// { "generated", func_generated },
+ { "input", func_input },
+ { "listbox", func_listbox },
+ { "menu", func_menu },
+ { "monitor", func_monitor },
+ { "msg", func_msg },
+ { "resource", func_resource },
+ { "sysparams", func_sysparams },
+ { "text", func_text },
+ { "win", func_win },
+ { "winstation", func_winstation },
+ { "wsprintf", func_wsprintf },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * DrawText tests
+ *
+ * Copyright (c) 2004 Zach Gorman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ */
+
+#include <assert.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+
+
+static void test_DrawTextCalcRect(void)
+{
+ HWND hwnd;
+ HDC hdc;
+ HFONT hFont, hOldFont;
+ LOGFONTA lf;
+ const char text[] = "Example text for testing DrawText in "
+ "MM_HIENGLISH mode";
+ const WCHAR textW[] = {'W','i','d','e',' ','c','h','a','r',' ',
+ 's','t','r','i','n','g','\0'};
+ const WCHAR emptystringW[] = { 0 };
+ INT textlen,textheight;
+ RECT rect = { 0, 0, 100, 0 };
+ BOOL ret;
+
+ /* Initialization */
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP,
+ 0, 0, 200, 200, 0, 0, 0, NULL);
+ ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
+ hdc = GetDC(hwnd);
+ ok(hdc != 0, "GetDC error %lu\n", GetLastError());
+ trace("hdc %p\n", hdc);
+ textlen = lstrlenA(text);
+
+ /* LOGFONT initialization */
+ memset(&lf, 0, sizeof(lf));
+ lf.lfCharSet = ANSI_CHARSET;
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf.lfWeight = FW_DONTCARE;
+ lf.lfHeight = 0; /* mapping mode dependent */
+ lf.lfQuality = DEFAULT_QUALITY;
+ lstrcpyA(lf.lfFaceName, "Arial");
+
+ /* DrawText in MM_HIENGLISH with DT_CALCRECT */
+ SetMapMode(hdc, MM_HIENGLISH);
+ lf.lfHeight = 100 * 9 / 72; /* 9 point */
+ hFont = CreateFontIndirectA(&lf);
+ ok(hFont != 0, "CreateFontIndirectA error %lu\n",
+ GetLastError());
+ hOldFont = SelectObject(hdc, hFont);
+
+ textheight = DrawTextA(hdc, text, textlen, &rect, DT_CALCRECT |
+ DT_EXTERNALLEADING | DT_WORDBREAK | DT_NOCLIP | DT_LEFT |
+ DT_NOPREFIX);
+ ok( textheight, "DrawTextA error %lu\n", GetLastError());
+
+ trace("MM_HIENGLISH rect.bottom %ld\n", rect.bottom);
+ todo_wine ok(rect.bottom < 0, "In MM_HIENGLISH, DrawText with "
+ "DT_CALCRECT should return a negative rectangle bottom. "
+ "(bot=%ld)\n", rect.bottom);
+
+ SelectObject(hdc, hOldFont);
+ ret = DeleteObject(hFont);
+ ok( ret, "DeleteObject error %lu\n", GetLastError());
+
+
+ /* DrawText in MM_TEXT with DT_CALCRECT */
+ SetMapMode(hdc, MM_TEXT);
+ lf.lfHeight = -MulDiv(9, GetDeviceCaps(hdc,
+ LOGPIXELSY), 72); /* 9 point */
+ hFont = CreateFontIndirectA(&lf);
+ ok(hFont != 0, "CreateFontIndirectA error %lu\n",
+ GetLastError());
+ hOldFont = SelectObject(hdc, hFont);
+
+ textheight = DrawTextA(hdc, text, textlen, &rect, DT_CALCRECT |
+ DT_EXTERNALLEADING | DT_WORDBREAK | DT_NOCLIP | DT_LEFT |
+ DT_NOPREFIX);
+ ok( textheight, "DrawTextA error %lu\n", GetLastError());
+
+ trace("MM_TEXT rect.bottom %ld\n", rect.bottom);
+ ok(rect.bottom > 0, "In MM_TEXT, DrawText with DT_CALCRECT "
+ "should return a positive rectangle bottom. (bot=%ld)\n",
+ rect.bottom);
+
+ /* empty or null text should in some cases calc an empty rectangle */
+ /* note: testing the function's return value is useless, it differs
+ * ( 0 or 1) on every Windows version I tried */
+ SetRect( &rect, 10,10, 100, 100);
+ textheight = DrawTextExA(hdc, (char *) text, 0, &rect, DT_CALCRECT, NULL );
+ ok( !(rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should NOT be empty.\n");
+ SetRect( &rect, 10,10, 100, 100);
+ SetLastError( 0);
+ textheight = DrawTextExA(hdc, "", -1, &rect, DT_CALCRECT, NULL );
+ ok( (rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should be empty.\n");
+ SetRect( &rect, 10,10, 100, 100);
+ SetLastError( 0);
+ textheight = DrawTextExA(hdc, NULL, -1, &rect, DT_CALCRECT, NULL );
+ ok( (rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should be empty.\n");
+ SetRect( &rect, 10,10, 100, 100);
+ textheight = DrawTextExA(hdc, NULL, 0, &rect, DT_CALCRECT, NULL );
+ ok( !(rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should NOT be empty.\n");
+
+ /* Wide char versions */
+ SetRect( &rect, 10,10, 100, 100);
+ SetLastError( 0);
+ textheight = DrawTextExW(hdc, (WCHAR *) textW, 0, &rect, DT_CALCRECT, NULL );
+ if( GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) {
+ ok( !(rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should NOT be empty.\n");
+ SetRect( &rect, 10,10, 100, 100);
+ textheight = DrawTextExW(hdc, (WCHAR *) emptystringW, -1, &rect, DT_CALCRECT, NULL );
+ ok( (rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should be empty.\n");
+ SetRect( &rect, 10,10, 100, 100);
+ textheight = DrawTextExW(hdc, NULL, -1, &rect, DT_CALCRECT, NULL );
+ ok( !(rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should NOT be empty.\n");
+ SetRect( &rect, 10,10, 100, 100);
+ textheight = DrawTextExW(hdc, NULL, 0, &rect, DT_CALCRECT, NULL );
+ ok( !(rect.left == rect.right && rect.bottom == rect.top),
+ "rectangle should NOT be empty.\n");
+ }
+
+ SelectObject(hdc, hOldFont);
+ ret = DeleteObject(hFont);
+ ok( ret, "DeleteObject error %lu\n", GetLastError());
+
+ /* Clean up */
+ ret = ReleaseDC(hwnd, hdc);
+ ok( ret, "ReleaseDC error %lu\n", GetLastError());
+ ret = DestroyWindow(hwnd);
+ ok( ret, "DestroyWindow error %lu\n", GetLastError());
+}
+
+/* replace tabs by \t */
+static void strfmt( char *str, char *strout)
+{
+ unsigned int i,j ;
+ for(i=0,j=0;i<=strlen(str);i++,j++)
+ if((strout[j]=str[i])=='\t') {
+ strout[j++]='\\';
+ strout[j]='t';
+ }
+}
+
+
+#define TABTEST( tabval, tabcount, string, _exp) \
+{ int i,x_act, x_exp; char strdisp[64];\
+ for(i=0;i<8;i++) tabs[i]=(i+1)*(tabval); \
+ extent = GetTabbedTextExtentA( hdc, string, strlen( string), (tabcount), tabs); \
+ strfmt( string, strdisp); \
+ /* trace( "Extent is %08lx\n", extent); */\
+ x_act = LOWORD( extent); \
+ x_exp = (_exp); \
+ ok( x_act == x_exp, "Test case \"%s\". Text extent is %d, expected %d tab %d tabcount %d\n", \
+ strdisp, x_act, x_exp, tabval, tabcount); \
+} \
+
+
+static void test_TabbedText(void)
+{
+ HWND hwnd;
+ HDC hdc;
+ BOOL ret;
+ TEXTMETRICA tm;
+ DWORD extent;
+ INT tabs[8], cx, cy, tab, tabcount,t,align;
+
+ /* Initialization */
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP,
+ 0, 0, 200, 200, 0, 0, 0, NULL);
+ ok(hwnd != 0, "CreateWindowExA error %lu\n", GetLastError());
+ hdc = GetDC(hwnd);
+ ok(hdc != 0, "GetDC error %lu\n", GetLastError());
+
+ ret = GetTextMetricsA( hdc, &tm);
+ ok( ret, "GetTextMetrics error %lu\n", GetLastError());
+
+ extent = GetTabbedTextExtentA( hdc, "x", 1, 1, tabs);
+ cx = LOWORD( extent);
+ cy = HIWORD( extent);
+ trace( "cx is %d cy is %d\n", cx, cy);
+
+ align=1;
+ for( t=-1; t<=1; t++) { /* slightly adjust the 4 char tabstop, to
+ catch the one off errors */
+ tab = (cx *4 + t);
+ /* test the special case tabcount =1 and the general array (80 of tabs */
+ for( tabcount = 1; tabcount <= 8; tabcount +=7) {
+ TABTEST( align * tab, tabcount, "\t", tab)
+ TABTEST( align * tab, tabcount, "xxx\t", tab)
+ TABTEST( align * tab, tabcount, "\tx", tab+cx)
+ TABTEST( align * tab, tabcount, "\t\t", tab*2)
+ TABTEST( align * tab, tabcount, "\tx\t", tab*2)
+ TABTEST( align * tab, tabcount, "x\tx", tab+cx)
+ TABTEST( align * tab, tabcount, "xx\tx", tab+cx)
+ TABTEST( align * tab, tabcount, "xxx\tx", tab+cx)
+ TABTEST( align * tab, tabcount, "xxxx\tx", t>0 ? tab + cx : 2*tab+cx)
+ TABTEST( align * tab, tabcount, "xxxxx\tx", 2*tab+cx)
+ }
+ }
+ align=-1;
+ for( t=-1; t<=1; t++) { /* slightly adjust the 4 char tabstop, to
+ catch the one off errors */
+ tab = (cx *4 + t);
+ /* test the special case tabcount =1 and the general array (8) of tabs */
+ for( tabcount = 1; tabcount <= 8; tabcount +=7) {
+ TABTEST( align * tab, tabcount, "\t", tab)
+ TABTEST( align * tab, tabcount, "xxx\t", tab)
+ TABTEST( align * tab, tabcount, "\tx", tab)
+ TABTEST( align * tab, tabcount, "\t\t", tab*2)
+ TABTEST( align * tab, tabcount, "\tx\t", tab*2)
+ TABTEST( align * tab, tabcount, "x\tx", tab)
+ TABTEST( align * tab, tabcount, "xx\tx", tab)
+ TABTEST( align * tab, tabcount, "xxx\tx", 4 * cx >= tab ? 2*tab :tab)
+ TABTEST( align * tab, tabcount, "xxxx\tx", 2*tab)
+ TABTEST( align * tab, tabcount, "xxxxx\tx", 2*tab)
+ }
+ }
+
+
+}
+
+START_TEST(text)
+{
+ test_TabbedText();
+ test_DrawTextCalcRect();
+}
--- /dev/null
+<module name="user32_winetest" type="win32cui" installbase="bin" installname="user32_winetest.exe" allowwarnings="true">
+ <include base="user32_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>ntdll</library>
+ <library>user32</library>
+ <library>gdi32</library>
+ <library>advapi32</library>
+ <library>kernel32</library>
+ <file>class.c</file>
+ <file>clipboard.c</file>
+ <file>dce.c</file>
+ <file>dde.c</file>
+ <file>dialog.c</file>
+ <file>edit.c</file>
+ <file>input.c</file>
+ <file>listbox.c</file>
+ <file>menu.c</file>
+ <file>monitor.c</file>
+ <file>msg.c</file>
+ <file>resource.c</file>
+ <file>sysparams.c</file>
+ <file>text.c</file>
+ <file>win.c</file>
+ <file>winstation.c</file>
+ <file>wsprintf.c</file>
+ <file>testlist.c</file>
+ <file>resource.rc</file>
+</module>
--- /dev/null
+/*
+ * Unit tests for window handling
+ *
+ * Copyright 2002 Bill Medland
+ * Copyright 2002 Alexandre Julliard
+ * Copyright 2003 Dmitry Timoshkov
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* To get ICON_SMALL2 with the MSVC headers */
+#define _WIN32_WINNT 0x0501
+
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/test.h"
+
+#ifndef SPI_GETDESKWALLPAPER
+#define SPI_GETDESKWALLPAPER 0x0073
+#endif
+
+#define MAKEINTATOMA(atom) ((LPCSTR)((ULONG_PTR)((WORD)(atom))))
+
+#define LONG_PTR INT_PTR
+#define ULONG_PTR UINT_PTR
+
+void dump_region(HRGN hrgn);
+
+static HWND (WINAPI *pGetAncestor)(HWND,UINT);
+static BOOL (WINAPI *pGetWindowInfo)(HWND,WINDOWINFO*);
+
+static BOOL test_lbuttondown_flag;
+static HWND hwndMessage;
+static HWND hwndMain, hwndMain2;
+static HHOOK hhook;
+
+static const char* szAWRClass = "Winsize";
+static HMENU hmenu;
+
+#define COUNTOF(arr) (sizeof(arr)/sizeof(arr[0]))
+
+/* try to make sure pending X events have been processed before continuing */
+static void flush_events(void)
+{
+ MSG msg;
+ int diff = 100;
+ DWORD time = GetTickCount() + diff;
+
+ while (diff > 0)
+ {
+ MsgWaitForMultipleObjects( 0, NULL, FALSE, diff, QS_ALLINPUT );
+ while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
+ diff = time - GetTickCount();
+ }
+}
+
+/* check the values returned by the various parent/owner functions on a given window */
+static void check_parents( HWND hwnd, HWND ga_parent, HWND gwl_parent, HWND get_parent,
+ HWND gw_owner, HWND ga_root, HWND ga_root_owner )
+{
+ HWND res;
+
+ if (pGetAncestor)
+ {
+ res = pGetAncestor( hwnd, GA_PARENT );
+ ok( res == ga_parent, "Wrong result for GA_PARENT %p expected %p\n", res, ga_parent );
+ }
+ res = (HWND)GetWindowLongPtrA( hwnd, GWLP_HWNDPARENT );
+ ok( res == gwl_parent, "Wrong result for GWL_HWNDPARENT %p expected %p\n", res, gwl_parent );
+ res = GetParent( hwnd );
+ ok( res == get_parent, "Wrong result for GetParent %p expected %p\n", res, get_parent );
+ res = GetWindow( hwnd, GW_OWNER );
+ ok( res == gw_owner, "Wrong result for GW_OWNER %p expected %p\n", res, gw_owner );
+ if (pGetAncestor)
+ {
+ res = pGetAncestor( hwnd, GA_ROOT );
+ ok( res == ga_root, "Wrong result for GA_ROOT %p expected %p\n", res, ga_root );
+ res = pGetAncestor( hwnd, GA_ROOTOWNER );
+ ok( res == ga_root_owner, "Wrong result for GA_ROOTOWNER %p expected %p\n", res, ga_root_owner );
+ }
+}
+
+BOOL CALLBACK EnumChildProc( HWND hwndChild, LPARAM lParam)
+{
+ (*(LPINT)lParam)++;
+ trace("EnumChildProc on %p\n", hwndChild);
+ if (*(LPINT)lParam > 1) return FALSE;
+ return TRUE;
+}
+
+/* will search for the given window */
+BOOL CALLBACK EnumChildProc1( HWND hwndChild, LPARAM lParam)
+{
+ trace("EnumChildProc1 on %p\n", hwndChild);
+ if ((HWND)lParam == hwndChild) return FALSE;
+ return TRUE;
+}
+
+static HWND create_tool_window( LONG style, HWND parent )
+{
+ HWND ret = CreateWindowExA(0, "ToolWindowClass", "Tool window 1", style,
+ 0, 0, 100, 100, parent, 0, 0, NULL );
+ ok( ret != 0, "Creation failed\n" );
+ return ret;
+}
+
+/* test parent and owner values for various combinations */
+static void test_parent_owner(void)
+{
+ LONG style;
+ HWND test, owner, ret;
+ HWND desktop = GetDesktopWindow();
+ HWND child = create_tool_window( WS_CHILD, hwndMain );
+ INT numChildren;
+
+ trace( "main window %p main2 %p desktop %p child %p\n", hwndMain, hwndMain2, desktop, child );
+
+ /* child without parent, should fail */
+ test = CreateWindowExA(0, "ToolWindowClass", "Tool window 1",
+ WS_CHILD, 0, 0, 100, 100, 0, 0, 0, NULL );
+ ok( !test, "WS_CHILD without parent created\n" );
+
+ /* desktop window */
+ check_parents( desktop, 0, 0, 0, 0, 0, 0 );
+ style = GetWindowLongA( desktop, GWL_STYLE );
+ ok( !SetWindowLongA( desktop, GWL_STYLE, WS_POPUP ), "Set GWL_STYLE on desktop succeeded\n" );
+ ok( !SetWindowLongA( desktop, GWL_STYLE, 0 ), "Set GWL_STYLE on desktop succeeded\n" );
+ ok( GetWindowLongA( desktop, GWL_STYLE ) == style, "Desktop style changed\n" );
+
+ /* normal child window */
+ test = create_tool_window( WS_CHILD, hwndMain );
+ trace( "created child %p\n", test );
+ check_parents( test, hwndMain, hwndMain, hwndMain, 0, hwndMain, hwndMain );
+ SetWindowLongA( test, GWL_STYLE, 0 );
+ check_parents( test, hwndMain, hwndMain, 0, 0, hwndMain, test );
+ SetWindowLongA( test, GWL_STYLE, WS_POPUP );
+ check_parents( test, hwndMain, hwndMain, 0, 0, hwndMain, test );
+ SetWindowLongA( test, GWL_STYLE, WS_POPUP|WS_CHILD );
+ check_parents( test, hwndMain, hwndMain, 0, 0, hwndMain, test );
+ SetWindowLongA( test, GWL_STYLE, WS_CHILD );
+ DestroyWindow( test );
+
+ /* normal child window with WS_MAXIMIZE */
+ test = create_tool_window( WS_CHILD | WS_MAXIMIZE, hwndMain );
+ DestroyWindow( test );
+
+ /* normal child window with WS_THICKFRAME */
+ test = create_tool_window( WS_CHILD | WS_THICKFRAME, hwndMain );
+ DestroyWindow( test );
+
+ /* popup window with WS_THICKFRAME */
+ test = create_tool_window( WS_POPUP | WS_THICKFRAME, hwndMain );
+ DestroyWindow( test );
+
+ /* child of desktop */
+ test = create_tool_window( WS_CHILD, desktop );
+ trace( "created child of desktop %p\n", test );
+ check_parents( test, desktop, 0, desktop, 0, test, desktop );
+ SetWindowLongA( test, GWL_STYLE, WS_POPUP );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ SetWindowLongA( test, GWL_STYLE, 0 );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ DestroyWindow( test );
+
+ /* child of desktop with WS_MAXIMIZE */
+ test = create_tool_window( WS_CHILD | WS_MAXIMIZE, desktop );
+ DestroyWindow( test );
+
+ /* child of desktop with WS_MINIMIZE */
+ test = create_tool_window( WS_CHILD | WS_MINIMIZE, desktop );
+ DestroyWindow( test );
+
+ /* child of child */
+ test = create_tool_window( WS_CHILD, child );
+ trace( "created child of child %p\n", test );
+ check_parents( test, child, child, child, 0, hwndMain, hwndMain );
+ SetWindowLongA( test, GWL_STYLE, 0 );
+ check_parents( test, child, child, 0, 0, hwndMain, test );
+ SetWindowLongA( test, GWL_STYLE, WS_POPUP );
+ check_parents( test, child, child, 0, 0, hwndMain, test );
+ DestroyWindow( test );
+
+ /* child of child with WS_MAXIMIZE */
+ test = create_tool_window( WS_CHILD | WS_MAXIMIZE, child );
+ DestroyWindow( test );
+
+ /* child of child with WS_MINIMIZE */
+ test = create_tool_window( WS_CHILD | WS_MINIMIZE, child );
+ DestroyWindow( test );
+
+ /* not owned top-level window */
+ test = create_tool_window( 0, 0 );
+ trace( "created top-level %p\n", test );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ SetWindowLongA( test, GWL_STYLE, WS_POPUP );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ SetWindowLongA( test, GWL_STYLE, WS_CHILD );
+ check_parents( test, desktop, 0, desktop, 0, test, desktop );
+ DestroyWindow( test );
+
+ /* not owned top-level window with WS_MAXIMIZE */
+ test = create_tool_window( WS_MAXIMIZE, 0 );
+ DestroyWindow( test );
+
+ /* owned top-level window */
+ test = create_tool_window( 0, hwndMain );
+ trace( "created owned top-level %p\n", test );
+ check_parents( test, desktop, hwndMain, 0, hwndMain, test, test );
+ SetWindowLongA( test, GWL_STYLE, WS_POPUP );
+ check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain );
+ SetWindowLongA( test, GWL_STYLE, WS_CHILD );
+ check_parents( test, desktop, hwndMain, desktop, hwndMain, test, desktop );
+ DestroyWindow( test );
+
+ /* owned top-level window with WS_MAXIMIZE */
+ test = create_tool_window( WS_MAXIMIZE, hwndMain );
+ DestroyWindow( test );
+
+ /* not owned popup */
+ test = create_tool_window( WS_POPUP, 0 );
+ trace( "created popup %p\n", test );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ SetWindowLongA( test, GWL_STYLE, WS_CHILD );
+ check_parents( test, desktop, 0, desktop, 0, test, desktop );
+ SetWindowLongA( test, GWL_STYLE, 0 );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ DestroyWindow( test );
+
+ /* not owned popup with WS_MAXIMIZE */
+ test = create_tool_window( WS_POPUP | WS_MAXIMIZE, 0 );
+ DestroyWindow( test );
+
+ /* owned popup */
+ test = create_tool_window( WS_POPUP, hwndMain );
+ trace( "created owned popup %p\n", test );
+ check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain );
+ SetWindowLongA( test, GWL_STYLE, WS_CHILD );
+ check_parents( test, desktop, hwndMain, desktop, hwndMain, test, desktop );
+ SetWindowLongA( test, GWL_STYLE, 0 );
+ check_parents( test, desktop, hwndMain, 0, hwndMain, test, test );
+ DestroyWindow( test );
+
+ /* owned popup with WS_MAXIMIZE */
+ test = create_tool_window( WS_POPUP | WS_MAXIMIZE, hwndMain );
+ DestroyWindow( test );
+
+ /* top-level window owned by child (same as owned by top-level) */
+ test = create_tool_window( 0, child );
+ trace( "created top-level owned by child %p\n", test );
+ check_parents( test, desktop, hwndMain, 0, hwndMain, test, test );
+ DestroyWindow( test );
+
+ /* top-level window owned by child (same as owned by top-level) with WS_MAXIMIZE */
+ test = create_tool_window( WS_MAXIMIZE, child );
+ DestroyWindow( test );
+
+ /* popup owned by desktop (same as not owned) */
+ test = create_tool_window( WS_POPUP, desktop );
+ trace( "created popup owned by desktop %p\n", test );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ DestroyWindow( test );
+
+ /* popup owned by desktop (same as not owned) with WS_MAXIMIZE */
+ test = create_tool_window( WS_POPUP | WS_MAXIMIZE, desktop );
+ DestroyWindow( test );
+
+ /* popup owned by child (same as owned by top-level) */
+ test = create_tool_window( WS_POPUP, child );
+ trace( "created popup owned by child %p\n", test );
+ check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain );
+ DestroyWindow( test );
+
+ /* popup owned by child (same as owned by top-level) with WS_MAXIMIZE */
+ test = create_tool_window( WS_POPUP | WS_MAXIMIZE, child );
+ DestroyWindow( test );
+
+ /* not owned popup with WS_CHILD (same as WS_POPUP only) */
+ test = create_tool_window( WS_POPUP | WS_CHILD, 0 );
+ trace( "created WS_CHILD popup %p\n", test );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ DestroyWindow( test );
+
+ /* not owned popup with WS_CHILD | WS_MAXIMIZE (same as WS_POPUP only) */
+ test = create_tool_window( WS_POPUP | WS_CHILD | WS_MAXIMIZE, 0 );
+ DestroyWindow( test );
+
+ /* owned popup with WS_CHILD (same as WS_POPUP only) */
+ test = create_tool_window( WS_POPUP | WS_CHILD, hwndMain );
+ trace( "created owned WS_CHILD popup %p\n", test );
+ check_parents( test, desktop, hwndMain, hwndMain, hwndMain, test, hwndMain );
+ DestroyWindow( test );
+
+ /* owned popup with WS_CHILD (same as WS_POPUP only) with WS_MAXIMIZE */
+ test = create_tool_window( WS_POPUP | WS_CHILD | WS_MAXIMIZE, hwndMain );
+ DestroyWindow( test );
+
+ /******************** parent changes *************************/
+ trace( "testing parent changes\n" );
+
+ /* desktop window */
+ check_parents( desktop, 0, 0, 0, 0, 0, 0 );
+#if 0 /* this test succeeds on NT but crashes on win9x systems */
+ ret = (HWND)SetWindowLongA( test, GWL_HWNDPARENT, (LONG_PTR)hwndMain2 );
+ ok( !ret, "Set GWL_HWNDPARENT succeeded on desktop\n" );
+ check_parents( desktop, 0, 0, 0, 0, 0, 0 );
+ ok( !SetParent( desktop, hwndMain ), "SetParent succeeded on desktop\n" );
+ check_parents( desktop, 0, 0, 0, 0, 0, 0 );
+#endif
+ /* normal child window */
+ test = create_tool_window( WS_CHILD, hwndMain );
+ trace( "created child %p\n", test );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)hwndMain2 );
+ ok( ret == hwndMain, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain );
+ check_parents( test, hwndMain2, hwndMain2, hwndMain2, 0, hwndMain2, hwndMain2 );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)child );
+ ok( ret == hwndMain2, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain2 );
+ check_parents( test, child, child, child, 0, hwndMain, hwndMain );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)desktop );
+ ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child );
+ check_parents( test, desktop, 0, desktop, 0, test, desktop );
+
+ /* window is now child of desktop so GWLP_HWNDPARENT changes owner from now on */
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)child );
+ ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret );
+ check_parents( test, desktop, child, desktop, child, test, desktop );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, 0 );
+ ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child );
+ check_parents( test, desktop, 0, desktop, 0, test, desktop );
+ DestroyWindow( test );
+
+ /* not owned top-level window */
+ test = create_tool_window( 0, 0 );
+ trace( "created top-level %p\n", test );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)hwndMain2 );
+ ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret );
+ check_parents( test, desktop, hwndMain2, 0, hwndMain2, test, test );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)child );
+ ok( ret == hwndMain2, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain2 );
+ check_parents( test, desktop, child, 0, child, test, test );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, 0 );
+ ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ DestroyWindow( test );
+
+ /* not owned popup */
+ test = create_tool_window( WS_POPUP, 0 );
+ trace( "created popup %p\n", test );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)hwndMain2 );
+ ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret );
+ check_parents( test, desktop, hwndMain2, hwndMain2, hwndMain2, test, hwndMain2 );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (LONG_PTR)child );
+ ok( ret == hwndMain2, "GWL_HWNDPARENT return value %p expected %p\n", ret, hwndMain2 );
+ check_parents( test, desktop, child, child, child, test, hwndMain );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, 0 );
+ ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child );
+ check_parents( test, desktop, 0, 0, 0, test, test );
+ DestroyWindow( test );
+
+ /* normal child window */
+ test = create_tool_window( WS_CHILD, hwndMain );
+ trace( "created child %p\n", test );
+
+ ret = SetParent( test, desktop );
+ ok( ret == hwndMain, "SetParent return value %p expected %p\n", ret, hwndMain );
+ check_parents( test, desktop, 0, desktop, 0, test, desktop );
+
+ ret = SetParent( test, child );
+ ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop );
+ check_parents( test, child, child, child, 0, hwndMain, hwndMain );
+
+ ret = SetParent( test, hwndMain2 );
+ ok( ret == child, "SetParent return value %p expected %p\n", ret, child );
+ check_parents( test, hwndMain2, hwndMain2, hwndMain2, 0, hwndMain2, hwndMain2 );
+ DestroyWindow( test );
+
+ /* not owned top-level window */
+ test = create_tool_window( 0, 0 );
+ trace( "created top-level %p\n", test );
+
+ ret = SetParent( test, child );
+ ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop );
+ check_parents( test, child, child, 0, 0, hwndMain, test );
+ DestroyWindow( test );
+
+ /* owned popup */
+ test = create_tool_window( WS_POPUP, hwndMain2 );
+ trace( "created owned popup %p\n", test );
+
+ ret = SetParent( test, child );
+ ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop );
+ check_parents( test, child, child, hwndMain2, hwndMain2, hwndMain, hwndMain2 );
+
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (ULONG_PTR)hwndMain );
+ ok( ret == child, "GWL_HWNDPARENT return value %p expected %p\n", ret, child );
+ check_parents( test, hwndMain, hwndMain, hwndMain2, hwndMain2, hwndMain, hwndMain2 );
+ DestroyWindow( test );
+
+ /**************** test owner destruction *******************/
+
+ /* owned child popup */
+ owner = create_tool_window( 0, 0 );
+ test = create_tool_window( WS_POPUP, owner );
+ trace( "created owner %p and popup %p\n", owner, test );
+ ret = SetParent( test, child );
+ ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop );
+ check_parents( test, child, child, owner, owner, hwndMain, owner );
+ /* window is now child of 'child' but owned by 'owner' */
+ DestroyWindow( owner );
+ ok( IsWindow(test), "Window %p destroyed by owner destruction\n", test );
+ /* Win98 doesn't pass this test. It doesn't allow a destroyed owner,
+ * while Win95, Win2k, WinXP do.
+ */
+ /*check_parents( test, child, child, owner, owner, hwndMain, owner );*/
+ ok( !IsWindow(owner), "Owner %p not destroyed\n", owner );
+ DestroyWindow(test);
+
+ /* owned top-level popup */
+ owner = create_tool_window( 0, 0 );
+ test = create_tool_window( WS_POPUP, owner );
+ trace( "created owner %p and popup %p\n", owner, test );
+ check_parents( test, desktop, owner, owner, owner, test, owner );
+ DestroyWindow( owner );
+ ok( !IsWindow(test), "Window %p not destroyed by owner destruction\n", test );
+
+ /* top-level popup owned by child */
+ owner = create_tool_window( WS_CHILD, hwndMain2 );
+ test = create_tool_window( WS_POPUP, 0 );
+ trace( "created owner %p and popup %p\n", owner, test );
+ ret = (HWND)SetWindowLongPtrA( test, GWLP_HWNDPARENT, (ULONG_PTR)owner );
+ ok( ret == 0, "GWL_HWNDPARENT return value %p expected 0\n", ret );
+ check_parents( test, desktop, owner, owner, owner, test, hwndMain2 );
+ DestroyWindow( owner );
+ ok( IsWindow(test), "Window %p destroyed by owner destruction\n", test );
+ ok( !IsWindow(owner), "Owner %p not destroyed\n", owner );
+ /* Win98 doesn't pass this test. It doesn't allow a destroyed owner,
+ * while Win95, Win2k, WinXP do.
+ */
+ /*check_parents( test, desktop, owner, owner, owner, test, owner );*/
+ DestroyWindow(test);
+
+ /* final cleanup */
+ DestroyWindow(child);
+
+
+ owner = create_tool_window( WS_OVERLAPPED, 0 );
+ test = create_tool_window( WS_POPUP, desktop );
+
+ ok( !GetWindow( test, GW_OWNER ), "Wrong owner window\n" );
+ numChildren = 0;
+ ok( !EnumChildWindows( owner, EnumChildProc, (LPARAM)&numChildren ),
+ "EnumChildWindows should have returned FALSE\n" );
+ ok( numChildren == 0, "numChildren should be 0 got %d\n", numChildren );
+
+ SetWindowLongA( test, GWL_STYLE, (GetWindowLongA( test, GWL_STYLE ) & ~WS_POPUP) | WS_CHILD );
+ ret = SetParent( test, owner );
+ ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop );
+
+ numChildren = 0;
+ ok( EnumChildWindows( owner, EnumChildProc, (LPARAM)&numChildren ),
+ "EnumChildWindows should have returned TRUE\n" );
+ ok( numChildren == 1, "numChildren should be 1 got %d\n", numChildren );
+
+ child = create_tool_window( WS_CHILD, owner );
+ numChildren = 0;
+ ok( !EnumChildWindows( owner, EnumChildProc, (LPARAM)&numChildren ),
+ "EnumChildWindows should have returned FALSE\n" );
+ ok( numChildren == 2, "numChildren should be 2 got %d\n", numChildren );
+ DestroyWindow( child );
+
+ child = create_tool_window( WS_VISIBLE | WS_OVERLAPPEDWINDOW, owner );
+ ok( GetWindow( child, GW_OWNER ) == owner, "Wrong owner window\n" );
+ numChildren = 0;
+ ok( EnumChildWindows( owner, EnumChildProc, (LPARAM)&numChildren ),
+ "EnumChildWindows should have returned TRUE\n" );
+ ok( numChildren == 1, "numChildren should be 1 got %d\n", numChildren );
+
+ ret = SetParent( child, owner );
+ ok( GetWindow( child, GW_OWNER ) == owner, "Wrong owner window\n" );
+ ok( ret == desktop, "SetParent return value %p expected %p\n", ret, desktop );
+ numChildren = 0;
+ ok( !EnumChildWindows( owner, EnumChildProc, (LPARAM)&numChildren ),
+ "EnumChildWindows should have returned FALSE\n" );
+ ok( numChildren == 2, "numChildren should be 2 got %d\n", numChildren );
+
+ ret = SetParent( child, NULL );
+ ok( GetWindow( child, GW_OWNER ) == owner, "Wrong owner window\n" );
+ ok( ret == owner, "SetParent return value %p expected %p\n", ret, owner );
+ numChildren = 0;
+ ok( EnumChildWindows( owner, EnumChildProc, (LPARAM)&numChildren ),
+ "EnumChildWindows should have returned TRUE\n" );
+ ok( numChildren == 1, "numChildren should be 1 got %d\n", numChildren );
+
+ /* even GW_OWNER == owner it's still a desktop's child */
+ ok( !EnumChildWindows( desktop, EnumChildProc1, (LPARAM)child ),
+ "EnumChildWindows should have found %p and returned FALSE\n", child );
+
+ DestroyWindow( child );
+ child = create_tool_window( WS_VISIBLE | WS_OVERLAPPEDWINDOW, NULL );
+
+ ok( !EnumChildWindows( desktop, EnumChildProc1, (LPARAM)child ),
+ "EnumChildWindows should have found %p and returned FALSE\n", child );
+
+ DestroyWindow( child );
+ DestroyWindow( test );
+ DestroyWindow( owner );
+}
+
+
+static LRESULT WINAPI main_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO* minmax = (MINMAXINFO *)lparam;
+
+ trace("hwnd %p, WM_GETMINMAXINFO, %08x, %08lx\n", hwnd, wparam, lparam);
+ trace("ptReserved (%ld,%ld), ptMaxSize (%ld,%ld), ptMaxPosition (%ld,%ld)\n"
+ " ptMinTrackSize (%ld,%ld), ptMaxTrackSize (%ld,%ld)\n",
+ minmax->ptReserved.x, minmax->ptReserved.y,
+ minmax->ptMaxSize.x, minmax->ptMaxSize.y,
+ minmax->ptMaxPosition.x, minmax->ptMaxPosition.y,
+ minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y,
+ minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y);
+ SetWindowLongPtrA(hwnd, GWLP_USERDATA, 0x20031021);
+ break;
+ }
+ case WM_WINDOWPOSCHANGING:
+ {
+ BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0;
+ WINDOWPOS *winpos = (WINDOWPOS *)lparam;
+ trace("main: WM_WINDOWPOSCHANGING\n");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+ if (!(winpos->flags & SWP_NOMOVE))
+ {
+ ok(winpos->x >= -32768 && winpos->x <= 32767, "bad winpos->x %d\n", winpos->x);
+ ok(winpos->y >= -32768 && winpos->y <= 32767, "bad winpos->y %d\n", winpos->y);
+ }
+ /* Win9x does not fixup cx/xy for WM_WINDOWPOSCHANGING */
+ if (!(winpos->flags & SWP_NOSIZE) && !is_win9x)
+ {
+ ok(winpos->cx >= 0 && winpos->cx <= 32767, "bad winpos->cx %d\n", winpos->cx);
+ ok(winpos->cy >= 0 && winpos->cy <= 32767, "bad winpos->cy %d\n", winpos->cy);
+ }
+ break;
+ }
+ case WM_WINDOWPOSCHANGED:
+ {
+ RECT rc1, rc2;
+ WINDOWPOS *winpos = (WINDOWPOS *)lparam;
+ trace("main: WM_WINDOWPOSCHANGED\n");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+ ok(winpos->x >= -32768 && winpos->x <= 32767, "bad winpos->x %d\n", winpos->x);
+ ok(winpos->y >= -32768 && winpos->y <= 32767, "bad winpos->y %d\n", winpos->y);
+
+ ok(winpos->cx >= 0 && winpos->cx <= 32767, "bad winpos->cx %d\n", winpos->cx);
+ ok(winpos->cy >= 0 && winpos->cy <= 32767, "bad winpos->cy %d\n", winpos->cy);
+
+ GetWindowRect(hwnd, &rc1);
+ trace("window: (%ld,%ld)-(%ld,%ld)\n", rc1.left, rc1.top, rc1.right, rc1.bottom);
+ SetRect(&rc2, winpos->x, winpos->y, winpos->x + winpos->cx, winpos->y + winpos->cy);
+ /* note: winpos coordinates are relative to parent */
+ MapWindowPoints(GetParent(hwnd), 0, (LPPOINT)&rc2, 2);
+ trace("pos: (%ld,%ld)-(%ld,%ld)\n", rc2.left, rc2.top, rc2.right, rc2.bottom);
+#if 0 /* Uncomment this once the test succeeds in all cases */
+ ok(EqualRect(&rc1, &rc2), "rects do not match\n");
+#endif
+
+ GetClientRect(hwnd, &rc2);
+ DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc1);
+ MapWindowPoints(0, hwnd, (LPPOINT)&rc1, 2);
+ ok(EqualRect(&rc1, &rc2), "rects do not match (%ld,%ld-%ld,%ld) / (%ld,%ld-%ld,%ld)\n",
+ rc1.left, rc1.top, rc1.right, rc1.bottom, rc2.left, rc2.top, rc2.right, rc2.bottom );
+ break;
+ }
+ case WM_NCCREATE:
+ {
+ BOOL got_getminmaxinfo = GetWindowLongPtrA(hwnd, GWLP_USERDATA) == 0x20031021;
+ CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam;
+
+ trace("WM_NCCREATE: hwnd %p, parent %p, style %08lx\n", hwnd, cs->hwndParent, cs->style);
+ if (got_getminmaxinfo)
+ trace("%p got WM_GETMINMAXINFO\n", hwnd);
+
+ if ((cs->style & WS_THICKFRAME) || !(cs->style & (WS_POPUP | WS_CHILD)))
+ ok(got_getminmaxinfo, "main: WM_GETMINMAXINFO should have been received before WM_NCCREATE\n");
+ else
+ ok(!got_getminmaxinfo, "main: WM_GETMINMAXINFO should NOT have been received before WM_NCCREATE\n");
+ break;
+ }
+ case WM_COMMAND:
+ if (test_lbuttondown_flag)
+ ShowWindow((HWND)wparam, SW_SHOW);
+ break;
+ }
+
+ return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI tool_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_GETMINMAXINFO:
+ {
+ MINMAXINFO* minmax = (MINMAXINFO *)lparam;
+
+ trace("hwnd %p, WM_GETMINMAXINFO, %08x, %08lx\n", hwnd, wparam, lparam);
+ trace("ptReserved (%ld,%ld), ptMaxSize (%ld,%ld), ptMaxPosition (%ld,%ld)\n"
+ " ptMinTrackSize (%ld,%ld), ptMaxTrackSize (%ld,%ld)\n",
+ minmax->ptReserved.x, minmax->ptReserved.y,
+ minmax->ptMaxSize.x, minmax->ptMaxSize.y,
+ minmax->ptMaxPosition.x, minmax->ptMaxPosition.y,
+ minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y,
+ minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y);
+ SetWindowLongPtrA(hwnd, GWLP_USERDATA, 0x20031021);
+ break;
+ }
+ case WM_NCCREATE:
+ {
+ BOOL got_getminmaxinfo = GetWindowLongPtrA(hwnd, GWLP_USERDATA) == 0x20031021;
+ CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam;
+
+ trace("WM_NCCREATE: hwnd %p, parent %p, style %08lx\n", hwnd, cs->hwndParent, cs->style);
+ if (got_getminmaxinfo)
+ trace("%p got WM_GETMINMAXINFO\n", hwnd);
+
+ if ((cs->style & WS_THICKFRAME) || !(cs->style & (WS_POPUP | WS_CHILD)))
+ ok(got_getminmaxinfo, "tool: WM_GETMINMAXINFO should have been received before WM_NCCREATE\n");
+ else
+ ok(!got_getminmaxinfo, "tool: WM_GETMINMAXINFO should NOT have been received before WM_NCCREATE\n");
+ break;
+ }
+ }
+
+ return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static BOOL RegisterWindowClasses(void)
+{
+ WNDCLASSA cls;
+
+ cls.style = CS_DBLCLKS;
+ cls.lpfnWndProc = main_window_procA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "MainWindowClass";
+
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ cls.style = 0;
+ cls.lpfnWndProc = tool_window_procA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "ToolWindowClass";
+
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ return TRUE;
+}
+
+static void verify_window_info(HWND hwnd, const WINDOWINFO *info, BOOL test_borders)
+{
+ RECT rcWindow, rcClient;
+ UINT border;
+ DWORD status;
+
+ ok(IsWindow(hwnd), "bad window handle\n");
+
+ GetWindowRect(hwnd, &rcWindow);
+ ok(EqualRect(&rcWindow, &info->rcWindow), "wrong rcWindow\n");
+
+ GetClientRect(hwnd, &rcClient);
+ /* translate to screen coordinates */
+ MapWindowPoints(hwnd, 0, (LPPOINT)&rcClient, 2);
+ ok(EqualRect(&rcClient, &info->rcClient), "wrong rcClient\n");
+
+ ok(info->dwStyle == (DWORD)GetWindowLongA(hwnd, GWL_STYLE),
+ "wrong dwStyle: %08lx != %08lx\n", info->dwStyle, GetWindowLongA(hwnd, GWL_STYLE));
+ ok(info->dwExStyle == (DWORD)GetWindowLongA(hwnd, GWL_EXSTYLE),
+ "wrong dwExStyle: %08lx != %08lx\n", info->dwExStyle, GetWindowLongA(hwnd, GWL_EXSTYLE));
+ status = (GetActiveWindow() == hwnd) ? WS_ACTIVECAPTION : 0;
+ ok(info->dwWindowStatus == status, "wrong dwWindowStatus: %04lx != %04lx\n",
+ info->dwWindowStatus, status);
+
+ if (test_borders && !IsRectEmpty(&rcWindow))
+ {
+ trace("rcWindow: %ld,%ld - %ld,%ld\n", rcWindow.left, rcWindow.top, rcWindow.right, rcWindow.bottom);
+ trace("rcClient: %ld,%ld - %ld,%ld\n", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
+
+ ok(info->cxWindowBorders == (unsigned)(rcClient.left - rcWindow.left),
+ "wrong cxWindowBorders %d != %ld\n", info->cxWindowBorders, rcClient.left - rcWindow.left);
+ border = min(rcWindow.bottom - rcClient.bottom, rcClient.top - rcWindow.top);
+ ok(info->cyWindowBorders == border,
+ "wrong cyWindowBorders %d != %d\n", info->cyWindowBorders, border);
+ }
+
+ ok(info->atomWindowType == GetClassLongA(hwnd, GCW_ATOM), "wrong atomWindowType\n");
+ ok(info->wCreatorVersion == 0x0400, "wrong wCreatorVersion %04x\n", info->wCreatorVersion);
+}
+
+static void FixedAdjustWindowRectEx(RECT* rc, LONG style, BOOL menu, LONG exstyle)
+{
+ AdjustWindowRectEx(rc, style, menu, exstyle);
+ /* AdjustWindowRectEx does not include scroll bars */
+ if (style & WS_VSCROLL)
+ {
+ if(exstyle & WS_EX_LEFTSCROLLBAR)
+ rc->left -= GetSystemMetrics(SM_CXVSCROLL);
+ else
+ rc->right += GetSystemMetrics(SM_CXVSCROLL);
+ }
+ if (style & WS_HSCROLL)
+ rc->bottom += GetSystemMetrics(SM_CYHSCROLL);
+}
+
+static void test_nonclient_area(HWND hwnd)
+{
+ DWORD style, exstyle;
+ RECT rc_window, rc_client, rc;
+ BOOL menu;
+ BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0;
+
+ style = GetWindowLongA(hwnd, GWL_STYLE);
+ exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+ menu = !(style & WS_CHILD) && GetMenu(hwnd) != 0;
+
+ GetWindowRect(hwnd, &rc_window);
+ trace("window: (%ld,%ld)-(%ld,%ld)\n", rc_window.left, rc_window.top, rc_window.right, rc_window.bottom);
+ GetClientRect(hwnd, &rc_client);
+ trace("client: (%ld,%ld)-(%ld,%ld)\n", rc_client.left, rc_client.top, rc_client.right, rc_client.bottom);
+
+ /* avoid some cases when things go wrong */
+ if (IsRectEmpty(&rc_window) || IsRectEmpty(&rc_client) ||
+ rc_window.right > 32768 || rc_window.bottom > 32768) return;
+
+ CopyRect(&rc, &rc_client);
+ MapWindowPoints(hwnd, 0, (LPPOINT)&rc, 2);
+ FixedAdjustWindowRectEx(&rc, style, menu, exstyle);
+
+ trace("calc window: (%ld,%ld)-(%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ ok(EqualRect(&rc, &rc_window), "window rect does not match: style:exstyle=0x%08lx:0x%08lx, menu=%d\n", style, exstyle, menu);
+
+
+ CopyRect(&rc, &rc_window);
+ DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc);
+ MapWindowPoints(0, hwnd, (LPPOINT)&rc, 2);
+ trace("calc client: (%ld,%ld)-(%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ ok(EqualRect(&rc, &rc_client), "client rect does not match: style:exstyle=0x%08lx:0x%08lx, menu=%d\n", style, exstyle, menu);
+
+ /* Win9x doesn't like WM_NCCALCSIZE with synthetic data and crashes */;
+ if (is_win9x)
+ return;
+
+ /* and now test AdjustWindowRectEx and WM_NCCALCSIZE on synthetic data */
+ SetRect(&rc_client, 0, 0, 250, 150);
+ CopyRect(&rc_window, &rc_client);
+ MapWindowPoints(hwnd, 0, (LPPOINT)&rc_window, 2);
+ FixedAdjustWindowRectEx(&rc_window, style, menu, exstyle);
+ trace("calc window: (%ld,%ld)-(%ld,%ld)\n",
+ rc_window.left, rc_window.top, rc_window.right, rc_window.bottom);
+
+ CopyRect(&rc, &rc_window);
+ DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc);
+ MapWindowPoints(0, hwnd, (LPPOINT)&rc, 2);
+ trace("calc client: (%ld,%ld)-(%ld,%ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ ok(EqualRect(&rc, &rc_client), "synthetic rect does not match: style:exstyle=0x%08lx:0x%08lx, menu=%d\n", style, exstyle, menu);
+}
+
+static LRESULT CALLBACK cbt_hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ static const char *CBT_code_name[10] = {
+ "HCBT_MOVESIZE",
+ "HCBT_MINMAX",
+ "HCBT_QS",
+ "HCBT_CREATEWND",
+ "HCBT_DESTROYWND",
+ "HCBT_ACTIVATE",
+ "HCBT_CLICKSKIPPED",
+ "HCBT_KEYSKIPPED",
+ "HCBT_SYSCOMMAND",
+ "HCBT_SETFOCUS" };
+ const char *code_name = (nCode >= 0 && nCode <= HCBT_SETFOCUS) ? CBT_code_name[nCode] : "Unknown";
+
+ trace("CBT: %d (%s), %08x, %08lx\n", nCode, code_name, wParam, lParam);
+
+ /* on HCBT_DESTROYWND window state is undefined */
+ if (nCode != HCBT_DESTROYWND && IsWindow((HWND)wParam))
+ {
+ if (pGetWindowInfo)
+ {
+ WINDOWINFO info;
+
+ /* Win98 actually does check the info.cbSize and doesn't allow
+ * it to be anything except sizeof(WINDOWINFO), while Win95, Win2k,
+ * WinXP do not check it at all.
+ */
+ info.cbSize = sizeof(WINDOWINFO);
+ ok(pGetWindowInfo((HWND)wParam, &info), "GetWindowInfo should not fail\n");
+ /* win2k SP4 returns broken border info if GetWindowInfo
+ * is being called from HCBT_DESTROYWND or HCBT_MINMAX hook proc.
+ */
+ verify_window_info((HWND)wParam, &info, nCode != HCBT_MINMAX);
+ }
+ }
+
+ switch (nCode)
+ {
+ case HCBT_CREATEWND:
+ {
+#if 0 /* Uncomment this once the test succeeds in all cases */
+ static const RECT rc_null;
+ RECT rc;
+#endif
+ LONG style;
+ CBT_CREATEWNDA *createwnd = (CBT_CREATEWNDA *)lParam;
+ trace("HCBT_CREATEWND: hwnd %p, parent %p, style %08lx\n",
+ (HWND)wParam, createwnd->lpcs->hwndParent, createwnd->lpcs->style);
+ ok(createwnd->hwndInsertAfter == HWND_TOP, "hwndInsertAfter should be always HWND_TOP\n");
+
+ /* WS_VISIBLE should be turned off yet */
+ style = createwnd->lpcs->style & ~WS_VISIBLE;
+ ok(style == GetWindowLongA((HWND)wParam, GWL_STYLE),
+ "style of hwnd and style in the CREATESTRUCT do not match: %08lx != %08lx\n",
+ GetWindowLongA((HWND)wParam, GWL_STYLE), style);
+
+#if 0 /* Uncomment this once the test succeeds in all cases */
+ if ((style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
+ {
+ ok(GetParent((HWND)wParam) == hwndMessage,
+ "wrong result from GetParent %p: message window %p\n",
+ GetParent((HWND)wParam), hwndMessage);
+ }
+ else
+ ok(!GetParent((HWND)wParam), "GetParent should return 0 at this point\n");
+
+ ok(!GetWindow((HWND)wParam, GW_OWNER), "GW_OWNER should be set to 0 at this point\n");
+#endif
+#if 0 /* while NT assigns GW_HWNDFIRST/LAST some values at this point,
+ * Win9x still has them set to 0.
+ */
+ ok(GetWindow((HWND)wParam, GW_HWNDFIRST) != 0, "GW_HWNDFIRST should not be set to 0 at this point\n");
+ ok(GetWindow((HWND)wParam, GW_HWNDLAST) != 0, "GW_HWNDLAST should not be set to 0 at this point\n");
+#endif
+ ok(!GetWindow((HWND)wParam, GW_HWNDPREV), "GW_HWNDPREV should be set to 0 at this point\n");
+ ok(!GetWindow((HWND)wParam, GW_HWNDNEXT), "GW_HWNDNEXT should be set to 0 at this point\n");
+
+#if 0 /* Uncomment this once the test succeeds in all cases */
+ if (pGetAncestor)
+ {
+ ok(pGetAncestor((HWND)wParam, GA_PARENT) == hwndMessage, "GA_PARENT should be set to hwndMessage at this point\n");
+ ok(pGetAncestor((HWND)wParam, GA_ROOT) == (HWND)wParam,
+ "GA_ROOT is set to %p, expected %p\n", pGetAncestor((HWND)wParam, GA_ROOT), (HWND)wParam);
+
+ if ((style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
+ ok(pGetAncestor((HWND)wParam, GA_ROOTOWNER) == hwndMessage,
+ "GA_ROOTOWNER should be set to hwndMessage at this point\n");
+ else
+ ok(pGetAncestor((HWND)wParam, GA_ROOTOWNER) == (HWND)wParam,
+ "GA_ROOTOWNER is set to %p, expected %p\n", pGetAncestor((HWND)wParam, GA_ROOTOWNER), (HWND)wParam);
+ }
+
+ ok(GetWindowRect((HWND)wParam, &rc), "GetWindowRect failed\n");
+ ok(EqualRect(&rc, &rc_null), "window rect should be set to 0 HCBT_CREATEWND\n");
+ ok(GetClientRect((HWND)wParam, &rc), "GetClientRect failed\n");
+ ok(EqualRect(&rc, &rc_null), "client rect should be set to 0 on HCBT_CREATEWND\n");
+#endif
+ break;
+ }
+ }
+
+ return CallNextHookEx(hhook, nCode, wParam, lParam);
+}
+
+static void test_shell_window(void)
+{
+ BOOL ret;
+ DWORD error;
+ HMODULE hinst, hUser32;
+ BOOL (WINAPI*SetShellWindow)(HWND);
+ BOOL (WINAPI*SetShellWindowEx)(HWND, HWND);
+ HWND hwnd1, hwnd2, hwnd3, hwnd4, hwnd5;
+ HWND shellWindow, nextWnd;
+
+ if (!GetWindowLongW(GetDesktopWindow(), GWL_STYLE))
+ {
+ trace("Skipping shell window test on Win9x\n");
+ return;
+ }
+
+ shellWindow = GetShellWindow();
+ hinst = GetModuleHandle(0);
+ hUser32 = GetModuleHandleA("user32");
+
+ SetShellWindow = (void *)GetProcAddress(hUser32, "SetShellWindow");
+ SetShellWindowEx = (void *)GetProcAddress(hUser32, "SetShellWindowEx");
+
+ trace("previous shell window: %p\n", shellWindow);
+
+ if (shellWindow) {
+ DWORD pid;
+ HANDLE hProcess;
+
+ ret = DestroyWindow(shellWindow);
+ error = GetLastError();
+
+ ok(!ret, "DestroyWindow(shellWindow)\n");
+ /* passes on Win XP, but not on Win98 */
+ ok(error==ERROR_ACCESS_DENIED, "ERROR_ACCESS_DENIED after DestroyWindow(shellWindow)\n");
+
+ /* close old shell instance */
+ GetWindowThreadProcessId(shellWindow, &pid);
+ hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
+ ret = TerminateProcess(hProcess, 0);
+ ok(ret, "termination of previous shell process failed: GetLastError()=%ld\n", GetLastError());
+ WaitForSingleObject(hProcess, INFINITE); /* wait for termination */
+ CloseHandle(hProcess);
+ }
+
+ hwnd1 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST1"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 100, 100, 300, 200, 0, 0, hinst, 0);
+ trace("created window 1: %p\n", hwnd1);
+
+ ret = SetShellWindow(hwnd1);
+ ok(ret, "first call to SetShellWindow(hwnd1)\n");
+ shellWindow = GetShellWindow();
+ ok(shellWindow==hwnd1, "wrong shell window: %p\n", shellWindow);
+
+ ret = SetShellWindow(hwnd1);
+ ok(!ret, "second call to SetShellWindow(hwnd1)\n");
+
+ ret = SetShellWindow(0);
+ error = GetLastError();
+ /* passes on Win XP, but not on Win98
+ ok(!ret, "reset shell window by SetShellWindow(0)\n");
+ ok(error==ERROR_INVALID_WINDOW_HANDLE, "ERROR_INVALID_WINDOW_HANDLE after SetShellWindow(0)\n"); */
+
+ ret = SetShellWindow(hwnd1);
+ /* passes on Win XP, but not on Win98
+ ok(!ret, "third call to SetShellWindow(hwnd1)\n"); */
+
+ todo_wine
+ {
+ SetWindowLong(hwnd1, GWL_EXSTYLE, GetWindowLong(hwnd1,GWL_EXSTYLE)|WS_EX_TOPMOST);
+ ret = GetWindowLong(hwnd1,GWL_EXSTYLE)&WS_EX_TOPMOST? TRUE: FALSE;
+ ok(!ret, "SetWindowExStyle(hwnd1, WS_EX_TOPMOST)\n");
+ }
+
+ ret = DestroyWindow(hwnd1);
+ ok(ret, "DestroyWindow(hwnd1)\n");
+
+ hwnd2 = CreateWindowEx(WS_EX_TOPMOST, TEXT("#32770"), TEXT("TEST2"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 150, 250, 300, 200, 0, 0, hinst, 0);
+ trace("created window 2: %p\n", hwnd2);
+ ret = SetShellWindow(hwnd2);
+ ok(!ret, "SetShellWindow(hwnd2) with WS_EX_TOPMOST\n");
+
+ hwnd3 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST3"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 200, 400, 300, 200, 0, 0, hinst, 0);
+ trace("created window 3: %p\n", hwnd3);
+
+ hwnd4 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST4"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 250, 500, 300, 200, 0, 0, hinst, 0);
+ trace("created window 4: %p\n", hwnd4);
+
+ nextWnd = GetWindow(hwnd4, GW_HWNDNEXT);
+ ok(nextWnd==hwnd3, "wrong next window for hwnd4: %p - expected hwnd3\n", nextWnd);
+
+ ret = SetShellWindow(hwnd4);
+ ok(ret, "SetShellWindow(hwnd4)\n");
+ shellWindow = GetShellWindow();
+ ok(shellWindow==hwnd4, "wrong shell window: %p - expected hwnd4\n", shellWindow);
+
+ nextWnd = GetWindow(hwnd4, GW_HWNDNEXT);
+ ok(nextWnd==0, "wrong next window for hwnd4: %p - expected 0\n", nextWnd);
+
+ ret = SetWindowPos(hwnd4, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
+ ok(ret, "SetWindowPos(hwnd4, HWND_TOPMOST)\n");
+
+ ret = SetWindowPos(hwnd4, hwnd3, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
+ ok(ret, "SetWindowPos(hwnd4, hwnd3\n");
+
+ ret = SetShellWindow(hwnd3);
+ ok(!ret, "SetShellWindow(hwnd3)\n");
+ shellWindow = GetShellWindow();
+ ok(shellWindow==hwnd4, "wrong shell window: %p - expected hwnd4\n", shellWindow);
+
+ hwnd5 = CreateWindowEx(0, TEXT("#32770"), TEXT("TEST5"), WS_OVERLAPPEDWINDOW/*|WS_VISIBLE*/, 300, 600, 300, 200, 0, 0, hinst, 0);
+ trace("created window 5: %p\n", hwnd5);
+ ret = SetWindowPos(hwnd4, hwnd5, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
+ ok(ret, "SetWindowPos(hwnd4, hwnd5)\n");
+
+ todo_wine
+ {
+ nextWnd = GetWindow(hwnd4, GW_HWNDNEXT);
+ ok(nextWnd==0, "wrong next window for hwnd4 after SetWindowPos(): %p - expected 0\n", nextWnd);
+ }
+
+ /* destroy test windows */
+ DestroyWindow(hwnd2);
+ DestroyWindow(hwnd3);
+ DestroyWindow(hwnd4);
+ DestroyWindow(hwnd5);
+}
+
+/************** MDI test ****************/
+
+static const char mdi_lParam_test_message[] = "just a test string";
+
+static void test_MDI_create(HWND parent, HWND mdi_client, INT first_id)
+{
+ MDICREATESTRUCTA mdi_cs;
+ HWND mdi_child;
+ static const WCHAR classW[] = {'M','D','I','_','c','h','i','l','d','_','C','l','a','s','s','_','1',0};
+ static const WCHAR titleW[] = {'M','D','I',' ','c','h','i','l','d',0};
+ BOOL isWin9x = FALSE;
+
+ mdi_cs.szClass = "MDI_child_Class_1";
+ mdi_cs.szTitle = "MDI child";
+ mdi_cs.hOwner = GetModuleHandle(0);
+ mdi_cs.x = CW_USEDEFAULT;
+ mdi_cs.y = CW_USEDEFAULT;
+ mdi_cs.cx = CW_USEDEFAULT;
+ mdi_cs.cy = CW_USEDEFAULT;
+ mdi_cs.style = 0;
+ mdi_cs.lParam = (LPARAM)mdi_lParam_test_message;
+ mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+
+ mdi_cs.style = 0x7fffffff; /* without WS_POPUP */
+ mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+
+ mdi_cs.style = 0xffffffff; /* with WS_POPUP */
+ mdi_child = (HWND)SendMessageA(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs);
+ if (GetWindowLongA(mdi_client, GWL_STYLE) & MDIS_ALLCHILDSTYLES)
+ {
+ ok(!mdi_child, "MDI child with WS_POPUP and with MDIS_ALLCHILDSTYLES should fail\n");
+ }
+ else
+ {
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+ }
+
+ /* test MDICREATESTRUCT A<->W mapping */
+ /* MDICREATESTRUCTA and MDICREATESTRUCTW have the same layout */
+ mdi_cs.style = 0;
+ mdi_cs.szClass = (LPCSTR)classW;
+ mdi_cs.szTitle = (LPCSTR)titleW;
+ SetLastError(0xdeadbeef);
+ mdi_child = (HWND)SendMessageW(mdi_client, WM_MDICREATE, 0, (LPARAM)&mdi_cs);
+ if (!mdi_child)
+ {
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ isWin9x = TRUE;
+ else
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ }
+ else
+ {
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+ }
+
+ mdi_child = CreateMDIWindowA("MDI_child_Class_1", "MDI child",
+ 0,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, GetModuleHandle(0),
+ (LPARAM)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+
+ mdi_child = CreateMDIWindowA("MDI_child_Class_1", "MDI child",
+ 0x7fffffff, /* without WS_POPUP */
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, GetModuleHandle(0),
+ (LPARAM)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+
+ mdi_child = CreateMDIWindowA("MDI_child_Class_1", "MDI child",
+ 0xffffffff, /* with WS_POPUP */
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, GetModuleHandle(0),
+ (LPARAM)mdi_lParam_test_message);
+ if (GetWindowLongA(mdi_client, GWL_STYLE) & MDIS_ALLCHILDSTYLES)
+ {
+ ok(!mdi_child, "MDI child with WS_POPUP and with MDIS_ALLCHILDSTYLES should fail\n");
+ }
+ else
+ {
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+ }
+
+ /* test MDICREATESTRUCT A<->W mapping */
+ SetLastError(0xdeadbeef);
+ mdi_child = CreateMDIWindowW(classW, titleW,
+ 0,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, GetModuleHandle(0),
+ (LPARAM)mdi_lParam_test_message);
+ if (!mdi_child)
+ {
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ isWin9x = TRUE;
+ else
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ }
+ else
+ {
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+ }
+
+ mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_1", "MDI child",
+ 0,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+
+ mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_1", "MDI child",
+ 0x7fffffff, /* without WS_POPUP */
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+
+ mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_1", "MDI child",
+ 0xffffffff, /* with WS_POPUP */
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ if (GetWindowLongA(mdi_client, GWL_STYLE) & MDIS_ALLCHILDSTYLES)
+ {
+ ok(!mdi_child, "MDI child with WS_POPUP and with MDIS_ALLCHILDSTYLES should fail\n");
+ }
+ else
+ {
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+ }
+
+ /* test MDICREATESTRUCT A<->W mapping */
+ SetLastError(0xdeadbeef);
+ mdi_child = CreateWindowExW(WS_EX_MDICHILD, classW, titleW,
+ 0,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ if (!mdi_child)
+ {
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ isWin9x = TRUE;
+ else
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ }
+ else
+ {
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == first_id, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ SendMessageA(mdi_client, WM_MDIDESTROY, (WPARAM)mdi_child, 0);
+ ok(!IsWindow(mdi_child), "WM_MDIDESTROY failed\n");
+ }
+
+ /* This test fails on Win9x */
+ if (!isWin9x)
+ {
+ mdi_child = CreateWindowExA(WS_EX_MDICHILD, "MDI_child_Class_2", "MDI child",
+ WS_CHILD,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ parent, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(!mdi_child, "WS_EX_MDICHILD with a not MDIClient parent should fail\n");
+ }
+
+ mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child",
+ WS_CHILD, /* without WS_POPUP */
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == 0, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ DestroyWindow(mdi_child);
+
+ mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child",
+ WS_CHILD | WS_POPUP, /* with WS_POPUP */
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == 0, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ DestroyWindow(mdi_child);
+
+ /* maximized child */
+ mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child",
+ WS_CHILD | WS_MAXIMIZE,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == 0, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ DestroyWindow(mdi_child);
+
+ trace("Creating maximized child with a caption\n");
+ mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child",
+ WS_CHILD | WS_MAXIMIZE | WS_CAPTION,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == 0, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ DestroyWindow(mdi_child);
+
+ trace("Creating maximized child with a caption and a thick frame\n");
+ mdi_child = CreateWindowExA(0, "MDI_child_Class_2", "MDI child",
+ WS_CHILD | WS_MAXIMIZE | WS_CAPTION | WS_THICKFRAME,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ mdi_client, 0, GetModuleHandle(0),
+ (LPVOID)mdi_lParam_test_message);
+ ok(mdi_child != 0, "MDI child creation failed\n");
+ ok(GetWindowLongPtrA(mdi_child, GWLP_ID) == 0, "wrong child id %ld\n", GetWindowLongPtrA(mdi_child, GWLP_ID));
+ DestroyWindow(mdi_child);
+}
+
+/**********************************************************************
+ * MDI_ChildGetMinMaxInfo (copied from windows/mdi.c)
+ *
+ * Note: The rule here is that client rect of the maximized MDI child
+ * is equal to the client rect of the MDI client window.
+ */
+static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax )
+{
+ RECT rect;
+
+ GetClientRect( client, &rect );
+ AdjustWindowRectEx( &rect, GetWindowLongA( hwnd, GWL_STYLE ),
+ 0, GetWindowLongA( hwnd, GWL_EXSTYLE ));
+
+ rect.right -= rect.left;
+ rect.bottom -= rect.top;
+ lpMinMax->ptMaxSize.x = rect.right;
+ lpMinMax->ptMaxSize.y = rect.bottom;
+
+ lpMinMax->ptMaxPosition.x = rect.left;
+ lpMinMax->ptMaxPosition.y = rect.top;
+
+ trace("max rect (%ld,%ld - %ld, %ld)\n",
+ rect.left, rect.top, rect.right, rect.bottom);
+}
+
+static LRESULT WINAPI mdi_child_wnd_proc_1(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_NCCREATE:
+ case WM_CREATE:
+ {
+ CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam;
+ MDICREATESTRUCTA *mdi_cs = (MDICREATESTRUCTA *)cs->lpCreateParams;
+
+ ok(cs->dwExStyle & WS_EX_MDICHILD, "WS_EX_MDICHILD should be set\n");
+ ok(mdi_cs->lParam == (LPARAM)mdi_lParam_test_message, "wrong mdi_cs->lParam\n");
+
+ ok(!lstrcmpA(cs->lpszClass, "MDI_child_Class_1"), "wrong class name\n");
+ ok(!lstrcmpA(cs->lpszClass, mdi_cs->szClass), "class name does not match\n");
+ ok(!lstrcmpA(cs->lpszName, "MDI child"), "wrong title\n");
+ ok(!lstrcmpA(cs->lpszName, mdi_cs->szTitle), "title does not match\n");
+ ok(cs->hInstance == mdi_cs->hOwner, "%p != %p\n", cs->hInstance, mdi_cs->hOwner);
+
+ /* MDICREATESTRUCT should have original values */
+ ok(mdi_cs->style == 0 || mdi_cs->style == 0x7fffffff || mdi_cs->style == 0xffffffff,
+ "mdi_cs->style does not match (%08lx)\n", mdi_cs->style);
+ ok(mdi_cs->x == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->x);
+ ok(mdi_cs->y == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->y);
+ ok(mdi_cs->cx == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->cx);
+ ok(mdi_cs->cy == CW_USEDEFAULT, "%d != CW_USEDEFAULT\n", mdi_cs->cy);
+
+ /* CREATESTRUCT should have fixed values */
+ ok(cs->x != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->x);
+ ok(cs->y != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->y);
+
+ /* cx/cy == CW_USEDEFAULT are translated to NOT zero values */
+ ok(cs->cx != CW_USEDEFAULT && cs->cx != 0, "%d == CW_USEDEFAULT\n", cs->cx);
+ ok(cs->cy != CW_USEDEFAULT && cs->cy != 0, "%d == CW_USEDEFAULT\n", cs->cy);
+
+ ok(!(cs->style & WS_POPUP), "WS_POPUP is not allowed\n");
+
+ if (GetWindowLongA(cs->hwndParent, GWL_STYLE) & MDIS_ALLCHILDSTYLES)
+ {
+ LONG style = mdi_cs->style | WS_CHILD | WS_CLIPSIBLINGS;
+ ok(cs->style == style,
+ "cs->style does not match (%08lx)\n", cs->style);
+ }
+ else
+ {
+ LONG style = mdi_cs->style;
+ style &= ~WS_POPUP;
+ style |= WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION |
+ WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
+ ok(cs->style == style,
+ "cs->style does not match (%08lx)\n", cs->style);
+ }
+ break;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ HWND client = GetParent(hwnd);
+ RECT rc;
+ MINMAXINFO *minmax = (MINMAXINFO *)lparam;
+ MINMAXINFO my_minmax;
+ LONG style, exstyle;
+
+ style = GetWindowLongA(hwnd, GWL_STYLE);
+ exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+
+ GetWindowRect(client, &rc);
+ trace("MDI client %p window size = (%ld x %ld)\n", client, rc.right-rc.left, rc.bottom-rc.top);
+ GetClientRect(client, &rc);
+ trace("MDI client %p client size = (%ld x %ld)\n", client, rc.right, rc.bottom);
+ trace("screen size: %d x %d\n", GetSystemMetrics(SM_CXSCREEN),
+ GetSystemMetrics(SM_CYSCREEN));
+
+ GetClientRect(client, &rc);
+ if ((style & WS_CAPTION) == WS_CAPTION)
+ style &= ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */
+ AdjustWindowRectEx(&rc, style, 0, exstyle);
+ trace("MDI child: calculated max window size = (%ld x %ld)\n", rc.right-rc.left, rc.bottom-rc.top);
+
+ trace("ptReserved = (%ld,%ld)\n"
+ "ptMaxSize = (%ld,%ld)\n"
+ "ptMaxPosition = (%ld,%ld)\n"
+ "ptMinTrackSize = (%ld,%ld)\n"
+ "ptMaxTrackSize = (%ld,%ld)\n",
+ minmax->ptReserved.x, minmax->ptReserved.y,
+ minmax->ptMaxSize.x, minmax->ptMaxSize.y,
+ minmax->ptMaxPosition.x, minmax->ptMaxPosition.y,
+ minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y,
+ minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y);
+
+ ok(minmax->ptMaxSize.x == rc.right - rc.left, "default width of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.x, rc.right - rc.left);
+ ok(minmax->ptMaxSize.y == rc.bottom - rc.top, "default height of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.y, rc.bottom - rc.top);
+
+ DefMDIChildProcA(hwnd, msg, wparam, lparam);
+
+ trace("DefMDIChildProc returned:\n"
+ "ptReserved = (%ld,%ld)\n"
+ "ptMaxSize = (%ld,%ld)\n"
+ "ptMaxPosition = (%ld,%ld)\n"
+ "ptMinTrackSize = (%ld,%ld)\n"
+ "ptMaxTrackSize = (%ld,%ld)\n",
+ minmax->ptReserved.x, minmax->ptReserved.y,
+ minmax->ptMaxSize.x, minmax->ptMaxSize.y,
+ minmax->ptMaxPosition.x, minmax->ptMaxPosition.y,
+ minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y,
+ minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y);
+
+ MDI_ChildGetMinMaxInfo(client, hwnd, &my_minmax);
+ ok(minmax->ptMaxSize.x == my_minmax.ptMaxSize.x, "default width of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.x, my_minmax.ptMaxSize.x);
+ ok(minmax->ptMaxSize.y == my_minmax.ptMaxSize.y, "default height of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.y, my_minmax.ptMaxSize.y);
+
+ return 1;
+ }
+
+ case WM_MDIACTIVATE:
+ {
+ HWND active, client = GetParent(hwnd);
+ /*trace("%p WM_MDIACTIVATE %08x %08lx\n", hwnd, wparam, lparam);*/
+ active = (HWND)SendMessageA(client, WM_MDIGETACTIVE, 0, 0);
+ if (hwnd == (HWND)lparam) /* if we are being activated */
+ ok (active == (HWND)lparam, "new active %p != active %p\n", (HWND)lparam, active);
+ else
+ ok (active == (HWND)wparam, "old active %p != active %p\n", (HWND)wparam, active);
+ break;
+ }
+ }
+ return DefMDIChildProcA(hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI mdi_child_wnd_proc_2(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_NCCREATE:
+ case WM_CREATE:
+ {
+ CREATESTRUCTA *cs = (CREATESTRUCTA *)lparam;
+
+ trace("%s\n", (msg == WM_NCCREATE) ? "WM_NCCREATE" : "WM_CREATE");
+ trace("x %d, y %d, cx %d, cy %d\n", cs->x, cs->y, cs->cx, cs->cy);
+
+ ok(!(cs->dwExStyle & WS_EX_MDICHILD), "WS_EX_MDICHILD should not be set\n");
+ ok(cs->lpCreateParams == mdi_lParam_test_message, "wrong cs->lpCreateParams\n");
+
+ ok(!lstrcmpA(cs->lpszClass, "MDI_child_Class_2"), "wrong class name\n");
+ ok(!lstrcmpA(cs->lpszName, "MDI child"), "wrong title\n");
+
+ /* CREATESTRUCT should have fixed values */
+ /* For some reason Win9x doesn't translate cs->x from CW_USEDEFAULT,
+ while NT does. */
+ /*ok(cs->x != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->x);*/
+ ok(cs->y != CW_USEDEFAULT, "%d == CW_USEDEFAULT\n", cs->y);
+
+ /* cx/cy == CW_USEDEFAULT are translated to 0 */
+ /* For some reason Win98 doesn't translate cs->cx from CW_USEDEFAULT,
+ while Win95, Win2k, WinXP do. */
+ /*ok(cs->cx == 0, "%d != 0\n", cs->cx);*/
+ ok(cs->cy == 0, "%d != 0\n", cs->cy);
+ break;
+ }
+
+ case WM_GETMINMAXINFO:
+ {
+ HWND parent = GetParent(hwnd);
+ RECT rc;
+ MINMAXINFO *minmax = (MINMAXINFO *)lparam;
+ LONG style, exstyle;
+
+ trace("WM_GETMINMAXINFO\n");
+
+ style = GetWindowLongA(hwnd, GWL_STYLE);
+ exstyle = GetWindowLongA(hwnd, GWL_EXSTYLE);
+
+ GetClientRect(parent, &rc);
+ trace("parent %p client size = (%ld x %ld)\n", parent, rc.right, rc.bottom);
+
+ GetClientRect(parent, &rc);
+ if ((style & WS_CAPTION) == WS_CAPTION)
+ style &= ~WS_BORDER; /* WS_CAPTION = WS_DLGFRAME | WS_BORDER */
+ AdjustWindowRectEx(&rc, style, 0, exstyle);
+ trace("calculated max child window size = (%ld x %ld)\n", rc.right-rc.left, rc.bottom-rc.top);
+
+ trace("ptReserved = (%ld,%ld)\n"
+ "ptMaxSize = (%ld,%ld)\n"
+ "ptMaxPosition = (%ld,%ld)\n"
+ "ptMinTrackSize = (%ld,%ld)\n"
+ "ptMaxTrackSize = (%ld,%ld)\n",
+ minmax->ptReserved.x, minmax->ptReserved.y,
+ minmax->ptMaxSize.x, minmax->ptMaxSize.y,
+ minmax->ptMaxPosition.x, minmax->ptMaxPosition.y,
+ minmax->ptMinTrackSize.x, minmax->ptMinTrackSize.y,
+ minmax->ptMaxTrackSize.x, minmax->ptMaxTrackSize.y);
+
+ ok(minmax->ptMaxSize.x == rc.right - rc.left, "default width of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.x, rc.right - rc.left);
+ ok(minmax->ptMaxSize.y == rc.bottom - rc.top, "default height of maximized child %ld != %ld\n",
+ minmax->ptMaxSize.y, rc.bottom - rc.top);
+ break;
+ }
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lparam;
+ RECT rc1, rc2;
+
+ GetWindowRect(hwnd, &rc1);
+ trace("window: (%ld,%ld)-(%ld,%ld)\n", rc1.left, rc1.top, rc1.right, rc1.bottom);
+ SetRect(&rc2, winpos->x, winpos->y, winpos->x + winpos->cx, winpos->y + winpos->cy);
+ /* note: winpos coordinates are relative to parent */
+ MapWindowPoints(GetParent(hwnd), 0, (LPPOINT)&rc2, 2);
+ trace("pos: (%ld,%ld)-(%ld,%ld)\n", rc2.left, rc2.top, rc2.right, rc2.bottom);
+ ok(EqualRect(&rc1, &rc2), "rects do not match\n");
+
+ GetWindowRect(hwnd, &rc1);
+ GetClientRect(hwnd, &rc2);
+ DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc1);
+ MapWindowPoints(0, hwnd, (LPPOINT)&rc1, 2);
+ ok(EqualRect(&rc1, &rc2), "rects do not match\n");
+ }
+ /* fall through */
+ case WM_WINDOWPOSCHANGING:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lparam;
+ WINDOWPOS my_winpos = *winpos;
+
+ trace("%s\n", (msg == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ DefWindowProcA(hwnd, msg, wparam, lparam);
+
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ ok(!memcmp(&my_winpos, winpos, sizeof(WINDOWPOS)),
+ "DefWindowProc should not change WINDOWPOS values\n");
+
+ return 1;
+ }
+ }
+ return DefWindowProcA(hwnd, msg, wparam, lparam);
+}
+
+static LRESULT WINAPI mdi_main_wnd_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ static HWND mdi_client;
+
+ switch (msg)
+ {
+ case WM_CREATE:
+ {
+ CLIENTCREATESTRUCT client_cs;
+ RECT rc;
+
+ GetClientRect(hwnd, &rc);
+
+ client_cs.hWindowMenu = 0;
+ client_cs.idFirstChild = 1;
+
+ /* MDIClient without MDIS_ALLCHILDSTYLES */
+ mdi_client = CreateWindowExA(0, "mdiclient",
+ NULL,
+ WS_CHILD /*| WS_VISIBLE*/,
+ /* tests depend on a not zero MDIClient size */
+ 0, 0, rc.right, rc.bottom,
+ hwnd, 0, GetModuleHandle(0),
+ (LPVOID)&client_cs);
+ assert(mdi_client);
+ test_MDI_create(hwnd, mdi_client, client_cs.idFirstChild);
+ DestroyWindow(mdi_client);
+
+ /* MDIClient with MDIS_ALLCHILDSTYLES */
+ mdi_client = CreateWindowExA(0, "mdiclient",
+ NULL,
+ WS_CHILD | MDIS_ALLCHILDSTYLES /*| WS_VISIBLE*/,
+ /* tests depend on a not zero MDIClient size */
+ 0, 0, rc.right, rc.bottom,
+ hwnd, 0, GetModuleHandle(0),
+ (LPVOID)&client_cs);
+ assert(mdi_client);
+ test_MDI_create(hwnd, mdi_client, client_cs.idFirstChild);
+ DestroyWindow(mdi_client);
+ break;
+ }
+
+ case WM_WINDOWPOSCHANGED:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lparam;
+ RECT rc1, rc2;
+
+ GetWindowRect(hwnd, &rc1);
+ trace("window: (%ld,%ld)-(%ld,%ld)\n", rc1.left, rc1.top, rc1.right, rc1.bottom);
+ SetRect(&rc2, winpos->x, winpos->y, winpos->x + winpos->cx, winpos->y + winpos->cy);
+ /* note: winpos coordinates are relative to parent */
+ MapWindowPoints(GetParent(hwnd), 0, (LPPOINT)&rc2, 2);
+ trace("pos: (%ld,%ld)-(%ld,%ld)\n", rc2.left, rc2.top, rc2.right, rc2.bottom);
+ ok(EqualRect(&rc1, &rc2), "rects do not match\n");
+
+ GetWindowRect(hwnd, &rc1);
+ GetClientRect(hwnd, &rc2);
+ DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)&rc1);
+ MapWindowPoints(0, hwnd, (LPPOINT)&rc1, 2);
+ ok(EqualRect(&rc1, &rc2), "rects do not match\n");
+ }
+ /* fall through */
+ case WM_WINDOWPOSCHANGING:
+ {
+ WINDOWPOS *winpos = (WINDOWPOS *)lparam;
+ WINDOWPOS my_winpos = *winpos;
+
+ trace("%s\n", (msg == WM_WINDOWPOSCHANGING) ? "WM_WINDOWPOSCHANGING" : "WM_WINDOWPOSCHANGED");
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ DefWindowProcA(hwnd, msg, wparam, lparam);
+
+ trace("%p after %p, x %d, y %d, cx %d, cy %d flags %08x\n",
+ winpos->hwnd, winpos->hwndInsertAfter,
+ winpos->x, winpos->y, winpos->cx, winpos->cy, winpos->flags);
+
+ ok(!memcmp(&my_winpos, winpos, sizeof(WINDOWPOS)),
+ "DefWindowProc should not change WINDOWPOS values\n");
+
+ return 1;
+ }
+
+ case WM_CLOSE:
+ PostQuitMessage(0);
+ break;
+ }
+ return DefFrameProcA(hwnd, mdi_client, msg, wparam, lparam);
+}
+
+static BOOL mdi_RegisterWindowClasses(void)
+{
+ WNDCLASSA cls;
+
+ cls.style = 0;
+ cls.lpfnWndProc = mdi_main_wnd_procA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "MDI_parent_Class";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ cls.lpfnWndProc = mdi_child_wnd_proc_1;
+ cls.lpszClassName = "MDI_child_Class_1";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ cls.lpfnWndProc = mdi_child_wnd_proc_2;
+ cls.lpszClassName = "MDI_child_Class_2";
+ if(!RegisterClassA(&cls)) return FALSE;
+
+ return TRUE;
+}
+
+static void test_mdi(void)
+{
+ HWND mdi_hwndMain;
+ /*MSG msg;*/
+
+ if (!mdi_RegisterWindowClasses()) assert(0);
+
+ mdi_hwndMain = CreateWindowExA(0, "MDI_parent_Class", "MDI parent window",
+ WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+ WS_MAXIMIZEBOX /*| WS_VISIBLE*/,
+ 100, 100, CW_USEDEFAULT, CW_USEDEFAULT,
+ GetDesktopWindow(), 0,
+ GetModuleHandle(0), NULL);
+ assert(mdi_hwndMain);
+/*
+ while(GetMessage(&msg, 0, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+*/
+}
+
+static void test_icons(void)
+{
+ WNDCLASSEXA cls;
+ HWND hwnd;
+ HICON icon = LoadIconA(0, (LPSTR)IDI_APPLICATION);
+ HICON icon2 = LoadIconA(0, (LPSTR)IDI_QUESTION);
+ HICON small_icon = LoadImageA(0, (LPSTR)IDI_APPLICATION, IMAGE_ICON,
+ GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED );
+ HICON res;
+
+ cls.cbSize = sizeof(cls);
+ cls.style = 0;
+ cls.lpfnWndProc = DefWindowProcA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = 0;
+ cls.hIcon = LoadIconA(0, (LPSTR)IDI_HAND);
+ cls.hIconSm = small_icon;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "IconWindowClass";
+
+ RegisterClassExA(&cls);
+
+ hwnd = CreateWindowExA(0, "IconWindowClass", "icon test", 0,
+ 100, 100, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, NULL, NULL);
+ assert( hwnd );
+
+ res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 );
+ ok( res == 0, "wrong big icon %p/0\n", res );
+ res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_BIG, (LPARAM)icon );
+ ok( res == 0, "wrong previous big icon %p/0\n", res );
+ res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 );
+ ok( res == icon, "wrong big icon after set %p/%p\n", res, icon );
+ res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_BIG, (LPARAM)icon2 );
+ ok( res == icon, "wrong previous big icon %p/%p\n", res, icon );
+ res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 );
+ ok( res == icon2, "wrong big icon after set %p/%p\n", res, icon2 );
+
+ res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL, 0 );
+ ok( res == 0, "wrong small icon %p/0\n", res );
+ /* this test is XP specific */
+ /*res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL2, 0 );
+ ok( res != 0, "wrong small icon %p\n", res );*/
+ res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_SMALL, (LPARAM)icon );
+ ok( res == 0, "wrong previous small icon %p/0\n", res );
+ res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL, 0 );
+ ok( res == icon, "wrong small icon after set %p/%p\n", res, icon );
+ /* this test is XP specific */
+ /*res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL2, 0 );
+ ok( res == icon, "wrong small icon after set %p/%p\n", res, icon );*/
+ res = (HICON)SendMessageA( hwnd, WM_SETICON, ICON_SMALL, (LPARAM)small_icon );
+ ok( res == icon, "wrong previous small icon %p/%p\n", res, icon );
+ res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL, 0 );
+ ok( res == small_icon, "wrong small icon after set %p/%p\n", res, small_icon );
+ /* this test is XP specific */
+ /*res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_SMALL2, 0 );
+ ok( res == small_icon, "wrong small icon after set %p/%p\n", res, small_icon );*/
+
+ /* make sure the big icon hasn't changed */
+ res = (HICON)SendMessageA( hwnd, WM_GETICON, ICON_BIG, 0 );
+ ok( res == icon2, "wrong big icon after set %p/%p\n", res, icon2 );
+}
+
+static LRESULT WINAPI nccalcsize_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ if (msg == WM_NCCALCSIZE)
+ {
+ RECT *rect = (RECT *)lparam;
+ /* first time around increase the rectangle, next time decrease it */
+ if (rect->left == 100) InflateRect( rect, 10, 10 );
+ else InflateRect( rect, -10, -10 );
+ return 0;
+ }
+ return DefWindowProc( hwnd, msg, wparam, lparam );
+}
+
+static void test_SetWindowPos(HWND hwnd)
+{
+ RECT orig_win_rc, rect;
+ LONG_PTR old_proc;
+ BOOL is_win9x = GetWindowLongPtrW(hwnd, GWLP_WNDPROC) == 0;
+
+ SetRect(&rect, 111, 222, 333, 444);
+ ok(!GetWindowRect(0, &rect), "GetWindowRect succeeded\n");
+ ok(rect.left == 111 && rect.top == 222 && rect.right == 333 && rect.bottom == 444,
+ "wrong window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+ SetRect(&rect, 111, 222, 333, 444);
+ ok(!GetClientRect(0, &rect), "GetClientRect succeeded\n");
+ ok(rect.left == 111 && rect.top == 222 && rect.right == 333 && rect.bottom == 444,
+ "wrong window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+ GetWindowRect(hwnd, &orig_win_rc);
+
+ old_proc = SetWindowLongPtr( hwnd, GWLP_WNDPROC, (ULONG_PTR)nccalcsize_proc );
+ SetWindowPos(hwnd, 0, 100, 100, 0, 0, SWP_NOZORDER|SWP_FRAMECHANGED);
+ GetWindowRect( hwnd, &rect );
+ ok( rect.left == 100 && rect.top == 100 && rect.right == 100 && rect.bottom == 100,
+ "invalid window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ GetClientRect( hwnd, &rect );
+ MapWindowPoints( hwnd, 0, (POINT *)&rect, 2 );
+ ok( rect.left == 90 && rect.top == 90 && rect.right == 110 && rect.bottom == 110,
+ "invalid client rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+ SetWindowPos(hwnd, 0, 200, 200, 0, 0, SWP_NOZORDER|SWP_FRAMECHANGED);
+ GetWindowRect( hwnd, &rect );
+ ok( rect.left == 200 && rect.top == 200 && rect.right == 200 && rect.bottom == 200,
+ "invalid window rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+ GetClientRect( hwnd, &rect );
+ MapWindowPoints( hwnd, 0, (POINT *)&rect, 2 );
+ ok( rect.left == 210 && rect.top == 210 && rect.right == 190 && rect.bottom == 190,
+ "invalid client rect %ld,%ld-%ld,%ld\n", rect.left, rect.top, rect.right, rect.bottom );
+
+ SetWindowPos(hwnd, 0, orig_win_rc.left, orig_win_rc.top,
+ orig_win_rc.right, orig_win_rc.bottom, 0);
+ SetWindowLongPtr( hwnd, GWLP_WNDPROC, old_proc );
+
+ /* Win9x truncates coordinates to 16-bit irrespectively */
+ if (!is_win9x)
+ {
+ SetWindowPos(hwnd, 0, -32769, -40000, -32769, -90000, SWP_NOMOVE);
+ SetWindowPos(hwnd, 0, 32768, 40000, 32768, 40000, SWP_NOMOVE);
+
+ SetWindowPos(hwnd, 0, -32769, -40000, -32769, -90000, SWP_NOSIZE);
+ SetWindowPos(hwnd, 0, 32768, 40000, 32768, 40000, SWP_NOSIZE);
+ }
+
+ SetWindowPos(hwnd, 0, orig_win_rc.left, orig_win_rc.top,
+ orig_win_rc.right, orig_win_rc.bottom, 0);
+}
+
+static void test_SetMenu(HWND parent)
+{
+ HWND child;
+ HMENU hMenu, ret;
+ BOOL is_win9x = GetWindowLongPtrW(parent, GWLP_WNDPROC) == 0;
+ BOOL retok;
+ DWORD style;
+
+ hMenu = CreateMenu();
+ assert(hMenu);
+
+ ok(SetMenu(parent, hMenu), "SetMenu on a top level window should not fail\n");
+#if 0
+ /* fails on (at least) Wine, NT4, XP SP2 */
+ test_nonclient_area(parent);
+#endif
+ ret = GetMenu(parent);
+ ok(ret == hMenu, "unexpected menu id %p\n", ret);
+ /* test whether we can destroy a menu assigned to a window */
+ retok = DestroyMenu(hMenu);
+ ok( retok, "DestroyMenu error %ld\n", GetLastError());
+ ok(!IsMenu(hMenu), "menu handle should be not valid after DestroyMenu\n");
+ ret = GetMenu(parent);
+ /* This test fails on Win9x */
+ if (!is_win9x)
+ ok(ret == hMenu, "unexpected menu id %p\n", ret);
+ ok(SetMenu(parent, 0), "SetMenu(0) on a top level window should not fail\n");
+ test_nonclient_area(parent);
+
+ hMenu = CreateMenu();
+ assert(hMenu);
+
+ /* parent */
+ ret = GetMenu(parent);
+ ok(ret == 0, "unexpected menu id %p\n", ret);
+
+ ok(!SetMenu(parent, (HMENU)20), "SetMenu with invalid menu handle should fail\n");
+ test_nonclient_area(parent);
+ ret = GetMenu(parent);
+ ok(ret == 0, "unexpected menu id %p\n", ret);
+
+ ok(SetMenu(parent, hMenu), "SetMenu on a top level window should not fail\n");
+#if 0
+ /* fails on (at least) Wine, NT4, XP SP2 */
+ test_nonclient_area(parent);
+#endif
+ ret = GetMenu(parent);
+ ok(ret == hMenu, "unexpected menu id %p\n", ret);
+
+ ok(SetMenu(parent, 0), "SetMenu(0) on a top level window should not fail\n");
+ test_nonclient_area(parent);
+ ret = GetMenu(parent);
+ ok(ret == 0, "unexpected menu id %p\n", ret);
+
+ /* child */
+ child = CreateWindowExA(0, "static", NULL, WS_CHILD, 0, 0, 0, 0, parent, (HMENU)10, 0, NULL);
+ assert(child);
+
+ ret = GetMenu(child);
+ ok(ret == (HMENU)10, "unexpected menu id %p\n", ret);
+
+ ok(!SetMenu(child, (HMENU)20), "SetMenu with invalid menu handle should fail\n");
+ test_nonclient_area(child);
+ ret = GetMenu(child);
+ ok(ret == (HMENU)10, "unexpected menu id %p\n", ret);
+
+ ok(!SetMenu(child, hMenu), "SetMenu on a child window should fail\n");
+ test_nonclient_area(child);
+ ret = GetMenu(child);
+ ok(ret == (HMENU)10, "unexpected menu id %p\n", ret);
+
+ ok(!SetMenu(child, 0), "SetMenu(0) on a child window should fail\n");
+ test_nonclient_area(child);
+ ret = GetMenu(child);
+ ok(ret == (HMENU)10, "unexpected menu id %p\n", ret);
+
+ style = GetWindowLong(child, GWL_STYLE);
+ SetWindowLong(child, GWL_STYLE, style | WS_POPUP);
+ ok(SetMenu(child, hMenu), "SetMenu on a popup child window should not fail\n");
+ ok(SetMenu(child, 0), "SetMenu on a popup child window should not fail\n");
+ SetWindowLong(child, GWL_STYLE, style);
+
+ SetWindowLong(child, GWL_STYLE, style | WS_OVERLAPPED);
+ ok(!SetMenu(child, hMenu), "SetMenu on a overlapped child window should fail\n");
+ SetWindowLong(child, GWL_STYLE, style);
+
+ DestroyWindow(child);
+ DestroyMenu(hMenu);
+}
+
+static void test_window_tree(HWND parent, const DWORD *style, const int *order, int total)
+{
+ HWND child[5], hwnd;
+ int i;
+
+ assert(total <= 5);
+
+ hwnd = GetWindow(parent, GW_CHILD);
+ ok(!hwnd, "have to start without children to perform the test\n");
+
+ for (i = 0; i < total; i++)
+ {
+ if (style[i] & DS_CONTROL)
+ {
+ child[i] = CreateWindowExA(0, MAKEINTATOMA(32770), "", style[i] & ~WS_VISIBLE,
+ 0,0,0,0, parent, (HMENU)i, 0, NULL);
+ if (style[i] & WS_VISIBLE)
+ ShowWindow(child[i], SW_SHOW);
+
+ SetWindowPos(child[i], HWND_BOTTOM, 0,0,10,10, SWP_NOACTIVATE);
+ }
+ else
+ child[i] = CreateWindowExA(0, "static", "", style[i], 0,0,10,10,
+ parent, (HMENU)i, 0, NULL);
+ trace("child[%d] = %p\n", i, child[i]);
+ ok(child[i] != 0, "CreateWindowEx failed to create child window\n");
+ }
+
+ hwnd = GetWindow(parent, GW_CHILD);
+ ok(hwnd != 0, "GetWindow(GW_CHILD) failed\n");
+ ok(hwnd == GetWindow(child[total - 1], GW_HWNDFIRST), "GW_HWNDFIRST is wrong\n");
+ ok(child[order[total - 1]] == GetWindow(child[0], GW_HWNDLAST), "GW_HWNDLAST is wrong\n");
+
+ for (i = 0; i < total; i++)
+ {
+ trace("hwnd[%d] = %p\n", i, hwnd);
+ ok(child[order[i]] == hwnd, "Z order of child #%d is wrong\n", i);
+
+ hwnd = GetWindow(hwnd, GW_HWNDNEXT);
+ }
+
+ for (i = 0; i < total; i++)
+ ok(DestroyWindow(child[i]), "DestroyWindow failed\n");
+}
+
+static void test_children_zorder(HWND parent)
+{
+ const DWORD simple_style[5] = { WS_CHILD, WS_CHILD, WS_CHILD, WS_CHILD,
+ WS_CHILD };
+ const int simple_order[5] = { 0, 1, 2, 3, 4 };
+
+ const DWORD complex_style[5] = { WS_CHILD, WS_CHILD | WS_MAXIMIZE,
+ WS_CHILD | WS_VISIBLE, WS_CHILD,
+ WS_CHILD | WS_MAXIMIZE | WS_VISIBLE };
+ const int complex_order_1[1] = { 0 };
+ const int complex_order_2[2] = { 1, 0 };
+ const int complex_order_3[3] = { 1, 0, 2 };
+ const int complex_order_4[4] = { 1, 0, 2, 3 };
+ const int complex_order_5[5] = { 4, 1, 0, 2, 3 };
+ const DWORD complex_style_6[3] = { WS_CHILD | WS_VISIBLE,
+ WS_CHILD | WS_CLIPSIBLINGS | DS_CONTROL | WS_VISIBLE,
+ WS_CHILD | WS_VISIBLE };
+ const int complex_order_6[3] = { 0, 1, 2 };
+
+ /* simple WS_CHILD */
+ test_window_tree(parent, simple_style, simple_order, 5);
+
+ /* complex children styles */
+ test_window_tree(parent, complex_style, complex_order_1, 1);
+ test_window_tree(parent, complex_style, complex_order_2, 2);
+ test_window_tree(parent, complex_style, complex_order_3, 3);
+ test_window_tree(parent, complex_style, complex_order_4, 4);
+ test_window_tree(parent, complex_style, complex_order_5, 5);
+
+ /* another set of complex children styles */
+ test_window_tree(parent, complex_style_6, complex_order_6, 3);
+}
+
+static void test_vis_rgn( HWND hwnd )
+{
+ RECT win_rect, rgn_rect;
+ HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
+ HDC hdc;
+
+ ShowWindow(hwnd,SW_SHOW);
+ hdc = GetDC( hwnd );
+ ok( GetRandomRgn( hdc, hrgn, SYSRGN ) != 0, "GetRandomRgn failed\n" );
+ GetWindowRect( hwnd, &win_rect );
+ GetRgnBox( hrgn, &rgn_rect );
+ if (GetVersion() & 0x80000000)
+ {
+ trace("win9x, mapping to screen coords\n");
+ MapWindowPoints( hwnd, 0, (POINT *)&rgn_rect, 2 );
+ }
+ trace("win: %ld,%ld-%ld,%ld\n", win_rect.left, win_rect.top, win_rect.right, win_rect.bottom );
+ trace("rgn: %ld,%ld-%ld,%ld\n", rgn_rect.left, rgn_rect.top, rgn_rect.right, rgn_rect.bottom );
+ ok( win_rect.left <= rgn_rect.left, "rgn left %ld not inside win rect %ld\n",
+ rgn_rect.left, win_rect.left );
+ ok( win_rect.top <= rgn_rect.top, "rgn top %ld not inside win rect %ld\n",
+ rgn_rect.top, win_rect.top );
+ ok( win_rect.right >= rgn_rect.right, "rgn right %ld not inside win rect %ld\n",
+ rgn_rect.right, win_rect.right );
+ ok( win_rect.bottom >= rgn_rect.bottom, "rgn bottom %ld not inside win rect %ld\n",
+ rgn_rect.bottom, win_rect.bottom );
+ ReleaseDC( hwnd, hdc );
+}
+
+static void test_SetFocus(HWND hwnd)
+{
+ HWND child;
+
+ /* check if we can set focus to non-visible windows */
+
+ ShowWindow(hwnd, SW_SHOW);
+ SetFocus(0);
+ SetFocus(hwnd);
+ ok( GetFocus() == hwnd, "Failed to set focus to visible window %p\n", hwnd );
+ ok( GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE, "Window %p not visible\n", hwnd );
+ ShowWindow(hwnd, SW_HIDE);
+ SetFocus(0);
+ SetFocus(hwnd);
+ ok( GetFocus() == hwnd, "Failed to set focus to invisible window %p\n", hwnd );
+ ok( !(GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE), "Window %p still visible\n", hwnd );
+ child = CreateWindowExA(0, "static", NULL, WS_CHILD, 0, 0, 0, 0, hwnd, 0, 0, NULL);
+ assert(child);
+ SetFocus(child);
+ ok( GetFocus() == child, "Failed to set focus to invisible child %p\n", child );
+ ok( !(GetWindowLong(child,GWL_STYLE) & WS_VISIBLE), "Child %p is visible\n", child );
+ ShowWindow(child, SW_SHOW);
+ ok( GetWindowLong(child,GWL_STYLE) & WS_VISIBLE, "Child %p is not visible\n", child );
+ ok( GetFocus() == child, "Focus no longer on child %p\n", child );
+ ShowWindow(child, SW_HIDE);
+ ok( !(GetWindowLong(child,GWL_STYLE) & WS_VISIBLE), "Child %p is visible\n", child );
+ ok( GetFocus() == hwnd, "Focus should be on parent %p, not %p\n", hwnd, GetFocus() );
+ ShowWindow(child, SW_SHOW);
+ SetFocus(child);
+ ok( GetFocus() == child, "Focus should be on child %p\n", child );
+ SetWindowPos(child,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_HIDEWINDOW);
+ ok( GetFocus() == child, "Focus should still be on child %p\n", child );
+
+ ShowWindow(child, SW_HIDE);
+ SetFocus(hwnd);
+ ok( GetFocus() == hwnd, "Focus should be on parent %p, not %p\n", hwnd, GetFocus() );
+ SetWindowPos(child,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
+ ok( GetFocus() == hwnd, "Focus should still be on parent %p, not %p\n", hwnd, GetFocus() );
+ ShowWindow(child, SW_HIDE);
+ ok( GetFocus() == hwnd, "Focus should still be on parent %p, not %p\n", hwnd, GetFocus() );
+
+ ShowWindow(hwnd, SW_SHOW);
+ ShowWindow(child, SW_SHOW);
+ SetFocus(child);
+ ok( GetFocus() == child, "Focus should be on child %p\n", child );
+ EnableWindow(hwnd, FALSE);
+ ok( GetFocus() == child, "Focus should still be on child %p\n", child );
+ EnableWindow(hwnd, TRUE);
+
+ DestroyWindow( child );
+}
+
+static void test_SetActiveWindow(HWND hwnd)
+{
+ HWND hwnd2;
+
+ ShowWindow(hwnd, SW_SHOW);
+ SetActiveWindow(0);
+ SetActiveWindow(hwnd);
+ ok( GetActiveWindow() == hwnd, "Failed to set focus to visible window %p\n", hwnd );
+ SetWindowPos(hwnd,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_HIDEWINDOW);
+ ok( GetActiveWindow() == hwnd, "Window %p no longer active\n", hwnd );
+ SetWindowPos(hwnd,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_SHOWWINDOW);
+ ShowWindow(hwnd, SW_HIDE);
+ ok( GetActiveWindow() != hwnd, "Window %p is still active\n", hwnd );
+
+ /* trace("**testing an invisible window now\n"); */
+ SetActiveWindow(hwnd);
+ ok( GetActiveWindow() == hwnd, "Window %p not active\n", hwnd );
+ ok( !(GetWindowLong(hwnd,GWL_STYLE) & WS_VISIBLE), "Window %p is visible\n", hwnd );
+
+ ShowWindow(hwnd, SW_SHOW);
+
+ hwnd2 = CreateWindowExA(0, "static", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, hwnd, 0, 0, NULL);
+ ok( GetActiveWindow() == hwnd2, "Window %p is not active\n", hwnd2 );
+ DestroyWindow(hwnd2);
+ ok( GetActiveWindow() != hwnd2, "Window %p is still active\n", hwnd2 );
+
+ hwnd2 = CreateWindowExA(0, "static", NULL, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, hwnd, 0, 0, NULL);
+ ok( GetActiveWindow() == hwnd2, "Window %p is not active\n", hwnd2 );
+ SetWindowPos(hwnd2,0,0,0,0,0,SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_HIDEWINDOW);
+ ok( GetActiveWindow() == hwnd2, "Window %p no longer active (%p)\n", hwnd2, GetActiveWindow() );
+ DestroyWindow(hwnd2);
+ ok( GetActiveWindow() != hwnd2, "Window %p is still active\n", hwnd2 );
+}
+
+static void check_wnd_state(HWND active, HWND foreground, HWND focus, HWND capture)
+{
+ ok(active == GetActiveWindow(), "GetActiveWindow() = %p\n", GetActiveWindow());
+ if (foreground)
+ ok(foreground == GetForegroundWindow(), "GetForegroundWindow() = %p\n", GetForegroundWindow());
+ ok(focus == GetFocus(), "GetFocus() = %p\n", GetFocus());
+ ok(capture == GetCapture(), "GetCapture() = %p\n", GetCapture());
+}
+
+static WNDPROC old_button_proc;
+
+static LRESULT WINAPI button_hook_proc(HWND button, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ LRESULT ret;
+ USHORT key_state;
+
+ key_state = GetKeyState(VK_LBUTTON);
+ ok(!(key_state & 0x8000), "VK_LBUTTON should not be pressed, state %04x\n", key_state);
+
+ ret = CallWindowProcA(old_button_proc, button, msg, wparam, lparam);
+
+ if (msg == WM_LBUTTONDOWN)
+ {
+ HWND hwnd, capture;
+
+ check_wnd_state(button, button, button, button);
+
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL);
+ assert(hwnd);
+ trace("hwnd %p\n", hwnd);
+
+ check_wnd_state(button, button, button, button);
+
+ ShowWindow(hwnd, SW_SHOWNOACTIVATE);
+
+ check_wnd_state(button, button, button, button);
+
+ DestroyWindow(hwnd);
+
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL);
+ assert(hwnd);
+ trace("hwnd %p\n", hwnd);
+
+ check_wnd_state(button, button, button, button);
+
+ /* button wnd proc should release capture on WM_KILLFOCUS if it does
+ * match internal button state.
+ */
+ SendMessage(button, WM_KILLFOCUS, 0, 0);
+ check_wnd_state(button, button, button, 0);
+
+ ShowWindow(hwnd, SW_SHOW);
+ check_wnd_state(hwnd, hwnd, hwnd, 0);
+
+ capture = SetCapture(hwnd);
+ ok(capture == 0, "SetCapture() = %p\n", capture);
+
+ check_wnd_state(hwnd, hwnd, hwnd, hwnd);
+
+ DestroyWindow(hwnd);
+
+ check_wnd_state(button, 0, button, 0);
+ }
+
+ return ret;
+}
+
+static void test_capture_1(void)
+{
+ HWND button, capture;
+
+ capture = GetCapture();
+ ok(capture == 0, "GetCapture() = %p\n", capture);
+
+ button = CreateWindowExA(0, "button", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+ assert(button);
+ trace("button %p\n", button);
+
+ old_button_proc = (WNDPROC)SetWindowLongPtrA(button, GWLP_WNDPROC, (LONG_PTR)button_hook_proc);
+
+ SendMessageA(button, WM_LBUTTONDOWN, 0, 0);
+
+ capture = SetCapture(button);
+ ok(capture == 0, "SetCapture() = %p\n", capture);
+ check_wnd_state(button, 0, button, button);
+
+ DestroyWindow(button);
+ check_wnd_state(0, 0, 0, 0);
+}
+
+static void test_capture_2(void)
+{
+ HWND button, hwnd, capture;
+
+ check_wnd_state(0, 0, 0, 0);
+
+ button = CreateWindowExA(0, "button", NULL, WS_POPUP | WS_VISIBLE, 0, 0, 10, 10, 0, 0, 0, NULL);
+ assert(button);
+ trace("button %p\n", button);
+
+ check_wnd_state(button, button, button, 0);
+
+ capture = SetCapture(button);
+ ok(capture == 0, "SetCapture() = %p\n", capture);
+
+ check_wnd_state(button, button, button, button);
+
+ /* button wnd proc should ignore WM_KILLFOCUS if it doesn't match
+ * internal button state.
+ */
+ SendMessage(button, WM_KILLFOCUS, 0, 0);
+ check_wnd_state(button, button, button, button);
+
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL);
+ assert(hwnd);
+ trace("hwnd %p\n", hwnd);
+
+ check_wnd_state(button, button, button, button);
+
+ ShowWindow(hwnd, SW_SHOWNOACTIVATE);
+
+ check_wnd_state(button, button, button, button);
+
+ DestroyWindow(hwnd);
+
+ hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP, 0, 0, 10, 10, 0, 0, 0, NULL);
+ assert(hwnd);
+ trace("hwnd %p\n", hwnd);
+
+ check_wnd_state(button, button, button, button);
+
+ ShowWindow(hwnd, SW_SHOW);
+
+ check_wnd_state(hwnd, hwnd, hwnd, button);
+
+ capture = SetCapture(hwnd);
+ ok(capture == button, "SetCapture() = %p\n", capture);
+
+ check_wnd_state(hwnd, hwnd, hwnd, hwnd);
+
+ DestroyWindow(hwnd);
+ check_wnd_state(button, button, button, 0);
+
+ DestroyWindow(button);
+ check_wnd_state(0, 0, 0, 0);
+}
+
+static void test_capture_3(HWND hwnd1, HWND hwnd2)
+{
+ BOOL ret;
+
+ ShowWindow(hwnd1, SW_HIDE);
+ ShowWindow(hwnd2, SW_HIDE);
+
+ ok(!IsWindowVisible(hwnd1), "%p should be invisible\n", hwnd1);
+ ok(!IsWindowVisible(hwnd2), "%p should be invisible\n", hwnd2);
+
+ SetCapture(hwnd1);
+ check_wnd_state(0, 0, 0, hwnd1);
+
+ SetCapture(hwnd2);
+ check_wnd_state(0, 0, 0, hwnd2);
+
+ ShowWindow(hwnd1, SW_SHOW);
+ check_wnd_state(hwnd1, hwnd1, hwnd1, hwnd2);
+
+ ret = ReleaseCapture();
+ ok (ret, "releasecapture did not return TRUE.\n");
+ ret = ReleaseCapture();
+ ok (ret, "releasecapture did not return TRUE after second try.\n");
+}
+
+static void test_keyboard_input(HWND hwnd)
+{
+ MSG msg;
+ BOOL ret;
+
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow(hwnd);
+
+ ok(GetActiveWindow() == hwnd, "wrong active window %p\n", GetActiveWindow());
+
+ SetFocus(hwnd);
+ ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus());
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+
+ PostMessageA(hwnd, WM_KEYDOWN, 0, 0);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+
+ ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus());
+
+ PostThreadMessageA(GetCurrentThreadId(), WM_KEYDOWN, 0, 0);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(!msg.hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+
+ ok(GetFocus() == hwnd, "wrong focus window %p\n", GetFocus());
+
+ keybd_event(VK_SPACE, 0, 0, 0);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+
+ SetFocus(0);
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessage(&msg);
+
+ PostMessageA(hwnd, WM_KEYDOWN, 0, 0);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ PostThreadMessageA(GetCurrentThreadId(), WM_KEYDOWN, 0, 0);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(!msg.hwnd && msg.message == WM_KEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+
+ ok(GetFocus() == 0, "wrong focus window %p\n", GetFocus());
+
+ keybd_event(VK_SPACE, 0, 0, 0);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == hwnd && msg.message == WM_SYSKEYDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+}
+
+static void test_mouse_input(HWND hwnd)
+{
+ RECT rc;
+ POINT pt;
+ int x, y;
+ HWND popup;
+ MSG msg;
+ BOOL ret;
+ LRESULT res;
+
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow(hwnd);
+
+ GetWindowRect(hwnd, &rc);
+ trace("main window %p: (%ld,%ld)-(%ld,%ld)\n", hwnd, rc.left, rc.top, rc.right, rc.bottom);
+
+ popup = CreateWindowExA(0, "MainWindowClass", NULL, WS_POPUP,
+ rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
+ hwnd, 0, 0, NULL);
+ assert(popup != 0);
+ ShowWindow(popup, SW_SHOW);
+ UpdateWindow(popup);
+
+ GetWindowRect(popup, &rc);
+ trace("popup window %p: (%ld,%ld)-(%ld,%ld)\n", popup, rc.left, rc.top, rc.right, rc.bottom);
+
+ x = rc.left + (rc.right - rc.left) / 2;
+ y = rc.top + (rc.bottom - rc.top) / 2;
+ trace("setting cursor to (%d,%d)\n", x, y);
+
+ SetCursorPos(x, y);
+ GetCursorPos(&pt);
+ ok(x == pt.x && y == pt.y, "wrong cursor pos (%ld,%ld), expected (%d,%d)\n", pt.x, pt.y, x, y);
+
+ /* force the system to update its internal queue mouse position,
+ * otherwise it won't generate relative mouse movements below.
+ */
+ mouse_event(MOUSEEVENTF_MOVE, -1, -1, 0, 0);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+
+ msg.message = 0;
+ mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_MOUSEMOVE, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ /* FIXME: SetCursorPos in Wine generates additional WM_MOUSEMOVE message */
+ if (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
+ ok(msg.hwnd == popup && msg.message == WM_MOUSEMOVE, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+
+ mouse_event(MOUSEEVENTF_MOVE, -1, -1, 0, 0);
+ ShowWindow(popup, SW_HIDE);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == hwnd && msg.message == WM_MOUSEMOVE, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+
+ mouse_event(MOUSEEVENTF_MOVE, 1, 1, 0, 0);
+ ShowWindow(hwnd, SW_HIDE);
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok( !ret, "message %04x available\n", msg.message);
+
+ /* test mouse clicks */
+
+ ShowWindow(hwnd, SW_SHOW);
+ ShowWindow(popup, SW_SHOW);
+
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+
+ ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+
+ ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+
+ ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONDBLCLK, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONDBLCLK, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+
+ ok(PeekMessageA(&msg, 0, 0, 0, 0), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+
+ ret = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
+ ok(!ret, "message %04x available\n", msg.message);
+
+ ShowWindow(popup, SW_HIDE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
+ mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
+
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == hwnd && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == hwnd && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+
+ test_lbuttondown_flag = TRUE;
+ SendMessageA(hwnd, WM_COMMAND, (WPARAM)popup, 0);
+ test_lbuttondown_flag = FALSE;
+
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONDOWN, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+ ok(msg.hwnd == popup && msg.message == WM_LBUTTONUP, "hwnd %p message %04x\n", msg.hwnd, msg.message);
+ ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "no message available\n");
+
+ /* Test WM_MOUSEACTIVATE */
+#define TEST_MOUSEACTIVATE(A,B) \
+ res = SendMessageA(hwnd, WM_MOUSEACTIVATE, (WPARAM)hwnd, (LPARAM)MAKELRESULT(A,0)); \
+ ok(res == B, "WM_MOUSEACTIVATE for %s returned %ld\n", #A, res);
+
+ TEST_MOUSEACTIVATE(HTERROR,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTTRANSPARENT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTNOWHERE,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTCLIENT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTCAPTION,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTSYSMENU,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTSIZE,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTMENU,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTHSCROLL,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTVSCROLL,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTMINBUTTON,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTMAXBUTTON,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTLEFT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTRIGHT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTTOP,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTTOPLEFT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTTOPRIGHT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTBOTTOM,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTBOTTOMLEFT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTBOTTOMRIGHT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTBORDER,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTOBJECT,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTCLOSE,MA_ACTIVATE);
+ TEST_MOUSEACTIVATE(HTHELP,MA_ACTIVATE);
+
+ /* Clear any messages left behind by WM_MOUSEACTIVATE tests */
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+
+ DestroyWindow(popup);
+}
+
+static void test_validatergn(HWND hwnd)
+{
+ HWND child;
+ RECT rc, rc2;
+ HRGN rgn;
+ int ret;
+ child = CreateWindowExA(0, "static", NULL, WS_CHILD| WS_VISIBLE, 10, 10, 10, 10, hwnd, 0, 0, NULL);
+ ShowWindow(hwnd, SW_SHOW);
+ UpdateWindow( hwnd);
+ /* test that ValidateRect validates children*/
+ InvalidateRect( child, NULL, 1);
+ GetWindowRect( child, &rc);
+ MapWindowPoints( NULL, hwnd, (POINT*) &rc, 2);
+ ret = GetUpdateRect( child, &rc2, 0);
+ ok( rc2.right > rc2.left && rc2.bottom > rc2.top,
+ "Update rectangle is empty!\n");
+ ValidateRect( hwnd, &rc);
+ ret = GetUpdateRect( child, &rc2, 0);
+ ok( rc2.left == 0 && rc2.top == 0 && rc2.right == 0 && rc2.bottom == 0,
+ "Update rectangle %ld,%ld-%ld,%ld is not empty!\n", rc2.left, rc2.top,
+ rc2.right, rc2.bottom);
+
+ /* now test ValidateRgn */
+ InvalidateRect( child, NULL, 1);
+ GetWindowRect( child, &rc);
+ MapWindowPoints( NULL, hwnd, (POINT*) &rc, 2);
+ rgn = CreateRectRgnIndirect( &rc);
+ ValidateRgn( hwnd, rgn);
+ ret = GetUpdateRect( child, &rc2, 0);
+ ok( rc2.left == 0 && rc2.top == 0 && rc2.right == 0 && rc2.bottom == 0,
+ "Update rectangle %ld,%ld-%ld,%ld is not empty!\n", rc2.left, rc2.top,
+ rc2.right, rc2.bottom);
+
+ DeleteObject( rgn);
+ DestroyWindow( child );
+}
+
+static void nccalchelper(HWND hwnd, INT x, INT y, RECT *prc)
+{
+ MoveWindow( hwnd, 0, 0, x, y, 0);
+ GetWindowRect( hwnd, prc);
+ trace("window rect is %ld,%ld - %ld,%ld\n",
+ prc->left,prc->top,prc->right,prc->bottom);
+ DefWindowProcA(hwnd, WM_NCCALCSIZE, 0, (LPARAM)prc);
+ trace("nccalc rect is %ld,%ld - %ld,%ld\n",
+ prc->left,prc->top,prc->right,prc->bottom);
+}
+
+static void test_nccalcscroll(HWND parent)
+{
+ RECT rc1;
+ INT sbheight = GetSystemMetrics( SM_CYHSCROLL);
+ INT sbwidth = GetSystemMetrics( SM_CXVSCROLL);
+ HWND hwnd = CreateWindowExA(0, "static", NULL,
+ WS_CHILD| WS_VISIBLE | WS_VSCROLL | WS_HSCROLL ,
+ 10, 10, 200, 200, parent, 0, 0, NULL);
+ ShowWindow( parent, SW_SHOW);
+ UpdateWindow( parent);
+
+ /* test window too low for a horizontal scroll bar */
+ nccalchelper( hwnd, 100, sbheight, &rc1);
+ ok( rc1.bottom - rc1.top == sbheight, "Height should be %d size is %ld,%ld - %ld,%ld\n",
+ sbheight, rc1.left, rc1.top, rc1.right, rc1.bottom);
+
+ /* test window just high enough for a horizontal scroll bar */
+ nccalchelper( hwnd, 100, sbheight + 1, &rc1);
+ ok( rc1.bottom - rc1.top == 1, "Height should be %d size is %ld,%ld - %ld,%ld\n",
+ 1, rc1.left, rc1.top, rc1.right, rc1.bottom);
+
+ /* test window too narrow for a vertical scroll bar */
+ nccalchelper( hwnd, sbwidth - 1, 100, &rc1);
+ ok( rc1.right - rc1.left == sbwidth - 1 , "Width should be %d size is %ld,%ld - %ld,%ld\n",
+ sbwidth - 1, rc1.left, rc1.top, rc1.right, rc1.bottom);
+
+ /* test window just wide enough for a vertical scroll bar */
+ nccalchelper( hwnd, sbwidth, 100, &rc1);
+ ok( rc1.right - rc1.left == 0, "Width should be %d size is %ld,%ld - %ld,%ld\n",
+ 0, rc1.left, rc1.top, rc1.right, rc1.bottom);
+
+ /* same test, but with client edge: not enough width */
+ SetWindowLong( hwnd, GWL_EXSTYLE, WS_EX_CLIENTEDGE | GetWindowLong( hwnd, GWL_EXSTYLE));
+ nccalchelper( hwnd, sbwidth, 100, &rc1);
+ ok( rc1.right - rc1.left == sbwidth - 2 * GetSystemMetrics(SM_CXEDGE),
+ "Width should be %d size is %ld,%ld - %ld,%ld\n",
+ sbwidth - 2 * GetSystemMetrics(SM_CXEDGE), rc1.left, rc1.top, rc1.right, rc1.bottom);
+
+ DestroyWindow( hwnd);
+}
+
+static void test_SetParent(void)
+{
+ BOOL ret;
+ HWND desktop = GetDesktopWindow();
+ HMENU hMenu;
+ BOOL is_win9x = GetWindowLongPtrW(desktop, GWLP_WNDPROC) == 0;
+ HWND parent, child1, child2, child3, child4, sibling;
+
+ parent = CreateWindowExA(0, "static", NULL, WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ assert(parent != 0);
+ child1 = CreateWindowExA(0, "static", NULL, WS_CHILD,
+ 0, 0, 50, 50, parent, 0, 0, NULL);
+ assert(child1 != 0);
+ child2 = CreateWindowExA(0, "static", NULL, WS_POPUP,
+ 0, 0, 50, 50, child1, 0, 0, NULL);
+ assert(child2 != 0);
+ child3 = CreateWindowExA(0, "static", NULL, WS_CHILD,
+ 0, 0, 50, 50, child2, 0, 0, NULL);
+ assert(child3 != 0);
+ child4 = CreateWindowExA(0, "static", NULL, WS_POPUP,
+ 0, 0, 50, 50, child3, 0, 0, NULL);
+ assert(child4 != 0);
+
+ trace("parent %p, child1 %p, child2 %p, child3 %p, child4 %p\n",
+ parent, child1, child2, child3, child4);
+
+ check_parents(parent, desktop, 0, 0, 0, parent, parent);
+ check_parents(child1, parent, parent, parent, 0, parent, parent);
+ check_parents(child2, desktop, parent, parent, parent, child2, parent);
+ check_parents(child3, child2, child2, child2, 0, child2, parent);
+ check_parents(child4, desktop, child2, child2, child2, child4, parent);
+
+todo_wine {
+ ok(!IsChild(desktop, parent), "wrong parent/child %p/%p\n", desktop, parent);
+ ok(!IsChild(desktop, child1), "wrong parent/child %p/%p\n", desktop, child1);
+ ok(!IsChild(desktop, child2), "wrong parent/child %p/%p\n", desktop, child2);
+ ok(!IsChild(desktop, child3), "wrong parent/child %p/%p\n", desktop, child3);
+ ok(!IsChild(desktop, child4), "wrong parent/child %p/%p\n", desktop, child4);
+}
+
+ ok(IsChild(parent, child1), "wrong parent/child %p/%p\n", parent, child1);
+todo_wine {
+ ok(!IsChild(desktop, child2), "wrong parent/child %p/%p\n", desktop, child2);
+}
+ ok(!IsChild(parent, child2), "wrong parent/child %p/%p\n", parent, child2);
+ ok(!IsChild(child1, child2), "wrong parent/child %p/%p\n", child1, child2);
+ ok(!IsChild(parent, child3), "wrong parent/child %p/%p\n", parent, child3);
+ ok(IsChild(child2, child3), "wrong parent/child %p/%p\n", child2, child3);
+ ok(!IsChild(parent, child4), "wrong parent/child %p/%p\n", parent, child4);
+ ok(!IsChild(child3, child4), "wrong parent/child %p/%p\n", child3, child4);
+todo_wine {
+ ok(!IsChild(desktop, child4), "wrong parent/child %p/%p\n", desktop, child4);
+}
+
+ if (!is_win9x) /* Win9x doesn't survive this test */
+ {
+ ok(!SetParent(parent, child1), "SetParent should fail\n");
+ ok(!SetParent(child2, child3), "SetParent should fail\n");
+ ok(SetParent(child1, parent) != 0, "SetParent should not fail\n");
+ ok(SetParent(parent, child2) != 0, "SetParent should not fail\n");
+ ok(SetParent(parent, child3) != 0, "SetParent should not fail\n");
+ ok(!SetParent(child2, parent), "SetParent should fail\n");
+ ok(SetParent(parent, child4) != 0, "SetParent should not fail\n");
+
+ check_parents(parent, child4, child4, 0, 0, child4, parent);
+ check_parents(child1, parent, parent, parent, 0, child4, parent);
+ check_parents(child2, desktop, parent, parent, parent, child2, parent);
+ check_parents(child3, child2, child2, child2, 0, child2, parent);
+ check_parents(child4, desktop, child2, child2, child2, child4, parent);
+ }
+
+ hMenu = CreateMenu();
+ sibling = CreateWindowExA(0, "static", NULL, WS_OVERLAPPEDWINDOW,
+ 100, 100, 200, 200, 0, hMenu, 0, NULL);
+ assert(sibling != 0);
+
+ ok(SetParent(sibling, parent) != 0, "SetParent should not fail\n");
+ ok(GetMenu(sibling) == hMenu, "SetParent should not remove menu\n");
+
+ ret = DestroyWindow(parent);
+ ok( ret, "DestroyWindow() error %ld\n", GetLastError());
+
+ ok(!IsWindow(parent), "parent still exists\n");
+ ok(!IsWindow(sibling), "sibling still exists\n");
+ ok(!IsWindow(child1), "child1 still exists\n");
+ ok(!IsWindow(child2), "child2 still exists\n");
+ ok(!IsWindow(child3), "child3 still exists\n");
+ ok(!IsWindow(child4), "child4 still exists\n");
+}
+
+static LRESULT WINAPI StyleCheckProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ LPCREATESTRUCT lpcs;
+ LPSTYLESTRUCT lpss;
+
+ switch (msg)
+ {
+ case WM_NCCREATE:
+ case WM_CREATE:
+ lpcs = (LPCREATESTRUCT)lparam;
+ lpss = (LPSTYLESTRUCT)lpcs->lpCreateParams;
+ if (lpss)
+ {
+ if ((lpcs->dwExStyle & WS_EX_DLGMODALFRAME) ||
+ ((!(lpcs->dwExStyle & WS_EX_STATICEDGE)) &&
+ (lpcs->style & (WS_DLGFRAME | WS_THICKFRAME))))
+ ok(lpcs->dwExStyle & WS_EX_WINDOWEDGE, "Window should have WS_EX_WINDOWEDGE style\n");
+ else
+ ok(!(lpcs->dwExStyle & WS_EX_WINDOWEDGE), "Window shouldn't have WS_EX_WINDOWEDGE style\n");
+
+ ok((lpss->styleOld & ~WS_EX_WINDOWEDGE) == (lpcs->dwExStyle & ~WS_EX_WINDOWEDGE),
+ "Ex style (0x%08lx) should match what the caller passed to CreateWindowEx (0x%08lx)\n",
+ (lpss->styleOld & ~WS_EX_WINDOWEDGE), (lpcs->dwExStyle & ~WS_EX_WINDOWEDGE));
+
+ ok(lpss->styleNew == lpcs->style,
+ "Style (0x%08lx) should match what the caller passed to CreateWindowEx (0x%08lx)\n",
+ lpss->styleNew, lpcs->style);
+ }
+ break;
+ }
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+static ATOM atomStyleCheckClass;
+
+static void register_style_check_class(void)
+{
+ WNDCLASS wc =
+ {
+ 0,
+ StyleCheckProc,
+ 0,
+ 0,
+ GetModuleHandle(NULL),
+ NULL,
+ LoadCursor(NULL, IDC_ARROW),
+ (HBRUSH)(COLOR_BTNFACE+1),
+ NULL,
+ TEXT("WineStyleCheck"),
+ };
+
+ atomStyleCheckClass = RegisterClass(&wc);
+}
+
+static void check_window_style(DWORD dwStyleIn, DWORD dwExStyleIn, DWORD dwStyleOut, DWORD dwExStyleOut)
+{
+ DWORD dwActualStyle;
+ DWORD dwActualExStyle;
+ STYLESTRUCT ss;
+ HWND hwnd;
+ HWND hwndParent = NULL;
+ MSG msg;
+
+ ss.styleNew = dwStyleIn;
+ ss.styleOld = dwExStyleIn;
+
+ if (dwStyleIn & WS_CHILD)
+ {
+ hwndParent = CreateWindowEx(0, MAKEINTATOM(atomStyleCheckClass), NULL,
+ WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+ }
+
+ hwnd = CreateWindowEx(dwExStyleIn, MAKEINTATOM(atomStyleCheckClass), NULL,
+ dwStyleIn, 0, 0, 0, 0, hwndParent, NULL, NULL, &ss);
+ assert(hwnd);
+
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ dwActualStyle = GetWindowLong(hwnd, GWL_STYLE);
+ dwActualExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ ok((dwActualStyle == dwStyleOut) && (dwActualExStyle == dwExStyleOut),
+ "Style (0x%08lx) should really be 0x%08lx and/or Ex style (0x%08lx) should really be 0x%08lx\n",
+ dwActualStyle, dwStyleOut, dwActualExStyle, dwExStyleOut);
+
+ DestroyWindow(hwnd);
+ if (hwndParent) DestroyWindow(hwndParent);
+}
+
+/* tests what window styles the window manager automatically adds */
+static void test_window_styles(void)
+{
+ register_style_check_class();
+
+ check_window_style(0, 0, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_WINDOWEDGE);
+ check_window_style(WS_OVERLAPPEDWINDOW, 0, WS_CLIPSIBLINGS|WS_OVERLAPPEDWINDOW, WS_EX_WINDOWEDGE);
+ check_window_style(WS_CHILD, 0, WS_CHILD, 0);
+ check_window_style(WS_CHILD, WS_EX_WINDOWEDGE, WS_CHILD, 0);
+ check_window_style(0, WS_EX_TOOLWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_WINDOWEDGE|WS_EX_TOOLWINDOW);
+ check_window_style(WS_POPUP, 0, WS_POPUP|WS_CLIPSIBLINGS, 0);
+ check_window_style(WS_POPUP, WS_EX_WINDOWEDGE, WS_POPUP|WS_CLIPSIBLINGS, 0);
+ check_window_style(WS_CHILD, WS_EX_DLGMODALFRAME, WS_CHILD, WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME);
+ check_window_style(WS_CHILD, WS_EX_DLGMODALFRAME|WS_EX_STATICEDGE, WS_CHILD, WS_EX_STATICEDGE|WS_EX_WINDOWEDGE|WS_EX_DLGMODALFRAME);
+ check_window_style(WS_CAPTION, WS_EX_STATICEDGE, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_STATICEDGE|WS_EX_WINDOWEDGE);
+ check_window_style(0, WS_EX_APPWINDOW, WS_CLIPSIBLINGS|WS_CAPTION, WS_EX_APPWINDOW|WS_EX_WINDOWEDGE);
+}
+
+static void test_scrollvalidate( HWND parent)
+{
+ HDC hdc;
+ HRGN hrgn=CreateRectRgn(0,0,0,0);
+ HRGN exprgn, tmprgn, clipping;
+ RECT rc, rcu, cliprc;
+ /* create two overlapping child windows. The visual region
+ * of hwnd1 is clipped by the overlapping part of
+ * hwnd2 because of the WS_CLIPSIBLING style */
+ HWND hwnd1, hwnd2;
+
+ clipping = CreateRectRgn(0,0,0,0);
+ tmprgn = CreateRectRgn(0,0,0,0);
+ exprgn = CreateRectRgn(0,0,0,0);
+ hwnd2 = CreateWindowExA(0, "static", NULL,
+ WS_CHILD| WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER ,
+ 75, 30, 100, 100, parent, 0, 0, NULL);
+ hwnd1 = CreateWindowExA(0, "static", NULL,
+ WS_CHILD| WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER ,
+ 25, 50, 100, 100, parent, 0, 0, NULL);
+ ShowWindow( parent, SW_SHOW);
+ UpdateWindow( parent);
+ GetClientRect( hwnd1, &rc);
+ cliprc=rc;
+ SetRectRgn( clipping, 10, 10, 90, 90);
+ hdc = GetDC( hwnd1);
+ /* for a visual touch */
+ TextOut( hdc, 0,10, "0123456789", 10);
+ ScrollDC( hdc, -10, -5, &rc, &cliprc, hrgn, &rcu);
+ if (winetest_debug > 0) dump_region(hrgn);
+ /* create a region with what is expected */
+ SetRectRgn( exprgn, 39,0,49,74);
+ SetRectRgn( tmprgn, 88,79,98,93);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ SetRectRgn( tmprgn, 0,93,98,98);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ ok( EqualRgn( exprgn, hrgn), "wrong update region\n");
+ trace("update rect is %ld,%ld - %ld,%ld\n",
+ rcu.left,rcu.top,rcu.right,rcu.bottom);
+ /* now with clipping region */
+ SelectClipRgn( hdc, clipping);
+ ScrollDC( hdc, -10, -5, &rc, &cliprc, hrgn, &rcu);
+ if (winetest_debug > 0) dump_region(hrgn);
+ /* create a region with what is expected */
+ SetRectRgn( exprgn, 39,10,49,74);
+ SetRectRgn( tmprgn, 80,79,90,85);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ SetRectRgn( tmprgn, 10,85,90,90);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ ok( EqualRgn( exprgn, hrgn), "wrong update region\n");
+ trace("update rect is %ld,%ld - %ld,%ld\n",
+ rcu.left,rcu.top,rcu.right,rcu.bottom);
+ ReleaseDC( hwnd1, hdc);
+
+ /* test scrolling a window with an update region */
+ DestroyWindow( hwnd2);
+ ValidateRect( hwnd1, NULL);
+ SetRect( &rc, 40,40, 50,50);
+ InvalidateRect( hwnd1, &rc, 1);
+ GetClientRect( hwnd1, &rc);
+ cliprc=rc;
+ ScrollWindowEx( hwnd1, -10, 0, &rc, &cliprc, hrgn, &rcu,
+ SW_SCROLLCHILDREN | SW_INVALIDATE);
+ if (winetest_debug > 0) dump_region(hrgn);
+ SetRectRgn( exprgn, 88,0,98,98);
+ SetRectRgn( tmprgn, 30, 40, 50, 50);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ ok( EqualRgn( exprgn, hrgn), "wrong update region\n");
+
+ /* now test ScrollWindowEx with a combination of
+ * WS_CLIPCHILDREN style and SW_SCROLLCHILDREN flag */
+ /* make hwnd2 the child of hwnd1 */
+ hwnd2 = CreateWindowExA(0, "static", NULL,
+ WS_CHILD| WS_VISIBLE | WS_BORDER ,
+ 50, 50, 100, 100, hwnd1, 0, 0, NULL);
+ SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) & ~WS_CLIPSIBLINGS);
+ GetClientRect( hwnd1, &rc);
+ cliprc=rc;
+
+ /* WS_CLIPCHILDREN and SW_SCROLLCHILDREN */
+ SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) | WS_CLIPCHILDREN );
+ ValidateRect( hwnd1, NULL);
+ ValidateRect( hwnd2, NULL);
+ ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu,
+ SW_SCROLLCHILDREN | SW_INVALIDATE);
+ if (winetest_debug > 0) dump_region(hrgn);
+ SetRectRgn( exprgn, 88,0,98,88);
+ SetRectRgn( tmprgn, 0,88,98,98);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ ok( EqualRgn( exprgn, hrgn), "wrong update region\n");
+
+ /* SW_SCROLLCHILDREN */
+ SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) & ~WS_CLIPCHILDREN );
+ ValidateRect( hwnd1, NULL);
+ ValidateRect( hwnd2, NULL);
+ ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu, SW_SCROLLCHILDREN | SW_INVALIDATE);
+ if (winetest_debug > 0) dump_region(hrgn);
+ /* expected region is the same as in previous test */
+ ok( EqualRgn( exprgn, hrgn), "wrong update region\n");
+
+ /* no SW_SCROLLCHILDREN */
+ SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) & ~WS_CLIPCHILDREN );
+ ValidateRect( hwnd1, NULL);
+ ValidateRect( hwnd2, NULL);
+ ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu, SW_INVALIDATE);
+ if (winetest_debug > 0) dump_region(hrgn);
+ /* expected region is the same as in previous test */
+ ok( EqualRgn( exprgn, hrgn), "wrong update region\n");
+
+ /* WS_CLIPCHILDREN and no SW_SCROLLCHILDREN */
+ SetWindowLong( hwnd1, GWL_STYLE, GetWindowLong( hwnd1, GWL_STYLE) | WS_CLIPCHILDREN );
+ ValidateRect( hwnd1, NULL);
+ ValidateRect( hwnd2, NULL);
+ ScrollWindowEx( hwnd1, -10, -10, &rc, &cliprc, hrgn, &rcu, SW_INVALIDATE);
+ if (winetest_debug > 0) dump_region(hrgn);
+ SetRectRgn( exprgn, 88,0,98,20);
+ SetRectRgn( tmprgn, 20,20,98,30);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ SetRectRgn( tmprgn, 20,30,30,88);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ SetRectRgn( tmprgn, 0,88,30,98);
+ CombineRgn( exprgn, exprgn, tmprgn, RGN_OR);
+ ok( EqualRgn( exprgn, hrgn), "wrong update region\n");
+
+ /* clean up */
+ DeleteObject( hrgn);
+ DeleteObject( exprgn);
+ DeleteObject( tmprgn);
+ DestroyWindow( hwnd1);
+ DestroyWindow( hwnd2);
+}
+
+/* couple of tests of return values of scrollbar functions
+ * called on a scrollbarless window */
+static void test_scroll(void)
+{
+ BOOL ret;
+ INT min, max;
+ SCROLLINFO si;
+ HWND hwnd = CreateWindowExA(0, "Static", "Wine test window",
+ WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP,
+ 100, 100, 200, 200, 0, 0, 0, NULL);
+ /* horizontal */
+ ret = GetScrollRange( hwnd, SB_HORZ, &min, &max);
+ ok( ret, "GetScrollRange returns FALSE\n");
+ ok( min == 0, "minimum scroll pos is %d (should be zero)\n", min);
+ ok( max == 0, "maximum scroll pos is %d (should be zero)\n", min);
+ si.cbSize = sizeof( si);
+ si.fMask = SIF_PAGE;
+ si.nPage = 0xdeadbeef;
+ ret = GetScrollInfo( hwnd, SB_HORZ, &si);
+ ok( !ret, "GetScrollInfo returns %d (should be zero)\n", ret);
+ ok( si.nPage == 0xdeadbeef, "unexpected value for nPage is %d\n", si.nPage);
+ /* vertical */
+ ret = GetScrollRange( hwnd, SB_VERT, &min, &max);
+ ok( ret, "GetScrollRange returns FALSE\n");
+ ok( min == 0, "minimum scroll pos is %d (should be zero)\n", min);
+ ok( max == 0, "maximum scroll pos is %d (should be zero)\n", min);
+ si.cbSize = sizeof( si);
+ si.fMask = SIF_PAGE;
+ si.nPage = 0xdeadbeef;
+ ret = GetScrollInfo( hwnd, SB_VERT, &si);
+ ok( !ret, "GetScrollInfo returns %d (should be zero)\n", ret);
+ ok( si.nPage == 0xdeadbeef, "unexpected value for nPage is %d\n", si.nPage);
+ /* clean up */
+ DestroyWindow( hwnd);
+}
+
+static void test_scrolldc( HWND parent)
+{
+ HDC hdc;
+ HRGN exprgn, tmprgn, hrgn;
+ RECT rc, rc2, rcu, cliprc;
+ HWND hwnd1;
+ COLORREF colr;
+
+ hrgn = CreateRectRgn(0,0,0,0);
+ tmprgn = CreateRectRgn(0,0,0,0);
+ exprgn = CreateRectRgn(0,0,0,0);
+
+ hwnd1 = CreateWindowExA(0, "static", NULL,
+ WS_CHILD| WS_VISIBLE,
+ 25, 50, 100, 100, parent, 0, 0, NULL);
+ ShowWindow( parent, SW_SHOW);
+ UpdateWindow( parent);
+ GetClientRect( hwnd1, &rc);
+ hdc = GetDC( hwnd1);
+ /* paint the upper half of the window black */
+ rc2 = rc;
+ rc2.bottom = ( rc.top + rc.bottom) /2;
+ FillRect( hdc, &rc2, GetStockObject(BLACK_BRUSH));
+ /* clip region is the lower half */
+ cliprc=rc;
+ cliprc.top = (rc.top + rc.bottom) /2;
+ /* test whether scrolled pixels are properly clipped */
+ colr = GetPixel( hdc, (rc.left+rc.right)/2, ( rc.top + rc.bottom) /2 - 1);
+ ok ( colr == 0, "pixel should be black, color is %08lx\n", colr);
+ /* this scroll should not cause any visible changes */
+ ScrollDC( hdc, 5, -20, &rc, &cliprc, hrgn, &rcu);
+ colr = GetPixel( hdc, (rc.left+rc.right)/2, ( rc.top + rc.bottom) /2 - 1);
+ ok ( colr == 0, "pixel should be black, color is %08lx\n", colr);
+ /* test with NULL clip rect */
+ ScrollDC( hdc, 20, -20, &rc, NULL, hrgn, &rcu);
+ /*FillRgn(hdc, hrgn, GetStockObject(WHITE_BRUSH));*/
+ trace("update rect: %ld,%ld - %ld,%ld\n",
+ rcu.left, rcu.top, rcu.right, rcu.bottom);
+ if (winetest_debug > 0) dump_region(hrgn);
+ SetRect(&rc2, 0, 0, 100, 100);
+ ok(EqualRect(&rcu, &rc2), "rects do not match (%ld,%ld-%ld,%ld) / (%ld,%ld-%ld,%ld)\n",
+ rcu.left, rcu.top, rcu.right, rcu.bottom, rc2.left, rc2.top, rc2.right, rc2.bottom);
+
+ SetRectRgn( exprgn, 0, 0, 20, 80);
+ SetRectRgn( tmprgn, 0, 80, 100, 100);
+ CombineRgn(exprgn, exprgn, tmprgn, RGN_OR);
+ if (winetest_debug > 0) dump_region(exprgn);
+ ok(EqualRgn(exprgn, hrgn), "wrong update region\n");
+ /* test clip rect > scroll rect */
+ FillRect( hdc, &rc, GetStockObject(WHITE_BRUSH));
+ rc2=rc;
+ InflateRect( &rc2, -(rc.right-rc.left)/4, -(rc.bottom-rc.top)/4);
+ FillRect( hdc, &rc2, GetStockObject(BLACK_BRUSH));
+ ScrollDC( hdc, 10, 10, &rc2, &rc, hrgn, &rcu);
+ SetRectRgn( exprgn, 25, 25, 75, 35);
+ SetRectRgn( tmprgn, 25, 35, 35, 75);
+ CombineRgn(exprgn, exprgn, tmprgn, RGN_OR);
+ ok(EqualRgn(exprgn, hrgn), "wrong update region\n");
+ colr = GetPixel( hdc, 80, 80);
+ ok ( colr == 0, "pixel should be black, color is %08lx\n", colr);
+ trace("update rect: %ld,%ld - %ld,%ld\n",
+ rcu.left, rcu.top, rcu.right, rcu.bottom);
+ if (winetest_debug > 0) dump_region(hrgn);
+
+ /* clean up */
+ DeleteObject(hrgn);
+ DeleteObject(exprgn);
+ DeleteObject(tmprgn);
+ DestroyWindow(hwnd1);
+}
+
+static void test_params(void)
+{
+ HWND hwnd;
+ INT rc;
+
+ /* Just a param check */
+ SetLastError(0xdeadbeef);
+ rc = GetWindowText(hwndMain2, NULL, 1024);
+ ok( rc==0, "GetWindowText: rc=%d err=%ld\n",rc,GetLastError());
+
+ SetLastError(0xdeadbeef);
+ hwnd=CreateWindow("LISTBOX", "TestList",
+ (LBS_STANDARD & ~LBS_SORT),
+ 0, 0, 100, 100,
+ NULL, (HMENU)1, NULL, 0);
+
+ ok(!hwnd, "CreateWindow with invalid menu handle should fail\n");
+ ok(GetLastError() == ERROR_INVALID_MENU_HANDLE || /* NT */
+ GetLastError() == 0xdeadbeef, /* Win9x */
+ "wrong last error value %ld\n", GetLastError());
+}
+
+static void test_AWRwindow(LPCSTR class, LONG style, LONG exStyle, BOOL menu)
+{
+ HWND hwnd = 0;
+
+ hwnd = CreateWindowEx(exStyle, class, class, style,
+ 110, 100,
+ 225, 200,
+ 0,
+ menu ? hmenu : 0,
+ 0, 0);
+ if (!hwnd) {
+ trace("Failed to create window class=%s, style=0x%08lx, exStyle=0x%08lx\n", class, style, exStyle);
+ return;
+ }
+ ShowWindow(hwnd, SW_SHOW);
+
+ test_nonclient_area(hwnd);
+
+ SetMenu(hwnd, 0);
+ DestroyWindow(hwnd);
+}
+
+static BOOL AWR_init(void)
+{
+ WNDCLASS class;
+
+ class.style = CS_HREDRAW | CS_VREDRAW;
+ class.lpfnWndProc = DefWindowProcA;
+ class.cbClsExtra = 0;
+ class.cbWndExtra = 0;
+ class.hInstance = 0;
+ class.hIcon = LoadIcon (0, IDI_APPLICATION);
+ class.hCursor = LoadCursor (0, IDC_ARROW);
+ class.hbrBackground = 0;
+ class.lpszMenuName = 0;
+ class.lpszClassName = szAWRClass;
+
+ if (!RegisterClass (&class)) {
+ ok(FALSE, "RegisterClass failed\n");
+ return FALSE;
+ }
+
+ hmenu = CreateMenu();
+ if (!hmenu)
+ return FALSE;
+ ok(hmenu != 0, "Failed to create menu\n");
+ ok(AppendMenu(hmenu, MF_STRING, 1, "Test!"), "Failed to create menu item\n");
+
+ return TRUE;
+}
+
+
+static void test_AWR_window_size(BOOL menu)
+{
+ LONG styles[] = {
+ WS_POPUP,
+ WS_MAXIMIZE, WS_BORDER, WS_DLGFRAME,
+ WS_SYSMENU,
+ WS_THICKFRAME,
+ WS_MINIMIZEBOX, WS_MAXIMIZEBOX,
+ WS_HSCROLL, WS_VSCROLL
+ };
+ LONG exStyles[] = {
+ WS_EX_CLIENTEDGE,
+ WS_EX_TOOLWINDOW, WS_EX_WINDOWEDGE,
+ WS_EX_APPWINDOW,
+#if 0
+ /* These styles have problems on (at least) WinXP (SP2) and Wine */
+ WS_EX_DLGMODALFRAME,
+ WS_EX_STATICEDGE,
+#endif
+ };
+
+ int i;
+
+ /* A exhaustive check of all the styles takes too long
+ * so just do a (hopefully representative) sample
+ */
+ for (i = 0; i < COUNTOF(styles); ++i)
+ test_AWRwindow(szAWRClass, styles[i], 0, menu);
+ for (i = 0; i < COUNTOF(exStyles); ++i) {
+ test_AWRwindow(szAWRClass, WS_POPUP, exStyles[i], menu);
+ test_AWRwindow(szAWRClass, WS_THICKFRAME, exStyles[i], menu);
+ }
+}
+#undef COUNTOF
+
+#define SHOWSYSMETRIC(SM) trace(#SM "=%d\n", GetSystemMetrics(SM))
+
+static void test_AdjustWindowRect(void)
+{
+ if (!AWR_init())
+ return;
+
+ SHOWSYSMETRIC(SM_CYCAPTION);
+ SHOWSYSMETRIC(SM_CYSMCAPTION);
+ SHOWSYSMETRIC(SM_CYMENU);
+ SHOWSYSMETRIC(SM_CXEDGE);
+ SHOWSYSMETRIC(SM_CYEDGE);
+ SHOWSYSMETRIC(SM_CXVSCROLL);
+ SHOWSYSMETRIC(SM_CYHSCROLL);
+ SHOWSYSMETRIC(SM_CXFRAME);
+ SHOWSYSMETRIC(SM_CYFRAME);
+ SHOWSYSMETRIC(SM_CXDLGFRAME);
+ SHOWSYSMETRIC(SM_CYDLGFRAME);
+ SHOWSYSMETRIC(SM_CXBORDER);
+ SHOWSYSMETRIC(SM_CYBORDER);
+
+ test_AWR_window_size(FALSE);
+ test_AWR_window_size(TRUE);
+
+ DestroyMenu(hmenu);
+}
+#undef SHOWSYSMETRIC
+
+
+/* Global variables to trigger exit from loop */
+static int redrawComplete, WMPAINT_count;
+
+static LRESULT WINAPI redraw_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ switch (msg)
+ {
+ case WM_PAINT:
+ trace("doing WM_PAINT %d\n", WMPAINT_count);
+ WMPAINT_count++;
+ if (WMPAINT_count > 10 && redrawComplete == 0) {
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd, &ps);
+ EndPaint(hwnd, &ps);
+ return 1;
+ }
+ return 0;
+ break;
+ }
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+/* Ensure we exit from RedrawNow regardless of invalidated area */
+static void test_redrawnow(void)
+{
+ WNDCLASSA cls;
+ HWND hwndMain;
+
+ cls.style = CS_DBLCLKS;
+ cls.lpfnWndProc = redraw_window_procA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "RedrawWindowClass";
+
+ if(!RegisterClassA(&cls)) {
+ trace("Register failed %ld\n", GetLastError());
+ return;
+ }
+
+ hwndMain = CreateWindowA("RedrawWindowClass", "Main Window", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, 100, 100, NULL, NULL, 0, NULL);
+
+ ok( WMPAINT_count == 0, "Multiple unexpected WM_PAINT calls %d\n", WMPAINT_count);
+ ShowWindow(hwndMain, SW_SHOW);
+ ok( WMPAINT_count == 0, "Multiple unexpected WM_PAINT calls %d\n", WMPAINT_count);
+ RedrawWindow(hwndMain, NULL,NULL,RDW_UPDATENOW | RDW_ALLCHILDREN);
+ ok( WMPAINT_count == 1, "Multiple unexpected WM_PAINT calls %d\n", WMPAINT_count);
+ redrawComplete = TRUE;
+ ok( WMPAINT_count < 10, "RedrawWindow (RDW_UPDATENOW) never completed (%d)\n", WMPAINT_count);
+
+ /* clean up */
+ DestroyWindow( hwndMain);
+}
+
+struct parentdc_stat {
+ RECT client;
+ RECT clip;
+ RECT paint;
+};
+
+struct parentdc_test {
+ struct parentdc_stat main, main_todo;
+ struct parentdc_stat child1, child1_todo;
+ struct parentdc_stat child2, child2_todo;
+};
+
+static LRESULT WINAPI parentdc_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ RECT rc;
+ PAINTSTRUCT ps;
+
+ struct parentdc_stat *t = (struct parentdc_stat *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_PAINT:
+ trace("doing WM_PAINT on %p\n", hwnd);
+ GetClientRect(hwnd, &rc);
+ CopyRect(&t->client, &rc);
+ trace("client rect (%ld, %ld)-(%ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ GetWindowRect(hwnd, &rc);
+ trace("window rect (%ld, %ld)-(%ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ BeginPaint(hwnd, &ps);
+ CopyRect(&t->paint, &ps.rcPaint);
+ GetClipBox(ps.hdc, &rc);
+ CopyRect(&t->clip, &rc);
+ trace("clip rect (%ld, %ld)-(%ld, %ld)\n", rc.left, rc.top, rc.right, rc.bottom);
+ trace("paint rect (%ld, %ld)-(%ld, %ld)\n", ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom);
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+ return DefWindowProc(hwnd, msg, wparam, lparam);
+}
+
+static void zero_parentdc_stat(struct parentdc_stat *t)
+{
+ SetRectEmpty(&t->client);
+ SetRectEmpty(&t->clip);
+ SetRectEmpty(&t->paint);
+}
+
+static void zero_parentdc_test(struct parentdc_test *t)
+{
+ zero_parentdc_stat(&t->main);
+ zero_parentdc_stat(&t->child1);
+ zero_parentdc_stat(&t->child2);
+}
+
+#define parentdc_field_ok(t, w, r, f, got) \
+ ok (t.w.r.f==got.w.r.f, "window " #w ", rect " #r ", field " #f \
+ ": expected %ld, got %ld\n", \
+ t.w.r.f, got.w.r.f)
+
+#define parentdc_todo_field_ok(t, w, r, f, got) \
+ if (t.w##_todo.r.f) todo_wine { parentdc_field_ok(t, w, r, f, got); } \
+ else parentdc_field_ok(t, w, r, f, got)
+
+#define parentdc_rect_ok(t, w, r, got) \
+ parentdc_todo_field_ok(t, w, r, left, got); \
+ parentdc_todo_field_ok(t, w, r, top, got); \
+ parentdc_todo_field_ok(t, w, r, right, got); \
+ parentdc_todo_field_ok(t, w, r, bottom, got);
+
+#define parentdc_win_ok(t, w, got) \
+ parentdc_rect_ok(t, w, client, got); \
+ parentdc_rect_ok(t, w, clip, got); \
+ parentdc_rect_ok(t, w, paint, got);
+
+#define parentdc_ok(t, got) \
+ parentdc_win_ok(t, main, got); \
+ parentdc_win_ok(t, child1, got); \
+ parentdc_win_ok(t, child2, got);
+
+static void test_csparentdc(void)
+{
+ WNDCLASSA clsMain, cls;
+ HWND hwndMain, hwnd1, hwnd2;
+ MSG msg;
+ RECT rc;
+
+ struct parentdc_test test_answer;
+
+#define nothing_todo {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}
+ const struct parentdc_test test1 =
+ {
+ {{0, 0, 150, 150}, {0, 0, 150, 150}, {0, 0, 150, 150}}, nothing_todo,
+ {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}},
+ {{0, 0, 40, 40}, {-40, -40, 110, 110}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}},
+ };
+
+ const struct parentdc_test test2 =
+ {
+ {{0, 0, 150, 150}, {0, 0, 50, 50}, {0, 0, 50, 50}}, nothing_todo,
+ {{0, 0, 40, 40}, {-20, -20, 30, 30}, {0, 0, 30, 30}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}},
+ {{0, 0, 40, 40}, {-40, -40, 10, 10}, {0, 0, 10, 10}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}},
+ };
+
+ const struct parentdc_test test3 =
+ {
+ {{0, 0, 150, 150}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo,
+ };
+
+ const struct parentdc_test test4 =
+ {
+ {{0, 0, 150, 150}, {40, 40, 50, 50}, {40, 40, 50, 50}}, nothing_todo,
+ {{0, 0, 40, 40}, {20, 20, 30, 30}, {20, 20, 30, 30}}, nothing_todo,
+ {{0, 0, 40, 40}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo,
+ };
+
+ const struct parentdc_test test5 =
+ {
+ {{0, 0, 150, 150}, {20, 20, 60, 60}, {20, 20, 60, 60}}, nothing_todo,
+ {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}},
+ {{0, 0, 40, 40}, {-20, -20, 20, 20}, {0, 0, 20, 20}}, {{0, 0, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 0}},
+ };
+
+ const struct parentdc_test test6 =
+ {
+ {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo,
+ {{0, 0, 40, 40}, {0, 0, 10, 10}, {0, 0, 10, 10}}, nothing_todo,
+ {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo,
+ };
+
+ const struct parentdc_test test7 =
+ {
+ {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo,
+ {{0, 0, 40, 40}, {-20, -20, 130, 130}, {0, 0, 40, 40}}, {{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}},
+ {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, nothing_todo,
+ };
+#undef nothing_todo
+
+ clsMain.style = CS_DBLCLKS;
+ clsMain.lpfnWndProc = parentdc_window_procA;
+ clsMain.cbClsExtra = 0;
+ clsMain.cbWndExtra = 0;
+ clsMain.hInstance = GetModuleHandleA(0);
+ clsMain.hIcon = 0;
+ clsMain.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ clsMain.hbrBackground = GetStockObject(WHITE_BRUSH);
+ clsMain.lpszMenuName = NULL;
+ clsMain.lpszClassName = "ParentDcMainWindowClass";
+
+ if(!RegisterClassA(&clsMain)) {
+ trace("Register failed %ld\n", GetLastError());
+ return;
+ }
+
+ cls.style = CS_DBLCLKS | CS_PARENTDC;
+ cls.lpfnWndProc = parentdc_window_procA;
+ cls.cbClsExtra = 0;
+ cls.cbWndExtra = 0;
+ cls.hInstance = GetModuleHandleA(0);
+ cls.hIcon = 0;
+ cls.hCursor = LoadCursorA(0, (LPSTR)IDC_ARROW);
+ cls.hbrBackground = GetStockObject(WHITE_BRUSH);
+ cls.lpszMenuName = NULL;
+ cls.lpszClassName = "ParentDcWindowClass";
+
+ if(!RegisterClassA(&cls)) {
+ trace("Register failed %ld\n", GetLastError());
+ return;
+ }
+
+ SetRect(&rc, 0, 0, 150, 150);
+ AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0);
+ hwndMain = CreateWindowA("ParentDcMainWindowClass", "Main Window", WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, 0, NULL);
+ SetWindowLongPtrA(hwndMain, GWLP_USERDATA, (DWORD_PTR)&test_answer.main);
+ hwnd1 = CreateWindowA("ParentDcWindowClass", "Child Window 1", WS_CHILD,
+ 20, 20, 40, 40, hwndMain, NULL, 0, NULL);
+ SetWindowLongPtrA(hwnd1, GWLP_USERDATA, (DWORD_PTR)&test_answer.child1);
+ hwnd2 = CreateWindowA("ParentDcWindowClass", "Child Window 2", WS_CHILD,
+ 40, 40, 40, 40, hwndMain, NULL, 0, NULL);
+ SetWindowLongPtrA(hwnd2, GWLP_USERDATA, (DWORD_PTR)&test_answer.child2);
+ ShowWindow(hwndMain, SW_SHOW);
+ ShowWindow(hwnd1, SW_SHOW);
+ ShowWindow(hwnd2, SW_SHOW);
+ flush_events();
+
+ zero_parentdc_test(&test_answer);
+ InvalidateRect(hwndMain, NULL, TRUE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ parentdc_ok(test1, test_answer);
+
+ zero_parentdc_test(&test_answer);
+ SetRect(&rc, 0, 0, 50, 50);
+ InvalidateRect(hwndMain, &rc, TRUE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ parentdc_ok(test2, test_answer);
+
+ zero_parentdc_test(&test_answer);
+ SetRect(&rc, 0, 0, 10, 10);
+ InvalidateRect(hwndMain, &rc, TRUE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ parentdc_ok(test3, test_answer);
+
+ zero_parentdc_test(&test_answer);
+ SetRect(&rc, 40, 40, 50, 50);
+ InvalidateRect(hwndMain, &rc, TRUE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ parentdc_ok(test4, test_answer);
+
+ zero_parentdc_test(&test_answer);
+ SetRect(&rc, 20, 20, 60, 60);
+ InvalidateRect(hwndMain, &rc, TRUE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ parentdc_ok(test5, test_answer);
+
+ zero_parentdc_test(&test_answer);
+ SetRect(&rc, 0, 0, 10, 10);
+ InvalidateRect(hwnd1, &rc, TRUE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ parentdc_ok(test6, test_answer);
+
+ zero_parentdc_test(&test_answer);
+ SetRect(&rc, -5, -5, 65, 65);
+ InvalidateRect(hwnd1, &rc, TRUE);
+ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
+ parentdc_ok(test7, test_answer);
+
+ DestroyWindow(hwndMain);
+ DestroyWindow(hwnd1);
+ DestroyWindow(hwnd2);
+}
+
+static void test_IsWindowUnicode(void)
+{
+ static const char ansi_class_nameA[] = "ansi class name";
+ static const WCHAR ansi_class_nameW[] = {'a','n','s','i',' ','c','l','a','s','s',' ','n','a','m','e',0};
+ static const char unicode_class_nameA[] = "unicode class name";
+ static const WCHAR unicode_class_nameW[] = {'u','n','i','c','o','d','e',' ','c','l','a','s','s',' ','n','a','m','e',0};
+ WNDCLASSA classA;
+ WNDCLASSW classW;
+ HWND hwnd;
+
+ memset(&classW, 0, sizeof(classW));
+ classW.hInstance = GetModuleHandleA(0);
+ classW.lpfnWndProc = DefWindowProcW;
+ classW.lpszClassName = unicode_class_nameW;
+ if (!RegisterClassW(&classW)) return;
+
+ memset(&classA, 0, sizeof(classA));
+ classA.hInstance = GetModuleHandleA(0);
+ classA.lpfnWndProc = DefWindowProcA;
+ classA.lpszClassName = ansi_class_nameA;
+ assert(RegisterClassA(&classA));
+
+ /* unicode class: window proc */
+ hwnd = CreateWindowExW(0, unicode_class_nameW, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+ SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA);
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+ SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW);
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+
+ DestroyWindow(hwnd);
+
+ hwnd = CreateWindowExA(0, unicode_class_nameA, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+ SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA);
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+ SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW);
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+
+ DestroyWindow(hwnd);
+
+ /* ansi class: window proc */
+ hwnd = CreateWindowExW(0, ansi_class_nameW, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+ SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW);
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+ SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA);
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+
+ DestroyWindow(hwnd);
+
+ hwnd = CreateWindowExA(0, ansi_class_nameA, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+ SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcW);
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+ SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (ULONG_PTR)DefWindowProcA);
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+
+ DestroyWindow(hwnd);
+
+ /* unicode class: class proc */
+ hwnd = CreateWindowExW(0, unicode_class_nameW, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+ SetClassLongPtrA(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcA);
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+ /* do not restore class window proc back to unicode */
+
+ DestroyWindow(hwnd);
+
+ hwnd = CreateWindowExA(0, unicode_class_nameA, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+ SetClassLongPtrW(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcW);
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+
+ DestroyWindow(hwnd);
+
+ /* ansi class: class proc */
+ hwnd = CreateWindowExW(0, ansi_class_nameW, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+ SetClassLongPtrW(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcW);
+ ok(!IsWindowUnicode(hwnd), "IsWindowUnicode expected to return FALSE\n");
+ /* do not restore class window proc back to ansi */
+
+ DestroyWindow(hwnd);
+
+ hwnd = CreateWindowExA(0, ansi_class_nameA, NULL, WS_POPUP,
+ 0, 0, 100, 100, GetDesktopWindow(), 0, 0, NULL);
+ assert(hwnd);
+
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+ SetClassLongPtrA(hwnd, GCLP_WNDPROC, (ULONG_PTR)DefWindowProcA);
+ ok(IsWindowUnicode(hwnd), "IsWindowUnicode expected to return TRUE\n");
+
+ DestroyWindow(hwnd);
+}
+
+START_TEST(win)
+{
+ pGetAncestor = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetAncestor" );
+ pGetWindowInfo = (void *)GetProcAddress( GetModuleHandleA("user32.dll"), "GetWindowInfo" );
+
+ hwndMain = CreateWindowExA(0, "static", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, NULL);
+ if (hwndMain)
+ {
+ ok(!GetParent(hwndMain), "GetParent should return 0 for message only windows\n");
+ if (pGetAncestor)
+ {
+ hwndMessage = pGetAncestor(hwndMain, GA_PARENT);
+ ok(hwndMessage != 0, "GetAncestor(GA_PARENT) should not return 0 for message only windows\n");
+ trace("hwndMessage %p\n", hwndMessage);
+ }
+ DestroyWindow(hwndMain);
+ }
+ else
+ trace("CreateWindowExA with parent HWND_MESSAGE failed\n");
+
+ if (!RegisterWindowClasses()) assert(0);
+
+ hhook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId());
+ assert(hhook);
+
+ hwndMain = CreateWindowExA(/*WS_EX_TOOLWINDOW*/ 0, "MainWindowClass", "Main window",
+ WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+ WS_MAXIMIZEBOX | WS_POPUP,
+ 100, 100, 200, 200,
+ 0, 0, 0, NULL);
+ test_nonclient_area(hwndMain);
+
+ hwndMain2 = CreateWindowExA(/*WS_EX_TOOLWINDOW*/ 0, "MainWindowClass", "Main window 2",
+ WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+ WS_MAXIMIZEBOX | WS_POPUP,
+ 100, 100, 200, 200,
+ 0, 0, 0, NULL);
+ assert( hwndMain );
+ assert( hwndMain2 );
+
+ /* Add the tests below this line */
+ test_params();
+
+ test_capture_1();
+ test_capture_2();
+ test_capture_3(hwndMain, hwndMain2);
+
+ test_parent_owner();
+ test_SetParent();
+ test_shell_window();
+
+ test_mdi();
+ test_icons();
+ test_SetWindowPos(hwndMain);
+ test_SetMenu(hwndMain);
+ test_SetFocus(hwndMain);
+ test_SetActiveWindow(hwndMain);
+
+ test_children_zorder(hwndMain);
+ test_keyboard_input(hwndMain);
+ test_mouse_input(hwndMain);
+ test_validatergn(hwndMain);
+ test_nccalcscroll( hwndMain);
+ test_scrollvalidate( hwndMain);
+ test_scrolldc( hwndMain);
+ test_scroll();
+ test_IsWindowUnicode();
+ test_vis_rgn(hwndMain);
+
+ test_AdjustWindowRect();
+ test_window_styles();
+ test_redrawnow();
+ test_csparentdc();
+
+ /* add the tests above this line */
+ UnhookWindowsHookEx(hhook);
+}
--- /dev/null
+/*
+ * Unit tests for window stations and desktops
+ *
+ * Copyright 2002 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+
+#define DESKTOP_ALL_ACCESS 0x01ff
+
+static void print_object( HANDLE obj )
+{
+ char buffer[100];
+ DWORD size;
+
+ strcpy( buffer, "foobar" );
+ if (!GetUserObjectInformationA( obj, UOI_NAME, buffer, sizeof(buffer), &size ))
+ trace( "could not get info for %p\n", obj );
+ else
+ trace( "obj %p name '%s'\n", obj, buffer );
+ strcpy( buffer, "foobar" );
+ if (!GetUserObjectInformationA( obj, UOI_TYPE, buffer, sizeof(buffer), &size ))
+ trace( "could not get type for %p\n", obj );
+ else
+ trace( "obj %p type '%s'\n", obj, buffer );
+}
+
+static HDESK initial_desktop;
+
+static DWORD CALLBACK thread( LPVOID arg )
+{
+ HDESK d1, d2;
+ HWND hwnd = CreateWindowExA(0,"BUTTON","test",WS_POPUP,0,0,100,100,GetDesktopWindow(),0,0,0);
+ ok( hwnd != 0, "CreateWindow failed\n" );
+ d1 = GetThreadDesktop(GetCurrentThreadId());
+ trace( "thread %p desktop: %p\n", arg, d1 );
+ ok( d1 == initial_desktop, "thread %p doesn't use initial desktop\n", arg );
+
+ SetLastError( 0xdeadbeef );
+ ok( !CloseHandle( d1 ), "CloseHandle succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() );
+ SetLastError( 0xdeadbeef );
+ ok( !CloseDesktop( d1 ), "CloseDesktop succeeded\n" );
+ ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() );
+ print_object( d1 );
+ d2 = CreateDesktop( "foobar2", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
+ trace( "created desktop %p\n", d2 );
+ ok( d2 != 0, "CreateDesktop failed\n" );
+
+ SetLastError( 0xdeadbeef );
+ ok( !SetThreadDesktop( d2 ), "set thread desktop succeeded with existing window\n" );
+ ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() );
+
+ DestroyWindow( hwnd );
+ ok( SetThreadDesktop( d2 ), "set thread desktop failed\n" );
+ d1 = GetThreadDesktop(GetCurrentThreadId());
+ ok( d1 == d2, "GetThreadDesktop did not return set desktop %p/%p\n", d1, d2 );
+ print_object( d2 );
+ if (arg < (LPVOID)5)
+ {
+ HANDLE hthread = CreateThread( NULL, 0, thread, (char *)arg + 1, 0, NULL );
+ Sleep(1000);
+ WaitForSingleObject( hthread, INFINITE );
+ CloseHandle( hthread );
+ }
+ return 0;
+}
+
+static void test_handles(void)
+{
+ HWINSTA w1, w2, w3;
+ HDESK d1, d2, d3;
+ HANDLE hthread;
+ DWORD id, flags;
+
+ /* win stations */
+
+ w1 = GetProcessWindowStation();
+ ok( GetProcessWindowStation() == w1, "GetProcessWindowStation returned different handles\n" );
+ ok( !CloseWindowStation(w1), "closing process win station succeeded\n" );
+ SetLastError( 0xdeadbeef );
+ ok( !CloseHandle(w1), "closing process win station handle succeeded\n" );
+ ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() );
+ print_object( w1 );
+
+ flags = 0;
+ ok( GetHandleInformation( w1, &flags ), "GetHandleInformation failed\n" );
+ ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), "handle %p PROTECT_FROM_CLOSE set\n", w1 );
+
+ ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0,
+ TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
+ ok( CloseWindowStation(w2), "closing dup win station failed\n" );
+
+ ok( DuplicateHandle( GetCurrentProcess(), w1, GetCurrentProcess(), (PHANDLE)&w2, 0,
+ TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
+ ok( CloseHandle(w2), "closing dup win station handle failed\n" );
+
+ w2 = CreateWindowStation("WinSta0", 0, WINSTA_ALL_ACCESS, NULL );
+ ok( w2 != 0, "CreateWindowStation failed\n" );
+ ok( w2 != w1, "CreateWindowStation returned default handle\n" );
+ SetLastError( 0xdeadbeef );
+ ok( !CloseDesktop( (HDESK)w2 ), "CloseDesktop succeeded on win station\n" );
+ ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() );
+ ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" );
+
+ w2 = CreateWindowStation("WinSta0", 0, WINSTA_ALL_ACCESS, NULL );
+ ok( CloseHandle( w2 ), "CloseHandle failed\n" );
+
+ w2 = OpenWindowStation("winsta0", TRUE, WINSTA_ALL_ACCESS );
+ ok( w2 != 0, "OpenWindowStation failed\n" );
+ ok( w2 != w1, "OpenWindowStation returned default handle\n" );
+ ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" );
+
+ w2 = OpenWindowStation("dummy name", TRUE, WINSTA_ALL_ACCESS );
+ ok( !w2, "open dummy win station succeeded\n" );
+
+ CreateMutexA( NULL, 0, "foobar" );
+ w2 = CreateWindowStation("foobar", 0, WINSTA_ALL_ACCESS, NULL );
+ ok( w2 != 0, "create foobar station failed\n" );
+
+ w3 = OpenWindowStation("foobar", TRUE, WINSTA_ALL_ACCESS );
+ ok( w3 != 0, "open foobar station failed\n" );
+ ok( w3 != w2, "open foobar station returned same handle\n" );
+ ok( CloseWindowStation( w2 ), "CloseWindowStation failed\n" );
+ ok( CloseWindowStation( w3 ), "CloseWindowStation failed\n" );
+
+ w3 = OpenWindowStation("foobar", TRUE, WINSTA_ALL_ACCESS );
+ ok( !w3, "open foobar station succeeded\n" );
+
+ /* desktops */
+ d1 = GetThreadDesktop(GetCurrentThreadId());
+ initial_desktop = d1;
+ ok( GetThreadDesktop(GetCurrentThreadId()) == d1,
+ "GetThreadDesktop returned different handles\n" );
+
+ flags = 0;
+ ok( GetHandleInformation( d1, &flags ), "GetHandleInformation failed\n" );
+ ok( !(flags & HANDLE_FLAG_PROTECT_FROM_CLOSE), "handle %p PROTECT_FROM_CLOSE set\n", d1 );
+
+ SetLastError( 0xdeadbeef );
+ ok( !CloseDesktop(d1), "closing thread desktop succeeded\n" );
+ ok( GetLastError() == ERROR_BUSY, "bad last error %ld\n", GetLastError() );
+
+ SetLastError( 0xdeadbeef );
+ ok( !CloseHandle(d1), "closing thread desktop handle failed\n" );
+ ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() );
+
+ ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0,
+ TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
+ ok( CloseDesktop(d2), "closing dup desktop failed\n" );
+
+ ok( DuplicateHandle( GetCurrentProcess(), d1, GetCurrentProcess(), (PHANDLE)&d2, 0,
+ TRUE, DUPLICATE_SAME_ACCESS ), "DuplicateHandle failed\n" );
+ ok( CloseHandle(d2), "closing dup desktop handle failed\n" );
+
+ d2 = OpenDesktop( "dummy name", 0, TRUE, DESKTOP_ALL_ACCESS );
+ ok( !d2, "open dummy desktop succeeded\n" );
+
+ d2 = CreateDesktop( "foobar", NULL, NULL, 0, DESKTOP_ALL_ACCESS, NULL );
+ ok( d2 != 0, "create foobar desktop failed\n" );
+ SetLastError( 0xdeadbeef );
+ ok( !CloseWindowStation( (HWINSTA)d2 ), "CloseWindowStation succeeded on desktop\n" );
+ ok( GetLastError() == ERROR_INVALID_HANDLE, "bad last error %ld\n", GetLastError() );
+
+ d3 = OpenDesktop( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS );
+ ok( d3 != 0, "open foobar desktop failed\n" );
+ ok( d3 != d2, "open foobar desktop returned same handle\n" );
+ ok( CloseDesktop( d2 ), "CloseDesktop failed\n" );
+ ok( CloseDesktop( d3 ), "CloseDesktop failed\n" );
+
+ d3 = OpenDesktop( "foobar", 0, TRUE, DESKTOP_ALL_ACCESS );
+ ok( !d3, "open foobar desktop succeeded\n" );
+
+ ok( !CloseHandle(d1), "closing thread desktop handle succeeded\n" );
+ d2 = GetThreadDesktop(GetCurrentThreadId());
+ ok( d1 == d2, "got different handles after close\n" );
+
+ trace( "thread 1 desktop: %p\n", d1 );
+ print_object( d1 );
+ hthread = CreateThread( NULL, 0, thread, (LPVOID)2, 0, &id );
+ Sleep(1000);
+ trace( "get other thread desktop: %p\n", GetThreadDesktop(id) );
+ WaitForSingleObject( hthread, INFINITE );
+ CloseHandle( hthread );
+}
+
+START_TEST(winstation)
+{
+ /* Check whether this platform supports WindowStation calls */
+
+ SetLastError( 0xdeadbeef );
+ GetProcessWindowStation();
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ {
+ trace("WindowStation calls not supported on this platform\n");
+ return;
+ }
+
+ test_handles();
+}
--- /dev/null
+ /* Unit test suite for the wsprintf functions
+ *
+ * Copyright 2002 Bill Medland
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windows.h"
+
+static void wsprintfATest(void)
+{
+ char buf[25];
+ int rc;
+
+ rc=wsprintfA(buf, "%010ld", -1);
+ ok(rc == 10, "wsPrintfA length failure: rc=%d error=%ld\n",rc,GetLastError());
+ ok((lstrcmpA(buf, "-000000001") == 0),
+ "wsprintfA zero padded negative value failure: buf=[%s]\n",buf);
+}
+
+static void wsprintfWTest(void)
+{
+ static const WCHAR fmt[] = {'%','0','1','0','l','d','\0'};
+ static const WCHAR target[] = {'-','0','0','0','0','0','0','0','0','1', '\0'};
+ WCHAR buf[25];
+ int rc;
+
+ rc=wsprintfW(buf, fmt, -1);
+ if (rc==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+ return;
+ ok(rc == 10, "wsPrintfW length failure: rc=%d error=%ld\n",rc,GetLastError());
+ ok((lstrcmpW(buf, target) == 0),
+ "wsprintfW zero padded negative value failure\n");
+}
+
+START_TEST(wsprintf)
+{
+ wsprintfATest();
+ wsprintfWTest();
+}
--- /dev/null
+#define WIN32_LEAN_AND_MEAN\r
+#include <windows.h>\r
+\r
+#define STANDALONE\r
+#include "wine/test.h"\r
+\r
+extern void func_usp10(void);\r
+\r
+\r
+const struct test winetest_testlist[] =\r
+{\r
+ { "usp10", func_usp10 },\r
+ { 0, 0 }\r
+};
\ No newline at end of file
--- /dev/null
+/*\r
+ * Tests for usp10 dll\r
+ *\r
+ * Copyright 2006 Jeff Latimer\r
+ * Copyright 2006 Hans Leidekker\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA\r
+ *\r
+ * Notes:\r
+ * Uniscribe allows for processing of complex scripts such as joining\r
+ * and filtering characters and bi-directional text with custom line breaks.\r
+ */\r
+\r
+//#include <assert.h>\r
+//#include <stdio.h>\r
+//#include <windows.h>\r
+//#include <wine/test.h>\r
+//#include <winbase.h>\r
+//#include <wingdi.h>\r
+//#include <winuser.h>\r
+//#include <winerror.h>\r
+//#include <winnls.h>\r
+//#include <usp10.h>\r
+\r
+#include <stdio.h>\r
+#include <windows.h>\r
+#include "usp10.h"\r
+#include "wine/test.h"\r
+#include "wine/windef.h"\r
+\r
+BOOL WINAPI ShowWindow(HWND,int);\r
+\r
+static void test_ScriptItemIzeShapePlace(HDC hdc, unsigned short pwOutGlyphs[256])\r
+{\r
+ HRESULT hr;\r
+ int iMaxProps;\r
+ const SCRIPT_PROPERTIES **ppSp;\r
+\r
+ int cInChars;\r
+ int cMaxItems;\r
+ SCRIPT_ITEM pItem[255];\r
+ int pcItems;\r
+ WCHAR TestItem1[] = {'T', 'e', 's', 't', 'a', 0}; \r
+ WCHAR TestItem2[] = {'T', 'e', 's', 't', 'b', 0}; \r
+ WCHAR TestItem3[] = {'T', 'e', 's', 't', 'c',' ','1','2','3',' ',' ','e','n','d',0}; \r
+ WCHAR TestItem4[] = {'T', 'e', 's', 't', 'c',' ',0x0684,0x0694,0x06a4,' ',' ','e','n','d',0};\r
+ WCHAR TestItem5[] = {0x0684,'T','e','s','t','c',' ',0x0684,0x0694,0x06a4,' ',' ','e','n','d',0}; \r
+\r
+ SCRIPT_CACHE psc;\r
+ int cChars;\r
+ int cMaxGlyphs;\r
+ unsigned short pwOutGlyphs1[256];\r
+ unsigned short pwOutGlyphs2[256];\r
+ unsigned short pwLogClust[256];\r
+ SCRIPT_VISATTR psva[256];\r
+ int pcGlyphs;\r
+ int piAdvance[256];\r
+ GOFFSET pGoffset[256];\r
+ ABC pABC[256];\r
+ int cnt;\r
+\r
+ /* Start testing usp10 functions */\r
+ /* This test determines that the pointer returned by ScriptGetProperties is valid\r
+ * by checking a known value in the table */\r
+ hr = ScriptGetProperties(&ppSp, &iMaxProps);\r
+ trace("number of script properties %d\n", iMaxProps);\r
+ ok (iMaxProps > 0, "Number of scripts returned should not be 0\n"); \r
+ if (iMaxProps > 0)\r
+ ok( ppSp[5]->langid == 9, "Langid[5] not = to 9\n"); /* Check a known value to ensure */\r
+ /* ptrs work */\r
+\r
+\r
+ /* This set of tests are to check that the various edits in ScriptIemize work */\r
+ cInChars = 5; /* Length of test without NULL */\r
+ cMaxItems = 1; /* Check threshold value */\r
+ hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if cMaxItems < 2. Was %d\n",\r
+ cMaxItems);\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(NULL, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if pwcInChars is NULL\n");\r
+\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(TestItem1, 0, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if cInChars is 0\n");\r
+\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, NULL, &pcItems);\r
+ ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if pItems is NULL\n");\r
+\r
+ /* This is a valid test that will cause parsing to take place */\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);\r
+ /* This test is for the interim operation of ScriptItemize where only one SCRIPT_ITEM is *\r
+ * returned. */\r
+ ok (pcItems > 0, "The number of SCRIPT_ITEMS should be greater than 0\n");\r
+ if (pcItems > 0)\r
+ ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == cInChars,\r
+ "Start pos not = 0 (%d) or end pos not = %d (%d)\n",\r
+ pItem[0].iCharPos, cInChars, pItem[1].iCharPos);\r
+\r
+ /* It would appear that we have a valid SCRIPT_ANALYSIS and can continue\r
+ * ie. ScriptItemize has succeeded and that pItem has been set */\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ if (hr == 0) {\r
+ psc = NULL; /* must be null on first call */\r
+ cChars = cInChars;\r
+ cMaxGlyphs = cInChars;\r
+ hr = ScriptShape(NULL, &psc, TestItem1, cChars,\r
+ cMaxGlyphs, &pItem[0].a,\r
+ pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);\r
+ ok (hr == E_PENDING, "If psc is NULL (%08x) the E_PENDING should be returned\n", hr);\r
+ cMaxGlyphs = 4;\r
+ hr = ScriptShape(hdc, &psc, TestItem1, cChars,\r
+ cMaxGlyphs, &pItem[0].a,\r
+ pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);\r
+ ok (hr == E_OUTOFMEMORY, "If not enough output area cChars (%d) is > than CMaxGlyphs "\r
+ "(%d) but not E_OUTOFMEMORY\n",\r
+ cChars, cMaxGlyphs);\r
+ cMaxGlyphs = 256;\r
+ hr = ScriptShape(hdc, &psc, TestItem1, cChars,\r
+ cMaxGlyphs, &pItem[0].a,\r
+ pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);\r
+ ok (hr == 0, "ScriptShape should return 0 not (%08x)\n", hr);\r
+ ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");\r
+ ok (pcGlyphs == cChars, "Chars in (%d) should equal Glyphs out (%d)\n", cChars, pcGlyphs);\r
+ if (hr ==0) {\r
+ hr = ScriptPlace(hdc, &psc, pwOutGlyphs1, pcGlyphs, psva, &pItem[0].a, piAdvance,\r
+ pGoffset, pABC);\r
+ ok (hr == 0, "ScriptPlace should return 0 not (%08x)\n", hr);\r
+ hr = ScriptPlace(NULL, &psc, pwOutGlyphs1, pcGlyphs, psva, &pItem[0].a, piAdvance,\r
+ pGoffset, pABC);\r
+ ok (hr == 0, "ScriptPlace should return 0 not (%08x)\n", hr);\r
+ for (cnt=0; cnt < pcGlyphs; cnt++)\r
+ pwOutGlyphs[cnt] = pwOutGlyphs1[cnt]; /* Send to next function */\r
+ }\r
+\r
+ /* This test will check to make sure that SCRIPT_CACHE is reused and that not translation *\r
+ * takes place if fNoGlyphIndex is set. */\r
+\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(TestItem2, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);\r
+ /* This test is for the intertrim operation of ScriptItemize where only one SCRIPT_ITEM is *\r
+ * returned. */\r
+ ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == cInChars,\r
+ "Start pos not = 0 (%d) or end pos not = %d (%d)\n",\r
+ pItem[0].iCharPos, cInChars, pItem[1].iCharPos);\r
+ /* It would appear that we have a valid SCRIPT_ANALYSIS and can continue */\r
+ if (hr == 0) {\r
+ cChars = cInChars;\r
+ cMaxGlyphs = 256;\r
+ pItem[0].a.fNoGlyphIndex = 1; /* say no translate */\r
+ hr = ScriptShape(NULL, &psc, TestItem2, cChars,\r
+ cMaxGlyphs, &pItem[0].a,\r
+ pwOutGlyphs2, pwLogClust, psva, &pcGlyphs);\r
+ ok (hr != E_PENDING, "If psc should not be NULL (%08x) and the E_PENDING should be returned\n", hr);\r
+ ok (hr == 0, "ScriptShape should return 0 not (%08x)\n", hr);\r
+ ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");\r
+ ok (pcGlyphs == cChars, "Chars in (%d) should equal Glyphs out (%d)\n", cChars, pcGlyphs);\r
+ for (cnt=0; cnt < cChars && TestItem2[cnt] == pwOutGlyphs2[cnt]; cnt++) {}\r
+ ok (cnt == cChars, "Translation to place when told not to. WCHAR %d - %04x != %04x\n",\r
+ cnt, TestItem2[cnt], pwOutGlyphs2[cnt]);\r
+ if (hr ==0) {\r
+ hr = ScriptPlace(hdc, &psc, pwOutGlyphs2, pcGlyphs, psva, &pItem[0].a, piAdvance,\r
+ pGoffset, pABC);\r
+ ok (hr == 0, "ScriptPlace should return 0 not (%08x)\n", hr);\r
+ }\r
+ }\r
+ hr = ScriptFreeCache( &psc);\r
+ ok (!psc, "psc is not null after ScriptFreeCache\n");\r
+\r
+ }\r
+\r
+ /* This is a valid test that will cause parsing to take place and create 3 script_items */\r
+ cInChars = (sizeof(TestItem3)/2)-1;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(TestItem3, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);\r
+ if (hr == 0)\r
+ {\r
+ ok (pcItems == 3, "The number of SCRIPT_ITEMS should be 3 not %d\n", pcItems);\r
+ if (pcItems > 2)\r
+ {\r
+ ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == 6,\r
+ "Start pos [0] not = 0 (%d) or end pos [1] not = %d\n",\r
+ pItem[0].iCharPos, pItem[1].iCharPos);\r
+ ok (pItem[1].iCharPos == 6 && pItem[2].iCharPos == 11,\r
+ "Start pos [1] not = 6 (%d) or end pos [2] not = 11 (%d)\n",\r
+ pItem[1].iCharPos, pItem[2].iCharPos);\r
+ ok (pItem[2].iCharPos == 11 && pItem[3].iCharPos == cInChars,\r
+ "Start pos [2] not = 11 (%d) or end [3] pos not = 14 (%d), cInChars = %d\n",\r
+ pItem[2].iCharPos, pItem[3].iCharPos, cInChars);\r
+ }\r
+ hr = ScriptFreeCache( &psc);\r
+ ok (!psc, "psc is not null after ScriptFreeCache\n");\r
+ }\r
+\r
+ /* This is a valid test that will cause parsing to take place and create 3 script_items */\r
+ cInChars = (sizeof(TestItem4)/2)-1;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(TestItem4, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);\r
+ if (hr == 0)\r
+ {\r
+ ok (pcItems == 3, "The number of SCRIPT_ITEMS should be 3 not %d\n", pcItems);\r
+ if (pcItems > 2)\r
+ {\r
+ ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == 6,\r
+ "Start pos [0] not = 0 (%d) or end pos [1] not = %d\n",\r
+ pItem[0].iCharPos, pItem[1].iCharPos);\r
+ ok (pItem[1].iCharPos == 6 && pItem[2].iCharPos == 11,\r
+ "Start pos [1] not = 6 (%d) or end pos [2] not = 11 (%d)\n",\r
+ pItem[1].iCharPos, pItem[2].iCharPos);\r
+ ok (pItem[2].iCharPos == 11 && pItem[3].iCharPos == cInChars,\r
+ "Start pos [2] not = 11 (%d) or end [3] pos not = 14 (%d), cInChars = %d\n",\r
+ pItem[2].iCharPos, pItem[3].iCharPos, cInChars);\r
+ }\r
+ hr = ScriptFreeCache( &psc);\r
+ ok (!psc, "psc is not null after ScriptFreeCache\n");\r
+ }\r
+\r
+ /*\r
+ * This test is for when the first unicode character requires bidi support\r
+ */ \r
+ cInChars = (sizeof(TestItem5)-1)/sizeof(WCHAR);\r
+ hr = ScriptItemize(TestItem5, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);\r
+ ok (pcItems == 4, "There should have been 4 items, found %d\n", pcItems);\r
+ ok (pItem[0].a.s.uBidiLevel == 1, "The first character should have been bidi=1 not %d\n", \r
+ pItem[0].a.s.uBidiLevel);\r
+}\r
+\r
+static void test_ScriptGetCMap(HDC hdc, unsigned short pwOutGlyphs[256])\r
+{\r
+ HRESULT hr;\r
+ SCRIPT_CACHE psc = NULL;\r
+ int cInChars;\r
+ int cChars;\r
+ unsigned short pwOutGlyphs3[256];\r
+ WCHAR TestItem1[] = {'T', 'e', 's', 't', 'a', 0}; \r
+ DWORD dwFlags;\r
+ int cnt;\r
+\r
+ /* Check to make sure that SCRIPT_CACHE gets allocated ok */\r
+ dwFlags = 0;\r
+ cInChars = cChars = 5;\r
+ /* Some sanity checks for ScriptGetCMap */\r
+\r
+ hr = ScriptGetCMap(NULL, NULL, NULL, 0, 0, NULL);\r
+ ok( hr == E_INVALIDARG, "(NULL,NULL,NULL,0,0,NULL), "\r
+ "expected E_INVALIDARG, got %08x\n", hr);\r
+\r
+ hr = ScriptGetCMap(NULL, NULL, TestItem1, cInChars, dwFlags, pwOutGlyphs3);\r
+ ok( hr == E_INVALIDARG, "(NULL,NULL,TestItem1, cInChars, dwFlags, pwOutGlyphs3), "\r
+ "expected E_INVALIDARG, got %08x\n", hr);\r
+\r
+ /* Set psc to NULL, to be able to check if a pointer is returned in psc */\r
+ psc = NULL;\r
+ hr = ScriptGetCMap(NULL, &psc, NULL, 0, 0, NULL);\r
+ ok( hr == E_PENDING, "(NULL,&psc,NULL,0,0NULL), expected E_PENDING, "\r
+ "got %08x\n", hr);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ /* Set psc to NULL but add hdc, to be able to check if a pointer is returned in psc */\r
+ psc = NULL;\r
+ hr = ScriptGetCMap(hdc, &psc, NULL, 0, 0, NULL);\r
+ ok( hr == S_OK, "ScriptGetCMap(NULL,&psc,NULL,0,0,NULL), expected S_OK, "\r
+ "got %08x\n", hr);\r
+ ok( psc != NULL, "ScritpGetCMap expected psc to be not NULL\n");\r
+\r
+ /* Set psc to NULL, to be able to check if a pointer is returned in psc */\r
+ psc = NULL;\r
+ hr = ScriptGetCMap(NULL, &psc, TestItem1, cInChars, dwFlags, pwOutGlyphs3);\r
+ ok( hr == E_PENDING, "(NULL,&psc,), expected E_PENDING, got %08x\n", hr);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+ /* Check to see if the results are the same as those returned by ScriptShape */\r
+ hr = ScriptGetCMap(hdc, &psc, TestItem1, cInChars, dwFlags, pwOutGlyphs3);\r
+ ok (hr == 0, "ScriptGetCMap should return 0 not (%08x)\n", hr);\r
+ ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");\r
+ for (cnt=0; cnt < cChars && pwOutGlyphs[cnt] == pwOutGlyphs3[cnt]; cnt++) {}\r
+ ok (cnt == cInChars, "Translation not correct. WCHAR %d - %04x != %04x\n",\r
+ cnt, pwOutGlyphs[cnt], pwOutGlyphs3[cnt]);\r
+ \r
+ hr = ScriptFreeCache( &psc);\r
+ ok (!psc, "psc is not null after ScriptFreeCache\n");\r
+\r
+}\r
+\r
+static void test_ScriptGetFontProperties(HDC hdc)\r
+{\r
+ HRESULT hr;\r
+ SCRIPT_CACHE psc,old_psc;\r
+ SCRIPT_FONTPROPERTIES sfp;\r
+\r
+ /* Some sanity checks for ScriptGetFontProperties */\r
+\r
+ hr = ScriptGetFontProperties(NULL,NULL,NULL);\r
+ ok( hr == E_INVALIDARG, "(NULL,NULL,NULL), expected E_INVALIDARG, got %08x\n", hr);\r
+\r
+ hr = ScriptGetFontProperties(NULL,NULL,&sfp);\r
+ ok( hr == E_INVALIDARG, "(NULL,NULL,&sfp), expected E_INVALIDARG, got %08x\n", hr);\r
+\r
+ /* Set psc to NULL, to be able to check if a pointer is returned in psc */\r
+ psc = NULL;\r
+ hr = ScriptGetFontProperties(NULL,&psc,NULL);\r
+ ok( hr == E_INVALIDARG, "(NULL,&psc,NULL), expected E_INVALIDARG, got %08x\n", hr);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ /* Set psc to NULL, to be able to check if a pointer is returned in psc */\r
+ psc = NULL;\r
+ hr = ScriptGetFontProperties(NULL,&psc,&sfp);\r
+ ok( hr == E_PENDING, "(NULL,&psc,&sfp), expected E_PENDING, got %08x\n", hr);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ hr = ScriptGetFontProperties(hdc,NULL,NULL);\r
+ ok( hr == E_INVALIDARG, "(hdc,NULL,NULL), expected E_INVALIDARG, got %08x\n", hr);\r
+\r
+ hr = ScriptGetFontProperties(hdc,NULL,&sfp);\r
+ ok( hr == E_INVALIDARG, "(hdc,NULL,&sfp), expected E_INVALIDARG, got %08x\n", hr);\r
+\r
+ /* Set psc to NULL, to be able to check if a pointer is returned in psc */\r
+ psc = NULL;\r
+ hr = ScriptGetFontProperties(hdc,&psc,NULL);\r
+ ok( hr == E_INVALIDARG, "(hdc,&psc,NULL), expected E_INVALIDARG, got %08x\n", hr);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ /* Pass an uninitialized sfp */\r
+ psc = NULL;\r
+ hr = ScriptGetFontProperties(hdc,&psc,&sfp);\r
+ ok( hr == E_INVALIDARG, "(hdc,&psc,&sfp) partly uninitialized, expected E_INVALIDARG, got %08x\n", hr);\r
+ ok( psc != NULL, "Expected a pointer in psc, got NULL\n");\r
+ ScriptFreeCache(&psc);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ /* Give it the correct cBytes, we don't care about what's coming back */\r
+ sfp.cBytes = sizeof(SCRIPT_FONTPROPERTIES);\r
+ psc = NULL;\r
+ hr = ScriptGetFontProperties(hdc,&psc,&sfp);\r
+ ok( hr == S_OK, "(hdc,&psc,&sfp) partly initialized, expected S_OK, got %08x\n", hr);\r
+ ok( psc != NULL, "Expected a pointer in psc, got NULL\n");\r
+\r
+ /* Save the psc pointer */\r
+ old_psc = psc;\r
+ /* Now a NULL hdc again */\r
+ hr = ScriptGetFontProperties(NULL,&psc,&sfp);\r
+ ok( hr == S_OK, "(NULL,&psc,&sfp), expected S_OK, got %08x\n", hr);\r
+ ok( psc == old_psc, "Expected psc not to be changed, was %p is now %p\n", old_psc, psc);\r
+ ScriptFreeCache(&psc);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+}\r
+\r
+static void test_ScriptTextOut(HDC hdc)\r
+{\r
+ HRESULT hr;\r
+\r
+ int cInChars;\r
+ int cMaxItems;\r
+ SCRIPT_ITEM pItem[255];\r
+ int pcItems;\r
+ WCHAR TestItem1[] = {'T', 'e', 's', 't', 'a', 0}; \r
+\r
+ SCRIPT_CACHE psc;\r
+ int cChars;\r
+ int cMaxGlyphs;\r
+ unsigned short pwOutGlyphs1[256];\r
+ WORD pwLogClust[256];\r
+ SCRIPT_VISATTR psva[256];\r
+ int pcGlyphs;\r
+ int piAdvance[256];\r
+ GOFFSET pGoffset[256];\r
+ ABC pABC[256];\r
+ RECT rect;\r
+ int piX;\r
+ int iCP = 1;\r
+ BOOL fTrailing = FALSE;\r
+ SCRIPT_LOGATTR *psla;\r
+ SCRIPT_LOGATTR sla[256];\r
+\r
+ /* This is a valid test that will cause parsing to take place */\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);\r
+ ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);\r
+ /* This test is for the interim operation of ScriptItemize where only one SCRIPT_ITEM is *\r
+ * returned. */\r
+ ok (pcItems > 0, "The number of SCRIPT_ITEMS should be greater than 0\n");\r
+ if (pcItems > 0)\r
+ ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == cInChars,\r
+ "Start pos not = 0 (%d) or end pos not = %d (%d)\n",\r
+ pItem[0].iCharPos, cInChars, pItem[1].iCharPos);\r
+\r
+ /* It would appear that we have a valid SCRIPT_ANALYSIS and can continue\r
+ * ie. ScriptItemize has succeeded and that pItem has been set */\r
+ cInChars = 5;\r
+ cMaxItems = 255;\r
+ if (hr == 0) {\r
+ psc = NULL; /* must be null on first call */\r
+ cChars = cInChars;\r
+ cMaxGlyphs = cInChars;\r
+ cMaxGlyphs = 256;\r
+ hr = ScriptShape(hdc, &psc, TestItem1, cChars,\r
+ cMaxGlyphs, &pItem[0].a,\r
+ pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);\r
+ ok (hr == 0, "ScriptShape should return 0 not (%08x)\n", hr);\r
+ ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");\r
+ ok (pcGlyphs == cChars, "Chars in (%d) should equal Glyphs out (%d)\n", cChars, pcGlyphs);\r
+ if (hr ==0) {\r
+ /* Note hdc is needed as glyph info is not yet in psc */\r
+ hr = ScriptPlace(hdc, &psc, pwOutGlyphs1, pcGlyphs, psva, &pItem[0].a, piAdvance,\r
+ pGoffset, pABC);\r
+ ok (hr == 0, "Should return 0 not (%08x)\n", hr);\r
+ ScriptFreeCache(&psc); /* Get rid of psc for next test set */\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ hr = ScriptTextOut(NULL, NULL, 0, 0, 0, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL);\r
+ ok (hr == E_INVALIDARG, "Should return 0 not (%08x)\n", hr);\r
+\r
+ hr = ScriptTextOut(NULL, NULL, 0, 0, 0, NULL, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,\r
+ piAdvance, NULL, pGoffset);\r
+ ok( hr == E_INVALIDARG, "(NULL,NULL,TestItem1, cInChars, dwFlags, pwOutGlyphs3), "\r
+ "expected E_INVALIDARG, got %08x\n", hr);\r
+\r
+ /* Set psc to NULL, to be able to check if a pointer is returned in psc */\r
+ psc = NULL;\r
+ hr = ScriptTextOut(NULL, &psc, 0, 0, 0, NULL, NULL, NULL, 0, NULL, 0,\r
+ NULL, NULL, NULL);\r
+ ok( hr == E_INVALIDARG, "(NULL,&psc,NULL,0,0,0,NULL,), expected E_INVALIDARG, "\r
+ "got %08x\n", hr);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ /* Set psc to NULL, to be able to check if a pointer is returned in psc\r
+ * hdc is required for this one rather than the usual optional */\r
+ psc = NULL;\r
+ hr = ScriptTextOut(NULL, &psc, 0, 0, 0, NULL, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,\r
+ piAdvance, NULL, pGoffset);\r
+ ok( hr == E_INVALIDARG, "(NULL,&psc,), expected E_INVALIDARG, got %08x\n", hr);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+\r
+ /* Set that is gets a psc and that returns 0 status */\r
+ hr = ScriptTextOut(hdc, &psc, 0, 0, 0, NULL, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,\r
+ piAdvance, NULL, pGoffset);\r
+ ok (hr == 0, "ScriptTextOut should return 0 not (%08x)\n", hr);\r
+ ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");\r
+\r
+ /* Test Rect Rgn is acceptable */\r
+ rect.top = 10;\r
+ rect.bottom = 20;\r
+ rect.left = 10;\r
+ rect.right = 40;\r
+ hr = ScriptTextOut(hdc, &psc, 0, 0, 0, &rect, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,\r
+ piAdvance, NULL, pGoffset);\r
+ ok (hr == 0, "ScriptTextOut should return 0 not (%08x)\n", hr);\r
+ ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");\r
+\r
+ iCP = 1;\r
+ hr = ScriptCPtoX(iCP, fTrailing, cChars, pcGlyphs, (const WORD *) &pwLogClust,\r
+ (const SCRIPT_VISATTR *) &psva, (const int *)&piAdvance, &pItem[0].a, &piX);\r
+ ok(hr == S_OK, "ScriptCPtoX Stub should return S_OK not %08x\n", hr);\r
+\r
+ psla = (SCRIPT_LOGATTR *)&sla;\r
+ hr = ScriptBreak(TestItem1, cChars, &pItem[0].a, psla);\r
+ ok(hr == S_OK, "ScriptBreak Stub should return S_OK not %08x\n", hr);\r
+\r
+ /* Clean up and go */\r
+ ScriptFreeCache(&psc);\r
+ ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);\r
+ }\r
+ }\r
+}\r
+\r
+static void test_ScriptXtoX(void)\r
+/****************************************************************************************\r
+ * This routine tests the ScriptXtoCP and ScriptCPtoX functions using static variables *\r
+ ****************************************************************************************/\r
+{\r
+ int iX, iCP;\r
+ int cChars;\r
+ int cGlyphs;\r
+ WORD pwLogClust[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\r
+ SCRIPT_VISATTR psva[10];\r
+ int piAdvance[10] = {200, 190, 210, 180, 170, 204, 189, 195, 212, 203};\r
+ SCRIPT_ANALYSIS psa;\r
+ int piCP, piX;\r
+ int piTrailing;\r
+ BOOL fTrailing;\r
+ HRESULT hr;\r
+\r
+ iX = -1;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == -1, "Negative iX should return piCP=-1 not %d\n", piCP);\r
+ ok(piTrailing == TRUE, "Negative iX should return piTrailing=TRUE not %d\n", piTrailing);\r
+ iX = 1954;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == 10, "Excessive iX should return piCP=10 not %d\n", piCP);\r
+ ok(piTrailing == FALSE, "Excessive iX should return piTrailing=FALSE not %d\n", piTrailing);\r
+ iX = 779;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == 3, "iX=%d should return piCP=3 not %d\n", iX, piCP);\r
+ ok(piTrailing == 1, "iX=%d should return piTrailing=1 not %d\n", iX, piTrailing);\r
+ iX = 780;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == 3, "iX=%d should return piCP=3 not %d\n", iX, piCP);\r
+ ok(piTrailing == 1, "iX=%d should return piTrailing=1 not %d\n", iX, piTrailing);\r
+ iX = 868;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == 4, "iX=%d should return piCP=4 not %d\n", iX, piCP);\r
+\r
+ iX = 0;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == 0, "iX=%d should return piCP=0 not %d\n", iX, piCP);\r
+ iX = 195;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == 0, "iX=%d should return piCP=0 not %d\n", iX, piCP);\r
+ iX = 196;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piCP, &piTrailing);\r
+ ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);\r
+ ok(piCP == 1, "iX=%d should return piCP=1 not %d\n", iX, piCP);\r
+\r
+ iCP=5;\r
+ fTrailing = FALSE;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piX);\r
+ ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);\r
+ ok(piX == 976, "iCP=%d should return piX=976 not %d\n", iCP, piX);\r
+ iCP=5;\r
+ fTrailing = TRUE;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piX);\r
+ ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);\r
+ ok(piX == 1171, "iCP=%d should return piX=1171 not %d\n", iCP, piX); \r
+ iCP=6;\r
+ fTrailing = FALSE;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piX);\r
+ ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);\r
+ ok(piX == 1171, "iCP=%d should return piX=1171 not %d\n", iCP, piX);\r
+ iCP=11;\r
+ fTrailing = FALSE;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piX);\r
+ ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);\r
+ ok(piX == 1953, "iCP=%d should return piX=1953 not %d\n", iCP, piX);\r
+ iCP=11;\r
+ fTrailing = TRUE;\r
+ cChars = 10;\r
+ cGlyphs = 10;\r
+ hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &psa, &piX);\r
+ ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);\r
+ ok(piX == 1953, "iCP=%d should return piX=1953 not %d\n", iCP, piX); \r
+\r
+}\r
+\r
+static void test_ScriptString(HDC hdc)\r
+{\r
+/*******************************************************************************************\r
+ *\r
+ * This set of tests are for the string functions of uniscribe. The ScriptStringAnalyse\r
+ * function allocates memory pointed to by the SCRIPT_STRING_ANALYSIS ssa pointer. This\r
+ * memory if freed by ScriptStringFree. There needs to be a valid hdc for this as\r
+ * ScriptStringAnalyse calls ScriptSItemize, ScriptShape and ScriptPlace which require it.\r
+ *\r
+ */\r
+\r
+ HRESULT hr;\r
+ WCHAR teststr[] = {'T','e','s','t','1',' ','a','2','b','3', '\0'};\r
+ int len = (sizeof(teststr) / sizeof(WCHAR)) - 1;\r
+ int Glyphs = len * 2 + 16;\r
+ int Charset;\r
+ DWORD Flags = SSA_GLYPHS;\r
+ int ReqWidth = 100;\r
+ SCRIPT_CONTROL Control;\r
+ SCRIPT_STATE State;\r
+ const int Dx[5] = {10, 10, 10, 10, 10};\r
+ SCRIPT_TABDEF Tabdef;\r
+ const BYTE InClass = 0;\r
+ SCRIPT_STRING_ANALYSIS ssa = NULL;\r
+\r
+ int X = 10; \r
+ int Y = 100;\r
+ UINT Options = 0; \r
+ const RECT rc = {0, 50, 100, 100}; \r
+ int MinSel = 0;\r
+ int MaxSel = 0;\r
+ BOOL Disabled = FALSE;\r
+ const int *clip_len;\r
+ UINT *order, i;\r
+\r
+\r
+ Charset = -1; /* this flag indicates unicode input */\r
+ /* Test without hdc to get E_PENDING */\r
+ hr = ScriptStringAnalyse( NULL, teststr, len, Glyphs, Charset, Flags,\r
+ ReqWidth, &Control, &State, Dx, &Tabdef,\r
+ &InClass, &ssa);\r
+ ok(hr == E_PENDING, "ScriptStringAnalyse Stub should return E_PENDING not %08x\n", hr);\r
+\r
+ /* test with hdc, this should be a valid test */\r
+ hr = ScriptStringAnalyse( hdc, teststr, len, Glyphs, Charset, Flags,\r
+ ReqWidth, &Control, &State, Dx, &Tabdef,\r
+ &InClass, &ssa);\r
+ ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08x\n", hr);\r
+\r
+ /* test makes sure that a call with a valid pssa still works */\r
+ hr = ScriptStringAnalyse( hdc, teststr, len, Glyphs, Charset, Flags,\r
+ ReqWidth, &Control, &State, Dx, &Tabdef,\r
+ &InClass, &ssa);\r
+ ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08x\n", hr);\r
+ ok(ssa != NULL, "ScriptStringAnalyse pssa should not be NULL\n");\r
+\r
+ if (hr == S_OK)\r
+ {\r
+ hr = ScriptStringOut(ssa, X, Y, Options, &rc, MinSel, MaxSel, Disabled);\r
+ ok(hr == S_OK, "ScriptStringOut should return S_OK not %08x\n", hr);\r
+ }\r
+\r
+ clip_len = ScriptString_pcOutChars(ssa);\r
+ ok(*clip_len == len, "ScriptString_pcOutChars failed, got %d, expected %d\n", *clip_len, len);\r
+\r
+ order = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *clip_len * sizeof(UINT));\r
+ hr = ScriptStringGetOrder(ssa, order);\r
+ ok(hr == S_OK, "ScriptStringGetOrder failed, got %08x, expected S_OK\n", hr);\r
+\r
+ for (i = 0; i < *clip_len; i++) ok(order[i] == i, "%d: got %d expected %d\n", i, order[i], i);\r
+ HeapFree(GetProcessHeap(), 0, order);\r
+\r
+ hr = ScriptStringFree(&ssa);\r
+ ok(hr == S_OK, "ScriptStringFree should return S_OK not %08x\n", hr);\r
+}\r
+\r
+static void test_ScriptStringXtoCP_CPtoX(HDC hdc)\r
+{\r
+/*****************************************************************************************\r
+ *\r
+ * This test is for the ScriptStringXtoCP and ScriptStringXtoCP functions. Due to the\r
+ * nature of the fonts between Windows and Wine, the test is implemented by generating\r
+ * values using one one function then checking the output of the second. In this way\r
+ * the validity of the functions is established using Windows as a base and confirming\r
+ * similar behaviour in wine.\r
+ */\r
+\r
+ HRESULT hr;\r
+ WCHAR teststr1[] = {'T', 'e', 's', 't', 'e', '1', '2', ' ', 'a', '\0'};\r
+ void *String = (WCHAR *) &teststr1; /* ScriptStringAnalysis needs void */\r
+ int String_len = (sizeof(teststr1)/sizeof(WCHAR))-1;\r
+ int Glyphs = String_len * 2 + 16; /* size of buffer as recommended */\r
+ int Charset = -1; /* unicode */\r
+ DWORD Flags = SSA_GLYPHS;\r
+ int ReqWidth = 100;\r
+ SCRIPT_CONTROL Control;\r
+ SCRIPT_STATE State;\r
+ SCRIPT_TABDEF Tabdef;\r
+ const BYTE InClass = 0;\r
+ SCRIPT_STRING_ANALYSIS ssa = NULL;\r
+\r
+ int Ch; /* Character position in string */\r
+ int iTrailing;\r
+ int Cp; /* Character position in string */\r
+ int X;\r
+ BOOL fTrailing;\r
+\r
+ /* Test with hdc, this should be a valid test\r
+ * Here we generrate an SCRIPT_STRING_ANALYSIS that will be used as input to the\r
+ * following character positions to X and X to character position functions.\r
+ */\r
+ hr = ScriptStringAnalyse( hdc, String, String_len, Glyphs, Charset, Flags,\r
+ ReqWidth, &Control, &State, NULL, &Tabdef,\r
+ &InClass, &ssa);\r
+ ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08x\n", hr);\r
+ ok(ssa != NULL, "ScriptStringAnalyse ssa should not be NULL\n");\r
+ if (hr == 0)\r
+ {\r
+ /*\r
+ * Loop to generate character positions to provide starting positions for the\r
+ * ScriptStringCPtoX and ScriptStringXtoCP functions\r
+ */\r
+ for (Cp = 0; Cp < String_len; Cp++)\r
+ {\r
+ /* The fTrailing flag is used to indicate whether the X being returned is at\r
+ * the beginning or the end of the character. What happens here is that if\r
+ * fTrailing indicates the end of the character, ie. FALSE, then ScriptStringXtoCP\r
+ * returns the beginning of the next character and iTrailing is FALSE. So for this\r
+ * loop iTrailing will be FALSE in both cases.\r
+ */\r
+ fTrailing = FALSE;\r
+ hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);\r
+ ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);\r
+ hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);\r
+ ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);\r
+ ok(Cp == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp, Ch, X);\r
+ ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", \r
+ iTrailing, X);\r
+ fTrailing = TRUE;\r
+ hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);\r
+ ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);\r
+ hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);\r
+ ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);\r
+\r
+ /*\r
+ * Check that character position returned by ScriptStringXtoCP in Ch matches the\r
+ * one input to ScriptStringCPtoX. This means that the Cp to X position and back\r
+ * again works\r
+ */\r
+ ok(Cp + 1 == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp + 1, Ch, X);\r
+ ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", \r
+ iTrailing, X);\r
+ }\r
+ /*\r
+ * This test is to check that if the X position is just inside the trailing edge of the\r
+ * character then iTrailing will indicate the trailing edge, ie. TRUE\r
+ */\r
+ fTrailing = TRUE;\r
+ Cp = 3;\r
+ hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);\r
+ ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);\r
+ X--; /* put X just inside the trailing edge */\r
+ hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);\r
+ ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);\r
+ ok(Cp == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp, Ch, X);\r
+ ok(iTrailing == TRUE, "ScriptStringXtoCP should return iTrailing = 1 not %d for X = %d\n", \r
+ iTrailing, X);\r
+\r
+ /*\r
+ * This test is to check that if the X position is just outside the trailing edge of the\r
+ * character then iTrailing will indicate the leading edge, ie. FALSE, and Ch will indicate\r
+ * the next character, ie. Cp + 1 \r
+ */\r
+ fTrailing = TRUE;\r
+ Cp = 3;\r
+ hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);\r
+ ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);\r
+ X++; /* put X just outside the trailing edge */\r
+ hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);\r
+ ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);\r
+ ok(Cp + 1 == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp + 1, Ch, X);\r
+ ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", \r
+ iTrailing, X);\r
+\r
+ /*\r
+ * This test is to check that if the X position is just outside the leading edge of the\r
+ * character then iTrailing will indicate the trailing edge, ie. TRUE, and Ch will indicate\r
+ * the next character down , ie. Cp - 1 \r
+ */\r
+ fTrailing = FALSE;\r
+ Cp = 3;\r
+ hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);\r
+ ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);\r
+ X--; /* put X just outside the leading edge */\r
+ hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);\r
+ ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);\r
+ ok(Cp - 1 == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp - 1, Ch, X);\r
+ ok(iTrailing == TRUE, "ScriptStringXtoCP should return iTrailing = 1 not %d for X = %d\n", \r
+ iTrailing, X);\r
+\r
+ /*\r
+ * Cleanup the the SSA for the next round of tests\r
+ */\r
+ hr = ScriptStringFree(&ssa);\r
+ ok(hr == S_OK, "ScriptStringFree should return S_OK not %08x\n", hr);\r
+\r
+ /*\r
+ * Test to see that exceeding the number of chars returns E_INVALIDARG. First\r
+ * generate an SSA for the subsequent tests.\r
+ */\r
+ hr = ScriptStringAnalyse( hdc, String, String_len, Glyphs, Charset, Flags,\r
+ ReqWidth, &Control, &State, NULL, &Tabdef,\r
+ &InClass, &ssa);\r
+ ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08x\n", hr);\r
+\r
+ /*\r
+ * When ScriptStringCPtoX is called with a character position Cp that exceeds the\r
+ * string length, return E_INVALIDARG. This also invalidates the ssa so a \r
+ * ScriptStringFree should also fail.\r
+ */\r
+ fTrailing = FALSE;\r
+ Cp = String_len + 1; \r
+ hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);\r
+ ok(hr == E_INVALIDARG, "ScriptStringCPtoX should return E_INVALIDARG not %08x\n", hr);\r
+\r
+ hr = ScriptStringFree(&ssa);\r
+ /*\r
+ * ScriptStringCPtoX should free ssa, hence ScriptStringFree should fail\r
+ */\r
+ ok(hr == E_INVALIDARG, "ScriptStringFree should return E_INVALIDARG not %08x\n", hr);\r
+ }\r
+}\r
+\r
+static void test_ScriptCacheGetHeight(HDC hdc)\r
+{\r
+ HRESULT hr;\r
+ SCRIPT_CACHE sc = NULL;\r
+ LONG height;\r
+\r
+ hr = ScriptCacheGetHeight(NULL, NULL, NULL);\r
+ ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);\r
+\r
+ hr = ScriptCacheGetHeight(NULL, &sc, NULL);\r
+ ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);\r
+\r
+ hr = ScriptCacheGetHeight(NULL, &sc, &height);\r
+ ok(hr == E_PENDING, "expected E_PENDING, got 0x%08x\n", hr);\r
+\r
+ height = 0;\r
+\r
+ hr = ScriptCacheGetHeight(hdc, &sc, &height);\r
+ ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);\r
+\r
+ ok(height > 0, "expected height > 0\n");\r
+}\r
+\r
+static void test_ScriptGetGlyphABCWidth(HDC hdc)\r
+{\r
+ HRESULT hr;\r
+ SCRIPT_CACHE sc = NULL;\r
+ ABC abc;\r
+\r
+ hr = ScriptGetGlyphABCWidth(NULL, NULL, 'a', NULL);\r
+ ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);\r
+\r
+ hr = ScriptGetGlyphABCWidth(NULL, &sc, 'a', NULL);\r
+ ok(hr == E_PENDING, "expected E_PENDING, got 0x%08x\n", hr);\r
+\r
+ if (0) { /* crashes on WinXP */\r
+ hr = ScriptGetGlyphABCWidth(hdc, &sc, 'a', NULL);\r
+ ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);\r
+ }\r
+\r
+ hr = ScriptGetGlyphABCWidth(hdc, &sc, 'a', &abc);\r
+ ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);\r
+}\r
+\r
+static void test_ScriptLayout(void)\r
+{\r
+ HRESULT hr;\r
+ static const BYTE levels[][5] =\r
+ {\r
+ { 0, 0, 0, 0, 0 },\r
+ { 1, 1, 1, 1, 1 },\r
+ { 2, 2, 2, 2, 2 },\r
+ { 3, 3, 3, 3, 3 },\r
+ };\r
+ static const int expect[][5] =\r
+ {\r
+ { 0, 1, 2, 3, 4 },\r
+ { 4, 3, 2, 1, 0 },\r
+ { 0, 1, 2, 3, 4 },\r
+ { 4, 3, 2, 1, 0 }\r
+ };\r
+ int i, j, vistolog[sizeof(levels[0])], logtovis[sizeof(levels[0])];\r
+\r
+ hr = ScriptLayout(sizeof(levels[0]), NULL, vistolog, logtovis);\r
+ ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);\r
+\r
+ hr = ScriptLayout(sizeof(levels[0]), levels[0], NULL, NULL);\r
+ ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);\r
+\r
+ for (i = 0; i < sizeof(levels)/sizeof(levels[0]); i++)\r
+ {\r
+ hr = ScriptLayout(sizeof(levels[0]), levels[i], vistolog, logtovis);\r
+ ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);\r
+\r
+ for (j = 0; j < sizeof(levels[i]); j++)\r
+ {\r
+ ok(expect[i][j] == vistolog[j],\r
+ "failure: levels[%d][%d] = %d, vistolog[%d] = %d\n",\r
+ i, j, levels[i][j], j, vistolog[j] );\r
+ }\r
+\r
+ for (j = 0; j < sizeof(levels[i]); j++)\r
+ {\r
+ ok(expect[i][j] == logtovis[j],\r
+ "failure: levels[%d][%d] = %d, logtovis[%d] = %d\n",\r
+ i, j, levels[i][j], j, logtovis[j] );\r
+ }\r
+ }\r
+}\r
+\r
+static const struct\r
+{\r
+ LGRPID group;\r
+ LCID lcid;\r
+ SCRIPT_DIGITSUBSTITUTE sds;\r
+ DWORD uDefaultLanguage;\r
+ DWORD fContextDigits;\r
+ WORD fDigitSubstitute;\r
+}\r
+subst_data[] =\r
+{\r
+ { 0x01, 0x00403, { 9, 3, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00406, { 9, 6, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00407, { 9, 7, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00409, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0040a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0040b, { 9, 11, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0040c, { 9, 12, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0040f, { 9, 15, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00410, { 9, 16, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00413, { 9, 19, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00414, { 9, 20, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00416, { 9, 22, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0041d, { 9, 29, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00421, { 9, 33, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0042d, { 9, 45, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00432, { 9, 50, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00434, { 9, 52, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00435, { 9, 53, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00436, { 9, 54, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00438, { 9, 56, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0043a, { 9, 58, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0043b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0043e, { 9, 62, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00441, { 9, 65, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00452, { 9, 82, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00456, { 9, 86, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0046b, { 9, 107, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0046c, { 9, 108, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00481, { 9, 129, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00807, { 9, 7, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00809, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0080a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0080c, { 9, 12, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00810, { 9, 16, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00813, { 9, 19, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00814, { 9, 20, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00816, { 9, 22, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0081d, { 9, 29, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0083b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0083e, { 9, 62, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0086b, { 9, 107, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00c07, { 9, 7, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00c09, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00c0a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00c0c, { 9, 12, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00c3b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x00c6b, { 9, 107, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01007, { 9, 7, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01009, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0100a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0100c, { 9, 12, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0103b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01407, { 9, 7, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01409, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0140a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0140c, { 9, 12, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0143b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01809, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0180a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0180c, { 9, 12, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0183b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01c09, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01c0a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x01c3b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x02009, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0200a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0203b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x02409, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0240a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0243b, { 9, 59, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x02809, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0280a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x02c09, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x02c0a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x03009, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0300a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x03409, { 9, 9, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0340a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0380a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x03c0a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0400a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0440a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0480a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x04c0a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x0500a, { 9, 10, 1, 0 }, 9, 0, 0 },\r
+ { 0x01, 0x10407, { 9, 7, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x00405, { 9, 5, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0040e, { 9, 14, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x00415, { 9, 21, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x00418, { 9, 24, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0041a, { 9, 26, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0041b, { 9, 27, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0041c, { 9, 28, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x00424, { 9, 36, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0081a, { 9, 26, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0101a, { 9, 26, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0141a, { 9, 26, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x0181a, { 9, 26, 1, 0 }, 9, 0, 0 },\r
+ { 0x02, 0x1040e, { 9, 14, 1, 0 }, 9, 0, 0 },\r
+ { 0x03, 0x00425, { 9, 37, 1, 0 }, 9, 0, 0 },\r
+ { 0x03, 0x00426, { 9, 38, 1, 0 }, 9, 0, 0 },\r
+ { 0x03, 0x00427, { 9, 39, 1, 0 }, 9, 0, 0 },\r
+ { 0x04, 0x00408, { 9, 8, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00402, { 9, 2, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00419, { 9, 25, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00422, { 9, 34, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00423, { 9, 35, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x0042f, { 9, 47, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x0043f, { 9, 63, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00440, { 9, 64, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00444, { 9, 68, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00450, { 9, 80, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x0082c, { 9, 44, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00843, { 9, 67, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x00c1a, { 9, 26, 1, 0 }, 9, 0, 0 },\r
+ { 0x05, 0x01c1a, { 9, 26, 1, 0 }, 9, 0, 0 },\r
+ { 0x06, 0x0041f, { 9, 31, 1, 0 }, 9, 0, 0 },\r
+ { 0x06, 0x0042c, { 9, 44, 1, 0 }, 9, 0, 0 },\r
+ { 0x06, 0x00443, { 9, 67, 1, 0 }, 9, 0, 0 },\r
+ { 0x07, 0x00411, { 9, 17, 1, 0 }, 9, 0, 0 },\r
+ { 0x08, 0x00412, { 9, 18, 1, 0 }, 9, 0, 0 },\r
+ { 0x09, 0x00404, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x09, 0x00c04, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x09, 0x01404, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x09, 0x21404, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x09, 0x30404, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x0a, 0x00804, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x0a, 0x01004, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x0a, 0x20804, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x0a, 0x21004, { 9, 4, 1, 0 }, 9, 0, 0 },\r
+ { 0x0b, 0x0041e, { 9, 30, 1, 0 }, 9, 0, 0 },\r
+ { 0x0c, 0x0040d, { 9, 13, 1, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x00401, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x00420, { 9, 32, 1, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x00429, { 41, 41, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x0045a, { 9, 90, 1, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x00465, { 9, 101, 1, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x00801, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x00c01, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x01001, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x01401, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x01801, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x01c01, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x02001, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x02401, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x02801, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x02c01, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x03001, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x03401, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x03801, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x03c01, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0d, 0x04001, { 1, 1, 0, 0 }, 9, 0, 0 },\r
+ { 0x0e, 0x0042a, { 9, 42, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x00439, { 9, 57, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x00446, { 9, 70, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x00447, { 9, 71, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x00449, { 9, 73, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x0044a, { 9, 74, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x0044b, { 9, 75, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x0044e, { 9, 78, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x0044f, { 9, 79, 1, 0 }, 9, 0, 0 },\r
+ { 0x0f, 0x00457, { 9, 87, 1, 0 }, 9, 0, 0 },\r
+ { 0x10, 0x00437, { 9, 55, 1, 0 }, 9, 0, 0 },\r
+ { 0x10, 0x10437, { 9, 55, 1, 0 }, 9, 0, 0 },\r
+ { 0x11, 0x0042b, { 9, 43, 1, 0 }, 9, 0, 0 }\r
+};\r
+\r
+static BOOL CALLBACK enum_proc(LGRPID group, LCID lcid, LPSTR locale, LONG_PTR lparam)\r
+{\r
+ HRESULT hr;\r
+ SCRIPT_DIGITSUBSTITUTE sds;\r
+ SCRIPT_CONTROL sc;\r
+ SCRIPT_STATE ss;\r
+ LCID lcid_old;\r
+ unsigned int i;\r
+\r
+ if (!IsValidLocale(lcid, LCID_INSTALLED)) return TRUE;\r
+\r
+ memset(&sds, 0, sizeof(sds));\r
+ memset(&sc, 0, sizeof(sc));\r
+ memset(&ss, 0, sizeof(ss));\r
+\r
+ lcid_old = GetThreadLocale();\r
+ if (!SetThreadLocale(lcid)) return TRUE;\r
+\r
+ hr = ScriptRecordDigitSubstitution(lcid, &sds);\r
+ ok(hr == S_OK, "ScriptRecordDigitSubstitution failed: 0x%08x\n", hr);\r
+\r
+ hr = ScriptApplyDigitSubstitution(&sds, &sc, &ss);\r
+ ok(hr == S_OK, "ScriptApplyDigitSubstitution failed: 0x%08x\n", hr);\r
+\r
+ for (i = 0; i < sizeof(subst_data)/sizeof(subst_data[0]); i++)\r
+ {\r
+ if (group == subst_data[i].group && lcid == subst_data[i].lcid)\r
+ {\r
+ ok(!memcmp(&sds, &subst_data[i].sds, sizeof(sds)),\r
+ "substitution data does not match\n");\r
+\r
+ ok(sc.uDefaultLanguage == subst_data[i].uDefaultLanguage,\r
+ "sc.uDefaultLanguage does not match\n");\r
+ ok(sc.fContextDigits == subst_data[i].fContextDigits,\r
+ "sc.fContextDigits does not match\n");\r
+ ok(ss.fDigitSubstitute == subst_data[i].fDigitSubstitute,\r
+ "ss.fDigitSubstitute does not match\n");\r
+ }\r
+ }\r
+ SetThreadLocale(lcid_old);\r
+ return TRUE;\r
+}\r
+\r
+static void test_digit_substitution(void)\r
+{\r
+ BOOL ret;\r
+ unsigned int i;\r
+ static const LGRPID groups[] =\r
+ {\r
+ LGRPID_WESTERN_EUROPE,\r
+ LGRPID_CENTRAL_EUROPE,\r
+ LGRPID_BALTIC,\r
+ LGRPID_GREEK,\r
+ LGRPID_CYRILLIC,\r
+ LGRPID_TURKISH,\r
+ LGRPID_JAPANESE,\r
+ LGRPID_KOREAN,\r
+ LGRPID_TRADITIONAL_CHINESE,\r
+ LGRPID_SIMPLIFIED_CHINESE,\r
+ LGRPID_THAI,\r
+ LGRPID_HEBREW,\r
+ LGRPID_ARABIC,\r
+ LGRPID_VIETNAMESE,\r
+ LGRPID_INDIC,\r
+ LGRPID_GEORGIAN,\r
+ LGRPID_ARMENIAN\r
+ };\r
+ HMODULE hKernel32;\r
+ static BOOL (WINAPI * pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROC,LGRPID,DWORD,LONG_PTR);\r
+\r
+ hKernel32 = GetModuleHandleA("kernel32.dll");\r
+ pEnumLanguageGroupLocalesA = (void*)GetProcAddress(hKernel32, "EnumLanguageGroupLocalesA");\r
+\r
+ if (!pEnumLanguageGroupLocalesA)\r
+ {\r
+ trace("EnumLanguageGroupLocalesA not available on this platform\n");\r
+ return;\r
+ }\r
+\r
+ for (i = 0; i < sizeof(groups)/sizeof(groups[0]); i++)\r
+ {\r
+ ret = pEnumLanguageGroupLocalesA(enum_proc, groups[i], 0, 0);\r
+ ok(ret, "EnumLanguageGroupLocalesA failed unexpectedly: %u\n", GetLastError());\r
+ }\r
+}\r
+\r
+static void test_ScriptGetProperties(void)\r
+{\r
+ const SCRIPT_PROPERTIES **props;\r
+ HRESULT hr;\r
+ int num;\r
+\r
+ hr = ScriptGetProperties(NULL, NULL);\r
+ ok(hr == E_INVALIDARG, "ScriptGetProperties succeeded\n");\r
+\r
+ hr = ScriptGetProperties(NULL, &num);\r
+ ok(hr == S_OK, "ScriptGetProperties failed: 0x%08x\n", hr);\r
+\r
+ hr = ScriptGetProperties(&props, NULL);\r
+ ok(hr == S_OK, "ScriptGetProperties failed: 0x%08x\n", hr);\r
+\r
+ hr = ScriptGetProperties(&props, &num);\r
+ ok(hr == S_OK, "ScriptGetProperties failed: 0x%08x\n", hr);\r
+}\r
+\r
+START_TEST(usp10)\r
+{\r
+ HWND hwnd;\r
+ HDC hdc;\r
+ LOGFONTA lf;\r
+ HFONT hfont;\r
+\r
+ unsigned short pwOutGlyphs[256];\r
+\r
+ /* We need a valid HDC to drive a lot of Script functions which requires the following *\r
+ * to set up for the tests. */\r
+ hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,\r
+ 0, 0, 0, NULL);\r
+ // FIXME do wine assert\r
+ //assert(hwnd != 0);\r
+ ShowWindow(hwnd, SW_SHOW);\r
+ UpdateWindow(hwnd);\r
+\r
+ hdc = GetDC(hwnd); /* We now have a hdc */\r
+ ok( hdc != NULL, "HDC failed to be created %p\n", hdc);\r
+\r
+ memset(&lf, 0, sizeof(HFONT));\r
+ lstrcpyA(lf.lfFaceName, "Symbol");\r
+ lf.lfHeight = 10;\r
+ lf.lfWeight = 3;\r
+ lf.lfWidth = 10;\r
+\r
+ hfont = SelectObject(hdc, CreateFontIndirectA(&lf));\r
+\r
+ test_ScriptItemIzeShapePlace(hdc,pwOutGlyphs);\r
+ test_ScriptGetCMap(hdc, pwOutGlyphs);\r
+ test_ScriptCacheGetHeight(hdc);\r
+ test_ScriptGetGlyphABCWidth(hdc);\r
+\r
+ test_ScriptGetFontProperties(hdc);\r
+ test_ScriptTextOut(hdc);\r
+ test_ScriptXtoX();\r
+ test_ScriptString(hdc);\r
+ test_ScriptStringXtoCP_CPtoX(hdc);\r
+\r
+ test_ScriptLayout();\r
+ test_digit_substitution();\r
+ test_ScriptGetProperties();\r
+\r
+ ReleaseDC(hwnd, hdc);\r
+ DestroyWindow(hwnd);\r
+}\r
--- /dev/null
+<module name="usp10_winetest" type="win32cui" installbase="bin" installname="usp10_winetest.exe" allowwarnings="true">\r
+ <include base="usp10_winetest">.</include>\r
+ <define name="__USE_W32API" />\r
+ <library>ntdll</library>\r
+ <library>gdi32</library>\r
+ <library>kernel32</library>\r
+ <library>user32</library>\r
+ <library>usp10</library>\r
+ <file>usp10.c</file>\r
+ <file>testlist.c</file>\r
+</module>\r
--- /dev/null
+/*
+ * Copyright (C) 2004 Stefan Leichter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winver.h"
+
+#define MY_LAST_ERROR -1L
+#define EXPECT_BAD_PATH__NOT_FOUND \
+ ok( (ERROR_PATH_NOT_FOUND == GetLastError()) || \
+ (ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) || \
+ (ERROR_FILE_NOT_FOUND == GetLastError()) || \
+ (ERROR_BAD_PATHNAME == GetLastError()), \
+ "Last error wrong! ERROR_RESOURCE_DATA_NOT_FOUND/ERROR_BAD_PATHNAME (98)/" \
+ "ERROR_PATH_NOT_FOUND (NT4)/ERROR_FILE_NOT_FOUND (2k3)" \
+ "expected, got 0x%08lx\n", GetLastError());
+#define EXPECT_INVALID__NOT_FOUND \
+ ok( (ERROR_PATH_NOT_FOUND == GetLastError()) || \
+ (ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) || \
+ (ERROR_FILE_NOT_FOUND == GetLastError()) || \
+ (ERROR_INVALID_PARAMETER == GetLastError()), \
+ "Last error wrong! ERROR_RESOURCE_DATA_NOT_FOUND/ERROR_INVALID_PARAMETER (98)/" \
+ "ERROR_PATH_NOT_FOUND (NT4)/ERROR_FILE_NOT_FOUND (2k3)" \
+ "expected, got 0x%08lx\n", GetLastError());
+
+static void test_info_size(void)
+{ DWORD hdl, retval;
+
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( NULL, NULL);
+ ok( !retval,
+ "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n",
+ retval);
+ EXPECT_INVALID__NOT_FOUND;
+
+ hdl = 0x55555555;
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( NULL, &hdl);
+ ok( !retval,
+ "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n",
+ retval);
+ EXPECT_INVALID__NOT_FOUND;
+ ok( hdl == 0L,
+ "Handle wrong! 0L expected, got 0x%08lx\n", hdl);
+
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( "", NULL);
+ ok( !retval,
+ "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n",
+ retval);
+ EXPECT_BAD_PATH__NOT_FOUND;
+
+ hdl = 0x55555555;
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( "", &hdl);
+ ok( !retval,
+ "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n",
+ retval);
+ EXPECT_BAD_PATH__NOT_FOUND;
+ ok( hdl == 0L,
+ "Handle wrong! 0L expected, got 0x%08lx\n", hdl);
+
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( "kernel32.dll", NULL);
+ ok( retval,
+ "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08lx\n",
+ retval);
+ ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
+ "Last error wrong! NO_ERROR/0x%08lx (NT4) expected, got 0x%08lx\n",
+ MY_LAST_ERROR, GetLastError());
+
+ hdl = 0x55555555;
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( "kernel32.dll", &hdl);
+ ok( retval,
+ "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08lx\n",
+ retval);
+ ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
+ "Last error wrong! NO_ERROR/0x%08lx (NT4) expected, got 0x%08lx\n",
+ MY_LAST_ERROR, GetLastError());
+ ok( hdl == 0L,
+ "Handle wrong! 0L expected, got 0x%08lx\n", hdl);
+
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( "notexist.dll", NULL);
+ ok( !retval,
+ "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n",
+ retval);
+ ok( (ERROR_FILE_NOT_FOUND == GetLastError()) ||
+ (ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) ||
+ (MY_LAST_ERROR == GetLastError()),
+ "Last error wrong! ERROR_FILE_NOT_FOUND/ERROR_RESOURCE_DATA_NOT_FOUND "
+ "(XP)/0x%08lx (NT4) expected, got 0x%08lx\n", MY_LAST_ERROR, GetLastError());
+}
+
+static void VersionDwordLong2String(DWORDLONG Version, LPSTR lpszVerString)
+{
+ WORD a, b, c, d;
+
+ a = (WORD)(Version >> 48);
+ b = (WORD)((Version >> 32) & 0xffff);
+ c = (WORD)((Version >> 16) & 0xffff);
+ d = (WORD)(Version & 0xffff);
+
+ sprintf(lpszVerString, "%d.%d.%d.%d", a, b, c, d);
+
+ return;
+}
+
+static void test_info(void)
+{
+ DWORD hdl, retval;
+ PVOID pVersionInfo = NULL;
+ BOOL boolret;
+ VS_FIXEDFILEINFO *pFixedVersionInfo;
+ UINT uiLength;
+ char VersionString[MAX_PATH];
+ DWORDLONG dwlVersion;
+
+ hdl = 0x55555555;
+ SetLastError(MY_LAST_ERROR);
+ retval = GetFileVersionInfoSizeA( "kernel32.dll", &hdl);
+ ok( retval,
+ "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08lx\n",
+ retval);
+ ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()),
+ "Last error wrong! NO_ERROR/0x%08lx (NT4) expected, got 0x%08lx\n",
+ MY_LAST_ERROR, GetLastError());
+ ok( hdl == 0L,
+ "Handle wrong! 0L expected, got 0x%08lx\n", hdl);
+
+ if ( retval == 0 || hdl != 0)
+ return;
+
+ pVersionInfo = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, retval );
+ ok(pVersionInfo != 0, "HeapAlloc failed\n" );
+ if (pVersionInfo == 0)
+ return;
+
+ boolret = GetFileVersionInfoA( "kernel32.dll", 0, retval, 0);
+ ok (!boolret, "GetFileVersionInfoA should have failed: GetLastError = 0x%08lx\n", GetLastError());
+ ok ((GetLastError() == ERROR_INVALID_DATA) || (GetLastError() == ERROR_BAD_PATHNAME),
+ "Last error wrong! ERROR_INVALID_DATA/ERROR_BAD_PATHNAME (ME) expected, got 0x%08lx\n",
+ GetLastError());
+
+ boolret = GetFileVersionInfoA( "kernel32.dll", 0, retval, pVersionInfo );
+ ok (boolret, "GetFileVersionInfoA failed: GetLastError = 0x%08lx\n", GetLastError());
+ if (!boolret)
+ return;
+
+ boolret = VerQueryValueA( pVersionInfo, "\\", (LPVOID *)&pFixedVersionInfo, &uiLength );
+ ok (boolret, "VerQueryValueA failed: GetLastError = 0x%08lx\n", GetLastError());
+ if (!boolret)
+ return;
+
+ dwlVersion = (((DWORDLONG)pFixedVersionInfo->dwFileVersionMS) << 32) +
+ pFixedVersionInfo->dwFileVersionLS;
+
+ VersionDwordLong2String(dwlVersion, VersionString);
+
+ trace("kernel32.dll version: %s\n", VersionString);
+
+ boolret = VerQueryValueA( pVersionInfo, "\\", (LPVOID *)&pFixedVersionInfo, 0);
+ ok (boolret, "VerQueryValue failed: GetLastError = 0x%08lx\n", GetLastError());
+}
+
+START_TEST(info)
+{
+ test_info_size();
+ test_info();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_info(void);
+extern void func_install(void);
+
+const struct test winetest_testlist[] =
+{
+ { "info", func_info },
+// { "install", func_install },
+ { 0, 0 }
+};
--- /dev/null
+<module name="version_winetest" type="win32cui" installbase="bin" installname="version_winetest.exe" allowwarnings="true">
+ <include base="version_winetest">.</include>
+ <define name="__USE_W32API" />
+ <library>ntdll</library>
+ <library>version</library>
+ <library>kernel32</library>
+ <file>testlist.c</file>
+ <file>info.c</file>
+</module>
--- /dev/null
+/* include/config.h. Generated by configure. */
+/* include/config.h.in. Generated from configure.ac by autoheader. */
+
+#define __WINE_CONFIG_H
+
+/* Specifies the compiler flag that forces a short wchar_t */
+#define CC_FLAG_SHORT_WCHAR "-fshort-wchar"
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+ systems. This function is required for `alloca.c' support on those systems.
+ */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+ */
+/* #undef HAVE_ALLOCA_H */
+
+/* Define if you have ALSA 1.x including devel headers */
+/* #undef HAVE_ALSA */
+
+/* Define to 1 if you have the <alsa/asoundlib.h> header file. */
+/* #undef HAVE_ALSA_ASOUNDLIB_H */
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+/* #undef HAVE_ARPA_INET_H */
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+/* #undef HAVE_ARPA_NAMESER_H */
+
+/* Define if you have ARTS sound server */
+/* #undef HAVE_ARTS */
+
+/* Define if the assembler keyword .size is accepted */
+/* #undef HAVE_ASM_DOT_SIZE */
+
+/* Define to 1 if you have the <audio/audiolib.h> header file. */
+/* #undef HAVE_AUDIO_AUDIOLIB_H */
+
+/* Define to 1 if you have the <audio/soundlib.h> header file. */
+/* #undef HAVE_AUDIO_SOUNDLIB_H */
+
+/* Define to 1 if you have the <capi20.h> header file. */
+/* #undef HAVE_CAPI20_H */
+
+/* Define if you have capi4linux libs and headers */
+/* #undef HAVE_CAPI4LINUX */
+
+/* Define to 1 if you have the `chsize' function. */
+#define HAVE_CHSIZE 1
+
+/* Define to 1 if you have the `clone' function. */
+/* #undef HAVE_CLONE */
+
+/* Define to 1 if you have the `connect' function. */
+/* #undef HAVE_CONNECT */
+
+/* Define if we have linux/input.h AND it contains the INPUT event API */
+/* #undef HAVE_CORRECT_LINUXINPUT_H */
+
+/* Define to 1 if you have the <cups/cups.h> header file. */
+/* #undef HAVE_CUPS_CUPS_H */
+
+/* Define to 1 if you have the <curses.h> header file. */
+/* #undef HAVE_CURSES_H */
+
+/* Define to 1 if you have the <direct.h> header file. */
+#define HAVE_DIRECT_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #undef HAVE_DLFCN_H */
+
+/* Define if you have dlopen */
+/* #undef HAVE_DLOPEN */
+
+/* Define to 1 if you have the <elf.h> header file. */
+/* #undef HAVE_ELF_H */
+
+/* Define to 1 if you have the `epoll_create' function. */
+/* #undef HAVE_EPOLL_CREATE */
+
+/* Define to 1 if you have the `ffs' function. */
+/* #undef HAVE_FFS */
+
+/* Define to 1 if you have the `finite' function. */
+#define HAVE_FINITE 1
+
+/* Define to 1 if you have the <float.h> header file. */
+#define HAVE_FLOAT_H 1
+
+/* Define to 1 if you have the <fontconfig/fontconfig.h> header file. */
+/* #undef HAVE_FONTCONFIG_FONTCONFIG_H */
+
+/* Define to 1 if you have the `fork' function. */
+/* #undef HAVE_FORK */
+
+/* Define to 1 if you have the `fpclass' function. */
+#define HAVE_FPCLASS 1
+
+/* Define if FreeType 2 is installed */
+/* #undef HAVE_FREETYPE */
+
+/* Define to 1 if you have the <freetype/freetype.h> header file. */
+/* #undef HAVE_FREETYPE_FREETYPE_H */
+
+/* Define to 1 if you have the <freetype/ftglyph.h> header file. */
+/* #undef HAVE_FREETYPE_FTGLYPH_H */
+
+/* Define to 1 if you have the <freetype/ftnames.h> header file. */
+/* #undef HAVE_FREETYPE_FTNAMES_H */
+
+/* Define to 1 if you have the <freetype/ftoutln.h> header file. */
+/* #undef HAVE_FREETYPE_FTOUTLN_H */
+
+/* Define to 1 if you have the <freetype/ftsnames.h> header file. */
+/* #undef HAVE_FREETYPE_FTSNAMES_H */
+
+/* Define if you have the <freetype/fttrigon.h> header file. */
+/* #undef HAVE_FREETYPE_FTTRIGON_H */
+
+/* Define to 1 if you have the <freetype/ftwinfnt.h> header file. */
+/* #undef HAVE_FREETYPE_FTWINFNT_H */
+
+/* Define to 1 if you have the <freetype/internal/sfnt.h> header file. */
+/* #undef HAVE_FREETYPE_INTERNAL_SFNT_H */
+
+/* Define to 1 if you have the <freetype/ttnameid.h> header file. */
+/* #undef HAVE_FREETYPE_TTNAMEID_H */
+
+/* Define to 1 if you have the <freetype/tttables.h> header file. */
+/* #undef HAVE_FREETYPE_TTTABLES_H */
+
+/* Define to 1 if the system has the type `fsblkcnt_t'. */
+/* #undef HAVE_FSBLKCNT_T */
+
+/* Define to 1 if the system has the type `fsfilcnt_t'. */
+/* #undef HAVE_FSFILCNT_T */
+
+/* Define to 1 if you have the `fstatfs' function. */
+/* #undef HAVE_FSTATFS */
+
+/* Define to 1 if you have the `fstatvfs' function. */
+/* #undef HAVE_FSTATVFS */
+
+/* Define to 1 if you have the <ft2build.h> header file. */
+/* #undef HAVE_FT2BUILD_H */
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `futimes' function. */
+/* #undef HAVE_FUTIMES */
+
+/* Define to 1 if you have the `gethostbyname' function. */
+/* #undef HAVE_GETHOSTBYNAME */
+
+/* Define to 1 if you have the `getnetbyname' function. */
+/* #undef HAVE_GETNETBYNAME */
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#define HAVE_GETOPT_H 1
+
+/* Define to 1 if you have the `getopt_long' function. */
+#define HAVE_GETOPT_LONG 1
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the `getprotobyname' function. */
+/* #undef HAVE_GETPROTOBYNAME */
+
+/* Define to 1 if you have the `getprotobynumber' function. */
+/* #undef HAVE_GETPROTOBYNUMBER */
+
+/* Define to 1 if you have the `getpwuid' function. */
+/* #undef HAVE_GETPWUID */
+
+/* Define to 1 if you have the `getservbyport' function. */
+/* #undef HAVE_GETSERVBYPORT */
+
+/* Define to 1 if you have the `gettid' function. */
+/* #undef HAVE_GETTID */
+
+/* Define to 1 if you have the `gettimeofday' function. */
+/* #undef HAVE_GETTIMEOFDAY */
+
+/* Define to 1 if you have the `getuid' function. */
+/* #undef HAVE_GETUID */
+
+/* Define to 1 if you have the <gif_lib.h> header file. */
+/* #undef HAVE_GIF_LIB_H */
+
+/* Define to 1 if you have the <GL/glext.h> header file. */
+/* #undef HAVE_GL_GLEXT_H */
+
+/* Define to 1 if you have the <GL/glx.h> header file. */
+/* #undef HAVE_GL_GLX_H */
+
+/* Define to 1 if you have the <GL/gl.h> header file. */
+/* #undef HAVE_GL_GL_H */
+
+/* Define to 1 if the ICU libraries are installed */
+/* #undef HAVE_ICU */
+
+/* Define to 1 if you have the <ieeefp.h> header file. */
+/* #undef HAVE_IEEEFP_H */
+
+/* Define to 1 if you have the `inet_aton' function. */
+/* #undef HAVE_INET_ATON */
+
+/* Define to 1 if you have the `inet_network' function. */
+/* #undef HAVE_INET_NETWORK */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <io.h> header file. */
+#define HAVE_IO_H 1
+
+/* Define if IPX should use netipx/ipx.h from libc */
+/* #undef HAVE_IPX_GNU */
+
+/* Define if IPX includes are taken from Linux kernel */
+/* #undef HAVE_IPX_LINUX */
+
+/* Define to 1 if you have the `iswalnum' function. */
+#define HAVE_ISWALNUM 1
+
+/* Define to 1 if you have the <jack/jack.h> header file. */
+/* #undef HAVE_JACK_JACK_H */
+
+/* Define to 1 if you have the <jpeglib.h> header file. */
+/* #undef HAVE_JPEGLIB_H */
+
+/* Define to 1 if you have the <lcms.h> header file. */
+/* #undef HAVE_LCMS_H */
+
+/* Define to 1 if you have the <lcms/lcms.h> header file. */
+/* #undef HAVE_LCMS_LCMS_H */
+
+/* Define if you have libaudioIO */
+/* #undef HAVE_LIBAUDIOIO */
+
+/* Define to 1 if you have the <libaudioio.h> header file. */
+/* #undef HAVE_LIBAUDIOIO_H */
+
+/* Define if you have the curses library (-lcurses) */
+/* #undef HAVE_LIBCURSES */
+
+/* Define to 1 if you have the `i386' library (-li386). */
+/* #undef HAVE_LIBI386 */
+
+/* Define if you have the ncurses library (-lncurses) */
+/* #undef HAVE_LIBNCURSES */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `ossaudio' library (-lossaudio). */
+/* #undef HAVE_LIBOSSAUDIO */
+
+/* Define to 1 if you have the `poll' library (-lpoll). */
+/* #undef HAVE_LIBPOLL */
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+/* #undef HAVE_LIBRESOLV */
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Define to 1 if you have the `w' library (-lw). */
+/* #undef HAVE_LIBW */
+
+/* Define to 1 if you have the `xpg4' library (-lxpg4). */
+/* #undef HAVE_LIBXPG4 */
+
+/* Define if you have the Xrandr library */
+/* #undef HAVE_LIBXRANDR */
+
+/* Define if you have the X Shape extension */
+/* #undef HAVE_LIBXSHAPE */
+
+/* Define if you have the Xxf86dga library version 2 */
+/* #undef HAVE_LIBXXF86DGA2 */
+
+/* Define if you have the Xxf86vm library */
+/* #undef HAVE_LIBXXF86VM */
+
+/* Define if you have the X Shm extension */
+/* #undef HAVE_LIBXXSHM */
+
+/* Define to 1 if you have the <link.h> header file. */
+/* #undef HAVE_LINK_H */
+
+/* Define if <linux/joystick.h> defines the Linux 2.2 joystick API */
+/* #undef HAVE_LINUX_22_JOYSTICK_API */
+
+/* Define to 1 if you have the <linux/capi.h> header file. */
+/* #undef HAVE_LINUX_CAPI_H */
+
+/* Define to 1 if you have the <linux/cdrom.h> header file. */
+/* #undef HAVE_LINUX_CDROM_H */
+
+/* Define to 1 if you have the <linux/compiler.h> header file. */
+/* #undef HAVE_LINUX_COMPILER_H */
+
+/* Define if Linux-style gethostbyname_r and gethostbyaddr_r are available */
+/* #undef HAVE_LINUX_GETHOSTBYNAME_R_6 */
+
+/* Define to 1 if you have the <linux/hdreg.h> header file. */
+/* #undef HAVE_LINUX_HDREG_H */
+
+/* Define to 1 if you have the <linux/input.h> header file. */
+/* #undef HAVE_LINUX_INPUT_H */
+
+/* Define to 1 if you have the <linux/ioctl.h> header file. */
+/* #undef HAVE_LINUX_IOCTL_H */
+
+/* Define to 1 if you have the <linux/joystick.h> header file. */
+/* #undef HAVE_LINUX_JOYSTICK_H */
+
+/* Define to 1 if you have the <linux/major.h> header file. */
+/* #undef HAVE_LINUX_MAJOR_H */
+
+/* Define to 1 if you have the <linux/param.h> header file. */
+/* #undef HAVE_LINUX_PARAM_H */
+
+/* Define to 1 if you have the <linux/serial.h> header file. */
+/* #undef HAVE_LINUX_SERIAL_H */
+
+/* Define to 1 if you have the <linux/ucdrom.h> header file. */
+/* #undef HAVE_LINUX_UCDROM_H */
+
+/* Define to 1 if the system has the type `long long'. */
+#define HAVE_LONG_LONG 1
+
+/* Define to 1 if you have the `lstat' function. */
+/* #undef HAVE_LSTAT */
+
+/* Define to 1 if you have the <machine/cpu.h> header file. */
+/* #undef HAVE_MACHINE_CPU_H */
+
+/* Define to 1 if you have the <machine/soundcard.h> header file. */
+/* #undef HAVE_MACHINE_SOUNDCARD_H */
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mmap' function. */
+/* #undef HAVE_MMAP */
+
+/* Define to 1 if you have the <mntent.h> header file. */
+/* #undef HAVE_MNTENT_H */
+
+/* Define to 1 if the system has the type `mode_t'. */
+#define HAVE_MODE_T 1
+
+/* Define if you have NAS including devel headers */
+/* #undef HAVE_NAS */
+
+/* Define to 1 if you have the <ncurses.h> header file. */
+/* #undef HAVE_NCURSES_H */
+
+/* Define to 1 if you have the <netdb.h> header file. */
+/* #undef HAVE_NETDB_H */
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+/* #undef HAVE_NETINET_IN_H */
+
+/* Define to 1 if you have the <netinet/in_systm.h> header file. */
+/* #undef HAVE_NETINET_IN_SYSTM_H */
+
+/* Define to 1 if you have the <netinet/tcp_fsm.h> header file. */
+/* #undef HAVE_NETINET_TCP_FSM_H */
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+/* #undef HAVE_NETINET_TCP_H */
+
+/* Define to 1 if you have the <net/if_arp.h> header file. */
+/* #undef HAVE_NET_IF_ARP_H */
+
+/* Define to 1 if you have the <net/if_dl.h> header file. */
+/* #undef HAVE_NET_IF_DL_H */
+
+/* Define to 1 if you have the <net/if.h> header file. */
+/* #undef HAVE_NET_IF_H */
+
+/* Define to 1 if you have the <net/if_types.h> header file. */
+/* #undef HAVE_NET_IF_TYPES_H */
+
+/* Define to 1 if you have the <net/route.h> header file. */
+/* #undef HAVE_NET_ROUTE_H */
+
+/* Define to 1 if the system has the type `off_t'. */
+#define HAVE_OFF_T 1
+
+/* Define if OpenGL is present on the system */
+/* #undef HAVE_OPENGL */
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+/* #undef HAVE_OPENSSL_SSL_H */
+
+/* Define if you have the Open Sound system */
+/* #undef HAVE_OSS */
+
+/* Define if you have the Open Sound system (MIDI interface) */
+/* #undef HAVE_OSS_MIDI */
+
+/* Define to 1 if you have the `pclose' function. */
+#define HAVE_PCLOSE 1
+
+/* Define to 1 if the system has the type `pid_t'. */
+#define HAVE_PID_T 1
+
+/* Define to 1 if you have the `popen' function. */
+#define HAVE_POPEN 1
+
+/* Define if we can use ppdev.h for parallel port access */
+/* #undef HAVE_PPDEV */
+
+/* Define to 1 if you have the `pread' function. */
+/* #undef HAVE_PREAD */
+
+/* Define to 1 if you have the <process.h> header file. */
+#define HAVE_PROCESS_H 1
+
+/* Define to 1 if you have the `pthread_getattr_np' function. */
+/* #undef HAVE_PTHREAD_GETATTR_NP */
+
+/* Define to 1 if you have the `pthread_get_stackaddr_np' function. */
+/* #undef HAVE_PTHREAD_GET_STACKADDR_NP */
+
+/* Define to 1 if you have the `pthread_get_stacksize_np' function. */
+/* #undef HAVE_PTHREAD_GET_STACKSIZE_NP */
+
+/* Define to 1 if you have the <pthread.h> header file. */
+/* #undef HAVE_PTHREAD_H */
+
+/* Define to 1 if the system has the type `pthread_rwlockattr_t'. */
+/* #undef HAVE_PTHREAD_RWLOCKATTR_T */
+
+/* Define to 1 if the system has the type `pthread_rwlock_t'. */
+/* #undef HAVE_PTHREAD_RWLOCK_T */
+
+/* Define to 1 if you have the <pwd.h> header file. */
+/* #undef HAVE_PWD_H */
+
+/* Define to 1 if you have the `pwrite' function. */
+/* #undef HAVE_PWRITE */
+
+/* Define to 1 if you have the `readlink' function. */
+/* #undef HAVE_READLINK */
+
+/* Define to 1 if you have the <regex.h> header file. */
+/* #undef HAVE_REGEX_H */
+
+/* Define to 1 if you have the <resolv.h> header file. */
+/* #undef HAVE_RESOLV_H */
+
+/* Define to 1 if you have the `rfork' function. */
+/* #undef HAVE_RFORK */
+
+/* Define if we have SANE development environment */
+/* #undef HAVE_SANE */
+
+/* Define to 1 if you have the <sched.h> header file. */
+/* #undef HAVE_SCHED_H */
+
+/* Define to 1 if you have the `sched_yield' function. */
+/* #undef HAVE_SCHED_YIELD */
+
+/* Define to 1 if you have the <scsi/scsi.h> header file. */
+/* #undef HAVE_SCSI_SCSI_H */
+
+/* Define to 1 if you have the <scsi/scsi_ioctl.h> header file. */
+/* #undef HAVE_SCSI_SCSI_IOCTL_H */
+
+/* Define to 1 if you have the <scsi/sg.h> header file. */
+/* #undef HAVE_SCSI_SG_H */
+
+/* Define to 1 if you have the `select' function. */
+/* #undef HAVE_SELECT */
+
+/* Define to 1 if you have the `sendmsg' function. */
+/* #undef HAVE_SENDMSG */
+
+/* Define to 1 if you have the `settimeofday' function. */
+/* #undef HAVE_SETTIMEOFDAY */
+
+/* Define if sigaddset is supported */
+/* #undef HAVE_SIGADDSET */
+
+/* Define to 1 if you have the `sigaltstack' function. */
+/* #undef HAVE_SIGALTSTACK */
+
+/* Define to 1 if `si_fd' is member of `siginfo_t'. */
+/* #undef HAVE_SIGINFO_T_SI_FD */
+
+/* Define to 1 if you have the `sigprocmask' function. */
+/* #undef HAVE_SIGPROCMASK */
+
+/* Define to 1 if you have the sigsetjmp (and siglongjmp) function */
+/* #undef HAVE_SIGSETJMP */
+
+/* Define to 1 if the system has the type `sigset_t'. */
+/* #undef HAVE_SIGSET_T */
+
+/* Define to 1 if the system has the type `size_t'. */
+#define HAVE_SIZE_T 1
+
+/* Define to 1 if you have the `snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if you have the <soundcard.h> header file. */
+/* #undef HAVE_SOUNDCARD_H */
+
+/* Define to 1 if you have the `spawnvp' function. */
+#define HAVE_SPAWNVP 1
+
+/* Define to 1 if the system has the type `ssize_t'. */
+#define HAVE_SSIZE_T 1
+
+/* Define to 1 if you have the `statfs' function. */
+/* #undef HAVE_STATFS */
+
+/* Define to 1 if you have the `statvfs' function. */
+/* #undef HAVE_STATVFS */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#define HAVE_STRNCASECMP 1
+
+/* Define to 1 if `msg_accrights' is member of `struct msghdr'. */
+/* #undef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS */
+
+/* Define to 1 if `name' is member of `struct option'. */
+#define HAVE_STRUCT_OPTION_NAME 1
+
+/* Define to 1 if `sa_len' is member of `struct sockaddr'. */
+/* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */
+
+/* Define to 1 if `sun_len' is member of `struct sockaddr_un'. */
+/* #undef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
+
+/* Define to 1 if `f_bavail' is member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_BAVAIL */
+
+/* Define to 1 if `f_bfree' is member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_BFREE */
+
+/* Define to 1 if `f_favail' is member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_FAVAIL */
+
+/* Define to 1 if `f_ffree' is member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_FFREE */
+
+/* Define to 1 if `f_frsize' is member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_FRSIZE */
+
+/* Define to 1 if `f_namelen' is member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_NAMELEN */
+
+/* Define to 1 if `f_blocks' is member of `struct statvfs'. */
+/* #undef HAVE_STRUCT_STATVFS_F_BLOCKS */
+
+/* Define to 1 if `st_blocks' is member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_BLOCKS */
+
+/* Define to 1 if you have the <syscall.h> header file. */
+/* #undef HAVE_SYSCALL_H */
+
+/* Define to 1 if you have the <sys/asoundlib.h> header file. */
+/* #undef HAVE_SYS_ASOUNDLIB_H */
+
+/* Define to 1 if you have the <sys/cdio.h> header file. */
+/* #undef HAVE_SYS_CDIO_H */
+
+/* Define to 1 if you have the <sys/elf32.h> header file. */
+/* #undef HAVE_SYS_ELF32_H */
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+/* #undef HAVE_SYS_EPOLL_H */
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+/* #undef HAVE_SYS_ERRNO_H */
+
+/* Define to 1 if you have the <sys/exec_elf.h> header file. */
+/* #undef HAVE_SYS_EXEC_ELF_H */
+
+/* Define to 1 if you have the <sys/filio.h> header file. */
+/* #undef HAVE_SYS_FILIO_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+/* #undef HAVE_SYS_IOCTL_H */
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+/* #undef HAVE_SYS_IPC_H */
+
+/* Define to 1 if you have the <sys/link.h> header file. */
+/* #undef HAVE_SYS_LINK_H */
+
+/* Define to 1 if you have the <sys/lwp.h> header file. */
+/* #undef HAVE_SYS_LWP_H */
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+/* #undef HAVE_SYS_MMAN_H */
+
+/* Define to 1 if you have the <sys/modem.h> header file. */
+/* #undef HAVE_SYS_MODEM_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+/* #undef HAVE_SYS_MOUNT_H */
+
+/* Define to 1 if you have the <sys/msg.h> header file. */
+/* #undef HAVE_SYS_MSG_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #undef HAVE_SYS_POLL_H */
+
+/* Define to 1 if you have the <sys/ptrace.h> header file. */
+/* #undef HAVE_SYS_PTRACE_H */
+
+/* Define to 1 if you have the <sys/reg.h> header file. */
+/* #undef HAVE_SYS_REG_H */
+
+/* Define to 1 if you have the <sys/scsiio.h> header file. */
+/* #undef HAVE_SYS_SCSIIO_H */
+
+/* Define to 1 if you have the <sys/shm.h> header file. */
+/* #undef HAVE_SYS_SHM_H */
+
+/* Define to 1 if you have the <sys/signal.h> header file. */
+/* #undef HAVE_SYS_SIGNAL_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #undef HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+/* #undef HAVE_SYS_SOCKIO_H */
+
+/* Define to 1 if you have the <sys/soundcard.h> header file. */
+/* #undef HAVE_SYS_SOUNDCARD_H */
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+/* #undef HAVE_SYS_STATVFS_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/strtio.h> header file. */
+/* #undef HAVE_SYS_STRTIO_H */
+
+/* Define to 1 if you have the <sys/syscall.h> header file. */
+/* #undef HAVE_SYS_SYSCALL_H */
+
+/* Define to 1 if you have the <sys/sysctl.h> header file. */
+/* #undef HAVE_SYS_SYSCTL_H */
+
+/* Define to 1 if you have the <sys/times.h> header file. */
+/* #undef HAVE_SYS_TIMES_H */
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+/* #undef HAVE_SYS_UIO_H */
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+/* #undef HAVE_SYS_UN_H */
+
+/* Define to 1 if you have the <sys/user.h> header file. */
+/* #undef HAVE_SYS_USER_H */
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define to 1 if you have the <sys/vm86.h> header file. */
+/* #undef HAVE_SYS_VM86_H */
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+/* #undef HAVE_SYS_WAIT_H */
+
+/* Define to 1 if you have the `tcgetattr' function. */
+/* #undef HAVE_TCGETATTR */
+
+/* Define to 1 if you have the <termios.h> header file. */
+/* #undef HAVE_TERMIOS_H */
+
+/* Define to 1 if you have the `timegm' function. */
+/* #undef HAVE_TIMEGM */
+
+/* Define to 1 if you have the <ucontext.h> header file. */
+/* #undef HAVE_UCONTEXT_H */
+
+/* Define to 1 if you have the <unicode/ubidi.h> header file. */
+/* #undef HAVE_UNICODE_UBIDI_H */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `usleep' function. */
+/* #undef HAVE_USLEEP */
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the <valgrind/memcheck.h> header file. */
+/* #undef HAVE_VALGRIND_MEMCHECK_H */
+
+/* Define if we have va_copy */
+#define HAVE_VA_COPY 1
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#define HAVE_VSNPRINTF 1
+
+/* Define to 1 if you have the `wait4' function. */
+/* #undef HAVE_WAIT4 */
+
+/* Define to 1 if you have the `waitpid' function. */
+/* #undef HAVE_WAITPID */
+
+/* Define to 1 if you have the <X11/extensions/shape.h> header file. */
+/* #undef HAVE_X11_EXTENSIONS_SHAPE_H */
+
+/* Define to 1 if you have the <X11/extensions/xf86dga.h> header file. */
+/* #undef HAVE_X11_EXTENSIONS_XF86DGA_H */
+
+/* Define to 1 if you have the <X11/extensions/xf86vmode.h> header file. */
+/* #undef HAVE_X11_EXTENSIONS_XF86VMODE_H */
+
+/* Define to 1 if you have the <X11/extensions/XInput.h> header file. */
+/* #undef HAVE_X11_EXTENSIONS_XINPUT_H */
+
+/* Define to 1 if you have the <X11/extensions/Xrandr.h> header file. */
+/* #undef HAVE_X11_EXTENSIONS_XRANDR_H */
+
+/* Define to 1 if you have the <X11/extensions/Xrender.h> header file. */
+/* #undef HAVE_X11_EXTENSIONS_XRENDER_H */
+
+/* Define to 1 if you have the <X11/extensions/XShm.h> header file. */
+/* #undef HAVE_X11_EXTENSIONS_XSHM_H */
+
+/* Define to 1 if you have the <X11/XKBlib.h> header file. */
+/* #undef HAVE_X11_XKBLIB_H */
+
+/* Define to 1 if you have the <X11/Xlib.h> header file. */
+/* #undef HAVE_X11_XLIB_H */
+
+/* Define to 1 if you have the <X11/Xutil.h> header file. */
+/* #undef HAVE_X11_XUTIL_H */
+
+/* Define if you have the XKB extension */
+/* #undef HAVE_XKB */
+
+/* Define if Xrender has the XRenderSetPictureTransform function */
+/* #undef HAVE_XRENDERSETPICTURETRANSFORM */
+
+/* Define to 1 if you have the `_lwp_create' function. */
+/* #undef HAVE__LWP_CREATE */
+
+/* Define to 1 if you have the `_lwp_self' function. */
+/* #undef HAVE__LWP_SELF */
+
+/* Define to 1 if you have the `_pclose' function. */
+#define HAVE__PCLOSE 1
+
+/* Define to 1 if you have the `_popen' function. */
+#define HAVE__POPEN 1
+
+/* Define to 1 if you have the `_snprintf' function. */
+#define HAVE__SNPRINTF 1
+
+/* Define to 1 if you have the `_spawnvp' function. */
+#define HAVE__SPAWNVP 1
+
+/* Define to 1 if you have the `_stricmp' function. */
+#define HAVE__STRICMP 1
+
+/* Define to 1 if you have the `_strnicmp' function. */
+#define HAVE__STRNICMP 1
+
+/* Define to 1 if you have the `_vsnprintf' function. */
+#define HAVE__VSNPRINTF 1
+
+/* Define if we have __va_copy */
+#define HAVE___VA_COPY 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "wine-devel@winehq.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "Wine"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "Wine 20050211"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "wine"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "20050211"
+
+/* Define to the soname of the libcapi20 library. */
+/* #undef SONAME_LIBCAPI20 */
+
+/* Define to the soname of the libcrypto library. */
+/* #undef SONAME_LIBCRYPTO */
+
+/* Define to the soname of the libcups library. */
+/* #undef SONAME_LIBCUPS */
+
+/* Define to the soname of the libcurses library. */
+/* #undef SONAME_LIBCURSES */
+
+/* Define to the soname of the libfontconfig library. */
+/* #undef SONAME_LIBFONTCONFIG */
+
+/* Define to the soname of the libfreetype library. */
+/* #undef SONAME_LIBFREETYPE */
+
+/* Define to the soname of the libgif library. */
+/* #undef SONAME_LIBGIF */
+
+/* Define to the soname of the libGL library. */
+/* #undef SONAME_LIBGL */
+
+/* Define to the soname of the libjack library. */
+/* #undef SONAME_LIBJACK */
+
+/* Define to the soname of the libjpeg library. */
+/* #undef SONAME_LIBJPEG */
+
+/* Define to the soname of the liblcms library. */
+/* #undef SONAME_LIBLCMS */
+
+/* Define to the soname of the libncurses library. */
+/* #undef SONAME_LIBNCURSES */
+
+/* Define to the soname of the libssl library. */
+/* #undef SONAME_LIBSSL */
+
+/* Define to the soname of the libtxc_dxtn library. */
+/* #undef SONAME_LIBTXC_DXTN */
+
+/* Define to the soname of the libungif library. */
+/* #undef SONAME_LIBUNGIF */
+
+/* Define to the soname of the libX11 library. */
+/* #undef SONAME_LIBX11 */
+
+/* Define to the soname of the libXext library. */
+/* #undef SONAME_LIBXEXT */
+
+/* Define to the soname of the libXi library. */
+/* #undef SONAME_LIBXI */
+
+/* Define to the soname of the libXrandr library. */
+/* #undef SONAME_LIBXRANDR */
+
+/* Define to the soname of the libXrender library. */
+/* #undef SONAME_LIBXRENDER */
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define if the struct statfs is defined by <sys/mount.h> */
+/* #undef STATFS_DEFINED_BY_SYS_MOUNT */
+
+/* Define if the struct statfs is defined by <sys/statfs.h> */
+/* #undef STATFS_DEFINED_BY_SYS_STATFS */
+
+/* Define if the struct statfs is defined by <sys/vfs.h> */
+/* #undef STATFS_DEFINED_BY_SYS_VFS */
+
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if the X Window System is missing or not being used. */
+#define X_DISPLAY_MISSING 1
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+ `char[]'. */
+#define YYTEXT_POINTER 1
+
+/* Set this to 64 to enable 64-bit file support on Linux */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to a macro to generate an assembly function directive */
+#define __ASM_FUNC(name) ".def " __ASM_NAME(name) "; .scl 2; .type 32; .endef"
+
+/* Define to a macro to generate an assembly name from a C symbol */
+#define __ASM_NAME(name) "_" name
+
+/* Define to the assembler keyword used to specify a word value */
+#define __ASM_SHORT ".short"
+
+/* Define to the assembler keyword used to specify an ASCII string */
+#define __ASM_STRING ".string"
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
--- /dev/null
+/*
+ * Resources for the binary we distribute to testers
+ *
+ * Copyright 2004 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "winetest.rc"
+
+WINE_BUILD STRINGRES "build.id"
+BUILD_INFO STRINGRES "build.nfo"
+TESTS_URL STRINGRES "tests.url"
--- /dev/null
+/*
+ * GUI support
+ *
+ * Copyright 2004 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <windows.h>
+#include <commctrl.h>
+
+#include "resource.h"
+#include "winetest.h"
+
+/* Event object to signal successful window creation to main thread.
+ */
+static HANDLE initEvent;
+
+/* Dialog handle
+ */
+static HWND dialog;
+
+/* Progress data for the text* functions and for scaling.
+ */
+static unsigned int progressMax, progressCurr;
+static double progressScale;
+
+/* Progress group counter for the gui* functions.
+ */
+static int progressGroup;
+
+static WNDPROC DefEditProc;
+
+static int
+MBdefault (int uType)
+{
+ static const int matrix[][4] = {{IDOK, 0, 0, 0},
+ {IDOK, IDCANCEL, 0, 0},
+ {IDABORT, IDRETRY, IDIGNORE, 0},
+ {IDYES, IDNO, IDCANCEL, 0},
+ {IDYES, IDNO, 0, 0},
+ {IDRETRY, IDCANCEL, 0, 0}};
+ int type = uType & MB_TYPEMASK;
+ int def = (uType & MB_DEFMASK) / MB_DEFBUTTON2;
+
+ return matrix[type][def];
+}
+
+/* report (R_STATUS, fmt, ...) */
+static int
+textStatus (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ fputs (str, stderr);
+ fputc ('\n', stderr);
+ free (str);
+ return 0;
+}
+
+static int
+guiStatus (va_list ap)
+{
+ size_t len;
+ char *str = vstrmake (&len, ap);
+
+ if (len > 128) str[129] = 0;
+ SetDlgItemText (dialog, IDC_SB, str);
+ free (str);
+ return 0;
+}
+
+/* report (R_PROGRESS, barnum, steps) */
+static int
+textProgress (va_list ap)
+{
+ progressGroup = va_arg (ap, int);
+ progressMax = va_arg (ap, int);
+ progressCurr = 0;
+ return 0;
+}
+
+static int
+guiProgress (va_list ap)
+{
+ unsigned int max;
+ HWND pb;
+
+ progressGroup = va_arg (ap, int);
+ progressMax = max = va_arg (ap, int);
+ progressCurr = 0;
+ if (max > 0xffff) {
+ progressScale = (double)0xffff / max;
+ max = 0xffff;
+ }
+ else progressScale = 1;
+ pb = GetDlgItem (dialog, IDC_PB0 + progressGroup * 2);
+ SendMessage (pb, PBM_SETRANGE, 0, MAKELPARAM (0, max));
+ SendMessage (pb, PBM_SETSTEP, (WPARAM)1, 0);
+ return 0;
+}
+
+/* report (R_STEP, fmt, ...) */
+static int
+textStep (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ progressCurr++;
+ fputs (str, stderr);
+ fprintf (stderr, " (%d of %d)\n", progressCurr, progressMax);
+ free (str);
+ return 0;
+}
+
+static int
+guiStep (va_list ap)
+{
+ const int pgID = IDC_ST0 + progressGroup * 2;
+ char *str = vstrmake (NULL, ap);
+
+ progressCurr++;
+ SetDlgItemText (dialog, pgID, str);
+ SendDlgItemMessage (dialog, pgID+1, PBM_SETPOS,
+ (WPARAM)(progressScale * progressCurr), 0);
+ free (str);
+ return 0;
+}
+
+/* report (R_DELTA, inc, fmt, ...) */
+static int
+textDelta (va_list ap)
+{
+ const int inc = va_arg (ap, int);
+ char *str = vstrmake (NULL, ap);
+
+ progressCurr += inc;
+ fputs (str, stderr);
+ fprintf (stderr, " (%d of %d)\n", progressCurr, progressMax);
+ free (str);
+ return 0;
+}
+
+static int
+guiDelta (va_list ap)
+{
+ const int inc = va_arg (ap, int);
+ const int pgID = IDC_ST0 + progressGroup * 2;
+ char *str = vstrmake (NULL, ap);
+
+ progressCurr += inc;
+ SetDlgItemText (dialog, pgID, str);
+ SendDlgItemMessage (dialog, pgID+1, PBM_SETPOS,
+ (WPARAM)(progressScale * progressCurr), 0);
+ free (str);
+ return 0;
+}
+
+/* report (R_TAG) */
+static int
+textTag (va_list ap)
+{
+ fputs ("Tag: ", stderr);
+ fputs (tag, stderr);
+ fputc ('\n', stderr);
+ return 0;
+}
+
+static int
+guiTag (va_list ap)
+{
+ SetDlgItemText (dialog, IDC_TAG, tag);
+ return 0;
+}
+
+/* report (R_DIR, fmt, ...) */
+static int
+textDir (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ fputs ("Temporary directory: ", stderr);
+ fputs (str, stderr);
+ fputc ('\n', stderr);
+ free (str);
+ return 0;
+}
+
+static int
+guiDir (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ SetDlgItemText (dialog, IDC_DIR, str);
+ free (str);
+ return 0;
+}
+
+/* report (R_OUT, fmt, ...) */
+static int
+textOut (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ fputs ("Log file: ", stderr);
+ fputs (str, stderr);
+ fputc ('\n', stderr);
+ free (str);
+ return 0;
+}
+
+static int
+guiOut (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ SetDlgItemText (dialog, IDC_OUT, str);
+ free (str);
+ return 0;
+}
+
+/* report (R_WARNING, fmt, ...) */
+static int
+textWarning (va_list ap)
+{
+ fputs ("Warning: ", stderr);
+ textStatus (ap);
+ return 0;
+}
+
+static int
+guiWarning (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ MessageBox (dialog, str, "Warning", MB_ICONWARNING | MB_OK);
+ free (str);
+ return 0;
+}
+
+/* report (R_ERROR, fmt, ...) */
+static int
+textError (va_list ap)
+{
+ fputs ("Error: ", stderr);
+ textStatus (ap);
+ return 0;
+}
+
+static int
+guiError (va_list ap)
+{
+ char *str = vstrmake (NULL, ap);
+
+ MessageBox (dialog, str, "Error", MB_ICONERROR | MB_OK);
+ free (str);
+ return 0;
+}
+
+/* report (R_FATAL, fmt, ...) */
+static int
+textFatal (va_list ap)
+{
+ textError (ap);
+ exit (1);
+}
+
+static int
+guiFatal (va_list ap)
+{
+ guiError (ap);
+ exit (1);
+}
+
+/* report (R_ASK, type, fmt, ...) */
+static int
+textAsk (va_list ap)
+{
+ int uType = va_arg (ap, int);
+ int ret = MBdefault (uType);
+ char *str = vstrmake (NULL, ap);
+
+ fprintf (stderr, "Question of type %d: %s\n"
+ "Returning default: %d\n", uType, str, ret);
+ free (str);
+ return ret;
+}
+
+static int
+guiAsk (va_list ap)
+{
+ int uType = va_arg (ap, int);
+ char *str = vstrmake (NULL, ap);
+ int ret = MessageBox (dialog, str, "Question",
+ MB_ICONQUESTION | uType);
+
+ free (str);
+ return ret;
+}
+
+static BOOL CALLBACK
+EditTagProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_CHAR:
+ if (wParam == 8) break; /* backspace is OK */
+ if (GetWindowTextLengthA (hwnd) == MAXTAGLEN ||
+ !goodtagchar (wParam)) return TRUE;
+ break;
+ }
+ return CallWindowProcA (DefEditProc, hwnd, msg, wParam, lParam);
+}
+
+static BOOL CALLBACK
+AskTagProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int len;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ DefEditProc = (WNDPROC)SetWindowLongPtr
+ (GetDlgItem (hwnd, IDC_TAG), GWLP_WNDPROC, (LONG_PTR)EditTagProc);
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD (wParam)) {
+ case IDOK:
+ len = GetWindowTextLengthA (GetDlgItem (hwnd, IDC_TAG));
+ tag = xmalloc (len+1);
+ GetDlgItemTextA (hwnd, IDC_TAG, tag, len+1);
+ EndDialog (hwnd, IDOK);
+ return TRUE;
+ case IDABORT:
+ EndDialog (hwnd, IDABORT);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+int
+guiAskTag (void)
+{
+ return DialogBox (GetModuleHandle (NULL),
+ MAKEINTRESOURCE (IDD_TAG),
+ dialog, AskTagProc);
+}
+
+/* Quiet functions */
+static int
+qNoOp (va_list ap)
+{
+ return 0;
+}
+
+static int
+qFatal (va_list ap)
+{
+ exit (1);
+}
+
+static int
+qAsk (va_list ap)
+{
+ return MBdefault (va_arg (ap, int));
+}
+
+static BOOL CALLBACK
+AboutProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_COMMAND:
+ switch (LOWORD (wParam)) {
+ case IDCANCEL:
+ EndDialog (hwnd, IDCANCEL);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static BOOL CALLBACK
+DlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ SendMessage (hwnd, WM_SETICON, ICON_SMALL,
+ (LPARAM)LoadIcon (GetModuleHandle (NULL),
+ MAKEINTRESOURCE (IDI_WINE)));
+ SendMessage (hwnd, WM_SETICON, ICON_BIG,
+ (LPARAM)LoadIcon (GetModuleHandle (NULL),
+ MAKEINTRESOURCE (IDI_WINE)));
+ dialog = hwnd;
+ if (!SetEvent (initEvent)) {
+ report (R_STATUS, "Can't signal main thread: %d",
+ GetLastError ());
+ EndDialog (hwnd, 2);
+ }
+ return TRUE;
+ case WM_CLOSE:
+ EndDialog (hwnd, 3);
+ return TRUE;
+ case WM_COMMAND:
+ switch (LOWORD (wParam)) {
+ case IDHELP:
+ DialogBox (GetModuleHandle (NULL),
+ MAKEINTRESOURCE (IDD_ABOUT), hwnd, AboutProc);
+ return TRUE;
+ case IDABORT:
+ report (R_WARNING, "Not implemented");
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static DWORD WINAPI
+DlgThreadProc (LPVOID param)
+{
+ int ret;
+
+ InitCommonControls ();
+ ret = DialogBox (GetModuleHandle (NULL),
+ MAKEINTRESOURCE (IDD_STATUS),
+ NULL, DlgProc);
+ switch (ret) {
+ case 0:
+ report (R_WARNING, "Invalid parent handle");
+ break;
+ case 1:
+ report (R_WARNING, "DialogBox failed: %d",
+ GetLastError ());
+ break;
+ case 3:
+ exit (0);
+ default:
+ report (R_STATUS, "Dialog exited: %d", ret);
+ }
+ return 0;
+}
+
+int
+report (enum report_type t, ...)
+{
+ typedef int r_fun_t (va_list);
+
+ va_list ap;
+ int ret = 0;
+ static r_fun_t * const text_funcs[] =
+ {textStatus, textProgress, textStep, textDelta,
+ textTag, textDir, textOut,
+ textWarning, textError, textFatal, textAsk};
+ static r_fun_t * const GUI_funcs[] =
+ {guiStatus, guiProgress, guiStep, guiDelta,
+ guiTag, guiDir, guiOut,
+ guiWarning, guiError, guiFatal, guiAsk};
+ static r_fun_t * const quiet_funcs[] =
+ {qNoOp, qNoOp, qNoOp, qNoOp,
+ qNoOp, qNoOp, qNoOp,
+ qNoOp, qNoOp, qFatal, qAsk};
+ static r_fun_t * const * funcs = NULL;
+
+ switch (t) {
+ case R_TEXTMODE:
+ funcs = text_funcs;
+ return 0;
+ case R_QUIET:
+ funcs = quiet_funcs;
+ return 0;
+ default:
+ break;
+ }
+
+ if (!funcs) {
+ HANDLE DlgThread;
+ DWORD DlgThreadID;
+
+ funcs = text_funcs;
+ initEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ if (!initEvent)
+ report (R_STATUS, "Can't create event object: %d",
+ GetLastError ());
+ else {
+ DlgThread = CreateThread (NULL, 0, DlgThreadProc,
+ NULL, 0, &DlgThreadID);
+ if (!DlgThread)
+ report (R_STATUS, "Can't create GUI thread: %d",
+ GetLastError ());
+ else {
+ DWORD ret = WaitForSingleObject (initEvent, INFINITE);
+ switch (ret) {
+ case WAIT_OBJECT_0:
+ funcs = GUI_funcs;
+ break;
+ case WAIT_TIMEOUT:
+ report (R_STATUS, "GUI creation timed out");
+ break;
+ case WAIT_FAILED:
+ report (R_STATUS, "Wait for GUI failed: %d",
+ GetLastError ());
+ break;
+ default:
+ report (R_STATUS, "Wait returned %d",
+ ret);
+ break;
+ }
+ }
+ }
+ }
+
+ va_start (ap, t);
+ if (t < sizeof text_funcs / sizeof text_funcs[0] &&
+ t < sizeof GUI_funcs / sizeof GUI_funcs[0]) ret = funcs[t](ap);
+ else report (R_WARNING, "unimplemented report type: %d", t);
+ va_end (ap);
+ return ret;
+}
--- /dev/null
+/*
+ * Wine Conformance Test EXE
+ *
+ * Copyright 2003, 2004 Jakob Eriksson (for Solid Form Sweden AB)
+ * Copyright 2003 Dimitrie O. Paun
+ * Copyright 2003 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * This program is dedicated to Anna Lindh,
+ * Swedish Minister of Foreign Affairs.
+ * Anna was murdered September 11, 2003.
+ *
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <windows.h>
+
+#include "winetest.h"
+#include "resource.h"
+
+struct wine_test
+{
+ char *name;
+ int resource;
+ int subtest_count;
+ char **subtests;
+ char *exename;
+};
+
+struct rev_info
+{
+ const char* file;
+ const char* rev;
+};
+
+char *tag = NULL;
+static struct wine_test *wine_tests;
+static struct rev_info *rev_infos = NULL;
+static const char whitespace[] = " \t\r\n";
+
+static int running_under_wine (void)
+{
+ HMODULE module = GetModuleHandleA("ntdll.dll");
+
+ if (!module) return 0;
+ return (GetProcAddress(module, "wine_server_call") != NULL);
+}
+
+static int running_on_visible_desktop (void)
+{
+ HWND desktop;
+ HMODULE huser32 = GetModuleHandle("user32.dll");
+ FARPROC pGetProcessWindowStation = GetProcAddress(huser32, "GetProcessWindowStation");
+ FARPROC pGetUserObjectInformationA = GetProcAddress(huser32, "GetUserObjectInformationA");
+
+ desktop = GetDesktopWindow();
+ if (!GetWindowLongPtrW(desktop, GWLP_WNDPROC)) /* Win9x */
+ return IsWindowVisible(desktop);
+
+ if (pGetProcessWindowStation && pGetUserObjectInformationA)
+ {
+ DWORD len;
+ HWINSTA wstation;
+ USEROBJECTFLAGS uoflags;
+
+ wstation = (HWINSTA)pGetProcessWindowStation();
+ assert(pGetUserObjectInformationA(wstation, UOI_FLAGS, &uoflags, sizeof(uoflags), &len));
+ return (uoflags.dwFlags & WSF_VISIBLE) != 0;
+ }
+ return IsWindowVisible(desktop);
+}
+
+static void print_version (void)
+{
+ OSVERSIONINFOEX ver;
+ BOOL ext;
+
+ ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ if (!(ext = GetVersionEx ((OSVERSIONINFO *) &ver)))
+ {
+ ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (!GetVersionEx ((OSVERSIONINFO *) &ver))
+ report (R_FATAL, "Can't get OS version.");
+ }
+
+ xprintf (" bRunningUnderWine=%d\n", running_under_wine ());
+ xprintf (" bRunningOnVisibleDesktop=%d\n", running_on_visible_desktop ());
+ xprintf (" dwMajorVersion=%ld\n dwMinorVersion=%ld\n"
+ " dwBuildNumber=%ld\n PlatformId=%ld\n szCSDVersion=%s\n",
+ ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
+ ver.dwPlatformId, ver.szCSDVersion);
+
+ if (!ext) return;
+
+ xprintf (" wServicePackMajor=%d\n wServicePackMinor=%d\n"
+ " wSuiteMask=%d\n wProductType=%d\n wReserved=%d\n",
+ ver.wServicePackMajor, ver.wServicePackMinor, ver.wSuiteMask,
+ ver.wProductType, ver.wReserved);
+}
+
+static inline int is_dot_dir(const char* x)
+{
+ return ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))));
+}
+
+static void remove_dir (const char *dir)
+{
+ HANDLE hFind;
+ WIN32_FIND_DATA wfd;
+ char path[MAX_PATH];
+ size_t dirlen = strlen (dir);
+
+ /* Make sure the directory exists before going further */
+ memcpy (path, dir, dirlen);
+ strcpy (path + dirlen++, "\\*");
+ hFind = FindFirstFile (path, &wfd);
+ if (hFind == INVALID_HANDLE_VALUE) return;
+
+ do {
+ char *lp = wfd.cFileName;
+
+ if (!lp[0]) lp = wfd.cAlternateFileName; /* ? FIXME not (!lp) ? */
+ if (is_dot_dir (lp)) continue;
+ strcpy (path + dirlen, lp);
+ if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
+ remove_dir(path);
+ else if (!DeleteFile (path))
+ report (R_WARNING, "Can't delete file %s: error %d",
+ path, GetLastError ());
+ } while (FindNextFile (hFind, &wfd));
+ FindClose (hFind);
+ if (!RemoveDirectory (dir))
+ report (R_WARNING, "Can't remove directory %s: error %d",
+ dir, GetLastError ());
+}
+
+static const char* get_test_source_file(const char* test, const char* subtest)
+{
+ static const char* special_dirs[][2] = {
+ { "gdi32", "gdi"}, { "kernel32", "kernel" },
+ { "msacm32", "msacm" },
+ { "user32", "user" }, { "winspool.drv", "winspool" },
+ { "ws2_32", "winsock" }, { 0, 0 }
+ };
+ static char buffer[MAX_PATH];
+ int i;
+
+ for (i = 0; special_dirs[i][0]; i++) {
+ if (strcmp(test, special_dirs[i][0]) == 0) {
+ test = special_dirs[i][1];
+ break;
+ }
+ }
+
+ snprintf(buffer, sizeof(buffer), "dlls/%s/tests/%s.c", test, subtest);
+ return buffer;
+}
+
+static const char* get_file_rev(const char* file)
+{
+ const struct rev_info* rev;
+
+ for(rev = rev_infos; rev->file; rev++) {
+ if (strcmp(rev->file, file) == 0) return rev->rev;
+ }
+
+ return "-";
+}
+
+static void extract_rev_infos (void)
+{
+ char revinfo[256], *p;
+ int size = 0, i;
+ unsigned int len;
+ HMODULE module = GetModuleHandle (NULL);
+
+ for (i = 0; TRUE; i++) {
+ if (i >= size) {
+ size += 100;
+ rev_infos = xrealloc (rev_infos, size * sizeof (*rev_infos));
+ }
+ memset(rev_infos + i, 0, sizeof(rev_infos[i]));
+
+ len = LoadStringA (module, REV_INFO+i, revinfo, sizeof(revinfo));
+ if (len == 0) break; /* end of revision info */
+ if (len >= sizeof(revinfo) - 1)
+ report (R_FATAL, "Revision info too long.");
+ if(!(p = strrchr(revinfo, ':')))
+ report (R_FATAL, "Revision info malformed (i=%d)", i);
+ *p = 0;
+ rev_infos[i].file = strdup(revinfo);
+ rev_infos[i].rev = strdup(p + 1);
+ }
+}
+
+static void* extract_rcdata (int id, int type, DWORD* size)
+{
+ HRSRC rsrc;
+ HGLOBAL hdl;
+ LPVOID addr;
+
+ if (!(rsrc = FindResource (NULL, (LPTSTR)id, MAKEINTRESOURCE(type))) ||
+ !(*size = SizeofResource (0, rsrc)) ||
+ !(hdl = LoadResource (0, rsrc)) ||
+ !(addr = LockResource (hdl)))
+ return NULL;
+ return addr;
+}
+
+/* Fills in the name and exename fields */
+static void
+extract_test (struct wine_test *test, const char *dir, int id)
+{
+ BYTE* code;
+ DWORD size;
+ FILE* fout;
+ int strlen, bufflen = 128;
+ char *exepos;
+
+ code = extract_rcdata (id, TESTRES, &size);
+ if (!code) report (R_FATAL, "Can't find test resource %d: %d",
+ id, GetLastError ());
+ test->name = xmalloc (bufflen);
+ while ((strlen = LoadStringA (NULL, id, test->name, bufflen))
+ == bufflen - 1) {
+ bufflen *= 2;
+ test->name = xrealloc (test->name, bufflen);
+ }
+ if (!strlen) report (R_FATAL, "Can't read name of test %d.", id);
+ test->exename = strmake (NULL, "%s/%s", dir, test->name);
+ exepos = strstr (test->name, "_test.exe");
+ if (!exepos) report (R_FATAL, "Not an .exe file: %s", test->name);
+ *exepos = 0;
+ test->name = xrealloc (test->name, exepos - test->name + 1);
+ report (R_STEP, "Extracting: %s", test->name);
+
+ if (!(fout = fopen (test->exename, "wb")) ||
+ (fwrite (code, size, 1, fout) != 1) ||
+ fclose (fout)) report (R_FATAL, "Failed to write file %s.",
+ test->exename);
+}
+
+/* Run a command for MS milliseconds. If OUT != NULL, also redirect
+ stdout to there.
+
+ Return the exit status, -2 if can't create process or the return
+ value of WaitForSingleObject.
+ */
+static int
+run_ex (char *cmd, const char *out, DWORD ms)
+{
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ int fd, oldstdout = -1;
+ DWORD wait, status;
+
+ GetStartupInfo (&si);
+ si.wShowWindow = SW_HIDE;
+ si.dwFlags = STARTF_USESHOWWINDOW;
+
+ if (out) {
+ fd = open (out, O_WRONLY | O_CREAT, 0666);
+ if (-1 == fd)
+ report (R_FATAL, "Can't open '%s': %d", out, errno);
+ oldstdout = dup (1);
+ if (-1 == oldstdout)
+ report (R_FATAL, "Can't save stdout: %d", errno);
+ if (-1 == dup2 (fd, 1))
+ report (R_FATAL, "Can't redirect stdout: %d", errno);
+ close (fd);
+ }
+
+ if (!CreateProcessA (NULL, cmd, NULL, NULL, TRUE, 0,
+ NULL, NULL, &si, &pi)) {
+ status = -2;
+ } else {
+ CloseHandle (pi.hThread);
+ wait = WaitForSingleObject (pi.hProcess, ms);
+ if (wait == WAIT_OBJECT_0) {
+ GetExitCodeProcess (pi.hProcess, &status);
+ } else {
+ switch (wait) {
+ case WAIT_FAILED:
+ report (R_ERROR, "Wait for '%s' failed: %d", cmd,
+ GetLastError ());
+ break;
+ case WAIT_TIMEOUT:
+ report (R_ERROR, "Process '%s' timed out.", cmd);
+ break;
+ default:
+ report (R_ERROR, "Wait returned %d", wait);
+ }
+ status = wait;
+ if (!TerminateProcess (pi.hProcess, 257))
+ report (R_ERROR, "TerminateProcess failed: %d",
+ GetLastError ());
+ wait = WaitForSingleObject (pi.hProcess, 5000);
+ switch (wait) {
+ case WAIT_FAILED:
+ report (R_ERROR,
+ "Wait for termination of '%s' failed: %d",
+ cmd, GetLastError ());
+ break;
+ case WAIT_OBJECT_0:
+ break;
+ case WAIT_TIMEOUT:
+ report (R_ERROR, "Can't kill process '%s'", cmd);
+ break;
+ default:
+ report (R_ERROR, "Waiting for termination: %d",
+ wait);
+ }
+ }
+ CloseHandle (pi.hProcess);
+ }
+
+ if (out) {
+ close (1);
+ if (-1 == dup2 (oldstdout, 1))
+ report (R_FATAL, "Can't recover stdout: %d", errno);
+ close (oldstdout);
+ }
+ return status;
+}
+
+static void
+get_subtests (const char *tempdir, struct wine_test *test, int id)
+{
+ char *subname, *cmd;
+ FILE *subfile;
+ size_t total;
+ char buffer[8192], *index;
+ static const char header[] = "Valid test names:";
+ int allocated;
+
+ test->subtest_count = 0;
+
+ subname = tempnam (0, "sub");
+ if (!subname) report (R_FATAL, "Can't name subtests file.");
+
+ extract_test (test, tempdir, id);
+ cmd = strmake (NULL, "%s --list", test->exename);
+ run_ex (cmd, subname, 5000);
+ free (cmd);
+
+ subfile = fopen (subname, "r");
+ if (!subfile) {
+ report (R_ERROR, "Can't open subtests output of %s: %d",
+ test->name, errno);
+ goto quit;
+ }
+ total = fread (buffer, 1, sizeof buffer, subfile);
+ fclose (subfile);
+ if (sizeof buffer == total) {
+ report (R_ERROR, "Subtest list of %s too big.",
+ test->name, sizeof buffer);
+ goto quit;
+ }
+ buffer[total] = 0;
+
+ index = strstr (buffer, header);
+ if (!index) {
+ report (R_ERROR, "Can't parse subtests output of %s",
+ test->name);
+ goto quit;
+ }
+ index += sizeof header;
+
+ allocated = 10;
+ test->subtests = xmalloc (allocated * sizeof(char*));
+ index = strtok (index, whitespace);
+ while (index) {
+ if (test->subtest_count == allocated) {
+ allocated *= 2;
+ test->subtests = xrealloc (test->subtests,
+ allocated * sizeof(char*));
+ }
+ test->subtests[test->subtest_count++] = strdup (index);
+ index = strtok (NULL, whitespace);
+ }
+ test->subtests = xrealloc (test->subtests,
+ test->subtest_count * sizeof(char*));
+
+ quit:
+ if (remove (subname))
+ report (R_WARNING, "Can't delete file '%s': %d",
+ subname, errno);
+ free (subname);
+}
+
+static void
+run_test (struct wine_test* test, const char* subtest)
+{
+ int status;
+ const char* file = get_test_source_file(test->name, subtest);
+ const char* rev = get_file_rev(file);
+ char *cmd = strmake (NULL, "%s %s", test->exename, subtest);
+
+ xprintf ("%s:%s start %s %s\n", test->name, subtest, file, rev);
+ status = run_ex (cmd, NULL, 120000);
+ free (cmd);
+ xprintf ("%s:%s done (%d)\n", test->name, subtest, status);
+}
+
+static BOOL CALLBACK
+EnumTestFileProc (HMODULE hModule, LPCTSTR lpszType,
+ LPTSTR lpszName, LONG_PTR lParam)
+{
+ (*(int*)lParam)++;
+ return TRUE;
+}
+
+static char *
+run_tests (char *logname)
+{
+ int nr_of_files = 0, nr_of_tests = 0, i;
+ char *tempdir, *shorttempdir;
+ int logfile;
+ char *strres, *eol, *nextline;
+ DWORD strsize;
+
+ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
+
+ if (!logname) {
+ logname = tempnam (0, "res");
+ if (!logname) report (R_FATAL, "Can't name logfile.");
+ }
+ report (R_OUT, logname);
+
+ logfile = open (logname, O_WRONLY | O_CREAT | O_EXCL | O_APPEND,
+ 0666);
+ if (-1 == logfile) {
+ if (EEXIST == errno)
+ report (R_FATAL, "File %s already exists.", logname);
+ else report (R_FATAL, "Could not open logfile: %d", errno);
+ }
+ if (-1 == dup2 (logfile, 1))
+ report (R_FATAL, "Can't redirect stdout: %d", errno);
+ close (logfile);
+
+ tempdir = tempnam (0, "wct");
+ if (!tempdir)
+ report (R_FATAL, "Can't name temporary dir (check %%TEMP%%).");
+ shorttempdir = strdup (tempdir);
+ if (shorttempdir) { /* try stable path for ZoneAlarm */
+ strstr (shorttempdir, "wct")[3] = 0;
+ if (CreateDirectoryA (shorttempdir, NULL)) {
+ free (tempdir);
+ tempdir = shorttempdir;
+ } else free (shorttempdir);
+ }
+ if (tempdir != shorttempdir && !CreateDirectoryA (tempdir, NULL))
+ report (R_FATAL, "Could not create directory: %s", tempdir);
+ report (R_DIR, tempdir);
+
+ xprintf ("Version 3\n");
+ strres = extract_rcdata (WINE_BUILD, STRINGRES, &strsize);
+ xprintf ("Tests from build ");
+ if (strres) xprintf ("%.*s", strsize, strres);
+ else xprintf ("-\n");
+ strres = extract_rcdata (TESTS_URL, STRINGRES, &strsize);
+ xprintf ("Archive: ");
+ if (strres) xprintf ("%.*s", strsize, strres);
+ else xprintf ("-\n");
+ xprintf ("Tag: %s\n", tag);
+ xprintf ("Build info:\n");
+ strres = extract_rcdata (BUILD_INFO, STRINGRES, &strsize);
+ while (strres) {
+ eol = memchr (strres, '\n', strsize);
+ if (!eol) {
+ nextline = NULL;
+ eol = strres + strsize;
+ } else {
+ strsize -= eol - strres + 1;
+ nextline = strsize?eol+1:NULL;
+ if (eol > strres && *(eol-1) == '\r') eol--;
+ }
+ xprintf (" %.*s\n", eol-strres, strres);
+ strres = nextline;
+ }
+ xprintf ("Operating system version:\n");
+ print_version ();
+ xprintf ("Test output:\n" );
+
+ report (R_STATUS, "Counting tests");
+ if (!EnumResourceNames (NULL, MAKEINTRESOURCE(TESTRES),
+ EnumTestFileProc, (LPARAM)&nr_of_files))
+ report (R_FATAL, "Can't enumerate test files: %d",
+ GetLastError ());
+ wine_tests = xmalloc (nr_of_files * sizeof wine_tests[0]);
+
+ report (R_STATUS, "Extracting tests");
+ report (R_PROGRESS, 0, nr_of_files);
+ for (i = 0; i < nr_of_files; i++) {
+ get_subtests (tempdir, wine_tests+i, i);
+ nr_of_tests += wine_tests[i].subtest_count;
+ }
+ report (R_DELTA, 0, "Extracting: Done");
+
+ report (R_STATUS, "Running tests");
+ report (R_PROGRESS, 1, nr_of_tests);
+ for (i = 0; i < nr_of_files; i++) {
+ struct wine_test *test = wine_tests + i;
+ int j;
+
+ for (j = 0; j < test->subtest_count; j++) {
+ report (R_STEP, "Running: %s:%s", test->name,
+ test->subtests[j]);
+ run_test (test, test->subtests[j]);
+ }
+ }
+ report (R_DELTA, 0, "Running: Done");
+
+ report (R_STATUS, "Cleaning up");
+ close (1);
+ remove_dir (tempdir);
+ free (tempdir);
+ free (wine_tests);
+
+ return logname;
+}
+
+static void
+usage (void)
+{
+ fprintf (stderr, "\
+Usage: winetest [OPTION]...\n\n\
+ -c console mode, no GUI\n\
+ -e preserve the environment\n\
+ -h print this message and exit\n\
+ -q quiet mode, no output at all\n\
+ -o FILE put report into FILE, do not submit\n\
+ -s FILE submit FILE, do not run tests\n\
+ -t TAG include TAG of characters [-.0-9a-zA-Z] in the report\n");
+}
+
+int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst,
+ LPSTR cmdLine, int cmdShow)
+{
+ char *logname = NULL;
+ const char *cp, *submit = NULL;
+ int reset_env = 1;
+ int interactive = 1;
+
+ /* initialize the revision information first */
+ extract_rev_infos();
+
+ cmdLine = strtok (cmdLine, whitespace);
+ while (cmdLine) {
+ if (cmdLine[0] != '-' || cmdLine[2]) {
+ report (R_ERROR, "Not a single letter option: %s", cmdLine);
+ usage ();
+ exit (2);
+ }
+ switch (cmdLine[1]) {
+ case 'c':
+ report (R_TEXTMODE);
+ interactive = 0;
+ break;
+ case 'e':
+ reset_env = 0;
+ break;
+ case 'h':
+ usage ();
+ exit (0);
+ case 'q':
+ report (R_QUIET);
+ interactive = 0;
+ break;
+ case 's':
+ submit = strtok (NULL, whitespace);
+ if (tag)
+ report (R_WARNING, "ignoring tag for submission");
+ send_file (submit);
+ break;
+ case 'o':
+ logname = strtok (NULL, whitespace);
+ break;
+ case 't':
+ tag = strtok (NULL, whitespace);
+ if (strlen (tag) > MAXTAGLEN)
+ report (R_FATAL, "tag is too long (maximum %d characters)",
+ MAXTAGLEN);
+ cp = findbadtagchar (tag);
+ if (cp) {
+ report (R_ERROR, "invalid char in tag: %c", *cp);
+ usage ();
+ exit (2);
+ }
+ break;
+ default:
+ report (R_ERROR, "invalid option: -%c", cmdLine[1]);
+ usage ();
+ exit (2);
+ }
+ cmdLine = strtok (NULL, whitespace);
+ }
+ if (!submit) {
+ report (R_STATUS, "Starting up");
+
+ if (!running_on_visible_desktop ())
+ report (R_FATAL, "Tests must be run on a visible desktop");
+
+ if (reset_env && (putenv ("WINETEST_PLATFORM=windows") ||
+ putenv ("WINETEST_DEBUG=1") ||
+ putenv ("WINETEST_INTERACTIVE=0") ||
+ putenv ("WINETEST_REPORT_SUCCESS=0")))
+ report (R_FATAL, "Could not reset environment: %d", errno);
+
+ if (!tag) {
+ if (!interactive)
+ report (R_FATAL, "Please specify a tag (-t option) if "
+ "running noninteractive!");
+ if (guiAskTag () == IDABORT) exit (1);
+ }
+ report (R_TAG);
+
+ if (!logname) {
+ logname = run_tests (NULL);
+ if (report (R_ASK, MB_YESNO, "Do you want to submit the "
+ "test results?") == IDYES)
+ if (!send_file (logname) && remove (logname))
+ report (R_WARNING, "Can't remove logfile: %d.", errno);
+ free (logname);
+ } else run_tests (logname);
+ report (R_STATUS, "Finished");
+ }
+ exit (0);
+}
--- /dev/null
+/*
+ * Wine porting definitions
+ *
+ * Copyright 1996 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINE_WINE_PORT_H
+#define __WINE_WINE_PORT_H
+
+#ifndef __WINE_CONFIG_H
+# error You must include config.h to use this header
+#endif
+
+#define _GNU_SOURCE /* for pread/pwrite */
+#include <fcntl.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_DIRECT_H
+# include <direct.h>
+#endif
+#ifdef HAVE_IO_H
+# include <io.h>
+#endif
+#ifdef HAVE_PROCESS_H
+# include <process.h>
+#endif
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+
+/****************************************************************
+ * Type definitions
+ */
+
+#ifndef HAVE_MODE_T
+typedef int mode_t;
+#endif
+#ifndef HAVE_OFF_T
+typedef long off_t;
+#endif
+#ifndef HAVE_PID_T
+typedef int pid_t;
+#endif
+#ifndef HAVE_SIZE_T
+typedef unsigned int size_t;
+#endif
+#ifndef HAVE_SSIZE_T
+typedef int ssize_t;
+#endif
+#ifndef HAVE_FSBLKCNT_T
+typedef unsigned long fsblkcnt_t;
+#endif
+#ifndef HAVE_FSFILCNT_T
+typedef unsigned long fsfilcnt_t;
+#endif
+
+#ifndef HAVE_STRUCT_STATVFS_F_BLOCKS
+struct statvfs
+{
+ unsigned long f_bsize;
+ unsigned long f_frsize;
+ fsblkcnt_t f_blocks;
+ fsblkcnt_t f_bfree;
+ fsblkcnt_t f_bavail;
+ fsfilcnt_t f_files;
+ fsfilcnt_t f_ffree;
+ fsfilcnt_t f_favail;
+ unsigned long f_fsid;
+ unsigned long f_flag;
+ unsigned long f_namemax;
+};
+#endif /* HAVE_STRUCT_STATVFS_F_BLOCKS */
+
+
+/****************************************************************
+ * Macro definitions
+ */
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#else
+#define RTLD_LAZY 0x001
+#define RTLD_NOW 0x002
+#define RTLD_GLOBAL 0x100
+#endif
+
+#if !defined(HAVE_FTRUNCATE) && defined(HAVE_CHSIZE)
+#define ftruncate chsize
+#endif
+
+#if !defined(HAVE_POPEN) && defined(HAVE__POPEN)
+#define popen _popen
+#endif
+
+#if !defined(HAVE_PCLOSE) && defined(HAVE__PCLOSE)
+#define pclose _pclose
+#endif
+
+#if !defined(HAVE_SNPRINTF) && defined(HAVE__SNPRINTF)
+#define snprintf _snprintf
+#endif
+
+#if !defined(HAVE_VSNPRINTF) && defined(HAVE__VSNPRINTF)
+#define vsnprintf _vsnprintf
+#endif
+
+#ifndef S_ISLNK
+# define S_ISLNK(mod) (0)
+#endif
+
+#ifndef S_ISSOCK
+# define S_ISSOCK(mod) (0)
+#endif
+
+#ifndef S_ISDIR
+# define S_ISDIR(mod) (((mod) & _S_IFMT) == _S_IFDIR)
+#endif
+
+#ifndef S_ISCHR
+# define S_ISCHR(mod) (((mod) & _S_IFMT) == _S_IFCHR)
+#endif
+
+#ifndef S_ISFIFO
+# define S_ISFIFO(mod) (((mod) & _S_IFMT) == _S_IFIFO)
+#endif
+
+#ifndef S_ISREG
+# define S_ISREG(mod) (((mod) & _S_IFMT) == _S_IFREG)
+#endif
+
+#ifndef S_IWUSR
+# define S_IWUSR 0
+#endif
+
+/* So we open files in 64 bit access mode on Linux */
+#ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+
+#ifndef O_NONBLOCK
+# define O_NONBLOCK 0
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+#if !defined(S_IXUSR) && defined(S_IEXEC)
+# define S_IXUSR S_IEXEC
+#endif
+#if !defined(S_IXGRP) && defined(S_IEXEC)
+# define S_IXGRP S_IEXEC
+#endif
+#if !defined(S_IXOTH) && defined(S_IEXEC)
+# define S_IXOTH S_IEXEC
+#endif
+
+
+/****************************************************************
+ * Constants
+ */
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef M_PI_2
+#define M_PI_2 1.570796326794896619
+#endif
+
+
+/* Macros to define assembler functions somewhat portably */
+
+#if defined(__GNUC__) && !defined(__MINGW32__) && !defined(__CYGWIN__) && !defined(__APPLE__)
+# define __ASM_GLOBAL_FUNC(name,code) \
+ __asm__( ".text\n\t" \
+ ".align 4\n\t" \
+ ".globl " __ASM_NAME(#name) "\n\t" \
+ __ASM_FUNC(#name) "\n" \
+ __ASM_NAME(#name) ":\n\t" \
+ code \
+ "\n\t.previous" );
+#else /* defined(__GNUC__) && !defined(__MINGW32__) && !defined(__APPLE__) */
+# define __ASM_GLOBAL_FUNC(name,code) \
+ void __asm_dummy_##name(void) { \
+ asm( ".align 4\n\t" \
+ ".globl " __ASM_NAME(#name) "\n\t" \
+ __ASM_FUNC(#name) "\n" \
+ __ASM_NAME(#name) ":\n\t" \
+ code ); \
+ }
+#endif /* __GNUC__ */
+
+
+/* Constructor functions */
+
+#ifdef __GNUC__
+# define DECL_GLOBAL_CONSTRUCTOR(func) \
+ static void func(void) __attribute__((constructor)); \
+ static void func(void)
+#elif defined(__i386__)
+# define DECL_GLOBAL_CONSTRUCTOR(func) \
+ static void __dummy_init_##func(void) { \
+ asm(".section .init,\"ax\"\n\t" \
+ "call " #func "\n\t" \
+ ".previous"); } \
+ static void func(void)
+#elif defined(__sparc__)
+# define DECL_GLOBAL_CONSTRUCTOR(func) \
+ static void __dummy_init_##func(void) { \
+ asm("\t.section \".init\",#alloc,#execinstr\n" \
+ "\tcall " #func "\n" \
+ "\tnop\n" \
+ "\t.section \".text\",#alloc,#execinstr\n" ); } \
+ static void func(void)
+#else
+# error You must define the DECL_GLOBAL_CONSTRUCTOR macro for your platform
+#endif
+
+
+/* Register functions */
+
+#ifdef __i386__
+#define DEFINE_REGS_ENTRYPOINT( name, fn, args, pop_args ) \
+ __ASM_GLOBAL_FUNC( name, \
+ "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t" \
+ ".long " __ASM_NAME(#fn) "\n\t" \
+ ".byte " #args "," #pop_args )
+/* FIXME: add support for other CPUs */
+#endif /* __i386__ */
+
+
+/****************************************************************
+ * Function definitions (only when using libwine_port)
+ */
+
+#ifndef NO_LIBWINE_PORT
+
+#ifndef HAVE_FSTATVFS
+int fstatvfs( int fd, struct statvfs *buf );
+#endif
+
+#ifndef HAVE_GETOPT_LONG
+extern char *optarg;
+extern int optind;
+extern int opterr;
+extern int optopt;
+struct option;
+
+#ifndef HAVE_STRUCT_OPTION_NAME
+struct option
+{
+ const char *name;
+ int has_arg;
+ int *flag;
+ int val;
+};
+#endif
+
+extern int getopt_long (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+extern int getopt_long_only (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+#endif /* HAVE_GETOPT_LONG */
+
+#ifndef HAVE_FFS
+int ffs( int x );
+#endif
+
+#ifndef HAVE_FUTIMES
+struct timeval;
+int futimes(int fd, const struct timeval tv[2]);
+#endif
+
+#ifndef HAVE_GETPAGESIZE
+size_t getpagesize(void);
+#endif /* HAVE_GETPAGESIZE */
+
+#ifndef HAVE_GETTID
+pid_t gettid(void);
+#endif /* HAVE_GETTID */
+
+#ifndef HAVE_LSTAT
+int lstat(const char *file_name, struct stat *buf);
+#endif /* HAVE_LSTAT */
+
+#ifndef HAVE_MEMMOVE
+void *memmove(void *dest, const void *src, size_t len);
+#endif /* !defined(HAVE_MEMMOVE) */
+
+#ifndef HAVE_PREAD
+ssize_t pread( int fd, void *buf, size_t count, off_t offset );
+#endif /* HAVE_PREAD */
+
+#ifndef HAVE_PWRITE
+ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset );
+#endif /* HAVE_PWRITE */
+
+#ifndef HAVE_READLINK
+int readlink( const char *path, char *buf, size_t size );
+#endif /* HAVE_READLINK */
+
+#ifndef HAVE_SIGSETJMP
+# include <setjmp.h>
+typedef jmp_buf sigjmp_buf;
+int sigsetjmp( sigjmp_buf buf, int savesigs );
+void siglongjmp( sigjmp_buf buf, int val );
+#endif /* HAVE_SIGSETJMP */
+
+#ifndef HAVE_STATVFS
+int statvfs( const char *path, struct statvfs *buf );
+#endif
+
+#ifndef HAVE_STRNCASECMP
+# ifndef HAVE__STRNICMP
+int strncasecmp(const char *str1, const char *str2, size_t n);
+# else
+# define strncasecmp _strnicmp
+# endif
+#endif /* !defined(HAVE_STRNCASECMP) */
+
+#ifndef HAVE_STRERROR
+const char *strerror(int err);
+#endif /* !defined(HAVE_STRERROR) */
+
+#ifndef HAVE_STRCASECMP
+# ifndef HAVE__STRICMP
+int strcasecmp(const char *str1, const char *str2);
+# else
+# define strcasecmp _stricmp
+# endif
+#endif /* !defined(HAVE_STRCASECMP) */
+
+#ifndef HAVE_USLEEP
+int usleep (unsigned int useconds);
+#endif /* !defined(HAVE_USLEEP) */
+
+#ifdef __i386__
+static inline void *memcpy_unaligned( void *dst, const void *src, size_t size )
+{
+ return memcpy( dst, src, size );
+}
+#else
+extern void *memcpy_unaligned( void *dst, const void *src, size_t size );
+#endif /* __i386__ */
+
+extern int mkstemps(char *template, int suffix_len);
+
+/* Process creation flags */
+#ifndef _P_WAIT
+# define _P_WAIT 0
+# define _P_NOWAIT 1
+# define _P_OVERLAY 2
+# define _P_NOWAITO 3
+# define _P_DETACH 4
+#endif
+#ifndef HAVE_SPAWNVP
+extern int spawnvp(int mode, const char *cmdname, const char * const argv[]);
+#endif
+
+/* Interlocked functions */
+
+#if defined(__i386__) && defined(__GNUC__)
+
+extern inline long interlocked_cmpxchg( long *dest, long xchg, long compare );
+extern inline void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare );
+extern inline long interlocked_xchg( long *dest, long val );
+extern inline void *interlocked_xchg_ptr( void **dest, void *val );
+extern inline long interlocked_xchg_add( long *dest, long incr );
+
+extern inline long interlocked_cmpxchg( long *dest, long xchg, long compare )
+{
+ long ret;
+ __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
+ : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
+ return ret;
+}
+
+extern inline void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
+{
+ void *ret;
+ __asm__ __volatile__( "lock; cmpxchgl %2,(%1)"
+ : "=a" (ret) : "r" (dest), "r" (xchg), "0" (compare) : "memory" );
+ return ret;
+}
+
+extern inline long interlocked_xchg( long *dest, long val )
+{
+ long ret;
+ __asm__ __volatile__( "lock; xchgl %0,(%1)"
+ : "=r" (ret) : "r" (dest), "0" (val) : "memory" );
+ return ret;
+}
+
+extern inline void *interlocked_xchg_ptr( void **dest, void *val )
+{
+ void *ret;
+ __asm__ __volatile__( "lock; xchgl %0,(%1)"
+ : "=r" (ret) : "r" (dest), "0" (val) : "memory" );
+ return ret;
+}
+
+extern inline long interlocked_xchg_add( long *dest, long incr )
+{
+ long ret;
+ __asm__ __volatile__( "lock; xaddl %0,(%1)"
+ : "=r" (ret) : "r" (dest), "0" (incr) : "memory" );
+ return ret;
+}
+
+#else /* __i386___ && __GNUC__ */
+
+extern long interlocked_cmpxchg( long *dest, long xchg, long compare );
+extern void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare );
+extern long interlocked_xchg( long *dest, long val );
+extern void *interlocked_xchg_ptr( void **dest, void *val );
+extern long interlocked_xchg_add( long *dest, long incr );
+
+#endif /* __i386___ && __GNUC__ */
+
+#else /* NO_LIBWINE_PORT */
+
+#define __WINE_NOT_PORTABLE(func) func##_is_not_portable func##_is_not_portable
+
+#define ffs __WINE_NOT_PORTABLE(ffs)
+#define fstatvfs __WINE_NOT_PORTABLE(fstatvfs)
+#define futimes __WINE_NOT_PORTABLE(futimes)
+#define getopt_long __WINE_NOT_PORTABLE(getopt_long)
+#define getopt_long_only __WINE_NOT_PORTABLE(getopt_long_only)
+#define getpagesize __WINE_NOT_PORTABLE(getpagesize)
+#define interlocked_cmpxchg __WINE_NOT_PORTABLE(interlocked_cmpxchg)
+#define interlocked_cmpxchg_ptr __WINE_NOT_PORTABLE(interlocked_cmpxchg_ptr)
+#define interlocked_xchg __WINE_NOT_PORTABLE(interlocked_xchg)
+#define interlocked_xchg_ptr __WINE_NOT_PORTABLE(interlocked_xchg_ptr)
+#define interlocked_xchg_add __WINE_NOT_PORTABLE(interlocked_xchg_add)
+#define lstat __WINE_NOT_PORTABLE(lstat)
+#define memcpy_unaligned __WINE_NOT_PORTABLE(memcpy_unaligned)
+#define memmove __WINE_NOT_PORTABLE(memmove)
+#define pread __WINE_NOT_PORTABLE(pread)
+#define pwrite __WINE_NOT_PORTABLE(pwrite)
+#define spawnvp __WINE_NOT_PORTABLE(spawnvp)
+#define statvfs __WINE_NOT_PORTABLE(statvfs)
+#define strcasecmp __WINE_NOT_PORTABLE(strcasecmp)
+#define strerror __WINE_NOT_PORTABLE(strerror)
+#define strncasecmp __WINE_NOT_PORTABLE(strncasecmp)
+#define usleep __WINE_NOT_PORTABLE(usleep)
+
+#endif /* NO_LIBWINE_PORT */
+
+#endif /* !defined(__WINE_WINE_PORT_H) */
--- /dev/null
+/*
+ * Resource definitions
+ *
+ * Copyright 2004 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define IDI_WINE 1
+
+#define IDD_STATUS 100
+#define IDD_ABOUT 101
+#define IDD_TAG 102
+
+#define IDC_ST0 1000
+#define IDC_PB0 1001
+#define IDC_ST1 1002
+#define IDC_PB1 1003
+#define IDC_ST2 1004
+#define IDC_PB2 1005
+
+#define IDC_DIR 2000
+#define IDC_OUT 2001
+#define IDC_TAG 2002
+
+#define IDC_SB 3000
+
+#define IDC_EDIT 4000
+#define IDC_ABOUT 4001
+
+/* Resource types */
+
+#define TESTRES 1000
+#define STRINGRES 1001
+
+/* String resources */
+
+#define WINE_BUILD 10000
+#define BUILD_INFO 10001
+#define TESTS_URL 10002
+
+/* Revision info strings start from this index: */
+#define REV_INFO 30000
--- /dev/null
+/*
+ * HTTP handling functions.
+ *
+ * Copyright 2003 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <winsock.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "winetest.h"
+
+static SOCKET
+open_http (const char *server)
+{
+ WSADATA wsad;
+ struct sockaddr_in sa;
+ SOCKET s;
+
+ report (R_STATUS, "Opening HTTP connection to %s", server);
+ if (WSAStartup (MAKEWORD (2,2), &wsad)) return INVALID_SOCKET;
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (80);
+ sa.sin_addr.s_addr = inet_addr (server);
+ if (sa.sin_addr.s_addr == INADDR_NONE) {
+ struct hostent *host = gethostbyname (server);
+ if (!host) {
+ report (R_ERROR, "Hostname lookup failed for %s", server);
+ goto failure;
+ }
+ sa.sin_addr.s_addr = ((struct in_addr *)host->h_addr)->s_addr;
+ }
+ s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (s == INVALID_SOCKET) {
+ report (R_ERROR, "Can't open network socket: %d",
+ WSAGetLastError ());
+ goto failure;
+ }
+ if (!connect (s, (struct sockaddr*)&sa, sizeof (struct sockaddr_in)))
+ return s;
+
+ report (R_ERROR, "Can't connect: %d", WSAGetLastError ());
+ closesocket (s);
+ failure:
+ WSACleanup ();
+ return INVALID_SOCKET;
+}
+
+static int
+close_http (SOCKET s)
+{
+ int ret;
+
+ ret = closesocket (s);
+ return (WSACleanup () || ret);
+}
+
+static int
+send_buf (SOCKET s, const char *buf, size_t length)
+{
+ int sent;
+
+ while (length > 0) {
+ sent = send (s, buf, length, 0);
+ if (sent == SOCKET_ERROR) return 1;
+ buf += sent;
+ length -= sent;
+ }
+ return 0;
+}
+
+static int
+send_str (SOCKET s, ...)
+{
+ va_list ap;
+ char *p;
+ int ret;
+ size_t len;
+
+ va_start (ap, s);
+ p = vstrmake (&len, ap);
+ va_end (ap);
+ if (!p) return 1;
+ ret = send_buf (s, p, len);
+ free (p);
+ return ret;
+}
+
+int
+send_file (const char *name)
+{
+ SOCKET s;
+ FILE *f;
+#define BUFLEN 8192
+ unsigned char buffer[BUFLEN+1];
+ size_t bytes_read, total, filesize;
+ char *str;
+ int ret;
+
+ /* RFC 2616 */
+#define SEP "--8<--cut-here--8<--"
+ static const char head[] = "POST /submit HTTP/1.0\r\n"
+ "Host: test.winehq.org\r\n"
+ "User-Agent: Winetest Shell\r\n"
+ "Content-Type: multipart/form-data; boundary=\"" SEP "\"\r\n"
+ "Content-Length: %u\r\n\r\n";
+ static const char body1[] = "--" SEP "\r\n"
+ "Content-Disposition: form-data; name=\"reportfile\"; filename=\"%s\"\r\n"
+ "Content-Type: application/octet-stream\r\n\r\n";
+ static const char body2[] = "\r\n--" SEP "\r\n"
+ "Content-Disposition: form-data; name=\"submit\"\r\n\r\n"
+ "Upload File\r\n"
+ "--" SEP "--\r\n";
+
+ s = open_http ("test.winehq.org");
+ if (s == INVALID_SOCKET) return 1;
+
+ f = fopen (name, "rb");
+ if (!f) {
+ report (R_WARNING, "Can't open file '%s': %d", name, errno);
+ goto abort1;
+ }
+ fseek (f, 0, SEEK_END);
+ filesize = ftell (f);
+ if (filesize > 1024*1024) {
+ report (R_WARNING,
+ "File too big (%.1f MB > 1 MB); submitting partial report.",
+ filesize/1024.0/1024);
+ filesize = 1024*1024;
+ }
+ fseek (f, 0, SEEK_SET);
+
+ report (R_STATUS, "Sending header");
+ str = strmake (&total, body1, name);
+ ret = send_str (s, head, filesize + total + sizeof body2 - 1) ||
+ send_buf (s, str, total);
+ free (str);
+ if (ret) {
+ report (R_WARNING, "Error sending header: %d, %d",
+ errno, WSAGetLastError ());
+ goto abort2;
+ }
+
+ report (R_STATUS, "Sending %u bytes of data", filesize);
+ report (R_PROGRESS, 2, filesize);
+ total = 0;
+ while (total < filesize && (bytes_read = fread (buffer, 1, BUFLEN/2, f))) {
+ if ((signed)bytes_read == -1) {
+ report (R_WARNING, "Error reading log file: %d", errno);
+ goto abort2;
+ }
+ total += bytes_read;
+ if (total > filesize) bytes_read -= total - filesize;
+ if (send_buf (s, buffer, bytes_read)) {
+ report (R_WARNING, "Error sending body: %d, %d",
+ errno, WSAGetLastError ());
+ goto abort2;
+ }
+ report (R_DELTA, bytes_read, "Network transfer: In progress");
+ }
+ fclose (f);
+
+ if (send_buf (s, body2, sizeof body2 - 1)) {
+ report (R_WARNING, "Error sending trailer: %d, %d",
+ errno, WSAGetLastError ());
+ goto abort2;
+ }
+ report (R_DELTA, 0, "Network transfer: Done");
+
+ total = 0;
+ while ((bytes_read = recv (s, buffer+total, BUFLEN-total, 0))) {
+ if ((signed)bytes_read == SOCKET_ERROR) {
+ report (R_WARNING, "Error receiving reply: %d, %d",
+ errno, WSAGetLastError ());
+ goto abort1;
+ }
+ total += bytes_read;
+ if (total == BUFLEN) {
+ report (R_WARNING, "Buffer overflow");
+ goto abort1;
+ }
+ }
+ if (close_http (s)) {
+ report (R_WARNING, "Error closing connection: %d, %d",
+ errno, WSAGetLastError ());
+ return 1;
+ }
+
+ str = strmake (&bytes_read, "Received %s (%d bytes).\n",
+ name, filesize);
+ ret = memcmp (str, buffer + total - bytes_read, bytes_read);
+ free (str);
+ if (ret) {
+ buffer[total] = 0;
+ str = strstr (buffer, "\r\n\r\n");
+ if (!str) str = buffer;
+ else str = str + 4;
+ report (R_ERROR, "Can't submit logfile '%s'. "
+ "Server response: %s", name, str);
+ }
+ return ret;
+
+ abort2:
+ fclose (f);
+ abort1:
+ close_http (s);
+ return 1;
+}
--- /dev/null
+/* Automatically generated -- do not edit! */
+#include "resource.h"
+STRINGTABLE {
+0 "advapi32_test.exe"
+1 "comctl32_test.exe"
+}
+0 TESTRES "../../../../../output-i386/regtests/winetest/advapi32/advapi32_test.exe"
+1 TESTRES "output-i386/regtests/winetest/comctl32/comctl32_test.exe"
--- /dev/null
+/*
+ * Utility functions.
+ *
+ * Copyright 2003 Dimitrie O. Paun
+ * Copyright 2003 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "winetest.h"
+
+void *xmalloc (size_t len)
+{
+ void *p = malloc (len);
+
+ if (!p) report (R_FATAL, "Out of memory.");
+ return p;
+}
+
+void *xrealloc (void *op, size_t len)
+{
+ void *p = realloc (op, len);
+
+ if (len && !p) report (R_FATAL, "Out of memory.");
+ return p;
+}
+
+static char *vstrfmtmake (size_t *lenp, const char *fmt, va_list ap)
+{
+ size_t size = 1000;
+ char *p, *q;
+ int n;
+
+ p = malloc (size);
+ if (!p) return NULL;
+ while (1) {
+ n = vsnprintf (p, size, fmt, ap);
+ if (n < 0) size *= 2; /* Windows */
+ else if ((unsigned)n >= size) size = n+1; /* glibc */
+ else break;
+ q = realloc (p, size);
+ if (!q) {
+ free (p);
+ return NULL;
+ }
+ p = q;
+ }
+ if (lenp) *lenp = n;
+ return p;
+}
+
+char *vstrmake (size_t *lenp, va_list ap)
+{
+ const char *fmt;
+
+ fmt = va_arg (ap, const char*);
+ return vstrfmtmake (lenp, fmt, ap);
+}
+
+char *strmake (size_t *lenp, ...)
+{
+ va_list ap;
+ char *p;
+
+ va_start (ap, lenp);
+ p = vstrmake (lenp, ap);
+ if (!p) report (R_FATAL, "Out of memory.");
+ va_end (ap);
+ return p;
+}
+
+void xprintf (const char *fmt, ...)
+{
+ va_list ap;
+ size_t size;
+ ssize_t written;
+ char *buffer, *head;
+
+ va_start (ap, fmt);
+ buffer = vstrfmtmake (&size, fmt, ap);
+ head = buffer;
+ va_end (ap);
+ while ((written = write (1, head, size)) != size) {
+ if (written == -1)
+ report (R_FATAL, "Can't write logs: %d", errno);
+ head += written;
+ size -= written;
+ }
+ free (buffer);
+}
+
+int
+goodtagchar (char c)
+{
+ return (('a'<=c && c<='z') ||
+ ('A'<=c && c<='Z') ||
+ ('0'<=c && c<='9') ||
+ c=='-' || c=='.');
+}
+
+const char *
+findbadtagchar (const char *tag)
+{
+ while (*tag)
+ if (goodtagchar (*tag)) tag++;
+ else return tag;
+ return NULL;
+}
--- /dev/null
+/*
+ * winetest definitions
+ *
+ * Copyright 2003 Dimitrie O. Paun
+ * Copyright 2003 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINETESTS_H
+#define __WINETESTS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+void fatal (const char* msg);
+void warning (const char* msg);
+void *xmalloc (size_t len);
+void *xrealloc (void *op, size_t len);
+void xprintf (const char *fmt, ...);
+char *vstrmake (size_t *lenp, va_list ap);
+char *strmake (size_t *lenp, ...);
+int goodtagchar (char c);
+const char *findbadtagchar (const char *tag);
+
+int send_file (const char *name);
+
+/* GUI definitions */
+
+#include <windows.h>
+
+enum report_type {
+ R_STATUS = 0,
+ R_PROGRESS,
+ R_STEP,
+ R_DELTA,
+ R_TAG,
+ R_DIR,
+ R_OUT,
+ R_WARNING,
+ R_ERROR,
+ R_FATAL,
+ R_ASK,
+ R_TEXTMODE,
+ R_QUIET
+};
+
+#define MAXTAGLEN 20
+extern char *tag;
+int guiAskTag (void);
+int report (enum report_type t, ...);
+
+#endif /* __WINETESTS_H */
--- /dev/null
+/*
+ * Winetest resources
+ *
+ * Copyright 2004 Ferenc Wagner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <windows.h>
+#include <winres.h>
+#include "resource.h"
+#include "tests.rc"
+
+IDD_TAG DIALOG 0, 0, 150, 65
+STYLE WS_POPUP
+CAPTION "No tag supplied"
+BEGIN
+ CTEXT "Please supply a tag for your report. You can use letters, digits, dashes and periods."
+ IDC_STATIC, 10, 5, 130, 30
+ EDITTEXT IDC_TAG, 35, 30, 80, 10, ES_AUTOHSCROLL
+ DEFPUSHBUTTON "Start", IDOK, 25, 45, 40, 14
+ PUSHBUTTON "Abort", IDABORT, 85, 45, 40, 14
+END
+
+IDD_STATUS DIALOG 0, 0, 160, 150
+STYLE WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
+CAPTION "Wine Test Shell"
+BEGIN
+ LTEXT "Extracting:", IDC_ST0, 10, 5, 140, 10
+ CONTROL "PB0", IDC_PB0, PROGRESS_CLASS, 0, 5, 15, 150, 10
+ LTEXT "Running:", IDC_ST1, 10, 30, 140, 10
+ CONTROL "PB1", IDC_PB1, PROGRESS_CLASS, 0, 5, 40, 150, 15
+ LTEXT "Network transfer:", IDC_ST2, 10, 60, 140, 10
+ CONTROL "PB2", IDC_PB2, PROGRESS_CLASS, 0, 5, 70, 150, 10
+
+ LTEXT "Tag:", IDC_STATIC, 10, 89, 100, 10
+ EDITTEXT IDC_TAG, 25, 88, 125, 10,
+ ES_READONLY
+ LTEXT "Working directory:", IDC_STATIC, 10, 100, 100, 10
+ EDITTEXT IDC_DIR, 71, 99, 79, 10,
+ ES_READONLY | ES_AUTOHSCROLL
+ LTEXT "Output file:", IDC_STATIC, 10, 111, 100, 10
+ EDITTEXT IDC_OUT, 46, 110, 104, 10,
+ ES_READONLY | ES_AUTOHSCROLL
+
+ DEFPUSHBUTTON "About", IDHELP, 20, 123, 30, 14
+ PUSHBUTTON "Edit", IDCANCEL, 65, 123, 30, 14,
+ WS_DISABLED
+ PUSHBUTTON "Stop", IDABORT, 110, 123, 30, 14
+
+ CONTROL "Created", IDC_SB, STATUSCLASSNAME, 0, 0,0,0,0
+END
+
+IDD_ABOUT DIALOG 0, 0, 150, 60
+STYLE WS_POPUP
+CAPTION "About Wine Test Shell"
+BEGIN
+ CTEXT "This program extracts and runs a series of tests which check Wine's conformance to the Windows API.",
+ IDC_STATIC, 10, 5, 130, 30
+ DEFPUSHBUTTON "Close", IDCANCEL, 55, 40, 40, 14
+END
+
+/* BINRES wine.ico */
+IDI_WINE ICON "wine.ico"
+/* {
+ '00 00 01 00 02 00 20 20 10 00 00 00 00 00 E8 02'
+ '00 00 26 00 00 00 10 10 10 00 00 00 00 00 28 01'
+ '00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00'
+ '00 00 01 00 04 00 00 00 00 00 00 02 00 00 00 00'
+ '00 00 00 00 00 00 10 00 00 00 00 00 00 00 39 02'
+ 'B1 00 23 02 6C 00 0F 03 29 00 1B 02 51 00 FF FF'
+ 'FF 00 1B 1A 1B 00 1E 02 63 00 33 02 A1 00 08 08'
+ '08 00 14 03 3C 00 0C 04 1E 00 2E 02 8E 00 10 0F'
+ '10 00 2A 02 82 00 29 02 7D 00 03 02 04 00 44 44'
+ '44 44 44 44 44 44 55 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 8F FF 84 44 44 44 44 44 44 44 44 44'
+ '44 44 44 8F F8 F8 44 44 44 44 44 44 44 44 44 44'
+ '44 44 8F FF F5 44 44 44 44 44 44 44 44 44 44 44'
+ '44 5C F8 C8 F5 44 44 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 85 44 44 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 4C 44 44 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 4C 44 44 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 45 54 44 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 45 F4 44 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 45 FF 44 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 48 FF F4 44 44 44 44 44 44 44 44 44'
+ '44 44 44 44 48 23 9A 84 44 44 44 44 44 44 44 44'
+ '44 44 44 44 42 B7 7E AF 44 44 44 44 44 44 44 44'
+ '44 44 44 44 49 00 00 EA C4 44 44 44 44 44 44 44'
+ '44 44 44 44 46 00 00 01 F4 44 44 44 44 44 44 44'
+ '44 44 44 44 46 00 00 00 9F 44 44 44 44 44 44 44'
+ '44 44 44 44 46 00 70 00 EF 44 44 44 44 44 44 44'
+ '44 44 44 44 43 00 00 00 79 F4 44 44 44 44 44 44'
+ '44 44 44 44 49 00 00 00 0E F4 44 44 44 44 44 44'
+ '44 44 44 44 42 00 00 00 07 24 44 44 44 44 44 44'
+ '44 44 44 44 43 B0 00 00 00 34 44 44 44 44 44 44'
+ '44 44 44 44 4C 30 00 00 00 1F 44 44 44 44 44 44'
+ '44 44 44 44 48 27 E1 1D B1 2C 44 44 44 44 44 44'
+ '44 44 44 44 44 A9 CC CF F8 48 C4 44 44 44 44 44'
+ '44 44 44 44 44 58 44 44 44 45 C4 44 44 44 44 44'
+ '44 44 44 44 44 4C 44 44 44 44 84 44 44 44 44 44'
+ '44 44 44 44 44 48 44 44 44 44 C4 44 44 44 44 44'
+ '44 44 44 44 44 48 C4 44 44 44 C4 44 44 44 44 44'
+ '44 44 44 44 44 44 F4 44 44 4C C4 44 44 44 44 44'
+ '44 44 44 44 44 44 84 44 F8 84 44 44 44 44 44 44'
+ '44 44 44 44 44 44 48 F8 44 44 44 44 44 44 FF FF'
+ '3F FF FF F0 7F FF FF C0 FF FF FF 03 FF FF FC 03'
+ 'FF FF FF F3 FF FF FF FB FF FF FF FB FF FF FF F9'
+ 'FF FF FF F9 FF FF FF F8 FF FF FF F8 7F FF FF F8'
+ '1F FF FF F8 0F FF FF F8 07 FF FF F8 07 FF FF F8'
+ '03 FF FF F8 03 FF FF F8 01 FF FF F8 01 FF FF F8'
+ '01 FF FF F8 01 FF FF F8 00 FF FF F8 00 FF FF FC'
+ '02 7F FF FC FE 7F FF FE FF 7F FF FE FF 7F FF FE'
+ '7F 7F FF FF 7E 7F FF FF 71 FF FF FF 8F FF 28 00'
+ '00 00 10 00 00 00 20 00 00 00 01 00 04 00 00 00'
+ '00 00 80 00 00 00 00 00 00 00 00 00 00 00 10 00'
+ '00 00 00 00 00 00 3A 02 B1 00 0A 06 14 00 12 03'
+ '33 00 FF FF FF 00 12 12 12 00 0B 0B 0B 00 1B 1B'
+ '1B 00 25 02 6F 00 2E 02 92 00 1A 02 52 00 36 02'
+ 'A6 00 15 03 3E 00 04 04 05 00 13 11 19 00 1E 02'
+ '62 00 2A 02 82 00 33 33 33 CC 43 33 33 33 33 33'
+ 'CC 5C 33 33 33 33 33 36 C5 53 33 33 33 33 33 33'
+ '33 43 33 33 33 33 33 33 33 65 33 33 33 33 33 33'
+ '33 DC 33 33 33 33 33 33 33 17 EC 33 33 33 33 33'
+ '33 B0 07 53 33 33 33 33 33 90 00 B3 33 33 33 33'
+ '33 B0 00 FC 33 33 33 33 33 BA 00 A2 33 33 33 33'
+ '33 C7 88 82 33 33 33 33 33 3D D5 14 43 33 33 33'
+ '33 35 33 33 53 33 33 33 33 33 53 33 53 33 33 33'
+ '33 33 C5 5C 33 33 FC 7F 00 00 F0 FF 00 00 E1 FF'
+ '00 00 FD FF 00 00 FC FF 00 00 FC FF 00 00 FC 3F'
+ '00 00 FC 1F 00 00 FC 1F 00 00 FC 0F 00 00 FC 0F'
+ '00 00 FC 0F 00 00 FE 07 00 00 FE F7 00 00 FF 77'
+ '00 00 FF 0F 00 00'
+} */