diff --git a/rostests/winetests/advpack/advpack.c b/rostests/winetests/advpack/advpack.c new file mode 100644 index 00000000000..b09a9c6158c --- /dev/null +++ b/rostests/winetests/advpack/advpack.c @@ -0,0 +1,597 @@ +/* + * Unit tests for advpack.dll + * + * Copyright (C) 2005 Robert Reif + * Copyright (C) 2005 Sami Aario + * + * 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 +#include +#include +#include +#include +#include "wine/test.h" + +/* defines for the TranslateInfString/Ex tests */ +#define TEST_STRING1 "\\Application Name" +#define TEST_STRING2 "%49001%\\Application Name" + +/* defines for the SetPerUserSecValues tests */ +#define GUID_KEY "SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\guid" +#define REG_VAL_EXISTS(key, value) !RegQueryValueEx(key, value, NULL, NULL, NULL, NULL) +#define OPEN_GUID_KEY() !RegOpenKey(HKEY_LOCAL_MACHINE, GUID_KEY, &guid) + +static HRESULT (WINAPI *pCloseINFEngine)(HINF); +static HRESULT (WINAPI *pDelNode)(LPCSTR,DWORD); +static HRESULT (WINAPI *pGetVersionFromFile)(LPCSTR,LPDWORD,LPDWORD,BOOL); +static HRESULT (WINAPI *pOpenINFEngine)(PCSTR,PCSTR,DWORD,HINF*,PVOID); +static HRESULT (WINAPI *pSetPerUserSecValues)(PPERUSERSECTION pPerUser); +static HRESULT (WINAPI *pTranslateInfString)(LPCSTR,LPCSTR,LPCSTR,LPCSTR,LPSTR,DWORD,LPDWORD,LPVOID); +static HRESULT (WINAPI *pTranslateInfStringEx)(HINF,PCSTR,PCSTR,PCSTR,PSTR,DWORD,PDWORD,PVOID); + +static CHAR inf_file[MAX_PATH]; +static CHAR PROG_FILES_ROOT[MAX_PATH]; +static CHAR PROG_FILES[MAX_PATH]; +static CHAR APP_PATH[MAX_PATH]; +static DWORD APP_PATH_LEN; + +static void get_progfiles_dir(void) +{ + HKEY hkey; + DWORD size = MAX_PATH; + + RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey); + RegQueryValueExA(hkey, "ProgramFilesDir", NULL, NULL, (LPBYTE)PROG_FILES_ROOT, &size); + RegCloseKey(hkey); + + lstrcpyA(PROG_FILES, PROG_FILES_ROOT + 3); /* skip C:\ */ + lstrcpyA(APP_PATH, PROG_FILES_ROOT); + lstrcatA(APP_PATH, TEST_STRING1); + APP_PATH_LEN = lstrlenA(APP_PATH) + 1; +} + +static BOOL init_function_pointers(void) +{ + HMODULE hAdvPack = LoadLibraryA("advpack.dll"); + + if (!hAdvPack) + return FALSE; + + pCloseINFEngine = (void*)GetProcAddress(hAdvPack, "CloseINFEngine"); + pDelNode = (void *)GetProcAddress(hAdvPack, "DelNode"); + pGetVersionFromFile = (void *)GetProcAddress(hAdvPack, "GetVersionFromFile"); + pOpenINFEngine = (void*)GetProcAddress(hAdvPack, "OpenINFEngine"); + pSetPerUserSecValues = (void*)GetProcAddress(hAdvPack, "SetPerUserSecValues"); + pTranslateInfString = (void *)GetProcAddress(hAdvPack, "TranslateInfString"); + pTranslateInfStringEx = (void*)GetProcAddress(hAdvPack, "TranslateInfStringEx"); + + if (!pCloseINFEngine || !pDelNode || !pGetVersionFromFile || + !pOpenINFEngine || !pSetPerUserSecValues || !pTranslateInfString) + return FALSE; + + return TRUE; +} + +static void version_test(void) +{ + HRESULT hr; + DWORD major, minor; + + major = minor = 0; + hr = pGetVersionFromFile("kernel32.dll", &major, &minor, FALSE); + ok (hr == S_OK, "GetVersionFromFileEx(kernel32.dll) failed, returned " + "0x%08x\n", hr); + trace("kernel32.dll Language ID: 0x%08x, Codepage ID: 0x%08x\n", + major, minor); + + major = minor = 0; + hr = pGetVersionFromFile("kernel32.dll", &major, &minor, TRUE); + ok (hr == S_OK, "GetVersionFromFileEx(kernel32.dll) failed, returned " + "0x%08x\n", hr); + trace("kernel32.dll version: %d.%d.%d.%d\n", HIWORD(major), LOWORD(major), + HIWORD(minor), LOWORD(minor)); + + major = minor = 0; + hr = pGetVersionFromFile("advpack.dll", &major, &minor, FALSE); + ok (hr == S_OK, "GetVersionFromFileEx(advpack.dll) failed, returned " + "0x%08x\n", hr); + trace("advpack.dll Language ID: 0x%08x, Codepage ID: 0x%08x\n", + major, minor); + + major = minor = 0; + hr = pGetVersionFromFile("advpack.dll", &major, &minor, TRUE); + ok (hr == S_OK, "GetVersionFromFileEx(advpack.dll) failed, returned " + "0x%08x\n", hr); + trace("advpack.dll version: %d.%d.%d.%d\n", HIWORD(major), LOWORD(major), + HIWORD(minor), LOWORD(minor)); +} + +static void delnode_test(void) +{ + HRESULT hr; + HANDLE hn; + CHAR currDir[MAX_PATH]; + int currDirLen; + + /* Native DelNode apparently does not support relative paths, so we use + absolute paths for testing */ + currDirLen = GetCurrentDirectoryA(sizeof(currDir) / sizeof(CHAR), currDir); + assert(currDirLen > 0 && currDirLen < sizeof(currDir) / sizeof(CHAR)); + + if(currDir[currDirLen - 1] == '\\') + currDir[--currDirLen] = 0; + + /* Simple tests; these should fail. */ + hr = pDelNode(NULL, 0); + ok (hr == E_FAIL, "DelNode called with NULL pathname should return E_FAIL\n"); + hr = pDelNode("", 0); + ok (hr == E_FAIL, "DelNode called with empty pathname should return E_FAIL\n"); + + /* Test deletion of a file. */ + hn = CreateFile("DelNodeTestFile1", GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + assert(hn != INVALID_HANDLE_VALUE); + CloseHandle(hn); + hr = pDelNode(lstrcat(currDir, "\\DelNodeTestFile1"), 0); + ok (hr == S_OK, "DelNode failed deleting a single file\n"); + currDir[currDirLen] = '\0'; + + /* Test deletion of an empty directory. */ + CreateDirectoryA("DelNodeTestDir", NULL); + hr = pDelNode(lstrcat(currDir, "\\DelNodeTestDir"), 0); + ok (hr == S_OK, "DelNode failed deleting an empty directory\n"); + currDir[currDirLen] = '\0'; + + /* Test deletion of a directory containing one file. */ + CreateDirectoryA("DelNodeTestDir", NULL); + hn = CreateFile("DelNodeTestDir\\DelNodeTestFile1", GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + assert(hn != INVALID_HANDLE_VALUE); + CloseHandle(hn); + hr = pDelNode(lstrcat(currDir, "\\DelNodeTestDir"), 0); + ok (hr == S_OK, "DelNode failed deleting a directory containing one file\n"); + currDir[currDirLen] = '\0'; + + /* Test deletion of a directory containing multiple files. */ + CreateDirectoryA("DelNodeTestDir", NULL); + hn = CreateFile("DelNodeTestDir\\DelNodeTestFile1", GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + assert(hn != INVALID_HANDLE_VALUE); + CloseHandle(hn); + hn = CreateFile("DelNodeTestDir\\DelNodeTestFile2", GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + assert(hn != INVALID_HANDLE_VALUE); + CloseHandle(hn); + hn = CreateFile("DelNodeTestDir\\DelNodeTestFile3", GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + assert(hn != INVALID_HANDLE_VALUE); + CloseHandle(hn); + hr = pDelNode(lstrcat(currDir, "\\DelNodeTestDir"), 0); + ok (hr == S_OK, "DelNode failed deleting a directory containing multiple files\n"); + currDir[currDirLen] = '\0'; +} + +static void append_str(char **str, const char *data, ...) +{ + va_list valist; + + va_start(valist, data); + vsprintf(*str, data, valist); + *str += strlen(*str); + va_end(valist); +} + +static void create_inf_file(void) +{ + char data[1024]; + char *ptr = data; + DWORD dwNumberOfBytesWritten; + HANDLE hf = CreateFile(inf_file, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + append_str(&ptr, "[Version]\n"); + append_str(&ptr, "Signature=\"$Chicago$\"\n"); + append_str(&ptr, "[CustInstDestSection]\n"); + append_str(&ptr, "49001=ProgramFilesDir\n"); + append_str(&ptr, "49010=DestA,1\n"); + append_str(&ptr, "49020=DestB\n"); + append_str(&ptr, "49030=DestC\n"); + append_str(&ptr, "[ProgramFilesDir]\n"); + append_str(&ptr, "HKLM,\"Software\\Microsoft\\Windows\\CurrentVersion\","); + append_str(&ptr, "\"ProgramFilesDir\",,\"%%24%%\\%%LProgramF%%\"\n"); + append_str(&ptr, "[section]\n"); + append_str(&ptr, "NotACustomDestination=Version\n"); + append_str(&ptr, "CustomDestination=CustInstDestSection\n"); + append_str(&ptr, "[Options.NTx86]\n"); + append_str(&ptr, "49001=ProgramFilesDir\n"); + append_str(&ptr, "InstallDir=%%49001%%\\%%DefaultAppPath%%\n"); + append_str(&ptr, "Result1=%%49010%%\n"); + append_str(&ptr, "Result2=%%49020%%\n"); + append_str(&ptr, "Result3=%%49030%%\n"); + append_str(&ptr, "CustomHDestination=CustInstDestSection\n"); + append_str(&ptr, "[Strings]\n"); + append_str(&ptr, "DefaultAppPath=\"Application Name\"\n"); + append_str(&ptr, "LProgramF=\"%s\"\n", PROG_FILES); + append_str(&ptr, "[DestA]\n"); + append_str(&ptr, "HKLM,\"Software\\Garbage\",\"ProgramFilesDir\",,'%%24%%\\%%LProgramF%%'\n"); + append_str(&ptr, "[DestB]\n"); + append_str(&ptr, "'HKLM','Software\\Microsoft\\Windows\\CurrentVersion',"); + append_str(&ptr, "'ProgramFilesDir',,\"%%24%%\"\n"); + append_str(&ptr, "[DestC]\n"); + append_str(&ptr, "HKLM,\"Software\\Garbage\",\"ProgramFilesDir\",,'%%24%%'\n"); + + WriteFile(hf, data, ptr - data, &dwNumberOfBytesWritten, NULL); + CloseHandle(hf); +} + +static void translateinfstring_test(void) +{ + HRESULT hr; + char buffer[MAX_PATH]; + DWORD dwSize; + + create_inf_file(); + + /* pass in a couple invalid parameters */ + hr = pTranslateInfString(NULL, NULL, NULL, NULL, buffer, MAX_PATH, &dwSize, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", (UINT)hr); + + /* try to open an inf file that doesn't exist */ + hr = pTranslateInfString("c:\\a.inf", "Options.NTx86", "Options.NTx86", + "InstallDir", buffer, MAX_PATH, &dwSize, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || hr == E_INVALIDARG || + hr == HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND), + "Expected E_INVALIDARG, 0x80070002 or 0x8007007e, got 0x%08x\n", (UINT)hr); + + if(hr == HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND)) + { + trace("WinNT 3.51 detected. Skipping tests for TranslateInfString()\n"); + return; + } + + /* try a nonexistent section */ + buffer[0] = 0; + hr = pTranslateInfString(inf_file, "idontexist", "Options.NTx86", + "InstallDir", buffer, MAX_PATH, &dwSize, NULL); + ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", (UINT)hr); + ok(!strcmp(buffer, TEST_STRING2), "Expected %s, got %s\n", TEST_STRING2, buffer); + ok(dwSize == 25, "Expected size 25, got %d\n", dwSize); + + buffer[0] = 0; + /* try other nonexistent section */ + hr = pTranslateInfString(inf_file, "Options.NTx86", "idontexist", + "InstallDir", buffer, MAX_PATH, &dwSize, NULL); + ok(hr == SPAPI_E_LINE_NOT_FOUND || hr == E_INVALIDARG, + "Expected SPAPI_E_LINE_NOT_FOUND or E_INVALIDARG, got 0x%08x\n", (UINT)hr); + + buffer[0] = 0; + /* try nonexistent key */ + hr = pTranslateInfString(inf_file, "Options.NTx86", "Options.NTx86", + "notvalid", buffer, MAX_PATH, &dwSize, NULL); + ok(hr == SPAPI_E_LINE_NOT_FOUND || hr == E_INVALIDARG, + "Expected SPAPI_E_LINE_NOT_FOUND or E_INVALIDARG, got 0x%08x\n", (UINT)hr); + + buffer[0] = 0; + /* test the behavior of pszInstallSection */ + hr = pTranslateInfString(inf_file, "section", "Options.NTx86", + "InstallDir", buffer, MAX_PATH, &dwSize, NULL); + ok(hr == ERROR_SUCCESS || hr == E_FAIL, + "Expected ERROR_SUCCESS or E_FAIL, got 0x%08x\n", (UINT)hr); + + if(hr == ERROR_SUCCESS) + { + ok(!strcmp(buffer, APP_PATH), "Expected '%s', got '%s'\n", APP_PATH, buffer); + ok(dwSize == APP_PATH_LEN, "Expected size %d, got %d\n", APP_PATH_LEN, dwSize); + } + + buffer[0] = 0; + /* try without a pszInstallSection */ + hr = pTranslateInfString(inf_file, NULL, "Options.NTx86", + "InstallDir", buffer, MAX_PATH, &dwSize, NULL); + ok(hr == S_OK, "Expected S_OK, got 0x%08x\n", (UINT)hr); + todo_wine + { + ok(!strcmp(buffer, TEST_STRING2), "Expected %s, got %s\n", TEST_STRING2, buffer); + ok(dwSize == 25, "Expected size 25, got %d\n", dwSize); + } + + DeleteFile("c:\\a.inf"); + DeleteFile(inf_file); +} + +static void translateinfstringex_test(void) +{ + HINF hinf; + HRESULT hr; + char buffer[MAX_PATH]; + DWORD size = MAX_PATH; + + create_inf_file(); + + /* need to see if there are any flags */ + + /* try a NULL filename */ + hr = pOpenINFEngine(NULL, "Options.NTx86", 0, &hinf, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* try an empty filename */ + hr = pOpenINFEngine("", "Options.NTx86", 0, &hinf, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %08x\n", hr); + + /* try a NULL hinf */ + hr = pOpenINFEngine(inf_file, "Options.NTx86", 0, NULL, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* open the INF without the Install section specified */ + hr = pOpenINFEngine(inf_file, NULL, 0, &hinf, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + /* try a NULL hinf */ + hr = pTranslateInfStringEx(NULL, inf_file, "Options.NTx86", "InstallDir", + buffer, size, &size, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* try a NULL filename */ + hr = pTranslateInfStringEx(hinf, NULL, "Options.NTx86", "InstallDir", + buffer, size, &size, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* try an empty filename */ + memset(buffer, 'a', 25); + buffer[24] = '\0'; + size = MAX_PATH; + hr = pTranslateInfStringEx(hinf, "", "Options.NTx86", "InstallDir", + buffer, size, &size, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + todo_wine + { + ok(!strcmp(buffer, TEST_STRING2), "Expected %s, got %s\n", TEST_STRING2, buffer); + ok(size == 25, "Expected size 25, got %d\n", size); + } + + /* try a NULL translate section */ + hr = pTranslateInfStringEx(hinf, inf_file, NULL, "InstallDir", + buffer, size, &size, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* try an empty translate section */ + hr = pTranslateInfStringEx(hinf, inf_file, "", "InstallDir", + buffer, size, &size, NULL); + ok(hr == SPAPI_E_LINE_NOT_FOUND, "Expected SPAPI_E_LINE_NOT_FOUND, got %08x\n", hr); + + /* try a NULL translate key */ + hr = pTranslateInfStringEx(hinf, inf_file, "Options.NTx86", NULL, + buffer, size, &size, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* try an empty translate key */ + hr = pTranslateInfStringEx(hinf, inf_file, "Options.NTx86", "", + buffer, size, &size, NULL); + ok(hr == SPAPI_E_LINE_NOT_FOUND, "Expected SPAPI_E_LINE_NOT_FOUND, got %08x\n", hr); + + /* successfully translate the string */ + memset(buffer, 'a', 25); + buffer[24] = '\0'; + size = MAX_PATH; + hr = pTranslateInfStringEx(hinf, inf_file, "Options.NTx86", "InstallDir", + buffer, size, &size, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + todo_wine + { + ok(!strcmp(buffer, TEST_STRING2), "Expected %s, got %s\n", TEST_STRING2, buffer); + ok(size == 25, "Expected size 25, got %d\n", size); + } + + /* try a NULL hinf */ + hr = pCloseINFEngine(NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr); + + /* successfully close the hinf */ + hr = pCloseINFEngine(hinf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + /* open the inf with the install section */ + hr = pOpenINFEngine(inf_file, "section", 0, &hinf, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + /* translate the string with the install section specified */ + memset(buffer, 'a', APP_PATH_LEN); + buffer[APP_PATH_LEN - 1] = '\0'; + size = MAX_PATH; + hr = pTranslateInfStringEx(hinf, inf_file, "Options.NTx86", "InstallDir", + buffer, size, &size, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!strcmp(buffer, APP_PATH), "Expected %s, got %s\n", APP_PATH, buffer); + ok(size == APP_PATH_LEN, "Expected size %d, got %d\n", APP_PATH_LEN, size); + + /* Single quote test (Note size includes null on return from call) */ + memset(buffer, 'a', APP_PATH_LEN); + buffer[APP_PATH_LEN - 1] = '\0'; + size = MAX_PATH; + hr = pTranslateInfStringEx(hinf, inf_file, "Options.NTx86", "Result1", + buffer, size, &size, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpi(buffer, PROG_FILES_ROOT), + "Expected %s, got %s\n", PROG_FILES_ROOT, buffer); + ok(size == lstrlenA(PROG_FILES_ROOT)+1, "Expected size %d, got %d\n", + lstrlenA(PROG_FILES_ROOT)+1, size); + + memset(buffer, 'a', APP_PATH_LEN); + buffer[APP_PATH_LEN - 1] = '\0'; + size = MAX_PATH; + hr = pTranslateInfStringEx(hinf, inf_file, "Options.NTx86", "Result2", + buffer, size, &size, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpi(buffer, PROG_FILES_ROOT), + "Expected %s, got %s\n", PROG_FILES_ROOT, buffer); + ok(size == lstrlenA(PROG_FILES_ROOT)+1, "Expected size %d, got %d\n", + lstrlenA(PROG_FILES_ROOT)+1, size); + + { + char drive[MAX_PATH]; + lstrcpy(drive, PROG_FILES_ROOT); + drive[3] = 0x00; /* Just keep the system drive plus '\' */ + + memset(buffer, 'a', APP_PATH_LEN); + buffer[APP_PATH_LEN - 1] = '\0'; + size = MAX_PATH; + hr = pTranslateInfStringEx(hinf, inf_file, "Options.NTx86", "Result3", + buffer, size, &size, NULL); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(!lstrcmpi(buffer, drive), + "Expected %s, got %s\n", drive, buffer); + ok(size == lstrlenA(drive)+1, "Expected size %d, got %d\n", + lstrlenA(drive)+1, size); + } + + /* close the INF again */ + hr = pCloseINFEngine(hinf); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + DeleteFileA(inf_file); +} + +static BOOL check_reg_str(HKEY hkey, LPCSTR name, LPCSTR value) +{ + DWORD size = MAX_PATH; + char check[MAX_PATH]; + + if (RegQueryValueEx(hkey, name, NULL, NULL, (LPBYTE)check, &size)) + return FALSE; + + return !lstrcmp(check, value); +} + +static BOOL check_reg_dword(HKEY hkey, LPCSTR name, DWORD value) +{ + DWORD size = sizeof(DWORD); + DWORD check; + + if (RegQueryValueEx(hkey, name, NULL, NULL, (LPBYTE)&check, &size)) + return FALSE; + + return (check == value); +} + +static void setperusersecvalues_test(void) +{ + PERUSERSECTION peruser; + HRESULT hr; + HKEY guid; + + lstrcpy(peruser.szDispName, "displayname"); + lstrcpy(peruser.szLocale, "locale"); + lstrcpy(peruser.szStub, "stub"); + lstrcpy(peruser.szVersion, "1,1,1,1"); + lstrcpy(peruser.szCompID, "compid"); + peruser.dwIsInstalled = 1; + peruser.bRollback = FALSE; + + /* try a NULL pPerUser */ + if (0) + { + /* This crashes on systems with IE7 */ + hr = pSetPerUserSecValues(NULL); + todo_wine + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(!OPEN_GUID_KEY(), "Expected guid key to not exist\n"); + } + + /* at the very least, szGUID must be valid */ + peruser.szGUID[0] = '\0'; + hr = pSetPerUserSecValues(&peruser); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(!OPEN_GUID_KEY(), "Expected guid key to not exist\n"); + + /* set initial values */ + lstrcpy(peruser.szGUID, "guid"); + hr = pSetPerUserSecValues(&peruser); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(OPEN_GUID_KEY(), "Expected guid key to exist\n"); + ok(check_reg_str(guid, NULL, "displayname"), "Expected displayname\n"); + ok(check_reg_str(guid, "ComponentID", "compid"), "Expected compid\n"); + ok(check_reg_str(guid, "Locale", "locale"), "Expected locale\n"); + ok(check_reg_str(guid, "StubPath", "stub"), "Expected stub\n"); + ok(check_reg_str(guid, "Version", "1,1,1,1"), "Expected 1,1,1,1\n"); + ok(check_reg_dword(guid, "IsInstalled", 1), "Expected 1\n"); + ok(!REG_VAL_EXISTS(guid, "OldDisplayName"), "Expected OldDisplayName to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "OldLocale"), "Expected OldLocale to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "OldStubPath"), "Expected OldStubPath to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "OldVersion"), "Expected OldVersion to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "RealStubPath"), "Expected RealStubPath to not exist\n"); + + /* raise the version, but bRollback is FALSE, so vals not saved */ + lstrcpy(peruser.szVersion, "2,1,1,1"); + hr = pSetPerUserSecValues(&peruser); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(check_reg_str(guid, NULL, "displayname"), "Expected displayname\n"); + ok(check_reg_str(guid, "ComponentID", "compid"), "Expected compid\n"); + ok(check_reg_str(guid, "Locale", "locale"), "Expected locale\n"); + ok(check_reg_str(guid, "StubPath", "stub"), "Expected stub\n"); + ok(check_reg_str(guid, "Version", "2,1,1,1"), "Expected 2,1,1,1\n"); + ok(check_reg_dword(guid, "IsInstalled", 1), "Expected 1\n"); + ok(!REG_VAL_EXISTS(guid, "OldDisplayName"), "Expected OldDisplayName to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "OldLocale"), "Expected OldLocale to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "OldStubPath"), "Expected OldStubPath to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "OldVersion"), "Expected OldVersion to not exist\n"); + ok(!REG_VAL_EXISTS(guid, "RealStubPath"), "Expected RealStubPath to not exist\n"); + + /* raise the version again, bRollback is TRUE so vals are saved */ + peruser.bRollback = TRUE; + lstrcpy(peruser.szVersion, "3,1,1,1"); + hr = pSetPerUserSecValues(&peruser); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(check_reg_str(guid, NULL, "displayname"), "Expected displayname\n"); + ok(check_reg_str(guid, "ComponentID", "compid"), "Expected compid\n"); + ok(check_reg_str(guid, "Locale", "locale"), "Expected locale\n"); + ok(check_reg_dword(guid, "IsInstalled", 1), "Expected 1\n"); + ok(check_reg_str(guid, "Version", "3,1,1,1"), "Expected 3,1,1,1\n"); + todo_wine + { + ok(check_reg_str(guid, "OldDisplayName", "displayname"), "Expected displayname\n"); + ok(check_reg_str(guid, "OldLocale", "locale"), "Expected locale\n"); + ok(check_reg_str(guid, "RealStubPath", "stub"), "Expected stub\n"); + ok(check_reg_str(guid, "OldStubPath", "stub"), "Expected stub\n"); + ok(check_reg_str(guid, "OldVersion", "2,1,1,1"), "Expected 2,1,1,1\n"); + ok(check_reg_str(guid, "StubPath", + "rundll32.exe advpack.dll,UserInstStubWrapper guid"), + "Expected real stub\n"); + } + + RegDeleteKey(HKEY_LOCAL_MACHINE, GUID_KEY); +} + +START_TEST(advpack) +{ + if (!init_function_pointers()) + return; + + /* Make sure we create the temporary file in a directory + * were we have enough rights + */ + GetTempPath(MAX_PATH, inf_file); + lstrcat(inf_file,"test.inf"); + + get_progfiles_dir(); + + version_test(); + delnode_test(); + setperusersecvalues_test(); + translateinfstring_test(); + translateinfstringex_test(); +} diff --git a/rostests/winetests/advpack/advpack.rbuild b/rostests/winetests/advpack/advpack.rbuild new file mode 100644 index 00000000000..baabd337d90 --- /dev/null +++ b/rostests/winetests/advpack/advpack.rbuild @@ -0,0 +1,17 @@ + + + + . + 0x600 + 0x600 + wine + cabinet + user32 + advapi32 + kernel32 + ntdll + advpack.c + files.c + install.c + testlist.c + diff --git a/rostests/winetests/advpack/files.c b/rostests/winetests/advpack/files.c new file mode 100644 index 00000000000..71c1741d957 --- /dev/null +++ b/rostests/winetests/advpack/files.c @@ -0,0 +1,563 @@ +/* + * Unit tests for advpack.dll file 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 +#include +#include +#include +#include "wine/test.h" + +/* make the max size large so there is only one cab file */ +#define MEDIA_SIZE 999999999 +#define FOLDER_THRESHOLD 900000 + +/* function pointers */ +HMODULE hAdvPack; +static HRESULT (WINAPI *pAddDelBackupEntry)(LPCSTR, LPCSTR, LPCSTR, DWORD); +static HRESULT (WINAPI *pExtractFiles)(LPCSTR, LPCSTR, DWORD, LPCSTR, LPVOID, DWORD); +static HRESULT (WINAPI *pAdvInstallFile)(HWND,LPCSTR,LPCSTR,LPCSTR,LPCSTR,DWORD,DWORD); + +CHAR CURR_DIR[MAX_PATH]; + +static void init_function_pointers(void) +{ + hAdvPack = LoadLibraryA("advpack.dll"); + + if (hAdvPack) + { + pAddDelBackupEntry = (void *)GetProcAddress(hAdvPack, "AddDelBackupEntry"); + pExtractFiles = (void *)GetProcAddress(hAdvPack, "ExtractFiles"); + pAdvInstallFile = (void*)GetProcAddress(hAdvPack, "AdvInstallFile"); + } +} + +/* 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"); + RemoveDirectoryA("dest"); + + DeleteFileA("extract.cab"); +} + +static BOOL check_ini_file_attr(LPSTR filename) +{ + BOOL ret; + DWORD expected = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_READONLY; + DWORD attr = GetFileAttributesA(filename); + + ret = (attr & expected) && (attr != INVALID_FILE_ATTRIBUTES); + SetFileAttributesA(filename, FILE_ATTRIBUTE_NORMAL); + + return ret; +} + +#define FIELD_LEN 16 + +static BOOL check_ini_contents(LPSTR filename, BOOL add) +{ + CHAR field[FIELD_LEN]; + BOOL ret = TRUE, match; + + GetPrivateProfileStringA("backup", "one", NULL, field, FIELD_LEN, filename); + match = !lstrcmpA(field, "-1,0,0,0,0,0,-1"); + if ((add && !match) || (!add && match)) { + trace("first test: got %s\n", field); + ret = FALSE; + } + + GetPrivateProfileStringA("backup", "two", NULL, field, FIELD_LEN, filename); + if (lstrcmpA(field, "-1,0,0,0,0,0,-1")) { + trace("second test: got %s\n", field); + ret = FALSE; + } + + GetPrivateProfileStringA("backup", "three", NULL, field, FIELD_LEN, filename); + match = !lstrcmpA(field, "-1,0,0,0,0,0,-1"); + if ((add && !match) || (!add && match)) { + trace("third test: got %s\n", field); + ret = FALSE; + } + + return ret; +} + +static void test_AddDelBackupEntry(void) +{ + HRESULT res; + CHAR path[MAX_PATH]; + CHAR windir[MAX_PATH]; + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\backup\\basename.INI"); + + /* native AddDelBackupEntry crashes if lpcszBaseName is NULL */ + + /* try a NULL file list */ + res = pAddDelBackupEntry(NULL, "backup", "basename", AADBE_ADD_ENTRY); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(!DeleteFileA(path), "Expected path to not exist\n"); + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\backup\\.INI"); + + /* try an empty base name */ + res = pAddDelBackupEntry("one\0two\0three\0", "backup", "", AADBE_ADD_ENTRY); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(!DeleteFileA(path), "Expected path to not exist\n"); + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\basename.INI"); + + /* try an invalid flag */ + res = pAddDelBackupEntry("one\0two\0three\0", NULL, "basename", 0); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(!DeleteFileA(path), "Expected path to not exist\n"); + + lstrcpyA(path, "c:\\basename.INI"); + + /* create the INF file */ + res = pAddDelBackupEntry("one\0two\0three\0", "c:\\", "basename", AADBE_ADD_ENTRY); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(check_ini_file_attr(path), "Expected ini file to be hidden\n"); + ok(check_ini_contents(path, TRUE), "Expected ini contents to match\n"); + ok(DeleteFileA(path), "Expected path to exist\n"); + + lstrcpyA(path, CURR_DIR); + lstrcatA(path, "\\backup\\basename.INI"); + + /* try to create the INI file in a nonexistent directory */ + RemoveDirectoryA("backup"); + res = pAddDelBackupEntry("one\0two\0three\0", "backup", "basename", AADBE_ADD_ENTRY); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(!check_ini_file_attr(path), "Expected ini file to not be hidden\n"); + ok(!check_ini_contents(path, TRUE), "Expected ini contents to not match\n"); + ok(!DeleteFileA(path), "Expected path to not exist\n"); + + /* try an existent, relative backup directory */ + CreateDirectoryA("backup", NULL); + res = pAddDelBackupEntry("one\0two\0three\0", "backup", "basename", AADBE_ADD_ENTRY); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(check_ini_file_attr(path), "Expected ini file to be hidden\n"); + ok(check_ini_contents(path, TRUE), "Expected ini contents to match\n"); + ok(DeleteFileA(path), "Expected path to exist\n"); + RemoveDirectoryA("backup"); + + GetWindowsDirectoryA(windir, sizeof(windir)); + sprintf(path, "%s\\basename.INI", windir); + + /* try a NULL backup dir, INI is created in the windows directory */ + res = pAddDelBackupEntry("one\0two\0three\0", NULL, "basename", AADBE_ADD_ENTRY); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(check_ini_contents(path, TRUE), "Expected ini contents to match\n"); + + /* remove the entries with AADBE_DEL_ENTRY */ + SetFileAttributesA(path, FILE_ATTRIBUTE_NORMAL); + res = pAddDelBackupEntry("one\0three\0", NULL, "basename", AADBE_DEL_ENTRY); + SetFileAttributesA(path, FILE_ATTRIBUTE_NORMAL); + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(check_ini_contents(path, FALSE), "Expected ini contents to match\n"); + ok(DeleteFileA(path), "Expected path to exist\n"); +} + +/* 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; + /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */ + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + + 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; + static CHAR a_txt[] = "a.txt", + b_txt[] = "b.txt", + testdir_c_txt[] = "testdir\\c.txt", + testdir_d_txt[] = "testdir\\d.txt"; + 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_ExtractFiles(void) +{ + HRESULT hr; + char destFolder[MAX_PATH]; + + lstrcpyA(destFolder, CURR_DIR); + lstrcatA(destFolder, "\\"); + lstrcatA(destFolder, "dest"); + + /* try NULL cab file */ + hr = pExtractFiles(NULL, destFolder, 0, NULL, NULL, 0); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + ok(RemoveDirectoryA("dest"), "Expected dest to exist\n"); + + /* try NULL destination */ + hr = pExtractFiles("extract.cab", NULL, 0, NULL, NULL, 0); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + ok(!RemoveDirectoryA("dest"), "Expected dest to not exist\n"); + + /* extract all files in the cab to nonexistent destination directory */ + hr = pExtractFiles("extract.cab", destFolder, 0, NULL, NULL, 0); + ok(hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), + "Expected %d, got %d\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), hr); + 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(!RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to not exist\n"); + ok(!RemoveDirectoryA("dest"), "Expected dest to not exist\n"); + + /* extract all files in the cab to the destination directory */ + CreateDirectoryA("dest", NULL); + hr = pExtractFiles("extract.cab", destFolder, 0, NULL, NULL, 0); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + 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"); + + /* extract all files to a relative destination directory */ + hr = pExtractFiles("extract.cab", "dest", 0, NULL, NULL, 0); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + 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"); + + /* only extract two of the files from the cab */ + hr = pExtractFiles("extract.cab", "dest", 0, "a.txt:testdir\\c.txt", NULL, 0); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + 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(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir 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"); + + /* use valid chars before and after file list */ + hr = pExtractFiles("extract.cab", "dest", 0, " :\t: a.txt:testdir\\c.txt \t:", NULL, 0); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + 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(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir 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"); + + /* use invalid chars before and after file list */ + hr = pExtractFiles("extract.cab", "dest", 0, " +-\\ a.txt:testdir\\c.txt a_:", NULL, 0); + ok(hr == E_FAIL, "Expected E_FAIL, got %d\n", hr); + 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(!RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to not exist\n"); + + /* try an empty file list */ + hr = pExtractFiles("extract.cab", "dest", 0, "", NULL, 0); + ok(hr == E_FAIL, "Expected E_FAIL, got %d\n", hr); + ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n"); + ok(!RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to not exist\n"); + + /* try a nonexistent file in the file list */ + hr = pExtractFiles("extract.cab", "dest", 0, "a.txt:idontexist:testdir\\c.txt", NULL, 0); + ok(hr == E_FAIL, "Expected E_FAIL, got %d\n", hr); + 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(!RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to not exist\n"); +} + +static void test_AdvInstallFile(void) +{ + HRESULT hr; + char CURR_DIR[MAX_PATH]; + char destFolder[MAX_PATH]; + + GetCurrentDirectoryA(MAX_PATH, CURR_DIR); + + lstrcpyA(destFolder, CURR_DIR); + lstrcatA(destFolder, "\\"); + lstrcatA(destFolder, "dest"); + + createTestFile("source.txt"); + + /* try invalid source directory */ + hr = pAdvInstallFile(NULL, NULL, "source.txt", destFolder, "destination.txt", 0, 0); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + ok(!DeleteFileA("dest\\destination.txt"), "Expected dest\\destination.txt to not exist\n"); + + /* try invalid source file */ + hr = pAdvInstallFile(NULL, CURR_DIR, NULL, destFolder, "destination.txt", 0, 0); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + ok(!DeleteFileA("dest\\destination.txt"), "Expected dest\\destination.txt to not exist\n"); + + /* try invalid destination directory */ + hr = pAdvInstallFile(NULL, CURR_DIR, "source.txt", NULL, "destination.txt", 0, 0); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + ok(!DeleteFileA("dest\\destination.txt"), "Expected dest\\destination.txt to not exist\n"); + + /* try copying to nonexistent destination directory */ + hr = pAdvInstallFile(NULL, CURR_DIR, "source.txt", destFolder, "destination.txt", 0, 0); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(DeleteFileA("dest\\destination.txt"), "Expected dest\\destination.txt to exist\n"); + + /* native windows screws up if the source file doesn't exist */ + + /* test AIF_NOOVERWRITE behavior, asks the user to overwrite if AIF_QUIET is not specified */ + createTestFile("dest\\destination.txt"); + hr = pAdvInstallFile(NULL, CURR_DIR, "source.txt", destFolder, + "destination.txt", AIF_NOOVERWRITE | AIF_QUIET, 0); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(DeleteFileA("dest\\destination.txt"), "Expected dest\\destination.txt to exist\n"); + ok(RemoveDirectoryA("dest"), "Expected dest to exist\n"); + + DeleteFileA("source.txt"); +} + +START_TEST(files) +{ + init_function_pointers(); + create_test_files(); + create_cab_file(); + + test_AddDelBackupEntry(); + test_ExtractFiles(); + test_AdvInstallFile(); + + delete_test_files(); +} diff --git a/rostests/winetests/advpack/install.c b/rostests/winetests/advpack/install.c new file mode 100644 index 00000000000..607596347f5 --- /dev/null +++ b/rostests/winetests/advpack/install.c @@ -0,0 +1,276 @@ +/* + * Unit tests for advpack.dll install 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 +#include +#include +#include "wine/test.h" + +/* function pointers */ +static HRESULT (WINAPI *pRunSetupCommand)(HWND, LPCSTR, LPCSTR, LPCSTR, LPCSTR, HANDLE*, DWORD, LPVOID); +static HRESULT (WINAPI *pLaunchINFSection)(HWND, HINSTANCE, LPSTR, INT); +static HRESULT (WINAPI *pLaunchINFSectionEx)(HWND, HINSTANCE, LPSTR, INT); + +static char CURR_DIR[MAX_PATH]; + +static BOOL init_function_pointers(void) +{ + HMODULE hAdvPack = LoadLibraryA("advpack.dll"); + if (!hAdvPack) + return FALSE; + + pRunSetupCommand = (void *)GetProcAddress(hAdvPack, "RunSetupCommand"); + pLaunchINFSection = (void *)GetProcAddress(hAdvPack, "LaunchINFSection"); + pLaunchINFSectionEx = (void *)GetProcAddress(hAdvPack, "LaunchINFSectionEx"); + + if (!pRunSetupCommand || !pLaunchINFSection || !pLaunchINFSectionEx) + return FALSE; + + return TRUE; +} + +static BOOL is_spapi_err(DWORD err) +{ + const DWORD SPAPI_PREFIX = 0x800F0000L; + const DWORD SPAPI_MASK = 0xFFFF0000L; + + return (((err & SPAPI_MASK) ^ SPAPI_PREFIX) == 0); +} + +static void append_str(char **str, const char *data) +{ + sprintf(*str, data); + *str += strlen(*str); +} + +static void create_inf_file(LPCSTR 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"); + append_str(&ptr, "[DefaultInstall]\n"); + append_str(&ptr, "RegisterOCXs=RegisterOCXsSection\n"); + append_str(&ptr, "[RegisterOCXsSection]\n"); + append_str(&ptr, "%%11%%\\ole32.dll\n"); + + WriteFile(hf, data, ptr - data, &dwNumberOfBytesWritten, NULL); + CloseHandle(hf); +} + +static void test_RunSetupCommand(void) +{ + HRESULT hr; + HANDLE hexe; + char path[MAX_PATH]; + char dir[MAX_PATH]; + char systemdir[MAX_PATH]; + + GetSystemDirectoryA(systemdir, sizeof(systemdir)); + + /* try an invalid cmd name */ + hr = pRunSetupCommand(NULL, NULL, "Install", "Dir", "Title", NULL, 0, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + + /* try an invalid directory */ + hr = pRunSetupCommand(NULL, "winver.exe", "Install", NULL, "Title", NULL, 0, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + + /* try to run a nonexistent exe */ + hexe = (HANDLE)0xdeadbeef; + hr = pRunSetupCommand(NULL, "idontexist.exe", "Install", systemdir, "Title", &hexe, 0, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %d\n", hr); + ok(hexe == NULL, "Expcted hexe to be NULL\n"); + ok(!TerminateProcess(hexe, 0), "Expected TerminateProcess to fail\n"); + + /* try a bad directory */ + hexe = (HANDLE)0xdeadbeef; + hr = pRunSetupCommand(NULL, "winver.exe", "Install", "non\\existent\\directory", "Title", &hexe, 0, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_DIRECTORY), + "Expected HRESULT_FROM_WIN32(ERROR_DIRECTORY), got %d\n", hr); + ok(hexe == NULL, "Expcted hexe to be NULL\n"); + ok(!TerminateProcess(hexe, 0), "Expected TerminateProcess to fail\n"); + + /* try to run an exe with the RSC_FLAG_INF flag */ + hexe = (HANDLE)0xdeadbeef; + hr = pRunSetupCommand(NULL, "winver.exe", "Install", systemdir, "Title", &hexe, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(is_spapi_err(hr), "Expected a setupapi error, got %d\n", hr); + ok(hexe == (HANDLE)0xdeadbeef, "Expected hexe to be 0xdeadbeef\n"); + ok(!TerminateProcess(hexe, 0), "Expected TerminateProcess to fail\n"); + + /* run winver.exe */ + hexe = (HANDLE)0xdeadbeef; + hr = pRunSetupCommand(NULL, "winver.exe", "Install", systemdir, "Title", &hexe, 0, NULL); + ok(hr == S_ASYNCHRONOUS, "Expected S_ASYNCHRONOUS, got %d\n", hr); + ok(hexe != NULL, "Expected hexe to be non-NULL\n"); + ok(TerminateProcess(hexe, 0), "Expected TerminateProcess to succeed\n"); + + CreateDirectoryA("one", NULL); + create_inf_file("one\\test.inf"); + + /* try a full path to the INF, with working dir provided */ + lstrcpy(path, CURR_DIR); + lstrcat(path, "\\one\\test.inf"); + lstrcpy(dir, CURR_DIR); + lstrcat(dir, "\\one"); + hr = pRunSetupCommand(NULL, path, "DefaultInstall", dir, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", hr); + + /* try a full path to the INF, NULL working dir */ + hr = pRunSetupCommand(NULL, path, "DefaultInstall", NULL, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %d\n", hr); + + /* try a full path to the INF, empty working dir */ + hr = pRunSetupCommand(NULL, path, "DefaultInstall", "", "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", hr); + + /* try a relative path to the INF, with working dir provided */ + hr = pRunSetupCommand(NULL, "one\\test.inf", "DefaultInstall", dir, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", hr); + + /* try a relative path to the INF, NULL working dir */ + hr = pRunSetupCommand(NULL, "one\\test.inf", "DefaultInstall", NULL, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %d\n", hr); + + /* try a relative path to the INF, empty working dir */ + hr = pRunSetupCommand(NULL, "one\\test.inf", "DefaultInstall", "", "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", hr); + + /* try only the INF filename, with working dir provided */ + hr = pRunSetupCommand(NULL, "test.inf", "DefaultInstall", dir, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %d\n", hr); + + /* try only the INF filename, NULL working dir */ + hr = pRunSetupCommand(NULL, "test.inf", "DefaultInstall", NULL, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %d\n", hr); + + /* try only the INF filename, empty working dir */ + hr = pRunSetupCommand(NULL, "test.inf", "DefaultInstall", "", "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %d\n", hr); + + DeleteFileA("one\\test.inf"); + RemoveDirectoryA("one"); + + create_inf_file("test.inf"); + + /* try INF file in the current directory, working directory provided */ + hr = pRunSetupCommand(NULL, "test.inf", "DefaultInstall", CURR_DIR, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %d\n", hr); + + /* try INF file in the current directory, NULL working directory */ + hr = pRunSetupCommand(NULL, "test.inf", "DefaultInstall", NULL, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), + "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %d\n", hr); + + /* try INF file in the current directory, empty working directory */ + hr = pRunSetupCommand(NULL, "test.inf", "DefaultInstall", CURR_DIR, "Title", NULL, RSC_FLAG_INF | RSC_FLAG_QUIET, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %d\n", hr); +} + +static void test_LaunchINFSection(void) +{ + HRESULT hr; + char cmdline[MAX_PATH]; + static char file[] = "test.inf,DefaultInstall,4,0"; + + /* The 'No UI' flag seems to have no effect whatsoever on Windows. + * So only do this test in interactive mode. + */ + if (winetest_interactive) + { + /* try an invalid cmdline */ + hr = pLaunchINFSection(NULL, NULL, NULL, 0); + ok(hr == 1, "Expected 1, got %d\n", hr); + } + + CreateDirectoryA("one", NULL); + create_inf_file("one\\test.inf"); + + /* try a full path to the INF */ + lstrcpy(cmdline, CURR_DIR); + lstrcat(cmdline, "\\"); + lstrcat(cmdline, "one\\test.inf,DefaultInstall,,4"); + hr = pLaunchINFSection(NULL, NULL, cmdline, 0); + ok(hr == 0, "Expected 0, got %d\n", hr); + + DeleteFileA("one\\test.inf"); + RemoveDirectoryA("one"); + + create_inf_file("test.inf"); + + /* try just the INF filename */ + hr = pLaunchINFSection(NULL, NULL, file, 0); + ok(hr == 0, "Expected 0, got %d\n", hr); + + DeleteFileA("test.inf"); +} + +static void test_LaunchINFSectionEx(void) +{ + HRESULT hr; + char cmdline[MAX_PATH]; + + create_inf_file("test.inf"); + + /* try an invalid CAB filename with an absolute INF name */ + lstrcpy(cmdline, CURR_DIR); + lstrcat(cmdline, "\\"); + lstrcat(cmdline, "test.inf,DefaultInstall,c:imacab.cab,4"); + hr = pLaunchINFSectionEx(NULL, NULL, cmdline, 0); + ok(hr == 0, "Expected 0, got %d\n", hr); + + /* The 'No UI' flag seems to have no effect whatsoever on Windows. + * So only do this test in interactive mode. + */ + if (winetest_interactive) + { + /* try an invalid CAB filename with a relative INF name */ + lstrcpy(cmdline, "test.inf,DefaultInstall,c:imacab.cab,4"); + hr = pLaunchINFSectionEx(NULL, NULL, cmdline, 0); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %d\n", hr); + } + + DeleteFileA("test.inf"); +} + +START_TEST(install) +{ + if (!init_function_pointers()) + return; + + GetCurrentDirectoryA(MAX_PATH, CURR_DIR); + + test_RunSetupCommand(); + test_LaunchINFSection(); + test_LaunchINFSectionEx(); +} diff --git a/rostests/winetests/advpack/testlist.c b/rostests/winetests/advpack/testlist.c new file mode 100644 index 00000000000..3999a51fceb --- /dev/null +++ b/rostests/winetests/advpack/testlist.c @@ -0,0 +1,19 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_advpack(void); +extern void func_files(void); +extern void func_install(void); + +const struct test winetest_testlist[] = +{ + { "advpack", func_advpack }, + { "files", func_files }, + { "install", func_install }, + { 0, 0 } +}; diff --git a/rostests/winetests/browseui/autocomplete.c b/rostests/winetests/browseui/autocomplete.c new file mode 100644 index 00000000000..ba52b66763b --- /dev/null +++ b/rostests/winetests/browseui/autocomplete.c @@ -0,0 +1,320 @@ +/* Unit tests for autocomplete + * + * Copyright 2007 Mikolaj Zalewski + * + * 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 +#include + +#include +#include +#include + +#include "wine/test.h" + +#define stop_on_error(exp) \ +{ \ + HRESULT res = (exp); \ + if (FAILED(res)) \ + { \ + ok(FALSE, #exp " failed: %x\n", res); \ + return; \ + } \ +} + +#define ole_ok(exp) \ +{ \ + HRESULT res = (exp); \ + if (res != S_OK) \ + ok(FALSE, #exp " failed: %x\n", res); \ +} + +LPWSTR strdup_AtoW(LPCSTR str) +{ + int size = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + LPWSTR wstr = (LPWSTR)CoTaskMemAlloc((size + 1)*sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, str, -1, wstr, size+1); + return wstr; +} + +typedef struct +{ + IEnumStringVtbl *vtbl; + IACListVtbl *aclVtbl; + LONG ref; + HRESULT expret; + INT expcount; + INT pos; + INT limit; + const char **data; +} TestACL; + +extern IEnumStringVtbl TestACLVtbl; +extern IACListVtbl TestACL_ACListVtbl; + +static TestACL *impl_from_IACList(IACList *iface) +{ + return (TestACL *)((char *)iface - FIELD_OFFSET(TestACL, aclVtbl)); +} + +static TestACL *TestACL_Constructor(int limit, const char **strings) +{ + TestACL *This = CoTaskMemAlloc(sizeof(TestACL)); + ZeroMemory(This, sizeof(*This)); + This->vtbl = &TestACLVtbl; + This->aclVtbl = &TestACL_ACListVtbl; + This->ref = 1; + This->expret = S_OK; + This->limit = limit; + This->data = strings; + return This; +} + +ULONG STDMETHODCALLTYPE TestACL_AddRef(IEnumString *iface) +{ + TestACL *This = (TestACL *)iface; + trace("ACL(%p): addref (%d)\n", This, This->ref+1); + return InterlockedIncrement(&This->ref); +} + +ULONG STDMETHODCALLTYPE TestACL_Release(IEnumString *iface) +{ + TestACL *This = (TestACL *)iface; + ULONG res; + + res = InterlockedDecrement(&This->ref); + trace("ACL(%p): release (%d)\n", This, res); + return res; +} + +HRESULT STDMETHODCALLTYPE TestACL_QueryInterface(IEnumString *iface, REFIID iid, LPVOID *ppvOut) +{ + TestACL *This = (TestACL *)iface; + *ppvOut = NULL; + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IEnumString)) + { + *ppvOut = iface; + } + else if (IsEqualGUID(iid, &IID_IACList)) + { + *ppvOut = &This->aclVtbl; + } + + if (*ppvOut) + { + iface->lpVtbl->AddRef(iface); + return S_OK; + } + +#if 0 /* IID_IEnumACString not defined yet in wine */ + if (!IsEqualGUID(iid, &IID_IEnumACString)) + trace("unknown interface queried\n"); +#endif + return E_NOINTERFACE; +} + +HRESULT STDMETHODCALLTYPE TestACL_Next(IEnumString *iface, ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) +{ + TestACL *This = (TestACL *)iface; + ULONG i; + + trace("ACL(%p): read %d item(s)\n", This, celt); + for (i = 0; i < celt; i++) + { + if (This->pos >= This->limit) + break; + rgelt[i] = strdup_AtoW(This->data[This->pos]); + This->pos++; + } + + if (pceltFetched) + *pceltFetched = i; + if (i == celt) + return S_OK; + return S_FALSE; +} + +HRESULT STDMETHODCALLTYPE TestACL_Skip(IEnumString *iface, ULONG celt) +{ + ok(FALSE, "Unexpected call to TestACL_Skip\n"); + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE TestACL_Clone(IEnumString *iface, IEnumString **out) +{ + ok(FALSE, "Unexpected call to TestACL_Clone\n"); + return E_OUTOFMEMORY; +} + +HRESULT STDMETHODCALLTYPE TestACL_Reset(IEnumString *iface) +{ + TestACL *This = (TestACL *)iface; + trace("ACL(%p): Reset\n", This); + This->pos = 0; + return S_OK; +} + +HRESULT STDMETHODCALLTYPE TestACL_Expand(IACList *iface, LPCOLESTR str) +{ + TestACL *This = impl_from_IACList(iface); + trace("ACL(%p): Expand\n", impl_from_IACList(iface)); + This->expcount++; + return This->expret; +} + +IEnumStringVtbl TestACLVtbl = +{ + TestACL_QueryInterface, + TestACL_AddRef, + TestACL_Release, + + TestACL_Next, + TestACL_Skip, + TestACL_Reset, + TestACL_Clone +}; + +ULONG STDMETHODCALLTYPE TestACL_ACList_AddRef(IACList *iface) +{ + return TestACL_AddRef((IEnumString *)impl_from_IACList(iface)); +} + +ULONG STDMETHODCALLTYPE TestACL_ACList_Release(IACList *iface) +{ + return TestACL_Release((IEnumString *)impl_from_IACList(iface)); +} + +HRESULT STDMETHODCALLTYPE TestACL_ACList_QueryInterface(IACList *iface, REFIID iid, LPVOID *ppvout) +{ + return TestACL_QueryInterface((IEnumString *)impl_from_IACList(iface), iid, ppvout); +} + +IACListVtbl TestACL_ACListVtbl = +{ + TestACL_ACList_QueryInterface, + TestACL_ACList_AddRef, + TestACL_ACList_Release, + + TestACL_Expand +}; + +#define expect_str(obj, str) \ +{ \ + ole_ok(obj->lpVtbl->Next(obj, 1, &wstr, &i)); \ + ok(i == 1, "Expected i == 1, got %d\n", i); \ + ok(str[0] == wstr[0], "String mismatch\n"); \ +} + +#define expect_end(obj) \ + ok(obj->lpVtbl->Next(obj, 1, &wstr, &i) == S_FALSE, "Unexpected return from Next\n"); + +static void test_ACLMulti(void) +{ + const char *strings1[] = {"a", "c", "e"}; + const char *strings2[] = {"a", "b", "d"}; + WCHAR exp[] = {'A','B','C',0}; + IEnumString *obj; + TestACL *acl1, *acl2; + IACList *acl; + IObjMgr *mgr; + LPWSTR wstr; + LPWSTR wstrtab[15]; + LPVOID tmp; + UINT i; + + stop_on_error(CoCreateInstance(&CLSID_ACLMulti, NULL, CLSCTX_INPROC, &IID_IEnumString, (LPVOID *)&obj)); + stop_on_error(obj->lpVtbl->QueryInterface(obj, &IID_IACList, (LPVOID *)&acl)); + ok(obj->lpVtbl->QueryInterface(obj, &IID_IACList2, &tmp) == E_NOINTERFACE, + "Unexpected interface IACList2 in ACLMulti\n"); + stop_on_error(obj->lpVtbl->QueryInterface(obj, &IID_IObjMgr, (LPVOID *)&mgr)); +#if 0 /* IID_IEnumACString not defined yet in wine */ + ole_ok(obj->lpVtbl->QueryInterface(obj, &IID_IEnumACString, &unk)); + if (unk != NULL) + unk->lpVtbl->Release(unk); +#endif + + ok(obj->lpVtbl->Next(obj, 1, (LPOLESTR *)&tmp, &i) == S_FALSE, "Unexpected return from Next\n"); + ok(i == 0, "Unexpected fetched value %d\n", i); + ok(obj->lpVtbl->Next(obj, 44, (LPOLESTR *)&tmp, &i) == S_FALSE, "Unexpected return from Next\n"); + ok(obj->lpVtbl->Skip(obj, 1) == E_NOTIMPL, "Unexpected return from Skip\n"); + ok(obj->lpVtbl->Clone(obj, (IEnumString **)&tmp) == E_OUTOFMEMORY, "Unexpected return from Clone\n"); + ole_ok(acl->lpVtbl->Expand(acl, exp)); + + acl1 = TestACL_Constructor(3, strings1); + acl2 = TestACL_Constructor(3, strings2); + stop_on_error(mgr->lpVtbl->Append(mgr, (IUnknown *)acl1)); + stop_on_error(mgr->lpVtbl->Append(mgr, (IUnknown *)acl2)); + ok(mgr->lpVtbl->Append(mgr, NULL) == E_FAIL, "Unexpected return from Append\n"); + expect_str(obj, "a"); + expect_str(obj, "c"); + expect_str(obj, "e"); + expect_str(obj, "a"); + expect_str(obj, "b"); + expect_str(obj, "d"); + expect_end(obj); + + ole_ok(obj->lpVtbl->Reset(obj)); + ok(acl1->pos == 0, "acl1 not reset\n"); + ok(acl2->pos == 0, "acl2 not reset\n"); + + ole_ok(acl->lpVtbl->Expand(acl, exp)); + ok(acl1->expcount == 1, "expcount - expected 1, got %d\n", acl1->expcount); + ok(acl2->expcount == 0, "expcount - expected 0, got %d\n", acl2->expcount); + + ole_ok(obj->lpVtbl->Next(obj, 15, wstrtab, &i)); + ok(i == 1, "Expected i == 1, got %d\n", i); + ole_ok(obj->lpVtbl->Next(obj, 15, wstrtab, &i)); + ole_ok(obj->lpVtbl->Next(obj, 15, wstrtab, &i)); + ole_ok(obj->lpVtbl->Next(obj, 15, wstrtab, &i)); + ole_ok(acl->lpVtbl->Expand(acl, exp)); + ok(acl1->expcount == 2, "expcount - expected 1, got %d\n", acl1->expcount); + ok(acl2->expcount == 0, "expcount - expected 0, got %d\n", acl2->expcount); + acl1->expret = S_FALSE; + ole_ok(acl->lpVtbl->Expand(acl, exp)); + ok(acl1->expcount == 3, "expcount - expected 1, got %d\n", acl1->expcount); + ok(acl2->expcount == 1, "expcount - expected 0, got %d\n", acl2->expcount); + acl1->expret = E_NOTIMPL; + ole_ok(acl->lpVtbl->Expand(acl, exp)); + ok(acl1->expcount == 4, "expcount - expected 1, got %d\n", acl1->expcount); + ok(acl2->expcount == 2, "expcount - expected 0, got %d\n", acl2->expcount); + acl2->expret = E_OUTOFMEMORY; + ok(acl->lpVtbl->Expand(acl, exp) == E_OUTOFMEMORY, "Unexpected Expand return\n"); + acl2->expret = E_FAIL; + ok(acl->lpVtbl->Expand(acl, exp) == E_FAIL, "Unexpected Expand return\n"); + + stop_on_error(mgr->lpVtbl->Remove(mgr, (IUnknown *)acl1)); + ok(acl1->ref == 1, "acl1 not released\n"); + expect_end(obj); + obj->lpVtbl->Reset(obj); + expect_str(obj, "a"); + expect_str(obj, "b"); + expect_str(obj, "d"); + expect_end(obj); + + obj->lpVtbl->Release(obj); + acl->lpVtbl->Release(acl); + ok(mgr->lpVtbl->Release(mgr) == 0, "Unexpected references\n"); + ok(acl1->ref == 1, "acl1 not released\n"); + ok(acl2->ref == 1, "acl2 not released\n"); +} + +START_TEST(autocomplete) +{ + CoInitialize(NULL); + test_ACLMulti(); + CoUninitialize(); +} diff --git a/rostests/winetests/browseui/browseui.rbuild b/rostests/winetests/browseui/browseui.rbuild new file mode 100644 index 00000000000..66b7b4dc51e --- /dev/null +++ b/rostests/winetests/browseui/browseui.rbuild @@ -0,0 +1,15 @@ + + + + . + 0x600 + 0x600 + wine + ole32 + user32 + kernel32 + uuid + ntdll + autocomplete.c + testlist.c + diff --git a/rostests/winetests/browseui/testlist.c b/rostests/winetests/browseui/testlist.c new file mode 100644 index 00000000000..89bfdebb386 --- /dev/null +++ b/rostests/winetests/browseui/testlist.c @@ -0,0 +1,15 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_autocomplete(void); + +const struct test winetest_testlist[] = +{ + { "autocomplete", func_autocomplete }, + { 0, 0 } +}; diff --git a/rostests/winetests/cabinet/cabinet.rbuild b/rostests/winetests/cabinet/cabinet.rbuild index e87353b7bc7..c119392bc1a 100644 --- a/rostests/winetests/cabinet/cabinet.rbuild +++ b/rostests/winetests/cabinet/cabinet.rbuild @@ -1,9 +1,15 @@ - - . - - cabinet - kernel32 - ntdll - extract.c - testlist.c + + + + . + 0x600 + 0x600 + wine + cabinet + user32 + kernel32 + ntdll + extract.c + fdi.c + testlist.c diff --git a/rostests/winetests/cabinet/extract.c b/rostests/winetests/cabinet/extract.c index b4092872e55..2cf5154b7af 100644 --- a/rostests/winetests/cabinet/extract.c +++ b/rostests/winetests/cabinet/extract.c @@ -20,7 +20,8 @@ #include #include -#include +#include "fci.h" +#include "fdi.h" #include "wine/test.h" /* make the max size large so there is only one cab file */ @@ -31,41 +32,39 @@ * because they are undocumented in windows. */ -/* EXTRACTdest flags */ +/* SESSION Operation */ #define EXTRACT_FILLFILELIST 0x00000001 #define EXTRACT_EXTRACTFILES 0x00000002 -struct ExtractFileList { - LPSTR filename; - struct ExtractFileList *next; - BOOL unknown; /* always 1L */ +struct FILELIST{ + LPSTR FileName; + struct FILELIST *next; + BOOL DoExtract; }; -/* 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; + INT FileSize; + ERF Error; + struct FILELIST *FileList; + INT FileCount; + INT Operation; + CHAR Destination[MAX_PATH]; + CHAR CurrentFile[MAX_PATH]; + CHAR Reserved[MAX_PATH]; + struct FILELIST *FilterList; +} SESSION; /* function pointers */ HMODULE hCabinet; -static HRESULT (WINAPI *pExtract)(EXTRACTDEST*, LPCSTR); +static HRESULT (WINAPI *pExtract)(SESSION*, LPCSTR); CHAR CURR_DIR[MAX_PATH]; static void init_function_pointers(void) { - hCabinet = LoadLibraryA("cabinet.dll"); + hCabinet = GetModuleHandleA("cabinet.dll"); - if (hCabinet) - { - pExtract = (void *)GetProcAddress(hCabinet, "Extract"); - } + pExtract = (void *)GetProcAddress(hCabinet, "Extract"); } /* creates a file with the specified name for tests */ @@ -144,9 +143,10 @@ static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv) 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; + /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */ + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES) dwCreateDisposition = OPEN_EXISTING; @@ -166,7 +166,7 @@ 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"); @@ -197,7 +197,7 @@ 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"); @@ -247,7 +247,7 @@ static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime, res = GetFileInformationByHandle(handle, &finfo); ok(res, "Expected GetFileInformationByHandle to succeed\n"); - + FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime); FileTimeToDosDateTime(&filetime, pdate, ptime); @@ -288,6 +288,10 @@ static void create_cab_file(void) CCAB cabParams; HFCI hfci; ERF erf; + static CHAR a_txt[] = "a.txt", + b_txt[] = "b.txt", + testdir_c_txt[] = "testdir\\c.txt", + testdir_d_txt[] = "testdir\\d.txt"; BOOL res; set_cab_parameters(&cabParams); @@ -298,10 +302,10 @@ static void create_cab_file(void) 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"); + 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"); @@ -310,10 +314,26 @@ static void create_cab_file(void) ok(res, "Failed to destroy the cabinet\n"); } +static BOOL check_list(struct FILELIST **node, const char *filename, BOOL do_extract) +{ + if (!*node) + return FALSE; + + if (lstrcmpA((*node)->FileName, filename)) + return FALSE; + + if ((*node)->DoExtract != do_extract) + return FALSE; + + *node = (*node)->next; + return TRUE; +} + static void test_Extract(void) { - EXTRACTDEST extractDest; + SESSION session; HRESULT res; + struct FILELIST *node; /* native windows crashes if * - invalid parameters are sent in @@ -322,58 +342,341 @@ static void test_Extract(void) */ /* 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); + ZeroMemory(&session, sizeof(SESSION)); + lstrcpyA(session.Destination, "dest"); + session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES; + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount); + ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES), + "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); 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(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", FALSE), "list entry wrong\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); + ZeroMemory(&session, sizeof(SESSION)); + lstrcpyA(session.Destination, "dest"); + session.Operation = EXTRACT_FILLFILELIST; + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount); + ok(session.Operation == EXTRACT_FILLFILELIST, + "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); 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); + ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n"); + ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n"); /* 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); + session.Operation = EXTRACT_EXTRACTFILES; + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount); + ok(session.Operation == EXTRACT_EXTRACTFILES, + "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); 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"); + ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", FALSE), "list entry wrong\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); + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount); + ok(session.Operation == EXTRACT_EXTRACTFILES, + "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n"); + ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n"); ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n"); + ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n"); + ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n"); /* remove two of the files in the list */ - extractDest.filelist->next = extractDest.filelist->next->next; - extractDest.filelist->next->next = NULL; + session.FileList->next = session.FileList->next->next; + session.FileList->next->next = NULL; + session.FilterList = NULL; CreateDirectoryA("dest", NULL); - res = pExtract(&extractDest, "extract.cab"); - ok(res == S_OK, "Expected S_OK, got %ld\n", res); + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount); + ok(session.Operation == EXTRACT_EXTRACTFILES, + "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); 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(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n"); + ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n"); + + session.Operation = EXTRACT_FILLFILELIST; + session.FileList = NULL; + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount); + ok(session.Operation == EXTRACT_FILLFILELIST, + "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); + 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(!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(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n"); + ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n"); + + session.Operation = 0; + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount); + ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); + 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 exist\n"); + ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n"); + ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n"); + ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n"); + + session.Operation = 0; + session.FilterList = session.FileList; + res = pExtract(&session, "extract.cab"); + node = session.FileList; + ok(res == S_OK, "Expected S_OK, got %d\n", res); + ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_NONE, + "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError); + ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount); + ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"), + "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + 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 exist\n"); + ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n"); + ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n"); + node = session.FilterList; + ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n"); + + /* cabinet does not exist */ + ZeroMemory(&session, sizeof(SESSION)); + lstrcpyA(session.Destination, "dest"); + session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES; + res = pExtract(&session, "nonexistent.cab"); + node = session.FileList; + ok(res == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %08x\n", res); + ok(session.Error.erfOper == FDIERROR_CABINET_NOT_FOUND, + "Expected FDIERROR_CABINET_NOT_FOUND, got %d\n", session.Error.erfOper); + ok(session.FileSize == 0, "Expected 0, got %d\n", session.FileSize); + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError); + ok(session.FileCount == 0, "Expected 0, got %d\n", session.FileCount); + ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES), + "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!*session.CurrentFile, "Expected empty string, got %s\n", session.CurrentFile); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); + ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n"); + ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n"); + ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n"); + ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n"); + ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n"); + ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n"); + ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n"); + ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n"); + + /* first file exists */ + createTestFile("dest\\a.txt"); + SetFileAttributes("dest\\a.txt", FILE_ATTRIBUTE_READONLY); + ZeroMemory(&session, sizeof(SESSION)); + lstrcpyA(session.Destination, "dest"); + session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES; + res = pExtract(&session, "extract.cab"); + node = session.FileList; + todo_wine + { + ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL, + "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res); + ok(session.FileSize == 6, "Expected 6, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_USER_ABORT, + "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper); + ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError); + ok(session.FileCount == 1, "Expected 1, got %d\n", session.FileCount); + ok(!lstrcmpA(session.CurrentFile, "dest\\a.txt"), + "Expected dest\\a.txt, got %s\n", session.CurrentFile); + } + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES), + "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); + ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n"); + todo_wine + { + ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n"); + ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n"); + ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n"); + ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n"); + ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n"); + ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n"); + } + ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n"); + + SetFileAttributesA("dest\\a.txt", FILE_ATTRIBUTE_NORMAL); + DeleteFileA("dest\\a.txt"); + + /* third file exists */ + createTestFile("dest\\testdir\\c.txt"); + SetFileAttributes("dest\\testdir\\c.txt", FILE_ATTRIBUTE_READONLY); + ZeroMemory(&session, sizeof(SESSION)); + lstrcpyA(session.Destination, "dest"); + session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES; + res = pExtract(&session, "extract.cab"); + todo_wine + { + ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL, + "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res); + ok(session.FileSize == 26, "Expected 26, got %d\n", session.FileSize); + ok(session.Error.erfOper == FDIERROR_USER_ABORT, + "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper); + ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError); + ok(session.FileCount == 3, "Expected 3, got %d\n", session.FileCount); + ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\c.txt"), + "Expected dest\\c.txt, got %s\n", session.CurrentFile); + } + ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType); + ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES), + "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation); + ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination); + ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved); + ok(!session.FilterList, "Expected empty filter list\n"); + 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 not exist\n"); + todo_wine + { + ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n"); + } + ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n"); + ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n"); + ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n"); + ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n"); + + SetFileAttributesA("dest\\testdir\\c.txt", FILE_ATTRIBUTE_NORMAL); + DeleteFileA("dest\\testdir\\c.txt"); + ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n"); - ok(RemoveDirectoryA("dest"), "Expected dest\\testdir to exist\n"); + ok(RemoveDirectoryA("dest"), "Expected dest to exist\n"); } START_TEST(extract) diff --git a/rostests/winetests/cabinet/fdi.c b/rostests/winetests/cabinet/fdi.c new file mode 100644 index 00000000000..7c10b3d1663 --- /dev/null +++ b/rostests/winetests/cabinet/fdi.c @@ -0,0 +1,580 @@ +/* + * Unit tests for the File Decompression Interface + * + * 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 +#include +#include "fci.h" +#include "fdi.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 + +CHAR CURR_DIR[MAX_PATH]; + +/* FDI callbacks */ + +static void *fdi_alloc(ULONG cb) +{ + return HeapAlloc(GetProcessHeap(), 0, cb); +} + +static void *fdi_alloc_bad(ULONG cb) +{ + return NULL; +} + +static void fdi_free(void *pv) +{ + HeapFree(GetProcessHeap(), 0, pv); +} + +static INT_PTR fdi_open(char *pszFile, int oflag, int pmode) +{ + HANDLE handle; + handle = CreateFileA(pszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, 0, NULL ); + if (handle == INVALID_HANDLE_VALUE) + return 0; + return (INT_PTR) handle; +} + +static UINT fdi_read(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE) hf; + DWORD dwRead; + if (ReadFile(handle, pv, cb, &dwRead, NULL)) + return dwRead; + return 0; +} + +static UINT fdi_write(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE) hf; + DWORD dwWritten; + if (WriteFile(handle, pv, cb, &dwWritten, NULL)) + return dwWritten; + return 0; +} + +static int fdi_close(INT_PTR hf) +{ + HANDLE handle = (HANDLE) hf; + return CloseHandle(handle) ? 0 : -1; +} + +static long fdi_seek(INT_PTR hf, long dist, int seektype) +{ + HANDLE handle = (HANDLE) hf; + return SetFilePointer(handle, dist, NULL, seektype); +} + +static void test_FDICreate(void) +{ + HFDI hfdi; + ERF erf; + + erf.erfOper = 0xcafefeed; + erf.erfType = 0xdeadbabe; + erf.fError = 0xdecaface; + + /* native crashes if pfnalloc is NULL */ + + /* FDICreate does not crash with a NULL pfnfree, + * but FDIDestroy will crash when it tries to access it. + */ + if (0) + { + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, NULL, fdi_open, fdi_read, + fdi_write, fdi_close, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + } + + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, fdi_free, NULL, fdi_read, + fdi_write, fdi_close, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, NULL, + fdi_write, fdi_close, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + NULL, fdi_close, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + fdi_write, NULL, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + fdi_write, fdi_close, NULL, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + fdi_write, fdi_close, fdi_seek, + cpuUNKNOWN, NULL); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + + /* bad cpu type */ + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + fdi_write, fdi_close, fdi_seek, + 0xcafebabe, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper); + ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType); + ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError); + + FDIDestroy(hfdi); + + /* pfnalloc fails */ + SetLastError(0xdeadbeef); + hfdi = FDICreate(fdi_alloc_bad, fdi_free, fdi_open, fdi_read, + fdi_write, fdi_close, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi == NULL, "Expected NULL context, got %p\n", hfdi); + ok(erf.erfOper == FDIERROR_ALLOC_FAIL, + "Expected FDIERROR_ALLOC_FAIL, got %d\n", erf.erfOper); + ok(erf.fError == TRUE, "Expected TRUE, got %d\n", erf.fError); + todo_wine + { + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(erf.erfType == 0, "Expected 0, got %d\n", erf.erfType); + } +} + +static void test_FDIDestroy(void) +{ + HFDI hfdi; + ERF erf; + BOOL ret; + + /* native crashes if hfdi is NULL or invalid */ + + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + fdi_write, fdi_close, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + + /* successfully destroy hfdi */ + ret = FDIDestroy(hfdi); + ok(ret == TRUE, "Expected TRUE, got %d\n", ret); + + /* native crashes if you try to destroy hfdi twice */ + if (0) + { + /* try to destroy hfdi again */ + ret = FDIDestroy(hfdi); + ok(ret == TRUE, "Expected TRUE, got %d\n", ret); + } +} + +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"); +} + +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"); +} + +/* 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; + /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */ + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + + 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"); + /* fixme: should convert attrs to *pattribs, make sure + * have a test that catches the fact that we don't? + */ + + 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; + static CHAR a_txt[] = "a.txt", + b_txt[] = "b.txt", + testdir_c_txt[] = "testdir\\c.txt", + testdir_d_txt[] = "testdir\\d.txt"; + 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_FDIIsCabinet(void) +{ + ERF erf; + BOOL ret; + HFDI hfdi; + INT_PTR fd; + FDICABINETINFO cabinfo; + char temp[] = "temp.txt"; + char extract[] = "extract.cab"; + + create_test_files(); + create_cab_file(); + + hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read, + fdi_write, fdi_close, fdi_seek, + cpuUNKNOWN, &erf); + ok(hfdi != NULL, "Expected non-NULL context\n"); + + /* native crashes if hfdi or cabinfo are NULL or invalid */ + + /* invalid file handle */ + ZeroMemory(&cabinfo, sizeof(FDICABINETINFO)); + SetLastError(0xdeadbeef); + ret = FDIIsCabinet(hfdi, (int)INVALID_HANDLE_VALUE, &cabinfo); + ok(ret == FALSE, "Expected FALSE, got %d\n", ret); + ok(GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + ok(cabinfo.cbCabinet == 0, "Expected 0, got %ld\n", cabinfo.cbCabinet); + ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles); + ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders); + ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet); + ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID); + + createTestFile("temp.txt"); + fd = fdi_open(temp, 0, 0); + + /* file handle doesn't point to a cabinet */ + ZeroMemory(&cabinfo, sizeof(FDICABINETINFO)); + SetLastError(0xdeadbeef); + ret = FDIIsCabinet(hfdi, fd, &cabinfo); + ok(ret == FALSE, "Expected FALSE, got %d\n", ret); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(cabinfo.cbCabinet == 0, "Expected 0, got %ld\n", cabinfo.cbCabinet); + ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles); + ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders); + ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet); + ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID); + + fdi_close(fd); + DeleteFileA("temp.txt"); + + /* try a real cab */ + fd = fdi_open(extract, 0, 0); + ZeroMemory(&cabinfo, sizeof(FDICABINETINFO)); + SetLastError(0xdeadbeef); + ret = FDIIsCabinet(hfdi, fd, &cabinfo); + ok(ret == TRUE, "Expected TRUE, got %d\n", ret); + ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(cabinfo.cFiles == 4, "Expected 4, got %d\n", cabinfo.cFiles); + ok(cabinfo.cFolders == 1, "Expected 1, got %d\n", cabinfo.cFolders); + ok(cabinfo.setID == 0xbeef, "Expected 0xbeef, got %d\n", cabinfo.setID); + todo_wine + { + ok(cabinfo.cbCabinet == 182, "Expected 182, got %ld\n", cabinfo.cbCabinet); + ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet); + } + + fdi_close(fd); + FDIDestroy(hfdi); + delete_test_files(); +} + +START_TEST(fdi) +{ + test_FDICreate(); + test_FDIDestroy(); + test_FDIIsCabinet(); +} diff --git a/rostests/winetests/cabinet/testlist.c b/rostests/winetests/cabinet/testlist.c index b83778643d4..992cd35d4ef 100644 --- a/rostests/winetests/cabinet/testlist.c +++ b/rostests/winetests/cabinet/testlist.c @@ -1,3 +1,5 @@ +/* Automatically generated file; DO NOT EDIT!! */ + #define WIN32_LEAN_AND_MEAN #include @@ -5,9 +7,11 @@ #include "wine/test.h" extern void func_extract(void); +extern void func_fdi(void); const struct test winetest_testlist[] = { { "extract", func_extract }, + { "fdi", func_fdi }, { 0, 0 } }; diff --git a/rostests/winetests/comcat/comcat.c b/rostests/winetests/comcat/comcat.c new file mode 100644 index 00000000000..914cbe8b2a2 --- /dev/null +++ b/rostests/winetests/comcat/comcat.c @@ -0,0 +1,129 @@ +/* + * tests for comcat functions + * + * Copyright 2006 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 +#include + +#include "objbase.h" +#include "comcat.h" + +#include "wine/test.h" + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x \n", hr) + +static void register_testentry(void) +{ + HKEY hkey,hkey2; + + RegCreateKeyA(HKEY_CLASSES_ROOT,"CLSID\\{deadcafe-beed-bead-dead-cafebeaddead}", + &hkey); + RegSetValueA(hkey,NULL,REG_SZ,"ComCat Test key",16); + RegCreateKeyA(hkey, + "Implemented Categories\\{deadcafe-0000-0000-0000-000000000000}", + &hkey2); + + RegCloseKey(hkey); + RegCloseKey(hkey2); +} + +static void unregister_testentry(void) +{ + RegDeleteKeyA(HKEY_CLASSES_ROOT, + "CLSID\\{deadcafe-beed-bead-dead-cafebeaddead}\\Implemented Categories\\{deadcafe-0000-0000-0000-000000000000}"); + RegDeleteKeyA(HKEY_CLASSES_ROOT, + "CLSID\\{deadcafe-beed-bead-dead-cafebeaddead}\\Implemented Categories"); + RegDeleteKeyA(HKEY_CLASSES_ROOT, + "CLSID\\{deadcafe-beed-bead-dead-cafebeaddead}"); +} + +static void do_enum(void) +{ + HRESULT hr; + REFCLSID rclsid = &CLSID_StdComponentCategoriesMgr; + ICatInformation *pICat = (ICatInformation*)0xdeadbeef; + GUID the_guid[1]; + GUID the_cat[1]; + GUID wanted_guid; + ULONG fetched = -1; + + static WCHAR szCatID[] = { + '{', + 'd','e','a','d','c','a','f','e', + '-','0','0','0','0','-','0','0','0','0', + '-','0','0','0','0', + '-','0','0','0','0','0','0','0','0','0','0','0','0', + '}',0}; + static WCHAR szGuid[] = { + '{', + 'd','e','a','d','c','a','f','e','-', + 'b','e','e','d','-', + 'b','e','a','d','-', + 'd','e','a','d','-', + 'c','a','f','e','b','e','a','d','d','e','a','d', + '}',0}; + + IEnumCLSID *pIEnum =(IEnumCLSID*)0xdeadcafe; + + CLSIDFromString((LPOLESTR)szCatID,the_cat); + CLSIDFromString((LPOLESTR)szGuid,&wanted_guid); + + OleInitialize(NULL); + + hr = CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER, + &IID_ICatInformation, (void **)&pICat); + ok_ole_success(hr, "CoCreateInstance"); + + hr = ICatInformation_EnumClassesOfCategories(pICat, -1, NULL, -1, NULL, + &pIEnum); + ok_ole_success(hr,"ICatInformation_EnumClassesOfCategories"); + + IEnumGUID_Release(pIEnum); + + hr = ICatInformation_EnumClassesOfCategories(pICat, 1, the_cat, -1, NULL, + &pIEnum); + ok_ole_success(hr,"ICatInformation_EnumClassesOfCategories"); + + hr = IEnumGUID_Next(pIEnum,1,the_guid, &fetched); + ok (fetched == 0,"Fetched wrong number of guids %u\n",fetched); + IEnumGUID_Release(pIEnum); + + register_testentry(); + hr = ICatInformation_EnumClassesOfCategories(pICat, 1, the_cat, -1, NULL, + &pIEnum); + ok_ole_success(hr,"ICatInformation_EnumClassesOfCategories"); + + hr = IEnumGUID_Next(pIEnum,1,the_guid, &fetched); + ok (fetched == 1,"Fetched wrong number of guids %u\n",fetched); + ok (IsEqualGUID(the_guid,&wanted_guid),"Guids do not match\n"); + + IEnumGUID_Release(pIEnum); + ICatInformation_Release(pICat); + unregister_testentry(); + + OleUninitialize(); +} + + +START_TEST(comcat) +{ + do_enum(); +} diff --git a/rostests/winetests/comcat/comcat.rbuild b/rostests/winetests/comcat/comcat.rbuild new file mode 100644 index 00000000000..7fe8a38683f --- /dev/null +++ b/rostests/winetests/comcat/comcat.rbuild @@ -0,0 +1,15 @@ + + + + . + 0x600 + 0x600 + wine + ole32 + advapi32 + kernel32 + uuid + ntdll + comcat.c + testlist.c + diff --git a/rostests/winetests/comcat/testlist.c b/rostests/winetests/comcat/testlist.c new file mode 100644 index 00000000000..ba94e96b2a6 --- /dev/null +++ b/rostests/winetests/comcat/testlist.c @@ -0,0 +1,15 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_comcat(void); + +const struct test winetest_testlist[] = +{ + { "comcat", func_comcat }, + { 0, 0 } +}; diff --git a/rostests/winetests/comdlg32/comdlg32.rbuild b/rostests/winetests/comdlg32/comdlg32.rbuild index 2b7ed263c46..fc3c02ecb86 100644 --- a/rostests/winetests/comdlg32/comdlg32.rbuild +++ b/rostests/winetests/comdlg32/comdlg32.rbuild @@ -1,15 +1,15 @@ - - . - - 0x600 - 0x501 - 0x501 - wine - comdlg32 - user32 - kernel32 - ntdll - filedlg.c - printdlg.c - testlist.c - + + + + . + 0x600 + 0x600 + wine + comdlg32 + user32 + kernel32 + ntdll + filedlg.c + printdlg.c + testlist.c + diff --git a/rostests/winetests/comdlg32/filedlg.c b/rostests/winetests/comdlg32/filedlg.c index 698af1ac036..03192bab31e 100644 --- a/rostests/winetests/comdlg32/filedlg.c +++ b/rostests/winetests/comdlg32/filedlg.c @@ -1,116 +1,116 @@ -/* - * Unit test suite for comdlg32 API functions: file dialogs - * - * Copyright 2007 Google (Lei Zhang) - * - * 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 -#include - - -/* ##### */ - -static UINT CALLBACK OFNHookProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - LPNMHDR nmh; - - if( msg == WM_NOTIFY) - { - nmh = (LPNMHDR) lParam; - if( nmh->code == CDN_INITDONE) - { - PostMessage( GetParent(hDlg), WM_COMMAND, IDCANCEL, FALSE); - } - } - - return 0; -} - -/* bug 6829 */ -static void test_DialogCancel(void) -{ - OPENFILENAMEA ofn; - BOOL result; - char szFileName[MAX_PATH] = ""; - - ZeroMemory(&ofn, sizeof(ofn)); - - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = NULL; - ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0"; - ofn.lpstrFile = szFileName; - ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLEHOOK; - ofn.lpstrDefExt = "txt"; - ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProc; - - PrintDlgA(NULL); - ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", - CDERR_INITIALIZATION, CommDlgExtendedError()); - - result = GetOpenFileNameA(&ofn); - ok(0 == result, "expected %d, got %d\n", 0, result); - ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, - CommDlgExtendedError()); - - PrintDlgA(NULL); - ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", - CDERR_INITIALIZATION, CommDlgExtendedError()); - - SetLastError(0xdeadbeef); - result = GetOpenFileNameW((LPOPENFILENAMEW) &ofn); - if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) - skip("GetOpenFileNameW is not implemented\n"); - else - { - ok(0 == result, "expected %d, got %d\n", 0, result); - ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, - CommDlgExtendedError()); - } - - PrintDlgA(NULL); - ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", - CDERR_INITIALIZATION, CommDlgExtendedError()); - - result = GetSaveFileNameA(&ofn); - ok(0 == result, "expected %d, got %d\n", 0, result); - ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, - CommDlgExtendedError()); - - PrintDlgA(NULL); - ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", - CDERR_INITIALIZATION, CommDlgExtendedError()); - - SetLastError(0xdeadbeef); - result = GetSaveFileNameW((LPOPENFILENAMEW) &ofn); - if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) - skip("GetSaveFileNameW is not implemented\n"); - else - { - ok(0 == result, "expected %d, got %d\n", 0, result); - ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, - CommDlgExtendedError()); - } -} - - -START_TEST(filedlg) -{ - test_DialogCancel(); - -} +/* + * Unit test suite for comdlg32 API functions: file dialogs + * + * Copyright 2007 Google (Lei Zhang) + * + * 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 +#include + + +/* ##### */ + +static UINT CALLBACK OFNHookProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + LPNMHDR nmh; + + if( msg == WM_NOTIFY) + { + nmh = (LPNMHDR) lParam; + if( nmh->code == CDN_INITDONE) + { + PostMessage( GetParent(hDlg), WM_COMMAND, IDCANCEL, FALSE); + } + } + + return 0; +} + +/* bug 6829 */ +static void test_DialogCancel(void) +{ + OPENFILENAMEA ofn; + BOOL result; + char szFileName[MAX_PATH] = ""; + + ZeroMemory(&ofn, sizeof(ofn)); + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL; + ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0"; + ofn.lpstrFile = szFileName; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLEHOOK; + ofn.lpstrDefExt = "txt"; + ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProc; + + PrintDlgA(NULL); + ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", + CDERR_INITIALIZATION, CommDlgExtendedError()); + + result = GetOpenFileNameA(&ofn); + ok(0 == result, "expected %d, got %d\n", 0, result); + ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, + CommDlgExtendedError()); + + PrintDlgA(NULL); + ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", + CDERR_INITIALIZATION, CommDlgExtendedError()); + + SetLastError(0xdeadbeef); + result = GetOpenFileNameW((LPOPENFILENAMEW) &ofn); + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + skip("GetOpenFileNameW is not implemented\n"); + else + { + ok(0 == result, "expected %d, got %d\n", 0, result); + ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, + CommDlgExtendedError()); + } + + PrintDlgA(NULL); + ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", + CDERR_INITIALIZATION, CommDlgExtendedError()); + + result = GetSaveFileNameA(&ofn); + ok(0 == result, "expected %d, got %d\n", 0, result); + ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, + CommDlgExtendedError()); + + PrintDlgA(NULL); + ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n", + CDERR_INITIALIZATION, CommDlgExtendedError()); + + SetLastError(0xdeadbeef); + result = GetSaveFileNameW((LPOPENFILENAMEW) &ofn); + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + skip("GetSaveFileNameW is not implemented\n"); + else + { + ok(0 == result, "expected %d, got %d\n", 0, result); + ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0, + CommDlgExtendedError()); + } +} + + +START_TEST(filedlg) +{ + test_DialogCancel(); + +} diff --git a/rostests/winetests/comdlg32/printdlg.c b/rostests/winetests/comdlg32/printdlg.c index 2748248047a..783773f1adc 100644 --- a/rostests/winetests/comdlg32/printdlg.c +++ b/rostests/winetests/comdlg32/printdlg.c @@ -1,153 +1,312 @@ -/* - * Unit test suite for comdlg32 API functions: printer dialogs - * - * Copyright 2006 Detlef Riekenberg - * - * 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 - -#include "windef.h" -#include "winbase.h" -#include "winerror.h" -#include "wingdi.h" -#include "wingdi.h" -#include "winuser.h" - -#include "cderr.h" -#include "commdlg.h" - -#include "wine/test.h" - - -/* ######## */ - -static void test_PageSetupDlgA(void) -{ - LPPAGESETUPDLGA pDlg; - DWORD res; - - pDlg = HeapAlloc(GetProcessHeap(), 0, (sizeof(PAGESETUPDLGA)) * 2); - if (!pDlg) return; - - SetLastError(0xdeadbeef); - res = PageSetupDlgA(NULL); - ok( !res && (CommDlgExtendedError() == CDERR_INITIALIZATION), - "returned %u with %u and 0x%x (expected '0' and " - "CDERR_INITIALIZATION)\n", res, GetLastError(), CommDlgExtendedError()); - - ZeroMemory(pDlg, sizeof(PAGESETUPDLGA)); - pDlg->lStructSize = sizeof(PAGESETUPDLGA) -1; - SetLastError(0xdeadbeef); - res = PageSetupDlgA(pDlg); - ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), - "returned %u with %u and 0x%x (expected '0' and " - "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError()); - - ZeroMemory(pDlg, sizeof(PAGESETUPDLGA)); - pDlg->lStructSize = sizeof(PAGESETUPDLGA) +1; - pDlg->Flags = PSD_RETURNDEFAULT; - SetLastError(0xdeadbeef); - res = PageSetupDlgA(pDlg); - ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), - "returned %u with %u and 0x%x (expected '0' and CDERR_STRUCTSIZE)\n", - res, GetLastError(), CommDlgExtendedError()); - - - ZeroMemory(pDlg, sizeof(PAGESETUPDLGA)); - pDlg->lStructSize = sizeof(PAGESETUPDLGA); - pDlg->Flags = PSD_RETURNDEFAULT; - SetLastError(0xdeadbeef); - res = PageSetupDlgA(pDlg); - trace("after pagesetupdlga res = %d, le %d, ext error 0x%x\n", - res, GetLastError(), CommDlgExtendedError()); - ok( res || (CommDlgExtendedError() == PDERR_NODEFAULTPRN), - "returned %u with %u and 0x%x (expected '!= 0' or '0' and " - "PDERR_NODEFAULTPRN)\n", res, GetLastError(), CommDlgExtendedError()); - if (!res && (CommDlgExtendedError() == PDERR_NODEFAULTPRN)) { - skip("No printer configured.\n"); - HeapFree(GetProcessHeap(), 0, pDlg); - return; - } - ok( pDlg->hDevMode && pDlg->hDevNames, - "got %p and %p (expected '!= NULL' for both)\n", - pDlg->hDevMode, pDlg->hDevNames); - - GlobalFree(pDlg->hDevMode); - GlobalFree(pDlg->hDevNames); - - HeapFree(GetProcessHeap(), 0, pDlg); - -} - -/* ##### */ - -static void test_PrintDlgA(void) -{ - DWORD res; - LPPRINTDLGA pDlg; - - - pDlg = HeapAlloc(GetProcessHeap(), 0, (sizeof(PRINTDLGA)) * 2); - if (!pDlg) return; - - - /* will crash with unpatched wine */ - SetLastError(0xdeadbeef); - res = PrintDlgA(NULL); - ok( !res && (CommDlgExtendedError() == CDERR_INITIALIZATION), - "returned %d with 0x%x and 0x%x (expected '0' and " - "CDERR_INITIALIZATION)\n", res, GetLastError(), CommDlgExtendedError()); - - ZeroMemory(pDlg, sizeof(PRINTDLGA)); - pDlg->lStructSize = sizeof(PRINTDLGA) - 1; - SetLastError(0xdeadbeef); - res = PrintDlgA(pDlg); - ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), - "returned %d with 0x%x and 0x%x (expected '0' and " - "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError()); - - ZeroMemory(pDlg, sizeof(PRINTDLGA)); - pDlg->lStructSize = sizeof(PRINTDLGA) + 1; - pDlg->Flags = PD_RETURNDEFAULT; - SetLastError(0xdeadbeef); - res = PrintDlgA(pDlg); - ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), - "returned %u with %u and 0x%x (expected '0' and " - "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError()); - - - ZeroMemory(pDlg, sizeof(PRINTDLGA)); - pDlg->lStructSize = sizeof(PRINTDLGA); - pDlg->Flags = PD_RETURNDEFAULT; - SetLastError(0xdeadbeef); - res = PrintDlgA(pDlg); - ok( res || (CommDlgExtendedError() == PDERR_NODEFAULTPRN), - "returned %d with 0x%x and 0x%x (expected '!= 0' or '0' and " - "PDERR_NODEFAULTPRN)\n", res, GetLastError(), CommDlgExtendedError()); - - HeapFree(GetProcessHeap(), 0, pDlg); - -} - - -START_TEST(printdlg) -{ - test_PageSetupDlgA(); - test_PrintDlgA(); - -} +/* + * Unit test suite for comdlg32 API functions: printer dialogs + * + * Copyright 2006-2007 Detlef Riekenberg + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" + +#include "cderr.h" +#include "commdlg.h" + +#include "wine/test.h" + +/* ########################### */ + +static HMODULE hcomdlg32; +static HRESULT (WINAPI * pPrintDlgExA)(LPPRINTDLGEXA); +static HRESULT (WINAPI * pPrintDlgExW)(LPPRINTDLGEXW); + +/* ########################### */ + +static const CHAR emptyA[] = ""; +static const CHAR PrinterPortsA[] = "PrinterPorts"; + +/* ########################### */ + +static LPCSTR load_functions(void) +{ + LPCSTR ptr; + + ptr = "comdlg32.dll"; + hcomdlg32 = LoadLibraryA(ptr); + if (!hcomdlg32) return ptr; + + ptr = "PrintDlgExA"; + pPrintDlgExA = (void *) GetProcAddress(hcomdlg32, ptr); + if (!pPrintDlgExA) return ptr; + + ptr = "PrintDlgExW"; + pPrintDlgExW = (void *) GetProcAddress(hcomdlg32, ptr); + if (!pPrintDlgExW) return ptr; + + return NULL; + +} + +/* ########################### */ + +static void test_PageSetupDlgA(void) +{ + LPPAGESETUPDLGA pDlg; + DWORD res; + + pDlg = HeapAlloc(GetProcessHeap(), 0, (sizeof(PAGESETUPDLGA)) * 2); + if (!pDlg) return; + + SetLastError(0xdeadbeef); + res = PageSetupDlgA(NULL); + ok( !res && (CommDlgExtendedError() == CDERR_INITIALIZATION), + "returned %u with %u and 0x%x (expected '0' and " + "CDERR_INITIALIZATION)\n", res, GetLastError(), CommDlgExtendedError()); + + ZeroMemory(pDlg, sizeof(PAGESETUPDLGA)); + pDlg->lStructSize = sizeof(PAGESETUPDLGA) -1; + SetLastError(0xdeadbeef); + res = PageSetupDlgA(pDlg); + ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), + "returned %u with %u and 0x%x (expected '0' and " + "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError()); + + ZeroMemory(pDlg, sizeof(PAGESETUPDLGA)); + pDlg->lStructSize = sizeof(PAGESETUPDLGA) +1; + pDlg->Flags = PSD_RETURNDEFAULT; + SetLastError(0xdeadbeef); + res = PageSetupDlgA(pDlg); + ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), + "returned %u with %u and 0x%x (expected '0' and CDERR_STRUCTSIZE)\n", + res, GetLastError(), CommDlgExtendedError()); + + + ZeroMemory(pDlg, sizeof(PAGESETUPDLGA)); + pDlg->lStructSize = sizeof(PAGESETUPDLGA); + pDlg->Flags = PSD_RETURNDEFAULT | PSD_NOWARNING; + SetLastError(0xdeadbeef); + res = PageSetupDlgA(pDlg); + ok( res || (CommDlgExtendedError() == PDERR_NODEFAULTPRN), + "returned %u with %u and 0x%x (expected '!= 0' or '0' and " + "PDERR_NODEFAULTPRN)\n", res, GetLastError(), CommDlgExtendedError()); + + if (!res && (CommDlgExtendedError() == PDERR_NODEFAULTPRN)) { + skip("No printer configured.\n"); + HeapFree(GetProcessHeap(), 0, pDlg); + return; + } + + ok( pDlg->hDevMode && pDlg->hDevNames, + "got %p and %p (expected '!= NULL' for both)\n", + pDlg->hDevMode, pDlg->hDevNames); + + GlobalFree(pDlg->hDevMode); + GlobalFree(pDlg->hDevNames); + + HeapFree(GetProcessHeap(), 0, pDlg); + +} + +/* ########################### */ + +static void test_PrintDlgA(void) +{ + DWORD res; + LPPRINTDLGA pDlg; + DEVNAMES *pDevNames; + LPCSTR driver; + LPCSTR device; + LPCSTR port; + CHAR buffer[MAX_PATH]; + LPSTR ptr; + + + pDlg = HeapAlloc(GetProcessHeap(), 0, (sizeof(PRINTDLGA)) * 2); + if (!pDlg) return; + + + /* will crash with unpatched wine */ + SetLastError(0xdeadbeef); + res = PrintDlgA(NULL); + ok( !res && (CommDlgExtendedError() == CDERR_INITIALIZATION), + "returned %d with 0x%x and 0x%x (expected '0' and " + "CDERR_INITIALIZATION)\n", res, GetLastError(), CommDlgExtendedError()); + + ZeroMemory(pDlg, sizeof(PRINTDLGA)); + pDlg->lStructSize = sizeof(PRINTDLGA) - 1; + SetLastError(0xdeadbeef); + res = PrintDlgA(pDlg); + ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), + "returned %d with 0x%x and 0x%x (expected '0' and " + "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError()); + + ZeroMemory(pDlg, sizeof(PRINTDLGA)); + pDlg->lStructSize = sizeof(PRINTDLGA) + 1; + pDlg->Flags = PD_RETURNDEFAULT; + SetLastError(0xdeadbeef); + res = PrintDlgA(pDlg); + ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE), + "returned %u with %u and 0x%x (expected '0' and " + "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError()); + + + ZeroMemory(pDlg, sizeof(PRINTDLGA)); + pDlg->lStructSize = sizeof(PRINTDLGA); + pDlg->Flags = PD_RETURNDEFAULT; + SetLastError(0xdeadbeef); + res = PrintDlgA(pDlg); + ok( res || (CommDlgExtendedError() == PDERR_NODEFAULTPRN), + "returned %d with 0x%x and 0x%x (expected '!= 0' or '0' and " + "PDERR_NODEFAULTPRN)\n", res, GetLastError(), CommDlgExtendedError()); + + if (!res && (CommDlgExtendedError() == PDERR_NODEFAULTPRN)) { + skip("No printer configured.\n"); + HeapFree(GetProcessHeap(), 0, pDlg); + return; + } + + ok(pDlg->hDevNames != NULL, "(expected '!= NULL')\n"); + pDevNames = GlobalLock(pDlg->hDevNames); + ok(pDevNames != NULL, "(expected '!= NULL')\n"); + + if (pDevNames) { + ok(pDevNames->wDriverOffset, "(expected '!= 0' for wDriverOffset)\n"); + ok(pDevNames->wDeviceOffset, "(expected '!= 0' for wDeviceOffset)\n"); + ok(pDevNames->wOutputOffset, "(expected '!= 0' for wOutputOffset)\n"); + ok(pDevNames->wDefault == DN_DEFAULTPRN, "got 0x%x (expected DN_DEFAULTPRN)\n", pDevNames->wDefault); + + driver = (LPCSTR)pDevNames + pDevNames->wDriverOffset; + device = (LPCSTR)pDevNames + pDevNames->wDeviceOffset; + port = (LPCSTR)pDevNames + pDevNames->wOutputOffset; + trace("driver '%s' device '%s' port '%s'\n", driver, device, port); + + /* The Driver Entry does not include a Path */ + ptr = strrchr(driver, '\\'); + todo_wine { + ok( ptr == NULL, "got %p for '%s' (expected NULL for a simple name)\n", ptr, driver); + } + + /* The Driver Entry does not have an extension (fixed to ".drv") */ + ptr = strrchr(driver, '.'); + todo_wine { + ok( ptr == NULL, "got %p for '%s' (expected NULL for no extension)\n", ptr, driver); + } + + + buffer[0] = '\0'; + SetLastError(0xdeadbeef); + res = GetProfileStringA(PrinterPortsA, device, emptyA, buffer, sizeof(buffer)); + ptr = strchr(buffer, ','); + todo_wine { + ok( (res > 1) && (ptr != NULL), + "got %u with %u and %p for '%s' (expected '>1' and '!= NULL')\n", + res, GetLastError(), ptr, buffer); + } + + if (ptr) ptr[0] = '\0'; + todo_wine { + ok( lstrcmpiA(driver, buffer) == 0, + "got driver '%s' (expected '%s')\n", driver, buffer); + } + + } + + GlobalUnlock(pDlg->hDevNames); + + GlobalFree(pDlg->hDevMode); + GlobalFree(pDlg->hDevNames); + HeapFree(GetProcessHeap(), 0, pDlg); + +} + +/* ########################### */ + +static void test_PrintDlgExW(void) +{ + LPPRINTDLGEXW pDlg; + HRESULT res; + + /* Set CommDlgExtendedError != 0 */ + PrintDlg(NULL); + SetLastError(0xdeadbeef); + res = pPrintDlgExW(NULL); + ok( (res == E_INVALIDARG), + "got 0x%x with %u and %u (expected 'E_INVALIDARG')\n", + res, GetLastError(), CommDlgExtendedError()); + + + pDlg = HeapAlloc(GetProcessHeap(), 0, (sizeof(PRINTDLGEXW)) + 8); + if (!pDlg) return; + + /* lStructSize must be exact */ + ZeroMemory(pDlg, sizeof(PRINTDLGEXW)); + pDlg->lStructSize = sizeof(PRINTDLGEXW) - 1; + PrintDlg(NULL); + SetLastError(0xdeadbeef); + res = pPrintDlgExW(pDlg); + ok( (res == E_INVALIDARG), + "got 0x%x with %u and %u (expected 'E_INVALIDARG')\n", + res, GetLastError(), CommDlgExtendedError()); + + + ZeroMemory(pDlg, sizeof(PRINTDLGEXW)); + pDlg->lStructSize = sizeof(PRINTDLGEXW) + 1; + PrintDlg(NULL); + SetLastError(0xdeadbeef); + res = pPrintDlgExW(pDlg); + ok( (res == E_INVALIDARG), + "got 0x%x with %u and %u (expected 'E_INVALIDARG')\n", + res, GetLastError(), CommDlgExtendedError()); + + + ZeroMemory(pDlg, sizeof(PRINTDLGEXW)); + pDlg->lStructSize = sizeof(PRINTDLGEXW); + SetLastError(0xdeadbeef); + res = pPrintDlgExW(pDlg); + ok( (res == E_HANDLE), + "got 0x%x with %u and %u (expected 'E_HANDLE')\n", + res, GetLastError(), CommDlgExtendedError()); + + + HeapFree(GetProcessHeap(), 0, pDlg); + return; + +} + +/* ########################### */ + +START_TEST(printdlg) +{ + LPCSTR ptr; + + ptr = load_functions(); + + test_PageSetupDlgA(); + test_PrintDlgA(); + + /* PrintDlgEx not present before w2k */ + if (ptr) { + skip("%s\n", ptr); + return; + } + + test_PrintDlgExW(); +} diff --git a/rostests/winetests/comdlg32/testlist.c b/rostests/winetests/comdlg32/testlist.c index abbda30eb5f..339b3525ac5 100644 --- a/rostests/winetests/comdlg32/testlist.c +++ b/rostests/winetests/comdlg32/testlist.c @@ -1,17 +1,17 @@ -/* Automatically generated file; DO NOT EDIT!! */ - -#define WIN32_LEAN_AND_MEAN -#include - -#define STANDALONE -#include "wine/test.h" - -extern void func_filedlg(void); -extern void func_printdlg(void); - -const struct test winetest_testlist[] = -{ - { "filedlg", func_filedlg }, - { "printdlg", func_printdlg }, - { 0, 0 } -}; +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_filedlg(void); +extern void func_printdlg(void); + +const struct test winetest_testlist[] = +{ + { "filedlg", func_filedlg }, + { "printdlg", func_printdlg }, + { 0, 0 } +}; diff --git a/rostests/winetests/directory.rbuild b/rostests/winetests/directory.rbuild index d4cc460d946..686d51c5fa0 100644 --- a/rostests/winetests/directory.rbuild +++ b/rostests/winetests/directory.rbuild @@ -2,9 +2,18 @@ + + + + + + + + + @@ -23,6 +32,9 @@ + + + @@ -32,15 +44,30 @@ + + + + + + + + + + + + + + + @@ -50,13 +77,22 @@ + + + + + + + + + diff --git a/rostests/winetests/lz32/lz32.rbuild b/rostests/winetests/lz32/lz32.rbuild index dd2500a3069..abe12d9353b 100644 --- a/rostests/winetests/lz32/lz32.rbuild +++ b/rostests/winetests/lz32/lz32.rbuild @@ -1,9 +1,13 @@ - - . - - kernel32 - ntdll - lz32 - testlist.c - lzexpand_main.c + + + + . + 0x600 + 0x600 + wine + lz32 + kernel32 + ntdll + lzexpand_main.c + testlist.c diff --git a/rostests/winetests/lz32/lzexpand_main.c b/rostests/winetests/lz32/lzexpand_main.c index a5d0e59f155..b51067bcff3 100644 --- a/rostests/winetests/lz32/lzexpand_main.c +++ b/rostests/winetests/lz32/lzexpand_main.c @@ -27,22 +27,39 @@ #include "wine/test.h" +/* Compressed file names end with underscore. */ +static char filename [] = "testfile.xxx"; static char filename_[] = "testfile.xx_"; -#if 0 -static char filename[] = "testfile.xxx"; -#endif +static WCHAR filenameW [] = {'t','e','s','t','f','i','l','e','.','x','x','x',0}; +static WCHAR filenameW_[] = {'t','e','s','t','f','i','l','e','.','x','x','_',0}; + +static char dotless [] = "dotless"; +static char dotless_[] = "dotless._"; +static WCHAR dotlessW [] = {'d','o','t','l','e','s','s', 0}; +static WCHAR dotlessW_[] = {'d','o','t','l','e','s','s','.','_', 0}; + +static char extless [] = "extless."; +static char extless_[] = "extless._"; +static WCHAR extlessW [] = {'e','x','t','l','e','s','s','.', 0}; +static WCHAR extlessW_[] = {'e','x','t','l','e','s','s','.','_', 0}; + +static char _terminated [] = "_terminated.xxxx_"; +static char _terminated_[] = "_terminated.xxxx_"; +static WCHAR _terminatedW [] = {'_','t','e','r','m','i','n','a','t','e','d','.','x','x','x','x','_', 0}; +static WCHAR _terminatedW_[] = {'_','t','e','r','m','i','n','a','t','e','d','.','x','x','x','x','_', 0}; + 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: + following URL: http://www.microsoft.com/downloads/details.aspx?FamilyID=9d467a69-57ff-4ae7-96ee-b18c4790cffd&displaylang=en */ -static const unsigned char compressed_file[] = +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, @@ -54,67 +71,404 @@ static const DWORD uncompressed_data_size = sizeof(uncompressed_data) - 1; static char *buf; -static void test_lzopenfile(void) +static void full_file_path_name_in_a_CWD(const char *src, char *dst, BOOL expect_short) +{ + DWORD retval; + char shortname[MAX_PATH]; + + retval = GetCurrentDirectoryA(MAX_PATH, dst); + ok(retval > 0, "GetCurrentDirectoryA returned %d, GLE=%d\n", + retval, GetLastError()); + if(dst[retval-1] != '\\') + /* Append backslash only when it's missing */ + lstrcatA(dst, "\\"); + lstrcatA(dst, src); + if(expect_short) + { + memcpy(shortname, dst, MAX_PATH); + retval = GetShortPathName(shortname, dst, MAX_PATH-1); + ok(retval > 0, "GetShortPathName returned %d for '%s', GLE=%d\n", + retval, dst, GetLastError()); + } +} + +static void create_file(char *fname) +{ + INT file; + OFSTRUCT ofs; + DWORD retval; + + file = LZOpenFileA(fname, &ofs, OF_CREATE); + ok(file >= 0, "LZOpenFileA failed to create '%s'\n", fname); + LZClose(file); + retval = GetFileAttributesA(fname); + ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA('%s'): error %d\n", ofs.szPathName, GetLastError()); +} + +static void delete_file(char *fname) +{ + INT file; + OFSTRUCT ofs; + DWORD retval; + + file = LZOpenFileA(fname, &ofs, OF_DELETE); + ok(file >= 0, "LZOpenFileA failed to delete '%s'\n", fname); + LZClose(file); + retval = GetFileAttributesA(fname); + ok(retval == INVALID_FILE_ATTRIBUTES, "GetFileAttributesA succeeded on deleted file ('%s')\n", ofs.szPathName); +} + +static void test_LZOpenFileA_existing_compressed(void) +{ + OFSTRUCT test; + INT file; + char expected[MAX_PATH]; + char short_expected[MAX_PATH]; + char filled_0xA5[OFS_MAXPATHNAME]; + + /* Try to open existing compressed files: */ + create_file(filename_); + create_file(dotless_); + create_file(extless_); + create_file(_terminated_); + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(filename_, expected, FALSE); + SetLastError(0xfaceabee); + + /* a, using 8.3-conformant file name. */ + file = LZOpenFileA(filename, &test, OF_EXIST); + /* If the file "foo.xxx" does not exist, LZOpenFileA 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. + */ + if(file != LZERROR_BADINHANDLE) { + ok(file >= 0, "LZOpenFileA returns negative file descriptor for '%s'\n", filename); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", + test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileA returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + } else { /* Win9x */ + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + ok(test.cBytes == 0xA5, + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, + "LZOpenFileA returned '%s', but was expected to return '%s'\n", + test.szPathName, filled_0xA5); + } + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(dotless_, expected, FALSE); + SetLastError(0xfaceabee); + + /* b, using dotless file name. */ + file = LZOpenFileA(dotless, &test, OF_EXIST); + if(file != LZERROR_BADINHANDLE) { + ok(file >= 0, "LZOpenFileA returns negative file descriptor for '%s'\n", dotless); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", + test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileA returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + } else { /* Win9x */ + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + todo_wine + ok(strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, + "LZOpenFileA returned '%s', but was expected to return '%s'\n", + test.szPathName, filled_0xA5); + } + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(extless_, expected, FALSE); + SetLastError(0xfaceabee); + + /* c, using extensionless file name. */ + file = LZOpenFileA(extless, &test, OF_EXIST); + if(file != LZERROR_BADINHANDLE) { + ok(file >= 0, "LZOpenFileA returns negative file descriptor for '%s'\n", extless); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", + test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileA returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + } else { /* Win9x */ + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + ok(test.cBytes == 0xA5, + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, + "LZOpenFileA returned '%s', but was expected to return '%s'\n", + test.szPathName, filled_0xA5); + } + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(_terminated_, expected, FALSE); + full_file_path_name_in_a_CWD(_terminated_, short_expected, TRUE); + + /* d, using underscore-terminated file name. */ + file = LZOpenFileA(_terminated, &test, OF_EXIST); + ok(file >= 0, "LZOpenFileA failed on switching to a compressed file name\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", + test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + lstrcmpA(test.szPathName, short_expected) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, short_expected); + LZClose(file); + + delete_file(filename_); + delete_file(dotless_); + delete_file(extless_); + delete_file(_terminated_); +} + +static void test_LZOpenFileA_nonexisting_compressed(void) +{ + OFSTRUCT test; + INT file; + char expected[MAX_PATH]; + char filled_0xA5[OFS_MAXPATHNAME]; + + /* Try to open nonexisting compressed files: */ + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(filename_, expected, FALSE); + SetLastError(0xfaceabee); + + /* a, using 8.3-conformant file name. */ + file = LZOpenFileA(filename, &test, OF_EXIST); + /* If the file "foo.xxx" does not exist, LZOpenFileA 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. + */ + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileA succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(dotless_, expected, FALSE); + SetLastError(0xfaceabee); + + /* b, using dotless file name. */ + file = LZOpenFileA(dotless, &test, OF_EXIST); + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileA succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(extless_, expected, FALSE); + SetLastError(0xfaceabee); + + /* c, using extensionless file name. */ + file = LZOpenFileA(extless, &test, OF_EXIST); + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileA succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(_terminated_, expected, FALSE); + SetLastError(0xfaceabee); + + /* d, using underscore-terminated file name. */ + file = LZOpenFileA(_terminated, &test, OF_EXIST); + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileA succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); +} + +static void test_LZOpenFileA(void) { OFSTRUCT test; DWORD retval; INT file; + static char badfilename_[] = "badfilename_"; + char expected[MAX_PATH]; + char short_expected[MAX_PATH]; + SetLastError(0xfaceabee); /* Check for nonexistent file. */ - file = LZOpenFile("badfilename_", &test, OF_READ); - ok(file == LZERROR_BADINHANDLE, - "LZOpenFile succeeded on nonexistent file\n"); + file = LZOpenFileA(badfilename_, &test, OF_READ); + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileA succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); LZClose(file); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(filename_, expected, FALSE); + /* Create an empty file. */ - file = LZOpenFile(filename_, &test, OF_CREATE); - ok(file >= 0, "LZOpenFile failed on creation\n"); + file = LZOpenFileA(filename_, &test, OF_CREATE); + ok(file >= 0, "LZOpenFileA failed on creation\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileA returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); LZClose(file); - retval = GetFileAttributes(filename_); - ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributes: error %ld\n", + + retval = GetFileAttributesA(filename_); + ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA: error %d\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"); + /* Check various opening options: */ + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(filename_, short_expected, TRUE); + + /* a, for reading. */ + file = LZOpenFileA(filename_, &test, OF_READ); + ok(file >= 0, "LZOpenFileA failed on read\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + lstrcmpA(test.szPathName, short_expected) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, short_expected); LZClose(file); + memset(&test, 0xA5, sizeof(test)); - /* 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"); + /* b, for writing. */ + file = LZOpenFileA(filename_, &test, OF_WRITE); + ok(file >= 0, "LZOpenFileA failed on write\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + lstrcmpA(test.szPathName, short_expected) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, short_expected); LZClose(file); -#endif + + memset(&test, 0xA5, sizeof(test)); + + /* c, for reading and writing. */ + file = LZOpenFileA(filename_, &test, OF_READWRITE); + ok(file >= 0, "LZOpenFileA failed on read/write\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + lstrcmpA(test.szPathName, short_expected) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, short_expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + + /* d, for checking file existence. */ + file = LZOpenFileA(filename_, &test, OF_EXIST); + ok(file >= 0, "LZOpenFileA failed on read/write\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + lstrcmpA(test.szPathName, short_expected) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, short_expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); /* 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"); + file = LZOpenFileA(filename_, &test, OF_DELETE); + ok(file >= 0, "LZOpenFileA failed on delete\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileA set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0 || + lstrcmpA(test.szPathName, short_expected) == 0, /* Win9x */ + "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, short_expected); LZClose(file); - retval = GetFileAttributes(filename_); - ok(retval == INVALID_FILE_ATTRIBUTES, - "GetFileAttributes succeeded on deleted file\n"); + retval = GetFileAttributesA(filename_); + ok(retval == INVALID_FILE_ATTRIBUTES, + "GetFileAttributesA succeeded on deleted file\n"); + test_LZOpenFileA_existing_compressed(); + test_LZOpenFileA_nonexisting_compressed(); } -static void test_lzread(void) +static void test_LZRead(void) { HANDLE file; DWORD ret; @@ -123,15 +477,15 @@ static void test_lzread(void) BOOL retok; /* Create the compressed file. */ - file = CreateFile(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0); + file = CreateFileA(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( retok, "WriteFile: error %d\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"); + cfile = LZOpenFileA(filename_, &test, OF_READ); + ok(cfile > 0, "LZOpenFileA failed\n"); ret = LZRead(cfile, buf, uncompressed_data_size); ok(ret == uncompressed_data_size, "Read wrong number of bytes\n"); @@ -148,11 +502,11 @@ static void test_lzread(void) LZClose(cfile); - ret = DeleteFile(filename_); - ok(ret, "DeleteFile: error %ld\n", GetLastError()); + ret = DeleteFileA(filename_); + ok(ret, "DeleteFileA: error %d\n", GetLastError()); } -static void test_lzcopy(void) +static void test_LZCopy(void) { HANDLE file; DWORD ret; @@ -161,18 +515,18 @@ static void test_lzcopy(void) 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()); + file = CreateFileA(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0); + ok(file != INVALID_HANDLE_VALUE, + "CreateFileA: error %d\n", GetLastError()); retok = WriteFile(file, compressed_file, compressed_file_size, &ret, 0); - ok( retok, "WriteFile error %ld\n", GetLastError()); + ok( retok, "WriteFile error %d\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); + source = LZOpenFileA(filename_, &stest, OF_READ); + ok(source >= 0, "LZOpenFileA failed on compressed file\n"); + dest = LZOpenFileA(filename2, &dtest, OF_CREATE); + ok(dest >= 0, "LZOpenFileA failed on creating new file %d\n", dest); ret = LZCopy(source, dest); ok(ret > 0, "LZCopy error\n"); @@ -180,29 +534,353 @@ static void test_lzcopy(void) LZClose(source); LZClose(dest); - file = CreateFile(filename2, GENERIC_READ, 0, NULL, OPEN_EXISTING, - 0, 0); + file = CreateFileA(filename2, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); ok(file != INVALID_HANDLE_VALUE, - "CreateFile: error %ld\n", GetLastError()); + "CreateFileA: error %d\n", GetLastError()); retok = ReadFile(file, buf, uncompressed_data_size*2, &ret, 0); - ok( retok && ret == uncompressed_data_size, "ReadFile: error %ld\n", GetLastError()); + ok( retok && ret == uncompressed_data_size, "ReadFile: error %d\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()); + ret = DeleteFileA(filename_); + ok(ret, "DeleteFileA: error %d\n", GetLastError()); + ret = DeleteFileA(filename2); + ok(ret, "DeleteFileA: error %d\n", GetLastError()); } +static void create_fileW(WCHAR *fnameW) +{ + INT file; + OFSTRUCT ofs; + DWORD retval; + + file = LZOpenFileW(fnameW, &ofs, OF_CREATE); + ok(file >= 0, "LZOpenFileW failed on creation\n"); + LZClose(file); + retval = GetFileAttributesW(fnameW); + ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributesW('%s'): error %d\n", ofs.szPathName, GetLastError()); +} + +static void delete_fileW(WCHAR *fnameW) +{ + INT file; + OFSTRUCT ofs; + DWORD retval; + + file = LZOpenFileW(fnameW, &ofs, OF_DELETE); + ok(file >= 0, "LZOpenFileW failed on delete\n"); + LZClose(file); + retval = GetFileAttributesW(fnameW); + ok(retval == INVALID_FILE_ATTRIBUTES, "GetFileAttributesW succeeded on deleted file ('%s')\n", ofs.szPathName); +} + +static void test_LZOpenFileW_existing_compressed(void) +{ + OFSTRUCT test; + INT file; + char expected[MAX_PATH]; + + /* Try to open existing compressed files: */ + create_fileW(filenameW_); + create_fileW(dotlessW_); + create_fileW(extlessW_); + create_fileW(_terminatedW_); + + full_file_path_name_in_a_CWD(filename_, expected, FALSE); + memset(&test, 0xA5, sizeof(test)); + + /* a, using 8.3-conformant file name. */ + file = LZOpenFileW(filenameW, &test, OF_EXIST); + /* If the file "foo.xxx" does not exist, LZOpenFileW should then + check for the file "foo.xx_" and open that. + */ + ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", + test.nErrCode); + /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */ + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(dotless_, expected, FALSE); + + /* b, using dotless file name. */ + file = LZOpenFileW(dotlessW, &test, OF_EXIST); + ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", + test.nErrCode); + /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */ + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(extless_, expected, FALSE); + + /* c, using extensionless file name. */ + file = LZOpenFileW(extlessW, &test, OF_EXIST); + ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", + test.nErrCode); + /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */ + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(_terminated_, expected, FALSE); + + /* d, using underscore-terminated file name. */ + file = LZOpenFileW(_terminatedW, &test, OF_EXIST); + ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", + test.nErrCode); + /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */ + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + delete_fileW(filenameW_); + delete_fileW(dotlessW_); + delete_fileW(extlessW_); + delete_fileW(_terminatedW_); +} + +static void test_LZOpenFileW_nonexisting_compressed(void) +{ + OFSTRUCT test; + INT file; + char expected[MAX_PATH]; + char filled_0xA5[OFS_MAXPATHNAME]; + + /* Try to open nonexisting compressed files: */ + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(filename_, expected, FALSE); + SetLastError(0xfaceabee); + + /* a, using 8.3-conformant file name. */ + file = LZOpenFileW(filenameW, &test, OF_EXIST); + /* If the file "foo.xxx" does not exist, LZOpenFileA 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. + */ + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileW succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(dotless_, expected, FALSE); + SetLastError(0xfaceabee); + + /* b, using dotless file name. */ + file = LZOpenFileW(dotlessW, &test, OF_EXIST); + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileW succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(extless_, expected, FALSE); + SetLastError(0xfaceabee); + + /* c, using extensionless file name. */ + file = LZOpenFileW(extlessW, &test, OF_EXIST); + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileW succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); + + memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME); + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(_terminated_, expected, FALSE); + SetLastError(0xfaceabee); + + /* d, using underscore-terminated file name. */ + file = LZOpenFileW(_terminatedW, &test, OF_EXIST); + ok(file == LZERROR_BADINHANDLE, + "LZOpenFileW succeeded on nonexistent file\n"); + ok(GetLastError() == ERROR_FILE_NOT_FOUND, + "GetLastError() returns %d\n", GetLastError()); + todo_wine + ok(test.cBytes == 0xA5, + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_FILE_NOT_FOUND, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n", + test.szPathName, expected, filled_0xA5); +} + +static void test_LZOpenFileW(void) +{ + OFSTRUCT test; + DWORD retval; + INT file; + static WCHAR badfilenameW[] = {'b','a','d','f','i','l','e','n','a','m','e','.','x','t','n',0}; + char expected[MAX_PATH]; + + SetLastError(0xfaceabee); + /* Check for nonexistent file. */ + file = LZOpenFileW(badfilenameW, &test, OF_READ); + ok(GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED, + "GetLastError() returns %d\n", GetLastError()); + if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + trace("LZOpenFileW call not implemented, skipping rest of the test\n"); + return; + } + ok(file == LZERROR_BADINHANDLE, "LZOpenFileW succeeded on nonexistent file\n"); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + full_file_path_name_in_a_CWD(filename_, expected, FALSE); + + /* Create an empty file. */ + file = LZOpenFileW(filenameW_, &test, OF_CREATE); + ok(file >= 0, "LZOpenFile failed on creation\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + retval = GetFileAttributesW(filenameW_); + ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributes: error %d\n", + GetLastError()); + + /* Check various opening options: */ + memset(&test, 0xA5, sizeof(test)); + + /* a, for reading. */ + file = LZOpenFileW(filenameW_, &test, OF_READ); + ok(file >= 0, "LZOpenFileW failed on read\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + + /* b, for writing. */ + file = LZOpenFileW(filenameW_, &test, OF_WRITE); + ok(file >= 0, "LZOpenFileW failed on write\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + + /* c, for reading and writing. */ + file = LZOpenFileW(filenameW_, &test, OF_READWRITE); + ok(file >= 0, "LZOpenFileW failed on read/write\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + + /* d, for checking file existence. */ + file = LZOpenFileW(filenameW_, &test, OF_EXIST); + ok(file >= 0, "LZOpenFileW failed on read/write\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + memset(&test, 0xA5, sizeof(test)); + + /* Delete the file then make sure it doesn't exist anymore. */ + file = LZOpenFileW(filenameW_, &test, OF_DELETE); + ok(file >= 0, "LZOpenFileW failed on delete\n"); + ok(test.cBytes == sizeof(OFSTRUCT), + "LZOpenFileW set test.cBytes to %d\n", test.cBytes); + ok(test.nErrCode == ERROR_SUCCESS, + "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode); + ok(lstrcmpA(test.szPathName, expected) == 0, + "LZOpenFileW returned '%s', but was expected to return '%s'\n", + test.szPathName, expected); + LZClose(file); + + retval = GetFileAttributesW(filenameW_); + ok(retval == INVALID_FILE_ATTRIBUTES, + "GetFileAttributesW succeeded on deleted file\n"); + + test_LZOpenFileW_existing_compressed(); + test_LZOpenFileW_nonexisting_compressed(); +} + + START_TEST(lzexpand_main) { buf = malloc(uncompressed_data_size * 2); - test_lzopenfile(); - test_lzread(); - test_lzcopy(); + test_LZOpenFileA(); + test_LZOpenFileW(); + test_LZRead(); + test_LZCopy(); free(buf); } diff --git a/rostests/winetests/mapi32/imalloc.c b/rostests/winetests/mapi32/imalloc.c new file mode 100644 index 00000000000..63c66f27747 --- /dev/null +++ b/rostests/winetests/mapi32/imalloc.c @@ -0,0 +1,114 @@ +/* + * Unit test suite for MAPI IMalloc 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winerror.h" +#include "winnt.h" +#include "mapiutil.h" + +static HMODULE hMapi32 = 0; + +static SCODE (WINAPI *pScInitMapiUtil)(ULONG); +static LPMALLOC (WINAPI *pMAPIGetDefaultMalloc)(void); + +static void test_IMalloc(void) +{ + LPVOID lpMem; + ULONG ulRef; + int iRet; + HRESULT hRet; + LPMALLOC lpMalloc; + LPVOID lpVoid; + + pMAPIGetDefaultMalloc = (void*)GetProcAddress(hMapi32, + "MAPIGetDefaultMalloc@0"); + if (!pMAPIGetDefaultMalloc) + return; + + lpMalloc = pMAPIGetDefaultMalloc(); + if (!lpMalloc) + return; + + lpVoid = NULL; + hRet = IMalloc_QueryInterface(lpMalloc, &IID_IUnknown, &lpVoid); + ok (hRet == S_OK && lpVoid != NULL, + "IID_IUnknown: expected S_OK, non-null, got 0x%08x, %p\n", + hRet, lpVoid); + + lpVoid = NULL; + hRet = IMalloc_QueryInterface(lpMalloc, &IID_IMalloc, &lpVoid); + ok (hRet == S_OK && lpVoid != NULL, + "IID_IIMalloc: expected S_OK, non-null, got 0x%08x, %p\n", + hRet, lpVoid); + + /* Prove that native mapi uses LocalAlloc/LocalFree */ + lpMem = IMalloc_Alloc(lpMalloc, 61); + ok (lpMem && IMalloc_GetSize(lpMalloc, lpMem) == + LocalSize((HANDLE)lpMem), + "Expected non-null, same size, got %p, %s size\n", lpMem, + lpMem ? "different" : "same"); + + iRet = IMalloc_DidAlloc(lpMalloc, lpMem); + ok (iRet == -1, "DidAlloc, expected -1. got %d\n", iRet); + + IMalloc_HeapMinimize(lpMalloc); + + LocalFree(lpMem); + + ulRef = IMalloc_AddRef(lpMalloc); + ok (ulRef == 1u, "AddRef expected 1, returned %d\n", ulRef); + + ulRef = IMalloc_Release(lpMalloc); + ok (ulRef == 1u, "AddRef expected 1, returned %d\n", ulRef); + + IMalloc_Release(lpMalloc); +} + +START_TEST(imalloc) +{ + SCODE ret; + + hMapi32 = LoadLibraryA("mapi32.dll"); + + pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4"); + if (!pScInitMapiUtil) + { + skip("ScInitMapiUtil is not available\n"); + FreeLibrary(hMapi32); + return; + } + + SetLastError(0xdeadbeef); + ret = pScInitMapiUtil(0); + if ((ret != S_OK) && (GetLastError() == ERROR_PROC_NOT_FOUND)) + { + skip("ScInitMapiUtil is not implemented\n"); + FreeLibrary(hMapi32); + return; + } + + test_IMalloc(); + + FreeLibrary(hMapi32); +} diff --git a/rostests/winetests/mapi32/mapi32.rbuild b/rostests/winetests/mapi32/mapi32.rbuild new file mode 100644 index 00000000000..8712bb2a28d --- /dev/null +++ b/rostests/winetests/mapi32/mapi32.rbuild @@ -0,0 +1,15 @@ + + + + . + 0x600 + 0x600 + wine + kernel32 + uuid + ntdll + imalloc.c + prop.c + util.c + testlist.c + diff --git a/rostests/winetests/mapi32/prop.c b/rostests/winetests/mapi32/prop.c new file mode 100644 index 00000000000..d78eb3697f3 --- /dev/null +++ b/rostests/winetests/mapi32/prop.c @@ -0,0 +1,1381 @@ +/* + * Unit test suite for MAPI property 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winerror.h" +#include "winnt.h" +#include "mapiutil.h" +#include "mapitags.h" + +static HMODULE hMapi32 = 0; + +static SCODE (WINAPI *pScInitMapiUtil)(ULONG); +static SCODE (WINAPI *pPropCopyMore)(LPSPropValue,LPSPropValue,ALLOCATEMORE*,LPVOID); +static ULONG (WINAPI *pUlPropSize)(LPSPropValue); +static BOOL (WINAPI *pFPropContainsProp)(LPSPropValue,LPSPropValue,ULONG); +static BOOL (WINAPI *pFPropCompareProp)(LPSPropValue,ULONG,LPSPropValue); +static LONG (WINAPI *pLPropCompareProp)(LPSPropValue,LPSPropValue); +static LPSPropValue (WINAPI *pPpropFindProp)(LPSPropValue,ULONG,ULONG); +static SCODE (WINAPI *pScCountProps)(INT,LPSPropValue,ULONG*); +static SCODE (WINAPI *pScCopyProps)(int,LPSPropValue,LPVOID,ULONG*); +static SCODE (WINAPI *pScRelocProps)(int,LPSPropValue,LPVOID,LPVOID,ULONG*); +static LPSPropValue (WINAPI *pLpValFindProp)(ULONG,ULONG,LPSPropValue); +static BOOL (WINAPI *pFBadRglpszA)(LPSTR*,ULONG); +static BOOL (WINAPI *pFBadRglpszW)(LPWSTR*,ULONG); +static BOOL (WINAPI *pFBadRowSet)(LPSRowSet); +static ULONG (WINAPI *pFBadPropTag)(ULONG); +static ULONG (WINAPI *pFBadRow)(LPSRow); +static ULONG (WINAPI *pFBadProp)(LPSPropValue); +static ULONG (WINAPI *pFBadColumnSet)(LPSPropTagArray); +static SCODE (WINAPI *pCreateIProp)(LPCIID,ALLOCATEBUFFER*,ALLOCATEMORE*, + FREEBUFFER*,LPVOID,LPPROPDATA*); +static SCODE (WINAPI *pMAPIAllocateBuffer)(ULONG, LPVOID); +static SCODE (WINAPI *pMAPIAllocateMore)(ULONG, LPVOID, LPVOID); +static SCODE (WINAPI *pMAPIFreeBuffer)(LPVOID); + +static BOOL InitFuncPtrs(void) +{ + hMapi32 = LoadLibraryA("mapi32.dll"); + + pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4"); + pMAPIAllocateBuffer = (void*)GetProcAddress(hMapi32, "MAPIAllocateBuffer"); + pMAPIAllocateMore = (void*)GetProcAddress(hMapi32, "MAPIAllocateMore"); + pMAPIFreeBuffer = (void*)GetProcAddress(hMapi32, "MAPIFreeBuffer"); + if(pScInitMapiUtil && pMAPIAllocateBuffer && pMAPIAllocateMore && pMAPIFreeBuffer) + return TRUE; + else + return FALSE; +} + +static ULONG ptTypes[] = { + PT_I2, PT_I4, PT_R4, PT_R8, PT_CURRENCY, PT_APPTIME, PT_SYSTIME, + PT_ERROR, PT_BOOLEAN, PT_I8, PT_CLSID, PT_STRING8, PT_BINARY, + PT_UNICODE +}; + +static inline int strcmpW(const WCHAR *str1, const WCHAR *str2) +{ + while (*str1 && (*str1 == *str2)) { str1++; str2++; } + return *str1 - *str2; +} + +static void test_PropCopyMore(void) +{ + static char szHiA[] = "Hi!"; + static WCHAR szHiW[] = { 'H', 'i', '!', '\0' }; + SPropValue *lpDest = NULL, *lpSrc = NULL; + ULONG i; + SCODE scode; + + pPropCopyMore = (void*)GetProcAddress(hMapi32, "PropCopyMore@16"); + + if (!pPropCopyMore) + return; + + scode = pMAPIAllocateBuffer(sizeof(LPSPropValue), (LPVOID *)lpDest); + if (FAILED(scode)) + return; + + scode = pMAPIAllocateMore(sizeof(LPSPropValue), lpDest, (LPVOID *)lpSrc); + if (FAILED(scode)) + return; + + for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++) + { + lpSrc->ulPropTag = ptTypes[i]; + + switch (ptTypes[i]) + { + case PT_STRING8: + lpSrc->Value.lpszA = szHiA; + break; + case PT_UNICODE: + lpSrc->Value.lpszW = szHiW; + break; + case PT_BINARY: + lpSrc->Value.bin.cb = 4; + lpSrc->Value.bin.lpb = (LPBYTE)szHiA; + break; + } + + memset(lpDest, 0xff, sizeof(SPropValue)); + + scode = pPropCopyMore(lpDest, lpSrc, (ALLOCATEMORE*)pMAPIAllocateMore, lpDest); + ok(!scode && lpDest->ulPropTag == lpSrc->ulPropTag, + "PropCopyMore: Expected 0x0,%d, got 0x%08x,%d\n", + lpSrc->ulPropTag, scode, lpDest->ulPropTag); + if (SUCCEEDED(scode)) + { + switch (ptTypes[i]) + { + case PT_STRING8: + ok(lstrcmpA(lpDest->Value.lpszA, lpSrc->Value.lpszA) == 0, + "PropCopyMore: Ascii string differs\n"); + break; + case PT_UNICODE: + ok(strcmpW(lpDest->Value.lpszW, lpSrc->Value.lpszW) == 0, + "PropCopyMore: Unicode string differs\n"); + break; + case PT_BINARY: + ok(lpDest->Value.bin.cb == 4 && + !memcmp(lpSrc->Value.bin.lpb, lpDest->Value.bin.lpb, 4), + "PropCopyMore: Binary array differs\n"); + break; + } + } + } + + /* Since all allocations are linked, freeing lpDest frees everything */ + pMAPIFreeBuffer(lpDest); +} + +static void test_UlPropSize(void) +{ + static char szHiA[] = "Hi!"; + static WCHAR szHiW[] = { 'H', 'i', '!', '\0' }; + LPSTR buffa[2]; + LPWSTR buffw[2]; + SBinary buffbin[2]; + ULONG pt, exp, res; + + pUlPropSize = (void*)GetProcAddress(hMapi32, "UlPropSize@4"); + + if (!pUlPropSize) + return; + + for (pt = 0; pt < PROP_ID_INVALID; pt++) + { + SPropValue pv; + + memset(&pv, 0 ,sizeof(pv)); + pv.ulPropTag = pt; + + exp = 1u; /* Default to one item for non-MV properties */ + + switch (PROP_TYPE(pt)) + { + case PT_MV_I2: pv.Value.MVi.cValues = exp = 2; + case PT_I2: exp *= sizeof(USHORT); break; + case PT_MV_I4: pv.Value.MVl.cValues = exp = 2; + case PT_I4: exp *= sizeof(LONG); break; + case PT_MV_R4: pv.Value.MVflt.cValues = exp = 2; + case PT_R4: exp *= sizeof(float); break; + case PT_MV_DOUBLE: pv.Value.MVdbl.cValues = exp = 2; + case PT_R8: exp *= sizeof(double); break; + case PT_MV_CURRENCY: pv.Value.MVcur.cValues = exp = 2; + case PT_CURRENCY: exp *= sizeof(CY); break; + case PT_MV_APPTIME: pv.Value.MVat.cValues = exp = 2; + case PT_APPTIME: exp *= sizeof(double); break; + case PT_MV_SYSTIME: pv.Value.MVft.cValues = exp = 2; + case PT_SYSTIME: exp *= sizeof(FILETIME); break; + case PT_ERROR: exp = sizeof(SCODE); break; + case PT_BOOLEAN: exp = sizeof(USHORT); break; + case PT_OBJECT: exp = 0; break; + case PT_MV_I8: pv.Value.MVli.cValues = exp = 2; + case PT_I8: exp *= sizeof(LONG64); break; +#if 0 + /* My version of native mapi returns 0 for PT_MV_CLSID even if a valid + * array is given. This _has_ to be a bug, so Wine does + * the right thing(tm) and we don't test it here. + */ + case PT_MV_CLSID: pv.Value.MVguid.cValues = exp = 2; +#endif + case PT_CLSID: exp *= sizeof(GUID); break; + case PT_STRING8: + pv.Value.lpszA = szHiA; + exp = 4; + break; + case PT_UNICODE: + pv.Value.lpszW = szHiW; + exp = 4 * sizeof(WCHAR); + break; + case PT_BINARY: + pv.Value.bin.cb = exp = 19; + break; + case PT_MV_STRING8: + pv.Value.MVszA.cValues = 2; + pv.Value.MVszA.lppszA = buffa; + buffa[0] = szHiA; + buffa[1] = szHiA; + exp = 8; + break; + case PT_MV_UNICODE: + pv.Value.MVszW.cValues = 2; + pv.Value.MVszW.lppszW = buffw; + buffw[0] = szHiW; + buffw[1] = szHiW; + exp = 8 * sizeof(WCHAR); + break; + case PT_MV_BINARY: + pv.Value.MVbin.cValues = 2; + pv.Value.MVbin.lpbin = buffbin; + buffbin[0].cb = 19; + buffbin[1].cb = 1; + exp = 20; + break; + default: + exp = 0; + } + + res = pUlPropSize(&pv); + ok(res == exp, "pt= %d: Expected %d, got %d\n", pt, exp, res); + } +} + +static void test_FPropContainsProp(void) +{ + static char szFull[] = "Full String"; + static char szFullLower[] = "full string"; + static char szPrefix[] = "Full"; + static char szPrefixLower[] = "full"; + static char szSubstring[] = "ll St"; + static char szSubstringLower[] = "ll st"; + SPropValue pvLeft, pvRight; + ULONG pt; + BOOL bRet; + + pFPropContainsProp = (void*)GetProcAddress(hMapi32, "FPropContainsProp@12"); + + if (!pFPropContainsProp) + return; + + /* Ensure that only PT_STRING8 and PT_BINARY are handled */ + for (pt = 0; pt < PROP_ID_INVALID; pt++) + { + if (pt == PT_STRING8 || pt == PT_BINARY) + continue; /* test these later */ + + memset(&pvLeft, 0 ,sizeof(pvLeft)); + memset(&pvRight, 0 ,sizeof(pvRight)); + pvLeft.ulPropTag = pvRight.ulPropTag = pt; + + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING); + ok(bRet == FALSE, "pt= %d: Expected FALSE, got %d\n", pt, bRet); + } + + /* test the various flag combinations */ + pvLeft.ulPropTag = pvRight.ulPropTag = PT_STRING8; + pvLeft.Value.lpszA = szFull; + pvRight.Value.lpszA = szFull; + + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING); + ok(bRet == TRUE, "(full,full)[] match failed\n"); + pvRight.Value.lpszA = szPrefix; + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING); + ok(bRet == FALSE, "(full,prefix)[] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == TRUE, "(full,prefix)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == TRUE, "(full,prefix)[SUBSTRING] match failed\n"); + pvRight.Value.lpszA = szPrefixLower; + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == FALSE, "(full,prefixlow)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == FALSE, "(full,prefixlow)[SUBSTRING] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE); + ok(bRet == TRUE, "(full,prefixlow)[PREFIX|IGNORECASE] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE); + ok(bRet == TRUE, "(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n"); + pvRight.Value.lpszA = szSubstring; + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING); + ok(bRet == FALSE, "(full,substr)[] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == FALSE, "(full,substr)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == TRUE, "(full,substr)[SUBSTRING] match failed\n"); + pvRight.Value.lpszA = szSubstringLower; + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == FALSE, "(full,substrlow)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == FALSE, "(full,substrlow)[SUBSTRING] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE); + ok(bRet == FALSE, "(full,substrlow)[PREFIX|IGNORECASE] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE); + ok(bRet == TRUE, "(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n"); + pvRight.Value.lpszA = szFullLower; + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE); + ok(bRet == TRUE, "(full,fulllow)[IGNORECASE] match failed\n"); + + pvLeft.ulPropTag = pvRight.ulPropTag = PT_BINARY; + pvLeft.Value.bin.lpb = (LPBYTE)szFull; + pvRight.Value.bin.lpb = (LPBYTE)szFull; + pvLeft.Value.bin.cb = pvRight.Value.bin.cb = strlen(szFull); + + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING); + ok(bRet == TRUE, "bin(full,full)[] match failed\n"); + pvRight.Value.bin.lpb = (LPBYTE)szPrefix; + pvRight.Value.bin.cb = strlen(szPrefix); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING); + ok(bRet == FALSE, "bin(full,prefix)[] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == TRUE, "bin(full,prefix)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == TRUE, "bin(full,prefix)[SUBSTRING] match failed\n"); + pvRight.Value.bin.lpb = (LPBYTE)szPrefixLower; + pvRight.Value.bin.cb = strlen(szPrefixLower); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE); + ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX|IGNORECASE] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE); + ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n"); + pvRight.Value.bin.lpb = (LPBYTE)szSubstring; + pvRight.Value.bin.cb = strlen(szSubstring); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING); + ok(bRet == FALSE, "bin(full,substr)[] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == FALSE, "bin(full,substr)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == TRUE, "bin(full,substr)[SUBSTRING] match failed\n"); + pvRight.Value.bin.lpb = (LPBYTE)szSubstringLower; + pvRight.Value.bin.cb = strlen(szSubstringLower); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX); + ok(bRet == FALSE, "bin(full,substrlow)[PREFIX] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING); + ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE); + ok(bRet == FALSE, "bin(full,substrlow)[PREFIX|IGNORECASE] match failed\n"); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE); + ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n"); + pvRight.Value.bin.lpb = (LPBYTE)szFullLower; + pvRight.Value.bin.cb = strlen(szFullLower); + bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE); + ok(bRet == FALSE, "bin(full,fulllow)[IGNORECASE] match failed\n"); +} + +typedef struct tagFPropCompareProp_Result +{ + SHORT lVal; + SHORT rVal; + ULONG relOp; + BOOL bRet; +} FPropCompareProp_Result; + +static const FPropCompareProp_Result FPCProp_Results[] = +{ + { 1, 2, RELOP_LT, TRUE }, + { 1, 1, RELOP_LT, FALSE }, + { 2, 1, RELOP_LT, FALSE }, + { 1, 2, RELOP_LE, TRUE }, + { 1, 1, RELOP_LE, TRUE }, + { 2, 1, RELOP_LE, FALSE }, + { 1, 2, RELOP_GT, FALSE }, + { 1, 1, RELOP_GT, FALSE }, + { 2, 1, RELOP_GT, TRUE }, + { 1, 2, RELOP_GE, FALSE }, + { 1, 1, RELOP_GE, TRUE }, + { 2, 1, RELOP_GE, TRUE }, + { 1, 2, RELOP_EQ, FALSE }, + { 1, 1, RELOP_EQ, TRUE }, + { 2, 1, RELOP_EQ, FALSE } +}; + +static const char *relops[] = { "RELOP_LT", "RELOP_LE", "RELOP_GT", "RELOP_GE", "RELOP_EQ" }; + +static void test_FPropCompareProp(void) +{ + SPropValue pvLeft, pvRight; + GUID lguid, rguid; + char lbuffa[2], rbuffa[2]; + WCHAR lbuffw[2], rbuffw[2]; + ULONG i, j; + BOOL bRet, bExp; + + pFPropCompareProp = (void*)GetProcAddress(hMapi32, "FPropCompareProp@12"); + + if (!pFPropCompareProp) + return; + + lbuffa[1] = '\0'; + rbuffa[1] = '\0'; + lbuffw[1] = '\0'; + rbuffw[1] = '\0'; + + for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++) + { + pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i]; + + for (j = 0; j < sizeof(FPCProp_Results)/sizeof(FPCProp_Results[0]); j++) + { + SHORT lVal = FPCProp_Results[j].lVal; + SHORT rVal = FPCProp_Results[j].rVal; + + bExp = FPCProp_Results[j].bRet; + + switch (ptTypes[i]) + { + case PT_BOOLEAN: + /* Boolean values have no concept of less or greater than, only equality */ + if ((lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_LT) || + (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_LE)|| + (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_GT)|| + (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_GE)|| + (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_EQ)|| + (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_EQ)) + bExp = !bExp; + /* Fall through ... */ + case PT_I2: + pvLeft.Value.i = lVal; + pvRight.Value.i = rVal; + break; + case PT_ERROR: + case PT_I4: + pvLeft.Value.l = lVal; + pvRight.Value.l = rVal; + break; + case PT_R4: + pvLeft.Value.flt = lVal; + pvRight.Value.flt = rVal; + break; + case PT_APPTIME: + case PT_R8: + pvLeft.Value.dbl = lVal; + pvRight.Value.dbl = rVal; + break; + case PT_CURRENCY: + pvLeft.Value.cur.int64 = lVal; + pvRight.Value.cur.int64 = rVal; + break; + case PT_SYSTIME: + pvLeft.Value.ft.dwLowDateTime = lVal; + pvLeft.Value.ft.dwHighDateTime = 0; + pvRight.Value.ft.dwLowDateTime = rVal; + pvRight.Value.ft.dwHighDateTime = 0; + break; + case PT_I8: + pvLeft.Value.li.u.LowPart = lVal; + pvLeft.Value.li.u.HighPart = 0; + pvRight.Value.li.u.LowPart = rVal; + pvRight.Value.li.u.HighPart = 0; + break; + case PT_CLSID: + memset(&lguid, 0, sizeof(GUID)); + memset(&rguid, 0, sizeof(GUID)); + lguid.Data4[7] = lVal; + rguid.Data4[7] = rVal; + pvLeft.Value.lpguid = &lguid; + pvRight.Value.lpguid = &rguid; + break; + case PT_STRING8: + pvLeft.Value.lpszA = lbuffa; + pvRight.Value.lpszA = rbuffa; + lbuffa[0] = '0' + lVal; + rbuffa[0] = '0' + rVal; + break; + case PT_UNICODE: + pvLeft.Value.lpszW = lbuffw; + pvRight.Value.lpszW = rbuffw; + lbuffw[0] = '0' + lVal; + rbuffw[0] = '0' + rVal; + break; + case PT_BINARY: + pvLeft.Value.bin.cb = 1; + pvRight.Value.bin.cb = 1; + pvLeft.Value.bin.lpb = (LPBYTE)lbuffa; + pvRight.Value.bin.lpb = (LPBYTE)rbuffa; + lbuffa[0] = lVal; + rbuffa[0] = rVal; + break; + } + + bRet = pFPropCompareProp(&pvLeft, FPCProp_Results[j].relOp, &pvRight); + ok(bRet == bExp, "pt %d (%d,%d,%s): expected %d, got %d\n", ptTypes[i], + FPCProp_Results[j].lVal, FPCProp_Results[j].rVal, + relops[FPCProp_Results[j].relOp], bExp, bRet); + } + } +} + +typedef struct tagLPropCompareProp_Result +{ + SHORT lVal; + SHORT rVal; + INT iRet; +} LPropCompareProp_Result; + +static const LPropCompareProp_Result LPCProp_Results[] = +{ + { 1, 2, -1 }, + { 1, 1, 0 }, + { 2, 1, 1 }, +}; + +static void test_LPropCompareProp(void) +{ + SPropValue pvLeft, pvRight; + GUID lguid, rguid; + char lbuffa[2], rbuffa[2]; + WCHAR lbuffw[2], rbuffw[2]; + ULONG i, j; + INT iRet, iExp; + + pLPropCompareProp = (void*)GetProcAddress(hMapi32, "LPropCompareProp@8"); + + if (!pLPropCompareProp) + return; + + lbuffa[1] = '\0'; + rbuffa[1] = '\0'; + lbuffw[1] = '\0'; + rbuffw[1] = '\0'; + + for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++) + { + pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i]; + + for (j = 0; j < sizeof(LPCProp_Results)/sizeof(LPCProp_Results[0]); j++) + { + SHORT lVal = LPCProp_Results[j].lVal; + SHORT rVal = LPCProp_Results[j].rVal; + + iExp = LPCProp_Results[j].iRet; + + switch (ptTypes[i]) + { + case PT_BOOLEAN: + /* Boolean values have no concept of less or greater than, only equality */ + if (lVal && rVal) + iExp = 0; + /* Fall through ... */ + case PT_I2: + pvLeft.Value.i = lVal; + pvRight.Value.i = rVal; + break; + case PT_ERROR: + case PT_I4: + pvLeft.Value.l = lVal; + pvRight.Value.l = rVal; + break; + case PT_R4: + pvLeft.Value.flt = lVal; + pvRight.Value.flt = rVal; + break; + case PT_APPTIME: + case PT_R8: + pvLeft.Value.dbl = lVal; + pvRight.Value.dbl = rVal; + break; + case PT_CURRENCY: + pvLeft.Value.cur.int64 = lVal; + pvRight.Value.cur.int64 = rVal; + break; + case PT_SYSTIME: + pvLeft.Value.ft.dwLowDateTime = lVal; + pvLeft.Value.ft.dwHighDateTime = 0; + pvRight.Value.ft.dwLowDateTime = rVal; + pvRight.Value.ft.dwHighDateTime = 0; + break; + case PT_I8: + pvLeft.Value.li.u.LowPart = lVal; + pvLeft.Value.li.u.HighPart = 0; + pvRight.Value.li.u.LowPart = rVal; + pvRight.Value.li.u.HighPart = 0; + break; + case PT_CLSID: + memset(&lguid, 0, sizeof(GUID)); + memset(&rguid, 0, sizeof(GUID)); + lguid.Data4[7] = lVal; + rguid.Data4[7] = rVal; + pvLeft.Value.lpguid = &lguid; + pvRight.Value.lpguid = &rguid; + break; + case PT_STRING8: + pvLeft.Value.lpszA = lbuffa; + pvRight.Value.lpszA = rbuffa; + lbuffa[0] = '0' + lVal; + rbuffa[0] = '0' + rVal; + break; + case PT_UNICODE: + pvLeft.Value.lpszW = lbuffw; + pvRight.Value.lpszW = rbuffw; + lbuffw[0] = '0' + lVal; + rbuffw[0] = '0' + rVal; + break; + case PT_BINARY: + pvLeft.Value.bin.cb = 1; + pvRight.Value.bin.cb = 1; + pvLeft.Value.bin.lpb = (LPBYTE)lbuffa; + pvRight.Value.bin.lpb = (LPBYTE)rbuffa; + lbuffa[0] = lVal; + rbuffa[0] = rVal; + break; + } + + iRet = pLPropCompareProp(&pvLeft, &pvRight); + ok(iRet == iExp, "pt %d (%d,%d): expected %d, got %d\n", ptTypes[i], + LPCProp_Results[j].lVal, LPCProp_Results[j].rVal, iExp, iRet); + } + } +} + +static void test_PpropFindProp(void) +{ + SPropValue pvProp, *pRet; + ULONG i; + + pPpropFindProp = (void*)GetProcAddress(hMapi32, "PpropFindProp@12"); + + if (!pPpropFindProp) + return; + + for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++) + { + pvProp.ulPropTag = ptTypes[i]; + + pRet = pPpropFindProp(&pvProp, 1u, ptTypes[i]); + ok(pRet == &pvProp, "PpropFindProp[%d]: Didn't find existing propery\n", + ptTypes[i]); + + pRet = pPpropFindProp(&pvProp, 1u, i ? ptTypes[i-1] : ptTypes[i+1]); + ok(pRet == NULL, "PpropFindProp[%d]: Found nonexistent propery\n", + ptTypes[i]); + } + + pvProp.ulPropTag = PROP_TAG(PT_I2, 1u); + pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 0u)); + ok(pRet == NULL, "PpropFindProp[UNSPECIFIED]: Matched on different id\n"); + pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 1u)); + ok(pRet == &pvProp, "PpropFindProp[UNSPECIFIED]: Didn't match id\n"); +} + +static void test_ScCountProps(void) +{ + static char szHiA[] = "Hi!"; + static WCHAR szHiW[] = { 'H', 'i', '!', '\0' }; + static const ULONG ULHILEN = 4; /* chars in szHiA/W incl. NUL */ + LPSTR buffa[3]; + LPWSTR buffw[3]; + SBinary buffbin[3]; + GUID iids[4], *iid = iids; + SCODE res; + ULONG pt, exp, ulRet; + int success = 1; + + pScCountProps = (void*)GetProcAddress(hMapi32, "ScCountProps@12"); + + if (!pScCountProps) + return; + + for (pt = 0; pt < PROP_ID_INVALID && success; pt++) + { + SPropValue pv; + + memset(&pv, 0 ,sizeof(pv)); + pv.ulPropTag = PROP_TAG(pt, 1u); + + switch (PROP_TYPE(pt)) + { + case PT_I2: + case PT_I4: + case PT_R4: + case PT_R8: + case PT_CURRENCY: + case PT_APPTIME: + case PT_SYSTIME: + case PT_ERROR: + case PT_BOOLEAN: + case PT_OBJECT: + case PT_I8: + exp = sizeof(pv); + break; + case PT_CLSID: + pv.Value.lpguid = iid; + exp = sizeof(GUID) + sizeof(pv); + break; + case PT_STRING8: + pv.Value.lpszA = szHiA; + exp = 4 + sizeof(pv); + break; + case PT_UNICODE: + pv.Value.lpszW = szHiW; + exp = 4 * sizeof(WCHAR) + sizeof(pv); + break; + case PT_BINARY: + pv.Value.bin.cb = 2; + pv.Value.bin.lpb = (LPBYTE)iid; + exp = 2 + sizeof(pv); + break; + case PT_MV_I2: + pv.Value.MVi.cValues = 3; + pv.Value.MVi.lpi = (SHORT*)iid; + exp = 3 * sizeof(SHORT) + sizeof(pv); + break; + case PT_MV_I4: + pv.Value.MVl.cValues = 3; + pv.Value.MVl.lpl = (LONG*)iid; + exp = 3 * sizeof(LONG) + sizeof(pv); + break; + case PT_MV_I8: + pv.Value.MVli.cValues = 3; + pv.Value.MVli.lpli = (LARGE_INTEGER*)iid; + exp = 3 * sizeof(LARGE_INTEGER) + sizeof(pv); + break; + case PT_MV_R4: + pv.Value.MVflt.cValues = 3; + pv.Value.MVflt.lpflt = (float*)iid; + exp = 3 * sizeof(float) + sizeof(pv); + break; + case PT_MV_APPTIME: + case PT_MV_R8: + pv.Value.MVdbl.cValues = 3; + pv.Value.MVdbl.lpdbl = (double*)iid; + exp = 3 * sizeof(double) + sizeof(pv); + break; + case PT_MV_CURRENCY: + pv.Value.MVcur.cValues = 3; + pv.Value.MVcur.lpcur = (CY*)iid; + exp = 3 * sizeof(CY) + sizeof(pv); + break; + case PT_MV_SYSTIME: + pv.Value.MVft.cValues = 3; + pv.Value.MVft.lpft = (FILETIME*)iid; + exp = 3 * sizeof(CY) + sizeof(pv); + break; + case PT_MV_STRING8: + pv.Value.MVszA.cValues = 3; + pv.Value.MVszA.lppszA = buffa; + buffa[0] = szHiA; + buffa[1] = szHiA; + buffa[2] = szHiA; + exp = ULHILEN * 3 + 3 * sizeof(char*) + sizeof(pv); + break; + case PT_MV_UNICODE: + pv.Value.MVszW.cValues = 3; + pv.Value.MVszW.lppszW = buffw; + buffw[0] = szHiW; + buffw[1] = szHiW; + buffw[2] = szHiW; + exp = ULHILEN * 3 * sizeof(WCHAR) + 3 * sizeof(WCHAR*) + sizeof(pv); + break; + case PT_MV_BINARY: + pv.Value.MVbin.cValues = 3; + pv.Value.MVbin.lpbin = buffbin; + buffbin[0].cb = 17; + buffbin[0].lpb = (LPBYTE)&iid; + buffbin[1].cb = 2; + buffbin[1].lpb = (LPBYTE)&iid; + buffbin[2].cb = 1; + buffbin[2].lpb = (LPBYTE)&iid; + exp = 20 + sizeof(pv) + sizeof(SBinary) * 3; + break; + default: + exp = 0; + } + + ulRet = 0xffffffff; + res = pScCountProps(1, &pv, &ulRet); + if (!exp) { + success = res == MAPI_E_INVALID_PARAMETER && ulRet == 0xffffffff; + ok(success, "pt= %d: Expected failure, got %d, ret=0x%08X\n", + pt, ulRet, res); + } + else { + success = res == S_OK && ulRet == exp; + ok(success, "pt= %d: Expected %d, got %d, ret=0x%08X\n", + pt, exp, ulRet, res); + } + } + +} + +static void test_ScCopyRelocProps(void) +{ + static char szTestA[] = "Test"; + char buffer[512], buffer2[512], *lppszA[1]; + SPropValue pvProp, *lpResProp = (LPSPropValue)buffer; + ULONG ulCount; + SCODE sc; + + pScCopyProps = (void*)GetProcAddress(hMapi32, "ScCopyProps@16"); + pScRelocProps = (void*)GetProcAddress(hMapi32, "ScRelocProps@20"); + + if (!pScCopyProps || !pScRelocProps) + return; + + pvProp.ulPropTag = PROP_TAG(PT_MV_STRING8, 1u); + + lppszA[0] = szTestA; + pvProp.Value.MVszA.cValues = 1; + pvProp.Value.MVszA.lppszA = lppszA; + ulCount = 0; + + sc = pScCopyProps(1, &pvProp, buffer, &ulCount); + ok(sc == S_OK, "wrong ret %d\n", sc); + ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %x\n",lpResProp->ulPropTag); + ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %d\n", lpResProp->Value.MVszA.cValues); + ok(lpResProp->Value.MVszA.lppszA[0] == buffer + sizeof(SPropValue) + sizeof(char*), + "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]); + ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5, "wrong count %d\n", ulCount); + ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA), + "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]); + + memcpy(buffer2, buffer, sizeof(buffer)); + + /* Clear the data in the source buffer. Since pointers in the copied buffer + * refer to the source buffer, this proves that native always assumes that + * the copied buffers pointers are bad (needing to be relocated first). + */ + memset(buffer, 0, sizeof(buffer)); + ulCount = 0; + + sc = pScRelocProps(1, (LPSPropValue)buffer2, buffer, buffer2, &ulCount); + lpResProp = (LPSPropValue)buffer2; + + ok(sc == S_OK, "wrong ret %d\n", sc); + ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %x\n",lpResProp->ulPropTag); + ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %d\n", lpResProp->Value.MVszA.cValues); + ok(lpResProp->Value.MVszA.lppszA[0] == buffer2 + sizeof(SPropValue) + sizeof(char*), + "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]); + /* Native has a bug whereby it calculates the size correctly when copying + * but when relocating does not (presumably it uses UlPropSize() which + * ignores multivalue pointers). Wine returns the correct value. + */ + ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5 || ulCount == sizeof(SPropValue) + 5, + "wrong count %d\n", ulCount); + ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA), + "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]); + + /* Native crashes with lpNew or lpOld set to NULL so skip testing this */ +} + +static void test_LpValFindProp(void) +{ + SPropValue pvProp, *pRet; + ULONG i; + + pLpValFindProp = (void*)GetProcAddress(hMapi32, "LpValFindProp@12"); + + if (!pLpValFindProp) + return; + + for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++) + { + pvProp.ulPropTag = PROP_TAG(ptTypes[i], 1u); + + pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 1u), 1u, &pvProp); + ok(pRet == &pvProp, "LpValFindProp[%d]: Didn't find existing propery id/type\n", + ptTypes[i]); + + pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 0u), 1u, &pvProp); + ok(pRet == NULL, "LpValFindProp[%d]: Found nonexistent propery id\n", + ptTypes[i]); + + pRet = pLpValFindProp(PROP_TAG(PT_NULL, 0u), 1u, &pvProp); + ok(pRet == NULL, "LpValFindProp[%d]: Found nonexistent propery id/type\n", + ptTypes[i]); + + pRet = pLpValFindProp(PROP_TAG(PT_NULL, 1u), 1u, &pvProp); + ok(pRet == &pvProp, "LpValFindProp[%d]: Didn't find existing propery id\n", + ptTypes[i]); + } +} + +static void test_FBadRglpszA(void) +{ + LPSTR lpStrs[4]; + static CHAR szString[] = "A String"; + BOOL bRet; + + pFBadRglpszA = (void*)GetProcAddress(hMapi32, "FBadRglpszA@8"); + if (!pFBadRglpszA) + return; + + bRet = pFBadRglpszA(NULL, 10); + ok(bRet == TRUE, "FBadRglpszA(Null): expected TRUE, got FALSE\n"); + + lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL; + bRet = pFBadRglpszA(lpStrs, 4); + ok(bRet == TRUE, "FBadRglpszA(Nulls): expected TRUE, got FALSE\n"); + + lpStrs[0] = lpStrs[1] = lpStrs[2] = szString; + bRet = pFBadRglpszA(lpStrs, 3); + ok(bRet == FALSE, "FBadRglpszA(valid): expected FALSE, got TRUE\n"); + + bRet = pFBadRglpszA(lpStrs, 4); + ok(bRet == TRUE, "FBadRglpszA(1 invalid): expected TRUE, got FALSE\n"); +} + +static void test_FBadRglpszW(void) +{ + LPWSTR lpStrs[4]; + static WCHAR szString[] = { 'A',' ','S','t','r','i','n','g','\0' }; + BOOL bRet; + + pFBadRglpszW = (void*)GetProcAddress(hMapi32, "FBadRglpszW@8"); + if (!pFBadRglpszW) + return; + + bRet = pFBadRglpszW(NULL, 10); + ok(bRet == TRUE, "FBadRglpszW(Null): expected TRUE, got FALSE\n"); + + lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL; + bRet = pFBadRglpszW(lpStrs, 4); + ok(bRet == TRUE, "FBadRglpszW(Nulls): expected TRUE, got FALSE\n"); + + lpStrs[0] = lpStrs[1] = lpStrs[2] = szString; + bRet = pFBadRglpszW(lpStrs, 3); + ok(bRet == FALSE, "FBadRglpszW(valid): expected FALSE, got TRUE\n"); + + bRet = pFBadRglpszW(lpStrs, 4); + ok(bRet == TRUE, "FBadRglpszW(1 invalid): expected TRUE, got FALSE\n"); +} + +static void test_FBadRowSet(void) +{ + ULONG ulRet; + + pFBadRowSet = (void*)GetProcAddress(hMapi32, "FBadRowSet@4"); + if (!pFBadRowSet) + return; + + ulRet = pFBadRowSet(NULL); + ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n"); + + /* FIXME */ +} + +static void test_FBadPropTag(void) +{ + ULONG pt, res; + + pFBadPropTag = (void*)GetProcAddress(hMapi32, "FBadPropTag@4"); + if (!pFBadPropTag) + return; + + for (pt = 0; pt < PROP_ID_INVALID; pt++) + { + BOOL bBad = TRUE; + + switch (pt & (~MV_FLAG & PROP_TYPE_MASK)) + { + case PT_UNSPECIFIED: + case PT_NULL: case PT_I2: case PT_I4: case PT_R4: + case PT_R8: case PT_CURRENCY: case PT_APPTIME: + case PT_ERROR: case PT_BOOLEAN: case PT_OBJECT: + case PT_I8: case PT_STRING8: case PT_UNICODE: + case PT_SYSTIME: case PT_CLSID: case PT_BINARY: + bBad = FALSE; + } + + res = pFBadPropTag(pt); + if (bBad) + ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt); + else + ok(res == 0, "pt= %d: Expected zero, got %d\n", pt, res); + } +} + +static void test_FBadRow(void) +{ + ULONG ulRet; + + pFBadRow = (void*)GetProcAddress(hMapi32, "FBadRow@4"); + if (!pFBadRow) + return; + + ulRet = pFBadRow(NULL); + ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n"); + + /* FIXME */ +} + +static void test_FBadProp(void) +{ + static WCHAR szEmpty[] = { '\0' }; + GUID iid; + ULONG pt, res; + SPropValue pv; + + pFBadProp = (void*)GetProcAddress(hMapi32, "FBadProp@4"); + if (!pFBadProp) + return; + + for (pt = 0; pt < PROP_ID_INVALID; pt++) + { + BOOL bBad = TRUE; + + memset(&pv, 0, sizeof(pv)); + pv.ulPropTag = pt; + + /* Note that MV values are valid below because their array count is 0, + * so no pointers are validated. + */ + switch (PROP_TYPE(pt)) + { + case (MV_FLAG|PT_UNSPECIFIED): + case PT_UNSPECIFIED: + case (MV_FLAG|PT_NULL): + case PT_NULL: + case PT_MV_I2: + case PT_I2: + case PT_MV_I4: + case PT_I4: + case PT_MV_I8: + case PT_I8: + case PT_MV_R4: + case PT_R4: + case PT_MV_R8: + case PT_R8: + case PT_MV_CURRENCY: + case PT_CURRENCY: + case PT_MV_APPTIME: + case PT_APPTIME: + case (MV_FLAG|PT_ERROR): + case PT_ERROR: + case (MV_FLAG|PT_BOOLEAN): + case PT_BOOLEAN: + case (MV_FLAG|PT_OBJECT): + case PT_OBJECT: + case PT_MV_STRING8: + case PT_MV_UNICODE: + case PT_MV_SYSTIME: + case PT_SYSTIME: + case PT_MV_BINARY: + case PT_BINARY: + case PT_MV_CLSID: + bBad = FALSE; + break; + case PT_STRING8: + case PT_UNICODE: + pv.Value.lpszW = szEmpty; + bBad = FALSE; + break; + case PT_CLSID: + pv.Value.lpguid = &iid; + bBad = FALSE; + break; + } + + res = pFBadProp(&pv); + if (bBad) + ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt); + else + ok(res == 0, "pt= %d: Expected zero, got %d\n", pt, res); + } +} + +static void test_FBadColumnSet(void) +{ + SPropTagArray pta; + ULONG pt, res; + + pFBadColumnSet = (void*)GetProcAddress(hMapi32, "FBadColumnSet@4"); + if (!pFBadColumnSet) + return; + + res = pFBadColumnSet(NULL); + ok(res != 0, "(null): Expected non-zero, got 0\n"); + + pta.cValues = 1; + + for (pt = 0; pt < PROP_ID_INVALID; pt++) + { + BOOL bBad = TRUE; + + pta.aulPropTag[0] = pt; + + switch (pt & (~MV_FLAG & PROP_TYPE_MASK)) + { + case PT_UNSPECIFIED: + case PT_NULL: + case PT_I2: + case PT_I4: + case PT_R4: + case PT_R8: + case PT_CURRENCY: + case PT_APPTIME: + case PT_BOOLEAN: + case PT_OBJECT: + case PT_I8: + case PT_STRING8: + case PT_UNICODE: + case PT_SYSTIME: + case PT_CLSID: + case PT_BINARY: + bBad = FALSE; + } + if (pt == (MV_FLAG|PT_ERROR)) + bBad = FALSE; + + res = pFBadColumnSet(&pta); + if (bBad) + ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt); + else + ok(res == 0, "pt= %d: Expected zero, got %d\n", pt, res); + } +} + + +static void test_IProp(void) +{ + IPropData *lpIProp; + LPMAPIERROR lpError; + LPSPropProblemArray lpProbs; + LPSPropValue lpProps; + LPSPropTagArray lpTags; + SPropValue pvs[2]; + SizedSPropTagArray(2,tags); + ULONG access[2], count; + SCODE sc; + + pCreateIProp = (void*)GetProcAddress(hMapi32, "CreateIProp@24"); + + if (!pCreateIProp) + return; + + memset(&tags, 0 , sizeof(tags)); + + /* Create the object */ + lpIProp = NULL; + sc = pCreateIProp(&IID_IMAPIPropData, (ALLOCATEBUFFER *)pMAPIAllocateBuffer, (ALLOCATEMORE*)pMAPIAllocateMore, + (FREEBUFFER *)pMAPIFreeBuffer, NULL, &lpIProp); + ok(sc == S_OK && lpIProp, + "CreateIProp: expected S_OK, non-null, got 0x%08X,%p\n", sc, lpIProp); + + if (sc != S_OK || !lpIProp) + return; + + /* GetLastError - No errors set */ + lpError = NULL; + IPropData_GetLastError(lpIProp, E_INVALIDARG, 0, &lpError); + ok(sc == S_OK && !lpError, + "GetLastError: Expected S_OK, null, got 0x%08X,%p\n", sc, lpError); + + /* Get prop tags - succeeds returning 0 items */ + lpTags = NULL; + sc = IPropData_GetPropList(lpIProp, 0, &lpTags); + ok(sc == S_OK && lpTags && lpTags->cValues == 0, + "GetPropList(empty): Expected S_OK, non-null, 0, got 0x%08X,%p,%d\n", + sc, lpTags, lpTags ? lpTags->cValues : 0); + if (lpTags) + pMAPIFreeBuffer(lpTags); + + /* Get props - succeeds returning 0 items */ + lpProps = NULL; + count = 0; + tags.cValues = 1; + tags.aulPropTag[0] = PR_IMPORTANCE; + sc = IPropData_GetProps(lpIProp, (LPSPropTagArray)&tags, 0, &count, &lpProps); + ok(sc == MAPI_W_ERRORS_RETURNED && lpProps && count == 1, + "GetProps(empty): Expected ERRORS_RETURNED, non-null, 1, got 0x%08X,%p,%d\n", + sc, lpProps, count); + if (lpProps && count > 0) + { + ok(lpProps[0].ulPropTag == CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR), + "GetProps(empty): Expected %x, got %x\n", + CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR), lpProps[0].ulPropTag); + + pMAPIFreeBuffer(lpProps); + } + + /* Add (NULL) - Can't add NULLs */ + lpProbs = NULL; + pvs[0].ulPropTag = PROP_TAG(PT_NULL,0x01); + sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs); + ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs, + "SetProps(): Expected INVALID_PARAMETER, null, got 0x%08X,%p\n", + sc, lpProbs); + + /* Add (OBJECT) - Can't add OBJECTs */ + lpProbs = NULL; + pvs[0].ulPropTag = PROP_TAG(PT_OBJECT,0x01); + sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs); + ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs, + "SetProps(OBJECT): Expected INVALID_PARAMETER, null, got 0x%08X,%p\n", + sc, lpProbs); + + /* Add - Adds value */ + lpProbs = NULL; + pvs[0].ulPropTag = PR_IMPORTANCE; + sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs); + ok(sc == S_OK && !lpProbs, + "SetProps(ERROR): Expected S_OK, null, got 0x%08X,%p\n", sc, lpProbs); + + /* Get prop list - returns 1 item */ + lpTags = NULL; + IPropData_GetPropList(lpIProp, 0, &lpTags); + ok(sc == S_OK && lpTags && lpTags->cValues == 1, + "GetPropList: Expected S_OK, non-null, 1, got 0x%08X,%p,%d\n", + sc, lpTags, lpTags ? lpTags->cValues : 0); + if (lpTags && lpTags->cValues > 0) + { + ok(lpTags->aulPropTag[0] == PR_IMPORTANCE, + "GetPropList: Expected %x, got %x\n", + PR_IMPORTANCE, lpTags->aulPropTag[0]); + pMAPIFreeBuffer(lpTags); + } + + /* Set access to read and write */ + sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE); + ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08X\n", sc); + + tags.cValues = 1; + tags.aulPropTag[0] = PR_IMPORTANCE; + + /* Set item access (bad access) - Fails */ + access[0] = 0; + sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access); + ok(sc == MAPI_E_INVALID_PARAMETER, + "SetPropAcess(0): Expected INVALID_PARAMETER got 0x%08X\n",sc); + access[0] = IPROP_READWRITE; + sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access); + ok(sc == MAPI_E_INVALID_PARAMETER, + "SetPropAcess(RW): Expected INVALID_PARAMETER got 0x%08X\n",sc); + access[0] = IPROP_CLEAN; + sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access); + ok(sc == MAPI_E_INVALID_PARAMETER, + "SetPropAcess(C): Expected INVALID_PARAMETER got 0x%08X\n",sc); + + /* Set item access to read/write/clean */ + tags.cValues = 1; + tags.aulPropTag[0] = PR_IMPORTANCE; + access[0] = IPROP_READWRITE|IPROP_CLEAN; + sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access); + ok(sc == S_OK, "SetPropAcess(RW/C): Expected S_OK got 0x%08X\n",sc); + + /* Set object access to read only */ + sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READONLY); + ok(sc == S_OK, "SetObjAcess(READ): Expected S_OK got 0x%08X\n", sc); + + /* Set item access to read/write/dirty - doesn't care about RO object */ + access[0] = IPROP_READONLY|IPROP_DIRTY; + sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access); + ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08X\n", sc); + + /* Delete any item when set to read only - Error */ + lpProbs = NULL; + tags.aulPropTag[0] = PR_RESPONSE_REQUESTED; + sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs); + ok(sc == E_ACCESSDENIED && !lpProbs, + "DeleteProps(nonexistent): Expected E_ACCESSDENIED null got 0x%08X %p\n", + sc, lpProbs); + + /* Set access to read and write */ + sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE); + ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08X\n", sc); + + /* Delete nonexistent item - No error */ + lpProbs = NULL; + tags.aulPropTag[0] = PR_RESPONSE_REQUESTED; + sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs); + ok(sc == S_OK && !lpProbs, + "DeleteProps(nonexistent): Expected S_OK null got 0x%08X %p\n", + sc, lpProbs); + + /* Delete existing item (r/o) - No error, but lpProbs populated */ + lpProbs = NULL; + tags.aulPropTag[0] = PR_IMPORTANCE; + sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs); + ok(sc == S_OK && lpProbs, + "DeleteProps(RO): Expected S_OK non-null got 0x%08X %p\n", sc, lpProbs); + + if (lpProbs && lpProbs->cProblem > 0) + { + ok(lpProbs->cProblem == 1 && + lpProbs->aProblem[0].ulIndex == 0 && + lpProbs->aProblem[0].ulPropTag == PR_IMPORTANCE && + lpProbs->aProblem[0].scode == E_ACCESSDENIED, + "DeleteProps(RO): Expected (1,0,%x,%x) got (%d,%x,%x)\n", + PR_IMPORTANCE, E_ACCESSDENIED, + lpProbs->aProblem[0].ulIndex, lpProbs->aProblem[0].ulPropTag, + lpProbs->aProblem[0].scode); + pMAPIFreeBuffer(lpProbs); + } + + lpProbs = NULL; + tags.cValues = 1; + tags.aulPropTag[0] = PR_RESPONSE_REQUESTED; + IPropData_HrAddObjProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs); + ok(sc == S_OK && !lpProbs, + "AddObjProps(RO): Expected S_OK null got 0x%08X %p\n", sc, lpProbs); + + /* Get prop list - returns 1 item */ + lpTags = NULL; + IPropData_GetPropList(lpIProp, 0, &lpTags); + ok(sc == S_OK && lpTags && lpTags->cValues == 1, + "GetPropList: Expected S_OK, non-null, 1, got 0x%08X,%p,%d\n", + sc, lpTags, lpTags ? lpTags->cValues : 0); + if (lpTags && lpTags->cValues > 0) + { + ok(lpTags->aulPropTag[0] == PR_IMPORTANCE, + "GetPropList: Expected %x, got %x\n", + PR_IMPORTANCE, lpTags->aulPropTag[0]); + pMAPIFreeBuffer(lpTags); + } + + /* Set item to r/w again */ + access[0] = IPROP_READWRITE|IPROP_DIRTY; + sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access); + ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08X\n", sc); + + /* Delete existing item (r/w) - No error, no problems */ + lpProbs = NULL; + sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs); + ok(sc == S_OK && !lpProbs, + "DeleteProps(RO): Expected S_OK null got 0x%08X %p\n", sc, lpProbs); + + /* Free the list */ + IPropData_Release(lpIProp); +} + +START_TEST(prop) +{ + SCODE ret; + + if(!InitFuncPtrs()) + { + skip("Needed functions are not available\n"); + return; + } + + SetLastError(0xdeadbeef); + ret = pScInitMapiUtil(0); + if ((ret != S_OK) && (GetLastError() == ERROR_PROC_NOT_FOUND)) + { + skip("ScInitMapiUtil is not implemented\n"); + FreeLibrary(hMapi32); + return; + } + + test_PropCopyMore(); + test_UlPropSize(); + test_FPropContainsProp(); + test_FPropCompareProp(); + test_LPropCompareProp(); + test_PpropFindProp(); + test_ScCountProps(); + test_ScCopyRelocProps(); + test_LpValFindProp(); + test_FBadRglpszA(); + test_FBadRglpszW(); + test_FBadRowSet(); + test_FBadPropTag(); + test_FBadRow(); + test_FBadProp(); + test_FBadColumnSet(); + + test_IProp(); + FreeLibrary(hMapi32); +} diff --git a/rostests/winetests/mapi32/testlist.c b/rostests/winetests/mapi32/testlist.c new file mode 100644 index 00000000000..97ef6c9bbdb --- /dev/null +++ b/rostests/winetests/mapi32/testlist.c @@ -0,0 +1,19 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_imalloc(void); +extern void func_prop(void); +extern void func_util(void); + +const struct test winetest_testlist[] = +{ + { "imalloc", func_imalloc }, + { "prop", func_prop }, + { "util", func_util }, + { 0, 0 } +}; diff --git a/rostests/winetests/mapi32/util.c b/rostests/winetests/mapi32/util.c new file mode 100644 index 00000000000..2ae42f1c814 --- /dev/null +++ b/rostests/winetests/mapi32/util.c @@ -0,0 +1,203 @@ +/* + * Unit test suite for MAPI utility 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winerror.h" +#include "winnt.h" +#include "mapiutil.h" +#include "mapitags.h" + +static HMODULE hMapi32 = 0; + +static SCODE (WINAPI *pScInitMapiUtil)(ULONG); +static void (WINAPI *pSwapPword)(PUSHORT,ULONG); +static void (WINAPI *pSwapPlong)(PULONG,ULONG); +static void (WINAPI *pHexFromBin)(LPBYTE,int,LPWSTR); +static void (WINAPI *pFBinFromHex)(LPWSTR,LPBYTE); +static UINT (WINAPI *pUFromSz)(LPCSTR); +static ULONG (WINAPI *pUlFromSzHex)(LPCSTR); +static ULONG (WINAPI *pCbOfEncoded)(LPCSTR); +static BOOL (WINAPI *pIsBadBoundedStringPtr)(LPCSTR,ULONG); + +static void test_SwapPword(void) +{ + USHORT shorts[3]; + + pSwapPword = (void*)GetProcAddress(hMapi32, "SwapPword@8"); + if (!pSwapPword) + return; + + shorts[0] = 0xff01; + shorts[1] = 0x10ff; + shorts[2] = 0x2001; + pSwapPword(shorts, 2); + ok(shorts[0] == 0x01ff && shorts[1] == 0xff10 && shorts[2] == 0x2001, + "Expected {0x01ff,0xff10,0x2001}, got {0x%04x,0x%04x,0x%04x}\n", + shorts[0], shorts[1], shorts[2]); +} + +static void test_SwapPlong(void) +{ + ULONG longs[3]; + + pSwapPlong = (void*)GetProcAddress(hMapi32, "SwapPlong@8"); + if (!pSwapPlong) + return; + + longs[0] = 0xffff0001; + longs[1] = 0x1000ffff; + longs[2] = 0x20000001; + pSwapPlong(longs, 2); + ok(longs[0] == 0x0100ffff && longs[1] == 0xffff0010 && longs[2] == 0x20000001, + "Expected {0x0100ffff,0xffff0010,0x20000001}, got {0x%08x,0x%08x,0x%08x}\n", + longs[0], longs[1], longs[2]); +} + +static void test_HexFromBin(void) +{ + static char res[] = { "000102030405060708090A0B0C0D0E0F101112131415161" + "718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B" + "3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6" + "06162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384" + "85868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A" + "9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCD" + "CECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F" + "2F3F4F5F6F7F8F9FAFBFCFDFE\0X" }; + BYTE data[255]; + WCHAR strw[256]; + BOOL bOk; + int i; + + pHexFromBin = (void*)GetProcAddress(hMapi32, "HexFromBin@12"); + pFBinFromHex = (void*)GetProcAddress(hMapi32, "FBinFromHex@8"); + if (!pHexFromBin || !pFBinFromHex) + return; + + for (i = 0; i < 255; i++) + data[i] = i; + memset(strw, 'X', sizeof(strw)); + pHexFromBin(data, sizeof(data), strw); + + ok(memcmp(strw, res, sizeof(res) - 1) == 0, "HexFromBin: Result differs\n"); + + memset(data, 0, sizeof(data)); + pFBinFromHex((LPWSTR)res, data); + bOk = TRUE; + for (i = 0; i < 255; i++) + if (data[i] != i) + bOk = FALSE; + ok(bOk == TRUE, "FBinFromHex: Result differs\n"); +} + +static void test_UFromSz(void) +{ + pUFromSz = (void*)GetProcAddress(hMapi32, "UFromSz@4"); + if (!pUFromSz) + return; + + ok(pUFromSz("105679") == 105679u, + "UFromSz: expected 105679, got %d\n", pUFromSz("105679")); + + ok(pUFromSz(" 4") == 0, "UFromSz: exected 0. got %d\n", + pUFromSz(" 4")); +} + +static void test_UlFromSzHex(void) +{ + pUlFromSzHex = (void*)GetProcAddress(hMapi32, "UlFromSzHex@4"); + if (!pUlFromSzHex) + return; + + ok(pUlFromSzHex("fF") == 0xffu, + "UlFromSzHex: expected 0xff, got 0x%x\n", pUlFromSzHex("fF")); + + ok(pUlFromSzHex(" c") == 0, "UlFromSzHex: exected 0x0. got 0x%x\n", + pUlFromSzHex(" c")); +} + +static void test_CbOfEncoded(void) +{ + char buff[129]; + unsigned int i; + + pCbOfEncoded = (void*)GetProcAddress(hMapi32, "CbOfEncoded@4"); + if (!pCbOfEncoded) + return; + + for (i = 0; i < sizeof(buff) - 1; i++) + { + ULONG ulRet, ulExpected = (((i | 3) >> 2) + 1) * 3; + + memset(buff, '\0', sizeof(buff)); + memset(buff, '?', i); + ulRet = pCbOfEncoded(buff); + ok(ulRet == ulExpected, "CbOfEncoded(length %d): expected %d, got %d\n", + i, ulExpected, ulRet); + } +} + +static void test_IsBadBoundedStringPtr(void) +{ + pIsBadBoundedStringPtr = (void*)GetProcAddress(hMapi32, "IsBadBoundedStringPtr@8"); + if (!pIsBadBoundedStringPtr) + return; + + ok(pIsBadBoundedStringPtr(NULL, 0) == TRUE, "IsBadBoundedStringPtr: expected TRUE\n"); + ok(pIsBadBoundedStringPtr("TEST", 4) == TRUE, "IsBadBoundedStringPtr: expected TRUE\n"); + ok(pIsBadBoundedStringPtr("TEST", 5) == FALSE, "IsBadBoundedStringPtr: expected FALSE\n"); +} + +START_TEST(util) +{ + SCODE ret; + + hMapi32 = LoadLibraryA("mapi32.dll"); + + pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4"); + + if (!pScInitMapiUtil) + { + skip("ScInitMapiUtil is not available\n"); + FreeLibrary(hMapi32); + return; + } + + SetLastError(0xdeadbeef); + ret = pScInitMapiUtil(0); + if ((ret != S_OK) && (GetLastError() == ERROR_PROC_NOT_FOUND)) + { + skip("ScInitMapiUtil is not implemented\n"); + FreeLibrary(hMapi32); + return; + } + + test_SwapPword(); + test_SwapPlong(); + test_HexFromBin(); + test_UFromSz(); + test_UlFromSzHex(); + test_CbOfEncoded(); + test_IsBadBoundedStringPtr(); + + FreeLibrary(hMapi32); +} diff --git a/rostests/winetests/mlang/mlang.c b/rostests/winetests/mlang/mlang.c index 76c9f287f82..c036f7a425c 100644 --- a/rostests/winetests/mlang/mlang.c +++ b/rostests/winetests/mlang/mlang.c @@ -629,7 +629,7 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL) "IMLangFontLink_CodePageToCodePages failed\n"); ok (dwCodePages != 0, "No CodePages returned\n"); ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwCodePages, 1035, - &CodePage)==S_OK, + &CodePage)==S_OK, "IMLangFontLink_CodePagesToCodePage failed\n"); ok(CodePage == 932, "Incorrect CodePage Returned (%i)\n",CodePage); @@ -644,12 +644,12 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL) dwManyCodePages = dwManyCodePages | dwCodePages; ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 1256, - &CodePage)==S_OK, + &CodePage)==S_OK, "IMLangFontLink_CodePagesToCodePage failed\n"); ok(CodePage == 1256, "Incorrect CodePage Returned (%i)\n",CodePage); ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 936, - &CodePage)==S_OK, + &CodePage)==S_OK, "IMLangFontLink_CodePagesToCodePage failed\n"); ok(CodePage == 1252, "Incorrect CodePage Returned (%i)\n",CodePage); } @@ -677,6 +677,7 @@ static void test_rfc1766(IMultiLanguage2 *iML2) ok(n == 1, "couldn't fetch 1 RFC1766INFO structure\n"); ok(IsValidLocale(info.lcid, LCID_SUPPORTED), "invalid lcid %04x\n", info.lcid); } + IEnumRfc1766_Release(pEnumRfc1766); } static void test_GetLcidFromRfc1766(IMultiLanguage2 *iML2) @@ -783,6 +784,6 @@ START_TEST(mlang) IMLangFontLink_Test(iMLFL); IMLangFontLink_Release(iMLFL); - + CoUninitialize(); } diff --git a/rostests/winetests/mlang/mlang.rbuild b/rostests/winetests/mlang/mlang.rbuild index e3bf83cb867..748cb29d283 100644 --- a/rostests/winetests/mlang/mlang.rbuild +++ b/rostests/winetests/mlang/mlang.rbuild @@ -1,9 +1,9 @@ - + + + . - - 0x600 - 0x501 - 0x501 + 0x600 + 0x600 wine ole32 gdi32 diff --git a/rostests/winetests/netapi32/access.c b/rostests/winetests/netapi32/access.c new file mode 100644 index 00000000000..3861768ef23 --- /dev/null +++ b/rostests/winetests/netapi32/access.c @@ -0,0 +1,345 @@ +/* + * Copyright 2002 Andriy Palamarchuk + * + * Conformance test of the access functions. + * + * 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 + +#include +#include +#include +#include +#include +#include + +#include "wine/test.h" + +WCHAR user_name[UNLEN + 1]; +WCHAR computer_name[MAX_COMPUTERNAME_LENGTH + 1]; + +static const WCHAR sNonexistentUser[] = {'N','o','n','e','x','i','s','t','e','n','t',' ', + 'U','s','e','r',0}; +static WCHAR sTooLongName[] = {'T','h','i','s',' ','i','s',' ','a',' ','b','a','d', + ' ','u','s','e','r','n','a','m','e',0}; +static WCHAR sTooLongPassword[] = {'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h','a','b','c','d','e','f','g','h', + 'a', 0}; + +static WCHAR sTestUserName[] = {'t', 'e', 's', 't', 'u', 's', 'e', 'r', 0}; +static WCHAR sTestUserOldPass[] = {'o', 'l', 'd', 'p', 'a', 's', 's', 0}; +static WCHAR sTestUserNewPass[] = {'n', 'e', 'w', 'p', 'a', 's', 's', 0}; +static const WCHAR sBadNetPath[] = {'\\','\\','B','a',' ',' ','p','a','t','h',0}; +static const WCHAR sInvalidName[] = {'\\',0}; +static const WCHAR sInvalidName2[] = {'\\','\\',0}; +static const WCHAR sEmptyStr[] = { 0 }; + +static NET_API_STATUS (WINAPI *pNetApiBufferFree)(LPVOID)=NULL; +static NET_API_STATUS (WINAPI *pNetApiBufferSize)(LPVOID,LPDWORD)=NULL; +static NET_API_STATUS (WINAPI *pNetQueryDisplayInformation)(LPWSTR,DWORD,DWORD,DWORD,DWORD,LPDWORD,PVOID*)=NULL; +static NET_API_STATUS (WINAPI *pNetUserGetInfo)(LPCWSTR,LPCWSTR,DWORD,LPBYTE*)=NULL; +static NET_API_STATUS (WINAPI *pNetUserModalsGet)(LPCWSTR,DWORD,LPBYTE*)=NULL; +static NET_API_STATUS (WINAPI *pNetUserAdd)(LPCWSTR,DWORD,LPBYTE,LPDWORD)=NULL; +static NET_API_STATUS (WINAPI *pNetUserChangePassword)(LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR)=NULL; +static NET_API_STATUS (WINAPI *pNetUserDel)(LPCWSTR,LPCWSTR)=NULL; + +static int init_access_tests(void) +{ + DWORD dwSize; + BOOL rc; + + user_name[0] = 0; + dwSize = sizeof(user_name); + rc=GetUserNameW(user_name, &dwSize); + if (rc==FALSE && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) + { + skip("GetUserNameW is not available.\n"); + return 0; + } + ok(rc, "User Name Retrieved\n"); + + computer_name[0] = 0; + dwSize = sizeof(computer_name); + ok(GetComputerNameW(computer_name, &dwSize), "Computer Name Retrieved\n"); + return 1; +} + +static NET_API_STATUS create_test_user(void) +{ + USER_INFO_1 usri; + + usri.usri1_name = sTestUserName; + usri.usri1_password = sTestUserOldPass; + usri.usri1_priv = USER_PRIV_USER; + usri.usri1_home_dir = NULL; + usri.usri1_comment = NULL; + usri.usri1_flags = UF_SCRIPT; + usri.usri1_script_path = NULL; + + return pNetUserAdd(NULL, 1, (LPBYTE)&usri, NULL); +} + +static NET_API_STATUS delete_test_user(void) +{ + return pNetUserDel(NULL, sTestUserName); +} + +static void run_usergetinfo_tests(void) +{ + NET_API_STATUS rc; + PUSER_INFO_0 ui0 = NULL; + PUSER_INFO_10 ui10 = NULL; + DWORD dwSize; + + if((rc = create_test_user()) != NERR_Success ) + { + skip("Skipping usergetinfo_tests, create_test_user failed: 0x%08x\n", rc); + return; + } + + /* Level 0 */ + rc=pNetUserGetInfo(NULL, sTestUserName, 0, (LPBYTE *)&ui0); + ok(rc == NERR_Success, "NetUserGetInfo level 0 failed: 0x%08x.\n", rc); + ok(!lstrcmpW(sTestUserName, ui0->usri0_name),"Username mismatch for level 0.\n"); + pNetApiBufferSize(ui0, &dwSize); + ok(dwSize >= (sizeof(USER_INFO_0) + + (lstrlenW(ui0->usri0_name) + 1) * sizeof(WCHAR)), + "Is allocated with NetApiBufferAllocate\n"); + + /* Level 10 */ + rc=pNetUserGetInfo(NULL, sTestUserName, 10, (LPBYTE *)&ui10); + ok(rc == NERR_Success, "NetUserGetInfo level 10 failed: 0x%08x.\n", rc); + ok(!lstrcmpW(sTestUserName, ui10->usri10_name), "Username mismatch for level 10.\n"); + pNetApiBufferSize(ui10, &dwSize); + ok(dwSize >= (sizeof(USER_INFO_10) + + (lstrlenW(ui10->usri10_name) + 1 + + lstrlenW(ui10->usri10_comment) + 1 + + lstrlenW(ui10->usri10_usr_comment) + 1 + + lstrlenW(ui10->usri10_full_name) + 1) * sizeof(WCHAR)), + "Is allocated with NetApiBufferAllocate\n"); + + pNetApiBufferFree(ui0); + pNetApiBufferFree(ui10); + + /* errors handling */ + rc=pNetUserGetInfo(NULL, sTestUserName, 10000, (LPBYTE *)&ui0); + ok(rc == ERROR_INVALID_LEVEL,"Invalid Level: rc=%d\n",rc); + rc=pNetUserGetInfo(NULL, sNonexistentUser, 0, (LPBYTE *)&ui0); + ok(rc == NERR_UserNotFound,"Invalid User Name: rc=%d\n",rc); + todo_wine { + /* FIXME - Currently Wine can't verify whether the network path is good or bad */ + rc=pNetUserGetInfo(sBadNetPath, sTestUserName, 0, (LPBYTE *)&ui0); + ok(rc == ERROR_BAD_NETPATH || rc == ERROR_NETWORK_UNREACHABLE, + "Bad Network Path: rc=%d\n",rc); + } + rc=pNetUserGetInfo(sEmptyStr, sTestUserName, 0, (LPBYTE *)&ui0); + ok(rc == ERROR_BAD_NETPATH || rc == NERR_Success, + "Bad Network Path: rc=%d\n",rc); + rc=pNetUserGetInfo(sInvalidName, sTestUserName, 0, (LPBYTE *)&ui0); + ok(rc == ERROR_INVALID_NAME,"Invalid Server Name: rc=%d\n",rc); + rc=pNetUserGetInfo(sInvalidName2, sTestUserName, 0, (LPBYTE *)&ui0); + ok(rc == ERROR_INVALID_NAME,"Invalid Server Name: rc=%d\n",rc); + + if(delete_test_user() != NERR_Success) + trace("Deleting the test user failed. You might have to manually delete it.\n"); +} + +/* checks Level 1 of NetQueryDisplayInformation + * FIXME: Needs to be rewritten to not depend on the spelling of the users, + * ideally based on the admin and guest user SIDs/RIDs.*/ +static void run_querydisplayinformation1_tests(void) +{ + PNET_DISPLAY_USER Buffer, rec; + DWORD Result, EntryCount; + DWORD i = 0; + BOOL hasAdmin = FALSE; + BOOL hasGuest = FALSE; + static const WCHAR sAdminUserName[] = {'A','d','m','i','n','i','s','t','r','a', + 't','o','r',0}; + static const WCHAR sGuestUserName[] = {'G','u','e','s','t',0}; + + do + { + Result = pNetQueryDisplayInformation( + NULL, 1, i, 1000, MAX_PREFERRED_LENGTH, &EntryCount, + (PVOID *)&Buffer); + + ok((Result == ERROR_SUCCESS) || (Result == ERROR_MORE_DATA), + "Information Retrieved\n"); + rec = Buffer; + for(; EntryCount > 0; EntryCount--) + { + if (!lstrcmpW(rec->usri1_name, sAdminUserName)) + { + ok(!hasAdmin, "One admin user\n"); + ok(rec->usri1_flags & UF_SCRIPT, "UF_SCRIPT flag is set\n"); + ok(rec->usri1_flags & UF_NORMAL_ACCOUNT, "UF_NORMAL_ACCOUNT flag is set\n"); + hasAdmin = TRUE; + } + else if (!lstrcmpW(rec->usri1_name, sGuestUserName)) + { + ok(!hasGuest, "One guest record\n"); + ok(rec->usri1_flags & UF_SCRIPT, "UF_SCRIPT flag is set\n"); + ok(rec->usri1_flags & UF_NORMAL_ACCOUNT, "UF_NORMAL_ACCOUNT flag is set\n"); + hasGuest = TRUE; + } + + i = rec->usri1_next_index; + rec++; + } + + pNetApiBufferFree(Buffer); + } while (Result == ERROR_MORE_DATA); + + ok(hasAdmin, "Has Administrator account\n"); +} + +static void run_usermodalsget_tests(void) +{ + NET_API_STATUS rc; + USER_MODALS_INFO_2 * umi2 = NULL; + + rc = pNetUserModalsGet(NULL, 2, (LPBYTE *)&umi2); + ok(rc == ERROR_SUCCESS, "NetUserModalsGet failed, rc = %d\n", rc); + + if (umi2) + pNetApiBufferFree(umi2); +} + +static void run_userhandling_tests(void) +{ + NET_API_STATUS ret; + USER_INFO_1 usri; + + usri.usri1_priv = USER_PRIV_USER; + usri.usri1_home_dir = NULL; + usri.usri1_comment = NULL; + usri.usri1_flags = UF_SCRIPT; + usri.usri1_script_path = NULL; + + usri.usri1_name = sTooLongName; + usri.usri1_password = sTestUserOldPass; + + ret = pNetUserAdd(NULL, 1, (LPBYTE)&usri, NULL); + ok(ret == NERR_BadUsername, "Adding user with too long username returned 0x%08x\n", ret); + + usri.usri1_name = sTestUserName; + usri.usri1_password = sTooLongPassword; + + ret = pNetUserAdd(NULL, 1, (LPBYTE)&usri, NULL); + ok(ret == NERR_PasswordTooShort, "Adding user with too long password returned 0x%08x\n", ret); + + usri.usri1_name = sTooLongName; + usri.usri1_password = sTooLongPassword; + + ret = pNetUserAdd(NULL, 1, (LPBYTE)&usri, NULL); + ok(ret == NERR_BadUsername, + "Adding user with too long username/password returned 0x%08x\n", ret); + + usri.usri1_name = sTestUserName; + usri.usri1_password = sTestUserOldPass; + + ret = pNetUserAdd(NULL, 5, (LPBYTE)&usri, NULL); + ok(ret == ERROR_INVALID_LEVEL, "Adding user with level 5 returned 0x%08x\n", ret); + + ret = pNetUserAdd(NULL, 1, (LPBYTE)&usri, NULL); + if(ret == ERROR_ACCESS_DENIED) + { + skip("Insufficient permissions to add users. Skipping test.\n"); + return; + } + if(ret == NERR_UserExists) + { + skip("User already exists, skipping test to not mess up the system\n"); + return; + } + + ok(ret == NERR_Success, "Adding user failed with error 0x%08x\n", ret); + if(ret != NERR_Success) + return; + + ret = pNetUserChangePassword(NULL, sNonexistentUser, sTestUserOldPass, + sTestUserNewPass); + ok(ret == NERR_UserNotFound, + "Changing password for nonexistent user returned 0x%08x.\n", ret); + + ret = pNetUserChangePassword(NULL, sTestUserName, sTestUserOldPass, + sTestUserOldPass); + ok(ret == NERR_Success, + "Changing old password to old password returned 0x%08x.\n", ret); + + ret = pNetUserChangePassword(NULL, sTestUserName, sTestUserNewPass, + sTestUserOldPass); + ok(ret == ERROR_INVALID_PASSWORD, + "Trying to change password giving an invalid password returned 0x%08x.\n", ret); + + ret = pNetUserChangePassword(NULL, sTestUserName, sTestUserOldPass, + sTooLongPassword); + ok(ret == ERROR_PASSWORD_RESTRICTION, + "Changing to a password that's too long returned 0x%08x.\n", ret); + + ret = pNetUserChangePassword(NULL, sTestUserName, sTestUserOldPass, + sTestUserNewPass); + ok(ret == NERR_Success, "Changing the password correctly returned 0x%08x.\n", ret); + + ret = pNetUserDel(NULL, sTestUserName); + ok(ret == NERR_Success, "Deleting the user failed.\n"); + + ret = pNetUserDel(NULL, sTestUserName); + ok(ret == NERR_UserNotFound, "Deleting a nonexistent user returned 0x%08x\n",ret); +} + +START_TEST(access) +{ + HMODULE hnetapi32=LoadLibraryA("netapi32.dll"); + + pNetApiBufferFree=(void*)GetProcAddress(hnetapi32,"NetApiBufferFree"); + pNetApiBufferSize=(void*)GetProcAddress(hnetapi32,"NetApiBufferSize"); + pNetQueryDisplayInformation=(void*)GetProcAddress(hnetapi32,"NetQueryDisplayInformation"); + pNetUserGetInfo=(void*)GetProcAddress(hnetapi32,"NetUserGetInfo"); + pNetUserModalsGet=(void*)GetProcAddress(hnetapi32,"NetUserModalsGet"); + pNetUserAdd=(void*)GetProcAddress(hnetapi32, "NetUserAdd"); + pNetUserChangePassword=(void*)GetProcAddress(hnetapi32, "NetUserChangePassword"); + pNetUserDel=(void*)GetProcAddress(hnetapi32, "NetUserDel"); + + /* These functions were introduced with NT. It's safe to assume that + * if one is not available, none are. + */ + if (!pNetApiBufferFree) { + skip("Needed functions are not available\n"); + FreeLibrary(hnetapi32); + return; + } + + if (init_access_tests()) { + run_userhandling_tests(); + run_usergetinfo_tests(); + run_querydisplayinformation1_tests(); + run_usermodalsget_tests(); + } + + FreeLibrary(hnetapi32); +} diff --git a/rostests/winetests/netapi32/apibuf.c b/rostests/winetests/netapi32/apibuf.c new file mode 100644 index 00000000000..920be6ae05e --- /dev/null +++ b/rostests/winetests/netapi32/apibuf.c @@ -0,0 +1,107 @@ +/* + * Copyright 2002 Andriy Palamarchuk + * + * Conformance test of the network buffer function. + * + * 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 + +#include "wine/test.h" +#include +#include +#include +#include +#include +#include +#include + +static NET_API_STATUS (WINAPI *pNetApiBufferAllocate)(DWORD,LPVOID*)=NULL; +static NET_API_STATUS (WINAPI *pNetApiBufferFree)(LPVOID)=NULL; +static NET_API_STATUS (WINAPI *pNetApiBufferReallocate)(LPVOID,DWORD,LPVOID*)=NULL; +static NET_API_STATUS (WINAPI *pNetApiBufferSize)(LPVOID,LPDWORD)=NULL; + + +static void run_apibuf_tests(void) +{ + VOID *p; + DWORD dwSize; + NET_API_STATUS res; + + /* test normal logic */ + ok(pNetApiBufferAllocate(1024, (LPVOID *)&p) == NERR_Success, + "Reserved memory\n"); + ok(pNetApiBufferSize(p, &dwSize) == NERR_Success, "Got size\n"); + ok(dwSize >= 1024, "The size is correct\n"); + + ok(pNetApiBufferReallocate(p, 1500, (LPVOID *) &p) == NERR_Success, + "Reallocated\n"); + ok(pNetApiBufferSize(p, &dwSize) == NERR_Success, "Got size\n"); + ok(dwSize >= 1500, "The size is correct\n"); + + ok(pNetApiBufferFree(p) == NERR_Success, "Freed\n"); + + /* test errors handling */ + ok(pNetApiBufferFree(p) == NERR_Success, "Freed\n"); + + ok(pNetApiBufferSize(p, &dwSize) == NERR_Success, "Got size\n"); + ok(pNetApiBufferSize(NULL, &dwSize) == ERROR_INVALID_PARAMETER, "Error for NULL pointer\n"); + + /* border reallocate cases */ + ok(pNetApiBufferReallocate(0, 1500, (LPVOID *) &p) == NERR_Success, "Reallocate with OldBuffer = NULL failed\n"); + ok(p != NULL, "No memory got allocated\n"); + ok(pNetApiBufferAllocate(1024, (LPVOID *)&p) == NERR_Success, "Memory not reserved\n"); + ok(pNetApiBufferReallocate(p, 0, (LPVOID *) &p) == NERR_Success, "Not freed\n"); + ok(p == NULL, "Pointer not cleared\n"); + + /* 0-length buffer */ + ok(pNetApiBufferAllocate(0, (LPVOID *)&p) == NERR_Success, + "Reserved memory\n"); + ok(pNetApiBufferSize(p, &dwSize) == NERR_Success, "Got size\n"); + ok(dwSize < 0xFFFFFFFF, "The size of the 0-length buffer\n"); + ok(pNetApiBufferFree(p) == NERR_Success, "Freed\n"); + + /* NULL-Pointer */ + /* NT: ERROR_INVALID_PARAMETER, lasterror is untouched) */ + SetLastError(0xdeadbeef); + res = pNetApiBufferAllocate(0, (LPVOID *)NULL); + ok( (res == ERROR_INVALID_PARAMETER) && (GetLastError() == 0xdeadbeef), + "returned %d with 0x%x (expected ERROR_INVALID_PARAMETER with " + "0xdeadbeef)\n", res, GetLastError()); + + SetLastError(0xdeadbeef); + res = pNetApiBufferAllocate(1024, (LPVOID *)NULL); + ok( (res == ERROR_INVALID_PARAMETER) && (GetLastError() == 0xdeadbeef), + "returned %d with 0x%x (expected ERROR_INVALID_PARAMETER with " + "0xdeadbeef)\n", res, GetLastError()); +} + +START_TEST(apibuf) +{ + HMODULE hnetapi32=LoadLibraryA("netapi32.dll"); + + pNetApiBufferAllocate=(void*)GetProcAddress(hnetapi32,"NetApiBufferAllocate"); + pNetApiBufferFree=(void*)GetProcAddress(hnetapi32,"NetApiBufferFree"); + pNetApiBufferReallocate=(void*)GetProcAddress(hnetapi32,"NetApiBufferReallocate"); + pNetApiBufferSize=(void*)GetProcAddress(hnetapi32,"NetApiBufferSize"); + + if (pNetApiBufferAllocate && pNetApiBufferFree && pNetApiBufferReallocate && pNetApiBufferSize) + run_apibuf_tests(); + else + skip("Needed functions are not available\n"); + + FreeLibrary(hnetapi32); +} diff --git a/rostests/winetests/netapi32/ds.c b/rostests/winetests/netapi32/ds.c new file mode 100644 index 00000000000..91119155046 --- /dev/null +++ b/rostests/winetests/netapi32/ds.c @@ -0,0 +1,93 @@ +/* + * Copyright 2005 Paul Vriens + * + * Conformance test of the ds functions. + * + * 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 + +#include +#include +#include +#include +#include + +static DWORD (WINAPI *pDsRoleGetPrimaryDomainInformation)(LPCWSTR, DSROLE_PRIMARY_DOMAIN_INFO_LEVEL, PBYTE*); +static void (WINAPI *pDsRoleFreeMemory)(PVOID); + +static void test_params(void) +{ + DWORD ret; + PDSROLE_PRIMARY_DOMAIN_INFO_BASIC dpdi; + + SetLastError(0xdeadbeef); + ret = pDsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, NULL); + ok( ret == ERROR_INVALID_PARAMETER, "Expected error ERROR_INVALID_PARAMETER, got (%d)\n", ret); + + SetLastError(0xdeadbeef); + ret = pDsRoleGetPrimaryDomainInformation(NULL, 0, NULL); + ok( ret == ERROR_INVALID_PARAMETER, "Expected error ERROR_INVALID_PARAMETER, got (%d)\n", ret); + SetLastError(0xdeadbeef); + ret = pDsRoleGetPrimaryDomainInformation(NULL, 4, NULL); + ok( ret == ERROR_INVALID_PARAMETER, "Expected error ERROR_INVALID_PARAMETER, got (%d)\n", ret); + + SetLastError(0xdeadbeef); + ret = pDsRoleGetPrimaryDomainInformation(NULL, 4, (PBYTE *)&dpdi); + ok( ret == ERROR_INVALID_PARAMETER, "Expected error ERROR_INVALID_PARAMETER, got (%d)\n", ret); +} + +static void test_get(void) +{ + DWORD ret; + PDSROLE_PRIMARY_DOMAIN_INFO_BASIC dpdi; + PDSROLE_UPGRADE_STATUS_INFO dusi; + PDSROLE_OPERATION_STATE_INFO dosi; + + SetLastError(0xdeadbeef); + ret = pDsRoleGetPrimaryDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *)&dpdi); + ok( ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got (%d)\n", ret); + pDsRoleFreeMemory(&dpdi); + + SetLastError(0xdeadbeef); + ret = pDsRoleGetPrimaryDomainInformation(NULL, DsRoleUpgradeStatus, (PBYTE *)&dusi); + todo_wine { ok( ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got (%d)\n", ret); } + pDsRoleFreeMemory(&dusi); + + SetLastError(0xdeadbeef); + ret = pDsRoleGetPrimaryDomainInformation(NULL, DsRoleOperationState, (PBYTE *)&dosi); + todo_wine { ok( ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got (%d)\n", ret); } + pDsRoleFreeMemory(&dosi); +} + + +START_TEST(ds) +{ + HMODULE hnetapi32 = LoadLibrary("netapi32.dll"); + + pDsRoleGetPrimaryDomainInformation=(void*)GetProcAddress(hnetapi32,"DsRoleGetPrimaryDomainInformation"); + if (pDsRoleGetPrimaryDomainInformation) + { + pDsRoleFreeMemory=(void*)GetProcAddress(hnetapi32,"DsRoleFreeMemory"); + + test_params(); + test_get(); + } + else + skip("DsRoleGetPrimaryDomainInformation is not available\n"); + + FreeLibrary(hnetapi32); +} diff --git a/rostests/winetests/netapi32/netapi32.rbuild b/rostests/winetests/netapi32/netapi32.rbuild new file mode 100644 index 00000000000..1baa392a26e --- /dev/null +++ b/rostests/winetests/netapi32/netapi32.rbuild @@ -0,0 +1,17 @@ + + + + . + 0x600 + 0x600 + wine + netapi32 + advapi32 + kernel32 + ntdll + access.c + apibuf.c + ds.c + wksta.c + testlist.c + diff --git a/rostests/winetests/netapi32/testlist.c b/rostests/winetests/netapi32/testlist.c new file mode 100644 index 00000000000..5d8d227ef15 --- /dev/null +++ b/rostests/winetests/netapi32/testlist.c @@ -0,0 +1,21 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_access(void); +extern void func_apibuf(void); +extern void func_ds(void); +extern void func_wksta(void); + +const struct test winetest_testlist[] = +{ + { "access", func_access }, + { "apibuf", func_apibuf }, + { "ds", func_ds }, + { "wksta", func_wksta }, + { 0, 0 } +}; diff --git a/rostests/winetests/netapi32/wksta.c b/rostests/winetests/netapi32/wksta.c new file mode 100644 index 00000000000..ba68eb49809 --- /dev/null +++ b/rostests/winetests/netapi32/wksta.c @@ -0,0 +1,197 @@ +/* + * Copyright 2002 Andriy Palamarchuk + * + * Conformance test of the workstation functions. + * + * 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 + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winnls.h" +#include "winresrc.h" /* Ensure we use Unicode defns with native headers */ +#include "nb30.h" +#include "lmcons.h" +#include "lmerr.h" +#include "lmwksta.h" +#include "lmapibuf.h" + +static NET_API_STATUS (WINAPI *pNetApiBufferFree)(LPVOID)=NULL; +static NET_API_STATUS (WINAPI *pNetApiBufferSize)(LPVOID,LPDWORD)=NULL; +static NET_API_STATUS (WINAPI *pNetpGetComputerName)(LPWSTR*)=NULL; +static NET_API_STATUS (WINAPI *pNetWkstaUserGetInfo)(LPWSTR,DWORD,PBYTE*)=NULL; +static NET_API_STATUS (WINAPI *pNetWkstaTransportEnum)(LPWSTR,DWORD,LPBYTE*, + DWORD,LPDWORD,LPDWORD,LPDWORD)=NULL; + +WCHAR user_name[UNLEN + 1]; +WCHAR computer_name[MAX_COMPUTERNAME_LENGTH + 1]; + +static int init_wksta_tests(void) +{ + DWORD dwSize; + BOOL rc; + + user_name[0] = 0; + dwSize = sizeof(user_name); + rc=GetUserNameW(user_name, &dwSize); + if (rc==FALSE && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) { + skip("GetUserNameW is not implemented\n"); + return 0; + } + ok(rc, "User Name Retrieved\n"); + + computer_name[0] = 0; + dwSize = sizeof(computer_name); + ok(GetComputerNameW(computer_name, &dwSize), "Computer Name Retrieved\n"); + return 1; +} + +static void run_get_comp_name_tests(void) +{ + LPWSTR ws = NULL; + + ok(pNetpGetComputerName(&ws) == NERR_Success, "Computer name is retrieved\n"); + ok(!lstrcmpW(computer_name, ws), "This is really computer name\n"); + pNetApiBufferFree(ws); +} + +static void run_wkstausergetinfo_tests(void) +{ + LPWKSTA_USER_INFO_0 ui0 = NULL; + LPWKSTA_USER_INFO_1 ui1 = NULL; + LPWKSTA_USER_INFO_1101 ui1101 = NULL; + DWORD dwSize; + + /* Level 0 */ + ok(pNetWkstaUserGetInfo(NULL, 0, (LPBYTE *)&ui0) == NERR_Success, + "NetWkstaUserGetInfo is successful\n"); + ok(!lstrcmpW(user_name, ui0->wkui0_username), "This is really user name\n"); + pNetApiBufferSize(ui0, &dwSize); + ok(dwSize >= (sizeof(WKSTA_USER_INFO_0) + + lstrlenW(ui0->wkui0_username) * sizeof(WCHAR)), + "Is allocated with NetApiBufferAllocate\n"); + + /* Level 1 */ + ok(pNetWkstaUserGetInfo(NULL, 1, (LPBYTE *)&ui1) == NERR_Success, + "NetWkstaUserGetInfo is successful\n"); + ok(lstrcmpW(ui1->wkui1_username, ui0->wkui0_username) == 0, + "the same name as returned for level 0\n"); + pNetApiBufferSize(ui1, &dwSize); + ok(dwSize >= (sizeof(WKSTA_USER_INFO_1) + + (lstrlenW(ui1->wkui1_username) + + lstrlenW(ui1->wkui1_logon_domain) + + lstrlenW(ui1->wkui1_oth_domains) + + lstrlenW(ui1->wkui1_logon_server)) * sizeof(WCHAR)), + "Is allocated with NetApiBufferAllocate\n"); + + /* Level 1101 */ + ok(pNetWkstaUserGetInfo(NULL, 1101, (LPBYTE *)&ui1101) == NERR_Success, + "NetWkstaUserGetInfo is successful\n"); + ok(lstrcmpW(ui1101->wkui1101_oth_domains, ui1->wkui1_oth_domains) == 0, + "the same oth_domains as returned for level 1\n"); + pNetApiBufferSize(ui1101, &dwSize); + ok(dwSize >= (sizeof(WKSTA_USER_INFO_1101) + + lstrlenW(ui1101->wkui1101_oth_domains) * sizeof(WCHAR)), + "Is allocated with NetApiBufferAllocate\n"); + + pNetApiBufferFree(ui0); + pNetApiBufferFree(ui1); + pNetApiBufferFree(ui1101); + + /* errors handling */ + ok(pNetWkstaUserGetInfo(NULL, 10000, (LPBYTE *)&ui0) == ERROR_INVALID_LEVEL, + "Invalid level\n"); +} + +static void run_wkstatransportenum_tests(void) +{ + LPBYTE bufPtr; + NET_API_STATUS apiReturn; + DWORD entriesRead, totalEntries; + + /* 1st check: is param 2 (level) correct? (only if param 5 passed?) */ + apiReturn = pNetWkstaTransportEnum(NULL, 1, NULL, MAX_PREFERRED_LENGTH, + NULL, &totalEntries, NULL); + ok(apiReturn == ERROR_INVALID_LEVEL || apiReturn == ERROR_INVALID_PARAMETER, + "NetWkstaTransportEnum returned %d\n", apiReturn); + + /* 2nd check: is param 5 passed? (only if level passes?) */ + apiReturn = pNetWkstaTransportEnum(NULL, 0, NULL, MAX_PREFERRED_LENGTH, + NULL, &totalEntries, NULL); + + /* if no network adapter present, bail, the rest of the test will fail */ + if (apiReturn == ERROR_NETWORK_UNREACHABLE) + return; + + ok(apiReturn == STATUS_ACCESS_VIOLATION || apiReturn == ERROR_INVALID_PARAMETER, + "NetWkstaTransportEnum returned %d\n", apiReturn); + + /* 3rd check: is param 3 passed? */ + apiReturn = pNetWkstaTransportEnum(NULL, 0, NULL, MAX_PREFERRED_LENGTH, + NULL, NULL, NULL); + ok(apiReturn == STATUS_ACCESS_VIOLATION || apiReturn == RPC_X_NULL_REF_POINTER || apiReturn == ERROR_INVALID_PARAMETER, + "NetWkstaTransportEnum returned %d\n", apiReturn); + + /* 4th check: is param 6 passed? */ + apiReturn = pNetWkstaTransportEnum(NULL, 0, &bufPtr, MAX_PREFERRED_LENGTH, + &entriesRead, NULL, NULL); + ok(apiReturn == RPC_X_NULL_REF_POINTER, "null pointer\n"); + + /* final check: valid return, actually get data back */ + apiReturn = pNetWkstaTransportEnum(NULL, 0, &bufPtr, MAX_PREFERRED_LENGTH, + &entriesRead, &totalEntries, NULL); + ok(apiReturn == NERR_Success || apiReturn == ERROR_NETWORK_UNREACHABLE, + "NetWkstaTransportEnum returned %d\n", apiReturn); + if (apiReturn == NERR_Success) { + /* WKSTA_TRANSPORT_INFO_0 *transports = (WKSTA_TRANSPORT_INFO_0 *)bufPtr; */ + + ok(bufPtr != NULL, "got data back\n"); + ok(entriesRead > 0, "read at least one transport\n"); + ok(totalEntries > 0, "at least one transport\n"); + pNetApiBufferFree(bufPtr); + } +} + +START_TEST(wksta) +{ + HMODULE hnetapi32=LoadLibraryA("netapi32.dll"); + + pNetApiBufferFree=(void*)GetProcAddress(hnetapi32,"NetApiBufferFree"); + pNetApiBufferSize=(void*)GetProcAddress(hnetapi32,"NetApiBufferSize"); + pNetpGetComputerName=(void*)GetProcAddress(hnetapi32,"NetpGetComputerName"); + pNetWkstaUserGetInfo=(void*)GetProcAddress(hnetapi32,"NetWkstaUserGetInfo"); + pNetWkstaTransportEnum=(void*)GetProcAddress(hnetapi32,"NetWkstaTransportEnum"); + + /* These functions were introduced with NT. It's safe to assume that + * if one is not available, none are. + */ + if (!pNetApiBufferFree) { + skip("Needed functions are not available\n"); + FreeLibrary(hnetapi32); + return; + } + + if (init_wksta_tests()) { + run_get_comp_name_tests(); + run_wkstausergetinfo_tests(); + run_wkstatransportenum_tests(); + } + + FreeLibrary(hnetapi32); +} diff --git a/rostests/winetests/odbccp32/misc.c b/rostests/winetests/odbccp32/misc.c new file mode 100644 index 00000000000..f5ea5361a0b --- /dev/null +++ b/rostests/winetests/odbccp32/misc.c @@ -0,0 +1,132 @@ +/* + * Copyright 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "odbcinst.h" + +static void test_SQLConfigMode(void) +{ + BOOL bool_ret; + DWORD error_code; + RETCODE sql_ret; + UWORD config_mode; + int i; + + ok(SQLGetConfigMode(NULL), "SQLGetConfigMode(NULL) should succeed\n"); + + bool_ret = SQLGetConfigMode(&config_mode); + ok(bool_ret && config_mode == ODBC_BOTH_DSN, "Failed to get the initial SQLGetConfigMode or it was not both\n"); + + bool_ret = SQLSetConfigMode(3); + sql_ret = SQLInstallerErrorW(1, &error_code, NULL, 0, NULL); + ok(!bool_ret && sql_ret == SQL_SUCCESS_WITH_INFO && error_code == ODBC_ERROR_INVALID_PARAM_SEQUENCE, "SQLSetConfigMode with invalid argument did not fail correctly\n"); + + ok (ODBC_SYSTEM_DSN == 2 && ODBC_USER_DSN == 1 && ODBC_BOTH_DSN == 0, "SQLSetConfigMode modes not as expected\n"); + for (i = ODBC_SYSTEM_DSN; i >= ODBC_BOTH_DSN; --i) + { + ok(SQLSetConfigMode((UWORD)i), "SQLSetConfigMode Failed to set config mode\n"); + bool_ret = SQLGetConfigMode(&config_mode); + ok(bool_ret && config_mode == i, "Failed to confirm SQLSetConfigMode.\n"); + } + /* And that leaves it correctly on BOTH */ +} + +static void test_SQLInstallerError(void) +{ + RETCODE sql_ret; + + /* MSDN states that the error number should be between 1 and 8. Passing 0 is an error */ + sql_ret = SQLInstallerError(0, NULL, NULL, 0, NULL); + ok(sql_ret == SQL_ERROR, "SQLInstallerError(0...) failed with %d instead of SQL_ERROR\n", sql_ret); + /* However numbers greater than 8 do not return SQL_ERROR. + * I am currenly unsure as to whether it should return SQL_NO_DATA or "the same as for error 8"; + * I have never been able to generate 8 errors to test it + */ + sql_ret = SQLInstallerError(65535, NULL, NULL, 0, NULL); + ok(sql_ret == SQL_NO_DATA, "SQLInstallerError(>8...) failed with %d instead of SQL_NO_DATA\n", sql_ret); + + /* Force an error to work with. This should generate ODBC_ERROR_INVALID_BUFF_LEN */ + ok(!SQLGetInstalledDrivers(0, 0, 0), "Failed to force an error for testing\n"); + sql_ret = SQLInstallerError(2, NULL, NULL, 0, NULL); + ok(sql_ret == SQL_NO_DATA, "Too many errors when forcing an error for testing\n"); + + /* Null pointers are acceptable in all obvious places */ + sql_ret = SQLInstallerError(1, NULL, NULL, 0, NULL); + ok(sql_ret == SQL_SUCCESS_WITH_INFO, "SQLInstallerError(null addresses) failed with %d instead of SQL_SUCCESS_WITH_INFO\n", sql_ret); +} + +static void test_SQLInstallDriverManager(void) +{ + BOOL bool_ret; + RETCODE sql_ret; + DWORD error_code; + CHAR target_path[MAX_PATH]; + WORD path_out; + + /* NULL check */ + bool_ret = SQLInstallDriverManager(NULL, 0, NULL); + sql_ret = SQLInstallerErrorW(1, &error_code, NULL, 0, NULL); + ok(!bool_ret, "SQLInstallDriverManager unexpectedly succeeded\n"); + todo_wine + ok(sql_ret == SQL_SUCCESS_WITH_INFO && error_code == ODBC_ERROR_INVALID_BUFF_LEN, + "Expected SQLInstallDriverManager to fail with ODBC_ERROR_INVALID_BUFF_LEN\n"); + + /* Length smaller than MAX_PATH */ + bool_ret = SQLInstallDriverManager(target_path, MAX_PATH / 2, NULL); + sql_ret = SQLInstallerErrorW(1, &error_code, NULL, 0, NULL); + todo_wine { + ok(!bool_ret, "SQLInstallDriverManager unexpectedly succeeded\n"); + ok(sql_ret == SQL_SUCCESS_WITH_INFO && error_code == ODBC_ERROR_INVALID_BUFF_LEN, + "Expected SQLInstallDriverManager to fail with ODBC_ERROR_INVALID_BUFF_LEN\n"); + } + + path_out = 0xcafe; + bool_ret = SQLInstallDriverManager(target_path, MAX_PATH / 2, &path_out); + sql_ret = SQLInstallerErrorW(1, &error_code, NULL, 0, NULL); + todo_wine { + ok(!bool_ret, "SQLInstallDriverManager unexpectedly succeeded\n"); + ok(sql_ret == SQL_SUCCESS_WITH_INFO && error_code == ODBC_ERROR_INVALID_BUFF_LEN, + "Expected SQLInstallDriverManager to fail with ODBC_ERROR_INVALID_BUFF_LEN\n"); + ok(path_out == 0xcafe, "Expected path_out to not have changed\n"); + } + + /* Length OK */ + bool_ret = SQLInstallDriverManager(target_path, MAX_PATH, NULL); + sql_ret = SQLInstallerErrorW(1, &error_code, NULL, 0, NULL); + ok(bool_ret, "SQLInstallDriverManager unexpectedly failed\n"); + ok(sql_ret == SQL_NO_DATA, "Expected SQL_NO_DATA, got %d\n", sql_ret); + + path_out = 0xcafe; + bool_ret = SQLInstallDriverManager(target_path, MAX_PATH, &path_out); + sql_ret = SQLInstallerErrorW(1, &error_code, NULL, 0, NULL); + ok(bool_ret, "SQLInstallDriverManager unexpectedly failed\n"); + ok(sql_ret == SQL_NO_DATA, "Expected SQL_NO_DATA, got %d\n", sql_ret); + /* path_out should in practice be less than 0xcafe */ + ok(path_out != 0xcafe, "Expected path_out to show the correct amount of bytes\n"); +} + +START_TEST(misc) +{ + test_SQLConfigMode(); + test_SQLInstallerError(); + test_SQLInstallDriverManager(); +} diff --git a/rostests/winetests/odbccp32/odbccp32.rbuild b/rostests/winetests/odbccp32/odbccp32.rbuild new file mode 100644 index 00000000000..d3550a35842 --- /dev/null +++ b/rostests/winetests/odbccp32/odbccp32.rbuild @@ -0,0 +1,14 @@ + + + + . + 0x600 + 0x600 + wine + odbccp32 + user32 + kernel32 + ntdll + misc.c + testlist.c + diff --git a/rostests/winetests/odbccp32/testlist.c b/rostests/winetests/odbccp32/testlist.c new file mode 100644 index 00000000000..a92d75f6c40 --- /dev/null +++ b/rostests/winetests/odbccp32/testlist.c @@ -0,0 +1,15 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_misc(void); + +const struct test winetest_testlist[] = +{ + { "misc", func_misc }, + { 0, 0 } +}; diff --git a/rostests/winetests/ole32/clipboard.c b/rostests/winetests/ole32/clipboard.c new file mode 100644 index 00000000000..0127ce5e44c --- /dev/null +++ b/rostests/winetests/ole32/clipboard.c @@ -0,0 +1,381 @@ +/* + * Clipboard unit tests + * + * Copyright 2006 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 + */ + +#define COBJMACROS + +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "wine/test.h" + +#define InitFormatEtc(fe, cf, med) \ + {\ + (fe).cfFormat=cf;\ + (fe).dwAspect=DVASPECT_CONTENT;\ + (fe).ptd=NULL;\ + (fe).tymed=med;\ + (fe).lindex=-1;\ + }; + +typedef struct DataObjectImpl { + const IDataObjectVtbl *lpVtbl; + LONG ref; + + FORMATETC *fmtetc; + UINT fmtetc_cnt; + + HANDLE text; +} DataObjectImpl; + +typedef struct EnumFormatImpl { + const IEnumFORMATETCVtbl *lpVtbl; + LONG ref; + + FORMATETC *fmtetc; + UINT fmtetc_cnt; + + UINT cur; +} EnumFormatImpl; + +static HRESULT EnumFormatImpl_Create(FORMATETC *fmtetc, UINT size, LPENUMFORMATETC *lplpformatetc); + +static HRESULT WINAPI EnumFormatImpl_QueryInterface(IEnumFORMATETC *iface, REFIID riid, LPVOID *ppvObj) +{ + EnumFormatImpl *This = (EnumFormatImpl*)iface; + + if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IEnumFORMATETC)) { + IEnumFORMATETC_AddRef(iface); + *ppvObj = (LPVOID)This; + return S_OK; + } + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI EnumFormatImpl_AddRef(IEnumFORMATETC *iface) +{ + EnumFormatImpl *This = (EnumFormatImpl*)iface; + LONG ref = InterlockedIncrement(&This->ref); + return ref; +} + +static ULONG WINAPI EnumFormatImpl_Release(IEnumFORMATETC *iface) +{ + EnumFormatImpl *This = (EnumFormatImpl*)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + if(!ref) { + HeapFree(GetProcessHeap(), 0, This->fmtetc); + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +static HRESULT WINAPI EnumFormatImpl_Next(IEnumFORMATETC *iface, ULONG celt, + FORMATETC *rgelt, ULONG *pceltFetched) +{ + EnumFormatImpl *This = (EnumFormatImpl*)iface; + ULONG count = 0; + + if(!rgelt) + return E_INVALIDARG; + + count = min(celt, This->fmtetc_cnt-This->cur); + if(count > 0) { + memcpy(rgelt, This->fmtetc+This->cur, count*sizeof(FORMATETC)); + This->cur += count; + } + if(pceltFetched) + *pceltFetched = count; + return count == celt ? S_OK : S_FALSE; +} + +static HRESULT WINAPI EnumFormatImpl_Skip(IEnumFORMATETC *iface, ULONG celt) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI EnumFormatImpl_Reset(IEnumFORMATETC *iface) +{ + EnumFormatImpl *This = (EnumFormatImpl*)iface; + + This->cur = 0; + return S_OK; +} + +static HRESULT WINAPI EnumFormatImpl_Clone(IEnumFORMATETC *iface, IEnumFORMATETC **ppenum) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static const IEnumFORMATETCVtbl VT_EnumFormatImpl = { + EnumFormatImpl_QueryInterface, + EnumFormatImpl_AddRef, + EnumFormatImpl_Release, + EnumFormatImpl_Next, + EnumFormatImpl_Skip, + EnumFormatImpl_Reset, + EnumFormatImpl_Clone +}; + +static HRESULT EnumFormatImpl_Create(FORMATETC *fmtetc, UINT fmtetc_cnt, IEnumFORMATETC **lplpformatetc) +{ + EnumFormatImpl *ret; + + ret = HeapAlloc(GetProcessHeap(), 0, sizeof(EnumFormatImpl)); + ret->lpVtbl = &VT_EnumFormatImpl; + ret->ref = 1; + ret->cur = 0; + ret->fmtetc_cnt = fmtetc_cnt; + ret->fmtetc = HeapAlloc(GetProcessHeap(), 0, fmtetc_cnt*sizeof(FORMATETC)); + memcpy(ret->fmtetc, fmtetc, fmtetc_cnt*sizeof(FORMATETC)); + *lplpformatetc = (LPENUMFORMATETC)ret; + return S_OK; +} + +static HRESULT WINAPI DataObjectImpl_QueryInterface(IDataObject *iface, REFIID riid, LPVOID *ppvObj) +{ + DataObjectImpl *This = (DataObjectImpl*)iface; + + if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDataObject)) { + IDataObject_AddRef(iface); + *ppvObj = (LPVOID)This; + return S_OK; + } + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI DataObjectImpl_AddRef(IDataObject* iface) +{ + DataObjectImpl *This = (DataObjectImpl*)iface; + ULONG ref = InterlockedIncrement(&This->ref); + return ref; +} + +static ULONG WINAPI DataObjectImpl_Release(IDataObject* iface) +{ + DataObjectImpl *This = (DataObjectImpl*)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + if(!ref) { + if(This->text) GlobalFree(This->text); + if(This->fmtetc) GlobalFree(This->fmtetc); + HeapFree(GetProcessHeap(), 0, This); + } + + return ref; +} + +static HRESULT WINAPI DataObjectImpl_GetData(IDataObject* iface, FORMATETC *pformatetc, STGMEDIUM *pmedium) +{ + DataObjectImpl *This = (DataObjectImpl*)iface; + + if(pformatetc->lindex != -1) + return DV_E_LINDEX; + + if(!(pformatetc->tymed & TYMED_HGLOBAL)) + return DV_E_TYMED; + + if(This->text && pformatetc->cfFormat == CF_TEXT) + U(*pmedium).hGlobal = This->text; + else + return DV_E_FORMATETC; + + pmedium->tymed = TYMED_HGLOBAL; + pmedium->pUnkForRelease = (LPUNKNOWN)iface; + IUnknown_AddRef(pmedium->pUnkForRelease); + return S_OK; +} + +static HRESULT WINAPI DataObjectImpl_GetDataHere(IDataObject* iface, FORMATETC *pformatetc, STGMEDIUM *pmedium) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObjectImpl_QueryGetData(IDataObject* iface, FORMATETC *pformatetc) +{ + DataObjectImpl *This = (DataObjectImpl*)iface; + UINT i; + BOOL foundFormat = FALSE; + + if(pformatetc->lindex != -1) + return DV_E_LINDEX; + + for(i=0; ifmtetc_cnt; i++) { + if(This->fmtetc[i].cfFormat == pformatetc->cfFormat) { + foundFormat = TRUE; + if(This->fmtetc[i].tymed == pformatetc->tymed) + return S_OK; + } + } + return foundFormat?DV_E_FORMATETC:DV_E_TYMED; +} + +static HRESULT WINAPI DataObjectImpl_GetCanonicalFormatEtc(IDataObject* iface, FORMATETC *pformatectIn, + FORMATETC *pformatetcOut) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObjectImpl_SetData(IDataObject* iface, FORMATETC *pformatetc, + STGMEDIUM *pmedium, BOOL fRelease) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObjectImpl_EnumFormatEtc(IDataObject* iface, DWORD dwDirection, + IEnumFORMATETC **ppenumFormatEtc) +{ + DataObjectImpl *This = (DataObjectImpl*)iface; + + if(dwDirection != DATADIR_GET) { + ok(0, "unexpected direction %d\n", dwDirection); + return E_NOTIMPL; + } + return EnumFormatImpl_Create(This->fmtetc, This->fmtetc_cnt, ppenumFormatEtc); +} + +static HRESULT WINAPI DataObjectImpl_DAdvise(IDataObject* iface, FORMATETC *pformatetc, DWORD advf, + IAdviseSink *pAdvSink, DWORD *pdwConnection) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObjectImpl_DUnadvise(IDataObject* iface, DWORD dwConnection) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObjectImpl_EnumDAdvise(IDataObject* iface, IEnumSTATDATA **ppenumAdvise) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static const IDataObjectVtbl VT_DataObjectImpl = +{ + DataObjectImpl_QueryInterface, + DataObjectImpl_AddRef, + DataObjectImpl_Release, + DataObjectImpl_GetData, + DataObjectImpl_GetDataHere, + DataObjectImpl_QueryGetData, + DataObjectImpl_GetCanonicalFormatEtc, + DataObjectImpl_SetData, + DataObjectImpl_EnumFormatEtc, + DataObjectImpl_DAdvise, + DataObjectImpl_DUnadvise, + DataObjectImpl_EnumDAdvise +}; + +static HRESULT DataObjectImpl_CreateText(LPCSTR text, LPDATAOBJECT *lplpdataobj) +{ + DataObjectImpl *obj; + + obj = HeapAlloc(GetProcessHeap(), 0, sizeof(DataObjectImpl)); + obj->lpVtbl = &VT_DataObjectImpl; + obj->ref = 1; + obj->text = GlobalAlloc(GMEM_MOVEABLE, strlen(text) + 1); + strcpy(GlobalLock(obj->text), text); + GlobalUnlock(obj->text); + + obj->fmtetc_cnt = 1; + obj->fmtetc = HeapAlloc(GetProcessHeap(), 0, obj->fmtetc_cnt*sizeof(FORMATETC)); + InitFormatEtc(obj->fmtetc[0], CF_TEXT, TYMED_HGLOBAL); + + *lplpdataobj = (LPDATAOBJECT)obj; + return S_OK; +} + +static void test_set_clipboard(void) +{ + HRESULT hr; + ULONG ref; + LPDATAOBJECT data1, data2; + hr = DataObjectImpl_CreateText("data1", &data1); + ok(SUCCEEDED(hr), "Failed to create data1 object: 0x%08x\n", hr); + if(FAILED(hr)) + return; + hr = DataObjectImpl_CreateText("data2", &data2); + ok(SUCCEEDED(hr), "Failed to create data2 object: 0x%08x\n", hr); + if(FAILED(hr)) + return; + + hr = OleSetClipboard(data1); + todo_wine + ok(hr == CO_E_NOTINITIALIZED, "OleSetClipboard should have failed with CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); + + CoInitialize(NULL); + hr = OleSetClipboard(data1); + todo_wine + ok(hr == CO_E_NOTINITIALIZED, "OleSetClipboard should have failed with CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); + CoUninitialize(); + + hr = OleInitialize(NULL); + ok(hr == S_OK, "OleInitialize failed with error 0x%08x\n", hr); + + hr = OleSetClipboard(data1); + ok(hr == S_OK, "failed to set clipboard to data1, hr = 0x%08x\n", hr); + hr = OleIsCurrentClipboard(data1); + ok(hr == S_OK, "expected current clipboard to be data1, hr = 0x%08x\n", hr); + hr = OleIsCurrentClipboard(data2); + ok(hr == S_FALSE, "did not expect current clipboard to be data2, hr = 0x%08x\n", hr); + + hr = OleSetClipboard(data2); + ok(hr == S_OK, "failed to set clipboard to data2, hr = 0x%08x\n", hr); + hr = OleIsCurrentClipboard(data1); + ok(hr == S_FALSE, "did not expect current clipboard to be data1, hr = 0x%08x\n", hr); + hr = OleIsCurrentClipboard(data2); + ok(hr == S_OK, "expected current clipboard to be data2, hr = 0x%08x\n", hr); + + hr = OleFlushClipboard(); + ok(hr == S_OK, "failed to flush clipboard, hr = 0x%08x\n", hr); + hr = OleIsCurrentClipboard(data1); + ok(hr == S_FALSE, "did not expect current clipboard to be data1, hr = 0x%08x\n", hr); + hr = OleIsCurrentClipboard(data2); + ok(hr == S_FALSE, "did not expect current clipboard to be data2, hr = 0x%08x\n", hr); + + ok(OleSetClipboard(NULL) == S_OK, "failed to clear clipboard, hr = 0x%08x\n", hr); + + ref = IDataObject_Release(data1); + ok(ref == 0, "expected data1 ref=0, got %d\n", ref); + ref = IDataObject_Release(data2); + ok(ref == 0, "expected data2 ref=0, got %d\n", ref); + + OleUninitialize(); +} + + +START_TEST(clipboard) +{ + test_set_clipboard(); +} diff --git a/rostests/winetests/ole32/compobj.c b/rostests/winetests/ole32/compobj.c new file mode 100644 index 00000000000..89cf9b9d891 --- /dev/null +++ b/rostests/winetests/ole32/compobj.c @@ -0,0 +1,1025 @@ +/* + * Component Object Tests + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#define CONST_VTABLE + +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "shlguid.h" +#include "urlmon.h" /* for CLSID_FileProtocol */ + +#include "wine/test.h" + +/* functions that are not present on all versions of Windows */ +HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit); +HRESULT (WINAPI * pCoGetObjectContext)(REFIID riid, LPVOID *ppv); + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) +#define ok_more_than_one_lock() ok(cLocks > 0, "Number of locks should be > 0, but actually is %d\n", cLocks) +#define ok_no_locks() ok(cLocks == 0, "Number of locks should be 0, but actually is %d\n", cLocks) + +static const CLSID CLSID_non_existent = { 0x12345678, 0x1234, 0x1234, { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 } }; +static const CLSID CLSID_CDeviceMoniker = { 0x4315d437, 0x5b8c, 0x11d0, { 0xbd, 0x3b, 0x00, 0xa0, 0xc9, 0x11, 0xce, 0x86 } }; +static WCHAR devicedotone[] = {'d','e','v','i','c','e','.','1',0}; +static const WCHAR wszNonExistent[] = {'N','o','n','E','x','i','s','t','e','n','t',0}; +static WCHAR wszCLSID_CDeviceMoniker[] = +{ + '{', + '4','3','1','5','d','4','3','7','-', + '5','b','8','c','-', + '1','1','d','0','-', + 'b','d','3','b','-', + '0','0','a','0','c','9','1','1','c','e','8','6', + '}',0 +}; + +static const IID IID_IWineTest = +{ + 0x5201163f, + 0x8164, + 0x4fd0, + {0xa1, 0xa2, 0x5d, 0x5a, 0x36, 0x54, 0xd3, 0xbd} +}; /* 5201163f-8164-4fd0-a1a2-5d5a3654d3bd */ +static const CLSID CLSID_WineOOPTest = { + 0x5201163f, + 0x8164, + 0x4fd0, + {0xa1, 0xa2, 0x5d, 0x5a, 0x36, 0x54, 0xd3, 0xbd} +}; /* 5201163f-8164-4fd0-a1a2-5d5a3654d3bd */ + +static LONG cLocks; + +static void LockModule(void) +{ + InterlockedIncrement(&cLocks); +} + +static void UnlockModule(void) +{ + InterlockedDecrement(&cLocks); +} + +static HRESULT WINAPI Test_IClassFactory_QueryInterface( + LPCLASSFACTORY iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IClassFactory)) + { + *ppvObj = (LPVOID)iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_IClassFactory_AddRef(LPCLASSFACTORY iface) +{ + LockModule(); + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_IClassFactory_Release(LPCLASSFACTORY iface) +{ + UnlockModule(); + return 1; /* non-heap-based object */ +} + +static HRESULT WINAPI Test_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + *ppvObj = NULL; + if (pUnkOuter) return CLASS_E_NOAGGREGATION; + return E_NOINTERFACE; +} + +static HRESULT WINAPI Test_IClassFactory_LockServer( + LPCLASSFACTORY iface, + BOOL fLock) +{ + return S_OK; +} + +static const IClassFactoryVtbl TestClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + Test_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +static IClassFactory Test_ClassFactory = { &TestClassFactory_Vtbl }; + +static void test_ProgIDFromCLSID(void) +{ + LPWSTR progid; + HRESULT hr = ProgIDFromCLSID(&CLSID_CDeviceMoniker, &progid); + ok(hr == S_OK, "ProgIDFromCLSID failed with error 0x%08x\n", hr); + if (hr == S_OK) + { + ok(!lstrcmpiW(progid, devicedotone), "Didn't get expected prog ID\n"); + CoTaskMemFree(progid); + } + + progid = (LPWSTR)0xdeadbeef; + hr = ProgIDFromCLSID(&CLSID_non_existent, &progid); + ok(hr == REGDB_E_CLASSNOTREG, "ProgIDFromCLSID returned %08x\n", hr); + ok(progid == NULL, "ProgIDFromCLSID returns with progid %p\n", progid); + + hr = ProgIDFromCLSID(&CLSID_CDeviceMoniker, NULL); + ok(hr == E_INVALIDARG, "ProgIDFromCLSID should return E_INVALIDARG instead of 0x%08x\n", hr); +} + +static void test_CLSIDFromProgID(void) +{ + CLSID clsid; + HRESULT hr = CLSIDFromProgID(devicedotone, &clsid); + ok(hr == S_OK, "CLSIDFromProgID failed with error 0x%08x\n", hr); + ok(IsEqualCLSID(&clsid, &CLSID_CDeviceMoniker), "clsid wasn't equal to CLSID_CDeviceMoniker\n"); + + hr = CLSIDFromString(devicedotone, &clsid); + ok_ole_success(hr, "CLSIDFromString"); + ok(IsEqualCLSID(&clsid, &CLSID_CDeviceMoniker), "clsid wasn't equal to CLSID_CDeviceMoniker\n"); + + /* test some failure cases */ + + hr = CLSIDFromProgID(wszNonExistent, NULL); + ok(hr == E_INVALIDARG, "CLSIDFromProgID should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CLSIDFromProgID(NULL, &clsid); + ok(hr == E_INVALIDARG, "CLSIDFromProgID should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + memset(&clsid, 0xcc, sizeof(clsid)); + hr = CLSIDFromProgID(wszNonExistent, &clsid); + ok(hr == CO_E_CLASSSTRING, "CLSIDFromProgID on nonexistent ProgID should have returned CO_E_CLASSSTRING instead of 0x%08x\n", hr); + ok(IsEqualCLSID(&clsid, &CLSID_NULL), "CLSIDFromProgID should have set clsid to all-zeros on failure\n"); +} + +static void test_CLSIDFromString(void) +{ + CLSID clsid; + HRESULT hr = CLSIDFromString(wszCLSID_CDeviceMoniker, &clsid); + ok_ole_success(hr, "CLSIDFromString"); + ok(IsEqualCLSID(&clsid, &CLSID_CDeviceMoniker), "clsid wasn't equal to CLSID_CDeviceMoniker\n"); + + hr = CLSIDFromString(NULL, &clsid); + ok_ole_success(hr, "CLSIDFromString"); + ok(IsEqualCLSID(&clsid, &CLSID_NULL), "clsid wasn't equal to CLSID_NULL\n"); +} + +static void test_CoCreateInstance(void) +{ + REFCLSID rclsid = &CLSID_MyComputer; + IUnknown *pUnk = (IUnknown *)0xdeadbeef; + HRESULT hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk); + ok(hr == CO_E_NOTINITIALIZED, "CoCreateInstance should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); + ok(pUnk == NULL, "CoCreateInstance should have changed the passed in pointer to NULL, instead of %p\n", pUnk); + + OleInitialize(NULL); + hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk); + ok_ole_success(hr, "CoCreateInstance"); + IUnknown_Release(pUnk); + OleUninitialize(); + + hr = CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk); + ok(hr == CO_E_NOTINITIALIZED, "CoCreateInstance should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); +} + +static void test_CoGetClassObject(void) +{ + IUnknown *pUnk = (IUnknown *)0xdeadbeef; + HRESULT hr = CoGetClassObject(&CLSID_MyComputer, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void **)&pUnk); + ok(hr == CO_E_NOTINITIALIZED, "CoGetClassObject should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); + ok(pUnk == NULL, "CoGetClassObject should have changed the passed in pointer to NULL, instead of %p\n", pUnk); + + hr = CoGetClassObject(&CLSID_MyComputer, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, NULL); + ok(hr == E_INVALIDARG, "CoGetClassObject should have returned E_INVALIDARG instead of 0x%08x\n", hr); +} + +static ATOM register_dummy_class(void) +{ + WNDCLASS wc = + { + 0, + DefWindowProc, + 0, + 0, + GetModuleHandle(NULL), + NULL, + LoadCursor(NULL, IDC_ARROW), + (HBRUSH)(COLOR_BTNFACE+1), + NULL, + TEXT("WineOleTestClass"), + }; + + return RegisterClass(&wc); +} + +static void test_ole_menu(void) +{ + HWND hwndFrame; + HRESULT hr; + + hwndFrame = CreateWindow(MAKEINTATOM(register_dummy_class()), "Test", 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); + hr = OleSetMenuDescriptor(NULL, hwndFrame, NULL, NULL, NULL); + todo_wine ok_ole_success(hr, "OleSetMenuDescriptor"); + + DestroyWindow(hwndFrame); +} + + +static HRESULT WINAPI MessageFilter_QueryInterface(IMessageFilter *iface, REFIID riid, void ** ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IClassFactory)) + { + *ppvObj = (LPVOID)iface; + IMessageFilter_AddRef(iface); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI MessageFilter_AddRef(IMessageFilter *iface) +{ + return 2; /* non-heap object */ +} + +static ULONG WINAPI MessageFilter_Release(IMessageFilter *iface) +{ + return 1; /* non-heap object */ +} + +static DWORD WINAPI MessageFilter_HandleInComingCall( + IMessageFilter *iface, + DWORD dwCallType, + HTASK threadIDCaller, + DWORD dwTickCount, + LPINTERFACEINFO lpInterfaceInfo) +{ + trace("HandleInComingCall\n"); + return SERVERCALL_ISHANDLED; +} + +static DWORD WINAPI MessageFilter_RetryRejectedCall( + IMessageFilter *iface, + HTASK threadIDCallee, + DWORD dwTickCount, + DWORD dwRejectType) +{ + trace("RetryRejectedCall\n"); + return 0; +} + +static DWORD WINAPI MessageFilter_MessagePending( + IMessageFilter *iface, + HTASK threadIDCallee, + DWORD dwTickCount, + DWORD dwPendingType) +{ + trace("MessagePending\n"); + return PENDINGMSG_WAITNOPROCESS; +} + +static const IMessageFilterVtbl MessageFilter_Vtbl = +{ + MessageFilter_QueryInterface, + MessageFilter_AddRef, + MessageFilter_Release, + MessageFilter_HandleInComingCall, + MessageFilter_RetryRejectedCall, + MessageFilter_MessagePending +}; + +static IMessageFilter MessageFilter = { &MessageFilter_Vtbl }; + +static void test_CoRegisterMessageFilter(void) +{ + HRESULT hr; + IMessageFilter *prev_filter; + + hr = CoRegisterMessageFilter(&MessageFilter, &prev_filter); + ok(hr == CO_E_NOT_SUPPORTED, + "CoRegisterMessageFilter should have failed with CO_E_NOT_SUPPORTED instead of 0x%08x\n", + hr); + + pCoInitializeEx(NULL, COINIT_MULTITHREADED); + prev_filter = (IMessageFilter *)0xdeadbeef; + hr = CoRegisterMessageFilter(&MessageFilter, &prev_filter); + ok(hr == CO_E_NOT_SUPPORTED, + "CoRegisterMessageFilter should have failed with CO_E_NOT_SUPPORTED instead of 0x%08x\n", + hr); + ok(prev_filter == (IMessageFilter *)0xdeadbeef, + "prev_filter should have been set to %p\n", prev_filter); + CoUninitialize(); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoRegisterMessageFilter(NULL, NULL); + ok_ole_success(hr, "CoRegisterMessageFilter"); + + prev_filter = (IMessageFilter *)0xdeadbeef; + hr = CoRegisterMessageFilter(NULL, &prev_filter); + ok_ole_success(hr, "CoRegisterMessageFilter"); + ok(prev_filter == NULL, "prev_filter should have been set to NULL instead of %p\n", prev_filter); + + hr = CoRegisterMessageFilter(&MessageFilter, &prev_filter); + ok_ole_success(hr, "CoRegisterMessageFilter"); + ok(prev_filter == NULL, "prev_filter should have been set to NULL instead of %p\n", prev_filter); + + hr = CoRegisterMessageFilter(NULL, NULL); + ok_ole_success(hr, "CoRegisterMessageFilter"); + + CoUninitialize(); +} + +static HRESULT WINAPI Test_IUnknown_QueryInterface( + LPUNKNOWN iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IWineTest)) + { + *ppvObj = (LPVOID)iface; + IUnknown_AddRef(iface); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_IUnknown_AddRef(LPUNKNOWN iface) +{ + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_IUnknown_Release(LPUNKNOWN iface) +{ + return 1; /* non-heap-based object */ +} + +static const IUnknownVtbl TestUnknown_Vtbl = +{ + Test_IUnknown_QueryInterface, + Test_IUnknown_AddRef, + Test_IUnknown_Release, +}; + +static IUnknown Test_Unknown = { &TestUnknown_Vtbl }; + +static HRESULT WINAPI PSFactoryBuffer_QueryInterface( + IPSFactoryBuffer * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void **ppvObject) +{ + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IPSFactoryBuffer)) + { + *ppvObject = This; + IPSFactoryBuffer_AddRef(This); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI PSFactoryBuffer_AddRef( + IPSFactoryBuffer * This) +{ + return 2; +} + +static ULONG WINAPI PSFactoryBuffer_Release( + IPSFactoryBuffer * This) +{ + return 1; +} + +static HRESULT WINAPI PSFactoryBuffer_CreateProxy( + IPSFactoryBuffer * This, + /* [in] */ IUnknown *pUnkOuter, + /* [in] */ REFIID riid, + /* [out] */ IRpcProxyBuffer **ppProxy, + /* [out] */ void **ppv) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI PSFactoryBuffer_CreateStub( + IPSFactoryBuffer * This, + /* [in] */ REFIID riid, + /* [unique][in] */ IUnknown *pUnkServer, + /* [out] */ IRpcStubBuffer **ppStub) +{ + return E_NOTIMPL; +} + +static IPSFactoryBufferVtbl PSFactoryBufferVtbl = +{ + PSFactoryBuffer_QueryInterface, + PSFactoryBuffer_AddRef, + PSFactoryBuffer_Release, + PSFactoryBuffer_CreateProxy, + PSFactoryBuffer_CreateStub +}; + +static IPSFactoryBuffer PSFactoryBuffer = { &PSFactoryBufferVtbl }; + +static const CLSID CLSID_WineTestPSFactoryBuffer = +{ + 0x52011640, + 0x8164, + 0x4fd0, + {0xa1, 0xa2, 0x5d, 0x5a, 0x36, 0x54, 0xd3, 0xbd} +}; /* 52011640-8164-4fd0-a1a2-5d5a3654d3bd */ + +static void test_CoRegisterPSClsid(void) +{ + HRESULT hr; + DWORD dwRegistrationKey; + IStream *stream; + CLSID clsid; + + hr = CoRegisterPSClsid(&IID_IWineTest, &CLSID_WineTestPSFactoryBuffer); + ok(hr == CO_E_NOTINITIALIZED, "CoRegisterPSClsid should have returened CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoRegisterClassObject(&CLSID_WineTestPSFactoryBuffer, (IUnknown *)&PSFactoryBuffer, + CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &dwRegistrationKey); + ok_ole_success(hr, "CoRegisterClassObject"); + + hr = CoRegisterPSClsid(&IID_IWineTest, &CLSID_WineTestPSFactoryBuffer); + ok_ole_success(hr, "CoRegisterPSClsid"); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + + hr = CoMarshalInterface(stream, &IID_IWineTest, (IUnknown *)&Test_Unknown, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok(hr == E_NOTIMPL, "CoMarshalInterface should have returned E_NOTIMPL instead of 0x%08x\n", hr); + IStream_Release(stream); + + hr = CoRevokeClassObject(dwRegistrationKey); + ok_ole_success(hr, "CoRevokeClassObject"); + + CoUninitialize(); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoGetPSClsid(&IID_IWineTest, &clsid); + ok(hr == REGDB_E_IIDNOTREG, "CoGetPSClsid should have returned REGDB_E_IIDNOTREG instead of 0x%08x\n", hr); + + CoUninitialize(); +} + +static void test_CoGetPSClsid(void) +{ + HRESULT hr; + CLSID clsid; + + hr = CoGetPSClsid(&IID_IClassFactory, &clsid); + ok(hr == CO_E_NOTINITIALIZED, + "CoGetPSClsid should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", + hr); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoGetPSClsid(&IID_IClassFactory, &clsid); + ok_ole_success(hr, "CoGetPSClsid"); + + hr = CoGetPSClsid(&IID_IWineTest, &clsid); + ok(hr == REGDB_E_IIDNOTREG, + "CoGetPSClsid for random IID returned 0x%08x instead of REGDB_E_IIDNOTREG\n", + hr); + + hr = CoGetPSClsid(&IID_IClassFactory, NULL); + ok(hr == E_INVALIDARG, + "CoGetPSClsid for null clsid returned 0x%08x instead of E_INVALIDARG\n", + hr); + + CoUninitialize(); +} + +/* basic test, mainly for invalid arguments. see marshal.c for more */ +static void test_CoUnmarshalInterface(void) +{ + IUnknown *pProxy; + IStream *pStream; + HRESULT hr; + + hr = CoUnmarshalInterface(NULL, &IID_IUnknown, (void **)&pProxy); + ok(hr == E_INVALIDARG, "CoUnmarshalInterface should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + + hr = CoUnmarshalInterface(pStream, &IID_IUnknown, (void **)&pProxy); + todo_wine + ok(hr == CO_E_NOTINITIALIZED, "CoUnmarshalInterface should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoUnmarshalInterface(pStream, &IID_IUnknown, (void **)&pProxy); + ok(hr == STG_E_READFAULT, "CoUnmarshalInterface should have returned STG_E_READFAULT instead of 0x%08x\n", hr); + + CoUninitialize(); + + hr = CoUnmarshalInterface(pStream, &IID_IUnknown, NULL); + ok(hr == E_INVALIDARG, "CoUnmarshalInterface should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + IStream_Release(pStream); +} + +static void test_CoGetInterfaceAndReleaseStream(void) +{ + HRESULT hr; + IUnknown *pUnk; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoGetInterfaceAndReleaseStream(NULL, &IID_IUnknown, (void**)&pUnk); + ok(hr == E_INVALIDARG, "hr %08x\n", hr); + + CoUninitialize(); +} + +/* basic test, mainly for invalid arguments. see marshal.c for more */ +static void test_CoMarshalInterface(void) +{ + IStream *pStream; + HRESULT hr; + static const LARGE_INTEGER llZero; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + + hr = CoMarshalInterface(pStream, &IID_IUnknown, NULL, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok(hr == E_INVALIDARG, "CoMarshalInterface should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CoMarshalInterface(NULL, &IID_IUnknown, (IUnknown *)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok(hr == E_INVALIDARG, "CoMarshalInterface should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CoMarshalInterface(pStream, &IID_IUnknown, (IUnknown *)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, "CoMarshalInterface"); + + /* stream not rewound */ + hr = CoReleaseMarshalData(pStream); + ok(hr == STG_E_READFAULT, "CoReleaseMarshalData should have returned STG_E_READFAULT instead of 0x%08x\n", hr); + + hr = IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + + hr = CoReleaseMarshalData(pStream); + ok_ole_success(hr, "CoReleaseMarshalData"); + + IStream_Release(pStream); + + CoUninitialize(); +} + +static void test_CoMarshalInterThreadInterfaceInStream(void) +{ + IStream *pStream; + HRESULT hr; + IClassFactory *pProxy; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + cLocks = 0; + + hr = CoMarshalInterThreadInterfaceInStream(&IID_IUnknown, (IUnknown *)&Test_ClassFactory, NULL); + ok(hr == E_INVALIDARG, "CoMarshalInterThreadInterfaceInStream should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CoMarshalInterThreadInterfaceInStream(&IID_IUnknown, NULL, &pStream); + ok(hr == E_INVALIDARG, "CoMarshalInterThreadInterfaceInStream should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + ok_no_locks(); + + hr = CoMarshalInterThreadInterfaceInStream(&IID_IUnknown, (IUnknown *)&Test_ClassFactory, &pStream); + ok_ole_success(hr, "CoMarshalInterThreadInterfaceInStream"); + + ok_more_than_one_lock(); + + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, "CoUnmarshalInterface"); + + IClassFactory_Release(pProxy); + IStream_Release(pStream); + + ok_no_locks(); + + CoUninitialize(); +} + +static void test_CoRegisterClassObject(void) +{ + DWORD cookie; + HRESULT hr; + IClassFactory *pcf; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + /* CLSCTX_INPROC_SERVER */ + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER, REGCLS_MULTI_SEPARATE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + /* CLSCTX_LOCAL_SERVER */ + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_LOCAL_SERVER, REGCLS_MULTI_SEPARATE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + /* CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER */ + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + /* test whether registered class becomes invalid when apartment is destroyed */ + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + + CoUninitialize(); + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoGetClassObject(&CLSID_WineOOPTest, CLSCTX_INPROC_SERVER, NULL, + &IID_IClassFactory, (void **)&pcf); + ok(hr == REGDB_E_CLASSNOTREG, "object registered in an apartment shouldn't accessible after it is destroyed\n"); + + /* crashes with at least win9x DCOM! */ + if (0) + hr = CoRevokeClassObject(cookie); + + CoUninitialize(); +} + +static HRESULT get_class_object(CLSCTX clsctx) +{ + HRESULT hr; + IClassFactory *pcf; + + hr = CoGetClassObject(&CLSID_WineOOPTest, clsctx, NULL, &IID_IClassFactory, + (void **)&pcf); + + if (SUCCEEDED(hr)) + IClassFactory_Release(pcf); + + return hr; +} + +static DWORD CALLBACK get_class_object_thread(LPVOID pv) +{ + CLSCTX clsctx = (CLSCTX)(DWORD_PTR)pv; + HRESULT hr; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = get_class_object(clsctx); + + CoUninitialize(); + + return hr; +} + +static DWORD CALLBACK get_class_object_proxy_thread(LPVOID pv) +{ + CLSCTX clsctx = (CLSCTX)(DWORD_PTR)pv; + HRESULT hr; + IClassFactory *pcf; + IMultiQI *pMQI; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoGetClassObject(&CLSID_WineOOPTest, clsctx, NULL, &IID_IClassFactory, + (void **)&pcf); + + if (SUCCEEDED(hr)) + { + hr = IClassFactory_QueryInterface(pcf, &IID_IMultiQI, (void **)&pMQI); + if (SUCCEEDED(hr)) + IMultiQI_Release(pMQI); + IClassFactory_Release(pcf); + } + + CoUninitialize(); + + return hr; +} + +static DWORD CALLBACK register_class_object_thread(LPVOID pv) +{ + HRESULT hr; + DWORD cookie; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie); + + CoUninitialize(); + + return hr; +} + +static DWORD CALLBACK revoke_class_object_thread(LPVOID pv) +{ + DWORD cookie = (DWORD_PTR)pv; + HRESULT hr; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoRevokeClassObject(cookie); + + CoUninitialize(); + + return hr; +} + +static void test_registered_object_thread_affinity(void) +{ + HRESULT hr; + DWORD cookie; + HANDLE thread; + DWORD tid; + DWORD exitcode; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + /* CLSCTX_INPROC_SERVER */ + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_INPROC_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + + thread = CreateThread(NULL, 0, get_class_object_thread, (LPVOID)CLSCTX_INPROC_SERVER, 0, &tid); + ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, &exitcode); + hr = exitcode; + ok(hr == REGDB_E_CLASSNOTREG, "CoGetClassObject on inproc object " + "registered in different thread should return REGDB_E_CLASSNOTREG " + "instead of 0x%08x\n", hr); + + hr = get_class_object(CLSCTX_INPROC_SERVER); + ok(hr == S_OK, "CoGetClassObject on inproc object registered in same " + "thread should return S_OK instead of 0x%08x\n", hr); + + thread = CreateThread(NULL, 0, register_class_object_thread, NULL, 0, &tid); + ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, &exitcode); + hr = exitcode; + ok(hr == S_OK, "CoRegisterClassObject with same CLSID but in different thread should return S_OK instead of 0x%08x\n", hr); + + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + /* CLSCTX_LOCAL_SERVER */ + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&Test_ClassFactory, + CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &cookie); + ok_ole_success(hr, "CoRegisterClassObject"); + + thread = CreateThread(NULL, 0, get_class_object_proxy_thread, (LPVOID)CLSCTX_LOCAL_SERVER, 0, &tid); + ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); + while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0 + 1) + { + MSG msg; + while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + } + GetExitCodeThread(thread, &exitcode); + hr = exitcode; + ok(hr == S_OK, "CoGetClassObject on local server object " + "registered in different thread should return S_OK " + "instead of 0x%08x\n", hr); + + hr = get_class_object(CLSCTX_LOCAL_SERVER); + ok(hr == S_OK, "CoGetClassObject on local server object registered in same " + "thread should return S_OK instead of 0x%08x\n", hr); + + thread = CreateThread(NULL, 0, revoke_class_object_thread, (LPVOID)cookie, 0, &tid); + ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, &exitcode); + hr = exitcode; + ok(hr == RPC_E_WRONG_THREAD, "CoRevokeClassObject called from different " + "thread to where registered should return RPC_E_WRONG_THREAD instead of 0x%08x\n", hr); + + thread = CreateThread(NULL, 0, register_class_object_thread, NULL, 0, &tid); + ok(thread != NULL, "CreateThread failed with error %d\n", GetLastError()); + WaitForSingleObject(thread, INFINITE); + GetExitCodeThread(thread, &exitcode); + hr = exitcode; + ok(hr == S_OK, "CoRegisterClassObject with same CLSID but in different " + "thread should return S_OK instead of 0x%08x\n", hr); + + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, "CoRevokeClassObject"); + + CoUninitialize(); +} + +static DWORD CALLBACK free_libraries_thread(LPVOID p) +{ + CoFreeUnusedLibraries(); + return 0; +} + +static inline BOOL is_module_loaded(const char *module) +{ + return GetModuleHandle(module) ? TRUE : FALSE; +} + +static void test_CoFreeUnusedLibraries(void) +{ + HRESULT hr; + IUnknown *pUnk; + DWORD tid; + HANDLE thread; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + ok(!is_module_loaded("urlmon.dll"), "urlmon.dll shouldn't be loaded\n"); + + hr = CoCreateInstance(&CLSID_FileProtocol, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&pUnk); + if (hr == REGDB_E_CLASSNOTREG) + { + trace("IE not installed so can't run CoFreeUnusedLibraries test\n"); + return; + } + ok_ole_success(hr, "CoCreateInstance"); + + ok(is_module_loaded("urlmon.dll"), "urlmon.dll should be loaded\n"); + + IUnknown_Release(pUnk); + + ok(is_module_loaded("urlmon.dll"), "urlmon.dll should be loaded\n"); + + thread = CreateThread(NULL, 0, free_libraries_thread, NULL, 0, &tid); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + ok(is_module_loaded("urlmon.dll"), "urlmon.dll should be loaded\n"); + + CoFreeUnusedLibraries(); + + ok(!is_module_loaded("urlmon.dll"), "urlmon.dll shouldn't be loaded\n"); + + CoUninitialize(); +} + +static void test_CoGetObjectContext(void) +{ + HRESULT hr; + ULONG refs; + IComThreadingInfo *pComThreadingInfo; + APTTYPE apttype; + THDTYPE thdtype; + + if (!pCoGetObjectContext) + { + skip("CoGetObjectContext not present\n"); + return; + } + + hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo); + ok(hr == CO_E_NOTINITIALIZED, "CoGetObjectContext should have returned CO_E_NOTINITIALIZED instead of 0x%08x\n", hr); + ok(pComThreadingInfo == NULL, "pComThreadingInfo should have been set to NULL\n"); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo); + ok_ole_success(hr, "CoGetObjectContext"); + + hr = IComThreadingInfo_GetCurrentApartmentType(pComThreadingInfo, &apttype); + ok_ole_success(hr, "IComThreadingInfo_GetCurrentApartmentType"); + ok(apttype == APTTYPE_MAINSTA, "apartment type should be APTTYPE_MAINSTA instead of %d\n", apttype); + + hr = IComThreadingInfo_GetCurrentThreadType(pComThreadingInfo, &thdtype); + ok_ole_success(hr, "IComThreadingInfo_GetCurrentThreadType"); + ok(thdtype == THDTYPE_PROCESSMESSAGES, "thread type should be THDTYPE_PROCESSMESSAGES instead of %d\n", thdtype); + + refs = IComThreadingInfo_Release(pComThreadingInfo); + ok(refs == 0, "pComThreadingInfo should have 0 refs instead of %d refs\n", refs); + + CoUninitialize(); + + pCoInitializeEx(NULL, COINIT_MULTITHREADED); + + hr = pCoGetObjectContext(&IID_IComThreadingInfo, (void **)&pComThreadingInfo); + ok_ole_success(hr, "CoGetObjectContext"); + + hr = IComThreadingInfo_GetCurrentApartmentType(pComThreadingInfo, &apttype); + ok_ole_success(hr, "IComThreadingInfo_GetCurrentApartmentType"); + ok(apttype == APTTYPE_MTA, "apartment type should be APTTYPE_MTA instead of %d\n", apttype); + + hr = IComThreadingInfo_GetCurrentThreadType(pComThreadingInfo, &thdtype); + ok_ole_success(hr, "IComThreadingInfo_GetCurrentThreadType"); + ok(thdtype == THDTYPE_BLOCKMESSAGES, "thread type should be THDTYPE_BLOCKMESSAGES instead of %d\n", thdtype); + + refs = IComThreadingInfo_Release(pComThreadingInfo); + ok(refs == 0, "pComThreadingInfo should have 0 refs instead of %d refs\n", refs); + + CoUninitialize(); +} + +START_TEST(compobj) +{ + HMODULE hOle32 = GetModuleHandle("ole32"); + pCoGetObjectContext = (void*)GetProcAddress(hOle32, "CoGetObjectContext"); + if (!(pCoInitializeEx = (void*)GetProcAddress(hOle32, "CoInitializeEx"))) + { + trace("You need DCOM95 installed to run this test\n"); + return; + } + + test_ProgIDFromCLSID(); + test_CLSIDFromProgID(); + test_CLSIDFromString(); + test_CoCreateInstance(); + test_ole_menu(); + test_CoGetClassObject(); + test_CoRegisterMessageFilter(); + test_CoRegisterPSClsid(); + test_CoGetPSClsid(); + test_CoUnmarshalInterface(); + test_CoGetInterfaceAndReleaseStream(); + test_CoMarshalInterface(); + test_CoMarshalInterThreadInterfaceInStream(); + test_CoRegisterClassObject(); + test_registered_object_thread_affinity(); + test_CoFreeUnusedLibraries(); + test_CoGetObjectContext(); +} diff --git a/rostests/winetests/ole32/dragdrop.c b/rostests/winetests/ole32/dragdrop.c new file mode 100644 index 00000000000..15610661c29 --- /dev/null +++ b/rostests/winetests/ole32/dragdrop.c @@ -0,0 +1,147 @@ +/* + * Drag and Drop Tests + * + * Copyright 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define _WIN32_DCOM +#define COBJMACROS +#define CONST_VTABLE + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "wine/test.h" + +/* functions that are not present on all versions of Windows */ +HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit); + +static int droptarget_addref_called; +static int droptarget_release_called; + +/* helper macros to make tests a bit leaner */ +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) + +static HRESULT WINAPI DropTarget_QueryInterface(IDropTarget* iface, REFIID riid, + void** ppvObject) +{ + trace("DropTarget_QueryInterface\n"); + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IDropTarget)) + { + IUnknown_AddRef(iface); + *ppvObject = iface; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI DropTarget_AddRef(IDropTarget* iface) +{ + droptarget_addref_called++; + return 2; +} + +static ULONG WINAPI DropTarget_Release(IDropTarget* iface) +{ + droptarget_release_called++; + return 1; +} + +static HRESULT WINAPI DropTarget_DragEnter(IDropTarget* iface, + IDataObject* pDataObj, + DWORD grfKeyState, POINTL pt, + DWORD* pdwEffect) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI DropTarget_DragOver(IDropTarget* iface, + DWORD grfKeyState, + POINTL pt, + DWORD* pdwEffect) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI DropTarget_DragLeave(IDropTarget* iface) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI DropTarget_Drop(IDropTarget* iface, + IDataObject* pDataObj, DWORD grfKeyState, + POINTL pt, DWORD* pdwEffect) +{ + return E_NOTIMPL; +} + +static const IDropTargetVtbl DropTarget_VTbl = +{ + DropTarget_QueryInterface, + DropTarget_AddRef, + DropTarget_Release, + DropTarget_DragEnter, + DropTarget_DragOver, + DropTarget_DragLeave, + DropTarget_Drop +}; + +static IDropTarget DropTarget = { &DropTarget_VTbl }; + +START_TEST(dragdrop) +{ + HRESULT hr; + + hr = RegisterDragDrop(GetDesktopWindow(), &DropTarget); + ok(hr == E_OUTOFMEMORY, "RegisterDragDrop without OLE initialized should have returned E_OUTOFMEMORY instead of 0x%08x\n", hr); + + OleInitialize(NULL); + + hr = RegisterDragDrop(GetDesktopWindow(), NULL); + ok(hr == E_INVALIDARG, "RegisterDragDrop with NULL IDropTarget * should return E_INVALIDARG instead of 0x%08x\n", hr); + + hr = RegisterDragDrop(NULL, &DropTarget); + ok(hr == DRAGDROP_E_INVALIDHWND, "RegisterDragDrop with NULL hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr); + + hr = RegisterDragDrop((HWND)0xdeadbeef, &DropTarget); + ok(hr == DRAGDROP_E_INVALIDHWND, "RegisterDragDrop with garbage hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr); + + ok(droptarget_addref_called == 0, "DropTarget_AddRef shouldn't have been called\n"); + hr = RegisterDragDrop(GetDesktopWindow(), &DropTarget); + ok_ole_success(hr, "RegisterDragDrop"); + ok(droptarget_addref_called == 1, "DropTarget_AddRef should have been called once, not %d times\n", droptarget_addref_called); + + hr = RegisterDragDrop(GetDesktopWindow(), &DropTarget); + ok(hr == DRAGDROP_E_ALREADYREGISTERED, "RegisterDragDrop with already registered hwnd should return DRAGDROP_E_ALREADYREGISTERED instead of 0x%08x\n", hr); + + ok(droptarget_release_called == 0, "DropTarget_Release shouldn't have been called\n"); + OleUninitialize(); + ok(droptarget_release_called == 0, "DropTarget_Release shouldn't have been called\n"); + + hr = RevokeDragDrop(GetDesktopWindow()); + ok_ole_success(hr, "RevokeDragDrop"); + ok(droptarget_release_called == 1, "DropTarget_Release should have been called once, not %d times\n", droptarget_release_called); + + hr = RevokeDragDrop(NULL); + ok(hr == DRAGDROP_E_INVALIDHWND, "RevokeDragDrop with NULL hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr); +} diff --git a/rostests/winetests/ole32/errorinfo.c b/rostests/winetests/ole32/errorinfo.c new file mode 100644 index 00000000000..f55d480ef20 --- /dev/null +++ b/rostests/winetests/ole32/errorinfo.c @@ -0,0 +1,111 @@ +/* + * Error Info Tests + * + * Copyright 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#define CONST_VTABLE + +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "wine/test.h" + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) + +static const CLSID CLSID_WineTest = +{ /* 9474ba1a-258b-490b-bc13-516e9239ace0 */ + 0x9474ba1a, + 0x258b, + 0x490b, + {0xbc, 0x13, 0x51, 0x6e, 0x92, 0x39, 0xac, 0xe0} +}; + +static void test_error_info(void) +{ + HRESULT hr; + ICreateErrorInfo *pCreateErrorInfo; + IErrorInfo *pErrorInfo; + static WCHAR wszDescription[] = {'F','a','i','l','e','d',' ','S','p','r','o','c','k','e','t',0}; + static WCHAR wszHelpFile[] = {'s','p','r','o','c','k','e','t','.','h','l','p',0}; + static WCHAR wszSource[] = {'s','p','r','o','c','k','e','t',0}; + + hr = CreateErrorInfo(&pCreateErrorInfo); + ok_ole_success(hr, "CreateErrorInfo"); + + hr = ICreateErrorInfo_SetDescription(pCreateErrorInfo, NULL); + ok_ole_success(hr, "ICreateErrorInfo_SetDescription"); + + hr = ICreateErrorInfo_SetDescription(pCreateErrorInfo, wszDescription); + ok_ole_success(hr, "ICreateErrorInfo_SetDescription"); + + hr = ICreateErrorInfo_SetGUID(pCreateErrorInfo, &CLSID_WineTest); + ok_ole_success(hr, "ICreateErrorInfo_SetGUID"); + + hr = ICreateErrorInfo_SetHelpContext(pCreateErrorInfo, 0xdeadbeef); + ok_ole_success(hr, "ICreateErrorInfo_SetHelpContext"); + + hr = ICreateErrorInfo_SetHelpFile(pCreateErrorInfo, NULL); + ok_ole_success(hr, "ICreateErrorInfo_SetHelpFile"); + + hr = ICreateErrorInfo_SetHelpFile(pCreateErrorInfo, wszHelpFile); + ok_ole_success(hr, "ICreateErrorInfo_SetHelpFile"); + + hr = ICreateErrorInfo_SetSource(pCreateErrorInfo, NULL); + ok_ole_success(hr, "ICreateErrorInfo_SetSource"); + + hr = ICreateErrorInfo_SetSource(pCreateErrorInfo, wszSource); + ok_ole_success(hr, "ICreateErrorInfo_SetSource"); + + hr = ICreateErrorInfo_QueryInterface(pCreateErrorInfo, &IID_IErrorInfo, (void **)&pErrorInfo); + ok_ole_success(hr, "ICreateErrorInfo_QueryInterface"); + + ICreateErrorInfo_Release(pCreateErrorInfo); + + hr = SetErrorInfo(0, pErrorInfo); + ok_ole_success(hr, "SetErrorInfo"); + + IErrorInfo_Release(pErrorInfo); + pErrorInfo = NULL; + + hr = GetErrorInfo(0, &pErrorInfo); + ok_ole_success(hr, "GetErrorInfo"); + + IErrorInfo_Release(pErrorInfo); + + hr = GetErrorInfo(0, &pErrorInfo); + ok(hr == S_FALSE, "GetErrorInfo should have returned S_FALSE instead of 0x%08x\n", hr); + ok(!pErrorInfo, "pErrorInfo should be set to NULL\n"); + + hr = SetErrorInfo(0, NULL); + ok_ole_success(hr, "SetErrorInfo"); + + hr = GetErrorInfo(0xdeadbeef, &pErrorInfo); + ok(hr == E_INVALIDARG, "GetErrorInfo should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = SetErrorInfo(0xdeadbeef, NULL); + ok(hr == E_INVALIDARG, "SetErrorInfo should have returned E_INVALIDARG instead of 0x%08x\n", hr); +} + +START_TEST(errorinfo) +{ + test_error_info(); +} diff --git a/rostests/winetests/ole32/hglobalstream.c b/rostests/winetests/ole32/hglobalstream.c new file mode 100644 index 00000000000..eb98a18c44a --- /dev/null +++ b/rostests/winetests/ole32/hglobalstream.c @@ -0,0 +1,318 @@ +/* + * Stream on HGLOBAL Tests + * + * Copyright 2006 Robert Shearman (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 + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" + +#include "wine/test.h" + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) + +static char const * const *expected_method_list; + +#define CHECK_EXPECTED_METHOD(method_name) \ +do { \ + ok(*expected_method_list != NULL, "Extra method %s called\n", method_name); \ + if (*expected_method_list) \ + { \ + ok(!strcmp(*expected_method_list, method_name), "Expected %s to be called instead of %s\n", \ + *expected_method_list, method_name); \ + expected_method_list++; \ + } \ +} while(0) + +static void test_streamonhglobal(IStream *pStream) +{ + const char data[] = "Test String"; + ULARGE_INTEGER ull; + LARGE_INTEGER ll; + char buffer[128]; + ULONG read; + STATSTG statstg; + HRESULT hr; + + ull.QuadPart = sizeof(data); + hr = IStream_SetSize(pStream, ull); + ok_ole_success(hr, "IStream_SetSize"); + + hr = IStream_Write(pStream, data, sizeof(data), NULL); + ok_ole_success(hr, "IStream_Write"); + + ll.QuadPart = 0; + hr = IStream_Seek(pStream, ll, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + + /* should return S_OK, not S_FALSE */ + hr = IStream_Read(pStream, buffer, sizeof(buffer), &read); + ok_ole_success(hr, "IStream_Read"); + ok(read == sizeof(data), "IStream_Read returned read %d\n", read); + + /* ignores HighPart */ + ull.u.HighPart = -1; + ull.u.LowPart = 0; + hr = IStream_SetSize(pStream, ull); + ok_ole_success(hr, "IStream_SetSize"); + + hr = IStream_Commit(pStream, STGC_DEFAULT); + ok_ole_success(hr, "IStream_Commit"); + + hr = IStream_Revert(pStream); + ok_ole_success(hr, "IStream_Revert"); + + hr = IStream_LockRegion(pStream, ull, ull, LOCK_WRITE); + ok(hr == STG_E_INVALIDFUNCTION, "IStream_LockRegion should have returned STG_E_INVALIDFUNCTION instead of 0x%08x\n", hr); + + hr = IStream_Stat(pStream, &statstg, STATFLAG_DEFAULT); + ok_ole_success(hr, "IStream_Stat"); + ok(statstg.type == STGTY_STREAM, "statstg.type should have been STGTY_STREAM instead of %d\n", statstg.type); + + /* test OOM condition */ + ull.u.HighPart = -1; + ull.u.LowPart = -1; + hr = IStream_SetSize(pStream, ull); + ok(hr == E_OUTOFMEMORY, "IStream_SetSize with large size should have returned E_OUTOFMEMORY instead of 0x%08x\n", hr); +} + +static HRESULT WINAPI TestStream_QueryInterface(IStream *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_ISequentialStream) || + IsEqualIID(riid, &IID_IStream)) + { + *ppv = iface; + IUnknown_AddRef(iface); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI TestStream_AddRef(IStream *iface) +{ + return 2; +} + +static ULONG WINAPI TestStream_Release(IStream *iface) +{ + return 1; +} + +static HRESULT WINAPI TestStream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) +{ + CHECK_EXPECTED_METHOD("TestStream_Read"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten) +{ + CHECK_EXPECTED_METHOD("TestStream_Write"); + *pcbWritten = 5; + return S_OK; +} + +static HRESULT WINAPI TestStream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +{ + CHECK_EXPECTED_METHOD("TestStream_Seek"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) +{ + CHECK_EXPECTED_METHOD("TestStream_SetSize"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_CopyTo(IStream *iface, IStream *pStream, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten) +{ + CHECK_EXPECTED_METHOD("TestStream_CopyTo"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_Commit(IStream *iface, DWORD grfCommitFlags) +{ + CHECK_EXPECTED_METHOD("TestStream_Commit"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_Revert(IStream *iface) +{ + CHECK_EXPECTED_METHOD("TestStream_Revert"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + CHECK_EXPECTED_METHOD("TestStream_LockRegion"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) +{ + CHECK_EXPECTED_METHOD("TestStream_UnlockRegion"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) +{ + CHECK_EXPECTED_METHOD("TestStream_Stat"); + return E_NOTIMPL; +} + +static HRESULT WINAPI TestStream_Clone(IStream *iface, IStream **pStream) +{ + CHECK_EXPECTED_METHOD("TestStream_Clone"); + return E_NOTIMPL; +} + +static /*const*/ IStreamVtbl StreamVtbl = +{ + TestStream_QueryInterface, + TestStream_AddRef, + TestStream_Release, + TestStream_Read, + TestStream_Write, + TestStream_Seek, + TestStream_SetSize, + TestStream_CopyTo, + TestStream_Commit, + TestStream_Revert, + TestStream_LockRegion, + TestStream_UnlockRegion, + TestStream_Stat, + TestStream_Clone +}; + +static IStream Test_Stream = { &StreamVtbl }; + +static void test_copyto(void) +{ + IStream *pStream, *pStream2; + HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + static const char szHello[] = "Hello"; + ULARGE_INTEGER cb; + static const char *methods_copyto[] = + { + "TestStream_Write", + NULL + }; + ULONG written; + ULARGE_INTEGER ullRead; + ULARGE_INTEGER ullWritten; + ULARGE_INTEGER libNewPosition; + static const LARGE_INTEGER llZero; + char buffer[15]; + + expected_method_list = methods_copyto; + + hr = IStream_Write(pStream, szHello, sizeof(szHello), &written); + ok_ole_success(hr, "IStream_Write"); + ok(written == sizeof(szHello), "only %d bytes written\n", written); + + hr = IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + + cb.QuadPart = sizeof(szHello); + hr = IStream_CopyTo(pStream, &Test_Stream, cb, &ullRead, &ullWritten); + ok(ullWritten.QuadPart == 5, "ullWritten was %d instead\n", (ULONG)ullWritten.QuadPart); + ok(ullRead.QuadPart == sizeof(szHello), "only %d bytes read\n", (ULONG)ullRead.QuadPart); + ok_ole_success(hr, "IStream_CopyTo"); + + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + hr = IStream_Clone(pStream, &pStream2); + ok_ole_success(hr, "IStream_Clone"); + + hr = IStream_Seek(pStream2, llZero, STREAM_SEEK_CUR, &libNewPosition); + ok_ole_success(hr, "IStream_Seek"); + ok(libNewPosition.QuadPart == sizeof(szHello), "libNewPosition wasn't set correctly for the cloned stream\n"); + + hr = IStream_Seek(pStream2, llZero, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + + hr = IStream_Read(pStream2, buffer, sizeof(buffer), NULL); + ok_ole_success(hr, "IStream_Read"); + ok(!strcmp(buffer, szHello), "read data \"%s\" didn't match originally written data\n", buffer); + + IStream_Release(pStream2); + IStream_Release(pStream); +} + +static void test_freed_hglobal(void) +{ + HRESULT hr; + IStream *pStream; + HGLOBAL hglobal; + char *p; + char buffer[10]; + ULARGE_INTEGER ull; + ULONG read, written; + + hglobal = GlobalAlloc(GMEM_DDESHARE|GMEM_NODISCARD|GMEM_MOVEABLE, strlen("Rob") + 1); + ok(hglobal != NULL, "GlobalAlloc failed with error %d\n", GetLastError()); + p = GlobalLock(hglobal); + strcpy(p, "Rob"); + GlobalUnlock(hglobal); + + hr = CreateStreamOnHGlobal(hglobal, FALSE, &pStream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + + hr = IStream_Read(pStream, buffer, sizeof(buffer), &read); + ok_ole_success(hr, "IStream_Read"); + ok(!strcmp(buffer, "Rob"), "buffer data %s differs\n", buffer); + ok(read == strlen("Rob") + 1, "read should be 4 instead of %d\n", read); + + GlobalFree(hglobal); + + memset(buffer, 0, sizeof(buffer)); + read = -1; + hr = IStream_Read(pStream, buffer, sizeof(buffer), &read); + ok_ole_success(hr, "IStream_Read"); + ok(buffer[0] == 0, "buffer data should be untouched\n"); + ok(read == 0, "read should be 0 instead of %d\n", read); + + ull.QuadPart = sizeof(buffer); + hr = IStream_SetSize(pStream, ull); + ok(hr == E_OUTOFMEMORY, "IStream_SetSize with invalid HGLOBAL should return E_OUTOFMEMORY instead of 0x%08x\n", hr); + + hr = IStream_Write(pStream, buffer, sizeof(buffer), &written); + ok(hr == E_OUTOFMEMORY, "IStream_Write with invalid HGLOBAL should return E_OUTOFMEMORY instead of 0x%08x\n", hr); + ok(written == 0, "written should be 0 instead of %d\n", written); + + IStream_Release(pStream); +} + +START_TEST(hglobalstream) +{ + HRESULT hr; + IStream *pStream; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + + test_streamonhglobal(pStream); + IStream_Release(pStream); + test_copyto(); + test_freed_hglobal(); +} diff --git a/rostests/winetests/ole32/marshal.c b/rostests/winetests/ole32/marshal.c new file mode 100644 index 00000000000..ae82e378723 --- /dev/null +++ b/rostests/winetests/ole32/marshal.c @@ -0,0 +1,2956 @@ +/* + * Marshaling Tests + * + * Copyright 2004 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define _WIN32_DCOM +#define COBJMACROS +#define CONST_VTABLE + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "shlguid.h" + +#include "wine/test.h" + +/* functions that are not present on all versions of Windows */ +HRESULT (WINAPI * pCoInitializeEx)(LPVOID lpReserved, DWORD dwCoInit); + +/* helper macros to make tests a bit leaner */ +#define ok_more_than_one_lock() ok(cLocks > 0, "Number of locks should be > 0, but actually is %d\n", cLocks) +#define ok_no_locks() ok(cLocks == 0, "Number of locks should be 0, but actually is %d\n", cLocks) +#define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08x\n", hr) + +static const IID IID_IWineTest = +{ + 0x5201163f, + 0x8164, + 0x4fd0, + {0xa1, 0xa2, 0x5d, 0x5a, 0x36, 0x54, 0xd3, 0xbd} +}; /* 5201163f-8164-4fd0-a1a2-5d5a3654d3bd */ + +static const IID IID_IRemUnknown = +{ + 0x00000131, + 0x0000, + 0x0000, + {0xc0,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} +}; + +#define EXTENTID_WineTest IID_IWineTest +#define CLSID_WineTest IID_IWineTest + +static const CLSID CLSID_WineOOPTest = +{ + 0x5201163f, + 0x8164, + 0x4fd0, + {0xa1, 0xa2, 0x5d, 0x5a, 0x36, 0x54, 0xd3, 0xbd} +}; /* 5201163f-8164-4fd0-a1a2-5d5a3654d3bd */ + +static void test_cocreateinstance_proxy(void) +{ + IUnknown *pProxy; + IMultiQI *pMQI; + HRESULT hr; + + pCoInitializeEx(NULL, COINIT_MULTITHREADED); + + hr = CoCreateInstance(&CLSID_ShellDesktop, NULL, CLSCTX_INPROC, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, CoCreateInstance); + hr = IUnknown_QueryInterface(pProxy, &IID_IMultiQI, (void **)&pMQI); + ok(hr == S_OK, "created object is not a proxy, so was created in the wrong apartment\n"); + if (hr == S_OK) + IMultiQI_Release(pMQI); + IUnknown_Release(pProxy); + + CoUninitialize(); +} + +static const LARGE_INTEGER ullZero; +static LONG cLocks; + +static void LockModule(void) +{ + InterlockedIncrement(&cLocks); +} + +static void UnlockModule(void) +{ + InterlockedDecrement(&cLocks); +} + + +static HRESULT WINAPI Test_IUnknown_QueryInterface( + LPUNKNOWN iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown)) + { + *ppvObj = (LPVOID)iface; + IUnknown_AddRef(iface); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_IUnknown_AddRef(LPUNKNOWN iface) +{ + LockModule(); + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_IUnknown_Release(LPUNKNOWN iface) +{ + UnlockModule(); + return 1; /* non-heap-based object */ +} + +static const IUnknownVtbl TestUnknown_Vtbl = +{ + Test_IUnknown_QueryInterface, + Test_IUnknown_AddRef, + Test_IUnknown_Release, +}; + +static IUnknown Test_Unknown = { &TestUnknown_Vtbl }; + + +static HRESULT WINAPI Test_IClassFactory_QueryInterface( + LPCLASSFACTORY iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IClassFactory) || + /* the only other interface Wine is currently able to marshal (for testing two proxies) */ + IsEqualGUID(riid, &IID_IRemUnknown)) + { + *ppvObj = (LPVOID)iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_IClassFactory_AddRef(LPCLASSFACTORY iface) +{ + LockModule(); + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_IClassFactory_Release(LPCLASSFACTORY iface) +{ + UnlockModule(); + return 1; /* non-heap-based object */ +} + +static HRESULT WINAPI Test_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + if (pUnkOuter) return CLASS_E_NOAGGREGATION; + return IUnknown_QueryInterface((IUnknown*)&Test_Unknown, riid, ppvObj); +} + +static HRESULT WINAPI Test_IClassFactory_LockServer( + LPCLASSFACTORY iface, + BOOL fLock) +{ + return S_OK; +} + +static const IClassFactoryVtbl TestClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + Test_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +static IClassFactory Test_ClassFactory = { &TestClassFactory_Vtbl }; + +#define RELEASEMARSHALDATA WM_USER + +struct host_object_data +{ + IStream *stream; + IID iid; + IUnknown *object; + MSHLFLAGS marshal_flags; + HANDLE marshal_event; + IMessageFilter *filter; +}; + +static DWORD CALLBACK host_object_proc(LPVOID p) +{ + struct host_object_data *data = (struct host_object_data *)p; + HRESULT hr; + MSG msg; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + if (data->filter) + { + IMessageFilter * prev_filter = NULL; + hr = CoRegisterMessageFilter(data->filter, &prev_filter); + if (prev_filter) IMessageFilter_Release(prev_filter); + ok_ole_success(hr, CoRegisterMessageFilter); + } + + hr = CoMarshalInterface(data->stream, &data->iid, data->object, MSHCTX_INPROC, NULL, data->marshal_flags); + ok_ole_success(hr, CoMarshalInterface); + + /* force the message queue to be created before signaling parent thread */ + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + SetEvent(data->marshal_event); + + while (GetMessage(&msg, NULL, 0, 0)) + { + if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA) + { + CoReleaseMarshalData(data->stream); + SetEvent((HANDLE)msg.lParam); + } + else + DispatchMessage(&msg); + } + + HeapFree(GetProcessHeap(), 0, data); + + CoUninitialize(); + + return hr; +} + +static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, IMessageFilter *filter, HANDLE *thread) +{ + DWORD tid = 0; + HANDLE marshal_event = CreateEvent(NULL, FALSE, FALSE, NULL); + struct host_object_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data)); + + data->stream = stream; + data->iid = *riid; + data->object = object; + data->marshal_flags = marshal_flags; + data->marshal_event = marshal_event; + data->filter = filter; + + *thread = CreateThread(NULL, 0, host_object_proc, data, 0, &tid); + + /* wait for marshaling to complete before returning */ + WaitForSingleObject(marshal_event, INFINITE); + CloseHandle(marshal_event); + + return tid; +} + +static DWORD start_host_object(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, HANDLE *thread) +{ + return start_host_object2(stream, riid, object, marshal_flags, NULL, thread); +} + +/* asks thread to release the marshal data because it has to be done by the + * same thread that marshaled the interface in the first place. */ +static void release_host_object(DWORD tid) +{ + HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + PostThreadMessage(tid, RELEASEMARSHALDATA, 0, (LPARAM)event); + WaitForSingleObject(event, INFINITE); + CloseHandle(event); +} + +static void end_host_object(DWORD tid, HANDLE thread) +{ + BOOL ret = PostThreadMessage(tid, WM_QUIT, 0, 0); + ok(ret, "PostThreadMessage failed with error %d\n", GetLastError()); + /* be careful of races - don't return until hosting thread has terminated */ + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); +} + +/* tests failure case of interface not having a marshaler specified in the + * registry */ +static void test_no_marshaler(void) +{ + IStream *pStream; + HRESULT hr; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + hr = CoMarshalInterface(pStream, &IID_IWineTest, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok(hr == E_NOINTERFACE, "CoMarshalInterface should have returned E_NOINTERFACE instead of 0x%08x\n", hr); + + IStream_Release(pStream); +} + +/* tests normal marshal and then release without unmarshaling */ +static void test_normal_marshal_and_release(void) +{ + HRESULT hr; + IStream *pStream = NULL; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoReleaseMarshalData(pStream); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_no_locks(); +} + +/* tests success case of a same-thread marshal and unmarshal */ +static void test_normal_marshal_and_unmarshal(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy); + + ok_no_locks(); +} + +/* tests failure case of unmarshaling a freed object */ +static void test_marshal_and_unmarshal_invalid(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *pProxy = NULL; + DWORD tid; + void * dummy; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoReleaseMarshalData(pStream); + ok_ole_success(hr, CoReleaseMarshalData); + + ok_no_locks(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + todo_wine { ok_ole_success(hr, CoUnmarshalInterface); } + + ok_no_locks(); + + if (pProxy) + { + hr = IClassFactory_CreateInstance(pProxy, NULL, &IID_IUnknown, &dummy); + ok(hr == RPC_E_DISCONNECTED, "Remote call should have returned RPC_E_DISCONNECTED, instead of 0x%08x\n", hr); + + IClassFactory_Release(pProxy); + } + + IStream_Release(pStream); + + end_host_object(tid, thread); +} + +static void test_same_apartment_unmarshal_failure(void) +{ + HRESULT hr; + IStream *pStream; + IUnknown *pProxy; + static const LARGE_INTEGER llZero; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + + hr = CoMarshalInterface(pStream, &IID_IUnknown, (IUnknown *)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + hr = IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, IStream_Seek); + + hr = CoUnmarshalInterface(pStream, &IID_IParseDisplayName, (void **)&pProxy); + ok(hr == E_NOINTERFACE, "CoUnmarshalInterface should have returned E_NOINTERFACE instead of 0x%08x\n", hr); + + ok_no_locks(); + + IStream_Release(pStream); +} + +/* tests success case of an interthread marshal */ +static void test_interthread_marshal_and_unmarshal(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy); + + ok_no_locks(); + + end_host_object(tid, thread); +} + +/* the number of external references that Wine's proxy manager normally gives + * out, so we can test the border case of running out of references */ +#define NORMALEXTREFS 5 + +/* tests success case of an interthread marshal and then marshaling the proxy */ +static void test_proxy_marshal_and_unmarshal(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + IUnknown *pProxy2 = NULL; + DWORD tid; + HANDLE thread; + int i; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + /* marshal the proxy */ + hr = CoMarshalInterface(pStream, &IID_IClassFactory, pProxy, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + /* marshal 5 more times to exhaust the normal external references of 5 */ + for (i = 0; i < NORMALEXTREFS; i++) + { + hr = CoMarshalInterface(pStream, &IID_IClassFactory, pProxy, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + } + + ok_more_than_one_lock(); + + /* release the original proxy to test that we successfully keep the + * original object alive */ + IUnknown_Release(pProxy); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy2); + + /* unmarshal all of the proxies to check that the object stub still exists */ + for (i = 0; i < NORMALEXTREFS; i++) + { + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok_ole_success(hr, CoUnmarshalInterface); + + IUnknown_Release(pProxy2); + } + + ok_no_locks(); + + IStream_Release(pStream); + + end_host_object(tid, thread); +} + +/* tests success case of an interthread marshal and then marshaling the proxy + * using an iid that hasn't previously been unmarshaled */ +static void test_proxy_marshal_and_unmarshal2(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + IUnknown *pProxy2 = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IUnknown, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + /* marshal the proxy */ + hr = CoMarshalInterface(pStream, &IID_IClassFactory, pProxy, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + /* unmarshal the second proxy to the object */ + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + /* now the proxies should be as follows: + * pProxy -> &Test_ClassFactory + * pProxy2 -> &Test_ClassFactory + * they should NOT be as follows: + * pProxy -> &Test_ClassFactory + * pProxy2 -> pProxy + * the above can only really be tested by looking in +ole traces + */ + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy2); + + ok_no_locks(); + + end_host_object(tid, thread); +} + +/* tests success case of an interthread marshal and then table-weak-marshaling the proxy */ +static void test_proxy_marshal_and_unmarshal_weak(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + IUnknown *pProxy2 = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + /* marshal the proxy */ + hr = CoMarshalInterface(pStream, &IID_IClassFactory, pProxy, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLEWEAK); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + /* release the original proxy to test that we successfully keep the + * original object alive */ + IUnknown_Release(pProxy); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + todo_wine + ok(hr == CO_E_OBJNOTREG, "CoUnmarshalInterface should return CO_E_OBJNOTREG instead of 0x%08x\n", hr); + + ok_no_locks(); + + IStream_Release(pStream); + + end_host_object(tid, thread); +} + +/* tests success case of an interthread marshal and then table-strong-marshaling the proxy */ +static void test_proxy_marshal_and_unmarshal_strong(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + IUnknown *pProxy2 = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + /* marshal the proxy */ + hr = CoMarshalInterface(pStream, &IID_IClassFactory, pProxy, MSHCTX_INPROC, NULL, MSHLFLAGS_TABLESTRONG); + ok(hr == S_OK /* WinNT */ || hr == E_INVALIDARG /* Win9x */, + "CoMarshalInterface should have return S_OK or E_INVALIDARG instead of 0x%08x\n", hr); + if (FAILED(hr)) + { + IUnknown_Release(pProxy); + goto end; + } + + ok_more_than_one_lock(); + + /* release the original proxy to test that we successfully keep the + * original object alive */ + IUnknown_Release(pProxy); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy2); + + ok_more_than_one_lock(); + +end: + IStream_Release(pStream); + + end_host_object(tid, thread); + + ok_no_locks(); +} + +/* tests that stubs are released when the containing apartment is destroyed */ +static void test_marshal_stub_apartment_shutdown(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + end_host_object(tid, thread); + + ok_no_locks(); + + IUnknown_Release(pProxy); + + ok_no_locks(); +} + +/* tests that proxies are released when the containing apartment is destroyed */ +static void test_marshal_proxy_apartment_shutdown(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + CoUninitialize(); + + ok_no_locks(); + + IUnknown_Release(pProxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); +} + +/* tests that proxies are released when the containing mta apartment is destroyed */ +static void test_marshal_proxy_mta_apartment_shutdown(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + DWORD tid; + HANDLE thread; + + CoUninitialize(); + pCoInitializeEx(NULL, COINIT_MULTITHREADED); + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + CoUninitialize(); + + ok_no_locks(); + + IUnknown_Release(pProxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); +} + +struct ncu_params +{ + LPSTREAM stream; + HANDLE marshal_event; + HANDLE unmarshal_event; +}; + +/* helper for test_no_couninitialize_server */ +static DWORD CALLBACK no_couninitialize_server_proc(LPVOID p) +{ + struct ncu_params *ncu_params = (struct ncu_params *)p; + HRESULT hr; + + pCoInitializeEx(NULL, COINIT_MULTITHREADED); + + hr = CoMarshalInterface(ncu_params->stream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + SetEvent(ncu_params->marshal_event); + + WaitForSingleObject(ncu_params->unmarshal_event, INFINITE); + + /* die without calling CoUninitialize */ + + return 0; +} + +/* tests apartment that an apartment with a stub is released without deadlock + * if the owning thread exits */ +static void test_no_couninitialize_server(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + DWORD tid; + HANDLE thread; + struct ncu_params ncu_params; + + cLocks = 0; + + ncu_params.marshal_event = CreateEvent(NULL, TRUE, FALSE, NULL); + ncu_params.unmarshal_event = CreateEvent(NULL, TRUE, FALSE, NULL); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + ncu_params.stream = pStream; + + thread = CreateThread(NULL, 0, no_couninitialize_server_proc, &ncu_params, 0, &tid); + + WaitForSingleObject(ncu_params.marshal_event, INFINITE); + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + SetEvent(ncu_params.unmarshal_event); + WaitForSingleObject(thread, INFINITE); + + ok_no_locks(); + + CloseHandle(thread); + CloseHandle(ncu_params.marshal_event); + CloseHandle(ncu_params.unmarshal_event); + + IUnknown_Release(pProxy); + + ok_no_locks(); +} + +/* STA -> STA call during DLL_THREAD_DETACH */ +static DWORD CALLBACK no_couninitialize_client_proc(LPVOID p) +{ + struct ncu_params *ncu_params = (struct ncu_params *)p; + HRESULT hr; + IUnknown *pProxy = NULL; + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = CoUnmarshalInterface(ncu_params->stream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(ncu_params->stream); + + ok_more_than_one_lock(); + + /* die without calling CoUninitialize */ + + return 0; +} + +/* tests STA -> STA call during DLL_THREAD_DETACH doesn't deadlock */ +static void test_no_couninitialize_client(void) +{ + HRESULT hr; + IStream *pStream = NULL; + DWORD tid; + DWORD host_tid; + HANDLE thread; + HANDLE host_thread; + struct ncu_params ncu_params; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + ncu_params.stream = pStream; + + /* NOTE: assumes start_host_object uses an STA to host the object, as MTAs + * always deadlock when called from within DllMain */ + host_tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown *)&Test_ClassFactory, MSHLFLAGS_NORMAL, &host_thread); + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + + ok_more_than_one_lock(); + + thread = CreateThread(NULL, 0, no_couninitialize_client_proc, &ncu_params, 0, &tid); + + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + ok_no_locks(); + + end_host_object(host_tid, host_thread); +} + +/* tests success case of a same-thread table-weak marshal, unmarshal, unmarshal */ +static void test_tableweak_marshal_and_unmarshal_twice(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy1 = NULL; + IUnknown *pProxy2 = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_TABLEWEAK, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy1); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + IStream_Release(pStream); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy1); + IUnknown_Release(pProxy2); + + /* this line is shows the difference between weak and strong table marshaling: + * weak has cLocks == 0 + * strong has cLocks > 0 */ + ok_no_locks(); + + end_host_object(tid, thread); +} + +/* tests releasing after unmarshaling one object */ +static void test_tableweak_marshal_releasedata1(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy1 = NULL; + IUnknown *pProxy2 = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_TABLEWEAK, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy1); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + /* release the remaining reference on the object by calling + * CoReleaseMarshalData in the hosting thread */ + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + release_host_object(tid); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy1); + if (pProxy2) + IUnknown_Release(pProxy2); + + /* this line is shows the difference between weak and strong table marshaling: + * weak has cLocks == 0 + * strong has cLocks > 0 */ + ok_no_locks(); + + end_host_object(tid, thread); +} + +/* tests releasing after unmarshaling one object */ +static void test_tableweak_marshal_releasedata2(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_TABLEWEAK, &thread); + + ok_more_than_one_lock(); + + /* release the remaining reference on the object by calling + * CoReleaseMarshalData in the hosting thread */ + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + release_host_object(tid); + + ok_no_locks(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + todo_wine + { + ok(hr == CO_E_OBJNOTREG, + "CoUnmarshalInterface should have failed with CO_E_OBJNOTREG, but returned 0x%08x instead\n", + hr); + } + IStream_Release(pStream); + + ok_no_locks(); + + end_host_object(tid, thread); +} + +/* tests success case of a same-thread table-strong marshal, unmarshal, unmarshal */ +static void test_tablestrong_marshal_and_unmarshal_twice(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy1 = NULL; + IUnknown *pProxy2 = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_TABLESTRONG, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy1); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + if (pProxy1) IUnknown_Release(pProxy1); + if (pProxy2) IUnknown_Release(pProxy2); + + /* this line is shows the difference between weak and strong table marshaling: + * weak has cLocks == 0 + * strong has cLocks > 0 */ + ok_more_than_one_lock(); + + /* release the remaining reference on the object by calling + * CoReleaseMarshalData in the hosting thread */ + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + release_host_object(tid); + IStream_Release(pStream); + + ok_no_locks(); + + end_host_object(tid, thread); +} + +/* tests CoLockObjectExternal */ +static void test_lock_object_external(void) +{ + HRESULT hr; + IStream *pStream = NULL; + + cLocks = 0; + + /* test the stub manager creation aspect of CoLockObjectExternal when the + * object hasn't been marshaled yet */ + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, TRUE); + + ok_more_than_one_lock(); + + CoDisconnectObject((IUnknown*)&Test_ClassFactory, 0); + + ok_no_locks(); + + /* test our empty stub manager being handled correctly in + * CoMarshalInterface */ + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, TRUE); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, TRUE); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoReleaseMarshalData(pStream); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, TRUE); + + ok_more_than_one_lock(); + + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, TRUE); + + ok_no_locks(); +} + +/* tests disconnecting stubs */ +static void test_disconnect_stub(void) +{ + HRESULT hr; + IStream *pStream = NULL; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + CoLockObjectExternal((IUnknown*)&Test_ClassFactory, TRUE, TRUE); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoReleaseMarshalData(pStream); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + CoDisconnectObject((IUnknown*)&Test_ClassFactory, 0); + + ok_no_locks(); +} + +/* tests failure case of a same-thread marshal and unmarshal twice */ +static void test_normal_marshal_and_unmarshal_twice(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy1 = NULL; + IUnknown *pProxy2 = NULL; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy1); + ok_ole_success(hr, CoUnmarshalInterface); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok(hr == CO_E_OBJNOTCONNECTED, + "CoUnmarshalInterface should have failed with error CO_E_OBJNOTCONNECTED for double unmarshal, instead of 0x%08x\n", hr); + + IStream_Release(pStream); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy1); + + ok_no_locks(); +} + +/* tests success case of marshaling and unmarshaling an HRESULT */ +static void test_hresult_marshaling(void) +{ + HRESULT hr; + HRESULT hr_marshaled = 0; + IStream *pStream = NULL; + static const HRESULT E_DEADBEEF = 0xdeadbeef; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + + hr = CoMarshalHresult(pStream, E_DEADBEEF); + ok_ole_success(hr, CoMarshalHresult); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = IStream_Read(pStream, &hr_marshaled, sizeof(HRESULT), NULL); + ok_ole_success(hr, IStream_Read); + + ok(hr_marshaled == E_DEADBEEF, "Didn't marshal HRESULT as expected: got value 0x%08x instead\n", hr_marshaled); + + hr_marshaled = 0; + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalHresult(pStream, &hr_marshaled); + ok_ole_success(hr, CoUnmarshalHresult); + + ok(hr_marshaled == E_DEADBEEF, "Didn't marshal HRESULT as expected: got value 0x%08x instead\n", hr_marshaled); + + IStream_Release(pStream); +} + + +/* helper for test_proxy_used_in_wrong_thread */ +static DWORD CALLBACK bad_thread_proc(LPVOID p) +{ + IClassFactory * cf = (IClassFactory *)p; + HRESULT hr; + IUnknown * proxy = NULL; + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (LPVOID*)&proxy); + todo_wine + ok(hr == CO_E_NOTINITIALIZED, + "COM should have failed with CO_E_NOTINITIALIZED on using proxy without apartment, but instead returned 0x%08x\n", + hr); + + hr = IClassFactory_QueryInterface(cf, &IID_IMultiQI, (LPVOID *)&proxy); + /* Win9x returns S_OK, whilst NT returns RPC_E_WRONG_THREAD */ + trace("call to proxy's QueryInterface for local interface without apartment returned 0x%08x\n", hr); + if (SUCCEEDED(hr)) + IUnknown_Release(proxy); + + hr = IClassFactory_QueryInterface(cf, &IID_IStream, (LPVOID *)&proxy); + /* Win9x returns E_NOINTERFACE, whilst NT returns RPC_E_WRONG_THREAD */ + trace("call to proxy's QueryInterface without apartment returned 0x%08x\n", hr); + if (SUCCEEDED(hr)) + IUnknown_Release(proxy); + + pCoInitializeEx(NULL, COINIT_MULTITHREADED); + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (LPVOID*)&proxy); + if (proxy) IUnknown_Release(proxy); + ok(hr == RPC_E_WRONG_THREAD, + "COM should have failed with RPC_E_WRONG_THREAD on using proxy from wrong apartment, but instead returned 0x%08x\n", + hr); + + hr = IClassFactory_QueryInterface(cf, &IID_IStream, (LPVOID *)&proxy); + /* Win9x returns E_NOINTERFACE, whilst NT returns RPC_E_WRONG_THREAD */ + trace("call to proxy's QueryInterface from wrong apartment returned 0x%08x\n", hr); + + /* this statement causes Win9x DCOM to crash during CoUninitialize of + * other apartment, so don't test this on Win9x (signified by NT-only + * export of CoRegisterSurrogateEx) */ + if (GetProcAddress(GetModuleHandle("ole32"), "CoRegisterSurrogateEx")) + /* now be really bad and release the proxy from the wrong apartment */ + IUnknown_Release(cf); + else + skip("skipping test for releasing proxy from wrong apartment that will succeed, but cause a crash during CoUninitialize\n"); + + CoUninitialize(); + + return 0; +} + +/* tests failure case of a using a proxy in the wrong apartment */ +static void test_proxy_used_in_wrong_thread(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + DWORD tid, tid2; + HANDLE thread; + HANDLE host_thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &host_thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + /* do a call that will fail, but result in IRemUnknown being used by the proxy */ + IClassFactory_QueryInterface(pProxy, &IID_IStream, (LPVOID *)&pStream); + + /* create a thread that we can misbehave in */ + thread = CreateThread(NULL, 0, bad_thread_proc, (LPVOID)pProxy, 0, &tid2); + + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + /* do release statement on Win9x that we should have done above */ + if (!GetProcAddress(GetModuleHandle("ole32"), "CoRegisterSurrogateEx")) + IUnknown_Release(pProxy); + + ok_no_locks(); + + end_host_object(tid, host_thread); +} + +static HRESULT WINAPI MessageFilter_QueryInterface(IMessageFilter *iface, REFIID riid, void ** ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IClassFactory)) + { + *ppvObj = (LPVOID)iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI MessageFilter_AddRef(IMessageFilter *iface) +{ + return 2; /* non-heap object */ +} + +static ULONG WINAPI MessageFilter_Release(IMessageFilter *iface) +{ + return 1; /* non-heap object */ +} + +static DWORD WINAPI MessageFilter_HandleInComingCall( + IMessageFilter *iface, + DWORD dwCallType, + HTASK threadIDCaller, + DWORD dwTickCount, + LPINTERFACEINFO lpInterfaceInfo) +{ + static int callcount = 0; + DWORD ret; + trace("HandleInComingCall\n"); + switch (callcount) + { + case 0: + ret = SERVERCALL_REJECTED; + break; + case 1: + ret = SERVERCALL_RETRYLATER; + break; + default: + ret = SERVERCALL_ISHANDLED; + break; + } + callcount++; + return ret; +} + +static DWORD WINAPI MessageFilter_RetryRejectedCall( + IMessageFilter *iface, + HTASK threadIDCallee, + DWORD dwTickCount, + DWORD dwRejectType) +{ + trace("RetryRejectedCall\n"); + return 0; +} + +static DWORD WINAPI MessageFilter_MessagePending( + IMessageFilter *iface, + HTASK threadIDCallee, + DWORD dwTickCount, + DWORD dwPendingType) +{ + trace("MessagePending\n"); + return PENDINGMSG_WAITNOPROCESS; +} + +static const IMessageFilterVtbl MessageFilter_Vtbl = +{ + MessageFilter_QueryInterface, + MessageFilter_AddRef, + MessageFilter_Release, + MessageFilter_HandleInComingCall, + MessageFilter_RetryRejectedCall, + MessageFilter_MessagePending +}; + +static IMessageFilter MessageFilter = { &MessageFilter_Vtbl }; + +static void test_message_filter(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *cf = NULL; + DWORD tid; + IUnknown *proxy = NULL; + IMessageFilter *prev_filter = NULL; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object2(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &MessageFilter, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&cf); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (LPVOID*)&proxy); + ok(hr == RPC_E_CALL_REJECTED, "Call should have returned RPC_E_CALL_REJECTED, but return 0x%08x instead\n", hr); + if (proxy) IUnknown_Release(proxy); + proxy = NULL; + + hr = CoRegisterMessageFilter(&MessageFilter, &prev_filter); + ok_ole_success(hr, CoRegisterMessageFilter); + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (LPVOID*)&proxy); + ok_ole_success(hr, IClassFactory_CreateInstance); + + IUnknown_Release(proxy); + + IClassFactory_Release(cf); + + ok_no_locks(); + + end_host_object(tid, thread); + + hr = CoRegisterMessageFilter(prev_filter, NULL); + ok_ole_success(hr, CoRegisterMessageFilter); +} + +/* test failure case of trying to unmarshal from bad stream */ +static void test_bad_marshal_stream(void) +{ + HRESULT hr; + IStream *pStream = NULL; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + hr = CoMarshalInterface(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + ok_more_than_one_lock(); + + /* try to read beyond end of stream */ + hr = CoReleaseMarshalData(pStream); + ok(hr == STG_E_READFAULT, "Should have failed with STG_E_READFAULT, but returned 0x%08x instead\n", hr); + + /* now release for real */ + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoReleaseMarshalData(pStream); + ok_ole_success(hr, CoReleaseMarshalData); + + IStream_Release(pStream); +} + +/* tests that proxies implement certain interfaces */ +static void test_proxy_interfaces(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + IUnknown *pOtherUnknown = NULL; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + hr = IUnknown_QueryInterface(pProxy, &IID_IUnknown, (LPVOID*)&pOtherUnknown); + ok_ole_success(hr, IUnknown_QueryInterface IID_IUnknown); + if (hr == S_OK) IUnknown_Release(pOtherUnknown); + + hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (LPVOID*)&pOtherUnknown); + ok_ole_success(hr, IUnknown_QueryInterface IID_IClientSecurity); + if (hr == S_OK) IUnknown_Release(pOtherUnknown); + + hr = IUnknown_QueryInterface(pProxy, &IID_IMultiQI, (LPVOID*)&pOtherUnknown); + ok_ole_success(hr, IUnknown_QueryInterface IID_IMultiQI); + if (hr == S_OK) IUnknown_Release(pOtherUnknown); + + hr = IUnknown_QueryInterface(pProxy, &IID_IMarshal, (LPVOID*)&pOtherUnknown); + ok_ole_success(hr, IUnknown_QueryInterface IID_IMarshal); + if (hr == S_OK) IUnknown_Release(pOtherUnknown); + + /* IMarshal2 is also supported on NT-based systems, but is pretty much + * useless as it has no more methods over IMarshal that it inherits from. */ + + IUnknown_Release(pProxy); + + ok_no_locks(); + + end_host_object(tid, thread); +} + +typedef struct +{ + const IUnknownVtbl *lpVtbl; + ULONG refs; +} HeapUnknown; + +static HRESULT WINAPI HeapUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + IUnknown_AddRef(iface); + *ppv = (LPVOID)iface; + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI HeapUnknown_AddRef(IUnknown *iface) +{ + HeapUnknown *This = (HeapUnknown *)iface; + return InterlockedIncrement((LONG*)&This->refs); +} + +static ULONG WINAPI HeapUnknown_Release(IUnknown *iface) +{ + HeapUnknown *This = (HeapUnknown *)iface; + ULONG refs = InterlockedDecrement((LONG*)&This->refs); + if (!refs) HeapFree(GetProcessHeap(), 0, This); + return refs; +} + +static const IUnknownVtbl HeapUnknown_Vtbl = +{ + HeapUnknown_QueryInterface, + HeapUnknown_AddRef, + HeapUnknown_Release +}; + +static void test_proxybuffer(REFIID riid) +{ + HRESULT hr; + IPSFactoryBuffer *psfb; + IRpcProxyBuffer *proxy; + LPVOID lpvtbl; + ULONG refs; + CLSID clsid; + HeapUnknown *pUnkOuter = HeapAlloc(GetProcessHeap(), 0, sizeof(*pUnkOuter)); + + pUnkOuter->lpVtbl = &HeapUnknown_Vtbl; + pUnkOuter->refs = 1; + + hr = CoGetPSClsid(riid, &clsid); + ok_ole_success(hr, CoGetPSClsid); + + hr = CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (LPVOID*)&psfb); + ok_ole_success(hr, CoGetClassObject); + + hr = IPSFactoryBuffer_CreateProxy(psfb, (IUnknown*)pUnkOuter, riid, &proxy, &lpvtbl); + ok_ole_success(hr, IPSFactoryBuffer_CreateProxy); + ok(lpvtbl != NULL, "IPSFactoryBuffer_CreateProxy succeeded, but returned a NULL vtable!\n"); + + /* release our reference to the outer unknown object - the PS factory + * buffer will have AddRef's it in the CreateProxy call */ + refs = IUnknown_Release((IUnknown *)pUnkOuter); + ok(refs == 1, "Ref count of outer unknown should have been 1 instead of %d\n", refs); + + refs = IPSFactoryBuffer_Release(psfb); + if (0) + { + /* not reliable on native. maybe it leaks references! */ + ok(refs == 0, "Ref-count leak of %d on IPSFactoryBuffer\n", refs); + } + + refs = IUnknown_Release((IUnknown *)lpvtbl); + ok(refs == 0, "Ref-count leak of %d on IRpcProxyBuffer\n", refs); + + refs = IRpcProxyBuffer_Release(proxy); + ok(refs == 0, "Ref-count leak of %d on IRpcProxyBuffer\n", refs); +} + +static void test_stubbuffer(REFIID riid) +{ + HRESULT hr; + IPSFactoryBuffer *psfb; + IRpcStubBuffer *stub; + ULONG refs; + CLSID clsid; + + cLocks = 0; + + hr = CoGetPSClsid(riid, &clsid); + ok_ole_success(hr, CoGetPSClsid); + + hr = CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IPSFactoryBuffer, (LPVOID*)&psfb); + ok_ole_success(hr, CoGetClassObject); + + hr = IPSFactoryBuffer_CreateStub(psfb, riid, (IUnknown*)&Test_ClassFactory, &stub); + ok_ole_success(hr, IPSFactoryBuffer_CreateStub); + + refs = IPSFactoryBuffer_Release(psfb); + if (0) + { + /* not reliable on native. maybe it leaks references */ + ok(refs == 0, "Ref-count leak of %d on IPSFactoryBuffer\n", refs); + } + + ok_more_than_one_lock(); + + IRpcStubBuffer_Disconnect(stub); + + ok_no_locks(); + + refs = IRpcStubBuffer_Release(stub); + ok(refs == 0, "Ref-count leak of %d on IRpcProxyBuffer\n", refs); +} + +static HWND hwnd_app; + +static HRESULT WINAPI TestRE_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + DWORD_PTR res; + if (IsEqualIID(riid, &IID_IWineTest)) + { + BOOL ret = SendMessageTimeout(hwnd_app, WM_NULL, 0, 0, SMTO_BLOCK, 5000, &res); + ok(ret, "Timed out sending a message to originating window during RPC call\n"); + } + return S_FALSE; +} + +static const IClassFactoryVtbl TestREClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + TestRE_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +IClassFactory TestRE_ClassFactory = { &TestREClassFactory_Vtbl }; + +static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_USER: + { + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *proxy = NULL; + IUnknown *object; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&TestRE_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + /* note the use of the magic IID_IWineTest value to tell remote thread + * to try to send a message back to us */ + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IWineTest, (void **)&object); + + IClassFactory_Release(proxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + PostMessage(hwnd, WM_QUIT, 0, 0); + + return 0; + } + case WM_USER+1: + { + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *proxy = NULL; + IUnknown *object; + DWORD tid; + HANDLE thread; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&TestRE_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + /* post quit message before a doing a COM call to show that a pending + * WM_QUIT message doesn't stop the call from succeeding */ + PostMessage(hwnd, WM_QUIT, 0, 0); + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&object); + + IClassFactory_Release(proxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + return 0; + } + case WM_USER+2: + { + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *proxy = NULL; + IUnknown *object; + DWORD tid; + HANDLE thread; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + /* shows that COM calls executed during the processing of sent + * messages should fail */ + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&object); + ok(hr == RPC_E_CANTCALLOUT_ININPUTSYNCCALL, + "COM call during processing of sent message should return RPC_E_CANTCALLOUT_ININPUTSYNCCALL instead of 0x%08x\n", hr); + + IClassFactory_Release(proxy); + + end_host_object(tid, thread); + + PostQuitMessage(0); + + return 0; + } + default: + return DefWindowProc(hwnd, msg, wparam, lparam); + } +} + +static void test_message_reentrancy(void) +{ + WNDCLASS wndclass; + MSG msg; + + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.lpfnWndProc = window_proc; + wndclass.lpszClassName = "WineCOMTest"; + RegisterClass(&wndclass); + + hwnd_app = CreateWindow("WineCOMTest", NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, 0); + ok(hwnd_app != NULL, "Window creation failed\n"); + + /* start message re-entrancy test */ + PostMessage(hwnd_app, WM_USER, 0, 0); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +static HRESULT WINAPI TestMsg_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + *ppvObj = NULL; + SendMessage(hwnd_app, WM_USER+2, 0, 0); + return S_OK; +} + +static IClassFactoryVtbl TestMsgClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + TestMsg_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +IClassFactory TestMsg_ClassFactory = { &TestMsgClassFactory_Vtbl }; + +static void test_call_from_message(void) +{ + MSG msg; + IStream *pStream; + HRESULT hr; + IClassFactory *proxy; + DWORD tid; + HANDLE thread; + IUnknown *object; + + hwnd_app = CreateWindow("WineCOMTest", NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, 0); + ok(hwnd_app != NULL, "Window creation failed\n"); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&TestMsg_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&proxy); + ok_ole_success(hr, CoReleaseMarshalData); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + /* start message re-entrancy test */ + hr = IClassFactory_CreateInstance(proxy, NULL, &IID_IUnknown, (void **)&object); + ok_ole_success(hr, IClassFactory_CreateInstance); + + IClassFactory_Release(proxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +static void test_WM_QUIT_handling(void) +{ + MSG msg; + + hwnd_app = CreateWindow("WineCOMTest", NULL, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, 0); + ok(hwnd_app != NULL, "Window creation failed\n"); + + /* start WM_QUIT handling test */ + PostMessage(hwnd_app, WM_USER+1, 0, 0); + + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +static void test_freethreadedmarshaldata(IStream *pStream, MSHCTX mshctx, void *ptr, DWORD mshlflags) +{ + HGLOBAL hglobal; + DWORD size; + char *marshal_data; + HRESULT hr; + + hr = GetHGlobalFromStream(pStream, &hglobal); + ok_ole_success(hr, GetHGlobalFromStream); + + size = GlobalSize(hglobal); + + marshal_data = (char *)GlobalLock(hglobal); + + if (mshctx == MSHCTX_INPROC) + { + DWORD expected_size = sizeof(DWORD) + sizeof(void *) + sizeof(DWORD) + sizeof(GUID); + ok(size == expected_size, "size should have been %d instead of %d\n", expected_size, size); + + ok(*(DWORD *)marshal_data == mshlflags, "expected 0x%x, but got 0x%x for mshctx\n", mshlflags, *(DWORD *)marshal_data); + marshal_data += sizeof(DWORD); + ok(*(void **)marshal_data == ptr, "expected %p, but got %p for mshctx\n", ptr, *(void **)marshal_data); + marshal_data += sizeof(void *); + ok(*(DWORD *)marshal_data == 0, "expected 0x0, but got 0x%x\n", *(DWORD *)marshal_data); + marshal_data += sizeof(DWORD); + trace("got guid data: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + ((GUID *)marshal_data)->Data1, ((GUID *)marshal_data)->Data2, ((GUID *)marshal_data)->Data3, + ((GUID *)marshal_data)->Data4[0], ((GUID *)marshal_data)->Data4[1], ((GUID *)marshal_data)->Data4[2], ((GUID *)marshal_data)->Data4[3], + ((GUID *)marshal_data)->Data4[4], ((GUID *)marshal_data)->Data4[5], ((GUID *)marshal_data)->Data4[6], ((GUID *)marshal_data)->Data4[7]); + } + else + { + ok(size > sizeof(DWORD), "size should have been > sizeof(DWORD), not %d\n", size); + ok(*(DWORD *)marshal_data == 0x574f454d /* MEOW */, + "marshal data should be filled by standard marshal and start with MEOW signature\n"); + } + + GlobalUnlock(hglobal); +} + +static void test_freethreadedmarshaler(void) +{ + HRESULT hr; + IUnknown *pFTUnknown; + IMarshal *pFTMarshal; + IStream *pStream; + IUnknown *pProxy; + static const LARGE_INTEGER llZero; + + cLocks = 0; + hr = CoCreateFreeThreadedMarshaler(NULL, &pFTUnknown); + ok_ole_success(hr, CoCreateFreeThreadedMarshaler); + hr = IUnknown_QueryInterface(pFTUnknown, &IID_IMarshal, (void **)&pFTMarshal); + ok_ole_success(hr, IUnknown_QueryInterface); + IUnknown_Release(pFTUnknown); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + + /* inproc normal marshaling */ + + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + test_freethreadedmarshaldata(pStream, MSHCTX_INPROC, &Test_ClassFactory, MSHLFLAGS_NORMAL); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + IUnknown_Release(pProxy); + + ok_no_locks(); + +/* native doesn't allow us to unmarshal or release the stream data, + * presumably because it wants us to call CoMarshalInterface instead */ + if (0) + { + /* local normal marshaling */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + test_freethreadedmarshaldata(pStream, MSHCTX_LOCAL, &Test_ClassFactory, MSHLFLAGS_NORMAL); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_ReleaseMarshalData(pFTMarshal, pStream); + ok_ole_success(hr, IMarshal_ReleaseMarshalData); + + ok_no_locks(); + } + + /* inproc table-strong marshaling */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, (void *)0xdeadbeef, + MSHLFLAGS_TABLESTRONG); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + test_freethreadedmarshaldata(pStream, MSHCTX_INPROC, &Test_ClassFactory, MSHLFLAGS_TABLESTRONG); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + IUnknown_Release(pProxy); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_ReleaseMarshalData(pFTMarshal, pStream); + ok_ole_success(hr, IMarshal_ReleaseMarshalData); + + ok_no_locks(); + + /* inproc table-weak marshaling */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, (void *)0xdeadbeef, + MSHLFLAGS_TABLEWEAK); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_no_locks(); + + test_freethreadedmarshaldata(pStream, MSHCTX_INPROC, &Test_ClassFactory, MSHLFLAGS_TABLEWEAK); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + ok_more_than_one_lock(); + + IUnknown_Release(pProxy); + + ok_no_locks(); + + /* inproc normal marshaling (for extraordinary cases) */ + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_MarshalInterface(pFTMarshal, pStream, &IID_IClassFactory, + (IUnknown*)&Test_ClassFactory, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, IMarshal_MarshalInterface); + + ok_more_than_one_lock(); + + /* this call shows that DisconnectObject does nothing */ + hr = IMarshal_DisconnectObject(pFTMarshal, 0); + ok_ole_success(hr, IMarshal_DisconnectObject); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_ReleaseMarshalData(pFTMarshal, pStream); + ok_ole_success(hr, IMarshal_ReleaseMarshalData); + + ok_no_locks(); + + /* doesn't enforce marshaling rules here and allows us to unmarshal the + * interface, even though it was freed above */ + IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + hr = IMarshal_UnmarshalInterface(pFTMarshal, pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, IMarshal_UnmarshalInterface); + + ok_no_locks(); + + IStream_Release(pStream); + IMarshal_Release(pFTMarshal); +} + +static void test_inproc_handler(void) +{ + HRESULT hr; + IUnknown *pObject; + IUnknown *pObject2; + char buffer[256]; + LPOLESTR pszClsid; + HKEY hkey; + DWORD dwDisposition; + DWORD error; + + hr = StringFromCLSID(&CLSID_WineTest, &pszClsid); + ok_ole_success(hr, "StringFromCLSID"); + strcpy(buffer, "CLSID\\"); + WideCharToMultiByte(CP_ACP, 0, pszClsid, -1, buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), NULL, NULL); + CoTaskMemFree(pszClsid); + strcat(buffer, "\\InprocHandler32"); + error = RegCreateKeyEx(HKEY_CLASSES_ROOT, buffer, 0, NULL, 0, KEY_SET_VALUE, NULL, &hkey, &dwDisposition); + ok(error == ERROR_SUCCESS, "RegCreateKeyEx failed with error %d\n", error); + error = RegSetValueEx(hkey, NULL, 0, REG_SZ, (const unsigned char *)"ole32.dll", strlen("ole32.dll") + 1); + ok(error == ERROR_SUCCESS, "RegSetValueEx failed with error %d\n", error); + RegCloseKey(hkey); + + hr = CoCreateInstance(&CLSID_WineTest, NULL, CLSCTX_INPROC_HANDLER, &IID_IUnknown, (void **)&pObject); + todo_wine + ok_ole_success(hr, "CoCreateInstance"); + + if (SUCCEEDED(hr)) + { + hr = IUnknown_QueryInterface(pObject, &IID_IWineTest, (void **)&pObject2); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface on handler for invalid interface returned 0x%08x instead of E_NOINTERFACE\n", hr); + + /* it's a handler as it supports IOleObject */ + hr = IUnknown_QueryInterface(pObject, &IID_IOleObject, (void **)&pObject2); + ok_ole_success(hr, "IUnknown_QueryInterface(&IID_IOleObject)"); + IUnknown_Release(pObject2); + + IUnknown_Release(pObject); + } + + RegDeleteKey(HKEY_CLASSES_ROOT, buffer); + *strrchr(buffer, '\\') = '\0'; + RegDeleteKey(HKEY_CLASSES_ROOT, buffer); +} + +static HRESULT WINAPI Test_SMI_QueryInterface( + IStdMarshalInfo *iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IStdMarshalInfo)) + { + *ppvObj = (LPVOID)iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_SMI_AddRef(IStdMarshalInfo *iface) +{ + LockModule(); + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_SMI_Release(IStdMarshalInfo *iface) +{ + UnlockModule(); + return 1; /* non-heap-based object */ +} + +static HRESULT WINAPI Test_SMI_GetClassForHandler( + IStdMarshalInfo *iface, + DWORD dwDestContext, + void *pvDestContext, + CLSID *pClsid) +{ + *pClsid = CLSID_WineTest; + return S_OK; +} + +static const IStdMarshalInfoVtbl Test_SMI_Vtbl = +{ + Test_SMI_QueryInterface, + Test_SMI_AddRef, + Test_SMI_Release, + Test_SMI_GetClassForHandler +}; + +static IStdMarshalInfo Test_SMI = {&Test_SMI_Vtbl}; + +static void test_handler_marshaling(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IUnknown *pProxy = NULL; + IUnknown *pObject; + DWORD tid; + HANDLE thread; + static const LARGE_INTEGER ullZero; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + tid = start_host_object(pStream, &IID_IUnknown, (IUnknown*)&Test_SMI, MSHLFLAGS_NORMAL, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IUnknown, (void **)&pProxy); + ok_ole_success(hr, "CoUnmarshalInterface"); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + hr = IUnknown_QueryInterface(pProxy, &IID_IWineTest, (void **)&pObject); + ok(hr == E_NOINTERFACE, "IUnknown_QueryInterface with unknown IID should have returned E_NOINTERFACE instead of 0x%08x\n", hr); + + /* it's a handler as it supports IOleObject */ + hr = IUnknown_QueryInterface(pProxy, &IID_IOleObject, (void **)&pObject); + todo_wine + ok_ole_success(hr, "IUnknown_QueryInterface(&IID_IOleObject)"); + if (SUCCEEDED(hr)) IUnknown_Release(pObject); + + IUnknown_Release(pProxy); + + ok_no_locks(); + + end_host_object(tid, thread); + + /* FIXME: test IPersist interface has the same effect as IStdMarshalInfo */ +} + + +static void test_client_security(void) +{ + HRESULT hr; + IStream *pStream = NULL; + IClassFactory *pProxy = NULL; + IUnknown *pProxy2 = NULL; + IUnknown *pUnknown1 = NULL; + IUnknown *pUnknown2 = NULL; + IClientSecurity *pCliSec = NULL; + IMarshal *pMarshal; + DWORD tid; + HANDLE thread; + static const LARGE_INTEGER ullZero; + DWORD dwAuthnSvc; + DWORD dwAuthzSvc; + OLECHAR *pServerPrincName; + DWORD dwAuthnLevel; + DWORD dwImpLevel; + void *pAuthInfo; + DWORD dwCapabilities; + void *pv; + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + tid = start_host_object(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &thread); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy); + ok_ole_success(hr, "CoUnmarshalInterface"); + IStream_Release(pStream); + + hr = IUnknown_QueryInterface(pProxy, &IID_IUnknown, (LPVOID*)&pUnknown1); + ok_ole_success(hr, "IUnknown_QueryInterface IID_IUnknown"); + + hr = IUnknown_QueryInterface(pProxy, &IID_IRemUnknown, (LPVOID*)&pProxy2); + ok_ole_success(hr, "IUnknown_QueryInterface IID_IStream"); + + hr = IUnknown_QueryInterface(pProxy2, &IID_IUnknown, (LPVOID*)&pUnknown2); + ok_ole_success(hr, "IUnknown_QueryInterface IID_IUnknown"); + + ok(pUnknown1 == pUnknown2, "both proxy's IUnknowns should be the same - %p, %p\n", pUnknown1, pUnknown2); + + hr = IUnknown_QueryInterface(pProxy, &IID_IMarshal, (LPVOID*)&pMarshal); + ok_ole_success(hr, "IUnknown_QueryInterface IID_IMarshal"); + + hr = IUnknown_QueryInterface(pProxy, &IID_IClientSecurity, (LPVOID*)&pCliSec); + ok_ole_success(hr, "IUnknown_QueryInterface IID_IClientSecurity"); + + hr = IClientSecurity_QueryBlanket(pCliSec, (IUnknown *)pProxy, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + todo_wine ok_ole_success(hr, "IClientSecurity_QueryBlanket (all NULLs)"); + + hr = IClientSecurity_QueryBlanket(pCliSec, (IUnknown *)pMarshal, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + todo_wine ok(hr == E_NOINTERFACE, "IClientSecurity_QueryBlanket with local interface should have returned E_NOINTERFACE instead of 0x%08x\n", hr); + + hr = IClientSecurity_QueryBlanket(pCliSec, (IUnknown *)pProxy, &dwAuthnSvc, &dwAuthzSvc, &pServerPrincName, &dwAuthnLevel, &dwImpLevel, &pAuthInfo, &dwCapabilities); + todo_wine ok_ole_success(hr, "IClientSecurity_QueryBlanket"); + + hr = IClientSecurity_SetBlanket(pCliSec, (IUnknown *)pProxy, dwAuthnSvc, dwAuthzSvc, pServerPrincName, dwAuthnLevel, RPC_C_IMP_LEVEL_IMPERSONATE, pAuthInfo, dwCapabilities); + todo_wine ok_ole_success(hr, "IClientSecurity_SetBlanket"); + + hr = IClassFactory_CreateInstance(pProxy, NULL, &IID_IWineTest, &pv); + ok(hr == E_NOINTERFACE, "COM call should have succeeded instead of returning 0x%08x\n", hr); + + hr = IClientSecurity_SetBlanket(pCliSec, (IUnknown *)pMarshal, dwAuthnSvc, dwAuthzSvc, pServerPrincName, dwAuthnLevel, dwImpLevel, pAuthInfo, dwCapabilities); + todo_wine ok(hr == E_NOINTERFACE, "IClientSecurity_SetBlanket with local interface should have returned E_NOINTERFACE instead of 0x%08x\n", hr); + + hr = IClientSecurity_SetBlanket(pCliSec, (IUnknown *)pProxy, 0xdeadbeef, dwAuthzSvc, pServerPrincName, dwAuthnLevel, dwImpLevel, pAuthInfo, dwCapabilities); + todo_wine ok(hr == E_INVALIDARG, "IClientSecurity_SetBlanke with invalid dwAuthnSvc should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + CoTaskMemFree(pServerPrincName); + + hr = IClientSecurity_QueryBlanket(pCliSec, (IUnknown *)pUnknown1, &dwAuthnSvc, &dwAuthzSvc, &pServerPrincName, &dwAuthnLevel, &dwImpLevel, &pAuthInfo, &dwCapabilities); + todo_wine ok_ole_success(hr, "IClientSecurity_QueryBlanket(IUnknown)"); + + CoTaskMemFree(pServerPrincName); + + IClassFactory_Release(pProxy); + IUnknown_Release(pProxy2); + IUnknown_Release(pUnknown1); + IUnknown_Release(pUnknown2); + IMarshal_Release(pMarshal); + IClientSecurity_Release(pCliSec); + + end_host_object(tid, thread); +} + +static HANDLE heventShutdown; + +static void LockModuleOOP(void) +{ + InterlockedIncrement(&cLocks); /* for test purposes only */ + CoAddRefServerProcess(); +} + +static void UnlockModuleOOP(void) +{ + InterlockedDecrement(&cLocks); /* for test purposes only */ + if (!CoReleaseServerProcess()) + SetEvent(heventShutdown); +} + +static HWND hwnd_app; + +static HRESULT WINAPI TestOOP_IClassFactory_QueryInterface( + LPCLASSFACTORY iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IClassFactory)) + { + *ppvObj = (LPVOID)iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI TestOOP_IClassFactory_AddRef(LPCLASSFACTORY iface) +{ + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI TestOOP_IClassFactory_Release(LPCLASSFACTORY iface) +{ + return 1; /* non-heap-based object */ +} + +static HRESULT WINAPI TestOOP_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown)) + { + *ppvObj = iface; + return S_OK; + } + return CLASS_E_CLASSNOTAVAILABLE; +} + +static HRESULT WINAPI TestOOP_IClassFactory_LockServer( + LPCLASSFACTORY iface, + BOOL fLock) +{ + if (fLock) + LockModuleOOP(); + else + UnlockModuleOOP(); + return S_OK; +} + +static const IClassFactoryVtbl TestClassFactoryOOP_Vtbl = +{ + TestOOP_IClassFactory_QueryInterface, + TestOOP_IClassFactory_AddRef, + TestOOP_IClassFactory_Release, + TestOOP_IClassFactory_CreateInstance, + TestOOP_IClassFactory_LockServer +}; + +static IClassFactory TestOOP_ClassFactory = { &TestClassFactoryOOP_Vtbl }; + +static void test_register_local_server(void) +{ + DWORD cookie; + HRESULT hr; + HANDLE ready_event; + HANDLE quit_event; + DWORD wait; + + heventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL); + + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&TestOOP_ClassFactory, + CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, &cookie); + ok_ole_success(hr, CoRegisterClassObject); + + ready_event = CreateEvent(NULL, FALSE, FALSE, "Wine COM Test Ready Event"); + SetEvent(ready_event); + + quit_event = CreateEvent(NULL, FALSE, FALSE, "Wine COM Test Quit Event"); + + do + { + wait = MsgWaitForMultipleObjects(1, &quit_event, FALSE, INFINITE, QS_ALLINPUT); + if (wait == WAIT_OBJECT_0+1) + { + MSG msg; + BOOL ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); + if (ret) + { + trace("Message 0x%x\n", msg.message); + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + while (wait == WAIT_OBJECT_0+1); + + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, CoRevokeClassObject); +} + +static HANDLE create_target_process(const char *arg) +{ + char **argv; + char cmdline[MAX_PATH]; + PROCESS_INFORMATION pi; + STARTUPINFO si = { 0 }; + si.cb = sizeof(si); + + pi.hThread = NULL; + pi.hProcess = NULL; + winetest_get_mainargs( &argv ); + sprintf(cmdline, "%s %s %s", argv[0], argv[1], arg); + ok(CreateProcess(argv[0], cmdline, NULL, NULL, FALSE, 0, NULL, NULL, + &si, &pi) != 0, "CreateProcess failed with error: %u\n", GetLastError()); + if (pi.hThread) CloseHandle(pi.hThread); + return pi.hProcess; +} + +/* tests functions commonly used by out of process COM servers */ +static void test_local_server(void) +{ + DWORD cookie; + HRESULT hr; + IClassFactory * cf; + DWORD ret; + HANDLE process; + HANDLE quit_event; + HANDLE ready_event; + + heventShutdown = CreateEvent(NULL, TRUE, FALSE, NULL); + + cLocks = 0; + + /* Start the object suspended */ + hr = CoRegisterClassObject(&CLSID_WineOOPTest, (IUnknown *)&TestOOP_ClassFactory, + CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED, &cookie); + ok_ole_success(hr, CoRegisterClassObject); + + /* ... and CoGetClassObject does not find it and fails when it looks for the + * class in the registry */ + hr = CoGetClassObject(&CLSID_WineOOPTest, CLSCTX_INPROC_SERVER, + NULL, &IID_IClassFactory, (LPVOID*)&cf); + ok(hr == REGDB_E_CLASSNOTREG || /* NT */ + hr == S_OK /* Win9x */, + "CoGetClassObject should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + + /* Resume the object suspended above ... */ + hr = CoResumeClassObjects(); + ok_ole_success(hr, CoResumeClassObjects); + + /* ... and now it should succeed */ + hr = CoGetClassObject(&CLSID_WineOOPTest, CLSCTX_INPROC_SERVER, + NULL, &IID_IClassFactory, (LPVOID*)&cf); + ok_ole_success(hr, CoGetClassObject); + + /* Now check the locking is working */ + /* NOTE: we are accessing the class directly, not through a proxy */ + + ok_no_locks(); + + hr = IClassFactory_LockServer(cf, TRUE); + ok_ole_success(hr, IClassFactory_LockServer); + + ok_more_than_one_lock(); + + IClassFactory_LockServer(cf, FALSE); + ok_ole_success(hr, IClassFactory_LockServer); + + ok_no_locks(); + + IClassFactory_Release(cf); + + /* wait for shutdown signal */ + ret = WaitForSingleObject(heventShutdown, 0); + ok(ret != WAIT_TIMEOUT, "Server didn't shut down\n"); + + /* try to connect again after SCM has suspended registered class objects */ + hr = CoGetClassObject(&CLSID_WineOOPTest, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, NULL, + &IID_IClassFactory, (LPVOID*)&cf); + ok(hr == CO_E_SERVER_STOPPING || /* NT */ + hr == S_OK /* Win9x */, + "CoGetClassObject should have returned CO_E_SERVER_STOPPING instead of 0x%08x\n", hr); + + hr = CoRevokeClassObject(cookie); + ok_ole_success(hr, CoRevokeClassObject); + + CloseHandle(heventShutdown); + + process = create_target_process("-Embedding"); + ok(process != NULL, "couldn't start local server process, error was %d\n", GetLastError()); + + ready_event = CreateEvent(NULL, FALSE, FALSE, "Wine COM Test Ready Event"); + WaitForSingleObject(ready_event, INFINITE); + CloseHandle(ready_event); + + hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IClassFactory, (void **)&cf); + ok_ole_success(hr, CoCreateInstance); + + IClassFactory_Release(cf); + + hr = CoCreateInstance(&CLSID_WineOOPTest, NULL, CLSCTX_LOCAL_SERVER, &IID_IClassFactory, (void **)&cf); + ok(hr == REGDB_E_CLASSNOTREG, "Second CoCreateInstance on REGCLS_SINGLEUSE object should have failed\n"); + + quit_event = CreateEvent(NULL, FALSE, FALSE, "Wine COM Test Quit Event"); + SetEvent(quit_event); + + WaitForSingleObject(process, INFINITE); + CloseHandle(quit_event); + CloseHandle(process); +} + +struct git_params +{ + DWORD cookie; + IGlobalInterfaceTable *git; +}; + +static DWORD CALLBACK get_global_interface_proc(LPVOID pv) +{ + HRESULT hr; + struct git_params *params = (struct git_params *)pv; + IClassFactory *cf; + + hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(params->git, params->cookie, &IID_IClassFactory, (void **)&cf); + ok(hr == CO_E_NOTINITIALIZED, + "IGlobalInterfaceTable_GetInterfaceFromGlobal should have failed with error CO_E_NOTINITIALIZED instead of 0x%08x\n", + hr); + + CoInitialize(NULL); + + hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(params->git, params->cookie, &IID_IClassFactory, (void **)&cf); + ok_ole_success(hr, IGlobalInterfaceTable_GetInterfaceFromGlobal); + + IClassFactory_Release(cf); + + CoUninitialize(); + + return hr; +} + +static void test_globalinterfacetable(void) +{ + HRESULT hr; + IGlobalInterfaceTable *git; + DWORD cookie; + HANDLE thread; + DWORD tid; + struct git_params params; + DWORD ret; + IUnknown *object; + + trace("test_globalinterfacetable\n"); + cLocks = 0; + + hr = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, &IID_IGlobalInterfaceTable, (void **)&git); + ok_ole_success(hr, CoCreateInstance); + + hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&Test_ClassFactory, &IID_IClassFactory, &cookie); + ok_ole_success(hr, IGlobalInterfaceTable_RegisterInterfaceInGlobal); + + ok_more_than_one_lock(); + + params.cookie = cookie; + params.git = git; + /* note: params is on stack so we MUST wait for get_global_interface_proc + * to exit before we can return */ + thread = CreateThread(NULL, 0, get_global_interface_proc, ¶ms, 0, &tid); + + ret = MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT); + while (ret == WAIT_OBJECT_0 + 1) + { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + DispatchMessage(&msg); + ret = MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT); + } + + CloseHandle(thread); + + /* test getting interface from global with different iid */ + hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, cookie, &IID_IUnknown, (void **)&object); + ok_ole_success(hr, IGlobalInterfaceTable_GetInterfaceFromGlobal); + IUnknown_Release(object); + + /* test getting interface from global with same iid */ + hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, cookie, &IID_IClassFactory, (void **)&object); + ok_ole_success(hr, IGlobalInterfaceTable_GetInterfaceFromGlobal); + IUnknown_Release(object); + + hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, cookie); + ok_ole_success(hr, IGlobalInterfaceTable_RevokeInterfaceFromGlobal); + + ok_no_locks(); + + IGlobalInterfaceTable_Release(git); +} + +static const char *debugstr_iid(REFIID riid) +{ + static char name[256]; + HKEY hkeyInterface; + WCHAR bufferW[39]; + char buffer[39]; + LONG name_size = sizeof(name); + StringFromGUID2(riid, bufferW, sizeof(bufferW)/sizeof(bufferW[0])); + WideCharToMultiByte(CP_ACP, 0, bufferW, sizeof(bufferW)/sizeof(bufferW[0]), buffer, sizeof(buffer), NULL, NULL); + if (RegOpenKeyEx(HKEY_CLASSES_ROOT, "Interface", 0, KEY_QUERY_VALUE, &hkeyInterface) != ERROR_SUCCESS) + { + memcpy(name, buffer, sizeof(buffer)); + goto done; + } + if (RegQueryValue(hkeyInterface, buffer, name, &name_size) != ERROR_SUCCESS) + { + memcpy(name, buffer, sizeof(buffer)); + goto done; + } + RegCloseKey(hkeyInterface); +done: + return name; +} + +static HRESULT WINAPI TestChannelHook_QueryInterface(IChannelHook *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IChannelHook)) + { + *ppv = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI TestChannelHook_AddRef(IChannelHook *iface) +{ + return 2; +} + +static ULONG WINAPI TestChannelHook_Release(IChannelHook *iface) +{ + return 1; +} + +static void WINAPI TestChannelHook_ClientGetSize( + IChannelHook *iface, + REFGUID uExtent, + REFIID riid, + ULONG *pDataSize ) +{ + SChannelHookCallInfo *info = (SChannelHookCallInfo *)riid; + trace("TestChannelHook_ClientGetBuffer\n"); + trace("\t%s method %d\n", debugstr_iid(riid), info->iMethod); + trace("\tcid: %s\n", debugstr_iid(&info->uCausality)); + ok(info->cbSize == sizeof(*info), "info->cbSize was %d instead of %d\n", info->cbSize, (int)sizeof(*info)); + ok(info->dwServerPid == GetCurrentProcessId(), "info->dwServerPid was 0x%x instead of 0x%x\n", info->dwServerPid, GetCurrentProcessId()); + ok(!info->pObject, "info->pObject should be NULL\n"); + ok(IsEqualGUID(uExtent, &EXTENTID_WineTest), "uExtent wasn't correct\n"); + + *pDataSize = 1; +} + +static void WINAPI TestChannelHook_ClientFillBuffer( + IChannelHook *iface, + REFGUID uExtent, + REFIID riid, + ULONG *pDataSize, + void *pDataBuffer ) +{ + SChannelHookCallInfo *info = (SChannelHookCallInfo *)riid; + trace("TestChannelHook_ClientFillBuffer\n"); + ok(info->cbSize == sizeof(*info), "info->cbSize was %d instead of %d\n", info->cbSize, (int)sizeof(*info)); + ok(info->dwServerPid == GetCurrentProcessId(), "info->dwServerPid was 0x%x instead of 0x%x\n", info->dwServerPid, GetCurrentProcessId()); + ok(!info->pObject, "info->pObject should be NULL\n"); + ok(IsEqualGUID(uExtent, &EXTENTID_WineTest), "uExtent wasn't correct\n"); + + *(unsigned char *)pDataBuffer = 0xcc; + *pDataSize = 1; +} + +static void WINAPI TestChannelHook_ClientNotify( + IChannelHook *iface, + REFGUID uExtent, + REFIID riid, + ULONG cbDataSize, + void *pDataBuffer, + DWORD lDataRep, + HRESULT hrFault ) +{ + SChannelHookCallInfo *info = (SChannelHookCallInfo *)riid; + trace("TestChannelHook_ClientNotify hrFault = 0x%08x\n", hrFault); + ok(info->cbSize == sizeof(*info), "info->cbSize was %d instead of %d\n", info->cbSize, (int)sizeof(*info)); + ok(info->dwServerPid == GetCurrentProcessId(), "info->dwServerPid was 0x%x instead of 0x%x\n", info->dwServerPid, GetCurrentProcessId()); + todo_wine { + ok(info->pObject != NULL, "info->pObject shouldn't be NULL\n"); + } + ok(IsEqualGUID(uExtent, &EXTENTID_WineTest), "uExtent wasn't correct\n"); +} + +static void WINAPI TestChannelHook_ServerNotify( + IChannelHook *iface, + REFGUID uExtent, + REFIID riid, + ULONG cbDataSize, + void *pDataBuffer, + DWORD lDataRep ) +{ + SChannelHookCallInfo *info = (SChannelHookCallInfo *)riid; + trace("TestChannelHook_ServerNotify\n"); + ok(info->cbSize == sizeof(*info), "info->cbSize was %d instead of %d\n", info->cbSize, (int)sizeof(*info)); + ok(info->dwServerPid == GetCurrentProcessId(), "info->dwServerPid was 0x%x instead of 0x%x\n", info->dwServerPid, GetCurrentProcessId()); + ok(info->pObject != NULL, "info->pObject shouldn't be NULL\n"); + ok(cbDataSize == 1, "cbDataSize should have been 1 instead of %d\n", cbDataSize); + ok(*(unsigned char *)pDataBuffer == 0xcc, "pDataBuffer should have contained 0xcc instead of 0x%x\n", *(unsigned char *)pDataBuffer); + ok(IsEqualGUID(uExtent, &EXTENTID_WineTest), "uExtent wasn't correct\n"); +} + +static void WINAPI TestChannelHook_ServerGetSize( + IChannelHook *iface, + REFGUID uExtent, + REFIID riid, + HRESULT hrFault, + ULONG *pDataSize ) +{ + SChannelHookCallInfo *info = (SChannelHookCallInfo *)riid; + trace("TestChannelHook_ServerGetSize\n"); + trace("\t%s method %d\n", debugstr_iid(riid), info->iMethod); + ok(info->cbSize == sizeof(*info), "info->cbSize was %d instead of %d\n", info->cbSize, (int)sizeof(*info)); + ok(info->dwServerPid == GetCurrentProcessId(), "info->dwServerPid was 0x%x instead of 0x%x\n", info->dwServerPid, GetCurrentProcessId()); + ok(info->pObject != NULL, "info->pObject shouldn't be NULL\n"); + ok(IsEqualGUID(uExtent, &EXTENTID_WineTest), "uExtent wasn't correct\n"); + if (hrFault != S_OK) + trace("\thrFault = 0x%08x\n", hrFault); + + *pDataSize = 0; +} + +static void WINAPI TestChannelHook_ServerFillBuffer( + IChannelHook *iface, + REFGUID uExtent, + REFIID riid, + ULONG *pDataSize, + void *pDataBuffer, + HRESULT hrFault ) +{ + trace("TestChannelHook_ServerFillBuffer\n"); + ok(0, "TestChannelHook_ServerFillBuffer shouldn't be called\n"); +} + +static const IChannelHookVtbl TestChannelHookVtbl = +{ + TestChannelHook_QueryInterface, + TestChannelHook_AddRef, + TestChannelHook_Release, + TestChannelHook_ClientGetSize, + TestChannelHook_ClientFillBuffer, + TestChannelHook_ClientNotify, + TestChannelHook_ServerNotify, + TestChannelHook_ServerGetSize, + TestChannelHook_ServerFillBuffer, +}; + +static IChannelHook TestChannelHook = { &TestChannelHookVtbl }; + +static void test_channel_hook(void) +{ + IStream *pStream = NULL; + IClassFactory *cf = NULL; + DWORD tid; + IUnknown *proxy = NULL; + HANDLE thread; + HRESULT hr; + + hr = CoRegisterChannelHook(&EXTENTID_WineTest, &TestChannelHook); + ok_ole_success(hr, CoRegisterChannelHook); + + hr = CoRegisterMessageFilter(&MessageFilter, NULL); + ok_ole_success(hr, CoRegisterMessageFilter); + + cLocks = 0; + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object2(pStream, &IID_IClassFactory, (IUnknown*)&Test_ClassFactory, MSHLFLAGS_NORMAL, &MessageFilter, &thread); + + ok_more_than_one_lock(); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&cf); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + ok_more_than_one_lock(); + + hr = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (LPVOID*)&proxy); + ok_ole_success(hr, IClassFactory_CreateInstance); + IUnknown_Release(proxy); + + IClassFactory_Release(cf); + + ok_no_locks(); + + end_host_object(tid, thread); + + hr = CoRegisterMessageFilter(NULL, NULL); + ok_ole_success(hr, CoRegisterMessageFilter); +} + +START_TEST(marshal) +{ + WNDCLASS wndclass; + HMODULE hOle32 = GetModuleHandle("ole32"); + int argc; + char **argv; + + if (!(pCoInitializeEx = (void*)GetProcAddress(hOle32, "CoInitializeEx"))) goto no_test; + + argc = winetest_get_mainargs( &argv ); + if (argc > 2 && (!strcmp(argv[2], "-Embedding"))) + { + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + test_register_local_server(); + CoUninitialize(); + + return; + } + + /* register a window class used in several tests */ + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.lpfnWndProc = window_proc; + wndclass.lpszClassName = "WineCOMTest"; + RegisterClass(&wndclass); + + test_cocreateinstance_proxy(); + + pCoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + /* FIXME: test CoCreateInstanceEx */ + + /* lifecycle management and marshaling tests */ + test_no_marshaler(); + test_normal_marshal_and_release(); + test_normal_marshal_and_unmarshal(); + test_marshal_and_unmarshal_invalid(); + test_same_apartment_unmarshal_failure(); + test_interthread_marshal_and_unmarshal(); + test_proxy_marshal_and_unmarshal(); + test_proxy_marshal_and_unmarshal2(); + test_proxy_marshal_and_unmarshal_weak(); + test_proxy_marshal_and_unmarshal_strong(); + test_marshal_stub_apartment_shutdown(); + test_marshal_proxy_apartment_shutdown(); + test_marshal_proxy_mta_apartment_shutdown(); + test_no_couninitialize_server(); + test_no_couninitialize_client(); + test_tableweak_marshal_and_unmarshal_twice(); + test_tableweak_marshal_releasedata1(); + test_tableweak_marshal_releasedata2(); + test_tablestrong_marshal_and_unmarshal_twice(); + test_lock_object_external(); + test_disconnect_stub(); + test_normal_marshal_and_unmarshal_twice(); + test_hresult_marshaling(); + test_proxy_used_in_wrong_thread(); + test_message_filter(); + test_bad_marshal_stream(); + test_proxy_interfaces(); + test_stubbuffer(&IID_IClassFactory); + test_proxybuffer(&IID_IClassFactory); + test_message_reentrancy(); + test_call_from_message(); + test_WM_QUIT_handling(); + test_freethreadedmarshaler(); + test_inproc_handler(); + test_handler_marshaling(); + test_client_security(); + + test_local_server(); + + test_globalinterfacetable(); + + /* must be last test as channel hooks can't be unregistered */ + test_channel_hook(); + + CoUninitialize(); + return; + +no_test: + trace("You need DCOM95 installed to run this test\n"); + return; +} diff --git a/rostests/winetests/ole32/moniker.c b/rostests/winetests/ole32/moniker.c new file mode 100644 index 00000000000..a5a1e8fcae8 --- /dev/null +++ b/rostests/winetests/ole32/moniker.c @@ -0,0 +1,1709 @@ +/* + * Moniker Tests + * + * Copyright 2004 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define _WIN32_DCOM +#define COBJMACROS +#define CONST_VTABLE + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "comcat.h" +#include "olectl.h" + +#include "wine/test.h" + +#define ok_more_than_one_lock() ok(cLocks > 0, "Number of locks should be > 0, but actually is %d\n", cLocks) +#define ok_no_locks() ok(cLocks == 0, "Number of locks should be 0, but actually is %d\n", cLocks) +#define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08x\n", hr) +#define COUNTOF(x) (sizeof(x) / sizeof(x[0])) + +#define CHECK_EXPECTED_METHOD(method_name) \ +do { \ + trace("%s\n", method_name); \ + ok(*expected_method_list != NULL, "Extra method %s called\n", method_name); \ + if (*expected_method_list) \ + { \ + ok(!strcmp(*expected_method_list, method_name), "Expected %s to be called instead of %s\n", \ + *expected_method_list, method_name); \ + expected_method_list++; \ + } \ +} while(0) + +static char const * const *expected_method_list; +static const WCHAR wszFileName1[] = {'c',':','\\','w','i','n','d','o','w','s','\\','t','e','s','t','1','.','d','o','c',0}; +static const WCHAR wszFileName2[] = {'c',':','\\','w','i','n','d','o','w','s','\\','t','e','s','t','2','.','d','o','c',0}; + +static const CLSID CLSID_WineTest = +{ /* 9474ba1a-258b-490b-bc13-516e9239ace0 */ + 0x9474ba1a, + 0x258b, + 0x490b, + {0xbc, 0x13, 0x51, 0x6e, 0x92, 0x39, 0xac, 0xe0} +}; + +static const CLSID CLSID_TestMoniker = +{ /* b306bfbc-496e-4f53-b93e-2ff9c83223d7 */ + 0xb306bfbc, + 0x496e, + 0x4f53, + {0xb9, 0x3e, 0x2f, 0xf9, 0xc8, 0x32, 0x23, 0xd7} +}; + +static LONG cLocks; + +static void LockModule(void) +{ + InterlockedIncrement(&cLocks); +} + +static void UnlockModule(void) +{ + InterlockedDecrement(&cLocks); +} + +static HRESULT WINAPI Test_IClassFactory_QueryInterface( + LPCLASSFACTORY iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IClassFactory)) + { + *ppvObj = (LPVOID)iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_IClassFactory_AddRef(LPCLASSFACTORY iface) +{ + LockModule(); + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_IClassFactory_Release(LPCLASSFACTORY iface) +{ + UnlockModule(); + return 1; /* non-heap-based object */ +} + +static HRESULT WINAPI Test_IClassFactory_CreateInstance( + LPCLASSFACTORY iface, + LPUNKNOWN pUnkOuter, + REFIID riid, + LPVOID *ppvObj) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI Test_IClassFactory_LockServer( + LPCLASSFACTORY iface, + BOOL fLock) +{ + return S_OK; +} + +static const IClassFactoryVtbl TestClassFactory_Vtbl = +{ + Test_IClassFactory_QueryInterface, + Test_IClassFactory_AddRef, + Test_IClassFactory_Release, + Test_IClassFactory_CreateInstance, + Test_IClassFactory_LockServer +}; + +static IClassFactory Test_ClassFactory = { &TestClassFactory_Vtbl }; + +typedef struct +{ + const IUnknownVtbl *lpVtbl; + ULONG refs; +} HeapUnknown; + +static HRESULT WINAPI HeapUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + IUnknown_AddRef(iface); + *ppv = (LPVOID)iface; + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI HeapUnknown_AddRef(IUnknown *iface) +{ + HeapUnknown *This = (HeapUnknown *)iface; + return InterlockedIncrement((LONG*)&This->refs); +} + +static ULONG WINAPI HeapUnknown_Release(IUnknown *iface) +{ + HeapUnknown *This = (HeapUnknown *)iface; + ULONG refs = InterlockedDecrement((LONG*)&This->refs); + if (!refs) HeapFree(GetProcessHeap(), 0, This); + return refs; +} + +static const IUnknownVtbl HeapUnknown_Vtbl = +{ + HeapUnknown_QueryInterface, + HeapUnknown_AddRef, + HeapUnknown_Release +}; + +static HRESULT WINAPI +MonikerNoROTData_QueryInterface(IMoniker* iface,REFIID riid,void** ppvObject) +{ + if (!ppvObject) + return E_INVALIDARG; + + *ppvObject = 0; + + if (IsEqualIID(&IID_IUnknown, riid) || + IsEqualIID(&IID_IPersist, riid) || + IsEqualIID(&IID_IPersistStream,riid) || + IsEqualIID(&IID_IMoniker, riid)) + *ppvObject = iface; + if (IsEqualIID(&IID_IROTData, riid)) + CHECK_EXPECTED_METHOD("Moniker_QueryInterface(IID_IROTData)"); + + if ((*ppvObject)==0) + return E_NOINTERFACE; + + IMoniker_AddRef(iface); + + return S_OK; +} + +static ULONG WINAPI +Moniker_AddRef(IMoniker* iface) +{ + return 2; +} + +static ULONG WINAPI +Moniker_Release(IMoniker* iface) +{ + return 1; +} + +static HRESULT WINAPI +Moniker_GetClassID(IMoniker* iface, CLSID *pClassID) +{ + CHECK_EXPECTED_METHOD("Moniker_GetClassID"); + + *pClassID = CLSID_TestMoniker; + + return S_OK; +} + +static HRESULT WINAPI +Moniker_IsDirty(IMoniker* iface) +{ + CHECK_EXPECTED_METHOD("Moniker_IsDirty"); + + return S_FALSE; +} + +static HRESULT WINAPI +Moniker_Load(IMoniker* iface, IStream* pStm) +{ + CHECK_EXPECTED_METHOD("Moniker_Load"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_Save(IMoniker* iface, IStream* pStm, BOOL fClearDirty) +{ + CHECK_EXPECTED_METHOD("Moniker_Save"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_GetSizeMax(IMoniker* iface, ULARGE_INTEGER* pcbSize) +{ + CHECK_EXPECTED_METHOD("Moniker_GetSizeMax"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_BindToObject(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, + REFIID riid, VOID** ppvResult) +{ + CHECK_EXPECTED_METHOD("Moniker_BindToObject"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_BindToStorage(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, + REFIID riid, VOID** ppvObject) +{ + CHECK_EXPECTED_METHOD("Moniker_BindToStorage"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_Reduce(IMoniker* iface, IBindCtx* pbc, DWORD dwReduceHowFar, + IMoniker** ppmkToLeft, IMoniker** ppmkReduced) +{ + CHECK_EXPECTED_METHOD("Moniker_Reduce"); + + if (ppmkReduced==NULL) + return E_POINTER; + + IMoniker_AddRef(iface); + + *ppmkReduced=iface; + + return MK_S_REDUCED_TO_SELF; +} + +static HRESULT WINAPI +Moniker_ComposeWith(IMoniker* iface, IMoniker* pmkRight, + BOOL fOnlyIfNotGeneric, IMoniker** ppmkComposite) +{ + CHECK_EXPECTED_METHOD("Moniker_ComposeWith"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_Enum(IMoniker* iface,BOOL fForward, IEnumMoniker** ppenumMoniker) +{ + CHECK_EXPECTED_METHOD("Moniker_Enum"); + + if (ppenumMoniker == NULL) + return E_POINTER; + + *ppenumMoniker = NULL; + + return S_OK; +} + +static HRESULT WINAPI +Moniker_IsEqual(IMoniker* iface,IMoniker* pmkOtherMoniker) +{ + CHECK_EXPECTED_METHOD("Moniker_IsEqual"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_Hash(IMoniker* iface,DWORD* pdwHash) +{ + CHECK_EXPECTED_METHOD("Moniker_Hash"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_IsRunning(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, + IMoniker* pmkNewlyRunning) +{ + CHECK_EXPECTED_METHOD("Moniker_IsRunning"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_GetTimeOfLastChange(IMoniker* iface, IBindCtx* pbc, + IMoniker* pmkToLeft, FILETIME* pFileTime) +{ + CHECK_EXPECTED_METHOD("Moniker_GetTimeOfLastChange"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_Inverse(IMoniker* iface,IMoniker** ppmk) +{ + CHECK_EXPECTED_METHOD("Moniker_Inverse"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_CommonPrefixWith(IMoniker* iface,IMoniker* pmkOther,IMoniker** ppmkPrefix) +{ + CHECK_EXPECTED_METHOD("Moniker_CommonPrefixWith"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_RelativePathTo(IMoniker* iface,IMoniker* pmOther, IMoniker** ppmkRelPath) +{ + CHECK_EXPECTED_METHOD("Moniker_RelativePathTo"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_GetDisplayName(IMoniker* iface, IBindCtx* pbc, + IMoniker* pmkToLeft, LPOLESTR *ppszDisplayName) +{ + static const WCHAR wszDisplayName[] = {'*','*','G','e','m','m','a',0}; + CHECK_EXPECTED_METHOD("Moniker_GetDisplayName"); + *ppszDisplayName = (LPOLESTR)CoTaskMemAlloc(sizeof(wszDisplayName)); + memcpy(*ppszDisplayName, wszDisplayName, sizeof(wszDisplayName)); + return S_OK; +} + +static HRESULT WINAPI +Moniker_ParseDisplayName(IMoniker* iface, IBindCtx* pbc, IMoniker* pmkToLeft, + LPOLESTR pszDisplayName, ULONG* pchEaten, IMoniker** ppmkOut) +{ + CHECK_EXPECTED_METHOD("Moniker_ParseDisplayName"); + return E_NOTIMPL; +} + +static HRESULT WINAPI +Moniker_IsSystemMoniker(IMoniker* iface,DWORD* pwdMksys) +{ + CHECK_EXPECTED_METHOD("Moniker_IsSystemMoniker"); + + if (!pwdMksys) + return E_POINTER; + + (*pwdMksys)=MKSYS_NONE; + + return S_FALSE; +} + +static const IMonikerVtbl MonikerNoROTDataVtbl = +{ + MonikerNoROTData_QueryInterface, + Moniker_AddRef, + Moniker_Release, + Moniker_GetClassID, + Moniker_IsDirty, + Moniker_Load, + Moniker_Save, + Moniker_GetSizeMax, + Moniker_BindToObject, + Moniker_BindToStorage, + Moniker_Reduce, + Moniker_ComposeWith, + Moniker_Enum, + Moniker_IsEqual, + Moniker_Hash, + Moniker_IsRunning, + Moniker_GetTimeOfLastChange, + Moniker_Inverse, + Moniker_CommonPrefixWith, + Moniker_RelativePathTo, + Moniker_GetDisplayName, + Moniker_ParseDisplayName, + Moniker_IsSystemMoniker +}; + +static IMoniker MonikerNoROTData = { &MonikerNoROTDataVtbl }; + +static IMoniker Moniker; + +static HRESULT WINAPI +ROTData_QueryInterface(IROTData *iface,REFIID riid,VOID** ppvObject) +{ + return IMoniker_QueryInterface(&Moniker, riid, ppvObject); +} + +static ULONG WINAPI +ROTData_AddRef(IROTData *iface) +{ + return 2; +} + +static ULONG WINAPI +ROTData_Release(IROTData* iface) +{ + return 1; +} + +static HRESULT WINAPI +ROTData_GetComparisonData(IROTData* iface, BYTE* pbData, + ULONG cbMax, ULONG* pcbData) +{ + CHECK_EXPECTED_METHOD("ROTData_GetComparisonData"); + + *pcbData = 1; + if (cbMax < *pcbData) + return E_OUTOFMEMORY; + + *pbData = 0xde; + + return S_OK; +} + +static IROTDataVtbl ROTDataVtbl = +{ + ROTData_QueryInterface, + ROTData_AddRef, + ROTData_Release, + ROTData_GetComparisonData +}; + +static IROTData ROTData = { &ROTDataVtbl }; + +static HRESULT WINAPI +Moniker_QueryInterface(IMoniker* iface,REFIID riid,void** ppvObject) +{ + if (!ppvObject) + return E_INVALIDARG; + + *ppvObject = 0; + + if (IsEqualIID(&IID_IUnknown, riid) || + IsEqualIID(&IID_IPersist, riid) || + IsEqualIID(&IID_IPersistStream,riid) || + IsEqualIID(&IID_IMoniker, riid)) + *ppvObject = iface; + if (IsEqualIID(&IID_IROTData, riid)) + { + CHECK_EXPECTED_METHOD("Moniker_QueryInterface(IID_IROTData)"); + *ppvObject = &ROTData; + } + + if ((*ppvObject)==0) + return E_NOINTERFACE; + + IMoniker_AddRef(iface); + + return S_OK; +} + +static const IMonikerVtbl MonikerVtbl = +{ + Moniker_QueryInterface, + Moniker_AddRef, + Moniker_Release, + Moniker_GetClassID, + Moniker_IsDirty, + Moniker_Load, + Moniker_Save, + Moniker_GetSizeMax, + Moniker_BindToObject, + Moniker_BindToStorage, + Moniker_Reduce, + Moniker_ComposeWith, + Moniker_Enum, + Moniker_IsEqual, + Moniker_Hash, + Moniker_IsRunning, + Moniker_GetTimeOfLastChange, + Moniker_Inverse, + Moniker_CommonPrefixWith, + Moniker_RelativePathTo, + Moniker_GetDisplayName, + Moniker_ParseDisplayName, + Moniker_IsSystemMoniker +}; + +static IMoniker Moniker = { &MonikerVtbl }; + +static void test_ROT(void) +{ + static const WCHAR wszFileName[] = {'B','E','2','0','E','2','F','5','-', + '1','9','0','3','-','4','A','A','E','-','B','1','A','F','-', + '2','0','4','6','E','5','8','6','C','9','2','5',0}; + HRESULT hr; + IMoniker *pMoniker = NULL; + IRunningObjectTable *pROT = NULL; + DWORD dwCookie; + static const char *methods_register_no_ROTData[] = + { + "Moniker_Reduce", + "Moniker_GetTimeOfLastChange", + "Moniker_QueryInterface(IID_IROTData)", + "Moniker_GetDisplayName", + "Moniker_GetClassID", + NULL + }; + static const char *methods_register[] = + { + "Moniker_Reduce", + "Moniker_GetTimeOfLastChange", + "Moniker_QueryInterface(IID_IROTData)", + "ROTData_GetComparisonData", + NULL + }; + static const char *methods_isrunning_no_ROTData[] = + { + "Moniker_Reduce", + "Moniker_QueryInterface(IID_IROTData)", + "Moniker_GetDisplayName", + "Moniker_GetClassID", + NULL + }; + static const char *methods_isrunning[] = + { + "Moniker_Reduce", + "Moniker_QueryInterface(IID_IROTData)", + "ROTData_GetComparisonData", + NULL + }; + + cLocks = 0; + + hr = GetRunningObjectTable(0, &pROT); + ok_ole_success(hr, GetRunningObjectTable); + + expected_method_list = methods_register_no_ROTData; + /* try with our own moniker that doesn't support IROTData */ + hr = IRunningObjectTable_Register(pROT, ROTFLAGS_REGISTRATIONKEEPSALIVE, + (IUnknown*)&Test_ClassFactory, &MonikerNoROTData, &dwCookie); + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_ole_success(hr, IRunningObjectTable_Register); + } + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_more_than_one_lock(); + } + + expected_method_list = methods_isrunning_no_ROTData; + hr = IRunningObjectTable_IsRunning(pROT, &MonikerNoROTData); + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_ole_success(hr, IRunningObjectTable_IsRunning); + } + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + hr = IRunningObjectTable_Revoke(pROT, dwCookie); + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_ole_success(hr, IRunningObjectTable_Revoke); + } + + ok_no_locks(); + + expected_method_list = methods_register; + /* try with our own moniker */ + hr = IRunningObjectTable_Register(pROT, ROTFLAGS_REGISTRATIONKEEPSALIVE, + (IUnknown*)&Test_ClassFactory, &Moniker, &dwCookie); + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_ole_success(hr, IRunningObjectTable_Register); + } + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_more_than_one_lock(); + } + + expected_method_list = methods_isrunning; + hr = IRunningObjectTable_IsRunning(pROT, &Moniker); + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_ole_success(hr, IRunningObjectTable_IsRunning); + } + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + hr = IRunningObjectTable_Revoke(pROT, dwCookie); + todo_wine { /* only fails because of lack of IMoniker marshaling */ + ok_ole_success(hr, IRunningObjectTable_Revoke); + } + + ok_no_locks(); + + hr = CreateFileMoniker(wszFileName, &pMoniker); + ok_ole_success(hr, CreateClassMoniker); + + /* test flags: 0 */ + hr = IRunningObjectTable_Register(pROT, 0, (IUnknown*)&Test_ClassFactory, + pMoniker, &dwCookie); + ok_ole_success(hr, IRunningObjectTable_Register); + + ok_more_than_one_lock(); + + hr = IRunningObjectTable_Revoke(pROT, dwCookie); + ok_ole_success(hr, IRunningObjectTable_Revoke); + + ok_no_locks(); + + /* test flags: ROTFLAGS_REGISTRATIONKEEPSALIVE */ + hr = IRunningObjectTable_Register(pROT, ROTFLAGS_REGISTRATIONKEEPSALIVE, + (IUnknown*)&Test_ClassFactory, pMoniker, &dwCookie); + ok_ole_success(hr, IRunningObjectTable_Register); + + ok_more_than_one_lock(); + + hr = IRunningObjectTable_Revoke(pROT, dwCookie); + ok_ole_success(hr, IRunningObjectTable_Revoke); + + ok_no_locks(); + + /* test flags: ROTFLAGS_REGISTRATIONKEEPSALIVE|ROTFLAGS_ALLOWANYCLIENT */ + /* only succeeds when process is started by SCM and has LocalService + * or RunAs AppId values */ + hr = IRunningObjectTable_Register(pROT, + ROTFLAGS_REGISTRATIONKEEPSALIVE|ROTFLAGS_ALLOWANYCLIENT, + (IUnknown*)&Test_ClassFactory, pMoniker, &dwCookie); + todo_wine { + ok(hr == CO_E_WRONG_SERVER_IDENTITY, "IRunningObjectTable_Register should have returned CO_E_WRONG_SERVER_IDENTITY instead of 0x%08x\n", hr); + } + + hr = IRunningObjectTable_Register(pROT, 0xdeadbeef, + (IUnknown*)&Test_ClassFactory, pMoniker, &dwCookie); + ok(hr == E_INVALIDARG, "IRunningObjectTable_Register should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + IMoniker_Release(pMoniker); + + IRunningObjectTable_Release(pROT); +} + +static HRESULT WINAPI ParseDisplayName_QueryInterface(IParseDisplayName *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IParseDisplayName)) + { + *ppv = iface; + IUnknown_AddRef(iface); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI ParseDisplayName_AddRef(IParseDisplayName *iface) +{ + return 2; +} + +static ULONG WINAPI ParseDisplayName_Release(IParseDisplayName *iface) +{ + return 1; +} + +static LPCWSTR expected_display_name; + +static HRESULT WINAPI ParseDisplayName_ParseDisplayName(IParseDisplayName *iface, + IBindCtx *pbc, + LPOLESTR pszDisplayName, + ULONG *pchEaten, + IMoniker **ppmkOut) +{ + char display_nameA[256]; + WideCharToMultiByte(CP_ACP, 0, pszDisplayName, -1, display_nameA, sizeof(display_nameA), NULL, NULL); + ok(!lstrcmpW(pszDisplayName, expected_display_name), "unexpected display name \"%s\"\n", display_nameA); + ok(pszDisplayName == expected_display_name, "pszDisplayName should be the same pointer as passed into MkParseDisplayName\n"); + *pchEaten = lstrlenW(pszDisplayName); + return CreateAntiMoniker(ppmkOut); +} + +static const IParseDisplayNameVtbl ParseDisplayName_Vtbl = +{ + ParseDisplayName_QueryInterface, + ParseDisplayName_AddRef, + ParseDisplayName_Release, + ParseDisplayName_ParseDisplayName +}; + +static IParseDisplayName ParseDisplayName = { &ParseDisplayName_Vtbl }; + +static int count_moniker_matches(IBindCtx * pbc, IEnumMoniker * spEM) +{ + IMoniker * spMoniker; + int monCnt=0, matchCnt=0; + + while ((IEnumMoniker_Next(spEM, 1, &spMoniker, NULL)==S_OK)) + { + HRESULT hr; + WCHAR * szDisplayn; + monCnt++; + hr=IMoniker_GetDisplayName(spMoniker, pbc, NULL, &szDisplayn); + if (SUCCEEDED(hr)) + { + if (!lstrcmpiW(szDisplayn, wszFileName1) || !lstrcmpiW(szDisplayn, wszFileName2)) + matchCnt++; + CoTaskMemFree(szDisplayn); + } + } + trace("Total number of monikers is %i\n", monCnt); + return matchCnt; +} + +static void test_MkParseDisplayName(void) +{ + IBindCtx * pbc = NULL; + HRESULT hr; + IMoniker * pmk = NULL; + IMoniker * pmk1 = NULL; + IMoniker * pmk2 = NULL; + ULONG eaten; + int matchCnt; + IUnknown * object = NULL; + + IUnknown *lpEM1; + + IEnumMoniker *spEM1 = NULL; + IEnumMoniker *spEM2 = NULL; + IEnumMoniker *spEM3 = NULL; + + DWORD pdwReg1=0; + DWORD grflags=0; + DWORD pdwReg2=0; + DWORD moniker_type; + IRunningObjectTable * pprot=NULL; + + /* CLSID of My Computer */ + static const WCHAR wszDisplayName[] = {'c','l','s','i','d',':', + '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}; + static const WCHAR wszDisplayNameClsid[] = {'c','l','s','i','d',':',0}; + static const WCHAR wszNonExistentProgId[] = {'N','o','n','E','x','i','s','t','e','n','t','P','r','o','g','I','d',':',0}; + static const WCHAR wszDisplayNameRunning[] = {'W','i','n','e','T','e','s','t','R','u','n','n','i','n','g',0}; + static const WCHAR wszDisplayNameProgId1[] = {'S','t','d','F','o','n','t',':',0}; + static const WCHAR wszDisplayNameProgId2[] = {'@','S','t','d','F','o','n','t',0}; + static const WCHAR wszDisplayNameProgIdFail[] = {'S','t','d','F','o','n','t',0}; + char szDisplayNameFile[256]; + WCHAR wszDisplayNameFile[256]; + + hr = CreateBindCtx(0, &pbc); + ok_ole_success(hr, CreateBindCtx); + + hr = MkParseDisplayName(pbc, wszNonExistentProgId, &eaten, &pmk); + ok(hr == MK_E_SYNTAX || hr == MK_E_CANTOPENFILE /* Win9x */, + "MkParseDisplayName should have failed with MK_E_SYNTAX or MK_E_CANTOPENFILE instead of 0x%08x\n", hr); + + /* no special handling of "clsid:" without the string form of the clsid + * following */ + hr = MkParseDisplayName(pbc, wszDisplayNameClsid, &eaten, &pmk); + ok(hr == MK_E_SYNTAX || hr == MK_E_CANTOPENFILE /* Win9x */, + "MkParseDisplayName should have failed with MK_E_SYNTAX or MK_E_CANTOPENFILE instead of 0x%08x\n", hr); + + /* shows clsid has higher precedence than a running object */ + hr = CreateFileMoniker(wszDisplayName, &pmk); + ok_ole_success(hr, CreateFileMoniker); + hr = IBindCtx_GetRunningObjectTable(pbc, &pprot); + ok_ole_success(hr, IBindCtx_GetRunningObjectTable); + hr = IRunningObjectTable_Register(pprot, 0, (IUnknown *)&Test_ClassFactory, pmk, &pdwReg1); + ok_ole_success(hr, IRunningObjectTable_Register); + IMoniker_Release(pmk); + pmk = NULL; + hr = MkParseDisplayName(pbc, wszDisplayName, &eaten, &pmk); + ok_ole_success(hr, MkParseDisplayName); + if (pmk) + { + IMoniker_IsSystemMoniker(pmk, &moniker_type); + ok(moniker_type == MKSYS_CLASSMONIKER, "moniker_type was %d instead of MKSYS_CLASSMONIKER\n", moniker_type); + IMoniker_Release(pmk); + } + hr = IRunningObjectTable_Revoke(pprot, pdwReg1); + ok_ole_success(hr, IRunningObjectTable_Revoke); + IRunningObjectTable_Release(pprot); + + hr = CreateFileMoniker(wszDisplayNameRunning, &pmk); + ok_ole_success(hr, CreateFileMoniker); + hr = IBindCtx_GetRunningObjectTable(pbc, &pprot); + ok_ole_success(hr, IBindCtx_GetRunningObjectTable); + hr = IRunningObjectTable_Register(pprot, 0, (IUnknown *)&Test_ClassFactory, pmk, &pdwReg1); + ok_ole_success(hr, IRunningObjectTable_Register); + IMoniker_Release(pmk); + pmk = NULL; + hr = MkParseDisplayName(pbc, wszDisplayNameRunning, &eaten, &pmk); + ok_ole_success(hr, MkParseDisplayName); + if (pmk) + { + IMoniker_IsSystemMoniker(pmk, &moniker_type); + ok(moniker_type == MKSYS_FILEMONIKER, "moniker_type was %d instead of MKSYS_FILEMONIKER\n", moniker_type); + IMoniker_Release(pmk); + } + hr = IRunningObjectTable_Revoke(pprot, pdwReg1); + ok_ole_success(hr, IRunningObjectTable_Revoke); + IRunningObjectTable_Release(pprot); + + hr = CoRegisterClassObject(&CLSID_StdFont, (IUnknown *)&ParseDisplayName, CLSCTX_INPROC_SERVER, REGCLS_MULTI_SEPARATE, &pdwReg1); + ok_ole_success(hr, CoRegisterClassObject); + + expected_display_name = wszDisplayNameProgId1; + hr = MkParseDisplayName(pbc, wszDisplayNameProgId1, &eaten, &pmk); + ok_ole_success(hr, MkParseDisplayName); + if (pmk) + { + IMoniker_IsSystemMoniker(pmk, &moniker_type); + ok(moniker_type == MKSYS_ANTIMONIKER, "moniker_type was %d instead of MKSYS_ANTIMONIKER\n", moniker_type); + IMoniker_Release(pmk); + } + + expected_display_name = wszDisplayNameProgId2; + hr = MkParseDisplayName(pbc, wszDisplayNameProgId2, &eaten, &pmk); + ok_ole_success(hr, MkParseDisplayName); + if (pmk) + { + IMoniker_IsSystemMoniker(pmk, &moniker_type); + ok(moniker_type == MKSYS_ANTIMONIKER, "moniker_type was %d instead of MKSYS_ANTIMONIKER\n", moniker_type); + IMoniker_Release(pmk); + } + + hr = MkParseDisplayName(pbc, wszDisplayNameProgIdFail, &eaten, &pmk); + ok(hr == MK_E_SYNTAX || hr == MK_E_CANTOPENFILE /* Win9x */, + "MkParseDisplayName with ProgId without marker should fail with MK_E_SYNTAX or MK_E_CANTOPENFILE instead of 0x%08x\n", hr); + + hr = CoRevokeClassObject(pdwReg1); + ok_ole_success(hr, CoRevokeClassObject); + + GetSystemDirectoryA(szDisplayNameFile, sizeof(szDisplayNameFile)); + strcat(szDisplayNameFile, "\\kernel32.dll"); + MultiByteToWideChar(CP_ACP, 0, szDisplayNameFile, -1, wszDisplayNameFile, sizeof(wszDisplayNameFile)/sizeof(wszDisplayNameFile[0])); + hr = MkParseDisplayName(pbc, wszDisplayNameFile, &eaten, &pmk); + ok_ole_success(hr, MkParseDisplayName); + if (pmk) + { + IMoniker_IsSystemMoniker(pmk, &moniker_type); + ok(moniker_type == MKSYS_FILEMONIKER, "moniker_type was %d instead of MKSYS_FILEMONIKER\n", moniker_type); + IMoniker_Release(pmk); + } + + hr = MkParseDisplayName(pbc, wszDisplayName, &eaten, &pmk); + ok_ole_success(hr, MkParseDisplayName); + + if (pmk) + { + hr = IMoniker_BindToObject(pmk, pbc, NULL, &IID_IUnknown, (LPVOID*)&object); + ok_ole_success(hr, IMoniker_BindToObject); + + IUnknown_Release(object); + IMoniker_Release(pmk); + } + IBindCtx_Release(pbc); + + /* Test the EnumMoniker interface */ + hr = CreateBindCtx(0, &pbc); + ok_ole_success(hr, CreateBindCtx); + + hr = CreateFileMoniker(wszFileName1, &pmk1); + ok(hr==0, "CreateFileMoniker for file hr=%08x\n", hr); + hr = CreateFileMoniker(wszFileName2, &pmk2); + ok(hr==0, "CreateFileMoniker for file hr=%08x\n", hr); + hr = IBindCtx_GetRunningObjectTable(pbc, &pprot); + ok(hr==0, "IBindCtx_GetRunningObjectTable hr=%08x\n", hr); + + /* Check EnumMoniker before registering */ + hr = IRunningObjectTable_EnumRunning(pprot, &spEM1); + ok(hr==0, "IRunningObjectTable_EnumRunning hr=%08x\n", hr); + hr = IEnumMoniker_QueryInterface(spEM1, &IID_IUnknown, (void*) &lpEM1); + /* Register a couple of Monikers and check is ok */ + ok(hr==0, "IEnumMoniker_QueryInterface hr %08x %p\n", hr, lpEM1); + hr = MK_E_NOOBJECT; + + matchCnt = count_moniker_matches(pbc, spEM1); + trace("Number of matches is %i\n", matchCnt); + + grflags= grflags | ROTFLAGS_REGISTRATIONKEEPSALIVE; + hr = IRunningObjectTable_Register(pprot, grflags, lpEM1, pmk1, &pdwReg1); + ok(hr==0, "IRunningObjectTable_Register hr=%08x %p %08x %p %p %d\n", + hr, pprot, grflags, lpEM1, pmk1, pdwReg1); + + trace("IROT::Register\n"); + grflags=0; + grflags= grflags | ROTFLAGS_REGISTRATIONKEEPSALIVE; + hr = IRunningObjectTable_Register(pprot, grflags, lpEM1, pmk2, &pdwReg2); + ok(hr==0, "IRunningObjectTable_Register hr=%08x %p %08x %p %p %d\n", hr, + pprot, grflags, lpEM1, pmk2, pdwReg2); + + hr = IRunningObjectTable_EnumRunning(pprot, &spEM2); + ok(hr==0, "IRunningObjectTable_EnumRunning hr=%08x\n", hr); + + matchCnt = count_moniker_matches(pbc, spEM2); + ok(matchCnt==2, "Number of matches should be equal to 2 not %i\n", matchCnt); + + trace("IEnumMoniker::Clone\n"); + IEnumMoniker_Clone(spEM2, &spEM3); + + matchCnt = count_moniker_matches(pbc, spEM3); + ok(matchCnt==0, "Number of matches should be equal to 0 not %i\n", matchCnt); + trace("IEnumMoniker::Reset\n"); + IEnumMoniker_Reset(spEM3); + + matchCnt = count_moniker_matches(pbc, spEM3); + ok(matchCnt==2, "Number of matches should be equal to 2 not %i\n", matchCnt); + + IRunningObjectTable_Revoke(pprot,pdwReg1); + IRunningObjectTable_Revoke(pprot,pdwReg2); + IUnknown_Release(lpEM1); + IEnumMoniker_Release(spEM1); + IEnumMoniker_Release(spEM2); + IEnumMoniker_Release(spEM3); + IMoniker_Release(pmk1); + IMoniker_Release(pmk2); + IRunningObjectTable_Release(pprot); + + IBindCtx_Release(pbc); +} + +static const LARGE_INTEGER llZero; + +static const BYTE expected_class_moniker_marshal_data[] = +{ + 0x4d,0x45,0x4f,0x57,0x04,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x1a,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x05,0xe0,0x02,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x00,0x00,0x00,0x00, +}; + +static const BYTE expected_class_moniker_saved_data[] = +{ + 0x05,0xe0,0x02,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x00,0x00,0x00,0x00, +}; + +static const BYTE expected_class_moniker_comparison_data[] = +{ + 0x1a,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x05,0xe0,0x02,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, +}; + +static const WCHAR expected_class_moniker_display_name[] = +{ + 'c','l','s','i','d',':','0','0','0','2','E','0','0','5','-','0','0','0', + '0','-','0','0','0','0','-','C','0','0','0','-','0','0','0','0','0','0', + '0','0','0','0','4','6',':',0 +}; + +static const BYTE expected_item_moniker_comparison_data[] = +{ + 0x04,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x21,0x00,0x54,0x00,0x45,0x00,0x53,0x00, + 0x54,0x00,0x00,0x00, +}; + +static const BYTE expected_item_moniker_saved_data[] = +{ + 0x02,0x00,0x00,0x00,0x21,0x00,0x05,0x00, + 0x00,0x00,0x54,0x65,0x73,0x74,0x00, +}; + +static const BYTE expected_item_moniker_marshal_data[] = +{ + 0x4d,0x45,0x4f,0x57,0x04,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x04,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x21,0x00,0x05,0x00, + 0x00,0x00,0x54,0x65,0x73,0x74,0x00, +}; + +static const BYTE expected_anti_moniker_marshal_data[] = +{ + 0x4d,0x45,0x4f,0x57,0x04,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x05,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00, +}; + +static const BYTE expected_anti_moniker_saved_data[] = +{ + 0x01,0x00,0x00,0x00, +}; + +static const BYTE expected_anti_moniker_comparison_data[] = +{ + 0x05,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x01,0x00,0x00,0x00, +}; + +static const BYTE expected_gc_moniker_marshal_data[] = +{ + 0x4d,0x45,0x4f,0x57,0x04,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x09,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x00,0x00,0x00,0x00,0x2c,0x01,0x00,0x00, + 0x4d,0x45,0x4f,0x57,0x04,0x00,0x00,0x00, + 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x04,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x21,0x00,0x05,0x00, + 0x00,0x00,0x54,0x65,0x73,0x74,0x00,0x4d, + 0x45,0x4f,0x57,0x04,0x00,0x00,0x00,0x0f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x04, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xc0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x00, + 0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x02, + 0x00,0x00,0x00,0x23,0x00,0x05,0x00,0x00, + 0x00,0x57,0x69,0x6e,0x65,0x00, +}; + +static const BYTE expected_gc_moniker_saved_data[] = +{ + 0x02,0x00,0x00,0x00,0x04,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x46,0x02,0x00,0x00,0x00, + 0x21,0x00,0x05,0x00,0x00,0x00,0x54,0x65, + 0x73,0x74,0x00,0x04,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00, + 0x00,0x00,0x46,0x02,0x00,0x00,0x00,0x23, + 0x00,0x05,0x00,0x00,0x00,0x57,0x69,0x6e, + 0x65,0x00, +}; + +static const BYTE expected_gc_moniker_comparison_data[] = +{ + 0x09,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x04,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46, + 0x21,0x00,0x54,0x00,0x45,0x00,0x53,0x00, + 0x54,0x00,0x00,0x00,0x04,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x46,0x23,0x00,0x57,0x00, + 0x49,0x00,0x4e,0x00,0x45,0x00,0x00,0x00, +}; + +static void test_moniker( + const char *testname, IMoniker *moniker, + const BYTE *expected_moniker_marshal_data, unsigned int sizeof_expected_moniker_marshal_data, + const BYTE *expected_moniker_saved_data, unsigned int sizeof_expected_moniker_saved_data, + const BYTE *expected_moniker_comparison_data, unsigned int sizeof_expected_moniker_comparison_data, + LPCWSTR expected_display_name) +{ + IStream * stream; + IROTData * rotdata; + HRESULT hr; + HGLOBAL hglobal; + LPBYTE moniker_data; + DWORD moniker_size; + DWORD i; + BOOL same = TRUE; + BYTE buffer[128]; + IMoniker * moniker_proxy; + LPOLESTR display_name; + IBindCtx *bindctx; + + hr = IMoniker_IsDirty(moniker); + ok(hr == S_FALSE, "%s: IMoniker_IsDirty should return S_FALSE, not 0x%08x\n", testname, hr); + + /* Display Name */ + + hr = CreateBindCtx(0, &bindctx); + ok_ole_success(hr, CreateBindCtx); + + hr = IMoniker_GetDisplayName(moniker, bindctx, NULL, &display_name); + ok_ole_success(hr, IMoniker_GetDisplayName); + ok(!lstrcmpW(display_name, expected_display_name), "%s: display name wasn't what was expected\n", testname); + + CoTaskMemFree(display_name); + IBindCtx_Release(bindctx); + + hr = IMoniker_IsDirty(moniker); + ok(hr == S_FALSE, "%s: IMoniker_IsDirty should return S_FALSE, not 0x%08x\n", testname, hr); + + /* IROTData::GetComparisonData test */ + + hr = IMoniker_QueryInterface(moniker, &IID_IROTData, (void **)&rotdata); + ok_ole_success(hr, IMoniker_QueryInterface_IID_IROTData); + + hr = IROTData_GetComparisonData(rotdata, buffer, sizeof(buffer), &moniker_size); + ok_ole_success(hr, IROTData_GetComparisonData); + + if (hr != S_OK) moniker_size = 0; + + /* first check we have the right amount of data */ + ok(moniker_size == sizeof_expected_moniker_comparison_data, + "%s: Size of comparison data differs (expected %d, actual %d)\n", + testname, sizeof_expected_moniker_comparison_data, moniker_size); + + /* then do a byte-by-byte comparison */ + for (i = 0; i < min(moniker_size, sizeof_expected_moniker_comparison_data); i++) + { + if (expected_moniker_comparison_data[i] != buffer[i]) + { + same = FALSE; + break; + } + } + + ok(same, "%s: Comparison data differs\n", testname); + if (!same) + { + for (i = 0; i < moniker_size; i++) + { + if (i % 8 == 0) printf(" "); + printf("0x%02x,", buffer[i]); + if (i % 8 == 7) printf("\n"); + } + printf("\n"); + } + + IROTData_Release(rotdata); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + + /* Saving */ + + hr = IMoniker_Save(moniker, stream, TRUE); + ok_ole_success(hr, IMoniker_Save); + + hr = GetHGlobalFromStream(stream, &hglobal); + ok_ole_success(hr, GetHGlobalFromStream); + + moniker_size = GlobalSize(hglobal); + + moniker_data = GlobalLock(hglobal); + + /* first check we have the right amount of data */ + ok(moniker_size == sizeof_expected_moniker_saved_data, + "%s: Size of saved data differs (expected %d, actual %d)\n", + testname, sizeof_expected_moniker_saved_data, moniker_size); + + /* then do a byte-by-byte comparison */ + for (i = 0; i < min(moniker_size, sizeof_expected_moniker_saved_data); i++) + { + if (expected_moniker_saved_data[i] != moniker_data[i]) + { + same = FALSE; + break; + } + } + + ok(same, "%s: Saved data differs\n", testname); + if (!same) + { + for (i = 0; i < moniker_size; i++) + { + if (i % 8 == 0) printf(" "); + printf("0x%02x,", moniker_data[i]); + if (i % 8 == 7) printf("\n"); + } + printf("\n"); + } + + GlobalUnlock(hglobal); + + IStream_Release(stream); + + /* Marshaling tests */ + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok_ole_success(hr, CreateStreamOnHGlobal); + + hr = CoMarshalInterface(stream, &IID_IMoniker, (IUnknown *)moniker, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + hr = GetHGlobalFromStream(stream, &hglobal); + ok_ole_success(hr, GetHGlobalFromStream); + + moniker_size = GlobalSize(hglobal); + + moniker_data = GlobalLock(hglobal); + + /* first check we have the right amount of data */ + ok(moniker_size == sizeof_expected_moniker_marshal_data, + "%s: Size of marshaled data differs (expected %d, actual %d)\n", + testname, sizeof_expected_moniker_marshal_data, moniker_size); + + /* then do a byte-by-byte comparison */ + if (expected_moniker_marshal_data) + { + for (i = 0; i < min(moniker_size, sizeof_expected_moniker_marshal_data); i++) + { + if (expected_moniker_marshal_data[i] != moniker_data[i]) + { + same = FALSE; + break; + } + } + } + + ok(same, "%s: Marshaled data differs\n", testname); + if (!same) + { + for (i = 0; i < moniker_size; i++) + { + if (i % 8 == 0) printf(" "); + printf("0x%02x,", moniker_data[i]); + if (i % 8 == 7) printf("\n"); + } + printf("\n"); + } + + GlobalUnlock(hglobal); + + IStream_Seek(stream, llZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(stream, &IID_IMoniker, (void **)&moniker_proxy); + ok_ole_success(hr, CoUnmarshalInterface); + + IStream_Release(stream); + IMoniker_Release(moniker_proxy); +} + +static void test_class_moniker(void) +{ + HRESULT hr; + IMoniker *moniker; + DWORD moniker_type; + DWORD hash; + IBindCtx *bindctx; + IMoniker *inverse; + IUnknown *unknown; + FILETIME filetime; + + hr = CreateClassMoniker(&CLSID_StdComponentCategoriesMgr, &moniker); + ok_ole_success(hr, CreateClassMoniker); + if (!moniker) return; + + test_moniker("class moniker", moniker, + expected_class_moniker_marshal_data, sizeof(expected_class_moniker_marshal_data), + expected_class_moniker_saved_data, sizeof(expected_class_moniker_saved_data), + expected_class_moniker_comparison_data, sizeof(expected_class_moniker_comparison_data), + expected_class_moniker_display_name); + + /* Hashing */ + + hr = IMoniker_Hash(moniker, &hash); + ok_ole_success(hr, IMoniker_Hash); + + ok(hash == CLSID_StdComponentCategoriesMgr.Data1, + "Hash value != Data1 field of clsid, instead was 0x%08x\n", + hash); + + /* IsSystemMoniker test */ + + hr = IMoniker_IsSystemMoniker(moniker, &moniker_type); + ok_ole_success(hr, IMoniker_IsSystemMoniker); + + ok(moniker_type == MKSYS_CLASSMONIKER, + "dwMkSys != MKSYS_CLASSMONIKER, instead was 0x%08x\n", + moniker_type); + + hr = CreateBindCtx(0, &bindctx); + ok_ole_success(hr, CreateBindCtx); + + /* IsRunning test */ + hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL); + ok(hr == E_NOTIMPL, "IMoniker_IsRunning should return E_NOTIMPL, not 0x%08x\n", hr); + + hr = IMoniker_GetTimeOfLastChange(moniker, bindctx, NULL, &filetime); + ok(hr == MK_E_UNAVAILABLE, "IMoniker_GetTimeOfLastChange should return MK_E_UNAVAILABLE, not 0x%08x\n", hr); + + hr = IMoniker_BindToObject(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + ok_ole_success(hr, IMoniker_BindToStorage); + IUnknown_Release(unknown); + + hr = IMoniker_BindToStorage(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + ok_ole_success(hr, IMoniker_BindToStorage); + IUnknown_Release(unknown); + + IBindCtx_Release(bindctx); + + hr = IMoniker_Inverse(moniker, &inverse); + ok_ole_success(hr, IMoniker_Inverse); + IMoniker_Release(inverse); + + IMoniker_Release(moniker); +} + +static void test_file_moniker(WCHAR* path) +{ + IStream *stream; + IMoniker *moniker1 = NULL, *moniker2 = NULL; + HRESULT hr; + + hr = CreateFileMoniker(path, &moniker1); + ok_ole_success(hr, CreateFileMoniker); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + + /* Marshal */ + hr = CoMarshalInterface(stream, &IID_IMoniker, (IUnknown *)moniker1, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + + /* Rewind */ + hr = IStream_Seek(stream, llZero, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, IStream_Seek); + + /* Unmarshal */ + hr = CoUnmarshalInterface(stream, &IID_IMoniker, (void**)&moniker2); + ok_ole_success(hr, CoUnmarshalInterface); + + hr = IMoniker_IsEqual(moniker1, moniker2); + ok_ole_success(hr, IsEqual); + + IStream_Release(stream); + if (moniker1) + IMoniker_Release(moniker1); + if (moniker2) + IMoniker_Release(moniker2); +} + +static void test_file_monikers(void) +{ + static WCHAR wszFile[][30] = { + {'\\', 'w','i','n','d','o','w','s','\\','s','y','s','t','e','m','\\','t','e','s','t','1','.','d','o','c',0}, + {'\\', 'a','b','c','d','e','f','g','\\','h','i','j','k','l','\\','m','n','o','p','q','r','s','t','u','.','m','n','o',0}, + /* These map to themselves in Windows-1252 & 932 (Shift-JIS) */ + {0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0}, + /* U+2020 = DAGGER = 0x86 (1252) = 0x813f (932) + * U+20AC = EURO SIGN = 0x80 (1252) = undef (932) + * U+0100 .. = Latin extended-A + */ + {0x20ac, 0x2020, 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10a, 0x10b, 0x10c, 0}, + }; + + int i; + + trace("ACP is %u\n", GetACP()); + + for (i = 0; i < COUNTOF(wszFile); ++i) + { + int j ; + for (j = lstrlenW(wszFile[i]); j > 0; --j) + { + wszFile[i][j] = 0; + test_file_moniker(wszFile[i]); + } + } +} + +static void test_item_moniker(void) +{ + HRESULT hr; + IMoniker *moniker; + DWORD moniker_type; + DWORD hash; + IBindCtx *bindctx; + IMoniker *inverse; + IUnknown *unknown; + static const WCHAR wszDelimeter[] = {'!',0}; + static const WCHAR wszObjectName[] = {'T','e','s','t',0}; + static const WCHAR expected_display_name[] = { '!','T','e','s','t',0 }; + + hr = CreateItemMoniker(wszDelimeter, wszObjectName, &moniker); + ok_ole_success(hr, CreateItemMoniker); + + test_moniker("item moniker", moniker, + expected_item_moniker_marshal_data, sizeof(expected_item_moniker_marshal_data), + expected_item_moniker_saved_data, sizeof(expected_item_moniker_saved_data), + expected_item_moniker_comparison_data, sizeof(expected_item_moniker_comparison_data), + expected_display_name); + + /* Hashing */ + + hr = IMoniker_Hash(moniker, &hash); + ok_ole_success(hr, IMoniker_Hash); + + ok(hash == 0x73c, + "Hash value != 0x73c, instead was 0x%08x\n", + hash); + + /* IsSystemMoniker test */ + + hr = IMoniker_IsSystemMoniker(moniker, &moniker_type); + ok_ole_success(hr, IMoniker_IsSystemMoniker); + + ok(moniker_type == MKSYS_ITEMMONIKER, + "dwMkSys != MKSYS_ITEMMONIKER, instead was 0x%08x\n", + moniker_type); + + hr = CreateBindCtx(0, &bindctx); + ok_ole_success(hr, CreateBindCtx); + + /* IsRunning test */ + hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL); + ok(hr == S_FALSE, "IMoniker_IsRunning should return S_FALSE, not 0x%08x\n", hr); + + hr = IMoniker_BindToObject(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + ok(hr == E_INVALIDARG, "IMoniker_BindToStorage should return E_INVALIDARG, not 0x%08x\n", hr); + + hr = IMoniker_BindToStorage(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + ok(hr == E_INVALIDARG, "IMoniker_BindToObject should return E_INVALIDARG, not 0x%08x\n", hr); + + IBindCtx_Release(bindctx); + + hr = IMoniker_Inverse(moniker, &inverse); + ok_ole_success(hr, IMoniker_Inverse); + IMoniker_Release(inverse); + + IMoniker_Release(moniker); +} + +static void test_anti_moniker(void) +{ + HRESULT hr; + IMoniker *moniker; + DWORD moniker_type; + DWORD hash; + IBindCtx *bindctx; + FILETIME filetime; + IMoniker *inverse; + IUnknown *unknown; + static const WCHAR expected_display_name[] = { '\\','.','.',0 }; + + hr = CreateAntiMoniker(&moniker); + ok_ole_success(hr, CreateAntiMoniker); + if (!moniker) return; + + test_moniker("anti moniker", moniker, + expected_anti_moniker_marshal_data, sizeof(expected_anti_moniker_marshal_data), + expected_anti_moniker_saved_data, sizeof(expected_anti_moniker_saved_data), + expected_anti_moniker_comparison_data, sizeof(expected_anti_moniker_comparison_data), + expected_display_name); + + /* Hashing */ + hr = IMoniker_Hash(moniker, &hash); + ok_ole_success(hr, IMoniker_Hash); + ok(hash == 0x80000001, + "Hash value != 0x80000001, instead was 0x%08x\n", + hash); + + /* IsSystemMoniker test */ + hr = IMoniker_IsSystemMoniker(moniker, &moniker_type); + ok_ole_success(hr, IMoniker_IsSystemMoniker); + ok(moniker_type == MKSYS_ANTIMONIKER, + "dwMkSys != MKSYS_ANTIMONIKER, instead was 0x%08x\n", + moniker_type); + + hr = IMoniker_Inverse(moniker, &inverse); + ok(hr == MK_E_NOINVERSE, "IMoniker_Inverse should have returned MK_E_NOINVERSE instead of 0x%08x\n", hr); + ok(inverse == NULL, "inverse should have been set to NULL instead of %p\n", inverse); + + hr = CreateBindCtx(0, &bindctx); + ok_ole_success(hr, CreateBindCtx); + + /* IsRunning test */ + hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL); + ok(hr == S_FALSE, "IMoniker_IsRunning should return S_FALSE, not 0x%08x\n", hr); + + hr = IMoniker_GetTimeOfLastChange(moniker, bindctx, NULL, &filetime); + ok(hr == E_NOTIMPL, "IMoniker_GetTimeOfLastChange should return E_NOTIMPL, not 0x%08x\n", hr); + + hr = IMoniker_BindToObject(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + ok(hr == E_NOTIMPL, "IMoniker_BindToObject should return E_NOTIMPL, not 0x%08x\n", hr); + + hr = IMoniker_BindToStorage(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + ok(hr == E_NOTIMPL, "IMoniker_BindToStorage should return E_NOTIMPL, not 0x%08x\n", hr); + + IBindCtx_Release(bindctx); + + IMoniker_Release(moniker); +} + +static void test_generic_composite_moniker(void) +{ + HRESULT hr; + IMoniker *moniker; + IMoniker *moniker1; + IMoniker *moniker2; + DWORD moniker_type; + DWORD hash; + IBindCtx *bindctx; + FILETIME filetime; + IMoniker *inverse; + IUnknown *unknown; + static const WCHAR wszDelimeter1[] = {'!',0}; + static const WCHAR wszObjectName1[] = {'T','e','s','t',0}; + static const WCHAR wszDelimeter2[] = {'#',0}; + static const WCHAR wszObjectName2[] = {'W','i','n','e',0}; + static const WCHAR expected_display_name[] = { '!','T','e','s','t','#','W','i','n','e',0 }; + + hr = CreateItemMoniker(wszDelimeter1, wszObjectName1, &moniker1); + ok_ole_success(hr, CreateItemMoniker); + hr = CreateItemMoniker(wszDelimeter2, wszObjectName2, &moniker2); + ok_ole_success(hr, CreateItemMoniker); + hr = CreateGenericComposite(moniker1, moniker2, &moniker); + ok_ole_success(hr, CreateGenericComposite); + + test_moniker("generic composite moniker", moniker, + expected_gc_moniker_marshal_data, sizeof(expected_gc_moniker_marshal_data), + expected_gc_moniker_saved_data, sizeof(expected_gc_moniker_saved_data), + expected_gc_moniker_comparison_data, sizeof(expected_gc_moniker_comparison_data), + expected_display_name); + + /* Hashing */ + + hr = IMoniker_Hash(moniker, &hash); + ok_ole_success(hr, IMoniker_Hash); + + ok(hash == 0xd87, + "Hash value != 0xd87, instead was 0x%08x\n", + hash); + + /* IsSystemMoniker test */ + + hr = IMoniker_IsSystemMoniker(moniker, &moniker_type); + ok_ole_success(hr, IMoniker_IsSystemMoniker); + + ok(moniker_type == MKSYS_GENERICCOMPOSITE, + "dwMkSys != MKSYS_GENERICCOMPOSITE, instead was 0x%08x\n", + moniker_type); + + hr = CreateBindCtx(0, &bindctx); + ok_ole_success(hr, CreateBindCtx); + + /* IsRunning test */ + hr = IMoniker_IsRunning(moniker, bindctx, NULL, NULL); + todo_wine + ok(hr == S_FALSE, "IMoniker_IsRunning should return S_FALSE, not 0x%08x\n", hr); + + hr = IMoniker_GetTimeOfLastChange(moniker, bindctx, NULL, &filetime); + ok(hr == MK_E_NOTBINDABLE, "IMoniker_GetTimeOfLastChange should return MK_E_NOTBINDABLE, not 0x%08x\n", hr); + + hr = IMoniker_BindToObject(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + todo_wine + ok(hr == E_INVALIDARG, "IMoniker_BindToObject should return E_INVALIDARG, not 0x%08x\n", hr); + + todo_wine + hr = IMoniker_BindToStorage(moniker, bindctx, NULL, &IID_IUnknown, (void **)&unknown); + ok(hr == E_INVALIDARG, "IMoniker_BindToStorage should return E_INVALIDARG, not 0x%08x\n", hr); + + IBindCtx_Release(bindctx); + + hr = IMoniker_Inverse(moniker, &inverse); + ok_ole_success(hr, IMoniker_Inverse); + IMoniker_Release(inverse); + + IMoniker_Release(moniker); +} + +static void test_bind_context(void) +{ + HRESULT hr; + IBindCtx *pBindCtx; + IEnumString *pEnumString; + BIND_OPTS2 bind_opts; + HeapUnknown *unknown; + HeapUnknown *unknown2; + IUnknown *param_obj; + ULONG refs; + static const WCHAR wszParamName[] = {'G','e','m','m','a',0}; + static const WCHAR wszNonExistent[] = {'N','o','n','E','x','i','s','t','e','n','t',0}; + + hr = CreateBindCtx(0, NULL); + ok(hr == E_INVALIDARG, "CreateBindCtx with NULL ppbc should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CreateBindCtx(0xdeadbeef, &pBindCtx); + ok(hr == E_INVALIDARG, "CreateBindCtx with reserved value non-zero should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CreateBindCtx(0, &pBindCtx); + ok_ole_success(hr, "CreateBindCtx"); + + bind_opts.cbStruct = -1; + hr = IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS *)&bind_opts); + ok_ole_success(hr, "IBindCtx_GetBindOptions"); + ok(bind_opts.cbStruct == sizeof(bind_opts), "bind_opts.cbStruct was %d\n", bind_opts.cbStruct); + + bind_opts.cbStruct = sizeof(BIND_OPTS); + hr = IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS *)&bind_opts); + ok_ole_success(hr, "IBindCtx_GetBindOptions"); + ok(bind_opts.cbStruct == sizeof(BIND_OPTS), "bind_opts.cbStruct was %d\n", bind_opts.cbStruct); + + bind_opts.cbStruct = sizeof(bind_opts); + hr = IBindCtx_GetBindOptions(pBindCtx, (BIND_OPTS *)&bind_opts); + ok_ole_success(hr, "IBindCtx_GetBindOptions"); + ok(bind_opts.cbStruct == sizeof(bind_opts), "bind_opts.cbStruct was %d\n", bind_opts.cbStruct); + ok(bind_opts.grfFlags == 0, "bind_opts.grfFlags was 0x%x instead of 0\n", bind_opts.grfFlags); + ok(bind_opts.grfMode == STGM_READWRITE, "bind_opts.grfMode was 0x%x instead of STGM_READWRITE\n", bind_opts.grfMode); + ok(bind_opts.dwTickCountDeadline == 0, "bind_opts.dwTickCountDeadline was %d instead of 0\n", bind_opts.dwTickCountDeadline); + ok(bind_opts.dwTrackFlags == 0, "bind_opts.dwTrackFlags was 0x%x instead of 0\n", bind_opts.dwTrackFlags); + ok(bind_opts.dwClassContext == (CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER), + "bind_opts.dwClassContext should have been 0x15 instead of 0x%x\n", bind_opts.dwClassContext); + ok(bind_opts.locale == GetThreadLocale(), "bind_opts.locale should have been 0x%x instead of 0x%x\n", GetThreadLocale(), bind_opts.locale); + ok(bind_opts.pServerInfo == NULL, "bind_opts.pServerInfo should have been NULL instead of %p\n", bind_opts.pServerInfo); + + bind_opts.cbStruct = -1; + hr = IBindCtx_SetBindOptions(pBindCtx, (BIND_OPTS *)&bind_opts); + ok(hr == E_INVALIDARG, "IBindCtx_SetBindOptions with bad cbStruct should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = IBindCtx_RegisterObjectParam(pBindCtx, (WCHAR *)wszParamName, NULL); + ok(hr == E_INVALIDARG, "IBindCtx_RegisterObjectParam should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + unknown = HeapAlloc(GetProcessHeap(), 0, sizeof(*unknown)); + unknown->lpVtbl = &HeapUnknown_Vtbl; + unknown->refs = 1; + hr = IBindCtx_RegisterObjectParam(pBindCtx, (WCHAR *)wszParamName, (IUnknown *)&unknown->lpVtbl); + ok_ole_success(hr, "IBindCtx_RegisterObjectParam"); + + hr = IBindCtx_GetObjectParam(pBindCtx, (WCHAR *)wszParamName, ¶m_obj); + ok_ole_success(hr, "IBindCtx_GetObjectParam"); + IUnknown_Release(param_obj); + + hr = IBindCtx_GetObjectParam(pBindCtx, (WCHAR *)wszNonExistent, ¶m_obj); + ok(hr == E_FAIL, "IBindCtx_GetObjectParam with nonexistent key should have failed with E_FAIL instead of 0x%08x\n", hr); + ok(param_obj == NULL, "IBindCtx_GetObjectParam with nonexistent key should have set output parameter to NULL instead of %p\n", param_obj); + + hr = IBindCtx_RevokeObjectParam(pBindCtx, (WCHAR *)wszNonExistent); + ok(hr == E_FAIL, "IBindCtx_RevokeObjectParam with nonexistent key should have failed with E_FAIL instead of 0x%08x\n", hr); + + hr = IBindCtx_EnumObjectParam(pBindCtx, &pEnumString); + ok(hr == E_NOTIMPL, "IBindCtx_EnumObjectParam should have returned E_NOTIMPL instead of 0x%08x\n", hr); + ok(!pEnumString, "pEnumString should be NULL\n"); + + hr = IBindCtx_RegisterObjectBound(pBindCtx, NULL); + ok_ole_success(hr, "IBindCtx_RegisterObjectBound(NULL)"); + + hr = IBindCtx_RevokeObjectBound(pBindCtx, NULL); + ok(hr == E_INVALIDARG, "IBindCtx_RevokeObjectBound(NULL) should have return E_INVALIDARG instead of 0x%08x\n", hr); + + unknown2 = HeapAlloc(GetProcessHeap(), 0, sizeof(*unknown)); + unknown2->lpVtbl = &HeapUnknown_Vtbl; + unknown2->refs = 1; + hr = IBindCtx_RegisterObjectBound(pBindCtx, (IUnknown *)&unknown2->lpVtbl); + ok_ole_success(hr, "IBindCtx_RegisterObjectBound"); + + hr = IBindCtx_RevokeObjectBound(pBindCtx, (IUnknown *)&unknown2->lpVtbl); + ok_ole_success(hr, "IBindCtx_RevokeObjectBound"); + + hr = IBindCtx_RevokeObjectBound(pBindCtx, (IUnknown *)&unknown2->lpVtbl); + ok(hr == MK_E_NOTBOUND, "IBindCtx_RevokeObjectBound with not bound object should have returned MK_E_NOTBOUND instead of 0x%08x\n", hr); + + IBindCtx_Release(pBindCtx); + + refs = IUnknown_Release((IUnknown *)&unknown->lpVtbl); + ok(!refs, "object param should have been destroyed, instead of having %d refs\n", refs); + + refs = IUnknown_Release((IUnknown *)&unknown2->lpVtbl); + ok(!refs, "bound object should have been destroyed, instead of having %d refs\n", refs); +} + +START_TEST(moniker) +{ + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + test_ROT(); + test_MkParseDisplayName(); + test_class_moniker(); + test_file_monikers(); + test_item_moniker(); + test_anti_moniker(); + test_generic_composite_moniker(); + + /* FIXME: test moniker creation funcs and parsing other moniker formats */ + + test_bind_context(); + + CoUninitialize(); +} diff --git a/rostests/winetests/ole32/ole2.c b/rostests/winetests/ole32/ole2.c new file mode 100644 index 00000000000..88b58e7c50a --- /dev/null +++ b/rostests/winetests/ole32/ole2.c @@ -0,0 +1,1536 @@ +/* + * Object Linking and Embedding Tests + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#define CONST_VTABLE + +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "shlguid.h" + +#include "wine/test.h" + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) + +static IPersistStorage OleObjectPersistStg; +static IOleCache *cache; +static IRunnableObject *runnable; + +static const CLSID CLSID_WineTest = +{ /* 9474ba1a-258b-490b-bc13-516e9239ace0 */ + 0x9474ba1a, + 0x258b, + 0x490b, + {0xbc, 0x13, 0x51, 0x6e, 0x92, 0x39, 0xac, 0xe0} +}; + +static char const * const *expected_method_list; + +BOOL g_showRunnable = TRUE; +BOOL g_isRunning = TRUE; + +#define CHECK_EXPECTED_METHOD(method_name) \ + do { \ + trace("%s\n", method_name); \ + ok(*expected_method_list != NULL, "Extra method %s called\n", method_name); \ + if (*expected_method_list) \ + { \ + ok(!strcmp(*expected_method_list, method_name), "Expected %s to be called instead of %s\n", \ + *expected_method_list, method_name); \ + expected_method_list++; \ + } \ + } while(0) + +static HRESULT WINAPI OleObject_QueryInterface(IOleObject *iface, REFIID riid, void **ppv) +{ + CHECK_EXPECTED_METHOD("OleObject_QueryInterface"); + + *ppv = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IOleObject)) + *ppv = (void *)iface; + else if (IsEqualIID(riid, &IID_IPersistStorage)) + *ppv = &OleObjectPersistStg; + else if (IsEqualIID(riid, &IID_IOleCache)) + *ppv = cache; + else if (IsEqualIID(riid, &IID_IRunnableObject) && g_showRunnable) + *ppv = runnable; + + if(*ppv) { + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + trace("OleObject_QueryInterface: returning E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI OleObject_AddRef(IOleObject *iface) +{ + CHECK_EXPECTED_METHOD("OleObject_AddRef"); + return 2; +} + +static ULONG WINAPI OleObject_Release(IOleObject *iface) +{ + CHECK_EXPECTED_METHOD("OleObject_Release"); + return 1; +} + +static HRESULT WINAPI OleObject_SetClientSite + ( + IOleObject *iface, + IOleClientSite *pClientSite + ) +{ + CHECK_EXPECTED_METHOD("OleObject_SetClientSite"); + return S_OK; +} + +static HRESULT WINAPI OleObject_GetClientSite + ( + IOleObject *iface, + IOleClientSite **ppClientSite + ) +{ + CHECK_EXPECTED_METHOD("OleObject_GetClientSite"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObject_SetHostNames + ( + IOleObject *iface, + LPCOLESTR szContainerApp, + LPCOLESTR szContainerObj + ) +{ + CHECK_EXPECTED_METHOD("OleObject_SetHostNames"); + return S_OK; +} + +static HRESULT WINAPI OleObject_Close + ( + IOleObject *iface, + DWORD dwSaveOption + ) +{ + CHECK_EXPECTED_METHOD("OleObject_Close"); + return S_OK; +} + +static HRESULT WINAPI OleObject_SetMoniker + ( + IOleObject *iface, + DWORD dwWhichMoniker, + IMoniker *pmk + ) +{ + CHECK_EXPECTED_METHOD("OleObject_SetMoniker"); + return S_OK; +} + +static HRESULT WINAPI OleObject_GetMoniker + ( + IOleObject *iface, + DWORD dwAssign, + DWORD dwWhichMoniker, + IMoniker **ppmk + ) +{ + CHECK_EXPECTED_METHOD("OleObject_GetMoniker"); + return S_OK; +} + +static HRESULT WINAPI OleObject_InitFromData + ( + IOleObject *iface, + IDataObject *pDataObject, + BOOL fCreation, + DWORD dwReserved + ) +{ + CHECK_EXPECTED_METHOD("OleObject_InitFromData"); + return S_OK; +} + +static HRESULT WINAPI OleObject_GetClipboardData + ( + IOleObject *iface, + DWORD dwReserved, + IDataObject **ppDataObject + ) +{ + CHECK_EXPECTED_METHOD("OleObject_GetClipboardData"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObject_DoVerb + ( + IOleObject *iface, + LONG iVerb, + LPMSG lpmsg, + IOleClientSite *pActiveSite, + LONG lindex, + HWND hwndParent, + LPCRECT lprcPosRect + ) +{ + CHECK_EXPECTED_METHOD("OleObject_DoVerb"); + return S_OK; +} + +static HRESULT WINAPI OleObject_EnumVerbs + ( + IOleObject *iface, + IEnumOLEVERB **ppEnumOleVerb + ) +{ + CHECK_EXPECTED_METHOD("OleObject_EnumVerbs"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObject_Update + ( + IOleObject *iface + ) +{ + CHECK_EXPECTED_METHOD("OleObject_Update"); + return S_OK; +} + +static HRESULT WINAPI OleObject_IsUpToDate + ( + IOleObject *iface + ) +{ + CHECK_EXPECTED_METHOD("OleObject_IsUpToDate"); + return S_OK; +} + +static HRESULT WINAPI OleObject_GetUserClassID +( + IOleObject *iface, + CLSID *pClsid +) +{ + CHECK_EXPECTED_METHOD("OleObject_GetUserClassID"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObject_GetUserType +( + IOleObject *iface, + DWORD dwFormOfType, + LPOLESTR *pszUserType +) +{ + CHECK_EXPECTED_METHOD("OleObject_GetUserType"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObject_SetExtent +( + IOleObject *iface, + DWORD dwDrawAspect, + SIZEL *psizel +) +{ + CHECK_EXPECTED_METHOD("OleObject_SetExtent"); + return S_OK; +} + +static HRESULT WINAPI OleObject_GetExtent +( + IOleObject *iface, + DWORD dwDrawAspect, + SIZEL *psizel +) +{ + CHECK_EXPECTED_METHOD("OleObject_GetExtent"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObject_Advise +( + IOleObject *iface, + IAdviseSink *pAdvSink, + DWORD *pdwConnection +) +{ + CHECK_EXPECTED_METHOD("OleObject_Advise"); + return S_OK; +} + +static HRESULT WINAPI OleObject_Unadvise +( + IOleObject *iface, + DWORD dwConnection +) +{ + CHECK_EXPECTED_METHOD("OleObject_Unadvise"); + return S_OK; +} + +static HRESULT WINAPI OleObject_EnumAdvise +( + IOleObject *iface, + IEnumSTATDATA **ppenumAdvise +) +{ + CHECK_EXPECTED_METHOD("OleObject_EnumAdvise"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObject_GetMiscStatus +( + IOleObject *iface, + DWORD dwAspect, + DWORD *pdwStatus +) +{ + CHECK_EXPECTED_METHOD("OleObject_GetMiscStatus"); + *pdwStatus = DVASPECT_CONTENT; + return S_OK; +} + +static HRESULT WINAPI OleObject_SetColorScheme +( + IOleObject *iface, + LOGPALETTE *pLogpal +) +{ + CHECK_EXPECTED_METHOD("OleObject_SetColorScheme"); + return E_NOTIMPL; +} + +static const IOleObjectVtbl OleObjectVtbl = +{ + OleObject_QueryInterface, + OleObject_AddRef, + OleObject_Release, + OleObject_SetClientSite, + OleObject_GetClientSite, + OleObject_SetHostNames, + OleObject_Close, + OleObject_SetMoniker, + OleObject_GetMoniker, + OleObject_InitFromData, + OleObject_GetClipboardData, + OleObject_DoVerb, + OleObject_EnumVerbs, + OleObject_Update, + OleObject_IsUpToDate, + OleObject_GetUserClassID, + OleObject_GetUserType, + OleObject_SetExtent, + OleObject_GetExtent, + OleObject_Advise, + OleObject_Unadvise, + OleObject_EnumAdvise, + OleObject_GetMiscStatus, + OleObject_SetColorScheme +}; + +static IOleObject OleObject = { &OleObjectVtbl }; + +static HRESULT WINAPI OleObjectPersistStg_QueryInterface(IPersistStorage *iface, REFIID riid, void **ppv) +{ + trace("OleObjectPersistStg_QueryInterface\n"); + return IUnknown_QueryInterface((IUnknown *)&OleObject, riid, ppv); +} + +static ULONG WINAPI OleObjectPersistStg_AddRef(IPersistStorage *iface) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_AddRef"); + return 2; +} + +static ULONG WINAPI OleObjectPersistStg_Release(IPersistStorage *iface) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_Release"); + return 1; +} + +static HRESULT WINAPI OleObjectPersistStg_GetClassId(IPersistStorage *iface, CLSID *clsid) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_GetClassId"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObjectPersistStg_IsDirty +( + IPersistStorage *iface +) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_IsDirty"); + return S_OK; +} + +static HRESULT WINAPI OleObjectPersistStg_InitNew +( + IPersistStorage *iface, + IStorage *pStg +) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_InitNew"); + return S_OK; +} + +static HRESULT WINAPI OleObjectPersistStg_Load +( + IPersistStorage *iface, + IStorage *pStg +) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_Load"); + return S_OK; +} + +static HRESULT WINAPI OleObjectPersistStg_Save +( + IPersistStorage *iface, + IStorage *pStgSave, + BOOL fSameAsLoad +) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_Save"); + return S_OK; +} + +static HRESULT WINAPI OleObjectPersistStg_SaveCompleted +( + IPersistStorage *iface, + IStorage *pStgNew +) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_SaveCompleted"); + return S_OK; +} + +static HRESULT WINAPI OleObjectPersistStg_HandsOffStorage +( + IPersistStorage *iface +) +{ + CHECK_EXPECTED_METHOD("OleObjectPersistStg_HandsOffStorage"); + return S_OK; +} + +static const IPersistStorageVtbl OleObjectPersistStgVtbl = +{ + OleObjectPersistStg_QueryInterface, + OleObjectPersistStg_AddRef, + OleObjectPersistStg_Release, + OleObjectPersistStg_GetClassId, + OleObjectPersistStg_IsDirty, + OleObjectPersistStg_InitNew, + OleObjectPersistStg_Load, + OleObjectPersistStg_Save, + OleObjectPersistStg_SaveCompleted, + OleObjectPersistStg_HandsOffStorage +}; + +static IPersistStorage OleObjectPersistStg = { &OleObjectPersistStgVtbl }; + +static HRESULT WINAPI OleObjectCache_QueryInterface(IOleCache *iface, REFIID riid, void **ppv) +{ + return IUnknown_QueryInterface((IUnknown *)&OleObject, riid, ppv); +} + +static ULONG WINAPI OleObjectCache_AddRef(IOleCache *iface) +{ + CHECK_EXPECTED_METHOD("OleObjectCache_AddRef"); + return 2; +} + +static ULONG WINAPI OleObjectCache_Release(IOleCache *iface) +{ + CHECK_EXPECTED_METHOD("OleObjectCache_Release"); + return 1; +} + +static HRESULT WINAPI OleObjectCache_Cache +( + IOleCache *iface, + FORMATETC *pformatetc, + DWORD advf, + DWORD *pdwConnection +) +{ + CHECK_EXPECTED_METHOD("OleObjectCache_Cache"); + return S_OK; +} + +static HRESULT WINAPI OleObjectCache_Uncache +( + IOleCache *iface, + DWORD dwConnection +) +{ + CHECK_EXPECTED_METHOD("OleObjectCache_Uncache"); + return S_OK; +} + +static HRESULT WINAPI OleObjectCache_EnumCache +( + IOleCache *iface, + IEnumSTATDATA **ppenumSTATDATA +) +{ + CHECK_EXPECTED_METHOD("OleObjectCache_EnumCache"); + return S_OK; +} + + +static HRESULT WINAPI OleObjectCache_InitCache +( + IOleCache *iface, + IDataObject *pDataObject +) +{ + CHECK_EXPECTED_METHOD("OleObjectCache_InitCache"); + return S_OK; +} + + +static HRESULT WINAPI OleObjectCache_SetData +( + IOleCache *iface, + FORMATETC *pformatetc, + STGMEDIUM *pmedium, + BOOL fRelease +) +{ + CHECK_EXPECTED_METHOD("OleObjectCache_SetData"); + return S_OK; +} + + +static const IOleCacheVtbl OleObjectCacheVtbl = +{ + OleObjectCache_QueryInterface, + OleObjectCache_AddRef, + OleObjectCache_Release, + OleObjectCache_Cache, + OleObjectCache_Uncache, + OleObjectCache_EnumCache, + OleObjectCache_InitCache, + OleObjectCache_SetData +}; + +static IOleCache OleObjectCache = { &OleObjectCacheVtbl }; + +static HRESULT WINAPI OleObjectCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory)) + { + *ppv = iface; + IUnknown_AddRef(iface); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI OleObjectCF_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI OleObjectCF_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI OleObjectCF_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID riid, void **ppv) +{ + return IUnknown_QueryInterface((IUnknown *)&OleObject, riid, ppv); +} + +static HRESULT WINAPI OleObjectCF_LockServer(IClassFactory *iface, BOOL lock) +{ + return S_OK; +} + +static const IClassFactoryVtbl OleObjectCFVtbl = +{ + OleObjectCF_QueryInterface, + OleObjectCF_AddRef, + OleObjectCF_Release, + OleObjectCF_CreateInstance, + OleObjectCF_LockServer +}; + +static IClassFactory OleObjectCF = { &OleObjectCFVtbl }; + +static HRESULT WINAPI OleObjectRunnable_QueryInterface(IRunnableObject *iface, REFIID riid, void **ppv) +{ + return IUnknown_QueryInterface((IUnknown *)&OleObject, riid, ppv); +} + +static ULONG WINAPI OleObjectRunnable_AddRef(IRunnableObject *iface) +{ + CHECK_EXPECTED_METHOD("OleObjectRunnable_AddRef"); + return 2; +} + +static ULONG WINAPI OleObjectRunnable_Release(IRunnableObject *iface) +{ + CHECK_EXPECTED_METHOD("OleObjectRunnable_Release"); + return 1; +} + +static HRESULT WINAPI OleObjectRunnable_GetRunningClass( + IRunnableObject *iface, + LPCLSID lpClsid) +{ + CHECK_EXPECTED_METHOD("OleObjectRunnable_GetRunningClass"); + return E_NOTIMPL; +} + +static HRESULT WINAPI OleObjectRunnable_Run( + IRunnableObject *iface, + LPBINDCTX pbc) +{ + CHECK_EXPECTED_METHOD("OleObjectRunnable_Run"); + return S_OK; +} + +static BOOL WINAPI OleObjectRunnable_IsRunning(IRunnableObject *iface) +{ + CHECK_EXPECTED_METHOD("OleObjectRunnable_IsRunning"); + return g_isRunning; +} + +static HRESULT WINAPI OleObjectRunnable_LockRunning( + IRunnableObject *iface, + BOOL fLock, + BOOL fLastUnlockCloses) +{ + CHECK_EXPECTED_METHOD("OleObjectRunnable_LockRunning"); + return S_OK; +} + +static HRESULT WINAPI OleObjectRunnable_SetContainedObject( + IRunnableObject *iface, + BOOL fContained) +{ + CHECK_EXPECTED_METHOD("OleObjectRunnable_SetContainedObject"); + return S_OK; +} + +static const IRunnableObjectVtbl OleObjectRunnableVtbl = +{ + OleObjectRunnable_QueryInterface, + OleObjectRunnable_AddRef, + OleObjectRunnable_Release, + OleObjectRunnable_GetRunningClass, + OleObjectRunnable_Run, + OleObjectRunnable_IsRunning, + OleObjectRunnable_LockRunning, + OleObjectRunnable_SetContainedObject +}; + +static IRunnableObject OleObjectRunnable = { &OleObjectRunnableVtbl }; + +static const CLSID CLSID_Equation3 = {0x0002CE02, 0x0000, 0x0000, {0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} }; + +static void test_OleCreate(IStorage *pStorage) +{ + HRESULT hr; + IOleObject *pObject; + FORMATETC formatetc; + static const char *methods_olerender_none[] = + { + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_QueryInterface", + "OleObjectPersistStg_AddRef", + "OleObjectPersistStg_InitNew", + "OleObjectPersistStg_Release", + "OleObject_Release", + NULL + }; + static const char *methods_olerender_draw[] = + { + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_QueryInterface", + "OleObjectPersistStg_AddRef", + "OleObjectPersistStg_InitNew", + "OleObjectPersistStg_Release", + "OleObject_QueryInterface", + "OleObjectRunnable_AddRef", + "OleObjectRunnable_Run", + "OleObjectRunnable_Release", + "OleObject_QueryInterface", + "OleObjectCache_AddRef", + "OleObjectCache_Cache", + "OleObjectCache_Release", + "OleObject_Release", + NULL + }; + static const char *methods_olerender_format[] = + { + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_GetMiscStatus", + "OleObject_QueryInterface", + "OleObjectPersistStg_AddRef", + "OleObjectPersistStg_InitNew", + "OleObjectPersistStg_Release", + "OleObject_SetClientSite", + "OleObject_Release", + "OleObject_QueryInterface", + "OleObjectRunnable_AddRef", + "OleObjectRunnable_Run", + "OleObjectRunnable_Release", + "OleObject_QueryInterface", + "OleObjectCache_AddRef", + "OleObjectCache_Cache", + "OleObjectCache_Release", + "OleObject_Release", + NULL + }; + static const char *methods_olerender_asis[] = + { + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_QueryInterface", + "OleObjectPersistStg_AddRef", + "OleObjectPersistStg_InitNew", + "OleObjectPersistStg_Release", + "OleObject_Release", + NULL + }; + static const char *methods_olerender_draw_no_runnable[] = + { + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_QueryInterface", + "OleObjectPersistStg_AddRef", + "OleObjectPersistStg_InitNew", + "OleObjectPersistStg_Release", + "OleObject_QueryInterface", + "OleObject_QueryInterface", + "OleObjectCache_AddRef", + "OleObjectCache_Cache", + "OleObjectCache_Release", + "OleObject_Release", + NULL + }; + static const char *methods_olerender_draw_no_cache[] = + { + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_QueryInterface", + "OleObjectPersistStg_AddRef", + "OleObjectPersistStg_InitNew", + "OleObjectPersistStg_Release", + "OleObject_QueryInterface", + "OleObjectRunnable_AddRef", + "OleObjectRunnable_Run", + "OleObjectRunnable_Release", + "OleObject_QueryInterface", + "OleObject_Release", + NULL + }; + + runnable = &OleObjectRunnable; + cache = &OleObjectCache; + expected_method_list = methods_olerender_none; + trace("OleCreate with OLERENDER_NONE:\n"); + hr = OleCreate(&CLSID_Equation3, &IID_IOleObject, OLERENDER_NONE, NULL, NULL, pStorage, (void **)&pObject); + ok_ole_success(hr, "OleCreate"); + IOleObject_Release(pObject); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + expected_method_list = methods_olerender_draw; + trace("OleCreate with OLERENDER_DRAW:\n"); + hr = OleCreate(&CLSID_Equation3, &IID_IOleObject, OLERENDER_DRAW, NULL, NULL, pStorage, (void **)&pObject); + ok_ole_success(hr, "OleCreate"); + IOleObject_Release(pObject); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + formatetc.cfFormat = CF_TEXT; + formatetc.ptd = NULL; + formatetc.dwAspect = DVASPECT_CONTENT; + formatetc.lindex = -1; + formatetc.tymed = TYMED_HGLOBAL; + expected_method_list = methods_olerender_format; + trace("OleCreate with OLERENDER_FORMAT:\n"); + hr = OleCreate(&CLSID_Equation3, &IID_IOleObject, OLERENDER_FORMAT, &formatetc, (IOleClientSite *)0xdeadbeef, pStorage, (void **)&pObject); + ok_ole_success(hr, "OleCreate"); + IOleObject_Release(pObject); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + expected_method_list = methods_olerender_asis; + trace("OleCreate with OLERENDER_ASIS:\n"); + hr = OleCreate(&CLSID_Equation3, &IID_IOleObject, OLERENDER_ASIS, NULL, NULL, pStorage, (void **)&pObject); + ok_ole_success(hr, "OleCreate"); + IOleObject_Release(pObject); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + runnable = NULL; + expected_method_list = methods_olerender_draw_no_runnable; + trace("OleCreate with OLERENDER_DRAW (no IOlObjectRunnable):\n"); + hr = OleCreate(&CLSID_Equation3, &IID_IOleObject, OLERENDER_DRAW, NULL, NULL, pStorage, (void **)&pObject); + ok_ole_success(hr, "OleCreate"); + IOleObject_Release(pObject); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + runnable = &OleObjectRunnable; + cache = NULL; + expected_method_list = methods_olerender_draw_no_cache; + trace("OleCreate with OLERENDER_DRAW (no IOlObjectRunnable):\n"); + hr = OleCreate(&CLSID_Equation3, &IID_IOleObject, OLERENDER_DRAW, NULL, NULL, pStorage, (void **)&pObject); + ok_ole_success(hr, "OleCreate"); + IOleObject_Release(pObject); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + trace("end\n"); +} + +static void test_OleLoad(IStorage *pStorage) +{ + HRESULT hr; + IOleObject *pObject; + + static const char *methods_oleload[] = + { + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_QueryInterface", + "OleObject_AddRef", + "OleObject_GetMiscStatus", + "OleObject_QueryInterface", + "OleObjectPersistStg_AddRef", + "OleObjectPersistStg_Load", + "OleObjectPersistStg_Release", + "OleObject_SetClientSite", + "OleObject_Release", + "OleObject_QueryInterface", + "OleObject_Release", + NULL + }; + + expected_method_list = methods_oleload; + trace("OleLoad:\n"); + hr = OleLoad(pStorage, &IID_IOleObject, (IOleClientSite *)0xdeadbeef, (void **)&pObject); + ok_ole_success(hr, "OleLoad"); + IOleObject_Release(pObject); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); +} + +static BOOL STDMETHODCALLTYPE draw_continue(ULONG_PTR param) +{ + CHECK_EXPECTED_METHOD("draw_continue"); + return TRUE; +} + +static HRESULT WINAPI AdviseSink_QueryInterface(IAdviseSink *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IAdviseSink) || IsEqualIID(riid, &IID_IUnknown)) + { + *ppv = iface; + IUnknown_AddRef(iface); + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI AdviseSink_AddRef(IAdviseSink *iface) +{ + return 2; +} + +static ULONG WINAPI AdviseSink_Release(IAdviseSink *iface) +{ + return 1; +} + + +static void WINAPI AdviseSink_OnDataChange( + IAdviseSink *iface, + FORMATETC *pFormatetc, + STGMEDIUM *pStgmed) +{ + CHECK_EXPECTED_METHOD("AdviseSink_OnDataChange"); +} + +static void WINAPI AdviseSink_OnViewChange( + IAdviseSink *iface, + DWORD dwAspect, + LONG lindex) +{ + CHECK_EXPECTED_METHOD("AdviseSink_OnViewChange"); +} + +static void WINAPI AdviseSink_OnRename( + IAdviseSink *iface, + IMoniker *pmk) +{ + CHECK_EXPECTED_METHOD("AdviseSink_OnRename"); +} + +static void WINAPI AdviseSink_OnSave(IAdviseSink *iface) +{ + CHECK_EXPECTED_METHOD("AdviseSink_OnSave"); +} + +static void WINAPI AdviseSink_OnClose(IAdviseSink *iface) +{ + CHECK_EXPECTED_METHOD("AdviseSink_OnClose"); +} + +static const IAdviseSinkVtbl AdviseSinkVtbl = +{ + AdviseSink_QueryInterface, + AdviseSink_AddRef, + AdviseSink_Release, + AdviseSink_OnDataChange, + AdviseSink_OnViewChange, + AdviseSink_OnRename, + AdviseSink_OnSave, + AdviseSink_OnClose +}; + +static IAdviseSink AdviseSink = { &AdviseSinkVtbl }; + +static HRESULT WINAPI DataObject_QueryInterface( + IDataObject* iface, + REFIID riid, + void** ppvObject) +{ + if (IsEqualIID(riid, &IID_IDataObject) || IsEqualIID(riid, &IID_IUnknown)) + { + *ppvObject = iface; + return S_OK; + } + *ppvObject = NULL; + return S_OK; +} + +static ULONG WINAPI DataObject_AddRef( + IDataObject* iface) +{ + return 2; +} + +static ULONG WINAPI DataObject_Release( + IDataObject* iface) +{ + return 1; +} + +static HRESULT WINAPI DataObject_GetData( + IDataObject* iface, + LPFORMATETC pformatetcIn, + STGMEDIUM* pmedium) +{ + CHECK_EXPECTED_METHOD("DataObject_GetData"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObject_GetDataHere( + IDataObject* iface, + LPFORMATETC pformatetc, + STGMEDIUM* pmedium) +{ + CHECK_EXPECTED_METHOD("DataObject_GetDataHere"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObject_QueryGetData( + IDataObject* iface, + LPFORMATETC pformatetc) +{ + CHECK_EXPECTED_METHOD("DataObject_QueryGetData"); + return S_OK; +} + +static HRESULT WINAPI DataObject_GetCanonicalFormatEtc( + IDataObject* iface, + LPFORMATETC pformatectIn, + LPFORMATETC pformatetcOut) +{ + CHECK_EXPECTED_METHOD("DataObject_GetCanonicalFormatEtc"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObject_SetData( + IDataObject* iface, + LPFORMATETC pformatetc, + STGMEDIUM* pmedium, + BOOL fRelease) +{ + CHECK_EXPECTED_METHOD("DataObject_SetData"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObject_EnumFormatEtc( + IDataObject* iface, + DWORD dwDirection, + IEnumFORMATETC** ppenumFormatEtc) +{ + CHECK_EXPECTED_METHOD("DataObject_EnumFormatEtc"); + return E_NOTIMPL; +} + +static HRESULT WINAPI DataObject_DAdvise( + IDataObject* iface, + FORMATETC* pformatetc, + DWORD advf, + IAdviseSink* pAdvSink, + DWORD* pdwConnection) +{ + CHECK_EXPECTED_METHOD("DataObject_DAdvise"); + *pdwConnection = 1; + return S_OK; +} + +static HRESULT WINAPI DataObject_DUnadvise( + IDataObject* iface, + DWORD dwConnection) +{ + CHECK_EXPECTED_METHOD("DataObject_DUnadvise"); + return S_OK; +} + +static HRESULT WINAPI DataObject_EnumDAdvise( + IDataObject* iface, + IEnumSTATDATA** ppenumAdvise) +{ + CHECK_EXPECTED_METHOD("DataObject_EnumDAdvise"); + return OLE_E_ADVISENOTSUPPORTED; +} + +static IDataObjectVtbl DataObjectVtbl = +{ + DataObject_QueryInterface, + DataObject_AddRef, + DataObject_Release, + DataObject_GetData, + DataObject_GetDataHere, + DataObject_QueryGetData, + DataObject_GetCanonicalFormatEtc, + DataObject_SetData, + DataObject_EnumFormatEtc, + DataObject_DAdvise, + DataObject_DUnadvise, + DataObject_EnumDAdvise +}; + +static IDataObject DataObject = { &DataObjectVtbl }; + +static void test_data_cache(void) +{ + HRESULT hr; + IOleCache2 *pOleCache; + IStorage *pStorage; + IPersistStorage *pPS; + IViewObject *pViewObject; + IOleCacheControl *pOleCacheControl; + FORMATETC fmtetc; + STGMEDIUM stgmedium; + DWORD dwConnection; + DWORD dwFreeze; + RECTL rcBounds; + HDC hdcMem; + CLSID clsid; + char szSystemDir[MAX_PATH]; + WCHAR wszPath[MAX_PATH]; + static const WCHAR wszShell32[] = {'\\','s','h','e','l','l','3','2','.','d','l','l',0}; + + static const char *methods_cacheinitnew[] = + { + "AdviseSink_OnViewChange", + "AdviseSink_OnViewChange", + "draw_continue", + "DataObject_DAdvise", + "DataObject_DAdvise", + "DataObject_DUnadvise", + "DataObject_DUnadvise", + NULL + }; + static const char *methods_cacheload[] = + { + "AdviseSink_OnViewChange", + "draw_continue", + "draw_continue", + "draw_continue", + "DataObject_GetData", + "DataObject_GetData", + "DataObject_GetData", + NULL + }; + + GetSystemDirectory(szSystemDir, sizeof(szSystemDir)/sizeof(szSystemDir[0])); + + expected_method_list = methods_cacheinitnew; + + fmtetc.cfFormat = CF_METAFILEPICT; + fmtetc.dwAspect = DVASPECT_ICON; + fmtetc.lindex = -1; + fmtetc.ptd = NULL; + fmtetc.tymed = TYMED_MFPICT; + + hr = StgCreateDocfile(NULL, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &pStorage); + ok_ole_success(hr, "StgCreateDocfile"); + + /* Test with new data */ + + hr = CreateDataCache(NULL, &CLSID_NULL, &IID_IOleCache2, (LPVOID *)&pOleCache); + ok_ole_success(hr, "CreateDataCache"); + + hr = IOleCache_QueryInterface(pOleCache, &IID_IPersistStorage, (LPVOID *)&pPS); + ok_ole_success(hr, "IOleCache_QueryInterface(IID_IPersistStorage)"); + hr = IOleCache_QueryInterface(pOleCache, &IID_IViewObject, (LPVOID *)&pViewObject); + ok_ole_success(hr, "IOleCache_QueryInterface(IID_IViewObject)"); + hr = IOleCache_QueryInterface(pOleCache, &IID_IOleCacheControl, (LPVOID *)&pOleCacheControl); + ok_ole_success(hr, "IOleCache_QueryInterface(IID_IOleCacheControl)"); + + hr = IViewObject_SetAdvise(pViewObject, DVASPECT_ICON, ADVF_PRIMEFIRST, &AdviseSink); + ok_ole_success(hr, "IViewObject_SetAdvise"); + + hr = IPersistStorage_InitNew(pPS, pStorage); + ok_ole_success(hr, "IPersistStorage_InitNew"); + + hr = IPersistStorage_IsDirty(pPS); + ok_ole_success(hr, "IPersistStorage_IsDirty"); + + hr = IPersistStorage_GetClassID(pPS, &clsid); + ok_ole_success(hr, "IPersistStorage_GetClassID"); + ok(IsEqualCLSID(&clsid, &IID_NULL), "clsid should be blank\n"); + + hr = IOleCache_Uncache(pOleCache, 0xdeadbeef); + ok(hr == OLE_E_NOCONNECTION, "IOleCache_Uncache with invalid value should return OLE_E_NOCONNECTION instead of 0x%x\n", hr); + + hr = IOleCache_Cache(pOleCache, NULL, 0, &dwConnection); + ok(hr == E_INVALIDARG, "IOleCache_Cache with NULL fmtetc should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = IOleCache_Cache(pOleCache, NULL, 0, NULL); + ok(hr == E_INVALIDARG, "IOleCache_Cache with NULL pdwConnection should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + for (fmtetc.cfFormat = CF_TEXT; fmtetc.cfFormat < CF_MAX; fmtetc.cfFormat++) + { + int i; + fmtetc.dwAspect = DVASPECT_THUMBNAIL; + for (i = 0; i < 7; i++) + { + fmtetc.tymed = 1 << i; + hr = IOleCache_Cache(pOleCache, &fmtetc, 0, &dwConnection); + if ((fmtetc.cfFormat == CF_METAFILEPICT && fmtetc.tymed == TYMED_MFPICT) || + (fmtetc.cfFormat == CF_BITMAP && fmtetc.tymed == TYMED_GDI) || + (fmtetc.cfFormat == CF_DIB && fmtetc.tymed == TYMED_HGLOBAL) || + (fmtetc.cfFormat == CF_ENHMETAFILE && fmtetc.tymed == TYMED_ENHMF)) + ok(hr == S_OK, "IOleCache_Cache cfFormat = %d, tymed = %d should have returned S_OK instead of 0x%08x\n", + fmtetc.cfFormat, fmtetc.tymed, hr); + else if (fmtetc.tymed == TYMED_HGLOBAL) + ok(hr == CACHE_S_FORMATETC_NOTSUPPORTED, + "IOleCache_Cache cfFormat = %d, tymed = %d should have returned CACHE_S_FORMATETC_NOTSUPPORTED instead of 0x%08x\n", + fmtetc.cfFormat, fmtetc.tymed, hr); + else + ok(hr == DV_E_TYMED, "IOleCache_Cache cfFormat = %d, tymed = %d should have returned DV_E_TYMED instead of 0x%08x\n", + fmtetc.cfFormat, fmtetc.tymed, hr); + if (SUCCEEDED(hr)) + { + hr = IOleCache_Uncache(pOleCache, dwConnection); + ok_ole_success(hr, "IOleCache_Uncache"); + } + } + } + + fmtetc.cfFormat = CF_BITMAP; + fmtetc.dwAspect = DVASPECT_THUMBNAIL; + fmtetc.tymed = TYMED_GDI; + hr = IOleCache_Cache(pOleCache, &fmtetc, 0, &dwConnection); + ok_ole_success(hr, "IOleCache_Cache"); + + fmtetc.cfFormat = 0; + fmtetc.dwAspect = DVASPECT_ICON; + fmtetc.tymed = TYMED_MFPICT; + hr = IOleCache_Cache(pOleCache, &fmtetc, 0, &dwConnection); + ok_ole_success(hr, "IOleCache_Cache"); + + MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszPath, sizeof(wszPath)/sizeof(wszPath[0])); + memcpy(wszPath+lstrlenW(wszPath), wszShell32, sizeof(wszShell32)); + + fmtetc.cfFormat = CF_METAFILEPICT; + stgmedium.tymed = TYMED_MFPICT; + U(stgmedium).hMetaFilePict = OleMetafilePictFromIconAndLabel( + LoadIcon(NULL, IDI_APPLICATION), wszPath, wszPath, 0); + stgmedium.pUnkForRelease = NULL; + + fmtetc.dwAspect = DVASPECT_CONTENT; + hr = IOleCache_SetData(pOleCache, &fmtetc, &stgmedium, FALSE); + ok(hr == OLE_E_BLANK, "IOleCache_SetData for aspect not in cache should have return OLE_E_BLANK instead of 0x%08x\n", hr); + + fmtetc.dwAspect = DVASPECT_ICON; + hr = IOleCache_SetData(pOleCache, &fmtetc, &stgmedium, FALSE); + ok_ole_success(hr, "IOleCache_SetData"); + + hr = IViewObject_Freeze(pViewObject, DVASPECT_ICON, -1, NULL, &dwFreeze); + todo_wine { + ok_ole_success(hr, "IViewObject_Freeze"); + hr = IViewObject_Freeze(pViewObject, DVASPECT_CONTENT, -1, NULL, &dwFreeze); + ok(hr == OLE_E_BLANK, "IViewObject_Freeze with uncached aspect should have returned OLE_E_BLANK instead of 0x%08x\n", hr); + } + + rcBounds.left = 0; + rcBounds.top = 0; + rcBounds.right = 100; + rcBounds.bottom = 100; + hdcMem = CreateCompatibleDC(NULL); + + hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef); + ok_ole_success(hr, "IViewObject_Draw"); + + hr = IViewObject_Draw(pViewObject, DVASPECT_CONTENT, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef); + ok(hr == OLE_E_BLANK, "IViewObject_Draw with uncached aspect should have returned OLE_E_BLANK instead of 0x%08x\n", hr); + + DeleteDC(hdcMem); + + hr = IOleCacheControl_OnRun(pOleCacheControl, &DataObject); + todo_wine { + ok_ole_success(hr, "IOleCacheControl_OnRun"); + } + + hr = IPersistStorage_Save(pPS, pStorage, TRUE); + ok_ole_success(hr, "IPersistStorage_Save"); + + hr = IPersistStorage_SaveCompleted(pPS, NULL); + ok_ole_success(hr, "IPersistStorage_SaveCompleted"); + + hr = IPersistStorage_IsDirty(pPS); + ok(hr == S_FALSE, "IPersistStorage_IsDirty should have returned S_FALSE instead of 0x%x\n", hr); + + IPersistStorage_Release(pPS); + IViewObject_Release(pViewObject); + IOleCache_Release(pOleCache); + IOleCacheControl_Release(pOleCacheControl); + + todo_wine { + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + } + + /* Test with loaded data */ + trace("Testing loaded data with CreateDataCache:\n"); + expected_method_list = methods_cacheload; + + hr = CreateDataCache(NULL, &CLSID_NULL, &IID_IOleCache2, (LPVOID *)&pOleCache); + ok_ole_success(hr, "CreateDataCache"); + + hr = IOleCache_QueryInterface(pOleCache, &IID_IPersistStorage, (LPVOID *)&pPS); + ok_ole_success(hr, "IOleCache_QueryInterface(IID_IPersistStorage)"); + hr = IOleCache_QueryInterface(pOleCache, &IID_IViewObject, (LPVOID *)&pViewObject); + ok_ole_success(hr, "IOleCache_QueryInterface(IID_IViewObject)"); + + hr = IViewObject_SetAdvise(pViewObject, DVASPECT_ICON, ADVF_PRIMEFIRST, &AdviseSink); + ok_ole_success(hr, "IViewObject_SetAdvise"); + + hr = IPersistStorage_Load(pPS, pStorage); + ok_ole_success(hr, "IPersistStorage_Load"); + + hr = IPersistStorage_IsDirty(pPS); + ok(hr == S_FALSE, "IPersistStorage_IsDirty should have returned S_FALSE instead of 0x%x\n", hr); + + fmtetc.cfFormat = 0; + fmtetc.dwAspect = DVASPECT_ICON; + fmtetc.lindex = -1; + fmtetc.ptd = NULL; + fmtetc.tymed = TYMED_MFPICT; + hr = IOleCache_Cache(pOleCache, &fmtetc, 0, &dwConnection); + ok(hr == CACHE_S_SAMECACHE, "IOleCache_Cache with already loaded data format type should return CACHE_S_SAMECACHE instead of 0x%x\n", hr); + + rcBounds.left = 0; + rcBounds.top = 0; + rcBounds.right = 100; + rcBounds.bottom = 100; + hdcMem = CreateCompatibleDC(NULL); + + hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef); + ok_ole_success(hr, "IViewObject_Draw"); + + hr = IViewObject_Draw(pViewObject, DVASPECT_CONTENT, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef); + ok(hr == OLE_E_BLANK, "IViewObject_Draw with uncached aspect should have returned OLE_E_BLANK instead of 0x%08x\n", hr); + + /* unload the cached storage object, causing it to be reloaded */ + hr = IOleCache2_DiscardCache(pOleCache, DISCARDCACHE_NOSAVE); + ok_ole_success(hr, "IOleCache2_DiscardCache"); + hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef); + ok_ole_success(hr, "IViewObject_Draw"); + + /* unload the cached storage object, but don't allow it to be reloaded */ + hr = IPersistStorage_HandsOffStorage(pPS); + ok_ole_success(hr, "IPersistStorage_HandsOffStorage"); + hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef); + ok_ole_success(hr, "IViewObject_Draw"); + hr = IOleCache2_DiscardCache(pOleCache, DISCARDCACHE_NOSAVE); + ok_ole_success(hr, "IOleCache2_DiscardCache"); + hr = IViewObject_Draw(pViewObject, DVASPECT_ICON, -1, NULL, NULL, NULL, hdcMem, &rcBounds, NULL, draw_continue, 0xdeadbeef); + ok(hr == OLE_E_BLANK, "IViewObject_Draw with uncached aspect should have returned OLE_E_BLANK instead of 0x%08x\n", hr); + + DeleteDC(hdcMem); + + todo_wine { + hr = IOleCache_InitCache(pOleCache, &DataObject); + ok(hr == CACHE_E_NOCACHE_UPDATED, "IOleCache_InitCache should have returned CACHE_E_NOCACHE_UPDATED instead of 0x%08x\n", hr); + } + + IPersistStorage_Release(pPS); + IViewObject_Release(pViewObject); + IOleCache_Release(pOleCache); + + todo_wine { + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + } + + IStorage_Release(pStorage); + ReleaseStgMedium(&stgmedium); +} + +static void test_default_handler(void) +{ + HRESULT hr; + IOleObject *pObject; + IRunnableObject *pRunnableObject; + IOleClientSite *pClientSite; + IDataObject *pDataObject; + SIZEL sizel; + DWORD dwStatus; + CLSID clsid; + LPOLESTR pszUserType; + LOGPALETTE palette; + DWORD dwAdvConn; + IMoniker *pMoniker; + FORMATETC fmtetc; + IOleInPlaceObject *pInPlaceObj; + IEnumOLEVERB *pEnumVerbs; + static const WCHAR wszUnknown[] = {'U','n','k','n','o','w','n',0}; + static const WCHAR wszHostName[] = {'W','i','n','e',' ','T','e','s','t',' ','P','r','o','g','r','a','m',0}; + static const WCHAR wszDelim[] = {'!',0}; + + hr = CoCreateInstance(&CLSID_WineTest, NULL, CLSCTX_INPROC_HANDLER, &IID_IOleObject, (void **)&pObject); + ok(hr == REGDB_E_CLASSNOTREG, "CoCreateInstance should have failed with REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + + hr = OleCreateDefaultHandler(&CLSID_WineTest, NULL, &IID_IOleObject, (void **)&pObject); + ok_ole_success(hr, "OleCreateDefaultHandler"); + + hr = IOleObject_QueryInterface(pObject, &IID_IOleInPlaceObject, (void **)&pInPlaceObj); + ok(hr == E_NOINTERFACE, "IOleObject_QueryInterface(&IID_IOleInPlaceObject) should return E_NOINTERFACE instead of 0x%08x\n", hr); + + hr = IOleObject_Advise(pObject, &AdviseSink, &dwAdvConn); + ok_ole_success(hr, "IOleObject_Advise"); + + hr = IOleObject_Close(pObject, OLECLOSE_NOSAVE); + ok_ole_success(hr, "IOleObject_Close"); + + /* FIXME: test IOleObject_EnumAdvise */ + + hr = IOleObject_EnumVerbs(pObject, &pEnumVerbs); + ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_EnumVerbs should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + + hr = IOleObject_GetClientSite(pObject, &pClientSite); + ok_ole_success(hr, "IOleObject_GetClientSite"); + + hr = IOleObject_GetClipboardData(pObject, 0, &pDataObject); + ok(hr == OLE_E_NOTRUNNING, + "IOleObject_GetClipboardData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", + hr); + + hr = IOleObject_GetExtent(pObject, DVASPECT_CONTENT, &sizel); + ok(hr == OLE_E_BLANK, "IOleObject_GetExtent should have returned OLE_E_BLANK instead of 0x%08x\n", + hr); + + hr = IOleObject_GetMiscStatus(pObject, DVASPECT_CONTENT, &dwStatus); + todo_wine + ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_GetMiscStatus should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + + hr = IOleObject_GetUserClassID(pObject, &clsid); + ok_ole_success(hr, "IOleObject_GetUserClassID"); + ok(IsEqualCLSID(&clsid, &CLSID_WineTest), "clsid != CLSID_WineTest\n"); + + hr = IOleObject_GetUserType(pObject, USERCLASSTYPE_FULL, &pszUserType); + todo_wine { + ok_ole_success(hr, "IOleObject_GetUserType"); + ok(!lstrcmpW(pszUserType, wszUnknown), "Retrieved user type was wrong\n"); + } + + hr = IOleObject_InitFromData(pObject, NULL, TRUE, 0); + ok(hr == OLE_E_NOTRUNNING, "IOleObject_InitFromData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr); + + hr = IOleObject_IsUpToDate(pObject); + ok(hr == OLE_E_NOTRUNNING, "IOleObject_IsUpToDate should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr); + + palette.palNumEntries = 1; + palette.palVersion = 2; + memset(&palette.palPalEntry[0], 0, sizeof(palette.palPalEntry[0])); + hr = IOleObject_SetColorScheme(pObject, &palette); + ok(hr == OLE_E_NOTRUNNING, "IOleObject_SetColorScheme should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr); + + sizel.cx = sizel.cy = 0; + hr = IOleObject_SetExtent(pObject, DVASPECT_CONTENT, &sizel); + + hr = IOleObject_SetHostNames(pObject, wszHostName, NULL); + ok_ole_success(hr, "IOleObject_SetHostNames"); + + hr = CreateItemMoniker(wszDelim, wszHostName, &pMoniker); + ok_ole_success(hr, "CreateItemMoniker"); + hr = IOleObject_SetMoniker(pObject, OLEWHICHMK_CONTAINER, pMoniker); + ok_ole_success(hr, "IOleObject_SetMoniker"); + IMoniker_Release(pMoniker); + + hr = IOleObject_GetMoniker(pObject, OLEGETMONIKER_ONLYIFTHERE, OLEWHICHMK_CONTAINER, &pMoniker); + ok(hr == E_FAIL, "IOleObject_GetMoniker should have returned E_FAIL instead of 0x%08x\n", hr); + + hr = IOleObject_Update(pObject); + todo_wine + ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_Update should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + + hr = IOleObject_QueryInterface(pObject, &IID_IDataObject, (void **)&pDataObject); + ok_ole_success(hr, "IOleObject_QueryInterface"); + + fmtetc.cfFormat = CF_TEXT; + fmtetc.ptd = NULL; + fmtetc.dwAspect = DVASPECT_CONTENT; + fmtetc.lindex = -1; + fmtetc.tymed = TYMED_NULL; + hr = IDataObject_DAdvise(pDataObject, &fmtetc, 0, &AdviseSink, &dwAdvConn); + ok_ole_success(hr, "IDataObject_DAdvise"); + + fmtetc.cfFormat = CF_ENHMETAFILE; + fmtetc.ptd = NULL; + fmtetc.dwAspect = DVASPECT_CONTENT; + fmtetc.lindex = -1; + fmtetc.tymed = TYMED_ENHMF; + hr = IDataObject_DAdvise(pDataObject, &fmtetc, 0, &AdviseSink, &dwAdvConn); + ok_ole_success(hr, "IDataObject_DAdvise"); + + fmtetc.cfFormat = CF_ENHMETAFILE; + fmtetc.ptd = NULL; + fmtetc.dwAspect = DVASPECT_CONTENT; + fmtetc.lindex = -1; + fmtetc.tymed = TYMED_ENHMF; + hr = IDataObject_QueryGetData(pDataObject, &fmtetc); + todo_wine + ok(hr == OLE_E_NOTRUNNING, "IDataObject_QueryGetData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr); + + fmtetc.cfFormat = CF_TEXT; + fmtetc.ptd = NULL; + fmtetc.dwAspect = DVASPECT_CONTENT; + fmtetc.lindex = -1; + fmtetc.tymed = TYMED_NULL; + hr = IDataObject_QueryGetData(pDataObject, &fmtetc); + todo_wine + ok(hr == OLE_E_NOTRUNNING, "IDataObject_QueryGetData should have returned OLE_E_NOTRUNNING instead of 0x%08x\n", hr); + + hr = IOleObject_QueryInterface(pObject, &IID_IRunnableObject, (void **)&pRunnableObject); + ok_ole_success(hr, "IOleObject_QueryInterface"); + + hr = IRunnableObject_SetContainedObject(pRunnableObject, TRUE); + ok_ole_success(hr, "IRunnableObject_SetContainedObject"); + + hr = IRunnableObject_Run(pRunnableObject, NULL); + ok(hr == REGDB_E_CLASSNOTREG, "IOleObject_Run should have returned REGDB_E_CLASSNOTREG instead of 0x%08x\n", hr); + + hr = IOleObject_Close(pObject, OLECLOSE_NOSAVE); + ok_ole_success(hr, "IOleObject_Close"); + + IRunnableObject_Release(pRunnableObject); + IOleObject_Release(pObject); +} + +void test_runnable(void) +{ + static const char *methods_query_runnable[] = + { + "OleObject_QueryInterface", + "OleObjectRunnable_AddRef", + "OleObjectRunnable_IsRunning", + "OleObjectRunnable_Release", + NULL + }; + + static const char *methods_no_runnable[] = + { + "OleObject_QueryInterface", + NULL + }; + + IOleObject *object = (IOleObject *)&OleObject; + + expected_method_list = methods_query_runnable; + ok(OleIsRunning(object), "Object should be running\n"); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + g_isRunning = FALSE; + expected_method_list = methods_query_runnable; + ok(OleIsRunning(object) == FALSE, "Object should not be running\n"); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + g_showRunnable = FALSE; /* QueryInterface(IID_IRunnableObject, ...) will fail */ + expected_method_list = methods_no_runnable; + ok(OleIsRunning(object), "Object without IRunnableObject should be running\n"); + ok(!*expected_method_list, "Method sequence starting from %s not called\n", *expected_method_list); + + g_isRunning = TRUE; + g_showRunnable = TRUE; +} + +START_TEST(ole2) +{ + DWORD dwRegister; + IStorage *pStorage; + STATSTG statstg; + HRESULT hr; + + CoInitialize(NULL); + + hr = CoRegisterClassObject(&CLSID_Equation3, (IUnknown *)&OleObjectCF, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &dwRegister); + ok_ole_success(hr, "CoRegisterClassObject"); + + hr = StgCreateDocfile(NULL, STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &pStorage); + ok_ole_success(hr, "StgCreateDocfile"); + + test_OleCreate(pStorage); + + hr = IStorage_Stat(pStorage, &statstg, STATFLAG_NONAME); + ok_ole_success(hr, "IStorage_Stat"); + ok(IsEqualCLSID(&CLSID_Equation3, &statstg.clsid), "Wrong CLSID in storage\n"); + + test_OleLoad(pStorage); + + IStorage_Release(pStorage); + + hr = CoRevokeClassObject(dwRegister); + ok_ole_success(hr, "CoRevokeClassObject"); + + test_data_cache(); + test_default_handler(); + test_runnable(); + + CoUninitialize(); +} diff --git a/rostests/winetests/ole32/ole32.rbuild b/rostests/winetests/ole32/ole32.rbuild new file mode 100644 index 00000000000..526c15fb5ac --- /dev/null +++ b/rostests/winetests/ole32/ole32.rbuild @@ -0,0 +1,29 @@ + + + + . + 0x600 + 0x600 + wine + oleaut32 + ole32 + user32 + gdi32 + advapi32 + kernel32 + uuid + ntdll + clipboard.c + compobj.c + dragdrop.c + errorinfo.c + hglobalstream.c + marshal.c + moniker.c + ole2.c + propvariant.c + stg_prop.c + storage32.c + usrmarshal.c + testlist.c + diff --git a/rostests/winetests/ole32/propvariant.c b/rostests/winetests/ole32/propvariant.c new file mode 100644 index 00000000000..f1cbc76ecdc --- /dev/null +++ b/rostests/winetests/ole32/propvariant.c @@ -0,0 +1,242 @@ +/* + * PropVariant Tests + * + * Copyright 2004 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "windows.h" + +#include "wine/test.h" + +/* invalid in all versions */ +#define PROP_INV 255 +/* valid in v0 and above (NT4+) */ +#define PROP_V0 0 +/* valid in v1 and above (Win2k+) */ +#define PROP_V1 1 +/* valid in v1a and above (WinXP+) */ +#define PROP_V1A 2 + +struct valid_mapping +{ + BYTE simple; + BYTE with_array; + BYTE with_vector; + BYTE byref; +} valid_types[] = +{ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_EMPTY */ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_NULL */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_I2 */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_I4 */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_R4 */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_R8 */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_CY */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_DATE */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_BSTR */ + { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 }, /* VT_DISPATCH */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_ERROR */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_BOOL */ + { PROP_V1 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_VARIANT */ + { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 }, /* VT_UNKNOWN */ + { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 }, /* VT_DECIMAL */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 15 */ + { PROP_V1 , PROP_V1 , PROP_V1 , PROP_V1 }, /* VT_I1 */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_UI1 */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_UI2 */ + { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 }, /* VT_UI4 */ + { PROP_V0 , PROP_V1A, PROP_V0 , PROP_V1A }, /* VT_I8 */ + { PROP_V0 , PROP_V1A, PROP_V0 , PROP_V1A }, /* VT_UI8 */ + { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 }, /* VT_INT */ + { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 }, /* VT_UINT */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_VOID */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_HRESULT */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_PTR */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_SAFEARRAY */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_CARRAY */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_USERDEFINED */ + { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV }, /* VT_LPSTR */ + { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV }, /* VT_LPWSTR */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 32 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 33 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 34 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 35 */ + { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 }, /* VT_RECORD */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_INT_PTR */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_UINT_PTR */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 39 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 40 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 41 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 42 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 43 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 44 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 45 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 46 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 47 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 48 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 49 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 50 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 51 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 52 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 53 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 54 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 55 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 56 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 57 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 58 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 59 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 60 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 61 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 62 */ + { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 63 */ + { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV }, /* VT_FILETIME */ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_BLOB */ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STREAM */ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STORAGE */ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STREAMED_OBJECT */ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STORED_OBJECT */ + { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_BLOB_OBJECT */ + { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV } /* VT_CF */ +}; + +static const char* wine_vtypes[VT_CLSID+1] = +{ + "VT_EMPTY","VT_NULL","VT_I2","VT_I4","VT_R4","VT_R8","VT_CY","VT_DATE", + "VT_BSTR","VT_DISPATCH","VT_ERROR","VT_BOOL","VT_VARIANT","VT_UNKNOWN", + "VT_DECIMAL","15","VT_I1","VT_UI1","VT_UI2","VT_UI4","VT_I8","VT_UI8", + "VT_INT","VT_UINT","VT_VOID","VT_HRESULT","VT_PTR","VT_SAFEARRAY", + "VT_CARRAY","VT_USERDEFINED","VT_LPSTR","VT_LPWSTR","32","33","34","35", + "VT_RECORD","VT_INT_PTR","VT_UINT_PTR","39","40","41","42","43","44","45", + "46","47","48","49","50","51","52","53","54","55","56","57","58","59","60", + "61","62","63","VT_FILETIME","VT_BLOB","VT_STREAM","VT_STORAGE", + "VT_STREAMED_OBJECT","VT_STORED_OBJECT","VT_BLOB_OBJECT","VT_CF","VT_CLSID" +}; + +static void test_validtypes(void) +{ + PROPVARIANT propvar; + HRESULT hr; + unsigned int i; + BYTE version; + + memset(&propvar, 0, sizeof(propvar)); + + /* detect version */ + propvar.vt = VT_I2 | VT_ARRAY; + hr = PropVariantClear(&propvar); + if (hr == S_OK) + { + propvar.vt = VT_I8 | VT_ARRAY; + hr = PropVariantClear(&propvar); + if (hr == S_OK) + { + version = PROP_V1A; + trace("detected prop variant v1a\n"); + } + else + { + version = PROP_V1; + trace("detected prop variant v1\n"); + } + } + else + { + version = PROP_V0; + trace("detected prop variant v0\n"); + } + + for (i = 0; i < sizeof(valid_types)/sizeof(valid_types[0]); i++) + { + BOOL expected_result; + + propvar.vt = i; + hr = PropVariantClear(&propvar); + expected_result = (valid_types[i].simple <= version ? TRUE : FALSE); + ok(expected_result == !(hr == STG_E_INVALIDPARAMETER), + "PropVariantClear(%s) should have returned 0x%08x, but returned 0x%08x\n", + wine_vtypes[i], + expected_result ? S_OK : STG_E_INVALIDPARAMETER, hr); + + propvar.vt = i | VT_ARRAY; + hr = PropVariantClear(&propvar); + expected_result = (valid_types[i].with_array <= version ? TRUE : FALSE); + ok(expected_result == !(hr == STG_E_INVALIDPARAMETER), + "PropVariantClear(%s|VT_ARRAY) should have returned 0x%08x, but returned 0x%08x\n", + wine_vtypes[i], + expected_result ? S_OK : STG_E_INVALIDPARAMETER, hr); + + propvar.vt = i | VT_VECTOR; + hr = PropVariantClear(&propvar); + expected_result = (valid_types[i].with_vector <= version ? TRUE : FALSE); + ok(expected_result == !(hr == STG_E_INVALIDPARAMETER), + "PropVariantClear(%s|VT_VECTOR) should have returned 0x%08x, but returned 0x%08x\n", + wine_vtypes[i], + expected_result ? S_OK : STG_E_INVALIDPARAMETER, hr); + + propvar.vt = i | VT_BYREF; + hr = PropVariantClear(&propvar); + expected_result = (valid_types[i].byref <= version ? TRUE : FALSE); + ok(expected_result == !(hr == STG_E_INVALIDPARAMETER), + "PropVariantClear(%s|VT_BYREF) should have returned 0x%08x, but returned 0x%08x\n", + wine_vtypes[i], + expected_result ? S_OK : STG_E_INVALIDPARAMETER, hr); + } +} + +static void test_copy(void) +{ + static char szTestString[] = "Test String"; + static WCHAR wszTestString[] = {'T','e','s','t',' ','S','t','r','i','n','g',0}; + PROPVARIANT propvarSrc; + PROPVARIANT propvarDst; + HRESULT hr; + + propvarSrc.vt = VT_BSTR; + U(propvarSrc).bstrVal = SysAllocString(wszTestString); + + hr = PropVariantCopy(&propvarDst, &propvarSrc); + ok(hr == S_OK, "PropVariantCopy(...VT_BSTR...) failed\n"); + ok(!lstrcmpW(U(propvarSrc).bstrVal, U(propvarDst).bstrVal), "BSTR not copied properly\n"); + hr = PropVariantClear(&propvarSrc); + ok(hr == S_OK, "PropVariantClear(...VT_BSTR...) failed\n"); + hr = PropVariantClear(&propvarDst); + ok(hr == S_OK, "PropVariantClear(...VT_BSTR...) failed\n"); + + propvarSrc.vt = VT_LPWSTR; + U(propvarSrc).pwszVal = wszTestString; + hr = PropVariantCopy(&propvarDst, &propvarSrc); + ok(hr == S_OK, "PropVariantCopy(...VT_LPWSTR...) failed\n"); + ok(!lstrcmpW(U(propvarSrc).pwszVal, U(propvarDst).pwszVal), "Wide string not copied properly\n"); + hr = PropVariantClear(&propvarDst); + ok(hr == S_OK, "PropVariantClear(...VT_LPWSTR...) failed\n"); + memset(&propvarSrc, 0, sizeof(propvarSrc)); + + propvarSrc.vt = VT_LPSTR; + U(propvarSrc).pszVal = szTestString; + hr = PropVariantCopy(&propvarDst, &propvarSrc); + ok(hr == S_OK, "PropVariantCopy(...VT_LPSTR...) failed\n"); + ok(!strcmp(U(propvarSrc).pszVal, U(propvarDst).pszVal), "String not copied properly\n"); + hr = PropVariantClear(&propvarDst); + ok(hr == S_OK, "PropVariantClear(...VT_LPSTR...) failed\n"); + memset(&propvarSrc, 0, sizeof(propvarSrc)); +} + +START_TEST(propvariant) +{ + test_validtypes(); + test_copy(); +} diff --git a/rostests/winetests/ole32/stg_prop.c b/rostests/winetests/ole32/stg_prop.c new file mode 100644 index 00000000000..5a007e48105 --- /dev/null +++ b/rostests/winetests/ole32/stg_prop.c @@ -0,0 +1,529 @@ +/* IPropertyStorage unit tests + * Copyright 2005 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#include +#define COBJMACROS +#include "objbase.h" +#include "wine/test.h" + +#ifndef PID_BEHAVIOR +#define PID_BEHAVIOR 0x80000003 +#endif + +static HRESULT (WINAPI *pFmtIdToPropStgName)(const FMTID *, LPOLESTR); +static HRESULT (WINAPI *pPropStgNameToFmtId)(const LPOLESTR, FMTID *); +static HRESULT (WINAPI *pStgCreatePropSetStg)(IStorage *, DWORD, IPropertySetStorage **); + +static void init_function_pointers(void) +{ + HMODULE hmod = GetModuleHandleA("ole32.dll"); + pFmtIdToPropStgName = (void*)GetProcAddress(hmod, "FmtIdToPropStgName"); + pPropStgNameToFmtId = (void*)GetProcAddress(hmod, "PropStgNameToFmtId"); + pStgCreatePropSetStg = (void*)GetProcAddress(hmod, "StgCreatePropSetStg"); +} +/* FIXME: this creates an ANSI storage, try to find conditions under which + * Unicode translation fails + */ +static void testProps(void) +{ + static const WCHAR szDot[] = { '.',0 }; + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static WCHAR propName[] = { 'p','r','o','p',0 }; + static char val[] = "l33t auth0r"; + WCHAR filename[MAX_PATH]; + HRESULT hr; + IStorage *storage = NULL; + IPropertySetStorage *propSetStorage = NULL; + IPropertyStorage *propertyStorage = NULL; + PROPSPEC spec; + PROPVARIANT var; + CLIPDATA clipdata; + unsigned char clipcontent[] = "foobar"; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + DeleteFileW(filename); + + hr = StgCreateDocfile(filename, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + + if(!pStgCreatePropSetStg) + { + IStorage_Release(storage); + DeleteFileW(filename); + return; + } + hr = pStgCreatePropSetStg(storage, 0, &propSetStorage); + ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr); + + hr = IPropertySetStorage_Create(propSetStorage, + &FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI, + STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, + &propertyStorage); + ok(hr == S_OK, "IPropertySetStorage_Create failed: 0x%08x\n", hr); + + hr = IPropertyStorage_WriteMultiple(propertyStorage, 0, NULL, NULL, 0); + ok(hr == S_OK, "WriteMultiple with 0 args failed: 0x%08x\n", hr); + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, NULL, NULL, 0); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr); + + /* test setting one that I can't set */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_DICTIONARY; + var.vt = VT_I4; + U(var).lVal = 1; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == STG_E_INVALIDPARAMETER, + "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr); + + /* test setting one by name with an invalid propidNameFirst */ + spec.ulKind = PRSPEC_LPWSTR; + U(spec).lpwstr = (LPOLESTR)propName; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, + PID_DICTIONARY); + ok(hr == STG_E_INVALIDPARAMETER, + "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr); + + /* test setting behavior (case-sensitive) */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_BEHAVIOR; + U(var).lVal = 1; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == STG_E_INVALIDPARAMETER, + "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr); + + /* set one by value.. */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_FIRST_USABLE; + U(var).lVal = 1; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + + /* set one by name */ + spec.ulKind = PRSPEC_LPWSTR; + U(spec).lpwstr = (LPOLESTR)propName; + U(var).lVal = 2; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, + PID_FIRST_USABLE); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + + /* set a string value */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PIDSI_AUTHOR; + var.vt = VT_LPSTR; + U(var).pszVal = val; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + + /* set a clipboard value */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PIDSI_THUMBNAIL; + var.vt = VT_CF; + clipdata.cbSize = sizeof clipcontent + sizeof (ULONG); + clipdata.ulClipFmt = CF_ENHMETAFILE; + clipdata.pClipData = clipcontent; + U(var).pclipdata = &clipdata; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + + + /* check reading */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 0, NULL, NULL); + ok(hr == S_FALSE, "ReadMultiple with 0 args failed: 0x%08x\n", hr); + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, NULL, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr); + /* read by propid */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_FIRST_USABLE; + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I4 && U(var).lVal == 1, + "Didn't get expected type or value for property (got type %d, value %ld)\n", + var.vt, U(var).lVal); + /* read by name */ + spec.ulKind = PRSPEC_LPWSTR; + U(spec).lpwstr = (LPOLESTR)propName; + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I4 && U(var).lVal == 2, + "Didn't get expected type or value for property (got type %d, value %ld)\n", + var.vt, U(var).lVal); + /* read string value */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PIDSI_AUTHOR; + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val), + "Didn't get expected type or value for property (got type %d, value %s)\n", + var.vt, U(var).pszVal); + + /* read clipboard format */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PIDSI_THUMBNAIL; + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(SUCCEEDED(hr), "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_CF, "variant type wrong\n"); + ok(U(var).pclipdata->ulClipFmt == CF_ENHMETAFILE, + "clipboard type wrong\n"); + ok(U(var).pclipdata->cbSize == sizeof clipcontent + sizeof (ULONG), + "clipboard size wrong\n"); + ok(!memcmp(U(var).pclipdata->pClipData, clipcontent, sizeof clipcontent), + "clipboard contents wrong\n"); + ok(S_OK == PropVariantClear(&var), "failed to clear variant\n"); + + /* check deleting */ + hr = IPropertyStorage_DeleteMultiple(propertyStorage, 0, NULL); + ok(hr == S_OK, "DeleteMultiple with 0 args failed: 0x%08x\n", hr); + hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr); + /* contrary to what the docs say, you can't delete the dictionary */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_DICTIONARY; + hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec); + ok(hr == STG_E_INVALIDPARAMETER, + "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr); + /* now delete the first value.. */ + U(spec).propid = PID_FIRST_USABLE; + hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec); + ok(hr == S_OK, "DeleteMultiple failed: 0x%08x\n", hr); + /* and check that it's no longer readable */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_FALSE, "Expected S_FALSE, got 0x%08x\n", hr); + + hr = IPropertyStorage_Commit(propertyStorage, STGC_DEFAULT); + ok(hr == S_OK, "Commit failed: 0x%08x\n", hr); + + /* check reverting */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_FIRST_USABLE; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + hr = IPropertyStorage_Revert(propertyStorage); + ok(hr == S_OK, "Revert failed: 0x%08x\n", hr); + /* now check that it's still not there */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_FALSE, "Expected S_FALSE, got 0x%08x\n", hr); + /* set an integer value again */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_FIRST_USABLE; + var.vt = VT_I4; + U(var).lVal = 1; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + /* commit it */ + hr = IPropertyStorage_Commit(propertyStorage, STGC_DEFAULT); + ok(hr == S_OK, "Commit failed: 0x%08x\n", hr); + /* set it to a string value */ + var.vt = VT_LPSTR; + U(var).pszVal = (LPSTR)val; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + /* revert it */ + hr = IPropertyStorage_Revert(propertyStorage); + ok(hr == S_OK, "Revert failed: 0x%08x\n", hr); + /* Oddly enough, there's no guarantee that a successful revert actually + * implies the value wasn't saved. Maybe transactional mode needs to be + * used for that? + */ + + IPropertyStorage_Release(propertyStorage); + propertyStorage = NULL; + IPropertySetStorage_Release(propSetStorage); + propSetStorage = NULL; + IStorage_Release(storage); + storage = NULL; + + /* now open it again */ + hr = StgOpenStorage(filename, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + NULL, 0, &storage); + ok(hr == S_OK, "StgOpenStorage failed: 0x%08x\n", hr); + + hr = pStgCreatePropSetStg(storage, 0, &propSetStorage); + ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr); + + hr = IPropertySetStorage_Open(propSetStorage, &FMTID_SummaryInformation, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &propertyStorage); + ok(hr == S_OK, "IPropertySetStorage_Open failed: 0x%08x\n", hr); + + /* check properties again */ + spec.ulKind = PRSPEC_LPWSTR; + U(spec).lpwstr = (LPOLESTR)propName; + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I4 && U(var).lVal == 2, + "Didn't get expected type or value for property (got type %d, value %ld)\n", + var.vt, U(var).lVal); + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PIDSI_AUTHOR; + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val), + "Didn't get expected type or value for property (got type %d, value %s)\n", + var.vt, U(var).pszVal); + + IPropertyStorage_Release(propertyStorage); + IPropertySetStorage_Release(propSetStorage); + IStorage_Release(storage); + + DeleteFileW(filename); +} + +static void testCodepage(void) +{ + static const WCHAR szDot[] = { '.',0 }; + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static CHAR aval[] = "hi"; + static WCHAR wval[] = { 'h','i',0 }; + HRESULT hr; + IStorage *storage = NULL; + IPropertySetStorage *propSetStorage = NULL; + IPropertyStorage *propertyStorage = NULL; + PROPSPEC spec; + PROPVARIANT var; + WCHAR fileName[MAX_PATH]; + + if(!GetTempFileNameW(szDot, szPrefix, 0, fileName)) + return; + + hr = StgCreateDocfile(fileName, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + + if(!pStgCreatePropSetStg) + { + IStorage_Release(storage); + DeleteFileW(fileName); + return; + } + hr = pStgCreatePropSetStg(storage, 0, &propSetStorage); + ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr); + + hr = IPropertySetStorage_Create(propSetStorage, + &FMTID_SummaryInformation, NULL, PROPSETFLAG_DEFAULT, + STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, + &propertyStorage); + ok(hr == S_OK, "IPropertySetStorage_Create failed: 0x%08x\n", hr); + + PropVariantInit(&var); + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_CODEPAGE; + /* check code page before it's been explicitly set */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I2 && U(var).iVal == 1200, + "Didn't get expected type or value for property\n"); + /* Set the code page to ascii */ + var.vt = VT_I2; + U(var).iVal = 1252; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + /* check code page */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I2 && U(var).iVal == 1252, + "Didn't get expected type or value for property\n"); + /* Set code page to Unicode */ + U(var).iVal = 1200; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + /* check code page */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I2 && U(var).iVal == 1200, + "Didn't get expected type or value for property\n"); + /* Set a string value */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_FIRST_USABLE; + var.vt = VT_LPSTR; + U(var).pszVal = aval; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "hi"), + "Didn't get expected type or value for property\n"); + /* This seemingly non-sensical test is to show that the string is indeed + * interpreted according to the current system code page, not according to + * the property set's code page. (If the latter were true, the whole + * string would be maintained. As it is, only the first character is.) + */ + U(var).pszVal = (LPSTR)wval; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "h"), + "Didn't get expected type or value for property\n"); + /* now that a property's been set, you can't change the code page */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_CODEPAGE; + var.vt = VT_I2; + U(var).iVal = 1200; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == STG_E_INVALIDPARAMETER, + "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr); + + IPropertyStorage_Release(propertyStorage); + IPropertySetStorage_Release(propSetStorage); + IStorage_Release(storage); + + DeleteFileW(fileName); + + /* same tests, but with PROPSETFLAG_ANSI */ + hr = StgCreateDocfile(fileName, + STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage); + ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr); + + hr = pStgCreatePropSetStg(storage, 0, &propSetStorage); + ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr); + + hr = IPropertySetStorage_Create(propSetStorage, + &FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI, + STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE, + &propertyStorage); + ok(hr == S_OK, "IPropertySetStorage_Create failed: 0x%08x\n", hr); + + /* check code page before it's been explicitly set */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I2 && U(var).iVal == 1252, + "Didn't get expected type or value for property\n"); + /* Set code page to Unicode */ + U(var).iVal = 1200; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + /* check code page */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_I2 && U(var).iVal == 1200, + "Didn't get expected type or value for property\n"); + /* This test is commented out for documentation. It fails under Wine, + * and I expect it would under Windows as well, yet it succeeds. There's + * obviously something about string conversion I don't understand. + */ + if(0) { + static unsigned char strVal[] = { 0x81, 0xff, 0x04, 0 }; + /* Set code page to 950 (Traditional Chinese) */ + U(var).iVal = 950; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + /* Try writing an invalid string: lead byte 0x81 is unused in Traditional + * Chinese. + */ + spec.ulKind = PRSPEC_PROPID; + U(spec).propid = PID_FIRST_USABLE; + var.vt = VT_LPSTR; + U(var).pszVal = (LPSTR)strVal; + hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0); + ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr); + /* Check returned string */ + hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var); + ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr); + ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, (LPCSTR)strVal), + "Didn't get expected type or value for property\n"); + } + + IPropertyStorage_Release(propertyStorage); + IPropertySetStorage_Release(propSetStorage); + IStorage_Release(storage); + + DeleteFileW(fileName); +} + +static void testFmtId(void) +{ + WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y', + 'I','n','f','o','r','m','a','t','i','o','n',0 }; + WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t', + 'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n', + 0 }; + WCHAR szIID_IPropSetStg[] = { 5,'0','j','a','a','a','a','a', + 'a','A','a','a','a','a','a','d','a','A','a','a','a','a','a','a','a','G', + 'c',0 }; + WCHAR name[32]; + FMTID fmtid; + HRESULT hr; + + if (pFmtIdToPropStgName) { + hr = pFmtIdToPropStgName(NULL, name); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr); + hr = pFmtIdToPropStgName(&FMTID_SummaryInformation, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr); + hr = pFmtIdToPropStgName(&FMTID_SummaryInformation, name); + ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr); + ok(!memcmp(name, szSummaryInfo, (lstrlenW(szSummaryInfo) + 1) * + sizeof(WCHAR)), "Got wrong name for FMTID_SummaryInformation\n"); + hr = pFmtIdToPropStgName(&FMTID_DocSummaryInformation, name); + ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr); + ok(!memcmp(name, szDocSummaryInfo, (lstrlenW(szDocSummaryInfo) + 1) * + sizeof(WCHAR)), "Got wrong name for FMTID_DocSummaryInformation\n"); + hr = pFmtIdToPropStgName(&FMTID_UserDefinedProperties, name); + ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr); + ok(!memcmp(name, szDocSummaryInfo, (lstrlenW(szDocSummaryInfo) + 1) * + sizeof(WCHAR)), "Got wrong name for FMTID_DocSummaryInformation\n"); + hr = pFmtIdToPropStgName(&IID_IPropertySetStorage, name); + ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr); + ok(!memcmp(name, szIID_IPropSetStg, (lstrlenW(szIID_IPropSetStg) + 1) * + sizeof(WCHAR)), "Got wrong name for IID_IPropertySetStorage\n"); + } + + if(pPropStgNameToFmtId) { + /* test args first */ + hr = pPropStgNameToFmtId(NULL, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr); + hr = pPropStgNameToFmtId(NULL, &fmtid); + ok(hr == STG_E_INVALIDNAME, "Expected STG_E_INVALIDNAME, got 0x%08x\n", + hr); + hr = pPropStgNameToFmtId(szDocSummaryInfo, NULL); + ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr); + /* test the known format IDs */ + hr = pPropStgNameToFmtId(szSummaryInfo, &fmtid); + ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr); + ok(!memcmp(&fmtid, &FMTID_SummaryInformation, sizeof(fmtid)), + "Got unexpected FMTID, expected FMTID_SummaryInformation\n"); + hr = pPropStgNameToFmtId(szDocSummaryInfo, &fmtid); + ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr); + ok(!memcmp(&fmtid, &FMTID_DocSummaryInformation, sizeof(fmtid)), + "Got unexpected FMTID, expected FMTID_DocSummaryInformation\n"); + /* test another GUID */ + hr = pPropStgNameToFmtId(szIID_IPropSetStg, &fmtid); + ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr); + ok(!memcmp(&fmtid, &IID_IPropertySetStorage, sizeof(fmtid)), + "Got unexpected FMTID, expected IID_IPropertySetStorage\n"); + /* now check case matching */ + CharUpperW(szDocSummaryInfo + 1); + hr = pPropStgNameToFmtId(szDocSummaryInfo, &fmtid); + ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr); + ok(!memcmp(&fmtid, &FMTID_DocSummaryInformation, sizeof(fmtid)), + "Got unexpected FMTID, expected FMTID_DocSummaryInformation\n"); + CharUpperW(szIID_IPropSetStg + 1); + hr = pPropStgNameToFmtId(szIID_IPropSetStg, &fmtid); + ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr); + ok(!memcmp(&fmtid, &IID_IPropertySetStorage, sizeof(fmtid)), + "Got unexpected FMTID, expected IID_IPropertySetStorage\n"); + } +} + +START_TEST(stg_prop) +{ + init_function_pointers(); + testProps(); + testCodepage(); + testFmtId(); +} diff --git a/rostests/winetests/ole32/storage32.c b/rostests/winetests/ole32/storage32.c new file mode 100644 index 00000000000..7dc633c9fb7 --- /dev/null +++ b/rostests/winetests/ole32/storage32.c @@ -0,0 +1,1013 @@ +/* + * Unit tests for OLE storage + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#define COBJMACROS + +#include +#include "wine/test.h" + +#include "ole2.h" +#include "objidl.h" +#include "initguid.h" + +DEFINE_GUID( test_stg_cls, 0x88888888, 0x0425, 0x0000, 0,0,0,0,0,0,0,0); + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) + +static void test_hglobal_storage_stat(void) +{ + ILockBytes *ilb = NULL; + IStorage *stg = NULL; + HRESULT r; + STATSTG stat; + DWORD mode, refcount; + + r = CreateILockBytesOnHGlobal( NULL, TRUE, &ilb ); + ok( r == S_OK, "CreateILockBytesOnHGlobal failed\n"); + + mode = STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE;/*0x1012*/ + r = StgCreateDocfileOnILockBytes( ilb, mode, 0, &stg ); + ok( r == S_OK, "StgCreateDocfileOnILockBytes failed\n"); + + r = WriteClassStg( stg, &test_stg_cls ); + ok( r == S_OK, "WriteClassStg failed\n"); + + memset( &stat, 0, sizeof stat ); + r = IStorage_Stat( stg, &stat, 0 ); + + ok( stat.pwcsName == NULL, "storage name not null\n"); + ok( stat.type == 1, "type is wrong\n"); + ok( stat.grfMode == 0x12, "grf mode is incorrect\n"); + ok( !memcmp(&stat.clsid, &test_stg_cls, sizeof test_stg_cls), "CLSID is wrong\n"); + + refcount = IStorage_Release( stg ); + ok( refcount == 0, "IStorage refcount is wrong\n"); + refcount = ILockBytes_Release( ilb ); + ok( refcount == 0, "ILockBytes refcount is wrong\n"); +} + +static void test_create_storage_modes(void) +{ + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static const WCHAR szDot[] = { '.',0 }; + WCHAR filename[MAX_PATH]; + IStorage *stg = NULL; + HRESULT r; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + DeleteFileW(filename); + + /* test with some invalid parameters */ + r = StgCreateDocfile( NULL, 0, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, 0, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_CREATE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_CREATE | STGM_READWRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, NULL); + ok(r==STG_E_INVALIDPOINTER, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 1, &stg); + ok(r==STG_E_INVALIDPARAMETER, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_DENY_WRITE | STGM_READWRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READ, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_PRIORITY, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + + /* StgCreateDocfile seems to be very particular about the flags it accepts */ + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED | STGM_WRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED | 8, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED | 0x80, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED | 0x800, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED | 0x8000, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED | 0x80000, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED | 0x800000, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + ok(stg == NULL, "stg was set\n"); + + /* check what happens if the file already exists (which is how it's meant to be used) */ + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==STG_E_FILEALREADYEXISTS, "StgCreateDocfile wrong error\n"); /* FAILIFTHERE is default */ + r = StgCreateDocfile( filename, STGM_READ, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); /* need at least readmode and sharemode */ + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_DENY_WRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_SHARE_DENY_NONE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile failed\n"); + r = StgCreateDocfile( filename, STGM_SHARE_DENY_NONE | STGM_TRANSACTED, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile failed\n"); + r = StgCreateDocfile( filename, STGM_SHARE_DENY_NONE | STGM_READWRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile failed\n"); + r = StgCreateDocfile( filename, STGM_SHARE_DENY_NONE | STGM_WRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile failed\n"); + r = StgCreateDocfile( filename, STGM_SHARE_DENY_WRITE | STGM_WRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile failed\n"); + r = StgCreateDocfile( filename, STGM_SHARE_DENY_WRITE | STGM_READ, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile wrong error\n"); + r = StgCreateDocfile( filename, STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READ, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile wrong error\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED |STGM_FAILIFTHERE, 0, &stg); + ok(r==STG_E_FILEALREADYEXISTS, "StgCreateDocfile wrong error\n"); + r = StgCreateDocfile( filename, STGM_SHARE_EXCLUSIVE | STGM_WRITE, 0, &stg); + ok(r==STG_E_FILEALREADYEXISTS, "StgCreateDocfile wrong error\n"); + + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_DENY_WRITE | STGM_READWRITE, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgCreateDocfile succeeded\n"); + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + + r = StgCreateDocfile( filename, STGM_CREATE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + + /* test the way excel uses StgCreateDocFile */ + r = StgCreateDocfile( filename, STGM_TRANSACTED|STGM_CREATE|STGM_SHARE_DENY_WRITE|STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile the excel way failed\n"); + if(r == S_OK) + { + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + } + + /* and the way windows media uses it ... */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_DENY_NONE | STGM_READWRITE | STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile the windows media way failed\n"); + if (r == S_OK) + { + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + } + + /* looks like we need STGM_TRANSACTED or STGM_CREATE */ + r = StgCreateDocfile( filename, STGM_TRANSACTED|STGM_SHARE_DENY_WRITE|STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile the excel way failed\n"); + if(r == S_OK) + { + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + } + + r = StgCreateDocfile( filename, STGM_TRANSACTED|STGM_CREATE|STGM_SHARE_DENY_WRITE|STGM_WRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile the excel way failed\n"); + if(r == S_OK) + { + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + } + + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &stg); + ok(r==S_OK, "StgCreateDocfile the powerpoint way failed\n"); + if(r == S_OK) + { + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); + } + + /* test the way msi uses StgCreateDocfile */ + r = StgCreateDocfile( filename, STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg); + ok(r==S_OK, "StgCreateDocFile failed\n"); + r = IStorage_Release(stg); + ok(r == 0, "storage not released\n"); + ok(DeleteFileW(filename), "failed to delete file\n"); +} + +static void test_storage_stream(void) +{ + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static const WCHAR szDot[] = { '.',0 }; + static const WCHAR longname[] = { + '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 + }; + WCHAR filename[MAX_PATH]; + IStorage *stg = NULL; + HRESULT r; + IStream *stm = NULL; + IStream *stm2 = NULL; + ULONG count = 0; + LARGE_INTEGER pos; + ULARGE_INTEGER p; + unsigned char buffer[0x100]; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + DeleteFileW(filename); + + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* try create some invalid streams */ + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 1, 0, &stm ); + ok(r==STG_E_INVALIDPARAMETER, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 1, &stm ); + ok(r==STG_E_INVALIDPARAMETER, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, NULL ); + ok(r==STG_E_INVALIDPOINTER, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, NULL, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==STG_E_INVALIDNAME, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, longname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==STG_E_INVALIDNAME, "IStorage->CreateStream wrong error, got %d GetLastError()=%d\n", r, GetLastError()); + r = IStorage_CreateStream(stg, stmname, STGM_READWRITE, 0, 0, &stm ); + ok(r==STG_E_INVALIDFLAG, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, stmname, STGM_READ, 0, 0, &stm ); + ok(r==STG_E_INVALIDFLAG, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, stmname, STGM_WRITE, 0, 0, &stm ); + ok(r==STG_E_INVALIDFLAG, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_DENY_NONE | STGM_READWRITE, 0, 0, &stm ); + ok(r==STG_E_INVALIDFLAG, "IStorage->CreateStream wrong error\n"); + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_DENY_NONE | STGM_READ, 0, 0, &stm ); + ok(r==STG_E_INVALIDFLAG, "IStorage->CreateStream wrong error\n"); + + /* now really create a stream and delete it */ + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + r = IStream_Release(stm); + ok(r == 0, "wrong ref count\n"); + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==STG_E_FILEALREADYEXISTS, "IStorage->CreateStream failed\n"); + r = IStorage_DestroyElement(stg,stmname); + ok(r==S_OK, "IStorage->DestroyElement failed\n"); + + /* create a stream and write to it */ + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Clone(stm, &stm2); + ok(r==S_OK, "failed to clone stream\n"); + + r = IStream_Write(stm, NULL, 0, NULL ); + ok(r==STG_E_INVALIDPOINTER, "IStream->Write wrong error\n"); + r = IStream_Write(stm, "Hello\n", 0, NULL ); + ok(r==S_OK, "failed to write stream\n"); + r = IStream_Write(stm, "Hello\n", 0, &count ); + ok(r==S_OK, "failed to write stream\n"); + r = IStream_Write(stm, "Hello\n", 6, &count ); + ok(r==S_OK, "failed to write stream\n"); + r = IStream_Commit(stm, STGC_DEFAULT ); + ok(r==S_OK, "failed to commit stream\n"); + r = IStream_Commit(stm, STGC_DEFAULT ); + ok(r==S_OK, "failed to commit stream\n"); + + /* seek round a bit, reset the stream size */ + pos.QuadPart = 0; + r = IStream_Seek(stm, pos, 3, &p ); + ok(r==STG_E_INVALIDFUNCTION, "IStream->Seek returned wrong error\n"); + r = IStream_Seek(stm, pos, STREAM_SEEK_SET, NULL); + ok(r==S_OK, "failed to seek stream\n"); + r = IStream_Seek(stm, pos, STREAM_SEEK_SET, &p ); + ok(r==S_OK, "failed to seek stream\n"); + r = IStream_SetSize(stm,p); + ok(r==S_OK, "failed to set pos\n"); + pos.QuadPart = 10; + r = IStream_Seek(stm, pos, STREAM_SEEK_SET, &p ); + ok(r==S_OK, "failed to seek stream\n"); + ok(p.QuadPart == 10, "at wrong place\n"); + pos.QuadPart = 0; + r = IStream_Seek(stm, pos, STREAM_SEEK_END, &p ); + ok(r==S_OK, "failed to seek stream\n"); + ok(p.QuadPart == 0, "at wrong place\n"); + r = IStream_Read(stm, buffer, sizeof buffer, &count ); + ok(r==S_OK, "failed to set pos\n"); + ok(count == 0, "read bytes from empty stream\n"); + + /* wrap up */ + r = IStream_Release(stm2); + ok(r == 0, "wrong ref count\n"); + + /* create a stream and write to it */ + r = IStorage_CreateStream(stg, stmname, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm2 ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Seek(stm, pos, STREAM_SEEK_SET, &p); + ok(r==STG_E_REVERTED, "overwritten stream should return STG_E_REVERTED instead of 0x%08x\n", r); + + r = IStream_Release(stm2); + ok(r == 0, "wrong ref count\n"); + r = IStream_Release(stm); + ok(r == 0, "wrong ref count\n"); + + r = IStorage_Release(stg); + ok(r == 0, "wrong ref count\n"); + r = DeleteFileW(filename); + ok(r, "file should exist\n"); +} + +static BOOL touch_file(LPCWSTR filename) +{ + HANDLE file; + + file = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file==INVALID_HANDLE_VALUE) + return FALSE; + CloseHandle(file); + return TRUE; +} + +static BOOL is_zero_length(LPCWSTR filename) +{ + HANDLE file; + DWORD len; + + file = CreateFileW(filename, GENERIC_READ, 0, NULL, + OPEN_EXISTING, 0, NULL); + if (file==INVALID_HANDLE_VALUE) + return FALSE; + len = GetFileSize(file, NULL); + CloseHandle(file); + return len == 0; +} + +static BOOL is_existing_file(LPCWSTR filename) +{ + HANDLE file; + + file = CreateFileW(filename, GENERIC_READ, 0, NULL, + OPEN_EXISTING, 0, NULL); + if (file==INVALID_HANDLE_VALUE) + return FALSE; + CloseHandle(file); + return TRUE; +} + +static void test_open_storage(void) +{ + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static const WCHAR szNonExist[] = { 'n','o','n','e','x','i','s','t',0 }; + static const WCHAR szDot[] = { '.',0 }; + WCHAR filename[MAX_PATH]; + IStorage *stg = NULL, *stg2 = NULL; + HRESULT r; + DWORD stgm; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + /* try opening a zero length file - it should stay zero length */ + DeleteFileW(filename); + touch_file(filename); + stgm = STGM_NOSCRATCH | STGM_TRANSACTED | STGM_SHARE_DENY_WRITE | STGM_READWRITE; + r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg); + ok(r==STG_E_FILEALREADYEXISTS, "StgOpenStorage didn't fail\n"); + + stgm = STGM_SHARE_EXCLUSIVE | STGM_READWRITE; + r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg); + ok(r==STG_E_FILEALREADYEXISTS, "StgOpenStorage didn't fail\n"); + ok(is_zero_length(filename), "file length changed\n"); + + DeleteFileW(filename); + + /* try opening a nonexistent file - it should not create it */ + stgm = STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE; + r = StgOpenStorage( filename, NULL, stgm, NULL, 0, &stg); + ok(r!=S_OK, "StgOpenStorage failed: 0x%08x\n", r); + if (r==S_OK) IStorage_Release(stg); + ok(!is_existing_file(filename), "StgOpenStorage should not create a file\n"); + DeleteFileW(filename); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + IStorage_Release(stg); + + r = StgOpenStorage( filename, NULL, 0, NULL, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgOpenStorage wrong error\n"); + r = StgOpenStorage( NULL, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); + ok(r==STG_E_INVALIDNAME, "StgOpenStorage wrong error\n"); + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, NULL); + ok(r==STG_E_INVALIDPOINTER, "StgOpenStorage wrong error\n"); + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 1, &stg); + ok(r==STG_E_INVALIDPARAMETER, "StgOpenStorage wrong error\n"); + r = StgOpenStorage( szNonExist, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, &stg); + ok(r==STG_E_FILENOTFOUND, "StgOpenStorage failed\n"); + r = StgOpenStorage( filename, NULL, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgOpenStorage failed\n"); + r = StgOpenStorage( filename, NULL, STGM_SHARE_DENY_NONE | STGM_READ, NULL, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgOpenStorage failed\n"); + r = StgOpenStorage( filename, NULL, STGM_SHARE_DENY_READ | STGM_READ, NULL, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgOpenStorage failed\n"); + r = StgOpenStorage( filename, NULL, STGM_SHARE_DENY_WRITE | STGM_READWRITE, NULL, 0, &stg); + ok(r==STG_E_INVALIDFLAG, "StgOpenStorage failed\n"); + + /* open it for real */ + r = StgOpenStorage( filename, NULL, STGM_SHARE_DENY_NONE | STGM_READ | STGM_TRANSACTED, NULL, 0, &stg); /* XLViewer 97/2000 */ + ok(r==S_OK, "StgOpenStorage failed\n"); + if(stg) + { + r = IStorage_Release(stg); + ok(r == 0, "wrong ref count\n"); + } + + r = StgOpenStorage( filename, NULL, STGM_SHARE_DENY_WRITE | STGM_READ, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed\n"); + if(stg) + { + r = IStorage_Release(stg); + ok(r == 0, "wrong ref count\n"); + } + + /* test the way word opens its custom dictionary */ + r = StgOpenStorage( filename, NULL, STGM_NOSCRATCH | STGM_TRANSACTED | + STGM_SHARE_DENY_WRITE | STGM_READWRITE, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed\n"); + if(stg) + { + r = IStorage_Release(stg); + ok(r == 0, "wrong ref count\n"); + } + + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed\n"); + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, &stg2); + ok(r==STG_E_SHAREVIOLATION, "StgOpenStorage failed\n"); + if(stg) + { + r = IStorage_Release(stg); + ok(r == 0, "wrong ref count\n"); + } + + /* now try write to a storage file we opened read-only */ + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE | STGM_READ, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed\n"); + if(stg) + { + static const WCHAR stmname[] = { 'w','i','n','e','t','e','s','t',0}; + IStream *stm = NULL; + IStorage *stg2 = NULL; + + r = IStorage_CreateStream( stg, stmname, STGM_WRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &stm ); + ok(r == STG_E_ACCESSDENIED, "CreateStream should fail\n"); + r = IStorage_CreateStorage( stg, stmname, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stg2); + ok(r == STG_E_ACCESSDENIED, "CreateStream should fail\n"); + + r = IStorage_Release(stg); + ok(r == 0, "wrong ref count\n"); + } + + /* open like visio 2003 */ + stg = NULL; + r = StgOpenStorage( filename, NULL, STGM_PRIORITY | STGM_SHARE_DENY_NONE, NULL, 0, &stg); + ok(r == S_OK, "should succeed\n"); + if (stg) + IStorage_Release(stg); + + /* test other sharing modes with STGM_PRIORITY */ + stg = NULL; + r = StgOpenStorage( filename, NULL, STGM_PRIORITY | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); + ok(r == S_OK, "should succeed\n"); + if (stg) + IStorage_Release(stg); + + stg = NULL; + r = StgOpenStorage( filename, NULL, STGM_PRIORITY | STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + ok(r == S_OK, "should succeed\n"); + if (stg) + IStorage_Release(stg); + + stg = NULL; + r = StgOpenStorage( filename, NULL, STGM_PRIORITY | STGM_SHARE_DENY_READ, NULL, 0, &stg); + ok(r == S_OK, "should succeed\n"); + if (stg) + IStorage_Release(stg); + + /* open like Project 2003 */ + stg = NULL; + r = StgOpenStorage( filename, NULL, STGM_PRIORITY, NULL, 0, &stg); + ok(r == S_OK, "should succeed\n"); + r = StgOpenStorage( filename, NULL, STGM_PRIORITY, NULL, 0, &stg2); + ok(r == S_OK, "should succeed\n"); + if (stg2) + IStorage_Release(stg2); + if (stg) + IStorage_Release(stg); + + stg = NULL; + r = StgOpenStorage( filename, NULL, STGM_PRIORITY | STGM_READWRITE, NULL, 0, &stg); + ok(r == STG_E_INVALIDFLAG, "should fail\n"); + + r = StgOpenStorage( filename, NULL, STGM_TRANSACTED | STGM_PRIORITY, NULL, 0, &stg); + ok(r == STG_E_INVALIDFLAG, "should fail\n"); + + r = StgOpenStorage( filename, NULL, STGM_SIMPLE | STGM_PRIORITY, NULL, 0, &stg); + ok(r == STG_E_INVALIDFLAG, "should fail\n"); + + r = StgOpenStorage( filename, NULL, STGM_DELETEONRELEASE | STGM_PRIORITY, NULL, 0, &stg); + ok(r == STG_E_INVALIDFUNCTION, "should fail\n"); + + r = StgOpenStorage( filename, NULL, STGM_NOSCRATCH | STGM_PRIORITY, NULL, 0, &stg); + ok(r == STG_E_INVALIDFLAG, "should fail\n"); + + r = StgOpenStorage( filename, NULL, STGM_NOSNAPSHOT | STGM_PRIORITY, NULL, 0, &stg); + ok(r == STG_E_INVALIDFLAG, "should fail\n"); + + r = DeleteFileW(filename); + ok(r, "file didn't exist\n"); +} + +static void test_storage_suminfo(void) +{ + static const WCHAR szDot[] = { '.',0 }; + static const WCHAR szPrefix[] = { 's','t','g',0 }; + WCHAR filename[MAX_PATH]; + IStorage *stg = NULL; + IPropertySetStorage *propset = NULL; + IPropertyStorage *ps = NULL; + HRESULT r; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + DeleteFileW(filename); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + r = IStorage_QueryInterface( stg, &IID_IPropertySetStorage, (LPVOID) &propset ); + ok(r == S_OK, "query interface failed\n"); + + /* delete it */ + r = IPropertySetStorage_Delete( propset, &FMTID_SummaryInformation ); + ok(r == STG_E_FILENOTFOUND, "deleted property set storage\n"); + + r = IPropertySetStorage_Open( propset, &FMTID_SummaryInformation, + STGM_READ | STGM_SHARE_EXCLUSIVE, &ps ); + ok(r == STG_E_FILENOTFOUND, "opened property set storage\n"); + + r = IPropertySetStorage_Create( propset, &FMTID_SummaryInformation, NULL, 0, + STGM_READ | STGM_SHARE_EXCLUSIVE, &ps ); + ok(r == STG_E_INVALIDFLAG, "created property set storage\n"); + + r = IPropertySetStorage_Create( propset, &FMTID_SummaryInformation, NULL, 0, + STGM_READ, &ps ); + ok(r == STG_E_INVALIDFLAG, "created property set storage\n"); + + r = IPropertySetStorage_Create( propset, &FMTID_SummaryInformation, NULL, 0, 0, &ps ); + ok(r == STG_E_INVALIDFLAG, "created property set storage\n"); + + r = IPropertySetStorage_Create( propset, &FMTID_SummaryInformation, NULL, 0, + STGM_WRITE|STGM_SHARE_EXCLUSIVE, &ps ); + ok(r == STG_E_INVALIDFLAG, "created property set storage\n"); + + r = IPropertySetStorage_Create( propset, &FMTID_SummaryInformation, NULL, 0, + STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, &ps ); + ok(r == STG_E_INVALIDFLAG, "created property set storage\n"); + + /* now try really creating a property set */ + r = IPropertySetStorage_Create( propset, &FMTID_SummaryInformation, NULL, 0, + STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ps ); + ok(r == S_OK, "failed to create property set storage\n"); + + if( ps ) + IPropertyStorage_Release(ps); + + /* now try creating the same thing again */ + r = IPropertySetStorage_Create( propset, &FMTID_SummaryInformation, NULL, 0, + STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ps ); + ok(r == S_OK, "failed to create property set storage\n"); + if( ps ) + IPropertyStorage_Release(ps); + + /* should be able to open it */ + r = IPropertySetStorage_Open( propset, &FMTID_SummaryInformation, + STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ps); + ok(r == S_OK, "open failed\n"); + if(r == S_OK) + IPropertyStorage_Release(ps); + + /* delete it */ + r = IPropertySetStorage_Delete( propset, &FMTID_SummaryInformation ); + ok(r == S_OK, "failed to delete property set storage\n"); + + /* try opening with an invalid FMTID */ + r = IPropertySetStorage_Open( propset, NULL, + STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ps); + ok(r == E_INVALIDARG, "open succeeded\n"); + if(r == S_OK) + IPropertyStorage_Release(ps); + + /* try a bad guid */ + r = IPropertySetStorage_Open( propset, &IID_IStorage, + STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ps); + ok(r == STG_E_FILENOTFOUND, "open succeeded\n"); + if(r == S_OK) + IPropertyStorage_Release(ps); + + + /* try some invalid flags */ + r = IPropertySetStorage_Open( propset, &FMTID_SummaryInformation, + STGM_CREATE | STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ps); + ok(r == STG_E_INVALIDFLAG, "open succeeded\n"); + if(r == S_OK) + IPropertyStorage_Release(ps); + + /* after deleting it, it should be gone */ + r = IPropertySetStorage_Open( propset, &FMTID_SummaryInformation, + STGM_READWRITE|STGM_SHARE_EXCLUSIVE, &ps); + ok(r == STG_E_FILENOTFOUND, "open failed\n"); + if(r == S_OK) + IPropertyStorage_Release(ps); + + r = IPropertySetStorage_Release( propset ); + ok(r == 1, "ref count wrong\n"); + + r = IStorage_Release(stg); + ok(r == 0, "ref count wrong\n"); + + DeleteFileW(filename); +} + +static void test_storage_refcount(void) +{ + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static const WCHAR szDot[] = { '.',0 }; + WCHAR filename[MAX_PATH]; + IStorage *stg = NULL; + IStorage *stgprio = NULL; + HRESULT r; + IStream *stm = NULL; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + LARGE_INTEGER pos; + ULARGE_INTEGER upos; + STATSTG stat; + char buffer[10]; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + DeleteFileW(filename); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + r = WriteClassStg( stg, &test_stg_cls ); + ok( r == S_OK, "WriteClassStg failed\n"); + + r = IStorage_Commit( stg, STGC_DEFAULT ); + ok( r == S_OK, "IStorage_Commit failed\n"); + + /* now create a stream */ + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStorage_Release( stg ); + ok (r == 0, "storage not released\n"); + + pos.QuadPart = 0; + r = IStream_Seek( stm, pos, 0, &upos ); + ok (r == STG_E_REVERTED, "seek should fail\n"); + + r = IStream_Stat( stm, &stat, STATFLAG_DEFAULT ); + ok (r == STG_E_REVERTED, "stat should fail\n"); + + r = IStream_Write( stm, "Test string", strlen("Test string"), NULL); + ok (r == STG_E_REVERTED, "IStream_Write should return STG_E_REVERTED instead of 0x%08x\n", r); + + r = IStream_Read( stm, buffer, sizeof(buffer), NULL); + ok (r == STG_E_REVERTED, "IStream_Read should return STG_E_REVERTED instead of 0x%08x\n", r); + + r = IStream_Release(stm); + ok (r == 0, "stream not released\n"); + + /* tests that STGM_PRIORITY doesn't prevent readwrite access from other + * StgOpenStorage calls in transacted mode */ + r = StgOpenStorage( filename, NULL, STGM_PRIORITY, NULL, 0, &stgprio); + ok(r==S_OK, "StgOpenStorage failed with error 0x%08x\n", r); + + todo_wine { + /* non-transacted mode read/write fails */ + r = StgOpenStorage( filename, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg); + ok(r==STG_E_LOCKVIOLATION, "StgOpenStorage should return STG_E_LOCKVIOLATION instead of 0x%08x\n", r); + } + + /* non-transacted mode read-only succeeds */ + r = StgOpenStorage( filename, NULL, STGM_SHARE_DENY_WRITE|STGM_READ, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed with error 0x%08x\n", r); + IStorage_Release(stg); + + r = StgOpenStorage( filename, NULL, STGM_TRANSACTED|STGM_SHARE_DENY_WRITE|STGM_READWRITE, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed with error 0x%08x\n", r); + if(stg) + { + static const WCHAR stgname[] = { ' ',' ',' ','2','9',0 }; + static const WCHAR stgname2[] = { 'C','V','_','i','e','w',0 }; + static const WCHAR stmname2[] = { 'V','a','r','2','D','a','t','a',0 }; + IStorage *stg2; + IStorage *stg3; + STATSTG statstg; + + r = IStorage_Stat( stg, &statstg, STATFLAG_NONAME ); + ok(r == S_OK, "Stat should have succeded instead of returning 0x%08x\n", r); + ok(statstg.type == STGTY_STORAGE, "Statstg type should have been STGTY_STORAGE instead of %d\n", statstg.type); + ok(U(statstg.cbSize).LowPart == 0, "Statstg cbSize.LowPart should have been 0 instead of %d\n", U(statstg.cbSize).LowPart); + ok(U(statstg.cbSize).HighPart == 0, "Statstg cbSize.HighPart should have been 0 instead of %d\n", U(statstg.cbSize).HighPart); + ok(statstg.grfMode == (STGM_TRANSACTED|STGM_SHARE_DENY_WRITE|STGM_READWRITE), + "Statstg grfMode should have been 0x10022 instead of 0x%x\n", statstg.grfMode); + ok(statstg.grfLocksSupported == 0, "Statstg grfLocksSupported should have been 0 instead of %d\n", statstg.grfLocksSupported); + ok(IsEqualCLSID(&statstg.clsid, &test_stg_cls), "Statstg clsid is not test_stg_cls\n"); + ok(statstg.grfStateBits == 0, "Statstg grfStateBits should have been 0 instead of %d\n", statstg.grfStateBits); + ok(statstg.reserved == 0, "Statstg reserved should have been 0 instead of %d\n", statstg.reserved); + + r = IStorage_CreateStorage( stg, stgname, STGM_SHARE_EXCLUSIVE, 0, 0, &stg2 ); + ok(r == S_OK, "CreateStorage should have succeeded instead of returning 0x%08x\n", r); + + r = IStorage_Stat( stg2, &statstg, STATFLAG_DEFAULT ); + ok(r == S_OK, "Stat should have succeded instead of returning 0x%08x\n", r); + ok(!lstrcmpW(statstg.pwcsName, stgname), + "Statstg pwcsName should have been the name the storage was created with\n"); + ok(statstg.type == STGTY_STORAGE, "Statstg type should have been STGTY_STORAGE instead of %d\n", statstg.type); + ok(U(statstg.cbSize).LowPart == 0, "Statstg cbSize.LowPart should have been 0 instead of %d\n", U(statstg.cbSize).LowPart); + ok(U(statstg.cbSize).HighPart == 0, "Statstg cbSize.HighPart should have been 0 instead of %d\n", U(statstg.cbSize).HighPart); + ok(statstg.grfMode == STGM_SHARE_EXCLUSIVE, + "Statstg grfMode should have been STGM_SHARE_EXCLUSIVE instead of 0x%x\n", statstg.grfMode); + ok(statstg.grfLocksSupported == 0, "Statstg grfLocksSupported should have been 0 instead of %d\n", statstg.grfLocksSupported); + ok(IsEqualCLSID(&statstg.clsid, &CLSID_NULL), "Statstg clsid is not CLSID_NULL\n"); + ok(statstg.grfStateBits == 0, "Statstg grfStateBits should have been 0 instead of %d\n", statstg.grfStateBits); + ok(statstg.reserved == 0, "Statstg reserved should have been 0 instead of %d\n", statstg.reserved); + CoTaskMemFree(statstg.pwcsName); + + r = IStorage_CreateStorage( stg2, stgname2, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0, &stg3 ); + ok(r == STG_E_ACCESSDENIED, "CreateStorage should have returned STG_E_ACCESSDENIED instead of 0x%08x\n", r); + + r = IStorage_CreateStream( stg2, stmname2, STGM_CREATE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + ok(r == STG_E_ACCESSDENIED, "CreateStream should have returned STG_E_ACCESSDENIED instead of 0x%08x\n", r); + + IStorage_Release(stg2); + + r = IStorage_Release(stg); + ok(r == 0, "wrong ref count\n"); + } + IStorage_Release(stgprio); + + DeleteFileW(filename); +} + +static void test_streamenum(void) +{ + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static const WCHAR szDot[] = { '.',0 }; + WCHAR filename[MAX_PATH]; + IStorage *stg = NULL; + HRESULT r; + IStream *stm = NULL; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + STATSTG stat; + IEnumSTATSTG *ee = NULL; + ULONG count; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + DeleteFileW(filename); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + r = WriteClassStg( stg, &test_stg_cls ); + ok( r == S_OK, "WriteClassStg failed\n"); + + r = IStorage_Commit( stg, STGC_DEFAULT ); + ok( r == S_OK, "IStorage_Commit failed\n"); + + /* now create a stream */ + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Release(stm); + + /* first enum ... should be 1 stream */ + r = IStorage_EnumElements(stg, 0, NULL, 0, &ee); + ok(r==S_OK, "IStorage->EnumElements failed\n"); + + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_OK, "IEnumSTATSTG->Next failed\n"); + ok(count == 1, "count wrong\n"); + + r = IEnumSTATSTG_Release(ee); + + /* second enum... destroy the stream before reading */ + r = IStorage_EnumElements(stg, 0, NULL, 0, &ee); + ok(r==S_OK, "IStorage->EnumElements failed\n"); + + r = IStorage_DestroyElement(stg, stmname); + ok(r==S_OK, "IStorage->EnumElements failed\n"); + + todo_wine { + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_FALSE, "IEnumSTATSTG->Next failed\n"); + ok(count == 0, "count wrong\n"); + } + + /* reset and try again */ + r = IEnumSTATSTG_Reset(ee); + ok(r==S_OK, "IEnumSTATSTG->Reset failed\n"); + + count = 0xf00; + r = IEnumSTATSTG_Next(ee, 1, &stat, &count); + ok(r==S_FALSE, "IEnumSTATSTG->Next failed\n"); + ok(count == 0, "count wrong\n"); + + r = IEnumSTATSTG_Release(ee); + ok (r == 0, "enum not released\n"); + + r = IStorage_Release( stg ); + ok (r == 0, "storage not released\n"); + + DeleteFileW(filename); +} + +static void test_transact(void) +{ + static const WCHAR szPrefix[] = { 's','t','g',0 }; + static const WCHAR szDot[] = { '.',0 }; + WCHAR filename[MAX_PATH]; + IStorage *stg = NULL, *stg2 = NULL; + HRESULT r; + IStream *stm = NULL; + static const WCHAR stmname[] = { 'C','O','N','T','E','N','T','S',0 }; + static const WCHAR stmname2[] = { 'F','O','O',0 }; + + if(!GetTempFileNameW(szDot, szPrefix, 0, filename)) + return; + + DeleteFileW(filename); + + /* create the file */ + r = StgCreateDocfile( filename, STGM_CREATE | STGM_SHARE_EXCLUSIVE | + STGM_READWRITE |STGM_TRANSACTED, 0, &stg); + ok(r==S_OK, "StgCreateDocfile failed\n"); + + /* now create a stream, but don't commit it */ + r = IStorage_CreateStream(stg, stmname2, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Write(stm, "this is stream 1\n", 16, NULL); + ok(r==S_OK, "IStream->Write failed\n"); + + r = IStream_Release(stm); + + r = IStorage_Commit(stg, 0); + ok(r==S_OK, "IStorage->Commit failed\n"); + + /* now create a stream, but don't commit it */ + stm = NULL; + r = IStorage_CreateStream(stg, stmname, STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, 0, &stm ); + ok(r==S_OK, "IStorage->CreateStream failed\n"); + + r = IStream_Write(stm, "this is stream 2\n", 16, NULL); + ok(r==S_OK, "IStream->Write failed\n"); + + r = IStream_Commit(stm, STGC_ONLYIFCURRENT | STGC_DANGEROUSLYCOMMITMERELYTODISKCACHE); + ok(r==S_OK, "IStream->Commit failed\n"); + + r = IStream_Release(stm); + + IStorage_Release(stg); + + stm = NULL; + stg = NULL; + r = StgOpenStorage( filename, NULL, STGM_SHARE_DENY_NONE | STGM_READ | STGM_TRANSACTED, NULL, 0, &stg); + ok(r==S_OK, "StgOpenStorage failed\n"); + + if (!stg) + return; + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_DENY_NONE|STGM_READ, 0, &stm ); + ok(r==STG_E_INVALIDFLAG, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_DELETEONRELEASE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_INVALIDFUNCTION, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStream(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_INVALIDFUNCTION, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStorage(stg, stmname, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r); + + todo_wine { + r = IStorage_OpenStream(stg, stmname, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream should fail %08x\n", r); + } + if (stm) + r = IStream_Release(stm); + + r = IStorage_OpenStorage(stg, stmname2, NULL, STGM_TRANSACTED|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, 0, &stg2 ); + ok(r==STG_E_FILENOTFOUND, "IStorage->OpenStream failed %08x\n", r); + + r = IStorage_OpenStream(stg, stmname2, NULL, STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &stm ); + ok(r==S_OK, "IStorage->OpenStream should fail %08x\n", r); + if (stm) + r = IStream_Release(stm); + + IStorage_Release(stg); + + r = DeleteFileW(filename); + ok( r == TRUE, "deleted file\n"); +} + +static void test_ReadClassStm(void) +{ + CLSID clsid; + HRESULT hr; + IStream *pStream; + static const LARGE_INTEGER llZero; + + hr = ReadClassStm(NULL, &clsid); + ok(hr == E_INVALIDARG, "ReadClassStm should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + hr = WriteClassStm(pStream, &test_stg_cls); + ok_ole_success(hr, "WriteClassStm"); + + hr = ReadClassStm(pStream, NULL); + ok(hr == E_INVALIDARG, "ReadClassStm should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + /* test not rewound stream */ + hr = ReadClassStm(pStream, &clsid); + ok(hr == STG_E_READFAULT, "ReadClassStm should have returned STG_E_READFAULT instead of 0x%08x\n", hr); + ok(IsEqualCLSID(&clsid, &CLSID_NULL), "clsid should have been zeroed\n"); + + hr = IStream_Seek(pStream, llZero, STREAM_SEEK_SET, NULL); + ok_ole_success(hr, "IStream_Seek"); + hr = ReadClassStm(pStream, &clsid); + ok_ole_success(hr, "ReadClassStm"); + ok(IsEqualCLSID(&clsid, &test_stg_cls), "clsid should have been set to CLSID_WineTest\n"); +} + +START_TEST(storage32) +{ + test_hglobal_storage_stat(); + test_create_storage_modes(); + test_storage_stream(); + test_open_storage(); + test_storage_suminfo(); + test_storage_refcount(); + test_streamenum(); + test_transact(); + test_ReadClassStm(); +} diff --git a/rostests/winetests/ole32/testlist.c b/rostests/winetests/ole32/testlist.c new file mode 100644 index 00000000000..0d19af299c6 --- /dev/null +++ b/rostests/winetests/ole32/testlist.c @@ -0,0 +1,37 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_clipboard(void); +extern void func_compobj(void); +extern void func_dragdrop(void); +extern void func_errorinfo(void); +extern void func_hglobalstream(void); +extern void func_marshal(void); +extern void func_moniker(void); +extern void func_ole2(void); +extern void func_propvariant(void); +extern void func_stg_prop(void); +extern void func_storage32(void); +extern void func_usrmarshal(void); + +const struct test winetest_testlist[] = +{ + { "clipboard", func_clipboard }, + { "compobj", func_compobj }, + { "dragdrop", func_dragdrop }, + { "errorinfo", func_errorinfo }, + { "hglobalstream", func_hglobalstream }, + { "marshal", func_marshal }, + { "moniker", func_moniker }, + { "ole2", func_ole2 }, + { "propvariant", func_propvariant }, + { "stg_prop", func_stg_prop }, + { "storage32", func_storage32 }, + { "usrmarshal", func_usrmarshal }, + { 0, 0 } +}; diff --git a/rostests/winetests/ole32/usrmarshal.c b/rostests/winetests/ole32/usrmarshal.c new file mode 100644 index 00000000000..281f9a01045 --- /dev/null +++ b/rostests/winetests/ole32/usrmarshal.c @@ -0,0 +1,494 @@ +/* + * User Marshaling Tests + * + * Copyright 2004-2006 Robert Shearman 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 +#define CONST_VTABLE +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "objidl.h" + +#include "wine/test.h" + +ULONG __RPC_USER HMETAFILE_UserSize(ULONG *, unsigned long, HMETAFILE *); +unsigned char * __RPC_USER HMETAFILE_UserMarshal(ULONG *, unsigned char *, HMETAFILE *); +unsigned char * __RPC_USER HMETAFILE_UserUnmarshal(ULONG *, unsigned char *, HMETAFILE *); +void __RPC_USER HMETAFILE_UserFree(ULONG *, HMETAFILE *); + +static const char cf_marshaled[] = +{ + 0x9, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x9, 0x0, 0x0, 0x0, + 'M', 0x0, 'y', 0x0, + 'F', 0x0, 'o', 0x0, + 'r', 0x0, 'm', 0x0, + 'a', 0x0, 't', 0x0, + 0x0, 0x0 +}; + +static void test_marshal_CLIPFORMAT(void) +{ + unsigned char *buffer; + ULONG size; + ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); + wireCLIPFORMAT wirecf; + CLIPFORMAT cf = RegisterClipboardFormatA("MyFormat"); + CLIPFORMAT cf2; + + size = CLIPFORMAT_UserSize(&flags, 0, &cf); + ok(size == sizeof(*wirecf) + sizeof(cf_marshaled), "Wrong size %d\n", size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + CLIPFORMAT_UserMarshal(&flags, buffer, &cf); + wirecf = (wireCLIPFORMAT)buffer; + ok(wirecf->fContext == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08lx\n", wirecf->fContext); + ok(wirecf->u.dwValue == cf, "Marshaled value should be 0x%04x instead of 0x%04x\n", cf, wirecf->u.dwValue); + ok(!memcmp(wirecf+1, cf_marshaled, sizeof(cf_marshaled)), "Marshaled data differs\n"); + + CLIPFORMAT_UserUnmarshal(&flags, buffer, &cf2); + ok(cf == cf2, "Didn't unmarshal properly\n"); + HeapFree(GetProcessHeap(), 0, buffer); + + CLIPFORMAT_UserFree(&flags, &cf2); +} + +static void test_marshal_HWND(void) +{ + unsigned char *buffer; + ULONG size; + ULONG flags = MAKELONG(MSHCTX_LOCAL, NDR_LOCAL_DATA_REPRESENTATION); + HWND hwnd = GetDesktopWindow(); + HWND hwnd2; + wireHWND wirehwnd; + + size = HWND_UserSize(&flags, 0, &hwnd); + ok(size == sizeof(*wirehwnd), "Wrong size %d\n", size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HWND_UserMarshal(&flags, buffer, &hwnd); + wirehwnd = (wireHWND)buffer; + ok(wirehwnd->fContext == WDT_INPROC_CALL, "Context should be WDT_INPROC_CALL instead of 0x%08lx\n", wirehwnd->fContext); + ok(wirehwnd->u.hInproc == (LONG_PTR)hwnd, "Marshaled value should be %p instead of %p\n", hwnd, (HANDLE)wirehwnd->u.hRemote); + + HWND_UserUnmarshal(&flags, buffer, &hwnd2); + ok(hwnd == hwnd2, "Didn't unmarshal properly\n"); + HeapFree(GetProcessHeap(), 0, buffer); + + HWND_UserFree(&flags, &hwnd2); +} + +static void test_marshal_HGLOBAL(void) +{ + unsigned char *buffer; + ULONG size; + ULONG flags = MAKELONG(MSHCTX_LOCAL, NDR_LOCAL_DATA_REPRESENTATION); + HGLOBAL hglobal; + HGLOBAL hglobal2; + unsigned char *wirehglobal; + int i; + + hglobal = NULL; + flags = MAKELONG(MSHCTX_LOCAL, NDR_LOCAL_DATA_REPRESENTATION); + size = HGLOBAL_UserSize(&flags, 0, &hglobal); + /* native is poorly programmed and allocates 4 bytes more than it needs to + * here - Wine doesn't have to emulate that */ + ok((size == 8) || (size == 12), "Size should be 12, instead of %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HGLOBAL_UserMarshal(&flags, buffer, &hglobal); + wirehglobal = buffer; + ok(*(ULONG *)wirehglobal == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(ULONG *)wirehglobal); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == (ULONG)hglobal, "buffer+4 should be HGLOBAL\n"); + HGLOBAL_UserUnmarshal(&flags, buffer, &hglobal2); + ok(hglobal2 == hglobal, "Didn't unmarshal properly\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HGLOBAL_UserFree(&flags, &hglobal2); + + hglobal = GlobalAlloc(0, 4); + buffer = GlobalLock(hglobal); + for (i = 0; i < 4; i++) + buffer[i] = i; + GlobalUnlock(hglobal); + flags = MAKELONG(MSHCTX_LOCAL, NDR_LOCAL_DATA_REPRESENTATION); + size = HGLOBAL_UserSize(&flags, 0, &hglobal); + /* native is poorly programmed and allocates 4 bytes more than it needs to + * here - Wine doesn't have to emulate that */ + ok((size == 24) || (size == 28), "Size should be 24 or 28, instead of %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HGLOBAL_UserMarshal(&flags, buffer, &hglobal); + wirehglobal = buffer; + ok(*(ULONG *)wirehglobal == WDT_REMOTE_CALL, "Context should be WDT_REMOTE_CALL instead of 0x%08x\n", *(ULONG *)wirehglobal); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == (ULONG)hglobal, "buffer+0x4 should be HGLOBAL\n"); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == 4, "buffer+0x8 should be size of HGLOBAL\n"); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == (ULONG)hglobal, "buffer+0xc should be HGLOBAL\n"); + wirehglobal += sizeof(ULONG); + ok(*(ULONG *)wirehglobal == 4, "buffer+0x10 should be size of HGLOBAL\n"); + wirehglobal += sizeof(ULONG); + for (i = 0; i < 4; i++) + ok(wirehglobal[i] == i, "buffer+0x%x should be %d\n", 0x10 + i, i); + HGLOBAL_UserUnmarshal(&flags, buffer, &hglobal2); + ok(hglobal2 != NULL, "Didn't unmarshal properly\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HGLOBAL_UserFree(&flags, &hglobal2); + GlobalFree(hglobal); +} + +static HENHMETAFILE create_emf(void) +{ + RECT rect = {0, 0, 100, 100}; + HDC hdc = CreateEnhMetaFile(NULL, NULL, &rect, "HENHMETAFILE Marshaling Test\0Test\0\0"); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, NULL, "Test String", strlen("Test String"), NULL); + return CloseEnhMetaFile(hdc); +} + +static void test_marshal_HENHMETAFILE(void) +{ + unsigned char *buffer; + ULONG size; + ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); + HENHMETAFILE hemf; + HENHMETAFILE hemf2 = NULL; + unsigned char *wirehemf; + + hemf = create_emf(); + + size = HENHMETAFILE_UserSize(&flags, 0, &hemf); + ok(size > 20, "size should be at least 20 bytes, not %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HENHMETAFILE_UserMarshal(&flags, buffer, &hemf); + wirehemf = buffer; + ok(*(DWORD *)wirehemf == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehemf); + wirehemf += sizeof(DWORD); + ok(*(DWORD *)wirehemf == (DWORD)(DWORD_PTR)hemf, "wirestgm + 0x4 should be hemf instead of 0x%08x\n", *(DWORD *)wirehemf); + wirehemf += sizeof(DWORD); + ok(*(DWORD *)wirehemf == (size - 0x10), "wirestgm + 0x8 should be size - 0x10 instead of 0x%08x\n", *(DWORD *)wirehemf); + wirehemf += sizeof(DWORD); + ok(*(DWORD *)wirehemf == (size - 0x10), "wirestgm + 0xc should be size - 0x10 instead of 0x%08x\n", *(DWORD *)wirehemf); + wirehemf += sizeof(DWORD); + ok(*(DWORD *)wirehemf == EMR_HEADER, "wirestgm + 0x10 should be EMR_HEADER instead of %d\n", *(DWORD *)wirehemf); + wirehemf += sizeof(DWORD); + /* ... rest of data not tested - refer to tests for GetEnhMetaFileBits + * at this point */ + + HENHMETAFILE_UserUnmarshal(&flags, buffer, &hemf2); + ok(hemf2 != NULL, "HENHMETAFILE didn't unmarshal\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HENHMETAFILE_UserFree(&flags, &hemf2); + DeleteEnhMetaFile(hemf); + + /* test NULL emf */ + hemf = NULL; + + size = HENHMETAFILE_UserSize(&flags, 0, &hemf); + ok(size == 8, "size should be 8 bytes, not %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HENHMETAFILE_UserMarshal(&flags, buffer, &hemf); + wirehemf = buffer; + ok(*(DWORD *)wirehemf == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehemf); + wirehemf += sizeof(DWORD); + ok(*(DWORD *)wirehemf == (DWORD)(DWORD_PTR)hemf, "wirestgm + 0x4 should be hemf instead of 0x%08x\n", *(DWORD *)wirehemf); + wirehemf += sizeof(DWORD); + + HENHMETAFILE_UserUnmarshal(&flags, buffer, &hemf2); + ok(hemf2 == NULL, "NULL HENHMETAFILE didn't unmarshal\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HENHMETAFILE_UserFree(&flags, &hemf2); +} + +static HMETAFILE create_mf(void) +{ + RECT rect = {0, 0, 100, 100}; + HDC hdc = CreateMetaFile(NULL); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rect, "Test String", strlen("Test String"), NULL); + return CloseMetaFile(hdc); +} + +static void test_marshal_HMETAFILE(void) +{ + unsigned char *buffer; + ULONG size; + ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); + HMETAFILE hmf; + HMETAFILE hmf2 = NULL; + unsigned char *wirehmf; + + hmf = create_mf(); + + size = HMETAFILE_UserSize(&flags, 0, &hmf); + ok(size > 20, "size should be at least 20 bytes, not %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HMETAFILE_UserMarshal(&flags, buffer, &hmf); + wirehmf = buffer; + ok(*(DWORD *)wirehmf == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehmf); + wirehmf += sizeof(DWORD); + ok(*(DWORD *)wirehmf == (DWORD)(DWORD_PTR)hmf, "wirestgm + 0x4 should be hmf instead of 0x%08x\n", *(DWORD *)wirehmf); + wirehmf += sizeof(DWORD); + ok(*(DWORD *)wirehmf == (size - 0x10), "wirestgm + 0x8 should be size - 0x10 instead of 0x%08x\n", *(DWORD *)wirehmf); + wirehmf += sizeof(DWORD); + ok(*(DWORD *)wirehmf == (size - 0x10), "wirestgm + 0xc should be size - 0x10 instead of 0x%08x\n", *(DWORD *)wirehmf); + wirehmf += sizeof(DWORD); + ok(*(WORD *)wirehmf == 1, "wirestgm + 0x10 should be 1 instead of 0x%08x\n", *(DWORD *)wirehmf); + wirehmf += sizeof(DWORD); + /* ... rest of data not tested - refer to tests for GetMetaFileBits + * at this point */ + + HMETAFILE_UserUnmarshal(&flags, buffer, &hmf2); + ok(hmf2 != NULL, "HMETAFILE didn't unmarshal\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HMETAFILE_UserFree(&flags, &hmf2); + DeleteMetaFile(hmf); + + /* test NULL emf */ + hmf = NULL; + + size = HMETAFILE_UserSize(&flags, 0, &hmf); + ok(size == 8, "size should be 8 bytes, not %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HMETAFILE_UserMarshal(&flags, buffer, &hmf); + wirehmf = buffer; + ok(*(DWORD *)wirehmf == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehmf); + wirehmf += sizeof(DWORD); + ok(*(DWORD *)wirehmf == (DWORD)(DWORD_PTR)hmf, "wirestgm + 0x4 should be hmf instead of 0x%08x\n", *(DWORD *)wirehmf); + wirehmf += sizeof(DWORD); + + HMETAFILE_UserUnmarshal(&flags, buffer, &hmf2); + ok(hmf2 == NULL, "NULL HMETAFILE didn't unmarshal\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HMETAFILE_UserFree(&flags, &hmf2); +} + +#define USER_MARSHAL_PTR_PREFIX \ + ( (DWORD)'U' | ( (DWORD)'s' << 8 ) | \ + ( (DWORD)'e' << 16 ) | ( (DWORD)'r' << 24 ) ) + +static void test_marshal_HMETAFILEPICT(void) +{ + unsigned char *buffer, *buffer_end; + ULONG size; + ULONG flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); + HMETAFILEPICT hmfp; + HMETAFILEPICT hmfp2 = NULL; + METAFILEPICT *pmfp; + unsigned char *wirehmfp; + + hmfp = GlobalAlloc(GMEM_MOVEABLE, sizeof(*pmfp)); + pmfp = GlobalLock(hmfp); + pmfp->mm = MM_ISOTROPIC; + pmfp->xExt = 1; + pmfp->yExt = 2; + pmfp->hMF = create_mf(); + GlobalUnlock(hmfp); + + size = HMETAFILEPICT_UserSize(&flags, 0, &hmfp); + ok(size > 20, "size should be at least 20 bytes, not %d\n", size); + trace("size is %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + buffer_end = HMETAFILEPICT_UserMarshal(&flags, buffer, &hmfp); + wirehmfp = buffer; + ok(*(DWORD *)wirehmfp == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == (DWORD)(DWORD_PTR)hmfp, "wirestgm + 0x4 should be hmf instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == MM_ISOTROPIC, "wirestgm + 0x8 should be MM_ISOTROPIC instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == 1, "wirestgm + 0xc should be 1 instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == 2, "wirestgm + 0x10 should be 2 instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == USER_MARSHAL_PTR_PREFIX, "wirestgm + 0x14 should be \"User\" instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == WDT_REMOTE_CALL, "wirestgm + 0x18 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + pmfp = GlobalLock(hmfp); + ok(*(DWORD *)wirehmfp == (DWORD)(DWORD_PTR)pmfp->hMF, "wirestgm + 0x1c should be pmfp->hMF instead of 0x%08x\n", *(DWORD *)wirehmfp); + GlobalUnlock(hmfp); + wirehmfp += sizeof(DWORD); + /* Note use (buffer_end - buffer) instead of size here, because size is an + * overestimate with native */ + ok(*(DWORD *)wirehmfp == (buffer_end - buffer - 0x28), "wirestgm + 0x20 should be size - 0x34 instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == (buffer_end - buffer - 0x28), "wirestgm + 0x24 should be size - 0x34 instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(WORD *)wirehmfp == 1, "wirehmfp + 0x28 should be 1 instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + /* ... rest of data not tested - refer to tests for GetMetaFileBits + * at this point */ + + HMETAFILEPICT_UserUnmarshal(&flags, buffer, &hmfp2); + ok(hmfp2 != NULL, "HMETAFILEPICT didn't unmarshal\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HMETAFILEPICT_UserFree(&flags, &hmfp2); + pmfp = GlobalLock(hmfp); + DeleteMetaFile(pmfp->hMF); + GlobalUnlock(hmfp); + GlobalFree(hmfp); + + /* test NULL emf */ + hmfp = NULL; + + size = HMETAFILEPICT_UserSize(&flags, 0, &hmfp); + ok(size == 8, "size should be 8 bytes, not %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + HMETAFILEPICT_UserMarshal(&flags, buffer, &hmfp); + wirehmfp = buffer; + ok(*(DWORD *)wirehmfp == WDT_REMOTE_CALL, "wirestgm + 0x0 should be WDT_REMOTE_CALL instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + ok(*(DWORD *)wirehmfp == (DWORD)(DWORD_PTR)hmfp, "wirestgm + 0x4 should be hmf instead of 0x%08x\n", *(DWORD *)wirehmfp); + wirehmfp += sizeof(DWORD); + + hmfp2 = NULL; + HMETAFILEPICT_UserUnmarshal(&flags, buffer, &hmfp2); + ok(hmfp2 == NULL, "NULL HMETAFILE didn't unmarshal\n"); + HeapFree(GetProcessHeap(), 0, buffer); + HMETAFILEPICT_UserFree(&flags, &hmfp2); +} + +static HRESULT WINAPI Test_IUnknown_QueryInterface( + LPUNKNOWN iface, + REFIID riid, + LPVOID *ppvObj) +{ + if (ppvObj == NULL) return E_POINTER; + + if (IsEqualGUID(riid, &IID_IUnknown)) + { + *ppvObj = (LPVOID)iface; + IUnknown_AddRef(iface); + return S_OK; + } + + *ppvObj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Test_IUnknown_AddRef(LPUNKNOWN iface) +{ + return 2; /* non-heap-based object */ +} + +static ULONG WINAPI Test_IUnknown_Release(LPUNKNOWN iface) +{ + return 1; /* non-heap-based object */ +} + +static const IUnknownVtbl TestUnknown_Vtbl = +{ + Test_IUnknown_QueryInterface, + Test_IUnknown_AddRef, + Test_IUnknown_Release, +}; + +static IUnknown Test_Unknown = { &TestUnknown_Vtbl }; + +ULONG __RPC_USER WdtpInterfacePointer_UserSize(ULONG *, ULONG, ULONG, IUnknown *, REFIID); +unsigned char * __RPC_USER WdtpInterfacePointer_UserMarshal(ULONG *, ULONG, unsigned char *, IUnknown *, REFIID); +unsigned char * __RPC_USER WdtpInterfacePointer_UserUnmarshal(ULONG *, unsigned char *, IUnknown **, REFIID); +void __RPC_USER WdtpInterfacePointer_UserFree(IUnknown *); + +static void test_marshal_WdtpInterfacePointer(void) +{ + unsigned char *buffer, *buffer_end; + ULONG size; + MIDL_STUB_MESSAGE stubmsg; + USER_MARSHAL_CB umcb; + IUnknown *unk; + IUnknown *unk2; + unsigned char *wireip; + const IID *iid; + + memset(&stubmsg, 0xcc, sizeof(stubmsg)); + stubmsg.dwDestContext = MSHCTX_INPROC; + stubmsg.pvDestContext = NULL; + + memset(&umcb, 0xcc, sizeof(umcb)); + umcb.Flags = MAKELONG(MSHCTX_INPROC, NDR_LOCAL_DATA_REPRESENTATION); + umcb.pStubMsg = &stubmsg; + + /* shows that the WdtpInterfacePointer functions don't marshal anything for + * NULL pointers, so code using these functions must handle that case + * itself */ + unk = NULL; + size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 0, unk, &IID_IUnknown); + ok(size == 0, "size should be 0 bytes, not %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, buffer, unk, &IID_IUnknown); + wireip = buffer; + HeapFree(GetProcessHeap(), 0, buffer); + + unk = &Test_Unknown; + size = WdtpInterfacePointer_UserSize(&umcb.Flags, umcb.Flags, 0, unk, &IID_IUnknown); + todo_wine + ok(size == 108, "size should be 108 bytes, not %d\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + buffer_end = WdtpInterfacePointer_UserMarshal(&umcb.Flags, umcb.Flags, buffer, unk, &IID_IUnknown); + wireip = buffer; + if (size >= 28) + { + ok(*(DWORD *)wireip == 0x44, "wireip + 0x0 should be 0x4c instead of 0x%08x\n", *(DWORD *)wireip); + wireip += sizeof(DWORD); + ok(*(DWORD *)wireip == 0x44, "wireip + 0x8 should be 0x4c instead of 0x%08x\n", *(DWORD *)wireip); + wireip += sizeof(DWORD); + ok(*(DWORD *)wireip == 0x574f454d /* 'MEOW' */, "wireip + 0xc should be 0x574f454d instead of 0x%08x\n", *(DWORD *)wireip); + wireip += sizeof(DWORD); + ok(*(DWORD *)wireip == 0x1, "wireip + 0x10 should be 0x1 instead of 0x%08x\n", *(DWORD *)wireip); + wireip += sizeof(DWORD); + iid = (const IID *)buffer; + ok(!IsEqualIID(iid, &IID_IUnknown), + "wireip + 0x14 should be IID_IUnknown instead of {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + iid->Data1, iid->Data2, iid->Data3, + iid->Data4[0], iid->Data4[1], iid->Data4[2], iid->Data4[3], + iid->Data4[4], iid->Data4[5], iid->Data4[6], iid->Data4[7]); + ok(*(DWORD *)wireip == 0, "wireip + 0x14 should be 0 instead of 0x%08x\n", *(DWORD *)wireip); + wireip += sizeof(IID); + ok(*(DWORD *)wireip == 0, "wireip + 0x20 should be 0 instead of 0x%08x\n", *(DWORD *)wireip); + wireip += sizeof(DWORD); + ok(*(DWORD *)wireip == 5, "wireip + 0x24 should be 5 instead of %d\n", *(DWORD *)wireip); + wireip += sizeof(DWORD); + /* the rest is dynamic so can't really be tested */ + } + + unk2 = NULL; + WdtpInterfacePointer_UserUnmarshal(&umcb.Flags, buffer, &unk2, &IID_IUnknown); + todo_wine + ok(unk2 != NULL, "IUnknown object didn't unmarshal properly\n"); + HeapFree(GetProcessHeap(), 0, buffer); + WdtpInterfacePointer_UserFree(unk2); +} + +START_TEST(usrmarshal) +{ + CoInitialize(NULL); + + test_marshal_CLIPFORMAT(); + test_marshal_HWND(); + test_marshal_HGLOBAL(); + test_marshal_HENHMETAFILE(); + test_marshal_HMETAFILE(); + test_marshal_HMETAFILEPICT(); + test_marshal_WdtpInterfacePointer(); + + CoUninitialize(); +} diff --git a/rostests/winetests/oleaut32/oleaut32.rbuild b/rostests/winetests/oleaut32/oleaut32.rbuild new file mode 100644 index 00000000000..fa9161f2521 --- /dev/null +++ b/rostests/winetests/oleaut32/oleaut32.rbuild @@ -0,0 +1,31 @@ + + + + + . + 0x600 + 0x600 + wine + oleaut32 + ole32 + shlwapi + rpcrt4 + user32 + gdi32 + advapi32 + kernel32 + uuid + ntdll + olefont.c + olepicture.c + safearray.c + tmarshal.c + typelib.c + usrmarshal.c + varformat.c + vartest.c + vartype.c + tmarshal.rc + testlist.c + + diff --git a/rostests/winetests/oleaut32/olefont.c b/rostests/winetests/oleaut32/olefont.c new file mode 100644 index 00000000000..1e6678b3499 --- /dev/null +++ b/rostests/winetests/oleaut32/olefont.c @@ -0,0 +1,828 @@ +/* + * OLEFONT test program + * + * Copyright 2003 Marcus Meissner + * Copyright 2006 (Google) Benjamin Arai + * + * + * 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 +#include +#include +#include +#include + +#define COBJMACROS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static WCHAR MSSansSerif_font[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0}; +static WCHAR system_font[] = { 'S','y','s','t','e','m',0 }; +static WCHAR arial_font[] = { 'A','r','i','a','l',0 }; + +static HMODULE hOleaut32; + +static HRESULT (WINAPI *pOleCreateFontIndirect)(LPFONTDESC,REFIID,LPVOID*); + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) + +/* Create a font with cySize given by lo_size, hi_size, */ +/* SetRatio to ratio_logical, ratio_himetric, */ +/* check that resulting hfont has height hfont_height. */ +/* Various checks along the way. */ + +static void test_ifont_sizes(long lo_size, long hi_size, + long ratio_logical, long ratio_himetric, + long hfont_height, const char * test_name) +{ + FONTDESC fd; + LPVOID pvObj = NULL; + IFont* ifnt = NULL; + HFONT hfont; + LOGFONT lf; + CY psize; + HRESULT hres; + + fd.cbSizeofstruct = sizeof(FONTDESC); + fd.lpstrName = system_font; + S(fd.cySize).Lo = lo_size; + S(fd.cySize).Hi = hi_size; + fd.sWeight = 0; + fd.sCharset = 0; + fd.fItalic = 0; + fd.fUnderline = 0; + fd.fStrikethrough = 0; + + /* Create font, test that it worked. */ + hres = pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj); + ifnt = pvObj; + ok(hres == S_OK,"%s: OCFI returns 0x%08x instead of S_OK.\n", + test_name, hres); + ok(pvObj != NULL,"%s: OCFI returns NULL.\n", test_name); + + /* Read back size. Hi part was ignored. */ + hres = IFont_get_Size(ifnt, &psize); + ok(hres == S_OK,"%s: IFont_get_size returns 0x%08x instead of S_OK.\n", + test_name, hres); + ok(S(psize).Lo == lo_size && S(psize).Hi == 0, + "%s: get_Size: Lo=%d, Hi=%d; expected Lo=%ld, Hi=%ld.\n", + test_name, S(psize).Lo, S(psize).Hi, lo_size, 0L); + + /* Change ratio, check size unchanged. Standard is 72, 2540. */ + hres = IFont_SetRatio(ifnt, ratio_logical, ratio_himetric); + ok(hres == S_OK,"%s: IFont_SR returns 0x%08x instead of S_OK.\n", + test_name, hres); + hres = IFont_get_Size(ifnt, &psize); + ok(hres == S_OK,"%s: IFont_get_size returns 0x%08x instead of S_OK.\n", + test_name, hres); + ok(S(psize).Lo == lo_size && S(psize).Hi == 0, + "%s: gS after SR: Lo=%d, Hi=%d; expected Lo=%ld, Hi=%ld.\n", + test_name, S(psize).Lo, S(psize).Hi, lo_size, 0L); + + /* Check hFont size with this ratio. This tests an important */ + /* conversion for which MSDN is very wrong. */ + hres = IFont_get_hFont (ifnt, &hfont); + ok(hres == S_OK, "%s: IFont_get_hFont returns 0x%08x instead of S_OK.\n", + test_name, hres); + hres = GetObject (hfont, sizeof(LOGFONT), &lf); + ok(lf.lfHeight == hfont_height, + "%s: hFont has lf.lfHeight=%d, expected %ld.\n", + test_name, lf.lfHeight, hfont_height); + + /* Free IFont. */ + IFont_Release(ifnt); +} + +static void test_QueryInterface(void) +{ + LPVOID pvObj = NULL; + HRESULT hres; + IFont* font = NULL; + LONG ret; + + hres = pOleCreateFontIndirect(NULL, &IID_IFont, &pvObj); + font = pvObj; + + ok(hres == S_OK,"OCFI (NULL,..) does not return 0, but 0x%08x\n",hres); + ok(font != NULL,"OCFI (NULL,..) returns NULL, instead of !NULL\n"); + + pvObj = NULL; + hres = IFont_QueryInterface( font, &IID_IFont, &pvObj); + + /* Test if QueryInterface increments ref counter for IFONTs */ + ret = IFont_AddRef(font); + ok(ret == 3, "IFont_QI expected ref value 3 but instead got %12u\n",ret); + IFont_Release(font); + + ok(hres == S_OK,"IFont_QI does not return S_OK, but 0x%08x\n", hres); + ok(pvObj != NULL,"IFont_QI does return NULL, instead of a ptr\n"); + + /* Orignial ref and QueryInterface ref both have to be released */ + IFont_Release(font); + IFont_Release(font); +} + +static void test_type_info(void) +{ + LPVOID pvObj = NULL; + HRESULT hres; + IFontDisp* fontdisp = NULL; + ITypeInfo* pTInfo; + WCHAR name_Name[] = {'N','a','m','e',0}; + BSTR names[3]; + UINT n; + LCID en_us = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), + SORT_DEFAULT); + DISPPARAMS dispparams; + VARIANT varresult; + + pOleCreateFontIndirect(NULL, &IID_IFontDisp, &pvObj); + fontdisp = pvObj; + + hres = IFontDisp_GetTypeInfo(fontdisp, 0, en_us, &pTInfo); + ok(hres == S_OK, "GTI returned 0x%08x instead of S_OK.\n", hres); + ok(pTInfo != NULL, "GTI returned NULL.\n"); + + hres = ITypeInfo_GetNames(pTInfo, DISPID_FONT_NAME, names, 3, &n); + ok(hres == S_OK, "GetNames returned 0x%08x instead of S_OK.\n", hres); + ok(n == 1, "GetNames returned %d names instead of 1.\n", n); + ok(!lstrcmpiW(names[0],name_Name), "DISPID_FONT_NAME doesn't get 'Names'.\n"); + + ITypeInfo_Release(pTInfo); + + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 0; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hres = IFontDisp_Invoke(fontdisp, DISPID_FONT_NAME, &IID_NULL, + LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, + NULL, NULL); + ok(hres == S_OK, "IFontDisp_Invoke return 0x%08x instead of S_OK.\n", hres); + VariantClear(&varresult); + + IFontDisp_Release(fontdisp); +} + +static HRESULT WINAPI FontEventsDisp_QueryInterface( + IFontEventsDisp *iface, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) +{ + if (IsEqualIID(riid, &IID_IFontEventsDisp) || IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch)) + { + IUnknown_AddRef(iface); + *ppvObject = iface; + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +static ULONG WINAPI FontEventsDisp_AddRef( + IFontEventsDisp *iface) +{ + return 2; +} + +static ULONG WINAPI FontEventsDisp_Release( + IFontEventsDisp *iface) +{ + return 1; +} + +static int fonteventsdisp_invoke_called = 0; + +static HRESULT WINAPI FontEventsDisp_Invoke( + IFontEventsDisp __RPC_FAR * iface, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr) +{ + static const WCHAR wszBold[] = {'B','o','l','d',0}; + ok(wFlags == INVOKE_FUNC, "invoke flags should have been INVOKE_FUNC instead of 0x%x\n", wFlags); + ok(dispIdMember == DISPID_FONT_CHANGED, "dispIdMember should have been DISPID_FONT_CHANGED instead of 0x%x\n", dispIdMember); + ok(pDispParams->cArgs == 1, "pDispParams->cArgs should have been 1 instead of %d\n", pDispParams->cArgs); + ok(V_VT(&pDispParams->rgvarg[0]) == VT_BSTR, "VT of first param should have been VT_BSTR instead of %d\n", V_VT(&pDispParams->rgvarg[0])); + ok(!lstrcmpW(V_BSTR(&pDispParams->rgvarg[0]), wszBold), "String in first param should have been \"Bold\"\n"); + + fonteventsdisp_invoke_called++; + return S_OK; +} + +static IFontEventsDispVtbl FontEventsDisp_Vtbl = +{ + FontEventsDisp_QueryInterface, + FontEventsDisp_AddRef, + FontEventsDisp_Release, + NULL, + NULL, + NULL, + FontEventsDisp_Invoke +}; + +static IFontEventsDisp FontEventsDisp = { &FontEventsDisp_Vtbl }; + +static void test_font_events_disp(void) +{ + IFont *pFont; + IFont *pFont2; + IConnectionPointContainer *pCPC; + IConnectionPoint *pCP; + FONTDESC fontdesc; + HRESULT hr; + DWORD dwCookie; + IFontDisp *pFontDisp; + DISPPARAMS dispparams; + VARIANTARG vararg; + + fontdesc.cbSizeofstruct = sizeof(fontdesc); + fontdesc.lpstrName = MSSansSerif_font; + fontdesc.cySize.int64 = 12 * 10000; /* 12 pt */ + fontdesc.sWeight = FW_NORMAL; + fontdesc.sCharset = 0; + fontdesc.fItalic = FALSE; + fontdesc.fUnderline = FALSE; + fontdesc.fStrikethrough = FALSE; + + hr = pOleCreateFontIndirect(&fontdesc, &IID_IFont, (void **)&pFont); + ok_ole_success(hr, "OleCreateFontIndirect"); + + hr = IFont_QueryInterface(pFont, &IID_IConnectionPointContainer, (void **)&pCPC); + ok_ole_success(hr, "IFont_QueryInterface"); + + hr = IConnectionPointContainer_FindConnectionPoint(pCPC, &IID_IFontEventsDisp, &pCP); + ok_ole_success(hr, "IConnectionPointContainer_FindConnectionPoint"); + IConnectionPointContainer_Release(pCPC); + + hr = IConnectionPoint_Advise(pCP, (IUnknown *)&FontEventsDisp, &dwCookie); + ok_ole_success(hr, "IConnectionPoint_Advise"); + IConnectionPoint_Release(pCP); + + hr = IFont_put_Bold(pFont, TRUE); + ok_ole_success(hr, "IFont_put_Bold"); + + ok(fonteventsdisp_invoke_called == 1, "IFontEventDisp::Invoke wasn't called once\n"); + + hr = IFont_QueryInterface(pFont, &IID_IFontDisp, (void **)&pFontDisp); + ok_ole_success(hr, "IFont_QueryInterface"); + + V_VT(&vararg) = VT_BOOL; + V_BOOL(&vararg) = VARIANT_FALSE; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 1; + dispparams.rgvarg = &vararg; + hr = IFontDisp_Invoke(pFontDisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); + + IFontDisp_Release(pFontDisp); + + ok(fonteventsdisp_invoke_called == 2, "IFontEventDisp::Invoke was called %d times instead of twice\n", + fonteventsdisp_invoke_called); + + hr = IFont_Clone(pFont, &pFont2); + ok_ole_success(hr, "IFont_Clone"); + IFont_Release(pFont); + + hr = IFont_put_Bold(pFont2, FALSE); + ok_ole_success(hr, "IFont_put_Bold"); + + /* this test shows that the notification routine isn't called again */ + ok(fonteventsdisp_invoke_called == 2, "IFontEventDisp::Invoke was called %d times instead of twice\n", + fonteventsdisp_invoke_called); + + IFont_Release(pFont2); +} + +static void test_names_ids(WCHAR* w_name_1, const char* a_name_1, + WCHAR* w_name_2, const char* a_name_2, + LCID lcid, DISPID id_1, DISPID id_2, + HRESULT hres_expect, int numnames) +{ + LPVOID pvObj = NULL; + IFontDisp *fontdisp = NULL; + HRESULT hres; + DISPID rgDispId[2] = {0xdeadbeef, 0xdeadbeef}; + LPOLESTR names[2] = {w_name_1, w_name_2}; + + pOleCreateFontIndirect(NULL, &IID_IFontDisp, &pvObj); + fontdisp = pvObj; + + hres = IFontDisp_GetIDsOfNames(fontdisp, &IID_NULL, names, numnames, + lcid, rgDispId); + + /* test hres */ + ok(hres == hres_expect, + "GetIDsOfNames: \"%s\", \"%s\" returns 0x%08x, expected 0x%08x.\n", + a_name_1, a_name_2, hres, hres_expect); + + /* test first DISPID */ + ok(rgDispId[0]==id_1, + "GetIDsOfNames: \"%s\" gets DISPID 0x%08x, expected 0x%08x.\n", + a_name_1, rgDispId[0], id_1); + + /* test second DISPID is present */ + if (numnames == 2) + { + ok(rgDispId[1]==id_2, + "GetIDsOfNames: ..., \"%s\" gets DISPID 0x%08x, expected 0x%08x.\n", + a_name_2, rgDispId[1], id_2); + } + + IFontDisp_Release(fontdisp); +} + +static void test_GetIDsOfNames(void) +{ + WCHAR name_Name[] = {'N','a','m','e',0}; + WCHAR name_Italic[] = {'I','t','a','l','i','c',0}; + WCHAR name_Size[] = {'S','i','z','e',0}; + WCHAR name_Bold[] = {'B','o','l','d',0}; + WCHAR name_Underline[] = {'U','n','d','e','r','l','i','n','e',0}; + WCHAR name_Strikethrough[] = {'S','t','r','i','k','e','t','h','r','o','u','g','h',0}; + WCHAR name_Weight[] = {'W','e','i','g','h','t',0}; + WCHAR name_Charset[] = {'C','h','a','r','s','e','t',0}; + WCHAR name_Foo[] = {'F','o','o',0}; + WCHAR name_nAmE[] = {'n','A','m','E',0}; + WCHAR name_Nom[] = {'N','o','m',0}; + + LCID en_us = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US), + SORT_DEFAULT); + LCID fr_fr = MAKELCID(MAKELANGID(LANG_FRENCH,SUBLANG_FRENCH), + SORT_DEFAULT); + + /* Test DISPID_FONTs for the various properties. */ + test_names_ids(name_Name, "Name", NULL, "", en_us, + DISPID_FONT_NAME, 0, S_OK,1); + test_names_ids(name_Size, "Size", NULL, "", en_us, + DISPID_FONT_SIZE, 0, S_OK,1); + test_names_ids(name_Bold, "Bold", NULL, "", en_us, + DISPID_FONT_BOLD, 0, S_OK,1); + test_names_ids(name_Italic, "Italic", NULL, "", en_us, + DISPID_FONT_ITALIC, 0, S_OK,1); + test_names_ids(name_Underline, "Underline", NULL, "", en_us, + DISPID_FONT_UNDER, 0, S_OK,1); + test_names_ids(name_Strikethrough, "Strikethrough", NULL, "", en_us, + DISPID_FONT_STRIKE, 0, S_OK,1); + test_names_ids(name_Weight, "Weight", NULL, "", en_us, + DISPID_FONT_WEIGHT, 0, S_OK,1); + test_names_ids(name_Charset, "Charset", NULL, "", en_us, + DISPID_FONT_CHARSET, 0, S_OK,1); + + /* Capitalization doesn't matter. */ + test_names_ids(name_nAmE, "nAmE", NULL, "", en_us, + DISPID_FONT_NAME, 0, S_OK,1); + + /* Unknown name. */ + test_names_ids(name_Foo, "Foo", NULL, "", en_us, + DISPID_UNKNOWN, 0, DISP_E_UNKNOWNNAME,1); + + /* Pass several names: first is processed, */ + /* second gets DISPID_UNKNOWN and doesn't affect retval. */ + test_names_ids(name_Italic, "Italic", name_Name, "Name", en_us, + DISPID_FONT_ITALIC, DISPID_UNKNOWN, S_OK,2); + test_names_ids(name_Italic, "Italic", name_Foo, "Foo", en_us, + DISPID_FONT_ITALIC, DISPID_UNKNOWN, S_OK,2); + + /* Locale ID has no effect. */ + test_names_ids(name_Name, "Name", NULL, "", fr_fr, + DISPID_FONT_NAME, 0, S_OK,1); + test_names_ids(name_Nom, "This is not a font", NULL, "", fr_fr, + DISPID_UNKNOWN, 0, DISP_E_UNKNOWNNAME,1); + + /* One of the arguments are invalid */ + test_names_ids(name_Name, "Name", NULL, "", en_us, + 0xdeadbeef, 0xdeadbeef, E_INVALIDARG,0); + test_names_ids(name_Italic, "Italic", NULL, "", en_us, + 0xdeadbeef, 0xdeadbeef, E_INVALIDARG,0); + test_names_ids(name_Foo, "Foo", NULL, "", en_us, + 0xdeadbeef, 0xdeadbeef, E_INVALIDARG,0); + + /* Crazy locale ID? */ + test_names_ids(name_Name, "Name", NULL, "", -1, + DISPID_FONT_NAME, 0, S_OK,1); +} + +static void test_Invoke(void) +{ + IFontDisp *fontdisp; + HRESULT hr; + VARIANTARG vararg; + DISPPARAMS dispparams; + VARIANT varresult; + + hr = pOleCreateFontIndirect(NULL, &IID_IFontDisp, (void **)&fontdisp); + ok_ole_success(hr, "OleCreateFontIndirect"); + + V_VT(&vararg) = VT_BOOL; + V_BOOL(&vararg) = VARIANT_FALSE; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 1; + dispparams.rgvarg = &vararg; + hr = IFontDisp_Invoke(fontdisp, DISPID_FONT_BOLD, &IID_IFontDisp, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_UNKNOWNINTERFACE, "IFontDisp_Invoke should have returned DISP_E_UNKNOWNINTERFACE instead of 0x%08x\n", hr); + + dispparams.cArgs = 0; + dispparams.rgvarg = NULL; + hr = IFontDisp_Invoke(fontdisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_BADPARAMCOUNT, "IFontDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr); + + hr = IFontDisp_Invoke(fontdisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_PROPERTYPUT, NULL, NULL, NULL, NULL); + ok(hr == DISP_E_PARAMNOTOPTIONAL, "IFontDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr); + + hr = IFontDisp_Invoke(fontdisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, NULL, NULL, NULL); + ok(hr == DISP_E_PARAMNOTOPTIONAL, "IFontDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr); + + hr = IFontDisp_Invoke(fontdisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, &varresult, NULL, NULL); + ok_ole_success(hr, "IFontDisp_Invoke"); + + hr = IFontDisp_Invoke(fontdisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_METHOD, NULL, &varresult, NULL, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IFontDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr); + + hr = IFontDisp_Invoke(fontdisp, 0xdeadbeef, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, &varresult, NULL, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IFontDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr); + + dispparams.cArgs = 1; + dispparams.rgvarg = &vararg; + hr = IFontDisp_Invoke(fontdisp, DISPID_FONT_BOLD, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL); + ok_ole_success(hr, "IFontDisp_Invoke"); + + IFontDisp_Release(fontdisp); +} + +static void test_IsEqual(void) +{ + FONTDESC fd; + LPVOID pvObj = NULL; + LPVOID pvObj2 = NULL; + IFont* ifnt = NULL; + IFont* ifnt2 = NULL; + HRESULT hres; + + /* Basic font description */ + fd.cbSizeofstruct = sizeof(FONTDESC); + fd.lpstrName = system_font; + S(fd.cySize).Lo = 100; + S(fd.cySize).Hi = 100; + fd.sWeight = 0; + fd.sCharset = 0; + fd.fItalic = 0; + fd.fUnderline = 0; + fd.fStrikethrough = 0; + + /* Create font */ + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj); + ifnt = pvObj; + + /* Test equal fonts */ + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + ifnt2 = pvObj2; + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_OK, + "IFont_IsEqual: (EQUAL) Expected S_OK but got 0x%08x\n",hres); + IFont_Release(ifnt2); + + /* Check for bad pointer */ + hres = IFont_IsEqual(ifnt,NULL); + ok(hres == E_POINTER, + "IFont_IsEqual: (NULL) Expected 0x80004003 but got 0x%08x\n",hres); + + /* Test strName */ + fd.lpstrName = arial_font; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (strName) Expected S_FALSE but got 0x%08x\n",hres); + fd.lpstrName = system_font; + IFont_Release(ifnt2); + + /* Test lo font size */ + S(fd.cySize).Lo = 10000; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + ifnt2 = pvObj2; + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (Lo font size) Expected S_FALSE but got 0x%08x\n",hres); + S(fd.cySize).Lo = 100; + IFont_Release(ifnt2); + + /* Test hi font size */ + S(fd.cySize).Hi = 10000; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + ifnt2 = pvObj2; + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (Hi font size) Expected S_FALSE but got 0x%08x\n",hres); + S(fd.cySize).Hi = 100; + IFont_Release(ifnt2); + + /* Test font weight */ + fd.sWeight = 100; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + ifnt2 = pvObj2; + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (Weight) Expected S_FALSE but got 0x%08x\n",hres); + fd.sWeight = 0; + IFont_Release(ifnt2); + + /* Test charset */ + fd.sCharset = 1; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (Charset) Expected S_FALSE but got 0x%08x\n",hres); + fd.sCharset = 0; + IFont_Release(ifnt2); + + /* Test italic setting */ + fd.fItalic = 1; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (Italic) Expected S_FALSE but got 0x%08x\n",hres); + fd.fItalic = 0; + IFont_Release(ifnt2); + + /* Test underline setting */ + fd.fUnderline = 1; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (Underline) Expected S_FALSE but got 0x%08x\n",hres); + fd.fUnderline = 0; + IFont_Release(ifnt2); + + /* Test strikethrough setting */ + fd.fStrikethrough = 1; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + hres = IFont_IsEqual(ifnt,ifnt2); + ok(hres == S_FALSE, + "IFont_IsEqual: (Strikethrough) Expected S_FALSE but got 0x%08x\n",hres); + fd.fStrikethrough = 0; + IFont_Release(ifnt2); + + /* Free IFont. */ + IFont_Release(ifnt); +} + +static void test_ReleaseHfont(void) +{ + FONTDESC fd; + LPVOID pvObj1 = NULL; + LPVOID pvObj2 = NULL; + IFont* ifnt1 = NULL; + IFont* ifnt2 = NULL; + HFONT hfnt1 = 0; + HFONT hfnt2 = 0; + HRESULT hres; + + /* Basic font description */ + fd.cbSizeofstruct = sizeof(FONTDESC); + fd.lpstrName = system_font; + S(fd.cySize).Lo = 100; + S(fd.cySize).Hi = 100; + fd.sWeight = 0; + fd.sCharset = 0; + fd.fItalic = 0; + fd.fUnderline = 0; + fd.fStrikethrough = 0; + + /* Create HFONTs and IFONTs */ + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj1); + ifnt1 = pvObj1; + IFont_get_hFont(ifnt1,&hfnt1); + fd.lpstrName = arial_font; + pOleCreateFontIndirect(&fd, &IID_IFont, &pvObj2); + ifnt2 = pvObj2; + IFont_get_hFont(ifnt2,&hfnt2); + + /* Try invalid HFONT */ + hres = IFont_ReleaseHfont(ifnt1,NULL); + ok(hres == E_INVALIDARG, + "IFont_ReleaseHfont: (Bad HFONT) Expected E_INVALIDARG but got 0x%08x\n", + hres); + + /* Try to add a bad HFONT */ + hres = IFont_ReleaseHfont(ifnt1,(HFONT)32); + ok(hres == S_FALSE, + "IFont_ReleaseHfont: (Bad HFONT) Expected S_FALSE but got 0x%08x\n", + hres); + + /* Release all refs */ + hres = IFont_ReleaseHfont(ifnt1,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + hres = IFont_ReleaseHfont(ifnt2,hfnt2); + ok(hres == S_OK, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + /* Check that both lists are empty */ + hres = IFont_ReleaseHfont(ifnt1,hfnt1); + ok(hres == S_FALSE, + "IFont_AddRefHfont: (Release ref) Expected S_FALSE but got 0x%08x\n", + hres); + + /* The list should be empty */ + hres = IFont_ReleaseHfont(ifnt2,hfnt2); + ok(hres == S_FALSE, + "IFont_AddRefHfont: (Release ref) Expected S_FALSE but got 0x%08x\n", + hres); + + IFont_Release(ifnt1); + IFont_Release(ifnt2); +} + +static void test_AddRefHfont(void) +{ + FONTDESC fd; + IFont* ifnt1 = NULL; + IFont* ifnt2 = NULL; + IFont* ifnt3 = NULL; + HFONT hfnt1 = 0; + HFONT hfnt2 = 0; + HFONT hfnt3 = 0; + HRESULT hres; + + /* Basic font description */ + fd.cbSizeofstruct = sizeof(FONTDESC); + fd.lpstrName = system_font; + S(fd.cySize).Lo = 100; + S(fd.cySize).Hi = 100; + fd.sWeight = 0; + fd.sCharset = 0; + fd.fItalic = 0; + fd.fUnderline = 0; + fd.fStrikethrough = 0; + + /* Create HFONTs and IFONTs */ + pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt1); + IFont_get_hFont(ifnt1,&hfnt1); + fd.lpstrName = arial_font; + pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt2); + IFont_get_hFont(ifnt2,&hfnt2); + + /* Try invalid HFONT */ + hres = IFont_AddRefHfont(ifnt1,NULL); + ok(hres == E_INVALIDARG, + "IFont_AddRefHfont: (Bad HFONT) Expected E_INVALIDARG but got 0x%08x\n", + hres); + + /* Try to add a bad HFONT */ + hres = IFont_AddRefHfont(ifnt1,(HFONT)32); + ok(hres == S_FALSE, + "IFont_AddRefHfont: (Bad HFONT) Expected S_FALSE but got 0x%08x\n", + hres); + + /* Add simple IFONT HFONT pair */ + hres = IFont_AddRefHfont(ifnt1,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Add ref) Expected S_OK but got 0x%08x\n", + hres); + + /* IFONT and HFONT do not have to be the same (always looks at HFONT) */ + hres = IFont_AddRefHfont(ifnt2,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Add ref) Expected S_OK but got 0x%08x\n", + hres); + + /* Release all hfnt1 refs */ + hres = IFont_ReleaseHfont(ifnt1,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + hres = IFont_ReleaseHfont(ifnt1,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + hres = IFont_ReleaseHfont(ifnt1,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + /* Check if hfnt1 is empty */ + hres = IFont_ReleaseHfont(ifnt1,hfnt1); + ok(hres == S_FALSE, + "IFont_AddRefHfont: (Release ref) Expected S_FALSE but got 0x%08x\n", + hres); + + /* Release all hfnt2 refs */ + hres = IFont_ReleaseHfont(ifnt2,hfnt2); + ok(hres == S_OK, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + /* Check if hfnt2 is empty */ + hres = IFont_ReleaseHfont(ifnt2,hfnt2); + ok(hres == S_FALSE, + "IFont_AddRefHfont: (Release ref) Expected S_FALSE but got 0x%08x\n", + hres); + + /* Show that releasing an IFONT does not always release it from the HFONT cache. */ + + IFont_Release(ifnt1); + + /* Add a reference for destroyed hfnt1 */ + hres = IFont_AddRefHfont(ifnt2,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Add ref) Expected S_OK but got 0x%08x\n", + hres); + + /* Decrement reference for destroyed hfnt1 */ + hres = IFont_ReleaseHfont(ifnt2,hfnt1); + ok(hres == S_OK, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + /* Shows that releasing all IFONT's does clear the HFONT cache. */ + + IFont_Release(ifnt2); + + /* Need to make a new IFONT for testing */ + fd.fUnderline = 1; + pOleCreateFontIndirect(&fd, &IID_IFont, (void **)&ifnt3); + IFont_get_hFont(ifnt3,&hfnt3); + + /* Add a reference for destroyed hfnt1 */ + hres = IFont_AddRefHfont(ifnt3,hfnt1); + ok(hres == S_FALSE, + "IFont_AddRefHfont: (Add ref) Expected S_OK but got 0x%08x\n", + hres); + + /* Decrement reference for destroyed hfnt1 */ + hres = IFont_ReleaseHfont(ifnt3,hfnt1); + ok(hres == S_FALSE, + "IFont_AddRefHfont: (Release ref) Expected S_OK but got 0x%08x\n", + hres); + + IFont_Release(ifnt3); +} + +START_TEST(olefont) +{ + hOleaut32 = GetModuleHandleA("oleaut32.dll"); + pOleCreateFontIndirect = (void*)GetProcAddress(hOleaut32, "OleCreateFontIndirect"); + if (!pOleCreateFontIndirect) + { + skip("OleCreateFontIndirect not available\n"); + return; + } + + test_QueryInterface(); + test_type_info(); + + /* Test various size operations and conversions. */ + /* Add more as needed. */ + test_ifont_sizes(180000, 0, 72, 2540, -18, "default"); + test_ifont_sizes(180000, 0, 144, 2540, -36, "ratio1"); /* change ratio */ + test_ifont_sizes(180000, 0, 72, 1270, -36, "ratio2"); /* 2nd part of ratio */ + + /* These depend on details of how IFont rounds sizes internally. */ + test_ifont_sizes(0, 0, 72, 2540, 0, "zero size"); /* zero size */ + test_ifont_sizes(186000, 0, 72, 2540, -19, "rounding"); /* test rounding */ + + test_font_events_disp(); + test_GetIDsOfNames(); + test_Invoke(); + test_IsEqual(); + test_ReleaseHfont(); + test_AddRefHfont(); +} diff --git a/rostests/winetests/oleaut32/olepicture.c b/rostests/winetests/oleaut32/olepicture.c new file mode 100644 index 00000000000..c0d0de9d969 --- /dev/null +++ b/rostests/winetests/oleaut32/olepicture.c @@ -0,0 +1,799 @@ +/* + * OLEPICTURE test program + * + * Copyright 2005 Marcus Meissner + * + * + * 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 +#include +#include +#include +#include + +#define COBJMACROS + +#include "wine/test.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define expect_eq(expr, value, type, format) { type ret = (expr); ok((value) == ret, #expr " expected " format " got " format "\n", value, ret); } + +#define ole_expect(expr, expect) { \ + HRESULT r = expr; \ + ok(r == (expect), #expr " returned %x, expected %s (%x)\n", r, #expect, expect); \ +} + +#define ole_check(expr) ole_expect(expr, S_OK); + +static HMODULE hOleaut32; + +static HRESULT (WINAPI *pOleLoadPicture)(LPSTREAM,LONG,BOOL,REFIID,LPVOID*); +static HRESULT (WINAPI *pOleCreatePictureIndirect)(PICTDESC*,REFIID,BOOL,LPVOID*); + +#define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) + +/* 1x1 pixel gif */ +static const unsigned char gifimage[35] = { +0x47,0x49,0x46,0x38,0x37,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x00,0xff,0xff,0xff, +0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x02,0x44, +0x01,0x00,0x3b +}; + +/* 1x1 pixel jpg */ +static const unsigned char jpgimage[285] = { +0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x01,0x01,0x2c, +0x01,0x2c,0x00,0x00,0xff,0xdb,0x00,0x43,0x00,0x05,0x03,0x04,0x04,0x04,0x03,0x05, +0x04,0x04,0x04,0x05,0x05,0x05,0x06,0x07,0x0c,0x08,0x07,0x07,0x07,0x07,0x0f,0x0b, +0x0b,0x09,0x0c,0x11,0x0f,0x12,0x12,0x11,0x0f,0x11,0x11,0x13,0x16,0x1c,0x17,0x13, +0x14,0x1a,0x15,0x11,0x11,0x18,0x21,0x18,0x1a,0x1d,0x1d,0x1f,0x1f,0x1f,0x13,0x17, +0x22,0x24,0x22,0x1e,0x24,0x1c,0x1e,0x1f,0x1e,0xff,0xdb,0x00,0x43,0x01,0x05,0x05, +0x05,0x07,0x06,0x07,0x0e,0x08,0x08,0x0e,0x1e,0x14,0x11,0x14,0x1e,0x1e,0x1e,0x1e, +0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, +0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e, +0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0xff,0xc0, +0x00,0x11,0x08,0x00,0x01,0x00,0x01,0x03,0x01,0x22,0x00,0x02,0x11,0x01,0x03,0x11, +0x01,0xff,0xc4,0x00,0x15,0x00,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xff,0xc4,0x00,0x14,0x10,0x01,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xc4, +0x00,0x14,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0xff,0xc4,0x00,0x14,0x11,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xda,0x00,0x0c,0x03,0x01, +0x00,0x02,0x11,0x03,0x11,0x00,0x3f,0x00,0xb2,0xc0,0x07,0xff,0xd9 +}; + +/* 1x1 pixel png */ +static const unsigned char pngimage[285] = { +0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, +0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x08,0x02,0x00,0x00,0x00,0x90,0x77,0x53, +0xde,0x00,0x00,0x00,0x09,0x70,0x48,0x59,0x73,0x00,0x00,0x0b,0x13,0x00,0x00,0x0b, +0x13,0x01,0x00,0x9a,0x9c,0x18,0x00,0x00,0x00,0x07,0x74,0x49,0x4d,0x45,0x07,0xd5, +0x06,0x03,0x0f,0x07,0x2d,0x12,0x10,0xf0,0xfd,0x00,0x00,0x00,0x0c,0x49,0x44,0x41, +0x54,0x08,0xd7,0x63,0xf8,0xff,0xff,0x3f,0x00,0x05,0xfe,0x02,0xfe,0xdc,0xcc,0x59, +0xe7,0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x82 +}; + +/* 1x1 pixel bmp */ +static const unsigned char bmpimage[66] = { +0x42,0x4d,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x00,0x00,0x00,0x28,0x00, +0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x00, +0x00,0x00,0x04,0x00,0x00,0x00,0x12,0x0b,0x00,0x00,0x12,0x0b,0x00,0x00,0x02,0x00, +0x00,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0x00, +0x00,0x00 +}; + +/* 2x2 pixel gif */ +static const unsigned char gif4pixel[42] = { +0x47,0x49,0x46,0x38,0x37,0x61,0x02,0x00,0x02,0x00,0xa1,0x00,0x00,0x00,0x00,0x00, +0x39,0x62,0xfc,0xff,0x1a,0xe5,0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x02,0x00, +0x02,0x00,0x00,0x02,0x03,0x14,0x16,0x05,0x00,0x3b +}; + +/* APM with an empty metafile with some padding zeros - looks like under Window the + * metafile data should be at least 20 bytes */ +static const unsigned char apmdata[] = { +0xd7,0xcd,0xc6,0x9a, 0x00,0x00,0x00,0x00, 0x00,0x00,0xee,0x02, 0xb1,0x03,0xa0,0x05, +0x00,0x00,0x00,0x00, 0xee,0x53,0x01,0x00, 0x09,0x00,0x00,0x03, 0x13,0x00,0x00,0x00, +0x01,0x00,0x05,0x00, 0x00,0x00,0x00,0x00, 0x03,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00 +}; + +struct NoStatStreamImpl +{ + const IStreamVtbl *lpVtbl; + LONG ref; + + HGLOBAL supportHandle; + ULARGE_INTEGER streamSize; + ULARGE_INTEGER currentPosition; +}; +typedef struct NoStatStreamImpl NoStatStreamImpl; +static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal); + +static void +test_pic_with_stream(LPSTREAM stream, unsigned int imgsize) +{ + IPicture* pic = NULL; + HRESULT hres; + LPVOID pvObj = NULL; + OLE_HANDLE handle, hPal; + OLE_XSIZE_HIMETRIC width; + OLE_YSIZE_HIMETRIC height; + short type; + DWORD attr; + ULONG res; + + pvObj = NULL; + hres = pOleLoadPicture(stream, imgsize, TRUE, &IID_IPicture, &pvObj); + pic = pvObj; + + ok(hres == S_OK,"OLP (NULL,..) does not return 0, but 0x%08x\n",hres); + ok(pic != NULL,"OLP (NULL,..) returns NULL, instead of !NULL\n"); + if (pic == NULL) + return; + + pvObj = NULL; + hres = IPicture_QueryInterface (pic, &IID_IPicture, &pvObj); + + ok(hres == S_OK,"IPicture_QI does not return S_OK, but 0x%08x\n", hres); + ok(pvObj != NULL,"IPicture_QI does return NULL, instead of a ptr\n"); + + IPicture_Release ((IPicture*)pvObj); + + handle = 0; + hres = IPicture_get_Handle (pic, &handle); + ok(hres == S_OK,"IPicture_get_Handle does not return S_OK, but 0x%08x\n", hres); + ok(handle != 0, "IPicture_get_Handle returns a NULL handle, but it should be non NULL\n"); + + width = 0; + hres = IPicture_get_Width (pic, &width); + ok(hres == S_OK,"IPicture_get_Width does not return S_OK, but 0x%08x\n", hres); + ok(width != 0, "IPicture_get_Width returns 0, but it should not be 0.\n"); + + height = 0; + hres = IPicture_get_Height (pic, &height); + ok(hres == S_OK,"IPicture_get_Height does not return S_OK, but 0x%08x\n", hres); + ok(height != 0, "IPicture_get_Height returns 0, but it should not be 0.\n"); + + type = 0; + hres = IPicture_get_Type (pic, &type); + ok(hres == S_OK,"IPicture_get_Type does not return S_OK, but 0x%08x\n", hres); + ok(type == PICTYPE_BITMAP, "IPicture_get_Type returns %d, but it should be PICTYPE_BITMAP(%d).\n", type, PICTYPE_BITMAP); + + attr = 0; + hres = IPicture_get_Attributes (pic, &attr); + ok(hres == S_OK,"IPicture_get_Attributes does not return S_OK, but 0x%08x\n", hres); + ok(attr == 0, "IPicture_get_Attributes returns %d, but it should be 0.\n", attr); + + hPal = 0; + hres = IPicture_get_hPal (pic, &hPal); + ok(hres == S_OK,"IPicture_get_hPal does not return S_OK, but 0x%08x\n", hres); + /* a single pixel b/w image has no palette */ + ok(hPal == 0, "IPicture_get_hPal returns %d, but it should be 0.\n", hPal); + + res = IPicture_Release (pic); + ok (res == 0, "refcount after release is %d, but should be 0?\n", res); +} + +static void +test_pic(const unsigned char *imgdata, unsigned int imgsize) +{ + LPSTREAM stream; + HGLOBAL hglob; + LPBYTE data; + HRESULT hres; + LARGE_INTEGER seekto; + ULARGE_INTEGER newpos1; + DWORD * header; + unsigned int i,j; + + /* Let the fun begin */ + hglob = GlobalAlloc (0, imgsize); + data = GlobalLock (hglob); + memcpy(data, imgdata, imgsize); + GlobalUnlock(hglob); data = NULL; + + hres = CreateStreamOnHGlobal (hglob, FALSE, &stream); + ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08x\n", hres); + + memset(&seekto,0,sizeof(seekto)); + hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1); + ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres); + test_pic_with_stream(stream, imgsize); + + IStream_Release(stream); + + /* again with Non Statable and Non Seekable stream */ + stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob); + hglob = 0; /* Non-statable impl always deletes on release */ + test_pic_with_stream(stream, 0); + + IStream_Release(stream); + for (i = 1; i <= 8; i++) { + /* more fun!!! */ + hglob = GlobalAlloc (0, imgsize + i * (2 * sizeof(DWORD))); + data = GlobalLock (hglob); + header = (DWORD *)data; + + /* multiple copies of header */ + memcpy(data,"lt\0\0",4); + header[1] = imgsize; + for (j = 2; j <= i; j++) { + memcpy(&(header[2 * (j - 1)]), header, 2 * sizeof(DWORD)); + } + memcpy(data + i * (2 * sizeof(DWORD)), imgdata, imgsize); + GlobalUnlock(hglob); data = NULL; + + hres = CreateStreamOnHGlobal (hglob, FALSE, &stream); + ok (hres == S_OK, "createstreamonhglobal failed? doubt it... hres 0x%08x\n", hres); + + memset(&seekto,0,sizeof(seekto)); + hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1); + ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres); + test_pic_with_stream(stream, imgsize); + + IStream_Release(stream); + + /* again with Non Statable and Non Seekable stream */ + stream = (LPSTREAM)NoStatStreamImpl_Construct(hglob); + hglob = 0; /* Non-statable impl always deletes on release */ + test_pic_with_stream(stream, 0); + + IStream_Release(stream); + } +} + +static void test_empty_image(void) { + LPBYTE data; + LPSTREAM stream; + IPicture* pic = NULL; + HRESULT hres; + LPVOID pvObj = NULL; + HGLOBAL hglob; + OLE_HANDLE handle; + ULARGE_INTEGER newpos1; + LARGE_INTEGER seekto; + short type; + + /* Empty image. Happens occasionally in VB programs. */ + hglob = GlobalAlloc (0, 8); + data = GlobalLock (hglob); + memcpy(data,"lt\0\0",4); + ((DWORD*)data)[1] = 0; + hres = CreateStreamOnHGlobal (hglob, TRUE, &stream); + ok (hres == S_OK, "CreatestreamOnHGlobal failed? doubt it... hres 0x%08x\n", hres); + + memset(&seekto,0,sizeof(seekto)); + hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1); + ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres); + + pvObj = NULL; + hres = pOleLoadPicture(stream, 8, TRUE, &IID_IPicture, &pvObj); + pic = pvObj; + ok(hres == S_OK,"empty picture not loaded, hres 0x%08x\n", hres); + ok(pic != NULL,"empty picture not loaded, pic is NULL\n"); + + hres = IPicture_get_Type (pic, &type); + ok (hres == S_OK,"empty picture get type failed with hres 0x%08x\n", hres); + ok (type == PICTYPE_NONE,"type is %d, but should be PICTYPE_NONE(0)\n", type); + + hres = IPicture_get_Handle (pic, &handle); + ok (hres == S_OK,"empty picture get handle failed with hres 0x%08x\n", hres); + ok (handle == 0, "empty picture get handle did not return 0, but 0x%08x\n", handle); + IPicture_Release (pic); +} + +static void test_empty_image_2(void) { + LPBYTE data; + LPSTREAM stream; + IPicture* pic = NULL; + HRESULT hres; + LPVOID pvObj = NULL; + HGLOBAL hglob; + ULARGE_INTEGER newpos1; + LARGE_INTEGER seekto; + short type; + + /* Empty image at random stream position. */ + hglob = GlobalAlloc (0, 200); + data = GlobalLock (hglob); + data += 42; + memcpy(data,"lt\0\0",4); + ((DWORD*)data)[1] = 0; + hres = CreateStreamOnHGlobal (hglob, TRUE, &stream); + ok (hres == S_OK, "CreatestreamOnHGlobal failed? doubt it... hres 0x%08x\n", hres); + + memset(&seekto,0,sizeof(seekto)); + seekto.u.LowPart = 42; + hres = IStream_Seek(stream,seekto,SEEK_CUR,&newpos1); + ok (hres == S_OK, "istream seek failed? doubt it... hres 0x%08x\n", hres); + + pvObj = NULL; + hres = pOleLoadPicture(stream, 8, TRUE, &IID_IPicture, &pvObj); + pic = pvObj; + ok(hres == S_OK,"empty picture not loaded, hres 0x%08x\n", hres); + ok(pic != NULL,"empty picture not loaded, pic is NULL\n"); + + hres = IPicture_get_Type (pic, &type); + ok (hres == S_OK,"empty picture get type failed with hres 0x%08x\n", hres); + ok (type == PICTYPE_NONE,"type is %d, but should be PICTYPE_NONE(0)\n", type); + + IPicture_Release (pic); +} + +static void test_Invoke(void) +{ + IPictureDisp *picdisp; + HRESULT hr; + VARIANTARG vararg; + DISPPARAMS dispparams; + VARIANT varresult; + IStream *stream; + HGLOBAL hglob; + void *data; + + hglob = GlobalAlloc (0, sizeof(gifimage)); + data = GlobalLock(hglob); + memcpy(data, gifimage, sizeof(gifimage)); + GlobalUnlock(hglob); + + hr = CreateStreamOnHGlobal (hglob, FALSE, &stream); + ok_ole_success(hr, "CreateStreamOnHGlobal"); + + hr = pOleLoadPicture(stream, sizeof(gifimage), TRUE, &IID_IPictureDisp, (void **)&picdisp); + IStream_Release(stream); + ok_ole_success(hr, "OleLoadPicture"); + + V_VT(&vararg) = VT_BOOL; + V_BOOL(&vararg) = VARIANT_FALSE; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 1; + dispparams.rgvarg = &vararg; + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_IPictureDisp, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_UNKNOWNNAME, "IPictureDisp_Invoke should have returned DISP_E_UNKNOWNNAME instead of 0x%08x\n", hr); + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_IUnknown, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_UNKNOWNNAME, "IPictureDisp_Invoke should have returned DISP_E_UNKNOWNNAME instead of 0x%08x\n", hr); + + dispparams.cArgs = 0; + dispparams.rgvarg = NULL; + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr); + + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYPUT, NULL, NULL, NULL, NULL); + ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr); + + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, NULL, NULL, NULL); + ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr); + + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, NULL, &varresult, NULL, NULL); + ok(hr == DISP_E_PARAMNOTOPTIONAL, "IPictureDisp_Invoke should have returned DISP_E_PARAMNOTOPTIONAL instead of 0x%08x\n", hr); + + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL); + ok_ole_success(hr, "IPictureDisp_Invoke"); + ok(V_VT(&varresult) == VT_I4, "V_VT(&varresult) should have been VT_UINT instead of %d\n", V_VT(&varresult)); + + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_METHOD, &dispparams, &varresult, NULL, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IPictureDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr); + + hr = IPictureDisp_Invoke(picdisp, 0xdeadbeef, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "IPictureDisp_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr); + + dispparams.cArgs = 1; + dispparams.rgvarg = &vararg; + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL); + ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr); + + dispparams.cArgs = 1; + dispparams.rgvarg = &vararg; + hr = IPictureDisp_Invoke(picdisp, DISPID_PICT_HPAL, &IID_NULL, 0, DISPATCH_PROPERTYGET, &dispparams, &varresult, NULL, NULL); + ok(hr == DISP_E_BADPARAMCOUNT, "IPictureDisp_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr); + + IPictureDisp_Release(picdisp); +} + +static void test_OleCreatePictureIndirect(void) +{ + IPicture *pict; + HRESULT hr; + short type; + OLE_HANDLE handle; + + if(!pOleCreatePictureIndirect) + { + skip("Skipping OleCreatePictureIndirect tests\n"); + return; + } + + hr = pOleCreatePictureIndirect(NULL, &IID_IPicture, TRUE, (void**)&pict); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = IPicture_get_Type(pict, &type); + ok(hr == S_OK, "hr %08x\n", hr); + ok(type == PICTYPE_UNINITIALIZED, "type %d\n", type); + + hr = IPicture_get_Handle(pict, &handle); + ok(hr == S_OK, "hr %08x\n", hr); + ok(handle == 0, "handle %08x\n", handle); + + IPicture_Release(pict); +} + +static void test_apm() +{ + OLE_HANDLE handle; + LPSTREAM stream; + IPicture *pict; + HGLOBAL hglob; + LPBYTE *data; + LONG cxy; + BOOL keep; + short type; + + hglob = GlobalAlloc (0, sizeof(apmdata)); + data = GlobalLock(hglob); + memcpy(data, apmdata, sizeof(apmdata)); + + ole_check(CreateStreamOnHGlobal(hglob, TRUE, &stream)); + ole_check(OleLoadPictureEx(stream, sizeof(apmdata), TRUE, &IID_IPicture, 100, 100, 0, (LPVOID *)&pict)); + + ole_check(IPicture_get_Handle(pict, &handle)); + ok(handle != 0, "handle is null\n"); + + ole_check(IPicture_get_Type(pict, &type)); + expect_eq(type, PICTYPE_METAFILE, short, "%d"); + + ole_check(IPicture_get_Height(pict, &cxy)); + expect_eq(cxy, 1667, LONG, "%d"); + + ole_check(IPicture_get_Width(pict, &cxy)); + expect_eq(cxy, 1323, LONG, "%d"); + + ole_check(IPicture_get_KeepOriginalFormat(pict, &keep)); + todo_wine expect_eq(keep, FALSE, LONG, "%d"); + + ole_expect(IPicture_get_hPal(pict, &handle), E_FAIL); + IPicture_Release(pict); + IStream_Release(stream); +} + +START_TEST(olepicture) +{ + hOleaut32 = GetModuleHandleA("oleaut32.dll"); + pOleLoadPicture = (void*)GetProcAddress(hOleaut32, "OleLoadPicture"); + pOleCreatePictureIndirect = (void*)GetProcAddress(hOleaut32, "OleCreatePictureIndirect"); + if (!pOleLoadPicture) + { + skip("OleLoadPicture is not available\n"); + return; + } + + /* Test regular 1x1 pixel images of gif, jpg, bmp type */ + test_pic(gifimage, sizeof(gifimage)); + test_pic(jpgimage, sizeof(jpgimage)); + test_pic(bmpimage, sizeof(bmpimage)); + test_pic(gif4pixel, sizeof(gif4pixel)); + /* FIXME: No PNG support yet in Wine or in older Windows... */ + if (0) test_pic(pngimage, sizeof(pngimage)); + test_empty_image(); + test_empty_image_2(); + test_apm(); + + test_Invoke(); + test_OleCreatePictureIndirect(); +} + + +/* Helper functions only ... */ + + +static void NoStatStreamImpl_Destroy(NoStatStreamImpl* This) +{ + GlobalFree(This->supportHandle); + This->supportHandle=0; + HeapFree(GetProcessHeap(), 0, This); +} + +static ULONG WINAPI NoStatStreamImpl_AddRef( + IStream* iface) +{ + NoStatStreamImpl* const This=(NoStatStreamImpl*)iface; + return InterlockedIncrement(&This->ref); +} + +static HRESULT WINAPI NoStatStreamImpl_QueryInterface( + IStream* iface, + REFIID riid, /* [in] */ + void** ppvObject) /* [iid_is][out] */ +{ + NoStatStreamImpl* const This=(NoStatStreamImpl*)iface; + if (ppvObject==0) return E_INVALIDARG; + *ppvObject = 0; + if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) + { + *ppvObject = (IStream*)This; + } + else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0) + { + *ppvObject = (IStream*)This; + } + + if ((*ppvObject)==0) + return E_NOINTERFACE; + NoStatStreamImpl_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI NoStatStreamImpl_Release( + IStream* iface) +{ + NoStatStreamImpl* const This=(NoStatStreamImpl*)iface; + ULONG newRef = InterlockedDecrement(&This->ref); + if (newRef==0) + NoStatStreamImpl_Destroy(This); + return newRef; +} + +static HRESULT WINAPI NoStatStreamImpl_Read( + IStream* iface, + void* pv, /* [length_is][size_is][out] */ + ULONG cb, /* [in] */ + ULONG* pcbRead) /* [out] */ +{ + NoStatStreamImpl* const This=(NoStatStreamImpl*)iface; + void* supportBuffer; + ULONG bytesReadBuffer; + ULONG bytesToReadFromBuffer; + + if (pcbRead==0) + pcbRead = &bytesReadBuffer; + bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb); + supportBuffer = GlobalLock(This->supportHandle); + memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer); + This->currentPosition.u.LowPart+=bytesToReadFromBuffer; + *pcbRead = bytesToReadFromBuffer; + GlobalUnlock(This->supportHandle); + if(*pcbRead == cb) + return S_OK; + return S_FALSE; +} + +static HRESULT WINAPI NoStatStreamImpl_Write( + IStream* iface, + const void* pv, /* [size_is][in] */ + ULONG cb, /* [in] */ + ULONG* pcbWritten) /* [out] */ +{ + NoStatStreamImpl* const This=(NoStatStreamImpl*)iface; + void* supportBuffer; + ULARGE_INTEGER newSize; + ULONG bytesWritten = 0; + + if (pcbWritten == 0) + pcbWritten = &bytesWritten; + if (cb == 0) + return S_OK; + newSize.u.HighPart = 0; + newSize.u.LowPart = This->currentPosition.u.LowPart + cb; + if (newSize.u.LowPart > This->streamSize.u.LowPart) + IStream_SetSize(iface, newSize); + + supportBuffer = GlobalLock(This->supportHandle); + memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb); + This->currentPosition.u.LowPart+=cb; + *pcbWritten = cb; + GlobalUnlock(This->supportHandle); + return S_OK; +} + +static HRESULT WINAPI NoStatStreamImpl_Seek( + IStream* iface, + LARGE_INTEGER dlibMove, /* [in] */ + DWORD dwOrigin, /* [in] */ + ULARGE_INTEGER* plibNewPosition) /* [out] */ +{ + NoStatStreamImpl* const This=(NoStatStreamImpl*)iface; + ULARGE_INTEGER newPosition; + switch (dwOrigin) + { + case STREAM_SEEK_SET: + newPosition.u.HighPart = 0; + newPosition.u.LowPart = 0; + break; + case STREAM_SEEK_CUR: + newPosition = This->currentPosition; + break; + case STREAM_SEEK_END: + newPosition = This->streamSize; + break; + default: + return STG_E_INVALIDFUNCTION; + } + if (dlibMove.QuadPart < 0 && newPosition.QuadPart < -dlibMove.QuadPart) + return STG_E_INVALIDFUNCTION; + newPosition.QuadPart += dlibMove.QuadPart; + if (plibNewPosition) *plibNewPosition = newPosition; + This->currentPosition = newPosition; + return S_OK; +} + +static HRESULT WINAPI NoStatStreamImpl_SetSize( + IStream* iface, + ULARGE_INTEGER libNewSize) /* [in] */ +{ + NoStatStreamImpl* const This=(NoStatStreamImpl*)iface; + HGLOBAL supportHandle; + if (libNewSize.u.HighPart != 0) + return STG_E_INVALIDFUNCTION; + if (This->streamSize.u.LowPart == libNewSize.u.LowPart) + return S_OK; + supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0); + if (supportHandle == 0) + return STG_E_MEDIUMFULL; + This->supportHandle = supportHandle; + This->streamSize.u.LowPart = libNewSize.u.LowPart; + return S_OK; +} + +static HRESULT WINAPI NoStatStreamImpl_CopyTo( + IStream* iface, + IStream* pstm, /* [unique][in] */ + ULARGE_INTEGER cb, /* [in] */ + ULARGE_INTEGER* pcbRead, /* [out] */ + ULARGE_INTEGER* pcbWritten) /* [out] */ +{ + HRESULT hr = S_OK; + BYTE tmpBuffer[128]; + ULONG bytesRead, bytesWritten, copySize; + ULARGE_INTEGER totalBytesRead; + ULARGE_INTEGER totalBytesWritten; + + if ( pstm == 0 ) + return STG_E_INVALIDPOINTER; + totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0; + totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0; + + while ( cb.u.LowPart > 0 ) + { + if ( cb.u.LowPart >= 128 ) + copySize = 128; + else + copySize = cb.u.LowPart; + IStream_Read(iface, tmpBuffer, copySize, &bytesRead); + totalBytesRead.u.LowPart += bytesRead; + IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten); + totalBytesWritten.u.LowPart += bytesWritten; + if (bytesRead != bytesWritten) + { + hr = STG_E_MEDIUMFULL; + break; + } + if (bytesRead!=copySize) + cb.u.LowPart = 0; + else + cb.u.LowPart -= bytesRead; + } + if (pcbRead) + { + pcbRead->u.LowPart = totalBytesRead.u.LowPart; + pcbRead->u.HighPart = totalBytesRead.u.HighPart; + } + + if (pcbWritten) + { + pcbWritten->u.LowPart = totalBytesWritten.u.LowPart; + pcbWritten->u.HighPart = totalBytesWritten.u.HighPart; + } + return hr; +} + +static HRESULT WINAPI NoStatStreamImpl_Commit(IStream* iface,DWORD grfCommitFlags) +{ + return S_OK; +} +static HRESULT WINAPI NoStatStreamImpl_Revert(IStream* iface) { return S_OK; } + +static HRESULT WINAPI NoStatStreamImpl_LockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, /* [in] */ + ULARGE_INTEGER cb, /* [in] */ + DWORD dwLockType) /* [in] */ +{ + return S_OK; +} + +static HRESULT WINAPI NoStatStreamImpl_UnlockRegion( + IStream* iface, + ULARGE_INTEGER libOffset, /* [in] */ + ULARGE_INTEGER cb, /* [in] */ + DWORD dwLockType) /* [in] */ +{ + return S_OK; +} + +static HRESULT WINAPI NoStatStreamImpl_Stat( + IStream* iface, + STATSTG* pstatstg, /* [out] */ + DWORD grfStatFlag) /* [in] */ +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI NoStatStreamImpl_Clone( + IStream* iface, + IStream** ppstm) /* [out] */ +{ + return E_NOTIMPL; +} +static const IStreamVtbl NoStatStreamImpl_Vtbl; + +/* + Build an object that implements IStream, without IStream_Stat capabilities. + Receives a memory handle with data buffer. If memory handle is non-null, + it is assumed to be unlocked, otherwise an internal memory handle is allocated. + In any case the object takes ownership of memory handle and will free it on + object release. + */ +static NoStatStreamImpl* NoStatStreamImpl_Construct(HGLOBAL hGlobal) +{ + NoStatStreamImpl* newStream; + + newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(NoStatStreamImpl)); + if (newStream!=0) + { + newStream->lpVtbl = &NoStatStreamImpl_Vtbl; + newStream->ref = 1; + newStream->supportHandle = hGlobal; + + if (!newStream->supportHandle) + newStream->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD | + GMEM_SHARE, 0); + newStream->currentPosition.u.HighPart = 0; + newStream->currentPosition.u.LowPart = 0; + newStream->streamSize.u.HighPart = 0; + newStream->streamSize.u.LowPart = GlobalSize(newStream->supportHandle); + } + return newStream; +} + + +static const IStreamVtbl NoStatStreamImpl_Vtbl = +{ + NoStatStreamImpl_QueryInterface, + NoStatStreamImpl_AddRef, + NoStatStreamImpl_Release, + NoStatStreamImpl_Read, + NoStatStreamImpl_Write, + NoStatStreamImpl_Seek, + NoStatStreamImpl_SetSize, + NoStatStreamImpl_CopyTo, + NoStatStreamImpl_Commit, + NoStatStreamImpl_Revert, + NoStatStreamImpl_LockRegion, + NoStatStreamImpl_UnlockRegion, + NoStatStreamImpl_Stat, + NoStatStreamImpl_Clone +}; diff --git a/rostests/winetests/oleaut32/safearray.c b/rostests/winetests/oleaut32/safearray.c new file mode 100644 index 00000000000..1857ab5a5ca --- /dev/null +++ b/rostests/winetests/oleaut32/safearray.c @@ -0,0 +1,1712 @@ +/* + * SafeArray test program + * + * Copyright 2002 Marcus Meissner + * + * 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 +#include +#include +#include +#include + +#define COBJMACROS +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wingdi.h" +#include "winnls.h" +#include "winsock.h" +#include "winerror.h" +#include "winnt.h" + +#include "wtypes.h" +#include "oleauto.h" + +static HMODULE hOleaut32; + +static HRESULT (WINAPI *pSafeArrayAllocDescriptorEx)(VARTYPE,UINT,SAFEARRAY**); +static HRESULT (WINAPI *pSafeArrayCopyData)(SAFEARRAY*,SAFEARRAY*); +static HRESULT (WINAPI *pSafeArrayGetIID)(SAFEARRAY*,GUID*); +static HRESULT (WINAPI *pSafeArraySetIID)(SAFEARRAY*,REFGUID); +static HRESULT (WINAPI *pSafeArrayGetVartype)(SAFEARRAY*,VARTYPE*); +static HRESULT (WINAPI *pSafeArrayGetRecordInfo)(SAFEARRAY*,IRecordInfo**); +static SAFEARRAY* (WINAPI *pSafeArrayCreateEx)(VARTYPE,UINT,SAFEARRAYBOUND*,LPVOID); +static SAFEARRAY* (WINAPI *pSafeArrayCreateVector)(VARTYPE,LONG,ULONG); + +#define GETPTR(func) p##func = (void*)GetProcAddress(hOleaut32, #func) + +/* Is a given function exported from oleaut32? */ +#define HAVE_FUNC(func) ((void*)GetProcAddress(hOleaut32, #func) != NULL) + +/* Have IRecordInfo data type? */ +#define HAVE_OLEAUT32_RECORD HAVE_FUNC(SafeArraySetRecordInfo) +/* Have R8 data type? */ +#define HAVE_OLEAUT32_R8 HAVE_FUNC(VarR8FromI1) +/* Have I8/UI8 data type? */ +#define HAVE_OLEAUT32_I8 HAVE_FUNC(VarI8FromI1) +/* Have the decimal type? */ +#define HAVE_OLEAUT32_DECIMAL HAVE_FUNC(VarDecAdd) +/* Have INT_PTR/UINT_PTR type? */ +static BOOL HAVE_OLEAUT32_INT_PTR; + +/* very old version? */ +#define IS_ANCIENT (!HAVE_FUNC(VarI1FromI2)) + +#define START_REF_COUNT 1 +#define RECORD_SIZE 64 +#define RECORD_SIZE_FAIL 17 +/************************************************************************ + * Dummy IRecordInfo Implementation + */ +typedef struct IRecordInfoImpl +{ + const IRecordInfoVtbl *lpvtbl; + LONG ref; + DWORD sizeCalled; + DWORD clearCalled; +} IRecordInfoImpl; + +static const IRecordInfoVtbl IRecordInfoImpl_VTable; + +static IRecordInfoImpl *IRecordInfoImpl_Construct(void) +{ + IRecordInfoImpl *rec; + + rec = HeapAlloc(GetProcessHeap(), 0, sizeof(IRecordInfoImpl)); + rec->lpvtbl = &IRecordInfoImpl_VTable; + rec->ref = START_REF_COUNT; + rec->clearCalled = 0; + rec->sizeCalled = 0; + return rec; +} + +static ULONG CALLBACK IRecordInfoImpl_AddRef(IRecordInfo *iface) +{ + IRecordInfoImpl* This=(IRecordInfoImpl*)iface; + return InterlockedIncrement(&This->ref); +} + +static ULONG CALLBACK IRecordInfoImpl_Release(IRecordInfo *iface) +{ + IRecordInfoImpl* This=(IRecordInfoImpl*)iface; + return InterlockedDecrement(&This->ref); +} + +static BOOL fail_GetSize; /* Whether to fail the GetSize call */ + +static HRESULT CALLBACK IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting) +{ + IRecordInfoImpl* This=(IRecordInfoImpl*)iface; + This->clearCalled++; + return S_OK; +} + +static HRESULT CALLBACK IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG* size) +{ + IRecordInfoImpl* This=(IRecordInfoImpl*)iface; + This->sizeCalled++; + *size = 17; + if (fail_GetSize) + return E_UNEXPECTED; + *size = RECORD_SIZE; + return S_OK; +} + +static HRESULT CALLBACK IRecordInfoImpl_Dummy(IRecordInfo *iface) +{ + trace("Called an unexpected IRecordInfo method - please report!\n"); + /* Quit because we'll just crash anyway */ + fflush(NULL); + exit(255); +} + +static const IRecordInfoVtbl IRecordInfoImpl_VTable = +{ + (PVOID)IRecordInfoImpl_Dummy, + IRecordInfoImpl_AddRef, + IRecordInfoImpl_Release, + (PVOID)IRecordInfoImpl_Dummy, + IRecordInfoImpl_RecordClear, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_GetSize, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy, + (PVOID)IRecordInfoImpl_Dummy +}; + +static DWORD SAFEARRAY_GetVTSize(VARTYPE vt) +{ + switch (vt) + { + case VT_I1: + case VT_UI1: return sizeof(BYTE); + case VT_BOOL: + case VT_I2: + case VT_UI2: return sizeof(SHORT); + case VT_I4: + case VT_UI4: + case VT_R4: + case VT_ERROR: return sizeof(LONG); + case VT_R8: + if (HAVE_OLEAUT32_R8) + return sizeof(LONG64); + case VT_I8: + case VT_UI8: + if (HAVE_OLEAUT32_I8) + return sizeof(LONG64); + break; + case VT_INT: + case VT_UINT: return sizeof(INT); + case VT_INT_PTR: + case VT_UINT_PTR: + if (HAVE_OLEAUT32_INT_PTR) + return sizeof(UINT_PTR); + break; + case VT_CY: return sizeof(CY); + case VT_DATE: return sizeof(DATE); + case VT_BSTR: return sizeof(BSTR); + case VT_DISPATCH: return sizeof(LPDISPATCH); + case VT_VARIANT: return sizeof(VARIANT); + case VT_UNKNOWN: return sizeof(LPUNKNOWN); + case VT_DECIMAL: + if (HAVE_OLEAUT32_DECIMAL) + return sizeof(DECIMAL); + break; + } + return 0; +} + +static void check_for_VT_INT_PTR(void) +{ + /* Set a global flag if VT_INT_PTR is supported */ + + SAFEARRAY* a; + SAFEARRAYBOUND bound; + bound.cElements = 0; + bound.lLbound = 0; + a = SafeArrayCreate(VT_INT_PTR, 1, &bound); + if (a) { + trace("VT_INT_PTR is supported\n"); + HAVE_OLEAUT32_INT_PTR = TRUE; + SafeArrayDestroy(a); + } + else { + trace("VT_INT_PTR is not supported\n"); + HAVE_OLEAUT32_INT_PTR = FALSE; + } +} + +#define VARTYPE_NOT_SUPPORTED 0 +static struct { + VARTYPE vt; /* VT */ + UINT elemsize; /* elementsize by VT */ + UINT expflags; /* fFeatures from SafeArrayAllocDescriptorEx */ + UINT addflags; /* additional fFeatures from SafeArrayCreate */ +} vttypes[] = { +{VT_EMPTY, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_NULL, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_I2, 2, FADF_HAVEVARTYPE,0}, +{VT_I4, 4, FADF_HAVEVARTYPE,0}, +{VT_R4, 4, FADF_HAVEVARTYPE,0}, +{VT_R8, 8, FADF_HAVEVARTYPE,0}, +{VT_CY, 8, FADF_HAVEVARTYPE,0}, +{VT_DATE, 8, FADF_HAVEVARTYPE,0}, +{VT_BSTR, sizeof(BSTR), FADF_HAVEVARTYPE,FADF_BSTR}, +{VT_DISPATCH, sizeof(LPDISPATCH), FADF_HAVEIID, FADF_DISPATCH}, +{VT_ERROR, 4, FADF_HAVEVARTYPE,0}, +{VT_BOOL, 2, FADF_HAVEVARTYPE,0}, +{VT_VARIANT, sizeof(VARIANT), FADF_HAVEVARTYPE,FADF_VARIANT}, +{VT_UNKNOWN, sizeof(LPUNKNOWN), FADF_HAVEIID, FADF_UNKNOWN}, +{VT_DECIMAL, sizeof(DECIMAL), FADF_HAVEVARTYPE,0}, +{15, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, /* no VT_xxx */ +{VT_I1, 1, FADF_HAVEVARTYPE,0}, +{VT_UI1, 1, FADF_HAVEVARTYPE,0}, +{VT_UI2, 2, FADF_HAVEVARTYPE,0}, +{VT_UI4, 4, FADF_HAVEVARTYPE,0}, +{VT_I8, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_UI8, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_INT, sizeof(INT), FADF_HAVEVARTYPE,0}, +{VT_UINT, sizeof(UINT), FADF_HAVEVARTYPE,0}, +{VT_VOID, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_HRESULT, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_PTR, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_SAFEARRAY,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_CARRAY, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_USERDEFINED,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_LPSTR, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_LPWSTR, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_FILETIME, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_RECORD, VARTYPE_NOT_SUPPORTED,FADF_RECORD,0}, +{VT_BLOB, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_STREAM, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_STORAGE, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_STREAMED_OBJECT,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_STORED_OBJECT,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_BLOB_OBJECT,VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_CF, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +{VT_CLSID, VARTYPE_NOT_SUPPORTED,FADF_HAVEVARTYPE,0}, +}; + +static void test_safearray(void) +{ + SAFEARRAY *a, b, *c; + unsigned int i; + LONG indices[2]; + HRESULT hres; + SAFEARRAYBOUND bound, bounds[2]; + VARIANT v; + LPVOID data; + IID iid; + VARTYPE vt; + LONG l; + unsigned char *ptr1, *ptr2; + + hres = SafeArrayDestroy( NULL); + ok( hres == S_OK, "SafeArrayDestroy( NULL) returned 0x%x\n", hres); + + bound.cElements = 1; + bound.lLbound = 0; + a = SafeArrayCreate(-1, 1, &bound); + ok(NULL == a,"SAC(-1,1,[1,0]) not failed?\n"); + + bound.cElements = 0; + bound.lLbound = 42; + a = SafeArrayCreate(VT_I4, 1, &bound); + ok(NULL != a,"SAC(VT_I4,1,[0,0]) failed.\n"); + + hres = SafeArrayGetLBound(a, 1, &l); + ok(hres == S_OK, "SAGLB of 0 size dimensioned array failed with %x\n",hres); + ok(l == 42, "SAGLB of 0 size dimensioned array failed to return 42, but returned %d\n",l); + hres = SafeArrayGetUBound(a, 1, &l); + ok(hres == S_OK, "SAGUB of 0 size dimensioned array failed with %x\n",hres); + ok(l == 41, "SAGUB of 0 size dimensioned array failed to return 41, but returned %d\n",l); + + hres = SafeArrayAccessData(a, &data); + ok(hres == S_OK, "SafeArrayAccessData of 0 size dimensioned array failed with %x\n", hres); + SafeArrayUnaccessData(a); + + bound.cElements = 2; + hres = SafeArrayRedim(a, &bound); + ok(hres == S_OK,"SAR of a 0 elements dimension failed with hres %x\n", hres); + bound.cElements = 0; + hres = SafeArrayRedim(a, &bound); + ok(hres == S_OK || hres == E_OUTOFMEMORY, + "SAR to a 0 elements dimension failed with hres %x\n", hres); + hres = SafeArrayDestroy(a); + ok(hres == S_OK,"SAD of 0 dim array faild with hres %x\n", hres); + + SafeArrayAllocDescriptor(2, &a); + a->rgsabound[0].cElements = 2; + a->rgsabound[0].lLbound = 1; + a->rgsabound[1].cElements = 4; + a->rgsabound[1].lLbound = 1; + a->cbElements = 2; + SafeArrayAllocData(a); + + indices[0] = 4; + indices[1] = 2; + hres = SafeArrayPtrOfIndex(a, indices, (void **)&ptr1); + ok(hres == S_OK, "SAPOI failed with hres %x\n", hres); + SafeArrayAccessData(a, (void **)&ptr2); + ok(ptr1 - ptr2 == 14, "SAPOI got wrong ptr\n"); + *(WORD *)ptr1 = 0x55aa; + SafeArrayUnaccessData(a); + + bound.cElements = 10; + bound.lLbound = 1; + SafeArrayRedim(a, &bound); + ptr1 = NULL; + SafeArrayPtrOfIndex(a, indices, (void **)&ptr1); + ok(*(WORD *)ptr1 == 0x55aa, "Data not preserved when resizing array\n"); + + bound.cElements = 10; + bound.lLbound = 0; + SafeArrayRedim(a, &bound); + SafeArrayPtrOfIndex(a, indices, (void **)&ptr1); + ok(*(WORD *)ptr1 == 0, "Expanded area not zero-initialized\n"); + + indices[1] = 1; + SafeArrayPtrOfIndex(a, indices, (void **)&ptr1); + ok(*(WORD *)ptr1 == 0x55aa, "Data not preserved when resizing array\n"); + + bounds[0].cElements = 0; bounds[0].lLbound = 1; + bounds[1].cElements = 2; bounds[1].lLbound = 23; + a = SafeArrayCreate(VT_I4,2,bounds); + ok(a != NULL,"SAC(VT_INT32,2,...) with 0 element dim failed.\n"); + bounds[0].cElements = 1; bounds[0].lLbound = 1; + bounds[1].cElements = 0; bounds[1].lLbound = 23; + a = SafeArrayCreate(VT_I4,2,bounds); + ok(a != NULL,"SAC(VT_INT32,2,...) with 0 element dim failed.\n"); + + bounds[0].cElements = 42; bounds[0].lLbound = 1; + bounds[1].cElements = 2; bounds[1].lLbound = 23; + a = SafeArrayCreate(VT_I4,2,bounds); + ok(a != NULL,"SAC(VT_INT32,2,...) failed.\n"); + + hres = SafeArrayGetLBound (a, 0, &l); + ok (hres == DISP_E_BADINDEX, "SAGLB 0 failed with %x\n", hres); + hres = SafeArrayGetLBound (a, 1, &l); + ok (hres == S_OK, "SAGLB 1 failed with %x\n", hres); + ok (l == 1, "SAGLB 1 returned %d instead of 1\n", l); + hres = SafeArrayGetLBound (a, 2, &l); + ok (hres == S_OK, "SAGLB 2 failed with %x\n", hres); + ok (l == 23, "SAGLB 2 returned %d instead of 23\n", l); + hres = SafeArrayGetLBound (a, 3, &l); + ok (hres == DISP_E_BADINDEX, "SAGLB 3 failed with %x\n", hres); + + hres = SafeArrayGetUBound (a, 0, &l); + ok (hres == DISP_E_BADINDEX, "SAGUB 0 failed with %x\n", hres); + hres = SafeArrayGetUBound (a, 1, &l); + ok (hres == S_OK, "SAGUB 1 failed with %x\n", hres); + ok (l == 42, "SAGUB 1 returned %d instead of 42\n", l); + hres = SafeArrayGetUBound (a, 2, &l); + ok (hres == S_OK, "SAGUB 2 failed with %x\n", hres); + ok (l == 24, "SAGUB 2 returned %d instead of 24\n", l); + hres = SafeArrayGetUBound (a, 3, &l); + ok (hres == DISP_E_BADINDEX, "SAGUB 3 failed with %x\n", hres); + + i = SafeArrayGetDim(a); + ok(i == 2, "getdims of 2 din array returned %d\n",i); + + indices[0] = 0; + indices[1] = 23; + hres = SafeArrayGetElement(a, indices, &i); + ok(DISP_E_BADINDEX == hres,"SAGE failed [0,23], hres 0x%x\n",hres); + + indices[0] = 1; + indices[1] = 22; + hres = SafeArrayGetElement(a, indices, &i); + ok(DISP_E_BADINDEX == hres,"SAGE failed [1,22], hres 0x%x\n",hres); + + indices[0] = 1; + indices[1] = 23; + hres = SafeArrayGetElement(a, indices, &i); + ok(S_OK == hres,"SAGE failed [1,23], hres 0x%x\n",hres); + + indices[0] = 1; + indices[1] = 25; + hres = SafeArrayGetElement(a, indices, &i); + ok(DISP_E_BADINDEX == hres,"SAGE failed [1,24], hres 0x%x\n",hres); + + indices[0] = 3; + indices[1] = 23; + hres = SafeArrayGetElement(a, indices, &i); + ok(S_OK == hres,"SAGE failed [42,23], hres 0x%x\n",hres); + + hres = SafeArrayAccessData(a, (void**)&ptr1); + ok(S_OK == hres, "SAAD failed with 0x%x\n", hres); + + indices[0] = 3; + indices[1] = 23; + hres = SafeArrayPtrOfIndex(a, indices, (void**)&ptr2); + ok(S_OK == hres,"SAPOI failed [1,23], hres 0x%x\n",hres); + ok(ptr2 - ptr1 == 8,"ptr difference is not 8, but %d (%p vs %p)\n", ptr2-ptr1, ptr2, ptr1); + + indices[0] = 3; + indices[1] = 24; + hres = SafeArrayPtrOfIndex(a, indices, (void**)&ptr2); + ok(S_OK == hres,"SAPOI failed [5,24], hres 0x%x\n",hres); + ok(ptr2 - ptr1 == 176,"ptr difference is not 176, but %d (%p vs %p)\n", ptr2-ptr1, ptr2, ptr1); + + indices[0] = 20; + indices[1] = 23; + hres = SafeArrayPtrOfIndex(a, indices, (void**)&ptr2); + ok(S_OK == hres,"SAPOI failed [20,23], hres 0x%x\n",hres); + ok(ptr2 - ptr1 == 76,"ptr difference is not 76, but %d (%p vs %p)\n", ptr2-ptr1, ptr2, ptr1); + + hres = SafeArrayUnaccessData(a); + ok(S_OK == hres, "SAUAD failed with 0x%x\n", hres); + + for (i=0;icbElements) || + (IS_ANCIENT && (vttypes[i].vt == VT_DECIMAL || vttypes[i].vt == VT_I1 || + vttypes[i].vt == VT_UI2 || vttypes[i].vt == VT_UI4 || vttypes[i].vt == VT_INT || + vttypes[i].vt == VT_UINT)), + "SAC(%d,1,[1,0]), %p result %d, expected %d\n", + vttypes[i].vt,a,(a?a->cbElements:0),vttypes[i].elemsize); + + if (a) + { + if (!HAVE_OLEAUT32_RECORD) + vttypes[i].expflags = 0; + ok(a->fFeatures == (vttypes[i].expflags | vttypes[i].addflags), + "SAC of %d returned feature flags %x, expected %x\n", + vttypes[i].vt, a->fFeatures, + vttypes[i].expflags|vttypes[i].addflags); + ok(SafeArrayGetElemsize(a) == vttypes[i].elemsize, + "SAGE for vt %d returned elemsize %d instead of expected %d\n", + vttypes[i].vt, SafeArrayGetElemsize(a),vttypes[i].elemsize); + } + + if (!a) continue; + + if (pSafeArrayGetVartype) + { + hres = pSafeArrayGetVartype(a, &vt); + ok(hres == S_OK, "SAGVT of arra y with vt %d failed with %x\n", vttypes[i].vt, hres); + if (vttypes[i].vt == VT_DISPATCH) { + /* Special case. Checked against Windows. */ + ok(vt == VT_UNKNOWN, "SAGVT of a rray with VT_DISPATCH returned not VT_UNKNOWN, but %d\n", vt); + } else { + ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d\n", vttypes[i].vt, vt); + } + } + + hres = SafeArrayCopy(a, &c); + ok(hres == S_OK, "failed to copy safearray of vt %d with hres %x\n", vttypes[i].vt, hres); + + ok(vttypes[i].elemsize == c->cbElements,"copy of SAC(%d,1,[1,0]), result %d, expected %d\n",vttypes[i].vt,(c?c->cbElements:0),vttypes[i].elemsize + ); + ok(c->fFeatures == (vttypes[i].expflags | vttypes[i].addflags),"SAC of %d returned feature flags %x, expected %x\n", vttypes[i].vt, c->fFeatures, vttypes[i].expflags|vttypes[i].addflags); + ok(SafeArrayGetElemsize(c) == vttypes[i].elemsize,"SAGE for vt %d returned elemsize %d instead of expected %d\n",vttypes[i].vt, SafeArrayGetElemsize(c),vttypes[i].elemsize); + + if (pSafeArrayGetVartype) { + hres = pSafeArrayGetVartype(c, &vt); + ok(hres == S_OK, "SAGVT of array with vt %d failed with %x\n", vttypes[i].vt, hres); + if (vttypes[i].vt == VT_DISPATCH) { + /* Special case. Checked against Windows. */ + ok(vt == VT_UNKNOWN, "SAGVT of array with VT_DISPATCH returned not VT_UNKNOWN, but %d\n", vt); + } else { + ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d\n", vttypes[i].vt, vt); + } + } + + if (pSafeArrayCopyData) { + hres = pSafeArrayCopyData(a, c); + ok(hres == S_OK, "failed to copy safearray data of vt %d with hres %x\n", vttypes[i].vt, hres); + + hres = SafeArrayDestroyData(c); + ok(hres == S_OK,"SADD of copy of array with vt %d failed with hres %x\n", vttypes[i].vt, hres); + } + + hres = SafeArrayDestroy(a); + ok(hres == S_OK,"SAD of array with vt %d failed with hres %x\n", vttypes[i].vt, hres); + } + + /* Test conversion of type|VT_ARRAY <-> VT_BSTR */ + bound.lLbound = 0; + bound.cElements = 10; + a = SafeArrayCreate(VT_UI1, 1, &bound); + ok(a != NULL, "SAC failed.\n"); + ok(S_OK == SafeArrayAccessData(a, &data),"SACD failed\n"); + memcpy(data,"Hello World\n",10); + ok(S_OK == SafeArrayUnaccessData(a),"SAUD failed\n"); + V_VT(&v) = VT_ARRAY|VT_UI1; + V_ARRAY(&v) = a; + hres = VariantChangeTypeEx(&v, &v, 0, 0, VT_BSTR); + ok(hres==S_OK, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR failed with %x\n",hres); + ok(V_VT(&v) == VT_BSTR,"CTE VT_ARRAY|VT_UI1 -> VT_BSTR did not return VT_BSTR, but %d.v\n",V_VT(&v)); + ok(V_BSTR(&v)[0] == 0x6548,"First letter are not 'He', but %x\n", V_BSTR(&v)[0]); + + /* check locking functions */ + a = SafeArrayCreate(VT_I4, 1, &bound); + ok(a!=NULL,"SAC should not fail\n"); + + hres = SafeArrayAccessData(a, &data); + ok(hres == S_OK,"SAAD failed with hres %x\n",hres); + + hres = SafeArrayDestroy(a); + ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy not failed with DISP_E_ARRAYISLOCKED, but with hres %x\n", hres); + + hres = SafeArrayDestroyData(a); + ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy data not failed with DISP_E_ARRAYISLOCKED, but with hres %x\n", hres); + + hres = SafeArrayDestroyDescriptor(a); + ok(hres == DISP_E_ARRAYISLOCKED,"locked safe array destroy descriptor not failed with DISP_E_ARRAYISLOCKED, but with hres %x\n", hres); + + hres = SafeArrayUnaccessData(a); + ok(hres == S_OK,"SAUD failed after lock/destroy test\n"); + + hres = SafeArrayDestroy(a); + ok(hres == S_OK,"SAD failed after lock/destroy test\n"); + + /* Test if we need to destroy data before descriptor */ + a = SafeArrayCreate(VT_I4, 1, &bound); + ok(a!=NULL,"SAC should not fail\n"); + hres = SafeArrayDestroyDescriptor(a); + ok(hres == S_OK,"SADD with data in array failed with hres %x\n",hres); + + + /* IID functions */ + /* init a small stack safearray */ + if (pSafeArraySetIID) { + memset(&b, 0, sizeof(b)); + b.cDims = 1; + memset(&iid, 0x42, sizeof(IID)); + hres = pSafeArraySetIID(&b,&iid); + ok(hres == E_INVALIDARG,"SafeArraySetIID of non IID capable safearray did not return E_INVALIDARG, but %x\n",hres); + + hres = SafeArrayAllocDescriptor(1,&a); + ok((a->fFeatures & FADF_HAVEIID) == 0,"newly allocated descriptor with SAAD should not have FADF_HAVEIID\n"); + hres = pSafeArraySetIID(a,&iid); + ok(hres == E_INVALIDARG,"SafeArraySetIID of newly allocated descriptor with SAAD should return E_INVALIDARG, but %x\n",hres); + } + + if (!pSafeArrayAllocDescriptorEx) + return; + + for (i=0;ifFeatures == vttypes[i].expflags,"SAADE(%d) resulted with flags %x, expected %x\n", vttypes[i].vt, a->fFeatures, vttypes[i].expflags); + if (a->fFeatures & FADF_HAVEIID) { + hres = pSafeArrayGetIID(a, &iid); + ok(hres == S_OK,"SAGIID failed for vt %d with hres %x\n", vttypes[i].vt,hres); + switch (vttypes[i].vt) { + case VT_UNKNOWN: + ok(IsEqualGUID(((GUID*)a)-1,&IID_IUnknown),"guid for VT_UNKNOWN is not IID_IUnknown\n"); + ok(IsEqualGUID(&iid, &IID_IUnknown),"SAGIID returned wrong GUID for IUnknown\n"); + break; + case VT_DISPATCH: + ok(IsEqualGUID(((GUID*)a)-1,&IID_IDispatch),"guid for VT_UNKNOWN is not IID_IDispatch\n"); + ok(IsEqualGUID(&iid, &IID_IDispatch),"SAGIID returned wrong GUID for IDispatch\n"); + break; + default: + ok(FALSE,"unknown vt %d with FADF_HAVEIID\n",vttypes[i].vt); + break; + } + } else { + hres = pSafeArrayGetIID(a, &iid); + ok(hres == E_INVALIDARG,"SAGIID did not fail for vt %d with hres %x\n", vttypes[i].vt,hres); + } + if (a->fFeatures & FADF_RECORD) { + ok(vttypes[i].vt == VT_RECORD,"FADF_RECORD for non record %d\n",vttypes[i].vt); + } + if (a->fFeatures & FADF_HAVEVARTYPE) { + ok(vttypes[i].vt == ((DWORD*)a)[-1], "FADF_HAVEVARTYPE set, but vt %d mismatch stored %d\n",vttypes[i].vt,((DWORD*)a)[-1]); + } + + hres = pSafeArrayGetVartype(a, &vt); + ok(hres == S_OK, "SAGVT of array with vt %d failed with %x\n", vttypes[i].vt, hres); + + if (vttypes[i].vt == VT_DISPATCH) { + /* Special case. Checked against Windows. */ + ok(vt == VT_UNKNOWN, "SAGVT of array with VT_DISPATCH returned not VT_UNKNOWN, but %d\n", vt); + } else { + ok(vt == vttypes[i].vt, "SAGVT of array with vt %d returned %d\n", vttypes[i].vt, vt); + } + + if (a->fFeatures & FADF_HAVEIID) { + hres = pSafeArraySetIID(a, &IID_IStorage); /* random IID */ + ok(hres == S_OK,"SASIID failed with FADF_HAVEIID set for vt %d with %x\n", vttypes[i].vt, hres); + hres = pSafeArrayGetIID(a, &iid); + ok(hres == S_OK,"SAGIID failed with FADF_HAVEIID set for vt %d with %x\n", vttypes[i].vt, hres); + ok(IsEqualGUID(&iid, &IID_IStorage),"returned iid is not IID_IStorage\n"); + } else { + hres = pSafeArraySetIID(a, &IID_IStorage); /* random IID */ + ok(hres == E_INVALIDARG,"SASIID did not failed with !FADF_HAVEIID set for vt %d with %x\n", vttypes[i].vt, hres); + } + hres = SafeArrayDestroyDescriptor(a); + ok(hres == S_OK,"SADD failed with hres %x\n",hres); + } +} + +static void test_SafeArrayAllocDestroyDescriptor(void) +{ + SAFEARRAY *sa; + HRESULT hres; + int i; + + /* Failure cases */ + hres = SafeArrayAllocDescriptor(0, &sa); + ok(hres == E_INVALIDARG, "0 dimensions gave hres 0x%x\n", hres); + + hres = SafeArrayAllocDescriptor(65536, &sa); + ok(IS_ANCIENT || hres == E_INVALIDARG, + "65536 dimensions gave hres 0x%x\n", hres); + + if (0) + { + /* Crashes on 95: XP & Wine return E_POINTER */ + hres=SafeArrayAllocDescriptor(1, NULL); + ok(hres == E_POINTER,"NULL parm gave hres 0x%x\n", hres); + } + + /* Test up to the dimension boundary case */ + for (i = 5; i <= 65535; i += 30) + { + hres = SafeArrayAllocDescriptor(i, &sa); + ok(hres == S_OK, "%d dimensions failed; hres 0x%x\n", i, hres); + + if (hres == S_OK) + { + ok(SafeArrayGetDim(sa) == (UINT)i, "Dimension is %d; should be %d\n", + SafeArrayGetDim(sa), i); + + hres = SafeArrayDestroyDescriptor(sa); + ok(hres == S_OK, "destroy failed; hres 0x%x\n", hres); + } + } + + if (!pSafeArrayAllocDescriptorEx) + return; + + hres = pSafeArrayAllocDescriptorEx(VT_UI1, 0, &sa); + ok(hres == E_INVALIDARG, "0 dimensions gave hres 0x%x\n", hres); + + hres = pSafeArrayAllocDescriptorEx(VT_UI1, 65536, &sa); + ok(hres == E_INVALIDARG, "65536 dimensions gave hres 0x%x\n", hres); + + hres = pSafeArrayAllocDescriptorEx(VT_UI1, 1, NULL); + ok(hres == E_POINTER,"NULL parm gave hres 0x%x\n", hres); + + hres = pSafeArrayAllocDescriptorEx(-1, 1, &sa); + ok(hres == S_OK, "VT = -1 gave hres 0x%x\n", hres); + + sa->rgsabound[0].cElements = 0; + sa->rgsabound[0].lLbound = 1; + + hres = SafeArrayAllocData(sa); + ok(hres == S_OK, "SafeArrayAllocData gave hres 0x%x\n", hres); +} + +static void test_SafeArrayCreateLockDestroy(void) +{ + SAFEARRAYBOUND sab[4]; + SAFEARRAY *sa; + HRESULT hres; + VARTYPE vt; + int dimension; + +#define NUM_DIMENSIONS (int)(sizeof(sab) / sizeof(sab[0])) + + for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++) + { + sab[dimension].lLbound = 0; + sab[dimension].cElements = 8; + } + + /* Failure cases */ +/* This test crashes very early versions with no error checking... + sa = SafeArrayCreate(VT_UI1, 1, NULL); + ok(sa == NULL, "NULL bounds didn't fail\n"); +*/ + sa = SafeArrayCreate(VT_UI1, 65536, sab); + ok(IS_ANCIENT || !sa, "Max bounds didn't fail\n"); + + memset(sab, 0, sizeof(sab)); + + /* Don't test 0 sized dimensions, as Windows has a bug which allows this */ + + for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++) + sab[dimension].cElements = 8; + + /* Test all VARTYPES in 1-4 dimensions */ + for (dimension = 1; dimension < 4; dimension++) + { + for (vt = VT_EMPTY; vt < VT_CLSID; vt++) + { + DWORD dwLen = SAFEARRAY_GetVTSize(vt); + + sa = SafeArrayCreate(vt, dimension, sab); + + if (dwLen) + ok(sa || (IS_ANCIENT && (vt == VT_DECIMAL || vt == VT_I1 || vt == VT_UI2 || + vt == VT_UI4 || vt == VT_INT || vt == VT_UINT)), + "VARTYPE %d (@%d dimensions) failed\n", vt, dimension); + else + ok(sa == NULL || vt == VT_R8, + "VARTYPE %d (@%d dimensions) succeeded!\n", vt, dimension); + + if (sa) + { + ok(SafeArrayGetDim(sa) == (UINT)dimension, + "VARTYPE %d (@%d dimensions) cDims is %d, expected %d\n", + vt, dimension, SafeArrayGetDim(sa), dimension); + ok(SafeArrayGetElemsize(sa) == dwLen || vt == VT_R8, + "VARTYPE %d (@%d dimensions) cbElements is %d, expected %d\n", + vt, dimension, SafeArrayGetElemsize(sa), dwLen); + + if (vt != VT_UNKNOWN && vt != VT_DISPATCH) + { + ok((sa->fFeatures & FADF_HAVEIID) == 0, + "Non interface type should not have FADF_HAVEIID\n"); + if (pSafeArraySetIID) + { + hres = pSafeArraySetIID(sa, &IID_IUnknown); + ok(hres == E_INVALIDARG, + "Non interface type allowed SetIID(), hres %x\n", hres); + } + if (vt != VT_RECORD) + { + VARTYPE aVt; + + ok(IS_ANCIENT || sa->fFeatures & FADF_HAVEVARTYPE, + "Non interface type should have FADF_HAVEVARTYPE\n"); + if (pSafeArrayGetVartype) + { + hres = pSafeArrayGetVartype(sa, &aVt); + ok(hres == S_OK && aVt == vt, + "Non interface type %d: bad type %d, hres %x\n", vt, aVt, hres); + } + } + } + else + { + ok(IS_ANCIENT || sa->fFeatures & FADF_HAVEIID, + "Interface type should have FADF_HAVEIID\n"); + if (pSafeArraySetIID) + { + hres = pSafeArraySetIID(sa, &IID_IUnknown); + ok(hres == S_OK, + "Non interface type disallowed SetIID(), hres %x\n", hres); + } + ok((sa->fFeatures & FADF_HAVEVARTYPE) == 0, + "Interface type %d should not have FADF_HAVEVARTYPE\n", vt); + } + + hres = SafeArrayLock(sa); + ok(hres == S_OK, "Lock VARTYPE %d (@%d dimensions) failed; hres 0x%x\n", + vt, dimension, hres); + + if (hres == S_OK) + { + hres = SafeArrayDestroy(sa); + ok(hres == DISP_E_ARRAYISLOCKED,"Destroy() got hres %x\n", hres); + + hres = SafeArrayDestroyData(sa); + ok(hres == DISP_E_ARRAYISLOCKED,"DestroyData() got hres %x\n", hres); + + hres = SafeArrayDestroyDescriptor(sa); + ok(hres == DISP_E_ARRAYISLOCKED,"DestroyDescriptor() got hres %x\n", hres); + + hres = SafeArrayUnlock(sa); + ok(hres == S_OK, "Unlock VARTYPE %d (@%d dims) hres 0x%x\n", + vt, dimension, hres); + + hres = SafeArrayDestroyDescriptor(sa); + ok(hres == S_OK, "destroy VARTYPE %d (@%d dims) hres 0x%x\n", + vt, dimension, hres); + } + } + } + } +} + +static void test_VectorCreateLockDestroy(void) +{ + SAFEARRAY *sa; + HRESULT hres; + VARTYPE vt; + int element; + + if (!pSafeArrayCreateVector) + return; + sa = pSafeArrayCreateVector(VT_UI1, 0, 0); + ok(sa != NULL, "SACV with 0 elements failed.\n"); + + /* Test all VARTYPES in different lengths */ + for (element = 1; element <= 101; element += 10) + { + for (vt = VT_EMPTY; vt < VT_CLSID; vt++) + { + DWORD dwLen = SAFEARRAY_GetVTSize(vt); + + sa = pSafeArrayCreateVector(vt, 0, element); + + if (dwLen) + ok(sa != NULL, "VARTYPE %d (@%d elements) failed\n", vt, element); + else + ok(sa == NULL, "VARTYPE %d (@%d elements) succeeded!\n", vt, element); + + if (sa) + { + ok(SafeArrayGetDim(sa) == 1, "VARTYPE %d (@%d elements) cDims %d, not 1\n", + vt, element, SafeArrayGetDim(sa)); + ok(SafeArrayGetElemsize(sa) == dwLen, + "VARTYPE %d (@%d elements) cbElements is %d, expected %d\n", + vt, element, SafeArrayGetElemsize(sa), dwLen); + + hres = SafeArrayLock(sa); + ok(hres == S_OK, "Lock VARTYPE %d (@%d elements) failed; hres 0x%x\n", + vt, element, hres); + + if (hres == S_OK) + { + hres = SafeArrayUnlock(sa); + ok(hres == S_OK, "Unlock VARTYPE %d (@%d elements) failed; hres 0x%x\n", + vt, element, hres); + + hres = SafeArrayDestroyDescriptor(sa); + ok(hres == S_OK, "destroy VARTYPE %d (@%d elements) failed; hres 0x%x\n", + vt, element, hres); + } + } + } + } +} + +static void test_LockUnlock(void) +{ + SAFEARRAYBOUND sab[4]; + SAFEARRAY *sa; + HRESULT hres; + BOOL bVector = FALSE; + int dimension; + + /* Failure cases */ + hres = SafeArrayLock(NULL); + ok(hres == E_INVALIDARG, "Lock NULL array hres 0x%x\n", hres); + hres = SafeArrayUnlock(NULL); + ok(hres == E_INVALIDARG, "Lock NULL array hres 0x%x\n", hres); + + for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++) + { + sab[dimension].lLbound = 0; + sab[dimension].cElements = 8; + } + + sa = SafeArrayCreate(VT_UI1, NUM_DIMENSIONS, sab); + + /* Test maximum locks */ +test_LockUnlock_Vector: + if (sa) + { + int count = 0; + + hres = SafeArrayUnlock(sa); + ok (hres == E_UNEXPECTED, "Bad %sUnlock gave hres 0x%x\n", + bVector ? "vector " : "\n", hres); + + while ((hres = SafeArrayLock(sa)) == S_OK) + count++; + ok (count == 65535 && hres == E_UNEXPECTED, "Lock %sfailed at %d; hres 0x%x\n", + bVector ? "vector " : "\n", count, hres); + + if (count == 65535 && hres == E_UNEXPECTED) + { + while ((hres = SafeArrayUnlock(sa)) == S_OK) + count--; + ok (count == 0 && hres == E_UNEXPECTED, "Unlock %sfailed at %d; hres 0x%x\n", + bVector ? "vector " : "\n", count, hres); + } + + SafeArrayDestroy(sa); + } + + if (bVector == FALSE && pSafeArrayCreateVector) + { + /* Test again with a vector */ + sa = pSafeArrayCreateVector(VT_UI1, 0, 100); + bVector = TRUE; + goto test_LockUnlock_Vector; + } +} + +static void test_SafeArrayGetPutElement(void) +{ + SAFEARRAYBOUND sab[4]; + LONG indices[NUM_DIMENSIONS]; + SAFEARRAY *sa; + HRESULT hres; + int value = 0, gotvalue, dimension; + unsigned int x,y,z,a; + + for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++) + { + sab[dimension].lLbound = dimension * 2 + 1; + sab[dimension].cElements = dimension * 3 + 1; + } + + sa = SafeArrayCreate(VT_INT, NUM_DIMENSIONS, sab); + if (!sa) + return; /* Some early versions can't handle > 3 dims */ + + ok(sa->cbElements == sizeof(value), "int size mismatch\n"); + if (sa->cbElements != sizeof(value)) + return; + + /* Failure cases */ + for (x = 0; x < NUM_DIMENSIONS; x++) + { + indices[0] = sab[0].lLbound; + indices[1] = sab[1].lLbound; + indices[2] = sab[2].lLbound; + indices[3] = sab[3].lLbound; + + indices[x] = indices[x] - 1; + hres = SafeArrayPutElement(sa, indices, &value); + ok(hres == DISP_E_BADINDEX, "Put allowed too small index in dimension %d\n", x); + hres = SafeArrayGetElement(sa, indices, &value); + ok(hres == DISP_E_BADINDEX, "Get allowed too small index in dimension %d\n", x); + + indices[x] = sab[x].lLbound + sab[x].cElements; + hres = SafeArrayPutElement(sa, indices, &value); + ok(hres == DISP_E_BADINDEX, "Put allowed too big index in dimension %d\n", x); + hres = SafeArrayGetElement(sa, indices, &value); + ok(hres == DISP_E_BADINDEX, "Get allowed too big index in dimension %d\n", x); + } + + indices[0] = sab[0].lLbound; + indices[1] = sab[1].lLbound; + indices[2] = sab[2].lLbound; + indices[3] = sab[3].lLbound; + + hres = SafeArrayPutElement(NULL, indices, &value); + ok(hres == E_INVALIDARG, "Put NULL array hres 0x%x\n", hres); + hres = SafeArrayGetElement(NULL, indices, &value); + ok(hres == E_INVALIDARG, "Get NULL array hres 0x%x\n", hres); + + hres = SafeArrayPutElement(sa, NULL, &value); + ok(hres == E_INVALIDARG, "Put NULL indices hres 0x%x\n", hres); + hres = SafeArrayGetElement(sa, NULL, &value); + ok(hres == E_INVALIDARG, "Get NULL indices hres 0x%x\n", hres); + + if (0) + { + /* This is retarded. Windows checks every case of invalid parameters + * except the following, which crashes. We ERR this in Wine. + */ + hres = SafeArrayPutElement(sa, indices, NULL); + ok(hres == E_INVALIDARG, "Put NULL value hres 0x%x\n", hres); + } + + hres = SafeArrayGetElement(sa, indices, NULL); + ok(hres == E_INVALIDARG, "Get NULL value hres 0x%x\n", hres); + + value = 0; + + /* Make sure we can read and get back the correct values in 4 dimensions, + * Each with a different size and lower bound. + */ + for (x = 0; x < sab[0].cElements; x++) + { + indices[0] = sab[0].lLbound + x; + for (y = 0; y < sab[1].cElements; y++) + { + indices[1] = sab[1].lLbound + y; + for (z = 0; z < sab[2].cElements; z++) + { + indices[2] = sab[2].lLbound + z; + for (a = 0; a < sab[3].cElements; a++) + { + indices[3] = sab[3].lLbound + a; + hres = SafeArrayPutElement(sa, indices, &value); + ok(hres == S_OK, "Failed to put element at (%d,%d,%d,%d) hres 0x%x\n", + x, y, z, a, hres); + value++; + } + } + } + } + + value = 0; + + for (x = 0; x < sab[0].cElements; x++) + { + indices[0] = sab[0].lLbound + x; + for (y = 0; y < sab[1].cElements; y++) + { + indices[1] = sab[1].lLbound + y; + for (z = 0; z < sab[2].cElements; z++) + { + indices[2] = sab[2].lLbound + z; + for (a = 0; a < sab[3].cElements; a++) + { + indices[3] = sab[3].lLbound + a; + gotvalue = value / 3; + hres = SafeArrayGetElement(sa, indices, &gotvalue); + ok(hres == S_OK, "Failed to get element at (%d,%d,%d,%d) hres 0x%x\n", + x, y, z, a, hres); + if (hres == S_OK) + ok(value == gotvalue, "Got value %d instead of %d at (%d,%d,%d,%d)\n", + gotvalue, value, x, y, z, a); + value++; + } + } + } + } + SafeArrayDestroy(sa); +} + +static void test_SafeArrayGetPutElement_BSTR(void) +{ + SAFEARRAYBOUND sab; + LONG indices[1]; + SAFEARRAY *sa; + HRESULT hres; + BSTR value = 0, gotvalue; + const OLECHAR szTest[5] = { 'T','e','s','t','\0' }; + + sab.lLbound = 1; + sab.cElements = 1; + + sa = SafeArrayCreate(VT_BSTR, 1, &sab); + ok(sa != NULL, "BSTR test couldn't create array\n"); + if (!sa) + return; + + ok(sa->cbElements == sizeof(BSTR), "BSTR size mismatch\n"); + if (sa->cbElements != sizeof(BSTR)) + return; + + indices[0] = sab.lLbound; + value = SysAllocString(szTest); + ok (value != NULL, "Expected non-NULL\n"); + hres = SafeArrayPutElement(sa, indices, value); + ok(hres == S_OK, "Failed to put bstr element hres 0x%x\n", hres); + gotvalue = NULL; + hres = SafeArrayGetElement(sa, indices, &gotvalue); + ok(hres == S_OK, "Failed to get bstr element at hres 0x%x\n", hres); + if (hres == S_OK) + ok(SysStringLen(value) == SysStringLen(gotvalue), "Got len %d instead of %d\n", SysStringLen(gotvalue), SysStringLen(value)); + SafeArrayDestroy(sa); +} + +static int tunk_xref = 0; +static HRESULT WINAPI tunk_QueryInterface(LPUNKNOWN punk,REFIID riid, LPVOID *x) { + return E_FAIL; +} +static ULONG WINAPI tunk_AddRef(LPUNKNOWN punk) { + return ++tunk_xref; +} + +static ULONG WINAPI tunk_Release(LPUNKNOWN punk) { + return --tunk_xref; +} + +static const IUnknownVtbl xtunk_vtbl = { + tunk_QueryInterface, + tunk_AddRef, + tunk_Release +}; + +static struct xtunk_iface { + const IUnknownVtbl *lpvtbl; +} xtunk_iface; + + +static void test_SafeArrayGetPutElement_IUnknown(void) +{ + SAFEARRAYBOUND sab; + LONG indices[1]; + SAFEARRAY *sa; + HRESULT hres; + LPUNKNOWN value = 0, gotvalue; + + sab.lLbound = 1; + sab.cElements = 1; + sa = SafeArrayCreate(VT_UNKNOWN, 1, &sab); + ok(sa != NULL, "UNKNOWN test couldn't create array\n"); + if (!sa) + return; + + ok(sa->cbElements == sizeof(LPUNKNOWN), "LPUNKNOWN size mismatch\n"); + if (sa->cbElements != sizeof(LPUNKNOWN)) + return; + + indices[0] = sab.lLbound; + xtunk_iface.lpvtbl = &xtunk_vtbl; + value = (LPUNKNOWN)&xtunk_iface; + tunk_xref = 1; + ok (value != NULL, "Expected non-NULL\n"); + hres = SafeArrayPutElement(sa, indices, value); + ok(hres == S_OK, "Failed to put bstr element hres 0x%x\n", hres); + ok(tunk_xref == 2,"Failed to increment refcount of iface.\n"); + gotvalue = NULL; + hres = SafeArrayGetElement(sa, indices, &gotvalue); + ok(tunk_xref == 3,"Failed to increment refcount of iface.\n"); + ok(hres == S_OK, "Failed to get bstr element at hres 0x%x\n", hres); + if (hres == S_OK) + ok(value == gotvalue, "Got %p instead of %p\n", gotvalue, value); + SafeArrayDestroy(sa); + ok(tunk_xref == 2,"Failed to decrement refcount of iface.\n"); +} + +static void test_SafeArrayRedim_IUnknown(void) +{ + SAFEARRAYBOUND sab; + LONG indices[1]; + SAFEARRAY *sa; + HRESULT hres; + LPUNKNOWN value; + + sab.lLbound = 1; + sab.cElements = 2; + sa = SafeArrayCreate(VT_UNKNOWN, 1, &sab); + ok(sa != NULL, "UNKNOWN test couldn't create array\n"); + if (!sa) + return; + + ok(sa->cbElements == sizeof(LPUNKNOWN), "LPUNKNOWN size mismatch\n"); + if (sa->cbElements != sizeof(LPUNKNOWN)) + return; + + indices[0] = 2; + xtunk_iface.lpvtbl = &xtunk_vtbl; + value = (LPUNKNOWN)&xtunk_iface; + tunk_xref = 1; + hres = SafeArrayPutElement(sa, indices, value); + ok(hres == S_OK, "Failed to put IUnknown element hres 0x%x\n", hres); + ok(tunk_xref == 2,"Failed to increment refcount of iface.\n"); + sab.cElements = 1; + hres = SafeArrayRedim(sa, &sab); + ok(hres == S_OK, "Failed to shrink array hres 0x%x\n", hres); + ok(tunk_xref == 1, "Failed to decrement refcount\n"); + SafeArrayDestroy(sa); +} + +static void test_SafeArrayGetPutElement_VARIANT(void) +{ + SAFEARRAYBOUND sab; + LONG indices[1]; + SAFEARRAY *sa; + HRESULT hres; + VARIANT value, gotvalue; + + sab.lLbound = 1; + sab.cElements = 1; + sa = SafeArrayCreate(VT_VARIANT, 1, &sab); + ok(sa != NULL, "VARIANT test couldn't create array\n"); + if (!sa) + return; + + ok(sa->cbElements == sizeof(VARIANT), "VARIANT size mismatch\n"); + if (sa->cbElements != sizeof(VARIANT)) + return; + + indices[0] = sab.lLbound; + V_VT(&value) = VT_I4; + V_I4(&value) = 0x42424242; + hres = SafeArrayPutElement(sa, indices, &value); + ok(hres == S_OK, "Failed to put Variant I4 element hres 0x%x\n", hres); + + V_VT(&gotvalue) = 0xdead; + hres = SafeArrayGetElement(sa, indices, &gotvalue); + ok(hres == S_OK, "Failed to get variant element at hres 0x%x\n", hres); + + V_VT(&gotvalue) = VT_EMPTY; + hres = SafeArrayGetElement(sa, indices, &gotvalue); + ok(hres == S_OK, "Failed to get variant element at hres 0x%x\n", hres); + if (hres == S_OK) { + ok(V_VT(&value) == V_VT(&gotvalue), "Got type 0x%x instead of 0x%x\n", V_VT(&value), V_VT(&gotvalue)); + if (V_VT(&value) == V_VT(&gotvalue)) + ok(V_I4(&value) == V_I4(&gotvalue), "Got %d instead of %d\n", V_I4(&value), V_VT(&gotvalue)); + } + SafeArrayDestroy(sa); +} + + +static void test_SafeArrayCopyData(void) +{ + SAFEARRAYBOUND sab[4]; + SAFEARRAY *sa; + SAFEARRAY *sacopy; + HRESULT hres; + int dimension,size=1; + + if (!pSafeArrayCopyData) + return; + + for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++) + { + sab[dimension].lLbound = dimension * 2 + 2; + sab[dimension].cElements = dimension * 3 + 1; + size *= sab[dimension].cElements; + } + + sa = SafeArrayCreate(VT_INT, NUM_DIMENSIONS, sab); + ok(sa != NULL, "Copy test couldn't create array\n"); + sacopy = SafeArrayCreate(VT_INT, NUM_DIMENSIONS, sab); + ok(sacopy != NULL, "Copy test couldn't create copy array\n"); + + if (!sa || !sacopy) + return; + + ok(sa->cbElements == sizeof(int), "int size mismatch\n"); + if (sa->cbElements != sizeof(int)) + return; + + /* Fill the source array with some data; it doesn't matter what */ + for (dimension = 0; dimension < size; dimension++) + { + int* data = (int*)sa->pvData; + data[dimension] = dimension; + } + + hres = pSafeArrayCopyData(sa, sacopy); + ok(hres == S_OK, "copy data failed hres 0x%x\n", hres); + if (hres == S_OK) + { + ok(!memcmp(sa->pvData, sacopy->pvData, size * sizeof(int)), "compared different\n"); + } + + /* Failure cases */ + hres = pSafeArrayCopyData(NULL, sacopy); + ok(hres == E_INVALIDARG, "Null copy source hres 0x%x\n", hres); + hres = pSafeArrayCopyData(sa, NULL); + ok(hres == E_INVALIDARG, "Null copy hres 0x%x\n", hres); + + sacopy->rgsabound[0].cElements += 1; + hres = pSafeArrayCopyData(sa, sacopy); + ok(hres == E_INVALIDARG, "Bigger copy first dimension hres 0x%x\n", hres); + + sacopy->rgsabound[0].cElements -= 2; + hres = pSafeArrayCopyData(sa, sacopy); + ok(hres == E_INVALIDARG, "Smaller copy first dimension hres 0x%x\n", hres); + sacopy->rgsabound[0].cElements += 1; + + sacopy->rgsabound[3].cElements += 1; + hres = pSafeArrayCopyData(sa, sacopy); + ok(hres == E_INVALIDARG, "Bigger copy last dimension hres 0x%x\n", hres); + + sacopy->rgsabound[3].cElements -= 2; + hres = pSafeArrayCopyData(sa, sacopy); + ok(hres == E_INVALIDARG, "Smaller copy last dimension hres 0x%x\n", hres); + sacopy->rgsabound[3].cElements += 1; + + SafeArrayDestroy(sacopy); + sacopy = NULL; + hres = pSafeArrayCopyData(sa, sacopy); + ok(hres == E_INVALIDARG, "->Null copy hres 0x%x\n", hres); + + hres = SafeArrayCopy(sa, &sacopy); + ok(hres == S_OK, "copy failed hres 0x%x\n", hres); + if (hres == S_OK) + { + ok(SafeArrayGetElemsize(sa) == SafeArrayGetElemsize(sacopy),"elemsize wrong\n"); + ok(SafeArrayGetDim(sa) == SafeArrayGetDim(sacopy),"dimensions wrong\n"); + ok(!memcmp(sa->pvData, sacopy->pvData, size * sizeof(int)), "compared different\n"); + } + + SafeArrayDestroy(sa); +} + +static void test_SafeArrayCreateEx(void) +{ + IRecordInfoImpl* iRec; + SAFEARRAYBOUND sab[4]; + SAFEARRAY *sa; + HRESULT hres; + int dimension; + + if (!pSafeArrayCreateEx) + return; + + for (dimension = 0; dimension < NUM_DIMENSIONS; dimension++) + { + sab[dimension].lLbound = 0; + sab[dimension].cElements = 8; + } + + /* Failure cases */ + sa = pSafeArrayCreateEx(VT_UI1, 1, NULL, NULL); + ok(sa == NULL, "CreateEx NULL bounds didn't fail\n"); + + /* test IID storage & defaulting */ + sa = pSafeArrayCreateEx(VT_DISPATCH, 1, sab, (PVOID)&IID_ITypeInfo); + ok(sa != NULL, "CreateEx (ITypeInfo) failed\n"); + + if (sa) + { + GUID guid; + if (pSafeArrayGetIID) + { + hres = pSafeArrayGetIID(sa, &guid); + ok(hres == S_OK, "CreateEx (ITypeInfo) no IID hres 0x%x\n", hres); + if (hres == S_OK) + { + ok(IsEqualGUID(&guid, &IID_ITypeInfo), "CreateEx (ITypeInfo) bad IID\n"); + } + } + if (pSafeArraySetIID) + { + hres = pSafeArraySetIID(sa, &IID_IUnknown); + ok(hres == S_OK, "Failed to set IID, hres = %8x\n", hres); + if (hres == S_OK && pSafeArrayGetIID) + { + hres = pSafeArrayGetIID(sa, &guid); + ok(hres == S_OK && IsEqualGUID(&guid, &IID_IUnknown), "Set bad IID\n"); + } + } + SafeArrayDestroy(sa); + } + + sa = pSafeArrayCreateEx(VT_DISPATCH, 1, sab, NULL); + ok(sa != NULL, "CreateEx (NULL) failed\n"); + + if (sa) + { + GUID guid; + if (pSafeArrayGetIID) + { + hres = pSafeArrayGetIID(sa, &guid); + ok(hres == S_OK, "CreateEx (NULL) no IID hres 0x%x\n", hres); + if (hres == S_OK) + { + ok(IsEqualGUID(&guid, &IID_IDispatch), "CreateEx (NULL) bad IID\n"); + } + } + SafeArrayDestroy(sa); + } + + sa = pSafeArrayCreateEx(VT_UNKNOWN, 1, sab, NULL); + ok(sa != NULL, "CreateEx (NULL-Unk) failed\n"); + + if (sa) + { + GUID guid; + if (pSafeArrayGetIID) + { + hres = pSafeArrayGetIID(sa, &guid); + ok(hres == S_OK, "CreateEx (NULL-Unk) no IID hres 0x%x\n", hres); + if (hres == S_OK) + { + ok(IsEqualGUID(&guid, &IID_IUnknown), "CreateEx (NULL-Unk) bad IID\n"); + } + } + SafeArrayDestroy(sa); + } + + /* VT_RECORD failure case */ + sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, NULL); + ok(sa == NULL, "CreateEx (NULL-Rec) succeded\n"); + + iRec = IRecordInfoImpl_Construct(); + + /* Win32 doesn't care if GetSize fails */ + fail_GetSize = TRUE; + sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, (LPVOID)iRec); + ok(sa != NULL, "CreateEx (Fail Size) failed\n"); + ok(iRec->ref == START_REF_COUNT + 1, "Wrong iRec refcount %d\n", iRec->ref); + ok(iRec->sizeCalled == 1, "GetSize called %d times\n", iRec->sizeCalled); + ok(iRec->clearCalled == 0, "Clear called %d times\n", iRec->clearCalled); + if (sa) + { + ok(sa->cbElements == RECORD_SIZE_FAIL, "Altered size to %d\n", sa->cbElements); + SafeArrayDestroy(sa); + ok(iRec->clearCalled == sab[0].cElements, "Destroy->Clear called %d times\n", iRec->clearCalled); + } + + /* Test VT_RECORD array */ + fail_GetSize = FALSE; + iRec->ref = START_REF_COUNT; + iRec->sizeCalled = 0; + iRec->clearCalled = 0; + sa = pSafeArrayCreateEx(VT_RECORD, 1, sab, (LPVOID)iRec); + ok(sa != NULL, "CreateEx (Rec) failed\n"); + ok(iRec->ref == START_REF_COUNT + 1, "Wrong iRec refcount %d\n", iRec->ref); + ok(iRec->sizeCalled == 1, "GetSize called %d times\n", iRec->sizeCalled); + ok(iRec->clearCalled == 0, "Clear called %d times\n", iRec->clearCalled); + if (sa && pSafeArrayGetRecordInfo) + { + IRecordInfo* saRec = NULL; + hres = pSafeArrayGetRecordInfo(sa, &saRec); + + ok(hres == S_OK,"GRI failed\n"); + ok(saRec == (IRecordInfo*)iRec,"Different saRec\n"); + ok(iRec->ref == START_REF_COUNT + 2, "Didn't AddRef %d\n", iRec->ref); + if (iRec->ref == START_REF_COUNT + 2) + IRecordInfo_Release(saRec); + + ok(sa->cbElements == RECORD_SIZE,"Elemsize is %d\n", sa->cbElements); + + SafeArrayDestroy(sa); + ok(iRec->sizeCalled == 1, "Destroy->GetSize called %d times\n", iRec->sizeCalled); + ok(iRec->clearCalled == sab[0].cElements, "Destroy->Clear called %d times\n", iRec->clearCalled); + ok(iRec->ref == START_REF_COUNT, "Wrong iRec refcount %d\n", iRec->ref); + } +} + +static void test_SafeArrayClear(void) +{ + SAFEARRAYBOUND sab; + SAFEARRAY *sa; + VARIANTARG v; + HRESULT hres; + + sab.lLbound = 0; + sab.cElements = 10; + sa = SafeArrayCreate(VT_UI1, 1, &sab); + ok(sa != NULL, "Create() failed.\n"); + if (!sa) + return; + + /* Test clearing non-NULL variants containing arrays */ + V_VT(&v) = VT_ARRAY|VT_UI1; + V_ARRAY(&v) = sa; + hres = VariantClear(&v); + ok(hres == S_OK && V_VT(&v) == VT_EMPTY, "VariantClear: hres 0x%x, Type %d\n", hres, V_VT(&v)); + ok(V_ARRAY(&v) == sa, "VariantClear: Overwrote value\n"); + + sa = SafeArrayCreate(VT_UI1, 1, &sab); + ok(sa != NULL, "Create() failed.\n"); + if (!sa) + return; + + V_VT(&v) = VT_SAFEARRAY; + V_ARRAY(&v) = sa; + hres = VariantClear(&v); + ok(hres == DISP_E_BADVARTYPE, "VariantClear: hres 0x%x\n", hres); + + V_VT(&v) = VT_SAFEARRAY|VT_BYREF; + V_ARRAYREF(&v) = &sa; + hres = VariantClear(&v); + ok(hres == DISP_E_BADVARTYPE, "VariantClear: hres 0x%x\n", hres); + + SafeArrayDestroy(sa); +} + +static void test_SafeArrayCopy(void) +{ + SAFEARRAYBOUND sab; + SAFEARRAY *sa, *sa2; + VARIANTARG vSrc, vDst; + HRESULT hres; + + sab.lLbound = 0; + sab.cElements = 10; + sa = SafeArrayCreate(VT_UI1, 1, &sab); + ok(sa != NULL, "Create() failed.\n"); + if (!sa) + return; + + /* Test copying non-NULL variants containing arrays */ + V_VT(&vSrc) = (VT_ARRAY|VT_BYREF|VT_UI1); + V_ARRAYREF(&vSrc) = &sa; + V_VT(&vDst) = VT_EMPTY; + + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == (VT_ARRAY|VT_BYREF|VT_UI1), + "VariantCopy: hres 0x%x, Type %d\n", hres, V_VT(&vDst)); + ok(V_ARRAYREF(&vDst) == &sa, "VariantClear: Performed deep copy\n"); + + V_VT(&vSrc) = (VT_ARRAY|VT_UI1); + V_ARRAY(&vSrc) = sa; + V_VT(&vDst) = VT_EMPTY; + + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == (VT_ARRAY|VT_UI1), + "VariantCopy: hres 0x%x, Type %d\n", hres, V_VT(&vDst)); + ok(V_ARRAY(&vDst) != sa, "VariantClear: Performed shallow copy\n"); + + SafeArrayDestroy(V_ARRAY(&vSrc)); + SafeArrayDestroy(V_ARRAY(&vDst)); + + hres = SafeArrayAllocDescriptor(1, &sa); + ok(hres == S_OK, "SafeArrayAllocDescriptor failed with error 0x%08x\n", hres); + + hres = SafeArrayCopy(sa, &sa2); + ok(hres == E_INVALIDARG, + "SafeArrayCopy with empty array should have failed with error E_INVALIDARG instead of 0x%08x\n", + hres); + sa->cbElements = 16; + hres = SafeArrayCopy(sa, &sa2); + ok(hres == S_OK, "SafeArrayCopy failed with error 0x%08x\n", hres); + + SafeArrayDestroy(sa); +} + +#define MKARRAY(low,num,typ) sab.lLbound = low; sab.cElements = num; \ + sa = SafeArrayCreate(typ, 1, &sab); ok(sa != NULL, "Create() failed.\n"); \ + if (!sa) return; \ + V_VT(&v) = VT_ARRAY|typ; V_ARRAY(&v) = sa; VariantInit(&v2) + +#define MKARRAYCONT(low,num,typ) sab.lLbound = low; sab.cElements = num; \ + sa = SafeArrayCreate(typ, 1, &sab); if (!sa) continue; \ + V_VT(&v) = VT_ARRAY|typ; V_ARRAY(&v) = sa; VariantInit(&v2) + +static void test_SafeArrayChangeTypeEx(void) +{ + static const char *szHello = "Hello World"; + SAFEARRAYBOUND sab; + SAFEARRAY *sa; + VARIANTARG v,v2; + VARTYPE vt; + HRESULT hres; + + /* VT_ARRAY|VT_UI1 -> VT_BSTR */ + MKARRAY(0,strlen(szHello)+1,VT_UI1); + memcpy(sa->pvData, szHello, strlen(szHello)+1); + + hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_BSTR); + ok(hres == S_OK, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR failed with %x\n", hres); + if (hres == S_OK) + { + ok(V_VT(&v2) == VT_BSTR, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR did not return VT_BSTR, but %d.\n",V_VT(&v2)); + ok(strcmp((char*)V_BSTR(&v2),szHello) == 0,"Expected string '%s', got '%s'\n", szHello, + (char*)V_BSTR(&v2)); + VariantClear(&v2); + } + + /* VT_VECTOR|VT_UI1 -> VT_BSTR */ + SafeArrayDestroy(sa); + if (pSafeArrayCreateVector) + { + sa = pSafeArrayCreateVector(VT_UI1, 0, strlen(szHello)+1); + ok(sa != NULL, "CreateVector() failed.\n"); + if (!sa) + return; + + memcpy(sa->pvData, szHello, strlen(szHello)+1); + V_VT(&v) = VT_VECTOR|VT_UI1; + V_ARRAY(&v) = sa; + VariantInit(&v2); + + hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_BSTR); + ok(hres == DISP_E_BADVARTYPE, "CTE VT_VECTOR|VT_UI1 returned %x\n", hres); + + /* (vector)VT_ARRAY|VT_UI1 -> VT_BSTR (In place) */ + V_VT(&v) = VT_ARRAY|VT_UI1; + hres = VariantChangeTypeEx(&v, &v, 0, 0, VT_BSTR); + ok(hres == S_OK, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR failed with %x\n", hres); + if (hres == S_OK) + { + ok(V_VT(&v) == VT_BSTR, "CTE VT_ARRAY|VT_UI1 -> VT_BSTR did not return VT_BSTR, but %d.\n",V_VT(&v)); + ok(strcmp((char*)V_BSTR(&v),szHello) == 0,"Expected string '%s', got '%s'\n", szHello, + (char*)V_BSTR(&v)); + VariantClear(&v); + } + } + + /* To/from BSTR only works with arrays of VT_UI1 */ + for (vt = 0; vt <= VT_CLSID; vt++) + { + if (vt == VT_UI1) + continue; + + MKARRAYCONT(0,1,vt); + hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_BSTR); + ok(hres != S_OK, "CTE VT_ARRAY|VT %d->BSTR succeeded\n", vt); + VariantClear(&v2); + } + + /* Can't change an array of one type into array of another type , even + * if the other type is the same size + */ + if (pSafeArrayCreateVector) + { + sa = pSafeArrayCreateVector(VT_UI1, 0, 1); + ok(sa != NULL, "CreateVector() failed.\n"); + if (!sa) + return; + + V_VT(&v) = VT_ARRAY|VT_UI1; + V_ARRAY(&v) = sa; + hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_ARRAY|VT_I1); + ok(hres == DISP_E_TYPEMISMATCH, "CTE VT_ARRAY|VT_UI1->VT_ARRAY|VT_I1 returned %x\n", hres); + + /* But can change to the same array type */ + SafeArrayDestroy(sa); + sa = pSafeArrayCreateVector(VT_UI1, 0, 1); + ok(sa != NULL, "CreateVector() failed.\n"); + if (!sa) + return; + V_VT(&v) = VT_ARRAY|VT_UI1; + V_ARRAY(&v) = sa; + hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_ARRAY|VT_UI1); + ok(hres == S_OK, "CTE VT_ARRAY|VT_UI1->VT_ARRAY|VT_UI1 returned %x\n", hres); + } + + /* NULL/EMPTY */ + MKARRAY(0,1,VT_UI1); + hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_NULL); + ok(hres == DISP_E_TYPEMISMATCH, "CTE VT_ARRAY|VT_UI1 returned %x\n", hres); + MKARRAY(0,1,VT_UI1); + hres = VariantChangeTypeEx(&v2, &v, 0, 0, VT_EMPTY); + ok(hres == DISP_E_TYPEMISMATCH, "CTE VT_ARRAY|VT_UI1 returned %x\n", hres); + +} + +static void test_SafeArrayDestroyData (void) +{ + SAFEARRAYBOUND sab; + SAFEARRAY *sa; + HRESULT hres; + int value = 0xdeadbeef; + LONG index[1]; + void HUGEP *temp_pvData; + + sab.lLbound = 0; + sab.cElements = 10; + sa = SafeArrayCreate(VT_INT, 1, &sab); + ok(sa != NULL, "Create() failed.\n"); + if (!sa) + return; + index[0] = 1; + SafeArrayPutElement (sa, index, &value); + +/* SafeArrayDestroyData shouldn't free pvData if FADF_STATIC is set. */ + sa->fFeatures |= FADF_STATIC; + temp_pvData = sa->pvData; + hres = SafeArrayDestroyData(sa); + ok(hres == S_OK, "SADData FADF_STATIC failed, error code %x.\n",hres); + ok(sa->pvData == temp_pvData, "SADData FADF_STATIC: pvData=%p, expected %p (fFeatures = %d).\n", + sa->pvData, temp_pvData, sa->fFeatures); + SafeArrayGetElement (sa, index, &value); + ok(value == 0, "Data not cleared after SADData\n"); + +/* Clear FADF_STATIC, now really destroy the data. */ + sa->fFeatures ^= FADF_STATIC; + hres = SafeArrayDestroyData(sa); + ok(hres == S_OK, "SADData !FADF_STATIC failed, error code %x.\n",hres); + ok(sa->pvData == NULL, "SADData !FADF_STATIC: pvData=%p, expected NULL.\n", sa->pvData); + + hres = SafeArrayDestroy(sa); + ok(hres == S_OK, "SAD failed, error code %x.\n", hres); +} + +START_TEST(safearray) +{ + hOleaut32 = GetModuleHandleA("oleaut32.dll"); + + GETPTR(SafeArrayAllocDescriptorEx); + GETPTR(SafeArrayCopyData); + GETPTR(SafeArrayGetIID); + GETPTR(SafeArraySetIID); + GETPTR(SafeArrayGetVartype); + GETPTR(SafeArrayCreateEx); + GETPTR(SafeArrayCreateVector); + + check_for_VT_INT_PTR(); + test_safearray(); + test_SafeArrayAllocDestroyDescriptor(); + test_SafeArrayCreateLockDestroy(); + test_VectorCreateLockDestroy(); + test_LockUnlock(); + test_SafeArrayChangeTypeEx(); + test_SafeArrayCopy(); + test_SafeArrayClear(); + test_SafeArrayCreateEx(); + test_SafeArrayCopyData(); + test_SafeArrayDestroyData(); + test_SafeArrayGetPutElement(); + test_SafeArrayGetPutElement_BSTR(); + test_SafeArrayGetPutElement_IUnknown(); + test_SafeArrayRedim_IUnknown(); + test_SafeArrayGetPutElement_VARIANT(); +} diff --git a/rostests/winetests/oleaut32/test_tlb.idl b/rostests/winetests/oleaut32/test_tlb.idl new file mode 100644 index 00000000000..91b6a7680af --- /dev/null +++ b/rostests/winetests/oleaut32/test_tlb.idl @@ -0,0 +1,39 @@ +/* + * ITypeLib test IDL - we dump it and compare results in typelib.c + * + * Copyright 2007 Google (Mikolaj Zalewski) + * + * 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 + */ + +import "oaidl.idl"; /* needed by widl */ + +[uuid(8b05fe77-4a6c-4133-b9cd-8f81747af784)] +library Test +{ + importlib("stdole2.tlb"); + + [dual,uuid(b14b6bb5-904e-4ff9-b247-bd361f7aaedd)] + interface IDualIface : IDispatch + { + HRESULT test(); + } + + [uuid(ec5dfcd6-eeb0-4cd6-b51e-8030e1dac009)] + interface ISimpleIface : IDispatch + { + HRESULT test(); + } +} diff --git a/rostests/winetests/oleaut32/testlist.c b/rostests/winetests/oleaut32/testlist.c new file mode 100644 index 00000000000..324c7b74570 --- /dev/null +++ b/rostests/winetests/oleaut32/testlist.c @@ -0,0 +1,31 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_olefont(void); +extern void func_olepicture(void); +extern void func_safearray(void); +extern void func_tmarshal(void); +extern void func_typelib(void); +extern void func_usrmarshal(void); +extern void func_varformat(void); +extern void func_vartest(void); +extern void func_vartype(void); + +const struct test winetest_testlist[] = +{ + { "olefont", func_olefont }, + { "olepicture", func_olepicture }, + { "safearray", func_safearray }, + { "tmarshal", func_tmarshal }, + { "typelib", func_typelib }, + { "usrmarshal", func_usrmarshal }, + { "varformat", func_varformat }, + { "vartest", func_vartest }, + { "vartype", func_vartype }, + { 0, 0 } +}; diff --git a/rostests/winetests/oleaut32/tmarshal.c b/rostests/winetests/oleaut32/tmarshal.c new file mode 100644 index 00000000000..c96fb33d618 --- /dev/null +++ b/rostests/winetests/oleaut32/tmarshal.c @@ -0,0 +1,1178 @@ +/* + * Copyright (C) 2005-2006 Robert Shearman 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 +#include +#include + +#include + +#include "tmarshal.h" +#include "tmarshal_dispids.h" + +#define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08lx\n", (unsigned long int)hr) + +/* Debugging functions from wine/libs/wine/debug.c */ + +/* allocate some tmp string space */ +/* FIXME: this is not 100% thread-safe */ +static char *get_tmp_space( int size ) +{ + static char *list[32]; + static long pos; + char *ret; + int idx; + + idx = ++pos % (sizeof(list)/sizeof(list[0])); + if ((ret = realloc( list[idx], size ))) list[idx] = ret; + return ret; +} + +/* default implementation of wine_dbgstr_wn */ +static const char *default_dbgstr_wn( const WCHAR *str, int n ) +{ + char *dst, *res; + + if (!HIWORD(str)) + { + if (!str) return "(null)"; + res = get_tmp_space( 6 ); + sprintf( res, "#%04x", LOWORD(str) ); + return res; + } + if (n == -1) n = lstrlenW(str); + if (n < 0) n = 0; + else if (n > 200) n = 200; + dst = res = get_tmp_space( n * 5 + 7 ); + *dst++ = 'L'; + *dst++ = '"'; + while (n-- > 0) + { + WCHAR c = *str++; + switch (c) + { + case '\n': *dst++ = '\\'; *dst++ = 'n'; break; + case '\r': *dst++ = '\\'; *dst++ = 'r'; break; + case '\t': *dst++ = '\\'; *dst++ = 't'; break; + case '"': *dst++ = '\\'; *dst++ = '"'; break; + case '\\': *dst++ = '\\'; *dst++ = '\\'; break; + default: + if (c >= ' ' && c <= 126) + *dst++ = (char)c; + else + { + *dst++ = '\\'; + sprintf(dst,"%04x",c); + dst+=4; + } + } + } + *dst++ = '"'; + if (*str) + { + *dst++ = '.'; + *dst++ = '.'; + *dst++ = '.'; + } + *dst = 0; + return res; +} + +const char *wine_dbgstr_wn( const WCHAR *s, int n ) +{ + return default_dbgstr_wn(s, n); +} + +const char *wine_dbgstr_w( const WCHAR *s ) +{ + return default_dbgstr_wn( s, -1 ); +} + + +#define RELEASEMARSHALDATA WM_USER + +struct host_object_data +{ + IStream *stream; + IID iid; + IUnknown *object; + MSHLFLAGS marshal_flags; + HANDLE marshal_event; + IMessageFilter *filter; +}; + +static DWORD CALLBACK host_object_proc(LPVOID p) +{ + struct host_object_data *data = (struct host_object_data *)p; + HRESULT hr; + MSG msg; + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + if (data->filter) + { + IMessageFilter * prev_filter = NULL; + hr = CoRegisterMessageFilter(data->filter, &prev_filter); + if (prev_filter) IMessageFilter_Release(prev_filter); + ok_ole_success(hr, CoRegisterMessageFilter); + } + + hr = CoMarshalInterface(data->stream, &data->iid, data->object, MSHCTX_INPROC, NULL, data->marshal_flags); + ok_ole_success(hr, CoMarshalInterface); + + /* force the message queue to be created before signaling parent thread */ + PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + SetEvent(data->marshal_event); + + while (GetMessage(&msg, NULL, 0, 0)) + { + if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA) + { + trace("releasing marshal data\n"); + CoReleaseMarshalData(data->stream); + SetEvent((HANDLE)msg.lParam); + } + else + DispatchMessage(&msg); + } + + HeapFree(GetProcessHeap(), 0, data); + + CoUninitialize(); + + return hr; +} + +static DWORD start_host_object2(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, IMessageFilter *filter, HANDLE *thread) +{ + DWORD tid = 0; + HANDLE marshal_event = CreateEvent(NULL, FALSE, FALSE, NULL); + struct host_object_data *data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data)); + + data->stream = stream; + data->iid = *riid; + data->object = object; + data->marshal_flags = marshal_flags; + data->marshal_event = marshal_event; + data->filter = filter; + + *thread = CreateThread(NULL, 0, host_object_proc, data, 0, &tid); + + /* wait for marshaling to complete before returning */ + WaitForSingleObject(marshal_event, INFINITE); + CloseHandle(marshal_event); + + return tid; +} + +static DWORD start_host_object(IStream *stream, REFIID riid, IUnknown *object, MSHLFLAGS marshal_flags, HANDLE *thread) +{ + return start_host_object2(stream, riid, object, marshal_flags, NULL, thread); +} + +#if 0 /* not used */ +/* asks thread to release the marshal data because it has to be done by the + * same thread that marshaled the interface in the first place. */ +static void release_host_object(DWORD tid) +{ + HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL); + PostThreadMessage(tid, RELEASEMARSHALDATA, 0, (LPARAM)event); + WaitForSingleObject(event, INFINITE); + CloseHandle(event); +} +#endif + +static void end_host_object(DWORD tid, HANDLE thread) +{ + BOOL ret = PostThreadMessage(tid, WM_QUIT, 0, 0); + ok(ret, "PostThreadMessage failed with error %d\n", GetLastError()); + /* be careful of races - don't return until hosting thread has terminated */ + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); +} + +typedef struct Widget +{ + const IWidgetVtbl *lpVtbl; + LONG refs; + IUnknown *pDispatchUnknown; +} Widget; + +HRESULT WINAPI Widget_QueryInterface( + IWidget *iface, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) +{ + if (IsEqualIID(riid, &IID_IWidget) || IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDispatch)) + { + IWidget_AddRef(iface); + *ppvObject = iface; + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +ULONG WINAPI Widget_AddRef( + IWidget *iface) +{ + Widget *This = (Widget *)iface; + + return InterlockedIncrement(&This->refs); +} + +ULONG WINAPI Widget_Release( + IWidget *iface) +{ + Widget *This = (Widget *)iface; + ULONG refs = InterlockedDecrement(&This->refs); + if (!refs) + { + IUnknown_Release(This->pDispatchUnknown); + memset(This, 0xcc, sizeof(*This)); + HeapFree(GetProcessHeap(), 0, This); + trace("Widget destroyed!\n"); + } + + return refs; +} + +HRESULT WINAPI Widget_GetTypeInfoCount( + IWidget *iface, + /* [out] */ UINT __RPC_FAR *pctinfo) +{ + Widget *This = (Widget *)iface; + IDispatch *pDispatch; + HRESULT hr = IUnknown_QueryInterface(This->pDispatchUnknown, &IID_IDispatch, (void **)&pDispatch); + if (SUCCEEDED(hr)) + { + hr = IDispatch_GetTypeInfoCount(pDispatch, pctinfo); + IDispatch_Release(pDispatch); + } + return hr; +} + +HRESULT WINAPI Widget_GetTypeInfo( + IWidget __RPC_FAR * iface, + /* [in] */ UINT iTInfo, + /* [in] */ LCID lcid, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo) +{ + Widget *This = (Widget *)iface; + IDispatch *pDispatch; + HRESULT hr = IUnknown_QueryInterface(This->pDispatchUnknown, &IID_IDispatch, (void **)&pDispatch); + if (SUCCEEDED(hr)) + { + hr = IDispatch_GetTypeInfo(pDispatch, iTInfo, lcid, ppTInfo); + IDispatch_Release(pDispatch); + } + return hr; +} + +HRESULT WINAPI Widget_GetIDsOfNames( + IWidget __RPC_FAR * iface, + /* [in] */ REFIID riid, + /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, + /* [in] */ UINT cNames, + /* [in] */ LCID lcid, + /* [size_is][out] */ DISPID __RPC_FAR *rgDispId) +{ + Widget *This = (Widget *)iface; + IDispatch *pDispatch; + HRESULT hr = IUnknown_QueryInterface(This->pDispatchUnknown, &IID_IDispatch, (void **)&pDispatch); + if (SUCCEEDED(hr)) + { + hr = IDispatch_GetIDsOfNames(pDispatch, riid, rgszNames, cNames, lcid, rgDispId); + IDispatch_Release(pDispatch); + } + return hr; +} + +HRESULT WINAPI Widget_Invoke( + IWidget __RPC_FAR * iface, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr) +{ + Widget *This = (Widget *)iface; + IDispatch *pDispatch; + HRESULT hr = IUnknown_QueryInterface(This->pDispatchUnknown, &IID_IDispatch, (void **)&pDispatch); + if (SUCCEEDED(hr)) + { + hr = IDispatch_Invoke(pDispatch, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + IDispatch_Release(pDispatch); + } + return hr; +} + +HRESULT WINAPI Widget_put_Name( + IWidget __RPC_FAR * iface, + /* [in] */ BSTR name) +{ + trace("put_Name(%s)\n", wine_dbgstr_w(name)); + return S_OK; +} + +HRESULT WINAPI Widget_get_Name( + IWidget __RPC_FAR * iface, + /* [out] */ BSTR __RPC_FAR *name) +{ + static const WCHAR szCat[] = { 'C','a','t',0 }; + trace("get_Name()\n"); + *name = SysAllocString(szCat); + return S_OK; +} + +HRESULT WINAPI Widget_DoSomething( + IWidget __RPC_FAR * iface, + /* [in] */ double number, + /* [out] */ BSTR *str1, + /* [defaultvalue][in] */ BSTR str2, + /* [optional][in] */ VARIANT __RPC_FAR *opt) +{ + static const WCHAR szString[] = { 'S','t','r','i','n','g',0 }; + trace("DoSomething()\n"); + + ok(number == 3.141, "number(%f) != 3.141\n", number); + ok(*str2 == '\0', "str2(%s) != \"\"\n", wine_dbgstr_w(str2)); + ok(V_VT(opt) == VT_ERROR, "V_VT(opt) should be VT_ERROR instead of 0x%x\n", V_VT(opt)); + ok(V_ERROR(opt) == DISP_E_PARAMNOTFOUND, "V_ERROR(opt) should be DISP_E_PARAMNOTFOUND instead of 0x%08x\n", V_ERROR(opt)); + *str1 = SysAllocString(szString); + + return S_FALSE; +} + +HRESULT WINAPI Widget_get_State( + IWidget __RPC_FAR * iface, + /* [retval][out] */ STATE __RPC_FAR *state) +{ + trace("get_State() = STATE_WIDGETIFIED\n"); + *state = STATE_WIDGETIFIED; + return S_OK; +} + +HRESULT WINAPI Widget_put_State( + IWidget __RPC_FAR * iface, + /* [in] */ STATE state) +{ + trace("put_State(%d)\n", state); + return S_OK; +} + +HRESULT WINAPI Widget_Map( + IWidget * iface, + BSTR bstrId, + BSTR *sValue) +{ + trace("Map(%s, %p)\n", wine_dbgstr_w(bstrId), sValue); + *sValue = SysAllocString(bstrId); + return S_OK; +} + +HRESULT WINAPI Widget_SetOleColor( + IWidget * iface, + OLE_COLOR val) +{ + trace("SetOleColor(0x%x)\n", val); + return S_OK; +} + +HRESULT WINAPI Widget_GetOleColor( + IWidget * iface, + OLE_COLOR *pVal) +{ + trace("GetOleColor() = 0x8000000f\n"); + *pVal = 0x8000000f; + return S_FALSE; +} + +HRESULT WINAPI Widget_Clone( + IWidget *iface, + IWidget **ppVal) +{ + trace("Clone()\n"); + return Widget_QueryInterface(iface, &IID_IWidget, (void **)ppVal); +} + +HRESULT WINAPI Widget_CloneDispatch( + IWidget *iface, + IDispatch **ppVal) +{ + trace("CloneDispatch()\n"); + return Widget_QueryInterface(iface, &IID_IWidget, (void **)ppVal); +} + +HRESULT WINAPI Widget_CloneCoclass( + IWidget *iface, + ApplicationObject2 **ppVal) +{ + trace("CloneCoclass()\n"); + return S_OK; +} + +HRESULT WINAPI Widget_Value( + IWidget __RPC_FAR * iface, + VARIANT *value, + VARIANT *retval) +{ + trace("Value(%p, %p)\n", value, retval); + ok(V_VT(value) == VT_I2, "V_VT(value) was %d instead of VT_I2\n", V_VT(value)); + ok(V_I2(value) == 1, "V_I2(value) was %d instead of 1\n", V_I2(value)); + V_VT(retval) = VT_I2; + V_I2(retval) = 1234; + return S_OK; +} + +HRESULT WINAPI Widget_Array( + IWidget * iface, + SAFEARRAY * values) +{ + trace("Array(%p)\n", values); + return S_OK; +} + +HRESULT WINAPI Widget_VariantArrayPtr( + IWidget * iface, + SAFEARRAY ** values) +{ + trace("VariantArrayPtr(%p)\n", values); + return S_OK; +} + +void WINAPI Widget_Variant( + IWidget __RPC_FAR * iface, + VARIANT var) +{ + trace("Variant()\n"); + ok(V_VT(&var) == VT_CY, "V_VT(&var) was %d\n", V_VT(&var)); + ok(S(V_CY(&var)).Hi == 0xdababe, "V_CY(&var).Hi was 0x%x\n", S(V_CY(&var)).Hi); + ok(S(V_CY(&var)).Lo == 0xdeadbeef, "V_CY(&var).Lo was 0x%x\n", S(V_CY(&var)).Lo); +} + +void WINAPI Widget_VarArg( + IWidget * iface, + int numexpect, + SAFEARRAY * values) +{ + LONG lbound, ubound, i; + VARIANT * data; + HRESULT hr; + + trace("VarArg(%p)\n", values); + + hr = SafeArrayGetLBound(values, 1, &lbound); + ok(hr == S_OK, "SafeArrayGetLBound failed with %x\n", hr); + ok(lbound == 0, "SafeArrayGetLBound returned %d\n", lbound); + + hr = SafeArrayGetUBound(values, 1, &ubound); + ok(hr == S_OK, "SafeArrayGetUBound failed with %x\n", hr); + ok(ubound == numexpect-1, "SafeArrayGetUBound returned %d, but expected %d\n", ubound, numexpect-1); + + hr = SafeArrayAccessData(values, (LPVOID)&data); + ok(hr == S_OK, "SafeArrayAccessData failed with %x\n", hr); + + for (i=0; i<=ubound-lbound; i++) + { + ok(V_VT(&data[i]) == VT_I4, "V_VT(&data[%d]) was %d\n", i, V_VT(&data[i])); + ok(V_I4(&data[i]) == i, "V_I4(&data[%d]) was %d\n", i, V_I4(&data[i])); + } + + hr = SafeArrayUnaccessData(values); + ok(hr == S_OK, "SafeArrayUnaccessData failed with %x\n", hr); +} + +HRESULT WINAPI Widget_Error( + IWidget __RPC_FAR * iface) +{ + trace("Error()\n"); + return E_NOTIMPL; +} + +HRESULT WINAPI Widget_CloneInterface( + IWidget __RPC_FAR * iface, + ISomethingFromDispatch **ppVal) +{ + trace("CloneInterface()\n"); + *ppVal = 0; + return S_OK; +} + +static const struct IWidgetVtbl Widget_VTable = +{ + Widget_QueryInterface, + Widget_AddRef, + Widget_Release, + Widget_GetTypeInfoCount, + Widget_GetTypeInfo, + Widget_GetIDsOfNames, + Widget_Invoke, + Widget_put_Name, + Widget_get_Name, + Widget_DoSomething, + Widget_get_State, + Widget_put_State, + Widget_Map, + Widget_SetOleColor, + Widget_GetOleColor, + Widget_Clone, + Widget_CloneDispatch, + Widget_CloneCoclass, + Widget_Value, + Widget_Array, + Widget_VariantArrayPtr, + Widget_Variant, + Widget_VarArg, + Widget_Error, + Widget_CloneInterface +}; + + +typedef struct KindaEnum +{ + const IKindaEnumWidgetVtbl *lpVtbl; + LONG refs; +} KindaEnum; + +static HRESULT register_current_module_typelib(void) +{ + WCHAR path[MAX_PATH]; + HRESULT hr; + ITypeLib *typelib; + + GetModuleFileNameW(NULL, path, MAX_PATH); + + hr = LoadTypeLib(path, &typelib); + if (SUCCEEDED(hr)) + { + hr = RegisterTypeLib(typelib, path, NULL); + ITypeLib_Release(typelib); + } + return hr; +} + +static IWidget *Widget_Create(void) +{ + Widget *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + HRESULT hr; + ITypeLib *pTypeLib; + + This->lpVtbl = &Widget_VTable; + This->refs = 1; + + hr = LoadRegTypeLib(&LIBID_TestTypelib, 1, 0, LOCALE_NEUTRAL, &pTypeLib); + ok_ole_success(hr, LoadRegTypeLib); + if (SUCCEEDED(hr)) + { + ITypeInfo *pTypeInfo; + hr = ITypeLib_GetTypeInfoOfGuid(pTypeLib, &IID_IWidget, &pTypeInfo); + ok_ole_success(hr, ITypeLib_GetTypeInfoOfGuid); + if (SUCCEEDED(hr)) + { + This->pDispatchUnknown = NULL; + hr = CreateStdDispatch((IUnknown *)&This->lpVtbl, This, pTypeInfo, &This->pDispatchUnknown); + ok_ole_success(hr, CreateStdDispatch); + ITypeInfo_Release(pTypeInfo); + } + } + + if (SUCCEEDED(hr)) + return (IWidget *)&This->lpVtbl; + else + { + HeapFree(GetProcessHeap(), 0, This); + return NULL; + } +} + +HRESULT WINAPI KindaEnum_QueryInterface( + IKindaEnumWidget *iface, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) +{ + if (IsEqualIID(riid, &IID_IKindaEnumWidget) || IsEqualIID(riid, &IID_IUnknown)) + { + IKindaEnumWidget_AddRef(iface); + *ppvObject = iface; + return S_OK; + } + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +ULONG WINAPI KindaEnum_AddRef( + IKindaEnumWidget *iface) +{ + KindaEnum *This = (KindaEnum *)iface; + + return InterlockedIncrement(&This->refs); +} + +ULONG WINAPI KindaEnum_Release( + IKindaEnumWidget *iface) +{ + KindaEnum *This = (KindaEnum *)iface; + ULONG refs = InterlockedDecrement(&This->refs); + if (!refs) + { + memset(This, 0xcc, sizeof(*This)); + HeapFree(GetProcessHeap(), 0, This); + trace("KindaEnumWidget destroyed!\n"); + } + + return refs; +} + +HRESULT WINAPI KindaEnum_Next( + IKindaEnumWidget *iface, + /* [out] */ IWidget __RPC_FAR *__RPC_FAR *widget) +{ + *widget = Widget_Create(); + if (*widget) + return S_OK; + else + return E_OUTOFMEMORY; +} + +HRESULT WINAPI KindaEnum_Count( + IKindaEnumWidget *iface, + /* [out] */ unsigned long __RPC_FAR *count) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI KindaEnum_Reset( + IKindaEnumWidget *iface) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI KindaEnum_Clone( + IKindaEnumWidget *iface, + /* [out] */ IKindaEnumWidget __RPC_FAR *__RPC_FAR *ppenum) +{ + return E_NOTIMPL; +} + +static const IKindaEnumWidgetVtbl KindaEnumWidget_VTable = +{ + KindaEnum_QueryInterface, + KindaEnum_AddRef, + KindaEnum_Release, + KindaEnum_Next, + KindaEnum_Count, + KindaEnum_Reset, + KindaEnum_Clone +}; + +static IKindaEnumWidget *KindaEnumWidget_Create(void) +{ + KindaEnum *This; + + This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) return NULL; + This->lpVtbl = &KindaEnumWidget_VTable; + This->refs = 1; + return (IKindaEnumWidget *)This; +} + +static HRESULT WINAPI NonOleAutomation_QueryInterface(INonOleAutomation *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_INonOleAutomation)) + { + *(INonOleAutomation **)ppv = iface; + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI NonOleAutomation_AddRef(INonOleAutomation *iface) +{ + return 2; +} + +static ULONG WINAPI NonOleAutomation_Release(INonOleAutomation *iface) +{ + return 1; +} + +static BSTR WINAPI NonOleAutomation_BstrRet(INonOleAutomation *iface) +{ + static const WCHAR wszTestString[] = {'T','h','i','s',' ','i','s',' ','a',' ','t','e','s','t',' ','s','t','r','i','n','g',0}; + return SysAllocString(wszTestString); +} + +static INonOleAutomationVtbl NonOleAutomation_VTable = +{ + NonOleAutomation_QueryInterface, + NonOleAutomation_AddRef, + NonOleAutomation_Release, + NonOleAutomation_BstrRet, +}; + +static INonOleAutomation NonOleAutomation = { &NonOleAutomation_VTable }; + +static ITypeInfo *NonOleAutomation_GetTypeInfo(void) +{ + ITypeLib *pTypeLib; + HRESULT hr = LoadRegTypeLib(&LIBID_TestTypelib, 1, 0, LOCALE_NEUTRAL, &pTypeLib); + ok_ole_success(hr, LoadRegTypeLib); + if (SUCCEEDED(hr)) + { + ITypeInfo *pTypeInfo; + hr = ITypeLib_GetTypeInfoOfGuid(pTypeLib, &IID_INonOleAutomation, &pTypeInfo); + ok_ole_success(hr, ITypeLib_GetTypeInfoOfGuid); + return pTypeInfo; + } + return NULL; +} + +static void test_typelibmarshal(void) +{ + static const WCHAR szCat[] = { 'C','a','t',0 }; + static const WCHAR szTestTest[] = { 'T','e','s','t','T','e','s','t',0 }; + static WCHAR szSuperman[] = { 'S','u','p','e','r','m','a','n',0 }; + HRESULT hr; + IKindaEnumWidget *pKEW = KindaEnumWidget_Create(); + IWidget *pWidget; + IStream *pStream; + IDispatch *pDispatch; + static const LARGE_INTEGER ullZero; + EXCEPINFO excepinfo; + VARIANT varresult; + DISPID dispidNamed = DISPID_PROPERTYPUT; + DISPPARAMS dispparams; + VARIANTARG vararg[4]; + STATE the_state; + HANDLE thread; + DWORD tid; + BSTR bstr; + ITypeInfo *pTypeInfo; + + ok(pKEW != NULL, "Widget creation failed\n"); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream); + ok_ole_success(hr, CreateStreamOnHGlobal); + tid = start_host_object(pStream, &IID_IKindaEnumWidget, (IUnknown *)pKEW, MSHLFLAGS_NORMAL, &thread); + + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); + hr = CoUnmarshalInterface(pStream, &IID_IKindaEnumWidget, (void **)&pKEW); + ok_ole_success(hr, CoUnmarshalInterface); + IStream_Release(pStream); + + hr = IKindaEnumWidget_Next(pKEW, &pWidget); + ok_ole_success(hr, IKindaEnumWidget_Next); + + IKindaEnumWidget_Release(pKEW); + + hr = IWidget_QueryInterface(pWidget, &IID_IDispatch, (void **)&pDispatch); + + /* call put_Name */ + VariantInit(&vararg[0]); + dispparams.cNamedArgs = 1; + dispparams.rgdispidNamedArgs = &dispidNamed; + dispparams.cArgs = 1; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_NAME, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + ok(excepinfo.wCode == 0x0 && excepinfo.scode == S_OK, + "EXCEPINFO differs from expected: wCode = 0x%x, scode = 0x%08x\n", + excepinfo.wCode, excepinfo.scode); + VariantClear(&varresult); + + /* call put_Name (direct) */ + bstr = SysAllocString(szSuperman); + hr = IWidget_put_Name(pWidget, bstr); + ok_ole_success(hr, IWidget_put_Name); + SysFreeString(bstr); + + /* call get_Name */ + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 0; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_NAME, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + ok(excepinfo.wCode == 0x0 && excepinfo.scode == S_OK, + "EXCEPINFO differs from expected: wCode = 0x%x, scode = 0x%08x\n", + excepinfo.wCode, excepinfo.scode); + trace("Name = %s\n", wine_dbgstr_w(V_BSTR(&varresult))); + VariantClear(&varresult); + + /* call get_Name (direct) */ + bstr = NULL; + hr = IWidget_get_Name(pWidget, &bstr); + ok_ole_success(hr, IWidget_get_Name); + ok(!lstrcmpW(bstr, szCat), "IWidget_get_Name should have returned string \"Cat\" instead of %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + + /* call DoSomething */ + VariantInit(&vararg[0]); + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_R8; + V_R8(&vararg[1]) = 3.141; + dispparams.cNamedArgs = 0; + dispparams.cArgs = 2; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_DOSOMETHING, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + ok(V_VT(&varresult) == VT_EMPTY, "varresult should be VT_EMPTY\n"); + VariantClear(&varresult); + + /* call get_State */ + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + hr = IDispatch_Invoke(pDispatch, DISPID_TM_STATE, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + ok((V_VT(&varresult) == VT_I4) && (V_I4(&varresult) == STATE_WIDGETIFIED), "Return val mismatch\n"); + + /* call get_State (direct) */ + hr = IWidget_get_State(pWidget, &the_state); + ok_ole_success(hr, IWidget_get_state); + ok(the_state == STATE_WIDGETIFIED, "should have returned WIDGET_WIDGETIFIED instead of %d\n", the_state); + + /* call put_State */ + the_state = STATE_WIDGETIFIED; + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BYREF|VT_I4; + V_I4REF(&vararg[0]) = (int *)&the_state; + dispparams.cNamedArgs = 1; + dispparams.cArgs = 1; + dispparams.rgdispidNamedArgs = &dispidNamed; + dispparams.rgvarg = vararg; + hr = IDispatch_Invoke(pDispatch, DISPID_TM_STATE, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + + /* call Map */ + bstr = SysAllocString(szTestTest); + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BYREF|VT_BSTR; + V_BSTRREF(&vararg[0]) = &bstr; + dispparams.cNamedArgs = 0; + dispparams.cArgs = 1; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_MAP, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + ok(V_VT(&varresult) == VT_BSTR, "Return value should be of type BSTR instead of %d\n", V_VT(&varresult)); + ok(!lstrcmpW(V_BSTR(&varresult), szTestTest), "Return value should have been \"TestTest\" instead of %s\n", wine_dbgstr_w(V_BSTR(&varresult))); + VariantClear(&varresult); + + /* call SetOleColor with large negative VT_I4 param */ + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = 0x80000005; + dispparams.cNamedArgs = 0; + dispparams.cArgs = 1; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = vararg; + hr = IDispatch_Invoke(pDispatch, DISPID_TM_SETOLECOLOR, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, NULL, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + + /* call GetOleColor */ + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_GETOLECOLOR, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + VariantClear(&varresult); + + /* call Clone */ + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_CLONE, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + ok(V_VT(&varresult) == VT_DISPATCH, "vt %x\n", V_VT(&varresult)); + VariantClear(&varresult); + + /* call CloneInterface */ + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_CLONEINTERFACE, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + ok(V_VT(&varresult) == VT_DISPATCH, "vt %x\n", V_VT(&varresult)); + VariantClear(&varresult); + + /* call CloneDispatch with automatic value getting */ + V_VT(&vararg[0]) = VT_I2; + V_I2(&vararg[0]) = 1; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 1; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_CLONEDISPATCH, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + + ok(excepinfo.wCode == 0x0 && excepinfo.scode == S_OK, + "EXCEPINFO differs from expected: wCode = 0x%x, scode = 0x%08x\n", + excepinfo.wCode, excepinfo.scode); + + ok(V_VT(&varresult) == VT_I2, "V_VT(&varresult) was %d instead of VT_I2\n", V_VT(&varresult)); + ok(V_I2(&varresult) == 1234, "V_I2(&varresult) was %d instead of 1234\n", V_I2(&varresult)); + VariantClear(&varresult); + + /* call CloneCoclass */ + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_CLONECOCLASS, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + + ok(excepinfo.wCode == 0x0 && excepinfo.scode == S_OK, + "EXCEPINFO differs from expected: wCode = 0x%x, scode = 0x%08x\n", + excepinfo.wCode, excepinfo.scode); + + ok(V_VT(&varresult) == VT_DISPATCH, "V_VT(&varresult) was %d instead of VT_DISPATCH\n", V_VT(&varresult)); + ok(!V_DISPATCH(&varresult), "V_DISPATCH(&varresult) should be NULL instead of %p\n", V_DISPATCH(&varresult)); + VariantClear(&varresult); + + /* call Value with a VT_VARIANT|VT_BYREF type */ + V_VT(&vararg[0]) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&vararg[0]) = &vararg[1]; + V_VT(&vararg[1]) = VT_I2; + V_I2(&vararg[1]) = 1; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 1; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_VALUE, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, IDispatch_Invoke); + + ok(excepinfo.wCode == 0x0 && excepinfo.scode == S_OK, + "EXCEPINFO differs from expected: wCode = 0x%x, scode = 0x%08x\n", + excepinfo.wCode, excepinfo.scode); + + ok(V_VT(&varresult) == VT_I2, "V_VT(&varresult) was %d instead of VT_I2\n", V_VT(&varresult)); + ok(V_I2(&varresult) == 1234, "V_I2(&varresult) was %d instead of 1234\n", V_I2(&varresult)); + VariantClear(&varresult); + + /* call Variant - exercises variant copying in ITypeInfo::Invoke and + * handling of void return types */ + /* use a big type to ensure that the variant was properly copied into the + * destination function's args */ + V_VT(&vararg[0]) = VT_CY; + S(V_CY(&vararg[0])).Hi = 0xdababe; + S(V_CY(&vararg[0])).Lo = 0xdeadbeef; + dispparams.cNamedArgs = 0; + dispparams.cArgs = 1; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_VARIANT, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok_ole_success(hr, IDispatch_Invoke); + VariantClear(&varresult); + + /* call VarArg */ + VariantInit(&vararg[3]); + V_VT(&vararg[3]) = VT_I4; + V_I4(&vararg[3]) = 3; + VariantInit(&vararg[2]); + V_VT(&vararg[2]) = VT_I4; + V_I4(&vararg[2]) = 0; + VariantInit(&vararg[1]); + V_VT(&vararg[1]) = VT_I4; + V_I4(&vararg[1]) = 1; + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = 2; + dispparams.cNamedArgs = 0; + dispparams.cArgs = 4; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = vararg; + hr = IDispatch_Invoke(pDispatch, DISPID_TM_VARARG, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok_ole_success(hr, ITypeInfo_Invoke); + + /* call VarArg, even one (non-optional, non-safearray) named argument is not allowed */ + dispidNamed = 0; + dispparams.cNamedArgs = 1; + dispparams.rgdispidNamedArgs = &dispidNamed; + hr = IDispatch_Invoke(pDispatch, DISPID_TM_VARARG, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_NONAMEDARGS, "IDispatch_Invoke should have returned DISP_E_NONAMEDARGS instead of 0x%08x\n", hr); + dispidNamed = DISPID_PROPERTYPUT; + + /* call Error */ + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_ERROR, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, NULL, &excepinfo, NULL); + ok(hr == DISP_E_EXCEPTION, "IDispatch_Invoke should have returned DISP_E_EXCEPTION instead of 0x%08x\n", hr); + ok(excepinfo.wCode == 0x0 && excepinfo.scode == E_NOTIMPL, + "EXCEPINFO differs from expected: wCode = 0x%x, scode = 0x%08x\n", + excepinfo.wCode, excepinfo.scode); + VariantClear(&varresult); + + /* call BstrRet */ + pTypeInfo = NonOleAutomation_GetTypeInfo(); + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = ITypeInfo_Invoke(pTypeInfo, &NonOleAutomation, DISPID_NOA_BSTRRET, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + ok_ole_success(hr, ITypeInfo_Invoke); + ok(V_VT(&varresult) == VT_BSTR, "V_VT(&varresult) should be VT_BSTR instead of %d\n", V_VT(&varresult)); + ok(V_BSTR(&varresult) != NULL, "V_BSTR(&varresult) should not be NULL\n"); + + VariantClear(&varresult); + ITypeInfo_Release(pTypeInfo); + + /* tests call put_Name without named arg */ + VariantInit(&vararg[0]); + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 1; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_NAME, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_PARAMNOTFOUND, "IDispatch_Invoke should have returned DISP_E_PARAMNOTFOUND instead of 0x%08x\n", hr); + VariantClear(&varresult); + + /* tests param type that cannot be coerced */ + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_UNKNOWN; + V_UNKNOWN(&vararg[0]) = NULL; + dispparams.cNamedArgs = 1; + dispparams.rgdispidNamedArgs = &dispidNamed; + dispparams.cArgs = 1; + dispparams.rgvarg = vararg; + VariantInit(&varresult); +#if 0 /* NULL unknown not currently marshaled correctly */ + hr = IDispatch_Invoke(pDispatch, DISPID_TM_NAME, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_TYPEMISMATCH, "IDispatch_Invoke should have returned DISP_E_TYPEMISMATCH instead of 0x%08x\n", hr); +#endif + VariantClear(&varresult); + + /* tests bad param type */ + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_CLSID; + V_BYREF(&vararg[0]) = NULL; + dispparams.cNamedArgs = 1; + dispparams.rgdispidNamedArgs = &dispidNamed; + dispparams.cArgs = 1; + dispparams.rgvarg = vararg; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_NAME, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYPUT, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_BADVARTYPE, "IDispatch_Invoke should have returned DISP_E_BADVARTYPE instead of 0x%08x\n", hr); + VariantClear(&varresult); + + /* tests too small param count */ + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.cArgs = 0; + dispparams.rgvarg = NULL; + VariantInit(&varresult); + hr = IDispatch_Invoke(pDispatch, DISPID_TM_DOSOMETHING, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_METHOD, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_BADPARAMCOUNT, "IDispatch_Invoke should have returned DISP_E_BADPARAMCOUNT instead of 0x%08x\n", hr); + VariantClear(&varresult); + + /* tests propget function with large param count */ + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_BSTR; + V_BSTR(&vararg[0]) = NULL; + V_VT(&vararg[1]) = VT_I4; + V_I4(&vararg[1]) = 1; + dispparams.cNamedArgs = 0; + dispparams.cArgs = 2; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = vararg; + hr = IDispatch_Invoke(pDispatch, DISPID_TM_STATE, &IID_NULL, LOCALE_NEUTRAL, DISPATCH_PROPERTYGET, &dispparams, &varresult, &excepinfo, NULL); + ok(hr == DISP_E_NOTACOLLECTION, "IDispatch_Invoke should have returned DISP_E_NOTACOLLECTION instead of 0x%08x\n", hr); + + IDispatch_Release(pDispatch); + IWidget_Release(pWidget); + + trace("calling end_host_object\n"); + end_host_object(tid, thread); +} + +static void test_DispCallFunc(void) +{ + static const WCHAR szEmpty[] = { 0 }; + VARTYPE rgvt[] = { VT_R8, VT_BSTR, VT_BSTR, VT_VARIANT|VT_BYREF }; + VARIANTARG vararg[4]; + VARIANTARG varref; + VARIANTARG *rgpvarg[4] = { &vararg[0], &vararg[1], &vararg[2], &vararg[3] }; + VARIANTARG varresult; + HRESULT hr; + IWidget *pWidget = Widget_Create(); + V_VT(&vararg[0]) = VT_R8; + V_R8(&vararg[0]) = 3.141; + V_VT(&vararg[1]) = VT_BSTR; + V_BSTR(&vararg[1]) = SysAllocString(szEmpty); + V_VT(&vararg[2]) = VT_BSTR; + V_BSTR(&vararg[2]) = SysAllocString(szEmpty); + V_VT(&vararg[3]) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&vararg[3]) = &varref; + V_VT(&varref) = VT_ERROR; + V_ERROR(&varref) = DISP_E_PARAMNOTFOUND; + VariantInit(&varresult); + hr = DispCallFunc(pWidget, 36, CC_STDCALL, VT_UI4, 4, rgvt, rgpvarg, &varresult); + ok_ole_success(hr, DispCallFunc); + VariantClear(&varresult); +} + +START_TEST(tmarshal) +{ + HRESULT hr; + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hr = register_current_module_typelib(); + ok_ole_success(hr, register_current_module_typelib); + + test_typelibmarshal(); + test_DispCallFunc(); + + CoUninitialize(); +} diff --git a/rostests/winetests/oleaut32/tmarshal.idl b/rostests/winetests/oleaut32/tmarshal.idl new file mode 100644 index 00000000000..9b94843b46e --- /dev/null +++ b/rostests/winetests/oleaut32/tmarshal.idl @@ -0,0 +1,286 @@ +/* + * Copyright (C) 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + */ + +#include "tmarshal_dispids.h" +import "ocidl.idl"; + +[ + uuid(d96d8a3e-78b6-4c8d-8f27-059db959be8a), + version(1.0), + helpstring("Test Typelib") +] +library TestTypelib +{ + importlib("stdole2.tlb"); + + typedef enum tagSTATE + { + STATE_UNWIDGETIFIED = 1, + STATE_WIDGETIFIED + } STATE; + + coclass ApplicationObject2; + + [ + odl, + uuid(12345678-1234-4321-1234-121212121212) + ] + interface ISomethingFromDispatch : IDispatch + { + HRESULT anotherfn(void); + } + + [ + odl, + uuid(a1f8cae3-c947-4c5f-b57d-c87b9b5f3586), + oleautomation, + dual + ] + interface IWidget : IDispatch + { + [propput, id(DISPID_TM_NAME)] + HRESULT Name([in] BSTR name); + [propget, id(DISPID_TM_NAME)] + HRESULT Name([out, retval] BSTR *name); + + [id(DISPID_TM_DOSOMETHING)] + HRESULT DoSomething([in] double number, [out] BSTR *str1, [in, defaultvalue("")] BSTR str2, [in, optional] VARIANT *opt); + + [propget, id(DISPID_TM_STATE)] + HRESULT State([out, retval] STATE *state); + [propput, id(DISPID_TM_STATE)] + HRESULT State([in] STATE state); + + [id(DISPID_TM_MAP)] + HRESULT Map([in] BSTR bstrId, [out, retval] BSTR *sValue); + + [id(DISPID_TM_SETOLECOLOR)] + HRESULT SetOleColor([in] OLE_COLOR val); + + [id(DISPID_TM_GETOLECOLOR)] + HRESULT GetOleColor([out, retval] OLE_COLOR *pVal); + + [propget, id(DISPID_TM_CLONE)] + HRESULT Clone([out, retval] IWidget **ppVal); + + [propget, id(DISPID_TM_CLONEDISPATCH)] + HRESULT CloneDispatch([out, retval] IDispatch **ppVal); + + [propget, id(DISPID_TM_CLONECOCLASS)] + HRESULT CloneCoclass([out, retval] ApplicationObject2 **ppVal); + + [propget, id(DISPID_VALUE)] + HRESULT Value([in] VARIANT *value, [out, retval] VARIANT *retval); + + [id(DISPID_TM_ARRAY)] + HRESULT Array([in] SAFEARRAY(BSTR) values); + + [id(DISPID_TM_VARARRAYPTR)] + HRESULT VariantArrayPtr([in] SAFEARRAY(VARIANT) *values); + + [id(DISPID_TM_VARIANT)] + void Variant([in] VARIANT var); + + [vararg, id(DISPID_TM_VARARG)] + void VarArg([in] int numexpect, [in] SAFEARRAY(VARIANT) values); + + [id(DISPID_TM_ERROR)] + HRESULT Error(); + + [propget, id(DISPID_TM_CLONEINTERFACE)] + HRESULT CloneInterface([out, retval] ISomethingFromDispatch **ppVal); + + } + + [ + odl, + uuid(a028db05-30f0-4b93-b17a-41c72f831d84), +#if 0 /* FIXME: commented out as causes widl to generate incorrect typelib */ + dual, +#endif + oleautomation + ] + interface IKindaEnumWidget : IUnknown + { + HRESULT Next( + [out] IWidget **widget); + + HRESULT Count( + [out] unsigned long *count); + + HRESULT Reset(); + + HRESULT Clone( + [out] IKindaEnumWidget **ppenum); + } + + [ + odl, + uuid(a028db06-30f0-4b93-b17a-41c72f831d84), + ] + interface INonOleAutomation : IUnknown + { + [id(DISPID_NOA_BSTRRET)] + BSTR BstrRet(); + } + + + [ + dllname("comm.drv"), + uuid(d377f60b-8639-4261-8ee7-75c8340d2cc9), + ] + module BadModule + { + [ + entry("Foo"), + ] + HRESULT BadModuleFoo(); + }; + + [ + dllname("oleaut32.dll"), + uuid(d377f60c-8639-4261-8ee7-75c8340d2cc9), + ] + module BadEntry + { + [ + entry("Foo"), + ] + HRESULT BadEntryFoo(); + }; + + [ + uuid(bb171948-10ec-407a-9a57-2f85f797ff1a), + appobject, + ] + coclass ApplicationObject2 + { + interface IWidget; + [source] interface IWidget; + }; + + [ + odl, + uuid(375f8a9d-33d0-44f3-b972-61f8407899e0) + ] + interface ItestIF1 : IUnknown + { + HRESULT fn1([in] int x); + HRESULT fn2([out,retval] int *x); + } + + [ + odl, + uuid(094056a3-666f-4956-be12-1859668310b8) + ] + interface ItestIF2 : ItestIF1 + { + HRESULT fn3([in] int y); + } + + [ + odl, + uuid(33baba09-2e68-43ab-81fe-d84b403df2e5) + ] + dispinterface ItestIF3 + { + interface ItestIF2; + } + + [ + odl, + uuid(a01005c7-7491-42eb-94f3-668e37ce60a6) + ] + dispinterface ItestIF4 + { + properties: + methods: + [id(0x1c)] HRESULT fn([in] int z); + } + + [ + odl, + uuid(4ab61e25-c09f-4239-8f7f-7a018ea0199f), + dual + ] + interface ItestIF5 : ItestIF2 + { + [id(0x1234)] HRESULT fn4([in] int a); + [id(0x1235)] HRESULT fn5([in] int a); + } + + [ + odl, + uuid(ec236d8e-2cc7-44f2-b394-36c86ff3da74) + ] + interface ItestIF6 : IDispatch + { + [id(0x1234)] HRESULT fn4([in] int a); + [id(0x1235)] HRESULT fn5([in] int a); + } + + [ + odl, + uuid(f711b105-554d-4751-818c-46fcc5d7c0d5), + dual + ] + interface ItestIF7 : ItestIF6 + { + [id(0x1236)] HRESULT fn6([in] int a); + } + + [ + odl, + uuid(bdfa260b-ef40-43d3-b071-cddec919f132) + ] + interface ItestIF8 + { + HRESULT fn1([in] int x); + HRESULT fn2([out,retval] int *x); + } + + [ + odl, + uuid(51033a23-dc37-4f19-aa34-4d8a670458a0) + + ] + interface ItestIF9 : ItestIF8 + { + HRESULT fn3([in] int y); + } + + [ + odl, + uuid(2e8f14fe-0bce-42f0-8b7d-3af8393c7967) + ] + dispinterface ItestIF10 + { + interface ItestIF9; + } + + [ + odl, + uuid(7d9e9371-482e-4944-9b19-511fc705236f) + ] + dispinterface ItestIF11 + { + interface ItestIF7; + } + +}; diff --git a/rostests/winetests/oleaut32/tmarshal.rc b/rostests/winetests/oleaut32/tmarshal.rc new file mode 100644 index 00000000000..aabf61181b9 --- /dev/null +++ b/rostests/winetests/oleaut32/tmarshal.rc @@ -0,0 +1,30 @@ +/* + * Resource file for tmarshaltest + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#include "wine/wine_common_ver.rc" + +1 TYPELIB LOADONCALL DISCARDABLE tmarshal.tlb diff --git a/rostests/winetests/oleaut32/tmarshal_dispids.h b/rostests/winetests/oleaut32/tmarshal_dispids.h new file mode 100644 index 00000000000..4bf47cdf629 --- /dev/null +++ b/rostests/winetests/oleaut32/tmarshal_dispids.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2006 Robert Shearman 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 DISPID_TM_NAME 1 +#define DISPID_TM_DOSOMETHING 2 +#define DISPID_TM_STATE 3 +#define DISPID_TM_MAP 4 +#define DISPID_TM_SETOLECOLOR 5 +#define DISPID_TM_GETOLECOLOR 6 +#define DISPID_TM_CLONE 7 +#define DISPID_TM_CLONEDISPATCH 8 +#define DISPID_TM_CLONECOCLASS 9 +#define DISPID_TM_ARRAY 10 +#define DISPID_TM_VARARRAYPTR 11 +#define DISPID_TM_VARIANT 12 +#define DISPID_TM_VARARG 13 +#define DISPID_TM_ERROR 14 +#define DISPID_TM_CLONEINTERFACE 15 + +#define DISPID_NOA_BSTRRET 1 diff --git a/rostests/winetests/oleaut32/typelib.c b/rostests/winetests/oleaut32/typelib.c new file mode 100644 index 00000000000..e022b8263de --- /dev/null +++ b/rostests/winetests/oleaut32/typelib.c @@ -0,0 +1,1285 @@ +/* + * ITypeLib and ITypeInfo test + * + * Copyright 2004 Jacek Caban + * 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 + */ + +#define COBJMACROS + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "oleauto.h" +#include "ocidl.h" +#include "shlwapi.h" +#include "tmarshal.h" + +#define expect_eq(expr, value, type, format) { type _ret = (expr); ok((value) == _ret, #expr " expected " format " got " format "\n", value, _ret); } +#define expect_int(expr, value) expect_eq(expr, (int)value, int, "%d") +#define expect_hex(expr, value) expect_eq(expr, (int)value, int, "0x%x") +#define expect_null(expr) expect_eq(expr, NULL, const void *, "%p") + +#define expect_wstr_utf8val(expr, value) \ + { \ + CHAR buf[260]; \ + expect_eq(!WideCharToMultiByte(CP_UTF8, 0, (expr), -1, buf, 260, NULL, NULL), 0, int, "%d"); \ + ok(lstrcmp(value, buf) == 0, #expr " expected \"%s\" got \"%s\"\n", value, buf); \ + } + +#define ole_expect(expr, expect) { \ + HRESULT r = expr; \ + ok(r == (expect), #expr " returned %x, expected %s (%x)\n", r, #expect, expect); \ +} + +#define ole_check(expr) ole_expect(expr, S_OK); + +#define ok_ole_success(hr, func) ok(hr == S_OK, #func " failed with error 0x%08x\n", hr) + +static const WCHAR wszStdOle2[] = {'s','t','d','o','l','e','2','.','t','l','b',0}; + +static void ref_count_test(LPCWSTR type_lib) +{ + ITypeLib *iface; + ITypeInfo *iti1, *iti2; + HRESULT hRes; + int ref_count; + + trace("Loading type library\n"); + hRes = LoadTypeLib(type_lib, &iface); + ok(hRes == S_OK, "Could not load type library\n"); + if(hRes != S_OK) + return; + + hRes = ITypeLib_GetTypeInfo(iface, 1, &iti1); + ok(hRes == S_OK, "ITypeLib_GetTypeInfo failed on index = 1\n"); + ok(ref_count=ITypeLib_Release(iface) > 0, "ITypeLib destroyed while ITypeInfo has back pointer\n"); + if(!ref_count) + return; + + hRes = ITypeLib_GetTypeInfo(iface, 1, &iti2); + ok(hRes == S_OK, "ITypeLib_GetTypeInfo failed on index = 1\n"); + ok(iti1 == iti2, "ITypeLib_GetTypeInfo returned different pointers for same indexes\n"); + + ITypeLib_AddRef(iface); + ITypeInfo_Release(iti2); + ITypeInfo_Release(iti1); + ok(ITypeLib_Release(iface) == 0, "ITypeLib should be destroyed here.\n"); +} + +static void test_TypeComp(void) +{ + ITypeLib *pTypeLib; + ITypeComp *pTypeComp; + HRESULT hr; + ULONG ulHash; + DESCKIND desckind; + BINDPTR bindptr; + ITypeInfo *pTypeInfo; + ITypeInfo *pFontTypeInfo; + static WCHAR wszStdFunctions[] = {'S','t','d','F','u','n','c','t','i','o','n','s',0}; + static WCHAR wszSavePicture[] = {'S','a','v','e','P','i','c','t','u','r','e',0}; + static WCHAR wszOLE_TRISTATE[] = {'O','L','E','_','T','R','I','S','T','A','T','E',0}; + static WCHAR wszUnchecked[] = {'U','n','c','h','e','c','k','e','d',0}; + static WCHAR wszIUnknown[] = {'I','U','n','k','n','o','w','n',0}; + static WCHAR wszFont[] = {'F','o','n','t',0}; + static WCHAR wszGUID[] = {'G','U','I','D',0}; + static WCHAR wszStdPicture[] = {'S','t','d','P','i','c','t','u','r','e',0}; + static WCHAR wszOLE_COLOR[] = {'O','L','E','_','C','O','L','O','R',0}; + static WCHAR wszClone[] = {'C','l','o','n','e',0}; + static WCHAR wszclone[] = {'c','l','o','n','e',0}; + + hr = LoadTypeLib(wszStdOle2, &pTypeLib); + ok_ole_success(hr, LoadTypeLib); + + hr = ITypeLib_GetTypeComp(pTypeLib, &pTypeComp); + ok_ole_success(hr, ITypeLib_GetTypeComp); + + /* test getting a TKIND_MODULE */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszStdFunctions); + hr = ITypeComp_Bind(pTypeComp, wszStdFunctions, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_TYPECOMP, + "desckind should have been DESCKIND_TYPECOMP instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + + ITypeComp_Release(bindptr.lptcomp); + + /* test getting a TKIND_MODULE with INVOKE_PROPERTYGET */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszStdFunctions); + hr = ITypeComp_Bind(pTypeComp, wszStdFunctions, ulHash, INVOKE_PROPERTYGET, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_TYPECOMP, + "desckind should have been DESCKIND_TYPECOMP instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ITypeComp_Release(bindptr.lptcomp); + + /* test getting a function within a TKIND_MODULE */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszSavePicture); + hr = ITypeComp_Bind(pTypeComp, wszSavePicture, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_FUNCDESC, + "desckind should have been DESCKIND_FUNCDESC instead of %d\n", + desckind); + ok(bindptr.lpfuncdesc != NULL, "bindptr.lpfuncdesc should not have been set to NULL\n"); + ITypeInfo_ReleaseFuncDesc(pTypeInfo, bindptr.lpfuncdesc); + ITypeInfo_Release(pTypeInfo); + + /* test getting a function within a TKIND_MODULE with INVOKE_PROPERTYGET */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszSavePicture); + hr = ITypeComp_Bind(pTypeComp, wszSavePicture, ulHash, INVOKE_PROPERTYGET, &pTypeInfo, &desckind, &bindptr); + todo_wine ok(hr == TYPE_E_TYPEMISMATCH, + "ITypeComp_Bind should have failed with TYPE_E_TYPEMISMATCH instead of 0x%08x\n", + hr); + + ok(desckind == DESCKIND_NONE, + "desckind should have been DESCKIND_NONE instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ok(!bindptr.lptcomp, "bindptr should have been set to NULL\n"); + + /* test getting a TKIND_ENUM */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszOLE_TRISTATE); + hr = ITypeComp_Bind(pTypeComp, wszOLE_TRISTATE, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_TYPECOMP, + "desckind should have been DESCKIND_TYPECOMP instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + + ITypeComp_Release(bindptr.lptcomp); + + /* test getting a value within a TKIND_ENUM */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszUnchecked); + hr = ITypeComp_Bind(pTypeComp, wszUnchecked, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_VARDESC, + "desckind should have been DESCKIND_VARDESC instead of %d\n", + desckind); + ITypeInfo_ReleaseVarDesc(pTypeInfo, bindptr.lpvardesc); + ITypeInfo_Release(pTypeInfo); + + /* test getting a TKIND_INTERFACE */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszIUnknown); + hr = ITypeComp_Bind(pTypeComp, wszIUnknown, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_NONE, + "desckind should have been DESCKIND_NONE instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ok(!bindptr.lptcomp, "bindptr should have been set to NULL\n"); + + /* test getting a TKIND_DISPATCH */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszFont); + hr = ITypeComp_Bind(pTypeComp, wszFont, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_NONE, + "desckind should have been DESCKIND_NONE instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ok(!bindptr.lptcomp, "bindptr should have been set to NULL\n"); + + /* test getting a TKIND_RECORD/TKIND_ALIAS */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszGUID); + hr = ITypeComp_Bind(pTypeComp, wszGUID, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_NONE, + "desckind should have been DESCKIND_NONE instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ok(!bindptr.lptcomp, "bindptr should have been set to NULL\n"); + + /* test getting a TKIND_ALIAS */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszOLE_COLOR); + hr = ITypeComp_Bind(pTypeComp, wszOLE_COLOR, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_NONE, + "desckind should have been DESCKIND_NONE instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ok(!bindptr.lptcomp, "bindptr should have been set to NULL\n"); + + /* test getting a TKIND_COCLASS */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszStdPicture); + hr = ITypeComp_Bind(pTypeComp, wszStdPicture, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_NONE, + "desckind should have been DESCKIND_NONE instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ok(!bindptr.lptcomp, "bindptr should have been set to NULL\n"); + + ITypeComp_Release(pTypeComp); + + /* tests for ITypeComp on an interface */ + hr = ITypeLib_GetTypeInfoOfGuid(pTypeLib, &IID_IFont, &pFontTypeInfo); + ok_ole_success(hr, ITypeLib_GetTypeInfoOfGuid); + + hr = ITypeInfo_GetTypeComp(pFontTypeInfo, &pTypeComp); + ok_ole_success(hr, ITypeLib_GetTypeComp); + + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszClone); + hr = ITypeComp_Bind(pTypeComp, wszClone, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_FUNCDESC, + "desckind should have been DESCKIND_FUNCDESC instead of %d\n", + desckind); + ok(bindptr.lpfuncdesc != NULL, "bindptr.lpfuncdesc should not have been set to NULL\n"); + ITypeInfo_ReleaseFuncDesc(pTypeInfo, bindptr.lpfuncdesc); + ITypeInfo_Release(pTypeInfo); + + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszClone); + hr = ITypeComp_Bind(pTypeComp, wszClone, ulHash, INVOKE_PROPERTYGET, &pTypeInfo, &desckind, &bindptr); + ok(hr == TYPE_E_TYPEMISMATCH, "ITypeComp_Bind should have failed with TYPE_E_TYPEMISMATCH instead of 0x%08x\n", hr); + + ok(desckind == DESCKIND_NONE, + "desckind should have been DESCKIND_NONE instead of %d\n", + desckind); + ok(!pTypeInfo, "pTypeInfo should have been set to NULL\n"); + ok(!bindptr.lptcomp, "bindptr should have been set to NULL\n"); + + /* tests that the compare is case-insensitive */ + ulHash = LHashValOfNameSys(SYS_WIN32, LOCALE_NEUTRAL, wszclone); + hr = ITypeComp_Bind(pTypeComp, wszclone, ulHash, 0, &pTypeInfo, &desckind, &bindptr); + ok_ole_success(hr, ITypeComp_Bind); + + ok(desckind == DESCKIND_FUNCDESC, + "desckind should have been DESCKIND_FUNCDESC instead of %d\n", + desckind); + ok(bindptr.lpfuncdesc != NULL, "bindptr.lpfuncdesc should not have been set to NULL\n"); + ITypeInfo_ReleaseFuncDesc(pTypeInfo, bindptr.lpfuncdesc); + ITypeInfo_Release(pTypeInfo); + + ITypeComp_Release(pTypeComp); + ITypeInfo_Release(pFontTypeInfo); + ITypeLib_Release(pTypeLib); +} + +static void test_CreateDispTypeInfo(void) +{ + ITypeInfo *pTypeInfo, *pTI2; + HRESULT hr; + INTERFACEDATA ifdata; + METHODDATA methdata[4]; + PARAMDATA parms1[2]; + PARAMDATA parms3[1]; + TYPEATTR *pTypeAttr; + HREFTYPE href; + FUNCDESC *pFuncDesc; + MEMBERID memid; + + static WCHAR func1[] = {'f','u','n','c','1',0}; + static const WCHAR func2[] = {'f','u','n','c','2',0}; + static const WCHAR func3[] = {'f','u','n','c','3',0}; + static const WCHAR parm1[] = {'p','a','r','m','1',0}; + static const WCHAR parm2[] = {'p','a','r','m','2',0}; + OLECHAR *name = func1; + + ifdata.pmethdata = methdata; + ifdata.cMembers = sizeof(methdata) / sizeof(methdata[0]); + + methdata[0].szName = SysAllocString(func1); + methdata[0].ppdata = parms1; + methdata[0].dispid = 0x123; + methdata[0].iMeth = 0; + methdata[0].cc = CC_STDCALL; + methdata[0].cArgs = 2; + methdata[0].wFlags = DISPATCH_METHOD; + methdata[0].vtReturn = VT_HRESULT; + parms1[0].szName = SysAllocString(parm1); + parms1[0].vt = VT_I4; + parms1[1].szName = SysAllocString(parm2); + parms1[1].vt = VT_BSTR; + + methdata[1].szName = SysAllocString(func2); + methdata[1].ppdata = NULL; + methdata[1].dispid = 0x124; + methdata[1].iMeth = 1; + methdata[1].cc = CC_STDCALL; + methdata[1].cArgs = 0; + methdata[1].wFlags = DISPATCH_PROPERTYGET; + methdata[1].vtReturn = VT_I4; + + methdata[2].szName = SysAllocString(func3); + methdata[2].ppdata = parms3; + methdata[2].dispid = 0x125; + methdata[2].iMeth = 3; + methdata[2].cc = CC_STDCALL; + methdata[2].cArgs = 1; + methdata[2].wFlags = DISPATCH_PROPERTYPUT; + methdata[2].vtReturn = VT_HRESULT; + parms3[0].szName = SysAllocString(parm1); + parms3[0].vt = VT_I4; + + methdata[3].szName = SysAllocString(func3); + methdata[3].ppdata = NULL; + methdata[3].dispid = 0x125; + methdata[3].iMeth = 4; + methdata[3].cc = CC_STDCALL; + methdata[3].cArgs = 0; + methdata[3].wFlags = DISPATCH_PROPERTYGET; + methdata[3].vtReturn = VT_I4; + + hr = CreateDispTypeInfo(&ifdata, LOCALE_NEUTRAL, &pTypeInfo); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTypeInfo, &pTypeAttr); + ok(hr == S_OK, "hr %08x\n", hr); + + ok(pTypeAttr->typekind == TKIND_COCLASS, "typekind %0x\n", pTypeAttr->typekind); + ok(pTypeAttr->cImplTypes == 1, "cImplTypes %d\n", pTypeAttr->cImplTypes); + ok(pTypeAttr->cFuncs == 0, "cFuncs %d\n", pTypeAttr->cFuncs); + ok(pTypeAttr->wTypeFlags == 0, "wTypeFlags %04x\n", pTypeAttr->cFuncs); + ITypeInfo_ReleaseTypeAttr(pTypeInfo, pTypeAttr); + + hr = ITypeInfo_GetRefTypeOfImplType(pTypeInfo, 0, &href); + ok(hr == S_OK, "hr %08x\n", hr); + ok(href == 0, "href = 0x%x\n", href); + hr = ITypeInfo_GetRefTypeInfo(pTypeInfo, href, &pTI2); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(pTI2, &pTypeAttr); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTypeAttr->typekind == TKIND_INTERFACE, "typekind %0x\n", pTypeAttr->typekind); + ok(pTypeAttr->cFuncs == 4, "cFuncs %d\n", pTypeAttr->cFuncs); + ok(IsEqualGUID(&pTypeAttr->guid, &GUID_NULL), "guid {%08x-...}\n", pTypeAttr->guid.Data1); + ok(pTypeAttr->wTypeFlags == 0, "typeflags %08x\n", pTypeAttr->wTypeFlags); + + ITypeInfo_ReleaseTypeAttr(pTI2, pTypeAttr); + + hr = ITypeInfo_GetFuncDesc(pTI2, 0, &pFuncDesc); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFuncDesc->memid == 0x123, "memid %x\n", pFuncDesc->memid); + ok(pFuncDesc->funckind == FUNC_VIRTUAL, "funckind %d\n", pFuncDesc->funckind); + ok(pFuncDesc->invkind == methdata[0].wFlags, "invkind %d\n", pFuncDesc->invkind); + ok(pFuncDesc->callconv == methdata[0].cc, "callconv %d\n", pFuncDesc->callconv); + ok(pFuncDesc->cParams == methdata[0].cArgs, "cParams %d\n", pFuncDesc->cParams); + ok(pFuncDesc->oVft == 0, "oVft %d\n", pFuncDesc->oVft); + ok(pFuncDesc->wFuncFlags == 0, "oVft %d\n", pFuncDesc->wFuncFlags); + ok(pFuncDesc->elemdescFunc.tdesc.vt == VT_HRESULT, "ret vt %x\n", pFuncDesc->elemdescFunc.tdesc.vt); + ok(pFuncDesc->lprgelemdescParam[0].tdesc.vt == VT_I4, "parm 0 vt %x\n", pFuncDesc->lprgelemdescParam[0].tdesc.vt); + ok(U(pFuncDesc->lprgelemdescParam[0]).paramdesc.wParamFlags == PARAMFLAG_NONE, "parm 0 flags %x\n", U(pFuncDesc->lprgelemdescParam[0]).paramdesc.wParamFlags); + + ok(pFuncDesc->lprgelemdescParam[1].tdesc.vt == VT_BSTR, "parm 1 vt %x\n", pFuncDesc->lprgelemdescParam[1].tdesc.vt); + ok(U(pFuncDesc->lprgelemdescParam[1]).paramdesc.wParamFlags == PARAMFLAG_NONE, "parm 1 flags %x\n", U(pFuncDesc->lprgelemdescParam[1]).paramdesc.wParamFlags); + ITypeInfo_ReleaseFuncDesc(pTI2, pFuncDesc); + + hr = ITypeInfo_GetFuncDesc(pTI2, 1, &pFuncDesc); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFuncDesc->funckind == FUNC_VIRTUAL, "funckind %d\n", pFuncDesc->funckind); + ok(pFuncDesc->invkind == methdata[1].wFlags, "invkind %d\n", pFuncDesc->invkind); + ok(pFuncDesc->callconv == methdata[1].cc, "callconv %d\n", pFuncDesc->callconv); + ok(pFuncDesc->cParams == methdata[1].cArgs, "cParams %d\n", pFuncDesc->cParams); + ok(pFuncDesc->oVft == 4, "oVft %d\n", pFuncDesc->oVft); + ok(pFuncDesc->wFuncFlags == 0, "oVft %d\n", pFuncDesc->wFuncFlags); + ok(pFuncDesc->elemdescFunc.tdesc.vt == VT_I4, "ret vt %x\n", pFuncDesc->elemdescFunc.tdesc.vt); + ITypeInfo_ReleaseFuncDesc(pTI2, pFuncDesc); + + hr = ITypeInfo_GetFuncDesc(pTI2, 2, &pFuncDesc); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFuncDesc->funckind == FUNC_VIRTUAL, "funckind %d\n", pFuncDesc->funckind); + ok(pFuncDesc->invkind == methdata[2].wFlags, "invkind %d\n", pFuncDesc->invkind); + ok(pFuncDesc->callconv == methdata[2].cc, "callconv %d\n", pFuncDesc->callconv); + ok(pFuncDesc->cParams == methdata[2].cArgs, "cParams %d\n", pFuncDesc->cParams); + ok(pFuncDesc->oVft == 12, "oVft %d\n", pFuncDesc->oVft); + ok(pFuncDesc->wFuncFlags == 0, "oVft %d\n", pFuncDesc->wFuncFlags); + ok(pFuncDesc->elemdescFunc.tdesc.vt == VT_HRESULT, "ret vt %x\n", pFuncDesc->elemdescFunc.tdesc.vt); + ok(pFuncDesc->lprgelemdescParam[0].tdesc.vt == VT_I4, "parm 0 vt %x\n", pFuncDesc->lprgelemdescParam[0].tdesc.vt); + ok(U(pFuncDesc->lprgelemdescParam[0]).paramdesc.wParamFlags == PARAMFLAG_NONE, "parm 0 flags %x\n", U(pFuncDesc->lprgelemdescParam[0]).paramdesc.wParamFlags); + ITypeInfo_ReleaseFuncDesc(pTI2, pFuncDesc); + + hr = ITypeInfo_GetFuncDesc(pTI2, 3, &pFuncDesc); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFuncDesc->funckind == FUNC_VIRTUAL, "funckind %d\n", pFuncDesc->funckind); + ok(pFuncDesc->invkind == methdata[3].wFlags, "invkind %d\n", pFuncDesc->invkind); + ok(pFuncDesc->callconv == methdata[3].cc, "callconv %d\n", pFuncDesc->callconv); + ok(pFuncDesc->cParams == methdata[3].cArgs, "cParams %d\n", pFuncDesc->cParams); + ok(pFuncDesc->oVft == 16, "oVft %d\n", pFuncDesc->oVft); + ok(pFuncDesc->wFuncFlags == 0, "oVft %d\n", pFuncDesc->wFuncFlags); + ok(pFuncDesc->elemdescFunc.tdesc.vt == VT_I4, "ret vt %x\n", pFuncDesc->elemdescFunc.tdesc.vt); + ITypeInfo_ReleaseFuncDesc(pTI2, pFuncDesc); + + /* test GetIDsOfNames on a coclass to see if it searches its interfaces */ + hr = ITypeInfo_GetIDsOfNames(pTypeInfo, &name, 1, &memid); + ok(hr == S_OK, "hr 0x%08x\n", hr); + ok(memid == 0x123, "memid 0x%08x\n", memid); + + ITypeInfo_Release(pTI2); + ITypeInfo_Release(pTypeInfo); + + SysFreeString(parms1[0].szName); + SysFreeString(parms1[1].szName); + SysFreeString(parms3[0].szName); + SysFreeString(methdata[0].szName); + SysFreeString(methdata[1].szName); + SysFreeString(methdata[2].szName); + SysFreeString(methdata[3].szName); +} + +static void test_TypeInfo(void) +{ + ITypeLib *pTypeLib; + ITypeInfo *pTypeInfo; + HRESULT hr; + static WCHAR wszBogus[] = { 'b','o','g','u','s',0 }; + static WCHAR wszGetTypeInfo[] = { 'G','e','t','T','y','p','e','I','n','f','o',0 }; + static WCHAR wszClone[] = {'C','l','o','n','e',0}; + OLECHAR* bogus = wszBogus; + OLECHAR* pwszGetTypeInfo = wszGetTypeInfo; + OLECHAR* pwszClone = wszClone; + DISPID dispidMember; + DISPPARAMS dispparams; + + hr = LoadTypeLib(wszStdOle2, &pTypeLib); + ok_ole_success(hr, LoadTypeLib); + + hr = ITypeLib_GetTypeInfoOfGuid(pTypeLib, &IID_IFont, &pTypeInfo); + ok_ole_success(hr, ITypeLib_GetTypeInfoOfGuid); + + /* test nonexistent method name */ + hr = ITypeInfo_GetIDsOfNames(pTypeInfo, &bogus, 1, &dispidMember); + ok(hr == DISP_E_UNKNOWNNAME, + "ITypeInfo_GetIDsOfNames should have returned DISP_E_UNKNOWNNAME instead of 0x%08x\n", + hr); + + /* test invalid memberid */ + dispparams.cNamedArgs = 0; + dispparams.cArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = NULL; + hr = ITypeInfo_Invoke(pTypeInfo, (void *)0xdeadbeef, 0xdeadbeef, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "ITypeInfo_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr); + + hr = ITypeInfo_GetIDsOfNames(pTypeInfo, &pwszClone, 1, &dispidMember); + ok_ole_success(hr, ITypeInfo_GetIDsOfNames); + + /* test correct memberid, but wrong flags */ + hr = ITypeInfo_Invoke(pTypeInfo, (void *)0xdeadbeef, dispidMember, DISPATCH_PROPERTYGET, &dispparams, NULL, NULL, NULL); + ok(hr == DISP_E_MEMBERNOTFOUND, "ITypeInfo_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr); + + /* test NULL dispparams */ + hr = ITypeInfo_Invoke(pTypeInfo, (void *)0xdeadbeef, dispidMember, DISPATCH_METHOD, NULL, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "ITypeInfo_Invoke should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + /* test dispparams->cNamedArgs being bigger than dispparams->cArgs */ + dispparams.cNamedArgs = 1; + hr = ITypeInfo_Invoke(pTypeInfo, (void *)0xdeadbeef, dispidMember, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "ITypeInfo_Invoke should have returned E_INVALIDARG instead of 0x%08x\n", hr); + + ITypeInfo_Release(pTypeInfo); + + hr = ITypeLib_GetTypeInfoOfGuid(pTypeLib, &IID_IDispatch, &pTypeInfo); + ok_ole_success(hr, ITypeLib_GetTypeInfoOfGuid); + + hr = ITypeInfo_GetIDsOfNames(pTypeInfo, &pwszGetTypeInfo, 1, &dispidMember); + ok_ole_success(hr, ITypeInfo_GetIDsOfNames); + + /* test invoking a method with a [restricted] keyword */ + hr = ITypeInfo_Invoke(pTypeInfo, NULL, dispidMember, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL); + todo_wine { + ok(hr == DISP_E_MEMBERNOTFOUND, "ITypeInfo_Invoke should have returned DISP_E_MEMBERNOTFOUND instead of 0x%08x\n", hr); + } + + ITypeInfo_Release(pTypeInfo); + ITypeLib_Release(pTypeLib); +} + +static BOOL do_typelib_reg_key(GUID *uid, WORD maj, WORD min, LPCWSTR base, BOOL remove) +{ + static const WCHAR typelibW[] = {'T','y','p','e','l','i','b','\\',0}; + static const WCHAR formatW[] = {'\\','%','u','.','%','u','\\','0','\\','w','i','n','3','2',0}; + static const WCHAR format2W[] = {'%','s','_','%','u','_','%','u','.','d','l','l',0}; + WCHAR buf[128]; + HKEY hkey; + BOOL ret = TRUE; + + memcpy(buf, typelibW, sizeof(typelibW)); + StringFromGUID2(uid, buf + lstrlenW(buf), 40); + + if (remove) + { + ok(SHDeleteKeyW(HKEY_CLASSES_ROOT, buf) == ERROR_SUCCESS, "SHDeleteKey failed\n"); + return TRUE; + } + + wsprintfW(buf + lstrlenW(buf), formatW, maj, min ); + + if (RegCreateKeyExW(HKEY_CLASSES_ROOT, buf, 0, NULL, 0, + KEY_WRITE, NULL, &hkey, NULL) != ERROR_SUCCESS) + { + trace("RegCreateKeyExW failed\n"); + return FALSE; + } + + wsprintfW(buf, format2W, base, maj, min); + if (RegSetValueExW(hkey, NULL, 0, REG_SZ, + (BYTE *)buf, (lstrlenW(buf) + 1) * sizeof(WCHAR)) != ERROR_SUCCESS) + { + trace("RegSetValueExW failed\n"); + ret = FALSE; + } + RegCloseKey(hkey); + return ret; +} + +static void test_QueryPathOfRegTypeLib(void) +{ + static const struct test_data + { + WORD maj, min; + HRESULT ret; + const WCHAR path[16]; + } td[] = { + { 1, 0, TYPE_E_LIBNOTREGISTERED, { 0 } }, + { 3, 0, S_OK, {'f','a','k','e','_','3','_','0','.','d','l','l',0 } }, + { 3, 1, S_OK, {'f','a','k','e','_','3','_','1','.','d','l','l',0 } }, + { 3, 22, S_OK, {'f','a','k','e','_','3','_','3','7','.','d','l','l',0 } }, + { 3, 37, S_OK, {'f','a','k','e','_','3','_','3','7','.','d','l','l',0 } }, + { 3, 40, S_OK, {'f','a','k','e','_','3','_','3','7','.','d','l','l',0 } }, + { 4, 0, TYPE_E_LIBNOTREGISTERED, { 0 } } + }; + static const WCHAR base[] = {'f','a','k','e',0}; + UINT i; + RPC_STATUS status; + GUID uid; + WCHAR uid_str[40]; + HRESULT ret; + BSTR path; + + status = UuidCreate(&uid); + ok(!status, "UuidCreate error %08lx\n", status); + + StringFromGUID2(&uid, uid_str, 40); + /*trace("GUID: %s\n", wine_dbgstr_w(uid_str));*/ + + if (!do_typelib_reg_key(&uid, 3, 0, base, 0)) return; + if (!do_typelib_reg_key(&uid, 3, 1, base, 0)) return; + if (!do_typelib_reg_key(&uid, 3, 37, base, 0)) return; + + for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + { + ret = QueryPathOfRegTypeLib(&uid, td[i].maj, td[i].min, 0, &path); + ok(ret == td[i].ret, "QueryPathOfRegTypeLib(%u.%u) returned %08x\n", td[i].maj, td[i].min, ret); + if (ret == S_OK) + { + ok(!lstrcmpW(td[i].path, path), "typelib %u.%u path doesn't match\n", td[i].maj, td[i].min); + SysFreeString(path); + } + } + + do_typelib_reg_key(&uid, 0, 0, NULL, 1); +} + +static void test_inheritance(void) +{ + HRESULT hr; + ITypeLib *pTL; + ITypeInfo *pTI, *pTI_p; + TYPEATTR *pTA; + HREFTYPE href; + FUNCDESC *pFD; + WCHAR path[MAX_PATH]; + static const WCHAR tl_path[] = {'.','\\','m','i','d','l','_','t','m','a','r','s','h','a','l','.','t','l','b',0}; + + BOOL use_midl_tlb = 0; + + GetModuleFileNameW(NULL, path, MAX_PATH); + + if(use_midl_tlb) + memcpy(path, tl_path, sizeof(tl_path)); + + hr = LoadTypeLib(path, &pTL); + if(FAILED(hr)) return; + + + /* ItestIF3 is a syntax 2 dispinterface */ + hr = ITypeLib_GetTypeInfoOfGuid(pTL, &DIID_ItestIF3, &pTI); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTI, &pTA); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTA->typekind == TKIND_DISPATCH, "kind %04x\n", pTA->typekind); + ok(pTA->cbSizeVft == 28, "sizevft %d\n", pTA->cbSizeVft); + ok(pTA->wTypeFlags == TYPEFLAG_FDISPATCHABLE, "typeflags %x\n", pTA->wTypeFlags); +if(use_midl_tlb) { + ok(pTA->cFuncs == 6, "cfuncs %d\n", pTA->cFuncs); + ok(pTA->cImplTypes == 1, "cimpltypes %d\n", pTA->cImplTypes); + ITypeInfo_ReleaseTypeAttr(pTI, pTA); + + hr = ITypeInfo_GetRefTypeOfImplType(pTI, 0, &href); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetRefTypeInfo(pTI, href, &pTI_p); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(pTI_p, &pTA); + ok(IsEqualGUID(&pTA->guid, &IID_IDispatch), "guid {%08x-....\n", pTA->guid.Data1); + ITypeInfo_ReleaseTypeAttr(pTI_p, pTA); + ITypeInfo_Release(pTI_p); + + /* Should have six methods */ + hr = ITypeInfo_GetFuncDesc(pTI, 6, &pFD); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "hr %08x\n", hr); + hr = ITypeInfo_GetFuncDesc(pTI, 5, &pFD); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFD->memid == 0x60020000, "memid %08x\n", pFD->memid); + ok(pFD->oVft == 20, "oVft %d\n", pFD->oVft); + ITypeInfo_ReleaseFuncDesc(pTI, pFD); +} + ITypeInfo_Release(pTI); + + + /* ItestIF4 is a syntax 1 dispinterface */ + hr = ITypeLib_GetTypeInfoOfGuid(pTL, &DIID_ItestIF4, &pTI); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTI, &pTA); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTA->typekind == TKIND_DISPATCH, "kind %04x\n", pTA->typekind); + ok(pTA->cbSizeVft == 28, "sizevft %d\n", pTA->cbSizeVft); + ok(pTA->wTypeFlags == TYPEFLAG_FDISPATCHABLE, "typeflags %x\n", pTA->wTypeFlags); + ok(pTA->cFuncs == 1, "cfuncs %d\n", pTA->cFuncs); + ok(pTA->cImplTypes == 1, "cimpltypes %d\n", pTA->cImplTypes); + ITypeInfo_ReleaseTypeAttr(pTI, pTA); + + hr = ITypeInfo_GetRefTypeOfImplType(pTI, 0, &href); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetRefTypeInfo(pTI, href, &pTI_p); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(pTI_p, &pTA); + ok(IsEqualGUID(&pTA->guid, &IID_IDispatch), "guid {%08x-....\n", pTA->guid.Data1); + ITypeInfo_ReleaseTypeAttr(pTI_p, pTA); + ITypeInfo_Release(pTI_p); + hr = ITypeInfo_GetFuncDesc(pTI, 1, &pFD); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "hr %08x\n", hr); + hr = ITypeInfo_GetFuncDesc(pTI, 0, &pFD); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFD->memid == 0x1c, "memid %08x\n", pFD->memid); + ITypeInfo_ReleaseFuncDesc(pTI, pFD); + ITypeInfo_Release(pTI); + + + /* ItestIF5 is dual with inherited ifaces which derive from IUnknown but not IDispatch */ + hr = ITypeLib_GetTypeInfoOfGuid(pTL, &IID_ItestIF5, &pTI); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTI, &pTA); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTA->typekind == TKIND_DISPATCH, "kind %04x\n", pTA->typekind); + ok(pTA->cbSizeVft == 28, "sizevft %d\n", pTA->cbSizeVft); +if(use_midl_tlb) { + ok(pTA->wTypeFlags == TYPEFLAG_FDUAL, "typeflags %x\n", pTA->wTypeFlags); + } + ok(pTA->cFuncs == 8, "cfuncs %d\n", pTA->cFuncs); + ok(pTA->cImplTypes == 1, "cimpltypes %d\n", pTA->cImplTypes); + ITypeInfo_ReleaseTypeAttr(pTI, pTA); + + hr = ITypeInfo_GetRefTypeOfImplType(pTI, 0, &href); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetRefTypeInfo(pTI, href, &pTI_p); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(pTI_p, &pTA); + ok(IsEqualGUID(&pTA->guid, &IID_IDispatch), "guid {%08x-....\n", pTA->guid.Data1); + ITypeInfo_ReleaseTypeAttr(pTI_p, pTA); + ITypeInfo_Release(pTI_p); +if(use_midl_tlb) { + hr = ITypeInfo_GetFuncDesc(pTI, 6, &pFD); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFD->memid == 0x1234, "memid %08x\n", pFD->memid); + ITypeInfo_ReleaseFuncDesc(pTI, pFD); +} + ITypeInfo_Release(pTI); + + /* ItestIF7 is dual with inherited ifaces which derive from Dispatch */ + hr = ITypeLib_GetTypeInfoOfGuid(pTL, &IID_ItestIF7, &pTI); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTI, &pTA); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTA->typekind == TKIND_DISPATCH, "kind %04x\n", pTA->typekind); + ok(pTA->cbSizeVft == 28, "sizevft %d\n", pTA->cbSizeVft); + ok(pTA->wTypeFlags == (TYPEFLAG_FDISPATCHABLE|TYPEFLAG_FDUAL), "typeflags %x\n", pTA->wTypeFlags); + ok(pTA->cFuncs == 10, "cfuncs %d\n", pTA->cFuncs); + ok(pTA->cImplTypes == 1, "cimpltypes %d\n", pTA->cImplTypes); + ITypeInfo_ReleaseTypeAttr(pTI, pTA); + + hr = ITypeInfo_GetRefTypeOfImplType(pTI, 0, &href); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetRefTypeInfo(pTI, href, &pTI_p); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(pTI_p, &pTA); + ok(IsEqualGUID(&pTA->guid, &IID_IDispatch), "guid {%08x-....\n", pTA->guid.Data1); + ITypeInfo_ReleaseTypeAttr(pTI_p, pTA); + ITypeInfo_Release(pTI_p); + + hr = ITypeInfo_GetFuncDesc(pTI, 9, &pFD); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFD->memid == 0x1236, "memid %08x\n", pFD->memid); + ITypeInfo_ReleaseFuncDesc(pTI, pFD); + ITypeInfo_Release(pTI); + + /* ItestIF10 is a syntax 2 dispinterface which doesn't derive from IUnknown */ + hr = ITypeLib_GetTypeInfoOfGuid(pTL, &DIID_ItestIF10, &pTI); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTI, &pTA); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTA->typekind == TKIND_DISPATCH, "kind %04x\n", pTA->typekind); + ok(pTA->cbSizeVft == 28, "sizevft %d\n", pTA->cbSizeVft); + ok(pTA->wTypeFlags == TYPEFLAG_FDISPATCHABLE, "typeflags %x\n", pTA->wTypeFlags); +if(use_midl_tlb) { + ok(pTA->cFuncs == 3, "cfuncs %d\n", pTA->cFuncs); + ok(pTA->cImplTypes == 1, "cimpltypes %d\n", pTA->cImplTypes); + ITypeInfo_ReleaseTypeAttr(pTI, pTA); + + hr = ITypeInfo_GetRefTypeOfImplType(pTI, -1, &href); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "hr %08x\n", hr); + hr = ITypeInfo_GetRefTypeOfImplType(pTI, 0, &href); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetRefTypeInfo(pTI, href, &pTI_p); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(pTI_p, &pTA); + ok(IsEqualGUID(&pTA->guid, &IID_IDispatch), "guid {%08x-....\n", pTA->guid.Data1); + ITypeInfo_ReleaseTypeAttr(pTI_p, pTA); + ITypeInfo_Release(pTI_p); + + /* Should have three methods */ + hr = ITypeInfo_GetFuncDesc(pTI, 3, &pFD); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "hr %08x\n", hr); + hr = ITypeInfo_GetFuncDesc(pTI, 2, &pFD); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFD->memid == 0x60010000, "memid %08x\n", pFD->memid); + ok(pFD->oVft == 8, "oVft %d\n", pFD->oVft); + ITypeInfo_ReleaseFuncDesc(pTI, pFD); +} + ITypeInfo_Release(pTI); + + /* ItestIF11 is a syntax 2 dispinterface which derives from IDispatch */ + hr = ITypeLib_GetTypeInfoOfGuid(pTL, &DIID_ItestIF11, &pTI); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTI, &pTA); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTA->typekind == TKIND_DISPATCH, "kind %04x\n", pTA->typekind); + ok(pTA->cbSizeVft == 28, "sizevft %d\n", pTA->cbSizeVft); + ok(pTA->wTypeFlags == TYPEFLAG_FDISPATCHABLE, "typeflags %x\n", pTA->wTypeFlags); +if(use_midl_tlb) { + ok(pTA->cFuncs == 10, "cfuncs %d\n", pTA->cFuncs); + ok(pTA->cImplTypes == 1, "cimpltypes %d\n", pTA->cImplTypes); + ITypeInfo_ReleaseTypeAttr(pTI, pTA); + + hr = ITypeInfo_GetRefTypeOfImplType(pTI, 0, &href); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetRefTypeInfo(pTI, href, &pTI_p); + ok(hr == S_OK, "hr %08x\n", hr); + hr = ITypeInfo_GetTypeAttr(pTI_p, &pTA); + ok(IsEqualGUID(&pTA->guid, &IID_IDispatch), "guid {%08x-....\n", pTA->guid.Data1); + ITypeInfo_ReleaseTypeAttr(pTI_p, pTA); + ITypeInfo_Release(pTI_p); + + /* Should have ten methods */ + hr = ITypeInfo_GetFuncDesc(pTI, 10, &pFD); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "hr %08x\n", hr); + hr = ITypeInfo_GetFuncDesc(pTI, 9, &pFD); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFD->memid == 0x1236, "memid %08x\n", pFD->memid); + ok(pFD->oVft == 36, "oVft %d\n", pFD->oVft); + ITypeInfo_ReleaseFuncDesc(pTI, pFD); +} + ITypeInfo_Release(pTI); + + + /* ItestIF2 is an interface which derives from IUnknown */ + hr = ITypeLib_GetTypeInfoOfGuid(pTL, &IID_ItestIF2, &pTI); + ok(hr == S_OK, "hr %08x\n", hr); + + hr = ITypeInfo_GetTypeAttr(pTI, &pTA); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pTA->typekind == TKIND_INTERFACE, "kind %04x\n", pTA->typekind); + ok(pTA->cbSizeVft == 24, "sizevft %d\n", pTA->cbSizeVft); + ok(pTA->wTypeFlags == 0, "typeflags %x\n", pTA->wTypeFlags); +if(use_midl_tlb) { + ok(pTA->cFuncs == 1, "cfuncs %d\n", pTA->cFuncs); + ok(pTA->cImplTypes == 1, "cimpltypes %d\n", pTA->cImplTypes); + ITypeInfo_ReleaseTypeAttr(pTI, pTA); + + /* Should have one method */ + hr = ITypeInfo_GetFuncDesc(pTI, 1, &pFD); + ok(hr == TYPE_E_ELEMENTNOTFOUND, "hr %08x\n", hr); + hr = ITypeInfo_GetFuncDesc(pTI, 0, &pFD); + ok(hr == S_OK, "hr %08x\n", hr); + ok(pFD->memid == 0x60020000, "memid %08x\n", pFD->memid); + ok(pFD->oVft == 20, "oVft %d\n", pFD->oVft); + ITypeInfo_ReleaseFuncDesc(pTI, pFD); +} + ITypeInfo_Release(pTI); + + ITypeLib_Release(pTL); + + return; +} + +#if 0 /* use this to generate more tests */ + +#define OLE_CHECK(x) { HRESULT hr = x; if (FAILED(hr)) { printf(#x "failed - %x\n", hr); return; } } + +static char *dump_string(LPWSTR wstr) +{ + int size = lstrlenW(wstr)+3; + char *out = CoTaskMemAlloc(size); + WideCharToMultiByte(20127, 0, wstr, -1, out+1, size, NULL, NULL); + out[0] = '\"'; + strcat(out, "\""); + return out; +} + +struct map_entry +{ + DWORD value; + const char *name; +}; + +#define MAP_ENTRY(x) { x, #x } +static const struct map_entry tkind_map[] = { + MAP_ENTRY(TKIND_ENUM), + MAP_ENTRY(TKIND_RECORD), + MAP_ENTRY(TKIND_MODULE), + MAP_ENTRY(TKIND_INTERFACE), + MAP_ENTRY(TKIND_DISPATCH), + MAP_ENTRY(TKIND_COCLASS), + MAP_ENTRY(TKIND_ALIAS), + MAP_ENTRY(TKIND_UNION), + MAP_ENTRY(TKIND_MAX), + {0, NULL} +}; + +static const struct map_entry funckind_map[] = { + MAP_ENTRY(FUNC_VIRTUAL), + MAP_ENTRY(FUNC_PUREVIRTUAL), + MAP_ENTRY(FUNC_NONVIRTUAL), + MAP_ENTRY(FUNC_STATIC), + MAP_ENTRY(FUNC_DISPATCH), + {0, NULL} +}; + +static const struct map_entry invkind_map[] = { + MAP_ENTRY(INVOKE_FUNC), + MAP_ENTRY(INVOKE_PROPERTYGET), + MAP_ENTRY(INVOKE_PROPERTYPUT), + MAP_ENTRY(INVOKE_PROPERTYPUTREF), + {0, NULL} +}; + +#undef MAP_ENTRY + +static const char *map_value(DWORD val, const struct map_entry *map) +{ + static int map_id; + static char bufs[16][256]; + char *buf; + + while (map->name) + { + if (map->value == val) + return map->name; + map++; + } + + buf = bufs[(map_id++)%16]; + sprintf(buf, "0x%x", val); + return buf; +} + +static void test_dump_typelib(const char *name) +{ + WCHAR wszString[260]; + ITypeInfo *info; + ITypeLib *lib; + int count; + int i; + + MultiByteToWideChar(CP_ACP, 0, name, -1, wszString, 260); + OLE_CHECK(LoadTypeLib(wszString, &lib)); + count = ITypeLib_GetTypeInfoCount(lib); + printf("/* interfaces count: %d */\n", count); + for (i = 0; i < count; i++) + { + TYPEATTR *attr; + BSTR name; + int f = 0; + + OLE_CHECK(ITypeLib_GetDocumentation(lib, i, &name, NULL, NULL, NULL)); + printf("{\n" + " %s,\n", dump_string(name)); + SysFreeString(name); + + OLE_CHECK(ITypeLib_GetTypeInfo(lib, i, &info)); + ITypeInfo_GetTypeAttr(info, &attr); + printf(" /*kind*/ %s, /*flags*/ 0x%x, /*align*/ %d, /*size*/ %d,\n" + " /*#vtbl*/ %d, /*#func*/ %d,\n" + " {\n", + map_value(attr->typekind, tkind_map), attr->wTypeFlags, attr->cbAlignment, attr->cbSizeInstance, attr->cbSizeVft, + attr->cFuncs); + ITypeInfo_ReleaseTypeAttr(info, attr); + while (1) + { + FUNCDESC *desc; + BSTR tab[256]; + UINT cNames; + int p; + + if (FAILED(ITypeInfo_GetFuncDesc(info, f, &desc))) + break; + printf(" {\n" + " 0x%x, /*func*/ %s, /*inv*/ %s, /*call*/ 0x%x,\n", + desc->memid, map_value(desc->funckind, funckind_map), map_value(desc->invkind, invkind_map), + desc->callconv); + printf(" /*#param*/ %d, /*#opt*/ %d, /*vtbl*/ %d, /*#scodes*/ %d, /*flags*/ 0x%x,\n", + desc->cParams, desc->cParamsOpt, desc->oVft, desc->cScodes, desc->wFuncFlags); + printf(" {%d, %x}, /* ret */\n", desc->elemdescFunc.tdesc.vt, desc->elemdescFunc.paramdesc.wParamFlags); + printf(" { /* params */\n"); + for (p = 0; p < desc->cParams; p++) + { + ELEMDESC e = desc->lprgelemdescParam[p]; + printf(" {%d, %x},\n", e.tdesc.vt, e.paramdesc.wParamFlags); + } + printf(" {-1, -1}\n"); + printf(" },\n"); + printf(" { /* names */\n"); + OLE_CHECK(ITypeInfo_GetNames(info, desc->memid, tab, 256, &cNames)); + for (p = 0; p < cNames; p++) + { + printf(" %s,\n", dump_string(tab[p])); + SysFreeString(tab[p]); + } + printf(" NULL,\n"); + printf(" },\n"); + printf(" },\n"); + ITypeInfo_ReleaseFuncDesc(info, desc); + f++; + } + printf(" }\n"); + printf("},\n"); + ITypeInfo_Release(info); + } + ITypeLib_Release(lib); +} + +#else + +typedef struct _element_info +{ + VARTYPE vt; + USHORT wParamFlags; +} element_info; + +typedef struct _function_info +{ + MEMBERID memid; + FUNCKIND funckind; + INVOKEKIND invkind; + CALLCONV callconv; + short cParams; + short cParamsOpt; + short oVft; + short cScodes; + WORD wFuncFlags; + element_info ret_type; + element_info params[15]; + LPCSTR names[15]; +} function_info; + +typedef struct _interface_info +{ + LPCSTR name; + TYPEKIND type; + WORD wTypeFlags; + USHORT cbAlignment; + USHORT cbSizeInstance; + USHORT cbSizeVft; + USHORT cFuncs; + function_info funcs[20]; +} interface_info; + +static const interface_info info[] = { +/* interfaces count: 2 */ +{ + "IDualIface", + /*kind*/ TKIND_DISPATCH, /*flags*/ 0x1040, /*align*/ 4, /*size*/ 4, + /*#vtbl*/ 28, /*#func*/ 8, + { + { + 0x60000000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 2, /*#opt*/ 0, /*vtbl*/ 0, /*#scodes*/ 0, /*flags*/ 0x1, + {24, 0}, /* ret */ + { /* params */ + {26, 1}, + {26, 2}, + {-1, -1} + }, + { /* names */ + "QueryInterface", + "riid", + "ppvObj", + NULL, + }, + }, + { + 0x60000001, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 4, /*#scodes*/ 0, /*flags*/ 0x1, + {19, 0}, /* ret */ + { /* params */ + {-1, -1} + }, + { /* names */ + "AddRef", + NULL, + }, + }, + { + 0x60000002, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 8, /*#scodes*/ 0, /*flags*/ 0x1, + {19, 0}, /* ret */ + { /* params */ + {-1, -1} + }, + { /* names */ + "Release", + NULL, + }, + }, + { + 0x60010000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 1, /*#opt*/ 0, /*vtbl*/ 12, /*#scodes*/ 0, /*flags*/ 0x1, + {24, 0}, /* ret */ + { /* params */ + {26, 2}, + {-1, -1} + }, + { /* names */ + "GetTypeInfoCount", + "pctinfo", + NULL, + }, + }, + { + 0x60010001, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 3, /*#opt*/ 0, /*vtbl*/ 16, /*#scodes*/ 0, /*flags*/ 0x1, + {24, 0}, /* ret */ + { /* params */ + {23, 1}, + {19, 1}, + {26, 2}, + {-1, -1} + }, + { /* names */ + "GetTypeInfo", + "itinfo", + "lcid", + "pptinfo", + NULL, + }, + }, + { + 0x60010002, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 5, /*#opt*/ 0, /*vtbl*/ 20, /*#scodes*/ 0, /*flags*/ 0x1, + {24, 0}, /* ret */ + { /* params */ + {26, 1}, + {26, 1}, + {23, 1}, + {19, 1}, + {26, 2}, + {-1, -1} + }, + { /* names */ + "GetIDsOfNames", + "riid", + "rgszNames", + "cNames", + "lcid", + "rgdispid", + NULL, + }, + }, + { + 0x60010003, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 8, /*#opt*/ 0, /*vtbl*/ 24, /*#scodes*/ 0, /*flags*/ 0x1, + {24, 0}, /* ret */ + { /* params */ + {3, 1}, + {26, 1}, + {19, 1}, + {18, 1}, + {26, 1}, + {26, 2}, + {26, 2}, + {26, 2}, + {-1, -1} + }, + { /* names */ + "Invoke", + "dispidMember", + "riid", + "lcid", + "wFlags", + "pdispparams", + "pvarResult", + "pexcepinfo", + "puArgErr", + NULL, + }, + }, + { + 0x60020000, /*func*/ FUNC_DISPATCH, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 28, /*#scodes*/ 0, /*flags*/ 0x0, + {24, 0}, /* ret */ + { /* params */ + {-1, -1} + }, + { /* names */ + "Test", + NULL, + }, + }, + } +}, +{ + "ISimpleIface", + /*kind*/ TKIND_INTERFACE, /*flags*/ 0x1000, /*align*/ 4, /*size*/ 4, + /*#vtbl*/ 32, /*#func*/ 1, + { + { + 0x60020000, /*func*/ FUNC_PUREVIRTUAL, /*inv*/ INVOKE_FUNC, /*call*/ 0x4, + /*#param*/ 0, /*#opt*/ 0, /*vtbl*/ 28, /*#scodes*/ 0, /*flags*/ 0x0, + {25, 0}, /* ret */ + { /* params */ + {-1, -1} + }, + { /* names */ + "Test", + NULL, + }, + }, + } +}, +}; + +#define check_type(elem, info) { \ + expect_int((elem)->tdesc.vt, (info)->vt); \ + expect_hex(U(*(elem)).paramdesc.wParamFlags, (info)->wParamFlags); \ + } + +static void test_dump_typelib(const char *name) +{ + WCHAR wszName[MAX_PATH]; + ITypeLib *typelib; + int ifcount = sizeof(info)/sizeof(info[0]); + int iface, func; + + MultiByteToWideChar(CP_UTF8, 0, name, -1, wszName, MAX_PATH); + ole_check(LoadTypeLibEx(wszName, REGKIND_NONE, &typelib)); + expect_eq(ITypeLib_GetTypeInfoCount(typelib), ifcount, UINT, "%d"); + for (iface = 0; iface < ifcount; iface++) + { + const interface_info *if_info = &info[iface]; + ITypeInfo *typeinfo; + TYPEATTR *typeattr; + BSTR bstrIfName; + + trace("Interface %s\n", if_info->name); + ole_check(ITypeLib_GetTypeInfo(typelib, iface, &typeinfo)); + ole_check(ITypeLib_GetDocumentation(typelib, iface, &bstrIfName, NULL, NULL, NULL)); + expect_wstr_utf8val(bstrIfName, if_info->name); + SysFreeString(bstrIfName); + + ole_check(ITypeInfo_GetTypeAttr(typeinfo, &typeattr)); + expect_int(typeattr->typekind, if_info->type); + expect_hex(typeattr->wTypeFlags, if_info->wTypeFlags); + expect_int(typeattr->cbAlignment, if_info->cbAlignment); + expect_int(typeattr->cbSizeInstance, if_info->cbSizeInstance); + expect_int(typeattr->cbSizeVft, if_info->cbSizeVft); + expect_int(typeattr->cFuncs, if_info->cFuncs); + + for (func = 0; func < typeattr->cFuncs; func++) + { + function_info *fn_info = (function_info *)&if_info->funcs[func]; + FUNCDESC *desc; + BSTR namesTab[256]; + UINT cNames; + int i; + + trace("Function %s\n", fn_info->names[0]); + ole_check(ITypeInfo_GetFuncDesc(typeinfo, func, &desc)); + expect_int(desc->memid, fn_info->memid); + expect_int(desc->funckind, fn_info->funckind); + expect_int(desc->invkind, fn_info->invkind); + expect_int(desc->callconv, fn_info->callconv); + expect_int(desc->cParams, fn_info->cParams); + expect_int(desc->cParamsOpt, fn_info->cParamsOpt); + expect_int(desc->oVft, fn_info->oVft); + expect_int(desc->cScodes, fn_info->cScodes); + expect_int(desc->wFuncFlags, fn_info->wFuncFlags); + ole_check(ITypeInfo_GetNames(typeinfo, desc->memid, namesTab, 256, &cNames)); + for (i = 0; i < cNames; i++) + { + expect_wstr_utf8val(namesTab[i], fn_info->names[i]); + SysFreeString(namesTab[i]); + } + expect_null(fn_info->names[cNames]); + + check_type(&desc->elemdescFunc, &fn_info->ret_type); + for (i = 0 ; i < desc->cParams; i++) + { + check_type(&desc->lprgelemdescParam[i], &fn_info->params[i]); + } + expect_int(fn_info->params[desc->cParams].vt, (VARTYPE)-1); + + ITypeInfo_ReleaseFuncDesc(typeinfo, desc); + } + + ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr); + ITypeInfo_Release(typeinfo); + } + ITypeLib_Release(typelib); +} + +#endif + +START_TEST(typelib) +{ + ref_count_test(wszStdOle2); + test_TypeComp(); + test_CreateDispTypeInfo(); + test_TypeInfo(); + test_QueryPathOfRegTypeLib(); + test_inheritance(); + test_dump_typelib("test_tlb.tlb"); +} diff --git a/rostests/winetests/oleaut32/usrmarshal.c b/rostests/winetests/oleaut32/usrmarshal.c new file mode 100644 index 00000000000..633a382fba1 --- /dev/null +++ b/rostests/winetests/oleaut32/usrmarshal.c @@ -0,0 +1,1282 @@ +/* + * Marshaling Tests + * + * Copyright 2004 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "propidl.h" /* for LPSAFEARRAY_User* routines */ + +#include "wine/test.h" + +#if (__STDC__ && !defined(_FORCENAMELESSUNION)) || defined(NONAMELESSUNION) +# define V_U2(A) ((A)->n1.n2) +#else +# define V_U2(A) (*(A)) +#endif + +/* doesn't work on Windows due to needing more of the + * MIDL_STUB_MESSAGE structure to be filled out */ +#define LPSAFEARRAY_UNMARSHAL_WORKS 0 +#define BSTR_UNMARSHAL_WORKS 0 +#define VARIANT_UNMARSHAL_WORKS 1 + +static inline SF_TYPE get_union_type(SAFEARRAY *psa) +{ + VARTYPE vt; + HRESULT hr; + + hr = SafeArrayGetVartype(psa, &vt); + if (FAILED(hr)) + { + if(psa->fFeatures & FADF_VARIANT) return SF_VARIANT; + + switch(psa->cbElements) + { + case 1: vt = VT_I1; break; + case 2: vt = VT_I2; break; + case 4: vt = VT_I4; break; + case 8: vt = VT_I8; break; + default: return 0; + } + } + + if (psa->fFeatures & FADF_HAVEIID) + return SF_HAVEIID; + + switch (vt) + { + case VT_I1: + case VT_UI1: return SF_I1; + case VT_BOOL: + case VT_I2: + case VT_UI2: return SF_I2; + case VT_INT: + case VT_UINT: + case VT_I4: + case VT_UI4: + case VT_R4: return SF_I4; + case VT_DATE: + case VT_CY: + case VT_R8: + case VT_I8: + case VT_UI8: return SF_I8; + case VT_INT_PTR: + case VT_UINT_PTR: return (sizeof(UINT_PTR) == 4 ? SF_I4 : SF_I8); + case VT_BSTR: return SF_BSTR; + case VT_DISPATCH: return SF_DISPATCH; + case VT_VARIANT: return SF_VARIANT; + case VT_UNKNOWN: return SF_UNKNOWN; + /* Note: Return a non-zero size to indicate vt is valid. The actual size + * of a UDT is taken from the result of IRecordInfo_GetSize(). + */ + case VT_RECORD: return SF_RECORD; + default: return SF_ERROR; + } +} + +static ULONG get_cell_count(const SAFEARRAY *psa) +{ + const SAFEARRAYBOUND* psab = psa->rgsabound; + USHORT cCount = psa->cDims; + ULONG ulNumCells = 1; + + while (cCount--) + { + if (!psab->cElements) + return 0; + ulNumCells *= psab->cElements; + psab++; + } + return ulNumCells; +} + +static void check_safearray(void *buffer, LPSAFEARRAY lpsa) +{ + unsigned char *wiresa = buffer; + VARTYPE vt; + SF_TYPE sftype; + ULONG cell_count; + + if(!lpsa) + { + ok(*(void **)wiresa == NULL, "wiresa + 0x0 should be NULL instead of 0x%08x\n", *(DWORD *)wiresa); + return; + } + + if(FAILED(SafeArrayGetVartype(lpsa, &vt))) + vt = 0; + + sftype = get_union_type(lpsa); + cell_count = get_cell_count(lpsa); + + ok(*(DWORD *)wiresa, "wiresa + 0x0 should be non-NULL instead of 0x%08x\n", *(DWORD *)wiresa); /* win2k: this is lpsa. winxp: this is 0x00000001 */ + wiresa += sizeof(DWORD); + ok(*(DWORD *)wiresa == lpsa->cDims, "wiresa + 0x4 should be lpsa->cDims instead of 0x%08x\n", *(DWORD *)wiresa); + wiresa += sizeof(DWORD); + ok(*(WORD *)wiresa == lpsa->cDims, "wiresa + 0x8 should be lpsa->cDims instead of 0x%04x\n", *(WORD *)wiresa); + wiresa += sizeof(WORD); + ok(*(WORD *)wiresa == lpsa->fFeatures, "wiresa + 0xc should be lpsa->fFeatures instead of 0x%08x\n", *(WORD *)wiresa); + wiresa += sizeof(WORD); + ok(*(DWORD *)wiresa == lpsa->cbElements, "wiresa + 0x10 should be lpsa->cbElements instead of 0x%08x\n", *(DWORD *)wiresa); + wiresa += sizeof(DWORD); + ok(*(WORD *)wiresa == lpsa->cLocks, "wiresa + 0x16 should be lpsa->cLocks instead of 0x%04x\n", *(WORD *)wiresa); + wiresa += sizeof(WORD); + ok(*(WORD *)wiresa == vt, "wiresa + 0x14 should be %04x instead of 0x%04x\n", vt, *(WORD *)wiresa); + wiresa += sizeof(WORD); + ok(*(DWORD *)wiresa == sftype, "wiresa + 0x18 should be %08x instead of 0x%08x\n", (DWORD)sftype, *(DWORD *)wiresa); + wiresa += sizeof(DWORD); + ok(*(DWORD *)wiresa == cell_count, "wiresa + 0x1c should be %u instead of %u\n", cell_count, *(DWORD *)wiresa); + wiresa += sizeof(DWORD); + ok(*(DWORD_PTR *)wiresa == (DWORD_PTR)lpsa->pvData, "wiresa + 0x20 should be lpsa->pvData instead of 0x%08lx\n", *(DWORD_PTR *)wiresa); + wiresa += sizeof(DWORD_PTR); + if(sftype == SF_HAVEIID) + { + GUID guid; + SafeArrayGetIID(lpsa, &guid); + ok(IsEqualGUID(&guid, (GUID*)wiresa), "guid mismatch\n"); + wiresa += sizeof(GUID); + } + ok(!memcmp(wiresa, lpsa->rgsabound, sizeof(lpsa->rgsabound[0]) * lpsa->cDims), "bounds mismatch\n"); + wiresa += sizeof(lpsa->rgsabound[0]) * lpsa->cDims; + + ok(*(DWORD *)wiresa == cell_count, "wiresa + 0x2c should be %u instead of %u\n", cell_count, *(DWORD*)wiresa); + wiresa += sizeof(DWORD); + /* elements are now pointed to by wiresa */ +} + +static void test_marshal_LPSAFEARRAY(void) +{ + unsigned char *buffer; + unsigned long size; + LPSAFEARRAY lpsa; + LPSAFEARRAY lpsa2 = NULL; + SAFEARRAYBOUND sab; + MIDL_STUB_MESSAGE stubMsg = { 0 }; + USER_MARSHAL_CB umcb = { 0 }; + HRESULT hr; + VARTYPE vt; + + umcb.Flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); + umcb.pReserve = NULL; + umcb.pStubMsg = &stubMsg; + + sab.lLbound = 5; + sab.cElements = 10; + + lpsa = SafeArrayCreate(VT_I2, 1, &sab); + *(DWORD *)lpsa->pvData = 0xcafebabe; + + lpsa->cLocks = 7; + size = LPSAFEARRAY_UserSize(&umcb.Flags, 1, &lpsa); + ok(size == 68, "size should be 68 bytes, not %ld\n", size); + size = LPSAFEARRAY_UserSize(&umcb.Flags, 0, &lpsa); + ok(size == 64, "size should be 64 bytes, not %ld\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + LPSAFEARRAY_UserMarshal(&umcb.Flags, buffer, &lpsa); + + check_safearray(buffer, lpsa); + + if (LPSAFEARRAY_UNMARSHAL_WORKS) + { + VARTYPE vt, vt2; + LPSAFEARRAY_UserUnmarshal(&umcb.Flags, buffer, &lpsa2); + ok(lpsa2 != NULL, "LPSAFEARRAY didn't unmarshal\n"); + SafeArrayGetVartype(lpsa, &vt); + SafeArrayGetVartype(lpsa2, &vt2); + ok(vt == vt2, "vts differ %x %x\n", vt, vt2); + LPSAFEARRAY_UserFree(&umcb.Flags, &lpsa2); + } + HeapFree(GetProcessHeap(), 0, buffer); + SafeArrayDestroy(lpsa); + + /* test NULL safe array */ + lpsa = NULL; + + size = LPSAFEARRAY_UserSize(&umcb.Flags, 0, &lpsa); + ok(size == 4, "size should be 4 bytes, not %ld\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + LPSAFEARRAY_UserMarshal(&umcb.Flags, buffer, &lpsa); + check_safearray(buffer, lpsa); + + if (LPSAFEARRAY_UNMARSHAL_WORKS) + { + LPSAFEARRAY_UserUnmarshal(&umcb.Flags, buffer, &lpsa2); + ok(lpsa2 == NULL, "NULL LPSAFEARRAY didn't unmarshal\n"); + LPSAFEARRAY_UserFree(&umcb.Flags, &lpsa2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + sab.lLbound = 5; + sab.cElements = 10; + + lpsa = SafeArrayCreate(VT_R8, 1, &sab); + *(double *)lpsa->pvData = 3.1415; + + lpsa->cLocks = 7; + size = LPSAFEARRAY_UserSize(&umcb.Flags, 1, &lpsa); + ok(size == 128, "size should be 128 bytes, not %ld\n", size); + size = LPSAFEARRAY_UserSize(&umcb.Flags, 0, &lpsa); + ok(size == 128, "size should be 128 bytes, not %ld\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + LPSAFEARRAY_UserMarshal(&umcb.Flags, buffer, &lpsa); + + check_safearray(buffer, lpsa); + + HeapFree(GetProcessHeap(), 0, buffer); + SafeArrayDestroy(lpsa); + + /* VARTYPE-less arrays can be marshaled if cbElements is 1,2,4 or 8 as type SF_In */ + hr = SafeArrayAllocDescriptor(1, &lpsa); + ok(hr == S_OK, "saad failed %08x\n", hr); + lpsa->cbElements = 8; + lpsa->rgsabound[0].lLbound = 2; + lpsa->rgsabound[0].cElements = 48; + hr = SafeArrayAllocData(lpsa); + ok(hr == S_OK, "saad failed %08x\n", hr); + + hr = SafeArrayGetVartype(lpsa, &vt); + ok(hr == E_INVALIDARG, "ret %08x\n", hr); + + size = LPSAFEARRAY_UserSize(&umcb.Flags, 0, &lpsa); + ok(size == 432, "size %ld\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + LPSAFEARRAY_UserMarshal(&umcb.Flags, buffer, &lpsa); + check_safearray(buffer, lpsa); + HeapFree(GetProcessHeap(), 0, buffer); + SafeArrayDestroyData(lpsa); + SafeArrayDestroyDescriptor(lpsa); + + /* VARTYPE-less arrays with FADF_VARIANT */ + hr = SafeArrayAllocDescriptor(1, &lpsa); + ok(hr == S_OK, "saad failed %08x\n", hr); + lpsa->cbElements = 16; + lpsa->fFeatures = FADF_VARIANT; + lpsa->rgsabound[0].lLbound = 2; + lpsa->rgsabound[0].cElements = 48; + hr = SafeArrayAllocData(lpsa); + ok(hr == S_OK, "saad failed %08x\n", hr); + + hr = SafeArrayGetVartype(lpsa, &vt); + ok(hr == E_INVALIDARG, "ret %08x\n", hr); + + size = LPSAFEARRAY_UserSize(&umcb.Flags, 0, &lpsa); + todo_wine + ok(size == 1388, "size %ld\n", size); + buffer = HeapAlloc(GetProcessHeap(), 0, size); + LPSAFEARRAY_UserMarshal(&umcb.Flags, buffer, &lpsa); + check_safearray(buffer, lpsa); + HeapFree(GetProcessHeap(), 0, buffer); + SafeArrayDestroyData(lpsa); + SafeArrayDestroyDescriptor(lpsa); +} + +static void check_bstr(void *buffer, BSTR b) +{ + DWORD *wireb = buffer; + DWORD len = SysStringByteLen(b); + + ok(*wireb == (len + 1) / 2, "wv[0] %08x\n", *wireb); + wireb++; + if(b) + ok(*wireb == len, "wv[1] %08x\n", *wireb); + else + ok(*wireb == 0xffffffff, "wv[1] %08x\n", *wireb); + wireb++; + ok(*wireb == (len + 1) / 2, "wv[2] %08x\n", *wireb); + if(len) + { + wireb++; + ok(!memcmp(wireb, b, (len + 1) & ~1), "strings differ\n"); + } + return; +} + +static void test_marshal_BSTR(void) +{ + unsigned long size; + MIDL_STUB_MESSAGE stubMsg = { 0 }; + USER_MARSHAL_CB umcb = { 0 }; + unsigned char *buffer, *next; + BSTR b, b2; + WCHAR str[] = {'m','a','r','s','h','a','l',' ','t','e','s','t','1',0}; + DWORD len; + + umcb.Flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); + umcb.pReserve = NULL; + umcb.pStubMsg = &stubMsg; + + b = SysAllocString(str); + len = SysStringLen(b); + ok(len == 13, "get %d\n", len); + + /* BSTRs are DWORD aligned */ + size = BSTR_UserSize(&umcb.Flags, 1, &b); + ok(size == 42, "size %ld\n", size); + + size = BSTR_UserSize(&umcb.Flags, 0, &b); + ok(size == 38, "size %ld\n", size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + next = BSTR_UserMarshal(&umcb.Flags, buffer, &b); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + check_bstr(buffer, b); + + if (BSTR_UNMARSHAL_WORKS) + { + b2 = NULL; + next = BSTR_UserUnmarshal(&umcb.Flags, buffer, &b2); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + ok(b2 != NULL, "BSTR didn't unmarshal\n"); + ok(!memcmp(b, b2, (len + 1) * 2), "strings differ\n"); + BSTR_UserFree(&umcb.Flags, &b2); + } + + HeapFree(GetProcessHeap(), 0, buffer); + SysFreeString(b); + + b = NULL; + size = BSTR_UserSize(&umcb.Flags, 0, &b); + ok(size == 12, "size %ld\n", size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + next = BSTR_UserMarshal(&umcb.Flags, buffer, &b); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + + check_bstr(buffer, b); + if (BSTR_UNMARSHAL_WORKS) + { + b2 = NULL; + next = BSTR_UserUnmarshal(&umcb.Flags, buffer, &b2); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + ok(b2 == NULL, "NULL BSTR didn't unmarshal\n"); + BSTR_UserFree(&umcb.Flags, &b2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + b = SysAllocStringByteLen("abc", 3); + *(((char*)b) + 3) = 'd'; + len = SysStringLen(b); + ok(len == 1, "get %d\n", len); + len = SysStringByteLen(b); + ok(len == 3, "get %d\n", len); + + size = BSTR_UserSize(&umcb.Flags, 0, &b); + ok(size == 16, "size %ld\n", size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + memset(buffer, 0xcc, size); + next = BSTR_UserMarshal(&umcb.Flags, buffer, &b); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + check_bstr(buffer, b); + ok(buffer[15] == 'd', "buffer[15] %02x\n", buffer[15]); + + if (BSTR_UNMARSHAL_WORKS) + { + b2 = NULL; + next = BSTR_UserUnmarshal(&umcb.Flags, buffer, &b2); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + ok(b2 != NULL, "BSTR didn't unmarshal\n"); + ok(!memcmp(b, b2, len), "strings differ\n"); + BSTR_UserFree(&umcb.Flags, &b2); + } + HeapFree(GetProcessHeap(), 0, buffer); + SysFreeString(b); + + b = SysAllocStringByteLen("", 0); + len = SysStringLen(b); + ok(len == 0, "get %d\n", len); + len = SysStringByteLen(b); + ok(len == 0, "get %d\n", len); + + size = BSTR_UserSize(&umcb.Flags, 0, &b); + ok(size == 12, "size %ld\n", size); + + buffer = HeapAlloc(GetProcessHeap(), 0, size); + next = BSTR_UserMarshal(&umcb.Flags, buffer, &b); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + check_bstr(buffer, b); + + if (BSTR_UNMARSHAL_WORKS) + { + b2 = NULL; + next = BSTR_UserUnmarshal(&umcb.Flags, buffer, &b2); + ok(next == buffer + size, "got %p expect %p\n", next, buffer + size); + ok(b2 != NULL, "NULL LPSAFEARRAY didn't unmarshal\n"); + len = SysStringByteLen(b2); + ok(len == 0, "byte len %d\n", len); + BSTR_UserFree(&umcb.Flags, &b2); + } + HeapFree(GetProcessHeap(), 0, buffer); + SysFreeString(b); +} + +typedef struct +{ + const IUnknownVtbl *lpVtbl; + ULONG refs; +} HeapUnknown; + +static HRESULT WINAPI HeapUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + IUnknown_AddRef(iface); + *ppv = (LPVOID)iface; + return S_OK; + } + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI HeapUnknown_AddRef(IUnknown *iface) +{ + HeapUnknown *This = (HeapUnknown *)iface; + return InterlockedIncrement((LONG*)&This->refs); +} + +static ULONG WINAPI HeapUnknown_Release(IUnknown *iface) +{ + HeapUnknown *This = (HeapUnknown *)iface; + ULONG refs = InterlockedDecrement((LONG*)&This->refs); + if (!refs) HeapFree(GetProcessHeap(), 0, This); + return refs; +} + +static const IUnknownVtbl HeapUnknown_Vtbl = +{ + HeapUnknown_QueryInterface, + HeapUnknown_AddRef, + HeapUnknown_Release +}; + +static void check_variant_header(DWORD *wirev, VARIANT *v, unsigned long size) +{ + WORD *wp; + DWORD switch_is; + + ok(*wirev == (size + 7) >> 3, "wv[0] %08x, expected %08lx\n", *wirev, (size + 7) >> 3); + wirev++; + ok(*wirev == 0, "wv[1] %08x\n", *wirev); + wirev++; + wp = (WORD*)wirev; + ok(*wp == V_VT(v), "vt %04x expected %04x\n", *wp, V_VT(v)); + wp++; + ok(*wp == V_U2(v).wReserved1, "res1 %04x expected %04x\n", *wp, V_U2(v).wReserved1); + wp++; + ok(*wp == V_U2(v).wReserved2, "res2 %04x expected %04x\n", *wp, V_U2(v).wReserved2); + wp++; + ok(*wp == V_U2(v).wReserved3, "res3 %04x expected %04x\n", *wp, V_U2(v).wReserved3); + wp++; + wirev = (DWORD*)wp; + switch_is = V_VT(v); + if(switch_is & VT_ARRAY) + switch_is &= ~VT_TYPEMASK; + ok(*wirev == switch_is, "switch_is %08x expected %08x\n", *wirev, switch_is); +} + +static void test_marshal_VARIANT(void) +{ + VARIANT v, v2; + MIDL_STUB_MESSAGE stubMsg = { 0 }; + RPC_MESSAGE rpcMsg = { 0 }; + USER_MARSHAL_CB umcb = { 0 }; + unsigned char *buffer, *next; + ULONG ul; + short s; + double d; + DWORD *wirev; + BSTR b; + WCHAR str[] = {'m','a','r','s','h','a','l',' ','t','e','s','t',0}; + SAFEARRAYBOUND sab; + LPSAFEARRAY lpsa; + DECIMAL dec, dec2; + HeapUnknown *heap_unknown; + + stubMsg.RpcMsg = &rpcMsg; + + umcb.Flags = MAKELONG(MSHCTX_DIFFERENTMACHINE, NDR_LOCAL_DATA_REPRESENTATION); + umcb.pStubMsg = &stubMsg; + umcb.pReserve = NULL; + umcb.Signature = USER_MARSHAL_CB_SIGNATURE; + umcb.CBType = USER_MARSHAL_CB_UNMARSHALL; + + /*** I1 ***/ + VariantInit(&v); + V_VT(&v) = VT_I1; + V_I1(&v) = 0x12; + + /* Variants have an alignment of 8 */ + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 1, &v); + ok(stubMsg.BufferLength == 29, "size %d\n", stubMsg.BufferLength); + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 21, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*(char*)wirev == V_I1(&v), "wv[5] %08x\n", *wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(V_I1(&v) == V_I1(&v2), "got i1 %x expect %x\n", V_I1(&v), V_I1(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** I2 ***/ + VariantInit(&v); + V_VT(&v) = VT_I2; + V_I2(&v) = 0x1234; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 22, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*(short*)wirev == V_I2(&v), "wv[5] %08x\n", *wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(V_I2(&v) == V_I2(&v2), "got i2 %x expect %x\n", V_I2(&v), V_I2(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** I2 BYREF ***/ + VariantInit(&v); + V_VT(&v) = VT_I2 | VT_BYREF; + s = 0x1234; + V_I2REF(&v) = &s; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 26, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 0x4, "wv[5] %08x\n", *wirev); + wirev++; + ok(*(short*)wirev == s, "wv[6] %08x\n", *wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(*V_I2REF(&v) == *V_I2REF(&v2), "got i2 ref %x expect ui4 ref %x\n", *V_I2REF(&v), *V_I2REF(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** I4 ***/ + VariantInit(&v); + V_VT(&v) = VT_I4; + V_I4(&v) = 0x1234; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 24, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == V_I4(&v), "wv[5] %08x\n", *wirev); + + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(V_I4(&v) == V_I4(&v2), "got i4 %x expect %x\n", V_I4(&v), V_I4(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + + HeapFree(GetProcessHeap(), 0, buffer); + + /*** UI4 ***/ + VariantInit(&v); + V_VT(&v) = VT_UI4; + V_UI4(&v) = 0x1234; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 24, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 0x1234, "wv[5] %08x\n", *wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(V_UI4(&v) == V_UI4(&v2), "got ui4 %x expect %x\n", V_UI4(&v), V_UI4(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + + HeapFree(GetProcessHeap(), 0, buffer); + + /*** UI4 BYREF ***/ + VariantInit(&v); + V_VT(&v) = VT_UI4 | VT_BYREF; + ul = 0x1234; + V_UI4REF(&v) = &ul; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 28, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 0x4, "wv[5] %08x\n", *wirev); + wirev++; + ok(*wirev == ul, "wv[6] %08x\n", *wirev); + + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(*V_UI4REF(&v) == *V_UI4REF(&v2), "got ui4 ref %x expect ui4 ref %x\n", *V_UI4REF(&v), *V_UI4REF(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** R4 ***/ + VariantInit(&v); + V_VT(&v) = VT_R4; + V_R8(&v) = 3.1415; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 24, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*(float*)wirev == V_R4(&v), "wv[5] %08x\n", *wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(V_R4(&v) == V_R4(&v2), "got r4 %f expect %f\n", V_R4(&v), V_R4(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** R8 ***/ + VariantInit(&v); + V_VT(&v) = VT_R8; + V_R8(&v) = 3.1415; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 32, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + memset(buffer, 0xcc, stubMsg.BufferLength); + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 0xcccccccc, "wv[5] %08x\n", *wirev); /* pad */ + wirev++; + ok(*(double*)wirev == V_R8(&v), "wv[6] %08x, wv[7] %08x\n", *wirev, *(wirev+1)); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(V_R8(&v) == V_R8(&v2), "got r8 %f expect %f\n", V_R8(&v), V_R8(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** R8 BYREF ***/ + VariantInit(&v); + V_VT(&v) = VT_R8 | VT_BYREF; + d = 3.1415; + V_R8REF(&v) = &d; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 32, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 8, "wv[5] %08x\n", *wirev); + wirev++; + ok(*(double*)wirev == d, "wv[6] %08x wv[7] %08x\n", *wirev, *(wirev+1)); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(*V_R8REF(&v) == *V_R8REF(&v2), "got r8 ref %f expect %f\n", *V_R8REF(&v), *V_R8REF(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** VARIANT_BOOL ***/ + VariantInit(&v); + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = 0x1234; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 22, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*(short*)wirev == V_BOOL(&v), "wv[5] %04x\n", *(WORD*)wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(V_BOOL(&v) == V_BOOL(&v2), "got bool %x expect %x\n", V_BOOL(&v), V_BOOL(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** DECIMAL ***/ + VarDecFromI4(0x12345678, &dec); + VariantInit(&v); + V_DECIMAL(&v) = dec; + V_VT(&v) = VT_DECIMAL; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 40, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + memset(buffer, 0xcc, stubMsg.BufferLength); + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 0xcccccccc, "wirev[5] %08x\n", *wirev); /* pad */ + wirev++; + dec2 = dec; + dec2.wReserved = VT_DECIMAL; + ok(!memcmp(wirev, &dec2, sizeof(dec2)), "wirev[6] %08x wirev[7] %08x wirev[8] %08x wirev[9] %08x\n", + *wirev, *(wirev + 1), *(wirev + 2), *(wirev + 3)); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(!memcmp(&V_DECIMAL(&v), & V_DECIMAL(&v2), sizeof(DECIMAL)), "decimals differ\n"); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** DECIMAL BYREF ***/ + VariantInit(&v); + V_VT(&v) = VT_DECIMAL | VT_BYREF; + V_DECIMALREF(&v) = &dec; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 40, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 16, "wv[5] %08x\n", *wirev); + wirev++; + ok(!memcmp(wirev, &dec, sizeof(dec)), "wirev[6] %08x wirev[7] %08x wirev[8] %08x wirev[9] %08x\n", *wirev, *(wirev + 1), *(wirev + 2), *(wirev + 3)); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(!memcmp(V_DECIMALREF(&v), V_DECIMALREF(&v2), sizeof(DECIMAL)), "decimals differ\n"); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** EMPTY ***/ + VariantInit(&v); + V_VT(&v) = VT_EMPTY; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 20, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** NULL ***/ + VariantInit(&v); + V_VT(&v) = VT_NULL; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 20, "size %d\n", stubMsg.BufferLength); + + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** BSTR ***/ + b = SysAllocString(str); + VariantInit(&v); + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = b; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 60, "size %d\n", stubMsg.BufferLength); + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev, "wv[5] %08x\n", *wirev); /* win2k: this is b. winxp: this is (char*)b + 1 */ + wirev++; + check_bstr(wirev, V_BSTR(&v)); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(SysStringByteLen(V_BSTR(&v)) == SysStringByteLen(V_BSTR(&v2)), "bstr string lens differ\n"); + ok(!memcmp(V_BSTR(&v), V_BSTR(&v2), SysStringByteLen(V_BSTR(&v))), "bstrs differ\n"); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** BSTR BYREF ***/ + VariantInit(&v); + V_VT(&v) = VT_BSTR | VT_BYREF; + V_BSTRREF(&v) = &b; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 64, "size %d\n", stubMsg.BufferLength); + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 0x4, "wv[5] %08x\n", *wirev); + wirev++; + ok(*wirev, "wv[6] %08x\n", *wirev); /* win2k: this is b. winxp: this is (char*)b + 1 */ + wirev++; + check_bstr(wirev, b); + if (VARIANT_UNMARSHAL_WORKS) + { + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(SysStringByteLen(*V_BSTRREF(&v)) == SysStringByteLen(*V_BSTRREF(&v2)), "bstr string lens differ\n"); + ok(!memcmp(*V_BSTRREF(&v), *V_BSTRREF(&v2), SysStringByteLen(*V_BSTRREF(&v))), "bstrs differ\n"); + + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + SysFreeString(b); + + /*** ARRAY ***/ + sab.lLbound = 5; + sab.cElements = 10; + + lpsa = SafeArrayCreate(VT_R8, 1, &sab); + *(DWORD *)lpsa->pvData = 0xcafebabe; + *((DWORD *)lpsa->pvData + 1) = 0xdeadbeef; + lpsa->cLocks = 7; + + VariantInit(&v); + V_VT(&v) = VT_UI4 | VT_ARRAY; + V_ARRAY(&v) = lpsa; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 152, "size %d\n", stubMsg.BufferLength); + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev, "wv[5] %08x\n", *wirev); /* win2k: this is lpsa. winxp: this is (char*)lpsa + 1 */ + wirev++; + check_safearray(wirev, lpsa); + if (VARIANT_UNMARSHAL_WORKS) + { + LONG bound, bound2; + VARTYPE vt, vt2; + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(SafeArrayGetDim(V_ARRAY(&v)) == SafeArrayGetDim(V_ARRAY(&v)), "array dims differ\n"); + SafeArrayGetLBound(V_ARRAY(&v), 1, &bound); + SafeArrayGetLBound(V_ARRAY(&v2), 1, &bound2); + ok(bound == bound2, "array lbounds differ\n"); + SafeArrayGetUBound(V_ARRAY(&v), 1, &bound); + SafeArrayGetUBound(V_ARRAY(&v2), 1, &bound2); + ok(bound == bound2, "array ubounds differ\n"); + SafeArrayGetVartype(V_ARRAY(&v), &vt); + SafeArrayGetVartype(V_ARRAY(&v2), &vt2); + ok(vt == vt2, "array vts differ %x %x\n", vt, vt2); + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** ARRAY BYREF ***/ + VariantInit(&v); + V_VT(&v) = VT_UI4 | VT_ARRAY | VT_BYREF; + V_ARRAYREF(&v) = &lpsa; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 152, "size %d\n", stubMsg.BufferLength); + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + ok(*wirev == 4, "wv[5] %08x\n", *wirev); + wirev++; + ok(*wirev, "wv[6] %08x\n", *wirev); /* win2k: this is lpsa. winxp: this is (char*)lpsa + 1 */ + wirev++; + check_safearray(wirev, lpsa); + if (VARIANT_UNMARSHAL_WORKS) + { + LONG bound, bound2; + VARTYPE vt, vt2; + VariantInit(&v2); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v2); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v2), "got vt %d expect %d\n", V_VT(&v), V_VT(&v2)); + ok(SafeArrayGetDim(*V_ARRAYREF(&v)) == SafeArrayGetDim(*V_ARRAYREF(&v)), "array dims differ\n"); + SafeArrayGetLBound(*V_ARRAYREF(&v), 1, &bound); + SafeArrayGetLBound(*V_ARRAYREF(&v2), 1, &bound2); + ok(bound == bound2, "array lbounds differ\n"); + SafeArrayGetUBound(*V_ARRAYREF(&v), 1, &bound); + SafeArrayGetUBound(*V_ARRAYREF(&v2), 1, &bound2); + ok(bound == bound2, "array ubounds differ\n"); + SafeArrayGetVartype(*V_ARRAYREF(&v), &vt); + SafeArrayGetVartype(*V_ARRAYREF(&v2), &vt2); + ok(vt == vt2, "array vts differ %x %x\n", vt, vt2); + VARIANT_UserFree(&umcb.Flags, &v2); + } + HeapFree(GetProcessHeap(), 0, buffer); + SafeArrayDestroy(lpsa); + + /*** VARIANT BYREF ***/ + VariantInit(&v); + VariantInit(&v2); + V_VT(&v2) = VT_R8; + V_R8(&v2) = 3.1415; + V_VT(&v) = VT_VARIANT | VT_BYREF; + V_VARIANTREF(&v) = &v2; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength == 64, "size %d\n", stubMsg.BufferLength); + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + memset(buffer, 0xcc, stubMsg.BufferLength); + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + wirev = (DWORD*)buffer; + check_variant_header(wirev, &v, stubMsg.BufferLength); + wirev += 5; + + ok(*wirev == 16, "wv[5] %08x\n", *wirev); + wirev++; + ok(*wirev == ('U' | 's' << 8 | 'e' << 16 | 'r' << 24), "wv[6] %08x\n", *wirev); /* 'User' */ + wirev++; + ok(*wirev == 0xcccccccc, "wv[7] %08x\n", *wirev); /* pad */ + wirev++; + check_variant_header(wirev, &v2, stubMsg.BufferLength - 32); + wirev += 5; + ok(*wirev == 0xcccccccc, "wv[13] %08x\n", *wirev); /* pad for VT_R8 */ + wirev++; + ok(*(double*)wirev == V_R8(&v2), "wv[6] %08x wv[7] %08x\n", *wirev, *(wirev+1)); + if (VARIANT_UNMARSHAL_WORKS) + { + VARIANT v3; + VariantInit(&v3); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v3); + ok(next == buffer + stubMsg.BufferLength, "got %p expect %p\n", next, buffer + stubMsg.BufferLength); + ok(V_VT(&v) == V_VT(&v3), "got vt %d expect %d\n", V_VT(&v), V_VT(&v3)); + ok(V_VT(V_VARIANTREF(&v)) == V_VT(V_VARIANTREF(&v3)), "vts differ %x %x\n", + V_VT(V_VARIANTREF(&v)), V_VT(V_VARIANTREF(&v3))); + ok(V_R8(V_VARIANTREF(&v)) == V_R8(V_VARIANTREF(&v3)), "r8s differ\n"); + VARIANT_UserFree(&umcb.Flags, &v3); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** UNKNOWN ***/ + heap_unknown = HeapAlloc(GetProcessHeap(), 0, sizeof(*heap_unknown)); + heap_unknown->lpVtbl = &HeapUnknown_Vtbl; + heap_unknown->refs = 1; + VariantInit(&v); + VariantInit(&v2); + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = (IUnknown *)heap_unknown; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength > 32, "size %d\n", stubMsg.BufferLength); + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + memset(buffer, 0xcc, stubMsg.BufferLength); + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + wirev = (DWORD*)buffer; + check_variant_header(wirev, &v, next - buffer); + wirev += 5; + + todo_wine + ok(*wirev == (DWORD_PTR)V_UNKNOWN(&v) /* Win9x */ || + *wirev == (DWORD_PTR)V_UNKNOWN(&v) + 1 /* NT */, "wv[5] %08x\n", *wirev); + wirev++; + todo_wine + ok(*wirev == next - buffer - 0x20, "wv[6] %08x\n", *wirev); + wirev++; + todo_wine + ok(*wirev == next - buffer - 0x20, "wv[7] %08x\n", *wirev); + wirev++; + todo_wine + ok(*wirev == 0x574f454d, "wv[8] %08x\n", *wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VARIANT v3; + VariantInit(&v3); + V_VT(&v3) = VT_UNKNOWN; + V_UNKNOWN(&v3) = (IUnknown *)heap_unknown; + IUnknown_AddRef(V_UNKNOWN(&v3)); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v3); + ok(V_VT(&v) == V_VT(&v3), "got vt %d expect %d\n", V_VT(&v), V_VT(&v3)); + ok(V_VT(V_VARIANTREF(&v)) == V_VT(V_VARIANTREF(&v3)), "vts differ %x %x\n", + V_VT(V_VARIANTREF(&v)), V_VT(V_VARIANTREF(&v3))); + ok(V_R8(V_VARIANTREF(&v)) == V_R8(V_VARIANTREF(&v3)), "r8s differ\n"); + VARIANT_UserFree(&umcb.Flags, &v3); + ok(heap_unknown->refs == 1, "%d refcounts of IUnknown leaked\n", heap_unknown->refs - 1); + IUnknown_Release((IUnknown *)heap_unknown); + } + HeapFree(GetProcessHeap(), 0, buffer); + + /*** UNKNOWN BYREF ***/ + heap_unknown = HeapAlloc(GetProcessHeap(), 0, sizeof(*heap_unknown)); + heap_unknown->lpVtbl = &HeapUnknown_Vtbl; + heap_unknown->refs = 1; + VariantInit(&v); + VariantInit(&v2); + V_VT(&v) = VT_UNKNOWN | VT_BYREF; + V_UNKNOWNREF(&v) = (IUnknown **)&heap_unknown; + + rpcMsg.BufferLength = stubMsg.BufferLength = VARIANT_UserSize(&umcb.Flags, 0, &v); + ok(stubMsg.BufferLength > 36, "size %d\n", stubMsg.BufferLength); + buffer = rpcMsg.Buffer = stubMsg.Buffer = stubMsg.BufferStart = HeapAlloc(GetProcessHeap(), 0, stubMsg.BufferLength); + stubMsg.BufferEnd = stubMsg.Buffer + stubMsg.BufferLength; + memset(buffer, 0xcc, stubMsg.BufferLength); + next = VARIANT_UserMarshal(&umcb.Flags, buffer, &v); + wirev = (DWORD*)buffer; + check_variant_header(wirev, &v, next - buffer); + wirev += 5; + + ok(*wirev == 4, "wv[5] %08x\n", *wirev); + wirev++; + todo_wine + ok(*wirev == (DWORD_PTR)heap_unknown /* Win9x, Win2000 */ || + *wirev == (DWORD_PTR)heap_unknown + 1 /* XP */, "wv[6] %08x\n", *wirev); + wirev++; + todo_wine + ok(*wirev == next - buffer - 0x24, "wv[7] %08x\n", *wirev); + wirev++; + todo_wine + ok(*wirev == next - buffer - 0x24, "wv[8] %08x\n", *wirev); + wirev++; + todo_wine + ok(*wirev == 0x574f454d, "wv[9] %08x\n", *wirev); + if (VARIANT_UNMARSHAL_WORKS) + { + VARIANT v3; + VariantInit(&v3); + V_VT(&v3) = VT_UNKNOWN; + V_UNKNOWN(&v3) = (IUnknown *)heap_unknown; + IUnknown_AddRef(V_UNKNOWN(&v3)); + stubMsg.Buffer = buffer; + next = VARIANT_UserUnmarshal(&umcb.Flags, buffer, &v3); + ok(V_VT(&v) == V_VT(&v3), "got vt %d expect %d\n", V_VT(&v), V_VT(&v3)); + ok(*V_UNKNOWNREF(&v) == *V_UNKNOWNREF(&v3), "got %p expect %p\n", *V_UNKNOWNREF(&v), *V_UNKNOWNREF(&v3)); + VARIANT_UserFree(&umcb.Flags, &v3); + ok(heap_unknown->refs == 1, "%d refcounts of IUnknown leaked\n", heap_unknown->refs - 1); + IUnknown_Release((IUnknown *)heap_unknown); + } + HeapFree(GetProcessHeap(), 0, buffer); +} + + +START_TEST(usrmarshal) +{ + CoInitialize(NULL); + + test_marshal_LPSAFEARRAY(); + test_marshal_BSTR(); + test_marshal_VARIANT(); + + CoUninitialize(); +} diff --git a/rostests/winetests/oleaut32/varformat.c b/rostests/winetests/oleaut32/varformat.c new file mode 100644 index 00000000000..f7c7c28bd8d --- /dev/null +++ b/rostests/winetests/oleaut32/varformat.c @@ -0,0 +1,401 @@ +/* + * VARFORMAT test program + * + * Copyright 1998 Jean-Claude Cote + * Copyright 2006 Google (Benjamin Arai) + * + * 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 +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winsock.h" +#include "wine/test.h" +#include "winuser.h" +#include "wingdi.h" +#include "winnls.h" +#include "winerror.h" +#include "winnt.h" + +#include "wtypes.h" +#include "oleauto.h" + +static HMODULE hOleaut32; + +static HRESULT (WINAPI *pVarFormatNumber)(LPVARIANT,int,int,int,int,ULONG,BSTR*); +static HRESULT (WINAPI *pVarFormat)(LPVARIANT,LPOLESTR,int,int,ULONG,BSTR*); + +/* Have I8/UI8 data type? */ +#define HAVE_OLEAUT32_I8 HAVE_FUNC(VarI8FromI1) + +/* Is a given function exported from oleaut32? */ +#define HAVE_FUNC(func) ((void*)GetProcAddress(hOleaut32, #func) != NULL) + +/* Get a conversion function ptr, return if function not available */ +#define CHECKPTR(func) p##func = (void*)GetProcAddress(hOleaut32, #func); \ + if (!p##func) { trace("function " # func " not available, not testing it\n"); return; } + +static inline int strcmpW( const WCHAR *str1, const WCHAR *str2 ) +{ + while (*str1 && (*str1 == *str2)) { str1++; str2++; } + return *str1 - *str2; +} + +#define FMT_NUMBER(vt,val) \ + VariantInit(&v); V_VT(&v) = vt; val(&v) = 1; \ + hres = pVarFormatNumber(&v,2,0,0,0,0,&str); \ + ok(hres == S_OK, "VarFormatNumber (vt %d): returned %8x\n", vt, hres); \ + if (hres == S_OK) \ + ok(str && strcmpW(str,szResult1) == 0, \ + "VarFormatNumber (vt %d): string different\n", vt) + +static void test_VarFormatNumber(void) +{ + static const WCHAR szSrc1[] = { '1','\0' }; + static const WCHAR szResult1[] = { '1','.','0','0','\0' }; + static const WCHAR szSrc2[] = { '-','1','\0' }; + static const WCHAR szResult2[] = { '(','1','.','0','0',')','\0' }; + char buff[8]; + HRESULT hres; + VARIANT v; + BSTR str = NULL; + + CHECKPTR(VarFormatNumber); + + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buff, sizeof(buff)/sizeof(char)); + if (buff[0] != '.' || buff[1]) + { + skip("Skipping VarFormatNumber tests as decimal separator is '%s'\n", buff); + return; + } + + FMT_NUMBER(VT_I1, V_I1); + FMT_NUMBER(VT_UI1, V_UI1); + FMT_NUMBER(VT_I2, V_I2); + FMT_NUMBER(VT_UI2, V_UI2); + FMT_NUMBER(VT_I4, V_I4); + FMT_NUMBER(VT_UI4, V_UI4); + if (HAVE_OLEAUT32_I8) + { + FMT_NUMBER(VT_I8, V_I8); + FMT_NUMBER(VT_UI8, V_UI8); + } + FMT_NUMBER(VT_R4, V_R4); + FMT_NUMBER(VT_R8, V_R8); + FMT_NUMBER(VT_BOOL, V_BOOL); + + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = SysAllocString(szSrc1); + + hres = pVarFormatNumber(&v,2,0,0,0,0,&str); + ok(hres == S_OK, "VarFormatNumber (bstr): returned %8x\n", hres); + if (hres == S_OK) + ok(str && strcmpW(str, szResult1) == 0, "VarFormatNumber (bstr): string different\n"); + SysFreeString(V_BSTR(&v)); + SysFreeString(str); + + V_BSTR(&v) = SysAllocString(szSrc2); + hres = pVarFormatNumber(&v,2,0,-1,0,0,&str); + ok(hres == S_OK, "VarFormatNumber (bstr): returned %8x\n", hres); + if (hres == S_OK) + ok(str && strcmpW(str, szResult2) == 0, "VarFormatNumber (-bstr): string different\n"); + SysFreeString(V_BSTR(&v)); + SysFreeString(str); +} + +#define SIGNED_VTBITS (VTBIT_I1|VTBIT_I2|VTBIT_I4|VTBIT_I8|VTBIT_R4|VTBIT_R8) + +static const char *szVarFmtFail = "VT %d|0x%04x Format %s: expected 0x%08x, '%s', got 0x%08x, '%s'\n"; +#define VARFMT(vt,v,val,fmt,ret,str) do { \ + if (out) SysFreeString(out); out = NULL; \ + V_VT(&in) = (vt); v(&in) = val; \ + if (fmt) MultiByteToWideChar(CP_ACP, 0, fmt, -1, buffW, sizeof(buffW)/sizeof(WCHAR)); \ + hres = pVarFormat(&in,fmt ? buffW : NULL,fd,fw,flags,&out); \ + if (SUCCEEDED(hres)) WideCharToMultiByte(CP_ACP, 0, out, -1, buff, sizeof(buff),0,0); \ + else buff[0] = '\0'; \ + ok(hres == ret && (FAILED(ret) || !strcmp(buff, str)), \ + szVarFmtFail, \ + (vt)&VT_TYPEMASK,(vt)&~VT_TYPEMASK,fmt?fmt:"",ret,str,hres,buff); \ + } while(0) + +typedef struct tagFMTRES +{ + LPCSTR fmt; + LPCSTR one_res; + LPCSTR zero_res; +} FMTRES; + +static const FMTRES VarFormat_results[] = +{ + { NULL, "1", "0" }, + { "", "1", "0" }, + { "General Number", "1", "0" }, + { "Percent", "100.00%", "0.00%" }, + { "Standard", "1.00", "0.00" }, + { "Scientific","1.00E+00", "0.00E+00" }, + { "True/False", "True", "False" }, + { "On/Off", "On", "Off" }, + { "Yes/No", "Yes", "No" }, + { "#", "1", "" }, + { "##", "1", "" }, + { "#.#", "1.", "." }, + { "0", "1", "0" }, + { "00", "01", "00" }, + { "0.0", "1.0", "0.0" }, + { "00\\c\\o\\p\\y", "01copy","00copy" }, + { "\"pos\";\"neg\"", "pos", "pos" }, + { "\"pos\";\"neg\";\"zero\"","pos", "zero" } +}; + +typedef struct tagFMTDATERES +{ + DATE val; + LPCSTR fmt; + LPCSTR res; +} FMTDATERES; + +static const FMTDATERES VarFormat_date_results[] = +{ + { 0.0, "w", "7" }, + { 0.0, "w", "6" }, + { 0.0, "w", "5" }, + { 0.0, "w", "4" }, + { 0.0, "w", "3" }, + { 0.0, "w", "2" }, + { 0.0, "w", "1" }, /* First 7 entries must remain in this order! */ + { 2.525, "am/pm", "pm" }, + { 2.525, "AM/PM", "PM" }, + { 2.525, "A/P", "P" }, + { 2.525, "a/p", "p" }, + { 2.525, "q", "1" }, + { 2.525, "d", "1" }, + { 2.525, "dd", "01" }, + { 2.525, "ddd", "Mon" }, + { 2.525, "dddd", "Monday" }, + { 2.525, "mmm", "Jan" }, + { 2.525, "mmmm", "January" }, + { 2.525, "y", "1" }, + { 2.525, "yy", "00" }, + { 2.525, "yyy", "001" }, + { 2.525, "yyyy", "1900" }, + { 2.525, "dd mm yyyy hh:mm:ss", "01 01 1900 12:36:00" }, + { 2.525, "dd mm yyyy mm", "01 01 1900 01" }, + { 2.525, "dd mm yyyy :mm", "01 01 1900 :01" }, + { 2.525, "dd mm yyyy hh:mm", "01 01 1900 12:36" }, + { 2.525, "mm mm", "01 01" }, + { 2.525, "mm :mm:ss", "01 :01:00" }, + { 2.525, "mm :ss:mm", "01 :00:01" }, + { 2.525, "hh:mm :ss:mm", "12:36 :00:01" }, + { 2.525, "hh:dd :mm:mm", "12:01 :01:01" }, + { 2.525, "dd:hh :mm:mm", "01:12 :36:01" }, + { 2.525, "hh :mm:mm", "12 :36:01" }, + { 2.525, "dd :mm:mm", "01 :01:01" }, + { 2.525, "dd :mm:nn", "01 :01:36" }, + { 2.725, "hh:nn:ss A/P", "05:24:00 P" } +}; + +#define VNUMFMT(vt,v) \ + for (i = 0; i < sizeof(VarFormat_results)/sizeof(FMTRES); i++) \ + { \ + VARFMT(vt,v,1,VarFormat_results[i].fmt,S_OK,VarFormat_results[i].one_res); \ + VARFMT(vt,v,0,VarFormat_results[i].fmt,S_OK,VarFormat_results[i].zero_res); \ + } \ + if ((1 << vt) & SIGNED_VTBITS) \ + { \ + VARFMT(vt,v,-1,"\"pos\";\"neg\"",S_OK,"neg"); \ + VARFMT(vt,v,-1,"\"pos\";\"neg\";\"zero\"",S_OK,"neg"); \ + } + +static void test_VarFormat(void) +{ + static const WCHAR szTesting[] = { 't','e','s','t','i','n','g','\0' }; + size_t i; + WCHAR buffW[256]; + char buff[256]; + VARIANT in; + VARIANT_BOOL bTrue = VARIANT_TRUE, bFalse = VARIANT_FALSE; + int fd = 0, fw = 0; + ULONG flags = 0; + BSTR bstrin, out = NULL; + HRESULT hres; + + CHECKPTR(VarFormat); + + if (PRIMARYLANGID(LANGIDFROMLCID(GetUserDefaultLCID())) != LANG_ENGLISH) + { + skip("Skipping VarFormat tests for non english language\n"); + return; + } + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buff, sizeof(buff)/sizeof(char)); + if (buff[0] != '.' || buff[1]) + { + skip("Skipping VarFormat tests as decimal separator is '%s'\n", buff); + return; + } + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, buff, sizeof(buff)/sizeof(char)); + if (buff[0] != '2' || buff[1]) + { + skip("Skipping VarFormat tests as decimal places is '%s'\n", buff); + return; + } + + VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"True/False",S_OK,"True"); + VARFMT(VT_BOOL,V_BOOL,VARIANT_FALSE,"True/False",S_OK,"False"); + + VNUMFMT(VT_I1,V_I1); + VNUMFMT(VT_I2,V_I2); + VNUMFMT(VT_I4,V_I4); + if (HAVE_OLEAUT32_I8) + { + VNUMFMT(VT_I8,V_I8); + } + VNUMFMT(VT_INT,V_INT); + VNUMFMT(VT_UI1,V_UI1); + VNUMFMT(VT_UI2,V_UI2); + VNUMFMT(VT_UI4,V_UI4); + if (HAVE_OLEAUT32_I8) + { + VNUMFMT(VT_UI8,V_UI8); + } + VNUMFMT(VT_UINT,V_UINT); + VNUMFMT(VT_R4,V_R4); + VNUMFMT(VT_R8,V_R8); + + /* Reference types are dereferenced */ + VARFMT(VT_BOOL|VT_BYREF,V_BOOLREF,&bTrue,"True/False",S_OK,"True"); + VARFMT(VT_BOOL|VT_BYREF,V_BOOLREF,&bFalse,"True/False",S_OK,"False"); + + /* Dates */ + for (i = 0; i < sizeof(VarFormat_date_results)/sizeof(FMTDATERES); i++) + { + if (i < 7) + fd = i + 1; /* Test first day */ + else + fd = 0; + VARFMT(VT_DATE,V_DATE,VarFormat_date_results[i].val, + VarFormat_date_results[i].fmt,S_OK, + VarFormat_date_results[i].res); + } + + /* Strings */ + bstrin = SysAllocString(szTesting); + VARFMT(VT_BSTR,V_BSTR,bstrin,"",S_OK,"testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"@",S_OK,"testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"&",S_OK,"testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"\\x@\\x@",S_OK,"xtxesting"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"\\x&\\x&",S_OK,"xtxesting"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"@\\x",S_OK,"txesting"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"@@@@@@@@",S_OK," testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"@\\x@@@@@@@",S_OK," xtesting"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"&&&&&&&&",S_OK,"testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"!&&&&&&&",S_OK,"testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"&&&&&&&!",S_OK,"testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,">&&",S_OK,"TESTING"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"<&&",S_OK,"testing"); + VARFMT(VT_BSTR,V_BSTR,bstrin,"<&>&",S_OK,"testing"); + SysFreeString(bstrin); + /* Numeric values are converted to strings then output */ + VARFMT(VT_I1,V_I1,1,"<&>&",S_OK,"1"); + + /* Number formats */ + VARFMT(VT_I4,V_I4,1,"#00000000",S_OK,"00000001"); + VARFMT(VT_I4,V_I4,1,"000###",S_OK,"000001"); + VARFMT(VT_I4,V_I4,1,"#00##00#0",S_OK,"00000001"); + VARFMT(VT_I4,V_I4,1,"1#####0000",S_OK,"10001"); + todo_wine { + VARFMT(VT_I4,V_I4,100000,"#,###,###,###",S_OK,"100,000"); + } + VARFMT(VT_R8,V_R8,1.23456789,"0#.0#0#0#0#0",S_OK,"01.234567890"); + VARFMT(VT_R8,V_R8,1.2,"0#.0#0#0#0#0",S_OK,"01.200000000"); + VARFMT(VT_R8,V_R8,9.87654321,"#0.#0#0#0#0#",S_OK,"9.87654321"); + VARFMT(VT_R8,V_R8,9.8,"#0.#0#0#0#0#",S_OK,"9.80000000"); + VARFMT(VT_R8,V_R8,0.00000008,"#0.#0#0#0#0#0",S_OK,"0.0000000800"); + VARFMT(VT_R8,V_R8,0.00010705,"#0.##########",S_OK,"0.00010705"); + VARFMT(VT_I4,V_I4,17,"#0",S_OK,"17"); + VARFMT(VT_I4,V_I4,4711,"#0",S_OK,"4711"); + VARFMT(VT_I4,V_I4,17,"#00",S_OK,"17"); + VARFMT(VT_I4,V_I4,100,"0##",S_OK,"100"); + VARFMT(VT_I4,V_I4,17,"#000",S_OK,"017"); + VARFMT(VT_I4,V_I4,17,"#0.00",S_OK,"17.00"); + VARFMT(VT_I4,V_I4,17,"#0000.00",S_OK,"0017.00"); + VARFMT(VT_I4,V_I4,17,"#.00",S_OK,"17.00"); + VARFMT(VT_R8,V_R8,1.7,"#.00",S_OK,"1.70"); + VARFMT(VT_R8,V_R8,.17,"#.00",S_OK,".17"); + VARFMT(VT_I4,V_I4,17,"#3",S_OK,"173"); + VARFMT(VT_I4,V_I4,17,"#33",S_OK,"1733"); + VARFMT(VT_I4,V_I4,17,"#3.33",S_OK,"173.33"); + VARFMT(VT_I4,V_I4,17,"#3333.33",S_OK,"173333.33"); + VARFMT(VT_I4,V_I4,17,"#.33",S_OK,"17.33"); + VARFMT(VT_R8,V_R8,.17,"#.33",S_OK,".33"); + VARFMT(VT_R8,V_R8,1.7,"0.0000E-000",S_OK,"1.7000E000"); + VARFMT(VT_R8,V_R8,1.7,"0.0000e-1",S_OK,"1.7000e01"); + VARFMT(VT_R8,V_R8,86.936849,"#0.000000000000e-000",S_OK,"86.936849000000e000"); + VARFMT(VT_R8,V_R8,1.7,"#0",S_OK,"2"); + VARFMT(VT_R8,V_R8,1.7,"#.33",S_OK,"2.33"); + VARFMT(VT_R8,V_R8,1.7,"#3",S_OK,"23"); + VARFMT(VT_R8,V_R8,1.73245,"0.0000E+000",S_OK,"1.7325E+000"); + VARFMT(VT_R8,V_R8,9.9999999,"#0.000000",S_OK,"10.000000"); + VARFMT(VT_R8,V_R8,1.7,"0.0000e+0#",S_OK,"1.7000e+0"); + VARFMT(VT_R8,V_R8,100.0001e+0,"0.0000E+0",S_OK,"1.0000E+2"); + VARFMT(VT_R8,V_R8,1000001,"0.0000e+1",S_OK,"1.0000e+61"); + VARFMT(VT_R8,V_R8,100.0001e+25,"0.0000e+0",S_OK,"1.0000e+27"); + VARFMT(VT_R8,V_R8,450.0001e+43,"#000.0000e+0",S_OK,"4500.0010e+42"); + VARFMT(VT_R8,V_R8,0.0001e-11,"##00.0000e-0",S_OK,"1000.0000e-18"); + VARFMT(VT_R8,V_R8,0.0317e-11,"0000.0000e-0",S_OK,"3170.0000e-16"); + VARFMT(VT_R8,V_R8,0.0021e-11,"00##.0000e-0",S_OK,"2100.0000e-17"); + VARFMT(VT_R8,V_R8,1.0001e-27,"##00.0000e-0",S_OK,"1000.1000e-30"); + VARFMT(VT_R8,V_R8,47.11,".0000E+0",S_OK,".4711E+2"); + VARFMT(VT_R8,V_R8,3.0401e-13,"#####.####e-0%",S_OK,"30401.e-15%"); + + + /* 'out' is not cleared */ + out = (BSTR)0x1; + pVarFormat(&in,NULL,fd,fw,flags,&out); /* Would crash if out is cleared */ + out = NULL; + + /* VT_NULL */ + V_VT(&in) = VT_NULL; + hres = pVarFormat(&in,NULL,fd,fw,0,&out); + ok(hres == S_OK, "VarFormat failed with 0x%08x\n", hres); + ok(out == NULL, "expected NULL formatted string\n"); + + /* Invalid args */ + hres = pVarFormat(&in,NULL,fd,fw,flags,NULL); + ok(hres == E_INVALIDARG, "Null out: expected E_INVALIDARG, got 0x%08x\n", hres); + hres = pVarFormat(NULL,NULL,fd,fw,flags,&out); + ok(hres == E_INVALIDARG, "Null in: expected E_INVALIDARG, got 0x%08x\n", hres); + fd = -1; + VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,""); + fd = 8; + VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,""); + fd = 0; fw = -1; + VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,""); + fw = 4; + VARFMT(VT_BOOL,V_BOOL,VARIANT_TRUE,"",E_INVALIDARG,""); +} + +START_TEST(varformat) +{ + hOleaut32 = GetModuleHandleA("oleaut32.dll"); + + test_VarFormatNumber(); + test_VarFormat(); +} diff --git a/rostests/winetests/oleaut32/vartest.c b/rostests/winetests/oleaut32/vartest.c new file mode 100644 index 00000000000..fd95361b4e8 --- /dev/null +++ b/rostests/winetests/oleaut32/vartest.c @@ -0,0 +1,8442 @@ +/* + * VARIANT test program + * + * Copyright 1998 Jean-Claude Cote + * Copyright 2006 Google (Benjamin Arai) + * + * 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 +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winsock.h" +#include "wine/test.h" +#include "winuser.h" +#include "wingdi.h" +#include "winnls.h" +#include "winerror.h" +#include "winnt.h" + +#include "wtypes.h" +#include "oleauto.h" + +static HMODULE hOleaut32; + +static HRESULT (WINAPI *pVarUdateFromDate)(DATE,ULONG,UDATE*); +static HRESULT (WINAPI *pVarDateFromUdate)(UDATE*,ULONG,DATE*); +static INT (WINAPI *pSystemTimeToVariantTime)(LPSYSTEMTIME,double*); +static INT (WINAPI *pVariantTimeToSystemTime)(double,LPSYSTEMTIME); +static INT (WINAPI *pDosDateTimeToVariantTime)(USHORT,USHORT,double*); +static INT (WINAPI *pVariantTimeToDosDateTime)(double,USHORT*,USHORT *); + +/* Get a conversion function ptr, return if function not available */ +#define CHECKPTR(func) p##func = (void*)GetProcAddress(hOleaut32, #func); \ + if (!p##func) { trace("function " # func " not available, not testing it\n"); return; } + +/* Have IRecordInfo data type? */ +static int HAVE_OLEAUT32_RECORD = 0; +/* Have I8/UI8 data type? */ +static int HAVE_OLEAUT32_I8 = 0; +/* Is this an ancient version with support for only I2/I4/R4/R8/DATE? */ +static int IS_ANCIENT = 0; + +/* When comparing floating point values we cannot expect an exact match + * because the rounding errors depend on the exact algorithm. + */ +#define EQ_DOUBLE(a,b) (fabs((a)-(b)) / (1.0+fabs(a)+fabs(b)) < 1e-14) +#define EQ_FLOAT(a,b) (fabs((a)-(b)) / (1.0+fabs(a)+fabs(b)) < 1e-7) + +#define SKIPTESTS(a) if((a > VT_CLSID+10) && (a < VT_BSTR_BLOB-10)) continue + +/* Allow our test macros to work for VT_NULL and VT_EMPTY too */ +#define V_EMPTY(v) V_I4(v) +#define V_NULL(v) V_I4(v) + +/* Size constraints for overflow tests */ +#define I1_MAX 0x7f +#define I1_MIN ((-I1_MAX)-1) +#define UI1_MAX 0xff +#define UI1_MIN 0 +#define I2_MAX 0x7fff +#define I2_MIN ((-I2_MAX)-1) +#define UI2_MAX 0xffff +#define UI2_MIN 0 +#define I4_MAX 0x7fffffff +#define I4_MIN ((-I4_MAX)-1) +#define UI4_MAX 0xffffffff +#define UI4_MIN 0 +#define I8_MAX (((LONGLONG)I4_MAX << 32) | UI4_MAX) +#define I8_MIN ((-I8_MAX)-1) +#define UI8_MAX (((ULONGLONG)UI4_MAX << 32) | UI4_MAX) +#define UI8_MIN 0 +#define DATE_MAX 2958465 +#define DATE_MIN -657434 +#define R4_MAX FLT_MAX +#define R4_MIN FLT_MIN +#define R8_MAX DBL_MAX +#define R8_MIN DBL_MIN + +static void init(void) +{ + hOleaut32 = GetModuleHandle("oleaut32.dll"); + + /* Is a given function exported from oleaut32? */ +#define HAVE_FUNC(func) ((void*)GetProcAddress(hOleaut32, #func) != NULL) + + HAVE_OLEAUT32_I8 = HAVE_FUNC(VarI8FromI1); + if (!HAVE_OLEAUT32_I8) + skip("No support for I8 and UI8 data types\n"); + + HAVE_OLEAUT32_RECORD = HAVE_FUNC(SafeArraySetRecordInfo); + IS_ANCIENT = (!HAVE_FUNC(VarI1FromI2)); + +#undef HAVE_FUNC +} + +/* Functions to set a DECIMAL */ +static void setdec(DECIMAL* dec, BYTE scl, BYTE sgn, ULONG hi32, ULONG64 lo64) +{ + S(U(*dec)).scale = scl; + S(U(*dec)).sign = sgn; + dec->Hi32 = hi32; + U1(*dec).Lo64 = lo64; +} + +static void setdec64(DECIMAL* dec, BYTE scl, BYTE sgn, ULONG hi32, ULONG mid32, ULONG lo32) +{ + S(U(*dec)).scale = scl; + S(U(*dec)).sign = sgn; + dec->Hi32 = hi32; + S1(U1(*dec)).Mid32 = mid32; + S1(U1(*dec)).Lo32 = lo32; +} + +static inline int strcmpW( const WCHAR *str1, const WCHAR *str2 ) +{ + while (*str1 && (*str1 == *str2)) { str1++; str2++; } + return *str1 - *str2; +} + +/* return the string text of a given variant type */ +static char vtstr_buffer[16][256]; +static int vtstr_current=0; +static const char *vtstr(int x) +{ + switch(x) { +#define CASE(vt) case VT_##vt: return #vt + CASE(EMPTY); + CASE(NULL); + CASE(I2); + CASE(I4); + CASE(R4); + CASE(R8); + CASE(CY); + CASE(DATE); + CASE(BSTR); + CASE(DISPATCH); + CASE(ERROR); + CASE(BOOL); + CASE(VARIANT); + CASE(UNKNOWN); + CASE(DECIMAL); + CASE(I1); + CASE(UI1); + CASE(UI2); + CASE(UI4); + CASE(I8); + CASE(UI8); + CASE(INT); + CASE(UINT); + CASE(VOID); + CASE(HRESULT); + CASE(PTR); + CASE(SAFEARRAY); + CASE(CARRAY); + CASE(USERDEFINED); + CASE(LPSTR); + CASE(LPWSTR); + CASE(RECORD); + CASE(INT_PTR); + CASE(UINT_PTR); + CASE(FILETIME); + CASE(BLOB); + CASE(STREAM); + CASE(STORAGE); + CASE(STREAMED_OBJECT); + CASE(STORED_OBJECT); + CASE(BLOB_OBJECT); + CASE(CF); + CASE(CLSID); + CASE(VERSIONED_STREAM); + CASE(VECTOR); + CASE(ARRAY); + CASE(BYREF); + CASE(RESERVED); + CASE(ILLEGAL); +#undef CASE + + case 0xfff: + return "VT_BSTR_BLOB/VT_ILLEGALMASKED/VT_TYPEMASK"; + + default: + vtstr_current %= sizeof(vtstr_buffer)/sizeof(*vtstr_buffer); + sprintf(vtstr_buffer[vtstr_current], "unknown variant type %d", x); + return vtstr_buffer[vtstr_current++]; + } +} + +static const char *variantstr( const VARIANT *var ) +{ + vtstr_current %= sizeof(vtstr_buffer)/sizeof(*vtstr_buffer); + switch(V_VT(var)) + { + case VT_I1: + sprintf( vtstr_buffer[vtstr_current], "VT_I1(%d)", V_I1(var) ); break; + case VT_I2: + sprintf( vtstr_buffer[vtstr_current], "VT_I2(%d)", V_I2(var) ); break; + case VT_I4: + sprintf( vtstr_buffer[vtstr_current], "VT_I4(%d)", V_I4(var) ); break; + case VT_INT: + sprintf( vtstr_buffer[vtstr_current], "VT_INT(%d)", V_INT(var) ); break; + case VT_I8: + sprintf( vtstr_buffer[vtstr_current], "VT_I8(%x%08x)", (UINT)(V_I8(var) >> 32), (UINT)V_I8(var) ); break; + case VT_UI8: + sprintf( vtstr_buffer[vtstr_current], "VT_UI8(%x%08x)", (UINT)(V_UI8(var) >> 32), (UINT)V_UI8(var) ); break; + case VT_R4: + sprintf( vtstr_buffer[vtstr_current], "VT_R4(%g)", V_R4(var) ); break; + case VT_R8: + sprintf( vtstr_buffer[vtstr_current], "VT_R8(%g)", V_R8(var) ); break; + case VT_UI1: + sprintf( vtstr_buffer[vtstr_current], "VT_UI1(%u)", V_UI1(var) ); break; + case VT_UI2: + sprintf( vtstr_buffer[vtstr_current], "VT_UI2(%u)", V_UI2(var) ); break; + case VT_UI4: + sprintf( vtstr_buffer[vtstr_current], "VT_UI4(%u)", V_UI4(var) ); break; + case VT_UINT: + sprintf( vtstr_buffer[vtstr_current], "VT_UINT(%d)", V_UINT(var) ); break; + case VT_CY: + sprintf( vtstr_buffer[vtstr_current], "VT_CY(%x%08x)", S(V_CY(var)).Hi, S(V_CY(var)).Lo ); break; + case VT_DATE: + sprintf( vtstr_buffer[vtstr_current], "VT_DATE(%g)", V_DATE(var) ); break; + default: + return vtstr(V_VT(var)); + } + return vtstr_buffer[vtstr_current++]; +} + +static BOOL is_expected_variant( const VARIANT *result, const VARIANT *expected ) +{ + if (V_VT(result) != V_VT(expected)) return FALSE; + switch(V_VT(expected)) + { + case VT_EMPTY: + case VT_NULL: + return TRUE; + +#define CASE(vt) case VT_##vt: return (V_##vt(result) == V_##vt(expected)) + CASE(BOOL); + CASE(I1); + CASE(UI1); + CASE(I2); + CASE(UI2); + CASE(I4); + CASE(UI4); + CASE(I8); + CASE(UI8); + CASE(INT); + CASE(UINT); +#undef CASE + + case VT_DATE: + return EQ_FLOAT(V_DATE(result), V_DATE(expected)); + case VT_R4: + return EQ_FLOAT(V_R4(result), V_R4(expected)); + case VT_R8: + return EQ_FLOAT(V_R8(result), V_R8(expected)); + case VT_CY: + return (V_CY(result).int64 == V_CY(expected).int64); + case VT_BSTR: + return !lstrcmpW( V_BSTR(result), V_BSTR(expected) ); + case VT_DECIMAL: + return !memcmp( &V_DECIMAL(result), &V_DECIMAL(expected), sizeof(DECIMAL) ); + default: + ok(0, "unhandled variant type %s\n",vtstr(V_VT(expected))); + return 0; + } +} + +static void test_var_call1( int line, HRESULT (WINAPI *func)(LPVARIANT,LPVARIANT), + VARIANT *arg, VARIANT *expected ) +{ + VARIANT old_arg = *arg; + VARIANT result; + HRESULT hres; + + memset( &result, 0, sizeof(result) ); + hres = func( arg, &result ); + ok_(__FILE__,line)( hres == S_OK, "wrong result %x\n", hres ); + if (hres == S_OK) + ok_(__FILE__,line)( is_expected_variant( &result, expected ), + "got %s expected %s\n", variantstr(&result), variantstr(expected) ); + ok_(__FILE__,line)( is_expected_variant( arg, &old_arg ), "Modified argument %s / %s\n", + variantstr(&old_arg), variantstr(arg)); +} + +static void test_var_call2( int line, HRESULT (WINAPI *func)(LPVARIANT,LPVARIANT,LPVARIANT), + VARIANT *left, VARIANT *right, VARIANT *expected ) +{ + VARIANT old_left = *left, old_right = *right; + VARIANT result; + HRESULT hres; + + memset( &result, 0, sizeof(result) ); + hres = func( left, right, &result ); + ok_(__FILE__,line)( hres == S_OK, "wrong result %x\n", hres ); + if (hres == S_OK) + ok_(__FILE__,line)( is_expected_variant( &result, expected ), + "got %s expected %s\n", variantstr(&result), variantstr(expected) ); + ok_(__FILE__,line)( is_expected_variant( left, &old_left ), "Modified left argument %s / %s\n", + variantstr(&old_left), variantstr(left)); + ok_(__FILE__,line)( is_expected_variant( right, &old_right ), "Modified right argument %s / %s\n", + variantstr(&old_right), variantstr(right)); +} + + +static void test_VariantInit(void) +{ + VARIANTARG v1, v2; + + /* Test that VariantInit() only sets the type */ + memset(&v1, -1, sizeof(v1)); + v2 = v1; + V_VT(&v2) = VT_EMPTY; + VariantInit(&v1); + ok(!memcmp(&v1, &v2, sizeof(v1)), "VariantInit() set extra fields\n"); +} + +/* All possible combinations of extra V_VT() flags */ +static const VARTYPE ExtraFlags[16] = +{ + 0, + VT_VECTOR, + VT_ARRAY, + VT_BYREF, + VT_RESERVED, + VT_VECTOR|VT_ARRAY, + VT_VECTOR|VT_BYREF, + VT_VECTOR|VT_RESERVED, + VT_VECTOR|VT_ARRAY|VT_BYREF, + VT_VECTOR|VT_ARRAY|VT_RESERVED, + VT_VECTOR|VT_BYREF|VT_RESERVED, + VT_VECTOR|VT_ARRAY|VT_BYREF|VT_RESERVED, + VT_ARRAY|VT_BYREF, + VT_ARRAY|VT_RESERVED, + VT_ARRAY|VT_BYREF|VT_RESERVED, + VT_BYREF|VT_RESERVED, +}; + +/* Determine if a vt is valid for VariantClear() */ +static int IsValidVariantClearVT(VARTYPE vt, VARTYPE extraFlags) +{ + int ret = 0; + + /* Only the following flags/types are valid */ + if ((vt <= VT_LPWSTR || vt == VT_RECORD || vt == VT_CLSID) && + vt != (VARTYPE)15 && + (vt < (VARTYPE)24 || vt > (VARTYPE)31) && + (!(extraFlags & (VT_BYREF|VT_ARRAY)) || vt > VT_NULL) && + (extraFlags == 0 || extraFlags == VT_BYREF || extraFlags == VT_ARRAY || + extraFlags == (VT_ARRAY|VT_BYREF))) + ret = 1; /* ok */ + + if ((vt == VT_RECORD && !HAVE_OLEAUT32_RECORD) || + ((vt == VT_I8 || vt == VT_UI8) && !HAVE_OLEAUT32_I8)) + ret = 0; /* Old versions of oleaut32 */ + return ret; +} + +typedef struct +{ + const IUnknownVtbl *lpVtbl; + LONG ref; + LONG events; +} test_VariantClearImpl; + +static HRESULT WINAPI VC_QueryInterface(LPUNKNOWN iface,REFIID riid,LPVOID *ppobj) +{ + test_VariantClearImpl *This = (test_VariantClearImpl *)iface; + This->events |= 0x1; + return E_NOINTERFACE; +} + +static ULONG WINAPI VC_AddRef(LPUNKNOWN iface) { + test_VariantClearImpl *This = (test_VariantClearImpl *)iface; + This->events |= 0x2; + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI VC_Release(LPUNKNOWN iface) { + test_VariantClearImpl *This = (test_VariantClearImpl *)iface; + /* static class, won't be freed */ + This->events |= 0x4; + return InterlockedDecrement(&This->ref); +} + +static const IUnknownVtbl test_VariantClear_vtbl = { + VC_QueryInterface, + VC_AddRef, + VC_Release, +}; + +static test_VariantClearImpl test_myVariantClearImpl = {&test_VariantClear_vtbl, 1, 0}; + +static void test_VariantClear(void) +{ + HRESULT hres; + VARIANTARG v; + VARIANT v2; + size_t i; + LONG i4; + IUnknown *punk; + + /* Crashes: Native does not test input for NULL, so neither does Wine */ + if (0) hres = VariantClear(NULL); + + /* Only the type field is set, to VT_EMPTY */ + V_VT(&v) = VT_UI4; + V_UI4(&v) = ~0u; + hres = VariantClear(&v); + ok((hres == S_OK && V_VT(&v) == VT_EMPTY) || + (IS_ANCIENT && hres == DISP_E_BADVARTYPE && V_VT(&v) == VT_UI4), + "VariantClear: Type set to %d, res %08x\n", V_VT(&v), hres); + ok(V_UI4(&v) == ~0u, "VariantClear: Overwrote value\n"); + + /* Test all possible V_VT values. + * Also demonstrates that null pointers in 'v' are not dereferenced. + * Individual variant tests should test VariantClear() with non-NULL values. + */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]) && !IS_ANCIENT; i++) + { + VARTYPE vt; + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT hExpected = DISP_E_BADVARTYPE; + + SKIPTESTS(vt); + + memset(&v, 0, sizeof(v)); + V_VT(&v) = vt | ExtraFlags[i]; + + hres = VariantClear(&v); + + if (IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + + ok(hres == hExpected, "VariantClear: expected 0x%X, got 0x%X for vt %d | 0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + } + } + + /* Some BYREF tests with non-NULL ptrs */ + + /* VARIANT BYREF */ + V_VT(&v2) = VT_I4; + V_I4(&v2) = 0x1234; + V_VT(&v) = VT_VARIANT | VT_BYREF; + V_VARIANTREF(&v) = &v2; + + hres = VariantClear(&v); + ok(hres == S_OK, "ret %08x\n", hres); + ok(V_VT(&v) == 0, "vt %04x\n", V_VT(&v)); + ok(V_VARIANTREF(&v) == &v2, "variant ref %p\n", V_VARIANTREF(&v2)); + ok(V_VT(&v2) == VT_I4, "vt %04x\n", V_VT(&v2)); + ok(V_I4(&v2) == 0x1234, "i4 %04x\n", V_I4(&v2)); + + /* I4 BYREF */ + i4 = 0x4321; + V_VT(&v) = VT_I4 | VT_BYREF; + V_I4REF(&v) = &i4; + + hres = VariantClear(&v); + ok(hres == S_OK, "ret %08x\n", hres); + ok(V_VT(&v) == 0, "vt %04x\n", V_VT(&v)); + ok(V_I4REF(&v) == &i4, "i4 ref %p\n", V_I4REF(&v2)); + ok(i4 == 0x4321, "i4 changed %08x\n", i4); + + + /* UNKNOWN */ + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = (IUnknown*)&test_myVariantClearImpl; + test_myVariantClearImpl.events = 0; + hres = VariantClear(&v); + ok(hres == S_OK, "ret %08x\n", hres); + ok(V_VT(&v) == 0, "vt %04x\n", V_VT(&v)); + ok(V_UNKNOWN(&v) == (IUnknown*)&test_myVariantClearImpl, "unknown %p\n", V_UNKNOWN(&v)); + /* Check that Release got called, but nothing else */ + ok(test_myVariantClearImpl.events == 0x4, "Unexpected call. events %08x\n", test_myVariantClearImpl.events); + + /* UNKNOWN BYREF */ + punk = (IUnknown*)&test_myVariantClearImpl; + V_VT(&v) = VT_UNKNOWN | VT_BYREF; + V_UNKNOWNREF(&v) = &punk; + test_myVariantClearImpl.events = 0; + hres = VariantClear(&v); + ok(hres == S_OK, "ret %08x\n", hres); + ok(V_VT(&v) == 0, "vt %04x\n", V_VT(&v)); + ok(V_UNKNOWNREF(&v) == &punk, "unknown ref %p\n", V_UNKNOWNREF(&v)); + /* Check that nothing got called */ + ok(test_myVariantClearImpl.events == 0, "Unexpected call. events %08x\n", test_myVariantClearImpl.events); + + /* DISPATCH */ + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)&test_myVariantClearImpl; + test_myVariantClearImpl.events = 0; + hres = VariantClear(&v); + ok(hres == S_OK, "ret %08x\n", hres); + ok(V_VT(&v) == 0, "vt %04x\n", V_VT(&v)); + ok(V_DISPATCH(&v) == (IDispatch*)&test_myVariantClearImpl, "dispatch %p\n", V_DISPATCH(&v)); + /* Check that Release got called, but nothing else */ + ok(test_myVariantClearImpl.events == 0x4, "Unexpected call. events %08x\n", test_myVariantClearImpl.events); + + /* DISPATCH BYREF */ + punk = (IUnknown*)&test_myVariantClearImpl; + V_VT(&v) = VT_DISPATCH | VT_BYREF; + V_DISPATCHREF(&v) = (IDispatch**)&punk; + test_myVariantClearImpl.events = 0; + hres = VariantClear(&v); + ok(hres == S_OK, "ret %08x\n", hres); + ok(V_VT(&v) == 0, "vt %04x\n", V_VT(&v)); + ok(V_DISPATCHREF(&v) == (IDispatch**)&punk, "dispatch ref %p\n", V_DISPATCHREF(&v)); + /* Check that nothing got called */ + ok(test_myVariantClearImpl.events == 0, "Unexpected call. events %08x\n", test_myVariantClearImpl.events); +} + +static void test_VariantCopy(void) +{ + VARIANTARG vSrc, vDst; + VARTYPE vt; + size_t i; + HRESULT hres, hExpected; + + /* Establish that the failure/other cases are dealt with. Individual tests + * for each type should verify that data is copied correctly, references + * are updated, etc. + */ + + /* vSrc == vDst */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]) && !IS_ANCIENT; i++) + { + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + SKIPTESTS(vt); + + memset(&vSrc, 0, sizeof(vSrc)); + V_VT(&vSrc) = vt | ExtraFlags[i]; + + hExpected = DISP_E_BADVARTYPE; + /* src is allowed to be a VT_CLSID */ + if (vt != VT_CLSID && IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + + hres = VariantCopy(&vSrc, &vSrc); + + ok(hres == hExpected, + "Copy(src==dst): expected 0x%X, got 0x%X for src==dest vt %d|0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + } + } + + /* Test that if VariantClear() fails on dest, the function fails. This also + * shows that dest is in fact cleared and not just overwritten + */ + memset(&vSrc, 0, sizeof(vSrc)); + V_VT(&vSrc) = VT_UI1; + + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]) && !IS_ANCIENT; i++) + { + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + SKIPTESTS(vt); + + hExpected = DISP_E_BADVARTYPE; + + memset(&vDst, 0, sizeof(vDst)); + V_VT(&vDst) = vt | ExtraFlags[i]; + + if (IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + + hres = VariantCopy(&vDst, &vSrc); + + ok(hres == hExpected, + "Copy(bad dst): expected 0x%X, got 0x%X for dest vt %d|0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + if (hres == S_OK) + ok(V_VT(&vDst) == VT_UI1, + "Copy(bad dst): expected vt = VT_UI1, got %d\n", V_VT(&vDst)); + } + } + + /* Test that VariantClear() checks vSrc for validity before copying */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]) && !IS_ANCIENT; i++) + { + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + SKIPTESTS(vt); + + hExpected = DISP_E_BADVARTYPE; + + memset(&vDst, 0, sizeof(vDst)); + V_VT(&vDst) = VT_EMPTY; + + memset(&vSrc, 0, sizeof(vSrc)); + V_VT(&vSrc) = vt | ExtraFlags[i]; + + /* src is allowed to be a VT_CLSID */ + if (vt != VT_CLSID && IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + + hres = VariantCopy(&vDst, &vSrc); + + ok(hres == hExpected, + "Copy(bad src): expected 0x%X, got 0x%X for src vt %d|0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + if (hres == S_OK) + ok(V_VT(&vDst) == (vt|ExtraFlags[i]), + "Copy(bad src): expected vt = %d, got %d\n", + vt | ExtraFlags[i], V_VT(&vDst)); + } + } + + /* Test that copying a NULL BSTR results in an empty BSTR */ + memset(&vDst, 0, sizeof(vDst)); + V_VT(&vDst) = VT_EMPTY; + memset(&vSrc, 0, sizeof(vSrc)); + V_VT(&vSrc) = VT_BSTR; + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK, "Copy(NULL BSTR): Failed to copy a NULL BSTR\n"); + if (hres == S_OK) + { + ok((V_VT(&vDst) == VT_BSTR) && V_BSTR(&vDst), + "Copy(NULL BSTR): should have non-NULL result\n"); + if ((V_VT(&vDst) == VT_BSTR) && V_BSTR(&vDst)) + { + ok(*V_BSTR(&vDst) == 0, "Copy(NULL BSTR): result not empty\n"); + } + } +} + +/* Determine if a vt is valid for VariantCopyInd() */ +static int IsValidVariantCopyIndVT(VARTYPE vt, VARTYPE extraFlags) +{ + int ret = 0; + + if ((extraFlags & VT_ARRAY) || + (vt > VT_NULL && vt != (VARTYPE)15 && vt < VT_VOID && + !(extraFlags & (VT_VECTOR|VT_RESERVED)))) + { + ret = 1; /* ok */ + } + return ret; +} + +static void test_VariantCopyInd(void) +{ + VARIANTARG vSrc, vDst, vRef, vRef2; + VARTYPE vt; + size_t i; + BYTE buffer[64]; + HRESULT hres, hExpected; + + memset(buffer, 0, sizeof(buffer)); + + /* vSrc == vDst */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]) && !IS_ANCIENT; i++) + { + if (ExtraFlags[i] & VT_ARRAY) + continue; /* Native crashes on NULL safearray */ + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + SKIPTESTS(vt); + + memset(&vSrc, 0, sizeof(vSrc)); + V_VT(&vSrc) = vt | ExtraFlags[i]; + + hExpected = DISP_E_BADVARTYPE; + if (!(ExtraFlags[i] & VT_BYREF)) + { + /* if src is not by-reference, acts as VariantCopy() */ + if (vt != VT_CLSID && IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + } + else + { + if (vt == VT_SAFEARRAY || vt == VT_BSTR || vt == VT_UNKNOWN || + vt == VT_DISPATCH || vt == VT_RECORD) + continue; /* Need valid ptrs for deep copies */ + + V_BYREF(&vSrc) = &buffer; + hExpected = E_INVALIDARG; + + if ((vt == VT_I8 || vt == VT_UI8) && + ExtraFlags[i] == VT_BYREF) + { + if (HAVE_OLEAUT32_I8) + hExpected = S_OK; /* Only valid if I8 is a known type */ + } + else if (IsValidVariantCopyIndVT(vt, ExtraFlags[i])) + hExpected = S_OK; + } + + hres = VariantCopyInd(&vSrc, &vSrc); + + ok(hres == hExpected, + "CopyInd(src==dst): expected 0x%X, got 0x%X for src==dst vt %d|0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + } + } + + /* Bad dest */ + memset(&vSrc, 0, sizeof(vSrc)); + V_VT(&vSrc) = VT_UI1|VT_BYREF; + V_BYREF(&vSrc) = &buffer; + + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]) && !IS_ANCIENT; i++) + { + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + SKIPTESTS(vt); + + memset(&vDst, 0, sizeof(vDst)); + V_VT(&vDst) = vt | ExtraFlags[i]; + + hExpected = DISP_E_BADVARTYPE; + + if (IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + + hres = VariantCopyInd(&vDst, &vSrc); + + ok(hres == hExpected, + "CopyInd(bad dst): expected 0x%X, got 0x%X for dst vt %d|0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + if (hres == S_OK) + ok(V_VT(&vDst) == VT_UI1, + "CopyInd(bad dst): expected vt = VT_UI1, got %d\n", V_VT(&vDst)); + } + } + + /* bad src */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]) && !IS_ANCIENT; i++) + { + if (ExtraFlags[i] & VT_ARRAY) + continue; /* Native crashes on NULL safearray */ + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + SKIPTESTS(vt); + + memset(&vDst, 0, sizeof(vDst)); + V_VT(&vDst) = VT_EMPTY; + + memset(&vSrc, 0, sizeof(vSrc)); + V_VT(&vSrc) = vt | ExtraFlags[i]; + + hExpected = DISP_E_BADVARTYPE; + if (!(ExtraFlags[i] & VT_BYREF)) + { + /* if src is not by-reference, acts as VariantCopy() */ + if (vt != VT_CLSID && IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + } + else + { + if (vt == VT_SAFEARRAY || vt == VT_BSTR || vt == VT_UNKNOWN || + vt == VT_DISPATCH || vt == VT_RECORD) + continue; /* Need valid ptrs for deep copies, see vartype.c */ + + V_BYREF(&vSrc) = &buffer; + + hExpected = E_INVALIDARG; + + if ((vt == VT_I8 || vt == VT_UI8) && + ExtraFlags[i] == VT_BYREF) + { + if (HAVE_OLEAUT32_I8) + hExpected = S_OK; /* Only valid if I8 is a known type */ + } + else if (IsValidVariantCopyIndVT(vt, ExtraFlags[i])) + hExpected = S_OK; + } + + hres = VariantCopyInd(&vDst, &vSrc); + + ok(hres == hExpected, + "CopyInd(bad src): expected 0x%X, got 0x%X for src vt %d|0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + if (hres == S_OK) + { + if (vt == VT_VARIANT && ExtraFlags[i] == VT_BYREF) + { + /* Type of vDst should be the type of the referenced variant. + * Since we set the buffer to all zeros, its type should be + * VT_EMPTY. + */ + ok(V_VT(&vDst) == VT_EMPTY, + "CopyInd(bad src): expected dst vt = VT_EMPTY, got %d|0x%X\n", + V_VT(&vDst) & VT_TYPEMASK, V_VT(&vDst) & ~VT_TYPEMASK); + } + else + { + ok(V_VT(&vDst) == (vt|(ExtraFlags[i] & ~VT_BYREF)), + "CopyInd(bad src): expected dst vt = %d|0x%X, got %d|0x%X\n", + vt, ExtraFlags[i] & ~VT_BYREF, + V_VT(&vDst) & VT_TYPEMASK, V_VT(&vDst) & ~VT_TYPEMASK); + } + } + } + } + + /* By-reference variants are dereferenced */ + V_VT(&vRef) = VT_UI1; + V_UI1(&vRef) = 0x77; + V_VT(&vSrc) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&vSrc) = &vRef; + VariantInit(&vDst); + + hres = VariantCopyInd(&vDst, &vSrc); + ok(V_VT(&vDst) == VT_UI1 && V_UI1(&vDst) == 0x77, + "CopyInd(deref): expected dst vt = VT_UI1, val 0x77, got %d|0x%X, 0x%2X\n", + V_VT(&vDst) & VT_TYPEMASK, V_VT(&vDst) & ~VT_TYPEMASK, V_UI1(&vDst)); + + /* By-reference variant to a by-reference type succeeds */ + V_VT(&vRef) = VT_UI1|VT_BYREF; + V_UI1REF(&vRef) = buffer; buffer[0] = 0x88; + V_VT(&vSrc) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&vSrc) = &vRef; + VariantInit(&vDst); + + hres = VariantCopyInd(&vDst, &vSrc); + ok(V_VT(&vDst) == VT_UI1 && V_UI1(&vDst) == 0x88, + "CopyInd(deref): expected dst vt = VT_UI1, val 0x77, got %d|0x%X, 0x%2X\n", + V_VT(&vDst) & VT_TYPEMASK, V_VT(&vDst) & ~VT_TYPEMASK, V_UI1(&vDst)); + + /* But a by-reference variant to a by-reference variant fails */ + V_VT(&vRef2) = VT_UI1; + V_UI1(&vRef2) = 0x77; + V_VT(&vRef) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&vRef) = &vRef2; + V_VT(&vSrc) = VT_VARIANT|VT_BYREF; + V_VARIANTREF(&vSrc) = &vRef; + VariantInit(&vDst); + + hres = VariantCopyInd(&vDst, &vSrc); + ok(hres == E_INVALIDARG, + "CopyInd(ref->ref): expected E_INVALIDARG, got 0x%08x\n", hres); +} + +static HRESULT (WINAPI *pVarParseNumFromStr)(OLECHAR*,LCID,ULONG,NUMPARSE*,BYTE*); + +/* Macros for converting and testing the result of VarParseNumFromStr */ +#define FAILDIG 255 + +static HRESULT convert_str( const char *str, INT dig, ULONG flags, + NUMPARSE *np, BYTE rgb[128], LCID lcid ) +{ + OLECHAR buff[128]; + MultiByteToWideChar( CP_ACP,0, str, -1, buff, sizeof(buff)/sizeof(WCHAR) ); + memset( rgb, FAILDIG, 128 ); + memset( np, 255, sizeof(*np) ); + np->cDig = dig; + np->dwInFlags = flags; + return pVarParseNumFromStr( buff, lcid, LOCALE_NOUSEROVERRIDE, np, rgb); +} + +static void expect_NumFromStr( int line, HRESULT hres, NUMPARSE *np, INT a, ULONG b, ULONG c, + INT d, INT e, INT f ) +{ + if (hres == (HRESULT)S_OK) + { + ok_(__FILE__,line)(np->cDig == a, "Expected cDig = %d, got %d\n", a, np->cDig); + ok_(__FILE__,line)(np->dwInFlags == b, "Expected dwInFlags = 0x%x, got 0x%x\n", b, np->dwInFlags); + ok_(__FILE__,line)(np->dwOutFlags == c, "Expected dwOutFlags = 0x%x, got 0x%x\n", c, np->dwOutFlags); + ok_(__FILE__,line)(np->cchUsed == d, "Expected cchUsed = %d, got %d\n", d, np->cchUsed); + ok_(__FILE__,line)(np->nBaseShift == e, "Expected nBaseShift = %d, got %d\n", e, np->nBaseShift); + ok_(__FILE__,line)(np->nPwr10 == f, "Expected nPwr10 = %d, got %d\n", f, np->nPwr10); + } +} + +#define CONVERTN(str,dig,flags) hres = convert_str( str, dig, flags, &np, rgb, lcid ) +#define CONVERT(str,flags) CONVERTN(str,sizeof(rgb),flags) +#define EXPECT(a,b,c,d,e,f) expect_NumFromStr( __LINE__, hres, &np, a, b, c, d, e, f ) +#define EXPECTRGB(a,b) ok(rgb[a] == b, "Digit[%d], expected %d, got %d\n", a, b, rgb[a]) +#define EXPECTFAIL ok(hres == (HRESULT)DISP_E_TYPEMISMATCH, "Call succeeded, hres = %08x\n", hres) +#define EXPECT2(a,b) EXPECTRGB(0,a); EXPECTRGB(1,b) + +static void test_VarParseNumFromStr(void) +{ + HRESULT hres; + /* Ensure all tests are using the same locale characters for '$', ',' etc */ + LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + NUMPARSE np; + BYTE rgb[128]; + + /** No flags **/ + + CHECKPTR(VarParseNumFromStr); + + /* Consume a single digit */ + CONVERT("7", 0); + EXPECT(1,0,0,1,0,0); + EXPECT2(7,FAILDIG); + + /* cDig is not literal digits - zeros are suppressed and nPwr10 is increased */ + CONVERT("10", 0); + EXPECT(1,0,0,2,0,1); + /* Note: Win32 writes the trailing zeros if they are within cDig's limits, + * but then excludes them from the returned cDig count. + * In our implementation we don't bother writing them at all. + */ + EXPECTRGB(0, 1); + + /* if cDig is too small and numbers follow, sets INEXACT */ + CONVERTN("11",1, 0); + EXPECT(1,0,NUMPRS_INEXACT,2,0,1); + EXPECT2(1,FAILDIG); + + /* Strips leading zeros */ + CONVERT("01", 0); + EXPECT(1,0,0,2,0,0); + EXPECT2(1,FAILDIG); + + /* Strips leading zeros */ + CONVERTN("01",1, 0); + EXPECT(1,0,0,2,0,0); + EXPECT2(1,FAILDIG); + + + /* Fails on non digits */ + CONVERT("a", 0); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + /** NUMPRS_LEADING_WHITE/NUMPRS_TRAILING_WHITE **/ + + /* Without flag, fails on whitespace */ + CONVERT(" 0", 0); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + + /* With flag, consumes whitespace */ + CONVERT(" 0", NUMPRS_LEADING_WHITE); + EXPECT(1,NUMPRS_LEADING_WHITE,NUMPRS_LEADING_WHITE,2,0,0); + EXPECT2(0,FAILDIG); + + /* Test TAB once, then assume it acts as space for all cases */ + CONVERT("\t0", NUMPRS_LEADING_WHITE); + EXPECT(1,NUMPRS_LEADING_WHITE,NUMPRS_LEADING_WHITE,2,0,0); + EXPECT2(0,FAILDIG); + + + /* Doesn't pick up trailing whitespace without flag */ + CONVERT("0 ", 0); + EXPECT(1,0,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* With flag, consumes trailing whitespace */ + CONVERT("0 ", NUMPRS_TRAILING_WHITE); + EXPECT(1,NUMPRS_TRAILING_WHITE,NUMPRS_TRAILING_WHITE,2,0,0); + EXPECT2(0,FAILDIG); + + /* Leading flag only consumes leading */ + CONVERT(" 0 ", NUMPRS_LEADING_WHITE); + EXPECT(1,NUMPRS_LEADING_WHITE,NUMPRS_LEADING_WHITE,2,0,0); + EXPECT2(0,FAILDIG); + + /* Both flags consumes both */ + CONVERT(" 0 ", NUMPRS_LEADING_WHITE|NUMPRS_TRAILING_WHITE); + EXPECT(1,NUMPRS_LEADING_WHITE|NUMPRS_TRAILING_WHITE,NUMPRS_LEADING_WHITE|NUMPRS_TRAILING_WHITE,3,0,0); + EXPECT2(0,FAILDIG); + + /** NUMPRS_LEADING_PLUS/NUMPRS_TRAILING_PLUS **/ + + /* Without flag, fails on + */ + CONVERT("+0", 0); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + /* With flag, consumes + */ + CONVERT("+0", NUMPRS_LEADING_PLUS); + EXPECT(1,NUMPRS_LEADING_PLUS,NUMPRS_LEADING_PLUS,2,0,0); + EXPECT2(0,FAILDIG); + + /* Without flag, doesn't consume trailing + */ + CONVERT("0+", 0); + EXPECT(1,0,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* With flag, consumes trailing + */ + CONVERT("0+", NUMPRS_TRAILING_PLUS); + EXPECT(1,NUMPRS_TRAILING_PLUS,NUMPRS_TRAILING_PLUS,2,0,0); + EXPECT2(0,FAILDIG); + + /* With leading flag, doesn't consume trailing + */ + CONVERT("+0+", NUMPRS_LEADING_PLUS); + EXPECT(1,NUMPRS_LEADING_PLUS,NUMPRS_LEADING_PLUS,2,0,0); + EXPECT2(0,FAILDIG); + + /* Trailing + doesn't get consumed if we specify both (unlike whitespace) */ + CONVERT("+0+", NUMPRS_LEADING_PLUS|NUMPRS_TRAILING_PLUS); + EXPECT(1,NUMPRS_LEADING_PLUS|NUMPRS_TRAILING_PLUS,NUMPRS_LEADING_PLUS,2,0,0); + EXPECT2(0,FAILDIG); + + /** NUMPRS_LEADING_MINUS/NUMPRS_TRAILING_MINUS **/ + + /* Without flag, fails on - */ + CONVERT("-0", 0); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + /* With flag, consumes - */ + CONVERT("-0", NUMPRS_LEADING_MINUS); + EXPECT(1,NUMPRS_LEADING_MINUS,NUMPRS_NEG|NUMPRS_LEADING_MINUS,2,0,0); + EXPECT2(0,FAILDIG); + + /* Without flag, doesn't consume trailing - */ + CONVERT("0-", 0); + EXPECT(1,0,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* With flag, consumes trailing - */ + CONVERT("0-", NUMPRS_TRAILING_MINUS); + EXPECT(1,NUMPRS_TRAILING_MINUS,NUMPRS_NEG|NUMPRS_TRAILING_MINUS,2,0,0); + EXPECT2(0,FAILDIG); + + /* With leading flag, doesn't consume trailing - */ + CONVERT("-0-", NUMPRS_LEADING_MINUS); + EXPECT(1,NUMPRS_LEADING_MINUS,NUMPRS_NEG|NUMPRS_LEADING_MINUS,2,0,0); + EXPECT2(0,FAILDIG); + + /* Trailing - doesn't get consumed if we specify both (unlike whitespace) */ + CONVERT("-0-", NUMPRS_LEADING_MINUS|NUMPRS_TRAILING_MINUS); + EXPECT(1,NUMPRS_LEADING_MINUS|NUMPRS_TRAILING_MINUS,NUMPRS_NEG|NUMPRS_LEADING_MINUS,2,0,0); + EXPECT2(0,FAILDIG); + + /** NUMPRS_HEX_OCT **/ + + /* Could be hex, octal or decimal - With flag reads as decimal */ + CONVERT("0", NUMPRS_HEX_OCT); + EXPECT(1,NUMPRS_HEX_OCT,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* Doesn't recognise hex in .asm sytax */ + CONVERT("0h", NUMPRS_HEX_OCT); + EXPECT(1,NUMPRS_HEX_OCT,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* Doesn't fail with valid leading string but no digits */ + CONVERT("0x", NUMPRS_HEX_OCT); + EXPECT(1,NUMPRS_HEX_OCT,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* Doesn't recognise hex format humbers at all! */ + CONVERT("0x0", NUMPRS_HEX_OCT); + EXPECT(1,NUMPRS_HEX_OCT,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* Doesn't recognise plain hex digits either */ + CONVERT("FE", NUMPRS_HEX_OCT); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + /* Octal */ + CONVERT("0100", NUMPRS_HEX_OCT); + EXPECT(1,NUMPRS_HEX_OCT,0,4,0,2); + EXPECTRGB(0,1); + EXPECTRGB(1,0); + EXPECTRGB(2,0); + EXPECTRGB(3,FAILDIG); + + /* VB hex */ + CONVERT("&HF800", NUMPRS_HEX_OCT); + EXPECT(4,NUMPRS_HEX_OCT,0x40,6,4,0); + EXPECTRGB(0,15); + EXPECTRGB(1,8); + EXPECTRGB(2,0); + EXPECTRGB(3,0); + EXPECTRGB(4,FAILDIG); + + /* VB hex lower case and leading zero */ + CONVERT("&h0abcdef", NUMPRS_HEX_OCT); + EXPECT(6,NUMPRS_HEX_OCT,0x40,9,4,0); + EXPECTRGB(0,10); + EXPECTRGB(1,11); + EXPECTRGB(2,12); + EXPECTRGB(3,13); + EXPECTRGB(4,14); + EXPECTRGB(5,15); + EXPECTRGB(6,FAILDIG); + + /* VB oct */ + CONVERT("&O300", NUMPRS_HEX_OCT); + EXPECT(3,NUMPRS_HEX_OCT,0x40,5,3,0); + EXPECTRGB(0,3); + EXPECTRGB(1,0); + EXPECTRGB(2,0); + EXPECTRGB(3,FAILDIG); + + /* VB oct lower case and leading zero */ + CONVERT("&o0777", NUMPRS_HEX_OCT); + EXPECT(3,NUMPRS_HEX_OCT,0x40,6,3,0); + EXPECTRGB(0,7); + EXPECTRGB(1,7); + EXPECTRGB(2,7); + EXPECTRGB(3,FAILDIG); + + /* VB oct char bigger than 7 */ + CONVERT("&o128", NUMPRS_HEX_OCT); +/* + Native versions 2.x of oleaut32 allow this to succeed: later versions and Wine don't + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); +*/ + /** NUMPRS_PARENS **/ + + /* Empty parens = error */ + CONVERT("()", NUMPRS_PARENS); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + /* With flag, trailing parens not consumed */ + CONVERT("0()", NUMPRS_PARENS); + EXPECT(1,NUMPRS_PARENS,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* With flag, Number in parens made negative and parens consumed */ + CONVERT("(0)", NUMPRS_PARENS); + EXPECT(1,NUMPRS_PARENS,NUMPRS_NEG|NUMPRS_PARENS,3,0,0); + EXPECT2(0,FAILDIG); + + /** NUMPRS_THOUSANDS **/ + + /* With flag, thousands sep. not needed */ + CONVERT("0", NUMPRS_THOUSANDS); + EXPECT(1,NUMPRS_THOUSANDS,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* With flag, thousands sep. and following digits consumed */ + CONVERT("1,000", NUMPRS_THOUSANDS); + EXPECT(1,NUMPRS_THOUSANDS,NUMPRS_THOUSANDS,5,0,3); + EXPECTRGB(0,1); + + /* With flag and decimal point, thousands sep. but not decimals consumed */ + CONVERT("1,000.0", NUMPRS_THOUSANDS); + EXPECT(1,NUMPRS_THOUSANDS,NUMPRS_THOUSANDS,5,0,3); + EXPECTRGB(0,1); + + /** NUMPRS_CURRENCY **/ + + /* Without flag, chokes on currency sign */ + CONVERT("$11", 0); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + /* With flag, consumes currency sign */ + CONVERT("$11", NUMPRS_CURRENCY); + EXPECT(2,NUMPRS_CURRENCY,NUMPRS_CURRENCY,3,0,0); + EXPECT2(1,1); + EXPECTRGB(2,FAILDIG); + + /* With flag only, doesn't consume decimal point */ + CONVERT("$11.1", NUMPRS_CURRENCY); + EXPECT(2,NUMPRS_CURRENCY,NUMPRS_CURRENCY,3,0,0); + EXPECT2(1,1); + EXPECTRGB(2,FAILDIG); + + /* With flag and decimal flag, consumes decimal point and following digits */ + CONVERT("$11.1", NUMPRS_CURRENCY|NUMPRS_DECIMAL); + EXPECT(3,NUMPRS_CURRENCY|NUMPRS_DECIMAL,NUMPRS_CURRENCY|NUMPRS_DECIMAL,5,0,-1); + EXPECT2(1,1); + EXPECTRGB(2,1); + EXPECTRGB(3,FAILDIG); + + /* Thousands flag can only be used with currency */ + CONVERT("$1,234", NUMPRS_CURRENCY|NUMPRS_THOUSANDS); + EXPECT(4,NUMPRS_CURRENCY|NUMPRS_THOUSANDS,NUMPRS_CURRENCY|NUMPRS_THOUSANDS,6,0,0); + EXPECT2(1,2); + EXPECTRGB(2,3); + EXPECTRGB(3,4); + EXPECTRGB(4,FAILDIG); + + /** NUMPRS_DECIMAL **/ + + /* With flag, consumes decimal point */ + CONVERT("1.1", NUMPRS_DECIMAL); + EXPECT(2,NUMPRS_DECIMAL,NUMPRS_DECIMAL,3,0,-1); + EXPECT2(1,1); + EXPECTRGB(2,FAILDIG); + + /* With flag, consumes decimal point. Skipping the decimal part is not an error */ + CONVERT("1.", NUMPRS_DECIMAL); + EXPECT(1,NUMPRS_DECIMAL,NUMPRS_DECIMAL,2,0,0); + EXPECT2(1,FAILDIG); + + /* Consumes only one decimal point */ + CONVERT("1.1.", NUMPRS_DECIMAL); + EXPECT(2,NUMPRS_DECIMAL,NUMPRS_DECIMAL,3,0,-1); + EXPECT2(1,1); + EXPECTRGB(2,FAILDIG); + + /** NUMPRS_EXPONENT **/ + + /* Without flag, doesn't consume exponent */ + CONVERT("1e1", 0); + EXPECT(1,0,0,1,0,0); + EXPECT2(1,FAILDIG); + + /* With flag, consumes exponent */ + CONVERT("1e1", NUMPRS_EXPONENT); + EXPECT(1,NUMPRS_EXPONENT,NUMPRS_EXPONENT,3,0,1); + EXPECT2(1,FAILDIG); + + /* Negative exponents are accepted without flags */ + CONVERT("1e-1", NUMPRS_EXPONENT); + EXPECT(1,NUMPRS_EXPONENT,NUMPRS_EXPONENT,4,0,-1); + EXPECT2(1,FAILDIG); + + /* As are positive exponents and leading exponent 0s */ + CONVERT("1e+01", NUMPRS_EXPONENT); + EXPECT(1,NUMPRS_EXPONENT,NUMPRS_EXPONENT,5,0,1); + EXPECT2(1,FAILDIG); + + /* The same for zero exponents */ + CONVERT("1e0", NUMPRS_EXPONENT); + EXPECT(1,NUMPRS_EXPONENT,NUMPRS_EXPONENT,3,0,0); + EXPECT2(1,FAILDIG); + + /* Sign on a zero exponent doesn't matter */ + CONVERT("1e+0", NUMPRS_EXPONENT); + EXPECT(1,NUMPRS_EXPONENT,NUMPRS_EXPONENT,4,0,0); + EXPECT2(1,FAILDIG); + + CONVERT("1e-0", NUMPRS_EXPONENT); + EXPECT(1,NUMPRS_EXPONENT,NUMPRS_EXPONENT,4,0,0); + EXPECT2(1,FAILDIG); + + /* Doesn't consume a real number exponent */ + CONVERT("1e1.", NUMPRS_EXPONENT); + EXPECT(1,NUMPRS_EXPONENT,NUMPRS_EXPONENT,3,0,1); + EXPECT2(1,FAILDIG); + + /* Powers of 10 are calculated from the position of any decimal point */ + CONVERT("1.5e20", NUMPRS_EXPONENT|NUMPRS_DECIMAL); + EXPECT(2,NUMPRS_EXPONENT|NUMPRS_DECIMAL,NUMPRS_EXPONENT|NUMPRS_DECIMAL,6,0,19); + EXPECT2(1,5); + + CONVERT("1.5e-20", NUMPRS_EXPONENT|NUMPRS_DECIMAL); + EXPECT(2,NUMPRS_EXPONENT|NUMPRS_DECIMAL,NUMPRS_EXPONENT|NUMPRS_DECIMAL,7,0,-21); + EXPECT2(1,5); + + /** NUMPRS_USE_ALL **/ + + /* Flag expects all digits */ + CONVERT("0", NUMPRS_USE_ALL); + EXPECT(1,NUMPRS_USE_ALL,0,1,0,0); + EXPECT2(0,FAILDIG); + + /* Rejects anything trailing */ + CONVERT("0 ", NUMPRS_USE_ALL); + EXPECTFAIL; + EXPECT2(0,FAILDIG); + + /* Unless consumed by trailing flag */ + CONVERT("0 ", NUMPRS_USE_ALL|NUMPRS_TRAILING_WHITE); + EXPECT(1,NUMPRS_USE_ALL|NUMPRS_TRAILING_WHITE,NUMPRS_TRAILING_WHITE,2,0,0); + EXPECT2(0,FAILDIG); + + /** Combinations **/ + + /* Leading whitespace and plus, doesn't consume trailing whitespace */ + CONVERT("+ 0 ", NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE); + EXPECT(1,NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE,NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE,3,0,0); + EXPECT2(0,FAILDIG); + + /* Order of whitespace and plus is unimportant */ + CONVERT(" +0", NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE); + EXPECT(1,NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE,NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE,3,0,0); + EXPECT2(0,FAILDIG); + + /* Leading whitespace can be repeated */ + CONVERT(" + 0", NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE); + EXPECT(1,NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE,NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE,4,0,0); + EXPECT2(0,FAILDIG); + + /* But plus/minus etc. cannot */ + CONVERT("+ +0", NUMPRS_LEADING_PLUS|NUMPRS_LEADING_WHITE); + EXPECTFAIL; + EXPECTRGB(0,FAILDIG); + + /* Inexact is not set if trailing zeros are removed */ + CONVERTN("10", 1, 0); + EXPECT(1,0,0,2,0,1); + EXPECT2(1,FAILDIG); + + /* Make sure a leading 0 is stripped but decimals after it get read */ + CONVERT("-0.51", NUMPRS_STD); + EXPECT(2,NUMPRS_STD,NUMPRS_NEG|NUMPRS_DECIMAL|NUMPRS_LEADING_MINUS,5,0,-2); + EXPECT2(5,1); + + /* Keep trailing zeros on whole number part of a decimal */ + CONVERT("10.1", NUMPRS_STD); + EXPECT(3,NUMPRS_STD,NUMPRS_DECIMAL,4,0,-1); + EXPECT2(1,0); + EXPECTRGB(2,1); + + /* Zeros after decimal sign */ + CONVERT("0.01", NUMPRS_STD); + EXPECT(1,NUMPRS_STD,NUMPRS_DECIMAL,4,0,-2); + EXPECT2(1,FAILDIG); + + /* Trailing zeros after decimal part */ + CONVERT("0.10", NUMPRS_STD); + EXPECT(1,NUMPRS_STD,NUMPRS_DECIMAL,4,0,-1); + EXPECT2(1,0); +} + +static HRESULT (WINAPI *pVarNumFromParseNum)(NUMPARSE*,BYTE*,ULONG,VARIANT*); + +/* Macros for converting and testing the result of VarNumFromParseNum */ +#define SETRGB(indx,val) if (!indx) memset(rgb, FAILDIG, sizeof(rgb)); rgb[indx] = val +#undef CONVERT +#define CONVERT(a,b,c,d,e,f,bits) \ + np.cDig = (a); np.dwInFlags = (b); np.dwOutFlags = (c); np.cchUsed = (d); \ + np.nBaseShift = (e); np.nPwr10 = (f); hres = pVarNumFromParseNum(&np, rgb, bits, &vOut) +static const char *szFailOverflow = "Expected overflow, hres = %08x\n"; +#define EXPECT_OVERFLOW ok(hres == (HRESULT)DISP_E_OVERFLOW, szFailOverflow, hres) +static const char *szFailOk = "Call failed, hres = %08x\n"; +#define EXPECT_OK ok(hres == (HRESULT)S_OK, szFailOk, hres); \ + if (hres == (HRESULT)S_OK) +#define EXPECT_TYPE(typ) ok(V_VT(&vOut) == typ,"Expected Type = " #typ ", got %d\n", V_VT(&vOut)) +#define EXPECT_I1(val) EXPECT_OK { EXPECT_TYPE(VT_I1); \ + ok(V_I1(&vOut) == val, "Expected i1 = %d, got %d\n", (signed char)val, V_I1(&vOut)); } +#define EXPECT_UI1(val) EXPECT_OK { EXPECT_TYPE(VT_UI1); \ + ok(V_UI1(&vOut) == val, "Expected ui1 = %d, got %d\n", (BYTE)val, V_UI1(&vOut)); } +#define EXPECT_I2(val) EXPECT_OK { EXPECT_TYPE(VT_I2); \ + ok(V_I2(&vOut) == val, "Expected i2 = %d, got %d\n", (SHORT)val, V_I2(&vOut)); } +#define EXPECT_UI2(val) EXPECT_OK { EXPECT_TYPE(VT_UI2); \ + ok(V_UI2(&vOut) == val, "Expected ui2 = %d, got %d\n", (USHORT)val, V_UI2(&vOut)); } +#define EXPECT_I4(val) EXPECT_OK { EXPECT_TYPE(VT_I4); \ + ok(V_I4(&vOut) == val, "Expected i4 = %d, got %d\n", (LONG)val, V_I4(&vOut)); } +#define EXPECT_UI4(val) EXPECT_OK { EXPECT_TYPE(VT_UI4); \ + ok(V_UI4(&vOut) == val, "Expected ui4 = %d, got %d\n", (ULONG)val, V_UI4(&vOut)); } +#define EXPECT_I8(high,low) EXPECT_OK { EXPECT_TYPE(VT_I8); \ + ok(V_I8(&vOut) == ((((LONG64)(high))<<32)|(low)), "Expected i8 = %x%08x, got %x%08x\n", \ + (LONG)(high), (LONG)(low), (LONG)(V_I8(&vOut)>>32), (LONG)V_I8(&vOut) ); } +#define EXPECT_UI8(val) EXPECT_OK { EXPECT_TYPE(VT_UI8); \ + ok(V_UI8(&vOut) == val, "Expected ui8 = 0x%x%08x, got 0x%x%08x\n", \ + (DWORD)((ULONG64)val >> 32), (DWORD)(ULONG64)val, (DWORD)(V_UI8(&vOut) >> 32), (DWORD)V_UI8(&vOut)); } +#define EXPECT_R4(val) EXPECT_OK { EXPECT_TYPE(VT_R4); \ + ok(V_R4(&vOut) == val, "Expected r4 = %f, got %f\n", val, V_R4(&vOut)); } +#define EXPECT_R8(val) EXPECT_OK { EXPECT_TYPE(VT_R8); \ + ok(V_R8(&vOut) == val, "Expected r8 = %g, got %g\n", val, V_R8(&vOut)); } +#define CY_MULTIPLIER 10000 +#define EXPECT_CY(val) EXPECT_OK { EXPECT_TYPE(VT_CY); \ + ok(V_CY(&vOut).int64 == (LONG64)(val * CY_MULTIPLIER), "Expected r8 = 0x%x%08x, got 0x%x%08x\n", \ + (DWORD)((LONG64)val >> 23), (DWORD)(LONG64)val, (DWORD)(V_CY(&vOut).int64 >>32), (DWORD)V_CY(&vOut).int64); } +#define EXPECT_DECIMAL(valHi, valMid, valLo) EXPECT_OK { EXPECT_TYPE(VT_DECIMAL); \ + ok((V_DECIMAL(&vOut).Hi32 == valHi) && (S1(U1(V_DECIMAL(&vOut))).Mid32 == valMid) && \ + (S1(U1(V_DECIMAL(&vOut))).Lo32 == valLo), \ + "Expected decimal = %x/0x%x%08x, got %x/0x%x%08x\n", valHi, valMid, valLo, \ + V_DECIMAL(&vOut).Hi32, S1(U1(V_DECIMAL(&vOut))).Mid32, S1(U1(V_DECIMAL(&vOut))).Lo32); } + +static void test_VarNumFromParseNum(void) +{ + HRESULT hres; + NUMPARSE np; + BYTE rgb[128]; + VARIANT vOut; + + CHECKPTR(VarNumFromParseNum); + + /* Convert the number 1 to different types */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_I1); EXPECT_I1(1); + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_UI1); EXPECT_UI1(1); + /* Prefers a signed type to unsigned of the same size */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_I1|VTBIT_UI1); EXPECT_I1(1); + /* But takes the smaller size if possible */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_I2|VTBIT_UI1); EXPECT_UI1(1); + + /* Try different integer sizes */ +#define INTEGER_VTBITS (VTBIT_I1|VTBIT_UI1|VTBIT_I2|VTBIT_UI2|VTBIT_I4|VTBIT_UI4|VTBIT_I8|VTBIT_UI8) + + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, INTEGER_VTBITS); EXPECT_I1(1); + /* 127 */ + SETRGB(0, 1); SETRGB(1, 2); SETRGB(2, 7); + CONVERT(3,0,0,3,0,0, INTEGER_VTBITS); EXPECT_I1(127); + /* 128 */ + SETRGB(0, 1); SETRGB(1, 2); SETRGB(2, 8); + CONVERT(3,0,0,3,0,0, INTEGER_VTBITS); EXPECT_UI1(128); + /* 255 */ + SETRGB(0, 2); SETRGB(1, 5); SETRGB(2, 5); + CONVERT(3,0,0,3,0,0, INTEGER_VTBITS); EXPECT_UI1(255); + /* 256 */ + SETRGB(0, 2); SETRGB(1, 5); SETRGB(2, 6); + CONVERT(3,0,0,3,0,0, INTEGER_VTBITS); EXPECT_I2(256); + /* 32767 */ + SETRGB(0, 3); SETRGB(1, 2); SETRGB(2, 7); SETRGB(3, 6); SETRGB(4, 7); + CONVERT(5,0,0,5,0,0, INTEGER_VTBITS); EXPECT_I2(32767); + /* 32768 */ + SETRGB(0, 3); SETRGB(1, 2); SETRGB(2, 7); SETRGB(3, 6); SETRGB(4, 8); + CONVERT(5,0,0,5,0,0, INTEGER_VTBITS); EXPECT_UI2(32768); + + /* Assume the above pattern holds for remaining positive integers; test negative */ + + /* -128 */ + SETRGB(0, 1); SETRGB(1, 2); SETRGB(2, 8); + CONVERT(3,0,NUMPRS_NEG,3,0,0, INTEGER_VTBITS); EXPECT_I1(-128); + /* -129 */ + SETRGB(0, 1); SETRGB(1, 2); SETRGB(2, 9); + CONVERT(3,0,NUMPRS_NEG,3,0,0, INTEGER_VTBITS); EXPECT_I2(-129); + /* -32768 */ + SETRGB(0, 3); SETRGB(1, 2); SETRGB(2, 7); SETRGB(3, 6); SETRGB(4, 8); + CONVERT(5,0,NUMPRS_NEG,5,0,0, INTEGER_VTBITS); EXPECT_I2(-32768); + /* -32768 */ + SETRGB(0, 3); SETRGB(1, 2); SETRGB(2, 7); SETRGB(3, 6); SETRGB(4, 9); + CONVERT(5,0,NUMPRS_NEG,5,0,0, INTEGER_VTBITS); EXPECT_I4(-32769); + + /* Assume the above pattern holds for remaining negative integers */ + + /* Test hexadecimal conversions */ + SETRGB(0, 1); CONVERT(1,0,0,1,4,0, INTEGER_VTBITS); EXPECT_I1(0x01); + /* 0x7f */ + SETRGB(0, 7); SETRGB(1, 0xf); + CONVERT(2,0,0,2,4,0, INTEGER_VTBITS); EXPECT_I1(0x7f); + SETRGB(0, 7); SETRGB(1, 0xf); + CONVERT(2,0,0,2,4,0, VTBIT_DECIMAL); EXPECT_DECIMAL(0,0,0x7f); + /* 0x7fff */ + SETRGB(0, 7); SETRGB(1, 0xf); SETRGB(2, 0xf); SETRGB(3, 0xf); + CONVERT(4,0,0,4,4,0, INTEGER_VTBITS); EXPECT_I2(0x7fff); + /* 0x7fffffff */ + SETRGB(0, 7); SETRGB(1, 0xf); SETRGB(2, 0xf); SETRGB(3, 0xf); + SETRGB(4, 0xf); SETRGB(5, 0xf); SETRGB(6, 0xf); SETRGB(7, 0xf); + CONVERT(8,0,0,8,4,0, INTEGER_VTBITS); EXPECT_I4(0x7fffffffL); + /* 0x7fffffffffffffff (64 bits) */ + SETRGB(0, 7); SETRGB(1, 0xf); SETRGB(2, 0xf); SETRGB(3, 0xf); + SETRGB(4, 0xf); SETRGB(5, 0xf); SETRGB(6, 0xf); SETRGB(7, 0xf); + SETRGB(8, 0xf); SETRGB(9, 0xf); SETRGB(10, 0xf); SETRGB(11, 0xf); + SETRGB(12, 0xf); SETRGB(13, 0xf); SETRGB(14, 0xf); SETRGB(15, 0xf); + if (HAVE_OLEAUT32_I8) + { + /* We cannot use INTEGER_VTBITS as WinXP and Win2003 are broken(?). They + truncate the number to the smallest integer size requested: + CONVERT(16,0,0,16,4,0, INTEGER_VTBITS); EXPECT_I1((signed char)0xff); */ + CONVERT(16,0,0,16,4,0, VTBIT_I8); EXPECT_I8(0x7fffffff,0xffffffff); + } + + /* Assume the above pattern holds for numbers without hi-bit set, test (preservation of) hi-bit */ + /* 0x82 */ + SETRGB(0, 8); SETRGB(1, 2); + CONVERT(2,0,0,2,4,0, INTEGER_VTBITS); + EXPECT_I1((signed char)0x82); + /* 0x8002 */ + SETRGB(0, 8); SETRGB(1, 0); SETRGB(2, 0); SETRGB(3, 2); + CONVERT(4,0,0,4,4,0, INTEGER_VTBITS); + EXPECT_I2((signed short)0x8002); + /* 0x80000002 */ + SETRGB(0, 8); SETRGB(1, 0); SETRGB(2, 0); SETRGB(3, 0); + SETRGB(4, 0); SETRGB(5, 0); SETRGB(6, 0); SETRGB(7, 2); + CONVERT(8,0,0,8,4,0, INTEGER_VTBITS); EXPECT_I4(0x80000002); + /* 0x8000000000000002 (64 bits) */ + SETRGB(0, 8); SETRGB(1, 0); SETRGB(2, 0); SETRGB(3, 0); + SETRGB(4, 0); SETRGB(5, 0); SETRGB(6, 0); SETRGB(7, 0); + SETRGB(8, 0); SETRGB(9, 0); SETRGB(10, 0); SETRGB(11, 0); + SETRGB(12, 0); SETRGB(13, 0); SETRGB(14, 0); SETRGB(15, 2); + if (HAVE_OLEAUT32_I8) + { + /* We cannot use INTEGER_VTBITS as WinXP and Win2003 are broken(?). They + truncate the number to the smallest integer size requested: + CONVERT(16,0,0,16,4,0, INTEGER_VTBITS & ~VTBIT_I1); + EXPECT_I2((signed short)0x0002); */ + CONVERT(16,0,0,16,4,0, VTBIT_I8); EXPECT_I8(0x80000000,0x00000002); + } + + /* Test (preservation of) hi-bit with STRICT type requesting */ + /* 0x82 */ + SETRGB(0, 8); SETRGB(1, 2); + CONVERT(2,0,0,2,4,0, VTBIT_I1); + EXPECT_I1((signed char)0x82); + /* 0x8002 */ + SETRGB(0, 8); SETRGB(1, 0); SETRGB(2, 0); SETRGB(3, 2); + CONVERT(4,0,0,4,4,0, VTBIT_I2); + EXPECT_I2((signed short)0x8002); + /* 0x80000002 */ + SETRGB(0, 8); SETRGB(1, 0); SETRGB(2, 0); SETRGB(3, 0); + SETRGB(4, 0); SETRGB(5, 0); SETRGB(6, 0); SETRGB(7, 2); + CONVERT(8,0,0,8,4,0, VTBIT_I4); EXPECT_I4(0x80000002); + /* 0x8000000000000002 (64 bits) */ + SETRGB(0, 8); SETRGB(1, 0); SETRGB(2, 0); SETRGB(3, 0); + SETRGB(4, 0); SETRGB(5, 0); SETRGB(6, 0); SETRGB(7, 0); + SETRGB(8, 0); SETRGB(9, 0); SETRGB(10, 0); SETRGB(11, 0); + SETRGB(12, 0); SETRGB(13, 0); SETRGB(14, 0); SETRGB(15, 2); + if (HAVE_OLEAUT32_I8) + { + CONVERT(16,0,0,16,4,0, VTBIT_I8); EXPECT_I8(0x80000000,0x00000002); + } + /* Assume the above pattern holds for numbers with hi-bit set */ + + /* Negative numbers overflow if we have only unsigned outputs */ + /* -1 */ + SETRGB(0, 1); CONVERT(1,0,NUMPRS_NEG,1,0,0, VTBIT_UI1); EXPECT_OVERFLOW; + /* -0.6 */ + SETRGB(0, 6); CONVERT(1,0,NUMPRS_NEG,1,0,~0u, VTBIT_UI1); EXPECT_OVERFLOW; + + /* Except that rounding is done first, so -0.5 to 0 are accepted as 0 */ + /* -0.5 */ + SETRGB(0, 5); CONVERT(1,0,NUMPRS_NEG,1,0,~0u, VTBIT_UI1); EXPECT_UI1(0); + + /* Floating point zero is OK */ + /* 0.00000000E0 */ + SETRGB(0, 0); CONVERT(1,0,NUMPRS_DECIMAL|NUMPRS_EXPONENT,12,0,-8, VTBIT_R8); + EXPECT_R8(0.0); + + /* Float is acceptable for an integer input value */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_R4); EXPECT_R4(1.0f); + /* As is double */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_R8); EXPECT_R8(1.0); + /* As is currency */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_CY); EXPECT_CY(1); + + /* Float is preferred over double */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_R4|VTBIT_R8); EXPECT_R4(1.0f); + + /* Double is preferred over currency */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_R8|VTBIT_CY); EXPECT_R8(1.0); + + /* Currency is preferred over decimal */ + SETRGB(0, 1); CONVERT(1,0,0,1,0,0, VTBIT_CY|VTBIT_DECIMAL); EXPECT_CY(1); +} + + +static void test_UdateFromDate( int line, DATE dt, ULONG flags, HRESULT r, WORD d, WORD m, WORD y, + WORD h, WORD mn, WORD s, WORD ms, WORD dw, WORD dy) +{ + UDATE ud; + HRESULT res; + + memset(&ud, 0, sizeof(ud)); + res = pVarUdateFromDate(dt, flags, &ud); + ok_(__FILE__,line)(r == res, "Wrong result %x/%x\n", r, res); + if (SUCCEEDED(res)) + ok_(__FILE__,line)(ud.st.wYear == y && ud.st.wMonth == m && ud.st.wDay == d && + ud.st.wHour == h && ud.st.wMinute == mn && ud.st.wSecond == s && + ud.st.wMilliseconds == ms && ud.st.wDayOfWeek == dw && ud.wDayOfYear == dy, + "%.16g expected %d,%d,%d,%d,%d,%d,%d %d %d, got %d,%d,%d,%d,%d,%d,%d %d %d\n", + dt, d, m, y, h, mn, s, ms, dw, dy, + ud.st.wDay, ud.st.wMonth, ud.st.wYear, ud.st.wHour, ud.st.wMinute, + ud.st.wSecond, ud.st.wMilliseconds, ud.st.wDayOfWeek, ud.wDayOfYear ); +} +#define DT2UD(dt,flags,r,d,m,y,h,mn,s,ms,dw,dy) test_UdateFromDate(__LINE__,dt,flags,r,d,m,y,h,mn,s,ms,dw,dy) + +static void test_VarUdateFromDate(void) +{ + CHECKPTR(VarUdateFromDate); + DT2UD(29221.0,0,S_OK,1,1,1980,0,0,0,0,2,1); /* 1 Jan 1980 */ + DT2UD(29222.0,0,S_OK,2,1,1980,0,0,0,0,3,2); /* 2 Jan 1980 */ + DT2UD(33238.0,0,S_OK,31,12,1990,0,0,0,0,1,365); /* 31 Dec 1990 */ + DT2UD(0.0,0,S_OK,30,12,1899,0,0,0,0,6,364); /* 30 Dec 1899 - VT_DATE 0.0 */ + DT2UD(-657434.0,0,S_OK,1,1,100,0,0,0,0,5,1); /* 1 Jan 100 - Min */ + DT2UD(-657435.0,0,E_INVALIDARG,0,0,0,0,0,0,0,0,0); /* < 1 Jan 100 => err */ + DT2UD(2958465.0,0,S_OK,31,12,9999,0,0,0,0,5,365); /* 31 Dec 9999 - Max */ + DT2UD(2958466.0,0,E_INVALIDARG,0,0,0,0,0,0,0,0,0); /* > 31 Dec 9999 => err */ + + /* VAR_VALIDDATE doesn't prevent upper and lower bounds being checked */ + DT2UD(-657435.0,VAR_VALIDDATE,E_INVALIDARG,0,0,0,0,0,0,0,0,0); + DT2UD(2958466.0,VAR_VALIDDATE,E_INVALIDARG,0,0,0,0,0,0,0,0,0); + + /* Times */ + DT2UD(29221.25,0,S_OK,1,1,1980,6,0,0,0,2,1); /* 6 AM */ + DT2UD(29221.33333333,0,S_OK,1,1,1980,8,0,0,0,2,1); /* 8 AM */ + DT2UD(29221.5,0,S_OK,1,1,1980,12,0,0,0,2,1); /* 12 AM */ + DT2UD(29221.9888884444,0,S_OK,1,1,1980,23,44,0,0,2,1); /* 11:44 PM */ + DT2UD(29221.7508765432,0,S_OK,1,1,1980,18,1,16,0,2,1); /* 6:18:02 PM */ +} + + +static void test_DateFromUDate( int line, WORD d, WORD m, WORD y, WORD h, WORD mn, WORD s, WORD ms, + WORD dw, WORD dy, ULONG flags, HRESULT r, DATE dt ) +{ + UDATE ud; + double out; + HRESULT res; + + ud.st.wYear = y; + ud.st.wMonth = m; + ud.st.wDay = d; + ud.st.wHour = h; + ud.st.wMinute = mn; + ud.st.wSecond = s; + ud.st.wMilliseconds = ms; + ud.st.wDayOfWeek = dw; + ud.wDayOfYear = dy; + res = pVarDateFromUdate(&ud, flags, &out); + ok_(__FILE__,line)(r == res && (FAILED(r) || EQ_DOUBLE(out, dt)), + "expected %x, %.16g, got %x, %.16g\n", r, dt, res, out); +} +#define UD2T(d,m,y,h,mn,s,ms,dw,dy,flags,r,dt) test_DateFromUDate(__LINE__,d,m,y,h,mn,s,ms,dw,dy,flags,r,dt) + +static void test_VarDateFromUdate(void) +{ + CHECKPTR(VarDateFromUdate); + UD2T(1,1,1980,0,0,0,0,2,1,0,S_OK,29221.0); /* 1 Jan 1980 */ + UD2T(2,1,1980,0,0,0,0,3,2,0,S_OK,29222.0); /* 2 Jan 1980 */ + UD2T(31,12,1990,0,0,0,0,0,0,0,S_OK,33238.0); /* 31 Dec 1990 */ + UD2T(31,12,90,0,0,0,0,0,0,0,S_OK,33238.0); /* year < 100 is 1900+year! */ + UD2T(30,12,1899,0,0,0,0,6,364,0,S_OK,0.0); /* 30 Dec 1899 - VT_DATE 0.0 */ + UD2T(1,1,100,0,0,0,0,0,0,0,S_OK,-657434.0); /* 1 Jan 100 - Min */ + UD2T(31,12,9999,0,0,0,0,0,0,0,S_OK,2958465.0); /* 31 Dec 9999 - Max */ + UD2T(1,1,10000,0,0,0,0,0,0,0,E_INVALIDARG,0.0); /* > 31 Dec 9999 => err */ + + UD2T(1,1,1980,18,1,16,0,2,1,0,S_OK,29221.75087962963); /* 6:18:02 PM */ + + UD2T(0,1,1980,0,0,0,0,2,1,0,S_OK,29220.0); /* Rolls back to 31 Dec 1899 */ + UD2T(1,13,1980,0,0,0,0,2,1,0,S_OK,29587.0); /* Rolls fwd to 1/1/1981 */ +} + +static void test_st2dt(int line, WORD d, WORD m, WORD y, WORD h, WORD mn, + WORD s, WORD ms, INT r, double dt) +{ + SYSTEMTIME st; + double out; + INT res; + + st.wYear = y; + st.wMonth = m; + st.wDay = d; + st.wHour = h; + st.wMinute = mn; + st.wSecond = s; + st.wMilliseconds = ms; + st.wDayOfWeek = 0; + res = pSystemTimeToVariantTime(&st, &out); + ok_(__FILE__,line)(r == res && (!r || EQ_DOUBLE(out, dt)), + "expected %d, %.16g, got %d, %.16g\n", r, dt, res, out); +} +#define ST2DT(d,m,y,h,mn,s,ms,r,dt) test_st2dt(__LINE__,d,m,y,h,mn,s,ms,r,dt) + +static void test_SystemTimeToVariantTime(void) +{ + CHECKPTR(SystemTimeToVariantTime); + ST2DT(1,1,1980,0,0,0,0,TRUE,29221.0); + ST2DT(2,1,1980,0,0,0,0,TRUE,29222.0); + ST2DT(0,1,1980,0,0,0,0,TRUE,29220.0); /* Rolls back to 31 Dec 1899 */ + ST2DT(1,13,1980,0,0,0,0,FALSE,29587.0); /* Fails on invalid month */ + ST2DT(31,12,90,0,0,0,0,TRUE,33238.0); /* year < 100 is 1900+year! */ +} + +static void test_dt2st(int line, double dt, INT r, WORD d, WORD m, WORD y, + WORD h, WORD mn, WORD s, WORD ms) +{ + SYSTEMTIME st; + INT res; + + memset(&st, 0, sizeof(st)); + res = pVariantTimeToSystemTime(dt, &st); + ok_(__FILE__,line)(r == res && + (!r || (st.wYear == y && st.wMonth == m && st.wDay == d && + st.wHour == h && st.wMinute == mn && + st.wSecond == s && st.wMilliseconds == ms)), + "%.16g expected %d, %d,%d,%d,%d,%d,%d,%d, got %d, %d,%d,%d,%d,%d,%d,%d\n", + dt, r, d, m, y, h, mn, s, ms, res, st.wDay, st.wMonth, + st.wYear, st.wHour, st.wMinute, st.wSecond, + st.wMilliseconds); +} +#define DT2ST(dt,r,d,m,y,h,mn,s,ms) test_dt2st(__LINE__,dt,r,d,m,y,h,mn,s,ms) + +static void test_VariantTimeToSystemTime(void) +{ + CHECKPTR(VariantTimeToSystemTime); + DT2ST(29221.0,1,1,1,1980,0,0,0,0); + DT2ST(29222.0,1,2,1,1980,0,0,0,0); +} + +#define MKDOSDATE(d,m,y) ((d & 0x1f) | ((m & 0xf) << 5) | (((y-1980) & 0x7f) << 9)) +#define MKDOSTIME(h,m,s) (((s>>1) & 0x1f) | ((m & 0x3f) << 5) | ((h & 0x1f) << 11)) + +static void test_dos2dt(int line, WORD d, WORD m, WORD y, WORD h, WORD mn, + WORD s, INT r, double dt) +{ + unsigned short dosDate, dosTime; + double out; + INT res; + + out = 0.0; + dosDate = MKDOSDATE(d, m, y); + dosTime = MKDOSTIME(h, mn, s); + res = pDosDateTimeToVariantTime(dosDate, dosTime, &out); + ok_(__FILE__,line)(r == res && (!r || EQ_DOUBLE(out, dt)), + "expected %d, %.16g, got %d, %.16g\n", r, dt, res, out); +} +#define DOS2DT(d,m,y,h,mn,s,r,dt) test_dos2dt(__LINE__,d,m,y,h,mn,s,r,dt) + +static void test_DosDateTimeToVariantTime(void) +{ + CHECKPTR(DosDateTimeToVariantTime); + + /* Date */ + DOS2DT(1,1,1980,0,0,0,1,29221.0); /* 1/1/1980 */ + DOS2DT(31,12,2099,0,0,0,1,73050.0); /* 31/12/2099 */ + /* Dates are limited to the dos date max of 31/12/2099 */ + DOS2DT(31,12,2100,0,0,0,0,0.0); /* 31/12/2100 */ + /* Days and months of 0 cause date to roll back 1 day or month */ + DOS2DT(0,1,1980,0,0,0,1,29220.0); /* 0 Day => 31/12/1979 */ + DOS2DT(1,0,1980,0,0,0,1,29190.0); /* 0 Mth => 1/12/1979 */ + DOS2DT(0,0,1980,0,0,0,1,29189.0); /* 0 D/M => 30/11/1979 */ + /* Days > days in the month cause date to roll forward 1 month */ + DOS2DT(29,2,1981,0,0,0,1,29646.0); /* 29/2/1981 -> 3/1/1980 */ + DOS2DT(30,2,1981,0,0,0,1,29647.0); /* 30/2/1981 -> 4/1/1980 */ + /* Takes leap years into account when rolling forward */ + DOS2DT(29,2,1980,0,0,0,1,29280.0); /* 2/29/1980 */ + /* Months > 12 cause an error */ + DOS2DT(2,13,1980,0,0,0,0,0.0); + + /* Time */ + DOS2DT(1,1,1980,0,0,29,1,29221.00032407407); /* 1/1/1980 12:00:28 AM */ + DOS2DT(1,1,1980,0,0,31,1,29221.00034722222); /* 1/1/1980 12:00:30 AM */ + DOS2DT(1,1,1980,0,59,0,1,29221.04097222222); /* 1/1/1980 12:59:00 AM */ + DOS2DT(1,1,1980,0,60,0,0,0.0); /* Invalid seconds */ + DOS2DT(1,1,1980,23,0,0,1,29221.95833333333); /* 1/1/1980 11:00:00 PM */ + DOS2DT(1,1,1980,24,0,0,0,0.0); /* Invalid hours */ +} + +static void test_dt2dos(int line, double dt, INT r, WORD d, WORD m, WORD y, + WORD h, WORD mn, WORD s) +{ + unsigned short dosDate, dosTime, expDosDate, expDosTime; + INT res; + + dosTime = dosDate = 0; + expDosDate = MKDOSDATE(d,m,y); + expDosTime = MKDOSTIME(h,mn,s); + res = pVariantTimeToDosDateTime(dt, &dosDate, &dosTime); + ok_(__FILE__,line)(r == res && (!r || (dosTime == expDosTime && dosDate == expDosDate)), + "%g: expected %d,%d(%d/%d/%d),%d(%d:%d:%d) got %d,%d(%d/%d/%d),%d(%d:%d:%d)\n", + dt, r, expDosDate, expDosDate & 0x1f, + (expDosDate >> 5) & 0xf, 1980 + (expDosDate >> 9), + expDosTime, expDosTime >> 11, (expDosTime >> 5) & 0x3f, + (expDosTime & 0x1f), + res, dosDate, dosDate & 0x1f, (dosDate >> 5) & 0xf, + 1980 + (dosDate >> 9), dosTime, dosTime >> 11, + (dosTime >> 5) & 0x3f, (dosTime & 0x1f)); +} +#define DT2DOS(dt,r,d,m,y,h,mn,s) test_dt2dos(__LINE__,dt,r,d,m,y,h,mn,s) + +static void test_VariantTimeToDosDateTime(void) +{ + CHECKPTR(VariantTimeToDosDateTime); + + /* Date */ + DT2DOS(29221.0,1,1,1,1980,0,0,0); /* 1/1/1980 */ + DT2DOS(73050.0,1,31,12,2099,0,0,0); /* 31/12/2099 */ + DT2DOS(29220.0,0,0,0,0,0,0,0); /* 31/12/1979 - out of range */ + DT2DOS(73415.0,0,0,0,0,0,0,0); /* 31/12/2100 - out of range */ + + /* Time */ + DT2DOS(29221.00032407407,1,1,1,1980,0,0,29); /* 1/1/1980 12:00:28 AM */ + DT2DOS(29221.00034722222,1,1,1,1980,0,0,31); /* 1/1/1980 12:00:30 AM */ + DT2DOS(29221.04097222222,1,1,1,1980,0,59,0); /* 1/1/1980 12:59:00 AM */ + DT2DOS(29221.95833333333,1,1,1,1980,23,0,0); /* 1/1/1980 11:00:00 PM */ +} + +static HRESULT (WINAPI *pVarAbs)(LPVARIANT,LPVARIANT); + +#define VARABS(vt,val,rvt,rval) \ + V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call1( __LINE__, pVarAbs, &v, &exp ) + +static void test_VarAbs(void) +{ + static WCHAR szNum[] = {'-','1','.','1','\0' }; + char buff[8]; + HRESULT hres; + VARIANT v, vDst, exp; + size_t i; + + CHECKPTR(VarAbs); + + /* Test all possible V_VT values. + */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE vt; + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT hExpected = DISP_E_BADVARTYPE; + + SKIPTESTS(vt); + + memset(&v, 0, sizeof(v)); + V_VT(&v) = vt | ExtraFlags[i]; + V_VT(&vDst) = VT_EMPTY; + + hres = pVarAbs(&v,&vDst); + if (ExtraFlags[i] & (VT_ARRAY|VT_ARRAY) || + (!ExtraFlags[i] && (vt == VT_UNKNOWN || vt == VT_BSTR || + vt == VT_DISPATCH || vt == VT_ERROR || vt == VT_RECORD))) + { + hExpected = DISP_E_TYPEMISMATCH; + } + else if (ExtraFlags[i] || vt >= VT_CLSID || vt == VT_VARIANT) + { + hExpected = DISP_E_BADVARTYPE; + } + else if (IsValidVariantClearVT(vt, ExtraFlags[i])) + hExpected = S_OK; + + /* Native always fails on some vartypes that should be valid. don't + * check that Wine does the same; these are bugs in native. + */ + if (vt == VT_I8 || vt == VT_UI8 || vt == VT_INT || vt == VT_UINT || + vt == VT_I1 || vt == VT_UI2 || vt == VT_UI4) + continue; + ok(hres == hExpected, "VarAbs: expected 0x%X, got 0x%X for vt %d | 0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + } + } + + /* BOOL->I2, BSTR->R8, all others remain the same */ + VARABS(BOOL,VARIANT_TRUE,I2,-VARIANT_TRUE); + VARABS(BOOL,VARIANT_FALSE,I2,VARIANT_FALSE); + VARABS(EMPTY,0,I2,0); + VARABS(EMPTY,1,I2,0); + VARABS(NULL,0,NULL,0); + VARABS(NULL,1,NULL,0); + VARABS(I2,1,I2,1); + VARABS(I2,-1,I2,1); + VARABS(I4,1,I4,1); + VARABS(I4,-1,I4,1); + VARABS(UI1,1,UI1,1); + VARABS(R4,1,R4,1); + VARABS(R4,-1,R4,1); + VARABS(R8,1,R8,1); + VARABS(R8,-1,R8,1); + VARABS(DATE,1,DATE,1); + VARABS(DATE,-1,DATE,1); + V_VT(&v) = VT_CY; + V_CY(&v).int64 = -10000; + memset(&vDst,0,sizeof(vDst)); + hres = pVarAbs(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == 10000, + "VarAbs(CY): expected 0x0 got 0x%X\n", hres); + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buff, sizeof(buff)/sizeof(char)); + if (buff[1]) + { + trace("Skipping VarAbs(BSTR) as decimal separator is '%s'\n", buff); + return; + } else { + szNum[2] = buff[0]; + } + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = (BSTR)szNum; + memset(&vDst,0,sizeof(vDst)); + hres = pVarAbs(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_R8 && V_R8(&vDst) == 1.1, + "VarAbs: expected 0x0,%d,%g, got 0x%X,%d,%g\n", VT_R8, 1.1, hres, V_VT(&vDst), V_R8(&vDst)); +} + +static HRESULT (WINAPI *pVarNot)(LPVARIANT,LPVARIANT); + +#define VARNOT(vt,val,rvt,rval) \ + V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call1( __LINE__, pVarNot, &v, &exp ) + +static void test_VarNot(void) +{ + static const WCHAR szNum0[] = {'0','\0' }; + static const WCHAR szNum1[] = {'1','\0' }; + HRESULT hres; + VARIANT v, exp, vDst; + DECIMAL *pdec = &V_DECIMAL(&v); + CY *pcy = &V_CY(&v); + size_t i; + + CHECKPTR(VarNot); + + /* Test all possible V_VT values */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE vt; + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT hExpected = DISP_E_BADVARTYPE; + + SKIPTESTS(vt); + + memset(&v, 0, sizeof(v)); + V_VT(&v) = vt | ExtraFlags[i]; + V_VT(&vDst) = VT_EMPTY; + + switch (V_VT(&v)) + { + case VT_I1: case VT_UI1: case VT_I2: case VT_UI2: + case VT_INT: case VT_UINT: case VT_I4: case VT_UI4: + case VT_R4: case VT_R8: + case VT_DECIMAL: case VT_BOOL: case VT_NULL: case VT_EMPTY: + case VT_DATE: case VT_CY: + hExpected = S_OK; + break; + case VT_I8: case VT_UI8: + if (HAVE_OLEAUT32_I8) + hExpected = S_OK; + break; + case VT_RECORD: + if (HAVE_OLEAUT32_RECORD) + hExpected = DISP_E_TYPEMISMATCH; + break; + case VT_UNKNOWN: case VT_BSTR: case VT_DISPATCH: case VT_ERROR: + hExpected = DISP_E_TYPEMISMATCH; + break; + default: + if (IsValidVariantClearVT(vt, ExtraFlags[i]) && vt != VT_CLSID) + hExpected = DISP_E_TYPEMISMATCH; + break; + } + + hres = pVarNot(&v,&vDst); + ok(hres == hExpected, "VarNot: expected 0x%X, got 0x%X vt %d|0x%X\n", + hExpected, hres, vt, ExtraFlags[i]); + } + } + /* Test the values returned by all cases that can succeed */ + VARNOT(EMPTY,0,I2,-1); + VARNOT(EMPTY,1,I2,-1); + VARNOT(NULL,0,NULL,0); + VARNOT(NULL,1,NULL,0); + VARNOT(BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE); + VARNOT(BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE); + VARNOT(I1,-1,I4,0); + VARNOT(I1,0,I4,-1); + VARNOT(I2,-1,I2,0); + VARNOT(I2,0,I2,-1); + VARNOT(I2,1,I2,-2); + VARNOT(I4,1,I4,-2); + VARNOT(I4,0,I4,-1); + VARNOT(UI1,1,UI1,254); + VARNOT(UI1,0,UI1,255); + VARNOT(UI2,0,I4,-1); + VARNOT(UI2,1,I4,-2); + VARNOT(UI4,0,I4,-1); + VARNOT(UI4,1,I4,-2); + VARNOT(INT,0,I4,-1); + VARNOT(INT,1,I4,-2); + VARNOT(UINT,0,I4,-1); + VARNOT(UINT,1,I4,-2); + if (HAVE_OLEAUT32_I8) + { + VARNOT(I8,1,I8,-2); + VARNOT(I8,0,I8,-1); + VARNOT(UI8,0,I4,-1); + VARNOT(UI8,1,I4,-2); + } + VARNOT(R4,1,I4,-2); + VARNOT(R4,0,I4,-1); + VARNOT(R8,1,I4,-2); + VARNOT(R8,0,I4,-1); + VARNOT(DATE,1,I4,-2); + VARNOT(DATE,0,I4,-1); + VARNOT(BSTR,(BSTR)szNum0,I4,-1); + ok(V_VT(&v) == VT_BSTR && V_BSTR(&v) == szNum0, "VarNot(0): changed input\n"); + VARNOT(BSTR,(BSTR)szNum1,I4,-2); + ok(V_VT(&v) == VT_BSTR && V_BSTR(&v) == szNum1, "VarNot(1): changed input\n"); + + V_VT(&v) = VT_DECIMAL; + S(U(*pdec)).sign = DECIMAL_NEG; + S(U(*pdec)).scale = 0; + pdec->Hi32 = 0; + S1(U1(*pdec)).Mid32 = 0; + S1(U1(*pdec)).Lo32 = 1; + VARNOT(DECIMAL,*pdec,I4,0); + + pcy->int64 = 10000; + VARNOT(CY,*pcy,I4,-2); + + pcy->int64 = 0; + VARNOT(CY,*pcy,I4,-1); + + pcy->int64 = -1; + VARNOT(CY,*pcy,I4,-1); +} + +static HRESULT (WINAPI *pVarSub)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARSUB(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarSub, &left, &right, &exp ) + +static void test_VarSub(void) +{ + static const WCHAR sz12[] = {'1','2','\0'}; + VARIANT left, right, exp, result, cy, dec; + VARTYPE i; + BSTR lbstr, rbstr; + HRESULT hres, expectedhres; + double r; + + CHECKPTR(VarSub); + + lbstr = SysAllocString(sz12); + rbstr = SysAllocString(sz12); + + VariantInit(&left); + VariantInit(&right); + VariantInit(&result); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + + SKIPTESTS(rightvt); + expectedhres = S_OK; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + if (leftvt == VT_BSTR) + V_BSTR(&left) = lbstr; + V_VT(&right) = rightvt | ExtraFlags[i]; + if (rightvt == VT_BSTR) + V_BSTR(&right) = rbstr; + V_VT(&result) = VT_EMPTY; + resvt = VT_ERROR; + + /* All extra flags produce errors */ + if (ExtraFlags[i] == (VT_VECTOR|VT_BYREF|VT_RESERVED) || + ExtraFlags[i] == (VT_VECTOR|VT_RESERVED) || + ExtraFlags[i] == (VT_VECTOR|VT_BYREF) || + ExtraFlags[i] == (VT_BYREF|VT_RESERVED) || + ExtraFlags[i] == VT_VECTOR || + ExtraFlags[i] == VT_BYREF || + ExtraFlags[i] == VT_RESERVED) + { + expectedhres = DISP_E_BADVARTYPE; + resvt = VT_EMPTY; + } + else if (ExtraFlags[i] >= VT_ARRAY) + { + expectedhres = DISP_E_TYPEMISMATCH; + resvt = VT_EMPTY; + } + /* Native VarSub cannot handle: VT_I1, VT_UI2, VT_UI4, + VT_INT, VT_UINT and VT_UI8. Tested with WinXP */ + else if (!IsValidVariantClearVT(leftvt, ExtraFlags[i]) || + !IsValidVariantClearVT(rightvt, ExtraFlags[i]) || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_VARIANT || rightvt == VT_VARIANT || + leftvt == VT_I1 || rightvt == VT_I1 || + leftvt == VT_UI2 || rightvt == VT_UI2 || + leftvt == VT_UI4 || rightvt == VT_UI4 || + leftvt == VT_UI8 || rightvt == VT_UI8 || + leftvt == VT_INT || rightvt == VT_INT || + leftvt == VT_UINT || rightvt == VT_UINT || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN || + leftvt == VT_RECORD || rightvt == VT_RECORD) + { + if (leftvt == VT_RECORD && rightvt == VT_I8) + expectedhres = DISP_E_TYPEMISMATCH; + else if (leftvt < VT_UI1 && rightvt == VT_RECORD) + expectedhres = DISP_E_TYPEMISMATCH; + else if (leftvt >= VT_UI1 && rightvt == VT_RECORD) + expectedhres = DISP_E_TYPEMISMATCH; + else if (leftvt == VT_RECORD && rightvt <= VT_UI1) + expectedhres = DISP_E_TYPEMISMATCH; + else if (leftvt == VT_RECORD && rightvt > VT_UI1) + expectedhres = DISP_E_BADVARTYPE; + else + expectedhres = DISP_E_BADVARTYPE; + resvt = VT_EMPTY; + } + else if ((leftvt == VT_NULL && rightvt == VT_DISPATCH) || + (leftvt == VT_DISPATCH && rightvt == VT_NULL)) + resvt = VT_NULL; + else if (leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_ERROR || rightvt == VT_ERROR) + { + resvt = VT_EMPTY; + expectedhres = DISP_E_TYPEMISMATCH; + } + else if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if ((leftvt == VT_EMPTY && rightvt == VT_BSTR) || + (leftvt == VT_DATE && rightvt == VT_DATE) || + (leftvt == VT_BSTR && rightvt == VT_EMPTY) || + (leftvt == VT_BSTR && rightvt == VT_BSTR)) + resvt = VT_R8; + else if (leftvt == VT_DECIMAL || rightvt == VT_DECIMAL) + resvt = VT_DECIMAL; + else if (leftvt == VT_DATE || rightvt == VT_DATE) + resvt = VT_DATE; + else if (leftvt == VT_CY || rightvt == VT_CY) + resvt = VT_CY; + else if (leftvt == VT_R8 || rightvt == VT_R8) + resvt = VT_R8; + else if (leftvt == VT_BSTR || rightvt == VT_BSTR) { + resvt = VT_R8; + } else if (leftvt == VT_R4 || rightvt == VT_R4) { + if (leftvt == VT_I4 || rightvt == VT_I4 || + leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_R8; + else + resvt = VT_R4; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + else if (leftvt == VT_I4 || rightvt == VT_I4) + resvt = VT_I4; + else if (leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL || + (leftvt == VT_EMPTY && rightvt == VT_EMPTY)) + resvt = VT_I2; + else if (leftvt == VT_UI1 || rightvt == VT_UI1) + resvt = VT_UI1; + else + { + resvt = VT_EMPTY; + expectedhres = DISP_E_TYPEMISMATCH; + } + + hres = pVarSub(&left, &right, &result); + + ok(hres == expectedhres && V_VT(&result) == resvt, + "VarSub: %d|0x%X, %d|0x%X: Expected failure 0x%X, " + "got 0x%X, expected vt %d got vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], + expectedhres, hres, resvt, V_VT(&result)); + } + } + } + + /* Test returned values */ + VARSUB(I4,4,I4,2,I4,2); + VARSUB(I2,4,I2,2,I2,2); + VARSUB(I2,-13,I4,5,I4,-18); + VARSUB(I4,-13,I4,5,I4,-18); + VARSUB(I2,7,R4,0.5f,R4,6.5f); + VARSUB(R4,0.5f,I4,5,R8,-4.5); + VARSUB(R8,7.1,BOOL,0,R8,7.1); + VARSUB(BSTR,lbstr,I2,4,R8,8); + VARSUB(BSTR,lbstr,BOOL,1,R8,11); + VARSUB(BSTR,lbstr,R4,0.1f,R8,11.9); + VARSUB(R4,0.2f,BSTR,rbstr,R8,-11.8); + VARSUB(DATE,2.25,I4,7,DATE,-4.75); + VARSUB(DATE,1.25,R4,-1.7f,DATE,2.95); + + VARSUB(UI1, UI1_MAX, UI1, UI1_MAX, UI1, 0); + VARSUB(I2, I2_MAX, I2, I2_MAX, I2, 0); + VARSUB(I2, I2_MIN, I2, I2_MIN, I2, 0); + VARSUB(I4, I4_MAX, I4, I4_MAX, I4, 0); + VARSUB(I4, I4_MIN, I4, I4_MIN, I4, 0); + VARSUB(R4, R4_MAX, R4, R4_MAX, R4, 0.0f); + VARSUB(R4, R4_MAX, R4, R4_MIN, R4, R4_MAX - R4_MIN); + VARSUB(R4, R4_MIN, R4, R4_MIN, R4, 0.0f); + VARSUB(R8, R8_MAX, R8, R8_MIN, R8, R8_MAX - R8_MIN); + VARSUB(R8, R8_MIN, R8, R8_MIN, R8, 0.0); + + /* Manually test BSTR + BSTR */ + V_VT(&left) = VT_BSTR; + V_BSTR(&left) = lbstr; + V_VT(&right) = VT_BSTR; + V_BSTR(&right) = rbstr; + hres = VarSub(&left, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VarSub: expected coerced type VT_R8, got %s!\n", vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 0), + "VarSub: BSTR + BSTR, expected %f got %f\n", 0.0, V_R8(&result)); + + /* Manually test some VT_CY and VT_DECIMAL variants */ + V_VT(&cy) = VT_CY; + hres = VarCyFromI4(4711, &V_CY(&cy)); + ok(hres == S_OK, "VarCyFromI4 failed!\n"); + V_VT(&dec) = VT_DECIMAL; + hres = VarDecFromR8(-4.2, &V_DECIMAL(&dec)); + ok(hres == S_OK, "VarDecFromR4 failed!\n"); + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = VT_I4; + V_I4(&left) = -11; + V_VT(&right) = VT_UI1; + V_UI1(&right) = 9; + + hres = VarSub(&cy, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_CY, + "VarSub: expected coerced type VT_CY, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromCy(V_CY(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, 4702), + "VarSub: CY value %f, expected %f\n", r, (double)4720); + + hres = VarSub(&left, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_DECIMAL, + "VarSub: expected coerced type VT_DECIMAL, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromDec(&V_DECIMAL(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, -6.8), + "VarSub: DECIMAL value %f, expected %f\n", r, (double)-15.2); + + SysFreeString(lbstr); + SysFreeString(rbstr); +} + +static HRESULT (WINAPI *pVarMod)(LPVARIANT,LPVARIANT,LPVARIANT); + +static void test_Mod( int line, VARIANT *left, VARIANT *right, VARIANT *expected, HRESULT expres ) +{ + VARIANT result; + HRESULT hres; + + memset( &result, 0, sizeof(result) ); + hres = pVarMod( left, right, &result ); + ok_(__FILE__,line)( hres == expres, "wrong result %x/%x\n", hres, expres ); + if (hres == S_OK) + ok_(__FILE__,line)( is_expected_variant( &result, expected ), + "got %s expected %s\n", variantstr(&result), variantstr(expected) ); +} + +#define VARMOD(vt1,vt2,val1,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarMod, &left, &right, &exp ) + +#define VARMOD2(vt1,vt2,val1,val2,rvt,rval,hexpected) \ + V_VT(&left) = VT_##vt1; V_I4(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_I4(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_I4(&exp) = rval; \ + test_Mod( __LINE__, &left, &right, &exp, hexpected ) + +static void test_VarMod(void) +{ + VARIANT v1, v2, vDst, left, right, exp; + HRESULT hres; + HRESULT hexpected = 0; + static const WCHAR szNum0[] = {'1','2','5','\0'}; + static const WCHAR szNum1[] = {'1','0','\0'}; + int l, r; + BOOL lFound, rFound; + BOOL lValid, rValid; + BSTR strNum0, strNum1; + + CHECKPTR(VarMod); + + VARMOD(I1,BOOL,100,10,I4,0); + VARMOD(I1,I1,100,10,I4,0); + VARMOD(I1,UI1,100,10,I4,0); + VARMOD(I1,I2,100,10,I4,0); + VARMOD(I1,UI2,100,10,I4,0); + VARMOD(I1,I4,100,10,I4,0); + VARMOD(I1,UI4,100,10,I4,0); + VARMOD(I1,R4,100,10,I4,0); + VARMOD(I1,R8,100,10,I4,0); + + VARMOD(UI1,BOOL,100,10,I2,0); + VARMOD(UI1,I1,100,10,I4,0); + VARMOD(UI1,UI1,100,10,UI1,0); + VARMOD(UI1,I2,100,10,I2,0); + VARMOD(UI1,UI2,100,10,I4,0); + VARMOD(UI1,I4,100,10,I4,0); + VARMOD(UI1,UI4,100,10,I4,0); + VARMOD(UI1,R4,100,10,I4,0); + VARMOD(UI1,R8,100,10,I4,0); + + VARMOD(I2,BOOL,100,10,I2,0); + VARMOD(I2,I1,100,10,I4,0); + VARMOD(I2,UI1,100,10,I2,0); + VARMOD(I2,I2,100,10,I2,0); + VARMOD(I2,UI2,100,10,I4,0); + VARMOD(I2,I4,100,10,I4,0); + VARMOD(I2,UI4,100,10,I4,0); + VARMOD(I2,R4,100,10,I4,0); + VARMOD(I2,R8,100,10,I4,0); + + VARMOD(I4,BOOL,100,10,I4,0); + VARMOD(I4,I1,100,10,I4,0); + VARMOD(I4,UI1,100,10,I4,0); + VARMOD(I4,I2,100,10,I4,0); + VARMOD(I4,UI2,100,10,I4,0); + VARMOD(I4,I4,100,10,I4,0); + VARMOD(I4,UI4,100,10,I4,0); + VARMOD(I4,R4,100,10,I4,0); + VARMOD(I4,R8,100,10,I4,0); + VARMOD(UI4,BOOL,100,10,I4,0); + VARMOD(UI4,I1,100,10,I4,0); + VARMOD(UI4,UI1,100,10,I4,0); + VARMOD(UI4,I2,100,10,I4,0); + VARMOD(UI4,UI2,100,10,I4,0); + VARMOD(UI4,I4,100,10,I4,0); + VARMOD(UI4,UI4,100,10,I4,0); + VARMOD(UI4,R4,100,10,I4,0); + VARMOD(UI4,R8,100,10,I4,0); + VARMOD(R4,BOOL,100,10,I4,0); + VARMOD(R4,I1,100,10,I4,0); + VARMOD(R4,UI1,100,10,I4,0); + VARMOD(R4,I2,100,10,I4,0); + VARMOD(R4,UI2,100,10,I4,0); + VARMOD(R4,I4,100,10,I4,0); + VARMOD(R4,UI4,100,10,I4,0); + VARMOD(R4,R4,100,10,I4,0); + VARMOD(R4,R8,100,10,I4,0); + VARMOD(R8,BOOL,100,10,I4,0); + VARMOD(R8,I1,100,10,I4,0); + VARMOD(R8,UI1,100,10,I4,0); + VARMOD(R8,I2,100,10,I4,0); + VARMOD(R8,UI2,100,10,I4,0); + VARMOD(R8,I4,100,10,I4,0); + VARMOD(R8,UI4,100,10,I4,0); + VARMOD(R8,R4,100,10,I4,0); + VARMOD(R8,R8,100,10,I4,0); + + VARMOD(INT,INT,100,10,I4,0); + VARMOD(INT,UINT,100,10,I4,0); + + VARMOD(BOOL,BOOL,100,10,I2,0); + VARMOD(BOOL,I1,100,10,I4,0); + VARMOD(BOOL,UI1,100,10,I2,0); + VARMOD(BOOL,I2,100,10,I2,0); + VARMOD(BOOL,UI2,100,10,I4,0); + VARMOD(BOOL,I4,100,10,I4,0); + VARMOD(BOOL,UI4,100,10,I4,0); + VARMOD(BOOL,R4,100,10,I4,0); + VARMOD(BOOL,R8,100,10,I4,0); + VARMOD(BOOL,DATE,100,10,I4,0); + + VARMOD(DATE,BOOL,100,10,I4,0); + VARMOD(DATE,I1,100,10,I4,0); + VARMOD(DATE,UI1,100,10,I4,0); + VARMOD(DATE,I2,100,10,I4,0); + VARMOD(DATE,UI2,100,10,I4,0); + VARMOD(DATE,I4,100,10,I4,0); + VARMOD(DATE,UI4,100,10,I4,0); + VARMOD(DATE,R4,100,10,I4,0); + VARMOD(DATE,R8,100,10,I4,0); + VARMOD(DATE,DATE,100,10,I4,0); + + strNum0 = SysAllocString(szNum0); + strNum1 = SysAllocString(szNum1); + VARMOD(BSTR,BSTR,strNum0,strNum1,I4,5); + VARMOD(BSTR,I1,strNum0,10,I4,5); + VARMOD(BSTR,I2,strNum0,10,I4,5); + VARMOD(BSTR,I4,strNum0,10,I4,5); + VARMOD(BSTR,R4,strNum0,10,I4,5); + VARMOD(BSTR,R8,strNum0,10,I4,5); + VARMOD(I4,BSTR,125,strNum1,I4,5); + + if (HAVE_OLEAUT32_I8) + { + VARMOD(BOOL,I8,100,10,I8,0); + VARMOD(I1,I8,100,10,I8,0); + VARMOD(UI1,I8,100,10,I8,0); + VARMOD(I2,I8,100,10,I8,0); + VARMOD(I4,I8,100,10,I8,0); + VARMOD(UI4,I8,100,10,I8,0); + VARMOD(R4,I8,100,10,I8,0); + VARMOD(R8,I8,100,10,I8,0); + VARMOD(DATE,I8,100,10,I8,0); + + VARMOD(I8,BOOL,100,10,I8,0); + VARMOD(I8,I1,100,10,I8,0); + VARMOD(I8,UI1,100,10,I8,0); + VARMOD(I8,I2,100,10,I8,0); + VARMOD(I8,UI2,100,10,I8,0); + VARMOD(I8,I4,100,10,I8,0); + VARMOD(I8,UI4,100,10,I8,0); + VARMOD(I8,R4,100,10,I8,0); + VARMOD(I8,R8,100,10,I8,0); + VARMOD(I8,I8,100,10,I8,0); + + VARMOD(BSTR,I8,strNum0,10,I8,5); + } + + /* test all combinations of types */ + for(l = 0; l < VT_BSTR_BLOB; l++) + { + SKIPTESTS(l); + + for(r = 0; r < VT_BSTR_BLOB; r++) + { + SKIPTESTS(r); + + if(l == VT_BSTR) continue; + if(l == VT_DISPATCH) continue; + if(r == VT_BSTR) continue; + if(r == VT_DISPATCH) continue; + + lFound = TRUE; + lValid = TRUE; + switch(l) + { + case VT_EMPTY: + case VT_NULL: + case VT_I1: + case VT_UI1: + case VT_I2: + case VT_UI2: + case VT_I4: + case VT_I8: + case VT_UI4: + case VT_UI8: + case VT_INT: + case VT_UINT: + case VT_R4: + case VT_R8: + case VT_BOOL: + case VT_DATE: + case VT_CY: + case VT_DECIMAL: + hexpected = S_OK; + break; + case VT_ERROR: + case VT_VARIANT: + case VT_UNKNOWN: + case VT_RECORD: + lValid = FALSE; + break; + default: + lFound = FALSE; + hexpected = DISP_E_BADVARTYPE; + break; + } + + rFound = TRUE; + rValid = TRUE; + switch(r) + { + case VT_EMPTY: + case VT_NULL: + case VT_I1: + case VT_UI1: + case VT_I2: + case VT_UI2: + case VT_I4: + case VT_I8: + case VT_UI4: + case VT_UI8: + case VT_INT: + case VT_UINT: + case VT_R4: + case VT_R8: + case VT_BOOL: + case VT_DATE: + case VT_DECIMAL: + case VT_CY: + hexpected = S_OK; + break; + case VT_ERROR: + case VT_VARIANT: + case VT_UNKNOWN: + case VT_RECORD: + rValid = FALSE; + break; + default: + rFound = FALSE; + break; + } + + if(((l == VT_I8) && (r == VT_INT)) || ((l == VT_INT) && (r == VT_I8))) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((l == VT_EMPTY) && (r == VT_NULL)) + { + hexpected = S_OK; + } else if((l == VT_NULL) && (r == VT_EMPTY)) + { + hexpected = S_OK; + } else if((l == VT_EMPTY) && (r == VT_CY)) + { + hexpected = S_OK; + } else if((l == VT_EMPTY) && (r == VT_RECORD)) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((r == VT_EMPTY) && lFound && lValid) + { + hexpected = DISP_E_DIVBYZERO; + } else if((l == VT_ERROR) || ((r == VT_ERROR) && lFound && lValid)) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((l == VT_NULL) && (r == VT_NULL)) + { + hexpected = S_OK; + } else if((l == VT_VARIANT) || ((r == VT_VARIANT) && lFound && lValid)) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((l == VT_NULL) && (r == VT_RECORD)) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((l == VT_I8) && (r == VT_DECIMAL)) + { + hexpected = S_OK; + } else if((l == VT_DECIMAL) && (r == VT_I8)) + { + hexpected = S_OK; + } else if((l == VT_UNKNOWN) || ((r == VT_UNKNOWN) && lFound && lValid)) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((l == VT_NULL) && rFound) + { + hexpected = S_OK; + } else if(l == VT_RECORD) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((r == VT_RECORD) && lValid && lFound) + { + hexpected = DISP_E_TYPEMISMATCH; + } else if((l == VT_EMPTY) && (r == VT_EMPTY)) + { + hexpected = DISP_E_DIVBYZERO; + } else if((l == VT_CY) && !rFound) + { + hexpected = DISP_E_BADVARTYPE; + } else if(lFound && !rFound) + { + hexpected = DISP_E_BADVARTYPE; + } else if(!lFound && rFound) + { + hexpected = DISP_E_BADVARTYPE; + } else if((r == VT_NULL) && lFound && lValid) + { + hexpected = S_OK; + } else if((l == VT_NULL) || (r == VT_NULL)) + { + hexpected = DISP_E_BADVARTYPE; + } else if((l == VT_VARIANT) || (r == VT_VARIANT)) + { + hexpected = DISP_E_BADVARTYPE; + } else if(lFound && !rFound) + { + hexpected = DISP_E_BADVARTYPE; + } else if(!lFound && !rFound) + { + hexpected = DISP_E_BADVARTYPE; + } + + V_VT(&v1) = l; + V_VT(&v2) = r; + + if(l == VT_CY) + V_CY(&v1).int64 = 1000000; + else if(l == VT_R4) + V_R4(&v1) = 100; + else if(l == VT_R8) + V_R8(&v1) = 100; + else if(l == VT_UI8) + V_UI8(&v1) = 100; + else if(l == VT_I8) + V_I8(&v1) = 100; + else if(l == VT_DATE) + V_DATE(&v1) = 1000; + else if (l == VT_DECIMAL) + { + V_DECIMAL(&v1).Hi32 = 0; + U1(V_DECIMAL(&v1)).Lo64 = 100; + U(V_DECIMAL(&v1)).signscale = 0; + } + else + V_I4(&v1) = 10000; + + if(r == VT_CY) + V_CY(&v2).int64 = 10000; + else if(r == VT_R4) + V_R4(&v2) = 100; + else if(r == VT_R8) + V_R8(&v2) = 100; + else if(r == VT_UI8) + V_UI8(&v2) = 100; + else if(r == VT_I8) + V_I8(&v2) = 100; + else if(r == VT_DATE) + V_DATE(&v2) = 1000; + else if (r == VT_DECIMAL) + { + V_DECIMAL(&v2).Hi32 = 0; + U1(V_DECIMAL(&v2)).Lo64 = 100; + U(V_DECIMAL(&v2)).signscale = 0; + } + else + V_I4(&v2) = 10000; + + if ((l != VT_I8 && l != VT_UI8 && r != VT_I8 && r != VT_UI8) || HAVE_OLEAUT32_I8) + { + hres = pVarMod(&v1,&v2,&vDst); + ok(hres == hexpected, + "VarMod: expected 0x%x, got 0x%X for l type of %d, r type of %d,\n", hexpected, hres, l, r); + } + } + } + + + /****************************/ + /* test some bad parameters */ + VARMOD(I4,I4,-1,-1,I4,0); + + /* test modulus with zero */ + VARMOD2(I4,I4,100,0,EMPTY,0,DISP_E_DIVBYZERO); + + VARMOD(I4,I4,0,10,I4,0); /* test 0 mod 10 */ + + /* right parameter is type empty */ + VARMOD2(I4,EMPTY,100,10,EMPTY,0,DISP_E_DIVBYZERO); + + /* left parameter is type empty */ + VARMOD2(EMPTY,I4,100,10,I4,0,S_OK); + + /* mod with a null left value */ + VARMOD2(NULL,I4,125,10,NULL,0,S_OK); + + /* mod with a null right value */ + VARMOD2(I4,NULL,100,10,NULL,0,S_OK); + + /* void left value */ + VARMOD2(VOID,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + + /* void right value */ + VARMOD2(I4,VOID,100,10,EMPTY,0,DISP_E_BADVARTYPE); + + /* null left value, void right value */ + VARMOD2(NULL,VOID,100,10,EMPTY, 0, DISP_E_BADVARTYPE); + + /* void left value, null right value */ + VARMOD2(VOID,NULL,100,10,EMPTY,0,DISP_E_BADVARTYPE); + + /* some currencies */ + V_VT(&v1) = VT_CY; + V_VT(&v2) = VT_CY; + V_CY(&v1).int64 = 100000; + V_CY(&v2).int64 = 100000; + hres = pVarMod(&v1,&v2,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I4 && V_I4(&vDst) == 0, + "VarMod: expected 0x%x,%d,%d, got 0x%X,%d,%d\n", S_OK, VT_I4, 0, hres, V_VT(&vDst), V_I4(&vDst)); + + V_VT(&v1) = VT_I4; + V_VT(&v2) = VT_CY; + V_I4(&v1) = 100; + V_CY(&v2).int64 = 100000; + hres = pVarMod(&v1,&v2,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I4 && V_I4(&vDst) == 0, + "VarMod: expected 0x%x,%d,%d, got 0x%X,%d,%d\n", S_OK, VT_I4, 0, hres, V_VT(&vDst), V_I4(&vDst)); + + /* some decimals */ + V_VT(&v1) = VT_DECIMAL; + V_VT(&v2) = VT_DECIMAL; + VarDecFromI4(100, &V_DECIMAL(&v1)); + VarDecFromI4(10, &V_DECIMAL(&v2)); + hres = pVarMod(&v1,&v2,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I4 && V_I4(&vDst) == 0, + "VarMod: expected 0x%x,%d,%d, got 0x%X,%d,%d\n", S_OK, VT_I4, 0, hres, V_VT(&vDst), V_I4(&vDst)); + + V_VT(&v1) = VT_I4; + V_VT(&v2) = VT_DECIMAL; + V_I4(&v1) = 100; + VarDecFromI4(10, &V_DECIMAL(&v2)); + hres = pVarMod(&v1,&v2,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I4 && V_I4(&vDst) == 0, + "VarMod: expected 0x%x,%d,%d, got 0x%X,%d,%d\n", S_OK, VT_I4, 0, hres, V_VT(&vDst), V_I4(&vDst)); + + VARMOD2(UINT,I4,100,10,I4,0,S_OK); + + /* test that an error results in the type of the result changing but not its value */ + V_VT(&v1) = VT_UNKNOWN; + V_VT(&v2) = VT_EMPTY; + V_I4(&v1) = 100; + V_CY(&v2).int64 = 100000; + V_VT(&vDst) = VT_I4; + V_I4(&vDst) = 1231; + hres = pVarMod(&v1,&v2,&vDst); + ok(hres == DISP_E_TYPEMISMATCH && V_VT(&vDst) == VT_EMPTY && V_I4(&vDst) == 1231, + "VarMod: expected 0x%x,%d,%d, got 0x%X,%d,%d\n", DISP_E_TYPEMISMATCH, VT_EMPTY, 1231, hres, V_VT(&vDst), V_I4(&vDst)); + + + /* test some invalid types */ + /*TODO: not testing VT_DISPATCH */ + if (HAVE_OLEAUT32_I8) + { + VARMOD2(I8,INT,100,10,EMPTY,0,DISP_E_TYPEMISMATCH); + } + VARMOD2(ERROR,I4,100,10,EMPTY,0,DISP_E_TYPEMISMATCH); + VARMOD2(VARIANT,I4,100,10,EMPTY,0,DISP_E_TYPEMISMATCH); + VARMOD2(UNKNOWN,I4,100,10,EMPTY,0,DISP_E_TYPEMISMATCH); + VARMOD2(VOID,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(HRESULT,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(PTR,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(SAFEARRAY,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(CARRAY,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(USERDEFINED,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(LPSTR,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(LPWSTR,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(RECORD,I4,100,10,EMPTY,0,DISP_E_TYPEMISMATCH); + VARMOD2(FILETIME,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(BLOB,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(STREAM,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(STORAGE,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(STREAMED_OBJECT,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(STORED_OBJECT,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(BLOB_OBJECT,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(CF,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(CLSID,CLSID,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(VECTOR,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(ARRAY,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + VARMOD2(BYREF,I4,100,10,EMPTY,0,DISP_E_BADVARTYPE); + + /* test some more invalid types */ + V_VT(&v1) = 456; + V_VT(&v2) = 234; + V_I4(&v1) = 100; + V_I4(&v2)= 10; + hres = pVarMod(&v1,&v2,&vDst); + ok(hres == DISP_E_BADVARTYPE && V_VT(&vDst) == VT_EMPTY, + "VarMod: expected 0x%x,%d, got 0x%X,%d\n", DISP_E_BADVARTYPE, VT_EMPTY, hres, V_VT(&vDst)); +} + +static HRESULT (WINAPI *pVarFix)(LPVARIANT,LPVARIANT); + +#define VARFIX(vt,val,rvt,rval) \ + V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call1( __LINE__, pVarFix, &v, &exp ) + +static void test_VarFix(void) +{ + static const WCHAR szNumMinus1[] = {'-','1','\0' }; + HRESULT hres; + VARIANT v, exp, vDst; + DECIMAL *pdec = &V_DECIMAL(&v); + CY *pcy = &V_CY(&v); + size_t i; + + CHECKPTR(VarFix); + + /* Test all possible V_VT values */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE vt; + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT bFail = TRUE; + + SKIPTESTS(vt); + + memset(&v, 0, sizeof(v)); + V_VT(&v) = vt | ExtraFlags[i]; + V_VT(&vDst) = VT_EMPTY; + + switch (V_VT(&v)) + { + case VT_UI1: case VT_I2: case VT_I4: case VT_R4: case VT_R8: + case VT_DECIMAL: case VT_BOOL: case VT_NULL: case VT_EMPTY: + case VT_DATE: case VT_CY: + bFail = FALSE; + break; + case VT_I8: + if (HAVE_OLEAUT32_I8) + bFail = FALSE; + break; + } + + hres = pVarFix(&v,&vDst); + if (bFail) + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarFix: expected failure, got 0x%X vt %d|0x%X\n", + hres, vt, ExtraFlags[i]); + else + ok(hres == S_OK, "VarFix: expected S_OK, got 0x%X vt %d|0x%X\n", + hres, vt, ExtraFlags[i]); + } + } + + VARFIX(BOOL,VARIANT_TRUE,I2,VARIANT_TRUE); + VARFIX(BOOL,VARIANT_FALSE,I2,0); + VARFIX(BOOL,1,I2,1); + VARFIX(UI1,1,UI1,1); + VARFIX(I2,-1,I2,-1); + VARFIX(I4,-1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARFIX(I8,-1,I8,-1); + } + VARFIX(R4,1.4f,R4,1); + VARFIX(R4,1.5f,R4,1); + VARFIX(R4,1.6f,R4,1); + VARFIX(R4,-1.4f,R4,-1); + VARFIX(R4,-1.5f,R4,-1); + VARFIX(R4,-1.6f,R4,-1); + /* DATE & R8 round as for R4 */ + VARFIX(DATE,-1,DATE,-1); + VARFIX(R8,-1,R8,-1); + VARFIX(BSTR,(BSTR)szNumMinus1,R8,-1); + + V_VT(&v) = VT_EMPTY; + hres = pVarFix(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I2 && V_I2(&vDst) == 0, + "VarFix: expected 0x0,%d,0 got 0x%X,%d,%d\n", VT_EMPTY, + hres, V_VT(&vDst), V_I2(&vDst)); + + V_VT(&v) = VT_NULL; + hres = pVarFix(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_NULL, + "VarFix: expected 0x0,%d got 0x%X,%d\n", VT_NULL, hres, V_VT(&vDst)); + + V_VT(&v) = VT_DECIMAL; + S(U(*pdec)).sign = DECIMAL_NEG; + S(U(*pdec)).scale = 0; + pdec->Hi32 = 0; + S1(U1(*pdec)).Mid32 = 0; + S1(U1(*pdec)).Lo32 = 1; + hres = pVarFix(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && !memcmp(&v, &vDst, sizeof(v)), + "VarFix: expected 0x0,%d,identical, got 0x%X,%d\n", VT_DECIMAL, + hres, V_VT(&vDst)); + + /* FIXME: Test some fractional decimals when VarDecFix is implemented */ + + V_VT(&v) = VT_CY; + pcy->int64 = -10000; + hres = pVarFix(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == -10000, + "VarFix: VT_CY wrong, hres=0x%X\n", hres); + + V_VT(&v) = VT_CY; + pcy->int64 = -16000; + hres = pVarFix(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == -10000, + "VarFix: VT_CY wrong, hres=0x%X\n", hres); +} + +static HRESULT (WINAPI *pVarInt)(LPVARIANT,LPVARIANT); + +#define VARINT(vt,val,rvt,rval) \ + V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call1( __LINE__, pVarInt, &v, &exp ) + +static void test_VarInt(void) +{ + static const WCHAR szNumMinus1[] = {'-','1','\0' }; + HRESULT hres; + VARIANT v, exp, vDst; + DECIMAL *pdec = &V_DECIMAL(&v); + CY *pcy = &V_CY(&v); + size_t i; + + CHECKPTR(VarInt); + + /* Test all possible V_VT values */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE vt; + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT bFail = TRUE; + + SKIPTESTS(vt); + + memset(&v, 0, sizeof(v)); + V_VT(&v) = vt | ExtraFlags[i]; + V_VT(&vDst) = VT_EMPTY; + + switch (V_VT(&v)) + { + case VT_UI1: case VT_I2: case VT_I4: case VT_R4: case VT_R8: + case VT_DECIMAL: case VT_BOOL: case VT_NULL: case VT_EMPTY: + case VT_DATE: case VT_CY: + bFail = FALSE; + break; + case VT_I8: + if (HAVE_OLEAUT32_I8) + bFail = FALSE; + break; + } + + hres = pVarInt(&v,&vDst); + if (bFail) + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarInt: expected failure, got 0x%X vt %d|0x%X\n", + hres, vt, ExtraFlags[i]); + else + ok(hres == S_OK, "VarInt: expected S_OK, got 0x%X vt %d|0x%X\n", + hres, vt, ExtraFlags[i]); + } + } + + VARINT(BOOL,VARIANT_TRUE,I2,VARIANT_TRUE); + VARINT(BOOL,VARIANT_FALSE,I2,0); + VARINT(BOOL,1,I2,1); + VARINT(UI1,1,UI1,1); + VARINT(I2,-1,I2,-1); + VARINT(I4,-1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARINT(I8,-1,I8,-1); + } + VARINT(R4,1.4f,R4,1); + VARINT(R4,1.5f,R4,1); + VARINT(R4,1.6f,R4,1); + VARINT(R4,-1.4f,R4,-2); /* Note these 3 are different from VarFix */ + VARINT(R4,-1.5f,R4,-2); + VARINT(R4,-1.6f,R4,-2); + /* DATE & R8 round as for R4 */ + VARINT(DATE,-1,DATE,-1); + VARINT(R8,-1,R8,-1); + VARINT(BSTR,(BSTR)szNumMinus1,R8,-1); + + V_VT(&v) = VT_EMPTY; + hres = pVarInt(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I2 && V_I2(&vDst) == 0, + "VarInt: expected 0x0,%d,0 got 0x%X,%d,%d\n", VT_EMPTY, + hres, V_VT(&vDst), V_I2(&vDst)); + + V_VT(&v) = VT_NULL; + hres = pVarInt(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_NULL, + "VarInt: expected 0x0,%d got 0x%X,%d\n", VT_NULL, hres, V_VT(&vDst)); + + V_VT(&v) = VT_DECIMAL; + S(U(*pdec)).sign = DECIMAL_NEG; + S(U(*pdec)).scale = 0; + pdec->Hi32 = 0; + S1(U1(*pdec)).Mid32 = 0; + S1(U1(*pdec)).Lo32 = 1; + hres = pVarInt(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && !memcmp(&v, &vDst, sizeof(v)), + "VarInt: expected 0x0,%d,identical, got 0x%X,%d\n", VT_DECIMAL, + hres, V_VT(&vDst)); + + /* FIXME: Test some fractional decimals when VarDecInt is implemented */ + + V_VT(&v) = VT_CY; + pcy->int64 = -10000; + hres = pVarInt(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == -10000, + "VarInt: VT_CY wrong, hres=0x%X\n", hres); + + V_VT(&v) = VT_CY; + pcy->int64 = -11000; + hres = pVarInt(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == -20000, + "VarInt: VT_CY wrong, hres=0x%X 0x%x%08x\n", + hres, (DWORD)(V_CY(&vDst).int64 >> 32), (DWORD)V_CY(&vDst).int64); +} + +static HRESULT (WINAPI *pVarNeg)(LPVARIANT,LPVARIANT); + +#define VARNEG(vt,val,rvt,rval) \ + V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call1( __LINE__, pVarNeg, &v, &exp ) + +static void test_VarNeg(void) +{ + static const WCHAR szNumMinus1[] = {'-','1','\0' }; + static const WCHAR szNum1[] = {'1','\0' }; + HRESULT hres; + VARIANT v, exp, vDst; + DECIMAL *pdec = &V_DECIMAL(&v); + CY *pcy = &V_CY(&v); + size_t i; + + CHECKPTR(VarNeg); + + /* Test all possible V_VT values. But don't test the exact return values + * except for success/failure, since M$ made a hash of them in the + * native version. This at least ensures (as with all tests here) that + * we will notice if/when new vtypes/flags are added in native. + */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE vt; + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT bFail = TRUE; + + SKIPTESTS(vt); + + memset(&v, 0, sizeof(v)); + V_VT(&v) = vt | ExtraFlags[i]; + V_VT(&vDst) = VT_EMPTY; + + switch (V_VT(&v)) + { + case VT_UI1: case VT_I2: case VT_I4: + case VT_R4: case VT_R8: + case VT_DECIMAL: case VT_BOOL: case VT_NULL: case VT_EMPTY: + case VT_DATE: case VT_CY: + bFail = FALSE; + break; + case VT_I8: + if (HAVE_OLEAUT32_I8) + bFail = FALSE; + } + + hres = pVarNeg(&v,&vDst); + if (bFail) + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarNeg: expected failure, got 0x%X vt %d|0x%X\n", + hres, vt, ExtraFlags[i]); + else + ok(hres == S_OK, "VarNeg: expected S_OK, got 0x%X vt %d|0x%X\n", + hres, vt, ExtraFlags[i]); + } + } + + VARNEG(BOOL,VARIANT_TRUE,I2,1); + VARNEG(BOOL,VARIANT_FALSE,I2,0); + VARNEG(BOOL,1,I2,-1); + VARNEG(UI1,1,I2,-1); + VARNEG(UI1,254,I2,-254); + VARNEG(I2,-32768,I4,32768); + VARNEG(I2,-1,I2,1); + VARNEG(I2,1,I2,-1); + VARNEG(I4,-((int)(~0u >> 1)) - 1,R8,-2147483648u); + VARNEG(I4,-1,I4,1); + VARNEG(I4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARNEG(I8,1,I8,-1); + VARNEG(I8,-1,I8,1); + } + VARNEG(R4,1,R4,-1); + VARNEG(R4,-1,R4,1); + VARNEG(DATE,1,DATE,-1); + VARNEG(DATE,-1,DATE,1); + VARNEG(R8,1,R8,-1); + VARNEG(R8,-1,R8,1); + VARNEG(BSTR,(BSTR)szNumMinus1,R8,1); + VARNEG(BSTR,(BSTR)szNum1,R8,-1); + + V_VT(&v) = VT_EMPTY; + hres = pVarNeg(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I2 && V_I2(&vDst) == 0, + "VarNeg: expected 0x0,%d,0 got 0x%X,%d,%d\n", VT_EMPTY, + hres, V_VT(&vDst), V_I2(&vDst)); + + V_VT(&v) = VT_NULL; + hres = pVarNeg(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_NULL, + "VarNeg: expected 0x0,%d got 0x%X,%d\n", VT_NULL, hres, V_VT(&vDst)); + + V_VT(&v) = VT_DECIMAL; + S(U(*pdec)).sign = DECIMAL_NEG; + S(U(*pdec)).scale = 0; + pdec->Hi32 = 0; + S1(U1(*pdec)).Mid32 = 0; + S1(U1(*pdec)).Lo32 = 1; + hres = pVarNeg(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && + S(U(V_DECIMAL(&vDst))).sign == 0, + "VarNeg: expected 0x0,%d,0x00, got 0x%X,%d,%02x\n", VT_DECIMAL, + hres, V_VT(&vDst), S(U(V_DECIMAL(&vDst))).sign); + + S(U(*pdec)).sign = 0; + hres = pVarNeg(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && + S(U(V_DECIMAL(&vDst))).sign == DECIMAL_NEG, + "VarNeg: expected 0x0,%d,0x7f, got 0x%X,%d,%02x\n", VT_DECIMAL, + hres, V_VT(&vDst), S(U(V_DECIMAL(&vDst))).sign); + + V_VT(&v) = VT_CY; + pcy->int64 = -10000; + hres = pVarNeg(&v,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == 10000, + "VarNeg: VT_CY wrong, hres=0x%X\n", hres); +} + +static HRESULT (WINAPI *pVarRound)(LPVARIANT,int,LPVARIANT); + +static void test_Round( int line, VARIANT *arg, int deci, VARIANT *expected ) +{ + VARIANT result; + HRESULT hres; + + memset( &result, 0, sizeof(result) ); + hres = pVarRound( arg, deci, &result ); + ok_(__FILE__,line)( hres == S_OK, "wrong result %x\n", hres ); + if (hres == S_OK) + ok_(__FILE__,line)( is_expected_variant( &result, expected ), + "got %s expected %s\n", variantstr(&result), variantstr(expected) ); +} +#define VARROUND(vt,val,deci,rvt,rval) \ + V_VT(&v) = VT_##vt; V_##vt(&v) = val; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_Round( __LINE__, &v, deci, &exp ) + +static void test_VarRound(void) +{ + static WCHAR szNumMin[] = {'-','1','.','4','5','\0' }; + static WCHAR szNum[] = {'1','.','4','5','\0' }; + HRESULT hres; + VARIANT v, exp, vDst; + CY *pcy = &V_CY(&v); + char buff[8]; + + CHECKPTR(VarRound); + + /* first check valid integer types */ + VARROUND(BOOL,VARIANT_TRUE,0,I2,-1); + VARROUND(BOOL,VARIANT_FALSE,0,I2,0); + VARROUND(BOOL,1,0,I2,1); + VARROUND(UI1,1,0,UI1,1); + VARROUND(UI1,254,0,UI1,254); + VARROUND(I2,-32768,0,I2,-32768); + VARROUND(I2,-1,0,I2,-1); + VARROUND(I2,1,0,I2,1); + VARROUND(I4,-((int)(~0u >> 1)) - 1,0,I4,-((int)(~0u >> 1)) - 1); + VARROUND(I4,-1,0,I4,-1); + VARROUND(I4,1,0,I4,1); + + + /* MSDN states that rounding of R4/R8 is dependent on the underlying + * bit pattern of the number and so is architecture dependent. In this + * case Wine returns .2 (which is more correct) and Native returns .3 + */ + + VARROUND(R4,1.0f,0,R4,1.0f); + VARROUND(R4,-1.0f,0,R4,-1.0f); + VARROUND(R8,1.0,0,R8,1.0); + VARROUND(R8,-1.0,0,R8,-1.0); + + /* floating point numbers aren't exactly equal and we can't just + * compare the first few digits. */ + VARROUND(DATE,1.451,1,DATE,1.5); + VARROUND(DATE,-1.45,1,DATE,-1.4); + + /* replace the decimal separator */ + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, buff, sizeof(buff)/sizeof(char)); + if (!buff[1]) { + szNumMin[2] = buff[0]; + szNum[1] = buff[0]; + VARROUND(BSTR,(BSTR)szNumMin,1,R8,-1.40); + if (0) { VARROUND(BSTR,(BSTR)szNum,1,R8,1.50); } + } else { + skip("Skipping VarRound(BSTR) as decimal separator is '%s'\n", buff); + } + + VARROUND(R4,1.23456f,0,R4,1.0f); + VARROUND(R4,1.23456f,1,R4,1.2f); + VARROUND(R4,1.23456f,2,R4,1.23f); + VARROUND(R4,1.23456f,3,R4,1.235f); + VARROUND(R4,1.23456f,4,R4,1.2346f); + VARROUND(R4,-1.23456f,0,R4,-1.0f); + VARROUND(R4,-1.23456f,1,R4,-1.2f); + VARROUND(R4,-1.23456f,2,R4,-1.23f); + VARROUND(R4,-1.23456f,3,R4,-1.235f); + VARROUND(R4,-1.23456f,4,R4,-1.2346f); + + VARROUND(R8,1.23456,0,R8,1.0); + VARROUND(R8,1.23456,1,R8,1.2); + VARROUND(R8,1.23456,2,R8,1.23); + VARROUND(R8,1.23456,3,R8,1.235); + VARROUND(R8,1.23456,4,R8,1.2346); + VARROUND(R8,-1.23456,0,R8,-1.0); + VARROUND(R8,-1.23456,1,R8,-1.2); + VARROUND(R8,-1.23456,2,R8,-1.23); + VARROUND(R8,-1.23456,3,R8,-1.235); + VARROUND(R8,-1.23456,4,R8,-1.2346); + + V_VT(&v) = VT_EMPTY; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_I2 && V_I2(&vDst) == 0, + "VarRound: expected 0x0,%d,0 got 0x%X,%d,%d\n", VT_EMPTY, + hres, V_VT(&vDst), V_I2(&vDst)); + + V_VT(&v) = VT_NULL; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_NULL, + "VarRound: expected 0x0,%d got 0x%X,%d\n", VT_NULL, hres, V_VT(&vDst)); + + /* not yet implemented so no use testing yet + todo_wine { + DECIMAL *pdec = &V_DECIMAL(&v); + V_VT(&v) = VT_DECIMAL; + S(U(*pdec)).sign = DECIMAL_NEG; + S(U(*pdec)).scale = 0; + pdec->Hi32 = 0; + S1(U1(*pdec)).Mid32 = 0; + S1(U1(*pdec)).Lo32 = 1; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && + S(U(V_DECIMAL(&vDst))).sign == 0, + "VarRound: expected 0x0,%d,0x00, got 0x%X,%d,%02x\n", VT_DECIMAL, + hres, V_VT(&vDst), S(U(V_DECIMAL(&vDst))).sign); + + S(U(*pdec)).sign = 0; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && + S(U(V_DECIMAL(&vDst))).sign == DECIMAL_NEG, + "VarRound: expected 0x0,%d,0x7f, got 0x%X,%d,%02x\n", VT_DECIMAL, + hres, V_VT(&vDst), S(U(V_DECIMAL(&vDst))).sign); + } + */ + + V_VT(&v) = VT_CY; + pcy->int64 = 10000; + hres = pVarRound(&v,0,&vDst); + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == 10000, + "VarRound: VT_CY wrong, hres=0x%X\n", hres); + +} + +static HRESULT (WINAPI *pVarXor)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARXOR(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarXor, &left, &right, &exp ) + +#define VARXORCY(vt1,val1,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_CY; V_CY(&right).int64 = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarXor, &left, &right, &exp ) + +static void test_VarXor(void) +{ + static const WCHAR szFalse[] = { '#','F','A','L','S','E','#','\0' }; + static const WCHAR szTrue[] = { '#','T','R','U','E','#','\0' }; + VARIANT left, right, exp, result; + BSTR lbstr, rbstr; + VARTYPE i; + HRESULT hres; + + CHECKPTR(VarXor); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + + SKIPTESTS(rightvt); + + if (leftvt == VT_BSTR || rightvt == VT_BSTR || + leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_I4; + + if (ExtraFlags[i] & VT_ARRAY || ExtraFlags[i] & VT_BYREF || + !IsValidVariantClearVT(leftvt, ExtraFlags[i]) || + !IsValidVariantClearVT(rightvt, ExtraFlags[i]) || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == VT_VARIANT || rightvt == VT_VARIANT || + leftvt == VT_ERROR || rightvt == VT_ERROR) + { + bFail = TRUE; + } + if (leftvt == VT_EMPTY || rightvt == VT_EMPTY) + { + if (leftvt == rightvt || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_UI1 || rightvt == VT_UI1 || + leftvt == VT_BOOL || rightvt == VT_BOOL) + resvt = VT_I2; + else if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_NULL || rightvt == VT_NULL) + { + resvt = VT_NULL; + } + else if (leftvt == VT_UI1 || rightvt == VT_UI1) + { + if (leftvt == rightvt) + resvt = VT_UI1; + else if (leftvt == rightvt || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL) + { + resvt = VT_I2; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_I2 || rightvt == VT_I2) + { + if (leftvt == rightvt || + leftvt == VT_BOOL || rightvt == VT_BOOL) + resvt = VT_I2; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_BOOL && rightvt == VT_BOOL) + { + resvt = VT_BOOL; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + { + if (leftvt == VT_INT || rightvt == VT_INT) + bFail = TRUE; + else + resvt = VT_I8; + } + hres = pVarXor(&left, &right, &result); + if (bFail) + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarXor: %d|0x%X, %d|0x%X: Expected failure, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], hres, + V_VT(&result)); + else + ok(hres == S_OK && V_VT(&result) == resvt, + "VarXor: %d|0x%X, %d|0x%X: expected S_OK, vt %d, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], resvt, hres, + V_VT(&result)); + } + } + } + + /* Test returned values + * FIXME: Test VT_DECIMAL/VT_DISPATCH + */ + VARXOR(EMPTY,0,EMPTY,0,I2,0); + VARXOR(EMPTY,1,EMPTY,0,I2,0); + VARXOR(EMPTY,0,NULL,0,NULL,0); + VARXOR(EMPTY,0,I1,0,I4,0); + VARXOR(EMPTY,0,I1,1,I4,1); + VARXOR(EMPTY,0,UI1,0,I2,0); + VARXOR(EMPTY,0,UI1,1,I2,1); + VARXOR(EMPTY,0,I2,0,I2,0); + VARXOR(EMPTY,0,I2,1,I2,1); + VARXOR(EMPTY,0,UI2,0,I4,0); + VARXOR(EMPTY,0,UI2,1,I4,1); + VARXOR(EMPTY,0,I4,0,I4,0); + VARXOR(EMPTY,0,I4,1,I4,1); + VARXOR(EMPTY,0,UI4,0,I4,0); + VARXOR(EMPTY,0,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VARXOR(EMPTY,0,I8,0,I8,0); + VARXOR(EMPTY,0,I8,1,I8,1); + VARXOR(EMPTY,0,UI8,0,I4,0); + VARXOR(EMPTY,0,UI8,1,I4,1); + } + VARXOR(EMPTY,0,INT,0,I4,0); + VARXOR(EMPTY,0,INT,1,I4,1); + VARXOR(EMPTY,0,UINT,0,I4,0); + VARXOR(EMPTY,0,UINT,1,I4,1); + VARXOR(EMPTY,0,BOOL,0,I2,0); + VARXOR(EMPTY,0,BOOL,1,I2,1); + VARXOR(EMPTY,0,R4,0,I4,0); + VARXOR(EMPTY,0,R4,1,I4,1); + VARXOR(EMPTY,0,R8,0,I4,0); + VARXOR(EMPTY,0,R8,1,I4,1); + rbstr = SysAllocString(szFalse); + VARXOR(EMPTY,0,BSTR,rbstr,I2,0); + rbstr = SysAllocString(szTrue); + VARXOR(EMPTY,0,BSTR,rbstr,I2,-1); + VARXORCY(EMPTY,0,10000,I4,1); + + /* NULL OR 0 = NULL. NULL OR n = n */ + VARXOR(NULL,0,NULL,0,NULL,0); + VARXOR(NULL,1,NULL,0,NULL,0); + VARXOR(NULL,0,I1,0,NULL,0); + VARXOR(NULL,0,I1,1,NULL,0); + VARXOR(NULL,0,UI1,0,NULL,0); + VARXOR(NULL,0,UI1,1,NULL,0); + VARXOR(NULL,0,I2,0,NULL,0); + VARXOR(NULL,0,I2,1,NULL,0); + VARXOR(NULL,0,UI2,0,NULL,0); + VARXOR(NULL,0,UI2,1,NULL,0); + VARXOR(NULL,0,I4,0,NULL,0); + VARXOR(NULL,0,I4,1,NULL,0); + VARXOR(NULL,0,UI4,0,NULL,0); + VARXOR(NULL,0,UI4,1,NULL,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(NULL,0,I8,0,NULL,0); + VARXOR(NULL,0,I8,1,NULL,0); + VARXOR(NULL,0,UI8,0,NULL,0); + VARXOR(NULL,0,UI8,1,NULL,0); + } + VARXOR(NULL,0,INT,0,NULL,0); + VARXOR(NULL,0,INT,1,NULL,0); + VARXOR(NULL,0,UINT,0,NULL,0); + VARXOR(NULL,0,UINT,1,NULL,0); + VARXOR(NULL,0,BOOL,0,NULL,0); + VARXOR(NULL,0,BOOL,1,NULL,0); + VARXOR(NULL,0,R4,0,NULL,0); + VARXOR(NULL,0,R4,1,NULL,0); + VARXOR(NULL,0,R8,0,NULL,0); + VARXOR(NULL,0,R8,1,NULL,0); + rbstr = SysAllocString(szFalse); + VARXOR(NULL,0,BSTR,rbstr,NULL,0); + rbstr = SysAllocString(szTrue); + VARXOR(NULL,0,BSTR,rbstr,NULL,0); + VARXORCY(NULL,0,10000,NULL,0); + VARXORCY(NULL,0,0,NULL,0); + + VARXOR(BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE); + VARXOR(BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE); + VARXOR(BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE); + VARXOR(BOOL,VARIANT_FALSE,BOOL,VARIANT_FALSE,BOOL,VARIANT_FALSE); + /* Assume x,y & y,x are the same from now on to reduce the number of tests */ + VARXOR(BOOL,VARIANT_TRUE,I1,-1,I4,0); + VARXOR(BOOL,VARIANT_TRUE,I1,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,I1,0,I4,0); + VARXOR(BOOL,VARIANT_TRUE,UI1,255,I2,-256); + VARXOR(BOOL,VARIANT_TRUE,UI1,0,I2,-1); + VARXOR(BOOL,VARIANT_FALSE,UI1,0,I2,0); + VARXOR(BOOL,VARIANT_TRUE,I2,-1,I2,0); + VARXOR(BOOL,VARIANT_TRUE,I2,0,I2,-1); + VARXOR(BOOL,VARIANT_FALSE,I2,0,I2,0); + VARXOR(BOOL,VARIANT_TRUE,UI2,65535,I4,-65536); + VARXOR(BOOL,VARIANT_TRUE,UI2,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,UI2,0,I4,0); + VARXOR(BOOL,VARIANT_TRUE,I4,-1,I4,0); + VARXOR(BOOL,VARIANT_TRUE,I4,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,I4,0,I4,0); + VARXOR(BOOL,VARIANT_TRUE,UI4,0xffffffff,I4,0); + VARXOR(BOOL,VARIANT_TRUE,UI4,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,UI4,0,I4,0); + VARXOR(BOOL,VARIANT_TRUE,R4,-1,I4,0); + VARXOR(BOOL,VARIANT_TRUE,R4,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,R4,0,I4,0); + VARXOR(BOOL,VARIANT_TRUE,R8,-1,I4,0); + VARXOR(BOOL,VARIANT_TRUE,R8,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,R8,0,I4,0); + VARXOR(BOOL,VARIANT_TRUE,DATE,-1,I4,0); + VARXOR(BOOL,VARIANT_TRUE,DATE,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(BOOL,VARIANT_TRUE,I8,-1,I8,0); + VARXOR(BOOL,VARIANT_TRUE,I8,0,I8,-1); + VARXOR(BOOL,VARIANT_FALSE,I8,0,I8,0); + /* This returns DISP_E_OVERFLOW which indicates that a conversion + * to I4 is performed. + */ + /* VARXOR(BOOL,VARIANT_TRUE,UI8,-1,I4,-1); */ + VARXOR(BOOL,VARIANT_TRUE,UI8,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,UI8,0,I4,0); + } + VARXOR(BOOL,VARIANT_TRUE,INT,-1,I4,0); + VARXOR(BOOL,VARIANT_TRUE,INT,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,INT,0,I4,0); + VARXOR(BOOL,VARIANT_TRUE,UINT,0xffffffff,I4,0); + VARXOR(BOOL,VARIANT_TRUE,UINT,0,I4,-1); + VARXOR(BOOL,VARIANT_FALSE,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(BOOL,VARIANT_FALSE,BSTR,rbstr,BOOL,VARIANT_FALSE); + VARXOR(BOOL,VARIANT_TRUE,BSTR,rbstr,BOOL,VARIANT_TRUE); + rbstr = SysAllocString(szTrue); + VARXOR(BOOL,VARIANT_FALSE,BSTR,rbstr,BOOL,VARIANT_TRUE); + VARXOR(BOOL,VARIANT_TRUE,BSTR,rbstr,BOOL,VARIANT_FALSE); + VARXORCY(BOOL,VARIANT_TRUE,10000,I4,-2); + VARXORCY(BOOL,VARIANT_TRUE,0,I4,-1); + VARXORCY(BOOL,VARIANT_FALSE,0,I4,0); + + VARXOR(I1,-1,I1,-1,I4,0); + VARXOR(I1,-1,I1,0,I4,-1); + VARXOR(I1,0,I1,0,I4,0); + VARXOR(I1,-1,UI1,255,I4,-256); + VARXOR(I1,-1,UI1,0,I4,-1); + VARXOR(I1,0,UI1,0,I4,0); + VARXOR(I1,-1,I2,-1,I4,0); + VARXOR(I1,-1,I2,0,I4,-1); + VARXOR(I1,0,I2,0,I4,0); + VARXOR(I1,-1,UI2,65535,I4,-65536); + VARXOR(I1,-1,UI2,0,I4,-1); + VARXOR(I1,0,UI2,0,I4,0); + VARXOR(I1,-1,I4,-1,I4,0); + VARXOR(I1,-1,I4,0,I4,-1); + VARXOR(I1,0,I4,0,I4,0); + VARXOR(I1,-1,UI4,0xffffffff,I4,0); + VARXOR(I1,-1,UI4,0,I4,-1); + VARXOR(I1,0,UI4,0,I4,0); + VARXOR(I1,-1,R4,-1,I4,0); + VARXOR(I1,-1,R4,0,I4,-1); + VARXOR(I1,0,R4,0,I4,0); + VARXOR(I1,-1,R8,-1,I4,0); + VARXOR(I1,-1,R8,0,I4,-1); + VARXOR(I1,0,R8,0,I4,0); + VARXOR(I1,-1,DATE,-1,I4,0); + VARXOR(I1,-1,DATE,0,I4,-1); + VARXOR(I1,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(I1,-1,I8,-1,I8,0); + VARXOR(I1,-1,I8,0,I8,-1); + VARXOR(I1,0,I8,0,I8,0); + VARXOR(I1,-1,UI8,0,I4,-1); + VARXOR(I1,0,UI8,0,I4,0); + } + VARXOR(I1,-1,INT,-1,I4,0); + VARXOR(I1,-1,INT,0,I4,-1); + VARXOR(I1,0,INT,0,I4,0); + VARXOR(I1,-1,UINT,0xffffffff,I4,0); + VARXOR(I1,-1,UINT,0,I4,-1); + VARXOR(I1,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(I1,0,BSTR,rbstr,I4,0); + VARXOR(I1,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VARXOR(I1,0,BSTR,rbstr,I4,-1); + VARXOR(I1,-1,BSTR,rbstr,I4,0); + VARXORCY(I1,-1,10000,I4,-2); + VARXORCY(I1,-1,0,I4,-1); + VARXORCY(I1,0,0,I4,0); + + VARXOR(UI1,255,UI1,255,UI1,0); + VARXOR(UI1,255,UI1,0,UI1,255); + VARXOR(UI1,0,UI1,0,UI1,0); + VARXOR(UI1,255,I2,-1,I2,-256); + VARXOR(UI1,255,I2,0,I2,255); + VARXOR(UI1,0,I2,0,I2,0); + VARXOR(UI1,255,UI2,65535,I4,65280); + VARXOR(UI1,255,UI2,0,I4,255); + VARXOR(UI1,0,UI2,0,I4,0); + VARXOR(UI1,255,I4,-1,I4,-256); + VARXOR(UI1,255,I4,0,I4,255); + VARXOR(UI1,0,I4,0,I4,0); + VARXOR(UI1,255,UI4,0xffffffff,I4,-256); + VARXOR(UI1,255,UI4,0,I4,255); + VARXOR(UI1,0,UI4,0,I4,0); + VARXOR(UI1,255,R4,-1,I4,-256); + VARXOR(UI1,255,R4,0,I4,255); + VARXOR(UI1,0,R4,0,I4,0); + VARXOR(UI1,255,R8,-1,I4,-256); + VARXOR(UI1,255,R8,0,I4,255); + VARXOR(UI1,0,R8,0,I4,0); + VARXOR(UI1,255,DATE,-1,I4,-256); + VARXOR(UI1,255,DATE,0,I4,255); + VARXOR(UI1,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(UI1,255,I8,-1,I8,-256); + VARXOR(UI1,255,I8,0,I8,255); + VARXOR(UI1,0,I8,0,I8,0); + VARXOR(UI1,255,UI8,0,I4,255); + VARXOR(UI1,0,UI8,0,I4,0); + } + VARXOR(UI1,255,INT,-1,I4,-256); + VARXOR(UI1,255,INT,0,I4,255); + VARXOR(UI1,0,INT,0,I4,0); + VARXOR(UI1,255,UINT,0xffffffff,I4,-256); + VARXOR(UI1,255,UINT,0,I4,255); + VARXOR(UI1,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(UI1,0,BSTR,rbstr,I2,0); + VARXOR(UI1,255,BSTR,rbstr,I2,255); + rbstr = SysAllocString(szTrue); + VARXOR(UI1,0,BSTR,rbstr,I2,-1); + VARXOR(UI1,255,BSTR,rbstr,I2,-256); + VARXORCY(UI1,255,10000,I4,254); + VARXORCY(UI1,255,0,I4,255); + VARXORCY(UI1,0,0,I4,0); + + VARXOR(I2,-1,I2,-1,I2,0); + VARXOR(I2,-1,I2,0,I2,-1); + VARXOR(I2,0,I2,0,I2,0); + VARXOR(I2,-1,UI2,65535,I4,-65536); + VARXOR(I2,-1,UI2,0,I4,-1); + VARXOR(I2,0,UI2,0,I4,0); + VARXOR(I2,-1,I4,-1,I4,0); + VARXOR(I2,-1,I4,0,I4,-1); + VARXOR(I2,0,I4,0,I4,0); + VARXOR(I2,-1,UI4,0xffffffff,I4,0); + VARXOR(I2,-1,UI4,0,I4,-1); + VARXOR(I2,0,UI4,0,I4,0); + VARXOR(I2,-1,R4,-1,I4,0); + VARXOR(I2,-1,R4,0,I4,-1); + VARXOR(I2,0,R4,0,I4,0); + VARXOR(I2,-1,R8,-1,I4,0); + VARXOR(I2,-1,R8,0,I4,-1); + VARXOR(I2,0,R8,0,I4,0); + VARXOR(I2,-1,DATE,-1,I4,0); + VARXOR(I2,-1,DATE,0,I4,-1); + VARXOR(I2,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(I2,-1,I8,-1,I8,0); + VARXOR(I2,-1,I8,0,I8,-1); + VARXOR(I2,0,I8,0,I8,0); + VARXOR(I2,-1,UI8,0,I4,-1); + VARXOR(I2,0,UI8,0,I4,0); + } + VARXOR(I2,-1,INT,-1,I4,0); + VARXOR(I2,-1,INT,0,I4,-1); + VARXOR(I2,0,INT,0,I4,0); + VARXOR(I2,-1,UINT,0xffffffff,I4,0); + VARXOR(I2,-1,UINT,0,I4,-1); + VARXOR(I2,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(I2,0,BSTR,rbstr,I2,0); + VARXOR(I2,-1,BSTR,rbstr,I2,-1); + rbstr = SysAllocString(szTrue); + VARXOR(I2,0,BSTR,rbstr,I2,-1); + VARXOR(I2,-1,BSTR,rbstr,I2,0); + VARXORCY(I2,-1,10000,I4,-2); + VARXORCY(I2,-1,0,I4,-1); + VARXORCY(I2,0,0,I4,0); + + VARXOR(UI2,65535,UI2,65535,I4,0); + VARXOR(UI2,65535,UI2,0,I4,65535); + VARXOR(UI2,0,UI2,0,I4,0); + VARXOR(UI2,65535,I4,-1,I4,-65536); + VARXOR(UI2,65535,I4,0,I4,65535); + VARXOR(UI2,0,I4,0,I4,0); + VARXOR(UI2,65535,UI4,0xffffffff,I4,-65536); + VARXOR(UI2,65535,UI4,0,I4,65535); + VARXOR(UI2,0,UI4,0,I4,0); + VARXOR(UI2,65535,R4,-1,I4,-65536); + VARXOR(UI2,65535,R4,0,I4,65535); + VARXOR(UI2,0,R4,0,I4,0); + VARXOR(UI2,65535,R8,-1,I4,-65536); + VARXOR(UI2,65535,R8,0,I4,65535); + VARXOR(UI2,0,R8,0,I4,0); + VARXOR(UI2,65535,DATE,-1,I4,-65536); + VARXOR(UI2,65535,DATE,0,I4,65535); + VARXOR(UI2,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(UI2,65535,I8,-1,I8,-65536); + VARXOR(UI2,65535,I8,0,I8,65535); + VARXOR(UI2,0,I8,0,I8,0); + VARXOR(UI2,65535,UI8,0,I4,65535); + VARXOR(UI2,0,UI8,0,I4,0); + } + VARXOR(UI2,65535,INT,-1,I4,-65536); + VARXOR(UI2,65535,INT,0,I4,65535); + VARXOR(UI2,0,INT,0,I4,0); + VARXOR(UI2,65535,UINT,0xffffffff,I4,-65536); + VARXOR(UI2,65535,UINT,0,I4,65535); + VARXOR(UI2,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(UI2,0,BSTR,rbstr,I4,0); + VARXOR(UI2,65535,BSTR,rbstr,I4,65535); + rbstr = SysAllocString(szTrue); + VARXOR(UI2,0,BSTR,rbstr,I4,-1); + VARXOR(UI2,65535,BSTR,rbstr,I4,-65536); + VARXORCY(UI2,65535,10000,I4,65534); + VARXORCY(UI2,65535,0,I4,65535); + VARXORCY(UI2,0,0,I4,0); + + VARXOR(I4,-1,I4,-1,I4,0); + VARXOR(I4,-1,I4,0,I4,-1); + VARXOR(I4,0,I4,0,I4,0); + VARXOR(I4,-1,UI4,0xffffffff,I4,0); + VARXOR(I4,-1,UI4,0,I4,-1); + VARXOR(I4,0,UI4,0,I4,0); + VARXOR(I4,-1,R4,-1,I4,0); + VARXOR(I4,-1,R4,0,I4,-1); + VARXOR(I4,0,R4,0,I4,0); + VARXOR(I4,-1,R8,-1,I4,0); + VARXOR(I4,-1,R8,0,I4,-1); + VARXOR(I4,0,R8,0,I4,0); + VARXOR(I4,-1,DATE,-1,I4,0); + VARXOR(I4,-1,DATE,0,I4,-1); + VARXOR(I4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(I4,-1,I8,-1,I8,0); + VARXOR(I4,-1,I8,0,I8,-1); + VARXOR(I4,0,I8,0,I8,0); + VARXOR(I4,-1,UI8,0,I4,-1); + VARXOR(I4,0,UI8,0,I4,0); + } + VARXOR(I4,-1,INT,-1,I4,0); + VARXOR(I4,-1,INT,0,I4,-1); + VARXOR(I4,0,INT,0,I4,0); + VARXOR(I4,-1,UINT,0xffffffff,I4,0); + VARXOR(I4,-1,UINT,0,I4,-1); + VARXOR(I4,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(I4,0,BSTR,rbstr,I4,0); + VARXOR(I4,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VARXOR(I4,0,BSTR,rbstr,I4,-1); + VARXOR(I4,-1,BSTR,rbstr,I4,0); + VARXORCY(I4,-1,10000,I4,-2); + VARXORCY(I4,-1,0,I4,-1); + VARXORCY(I4,0,0,I4,0); + + VARXOR(UI4,0xffffffff,UI4,0xffffffff,I4,0); + VARXOR(UI4,0xffffffff,UI4,0,I4,-1); + VARXOR(UI4,0,UI4,0,I4,0); + VARXOR(UI4,0xffffffff,R4,-1,I4,0); + VARXOR(UI4,0xffffffff,R4,0,I4,-1); + VARXOR(UI4,0,R4,0,I4,0); + VARXOR(UI4,0xffffffff,R8,-1,I4,0); + VARXOR(UI4,0xffffffff,R8,0,I4,-1); + VARXOR(UI4,0,R8,0,I4,0); + VARXOR(UI4,0xffffffff,DATE,-1,I4,0); + VARXOR(UI4,0xffffffff,DATE,0,I4,-1); + VARXOR(UI4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(UI4,0xffffffff,I8,0,I8,0xffffffff); + VARXOR(UI4,VARIANT_FALSE,I8,VARIANT_FALSE,I8,0); + VARXOR(UI4,0,I8,0,I8,0); + VARXOR(UI4,0xffffffff,UI8,0,I4,-1); + VARXOR(UI4,0,UI8,0,I4,0); + } + VARXOR(UI4,0xffffffff,INT,-1,I4,0); + VARXOR(UI4,0xffffffff,INT,0,I4,-1); + VARXOR(UI4,0,INT,0,I4,0); + VARXOR(UI4,0xffffffff,UINT,0xffffffff,I4,0); + VARXOR(UI4,0xffffffff,UINT,0,I4,-1); + VARXOR(UI4,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(UI4,0,BSTR,rbstr,I4,0); + VARXOR(UI4,0xffffffff,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VARXOR(UI4,0,BSTR,rbstr,I4,-1); + VARXOR(UI4,0xffffffff,BSTR,rbstr,I4,0); + VARXORCY(UI4,0xffffffff,10000,I4,-2); + VARXORCY(UI4,0xffffffff,0,I4,-1); + VARXORCY(UI4,0,0,I4,0); + + VARXOR(R4,-1,R4,-1,I4,0); + VARXOR(R4,-1,R4,0,I4,-1); + VARXOR(R4,0,R4,0,I4,0); + VARXOR(R4,-1,R8,-1,I4,0); + VARXOR(R4,-1,R8,0,I4,-1); + VARXOR(R4,0,R8,0,I4,0); + VARXOR(R4,-1,DATE,-1,I4,0); + VARXOR(R4,-1,DATE,0,I4,-1); + VARXOR(R4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(R4,-1,I8,-1,I8,0); + VARXOR(R4,-1,I8,0,I8,-1); + VARXOR(R4,0,I8,0,I8,0); + VARXOR(R4,-1,UI8,0,I4,-1); + VARXOR(R4,0,UI8,0,I4,0); + } + VARXOR(R4,-1,INT,-1,I4,0); + VARXOR(R4,-1,INT,0,I4,-1); + VARXOR(R4,0,INT,0,I4,0); + VARXOR(R4,-1,UINT,0xffffffff,I4,0); + VARXOR(R4,-1,UINT,0,I4,-1); + VARXOR(R4,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(R4,0,BSTR,rbstr,I4,0); + VARXOR(R4,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VARXOR(R4,0,BSTR,rbstr,I4,-1); + VARXOR(R4,-1,BSTR,rbstr,I4,0); + VARXORCY(R4,-1,10000,I4,-2); + VARXORCY(R4,-1,0,I4,-1); + VARXORCY(R4,0,0,I4,0); + + VARXOR(R8,-1,R8,-1,I4,0); + VARXOR(R8,-1,R8,0,I4,-1); + VARXOR(R8,0,R8,0,I4,0); + VARXOR(R8,-1,DATE,-1,I4,0); + VARXOR(R8,-1,DATE,0,I4,-1); + VARXOR(R8,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(R8,-1,I8,-1,I8,0); + VARXOR(R8,-1,I8,0,I8,-1); + VARXOR(R8,0,I8,0,I8,0); + VARXOR(R8,-1,UI8,0,I4,-1); + VARXOR(R8,0,UI8,0,I4,0); + } + VARXOR(R8,-1,INT,-1,I4,0); + VARXOR(R8,-1,INT,0,I4,-1); + VARXOR(R8,0,INT,0,I4,0); + VARXOR(R8,-1,UINT,0xffffffff,I4,0); + VARXOR(R8,-1,UINT,0,I4,-1); + VARXOR(R8,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(R8,0,BSTR,rbstr,I4,0); + VARXOR(R8,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VARXOR(R8,0,BSTR,rbstr,I4,-1); + VARXOR(R8,-1,BSTR,rbstr,I4,0); + VARXORCY(R8,-1,10000,I4,-2); + VARXORCY(R8,-1,0,I4,-1); + VARXORCY(R8,0,0,I4,0); + + VARXOR(DATE,-1,DATE,-1,I4,0); + VARXOR(DATE,-1,DATE,0,I4,-1); + VARXOR(DATE,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARXOR(DATE,-1,I8,-1,I8,0); + VARXOR(DATE,-1,I8,0,I8,-1); + VARXOR(DATE,0,I8,0,I8,0); + VARXOR(DATE,-1,UI8,0,I4,-1); + VARXOR(DATE,0,UI8,0,I4,0); + } + VARXOR(DATE,-1,INT,-1,I4,0); + VARXOR(DATE,-1,INT,0,I4,-1); + VARXOR(DATE,0,INT,0,I4,0); + VARXOR(DATE,-1,UINT,0xffffffff,I4,0); + VARXOR(DATE,-1,UINT,0,I4,-1); + VARXOR(DATE,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(DATE,0,BSTR,rbstr,I4,0); + VARXOR(DATE,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VARXOR(DATE,0,BSTR,rbstr,I4,-1); + VARXOR(DATE,-1,BSTR,rbstr,I4,0); + VARXORCY(DATE,-1,10000,I4,-2); + VARXORCY(DATE,-1,0,I4,-1); + VARXORCY(DATE,0,0,I4,0); + + if (HAVE_OLEAUT32_I8) + { + VARXOR(I8,-1,I8,-1,I8,0); + VARXOR(I8,-1,I8,0,I8,-1); + VARXOR(I8,0,I8,0,I8,0); + VARXOR(I8,-1,UI8,0,I8,-1); + VARXOR(I8,0,UI8,0,I8,0); + VARXOR(I8,-1,UINT,0,I8,-1); + VARXOR(I8,0,UINT,0,I8,0); + rbstr = SysAllocString(szFalse); + VARXOR(I8,0,BSTR,rbstr,I8,0); + VARXOR(I8,-1,BSTR,rbstr,I8,-1); + rbstr = SysAllocString(szTrue); + VARXOR(I8,0,BSTR,rbstr,I8,-1); + VARXOR(I8,-1,BSTR,rbstr,I8,0); + VARXORCY(I8,-1,10000,I8,-2); + VARXORCY(I8,-1,0,I8,-1); + VARXORCY(I8,0,0,I8,0); + + VARXOR(UI8,0xffff,UI8,0xffff,I4,0); + VARXOR(UI8,0xffff,UI8,0,I4,0xffff); + VARXOR(UI8,0,UI8,0,I4,0); + VARXOR(UI8,0xffff,INT,-1,I4,-65536); + VARXOR(UI8,0xffff,INT,0,I4,0xffff); + VARXOR(UI8,0,INT,0,I4,0); + VARXOR(UI8,0xffff,UINT,0xffff,I4,0); + VARXOR(UI8,0xffff,UINT,0,I4,0xffff); + VARXOR(UI8,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(UI8,0,BSTR,rbstr,I4,0); + VARXOR(UI8,0xffff,BSTR,rbstr,I4,0xffff); + rbstr = SysAllocString(szTrue); + VARXOR(UI8,0,BSTR,rbstr,I4,-1); + VARXOR(UI8,0xffff,BSTR,rbstr,I4,-65536); + VARXORCY(UI8,0xffff,10000,I4,65534); + VARXORCY(UI8,0xffff,0,I4,0xffff); + VARXORCY(UI8,0,0,I4,0); + } + + VARXOR(INT,-1,INT,-1,I4,0); + VARXOR(INT,-1,INT,0,I4,-1); + VARXOR(INT,0,INT,0,I4,0); + VARXOR(INT,-1,UINT,0xffff,I4,-65536); + VARXOR(INT,-1,UINT,0,I4,-1); + VARXOR(INT,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(INT,0,BSTR,rbstr,I4,0); + VARXOR(INT,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VARXOR(INT,0,BSTR,rbstr,I4,-1); + VARXOR(INT,-1,BSTR,rbstr,I4,0); + VARXORCY(INT,-1,10000,I4,-2); + VARXORCY(INT,-1,0,I4,-1); + VARXORCY(INT,0,0,I4,0); + + VARXOR(UINT,0xffff,UINT,0xffff,I4,0); + VARXOR(UINT,0xffff,UINT,0,I4,0xffff); + VARXOR(UINT,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VARXOR(UINT,0,BSTR,rbstr,I4,0); + VARXOR(UINT,0xffff,BSTR,rbstr,I4,0xffff); + rbstr = SysAllocString(szTrue); + VARXOR(UINT,0,BSTR,rbstr,I4,-1); + VARXOR(UINT,0xffff,BSTR,rbstr,I4,-65536); + VARXORCY(UINT,0xffff,10000,I4,65534); + VARXORCY(UINT,0xffff,0,I4,0xffff); + VARXORCY(UINT,0,0,I4,0); + + lbstr = SysAllocString(szFalse); + rbstr = SysAllocString(szFalse); + VARXOR(BSTR,lbstr,BSTR,rbstr,BOOL,0); + rbstr = SysAllocString(szTrue); + VARXOR(BSTR,lbstr,BSTR,rbstr,BOOL,VARIANT_TRUE); + lbstr = SysAllocString(szTrue); + VARXOR(BSTR,lbstr,BSTR,rbstr,BOOL,VARIANT_FALSE); + VARXORCY(BSTR,lbstr,10000,I4,-2); + lbstr = SysAllocString(szFalse); + VARXORCY(BSTR,lbstr,10000,I4,1); +} + +static HRESULT (WINAPI *pVarOr)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VAROR(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarOr, &left, &right, &exp ) + +#define VARORCY(vt1,val1,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_CY; V_CY(&right).int64 = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarOr, &left, &right, &exp ) + +static void test_VarOr(void) +{ + static const WCHAR szFalse[] = { '#','F','A','L','S','E','#','\0' }; + static const WCHAR szTrue[] = { '#','T','R','U','E','#','\0' }; + VARIANT left, right, exp, result; + BSTR lbstr, rbstr; + VARTYPE i; + HRESULT hres; + + CHECKPTR(VarOr); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + + SKIPTESTS(rightvt); + + if (leftvt == VT_BSTR || rightvt == VT_BSTR || + leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_I4; + + if (ExtraFlags[i] & VT_ARRAY || ExtraFlags[i] & VT_BYREF || + !IsValidVariantClearVT(leftvt, ExtraFlags[i]) || + !IsValidVariantClearVT(rightvt, ExtraFlags[i]) || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == VT_VARIANT || rightvt == VT_VARIANT || + leftvt == VT_ERROR || rightvt == VT_ERROR) + { + bFail = TRUE; + } + if (leftvt == VT_EMPTY || rightvt == VT_EMPTY) + { + if (leftvt == rightvt || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_UI1 || rightvt == VT_UI1 || + leftvt == VT_BOOL || rightvt == VT_BOOL) + resvt = VT_I2; + else if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_NULL || rightvt == VT_NULL) + { + resvt = VT_NULL; + } + else if (leftvt == VT_UI1 || rightvt == VT_UI1) + { + if (leftvt == rightvt) + resvt = VT_UI1; + else if (leftvt == rightvt || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL) + { + resvt = VT_I2; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_I2 || rightvt == VT_I2) + { + if (leftvt == rightvt || + leftvt == VT_BOOL || rightvt == VT_BOOL) + resvt = VT_I2; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_BOOL && rightvt == VT_BOOL) + { + resvt = VT_BOOL; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + { + if (leftvt == VT_INT || rightvt == VT_INT) + bFail = TRUE; + else + resvt = VT_I8; + } + hres = pVarOr(&left, &right, &result); + if (bFail) + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarOr: %d|0x%X, %d|0x%X: Expected failure, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], hres, + V_VT(&result)); + else + ok(hres == S_OK && V_VT(&result) == resvt, + "VarOr: %d|0x%X, %d|0x%X: expected S_OK, vt %d, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], resvt, hres, + V_VT(&result)); + } + } + } + + /* Test returned values. Since we know the returned type is correct + * and that we handle all combinations of invalid types, just check + * that good type combinations produce the desired value. + * FIXME: Test VT_DECIMAL/VT_DISPATCH + */ + VAROR(EMPTY,0,EMPTY,0,I2,0); + VAROR(EMPTY,1,EMPTY,0,I2,0); + VAROR(EMPTY,0,NULL,0,NULL,0); + VAROR(EMPTY,0,I1,0,I4,0); + VAROR(EMPTY,0,I1,1,I4,1); + VAROR(EMPTY,0,UI1,0,I2,0); + VAROR(EMPTY,0,UI1,1,I2,1); + VAROR(EMPTY,0,I2,0,I2,0); + VAROR(EMPTY,0,I2,1,I2,1); + VAROR(EMPTY,0,UI2,0,I4,0); + VAROR(EMPTY,0,UI2,1,I4,1); + VAROR(EMPTY,0,I4,0,I4,0); + VAROR(EMPTY,0,I4,1,I4,1); + VAROR(EMPTY,0,UI4,0,I4,0); + VAROR(EMPTY,0,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VAROR(EMPTY,0,I8,0,I8,0); + VAROR(EMPTY,0,I8,1,I8,1); + VAROR(EMPTY,0,UI8,0,I4,0); + VAROR(EMPTY,0,UI8,1,I4,1); + } + VAROR(EMPTY,0,INT,0,I4,0); + VAROR(EMPTY,0,INT,1,I4,1); + VAROR(EMPTY,0,UINT,0,I4,0); + VAROR(EMPTY,0,UINT,1,I4,1); + VAROR(EMPTY,0,BOOL,0,I2,0); + VAROR(EMPTY,0,BOOL,1,I2,1); + VAROR(EMPTY,0,R4,0,I4,0); + VAROR(EMPTY,0,R4,1,I4,1); + VAROR(EMPTY,0,R8,0,I4,0); + VAROR(EMPTY,0,R8,1,I4,1); + rbstr = SysAllocString(szFalse); + VAROR(EMPTY,0,BSTR,rbstr,I2,0); + rbstr = SysAllocString(szTrue); + VAROR(EMPTY,0,BSTR,rbstr,I2,-1); + VARORCY(EMPTY,0,10000,I4,1); + + /* NULL OR 0 = NULL. NULL OR n = n */ + VAROR(NULL,0,NULL,0,NULL,0); + VAROR(NULL,1,NULL,0,NULL,0); + VAROR(NULL,0,I1,0,NULL,0); + VAROR(NULL,0,I1,1,I4,1); + VAROR(NULL,0,UI1,0,NULL,0); + VAROR(NULL,0,UI1,1,UI1,1); + VAROR(NULL,0,I2,0,NULL,0); + VAROR(NULL,0,I2,1,I2,1); + VAROR(NULL,0,UI2,0,NULL,0); + VAROR(NULL,0,UI2,1,I4,1); + VAROR(NULL,0,I4,0,NULL,0); + VAROR(NULL,0,I4,1,I4,1); + VAROR(NULL,0,UI4,0,NULL,0); + VAROR(NULL,0,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VAROR(NULL,0,I8,0,NULL,0); + VAROR(NULL,0,I8,1,I8,1); + VAROR(NULL,0,UI8,0,NULL,0); + VAROR(NULL,0,UI8,1,I4,1); + } + VAROR(NULL,0,INT,0,NULL,0); + VAROR(NULL,0,INT,1,I4,1); + VAROR(NULL,0,UINT,0,NULL,0); + VAROR(NULL,0,UINT,1,I4,1); + VAROR(NULL,0,BOOL,0,NULL,0); + VAROR(NULL,0,BOOL,1,BOOL,1); + VAROR(NULL,0,R4,0,NULL,0); + VAROR(NULL,0,R4,1,I4,1); + VAROR(NULL,0,R8,0,NULL,0); + VAROR(NULL,0,R8,1,I4,1); + rbstr = SysAllocString(szFalse); + VAROR(NULL,0,BSTR,rbstr,NULL,0); + rbstr = SysAllocString(szTrue); + VAROR(NULL,0,BSTR,rbstr,BOOL,VARIANT_TRUE); + VARORCY(NULL,0,10000,I4,1); + VARORCY(NULL,0,0,NULL,0); + + VAROR(BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE); + VAROR(BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE); + VAROR(BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE); + VAROR(BOOL,VARIANT_FALSE,BOOL,VARIANT_FALSE,BOOL,VARIANT_FALSE); + /* Assume x,y & y,x are the same from now on to reduce the number of tests */ + VAROR(BOOL,VARIANT_TRUE,I1,-1,I4,-1); + VAROR(BOOL,VARIANT_TRUE,I1,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,I1,0,I4,0); + VAROR(BOOL,VARIANT_TRUE,UI1,255,I2,-1); + VAROR(BOOL,VARIANT_TRUE,UI1,0,I2,-1); + VAROR(BOOL,VARIANT_FALSE,UI1,0,I2,0); + VAROR(BOOL,VARIANT_TRUE,I2,-1,I2,-1); + VAROR(BOOL,VARIANT_TRUE,I2,0,I2,-1); + VAROR(BOOL,VARIANT_FALSE,I2,0,I2,0); + VAROR(BOOL,VARIANT_TRUE,UI2,65535,I4,-1); + VAROR(BOOL,VARIANT_TRUE,UI2,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,UI2,0,I4,0); + VAROR(BOOL,VARIANT_TRUE,I4,-1,I4,-1); + VAROR(BOOL,VARIANT_TRUE,I4,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,I4,0,I4,0); + VAROR(BOOL,VARIANT_TRUE,UI4,0xffffffff,I4,-1); + VAROR(BOOL,VARIANT_TRUE,UI4,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,UI4,0,I4,0); + VAROR(BOOL,VARIANT_TRUE,R4,-1,I4,-1); + VAROR(BOOL,VARIANT_TRUE,R4,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,R4,0,I4,0); + VAROR(BOOL,VARIANT_TRUE,R8,-1,I4,-1); + VAROR(BOOL,VARIANT_TRUE,R8,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,R8,0,I4,0); + VAROR(BOOL,VARIANT_TRUE,DATE,-1,I4,-1); + VAROR(BOOL,VARIANT_TRUE,DATE,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(BOOL,VARIANT_TRUE,I8,-1,I8,-1); + VAROR(BOOL,VARIANT_TRUE,I8,0,I8,-1); + VAROR(BOOL,VARIANT_FALSE,I8,0,I8,0); + /* This returns DISP_E_OVERFLOW which indicates that a conversion + * to I4 is performed. + */ + /* VAROR(BOOL,VARIANT_TRUE,UI8,-1,I4,-1); */ + VAROR(BOOL,VARIANT_TRUE,UI8,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,UI8,0,I4,0); + } + VAROR(BOOL,VARIANT_TRUE,INT,-1,I4,-1); + VAROR(BOOL,VARIANT_TRUE,INT,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,INT,0,I4,0); + VAROR(BOOL,VARIANT_TRUE,UINT,0xffffffff,I4,-1); + VAROR(BOOL,VARIANT_TRUE,UINT,0,I4,-1); + VAROR(BOOL,VARIANT_FALSE,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(BOOL,VARIANT_FALSE,BSTR,rbstr,BOOL,VARIANT_FALSE); + VAROR(BOOL,VARIANT_TRUE,BSTR,rbstr,BOOL,VARIANT_TRUE); + rbstr = SysAllocString(szTrue); + VAROR(BOOL,VARIANT_FALSE,BSTR,rbstr,BOOL,VARIANT_TRUE); + VAROR(BOOL,VARIANT_TRUE,BSTR,rbstr,BOOL,VARIANT_TRUE); + VARORCY(BOOL,VARIANT_TRUE,10000,I4,-1); + VARORCY(BOOL,VARIANT_TRUE,0,I4,-1); + VARORCY(BOOL,VARIANT_FALSE,0,I4,0); + + VAROR(I1,-1,I1,-1,I4,-1); + VAROR(I1,-1,I1,0,I4,-1); + VAROR(I1,0,I1,0,I4,0); + VAROR(I1,-1,UI1,255,I4,-1); + VAROR(I1,-1,UI1,0,I4,-1); + VAROR(I1,0,UI1,0,I4,0); + VAROR(I1,-1,I2,-1,I4,-1); + VAROR(I1,-1,I2,0,I4,-1); + VAROR(I1,0,I2,0,I4,0); + VAROR(I1,-1,UI2,65535,I4,-1); + VAROR(I1,-1,UI2,0,I4,-1); + VAROR(I1,0,UI2,0,I4,0); + VAROR(I1,-1,I4,-1,I4,-1); + VAROR(I1,-1,I4,0,I4,-1); + VAROR(I1,0,I4,0,I4,0); + VAROR(I1,-1,UI4,0xffffffff,I4,-1); + VAROR(I1,-1,UI4,0,I4,-1); + VAROR(I1,0,UI4,0,I4,0); + VAROR(I1,-1,R4,-1,I4,-1); + VAROR(I1,-1,R4,0,I4,-1); + VAROR(I1,0,R4,0,I4,0); + VAROR(I1,-1,R8,-1,I4,-1); + VAROR(I1,-1,R8,0,I4,-1); + VAROR(I1,0,R8,0,I4,0); + VAROR(I1,-1,DATE,-1,I4,-1); + VAROR(I1,-1,DATE,0,I4,-1); + VAROR(I1,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(I1,-1,I8,-1,I8,-1); + VAROR(I1,-1,I8,0,I8,-1); + VAROR(I1,0,I8,0,I8,0); + VAROR(I1,-1,UI8,0,I4,-1); + VAROR(I1,0,UI8,0,I4,0); + } + VAROR(I1,-1,INT,-1,I4,-1); + VAROR(I1,-1,INT,0,I4,-1); + VAROR(I1,0,INT,0,I4,0); + VAROR(I1,-1,UINT,0xffffffff,I4,-1); + VAROR(I1,-1,UINT,0,I4,-1); + VAROR(I1,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(I1,0,BSTR,rbstr,I4,0); + VAROR(I1,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VAROR(I1,0,BSTR,rbstr,I4,-1); + VAROR(I1,-1,BSTR,rbstr,I4,-1); + VARORCY(I1,-1,10000,I4,-1); + VARORCY(I1,-1,0,I4,-1); + VARORCY(I1,0,0,I4,0); + + VAROR(UI1,255,UI1,255,UI1,255); + VAROR(UI1,255,UI1,0,UI1,255); + VAROR(UI1,0,UI1,0,UI1,0); + VAROR(UI1,255,I2,-1,I2,-1); + VAROR(UI1,255,I2,0,I2,255); + VAROR(UI1,0,I2,0,I2,0); + VAROR(UI1,255,UI2,65535,I4,65535); + VAROR(UI1,255,UI2,0,I4,255); + VAROR(UI1,0,UI2,0,I4,0); + VAROR(UI1,255,I4,-1,I4,-1); + VAROR(UI1,255,I4,0,I4,255); + VAROR(UI1,0,I4,0,I4,0); + VAROR(UI1,255,UI4,0xffffffff,I4,-1); + VAROR(UI1,255,UI4,0,I4,255); + VAROR(UI1,0,UI4,0,I4,0); + VAROR(UI1,255,R4,-1,I4,-1); + VAROR(UI1,255,R4,0,I4,255); + VAROR(UI1,0,R4,0,I4,0); + VAROR(UI1,255,R8,-1,I4,-1); + VAROR(UI1,255,R8,0,I4,255); + VAROR(UI1,0,R8,0,I4,0); + VAROR(UI1,255,DATE,-1,I4,-1); + VAROR(UI1,255,DATE,0,I4,255); + VAROR(UI1,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(UI1,255,I8,-1,I8,-1); + VAROR(UI1,255,I8,0,I8,255); + VAROR(UI1,0,I8,0,I8,0); + VAROR(UI1,255,UI8,0,I4,255); + VAROR(UI1,0,UI8,0,I4,0); + } + VAROR(UI1,255,INT,-1,I4,-1); + VAROR(UI1,255,INT,0,I4,255); + VAROR(UI1,0,INT,0,I4,0); + VAROR(UI1,255,UINT,0xffffffff,I4,-1); + VAROR(UI1,255,UINT,0,I4,255); + VAROR(UI1,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(UI1,0,BSTR,rbstr,I2,0); + VAROR(UI1,255,BSTR,rbstr,I2,255); + rbstr = SysAllocString(szTrue); + VAROR(UI1,0,BSTR,rbstr,I2,-1); + VAROR(UI1,255,BSTR,rbstr,I2,-1); + VARORCY(UI1,255,10000,I4,255); + VARORCY(UI1,255,0,I4,255); + VARORCY(UI1,0,0,I4,0); + + VAROR(I2,-1,I2,-1,I2,-1); + VAROR(I2,-1,I2,0,I2,-1); + VAROR(I2,0,I2,0,I2,0); + VAROR(I2,-1,UI2,65535,I4,-1); + VAROR(I2,-1,UI2,0,I4,-1); + VAROR(I2,0,UI2,0,I4,0); + VAROR(I2,-1,I4,-1,I4,-1); + VAROR(I2,-1,I4,0,I4,-1); + VAROR(I2,0,I4,0,I4,0); + VAROR(I2,-1,UI4,0xffffffff,I4,-1); + VAROR(I2,-1,UI4,0,I4,-1); + VAROR(I2,0,UI4,0,I4,0); + VAROR(I2,-1,R4,-1,I4,-1); + VAROR(I2,-1,R4,0,I4,-1); + VAROR(I2,0,R4,0,I4,0); + VAROR(I2,-1,R8,-1,I4,-1); + VAROR(I2,-1,R8,0,I4,-1); + VAROR(I2,0,R8,0,I4,0); + VAROR(I2,-1,DATE,-1,I4,-1); + VAROR(I2,-1,DATE,0,I4,-1); + VAROR(I2,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(I2,-1,I8,-1,I8,-1); + VAROR(I2,-1,I8,0,I8,-1); + VAROR(I2,0,I8,0,I8,0); + VAROR(I2,-1,UI8,0,I4,-1); + VAROR(I2,0,UI8,0,I4,0); + } + VAROR(I2,-1,INT,-1,I4,-1); + VAROR(I2,-1,INT,0,I4,-1); + VAROR(I2,0,INT,0,I4,0); + VAROR(I2,-1,UINT,0xffffffff,I4,-1); + VAROR(I2,-1,UINT,0,I4,-1); + VAROR(I2,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(I2,0,BSTR,rbstr,I2,0); + VAROR(I2,-1,BSTR,rbstr,I2,-1); + rbstr = SysAllocString(szTrue); + VAROR(I2,0,BSTR,rbstr,I2,-1); + VAROR(I2,-1,BSTR,rbstr,I2,-1); + VARORCY(I2,-1,10000,I4,-1); + VARORCY(I2,-1,0,I4,-1); + VARORCY(I2,0,0,I4,0); + + VAROR(UI2,65535,UI2,65535,I4,65535); + VAROR(UI2,65535,UI2,0,I4,65535); + VAROR(UI2,0,UI2,0,I4,0); + VAROR(UI2,65535,I4,-1,I4,-1); + VAROR(UI2,65535,I4,0,I4,65535); + VAROR(UI2,0,I4,0,I4,0); + VAROR(UI2,65535,UI4,0xffffffff,I4,-1); + VAROR(UI2,65535,UI4,0,I4,65535); + VAROR(UI2,0,UI4,0,I4,0); + VAROR(UI2,65535,R4,-1,I4,-1); + VAROR(UI2,65535,R4,0,I4,65535); + VAROR(UI2,0,R4,0,I4,0); + VAROR(UI2,65535,R8,-1,I4,-1); + VAROR(UI2,65535,R8,0,I4,65535); + VAROR(UI2,0,R8,0,I4,0); + VAROR(UI2,65535,DATE,-1,I4,-1); + VAROR(UI2,65535,DATE,0,I4,65535); + VAROR(UI2,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(UI2,65535,I8,-1,I8,-1); + VAROR(UI2,65535,I8,0,I8,65535); + VAROR(UI2,0,I8,0,I8,0); + VAROR(UI2,65535,UI8,0,I4,65535); + VAROR(UI2,0,UI8,0,I4,0); + } + VAROR(UI2,65535,INT,-1,I4,-1); + VAROR(UI2,65535,INT,0,I4,65535); + VAROR(UI2,0,INT,0,I4,0); + VAROR(UI2,65535,UINT,0xffffffff,I4,-1); + VAROR(UI2,65535,UINT,0,I4,65535); + VAROR(UI2,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(UI2,0,BSTR,rbstr,I4,0); + VAROR(UI2,65535,BSTR,rbstr,I4,65535); + rbstr = SysAllocString(szTrue); + VAROR(UI2,0,BSTR,rbstr,I4,-1); + VAROR(UI2,65535,BSTR,rbstr,I4,-1); + VARORCY(UI2,65535,10000,I4,65535); + VARORCY(UI2,65535,0,I4,65535); + VARORCY(UI2,0,0,I4,0); + + VAROR(I4,-1,I4,-1,I4,-1); + VAROR(I4,-1,I4,0,I4,-1); + VAROR(I4,0,I4,0,I4,0); + VAROR(I4,-1,UI4,0xffffffff,I4,-1); + VAROR(I4,-1,UI4,0,I4,-1); + VAROR(I4,0,UI4,0,I4,0); + VAROR(I4,-1,R4,-1,I4,-1); + VAROR(I4,-1,R4,0,I4,-1); + VAROR(I4,0,R4,0,I4,0); + VAROR(I4,-1,R8,-1,I4,-1); + VAROR(I4,-1,R8,0,I4,-1); + VAROR(I4,0,R8,0,I4,0); + VAROR(I4,-1,DATE,-1,I4,-1); + VAROR(I4,-1,DATE,0,I4,-1); + VAROR(I4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(I4,-1,I8,-1,I8,-1); + VAROR(I4,-1,I8,0,I8,-1); + VAROR(I4,0,I8,0,I8,0); + VAROR(I4,-1,UI8,0,I4,-1); + VAROR(I4,0,UI8,0,I4,0); + } + VAROR(I4,-1,INT,-1,I4,-1); + VAROR(I4,-1,INT,0,I4,-1); + VAROR(I4,0,INT,0,I4,0); + VAROR(I4,-1,UINT,0xffffffff,I4,-1); + VAROR(I4,-1,UINT,0,I4,-1); + VAROR(I4,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(I4,0,BSTR,rbstr,I4,0); + VAROR(I4,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VAROR(I4,0,BSTR,rbstr,I4,-1); + VAROR(I4,-1,BSTR,rbstr,I4,-1); + VARORCY(I4,-1,10000,I4,-1); + VARORCY(I4,-1,0,I4,-1); + VARORCY(I4,0,0,I4,0); + + VAROR(UI4,0xffffffff,UI4,0xffffffff,I4,-1); + VAROR(UI4,0xffffffff,UI4,0,I4,-1); + VAROR(UI4,0,UI4,0,I4,0); + VAROR(UI4,0xffffffff,R4,-1,I4,-1); + VAROR(UI4,0xffffffff,R4,0,I4,-1); + VAROR(UI4,0,R4,0,I4,0); + VAROR(UI4,0xffffffff,R8,-1,I4,-1); + VAROR(UI4,0xffffffff,R8,0,I4,-1); + VAROR(UI4,0,R8,0,I4,0); + VAROR(UI4,0xffffffff,DATE,-1,I4,-1); + VAROR(UI4,0xffffffff,DATE,0,I4,-1); + VAROR(UI4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(UI4,0xffffffff,I8,-1,I8,-1); + VAROR(UI4,0xffffffff,I8,0,I8,0xffffffff); + VAROR(UI4,0,I8,0,I8,0); + VAROR(UI4,0xffffffff,UI8,0,I4,-1); + VAROR(UI4,0,UI8,0,I4,0); + } + VAROR(UI4,0xffffffff,INT,-1,I4,-1); + VAROR(UI4,0xffffffff,INT,0,I4,-1); + VAROR(UI4,0,INT,0,I4,0); + VAROR(UI4,0xffffffff,UINT,0xffffffff,I4,-1); + VAROR(UI4,0xffffffff,UINT,0,I4,-1); + VAROR(UI4,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(UI4,0,BSTR,rbstr,I4,0); + VAROR(UI4,0xffffffff,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VAROR(UI4,0,BSTR,rbstr,I4,-1); + VAROR(UI4,0xffffffff,BSTR,rbstr,I4,-1); + VARORCY(UI4,0xffffffff,10000,I4,-1); + VARORCY(UI4,0xffffffff,0,I4,-1); + VARORCY(UI4,0,0,I4,0); + + VAROR(R4,-1,R4,-1,I4,-1); + VAROR(R4,-1,R4,0,I4,-1); + VAROR(R4,0,R4,0,I4,0); + VAROR(R4,-1,R8,-1,I4,-1); + VAROR(R4,-1,R8,0,I4,-1); + VAROR(R4,0,R8,0,I4,0); + VAROR(R4,-1,DATE,-1,I4,-1); + VAROR(R4,-1,DATE,0,I4,-1); + VAROR(R4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(R4,-1,I8,-1,I8,-1); + VAROR(R4,-1,I8,0,I8,-1); + VAROR(R4,0,I8,0,I8,0); + VAROR(R4,-1,UI8,0,I4,-1); + VAROR(R4,0,UI8,0,I4,0); + } + VAROR(R4,-1,INT,-1,I4,-1); + VAROR(R4,-1,INT,0,I4,-1); + VAROR(R4,0,INT,0,I4,0); + VAROR(R4,-1,UINT,0xffffffff,I4,-1); + VAROR(R4,-1,UINT,0,I4,-1); + VAROR(R4,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(R4,0,BSTR,rbstr,I4,0); + VAROR(R4,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VAROR(R4,0,BSTR,rbstr,I4,-1); + VAROR(R4,-1,BSTR,rbstr,I4,-1); + VARORCY(R4,-1,10000,I4,-1); + VARORCY(R4,-1,0,I4,-1); + VARORCY(R4,0,0,I4,0); + + VAROR(R8,-1,R8,-1,I4,-1); + VAROR(R8,-1,R8,0,I4,-1); + VAROR(R8,0,R8,0,I4,0); + VAROR(R8,-1,DATE,-1,I4,-1); + VAROR(R8,-1,DATE,0,I4,-1); + VAROR(R8,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(R8,-1,I8,-1,I8,-1); + VAROR(R8,-1,I8,0,I8,-1); + VAROR(R8,0,I8,0,I8,0); + VAROR(R8,-1,UI8,0,I4,-1); + VAROR(R8,0,UI8,0,I4,0); + } + VAROR(R8,-1,INT,-1,I4,-1); + VAROR(R8,-1,INT,0,I4,-1); + VAROR(R8,0,INT,0,I4,0); + VAROR(R8,-1,UINT,0xffffffff,I4,-1); + VAROR(R8,-1,UINT,0,I4,-1); + VAROR(R8,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(R8,0,BSTR,rbstr,I4,0); + VAROR(R8,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VAROR(R8,0,BSTR,rbstr,I4,-1); + VAROR(R8,-1,BSTR,rbstr,I4,-1); + VARORCY(R8,-1,10000,I4,-1); + VARORCY(R8,-1,0,I4,-1); + VARORCY(R8,0,0,I4,0); + + VAROR(DATE,-1,DATE,-1,I4,-1); + VAROR(DATE,-1,DATE,0,I4,-1); + VAROR(DATE,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VAROR(DATE,-1,I8,-1,I8,-1); + VAROR(DATE,-1,I8,0,I8,-1); + VAROR(DATE,0,I8,0,I8,0); + VAROR(DATE,-1,UI8,0,I4,-1); + VAROR(DATE,0,UI8,0,I4,0); + } + VAROR(DATE,-1,INT,-1,I4,-1); + VAROR(DATE,-1,INT,0,I4,-1); + VAROR(DATE,0,INT,0,I4,0); + VAROR(DATE,-1,UINT,0xffffffff,I4,-1); + VAROR(DATE,-1,UINT,0,I4,-1); + VAROR(DATE,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(DATE,0,BSTR,rbstr,I4,0); + VAROR(DATE,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VAROR(DATE,0,BSTR,rbstr,I4,-1); + VAROR(DATE,-1,BSTR,rbstr,I4,-1); + VARORCY(DATE,-1,10000,I4,-1); + VARORCY(DATE,-1,0,I4,-1); + VARORCY(DATE,0,0,I4,0); + + if (HAVE_OLEAUT32_I8) + { + VAROR(I8,-1,I8,-1,I8,-1); + VAROR(I8,-1,I8,0,I8,-1); + VAROR(I8,0,I8,0,I8,0); + VAROR(I8,-1,UI8,0,I8,-1); + VAROR(I8,0,UI8,0,I8,0); + /* These overflow under native and Wine + VAROR(I8,-1,INT,-1,I4,-1); + VAROR(I8,-1,INT,0,I4,-1); + VAROR(I8,0,INT,0,I4,0); */ + VAROR(I8,-1,UINT,0xffffffff,I8,-1); + VAROR(I8,-1,UINT,0,I8,-1); + VAROR(I8,0,UINT,0,I8,0); + rbstr = SysAllocString(szFalse); + VAROR(I8,0,BSTR,rbstr,I8,0); + VAROR(I8,-1,BSTR,rbstr,I8,-1); + rbstr = SysAllocString(szTrue); + VAROR(I8,0,BSTR,rbstr,I8,-1); + VAROR(I8,-1,BSTR,rbstr,I8,-1); + VARORCY(I8,-1,10000,I8,-1); + VARORCY(I8,-1,0,I8,-1); + VARORCY(I8,0,0,I8,0); + + VAROR(UI8,0xffff,UI8,0xffff,I4,0xffff); + VAROR(UI8,0xffff,UI8,0,I4,0xffff); + VAROR(UI8,0,UI8,0,I4,0); + VAROR(UI8,0xffff,INT,-1,I4,-1); + VAROR(UI8,0xffff,INT,0,I4,0xffff); + VAROR(UI8,0,INT,0,I4,0); + VAROR(UI8,0xffff,UINT,0xffff,I4,0xffff); + VAROR(UI8,0xffff,UINT,0,I4,0xffff); + VAROR(UI8,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(UI8,0,BSTR,rbstr,I4,0); + VAROR(UI8,0xffff,BSTR,rbstr,I4,0xffff); + rbstr = SysAllocString(szTrue); + VAROR(UI8,0,BSTR,rbstr,I4,-1); + VAROR(UI8,0xffff,BSTR,rbstr,I4,-1); + VARORCY(UI8,0xffff,10000,I4,0xffff); + VARORCY(UI8,0xffff,0,I4,0xffff); + VARORCY(UI8,0,0,I4,0); + } + + VAROR(INT,-1,INT,-1,I4,-1); + VAROR(INT,-1,INT,0,I4,-1); + VAROR(INT,0,INT,0,I4,0); + VAROR(INT,-1,UINT,0xffff,I4,-1); + VAROR(INT,-1,UINT,0,I4,-1); + VAROR(INT,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(INT,0,BSTR,rbstr,I4,0); + VAROR(INT,-1,BSTR,rbstr,I4,-1); + rbstr = SysAllocString(szTrue); + VAROR(INT,0,BSTR,rbstr,I4,-1); + VAROR(INT,-1,BSTR,rbstr,I4,-1); + VARORCY(INT,-1,10000,I4,-1); + VARORCY(INT,-1,0,I4,-1); + VARORCY(INT,0,0,I4,0); + + VAROR(UINT,0xffff,UINT,0xffff,I4,0xffff); + VAROR(UINT,0xffff,UINT,0,I4,0xffff); + VAROR(UINT,0,UINT,0,I4,0); + rbstr = SysAllocString(szFalse); + VAROR(UINT,0,BSTR,rbstr,I4,0); + VAROR(UINT,0xffff,BSTR,rbstr,I4,0xffff); + rbstr = SysAllocString(szTrue); + VAROR(UINT,0,BSTR,rbstr,I4,-1); + VAROR(UINT,0xffff,BSTR,rbstr,I4,-1); + VARORCY(UINT,0xffff,10000,I4,0xffff); + VARORCY(UINT,0xffff,0,I4,0xffff); + VARORCY(UINT,0,0,I4,0); + + lbstr = SysAllocString(szFalse); + rbstr = SysAllocString(szFalse); + VAROR(BSTR,lbstr,BSTR,rbstr,BOOL,0); + rbstr = SysAllocString(szTrue); + VAROR(BSTR,lbstr,BSTR,rbstr,BOOL,VARIANT_TRUE); + lbstr = SysAllocString(szTrue); + VAROR(BSTR,lbstr,BSTR,rbstr,BOOL,VARIANT_TRUE); + VARORCY(BSTR,lbstr,10000,I4,-1); + lbstr = SysAllocString(szFalse); + VARORCY(BSTR,lbstr,10000,I4,1); +} + +static HRESULT (WINAPI *pVarEqv)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VAREQV(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarEqv, &left, &right, &exp ) + +static void test_VarEqv(void) +{ + VARIANT left, right, exp, result; + VARTYPE i; + HRESULT hres; + + CHECKPTR(VarEqv); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + + SKIPTESTS(rightvt); + + if (leftvt == VT_BSTR || rightvt == VT_BSTR || + leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_I4; + + if (ExtraFlags[i] & VT_ARRAY || ExtraFlags[i] & VT_BYREF || + !IsValidVariantClearVT(leftvt, ExtraFlags[i]) || + !IsValidVariantClearVT(rightvt, ExtraFlags[i]) || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == VT_VARIANT || rightvt == VT_VARIANT || + leftvt == VT_ERROR || rightvt == VT_ERROR) + { + bFail = TRUE; + } + if (leftvt == VT_EMPTY || rightvt == VT_EMPTY) + { + if (leftvt == rightvt || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_UI1 || rightvt == VT_UI1 || + leftvt == VT_BOOL || rightvt == VT_BOOL) + resvt = VT_I2; + else if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_NULL || rightvt == VT_NULL) + { + resvt = VT_NULL; + } + else if (leftvt == VT_UI1 || rightvt == VT_UI1) + { + if (leftvt == rightvt) + resvt = VT_UI1; + else if (leftvt == rightvt || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL) + { + resvt = VT_I2; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_I2 || rightvt == VT_I2) + { + if (leftvt == rightvt || + leftvt == VT_BOOL || rightvt == VT_BOOL) + resvt = VT_I2; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + } + else if (leftvt == VT_BOOL && rightvt == VT_BOOL) + { + resvt = VT_BOOL; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + { + if (leftvt == VT_INT || rightvt == VT_INT) + bFail = TRUE; + else + resvt = VT_I8; + } + hres = pVarEqv(&left, &right, &result); + if (bFail) + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarEqv: %d|0x%X, %d|0x%X: Expected failure, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], hres, + V_VT(&result)); + else + ok(hres == S_OK && V_VT(&result) == resvt, + "VarEqv: %d|0x%X, %d|0x%X: expected S_OK, vt %d, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], resvt, hres, + V_VT(&result)); + } + } + } + + /* Test returned values */ + VAREQV(BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE); + VAREQV(BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE); + VAREQV(BOOL,TRUE,BOOL,TRUE,BOOL,VARIANT_TRUE); + VAREQV(BOOL,FALSE,BOOL,FALSE,BOOL,VARIANT_TRUE); + VAREQV(BOOL,TRUE,BOOL,FALSE,BOOL,-2); + VAREQV(BOOL,FALSE,BOOL,TRUE,BOOL,-2); + VAREQV(BOOL,6,BOOL,7,BOOL,-2); + VAREQV(BOOL,6,BOOL,6,BOOL,VARIANT_TRUE); + VAREQV(BOOL,VARIANT_TRUE,I2,VARIANT_TRUE,I2,VARIANT_TRUE); + VAREQV(BOOL,VARIANT_TRUE,I2,VARIANT_FALSE,I2,VARIANT_FALSE); + VAREQV(BOOL,6,I2,7,I2,-2); + VAREQV(UI1,1,UI1,1,UI1,255); + VAREQV(UI1,1,UI1,0,UI1,254); + VAREQV(UI1,0,UI1,1,UI1,254); + if (HAVE_OLEAUT32_I8) + { + VAREQV(UI4,VARIANT_FALSE,I8,VARIANT_FALSE,I8,-1); + VAREQV(UI4,5,I8,19,I8,-23); + VAREQV(UI4,VARIANT_FALSE,UI8,VARIANT_FALSE,I4,-1); + } +} + +static HRESULT (WINAPI *pVarMul)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARMUL(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarMul, &left, &right, &exp ) + +static void test_VarMul(void) +{ + static const WCHAR sz12[] = {'1','2','\0'}; + VARIANT left, right, exp, result, cy, dec; + VARTYPE i; + BSTR lbstr, rbstr; + HRESULT hres; + double r; + + CHECKPTR(VarMul); + + lbstr = SysAllocString(sz12); + rbstr = SysAllocString(sz12); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + + SKIPTESTS(rightvt); + + if (leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + if (leftvt == VT_BSTR) + V_BSTR(&left) = lbstr; + V_VT(&right) = rightvt | ExtraFlags[i]; + if (rightvt == VT_BSTR) + V_BSTR(&right) = rbstr; + V_VT(&result) = VT_EMPTY; + resvt = VT_UNKNOWN; + + /* Don't ask me why but native VarMul cannot handle: + VT_I1, VT_UI2, VT_UI4, VT_INT, VT_UINT and VT_UI8. + Tested with DCOM98, Win2k, WinXP */ + if (ExtraFlags[i] & VT_ARRAY || ExtraFlags[i] & VT_BYREF || + !IsValidVariantClearVT(leftvt, ExtraFlags[i]) || + !IsValidVariantClearVT(rightvt, ExtraFlags[i]) || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == VT_VARIANT || rightvt == VT_VARIANT || + leftvt == VT_ERROR || rightvt == VT_ERROR || + leftvt == VT_I1 || rightvt == VT_I1 || + leftvt == VT_UI2 || rightvt == VT_UI2 || + leftvt == VT_UI4 || rightvt == VT_UI4 || + leftvt == VT_UI8 || rightvt == VT_UI8 || + leftvt == VT_INT || rightvt == VT_INT || + leftvt == VT_UINT || rightvt == VT_UINT) { + bFail = TRUE; + } + + if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if (leftvt == VT_DECIMAL || rightvt == VT_DECIMAL) + resvt = VT_DECIMAL; + else if (leftvt == VT_R8 || rightvt == VT_R8 || + leftvt == VT_BSTR || rightvt == VT_BSTR || + leftvt == VT_DATE || rightvt == VT_DATE) + resvt = VT_R8; + else if (leftvt == VT_R4 || rightvt == VT_R4) { + if (leftvt == VT_I4 || rightvt == VT_I4 || + leftvt == VT_I8 || rightvt == VT_I8 || + leftvt == VT_CY || rightvt == VT_CY) + resvt = VT_R8; + else + resvt = VT_R4; + } else if (leftvt == VT_CY || rightvt == VT_CY) + resvt = VT_CY; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + else if (leftvt == VT_I4 || rightvt == VT_I4) + resvt = VT_I4; + else if (leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL || + (leftvt == VT_EMPTY && rightvt == VT_EMPTY)) + resvt = VT_I2; + else if (leftvt == VT_UI1 || rightvt == VT_UI1) + resvt = VT_UI1; + + hres = pVarMul(&left, &right, &result); + if (bFail) { + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarMul: %d|0x%X, %d|0x%X: Expected failure, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], hres, + V_VT(&result)); + } else { + ok(hres == S_OK && V_VT(&result) == resvt, + "VarMul: %d|0x%X, %d|0x%X: expected S_OK, vt %d, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], resvt, hres, + V_VT(&result)); + } + } + } + } + + /* Test returned values */ + VARMUL(I4,4,I4,2,I4,8); + VARMUL(I2,4,I2,2,I2,8); + VARMUL(I2,-13,I4,5,I4,-65); + VARMUL(I4,-13,I4,5,I4,-65); + VARMUL(I2,7,R4,0.5f,R4,3.5f); + VARMUL(R4,0.5f,I4,5,R8,2.5); + VARMUL(R8,7.1,BOOL,0,R8,0); + VARMUL(BSTR,lbstr,I2,4,R8,48); + VARMUL(BSTR,lbstr,BOOL,1,R8,12); + VARMUL(BSTR,lbstr,R4,0.1f,R8,1.2); + VARMUL(BSTR,lbstr,BSTR,rbstr,R8,144); + VARMUL(R4,0.2f,BSTR,rbstr,R8,2.4); + VARMUL(DATE,2.25,I4,7,R8,15.75); + + VARMUL(UI1, UI1_MAX, UI1, UI1_MAX, I4, UI1_MAX * UI1_MAX); + VARMUL(I2, I2_MAX, I2, I2_MAX, I4, I2_MAX * I2_MAX); + VARMUL(I2, I2_MAX, I2, I2_MIN, I4, I2_MAX * I2_MIN); + VARMUL(I2, I2_MIN, I2, I2_MIN, I4, I2_MIN * I2_MIN); + VARMUL(I4, I4_MAX, I4, I4_MAX, R8, (double)I4_MAX * I4_MAX); + VARMUL(I4, I4_MAX, I4, I4_MIN, R8, (double)I4_MAX * I4_MIN); + VARMUL(I4, I4_MIN, I4, I4_MIN, R8, (double)I4_MIN * I4_MIN); + VARMUL(R4, R4_MAX, R4, R4_MAX, R8, (double)R4_MAX * R4_MAX); + VARMUL(R4, R4_MAX, R4, R4_MIN, R4, R4_MAX * R4_MIN); + VARMUL(R4, R4_MIN, R4, R4_MIN, R4, R4_MIN * R4_MIN); + VARMUL(R8, R8_MAX, R8, R8_MIN, R8, R8_MAX * R8_MIN); + VARMUL(R8, R8_MIN, R8, R8_MIN, R8, R8_MIN * R8_MIN); + + /* Manuly test some VT_CY and VT_DECIMAL variants */ + V_VT(&cy) = VT_CY; + hres = VarCyFromI4(4711, &V_CY(&cy)); + ok(hres == S_OK, "VarCyFromI4 failed!\n"); + V_VT(&dec) = VT_DECIMAL; + hres = VarDecFromR8(-4.2, &V_DECIMAL(&dec)); + ok(hres == S_OK, "VarDecFromR4 failed!\n"); + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = VT_I4; + V_I4(&left) = -11; + V_VT(&right) = VT_UI1; + V_UI1(&right) = 9; + + hres = VarMul(&cy, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_CY, "VarMul: expected coerced type VT_CY, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromCy(V_CY(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, 42399), "VarMul: CY value %f, expected %f\n", r, (double)42399); + + hres = VarMul(&left, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_DECIMAL, "VarMul: expected coerced type VT_DECIMAL, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromDec(&V_DECIMAL(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, 46.2), "VarMul: DECIMAL value %f, expected %f\n", r, (double)46.2); + + SysFreeString(lbstr); + SysFreeString(rbstr); +} + +static HRESULT (WINAPI *pVarAdd)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARADD(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarAdd, &left, &right, &exp ) + +static void test_VarAdd(void) +{ + static const WCHAR sz12[] = {'1','2','\0'}; + VARIANT left, right, exp, result, cy, dec; + VARTYPE i; + BSTR lbstr, rbstr; + HRESULT hres; + double r; + + CHECKPTR(VarAdd); + + lbstr = SysAllocString(sz12); + rbstr = SysAllocString(sz12); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + + SKIPTESTS(rightvt); + + if (leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + if (leftvt == VT_BSTR) + V_BSTR(&left) = lbstr; + V_VT(&right) = rightvt | ExtraFlags[i]; + if (rightvt == VT_BSTR) + V_BSTR(&right) = rbstr; + V_VT(&result) = VT_EMPTY; + resvt = VT_ERROR; + + /* Don't ask me why but native VarAdd cannot handle: + VT_I1, VT_UI2, VT_UI4, VT_INT, VT_UINT and VT_UI8. + Tested with DCOM98, Win2k, WinXP */ + if (ExtraFlags[i] & VT_ARRAY || ExtraFlags[i] & VT_BYREF || + !IsValidVariantClearVT(leftvt, ExtraFlags[i]) || + !IsValidVariantClearVT(rightvt, ExtraFlags[i]) || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == VT_VARIANT || rightvt == VT_VARIANT || + leftvt == VT_ERROR || rightvt == VT_ERROR || + leftvt == VT_I1 || rightvt == VT_I1 || + leftvt == VT_UI2 || rightvt == VT_UI2 || + leftvt == VT_UI4 || rightvt == VT_UI4 || + leftvt == VT_UI8 || rightvt == VT_UI8 || + leftvt == VT_INT || rightvt == VT_INT || + leftvt == VT_UINT || rightvt == VT_UINT) { + bFail = TRUE; + } + + if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if (leftvt == VT_DISPATCH || rightvt == VT_DISPATCH) + bFail = TRUE; + else if (leftvt == VT_DECIMAL || rightvt == VT_DECIMAL) + resvt = VT_DECIMAL; + else if (leftvt == VT_DATE || rightvt == VT_DATE) + resvt = VT_DATE; + else if (leftvt == VT_CY || rightvt == VT_CY) + resvt = VT_CY; + else if (leftvt == VT_R8 || rightvt == VT_R8) + resvt = VT_R8; + else if (leftvt == VT_BSTR || rightvt == VT_BSTR) { + if ((leftvt == VT_BSTR && rightvt == VT_BSTR) || + leftvt == VT_EMPTY || rightvt == VT_EMPTY) + resvt = VT_BSTR; + else + resvt = VT_R8; + } else if (leftvt == VT_R4 || rightvt == VT_R4) { + if (leftvt == VT_I4 || rightvt == VT_I4 || + leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_R8; + else + resvt = VT_R4; + } + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + else if (leftvt == VT_I4 || rightvt == VT_I4) + resvt = VT_I4; + else if (leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL || + (leftvt == VT_EMPTY && rightvt == VT_EMPTY)) + resvt = VT_I2; + else if (leftvt == VT_UI1 || rightvt == VT_UI1) + resvt = VT_UI1; + + hres = pVarAdd(&left, &right, &result); + if (bFail) { + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarAdd: %d|0x%X, %d|0x%X: Expected failure, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], hres, + V_VT(&result)); + } else { + ok(hres == S_OK && V_VT(&result) == resvt, + "VarAdd: %d|0x%X, %d|0x%X: expected S_OK, vt %d, got 0x%X vt %d\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], resvt, hres, + V_VT(&result)); + } + } + } + } + + /* Test returned values */ + VARADD(I4,4,I4,2,I4,6); + VARADD(I2,4,I2,2,I2,6); + VARADD(I2,-13,I4,5,I4,-8); + VARADD(I4,-13,I4,5,I4,-8); + VARADD(I2,7,R4,0.5f,R4,7.5f); + VARADD(R4,0.5f,I4,5,R8,5.5); + VARADD(R8,7.1,BOOL,0,R8,7.1); + VARADD(BSTR,lbstr,I2,4,R8,16); + VARADD(BSTR,lbstr,BOOL,1,R8,13); + VARADD(BSTR,lbstr,R4,0.1f,R8,12.1); + VARADD(R4,0.2f,BSTR,rbstr,R8,12.2); + VARADD(DATE,2.25,I4,7,DATE,9.25); + VARADD(DATE,1.25,R4,-1.7f,DATE,-0.45); + + VARADD(UI1, UI1_MAX, UI1, UI1_MAX, I2, UI1_MAX + UI1_MAX); + VARADD(I2, I2_MAX, I2, I2_MAX, I4, I2_MAX + I2_MAX); + VARADD(I2, I2_MAX, I2, I2_MIN, I2, I2_MAX + I2_MIN); + VARADD(I2, I2_MIN, I2, I2_MIN, I4, I2_MIN + I2_MIN); + VARADD(I4, I4_MAX, I4, I4_MIN, I4, I4_MAX + I4_MIN); + VARADD(I4, I4_MAX, I4, I4_MAX, R8, (double)I4_MAX + I4_MAX); + VARADD(I4, I4_MIN, I4, I4_MIN, R8, (double)I4_MIN + I4_MIN); + VARADD(R4, R4_MAX, R4, R4_MAX, R8, (double)R4_MAX + R4_MAX); + VARADD(R4, R4_MAX, R4, R4_MIN, R4, R4_MAX + R4_MIN); + VARADD(R4, R4_MIN, R4, R4_MIN, R4, R4_MIN + R4_MIN); + VARADD(R8, R8_MAX, R8, R8_MIN, R8, R8_MAX + R8_MIN); + VARADD(R8, R8_MIN, R8, R8_MIN, R8, R8_MIN + R8_MIN); + + /* Manually test BSTR + BSTR */ + V_VT(&left) = VT_BSTR; + V_BSTR(&left) = lbstr; + V_VT(&right) = VT_BSTR; + V_BSTR(&right) = rbstr; + hres = VarAdd(&left, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_BSTR, "VarAdd: expected coerced type VT_BSTR, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromStr(V_BSTR(&result), 0, 0, &r); + ok(hres == S_OK && EQ_DOUBLE(r, 1212), "VarAdd: BSTR value %f, expected %f\n", r, (double)1212); + + /* Manuly test some VT_CY and VT_DECIMAL variants */ + V_VT(&cy) = VT_CY; + hres = VarCyFromI4(4711, &V_CY(&cy)); + ok(hres == S_OK, "VarCyFromI4 failed!\n"); + V_VT(&dec) = VT_DECIMAL; + hres = VarDecFromR8(-4.2, &V_DECIMAL(&dec)); + ok(hres == S_OK, "VarDecFromR4 failed!\n"); + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = VT_I4; + V_I4(&left) = -11; + V_VT(&right) = VT_UI1; + V_UI1(&right) = 9; + + hres = VarAdd(&cy, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_CY, "VarAdd: expected coerced type VT_CY, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromCy(V_CY(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, 4720), "VarAdd: CY value %f, expected %f\n", r, (double)4720); + + hres = VarAdd(&left, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_DECIMAL, "VarAdd: expected coerced type VT_DECIMAL, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromDec(&V_DECIMAL(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, -15.2), "VarAdd: DECIMAL value %f, expected %f\n", r, (double)-15.2); + + SysFreeString(lbstr); + SysFreeString(rbstr); +} + +static void test_VarCat(void) +{ + LCID lcid; + VARIANT left, right, result, expected; + static const WCHAR sz12[] = {'1','2','\0'}; + static const WCHAR sz34[] = {'3','4','\0'}; + static const WCHAR sz1234[] = {'1','2','3','4','\0'}; + static const WCHAR date_sz12[] = {'9','/','3','0','/','1','9','8','0','1','2','\0'}; + static const WCHAR sz12_date[] = {'1','2','9','/','3','0','/','1','9','8','0','\0'}; + static const WCHAR sz_empty[] = {'\0'}; + static const WCHAR sz12_true[] = {'1','2','T','r','u','e','\0'}; + static const WCHAR sz12_false[] = {'1','2','F','a','l','s','e','\0'}; + TCHAR orig_date_format[128]; + VARTYPE leftvt, rightvt, resultvt; + HRESULT hres; + HRESULT expected_error_num; + + /* Set date format for testing */ + lcid = LOCALE_USER_DEFAULT; + GetLocaleInfo(lcid,LOCALE_SSHORTDATE,orig_date_format,128); + SetLocaleInfo(lcid,LOCALE_SSHORTDATE,"M/d/yyyy"); + + VariantInit(&left); + VariantInit(&right); + VariantInit(&result); + VariantInit(&expected); + + /* Check expected types for all combinations */ + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + /* Check if we need/have support for I8 and/or UI8 */ + if ((leftvt == VT_I8 || leftvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + + SKIPTESTS(rightvt); + expected_error_num = S_OK; + resultvt = VT_EMPTY; + + if (leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == 15 || rightvt == 15 /* Undefined type */) + continue; + + /* Check if we need/have support for I8 and/or UI8 */ + if ((rightvt == VT_I8 || rightvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + if (leftvt == VT_NULL && rightvt == VT_NULL) + resultvt = VT_NULL; + else if (leftvt == VT_VARIANT && (rightvt == VT_ERROR || + rightvt == VT_DATE || rightvt == VT_DECIMAL)) + expected_error_num = DISP_E_TYPEMISMATCH; + else if ((leftvt == VT_I2 || leftvt == VT_I4 || + leftvt == VT_R4 || leftvt == VT_R8 || + leftvt == VT_CY || leftvt == VT_BOOL || + leftvt == VT_BSTR || leftvt == VT_I1 || + leftvt == VT_UI1 || leftvt == VT_UI2 || + leftvt == VT_UI4 || leftvt == VT_I8 || + leftvt == VT_UI8 || leftvt == VT_INT || + leftvt == VT_UINT || leftvt == VT_EMPTY || + leftvt == VT_NULL || leftvt == VT_DECIMAL || + leftvt == VT_DATE) + && + (rightvt == VT_I2 || rightvt == VT_I4 || + rightvt == VT_R4 || rightvt == VT_R8 || + rightvt == VT_CY || rightvt == VT_BOOL || + rightvt == VT_BSTR || rightvt == VT_I1 || + rightvt == VT_UI1 || rightvt == VT_UI2 || + rightvt == VT_UI4 || rightvt == VT_I8 || + rightvt == VT_UI8 || rightvt == VT_INT || + rightvt == VT_UINT || rightvt == VT_EMPTY || + rightvt == VT_NULL || rightvt == VT_DECIMAL || + rightvt == VT_DATE)) + resultvt = VT_BSTR; + else if (rightvt == VT_ERROR && leftvt < VT_VOID) + expected_error_num = DISP_E_TYPEMISMATCH; + else if (leftvt == VT_ERROR && (rightvt == VT_DATE || + rightvt == VT_ERROR || rightvt == VT_DECIMAL)) + expected_error_num = DISP_E_TYPEMISMATCH; + else if (rightvt == VT_DATE || rightvt == VT_ERROR || + rightvt == VT_DECIMAL) + expected_error_num = DISP_E_BADVARTYPE; + else if (leftvt == VT_ERROR || rightvt == VT_ERROR) + expected_error_num = DISP_E_TYPEMISMATCH; + else if (leftvt == VT_VARIANT) + expected_error_num = DISP_E_TYPEMISMATCH; + else if (rightvt == VT_VARIANT && (leftvt == VT_EMPTY || + leftvt == VT_NULL || leftvt == VT_I2 || + leftvt == VT_I4 || leftvt == VT_R4 || + leftvt == VT_R8 || leftvt == VT_CY || + leftvt == VT_DATE || leftvt == VT_BSTR || + leftvt == VT_BOOL || leftvt == VT_DECIMAL || + leftvt == VT_I1 || leftvt == VT_UI1 || + leftvt == VT_UI2 || leftvt == VT_UI4 || + leftvt == VT_I8 || leftvt == VT_UI8 || + leftvt == VT_INT || leftvt == VT_UINT + )) + expected_error_num = DISP_E_TYPEMISMATCH; + else + expected_error_num = DISP_E_BADVARTYPE; + + V_VT(&left) = leftvt; + V_VT(&right) = rightvt; + + if (leftvt == VT_BSTR) + V_BSTR(&left) = SysAllocString(sz_empty); + if (rightvt == VT_BSTR) + V_BSTR(&right) = SysAllocString(sz_empty); + if (leftvt == VT_DATE) + V_DATE(&left) = 0.0; + if (rightvt == VT_DATE) + V_DATE(&right) = 0.0; + if (leftvt == VT_DECIMAL) + VarDecFromR8(0.0, &V_DECIMAL(&left)); + if (rightvt == VT_DECIMAL) + VarDecFromR8(0.0, &V_DECIMAL(&right)); + + hres = VarCat(&left, &right, &result); + + /* Determine the error code for the vt combination */ + ok(hres == expected_error_num, + "VarCat: %d, %d returned error, 0x%X expected 0x%X.\n", + leftvt, rightvt, expected_error_num, hres); + + /* Check types are correct */ + ok(V_VT(&result) == resultvt, + "VarCat: %d, %d: expected vt %d, got vt %d\n", + leftvt, rightvt, resultvt, V_VT(&result)); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + } + } + + /* Runnning single comparison tests to compare outputs */ + + /* Test concat strings */ + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_BSTR; + V_VT(&expected) = VT_BSTR; + V_BSTR(&left) = SysAllocString(sz12); + V_BSTR(&right) = SysAllocString(sz34); + V_BSTR(&expected) = SysAllocString(sz1234); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_BSTR concat with VT_BSTR failed to return correct result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + /* Test if expression is VT_ERROR */ + V_VT(&left) = VT_ERROR; + V_VT(&right) = VT_BSTR; + V_BSTR(&right) = SysAllocString(sz1234); + hres = VarCat(&left,&right,&result); + ok(hres == DISP_E_TYPEMISMATCH, "VarCat should have returned DISP_E_TYPEMISMATCH instead of 0x%08x\n", hres); + ok(V_VT(&result) == VT_EMPTY, + "VarCat: VT_ERROR concat with VT_BSTR should have returned VT_EMPTY\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_ERROR; + V_BSTR(&left) = SysAllocString(sz1234); + hres = VarCat(&left,&right,&result); + ok(hres == DISP_E_TYPEMISMATCH, "VarCat should have returned DISP_E_TYPEMISMATCH instead of 0x%08x\n", hres); + ok(V_VT(&result) == VT_EMPTY, + "VarCat: VT_BSTR concat with VT_ERROR should have returned VT_EMPTY\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + /* Test combining boolean with number */ + V_VT(&left) = VT_INT; + V_VT(&right) = VT_BOOL; + V_VT(&expected) = VT_BSTR; + V_INT(&left) = 12; + V_BOOL(&right) = TRUE; + V_BSTR(&expected) = SysAllocString(sz12_true); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_INT concat with VT_BOOL (TRUE) returned incorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + V_VT(&left) = VT_INT; + V_VT(&right) = VT_BOOL; + V_VT(&expected) = VT_BSTR; + V_INT(&left) = 12; + V_BOOL(&right) = FALSE; + V_BSTR(&expected) = SysAllocString(sz12_false); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_INT concat with VT_BOOL (FALSE) returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + /* Test when both expressions are numeric */ + V_VT(&left) = VT_INT; + V_VT(&right) = VT_INT; + V_VT(&expected) = VT_BSTR; + V_INT(&left) = 12; + V_INT(&right) = 34; + V_BSTR(&expected) = SysAllocString(sz1234); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: NUMBER concat with NUMBER returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + /* Test if one expression is numeric and the other is a string */ + V_VT(&left) = VT_INT; + V_VT(&right) = VT_BSTR; + V_INT(&left) = 12; + V_BSTR(&right) = SysAllocString(sz34); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: NUMBER concat with VT_BSTR, inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_INT; + V_BSTR(&left) = SysAllocString(sz12); + V_INT(&right) = 34; + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_BSTR concat with NUMBER, inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + /* Test concat dates with strings */ + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_DATE; + V_VT(&expected) = VT_BSTR; + V_BSTR(&left) = SysAllocString(sz12); + V_DATE(&right) = 29494.0; + V_BSTR(&expected)= SysAllocString(sz12_date); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_BSTR concat with VT_DATE returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + V_VT(&left) = VT_DATE; + V_VT(&right) = VT_BSTR; + V_VT(&expected) = VT_BSTR; + V_DATE(&left) = 29494.0; + V_BSTR(&right) = SysAllocString(sz12); + V_BSTR(&expected)= SysAllocString(date_sz12); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_DATE concat with VT_BSTR returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + /* Test of both expressions are empty */ + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_BSTR; + V_VT(&expected) = VT_BSTR; + V_BSTR(&left) = SysAllocString(sz_empty); + V_BSTR(&right) = SysAllocString(sz_empty); + V_BSTR(&expected)= SysAllocString(sz_empty); + hres = VarCat(&left,&right,&result); + ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres); + ok(VarCmp(&result,&left,lcid,0) == VARCMP_EQ, + "VarCat: EMPTY concat with EMPTY did not return empty VT_BSTR\n"); + + /* Restore original date format settings */ + SetLocaleInfo(lcid,LOCALE_SSHORTDATE,orig_date_format); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); +} + +static HRESULT (WINAPI *pVarAnd)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARAND(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarAnd, &left, &right, &exp ) + +#define VARANDCY(vt1,val1,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_CY; V_CY(&right).int64 = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarAnd, &left, &right, &exp ) + +/* Skip any type that is not defined or produces an error for every case */ +#define SKIPTESTAND(a) \ + if (a == VT_ERROR || a == VT_VARIANT || \ + a == VT_DISPATCH || a == VT_UNKNOWN || \ + a > VT_UINT || a == 15 /*not defined*/) \ + continue + +static void test_VarAnd(void) +{ + static const WCHAR szFalse[] = { '#','F','A','L','S','E','#','\0' }; + static const WCHAR szTrue[] = { '#','T','R','U','E','#','\0' }; + VARIANT left, right, exp, result; + BSTR false_str, true_str; + VARTYPE i; + HRESULT hres; + + true_str = SysAllocString(szTrue); + false_str = SysAllocString(szFalse); + + CHECKPTR(VarAnd); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + SKIPTESTAND(leftvt); + + /* Check if we need/have support for I8 and/or UI8 */ + if ((leftvt == VT_I8 || leftvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + SKIPTESTAND(rightvt); + + /* Check if we need/have support for I8 and/or UI8 */ + if ((rightvt == VT_I8 || rightvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_EMPTY; + if ((leftvt | ExtraFlags[i]) == VT_BSTR) + V_BSTR(&left) = true_str; + if ((rightvt | ExtraFlags[i]) == VT_BSTR) + V_BSTR(&right) = true_str; + + /* Native VarAnd always returns an error when using extra + * flags or if the variant combination is I8 and INT. + */ + if ((leftvt == VT_I8 && rightvt == VT_INT) || + (leftvt == VT_INT && rightvt == VT_I8) || + ExtraFlags[i] != 0) + bFail = TRUE; + + /* Determine return type */ + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + else if (leftvt == VT_I4 || rightvt == VT_I4 || + leftvt == VT_UINT || rightvt == VT_UINT || + leftvt == VT_INT || rightvt == VT_INT || + leftvt == VT_UINT || rightvt == VT_UINT || + leftvt == VT_R4 || rightvt == VT_R4 || + leftvt == VT_R8 || rightvt == VT_R8 || + leftvt == VT_CY || rightvt == VT_CY || + leftvt == VT_DATE || rightvt == VT_DATE || + leftvt == VT_I1 || rightvt == VT_I1 || + leftvt == VT_UI2 || rightvt == VT_UI2 || + leftvt == VT_UI4 || rightvt == VT_UI4 || + leftvt == VT_UI8 || rightvt == VT_UI8 || + leftvt == VT_DECIMAL || rightvt == VT_DECIMAL) + resvt = VT_I4; + else if (leftvt == VT_UI1 || rightvt == VT_UI1 || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_EMPTY || rightvt == VT_EMPTY) + if ((leftvt == VT_NULL && rightvt == VT_UI1) || + (leftvt == VT_UI1 && rightvt == VT_NULL) || + (leftvt == VT_UI1 && rightvt == VT_UI1)) + resvt = VT_UI1; + else + resvt = VT_I2; + else if (leftvt == VT_BOOL || rightvt == VT_BOOL || + (leftvt == VT_BSTR && rightvt == VT_BSTR)) + resvt = VT_BOOL; + else if (leftvt == VT_NULL || rightvt == VT_NULL || + leftvt == VT_BSTR || rightvt == VT_BSTR) + resvt = VT_NULL; + else + bFail = TRUE; + + hres = pVarAnd(&left, &right, &result); + + /* Check expected HRESULT and if result variant type is correct */ + if (bFail) + ok (hres == DISP_E_BADVARTYPE || hres == DISP_E_TYPEMISMATCH, + "VarAnd: %s|0x%X, %s|0x%X: got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], + vtstr(V_VT(&result)), hres); + else + ok (hres == S_OK && resvt == V_VT(&result), + "VarAnd: %s|0x%X, %s|0x%X: expected vt %s hr 0x%X, got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], vtstr(resvt), + S_OK, vtstr(V_VT(&result)), hres); + } + } + } + + /* + * Test returned values. Since we know the returned type is correct + * and that we handle all combinations of invalid types, just check + * that good type combinations produce the desired value. + * FIXME: Test VT_DECIMAL + */ + VARAND(EMPTY,0,EMPTY,0,I2,0); + VARAND(EMPTY,1,EMPTY,0,I2,0); + VARAND(EMPTY,1,EMPTY,1,I2,0); + VARAND(EMPTY,0,NULL,0,I2,0); + VARAND(EMPTY,1,NULL,0,I2,0); + VARAND(EMPTY,1,NULL,1,I2,0); + VARAND(EMPTY,0,I1,0,I4,0); + VARAND(EMPTY,0,I1,1,I4,0); + VARAND(EMPTY,1,I1,1,I4,0); + VARAND(EMPTY,0,UI1,0,I2,0); + VARAND(EMPTY,0,UI1,1,I2,0); + VARAND(EMPTY,1,UI1,1,I2,0); + VARAND(EMPTY,0,I2,0,I2,0); + VARAND(EMPTY,0,I2,1,I2,0); + VARAND(EMPTY,1,I2,1,I2,0); + VARAND(EMPTY,0,UI2,0,I4,0); + VARAND(EMPTY,0,UI2,1,I4,0); + VARAND(EMPTY,1,UI2,1,I4,0); + VARAND(EMPTY,0,I4,0,I4,0); + VARAND(EMPTY,0,I4,1,I4,0); + VARAND(EMPTY,1,I4,1,I4,0); + VARAND(EMPTY,0,UI4,0,I4,0); + VARAND(EMPTY,0,UI4,1,I4,0); + VARAND(EMPTY,1,UI4,1,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(EMPTY,0,I8,0,I8,0); + VARAND(EMPTY,0,I8,1,I8,0); + VARAND(EMPTY,1,I8,1,I8,0); + VARAND(EMPTY,0,UI8,0,I4,0); + VARAND(EMPTY,0,UI8,1,I4,0); + VARAND(EMPTY,1,UI8,1,I4,0); + } + VARAND(EMPTY,0,INT,0,I4,0); + VARAND(EMPTY,0,INT,1,I4,0); + VARAND(EMPTY,1,INT,1,I4,0); + VARAND(EMPTY,0,UINT,0,I4,0); + VARAND(EMPTY,0,UINT,1,I4,0); + VARAND(EMPTY,1,UINT,1,I4,0); + VARAND(EMPTY,0,BOOL,0,I2,0); + VARAND(EMPTY,0,BOOL,1,I2,0); + VARAND(EMPTY,1,BOOL,1,I2,0); + VARAND(EMPTY,0,R4,0,I4,0); + VARAND(EMPTY,0,R4,1,I4,0); + VARAND(EMPTY,1,R4,1,I4,0); + VARAND(EMPTY,0,R8,0,I4,0); + VARAND(EMPTY,0,R8,1,I4,0); + VARAND(EMPTY,1,R8,1,I4,0); + VARAND(EMPTY,0,BSTR,false_str,I2,0); + VARAND(EMPTY,0,BSTR,true_str,I2,0); + VARANDCY(EMPTY,0,10000,I4,0); + + /* NULL OR 0 = NULL. NULL OR n = n */ + VARAND(NULL,0,NULL,0,NULL,0); + VARAND(NULL,1,NULL,0,NULL,0); + VARAND(NULL,0,I1,0,I4,0); + VARAND(NULL,0,I1,1,NULL,0); + VARAND(NULL,0,UI1,0,UI1,0); + VARAND(NULL,0,UI1,1,NULL,0); + VARAND(NULL,0,I2,0,I2,0); + VARAND(NULL,0,I2,1,NULL,0); + VARAND(NULL,0,UI2,0,I4,0); + VARAND(NULL,0,UI2,1,NULL,0); + VARAND(NULL,0,I4,0,I4,0); + VARAND(NULL,0,I4,1,NULL,0); + VARAND(NULL,0,UI4,0,I4,0); + VARAND(NULL,0,UI4,1,NULL,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(NULL,0,I8,0,I8,0); + VARAND(NULL,0,I8,1,NULL,0); + VARAND(NULL,0,UI8,0,I4,0); + VARAND(NULL,0,UI8,1,NULL,0); + } + VARAND(NULL,0,INT,0,I4,0); + VARAND(NULL,0,INT,1,NULL,0); + VARAND(NULL,0,UINT,0,I4,0); + VARAND(NULL,0,UINT,1,NULL,0); + VARAND(NULL,0,BOOL,0,BOOL,0); + VARAND(NULL,0,BOOL,1,NULL,0); + VARAND(NULL,0,R4,0,I4,0); + VARAND(NULL,0,R4,1,NULL,0); + VARAND(NULL,0,R8,0,I4,0); + VARAND(NULL,0,R8,1,NULL,0); + VARAND(NULL,0,BSTR,false_str,BOOL,0); + VARAND(NULL,0,BSTR,true_str,NULL,VARIANT_FALSE); + VARANDCY(NULL,0,10000,NULL,0); + VARANDCY(NULL,0,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE); + VARAND(BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE,BOOL,VARIANT_FALSE); + VARAND(BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE); + VARAND(BOOL,VARIANT_FALSE,BOOL,VARIANT_FALSE,BOOL,VARIANT_FALSE); + + /* Assume x,y & y,x are the same from now on to reduce the number of tests */ + VARAND(BOOL,VARIANT_TRUE,I1,-1,I4,-1); + VARAND(BOOL,VARIANT_TRUE,I1,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,I1,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,UI1,255,I2,255); + VARAND(BOOL,VARIANT_TRUE,UI1,0,I2,0); + VARAND(BOOL,VARIANT_FALSE,UI1,0,I2,0); + VARAND(BOOL,VARIANT_TRUE,I2,-1,I2,-1); + VARAND(BOOL,VARIANT_TRUE,I2,0,I2,0); + VARAND(BOOL,VARIANT_FALSE,I2,0,I2,0); + VARAND(BOOL,VARIANT_TRUE,UI2,65535,I4,65535); + VARAND(BOOL,VARIANT_TRUE,UI2,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,UI2,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,I4,-1,I4,-1); + VARAND(BOOL,VARIANT_TRUE,I4,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,I4,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,UI4,0xffffffff,I4,-1); + VARAND(BOOL,VARIANT_TRUE,UI4,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,UI4,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,R4,-1,I4,-1); + VARAND(BOOL,VARIANT_TRUE,R4,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,R4,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,R8,-1,I4,-1); + VARAND(BOOL,VARIANT_TRUE,R8,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,R8,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,DATE,-1,I4,-1); + VARAND(BOOL,VARIANT_TRUE,DATE,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(BOOL,VARIANT_TRUE,I8,-1,I8,-1); + VARAND(BOOL,VARIANT_TRUE,I8,0,I8,0); + VARAND(BOOL,VARIANT_FALSE,I8,0,I8,0); + VARAND(BOOL,VARIANT_TRUE,UI8,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,UI8,0,I4,0); + } + VARAND(BOOL,VARIANT_TRUE,INT,-1,I4,-1); + VARAND(BOOL,VARIANT_TRUE,INT,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,INT,0,I4,0); + VARAND(BOOL,VARIANT_TRUE,UINT,0xffffffff,I4,-1); + VARAND(BOOL,VARIANT_TRUE,UINT,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,UINT,0,I4,0); + VARAND(BOOL,VARIANT_FALSE,BSTR,false_str,BOOL,VARIANT_FALSE); + VARAND(BOOL,VARIANT_TRUE,BSTR,false_str,BOOL,VARIANT_FALSE); + VARAND(BOOL,VARIANT_FALSE,BSTR,true_str,BOOL,VARIANT_FALSE); + VARAND(BOOL,VARIANT_TRUE,BSTR,true_str,BOOL,VARIANT_TRUE); + VARANDCY(BOOL,VARIANT_TRUE,10000,I4,1); + VARANDCY(BOOL,VARIANT_TRUE,0,I4,0); + VARANDCY(BOOL,VARIANT_FALSE,0,I4,0); + VARAND(I1,-1,I1,-1,I4,-1); + VARAND(I1,-1,I1,0,I4,0); + VARAND(I1,0,I1,0,I4,0); + VARAND(I1,-1,UI1,255,I4,255); + VARAND(I1,-1,UI1,0,I4,0); + VARAND(I1,0,UI1,0,I4,0); + VARAND(I1,-1,I2,-1,I4,-1); + VARAND(I1,-1,I2,0,I4,0); + VARAND(I1,0,I2,0,I4,0); + VARAND(I1,-1,UI2,65535,I4,65535); + VARAND(I1,-1,UI2,0,I4,0); + VARAND(I1,0,UI2,0,I4,0); + VARAND(I1,-1,I4,-1,I4,-1); + VARAND(I1,-1,I4,0,I4,0); + VARAND(I1,0,I4,0,I4,0); + VARAND(I1,-1,UI4,0xffffffff,I4,-1); + VARAND(I1,-1,UI4,0,I4,0); + VARAND(I1,0,UI4,0,I4,0); + VARAND(I1,-1,R4,-1,I4,-1); + VARAND(I1,-1,R4,0,I4,0); + VARAND(I1,0,R4,0,I4,0); + VARAND(I1,-1,R8,-1,I4,-1); + VARAND(I1,-1,R8,0,I4,0); + VARAND(I1,0,R8,0,I4,0); + VARAND(I1,-1,DATE,-1,I4,-1); + VARAND(I1,-1,DATE,0,I4,0); + VARAND(I1,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(I1,-1,I8,-1,I8,-1); + VARAND(I1,-1,I8,0,I8,0); + VARAND(I1,0,I8,0,I8,0); + VARAND(I1,-1,UI8,0,I4,0); + VARAND(I1,0,UI8,0,I4,0); + } + VARAND(I1,-1,INT,-1,I4,-1); + VARAND(I1,-1,INT,0,I4,0); + VARAND(I1,0,INT,0,I4,0); + VARAND(I1,-1,UINT,0xffffffff,I4,-1); + VARAND(I1,-1,UINT,0,I4,0); + VARAND(I1,0,UINT,0,I4,0); + VARAND(I1,0,BSTR,false_str,I4,0); + VARAND(I1,-1,BSTR,false_str,I4,0); + VARAND(I1,0,BSTR,true_str,I4,0); + VARAND(I1,-1,BSTR,true_str,I4,-1); + VARANDCY(I1,-1,10000,I4,1); + VARANDCY(I1,-1,0,I4,0); + VARANDCY(I1,0,0,I4,0); + + VARAND(UI1,255,UI1,255,UI1,255); + VARAND(UI1,255,UI1,0,UI1,0); + VARAND(UI1,0,UI1,0,UI1,0); + VARAND(UI1,255,I2,-1,I2,255); + VARAND(UI1,255,I2,0,I2,0); + VARAND(UI1,0,I2,0,I2,0); + VARAND(UI1,255,UI2,65535,I4,255); + VARAND(UI1,255,UI2,0,I4,0); + VARAND(UI1,0,UI2,0,I4,0); + VARAND(UI1,255,I4,-1,I4,255); + VARAND(UI1,255,I4,0,I4,0); + VARAND(UI1,0,I4,0,I4,0); + VARAND(UI1,255,UI4,0xffffffff,I4,255); + VARAND(UI1,255,UI4,0,I4,0); + VARAND(UI1,0,UI4,0,I4,0); + VARAND(UI1,255,R4,-1,I4,255); + VARAND(UI1,255,R4,0,I4,0); + VARAND(UI1,0,R4,0,I4,0); + VARAND(UI1,255,R8,-1,I4,255); + VARAND(UI1,255,R8,0,I4,0); + VARAND(UI1,0,R8,0,I4,0); + VARAND(UI1,255,DATE,-1,I4,255); + VARAND(UI1,255,DATE,0,I4,0); + VARAND(UI1,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(UI1,255,I8,-1,I8,255); + VARAND(UI1,255,I8,0,I8,0); + VARAND(UI1,0,I8,0,I8,0); + VARAND(UI1,255,UI8,0,I4,0); + VARAND(UI1,0,UI8,0,I4,0); + } + VARAND(UI1,255,INT,-1,I4,255); + VARAND(UI1,255,INT,0,I4,0); + VARAND(UI1,0,INT,0,I4,0); + VARAND(UI1,255,UINT,0xffffffff,I4,255); + VARAND(UI1,255,UINT,0,I4,0); + VARAND(UI1,0,UINT,0,I4,0); + VARAND(UI1,0,BSTR,false_str,I2,0); + VARAND(UI1,255,BSTR,false_str,I2,0); + VARAND(UI1,0,BSTR,true_str,I2,0); + VARAND(UI1,255,BSTR,true_str,I2,255); + VARANDCY(UI1,255,10000,I4,1); + VARANDCY(UI1,255,0,I4,0); + VARANDCY(UI1,0,0,I4,0); + + VARAND(I2,-1,I2,-1,I2,-1); + VARAND(I2,-1,I2,0,I2,0); + VARAND(I2,0,I2,0,I2,0); + VARAND(I2,-1,UI2,65535,I4,65535); + VARAND(I2,-1,UI2,0,I4,0); + VARAND(I2,0,UI2,0,I4,0); + VARAND(I2,-1,I4,-1,I4,-1); + VARAND(I2,-1,I4,0,I4,0); + VARAND(I2,0,I4,0,I4,0); + VARAND(I2,-1,UI4,0xffffffff,I4,-1); + VARAND(I2,-1,UI4,0,I4,0); + VARAND(I2,0,UI4,0,I4,0); + VARAND(I2,-1,R4,-1,I4,-1); + VARAND(I2,-1,R4,0,I4,0); + VARAND(I2,0,R4,0,I4,0); + VARAND(I2,-1,R8,-1,I4,-1); + VARAND(I2,-1,R8,0,I4,0); + VARAND(I2,0,R8,0,I4,0); + VARAND(I2,-1,DATE,-1,I4,-1); + VARAND(I2,-1,DATE,0,I4,0); + VARAND(I2,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(I2,-1,I8,-1,I8,-1); + VARAND(I2,-1,I8,0,I8,0); + VARAND(I2,0,I8,0,I8,0); + VARAND(I2,-1,UI8,0,I4,0); + VARAND(I2,0,UI8,0,I4,0); + } + VARAND(I2,-1,INT,-1,I4,-1); + VARAND(I2,-1,INT,0,I4,0); + VARAND(I2,0,INT,0,I4,0); + VARAND(I2,-1,UINT,0xffffffff,I4,-1); + VARAND(I2,-1,UINT,0,I4,0); + VARAND(I2,0,UINT,0,I4,0); + VARAND(I2,0,BSTR,false_str,I2,0); + VARAND(I2,-1,BSTR,false_str,I2,0); + VARAND(I2,0,BSTR,true_str,I2,0); + VARAND(I2,-1,BSTR,true_str,I2,-1); + VARANDCY(I2,-1,10000,I4,1); + VARANDCY(I2,-1,0,I4,0); + VARANDCY(I2,0,0,I4,0); + + VARAND(UI2,65535,UI2,65535,I4,65535); + VARAND(UI2,65535,UI2,0,I4,0); + VARAND(UI2,0,UI2,0,I4,0); + VARAND(UI2,65535,I4,-1,I4,65535); + VARAND(UI2,65535,I4,0,I4,0); + VARAND(UI2,0,I4,0,I4,0); + VARAND(UI2,65535,UI4,0xffffffff,I4,65535); + VARAND(UI2,65535,UI4,0,I4,0); + VARAND(UI2,0,UI4,0,I4,0); + VARAND(UI2,65535,R4,-1,I4,65535); + VARAND(UI2,65535,R4,0,I4,0); + VARAND(UI2,0,R4,0,I4,0); + VARAND(UI2,65535,R8,-1,I4,65535); + VARAND(UI2,65535,R8,0,I4,0); + VARAND(UI2,0,R8,0,I4,0); + VARAND(UI2,65535,DATE,-1,I4,65535); + VARAND(UI2,65535,DATE,0,I4,0); + VARAND(UI2,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(UI2,65535,I8,-1,I8,65535); + VARAND(UI2,65535,I8,0,I8,0); + VARAND(UI2,0,I8,0,I8,0); + VARAND(UI2,65535,UI8,0,I4,0); + VARAND(UI2,0,UI8,0,I4,0); + } + VARAND(UI2,65535,INT,-1,I4,65535); + VARAND(UI2,65535,INT,0,I4,0); + VARAND(UI2,0,INT,0,I4,0); + VARAND(UI2,65535,UINT,0xffffffff,I4,65535); + VARAND(UI2,65535,UINT,0,I4,0); + VARAND(UI2,0,UINT,0,I4,0); + VARAND(UI2,0,BSTR,false_str,I4,0); + VARAND(UI2,65535,BSTR,false_str,I4,0); + VARAND(UI2,0,BSTR,true_str,I4,0); + VARAND(UI2,65535,BSTR,true_str,I4,65535); + VARANDCY(UI2,65535,10000,I4,1); + VARANDCY(UI2,65535,0,I4,0); + VARANDCY(UI2,0,0,I4,0); + + VARAND(I4,-1,I4,-1,I4,-1); + VARAND(I4,-1,I4,0,I4,0); + VARAND(I4,0,I4,0,I4,0); + VARAND(I4,-1,UI4,0xffffffff,I4,-1); + VARAND(I4,-1,UI4,0,I4,0); + VARAND(I4,0,UI4,0,I4,0); + VARAND(I4,-1,R4,-1,I4,-1); + VARAND(I4,-1,R4,0,I4,0); + VARAND(I4,0,R4,0,I4,0); + VARAND(I4,-1,R8,-1,I4,-1); + VARAND(I4,-1,R8,0,I4,0); + VARAND(I4,0,R8,0,I4,0); + VARAND(I4,-1,DATE,-1,I4,-1); + VARAND(I4,-1,DATE,0,I4,0); + VARAND(I4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(I4,-1,I8,-1,I8,-1); + VARAND(I4,-1,I8,0,I8,0); + VARAND(I4,0,I8,0,I8,0); + VARAND(I4,-1,UI8,0,I4,0); + VARAND(I4,0,UI8,0,I4,0); + } + VARAND(I4,-1,INT,-1,I4,-1); + VARAND(I4,-1,INT,0,I4,0); + VARAND(I4,0,INT,0,I4,0); + VARAND(I4,-1,UINT,0xffffffff,I4,-1); + VARAND(I4,-1,UINT,0,I4,0); + VARAND(I4,0,UINT,0,I4,0); + VARAND(I4,0,BSTR,false_str,I4,0); + VARAND(I4,-1,BSTR,false_str,I4,0); + VARAND(I4,0,BSTR,true_str,I4,0); + VARAND(I4,-1,BSTR,true_str,I4,-1); + VARANDCY(I4,-1,10000,I4,1); + VARANDCY(I4,-1,0,I4,0); + VARANDCY(I4,0,0,I4,0); + + VARAND(UI4,0xffffffff,UI4,0xffffffff,I4,-1); + VARAND(UI4,0xffffffff,UI4,0,I4,0); + VARAND(UI4,0,UI4,0,I4,0); + VARAND(UI4,0xffffffff,R4,-1,I4,-1); + VARAND(UI4,0xffffffff,R4,0,I4,0); + VARAND(UI4,0,R4,0,I4,0); + VARAND(UI4,0xffffffff,R8,-1,I4,-1); + VARAND(UI4,0xffffffff,R8,0,I4,0); + VARAND(UI4,0,R8,0,I4,0); + VARAND(UI4,0xffffffff,DATE,-1,I4,-1); + VARAND(UI4,0xffffffff,DATE,0,I4,0); + VARAND(UI4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(UI4,0xffffffff,I8,0,I8,0); + VARAND(UI4,0,I8,0,I8,0); + VARAND(UI4,0xffffffff,UI8,0,I4,0); + VARAND(UI4,0,UI8,0,I4,0); + } + VARAND(UI4,0xffffffff,INT,-1,I4,-1); + VARAND(UI4,0xffffffff,INT,0,I4,0); + VARAND(UI4,0,INT,0,I4,0); + VARAND(UI4,0xffffffff,UINT,0xffffffff,I4,-1); + VARAND(UI4,0xffffffff,UINT,0,I4,0); + VARAND(UI4,0,UINT,0,I4,0); + VARAND(UI4,0,BSTR,false_str,I4,0); + VARAND(UI4,0xffffffff,BSTR,false_str,I4,0); + VARAND(UI4,0,BSTR,true_str,I4,0); + VARAND(UI4,0xffffffff,BSTR,true_str,I4,-1); + VARANDCY(UI4,0xffffffff,10000,I4,1); + VARANDCY(UI4,0xffffffff,0,I4,0); + VARANDCY(UI4,0,0,I4,0); + + VARAND(R4,-1,R4,-1,I4,-1); + VARAND(R4,-1,R4,0,I4,0); + VARAND(R4,0,R4,0,I4,0); + VARAND(R4,-1,R8,-1,I4,-1); + VARAND(R4,-1,R8,0,I4,0); + VARAND(R4,0,R8,0,I4,0); + VARAND(R4,-1,DATE,-1,I4,-1); + VARAND(R4,-1,DATE,0,I4,0); + VARAND(R4,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(R4,-1,I8,-1,I8,-1); + VARAND(R4,-1,I8,0,I8,0); + VARAND(R4,0,I8,0,I8,0); + VARAND(R4,-1,UI8,0,I4,0); + VARAND(R4,0,UI8,0,I4,0); + } + VARAND(R4,-1,INT,-1,I4,-1); + VARAND(R4,-1,INT,0,I4,0); + VARAND(R4,0,INT,0,I4,0); + VARAND(R4,-1,UINT,0xffffffff,I4,-1); + VARAND(R4,-1,UINT,0,I4,0); + VARAND(R4,0,UINT,0,I4,0); + VARAND(R4,0,BSTR,false_str,I4,0); + VARAND(R4,-1,BSTR,false_str,I4,0); + VARAND(R4,0,BSTR,true_str,I4,0); + VARAND(R4,-1,BSTR,true_str,I4,-1); + VARANDCY(R4,-1,10000,I4,1); + VARANDCY(R4,-1,0,I4,0); + VARANDCY(R4,0,0,I4,0); + + VARAND(R8,-1,R8,-1,I4,-1); + VARAND(R8,-1,R8,0,I4,0); + VARAND(R8,0,R8,0,I4,0); + VARAND(R8,-1,DATE,-1,I4,-1); + VARAND(R8,-1,DATE,0,I4,0); + VARAND(R8,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(R8,-1,I8,-1,I8,-1); + VARAND(R8,-1,I8,0,I8,0); + VARAND(R8,0,I8,0,I8,0); + VARAND(R8,-1,UI8,0,I4,0); + VARAND(R8,0,UI8,0,I4,0); + } + VARAND(R8,-1,INT,-1,I4,-1); + VARAND(R8,-1,INT,0,I4,0); + VARAND(R8,0,INT,0,I4,0); + VARAND(R8,-1,UINT,0xffffffff,I4,-1); + VARAND(R8,-1,UINT,0,I4,0); + VARAND(R8,0,UINT,0,I4,0); + VARAND(R8,0,BSTR,false_str,I4,0); + VARAND(R8,-1,BSTR,false_str,I4,0); + VARAND(R8,0,BSTR,true_str,I4,0); + VARAND(R8,-1,BSTR,true_str,I4,-1); + VARANDCY(R8,-1,10000,I4,1); + VARANDCY(R8,-1,0,I4,0); + VARANDCY(R8,0,0,I4,0); + + VARAND(DATE,-1,DATE,-1,I4,-1); + VARAND(DATE,-1,DATE,0,I4,0); + VARAND(DATE,0,DATE,0,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARAND(DATE,-1,I8,-1,I8,-1); + VARAND(DATE,-1,I8,0,I8,0); + VARAND(DATE,0,I8,0,I8,0); + VARAND(DATE,-1,UI8,0,I4,0); + VARAND(DATE,0,UI8,0,I4,0); + } + VARAND(DATE,-1,INT,-1,I4,-1); + VARAND(DATE,-1,INT,0,I4,0); + VARAND(DATE,0,INT,0,I4,0); + VARAND(DATE,-1,UINT,0xffffffff,I4,-1); + VARAND(DATE,-1,UINT,0,I4,0); + VARAND(DATE,0,UINT,0,I4,0); + VARAND(DATE,0,BSTR,false_str,I4,0); + VARAND(DATE,-1,BSTR,false_str,I4,0); + VARAND(DATE,0,BSTR,true_str,I4,0); + VARAND(DATE,-1,BSTR,true_str,I4,-1); + VARANDCY(DATE,-1,10000,I4,1); + VARANDCY(DATE,-1,0,I4,0); + VARANDCY(DATE,0,0,I4,0); + + if (HAVE_OLEAUT32_I8) + { + VARAND(I8,-1,I8,-1,I8,-1); + VARAND(I8,-1,I8,0,I8,0); + VARAND(I8,0,I8,0,I8,0); + VARAND(I8,-1,UI8,0,I8,0); + VARAND(I8,0,UI8,0,I8,0); + VARAND(I8,-1,UINT,0,I8,0); + VARAND(I8,0,UINT,0,I8,0); + VARAND(I8,0,BSTR,false_str,I8,0); + VARAND(I8,-1,BSTR,false_str,I8,0); + VARAND(I8,0,BSTR,true_str,I8,0); + VARAND(I8,-1,BSTR,true_str,I8,-1); + VARANDCY(I8,-1,10000,I8,1); + VARANDCY(I8,-1,0,I8,0); + VARANDCY(I8,0,0,I8,0); + + VARAND(UI8,0xffff,UI8,0xffff,I4,0xffff); + VARAND(UI8,0xffff,UI8,0,I4,0); + VARAND(UI8,0,UI8,0,I4,0); + VARAND(UI8,0xffff,INT,-1,I4,65535); + VARAND(UI8,0xffff,INT,0,I4,0); + VARAND(UI8,0,INT,0,I4,0); + VARAND(UI8,0xffff,UINT,0xffff,I4,0xffff); + VARAND(UI8,0xffff,UINT,0,I4,0); + VARAND(UI8,0,UINT,0,I4,0); + VARAND(UI8,0,BSTR,false_str,I4,0); + VARAND(UI8,0xffff,BSTR,false_str,I4,0); + VARAND(UI8,0,BSTR,true_str,I4,0); + VARAND(UI8,0xffff,BSTR,true_str,I4,65535); + VARANDCY(UI8,0xffff,10000,I4,1); + VARANDCY(UI8,0xffff,0,I4,0); + VARANDCY(UI8,0,0,I4,0); + } + + VARAND(INT,-1,INT,-1,I4,-1); + VARAND(INT,-1,INT,0,I4,0); + VARAND(INT,0,INT,0,I4,0); + VARAND(INT,-1,UINT,0xffff,I4,65535); + VARAND(INT,-1,UINT,0,I4,0); + VARAND(INT,0,UINT,0,I4,0); + VARAND(INT,0,BSTR,false_str,I4,0); + VARAND(INT,-1,BSTR,false_str,I4,0); + VARAND(INT,0,BSTR,true_str,I4,0); + VARAND(INT,-1,BSTR,true_str,I4,-1); + VARANDCY(INT,-1,10000,I4,1); + VARANDCY(INT,-1,0,I4,0); + VARANDCY(INT,0,0,I4,0); + + VARAND(UINT,0xffff,UINT,0xffff,I4,0xffff); + VARAND(UINT,0xffff,UINT,0,I4,0); + VARAND(UINT,0,UINT,0,I4,0); + VARAND(UINT,0,BSTR,false_str,I4,0); + VARAND(UINT,0xffff,BSTR, false_str,I4,0); + VARAND(UINT,0,BSTR,true_str,I4,0); + VARAND(UINT,0xffff,BSTR,true_str,I4,65535); + VARANDCY(UINT,0xffff,10000,I4,1); + VARANDCY(UINT,0xffff,0,I4,0); + VARANDCY(UINT,0,0,I4,0); + + VARAND(BSTR,false_str,BSTR,false_str,BOOL,0); + VARAND(BSTR,true_str,BSTR,false_str,BOOL,VARIANT_FALSE); + VARAND(BSTR,true_str,BSTR,true_str,BOOL,VARIANT_TRUE); + VARANDCY(BSTR,true_str,10000,I4,1); + VARANDCY(BSTR,false_str,10000,I4,0); + + SysFreeString(true_str); + SysFreeString(false_str); +} + +static HRESULT (WINAPI *pVarCmp)(LPVARIANT,LPVARIANT,LCID,ULONG); + +static void test_cmp( int line, LCID lcid, UINT flags, VARIANT *left, VARIANT *right, HRESULT result ) +{ + HRESULT hres; + + hres = pVarCmp(left,right,lcid,flags); + ok_(__FILE__,line)(hres == result, "VarCmp(%s,%s): expected 0x%x, got hres=0x%x\n", + variantstr(left), variantstr(right), result, hres ); +} +static void test_cmpex( int line, LCID lcid, VARIANT *left, VARIANT *right, + HRESULT res1, HRESULT res2, HRESULT res3, HRESULT res4 ) +{ + test_cmp( line, lcid, 0, left, right, res1 ); + V_VT(left) |= VT_RESERVED; + test_cmp( line, lcid, 0, left, right, res2 ); + V_VT(left) &= ~VT_RESERVED; + V_VT(right) |= VT_RESERVED; + test_cmp( line, lcid, 0, left, right, res3 ); + V_VT(left) |= VT_RESERVED; + test_cmp( line, lcid, 0, left, right, res4 ); + ok_(__FILE__,line)(V_VT(left) & V_VT(right) & VT_RESERVED, "VT_RESERVED filtered out\n"); +} + +/* ERROR from wingdi.h is interfering here */ +#undef ERROR +#define _VARCMP(vt1,val1,vtfl1,vt2,val2,vtfl2,lcid,flags,result) \ + V_##vt1(&left) = val1; V_VT(&left) = VT_##vt1 | vtfl1; \ + V_##vt2(&right) = val2; V_VT(&right) = VT_##vt2 | vtfl2; \ + test_cmp( __LINE__, lcid, flags, &left, &right, result ) +#define VARCMPEX(vt1,val1,vt2,val2,res1,res2,res3,res4) \ + V_##vt1(&left) = val1; V_VT(&left) = VT_##vt1; \ + V_##vt2(&right) = val2; V_VT(&right) = VT_##vt2; \ + test_cmpex( __LINE__, lcid, &left, &right, res1, res2, res3, res4 ) +#define VARCMP(vt1,val1,vt2,val2,result) \ + VARCMPEX(vt1,val1,vt2,val2,result,result,result,result) +/* The above macros do not work for VT_NULL as NULL gets expanded first */ +#define V_NULL_ V_NULL +#define VT_NULL_ VT_NULL + +static void test_VarCmp(void) +{ + VARIANT left, right; + VARTYPE i; + LCID lcid; + HRESULT hres; + DECIMAL dec; + static const WCHAR szhuh[] = {'h','u','h','?','\0'}; + static const WCHAR sz2cents[] = {'2','c','e','n','t','s','\0'}; + static const WCHAR szempty[] = {'\0'}; + static const WCHAR sz0[] = {'0','\0'}; + static const WCHAR sz1[] = {'1','\0'}; + static const WCHAR sz7[] = {'7','\0'}; + static const WCHAR sz42[] = {'4','2','\0'}; + static const WCHAR sz1neg[] = {'-','1','\0'}; + static const WCHAR sz666neg[] = {'-','6','6','6','\0'}; + static const WCHAR sz1few[] = {'1','.','0','0','0','0','0','0','0','1','\0'}; + BSTR bstrhuh, bstrempty, bstr0, bstr1, bstr7, bstr42, bstr1neg, bstr666neg; + BSTR bstr2cents, bstr1few; + + CHECKPTR(VarCmp); + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + bstrempty = SysAllocString(szempty); + bstrhuh = SysAllocString(szhuh); + bstr2cents = SysAllocString(sz2cents); + bstr0 = SysAllocString(sz0); + bstr1 = SysAllocString(sz1); + bstr7 = SysAllocString(sz7); + bstr42 = SysAllocString(sz42); + bstr1neg = SysAllocString(sz1neg); + bstr666neg = SysAllocString(sz666neg); + bstr1few = SysAllocString(sz1few); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + HRESULT expect = VARCMP_EQ; + + SKIPTESTS(rightvt); + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + if (leftvt == VT_BSTR) { + V_BSTR(&left) = bstr1neg; + if (ExtraFlags[i] & VT_RESERVED) + expect = VARCMP_LT; + else + expect = VARCMP_GT; + } + V_VT(&right) = rightvt | ExtraFlags[i]; + if (rightvt == VT_BSTR) { + V_BSTR(&right) = bstr1neg; + if (ExtraFlags[i] & VT_RESERVED) + expect = VARCMP_GT; + else + expect = VARCMP_LT; + } + + /* Don't ask me why but native VarCmp cannot handle: + VT_I1, VT_UI2, VT_UI4, VT_UINT and VT_UI8. + VT_INT is only supported as left variant. Go figure. + Tested with DCOM98, Win2k, WinXP */ + if (ExtraFlags[i] & VT_ARRAY || ExtraFlags[i] & VT_BYREF || + !IsValidVariantClearVT(leftvt, ExtraFlags[i] & ~VT_RESERVED) || + !IsValidVariantClearVT(rightvt, ExtraFlags[i] & ~VT_RESERVED) || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_ERROR || rightvt == VT_ERROR || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN || + leftvt == VT_VARIANT || rightvt == VT_VARIANT || + leftvt == VT_I1 || rightvt == VT_I1 || + leftvt == VT_UI2 || rightvt == VT_UI2 || + leftvt == VT_UI4 || rightvt == VT_UI4 || + leftvt == VT_UI8 || rightvt == VT_UI8 || + rightvt == VT_INT || + leftvt == VT_UINT || rightvt == VT_UINT) { + bFail = TRUE; + } + + if (leftvt == VT_ERROR && rightvt == VT_ERROR && + !(ExtraFlags[i] & ~VT_RESERVED)) { + expect = VARCMP_EQ; + bFail = FALSE; + } else if (leftvt == VT_NULL || rightvt == VT_NULL) + expect = VARCMP_NULL; + else if (leftvt == VT_BSTR && rightvt == VT_BSTR) + expect = VARCMP_EQ; + else if (leftvt == VT_BSTR && rightvt == VT_EMPTY) + expect = VARCMP_GT; + else if (leftvt == VT_EMPTY && rightvt == VT_BSTR) + expect = VARCMP_LT; + + hres = pVarCmp(&left, &right, LOCALE_USER_DEFAULT, 0); + if (bFail) { + ok(hres == DISP_E_TYPEMISMATCH || hres == DISP_E_BADVARTYPE, + "VarCmp: %d|0x%X, %d|0x%X: Expected failure, got 0x%X\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], hres); + } else { + ok(hres == expect, + "VarCmp: %d|0x%X, %d|0x%X: Expected 0x%X, got 0x%X\n", + leftvt, ExtraFlags[i], rightvt, ExtraFlags[i], expect, + hres); + } + } + } + } + + /* VARCMP{,EX} run each 4 tests with a permutation of all posible + input variants with (1) and without (0) VT_RESERVED set. The order + of the permutations is (0,0); (1,0); (0,1); (1,1) */ + VARCMP(INT,4711,I2,4711,VARCMP_EQ); + VARCMP(INT,4711,I2,-4711,VARCMP_GT); + VARCMP(ERROR,0,ERROR,0,VARCMP_EQ); + VARCMP(ERROR,0,UI1,0,DISP_E_TYPEMISMATCH); + VARCMP(EMPTY,0,EMPTY,0,VARCMP_EQ); + VARCMP(I4,1,R8,1.0,VARCMP_EQ); + VARCMP(EMPTY,19,I2,0,VARCMP_EQ); + ok(V_EMPTY(&left) == 19, "VT_EMPTY modified!\n"); + VARCMP(I4,1,UI1,1,VARCMP_EQ); + VARCMP(I2,2,I2,2,VARCMP_EQ); + VARCMP(I2,1,I2,2,VARCMP_LT); + VARCMP(I2,2,I2,1,VARCMP_GT); + VARCMP(I2,2,EMPTY,1,VARCMP_GT); + VARCMP(I2,2,NULL_,1,VARCMP_NULL); + + /* BSTR handling, especially in conjunction with VT_RESERVED */ + VARCMP(BSTR,bstr0,NULL_,0,VARCMP_NULL); + VARCMP(BSTR,bstr0,BSTR,bstr0,VARCMP_EQ); + VARCMP(BSTR,bstrempty,BSTR,bstr0,VARCMP_LT); + VARCMP(BSTR,bstr7,BSTR,bstr0,VARCMP_GT); + VARCMP(BSTR,bstr7,BSTR,bstr1neg,VARCMP_GT); + VARCMP(BSTR,bstr0,BSTR,NULL,VARCMP_GT); + VARCMP(BSTR,NULL,BSTR,NULL,VARCMP_EQ); + VARCMP(BSTR,bstrempty,BSTR,NULL,VARCMP_EQ); + VARCMP(BSTR,NULL,EMPTY,0,VARCMP_EQ); + VARCMP(EMPTY,0,BSTR,NULL,VARCMP_EQ); + VARCMP(EMPTY,0,BSTR,bstrempty,VARCMP_EQ); + VARCMP(EMPTY,1,BSTR,bstrempty,VARCMP_EQ); + VARCMP(BSTR,bstr0,EMPTY,0,VARCMP_GT); + VARCMP(BSTR,bstr42,EMPTY,0,VARCMP_GT); + VARCMPEX(BSTR,bstrempty,UI1,0,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMPEX(BSTR,bstrempty,I2,-1,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMPEX(I4,0,BSTR,bstrempty,VARCMP_LT,VARCMP_LT,VARCMP_GT,VARCMP_LT); + VARCMPEX(BSTR,NULL,UI1,0,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMPEX(I4,7,BSTR,NULL,VARCMP_LT,VARCMP_LT,VARCMP_GT,VARCMP_LT); + _VARCMP(BSTR,(BSTR)100,0,I2,100,0,lcid,0,VARCMP_GT); + VARCMPEX(BSTR,bstr0,UI1,0,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_GT); + VARCMPEX(I2,0,BSTR,bstr0,VARCMP_LT,VARCMP_EQ,VARCMP_EQ,VARCMP_LT); + VARCMP(BSTR,bstrhuh,I4,I4_MAX,VARCMP_GT); + VARCMP(BSTR,bstr2cents,I4,2,VARCMP_GT); + VARCMPEX(BSTR,bstr2cents,I4,42,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMP(BSTR,bstr2cents,I4,-1,VARCMP_GT); + VARCMPEX(BSTR,bstr2cents,I4,-666,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMPEX(BSTR,bstr0,I2,0,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_GT); + VARCMPEX(BSTR,bstr0,I4,0,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_GT); + VARCMPEX(BSTR,bstr0,I4,-666,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMP(BSTR,bstr1,I4,0,VARCMP_GT); + VARCMPEX(BSTR,bstr1,I4,1,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_GT); + VARCMPEX(BSTR,bstr1,I4,I4_MAX,VARCMP_GT,VARCMP_LT,VARCMP_LT,VARCMP_GT); + VARCMPEX(BSTR,bstr1,I4,-1,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMP(BSTR,bstr7,I4,1,VARCMP_GT); + VARCMPEX(BSTR,bstr7,I4,7,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_GT); + VARCMPEX(BSTR,bstr7,I4,42,VARCMP_GT,VARCMP_GT,VARCMP_LT,VARCMP_GT); + VARCMPEX(BSTR,bstr42,I4,7,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_GT); + VARCMPEX(BSTR,bstr42,I4,42,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_GT); + VARCMPEX(BSTR,bstr42,I4,I4_MAX,VARCMP_GT,VARCMP_GT,VARCMP_LT,VARCMP_GT); + VARCMPEX(BSTR,bstr1neg,I4,1,VARCMP_GT,VARCMP_GT,VARCMP_LT,VARCMP_LT); + VARCMPEX(BSTR,bstr1neg,I4,42,VARCMP_GT,VARCMP_LT,VARCMP_LT,VARCMP_LT); + VARCMPEX(BSTR,bstr1neg,I4,-1,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_LT); + VARCMPEX(BSTR,bstr1neg,I4,-666,VARCMP_GT,VARCMP_LT,VARCMP_GT,VARCMP_LT); + VARCMPEX(BSTR,bstr666neg,I4,1,VARCMP_GT,VARCMP_GT,VARCMP_LT,VARCMP_LT); + VARCMPEX(BSTR,bstr666neg,I4,7,VARCMP_GT,VARCMP_LT,VARCMP_LT,VARCMP_LT); + VARCMPEX(BSTR,bstr666neg,I4,42,VARCMP_GT,VARCMP_GT,VARCMP_LT,VARCMP_LT); + VARCMPEX(BSTR,bstr666neg,I4,I4_MAX,VARCMP_GT,VARCMP_GT,VARCMP_LT,VARCMP_LT); + VARCMPEX(BSTR,bstr666neg,I4,-1,VARCMP_GT,VARCMP_GT,VARCMP_LT,VARCMP_LT); + VARCMPEX(BSTR,bstr666neg,I4,-666,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_LT); + VARCMPEX(BSTR,bstr7,R8,7.0,VARCMP_GT,VARCMP_EQ,VARCMP_EQ,VARCMP_GT); + VARCMPEX(R8,3.141592,BSTR,NULL,VARCMP_LT,VARCMP_LT,VARCMP_GT,VARCMP_LT); + VARCMP(BSTR,bstr7,BSTR,bstr7,VARCMP_EQ); + VARCMP(BSTR,bstr7,BSTR,bstr42,VARCMP_GT); + VARCMP(BSTR,bstr42,BSTR,bstr7,VARCMP_LT); + + /* DECIMAL handling */ + setdec(&dec,0,0,0,0); + VARCMPEX(DECIMAL,dec,BSTR,bstr0,VARCMP_LT,VARCMP_EQ,VARCMP_EQ,VARCMP_LT); + setdec64(&dec,0,0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF); /* max DECIMAL */ + VARCMP(DECIMAL,dec,R8,R8_MAX,VARCMP_LT); /* R8 has bigger range */ + VARCMP(DECIMAL,dec,DATE,R8_MAX,VARCMP_LT); /* DATE has bigger range */ + setdec64(&dec,0,0x80,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF); + VARCMP(DECIMAL,dec,R8,-R8_MAX,VARCMP_GT); + setdec64(&dec,20,0,0x5,0x6BC75E2D,0x63100001); /* 1+1e-20 */ + VARCMP(DECIMAL,dec,R8,1,VARCMP_GT); /* DECIMAL has higher precision */ + + /* Show that DATE is handled just as a R8 */ + VARCMP(DATE,DATE_MAX,DATE,DATE_MAX+1,VARCMP_LT); + VARCMP(DATE,DATE_MIN,DATE,DATE_MIN-1,VARCMP_GT); + VARCMP(DATE,R8_MIN,R8,R8_MIN,VARCMP_EQ); + VARCMP(DATE,1,DATE,1+1e-15,VARCMP_LT); /* 1e-15 == 8.64e-11 seconds */ + VARCMP(DATE,25570.0,DATE,25570.0,VARCMP_EQ); + VARCMP(DATE,25570.0,DATE,25571.0,VARCMP_LT); + VARCMP(DATE,25571.0,DATE,25570.0,VARCMP_GT); + VARCMP(DATE,25570.0,EMPTY,0,VARCMP_GT); + VARCMP(DATE,25570.0,NULL_,0,VARCMP_NULL); + + /* R4 precision handling */ + VARCMP(R4,1,R8,1+1e-8,VARCMP_EQ); + VARCMP(R8,1+1e-8,R4,1,VARCMP_EQ); + VARCMP(R8,1+1e-8,R8,1,VARCMP_GT); + VARCMP(R8,R4_MAX*1.1,R4,R4_MAX,VARCMP_GT); + VARCMP(R4,R4_MAX,R8,R8_MAX,VARCMP_LT); + VARCMP(R4,1,DATE,1+1e-8,VARCMP_EQ); + VARCMP(R4,1,BSTR,bstr1few,VARCMP_LT); /* bstr1few == 1+1e-8 */ + setdec(&dec,8,0,0,0x5F5E101); /* 1+1e-8 */ + VARCMP(R4,1,DECIMAL,dec,VARCMP_LT); + + SysFreeString(bstrhuh); + SysFreeString(bstrempty); + SysFreeString(bstr0); + SysFreeString(bstr1); + SysFreeString(bstr7); + SysFreeString(bstr42); + SysFreeString(bstr1neg); + SysFreeString(bstr666neg); + SysFreeString(bstr2cents); + SysFreeString(bstr1few); +} + +static HRESULT (WINAPI *pVarPow)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARPOW(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarPow, &left, &right, &exp ) + +/* Skip any type that is not defined or produces an error for every case */ +#define SKIPTESTPOW(a) \ + if (a == VT_ERROR || a == VT_VARIANT || \ + a == VT_DISPATCH || a == VT_UNKNOWN || \ + a == VT_RECORD || a > VT_UINT || \ + a == 15 /*not defined*/) \ + continue + +static void test_VarPow(void) +{ + static const WCHAR str2[] = { '2','\0' }; + static const WCHAR str3[] = { '3','\0' }; + VARIANT left, right, exp, result, cy, dec; + BSTR num2_str, num3_str; + VARTYPE i; + HRESULT hres; + + CHECKPTR(VarPow); + + num2_str = SysAllocString(str2); + num3_str = SysAllocString(str3); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + SKIPTESTPOW(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + SKIPTESTPOW(rightvt); + + /* Native crashes with VT_BYREF */ + if (ExtraFlags[i] == VT_BYREF) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_EMPTY; + + if (leftvt == VT_BSTR) + V_BSTR(&left) = num2_str; + if (rightvt == VT_BSTR) + V_BSTR(&right) = num2_str; + + /* Native VarPow always returns an error when using extra flags */ + if (ExtraFlags[i] != 0) + bFail = TRUE; + + /* Determine return type */ + else if ((leftvt == VT_NULL || rightvt == VT_NULL) && + ((leftvt != VT_I8 && leftvt != VT_UI8 && + rightvt != VT_I8 && rightvt != VT_UI8) || HAVE_OLEAUT32_I8)) + resvt = VT_NULL; + else if ((leftvt == VT_EMPTY || leftvt == VT_I2 || + leftvt == VT_I4 || leftvt == VT_R4 || + leftvt == VT_R8 || leftvt == VT_CY || + leftvt == VT_DATE || leftvt == VT_BSTR || + leftvt == VT_BOOL || leftvt == VT_DECIMAL || + (leftvt >= VT_I1 && leftvt <= VT_UI4) || + (HAVE_OLEAUT32_I8 && (leftvt == VT_I8 || leftvt == VT_UI8)) || + leftvt == VT_INT || leftvt == VT_UINT) && + (rightvt == VT_EMPTY || rightvt == VT_I2 || + rightvt == VT_I4 || rightvt == VT_R4 || + rightvt == VT_R8 || rightvt == VT_CY || + rightvt == VT_DATE || rightvt == VT_BSTR || + rightvt == VT_BOOL || rightvt == VT_DECIMAL || + (rightvt >= VT_I1 && rightvt <= VT_UI4) || + (HAVE_OLEAUT32_I8 && (rightvt == VT_I8 || rightvt == VT_UI8)) || + rightvt == VT_INT || rightvt == VT_UINT)) + resvt = VT_R8; + else + bFail = TRUE; + + hres = pVarPow(&left, &right, &result); + + /* Check expected HRESULT and if result variant type is correct */ + if (bFail) + ok (hres == DISP_E_BADVARTYPE || hres == DISP_E_TYPEMISMATCH, + "VarPow: %s|0x%X, %s|0x%X: got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], + vtstr(V_VT(&result)), hres); + else + ok (hres == S_OK && resvt == V_VT(&result), + "VarPow: %s|0x%X, %s|0x%X: expected vt %s hr 0x%X, got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], vtstr(resvt), + S_OK, vtstr(V_VT(&result)), hres); + } + } + } + + /* Check return values for valid variant type combinations */ + VARPOW(EMPTY,0,EMPTY,0,R8,1.0); + VARPOW(EMPTY,0,NULL,0,NULL,0); + VARPOW(EMPTY,0,I2,3,R8,0.0); + VARPOW(EMPTY,0,I4,3,R8,0.0); + VARPOW(EMPTY,0,R4,3.0f,R8,0.0); + VARPOW(EMPTY,0,R8,3.0,R8,0.0); + VARPOW(EMPTY,0,DATE,3,R8,0.0); + VARPOW(EMPTY,0,BSTR,num3_str,R8,0.0); + VARPOW(EMPTY,0,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(EMPTY,0,I1,3,R8,0.0); + VARPOW(EMPTY,0,UI1,3,R8,0.0); + VARPOW(EMPTY,0,UI2,3,R8,0.0); + VARPOW(EMPTY,0,UI4,3,R8,0.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(EMPTY,0,I8,3,R8,0.0); + VARPOW(EMPTY,0,UI8,3,R8,0.0); + } + VARPOW(EMPTY,0,INT,3,R8,0.0); + VARPOW(EMPTY,0,UINT,3,R8,0.0); + VARPOW(NULL,0,EMPTY,0,NULL,0); + VARPOW(NULL,0,NULL,0,NULL,0); + VARPOW(NULL,0,I2,3,NULL,0); + VARPOW(NULL,0,I4,3,NULL,0); + VARPOW(NULL,0,R4,3.0f,NULL,0); + VARPOW(NULL,0,R8,3.0,NULL,0); + VARPOW(NULL,0,DATE,3,NULL,0); + VARPOW(NULL,0,BSTR,num3_str,NULL,0); + VARPOW(NULL,0,BOOL,VARIANT_TRUE,NULL,0); + VARPOW(NULL,0,I1,3,NULL,0); + VARPOW(NULL,0,UI1,3,NULL,0); + VARPOW(NULL,0,UI2,3,NULL,0); + VARPOW(NULL,0,UI4,3,NULL,0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(NULL,0,I8,3,NULL,0); + VARPOW(NULL,0,UI8,3,NULL,0); + } + VARPOW(NULL,0,INT,3,NULL,0); + VARPOW(NULL,0,UINT,3,NULL,0); + VARPOW(I2,2,EMPTY,0,R8,1.0); + VARPOW(I2,2,NULL,0,NULL,0); + VARPOW(I2,2,I2,3,R8,8.0); + VARPOW(I2,2,I4,3,R8,8.0); + VARPOW(I2,2,R4,3.0f,R8,8.0); + VARPOW(I2,2,R8,3.0,R8,8.0); + VARPOW(I2,2,DATE,3,R8,8.0); + VARPOW(I2,2,BSTR,num3_str,R8,8.0); + VARPOW(I2,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(I2,2,I1,3,R8,8.0); + VARPOW(I2,2,UI1,3,R8,8.0); + VARPOW(I2,2,UI2,3,R8,8.0); + VARPOW(I2,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(I2,2,I8,3,R8,8.0); + VARPOW(I2,2,UI8,3,R8,8.0); + } + VARPOW(I2,2,INT,3,R8,8.0); + VARPOW(I2,2,UINT,3,R8,8.0); + VARPOW(I4,2,EMPTY,0,R8,1.0); + VARPOW(I4,2,NULL,0,NULL,0); + VARPOW(I4,2,I2,3,R8,8.0); + VARPOW(I4,2,I4,3,R8,8.0); + VARPOW(I4,2,R4,3.0f,R8,8.0); + VARPOW(I4,2,R8,3.0,R8,8.0); + VARPOW(I4,2,DATE,3,R8,8.0); + VARPOW(I4,2,BSTR,num3_str,R8,8.0); + VARPOW(I4,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(I4,2,I1,3,R8,8.0); + VARPOW(I4,2,UI1,3,R8,8.0); + VARPOW(I4,2,UI2,3,R8,8.0); + VARPOW(I4,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(I4,2,I8,3,R8,8.0); + VARPOW(I4,2,UI8,3,R8,8.0); + } + VARPOW(I4,2,INT,3,R8,8.0); + VARPOW(I4,2,UINT,3,R8,8.0); + VARPOW(R4,2,EMPTY,0,R8,1.0); + VARPOW(R4,2,NULL,0,NULL,0); + VARPOW(R4,2,I2,3,R8,8.0); + VARPOW(R4,2,I4,3,R8,8.0); + VARPOW(R4,2,R4,3.0f,R8,8.0); + VARPOW(R4,2,R8,3.0,R8,8.0); + VARPOW(R4,2,DATE,3,R8,8.0); + VARPOW(R4,2,BSTR,num3_str,R8,8.0); + VARPOW(R4,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(R4,2,I1,3,R8,8.0); + VARPOW(R4,2,UI1,3,R8,8.0); + VARPOW(R4,2,UI2,3,R8,8.0); + VARPOW(R4,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(R4,2,I8,3,R8,8.0); + VARPOW(R4,2,UI8,3,R8,8.0); + } + VARPOW(R4,2,INT,3,R8,8.0); + VARPOW(R4,2,UINT,3,R8,8.0); + VARPOW(R8,2,EMPTY,0,R8,1.0); + VARPOW(R8,2,NULL,0,NULL,0); + VARPOW(R8,2,I2,3,R8,8.0); + VARPOW(R8,2,I4,3,R8,8.0); + VARPOW(R8,2,R4,3.0f,R8,8.0); + VARPOW(R8,2,R8,3.0,R8,8.0); + VARPOW(R8,2,DATE,3,R8,8.0); + VARPOW(R8,2,BSTR,num3_str,R8,8.0); + VARPOW(R8,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(R8,2,I1,3,R8,8.0); + VARPOW(R8,2,UI1,3,R8,8.0); + VARPOW(R8,2,UI2,3,R8,8.0); + VARPOW(R8,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(R8,2,I8,3,R8,8.0); + VARPOW(R8,2,UI8,3,R8,8.0); + } + VARPOW(R8,2,INT,3,R8,8.0); + VARPOW(R8,2,UINT,3,R8,8.0); + VARPOW(DATE,2,EMPTY,0,R8,1.0); + VARPOW(DATE,2,NULL,0,NULL,0); + VARPOW(DATE,2,I2,3,R8,8.0); + VARPOW(DATE,2,I4,3,R8,8.0); + VARPOW(DATE,2,R4,3.0f,R8,8.0); + VARPOW(DATE,2,R8,3.0,R8,8.0); + VARPOW(DATE,2,DATE,3,R8,8.0); + VARPOW(DATE,2,BSTR,num3_str,R8,8.0); + VARPOW(DATE,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(DATE,2,I1,3,R8,8.0); + VARPOW(DATE,2,UI1,3,R8,8.0); + VARPOW(DATE,2,UI2,3,R8,8.0); + VARPOW(DATE,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(DATE,2,I8,3,R8,8.0); + VARPOW(DATE,2,UI8,3,R8,8.0); + } + VARPOW(DATE,2,INT,3,R8,8.0); + VARPOW(DATE,2,UINT,3,R8,8.0); + VARPOW(BSTR,num2_str,EMPTY,0,R8,1.0); + VARPOW(BSTR,num2_str,NULL,0,NULL,0); + VARPOW(BSTR,num2_str,I2,3,R8,8.0); + VARPOW(BSTR,num2_str,I4,3,R8,8.0); + VARPOW(BSTR,num2_str,R4,3.0f,R8,8.0); + VARPOW(BSTR,num2_str,R8,3.0,R8,8.0); + VARPOW(BSTR,num2_str,DATE,3,R8,8.0); + VARPOW(BSTR,num2_str,BSTR,num3_str,R8,8.0); + VARPOW(BSTR,num2_str,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(BSTR,num2_str,I1,3,R8,8.0); + VARPOW(BSTR,num2_str,UI1,3,R8,8.0); + VARPOW(BSTR,num2_str,UI2,3,R8,8.0); + VARPOW(BSTR,num2_str,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(BSTR,num2_str,I8,3,R8,8.0); + VARPOW(BSTR,num2_str,UI8,3,R8,8.0); + } + VARPOW(BSTR,num2_str,INT,3,R8,8.0); + VARPOW(BSTR,num2_str,UINT,3,R8,8.0); + VARPOW(BOOL,VARIANT_TRUE,EMPTY,0,R8,1.0); + VARPOW(BOOL,VARIANT_TRUE,NULL,0,NULL,0); + VARPOW(BOOL,VARIANT_TRUE,I2,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,I4,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,R4,3.0f,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,R8,3.0,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,DATE,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,BSTR,num3_str,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,I1,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,UI1,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,UI2,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,UI4,3,R8,-1.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(BOOL,VARIANT_TRUE,I8,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,UI8,3,R8,-1.0); + } + VARPOW(BOOL,VARIANT_TRUE,INT,3,R8,-1.0); + VARPOW(BOOL,VARIANT_TRUE,UINT,3,R8,-1.0); + VARPOW(I1,2,EMPTY,0,R8,1.0); + VARPOW(I1,2,NULL,0,NULL,0); + VARPOW(I1,2,I2,3,R8,8.0); + VARPOW(I1,2,I4,3,R8,8.0); + VARPOW(I1,2,R4,3.0f,R8,8.0); + VARPOW(I1,2,R8,3.0,R8,8.0); + VARPOW(I1,2,DATE,3,R8,8.0); + VARPOW(I1,2,BSTR,num3_str,R8,8.0); + VARPOW(I1,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(I1,2,I1,3,R8,8.0); + VARPOW(I1,2,UI1,3,R8,8.0); + VARPOW(I1,2,UI2,3,R8,8.0); + VARPOW(I1,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(I1,2,I8,3,R8,8.0); + VARPOW(I1,2,UI8,3,R8,8.0); + } + VARPOW(I1,2,INT,3,R8,8.0); + VARPOW(I1,2,UINT,3,R8,8.0); + VARPOW(UI1,2,EMPTY,0,R8,1.0); + VARPOW(UI1,2,NULL,0,NULL,0); + VARPOW(UI1,2,I2,3,R8,8.0); + VARPOW(UI1,2,I4,3,R8,8.0); + VARPOW(UI1,2,R4,3.0f,R8,8.0); + VARPOW(UI1,2,R8,3.0,R8,8.0); + VARPOW(UI1,2,DATE,3,R8,8.0); + VARPOW(UI1,2,BSTR,num3_str,R8,8.0); + VARPOW(UI1,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(UI1,2,I1,3,R8,8.0); + VARPOW(UI1,2,UI1,3,R8,8.0); + VARPOW(UI1,2,UI2,3,R8,8.0); + VARPOW(UI1,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(UI1,2,I8,3,R8,8.0); + VARPOW(UI1,2,UI8,3,R8,8.0); + } + VARPOW(UI1,2,INT,3,R8,8.0); + VARPOW(UI1,2,UINT,3,R8,8.0); + VARPOW(UI2,2,EMPTY,0,R8,1.0); + VARPOW(UI2,2,NULL,0,NULL,0); + VARPOW(UI2,2,I2,3,R8,8.0); + VARPOW(UI2,2,I4,3,R8,8.0); + VARPOW(UI2,2,R4,3.0f,R8,8.0); + VARPOW(UI2,2,R8,3.0,R8,8.0); + VARPOW(UI2,2,DATE,3,R8,8.0); + VARPOW(UI2,2,BSTR,num3_str,R8,8.0); + VARPOW(UI2,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(UI2,2,I1,3,R8,8.0); + VARPOW(UI2,2,UI1,3,R8,8.0); + VARPOW(UI2,2,UI2,3,R8,8.0); + VARPOW(UI2,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(UI2,2,I8,3,R8,8.0); + VARPOW(UI2,2,UI8,3,R8,8.0); + } + VARPOW(UI2,2,INT,3,R8,8.0); + VARPOW(UI2,2,UINT,3,R8,8.0); + VARPOW(UI4,2,EMPTY,0,R8,1.0); + VARPOW(UI4,2,NULL,0,NULL,0); + VARPOW(UI4,2,I2,3,R8,8.0); + VARPOW(UI4,2,I4,3,R8,8.0); + VARPOW(UI4,2,R4,3.0f,R8,8.0); + VARPOW(UI4,2,R8,3.0,R8,8.0); + VARPOW(UI4,2,DATE,3,R8,8.0); + VARPOW(UI4,2,BSTR,num3_str,R8,8.0); + VARPOW(UI4,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(UI4,2,I1,3,R8,8.0); + VARPOW(UI4,2,UI1,3,R8,8.0); + VARPOW(UI4,2,UI2,3,R8,8.0); + VARPOW(UI4,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(UI4,2,I8,3,R8,8.0); + VARPOW(UI4,2,UI8,3,R8,8.0); + } + VARPOW(UI4,2,INT,3,R8,8.0); + VARPOW(UI4,2,UINT,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(I8,2,EMPTY,0,R8,1.0); + VARPOW(I8,2,NULL,0,NULL,0); + VARPOW(I8,2,I2,3,R8,8.0); + VARPOW(I8,2,I4,3,R8,8.0); + VARPOW(I8,2,R4,3.0f,R8,8.0); + VARPOW(I8,2,R8,3.0,R8,8.0); + VARPOW(I8,2,DATE,3,R8,8.0); + VARPOW(I8,2,BSTR,num3_str,R8,8.0); + VARPOW(I8,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(I8,2,I1,3,R8,8.0); + VARPOW(I8,2,UI1,3,R8,8.0); + VARPOW(I8,2,UI2,3,R8,8.0); + VARPOW(I8,2,UI4,3,R8,8.0); + VARPOW(I8,2,I8,3,R8,8.0); + VARPOW(I8,2,UI8,3,R8,8.0); + VARPOW(I8,2,INT,3,R8,8.0); + VARPOW(I8,2,UINT,3,R8,8.0); + VARPOW(UI8,2,EMPTY,0,R8,1.0); + VARPOW(UI8,2,NULL,0,NULL,0); + VARPOW(UI8,2,I2,3,R8,8.0); + VARPOW(UI8,2,I4,3,R8,8.0); + VARPOW(UI8,2,R4,3.0f,R8,8.0); + VARPOW(UI8,2,R8,3.0,R8,8.0); + VARPOW(UI8,2,DATE,3,R8,8.0); + VARPOW(UI8,2,BSTR,num3_str,R8,8.0); + VARPOW(UI8,2,I1,3,R8,8.0); + VARPOW(UI8,2,UI1,3,R8,8.0); + VARPOW(UI8,2,UI2,3,R8,8.0); + VARPOW(UI8,2,UI4,3,R8,8.0); + VARPOW(UI8,2,I8,3,R8,8.0); + VARPOW(UI8,2,UI8,3,R8,8.0); + VARPOW(UI8,2,INT,3,R8,8.0); + VARPOW(UI8,2,UINT,3,R8,8.0); + } + VARPOW(INT,2,EMPTY,0,R8,1.0); + VARPOW(INT,2,NULL,0,NULL,0); + VARPOW(INT,2,I2,3,R8,8.0); + VARPOW(INT,2,I4,3,R8,8.0); + VARPOW(INT,2,R4,3.0f,R8,8.0); + VARPOW(INT,2,R8,3.0,R8,8.0); + VARPOW(INT,2,DATE,3,R8,8.0); + VARPOW(INT,2,BSTR,num3_str,R8,8.0); + VARPOW(INT,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(INT,2,I1,3,R8,8.0); + VARPOW(INT,2,UI1,3,R8,8.0); + VARPOW(INT,2,UI2,3,R8,8.0); + VARPOW(INT,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(INT,2,I8,3,R8,8.0); + VARPOW(INT,2,UI8,3,R8,8.0); + } + VARPOW(INT,2,INT,3,R8,8.0); + VARPOW(INT,2,UINT,3,R8,8.0); + VARPOW(UINT,2,EMPTY,0,R8,1.0); + VARPOW(UINT,2,NULL,0,NULL,0); + VARPOW(UINT,2,I2,3,R8,8.0); + VARPOW(UINT,2,I4,3,R8,8.0); + VARPOW(UINT,2,R4,3.0f,R8,8.0); + VARPOW(UINT,2,R8,3.0,R8,8.0); + VARPOW(UINT,2,DATE,3,R8,8.0); + VARPOW(UINT,2,BSTR,num3_str,R8,8.0); + VARPOW(UINT,2,BOOL,VARIANT_FALSE,R8,1.0); + VARPOW(UINT,2,I1,3,R8,8.0); + VARPOW(UINT,2,UI1,3,R8,8.0); + VARPOW(UINT,2,UI2,3,R8,8.0); + VARPOW(UINT,2,UI4,3,R8,8.0); + if (HAVE_OLEAUT32_I8) + { + VARPOW(UINT,2,I8,3,R8,8.0); + VARPOW(UINT,2,UI8,3,R8,8.0); + } + VARPOW(UINT,2,INT,3,R8,8.0); + VARPOW(UINT,2,UINT,3,R8,8.0); + + /* Manually test some VT_CY, VT_DECIMAL variants */ + V_VT(&cy) = VT_CY; + hres = VarCyFromI4(2, &V_CY(&cy)); + ok(hres == S_OK, "VarCyFromI4 failed!\n"); + V_VT(&dec) = VT_DECIMAL; + hres = VarDecFromR8(2.0, &V_DECIMAL(&dec)); + ok(hres == S_OK, "VarDecFromR4 failed!\n"); + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = VT_I4; + V_I4(&left) = 100; + V_VT(&right) = VT_I8; + V_UI1(&right) = 2; + + hres = pVarPow(&cy, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARPOW: expected coerced hres 0x%X type VT_R8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 4.0), + "VARPOW: CY value %f, expected %f\n", V_R8(&result), 4.0); + + hres = pVarPow(&cy, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARPOW: expected coerced hres 0x%X type VT_R8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 4.0), + "VARPOW: CY value %f, expected %f\n", V_R8(&result), 4.0); + + hres = pVarPow(&left, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARPOW: expected coerced hres 0x%X type VT_R8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 10000.0), + "VARPOW: CY value %f, expected %f\n", V_R8(&result), 10000.0); + + hres = pVarPow(&left, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARPOW: expected coerced hres 0x%X type VT_R8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result),10000.0), + "VARPOW: DECIMAL value %f, expected %f\n", V_R8(&result), 10000.0); + + hres = pVarPow(&dec, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARPOW: expected coerced hres 0x%X type VT_R8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 4.0), + "VARPOW: DECIMAL value %f, expected %f\n", V_R8(&result), 4.0); + + hres = pVarPow(&dec, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARPOW: expected coerced hres 0x%X type VT_R8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 4.0), + "VARPOW: DECIMAL value %f, expected %f\n", V_R8(&result), 4.0); + + SysFreeString(num2_str); + SysFreeString(num3_str); +} + +static HRESULT (WINAPI *pVarDiv)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARDIV(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarDiv, &left, &right, &exp ) + +/* Skip any type that is not defined or produces an error for every case */ +#define SKIPTESTDIV(a) \ + if (a == VT_ERROR || a == VT_VARIANT || \ + a == VT_DISPATCH || a == VT_UNKNOWN || \ + a == VT_RECORD || a > VT_UINT || \ + a == VT_I1 || a == VT_UI8 || \ + a == VT_INT || a == VT_UINT || \ + a == VT_UI2 || a == VT_UI4 || \ + a == 15 /*not defined*/) \ + continue + +static void test_VarDiv(void) +{ + static const WCHAR str1[] = { '1','\0' }; + static const WCHAR str2[] = { '2','\0' }; + VARIANT left, right, exp, result, cy, dec; + BSTR num1_str, num2_str; + VARTYPE i; + HRESULT hres, expectedhres; + double r; + + num1_str = SysAllocString(str1); + num2_str = SysAllocString(str2); + + CHECKPTR(VarDiv); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + SKIPTESTDIV(leftvt); + + /* Check if we need/have support for I8 */ + if (leftvt == VT_I8 && !HAVE_OLEAUT32_I8) + continue; + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + SKIPTESTDIV(rightvt); + + /* Check if we need/have support for I8 */ + if (rightvt == VT_I8 && !HAVE_OLEAUT32_I8) + continue; + + /* Native crashes with VT_BYREF */ + if (ExtraFlags[i] == VT_BYREF) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_EMPTY; + expectedhres = S_OK; + + if (leftvt == VT_BSTR) + V_BSTR(&left) = num2_str; + else if (leftvt == VT_DECIMAL) + { + VarDecFromR8(2.0, &V_DECIMAL(&left)); + V_VT(&left) = leftvt | ExtraFlags[i]; + } + + /* Division by 0 is undefined */ + switch(rightvt) + { + case VT_BSTR: + V_BSTR(&right) = num2_str; + break; + case VT_DECIMAL: + VarDecFromR8(2.0, &V_DECIMAL(&right)); + V_VT(&right) = rightvt | ExtraFlags[i]; + break; + case VT_BOOL: + V_BOOL(&right) = VARIANT_TRUE; + break; + case VT_I2: V_I2(&right) = 2; break; + case VT_I4: V_I4(&right) = 2; break; + case VT_R4: V_R4(&right) = 2.0f; break; + case VT_R8: V_R8(&right) = 2.0; break; + case VT_CY: V_CY(&right).int64 = 2; break; + case VT_DATE: V_DATE(&right) = 2; break; + case VT_UI1: V_UI1(&right) = 2; break; + case VT_I8: V_I8(&right) = 2; break; + default: break; + } + + /* Determine return type */ + if (!(rightvt == VT_EMPTY)) + { + if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if (leftvt == VT_DECIMAL || rightvt == VT_DECIMAL) + resvt = VT_DECIMAL; + else if (leftvt == VT_I8 || rightvt == VT_I8 || + leftvt == VT_CY || rightvt == VT_CY || + leftvt == VT_DATE || rightvt == VT_DATE || + leftvt == VT_I4 || rightvt == VT_I4 || + leftvt == VT_BSTR || rightvt == VT_BSTR || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL || + leftvt == VT_R8 || rightvt == VT_R8 || + leftvt == VT_UI1 || rightvt == VT_UI1) + { + if ((leftvt == VT_UI1 && rightvt == VT_R4) || + (leftvt == VT_R4 && rightvt == VT_UI1)) + resvt = VT_R4; + else if ((leftvt == VT_R4 && (rightvt == VT_BOOL || + rightvt == VT_I2)) || (rightvt == VT_R4 && + (leftvt == VT_BOOL || leftvt == VT_I2))) + resvt = VT_R4; + else + resvt = VT_R8; + } + else if (leftvt == VT_R4 || rightvt == VT_R4) + resvt = VT_R4; + } + else if (leftvt == VT_NULL && rightvt == VT_EMPTY) + resvt = VT_NULL; + else + bFail = TRUE; + + /* Native VarDiv always returns an error when using extra flags */ + if (ExtraFlags[i] != 0) + bFail = TRUE; + + hres = pVarDiv(&left, &right, &result); + + /* Check expected HRESULT and if result variant type is correct */ + if (bFail) + ok (hres == DISP_E_BADVARTYPE || hres == DISP_E_TYPEMISMATCH || + hres == DISP_E_OVERFLOW || hres == DISP_E_DIVBYZERO, + "VarDiv: %s|0x%X, %s|0x%X: got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], + vtstr(V_VT(&result)), hres); + else + ok (hres == S_OK && resvt == V_VT(&result), + "VarDiv: %s|0x%X, %s|0x%X: expected vt %s hr 0x%X, got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], vtstr(resvt), + S_OK, vtstr(V_VT(&result)), hres); + } + } + } + + /* Test return values for all the good cases */ + VARDIV(EMPTY,0,NULL,0,NULL,0); + VARDIV(EMPTY,0,I2,2,R8,0.0); + VARDIV(EMPTY,0,I4,2,R8,0.0); + VARDIV(EMPTY,0,R4,2.0f,R4,0.0f); + VARDIV(EMPTY,0,R8,2.0,R8,0.0); + VARDIV(EMPTY,0,DATE,2.0,R8,0.0); + VARDIV(EMPTY,0,BSTR,num2_str,R8,0.0); + VARDIV(EMPTY,0,BOOL,VARIANT_TRUE,R8,0.0); + VARDIV(EMPTY,0,UI1,2,R8,0.0); + if (HAVE_OLEAUT32_I8) + { + VARDIV(EMPTY,0,I8,2,R8,0.0); + } + VARDIV(NULL,0,EMPTY,0,NULL,0); + VARDIV(NULL,0,NULL,0,NULL,0); + VARDIV(NULL,0,I2,2,NULL,0); + VARDIV(NULL,0,I4,2,NULL,0); + VARDIV(NULL,0,R4,2.0f,NULL,0); + VARDIV(NULL,0,R8,2.0,NULL,0); + VARDIV(NULL,0,DATE,2,NULL,0); + VARDIV(NULL,0,BSTR,num2_str,NULL,0); + VARDIV(NULL,0,BOOL,VARIANT_TRUE,NULL,0); + VARDIV(NULL,0,UI1,2,NULL,0); + if (HAVE_OLEAUT32_I8) + { + VARDIV(NULL,0,I8,2,NULL,0); + } + VARDIV(I2,2,NULL,0,NULL,0); + VARDIV(I2,1,I2,2,R8,0.5); + VARDIV(I2,1,I4,2,R8,0.5); + VARDIV(I2,1,R4,2,R4,0.5f); + VARDIV(I2,1,R8,2.0,R8,0.5); + VARDIV(I2,1,DATE,2,R8,0.5); + VARDIV(I2,1,BOOL,VARIANT_TRUE,R8,-1.0); + VARDIV(I2,1,UI1,2,R8,0.5); + if (HAVE_OLEAUT32_I8) + { + VARDIV(I2,1,I8,2,R8,0.5); + } + VARDIV(I4,1,NULL,0,NULL,0); + VARDIV(I4,1,I2,2,R8,0.5); + VARDIV(I4,1,I4,2,R8,0.5); + VARDIV(I4,1,R4,2.0f,R8,0.5); + VARDIV(I4,1,R8,2.0,R8,0.5); + VARDIV(I4,1,DATE,2,R8,0.5); + VARDIV(I4,1,BSTR,num2_str,R8,0.5); + VARDIV(I4,1,BOOL,VARIANT_TRUE,R8,-1.0); + VARDIV(I4,1,UI1,2,R8,0.5); + if (HAVE_OLEAUT32_I8) + { + VARDIV(I4,1,I8,2,R8,0.5); + } + VARDIV(R4,1.0f,NULL,0,NULL,0); + VARDIV(R4,1.0f,I2,2,R4,0.5f); + VARDIV(R4,1.0f,I4,2,R8,0.5); + VARDIV(R4,1.0f,R4,2.0f,R4,0.5f); + VARDIV(R4,1.0f,R8,2.0,R8,0.5); + VARDIV(R4,1.0f,DATE,2,R8,0.5); + VARDIV(R4,1.0f,BSTR,num2_str,R8,0.5); + VARDIV(R4,1.0f,BOOL,VARIANT_TRUE,R4,-1); + VARDIV(R4,1.0f,UI1,2,R4,0.5f); + if (HAVE_OLEAUT32_I8) + { + VARDIV(R4,1.0f,I8,2,R8,0.5); + } + VARDIV(R8,1.0,NULL,0,NULL,0); + VARDIV(R8,1.0,I2,2,R8,0.5); + VARDIV(R8,1.0,I4,2,R8,0.5); + VARDIV(R8,1.0,R4,2.0f,R8,0.5); + VARDIV(R8,1.0,R8,2.0,R8,0.5); + VARDIV(R8,1.0,DATE,2,R8,0.5); + VARDIV(R8,1.0,BSTR,num2_str,R8,0.5); + VARDIV(R8,1.0,BOOL,VARIANT_TRUE,R8,-1.0); + VARDIV(R8,1.0,UI1,2,R8,0.5); + if (HAVE_OLEAUT32_I8) + { + VARDIV(R8,1.0,I8,2,R8,0.5); + } + VARDIV(DATE,1,NULL,0,NULL,0); + VARDIV(DATE,1,I2,2,R8,0.5); + VARDIV(DATE,1,I4,2,R8,0.5); + VARDIV(DATE,1,R4,2.0f,R8,0.5); + VARDIV(DATE,1,R8,2.0,R8,0.5); + VARDIV(DATE,1,DATE,2,R8,0.5); + VARDIV(DATE,1,BSTR,num2_str,R8,0.5); + VARDIV(DATE,1,BOOL,VARIANT_TRUE,R8,-1.0); + VARDIV(DATE,1,UI1,2,R8,0.5); + if (HAVE_OLEAUT32_I8) + { + VARDIV(DATE,1,I8,2,R8,0.5); + } + VARDIV(BSTR,num1_str,NULL,0,NULL,0); + VARDIV(BSTR,num1_str,I2,2,R8,0.5); + VARDIV(BSTR,num1_str,I4,2,R8,0.5); + VARDIV(BSTR,num1_str,R4,2.0f,R8,0.5); + VARDIV(BSTR,num1_str,R8,2.0,R8,0.5); + VARDIV(BSTR,num1_str,DATE,2,R8,0.5); + VARDIV(BSTR,num1_str,BSTR,num2_str,R8,0.5); + VARDIV(BSTR,num1_str,BOOL,VARIANT_TRUE,R8,-1); + VARDIV(BSTR,num1_str,UI1,2,R8,0.5); + if (HAVE_OLEAUT32_I8) + { + VARDIV(BSTR,num1_str,I8,2,R8,0.5); + } + VARDIV(BOOL,VARIANT_TRUE,NULL,0,NULL,0); + VARDIV(BOOL,VARIANT_TRUE,I2,1,R8,-1.0); + VARDIV(BOOL,VARIANT_FALSE,I2,1,R8,0.0); + VARDIV(BOOL,VARIANT_TRUE,I4,1,R8,-1.0); + VARDIV(BOOL,VARIANT_FALSE,I4,1,R8,0.0); + VARDIV(BOOL,VARIANT_TRUE,R4,1,R4,-1.0f); + VARDIV(BOOL,VARIANT_FALSE,R4,1,R4,0.0f); + VARDIV(BOOL,VARIANT_TRUE,R8,1.0,R8,-1.0); + VARDIV(BOOL,VARIANT_FALSE,R8,1.0,R8,0.0); + VARDIV(BOOL,VARIANT_FALSE,DATE,2,R8,0.0); + VARDIV(BOOL,VARIANT_FALSE,BSTR,num2_str,R8,0.0); + VARDIV(BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE,R8,1.0); + VARDIV(BOOL,VARIANT_FALSE,BOOL,VARIANT_TRUE,R8,0.0); + VARDIV(BOOL,VARIANT_TRUE,UI1,1,R8,-1.0); + if (HAVE_OLEAUT32_I8) + { + VARDIV(BOOL,VARIANT_TRUE,I8,1,R8,-1.0); + } + VARDIV(UI1,1,NULL,0,NULL,0); + VARDIV(UI1,1,I2,2,R8,0.5); + VARDIV(UI1,1,I4,2,R8,0.5); + VARDIV(UI1,1,R4,2.0f,R4,0.5f); + VARDIV(UI1,1,R8,2.0,R8,0.5); + VARDIV(UI1,1,DATE,2,R8,0.5); + VARDIV(UI1,1,BSTR,num2_str,R8,0.5); + VARDIV(UI1,1,BOOL,VARIANT_TRUE,R8,-1); + VARDIV(UI1,1,UI1,2,R8,0.5); + if (HAVE_OLEAUT32_I8) + { + VARDIV(UI1,1,I8,2,R8,0.5); + VARDIV(I8,1,NULL,0,NULL,0); + VARDIV(I8,1,I2,2,R8,0.5); + VARDIV(I8,1,I4,2,R8,0.5); + VARDIV(I8,1,R4,2.0f,R8,0.5); + VARDIV(I8,1,R8,2.0,R8,0.5); + VARDIV(I8,1,DATE,2,R8,0.5); + VARDIV(I8,1,BSTR,num2_str,R8,0.5); + VARDIV(I8,1,BOOL,VARIANT_TRUE,R8,-1); + VARDIV(I8,1,UI1,2,R8,0.5); + VARDIV(I8,1,I8,2,R8,0.5); + } + + /* Manually test some VT_CY, VT_DECIMAL variants */ + V_VT(&cy) = VT_CY; + hres = VarCyFromI4(10000, &V_CY(&cy)); + ok(hres == S_OK, "VarCyFromI4 failed!\n"); + V_VT(&dec) = VT_DECIMAL; + hres = VarDecFromR8(2.0, &V_DECIMAL(&dec)); + ok(hres == S_OK, "VarDecFromR4 failed!\n"); + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = VT_I4; + V_I4(&left) = 100; + V_VT(&right) = VT_UI1; + V_UI1(&right) = 2; + + hres = pVarDiv(&cy, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARDIV: expected coerced type VT_R8, got %s!\n", vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 1.0), + "VARDIV: CY value %f, expected %f\n", V_R8(&result), (double)1.0); + + hres = pVarDiv(&cy, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARDIV: expected coerced type VT_R8, got %s!\n", vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 5000.0), + "VARDIV: CY value %f, expected %f\n", V_R8(&result), (double)5000.0); + + hres = pVarDiv(&left, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_R8, + "VARDIV: expected coerced type VT_R8, got %s!\n", vtstr(V_VT(&result))); + ok(hres == S_OK && EQ_DOUBLE(V_R8(&result), 0.01), + "VARDIV: CY value %f, expected %f\n", V_R8(&result), (double)0.01); + + hres = pVarDiv(&left, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_DECIMAL, + "VARDIV: expected coerced type VT_DECIMAL, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromDec(&V_DECIMAL(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, 50.0), + "VARDIV: DECIMAL value %f, expected %f\n", r, (double)50.0); + + hres = pVarDiv(&dec, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_DECIMAL, + "VARDIV: expected coerced type VT_DECIMAL, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromDec(&V_DECIMAL(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, 1.0), + "VARDIV: DECIMAL value %f, expected %f\n", r, (double)1.0); + + hres = pVarDiv(&dec, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_DECIMAL, + "VARDIV: expected coerced type VT_DECIMAL, got %s!\n", vtstr(V_VT(&result))); + hres = VarR8FromDec(&V_DECIMAL(&result), &r); + ok(hres == S_OK && EQ_DOUBLE(r, 1.0), + "VARDIV: DECIMAL value %f, expected %f\n", r, (double)1.0); + + /* Check for division by zero and overflow */ + V_VT(&left) = VT_R8; + V_I4(&left) = 1; + V_VT(&right) = VT_R8; + V_I4(&right) = 0; + hres = pVarDiv(&left, &right, &result); + ok(hres == DISP_E_DIVBYZERO && V_VT(&result) == VT_EMPTY, + "VARDIV: Division by (1.0/0.0) should result in DISP_E_DIVBYZERO but got 0x%X\n", hres); + + V_VT(&left) = VT_R8; + V_I4(&left) = 0; + V_VT(&right) = VT_R8; + V_I4(&right) = 0; + hres = pVarDiv(&left, &right, &result); + ok(hres == DISP_E_OVERFLOW && V_VT(&result) == VT_EMPTY, + "VARDIV: Division by (0.0/0.0) should result in DISP_E_OVERFLOW but got 0x%X\n", hres); + + SysFreeString(num1_str); + SysFreeString(num2_str); +} + +static HRESULT (WINAPI *pVarIdiv)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARIDIV(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarIdiv, &left, &right, &exp ) + +/* Skip any type that is not defined or produces an error for every case */ +#define SKIPTESTIDIV(a) \ + if (a == VT_ERROR || a == VT_VARIANT || \ + a == VT_DISPATCH || a == VT_UNKNOWN || \ + a == VT_RECORD || a > VT_UINT || \ + a == 15 /*not defined*/) \ + continue + +static void test_VarIdiv(void) +{ + static const WCHAR str1[] = { '1','\0' }; + static const WCHAR str2[] = { '2','\0' }; + VARIANT left, right, exp, result, cy, dec; + BSTR num1_str, num2_str; + VARTYPE i; + HRESULT hres; + + CHECKPTR(VarIdiv); + + num1_str = SysAllocString(str1); + num2_str = SysAllocString(str2); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + SKIPTESTIDIV(leftvt); + + /* Check if we need/have support for I8 and/or UI8 */ + if ((leftvt == VT_I8 || leftvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + SKIPTESTIDIV(rightvt); + + /* Native crashes with extra flag VT_BYREF */ + if (ExtraFlags[i] == VT_BYREF) + continue; + + /* Check if we need/have support for I8 and/or UI8 */ + if ((rightvt == VT_I8 || rightvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_EMPTY; + + if (leftvt == VT_BSTR) + V_BSTR(&left) = num2_str; + else if (leftvt == VT_DECIMAL) + { + VarDecFromR8(2.0, &V_DECIMAL(&left)); + V_VT(&left) = leftvt | ExtraFlags[i]; + } + + /* Division by 0 is undefined */ + switch(rightvt) + { + case VT_BSTR: + V_BSTR(&right) = num2_str; + break; + case VT_DECIMAL: + VarDecFromR8(2.0, &V_DECIMAL(&right)); + V_VT(&right) = rightvt | ExtraFlags[i]; + break; + case VT_BOOL: + V_BOOL(&right) = VARIANT_TRUE; + break; + case VT_CY: + VarCyFromI4(10000, &V_CY(&right)); + V_VT(&right) = rightvt | ExtraFlags[i]; + break; + case VT_I2: V_I2(&right) = 2; break; + case VT_I4: V_I4(&right) = 2; break; + case VT_R4: V_R4(&right) = 2.0f; break; + case VT_R8: V_R8(&right) = 2.0; break; + case VT_DATE: V_DATE(&right) = 2; break; + case VT_I1: V_I1(&right) = 2; break; + case VT_UI1: V_UI1(&right) = 2; break; + case VT_UI2: V_UI2(&right) = 2; break; + case VT_UI4: V_UI4(&right) = 2; break; + case VT_I8: V_I8(&right) = 2; break; + case VT_UI8: V_UI8(&right) = 2; break; + case VT_INT: V_INT(&right) = 2; break; + case VT_UINT: V_UINT(&right) = 2; break; + default: break; + } + + /* Native VarIdiv always returns an error when using extra + * flags or if the variant combination is I8 and INT. + */ + if ((leftvt == VT_I8 && rightvt == VT_INT) || + (leftvt == VT_INT && rightvt == VT_I8) || + (rightvt == VT_EMPTY && leftvt != VT_NULL) || + ExtraFlags[i] != 0) + bFail = TRUE; + + /* Determine variant type */ + else if (leftvt == VT_NULL || rightvt == VT_NULL) + resvt = VT_NULL; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + else if (leftvt == VT_I4 || rightvt == VT_I4 || + leftvt == VT_INT || rightvt == VT_INT || + leftvt == VT_UINT || rightvt == VT_UINT || + leftvt == VT_UI8 || rightvt == VT_UI8 || + leftvt == VT_UI4 || rightvt == VT_UI4 || + leftvt == VT_UI2 || rightvt == VT_UI2 || + leftvt == VT_I1 || rightvt == VT_I1 || + leftvt == VT_BSTR || rightvt == VT_BSTR || + leftvt == VT_DATE || rightvt == VT_DATE || + leftvt == VT_CY || rightvt == VT_CY || + leftvt == VT_DECIMAL || rightvt == VT_DECIMAL || + leftvt == VT_R8 || rightvt == VT_R8 || + leftvt == VT_R4 || rightvt == VT_R4) + resvt = VT_I4; + else if (leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_BOOL || rightvt == VT_BOOL || + leftvt == VT_EMPTY) + resvt = VT_I2; + else if (leftvt == VT_UI1 || rightvt == VT_UI1) + resvt = VT_UI1; + else + bFail = TRUE; + + hres = pVarIdiv(&left, &right, &result); + + /* Check expected HRESULT and if result variant type is correct */ + if (bFail) + ok (hres == DISP_E_BADVARTYPE || hres == DISP_E_TYPEMISMATCH || + hres == DISP_E_DIVBYZERO, + "VarIdiv: %s|0x%X, %s|0x%X: got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], + vtstr(V_VT(&result)), hres); + else + ok (hres == S_OK && resvt == V_VT(&result), + "VarIdiv: %s|0x%X, %s|0x%X: expected vt %s hr 0x%X, got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], vtstr(resvt), + S_OK, vtstr(V_VT(&result)), hres); + } + } + } + + /* Test return values for all the good cases */ + VARIDIV(EMPTY,0,NULL,0,NULL,0); + VARIDIV(EMPTY,0,I2,1,I2,0); + VARIDIV(EMPTY,0,I4,1,I4,0); + VARIDIV(EMPTY,0,R4,1.0f,I4,0); + VARIDIV(EMPTY,0,R8,1.0,I4,0); + VARIDIV(EMPTY,0,DATE,1.0,I4,0); + VARIDIV(EMPTY,0,BSTR,num1_str,I4,0); + VARIDIV(EMPTY,0,BOOL,VARIANT_TRUE,I2,0); + VARIDIV(EMPTY,0,I1,1,I4,0); + VARIDIV(EMPTY,0,UI1,1,I2,0); + VARIDIV(EMPTY,0,UI2,1,I4,0); + VARIDIV(EMPTY,0,UI4,1,I4,0); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(EMPTY,0,I8,1,I8,0); + VARIDIV(EMPTY,0,UI8,1,I4,0); + } + VARIDIV(EMPTY,0,INT,1,I4,0); + VARIDIV(EMPTY,0,UINT,1,I4,0); + VARIDIV(NULL,0,EMPTY,0,NULL,0); + VARIDIV(NULL,0,NULL,0,NULL,0); + VARIDIV(NULL,0,I2,1,NULL,0); + VARIDIV(NULL,0,I4,1,NULL,0); + VARIDIV(NULL,0,R4,1,NULL,0); + VARIDIV(NULL,0,R8,1,NULL,0); + VARIDIV(NULL,0,DATE,1,NULL,0); + VARIDIV(NULL,0,BSTR,num1_str,NULL,0); + VARIDIV(NULL,0,BOOL,VARIANT_TRUE,NULL,0); + VARIDIV(NULL,0,I1,1,NULL,0); + VARIDIV(NULL,0,UI1,1,NULL,0); + VARIDIV(NULL,0,UI2,1,NULL,0); + VARIDIV(NULL,0,UI4,1,NULL,0); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(NULL,0,I8,1,NULL,0); + VARIDIV(NULL,0,UI8,1,NULL,0); + } + VARIDIV(NULL,0,INT,1,NULL,0); + VARIDIV(NULL,0,UINT,1,NULL,0); + VARIDIV(I2,2,NULL,0,NULL,0); + VARIDIV(I2,2,I2,1,I2,2); + VARIDIV(I2,2,I4,1,I4,2); + VARIDIV(I2,2,R4,1,I4,2); + VARIDIV(I2,2,R8,1,I4,2); + VARIDIV(I2,2,DATE,1,I4,2); + VARIDIV(I2,2,BSTR,num1_str,I4,2); + VARIDIV(I2,2,BOOL,VARIANT_TRUE,I2,-2); + VARIDIV(I2,2,I1,1,I4,2); + VARIDIV(I2,2,UI1,1,I2,2); + VARIDIV(I2,2,UI2,1,I4,2); + VARIDIV(I2,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(I2,2,I8,1,I8,2); + VARIDIV(I2,2,UI8,1,I4,2); + } + VARIDIV(I2,2,INT,1,I4,2); + VARIDIV(I2,2,UINT,1,I4,2); + VARIDIV(I4,2,NULL,0,NULL,0); + VARIDIV(I4,2,I2,1,I4,2); + VARIDIV(I4,2,I4,1,I4,2); + VARIDIV(I4,2,R4,1,I4,2); + VARIDIV(I4,2,R8,1,I4,2); + VARIDIV(I4,2,DATE,1,I4,2); + VARIDIV(I4,2,BSTR,num1_str,I4,2); + VARIDIV(I4,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(I4,2,I1,1,I4,2); + VARIDIV(I4,2,UI1,1,I4,2); + VARIDIV(I4,2,UI2,1,I4,2); + VARIDIV(I4,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(I4,2,I8,1,I8,2); + VARIDIV(I4,2,UI8,1,I4,2); + } + VARIDIV(I4,2,INT,1,I4,2); + VARIDIV(I4,2,UINT,1,I4,2); + VARIDIV(R4,2.0f,NULL,0,NULL,0); + VARIDIV(R4,2.0f,I2,1,I4,2); + VARIDIV(R4,2.0f,I4,1,I4,2); + VARIDIV(R4,2.0f,R4,1.0f,I4,2); + VARIDIV(R4,2.0f,R8,1.0,I4,2); + VARIDIV(R4,2.0f,DATE,1,I4,2); + VARIDIV(R4,2.0f,BSTR,num1_str,I4,2); + VARIDIV(R4,2.0f,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(R4,2.0f,I1,1,I4,2); + VARIDIV(R4,2.0f,UI1,1,I4,2); + VARIDIV(R4,2.0f,UI2,1,I4,2); + VARIDIV(R4,2.0f,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(R4,2.0f,I8,1,I8,2); + VARIDIV(R4,2.0f,UI8,1,I4,2); + } + VARIDIV(R4,2.0f,INT,1,I4,2); + VARIDIV(R4,2.0f,UINT,1,I4,2); + VARIDIV(R8,2.0,NULL,0,NULL,0); + VARIDIV(R8,2.0,I2,1,I4,2); + VARIDIV(R8,2.0,I4,1,I4,2); + VARIDIV(R8,2.0,R4,1,I4,2); + VARIDIV(R8,2.0,R8,1,I4,2); + VARIDIV(R8,2.0,DATE,1,I4,2); + VARIDIV(R8,2.0,BSTR,num1_str,I4,2); + VARIDIV(R8,2.0,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(R8,2.0,I1,1,I4,2); + VARIDIV(R8,2.0,UI1,1,I4,2); + VARIDIV(R8,2.0,UI2,1,I4,2); + VARIDIV(R8,2.0,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(R8,2.0,I8,1,I8,2); + VARIDIV(R8,2.0,UI8,1,I4,2); + } + VARIDIV(R8,2.0,INT,1,I4,2); + VARIDIV(R8,2.0,UINT,1,I4,2); + VARIDIV(DATE,2,NULL,0,NULL,0); + VARIDIV(DATE,2,I2,1,I4,2); + VARIDIV(DATE,2,I4,1,I4,2); + VARIDIV(DATE,2,R4,1,I4,2); + VARIDIV(DATE,2,R8,1,I4,2); + VARIDIV(DATE,2,DATE,1,I4,2); + VARIDIV(DATE,2,BSTR,num1_str,I4,2); + VARIDIV(DATE,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(DATE,2,I1,1,I4,2); + VARIDIV(DATE,2,UI1,1,I4,2); + VARIDIV(DATE,2,UI2,1,I4,2); + VARIDIV(DATE,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(DATE,2,I8,1,I8,2); + VARIDIV(DATE,2,UI8,1,I4,2); + } + VARIDIV(DATE,2,INT,1,I4,2); + VARIDIV(DATE,2,UINT,1,I4,2); + VARIDIV(BSTR,num2_str,NULL,0,NULL,0); + VARIDIV(BSTR,num2_str,I2,1,I4,2); + VARIDIV(BSTR,num2_str,I4,1,I4,2); + VARIDIV(BSTR,num2_str,R4,1.0f,I4,2); + VARIDIV(BSTR,num2_str,R8,1.0,I4,2); + VARIDIV(BSTR,num2_str,DATE,1,I4,2); + VARIDIV(BSTR,num2_str,BSTR,num1_str,I4,2); + VARIDIV(BSTR,num2_str,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(BSTR,num2_str,I1,1,I4,2); + VARIDIV(BSTR,num2_str,UI1,1,I4,2); + VARIDIV(BSTR,num2_str,UI2,1,I4,2); + VARIDIV(BSTR,num2_str,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(BSTR,num2_str,I8,1,I8,2); + VARIDIV(BSTR,num2_str,UI8,1,I4,2); + } + VARIDIV(BSTR,num2_str,INT,1,I4,2); + VARIDIV(BSTR,num2_str,UINT,1,I4,2); + VARIDIV(BOOL,VARIANT_TRUE,NULL,0,NULL,0); + VARIDIV(BOOL,VARIANT_TRUE,I2,1,I2,-1); + VARIDIV(BOOL,VARIANT_TRUE,I4,1,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,R4,1.0f,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,R8,1.0,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,DATE,1,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,BSTR,num1_str,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,BOOL,VARIANT_TRUE,I2,1); + VARIDIV(BOOL,VARIANT_TRUE,I1,1,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,UI1,1,I2,-1); + VARIDIV(BOOL,VARIANT_TRUE,UI2,1,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(BOOL,VARIANT_TRUE,I8,1,I8,-1); + VARIDIV(BOOL,VARIANT_TRUE,UI8,1,I4,-1); + } + VARIDIV(BOOL,VARIANT_TRUE,INT,1,I4,-1); + VARIDIV(BOOL,VARIANT_TRUE,UINT,1,I4,-1); + VARIDIV(I1,2,NULL,0,NULL,0); + VARIDIV(I1,2,I2,1,I4,2); + VARIDIV(I1,2,I4,1,I4,2); + VARIDIV(I1,2,R4,1.0f,I4,2); + VARIDIV(I1,2,R8,1.0,I4,2); + VARIDIV(I1,2,DATE,1,I4,2); + VARIDIV(I1,2,BSTR,num1_str,I4,2); + VARIDIV(I1,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(I1,2,I1,1,I4,2); + VARIDIV(I1,2,UI1,1,I4,2); + VARIDIV(I1,2,UI2,1,I4,2); + VARIDIV(I1,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(I1,2,I8,1,I8,2); + VARIDIV(I1,2,UI8,1,I4,2); + } + VARIDIV(I1,2,INT,1,I4,2); + VARIDIV(I1,2,UINT,1,I4,2); + VARIDIV(UI1,2,NULL,0,NULL,0); + VARIDIV(UI1,2,I2,1,I2,2); + VARIDIV(UI1,2,I4,1,I4,2); + VARIDIV(UI1,2,R4,1.0f,I4,2); + VARIDIV(UI1,2,R8,1.0,I4,2); + VARIDIV(UI1,2,DATE,1,I4,2); + VARIDIV(UI1,2,BSTR,num1_str,I4,2); + VARIDIV(UI1,2,BOOL,VARIANT_TRUE,I2,-2); + VARIDIV(UI1,2,I1,1,I4,2); + VARIDIV(UI1,2,UI1,1,UI1,2); + VARIDIV(UI1,2,UI2,1,I4,2); + VARIDIV(UI1,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(UI1,2,I8,1,I8,2); + VARIDIV(UI1,2,UI8,1,I4,2); + } + VARIDIV(UI1,2,INT,1,I4,2); + VARIDIV(UI1,2,UINT,1,I4,2); + VARIDIV(UI2,2,NULL,0,NULL,0); + VARIDIV(UI2,2,I2,1,I4,2); + VARIDIV(UI2,2,I4,1,I4,2); + VARIDIV(UI2,2,R4,1.0f,I4,2); + VARIDIV(UI2,2,R8,1.0,I4,2); + VARIDIV(UI2,2,DATE,1,I4,2); + VARIDIV(UI2,2,BSTR,num1_str,I4,2); + VARIDIV(UI2,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(UI2,2,I1,1,I4,2); + VARIDIV(UI2,2,UI1,1,I4,2); + VARIDIV(UI2,2,UI2,1,I4,2); + VARIDIV(UI2,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(UI2,2,I8,1,I8,2); + VARIDIV(UI2,2,UI8,1,I4,2); + } + VARIDIV(UI2,2,INT,1,I4,2); + VARIDIV(UI2,2,UINT,1,I4,2); + VARIDIV(UI4,2,NULL,0,NULL,0); + VARIDIV(UI4,2,I2,1,I4,2); + VARIDIV(UI4,2,I4,1,I4,2); + VARIDIV(UI4,2,R4,1.0f,I4,2); + VARIDIV(UI4,2,R8,1.0,I4,2); + VARIDIV(UI4,2,DATE,1,I4,2); + VARIDIV(UI4,2,BSTR,num1_str,I4,2); + VARIDIV(UI4,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(UI4,2,I1,1,I4,2); + VARIDIV(UI4,2,UI1,1,I4,2); + VARIDIV(UI4,2,UI2,1,I4,2); + VARIDIV(UI4,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(UI4,2,I8,1,I8,2); + VARIDIV(UI4,2,UI8,1,I4,2); + } + VARIDIV(UI4,2,INT,1,I4,2); + VARIDIV(UI4,2,UINT,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(I8,2,NULL,0,NULL,0); + VARIDIV(I8,2,I2,1,I8,2); + VARIDIV(I8,2,I4,1,I8,2); + VARIDIV(I8,2,R4,1.0f,I8,2); + VARIDIV(I8,2,R8,1.0,I8,2); + VARIDIV(I8,2,DATE,1,I8,2); + VARIDIV(I8,2,BSTR,num1_str,I8,2); + VARIDIV(I8,2,BOOL,1,I8,2); + VARIDIV(I8,2,I1,1,I8,2); + VARIDIV(I8,2,UI1,1,I8,2); + VARIDIV(I8,2,UI2,1,I8,2); + VARIDIV(I8,2,UI4,1,I8,2); + VARIDIV(I8,2,I8,1,I8,2); + VARIDIV(I8,2,UI8,1,I8,2); + VARIDIV(I8,2,UINT,1,I8,2); + VARIDIV(UI8,2,NULL,0,NULL,0); + VARIDIV(UI8,2,I2,1,I4,2); + VARIDIV(UI8,2,I4,1,I4,2); + VARIDIV(UI8,2,R4,1.0f,I4,2); + VARIDIV(UI8,2,R8,1.0,I4,2); + VARIDIV(UI8,2,DATE,1,I4,2); + VARIDIV(UI8,2,BSTR,num1_str,I4,2); + VARIDIV(UI8,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(UI8,2,I1,1,I4,2); + VARIDIV(UI8,2,UI1,1,I4,2); + VARIDIV(UI8,2,UI2,1,I4,2); + VARIDIV(UI8,2,UI4,1,I4,2); + VARIDIV(UI8,2,I8,1,I8,2); + VARIDIV(UI8,2,UI8,1,I4,2); + VARIDIV(UI8,2,INT,1,I4,2); + VARIDIV(UI8,2,UINT,1,I4,2); + } + VARIDIV(INT,2,NULL,0,NULL,0); + VARIDIV(INT,2,I2,1,I4,2); + VARIDIV(INT,2,I4,1,I4,2); + VARIDIV(INT,2,R4,1.0f,I4,2); + VARIDIV(INT,2,R8,1.0,I4,2); + VARIDIV(INT,2,DATE,1,I4,2); + VARIDIV(INT,2,BSTR,num1_str,I4,2); + VARIDIV(INT,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(INT,2,I1,1,I4,2); + VARIDIV(INT,2,UI1,1,I4,2); + VARIDIV(INT,2,UI2,1,I4,2); + VARIDIV(INT,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(INT,2,UI8,1,I4,2); + } + VARIDIV(INT,2,INT,1,I4,2); + VARIDIV(INT,2,UINT,1,I4,2); + VARIDIV(UINT,2,NULL,0,NULL,0); + VARIDIV(UINT,2,I2,1,I4,2); + VARIDIV(UINT,2,I4,1,I4,2); + VARIDIV(UINT,2,R4,1.0f,I4,2); + VARIDIV(UINT,2,R8,1.0,I4,2); + VARIDIV(UINT,2,DATE,1,I4,2); + VARIDIV(UINT,2,BSTR,num1_str,I4,2); + VARIDIV(UINT,2,BOOL,VARIANT_TRUE,I4,-2); + VARIDIV(UINT,2,I1,1,I4,2); + VARIDIV(UINT,2,UI1,1,I4,2); + VARIDIV(UINT,2,UI2,1,I4,2); + VARIDIV(UINT,2,UI4,1,I4,2); + if (HAVE_OLEAUT32_I8) + { + VARIDIV(UINT,2,I8,1,I8,2); + VARIDIV(UINT,2,UI8,1,I4,2); + } + VARIDIV(UINT,2,INT,1,I4,2); + VARIDIV(UINT,2,UINT,1,I4,2); + + /* Manually test some VT_CY, VT_DECIMAL variants */ + V_VT(&cy) = VT_CY; + hres = VarCyFromI4(10000, &V_CY(&cy)); + ok(hres == S_OK, "VarCyFromI4 failed!\n"); + V_VT(&dec) = VT_DECIMAL; + hres = VarDecFromR8(2.0, &V_DECIMAL(&dec)); + ok(hres == S_OK, "VarDecFromR4 failed!\n"); + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = VT_I4; + V_I4(&left) = 100; + V_VT(&right) = VT_I8; + V_UI1(&right) = 2; + + hres = VarIdiv(&cy, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIDIV: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == 1, + "VARIDIV: CY value %d, expected %d\n", V_I4(&result), 1); + + if (HAVE_OLEAUT32_I8) + { + hres = VarIdiv(&cy, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_I8, + "VARIDIV: expected coerced hres 0x%X type VT_I8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I8(&result) == 5000, + "VARIDIV: CY value 0x%x%08x, expected 0x%x\n", + (DWORD)(V_I8(&result) >>32), (DWORD)V_I8(&result), 5000); + } + + hres = VarIdiv(&left, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIDIV: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == 0, + "VARIDIV: CY value %d, expected %d\n", V_I4(&result), 0); + + hres = VarIdiv(&left, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIDIV: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == 50, + "VARIDIV: DECIMAL value %d, expected %d\n", V_I4(&result), 50); + + hres = VarIdiv(&dec, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIDIV: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == 1, + "VARIDIV: DECIMAL value %d, expected %d\n", V_I4(&result), 1); + + if (HAVE_OLEAUT32_I8) + { + hres = VarIdiv(&dec, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_I8, + "VARIDIV: expected coerced hres 0x%X type VT_I8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I8(&result) == 1, + "VARIDIV: DECIMAL value 0x%x%08x, expected %d\n", + (DWORD)(V_I8(&result) >> 32), (DWORD)V_I8(&result), 1); + } + + /* Check for division by zero */ + V_VT(&left) = VT_INT; + V_I4(&left) = 1; + V_VT(&right) = VT_INT; + V_I4(&right) = 0; + hres = pVarIdiv(&left, &right, &result); + ok(hres == DISP_E_DIVBYZERO && V_VT(&result) == VT_EMPTY, + "VARIDIV: Division by 0 should result in DISP_E_DIVBYZERO but got 0x%X\n", hres); + + V_VT(&left) = VT_INT; + V_I4(&left) = 0; + V_VT(&right) = VT_INT; + V_I4(&right) = 0; + hres = pVarIdiv(&left, &right, &result); + ok(hres == DISP_E_DIVBYZERO && V_VT(&result) == VT_EMPTY, + "VARIDIV: Division by 0 should result in DISP_E_DIVBYZERO but got 0x%X\n", hres); + + SysFreeString(num1_str); + SysFreeString(num2_str); +} + + +static HRESULT (WINAPI *pVarImp)(LPVARIANT,LPVARIANT,LPVARIANT); + +#define VARIMP(vt1,val1,vt2,val2,rvt,rval) \ + V_VT(&left) = VT_##vt1; V_##vt1(&left) = val1; \ + V_VT(&right) = VT_##vt2; V_##vt2(&right) = val2; \ + V_VT(&exp) = VT_##rvt; V_##rvt(&exp) = rval; \ + test_var_call2( __LINE__, pVarImp, &left, &right, &exp ) + +/* Skip any type that is not defined or produces an error for every case */ +#define SKIPTESTIMP(a) \ + if (a == VT_ERROR || a == VT_VARIANT || \ + a == VT_DISPATCH || a == VT_UNKNOWN || \ + a == VT_RECORD || a > VT_UINT || \ + a == 15 /*not defined*/) \ + continue + +static void test_VarImp(void) +{ + static const WCHAR szFalse[] = { '#','F','A','L','S','E','#','\0' }; + static const WCHAR szTrue[] = { '#','T','R','U','E','#','\0' }; + VARIANT left, right, exp, result, cy, dec; + BSTR true_str, false_str; + VARTYPE i; + HRESULT hres; + + CHECKPTR(VarImp); + + true_str = SysAllocString(szTrue); + false_str = SysAllocString(szFalse); + + /* Test all possible flag/vt combinations & the resulting vt type */ + for (i = 0; i < sizeof(ExtraFlags)/sizeof(ExtraFlags[0]); i++) + { + VARTYPE leftvt, rightvt, resvt; + + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + SKIPTESTIMP(leftvt); + + /* Check if we need/have support for I8 and/or UI8 */ + if ((leftvt == VT_I8 || leftvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + SKIPTESTIMP(rightvt); + + /* Native crashes when using the extra flag VT_BYREF + * or with the following VT combinations + */ + if ((leftvt == VT_UI4 && rightvt == VT_BSTR) || + (leftvt == VT_UI8 && rightvt == VT_BSTR) || + ExtraFlags[i] == VT_BYREF) + continue; + + /* Check if we need/have support for I8 and/or UI8 */ + if ((rightvt == VT_I8 || rightvt == VT_UI8) && !HAVE_OLEAUT32_I8) + continue; + + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = leftvt | ExtraFlags[i]; + V_VT(&right) = rightvt | ExtraFlags[i]; + V_VT(&result) = VT_EMPTY; + resvt = VT_EMPTY; + + if (leftvt == VT_BSTR) + V_BSTR(&left) = true_str; + + /* This allows us to test return types that are not NULL + * (NULL Imp value = n, NULL Imp 0 = NULL) + */ + switch(rightvt) + { + case VT_BSTR: + V_BSTR(&right) = true_str; + break; + case VT_DECIMAL: + VarDecFromR8(2.0, &V_DECIMAL(&right)); + V_VT(&right) = rightvt | ExtraFlags[i]; + break; + case VT_BOOL: + V_BOOL(&right) = VARIANT_TRUE; + break; + case VT_I1: V_I1(&right) = 2; break; + case VT_I2: V_I2(&right) = 2; break; + case VT_I4: V_I4(&right) = 2; break; + case VT_R4: V_R4(&right) = 2.0f; break; + case VT_R8: V_R8(&right) = 2.0; break; + case VT_CY: V_CY(&right).int64 = 10000; break; + case VT_DATE: V_DATE(&right) = 2; break; + case VT_I8: V_I8(&right) = 2; break; + case VT_INT: V_INT(&right) = 2; break; + case VT_UINT: V_UINT(&right) = 2; break; + case VT_UI1: V_UI1(&right) = 2; break; + case VT_UI2: V_UI2(&right) = 2; break; + case VT_UI4: V_UI4(&right) = 2; break; + case VT_UI8: V_UI8(&right) = 2; break; + default: break; + } + + /* Native VarImp always returns an error when using extra + * flags or if the variants are I8 and INT. + */ + if ((leftvt == VT_I8 && rightvt == VT_INT) || + ExtraFlags[i] != 0) + bFail = TRUE; + + /* Determine result type */ + else if ((leftvt == VT_BSTR && rightvt == VT_NULL) || + (leftvt == VT_NULL && rightvt == VT_NULL) || + (leftvt == VT_NULL && rightvt == VT_EMPTY)) + resvt = VT_NULL; + else if (leftvt == VT_I8 || rightvt == VT_I8) + resvt = VT_I8; + else if (leftvt == VT_I4 || rightvt == VT_I4 || + leftvt == VT_INT || rightvt == VT_INT || + leftvt == VT_UINT || rightvt == VT_UINT || + leftvt == VT_UI4 || rightvt == VT_UI4 || + leftvt == VT_UI8 || rightvt == VT_UI8 || + leftvt == VT_UI2 || rightvt == VT_UI2 || + leftvt == VT_DECIMAL || rightvt == VT_DECIMAL || + leftvt == VT_DATE || rightvt == VT_DATE || + leftvt == VT_CY || rightvt == VT_CY || + leftvt == VT_R8 || rightvt == VT_R8 || + leftvt == VT_R4 || rightvt == VT_R4 || + leftvt == VT_I1 || rightvt == VT_I1) + resvt = VT_I4; + else if ((leftvt == VT_UI1 && rightvt == VT_UI1) || + (leftvt == VT_UI1 && rightvt == VT_NULL) || + (leftvt == VT_NULL && rightvt == VT_UI1)) + resvt = VT_UI1; + else if (leftvt == VT_EMPTY || rightvt == VT_EMPTY || + leftvt == VT_I2 || rightvt == VT_I2 || + leftvt == VT_UI1 || rightvt == VT_UI1) + resvt = VT_I2; + else if (leftvt == VT_BOOL || rightvt == VT_BOOL || + leftvt == VT_BSTR || rightvt == VT_BSTR) + resvt = VT_BOOL; + + hres = pVarImp(&left, &right, &result); + + /* Check expected HRESULT and if result variant type is correct */ + if (bFail) + ok (hres == DISP_E_BADVARTYPE || hres == DISP_E_TYPEMISMATCH, + "VarImp: %s|0x%X, %s|0x%X: got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], + vtstr(V_VT(&result)), hres); + else + ok (hres == S_OK && resvt == V_VT(&result), + "VarImp: %s|0x%X, %s|0x%X: expected vt %s hr 0x%X, got vt %s hr 0x%X\n", + vtstr(leftvt), ExtraFlags[i], vtstr(rightvt), ExtraFlags[i], vtstr(resvt), + S_OK, vtstr(V_VT(&result)), hres); + } + } + } + + VARIMP(EMPTY,0,EMPTY,0,I2,-1); + VARIMP(EMPTY,0,NULL,0,I2,-1); + VARIMP(EMPTY,0,I2,-1,I2,-1); + VARIMP(EMPTY,0,I4,-1,I4,-1); + VARIMP(EMPTY,0,R4,0.0f,I4,-1); + VARIMP(EMPTY,0,R8,-1.0,I4,-1); + VARIMP(EMPTY,0,DATE,0,I4,-1); + VARIMP(EMPTY,0,BSTR,true_str,I2,-1); + VARIMP(EMPTY,0,BOOL,VARIANT_FALSE,I2,-1); + VARIMP(EMPTY,0,I1,0,I4,-1); + VARIMP(EMPTY,0,UI1,1,I2,-1); + VARIMP(EMPTY,0,UI2,1,I4,-1); + VARIMP(EMPTY,0,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(EMPTY,0,I8,1,I8,-1); + VARIMP(EMPTY,0,UI8,1,I4,-1); + } + VARIMP(EMPTY,0,INT,-1,I4,-1); + VARIMP(EMPTY,0,UINT,1,I4,-1); + VARIMP(NULL,0,EMPTY,0,NULL,0); + VARIMP(NULL,0,NULL,0,NULL,0); + VARIMP(NULL,0,I2,-1,I2,-1); + VARIMP(NULL,0,I4,-1,I4,-1); + VARIMP(NULL,0,R4,0.0f,NULL,0); + VARIMP(NULL,0,R8,-1.0,I4,-1); + VARIMP(NULL,0,DATE,0,NULL,0); + VARIMP(NULL,0,BSTR,true_str,BOOL,-1); + VARIMP(NULL,0,BOOL,VARIANT_FALSE,NULL,0); + VARIMP(NULL,0,I1,0,NULL,0); + VARIMP(NULL,0,UI1,1,UI1,1); + VARIMP(NULL,0,UI2,1,I4,1); + VARIMP(NULL,0,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(NULL,0,I8,1,I8,1); + VARIMP(NULL,0,UI8,1,I4,1); + } + VARIMP(NULL,0,INT,-1,I4,-1); + VARIMP(NULL,0,UINT,1,I4,1); + VARIMP(I2,-1,EMPTY,0,I2,0); + VARIMP(I2,-1,I2,-1,I2,-1); + VARIMP(I2,-1,I4,-1,I4,-1); + VARIMP(I2,-1,R4,0.0f,I4,0); + VARIMP(I2,-1,R8,-1.0,I4,-1); + VARIMP(I2,-1,DATE,0,I4,0); + VARIMP(I2,-1,BSTR,true_str,I2,-1); + VARIMP(I2,-1,BOOL,VARIANT_FALSE,I2,0); + VARIMP(I2,-1,I1,0,I4,0); + VARIMP(I2,-1,UI1,1,I2,1); + VARIMP(I2,-1,UI2,1,I4,1); + VARIMP(I2,-1,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(I2,-1,I8,1,I8,1); + VARIMP(I2,-1,UI8,1,I4,1); + } + VARIMP(I2,-1,INT,-1,I4,-1); + VARIMP(I2,-1,UINT,1,I4,1); + VARIMP(I4,2,EMPTY,0,I4,-3); + VARIMP(I4,2,NULL,0,I4,-3); + VARIMP(I4,2,I2,-1,I4,-1); + VARIMP(I4,2,I4,-1,I4,-1); + VARIMP(I4,2,R4,0.0f,I4,-3); + VARIMP(I4,2,R8,-1.0,I4,-1); + VARIMP(I4,2,DATE,0,I4,-3); + VARIMP(I4,2,BSTR,true_str,I4,-1); + VARIMP(I4,2,BOOL,VARIANT_FALSE,I4,-3); + VARIMP(I4,2,I1,0,I4,-3); + VARIMP(I4,2,UI1,1,I4,-3); + VARIMP(I4,2,UI2,1,I4,-3); + VARIMP(I4,2,UI4,1,I4,-3); + if (HAVE_OLEAUT32_I8) + { + VARIMP(I4,2,I8,1,I8,-3); + VARIMP(I4,2,UI8,1,I4,-3); + } + VARIMP(I4,2,INT,-1,I4,-1); + VARIMP(I4,2,UINT,1,I4,-3); + VARIMP(R4,-1.0f,EMPTY,0,I4,0); + VARIMP(R4,-1.0f,NULL,0,NULL,0); + VARIMP(R4,-1.0f,I2,-1,I4,-1); + VARIMP(R4,-1.0f,I4,-1,I4,-1); + VARIMP(R4,-1.0f,R4,0.0f,I4,0); + VARIMP(R4,-1.0f,R8,-1.0,I4,-1); + VARIMP(R4,-1.0f,DATE,1,I4,1); + VARIMP(R4,-1.0f,BSTR,true_str,I4,-1); + VARIMP(R4,-1.0f,BOOL,VARIANT_FALSE,I4,0); + VARIMP(R4,-1.0f,I1,0,I4,0); + VARIMP(R4,-1.0f,UI1,1,I4,1); + VARIMP(R4,-1.0f,UI2,1,I4,1); + VARIMP(R4,-1.0f,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(R4,-1.0f,I8,1,I8,1); + VARIMP(R4,-1.0f,UI8,1,I4,1); + } + VARIMP(R4,-1.0f,INT,-1,I4,-1); + VARIMP(R4,-1.0f,UINT,1,I4,1); + VARIMP(R8,1.0,EMPTY,0,I4,-2); + VARIMP(R8,1.0,NULL,0,I4,-2); + VARIMP(R8,1.0,I2,-1,I4,-1); + VARIMP(R8,1.0,I4,-1,I4,-1); + VARIMP(R8,1.0,R4,0.0f,I4,-2); + VARIMP(R8,1.0,R8,-1.0,I4,-1); + VARIMP(R8,1.0,DATE,0,I4,-2); + VARIMP(R8,1.0,BSTR,true_str,I4,-1); + VARIMP(R8,1.0,BOOL,VARIANT_FALSE,I4,-2); + VARIMP(R8,1.0,I1,0,I4,-2); + VARIMP(R8,1.0,UI1,1,I4,-1); + VARIMP(R8,1.0,UI2,1,I4,-1); + VARIMP(R8,1.0,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(R8,1.0,I8,1,I8,-1); + VARIMP(R8,1.0,UI8,1,I4,-1); + } + VARIMP(R8,1.0,INT,-1,I4,-1); + VARIMP(R8,1.0,UINT,1,I4,-1); + VARIMP(DATE,0,EMPTY,0,I4,-1); + VARIMP(DATE,0,NULL,0,I4,-1); + VARIMP(DATE,0,I2,-1,I4,-1); + VARIMP(DATE,0,I4,-1,I4,-1); + VARIMP(DATE,0,R4,0.0f,I4,-1); + VARIMP(DATE,0,R8,-1.0,I4,-1); + VARIMP(DATE,0,DATE,0,I4,-1); + VARIMP(DATE,0,BSTR,true_str,I4,-1); + VARIMP(DATE,0,BOOL,VARIANT_FALSE,I4,-1); + VARIMP(DATE,0,I1,0,I4,-1); + VARIMP(DATE,0,UI1,1,I4,-1); + VARIMP(DATE,0,UI2,1,I4,-1); + VARIMP(DATE,0,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(DATE,0,I8,1,I8,-1); + VARIMP(DATE,0,UI8,1,I4,-1); + } + VARIMP(DATE,0,INT,-1,I4,-1); + VARIMP(DATE,0,UINT,1,I4,-1); + VARIMP(BSTR,false_str,EMPTY,0,I2,-1); + VARIMP(BSTR,false_str,NULL,0,BOOL,-1); + VARIMP(BSTR,false_str,I2,-1,I2,-1); + VARIMP(BSTR,false_str,I4,-1,I4,-1); + VARIMP(BSTR,false_str,R4,0.0f,I4,-1); + VARIMP(BSTR,false_str,R8,-1.0,I4,-1); + VARIMP(BSTR,false_str,DATE,0,I4,-1); + VARIMP(BSTR,false_str,BSTR,true_str,BOOL,-1); + VARIMP(BSTR,false_str,BOOL,VARIANT_FALSE,BOOL,-1); + VARIMP(BSTR,false_str,I1,0,I4,-1); + VARIMP(BSTR,false_str,UI1,1,I2,-1); + VARIMP(BSTR,false_str,UI2,1,I4,-1); + VARIMP(BSTR,false_str,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(BSTR,false_str,I8,1,I8,-1); + VARIMP(BSTR,false_str,UI8,1,I4,-1); + } + VARIMP(BSTR,false_str,INT,-1,I4,-1); + VARIMP(BSTR,false_str,UINT,1,I4,-1); + VARIMP(BOOL,VARIANT_TRUE,EMPTY,0,I2,0); + VARIMP(BOOL,VARIANT_TRUE,NULL,0,NULL,0); + VARIMP(BOOL,VARIANT_TRUE,I2,-1,I2,-1); + VARIMP(BOOL,VARIANT_TRUE,I4,-1,I4,-1); + VARIMP(BOOL,VARIANT_TRUE,R4,0.0f,I4,0); + VARIMP(BOOL,VARIANT_TRUE,R8,-1.0,I4,-1); + VARIMP(BOOL,VARIANT_TRUE,DATE,0,I4,0); + VARIMP(BOOL,VARIANT_TRUE,BSTR,true_str,BOOL,-1); + VARIMP(BOOL,VARIANT_TRUE,BOOL,VARIANT_FALSE,BOOL,0); + VARIMP(BOOL,VARIANT_TRUE,I1,0,I4,0); + VARIMP(BOOL,VARIANT_TRUE,UI1,1,I2,1); + VARIMP(BOOL,VARIANT_TRUE,UI2,1,I4,1); + VARIMP(BOOL,VARIANT_TRUE,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(BOOL,VARIANT_TRUE,I8,1,I8,1); + VARIMP(BOOL,VARIANT_TRUE,UI8,1,I4,1); + } + VARIMP(BOOL,VARIANT_TRUE,INT,-1,I4,-1); + VARIMP(BOOL,VARIANT_TRUE,UINT,1,I4,1); + VARIMP(I1,-1,EMPTY,0,I4,0); + VARIMP(I1,-1,NULL,0,NULL,0); + VARIMP(I1,-1,I2,-1,I4,-1); + VARIMP(I1,-1,I4,-1,I4,-1); + VARIMP(I1,-1,R4,0.0f,I4,0); + VARIMP(I1,-1,R8,-1.0,I4,-1); + VARIMP(I1,-1,DATE,0,I4,0); + VARIMP(I1,-1,BSTR,true_str,I4,-1); + VARIMP(I1,-1,BOOL,VARIANT_FALSE,I4,0); + VARIMP(I1,-1,I1,0,I4,0); + VARIMP(I1,-1,UI1,1,I4,1); + VARIMP(I1,-1,UI2,1,I4,1); + VARIMP(I1,-1,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(I1,-1,I8,1,I8,1); + VARIMP(I1,-1,UI8,1,I4,1); + } + VARIMP(I1,-1,INT,-1,I4,-1); + VARIMP(I1,-1,UINT,1,I4,1); + VARIMP(UI1,0,EMPTY,0,I2,-1); + VARIMP(UI1,0,NULL,0,UI1,255); + VARIMP(UI1,0,I2,-1,I2,-1); + VARIMP(UI1,0,I4,-1,I4,-1); + VARIMP(UI1,0,R4,0.0f,I4,-1); + VARIMP(UI1,0,R8,-1.0,I4,-1); + VARIMP(UI1,0,DATE,0,I4,-1); + VARIMP(UI1,0,BSTR,true_str,I2,-1); + VARIMP(UI1,0,BOOL,VARIANT_FALSE,I2,-1); + VARIMP(UI1,0,I1,0,I4,-1); + VARIMP(UI1,0,UI1,1,UI1,255); + VARIMP(UI1,0,UI2,1,I4,-1); + VARIMP(UI1,0,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(UI1,0,I8,1,I8,-1); + VARIMP(UI1,0,UI8,1,I4,-1); + } + VARIMP(UI1,0,INT,-1,I4,-1); + VARIMP(UI1,0,UINT,1,I4,-1); + VARIMP(UI2,0,EMPTY,0,I4,-1); + VARIMP(UI2,0,NULL,0,I4,-1); + VARIMP(UI2,0,I2,-1,I4,-1); + VARIMP(UI2,0,I4,-1,I4,-1); + VARIMP(UI2,0,R4,0.0f,I4,-1); + VARIMP(UI2,0,R8,-1.0,I4,-1); + VARIMP(UI2,0,DATE,0,I4,-1); + VARIMP(UI2,0,BSTR,true_str,I4,-1); + VARIMP(UI2,0,BOOL,VARIANT_FALSE,I4,-1); + VARIMP(UI2,0,I1,0,I4,-1); + VARIMP(UI2,0,UI1,1,I4,-1); + VARIMP(UI2,0,UI2,1,I4,-1); + VARIMP(UI2,0,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(UI2,0,I8,1,I8,-1); + VARIMP(UI2,0,UI8,1,I4,-1); + } + VARIMP(UI2,0,INT,-1,I4,-1); + VARIMP(UI2,0,UINT,1,I4,-1); + VARIMP(UI4,0,EMPTY,0,I4,-1); + VARIMP(UI4,0,NULL,0,I4,-1); + VARIMP(UI4,0,I2,-1,I4,-1); + VARIMP(UI4,0,I4,-1,I4,-1); + VARIMP(UI4,0,R4,0.0f,I4,-1); + VARIMP(UI4,0,R8,-1.0,I4,-1); + VARIMP(UI4,0,DATE,0,I4,-1); + VARIMP(UI4,0,BSTR,true_str,I4,-1); + VARIMP(UI4,0,BOOL,VARIANT_FALSE,I4,-1); + VARIMP(UI4,0,I1,0,I4,-1); + VARIMP(UI4,0,UI1,1,I4,-1); + VARIMP(UI4,0,UI2,1,I4,-1); + VARIMP(UI4,0,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(UI4,0,I8,1,I8,-1); + VARIMP(UI4,0,UI8,1,I4,-1); + } + VARIMP(UI4,0,INT,-1,I4,-1); + VARIMP(UI4,0,UINT,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(I8,-1,EMPTY,0,I8,0); + VARIMP(I8,-1,NULL,0,NULL,0); + VARIMP(I8,-1,I2,-1,I8,-1); + VARIMP(I8,-1,I4,-1,I8,-1); + VARIMP(I8,-1,R4,0.0f,I8,0); + VARIMP(I8,-1,R8,-1.0,I8,-1); + VARIMP(I8,-1,DATE,0,I8,0); + VARIMP(I8,-1,BSTR,true_str,I8,-1); + VARIMP(I8,-1,BOOL,VARIANT_FALSE,I8,0); + VARIMP(I8,-1,I1,0,I8,0); + VARIMP(I8,-1,UI1,1,I8,1); + VARIMP(I8,-1,UI2,1,I8,1); + VARIMP(I8,-1,UI4,1,I8,1); + VARIMP(I8,-1,I8,1,I8,1); + VARIMP(I8,-1,UI8,1,I8,1); + VARIMP(I8,-1,UINT,1,I8,1); + VARIMP(UI8,0,EMPTY,0,I4,-1); + VARIMP(UI8,0,NULL,0,I4,-1); + VARIMP(UI8,0,I2,-1,I4,-1); + VARIMP(UI8,0,I4,-1,I4,-1); + VARIMP(UI8,0,R4,0.0f,I4,-1); + VARIMP(UI8,0,R8,-1.0,I4,-1); + VARIMP(UI8,0,DATE,0,I4,-1); + VARIMP(UI8,0,BSTR,true_str,I4,-1); + VARIMP(UI8,0,BOOL,VARIANT_FALSE,I4,-1); + VARIMP(UI8,0,I1,0,I4,-1); + VARIMP(UI8,0,UI1,1,I4,-1); + VARIMP(UI8,0,UI2,1,I4,-1); + VARIMP(UI8,0,UI4,1,I4,-1); + VARIMP(UI8,0,I8,1,I8,-1); + VARIMP(UI8,0,UI8,1,I4,-1); + VARIMP(UI8,0,INT,-1,I4,-1); + VARIMP(UI8,0,UINT,1,I4,-1); + } + VARIMP(INT,-1,EMPTY,0,I4,0); + VARIMP(INT,-1,NULL,0,NULL,0); + VARIMP(INT,-1,I2,-1,I4,-1); + VARIMP(INT,-1,I4,-1,I4,-1); + VARIMP(INT,-1,R4,0.0f,I4,0); + VARIMP(INT,-1,R8,-1.0,I4,-1); + VARIMP(INT,-1,DATE,0,I4,0); + VARIMP(INT,-1,BSTR,true_str,I4,-1); + VARIMP(INT,-1,BOOL,VARIANT_FALSE,I4,0); + VARIMP(INT,-1,I1,0,I4,0); + VARIMP(INT,-1,UI1,1,I4,1); + VARIMP(INT,-1,UI2,1,I4,1); + VARIMP(INT,-1,UI4,1,I4,1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(INT,-1,I8,1,I8,1); + VARIMP(INT,-1,UI8,1,I4,1); + } + VARIMP(INT,-1,INT,-1,I4,-1); + VARIMP(INT,-1,UINT,1,I4,1); + VARIMP(UINT,1,EMPTY,0,I4,-2); + VARIMP(UINT,1,NULL,0,I4,-2); + VARIMP(UINT,1,I2,-1,I4,-1); + VARIMP(UINT,1,I4,-1,I4,-1); + VARIMP(UINT,1,R4,0.0f,I4,-2); + VARIMP(UINT,1,R8,-1.0,I4,-1); + VARIMP(UINT,1,DATE,0,I4,-2); + VARIMP(UINT,1,BSTR,true_str,I4,-1); + VARIMP(UINT,1,BOOL,VARIANT_FALSE,I4,-2); + VARIMP(UINT,1,I1,0,I4,-2); + VARIMP(UINT,1,UI1,1,I4,-1); + VARIMP(UINT,1,UI2,1,I4,-1); + VARIMP(UINT,1,UI4,1,I4,-1); + if (HAVE_OLEAUT32_I8) + { + VARIMP(UINT,1,I8,1,I8,-1); + VARIMP(UINT,1,UI8,1,I4,-1); + } + VARIMP(UINT,1,INT,-1,I4,-1); + VARIMP(UINT,1,UINT,1,I4,-1); + + /* Manually test some VT_CY, VT_DECIMAL variants */ + V_VT(&cy) = VT_CY; + hres = VarCyFromI4(1, &V_CY(&cy)); + ok(hres == S_OK, "VarCyFromI4 failed!\n"); + V_VT(&dec) = VT_DECIMAL; + hres = VarDecFromR8(2.0, &V_DECIMAL(&dec)); + ok(hres == S_OK, "VarDecFromR4 failed!\n"); + memset(&left, 0, sizeof(left)); + memset(&right, 0, sizeof(right)); + V_VT(&left) = VT_I4; + V_I4(&left) = 0; + V_VT(&right) = VT_I8; + V_UI1(&right) = 0; + + hres = pVarImp(&cy, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIMP: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == -1, + "VARIMP: CY value %d, expected %d\n", V_I4(&result), -1); + + if (HAVE_OLEAUT32_I8) + { + hres = pVarImp(&cy, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_I8, + "VARIMP: expected coerced hres 0x%X type VT_I8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I8(&result) == -2, + "VARIMP: CY value %x%08x, expected %d\n", + (DWORD)((V_I8(&result)) >> 32), (DWORD)(V_I8(&result)), -2); + } + + hres = pVarImp(&left, &cy, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIMP: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == -1, + "VARIMP: CY value %d, expected %d\n", V_I4(&result), -1); + + hres = pVarImp(&left, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIMP: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == -1, + "VARIMP: DECIMAL value %d, expected %d\n", V_I4(&result), -1); + + hres = pVarImp(&dec, &dec, &result); + ok(hres == S_OK && V_VT(&result) == VT_I4, + "VARIMP: expected coerced hres 0x%X type VT_I4, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I4(&result) == -1, + "VARIMP: DECIMAL value %d, expected %d\n", V_I4(&result), -1); + + if (HAVE_OLEAUT32_I8) + { + hres = pVarImp(&dec, &right, &result); + ok(hres == S_OK && V_VT(&result) == VT_I8, + "VARIMP: expected coerced hres 0x%X type VT_I8, got hres 0x%X type %s!\n", + S_OK, hres, vtstr(V_VT(&result))); + ok(hres == S_OK && V_I8(&result) == -3, + "VARIMP: DECIMAL value 0x%x%08x, expected %d\n", + (DWORD)(V_I8(&result) >>32), (DWORD)V_I8(&result), -3); + } + + SysFreeString(false_str); + SysFreeString(true_str); +} + +START_TEST(vartest) +{ + init(); + + test_VariantInit(); + test_VariantClear(); + test_VariantCopy(); + test_VariantCopyInd(); + test_VarParseNumFromStr(); + test_VarNumFromParseNum(); + test_VarUdateFromDate(); + test_VarDateFromUdate(); + test_SystemTimeToVariantTime(); + test_VariantTimeToSystemTime(); + test_DosDateTimeToVariantTime(); + test_VariantTimeToDosDateTime(); + test_VarAbs(); + test_VarNot(); + test_VarSub(); + test_VarMod(); + test_VarFix(); + test_VarInt(); + test_VarNeg(); + test_VarRound(); + test_VarXor(); + test_VarOr(); + test_VarPow(); + test_VarEqv(); + test_VarMul(); + test_VarAdd(); + test_VarCat(); + test_VarCmp(); + test_VarAnd(); + test_VarDiv(); + test_VarIdiv(); + test_VarImp(); +} diff --git a/rostests/winetests/oleaut32/vartype.c b/rostests/winetests/oleaut32/vartype.c new file mode 100644 index 00000000000..2292036027f --- /dev/null +++ b/rostests/winetests/oleaut32/vartype.c @@ -0,0 +1,6074 @@ +/* + * Low level variant tests + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/test.h" +#include "oleauto.h" +#include + +/* Some Visual C++ versions choke on __uint64 to float conversions. + * To fix this you need either VC++ 6.0 plus the processor pack + * or Visual C++ >=7.0. + */ +#ifndef _MSC_VER +# define HAS_UINT64_TO_FLOAT +#else +# if _MSC_VER >= 1300 +# define HAS_UINT64_TO_FLOAT +# else +# include +# if defined(_mm_free) +/* _mm_free is defined if the Processor Pack has been installed */ +# define HAS_UINT64_TO_FLOAT +# endif + +# endif +#endif + +static HMODULE hOleaut32; + +/* Get a conversion function ptr, return if function not available */ +#define CHECKPTR(func) p##func = (void*)GetProcAddress(hOleaut32, #func); \ + if (!p##func) { \ + skip("function " # func " not available, not testing it\n"); return; } + +/* Is a given function exported from oleaut32? */ +#define HAVE_FUNC(func) ((void*)GetProcAddress(hOleaut32, #func) != NULL) + +/* Have IRecordInfo data type? */ +#define HAVE_OLEAUT32_RECORD HAVE_FUNC(SafeArraySetRecordInfo) +/* Have DECIMAL data type with new error checking? */ +#define HAVE_OLEAUT32_DECIMAL HAVE_FUNC(VarDecAdd) +/* Have CY data type? */ +#define HAVE_OLEAUT32_CY HAVE_FUNC(VarCyAdd) +/* Have I8/UI8 data type? */ +#define HAVE_OLEAUT32_I8 HAVE_FUNC(VarI8FromI1) +/* Have proper locale conversions? */ +#define HAVE_OLEAUT32_LOCALES (HAVE_FUNC(GetVarConversionLocaleSetting) && HAVE_OLEAUT32_I8) +/* Is this an ancient version with support for only I2/I4/R4/R8/DATE? */ +#define IS_ANCIENT (!HAVE_FUNC(VarI1FromI2)) +/* Is vt a type unavailable to ancient versions? */ +#define IS_MODERN_VTYPE(vt) (vt==VT_VARIANT||vt==VT_DECIMAL|| \ + vt==VT_I1||vt==VT_UI2||vt==VT_UI4||vt == VT_INT||vt == VT_UINT) + +/* Macros for converting and testing results */ +#define CONVVARS(typ) HRESULT hres; CONV_TYPE out; typ in + +#define _EXPECTRES(res, x, fs) \ + ok((hres == S_OK && out == (CONV_TYPE)(x)) || ((HRESULT)res != S_OK && hres == (HRESULT)res), \ + "expected " #x ", got " fs "; hres=0x%08x\n", out, hres) +#define EXPECT(x) EXPECTRES(S_OK, (x)) +#define EXPECT_OVERFLOW EXPECTRES(DISP_E_OVERFLOW, DISP_E_OVERFLOW) +#define EXPECT_MISMATCH EXPECTRES(DISP_E_TYPEMISMATCH,DISP_E_TYPEMISMATCH) +#define EXPECT_BADVAR EXPECTRES(DISP_E_BADVARTYPE, DISP_E_BADVARTYPE) +#define EXPECT_INVALID EXPECTRES(E_INVALIDARG, E_INVALIDARG) +#define EXPECT_LT EXPECTRES(VARCMP_LT, VARCMP_LT) +#define EXPECT_GT EXPECTRES(VARCMP_GT, VARCMP_GT) +#define EXPECT_EQ EXPECTRES(VARCMP_EQ, VARCMP_EQ) +#define EXPECT_DBL(x) \ + ok(hres == S_OK && fabs(out-(x))<=1e-14*(x), "expected %16.16g, got %16.16g; hres=0x%08x\n", (x), out, hres) + +#define CONVERT(func, val) in = val; hres = p##func(in, &out) +#define CONVERTRANGE(func,start,end) for (i = start; i < end; i+=1) { CONVERT(func, i); EXPECT(i); }; +#define OVERFLOWRANGE(func,start,end) for (i = start; i < end; i+=1) { CONVERT(func, i); EXPECT_OVERFLOW; }; + +#define CY_MULTIPLIER 10000 + +#define DATE_MIN -657434 +#define DATE_MAX 2958465 + +#define CONVERT_I8(func,hi,lo) in = hi; in = (in << 32) | lo; hres = p##func(in, &out) + +#define CONVERT_CY(func,val) in.int64 = (LONGLONG)(val * CY_MULTIPLIER); hres = p##func(in, &out) + +#define CONVERT_CY64(func,hi,lo) S(in).Hi = hi; S(in).Lo = lo; in.int64 *= CY_MULTIPLIER; hres = p##func(in, &out) + +#define SETDEC(dec, scl, sgn, hi, lo) S(U(dec)).scale = (BYTE)scl; S(U(dec)).sign = (BYTE)sgn; \ + dec.Hi32 = (ULONG)hi; U1(dec).Lo64 = (ULONG64)lo + +#define SETDEC64(dec, scl, sgn, hi, mid, lo) S(U(dec)).scale = (BYTE)scl; S(U(dec)).sign = (BYTE)sgn; \ + dec.Hi32 = (ULONG)hi; S1(U1(dec)).Mid32 = mid; S1(U1(dec)).Lo32 = lo; + +#define CONVERT_DEC(func,scl,sgn,hi,lo) SETDEC(in,scl,sgn,hi,lo); hres = p##func(&in, &out) + +#define CONVERT_DEC64(func,scl,sgn,hi,mid,lo) SETDEC64(in,scl,sgn,hi,mid,lo); hres = p##func(&in, &out) + +#define CONVERT_BADDEC(func) \ + if (HAVE_OLEAUT32_DECIMAL) \ + { \ + CONVERT_DEC(func,29,0,0,0); EXPECT_INVALID; \ + CONVERT_DEC(func,0,0x1,0,0); EXPECT_INVALID; \ + CONVERT_DEC(func,0,0x40,0,0); EXPECT_INVALID; \ + CONVERT_DEC(func,0,0x7f,0,0); EXPECT_INVALID; \ + } + +#define CONVERT_STR(func,str,flags) \ + SetLastError(0); \ + if (str) MultiByteToWideChar(CP_ACP,0,str,-1,buff,sizeof(buff)/sizeof(WCHAR)); \ + hres = p##func(str ? buff : NULL,in,flags,&out) + +#define COPYTEST(val, vt, srcval, dstval, srcref, dstref, fs) do { \ + HRESULT hres; VARIANTARG vSrc, vDst; CONV_TYPE in = val; \ + VariantInit(&vSrc); VariantInit(&vDst); \ + V_VT(&vSrc) = vt; srcval = in; \ + hres = VariantCopy(&vDst, &vSrc); \ + ok(hres == S_OK && V_VT(&vDst) == vt && dstval == in, \ + "copy hres 0x%X, type %d, value (" fs ") " fs "\n", hres, V_VT(&vDst), val, dstval); \ + V_VT(&vSrc) = vt|VT_BYREF; srcref = ∈ \ + hres = VariantCopy(&vDst, &vSrc); \ + ok(hres == S_OK && V_VT(&vDst) == (vt|VT_BYREF) && dstref == &in, \ + "ref hres 0x%X, type %d, ref (%p) %p\n", hres, V_VT(&vDst), &in, dstref); \ + hres = VariantCopyInd(&vDst, &vSrc); \ + ok(hres == S_OK && V_VT(&vDst) == vt && dstval == in, \ + "ind hres 0x%X, type %d, value (" fs ") " fs "\n", hres, V_VT(&vDst), val, dstval); \ + } while(0) + +#define CHANGETYPEEX(typ) hres = VariantChangeTypeEx(&vDst, &vSrc, 0, 0, typ) + +#define TYPETEST(typ,res,fs) CHANGETYPEEX(typ); \ + ok(hres == S_OK && V_VT(&vDst) == typ && (CONV_TYPE)res == in, \ + "hres=0x%X, type=%d (should be %d(" #typ ")), value=" fs " (should be " fs ")\n", \ + hres, V_VT(&vDst), typ, (CONV_TYPE)res, in); +#define TYPETESTI8(typ,res) CHANGETYPEEX(typ); \ + ok(hres == S_OK && V_VT(&vDst) == typ && (CONV_TYPE)res == in, \ + "hres=0x%X, type=%d (should be %d(" #typ ")), value=%d (should be 1)\n", \ + hres, V_VT(&vDst), typ, (int)res); +#define BADVAR(typ) CHANGETYPEEX(typ); out = (CONV_TYPE)hres; EXPECT_BADVAR +#define MISMATCH(typ) CHANGETYPEEX(typ); out = (CONV_TYPE)hres; EXPECT_MISMATCH + +#define INITIAL_TYPETEST(vt, val, fs) \ + VariantInit(&vSrc); \ + VariantInit(&vDst); \ + V_VT(&vSrc) = vt; \ + (val(&vSrc)) = in; \ + if (!IS_ANCIENT) { \ + TYPETEST(VT_I1, V_I1(&vDst), fs); \ + TYPETEST(VT_UI2, V_UI2(&vDst), fs); \ + TYPETEST(VT_UI4, V_UI4(&vDst), fs); \ + TYPETEST(VT_INT, V_INT(&vDst), fs); \ + TYPETEST(VT_UINT, V_UINT(&vDst), fs); \ + } else { \ + BADVAR(VT_I1); BADVAR(VT_UI2); BADVAR(VT_UI4); \ + BADVAR(VT_INT); BADVAR(VT_UINT); \ + } \ + TYPETEST(VT_UI1, V_UI1(&vDst), fs); \ + TYPETEST(VT_I2, V_I2(&vDst), fs); \ + TYPETEST(VT_I4, V_I4(&vDst), fs); \ + TYPETEST(VT_R4, V_R4(&vDst), fs); \ + TYPETEST(VT_R8, V_R8(&vDst), fs); \ + TYPETEST(VT_DATE, V_DATE(&vDst), fs); \ + if (HAVE_OLEAUT32_I8) \ + { \ + TYPETEST(VT_I8, V_I8(&vDst), fs); \ + TYPETEST(VT_UI8, V_UI8(&vDst), fs); \ + } +#define NEGATIVE_TYPETEST(vt, val, fs, vtneg, valneg) \ + in = -in; \ + VariantInit(&vSrc); \ + VariantInit(&vDst); \ + V_VT(&vSrc) = vt; \ + (val(&vSrc)) = in; \ + if (!IS_ANCIENT) { \ + TYPETEST(vtneg, valneg(&vDst), fs); \ + } + +#define INITIAL_TYPETESTI8(vt, val) \ + VariantInit(&vSrc); \ + VariantInit(&vDst); \ + V_VT(&vSrc) = vt; \ + (val(&vSrc)) = in; \ + TYPETESTI8(VT_I1, V_I1(&vDst)); \ + TYPETESTI8(VT_UI1, V_UI1(&vDst)); \ + TYPETESTI8(VT_I2, V_I2(&vDst)); \ + TYPETESTI8(VT_UI2, V_UI2(&vDst)); \ + TYPETESTI8(VT_I4, V_I4(&vDst)); \ + TYPETESTI8(VT_UI4, V_UI4(&vDst)); \ + TYPETESTI8(VT_INT, V_INT(&vDst)); \ + TYPETESTI8(VT_UINT, V_UINT(&vDst)); \ + TYPETESTI8(VT_R4, V_R4(&vDst)); \ + TYPETESTI8(VT_R8, V_R8(&vDst)); \ + TYPETESTI8(VT_DATE, V_DATE(&vDst)); \ + TYPETESTI8(VT_I8, V_I8(&vDst)); \ + TYPETESTI8(VT_UI8, V_UI8(&vDst)) + +#define COMMON_TYPETEST \ + hres = VariantChangeTypeEx(&vDst, &vSrc, 0, 0, VT_BOOL); \ + ok(hres == S_OK && V_VT(&vDst) == VT_BOOL && \ + (V_BOOL(&vDst) == VARIANT_TRUE || (V_VT(&vSrc) == VT_BOOL && V_BOOL(&vDst) == 1)), \ + "->VT_BOOL hres=0x%X, type=%d (should be VT_BOOL), value %d (should be VARIANT_TRUE)\n", \ + hres, V_VT(&vDst), V_BOOL(&vDst)); \ + if (HAVE_OLEAUT32_CY) \ + { \ + hres = VariantChangeTypeEx(&vDst, &vSrc, 0, 0, VT_CY); \ + ok(hres == S_OK && V_VT(&vDst) == VT_CY && V_CY(&vDst).int64 == CY_MULTIPLIER, \ + "->VT_CY hres=0x%X, type=%d (should be VT_CY), value (%08x,%08x) (should be CY_MULTIPLIER)\n", \ + hres, V_VT(&vDst), S(V_CY(&vDst)).Hi, S(V_CY(&vDst)).Lo); \ + } \ + if (V_VT(&vSrc) != VT_DATE) \ + { \ + hres = VariantChangeTypeEx(&vDst, &vSrc, 0, 0, VT_BSTR); \ + ok(hres == S_OK && V_VT(&vDst) == VT_BSTR && \ + V_BSTR(&vDst) && V_BSTR(&vDst)[0] == '1' && V_BSTR(&vDst)[1] == '\0', \ + "->VT_BSTR hres=0x%X, type=%d (should be VT_BSTR), *bstr='%c'\n", \ + hres, V_VT(&vDst), V_BSTR(&vDst) ? *V_BSTR(&vDst) : '?'); \ + } \ + if (HAVE_OLEAUT32_DECIMAL) \ + { \ + hres = VariantChangeTypeEx(&vDst, &vSrc, 0, 0, VT_DECIMAL); \ + ok(hres == S_OK && V_VT(&vDst) == VT_DECIMAL && \ + S(U(V_DECIMAL(&vDst))).sign == 0 && S(U(V_DECIMAL(&vDst))).scale == 0 && \ + V_DECIMAL(&vDst).Hi32 == 0 && U1(V_DECIMAL(&vDst)).Lo64 == (ULONGLONG)in, \ + "->VT_DECIMAL hres=0x%X, type=%d (should be VT_DECIMAL), sign=%d, scale=%d, hi=%u, lo=(%8x %8x),\n", \ + hres, V_VT(&vDst), S(U(V_DECIMAL(&vDst))).sign, S(U(V_DECIMAL(&vDst))).scale, \ + V_DECIMAL(&vDst).Hi32, S1(U1(V_DECIMAL(&vDst))).Mid32, S1(U1(V_DECIMAL(&vDst))).Lo32); \ + } \ + hres = VariantChangeTypeEx(&vDst, &vSrc, 0, 0, VT_EMPTY); \ + ok(hres == S_OK && V_VT(&vDst) == VT_EMPTY, "->VT_EMPTY hres=0x%X, type=%d (should be VT_EMPTY)\n", hres, V_VT(&vDst)); \ + hres = VariantChangeTypeEx(&vDst, &vSrc, 0, 0, VT_NULL); \ + ok(hres == S_OK && V_VT(&vDst) == VT_NULL, "->VT_NULL hres=0x%X, type=%d (should be VT_NULL)\n", hres, V_VT(&vDst)); \ + MISMATCH(VT_DISPATCH); \ + MISMATCH(VT_ERROR); \ + MISMATCH(VT_UNKNOWN); \ + if (!IS_ANCIENT) { MISMATCH(VT_VARIANT); } else { BADVAR(VT_VARIANT); } \ + if (HAVE_OLEAUT32_RECORD) \ + { \ + MISMATCH(VT_RECORD); \ + } \ + BADVAR(VT_VOID); \ + BADVAR(VT_HRESULT); \ + BADVAR(VT_SAFEARRAY); \ + BADVAR(VT_CARRAY); \ + BADVAR(VT_USERDEFINED); \ + BADVAR(VT_LPSTR); \ + BADVAR(VT_LPWSTR); \ + BADVAR(VT_PTR); \ + BADVAR(VT_INT_PTR); \ + BADVAR(VT_UINT_PTR); \ + BADVAR(VT_FILETIME); \ + BADVAR(VT_BLOB); \ + BADVAR(VT_STREAM); \ + BADVAR(VT_STORAGE); \ + BADVAR(VT_STREAMED_OBJECT); \ + BADVAR(VT_STORED_OBJECT); \ + BADVAR(VT_BLOB_OBJECT); \ + BADVAR(VT_CF); \ + BADVAR(VT_CLSID); \ + BADVAR(VT_BSTR_BLOB) + +/* Early versions of oleaut32 are missing many functions */ +static HRESULT (WINAPI *pVarI1FromUI1)(BYTE,signed char*); +static HRESULT (WINAPI *pVarI1FromI2)(SHORT,signed char*); +static HRESULT (WINAPI *pVarI1FromI4)(LONG,signed char*); +static HRESULT (WINAPI *pVarI1FromR4)(FLOAT,signed char*); +static HRESULT (WINAPI *pVarI1FromR8)(double,signed char*); +static HRESULT (WINAPI *pVarI1FromDate)(DATE,signed char*); +static HRESULT (WINAPI *pVarI1FromCy)(CY,signed char*); +static HRESULT (WINAPI *pVarI1FromStr)(OLECHAR*,LCID,ULONG,signed char*); +static HRESULT (WINAPI *pVarI1FromBool)(VARIANT_BOOL,signed char*); +static HRESULT (WINAPI *pVarI1FromUI2)(USHORT,signed char*); +static HRESULT (WINAPI *pVarI1FromUI4)(ULONG,signed char*); +static HRESULT (WINAPI *pVarI1FromDec)(DECIMAL*,signed char*); +static HRESULT (WINAPI *pVarI1FromI8)(LONG64,signed char*); +static HRESULT (WINAPI *pVarI1FromUI8)(ULONG64,signed char*); +static HRESULT (WINAPI *pVarUI1FromI2)(SHORT,BYTE*); +static HRESULT (WINAPI *pVarUI1FromI4)(LONG,BYTE*); +static HRESULT (WINAPI *pVarUI1FromR4)(FLOAT,BYTE*); +static HRESULT (WINAPI *pVarUI1FromR8)(double,BYTE*); +static HRESULT (WINAPI *pVarUI1FromCy)(CY,BYTE*); +static HRESULT (WINAPI *pVarUI1FromDate)(DATE,BYTE*); +static HRESULT (WINAPI *pVarUI1FromStr)(OLECHAR*,LCID,ULONG,BYTE*); +static HRESULT (WINAPI *pVarUI1FromBool)(VARIANT_BOOL,BYTE*); +static HRESULT (WINAPI *pVarUI1FromI1)(signed char,BYTE*); +static HRESULT (WINAPI *pVarUI1FromUI2)(USHORT,BYTE*); +static HRESULT (WINAPI *pVarUI1FromUI4)(ULONG,BYTE*); +static HRESULT (WINAPI *pVarUI1FromDec)(DECIMAL*,BYTE*); +static HRESULT (WINAPI *pVarUI1FromI8)(LONG64,BYTE*); +static HRESULT (WINAPI *pVarUI1FromUI8)(ULONG64,BYTE*); +static HRESULT (WINAPI *pVarUI1FromDisp)(IDispatch*,LCID,BYTE*); + +static HRESULT (WINAPI *pVarI2FromUI1)(BYTE,SHORT*); +static HRESULT (WINAPI *pVarI2FromI4)(LONG,SHORT*); +static HRESULT (WINAPI *pVarI2FromR4)(FLOAT,SHORT*); +static HRESULT (WINAPI *pVarI2FromR8)(double,SHORT*); +static HRESULT (WINAPI *pVarI2FromCy)(CY,SHORT*); +static HRESULT (WINAPI *pVarI2FromDate)(DATE,SHORT*); +static HRESULT (WINAPI *pVarI2FromStr)(OLECHAR*,LCID,ULONG,SHORT*); +static HRESULT (WINAPI *pVarI2FromBool)(VARIANT_BOOL,SHORT*); +static HRESULT (WINAPI *pVarI2FromI1)(signed char,SHORT*); +static HRESULT (WINAPI *pVarI2FromUI2)(USHORT,SHORT*); +static HRESULT (WINAPI *pVarI2FromUI4)(ULONG,SHORT*); +static HRESULT (WINAPI *pVarI2FromDec)(DECIMAL*,SHORT*); +static HRESULT (WINAPI *pVarI2FromI8)(LONG64,SHORT*); +static HRESULT (WINAPI *pVarI2FromUI8)(ULONG64,SHORT*); +static HRESULT (WINAPI *pVarUI2FromUI1)(BYTE,USHORT*); +static HRESULT (WINAPI *pVarUI2FromI2)(SHORT,USHORT*); +static HRESULT (WINAPI *pVarUI2FromI4)(LONG,USHORT*); +static HRESULT (WINAPI *pVarUI2FromR4)(FLOAT,USHORT*); +static HRESULT (WINAPI *pVarUI2FromR8)(double,USHORT*); +static HRESULT (WINAPI *pVarUI2FromDate)(DATE,USHORT*); +static HRESULT (WINAPI *pVarUI2FromCy)(CY,USHORT*); +static HRESULT (WINAPI *pVarUI2FromStr)(OLECHAR*,LCID,ULONG,USHORT*); +static HRESULT (WINAPI *pVarUI2FromBool)(VARIANT_BOOL,USHORT*); +static HRESULT (WINAPI *pVarUI2FromI1)(signed char,USHORT*); +static HRESULT (WINAPI *pVarUI2FromUI4)(ULONG,USHORT*); +static HRESULT (WINAPI *pVarUI2FromDec)(DECIMAL*,USHORT*); +static HRESULT (WINAPI *pVarUI2FromI8)(LONG64,USHORT*); +static HRESULT (WINAPI *pVarUI2FromUI8)(ULONG64,USHORT*); + +static HRESULT (WINAPI *pVarI4FromUI1)(BYTE,LONG*); +static HRESULT (WINAPI *pVarI4FromI2)(SHORT,LONG*); +static HRESULT (WINAPI *pVarI4FromR4)(FLOAT,LONG*); +static HRESULT (WINAPI *pVarI4FromR8)(DOUBLE,LONG*); +static HRESULT (WINAPI *pVarI4FromCy)(CY,LONG*); +static HRESULT (WINAPI *pVarI4FromDate)(DATE,LONG*); +static HRESULT (WINAPI *pVarI4FromStr)(OLECHAR*,LCID,ULONG,LONG*); +static HRESULT (WINAPI *pVarI4FromBool)(VARIANT_BOOL,LONG*); +static HRESULT (WINAPI *pVarI4FromI1)(signed char,LONG*); +static HRESULT (WINAPI *pVarI4FromUI2)(USHORT,LONG*); +static HRESULT (WINAPI *pVarI4FromUI4)(ULONG,LONG*); +static HRESULT (WINAPI *pVarI4FromDec)(DECIMAL*,LONG*); +static HRESULT (WINAPI *pVarI4FromI8)(LONG64,LONG*); +static HRESULT (WINAPI *pVarI4FromUI8)(ULONG64,LONG*); +static HRESULT (WINAPI *pVarUI4FromUI1)(BYTE,ULONG*); +static HRESULT (WINAPI *pVarUI4FromI2)(SHORT,ULONG*); +static HRESULT (WINAPI *pVarUI4FromI4)(LONG,ULONG*); +static HRESULT (WINAPI *pVarUI4FromR4)(FLOAT,ULONG*); +static HRESULT (WINAPI *pVarUI4FromR8)(DOUBLE,ULONG*); +static HRESULT (WINAPI *pVarUI4FromDate)(DATE,ULONG*); +static HRESULT (WINAPI *pVarUI4FromCy)(CY,ULONG*); +static HRESULT (WINAPI *pVarUI4FromStr)(OLECHAR*,LCID,ULONG,ULONG*); +static HRESULT (WINAPI *pVarUI4FromBool)(VARIANT_BOOL,ULONG*); +static HRESULT (WINAPI *pVarUI4FromI1)(signed char,ULONG*); +static HRESULT (WINAPI *pVarUI4FromUI2)(USHORT,ULONG*); +static HRESULT (WINAPI *pVarUI4FromDec)(DECIMAL*,ULONG*); +static HRESULT (WINAPI *pVarUI4FromI8)(LONG64,ULONG*); +static HRESULT (WINAPI *pVarUI4FromUI8)(ULONG64,ULONG*); + +static HRESULT (WINAPI *pVarI8FromUI1)(BYTE,LONG64*); +static HRESULT (WINAPI *pVarI8FromI2)(SHORT,LONG64*); +static HRESULT (WINAPI *pVarI8FromR4)(FLOAT,LONG64*); +static HRESULT (WINAPI *pVarI8FromR8)(double,LONG64*); +static HRESULT (WINAPI *pVarI8FromCy)(CY,LONG64*); +static HRESULT (WINAPI *pVarI8FromDate)(DATE,LONG64*); +static HRESULT (WINAPI *pVarI8FromStr)(OLECHAR*,LCID,ULONG,LONG64*); +static HRESULT (WINAPI *pVarI8FromBool)(VARIANT_BOOL,LONG64*); +static HRESULT (WINAPI *pVarI8FromI1)(signed char,LONG64*); +static HRESULT (WINAPI *pVarI8FromUI2)(USHORT,LONG64*); +static HRESULT (WINAPI *pVarI8FromUI4)(ULONG,LONG64*); +static HRESULT (WINAPI *pVarI8FromDec)(DECIMAL*,LONG64*); +static HRESULT (WINAPI *pVarI8FromUI8)(ULONG64,LONG64*); +static HRESULT (WINAPI *pVarUI8FromI8)(LONG64,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromUI1)(BYTE,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromI2)(SHORT,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromR4)(FLOAT,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromR8)(double,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromCy)(CY,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromDate)(DATE,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromStr)(OLECHAR*,LCID,ULONG,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromBool)(VARIANT_BOOL,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromI1)(signed char,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromUI2)(USHORT,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromUI4)(ULONG,ULONG64*); +static HRESULT (WINAPI *pVarUI8FromDec)(DECIMAL*,ULONG64*); + +static HRESULT (WINAPI *pVarR4FromUI1)(BYTE,float*); +static HRESULT (WINAPI *pVarR4FromI2)(SHORT,float*); +static HRESULT (WINAPI *pVarR4FromI4)(LONG,float*); +static HRESULT (WINAPI *pVarR4FromR8)(double,float*); +static HRESULT (WINAPI *pVarR4FromCy)(CY,float*); +static HRESULT (WINAPI *pVarR4FromDate)(DATE,float*); +static HRESULT (WINAPI *pVarR4FromStr)(OLECHAR*,LCID,ULONG,float*); +static HRESULT (WINAPI *pVarR4FromBool)(VARIANT_BOOL,float*); +static HRESULT (WINAPI *pVarR4FromI1)(signed char,float*); +static HRESULT (WINAPI *pVarR4FromUI2)(USHORT,float*); +static HRESULT (WINAPI *pVarR4FromUI4)(ULONG,float*); +static HRESULT (WINAPI *pVarR4FromDec)(DECIMAL*,float*); +static HRESULT (WINAPI *pVarR4FromI8)(LONG64,float*); +static HRESULT (WINAPI *pVarR4FromUI8)(ULONG64,float*); + +static HRESULT (WINAPI *pVarR8FromUI1)(BYTE,double*); +static HRESULT (WINAPI *pVarR8FromI2)(SHORT,double*); +static HRESULT (WINAPI *pVarR8FromI4)(LONG,double*); +static HRESULT (WINAPI *pVarR8FromR4)(FLOAT,double*); +static HRESULT (WINAPI *pVarR8FromCy)(CY,double*); +static HRESULT (WINAPI *pVarR8FromDate)(DATE,double*); +static HRESULT (WINAPI *pVarR8FromStr)(OLECHAR*,LCID,ULONG,double*); +static HRESULT (WINAPI *pVarR8FromBool)(VARIANT_BOOL,double*); +static HRESULT (WINAPI *pVarR8FromI1)(signed char,double*); +static HRESULT (WINAPI *pVarR8FromUI2)(USHORT,double*); +static HRESULT (WINAPI *pVarR8FromUI4)(ULONG,double*); +static HRESULT (WINAPI *pVarR8FromDec)(DECIMAL*,double*); +static HRESULT (WINAPI *pVarR8FromI8)(LONG64,double*); +static HRESULT (WINAPI *pVarR8FromUI8)(ULONG64,double*); +static HRESULT (WINAPI *pVarR8Round)(double,int,double*); + +static HRESULT (WINAPI *pVarDateFromUI1)(BYTE,DATE*); +static HRESULT (WINAPI *pVarDateFromI2)(SHORT,DATE*); +static HRESULT (WINAPI *pVarDateFromI4)(LONG,DATE*); +static HRESULT (WINAPI *pVarDateFromR4)(FLOAT,DATE*); +static HRESULT (WINAPI *pVarDateFromCy)(CY,DATE*); +static HRESULT (WINAPI *pVarDateFromR8)(double,DATE*); +static HRESULT (WINAPI *pVarDateFromStr)(OLECHAR*,LCID,ULONG,DATE*); +static HRESULT (WINAPI *pVarDateFromBool)(VARIANT_BOOL,DATE*); +static HRESULT (WINAPI *pVarDateFromI1)(signed char,DATE*); +static HRESULT (WINAPI *pVarDateFromUI2)(USHORT,DATE*); +static HRESULT (WINAPI *pVarDateFromUI4)(ULONG,DATE*); +static HRESULT (WINAPI *pVarDateFromDec)(DECIMAL*,DATE*); +static HRESULT (WINAPI *pVarDateFromI8)(LONG64,DATE*); +static HRESULT (WINAPI *pVarDateFromUI8)(ULONG64,DATE*); + +static HRESULT (WINAPI *pVarCyFromUI1)(BYTE,CY*); +static HRESULT (WINAPI *pVarCyFromI2)(SHORT,CY*); +static HRESULT (WINAPI *pVarCyFromI4)(LONG,CY*); +static HRESULT (WINAPI *pVarCyFromR4)(FLOAT,CY*); +static HRESULT (WINAPI *pVarCyFromR8)(double,CY*); +static HRESULT (WINAPI *pVarCyFromDate)(DATE,CY*); +static HRESULT (WINAPI *pVarCyFromBool)(VARIANT_BOOL,CY*); +static HRESULT (WINAPI *pVarCyFromI1)(signed char,CY*); +static HRESULT (WINAPI *pVarCyFromUI2)(USHORT,CY*); +static HRESULT (WINAPI *pVarCyFromUI4)(ULONG,CY*); +static HRESULT (WINAPI *pVarCyFromDec)(DECIMAL*,CY*); +static HRESULT (WINAPI *pVarCyFromI8)(LONG64,CY*); +static HRESULT (WINAPI *pVarCyFromUI8)(ULONG64,CY*); +static HRESULT (WINAPI *pVarCyAdd)(const CY,const CY,CY*); +static HRESULT (WINAPI *pVarCyMul)(const CY,const CY,CY*); +static HRESULT (WINAPI *pVarCyMulI4)(const CY,LONG,CY*); +static HRESULT (WINAPI *pVarCySub)(const CY,const CY,CY*); +static HRESULT (WINAPI *pVarCyAbs)(const CY,CY*); +static HRESULT (WINAPI *pVarCyFix)(const CY,CY*); +static HRESULT (WINAPI *pVarCyInt)(const CY,CY*); +static HRESULT (WINAPI *pVarCyNeg)(const CY,CY*); +static HRESULT (WINAPI *pVarCyRound)(const CY,int,CY*); +static HRESULT (WINAPI *pVarCyCmp)(const CY,const CY); +static HRESULT (WINAPI *pVarCyCmpR8)(const CY,double); +static HRESULT (WINAPI *pVarCyMulI8)(const CY,LONG64,CY*); + +static HRESULT (WINAPI *pVarDecFromUI1)(BYTE,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromI2)(SHORT,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromI4)(LONG,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromI8)(LONG64,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromR4)(FLOAT,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromR8)(DOUBLE,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromDate)(DATE,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromStr)(OLECHAR*,LCID,ULONG,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromBool)(VARIANT_BOOL,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromI1)(signed char,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromUI2)(USHORT,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromUI4)(ULONG,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromUI8)(ULONG64,DECIMAL*); +static HRESULT (WINAPI *pVarDecFromCy)(CY,DECIMAL*); +static HRESULT (WINAPI *pVarDecAbs)(const DECIMAL*,DECIMAL*); +static HRESULT (WINAPI *pVarDecAdd)(const DECIMAL*,const DECIMAL*,DECIMAL*); +static HRESULT (WINAPI *pVarDecSub)(const DECIMAL*,const DECIMAL*,DECIMAL*); +static HRESULT (WINAPI *pVarDecMul)(const DECIMAL*,const DECIMAL*,DECIMAL*); +static HRESULT (WINAPI *pVarDecDiv)(const DECIMAL*,const DECIMAL*,DECIMAL*); +static HRESULT (WINAPI *pVarDecCmp)(const DECIMAL*,const DECIMAL*); +static HRESULT (WINAPI *pVarDecNeg)(const DECIMAL*,DECIMAL*); + +static HRESULT (WINAPI *pVarBoolFromUI1)(BYTE,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromI2)(SHORT,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromI4)(LONG,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromR4)(FLOAT,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromR8)(DOUBLE,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromDate)(DATE,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromCy)(CY,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromStr)(OLECHAR*,LCID,ULONG,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromI1)(signed char,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromUI2)(USHORT,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromUI4)(ULONG,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromDec)(DECIMAL*,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromI8)(LONG64,VARIANT_BOOL*); +static HRESULT (WINAPI *pVarBoolFromUI8)(ULONG64,VARIANT_BOOL*); + +static HRESULT (WINAPI *pVarBstrFromR4)(FLOAT,LCID,ULONG,BSTR*); +static HRESULT (WINAPI *pVarBstrFromDate)(DATE,LCID,ULONG,BSTR*); +static HRESULT (WINAPI *pVarBstrFromDec)(DECIMAL*,LCID,ULONG,BSTR*); +static HRESULT (WINAPI *pVarBstrCmp)(BSTR,BSTR,LCID,ULONG); + +static INT (WINAPI *pSystemTimeToVariantTime)(LPSYSTEMTIME,double*); +static void (WINAPI *pClearCustData)(LPCUSTDATA); + +/* Internal representation of a BSTR */ +typedef struct tagINTERNAL_BSTR +{ + DWORD dwLen; + OLECHAR szString[1]; +} INTERNAL_BSTR, *LPINTERNAL_BSTR; + +typedef struct +{ + const IDispatchVtbl *lpVtbl; + LONG ref; + VARTYPE vt; + BOOL bFailInvoke; +} DummyDispatch; + +static DummyDispatch dispatch; + +static ULONG WINAPI DummyDispatch_AddRef(LPDISPATCH iface) +{ + trace("AddRef(%p)\n", iface); + return InterlockedIncrement(&((DummyDispatch*)iface)->ref); +} + +static ULONG WINAPI DummyDispatch_Release(LPDISPATCH iface) +{ + trace("Release(%p)\n", iface); + return InterlockedDecrement(&((DummyDispatch*)iface)->ref); +} + +static HRESULT WINAPI DummyDispatch_QueryInterface(LPDISPATCH iface, + REFIID riid, + void** ppvObject) +{ + trace("QueryInterface(%p)\n", iface); + if (ppvObject) + { + *ppvObject = NULL; + if (IsEqualIID(riid, &IID_IDispatch)) + { + trace("Asked for IID_IDispatch\n"); + *ppvObject = iface; + } + else if (IsEqualIID(riid, &IID_IUnknown)) + { + trace("Asked for IID_IUnknown\n"); + *ppvObject = iface; + } + if (*ppvObject) + { + DummyDispatch_AddRef((IDispatch*)*ppvObject); + return S_OK; + } + } + return E_NOINTERFACE; +} + +static HRESULT WINAPI DummyDispatch_Invoke(LPDISPATCH iface, + DISPID dispIdMember, REFIID riid, + LCID lcid, WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + trace("Invoke(%p)\n", iface); + ok(wFlags == DISPATCH_PROPERTYGET, "Flags wrong\n"); + ok(pDispParams->cArgs == 0, "Property get has args\n"); + + if (dispatch.bFailInvoke) + return E_OUTOFMEMORY; + + memset(pVarResult, 0, sizeof(*pVarResult)); + V_VT(pVarResult) = dispatch.vt; + return S_OK; +} + +static const IDispatchVtbl DummyDispatch_VTable = +{ + DummyDispatch_QueryInterface, + DummyDispatch_AddRef, + DummyDispatch_Release, + NULL, + NULL, + NULL, + DummyDispatch_Invoke +}; + +static DummyDispatch dispatch = { &DummyDispatch_VTable, 1, 0, 0 }; + +/* + * VT_I1/VT_UI1 + */ + +#undef CONV_TYPE +#define CONV_TYPE signed char +#undef EXPECTRES +#define EXPECTRES(res, x) _EXPECTRES(res, x, "%d") + +static void test_VarI1FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarI1FromI2); + OVERFLOWRANGE(VarI1FromI2, -32768, -128); + CONVERTRANGE(VarI1FromI2, -128, 128); + OVERFLOWRANGE(VarI1FromI2, 129, 32768); +} + +static void test_VarI1FromI4(void) +{ + CONVVARS(LONG); + int i; + + CHECKPTR(VarI1FromI4); + CONVERT(VarI1FromI4, -129); EXPECT_OVERFLOW; + CONVERTRANGE(VarI1FromI4, -128, 128); + CONVERT(VarI1FromI4, 128); EXPECT_OVERFLOW; +} + +static void test_VarI1FromI8(void) +{ + CONVVARS(LONG64); + int i; + + CHECKPTR(VarI1FromI8); + CONVERT(VarI1FromI8, -129); EXPECT_OVERFLOW; + CONVERTRANGE(VarI1FromI8, -127, 128); + CONVERT(VarI1FromI8, 128); EXPECT_OVERFLOW; +} + +static void test_VarI1FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarI1FromUI1); + CONVERTRANGE(VarI1FromUI1, 0, 127); + OVERFLOWRANGE(VarI1FromUI1, 128, 255); +} + +static void test_VarI1FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarI1FromUI2); + CONVERTRANGE(VarI1FromUI2, 0, 127); + OVERFLOWRANGE(VarI1FromUI2, 128, 32768); +} + +static void test_VarI1FromUI4(void) +{ + CONVVARS(ULONG); + int i; + + CHECKPTR(VarI1FromUI4); + CONVERTRANGE(VarI1FromUI4, 0, 127); + CONVERT(VarI1FromUI4, 128); EXPECT_OVERFLOW; +} + +static void test_VarI1FromUI8(void) +{ + CONVVARS(ULONG64); + int i; + + CHECKPTR(VarI1FromUI8); + CONVERTRANGE(VarI1FromUI8, 0, 127); + CONVERT(VarI1FromUI8, 128); EXPECT_OVERFLOW; +} + +static void test_VarI1FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarI1FromBool); + /* Note that conversions from bool wrap around! */ + CONVERT(VarI1FromBool, -129); EXPECT(127); + CONVERTRANGE(VarI1FromBool, -128, 128); + CONVERT(VarI1FromBool, 128); EXPECT(-128); +} + +static void test_VarI1FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarI1FromR4); + CONVERT(VarI1FromR4, -129.0f); EXPECT_OVERFLOW; + CONVERT(VarI1FromR4, -128.0f); EXPECT(-128); + CONVERT(VarI1FromR4, -1.0f); EXPECT(-1); + CONVERT(VarI1FromR4, 0.0f); EXPECT(0); + CONVERT(VarI1FromR4, 1.0f); EXPECT(1); + CONVERT(VarI1FromR4, 127.0f); EXPECT(127); + CONVERT(VarI1FromR4, 128.0f); EXPECT_OVERFLOW; + + CONVERT(VarI1FromR4, -1.5f); EXPECT(-2); + CONVERT(VarI1FromR4, -0.6f); EXPECT(-1); + CONVERT(VarI1FromR4, -0.5f); EXPECT(0); + CONVERT(VarI1FromR4, -0.4f); EXPECT(0); + CONVERT(VarI1FromR4, 0.4f); EXPECT(0); + CONVERT(VarI1FromR4, 0.5f); EXPECT(0); + CONVERT(VarI1FromR4, 0.6f); EXPECT(1); + CONVERT(VarI1FromR4, 1.5f); EXPECT(2); +} + +static void test_VarI1FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarI1FromR8); + CONVERT(VarI1FromR8, -129.0); EXPECT_OVERFLOW; + CONVERT(VarI1FromR8, -128.0); EXPECT(-128); + CONVERT(VarI1FromR8, -1.0); EXPECT(-1); + CONVERT(VarI1FromR8, 0.0); EXPECT(0); + CONVERT(VarI1FromR8, 1.0); EXPECT(1); + CONVERT(VarI1FromR8, 127.0); EXPECT(127); + CONVERT(VarI1FromR8, 128.0); EXPECT_OVERFLOW; + + CONVERT(VarI1FromR8, -1.5); EXPECT(-2); + CONVERT(VarI1FromR8, -0.6); EXPECT(-1); + CONVERT(VarI1FromR8, -0.5); EXPECT(0); + CONVERT(VarI1FromR8, -0.4); EXPECT(0); + CONVERT(VarI1FromR8, 0.4); EXPECT(0); + CONVERT(VarI1FromR8, 0.5); EXPECT(0); + CONVERT(VarI1FromR8, 0.6); EXPECT(1); + CONVERT(VarI1FromR8, 1.5); EXPECT(2); +} + +static void test_VarI1FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarI1FromDate); + CONVERT(VarI1FromDate, -129.0); EXPECT_OVERFLOW; + CONVERT(VarI1FromDate, -128.0); EXPECT(-128); + CONVERT(VarI1FromDate, -1.0); EXPECT(-1); + CONVERT(VarI1FromDate, 0.0); EXPECT(0); + CONVERT(VarI1FromDate, 1.0); EXPECT(1); + CONVERT(VarI1FromDate, 127.0); EXPECT(127); + CONVERT(VarI1FromDate, 128.0); EXPECT_OVERFLOW; + + CONVERT(VarI1FromDate, -1.5); EXPECT(-2); + CONVERT(VarI1FromDate, -0.6); EXPECT(-1); + CONVERT(VarI1FromDate, -0.5); EXPECT(0); + CONVERT(VarI1FromDate, -0.4); EXPECT(0); + CONVERT(VarI1FromDate, 0.4); EXPECT(0); + CONVERT(VarI1FromDate, 0.5); EXPECT(0); + CONVERT(VarI1FromDate, 0.6); EXPECT(1); + CONVERT(VarI1FromDate, 1.5); EXPECT(2); +} + +static void test_VarI1FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarI1FromCy); + CONVERT_CY(VarI1FromCy,-129); EXPECT_OVERFLOW; + CONVERT_CY(VarI1FromCy,-128); EXPECT(128); + CONVERT_CY(VarI1FromCy,-1); EXPECT(-1); + CONVERT_CY(VarI1FromCy,0); EXPECT(0); + CONVERT_CY(VarI1FromCy,1); EXPECT(1); + CONVERT_CY(VarI1FromCy,127); EXPECT(127); + CONVERT_CY(VarI1FromCy,128); EXPECT_OVERFLOW; + + CONVERT_CY(VarI1FromCy,-1.5); EXPECT(-2); + CONVERT_CY(VarI1FromCy,-0.6); EXPECT(-1); + CONVERT_CY(VarI1FromCy,-0.5); EXPECT(0); + CONVERT_CY(VarI1FromCy,-0.4); EXPECT(0); + CONVERT_CY(VarI1FromCy,0.4); EXPECT(0); + CONVERT_CY(VarI1FromCy,0.5); EXPECT(0); + CONVERT_CY(VarI1FromCy,0.6); EXPECT(1); + CONVERT_CY(VarI1FromCy,1.5); EXPECT(2); +} + +static void test_VarI1FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarI1FromDec); + + CONVERT_BADDEC(VarI1FromDec); + + CONVERT_DEC(VarI1FromDec,0,0x80,0,129); EXPECT_OVERFLOW; + CONVERT_DEC(VarI1FromDec,0,0x80,0,128); EXPECT(-128); + CONVERT_DEC(VarI1FromDec,0,0x80,0,1); EXPECT(-1); + CONVERT_DEC(VarI1FromDec,0,0,0,0); EXPECT(0); + CONVERT_DEC(VarI1FromDec,0,0,0,1); EXPECT(1); + CONVERT_DEC(VarI1FromDec,0,0,0,127); EXPECT(127); + CONVERT_DEC(VarI1FromDec,0,0,0,128); EXPECT_OVERFLOW; + + CONVERT_DEC(VarI1FromDec,2,0x80,0,12800); EXPECT(-128); + CONVERT_DEC(VarI1FromDec,2,0,0,12700); EXPECT(127); +} + +static void test_VarI1FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarI1FromStr); + + CONVERT_STR(VarI1FromStr,NULL, 0); EXPECT_MISMATCH; + CONVERT_STR(VarI1FromStr,"0", 0); EXPECT(0); + CONVERT_STR(VarI1FromStr,"-129", 0); EXPECT_OVERFLOW; + CONVERT_STR(VarI1FromStr,"-128", 0); EXPECT(-128); + CONVERT_STR(VarI1FromStr,"127", 0); EXPECT(127); + CONVERT_STR(VarI1FromStr,"128", 0); EXPECT_OVERFLOW; + + CONVERT_STR(VarI1FromStr,"-1.5", LOCALE_NOUSEROVERRIDE); EXPECT(-2); + CONVERT_STR(VarI1FromStr,"-0.6", LOCALE_NOUSEROVERRIDE); EXPECT(-1); + CONVERT_STR(VarI1FromStr,"-0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI1FromStr,"-0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI1FromStr,"0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI1FromStr,"0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI1FromStr,"0.6", LOCALE_NOUSEROVERRIDE); EXPECT(1); + CONVERT_STR(VarI1FromStr,"1.5", LOCALE_NOUSEROVERRIDE); EXPECT(2); +} + +static void test_VarI1Copy(void) +{ + if (!IS_ANCIENT) + { + COPYTEST(1, VT_I1, V_I1(&vSrc), V_I1(&vDst), V_I1REF(&vSrc), V_I1REF(&vDst), "%d"); + } +} + +static void test_VarI1ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1; + + if (!IS_ANCIENT) + { + INITIAL_TYPETEST(VT_I1, V_I1, "%d"); + COMMON_TYPETEST; + NEGATIVE_TYPETEST(VT_I1, V_I1, "%d", VT_UI1, V_UI1); + } +} + +#undef CONV_TYPE +#define CONV_TYPE BYTE + +static void test_VarUI1FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarUI1FromI1); + OVERFLOWRANGE(VarUI1FromI1, -128, 0); + CONVERTRANGE(VarUI1FromI1, 0, 128); +} + +static void test_VarUI1FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarUI1FromI2); + OVERFLOWRANGE(VarUI1FromI2, -32768, 0); + CONVERTRANGE(VarUI1FromI2, 0, 256); + OVERFLOWRANGE(VarUI1FromI2, 256, 32768); +} + +static void test_VarUI1FromI4(void) +{ + CONVVARS(LONG); + int i; + + CHECKPTR(VarUI1FromI4); + CONVERT(VarUI1FromI4, -1); EXPECT_OVERFLOW; + CONVERTRANGE(VarUI1FromI4, 0, 256); + CONVERT(VarUI1FromI4, 256); EXPECT_OVERFLOW; +} + +static void test_VarUI1FromI8(void) +{ + CONVVARS(LONG64); + int i; + + CHECKPTR(VarUI1FromI8); + CONVERT(VarUI1FromI8, -1); EXPECT_OVERFLOW; + CONVERTRANGE(VarUI1FromI8, 0, 256); + CONVERT(VarUI1FromI8, 256); EXPECT_OVERFLOW; +} + +static void test_VarUI1FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarUI1FromUI2); + CONVERTRANGE(VarUI1FromUI2, 0, 256); + OVERFLOWRANGE(VarUI1FromUI2, 256, 65536); +} + +static void test_VarUI1FromUI4(void) +{ + CONVVARS(ULONG); + int i; + + CHECKPTR(VarUI1FromUI4); + CONVERTRANGE(VarUI1FromUI4, 0, 256); + CONVERT(VarUI1FromUI4, 256); EXPECT_OVERFLOW; +} + +static void test_VarUI1FromUI8(void) +{ + CONVVARS(ULONG64); + int i; + + CHECKPTR(VarUI1FromUI8); + CONVERTRANGE(VarUI1FromUI8, 0, 256); + CONVERT(VarUI1FromUI8, 256); EXPECT_OVERFLOW; +} + +static void test_VarUI1FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarUI1FromBool); + /* Note that conversions from bool overflow! */ + CONVERT(VarUI1FromBool, -1); EXPECT(255); + CONVERTRANGE(VarUI1FromBool, 0, 256); + CONVERT(VarUI1FromBool, 256); EXPECT(0); +} + +static void test_VarUI1FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarUI1FromR4); + CONVERT(VarUI1FromR4, -1.0f); EXPECT_OVERFLOW; + CONVERT(VarUI1FromR4, 0.0f); EXPECT(0); + CONVERT(VarUI1FromR4, 1.0f); EXPECT(1); + CONVERT(VarUI1FromR4, 255.0f); EXPECT(255); + CONVERT(VarUI1FromR4, 256.0f); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarUI1FromR4, -1.5f); EXPECT_OVERFLOW; + CONVERT(VarUI1FromR4, -0.6f); EXPECT_OVERFLOW; + CONVERT(VarUI1FromR4, -0.5f); EXPECT(0); + CONVERT(VarUI1FromR4, -0.4f); EXPECT(0); + CONVERT(VarUI1FromR4, 0.4f); EXPECT(0); + CONVERT(VarUI1FromR4, 0.5f); EXPECT(0); + CONVERT(VarUI1FromR4, 0.6f); EXPECT(1); + CONVERT(VarUI1FromR4, 1.5f); EXPECT(2); +} + +static void test_VarUI1FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarUI1FromR8); + CONVERT(VarUI1FromR8, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI1FromR8, 0.0); EXPECT(0); + CONVERT(VarUI1FromR8, 1.0); EXPECT(1); + CONVERT(VarUI1FromR8, 255.0); EXPECT(255); + CONVERT(VarUI1FromR8, 256.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarUI1FromR8, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI1FromR8, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI1FromR8, -0.5); EXPECT(0); + CONVERT(VarUI1FromR8, -0.4); EXPECT(0); + CONVERT(VarUI1FromR8, 0.4); EXPECT(0); + CONVERT(VarUI1FromR8, 0.5); EXPECT(0); + CONVERT(VarUI1FromR8, 0.6); EXPECT(1); + CONVERT(VarUI1FromR8, 1.5); EXPECT(2); +} + +static void test_VarUI1FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarUI1FromDate); + CONVERT(VarUI1FromDate, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI1FromDate, 0.0); EXPECT(0); + CONVERT(VarUI1FromDate, 1.0); EXPECT(1); + CONVERT(VarUI1FromDate, 255.0); EXPECT(255); + CONVERT(VarUI1FromDate, 256.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarUI1FromDate, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI1FromDate, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI1FromDate, -0.5); EXPECT(0); + CONVERT(VarUI1FromDate, -0.4); EXPECT(0); + CONVERT(VarUI1FromDate, 0.4); EXPECT(0); + CONVERT(VarUI1FromDate, 0.5); EXPECT(0); + CONVERT(VarUI1FromDate, 0.6); EXPECT(1); + CONVERT(VarUI1FromDate, 1.5); EXPECT(2); +} + +static void test_VarUI1FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarUI1FromCy); + CONVERT_CY(VarUI1FromCy,-1); EXPECT_OVERFLOW; + CONVERT_CY(VarUI1FromCy,0); EXPECT(0); + CONVERT_CY(VarUI1FromCy,1); EXPECT(1); + CONVERT_CY(VarUI1FromCy,255); EXPECT(255); + CONVERT_CY(VarUI1FromCy,256); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_CY(VarUI1FromCy,-1.5); EXPECT_OVERFLOW; + CONVERT_CY(VarUI1FromCy,-0.6); EXPECT_OVERFLOW; + CONVERT_CY(VarUI1FromCy,-0.5); EXPECT(0); + CONVERT_CY(VarUI1FromCy,-0.4); EXPECT(0); + CONVERT_CY(VarUI1FromCy,0.4); EXPECT(0); + CONVERT_CY(VarUI1FromCy,0.5); EXPECT(0); + CONVERT_CY(VarUI1FromCy,0.6); EXPECT(1); + CONVERT_CY(VarUI1FromCy,1.5); EXPECT(2); +} + +static void test_VarUI1FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarUI1FromDec); + + CONVERT_BADDEC(VarUI1FromDec); + + CONVERT_DEC(VarUI1FromDec,0,0x80,0,1); EXPECT_OVERFLOW; + CONVERT_DEC(VarUI1FromDec,0,0,0,0); EXPECT(0); + CONVERT_DEC(VarUI1FromDec,0,0,0,1); EXPECT(1); + CONVERT_DEC(VarUI1FromDec,0,0,0,255); EXPECT(255); + CONVERT_DEC(VarUI1FromDec,0,0,0,256); EXPECT_OVERFLOW; + + CONVERT_DEC(VarUI1FromDec,2,0x80,0,100); EXPECT_OVERFLOW; + CONVERT_DEC(VarUI1FromDec,2,0,0,25500); EXPECT(255); +} + +static void test_VarUI1FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarUI1FromStr); + + CONVERT_STR(VarUI1FromStr,NULL, 0); EXPECT_MISMATCH; + CONVERT_STR(VarUI1FromStr,"0", 0); EXPECT(0); + CONVERT_STR(VarUI1FromStr,"-1", 0); EXPECT_OVERFLOW; + CONVERT_STR(VarUI1FromStr,"255", 0); EXPECT(255); + CONVERT_STR(VarUI1FromStr,"256", 0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_STR(VarUI1FromStr,"-1.5", LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI1FromStr,"-0.6", LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI1FromStr,"-0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI1FromStr,"-0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI1FromStr,"0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI1FromStr,"0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI1FromStr,"0.6", LOCALE_NOUSEROVERRIDE); EXPECT(1); + CONVERT_STR(VarUI1FromStr,"1.5", LOCALE_NOUSEROVERRIDE); EXPECT(2); +} + +static void test_VarUI1FromDisp(void) +{ + CONVVARS(LCID); + VARIANTARG vSrc, vDst; + + CHECKPTR(VarUI1FromDisp); + + /* FIXME + * Conversions from IDispatch should get the default 'value' property + * from the IDispatch pointer and return it. The following tests this. + * However, I can't get these tests to return a valid value under native + * oleaut32, regardless of the value returned in response to the Invoke() + * call (early versions of oleaut32 call AddRef/Release, but not Invoke. + * I'm obviously missing something, as these conversions work fine + * when called through VBA on an object to get its default value property. + * + * Should this test be corrected so that it works under native it should be + * generalised and the remaining types checked as well. + */ + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + VariantInit(&vSrc); + VariantInit(&vDst); + + V_VT(&vSrc) = VT_DISPATCH; + V_DISPATCH(&vSrc) = (IDispatch*)&dispatch; + dispatch.vt = VT_UI1; + dispatch.bFailInvoke = FALSE; + + hres = VarUI1FromDisp((IDispatch*)&dispatch, in, &out); + trace("0x%08x\n", hres); + + hres = VariantChangeTypeEx(&vDst, &vSrc, in, 0, VT_UI1); + trace("0x%08x\n", hres); + + dispatch.bFailInvoke = TRUE; + + hres = VarUI1FromDisp((IDispatch*)&dispatch, in, &out); + trace("0x%08x\n", hres); + + hres = VariantChangeTypeEx(&vDst, &vSrc, in, 0, VT_UI1); + trace("0x%08x\n", hres); +} + +static void test_VarUI1Copy(void) +{ + COPYTEST(1, VT_UI1, V_UI1(&vSrc), V_UI1(&vDst), V_UI1REF(&vSrc), V_UI1REF(&vDst), "%d"); +} + +static void test_VarUI1ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1; + + INITIAL_TYPETEST(VT_UI1, V_UI1, "%d"); + COMMON_TYPETEST; + NEGATIVE_TYPETEST(VT_UI1, V_UI1, "%d", VT_I1, V_I1); +} + +/* + * VT_I2/VT_UI2 + */ + +#undef CONV_TYPE +#define CONV_TYPE SHORT +#undef EXPECTRES +#define EXPECTRES(res, x) _EXPECTRES(res, x, "%d") + +static void test_VarI2FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarI2FromI1); + CONVERTRANGE(VarI2FromI1, -128, 128); +} + +static void test_VarI2FromI4(void) +{ + CONVVARS(LONG); + int i; + + CHECKPTR(VarI2FromI4); + CONVERT(VarI2FromI4, -32769); EXPECT_OVERFLOW; + CONVERTRANGE(VarI2FromI4, -32768, 32768); + CONVERT(VarI2FromI4, 32768); EXPECT_OVERFLOW; +} + +static void test_VarI2FromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarI2FromI8); + CONVERT(VarI2FromI8, -32769); EXPECT_OVERFLOW; + CONVERT(VarI2FromI8, -32768); EXPECT(-32768); + CONVERT(VarI2FromI8, 32767); EXPECT(32767); + CONVERT(VarI2FromI8, 32768); EXPECT_OVERFLOW; +} + +static void test_VarI2FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarI2FromUI1); + CONVERTRANGE(VarI2FromUI1, 0, 256); +} + +static void test_VarI2FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarI2FromUI2); + CONVERTRANGE(VarI2FromUI2, 0, 32768); + CONVERT(VarI2FromUI2, 32768); EXPECT_OVERFLOW; +} + +static void test_VarI2FromUI4(void) +{ + CONVVARS(ULONG); + int i; + + CHECKPTR(VarI2FromUI4); + CONVERTRANGE(VarI2FromUI4, 0, 32768); + CONVERT(VarI2FromUI4, 32768); EXPECT_OVERFLOW; +} + +static void test_VarI2FromUI8(void) +{ + CONVVARS(ULONG64); + int i; + + CHECKPTR(VarI2FromUI8); + CONVERTRANGE(VarI2FromUI8, 0, 32768); + CONVERT(VarI2FromUI8, 32768); EXPECT_OVERFLOW; +} + +static void test_VarI2FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarI2FromBool); + CONVERTRANGE(VarI2FromBool, -32768, 32768); +} + +static void test_VarI2FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarI2FromR4); + CONVERT(VarI2FromR4, -32769.0f); EXPECT_OVERFLOW; + CONVERT(VarI2FromR4, -32768.0f); EXPECT(-32768); + CONVERT(VarI2FromR4, -1.0f); EXPECT(-1); + CONVERT(VarI2FromR4, 0.0f); EXPECT(0); + CONVERT(VarI2FromR4, 1.0f); EXPECT(1); + CONVERT(VarI2FromR4, 32767.0f); EXPECT(32767); + CONVERT(VarI2FromR4, 32768.0f); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarI2FromR4, -1.5f); EXPECT(-2); + CONVERT(VarI2FromR4, -0.6f); EXPECT(-1); + CONVERT(VarI2FromR4, -0.5f); EXPECT(0); + CONVERT(VarI2FromR4, -0.4f); EXPECT(0); + CONVERT(VarI2FromR4, 0.4f); EXPECT(0); + CONVERT(VarI2FromR4, 0.5f); EXPECT(0); + CONVERT(VarI2FromR4, 0.6f); EXPECT(1); + CONVERT(VarI2FromR4, 1.5f); EXPECT(2); +} + +static void test_VarI2FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarI2FromR8); + CONVERT(VarI2FromR8, -32769.0); EXPECT_OVERFLOW; + CONVERT(VarI2FromR8, -32768.0); EXPECT(-32768); + CONVERT(VarI2FromR8, -1.0); EXPECT(-1); + CONVERT(VarI2FromR8, 0.0); EXPECT(0); + CONVERT(VarI2FromR8, 1.0); EXPECT(1); + CONVERT(VarI2FromR8, 32767.0); EXPECT(32767); + CONVERT(VarI2FromR8, 32768.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarI2FromR8, -1.5); EXPECT(-2); + CONVERT(VarI2FromR8, -0.6); EXPECT(-1); + CONVERT(VarI2FromR8, -0.5); EXPECT(0); + CONVERT(VarI2FromR8, -0.4); EXPECT(0); + CONVERT(VarI2FromR8, 0.4); EXPECT(0); + CONVERT(VarI2FromR8, 0.5); EXPECT(0); + CONVERT(VarI2FromR8, 0.6); EXPECT(1); + CONVERT(VarI2FromR8, 1.5); EXPECT(2); +} + +static void test_VarI2FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarI2FromDate); + CONVERT(VarI2FromDate, -32769.0); EXPECT_OVERFLOW; + CONVERT(VarI2FromDate, -32768.0); EXPECT(-32768); + CONVERT(VarI2FromDate, -1.0); EXPECT(-1); + CONVERT(VarI2FromDate, 0.0); EXPECT(0); + CONVERT(VarI2FromDate, 1.0); EXPECT(1); + CONVERT(VarI2FromDate, 32767.0); EXPECT(32767); + CONVERT(VarI2FromDate, 32768.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarI2FromDate, -1.5); EXPECT(-2); + CONVERT(VarI2FromDate, -0.6); EXPECT(-1); + CONVERT(VarI2FromDate, -0.5); EXPECT(0); + CONVERT(VarI2FromDate, -0.4); EXPECT(0); + CONVERT(VarI2FromDate, 0.4); EXPECT(0); + CONVERT(VarI2FromDate, 0.5); EXPECT(0); + CONVERT(VarI2FromDate, 0.6); EXPECT(1); + CONVERT(VarI2FromDate, 1.5); EXPECT(2); +} + +static void test_VarI2FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarI2FromCy); + CONVERT_CY(VarI2FromCy,-32769); EXPECT_OVERFLOW; + CONVERT_CY(VarI2FromCy,-32768); EXPECT(32768); + CONVERT_CY(VarI2FromCy,-1); EXPECT(-1); + CONVERT_CY(VarI2FromCy,0); EXPECT(0); + CONVERT_CY(VarI2FromCy,1); EXPECT(1); + CONVERT_CY(VarI2FromCy,32767); EXPECT(32767); + CONVERT_CY(VarI2FromCy,32768); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_CY(VarI2FromCy,-1.5); EXPECT(-2); + CONVERT_CY(VarI2FromCy,-0.6); EXPECT(-1); + CONVERT_CY(VarI2FromCy,-0.5); EXPECT(0); + CONVERT_CY(VarI2FromCy,-0.4); EXPECT(0); + CONVERT_CY(VarI2FromCy,0.4); EXPECT(0); + CONVERT_CY(VarI2FromCy,0.5); EXPECT(0); + CONVERT_CY(VarI2FromCy,0.6); EXPECT(1); + CONVERT_CY(VarI2FromCy,1.5); EXPECT(2); +} + +static void test_VarI2FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarI2FromDec); + + CONVERT_BADDEC(VarI2FromDec); + + CONVERT_DEC(VarI2FromDec,0,0x80,0,32769); EXPECT_OVERFLOW; + CONVERT_DEC(VarI2FromDec,0,0x80,0,32768); EXPECT(-32768); + CONVERT_DEC(VarI2FromDec,0,0x80,0,1); EXPECT(-1); + CONVERT_DEC(VarI2FromDec,0,0,0,0); EXPECT(0); + CONVERT_DEC(VarI2FromDec,0,0,0,1); EXPECT(1); + CONVERT_DEC(VarI2FromDec,0,0,0,32767); EXPECT(32767); + CONVERT_DEC(VarI2FromDec,0,0,0,32768); EXPECT_OVERFLOW; + + CONVERT_DEC(VarI2FromDec,2,0x80,0,3276800); EXPECT(-32768); + CONVERT_DEC(VarI2FromDec,2,0,0,3276700); EXPECT(32767); + CONVERT_DEC(VarI2FromDec,2,0,0,3276800); EXPECT_OVERFLOW; +} + +static void test_VarI2FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarI2FromStr); + + CONVERT_STR(VarI2FromStr,NULL, 0); EXPECT_MISMATCH; + CONVERT_STR(VarI2FromStr,"0", 0); EXPECT(0); + CONVERT_STR(VarI2FromStr,"-32769", 0); EXPECT_OVERFLOW; + CONVERT_STR(VarI2FromStr,"-32768", 0); EXPECT(-32768); + CONVERT_STR(VarI2FromStr,"32767", 0); EXPECT(32767); + CONVERT_STR(VarI2FromStr,"32768", 0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_STR(VarI2FromStr,"-1.5", LOCALE_NOUSEROVERRIDE); EXPECT(-2); + CONVERT_STR(VarI2FromStr,"-0.6", LOCALE_NOUSEROVERRIDE); EXPECT(-1); + CONVERT_STR(VarI2FromStr,"-0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI2FromStr,"-0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI2FromStr,"0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI2FromStr,"0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI2FromStr,"0.6", LOCALE_NOUSEROVERRIDE); EXPECT(1); + CONVERT_STR(VarI2FromStr,"1.5", LOCALE_NOUSEROVERRIDE); EXPECT(2); +} + +static void test_VarI2Copy(void) +{ + COPYTEST(1, VT_I2, V_I2(&vSrc), V_I2(&vDst), V_I2REF(&vSrc), V_I2REF(&vDst), "%d"); +} + +static void test_VarI2ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1; + + INITIAL_TYPETEST(VT_I2, V_I2, "%d"); + COMMON_TYPETEST; + NEGATIVE_TYPETEST(VT_I2, V_I2, "%d", VT_UI2, V_UI2); +} + +#undef CONV_TYPE +#define CONV_TYPE USHORT + +static void test_VarUI2FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarUI2FromI1); + OVERFLOWRANGE(VarUI2FromI1, -128, 0); + CONVERTRANGE(VarUI2FromI1, 0, 128); +} + +static void test_VarUI2FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarUI2FromI2); + OVERFLOWRANGE(VarUI2FromI2, -32768, 0); + CONVERTRANGE(VarUI2FromI2, 0, 32768); +} + +static void test_VarUI2FromI4(void) +{ + CONVVARS(LONG); + int i; + + CHECKPTR(VarUI2FromI4); + OVERFLOWRANGE(VarUI2FromI4, -32768, 0); + CONVERT(VarUI2FromI4, 0); EXPECT(0); + CONVERT(VarUI2FromI4, 65535); EXPECT(65535); + CONVERT(VarUI2FromI4, 65536); EXPECT_OVERFLOW; +} + +static void test_VarUI2FromI8(void) +{ + CONVVARS(LONG64); + int i; + + CHECKPTR(VarUI2FromI8); + OVERFLOWRANGE(VarUI2FromI8, -32768, 0); + CONVERT(VarUI2FromI8, 0); EXPECT(0); + CONVERT(VarUI2FromI8, 65535); EXPECT(65535); + CONVERT(VarUI2FromI8, 65536); EXPECT_OVERFLOW; +} + +static void test_VarUI2FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarUI2FromUI1); + CONVERTRANGE(VarUI2FromUI1, 0, 256); +} + +static void test_VarUI2FromUI4(void) +{ + CONVVARS(ULONG); + + CHECKPTR(VarUI2FromUI4); + CONVERT(VarUI2FromUI4, 0); EXPECT(0); + CONVERT(VarUI2FromUI4, 65535); EXPECT(65535); + CONVERT(VarUI2FromUI4, 65536); EXPECT_OVERFLOW; +} + +static void test_VarUI2FromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarUI2FromUI8); + CONVERT(VarUI2FromUI8, 0); EXPECT(0); + CONVERT(VarUI2FromUI8, 65535); EXPECT(65535); + CONVERT(VarUI2FromUI8, 65536); EXPECT_OVERFLOW; +} + +static void test_VarUI2FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarUI2FromBool); + CONVERT(VarUI2FromBool, -1); EXPECT(65535); /* Wraps! */ + CONVERTRANGE(VarUI2FromBool, 0, 32768); +} + +static void test_VarUI2FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarUI2FromR4); + CONVERT(VarUI2FromR4, -1.0f); EXPECT_OVERFLOW; + CONVERT(VarUI2FromR4, 0.0f); EXPECT(0); + CONVERT(VarUI2FromR4, 1.0f); EXPECT(1); + CONVERT(VarUI2FromR4, 65535.0f); EXPECT(65535); + CONVERT(VarUI2FromR4, 65536.0f); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarUI2FromR4, -1.5f); EXPECT_OVERFLOW; + CONVERT(VarUI2FromR4, -0.6f); EXPECT_OVERFLOW; + CONVERT(VarUI2FromR4, -0.5f); EXPECT(0); + CONVERT(VarUI2FromR4, -0.4f); EXPECT(0); + CONVERT(VarUI2FromR4, 0.4f); EXPECT(0); + CONVERT(VarUI2FromR4, 0.5f); EXPECT(0); + CONVERT(VarUI2FromR4, 0.6f); EXPECT(1); + CONVERT(VarUI2FromR4, 1.5f); EXPECT(2); +} + +static void test_VarUI2FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarUI2FromR8); + CONVERT(VarUI2FromR8, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI2FromR8, 0.0); EXPECT(0); + CONVERT(VarUI2FromR8, 1.0); EXPECT(1); + CONVERT(VarUI2FromR8, 65535.0); EXPECT(65535); + CONVERT(VarUI2FromR8, 65536.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarUI2FromR8, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI2FromR8, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI2FromR8, -0.5); EXPECT(0); + CONVERT(VarUI2FromR8, -0.4); EXPECT(0); + CONVERT(VarUI2FromR8, 0.4); EXPECT(0); + CONVERT(VarUI2FromR8, 0.5); EXPECT(0); + CONVERT(VarUI2FromR8, 0.6); EXPECT(1); + CONVERT(VarUI2FromR8, 1.5); EXPECT(2); +} + +static void test_VarUI2FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarUI2FromDate); + CONVERT(VarUI2FromDate, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI2FromDate, 0.0); EXPECT(0); + CONVERT(VarUI2FromDate, 1.0); EXPECT(1); + CONVERT(VarUI2FromDate, 65535.0); EXPECT(65535); + CONVERT(VarUI2FromDate, 65536.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarUI2FromDate, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI2FromDate, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI2FromDate, -0.5); EXPECT(0); + CONVERT(VarUI2FromDate, -0.4); EXPECT(0); + CONVERT(VarUI2FromDate, 0.4); EXPECT(0); + CONVERT(VarUI2FromDate, 0.5); EXPECT(0); + CONVERT(VarUI2FromDate, 0.6); EXPECT(1); + CONVERT(VarUI2FromDate, 1.5); EXPECT(2); +} + +static void test_VarUI2FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarUI2FromCy); + CONVERT_CY(VarUI2FromCy,-1); EXPECT_OVERFLOW; + CONVERT_CY(VarUI2FromCy,0); EXPECT(0); + CONVERT_CY(VarUI2FromCy,1); EXPECT(1); + CONVERT_CY(VarUI2FromCy,65535); EXPECT(65535); + CONVERT_CY(VarUI2FromCy,65536); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_CY(VarUI2FromCy,-1.5); EXPECT_OVERFLOW; + CONVERT_CY(VarUI2FromCy,-0.6); EXPECT_OVERFLOW; + CONVERT_CY(VarUI2FromCy,-0.5); EXPECT(0); + CONVERT_CY(VarUI2FromCy,-0.4); EXPECT(0); + CONVERT_CY(VarUI2FromCy,0.4); EXPECT(0); + CONVERT_CY(VarUI2FromCy,0.5); EXPECT(0); + CONVERT_CY(VarUI2FromCy,0.6); EXPECT(1); + CONVERT_CY(VarUI2FromCy,1.5); EXPECT(2); +} + +static void test_VarUI2FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarUI2FromDec); + + CONVERT_BADDEC(VarUI2FromDec); + + CONVERT_DEC(VarUI2FromDec,0,0x80,0,1); EXPECT_OVERFLOW; + CONVERT_DEC(VarUI2FromDec,0,0,0,0); EXPECT(0); + CONVERT_DEC(VarUI2FromDec,0,0,0,1); EXPECT(1); + CONVERT_DEC(VarUI2FromDec,0,0,0,65535); EXPECT(65535); + CONVERT_DEC(VarUI2FromDec,0,0,0,65536); EXPECT_OVERFLOW; + + CONVERT_DEC(VarUI2FromDec,2,0x80,0,100); EXPECT_OVERFLOW; + CONVERT_DEC(VarUI2FromDec,2,0,0,6553500); EXPECT(65535); + CONVERT_DEC(VarUI2FromDec,2,0,0,6553600); EXPECT_OVERFLOW; +} + +static void test_VarUI2FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarUI2FromStr); + + CONVERT_STR(VarUI2FromStr,NULL, 0); EXPECT_MISMATCH; + CONVERT_STR(VarUI2FromStr,"0", 0); EXPECT(0); + CONVERT_STR(VarUI2FromStr,"-1", 0); EXPECT_OVERFLOW; + CONVERT_STR(VarUI2FromStr,"65535", 0); EXPECT(65535); + CONVERT_STR(VarUI2FromStr,"65536", 0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_STR(VarUI2FromStr,"-1.5", LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI2FromStr,"-0.6", LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI2FromStr,"-0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI2FromStr,"-0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI2FromStr,"0.4", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI2FromStr,"0.5", LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI2FromStr,"0.6", LOCALE_NOUSEROVERRIDE); EXPECT(1); + CONVERT_STR(VarUI2FromStr,"1.5", LOCALE_NOUSEROVERRIDE); EXPECT(2); +} + +static void test_VarUI2Copy(void) +{ + if (!IS_ANCIENT) + { + COPYTEST(1, VT_UI2, V_UI2(&vSrc), V_UI2(&vDst), V_UI2REF(&vSrc), V_UI2REF(&vDst), "%d"); + } +} + +static void test_VarUI2ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1; + + if (!IS_ANCIENT) + { + INITIAL_TYPETEST(VT_UI2, V_UI2, "%d"); + COMMON_TYPETEST; + NEGATIVE_TYPETEST(VT_UI2, V_UI2, "%d", VT_I2, V_I2); + } +} + +/* + * VT_I4/VT_UI4 + */ + +#undef CONV_TYPE +#define CONV_TYPE LONG +#undef EXPECTRES +#define EXPECTRES(res, x) _EXPECTRES(res, x, "%d") + + +static void test_VarI4FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarI4FromI1); + CONVERTRANGE(VarI4FromI1, -128, 128); +} + +static void test_VarI4FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarI4FromI2); + CONVERTRANGE(VarI4FromI2, -32768, 32768); +} + +static void test_VarI4FromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarI4FromI8); + CHECKPTR(VarI4FromDec); + + CONVERT(VarI4FromI8, -1); EXPECT(-1); + CONVERT(VarI4FromI8, 0); EXPECT(0); + CONVERT(VarI4FromI8, 1); EXPECT(1); + + CONVERT_I8(VarI4FromI8, -1, 2147483647ul); EXPECT_OVERFLOW; + CONVERT_I8(VarI4FromI8, -1, 2147483648ul); EXPECT(-2147483647 - 1); + CONVERT_I8(VarI4FromI8, 0, 2147483647ul); EXPECT(2147483647); + CONVERT_I8(VarI4FromI8, 0, 2147483648ul); EXPECT_OVERFLOW; +} + +static void test_VarI4FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarI4FromUI1); + CONVERTRANGE(VarI4FromUI1, 0, 256); +} + +static void test_VarI4FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarI4FromUI2); + CONVERTRANGE(VarI4FromUI2, 0, 65536); +} + +static void test_VarI4FromUI4(void) +{ + CONVVARS(ULONG); + + CHECKPTR(VarI4FromUI4); + CONVERT(VarI4FromUI4, 0); EXPECT(0); + CONVERT(VarI4FromUI4, 1); EXPECT(1); + CONVERT(VarI4FromUI4, 2147483647); EXPECT(2147483647); + CONVERT(VarI4FromUI4, 2147483648ul); EXPECT_OVERFLOW; +} + +static void test_VarI4FromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarI4FromUI8); + CONVERT(VarI4FromUI8, 0); EXPECT(0); + CONVERT(VarI4FromUI8, 1); EXPECT(1); + CONVERT(VarI4FromUI8, 2147483647); EXPECT(2147483647); + CONVERT(VarI4FromUI8, 2147483648ul); EXPECT_OVERFLOW; +} + +static void test_VarI4FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarI4FromBool); + CONVERTRANGE(VarI4FromBool, -32768, 32768); +} + +static void test_VarI4FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarI4FromR4); + + /* min/max values are not exactly representable in a float */ + CONVERT(VarI4FromR4, -1.0f); EXPECT(-1); + CONVERT(VarI4FromR4, 0.0f); EXPECT(0); + CONVERT(VarI4FromR4, 1.0f); EXPECT(1); + + CONVERT(VarI4FromR4, -1.5f); EXPECT(-2); + CONVERT(VarI4FromR4, -0.6f); EXPECT(-1); + CONVERT(VarI4FromR4, -0.5f); EXPECT(0); + CONVERT(VarI4FromR4, -0.4f); EXPECT(0); + CONVERT(VarI4FromR4, 0.4f); EXPECT(0); + CONVERT(VarI4FromR4, 0.5f); EXPECT(0); + CONVERT(VarI4FromR4, 0.6f); EXPECT(1); + CONVERT(VarI4FromR4, 1.5f); EXPECT(2); +} + +static void test_VarI4FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarI4FromR8); + CONVERT(VarI4FromR8, -2147483649.0); EXPECT_OVERFLOW; + CONVERT(VarI4FromR8, -2147483648.0); EXPECT(-2147483647 - 1); + CONVERT(VarI4FromR8, -1.0); EXPECT(-1); + CONVERT(VarI4FromR8, 0.0); EXPECT(0); + CONVERT(VarI4FromR8, 1.0); EXPECT(1); + CONVERT(VarI4FromR8, 2147483647.0); EXPECT(2147483647); + CONVERT(VarI4FromR8, 2147483648.0); EXPECT_OVERFLOW; + + CONVERT(VarI4FromR8, -1.5); EXPECT(-2); + CONVERT(VarI4FromR8, -0.6); EXPECT(-1); + CONVERT(VarI4FromR8, -0.5); EXPECT(0); + CONVERT(VarI4FromR8, -0.4); EXPECT(0); + CONVERT(VarI4FromR8, 0.4); EXPECT(0); + CONVERT(VarI4FromR8, 0.5); EXPECT(0); + CONVERT(VarI4FromR8, 0.6); EXPECT(1); + CONVERT(VarI4FromR8, 1.5); EXPECT(2); +} + +static void test_VarI4FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarI4FromDate); + CONVERT(VarI4FromDate, -2147483649.0); EXPECT_OVERFLOW; + CONVERT(VarI4FromDate, -2147483648.0); EXPECT(-2147483647 - 1); + CONVERT(VarI4FromDate, -1.0); EXPECT(-1); + CONVERT(VarI4FromDate, 0.0); EXPECT(0); + CONVERT(VarI4FromDate, 1.0); EXPECT(1); + CONVERT(VarI4FromDate, 2147483647.0); EXPECT(2147483647); + CONVERT(VarI4FromDate, 2147483648.0); EXPECT_OVERFLOW; + + CONVERT(VarI4FromDate, -1.5); EXPECT(-2); + CONVERT(VarI4FromDate, -0.6); EXPECT(-1); + CONVERT(VarI4FromDate, -0.5); EXPECT(0); + CONVERT(VarI4FromDate, -0.4); EXPECT(0); + CONVERT(VarI4FromDate, 0.4); EXPECT(0); + CONVERT(VarI4FromDate, 0.5); EXPECT(0); + CONVERT(VarI4FromDate, 0.6); EXPECT(1); + CONVERT(VarI4FromDate, 1.5); EXPECT(2); +} + +static void test_VarI4FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarI4FromCy); + CONVERT_CY(VarI4FromCy,-1); EXPECT(-1); + CONVERT_CY(VarI4FromCy,0); EXPECT(0); + CONVERT_CY(VarI4FromCy,1); EXPECT(1); + + CONVERT_CY64(VarI4FromCy,-1,2147483647ul); EXPECT_OVERFLOW; + CONVERT_CY64(VarI4FromCy,-1,2147483648ul); EXPECT(-2147483647 - 1); + CONVERT_CY64(VarI4FromCy,0,2147483647ul); EXPECT(2147483647ul); + CONVERT_CY64(VarI4FromCy,0,2147483648ul); EXPECT_OVERFLOW; + + CONVERT_CY(VarI4FromCy,-1.5); EXPECT(-2); + CONVERT_CY(VarI4FromCy,-0.6); EXPECT(-1); + CONVERT_CY(VarI4FromCy,-0.5); EXPECT(0); + CONVERT_CY(VarI4FromCy,-0.4); EXPECT(0); + CONVERT_CY(VarI4FromCy,0.4); EXPECT(0); + CONVERT_CY(VarI4FromCy,0.5); EXPECT(0); + CONVERT_CY(VarI4FromCy,0.6); EXPECT(1); + CONVERT_CY(VarI4FromCy,1.5); EXPECT(2); +} + +static void test_VarI4FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarI4FromDec); + + CONVERT_BADDEC(VarI4FromDec); + + CONVERT_DEC(VarI4FromDec,0,0x80,0,1); EXPECT(-1); + CONVERT_DEC(VarI4FromDec,0,0,0,0); EXPECT(0); + CONVERT_DEC(VarI4FromDec,0,0,0,1); EXPECT(1); + + CONVERT_DEC64(VarI4FromDec,0,0x80,0,0,2147483649ul); EXPECT_OVERFLOW; + CONVERT_DEC64(VarI4FromDec,0,0x80,0,0,2147483648ul); EXPECT(-2147483647 - 1); + CONVERT_DEC64(VarI4FromDec,0,0,0,0,2147483647ul); EXPECT(2147483647ul); + CONVERT_DEC64(VarI4FromDec,0,0,0,0,2147483648ul); EXPECT_OVERFLOW; + + CONVERT_DEC64(VarI4FromDec,2,0x80,0,50,100); EXPECT_OVERFLOW; + CONVERT_DEC64(VarI4FromDec,2,0x80,0,50,0); EXPECT(-2147483647 - 1); + CONVERT_DEC64(VarI4FromDec,2,0,0,49,4294967196ul); EXPECT(2147483647); + CONVERT_DEC64(VarI4FromDec,2,0,0,50,0); EXPECT_OVERFLOW; +} + +static void test_VarI4FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarI4FromStr); + + CONVERT_STR(VarI4FromStr,NULL,0); EXPECT_MISMATCH; + CONVERT_STR(VarI4FromStr,"0",0); EXPECT(0); + CONVERT_STR(VarI4FromStr,"-2147483649",0); EXPECT_OVERFLOW; + CONVERT_STR(VarI4FromStr,"-2147483648",0); EXPECT(-2147483647 -1); + CONVERT_STR(VarI4FromStr,"2147483647",0); EXPECT(2147483647); + CONVERT_STR(VarI4FromStr,"2147483648",0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_STR(VarI4FromStr,"-1.5",LOCALE_NOUSEROVERRIDE); EXPECT(-2); + CONVERT_STR(VarI4FromStr,"-0.6",LOCALE_NOUSEROVERRIDE); EXPECT(-1); + CONVERT_STR(VarI4FromStr,"-0.5",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI4FromStr,"-0.4",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI4FromStr,"0.4",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI4FromStr,"0.5",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarI4FromStr,"0.6",LOCALE_NOUSEROVERRIDE); EXPECT(1); + CONVERT_STR(VarI4FromStr,"1.5",LOCALE_NOUSEROVERRIDE); EXPECT(2); +} + +static void test_VarI4Copy(void) +{ + COPYTEST(1, VT_I4, V_I4(&vSrc), V_I4(&vDst), V_I4REF(&vSrc), V_I4REF(&vDst), "%d"); +} + +static void test_VarI4ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1; + + INITIAL_TYPETEST(VT_I4, V_I4, "%d"); + COMMON_TYPETEST; + NEGATIVE_TYPETEST(VT_I4, V_I4, "%d", VT_UI4, V_UI4); +} + +#undef CONV_TYPE +#define CONV_TYPE ULONG +#undef EXPECTRES +#define EXPECTRES(res, x) _EXPECTRES(res, x, "%u") + +static void test_VarUI4FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarUI4FromI1); + OVERFLOWRANGE(VarUI4FromI1, -127, 0); + CONVERTRANGE(VarUI4FromI1, 0, 128); +} + +static void test_VarUI4FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarUI4FromI2); + OVERFLOWRANGE(VarUI4FromI2, -32768, 0); + CONVERTRANGE(VarUI4FromI2, 0, 32768); +} + +static void test_VarUI4FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarUI4FromUI2); + CONVERTRANGE(VarUI4FromUI2, 0, 65536); +} + +static void test_VarUI4FromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarUI4FromI8); + CONVERT(VarUI4FromI8, -1); EXPECT_OVERFLOW; + CONVERT(VarUI4FromI8, 0); EXPECT(0); + CONVERT(VarUI4FromI8, 1); EXPECT(1); + CONVERT(VarUI4FromI8, 4294967295ul); EXPECT(4294967295ul); + CONVERT_I8(VarUI4FromI8, 1, 0); EXPECT_OVERFLOW; +} + +static void test_VarUI4FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarUI4FromUI1); + CONVERTRANGE(VarUI4FromUI1, 0, 256); +} + +static void test_VarUI4FromI4(void) +{ + CONVVARS(int); + + CHECKPTR(VarUI4FromI4); + CONVERT(VarUI4FromI4, -1); EXPECT_OVERFLOW; + CONVERT(VarUI4FromI4, 0); EXPECT(0); + CONVERT(VarUI4FromI4, 1); EXPECT(1); + CONVERT(VarUI4FromI4, 2147483647); EXPECT(2147483647); +} + +static void test_VarUI4FromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarUI4FromUI8); + CONVERT(VarUI4FromUI8, 0); EXPECT(0); + CONVERT(VarUI4FromUI8, 1); EXPECT(1); + CONVERT(VarUI4FromI8, 4294967295ul); EXPECT(4294967295ul); + CONVERT_I8(VarUI4FromI8, 1, 0); EXPECT_OVERFLOW; +} + +static void test_VarUI4FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarUI4FromBool); + CONVERTRANGE(VarUI4FromBool, -32768, 32768); +} + +static void test_VarUI4FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarUI4FromR4); + /* We can't test max values as they are not exactly representable in a float */ + CONVERT(VarUI4FromR4, -1.0f); EXPECT_OVERFLOW; + CONVERT(VarUI4FromR4, 0.0f); EXPECT(0); + CONVERT(VarUI4FromR4, 1.0f); EXPECT(1); + + CONVERT(VarUI4FromR4, -1.5f); EXPECT_OVERFLOW; + CONVERT(VarUI4FromR4, -0.6f); EXPECT_OVERFLOW; + CONVERT(VarUI4FromR4, -0.5f); EXPECT(0); + CONVERT(VarUI4FromR4, -0.4f); EXPECT(0); + CONVERT(VarUI4FromR4, 0.4f); EXPECT(0); + CONVERT(VarUI4FromR4, 0.5f); EXPECT(0); + CONVERT(VarUI4FromR4, 0.6f); EXPECT(1); + CONVERT(VarUI4FromR4, 1.5f); EXPECT(2); + +} + +static void test_VarUI4FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarUI4FromR8); + CONVERT(VarUI4FromR8, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI4FromR8, 0.0); EXPECT(0); + CONVERT(VarUI4FromR8, 1.0); EXPECT(1); + CONVERT(VarUI4FromR8, 4294967295.0); EXPECT(4294967295ul); + CONVERT(VarUI4FromR8, 4294967296.0); EXPECT_OVERFLOW; + + CONVERT(VarUI4FromR8, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI4FromR8, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI4FromR8, -0.5); EXPECT(0); + CONVERT(VarUI4FromR8, -0.4); EXPECT(0); + CONVERT(VarUI4FromR8, 0.4); EXPECT(0); + CONVERT(VarUI4FromR8, 0.5); EXPECT(0); + CONVERT(VarUI4FromR8, 0.6); EXPECT(1); + CONVERT(VarUI4FromR8, 1.5); EXPECT(2); +} + +static void test_VarUI4FromDate(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarUI4FromDate); + CONVERT(VarUI4FromDate, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI4FromDate, 0.0); EXPECT(0); + CONVERT(VarUI4FromDate, 1.0); EXPECT(1); + CONVERT(VarUI4FromDate, 4294967295.0); EXPECT(4294967295ul); + CONVERT(VarUI4FromDate, 4294967296.0); EXPECT_OVERFLOW; + + CONVERT(VarUI4FromDate, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI4FromDate, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI4FromDate, -0.5); EXPECT(0); + CONVERT(VarUI4FromDate, -0.4); EXPECT(0); + CONVERT(VarUI4FromDate, 0.4); EXPECT(0); + CONVERT(VarUI4FromDate, 0.5); EXPECT(0); + CONVERT(VarUI4FromDate, 0.6); EXPECT(1); + CONVERT(VarUI4FromDate, 1.5); EXPECT(2); +} + +static void test_VarUI4FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarUI4FromCy); + CONVERT_CY(VarUI4FromCy,-1); EXPECT_OVERFLOW; + CONVERT_CY(VarUI4FromCy,0); EXPECT(0); + CONVERT_CY(VarUI4FromCy,1); EXPECT(1); + CONVERT_CY64(VarUI4FromCy,0,4294967295ul); EXPECT(4294967295ul); + CONVERT_CY64(VarUI4FromCy,1,0); EXPECT_OVERFLOW; + + CONVERT_CY(VarUI4FromCy,-1.5); EXPECT_OVERFLOW; + CONVERT_CY(VarUI4FromCy,-0.6); EXPECT_OVERFLOW; + CONVERT_CY(VarUI4FromCy,-0.5); EXPECT(0); + CONVERT_CY(VarUI4FromCy,-0.4); EXPECT(0); + CONVERT_CY(VarUI4FromCy,0.4); EXPECT(0); + CONVERT_CY(VarUI4FromCy,0.5); EXPECT(0); + CONVERT_CY(VarUI4FromCy,0.6); EXPECT(1); + CONVERT_CY(VarUI4FromCy,1.5); EXPECT(2); +} + +static void test_VarUI4FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarUI4FromDec); + + CONVERT_BADDEC(VarUI4FromDec); + + CONVERT_DEC(VarUI4FromDec,0,0x80,0,1); EXPECT_OVERFLOW; + CONVERT_DEC(VarUI4FromDec,0,0,0,0); EXPECT(0); + CONVERT_DEC(VarUI4FromDec,0,0,0,1); EXPECT(1); + CONVERT_DEC64(VarUI4FromDec,0,0,0,0,4294967295ul); EXPECT(4294967295ul); + CONVERT_DEC64(VarUI4FromDec,0,0,0,1,0); EXPECT_OVERFLOW; + + CONVERT_DEC64(VarUI4FromDec,2,0,0,99,4294967196ul); EXPECT(4294967295ul); + CONVERT_DEC64(VarUI4FromDec,2,0,0,100,0); EXPECT_OVERFLOW; +} + +static void test_VarUI4FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarUI4FromStr); + + CONVERT_STR(VarUI4FromStr,NULL,0); EXPECT_MISMATCH; + CONVERT_STR(VarUI4FromStr,"-1",0); EXPECT_OVERFLOW; + CONVERT_STR(VarUI4FromStr,"0",0); EXPECT(0); + CONVERT_STR(VarUI4FromStr,"4294967295",0); EXPECT(4294967295ul); + CONVERT_STR(VarUI4FromStr,"4294967296",0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT_STR(VarUI4FromStr,"-1.5",LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI4FromStr,"-0.6",LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI4FromStr,"-0.5",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI4FromStr,"-0.4",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI4FromStr,"0.4",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI4FromStr,"0.5",LOCALE_NOUSEROVERRIDE); EXPECT(0); + CONVERT_STR(VarUI4FromStr,"0.6",LOCALE_NOUSEROVERRIDE); EXPECT(1); + CONVERT_STR(VarUI4FromStr,"1.5",LOCALE_NOUSEROVERRIDE); EXPECT(2); +} + +static void test_VarUI4Copy(void) +{ + if (!IS_ANCIENT) + { + COPYTEST(1u, VT_UI4, V_UI4(&vSrc), V_UI4(&vDst), V_UI4REF(&vSrc), V_UI4REF(&vDst), "%u"); + } +} + +static void test_VarUI4ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1; + + if (!IS_ANCIENT) + { + INITIAL_TYPETEST(VT_UI4, V_UI4, "%u"); + COMMON_TYPETEST; + NEGATIVE_TYPETEST(VT_UI4, V_UI4, "%u", VT_I4, V_I4); + } +} + +/* + * VT_I8/VT_UI8 + */ + +#undef CONV_TYPE +#define CONV_TYPE LONG64 +#undef EXPECTRES +#define EXPECTRES(res, x) \ + ok(hres == S_OK || ((HRESULT)res != S_OK && hres == (HRESULT)res), \ + "expected hres " #x ", got hres=0x%08x\n", hres) + +#define EXPECTI8(x) \ + ok((hres == S_OK && out == (CONV_TYPE)(x)), \ + "expected " #x "(%u,%u), got (%u,%u); hres=0x%08x\n", \ + (ULONG)((LONG64)(x) >> 32), (ULONG)((x) & 0xffffffff), \ + (ULONG)(out >> 32), (ULONG)(out & 0xffffffff), hres) + +#define EXPECTI864(x,y) \ + ok(hres == S_OK && (out >> 32) == (CONV_TYPE)(x) && (out & 0xffffffff) == (CONV_TYPE)(y), \ + "expected " #x "(%u,%u), got (%u,%u); hres=0x%08x\n", \ + (ULONG)(x), (ULONG)(y), \ + (ULONG)(out >> 32), (ULONG)(out & 0xffffffff), hres) + +static void test_VarI8FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarI8FromI1); + for (i = -128; i < 128; i++) + { + CONVERT(VarI8FromI1,i); EXPECTI8(i); + } +} + +static void test_VarI8FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarI8FromUI1); + for (i = 0; i < 256; i++) + { + CONVERT(VarI8FromUI1,i); EXPECTI8(i); + } +} + +static void test_VarI8FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarI8FromI2); + for (i = -32768; i < 32768; i++) + { + CONVERT(VarI8FromI2,i); EXPECTI8(i); + } +} + +static void test_VarI8FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarI8FromUI2); + for (i = -0; i < 65535; i++) + { + CONVERT(VarI8FromUI2,i); EXPECTI8(i); + } +} + +static void test_VarI8FromUI4(void) +{ + CONVVARS(ULONG); + + CHECKPTR(VarI8FromUI4); + CONVERT(VarI8FromUI4, 0); EXPECTI8(0); + CONVERT(VarI8FromUI4, 1); EXPECTI8(1); + CONVERT(VarI8FromUI4, 4294967295ul); EXPECTI8(4294967295ul); +} + +static void test_VarI8FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarI8FromR4); + + CONVERT(VarI8FromR4, -128.0f); EXPECTI8(-128); + CONVERT(VarI8FromR4, -1.0f); EXPECTI8(-1); + CONVERT(VarI8FromR4, 0.0f); EXPECTI8(0); + CONVERT(VarI8FromR4, 1.0f); EXPECTI8(1); + CONVERT(VarI8FromR4, 127.0f); EXPECTI8(127); + + CONVERT(VarI8FromR4, -1.5f); EXPECTI8(-2); + CONVERT(VarI8FromR4, -0.6f); EXPECTI8(-1); + CONVERT(VarI8FromR4, -0.5f); EXPECTI8(0); + CONVERT(VarI8FromR4, -0.4f); EXPECTI8(0); + CONVERT(VarI8FromR4, 0.4f); EXPECTI8(0); + CONVERT(VarI8FromR4, 0.5f); EXPECTI8(0); + CONVERT(VarI8FromR4, 0.6f); EXPECTI8(1); + CONVERT(VarI8FromR4, 1.5f); EXPECTI8(2); +} + +static void test_VarI8FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarI8FromR8); + CONVERT(VarI8FromR8, -128.0); EXPECTI8(-128); + CONVERT(VarI8FromR8, -1.0); EXPECTI8(-1); + CONVERT(VarI8FromR8, 0.0); EXPECTI8(0); + CONVERT(VarI8FromR8, 1.0); EXPECTI8(1); + CONVERT(VarI8FromR8, 127.0); EXPECTI8(127); + + CONVERT(VarI8FromR8, -1.5); EXPECTI8(-2); + CONVERT(VarI8FromR8, -0.6); EXPECTI8(-1); + CONVERT(VarI8FromR8, -0.5); EXPECTI8(0); + CONVERT(VarI8FromR8, -0.4); EXPECTI8(0); + CONVERT(VarI8FromR8, 0.4); EXPECTI8(0); + CONVERT(VarI8FromR8, 0.5); EXPECTI8(0); + CONVERT(VarI8FromR8, 0.6); EXPECTI8(1); + CONVERT(VarI8FromR8, 1.5); EXPECTI8(2); +} + +static void test_VarI8FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarI8FromDate); + CONVERT(VarI8FromDate, -128.0); EXPECTI8(-128); + CONVERT(VarI8FromDate, -1.0); EXPECTI8(-1); + CONVERT(VarI8FromDate, 0.0); EXPECTI8(0); + CONVERT(VarI8FromDate, 1.0); EXPECTI8(1); + CONVERT(VarI8FromDate, 127.0); EXPECTI8(127); + + CONVERT(VarI8FromDate, -1.5); EXPECTI8(-2); + CONVERT(VarI8FromDate, -0.6); EXPECTI8(-1); + CONVERT(VarI8FromDate, -0.5); EXPECTI8(0); + CONVERT(VarI8FromDate, -0.4); EXPECTI8(0); + CONVERT(VarI8FromDate, 0.4); EXPECTI8(0); + CONVERT(VarI8FromDate, 0.5); EXPECTI8(0); + CONVERT(VarI8FromDate, 0.6); EXPECTI8(1); + CONVERT(VarI8FromDate, 1.5); EXPECTI8(2); +} + +static void test_VarI8FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarI8FromBool); + for (i = -32768; i < 32768; i++) + { + CONVERT(VarI8FromBool,i); EXPECTI8(i); + } +} + +static void test_VarI8FromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarI8FromUI8); + CONVERT(VarI8FromUI8, 0); EXPECTI8(0); + CONVERT(VarI8FromUI8, 1); EXPECTI8(1); + CONVERT_I8(VarI8FromUI8, 0x7fffffff, 0xffffffff); EXPECTI864(0x7fffffff, 0xffffffff); + CONVERT_I8(VarI8FromUI8, 0x80000000, 0); EXPECT_OVERFLOW; +} + +static void test_VarI8FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarI8FromCy); + CONVERT_CY(VarI8FromCy,-128); EXPECTI8(-129); + CONVERT_CY(VarI8FromCy,-1); EXPECTI8(-2); + CONVERT_CY(VarI8FromCy,0); EXPECTI8(0); + CONVERT_CY(VarI8FromCy,1); EXPECTI8(1); + CONVERT_CY(VarI8FromCy,127); EXPECTI8(127); + + CONVERT_CY(VarI8FromCy,-1.5); EXPECTI8(-2); + CONVERT_CY(VarI8FromCy,-0.6); EXPECTI8(-1); + CONVERT_CY(VarI8FromCy,-0.5); EXPECTI8(-1); + CONVERT_CY(VarI8FromCy,-0.4); EXPECTI8(-1); + CONVERT_CY(VarI8FromCy,0.4); EXPECTI8(0); + CONVERT_CY(VarI8FromCy,0.5); EXPECTI8(0); + CONVERT_CY(VarI8FromCy,0.6); EXPECTI8(1); + CONVERT_CY(VarI8FromCy,1.5); EXPECTI8(2); +} + +static void test_VarI8FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarI8FromDec); + + CONVERT_BADDEC(VarI8FromDec); + + CONVERT_DEC(VarI8FromDec,0,0x80,0,128); EXPECTI8(-128); + CONVERT_DEC(VarI8FromDec,0,0x80,0,1); EXPECTI8(-1); + CONVERT_DEC(VarI8FromDec,0,0,0,0); EXPECTI8(0); + CONVERT_DEC(VarI8FromDec,0,0,0,1); EXPECTI8(1); + CONVERT_DEC(VarI8FromDec,0,0,0,127); EXPECTI8(127); + + CONVERT_DEC(VarI8FromDec,2,0x80,0,12700); EXPECTI8(-127); + CONVERT_DEC(VarI8FromDec,2,0,0,12700); EXPECTI8(127); +} + +static void test_VarI8FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarI8FromStr); + + CONVERT_STR(VarI8FromStr,NULL,0); EXPECT_MISMATCH; + CONVERT_STR(VarI8FromStr,"0",0); EXPECTI8(0); + CONVERT_STR(VarI8FromStr,"-1",0); EXPECTI8(-1); + CONVERT_STR(VarI8FromStr,"2147483647",0); EXPECTI8(2147483647); + + CONVERT_STR(VarI8FromStr,"-1.5",LOCALE_NOUSEROVERRIDE); EXPECTI8(-2); + CONVERT_STR(VarI8FromStr,"-0.6",LOCALE_NOUSEROVERRIDE); EXPECTI8(-1); + CONVERT_STR(VarI8FromStr,"-0.5",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarI8FromStr,"-0.4",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarI8FromStr,"0.4",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarI8FromStr,"0.5",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarI8FromStr,"0.6",LOCALE_NOUSEROVERRIDE); EXPECTI8(1); + CONVERT_STR(VarI8FromStr,"1.5",LOCALE_NOUSEROVERRIDE); EXPECTI8(2); +} + +static void test_VarI8Copy(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + LONGLONG in = 1; + + if (!HAVE_OLEAUT32_I8) + { + skip("I8 and UI8 data types are not available\n"); + return; + } + + VariantInit(&vSrc); + VariantInit(&vDst); + V_VT(&vSrc) = VT_I8; + V_I8(&vSrc) = in; + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == VT_I8 && V_I8(&vDst) == in, + "copy hres 0x%X, type %d, value (%x%08x) %x%08x\n", + hres, V_VT(&vDst), (UINT)(in >> 32), (UINT)in, (UINT)(V_I8(&vDst) >> 32), (UINT)V_I8(&vDst) ); + V_VT(&vSrc) = VT_I8|VT_BYREF; + V_I8REF(&vSrc) = ∈ + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == (VT_I8|VT_BYREF) && V_I8REF(&vDst) == &in, + "ref hres 0x%X, type %d, ref (%p) %p\n", hres, V_VT(&vDst), &in, V_I8REF(&vDst)); + hres = VariantCopyInd(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == VT_I8 && V_I8(&vDst) == in, + "copy hres 0x%X, type %d, value (%x%08x) %x%08x\n", + hres, V_VT(&vDst), (UINT)(in >> 32), (UINT)in, (UINT)(V_I8(&vDst) >> 32), (UINT)V_I8(&vDst) ); +} + +static void test_VarI8ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + if (!HAVE_OLEAUT32_I8) + { + skip("I8 and UI8 data types are not available\n"); + return; + } + + in = 1; + + INITIAL_TYPETESTI8(VT_I8, V_I8); + COMMON_TYPETEST; +} + +/* Adapt the test macros to UI8 */ +#undef CONV_TYPE +#define CONV_TYPE ULONG64 + +static void test_VarUI8FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarUI8FromI1); + for (i = -128; i < 128; i++) + { + CONVERT(VarUI8FromI1,i); + if (i < 0) + EXPECT_OVERFLOW; + else + EXPECTI8(i); + } +} + +static void test_VarUI8FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarUI8FromUI1); + for (i = 0; i < 256; i++) + { + CONVERT(VarUI8FromUI1,i); EXPECTI8(i); + } +} + +static void test_VarUI8FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarUI8FromI2); + for (i = -32768; i < 32768; i++) + { + CONVERT(VarUI8FromI2,i); + if (i < 0) + EXPECT_OVERFLOW; + else + EXPECTI8(i); + } +} + +static void test_VarUI8FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarUI8FromUI2); + for (i = 0; i < 65535; i++) + { + CONVERT(VarUI8FromUI2,i); EXPECTI8(i); + } +} + +static void test_VarUI8FromUI4(void) +{ + CONVVARS(ULONG); + + CHECKPTR(VarUI8FromUI4); + CONVERT(VarUI8FromUI4, 0); EXPECTI8(0); + CONVERT(VarUI8FromUI4, 0xffffffff); EXPECTI8(0xffffffff); +} + +static void test_VarUI8FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarUI8FromR4); + CONVERT(VarUI8FromR4, -1.0f); EXPECT_OVERFLOW; + CONVERT(VarUI8FromR4, 0.0f); EXPECTI8(0); + CONVERT(VarUI8FromR4, 1.0f); EXPECTI8(1); + CONVERT(VarUI8FromR4, 255.0f); EXPECTI8(255); + + CONVERT(VarUI8FromR4, -1.5f); EXPECT_OVERFLOW; + CONVERT(VarUI8FromR4, -0.6f); EXPECT_OVERFLOW; + CONVERT(VarUI8FromR4, -0.5f); EXPECTI8(0); + CONVERT(VarUI8FromR4, -0.4f); EXPECTI8(0); + CONVERT(VarUI8FromR4, 0.4f); EXPECTI8(0); + CONVERT(VarUI8FromR4, 0.5f); EXPECTI8(0); + CONVERT(VarUI8FromR4, 0.6f); EXPECTI8(1); + CONVERT(VarUI8FromR4, 1.5f); EXPECTI8(2); +} + +static void test_VarUI8FromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarUI8FromR8); + CONVERT(VarUI8FromR8, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI8FromR8, 0.0); EXPECTI8(0); + CONVERT(VarUI8FromR8, 1.0); EXPECTI8(1); + CONVERT(VarUI8FromR8, 255.0); EXPECTI8(255); + + CONVERT(VarUI8FromR8, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI8FromR8, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI8FromR8, -0.5); EXPECTI8(0); + CONVERT(VarUI8FromR8, -0.4); EXPECTI8(0); + CONVERT(VarUI8FromR8, 0.4); EXPECTI8(0); + CONVERT(VarUI8FromR8, 0.5); EXPECTI8(0); + CONVERT(VarUI8FromR8, 0.6); EXPECTI8(1); + CONVERT(VarUI8FromR8, 1.5); EXPECTI8(2); +} + +static void test_VarUI8FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarUI8FromDate); + CONVERT(VarUI8FromDate, -1.0); EXPECT_OVERFLOW; + CONVERT(VarUI8FromDate, 0.0); EXPECTI8(0); + CONVERT(VarUI8FromDate, 1.0); EXPECTI8(1); + CONVERT(VarUI8FromDate, 255.0); EXPECTI8(255); + + CONVERT(VarUI8FromDate, -1.5); EXPECT_OVERFLOW; + CONVERT(VarUI8FromDate, -0.6); EXPECT_OVERFLOW; + CONVERT(VarUI8FromDate, -0.5); EXPECTI8(0); + CONVERT(VarUI8FromDate, -0.4); EXPECTI8(0); + CONVERT(VarUI8FromDate, 0.4); EXPECTI8(0); + CONVERT(VarUI8FromDate, 0.5); EXPECTI8(0); + CONVERT(VarUI8FromDate, 0.6); EXPECTI8(1); + CONVERT(VarUI8FromDate, 1.5); EXPECTI8(2); +} + +static void test_VarUI8FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarUI8FromBool); + CONVERTRANGE(VarUI8FromBool, -32768, 32768); +} + +static void test_VarUI8FromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarUI8FromI8); + CONVERT(VarUI8FromI8, -1); EXPECT_OVERFLOW; + CONVERT(VarUI8FromI8, 0); EXPECTI8(0); + CONVERT(VarUI8FromI8, 1); EXPECTI8(1); +} + +static void test_VarUI8FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarUI8FromCy); + CONVERT_CY(VarUI8FromCy,-1); EXPECT_OVERFLOW; + CONVERT_CY(VarUI8FromCy,0); EXPECTI8(0); + CONVERT_CY(VarUI8FromCy,1); EXPECTI8(1); + CONVERT_CY(VarUI8FromCy,255); EXPECTI8(255); + + CONVERT_CY(VarUI8FromCy,-1.5); EXPECT_OVERFLOW; + CONVERT_CY(VarUI8FromCy,-0.6); EXPECT_OVERFLOW; + CONVERT_CY(VarUI8FromCy,-0.5); EXPECTI8(0); + CONVERT_CY(VarUI8FromCy,-0.4); EXPECTI8(0); + CONVERT_CY(VarUI8FromCy,0.4); EXPECTI8(0); + CONVERT_CY(VarUI8FromCy,0.5); EXPECTI8(0); + CONVERT_CY(VarUI8FromCy,0.6); EXPECTI8(1); + CONVERT_CY(VarUI8FromCy,1.5); EXPECTI8(2); +} + +static void test_VarUI8FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarUI8FromDec); + + CONVERT_BADDEC(VarUI8FromDec); + + /* This returns 1 under native; Wine fixes this bug and returns overflow */ + if (0) + { + CONVERT_DEC(VarUI8FromDec,0,0x80,0,1); + } + + CONVERT_DEC(VarUI8FromDec,0,0,0,0); EXPECTI8(0); + CONVERT_DEC(VarUI8FromDec,0,0,0,1); EXPECTI8(1); + CONVERT_DEC(VarUI8FromDec,0,0,0,255); EXPECTI8(255); + + CONVERT_DEC(VarUI8FromDec,2,0x80,0,100); EXPECT_OVERFLOW; + CONVERT_DEC(VarUI8FromDec,2,0,0,25500); EXPECTI8(255); +} + +static void test_VarUI8FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarUI8FromStr); + + CONVERT_STR(VarUI8FromStr,NULL,0); EXPECT_MISMATCH; + CONVERT_STR(VarUI8FromStr,"0",0); EXPECTI8(0); + CONVERT_STR(VarUI8FromStr,"-1",0); EXPECT_OVERFLOW; + CONVERT_STR(VarUI8FromStr,"2147483647",0); EXPECTI8(2147483647); + + CONVERT_STR(VarUI8FromStr,"-1.5",LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI8FromStr,"-0.6",LOCALE_NOUSEROVERRIDE); EXPECT_OVERFLOW; + CONVERT_STR(VarUI8FromStr,"-0.5",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarUI8FromStr,"-0.4",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarUI8FromStr,"0.4",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarUI8FromStr,"0.5",LOCALE_NOUSEROVERRIDE); EXPECTI8(0); + CONVERT_STR(VarUI8FromStr,"0.6",LOCALE_NOUSEROVERRIDE); EXPECTI8(1); + CONVERT_STR(VarUI8FromStr,"1.5",LOCALE_NOUSEROVERRIDE); EXPECTI8(2); +} + +static void test_VarUI8Copy(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + ULONGLONG in = 1; + + if (!HAVE_OLEAUT32_I8) + { + skip("I8 and UI8 data types are not available\n"); + return; + } + + VariantInit(&vSrc); + VariantInit(&vDst); + V_VT(&vSrc) = VT_UI8; + V_UI8(&vSrc) = in; + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == VT_UI8 && V_UI8(&vDst) == in, + "copy hres 0x%X, type %d, value (%x%08x) %x%08x\n", + hres, V_VT(&vDst), (UINT)(in >> 32), (UINT)in, (UINT)(V_UI8(&vDst) >> 32), (UINT)V_UI8(&vDst) ); + V_VT(&vSrc) = VT_UI8|VT_BYREF; + V_UI8REF(&vSrc) = ∈ + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == (VT_UI8|VT_BYREF) && V_UI8REF(&vDst) == &in, + "ref hres 0x%X, type %d, ref (%p) %p\n", hres, V_VT(&vDst), &in, V_UI8REF(&vDst)); + hres = VariantCopyInd(&vDst, &vSrc); + ok(hres == S_OK && V_VT(&vDst) == VT_UI8 && V_UI8(&vDst) == in, + "copy hres 0x%X, type %d, value (%x%08x) %x%08x\n", + hres, V_VT(&vDst), (UINT)(in >> 32), (UINT)in, (UINT)(V_UI8(&vDst) >> 32), (UINT)V_UI8(&vDst) ); +} + +static void test_VarUI8ChangeTypeEx(void) +{ + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + if (!HAVE_OLEAUT32_I8) + { + skip("I8 and UI8 data types are not available\n"); + return; + } + + in = 1; + + INITIAL_TYPETESTI8(VT_UI8, V_UI8); + COMMON_TYPETEST; +} + +/* + * VT_R4 + */ + +#undef CONV_TYPE +#define CONV_TYPE float +#undef EXPECTRES +#define EXPECTRES(res, x) _EXPECTRES(res, x, "%15.15f") + +static void test_VarR4FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarR4FromI1); + CONVERTRANGE(VarR4FromI1, -128, 128); +} + +static void test_VarR4FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarR4FromUI1); + CONVERTRANGE(VarR4FromUI1, 0, 256); +} + +static void test_VarR4FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarR4FromI2); + CONVERTRANGE(VarR4FromI2, -32768, 32768); +} + +static void test_VarR4FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarR4FromUI2); + CONVERTRANGE(VarR4FromUI2, 0, 65536); +} + +static void test_VarR4FromI4(void) +{ + CONVVARS(int); + + CHECKPTR(VarR4FromI4); + CONVERT(VarR4FromI4, -2147483647-1); EXPECT(-2147483648.0f); + CONVERT(VarR4FromI4, -1); EXPECT(-1.0f); + CONVERT(VarR4FromI4, 0); EXPECT(0.0f); + CONVERT(VarR4FromI4, 1); EXPECT(1.0f); + CONVERT(VarR4FromI4, 2147483647); EXPECT(2147483647.0f); +} + +static void test_VarR4FromUI4(void) +{ + CONVVARS(unsigned int); + + CHECKPTR(VarR4FromUI4); + CONVERT(VarR4FromUI4, 0); EXPECT(0.0f); + CONVERT(VarR4FromUI4, 1); EXPECT(1.0f); +#if defined(__i386__) && (defined(_MSC_VER) || defined(__GNUC__)) + CONVERT(VarR4FromUI4, 0xffffffff); EXPECT(4294967296.0f); +#endif +} + +static void test_VarR4FromR8(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarR4FromR8); + CONVERT(VarR4FromR8, -1.0); EXPECT(-1.0f); + CONVERT(VarR4FromR8, 0.0); EXPECT(0.0f); + CONVERT(VarR4FromR8, 1.0); EXPECT(1.0f); + CONVERT(VarR4FromR8, 1.5); EXPECT(1.5f); + + /* Skip rounding tests - no rounding is done */ +} + +static void test_VarR4FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + + CHECKPTR(VarR4FromBool); + CONVERT(VarR4FromBool, VARIANT_TRUE); EXPECT(VARIANT_TRUE * 1.0f); + CONVERT(VarR4FromBool, VARIANT_FALSE); EXPECT(VARIANT_FALSE * 1.0f); +} + +static void test_VarR4FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarR4FromCy); + CONVERT_CY(VarR4FromCy,-32768); EXPECT(-32768.0f); + CONVERT_CY(VarR4FromCy,-1); EXPECT(-1.0f); + CONVERT_CY(VarR4FromCy,0); EXPECT(0.0f); + CONVERT_CY(VarR4FromCy,1); EXPECT(1.0f); + CONVERT_CY(VarR4FromCy,32768); EXPECT(32768.0f); + + CONVERT_CY(VarR4FromCy,-1.5); EXPECT(-1.5f); + CONVERT_CY(VarR4FromCy,-0.6); EXPECT(-0.6f); + CONVERT_CY(VarR4FromCy,-0.5); EXPECT(-0.5f); + CONVERT_CY(VarR4FromCy,-0.4); EXPECT(-0.4f); + CONVERT_CY(VarR4FromCy,0.4); EXPECT(0.4f); + CONVERT_CY(VarR4FromCy,0.5); EXPECT(0.5f); + CONVERT_CY(VarR4FromCy,0.6); EXPECT(0.6f); + CONVERT_CY(VarR4FromCy,1.5); EXPECT(1.5f); +} + +static void test_VarR4FromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarR4FromI8); + CONVERT(VarR4FromI8, -1); EXPECT(-1.0f); + CONVERT(VarR4FromI8, 0); EXPECT(0.0f); + CONVERT(VarR4FromI8, 1); EXPECT(1.0f); +} + +static void test_VarR4FromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarR4FromUI8); + CONVERT(VarR4FromUI8, 0); EXPECT(0.0f); + CONVERT(VarR4FromUI8, 1); EXPECT(1.0f); +} + +static void test_VarR4FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarR4FromDec); + + CONVERT_BADDEC(VarR4FromDec); + + CONVERT_DEC(VarR4FromDec,0,0x80,0,32768); EXPECT(-32768.0f); + CONVERT_DEC(VarR4FromDec,0,0x80,0,1); EXPECT(-1.0f); + CONVERT_DEC(VarR4FromDec,0,0,0,0); EXPECT(0.0f); + CONVERT_DEC(VarR4FromDec,0,0,0,1); EXPECT(1.0f); + CONVERT_DEC(VarR4FromDec,0,0,0,32767); EXPECT(32767.0f); + + CONVERT_DEC(VarR4FromDec,2,0x80,0,3276800); EXPECT(-32768.0f); + CONVERT_DEC(VarR4FromDec,2,0,0,3276700); EXPECT(32767.0f); + + CONVERT_DEC(VarR4FromDec,0,0,1,0); EXPECT(18446744073709551616.0f); +} + +static void test_VarR4FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarR4FromDate); + CONVERT(VarR4FromDate, -1.0); EXPECT(-1.0f); + CONVERT(VarR4FromDate, 0.0); EXPECT(0.0f); + CONVERT(VarR4FromDate, 1.0); EXPECT(1.0f); +} + +static void test_VarR4FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarR4FromStr); + + CONVERT_STR(VarR4FromStr,NULL,0); EXPECT_MISMATCH; + CONVERT_STR(VarR4FromStr,"-1", 0); EXPECT(-1.0f); + CONVERT_STR(VarR4FromStr,"0", 0); EXPECT(0.0f); + CONVERT_STR(VarR4FromStr,"1", 0); EXPECT(1.0f); + + CONVERT_STR(VarR4FromStr,"-1.5",LOCALE_NOUSEROVERRIDE); EXPECT(-1.5f); + CONVERT_STR(VarR4FromStr,"-0.6",LOCALE_NOUSEROVERRIDE); EXPECT(-0.6f); + CONVERT_STR(VarR4FromStr,"-0.5",LOCALE_NOUSEROVERRIDE); EXPECT(-0.5f); + CONVERT_STR(VarR4FromStr,"-0.4",LOCALE_NOUSEROVERRIDE); EXPECT(-0.4f); + CONVERT_STR(VarR4FromStr,"0.4",LOCALE_NOUSEROVERRIDE); EXPECT(0.4f); + CONVERT_STR(VarR4FromStr,"0.5",LOCALE_NOUSEROVERRIDE); EXPECT(0.5f); + CONVERT_STR(VarR4FromStr,"0.6",LOCALE_NOUSEROVERRIDE); EXPECT(0.6f); + CONVERT_STR(VarR4FromStr,"1.5",LOCALE_NOUSEROVERRIDE); EXPECT(1.5f); +} + +static void test_VarR4Copy(void) +{ + COPYTEST(77665544.0f, VT_R4, V_R4(&vSrc), V_R4(&vDst), V_R4REF(&vSrc),V_R4REF(&vDst), "%15.15f"); +} + +static void test_VarR4ChangeTypeEx(void) +{ +#ifdef HAS_UINT64_TO_FLOAT + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1.0f; + + INITIAL_TYPETEST(VT_R4, V_R4, "%f"); + COMMON_TYPETEST; +#endif +} + +/* + * VT_R8 + */ + +#undef CONV_TYPE +#define CONV_TYPE double +#undef EXPECTRES +#define EXPECTRES(res, x) _EXPECTRES(res, x, "%15.15f") + +static void test_VarR8FromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarR8FromI1); + CONVERTRANGE(VarR8FromI1, -128, 128); +} + +static void test_VarR8FromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarR8FromUI1); + CONVERTRANGE(VarR8FromUI1, 0, 256); +} + +static void test_VarR8FromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarR8FromI2); + CONVERTRANGE(VarR8FromI2, -32768, 32768); +} + +static void test_VarR8FromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarR8FromUI2); + CONVERTRANGE(VarR8FromUI2, 0, 65536); +} + +static void test_VarR8FromI4(void) +{ + CONVVARS(int); + + CHECKPTR(VarR8FromI4); + CONVERT(VarR8FromI4, -2147483647-1); EXPECT(-2147483648.0); + CONVERT(VarR8FromI4, -1); EXPECT(-1.0); + CONVERT(VarR8FromI4, 0); EXPECT(0.0); + CONVERT(VarR8FromI4, 1); EXPECT(1.0); + CONVERT(VarR8FromI4, 0x7fffffff); EXPECT(2147483647.0); +} + +static void test_VarR8FromUI4(void) +{ + CONVVARS(unsigned int); + + CHECKPTR(VarR8FromUI4); + CONVERT(VarR8FromUI4, 0); EXPECT(0.0); + CONVERT(VarR8FromUI4, 1); EXPECT(1.0); + CONVERT(VarR8FromUI4, 0xffffffff); EXPECT(4294967295.0); +} + +static void test_VarR8FromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarR8FromR4); + CONVERT(VarR8FromR4, -1.0f); EXPECT(-1.0); + CONVERT(VarR8FromR4, 0.0f); EXPECT(0.0); + CONVERT(VarR8FromR4, 1.0f); EXPECT(1.0); + CONVERT(VarR8FromR4, 1.5f); EXPECT(1.5); + + /* Skip rounding tests - no rounding is done */ +} + +static void test_VarR8FromBool(void) +{ + CONVVARS(VARIANT_BOOL); + + CHECKPTR(VarR8FromBool); + CONVERT(VarR8FromBool, VARIANT_TRUE); EXPECT(VARIANT_TRUE * 1.0); + CONVERT(VarR8FromBool, VARIANT_FALSE); EXPECT(VARIANT_FALSE * 1.0); +} + +static void test_VarR8FromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarR8FromCy); + CONVERT_CY(VarR8FromCy,-32769); EXPECT(-32769.0); + CONVERT_CY(VarR8FromCy,-32768); EXPECT(-32768.0); + CONVERT_CY(VarR8FromCy,-1); EXPECT(-1.0); + CONVERT_CY(VarR8FromCy,0); EXPECT(0.0); + CONVERT_CY(VarR8FromCy,1); EXPECT(1.0); + CONVERT_CY(VarR8FromCy,32767); EXPECT(32767.0); + CONVERT_CY(VarR8FromCy,32768); EXPECT(32768.0); + + CONVERT_CY(VarR8FromCy,-1.5); EXPECT(-1.5); + CONVERT_CY(VarR8FromCy,-0.6); EXPECT(-0.6); + CONVERT_CY(VarR8FromCy,-0.5); EXPECT(-0.5); + CONVERT_CY(VarR8FromCy,-0.4); EXPECT(-0.4); + CONVERT_CY(VarR8FromCy,0.4); EXPECT(0.4); + CONVERT_CY(VarR8FromCy,0.5); EXPECT(0.5); + CONVERT_CY(VarR8FromCy,0.6); EXPECT(0.6); + CONVERT_CY(VarR8FromCy,1.5); EXPECT(1.5); +} + +static void test_VarR8FromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarR8FromI8); + CONVERT(VarR8FromI8, -1); EXPECT(-1.0); + CONVERT(VarR8FromI8, 0); EXPECT(0.0); + CONVERT(VarR8FromI8, 1); EXPECT(1.0); +#if defined(__i386__) && (defined(_MSC_VER) || defined(__GNUC__)) + CONVERT_I8(VarR8FromI8, 0x7fffffff,0xffffffff); EXPECT(9223372036854775808.0); +#endif +} + +static void test_VarR8FromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarR8FromUI8); + CONVERT(VarR8FromUI8, 0); EXPECT(0.0); + CONVERT(VarR8FromUI8, 1); EXPECT(1.0); +#if defined(__i386__) && (defined(_MSC_VER) || defined(__GNUC__)) + CONVERT_I8(VarR8FromUI8, 0x80000000,0); EXPECT(9223372036854775808.0); +#endif +} + +static void test_VarR8FromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarR8FromDec); + + CONVERT_BADDEC(VarR8FromDec); + + CONVERT_DEC(VarR8FromDec,0,0x80,0,32768); EXPECT(-32768.0); + CONVERT_DEC(VarR8FromDec,0,0x80,0,1); EXPECT(-1.0); + CONVERT_DEC(VarR8FromDec,0,0,0,0); EXPECT(0.0); + CONVERT_DEC(VarR8FromDec,0,0,0,1); EXPECT(1.0); + CONVERT_DEC(VarR8FromDec,0,0,0,32767); EXPECT(32767.0); + + CONVERT_DEC(VarR8FromDec,2,0x80,0,3276800); EXPECT(-32768.0); + CONVERT_DEC(VarR8FromDec,2,0,0,3276700); EXPECT(32767.0); + + CONVERT_DEC(VarR8FromDec,0,0,1,0); EXPECT(18446744073709551616.0); +} + +static void test_VarR8FromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarR8FromDate); + CONVERT(VarR8FromDate, -1.0); EXPECT(-1.0); + CONVERT(VarR8FromDate, -0.0); EXPECT(0.0); + CONVERT(VarR8FromDate, 1.0); EXPECT(1.0); +} + +static void test_VarR8FromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + in = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarR8FromStr); + + CONVERT_STR(VarR8FromStr,NULL,0); EXPECT_MISMATCH; + CONVERT_STR(VarR8FromStr,"",0); EXPECT_MISMATCH; + CONVERT_STR(VarR8FromStr," ",0); EXPECT_MISMATCH; + + CONVERT_STR(VarR8FromStr,"0",LOCALE_NOUSEROVERRIDE); EXPECT(0.0); + CONVERT_STR(VarR8FromStr,"-1.5",LOCALE_NOUSEROVERRIDE); EXPECT(-1.5); + CONVERT_STR(VarR8FromStr,"-0.6",LOCALE_NOUSEROVERRIDE); EXPECT(-0.6); + CONVERT_STR(VarR8FromStr,"-0.5",LOCALE_NOUSEROVERRIDE); EXPECT(-0.5); + CONVERT_STR(VarR8FromStr,"-0.4",LOCALE_NOUSEROVERRIDE); EXPECT(-0.4); + CONVERT_STR(VarR8FromStr,"0.4",LOCALE_NOUSEROVERRIDE); EXPECT(0.4); + CONVERT_STR(VarR8FromStr,"0.5",LOCALE_NOUSEROVERRIDE); EXPECT(0.5); + CONVERT_STR(VarR8FromStr,"0.6",LOCALE_NOUSEROVERRIDE); EXPECT(0.6); + CONVERT_STR(VarR8FromStr,"1.5",LOCALE_NOUSEROVERRIDE); EXPECT(1.5); + + /* We already have exhaustive tests for number parsing, so skip those tests here */ +} + +static void test_VarR8Copy(void) +{ + COPYTEST(77665544.0, VT_R8, V_R8(&vSrc), V_R8(&vDst), V_R8REF(&vSrc),V_R8REF(&vDst), "%16.16g"); +} + +static void test_VarR8ChangeTypeEx(void) +{ +#ifdef HAS_UINT64_TO_FLOAT + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + + in = 1.0; + + INITIAL_TYPETEST(VT_R8, V_R8, "%g"); + COMMON_TYPETEST; +#endif +} + +#define MATHRND(l, r) left = l; right = r; hres = pVarR8Round(left, right, &out) + +static void test_VarR8Round(void) +{ + HRESULT hres; + double left = 0.0, out; + int right; + + CHECKPTR(VarR8Round); + MATHRND(0.5432, 5); EXPECT(0.5432); + MATHRND(0.5432, 4); EXPECT(0.5432); + MATHRND(0.5432, 3); EXPECT(0.543); + MATHRND(0.5432, 2); EXPECT(0.54); + MATHRND(0.5432, 1); EXPECT(0.5); + MATHRND(0.5532, 0); EXPECT(1); + MATHRND(0.5532, -1); EXPECT_INVALID; + + MATHRND(0.5568, 5); EXPECT(0.5568); + MATHRND(0.5568, 4); EXPECT(0.5568); + MATHRND(0.5568, 3); EXPECT(0.557); + MATHRND(0.5568, 2); EXPECT(0.56); + MATHRND(0.5568, 1); EXPECT(0.6); + MATHRND(0.5568, 0); EXPECT(1); + MATHRND(0.5568, -1); EXPECT_INVALID; + + MATHRND(0.4999, 0); EXPECT(0); + MATHRND(0.5000, 0); EXPECT(0); + MATHRND(0.5001, 0); EXPECT(1); + MATHRND(1.4999, 0); EXPECT(1); + MATHRND(1.5000, 0); EXPECT(2); + MATHRND(1.5001, 0); EXPECT(2); +} + +/* + * VT_DATE + */ + +#undef CONV_TYPE +#define CONV_TYPE DATE + +static void test_VarDateFromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarDateFromI1); + CONVERTRANGE(VarDateFromI1, -128, 128); +} + +static void test_VarDateFromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarDateFromUI1); + CONVERTRANGE(VarDateFromUI1, 0, 256); +} + +static void test_VarDateFromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarDateFromI2); + CONVERTRANGE(VarDateFromI2, -32768, 32768); +} + +static void test_VarDateFromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarDateFromUI2); + CONVERTRANGE(VarDateFromUI2, 0, 65536); +} + +static void test_VarDateFromI4(void) +{ + CONVVARS(int); + + CHECKPTR(VarDateFromI4); + CONVERT(VarDateFromI4, DATE_MIN-1); + if (hres != DISP_E_TYPEMISMATCH) /* Early versions return this, incorrectly */ + EXPECT_OVERFLOW; + CONVERT(VarDateFromI4, DATE_MIN); EXPECT(DATE_MIN); + CONVERT(VarDateFromI4, -1); EXPECT(-1.0); + CONVERT(VarDateFromI4, 0); EXPECT(0.0); + CONVERT(VarDateFromI4, 1); EXPECT(1.0); + CONVERT(VarDateFromI4, DATE_MAX); EXPECT(DATE_MAX); + CONVERT(VarDateFromI4, DATE_MAX+1); + if (hres != DISP_E_TYPEMISMATCH) /* Early versions return this, incorrectly */ + EXPECT_OVERFLOW; +} + +static void test_VarDateFromUI4(void) +{ + CONVVARS(unsigned int); + + CHECKPTR(VarDateFromUI4); + CONVERT(VarDateFromUI4, 0); EXPECT(0.0); + CONVERT(VarDateFromUI4, 1); EXPECT(1.0); + CONVERT(VarDateFromUI4, DATE_MAX); EXPECT(DATE_MAX); + CONVERT(VarDateFromUI4, DATE_MAX+1); + if (hres != DISP_E_TYPEMISMATCH) /* Early versions return this, incorrectly */ + EXPECT_OVERFLOW; +} + +static void test_VarDateFromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarDateFromR4); + CONVERT(VarDateFromR4, -1.0f); EXPECT(-1.0); + CONVERT(VarDateFromR4, 0.0f); EXPECT(0.0); + CONVERT(VarDateFromR4, 1.0f); EXPECT(1.0); + CONVERT(VarDateFromR4, 1.5f); EXPECT(1.5); +} + +static void test_VarDateFromR8(void) +{ + CONVVARS(double); + + CHECKPTR(VarDateFromR8); + CONVERT(VarDateFromR8, -1.0f); EXPECT(-1.0); + CONVERT(VarDateFromR8, 0.0f); EXPECT(0.0); + CONVERT(VarDateFromR8, 1.0f); EXPECT(1.0); + CONVERT(VarDateFromR8, 1.5f); EXPECT(1.5); +} + +static void test_VarDateFromBool(void) +{ + CONVVARS(VARIANT_BOOL); + + CHECKPTR(VarDateFromBool); + CONVERT(VarDateFromBool, VARIANT_TRUE); EXPECT(VARIANT_TRUE * 1.0); + CONVERT(VarDateFromBool, VARIANT_FALSE); EXPECT(VARIANT_FALSE * 1.0); +} + +static void test_VarDateFromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarDateFromCy); + CONVERT_CY(VarDateFromCy,-32769); EXPECT(-32769.0); + CONVERT_CY(VarDateFromCy,-32768); EXPECT(-32768.0); + CONVERT_CY(VarDateFromCy,-1); EXPECT(-1.0); + CONVERT_CY(VarDateFromCy,0); EXPECT(0.0); + CONVERT_CY(VarDateFromCy,1); EXPECT(1.0); + CONVERT_CY(VarDateFromCy,32767); EXPECT(32767.0); + CONVERT_CY(VarDateFromCy,32768); EXPECT(32768.0); + + CONVERT_CY(VarDateFromCy,-1.5); EXPECT(-1.5); + CONVERT_CY(VarDateFromCy,-0.6); EXPECT(-0.6); + CONVERT_CY(VarDateFromCy,-0.5); EXPECT(-0.5); + CONVERT_CY(VarDateFromCy,-0.4); EXPECT(-0.4); + CONVERT_CY(VarDateFromCy,0.4); EXPECT(0.4); + CONVERT_CY(VarDateFromCy,0.5); EXPECT(0.5); + CONVERT_CY(VarDateFromCy,0.6); EXPECT(0.6); + CONVERT_CY(VarDateFromCy,1.5); EXPECT(1.5); +} + +static void test_VarDateFromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarDateFromI8); + CONVERT(VarDateFromI8, DATE_MIN-1); EXPECT_OVERFLOW; + CONVERT(VarDateFromI8, DATE_MIN); EXPECT(DATE_MIN); + CONVERT(VarDateFromI8, -1); EXPECT(-1.0); + CONVERT(VarDateFromI8, 0); EXPECT(0.0); + CONVERT(VarDateFromI8, 1); EXPECT(1.0); + CONVERT(VarDateFromI8, DATE_MAX); EXPECT(DATE_MAX); + CONVERT(VarDateFromI8, DATE_MAX+1); EXPECT_OVERFLOW; +} + +static void test_VarDateFromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarDateFromUI8); + CONVERT(VarDateFromUI8, 0); EXPECT(0.0); + CONVERT(VarDateFromUI8, 1); EXPECT(1.0); + CONVERT(VarDateFromUI8, DATE_MAX); EXPECT(DATE_MAX); + CONVERT(VarDateFromUI8, DATE_MAX+1); EXPECT_OVERFLOW; +} + +static void test_VarDateFromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarDateFromDec); + + CONVERT_BADDEC(VarDateFromDec); + + CONVERT_DEC(VarDateFromDec,0,0x80,0,32768); EXPECT(-32768.0); + CONVERT_DEC(VarDateFromDec,0,0x80,0,1); EXPECT(-1.0); + CONVERT_DEC(VarDateFromDec,0,0,0,0); EXPECT(0.0); + CONVERT_DEC(VarDateFromDec,0,0,0,1); EXPECT(1.0); + CONVERT_DEC(VarDateFromDec,0,0,0,32767); EXPECT(32767.0); + + CONVERT_DEC(VarDateFromDec,2,0x80,0,3276800); EXPECT(-32768.0); + CONVERT_DEC(VarDateFromDec,2,0,0,3276700); EXPECT(32767.0); +} + +#define DFS(str) \ + buff[0] = '\0'; out = 0.0; \ + if (str) MultiByteToWideChar(CP_ACP,0,str,-1,buff,sizeof(buff)/sizeof(WCHAR)); \ + hres = pVarDateFromStr(str ? buff : NULL,lcid,LOCALE_NOUSEROVERRIDE,&out) + +#define MKRELDATE(day,mth) st.wMonth = mth; st.wDay = day; \ + pSystemTimeToVariantTime(&st,&relative) + +static const char * const BadDateStrings[] = +{ + "True", "False", /* Plain text */ + "0.", ".0", "-1.1", "1.1-", /* Partial specifications */ + "1;2;3", "1*2*3", "1@2@3", "1#2#3", "(1:2)","<1:2>","1|2|3", /* Bad chars */ + "0", "1", /* 1 element */ + "0.60", "24.00", "0:60", "24:00", "1 2 am", "1 am 2", /* 2 elements */ + "1.5 2", "1 5.2", "2 32 3", "1 2 am 3", /* 3 elements */ + "1 2.3 4", "1.2.3 4", "1 2.3.4", "1.2 3.4", "1.2.3.4", "1 2 3 4", + "1 am 2 3.4", "1 2 am 3.4", "1.2 3 am 4", "1.2 3 4 am", /* 4 elements */ + "1.2.3.4.5", "1.2.3.4 5", "1.2.3 4.5", "1.2 3.4.5", "1.2 3.4 5", "1.2 3 4.5", + "1 2.3.4.5", "1 2.3.4 5", "1 2.3 4.5", "1 2.3 4 5", "1 2 3.4 5", "1 2 3 4 5", + "1.2.3 4 am 5", "1.2.3 4 5 am", "1.2 3 am 4 5", + "1.2 3 4 am 5", "1.2 3 4 5 am", "1 am 2 3.4.5", "1 2 am 3.4.5", + "1 am 2 3 4.5", "1 2 am 3 4.5", "1 2 3 am 4.5", /* 5 elements */ + /* 6 elements */ + "1.2.3.4.5.6", "1.2.3.4.5 6", "1.2.3.4 5.6", "1.2.3.4 5 6", "1.2.3 4.5.6", + "1.2.3 4.5 6", "1.2.3 4 5.6", "1.2 3.4.5.6", "1.2 3.4.5 6", "1.2 3.4 5.6", + "1.2 3.4 5 6", "1.2 3 4.5.6", "1.2 3 4.5 6", "1.2 3 4 5.6", "1.2 3 4 5 6", + "1 2.3.4.5.6", "1 2.3.4.5 6", "1 2.3.4 5.6", "1 2.3.4 5 6", "1 2.3 4.5.6", +#if 0 + /* following throws an exception on winME */ + "1 2.3 4.5 6", "1 2.3 4 5.6", "1 2.3 4 5 6", "1 2 3.4.5.6", "1 2 3.4.5 6", +#endif + "1 2 3.4 5.6", "1 2 3.4 5 6", "1 2 3 4.5 6", "1 2 3 4 5.6", "1 2 3 4 5 6", +#if 0 + /* following throws an exception on winME */ + "1.2.3 4 am 5 6", "1.2.3 4 5 am 6", "1.2.3 4 5 6 am", "1 am 2 3 4.5.6", +#endif + "1 2 am 3 4.5.6", "1 2 3 am 4.5.6" +}; + +static void test_VarDateFromStr(void) +{ + LCID lcid; + DATE out, relative; + HRESULT hres; + SYSTEMTIME st; + OLECHAR buff[128]; + size_t i; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + CHECKPTR(VarDateFromStr); + CHECKPTR(SystemTimeToVariantTime); + + /* Some date formats are relative, so we need to find the cuurent year */ + GetSystemTime(&st); + st.wHour = st.wMinute = st.wSecond = st.wMilliseconds = 0; + DFS(NULL); EXPECT_MISMATCH; + + /* Floating point number are not recognised */ + DFS("0.0"); + if (hres == S_OK) + EXPECT_DBL(0.0); /* Very old versions accept this string */ + else + EXPECT_MISMATCH; + + /* 1 element - can only be a time, and only if it has am/pm */ + DFS("1 am"); EXPECT_DBL(0.04166666666666666); + /* 2 elements */ + /* A decimal point is treated as a time separator. + * The following are converted as hours/minutes. + */ + DFS("0.1"); EXPECT_DBL(0.0006944444444444445); + DFS("0.40"); EXPECT_DBL(0.02777777777777778); + DFS("2.5"); EXPECT_DBL(0.08680555555555555); + /* A colon acts as a decimal point */ + DFS("0:1"); EXPECT_DBL(0.0006944444444444445); + DFS("0:20"); EXPECT_DBL(0.01388888888888889); + DFS("0:40"); EXPECT_DBL(0.02777777777777778); + DFS("3:5"); EXPECT_DBL(0.1284722222222222); + /* Check the am/pm limits */ + DFS("00:00 AM"); EXPECT_DBL(0.0); + DFS("00:00 a"); EXPECT_DBL(0.0); + DFS("12:59 AM"); EXPECT_DBL(0.04097222222222222); + DFS("12:59 A"); EXPECT_DBL(0.04097222222222222); + DFS("00:00 pm"); EXPECT_DBL(0.5); + DFS("00:00 p"); EXPECT_DBL(0.5); + DFS("12:59 pm"); EXPECT_DBL(0.5409722222222222); + DFS("12:59 p"); EXPECT_DBL(0.5409722222222222); + /* AM/PM is ignored if hours > 12 */ + DFS("13:00 AM"); EXPECT_DBL(0.5416666666666666); + DFS("13:00 PM"); EXPECT_DBL(0.5416666666666666); + + /* Space, dash and slash all indicate a date format. */ + /* If both numbers are valid month values => month/day of current year */ + DFS("1 2"); MKRELDATE(2,1); EXPECT_DBL(relative); + DFS("2 1"); MKRELDATE(1,2); EXPECT_DBL(relative); + /* one number not valid month, is a valid day, other number valid month: + * that number becomes the day. + */ + DFS("14 1"); MKRELDATE(14,1); EXPECT_DBL(relative); + DFS("1 14"); EXPECT_DBL(relative); + /* If the numbers can't be day/month, they are assumed to be year/month */ + DFS("30 2"); EXPECT_DBL(10990.0); + DFS("2 30"); EXPECT_DBL(10990.0); + DFS("32 49"); EXPECT_MISMATCH; /* Can't be any format */ + DFS("0 49"); EXPECT_MISMATCH; /* Can't be any format */ + /* If a month name is given the other number is the day */ + DFS("Jan 2"); MKRELDATE(2,1); EXPECT_DBL(relative); + DFS("2 Jan"); EXPECT_DBL(relative); + /* Unless it can't be, in which case it becomes the year */ + DFS("Jan 35"); EXPECT_DBL(12785.0); + DFS("35 Jan"); EXPECT_DBL(12785.0); + DFS("Jan-35"); EXPECT_DBL(12785.0); + DFS("35-Jan"); EXPECT_DBL(12785.0); + DFS("Jan/35"); EXPECT_DBL(12785.0); + DFS("35/Jan"); EXPECT_DBL(12785.0); + /* 3 elements */ + /* 3 numbers and time separator => h:m:s */ + DFS("0.1.0"); EXPECT_DBL(0.0006944444444444445); + DFS("1.5.2"); EXPECT_DBL(0.04516203703703704); + /* 3 numbers => picks date giving preference to lcid format */ + DFS("1 2 3"); EXPECT_DBL(37623.0); + DFS("14 2 3"); EXPECT_DBL(41673.0); + DFS("2 14 3"); EXPECT_DBL(37666.0); + DFS("2 3 14"); EXPECT_DBL(41673.0); + DFS("32 2 3"); EXPECT_DBL(11722.0); + DFS("2 3 32"); EXPECT_DBL(11722.0); + DFS("1 2 29"); EXPECT_DBL(47120.0); + /* After 30, two digit dates are expected to be in the 1900's */ + DFS("1 2 30"); EXPECT_DBL(10960.0); + DFS("1 2 31"); EXPECT_DBL(11325.0); + DFS("3 am 1 2"); MKRELDATE(2,1); relative += 0.125; EXPECT_DBL(relative); + DFS("1 2 3 am"); EXPECT_DBL(relative); + + /* 4 elements -interpreted as 2 digit date & time */ + DFS("1.2 3 4"); MKRELDATE(4,3); relative += 0.04305555556; EXPECT_DBL(relative); + DFS("3 4 1.2"); EXPECT_DBL(relative); + /* 5 elements - interpreted as 2 & 3 digit date/times */ + DFS("1.2.3 4 5"); MKRELDATE(5,4); relative += 0.04309027778; EXPECT_DBL(relative); + DFS("1.2 3 4 5"); EXPECT_DBL(38415.04305555556); +#if 0 + /* following throws an exception on winME */ + DFS("1 2 3.4.5"); MKRELDATE(2,1); relative += 0.12783564815; EXPECT_DBL(relative); +#endif + DFS("1 2 3 4.5"); EXPECT_DBL(37623.17013888889); + /* 6 elements - interpreted as 3 digit date/times */ + DFS("1.2.3 4 5 6"); EXPECT_DBL(38812.04309027778); + DFS("1 2 3 4.5.6"); EXPECT_DBL(37623.17020833334); + + for (i = 0; i < sizeof(BadDateStrings)/sizeof(char*); i++) + { + DFS(BadDateStrings[i]); EXPECT_MISMATCH; + } + + /* Some normal-ish strings */ + DFS("2 January, 1970"); EXPECT_DBL(25570.0); + DFS("2 January 1970"); EXPECT_DBL(25570.0); + DFS("2 Jan 1970"); EXPECT_DBL(25570.0); + DFS("2/Jan/1970"); EXPECT_DBL(25570.0); + DFS("2-Jan-1970"); EXPECT_DBL(25570.0); + DFS("1 2 1970"); EXPECT_DBL(25570.0); + DFS("1/2/1970"); EXPECT_DBL(25570.0); + DFS("1-2-1970"); EXPECT_DBL(25570.0); + /* Native fails "1999 January 3, 9AM". I consider that a bug in native */ +} + +static void test_VarDateCopy(void) +{ + COPYTEST(77665544.0, VT_DATE, V_DATE(&vSrc), V_DATE(&vDst), V_DATEREF(&vSrc), + V_DATEREF(&vDst), "%16.16g"); +} + +static const char* wtoascii(LPWSTR lpszIn) +{ + static char buff[256]; + WideCharToMultiByte(CP_ACP, 0, lpszIn, -1, buff, sizeof(buff), NULL, NULL); + return buff; +} + +static void test_VarDateChangeTypeEx(void) +{ + static const WCHAR sz25570[] = { + '1','/','2','/','1','9','7','0','\0' }; + static const WCHAR sz25570_2[] = { + '1','/','2','/','7','0','\0' }; + static const WCHAR sz25570Nls[] = { + '1','/','2','/','1','9','7','0',' ','1','2',':','0','0',':','0','0',' ','A','M','\0' }; + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + LCID lcid; + + in = 1.0; + +#ifdef HAS_UINT64_TO_FLOAT + INITIAL_TYPETEST(VT_DATE, V_DATE, "%g"); + COMMON_TYPETEST; +#endif + + V_VT(&vDst) = VT_EMPTY; + V_VT(&vSrc) = VT_DATE; + V_DATE(&vSrc) = 25570.0; + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, VARIANT_NOUSEROVERRIDE, VT_BSTR); + ok(hres == S_OK && V_VT(&vDst) == VT_BSTR && V_BSTR(&vDst) && + (!lstrcmpW(V_BSTR(&vDst), sz25570) || !lstrcmpW(V_BSTR(&vDst), sz25570_2)), + "hres=0x%X, type=%d (should be VT_BSTR), *bstr=%s\n", + hres, V_VT(&vDst), V_BSTR(&vDst) ? wtoascii(V_BSTR(&vDst)) : "?"); + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + if (HAVE_OLEAUT32_LOCALES) + { + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, VARIANT_NOUSEROVERRIDE|VARIANT_USE_NLS, VT_BSTR); + ok(hres == S_OK && V_VT(&vDst) == VT_BSTR && V_BSTR(&vDst) && !lstrcmpW(V_BSTR(&vDst), sz25570Nls), + "hres=0x%X, type=%d (should be VT_BSTR), *bstr=%s\n", + hres, V_VT(&vDst), V_BSTR(&vDst) ? wtoascii(V_BSTR(&vDst)) : "?"); + } +} + +/* + * VT_CY + */ + +#undef CONV_TYPE +#define CONV_TYPE CY +#undef EXPECTRES +#define EXPECTRES(res, x) \ + ok(hres == S_OK || ((HRESULT)res != S_OK && hres == (HRESULT)res), \ + "expected hres " #x ", got hres=0x%08x\n", hres) + +#define EXPECTCY(x) \ + ok((hres == S_OK && out.int64 == (LONGLONG)(x*CY_MULTIPLIER)), \ + "expected " #x "*CY_MULTIPLIER, got (%8x %8x); hres=0x%08x\n", S(out).Hi, S(out).Lo, hres) + +#define EXPECTCY64(x,y) \ + ok(hres == S_OK && S(out).Hi == (LONG)x && S(out).Lo == y, \ + "expected " #x #y "(%u,%u), got (%u,%u); hres=0x%08x\n", \ + (ULONG)(x), (ULONG)(y), S(out).Hi, S(out).Lo, hres) + +static void test_VarCyFromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarCyFromI1); + for (i = -128; i < 128; i++) + { + CONVERT(VarCyFromI1,i); EXPECTCY(i); + } +} + +static void test_VarCyFromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarCyFromUI1); + for (i = 0; i < 256; i++) + { + CONVERT(VarCyFromUI1,i); EXPECTCY(i); + } +} + +static void test_VarCyFromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarCyFromI2); + for (i = -16384; i < 16384; i++) + { + CONVERT(VarCyFromI2,i); EXPECTCY(i); + } +} + +static void test_VarCyFromUI2(void) +{ + CONVVARS(int); + int i; + + CHECKPTR(VarCyFromUI2); + for (i = 0; i < 32768; i++) + { + CONVERT(VarCyFromUI2,i); EXPECTCY(i); + } +} + +static void test_VarCyFromI4(void) +{ + CONVVARS(int); + + CHECKPTR(VarCyFromI4); + CONVERT(VarCyFromI4, -1); EXPECTCY(-1); + CONVERT(VarCyFromI4, 0); EXPECTCY(0); + CONVERT(VarCyFromI4, 1); EXPECTCY(1); + CONVERT(VarCyFromI4, 0x7fffffff); EXPECTCY64(0x1387, 0xffffd8f0); + CONVERT(VarCyFromI4, 0x80000000); EXPECTCY64(0xffffec78, 0); +} + +static void test_VarCyFromUI4(void) +{ + CONVVARS(unsigned int); + + CHECKPTR(VarCyFromUI4); + CONVERT(VarCyFromUI4, 0); EXPECTCY(0); + CONVERT(VarCyFromUI4, 1); EXPECTCY(1); + CONVERT(VarCyFromUI4, 0x80000000); EXPECTCY64(5000, 0); +} + +static void test_VarCyFromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarCyFromR4); + CONVERT(VarCyFromR4, -1.0f); EXPECTCY(-1); + CONVERT(VarCyFromR4, 0.0f); EXPECTCY(0); + CONVERT(VarCyFromR4, 1.0f); EXPECTCY(1); + CONVERT(VarCyFromR4, 1.5f); EXPECTCY(1.5); + + CONVERT(VarCyFromR4, -1.5f); EXPECTCY(-1.5); + CONVERT(VarCyFromR4, -0.6f); EXPECTCY(-0.6); + CONVERT(VarCyFromR4, -0.5f); EXPECTCY(-0.5); + CONVERT(VarCyFromR4, -0.4f); EXPECTCY(-0.4); + CONVERT(VarCyFromR4, 0.4f); EXPECTCY(0.4); + CONVERT(VarCyFromR4, 0.5f); EXPECTCY(0.5); + CONVERT(VarCyFromR4, 0.6f); EXPECTCY(0.6); + CONVERT(VarCyFromR4, 1.5f); EXPECTCY(1.5); + CONVERT(VarCyFromR4, 1.00009f); EXPECTCY(1.0001); + CONVERT(VarCyFromR4, -1.00001f); EXPECTCY(-1); + CONVERT(VarCyFromR4, -1.00005f); EXPECTCY(-1); + CONVERT(VarCyFromR4, -0.00009f); EXPECTCY(-0.0001); + CONVERT(VarCyFromR4, -0.00005f); EXPECTCY(0); + CONVERT(VarCyFromR4, -0.00001f); EXPECTCY(0); + CONVERT(VarCyFromR4, 0.00001f); EXPECTCY(0); + CONVERT(VarCyFromR4, 0.00005f); EXPECTCY(0); + CONVERT(VarCyFromR4, 0.00009f); EXPECTCY(0.0001); + CONVERT(VarCyFromR4, -1.00001f); EXPECTCY(-1); + CONVERT(VarCyFromR4, -1.00005f); EXPECTCY(-1); + CONVERT(VarCyFromR4, -1.00009f); EXPECTCY(-1.0001); +} + +static void test_VarCyFromR8(void) +{ + CONVVARS(DOUBLE); + + CHECKPTR(VarCyFromR8); + +#if defined(__i386__) && (defined(_MSC_VER) || defined(__GNUC__)) + /* Test our rounding is exactly the same. This fails if the special x86 + * code is taken out of VarCyFromR8. + */ + CONVERT(VarCyFromR8, -461168601842738.7904); EXPECTCY64(0xbfffffff, 0xffffff23); +#endif + + CONVERT(VarCyFromR8, -4611686018427388416.1); EXPECT_OVERFLOW; + CONVERT(VarCyFromR8, -1.0); EXPECTCY(-1); + CONVERT(VarCyFromR8, -0.0); EXPECTCY(0); + CONVERT(VarCyFromR8, 1.0); EXPECTCY(1); + CONVERT(VarCyFromR8, 4611686018427387648.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarCyFromR8, -1.5f); EXPECTCY(-1.5); + CONVERT(VarCyFromR8, -0.6f); EXPECTCY(-0.6); + CONVERT(VarCyFromR8, -0.5f); EXPECTCY(-0.5); + CONVERT(VarCyFromR8, -0.4f); EXPECTCY(-0.4); + CONVERT(VarCyFromR8, 0.4f); EXPECTCY(0.4); + CONVERT(VarCyFromR8, 0.5f); EXPECTCY(0.5); + CONVERT(VarCyFromR8, 0.6f); EXPECTCY(0.6); + CONVERT(VarCyFromR8, 1.5f); EXPECTCY(1.5); + CONVERT(VarCyFromR8, 1.00009f); EXPECTCY(1.0001); + CONVERT(VarCyFromR8, -1.00001f); EXPECTCY(-1); + CONVERT(VarCyFromR8, -1.00005f); EXPECTCY(-1); + CONVERT(VarCyFromR8, -0.00009f); EXPECTCY(-0.0001); + CONVERT(VarCyFromR8, -0.00005f); EXPECTCY(0); + CONVERT(VarCyFromR8, -0.00001f); EXPECTCY(0); + CONVERT(VarCyFromR8, 0.00001f); EXPECTCY(0); + CONVERT(VarCyFromR8, 0.00005f); EXPECTCY(0); + CONVERT(VarCyFromR8, 0.00009f); EXPECTCY(0.0001); + CONVERT(VarCyFromR8, -1.00001f); EXPECTCY(-1); + CONVERT(VarCyFromR8, -1.00005f); EXPECTCY(-1); + CONVERT(VarCyFromR8, -1.00009f); EXPECTCY(-1.0001); +} + +static void test_VarCyFromBool(void) +{ + CONVVARS(VARIANT_BOOL); + int i; + + CHECKPTR(VarCyFromBool); + for (i = -32768; i < 32768; i++) + { + CONVERT(VarCyFromBool, i); EXPECTCY(i); + } +} + +static void test_VarCyFromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarCyFromI8); + CONVERT_I8(VarCyFromI8, -214749, 2728163227ul); EXPECT_OVERFLOW; + CONVERT_I8(VarCyFromI8, -214749, 2728163228ul); EXPECTCY64(2147483648ul,15808); + CONVERT(VarCyFromI8, -1); EXPECTCY(-1); + CONVERT(VarCyFromI8, 0); EXPECTCY(0); + CONVERT(VarCyFromI8, 1); EXPECTCY(1); + CONVERT_I8(VarCyFromI8, 214748, 1566804068); EXPECTCY64(2147483647ul, 4294951488ul); + CONVERT_I8(VarCyFromI8, 214748, 1566804069); EXPECT_OVERFLOW; +} + +static void test_VarCyFromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarCyFromUI8); + CONVERT(VarCyFromUI8, 0); EXPECTCY(0); + CONVERT(VarCyFromUI8, 1); EXPECTCY(1); + CONVERT_I8(VarCyFromUI8, 214748, 1566804068); EXPECTCY64(2147483647ul, 4294951488ul); + CONVERT_I8(VarCyFromUI8, 214748, 1566804069); EXPECT_OVERFLOW; +} + +static void test_VarCyFromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarCyFromDec); + + CONVERT_BADDEC(VarCyFromDec); + + CONVERT_DEC(VarCyFromDec,0,0x80,0,1); EXPECTCY(-1); + CONVERT_DEC(VarCyFromDec,0,0,0,0); EXPECTCY(0); + CONVERT_DEC(VarCyFromDec,0,0,0,1); EXPECTCY(1); + + CONVERT_DEC64(VarCyFromDec,0,0,0,214748, 1566804068); EXPECTCY64(2147483647ul, 4294951488ul); + CONVERT_DEC64(VarCyFromDec,0,0,0,214748, 1566804069); EXPECT_OVERFLOW; + + CONVERT_DEC(VarCyFromDec,2,0,0,100); EXPECTCY(1); + CONVERT_DEC(VarCyFromDec,2,0x80,0,100); EXPECTCY(-1); + CONVERT_DEC(VarCyFromDec,2,0x80,0,1); EXPECTCY(-0.01); + CONVERT_DEC(VarCyFromDec,2,0,0,1); EXPECTCY(0.01); + CONVERT_DEC(VarCyFromDec,2,0x80,0,1); EXPECTCY(-0.01); + CONVERT_DEC(VarCyFromDec,2,0,0,999); EXPECTCY(9.99); + CONVERT_DEC(VarCyFromDec,2,0x80,0,999); EXPECTCY(-9.99); + CONVERT_DEC(VarCyFromDec,2,0,0,1500); EXPECTCY(15); + CONVERT_DEC(VarCyFromDec,2,0x80,0,1500); EXPECTCY(-15); +} + +static void test_VarCyFromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarCyFromDate); + +#if defined(__i386__) && (defined(_MSC_VER) || defined(__GNUC__)) + CONVERT(VarCyFromR8, -461168601842738.7904); EXPECTCY64(0xbfffffff, 0xffffff23); +#endif + + CONVERT(VarCyFromDate, -1.0); EXPECTCY(-1); + CONVERT(VarCyFromDate, -0.0); EXPECTCY(0); + CONVERT(VarCyFromDate, 1.0); EXPECTCY(1); + CONVERT(VarCyFromDate, -4611686018427388416.1); EXPECT_OVERFLOW; + CONVERT(VarCyFromDate, 4611686018427387648.0); EXPECT_OVERFLOW; + + /* Rounding */ + CONVERT(VarCyFromDate, -1.5f); EXPECTCY(-1.5); + CONVERT(VarCyFromDate, -0.6f); EXPECTCY(-0.6); + CONVERT(VarCyFromDate, -0.5f); EXPECTCY(-0.5); + CONVERT(VarCyFromDate, -0.4f); EXPECTCY(-0.4); + CONVERT(VarCyFromDate, 0.4f); EXPECTCY(0.4); + CONVERT(VarCyFromDate, 0.5f); EXPECTCY(0.5); + CONVERT(VarCyFromDate, 0.6f); EXPECTCY(0.6); + CONVERT(VarCyFromDate, 1.5f); EXPECTCY(1.5); + CONVERT(VarCyFromDate, 1.00009f); EXPECTCY(1.0001); + CONVERT(VarCyFromDate, -1.00001f); EXPECTCY(-1); + CONVERT(VarCyFromDate, -1.00005f); EXPECTCY(-1); + CONVERT(VarCyFromDate, -0.00009f); EXPECTCY(-0.0001); + CONVERT(VarCyFromDate, -0.00005f); EXPECTCY(0); + CONVERT(VarCyFromDate, -0.00001f); EXPECTCY(0); + CONVERT(VarCyFromDate, 0.00001f); EXPECTCY(0); + CONVERT(VarCyFromDate, 0.00005f); EXPECTCY(0); + CONVERT(VarCyFromDate, 0.00009f); EXPECTCY(0.0001); + CONVERT(VarCyFromDate, -1.00001f); EXPECTCY(-1); + CONVERT(VarCyFromDate, -1.00005f); EXPECTCY(-1); + CONVERT(VarCyFromDate, -1.00009f); EXPECTCY(-1.0001); +} + +#define MATHVARS1 HRESULT hres; double left = 0.0; CY cyLeft, out +#define MATHVARS2 MATHVARS1; double right = 0.0; CY cyRight +#define MATH1(func, l) left = (double)l; pVarCyFromR8(left, &cyLeft); hres = p##func(cyLeft, &out) +#define MATH2(func, l, r) left = (double)l; right = (double)r; \ + pVarCyFromR8(left, &cyLeft); pVarCyFromR8(right, &cyRight); \ + hres = p##func(cyLeft, cyRight, &out) + +static void test_VarCyAdd(void) +{ + MATHVARS2; + + CHECKPTR(VarCyAdd); + MATH2(VarCyAdd, 0.5, 0.5); EXPECTCY(1); + MATH2(VarCyAdd, 0.5, -0.4); EXPECTCY(0.1); + MATH2(VarCyAdd, 0.5, -0.6); EXPECTCY(-0.1); + MATH2(VarCyAdd, -0.5, -0.5); EXPECTCY(-1); + MATH2(VarCyAdd, -922337203685476.0, -922337203685476.0); EXPECT_OVERFLOW; + MATH2(VarCyAdd, -922337203685476.0, 922337203685476.0); EXPECTCY(0); + MATH2(VarCyAdd, 922337203685476.0, -922337203685476.0); EXPECTCY(0); + MATH2(VarCyAdd, 922337203685476.0, 922337203685476.0); EXPECT_OVERFLOW; +} + +static void test_VarCyMul(void) +{ + MATHVARS2; + + CHECKPTR(VarCyMul); + MATH2(VarCyMul, 534443.0, 0.0); EXPECTCY(0); + MATH2(VarCyMul, 0.5, 0.5); EXPECTCY(0.25); + MATH2(VarCyMul, 0.5, -0.4); EXPECTCY(-0.2); + MATH2(VarCyMul, 0.5, -0.6); EXPECTCY(-0.3); + MATH2(VarCyMul, -0.5, -0.5); EXPECTCY(0.25); + MATH2(VarCyMul, 922337203685476.0, 20000); EXPECT_OVERFLOW; +} + +static void test_VarCySub(void) +{ + MATHVARS2; + + CHECKPTR(VarCySub); + MATH2(VarCySub, 0.5, 0.5); EXPECTCY(0); + MATH2(VarCySub, 0.5, -0.4); EXPECTCY(0.9); + MATH2(VarCySub, 0.5, -0.6); EXPECTCY(1.1); + MATH2(VarCySub, -0.5, -0.5); EXPECTCY(0); + MATH2(VarCySub, -922337203685476.0, -922337203685476.0); EXPECTCY(0); + MATH2(VarCySub, -922337203685476.0, 922337203685476.0); EXPECT_OVERFLOW; + MATH2(VarCySub, 922337203685476.0, -922337203685476.0); EXPECT_OVERFLOW; + MATH2(VarCySub, 922337203685476.0, 922337203685476.0); EXPECTCY(0); +} + +static void test_VarCyAbs(void) +{ + MATHVARS1; + + CHECKPTR(VarCyAbs); + MATH1(VarCyAbs, 0.5); EXPECTCY(0.5); + MATH1(VarCyAbs, -0.5); EXPECTCY(0.5); + MATH1(VarCyAbs, 922337203685476.0); EXPECTCY64(2147483647ul,4294951488ul); + MATH1(VarCyAbs, -922337203685476.0); EXPECTCY64(2147483647ul,4294951488ul); +} + +static void test_VarCyNeg(void) +{ + MATHVARS1; + + CHECKPTR(VarCyNeg); + MATH1(VarCyNeg, 0.5); EXPECTCY(-0.5); + MATH1(VarCyNeg, -0.5); EXPECTCY(0.5); + MATH1(VarCyNeg, 922337203685476.0); EXPECTCY64(2147483648ul,15808); + MATH1(VarCyNeg, -922337203685476.0); EXPECTCY64(2147483647ul,4294951488ul); +} + +#define MATHMULI4(l, r) left = l; right = r; pVarCyFromR8(left, &cyLeft); \ + hres = pVarCyMulI4(cyLeft, right, &out) + +static void test_VarCyMulI4(void) +{ + MATHVARS1; + LONG right; + + CHECKPTR(VarCyMulI4); + MATHMULI4(534443.0, 0); EXPECTCY(0); + MATHMULI4(0.5, 1); EXPECTCY(0.5); + MATHMULI4(0.5, 2); EXPECTCY(1); + MATHMULI4(922337203685476.0, 1); EXPECTCY64(2147483647ul,4294951488ul); + MATHMULI4(922337203685476.0, 2); EXPECT_OVERFLOW; +} + +#define MATHMULI8(l, r) left = l; right = r; pVarCyFromR8(left, &cyLeft); \ + hres = pVarCyMulI8(cyLeft, right, &out) + +static void test_VarCyMulI8(void) +{ + MATHVARS1; + LONG64 right; + + CHECKPTR(VarCyMulI8); + MATHMULI8(534443.0, 0); EXPECTCY(0); + MATHMULI8(0.5, 1); EXPECTCY(0.5); + MATHMULI8(0.5, 2); EXPECTCY(1); + MATHMULI8(922337203685476.0, 1); EXPECTCY64(2147483647ul,4294951488ul); + MATHMULI8(922337203685476.0, 2); EXPECT_OVERFLOW; +} + +#define MATHCMP(l, r) left = l; right = r; pVarCyFromR8(left, &cyLeft); pVarCyFromR8(right, &cyRight); \ + hres = pVarCyCmp(cyLeft, cyRight); out.int64 = hres + +static void test_VarCyCmp(void) +{ + MATHVARS2; + + CHECKPTR(VarCyCmp); + MATHCMP(-1.0, -1.0); EXPECT_EQ; + MATHCMP(-1.0, 0.0); EXPECT_LT; + MATHCMP(-1.0, 1.0); EXPECT_LT; + MATHCMP(-1.0, 2.0); EXPECT_LT; + MATHCMP(0.0, 1.0); EXPECT_LT; + MATHCMP(0.0, 0.0); EXPECT_EQ; + MATHCMP(0.0, -1.0); EXPECT_GT; + MATHCMP(1.0, -1.0); EXPECT_GT; + MATHCMP(1.0, 0.0); EXPECT_GT; + MATHCMP(1.0, 1.0); EXPECT_EQ; + MATHCMP(1.0, 2.0); EXPECT_LT; +} + +#define MATHCMPR8(l, r) left = l; right = r; pVarCyFromR8(left, &cyLeft); \ + hres = pVarCyCmpR8(cyLeft, right); out.int64 = hres + +static void test_VarCyCmpR8(void) +{ + MATHVARS1; + double right; + + CHECKPTR(VarCyCmpR8); + MATHCMPR8(-1.0, -1.0); EXPECT_EQ; + MATHCMPR8(-1.0, 0.0); EXPECT_LT; + MATHCMPR8(-1.0, 1.0); EXPECT_LT; + MATHCMPR8(-1.0, 2.0); EXPECT_LT; + MATHCMPR8(0.0, 1.0); EXPECT_LT; + MATHCMPR8(0.0, 0.0); EXPECT_EQ; + MATHCMPR8(0.0, -1.0); EXPECT_GT; + MATHCMPR8(1.0, -1.0); EXPECT_GT; + MATHCMPR8(1.0, 0.0); EXPECT_GT; + MATHCMPR8(1.0, 1.0); EXPECT_EQ; + MATHCMPR8(1.0, 2.0); EXPECT_LT; +} + +#undef MATHRND +#define MATHRND(l, r) left = l; right = r; pVarCyFromR8(left, &cyLeft); \ + hres = pVarCyRound(cyLeft, right, &out) + +static void test_VarCyRound(void) +{ + MATHVARS1; + int right; + + CHECKPTR(VarCyRound); + MATHRND(0.5432, 5); EXPECTCY(0.5432); + MATHRND(0.5432, 4); EXPECTCY(0.5432); + MATHRND(0.5432, 3); EXPECTCY(0.543); + MATHRND(0.5432, 2); EXPECTCY(0.54); + MATHRND(0.5432, 1); EXPECTCY(0.5); + MATHRND(0.5532, 0); EXPECTCY(1); + MATHRND(0.5532, -1); EXPECT_INVALID; + + MATHRND(0.5568, 5); EXPECTCY(0.5568); + MATHRND(0.5568, 4); EXPECTCY(0.5568); + MATHRND(0.5568, 3); EXPECTCY(0.557); + MATHRND(0.5568, 2); EXPECTCY(0.56); + MATHRND(0.5568, 1); EXPECTCY(0.6); + MATHRND(0.5568, 0); EXPECTCY(1); + MATHRND(0.5568, -1); EXPECT_INVALID; + + MATHRND(0.4999, 0); EXPECTCY(0); + MATHRND(0.5000, 0); EXPECTCY(0); + MATHRND(0.5001, 0); EXPECTCY(1); + MATHRND(1.4999, 0); EXPECTCY(1); + MATHRND(1.5000, 0); EXPECTCY(2); + MATHRND(1.5001, 0); EXPECTCY(2); +} + +#define MATHFIX(l) left = l; pVarCyFromR8(left, &cyLeft); \ + hres = pVarCyFix(cyLeft, &out) + +static void test_VarCyFix(void) +{ + MATHVARS1; + + CHECKPTR(VarCyFix); + MATHFIX(-1.0001); EXPECTCY(-1); + MATHFIX(-1.4999); EXPECTCY(-1); + MATHFIX(-1.5001); EXPECTCY(-1); + MATHFIX(-1.9999); EXPECTCY(-1); + MATHFIX(-0.0001); EXPECTCY(0); + MATHFIX(-0.4999); EXPECTCY(0); + MATHFIX(-0.5001); EXPECTCY(0); + MATHFIX(-0.9999); EXPECTCY(0); + MATHFIX(0.0001); EXPECTCY(0); + MATHFIX(0.4999); EXPECTCY(0); + MATHFIX(0.5001); EXPECTCY(0); + MATHFIX(0.9999); EXPECTCY(0); + MATHFIX(1.0001); EXPECTCY(1); + MATHFIX(1.4999); EXPECTCY(1); + MATHFIX(1.5001); EXPECTCY(1); + MATHFIX(1.9999); EXPECTCY(1); +} + +#define MATHINT(l) left = l; pVarCyFromR8(left, &cyLeft); \ + hres = pVarCyInt(cyLeft, &out) + +static void test_VarCyInt(void) +{ + MATHVARS1; + + CHECKPTR(VarCyInt); + MATHINT(-1.0001); EXPECTCY(-2); + MATHINT(-1.4999); EXPECTCY(-2); + MATHINT(-1.5001); EXPECTCY(-2); + MATHINT(-1.9999); EXPECTCY(-2); + MATHINT(-0.0001); EXPECTCY(-1); + MATHINT(-0.4999); EXPECTCY(-1); + MATHINT(-0.5001); EXPECTCY(-1); + MATHINT(-0.9999); EXPECTCY(-1); + MATHINT(0.0001); EXPECTCY(0); + MATHINT(0.4999); EXPECTCY(0); + MATHINT(0.5001); EXPECTCY(0); + MATHINT(0.9999); EXPECTCY(0); + MATHINT(1.0001); EXPECTCY(1); + MATHINT(1.4999); EXPECTCY(1); + MATHINT(1.5001); EXPECTCY(1); + MATHINT(1.9999); EXPECTCY(1); +} + +/* + * VT_DECIMAL + */ + +#undef CONV_TYPE +#define CONV_TYPE DECIMAL +#undef EXPECTRES +#define EXPECTRES(res, x) \ + ok(hres == S_OK || ((HRESULT)res != S_OK && hres == (HRESULT)res), \ + "expected hres " #x ", got hres=0x%08x\n", hres) + +#define EXPECTDEC(scl, sgn, hi, lo) ok(hres == S_OK && \ + S(U(out)).scale == (BYTE)(scl) && S(U(out)).sign == (BYTE)(sgn) && \ + out.Hi32 == (ULONG)(hi) && U1(out).Lo64 == (ULONG64)(lo), \ + "expected (%d,%d,%d,(%x %x)), got (%d,%d,%d,(%x %x)) hres 0x%08x\n", \ + scl, sgn, hi, (LONG)((LONG64)(lo) >> 32), (LONG)((lo) & 0xffffffff), S(U(out)).scale, \ + S(U(out)).sign, out.Hi32, S1(U1(out)).Mid32, S1(U1(out)).Lo32, hres) + +#define EXPECTDEC64(scl, sgn, hi, mid, lo) ok(hres == S_OK && \ + S(U(out)).scale == (BYTE)(scl) && S(U(out)).sign == (BYTE)(sgn) && \ + out.Hi32 == (ULONG)(hi) && S1(U1(out)).Mid32 == (ULONG)(mid) && \ + S1(U1(out)).Lo32 == (ULONG)(lo), \ + "expected (%d,%d,%d,(%x %x)), got (%d,%d,%d,(%x %x)) hres 0x%08x\n", \ + scl, sgn, hi, (LONG)(mid), (LONG)(lo), S(U(out)).scale, \ + S(U(out)).sign, out.Hi32, S1(U1(out)).Mid32, S1(U1(out)).Lo32, hres) + +#define EXPECTDECI if (i < 0) EXPECTDEC(0, 0x80, 0, -i); else EXPECTDEC(0, 0, 0, i) + +static void test_VarDecFromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarDecFromI1); + for (i = -128; i < 128; i++) + { + CONVERT(VarDecFromI1,i); EXPECTDECI; + } +} + +static void test_VarDecFromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarDecFromI2); + for (i = -32768; i < 32768; i++) + { + CONVERT(VarDecFromI2,i); EXPECTDECI; + } +} + +static void test_VarDecFromI4(void) +{ + CONVVARS(LONG); + int i; + + CHECKPTR(VarDecFromI4); + for (i = -32768; i < 32768; i++) + { + CONVERT(VarDecFromI4,i); EXPECTDECI; + } +} + +static void test_VarDecFromI8(void) +{ + CONVVARS(LONG64); + int i; + + CHECKPTR(VarDecFromI8); + for (i = -32768; i < 32768; i++) + { + CONVERT(VarDecFromI8,i); EXPECTDECI; + } +} + +static void test_VarDecFromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarDecFromUI1); + for (i = 0; i < 256; i++) + { + CONVERT(VarDecFromUI1,i); EXPECTDECI; + } +} + +static void test_VarDecFromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarDecFromUI2); + for (i = 0; i < 65536; i++) + { + CONVERT(VarDecFromUI2,i); EXPECTDECI; + } +} + +static void test_VarDecFromUI4(void) +{ + CONVVARS(ULONG); + int i; + + CHECKPTR(VarDecFromUI4); + for (i = 0; i < 65536; i++) + { + CONVERT(VarDecFromUI4,i); EXPECTDECI; + } +} + +static void test_VarDecFromUI8(void) +{ + CONVVARS(ULONG64); + int i; + + CHECKPTR(VarDecFromUI8); + for (i = 0; i < 65536; i++) + { + CONVERT(VarDecFromUI8,i); EXPECTDECI; + } +} + +static void test_VarDecFromBool(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarDecFromBool); + /* Test all possible type values. Note that the result is reduced to 0 or -1 */ + for (i = -32768; i < 0; i++) + { + CONVERT(VarDecFromBool,i); + if (i) + EXPECTDEC(0,0x80,0,1); + else + EXPECTDEC(0,0,0,0); + } +} + +static void test_VarDecFromR4(void) +{ + CONVVARS(float); + + CHECKPTR(VarDecFromR4); + + CONVERT(VarDecFromR4,-0.6f); EXPECTDEC(1,0x80,0,6); + CONVERT(VarDecFromR4,-0.5f); EXPECTDEC(1,0x80,0,5); + CONVERT(VarDecFromR4,-0.4f); EXPECTDEC(1,0x80,0,4); + CONVERT(VarDecFromR4,0.0f); EXPECTDEC(0,0,0,0); + CONVERT(VarDecFromR4,0.4f); EXPECTDEC(1,0,0,4); + CONVERT(VarDecFromR4,0.5f); EXPECTDEC(1,0,0,5); + CONVERT(VarDecFromR4,0.6f); EXPECTDEC(1,0,0,6); +} + +static void test_VarDecFromR8(void) +{ + CONVVARS(double); + + CHECKPTR(VarDecFromR8); + + CONVERT(VarDecFromR8,-0.6); EXPECTDEC(1,0x80,0,6); + CONVERT(VarDecFromR8,-0.5); EXPECTDEC(1,0x80,0,5); + CONVERT(VarDecFromR8,-0.4); EXPECTDEC(1,0x80,0,4); + CONVERT(VarDecFromR8,0.0); EXPECTDEC(0,0,0,0); + CONVERT(VarDecFromR8,0.4); EXPECTDEC(1,0,0,4); + CONVERT(VarDecFromR8,0.5); EXPECTDEC(1,0,0,5); + CONVERT(VarDecFromR8,0.6); EXPECTDEC(1,0,0,6); +} + +static void test_VarDecFromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarDecFromDate); + + CONVERT(VarDecFromDate,-0.6); EXPECTDEC(1,0x80,0,6); + CONVERT(VarDecFromDate,-0.5); EXPECTDEC(1,0x80,0,5); + CONVERT(VarDecFromDate,-0.4); EXPECTDEC(1,0x80,0,4); + CONVERT(VarDecFromDate,0.0); EXPECTDEC(0,0,0,0); + CONVERT(VarDecFromDate,0.4); EXPECTDEC(1,0,0,4); + CONVERT(VarDecFromDate,0.5); EXPECTDEC(1,0,0,5); + CONVERT(VarDecFromDate,0.6); EXPECTDEC(1,0,0,6); +} + +static void test_VarDecFromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + CHECKPTR(VarDecFromStr); + + in = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + CONVERT_STR(VarDecFromStr,NULL,0); EXPECT_MISMATCH; + CONVERT_STR(VarDecFromStr,"-1", LOCALE_NOUSEROVERRIDE); EXPECTDEC(0,0x80,0,1); + CONVERT_STR(VarDecFromStr,"0", LOCALE_NOUSEROVERRIDE); EXPECTDEC(0,0,0,0); + CONVERT_STR(VarDecFromStr,"1", LOCALE_NOUSEROVERRIDE); EXPECTDEC(0,0,0,1); + CONVERT_STR(VarDecFromStr,"0.5", LOCALE_NOUSEROVERRIDE); EXPECTDEC(1,0,0,5); + CONVERT_STR(VarDecFromStr,"4294967296", LOCALE_NOUSEROVERRIDE); EXPECTDEC64(0,0,0,1,0); + CONVERT_STR(VarDecFromStr,"18446744073709551616", LOCALE_NOUSEROVERRIDE); EXPECTDEC(0,0,1,0); + CONVERT_STR(VarDecFromStr,"4294967296.0", LOCALE_NOUSEROVERRIDE); EXPECTDEC64(0,0,0,1,0); + CONVERT_STR(VarDecFromStr,"18446744073709551616.0", LOCALE_NOUSEROVERRIDE); EXPECTDEC(0,0,1,0); +} + +static void test_VarDecFromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarDecFromCy); + + CONVERT_CY(VarDecFromCy, -1); EXPECTDEC(4,0x80,0,10000); + CONVERT_CY(VarDecFromCy, 0); EXPECTDEC(4,0,0,0); + CONVERT_CY(VarDecFromCy, 1); EXPECTDEC(4,0,0,10000); + CONVERT_CY(VarDecFromCy, 0.5); EXPECTDEC(4,0,0,5000); +} + +#undef MATHVARS1 +#define MATHVARS1 HRESULT hres; DECIMAL l, out +#undef MATHVARS2 +#define MATHVARS2 MATHVARS1; DECIMAL r +#undef MATH1 +#define MATH1(func) hres = p##func(&l, &out) +#undef MATH2 +#define MATH2(func) hres = p##func(&l, &r, &out) + +static void test_VarDecAbs(void) +{ + MATHVARS1; + + CHECKPTR(VarDecAbs); + SETDEC(l,0,0x80,0,1); MATH1(VarDecAbs); EXPECTDEC(0,0,0,1); + SETDEC(l,0,0,0,0); MATH1(VarDecAbs); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0x80,0,0); MATH1(VarDecAbs); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0,0,1); MATH1(VarDecAbs); EXPECTDEC(0,0,0,1); + + /* Doesn't check for invalid input */ + SETDEC(l,0,0x7f,0,1); MATH1(VarDecAbs); EXPECTDEC(0,0x7f,0,1); + SETDEC(l,0,0x80,29,1); MATH1(VarDecAbs); EXPECTDEC(0,0,29,1); +} + +static void test_VarDecNeg(void) +{ + MATHVARS1; + + CHECKPTR(VarDecNeg); + SETDEC(l,0,0x80,0,1); MATH1(VarDecNeg); EXPECTDEC(0,0,0,1); + SETDEC(l,0,0,0,0); MATH1(VarDecNeg); EXPECTDEC(0,0x80,0,0); /* '-0'! */ + SETDEC(l,0,0x80,0,0); MATH1(VarDecNeg); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0,0,1); MATH1(VarDecNeg); EXPECTDEC(0,0x80,0,1); + + /* Doesn't check for invalid input */ + SETDEC(l,0,0x7f,0,1); MATH1(VarDecNeg); EXPECTDEC(0,0xff,0,1); + SETDEC(l,0,0x80,29,1); MATH1(VarDecNeg); EXPECTDEC(0,0,29,1); + SETDEC(l,0,0,29,1); MATH1(VarDecNeg); EXPECTDEC(0,0x80,29,1); +} + +static void test_VarDecAdd(void) +{ + MATHVARS2; + + CHECKPTR(VarDecAdd); + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,0); MATH2(VarDecAdd); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0,0,0); SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0x80,0,1); + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0,1); + + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,0); MATH2(VarDecAdd); EXPECTDEC(0,0,0,1); + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0,2); + SETDEC(l,0,0,0,1); SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0x80,0,0); /* '-0'! */ + SETDEC(l,0,0,0,1); SETDEC(r,0,0x80,0,2); MATH2(VarDecAdd); EXPECTDEC(0,0x80,0,1); + + SETDEC(l,0,0x80,0,0); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0,1); + SETDEC(l,0,0x80,0,1); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0x80,0,1); SETDEC(r,0,0,0,2); MATH2(VarDecAdd); EXPECTDEC(0,0,0,1); + SETDEC(l,0,0x80,0,1); SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0x80,0,2); + SETDEC(l,0,0x80,0,2); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0x80,0,1); + + SETDEC(l,0,0,0,0xffffffff); SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0,0xfffffffe); + SETDEC(l,0,0,0,0xffffffff); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0,(ULONG64)1 << 32); + SETDEC(l,0,0,0,0xffffffff); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0,(ULONG64)1 << 32); + + SETDEC64(l,0,0,0,0xffffffff,0); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC64(0,0,0,0xffffffff,1); + SETDEC64(l,0,0,0,0xffffffff,0); SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); + EXPECTDEC64(0,0,0,0xfffffffe,0xffffffff); + + SETDEC64(l,0,0,0,0xffffffff,0xffffffff); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,1,0); + SETDEC64(l,0,0,0,0xffffffff,0xffffffff); SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); + EXPECTDEC64(0,0,0,0xffffffff,0xfffffffe); + + SETDEC(l,0,0,0xffffffff,0); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(0,0,0xffffffff,1); + SETDEC(l,0,0,0xffffffff,0); SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); + EXPECTDEC64(0,0,0xfffffffe,0xffffffff,0xffffffff); + + SETDEC64(l,0,0,0xffffffff,0xffffffff,0xffffffff);SETDEC(r,0,0x80,0,1); MATH2(VarDecAdd); + EXPECTDEC64(0,0,0xffffffff,0xffffffff,0xfffffffe); + SETDEC64(l,0,0,0xffffffff,0xffffffff,0xffffffff);SETDEC(r,0,0,0,1); MATH2(VarDecAdd); + ok(hres == DISP_E_OVERFLOW,"Expected overflow, got (%d,%d,%d,(%8x,%8x)x) hres 0x%08x\n", + S(U(out)).scale, S(U(out)).sign, out.Hi32, S1(U1(out)).Mid32, S1(U1(out)).Lo32, hres); + + /* Promotes to the highest scale, so here the results are in the scale of 2 */ + SETDEC(l,2,0,0,0); SETDEC(r,0,0,0,0); MATH2(VarDecAdd); EXPECTDEC(2,0,0,0); + SETDEC(l,2,0,0,100); SETDEC(r,0,0,0,1); MATH2(VarDecAdd); EXPECTDEC(2,0,0,200); +} + +static void test_VarDecSub(void) +{ + MATHVARS2; + + CHECKPTR(VarDecSub); + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,0); MATH2(VarDecSub); EXPECTDEC(0,0x80,0,0); + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,1); MATH2(VarDecSub); EXPECTDEC(0,0x80,0,1); + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,1); MATH2(VarDecSub); EXPECTDEC(0,0x80,0,0); + SETDEC(l,0,0,0,1); SETDEC(r,0,0x80,0,1); MATH2(VarDecSub); EXPECTDEC(0,0,0,2); +} + +static void test_VarDecMul(void) +{ + MATHVARS2; + + CHECKPTR(VarDecMul); + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,0); MATH2(VarDecMul); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,0); MATH2(VarDecMul); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,1); MATH2(VarDecMul); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,1); MATH2(VarDecMul); EXPECTDEC(0,0,0,1); + SETDEC(l,0,0,0,45000);SETDEC(r,0,0,0,2); MATH2(VarDecMul); EXPECTDEC(0,0,0,90000); + SETDEC(l,0,0,0,2); SETDEC(r,0,0,0,45000); MATH2(VarDecMul); EXPECTDEC(0,0,0,90000); + + SETDEC(l,0,0x80,0,2); SETDEC(r,0,0,0,2); MATH2(VarDecMul); EXPECTDEC(0,0x80,0,4); + SETDEC(l,0,0,0,2); SETDEC(r,0,0x80,0,2); MATH2(VarDecMul); EXPECTDEC(0,0x80,0,4); + SETDEC(l,0,0x80,0,2); SETDEC(r,0,0x80,0,2); MATH2(VarDecMul); EXPECTDEC(0,0,0,4); + + SETDEC(l,4,0,0,2); SETDEC(r,0,0,0,2); MATH2(VarDecMul); EXPECTDEC(4,0,0,4); + SETDEC(l,0,0,0,2); SETDEC(r,3,0,0,2); MATH2(VarDecMul); EXPECTDEC(3,0,0,4); + SETDEC(l,4,0,0,2); SETDEC(r,3,0,0,2); MATH2(VarDecMul); EXPECTDEC(7,0,0,4); + /* this last one shows that native oleaut32 does *not* gratuitously seize opportunities + to reduce the scale if possible - the canonical result for the expected value is (6,0,0,1) + */ + SETDEC(l,4,0,0,5); SETDEC(r,3,0,0,2); MATH2(VarDecMul); EXPECTDEC(7,0,0,10); + + SETDEC64(l,0,0,0,0xFFFFFFFF,0xFFFFFFFF); SETDEC(r,0,0,0,2); MATH2(VarDecMul); EXPECTDEC64(0,0,1,0xFFFFFFFF,0xFFFFFFFE); + SETDEC(l,0,0,0,2); SETDEC64(r,0,0,0,0xFFFFFFFF,0xFFFFFFFF); MATH2(VarDecMul); EXPECTDEC64(0,0,1,0xFFFFFFFF,0xFFFFFFFE); + SETDEC(l,0,0,1,1); SETDEC(r,0,0,0,0x80000000); MATH2(VarDecMul); EXPECTDEC(0,0,0x80000000,0x80000000); + SETDEC(l,0,0,0,0x80000000); SETDEC(r,0,0,1,1); MATH2(VarDecMul); EXPECTDEC(0,0,0x80000000,0x80000000); + + /* near-overflow, used as a reference */ + SETDEC64(l,0,0,0,0xFFFFFFFF,0xFFFFFFFF); SETDEC(r,0,0,0,2000000000); MATH2(VarDecMul);EXPECTDEC64(0,0,1999999999,0xFFFFFFFF,0x88CA6C00); + /* actual overflow - right operand is 10 times the previous value */ + SETDEC64(l,0,0,0,0xFFFFFFFF,0xFFFFFFFF); SETDEC64(r,0,0,0,4,0xA817C800); MATH2(VarDecMul); + ok(hres == DISP_E_OVERFLOW,"Expected overflow, got (%d,%d,%d,(%8x,%8x)x) hres 0x%08x\n", + S(U(out)).scale, S(U(out)).sign, out.Hi32, S1(U1(out)).Mid32, S1(U1(out)).Lo32, hres); + /* here, native oleaut32 has an opportunity to avert the overflow, by reducing the scale of the result */ + SETDEC64(l,1,0,0,0xFFFFFFFF,0xFFFFFFFF); SETDEC64(r,0,0,0,4,0xA817C800); MATH2(VarDecMul);EXPECTDEC64(0,0,1999999999,0xFFFFFFFF,0x88CA6C00); + + /* near-overflow, used as a reference */ + SETDEC64(l,0,0,1,0xFFFFFFFF,0xFFFFFFFE); SETDEC(r,0,0,0,1000000000); MATH2(VarDecMul);EXPECTDEC64(0,0,1999999999,0xFFFFFFFF,0x88CA6C00); + /* actual overflow - right operand is 10 times the previous value */ + SETDEC64(l,0,0,1,0xFFFFFFFF,0xFFFFFFFE); SETDEC64(r,0,0,0,2,0x540BE400); MATH2(VarDecMul); + ok(hres == DISP_E_OVERFLOW,"Expected overflow, got (%d,%d,%d,(%8x,%8x)x) hres 0x%08x\n", + S(U(out)).scale, S(U(out)).sign, out.Hi32, S1(U1(out)).Mid32, S1(U1(out)).Lo32, hres); + /* here, native oleaut32 has an opportunity to avert the overflow, by reducing the scale of the result */ + SETDEC64(l,1,0,1,0xFFFFFFFF,0xFFFFFFFE); SETDEC64(r,0,0,0,2,0x540BE400); MATH2(VarDecMul);EXPECTDEC64(0,0,1999999999,0xFFFFFFFF,0x88CA6C00); + + /* this one shows that native oleaut32 is willing to lose significant digits in order to avert an overflow */ + SETDEC64(l,2,0,0,0xFFFFFFFF,0xFFFFFFFF); SETDEC64(r,0,0,0,9,0x502F9001); MATH2(VarDecMul);EXPECTDEC64(1,0,0xee6b2800,0x19999998,0xab2e719a); +} + +static void test_VarDecDiv(void) +{ + MATHVARS2; + + CHECKPTR(VarDecDiv); + /* identity divisions */ + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,1); MATH2(VarDecDiv); EXPECTDEC(0,0,0,0); + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,1); MATH2(VarDecDiv); EXPECTDEC(0,0,0,1); + SETDEC(l,1,0,0,1); SETDEC(r,0,0,0,1); MATH2(VarDecDiv); EXPECTDEC(1,0,0,1); + + /* exact divisions */ + SETDEC(l,0,0,0,45); SETDEC(r,0,0,0,9); MATH2(VarDecDiv); EXPECTDEC(0,0,0,5); + SETDEC(l,1,0,0,45); SETDEC(r,0,0,0,9); MATH2(VarDecDiv); EXPECTDEC(1,0,0,5); + SETDEC(l,0,0,0,45); SETDEC(r,1,0,0,9); MATH2(VarDecDiv); EXPECTDEC(0,0,0,50); + SETDEC(l,1,0,0,45); SETDEC(r,2,0,0,9); MATH2(VarDecDiv); EXPECTDEC(0,0,0,50); + /* these last three results suggest that native oleaut32 scales both operands down to zero + before the division, but does *not* try to scale the result, even if it is possible - + analogous to multiplication behavior + */ + SETDEC(l,1,0,0,45); SETDEC(r,1,0,0,9); MATH2(VarDecDiv); EXPECTDEC(0,0,0,5); + SETDEC(l,2,0,0,450); SETDEC(r,1,0,0,9); MATH2(VarDecDiv); EXPECTDEC(1,0,0,50); + + /* inexact divisions */ + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,3); MATH2(VarDecDiv); EXPECTDEC64(28,0,180700362,0x14b700cb,0x05555555); + SETDEC(l,1,0,0,1); SETDEC(r,0,0,0,3); MATH2(VarDecDiv); EXPECTDEC64(28,0,18070036,0x35458014,0x4d555555); + SETDEC(l,0,0,0,1); SETDEC(r,1,0,0,3); MATH2(VarDecDiv); EXPECTDEC64(28,0,1807003620,0xcf2607ee,0x35555555); + SETDEC(l,1,0,0,1); SETDEC(r,2,0,0,3); MATH2(VarDecDiv); EXPECTDEC64(28,0,1807003620,0xcf2607ee,0x35555555); + SETDEC(l,1,0,0,1); SETDEC(r,1,0,0,3); MATH2(VarDecDiv); EXPECTDEC64(28,0,180700362,0x14b700cb,0x05555555); + SETDEC(l,2,0,0,10); SETDEC(r,1,0,0,3); MATH2(VarDecDiv); EXPECTDEC64(28,0,180700362,0x14b700cb,0x05555555); + + /* this one shows that native oleaut32 rounds up the result */ + SETDEC(l,0,0,0,2); SETDEC(r,0,0,0,3); MATH2(VarDecDiv); EXPECTDEC64(28,0,361400724,0x296e0196,0x0aaaaaab); + + /* sign tests */ + SETDEC(l,0,0x80,0,45); SETDEC(r,0,0,0,9); MATH2(VarDecDiv); EXPECTDEC(0,0x80,0,5); + SETDEC(l,0,0,0,45); SETDEC(r,0,0x80,0,9); MATH2(VarDecDiv);EXPECTDEC(0,0x80,0,5); + SETDEC(l,0,0x80,0,45); SETDEC(r,0,0x80,0,9); MATH2(VarDecDiv);EXPECTDEC(0,0,0,5); + + /* oddballs */ + SETDEC(l,0,0,0,0); SETDEC(r,0,0,0,0); MATH2(VarDecDiv);/* indeterminate */ + ok(hres == DISP_E_DIVBYZERO,"Expected division-by-zero, got (%d,%d,%d,(%8x,%8x)x) hres 0x%08x\n", + S(U(out)).scale, S(U(out)).sign, out.Hi32, S1(U1(out)).Mid32, S1(U1(out)).Lo32, hres); + SETDEC(l,0,0,0,1); SETDEC(r,0,0,0,0); MATH2(VarDecDiv);/* division by zero */ + ok(hres == DISP_E_DIVBYZERO,"Expected division-by-zero, got (%d,%d,%d,(%8x,%8x)x) hres 0x%08x\n", + S(U(out)).scale, S(U(out)).sign, out.Hi32, S1(U1(out)).Mid32, S1(U1(out)).Lo32, hres); + +} + +static void test_VarDecCmp(void) +{ + MATHVARS1; + + CHECKPTR(VarDecCmp); + SETDEC(l,0,0,0,1); SETDEC(out,0,0,0,1); MATH1(VarDecCmp); EXPECT_EQ; + SETDEC(l,0,0,0,1); SETDEC(out,0,0,0,0); MATH1(VarDecCmp); EXPECT_GT; + SETDEC(l,0,0,0,0); SETDEC(out,0,0,0,1); MATH1(VarDecCmp); EXPECT_LT; +} + +/* + * VT_BOOL + */ + +#undef CONV_TYPE +#define CONV_TYPE VARIANT_BOOL +#undef _EXPECTRES +#define _EXPECTRES(res, x, fs) \ + ok((hres == S_OK && out == (CONV_TYPE)(x)) || ((HRESULT)res != S_OK && hres == (HRESULT)res), \ + "expected " #x ", got " fs "; hres=0x%08x\n", out, hres) +#undef EXPECTRES +#define EXPECTRES(res, x) _EXPECTRES(res, x, "%d") +#undef CONVERTRANGE +#define CONVERTRANGE(func,start,end) for (i = start; i < end; i++) { \ + CONVERT(func, i); if (i) { EXPECT(VARIANT_TRUE); } else { EXPECT(VARIANT_FALSE); } } + +static void test_VarBoolFromI1(void) +{ + CONVVARS(signed char); + int i; + + CHECKPTR(VarBoolFromI1); + CONVERTRANGE(VarBoolFromI1, -128, 128); +} + +static void test_VarBoolFromUI1(void) +{ + CONVVARS(BYTE); + int i; + + CHECKPTR(VarBoolFromUI1); + CONVERTRANGE(VarBoolFromUI1, 0, 256); +} + +static void test_VarBoolFromI2(void) +{ + CONVVARS(SHORT); + int i; + + CHECKPTR(VarBoolFromI2); + CONVERTRANGE(VarBoolFromI2, -32768, 32768); +} + +static void test_VarBoolFromUI2(void) +{ + CONVVARS(USHORT); + int i; + + CHECKPTR(VarBoolFromUI2); + CONVERTRANGE(VarBoolFromUI2, 0, 65536); +} + +static void test_VarBoolFromI4(void) +{ + CONVVARS(int); + + CHECKPTR(VarBoolFromI4); + CONVERT(VarBoolFromI4, 0x80000000); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromI4, -1); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromI4, 0); EXPECT(VARIANT_FALSE); + CONVERT(VarBoolFromI4, 1); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromI4, 0x7fffffff); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromUI4(void) +{ + CONVVARS(ULONG); + + CHECKPTR(VarBoolFromUI4); + CONVERT(VarBoolFromI4, 0); EXPECT(VARIANT_FALSE); + CONVERT(VarBoolFromI4, 1); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromI4, 0x80000000); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromR4(void) +{ + CONVVARS(FLOAT); + + CHECKPTR(VarBoolFromR4); + CONVERT(VarBoolFromR4, -1.0f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, 0.0f); EXPECT(VARIANT_FALSE); + CONVERT(VarBoolFromR4, 1.0f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, 1.5f); EXPECT(VARIANT_TRUE); + + /* Rounding */ + CONVERT(VarBoolFromR4, -1.5f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, -0.6f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, -0.5f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, -0.4f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, 0.4f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, 0.5f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, 0.6f); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR4, 1.5f); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromR8(void) +{ + CONVVARS(DOUBLE); + + /* Hopefully we made the point with R4 above that rounding is + * irrelevant, so we'll skip that for R8 and Date + */ + CHECKPTR(VarBoolFromR8); + CONVERT(VarBoolFromR8, -1.0); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromR8, -0.0); EXPECT(VARIANT_FALSE); + CONVERT(VarBoolFromR8, 1.0); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromCy(void) +{ + CONVVARS(CY); + + CHECKPTR(VarBoolFromCy); + CONVERT_CY(VarBoolFromCy, -32769); EXPECT(VARIANT_TRUE); + CONVERT_CY(VarBoolFromCy, -32768); EXPECT(VARIANT_TRUE); + CONVERT_CY(VarBoolFromCy, -1); EXPECT(VARIANT_TRUE); + CONVERT_CY(VarBoolFromCy, 0); EXPECT(VARIANT_FALSE); + CONVERT_CY(VarBoolFromCy, 1); EXPECT(VARIANT_TRUE); + CONVERT_CY(VarBoolFromCy, 32767); EXPECT(VARIANT_TRUE); + CONVERT_CY(VarBoolFromCy, 32768); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromI8(void) +{ + CONVVARS(LONG64); + + CHECKPTR(VarBoolFromI8); + CONVERT(VarBoolFromI8, -1); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromI8, 0); EXPECT(VARIANT_FALSE); + CONVERT(VarBoolFromI8, 1); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromUI8(void) +{ + CONVVARS(ULONG64); + + CHECKPTR(VarBoolFromUI8); + CONVERT(VarBoolFromUI8, 0); EXPECT(VARIANT_FALSE); + CONVERT(VarBoolFromUI8, 1); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromDec(void) +{ + CONVVARS(DECIMAL); + + CHECKPTR(VarBoolFromDec); + CONVERT_BADDEC(VarBoolFromDec); + + if (HAVE_OLEAUT32_DECIMAL) + { + /* Early versions of oleaut32 don't catch these errors */ + CONVERT_DEC(VarBoolFromDec,29,0,0,0); EXPECT_INVALID; + CONVERT_DEC(VarBoolFromDec,0,0x1,0,0); EXPECT_INVALID; + CONVERT_DEC(VarBoolFromDec,0,0x40,0,0); EXPECT_INVALID; + CONVERT_DEC(VarBoolFromDec,0,0x7f,0,0); EXPECT_INVALID; + } + + CONVERT_DEC(VarBoolFromDec,0,0x80,0,1); EXPECT(VARIANT_TRUE); + CONVERT_DEC(VarBoolFromDec,0,0,0,0); EXPECT(VARIANT_FALSE); + CONVERT_DEC(VarBoolFromDec,0,0,0,1); EXPECT(VARIANT_TRUE); + CONVERT_DEC(VarBoolFromDec,0,0,1,0); EXPECT(VARIANT_TRUE); + + CONVERT_DEC(VarBoolFromDec,2,0,0,CY_MULTIPLIER); EXPECT(VARIANT_TRUE); + CONVERT_DEC(VarBoolFromDec,2,0x80,0,CY_MULTIPLIER); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromDate(void) +{ + CONVVARS(DATE); + + CHECKPTR(VarBoolFromDate); + CONVERT(VarBoolFromDate, -1.0); EXPECT(VARIANT_TRUE); + CONVERT(VarBoolFromDate, -0.0); EXPECT(VARIANT_FALSE); + CONVERT(VarBoolFromDate, 1.0); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolFromStr(void) +{ + CONVVARS(LCID); + OLECHAR buff[128]; + + CHECKPTR(VarBoolFromStr); + + in = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + CONVERT_STR(VarBoolFromStr,NULL,0); + if (hres != E_INVALIDARG) + EXPECT_MISMATCH; + + /* #FALSE# and #TRUE# Are always accepted */ + CONVERT_STR(VarBoolFromStr,"#FALSE#",0); EXPECT(VARIANT_FALSE); + CONVERT_STR(VarBoolFromStr,"#TRUE#",0); EXPECT(VARIANT_TRUE); + + /* Match of #FALSE# and #TRUE# is case sensitive */ + CONVERT_STR(VarBoolFromStr,"#False#",0); EXPECT_MISMATCH; + /* But match against English is not */ + CONVERT_STR(VarBoolFromStr,"false",0); EXPECT(VARIANT_FALSE); + CONVERT_STR(VarBoolFromStr,"False",0); EXPECT(VARIANT_FALSE); + /* On/Off and yes/no are not acceptable inputs, with any flags set */ + CONVERT_STR(VarBoolFromStr,"On",0xffffffff); EXPECT_MISMATCH; + CONVERT_STR(VarBoolFromStr,"Yes",0xffffffff); EXPECT_MISMATCH; + + /* Change the LCID. This doesn't make any difference for text,unless we ask + * to check local boolean text with the VARIANT_LOCALBOOL flag. */ + in = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT); + + /* #FALSE# and #TRUE# are accepted in all locales */ + CONVERT_STR(VarBoolFromStr,"#FALSE#",0); EXPECT(VARIANT_FALSE); + CONVERT_STR(VarBoolFromStr,"#TRUE#",0); EXPECT(VARIANT_TRUE); + CONVERT_STR(VarBoolFromStr,"#FALSE#",VARIANT_LOCALBOOL); EXPECT(VARIANT_FALSE); + CONVERT_STR(VarBoolFromStr,"#TRUE#",VARIANT_LOCALBOOL); EXPECT(VARIANT_TRUE); + + /* English is accepted regardless of the locale */ + CONVERT_STR(VarBoolFromStr,"false",0); EXPECT(VARIANT_FALSE); + /* And is still not case sensitive */ + CONVERT_STR(VarBoolFromStr,"False",0); EXPECT(VARIANT_FALSE); + + if (HAVE_OLEAUT32_LOCALES) + { + /* French is rejected without VARIANT_LOCALBOOL */ + CONVERT_STR(VarBoolFromStr,"faux",0); EXPECT_MISMATCH; + /* But accepted if this flag is given */ + CONVERT_STR(VarBoolFromStr,"faux",VARIANT_LOCALBOOL); EXPECT(VARIANT_FALSE); + /* Regardless of case - from this we assume locale text comparisons ignore case */ + CONVERT_STR(VarBoolFromStr,"Faux",VARIANT_LOCALBOOL); EXPECT(VARIANT_FALSE); + + /* Changing the locale prevents the localised text from being compared - + * this demonstrates that only the indicated LCID and English are searched */ + in = MAKELCID(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT), SORT_DEFAULT); + CONVERT_STR(VarBoolFromStr,"faux",VARIANT_LOCALBOOL); EXPECT_MISMATCH; + } + + /* Numeric strings are read as 0 or non-0 */ + CONVERT_STR(VarBoolFromStr,"0",0); EXPECT(VARIANT_FALSE); + CONVERT_STR(VarBoolFromStr,"-1",0); EXPECT(VARIANT_TRUE); + CONVERT_STR(VarBoolFromStr,"+1",0); EXPECT(VARIANT_TRUE); + + if (HAVE_OLEAUT32_LOCALES) + { + /* Numeric strings are read as floating point numbers. The line below fails + * because '.' is not a valid decimal separator for Polish numbers */ + CONVERT_STR(VarBoolFromStr,"0.1",LOCALE_NOUSEROVERRIDE); EXPECT_MISMATCH; + } + + /* Changing the lcid back to US English reads the r8 correctly */ + in = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + CONVERT_STR(VarBoolFromStr,"0.1",LOCALE_NOUSEROVERRIDE); EXPECT(VARIANT_TRUE); +} + +static void test_VarBoolCopy(void) +{ + COPYTEST(1, VT_BOOL, V_BOOL(&vSrc), V_BOOL(&vDst), V_BOOLREF(&vSrc), V_BOOLREF(&vDst), "%d"); +} + +#define BOOL_STR(flags, str) hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, flags, VT_BSTR); \ + ok(hres == S_OK && V_VT(&vDst) == VT_BSTR && \ + V_BSTR(&vDst) && !memcmp(V_BSTR(&vDst), str, sizeof(str)), \ + "hres=0x%X, type=%d (should be VT_BSTR), *bstr='%c'\n", \ + hres, V_VT(&vDst), V_BSTR(&vDst) ? *V_BSTR(&vDst) : '?') + +static void test_VarBoolChangeTypeEx(void) +{ + static const WCHAR szTrue[] = { 'T','r','u','e','\0' }; + static const WCHAR szFalse[] = { 'F','a','l','s','e','\0' }; + static const WCHAR szFaux[] = { 'F','a','u','x','\0' }; + CONVVARS(CONV_TYPE); + VARIANTARG vSrc, vDst; + LCID lcid; + + in = 1; + + INITIAL_TYPETEST(VT_BOOL, V_BOOL, "%d"); + COMMON_TYPETEST; + + /* The common tests convert to a number. Try the different flags */ + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + V_VT(&vSrc) = VT_BOOL; + V_BOOL(&vSrc) = 1; + + if (!IS_ANCIENT) + { + BOOL_STR(VARIANT_ALPHABOOL, szTrue); + V_BOOL(&vSrc) = 0; + BOOL_STR(VARIANT_ALPHABOOL, szFalse); + } + + if (HAVE_OLEAUT32_LOCALES) + { + lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT); + + /* VARIANT_ALPHABOOL is always English */ + BOOL_STR(VARIANT_ALPHABOOL, szFalse); + /* VARIANT_LOCALBOOL uses the localised text */ + BOOL_STR(VARIANT_LOCALBOOL, szFaux); + /* Both flags together acts as VARIANT_LOCALBOOL */ + BOOL_STR(VARIANT_ALPHABOOL|VARIANT_LOCALBOOL, szFaux); + } +} + +/* + * BSTR + */ + +static void test_VarBstrFromR4(void) +{ + static const WCHAR szNative[] = { '6','5','4','3','2','2','.','3','\0' }; + static const WCHAR szZero[] = {'0', '\0'}; + static const WCHAR szOneHalf_English[] = { '0','.','5','\0' }; /* uses period */ + static const WCHAR szOneHalf_Spanish[] = { '0',',','5','\0' }; /* uses comma */ + LCID lcid; + LCID lcid_spanish; + HRESULT hres; + BSTR bstr = NULL; + + float f; + + CHECKPTR(VarBstrFromR4); + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + lcid_spanish = MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), SORT_DEFAULT); + f = 654322.23456f; + hres = pVarBstrFromR4(f, lcid, 0, &bstr); + ok(hres == S_OK, "got hres 0x%08x\n", hres); + if (bstr) + { + todo_wine { + /* MSDN states that rounding of R4/R8 is dependent on the underlying + * bit pattern of the number and so is architecture dependent. In this + * case Wine returns .2 (which is more correct) and Native returns .3 + */ + ok(memcmp(bstr, szNative, sizeof(szNative)) == 0, "string different\n"); + } + } + + f = -0.0; + hres = pVarBstrFromR4(f, lcid, 0, &bstr); + ok(hres == S_OK, "got hres 0x%08x\n", hres); + if (bstr) + { + ok(memcmp(bstr, szZero, sizeof(szZero)) == 0, "negative zero (got %s)\n", wtoascii(bstr)); + } + + /* The following tests that lcid is used for decimal separator even without LOCALE_USE_NLS */ + f = 0.5; + hres = pVarBstrFromR4(f, lcid, LOCALE_NOUSEROVERRIDE, &bstr); + ok(hres == S_OK, "got hres 0x%08x\n", hres); + if (bstr) + { + ok(memcmp(bstr, szOneHalf_English, sizeof(szOneHalf_English)) == 0, "English locale failed (got %s)\n", wtoascii(bstr)); + } + f = 0.5; + hres = pVarBstrFromR4(f, lcid_spanish, LOCALE_NOUSEROVERRIDE, &bstr); + ok(hres == S_OK, "got hres 0x%08x\n", hres); + if (bstr) + { + ok(memcmp(bstr, szOneHalf_Spanish, sizeof(szOneHalf_Spanish)) == 0, "Spanish locale failed (got %s)\n", wtoascii(bstr)); + } +} + +#define BSTR_DATE(dt,str) SysFreeString(bstr); bstr = NULL; \ + hres = pVarBstrFromDate(dt,lcid,LOCALE_NOUSEROVERRIDE,&bstr); \ + if (bstr) WideCharToMultiByte(CP_ACP, 0, bstr, -1, buff, sizeof(buff), 0, 0); \ + else buff[0] = 0; \ + ok(hres == S_OK && !strcmp(str,buff), "Expected '%s', got '%s', hres = 0x%08x\n", \ + str, buff, hres) + +static void test_VarBstrFromDate(void) +{ + char buff[256]; + LCID lcid; + HRESULT hres; + BSTR bstr = NULL; + + CHECKPTR(VarBstrFromDate); + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + BSTR_DATE(0.0, "12:00:00 AM"); + BSTR_DATE(3.34, "1/2/1900 8:09:36 AM"); + BSTR_DATE(3339.34, "2/20/1909 8:09:36 AM"); + BSTR_DATE(365.00, "12/30/1900"); + BSTR_DATE(365.25, "12/30/1900 6:00:00 AM"); + BSTR_DATE(1461.0, "12/31/1903"); + BSTR_DATE(1461.5, "12/31/1903 12:00:00 PM"); + todo_wine { BSTR_DATE(-657434.0, "1/1/100"); } + BSTR_DATE(2958465.0, "12/31/9999"); +} + +#define BSTR_DEC(l, a, b, c, d, e) \ + SETDEC(l, a,b,c,d);\ + hres = VarBstrFromDec(&l, lcid, LOCALE_NOUSEROVERRIDE, &bstr);\ + ok(hres == S_OK, "got hres 0x%08x\n", hres);\ + if (hres== S_OK && bstr)\ + {\ + ok(lstrcmpW(bstr, e) == 0, "invalid number (got %s)\n", wtoascii(bstr));\ + } + +#define BSTR_DEC64(l, a, b, c, x, d, e) \ + SETDEC64(l, a,b,c,x,d);\ + hres = VarBstrFromDec(&l, lcid, LOCALE_NOUSEROVERRIDE, &bstr);\ + ok(hres == S_OK, "got hres 0x%08x\n", hres);\ + if (hres== S_OK && bstr)\ + {\ + ok(lstrcmpW(bstr, e) == 0, "invalid number (got %s)\n", wtoascii(bstr));\ + } + +static void test_VarBstrFromDec(void) +{ + LCID lcid; + HRESULT hres; + BSTR bstr = NULL; + DECIMAL l; + + static const WCHAR szZero[] = {'0', '\0'}; + static const WCHAR szOne[] = {'1', '\0'}; + static const WCHAR szOnePointFive[] = {'1','.','5','\0'}; + static const WCHAR szMinusOnePointFive[] = {'-','1','.','5','\0'}; + static const WCHAR szBigNum1[] = {'4','2','9','4','9','6','7','2','9','5','\0'}; /* (1 << 32) - 1 */ + static const WCHAR szBigNum2[] = {'4','2','9','4','9','6','7','2','9','6','\0'}; /* (1 << 32) */ + static const WCHAR szBigNum3[] = {'1','8','4','4','6','7','4','4','0','7','3','7','0','9','5','5','1','6','1','5','\0'}; /* (1 << 64) - 1 */ + static const WCHAR szBigNum4[] = {'1','8','4','4','6','7','4','4','0','7','3','7','0','9','5','5','1','6','1','6','\0'}; /* (1 << 64) */ + static const WCHAR szBigNum5[] = {'7','9','2','2','8','1','6','2','5','1','4','2','6','4','3','3','7','5','9','3','5','4','3','9','5','0','3','3','5','\0'}; /* (1 << 96) - 1 */ + static const WCHAR szBigScale1[] = {'0','.','0','0','0','0','0','0','0','0','0','1','\0'}; /* 1 * 10^-10 */ + static const WCHAR szBigScale2[] = {'7','9','2','2','8','1','6','2','5','1','4','2','6','4','3','3','7','5','9','.','3','5','4','3','9','5','0','3','3','5','\0'}; /* ((1 << 96) - 1) * 10^-10 */ + static const WCHAR szBigScale3[] = {'7','.','9','2','2','8','1','6','2','5','1','4','2','6','4','3','3','7','5','9','3','5','4','3','9','5','0','3','3','5','\0'}; /* ((1 << 96) - 1) * 10^-28 */ + + static const WCHAR szSmallNumber_English[] = {'0','.','0','0','0','9','\0'}; + static const WCHAR szSmallNumber_Spanish[] = {'0',',','0','0','0','9','\0'}; + + CHECKPTR(VarBstrFromDec); + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + + /* check zero */ + BSTR_DEC(l, 0,0,0,0, szZero); + + /* check one */ + BSTR_DEC(l, 0,0,0,1, szOne); + BSTR_DEC(l, 1,0,0,10,szOne); + BSTR_DEC(l, 2,0,0,100,szOne); + BSTR_DEC(l, 3,0,0,1000,szOne); + + /* check one point five */ + BSTR_DEC(l, 1,0,0,15, szOnePointFive); + BSTR_DEC(l, 2,0,0,150, szOnePointFive); + BSTR_DEC(l, 3,0,0,1500, szOnePointFive); + + /* check minus one point five */ + BSTR_DEC(l, 1,0x80,0,15, szMinusOnePointFive); + + /* check bignum (1) */ + BSTR_DEC(l, 0,0,0,0xffffffff, szBigNum1); + + /* check bignum (2) */ + BSTR_DEC64(l, 0,0,0,1,0, szBigNum2); + + /* check bignum (3) */ + BSTR_DEC64(l, 0,0,0,0xffffffff,0xffffffff, szBigNum3); + + /* check bignum (4) */ + BSTR_DEC(l, 0,0,1,0, szBigNum4); + + /* check bignum (5) */ + BSTR_DEC64(l, 0,0,0xffffffff,0xffffffff,0xffffffff, szBigNum5); + + /* check bigscale (1) */ + BSTR_DEC(l, 10,0,0,1, szBigScale1); + + /* check bigscale (2) */ + BSTR_DEC64(l, 10,0,0xffffffffUL,0xffffffff,0xffffffff, szBigScale2); + + /* check bigscale (3) */ + BSTR_DEC64(l, 28,0,0xffffffffUL,0xffffffff,0xffffffff, szBigScale3); + + /* check leading zeros and decimal sep. for English locale */ + BSTR_DEC(l, 4,0,0,9, szSmallNumber_English); + BSTR_DEC(l, 5,0,0,90, szSmallNumber_English); + BSTR_DEC(l, 6,0,0,900, szSmallNumber_English); + BSTR_DEC(l, 7,0,0,9000, szSmallNumber_English); + + lcid = MAKELCID(MAKELANGID(LANG_SPANISH, SUBLANG_DEFAULT), SORT_DEFAULT); + + /* check leading zeros and decimal sep. for Spanish locale */ + BSTR_DEC(l, 4,0,0,9, szSmallNumber_Spanish); + BSTR_DEC(l, 5,0,0,90, szSmallNumber_Spanish); + BSTR_DEC(l, 6,0,0,900, szSmallNumber_Spanish); + BSTR_DEC(l, 7,0,0,9000, szSmallNumber_Spanish); +} +#undef BSTR_DEC +#undef BSTR_DEC64 + +#define _VARBSTRCMP(left,right,lcid,flags,result) \ + hres = pVarBstrCmp(left,right,lcid,flags); \ + ok(hres == result, "VarBstrCmp: expected " #result ", got hres=0x%x\n", hres) +#define VARBSTRCMP(left,right,flags,result) \ + _VARBSTRCMP(left,right,lcid,flags,result) + +static void test_VarBstrCmp(void) +{ + LCID lcid; + HRESULT hres; + static const WCHAR sz[] = {'W','u','r','s','c','h','t','\0'}; + static const WCHAR szempty[] = {'\0'}; + static const WCHAR sz1[] = { 'a',0 }; + static const WCHAR sz2[] = { 'A',0 }; + static const WCHAR s1[] = { 'a',0 }; + static const WCHAR s2[] = { 'a',0,'b' }; + static const char sb1[] = {1,0,1}; + static const char sb2[] = {1,0,2}; + BSTR bstr, bstrempty, bstr2; + + CHECKPTR(VarBstrCmp); + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); + bstr = SysAllocString(sz); + bstrempty = SysAllocString(szempty); + + /* NULL handling. Yepp, MSDN is totaly wrong here */ + VARBSTRCMP(NULL,NULL,0,VARCMP_EQ); + VARBSTRCMP(bstr,NULL,0,VARCMP_GT); + VARBSTRCMP(NULL,bstr,0,VARCMP_LT); + + /* NULL and empty string comparisions */ + VARBSTRCMP(bstrempty,NULL,0,VARCMP_EQ); + VARBSTRCMP(NULL,bstrempty,0,VARCMP_EQ); + + SysFreeString(bstr); + bstr = SysAllocString(sz1); + + bstr2 = SysAllocString(sz2); + VARBSTRCMP(bstr,bstr2,0,VARCMP_LT); + VARBSTRCMP(bstr,bstr2,NORM_IGNORECASE,VARCMP_EQ); + SysFreeString(bstr2); + /* These two strings are considered equal even though one is + * NULL-terminated and the other not. + */ + bstr2 = SysAllocStringLen(s1, sizeof(s1) / sizeof(WCHAR)); + VARBSTRCMP(bstr,bstr2,0,VARCMP_EQ); + SysFreeString(bstr2); + + /* These two strings are not equal */ + bstr2 = SysAllocStringLen(s2, sizeof(s2) / sizeof(WCHAR)); + VARBSTRCMP(bstr,bstr2,0,VARCMP_LT); + SysFreeString(bstr2); + + SysFreeString(bstr); + + /* When (LCID == 0) it should be a binary comparison + * so these two strings could not match. + */ + bstr = SysAllocStringByteLen(sb1, sizeof(sb1)); + bstr2 = SysAllocStringByteLen(sb2, sizeof(sb2)); + lcid = 0; + VARBSTRCMP(bstr,bstr2,0,VARCMP_LT); + SysFreeString(bstr2); + SysFreeString(bstr); +} + +/* Get the internal representation of a BSTR */ +static inline LPINTERNAL_BSTR Get(const BSTR lpszString) +{ + return lpszString ? (LPINTERNAL_BSTR)((char*)lpszString - sizeof(DWORD)) : NULL; +} + +static inline BSTR GetBSTR(const LPINTERNAL_BSTR bstr) +{ + return (BSTR)bstr->szString; +} + +static void test_SysStringLen(void) +{ + INTERNAL_BSTR bstr; + BSTR str = GetBSTR(&bstr); + + bstr.dwLen = 0; + ok (SysStringLen(str) == 0, "Expected dwLen 0, got %d\n", SysStringLen(str)); + bstr.dwLen = 2; + ok (SysStringLen(str) == 1, "Expected dwLen 1, got %d\n", SysStringLen(str)); +} + +static void test_SysStringByteLen(void) +{ + INTERNAL_BSTR bstr; + BSTR str = GetBSTR(&bstr); + + bstr.dwLen = 0; + ok (SysStringByteLen(str) == 0, "Expected dwLen 0, got %d\n", SysStringLen(str)); + bstr.dwLen = 2; + ok (SysStringByteLen(str) == 2, "Expected dwLen 2, got %d\n", SysStringByteLen(str)); +} + +static void test_SysAllocString(void) +{ + const OLECHAR szTest[5] = { 'T','e','s','t','\0' }; + BSTR str; + + str = SysAllocString(NULL); + ok (str == NULL, "Expected NULL, got %p\n", str); + + str = SysAllocString(szTest); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr = Get(str); + + ok (bstr->dwLen == 8, "Expected 8, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szTest), "String different\n"); + SysFreeString(str); + } +} + +static void test_SysAllocStringLen(void) +{ + const OLECHAR szTest[5] = { 'T','e','s','t','\0' }; + BSTR str; + + /* Very early native dlls do not limit the size of strings, so skip this test */ + if (0) + { + str = SysAllocStringLen(szTest, 0x80000000); + todo_wine { + ok (str == NULL, "Expected NULL, got %p\n", str); + } + } + + str = SysAllocStringLen(NULL, 0); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr = Get(str); + + ok (bstr->dwLen == 0, "Expected 0, got %d\n", bstr->dwLen); + ok (!bstr->szString[0], "String not empty\n"); + SysFreeString(str); + } + + str = SysAllocStringLen(szTest, 4); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr = Get(str); + + ok (bstr->dwLen == 8, "Expected 8, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szTest), "String different\n"); + SysFreeString(str); + } +} + +static void test_SysAllocStringByteLen(void) +{ + const OLECHAR szTest[10] = { 'T','e','s','t','\0' }; + const CHAR szTestA[6] = { 'T','e','s','t','\0','?' }; + BSTR str; + + str = SysAllocStringByteLen(szTestA, 0x80000000); + ok (str == NULL, "Expected NULL, got %p\n", str); + + str = SysAllocStringByteLen(szTestA, 0xffffffff); + ok (str == NULL, "Expected NULL, got %p\n", str); + + str = SysAllocStringByteLen(NULL, 0); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr = Get(str); + + ok (bstr->dwLen == 0, "Expected 0, got %d\n", bstr->dwLen); + ok (!bstr->szString[0], "String not empty\n"); + SysFreeString(str); + } + + str = SysAllocStringByteLen(szTestA, 4); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr = Get(str); + + ok (bstr->dwLen == 4, "Expected 4, got %d\n", bstr->dwLen); + ok (!lstrcmpA((LPCSTR)bstr->szString, szTestA), "String different\n"); + SysFreeString(str); + } + + /* Odd lengths are allocated rounded up, but truncated at the right position */ + str = SysAllocStringByteLen(szTestA, 3); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + const CHAR szTestTruncA[4] = { 'T','e','s','\0' }; + LPINTERNAL_BSTR bstr = Get(str); + + ok (bstr->dwLen == 3, "Expected 3, got %d\n", bstr->dwLen); + ok (!lstrcmpA((LPCSTR)bstr->szString, szTestTruncA), "String different\n"); + SysFreeString(str); + } + + str = SysAllocStringByteLen((LPCSTR)szTest, 8); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr = Get(str); + + ok (bstr->dwLen == 8, "Expected 8, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szTest), "String different\n"); + SysFreeString(str); + } +} + +static void test_SysReAllocString(void) +{ + const OLECHAR szTest[5] = { 'T','e','s','t','\0' }; + const OLECHAR szSmaller[2] = { 'x','\0' }; + const OLECHAR szLarger[7] = { 'L','a','r','g','e','r','\0' }; + BSTR str; + + str = SysAllocStringLen(szTest, 4); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr; + BSTR oldstr = str; + int changed; + + bstr = Get(str); + ok (bstr->dwLen == 8, "Expected 8, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szTest), "String different\n"); + + changed = SysReAllocString(&str, szSmaller); + ok (changed == 1, "Expected 1, got %d\n", changed); + ok (str == oldstr, "Created new string\n"); + bstr = Get(str); + ok (bstr->dwLen == 2, "Expected 2, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szSmaller), "String different\n"); + + oldstr = str; + changed = SysReAllocString(&str, szLarger); + ok (changed == 1, "Expected 1, got %d\n", changed); + /* Early versions always make new strings rather than resizing */ + /* ok (str == oldstr, "Created new string\n"); */ + bstr = Get(str); + ok (bstr->dwLen == 12, "Expected 12, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szLarger), "String different\n"); + + SysFreeString(str); + } +} + +static void test_SysReAllocStringLen(void) +{ + const OLECHAR szTest[5] = { 'T','e','s','t','\0' }; + const OLECHAR szSmaller[2] = { 'x','\0' }; + const OLECHAR szLarger[7] = { 'L','a','r','g','e','r','\0' }; + BSTR str; + + str = SysAllocStringLen(szTest, 4); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + LPINTERNAL_BSTR bstr; + BSTR oldstr = str; + int changed; + + bstr = Get(str); + ok (bstr->dwLen == 8, "Expected 8, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szTest), "String different\n"); + + changed = SysReAllocStringLen(&str, szSmaller, 1); + ok (changed == 1, "Expected 1, got %d\n", changed); + ok (str == oldstr, "Created new string\n"); + bstr = Get(str); + ok (bstr->dwLen == 2, "Expected 2, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szSmaller), "String different\n"); + + oldstr = str; + changed = SysReAllocStringLen(&str, szLarger, 6); + ok (changed == 1, "Expected 1, got %d\n", changed); + /* Early versions always make new strings rather than resizing */ + /* ok (str == oldstr, "Created new string\n"); */ + bstr = Get(str); + ok (bstr->dwLen == 12, "Expected 12, got %d\n", bstr->dwLen); + ok (!lstrcmpW(bstr->szString, szLarger), "String different\n"); + + changed = SysReAllocStringLen(&str, str, 6); + ok (changed == 1, "Expected 1, got %d\n", changed); + + SysFreeString(str); + } +} + +static void test_BstrCopy(void) +{ + const CHAR szTestA[6] = { 'T','e','s','t','\0','?' }; + const CHAR szTestTruncA[4] = { 'T','e','s','\0' }; + LPINTERNAL_BSTR bstr; + BSTR str; + HRESULT hres; + VARIANT vt1, vt2; + + str = SysAllocStringByteLen(szTestA, 3); + ok (str != NULL, "Expected non-NULL\n"); + if (str) + { + V_VT(&vt1) = VT_BSTR; + V_BSTR(&vt1) = str; + V_VT(&vt2) = VT_EMPTY; + hres = VariantCopy(&vt2, &vt1); + ok (hres == S_OK,"Failed to copy binary bstring with hres 0x%08x\n", hres); + bstr = Get(V_BSTR(&vt2)); + ok (bstr->dwLen == 3, "Expected 3, got %d\n", bstr->dwLen); + ok (!lstrcmpA((LPCSTR)bstr->szString, szTestTruncA), "String different\n"); + } +} + +static void test_VarBstrCat(void) +{ + static const WCHAR sz1[] = { 'a',0 }; + static const WCHAR sz2[] = { 'b',0 }; + static const WCHAR sz1sz2[] = { 'a','b',0 }; + static const WCHAR s1[] = { 'a',0 }; + static const WCHAR s2[] = { 'b',0 }; + static const WCHAR s1s2[] = { 'a',0,'b',0 }; + HRESULT ret; + BSTR str1, str2, res; + + /* Crash + ret = VarBstrCat(NULL, NULL, NULL); + */ + + /* Concatenation of two NULL strings works */ + ret = VarBstrCat(NULL, NULL, &res); + ok(ret == S_OK, "VarBstrCat failed: %08x\n", ret); + ok(res != NULL, "Expected a string\n"); + ok(SysStringLen(res) == 0, "Expected a 0-length string\n"); + SysFreeString(res); + + str1 = SysAllocString(sz1); + + /* Concatenation with one NULL arg */ + ret = VarBstrCat(NULL, str1, &res); + ok(ret == S_OK, "VarBstrCat failed: %08x\n", ret); + ok(res != NULL, "Expected a string\n"); + ok(SysStringLen(res) == SysStringLen(str1), "Unexpected length\n"); + ok(!memcmp(res, sz1, SysStringLen(str1)), "Unexpected value\n"); + SysFreeString(res); + ret = VarBstrCat(str1, NULL, &res); + ok(ret == S_OK, "VarBstrCat failed: %08x\n", ret); + ok(res != NULL, "Expected a string\n"); + ok(SysStringLen(res) == SysStringLen(str1), "Unexpected length\n"); + ok(!memcmp(res, sz1, SysStringLen(str1)), "Unexpected value\n"); + SysFreeString(res); + + /* Concatenation of two zero-terminated strings */ + str2 = SysAllocString(sz2); + ret = VarBstrCat(str1, str2, &res); + ok(ret == S_OK, "VarBstrCat failed: %08x\n", ret); + ok(res != NULL, "Expected a string\n"); + ok(SysStringLen(res) == sizeof(sz1sz2) / sizeof(WCHAR) - 1, + "Unexpected length\n"); + ok(!memcmp(res, sz1sz2, sizeof(sz1sz2)), "Unexpected value\n"); + SysFreeString(res); + + SysFreeString(str2); + SysFreeString(str1); + + /* Concatenation of two strings with embedded NULLs */ + str1 = SysAllocStringLen(s1, sizeof(s1) / sizeof(WCHAR)); + str2 = SysAllocStringLen(s2, sizeof(s2) / sizeof(WCHAR)); + + ret = VarBstrCat(str1, str2, &res); + ok(ret == S_OK, "VarBstrCat failed: %08x\n", ret); + ok(res != NULL, "Expected a string\n"); + ok(SysStringLen(res) == sizeof(s1s2) / sizeof(WCHAR), + "Unexpected length\n"); + ok(!memcmp(res, s1s2, sizeof(s1s2)), "Unexpected value\n"); + SysFreeString(res); + + SysFreeString(str2); + SysFreeString(str1); +} + +/* IUnknown */ + +static void test_IUnknownClear(void) +{ + HRESULT hres; + VARIANTARG v; + DummyDispatch u = { &DummyDispatch_VTable, 1, VT_UI1, FALSE }; + IUnknown* pu = (IUnknown*)&u; + + /* Test that IUnknown_Release is called on by-value */ + V_VT(&v) = VT_UNKNOWN; + V_UNKNOWN(&v) = (IUnknown*)&u; + hres = VariantClear(&v); + ok(hres == S_OK && u.ref == 0 && V_VT(&v) == VT_EMPTY, + "clear unknown: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 0, VT_EMPTY, hres, u.ref, V_VT(&v)); + + /* But not when clearing a by-reference*/ + u.ref = 1; + V_VT(&v) = VT_UNKNOWN|VT_BYREF; + V_UNKNOWNREF(&v) = &pu; + hres = VariantClear(&v); + ok(hres == S_OK && u.ref == 1 && V_VT(&v) == VT_EMPTY, + "clear dispatch: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 1, VT_EMPTY, hres, u.ref, V_VT(&v)); +} + +static void test_IUnknownCopy(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + DummyDispatch u = { &DummyDispatch_VTable, 1, VT_UI1, FALSE }; + IUnknown* pu = (IUnknown*)&u; + + /* AddRef is called on by-value copy */ + VariantInit(&vDst); + V_VT(&vSrc) = VT_UNKNOWN; + V_UNKNOWN(&vSrc) = pu; + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && u.ref == 2 && V_VT(&vDst) == VT_UNKNOWN, + "copy unknown: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 2, VT_EMPTY, hres, u.ref, V_VT(&vDst)); + + /* AddRef is skipped on copy of by-reference IDispatch */ + VariantInit(&vDst); + u.ref = 1; + V_VT(&vSrc) = VT_UNKNOWN|VT_BYREF; + V_UNKNOWNREF(&vSrc) = &pu; + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && u.ref == 1 && V_VT(&vDst) == (VT_UNKNOWN|VT_BYREF), + "copy unknown: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 1, VT_DISPATCH, hres, u.ref, V_VT(&vDst)); + + /* AddRef is called copying by-reference IDispatch with indirection */ + VariantInit(&vDst); + u.ref = 1; + V_VT(&vSrc) = VT_UNKNOWN|VT_BYREF; + V_UNKNOWNREF(&vSrc) = &pu; + hres = VariantCopyInd(&vDst, &vSrc); + ok(hres == S_OK && u.ref == 2 && V_VT(&vDst) == VT_UNKNOWN, + "copy unknown: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 2, VT_DISPATCH, hres, u.ref, V_VT(&vDst)); + + /* Indirection in place also calls AddRef */ + u.ref = 1; + V_VT(&vSrc) = VT_UNKNOWN|VT_BYREF; + V_UNKNOWNREF(&vSrc) = &pu; + hres = VariantCopyInd(&vSrc, &vSrc); + ok(hres == S_OK && u.ref == 2 && V_VT(&vSrc) == VT_UNKNOWN, + "copy unknown: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 2, VT_DISPATCH, hres, u.ref, V_VT(&vSrc)); +} + +static void test_IUnknownChangeTypeEx(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + LCID lcid; + VARTYPE vt; + DummyDispatch u = { &DummyDispatch_VTable, 1, VT_UI1, FALSE }; + IUnknown* pu = (IUnknown*)&u; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + V_VT(&vSrc) = VT_UNKNOWN; + V_UNKNOWN(&vSrc) = pu; + + /* =>IDispatch in place */ + hres = VariantChangeTypeEx(&vSrc, &vSrc, lcid, 0, VT_DISPATCH); + ok(hres == S_OK && u.ref == 1 && + V_VT(&vSrc) == VT_DISPATCH && V_DISPATCH(&vSrc) == (IDispatch*)pu, + "change unk(src=src): expected 0x%08x,%d,%d,%p, got 0x%08x,%d,%d,%p\n", + S_OK, 1, VT_DISPATCH, pu, hres, u.ref, V_VT(&vSrc), V_DISPATCH(&vSrc)); + + /* =>IDispatch */ + u.ref = 1; + V_VT(&vSrc) = VT_UNKNOWN; + V_UNKNOWN(&vSrc) = (IUnknown*)pu; + VariantInit(&vDst); + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, VT_UNKNOWN); + /* Note vSrc is not cleared, as final refcount is 2 */ + ok(hres == S_OK && u.ref == 2 && + V_VT(&vDst) == VT_UNKNOWN && V_UNKNOWN(&vDst) == (IUnknown*)pu, + "change unk(src,dst): expected 0x%08x,%d,%d,%p, got 0x%08x,%d,%d,%p\n", + S_OK, 2, VT_UNKNOWN, pu, hres, u.ref, V_VT(&vDst), V_UNKNOWN(&vDst)); + + /* Can't change unknown to anything else */ + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT hExpected = DISP_E_BADVARTYPE; + + V_VT(&vSrc) = VT_UNKNOWN; + V_UNKNOWN(&vSrc) = (IUnknown*)pu; + VariantInit(&vDst); + + if (vt == VT_UNKNOWN || vt == VT_DISPATCH || vt == VT_EMPTY || vt == VT_NULL) + hExpected = S_OK; + else + { + if (vt == VT_I8 || vt == VT_UI8) + { + if (HAVE_OLEAUT32_I8) + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt == VT_RECORD) + { + if (HAVE_OLEAUT32_RECORD) + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt >= VT_I2 && vt <= VT_UINT && vt != (VARTYPE)15) + hExpected = DISP_E_TYPEMISMATCH; + } + if (IS_ANCIENT && IS_MODERN_VTYPE(vt)) + hExpected = DISP_E_BADVARTYPE; + + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt); + ok(hres == hExpected, + "change unk(badvar): vt %d expected 0x%08x, got 0x%08x\n", + vt, hExpected, hres); + } +} + +/* IDispatch */ +static void test_IDispatchClear(void) +{ + HRESULT hres; + VARIANTARG v; + DummyDispatch d = { &DummyDispatch_VTable, 1, VT_UI1, FALSE }; + IDispatch* pd = (IDispatch*)&d; + + /* As per IUnknown */ + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = pd; + hres = VariantClear(&v); + ok(hres == S_OK && d.ref == 0 && V_VT(&v) == VT_EMPTY, + "clear dispatch: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 0, VT_EMPTY, hres, d.ref, V_VT(&v)); + + d.ref = 1; + V_VT(&v) = VT_DISPATCH|VT_BYREF; + V_DISPATCHREF(&v) = &pd; + hres = VariantClear(&v); + ok(hres == S_OK && d.ref == 1 && V_VT(&v) == VT_EMPTY, + "clear dispatch: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 1, VT_EMPTY, hres, d.ref, V_VT(&v)); +} + +static void test_IDispatchCopy(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + DummyDispatch d = { &DummyDispatch_VTable, 1, VT_UI1, FALSE }; + IDispatch* pd = (IDispatch*)&d; + + /* As per IUnknown */ + + VariantInit(&vDst); + V_VT(&vSrc) = VT_DISPATCH; + V_DISPATCH(&vSrc) = pd; + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && d.ref == 2 && V_VT(&vDst) == VT_DISPATCH, + "copy dispatch: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 2, VT_EMPTY, hres, d.ref, V_VT(&vDst)); + + VariantInit(&vDst); + d.ref = 1; + V_VT(&vSrc) = VT_DISPATCH|VT_BYREF; + V_DISPATCHREF(&vSrc) = &pd; + hres = VariantCopy(&vDst, &vSrc); + ok(hres == S_OK && d.ref == 1 && V_VT(&vDst) == (VT_DISPATCH|VT_BYREF), + "copy dispatch: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 1, VT_DISPATCH, hres, d.ref, V_VT(&vDst)); + + VariantInit(&vDst); + d.ref = 1; + V_VT(&vSrc) = VT_DISPATCH|VT_BYREF; + V_DISPATCHREF(&vSrc) = &pd; + hres = VariantCopyInd(&vDst, &vSrc); + ok(hres == S_OK && d.ref == 2 && V_VT(&vDst) == VT_DISPATCH, + "copy dispatch: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 2, VT_DISPATCH, hres, d.ref, V_VT(&vDst)); + + d.ref = 1; + V_VT(&vSrc) = VT_DISPATCH|VT_BYREF; + V_DISPATCHREF(&vSrc) = &pd; + hres = VariantCopyInd(&vSrc, &vSrc); + ok(hres == S_OK && d.ref == 2 && V_VT(&vSrc) == VT_DISPATCH, + "copy dispatch: expected 0x%08x, %d, %d, got 0x%08x, %d, %d\n", + S_OK, 2, VT_DISPATCH, hres, d.ref, V_VT(&vSrc)); +} + +static void test_IDispatchChangeTypeEx(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + LCID lcid; + DummyDispatch d = { &DummyDispatch_VTable, 1, VT_UI1, FALSE }; + IDispatch* pd = (IDispatch*)&d; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + V_VT(&vSrc) = VT_DISPATCH; + V_DISPATCH(&vSrc) = pd; + + /* =>IUnknown in place */ + hres = VariantChangeTypeEx(&vSrc, &vSrc, lcid, 0, VT_UNKNOWN); + ok(hres == S_OK && d.ref == 1 && + V_VT(&vSrc) == VT_UNKNOWN && V_UNKNOWN(&vSrc) == (IUnknown*)pd, + "change disp(src=src): expected 0x%08x,%d,%d,%p, got 0x%08x,%d,%d,%p\n", + S_OK, 1, VT_UNKNOWN, pd, hres, d.ref, V_VT(&vSrc), V_UNKNOWN(&vSrc)); + + /* =>IUnknown */ + d.ref = 1; + V_VT(&vSrc) = VT_DISPATCH; + V_DISPATCH(&vSrc) = pd; + VariantInit(&vDst); + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, VT_UNKNOWN); + /* Note vSrc is not cleared, as final refcount is 2 */ + ok(hres == S_OK && d.ref == 2 && + V_VT(&vDst) == VT_UNKNOWN && V_UNKNOWN(&vDst) == (IUnknown*)pd, + "change disp(src,dst): expected 0x%08x,%d,%d,%p, got 0x%08x,%d,%d,%p\n", + S_OK, 2, VT_UNKNOWN, pd, hres, d.ref, V_VT(&vDst), V_UNKNOWN(&vDst)); + + /* FIXME: Verify that VARIANT_NOVALUEPROP prevents conversion to integral + * types. this requires that the xxxFromDisp tests work first. + */ +} + +/* VT_ERROR */ +static void test_ErrorChangeTypeEx(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + VARTYPE vt; + LCID lcid; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT hExpected = DISP_E_BADVARTYPE; + + V_VT(&vSrc) = VT_ERROR; + V_ERROR(&vSrc) = 1; + VariantInit(&vDst); + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt); + + if (vt == VT_ERROR) + hExpected = S_OK; + else + { + if (vt == VT_I8 || vt == VT_UI8) + { + if (HAVE_OLEAUT32_I8) + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt == VT_RECORD) + { + if (HAVE_OLEAUT32_RECORD) + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt <= VT_UINT && vt != (VARTYPE)15) + hExpected = DISP_E_TYPEMISMATCH; + } + if (IS_ANCIENT && IS_MODERN_VTYPE(vt)) + hExpected = DISP_E_BADVARTYPE; + + ok(hres == hExpected, + "change err: vt %d expected 0x%08x, got 0x%08x\n", vt, hExpected, hres); + } +} + +/* VT_EMPTY */ +static void test_EmptyChangeTypeEx(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + VARTYPE vt; + LCID lcid; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT hExpected = DISP_E_BADVARTYPE; + + VariantInit(&vSrc); + memset(&vDst, 0, sizeof(vDst)); + V_VT(&vDst) = VT_EMPTY; + + if (vt == VT_I8 || vt == VT_UI8) + { + if (HAVE_OLEAUT32_I8) + hExpected = S_OK; + } + else if (vt == VT_RECORD) + { + if (HAVE_OLEAUT32_RECORD) + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt == VT_VARIANT || vt == VT_DISPATCH || + vt == VT_UNKNOWN || vt == VT_ERROR) + { + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt <= VT_UINT && vt != (VARTYPE)15) + hExpected = S_OK; + + if (IS_ANCIENT && IS_MODERN_VTYPE(vt)) + hExpected = DISP_E_BADVARTYPE; + + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt); + + ok(hres == hExpected && (hres != S_OK || V_VT(&vDst) == vt), + "change empty: vt %d expected 0x%08x, got 0x%08x, vt %d\n", + vt, hExpected, hres, V_VT(&vDst)); + } +} + +/* VT_NULL */ +static void test_NullChangeTypeEx(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + VARTYPE vt; + LCID lcid; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + for (vt = 0; vt <= VT_BSTR_BLOB; vt++) + { + HRESULT hExpected = DISP_E_BADVARTYPE; + + VariantInit(&vSrc); + V_VT(&vSrc) = VT_NULL; + memset(&vDst, 0, sizeof(vDst)); + V_VT(&vDst) = VT_EMPTY; + + if (vt == VT_I8 || vt == VT_UI8) + { + if (HAVE_OLEAUT32_I8) + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt == VT_RECORD) + { + if (HAVE_OLEAUT32_RECORD) + hExpected = DISP_E_TYPEMISMATCH; + } + else if (vt == VT_NULL) + { + hExpected = S_OK; + } + else if (vt == VT_VARIANT || vt == VT_DISPATCH || + vt == VT_UNKNOWN || vt == VT_ERROR || + (vt <= VT_UINT && vt != (VARTYPE)15)) + hExpected = DISP_E_TYPEMISMATCH; + + if (IS_ANCIENT && IS_MODERN_VTYPE(vt)) + hExpected = DISP_E_BADVARTYPE; + + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, vt); + + ok(hres == hExpected && (hres != S_OK || V_VT(&vDst) == vt), + "change null: vt %d expected 0x%08x, got 0x%08x, vt %d\n", + vt, hExpected, hres, V_VT(&vDst)); + } +} + + +/* VT_UINT */ +static void test_UintChangeTypeEx(void) +{ + HRESULT hres; + VARIANTARG vSrc, vDst; + LCID lcid; + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + /* Converting a VT_UINT to a VT_INT does not check for overflow */ + V_VT(&vDst) = VT_EMPTY; + V_VT(&vSrc) = VT_UINT; + V_UI4(&vSrc) = -1; + hres = VariantChangeTypeEx(&vDst, &vSrc, lcid, 0, VT_I4); + ok(hres == S_OK && V_VT(&vDst) == VT_I4 && V_I4(&vDst) == -1, + "change uint: Expected %d,0x%08x,%d got %d,0x%08x,%d\n", + VT_I4, S_OK, -1, V_VT(&vDst), hres, V_I4(&vDst)); +} + +#define NUM_CUST_ITEMS 16 + +static void test_ClearCustData(void) +{ + WCHAR buff[sizeof(CUSTDATAITEM) * NUM_CUST_ITEMS / sizeof(WCHAR)]; + CUSTDATA ci; + unsigned i; + + CHECKPTR(ClearCustData); + + memset(buff, 0, sizeof(buff)); + + ci.cCustData = NUM_CUST_ITEMS; + /* This is a bit tricky. We use SysAllocStringByteLen to allocate the + * array, since native uses an internal IMalloc interface for allocating + * its memory, while Wine uses HeapAlloc(). Doing this ensures we allocate + * using the correct function whether with native or builtin. + */ + ci.prgCustData = (LPCUSTDATAITEM)SysAllocStringByteLen((LPCSTR)buff, sizeof(buff)); + for (i = 0; i < NUM_CUST_ITEMS; i++) + VariantInit(&ci.prgCustData[i].varValue); + pClearCustData(&ci); + ok(!ci.cCustData && !ci.prgCustData, "ClearCustData didn't clear fields!\n"); +} + +static void test_NullByRef(void) +{ + VARIANT v1, v2; + HRESULT hRes; + + VariantClear(&v1); + VariantClear(&v2); + V_VT(&v1) = VT_BYREF|VT_VARIANT; + V_BYREF(&v1) = 0; + + hRes = VariantChangeTypeEx(&v2, &v1, 0, 0, VT_I4); + ok(hRes == DISP_E_TYPEMISMATCH, "VariantChangeTypeEx should return DISP_E_TYPEMISMATCH\n"); + + VariantClear(&v1); + V_VT(&v1) = VT_BYREF|VT_VARIANT; + V_BYREF(&v1) = 0; + V_VT(&v2) = VT_I4; + V_I4(&v2) = 123; + + hRes = VariantChangeTypeEx(&v2, &v1, 0, 0, VT_VARIANT); + ok(hRes == DISP_E_TYPEMISMATCH, "VariantChangeTypeEx should return DISP_E_TYPEMISMATCH\n"); + ok(V_VT(&v2) == VT_I4 && V_I4(&v2) == 123, "VariantChangeTypeEx shouldn't change pvargDest\n"); + + hRes = VariantChangeTypeEx(&v2, &v1, 0, 0, VT_BYREF|VT_I4); + ok(hRes == DISP_E_TYPEMISMATCH, "VariantChangeTypeEx should return DISP_E_TYPEMISMATCH\n"); + + hRes = VariantChangeTypeEx(&v2, &v1, 0, 0, 0x3847); + ok(hRes == DISP_E_BADVARTYPE, "VariantChangeTypeEx should return DISP_E_BADVARTYPE\n"); +} + +/* Dst Variant should remain unchanged if VariantChangeType cannot convert */ +static void test_ChangeType_keep_dst(void) +{ + VARIANT v1, v2; + BSTR bstr; + static const WCHAR testW[] = {'t','e','s','t',0}; + HRESULT hres; + + bstr = SysAllocString(testW); + VariantClear(&v1); + VariantClear(&v2); + V_VT(&v1) = VT_BSTR; + V_BSTR(&v1) = bstr; + hres = VariantChangeTypeEx(&v1, &v1, 0, 0, VT_INT); + ok(hres == DISP_E_TYPEMISMATCH, "VariantChangeTypeEx returns %08x\n", hres); + ok(V_VT(&v1) == VT_BSTR && V_BSTR(&v1) == bstr, "VariantChangeTypeEx changed dst variant\n"); + V_VT(&v2) = VT_INT; + V_INT(&v2) = 4; + hres = VariantChangeTypeEx(&v2, &v1, 0, 0, VT_INT); + ok(hres == DISP_E_TYPEMISMATCH, "VariantChangeTypeEx returns %08x\n", hres); + ok(V_VT(&v2) == VT_INT && V_INT(&v2) == 4, "VariantChangeTypeEx changed dst variant\n"); + SysFreeString(bstr); +} + +START_TEST(vartype) +{ + hOleaut32 = GetModuleHandleA("oleaut32.dll"); + + trace("LCID's: System=0x%08x, User=0x%08x\n", GetSystemDefaultLCID(), + GetUserDefaultLCID()); + + test_VarI1FromI2(); + test_VarI1FromI4(); + test_VarI1FromI8(); + test_VarI1FromUI1(); + test_VarI1FromUI2(); + test_VarI1FromUI4(); + test_VarI1FromUI8(); + test_VarI1FromBool(); + test_VarI1FromR4(); + test_VarI1FromR8(); + test_VarI1FromDate(); + test_VarI1FromCy(); + test_VarI1FromDec(); + test_VarI1FromStr(); + test_VarUI1FromDisp(); + test_VarI1Copy(); + test_VarI1ChangeTypeEx(); + + test_VarUI1FromI1(); + test_VarUI1FromI2(); + test_VarUI1FromI4(); + test_VarUI1FromI8(); + test_VarUI1FromUI2(); + test_VarUI1FromUI4(); + test_VarUI1FromUI8(); + test_VarUI1FromBool(); + test_VarUI1FromR4(); + test_VarUI1FromR8(); + test_VarUI1FromDate(); + test_VarUI1FromCy(); + test_VarUI1FromDec(); + test_VarUI1FromStr(); + test_VarUI1Copy(); + test_VarUI1ChangeTypeEx(); + + test_VarI2FromI1(); + test_VarI2FromI4(); + test_VarI2FromI8(); + test_VarI2FromUI1(); + test_VarI2FromUI2(); + test_VarI2FromUI4(); + test_VarI2FromUI8(); + test_VarI2FromBool(); + test_VarI2FromR4(); + test_VarI2FromR8(); + test_VarI2FromDate(); + test_VarI2FromCy(); + test_VarI2FromDec(); + test_VarI2FromStr(); + test_VarI2Copy(); + test_VarI2ChangeTypeEx(); + + test_VarUI2FromI1(); + test_VarUI2FromI2(); + test_VarUI2FromI4(); + test_VarUI2FromI8(); + test_VarUI2FromUI1(); + test_VarUI2FromUI4(); + test_VarUI2FromUI8(); + test_VarUI2FromBool(); + test_VarUI2FromR4(); + test_VarUI2FromR8(); + test_VarUI2FromDate(); + test_VarUI2FromCy(); + test_VarUI2FromDec(); + test_VarUI2FromStr(); + test_VarUI2Copy(); + test_VarUI2ChangeTypeEx(); + + test_VarI4FromI1(); + test_VarI4FromI2(); + test_VarI4FromI8(); + test_VarI4FromUI1(); + test_VarI4FromUI2(); + test_VarI4FromUI4(); + test_VarI4FromUI8(); + test_VarI4FromBool(); + test_VarI4FromR4(); + test_VarI4FromR8(); + test_VarI4FromDate(); + test_VarI4FromCy(); + test_VarI4FromDec(); + test_VarI4FromStr(); + test_VarI4Copy(); + test_VarI4ChangeTypeEx(); + + test_VarUI4FromI1(); + test_VarUI4FromI2(); + test_VarUI4FromUI2(); + test_VarUI4FromI8(); + test_VarUI4FromUI1(); + test_VarUI4FromI4(); + test_VarUI4FromUI8(); + test_VarUI4FromBool(); + test_VarUI4FromR4(); + test_VarUI4FromR8(); + test_VarUI4FromDate(); + test_VarUI4FromCy(); + test_VarUI4FromDec(); + test_VarUI4FromStr(); + test_VarUI4Copy(); + test_VarUI4ChangeTypeEx(); + + test_VarI8FromI1(); + test_VarI8FromUI1(); + test_VarI8FromI2(); + test_VarI8FromUI2(); + test_VarI8FromUI4(); + test_VarI8FromR4(); + test_VarI8FromR8(); + test_VarI8FromBool(); + test_VarI8FromUI8(); + test_VarI8FromCy(); + test_VarI8FromDec(); + test_VarI8FromDate(); + test_VarI8FromStr(); + test_VarI8Copy(); + test_VarI8ChangeTypeEx(); + + test_VarUI8FromI1(); + test_VarUI8FromUI1(); + test_VarUI8FromI2(); + test_VarUI8FromUI2(); + test_VarUI8FromUI4(); + test_VarUI8FromR4(); + test_VarUI8FromR8(); + test_VarUI8FromBool(); + test_VarUI8FromI8(); + test_VarUI8FromCy(); + test_VarUI8FromDec(); + test_VarUI8FromDate(); + test_VarUI8FromStr(); + test_VarUI8Copy(); + test_VarUI8ChangeTypeEx(); + + test_VarR4FromI1(); + test_VarR4FromUI1(); + test_VarR4FromI2(); + test_VarR4FromUI2(); + test_VarR4FromI4(); + test_VarR4FromUI4(); + test_VarR4FromR8(); + test_VarR4FromBool(); + test_VarR4FromCy(); + test_VarR4FromI8(); + test_VarR4FromUI8(); + test_VarR4FromDec(); + test_VarR4FromDate(); + test_VarR4FromStr(); + test_VarR4Copy(); + test_VarR4ChangeTypeEx(); + + test_VarR8FromI1(); + test_VarR8FromUI1(); + test_VarR8FromI2(); + test_VarR8FromUI2(); + test_VarR8FromI4(); + test_VarR8FromUI4(); + test_VarR8FromR4(); + test_VarR8FromBool(); + test_VarR8FromCy(); + test_VarR8FromI8(); + test_VarR8FromUI8(); + test_VarR8FromDec(); + test_VarR8FromDate(); + test_VarR8FromStr(); + test_VarR8Copy(); + test_VarR8ChangeTypeEx(); + test_VarR8Round(); + + test_VarDateFromI1(); + test_VarDateFromUI1(); + test_VarDateFromI2(); + test_VarDateFromUI2(); + test_VarDateFromI4(); + test_VarDateFromUI4(); + test_VarDateFromR4(); + test_VarDateFromR8(); + test_VarDateFromBool(); + test_VarDateFromCy(); + test_VarDateFromI8(); + test_VarDateFromUI8(); + test_VarDateFromDec(); + test_VarDateFromStr(); + test_VarDateCopy(); + test_VarDateChangeTypeEx(); + + test_VarCyFromI1(); + test_VarCyFromUI1(); + test_VarCyFromI2(); + test_VarCyFromUI2(); + test_VarCyFromI4(); + test_VarCyFromUI4(); + test_VarCyFromR4(); + test_VarCyFromR8(); + test_VarCyFromBool(); + test_VarCyFromI8(); + test_VarCyFromUI8(); + test_VarCyFromDec(); + test_VarCyFromDate(); + + test_VarCyAdd(); + test_VarCyMul(); + test_VarCySub(); + test_VarCyAbs(); + test_VarCyNeg(); + test_VarCyMulI4(); + test_VarCyMulI8(); + test_VarCyCmp(); + test_VarCyCmpR8(); + test_VarCyRound(); + test_VarCyFix(); + test_VarCyInt(); + + test_VarDecFromI1(); + test_VarDecFromI2(); + test_VarDecFromI4(); + test_VarDecFromI8(); + test_VarDecFromUI1(); + test_VarDecFromUI2(); + test_VarDecFromUI4(); + test_VarDecFromUI8(); + test_VarDecFromR4(); + test_VarDecFromR8(); + test_VarDecFromDate(); + test_VarDecFromStr(); + test_VarDecFromCy(); + test_VarDecFromDate(); + test_VarDecFromBool(); + + test_VarDecAbs(); + test_VarDecNeg(); + test_VarDecAdd(); + test_VarDecSub(); + test_VarDecCmp(); + test_VarDecMul(); + test_VarDecDiv(); + + test_VarBoolFromI1(); + test_VarBoolFromUI1(); + test_VarBoolFromI2(); + test_VarBoolFromUI2(); + test_VarBoolFromI4(); + test_VarBoolFromUI4(); + test_VarBoolFromR4(); + test_VarBoolFromR8(); + test_VarBoolFromCy(); + test_VarBoolFromI8(); + test_VarBoolFromUI8(); + test_VarBoolFromDec(); + test_VarBoolFromDate(); + test_VarBoolFromStr(); + test_VarBoolCopy(); + test_VarBoolChangeTypeEx(); + + test_VarBstrFromR4(); + test_VarBstrFromDate(); + test_VarBstrFromDec(); + test_VarBstrCmp(); + test_SysStringLen(); + test_SysStringByteLen(); + test_SysAllocString(); + test_SysAllocStringLen(); + test_SysAllocStringByteLen(); + test_SysReAllocString(); + test_SysReAllocStringLen(); + test_BstrCopy(); + test_VarBstrCat(); + + test_IUnknownClear(); + test_IUnknownCopy(); + test_IUnknownChangeTypeEx(); + + test_IDispatchClear(); + test_IDispatchCopy(); + test_IDispatchChangeTypeEx(); + + test_ErrorChangeTypeEx(); + test_EmptyChangeTypeEx(); + test_NullChangeTypeEx(); + test_UintChangeTypeEx(); + + test_ClearCustData(); + + test_NullByRef(); + test_ChangeType_keep_dst(); +} diff --git a/rostests/winetests/riched20/editor.c b/rostests/winetests/riched20/editor.c new file mode 100644 index 00000000000..5fb59750285 --- /dev/null +++ b/rostests/winetests/riched20/editor.c @@ -0,0 +1,2635 @@ +/* +* Unit test suite for rich edit control +* +* Copyright 2006 Google (Thomas Kho) +* Copyright 2007 Matt Finnicum +* Copyright 2007 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static HMODULE hmoduleRichEdit; + +static HWND new_window(LPCTSTR lpClassName, DWORD dwStyle, HWND parent) { + HWND hwnd; + hwnd = CreateWindow(lpClassName, NULL, dwStyle|WS_POPUP|WS_HSCROLL|WS_VSCROLL + |WS_VISIBLE, 0, 0, 200, 60, parent, NULL, + hmoduleRichEdit, NULL); + ok(hwnd != NULL, "class: %s, error: %d\n", lpClassName, (int) GetLastError()); + return hwnd; +} + +static HWND new_richedit(HWND parent) { + return new_window(RICHEDIT_CLASS, ES_MULTILINE, parent); +} + +static const char haystack[] = "WINEWine wineWine wine WineWine"; + /* ^0 ^10 ^20 ^30 */ + +struct find_s { + int start; + int end; + const char *needle; + int flags; + int expected_loc; + int _todo_wine; +}; + + +struct find_s find_tests[] = { + /* Find in empty text */ + {0, -1, "foo", FR_DOWN, -1, 0}, + {0, -1, "foo", 0, -1, 0}, + {0, -1, "", FR_DOWN, -1, 0}, + {20, 5, "foo", FR_DOWN, -1, 0}, + {5, 20, "foo", FR_DOWN, -1, 0} +}; + +struct find_s find_tests2[] = { + /* No-result find */ + {0, -1, "foo", FR_DOWN | FR_MATCHCASE, -1, 0}, + {5, 20, "WINE", FR_DOWN | FR_MATCHCASE, -1, 0}, + + /* Subsequent finds */ + {0, -1, "Wine", FR_DOWN | FR_MATCHCASE, 4, 0}, + {5, 31, "Wine", FR_DOWN | FR_MATCHCASE, 13, 0}, + {14, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0}, + {24, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0}, + + /* Find backwards */ + {19, 20, "Wine", FR_MATCHCASE, 13, 0}, + {10, 20, "Wine", FR_MATCHCASE, 4, 0}, + {20, 10, "Wine", FR_MATCHCASE, 13, 0}, + + /* Case-insensitive */ + {1, 31, "wInE", FR_DOWN, 4, 0}, + {1, 31, "Wine", FR_DOWN, 4, 0}, + + /* High-to-low ranges */ + {20, 5, "Wine", FR_DOWN, -1, 0}, + {2, 1, "Wine", FR_DOWN, -1, 0}, + {30, 29, "Wine", FR_DOWN, -1, 0}, + {20, 5, "Wine", 0, 13, 0}, + + /* Find nothing */ + {5, 10, "", FR_DOWN, -1, 0}, + {10, 5, "", FR_DOWN, -1, 0}, + {0, -1, "", FR_DOWN, -1, 0}, + {10, 5, "", 0, -1, 0}, + + /* Whole-word search */ + {0, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0}, + {0, -1, "win", FR_DOWN | FR_WHOLEWORD, -1, 0}, + {13, -1, "wine", FR_DOWN | FR_WHOLEWORD, 18, 0}, + {0, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 0, 0}, + {10, -1, "winewine", FR_DOWN | FR_WHOLEWORD, 23, 0}, + {11, -1, "winewine", FR_WHOLEWORD, 0, 0}, + {31, -1, "winewine", FR_WHOLEWORD, 23, 0}, + + /* Bad ranges */ + {5, 200, "XXX", FR_DOWN, -1, 0}, + {-20, 20, "Wine", FR_DOWN, -1, 0}, + {-20, 20, "Wine", FR_DOWN, -1, 0}, + {-15, -20, "Wine", FR_DOWN, -1, 0}, + {1<<12, 1<<13, "Wine", FR_DOWN, -1, 0}, + + /* Check the case noted in bug 4479 where matches at end aren't recognized */ + {23, 31, "Wine", FR_DOWN | FR_MATCHCASE, 23, 0}, + {27, 31, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0}, + {27, 32, "Wine", FR_DOWN | FR_MATCHCASE, 27, 0}, + {13, 31, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0}, + {13, 32, "WineWine", FR_DOWN | FR_MATCHCASE, 23, 0}, + + /* The backwards case of bug 4479; bounds look right + * Fails because backward find is wrong */ + {19, 20, "WINE", FR_MATCHCASE, 0, 0}, + {0, 20, "WINE", FR_MATCHCASE, -1, 0} +}; + +static void check_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *f, int id) { + int findloc; + FINDTEXT ft; + memset(&ft, 0, sizeof(ft)); + ft.chrg.cpMin = f->start; + ft.chrg.cpMax = f->end; + ft.lpstrText = f->needle; + findloc = SendMessage(hwnd, EM_FINDTEXT, f->flags, (LPARAM) &ft); + ok(findloc == f->expected_loc, + "EM_FINDTEXT(%s,%d) '%s' in range(%d,%d), flags %08x, got start at %d\n", + name, id, f->needle, f->start, f->end, f->flags, findloc); +} + +static void check_EM_FINDTEXTEX(HWND hwnd, const char *name, struct find_s *f, + int id) { + int findloc; + FINDTEXTEX ft; + memset(&ft, 0, sizeof(ft)); + ft.chrg.cpMin = f->start; + ft.chrg.cpMax = f->end; + ft.lpstrText = f->needle; + findloc = SendMessage(hwnd, EM_FINDTEXTEX, f->flags, (LPARAM) &ft); + ok(findloc == f->expected_loc, + "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n", + name, id, f->needle, f->start, f->end, f->flags, findloc); + ok(ft.chrgText.cpMin == f->expected_loc, + "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, start at %d\n", + name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMin); + ok(ft.chrgText.cpMax == ((f->expected_loc == -1) ? -1 + : f->expected_loc + strlen(f->needle)), + "EM_FINDTEXTEX(%s,%d) '%s' in range(%d,%d), flags %08x, end at %d\n", + name, id, f->needle, f->start, f->end, f->flags, ft.chrgText.cpMax); +} + +static void run_tests_EM_FINDTEXT(HWND hwnd, const char *name, struct find_s *find, + int num_tests) +{ + int i; + + for (i = 0; i < num_tests; i++) { + if (find[i]._todo_wine) { + todo_wine { + check_EM_FINDTEXT(hwnd, name, &find[i], i); + check_EM_FINDTEXTEX(hwnd, name, &find[i], i); + } + } else { + check_EM_FINDTEXT(hwnd, name, &find[i], i); + check_EM_FINDTEXTEX(hwnd, name, &find[i], i); + } + } +} + +static void test_EM_FINDTEXT(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + + /* Empty rich edit control */ + run_tests_EM_FINDTEXT(hwndRichEdit, "1", find_tests, + sizeof(find_tests)/sizeof(struct find_s)); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack); + + /* Haystack text */ + run_tests_EM_FINDTEXT(hwndRichEdit, "2", find_tests2, + sizeof(find_tests2)/sizeof(struct find_s)); + + DestroyWindow(hwndRichEdit); +} + +static const struct getline_s { + int line; + size_t buffer_len; + const char *text; +} gl[] = { + {0, 10, "foo bar\r"}, + {1, 10, "\r"}, + {2, 10, "bar\r"}, + {3, 10, "\r"}, + + /* Buffer smaller than line length */ + {0, 2, "foo bar\r"}, + {0, 1, "foo bar\r"}, + {0, 0, "foo bar\r"} +}; + +static void test_EM_GETLINE(void) +{ + int i; + HWND hwndRichEdit = new_richedit(NULL); + static const int nBuf = 1024; + char dest[1024], origdest[1024]; + const char text[] = "foo bar\n" + "\n" + "bar\n"; + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + + memset(origdest, 0xBB, nBuf); + for (i = 0; i < sizeof(gl)/sizeof(struct getline_s); i++) + { + int nCopied; + int expected_nCopied = min(gl[i].buffer_len, strlen(gl[i].text)); + int expected_bytes_written = min(gl[i].buffer_len, strlen(gl[i].text) + 1); + memset(dest, 0xBB, nBuf); + *(WORD *) dest = gl[i].buffer_len; + + /* EM_GETLINE appends a "\r\0" to the end of the line + * nCopied counts up to and including the '\r' */ + nCopied = SendMessage(hwndRichEdit, EM_GETLINE, gl[i].line, (LPARAM) dest); + ok(nCopied == expected_nCopied, "%d: %d!=%d\n", i, nCopied, + expected_nCopied); + /* two special cases since a parameter is passed via dest */ + if (gl[i].buffer_len == 0) + ok(!dest[0] && !dest[1] && !strncmp(dest+2, origdest+2, nBuf-2), + "buffer_len=0\n"); + else if (gl[i].buffer_len == 1) + ok(dest[0] == gl[i].text[0] && !dest[1] && + !strncmp(dest+2, origdest+2, nBuf-2), "buffer_len=1\n"); + else + { + ok(!strncmp(dest, gl[i].text, expected_bytes_written), + "%d: expected_bytes_written=%d\n", i, expected_bytes_written); + ok(!strncmp(dest + expected_bytes_written, origdest + + expected_bytes_written, nBuf - expected_bytes_written), + "%d: expected_bytes_written=%d\n", i, expected_bytes_written); + } + } + + DestroyWindow(hwndRichEdit); +} + +static int get_scroll_pos_y(HWND hwnd) +{ + POINT p = {-1, -1}; + SendMessage(hwnd, EM_GETSCROLLPOS, 0, (LPARAM) &p); + ok(p.x != -1 && p.y != -1, "p.x:%d p.y:%d\n", p.x, p.y); + return p.y; +} + +static void move_cursor(HWND hwnd, long charindex) +{ + CHARRANGE cr; + cr.cpMax = charindex; + cr.cpMin = charindex; + SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr); +} + +static void line_scroll(HWND hwnd, int amount) +{ + SendMessage(hwnd, EM_LINESCROLL, 0, amount); +} + +static void test_EM_SCROLLCARET(void) +{ + int prevY, curY; + HWND hwndRichEdit = new_richedit(NULL); + const char text[] = "aa\n" + "this is a long line of text that should be longer than the " + "control's width\n" + "cc\n" + "dd\n" + "ee\n" + "ff\n" + "gg\n" + "hh\n"; + + /* Can't verify this */ + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + + /* Caret above visible window */ + line_scroll(hwndRichEdit, 3); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY != curY, "%d == %d\n", prevY, curY); + + /* Caret below visible window */ + move_cursor(hwndRichEdit, sizeof(text) - 1); + line_scroll(hwndRichEdit, -3); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY != curY, "%d == %d\n", prevY, curY); + + /* Caret in visible window */ + move_cursor(hwndRichEdit, sizeof(text) - 2); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY == curY, "%d != %d\n", prevY, curY); + + /* Caret still in visible window */ + line_scroll(hwndRichEdit, -1); + prevY = get_scroll_pos_y(hwndRichEdit); + SendMessage(hwndRichEdit, EM_SCROLLCARET, 0, 0); + curY = get_scroll_pos_y(hwndRichEdit); + ok(prevY == curY, "%d != %d\n", prevY, curY); + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_SETCHARFORMAT(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + CHARFORMAT2 cf2; + int rc = 0; + + /* Invalid flags, CHARFORMAT2 structure blanked out */ + memset(&cf2, 0, sizeof(cf2)); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0, + (LPARAM) &cf2); + ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc); + + /* A valid flag, CHARFORMAT2 structure blanked out */ + memset(&cf2, 0, sizeof(cf2)); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT, + (LPARAM) &cf2); + ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc); + + /* A valid flag, CHARFORMAT2 structure blanked out */ + memset(&cf2, 0, sizeof(cf2)); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, + (LPARAM) &cf2); + ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc); + + /* A valid flag, CHARFORMAT2 structure blanked out */ + memset(&cf2, 0, sizeof(cf2)); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD, + (LPARAM) &cf2); + ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc); + + /* A valid flag, CHARFORMAT2 structure blanked out */ + memset(&cf2, 0, sizeof(cf2)); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, + (LPARAM) &cf2); + ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc); + + /* Invalid flags, CHARFORMAT2 structure minimally filled */ + memset(&cf2, 0, sizeof(cf2)); + cf2.cbSize = sizeof(CHARFORMAT2); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) 0xfffffff0, + (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + /* A valid flag, CHARFORMAT2 structure minimally filled */ + memset(&cf2, 0, sizeof(cf2)); + cf2.cbSize = sizeof(CHARFORMAT2); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_DEFAULT, + (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + /* A valid flag, CHARFORMAT2 structure minimally filled */ + memset(&cf2, 0, sizeof(cf2)); + cf2.cbSize = sizeof(CHARFORMAT2); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, + (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + /* A valid flag, CHARFORMAT2 structure minimally filled */ + memset(&cf2, 0, sizeof(cf2)); + cf2.cbSize = sizeof(CHARFORMAT2); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD, + (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + /* A valid flag, CHARFORMAT2 structure minimally filled */ + memset(&cf2, 0, sizeof(cf2)); + cf2.cbSize = sizeof(CHARFORMAT2); + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, + (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_SETTEXTMODE(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + CHARFORMAT2 cf2, cf2test; + CHARRANGE cr; + int rc = 0; + + /*Test that EM_SETTEXTMODE fails if text exists within the control*/ + /*Insert text into the control*/ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine"); + + /*Attempt to change the control to plain text mode*/ + rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0); + ok(rc != 0, "EM_SETTEXTMODE: changed text mode in control containing text - returned: %d\n", rc); + + /*Test that EM_SETTEXTMODE does not allow rich edit text to be pasted. + If rich text is pasted, it should have the same formatting as the rest + of the text in the control*/ + + /*Italicize the text + *NOTE: If the default text was already italicized, the test will simply + reverse; in other words, it will copy a regular "wine" into a plain + text window that uses an italicized format*/ + cf2.cbSize = sizeof(CHARFORMAT2); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, + (LPARAM) &cf2); + + cf2.dwMask = CFM_ITALIC | cf2.dwMask; + cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects; + + /*EM_SETCHARFORMAT is not yet fully implemented for all WPARAMs in wine; + however, SCF_ALL has been implemented*/ + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine"); + + /*Select the string "wine"*/ + cr.cpMin = 0; + cr.cpMax = 4; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + + /*Copy the italicized "wine" to the clipboard*/ + SendMessage(hwndRichEdit, WM_COPY, 0, 0); + + /*Reset the formatting to default*/ + cf2.dwEffects = CFE_ITALIC^cf2.dwEffects; + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + /*Clear the text in the control*/ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) ""); + + /*Switch to Plain Text Mode*/ + rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_PLAINTEXT, 0); + ok(rc == 0, "EM_SETTEXTMODE: unable to switch to plain text mode with empty control: returned: %d\n", rc); + + /*Input "wine" again in normal format*/ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine"); + + /*Paste the italicized "wine" into the control*/ + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + + /*Select a character from the first "wine" string*/ + cr.cpMin = 2; + cr.cpMax = 3; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + + /*Retrieve its formatting*/ + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, + (LPARAM) &cf2); + + /*Select a character from the second "wine" string*/ + cr.cpMin = 5; + cr.cpMax = 6; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + + /*Retrieve its formatting*/ + cf2test.cbSize = sizeof(CHARFORMAT2); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, + (LPARAM) &cf2test); + + /*Compare the two formattings*/ + ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects), + "two formats found in plain text mode - cf2.dwEffects: %x cf2test.dwEffects: %x\n", + cf2.dwEffects, cf2test.dwEffects); + /*Test TM_RICHTEXT by: switching back to Rich Text mode + printing "wine" in the current format(normal) + pasting "wine" from the clipboard(italicized) + comparing the two formats(should differ)*/ + + /*Attempt to switch with text in control*/ + rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0); + ok(rc != 0, "EM_SETTEXTMODE: changed from plain text to rich text with text in control - returned: %d\n", rc); + + /*Clear control*/ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) ""); + + /*Switch into Rich Text mode*/ + rc = SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0); + ok(rc == 0, "EM_SETTEXTMODE: unable to change to rich text with empty control - returned: %d\n", rc); + + /*Print "wine" in normal formatting into the control*/ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine"); + + /*Paste italicized "wine" into the control*/ + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + + /*Select text from the first "wine" string*/ + cr.cpMin = 1; + cr.cpMax = 3; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + + /*Retrieve its formatting*/ + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, + (LPARAM) &cf2); + + /*Select text from the second "wine" string*/ + cr.cpMin = 6; + cr.cpMax = 7; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + + /*Retrieve its formatting*/ + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, + (LPARAM) &cf2test); + + /*Test that the two formattings are not the same*/ + todo_wine ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects != cf2test.dwEffects), + "expected different formats - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n", + cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects); + + DestroyWindow(hwndRichEdit); +} + +static void test_TM_PLAINTEXT(void) +{ + /*Tests plain text properties*/ + + HWND hwndRichEdit = new_richedit(NULL); + CHARFORMAT2 cf2, cf2test; + CHARRANGE cr; + int rc = 0; + + /*Switch to plain text mode*/ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) ""); + SendMessage(hwndRichEdit, EM_SETTEXTMODE, TM_PLAINTEXT, 0); + + /*Fill control with text*/ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "Is Wine an emulator? No it's not"); + + /*Select some text and bold it*/ + + cr.cpMin = 10; + cr.cpMax = 20; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + cf2.cbSize = sizeof(CHARFORMAT2); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, + (LPARAM) &cf2); + + cf2.dwMask = CFM_BOLD | cf2.dwMask; + cf2.dwEffects = CFE_BOLD ^ cf2.dwEffects; + + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2); + ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc); + + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_WORD | SCF_SELECTION, (LPARAM) &cf2); + ok(rc == 0, "EM_SETCHARFORMAT returned %d instead of 0\n", rc); + + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM)&cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + /*Get the formatting of those characters*/ + + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2); + + /*Get the formatting of some other characters*/ + cf2test.cbSize = sizeof(CHARFORMAT2); + cr.cpMin = 21; + cr.cpMax = 30; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test); + + /*Test that they are the same as plain text allows only one formatting*/ + + ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects), + "two selections' formats differ - cf2.dwMask: %x, cf2test.dwMask %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n", + cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects); + + /*Fill the control with a "wine" string, which when inserted will be bold*/ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine"); + + /*Copy the bolded "wine" string*/ + + cr.cpMin = 0; + cr.cpMax = 4; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + SendMessage(hwndRichEdit, WM_COPY, 0, 0); + + /*Swap back to rich text*/ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) ""); + SendMessage(hwndRichEdit, EM_SETTEXTMODE, (WPARAM) TM_RICHTEXT, 0); + + /*Set the default formatting to bold italics*/ + + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, (LPARAM) &cf2); + cf2.dwMask |= CFM_ITALIC; + cf2.dwEffects ^= CFE_ITALIC; + rc = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2); + ok(rc == 1, "EM_SETCHARFORMAT returned %d instead of 1\n", rc); + + /*Set the text in the control to "wine", which will be bold and italicized*/ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "wine"); + + /*Paste the plain text "wine" string, which should take the insert + formatting, which at the moment is bold italics*/ + + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + + /*Select the first "wine" string and retrieve its formatting*/ + + cr.cpMin = 1; + cr.cpMax = 3; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2); + + /*Select the second "wine" string and retrieve its formatting*/ + + cr.cpMin = 5; + cr.cpMax = 7; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_SELECTION, (LPARAM) &cf2test); + + /*Compare the two formattings. They should be the same.*/ + + ok((cf2.dwMask == cf2test.dwMask) && (cf2.dwEffects == cf2test.dwEffects), + "Copied text retained formatting - cf2.dwMask: %x, cf2test.dwMask: %x, cf2.dwEffects: %x, cf2test.dwEffects: %x\n", + cf2.dwMask, cf2test.dwMask, cf2.dwEffects, cf2test.dwEffects); + DestroyWindow(hwndRichEdit); +} + +static void test_WM_GETTEXT(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + static const char text[] = "Hello. My name is RichEdit!"; + char buffer[1024] = {0}; + int result; + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer,text); + ok(result == 0, + "WM_GETTEXT: settext and gettext differ. strcmp: %d\n", result); + DestroyWindow(hwndRichEdit); +} + +/* FIXME: need to test unimplemented options and robustly test wparam */ +static void test_EM_SETOPTIONS(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + static const char text[] = "Hello. My name is RichEdit!"; + char buffer[1024] = {0}; + + /* NEGATIVE TESTING - NO OPTIONS SET */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, 0); + + /* testing no readonly by sending 'a' to the control*/ + SetFocus(hwndRichEdit); + SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + ok(buffer[0]=='a', + "EM_SETOPTIONS: Text not changed! s1:%s s2:%s\n", text, buffer); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + + /* READONLY - sending 'a' to the control */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + SendMessage(hwndRichEdit, EM_SETOPTIONS, ECOOP_SET, ECO_READONLY); + SetFocus(hwndRichEdit); + SendMessage(hwndRichEdit, WM_CHAR, 'a', 0x1E0001); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + ok(buffer[0]==text[0], + "EM_SETOPTIONS: Text changed! s1:%s s2:%s\n", text, buffer); + + DestroyWindow(hwndRichEdit); +} + +static void check_CFE_LINK_rcvd(HWND hwnd, int is_url, const char * url) +{ + CHARFORMAT2W text_format; + int link_present = 0; + text_format.cbSize = sizeof(text_format); + SendMessage(hwnd, EM_SETSEL, 0, 1); + SendMessage(hwnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM) &text_format); + link_present = text_format.dwEffects & CFE_LINK; + if (is_url) + { /* control text is url; should get CFE_LINK */ + ok(0 != link_present, "URL Case: CFE_LINK not set for [%s].\n", url); + } + else + { + ok(0 == link_present, "Non-URL Case: CFE_LINK set for [%s].\n", url); + } +} + +static HWND new_static_wnd(HWND parent) { + return new_window("Static", 0, parent); +} + +static void test_EM_AUTOURLDETECT(void) +{ + struct urls_s { + const char *text; + int is_url; + } urls[12] = { + {"winehq.org", 0}, + {"http://www.winehq.org", 1}, + {"http//winehq.org", 0}, + {"ww.winehq.org", 0}, + {"www.winehq.org", 1}, + {"ftp://192.168.1.1", 1}, + {"ftp//192.168.1.1", 0}, + {"mailto:your@email.com", 1}, + {"prospero:prosperoserver", 1}, + {"telnet:test", 1}, + {"news:newserver", 1}, + {"wais:waisserver", 1} + }; + + int i; + int urlRet=-1; + HWND hwndRichEdit, parent; + + parent = new_static_wnd(NULL); + hwndRichEdit = new_richedit(parent); + /* Try and pass EM_AUTOURLDETECT some test wParam values */ + urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0); + ok(urlRet==0, "Good wParam: urlRet is: %d\n", urlRet); + urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 1, 0); + ok(urlRet==0, "Good wParam2: urlRet is: %d\n", urlRet); + /* Windows returns -2147024809 (0x80070057) on bad wParam values */ + urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, 8, 0); + ok(urlRet==E_INVALIDARG, "Bad wParam: urlRet is: %d\n", urlRet); + urlRet=SendMessage(hwndRichEdit, EM_AUTOURLDETECT, (WPARAM)"h", (LPARAM)"h"); + ok(urlRet==E_INVALIDARG, "Bad wParam2: urlRet is: %d\n", urlRet); + /* for each url, check the text to see if CFE_LINK effect is present */ + for (i = 0; i < sizeof(urls)/sizeof(struct urls_s); i++) { + SendMessage(hwndRichEdit, EM_AUTOURLDETECT, FALSE, 0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text); + SendMessage(hwndRichEdit, WM_CHAR, 0, 0); + check_CFE_LINK_rcvd(hwndRichEdit, 0, urls[i].text); + SendMessage(hwndRichEdit, EM_AUTOURLDETECT, TRUE, 0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) urls[i].text); + SendMessage(hwndRichEdit, WM_CHAR, 0, 0); + check_CFE_LINK_rcvd(hwndRichEdit, urls[i].is_url, urls[i].text); + } + DestroyWindow(hwndRichEdit); + DestroyWindow(parent); +} + +static void test_EM_SCROLL(void) +{ + int i, j; + int r; /* return value */ + int expr; /* expected return value */ + HWND hwndRichEdit = new_richedit(NULL); + int y_before, y_after; /* units of lines of text */ + + /* test a richedit box containing a single line of text */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a");/* one line of text */ + expr = 0x00010000; + for (i = 0; i < 4; i++) { + static const int cmd[4] = { SB_PAGEDOWN, SB_PAGEUP, SB_LINEDOWN, SB_LINEUP }; + + r = SendMessage(hwndRichEdit, EM_SCROLL, cmd[i], 0); + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + ok(expr == r, "EM_SCROLL improper return value returned (i == %d). " + "Got 0x%08x, expected 0x%08x\n", i, r, expr); + ok(y_after == 0, "EM_SCROLL improper scroll. scrolled to line %d, not 1 " + "(i == %d)\n", y_after, i); + } + + /* + * test a richedit box that will scroll. There are two general + * cases: the case without any long lines and the case with a long + * line. + */ + for (i = 0; i < 2; i++) { /* iterate through different bodies of text */ + if (i == 0) + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "a\nb\nc\nd\ne"); + else + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) + "a LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE " + "LONG LINE LONG LINE LONG LINE LONG LINE LONG LINE " + "LONG LINE \nb\nc\nd\ne"); + for (j = 0; j < 12; j++) /* reset scrol position to top */ + SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); + + /* get first visible line */ + y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); /* page down */ + + /* get new current first visible line */ + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + + ok(((r & 0xffffff00) == 0x00010000) && + ((r & 0x000000ff) != 0x00000000), + "EM_SCROLL page down didn't scroll by a small positive number of " + "lines (r == 0x%08x)\n", r); + ok(y_after > y_before, "EM_SCROLL page down not functioning " + "(line %d scrolled to line %d\n", y_before, y_after); + + y_before = y_after; + + r = SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEUP, 0); /* page up */ + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + ok(((r & 0xffffff00) == 0x0001ff00), + "EM_SCROLL page up didn't scroll by a small negative number of lines " + "(r == 0x%08x)\n", r); + ok(y_after < y_before, "EM_SCROLL page up not functioning (line " + "%d scrolled to line %d\n", y_before, y_after); + + y_before = y_after; + + r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEDOWN, 0); /* line down */ + + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + + ok(r == 0x00010001, "EM_SCROLL line down didn't scroll by one line " + "(r == 0x%08x)\n", r); + ok(y_after -1 == y_before, "EM_SCROLL line down didn't go down by " + "1 line (%d scrolled to %d)\n", y_before, y_after); + + y_before = y_after; + + r = SendMessage(hwndRichEdit, EM_SCROLL, SB_LINEUP, 0); /* line up */ + + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + + ok(r == 0x0001ffff, "EM_SCROLL line up didn't scroll by one line " + "(r == 0x%08x)\n", r); + ok(y_after +1 == y_before, "EM_SCROLL line up didn't go up by 1 " + "line (%d scrolled to %d)\n", y_before, y_after); + + y_before = y_after; + + r = SendMessage(hwndRichEdit, EM_SCROLL, + SB_LINEUP, 0); /* lineup beyond top */ + + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + + ok(r == 0x00010000, + "EM_SCROLL line up returned indicating movement (0x%08x)\n", r); + ok(y_before == y_after, + "EM_SCROLL line up beyond top worked (%d)\n", y_after); + + y_before = y_after; + + r = SendMessage(hwndRichEdit, EM_SCROLL, + SB_PAGEUP, 0);/*page up beyond top */ + + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + + ok(r == 0x00010000, + "EM_SCROLL page up returned indicating movement (0x%08x)\n", r); + ok(y_before == y_after, + "EM_SCROLL page up beyond top worked (%d)\n", y_after); + + for (j = 0; j < 12; j++) /* page down all the way to the bottom */ + SendMessage(hwndRichEdit, EM_SCROLL, SB_PAGEDOWN, 0); + y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + r = SendMessage(hwndRichEdit, EM_SCROLL, + SB_PAGEDOWN, 0); /* page down beyond bot */ + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + + ok(r == 0x00010000, + "EM_SCROLL page down returned indicating movement (0x%08x)\n", r); + ok(y_before == y_after, + "EM_SCROLL page down beyond bottom worked (%d -> %d)\n", + y_before, y_after); + + y_before = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + SendMessage(hwndRichEdit, EM_SCROLL, + SB_LINEDOWN, 0); /* line down beyond bot */ + y_after = SendMessage(hwndRichEdit, EM_GETFIRSTVISIBLELINE, 0, 0); + + ok(r == 0x00010000, + "EM_SCROLL line down returned indicating movement (0x%08x)\n", r); + ok(y_before == y_after, + "EM_SCROLL line down beyond bottom worked (%d -> %d)\n", + y_before, y_after); + } + DestroyWindow(hwndRichEdit); +} + +static void test_EM_SETUNDOLIMIT(void) +{ + /* cases we test for: + * default behaviour - limiting at 100 undo's + * undo disabled - setting a limit of 0 + * undo limited - undo limit set to some to some number, like 2 + * bad input - sending a negative number should default to 100 undo's */ + + HWND hwndRichEdit = new_richedit(NULL); + CHARRANGE cr; + int i; + int result; + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x"); + cr.cpMin = 0; + cr.cpMax = 1; + SendMessage(hwndRichEdit, WM_COPY, 0, 0); + /*Load "x" into the clipboard. Paste is an easy, undo'able operation. + also, multiple pastes don't combine like WM_CHAR would */ + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + + /* first case - check the default */ + SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); + for (i=0; i<101; i++) /* Put 101 undo's on the stack */ + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + for (i=0; i<100; i++) /* Undo 100 of them */ + SendMessage(hwndRichEdit, WM_UNDO, 0, 0); + ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0), + "EM_SETUNDOLIMIT allowed more than a hundred undo's by default.\n"); + + /* second case - cannot undo */ + SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); + SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 0, 0); + SendMessage(hwndRichEdit, + WM_PASTE, 0, 0); /* Try to put something in the undo stack */ + ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0), + "EM_SETUNDOLIMIT allowed undo with UNDOLIMIT set to 0\n"); + + /* third case - set it to an arbitrary number */ + SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0, 0); + SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, 2, 0); + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + /* If SETUNDOLIMIT is working, there should only be two undo's after this */ + ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0,0), + "EM_SETUNDOLIMIT didn't allow the first undo with UNDOLIMIT set to 2\n"); + SendMessage(hwndRichEdit, WM_UNDO, 0, 0); + ok(SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0), + "EM_SETUNDOLIMIT didn't allow a second undo with UNDOLIMIT set to 2\n"); + SendMessage(hwndRichEdit, WM_UNDO, 0, 0); + ok(!SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0), + "EM_SETUNDOLIMIT allowed a third undo with UNDOLIMIT set to 2\n"); + + /* fourth case - setting negative numbers should default to 100 undos */ + SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); + result = SendMessage(hwndRichEdit, EM_SETUNDOLIMIT, -1, 0); + ok (result == 100, + "EM_SETUNDOLIMIT returned %d when set to -1, instead of 100\n",result); + + DestroyWindow(hwndRichEdit); +} + +static void test_ES_PASSWORD(void) +{ + /* This isn't hugely testable, so we're just going to run it through its paces */ + + HWND hwndRichEdit = new_richedit(NULL); + WCHAR result; + + /* First, check the default of a regular control */ + result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0); + ok (result == 0, + "EM_GETPASSWORDCHAR returned %c by default, instead of NULL\n",result); + + /* Now, set it to something normal */ + SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, 'x', 0); + result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0); + ok (result == 120, + "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result); + + /* Now, set it to something odd */ + SendMessage(hwndRichEdit, EM_SETPASSWORDCHAR, (WCHAR)1234, 0); + result = SendMessage(hwndRichEdit, EM_GETPASSWORDCHAR, 0, 0); + ok (result == 1234, + "EM_GETPASSWORDCHAR returned %c (%d) when set to 'x', instead of x (120)\n",result,result); + DestroyWindow(hwndRichEdit); +} + +static void test_WM_SETTEXT() +{ + HWND hwndRichEdit = new_richedit(NULL); + const char * TestItem1 = "TestSomeText"; + const char * TestItem2 = "TestSomeText\r"; + const char * TestItem2_after = "TestSomeText\r\n"; + const char * TestItem3 = "TestSomeText\rSomeMoreText\r"; + const char * TestItem3_after = "TestSomeText\r\nSomeMoreText\r\n"; + const char * TestItem4 = "TestSomeText\n\nTestSomeText"; + const char * TestItem4_after = "TestSomeText\r\n\r\nTestSomeText"; + const char * TestItem5 = "TestSomeText\r\r\nTestSomeText"; + const char * TestItem5_after = "TestSomeText TestSomeText"; + const char * TestItem6 = "TestSomeText\r\r\n\rTestSomeText"; + const char * TestItem6_after = "TestSomeText \r\nTestSomeText"; + const char * TestItem7 = "TestSomeText\r\n\r\r\n\rTestSomeText"; + const char * TestItem7_after = "TestSomeText\r\n \r\nTestSomeText"; + char buf[1024] = {0}; + LRESULT result; + + /* This test attempts to show that WM_SETTEXT on a riched20 control causes + any solitary \r to be converted to \r\n on return. Properly paired + \r\n are not affected. It also shows that the special sequence \r\r\n + gets converted to a single space. + */ + +#define TEST_SETTEXT(a, b) \ + result = SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) a); \ + ok (result == 1, "WM_SETTEXT returned %ld instead of 1\n", result); \ + result = SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buf); \ + ok (result == strlen(buf), \ + "WM_GETTEXT returned %ld instead of expected %u\n", \ + result, strlen(buf)); \ + result = strcmp(b, buf); \ + ok(result == 0, \ + "WM_SETTEXT round trip: strcmp = %ld\n", result); + + TEST_SETTEXT(TestItem1, TestItem1) + TEST_SETTEXT(TestItem2, TestItem2_after) + TEST_SETTEXT(TestItem3, TestItem3_after) + TEST_SETTEXT(TestItem3_after, TestItem3_after) + TEST_SETTEXT(TestItem4, TestItem4_after) + TEST_SETTEXT(TestItem5, TestItem5_after) + TEST_SETTEXT(TestItem6, TestItem6_after) + TEST_SETTEXT(TestItem7, TestItem7_after) + +#undef TEST_SETTEXT + DestroyWindow(hwndRichEdit); +} + +static void test_EM_SETTEXTEX(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + SETTEXTEX setText; + GETTEXTEX getText; + WCHAR TestItem1[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', 0}; + WCHAR TestItem2[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', + '\r', 0}; + const char * TestItem2_after = "TestSomeText\r\n"; + WCHAR TestItem3[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', + '\r','\n','\r','\n', 0}; + WCHAR TestItem3alt[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', + '\n','\n', 0}; + WCHAR TestItem3_after[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', + '\r','\r', 0}; + WCHAR TestItem4[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', + '\r','\r','\n','\r', + '\n', 0}; + WCHAR TestItem4_after[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', + ' ','\r', 0}; +#define MAX_BUF_LEN 1024 + WCHAR buf[MAX_BUF_LEN]; + int result; + CHARRANGE cr; + + setText.codepage = 1200; /* no constant for unicode */ + getText.codepage = 1200; /* no constant for unicode */ + getText.cb = MAX_BUF_LEN; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); + ok(lstrcmpW(buf, TestItem1) == 0, + "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n"); + + /* Unlike WM_SETTEXT/WM_GETTEXT pair, EM_SETTEXTEX/EM_GETTEXTEX does not + convert \r to \r\n on return + */ + setText.codepage = 1200; /* no constant for unicode */ + getText.codepage = 1200; /* no constant for unicode */ + getText.cb = MAX_BUF_LEN; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem2); + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); + ok(lstrcmpW(buf, TestItem2) == 0, + "EM_GETTEXTEX results not what was set by EM_SETTEXTEX\n"); + + /* However, WM_GETTEXT *does* see \r\n where EM_GETTEXTEX would see \r */ + SendMessage(hwndRichEdit, WM_GETTEXT, MAX_BUF_LEN, (LPARAM)buf); + ok(strcmp((const char *)buf, TestItem2_after) == 0, + "WM_GETTEXT did *not* see \\r converted to \\r\\n pairs.\n"); + + /* \r\n pairs get changed into \r */ + setText.codepage = 1200; /* no constant for unicode */ + getText.codepage = 1200; /* no constant for unicode */ + getText.cb = MAX_BUF_LEN; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3); + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); + ok(lstrcmpW(buf, TestItem3_after) == 0, + "EM_SETTEXTEX did not convert properly\n"); + + /* \n also gets changed to \r */ + setText.codepage = 1200; /* no constant for unicode */ + getText.codepage = 1200; /* no constant for unicode */ + getText.cb = MAX_BUF_LEN; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem3alt); + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); + ok(lstrcmpW(buf, TestItem3_after) == 0, + "EM_SETTEXTEX did not convert properly\n"); + + /* \r\r\n gets changed into single space */ + setText.codepage = 1200; /* no constant for unicode */ + getText.codepage = 1200; /* no constant for unicode */ + getText.cb = MAX_BUF_LEN; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem4); + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); + ok(lstrcmpW(buf, TestItem4_after) == 0, + "EM_SETTEXTEX did not convert properly\n"); + + result = SendMessage(hwndRichEdit, EM_SETTEXTEX, + (WPARAM)&setText, (LPARAM) NULL); + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); + + ok (result == 1, + "EM_SETTEXTEX returned %d, instead of 1\n",result); + ok(lstrlenW(buf) == 0, + "EM_SETTEXTEX with NULL lParam should clear rich edit.\n"); + + /* put some text back */ + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); + /* select some text */ + cr.cpMax = 1; + cr.cpMin = 3; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + /* replace current selection */ + setText.flags = ST_SELECTION; + result = SendMessage(hwndRichEdit, EM_SETTEXTEX, + (WPARAM)&setText, (LPARAM) NULL); + ok(result == 0, + "EM_SETTEXTEX with NULL lParam to replace selection" + " with no text should return 0. Got %i\n", + result); + + /* put some text back */ + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM) TestItem1); + /* select some text */ + cr.cpMax = 1; + cr.cpMin = 3; + SendMessage(hwndRichEdit, EM_EXSETSEL, 0, (LPARAM) &cr); + /* replace current selection */ + setText.flags = ST_SELECTION; + result = SendMessage(hwndRichEdit, EM_SETTEXTEX, + (WPARAM)&setText, (LPARAM) TestItem1); + /* get text */ + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buf); + ok(result == lstrlenW(TestItem1), + "EM_SETTEXTEX with NULL lParam to replace selection" + " with no text should return 0. Got %i\n", + result); + ok(lstrlenW(buf) == 22, + "EM_SETTEXTEX to replace selection with more text failed: %i.\n", + lstrlenW(buf) ); + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_LIMITTEXT(void) +{ + int ret; + + HWND hwndRichEdit = new_richedit(NULL); + + /* The main purpose of this test is to demonstrate that the nonsense in MSDN + * about setting the length to -1 for multiline edit controls doesn't happen. + */ + + /* Don't check default gettextlimit case. That's done in other tests */ + + /* Set textlimit to 100 */ + SendMessage (hwndRichEdit, EM_LIMITTEXT, 100, 0); + ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok (ret == 100, + "EM_LIMITTEXT: set to 100, returned: %d, expected: 100\n", ret); + + /* Set textlimit to 0 */ + SendMessage (hwndRichEdit, EM_LIMITTEXT, 0, 0); + ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok (ret == 65536, + "EM_LIMITTEXT: set to 0, returned: %d, expected: 65536\n", ret); + + /* Set textlimit to -1 */ + SendMessage (hwndRichEdit, EM_LIMITTEXT, -1, 0); + ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok (ret == -1, + "EM_LIMITTEXT: set to -1, returned: %d, expected: -1\n", ret); + + /* Set textlimit to -2 */ + SendMessage (hwndRichEdit, EM_LIMITTEXT, -2, 0); + ret = SendMessage (hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok (ret == -2, + "EM_LIMITTEXT: set to -2, returned: %d, expected: -2\n", ret); + + DestroyWindow (hwndRichEdit); +} + + +static void test_EM_EXLIMITTEXT(void) +{ + int i, selBegin, selEnd, len1, len2; + int result; + char text[1024 + 1]; + char buffer[1024 + 1]; + int textlimit = 0; /* multiple of 100 */ + HWND hwndRichEdit = new_richedit(NULL); + + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(32767 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 32767, i); /* default */ + + textlimit = 256000; + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + /* set higher */ + ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i); + + textlimit = 1000; + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + /* set lower */ + ok(textlimit == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", textlimit, i); + + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 0); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + /* default for WParam = 0 */ + ok(65536 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 65536, i); + + textlimit = sizeof(text)-1; + memset(text, 'W', textlimit); + text[sizeof(text)-1] = 0; + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit); + /* maxed out text */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + + SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */ + SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd); + len1 = selEnd - selBegin; + + SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 1); + SendMessage(hwndRichEdit, WM_CHAR, VK_BACK, 1); + SendMessage(hwndRichEdit, WM_KEYUP, VK_BACK, 1); + SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); + SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd); + len2 = selEnd - selBegin; + + ok(len1 != len2, + "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n", + len1,len2,i); + + SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1); + SendMessage(hwndRichEdit, WM_CHAR, 'A', 1); + SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); + SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); + SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd); + len1 = selEnd - selBegin; + + ok(len1 != len2, + "EM_EXLIMITTEXT: Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n", + len1,len2,i); + + SendMessage(hwndRichEdit, WM_KEYDOWN, 'A', 1); + SendMessage(hwndRichEdit, WM_CHAR, 'A', 1); + SendMessage(hwndRichEdit, WM_KEYUP, 'A', 1); /* full; should be no effect */ + SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); + SendMessage(hwndRichEdit, EM_GETSEL, (WPARAM)&selBegin, (LPARAM)&selEnd); + len2 = selEnd - selBegin; + + ok(len1 == len2, + "EM_EXLIMITTEXT: No Change Expected\nOld Length: %d, New Length: %d, Limit: %d\n", + len1,len2,i); + + /* set text up to the limit, select all the text, then add a char */ + textlimit = 5; + memset(text, 'W', textlimit); + text[textlimit] = 0; + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); + SendMessage(hwndRichEdit, WM_CHAR, 'A', 1); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp(buffer, "A"); + ok(0 == result, "got string = \"%s\"\n", buffer); + + /* WM_SETTEXT not limited */ + textlimit = 10; + memset(text, 'W', textlimit); + text[textlimit] = 0; + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit-5); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + i = strlen(buffer); + ok(10 == i, "expected 10 chars\n"); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i); + + /* try inserting more text at end */ + i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0); + ok(0 == i, "WM_CHAR wasn't processed\n"); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + i = strlen(buffer); + ok(10 == i, "expected 10 chars, got %i\n", i); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i); + + /* try inserting text at beginning */ + SendMessage(hwndRichEdit, EM_SETSEL, 0, 0); + i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0); + ok(0 == i, "WM_CHAR wasn't processed\n"); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + i = strlen(buffer); + ok(10 == i, "expected 10 chars, got %i\n", i); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(10 == i, "EM_EXLIMITTEXT: expected: %d, actual: %d\n", 10, i); + + /* WM_CHAR is limited */ + textlimit = 1; + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, textlimit); + SendMessage(hwndRichEdit, EM_SETSEL, 0, -1); /* select everything */ + i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0); + ok(0 == i, "WM_CHAR wasn't processed\n"); + i = SendMessage(hwndRichEdit, WM_CHAR, 'A', 0); + ok(0 == i, "WM_CHAR wasn't processed\n"); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + i = strlen(buffer); + ok(1 == i, "expected 1 chars, got %i instead\n", i); + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_GETLIMITTEXT(void) +{ + int i; + HWND hwndRichEdit = new_richedit(NULL); + + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(32767 == i, "expected: %d, actual: %d\n", 32767, i); /* default value */ + + SendMessage(hwndRichEdit, EM_EXLIMITTEXT, 0, 50000); + i = SendMessage(hwndRichEdit, EM_GETLIMITTEXT, 0, 0); + ok(50000 == i, "expected: %d, actual: %d\n", 50000, i); + + DestroyWindow(hwndRichEdit); +} + +static void test_WM_SETFONT(void) +{ + /* There is no invalid input or error conditions for this function. + * NULL wParam and lParam just fall back to their default values + * It should be noted that even if you use a gibberish name for your fonts + * here, it will still work because the name is stored. They will display as + * System, but will report their name to be whatever they were created as */ + + HWND hwndRichEdit = new_richedit(NULL); + HFONT testFont1 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | + FF_DONTCARE, "Marlett"); + HFONT testFont2 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, + OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | + FF_DONTCARE, "MS Sans Serif"); + HFONT testFont3 = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | + FF_DONTCARE, "Courier"); + LOGFONTA sentLogFont; + CHARFORMAT2A returnedCF2A; + + returnedCF2A.cbSize = sizeof(returnedCF2A); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "x"); + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont1,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A); + + GetObjectA(testFont1, sizeof(LOGFONTA), &sentLogFont); + ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName), + "EM_GETCHARFOMAT: Returned wrong font on test 1. Sent: %s, Returned: %s\n", + sentLogFont.lfFaceName,returnedCF2A.szFaceName); + + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont2,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A); + GetObjectA(testFont2, sizeof(LOGFONTA), &sentLogFont); + ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName), + "EM_GETCHARFOMAT: Returned wrong font on test 2. Sent: %s, Returned: %s\n", + sentLogFont.lfFaceName,returnedCF2A.szFaceName); + + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont3,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A); + GetObjectA(testFont3, sizeof(LOGFONTA), &sentLogFont); + ok (!strcmp(sentLogFont.lfFaceName,returnedCF2A.szFaceName), + "EM_GETCHARFOMAT: Returned wrong font on test 3. Sent: %s, Returned: %s\n", + sentLogFont.lfFaceName,returnedCF2A.szFaceName); + + /* This last test is special since we send in NULL. We clear the variables + * and just compare to "System" instead of the sent in font name. */ + ZeroMemory(&returnedCF2A,sizeof(returnedCF2A)); + ZeroMemory(&sentLogFont,sizeof(sentLogFont)); + returnedCF2A.cbSize = sizeof(returnedCF2A); + + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)NULL,(LPARAM) MAKELONG((WORD) TRUE, 0)); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, SCF_DEFAULT, (LPARAM) &returnedCF2A); + GetObjectA(NULL, sizeof(LOGFONTA), &sentLogFont); + ok (!strcmp("System",returnedCF2A.szFaceName), + "EM_GETCHARFOMAT: Returned wrong font on test 4. Sent: NULL, Returned: %s. Expected \"System\".\n",returnedCF2A.szFaceName); + + DestroyWindow(hwndRichEdit); +} + + +static DWORD CALLBACK test_EM_GETMODIFY_esCallback(DWORD_PTR dwCookie, + LPBYTE pbBuff, + LONG cb, + LONG *pcb) +{ + const char** str = (const char**)dwCookie; + int size = strlen(*str); + if(size > 3) /* let's make it peice-meal for fun */ + size = 3; + *pcb = cb; + if (*pcb > size) { + *pcb = size; + } + if (*pcb > 0) { + memcpy(pbBuff, *str, *pcb); + *str += *pcb; + } + return 0; +} + +static void test_EM_GETMODIFY(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + LRESULT result; + SETTEXTEX setText; + WCHAR TestItem1[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'T', 'e', 'x', 't', 0}; + WCHAR TestItem2[] = {'T', 'e', 's', 't', + 'S', 'o', 'm', 'e', + 'O', 't', 'h', 'e', 'r', + 'T', 'e', 'x', 't', 0}; + const char* streamText = "hello world"; + CHARFORMAT2 cf2; + PARAFORMAT2 pf2; + EDITSTREAM es; + + HFONT testFont = CreateFontA (0,0,0,0,FW_LIGHT, 0, 0, 0, ANSI_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | + FF_DONTCARE, "Courier"); + + setText.codepage = 1200; /* no constant for unicode */ + setText.flags = ST_KEEPUNDO; + + + /* modify flag shouldn't be set when richedit is first created */ + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result == 0, + "EM_GETMODIFY returned non-zero, instead of zero on create\n"); + + /* setting modify flag should actually set it */ + SendMessage(hwndRichEdit, EM_SETMODIFY, TRUE, 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero on EM_SETMODIFY\n"); + + /* clearing modify flag should actually clear it */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result == 0, + "EM_GETMODIFY returned non-zero, instead of zero on EM_SETMODIFY\n"); + + /* setting font doesn't change modify flag */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, WM_SETFONT, (WPARAM)testFont,(LPARAM) MAKELONG((WORD) TRUE, 0)); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result == 0, + "EM_GETMODIFY returned non-zero, instead of zero on setting font\n"); + + /* setting text should set modify flag */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero on setting text\n"); + + /* undo previous text doesn't reset modify flag */ + SendMessage(hwndRichEdit, WM_UNDO, 0, 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero on undo after setting text\n"); + + /* set text with no flag to keep undo stack should not set modify flag */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + setText.flags = 0; + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result == 0, + "EM_GETMODIFY returned non-zero, instead of zero when setting text while not keeping undo stack\n"); + + /* WM_SETTEXT doesn't modify */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)TestItem2); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result == 0, + "EM_GETMODIFY returned non-zero for WM_SETTEXT\n"); + + /* clear the text */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, WM_CLEAR, 0, 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result == 0, + "EM_GETMODIFY returned non-zero, instead of zero for WM_CLEAR\n"); + + /* replace text */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, EM_SETTEXTEX, (WPARAM)&setText, (LPARAM)TestItem1); + SendMessage(hwndRichEdit, EM_SETSEL, 0, 2); + SendMessage(hwndRichEdit, EM_REPLACESEL, TRUE, (LPARAM)TestItem2); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero when replacing text\n"); + + /* copy/paste text 1 */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, EM_SETSEL, 0, 2); + SendMessage(hwndRichEdit, WM_COPY, 0, 0); + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero when pasting identical text\n"); + + /* copy/paste text 2 */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, EM_SETSEL, 0, 2); + SendMessage(hwndRichEdit, WM_COPY, 0, 0); + SendMessage(hwndRichEdit, EM_SETSEL, 0, 3); + SendMessage(hwndRichEdit, WM_PASTE, 0, 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero when pasting different text\n"); + + /* press char */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, EM_SETSEL, 0, 1); + SendMessage(hwndRichEdit, WM_CHAR, 'A', 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero for WM_CHAR\n"); + + /* press del */ + SendMessage(hwndRichEdit, WM_CHAR, 'A', 0); + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + SendMessage(hwndRichEdit, WM_KEYDOWN, VK_BACK, 0); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero for backspace\n"); + + /* set char format */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + cf2.cbSize = sizeof(CHARFORMAT2); + SendMessage(hwndRichEdit, EM_GETCHARFORMAT, (WPARAM) SCF_DEFAULT, + (LPARAM) &cf2); + cf2.dwMask = CFM_ITALIC | cf2.dwMask; + cf2.dwEffects = CFE_ITALIC ^ cf2.dwEffects; + SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2); + result = SendMessage(hwndRichEdit, EM_SETCHARFORMAT, (WPARAM) SCF_ALL, (LPARAM) &cf2); + ok(result == 1, "EM_SETCHARFORMAT returned %ld instead of 1\n", result); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero for EM_SETCHARFORMAT\n"); + + /* set para format */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + pf2.cbSize = sizeof(PARAFORMAT2); + SendMessage(hwndRichEdit, EM_GETPARAFORMAT, 0, + (LPARAM) &pf2); + pf2.dwMask = PFM_ALIGNMENT | pf2.dwMask; + pf2.wAlignment = PFA_RIGHT; + SendMessage(hwndRichEdit, EM_SETPARAFORMAT, 0, (LPARAM) &pf2); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result == 0, + "EM_GETMODIFY returned zero, instead of non-zero for EM_SETPARAFORMAT\n"); + + /* EM_STREAM */ + SendMessage(hwndRichEdit, EM_SETMODIFY, FALSE, 0); + es.dwCookie = (DWORD_PTR)&streamText; + es.dwError = 0; + es.pfnCallback = test_EM_GETMODIFY_esCallback; + SendMessage(hwndRichEdit, EM_STREAMIN, + (WPARAM)(SF_TEXT), (LPARAM)&es); + result = SendMessage(hwndRichEdit, EM_GETMODIFY, 0, 0); + ok (result != 0, + "EM_GETMODIFY returned zero, instead of non-zero for EM_STREAM\n"); + + DestroyWindow(hwndRichEdit); +} + +struct exsetsel_s { + long min; + long max; + long expected_retval; + int expected_getsel_start; + int expected_getsel_end; + int _exsetsel_todo_wine; + int _getsel_todo_wine; +}; + +const struct exsetsel_s exsetsel_tests[] = { + /* sanity tests */ + {5, 10, 10, 5, 10, 0, 0}, + {15, 17, 17, 15, 17, 0, 0}, + /* test cpMax > strlen() */ + {0, 100, 18, 0, 18, 0, 1}, + /* test cpMin == cpMax */ + {5, 5, 5, 5, 5, 0, 0}, + /* test cpMin < 0 && cpMax >= 0 (bug 4462) */ + {-1, 0, 5, 5, 5, 0, 0}, + {-1, 17, 5, 5, 5, 0, 0}, + {-1, 18, 5, 5, 5, 0, 0}, + /* test cpMin < 0 && cpMax < 0 */ + {-1, -1, 17, 17, 17, 0, 0}, + {-4, -5, 17, 17, 17, 0, 0}, + /* test cMin >=0 && cpMax < 0 (bug 6814) */ + {0, -1, 18, 0, 18, 0, 1}, + {17, -5, 18, 17, 18, 0, 1}, + {18, -3, 17, 17, 17, 0, 0}, + /* test if cpMin > cpMax */ + {15, 19, 18, 15, 18, 0, 1}, + {19, 15, 18, 15, 18, 0, 1} +}; + +static void check_EM_EXSETSEL(HWND hwnd, const struct exsetsel_s *setsel, int id) { + CHARRANGE cr; + long result; + int start, end; + + cr.cpMin = setsel->min; + cr.cpMax = setsel->max; + result = SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM) &cr); + + if (setsel->_exsetsel_todo_wine) { + todo_wine { + ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result); + } + } else { + ok(result == setsel->expected_retval, "EM_EXSETSEL(%d): expected: %ld actual: %ld\n", id, setsel->expected_retval, result); + } + + SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end); + + if (setsel->_getsel_todo_wine) { + todo_wine { + ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end); + } + } else { + ok(start == setsel->expected_getsel_start && end == setsel->expected_getsel_end, "EM_EXSETSEL(%d): expected (%d,%d) actual:(%d,%d)\n", id, setsel->expected_getsel_start, setsel->expected_getsel_end, start, end); + } +} + +static void test_EM_EXSETSEL(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + int i; + const int num_tests = sizeof(exsetsel_tests)/sizeof(struct exsetsel_s); + + /* sending some text to the window */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection"); + /* 01234567890123456*/ + /* 10 */ + + for (i = 0; i < num_tests; i++) { + check_EM_EXSETSEL(hwndRichEdit, &exsetsel_tests[i], i); + } + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_REPLACESEL(void) +{ + HWND hwndRichEdit = new_richedit(NULL); + char buffer[1024] = {0}; + int r; + GETTEXTEX getText; + CHARRANGE cr; + + /* sending some text to the window */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) "testing selection"); + /* 01234567890123456*/ + /* 10 */ + + /* FIXME add more tests */ + SendMessage(hwndRichEdit, EM_SETSEL, 7, 17); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) NULL); + ok(0 == r, "EM_REPLACESEL returned %d, expected 0\n", r); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + r = strcmp(buffer, "testing"); + ok(0 == r, "expected %d, got %d\n", 0, r); + + DestroyWindow(hwndRichEdit); + + hwndRichEdit = new_richedit(NULL); + + /* Test behavior with carriage returns and newlines */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1"); + ok(9 == r, "EM_REPLACESEL returned %d, expected 9\n", r); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + r = strcmp(buffer, "RichEdit1"); + ok(0 == r, "expected %d, got %d\n", 0, r); + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "RichEdit1") == 0, + "EM_GETTEXTEX results not what was set by EM_REPLACESEL\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r"); + ok(10 == r, "EM_REPLACESEL returned %d, expected 10\n", r); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + r = strcmp(buffer, "RichEdit1\r\n"); + ok(0 == r, "expected %d, got %d\n", 0, r); + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "RichEdit1\r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + /* Win98's riched20 and WinXP's riched20 disagree on what to return from + EM_REPLACESEL. The general rule seems to be that Win98's riched20 + returns the number of characters *inserted* into the control (after + required conversions), but WinXP's riched20 returns the number of + characters interpreted from the original lParam. Wine's builtin riched20 + implements the WinXP behavior. + */ + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "RichEdit1\r\n"); + ok(11 == r /* WinXP */ || 10 == r /* Win98 */, + "EM_REPLACESEL returned %d, expected 11 or 10\n", r); + + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin); + ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax); + + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + r = strcmp(buffer, "RichEdit1\r\n"); + ok(0 == r, "expected %d, got %d\n", 0, r); + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "RichEdit1\r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 10, "EM_EXGETSEL returned cpMin=%d, expected 10\n", cr.cpMin); + ok(cr.cpMax == 10, "EM_EXGETSEL returned cpMax=%d, expected 10\n", cr.cpMax); + + /* The following tests show that richedit should handle the special \r\r\n + sequence by turning it into a single space on insertion. However, + EM_REPLACESEL on WinXP returns the number of characters in the original + string. + */ + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r"); + ok(2 == r, "EM_REPLACESEL returned %d, expected 4\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin); + ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "\r\r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n"); + ok(3 == r /* WinXP */ || 1 == r /* Win98 */, + "EM_REPLACESEL returned %d, expected 3 or 1\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 1, "EM_EXGETSEL returned cpMin=%d, expected 1\n", cr.cpMin); + ok(cr.cpMax == 1, "EM_EXGETSEL returned cpMax=%d, expected 1\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, " ") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\r\r\r\n\r\r\r"); + ok(9 == r /* WinXP */ || 7 == r /* Win98 */, + "EM_REPLACESEL returned %d, expected 9 or 7\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin); + ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "\r\r\r \r\r\r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\n"); + ok(5 == r /* WinXP */ || 2 == r /* Win98 */, + "EM_REPLACESEL returned %d, expected 5 or 2\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin); + ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, " \r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\r\r\n\r\r"); + ok(5 == r /* WinXP */ || 3 == r /* Win98 */, + "EM_REPLACESEL returned %d, expected 5 or 3\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 3, "EM_EXGETSEL returned cpMin=%d, expected 3\n", cr.cpMin); + ok(cr.cpMax == 3, "EM_EXGETSEL returned cpMax=%d, expected 3\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, " \r\r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\rX\r\n\r\r"); + ok(6 == r /* WinXP */ || 5 == r /* Win98 */, + "EM_REPLACESEL returned %d, expected 6 or 5\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 5, "EM_EXGETSEL returned cpMin=%d, expected 5\n", cr.cpMin); + ok(cr.cpMax == 5, "EM_EXGETSEL returned cpMax=%d, expected 5\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "\rX\r\r\r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n"); + ok(2 == r, "EM_REPLACESEL returned %d, expected 2\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 2, "EM_EXGETSEL returned cpMin=%d, expected 2\n", cr.cpMin); + ok(cr.cpMax == 2, "EM_EXGETSEL returned cpMax=%d, expected 2\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "\r\r") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM)NULL); + r = SendMessage(hwndRichEdit, EM_REPLACESEL, 0, (LPARAM) "\n\n\n\n\r\r\r\r\n"); + ok(9 == r /* WinXP */ || 7 == r /* Win98 */, + "EM_REPLACESEL returned %d, expected 9 or 7\n", r); + r = SendMessage(hwndRichEdit, EM_EXGETSEL, 0, (LPARAM)&cr); + ok(0 == r, "EM_EXGETSEL returned %d, expected 0\n", r); + ok(cr.cpMin == 7, "EM_EXGETSEL returned cpMin=%d, expected 7\n", cr.cpMin); + ok(cr.cpMax == 7, "EM_EXGETSEL returned cpMax=%d, expected 7\n", cr.cpMax); + + /* Test the actual string */ + getText.cb = 1024; + getText.codepage = CP_ACP; + getText.flags = GT_DEFAULT; + getText.lpDefaultChar = NULL; + getText.lpUsedDefaultChar = NULL; + SendMessage(hwndRichEdit, EM_GETTEXTEX, (WPARAM)&getText, (LPARAM) buffer); + ok(strcmp(buffer, "\r\r\r\r\r\r ") == 0, + "EM_GETTEXTEX returned incorrect string\n"); + + DestroyWindow(hwndRichEdit); +} + +static void test_WM_PASTE(void) +{ + MSG msg; + int result; + char buffer[1024] = {0}; + char key_info[][3] = + { + /* VirtualKey, ScanCode, WM_CHAR code */ + {'C', 0x2e, 3}, /* Ctrl-C */ + {'X', 0x2d, 24}, /* Ctrl-X */ + {'V', 0x2f, 22}, /* Ctrl-V */ + {'Z', 0x2c, 26}, /* Ctrl-Z */ + {'Y', 0x15, 25}, /* Ctrl-Y */ + }; + const char* text1 = "testing paste\r"; + const char* text1_step1 = "testing paste\r\ntesting paste\r\n"; + const char* text1_after = "testing paste\r\n"; + const char* text2 = "testing paste\r\rtesting paste"; + const char* text2_after = "testing paste\r\n\r\ntesting paste"; + const char* text3 = "testing paste\r\npaste\r\ntesting paste"; + HWND hwndRichEdit = new_richedit(NULL); + + /* Native riched20 won't obey WM_CHAR messages or WM_KEYDOWN/WM_KEYUP + messages, probably because it inspects the keyboard state itself. + Therefore, native requires this in order to obey Ctrl- keystrokes. + */ +#define SEND_CTRL_KEY(hwnd, k) \ + keybd_event(VK_CONTROL, 0x1d, 0, 0);\ + keybd_event(k[0], k[1], 0, 0);\ + keybd_event(k[0], k[1], KEYEVENTF_KEYUP, 0);\ + keybd_event(VK_CONTROL, 0x1d, KEYEVENTF_KEYUP, 0); \ + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { \ + TranslateMessage(&msg); \ + DispatchMessage(&msg); \ + } + +#define SEND_CTRL_C(hwnd) SEND_CTRL_KEY(hwnd, key_info[0]) +#define SEND_CTRL_X(hwnd) SEND_CTRL_KEY(hwnd, key_info[1]) +#define SEND_CTRL_V(hwnd) SEND_CTRL_KEY(hwnd, key_info[2]) +#define SEND_CTRL_Z(hwnd) SEND_CTRL_KEY(hwnd, key_info[3]) +#define SEND_CTRL_Y(hwnd) SEND_CTRL_KEY(hwnd, key_info[4]) + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text1); + SendMessage(hwndRichEdit, EM_SETSEL, 0, 14); + + SEND_CTRL_C(hwndRichEdit) /* Copy */ + SendMessage(hwndRichEdit, EM_SETSEL, 14, 14); + SEND_CTRL_V(hwndRichEdit) /* Paste */ + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + /* Pasted text should be visible at this step */ + result = strcmp(text1_step1, buffer); + ok(result == 0, + "test paste: strcmp = %i\n", result); + SEND_CTRL_Z(hwndRichEdit) /* Undo */ + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + /* Text should be the same as before (except for \r -> \r\n conversion) */ + result = strcmp(text1_after, buffer); + ok(result == 0, + "test paste: strcmp = %i\n", result); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) text2); + SendMessage(hwndRichEdit, EM_SETSEL, 8, 13); + SEND_CTRL_C(hwndRichEdit) /* Copy */ + SendMessage(hwndRichEdit, EM_SETSEL, 14, 14); + SEND_CTRL_V(hwndRichEdit) /* Paste */ + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + /* Pasted text should be visible at this step */ + result = strcmp(text3, buffer); + ok(result == 0, + "test paste: strcmp = %i\n", result); + SEND_CTRL_Z(hwndRichEdit) /* Undo */ + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + /* Text should be the same as before (except for \r -> \r\n conversion) */ + result = strcmp(text2_after, buffer); + ok(result == 0, + "test paste: strcmp = %i\n", result); + SEND_CTRL_Y(hwndRichEdit) /* Redo */ + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + /* Text should revert to post-paste state */ + result = strcmp(buffer,text3); + ok(result == 0, + "test paste: strcmp = %i\n", result); + + DestroyWindow(hwndRichEdit); +} + +static void test_EM_FORMATRANGE(void) +{ + int r; + FORMATRANGE fr; + HDC hdc; + HWND hwndRichEdit = new_richedit(NULL); + + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) haystack); + + hdc = GetDC(hwndRichEdit); + ok(hdc != NULL, "Could not get HDC\n"); + + fr.hdc = fr.hdcTarget = hdc; + fr.rc.top = fr.rcPage.top = fr.rc.left = fr.rcPage.left = 0; + fr.rc.right = fr.rcPage.right = GetDeviceCaps(hdc, HORZRES); + fr.rc.bottom = fr.rcPage.bottom = GetDeviceCaps(hdc, VERTRES); + fr.chrg.cpMin = 0; + fr.chrg.cpMax = 20; + + r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL); + todo_wine { + ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r); + } + + r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr); + todo_wine { + ok(r == 20, "EM_FORMATRANGE expect %d, got %d\n", 20, r); + } + + fr.chrg.cpMin = 0; + fr.chrg.cpMax = 10; + + r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) &fr); + todo_wine { + ok(r == 10, "EM_FORMATRANGE expect %d, got %d\n", 10, r); + } + + r = SendMessage(hwndRichEdit, EM_FORMATRANGE, TRUE, (LPARAM) NULL); + todo_wine { + ok(r == 31, "EM_FORMATRANGE expect %d, got %d\n", 31, r); + } + + DestroyWindow(hwndRichEdit); +} + +static int nCallbackCount = 0; + +static DWORD CALLBACK EditStreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, + LONG cb, LONG* pcb) +{ + const char text[] = {'t','e','s','t'}; + + if (sizeof(text) <= cb) + { + if ((int)dwCookie != nCallbackCount) + { + *pcb = 0; + return 0; + } + + memcpy (pbBuff, text, sizeof(text)); + *pcb = sizeof(text); + + nCallbackCount++; + + return 0; + } + else + return 1; /* indicates callback failed */ +} + +static void test_EM_StreamIn_Undo(void) +{ + /* The purpose of this test is to determine when a EM_StreamIn should be + * undoable. This is important because WM_PASTE currently uses StreamIn and + * pasting should always be undoable but streaming isn't always. + * + * cases to test: + * StreamIn plain text without SFF_SELECTION. + * StreamIn plain text with SFF_SELECTION set but a zero-length selection + * StreamIn plain text with SFF_SELECTION and a valid, normal selection + * StreamIn plain text with SFF_SELECTION and a backwards-selection (from>to) + * Feel free to add tests for other text modes or StreamIn things. + */ + + + HWND hwndRichEdit = new_richedit(NULL); + LRESULT result; + EDITSTREAM es; + char buffer[1024] = {0}; + const char randomtext[] = "Some text"; + + es.pfnCallback = (EDITSTREAMCALLBACK) EditStreamCallback; + + /* StreamIn, no SFF_SELECTION */ + es.dwCookie = nCallbackCount; + SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext); + SendMessage(hwndRichEdit, EM_SETSEL,0,0); + SendMessage(hwndRichEdit, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&es); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp (buffer,"test"); + ok (result == 0, + "EM_STREAMIN: Test 1 set wrong text: Result: %s\n",buffer); + + result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0); + ok (result == FALSE, + "EM_STREAMIN without SFF_SELECTION wrongly allows undo\n"); + + /* StreamIn, SFF_SELECTION, but nothing selected */ + es.dwCookie = nCallbackCount; + SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext); + SendMessage(hwndRichEdit, EM_SETSEL,0,0); + SendMessage(hwndRichEdit, EM_STREAMIN, + (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp (buffer,"testSome text"); + ok (result == 0, + "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer); + + result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0); + ok (result == TRUE, + "EM_STREAMIN with SFF_SELECTION but no selection set " + "should create an undo\n"); + + /* StreamIn, SFF_SELECTION, with a selection */ + es.dwCookie = nCallbackCount; + SendMessage(hwndRichEdit,EM_EMPTYUNDOBUFFER, 0,0); + SendMessage(hwndRichEdit, WM_SETTEXT, 0, (LPARAM) randomtext); + SendMessage(hwndRichEdit, EM_SETSEL,4,5); + SendMessage(hwndRichEdit, EM_STREAMIN, + (WPARAM)(SF_TEXT|SFF_SELECTION), (LPARAM)&es); + SendMessage(hwndRichEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + result = strcmp (buffer,"Sometesttext"); + ok (result == 0, + "EM_STREAMIN: Test 2 set wrong text: Result: %s\n",buffer); + + result = SendMessage(hwndRichEdit, EM_CANUNDO, 0, 0); + ok (result == TRUE, + "EM_STREAMIN with SFF_SELECTION and selection set " + "should create an undo\n"); + +} + +static BOOL is_em_settextex_supported(HWND hwnd) +{ + SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; + return SendMessageA(hwnd, EM_SETTEXTEX, (WPARAM)&stex, 0) != 0; +} + +static void test_unicode_conversions(void) +{ + static const WCHAR tW[] = {'t',0}; + static const WCHAR teW[] = {'t','e',0}; + static const WCHAR textW[] = {'t','e','s','t',0}; + static const char textA[] = "test"; + char bufA[64]; + WCHAR bufW[64]; + HWND hwnd; + int is_win9x, em_settextex_supported, ret; + + is_win9x = GetVersion() & 0x80000000; + +#define set_textA(hwnd, wm_set_text, txt) \ + do { \ + SETTEXTEX stex = { ST_DEFAULT, CP_ACP }; \ + WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \ + assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \ + ret = SendMessageA(hwnd, wm_set_text, wparam, (LPARAM)txt); \ + ok(ret, "SendMessageA(%02x) error %u\n", wm_set_text, GetLastError()); \ + } while(0) +#define expect_textA(hwnd, wm_get_text, txt) \ + do { \ + GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \ + WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \ + assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \ + memset(bufA, 0xAA, sizeof(bufA)); \ + ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \ + ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \ + ret = lstrcmpA(bufA, txt); \ + ok(!ret, "%02x: strings do not match: expected %s got %s\n", wm_get_text, txt, bufA); \ + } while(0) + +#define set_textW(hwnd, wm_set_text, txt) \ + do { \ + SETTEXTEX stex = { ST_DEFAULT, 1200 }; \ + WPARAM wparam = (wm_set_text == WM_SETTEXT) ? 0 : (WPARAM)&stex; \ + assert(wm_set_text == WM_SETTEXT || wm_set_text == EM_SETTEXTEX); \ + ret = SendMessageW(hwnd, wm_set_text, wparam, (LPARAM)txt); \ + ok(ret, "SendMessageW(%02x) error %u\n", wm_set_text, GetLastError()); \ + } while(0) +#define expect_textW(hwnd, wm_get_text, txt) \ + do { \ + GETTEXTEX gtex = { 64, GT_DEFAULT, 1200, NULL, NULL }; \ + WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \ + assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \ + memset(bufW, 0xAA, sizeof(bufW)); \ + if (is_win9x) \ + { \ + assert(wm_get_text == EM_GETTEXTEX); \ + ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufW); \ + ok(ret, "SendMessageA(%02x) error %u\n", wm_get_text, GetLastError()); \ + } \ + else \ + { \ + ret = SendMessageW(hwnd, wm_get_text, wparam, (LPARAM)bufW); \ + ok(ret, "SendMessageW(%02x) error %u\n", wm_get_text, GetLastError()); \ + } \ + ret = lstrcmpW(bufW, txt); \ + ok(!ret, "%02x: strings do not match: expected[0] %x got[0] %x\n", wm_get_text, txt[0], bufW[0]); \ + } while(0) +#define expect_empty(hwnd, wm_get_text) \ + do { \ + GETTEXTEX gtex = { 64, GT_DEFAULT, CP_ACP, NULL, NULL }; \ + WPARAM wparam = (wm_get_text == WM_GETTEXT) ? 64 : (WPARAM)>ex; \ + assert(wm_get_text == WM_GETTEXT || wm_get_text == EM_GETTEXTEX); \ + memset(bufA, 0xAA, sizeof(bufA)); \ + ret = SendMessageA(hwnd, wm_get_text, wparam, (LPARAM)bufA); \ + ok(!ret, "empty richedit should return 0, got %d\n", ret); \ + ok(!*bufA, "empty richedit should return empty string, got %s\n", bufA); \ + } while(0) + + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP, + 0, 0, 200, 60, 0, 0, 0, 0); + ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError()); + + ret = IsWindowUnicode(hwnd); + if (is_win9x) + ok(!ret, "RichEdit20W should NOT be unicode under Win9x\n"); + else + ok(ret, "RichEdit20W should be unicode under NT\n"); + + /* EM_SETTEXTEX is supported starting from version 3.0 */ + em_settextex_supported = is_em_settextex_supported(hwnd); + trace("EM_SETTEXTEX is %ssupported on this platform\n", + em_settextex_supported ? "" : "NOT "); + + expect_empty(hwnd, WM_GETTEXT); + expect_empty(hwnd, EM_GETTEXTEX); + + ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textW[0], 0); + ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret); + expect_textA(hwnd, WM_GETTEXT, "t"); + expect_textA(hwnd, EM_GETTEXTEX, "t"); + expect_textW(hwnd, EM_GETTEXTEX, tW); + + ret = SendMessageA(hwnd, WM_CHAR, (WPARAM)textA[1], 0); + ok(!ret, "SendMessageA(WM_CHAR) should return 0, got %d\n", ret); + expect_textA(hwnd, WM_GETTEXT, "te"); + expect_textA(hwnd, EM_GETTEXTEX, "te"); + expect_textW(hwnd, EM_GETTEXTEX, teW); + + set_textA(hwnd, WM_SETTEXT, NULL); + expect_empty(hwnd, WM_GETTEXT); + expect_empty(hwnd, EM_GETTEXTEX); + + if (is_win9x) + set_textA(hwnd, WM_SETTEXT, textW); + else + set_textA(hwnd, WM_SETTEXT, textA); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textA(hwnd, EM_GETTEXTEX, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + + if (em_settextex_supported) + { + set_textA(hwnd, EM_SETTEXTEX, textA); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textA(hwnd, EM_GETTEXTEX, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + } + + if (!is_win9x) + { + set_textW(hwnd, WM_SETTEXT, textW); + expect_textW(hwnd, WM_GETTEXT, textW); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + expect_textA(hwnd, EM_GETTEXTEX, textA); + + if (em_settextex_supported) + { + set_textW(hwnd, EM_SETTEXTEX, textW); + expect_textW(hwnd, WM_GETTEXT, textW); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + expect_textA(hwnd, EM_GETTEXTEX, textA); + } + } + DestroyWindow(hwnd); + + hwnd = CreateWindowExA(0, "RichEdit20A", NULL, WS_POPUP, + 0, 0, 200, 60, 0, 0, 0, 0); + ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError()); + + ret = IsWindowUnicode(hwnd); + ok(!ret, "RichEdit20A should NOT be unicode\n"); + + set_textA(hwnd, WM_SETTEXT, textA); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textA(hwnd, EM_GETTEXTEX, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + + if (em_settextex_supported) + { + set_textA(hwnd, EM_SETTEXTEX, textA); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textA(hwnd, EM_GETTEXTEX, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + } + + if (!is_win9x) + { + set_textW(hwnd, WM_SETTEXT, textW); + expect_textW(hwnd, WM_GETTEXT, textW); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + expect_textA(hwnd, EM_GETTEXTEX, textA); + + if (em_settextex_supported) + { + set_textW(hwnd, EM_SETTEXTEX, textW); + expect_textW(hwnd, WM_GETTEXT, textW); + expect_textA(hwnd, WM_GETTEXT, textA); + expect_textW(hwnd, EM_GETTEXTEX, textW); + expect_textA(hwnd, EM_GETTEXTEX, textA); + } + } + DestroyWindow(hwnd); +} + +static void test_WM_CHAR(void) +{ + HWND hwnd; + int ret; + const char * char_list = "abc\rabc\r"; + const char * expected_content_single = "abcabc"; + const char * expected_content_multi = "abc\r\nabc\r\n"; + char buffer[64] = {0}; + const char * p; + + /* single-line control must IGNORE carriage returns */ + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP, + 0, 0, 200, 60, 0, 0, 0, 0); + ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError()); + + p = char_list; + while (*p != '\0') { + SendMessageA(hwnd, WM_KEYDOWN, *p, 1); + ret = SendMessageA(hwnd, WM_CHAR, *p, 1); + ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret); + SendMessageA(hwnd, WM_KEYUP, *p, 1); + p++; + } + + SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer); + ret = strcmp(buffer, expected_content_single); + ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n"); + + DestroyWindow(hwnd); + + /* multi-line control inserts CR normally */ + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP|ES_MULTILINE, + 0, 0, 200, 60, 0, 0, 0, 0); + ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError()); + + p = char_list; + while (*p != '\0') { + SendMessageA(hwnd, WM_KEYDOWN, *p, 1); + ret = SendMessageA(hwnd, WM_CHAR, *p, 1); + ok(ret == 0, "WM_CHAR('%c') ret=%d\n", *p, ret); + SendMessageA(hwnd, WM_KEYUP, *p, 1); + p++; + } + + SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer); + ret = strcmp(buffer, expected_content_multi); + ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n"); + + DestroyWindow(hwnd); +} + +static void test_EM_GETTEXTLENGTHEX(void) +{ + HWND hwnd; + GETTEXTLENGTHEX gtl; + int ret; + const char * test_string = "a\nb\n\n\r\n"; + const char * test_string_after = "a"; + char buffer[64] = {0}; + + /* single line */ + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP, + 0, 0, 200, 60, 0, 0, 0, 0); + ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError()); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + ok(ret == 0, "ret %d\n",ret); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + ok(ret == 0, "ret %d\n",ret); + + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + ok(ret == 1, "ret %d\n",ret); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + ok(ret == 1, "ret %d\n",ret); + + SendMessage(hwnd, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer); + ret = strcmp(buffer, test_string_after); + ok(ret == 0, "WM_GETTEXT recovered incorrect string!\n"); + + DestroyWindow(hwnd); + + /* multi line */ + hwnd = CreateWindowExA(0, "RichEdit20W", NULL, WS_POPUP | ES_MULTILINE, + 0, 0, 200, 60, 0, 0, 0, 0); + ok(hwnd != 0, "CreateWindowExA error %u\n", GetLastError()); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + todo_wine ok(ret == 0, "ret %d\n",ret); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + ok(ret == 0, "ret %d\n",ret); + + SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM) test_string); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE | GTL_USECRLF; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + todo_wine ok(ret == 10, "ret %d\n",ret); + + gtl.flags = GTL_NUMCHARS | GTL_PRECISE; + gtl.codepage = CP_ACP; + ret = SendMessageA(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); + ok(ret == 6, "ret %d\n",ret); + + DestroyWindow(hwnd); +} + + +/* globals that parent and child access when checking event masks & notifications */ +static HWND eventMaskEditHwnd = 0; +static int queriedEventMask; +static int watchForEventMask = 0; + +/* parent proc that queries the edit's event mask when it gets a WM_COMMAND */ +static LRESULT WINAPI ParentMsgCheckProcA(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if(message == WM_COMMAND && (watchForEventMask & (wParam >> 16))) + { + queriedEventMask = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0); + } + return DefWindowProcA(hwnd, message, wParam, lParam); +} + +/* test event masks in combination with WM_COMMAND */ +static void test_eventMask(void) +{ + HWND parent; + int ret; + WNDCLASSA cls; + const char text[] = "foo bar\n"; + int eventMask; + + /* register class to capture WM_COMMAND */ + cls.style = 0; + cls.lpfnWndProc = ParentMsgCheckProcA; + 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 = "EventMaskParentClass"; + if(!RegisterClassA(&cls)) assert(0); + + parent = CreateWindow(cls.lpszClassName, NULL, WS_POPUP|WS_VISIBLE, + 0, 0, 200, 60, NULL, NULL, NULL, NULL); + ok (parent != 0, "Failed to create parent window\n"); + + eventMaskEditHwnd = new_richedit(parent); + ok(eventMaskEditHwnd != 0, "Failed to create edit window\n"); + + eventMask = ENM_CHANGE | ENM_UPDATE; + ret = SendMessage(eventMaskEditHwnd, EM_SETEVENTMASK, 0, (LPARAM) eventMask); + ok(ret == ENM_NONE, "wrong event mask\n"); + ret = SendMessage(eventMaskEditHwnd, EM_GETEVENTMASK, 0, 0); + ok(ret == eventMask, "failed to set event mask\n"); + + /* check what happens when we ask for EN_CHANGE and send WM_SETTEXT */ + queriedEventMask = 0; /* initialize to something other than we expect */ + watchForEventMask = EN_CHANGE; + ret = SendMessage(eventMaskEditHwnd, WM_SETTEXT, 0, (LPARAM) text); + ok(ret == TRUE, "failed to set text\n"); + /* richedit should mask off ENM_CHANGE when it sends an EN_CHANGE + notification in response to WM_SETTEXT */ + ok(queriedEventMask == (eventMask & ~ENM_CHANGE), + "wrong event mask (0x%x) during WM_COMMAND\n", queriedEventMask); + +} + + +START_TEST( editor ) +{ + MSG msg; + time_t end; + + /* Must explicitly LoadLibrary(). The test has no references to functions in + * RICHED20.DLL, so the linker doesn't actually link to it. */ + hmoduleRichEdit = LoadLibrary("RICHED20.DLL"); + ok(hmoduleRichEdit != NULL, "error: %d\n", (int) GetLastError()); + + test_WM_CHAR(); + test_EM_FINDTEXT(); + test_EM_GETLINE(); + test_EM_SCROLLCARET(); + test_EM_SCROLL(); + test_WM_SETTEXT(); + test_EM_SETCHARFORMAT(); + test_EM_SETTEXTMODE(); + test_TM_PLAINTEXT(); + test_EM_SETOPTIONS(); + test_WM_GETTEXT(); + test_EM_AUTOURLDETECT(); + test_EM_SETUNDOLIMIT(); + test_ES_PASSWORD(); + test_EM_SETTEXTEX(); + test_EM_LIMITTEXT(); + test_EM_EXLIMITTEXT(); + test_EM_GETLIMITTEXT(); + test_WM_SETFONT(); + test_EM_GETMODIFY(); + test_EM_EXSETSEL(); + test_WM_PASTE(); + test_EM_StreamIn_Undo(); + test_EM_FORMATRANGE(); + test_unicode_conversions(); + test_EM_GETTEXTLENGTHEX(); + test_EM_REPLACESEL(); + test_eventMask(); + + /* Set the environment variable WINETEST_RICHED20 to keep windows + * responsive and open for 30 seconds. This is useful for debugging. + * + * The message pump uses PeekMessage() to empty the queue and then sleeps for + * 50ms before retrying the queue. */ + end = time(NULL) + 30; + if (getenv( "WINETEST_RICHED20" )) { + while (time(NULL) < end) { + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + Sleep(50); + } + } + } + + OleFlushClipboard(); + ok(FreeLibrary(hmoduleRichEdit) != 0, "error: %d\n", (int) GetLastError()); +} diff --git a/rostests/winetests/riched20/riched20.rbuild b/rostests/winetests/riched20/riched20.rbuild new file mode 100644 index 00000000000..f9fac8b44cf --- /dev/null +++ b/rostests/winetests/riched20/riched20.rbuild @@ -0,0 +1,16 @@ + + + + . + 0x600 + 0x600 + wine + riched20 + ole32 + user32 + gdi32 + kernel32 + ntdll + editor.c + testlist.c + diff --git a/rostests/winetests/riched20/testlist.c b/rostests/winetests/riched20/testlist.c new file mode 100644 index 00000000000..08e717eeb0d --- /dev/null +++ b/rostests/winetests/riched20/testlist.c @@ -0,0 +1,15 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_editor(void); + +const struct test winetest_testlist[] = +{ + { "editor", func_editor }, + { 0, 0 } +}; diff --git a/rostests/winetests/rsabase/rsabase.c b/rostests/winetests/rsabase/rsabase.c new file mode 100644 index 00000000000..b7746b10319 --- /dev/null +++ b/rostests/winetests/rsabase/rsabase.c @@ -0,0 +1,84 @@ +/* + * Unit tests for rsabase 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wincrypt.h" + +HCRYPTPROV hProv; +static const char szContainer[] = "Wine Test Container"; +static const char szProvider[] = MS_DEF_PROV_A; + +static int init_environment(void) +{ + hProv = (HCRYPTPROV)INVALID_HANDLE_VALUE; + + if (!CryptAcquireContext(&hProv, szContainer, szProvider, PROV_RSA_FULL, 0)) + { + if (GetLastError()==NTE_BAD_KEYSET) + { + if(!CryptAcquireContext(&hProv, szContainer, szProvider, PROV_RSA_FULL, CRYPT_NEWKEYSET)) + { + trace("%08x\n", GetLastError()); + return 0; + } + } + else + { + trace("%08x\n", GetLastError()); + return 0; + } + } + + return 1; +} + +static void clean_up_environment(void) +{ + CryptAcquireContext(&hProv, szContainer, szProvider, PROV_RSA_FULL, CRYPT_DELETEKEYSET); +} + +static void test_gen_random(void) +{ + BOOL result; + BYTE rnd1[16], rnd2[16]; + + memset(rnd1, 0, sizeof(rnd1)); + memset(rnd2, 0, sizeof(rnd2)); + + result = CryptGenRandom(hProv, sizeof(rnd1), rnd1); + ok(result, "%08x\n", GetLastError()); + + result = CryptGenRandom(hProv, sizeof(rnd2), rnd2); + ok(result, "%08x\n", GetLastError()); + + ok(memcmp(rnd1, rnd2, sizeof(rnd1)), "CryptGenRandom generates non random data\n"); +} + +START_TEST(rsabase) +{ + if (!init_environment()) + return; + test_gen_random(); + clean_up_environment(); +} diff --git a/rostests/winetests/rsabase/rsabase.rbuild b/rostests/winetests/rsabase/rsabase.rbuild new file mode 100644 index 00000000000..f6399a28fd6 --- /dev/null +++ b/rostests/winetests/rsabase/rsabase.rbuild @@ -0,0 +1,13 @@ + + + + . + 0x600 + 0x600 + wine + advapi32 + kernel32 + ntdll + rsabase.c + testlist.c + diff --git a/rostests/winetests/rsabase/testlist.c b/rostests/winetests/rsabase/testlist.c new file mode 100644 index 00000000000..d55586bacd5 --- /dev/null +++ b/rostests/winetests/rsabase/testlist.c @@ -0,0 +1,15 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_rsabase(void); + +const struct test winetest_testlist[] = +{ + { "rsabase", func_rsabase }, + { 0, 0 } +}; diff --git a/rostests/winetests/rsaenh/rsaenh.c b/rostests/winetests/rsaenh/rsaenh.c new file mode 100644 index 00000000000..b44398d25e6 --- /dev/null +++ b/rostests/winetests/rsaenh/rsaenh.c @@ -0,0 +1,2014 @@ +/* + * Unit tests for rsaenh functions + * + * Copyright (c) 2004 Michael Jung + * Copyright (c) 2006 Juan Lang + * Copyright (c) 2007 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 +#include +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wincrypt.h" + +static HCRYPTPROV hProv; +static const char szContainer[] = "winetest"; +static const unsigned char pbData[] = "Wine rocks totally!"; +static const char szProvider[] = MS_ENHANCED_PROV_A; + +typedef struct _ctdatatype { + unsigned char origstr[32]; + unsigned char decstr[32]; + int strlen; + int enclen; + int buflen; +} cryptdata; + +static const cryptdata cTestData[4] = { + {"abcdefghijkl", + {'a','b','c','d','e','f','g','h',0x2,0x2,'k','l',0}, + 12,8,16}, + {"abcdefghij", + {'a','b','c','d','e','f','g','h',0x2,0x2,0}, + 10,8,16}, + {"abcdefgh", + {'a','b','c','d','e','f','g','h',0}, + 8,8,16}, + {"abcdefghijkl", + {'a','b','c','d','e','f','g','h','i','j','k','l',0}, + 12,12,16} +}; + +static void printBytes(const char *heading, const BYTE *pb, size_t cb) +{ + size_t i; + printf("%s: ",heading); + for(i=0;i + + + . + 0x600 + 0x600 + wine + advapi32 + kernel32 + ntdll + rsaenh.c + testlist.c + diff --git a/rostests/winetests/rsaenh/testlist.c b/rostests/winetests/rsaenh/testlist.c new file mode 100644 index 00000000000..0c1563415d0 --- /dev/null +++ b/rostests/winetests/rsaenh/testlist.c @@ -0,0 +1,15 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_rsaenh(void); + +const struct test winetest_testlist[] = +{ + { "rsaenh", func_rsaenh }, + { 0, 0 } +}; diff --git a/rostests/winetests/shlwapi/ordinal.c b/rostests/winetests/shlwapi/ordinal.c index 234097a251a..f5286f02f45 100755 --- a/rostests/winetests/shlwapi/ordinal.c +++ b/rostests/winetests/shlwapi/ordinal.c @@ -23,6 +23,8 @@ #include "winbase.h" #include "winerror.h" #include "winuser.h" +#include "ole2.h" +#include "oaidl.h" /* Function ptrs for ordinal calls */ static HMODULE hShlwapi; @@ -33,6 +35,7 @@ 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 HRESULT(WINAPIV *pSHPackDispParams)(DISPPARAMS*,VARIANTARG*,UINT,...); static void test_GetAcceptLanguagesA(void) { HRESULT retval; @@ -52,7 +55,7 @@ static void test_GetAcceptLanguagesA(void) trace("GetAcceptLanguagesA: skipping tests\n"); return; } - ok( (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()) || + ok( (ERROR_NO_IMPERSONATION_TOKEN == GetLastError()) || (ERROR_CLASS_DOES_NOT_EXIST == GetLastError()) || (ERROR_PROC_NOT_FOUND == GetLastError()) || (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) || @@ -380,7 +383,7 @@ static void test_GetShellSecurityDescriptor(void) "GetSecurityDescriptorControl failed with error %u\n", GetLastError()); ok(0 == (control & SE_SELF_RELATIVE), "SD should be absolute\n"); - ok(GetSecurityDescriptorDacl(psd, &bHasDacl, &pAcl, &bDefaulted), + ok(GetSecurityDescriptorDacl(psd, &bHasDacl, &pAcl, &bDefaulted), "GetSecurityDescriptorDacl failed with error %u\n", GetLastError()); ok(bHasDacl, "SD has no DACL\n"); @@ -404,21 +407,21 @@ static void test_GetShellSecurityDescriptor(void) ACCESS_ALLOWED_ACE *paaa; /* will use for DENIED too */ ok(GetAce(pAcl, 0, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError()); - ok(paaa->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, - "Invalid ACE type %d\n", paaa->Header.AceType); + ok(paaa->Header.AceType == ACCESS_ALLOWED_ACE_TYPE, + "Invalid ACE type %d\n", paaa->Header.AceType); ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags); ok(paaa->Mask == GENERIC_ALL, "Invalid ACE mask %x\n", paaa->Mask); ok(GetAce(pAcl, 1, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError()); - ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, - "Invalid ACE type %d\n", paaa->Header.AceType); + ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, + "Invalid ACE type %d\n", paaa->Header.AceType); /* first one of two ACEs generated from inheritable entry - without inheritance */ ok(paaa->Header.AceFlags == 0, "Invalid ACE flags %x\n", paaa->Header.AceFlags); ok(paaa->Mask == GENERIC_WRITE, "Invalid ACE mask %x\n", paaa->Mask); ok(GetAce(pAcl, 2, (LPVOID*)&paaa), "GetAce failed with error %u\n", GetLastError()); - ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, - "Invalid ACE type %d\n", paaa->Header.AceType); + ok(paaa->Header.AceType == ACCESS_DENIED_ACE_TYPE, + "Invalid ACE type %d\n", paaa->Header.AceType); /* second ACE - with inheritance */ ok(paaa->Header.AceFlags == MY_INHERITANCE, "Invalid ACE flags %x\n", paaa->Header.AceFlags); @@ -431,6 +434,53 @@ static void test_GetShellSecurityDescriptor(void) } } +static void test_SHPackDispParams(void) +{ + DISPPARAMS params; + VARIANT vars[10]; + HRESULT hres; + + if(!pSHPackDispParams) + skip("SHPackSidpParams not available\n"); + + memset(¶ms, 0xc0, sizeof(params)); + memset(vars, 0xc0, sizeof(vars)); + hres = pSHPackDispParams(¶ms, vars, 1, VT_I4, 0xdeadbeef); + ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres); + ok(params.cArgs == 1, "params.cArgs = %d\n", params.cArgs); + ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs); + ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs); + ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg); + ok(V_VT(vars) == VT_I4, "V_VT(var) = %d\n", V_VT(vars)); + ok(V_DISPATCH(vars) == (void*)0xdeadbeef, "failed\n"); + + memset(¶ms, 0xc0, sizeof(params)); + hres = pSHPackDispParams(¶ms, NULL, 0, 0); + ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres); + ok(params.cArgs == 0, "params.cArgs = %d\n", params.cArgs); + ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs); + ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs); + ok(params.rgvarg == NULL, "params.rgvarg = %p\n", params.rgvarg); + + memset(vars, 0xc0, sizeof(vars)); + memset(¶ms, 0xc0, sizeof(params)); + hres = pSHPackDispParams(¶ms, vars, 4, VT_BSTR, (void*)0xdeadbeef, VT_EMPTY, 10, + VT_I4, 100, VT_DISPATCH, (void*)0xdeadbeef); + ok(hres == S_OK, "SHPackDispParams failed: %08x\n", hres); + ok(params.cArgs == 4, "params.cArgs = %d\n", params.cArgs); + ok(params.cNamedArgs == 0, "params.cNamedArgs = %d\n", params.cArgs); + ok(params.rgdispidNamedArgs == NULL, "params.rgdispidNamedArgs = %p\n", params.rgdispidNamedArgs); + ok(params.rgvarg == vars, "params.rgvarg = %p\n", params.rgvarg); + ok(V_VT(vars) == VT_DISPATCH, "V_VT(vars[0]) = %x\n", V_VT(vars)); + ok(V_I4(vars) == 0xdeadbeef, "V_I4(vars[0]) = %x\n", V_I4(vars)); + ok(V_VT(vars+1) == VT_I4, "V_VT(vars[1]) = %d\n", V_VT(vars+1)); + ok(V_I4(vars+1) == 100, "V_I4(vars[1]) = %x\n", V_I4(vars+1)); + ok(V_VT(vars+2) == VT_I4, "V_VT(vars[2]) = %d\n", V_VT(vars+2)); + ok(V_I4(vars+2) == 10, "V_I4(vars[2]) = %x\n", V_I4(vars+2)); + ok(V_VT(vars+3) == VT_BSTR, "V_VT(vars[3]) = %d\n", V_VT(vars+3)); + ok(V_BSTR(vars+3) == (void*)0xdeadbeef, "V_BSTR(vars[3]) = %p\n", V_BSTR(vars+3)); +} + START_TEST(ordinal) { hShlwapi = GetModuleHandleA("shlwapi.dll"); @@ -441,10 +491,12 @@ START_TEST(ordinal) pSHLockShared=(void*)GetProcAddress(hShlwapi,(char*)8); pSHUnlockShared=(void*)GetProcAddress(hShlwapi,(char*)9); pSHFreeShared=(void*)GetProcAddress(hShlwapi,(char*)10); + pSHPackDispParams=(void*)GetProcAddress(hShlwapi,(char*)282); test_GetAcceptLanguagesA(); test_SHSearchMapInt(); test_alloc_shared(); test_fdsa(); test_GetShellSecurityDescriptor(); + test_SHPackDispParams(); } diff --git a/rostests/winetests/shlwapi/path.c b/rostests/winetests/shlwapi/path.c index d562062dcaf..5fb490d0fad 100755 --- a/rostests/winetests/shlwapi/path.c +++ b/rostests/winetests/shlwapi/path.c @@ -33,199 +33,7 @@ static HRESULT (WINAPI *pPathIsValidCharA)(char,DWORD); static HRESULT (WINAPI *pPathIsValidCharW)(WCHAR,DWORD); static LPWSTR (WINAPI *pPathCombineW)(LPWSTR, LPCWSTR, LPCWSTR); -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", 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", 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"}, - {"file:///c:/tests\\foo%20bar", URL_UNESCAPE , S_OK, "file:///c:/tests/foo bar"}, - {"file:///c:/tests/foo%20bar", 0, S_OK, "file:///c:/tests/foo%20bar"}, - {"file:///c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, - {"file://c:/tests/../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, - {"file://c:/tests\\../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, - {"file://c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, - {"file:///c://tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\\\tests\\foo bar"}, - {"file:///c:\\tests\\foo bar", 0, S_OK, "file:///c:/tests/foo bar"}, - {"file:///c:\\tests\\foo bar", URL_DONT_SIMPLIFY, S_OK, "file:///c:/tests/foo bar"}, - {"http://www.winehq.org/site/about", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org/site/about"}, - {"file_://www.winehq.org/site/about", URL_FILE_USE_PATHURL, S_OK, "file_://www.winehq.org/site/about"}, - {"c:\\dir\\file", 0, S_OK, "file:///c:/dir/file"}, - {"file:///c:\\dir\\file", 0, S_OK, "file:///c:/dir/file"}, - {"c:dir\\file", 0, S_OK, "file:///c:dir/file"}, - {"c:\\tests\\foo bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, - {"c:\\tests\\foo bar", 0, S_OK, "file:///c:/tests/foo%20bar"}, - {"A", 0, S_OK, "A"}, - {"", 0, S_OK, ""} -}; - -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"}, - {"http://www.%77inehq.org/tests", "tests1", 0, S_OK, "http://www.%77inehq.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/test1", "test2", 0, S_OK, "http://www.winehq.org/tests/test2"}, - {"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"}, - {"file:///C:\\dir\\file.txt", "test.txt", 0, S_OK, "file:///C:/dir/test.txt"}, - {"http://www.winehq.org/test/", "test%20file.txt", 0, S_OK, "http://www.winehq.org/test/test%20file.txt"}, - {"http://www.winehq.org/test/", "test%20file.txt", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org/test/test%20file.txt"}, - {"http://www.winehq.org%2ftest/", "test%20file.txt", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org%2ftest/test%20file.txt"}, - {"xxx:@MSITStore:file.chm/file.html", "dir/file", 0, S_OK, "xxx:dir/file"}, - {"mk:@MSITStore:file.chm::/file.html", "/dir/file", 0, S_OK, "mk:@MSITStore:file.chm::/dir/file"}, - {"mk:@MSITStore:file.chm::/file.html", "mk:@MSITStore:file.chm::/dir/file", 0, S_OK, "mk:@MSITStore:file.chm::/dir/file"}, -}; - -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; @@ -261,16 +69,8 @@ struct { }; -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 { +static struct { const char *path; BOOL expect; } TEST_PATH_IS_URL[] = { @@ -283,38 +83,6 @@ struct { {"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 } -}; - struct { const char *path; const char *result; @@ -332,10 +100,12 @@ struct { { "\"", "" } }; +/* ################ */ + 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; @@ -355,348 +125,15 @@ static LPSTR strdupA(LPCSTR p) return ret; } -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%08x 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(lstrcmpW(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) -{ - CHAR szPart[INTERNET_MAX_URL_LENGTH]; - DWORD dwSize; - HRESULT res; - - dwSize = sizeof szPart; - szPart[0]='x'; szPart[1]=0; - res = UrlGetPartA("hi", szPart, &dwSize, URL_PART_SCHEME, 0); - todo_wine { - ok (res==S_FALSE, "UrlGetPartA(\"hi\") returned %08X\n", res); - ok(szPart[0]==0, "UrlGetPartA(\"hi\") return \"%s\" instead of \"\"\n", szPart); - } - dwSize = sizeof szPart; - szPart[0]='x'; szPart[1]=0; - res = UrlGetPartA("hi", szPart, &dwSize, URL_PART_QUERY, 0); - todo_wine { - ok (res==S_FALSE, "UrlGetPartA(\"hi\") returned %08X\n", res); - ok(szPart[0]==0, "UrlGetPartA(\"hi\") return \"%s\" instead of \"\"\n", szPart); - } - - 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%08x 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%08x from \"%s\"\n", dwExpectReturn, szUrl); - WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0); - ok(lstrcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08x\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%08x\n", dwExpectReturn); - ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08x 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%08x\n", dwExpectReturn); - wszConvertedUrl = GetWideString(szReturnUrl); - ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize!\n"); - FreeWideString(wszConvertedUrl); - - - FreeWideString(wszUrl); - FreeWideString(wszExpectUrl); -} - - -static void test_UrlEscape(void) -{ - DWORD size; - HRESULT ret; - unsigned int i; - - ret = UrlEscapeA("/woningplan/woonkamer basis.swf", NULL, &size, URL_ESCAPE_SPACES_ONLY); - ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG); - - for(i=0; i + + + . - - 0x600 - 0x501 - 0x501 + 0x600 + 0x600 wine shlwapi advapi32 @@ -19,5 +19,6 @@ path.c shreg.c string.c + url.c testlist.c diff --git a/rostests/winetests/shlwapi/shreg.c b/rostests/winetests/shlwapi/shreg.c index dd61ab1d4f5..77a47e2240c 100755 --- a/rostests/winetests/shlwapi/shreg.c +++ b/rostests/winetests/shlwapi/shreg.c @@ -102,10 +102,10 @@ static HKEY create_test_entries(void) trace("sExplen2 = (%d)\n", nExpLen2); if (nExpectedLen2 != nExpLen2) - trace( "Expanding %s failed (expected %d) - known bug in NT4\n", sTestpath2, nExpectedLen2 ); + trace( "Expanding %s failed (expected %d) - known bug in NT4\n", sTestpath2, nExpectedLen2 ); /* Make sure we carry on with correct values */ - nExpLen1 = nExpectedLen1; + nExpLen1 = nExpectedLen1; nExpLen2 = nExpectedLen2; return hKey; } @@ -238,7 +238,7 @@ static void test_SHQUeryValueEx(void) todo_wine { ok( (0 == strcmp("", buf)) || (0 == strcmp(sTestpath2, buf)), - "Expected empty or unexpanded string (win98), got (%s)\n", buf); + "Expected empty or unexpanded string (win98), got (%s)\n", buf); } ok( dwSize >= nUsedBuffer2, "Buffer size (%u) should be >= (%u)\n", dwSize, nUsedBuffer2); diff --git a/rostests/winetests/shlwapi/string.c b/rostests/winetests/shlwapi/string.c index 8c0cf9136b6..895d767611c 100755 --- a/rostests/winetests/shlwapi/string.c +++ b/rostests/winetests/shlwapi/string.c @@ -704,7 +704,7 @@ static void test_StrRStrI(void) static const WCHAR wszPattern4[] = {'a','b',0}; LPWSTR retW; LPSTR retA; - + check_strrstri(A, szTest, 4, "A", szTest+1); check_strrstri(A, szTest, 4, "aX", szTest+1); check_strrstri(A, szTest, 4, "Ay", NULL); diff --git a/rostests/winetests/shlwapi/testlist.c b/rostests/winetests/shlwapi/testlist.c index ec8185df119..c34cd62b55a 100755 --- a/rostests/winetests/shlwapi/testlist.c +++ b/rostests/winetests/shlwapi/testlist.c @@ -13,6 +13,7 @@ extern void func_ordinal(void); extern void func_path(void); extern void func_shreg(void); extern void func_string(void); +extern void func_url(void); const struct test winetest_testlist[] = { @@ -23,5 +24,6 @@ const struct test winetest_testlist[] = { "path", func_path }, { "shreg", func_shreg }, { "string", func_string }, + { "url", func_url }, { 0, 0 } }; diff --git a/rostests/winetests/shlwapi/url.c b/rostests/winetests/shlwapi/url.c new file mode 100644 index 00000000000..6d219c97188 --- /dev/null +++ b/rostests/winetests/shlwapi/url.c @@ -0,0 +1,797 @@ +/* Unit test suite for Path functions + * + * Copyright 2002 Matthew Mastracci + * Copyright 2007 Detlef Riekenberg + * + * 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 +#include + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "shlwapi.h" +#include "wininet.h" + +/* ################ */ +static HMODULE hShlwapi; +static HRESULT (WINAPI *pUrlCanonicalizeW)(LPCWSTR, LPWSTR, LPDWORD, DWORD); + +static const char* TEST_URL_1 = "http://www.winehq.org/tests?date=10/10/1923"; +static const char* TEST_URL_2 = "http://localhost:8080/tests%2e.html?date=Mon%2010/10/1923"; +static const char* TEST_URL_3 = "http://foo:bar@localhost:21/internal.php?query=x&return=y"; +static const WCHAR winehqW[] = {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g','/',0}; +static const CHAR winehqA[] = {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g','/',0}; + +/* ################ */ + +typedef struct _TEST_URL_CANONICALIZE { + const char *url; + DWORD flags; + HRESULT expectret; + const char *expecturl; +} TEST_URL_CANONICALIZE; + +static 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", 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", 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"}, + {"file:///c:/tests\\foo%20bar", URL_UNESCAPE , S_OK, "file:///c:/tests/foo bar"}, + {"file:///c:/tests/foo%20bar", 0, S_OK, "file:///c:/tests/foo%20bar"}, + {"file:///c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, + {"file://c:/tests/../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, + {"file://c:/tests\\../tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, + {"file://c:/tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, + {"file:///c://tests/foo%20bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\\\tests\\foo bar"}, + {"file:///c:\\tests\\foo bar", 0, S_OK, "file:///c:/tests/foo bar"}, + {"file:///c:\\tests\\foo bar", URL_DONT_SIMPLIFY, S_OK, "file:///c:/tests/foo bar"}, + {"http://www.winehq.org/site/about", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org/site/about"}, + {"file_://www.winehq.org/site/about", URL_FILE_USE_PATHURL, S_OK, "file_://www.winehq.org/site/about"}, + {"c:\\dir\\file", 0, S_OK, "file:///c:/dir/file"}, + {"file:///c:\\dir\\file", 0, S_OK, "file:///c:/dir/file"}, + {"c:dir\\file", 0, S_OK, "file:///c:dir/file"}, + {"c:\\tests\\foo bar", URL_FILE_USE_PATHURL, S_OK, "file://c:\\tests\\foo bar"}, + {"c:\\tests\\foo bar", 0, S_OK, "file:///c:/tests/foo%20bar"}, + {"A", 0, S_OK, "A"}, + {"", 0, S_OK, ""} +}; + +/* ################ */ + +typedef struct _TEST_URL_ESCAPE { + const char *url; + DWORD flags; + DWORD expectescaped; + HRESULT expectret; + const char *expecturl; +} TEST_URL_ESCAPE; + +static 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; + +static const TEST_URL_COMBINE TEST_COMBINE[] = { + {"http://www.winehq.org/tests", "tests1", 0, S_OK, "http://www.winehq.org/tests1"}, + {"http://www.%77inehq.org/tests", "tests1", 0, S_OK, "http://www.%77inehq.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/test1", "test2", 0, S_OK, "http://www.winehq.org/tests/test2"}, + {"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"}, + {"file:///C:\\dir\\file.txt", "test.txt", 0, S_OK, "file:///C:/dir/test.txt"}, + {"http://www.winehq.org/test/", "test%20file.txt", 0, S_OK, "http://www.winehq.org/test/test%20file.txt"}, + {"http://www.winehq.org/test/", "test%20file.txt", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org/test/test%20file.txt"}, + {"http://www.winehq.org%2ftest/", "test%20file.txt", URL_FILE_USE_PATHURL, S_OK, "http://www.winehq.org%2ftest/test%20file.txt"}, + {"xxx:@MSITStore:file.chm/file.html", "dir/file", 0, S_OK, "xxx:dir/file"}, + {"mk:@MSITStore:file.chm::/file.html", "/dir/file", 0, S_OK, "mk:@MSITStore:file.chm::/dir/file"}, + {"mk:@MSITStore:file.chm::/file.html", "mk:@MSITStore:file.chm::/dir/file", 0, S_OK, "mk:@MSITStore:file.chm::/dir/file"}, + {"foo:today", "foo:calendar", 0, S_OK, "foo:calendar"}, + {"foo:today", "bar:calendar", 0, S_OK, "bar:calendar"}, + {"foo:/today", "foo:calendar", 0, S_OK, "foo:/calendar"}, + {"foo:/today/", "foo:calendar", 0, S_OK, "foo:/today/calendar"}, +}; + +/* ################ */ + +static const 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 +}; + +/* ################ */ + +static 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"} +}; + +/* ################ */ + +static const 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} +}; + +/* ################ */ + +static const 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%08x 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(lstrcmpW(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) +{ + CHAR szPart[INTERNET_MAX_URL_LENGTH]; + DWORD dwSize; + HRESULT res; + + dwSize = sizeof szPart; + szPart[0]='x'; szPart[1]=0; + res = UrlGetPartA("hi", szPart, &dwSize, URL_PART_SCHEME, 0); + todo_wine { + ok (res==S_FALSE, "UrlGetPartA(\"hi\") returned %08X\n", res); + ok(szPart[0]==0, "UrlGetPartA(\"hi\") return \"%s\" instead of \"\"\n", szPart); + } + dwSize = sizeof szPart; + szPart[0]='x'; szPart[1]=0; + res = UrlGetPartA("hi", szPart, &dwSize, URL_PART_QUERY, 0); + todo_wine { + ok (res==S_FALSE, "UrlGetPartA(\"hi\") returned %08X\n", res); + ok(szPart[0]==0, "UrlGetPartA(\"hi\") return \"%s\" instead of \"\"\n", szPart); + } + + 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%08x 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%08x from \"%s\"\n", dwExpectReturn, szUrl); + WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0); + ok(lstrcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08x\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%08x\n", dwExpectReturn); + ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08x 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%08x\n", dwExpectReturn); + wszConvertedUrl = GetWideString(szReturnUrl); + ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize!\n"); + FreeWideString(wszConvertedUrl); + + + FreeWideString(wszUrl); + FreeWideString(wszExpectUrl); +} + + +static void test_UrlEscape(void) +{ + DWORD size; + HRESULT ret; + unsigned int i; + + ret = UrlEscapeA("/woningplan/woonkamer basis.swf", NULL, &size, URL_ESCAPE_SPACES_ONLY); + ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG); + + for(i=0; i +#include "windef.h" +#include "winbase.h" +#include "urlmon.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_BINDINFO(void) +{ + /* BINDINFO (pack 4) */ + TEST_FIELD(BINDINFO, ULONG, cbSize, 0, 4, 4); + TEST_FIELD(BINDINFO, LPWSTR, szExtraInfo, 4, 4, 4); +} + +static void test_pack_IBindHost(void) +{ + /* IBindHost */ +} + +static void test_pack_IBindHostVtbl(void) +{ + /* IBindHostVtbl (pack 4) */ +} + +static void test_pack_IBindStatusCallback(void) +{ + /* IBindStatusCallback */ +} + +static void test_pack_IBindStatusCallbackVtbl(void) +{ + /* IBindStatusCallbackVtbl (pack 4) */ +} + +static void test_pack_IBinding(void) +{ + /* IBinding */ +} + +static void test_pack_IBindingVtbl(void) +{ + /* IBindingVtbl (pack 4) */ +} + +static void test_pack_IInternetProtocolInfo(void) +{ + /* IInternetProtocolInfo */ +} + +static void test_pack_IInternetProtocolInfoVtbl(void) +{ + /* IInternetProtocolInfoVtbl (pack 4) */ +} + +static void test_pack_IInternetSession(void) +{ + /* IInternetSession */ +} + +static void test_pack_IInternetSessionVtbl(void) +{ + /* IInternetSessionVtbl (pack 4) */ +} + +static void test_pack_IPersistMoniker(void) +{ + /* IPersistMoniker */ +} + +static void test_pack_IPersistMonikerVtbl(void) +{ + /* IPersistMonikerVtbl (pack 4) */ +} + +static void test_pack_IWinInetHttpInfo(void) +{ + /* IWinInetHttpInfo */ +} + +static void test_pack_IWinInetHttpInfoVtbl(void) +{ + /* IWinInetHttpInfoVtbl (pack 4) */ +} + +static void test_pack_IWinInetInfo(void) +{ + /* IWinInetInfo */ +} + +static void test_pack_IWinInetInfoVtbl(void) +{ + /* IWinInetInfoVtbl (pack 4) */ +} + +static void test_pack_LPBINDHOST(void) +{ + /* LPBINDHOST */ + TEST_TYPE(LPBINDHOST, 4, 4); +} + +static void test_pack_LPBINDING(void) +{ + /* LPBINDING */ + TEST_TYPE(LPBINDING, 4, 4); +} + +static void test_pack_LPBINDSTATUSCALLBACK(void) +{ + /* LPBINDSTATUSCALLBACK */ + TEST_TYPE(LPBINDSTATUSCALLBACK, 4, 4); +} + +static void test_pack_LPIINTERNETPROTOCOLINFO(void) +{ + /* LPIINTERNETPROTOCOLINFO */ + TEST_TYPE(LPIINTERNETPROTOCOLINFO, 4, 4); +} + +static void test_pack_LPIINTERNETSESSION(void) +{ + /* LPIINTERNETSESSION */ + TEST_TYPE(LPIINTERNETSESSION, 4, 4); +} + +static void test_pack_LPPERSISTMONIKER(void) +{ + /* LPPERSISTMONIKER */ + TEST_TYPE(LPPERSISTMONIKER, 4, 4); +} + +static void test_pack_LPREMFORMATETC(void) +{ + /* LPREMFORMATETC */ + TEST_TYPE(LPREMFORMATETC, 4, 4); +} + +static void test_pack_LPREMSECURITY_ATTRIBUTES(void) +{ + /* LPREMSECURITY_ATTRIBUTES */ + TEST_TYPE(LPREMSECURITY_ATTRIBUTES, 4, 4); +} + +static void test_pack_LPWININETHTTPINFO(void) +{ + /* LPWININETHTTPINFO */ + TEST_TYPE(LPWININETHTTPINFO, 4, 4); +} + +static void test_pack_LPWININETINFO(void) +{ + /* LPWININETINFO */ + TEST_TYPE(LPWININETINFO, 4, 4); +} + +static void test_pack_PREMSECURITY_ATTRIBUTES(void) +{ + /* PREMSECURITY_ATTRIBUTES */ + TEST_TYPE(PREMSECURITY_ATTRIBUTES, 4, 4); +} + +static void test_pack_REMSECURITY_ATTRIBUTES(void) +{ + /* REMSECURITY_ATTRIBUTES (pack 4) */ + TEST_TYPE(REMSECURITY_ATTRIBUTES, 12, 4); + TEST_FIELD(REMSECURITY_ATTRIBUTES, DWORD, nLength, 0, 4, 4); + TEST_FIELD(REMSECURITY_ATTRIBUTES, DWORD, lpSecurityDescriptor, 4, 4, 4); + TEST_FIELD(REMSECURITY_ATTRIBUTES, BOOL, bInheritHandle, 8, 4, 4); +} + +static void test_pack_RemBINDINFO(void) +{ + /* RemBINDINFO (pack 4) */ + TEST_TYPE(RemBINDINFO, 72, 4); + TEST_FIELD(RemBINDINFO, ULONG, cbSize, 0, 4, 4); + TEST_FIELD(RemBINDINFO, LPWSTR, szExtraInfo, 4, 4, 4); + TEST_FIELD(RemBINDINFO, DWORD, grfBindInfoF, 8, 4, 4); + TEST_FIELD(RemBINDINFO, DWORD, dwBindVerb, 12, 4, 4); + TEST_FIELD(RemBINDINFO, LPWSTR, szCustomVerb, 16, 4, 4); + TEST_FIELD(RemBINDINFO, DWORD, cbstgmedData, 20, 4, 4); + TEST_FIELD(RemBINDINFO, DWORD, dwOptions, 24, 4, 4); + TEST_FIELD(RemBINDINFO, DWORD, dwOptionsFlags, 28, 4, 4); + TEST_FIELD(RemBINDINFO, DWORD, dwCodePage, 32, 4, 4); + TEST_FIELD(RemBINDINFO, REMSECURITY_ATTRIBUTES, securityAttributes, 36, 12, 4); + TEST_FIELD(RemBINDINFO, IID, iid, 48, 16, 4); + TEST_FIELD(RemBINDINFO, IUnknown *, pUnk, 64, 4, 4); + TEST_FIELD(RemBINDINFO, DWORD, dwReserved, 68, 4, 4); +} + +static void test_pack_RemFORMATETC(void) +{ + /* RemFORMATETC (pack 4) */ + TEST_TYPE(RemFORMATETC, 20, 4); + TEST_FIELD(RemFORMATETC, DWORD, cfFormat, 0, 4, 4); + TEST_FIELD(RemFORMATETC, DWORD, ptd, 4, 4, 4); + TEST_FIELD(RemFORMATETC, DWORD, dwAspect, 8, 4, 4); + TEST_FIELD(RemFORMATETC, LONG, lindex, 12, 4, 4); + TEST_FIELD(RemFORMATETC, DWORD, tymed, 16, 4, 4); +} + +static void test_pack(void) +{ + test_pack_BINDINFO(); + test_pack_IBindHost(); + test_pack_IBindHostVtbl(); + test_pack_IBindStatusCallback(); + test_pack_IBindStatusCallbackVtbl(); + test_pack_IBinding(); + test_pack_IBindingVtbl(); + test_pack_IInternetProtocolInfo(); + test_pack_IInternetProtocolInfoVtbl(); + test_pack_IInternetSession(); + test_pack_IInternetSessionVtbl(); + test_pack_IPersistMoniker(); + test_pack_IPersistMonikerVtbl(); + test_pack_IWinInetHttpInfo(); + test_pack_IWinInetHttpInfoVtbl(); + test_pack_IWinInetInfo(); + test_pack_IWinInetInfoVtbl(); + test_pack_LPBINDHOST(); + test_pack_LPBINDING(); + test_pack_LPBINDSTATUSCALLBACK(); + test_pack_LPIINTERNETPROTOCOLINFO(); + test_pack_LPIINTERNETSESSION(); + test_pack_LPPERSISTMONIKER(); + test_pack_LPREMFORMATETC(); + test_pack_LPREMSECURITY_ATTRIBUTES(); + test_pack_LPWININETHTTPINFO(); + test_pack_LPWININETINFO(); + test_pack_PREMSECURITY_ATTRIBUTES(); + test_pack_REMSECURITY_ATTRIBUTES(); + test_pack_RemBINDINFO(); + test_pack_RemFORMATETC(); +} + +START_TEST(generated) +{ + test_pack(); +} diff --git a/rostests/winetests/urlmon/misc.c b/rostests/winetests/urlmon/misc.c new file mode 100644 index 00000000000..9f33f730168 --- /dev/null +++ b/rostests/winetests/urlmon/misc.c @@ -0,0 +1,1207 @@ +/* + * Copyright 2005-2006 Jacek Caban 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 +#define CONST_VTABLE + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "ole2.h" +#include "urlmon.h" + +#include "initguid.h" + +DEFINE_GUID(CLSID_AboutProtocol, 0x3050F406, 0x98B5, 0x11CF, 0xBB,0x82, 0x00,0xAA,0x00,0xBD,0xCE,0x0B); + +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + expect_ ## func = FALSE; \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +DEFINE_EXPECT(ParseUrl); +DEFINE_EXPECT(QI_IInternetProtocolInfo); +DEFINE_EXPECT(CreateInstance); +DEFINE_EXPECT(unk_Release); + +static const char *debugstr_w(LPCWSTR str) +{ + static char buf[1024]; + WideCharToMultiByte(CP_ACP, 0, str, -1, buf, sizeof(buf), NULL, NULL); + return buf; +} + +static void test_CreateFormatEnum(void) +{ + IEnumFORMATETC *fenum = NULL, *fenum2 = NULL; + FORMATETC fetc[5]; + ULONG ul; + HRESULT hres; + + static DVTARGETDEVICE dev = {sizeof(dev),0,0,0,0,{0}}; + static FORMATETC formatetc[] = { + {0,&dev,0,0,0}, + {0,&dev,0,1,0}, + {0,NULL,0,2,0}, + {0,NULL,0,3,0}, + {0,NULL,0,4,0} + }; + + hres = CreateFormatEnumerator(0, formatetc, &fenum); + ok(hres == E_FAIL, "CreateFormatEnumerator failed: %08x, expected E_FAIL\n", hres); + hres = CreateFormatEnumerator(0, formatetc, NULL); + ok(hres == E_INVALIDARG, "CreateFormatEnumerator failed: %08x, expected E_INVALIDARG\n", hres); + hres = CreateFormatEnumerator(5, formatetc, NULL); + ok(hres == E_INVALIDARG, "CreateFormatEnumerator failed: %08x, expected E_INVALIDARG\n", hres); + + + hres = CreateFormatEnumerator(5, formatetc, &fenum); + ok(hres == S_OK, "CreateFormatEnumerator failed: %08x\n", hres); + if(FAILED(hres)) + return; + + hres = IEnumFORMATETC_Next(fenum, 2, NULL, &ul); + ok(hres == E_INVALIDARG, "Next failed: %08x, expected E_INVALIDARG\n", hres); + ul = 100; + hres = IEnumFORMATETC_Next(fenum, 0, fetc, &ul); + ok(hres == S_OK, "Next failed: %08x\n", hres); + ok(ul == 0, "ul=%d, expected 0\n", ul); + + hres = IEnumFORMATETC_Next(fenum, 2, fetc, &ul); + ok(hres == S_OK, "Next failed: %08x\n", hres); + ok(fetc[0].lindex == 0, "fetc[0].lindex=%d, expected 0\n", fetc[0].lindex); + ok(fetc[1].lindex == 1, "fetc[1].lindex=%d, expected 1\n", fetc[1].lindex); + ok(fetc[0].ptd == &dev, "fetc[0].ptd=%p, expected %p\n", fetc[0].ptd, &dev); + ok(ul == 2, "ul=%d, expected 2\n", ul); + + hres = IEnumFORMATETC_Skip(fenum, 1); + ok(hres == S_OK, "Skip failed: %08x\n", hres); + + hres = IEnumFORMATETC_Next(fenum, 4, fetc, &ul); + ok(hres == S_FALSE, "Next failed: %08x, expected S_FALSE\n", hres); + ok(fetc[0].lindex == 3, "fetc[0].lindex=%d, expected 3\n", fetc[0].lindex); + ok(fetc[1].lindex == 4, "fetc[1].lindex=%d, expected 4\n", fetc[1].lindex); + ok(fetc[0].ptd == NULL, "fetc[0].ptd=%p, expected NULL\n", fetc[0].ptd); + ok(ul == 2, "ul=%d, expected 2\n", ul); + + hres = IEnumFORMATETC_Next(fenum, 4, fetc, &ul); + ok(hres == S_FALSE, "Next failed: %08x, expected S_FALSE\n", hres); + ok(ul == 0, "ul=%d, expected 0\n", ul); + ul = 100; + hres = IEnumFORMATETC_Next(fenum, 0, fetc, &ul); + ok(hres == S_OK, "Next failed: %08x\n", hres); + ok(ul == 0, "ul=%d, expected 0\n", ul); + + hres = IEnumFORMATETC_Skip(fenum, 3); + ok(hres == S_FALSE, "Skip failed: %08x, expected S_FALSE\n", hres); + + hres = IEnumFORMATETC_Reset(fenum); + ok(hres == S_OK, "Reset failed: %08x\n", hres); + + hres = IEnumFORMATETC_Next(fenum, 5, fetc, NULL); + ok(hres == S_OK, "Next failed: %08x\n", hres); + ok(fetc[0].lindex == 0, "fetc[0].lindex=%d, expected 0\n", fetc[0].lindex); + + hres = IEnumFORMATETC_Reset(fenum); + ok(hres == S_OK, "Reset failed: %08x\n", hres); + + hres = IEnumFORMATETC_Skip(fenum, 2); + ok(hres == S_OK, "Skip failed: %08x\n", hres); + + hres = IEnumFORMATETC_Clone(fenum, NULL); + ok(hres == E_INVALIDARG, "Clone failed: %08x, expected E_INVALIDARG\n", hres); + + hres = IEnumFORMATETC_Clone(fenum, &fenum2); + ok(hres == S_OK, "Clone failed: %08x\n", hres); + + if(SUCCEEDED(hres)) { + ok(fenum != fenum2, "fenum == fenum2\n"); + + hres = IEnumFORMATETC_Next(fenum2, 2, fetc, &ul); + ok(hres == S_OK, "Next failed: %08x\n", hres); + ok(fetc[0].lindex == 2, "fetc[0].lindex=%d, expected 2\n", fetc[0].lindex); + + IEnumFORMATETC_Release(fenum2); + } + + hres = IEnumFORMATETC_Next(fenum, 2, fetc, &ul); + ok(hres == S_OK, "Next failed: %08x\n", hres); + ok(fetc[0].lindex == 2, "fetc[0].lindex=%d, expected 2\n", fetc[0].lindex); + + hres = IEnumFORMATETC_Skip(fenum, 1); + ok(hres == S_OK, "Skip failed: %08x\n", hres); + + IEnumFORMATETC_Release(fenum); +} + +static void test_RegisterFormatEnumerator(void) +{ + IBindCtx *bctx = NULL; + IEnumFORMATETC *format = NULL, *format2 = NULL; + IUnknown *unk = NULL; + HRESULT hres; + + static FORMATETC formatetc = {0,NULL,0,0,0}; + static WCHAR wszEnumFORMATETC[] = + {'_','E','n','u','m','F','O','R','M','A','T','E','T','C','_',0}; + + CreateBindCtx(0, &bctx); + + hres = CreateFormatEnumerator(1, &formatetc, &format); + ok(hres == S_OK, "CreateFormatEnumerator failed: %08x\n", hres); + if(FAILED(hres)) + return; + + hres = RegisterFormatEnumerator(NULL, format, 0); + ok(hres == E_INVALIDARG, + "RegisterFormatEnumerator failed: %08x, expected E_INVALIDARG\n", hres); + hres = RegisterFormatEnumerator(bctx, NULL, 0); + ok(hres == E_INVALIDARG, + "RegisterFormatEnumerator failed: %08x, expected E_INVALIDARG\n", hres); + + hres = RegisterFormatEnumerator(bctx, format, 0); + ok(hres == S_OK, "RegisterFormatEnumerator failed: %08x\n", hres); + + hres = IBindCtx_GetObjectParam(bctx, wszEnumFORMATETC, &unk); + ok(hres == S_OK, "GetObjectParam failed: %08x\n", hres); + ok(unk == (IUnknown*)format, "unk != format\n"); + + hres = RevokeFormatEnumerator(NULL, format); + ok(hres == E_INVALIDARG, + "RevokeFormatEnumerator failed: %08x, expected E_INVALIDARG\n", hres); + + hres = RevokeFormatEnumerator(bctx, format); + ok(hres == S_OK, "RevokeFormatEnumerator failed: %08x\n", hres); + + hres = RevokeFormatEnumerator(bctx, format); + ok(hres == E_FAIL, "RevokeFormatEnumerator failed: %08x, expected E_FAIL\n", hres); + + hres = IBindCtx_GetObjectParam(bctx, wszEnumFORMATETC, &unk); + ok(hres == E_FAIL, "GetObjectParam failed: %08x, expected E_FAIL\n", hres); + + hres = RegisterFormatEnumerator(bctx, format, 0); + ok(hres == S_OK, "RegisterFormatEnumerator failed: %08x\n", hres); + + hres = CreateFormatEnumerator(1, &formatetc, &format2); + ok(hres == S_OK, "CreateFormatEnumerator failed: %08x\n", hres); + + if(SUCCEEDED(hres)) { + hres = RevokeFormatEnumerator(bctx, format); + ok(hres == S_OK, "RevokeFormatEnumerator failed: %08x\n", hres); + + IEnumFORMATETC_Release(format2); + } + + hres = IBindCtx_GetObjectParam(bctx, wszEnumFORMATETC, &unk); + ok(hres == E_FAIL, "GetObjectParam failed: %08x, expected E_FAIL\n", hres); + + IEnumFORMATETC_Release(format); + + hres = RegisterFormatEnumerator(bctx, format, 0); + ok(hres == S_OK, "RegisterFormatEnumerator failed: %08x\n", hres); + hres = RevokeFormatEnumerator(bctx, NULL); + ok(hres == S_OK, "RevokeFormatEnumerator failed: %08x\n", hres); + hres = IBindCtx_GetObjectParam(bctx, wszEnumFORMATETC, &unk); + ok(hres == E_FAIL, "GetObjectParam failed: %08x, expected E_FAIL\n", hres); + + IEnumFORMATETC_Release(format); + IBindCtx_Release(bctx); +} + +static const WCHAR url1[] = {'r','e','s',':','/','/','m','s','h','t','m','l','.','d','l','l', + '/','b','l','a','n','k','.','h','t','m',0}; +static const WCHAR url2[] = {'i','n','d','e','x','.','h','t','m',0}; +static const WCHAR url3[] = {'f','i','l','e',':','c',':','\\','I','n','d','e','x','.','h','t','m',0}; +static const WCHAR url4[] = {'f','i','l','e',':','s','o','m','e','%','2','0','f','i','l','e', + '%','2','e','j','p','g',0}; +static const WCHAR url5[] = {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q', + '.','o','r','g',0}; +static const WCHAR url6[] = {'a','b','o','u','t',':','b','l','a','n','k',0}; +static const WCHAR url7[] = {'f','t','p',':','/','/','w','i','n','e','h','q','.','o','r','g','/', + 'f','i','l','e','.','t','e','s','t',0}; +static const WCHAR url8[] = {'t','e','s','t',':','1','2','3','a','b','c',0}; + + +static const WCHAR url4e[] = {'f','i','l','e',':','s','o','m','e',' ','f','i','l','e', + '.','j','p','g',0}; + +static const WCHAR path3[] = {'c',':','\\','I','n','d','e','x','.','h','t','m',0}; +static const WCHAR path4[] = {'s','o','m','e',' ','f','i','l','e','.','j','p','g',0}; + +static const WCHAR wszRes[] = {'r','e','s',0}; +static const WCHAR wszFile[] = {'f','i','l','e',0}; +static const WCHAR wszHttp[] = {'h','t','t','p',0}; +static const WCHAR wszAbout[] = {'a','b','o','u','t',0}; +static const WCHAR wszEmpty[] = {0}; + +struct parse_test { + LPCWSTR url; + HRESULT secur_hres; + LPCWSTR encoded_url; + HRESULT path_hres; + LPCWSTR path; + LPCWSTR schema; +}; + +static const struct parse_test parse_tests[] = { + {url1, S_OK, url1, E_INVALIDARG, NULL, wszRes}, + {url2, E_FAIL, url2, E_INVALIDARG, NULL, wszEmpty}, + {url3, E_FAIL, url3, S_OK, path3, wszFile}, + {url4, E_FAIL, url4e, S_OK, path4, wszFile}, + {url5, E_FAIL, url5, E_INVALIDARG, NULL, wszHttp}, + {url6, S_OK, url6, E_INVALIDARG, NULL, wszAbout} +}; + +static void test_CoInternetParseUrl(void) +{ + HRESULT hres; + DWORD size; + int i; + + static WCHAR buf[4096]; + + memset(buf, 0xf0, sizeof(buf)); + hres = CoInternetParseUrl(parse_tests[0].url, PARSE_SCHEMA, 0, buf, + 3, &size, 0); + ok(hres == E_POINTER, "schema failed: %08x, expected E_POINTER\n", hres); + + for(i=0; i < sizeof(parse_tests)/sizeof(parse_tests[0]); i++) { + memset(buf, 0xf0, sizeof(buf)); + hres = CoInternetParseUrl(parse_tests[i].url, PARSE_SECURITY_URL, 0, buf, + sizeof(buf)/sizeof(WCHAR), &size, 0); + ok(hres == parse_tests[i].secur_hres, "[%d] security url failed: %08x, expected %08x\n", + i, hres, parse_tests[i].secur_hres); + + memset(buf, 0xf0, sizeof(buf)); + hres = CoInternetParseUrl(parse_tests[i].url, PARSE_ENCODE, 0, buf, + sizeof(buf)/sizeof(WCHAR), &size, 0); + ok(hres == S_OK, "[%d] encoding failed: %08x\n", i, hres); + ok(size == lstrlenW(parse_tests[i].encoded_url), "[%d] wrong size\n", i); + ok(!lstrcmpW(parse_tests[i].encoded_url, buf), "[%d] wrong encoded url\n", i); + + memset(buf, 0xf0, sizeof(buf)); + hres = CoInternetParseUrl(parse_tests[i].url, PARSE_PATH_FROM_URL, 0, buf, + sizeof(buf)/sizeof(WCHAR), &size, 0); + ok(hres == parse_tests[i].path_hres, "[%d] path failed: %08x, expected %08x\n", + i, hres, parse_tests[i].path_hres); + if(parse_tests[i].path) { + ok(size == lstrlenW(parse_tests[i].path), "[%d] wrong size\n", i); + ok(!lstrcmpW(parse_tests[i].path, buf), "[%d] wrong path\n", i); + } + + memset(buf, 0xf0, sizeof(buf)); + hres = CoInternetParseUrl(parse_tests[i].url, PARSE_SCHEMA, 0, buf, + sizeof(buf)/sizeof(WCHAR), &size, 0); + ok(hres == S_OK, "[%d] schema failed: %08x\n", i, hres); + ok(size == lstrlenW(parse_tests[i].schema), "[%d] wrong size\n", i); + ok(!lstrcmpW(parse_tests[i].schema, buf), "[%d] wrong schema\n", i); + } +} + +static void test_CoInternetCompareUrl(void) +{ + HRESULT hres; + + hres = CoInternetCompareUrl(url1, url1, 0); + ok(hres == S_OK, "CoInternetParseUrl failed: %08x\n", hres); + + hres = CoInternetCompareUrl(url1, url3, 0); + ok(hres == S_FALSE, "CoInternetParseUrl failed: %08x\n", hres); + + hres = CoInternetCompareUrl(url3, url1, 0); + ok(hres == S_FALSE, "CoInternetParseUrl failed: %08x\n", hres); +} + +static const WCHAR mimeTextHtml[] = {'t','e','x','t','/','h','t','m','l',0}; +static const WCHAR mimeTextPlain[] = {'t','e','x','t','/','p','l','a','i','n',0}; +static const WCHAR mimeTextRichtext[] = {'t','e','x','t','/','r','i','c','h','t','e','x','t',0}; +static const WCHAR mimeAppOctetStream[] = {'a','p','p','l','i','c','a','t','i','o','n','/', + 'o','c','t','e','t','-','s','t','r','e','a','m',0}; +static const WCHAR mimeImagePjpeg[] = {'i','m','a','g','e','/','p','j','p','e','g',0}; +static const WCHAR mimeImageGif[] = {'i','m','a','g','e','/','g','i','f',0}; +static const WCHAR mimeImageBmp[] = {'i','m','a','g','e','/','b','m','p',0}; +static const WCHAR mimeImageXPng[] = {'i','m','a','g','e','/','x','-','p','n','g',0}; +static const WCHAR mimeImageTiff[] = {'i','m','a','g','e','/','t','i','f','f',0}; +static const WCHAR mimeVideoAvi[] = {'v','i','d','e','o','/','a','v','i',0}; +static const WCHAR mimeVideoMpeg[] = {'v','i','d','e','o','/','m','p','e','g',0}; +static const WCHAR mimeAppPostscript[] = + {'a','p','p','l','i','c','a','t','i','o','n','/','p','o','s','t','s','c','r','i','p','t',0}; +static const WCHAR mimeAppXCompressed[] = {'a','p','p','l','i','c','a','t','i','o','n','/', + 'x','-','c','o','m','p','r','e','s','s','e','d',0}; +static const WCHAR mimeAppXZip[] = {'a','p','p','l','i','c','a','t','i','o','n','/', + 'x','-','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0}; +static const WCHAR mimeAppXGzip[] = {'a','p','p','l','i','c','a','t','i','o','n','/', + 'x','-','g','z','i','p','-','c','o','m','p','r','e','s','s','e','d',0}; +static const WCHAR mimeAppJava[] = {'a','p','p','l','i','c','a','t','i','o','n','/','j','a','v','a',0}; +static const WCHAR mimeAppPdf[] = {'a','p','p','l','i','c','a','t','i','o','n','/','p','d','f',0}; +static const WCHAR mimeAppXMSDownload[] = + {'a','p','p','l','i','c','a','t','i','o','n','/','x','-','m','s','d','o','w','n','l','o','a','d',0}; +static const WCHAR mimeAudioWav[] = {'a','u','d','i','o','/','w','a','v',0}; + +static const struct { + LPCWSTR url; + LPCWSTR mime; + HRESULT hres; +} mime_tests[] = { + {url1, mimeTextHtml, S_OK}, + {url2, mimeTextHtml, S_OK}, + {url3, mimeTextHtml, S_OK}, + {url4, NULL, E_FAIL}, + {url5, NULL, __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)}, + {url6, NULL, E_FAIL}, + {url7, NULL, __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)} +}; + +static BYTE data1[] = "test data\n"; +static BYTE data2[] = {31,'t','e','s',0xfa,'t',' ','d','a','t','a','\n',0}; +static BYTE data3[] = {0,0,0}; +static BYTE data4[] = {'t','e','s',0xfa,'t',' ','d','a','t','a','\n',0,0}; +static BYTE data5[] = {0xa,0xa,0xa,'x',32,'x',0}; +static BYTE data6[] = {0xfa,0xfa,0xfa,0xfa,'\n','\r','\t','x','x','x',1}; +static BYTE data7[] = "blahblah"; +static BYTE data8[] = {'t','e','s',0xfa,'t',' ','<','h','t','m','l','>','d','a','t','a','\n',0,0}; +static BYTE data9[] = {'t','e',0,'s',0xfa,'t',' ','<','h','t','m','l','>','d','a','t','a','\n',0,0}; +static BYTE data10[] = "blahblah"; +static BYTE data11[] = "blahblahblah"; +static BYTE data12[] = "blah'}; +static BYTE data19[] = {'G','I','F','8','7','a'}; +static BYTE data20[] = {'G','I','F','8','9','a'}; +static BYTE data21[] = {'G','I','F','8','7'}; +static BYTE data22[] = {'G','i','F','8','7','a'}; +static BYTE data23[] = {'G','i','F','8','8','a'}; +static BYTE data24[] = {'g','i','f','8','7','a'}; +static BYTE data25[] = {'G','i','F','8','7','A'}; +static BYTE data26[] = {'G','i','F','8','7','a','<','h','t','m','l','>'}; +static BYTE data27[] = {0x30,'G','i','F','8','7','A'}; +static BYTE data28[] = {0x42,0x4d,0x6e,0x42,0x1c,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00}; +static BYTE data29[] = {0x42,0x4d,'x','x','x','x',0x00,0x00,0x00,0x00,'x','x','x','x'}; +static BYTE data30[] = {0x42,0x4d,'x','x','x','x',0x00,0x01,0x00,0x00,'x','x','x','x'}; +static BYTE data31[] = {0x42,0x4d,'x','x','x','x',0x00,0x00,0x00,0x00,'<','h','t','m','l','>'}; +static BYTE data32[] = {0x42,0x4d,'x','x','x','x',0x00,0x00,0x00,0x00,'x','x','x'}; +static BYTE data33[] = {0x00,0x42,0x4d,'x','x','x','x',0x00,0x00,0x00,0x00,'x','x','x'}; +static BYTE data34[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a,'x'}; +static BYTE data35[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a,'x','x','x','x',0}; +static BYTE data36[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,'x','x'}; +static BYTE data37[] = {0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a,'<','h','t','m','l','>'}; +static BYTE data38[] = {0x00,0x89,'P','N','G',0x0d,0x0a,0x1a,0x0a,'x'}; +static BYTE data39[] = {0x4d,0x4d,0x00,0x2a}; +static BYTE data40[] = {0x4d,0x4d,0x00,0x2a,'<','h','t','m','l','>',0}; +static BYTE data41[] = {0x4d,0x4d,0xff}; +static BYTE data42[] = {0x4d,0x4d}; +static BYTE data43[] = {0x00,0x4d,0x4d,0x00}; +static BYTE data44[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'A','V','I',0x20,0xff}; +static BYTE data45[] = {'R','I','F','f',0xff,0xff,0xff,0xff,'A','V','I',0x20,0xff}; +static BYTE data46[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'A','V','I',0x20}; +static BYTE data47[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'A','V','I',0x21,0xff}; +static BYTE data48[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'A','V','I',0x20,'<','h','t','m','l','>'}; +static BYTE data49[] = {'R','I','F','F',0x0f,0x0f,0xf0,0xf0,'A','V','I',0x20,0xf0,0x00}; +static BYTE data50[] = {0x00,0x00,0x01,0xb3,0xff}; +static BYTE data51[] = {0x00,0x00,0x01,0xba,0xff}; +static BYTE data52[] = {0x00,0x00,0x01,0xb8,0xff}; +static BYTE data53[] = {0x00,0x00,0x01,0xba}; +static BYTE data54[] = {0x00,0x00,0x01,0xba,'<','h','t','m','l','>'}; +static BYTE data55[] = {0x1f,0x8b,'x'}; +static BYTE data56[] = {0x1f}; +static BYTE data57[] = {0x1f,0x8b,'<','h','t','m','l','>','t','e','s','t',0}; +static BYTE data58[] = {0x1f,0x8b}; +static BYTE data59[] = {0x50,0x4b,'x'}; +static BYTE data60[] = {0x50,0x4b}; +static BYTE data61[] = {0x50,0x4b,'<','h','t','m','l','>',0}; +static BYTE data62[] = {0xca,0xfe,0xba,0xbe,'x'}; +static BYTE data63[] = {0xca,0xfe,0xba,0xbe}; +static BYTE data64[] = {0xca,0xfe,0xba,0xbe,'<','h','t','m','l','>',0}; +static BYTE data65[] = {0x25,0x50,0x44,0x46,'x'}; +static BYTE data66[] = {0x25,0x50,0x44,0x46}; +static BYTE data67[] = {0x25,0x50,0x44,0x46,'x','<','h','t','m','l','>'}; +static BYTE data68[] = {'M','Z','x'}; +static BYTE data69[] = {'M','Z'}; +static BYTE data70[] = {'M','Z','<','h','t','m','l','>',0xff}; +static BYTE data71[] = {'{','\\','r','t','f',0}; +static BYTE data72[] = {'{','\\','r','t','f'}; +static BYTE data73[] = {' ','{','\\','r','t','f',' '}; +static BYTE data74[] = {'{','\\','r','t','f','<','h','t','m','l','>',' '}; +static BYTE data75[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'W','A','V','E',0xff}; +static BYTE data76[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'W','A','V','E'}; +static BYTE data77[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'W','A','V',0xff,0xff}; +static BYTE data78[] = {'R','I','F','F',0xff,0xff,0xff,0xff,'<','h','t','m','l','>',0xff}; +static BYTE data79[] = {'%','!',0xff}; +static BYTE data80[] = {'%','!'}; +static BYTE data81[] = {'%','!','P','S','<','h','t','m','l','>'}; + +static const struct { + BYTE *data; + DWORD size; + LPCWSTR mime; +} mime_tests2[] = { + {data1, sizeof(data1), mimeTextPlain}, + {data2, sizeof(data2), mimeAppOctetStream}, + {data3, sizeof(data3), mimeAppOctetStream}, + {data4, sizeof(data4), mimeAppOctetStream}, + {data5, sizeof(data5), mimeTextPlain}, + {data6, sizeof(data6), mimeTextPlain}, + {data7, sizeof(data7), mimeTextHtml}, + {data8, sizeof(data8), mimeTextHtml}, + {data9, sizeof(data9), mimeTextHtml}, + {data10, sizeof(data10), mimeTextHtml}, + {data11, sizeof(data11), mimeTextHtml}, + {data12, sizeof(data12), mimeTextHtml}, + {data13, sizeof(data13), mimeTextPlain}, + {data14, sizeof(data14), mimeTextPlain}, + {data15, sizeof(data15), mimeTextPlain}, + {data16, sizeof(data16), mimeImagePjpeg}, + {data17, sizeof(data17), mimeAppOctetStream}, + {data18, sizeof(data18), mimeTextHtml}, + {data19, sizeof(data19), mimeImageGif}, + {data20, sizeof(data20), mimeImageGif}, + {data21, sizeof(data21), mimeTextPlain}, + {data22, sizeof(data22), mimeImageGif}, + {data23, sizeof(data23), mimeTextPlain}, + {data24, sizeof(data24), mimeImageGif}, + {data25, sizeof(data25), mimeImageGif}, + {data26, sizeof(data26), mimeTextHtml}, + {data27, sizeof(data27), mimeTextPlain}, + {data28, sizeof(data28), mimeImageBmp}, + {data29, sizeof(data29), mimeImageBmp}, + {data30, sizeof(data30), mimeAppOctetStream}, + {data31, sizeof(data31), mimeTextHtml}, + {data32, sizeof(data32), mimeAppOctetStream}, + {data33, sizeof(data33), mimeAppOctetStream}, + {data34, sizeof(data34), mimeImageXPng}, + {data35, sizeof(data35), mimeImageXPng}, + {data36, sizeof(data36), mimeAppOctetStream}, + {data37, sizeof(data37), mimeTextHtml}, + {data38, sizeof(data38), mimeAppOctetStream}, + {data39, sizeof(data39), mimeImageTiff}, + {data40, sizeof(data40), mimeTextHtml}, + {data41, sizeof(data41), mimeImageTiff}, + {data42, sizeof(data42), mimeTextPlain}, + {data43, sizeof(data43), mimeAppOctetStream}, + {data44, sizeof(data44), mimeVideoAvi}, + {data45, sizeof(data45), mimeTextPlain}, + {data46, sizeof(data46), mimeTextPlain}, + {data47, sizeof(data47), mimeTextPlain}, + {data48, sizeof(data48), mimeTextHtml}, + {data49, sizeof(data49), mimeVideoAvi}, + {data50, sizeof(data50), mimeVideoMpeg}, + {data51, sizeof(data51), mimeVideoMpeg}, + {data52, sizeof(data52), mimeAppOctetStream}, + {data53, sizeof(data53), mimeAppOctetStream}, + {data54, sizeof(data54), mimeTextHtml}, + {data55, sizeof(data55), mimeAppXGzip}, + {data56, sizeof(data56), mimeTextPlain}, + {data57, sizeof(data57), mimeTextHtml}, + {data58, sizeof(data58), mimeAppOctetStream}, + {data59, sizeof(data59), mimeAppXZip}, + {data60, sizeof(data60), mimeTextPlain}, + {data61, sizeof(data61), mimeTextHtml}, + {data62, sizeof(data62), mimeAppJava}, + {data63, sizeof(data63), mimeTextPlain}, + {data64, sizeof(data64), mimeTextHtml}, + {data65, sizeof(data65), mimeAppPdf}, + {data66, sizeof(data66), mimeTextPlain}, + {data67, sizeof(data67), mimeTextHtml}, + {data68, sizeof(data68), mimeAppXMSDownload}, + {data69, sizeof(data69), mimeTextPlain}, + {data70, sizeof(data70), mimeTextHtml}, + {data71, sizeof(data71), mimeTextRichtext}, + {data72, sizeof(data72), mimeTextPlain}, + {data73, sizeof(data73), mimeTextPlain}, + {data74, sizeof(data74), mimeTextHtml}, + {data75, sizeof(data75), mimeAudioWav}, + {data76, sizeof(data76), mimeTextPlain}, + {data77, sizeof(data77), mimeTextPlain}, + {data78, sizeof(data78), mimeTextHtml}, + {data79, sizeof(data79), mimeAppPostscript}, + {data80, sizeof(data80), mimeTextPlain}, + {data81, sizeof(data81), mimeTextHtml} +}; + +static void test_FindMimeFromData(void) +{ + HRESULT hres; + LPWSTR mime; + int i; + + for(i=0; i 0, "size=%d, expected non-zero\n", size); + + size = 2; + str[0] = 'a'; + hres = ObtainUserAgentString(0, str, &size); + ok(hres == E_OUTOFMEMORY, "ObtainUserAgentString failed: %08x\n", hres); + ok(size > 0, "size=%d, expected non-zero\n", size); + ok(str[0] == 'a', "str[0]=%c, expected 'a'\n", str[0]); + + size = 0; + hres = ObtainUserAgentString(1, str, &size); + ok(hres == E_OUTOFMEMORY, "ObtainUserAgentString failed: %08x\n", hres); + ok(size > 0, "size=%d, expected non-zero\n", size); + + str2 = HeapAlloc(GetProcessHeap(), 0, (size+20)*sizeof(CHAR)); + if (!str2) + { + skip("skipping rest of ObtainUserAgent tests, out of memory\n"); + } + else + { + saved = size; + hres = ObtainUserAgentString(0, str2, &size); + ok(hres == S_OK, "ObtainUserAgentString failed: %08x\n", hres); + ok(size == saved, "size=%d, expected %d\n", size, saved); + ok(strlen(expected) <= strlen(str2) && + !memcmp(expected, str2, strlen(expected)*sizeof(CHAR)), + "user agent was \"%s\", expected to start with \"%s\"\n", + str2, expected); + + size = saved+10; + hres = ObtainUserAgentString(0, str2, &size); + ok(hres == S_OK, "ObtainUserAgentString failed: %08x\n", hres); + ok(size == saved, "size=%d, expected %d\n", size, saved); + } + HeapFree(GetProcessHeap(), 0, str2); +} + +START_TEST(misc) +{ + OleInitialize(NULL); + + register_protocols(); + + test_CreateFormatEnum(); + test_RegisterFormatEnumerator(); + test_CoInternetParseUrl(); + test_CoInternetCompareUrl(); + test_FindMimeFromData(); + test_SecurityManager(); + test_ZoneManager(); + test_NameSpace(); + test_ReleaseBindInfo(); + test_UrlMkGetSessionOption(); + test_ObtainUserAgentString(); + + OleUninitialize(); +} diff --git a/rostests/winetests/urlmon/protocol.c b/rostests/winetests/urlmon/protocol.c new file mode 100644 index 00000000000..00335b379f8 --- /dev/null +++ b/rostests/winetests/urlmon/protocol.c @@ -0,0 +1,1546 @@ +/* + * Copyright 2005-2007 Jacek Caban 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 +#define CONST_VTABLE + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "ole2.h" +#include "urlmon.h" + +#include "initguid.h" + +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + expect_ ## func = FALSE; \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CHECK_NOT_CALLED(func) \ + do { \ + ok(!called_ ## func, "unexpected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CLEAR_CALLED(func) \ + expect_ ## func = called_ ## func = FALSE + +DEFINE_EXPECT(GetBindInfo); +DEFINE_EXPECT(ReportProgress_MIMETYPEAVAILABLE); +DEFINE_EXPECT(ReportProgress_DIRECTBIND); +DEFINE_EXPECT(ReportProgress_RAWMIMETYPE); +DEFINE_EXPECT(ReportProgress_FINDINGRESOURCE); +DEFINE_EXPECT(ReportProgress_CONNECTING); +DEFINE_EXPECT(ReportProgress_SENDINGREQUEST); +DEFINE_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE); +DEFINE_EXPECT(ReportProgress_VERIFIEDMIMETYPEAVAILABLE); +DEFINE_EXPECT(ReportProgress_PROTOCOLCLASSID); +DEFINE_EXPECT(ReportData); +DEFINE_EXPECT(ReportResult); +DEFINE_EXPECT(GetBindString_ACCEPT_MIMES); +DEFINE_EXPECT(GetBindString_USER_AGENT); +DEFINE_EXPECT(GetBindString_POST_COOKIE); +DEFINE_EXPECT(QueryService_HttpNegotiate); +DEFINE_EXPECT(QueryService_InternetProtocol); +DEFINE_EXPECT(BeginningTransaction); +DEFINE_EXPECT(GetRootSecurityId); +DEFINE_EXPECT(OnResponse); +DEFINE_EXPECT(Switch); +DEFINE_EXPECT(CreateInstance); +DEFINE_EXPECT(Start); +DEFINE_EXPECT(Terminate); +DEFINE_EXPECT(Read); +DEFINE_EXPECT(SetPriority); + +static const WCHAR wszIndexHtml[] = {'i','n','d','e','x','.','h','t','m','l',0}; +static const WCHAR index_url[] = + {'f','i','l','e',':','i','n','d','e','x','.','h','t','m','l',0}; + +static HRESULT expect_hrResult; +static LPCWSTR file_name, http_url, expect_wsz; +static IInternetProtocol *http_protocol = NULL; +static BOOL first_data_notif = FALSE, http_is_first = FALSE, + http_post_test = FALSE; +static int state = 0; +static DWORD bindf = 0; +static IInternetBindInfo *prot_bind_info; +static void *expect_pv; +static HANDLE event_complete; + +static enum { + FILE_TEST, + HTTP_TEST, + MK_TEST, + BIND_TEST +} tested_protocol; + +static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate2 *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) + || IsEqualGUID(&IID_IHttpNegotiate, riid) + || IsEqualGUID(&IID_IHttpNegotiate2, riid)) { + *ppv = iface; + return S_OK; + } + + ok(0, "unexpected call\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate2 *iface) +{ + return 2; +} + +static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate2 *iface) +{ + return 1; +} + +static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface, LPCWSTR szURL, + LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders) +{ + LPWSTR addl_headers; + + static const WCHAR wszHeaders[] = + {'C','o','n','t','e','n','t','-','T','y','p','e',':',' ','a','p','p','l','i','c','a','t', + 'i','o','n','/','x','-','w','w','w','-','f','o','r','m','-','u','r','l','e','n','c','o', + 'd','e','d','\r','\n',0}; + + CHECK_EXPECT(BeginningTransaction); + + ok(!lstrcmpW(szURL, http_url), "szURL != http_url\n"); + ok(!dwReserved, "dwReserved=%d, expected 0\n", dwReserved); + ok(pszAdditionalHeaders != NULL, "pszAdditionalHeaders == NULL\n"); + if(pszAdditionalHeaders) + { + ok(*pszAdditionalHeaders == NULL, "*pszAdditionalHeaders != NULL\n"); + if (http_post_test) + { + addl_headers = CoTaskMemAlloc(sizeof(wszHeaders)); + if (!addl_headers) + { + http_post_test = FALSE; + skip("Out of memory\n"); + return E_OUTOFMEMORY; + } + lstrcpyW(addl_headers, wszHeaders); + *pszAdditionalHeaders = addl_headers; + } + } + + return S_OK; +} + +static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode, + LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) +{ + CHECK_EXPECT(OnResponse); + + ok(dwResponseCode == 200, "dwResponseCode=%d, expected 200\n", dwResponseCode); + ok(szResponseHeaders != NULL, "szResponseHeaders == NULL\n"); + ok(szRequestHeaders == NULL, "szRequestHeaders != NULL\n"); + ok(pszAdditionalRequestHeaders == NULL, "pszAdditionalHeaders != NULL\n"); + + return S_OK; +} + +static HRESULT WINAPI HttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface, + BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved) +{ + static const BYTE sec_id[] = {'h','t','t','p',':','t','e','s','t',1,0,0,0}; + + CHECK_EXPECT(GetRootSecurityId); + + ok(!dwReserved, "dwReserved=%ld, expected 0\n", dwReserved); + ok(pbSecurityId != NULL, "pbSecurityId == NULL\n"); + ok(pcbSecurityId != NULL, "pcbSecurityId == NULL\n"); + + if(pcbSecurityId) { + ok(*pcbSecurityId == 512, "*pcbSecurityId=%d, expected 512\n", *pcbSecurityId); + *pcbSecurityId = sizeof(sec_id); + } + + if(pbSecurityId) + memcpy(pbSecurityId, sec_id, sizeof(sec_id)); + + return E_FAIL; +} + +static IHttpNegotiate2Vtbl HttpNegotiateVtbl = { + HttpNegotiate_QueryInterface, + HttpNegotiate_AddRef, + HttpNegotiate_Release, + HttpNegotiate_BeginningTransaction, + HttpNegotiate_OnResponse, + HttpNegotiate_GetRootSecurityId +}; + +static IHttpNegotiate2 http_negotiate = { &HttpNegotiateVtbl }; + +static HRESULT QueryInterface(REFIID,void**); + +static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv) +{ + return QueryInterface(riid, ppv); +} + +static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface) +{ + return 2; +} + +static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface) +{ + return 1; +} + +static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface, REFGUID guidService, + REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IHttpNegotiate, guidService) || IsEqualGUID(&IID_IHttpNegotiate2, riid)) { + CHECK_EXPECT2(QueryService_HttpNegotiate); + return IHttpNegotiate2_QueryInterface(&http_negotiate, riid, ppv); + } + + if(IsEqualGUID(&IID_IInternetProtocol, guidService)) { + ok(IsEqualGUID(&IID_IInternetProtocol, riid), "unexpected riid\n"); + CHECK_EXPECT(QueryService_InternetProtocol); + return E_NOINTERFACE; + } + + ok(0, "unexpected call\n"); + return E_FAIL; +} + +static const IServiceProviderVtbl ServiceProviderVtbl = { + ServiceProvider_QueryInterface, + ServiceProvider_AddRef, + ServiceProvider_Release, + ServiceProvider_QueryService +}; + +static IServiceProvider service_provider = { &ServiceProviderVtbl }; + +static HRESULT WINAPI ProtocolSink_QueryInterface(IInternetProtocolSink *iface, REFIID riid, void **ppv) +{ + return QueryInterface(riid, ppv); +} + +static ULONG WINAPI ProtocolSink_AddRef(IInternetProtocolSink *iface) +{ + return 2; +} + +static ULONG WINAPI ProtocolSink_Release(IInternetProtocolSink *iface) +{ + return 1; +} + +static HRESULT WINAPI ProtocolSink_Switch(IInternetProtocolSink *iface, PROTOCOLDATA *pProtocolData) +{ + HRESULT hres; + + CHECK_EXPECT(Switch); + ok(pProtocolData != NULL, "pProtocolData == NULL\n"); + if (!state) { + if (http_is_first) { + CHECK_CALLED(ReportProgress_FINDINGRESOURCE); + CHECK_CALLED(ReportProgress_CONNECTING); + } else todo_wine { + CHECK_NOT_CALLED(ReportProgress_FINDINGRESOURCE); + CHECK_NOT_CALLED(ReportProgress_CONNECTING); + } + CHECK_CALLED(ReportProgress_SENDINGREQUEST); + SET_EXPECT(OnResponse); + SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE); + } + + SET_EXPECT(ReportData); + hres = IInternetProtocol_Continue(http_protocol, pProtocolData); + ok(hres == S_OK, "Continue failed: %08x\n", hres); + CHECK_CALLED(ReportData); + + if (!state) { + state = 1; + CHECK_CALLED(OnResponse); + CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE); + } + + SetEvent(event_complete); + return S_OK; +} + +static HRESULT WINAPI ProtocolSink_ReportProgress(IInternetProtocolSink *iface, ULONG ulStatusCode, + LPCWSTR szStatusText) +{ + static const WCHAR null_guid[] = {'{','0','0','0','0','0','0','0','0','-','0','0','0','0','-', + '0','0','0','0','-','0','0','0','0','-','0','0','0','0','0','0','0','0','0','0','0','0','}',0}; + static const WCHAR text_html[] = {'t','e','x','t','/','h','t','m','l',0}; + static const WCHAR text_plain[] = {'t','e','x','t','/','p','l','a','i','n',0}; + static const WCHAR host[] = + {'w','w','w','.','w','i','n','e','h','q','.','o','r','g',0}; + static const WCHAR post_host[] = + {'c','r','o','s','s','o','v','e','r','.','c','o','d','e', + 'w','e','a','v','e','r','s','.','c','o','m',0}; + static const WCHAR wszWineHQIP[] = + {'2','0','9','.','4','6','.','2','5','.','1','3','4',0}; + static const WCHAR wszCrossoverIP[] = + {'2','0','9','.','4','6','.','2','5','.','1','3','2',0}; + /* I'm not sure if it's a good idea to hardcode here the IP address... */ + + switch(ulStatusCode) { + case BINDSTATUS_MIMETYPEAVAILABLE: + CHECK_EXPECT(ReportProgress_MIMETYPEAVAILABLE); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) { + if(tested_protocol == BIND_TEST) + ok(szStatusText == expect_wsz, "unexpected szStatusText\n"); + else if (http_post_test) + ok(lstrlenW(text_plain) <= lstrlenW(szStatusText) && + !memcmp(szStatusText, text_plain, lstrlenW(text_plain)*sizeof(WCHAR)), + "szStatusText != text/plain\n"); + else + ok(lstrlenW(text_html) <= lstrlenW(szStatusText) && + !memcmp(szStatusText, text_html, lstrlenW(text_html)*sizeof(WCHAR)), + "szStatusText != text/html\n"); + } + break; + case BINDSTATUS_DIRECTBIND: + CHECK_EXPECT2(ReportProgress_DIRECTBIND); + ok(szStatusText == NULL, "szStatusText != NULL\n"); + break; + case BINDSTATUS_RAWMIMETYPE: + CHECK_EXPECT2(ReportProgress_RAWMIMETYPE); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) + ok(lstrlenW(szStatusText) < lstrlenW(text_html) || + !memcmp(szStatusText, text_html, lstrlenW(text_html)*sizeof(WCHAR)), + "szStatusText != text/html\n"); + break; + case BINDSTATUS_CACHEFILENAMEAVAILABLE: + CHECK_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) { + if(tested_protocol == BIND_TEST) + ok(szStatusText == expect_wsz, "unexpected szStatusText\n"); + else + ok(!lstrcmpW(szStatusText, file_name), "szStatusText != file_name\n"); + } + break; + case BINDSTATUS_FINDINGRESOURCE: + CHECK_EXPECT(ReportProgress_FINDINGRESOURCE); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) + { + if (!http_post_test) + ok(!lstrcmpW(szStatusText, host), + "szStatustext != \"www.winehq.org\"\n"); + else + ok(!lstrcmpW(szStatusText, post_host), + "szStatustext != \"crossover.codeweavers.com\"\n"); + } + break; + case BINDSTATUS_CONNECTING: + CHECK_EXPECT(ReportProgress_CONNECTING); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) + ok(!lstrcmpW(szStatusText, http_post_test ? + wszCrossoverIP : wszWineHQIP), + "Unexpected szStatusText\n"); + break; + case BINDSTATUS_SENDINGREQUEST: + CHECK_EXPECT(ReportProgress_SENDINGREQUEST); + if(tested_protocol == FILE_TEST) { + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) + ok(!*szStatusText, "wrong szStatusText\n"); + }else { + ok(szStatusText == NULL, "szStatusText != NULL\n"); + } + break; + case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE: + CHECK_EXPECT(ReportProgress_VERIFIEDMIMETYPEAVAILABLE); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) + ok(!lstrcmpW(szStatusText, text_html), "szStatusText != text/html\n"); + break; + case BINDSTATUS_PROTOCOLCLASSID: + CHECK_EXPECT(ReportProgress_PROTOCOLCLASSID); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + ok(!lstrcmpW(szStatusText, null_guid), "unexpected szStatusText\n"); + break; + default: + ok(0, "Unexpected call %d\n", ulStatusCode); + }; + + return S_OK; +} + +static HRESULT WINAPI ProtocolSink_ReportData(IInternetProtocolSink *iface, DWORD grfBSCF, + ULONG ulProgress, ULONG ulProgressMax) +{ + if(tested_protocol == FILE_TEST) { + CHECK_EXPECT2(ReportData); + + ok(ulProgress == ulProgressMax, "ulProgress (%d) != ulProgressMax (%d)\n", + ulProgress, ulProgressMax); + ok(ulProgressMax == 13, "ulProgressMax=%d, expected 13\n", ulProgressMax); + ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION), + "grcfBSCF = %08x\n", grfBSCF); + }else if(tested_protocol == HTTP_TEST) { + if(!(grfBSCF & BSCF_LASTDATANOTIFICATION)) + CHECK_EXPECT(ReportData); + else if (http_post_test) + ok(ulProgress == 13, "Read %u bytes instead of 13\n", ulProgress); + + ok(ulProgress, "ulProgress == 0\n"); + + if(first_data_notif) { + ok(grfBSCF == BSCF_FIRSTDATANOTIFICATION, "grcfBSCF = %08x\n", grfBSCF); + first_data_notif = FALSE; + } else { + ok(grfBSCF == BSCF_INTERMEDIATEDATANOTIFICATION + || grfBSCF == (BSCF_LASTDATANOTIFICATION|BSCF_INTERMEDIATEDATANOTIFICATION), + "grcfBSCF = %08x\n", grfBSCF); + } + + if(!(bindf & BINDF_FROMURLMON) && + !(grfBSCF & BSCF_LASTDATANOTIFICATION)) { + if(!state) { + state = 1; + if(http_is_first) { + CHECK_CALLED(ReportProgress_FINDINGRESOURCE); + CHECK_CALLED(ReportProgress_CONNECTING); + } else todo_wine { + CHECK_NOT_CALLED(ReportProgress_FINDINGRESOURCE); + CHECK_NOT_CALLED(ReportProgress_CONNECTING); + } + CHECK_CALLED(ReportProgress_SENDINGREQUEST); + CHECK_CALLED(OnResponse); + CHECK_CALLED(ReportProgress_RAWMIMETYPE); + } + SetEvent(event_complete); + } + } + return S_OK; +} + +static HRESULT WINAPI ProtocolSink_ReportResult(IInternetProtocolSink *iface, HRESULT hrResult, + DWORD dwError, LPCWSTR szResult) +{ + CHECK_EXPECT(ReportResult); + + ok(hrResult == expect_hrResult, "hrResult = %08x, expected: %08x\n", + hrResult, expect_hrResult); + if(SUCCEEDED(hrResult)) + ok(dwError == ERROR_SUCCESS, "dwError = %d, expected ERROR_SUCCESS\n", dwError); + else + ok(dwError != ERROR_SUCCESS, "dwError == ERROR_SUCCESS\n"); + ok(!szResult, "szResult != NULL\n"); + + return S_OK; +} + +static IInternetProtocolSinkVtbl protocol_sink_vtbl = { + ProtocolSink_QueryInterface, + ProtocolSink_AddRef, + ProtocolSink_Release, + ProtocolSink_Switch, + ProtocolSink_ReportProgress, + ProtocolSink_ReportData, + ProtocolSink_ReportResult +}; + +static IInternetProtocolSink protocol_sink = { &protocol_sink_vtbl }; + +static HRESULT QueryInterface(REFIID riid, void **ppv) +{ + *ppv = NULL; + + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocolSink, riid)) + *ppv = &protocol_sink; + if(IsEqualGUID(&IID_IServiceProvider, riid)) + *ppv = &service_provider; + + if(*ppv) + return S_OK; + + return E_NOINTERFACE; +} + +static HRESULT WINAPI BindInfo_QueryInterface(IInternetBindInfo *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetBindInfo, riid)) { + *ppv = iface; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI BindInfo_AddRef(IInternetBindInfo *iface) +{ + return 2; +} + +static ULONG WINAPI BindInfo_Release(IInternetBindInfo *iface) +{ + return 1; +} + +static HRESULT WINAPI BindInfo_GetBindInfo(IInternetBindInfo *iface, DWORD *grfBINDF, BINDINFO *pbindinfo) +{ + DWORD cbSize; + + static const CHAR szPostData[] = "mode=Test"; + + CHECK_EXPECT(GetBindInfo); + + ok(grfBINDF != NULL, "grfBINDF == NULL\n"); + ok(pbindinfo != NULL, "pbindinfo == NULL\n"); + ok(pbindinfo->cbSize == sizeof(BINDINFO), "wrong size of pbindinfo: %d\n", pbindinfo->cbSize); + + *grfBINDF = bindf; + cbSize = pbindinfo->cbSize; + memset(pbindinfo, 0, cbSize); + pbindinfo->cbSize = cbSize; + + if (http_post_test) + { + /* Must be GMEM_FIXED, GMEM_MOVABLE does not work properly + * with urlmon on native (Win98 and WinXP) */ + U(pbindinfo->stgmedData).hGlobal = GlobalAlloc(GPTR, sizeof(szPostData)); + if (!U(pbindinfo->stgmedData).hGlobal) + { + http_post_test = FALSE; + skip("Out of memory\n"); + return E_OUTOFMEMORY; + } + lstrcpy((LPSTR)U(pbindinfo->stgmedData).hGlobal, szPostData); + pbindinfo->cbstgmedData = sizeof(szPostData)-1; + pbindinfo->dwBindVerb = BINDVERB_POST; + pbindinfo->stgmedData.tymed = TYMED_HGLOBAL; + } + + return S_OK; +} + +static HRESULT WINAPI BindInfo_GetBindString(IInternetBindInfo *iface, ULONG ulStringType, + LPOLESTR *ppwzStr, ULONG cEl, ULONG *pcElFetched) +{ + static const WCHAR acc_mime[] = {'*','/','*',0}; + static const WCHAR user_agent[] = {'W','i','n','e',0}; + + ok(ppwzStr != NULL, "ppwzStr == NULL\n"); + ok(pcElFetched != NULL, "pcElFetched == NULL\n"); + + switch(ulStringType) { + case BINDSTRING_ACCEPT_MIMES: + CHECK_EXPECT(GetBindString_ACCEPT_MIMES); + ok(cEl == 256, "cEl=%d, expected 256\n", cEl); + if(pcElFetched) { + ok(*pcElFetched == 256, "*pcElFetched=%d, expected 256\n", *pcElFetched); + *pcElFetched = 1; + } + if(ppwzStr) { + *ppwzStr = CoTaskMemAlloc(sizeof(acc_mime)); + memcpy(*ppwzStr, acc_mime, sizeof(acc_mime)); + } + return S_OK; + case BINDSTRING_USER_AGENT: + CHECK_EXPECT(GetBindString_USER_AGENT); + ok(cEl == 1, "cEl=%d, expected 1\n", cEl); + if(pcElFetched) { + ok(*pcElFetched == 0, "*pcElFetch=%d, expectd 0\n", *pcElFetched); + *pcElFetched = 1; + } + if(ppwzStr) { + *ppwzStr = CoTaskMemAlloc(sizeof(user_agent)); + memcpy(*ppwzStr, user_agent, sizeof(user_agent)); + } + return S_OK; + case BINDSTRING_POST_COOKIE: + CHECK_EXPECT(GetBindString_POST_COOKIE); + ok(cEl == 1, "cEl=%d, expected 1\n", cEl); + if(pcElFetched) + ok(*pcElFetched == 0, "*pcElFetch=%d, expectd 0\n", *pcElFetched); + return S_OK; + default: + ok(0, "unexpected call\n"); + } + + return E_NOTIMPL; +} + +static IInternetBindInfoVtbl bind_info_vtbl = { + BindInfo_QueryInterface, + BindInfo_AddRef, + BindInfo_Release, + BindInfo_GetBindInfo, + BindInfo_GetBindString +}; + +static IInternetBindInfo bind_info = { &bind_info_vtbl }; + +static HRESULT WINAPI InternetPriority_QueryInterface(IInternetPriority *iface, + REFIID riid, void **ppv) +{ + ok(0, "unexpected call\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI InternetPriority_AddRef(IInternetPriority *iface) +{ + return 2; +} + +static ULONG WINAPI InternetPriority_Release(IInternetPriority *iface) +{ + return 1; +} + +static HRESULT WINAPI InternetPriority_SetPriority(IInternetPriority *iface, LONG nPriority) +{ + CHECK_EXPECT(SetPriority); + ok(nPriority == 100, "nPriority=%d\n", nPriority); + return S_OK; +} + +static HRESULT WINAPI InternetPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + + +static const IInternetPriorityVtbl InternetPriorityVtbl = { + InternetPriority_QueryInterface, + InternetPriority_AddRef, + InternetPriority_Release, + InternetPriority_SetPriority, + InternetPriority_GetPriority +}; + +static IInternetPriority InternetPriority = { &InternetPriorityVtbl }; + +static HRESULT WINAPI Protocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocol, riid)) { + *ppv = iface; + return S_OK; + } + + if(IsEqualGUID(&IID_IInternetPriority, riid)) { + *ppv = &InternetPriority; + return S_OK; + } + + ok(0, "unexpected call\n"); + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Protocol_AddRef(IInternetProtocol *iface) +{ + return 2; +} + +static ULONG WINAPI Protocol_Release(IInternetProtocol *iface) +{ + return 1; +} + +static HRESULT WINAPI Protocol_Start(IInternetProtocol *iface, LPCWSTR szUrl, + IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, + DWORD grfPI, DWORD dwReserved) +{ + BINDINFO bindinfo, exp_bindinfo; + DWORD cbindf = 0; + HRESULT hres; + + static const WCHAR wszTextHtml[] = {'t','e','x','t','/','h','t','m','l',0}; + static const WCHAR empty_str[] = {0}; + + CHECK_EXPECT(Start); + + ok(pOIProtSink != NULL, "pOIProtSink == NULL\n"); + ok(pOIBindInfo != NULL, "pOIBindInfo == NULL\n"); + ok(!grfPI, "grfPI = %x\n", grfPI); + ok(!dwReserved, "dwReserved = %d\n", dwReserved); + + memset(&bindinfo, 0, sizeof(bindinfo)); + bindinfo.cbSize = sizeof(bindinfo); + memcpy(&exp_bindinfo, &bindinfo, sizeof(bindinfo)); + SET_EXPECT(GetBindInfo); + hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &cbindf, &bindinfo); + ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres); + CHECK_CALLED(GetBindInfo); + ok(cbindf == (bindf|BINDF_FROMURLMON), "bindf = %x, expected %x\n", + cbindf, (bindf|BINDF_FROMURLMON)); + ok(!memcmp(&exp_bindinfo, &bindinfo, sizeof(bindinfo)), "unexpected bindinfo\n"); + + SET_EXPECT(ReportProgress_SENDINGREQUEST); + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_SENDINGREQUEST, empty_str); + ok(hres == S_OK, "ReportProgress(BINDSTATUS_SENDINGREQUEST) failed: %08x\n", hres); + CHECK_CALLED(ReportProgress_SENDINGREQUEST); + + SET_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE); + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_CACHEFILENAMEAVAILABLE, expect_wsz = empty_str); + ok(hres == S_OK, "ReportProgress(BINDSTATUS_CACHEFILENAMEAVAILABLE) failed: %08x\n", hres); + CHECK_CALLED(ReportProgress_CACHEFILENAMEAVAILABLE); + + SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE); + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE, expect_wsz = wszTextHtml); + ok(hres == S_OK, + "ReportProgress(BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE) failed: %08x\n", hres); + CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE); + + hres = IInternetProtocolSink_ReportData(pOIProtSink, + BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION, 13, 13); + ok(hres == S_OK, "ReportData failed: %08x\n", hres); + + SET_EXPECT(ReportResult); + hres = IInternetProtocolSink_ReportResult(pOIProtSink, S_OK, 0, NULL); + ok(hres == S_OK, "ReportResult failed: %08x\n", hres); + CHECK_CALLED(ReportResult); + + return S_OK; +} + +static HRESULT WINAPI Protocol_Continue(IInternetProtocol *iface, + PROTOCOLDATA *pProtocolData) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Abort(IInternetProtocol *iface, HRESULT hrReason, + DWORD dwOptions) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Terminate(IInternetProtocol *iface, DWORD dwOptions) +{ + CHECK_EXPECT(Terminate); + ok(!dwOptions, "dwOptions=%d\n", dwOptions); + return S_OK; +} + +static HRESULT WINAPI Protocol_Suspend(IInternetProtocol *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Resume(IInternetProtocol *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Read(IInternetProtocol *iface, void *pv, + ULONG cb, ULONG *pcbRead) +{ + static DWORD read; + + CHECK_EXPECT(Read); + + ok(pv == expect_pv, "pv != expect_pv\n"); + ok(cb == 1000, "cb=%d\n", cb); + ok(pcbRead != NULL, "pcbRead == NULL\n"); + ok(!*pcbRead, "*pcbRead = %d\n", *pcbRead); + + if(read) + return S_FALSE; + + memset(pv, 'x', 100); + *pcbRead = read = 100; + return S_OK; +} + +static HRESULT WINAPI Protocol_Seek(IInternetProtocol *iface, + LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions) +{ + ok(0, "unexpected call\n"); + return S_OK; +} + +static HRESULT WINAPI Protocol_UnlockRequest(IInternetProtocol *iface) +{ + ok(0, "unexpected call\n"); + return S_OK; +} + +static const IInternetProtocolVtbl ProtocolVtbl = { + Protocol_QueryInterface, + Protocol_AddRef, + Protocol_Release, + Protocol_Start, + Protocol_Continue, + Protocol_Abort, + Protocol_Terminate, + Protocol_Suspend, + Protocol_Resume, + Protocol_Read, + Protocol_Seek, + Protocol_LockRequest, + Protocol_UnlockRequest +}; + +static IInternetProtocol Protocol = { &ProtocolVtbl }; + +static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) +{ + ok(0, "unexpected call\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) +{ + return 2; +} + +static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) +{ + return 1; +} + +static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *pOuter, + REFIID riid, void **ppv) +{ + CHECK_EXPECT(CreateInstance); + + ok(pOuter == (IUnknown*)prot_bind_info, "pOuter != protocol_unk\n"); + ok(IsEqualGUID(&IID_IUnknown, riid), "unexpected riid\n"); + ok(ppv != NULL, "ppv == NULL\n"); + + *ppv = &Protocol; + return S_OK; +} + +static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL dolock) +{ + ok(0, "unexpected call\n"); + return S_OK; +} + +static const IClassFactoryVtbl ClassFactoryVtbl = { + ClassFactory_QueryInterface, + ClassFactory_AddRef, + ClassFactory_Release, + ClassFactory_CreateInstance, + ClassFactory_LockServer +}; + +static IClassFactory ClassFactory = { &ClassFactoryVtbl }; + +static void test_priority(IInternetProtocol *protocol) +{ + IInternetPriority *priority; + LONG pr; + HRESULT hres; + + hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetPriority, + (void**)&priority); + ok(hres == S_OK, "QueryInterface(IID_IInternetPriority) failed: %08x\n", hres); + if(FAILED(hres)) + return; + + hres = IInternetPriority_GetPriority(priority, &pr); + ok(hres == S_OK, "GetPriority failed: %08x\n", hres); + ok(pr == 0, "pr=%d, expected 0\n", pr); + + hres = IInternetPriority_SetPriority(priority, 1); + ok(hres == S_OK, "SetPriority failed: %08x\n", hres); + + hres = IInternetPriority_GetPriority(priority, &pr); + ok(hres == S_OK, "GetPriority failed: %08x\n", hres); + ok(pr == 1, "pr=%d, expected 1\n", pr); + + IInternetPriority_Release(priority); +} + +static void file_protocol_start(IInternetProtocol *protocol, LPCWSTR url, BOOL is_first) +{ + HRESULT hres; + + SET_EXPECT(GetBindInfo); + if(!(bindf & BINDF_FROMURLMON)) + SET_EXPECT(ReportProgress_DIRECTBIND); + if(is_first) { + SET_EXPECT(ReportProgress_SENDINGREQUEST); + SET_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE); + if(bindf & BINDF_FROMURLMON) + SET_EXPECT(ReportProgress_VERIFIEDMIMETYPEAVAILABLE); + else + SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE); + } + SET_EXPECT(ReportData); + if(is_first) + SET_EXPECT(ReportResult); + + expect_hrResult = S_OK; + + hres = IInternetProtocol_Start(protocol, url, &protocol_sink, &bind_info, 0, 0); + ok(hres == S_OK, "Start failed: %08x\n", hres); + + CHECK_CALLED(GetBindInfo); + if(!(bindf & BINDF_FROMURLMON)) + CHECK_CALLED(ReportProgress_DIRECTBIND); + if(is_first) { + CHECK_CALLED(ReportProgress_SENDINGREQUEST); + CHECK_CALLED(ReportProgress_CACHEFILENAMEAVAILABLE); + if(bindf & BINDF_FROMURLMON) + CHECK_CALLED(ReportProgress_VERIFIEDMIMETYPEAVAILABLE); + else + CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE); + } + CHECK_CALLED(ReportData); + if(is_first) + CHECK_CALLED(ReportResult); +} + +static void test_file_protocol_url(LPCWSTR url) +{ + IInternetProtocolInfo *protocol_info; + IUnknown *unk; + IClassFactory *factory; + HRESULT hres; + + hres = CoGetClassObject(&CLSID_FileProtocol, CLSCTX_INPROC_SERVER, NULL, + &IID_IUnknown, (void**)&unk); + ok(hres == S_OK, "CoGetClassObject failed: %08x\n", hres); + if(!SUCCEEDED(hres)) + return; + + hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&protocol_info); + ok(hres == E_NOINTERFACE, + "Could not get IInternetProtocolInfo interface: %08x, expected E_NOINTERFACE\n", hres); + + hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&factory); + ok(hres == S_OK, "Could not get IClassFactory interface\n"); + if(SUCCEEDED(hres)) { + IInternetProtocol *protocol; + BYTE buf[512]; + ULONG cb; + hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol); + ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres); + + if(SUCCEEDED(hres)) { + file_protocol_start(protocol, url, TRUE); + hres = IInternetProtocol_Read(protocol, buf, 2, &cb); + ok(hres == S_OK, "Read failed: %08x\n", hres); + ok(cb == 2, "cb=%u expected 2\n", cb); + hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb); + ok(hres == S_FALSE, "Read failed: %08x\n", hres); + hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb); + ok(hres == S_FALSE, "Read failed: %08x expected S_FALSE\n", hres); + ok(cb == 0, "cb=%u expected 0\n", cb); + hres = IInternetProtocol_UnlockRequest(protocol); + ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres); + + file_protocol_start(protocol, url, FALSE); + hres = IInternetProtocol_Read(protocol, buf, 2, &cb); + ok(hres == S_FALSE, "Read failed: %08x\n", hres); + hres = IInternetProtocol_LockRequest(protocol, 0); + ok(hres == S_OK, "LockRequest failed: %08x\n", hres); + hres = IInternetProtocol_UnlockRequest(protocol); + ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres); + + IInternetProtocol_Release(protocol); + } + + hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol); + ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres); + + if(SUCCEEDED(hres)) { + file_protocol_start(protocol, url, TRUE); + hres = IInternetProtocol_LockRequest(protocol, 0); + ok(hres == S_OK, "LockRequest failed: %08x\n", hres); + hres = IInternetProtocol_Terminate(protocol, 0); + ok(hres == S_OK, "Terminate failed: %08x\n", hres); + hres = IInternetProtocol_Read(protocol, buf, 2, &cb); + ok(hres == S_OK, "Read failed: %08x\n\n", hres); + hres = IInternetProtocol_UnlockRequest(protocol); + ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres); + hres = IInternetProtocol_Read(protocol, buf, 2, &cb); + ok(hres == S_OK, "Read failed: %08x\n", hres); + hres = IInternetProtocol_Terminate(protocol, 0); + ok(hres == S_OK, "Terminate failed: %08x\n", hres); + + IInternetProtocol_Release(protocol); + } + + hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol); + ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres); + + if(SUCCEEDED(hres)) { + file_protocol_start(protocol, url, TRUE); + hres = IInternetProtocol_Terminate(protocol, 0); + ok(hres == S_OK, "Terminate failed: %08x\n", hres); + hres = IInternetProtocol_Read(protocol, buf, 2, &cb); + ok(hres == S_OK, "Read failed: %08x\n", hres); + ok(cb == 2, "cb=%u expected 2\n", cb); + + IInternetProtocol_Release(protocol); + } + + IClassFactory_Release(factory); + } + + IUnknown_Release(unk); +} + +static void test_file_protocol_fail(void) +{ + IInternetProtocol *protocol; + HRESULT hres; + + static const WCHAR index_url2[] = + {'f','i','l','e',':','/','/','i','n','d','e','x','.','h','t','m','l',0}; + + hres = CoCreateInstance(&CLSID_FileProtocol, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, + &IID_IInternetProtocol, (void**)&protocol); + ok(hres == S_OK, "CoCreateInstance failed: %08x\n", hres); + if(FAILED(hres)) + return; + + SET_EXPECT(GetBindInfo); + expect_hrResult = MK_E_SYNTAX; + hres = IInternetProtocol_Start(protocol, wszIndexHtml, &protocol_sink, &bind_info, 0, 0); + ok(hres == MK_E_SYNTAX, "Start failed: %08x, expected MK_E_SYNTAX\n", hres); + CHECK_CALLED(GetBindInfo); + + SET_EXPECT(GetBindInfo); + if(!(bindf & BINDF_FROMURLMON)) + SET_EXPECT(ReportProgress_DIRECTBIND); + SET_EXPECT(ReportProgress_SENDINGREQUEST); + SET_EXPECT(ReportResult); + expect_hrResult = INET_E_RESOURCE_NOT_FOUND; + hres = IInternetProtocol_Start(protocol, index_url, &protocol_sink, &bind_info, 0, 0); + ok(hres == INET_E_RESOURCE_NOT_FOUND, + "Start failed: %08x expected INET_E_RESOURCE_NOT_FOUND\n", hres); + CHECK_CALLED(GetBindInfo); + if(!(bindf & BINDF_FROMURLMON)) + CHECK_CALLED(ReportProgress_DIRECTBIND); + CHECK_CALLED(ReportProgress_SENDINGREQUEST); + CHECK_CALLED(ReportResult); + + IInternetProtocol_Release(protocol); + + hres = CoCreateInstance(&CLSID_FileProtocol, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, + &IID_IInternetProtocol, (void**)&protocol); + ok(hres == S_OK, "CoCreateInstance failed: %08x\n", hres); + if(FAILED(hres)) + return; + + SET_EXPECT(GetBindInfo); + if(!(bindf & BINDF_FROMURLMON)) + SET_EXPECT(ReportProgress_DIRECTBIND); + SET_EXPECT(ReportProgress_SENDINGREQUEST); + SET_EXPECT(ReportResult); + expect_hrResult = INET_E_RESOURCE_NOT_FOUND; + + hres = IInternetProtocol_Start(protocol, index_url2, &protocol_sink, &bind_info, 0, 0); + ok(hres == INET_E_RESOURCE_NOT_FOUND, + "Start failed: %08x, expected INET_E_RESOURCE_NOT_FOUND\n", hres); + CHECK_CALLED(GetBindInfo); + if(!(bindf & BINDF_FROMURLMON)) + CHECK_CALLED(ReportProgress_DIRECTBIND); + CHECK_CALLED(ReportProgress_SENDINGREQUEST); + CHECK_CALLED(ReportResult); + + IInternetProtocol_Release(protocol); +} + +static void test_file_protocol(void) { + WCHAR buf[MAX_PATH]; + DWORD size; + ULONG len; + HANDLE file; + + static const WCHAR wszFile[] = {'f','i','l','e',':',0}; + static const WCHAR wszFile2[] = {'f','i','l','e',':','/','/',0}; + static const WCHAR wszFile3[] = {'f','i','l','e',':','/','/','/',0}; + static const char html_doc[] = ""; + + trace("Testing file protocol...\n"); + tested_protocol = FILE_TEST; + + file = CreateFileW(wszIndexHtml, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n"); + if(file == INVALID_HANDLE_VALUE) + return; + WriteFile(file, html_doc, sizeof(html_doc)-1, &size, NULL); + CloseHandle(file); + + file_name = wszIndexHtml; + bindf = 0; + test_file_protocol_url(index_url); + bindf = BINDF_FROMURLMON; + test_file_protocol_url(index_url); + + memcpy(buf, wszFile, sizeof(wszFile)); + len = sizeof(wszFile)/sizeof(WCHAR)-1; + len += GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR)-len, buf+len); + buf[len++] = '\\'; + memcpy(buf+len, wszIndexHtml, sizeof(wszIndexHtml)); + + file_name = buf + sizeof(wszFile)/sizeof(WCHAR)-1; + bindf = 0; + test_file_protocol_url(buf); + bindf = BINDF_FROMURLMON; + test_file_protocol_url(buf); + + memcpy(buf, wszFile2, sizeof(wszFile2)); + len = sizeof(wszFile2)/sizeof(WCHAR)-1; + len += GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR)-len, buf+len); + buf[len++] = '\\'; + memcpy(buf+len, wszIndexHtml, sizeof(wszIndexHtml)); + + file_name = buf + sizeof(wszFile2)/sizeof(WCHAR)-1; + bindf = 0; + test_file_protocol_url(buf); + bindf = BINDF_FROMURLMON; + test_file_protocol_url(buf); + + memcpy(buf, wszFile3, sizeof(wszFile3)); + len = sizeof(wszFile3)/sizeof(WCHAR)-1; + len += GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR)-len, buf+len); + buf[len++] = '\\'; + memcpy(buf+len, wszIndexHtml, sizeof(wszIndexHtml)); + + file_name = buf + sizeof(wszFile3)/sizeof(WCHAR)-1; + bindf = 0; + test_file_protocol_url(buf); + bindf = BINDF_FROMURLMON; + test_file_protocol_url(buf); + + DeleteFileW(wszIndexHtml); + + bindf = 0; + test_file_protocol_fail(); + bindf = BINDF_FROMURLMON; + test_file_protocol_fail(); +} + +static BOOL http_protocol_start(LPCWSTR url, BOOL is_first) +{ + static BOOL got_user_agent = FALSE; + HRESULT hres; + + first_data_notif = TRUE; + state = 0; + + SET_EXPECT(GetBindInfo); + if (!(bindf & BINDF_FROMURLMON)) + SET_EXPECT(ReportProgress_DIRECTBIND); + SET_EXPECT(GetBindString_USER_AGENT); + SET_EXPECT(GetBindString_ACCEPT_MIMES); + SET_EXPECT(QueryService_HttpNegotiate); + SET_EXPECT(BeginningTransaction); + SET_EXPECT(GetRootSecurityId); + if (http_post_test) + SET_EXPECT(GetBindString_POST_COOKIE); + + hres = IInternetProtocol_Start(http_protocol, url, &protocol_sink, &bind_info, 0, 0); + ok(hres == S_OK, "Start failed: %08x\n", hres); + if(FAILED(hres)) + return FALSE; + + CHECK_CALLED(GetBindInfo); + if (!(bindf & BINDF_FROMURLMON)) + CHECK_CALLED(ReportProgress_DIRECTBIND); + if (!got_user_agent) + { + CHECK_CALLED(GetBindString_USER_AGENT); + got_user_agent = TRUE; + } + else todo_wine + { + /* user agent only retrieved once, even with different URLs */ + CHECK_NOT_CALLED(GetBindString_USER_AGENT); + } + CHECK_CALLED(GetBindString_ACCEPT_MIMES); + CHECK_CALLED(QueryService_HttpNegotiate); + CHECK_CALLED(BeginningTransaction); + /* GetRootSecurityId called on WinXP but not on Win98 */ + CLEAR_CALLED(GetRootSecurityId); + if (http_post_test) + CHECK_CALLED(GetBindString_POST_COOKIE); + + return TRUE; +} + +/* is_first refers to whether this is the first call to this function + * _for this url_ */ +static void test_http_protocol_url(LPCWSTR url, BOOL is_first) +{ + IInternetProtocolInfo *protocol_info; + IClassFactory *factory; + IUnknown *unk; + HRESULT hres; + + trace("Testing http protocol...\n"); + http_url = url; + http_is_first = is_first; + + hres = CoGetClassObject(&CLSID_HttpProtocol, CLSCTX_INPROC_SERVER, NULL, + &IID_IUnknown, (void**)&unk); + ok(hres == S_OK, "CoGetClassObject failed: %08x\n", hres); + if(!SUCCEEDED(hres)) + return; + + hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&protocol_info); + ok(hres == E_NOINTERFACE, + "Could not get IInternetProtocolInfo interface: %08x, expected E_NOINTERFACE\n", + hres); + + hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&factory); + ok(hres == S_OK, "Could not get IClassFactory interface\n"); + IUnknown_Release(unk); + if(FAILED(hres)) + return; + + hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, + (void**)&http_protocol); + ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres); + if(SUCCEEDED(hres)) { + BYTE buf[3600]; + DWORD cb; + int *called = (bindf & BINDF_FROMURLMON) ? &called_Switch : &called_ReportData; + + test_priority(http_protocol); + + SET_EXPECT(ReportProgress_FINDINGRESOURCE); + SET_EXPECT(ReportProgress_CONNECTING); + SET_EXPECT(ReportProgress_SENDINGREQUEST); + if(!(bindf & BINDF_FROMURLMON)) { + SET_EXPECT(OnResponse); + SET_EXPECT(ReportProgress_RAWMIMETYPE); + SET_EXPECT(ReportData); + } else { + SET_EXPECT(Switch); + } + + if(!http_protocol_start(url, is_first)) + return; + + SET_EXPECT(ReportResult); + expect_hrResult = S_OK; + + hres = IInternetProtocol_Read(http_protocol, buf, 1, &cb); + ok((!*called && hres == E_PENDING && cb==0) || + (*called && hres == S_OK && cb==1), "Read failed: %08x (%d bytes)\n", hres, cb); + + WaitForSingleObject(event_complete, INFINITE); + if(bindf & BINDF_FROMURLMON) + CHECK_CALLED(Switch); + else + CHECK_CALLED(ReportData); + + while(1) { + if(bindf & BINDF_FROMURLMON) + SET_EXPECT(Switch); + else + SET_EXPECT(ReportData); + hres = IInternetProtocol_Read(http_protocol, buf, sizeof(buf), &cb); + if(hres == E_PENDING) { + hres = IInternetProtocol_Read(http_protocol, buf, 1, &cb); + ok((!*called && hres == E_PENDING && cb==0) || + (*called && hres == S_OK && cb==1), "Read failed: %08x (%d bytes)\n", hres, cb); + WaitForSingleObject(event_complete, INFINITE); + if(bindf & BINDF_FROMURLMON) + CHECK_CALLED(Switch); + else + CHECK_CALLED(ReportData); + } else { + if(bindf & BINDF_FROMURLMON) + CHECK_NOT_CALLED(Switch); + else + CHECK_NOT_CALLED(ReportData); + if(cb == 0) break; + } + } + ok(hres == S_FALSE, "Read failed: %08x\n", hres); + CHECK_CALLED(ReportResult); + + hres = IInternetProtocol_LockRequest(http_protocol, 0); + ok(hres == S_OK, "LockRequest failed: %08x\n", hres); + + hres = IInternetProtocol_Read(http_protocol, buf, 1, &cb); + ok(hres == S_FALSE, "Read failed: %08x\n", hres); + + hres = IInternetProtocol_Terminate(http_protocol, 0); + ok(hres == S_OK, "Terminate failed: %08x\n", hres); + + /* This wait is to give the internet handles being freed in Terminate + * enough time to actually terminate in all cases. Internet handles + * terminate asynchronously and native reuses the main InternetOpen + * handle. The only case in which this seems to be necessary is on + * wine with native wininet and urlmon, resulting in the next time + * test_http_protocol_url being called the first data notification actually + * being an extra last data notification from the previous connection + * about once out of every ten times. */ + Sleep(100); + + hres = IInternetProtocol_UnlockRequest(http_protocol); + ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres); + + IInternetProtocol_Release(http_protocol); + } + + IClassFactory_Release(factory); +} + +static void test_http_protocol(void) +{ + static const WCHAR winehq_url[] = + {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.', + 'o','r','g','/','s','i','t','e','/','a','b','o','u','t',0}; + static const WCHAR posttest_url[] = + {'h','t','t','p',':','/','/','c','r','o','s','s','o','v','e','r','.', + 'c','o','d','e','w','e','a','v','e','r','s','.','c','o','m','/', + 'p','o','s','t','t','e','s','t','.','p','h','p',0}; + + tested_protocol = HTTP_TEST; + bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; + test_http_protocol_url(winehq_url, TRUE); + bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_FROMURLMON; + test_http_protocol_url(winehq_url, FALSE); + + http_post_test = TRUE; + /* Without this flag we get a ReportProgress_CACHEFILENAMEAVAILABLE + * notification with BINDVERB_POST */ + bindf |= BINDF_NOWRITECACHE; + test_http_protocol_url(posttest_url, TRUE); + http_post_test = FALSE; +} + +static void test_mk_protocol(void) +{ + IInternetProtocolInfo *protocol_info; + IInternetProtocol *protocol; + IClassFactory *factory; + IUnknown *unk; + HRESULT hres; + + static const WCHAR wrong_url1[] = {'t','e','s','t',':','@','M','S','I','T','S','t','o','r','e', + ':',':','/','t','e','s','t','.','h','t','m','l',0}; + static const WCHAR wrong_url2[] = {'m','k',':','/','t','e','s','t','.','h','t','m','l',0}; + + trace("Testing mk protocol...\n"); + tested_protocol = MK_TEST; + + hres = CoGetClassObject(&CLSID_MkProtocol, CLSCTX_INPROC_SERVER, NULL, + &IID_IUnknown, (void**)&unk); + ok(hres == S_OK, "CoGetClassObject failed: %08x\n", hres); + + hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&protocol_info); + ok(hres == E_NOINTERFACE, + "Could not get IInternetProtocolInfo interface: %08x, expected E_NOINTERFACE\n", + hres); + + hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&factory); + ok(hres == S_OK, "Could not get IClassFactory interface\n"); + IUnknown_Release(unk); + if(FAILED(hres)) + return; + + hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, + (void**)&protocol); + IClassFactory_Release(factory); + ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres); + + SET_EXPECT(GetBindInfo); + hres = IInternetProtocol_Start(protocol, wrong_url1, &protocol_sink, &bind_info, 0, 0); + ok(hres == MK_E_SYNTAX || hres == INET_E_INVALID_URL, + "Start failed: %08x, expected MK_E_SYNTAX or INET_E_INVALID_URL\n", hres); + CLEAR_CALLED(GetBindInfo); + + SET_EXPECT(GetBindInfo); + SET_EXPECT(ReportProgress_DIRECTBIND); + SET_EXPECT(ReportProgress_SENDINGREQUEST); + SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE); + SET_EXPECT(ReportResult); + expect_hrResult = INET_E_RESOURCE_NOT_FOUND; + + hres = IInternetProtocol_Start(protocol, wrong_url2, &protocol_sink, &bind_info, 0, 0); + ok(hres == INET_E_RESOURCE_NOT_FOUND, "Start failed: %08x, expected INET_E_RESOURCE_NOT_FOUND\n", hres); + + CHECK_CALLED(GetBindInfo); + CHECK_CALLED(ReportProgress_DIRECTBIND); + CHECK_CALLED(ReportProgress_SENDINGREQUEST); + CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE); + CHECK_CALLED(ReportResult); + + IInternetProtocol_Release(protocol); +} + +static void test_CreateBinding(void) +{ + IInternetProtocol *protocol; + IInternetPriority *priority; + IInternetSession *session; + LONG p; + BYTE buf[1000]; + DWORD read; + HRESULT hres; + + static const WCHAR test_url[] = + {'t','e','s','t',':','/','/','f','i','l','e','.','h','t','m','l',0}; + static const WCHAR wsz_test[] = {'t','e','s','t',0}; + + trace("Testing CreateBinding...\n"); + tested_protocol = BIND_TEST; + + hres = CoInternetGetSession(0, &session, 0); + ok(hres == S_OK, "CoInternetGetSession failed: %08x\n", hres); + + hres = IInternetSession_RegisterNameSpace(session, &ClassFactory, &IID_NULL, wsz_test, 0, NULL, 0); + ok(hres == S_OK, "RegisterNameSpace failed: %08x\n", hres); + + hres = IInternetSession_CreateBinding(session, NULL, test_url, NULL, NULL, &protocol, 0); + ok(hres == S_OK, "CreateBinding failed: %08x\n", hres); + ok(protocol != NULL, "protocol == NULL\n"); + + hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetBindInfo, (void**)&prot_bind_info); + ok(hres == S_OK, "QueryInterface(IID_IInternetBindInfo) failed: %08x\n", hres); + + hres = IInternetProtocol_Start(protocol, test_url, NULL, &bind_info, 0, 0); + ok(hres == E_INVALIDARG, "Start failed: %08x, expected E_INVALIDARG\n", hres); + hres = IInternetProtocol_Start(protocol, test_url, &protocol_sink, NULL, 0, 0); + ok(hres == E_INVALIDARG, "Start failed: %08x, expected E_INVALIDARG\n", hres); + hres = IInternetProtocol_Start(protocol, NULL, &protocol_sink, &bind_info, 0, 0); + ok(hres == E_INVALIDARG, "Start failed: %08x, expected E_INVALIDARG\n", hres); + + hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetPriority, (void**)&priority); + ok(hres == S_OK, "QueryInterface(IID_IInternetPriority) failed: %08x\n", hres); + + p = 0xdeadbeef; + hres = IInternetPriority_GetPriority(priority, &p); + ok(hres == S_OK, "GetPriority failed: %08x\n", hres); + ok(!p, "p=%d\n", p); + + hres = IInternetPriority_SetPriority(priority, 100); + ok(hres == S_OK, "SetPriority failed: %08x\n", hres); + + p = 0xdeadbeef; + hres = IInternetPriority_GetPriority(priority, &p); + ok(hres == S_OK, "GetPriority failed: %08x\n", hres); + ok(p == 100, "p=%d\n", p); + + SET_EXPECT(QueryService_InternetProtocol); + SET_EXPECT(CreateInstance); + SET_EXPECT(ReportProgress_PROTOCOLCLASSID); + SET_EXPECT(SetPriority); + SET_EXPECT(Start); + + expect_hrResult = S_OK; + hres = IInternetProtocol_Start(protocol, test_url, &protocol_sink, &bind_info, 0, 0); + ok(hres == S_OK, "Start failed: %08x\n", hres); + + CHECK_CALLED(QueryService_InternetProtocol); + CHECK_CALLED(CreateInstance); + CHECK_CALLED(ReportProgress_PROTOCOLCLASSID); + CHECK_CALLED(SetPriority); + CHECK_CALLED(Start); + + SET_EXPECT(Read); + read = 0xdeadbeef; + hres = IInternetProtocol_Read(protocol, expect_pv = buf, sizeof(buf), &read); + ok(hres == S_OK, "Read failed: %08x\n", hres); + ok(read == 100, "read = %d\n", read); + CHECK_CALLED(Read); + + SET_EXPECT(Read); + read = 0xdeadbeef; + hres = IInternetProtocol_Read(protocol, expect_pv = buf, sizeof(buf), &read); + ok(hres == S_FALSE, "Read failed: %08x\n", hres); + ok(!read, "read = %d\n", read); + CHECK_CALLED(Read); + + p = 0xdeadbeef; + hres = IInternetPriority_GetPriority(priority, &p); + ok(hres == S_OK, "GetPriority failed: %08x\n", hres); + ok(p == 100, "p=%d\n", p); + + hres = IInternetPriority_SetPriority(priority, 101); + ok(hres == S_OK, "SetPriority failed: %08x\n", hres); + + SET_EXPECT(Terminate); + hres = IInternetProtocol_Terminate(protocol, 0xdeadbeef); + ok(hres == S_OK, "Terminate failed: %08x\n", hres); + CHECK_CALLED(Terminate); + + IInternetPriority_Release(priority); + IInternetBindInfo_Release(prot_bind_info); + IInternetProtocol_Release(protocol); + IInternetSession_Release(session); +} + +START_TEST(protocol) +{ + OleInitialize(NULL); + + event_complete = CreateEvent(NULL, FALSE, FALSE, NULL); + + test_file_protocol(); + test_http_protocol(); + test_mk_protocol(); + test_CreateBinding(); + + CloseHandle(event_complete); + + OleUninitialize(); +} diff --git a/rostests/winetests/urlmon/stream.c b/rostests/winetests/urlmon/stream.c new file mode 100644 index 00000000000..12133f9e730 --- /dev/null +++ b/rostests/winetests/urlmon/stream.c @@ -0,0 +1,372 @@ +/* + * Copyright 2007 Robert Shearman 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 +#define CONST_VTABLE + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "ole2.h" +#include "urlmon.h" +#include "wininet.h" + +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + expect_ ## func = FALSE; \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CHECK_NOT_CALLED(func) \ + do { \ + ok(!called_ ## func, "unexpected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CLEAR_CALLED(func) \ + expect_ ## func = called_ ## func = FALSE + +DEFINE_EXPECT(QueryInterface_IServiceProvider); +DEFINE_EXPECT(OnStartBinding); +DEFINE_EXPECT(OnProgress_FINDINGRESOURCE); +DEFINE_EXPECT(OnProgress_CONNECTING); +DEFINE_EXPECT(OnProgress_SENDINGREQUEST); +DEFINE_EXPECT(OnProgress_MIMETYPEAVAILABLE); +DEFINE_EXPECT(OnProgress_BEGINDOWNLOADDATA); +DEFINE_EXPECT(OnProgress_DOWNLOADINGDATA); +DEFINE_EXPECT(OnProgress_ENDDOWNLOADDATA); +DEFINE_EXPECT(OnStopBinding); +DEFINE_EXPECT(OnDataAvailable); +DEFINE_EXPECT(GetBindInfo); + +static const WCHAR wszIndexHtml[] = {'i','n','d','e','x','.','h','t','m','l',0}; +static WCHAR INDEX_HTML[MAX_PATH]; +static const char szHtmlDoc[] = ""; + +static HRESULT WINAPI statusclb_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv) +{ + if (IsEqualGUID(&IID_IBindStatusCallback, riid) || + IsEqualGUID(&IID_IUnknown, riid)) + { + *ppv = iface; + return S_OK; + } + else if (IsEqualGUID(&IID_IServiceProvider, riid)) + { + CHECK_EXPECT(QueryInterface_IServiceProvider); + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI statusclb_AddRef(IBindStatusCallback *iface) +{ + return 2; +} + +static ULONG WINAPI statusclb_Release(IBindStatusCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI statusclb_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved, + IBinding *pib) +{ + HRESULT hres; + IMoniker *mon; + + CHECK_EXPECT(OnStartBinding); + + ok(pib != NULL, "pib should not be NULL\n"); + + hres = IBinding_QueryInterface(pib, &IID_IMoniker, (void**)&mon); + ok(hres == E_NOINTERFACE, "IBinding should not have IMoniker interface\n"); + if(SUCCEEDED(hres)) + IMoniker_Release(mon); + + return S_OK; +} + +static HRESULT WINAPI statusclb_GetPriority(IBindStatusCallback *iface, LONG *pnPriority) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI statusclb_OnLowResource(IBindStatusCallback *iface, DWORD reserved) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI statusclb_OnProgress(IBindStatusCallback *iface, ULONG ulProgress, + ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) +{ + switch(ulStatusCode) { + case BINDSTATUS_FINDINGRESOURCE: + CHECK_EXPECT(OnProgress_FINDINGRESOURCE); + break; + case BINDSTATUS_CONNECTING: + CHECK_EXPECT(OnProgress_CONNECTING); + break; + case BINDSTATUS_SENDINGREQUEST: + CHECK_EXPECT(OnProgress_SENDINGREQUEST); + break; + case BINDSTATUS_MIMETYPEAVAILABLE: + CHECK_EXPECT(OnProgress_MIMETYPEAVAILABLE); + break; + case BINDSTATUS_BEGINDOWNLOADDATA: + CHECK_EXPECT(OnProgress_BEGINDOWNLOADDATA); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + break; + case BINDSTATUS_DOWNLOADINGDATA: + CHECK_EXPECT2(OnProgress_DOWNLOADINGDATA); + break; + case BINDSTATUS_ENDDOWNLOADDATA: + CHECK_EXPECT(OnProgress_ENDDOWNLOADDATA); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + break; + case BINDSTATUS_CACHEFILENAMEAVAILABLE: + ok(szStatusText != NULL, "szStatusText == NULL\n"); + break; + default: + todo_wine { ok(0, "unexpexted code %d\n", ulStatusCode); } + }; + return S_OK; +} + +static HRESULT WINAPI statusclb_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError) +{ + CHECK_EXPECT(OnStopBinding); + + /* ignore DNS failure */ + if (hresult != HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED)) + { + ok(SUCCEEDED(hresult), "Download failed: %08x\n", hresult); + ok(szError == NULL, "szError should be NULL\n"); + } + + return S_OK; +} + +static HRESULT WINAPI statusclb_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo) +{ + DWORD cbSize; + + CHECK_EXPECT(GetBindInfo); + + *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; + cbSize = pbindinfo->cbSize; + memset(pbindinfo, 0, cbSize); + pbindinfo->cbSize = cbSize; + + return S_OK; +} + +static HRESULT WINAPI statusclb_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF, + DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed) +{ + HRESULT hres; + DWORD readed; + BYTE buf[512]; + + CHECK_EXPECT2(OnDataAvailable); + + if (0) + { + /* FIXME: Uncomment after removing BindToStorage hack. */ + ok(pformatetc != NULL, "pformatetx == NULL\n"); + if(pformatetc) { + ok(pformatetc->cfFormat == 0xc02d, "clipformat=%x\n", pformatetc->cfFormat); + ok(pformatetc->ptd == NULL, "ptd = %p\n", pformatetc->ptd); + ok(pformatetc->dwAspect == 1, "dwAspect=%u\n", pformatetc->dwAspect); + ok(pformatetc->lindex == -1, "lindex=%d\n", pformatetc->lindex); + ok(pformatetc->tymed == TYMED_ISTREAM, "tymed=%u\n", pformatetc->tymed); + } + + ok(pstgmed != NULL, "stgmeg == NULL\n"); + if(pstgmed) { + ok(pstgmed->tymed == TYMED_ISTREAM, "tymed=%u\n", pstgmed->tymed); + ok(U(*pstgmed).pstm != NULL, "pstm == NULL\n"); + ok(pstgmed->pUnkForRelease != NULL, "pUnkForRelease == NULL\n"); + } + } + + if(U(*pstgmed).pstm) { + do hres = IStream_Read(U(*pstgmed).pstm, buf, 512, &readed); + while(hres == S_OK); + ok(hres == S_FALSE || hres == E_PENDING, "IStream_Read returned %08x\n", hres); + } + + return S_OK; +} + +static HRESULT WINAPI statusclb_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = { + statusclb_QueryInterface, + statusclb_AddRef, + statusclb_Release, + statusclb_OnStartBinding, + statusclb_GetPriority, + statusclb_OnLowResource, + statusclb_OnProgress, + statusclb_OnStopBinding, + statusclb_GetBindInfo, + statusclb_OnDataAvailable, + statusclb_OnObjectAvailable +}; + +static IBindStatusCallback BindStatusCallback = { &BindStatusCallbackVtbl }; + +static void set_file_url(void) +{ + int len; + + static const WCHAR wszFile[] = {'f','i','l','e',':','/','/'}; + + memcpy(INDEX_HTML, wszFile, sizeof(wszFile)); + len = sizeof(wszFile)/sizeof(WCHAR); + INDEX_HTML[len++] = '/'; + len += GetCurrentDirectoryW(sizeof(INDEX_HTML)/sizeof(WCHAR)-len, INDEX_HTML+len); + INDEX_HTML[len++] = '\\'; + memcpy(INDEX_HTML+len, wszIndexHtml, sizeof(wszIndexHtml)); +} + +static void create_file(void) +{ + HANDLE file; + DWORD size; + + file = CreateFileW(wszIndexHtml, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n"); + if(file == INVALID_HANDLE_VALUE) + return; + + WriteFile(file, szHtmlDoc, sizeof(szHtmlDoc)-1, &size, NULL); + CloseHandle(file); + + set_file_url(); +} + +static void test_URLOpenBlockingStreamW(void) +{ + HRESULT hr; + IStream *pStream; + char buffer[256]; + + hr = URLOpenBlockingStreamW(NULL, NULL, &pStream, 0, &BindStatusCallback); + ok(hr == E_INVALIDARG, "URLOpenBlockingStreamW should have failed with E_INVALIDARG instead of 0x%08x\n", hr); + hr = URLOpenBlockingStreamW(NULL, INDEX_HTML, NULL, 0, &BindStatusCallback); + ok(hr == E_INVALIDARG, "URLOpenBlockingStreamW should have failed with E_INVALIDARG instead of 0x%08x\n", hr); + + SET_EXPECT(GetBindInfo); + SET_EXPECT(QueryInterface_IServiceProvider); + SET_EXPECT(OnStartBinding); + SET_EXPECT(OnProgress_SENDINGREQUEST); + SET_EXPECT(OnProgress_MIMETYPEAVAILABLE); + SET_EXPECT(OnProgress_BEGINDOWNLOADDATA); + SET_EXPECT(OnProgress_ENDDOWNLOADDATA); + SET_EXPECT(OnStopBinding); + + hr = URLOpenBlockingStreamW(NULL, INDEX_HTML, &pStream, 0, &BindStatusCallback); + ok(hr == S_OK, "URLOpenBlockingStreamW failed with error 0x%08x\n", hr); + + CHECK_CALLED(GetBindInfo); + todo_wine CHECK_CALLED(QueryInterface_IServiceProvider); + CHECK_CALLED(OnStartBinding); + CHECK_CALLED(OnProgress_SENDINGREQUEST); + CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE); + CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA); + CHECK_CALLED(OnProgress_ENDDOWNLOADDATA); + CHECK_CALLED(OnStopBinding); + + ok(pStream != NULL, "pStream is NULL\n"); + + hr = IStream_Read(pStream, buffer, sizeof(buffer), NULL); + ok(hr == S_OK, "IStream_Read failed with error 0x%08x\n", hr); + ok(!memcmp(buffer, szHtmlDoc, sizeof(szHtmlDoc)-1), "read data differs from file\n"); + + IStream_Release(pStream); +} + +static void test_URLOpenStreamW(void) +{ + HRESULT hr; + + hr = URLOpenStreamW(NULL, NULL, 0, &BindStatusCallback); + ok(hr == E_INVALIDARG, "URLOpenStreamW should have failed with E_INVALIDARG instead of 0x%08x\n", hr); + + SET_EXPECT(GetBindInfo); + SET_EXPECT(QueryInterface_IServiceProvider); + SET_EXPECT(OnStartBinding); + SET_EXPECT(OnProgress_SENDINGREQUEST); + SET_EXPECT(OnProgress_MIMETYPEAVAILABLE); + SET_EXPECT(OnProgress_BEGINDOWNLOADDATA); + SET_EXPECT(OnProgress_ENDDOWNLOADDATA); + SET_EXPECT(OnDataAvailable); + SET_EXPECT(OnStopBinding); + + hr = URLOpenStreamW(NULL, INDEX_HTML, 0, &BindStatusCallback); + ok(hr == S_OK, "URLOpenStreamW failed with error 0x%08x\n", hr); + + CHECK_CALLED(GetBindInfo); + todo_wine CHECK_CALLED(QueryInterface_IServiceProvider); + CHECK_CALLED(OnStartBinding); + CHECK_CALLED(OnProgress_SENDINGREQUEST); + CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE); + CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA); + CHECK_CALLED(OnProgress_ENDDOWNLOADDATA); + CHECK_CALLED(OnDataAvailable); + CHECK_CALLED(OnStopBinding); +} + +START_TEST(stream) +{ + create_file(); + test_URLOpenBlockingStreamW(); + test_URLOpenStreamW(); + DeleteFileW(wszIndexHtml); +} diff --git a/rostests/winetests/urlmon/testlist.c b/rostests/winetests/urlmon/testlist.c new file mode 100644 index 00000000000..76cace38ca4 --- /dev/null +++ b/rostests/winetests/urlmon/testlist.c @@ -0,0 +1,23 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_generated(void); +extern void func_misc(void); +extern void func_protocol(void); +extern void func_stream(void); +extern void func_url(void); + +const struct test winetest_testlist[] = +{ + { "generated", func_generated }, + { "misc", func_misc }, + { "protocol", func_protocol }, + { "stream", func_stream }, + { "url", func_url }, + { 0, 0 } +}; diff --git a/rostests/winetests/urlmon/url.c b/rostests/winetests/urlmon/url.c new file mode 100644 index 00000000000..cd8432d670d --- /dev/null +++ b/rostests/winetests/urlmon/url.c @@ -0,0 +1,1882 @@ +/* + * UrlMon URL tests + * + * Copyright 2004 Kevin Koltzau + * Copyright 2004-2007 Jacek Caban 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 +#include + +#define COBJMACROS +#define CONST_VTABLE + +#include "windef.h" +#include "winbase.h" +#include "urlmon.h" +#include "wininet.h" +#include "mshtml.h" + +#include "wine/test.h" + +#define DEFINE_EXPECT(func) \ + static BOOL expect_ ## func = FALSE, called_ ## func = FALSE + +#define SET_EXPECT(func) \ + expect_ ## func = TRUE + +#define CHECK_EXPECT(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + expect_ ## func = FALSE; \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_EXPECT2(func) \ + do { \ + ok(expect_ ##func, "unexpected call " #func "\n"); \ + called_ ## func = TRUE; \ + }while(0) + +#define CHECK_CALLED(func) \ + do { \ + ok(called_ ## func, "expected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CHECK_NOT_CALLED(func) \ + do { \ + ok(!called_ ## func, "unexpected " #func "\n"); \ + expect_ ## func = called_ ## func = FALSE; \ + }while(0) + +#define CLEAR_CALLED(func) \ + expect_ ## func = called_ ## func = FALSE + +DEFINE_EXPECT(QueryInterface_IServiceProvider); +DEFINE_EXPECT(QueryInterface_IHttpNegotiate); +DEFINE_EXPECT(QueryInterface_IBindStatusCallback); +DEFINE_EXPECT(QueryInterface_IBindStatusCallbackHolder); +DEFINE_EXPECT(QueryInterface_IInternetBindInfo); +DEFINE_EXPECT(QueryInterface_IAuthenticate); +DEFINE_EXPECT(QueryInterface_IInternetProtocol); +DEFINE_EXPECT(QueryService_IAuthenticate); +DEFINE_EXPECT(QueryService_IInternetProtocol); +DEFINE_EXPECT(QueryService_IInternetBindInfo); +DEFINE_EXPECT(BeginningTransaction); +DEFINE_EXPECT(OnResponse); +DEFINE_EXPECT(QueryInterface_IHttpNegotiate2); +DEFINE_EXPECT(GetRootSecurityId); +DEFINE_EXPECT(GetBindInfo); +DEFINE_EXPECT(OnStartBinding); +DEFINE_EXPECT(OnProgress_FINDINGRESOURCE); +DEFINE_EXPECT(OnProgress_CONNECTING); +DEFINE_EXPECT(OnProgress_SENDINGREQUEST); +DEFINE_EXPECT(OnProgress_MIMETYPEAVAILABLE); +DEFINE_EXPECT(OnProgress_BEGINDOWNLOADDATA); +DEFINE_EXPECT(OnProgress_DOWNLOADINGDATA); +DEFINE_EXPECT(OnProgress_ENDDOWNLOADDATA); +DEFINE_EXPECT(OnProgress_CLASSIDAVAILABLE); +DEFINE_EXPECT(OnProgress_BEGINSYNCOPERATION); +DEFINE_EXPECT(OnProgress_ENDSYNCOPERATION); +DEFINE_EXPECT(OnStopBinding); +DEFINE_EXPECT(OnDataAvailable); +DEFINE_EXPECT(OnObjectAvailable); +DEFINE_EXPECT(Start); +DEFINE_EXPECT(Read); +DEFINE_EXPECT(LockRequest); +DEFINE_EXPECT(Terminate); +DEFINE_EXPECT(UnlockRequest); +DEFINE_EXPECT(Continue); + +static const WCHAR TEST_URL_1[] = {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.','o','r','g','/','\0'}; +static const WCHAR TEST_PART_URL_1[] = {'/','t','e','s','t','/','\0'}; + +static const WCHAR WINE_ABOUT_URL[] = {'h','t','t','p',':','/','/','w','w','w','.','w','i','n','e','h','q','.', + 'o','r','g','/','s','i','t','e','/','a','b','o','u','t',0}; +static const WCHAR SHORT_RESPONSE_URL[] = + {'h','t','t','p',':','/','/','c','r','o','s','s','o','v','e','r','.', + 'c','o','d','e','w','e','a','v','e','r','s','.','c','o','m','/', + 'p','o','s','t','t','e','s','t','.','p','h','p',0}; +static const WCHAR ABOUT_BLANK[] = {'a','b','o','u','t',':','b','l','a','n','k',0}; +static WCHAR INDEX_HTML[MAX_PATH]; +static const WCHAR ITS_URL[] = + {'i','t','s',':','t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0}; +static const WCHAR MK_URL[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':', + 't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0}; + +static const WCHAR wszTextHtml[] = {'t','e','x','t','/','h','t','m','l',0}; + +static WCHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 }; + +static const WCHAR wszWineHQSite[] = + {'w','w','w','.','w','i','n','e','h','q','.','o','r','g',0}; +static const WCHAR wszWineHQIP[] = + {'2','0','9','.','3','2','.','1','4','1','.','3',0}; +static const WCHAR wszIndexHtml[] = {'i','n','d','e','x','.','h','t','m','l',0}; + +static BOOL stopped_binding = FALSE, emulate_protocol = FALSE, + data_available = FALSE, http_is_first = TRUE; +static DWORD read = 0, bindf = 0, prot_state = 0, thread_id; +static CHAR mime_type[512]; +static IInternetProtocolSink *protocol_sink = NULL; +static HANDLE complete_event, complete_event2; + +extern IID IID_IBindStatusCallbackHolder; + +static LPCWSTR urls[] = { + WINE_ABOUT_URL, + ABOUT_BLANK, + INDEX_HTML, + ITS_URL, + MK_URL +}; + +static enum { + HTTP_TEST, + ABOUT_TEST, + FILE_TEST, + ITS_TEST, + MK_TEST +} test_protocol; + +static enum { + BEFORE_DOWNLOAD, + DOWNLOADING, + END_DOWNLOAD +} download_state; + +static const char *debugstr_guid(REFIID riid) +{ + static char buf[50]; + + sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", + riid->Data1, riid->Data2, riid->Data3, riid->Data4[0], + riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4], + riid->Data4[5], riid->Data4[6], riid->Data4[7]); + + return buf; +} + +static void test_CreateURLMoniker(LPCWSTR url1, LPCWSTR url2) +{ + HRESULT hr; + IMoniker *mon1 = NULL; + IMoniker *mon2 = NULL; + + hr = CreateURLMoniker(NULL, url1, &mon1); + ok(SUCCEEDED(hr), "failed to create moniker: 0x%08x\n", hr); + if(SUCCEEDED(hr)) { + hr = CreateURLMoniker(mon1, url2, &mon2); + ok(SUCCEEDED(hr), "failed to create moniker: 0x%08x\n", hr); + } + if(mon1) IMoniker_Release(mon1); + if(mon2) IMoniker_Release(mon2); +} + +static void test_create(void) +{ + test_CreateURLMoniker(TEST_URL_1, TEST_PART_URL_1); +} + +static HRESULT WINAPI Protocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocol, riid)) { + *ppv = iface; + return S_OK; + } + + *ppv = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Protocol_AddRef(IInternetProtocol *iface) +{ + return 2; +} + +static ULONG WINAPI Protocol_Release(IInternetProtocol *iface) +{ + return 1; +} + +static DWORD WINAPI thread_proc(PVOID arg) +{ + PROTOCOLDATA protocoldata; + HRESULT hres; + + SET_EXPECT(OnProgress_FINDINGRESOURCE); + hres = IInternetProtocolSink_ReportProgress(protocol_sink, + BINDSTATUS_FINDINGRESOURCE, wszWineHQSite); + ok(hres == S_OK, "ReportProgress failed: %08x\n", hres); + WaitForSingleObject(complete_event, INFINITE); + CHECK_CALLED(OnProgress_FINDINGRESOURCE); + + SET_EXPECT(OnProgress_CONNECTING); + hres = IInternetProtocolSink_ReportProgress(protocol_sink, + BINDSTATUS_CONNECTING, wszWineHQIP); + ok(hres == S_OK, "ReportProgress failed: %08x\n", hres); + WaitForSingleObject(complete_event, INFINITE); + CHECK_CALLED(OnProgress_CONNECTING); + + SET_EXPECT(OnProgress_SENDINGREQUEST); + hres = IInternetProtocolSink_ReportProgress(protocol_sink, + BINDSTATUS_SENDINGREQUEST, NULL); + ok(hres == S_OK, "ReportProgress failed: %08x\n", hres); + WaitForSingleObject(complete_event, INFINITE); + CHECK_CALLED(OnProgress_SENDINGREQUEST); + + SET_EXPECT(Continue); + prot_state = 1; + hres = IInternetProtocolSink_Switch(protocol_sink, &protocoldata); + ok(hres == S_OK, "Switch failed: %08x\n", hres); + WaitForSingleObject(complete_event, INFINITE); + CHECK_CALLED(Continue); + CHECK_CALLED(Read); + CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE); + CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA); + CHECK_CALLED(LockRequest); + CHECK_CALLED(OnDataAvailable); + + SET_EXPECT(Continue); + prot_state = 2; + hres = IInternetProtocolSink_Switch(protocol_sink, &protocoldata); + ok(hres == S_OK, "Switch failed: %08x\n", hres); + WaitForSingleObject(complete_event, INFINITE); + CHECK_CALLED(Continue); + CHECK_CALLED(Read); + CHECK_CALLED(OnProgress_DOWNLOADINGDATA); + CHECK_CALLED(OnDataAvailable); + + SET_EXPECT(Continue); + prot_state = 2; + hres = IInternetProtocolSink_Switch(protocol_sink, &protocoldata); + ok(hres == S_OK, "Switch failed: %08x\n", hres); + WaitForSingleObject(complete_event, INFINITE); + CHECK_CALLED(Continue); + CHECK_CALLED(Read); + CHECK_CALLED(OnProgress_DOWNLOADINGDATA); + CHECK_CALLED(OnDataAvailable); + + SET_EXPECT(Continue); + prot_state = 3; + hres = IInternetProtocolSink_Switch(protocol_sink, &protocoldata); + ok(hres == S_OK, "Switch failed: %08x\n", hres); + WaitForSingleObject(complete_event, INFINITE); + CHECK_CALLED(Continue); + CHECK_CALLED(Read); + CHECK_CALLED(OnProgress_ENDDOWNLOADDATA); + CHECK_CALLED(OnDataAvailable); + CHECK_CALLED(OnStopBinding); + + SetEvent(complete_event2); + + return 0; +} + +static HRESULT WINAPI Protocol_Start(IInternetProtocol *iface, LPCWSTR szUrl, + IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, + DWORD grfPI, DWORD dwReserved) +{ + BINDINFO bindinfo, bi = {sizeof(bi), 0}; + DWORD bindf, bscf = BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION; + WCHAR null_char = 0; + HRESULT hres; + + CHECK_EXPECT(Start); + + read = 0; + + ok(szUrl && !lstrcmpW(szUrl, urls[test_protocol]), "wrong url\n"); + ok(pOIProtSink != NULL, "pOIProtSink == NULL\n"); + ok(pOIBindInfo != NULL, "pOIBindInfo == NULL\n"); + ok(grfPI == 0, "grfPI=%d, expected 0\n", grfPI); + ok(dwReserved == 0, "dwReserved=%d, expected 0\n", dwReserved); + + memset(&bindinfo, 0, sizeof(bindinfo)); + bindinfo.cbSize = sizeof(bindinfo); + hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &bindf, &bindinfo); + ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres); + + if(test_protocol == FILE_TEST || test_protocol == MK_TEST || test_protocol == HTTP_TEST) { + ok(bindf == (BINDF_ASYNCHRONOUS|BINDF_ASYNCSTORAGE|BINDF_PULLDATA + |BINDF_FROMURLMON), + "bindf=%08x\n", bindf); + }else { + ok(bindf == (BINDF_ASYNCHRONOUS|BINDF_ASYNCSTORAGE|BINDF_PULLDATA| + BINDF_FROMURLMON|BINDF_NEEDFILE), + "bindf=%08x\n", bindf); + } + + ok(!memcmp(&bindinfo, &bi, sizeof(bindinfo)), "wrong bindinfo\n"); + + switch(test_protocol) { + case MK_TEST: + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_DIRECTBIND, NULL); + ok(hres == S_OK, + "ReportProgress(BINDSTATUS_SENDINGREQUEST) failed: %08x\n", hres); + + case FILE_TEST: + case ITS_TEST: + SET_EXPECT(OnProgress_SENDINGREQUEST); + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_SENDINGREQUEST, &null_char); + ok(hres == S_OK, + "ReportProgress(BINDSTATUS_SENDINGREQUEST) failed: %08x\n", hres); + CHECK_CALLED(OnProgress_SENDINGREQUEST); + default: + break; + } + + if(test_protocol == HTTP_TEST) { + IServiceProvider *service_provider; + IHttpNegotiate *http_negotiate; + IHttpNegotiate2 *http_negotiate2; + LPWSTR ua = (LPWSTR)0xdeadbeef, accept_mimes[256]; + LPWSTR additional_headers = (LPWSTR)0xdeadbeef; + BYTE sec_id[100]; + DWORD fetched = 256, size = 100; + + static const WCHAR wszMimes[] = {'*','/','*',0}; + + SET_EXPECT(QueryInterface_IInternetBindInfo); + SET_EXPECT(QueryService_IInternetBindInfo); + hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, + &ua, 1, &fetched); + todo_wine { + CHECK_CALLED(QueryInterface_IInternetBindInfo); + CHECK_CALLED(QueryService_IInternetBindInfo); + } + ok(hres == E_NOINTERFACE, + "GetBindString(BINDSTRING_USER_AGETNT) failed: %08x\n", hres); + ok(fetched == 256, "fetched = %d, expected 254\n", fetched); + ok(ua == (LPWSTR)0xdeadbeef, "ua = %p\n", ua); + + hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES, + accept_mimes, 256, &fetched); + ok(hres == S_OK, + "GetBindString(BINDSTRING_ACCEPT_MIMES) failed: %08x\n", hres); + ok(fetched == 1, "fetched = %d, expected 1\n", fetched); + ok(!lstrcmpW(wszMimes, accept_mimes[0]), "unexpected mimes\n"); + + hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES, + NULL, 256, &fetched); + ok(hres == E_INVALIDARG, + "GetBindString(BINDSTRING_ACCEPT_MIMES) failed: %08x\n", hres); + + hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES, + accept_mimes, 256, NULL); + ok(hres == E_INVALIDARG, + "GetBindString(BINDSTRING_ACCEPT_MIMES) failed: %08x\n", hres); + + hres = IInternetBindInfo_QueryInterface(pOIBindInfo, &IID_IServiceProvider, + (void**)&service_provider); + ok(hres == S_OK, "QueryInterface failed: %08x\n", hres); + + SET_EXPECT(QueryInterface_IHttpNegotiate); + hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, + &IID_IHttpNegotiate, (void**)&http_negotiate); + CHECK_CALLED(QueryInterface_IHttpNegotiate); + ok(hres == S_OK, "QueryService failed: %08x\n", hres); + + SET_EXPECT(BeginningTransaction); + hres = IHttpNegotiate_BeginningTransaction(http_negotiate, urls[test_protocol], + NULL, 0, &additional_headers); + CHECK_CALLED(BeginningTransaction); + IHttpNegotiate_Release(http_negotiate); + ok(hres == S_OK, "BeginningTransction failed: %08x\n", hres); + ok(additional_headers == NULL, "additional_headers=%p\n", additional_headers); + + SET_EXPECT(QueryInterface_IHttpNegotiate2); + hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2, + &IID_IHttpNegotiate2, (void**)&http_negotiate2); + CHECK_CALLED(QueryInterface_IHttpNegotiate2); + ok(hres == S_OK, "QueryService failed: %08x\n", hres); + + size = 512; + SET_EXPECT(GetRootSecurityId); + hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, sec_id, &size, 0); + CHECK_CALLED(GetRootSecurityId); + IHttpNegotiate2_Release(http_negotiate2); + ok(hres == E_FAIL, "GetRootSecurityId failed: %08x, expected E_FAIL\n", hres); + ok(size == 13, "size=%d\n", size); + + IServiceProvider_Release(service_provider); + + IInternetProtocolSink_AddRef(pOIProtSink); + protocol_sink = pOIProtSink; + CreateThread(NULL, 0, thread_proc, NULL, 0, NULL); + + return S_OK; + } + + if(test_protocol == FILE_TEST) { + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_CACHEFILENAMEAVAILABLE, &null_char); + ok(hres == S_OK, + "ReportProgress(BINDSTATUS_CACHEFILENAMEAVAILABLE) failed: %08x\n", hres); + + SET_EXPECT(OnProgress_MIMETYPEAVAILABLE); + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE, wszTextHtml); + ok(hres == S_OK, + "ReportProgress(BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE) failed: %08x\n", hres); + CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE); + }else { + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, + BINDSTATUS_MIMETYPEAVAILABLE, wszTextHtml); + ok(hres == S_OK, + "ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE) failed: %08x\n", hres); + } + + if(test_protocol == ABOUT_TEST) + bscf |= BSCF_DATAFULLYAVAILABLE; + if(test_protocol == ITS_TEST) + bscf = BSCF_FIRSTDATANOTIFICATION|BSCF_DATAFULLYAVAILABLE; + + SET_EXPECT(Read); + if(test_protocol != FILE_TEST && test_protocol != MK_TEST) + SET_EXPECT(OnProgress_MIMETYPEAVAILABLE); + SET_EXPECT(OnProgress_BEGINDOWNLOADDATA); + SET_EXPECT(OnProgress_ENDDOWNLOADDATA); + SET_EXPECT(LockRequest); + SET_EXPECT(OnDataAvailable); + SET_EXPECT(OnStopBinding); + + hres = IInternetProtocolSink_ReportData(pOIProtSink, bscf, 13, 13); + ok(hres == S_OK, "ReportData failed: %08x\n", hres); + + CHECK_CALLED(Read); + if(test_protocol != FILE_TEST && test_protocol != MK_TEST) + CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE); + CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA); + CHECK_CALLED(OnProgress_ENDDOWNLOADDATA); + CHECK_CALLED(LockRequest); + CHECK_CALLED(OnDataAvailable); + CHECK_CALLED(OnStopBinding); + + if(test_protocol == ITS_TEST) { + SET_EXPECT(Read); + hres = IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_BEGINDOWNLOADDATA, NULL); + ok(hres == S_OK, "ReportProgress(BINDSTATUS_BEGINDOWNLOADDATA) failed: %08x\n", hres); + CHECK_CALLED(Read); + } + + SET_EXPECT(Terminate); + hres = IInternetProtocolSink_ReportResult(pOIProtSink, S_OK, 0, NULL); + ok(hres == S_OK, "ReportResult failed: %08x\n", hres); + CHECK_CALLED(Terminate); + + return S_OK; +} + +static HRESULT WINAPI Protocol_Continue(IInternetProtocol *iface, + PROTOCOLDATA *pProtocolData) +{ + DWORD bscf = 0; + HRESULT hres; + + CHECK_EXPECT(Continue); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + ok(pProtocolData != NULL, "pProtocolData == NULL\n"); + if(!pProtocolData) + return S_OK; + + switch(prot_state) { + case 1: { + IServiceProvider *service_provider; + IHttpNegotiate *http_negotiate; + static WCHAR header[] = {'?',0}; + + hres = IInternetProtocolSink_QueryInterface(protocol_sink, &IID_IServiceProvider, + (void**)&service_provider); + ok(hres == S_OK, "Could not get IServiceProvicder\n"); + + hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, + &IID_IHttpNegotiate, (void**)&http_negotiate); + ok(hres == S_OK, "Could not get IHttpNegotiate\n"); + + SET_EXPECT(OnResponse); + hres = IHttpNegotiate_OnResponse(http_negotiate, 200, header, NULL, NULL); + CHECK_CALLED(OnResponse); + IHttpNegotiate_Release(http_negotiate); + ok(hres == S_OK, "OnResponse failed: %08x\n", hres); + + hres = IInternetProtocolSink_ReportProgress(protocol_sink, + BINDSTATUS_MIMETYPEAVAILABLE, wszTextHtml); + ok(hres == S_OK, + "ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE) failed: %08x\n", hres); + + bscf |= BSCF_FIRSTDATANOTIFICATION; + break; + } + case 2: + case 3: + bscf = BSCF_INTERMEDIATEDATANOTIFICATION; + break; + } + + hres = IInternetProtocolSink_ReportData(protocol_sink, bscf, 100, 400); + ok(hres == S_OK, "ReportData failed: %08x\n", hres); + + SET_EXPECT(Read); + switch(prot_state) { + case 1: + SET_EXPECT(OnProgress_MIMETYPEAVAILABLE); + SET_EXPECT(OnProgress_BEGINDOWNLOADDATA); + SET_EXPECT(LockRequest); + break; + case 2: + SET_EXPECT(OnProgress_DOWNLOADINGDATA); + break; + case 3: + SET_EXPECT(OnProgress_DOWNLOADINGDATA); + SET_EXPECT(OnProgress_ENDDOWNLOADDATA); + } + SET_EXPECT(OnDataAvailable); + if(prot_state == 3) + SET_EXPECT(OnStopBinding); + + return S_OK; +} + +static HRESULT WINAPI Protocol_Abort(IInternetProtocol *iface, HRESULT hrReason, + DWORD dwOptions) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Terminate(IInternetProtocol *iface, DWORD dwOptions) +{ + CHECK_EXPECT(Terminate); + + ok(dwOptions == 0, "dwOptions=%d, expected 0\n", dwOptions); + + if(protocol_sink) { + IInternetProtocolSink_Release(protocol_sink); + protocol_sink = NULL; + } + + return S_OK; +} + +static HRESULT WINAPI Protocol_Suspend(IInternetProtocol *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Resume(IInternetProtocol *iface) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_Read(IInternetProtocol *iface, void *pv, + ULONG cb, ULONG *pcbRead) +{ + static const char data[] = ""; + + CHECK_EXPECT2(Read); + + if(test_protocol == HTTP_TEST) { + HRESULT hres; + + static BOOL pending = TRUE; + + pending = !pending; + + switch(prot_state) { + case 1: + case 2: + if(pending) { + *pcbRead = 10; + memset(pv, '?', 10); + return E_PENDING; + }else { + memset(pv, '?', cb); + *pcbRead = cb; + read++; + return S_OK; + } + case 3: + prot_state++; + + *pcbRead = 0; + + hres = IInternetProtocolSink_ReportData(protocol_sink, + BSCF_LASTDATANOTIFICATION|BSCF_INTERMEDIATEDATANOTIFICATION, 2000, 2000); + ok(hres == S_OK, "ReportData failed: %08x\n", hres); + + hres = IInternetProtocolSink_ReportResult(protocol_sink, S_OK, 0, NULL); + ok(hres == S_OK, "ReportResult failed: %08x\n", hres); + + return S_FALSE; + case 4: + *pcbRead = 0; + return S_FALSE; + } + } + + if(read) { + *pcbRead = 0; + return S_FALSE; + } + + ok(pv != NULL, "pv == NULL\n"); + ok(cb != 0, "cb == 0\n"); + ok(pcbRead != NULL, "pcbRead == NULL\n"); + if(pcbRead) { + ok(*pcbRead == 0, "*pcbRead=%d, expected 0\n", *pcbRead); + read += *pcbRead = sizeof(data)-1; + } + if(pv) + memcpy(pv, data, sizeof(data)); + + return S_OK; +} + +static HRESULT WINAPI Protocol_Seek(IInternetProtocol *iface, + LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Protocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions) +{ + CHECK_EXPECT(LockRequest); + return S_OK; +} + +static HRESULT WINAPI Protocol_UnlockRequest(IInternetProtocol *iface) +{ + CHECK_EXPECT(UnlockRequest); + return S_OK; +} + +static const IInternetProtocolVtbl ProtocolVtbl = { + Protocol_QueryInterface, + Protocol_AddRef, + Protocol_Release, + Protocol_Start, + Protocol_Continue, + Protocol_Abort, + Protocol_Terminate, + Protocol_Suspend, + Protocol_Resume, + Protocol_Read, + Protocol_Seek, + Protocol_LockRequest, + Protocol_UnlockRequest +}; + +static IInternetProtocol Protocol = { &ProtocolVtbl }; + +static HRESULT WINAPI HttpNegotiate_QueryInterface(IHttpNegotiate2 *iface, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) + || IsEqualGUID(&IID_IHttpNegotiate, riid) + || IsEqualGUID(&IID_IHttpNegotiate2, riid)) { + *ppv = iface; + return S_OK; + } + + ok(0, "unexpected call\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI HttpNegotiate_AddRef(IHttpNegotiate2 *iface) +{ + return 2; +} + +static ULONG WINAPI HttpNegotiate_Release(IHttpNegotiate2 *iface) +{ + return 1; +} + +static HRESULT WINAPI HttpNegotiate_BeginningTransaction(IHttpNegotiate2 *iface, LPCWSTR szURL, + LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders) +{ + CHECK_EXPECT(BeginningTransaction); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + ok(!lstrcmpW(szURL, urls[test_protocol]), "szURL != urls[test_protocol]\n"); + ok(!dwReserved, "dwReserved=%d, expected 0\n", dwReserved); + ok(pszAdditionalHeaders != NULL, "pszAdditionalHeaders == NULL\n"); + if(pszAdditionalHeaders) + ok(*pszAdditionalHeaders == NULL, "*pszAdditionalHeaders != NULL\n"); + + return S_OK; +} + +static HRESULT WINAPI HttpNegotiate_OnResponse(IHttpNegotiate2 *iface, DWORD dwResponseCode, + LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) +{ + CHECK_EXPECT(OnResponse); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + ok(dwResponseCode == 200, "dwResponseCode=%d, expected 200\n", dwResponseCode); + ok(szResponseHeaders != NULL, "szResponseHeaders == NULL\n"); + ok(szRequestHeaders == NULL, "szRequestHeaders != NULL\n"); + /* Note: in protocol.c tests, OnResponse pszAdditionalRequestHeaders _is_ NULL */ + ok(pszAdditionalRequestHeaders != NULL, "pszAdditionalHeaders == NULL\n"); + if(pszAdditionalRequestHeaders) + ok(*pszAdditionalRequestHeaders == NULL, "*pszAdditionalHeaders != NULL\n"); + + return S_OK; +} + +static HRESULT WINAPI HttpNegotiate_GetRootSecurityId(IHttpNegotiate2 *iface, + BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved) +{ + static const BYTE sec_id[] = {'h','t','t','p',':','t','e','s','t',1,0,0,0}; + + CHECK_EXPECT(GetRootSecurityId); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + ok(!dwReserved, "dwReserved=%ld, expected 0\n", dwReserved); + ok(pbSecurityId != NULL, "pbSecurityId == NULL\n"); + ok(pcbSecurityId != NULL, "pcbSecurityId == NULL\n"); + + if(pbSecurityId == (void*)0xdeadbeef) + return E_NOTIMPL; + + if(pcbSecurityId) { + ok(*pcbSecurityId == 512, "*pcbSecurityId=%d, expected 512\n", *pcbSecurityId); + *pcbSecurityId = sizeof(sec_id); + } + + if(pbSecurityId) + memcpy(pbSecurityId, sec_id, sizeof(sec_id)); + + return E_FAIL; +} + +static IHttpNegotiate2Vtbl HttpNegotiateVtbl = { + HttpNegotiate_QueryInterface, + HttpNegotiate_AddRef, + HttpNegotiate_Release, + HttpNegotiate_BeginningTransaction, + HttpNegotiate_OnResponse, + HttpNegotiate_GetRootSecurityId +}; + +static IHttpNegotiate2 HttpNegotiate = { &HttpNegotiateVtbl }; + +static HRESULT WINAPI ServiceProvider_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv) +{ + ok(0, "unexpected call\n"); + return E_NOINTERFACE; +} + +static ULONG WINAPI ServiceProvider_AddRef(IServiceProvider *iface) +{ + return 2; +} + +static ULONG WINAPI ServiceProvider_Release(IServiceProvider *iface) +{ + return 1; +} + +static HRESULT WINAPI ServiceProvider_QueryService(IServiceProvider *iface, + REFGUID guidService, REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IAuthenticate, guidService)) { + CHECK_EXPECT(QueryService_IAuthenticate); + return E_NOTIMPL; + } + + if(IsEqualGUID(&IID_IInternetProtocol, guidService)) { + CHECK_EXPECT2(QueryService_IInternetProtocol); + return E_NOTIMPL; + } + + if(IsEqualGUID(&IID_IInternetBindInfo, guidService)) { + CHECK_EXPECT(QueryService_IInternetBindInfo); + return E_NOTIMPL; + } + + ok(0, "unexpected service %s\n", debugstr_guid(guidService)); + return E_NOINTERFACE; +} + +static IServiceProviderVtbl ServiceProviderVtbl = { + ServiceProvider_QueryInterface, + ServiceProvider_AddRef, + ServiceProvider_Release, + ServiceProvider_QueryService +}; + +static IServiceProvider ServiceProvider = { &ServiceProviderVtbl }; + +static HRESULT WINAPI statusclb_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv) +{ + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + if(IsEqualGUID(&IID_IInternetProtocol, riid)) { + CHECK_EXPECT2(QueryInterface_IInternetProtocol); + if(emulate_protocol) { + *ppv = &Protocol; + return S_OK; + }else { + return E_NOINTERFACE; + } + } + else if (IsEqualGUID(&IID_IServiceProvider, riid)) + { + CHECK_EXPECT2(QueryInterface_IServiceProvider); + *ppv = &ServiceProvider; + return S_OK; + } + else if (IsEqualGUID(&IID_IHttpNegotiate, riid)) + { + CHECK_EXPECT(QueryInterface_IHttpNegotiate); + *ppv = &HttpNegotiate; + return S_OK; + } + else if (IsEqualGUID(&IID_IHttpNegotiate2, riid)) + { + CHECK_EXPECT(QueryInterface_IHttpNegotiate2); + *ppv = &HttpNegotiate; + return S_OK; + } + else if (IsEqualGUID(&IID_IAuthenticate, riid)) + { + CHECK_EXPECT(QueryInterface_IAuthenticate); + return E_NOINTERFACE; + } + else if(IsEqualGUID(&IID_IBindStatusCallback, riid)) + { + CHECK_EXPECT2(QueryInterface_IBindStatusCallback); + *ppv = iface; + return S_OK; + } + else if(IsEqualGUID(&IID_IBindStatusCallbackHolder, riid)) + { + CHECK_EXPECT2(QueryInterface_IBindStatusCallbackHolder); + return E_NOINTERFACE; + } + else if(IsEqualGUID(&IID_IInternetBindInfo, riid)) + { + /* TODO */ + CHECK_EXPECT2(QueryInterface_IInternetBindInfo); + } + else + { + ok(0, "unexpected interface %s\n", debugstr_guid(riid)); + } + + return E_NOINTERFACE; +} + +static ULONG WINAPI statusclb_AddRef(IBindStatusCallback *iface) +{ + return 2; +} + +static ULONG WINAPI statusclb_Release(IBindStatusCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI statusclb_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved, + IBinding *pib) +{ + HRESULT hres; + IMoniker *mon; + + CHECK_EXPECT(OnStartBinding); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + ok(pib != NULL, "pib should not be NULL\n"); + ok(dwReserved == 0xff, "dwReserved=%x\n", dwReserved); + + if(pib == (void*)0xdeadbeef) + return S_OK; + + hres = IBinding_QueryInterface(pib, &IID_IMoniker, (void**)&mon); + ok(hres == E_NOINTERFACE, "IBinding should not have IMoniker interface\n"); + if(SUCCEEDED(hres)) + IMoniker_Release(mon); + + return S_OK; +} + +static HRESULT WINAPI statusclb_GetPriority(IBindStatusCallback *iface, LONG *pnPriority) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI statusclb_OnLowResource(IBindStatusCallback *iface, DWORD reserved) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI statusclb_OnProgress(IBindStatusCallback *iface, ULONG ulProgress, + ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) +{ + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + switch(ulStatusCode) { + case BINDSTATUS_FINDINGRESOURCE: + CHECK_EXPECT(OnProgress_FINDINGRESOURCE); + if((bindf & BINDF_ASYNCHRONOUS) && emulate_protocol) + SetEvent(complete_event); + break; + case BINDSTATUS_CONNECTING: + CHECK_EXPECT(OnProgress_CONNECTING); + if((bindf & BINDF_ASYNCHRONOUS) && emulate_protocol) + SetEvent(complete_event); + break; + case BINDSTATUS_SENDINGREQUEST: + CHECK_EXPECT(OnProgress_SENDINGREQUEST); + if((bindf & BINDF_ASYNCHRONOUS) && emulate_protocol) + SetEvent(complete_event); + break; + case BINDSTATUS_MIMETYPEAVAILABLE: + CHECK_EXPECT(OnProgress_MIMETYPEAVAILABLE); + ok(download_state == BEFORE_DOWNLOAD, "Download state was %d, expected BEFORE_DOWNLOAD\n", + download_state); + WideCharToMultiByte(CP_ACP, 0, szStatusText, -1, mime_type, sizeof(mime_type)-1, NULL, NULL); + break; + case BINDSTATUS_BEGINDOWNLOADDATA: + CHECK_EXPECT(OnProgress_BEGINDOWNLOADDATA); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) + ok(!lstrcmpW(szStatusText, urls[test_protocol]), "wrong szStatusText\n"); + ok(download_state == BEFORE_DOWNLOAD, "Download state was %d, expected BEFORE_DOWNLOAD\n", + download_state); + download_state = DOWNLOADING; + break; + case BINDSTATUS_DOWNLOADINGDATA: + CHECK_EXPECT2(OnProgress_DOWNLOADINGDATA); + ok(download_state == DOWNLOADING, "Download state was %d, expected DOWNLOADING\n", + download_state); + break; + case BINDSTATUS_ENDDOWNLOADDATA: + CHECK_EXPECT(OnProgress_ENDDOWNLOADDATA); + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText) + ok(!lstrcmpW(szStatusText, urls[test_protocol]), "wrong szStatusText\n"); + ok(download_state == DOWNLOADING, "Download state was %d, expected DOWNLOADING\n", + download_state); + download_state = END_DOWNLOAD; + break; + case BINDSTATUS_CACHEFILENAMEAVAILABLE: + ok(szStatusText != NULL, "szStatusText == NULL\n"); + if(szStatusText && test_protocol == FILE_TEST) + ok(!lstrcmpW(INDEX_HTML+7, szStatusText), "wrong szStatusText\n"); + break; + case BINDSTATUS_CLASSIDAVAILABLE: + { + CLSID clsid; + HRESULT hr; + CHECK_EXPECT(OnProgress_CLASSIDAVAILABLE); + hr = CLSIDFromString((LPOLESTR)szStatusText, &clsid); + ok(hr == S_OK, "CLSIDFromString failed with error 0x%08x\n", hr); + ok(IsEqualCLSID(&clsid, &CLSID_HTMLDocument), + "Expected clsid to be CLSID_HTMLDocument instead of {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + clsid.Data1, clsid.Data2, clsid.Data3, + clsid.Data4[0], clsid.Data4[1], clsid.Data4[2], clsid.Data4[3], + clsid.Data4[4], clsid.Data4[5], clsid.Data4[6], clsid.Data4[7]); + break; + } + case BINDSTATUS_BEGINSYNCOPERATION: + CHECK_EXPECT(OnProgress_BEGINSYNCOPERATION); + ok(szStatusText == NULL, "Expected szStatusText to be NULL\n"); + break; + case BINDSTATUS_ENDSYNCOPERATION: + CHECK_EXPECT(OnProgress_ENDSYNCOPERATION); + ok(szStatusText == NULL, "Expected szStatusText to be NULL\n"); + break; + default: + ok(0, "unexpexted code %d\n", ulStatusCode); + }; + return S_OK; +} + +static HRESULT WINAPI statusclb_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError) +{ + CHECK_EXPECT(OnStopBinding); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + stopped_binding = TRUE; + + /* ignore DNS failure */ + if (hresult == HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED)) + return S_OK; + + ok(hresult == S_OK, "binding failed: %08x\n", hresult); + ok(szError == NULL, "szError should be NULL\n"); + + if(test_protocol == HTTP_TEST && emulate_protocol) { + SetEvent(complete_event); + WaitForSingleObject(complete_event2, INFINITE); + } + + return S_OK; +} + +static HRESULT WINAPI statusclb_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo) +{ + DWORD cbSize; + + CHECK_EXPECT(GetBindInfo); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + *grfBINDF = bindf; + cbSize = pbindinfo->cbSize; + memset(pbindinfo, 0, cbSize); + pbindinfo->cbSize = cbSize; + + return S_OK; +} + +static HRESULT WINAPI statusclb_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF, + DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed) +{ + HRESULT hres; + DWORD readed; + BYTE buf[512]; + CHAR clipfmt[512]; + + CHECK_EXPECT2(OnDataAvailable); + + ok(GetCurrentThreadId() == thread_id, "wrong thread %d\n", GetCurrentThreadId()); + + ok(download_state == DOWNLOADING || download_state == END_DOWNLOAD, + "Download state was %d, expected DOWNLOADING or END_DOWNLOAD\n", + download_state); + data_available = TRUE; + + ok(pformatetc != NULL, "pformatetx == NULL\n"); + if(pformatetc) { + if (mime_type[0]) todo_wine { + clipfmt[0] = 0; + ok(GetClipboardFormatName(pformatetc->cfFormat, clipfmt, sizeof(clipfmt)-1), + "GetClipboardFormatName failed, error %d\n", GetLastError()); + ok(!lstrcmp(clipfmt, mime_type), "clipformat != mime_type, \"%s\" != \"%s\"\n", + clipfmt, mime_type); + } else { + ok(pformatetc->cfFormat == 0, "clipformat=%x\n", pformatetc->cfFormat); + } + ok(pformatetc->ptd == NULL, "ptd = %p\n", pformatetc->ptd); + ok(pformatetc->dwAspect == 1, "dwAspect=%u\n", pformatetc->dwAspect); + ok(pformatetc->lindex == -1, "lindex=%d\n", pformatetc->lindex); + ok(pformatetc->tymed == TYMED_ISTREAM, "tymed=%u\n", pformatetc->tymed); + } + + ok(pstgmed != NULL, "stgmeg == NULL\n"); + if(pstgmed) { + ok(pstgmed->tymed == TYMED_ISTREAM, "tymed=%u\n", pstgmed->tymed); + ok(U(*pstgmed).pstm != NULL, "pstm == NULL\n"); + ok(pstgmed->pUnkForRelease != NULL, "pUnkForRelease == NULL\n"); + } + + if(grfBSCF & BSCF_FIRSTDATANOTIFICATION) { + hres = IStream_Write(U(*pstgmed).pstm, buf, 10, NULL); + ok(hres == STG_E_ACCESSDENIED, + "Write failed: %08x, expected STG_E_ACCESSDENIED\n", hres); + + hres = IStream_Commit(U(*pstgmed).pstm, 0); + ok(hres == E_NOTIMPL, "Commit failed: %08x, expected E_NOTIMPL\n", hres); + + hres = IStream_Revert(U(*pstgmed).pstm); + ok(hres == E_NOTIMPL, "Revert failed: %08x, expected E_NOTIMPL\n", hres); + } + + ok(U(*pstgmed).pstm != NULL, "U(*pstgmed).pstm == NULL\n"); + + if(U(*pstgmed).pstm) { + do hres = IStream_Read(U(*pstgmed).pstm, buf, 512, &readed); + while(hres == S_OK); + ok(hres == S_FALSE || hres == E_PENDING, "IStream_Read returned %08x\n", hres); + } + + if(test_protocol == HTTP_TEST && emulate_protocol && prot_state < 4) + SetEvent(complete_event); + + return S_OK; +} + +static HRESULT WINAPI statusclb_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk) +{ + CHECK_EXPECT(OnObjectAvailable); + return S_OK; +} + +static const IBindStatusCallbackVtbl BindStatusCallbackVtbl = { + statusclb_QueryInterface, + statusclb_AddRef, + statusclb_Release, + statusclb_OnStartBinding, + statusclb_GetPriority, + statusclb_OnLowResource, + statusclb_OnProgress, + statusclb_OnStopBinding, + statusclb_GetBindInfo, + statusclb_OnDataAvailable, + statusclb_OnObjectAvailable +}; + +static IBindStatusCallback bsc = { &BindStatusCallbackVtbl }; + +static void test_CreateAsyncBindCtx(void) +{ + IBindCtx *bctx = (IBindCtx*)0x0ff00ff0; + IUnknown *unk; + HRESULT hres; + ULONG ref; + BIND_OPTS bindopts; + + hres = CreateAsyncBindCtx(0, NULL, NULL, &bctx); + ok(hres == E_INVALIDARG, "CreateAsyncBindCtx failed. expected: E_INVALIDARG, got: %08x\n", hres); + ok(bctx == (IBindCtx*)0x0ff00ff0, "bctx should not be changed\n"); + + hres = CreateAsyncBindCtx(0, NULL, NULL, NULL); + ok(hres == E_INVALIDARG, "CreateAsyncBindCtx failed. expected: E_INVALIDARG, got: %08x\n", hres); + + SET_EXPECT(QueryInterface_IServiceProvider); + hres = CreateAsyncBindCtx(0, &bsc, NULL, &bctx); + ok(hres == S_OK, "CreateAsyncBindCtx failed: %08x\n", hres); + CHECK_CALLED(QueryInterface_IServiceProvider); + + bindopts.cbStruct = sizeof(bindopts); + hres = IBindCtx_GetBindOptions(bctx, &bindopts); + ok(SUCCEEDED(hres), "IBindCtx_GetBindOptions failed: %08x\n", hres); + ok(bindopts.grfFlags == BIND_MAYBOTHERUSER, + "bindopts.grfFlags = %08x, expected: BIND_MAYBOTHERUSER\n", bindopts.grfFlags); + ok(bindopts.grfMode == (STGM_READWRITE | STGM_SHARE_EXCLUSIVE), + "bindopts.grfMode = %08x, expected: STGM_READWRITE | STGM_SHARE_EXCLUSIVE\n", + bindopts.grfMode); + ok(bindopts.dwTickCountDeadline == 0, + "bindopts.dwTickCountDeadline = %08x, expected: 0\n", bindopts.dwTickCountDeadline); + + hres = IBindCtx_QueryInterface(bctx, &IID_IAsyncBindCtx, (void**)&unk); + ok(hres == E_NOINTERFACE, "QueryInterface(IID_IAsyncBindCtx) failed: %08x, expected E_NOINTERFACE\n", hres); + if(SUCCEEDED(hres)) + IUnknown_Release(unk); + + ref = IBindCtx_Release(bctx); + ok(ref == 0, "bctx should be destroyed here\n"); +} + +static void test_CreateAsyncBindCtxEx(void) +{ + IBindCtx *bctx = NULL, *bctx_arg = NULL; + IUnknown *unk; + BIND_OPTS bindopts; + HRESULT hres; + + hres = CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, NULL, 0); + ok(hres == E_INVALIDARG, "CreateAsyncBindCtx failed: %08x, expected E_INVALIDARG\n", hres); + + hres = CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, &bctx, 0); + ok(hres == S_OK, "CreateAsyncBindCtxEx failed: %08x\n", hres); + + if(SUCCEEDED(hres)) { + bindopts.cbStruct = sizeof(bindopts); + hres = IBindCtx_GetBindOptions(bctx, &bindopts); + ok(SUCCEEDED(hres), "IBindCtx_GetBindOptions failed: %08x\n", hres); + ok(bindopts.grfFlags == BIND_MAYBOTHERUSER, + "bindopts.grfFlags = %08x, expected: BIND_MAYBOTHERUSER\n", bindopts.grfFlags); + ok(bindopts.grfMode == (STGM_READWRITE | STGM_SHARE_EXCLUSIVE), + "bindopts.grfMode = %08x, expected: STGM_READWRITE | STGM_SHARE_EXCLUSIVE\n", + bindopts.grfMode); + ok(bindopts.dwTickCountDeadline == 0, + "bindopts.dwTickCountDeadline = %08x, expected: 0\n", bindopts.dwTickCountDeadline); + + IBindCtx_Release(bctx); + } + + CreateBindCtx(0, &bctx_arg); + hres = CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, &bctx, 0); + ok(hres == S_OK, "CreateAsyncBindCtxEx failed: %08x\n", hres); + + if(SUCCEEDED(hres)) { + bindopts.cbStruct = sizeof(bindopts); + hres = IBindCtx_GetBindOptions(bctx, &bindopts); + ok(SUCCEEDED(hres), "IBindCtx_GetBindOptions failed: %08x\n", hres); + ok(bindopts.grfFlags == BIND_MAYBOTHERUSER, + "bindopts.grfFlags = %08x, expected: BIND_MAYBOTHERUSER\n", bindopts.grfFlags); + ok(bindopts.grfMode == (STGM_READWRITE | STGM_SHARE_EXCLUSIVE), + "bindopts.grfMode = %08x, expected: STGM_READWRITE | STGM_SHARE_EXCLUSIVE\n", + bindopts.grfMode); + ok(bindopts.dwTickCountDeadline == 0, + "bindopts.dwTickCountDeadline = %08x, expected: 0\n", bindopts.dwTickCountDeadline); + + IBindCtx_Release(bctx); + } + + IBindCtx_Release(bctx_arg); + + SET_EXPECT(QueryInterface_IServiceProvider); + hres = CreateAsyncBindCtxEx(NULL, 0, &bsc, NULL, &bctx, 0); + ok(hres == S_OK, "CreateAsyncBindCtxEx failed: %08x\n", hres); + CHECK_CALLED(QueryInterface_IServiceProvider); + + hres = IBindCtx_QueryInterface(bctx, &IID_IAsyncBindCtx, (void**)&unk); + ok(hres == S_OK, "QueryInterface(IID_IAsyncBindCtx) failed: %08x\n", hres); + if(SUCCEEDED(hres)) + IUnknown_Release(unk); + + if(SUCCEEDED(hres)) + IBindCtx_Release(bctx); +} + +static void test_bscholder(IBindStatusCallback *holder) +{ + IServiceProvider *serv_prov; + IHttpNegotiate *http_negotiate, *http_negotiate_serv; + IHttpNegotiate2 *http_negotiate2, *http_negotiate2_serv; + IAuthenticate *authenticate, *authenticate_serv; + IInternetProtocol *protocol; + BINDINFO bindinfo = {sizeof(bindinfo)}; + LPWSTR wstr; + DWORD dw; + HRESULT hres; + + static const WCHAR emptyW[] = {0}; + + hres = IBindStatusCallback_QueryInterface(holder, &IID_IServiceProvider, (void**)&serv_prov); + ok(hres == S_OK, "Could not get IServiceProvider interface: %08x\n", hres); + + dw = 0xdeadbeef; + SET_EXPECT(GetBindInfo); + hres = IBindStatusCallback_GetBindInfo(holder, &dw, &bindinfo); + ok(hres == S_OK, "GetBindInfo failed: %08x\n", hres); + CHECK_CALLED(GetBindInfo); + + SET_EXPECT(OnStartBinding); + hres = IBindStatusCallback_OnStartBinding(holder, 0, (void*)0xdeadbeef); + ok(hres == S_OK, "OnStartBinding failed: %08x\n", hres); + CHECK_CALLED(OnStartBinding); + + hres = IBindStatusCallback_QueryInterface(holder, &IID_IHttpNegotiate, (void**)&http_negotiate); + ok(hres == S_OK, "Could not get IHttpNegotiate interface: %08x\n", hres); + + wstr = (void*)0xdeadbeef; + hres = IHttpNegotiate_BeginningTransaction(http_negotiate, urls[test_protocol], (void*)0xdeadbeef, 0xff, &wstr); + ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres); + ok(wstr == NULL, "wstr = %p\n", wstr); + + SET_EXPECT(QueryInterface_IHttpNegotiate); + hres = IServiceProvider_QueryService(serv_prov, &IID_IHttpNegotiate, &IID_IHttpNegotiate, + (void**)&http_negotiate_serv); + ok(hres == S_OK, "Could not get IHttpNegotiate service: %08x\n", hres); + CHECK_CALLED(QueryInterface_IHttpNegotiate); + + ok(http_negotiate == http_negotiate_serv, "http_negotiate != http_negotiate_serv\n"); + + wstr = (void*)0xdeadbeef; + SET_EXPECT(BeginningTransaction); + hres = IHttpNegotiate_BeginningTransaction(http_negotiate_serv, urls[test_protocol], emptyW, 0, &wstr); + CHECK_CALLED(BeginningTransaction); + ok(hres == S_OK, "BeginningTransaction failed: %08x\n", hres); + ok(wstr == NULL, "wstr = %p\n", wstr); + + IHttpNegotiate_Release(http_negotiate_serv); + + hres = IServiceProvider_QueryService(serv_prov, &IID_IHttpNegotiate, &IID_IHttpNegotiate, + (void**)&http_negotiate_serv); + ok(hres == S_OK, "Could not get IHttpNegotiate service: %08x\n", hres); + ok(http_negotiate == http_negotiate_serv, "http_negotiate != http_negotiate_serv\n"); + IHttpNegotiate_Release(http_negotiate_serv); + + hres = IBindStatusCallback_QueryInterface(holder, &IID_IHttpNegotiate2, (void**)&http_negotiate2); + ok(hres == S_OK, "Could not get IHttpNegotiate2 interface: %08x\n", hres); + + hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, (void*)0xdeadbeef, (void*)0xdeadbeef, 0); + ok(hres == E_FAIL, "GetRootSecurityId failed: %08x\n", hres); + + IHttpNegotiate_Release(http_negotiate2); + + SET_EXPECT(QueryInterface_IHttpNegotiate2); + hres = IServiceProvider_QueryService(serv_prov, &IID_IHttpNegotiate2, &IID_IHttpNegotiate2, + (void**)&http_negotiate2_serv); + ok(hres == S_OK, "Could not get IHttpNegotiate2 service: %08x\n", hres); + CHECK_CALLED(QueryInterface_IHttpNegotiate2); + ok(http_negotiate2 == http_negotiate2_serv, "http_negotiate != http_negotiate_serv\n"); + + SET_EXPECT(GetRootSecurityId); + hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, (void*)0xdeadbeef, (void*)0xdeadbeef, 0); + ok(hres == E_NOTIMPL, "GetRootSecurityId failed: %08x\n", hres); + CHECK_CALLED(GetRootSecurityId); + + IHttpNegotiate_Release(http_negotiate2_serv); + + SET_EXPECT(OnProgress_FINDINGRESOURCE); + hres = IBindStatusCallback_OnProgress(holder, 0, 0, BINDSTATUS_FINDINGRESOURCE, NULL); + ok(hres == S_OK, "OnProgress failed: %08x\n", hres); + CHECK_CALLED(OnProgress_FINDINGRESOURCE); + + SET_EXPECT(OnResponse); + wstr = (void*)0xdeadbeef; + hres = IHttpNegotiate_OnResponse(http_negotiate, 200, emptyW, NULL, NULL); + ok(hres == S_OK, "OnResponse failed: %08x\n", hres); + CHECK_CALLED(OnResponse); + + IHttpNegotiate_Release(http_negotiate); + + hres = IBindStatusCallback_QueryInterface(holder, &IID_IAuthenticate, (void**)&authenticate); + ok(hres == S_OK, "Could not get IAuthenticate interface: %08x\n", hres); + + SET_EXPECT(QueryInterface_IAuthenticate); + SET_EXPECT(QueryService_IAuthenticate); + hres = IServiceProvider_QueryService(serv_prov, &IID_IAuthenticate, &IID_IAuthenticate, + (void**)&authenticate_serv); + ok(hres == S_OK, "Could not get IAuthenticate service: %08x\n", hres); + CHECK_CALLED(QueryInterface_IAuthenticate); + CHECK_CALLED(QueryService_IAuthenticate); + ok(authenticate == authenticate_serv, "authenticate != authenticate_serv\n"); + IAuthenticate_Release(authenticate_serv); + + hres = IServiceProvider_QueryService(serv_prov, &IID_IAuthenticate, &IID_IAuthenticate, + (void**)&authenticate_serv); + ok(hres == S_OK, "Could not get IAuthenticate service: %08x\n", hres); + ok(authenticate == authenticate_serv, "authenticate != authenticate_serv\n"); + + IAuthenticate_Release(authenticate); + IAuthenticate_Release(authenticate_serv); + + SET_EXPECT(OnStopBinding); + hres = IBindStatusCallback_OnStopBinding(holder, S_OK, NULL); + ok(hres == S_OK, "OnStopBinding failed: %08x\n", hres); + CHECK_CALLED(OnStopBinding); + + SET_EXPECT(QueryInterface_IInternetProtocol); + SET_EXPECT(QueryService_IInternetProtocol); + hres = IServiceProvider_QueryService(serv_prov, &IID_IInternetProtocol, &IID_IInternetProtocol, + (void**)&protocol); + ok(hres == E_NOINTERFACE, "QueryService(IInternetProtocol) failed: %08x\n", hres); + CHECK_CALLED(QueryInterface_IInternetProtocol); + CHECK_CALLED(QueryService_IInternetProtocol); + + IServiceProvider_Release(serv_prov); +} + +static void test_RegisterBindStatusCallback(void) +{ + IBindStatusCallback *prevbsc, *clb; + IBindCtx *bindctx; + IUnknown *unk; + HRESULT hres; + + hres = CreateBindCtx(0, &bindctx); + ok(hres == S_OK, "BindCtx failed: %08x\n", hres); + + SET_EXPECT(QueryInterface_IServiceProvider); + + hres = IBindCtx_RegisterObjectParam(bindctx, BSCBHolder, (IUnknown*)&bsc); + ok(hres == S_OK, "RegisterObjectParam failed: %08x\n", hres); + + SET_EXPECT(QueryInterface_IBindStatusCallback); + SET_EXPECT(QueryInterface_IBindStatusCallbackHolder); + prevbsc = (void*)0xdeadbeef; + hres = RegisterBindStatusCallback(bindctx, &bsc, &prevbsc, 0); + ok(hres == S_OK, "RegisterBindStatusCallback failed: %08x\n", hres); + ok(prevbsc == &bsc, "prevbsc=%p\n", prevbsc); + CHECK_CALLED(QueryInterface_IBindStatusCallback); + CHECK_CALLED(QueryInterface_IBindStatusCallbackHolder); + + CHECK_CALLED(QueryInterface_IServiceProvider); + + hres = IBindCtx_GetObjectParam(bindctx, BSCBHolder, &unk); + ok(hres == S_OK, "GetObjectParam failed: %08x\n", hres); + + hres = IUnknown_QueryInterface(unk, &IID_IBindStatusCallback, (void**)&clb); + IUnknown_Release(unk); + ok(hres == S_OK, "QueryInterface(IID_IBindStatusCallback) failed: %08x\n", hres); + ok(clb != &bsc, "bsc == clb\n"); + + test_bscholder(clb); + + IBindStatusCallback_Release(clb); + + hres = RevokeBindStatusCallback(bindctx, &bsc); + ok(hres == S_OK, "RevokeBindStatusCallback failed: %08x\n", hres); + + unk = (void*)0xdeadbeef; + hres = IBindCtx_GetObjectParam(bindctx, BSCBHolder, &unk); + ok(hres == E_FAIL, "GetObjectParam failed: %08x\n", hres); + ok(unk == NULL, "unk != NULL\n"); + + if(unk) + IUnknown_Release(unk); + + hres = RevokeBindStatusCallback(bindctx, (void*)0xdeadbeef); + ok(hres == S_OK, "RevokeBindStatusCallback failed: %08x\n", hres); + + hres = RevokeBindStatusCallback(NULL, (void*)0xdeadbeef); + ok(hres == E_INVALIDARG, "RevokeBindStatusCallback failed: %08x\n", hres); + + hres = RevokeBindStatusCallback(bindctx, NULL); + ok(hres == E_INVALIDARG, "RevokeBindStatusCallback failed: %08x\n", hres); + + IBindCtx_Release(bindctx); +} + +static void test_BindToStorage(int protocol, BOOL emul) +{ + IMoniker *mon; + HRESULT hres; + LPOLESTR display_name; + IBindCtx *bctx; + MSG msg; + IBindStatusCallback *previousclb; + IUnknown *unk = (IUnknown*)0x00ff00ff; + IBinding *bind; + + test_protocol = protocol; + emulate_protocol = emul; + download_state = BEFORE_DOWNLOAD; + stopped_binding = FALSE; + data_available = FALSE; + mime_type[0] = 0; + + SET_EXPECT(QueryInterface_IServiceProvider); + hres = CreateAsyncBindCtx(0, &bsc, NULL, &bctx); + ok(hres == S_OK, "CreateAsyncBindCtx failed: %08x\n\n", hres); + CHECK_CALLED(QueryInterface_IServiceProvider); + if(FAILED(hres)) + return; + + SET_EXPECT(QueryInterface_IServiceProvider); + hres = RegisterBindStatusCallback(bctx, &bsc, &previousclb, 0); + ok(hres == S_OK, "RegisterBindStatusCallback failed: %08x\n", hres); + ok(previousclb == &bsc, "previousclb(%p) != sclb(%p)\n", previousclb, &bsc); + CHECK_CALLED(QueryInterface_IServiceProvider); + if(previousclb) + IBindStatusCallback_Release(previousclb); + + hres = CreateURLMoniker(NULL, urls[test_protocol], &mon); + ok(SUCCEEDED(hres), "failed to create moniker: %08x\n", hres); + if(FAILED(hres)) { + IBindCtx_Release(bctx); + return; + } + + if(test_protocol == FILE_TEST && INDEX_HTML[7] == '/') + memmove(INDEX_HTML+7, INDEX_HTML+8, lstrlenW(INDEX_HTML+7)*sizeof(WCHAR)); + + hres = IMoniker_QueryInterface(mon, &IID_IBinding, (void**)&bind); + ok(hres == E_NOINTERFACE, "IMoniker should not have IBinding interface\n"); + if(SUCCEEDED(hres)) + IBinding_Release(bind); + + hres = IMoniker_GetDisplayName(mon, bctx, NULL, &display_name); + ok(hres == S_OK, "GetDisplayName failed %08x\n", hres); + ok(!lstrcmpW(display_name, urls[test_protocol]), "GetDisplayName got wrong name\n"); + + SET_EXPECT(GetBindInfo); + SET_EXPECT(QueryInterface_IInternetProtocol); + if(!emulate_protocol) + SET_EXPECT(QueryService_IInternetProtocol); + SET_EXPECT(OnStartBinding); + if(emulate_protocol) { + SET_EXPECT(Start); + if(test_protocol == HTTP_TEST) + SET_EXPECT(Terminate); + SET_EXPECT(UnlockRequest); + }else { + if(test_protocol == HTTP_TEST) { + SET_EXPECT(QueryInterface_IHttpNegotiate); + SET_EXPECT(BeginningTransaction); + SET_EXPECT(QueryInterface_IHttpNegotiate2); + SET_EXPECT(GetRootSecurityId); + SET_EXPECT(OnProgress_FINDINGRESOURCE); + SET_EXPECT(OnProgress_CONNECTING); + } + if(test_protocol == HTTP_TEST || test_protocol == FILE_TEST) + SET_EXPECT(OnProgress_SENDINGREQUEST); + if(test_protocol == HTTP_TEST) + SET_EXPECT(OnResponse); + SET_EXPECT(OnProgress_MIMETYPEAVAILABLE); + SET_EXPECT(OnProgress_BEGINDOWNLOADDATA); + if(test_protocol == HTTP_TEST) + SET_EXPECT(OnProgress_DOWNLOADINGDATA); + SET_EXPECT(OnProgress_ENDDOWNLOADDATA); + SET_EXPECT(OnDataAvailable); + SET_EXPECT(OnStopBinding); + } + + hres = IMoniker_BindToStorage(mon, bctx, NULL, &IID_IStream, (void**)&unk); + if (test_protocol == HTTP_TEST && hres == HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED)) + { + trace( "Network unreachable, skipping tests\n" ); + return; + } + if (!SUCCEEDED(hres)) return; + + if((bindf & BINDF_ASYNCHRONOUS) && !data_available) { + ok(hres == MK_S_ASYNCHRONOUS, "IMoniker_BindToStorage failed: %08x\n", hres); + ok(unk == NULL, "istr should be NULL\n"); + }else { + ok(hres == S_OK, "IMoniker_BindToStorage failed: %08x\n", hres); + ok(unk != NULL, "unk == NULL\n"); + } + if(unk) + IUnknown_Release(unk); + + while((bindf & BINDF_ASYNCHRONOUS) && + !stopped_binding && GetMessage(&msg,NULL,0,0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + CHECK_CALLED(GetBindInfo); + CHECK_CALLED(QueryInterface_IInternetProtocol); + if(!emulate_protocol) + CHECK_CALLED(QueryService_IInternetProtocol); + CHECK_CALLED(OnStartBinding); + if(emulate_protocol) { + CHECK_CALLED(Start); + if(test_protocol == HTTP_TEST) + CHECK_CALLED(Terminate); + CHECK_CALLED(UnlockRequest); + }else { + if(test_protocol == HTTP_TEST) { + CHECK_CALLED(QueryInterface_IHttpNegotiate); + CHECK_CALLED(BeginningTransaction); + /* QueryInterface_IHttpNegotiate2 and GetRootSecurityId + * called on WinXP but not on Win98 */ + CLEAR_CALLED(QueryInterface_IHttpNegotiate2); + CLEAR_CALLED(GetRootSecurityId); + if(http_is_first) { + CHECK_CALLED(OnProgress_FINDINGRESOURCE); + CHECK_CALLED(OnProgress_CONNECTING); + }else todo_wine { + CHECK_NOT_CALLED(OnProgress_FINDINGRESOURCE); + CHECK_NOT_CALLED(OnProgress_CONNECTING); + } + } + if(test_protocol == HTTP_TEST || test_protocol == FILE_TEST) + CHECK_CALLED(OnProgress_SENDINGREQUEST); + if(test_protocol == HTTP_TEST) + CHECK_CALLED(OnResponse); + CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE); + CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA); + if(test_protocol == HTTP_TEST) + CLEAR_CALLED(OnProgress_DOWNLOADINGDATA); + CHECK_CALLED(OnProgress_ENDDOWNLOADDATA); + CHECK_CALLED(OnDataAvailable); + CHECK_CALLED(OnStopBinding); + } + + ok(IMoniker_Release(mon) == 0, "mon should be destroyed here\n"); + ok(IBindCtx_Release(bctx) == 0, "bctx should be destroyed here\n"); + + if(test_protocol == HTTP_TEST) + http_is_first = FALSE; +} + +static void test_BindToObject(int protocol, BOOL emul) +{ + IMoniker *mon; + HRESULT hres; + LPOLESTR display_name; + IBindCtx *bctx; + MSG msg; + IBindStatusCallback *previousclb; + IUnknown *unk = (IUnknown*)0x00ff00ff; + IBinding *bind; + + test_protocol = protocol; + emulate_protocol = emul; + download_state = BEFORE_DOWNLOAD; + stopped_binding = FALSE; + data_available = FALSE; + mime_type[0] = 0; + + SET_EXPECT(QueryInterface_IServiceProvider); + hres = CreateAsyncBindCtx(0, &bsc, NULL, &bctx); + ok(SUCCEEDED(hres), "CreateAsyncBindCtx failed: %08x\n\n", hres); + if(FAILED(hres)) + return; + CHECK_CALLED(QueryInterface_IServiceProvider); + + SET_EXPECT(QueryInterface_IServiceProvider); + hres = RegisterBindStatusCallback(bctx, &bsc, &previousclb, 0); + ok(SUCCEEDED(hres), "RegisterBindStatusCallback failed: %08x\n", hres); + ok(previousclb == &bsc, "previousclb(%p) != sclb(%p)\n", previousclb, &bsc); + CHECK_CALLED(QueryInterface_IServiceProvider); + if(previousclb) + IBindStatusCallback_Release(previousclb); + + hres = CreateURLMoniker(NULL, urls[test_protocol], &mon); + ok(SUCCEEDED(hres), "failed to create moniker: %08x\n", hres); + if(FAILED(hres)) { + IBindCtx_Release(bctx); + return; + } + + if(test_protocol == FILE_TEST && INDEX_HTML[7] == '/') + memmove(INDEX_HTML+7, INDEX_HTML+8, lstrlenW(INDEX_HTML+7)*sizeof(WCHAR)); + + hres = IMoniker_QueryInterface(mon, &IID_IBinding, (void**)&bind); + ok(hres == E_NOINTERFACE, "IMoniker should not have IBinding interface\n"); + if(SUCCEEDED(hres)) + IBinding_Release(bind); + + hres = IMoniker_GetDisplayName(mon, bctx, NULL, &display_name); + ok(hres == S_OK, "GetDisplayName failed %08x\n", hres); + ok(!lstrcmpW(display_name, urls[test_protocol]), "GetDisplayName got wrong name\n"); + + SET_EXPECT(QueryInterface_IServiceProvider); + SET_EXPECT(GetBindInfo); + SET_EXPECT(OnStartBinding); + if(emulate_protocol) { + SET_EXPECT(Start); + SET_EXPECT(UnlockRequest); + }else { + if(test_protocol == HTTP_TEST) { + SET_EXPECT(QueryInterface_IHttpNegotiate); + SET_EXPECT(BeginningTransaction); + SET_EXPECT(QueryInterface_IHttpNegotiate2); + SET_EXPECT(GetRootSecurityId); + SET_EXPECT(OnProgress_FINDINGRESOURCE); + SET_EXPECT(OnProgress_CONNECTING); + } + if(test_protocol == HTTP_TEST || test_protocol == FILE_TEST) + SET_EXPECT(OnProgress_SENDINGREQUEST); + if(test_protocol == HTTP_TEST) + SET_EXPECT(OnResponse); + SET_EXPECT(OnProgress_MIMETYPEAVAILABLE); + SET_EXPECT(OnProgress_BEGINDOWNLOADDATA); + if(test_protocol == HTTP_TEST) + SET_EXPECT(OnProgress_DOWNLOADINGDATA); + SET_EXPECT(OnProgress_ENDDOWNLOADDATA); + SET_EXPECT(OnProgress_CLASSIDAVAILABLE); + SET_EXPECT(OnProgress_BEGINSYNCOPERATION); + SET_EXPECT(OnProgress_ENDSYNCOPERATION); + SET_EXPECT(OnObjectAvailable); + SET_EXPECT(OnStopBinding); + } + + hres = IMoniker_BindToObject(mon, bctx, NULL, &IID_IUnknown, (void**)&unk); + if (test_protocol == HTTP_TEST && hres == HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED)) + { + trace( "Network unreachable, skipping tests\n" ); + return; + } + todo_wine ok(SUCCEEDED(hres), "IMoniker_BindToObject failed with error 0x%08x\n", hres); + /* no point testing the calls if binding didn't even work */ + if (!SUCCEEDED(hres)) return; + + if((bindf & BINDF_ASYNCHRONOUS) && !data_available) { + ok(hres == MK_S_ASYNCHRONOUS, "IMoniker_BindToStorage failed: %08x\n", hres); + ok(unk == NULL, "istr should be NULL\n"); + }else { + ok(hres == S_OK, "IMoniker_BindToStorage failed: %08x\n", hres); + ok(unk != NULL, "unk == NULL\n"); + } + if(unk) + IUnknown_Release(unk); + + while((bindf & BINDF_ASYNCHRONOUS) && + !stopped_binding && GetMessage(&msg,NULL,0,0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + todo_wine CHECK_NOT_CALLED(QueryInterface_IServiceProvider); + CHECK_CALLED(GetBindInfo); + CHECK_CALLED(OnStartBinding); + if(emulate_protocol) { + CHECK_CALLED(Start); + CHECK_CALLED(UnlockRequest); + }else { + if(test_protocol == HTTP_TEST) { + CHECK_CALLED(QueryInterface_IHttpNegotiate); + CHECK_CALLED(BeginningTransaction); + /* QueryInterface_IHttpNegotiate2 and GetRootSecurityId + * called on WinXP but not on Win98 */ + CLEAR_CALLED(QueryInterface_IHttpNegotiate2); + CLEAR_CALLED(GetRootSecurityId); + if(http_is_first) { + CHECK_CALLED(OnProgress_FINDINGRESOURCE); + CHECK_CALLED(OnProgress_CONNECTING); + }else todo_wine { + CHECK_NOT_CALLED(OnProgress_FINDINGRESOURCE); + CHECK_NOT_CALLED(OnProgress_CONNECTING); + } + } + if(test_protocol == HTTP_TEST || test_protocol == FILE_TEST) + CHECK_CALLED(OnProgress_SENDINGREQUEST); + if(test_protocol == HTTP_TEST) + CHECK_CALLED(OnResponse); + CHECK_CALLED(OnProgress_MIMETYPEAVAILABLE); + CHECK_CALLED(OnProgress_BEGINDOWNLOADDATA); + if(test_protocol == HTTP_TEST) + CLEAR_CALLED(OnProgress_DOWNLOADINGDATA); + CHECK_CALLED(OnProgress_ENDDOWNLOADDATA); + CHECK_CALLED(OnProgress_CLASSIDAVAILABLE); + CHECK_CALLED(OnProgress_BEGINSYNCOPERATION); + CHECK_CALLED(OnProgress_ENDSYNCOPERATION); + CHECK_CALLED(OnObjectAvailable); + CHECK_CALLED(OnStopBinding); + } + + ok(IMoniker_Release(mon) == 0, "mon should be destroyed here\n"); + ok(IBindCtx_Release(bctx) == 0, "bctx should be destroyed here\n"); + + if(test_protocol == HTTP_TEST) + http_is_first = FALSE; +} + +static void set_file_url(void) +{ + int len; + + static const WCHAR wszFile[] = {'f','i','l','e',':','/','/'}; + + memcpy(INDEX_HTML, wszFile, sizeof(wszFile)); + len = sizeof(wszFile)/sizeof(WCHAR); + INDEX_HTML[len++] = '/'; + len += GetCurrentDirectoryW(sizeof(INDEX_HTML)/sizeof(WCHAR)-len, INDEX_HTML+len); + INDEX_HTML[len++] = '\\'; + memcpy(INDEX_HTML+len, wszIndexHtml, sizeof(wszIndexHtml)); +} + +static void create_file(void) +{ + HANDLE file; + DWORD size; + + static const char html_doc[] = ""; + + file = CreateFileW(wszIndexHtml, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "CreateFile failed\n"); + if(file == INVALID_HANDLE_VALUE) + return; + + WriteFile(file, html_doc, sizeof(html_doc)-1, &size, NULL); + CloseHandle(file); + + set_file_url(); +} + +static void test_BindToStorage_fail(void) +{ + IMoniker *mon = NULL; + IBindCtx *bctx = NULL; + IUnknown *unk; + HRESULT hres; + + hres = CreateURLMoniker(NULL, ABOUT_BLANK, &mon); + ok(hres == S_OK, "CreateURLMoniker failed: %08x\n", hres); + if(FAILED(hres)) + return; + + hres = CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, &bctx, 0); + ok(hres == S_OK, "CreateAsyncBindCtxEx failed: %08x\n", hres); + + hres = IMoniker_BindToStorage(mon, bctx, NULL, &IID_IStream, (void**)&unk); + ok(hres == MK_E_SYNTAX, "hres=%08x, expected INET_E_SYNTAX\n", hres); + + IBindCtx_Release(bctx); + + IMoniker_Release(mon); +} + +START_TEST(url) +{ + complete_event = CreateEvent(NULL, FALSE, FALSE, NULL); + complete_event2 = CreateEvent(NULL, FALSE, FALSE, NULL); + thread_id = GetCurrentThreadId(); + + test_create(); + test_CreateAsyncBindCtx(); + test_CreateAsyncBindCtxEx(); + test_RegisterBindStatusCallback(); + + trace("synchronous http test (COM not initialised)...\n"); + test_BindToStorage(HTTP_TEST, FALSE); + test_BindToStorage_fail(); + + CoInitialize(NULL); + + trace("synchronous http test...\n"); + test_BindToStorage(HTTP_TEST, FALSE); + test_BindToObject(HTTP_TEST, FALSE); + + trace("synchronous file test...\n"); + create_file(); + test_BindToStorage(FILE_TEST, FALSE); + test_BindToObject(FILE_TEST, FALSE); + DeleteFileW(wszIndexHtml); + + bindf = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA; + + trace("http test...\n"); + test_BindToStorage(HTTP_TEST, FALSE); + test_BindToObject(HTTP_TEST, FALSE); + + trace("http test (short response)...\n"); + http_is_first = TRUE; + urls[HTTP_TEST] = SHORT_RESPONSE_URL; + test_BindToStorage(HTTP_TEST, FALSE); + test_BindToObject(HTTP_TEST, FALSE); + + trace("emulated http test...\n"); + test_BindToStorage(HTTP_TEST, TRUE); + + trace("about test...\n"); + test_BindToStorage(ABOUT_TEST, FALSE); + test_BindToObject(ABOUT_TEST, FALSE); + + trace("emulated about test...\n"); + test_BindToStorage(ABOUT_TEST, TRUE); + + trace("file test...\n"); + create_file(); + test_BindToStorage(FILE_TEST, FALSE); + test_BindToObject(FILE_TEST, FALSE); + DeleteFileW(wszIndexHtml); + + trace("emulated file test...\n"); + set_file_url(); + test_BindToStorage(FILE_TEST, TRUE); + + trace("emulated its test...\n"); + test_BindToStorage(ITS_TEST, TRUE); + + trace("emulated mk test...\n"); + test_BindToStorage(MK_TEST, TRUE); + + test_BindToStorage_fail(); + + CloseHandle(complete_event); + CloseHandle(complete_event2); + CoUninitialize(); +} diff --git a/rostests/winetests/urlmon/urlmon.rbuild b/rostests/winetests/urlmon/urlmon.rbuild new file mode 100644 index 00000000000..674fede4bbf --- /dev/null +++ b/rostests/winetests/urlmon/urlmon.rbuild @@ -0,0 +1,20 @@ + + + + . + 0x600 + 0x600 + wine + urlmon + user32 + kernel32 + ole32 + uuid + ntdll + generated.c + misc.c + protocol.c + stream.c + url.c + testlist.c + diff --git a/rostests/winetests/uxtheme/system.c b/rostests/winetests/uxtheme/system.c new file mode 100644 index 00000000000..1fd3d974293 --- /dev/null +++ b/rostests/winetests/uxtheme/system.c @@ -0,0 +1,511 @@ +/* Unit test suite for uxtheme API functions + * + * Copyright 2006 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 + +#include "windows.h" +#include "vfwmsgs.h" +#include "uxtheme.h" + +#include "wine/test.h" + +static HRESULT (WINAPI * pCloseThemeData)(HTHEME); +static HRESULT (WINAPI * pGetCurrentThemeName)(LPWSTR, int, LPWSTR, int, LPWSTR, int); +static HTHEME (WINAPI * pGetWindowTheme)(HWND); +static BOOL (WINAPI * pIsAppThemed)(VOID); +static BOOL (WINAPI * pIsThemeActive)(VOID); +static BOOL (WINAPI * pIsThemePartDefined)(HTHEME, int, int); +static HTHEME (WINAPI * pOpenThemeData)(HWND, LPCWSTR); +static HRESULT (WINAPI * pSetWindowTheme)(HWND, LPCWSTR, LPCWSTR); + +static HMODULE hUxtheme = 0; + +#define UXTHEME_GET_PROC(func) \ + p ## func = (void*)GetProcAddress(hUxtheme, #func); \ + if(!p ## func) { \ + trace("GetProcAddress(%s) failed\n", #func); \ + FreeLibrary(hUxtheme); \ + return FALSE; \ + } + +static BOOL InitFunctionPtrs(void) +{ + hUxtheme = LoadLibraryA("uxtheme.dll"); + if(!hUxtheme) { + trace("Could not load uxtheme.dll\n"); + return FALSE; + } + if (hUxtheme) + { + UXTHEME_GET_PROC(CloseThemeData) + UXTHEME_GET_PROC(GetCurrentThemeName) + UXTHEME_GET_PROC(GetWindowTheme) + UXTHEME_GET_PROC(IsAppThemed) + UXTHEME_GET_PROC(IsThemeActive) + UXTHEME_GET_PROC(IsThemePartDefined) + UXTHEME_GET_PROC(OpenThemeData) + UXTHEME_GET_PROC(SetWindowTheme) + } + /* The following functions should be available, if not return FALSE. The Vista functions will + * be checked (at some point in time) within the single tests if needed. All used functions for + * now are present on WinXP, W2K3 and Wine. + */ + if (!pCloseThemeData || !pGetCurrentThemeName || + !pGetWindowTheme || !pIsAppThemed || + !pIsThemeActive || !pIsThemePartDefined || + !pOpenThemeData || !pSetWindowTheme) + return FALSE; + + return TRUE; +} + +static void test_IsThemed(void) +{ + BOOL bThemeActive; + BOOL bAppThemed; + BOOL bTPDefined; + + SetLastError(0xdeadbeef); + bThemeActive = pIsThemeActive(); + trace("Theming is %s\n", (bThemeActive) ? "active" : "inactive"); + todo_wine + ok( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got 0x%08x\n", + GetLastError()); + + /* This test is not themed */ + SetLastError(0xdeadbeef); + bAppThemed = pIsAppThemed(); + + if (bThemeActive) + todo_wine + ok( bAppThemed == FALSE, "Expected FALSE as this test executable is not (yet) themed.\n"); + else + /* Although Wine currently returns FALSE, the logic behind it is wrong. It is not a todo_wine though in the testing sense */ + ok( bAppThemed == FALSE, "Expected FALSE as this test executable is not (yet) themed.\n"); + + todo_wine + ok( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got 0x%08x\n", + GetLastError()); + + SetLastError(0xdeadbeef); + bTPDefined = pIsThemePartDefined(NULL, 0 , 0); + ok( bTPDefined == FALSE, "Expected FALSE\n"); + ok( GetLastError() == E_HANDLE, + "Expected E_HANDLE, got 0x%08x\n", + GetLastError()); +} + +static void test_GetWindowTheme(void) +{ + HTHEME hTheme; + HWND hWnd; + BOOL bDestroyed; + + SetLastError(0xdeadbeef); + hTheme = pGetWindowTheme(NULL); + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + todo_wine + ok( GetLastError() == E_HANDLE, + "Expected E_HANDLE, got 0x%08x\n", + GetLastError()); + + /* Only do the bare minumum to get a valid hwnd */ + hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL); + if (!hWnd) return; + + SetLastError(0xdeadbeef); + hTheme = pGetWindowTheme(hWnd); + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + bDestroyed = DestroyWindow(hWnd); + if (!bDestroyed) + trace("Window %p couldn't be destroyed : 0x%08x\n", + hWnd, GetLastError()); +} + +static void test_SetWindowTheme(void) +{ + HRESULT hRes; + HWND hWnd; + BOOL bDestroyed; + + SetLastError(0xdeadbeef); + hRes = pSetWindowTheme(NULL, NULL, NULL); + todo_wine + { + ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + } + + /* Only do the bare minumum to get a valid hwnd */ + hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL); + if (!hWnd) return; + + SetLastError(0xdeadbeef); + hRes = pSetWindowTheme(hWnd, NULL, NULL); + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + bDestroyed = DestroyWindow(hWnd); + if (!bDestroyed) + trace("Window %p couldn't be destroyed : 0x%08x\n", + hWnd, GetLastError()); +} + +static void test_OpenThemeData(void) +{ + HTHEME hTheme, hTheme2; + HWND hWnd; + BOOL bThemeActive; + HRESULT hRes; + BOOL bDestroyed; + BOOL bTPDefined; + + WCHAR szInvalidClassList[] = {'D','E','A','D','B','E','E','F', 0 }; + WCHAR szButtonClassList[] = {'B','u','t','t','o','n', 0 }; + WCHAR szButtonClassList2[] = {'b','U','t','T','o','N', 0 }; + WCHAR szClassList[] = {'B','u','t','t','o','n',';','L','i','s','t','B','o','x', 0 }; + + bThemeActive = pIsThemeActive(); + + /* All NULL */ + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(NULL, NULL); + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + todo_wine + ok( GetLastError() == E_POINTER, + "Expected GLE() to be E_POINTER, got 0x%08x\n", + GetLastError()); + + /* A NULL hWnd and an invalid classlist */ + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(NULL, szInvalidClassList); + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + todo_wine + ok( GetLastError() == E_PROP_ID_UNSUPPORTED, + "Expected GLE() to be E_PROP_ID_UNSUPPORTED, got 0x%08x\n", + GetLastError()); + + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(NULL, szClassList); + if (bThemeActive) + { + ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n"); + todo_wine + ok( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got 0x%08x\n", + GetLastError()); + } + else + { + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + todo_wine + ok( GetLastError() == E_PROP_ID_UNSUPPORTED, + "Expected GLE() to be E_PROP_ID_UNSUPPORTED, got 0x%08x\n", + GetLastError()); + } + + /* Only do the bare minumum to get a valid hdc */ + hWnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,0, 0, 0, NULL); + if (!hWnd) return; + + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(hWnd, NULL); + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + todo_wine + ok( GetLastError() == E_POINTER, + "Expected GLE() to be E_POINTER, got 0x%08x\n", + GetLastError()); + + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(hWnd, szInvalidClassList); + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + todo_wine + ok( GetLastError() == E_PROP_ID_UNSUPPORTED, + "Expected GLE() to be E_PROP_ID_UNSUPPORTED, got 0x%08x\n", + GetLastError()); + + if (!bThemeActive) + { + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(hWnd, szButtonClassList); + ok( hTheme == NULL, "Expected a NULL return, got %p\n", hTheme); + todo_wine + ok( GetLastError() == E_PROP_ID_UNSUPPORTED, + "Expected GLE() to be E_PROP_ID_UNSUPPORTED, got 0x%08x\n", + GetLastError()); + trace("No active theme, skipping rest of OpenThemeData tests\n"); + return; + } + + /* Only do the next checks if we have an active theme */ + + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(hWnd, szButtonClassList); + ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n"); + todo_wine + ok( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got 0x%08x\n", + GetLastError()); + + /* Test with bUtToN instead of Button */ + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(hWnd, szButtonClassList2); + ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n"); + todo_wine + ok( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got 0x%08x\n", + GetLastError()); + + SetLastError(0xdeadbeef); + hTheme = pOpenThemeData(hWnd, szClassList); + ok( hTheme != NULL, "got NULL, expected a HTHEME handle\n"); + todo_wine + ok( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got 0x%08x\n", + GetLastError()); + + /* GetWindowTheme should return the last handle opened by OpenThemeData */ + SetLastError(0xdeadbeef); + hTheme2 = pGetWindowTheme(hWnd); + ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n", + hTheme, hTheme2); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + SetLastError(0xdeadbeef); + hRes = pCloseThemeData(hTheme); + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* Close a second time */ + SetLastError(0xdeadbeef); + hRes = pCloseThemeData(hTheme); + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* See if closing makes a difference for GetWindowTheme */ + SetLastError(0xdeadbeef); + hTheme2 = NULL; + hTheme2 = pGetWindowTheme(hWnd); + ok( hTheme == hTheme2, "Expected the same HTHEME handle (%p<->%p)\n", + hTheme, hTheme2); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + SetLastError(0xdeadbeef); + bTPDefined = pIsThemePartDefined(hTheme, 0 , 0); + todo_wine + { + ok( bTPDefined == FALSE, "Expected FALSE\n"); + ok( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got 0x%08x\n", + GetLastError()); + } + + bDestroyed = DestroyWindow(hWnd); + if (!bDestroyed) + trace("Window %p couldn't be destroyed : 0x%08x\n", + hWnd, GetLastError()); +} + +static void test_GetCurrentThemeName(void) +{ + BOOL bThemeActive; + HRESULT hRes; + WCHAR currentTheme[MAX_PATH]; + WCHAR currentColor[MAX_PATH]; + WCHAR currentSize[MAX_PATH]; + + bThemeActive = pIsThemeActive(); + + /* All NULLs */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(NULL, 0, NULL, 0, NULL, 0); + if (bThemeActive) + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* Number of characters given is 0 */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, 0, NULL, 0, NULL, 0); + if (bThemeActive) + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* When the number of characters given is too small (not 0, see above), GetCurrentThemeName returns 0x8007007a. + * The only definition I found was in strsafe.h: + * + * #define STRSAFE_E_INSUFFICIENT_BUFFER ((HRESULT)0x8007007AL) // 0x7A = 122L = ERROR_INSUFFICIENT_BUFFER + */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, 2, NULL, 0, NULL, 0); + if (bThemeActive) + todo_wine + ok( LOWORD(hRes) == ERROR_INSUFFICIENT_BUFFER, "Expected 0x8007007A, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* The same is true if the number of characters is too small for Color and/or Size */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, sizeof(currentTheme) / sizeof(WCHAR), + currentColor, 2, + currentSize, sizeof(currentSize) / sizeof(WCHAR)); + if (bThemeActive) + todo_wine + ok( LOWORD(hRes) == ERROR_INSUFFICIENT_BUFFER, "Expected 0x8007007A, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* Given number of characters is correct */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, sizeof(currentTheme) / sizeof(WCHAR), NULL, 0, NULL, 0); + if (bThemeActive) + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* Given number of characters for the theme name is too large */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, sizeof(currentTheme), NULL, 0, NULL, 0); + todo_wine + ok( hRes == E_POINTER, "Expected E_POINTER, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* The too large case is only for the theme name, not for color name or size name */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, sizeof(currentTheme) / sizeof(WCHAR), + currentColor, sizeof(currentTheme), + currentSize, sizeof(currentSize) / sizeof(WCHAR)); + if (bThemeActive) + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, sizeof(currentTheme) / sizeof(WCHAR), + currentColor, sizeof(currentTheme) / sizeof(WCHAR), + currentSize, sizeof(currentSize)); + if (bThemeActive) + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); + + /* Correct call */ + SetLastError(0xdeadbeef); + hRes = pGetCurrentThemeName(currentTheme, sizeof(currentTheme) / sizeof(WCHAR), + currentColor, sizeof(currentColor) / sizeof(WCHAR), + currentSize, sizeof(currentSize) / sizeof(WCHAR)); + if (bThemeActive) + ok( hRes == S_OK, "Expected S_OK, got 0x%08x\n", hRes); + else + ok( hRes == E_PROP_ID_UNSUPPORTED, "Expected E_PROP_ID_UNSUPPORTED, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); +} + +static void test_CloseThemeData(void) +{ + HRESULT hRes; + + SetLastError(0xdeadbeef); + hRes = pCloseThemeData(NULL); + ok( hRes == E_HANDLE, "Expected E_HANDLE, got 0x%08x\n", hRes); + ok( GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got 0x%08x\n", + GetLastError()); +} + +START_TEST(system) +{ + if(!InitFunctionPtrs()) + return; + + /* No real functional tests will be done (yet). The current tests + * only show input/return behaviour + */ + + /* IsThemeActive, IsAppThemed and IsThemePartDefined*/ + trace("Starting test_IsThemed()\n"); + test_IsThemed(); + + /* GetWindowTheme */ + trace("Starting test_GetWindowTheme()\n"); + test_GetWindowTheme(); + + /* SetWindowTheme */ + trace("Starting test_SetWindowTheme()\n"); + test_SetWindowTheme(); + + /* OpenThemeData, a bit more functional now */ + trace("Starting test_OpenThemeData()\n"); + test_OpenThemeData(); + + /* GetCurrentThemeName */ + trace("Starting test_GetCurrentThemeName()\n"); + test_GetCurrentThemeName(); + + /* CloseThemeData */ + trace("Starting test_CloseThemeData()\n"); + test_CloseThemeData(); + + FreeLibrary(hUxtheme); +} diff --git a/rostests/winetests/uxtheme/testlist.c b/rostests/winetests/uxtheme/testlist.c new file mode 100644 index 00000000000..1b651a6f1f7 --- /dev/null +++ b/rostests/winetests/uxtheme/testlist.c @@ -0,0 +1,15 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_system(void); + +const struct test winetest_testlist[] = +{ + { "system", func_system }, + { 0, 0 } +}; diff --git a/rostests/winetests/uxtheme/uxtheme.rbuild b/rostests/winetests/uxtheme/uxtheme.rbuild new file mode 100644 index 00000000000..1c91cd74204 --- /dev/null +++ b/rostests/winetests/uxtheme/uxtheme.rbuild @@ -0,0 +1,13 @@ + + + + . + 0x600 + 0x600 + wine + user32 + kernel32 + ntdll + system.c + testlist.c + diff --git a/rostests/winetests/version/info.c b/rostests/winetests/version/info.c index 4e421851730..be1852fc024 100644 --- a/rostests/winetests/version/info.c +++ b/rostests/winetests/version/info.c @@ -13,19 +13,20 @@ * * 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 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include +#include -#include "wine/test.h" #include "windef.h" #include "winbase.h" #include "winerror.h" #include "winver.h" +#include "wine/test.h" -#define MY_LAST_ERROR -1L +#define MY_LAST_ERROR ((DWORD)-1) #define EXPECT_BAD_PATH__NOT_FOUND \ ok( (ERROR_PATH_NOT_FOUND == GetLastError()) || \ (ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) || \ @@ -33,7 +34,7 @@ (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()); + "expected, got %u\n", GetLastError()); #define EXPECT_INVALID__NOT_FOUND \ ok( (ERROR_PATH_NOT_FOUND == GetLastError()) || \ (ERROR_RESOURCE_DATA_NOT_FOUND == GetLastError()) || \ @@ -41,15 +42,16 @@ (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()); + "expected, got %u\n", GetLastError()); static void test_info_size(void) { DWORD hdl, retval; + char mypath[MAX_PATH] = ""; SetLastError(MY_LAST_ERROR); retval = GetFileVersionInfoSizeA( NULL, NULL); ok( !retval, - "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n", + "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n", retval); EXPECT_INVALID__NOT_FOUND; @@ -57,16 +59,16 @@ static void test_info_size(void) SetLastError(MY_LAST_ERROR); retval = GetFileVersionInfoSizeA( NULL, &hdl); ok( !retval, - "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n", + "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n", retval); EXPECT_INVALID__NOT_FOUND; ok( hdl == 0L, - "Handle wrong! 0L expected, got 0x%08lx\n", hdl); + "Handle wrong! 0L expected, got 0x%08x\n", hdl); SetLastError(MY_LAST_ERROR); retval = GetFileVersionInfoSizeA( "", NULL); ok( !retval, - "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n", + "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n", retval); EXPECT_BAD_PATH__NOT_FOUND; @@ -74,43 +76,83 @@ static void test_info_size(void) SetLastError(MY_LAST_ERROR); retval = GetFileVersionInfoSizeA( "", &hdl); ok( !retval, - "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n", + "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\n", retval); EXPECT_BAD_PATH__NOT_FOUND; ok( hdl == 0L, - "Handle wrong! 0L expected, got 0x%08lx\n", hdl); + "Handle wrong! 0L expected, got 0x%08x\n", hdl); SetLastError(MY_LAST_ERROR); retval = GetFileVersionInfoSizeA( "kernel32.dll", NULL); ok( retval, - "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08lx\n", + "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n", retval); ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()), - "Last error wrong! NO_ERROR/0x%08lx (NT4) expected, got 0x%08lx\n", + "Last error wrong! NO_ERROR/0x%08x (NT4) expected, got %u\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", + "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n", retval); ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()), - "Last error wrong! NO_ERROR/0x%08lx (NT4) expected, got 0x%08lx\n", + "Last error wrong! NO_ERROR/0x%08x (NT4) expected, got %u\n", MY_LAST_ERROR, GetLastError()); ok( hdl == 0L, - "Handle wrong! 0L expected, got 0x%08lx\n", hdl); + "Handle wrong! 0L expected, got 0x%08x\n", hdl); SetLastError(MY_LAST_ERROR); retval = GetFileVersionInfoSizeA( "notexist.dll", NULL); ok( !retval, - "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08lx\n", + "GetFileVersionInfoSizeA result wrong! 0L expected, got 0x%08x\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()); + "(XP)/0x%08x (NT4) expected, got %u\n", MY_LAST_ERROR, GetLastError()); + + /* test a currently loaded executable */ + if(GetModuleFileNameA(NULL, mypath, MAX_PATH)) { + hdl = 0x55555555; + SetLastError(MY_LAST_ERROR); + retval = GetFileVersionInfoSizeA( mypath, &hdl); + ok( retval, + "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n", + retval); + ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()), + "Last error wrong! NO_ERROR/0x%08x (NT4) expected, got %u\n", + MY_LAST_ERROR, GetLastError()); + ok( hdl == 0L, + "Handle wrong! 0L expected, got 0x%08x\n", hdl); + } + else + trace("skipping GetModuleFileNameA(NULL,..) failed\n"); + + /* test a not loaded executable */ + if(GetSystemDirectoryA(mypath, MAX_PATH)) { + lstrcatA(mypath, "\\regsvr32.exe"); + + if(INVALID_FILE_ATTRIBUTES == GetFileAttributesA(mypath)) + trace("GetFileAttributesA(%s) failed\n", mypath); + else { + hdl = 0x55555555; + SetLastError(MY_LAST_ERROR); + retval = GetFileVersionInfoSizeA( mypath, &hdl); + ok( retval, + "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n", + retval); + ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()), + "Last error wrong! NO_ERROR/0x%08x (NT4) expected, got %u\n", + MY_LAST_ERROR, GetLastError()); + ok( hdl == 0L, + "Handle wrong! 0L expected, got 0x%08x\n", hdl); + } + } + else + trace("skipping GetModuleFileNameA(NULL,..) failed\n"); } static void VersionDwordLong2String(DWORDLONG Version, LPSTR lpszVerString) @@ -135,19 +177,20 @@ static void test_info(void) VS_FIXEDFILEINFO *pFixedVersionInfo; UINT uiLength; char VersionString[MAX_PATH]; + static CHAR backslash[] = "\\"; DWORDLONG dwlVersion; hdl = 0x55555555; SetLastError(MY_LAST_ERROR); retval = GetFileVersionInfoSizeA( "kernel32.dll", &hdl); ok( retval, - "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08lx\n", + "GetFileVersionInfoSizeA result wrong! <> 0L expected, got 0x%08x\n", retval); ok((NO_ERROR == GetLastError()) || (MY_LAST_ERROR == GetLastError()), - "Last error wrong! NO_ERROR/0x%08lx (NT4) expected, got 0x%08lx\n", + "Last error wrong! NO_ERROR/0x%08x (NT4) expected, got %u\n", MY_LAST_ERROR, GetLastError()); ok( hdl == 0L, - "Handle wrong! 0L expected, got 0x%08lx\n", hdl); + "Handle wrong! 0L expected, got 0x%08x\n", hdl); if ( retval == 0 || hdl != 0) return; @@ -157,21 +200,28 @@ static void test_info(void) if (pVersionInfo == 0) return; + if (0) + { + /* this test crashes on WinNT4 + */ 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", + ok (!boolret, "GetFileVersionInfoA should have failed: GetLastError = %u\n", GetLastError()); + ok ((GetLastError() == ERROR_INVALID_DATA) || (GetLastError() == ERROR_BAD_PATHNAME) || + (GetLastError() == NO_ERROR), + "Last error wrong! ERROR_INVALID_DATA/ERROR_BAD_PATHNAME (ME)/" + "NO_ERROR (95) expected, got %u\n", GetLastError()); + } boolret = GetFileVersionInfoA( "kernel32.dll", 0, retval, pVersionInfo ); - ok (boolret, "GetFileVersionInfoA failed: GetLastError = 0x%08lx\n", GetLastError()); + ok (boolret, "GetFileVersionInfoA failed: GetLastError = %u\n", GetLastError()); if (!boolret) - return; + goto cleanup; - boolret = VerQueryValueA( pVersionInfo, "\\", (LPVOID *)&pFixedVersionInfo, &uiLength ); - ok (boolret, "VerQueryValueA failed: GetLastError = 0x%08lx\n", GetLastError()); + boolret = VerQueryValueA( pVersionInfo, backslash, (LPVOID *)&pFixedVersionInfo, &uiLength ); + ok (boolret, "VerQueryValueA failed: GetLastError = %u\n", GetLastError()); if (!boolret) - return; + goto cleanup; dwlVersion = (((DWORDLONG)pFixedVersionInfo->dwFileVersionMS) << 32) + pFixedVersionInfo->dwFileVersionLS; @@ -180,12 +230,306 @@ static void test_info(void) trace("kernel32.dll version: %s\n", VersionString); - boolret = VerQueryValueA( pVersionInfo, "\\", (LPVOID *)&pFixedVersionInfo, 0); - ok (boolret, "VerQueryValue failed: GetLastError = 0x%08lx\n", GetLastError()); + if (0) + { + /* this test crashes on WinNT4 + */ + boolret = VerQueryValueA( pVersionInfo, backslash, (LPVOID *)&pFixedVersionInfo, 0); + ok (boolret, "VerQueryValue failed: GetLastError = %u\n", GetLastError()); + } + +cleanup: + HeapFree( GetProcessHeap(), 0, pVersionInfo); +} + +static void test_32bit_win(void) +{ + DWORD hdlA, retvalA; + DWORD hdlW, retvalW = 0; + BOOL retA,retW; + PVOID pVersionInfoA = NULL; + PVOID pVersionInfoW = NULL; + char *pBufA; + WCHAR *pBufW; + UINT uiLengthA, uiLengthW; + char mypathA[MAX_PATH]; + WCHAR mypathW[MAX_PATH]; + char rootA[] = "\\"; + WCHAR rootW[] = { '\\', 0 }; + char varfileinfoA[] = "\\VarFileInfo\\Translation"; + WCHAR varfileinfoW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o', + '\\','T','r','a','n','s','l','a','t','i','o','n', 0 }; + char WineVarFileInfoA[] = { 0x09, 0x04, 0xE4, 0x04 }; + char FileDescriptionA[] = "\\StringFileInfo\\040904E4\\FileDescription"; + WCHAR FileDescriptionW[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o', + '\\','0','4','0','9','0','4','E','4', + '\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n', 0 }; + char WineFileDescriptionA[] = "FileDescription"; + WCHAR WineFileDescriptionW[] = { 'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n', 0 }; + BOOL is_unicode_enabled = TRUE; + + /* A copy from dlls/version/info.c */ + typedef struct + { + WORD wLength; + WORD wValueLength; + WORD wType; + WCHAR szKey[1]; +#if 0 /* variable length structure */ + /* DWORD aligned */ + BYTE Value[]; + /* DWORD aligned */ + VS_VERSION_INFO_STRUCT32 Children[]; +#endif + } VS_VERSION_INFO_STRUCT32; + + /* If we call GetFileVersionInfoA on a system that supports Unicode, NT/W2K/XP/W2K3 (by default) and Wine, + * the versioninfo will contain Unicode strings. + * Part of the test is to call both the A and W versions, which should have the same Version Information + * for some requests, on systems that support both calls. + */ + + /* First get the versioninfo via the W versions */ + SetLastError(0xdeadbeef); + GetModuleFileNameW(NULL, mypathW, MAX_PATH); + if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + trace("GetModuleFileNameW not existing on this platform, skipping comparison between A- and W-calls\n"); + is_unicode_enabled = FALSE; + } + + if (is_unicode_enabled) + { + retvalW = GetFileVersionInfoSizeW( mypathW, &hdlW); + pVersionInfoW = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, retvalW ); + retW = GetFileVersionInfoW( mypathW, 0, retvalW, pVersionInfoW ); + } + + GetModuleFileNameA(NULL, mypathA, MAX_PATH); + retvalA = GetFileVersionInfoSizeA( mypathA, &hdlA); + pVersionInfoA = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, retvalA ); + retA = GetFileVersionInfoA( mypathA, 0, retvalA, pVersionInfoA ); + + if (is_unicode_enabled) + { + ok( retvalA == retvalW, "The size of the struct should be the same for both A/W calls, it is (%d) vs. (%d)\n", + retvalA, retvalW); + ok( !memcmp(pVersionInfoA, pVersionInfoW, retvalA), "Both structs should be the same, they aren't\n"); + } + + /* The structs on Windows are bigger than just the struct for the basic information. The total struct + * contains also an empty part, which is used for converted strings. The converted strings are a result + * of calling VerQueryValueA on a 32bit resource and calling VerQueryValueW on a 16bit resource. + * The first WORD of the structure (wLength) shows the size of the base struct. The total struct size depends + * on the Windows version: + * + * 16bits resource (numbers are from a sample app): + * + * Windows Version Retrieved with A/W wLength StructSize + * ==================================================================================== + * Win98 A 0x01B4 (436) 436 + * NT4 A/W 0x01B4 (436) 2048 ??? + * W2K/XP/W2K3 A/W 0x01B4 (436) 1536 which is (436 - sizeof(VS_FIXEDFILEINFO)) * 4 + * + * 32bits resource (numbers are from this test executable version_crosstest.exe): + * Windows Version Retrieved with A/W wLength StructSize + * ============================================================= + * Win98 A 0x01E0 (480) 848 (structure data doesn't seem correct) + * NT4 A/W 0x0350 (848) 1272 (848 * 1.5) + * W2K/XP/W2K3 A/W 0x0350 (848) 1700 which is (848 * 2) + 4 + * + * Wine will follow the implementation (eventually) of W2K/XP/W2K3 + */ + + /* Now some tests for the above (only if we are unicode enabled) */ + + if (is_unicode_enabled) + { + VS_VERSION_INFO_STRUCT32 *vvis = (VS_VERSION_INFO_STRUCT32 *)pVersionInfoW; + ok ( retvalW == ((vvis->wLength * 2) + 4) || retvalW == (vvis->wLength * 1.5), + "Structure is not of the correct size\n"); + } + + /* Although the 32bit resource structures contain Unicode strings, VerQueryValueA will always return normal strings, + * VerQueryValueW will always return Unicode ones. (That means everything returned for StringFileInfo requests). + */ + + /* Get the VS_FIXEDFILEINFO information, this must be the same for both A- and W-Calls */ + + retA = VerQueryValueA( pVersionInfoA, rootA, (LPVOID *)&pBufA, &uiLengthA ); + ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError()); + ok ( uiLengthA == sizeof(VS_FIXEDFILEINFO), "Size (%d) doesn't match the size of the VS_FIXEDFILEINFO struct\n", uiLengthA); + + if (is_unicode_enabled) + { + retW = VerQueryValueW( pVersionInfoW, rootW, (LPVOID *)&pBufW, &uiLengthW ); + ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError()); + ok ( uiLengthA == sizeof(VS_FIXEDFILEINFO), "Size (%d) doesn't match the size of the VS_FIXEDFILEINFO struct\n", uiLengthA); + + ok( uiLengthA == uiLengthW, "The size of VS_FIXEDFILEINFO should be the same for both A/W calls, it is (%d) vs. (%d)\n", + uiLengthA, uiLengthW); + ok( !memcmp(pBufA, pBufW, uiLengthA), "Both values should be the same, they aren't\n"); + } + + /* Get some VarFileInfo information, this must be the same for both A- and W-Calls */ + + retA = VerQueryValueA( pVersionInfoA, varfileinfoA, (LPVOID *)&pBufA, &uiLengthA ); + ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError()); + ok( !memcmp(pBufA, WineVarFileInfoA, uiLengthA), "The VarFileInfo should have matched 0904e404 (non case sensitive)\n"); + + if (is_unicode_enabled) + { + retW = VerQueryValueW( pVersionInfoW, varfileinfoW, (LPVOID *)&pBufW, &uiLengthW ); + ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError()); + ok( uiLengthA == uiLengthW, "The size of the VarFileInfo information should be the same for both A/W calls, it is (%d) vs. (%d)\n", + uiLengthA, uiLengthW); + ok( !memcmp(pBufA, pBufW, uiLengthA), "Both values should be the same, they aren't\n"); + } + + /* Get some StringFileInfo information, this will be ANSI for A-Calls and Unicode for W-Calls */ + + retA = VerQueryValueA( pVersionInfoA, FileDescriptionA, (LPVOID *)&pBufA, &uiLengthA ); + ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError()); + ok( !lstrcmpA(WineFileDescriptionA, pBufA), "expected '%s' got '%s'\n", + WineFileDescriptionA, pBufA); + + /* Test a second time */ + retA = VerQueryValueA( pVersionInfoA, FileDescriptionA, (LPVOID *)&pBufA, &uiLengthA ); + ok (retA, "VerQueryValueA failed: GetLastError = %u\n", GetLastError()); + ok( !lstrcmpA(WineFileDescriptionA, pBufA), "expected '%s' got '%s'\n", + WineFileDescriptionA, pBufA); + + if (is_unicode_enabled) + { + retW = VerQueryValueW( pVersionInfoW, FileDescriptionW, (LPVOID *)&pBufW, &uiLengthW ); + ok (retW, "VerQueryValueW failed: GetLastError = %u\n", GetLastError()); + ok( !lstrcmpW(WineFileDescriptionW, pBufW), "FileDescription should have been '%s'\n", WineFileDescriptionA); + } + + HeapFree( GetProcessHeap(), 0, pVersionInfoA); + if (is_unicode_enabled) + HeapFree( GetProcessHeap(), 0, pVersionInfoW); +} + +static void test_VerQueryValue(void) +{ + static const char * const value_name[] = { + "Product", "CompanyName", "FileDescription", "Internal", + "ProductVersion", "InternalName", "File", "LegalCopyright", + "FileVersion", "Legal", "OriginalFilename", "ProductName", + "Company", "Original" }; + char *ver, *p; + UINT len, ret, translation, i; + char buf[MAX_PATH]; + + ret = GetModuleFileName(NULL, buf, sizeof(buf)); + assert(ret); + + SetLastError(0xdeadbeef); + len = GetFileVersionInfoSize(buf, NULL); + ok(len, "GetFileVersionInfoSize(%s) error %u\n", buf, GetLastError()); + + ver = HeapAlloc(GetProcessHeap(), 0, len); + assert(ver); + + SetLastError(0xdeadbeef); + ret = GetFileVersionInfo(buf, 0, len, ver); + ok(ret, "GetFileVersionInfo error %u\n", GetLastError()); + + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, "\\VarFileInfo\\Translation", (LPVOID*)&p, &len); + ok(ret, "VerQueryValue error %u\n", GetLastError()); + ok(len == 4, "VerQueryValue returned %u, expected 4\n", len); + + translation = *(UINT *)p; + translation = MAKELONG(HIWORD(translation), LOWORD(translation)); + + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, "String", (LPVOID*)&p, &len); + ok(!ret, "VerQueryValue should fail\n"); + ok(GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND, + "VerQueryValue returned %u\n", GetLastError()); + ok(p == (char *)0xdeadbeef, "expected 0xdeadbeef got %p\n", p); + ok(len == 0, "expected 0 got %x\n", len); + + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, "StringFileInfo", (LPVOID*)&p, &len); + ok(ret, "VerQueryValue error %u\n", GetLastError()); +todo_wine ok(len == 0, "VerQueryValue returned %u, expected 0\n", len); + ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n"); + + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, "\\StringFileInfo", (LPVOID*)&p, &len); + ok(ret, "VerQueryValue error %u\n", GetLastError()); +todo_wine ok(len == 0, "VerQueryValue returned %u, expected 0\n", len); + ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n"); + + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, "\\\\StringFileInfo", (LPVOID*)&p, &len); + ok(ret, "VerQueryValue error %u\n", GetLastError()); +todo_wine ok(len == 0, "VerQueryValue returned %u, expected 0\n", len); + ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n"); + + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, "\\StringFileInfo\\\\", (LPVOID*)&p, &len); + ok(ret, "VerQueryValue error %u\n", GetLastError()); +todo_wine ok(len == 0, "VerQueryValue returned %u, expected 0\n", len); + ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n"); + + sprintf(buf, "\\StringFileInfo\\%08x", translation); + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, buf, (LPVOID*)&p, &len); + ok(ret, "VerQueryValue error %u\n", GetLastError()); +todo_wine ok(len == 0, "VerQueryValue returned %u, expected 0\n", len); + ok(p != (char *)0xdeadbeef, "not expected 0xdeadbeef\n"); + + for (i = 0; i < sizeof(value_name)/sizeof(value_name[0]); i++) + { + sprintf(buf, "\\StringFileInfo\\%08x\\%s", translation, value_name[i]); + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, buf, (LPVOID*)&p, &len); + ok(ret, "VerQueryValue(%s) error %u\n", buf, GetLastError()); + ok(len == strlen(value_name[i]) + 1, "VerQueryValue returned %u\n", len); + ok(!strcmp(value_name[i], p), "expected \"%s\", got \"%s\"\n", + value_name[i], p); + + /* test partial value names */ + len = lstrlen(buf); + buf[len - 2] = 0; + p = (char *)0xdeadbeef; + len = 0xdeadbeef; + SetLastError(0xdeadbeef); + ret = VerQueryValue(ver, buf, (LPVOID*)&p, &len); + ok(!ret, "VerQueryValue(%s) succeeded\n", buf); + ok(GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND, + "VerQueryValue returned %u\n", GetLastError()); + ok(p == (char *)0xdeadbeef, "expected 0xdeadbeef got %p\n", p); + ok(len == 0, "expected 0 got %x\n", len); + } + + HeapFree(GetProcessHeap(), 0, ver); } START_TEST(info) { test_info_size(); test_info(); + test_32bit_win(); + test_VerQueryValue(); } diff --git a/rostests/winetests/version/install.c b/rostests/winetests/version/install.c new file mode 100644 index 00000000000..ada860ca4cf --- /dev/null +++ b/rostests/winetests/version/install.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "wine/test.h" +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winver.h" + +static void test_find_file(void) +{ + DWORD ret; + UINT dwCur, dwOut ; + char appdir[MAX_PATH]; + char curdir[MAX_PATH]; + char filename[MAX_PATH]; + char outBuf[MAX_PATH]; + char windir[MAX_PATH]; + static CHAR empty[] = "", + regedit[] = "regedit", + regedit_exe[] = "regedit.exe"; + + memset(appdir, 0, MAX_PATH); + memset(windir, 0, MAX_PATH); + + dwCur=MAX_PATH; + dwOut=MAX_PATH; + memset(curdir, 0, MAX_PATH); + memset(outBuf, 0, MAX_PATH); + ret = VerFindFileA(0, regedit, empty, empty, curdir, &dwCur, outBuf, &dwOut); + switch(ret) { + case 0L: + ok(dwCur == 1, "Wrong length of buffer for current location: " + "got %d(%s) expected 1\n", dwCur, curdir); + ok(dwOut == 1, "Wrong length of buffer for the recommended installation location: " + "got %d(%s) expected 1\n", dwOut, outBuf); + break; + case VFF_BUFFTOOSMALL: + ok(dwCur == MAX_PATH, "Wrong length of buffer for current location: " + "got %d(%s) expected MAX_PATH\n", dwCur, curdir); + ok(dwOut == MAX_PATH, "Wrong length of buffer for the recommended installation location: " + "got %d(%s) expected MAX_PATH\n", dwOut, outBuf); + break; + default: + ok(0, "Got unexpected return value %x\n", ret); + } + + if(!GetWindowsDirectoryA(windir, MAX_PATH)) + trace("GetWindowsDirectoryA failed\n"); + else { + sprintf(appdir, "%s\\regedit.exe", windir); + if(INVALID_FILE_ATTRIBUTES == GetFileAttributesA(appdir)) + trace("GetFileAttributesA(%s) failed\n", appdir); + else { + dwCur=MAX_PATH; + dwOut=MAX_PATH; + memset(curdir, 0, MAX_PATH); + memset(outBuf, 0, MAX_PATH); + ret = VerFindFileA(0, regedit_exe, empty, empty, curdir, &dwCur, outBuf, &dwOut); + switch(ret) { + case VFF_CURNEDEST: + ok(dwCur == 1 + strlen(windir), "Wrong length of buffer for current location: " + "got %d(%s) expected %d\n", dwCur, curdir, lstrlenA(windir)+1); + ok(dwOut == 1, "Wrong length of buffer for the recommended installation location: " + "got %d(%s) expected 1\n", dwOut, outBuf); + break; + case VFF_BUFFTOOSMALL: + ok(dwCur == MAX_PATH, "Wrong length of buffer for current location: " + "got %d(%s) expected MAX_PATH\n", dwCur, curdir); + ok(dwOut == MAX_PATH, "Wrong length of buffer for the recommended installation location: " + "got %d(%s) expected MAX_PATH\n", dwOut, outBuf); + break; + default: + todo_wine ok(0, "Got unexpected return value %x\n", ret); + } + + dwCur=MAX_PATH; + dwOut=MAX_PATH; + memset(curdir, 0, MAX_PATH); + memset(outBuf, 0, MAX_PATH); + ret = VerFindFileA(0, regedit_exe, NULL, NULL, curdir, &dwCur, outBuf, &dwOut); + switch(ret) { + case VFF_CURNEDEST: + ok(dwCur == 1 + strlen(windir), "Wrong length of buffer for current location: " + "got %d(%s) expected %d\n", dwCur, curdir, lstrlenA(windir)+1); + ok(dwOut == 1, "Wrong length of buffer for the recommended installation location: " + "got %d(%s) expected 1\n", dwOut, outBuf); + break; + case VFF_BUFFTOOSMALL: + ok(dwCur == MAX_PATH, "Wrong length of buffer for current location: " + "got %d(%s) expected MAX_PATH\n", dwCur, curdir); + ok(dwOut == MAX_PATH, "Wrong length of buffer for the recommended installation location: " + "got %d(%s) expected MAX_PATH\n", dwOut, outBuf); + break; + default: + todo_wine ok(0, "Got unexpected return value %x\n", ret); + } + } + } + if(!GetModuleFileNameA(NULL, filename, MAX_PATH) || + !GetSystemDirectoryA(windir, MAX_PATH) || + !GetTempPathA(MAX_PATH, appdir)) + trace("GetModuleFileNameA, GetSystemDirectoryA or GetTempPathA failed\n"); + else { + char *p = strrchr(filename, '\\'); + if(p) { + *(p++) ='\0'; + SetCurrentDirectoryA(filename); + memmove(filename, p, 1 + strlen(p)); + } + + dwCur=MAX_PATH; + dwOut=MAX_PATH; + memset(outBuf, 0, MAX_PATH); + memset(curdir, 0, MAX_PATH); + ret = VerFindFileA(0, filename, NULL, NULL, curdir, &dwCur, outBuf, &dwOut); + switch(ret) { + case VFF_CURNEDEST: + ok(dwOut == 1, "Wrong length of buffer for the recommended installation location" + "got %d(%s) expected 1\n", dwOut, outBuf); + break; + case VFF_BUFFTOOSMALL: + ok(dwOut == MAX_PATH, "Wrong length of buffer for the recommended installation location" + "got %d(%s) expected MAX_PATH\n", dwOut, outBuf); + break; + default: + todo_wine ok(0, "Got unexpected return value %x\n", ret); + } + + dwCur=MAX_PATH; + dwOut=MAX_PATH; + memset(outBuf, 0, MAX_PATH); + memset(curdir, 0, MAX_PATH); + ret = VerFindFileA(VFFF_ISSHAREDFILE, filename, NULL, appdir, curdir, &dwCur, outBuf, &dwOut); + todo_wine ok(VFF_CURNEDEST == ret, "Wrong return value got %x expected VFF_CURNEDEST\n", ret); + ok(dwOut == 1 + strlen(windir), "Wrong length of buffer for current location: " + "got %d(%s) expected %d\n", dwOut, outBuf, lstrlenA(windir)+1); + + dwCur=MAX_PATH; + dwOut=MAX_PATH; + memset(outBuf, 0, MAX_PATH); + memset(curdir, 0, MAX_PATH); + ret = VerFindFileA(0, filename, NULL, appdir, curdir, &dwCur, outBuf, &dwOut); + todo_wine ok(VFF_CURNEDEST == ret, "Wrong return value got %x expected VFF_CURNEDEST\n", ret); + ok(dwOut == 1 + strlen(appdir), "Wrong length of buffer for current location: " + "got %d(%s) expected %d\n", dwOut, outBuf, lstrlenA(appdir)+1); + } +} + +START_TEST(install) +{ + test_find_file(); +} diff --git a/rostests/winetests/version/testlist.c b/rostests/winetests/version/testlist.c index ec33baf6750..c73df287383 100644 --- a/rostests/winetests/version/testlist.c +++ b/rostests/winetests/version/testlist.c @@ -12,6 +12,6 @@ extern void func_install(void); const struct test winetest_testlist[] = { { "info", func_info }, -// { "install", func_install }, + { "install", func_install }, { 0, 0 } }; diff --git a/rostests/winetests/version/version.rbuild b/rostests/winetests/version/version.rbuild index 3fc14d46aac..5846259b24a 100644 --- a/rostests/winetests/version/version.rbuild +++ b/rostests/winetests/version/version.rbuild @@ -1,9 +1,15 @@ - - . - - ntdll - version - kernel32 - testlist.c - info.c + + + + . + 0x600 + 0x600 + wine + version + kernel32 + ntdll + info.c + install.c + version.rc + testlist.c diff --git a/rostests/winetests/version/version.rc b/rostests/winetests/version/version.rc new file mode 100644 index 00000000000..c754e0c35a7 --- /dev/null +++ b/rostests/winetests/version/version.rc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "windef.h" +#include "winbase.h" +#include "winver.h" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +FILEFLAGSMASK 63 +FILEFLAGS 0 +FILEOS VOS_UNKNOWN +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" /* LANG_ENGLISH/SUBLANG_DEFAULT, CP1252 */ + { + /* list of strings that partially match in arbitrary order */ + VALUE "Product", "Product" + VALUE "CompanyName", "CompanyName" + VALUE "FileDescription", "FileDescription" + VALUE "Internal", "Internal" + VALUE "ProductVersion", "ProductVersion" + VALUE "InternalName", "InternalName" + VALUE "File", "File" + VALUE "LegalCopyright", "LegalCopyright" + VALUE "FileVersion", "FileVersion" + VALUE "Legal", "Legal" + VALUE "OriginalFilename", "OriginalFilename" + VALUE "ProductName", "ProductName" + VALUE "Company", "Company" + VALUE "Original", "Original" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409,1252 /* LANG_ENGLISH/SUBLANG_DEFAULT, CP1252 */ + } +} diff --git a/rostests/winetests/wininet/ftp.c b/rostests/winetests/wininet/ftp.c new file mode 100644 index 00000000000..6d03fb86dcb --- /dev/null +++ b/rostests/winetests/wininet/ftp.c @@ -0,0 +1,740 @@ +/* + * Wininet - ftp tests + * + * Copyright 2007 Paul Vriens + * Copyright 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * FIXME: + * Use InternetGetLastResponseInfo when the last error is set to ERROR_INTERNET_EXTENDED_ERROR. + * TODO: + * Add W-function tests. + * Add missing function tests: + * FtpFindFirstFile + * FtpGetCurrentDirectory + * FtpGetFileSize + * FtpSetCurrentDirectory + */ + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wininet.h" +#include "winsock.h" + +#include "wine/test.h" + +static void test_getfile_no_open(void) +{ + BOOL bRet; + + /* Invalid internet handle, the others are valid parameters */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(NULL, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_NOT_INITIALIZED || + GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INTERNET_NOT_INITIALIZED or ERROR_INVALID_HANDLE (win98), got %d\n", GetLastError()); +} + +static void test_connect(HINTERNET hInternet) +{ + HINTERNET hFtp; + + /* Try a few username/password combinations: + * anonymous : NULL + * NULL : IEUser@ + * NULL : NULL + */ + + SetLastError(0xdeadbeef); + hFtp = InternetConnect(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "anonymous", NULL, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); + if (hFtp) /* some servers accept an empty password */ + { + ok ( GetLastError() == ERROR_SUCCESS, "ERROR_SUCCESS, got %d\n", GetLastError()); + InternetCloseHandle(hFtp); + } + else + ok ( GetLastError() == ERROR_INTERNET_LOGIN_FAILURE, + "Expected ERROR_INTERNET_LOGIN_FAILURE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + hFtp = InternetConnect(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, NULL, "IEUser@", INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); + ok ( hFtp == NULL, "Expected InternetConnect to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Using a NULL username and password will be interpreted as anonymous ftp. The username will be 'anonymous' the password + * is created via some simple heuristics (see dlls/wininet/ftp.c). + * On Wine this registry key is not set by default so (NULL, NULL) will result in anonymous ftp with an (most likely) not + * accepted password (the username). + * If the first call fails because we get an ERROR_INTERNET_LOGIN_FAILURE, we try again with a (more) correct password. + */ + + SetLastError(0xdeadbeef); + hFtp = InternetConnect(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, NULL, NULL, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); + if (!hFtp && (GetLastError() == ERROR_INTERNET_LOGIN_FAILURE)) + { + /* We are most likely running on a clean Wine install or a Windows install where the registry key is removed */ + SetLastError(0xdeadbeef); + hFtp = InternetConnect(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "anonymous", "IEUser@", INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); + } + ok ( hFtp != NULL, "InternetConnect failed : %d\n", GetLastError()); + ok ( GetLastError() == ERROR_SUCCESS, + "ERROR_SUCCESS, got %d\n", GetLastError()); +} + +static void test_createdir(HINTERNET hFtp, HINTERNET hConnect) +{ + BOOL bRet; + + /* Invalid internet handle, the other is a valid parameter */ + SetLastError(0xdeadbeef); + bRet = FtpCreateDirectoryA(NULL, "new_directory_deadbeef"); + ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + /* No directory-name */ + SetLastError(0xdeadbeef); + bRet = FtpCreateDirectoryA(hFtp, NULL); + ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Parameters are OK, but we shouldn't be allowed to create the directory */ + SetLastError(0xdeadbeef); + bRet = FtpCreateDirectoryA(hFtp, "new_directory_deadbeef"); + ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + + /* One small test to show that handle type is checked before parameters */ + SetLastError(0xdeadbeef); + bRet = FtpCreateDirectoryA(hConnect, NULL); + ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpCreateDirectoryA(hConnect, "new_directory_deadbeef"); + ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); +} + +static void test_deletefile(HINTERNET hFtp, HINTERNET hConnect) +{ + BOOL bRet; + + /* Invalid internet handle, the other is a valid parameter */ + SetLastError(0xdeadbeef); + bRet = FtpDeleteFileA(NULL, "non_existent_file_deadbeef"); + ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + /* No filename */ + SetLastError(0xdeadbeef); + bRet = FtpDeleteFileA(hFtp, NULL); + ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Parameters are OK but remote file should not be there */ + SetLastError(0xdeadbeef); + bRet = FtpDeleteFileA(hFtp, "non_existent_file_deadbeef"); + ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + + /* One small test to show that handle type is checked before parameters */ + SetLastError(0xdeadbeef); + bRet = FtpDeleteFileA(hConnect, NULL); + ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpDeleteFileA(hConnect, "non_existent_file_deadbeef"); + ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); +} + +static void test_getfile(HINTERNET hFtp, HINTERNET hConnect) +{ + BOOL bRet; + HANDLE hFile; + + /* The order of checking is: + * + * All parameters except 'session handle' and 'condition flags' + * Session handle + * Session handle type + * Condition flags + */ + + /* Test to show the parameter checking order depends on the Windows version */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(NULL, NULL, "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE || + GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_HANDLE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Test to show session handle is checked before 'condition flags' */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(NULL, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, 5, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + /* Make sure we start clean */ + + DeleteFileA("should_be_non_existing_deadbeef"); + DeleteFileA("should_also_be_non_existing_deadbeef"); + + /* No remote file */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, NULL, "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + ok (GetFileAttributesA("should_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES, + "Local file should not have been created\n"); + DeleteFileA("should_be_non_existing_deadbeef"); + + /* No local file */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "welcome.msg", NULL, FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Zero attributes */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_existing_non_deadbeef", FALSE, 0, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == TRUE, "Expected FtpGetFileA to succeed\n"); + ok (GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", GetLastError()); + ok (GetFileAttributesA("should_be_existing_non_deadbeef") != INVALID_FILE_ATTRIBUTES, + "Local file should have been created\n"); + DeleteFileA("should_be_existing_non_deadbeef"); + + /* Illegal condition flags */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, 0xffffffff, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR || GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INTERNET_EXTENDED_ERROR or ERROR_INVALID_PARAMETER (win98), got %d\n", GetLastError()); + ok (GetFileAttributesA("should_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES, + "Local file should not have been created\n"); + DeleteFileA("should_be_non_existing_deadbeef"); + + /* Remote file doesn't exist (and local doesn't exist as well) */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "should_be_non_existing_deadbeef", "should_also_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + /* Currently Wine always creates the local file (even on failure) which is not correct, hence the test */ + todo_wine + ok (GetFileAttributesA("should_also_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES, + "Local file should not have been created\n"); + + DeleteFileA("should_also_be_non_existing_deadbeef"); + + /* Same call as the previous but now the local file does exists. Windows just removes the file if the call fails + * even if the local existed before! + */ + + /* Create a temporary local file */ + SetLastError(0xdeadbeef); + hFile = CreateFileA("should_also_be_non_existing_deadbeef", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + ok ( hFile != NULL, "Error creating a local file : %d\n", GetLastError()); + CloseHandle(hFile); + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "should_be_non_existing_deadbeef", "should_also_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + /* Currently Wine always creates the local file (even on failure) which is not correct, hence the test */ + todo_wine + ok (GetFileAttributesA("should_also_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES, + "Local file should not have been created\n"); + + DeleteFileA("should_also_be_non_existing_deadbeef"); + + /* This one should succeed */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_existing_non_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == TRUE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", GetLastError()); + + if (GetFileAttributesA("should_be_existing_non_deadbeef") != INVALID_FILE_ATTRIBUTES) + { + /* Should succeed as fFailIfExists is set to FALSE (meaning don't fail if local file exists) */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == TRUE, "Expected FtpGetFileA to succeed\n"); + ok ( GetLastError() == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got %d\n", GetLastError()); + + /* Should fail as fFailIfExists is set to TRUE */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", TRUE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_FILE_EXISTS, + "Expected ERROR_FILE_EXISTS, got %d\n", GetLastError()); + + /* Prove that the existence of the local file is checked first (or at least reported last) */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "should_be_non_existing_deadbeef", "should_be_non_existing_deadbeef", TRUE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_FILE_EXISTS, + "Expected ERROR_FILE_EXISTS, got %d\n", GetLastError()); + + DeleteFileA("should_be_existing_non_deadbeef"); + } + + /* Test to show the parameter checking order depends on the Windows version */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hConnect, NULL, "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE || + GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Test to show that 'session handle type' is checked before 'condition flags' */ + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hConnect, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, 5, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hConnect, "should_be_non_existing_deadbeef", "should_be_non_existing_deadbeef", TRUE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); +} + +static void test_openfile(HINTERNET hFtp, HINTERNET hConnect) +{ + HINTERNET hOpenFile; + + /* Invalid internet handle, the rest are valid parameters */ + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(NULL, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0); + ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + InternetCloseHandle(hOpenFile); /* Just in case */ + + /* No filename */ + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(hFtp, NULL, GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0); + ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + InternetCloseHandle(hOpenFile); /* Just in case */ + + /* Illegal access flags */ + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", 0, FTP_TRANSFER_TYPE_ASCII, 0); + ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + InternetCloseHandle(hOpenFile); /* Just in case */ + + /* Illegal combination of access flags */ + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ|GENERIC_WRITE, FTP_TRANSFER_TYPE_ASCII, 0); + ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + InternetCloseHandle(hOpenFile); /* Just in case */ + + /* Illegal condition flags */ + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ, 0xffffffff, 0); + ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR || GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INTERNET_EXTENDED_ERROR or ERROR_INVALID_PARAMETER (win98), got %d\n", GetLastError()); + InternetCloseHandle(hOpenFile); /* Just in case */ + + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0); + ok ( hOpenFile != NULL, "Expected FtpOpenFileA to succeed\n"); + ok ( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", GetLastError()); + + if (hOpenFile) + { + BOOL bRet; + HINTERNET hOpenFile2; + HANDLE hFile; + + /* We have a handle so all ftp calls should fail (TODO: Put all ftp-calls in here) */ + SetLastError(0xdeadbeef); + bRet = FtpCreateDirectoryA(hFtp, "new_directory_deadbeef"); + ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS, + "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpDeleteFileA(hFtp, "non_existent_file_deadbeef"); + ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n"); + ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS, + "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n"); + ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS, + "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError()); + DeleteFileA("should_be_non_existing_deadbeef"); /* Just in case */ + + SetLastError(0xdeadbeef); + hOpenFile2 = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0); + ok ( bRet == FALSE, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS, + "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError()); + InternetCloseHandle(hOpenFile2); /* Just in case */ + + /* Create a temporary local file */ + SetLastError(0xdeadbeef); + hFile = CreateFileA("now_existing_local", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + ok ( hFile != NULL, "Error creating a local file : %d\n", GetLastError()); + CloseHandle(hFile); + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hFtp, "now_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS, + "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError()); + DeleteFileA("now_existing_local"); + + SetLastError(0xdeadbeef); + bRet = FtpRemoveDirectoryA(hFtp, "should_be_non_existing_deadbeef_dir"); + ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS, + "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpRenameFileA(hFtp , "should_be_non_existing_deadbeef", "new"); + ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n"); + ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS, + "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError()); + } + + InternetCloseHandle(hOpenFile); + + /* One small test to show that handle type is checked before parameters */ + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(hConnect, "welcome.msg", GENERIC_READ, 5, 0); + ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + InternetCloseHandle(hOpenFile); /* Just in case */ + + SetLastError(0xdeadbeef); + hOpenFile = FtpOpenFileA(hConnect, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0); + ok ( hOpenFile == NULL, "Expected FtpOpenFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + + InternetCloseHandle(hOpenFile); /* Just in case */ +} + +static void test_putfile(HINTERNET hFtp, HINTERNET hConnect) +{ + BOOL bRet; + HANDLE hFile; + + /* The order of checking is: + * + * All parameters except 'session handle' and 'condition flags' + * Session handle + * Session handle type + * Condition flags + */ + + /* Test to show the parameter checking order depends on the Windows version */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(NULL, NULL, "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE || + GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_HANDLE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Test to show session handle is checked before 'condition flags' */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(NULL, "non_existing_local", "non_existing_remote", 5, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + /* Start clean */ + DeleteFileA("non_existing_local"); + + /* No local file given */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hFtp, NULL, "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* No remote file given */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hFtp, "non_existing_local", NULL, FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Illegal condition flags */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hFtp, "non_existing_local", "non_existing_remote", 5, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_FILE_NOT_FOUND or ERROR_INVALID_PARAMETER (win98), got %d\n", GetLastError()); + + /* Parameters are OK but local file doesn't exist */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hFtp, "non_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_FILE_NOT_FOUND, + "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError()); + + /* Create a temporary local file */ + SetLastError(0xdeadbeef); + hFile = CreateFileA("now_existing_local", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + ok ( hFile != NULL, "Error creating a local file : %d\n", GetLastError()); + CloseHandle(hFile); + + /* Local file exists but we shouldn't be allowed to 'put' the file */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hFtp, "now_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + + DeleteFileA("now_existing_local"); + + /* Test to show the parameter checking order depends on the Windows version */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hConnect, NULL, "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE || + GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Test to show that 'session handle type' is checked before 'condition flags' */ + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hConnect, "non_existing_local", "non_existing_remote", 5, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpPutFileA(hConnect, "non_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0); + ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); +} + +static void test_removedir(HINTERNET hFtp, HINTERNET hConnect) +{ + BOOL bRet; + + /* Invalid internet handle, the other is a valid parameter */ + SetLastError(0xdeadbeef); + bRet = FtpRemoveDirectoryA(NULL, "should_be_non_existing_deadbeef_dir"); + ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + /* No remote directory given */ + SetLastError(0xdeadbeef); + bRet = FtpRemoveDirectoryA(hFtp, NULL); + ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Remote directory doesn't exist */ + SetLastError(0xdeadbeef); + bRet = FtpRemoveDirectoryA(hFtp, "should_be_non_existing_deadbeef_dir"); + ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + + /* We shouldn't be allowed to remove that directory */ + SetLastError(0xdeadbeef); + bRet = FtpRemoveDirectoryA(hFtp, "pub"); + ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + + /* One small test to show that handle type is checked before parameters */ + SetLastError(0xdeadbeef); + bRet = FtpRemoveDirectoryA(hConnect, NULL); + ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpRemoveDirectoryA(hConnect, "should_be_non_existing_deadbeef_dir"); + ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); +} + +static void test_renamefile(HINTERNET hFtp, HINTERNET hConnect) +{ + BOOL bRet; + + /* Invalid internet handle, the rest are valid parameters */ + SetLastError(0xdeadbeef); + bRet = FtpRenameFileA(NULL , "should_be_non_existing_deadbeef", "new"); + ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError()); + + /* No 'existing' file */ + SetLastError(0xdeadbeef); + bRet = FtpRenameFileA(hFtp , NULL, "new"); + ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* No new file */ + SetLastError(0xdeadbeef); + bRet = FtpRenameFileA(hFtp , "should_be_non_existing_deadbeef", NULL); + ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n"); + ok ( GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + + /* Existing file shouldn't be there */ + SetLastError(0xdeadbeef); + bRet = FtpRenameFileA(hFtp , "should_be_non_existing_deadbeef", "new"); + ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR, + "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError()); + + /* One small test to show that handle type is checked before parameters */ + SetLastError(0xdeadbeef); + bRet = FtpRenameFileA(hConnect , "should_be_non_existing_deadbeef", NULL); + ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + bRet = FtpRenameFileA(hConnect , "should_be_non_existing_deadbeef", "new"); + ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n"); + ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, + "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError()); +} + +static void test_command(HINTERNET hFtp, HINTERNET hConnect) +{ + BOOL ret; + DWORD error; + unsigned int i; + static const struct + { + BOOL ret; + DWORD error; + const char *cmd; + } + command_test[] = + { + { FALSE, ERROR_INVALID_PARAMETER, NULL }, + { FALSE, ERROR_INVALID_PARAMETER, "" }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "HELO" }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE " }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, " SIZE" }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE " }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE /welcome.msg /welcome.msg" }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE /welcome.msg" }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE /welcome.msg " }, + { TRUE, ERROR_SUCCESS, "SIZE\t/welcome.msg" }, + { TRUE, ERROR_SUCCESS, "SIZE /welcome.msg" }, + { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "PWD /welcome.msg" }, + { TRUE, ERROR_SUCCESS, "PWD" }, + { TRUE, ERROR_SUCCESS, "PWD\r\n" } + }; + + for (i = 0; i < sizeof(command_test) / sizeof(command_test[0]); i++) + { + SetLastError(0xdeadbeef); + ret = FtpCommandA(hFtp, FALSE, FTP_TRANSFER_TYPE_ASCII, command_test[i].cmd, 0, NULL); + error = GetLastError(); + + ok(ret == command_test[i].ret, "%d: expected FtpCommandA to %s\n", i, command_test[i].ret ? "succeed" : "fail"); + ok(error == command_test[i].error, "%d: expected error %u, got %u\n", i, command_test[i].error, error); + } +} + +START_TEST(ftp) +{ + HANDLE hInternet, hFtp, hHttp; + + SetLastError(0xdeadbeef); + hInternet = InternetOpen("winetest", 0, NULL, NULL, 0); + ok(hInternet != NULL, "InternetOpen failed: %u\n", GetLastError()); + + hFtp = InternetConnect(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "anonymous", NULL, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0); + if (!hFtp) + { + InternetCloseHandle(hInternet); + skip("No ftp connection could be made to ftp.winehq.org\n"); + return; + } + hHttp = InternetConnect(hInternet, "www.winehq.org", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + if (!hHttp) + { + InternetCloseHandle(hFtp); + InternetCloseHandle(hInternet); + skip("No http connection could be made to www.winehq.org\n"); + return; + } + + /* The first call should always be a proper InternetOpen, if not + * several calls will return ERROR_INTERNET_NOT_INITIALIZED when + * all parameters are correct but no session handle is given. Whereas + * the same call will return ERROR_INVALID_HANDLE if an InternetOpen + * is done before. + * The following test will show that behaviour, where the tests inside + * the other sub-tests will show the other situation. + */ + test_getfile_no_open(); + test_connect(hInternet); + test_createdir(hFtp, hHttp); + test_deletefile(hFtp, hHttp); + test_getfile(hFtp, hHttp); + test_openfile(hFtp, hHttp); + test_putfile(hFtp, hHttp); + test_removedir(hFtp, hHttp); + test_renamefile(hFtp, hHttp); + test_command(hFtp, hHttp); + + InternetCloseHandle(hHttp); + InternetCloseHandle(hFtp); + InternetCloseHandle(hInternet); +} diff --git a/rostests/winetests/wininet/generated.c b/rostests/winetests/wininet/generated.c new file mode 100644 index 00000000000..ce70211268e --- /dev/null +++ b/rostests/winetests/wininet/generated.c @@ -0,0 +1,1028 @@ +/* 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 +#include "windef.h" +#include "winbase.h" +#include "wininet.h" +#include "wininet_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_GOPHER_ABSTRACT_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_ABSTRACT_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_ABSTRACT_ATTRIBUTE_TYPEA, 8, 4); + TEST_FIELD(GOPHER_ABSTRACT_ATTRIBUTE_TYPEA, LPCSTR, ShortAbstract, 0, 4, 4); + TEST_FIELD(GOPHER_ABSTRACT_ATTRIBUTE_TYPEA, LPCSTR, AbstractFile, 4, 4, 4); +} + +static void test_pack_GOPHER_ABSTRACT_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_ABSTRACT_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_ABSTRACT_ATTRIBUTE_TYPEW, 8, 4); + TEST_FIELD(GOPHER_ABSTRACT_ATTRIBUTE_TYPEW, LPCWSTR, ShortAbstract, 0, 4, 4); + TEST_FIELD(GOPHER_ABSTRACT_ATTRIBUTE_TYPEW, LPCWSTR, AbstractFile, 4, 4, 4); +} + +static void test_pack_GOPHER_ADMIN_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_ADMIN_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_ADMIN_ATTRIBUTE_TYPEA, 8, 4); + TEST_FIELD(GOPHER_ADMIN_ATTRIBUTE_TYPEA, LPCSTR, Comment, 0, 4, 4); + TEST_FIELD(GOPHER_ADMIN_ATTRIBUTE_TYPEA, LPCSTR, EmailAddress, 4, 4, 4); +} + +static void test_pack_GOPHER_ADMIN_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_ADMIN_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_ADMIN_ATTRIBUTE_TYPEW, 8, 4); + TEST_FIELD(GOPHER_ADMIN_ATTRIBUTE_TYPEW, LPCWSTR, Comment, 0, 4, 4); + TEST_FIELD(GOPHER_ADMIN_ATTRIBUTE_TYPEW, LPCWSTR, EmailAddress, 4, 4, 4); +} + +static void test_pack_GOPHER_ASK_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_ASK_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_ASK_ATTRIBUTE_TYPEA, 8, 4); + TEST_FIELD(GOPHER_ASK_ATTRIBUTE_TYPEA, LPCSTR, QuestionType, 0, 4, 4); + TEST_FIELD(GOPHER_ASK_ATTRIBUTE_TYPEA, LPCSTR, QuestionText, 4, 4, 4); +} + +static void test_pack_GOPHER_ASK_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_ASK_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_ASK_ATTRIBUTE_TYPEW, 8, 4); + TEST_FIELD(GOPHER_ASK_ATTRIBUTE_TYPEW, LPCWSTR, QuestionType, 0, 4, 4); + TEST_FIELD(GOPHER_ASK_ATTRIBUTE_TYPEW, LPCWSTR, QuestionText, 4, 4, 4); +} + +static void test_pack_GOPHER_ATTRIBUTE_ENUMERATORA(void) +{ + /* GOPHER_ATTRIBUTE_ENUMERATORA */ + TEST_TYPE(GOPHER_ATTRIBUTE_ENUMERATORA, 4, 4); +} + +static void test_pack_GOPHER_ATTRIBUTE_ENUMERATORW(void) +{ + /* GOPHER_ATTRIBUTE_ENUMERATORW */ + TEST_TYPE(GOPHER_ATTRIBUTE_ENUMERATORW, 4, 4); +} + +static void test_pack_GOPHER_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_ATTRIBUTE_TYPEA (pack 4) */ + TEST_FIELD(GOPHER_ATTRIBUTE_TYPEA, DWORD, CategoryId, 0, 4, 4); + TEST_FIELD(GOPHER_ATTRIBUTE_TYPEA, DWORD, AttributeId, 4, 4, 4); +} + +static void test_pack_GOPHER_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_ATTRIBUTE_TYPEW (pack 4) */ + TEST_FIELD(GOPHER_ATTRIBUTE_TYPEW, DWORD, CategoryId, 0, 4, 4); + TEST_FIELD(GOPHER_ATTRIBUTE_TYPEW, DWORD, AttributeId, 4, 4, 4); +} + +static void test_pack_GOPHER_FIND_DATAA(void) +{ + /* GOPHER_FIND_DATAA (pack 4) */ + TEST_TYPE(GOPHER_FIND_DATAA, 808, 4); + TEST_FIELD(GOPHER_FIND_DATAA, CHAR[MAX_GOPHER_DISPLAY_TEXT + 1], DisplayString, 0, 129, 1); + TEST_FIELD(GOPHER_FIND_DATAA, DWORD, GopherType, 132, 4, 4); + TEST_FIELD(GOPHER_FIND_DATAA, DWORD, SizeLow, 136, 4, 4); + TEST_FIELD(GOPHER_FIND_DATAA, DWORD, SizeHigh, 140, 4, 4); + TEST_FIELD(GOPHER_FIND_DATAA, FILETIME, LastModificationTime, 144, 8, 4); + TEST_FIELD(GOPHER_FIND_DATAA, CHAR[MAX_GOPHER_LOCATOR_LENGTH + 1], Locator, 152, 654, 1); +} + +static void test_pack_GOPHER_FIND_DATAW(void) +{ + /* GOPHER_FIND_DATAW (pack 4) */ + TEST_TYPE(GOPHER_FIND_DATAW, 1588, 4); + TEST_FIELD(GOPHER_FIND_DATAW, WCHAR[MAX_GOPHER_DISPLAY_TEXT + 1], DisplayString, 0, 258, 2); + TEST_FIELD(GOPHER_FIND_DATAW, DWORD, GopherType, 260, 4, 4); + TEST_FIELD(GOPHER_FIND_DATAW, DWORD, SizeLow, 264, 4, 4); + TEST_FIELD(GOPHER_FIND_DATAW, DWORD, SizeHigh, 268, 4, 4); + TEST_FIELD(GOPHER_FIND_DATAW, FILETIME, LastModificationTime, 272, 8, 4); + TEST_FIELD(GOPHER_FIND_DATAW, WCHAR[MAX_GOPHER_LOCATOR_LENGTH + 1], Locator, 280, 1308, 2); +} + +static void test_pack_GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE(void) +{ + /* GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE (pack 4) */ + TEST_TYPE(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, 24, 4); + TEST_FIELD(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, INT, DegreesNorth, 0, 4, 4); + TEST_FIELD(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, INT, MinutesNorth, 4, 4, 4); + TEST_FIELD(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, INT, SecondsNorth, 8, 4, 4); + TEST_FIELD(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, INT, DegreesEast, 12, 4, 4); + TEST_FIELD(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, INT, MinutesEast, 16, 4, 4); + TEST_FIELD(GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, INT, SecondsEast, 20, 4, 4); +} + +static void test_pack_GOPHER_LOCATION_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_LOCATION_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_LOCATION_ATTRIBUTE_TYPEA, 4, 4); + TEST_FIELD(GOPHER_LOCATION_ATTRIBUTE_TYPEA, LPCSTR, Location, 0, 4, 4); +} + +static void test_pack_GOPHER_LOCATION_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_LOCATION_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_LOCATION_ATTRIBUTE_TYPEW, 4, 4); + TEST_FIELD(GOPHER_LOCATION_ATTRIBUTE_TYPEW, LPCWSTR, Location, 0, 4, 4); +} + +static void test_pack_GOPHER_MOD_DATE_ATTRIBUTE_TYPE(void) +{ + /* GOPHER_MOD_DATE_ATTRIBUTE_TYPE (pack 4) */ + TEST_TYPE(GOPHER_MOD_DATE_ATTRIBUTE_TYPE, 8, 4); + TEST_FIELD(GOPHER_MOD_DATE_ATTRIBUTE_TYPE, FILETIME, DateAndTime, 0, 8, 4); +} + +static void test_pack_GOPHER_ORGANIZATION_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_ORGANIZATION_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_ORGANIZATION_ATTRIBUTE_TYPEA, 4, 4); + TEST_FIELD(GOPHER_ORGANIZATION_ATTRIBUTE_TYPEA, LPCSTR, Organization, 0, 4, 4); +} + +static void test_pack_GOPHER_ORGANIZATION_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_ORGANIZATION_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_ORGANIZATION_ATTRIBUTE_TYPEW, 4, 4); + TEST_FIELD(GOPHER_ORGANIZATION_ATTRIBUTE_TYPEW, LPCWSTR, Organization, 0, 4, 4); +} + +static void test_pack_GOPHER_PROVIDER_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_PROVIDER_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_PROVIDER_ATTRIBUTE_TYPEA, 4, 4); + TEST_FIELD(GOPHER_PROVIDER_ATTRIBUTE_TYPEA, LPCSTR, Provider, 0, 4, 4); +} + +static void test_pack_GOPHER_PROVIDER_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_PROVIDER_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_PROVIDER_ATTRIBUTE_TYPEW, 4, 4); + TEST_FIELD(GOPHER_PROVIDER_ATTRIBUTE_TYPEW, LPCWSTR, Provider, 0, 4, 4); +} + +static void test_pack_GOPHER_SCORE_ATTRIBUTE_TYPE(void) +{ + /* GOPHER_SCORE_ATTRIBUTE_TYPE (pack 4) */ + TEST_TYPE(GOPHER_SCORE_ATTRIBUTE_TYPE, 4, 4); + TEST_FIELD(GOPHER_SCORE_ATTRIBUTE_TYPE, INT, Score, 0, 4, 4); +} + +static void test_pack_GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE(void) +{ + /* GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE (pack 4) */ + TEST_TYPE(GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE, 8, 4); + TEST_FIELD(GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE, INT, LowerBound, 0, 4, 4); + TEST_FIELD(GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE, INT, UpperBound, 4, 4, 4); +} + +static void test_pack_GOPHER_SITE_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_SITE_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_SITE_ATTRIBUTE_TYPEA, 4, 4); + TEST_FIELD(GOPHER_SITE_ATTRIBUTE_TYPEA, LPCSTR, Site, 0, 4, 4); +} + +static void test_pack_GOPHER_SITE_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_SITE_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_SITE_ATTRIBUTE_TYPEW, 4, 4); + TEST_FIELD(GOPHER_SITE_ATTRIBUTE_TYPEW, LPCWSTR, Site, 0, 4, 4); +} + +static void test_pack_GOPHER_TIMEZONE_ATTRIBUTE_TYPE(void) +{ + /* GOPHER_TIMEZONE_ATTRIBUTE_TYPE (pack 4) */ + TEST_TYPE(GOPHER_TIMEZONE_ATTRIBUTE_TYPE, 4, 4); + TEST_FIELD(GOPHER_TIMEZONE_ATTRIBUTE_TYPE, INT, Zone, 0, 4, 4); +} + +static void test_pack_GOPHER_TTL_ATTRIBUTE_TYPE(void) +{ + /* GOPHER_TTL_ATTRIBUTE_TYPE (pack 4) */ + TEST_TYPE(GOPHER_TTL_ATTRIBUTE_TYPE, 4, 4); + TEST_FIELD(GOPHER_TTL_ATTRIBUTE_TYPE, DWORD, Ttl, 0, 4, 4); +} + +static void test_pack_GOPHER_UNKNOWN_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_UNKNOWN_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_UNKNOWN_ATTRIBUTE_TYPEA, 4, 4); + TEST_FIELD(GOPHER_UNKNOWN_ATTRIBUTE_TYPEA, LPCSTR, Text, 0, 4, 4); +} + +static void test_pack_GOPHER_UNKNOWN_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_UNKNOWN_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_UNKNOWN_ATTRIBUTE_TYPEW, 4, 4); + TEST_FIELD(GOPHER_UNKNOWN_ATTRIBUTE_TYPEW, LPCWSTR, Text, 0, 4, 4); +} + +static void test_pack_GOPHER_VERONICA_ATTRIBUTE_TYPE(void) +{ + /* GOPHER_VERONICA_ATTRIBUTE_TYPE (pack 4) */ + TEST_TYPE(GOPHER_VERONICA_ATTRIBUTE_TYPE, 4, 4); + TEST_FIELD(GOPHER_VERONICA_ATTRIBUTE_TYPE, BOOL, TreeWalk, 0, 4, 4); +} + +static void test_pack_GOPHER_VERSION_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_VERSION_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_VERSION_ATTRIBUTE_TYPEA, 4, 4); + TEST_FIELD(GOPHER_VERSION_ATTRIBUTE_TYPEA, LPCSTR, Version, 0, 4, 4); +} + +static void test_pack_GOPHER_VERSION_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_VERSION_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_VERSION_ATTRIBUTE_TYPEW, 4, 4); + TEST_FIELD(GOPHER_VERSION_ATTRIBUTE_TYPEW, LPCWSTR, Version, 0, 4, 4); +} + +static void test_pack_GOPHER_VIEW_ATTRIBUTE_TYPEA(void) +{ + /* GOPHER_VIEW_ATTRIBUTE_TYPEA (pack 4) */ + TEST_TYPE(GOPHER_VIEW_ATTRIBUTE_TYPEA, 12, 4); + TEST_FIELD(GOPHER_VIEW_ATTRIBUTE_TYPEA, LPCSTR, ContentType, 0, 4, 4); + TEST_FIELD(GOPHER_VIEW_ATTRIBUTE_TYPEA, LPCSTR, Language, 4, 4, 4); + TEST_FIELD(GOPHER_VIEW_ATTRIBUTE_TYPEA, DWORD, Size, 8, 4, 4); +} + +static void test_pack_GOPHER_VIEW_ATTRIBUTE_TYPEW(void) +{ + /* GOPHER_VIEW_ATTRIBUTE_TYPEW (pack 4) */ + TEST_TYPE(GOPHER_VIEW_ATTRIBUTE_TYPEW, 12, 4); + TEST_FIELD(GOPHER_VIEW_ATTRIBUTE_TYPEW, LPCWSTR, ContentType, 0, 4, 4); + TEST_FIELD(GOPHER_VIEW_ATTRIBUTE_TYPEW, LPCWSTR, Language, 4, 4, 4); + TEST_FIELD(GOPHER_VIEW_ATTRIBUTE_TYPEW, DWORD, Size, 8, 4, 4); +} + +static void test_pack_GROUPID(void) +{ + /* GROUPID */ + TEST_TYPE(GROUPID, 8, 8); + TEST_TYPE_SIGNED(GROUPID); +} + +static void test_pack_HINTERNET(void) +{ + /* HINTERNET */ + TEST_TYPE(HINTERNET, 4, 4); +} + +static void test_pack_HTTP_VERSION_INFO(void) +{ + /* HTTP_VERSION_INFO (pack 4) */ + TEST_TYPE(HTTP_VERSION_INFO, 8, 4); + TEST_FIELD(HTTP_VERSION_INFO, DWORD, dwMajorVersion, 0, 4, 4); + TEST_FIELD(HTTP_VERSION_INFO, DWORD, dwMinorVersion, 4, 4, 4); +} + +static void test_pack_INTERNET_ASYNC_RESULT(void) +{ + /* INTERNET_ASYNC_RESULT (pack 4) */ + TEST_TYPE(INTERNET_ASYNC_RESULT, 8, 4); + TEST_FIELD(INTERNET_ASYNC_RESULT, DWORD, dwResult, 0, 4, 4); + TEST_FIELD(INTERNET_ASYNC_RESULT, DWORD, dwError, 4, 4, 4); +} + +static void test_pack_INTERNET_AUTH_NOTIFY_DATA(void) +{ + /* INTERNET_AUTH_NOTIFY_DATA (pack 4) */ + TEST_TYPE(INTERNET_AUTH_NOTIFY_DATA, 16, 4); + TEST_FIELD(INTERNET_AUTH_NOTIFY_DATA, DWORD, cbStruct, 0, 4, 4); + TEST_FIELD(INTERNET_AUTH_NOTIFY_DATA, DWORD, dwOptions, 4, 4, 4); + TEST_FIELD(INTERNET_AUTH_NOTIFY_DATA, PFN_AUTH_NOTIFY, pfnNotify, 8, 4, 4); + TEST_FIELD(INTERNET_AUTH_NOTIFY_DATA, DWORD, dwContext, 12, 4, 4); +} + +static void test_pack_INTERNET_BUFFERSA(void) +{ + /* INTERNET_BUFFERSA (pack 4) */ + TEST_TYPE(INTERNET_BUFFERSA, 40, 4); + TEST_FIELD(INTERNET_BUFFERSA, DWORD, dwStructSize, 0, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, struct _INTERNET_BUFFERSA *, Next, 4, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, LPCSTR, lpcszHeader, 8, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, DWORD, dwHeadersLength, 12, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, DWORD, dwHeadersTotal, 16, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, LPVOID, lpvBuffer, 20, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, DWORD, dwBufferLength, 24, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, DWORD, dwBufferTotal, 28, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, DWORD, dwOffsetLow, 32, 4, 4); + TEST_FIELD(INTERNET_BUFFERSA, DWORD, dwOffsetHigh, 36, 4, 4); +} + +static void test_pack_INTERNET_BUFFERSW(void) +{ + /* INTERNET_BUFFERSW (pack 4) */ + TEST_TYPE(INTERNET_BUFFERSW, 40, 4); + TEST_FIELD(INTERNET_BUFFERSW, DWORD, dwStructSize, 0, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, struct _INTERNET_BUFFERSW *, Next, 4, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, LPCWSTR, lpcszHeader, 8, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, DWORD, dwHeadersLength, 12, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, DWORD, dwHeadersTotal, 16, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, LPVOID, lpvBuffer, 20, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, DWORD, dwBufferLength, 24, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, DWORD, dwBufferTotal, 28, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, DWORD, dwOffsetLow, 32, 4, 4); + TEST_FIELD(INTERNET_BUFFERSW, DWORD, dwOffsetHigh, 36, 4, 4); +} + +static void test_pack_INTERNET_CACHE_ENTRY_INFOA(void) +{ + /* INTERNET_CACHE_ENTRY_INFOA (pack 4) */ + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, DWORD, dwStructSize, 0, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, LPSTR, lpszSourceUrlName, 4, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, LPSTR, lpszLocalFileName, 8, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, DWORD, CacheEntryType, 12, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, DWORD, dwUseCount, 16, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, DWORD, dwHitRate, 20, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, DWORD, dwSizeLow, 24, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, DWORD, dwSizeHigh, 28, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, FILETIME, LastModifiedTime, 32, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, FILETIME, ExpireTime, 40, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, FILETIME, LastAccessTime, 48, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, FILETIME, LastSyncTime, 56, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, LPBYTE, lpHeaderInfo, 64, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, DWORD, dwHeaderInfoSize, 68, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOA, LPSTR, lpszFileExtension, 72, 4, 4); +} + +static void test_pack_INTERNET_CACHE_ENTRY_INFOW(void) +{ + /* INTERNET_CACHE_ENTRY_INFOW (pack 4) */ + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, DWORD, dwStructSize, 0, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, LPWSTR, lpszSourceUrlName, 4, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, LPWSTR, lpszLocalFileName, 8, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, DWORD, CacheEntryType, 12, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, DWORD, dwUseCount, 16, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, DWORD, dwHitRate, 20, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, DWORD, dwSizeLow, 24, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, DWORD, dwSizeHigh, 28, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, FILETIME, LastModifiedTime, 32, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, FILETIME, ExpireTime, 40, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, FILETIME, LastAccessTime, 48, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, FILETIME, LastSyncTime, 56, 8, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, LPBYTE, lpHeaderInfo, 64, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, DWORD, dwHeaderInfoSize, 68, 4, 4); + TEST_FIELD(INTERNET_CACHE_ENTRY_INFOW, LPWSTR, lpszFileExtension, 72, 4, 4); +} + +static void test_pack_INTERNET_CERTIFICATE_INFOA(void) +{ + /* INTERNET_CERTIFICATE_INFOA (pack 4) */ + TEST_TYPE(INTERNET_CERTIFICATE_INFOA, 40, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, FILETIME, ftExpiry, 0, 8, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, FILETIME, ftStart, 8, 8, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, LPSTR, lpszSubjectInfo, 16, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, LPSTR, lpszIssuerInfo, 20, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, LPSTR, lpszProtocolName, 24, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, LPSTR, lpszSignatureAlgName, 28, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, LPSTR, lpszEncryptionAlgName, 32, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOA, DWORD, dwKeySize, 36, 4, 4); +} + +static void test_pack_INTERNET_CERTIFICATE_INFOW(void) +{ + /* INTERNET_CERTIFICATE_INFOW (pack 4) */ + TEST_TYPE(INTERNET_CERTIFICATE_INFOW, 40, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, FILETIME, ftExpiry, 0, 8, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, FILETIME, ftStart, 8, 8, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, LPWSTR, lpszSubjectInfo, 16, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, LPWSTR, lpszIssuerInfo, 20, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, LPWSTR, lpszProtocolName, 24, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, LPWSTR, lpszSignatureAlgName, 28, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, LPWSTR, lpszEncryptionAlgName, 32, 4, 4); + TEST_FIELD(INTERNET_CERTIFICATE_INFOW, DWORD, dwKeySize, 36, 4, 4); +} + +static void test_pack_INTERNET_CONNECTED_INFO(void) +{ + /* INTERNET_CONNECTED_INFO (pack 4) */ + TEST_TYPE(INTERNET_CONNECTED_INFO, 8, 4); + TEST_FIELD(INTERNET_CONNECTED_INFO, DWORD, dwConnectedState, 0, 4, 4); + TEST_FIELD(INTERNET_CONNECTED_INFO, DWORD, dwFlags, 4, 4, 4); +} + +static void test_pack_INTERNET_PORT(void) +{ + /* INTERNET_PORT */ + TEST_TYPE(INTERNET_PORT, 2, 2); + TEST_TYPE_UNSIGNED(INTERNET_PORT); +} + +static void test_pack_INTERNET_PROXY_INFOA(void) +{ + /* INTERNET_PROXY_INFOA (pack 4) */ + TEST_TYPE(INTERNET_PROXY_INFOA, 12, 4); + TEST_FIELD(INTERNET_PROXY_INFOA, DWORD, dwAccessType, 0, 4, 4); + TEST_FIELD(INTERNET_PROXY_INFOA, LPCSTR, lpszProxy, 4, 4, 4); + TEST_FIELD(INTERNET_PROXY_INFOA, LPCSTR, lpszProxyBypass, 8, 4, 4); +} + +static void test_pack_INTERNET_PROXY_INFOW(void) +{ + /* INTERNET_PROXY_INFOW (pack 4) */ + TEST_TYPE(INTERNET_PROXY_INFOW, 12, 4); + TEST_FIELD(INTERNET_PROXY_INFOW, DWORD, dwAccessType, 0, 4, 4); + TEST_FIELD(INTERNET_PROXY_INFOW, LPCWSTR, lpszProxy, 4, 4, 4); + TEST_FIELD(INTERNET_PROXY_INFOW, LPCWSTR, lpszProxyBypass, 8, 4, 4); +} + +static void test_pack_INTERNET_STATUS_CALLBACK(void) +{ + /* INTERNET_STATUS_CALLBACK */ + TEST_TYPE(INTERNET_STATUS_CALLBACK, 4, 4); +} + +static void test_pack_INTERNET_VERSION_INFO(void) +{ + /* INTERNET_VERSION_INFO (pack 4) */ + TEST_TYPE(INTERNET_VERSION_INFO, 8, 4); + TEST_FIELD(INTERNET_VERSION_INFO, DWORD, dwMajorVersion, 0, 4, 4); + TEST_FIELD(INTERNET_VERSION_INFO, DWORD, dwMinorVersion, 4, 4, 4); +} + +static void test_pack_LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEA, 8, 4); +} + +static void test_pack_LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEW, 8, 4); +} + +static void test_pack_LPGOPHER_ADMIN_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_ADMIN_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_ADMIN_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ADMIN_ATTRIBUTE_TYPEA, 8, 4); +} + +static void test_pack_LPGOPHER_ADMIN_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_ADMIN_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_ADMIN_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ADMIN_ATTRIBUTE_TYPEW, 8, 4); +} + +static void test_pack_LPGOPHER_ASK_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_ASK_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_ASK_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ASK_ATTRIBUTE_TYPEA, 8, 4); +} + +static void test_pack_LPGOPHER_ASK_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_ASK_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_ASK_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ASK_ATTRIBUTE_TYPEW, 8, 4); +} + +static void test_pack_LPGOPHER_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_ATTRIBUTE_TYPEA, 4, 4); +} + +static void test_pack_LPGOPHER_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_ATTRIBUTE_TYPEW, 4, 4); +} + +static void test_pack_LPGOPHER_FIND_DATAA(void) +{ + /* LPGOPHER_FIND_DATAA */ + TEST_TYPE(LPGOPHER_FIND_DATAA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_FIND_DATAA, 808, 4); +} + +static void test_pack_LPGOPHER_FIND_DATAW(void) +{ + /* LPGOPHER_FIND_DATAW */ + TEST_TYPE(LPGOPHER_FIND_DATAW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_FIND_DATAW, 1588, 4); +} + +static void test_pack_LPGOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE(void) +{ + /* LPGOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE */ + TEST_TYPE(LPGOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE, 24, 4); +} + +static void test_pack_LPGOPHER_LOCATION_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_LOCATION_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_LOCATION_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_LOCATION_ATTRIBUTE_TYPEA, 4, 4); +} + +static void test_pack_LPGOPHER_LOCATION_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_LOCATION_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_LOCATION_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_LOCATION_ATTRIBUTE_TYPEW, 4, 4); +} + +static void test_pack_LPGOPHER_MOD_DATE_ATTRIBUTE_TYPE(void) +{ + /* LPGOPHER_MOD_DATE_ATTRIBUTE_TYPE */ + TEST_TYPE(LPGOPHER_MOD_DATE_ATTRIBUTE_TYPE, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_MOD_DATE_ATTRIBUTE_TYPE, 8, 4); +} + +static void test_pack_LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEA, 4, 4); +} + +static void test_pack_LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEW, 4, 4); +} + +static void test_pack_LPGOPHER_PROVIDER_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_PROVIDER_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_PROVIDER_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_PROVIDER_ATTRIBUTE_TYPEA, 4, 4); +} + +static void test_pack_LPGOPHER_PROVIDER_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_PROVIDER_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_PROVIDER_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_PROVIDER_ATTRIBUTE_TYPEW, 4, 4); +} + +static void test_pack_LPGOPHER_SCORE_ATTRIBUTE_TYPE(void) +{ + /* LPGOPHER_SCORE_ATTRIBUTE_TYPE */ + TEST_TYPE(LPGOPHER_SCORE_ATTRIBUTE_TYPE, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_SCORE_ATTRIBUTE_TYPE, 4, 4); +} + +static void test_pack_LPGOPHER_SCORE_RANGE_ATTRIBUTE_TYPE(void) +{ + /* LPGOPHER_SCORE_RANGE_ATTRIBUTE_TYPE */ + TEST_TYPE(LPGOPHER_SCORE_RANGE_ATTRIBUTE_TYPE, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_SCORE_RANGE_ATTRIBUTE_TYPE, 8, 4); +} + +static void test_pack_LPGOPHER_SITE_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_SITE_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_SITE_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_SITE_ATTRIBUTE_TYPEA, 4, 4); +} + +static void test_pack_LPGOPHER_SITE_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_SITE_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_SITE_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_SITE_ATTRIBUTE_TYPEW, 4, 4); +} + +static void test_pack_LPGOPHER_TIMEZONE_ATTRIBUTE_TYPE(void) +{ + /* LPGOPHER_TIMEZONE_ATTRIBUTE_TYPE */ + TEST_TYPE(LPGOPHER_TIMEZONE_ATTRIBUTE_TYPE, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_TIMEZONE_ATTRIBUTE_TYPE, 4, 4); +} + +static void test_pack_LPGOPHER_TTL_ATTRIBUTE_TYPE(void) +{ + /* LPGOPHER_TTL_ATTRIBUTE_TYPE */ + TEST_TYPE(LPGOPHER_TTL_ATTRIBUTE_TYPE, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_TTL_ATTRIBUTE_TYPE, 4, 4); +} + +static void test_pack_LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEA, 4, 4); +} + +static void test_pack_LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEW, 4, 4); +} + +static void test_pack_LPGOPHER_VERONICA_ATTRIBUTE_TYPE(void) +{ + /* LPGOPHER_VERONICA_ATTRIBUTE_TYPE */ + TEST_TYPE(LPGOPHER_VERONICA_ATTRIBUTE_TYPE, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_VERONICA_ATTRIBUTE_TYPE, 4, 4); +} + +static void test_pack_LPGOPHER_VERSION_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_VERSION_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_VERSION_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_VERSION_ATTRIBUTE_TYPEA, 4, 4); +} + +static void test_pack_LPGOPHER_VERSION_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_VERSION_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_VERSION_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_VERSION_ATTRIBUTE_TYPEW, 4, 4); +} + +static void test_pack_LPGOPHER_VIEW_ATTRIBUTE_TYPEA(void) +{ + /* LPGOPHER_VIEW_ATTRIBUTE_TYPEA */ + TEST_TYPE(LPGOPHER_VIEW_ATTRIBUTE_TYPEA, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_VIEW_ATTRIBUTE_TYPEA, 12, 4); +} + +static void test_pack_LPGOPHER_VIEW_ATTRIBUTE_TYPEW(void) +{ + /* LPGOPHER_VIEW_ATTRIBUTE_TYPEW */ + TEST_TYPE(LPGOPHER_VIEW_ATTRIBUTE_TYPEW, 4, 4); + TEST_TYPE_POINTER(LPGOPHER_VIEW_ATTRIBUTE_TYPEW, 12, 4); +} + +static void test_pack_LPHINTERNET(void) +{ + /* LPHINTERNET */ + TEST_TYPE(LPHINTERNET, 4, 4); + TEST_TYPE_POINTER(LPHINTERNET, 4, 4); +} + +static void test_pack_LPHTTP_VERSION_INFO(void) +{ + /* LPHTTP_VERSION_INFO */ + TEST_TYPE(LPHTTP_VERSION_INFO, 4, 4); + TEST_TYPE_POINTER(LPHTTP_VERSION_INFO, 8, 4); +} + +static void test_pack_LPINTERNET_ASYNC_RESULT(void) +{ + /* LPINTERNET_ASYNC_RESULT */ + TEST_TYPE(LPINTERNET_ASYNC_RESULT, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_ASYNC_RESULT, 8, 4); +} + +static void test_pack_LPINTERNET_BUFFERSA(void) +{ + /* LPINTERNET_BUFFERSA */ + TEST_TYPE(LPINTERNET_BUFFERSA, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_BUFFERSA, 40, 4); +} + +static void test_pack_LPINTERNET_BUFFERSW(void) +{ + /* LPINTERNET_BUFFERSW */ + TEST_TYPE(LPINTERNET_BUFFERSW, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_BUFFERSW, 40, 4); +} + +static void test_pack_LPINTERNET_CACHE_ENTRY_INFOA(void) +{ + /* LPINTERNET_CACHE_ENTRY_INFOA */ + TEST_TYPE(LPINTERNET_CACHE_ENTRY_INFOA, 4, 4); +} + +static void test_pack_LPINTERNET_CACHE_ENTRY_INFOW(void) +{ + /* LPINTERNET_CACHE_ENTRY_INFOW */ + TEST_TYPE(LPINTERNET_CACHE_ENTRY_INFOW, 4, 4); +} + +static void test_pack_LPINTERNET_CERTIFICATE_INFOA(void) +{ + /* LPINTERNET_CERTIFICATE_INFOA */ + TEST_TYPE(LPINTERNET_CERTIFICATE_INFOA, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_CERTIFICATE_INFOA, 40, 4); +} + +static void test_pack_LPINTERNET_CERTIFICATE_INFOW(void) +{ + /* LPINTERNET_CERTIFICATE_INFOW */ + TEST_TYPE(LPINTERNET_CERTIFICATE_INFOW, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_CERTIFICATE_INFOW, 40, 4); +} + +static void test_pack_LPINTERNET_CONNECTED_INFO(void) +{ + /* LPINTERNET_CONNECTED_INFO */ + TEST_TYPE(LPINTERNET_CONNECTED_INFO, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_CONNECTED_INFO, 8, 4); +} + +static void test_pack_LPINTERNET_PORT(void) +{ + /* LPINTERNET_PORT */ + TEST_TYPE(LPINTERNET_PORT, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_PORT, 2, 2); +} + +static void test_pack_LPINTERNET_PROXY_INFOA(void) +{ + /* LPINTERNET_PROXY_INFOA */ + TEST_TYPE(LPINTERNET_PROXY_INFOA, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_PROXY_INFOA, 12, 4); +} + +static void test_pack_LPINTERNET_PROXY_INFOW(void) +{ + /* LPINTERNET_PROXY_INFOW */ + TEST_TYPE(LPINTERNET_PROXY_INFOW, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_PROXY_INFOW, 12, 4); +} + +static void test_pack_LPINTERNET_STATUS_CALLBACK(void) +{ + /* LPINTERNET_STATUS_CALLBACK */ + TEST_TYPE(LPINTERNET_STATUS_CALLBACK, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_STATUS_CALLBACK, 4, 4); +} + +static void test_pack_LPINTERNET_VERSION_INFO(void) +{ + /* LPINTERNET_VERSION_INFO */ + TEST_TYPE(LPINTERNET_VERSION_INFO, 4, 4); + TEST_TYPE_POINTER(LPINTERNET_VERSION_INFO, 8, 4); +} + +static void test_pack_LPURL_COMPONENTSA(void) +{ + /* LPURL_COMPONENTSA */ + TEST_TYPE(LPURL_COMPONENTSA, 4, 4); +} + +static void test_pack_LPURL_COMPONENTSW(void) +{ + /* LPURL_COMPONENTSW */ + TEST_TYPE(LPURL_COMPONENTSW, 4, 4); +} + +static void test_pack_PFN_AUTH_NOTIFY(void) +{ + /* PFN_AUTH_NOTIFY */ + TEST_TYPE(PFN_AUTH_NOTIFY, 4, 4); +} + +static void test_pack_PFN_DIAL_HANDLER(void) +{ + /* PFN_DIAL_HANDLER */ + TEST_TYPE(PFN_DIAL_HANDLER, 4, 4); +} + +static void test_pack_URL_COMPONENTSA(void) +{ + /* URL_COMPONENTSA (pack 4) */ + TEST_FIELD(URL_COMPONENTSA, DWORD, dwStructSize, 0, 4, 4); + TEST_FIELD(URL_COMPONENTSA, LPSTR, lpszScheme, 4, 4, 4); + TEST_FIELD(URL_COMPONENTSA, DWORD, dwSchemeLength, 8, 4, 4); +} + +static void test_pack_URL_COMPONENTSW(void) +{ + /* URL_COMPONENTSW (pack 4) */ + TEST_FIELD(URL_COMPONENTSW, DWORD, dwStructSize, 0, 4, 4); + TEST_FIELD(URL_COMPONENTSW, LPWSTR, lpszScheme, 4, 4, 4); + TEST_FIELD(URL_COMPONENTSW, DWORD, dwSchemeLength, 8, 4, 4); +} + +static void test_pack(void) +{ + test_pack_GOPHER_ABSTRACT_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_ABSTRACT_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_ADMIN_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_ADMIN_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_ASK_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_ASK_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_ATTRIBUTE_ENUMERATORA(); + test_pack_GOPHER_ATTRIBUTE_ENUMERATORW(); + test_pack_GOPHER_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_FIND_DATAA(); + test_pack_GOPHER_FIND_DATAW(); + test_pack_GOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE(); + test_pack_GOPHER_LOCATION_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_LOCATION_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_MOD_DATE_ATTRIBUTE_TYPE(); + test_pack_GOPHER_ORGANIZATION_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_ORGANIZATION_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_PROVIDER_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_PROVIDER_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_SCORE_ATTRIBUTE_TYPE(); + test_pack_GOPHER_SCORE_RANGE_ATTRIBUTE_TYPE(); + test_pack_GOPHER_SITE_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_SITE_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_TIMEZONE_ATTRIBUTE_TYPE(); + test_pack_GOPHER_TTL_ATTRIBUTE_TYPE(); + test_pack_GOPHER_UNKNOWN_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_UNKNOWN_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_VERONICA_ATTRIBUTE_TYPE(); + test_pack_GOPHER_VERSION_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_VERSION_ATTRIBUTE_TYPEW(); + test_pack_GOPHER_VIEW_ATTRIBUTE_TYPEA(); + test_pack_GOPHER_VIEW_ATTRIBUTE_TYPEW(); + test_pack_GROUPID(); + test_pack_HINTERNET(); + test_pack_HTTP_VERSION_INFO(); + test_pack_INTERNET_ASYNC_RESULT(); + test_pack_INTERNET_AUTH_NOTIFY_DATA(); + test_pack_INTERNET_BUFFERSA(); + test_pack_INTERNET_BUFFERSW(); + test_pack_INTERNET_CACHE_ENTRY_INFOA(); + test_pack_INTERNET_CACHE_ENTRY_INFOW(); + test_pack_INTERNET_CERTIFICATE_INFOA(); + test_pack_INTERNET_CERTIFICATE_INFOW(); + test_pack_INTERNET_CONNECTED_INFO(); + test_pack_INTERNET_PORT(); + test_pack_INTERNET_PROXY_INFOA(); + test_pack_INTERNET_PROXY_INFOW(); + test_pack_INTERNET_STATUS_CALLBACK(); + test_pack_INTERNET_VERSION_INFO(); + test_pack_LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_ADMIN_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_ADMIN_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_ASK_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_ASK_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_FIND_DATAA(); + test_pack_LPGOPHER_FIND_DATAW(); + test_pack_LPGOPHER_GEOGRAPHICAL_LOCATION_ATTRIBUTE_TYPE(); + test_pack_LPGOPHER_LOCATION_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_LOCATION_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_MOD_DATE_ATTRIBUTE_TYPE(); + test_pack_LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_PROVIDER_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_PROVIDER_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_SCORE_ATTRIBUTE_TYPE(); + test_pack_LPGOPHER_SCORE_RANGE_ATTRIBUTE_TYPE(); + test_pack_LPGOPHER_SITE_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_SITE_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_TIMEZONE_ATTRIBUTE_TYPE(); + test_pack_LPGOPHER_TTL_ATTRIBUTE_TYPE(); + test_pack_LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_VERONICA_ATTRIBUTE_TYPE(); + test_pack_LPGOPHER_VERSION_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_VERSION_ATTRIBUTE_TYPEW(); + test_pack_LPGOPHER_VIEW_ATTRIBUTE_TYPEA(); + test_pack_LPGOPHER_VIEW_ATTRIBUTE_TYPEW(); + test_pack_LPHINTERNET(); + test_pack_LPHTTP_VERSION_INFO(); + test_pack_LPINTERNET_ASYNC_RESULT(); + test_pack_LPINTERNET_BUFFERSA(); + test_pack_LPINTERNET_BUFFERSW(); + test_pack_LPINTERNET_CACHE_ENTRY_INFOA(); + test_pack_LPINTERNET_CACHE_ENTRY_INFOW(); + test_pack_LPINTERNET_CERTIFICATE_INFOA(); + test_pack_LPINTERNET_CERTIFICATE_INFOW(); + test_pack_LPINTERNET_CONNECTED_INFO(); + test_pack_LPINTERNET_PORT(); + test_pack_LPINTERNET_PROXY_INFOA(); + test_pack_LPINTERNET_PROXY_INFOW(); + test_pack_LPINTERNET_STATUS_CALLBACK(); + test_pack_LPINTERNET_VERSION_INFO(); + test_pack_LPURL_COMPONENTSA(); + test_pack_LPURL_COMPONENTSW(); + test_pack_PFN_AUTH_NOTIFY(); + test_pack_PFN_DIAL_HANDLER(); + test_pack_URL_COMPONENTSA(); + test_pack_URL_COMPONENTSW(); +} + +START_TEST(generated) +{ + test_pack(); +} diff --git a/rostests/winetests/wininet/http.c b/rostests/winetests/wininet/http.c new file mode 100644 index 00000000000..ec2df72d988 --- /dev/null +++ b/rostests/winetests/wininet/http.c @@ -0,0 +1,1626 @@ +/* + * Wininet - Http tests + * + * Copyright 2002 Aric Stewart + * Copyright 2004 Mike McCormack + * Copyright 2005 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wininet.h" +#include "winsock.h" + +#include "wine/test.h" + +#define TEST_URL "http://www.winehq.org/site/about" + +static BOOL first_connection_to_test_url = TRUE; + +/* Adapted from dlls/urlmon/tests/protocol.c */ + +#define SET_EXPECT2(status, num) \ + expect[status] = num + +#define SET_EXPECT(status) \ + SET_EXPECT2(status, 1) + +/* SET_WINE_ALLOW's should be used with an appropriate + * todo_wine CHECK_NOTIFIED at a later point in the code */ +#define SET_WINE_ALLOW2(status, num) \ + wine_allow[status] = num + +#define SET_WINE_ALLOW(status) \ + SET_WINE_ALLOW2(status, 1) + +#define CHECK_EXPECT(status) \ + do { \ + if (!expect[status] && wine_allow[status]) \ + { \ + todo_wine ok(expect[status], "unexpected status %d (%s)\n", status, \ + status < MAX_INTERNET_STATUS && status_string[status][0] != 0 ? \ + status_string[status] : "unknown"); \ + wine_allow[status]--; \ + } \ + else \ + { \ + ok(expect[status], "unexpected status %d (%s)\n", status, \ + status < MAX_INTERNET_STATUS && status_string[status][0] != 0 ? \ + status_string[status] : "unknown"); \ + expect[status]--; \ + } \ + notified[status]++; \ + }while(0) + +/* CLEAR_NOTIFIED used in cases when notification behavior + * differs between Windows versions */ +#define CLEAR_NOTIFIED(status) \ + expect[status] = wine_allow[status] = notified[status] = 0; + +#define CHECK_NOTIFIED2(status, num) \ + do { \ + ok(notified[status] == (num), "expected status %d (%s) %d times, received %d times\n", \ + status, status < MAX_INTERNET_STATUS && status_string[status][0] != 0 ? \ + status_string[status] : "unknown", (num), notified[status]); \ + CLEAR_NOTIFIED(status); \ + }while(0) + +#define CHECK_NOTIFIED(status) \ + CHECK_NOTIFIED2(status, 1) + +#define CHECK_NOT_NOTIFIED(status) \ + CHECK_NOTIFIED2(status, 0) + +#define MAX_INTERNET_STATUS (INTERNET_STATUS_COOKIE_HISTORY+1) +#define MAX_STATUS_NAME 50 +static int expect[MAX_INTERNET_STATUS], wine_allow[MAX_INTERNET_STATUS], + notified[MAX_INTERNET_STATUS]; +static CHAR status_string[MAX_INTERNET_STATUS][MAX_STATUS_NAME]; + +static HANDLE hCompleteEvent; + +static INTERNET_STATUS_CALLBACK (WINAPI *pInternetSetStatusCallbackA)(HINTERNET ,INTERNET_STATUS_CALLBACK); +static BOOL (WINAPI *pInternetTimeFromSystemTimeA)(CONST SYSTEMTIME *,DWORD ,LPSTR ,DWORD); +static BOOL (WINAPI *pInternetTimeFromSystemTimeW)(CONST SYSTEMTIME *,DWORD ,LPWSTR ,DWORD); +static BOOL (WINAPI *pInternetTimeToSystemTimeA)(LPCSTR ,SYSTEMTIME *,DWORD); +static BOOL (WINAPI *pInternetTimeToSystemTimeW)(LPCWSTR ,SYSTEMTIME *,DWORD); + + +static VOID WINAPI callback( + HINTERNET hInternet, + DWORD_PTR dwContext, + DWORD dwInternetStatus, + LPVOID lpvStatusInformation, + DWORD dwStatusInformationLength +) +{ + CHECK_EXPECT(dwInternetStatus); + switch (dwInternetStatus) + { + case INTERNET_STATUS_RESOLVING_NAME: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_RESOLVING_NAME \"%s\" %d\n", + GetCurrentThreadId(), hInternet, dwContext, + (LPCSTR)lpvStatusInformation,dwStatusInformationLength); + *(LPSTR)lpvStatusInformation = '\0'; + break; + case INTERNET_STATUS_NAME_RESOLVED: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_NAME_RESOLVED \"%s\" %d\n", + GetCurrentThreadId(), hInternet, dwContext, + (LPCSTR)lpvStatusInformation,dwStatusInformationLength); + *(LPSTR)lpvStatusInformation = '\0'; + break; + case INTERNET_STATUS_CONNECTING_TO_SERVER: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_CONNECTING_TO_SERVER \"%s\" %d\n", + GetCurrentThreadId(), hInternet, dwContext, + (LPCSTR)lpvStatusInformation,dwStatusInformationLength); + *(LPSTR)lpvStatusInformation = '\0'; + break; + case INTERNET_STATUS_CONNECTED_TO_SERVER: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_CONNECTED_TO_SERVER \"%s\" %d\n", + GetCurrentThreadId(), hInternet, dwContext, + (LPCSTR)lpvStatusInformation,dwStatusInformationLength); + *(LPSTR)lpvStatusInformation = '\0'; + break; + case INTERNET_STATUS_SENDING_REQUEST: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_SENDING_REQUEST %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_REQUEST_SENT: + ok(dwStatusInformationLength == sizeof(DWORD), + "info length should be sizeof(DWORD) instead of %d\n", + dwStatusInformationLength); + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_REQUEST_SENT 0x%x %d\n", + GetCurrentThreadId(), hInternet, dwContext, + *(DWORD *)lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_RECEIVING_RESPONSE: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_RECEIVING_RESPONSE %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_RESPONSE_RECEIVED: + ok(dwStatusInformationLength == sizeof(DWORD), + "info length should be sizeof(DWORD) instead of %d\n", + dwStatusInformationLength); + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_RESPONSE_RECEIVED 0x%x %d\n", + GetCurrentThreadId(), hInternet, dwContext, + *(DWORD *)lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_CTL_RESPONSE_RECEIVED: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_CTL_RESPONSE_RECEIVED %p %d\n", + GetCurrentThreadId(), hInternet,dwContext, + lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_PREFETCH: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_PREFETCH %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_CLOSING_CONNECTION: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_CLOSING_CONNECTION %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_CONNECTION_CLOSED: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_CONNECTION_CLOSED %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_HANDLE_CREATED: + ok(dwStatusInformationLength == sizeof(HINTERNET), + "info length should be sizeof(HINTERNET) instead of %d\n", + dwStatusInformationLength); + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_HANDLE_CREATED %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + *(HINTERNET *)lpvStatusInformation,dwStatusInformationLength); + break; + case INTERNET_STATUS_HANDLE_CLOSING: + ok(dwStatusInformationLength == sizeof(HINTERNET), + "info length should be sizeof(HINTERNET) instead of %d\n", + dwStatusInformationLength); + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_HANDLE_CLOSING %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + *(HINTERNET *)lpvStatusInformation, dwStatusInformationLength); + break; + case INTERNET_STATUS_REQUEST_COMPLETE: + { + INTERNET_ASYNC_RESULT *iar = (INTERNET_ASYNC_RESULT *)lpvStatusInformation; + ok(dwStatusInformationLength == sizeof(INTERNET_ASYNC_RESULT), + "info length should be sizeof(INTERNET_ASYNC_RESULT) instead of %d\n", + dwStatusInformationLength); + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_REQUEST_COMPLETE {%ld,%d} %d\n", + GetCurrentThreadId(), hInternet, dwContext, + iar->dwResult,iar->dwError,dwStatusInformationLength); + SetEvent(hCompleteEvent); + break; + } + case INTERNET_STATUS_REDIRECT: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_REDIRECT \"%s\" %d\n", + GetCurrentThreadId(), hInternet, dwContext, + (LPCSTR)lpvStatusInformation, dwStatusInformationLength); + *(LPSTR)lpvStatusInformation = '\0'; + break; + case INTERNET_STATUS_INTERMEDIATE_RESPONSE: + trace("%04x:Callback %p 0x%lx INTERNET_STATUS_INTERMEDIATE_RESPONSE %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, + lpvStatusInformation, dwStatusInformationLength); + break; + default: + trace("%04x:Callback %p 0x%lx %d %p %d\n", + GetCurrentThreadId(), hInternet, dwContext, dwInternetStatus, + lpvStatusInformation, dwStatusInformationLength); + } +} + +static void InternetReadFile_test(int flags) +{ + DWORD rc; + CHAR buffer[4000]; + DWORD length; + DWORD out; + const char *types[2] = { "*", NULL }; + HINTERNET hi, hic = 0, hor = 0; + + hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + trace("Starting InternetReadFile test with flags 0x%x\n",flags); + + trace("InternetOpenA <--\n"); + hi = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, flags); + ok((hi != 0x0),"InternetOpen failed with error %u\n", GetLastError()); + trace("InternetOpenA -->\n"); + + if (hi == 0x0) goto abort; + + pInternetSetStatusCallbackA(hi,&callback); + + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + + trace("InternetConnectA <--\n"); + hic=InternetConnectA(hi, "www.winehq.org", INTERNET_INVALID_PORT_NUMBER, + NULL, NULL, INTERNET_SERVICE_HTTP, 0x0, 0xdeadbeef); + ok((hic != 0x0),"InternetConnect failed with error %u\n", GetLastError()); + trace("InternetConnectA -->\n"); + + if (hic == 0x0) goto abort; + + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME); + SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED); + + trace("HttpOpenRequestA <--\n"); + hor = HttpOpenRequestA(hic, "GET", "/about/", NULL, NULL, types, + INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, + 0xdeadbead); + if (hor == 0x0 && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) { + /* + * If the internet name can't be resolved we are probably behind + * a firewall or in some other way not directly connected to the + * Internet. Not enough reason to fail the test. Just ignore and + * abort. + */ + } else { + ok((hor != 0x0),"HttpOpenRequest failed with error %u\n", GetLastError()); + } + trace("HttpOpenRequestA -->\n"); + + if (hor == 0x0) goto abort; + + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + todo_wine + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } + if (first_connection_to_test_url) + { + SET_EXPECT(INTERNET_STATUS_RESOLVING_NAME); + SET_EXPECT(INTERNET_STATUS_NAME_RESOLVED); + } + else + { + SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME); + SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED); + } + SET_WINE_ALLOW(INTERNET_STATUS_CONNECTING_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER); + SET_WINE_ALLOW(INTERNET_STATUS_CONNECTED_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER); + SET_EXPECT2(INTERNET_STATUS_SENDING_REQUEST, 2); + SET_EXPECT2(INTERNET_STATUS_REQUEST_SENT, 2); + SET_EXPECT2(INTERNET_STATUS_RECEIVING_RESPONSE, 2); + SET_EXPECT2(INTERNET_STATUS_RESPONSE_RECEIVED, 2); + SET_EXPECT(INTERNET_STATUS_REDIRECT); + if (flags & INTERNET_FLAG_ASYNC) + SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); + else + SET_WINE_ALLOW(INTERNET_STATUS_REQUEST_COMPLETE); + + trace("HttpSendRequestA -->\n"); + SetLastError(0xdeadbeef); + rc = HttpSendRequestA(hor, "", -1, NULL, 0); + if (flags & INTERNET_FLAG_ASYNC) + ok(((rc == 0)&&(GetLastError() == ERROR_IO_PENDING)), + "Asynchronous HttpSendRequest NOT returning 0 with error ERROR_IO_PENDING\n"); + else + ok((rc != 0) || GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED, + "Synchronous HttpSendRequest returning 0, error %u\n", GetLastError()); + trace("HttpSendRequestA <--\n"); + + if (flags & INTERNET_FLAG_ASYNC) + WaitForSingleObject(hCompleteEvent, INFINITE); + + if (first_connection_to_test_url) + { + CHECK_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CHECK_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } + else todo_wine + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } + CHECK_NOTIFIED2(INTERNET_STATUS_SENDING_REQUEST, 2); + CHECK_NOTIFIED2(INTERNET_STATUS_REQUEST_SENT, 2); + CHECK_NOTIFIED2(INTERNET_STATUS_RECEIVING_RESPONSE, 2); + CHECK_NOTIFIED2(INTERNET_STATUS_RESPONSE_RECEIVED, 2); + CHECK_NOTIFIED(INTERNET_STATUS_REDIRECT); + if (flags & INTERNET_FLAG_ASYNC) + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + else + todo_wine CHECK_NOT_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + /* Sent on WinXP only if first_connection_to_test_url is TRUE, on Win98 always sent */ + CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTING_TO_SERVER); + CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTED_TO_SERVER); + + length = 4; + rc = InternetQueryOptionA(hor,INTERNET_OPTION_REQUEST_FLAGS,&out,&length); + trace("Option 0x17 -> %i %i\n",rc,out); + + length = 100; + rc = InternetQueryOptionA(hor,INTERNET_OPTION_URL,buffer,&length); + trace("Option 0x22 -> %i %s\n",rc,buffer); + + length = 4000; + rc = HttpQueryInfoA(hor,HTTP_QUERY_RAW_HEADERS,buffer,&length,0x0); + buffer[length]=0; + trace("Option 0x16 -> %i %s\n",rc,buffer); + + length = 4000; + rc = InternetQueryOptionA(hor,INTERNET_OPTION_URL,buffer,&length); + buffer[length]=0; + trace("Option 0x22 -> %i %s\n",rc,buffer); + + length = 16; + rc = HttpQueryInfoA(hor,HTTP_QUERY_CONTENT_LENGTH,&buffer,&length,0x0); + trace("Option 0x5 -> %i %s (%u)\n",rc,buffer,GetLastError()); + + length = 100; + rc = HttpQueryInfoA(hor,HTTP_QUERY_CONTENT_TYPE,buffer,&length,0x0); + buffer[length]=0; + trace("Option 0x1 -> %i %s\n",rc,buffer); + + SetLastError(0xdeadbeef); + rc = InternetReadFile(NULL, buffer, 100, &length); + ok(!rc, "InternetReadFile should have failed\n"); + ok(GetLastError() == ERROR_INVALID_HANDLE, + "InternetReadFile should have set last error to ERROR_INVALID_HANDLE instead of %u\n", + GetLastError()); + + length = 100; + trace("Entering Query loop\n"); + + SET_EXPECT(INTERNET_STATUS_CLOSING_CONNECTION); + SET_EXPECT(INTERNET_STATUS_CONNECTION_CLOSED); + while (TRUE) + { + if (flags & INTERNET_FLAG_ASYNC) + SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); + rc = InternetQueryDataAvailable(hor,&length,0x0,0x0); + ok(!(rc == 0 && length != 0),"InternetQueryDataAvailable failed with non-zero length\n"); + ok(rc != 0 || ((flags & INTERNET_FLAG_ASYNC) && GetLastError() == ERROR_IO_PENDING), + "InternetQueryDataAvailable failed, error %d\n", GetLastError()); + if (flags & INTERNET_FLAG_ASYNC) + { + if (rc != 0) + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + } + else if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(hCompleteEvent, INFINITE); + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + continue; + } + } + if (length) + { + char *buffer; + buffer = HeapAlloc(GetProcessHeap(),0,length+1); + + rc = InternetReadFile(hor,buffer,length,&length); + + buffer[length]=0; + + trace("ReadFile -> %i %i\n",rc,length); + + HeapFree(GetProcessHeap(),0,buffer); + } + if (length == 0) + break; + } + /* WinXP does not send, but Win98 does */ + CLEAR_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); +abort: + SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); + if (hor != 0x0) { + SET_WINE_ALLOW(INTERNET_STATUS_CLOSING_CONNECTION); + SET_WINE_ALLOW(INTERNET_STATUS_CONNECTION_CLOSED); + SetLastError(0xdeadbeef); + rc = InternetCloseHandle(hor); + ok ((rc != 0), "InternetCloseHandle of handle opened by HttpOpenRequestA failed\n"); + SetLastError(0xdeadbeef); + rc = InternetCloseHandle(hor); + ok ((rc == 0), "Double close of handle opened by HttpOpenRequestA succeeded\n"); + ok (GetLastError() == ERROR_INVALID_HANDLE, + "Double close of handle should have set ERROR_INVALID_HANDLE instead of %u\n", + GetLastError()); + } + /* We intentionally do not close the handle opened by InternetConnectA as this + * tickles bug #9479: native closes child internet handles when the parent handles + * are closed. This is verified below by checking that the number of + * INTERNET_STATUS_HANDLE_CLOSING notifications matches the number expected. */ + if (hi != 0x0) { + SET_WINE_ALLOW(INTERNET_STATUS_HANDLE_CLOSING); + rc = InternetCloseHandle(hi); + ok ((rc != 0), "InternetCloseHandle of handle opened by InternetOpenA failed\n"); + if (flags & INTERNET_FLAG_ASYNC) + Sleep(100); + } + CHECK_NOTIFIED2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); + if (hor != 0x0) todo_wine + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); + } + else + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); + } + CloseHandle(hCompleteEvent); + first_connection_to_test_url = FALSE; +} + +static void InternetReadFileExA_test(int flags) +{ + DWORD rc; + DWORD length; + const char *types[2] = { "*", NULL }; + HINTERNET hi, hic = 0, hor = 0; + INTERNET_BUFFERS inetbuffers; + + hCompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + trace("Starting InternetReadFileExA test with flags 0x%x\n",flags); + + trace("InternetOpenA <--\n"); + hi = InternetOpenA("", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, flags); + ok((hi != 0x0),"InternetOpen failed with error %u\n", GetLastError()); + trace("InternetOpenA -->\n"); + + if (hi == 0x0) goto abort; + + pInternetSetStatusCallbackA(hi,&callback); + + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + + trace("InternetConnectA <--\n"); + hic=InternetConnectA(hi, "www.winehq.org", INTERNET_INVALID_PORT_NUMBER, + NULL, NULL, INTERNET_SERVICE_HTTP, 0x0, 0xdeadbeef); + ok((hic != 0x0),"InternetConnect failed with error %u\n", GetLastError()); + trace("InternetConnectA -->\n"); + + if (hic == 0x0) goto abort; + + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + SET_EXPECT(INTERNET_STATUS_HANDLE_CREATED); + SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME); + SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED); + + trace("HttpOpenRequestA <--\n"); + hor = HttpOpenRequestA(hic, "GET", "/about/", NULL, NULL, types, + INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_RESYNCHRONIZE, + 0xdeadbead); + if (hor == 0x0 && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) { + /* + * If the internet name can't be resolved we are probably behind + * a firewall or in some other way not directly connected to the + * Internet. Not enough reason to fail the test. Just ignore and + * abort. + */ + } else { + ok((hor != 0x0),"HttpOpenRequest failed with error %u\n", GetLastError()); + } + trace("HttpOpenRequestA -->\n"); + + if (hor == 0x0) goto abort; + + CHECK_NOTIFIED(INTERNET_STATUS_HANDLE_CREATED); + todo_wine + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } + if (first_connection_to_test_url) + { + SET_EXPECT(INTERNET_STATUS_RESOLVING_NAME); + SET_EXPECT(INTERNET_STATUS_NAME_RESOLVED); + } + else + { + SET_WINE_ALLOW(INTERNET_STATUS_RESOLVING_NAME); + SET_WINE_ALLOW(INTERNET_STATUS_NAME_RESOLVED); + } + SET_WINE_ALLOW(INTERNET_STATUS_CONNECTING_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_CONNECTING_TO_SERVER); + SET_WINE_ALLOW(INTERNET_STATUS_CONNECTED_TO_SERVER); + SET_EXPECT(INTERNET_STATUS_CONNECTED_TO_SERVER); + SET_EXPECT2(INTERNET_STATUS_SENDING_REQUEST, 2); + SET_EXPECT2(INTERNET_STATUS_REQUEST_SENT, 2); + SET_EXPECT2(INTERNET_STATUS_RECEIVING_RESPONSE, 2); + SET_EXPECT2(INTERNET_STATUS_RESPONSE_RECEIVED, 2); + SET_EXPECT(INTERNET_STATUS_REDIRECT); + if (flags & INTERNET_FLAG_ASYNC) + SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); + else + SET_WINE_ALLOW(INTERNET_STATUS_REQUEST_COMPLETE); + + trace("HttpSendRequestA -->\n"); + SetLastError(0xdeadbeef); + rc = HttpSendRequestA(hor, "", -1, NULL, 0); + if (flags & INTERNET_FLAG_ASYNC) + ok(((rc == 0)&&(GetLastError() == ERROR_IO_PENDING)), + "Asynchronous HttpSendRequest NOT returning 0 with error ERROR_IO_PENDING\n"); + else + ok((rc != 0) || GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED, + "Synchronous HttpSendRequest returning 0, error %u\n", GetLastError()); + trace("HttpSendRequestA <--\n"); + + if (!rc && (GetLastError() == ERROR_IO_PENDING)) + WaitForSingleObject(hCompleteEvent, INFINITE); + + if (first_connection_to_test_url) + { + CHECK_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CHECK_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } + else todo_wine + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESOLVING_NAME); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_NAME_RESOLVED); + } + CHECK_NOTIFIED2(INTERNET_STATUS_SENDING_REQUEST, 2); + CHECK_NOTIFIED2(INTERNET_STATUS_REQUEST_SENT, 2); + CHECK_NOTIFIED2(INTERNET_STATUS_RECEIVING_RESPONSE, 2); + CHECK_NOTIFIED2(INTERNET_STATUS_RESPONSE_RECEIVED, 2); + CHECK_NOTIFIED(INTERNET_STATUS_REDIRECT); + if (flags & INTERNET_FLAG_ASYNC) + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + else + todo_wine CHECK_NOT_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + /* Sent on WinXP only if first_connection_to_test_url is TRUE, on Win98 always sent */ + CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTING_TO_SERVER); + CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTED_TO_SERVER); + + /* tests invalid dwStructSize */ + inetbuffers.dwStructSize = sizeof(INTERNET_BUFFERS)+1; + inetbuffers.lpcszHeader = NULL; + inetbuffers.dwHeadersLength = 0; + inetbuffers.dwBufferLength = 10; + inetbuffers.lpvBuffer = HeapAlloc(GetProcessHeap(), 0, 10); + inetbuffers.dwOffsetHigh = 1234; + inetbuffers.dwOffsetLow = 5678; + rc = InternetReadFileEx(hor, &inetbuffers, 0, 0xdeadcafe); + ok(!rc && (GetLastError() == ERROR_INVALID_PARAMETER), + "InternetReadFileEx should have failed with ERROR_INVALID_PARAMETER instead of %s, %u\n", + rc ? "TRUE" : "FALSE", GetLastError()); + HeapFree(GetProcessHeap(), 0, inetbuffers.lpvBuffer); + + /* tests to see whether lpcszHeader is used - it isn't */ + inetbuffers.dwStructSize = sizeof(INTERNET_BUFFERS); + inetbuffers.lpcszHeader = (LPCTSTR)0xdeadbeef; + inetbuffers.dwHeadersLength = 255; + inetbuffers.dwBufferLength = 0; + inetbuffers.lpvBuffer = NULL; + inetbuffers.dwOffsetHigh = 1234; + inetbuffers.dwOffsetLow = 5678; + SET_WINE_ALLOW(INTERNET_STATUS_RECEIVING_RESPONSE); + SET_WINE_ALLOW(INTERNET_STATUS_RESPONSE_RECEIVED); + rc = InternetReadFileEx(hor, &inetbuffers, 0, 0xdeadcafe); + ok(rc, "InternetReadFileEx failed with error %u\n", GetLastError()); + todo_wine + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_RECEIVING_RESPONSE); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESPONSE_RECEIVED); + } + + rc = InternetReadFileEx(NULL, &inetbuffers, 0, 0xdeadcafe); + ok(!rc && (GetLastError() == ERROR_INVALID_HANDLE), + "InternetReadFileEx should have failed with ERROR_INVALID_HANDLE instead of %s, %u\n", + rc ? "TRUE" : "FALSE", GetLastError()); + + length = 0; + trace("Entering Query loop\n"); + + SET_EXPECT(INTERNET_STATUS_CLOSING_CONNECTION); + SET_EXPECT(INTERNET_STATUS_CONNECTION_CLOSED); + while (TRUE) + { + inetbuffers.dwStructSize = sizeof(INTERNET_BUFFERS); + inetbuffers.dwBufferLength = 1024; + inetbuffers.lpvBuffer = HeapAlloc(GetProcessHeap(), 0, inetbuffers.dwBufferLength+1); + inetbuffers.dwOffsetHigh = 1234; + inetbuffers.dwOffsetLow = 5678; + + SET_EXPECT(INTERNET_STATUS_RECEIVING_RESPONSE); + SET_EXPECT(INTERNET_STATUS_REQUEST_COMPLETE); + SET_EXPECT(INTERNET_STATUS_RESPONSE_RECEIVED); + rc = InternetReadFileExA(hor, &inetbuffers, IRF_ASYNC | IRF_USE_CONTEXT, 0xcafebabe); + if (!rc) + { + if (GetLastError() == ERROR_IO_PENDING) + { + trace("InternetReadFileEx -> PENDING\n"); + CHECK_NOTIFIED(INTERNET_STATUS_RECEIVING_RESPONSE); + WaitForSingleObject(hCompleteEvent, INFINITE); + CHECK_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_RESPONSE_RECEIVED); + } + else + { + trace("InternetReadFileEx -> FAILED %u\n", GetLastError()); + break; + } + } + else + { + trace("InternetReadFileEx -> SUCCEEDED\n"); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_REQUEST_COMPLETE); + if (inetbuffers.dwBufferLength) + { + CHECK_NOTIFIED(INTERNET_STATUS_RECEIVING_RESPONSE); + CHECK_NOTIFIED(INTERNET_STATUS_RESPONSE_RECEIVED); + } + else + { + /* Win98 still sends these when 0 bytes are read, WinXP does not */ + CLEAR_NOTIFIED(INTERNET_STATUS_RECEIVING_RESPONSE); + CLEAR_NOTIFIED(INTERNET_STATUS_RESPONSE_RECEIVED); + } + } + + trace("read %i bytes\n", inetbuffers.dwBufferLength); + ((char *)inetbuffers.lpvBuffer)[inetbuffers.dwBufferLength] = '\0'; + + ok(inetbuffers.dwOffsetHigh == 1234 && inetbuffers.dwOffsetLow == 5678, + "InternetReadFileEx sets offsets to 0x%x%08x\n", + inetbuffers.dwOffsetHigh, inetbuffers.dwOffsetLow); + + HeapFree(GetProcessHeap(), 0, inetbuffers.lpvBuffer); + + if (!inetbuffers.dwBufferLength) + break; + + length += inetbuffers.dwBufferLength; + } + ok(length > 0, "failed to read any of the document\n"); + trace("Finished. Read %d bytes\n", length); + + /* WinXP does not send, but Win98 does */ + CLEAR_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CLEAR_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); +abort: + SET_EXPECT2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); + if (hor) { + SET_WINE_ALLOW(INTERNET_STATUS_CLOSING_CONNECTION); + SET_WINE_ALLOW(INTERNET_STATUS_CONNECTION_CLOSED); + rc = InternetCloseHandle(hor); + ok ((rc != 0), "InternetCloseHandle of handle opened by HttpOpenRequestA failed\n"); + rc = InternetCloseHandle(hor); + ok ((rc == 0), "Double close of handle opened by HttpOpenRequestA succeeded\n"); + } + if (hic) { + rc = InternetCloseHandle(hic); + ok ((rc != 0), "InternetCloseHandle of handle opened by InternetConnectA failed\n"); + } + if (hi) { + SET_WINE_ALLOW(INTERNET_STATUS_HANDLE_CLOSING); + rc = InternetCloseHandle(hi); + ok ((rc != 0), "InternetCloseHandle of handle opened by InternetOpenA failed\n"); + if (flags & INTERNET_FLAG_ASYNC) + Sleep(100); + CHECK_NOTIFIED2(INTERNET_STATUS_HANDLE_CLOSING, (hor != 0x0) + (hic != 0x0)); + } + if (hor != 0x0) todo_wine + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); + } + else + { + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CLOSING_CONNECTION); + CHECK_NOT_NOTIFIED(INTERNET_STATUS_CONNECTION_CLOSED); + } + CloseHandle(hCompleteEvent); + first_connection_to_test_url = FALSE; +} + +static void InternetOpenUrlA_test(void) +{ + HINTERNET myhinternet, myhttp; + char buffer[0x400]; + DWORD size, readbytes, totalbytes=0; + BOOL ret; + + myhinternet = InternetOpen("Winetest",0,NULL,NULL,INTERNET_FLAG_NO_CACHE_WRITE); + ok((myhinternet != 0), "InternetOpen failed, error %u\n",GetLastError()); + size = 0x400; + ret = InternetCanonicalizeUrl(TEST_URL, buffer, &size,ICU_BROWSER_MODE); + ok( ret, "InternetCanonicalizeUrl failed, error %u\n",GetLastError()); + + SetLastError(0); + myhttp = InternetOpenUrl(myhinternet, TEST_URL, 0, 0, + INTERNET_FLAG_RELOAD|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_TRANSFER_BINARY,0); + if (GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) + return; /* WinXP returns this when not connected to the net */ + ok((myhttp != 0),"InternetOpenUrl failed, error %u\n",GetLastError()); + ret = InternetReadFile(myhttp, buffer,0x400,&readbytes); + ok( ret, "InternetReadFile failed, error %u\n",GetLastError()); + totalbytes += readbytes; + while (readbytes && InternetReadFile(myhttp, buffer,0x400,&readbytes)) + totalbytes += readbytes; + trace("read 0x%08x bytes\n",totalbytes); + + InternetCloseHandle(myhttp); + InternetCloseHandle(myhinternet); +} + +static void InternetTimeFromSystemTimeA_test(void) +{ + BOOL ret; + static const SYSTEMTIME time = { 2005, 1, 5, 7, 12, 6, 35, 0 }; + char string[INTERNET_RFC1123_BUFSIZE]; + static const char expect[] = "Fri, 07 Jan 2005 12:06:35 GMT"; + + ret = pInternetTimeFromSystemTimeA( &time, INTERNET_RFC1123_FORMAT, string, sizeof(string) ); + ok( ret, "InternetTimeFromSystemTimeA failed (%u)\n", GetLastError() ); + + ok( !memcmp( string, expect, sizeof(expect) ), + "InternetTimeFromSystemTimeA failed (%u)\n", GetLastError() ); +} + +static void InternetTimeFromSystemTimeW_test(void) +{ + BOOL ret; + static const SYSTEMTIME time = { 2005, 1, 5, 7, 12, 6, 35, 0 }; + WCHAR string[INTERNET_RFC1123_BUFSIZE + 1]; + static const WCHAR expect[] = { 'F','r','i',',',' ','0','7',' ','J','a','n',' ','2','0','0','5',' ', + '1','2',':','0','6',':','3','5',' ','G','M','T',0 }; + + ret = pInternetTimeFromSystemTimeW( &time, INTERNET_RFC1123_FORMAT, string, sizeof(string) ); + ok( ret, "InternetTimeFromSystemTimeW failed (%u)\n", GetLastError() ); + + ok( !memcmp( string, expect, sizeof(expect) ), + "InternetTimeFromSystemTimeW failed (%u)\n", GetLastError() ); +} + +static void InternetTimeToSystemTimeA_test(void) +{ + BOOL ret; + SYSTEMTIME time; + static const SYSTEMTIME expect = { 2005, 1, 5, 7, 12, 6, 35, 0 }; + static const char string[] = "Fri, 07 Jan 2005 12:06:35 GMT"; + static const char string2[] = " fri 7 jan 2005 12 06 35"; + + ret = pInternetTimeToSystemTimeA( string, &time, 0 ); + ok( ret, "InternetTimeToSystemTimeA failed (%u)\n", GetLastError() ); + ok( !memcmp( &time, &expect, sizeof(expect) ), + "InternetTimeToSystemTimeA failed (%u)\n", GetLastError() ); + + ret = pInternetTimeToSystemTimeA( string2, &time, 0 ); + ok( ret, "InternetTimeToSystemTimeA failed (%u)\n", GetLastError() ); + ok( !memcmp( &time, &expect, sizeof(expect) ), + "InternetTimeToSystemTimeA failed (%u)\n", GetLastError() ); +} + +static void InternetTimeToSystemTimeW_test(void) +{ + BOOL ret; + SYSTEMTIME time; + static const SYSTEMTIME expect = { 2005, 1, 5, 7, 12, 6, 35, 0 }; + static const WCHAR string[] = { 'F','r','i',',',' ','0','7',' ','J','a','n',' ','2','0','0','5',' ', + '1','2',':','0','6',':','3','5',' ','G','M','T',0 }; + static const WCHAR string2[] = { ' ','f','r','i',' ','7',' ','j','a','n',' ','2','0','0','5',' ', + '1','2',' ','0','6',' ','3','5',0 }; + static const WCHAR string3[] = { 'F','r',0 }; + + ret = pInternetTimeToSystemTimeW( NULL, NULL, 0 ); + ok( !ret, "InternetTimeToSystemTimeW succeeded (%u)\n", GetLastError() ); + + ret = pInternetTimeToSystemTimeW( NULL, &time, 0 ); + ok( !ret, "InternetTimeToSystemTimeW succeeded (%u)\n", GetLastError() ); + + ret = pInternetTimeToSystemTimeW( string, NULL, 0 ); + ok( !ret, "InternetTimeToSystemTimeW succeeded (%u)\n", GetLastError() ); + + ret = pInternetTimeToSystemTimeW( string, &time, 0 ); + ok( ret, "InternetTimeToSystemTimeW failed (%u)\n", GetLastError() ); + + ret = pInternetTimeToSystemTimeW( string, &time, 0 ); + ok( ret, "InternetTimeToSystemTimeW failed (%u)\n", GetLastError() ); + ok( !memcmp( &time, &expect, sizeof(expect) ), + "InternetTimeToSystemTimeW failed (%u)\n", GetLastError() ); + + ret = pInternetTimeToSystemTimeW( string2, &time, 0 ); + ok( ret, "InternetTimeToSystemTimeW failed (%u)\n", GetLastError() ); + ok( !memcmp( &time, &expect, sizeof(expect) ), + "InternetTimeToSystemTimeW failed (%u)\n", GetLastError() ); + + ret = pInternetTimeToSystemTimeW( string3, &time, 0 ); + ok( ret, "InternetTimeToSystemTimeW failed (%u)\n", GetLastError() ); +} + +static void HttpSendRequestEx_test(void) +{ + HINTERNET hSession; + HINTERNET hConnect; + HINTERNET hRequest; + + INTERNET_BUFFERS BufferIn; + DWORD dwBytesWritten; + DWORD dwBytesRead; + CHAR szBuffer[256]; + int i; + BOOL ret; + + static char szPostData[] = "mode=Test"; + static const char szContentType[] = "Content-Type: application/x-www-form-urlencoded"; + + hSession = InternetOpen("Wine Regression Test", + INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0); + ok( hSession != NULL ,"Unable to open Internet session\n"); + hConnect = InternetConnect(hSession, "crossover.codeweavers.com", + INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, + 0); + ok( hConnect != NULL, "Unable to connect to http://crossover.codeweavers.com\n"); + hRequest = HttpOpenRequest(hConnect, "POST", "/posttest.php", + NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); + if (!hRequest && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) + { + trace( "Network unreachable, skipping test\n" ); + goto done; + } + ok( hRequest != NULL, "Failed to open request handle err %u\n", GetLastError()); + + + BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS); + BufferIn.Next = (LPINTERNET_BUFFERS)0xdeadcab; + BufferIn.lpcszHeader = szContentType; + BufferIn.dwHeadersLength = sizeof(szContentType); + BufferIn.dwHeadersTotal = sizeof(szContentType); + BufferIn.lpvBuffer = szPostData; + BufferIn.dwBufferLength = 3; + BufferIn.dwBufferTotal = sizeof(szPostData)-1; + BufferIn.dwOffsetLow = 0; + BufferIn.dwOffsetHigh = 0; + + ret = HttpSendRequestEx(hRequest, &BufferIn, NULL, 0 ,0); + ok(ret, "HttpSendRequestEx Failed with error %u\n", GetLastError()); + + for (i = 3; szPostData[i]; i++) + ok(InternetWriteFile(hRequest, &szPostData[i], 1, &dwBytesWritten), + "InternetWriteFile failed\n"); + + ok(HttpEndRequest(hRequest, NULL, 0, 0), "HttpEndRequest Failed\n"); + + ok(InternetReadFile(hRequest, szBuffer, 255, &dwBytesRead), + "Unable to read response\n"); + szBuffer[dwBytesRead] = 0; + + ok(dwBytesRead == 13,"Read %u bytes instead of 13\n",dwBytesRead); + ok(strncmp(szBuffer,"mode => Test\n",dwBytesRead)==0,"Got string %s\n",szBuffer); + + ok(InternetCloseHandle(hRequest), "Close request handle failed\n"); +done: + ok(InternetCloseHandle(hConnect), "Close connect handle failed\n"); + ok(InternetCloseHandle(hSession), "Close session handle failed\n"); +} + +static void InternetOpenRequest_test(void) +{ + HINTERNET session, connect, request; + static const char *types[] = { "*", "", NULL }; + static const WCHAR slash[] = {'/', 0}, any[] = {'*', 0}, empty[] = {0}; + static const WCHAR *typesW[] = { any, empty, NULL }; + BOOL ret; + + session = InternetOpenA("Wine Regression Test", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + ok(session != NULL ,"Unable to open Internet session\n"); + + connect = InternetConnectA(session, "winehq.org", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, + INTERNET_SERVICE_HTTP, 0, 0); + ok(connect != NULL, "Unable to connect to http://winehq.org\n"); + + request = HttpOpenRequestA(connect, NULL, "/", NULL, NULL, types, INTERNET_FLAG_NO_CACHE_WRITE, 0); + if (!request && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) + { + trace( "Network unreachable, skipping test\n" ); + goto done; + } + ok(request != NULL, "Failed to open request handle err %u\n", GetLastError()); + + ret = HttpSendRequest(request, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed: %u\n", GetLastError()); + ok(InternetCloseHandle(request), "Close request handle failed\n"); + + request = HttpOpenRequestW(connect, NULL, slash, NULL, NULL, typesW, INTERNET_FLAG_NO_CACHE_WRITE, 0); + ok(request != NULL, "Failed to open request handle err %u\n", GetLastError()); + + ret = HttpSendRequest(request, NULL, 0, NULL, 0); + ok(ret, "HttpSendRequest failed: %u\n", GetLastError()); + ok(InternetCloseHandle(request), "Close request handle failed\n"); + +done: + ok(InternetCloseHandle(connect), "Close connect handle failed\n"); + ok(InternetCloseHandle(session), "Close session handle failed\n"); +} + +static void HttpHeaders_test(void) +{ + HINTERNET hSession; + HINTERNET hConnect; + HINTERNET hRequest; + CHAR buffer[256]; + DWORD len = 256; + DWORD index = 0; + + hSession = InternetOpen("Wine Regression Test", + INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0); + ok( hSession != NULL ,"Unable to open Internet session\n"); + hConnect = InternetConnect(hSession, "crossover.codeweavers.com", + INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, + 0); + ok( hConnect != NULL, "Unable to connect to http://crossover.codeweavers.com\n"); + hRequest = HttpOpenRequest(hConnect, "POST", "/posttest.php", + NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0); + if (!hRequest && GetLastError() == ERROR_INTERNET_NAME_NOT_RESOLVED) + { + trace( "Network unreachable, skipping test\n" ); + goto done; + } + ok( hRequest != NULL, "Failed to open request handle\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index)==0,"Warning hearder reported as Existing\n"); + + ok(HttpAddRequestHeaders(hRequest,"Warning:test1",-1,HTTP_ADDREQ_FLAG_ADD), + "Failed to add new header\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test1")==0, "incorrect string was returned(%s)\n",buffer); + ok(len == 5, "Invalid length (exp. 5, got %d)\n", len); + ok(buffer[len] == 0, "Buffer not NULL-terminated\n"); /* len show only 5 characters but the buffer is NULL-terminated*/ + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index)==0,"Second Index Should Not Exist\n"); + + index = 0; + len = 5; /* could store the string but not the NULL terminator */ + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index) == FALSE,"Query succeeded on a too small buffer\n"); + ok(strcmp(buffer,"Warning")==0, "incorrect string was returned(%s)\n",buffer); /* string not touched */ + ok(len == 6, "Invalid length (exp. 6, got %d)\n", len); /* unlike success, the length includes the NULL-terminator */ + + /* a call with NULL will fail but will return the length */ + index = 0; + len = sizeof(buffer); + SetLastError(0xdeadbeef); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_RAW_HEADERS_CRLF|HTTP_QUERY_FLAG_REQUEST_HEADERS, + NULL,&len,&index) == FALSE,"Query worked\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected last error: %d\n", GetLastError()); + ok(len > 40, "Invalid length (exp. more than 40, got %d)\n", len); + ok(index == 0, "Index was incremented\n"); + + /* even for a len that is too small */ + index = 0; + len = 15; + SetLastError(0xdeadbeef); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_RAW_HEADERS_CRLF|HTTP_QUERY_FLAG_REQUEST_HEADERS, + NULL,&len,&index) == FALSE,"Query worked\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected last error: %d\n", GetLastError()); + ok(len > 40, "Invalid length (exp. more than 40, got %d)\n", len); + ok(index == 0, "Index was incremented\n"); + + index = 0; + len = 0; + SetLastError(0xdeadbeef); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_RAW_HEADERS_CRLF|HTTP_QUERY_FLAG_REQUEST_HEADERS, + NULL,&len,&index) == FALSE,"Query worked\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected last error: %d\n", GetLastError()); + ok(len > 40, "Invalid length (exp. more than 40, got %d)\n", len); + ok(index == 0, "Index was incremented\n"); + + + /* a working query */ + index = 0; + len = sizeof(buffer); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_RAW_HEADERS_CRLF|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Unable to query header\n"); + /* what's in the middle differs between Wine and Windows so currently we check only the beginning and the end */ + ok(strncmp(buffer, "POST /posttest.php HTTP/1", 25)==0, "Invalid beginning of headers string\n"); + ok(strcmp(buffer + strlen(buffer) - 4, "\r\n\r\n")==0, "Invalid end of headers string\n"); + ok(index == 0, "Index was incremented\n"); + + + + ok(HttpAddRequestHeaders(hRequest,"Warning:test2",-1,HTTP_ADDREQ_FLAG_ADD), + "Failed to add duplicate header using HTTP_ADDREQ_FLAG_ADD\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test1")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Failed to get second header\n"); + ok(index == 2, "Index was not incremented\n"); + ok(strcmp(buffer,"test2")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index)==0,"Third Header Should Not Exist\n"); + + ok(HttpAddRequestHeaders(hRequest,"Warning:test3",-1,HTTP_ADDREQ_FLAG_REPLACE), "Failed to replace header using HTTP_ADDREQ_FLAG_REPLACE\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test2")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Failed to get second header\n"); + ok(index == 2, "Index was not incremented\n"); + ok(strcmp(buffer,"test3")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index)==0,"Third Header Should Not Exist\n"); + + ok(HttpAddRequestHeaders(hRequest,"Warning:test4",-1,HTTP_ADDREQ_FLAG_ADD_IF_NEW)==0, "HTTP_ADDREQ_FLAG_ADD_IF_NEW replaced existing header\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test2")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Failed to get second header\n"); + ok(index == 2, "Index was not incremented\n"); + ok(strcmp(buffer,"test3")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index)==0,"Third Header Should Not Exist\n"); + + ok(HttpAddRequestHeaders(hRequest,"Warning:test4",-1, HTTP_ADDREQ_FLAG_COALESCE), "HTTP_ADDREQ_FLAG_COALESCE Did not work\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, + buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test2, test4")==0, "incorrect string was returned(%s)\n", buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index),"Failed to get second header\n"); + ok(index == 2, "Index was not incremented\n"); + ok(strcmp(buffer,"test3")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index)==0,"Third Header Should Not Exist\n"); + + ok(HttpAddRequestHeaders(hRequest,"Warning:test5",-1, HTTP_ADDREQ_FLAG_COALESCE_WITH_COMMA), "HTTP_ADDREQ_FLAG_COALESCE Did not work\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test2, test4, test5")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index),"Failed to get second header\n"); + ok(index == 2, "Index was not incremented\n"); + ok(strcmp(buffer,"test3")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index)==0,"Third Header Should Not Exist\n"); + + ok(HttpAddRequestHeaders(hRequest,"Warning:test6",-1, HTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON), "HTTP_ADDREQ_FLAG_COALESCE Did not work\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test2, test4, test5; test6")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index),"Failed to get second header\n"); + ok(index == 2, "Index was not incremented\n"); + ok(strcmp(buffer,"test3")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index)==0,"Third Header Should Not Exist\n"); + + ok(HttpAddRequestHeaders(hRequest,"Warning:test7",-1, HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE), "HTTP_ADDREQ_FLAG_ADD with HTTP_ADDREQ_FLAG_REPALCE Did not work\n"); + + index = 0; + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index),"Unable to query header\n"); + ok(index == 1, "Index was not incremented\n"); + ok(strcmp(buffer,"test3")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index),"Failed to get second header\n"); + ok(index == 2, "Index was not incremented\n"); + ok(strcmp(buffer,"test7")==0, "incorrect string was returned(%s)\n",buffer); + len = sizeof(buffer); + strcpy(buffer,"Warning"); + ok(HttpQueryInfo(hRequest,HTTP_QUERY_CUSTOM|HTTP_QUERY_FLAG_REQUEST_HEADERS, buffer,&len,&index)==0,"Third Header Should Not Exist\n"); + + + ok(InternetCloseHandle(hRequest), "Close request handle failed\n"); +done: + ok(InternetCloseHandle(hConnect), "Close connect handle failed\n"); + ok(InternetCloseHandle(hSession), "Close session handle failed\n"); +} + +static const char okmsg[] = +"HTTP/1.0 200 OK\r\n" +"Server: winetest\r\n" +"\r\n"; + +static const char notokmsg[] = +"HTTP/1.0 400 Bad Request\r\n" +"Server: winetest\r\n" +"\r\n"; + +static const char noauthmsg[] = +"HTTP/1.0 401 Unauthorized\r\n" +"Server: winetest\r\n" +"\r\n"; + +static const char proxymsg[] = +"HTTP/1.1 407 Proxy Authentication Required\r\n" +"Server: winetest\r\n" +"Proxy-Connection: close\r\n" +"Proxy-Authenticate: Basic realm=\"placebo\"\r\n" +"\r\n"; + +static const char page1[] = +"\r\n" +"wininet test page\r\n" +"The quick brown fox jumped over the lazy dog

\r\n" +"\r\n\r\n"; + +struct server_info { + HANDLE hEvent; + int port; +}; + +static DWORD CALLBACK server_thread(LPVOID param) +{ + struct server_info *si = param; + int r, c, i, on; + SOCKET s; + struct sockaddr_in sa; + char buffer[0x100]; + WSADATA wsaData; + int last_request = 0; + + WSAStartup(MAKEWORD(1,1), &wsaData); + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + return 1; + + on = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof on); + + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(si->port); + sa.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); + + r = bind(s, (struct sockaddr*) &sa, sizeof sa); + if (r<0) + return 1; + + listen(s, 0); + + SetEvent(si->hEvent); + + do + { + c = accept(s, NULL, NULL); + + memset(buffer, 0, sizeof buffer); + for(i=0; i<(sizeof buffer-1); i++) + { + r = recv(c, &buffer[i], 1, 0); + if (r != 1) + break; + if (i<4) continue; + if (buffer[i-2] == '\n' && buffer[i] == '\n' && + buffer[i-3] == '\r' && buffer[i-1] == '\r') + break; + } + + if (strstr(buffer, "GET /test1")) + { + send(c, okmsg, sizeof okmsg-1, 0); + send(c, page1, sizeof page1-1, 0); + } + + if (strstr(buffer, "/test2")) + { + if (strstr(buffer, "Proxy-Authorization: Basic bWlrZToxMTAx")) + { + send(c, okmsg, sizeof okmsg-1, 0); + send(c, page1, sizeof page1-1, 0); + } + else + send(c, proxymsg, sizeof proxymsg-1, 0); + } + + if (strstr(buffer, "/test3")) + { + if (strstr(buffer, "Authorization: Basic dXNlcjpwd2Q=")) + send(c, okmsg, sizeof okmsg-1, 0); + else + send(c, noauthmsg, sizeof noauthmsg-1, 0); + } + + if (strstr(buffer, "/test4")) + { + if (strstr(buffer, "Connection: Close")) + send(c, okmsg, sizeof okmsg-1, 0); + else + send(c, notokmsg, sizeof notokmsg-1, 0); + } + + if (strstr(buffer, "/test5")) + { + if (strstr(buffer, "Content-Length: 0")) + { + send(c, okmsg, sizeof okmsg-1, 0); + send(c, page1, sizeof page1-1, 0); + } + else + send(c, notokmsg, sizeof notokmsg-1, 0); + } + + if (strstr(buffer, "/quit")) + { + send(c, okmsg, sizeof okmsg-1, 0); + send(c, page1, sizeof page1-1, 0); + last_request = 1; + } + + shutdown(c, 2); + closesocket(c); + } while (!last_request); + + closesocket(s); + + return 0; +} + +static void test_basic_request(int port, const char *url) +{ + HINTERNET hi, hc, hr; + DWORD r, count; + char buffer[0x100]; + + hi = InternetOpen(NULL, 0, NULL, NULL, 0); + ok(hi != NULL, "open failed\n"); + + hc = InternetConnect(hi, "localhost", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok(hc != NULL, "connect failed\n"); + + hr = HttpOpenRequest(hc, NULL, url, NULL, NULL, NULL, 0, 0); + ok(hr != NULL, "HttpOpenRequest failed\n"); + + r = HttpSendRequest(hr, NULL, 0, NULL, 0); + ok(r, "HttpSendRequest failed\n"); + + count = 0; + memset(buffer, 0, sizeof buffer); + r = InternetReadFile(hr, buffer, sizeof buffer, &count); + ok(r, "InternetReadFile failed\n"); + ok(count == sizeof page1 - 1, "count was wrong\n"); + ok(!memcmp(buffer, page1, sizeof page1), "http data wrong\n"); + + InternetCloseHandle(hr); + InternetCloseHandle(hc); + InternetCloseHandle(hi); +} + +static void test_proxy_indirect(int port) +{ + HINTERNET hi, hc, hr; + DWORD r, sz, val; + char buffer[0x40]; + + hi = InternetOpen(NULL, 0, NULL, NULL, 0); + ok(hi != NULL, "open failed\n"); + + hc = InternetConnect(hi, "localhost", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok(hc != NULL, "connect failed\n"); + + hr = HttpOpenRequest(hc, NULL, "/test2", NULL, NULL, NULL, 0, 0); + ok(hr != NULL, "HttpOpenRequest failed\n"); + + r = HttpSendRequest(hr, NULL, 0, NULL, 0); + ok(r, "HttpSendRequest failed\n"); + + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_PROXY_AUTHENTICATE, buffer, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + ok(!strcmp(buffer, "Basic realm=\"placebo\""), "proxy auth info wrong\n"); + + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_STATUS_CODE, buffer, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + ok(!strcmp(buffer, "407"), "proxy code wrong\n"); + + sz = sizeof val; + r = HttpQueryInfo(hr, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, &val, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + ok(val == 407, "proxy code wrong\n"); + + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_STATUS_TEXT, buffer, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + ok(!strcmp(buffer, "Proxy Authentication Required"), "proxy text wrong\n"); + + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_VERSION, buffer, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + ok(!strcmp(buffer, "HTTP/1.1"), "http version wrong\n"); + + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_SERVER, buffer, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + ok(!strcmp(buffer, "winetest"), "http server wrong\n"); + + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_CONTENT_ENCODING, buffer, &sz, NULL); + ok(GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND, "HttpQueryInfo should fail\n"); + ok(r == FALSE, "HttpQueryInfo failed\n"); + + InternetCloseHandle(hr); + InternetCloseHandle(hc); + InternetCloseHandle(hi); +} + +static void test_proxy_direct(int port) +{ + HINTERNET hi, hc, hr; + DWORD r, sz; + char buffer[0x40]; + static CHAR username[] = "mike", + password[] = "1101"; + + sprintf(buffer, "localhost:%d\n", port); + hi = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, buffer, NULL, 0); + ok(hi != NULL, "open failed\n"); + + /* try connect without authorization */ + hc = InternetConnect(hi, "www.winehq.org/", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok(hc != NULL, "connect failed\n"); + + hr = HttpOpenRequest(hc, NULL, "/test2", NULL, NULL, NULL, 0, 0); + ok(hr != NULL, "HttpOpenRequest failed\n"); + + r = HttpSendRequest(hr, NULL, 0, NULL, 0); + ok(r, "HttpSendRequest failed\n"); + + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_STATUS_CODE, buffer, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + ok(!strcmp(buffer, "407"), "proxy code wrong\n"); + + + /* set the user + password then try again */ + todo_wine { + r = InternetSetOption(hr, INTERNET_OPTION_PROXY_USERNAME, username, 4); + ok(r, "failed to set user\n"); + + r = InternetSetOption(hr, INTERNET_OPTION_PROXY_PASSWORD, password, 4); + ok(r, "failed to set password\n"); + } + + r = HttpSendRequest(hr, NULL, 0, NULL, 0); + ok(r, "HttpSendRequest failed\n"); + sz = sizeof buffer; + r = HttpQueryInfo(hr, HTTP_QUERY_STATUS_CODE, buffer, &sz, NULL); + ok(r, "HttpQueryInfo failed\n"); + todo_wine { + ok(!strcmp(buffer, "200"), "proxy code wrong\n"); + } + + + InternetCloseHandle(hr); + InternetCloseHandle(hc); + InternetCloseHandle(hi); +} + +static void test_header_handling_order(int port) +{ + static char authorization[] = "Authorization: Basic dXNlcjpwd2Q="; + static char connection[] = "Connection: Close"; + + static const char *types[2] = { "*", NULL }; + HINTERNET session, connect, request; + DWORD size, status; + BOOL ret; + + session = InternetOpen("winetest", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + ok(session != NULL, "InternetOpen failed\n"); + + connect = InternetConnect(session, "localhost", port, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok(connect != NULL, "InternetConnect failed\n"); + + request = HttpOpenRequest(connect, NULL, "/test3", NULL, NULL, types, INTERNET_FLAG_KEEP_CONNECTION, 0); + ok(request != NULL, "HttpOpenRequest failed\n"); + + ret = HttpSendRequest(request, authorization, ~0UL, NULL, 0); + ok(ret, "HttpSendRequest failed\n"); + + status = 0; + size = sizeof(status); + ret = HttpQueryInfo( request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &size, NULL ); + ok(ret, "HttpQueryInfo failed\n"); + ok(status == 200, "request failed with status %u\n", status); + + InternetCloseHandle(request); + + request = HttpOpenRequest(connect, NULL, "/test4", NULL, NULL, types, INTERNET_FLAG_KEEP_CONNECTION, 0); + ok(request != NULL, "HttpOpenRequest failed\n"); + + ret = HttpSendRequest(request, connection, ~0UL, NULL, 0); + ok(ret, "HttpSendRequest failed\n"); + + status = 0; + size = sizeof(status); + ret = HttpQueryInfo( request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &size, NULL ); + ok(ret, "HttpQueryInfo failed\n"); + ok(status == 200, "request failed with status %u\n", status); + + InternetCloseHandle(request); + InternetCloseHandle(connect); + InternetCloseHandle(session); +} + +static void test_http_connection(void) +{ + struct server_info si; + HANDLE hThread; + DWORD id = 0, r; + + si.hEvent = CreateEvent(NULL, 0, 0, NULL); + si.port = 7531; + + hThread = CreateThread(NULL, 0, server_thread, (LPVOID) &si, 0, &id); + ok( hThread != NULL, "create thread failed\n"); + + r = WaitForSingleObject(si.hEvent, 10000); + ok (r == WAIT_OBJECT_0, "failed to start wininet test server\n"); + if (r != WAIT_OBJECT_0) + return; + + test_basic_request(si.port, "/test1"); + test_proxy_indirect(si.port); + test_proxy_direct(si.port); + test_header_handling_order(si.port); + test_basic_request(si.port, "/test5"); + + /* send the basic request again to shutdown the server thread */ + test_basic_request(si.port, "/quit"); + + r = WaitForSingleObject(hThread, 3000); + ok( r == WAIT_OBJECT_0, "thread wait failed\n"); + CloseHandle(hThread); +} + +#define STATUS_STRING(status) \ + memcpy(status_string[status], #status, sizeof(CHAR) * \ + (strlen(#status) < MAX_STATUS_NAME ? \ + strlen(#status) : \ + MAX_STATUS_NAME - 1)) +static void init_status_tests(void) +{ + memset(expect, 0, sizeof(expect)); + memset(wine_allow, 0, sizeof(wine_allow)); + memset(notified, 0, sizeof(notified)); + memset(status_string, 0, sizeof(status_string)); + STATUS_STRING(INTERNET_STATUS_RESOLVING_NAME); + STATUS_STRING(INTERNET_STATUS_NAME_RESOLVED); + STATUS_STRING(INTERNET_STATUS_CONNECTING_TO_SERVER); + STATUS_STRING(INTERNET_STATUS_CONNECTED_TO_SERVER); + STATUS_STRING(INTERNET_STATUS_SENDING_REQUEST); + STATUS_STRING(INTERNET_STATUS_REQUEST_SENT); + STATUS_STRING(INTERNET_STATUS_RECEIVING_RESPONSE); + STATUS_STRING(INTERNET_STATUS_RESPONSE_RECEIVED); + STATUS_STRING(INTERNET_STATUS_CTL_RESPONSE_RECEIVED); + STATUS_STRING(INTERNET_STATUS_PREFETCH); + STATUS_STRING(INTERNET_STATUS_CLOSING_CONNECTION); + STATUS_STRING(INTERNET_STATUS_CONNECTION_CLOSED); + STATUS_STRING(INTERNET_STATUS_HANDLE_CREATED); + STATUS_STRING(INTERNET_STATUS_HANDLE_CLOSING); + STATUS_STRING(INTERNET_STATUS_REQUEST_COMPLETE); + STATUS_STRING(INTERNET_STATUS_REDIRECT); + STATUS_STRING(INTERNET_STATUS_INTERMEDIATE_RESPONSE); + STATUS_STRING(INTERNET_STATUS_USER_INPUT_REQUIRED); + STATUS_STRING(INTERNET_STATUS_STATE_CHANGE); + STATUS_STRING(INTERNET_STATUS_COOKIE_SENT); + STATUS_STRING(INTERNET_STATUS_COOKIE_RECEIVED); + STATUS_STRING(INTERNET_STATUS_PRIVACY_IMPACTED); + STATUS_STRING(INTERNET_STATUS_P3P_HEADER); + STATUS_STRING(INTERNET_STATUS_P3P_POLICYREF); + STATUS_STRING(INTERNET_STATUS_COOKIE_HISTORY); +} +#undef STATUS_STRING + +START_TEST(http) +{ + HMODULE hdll; + hdll = GetModuleHandleA("wininet.dll"); + pInternetSetStatusCallbackA = (void*)GetProcAddress(hdll, "InternetSetStatusCallbackA"); + pInternetTimeFromSystemTimeA = (void*)GetProcAddress(hdll, "InternetTimeFromSystemTimeA"); + pInternetTimeFromSystemTimeW = (void*)GetProcAddress(hdll, "InternetTimeFromSystemTimeW"); + pInternetTimeToSystemTimeA = (void*)GetProcAddress(hdll, "InternetTimeToSystemTimeA"); + pInternetTimeToSystemTimeW = (void*)GetProcAddress(hdll, "InternetTimeToSystemTimeW"); + + if (!pInternetSetStatusCallbackA) + skip("skipping the InternetReadFile tests\n"); + else + { + init_status_tests(); + InternetReadFile_test(INTERNET_FLAG_ASYNC); + InternetReadFile_test(0); + InternetReadFileExA_test(INTERNET_FLAG_ASYNC); + } + InternetOpenRequest_test(); + InternetOpenUrlA_test(); + if (!pInternetTimeFromSystemTimeA) + skip("skipping the InternetTime tests\n"); + else + { + InternetTimeFromSystemTimeA_test(); + InternetTimeFromSystemTimeW_test(); + InternetTimeToSystemTimeA_test(); + InternetTimeToSystemTimeW_test(); + } + HttpSendRequestEx_test(); + HttpHeaders_test(); + test_http_connection(); +} diff --git a/rostests/winetests/wininet/internet.c b/rostests/winetests/wininet/internet.c new file mode 100644 index 00000000000..8704f7dbd56 --- /dev/null +++ b/rostests/winetests/wininet/internet.c @@ -0,0 +1,332 @@ +/* + * Wininet - internet tests + * + * 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 +#include +#include "windef.h" +#include "winbase.h" +#include "wininet.h" +#include "winerror.h" + +#include "wine/test.h" + +/* ############################### */ + +static void test_InternetCanonicalizeUrlA(void) +{ + CHAR buffer[256]; + LPCSTR url; + DWORD urllen; + DWORD dwSize; + DWORD res; + + /* Acrobat Updater 5 calls this for Adobe Reader 8.1 */ + url = "http://swupmf.adobe.com/manifest/50/win/AdobeUpdater.upd"; + urllen = lstrlenA(url); + + memset(buffer, '#', sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; + dwSize = 1; /* Acrobat Updater use this size */ + SetLastError(0xdeadbeef); + res = InternetCanonicalizeUrlA(url, buffer, &dwSize, 0); + ok( !res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && (dwSize == (urllen+1)), + "got %u and %u with size %u for '%s' (%d)\n", + res, GetLastError(), dwSize, buffer, lstrlenA(buffer)); + + + /* buffer has no space for the terminating '\0' */ + memset(buffer, '#', sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; + dwSize = urllen; + SetLastError(0xdeadbeef); + res = InternetCanonicalizeUrlA(url, buffer, &dwSize, 0); + /* dwSize is nr. of needed bytes with the terminating '\0' */ + ok( !res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) && (dwSize == (urllen+1)), + "got %u and %u with size %u for '%s' (%d)\n", + res, GetLastError(), dwSize, buffer, lstrlenA(buffer)); + + /* buffer has the required size */ + memset(buffer, '#', sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; + dwSize = urllen+1; + SetLastError(0xdeadbeef); + res = InternetCanonicalizeUrlA(url, buffer, &dwSize, 0); + /* dwSize is nr. of copied bytes without the terminating '\0' */ + ok( res && (dwSize == urllen) && (lstrcmpA(url, buffer) == 0), + "got %u and %u with size %u for '%s' (%d)\n", + res, GetLastError(), dwSize, buffer, lstrlenA(buffer)); + + /* buffer is larger as the required size */ + memset(buffer, '#', sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; + dwSize = urllen+2; + SetLastError(0xdeadbeef); + res = InternetCanonicalizeUrlA(url, buffer, &dwSize, 0); + /* dwSize is nr. of copied bytes without the terminating '\0' */ + ok( res && (dwSize == urllen) && (lstrcmpA(url, buffer) == 0), + "got %u and %u with size %u for '%s' (%d)\n", + res, GetLastError(), dwSize, buffer, lstrlenA(buffer)); + + + /* check NULL pointers */ + memset(buffer, '#', urllen + 4); + buffer[urllen + 4] = '\0'; + dwSize = urllen+1; + SetLastError(0xdeadbeef); + res = InternetCanonicalizeUrlA(NULL, buffer, &dwSize, 0); + ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER), + "got %u and %u with size %u for '%s' (%d)\n", + res, GetLastError(), dwSize, buffer, lstrlenA(buffer)); + + memset(buffer, '#', urllen + 4); + buffer[urllen + 4] = '\0'; + dwSize = urllen+1; + SetLastError(0xdeadbeef); + res = InternetCanonicalizeUrlA(url, NULL, &dwSize, 0); + ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER), + "got %u and %u with size %u for '%s' (%d)\n", + res, GetLastError(), dwSize, buffer, lstrlenA(buffer)); + + memset(buffer, '#', urllen + 4); + buffer[urllen + 4] = '\0'; + dwSize = urllen+1; + SetLastError(0xdeadbeef); + res = InternetCanonicalizeUrlA(url, buffer, NULL, 0); + ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER), + "got %u and %u with size %u for '%s' (%d)\n", + res, GetLastError(), dwSize, buffer, lstrlenA(buffer)); + +} + +/* ############################### */ + +static void test_InternetQueryOptionA(void) +{ + HINTERNET hinet,hurl; + DWORD len; + DWORD err; + static const char useragent[] = {"Wininet Test"}; + char *buffer; + int retval; + + hinet = InternetOpenA(useragent,INTERNET_OPEN_TYPE_DIRECT,NULL,NULL, 0); + ok((hinet != 0x0),"InternetOpen Failed\n"); + + SetLastError(0xdeadbeef); + len=strlen(useragent)+1; + retval=InternetQueryOptionA(hinet,INTERNET_OPTION_USER_AGENT,NULL,&len); + err=GetLastError(); + ok(len == strlen(useragent)+1,"Got wrong user agent length %d instead of %d\n",len,lstrlenA(useragent)); + ok(retval == 0,"Got wrong return value %d\n",retval); + todo_wine ok(err == ERROR_INSUFFICIENT_BUFFER, "Got wrong error code%d\n",err); + + SetLastError(0xdeadbeef); + len=strlen(useragent)+1; + buffer=HeapAlloc(GetProcessHeap(),0,len); + retval=InternetQueryOptionA(hinet,INTERNET_OPTION_USER_AGENT,buffer,&len); + err=GetLastError(); + todo_wine ok(retval == 1,"Got wrong return value %d\n",retval); + if (retval) + { + todo_wine ok(!strcmp(useragent,buffer),"Got wrong user agent string %s instead of %s\n",buffer,useragent); + todo_wine ok(len == strlen(useragent),"Got wrong user agent length %d instead of %d\n",len,lstrlenA(useragent)); + } + ok(err == 0xdeadbeef, "Got wrong error code %d\n",err); + HeapFree(GetProcessHeap(),0,buffer); + + SetLastError(0xdeadbeef); + len=0; + buffer=HeapAlloc(GetProcessHeap(),0,100); + retval=InternetQueryOptionA(hinet,INTERNET_OPTION_USER_AGENT,buffer,&len); + err=GetLastError(); + todo_wine ok(len == strlen(useragent) + 1,"Got wrong user agent length %d instead of %d\n", len, lstrlenA(useragent) + 1); + ok(!retval, "Got wrong return value %d\n", retval); + todo_wine ok(err == ERROR_INSUFFICIENT_BUFFER, "Got wrong error code %d\n", err); + HeapFree(GetProcessHeap(),0,buffer); + + hurl = InternetConnectA(hinet,"www.winehq.com",INTERNET_DEFAULT_HTTP_PORT,NULL,NULL,INTERNET_SERVICE_HTTP,0,0); + + SetLastError(0xdeadbeef); + len=0; + retval = InternetQueryOptionA(hurl,INTERNET_OPTION_USER_AGENT,NULL,&len); + err=GetLastError(); + ok(len == 0,"Got wrong user agent length %d instead of 0\n",len); + ok(retval == 0,"Got wrong return value %d\n",retval); + todo_wine ok(err == ERROR_INTERNET_INCORRECT_HANDLE_TYPE, "Got wrong error code %d\n",err); + + InternetCloseHandle(hurl); + InternetCloseHandle(hinet); + + hinet = InternetOpenA("",INTERNET_OPEN_TYPE_DIRECT,NULL,NULL, 0); + ok((hinet != 0x0),"InternetOpen Failed\n"); + + SetLastError(0xdeadbeef); + len=0; + retval=InternetQueryOptionA(hinet,INTERNET_OPTION_USER_AGENT,NULL,&len); + err=GetLastError(); + todo_wine ok(len == 1,"Got wrong user agent length %d instead of %d\n",len,1); + ok(retval == 0,"Got wrong return value %d\n",retval); + todo_wine ok(err == ERROR_INSUFFICIENT_BUFFER, "Got wrong error code%d\n",err); + + InternetCloseHandle(hinet); + + hinet = InternetOpenA(NULL,INTERNET_OPEN_TYPE_DIRECT,NULL,NULL, 0); + ok((hinet != 0x0),"InternetOpen Failed\n"); + SetLastError(0xdeadbeef); + len=0; + retval=InternetQueryOptionA(hinet,INTERNET_OPTION_USER_AGENT,NULL,&len); + err=GetLastError(); + todo_wine ok(len == 1,"Got wrong user agent length %d instead of %d\n",len,1); + ok(retval == 0,"Got wrong return value %d\n",retval); + todo_wine ok(err == ERROR_INSUFFICIENT_BUFFER, "Got wrong error code%d\n",err); + + InternetCloseHandle(hinet); +} + +static void test_get_cookie(void) +{ + DWORD len; + BOOL ret; + + SetLastError(0xdeadbeef); + ret = InternetGetCookie("http://www.example.com", NULL, NULL, &len); + ok(!ret && GetLastError() == ERROR_NO_MORE_ITEMS, + "InternetGetCookie should have failed with %s and error %d\n", + ret ? "TRUE" : "FALSE", GetLastError()); +} + +static void test_null(void) +{ + HINTERNET hi, hc; + static const WCHAR szServer[] = { 's','e','r','v','e','r',0 }; + static const WCHAR szEmpty[] = { 0 }; + static const WCHAR szUrl[] = { 'h','t','t','p',':','/','/','a','.','b','.','c',0 }; + static const WCHAR szUrlEmpty[] = { 'h','t','t','p',':','/','/',0 }; + static const WCHAR szExpect[] = { 's','e','r','v','e','r',';',' ','s','e','r','v','e','r',0 }; + WCHAR buffer[0x20]; + BOOL r; + DWORD sz; + + hi = InternetOpenW(NULL, 0, NULL, NULL, 0); + ok(hi != NULL, "open failed\n"); + + hc = InternetConnectW(hi, NULL, 0, NULL, NULL, 0, 0, 0); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok(hc == NULL, "connect failed\n"); + + hc = InternetConnectW(hi, NULL, 0, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok(hc == NULL, "connect failed\n"); + + hc = InternetConnectW(hi, NULL, 0, NULL, NULL, INTERNET_SERVICE_FTP, 0, 0); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok(hc == NULL, "connect failed\n"); + + hc = InternetConnectW(NULL, szServer, 0, NULL, NULL, INTERNET_SERVICE_FTP, 0, 0); + ok(GetLastError() == ERROR_INVALID_HANDLE, "wrong error\n"); + ok(hc == NULL, "connect failed\n"); + + hc = InternetOpenUrlW(hi, NULL, NULL, 0, 0, 0); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok(hc == NULL, "connect failed\n"); + + hc = InternetOpenUrlW(hi, szServer, NULL, 0, 0, 0); + ok(GetLastError() == ERROR_INTERNET_UNRECOGNIZED_SCHEME, "wrong error\n"); + ok(hc == NULL, "connect failed\n"); + + InternetCloseHandle(hi); + + r = InternetSetCookieW(NULL, NULL, NULL); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok(r == FALSE, "return wrong\n"); + + r = InternetSetCookieW(szServer, NULL, NULL); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok(r == FALSE, "return wrong\n"); + + r = InternetSetCookieW(szUrl, szServer, NULL); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok(r == FALSE, "return wrong\n"); + + r = InternetSetCookieW(szUrl, szServer, szServer); + ok(r == TRUE, "return wrong\n"); + + r = InternetSetCookieW(szUrl, NULL, szServer); + ok(r == TRUE, "return wrong\n"); + + r = InternetSetCookieW(szUrl, szServer, szEmpty); + ok(r == TRUE, "return wrong\n"); + + r = InternetSetCookieW(szUrlEmpty, szServer, szServer); + ok(r == FALSE, "return wrong\n"); + + r = InternetSetCookieW(szServer, NULL, szServer); + todo_wine { + ok(GetLastError() == ERROR_INTERNET_UNRECOGNIZED_SCHEME, "wrong error\n"); + } + ok(r == FALSE, "return wrong\n"); + + sz = 0; + r = InternetGetCookieW(NULL, NULL, NULL, &sz); + ok(GetLastError() == ERROR_INVALID_PARAMETER, "wrong error\n"); + ok( r == FALSE, "return wrong\n"); + + r = InternetGetCookieW(szServer, NULL, NULL, &sz); + todo_wine { + ok(GetLastError() == ERROR_INTERNET_UNRECOGNIZED_SCHEME, "wrong error\n"); + } + ok( r == FALSE, "return wrong\n"); + + sz = 0; + r = InternetGetCookieW(szUrlEmpty, szServer, NULL, &sz); + ok( r == FALSE, "return wrong\n"); + + sz = 0; + r = InternetGetCookieW(szUrl, szServer, NULL, &sz); + ok( r == TRUE, "return wrong\n"); + + /* sz is 14 on XP SP2 and beyond, 30 on XP SP1 and before */ + ok( sz == 14 || sz == 30, "sz wrong, got %u, expected 14 or 30\n", sz); + + sz = 0x20; + memset(buffer, 0, sizeof buffer); + r = InternetGetCookieW(szUrl, szServer, buffer, &sz); + ok( r == TRUE, "return wrong\n"); + + /* sz == lstrlenW(buffer) only in XP SP1 */ + ok( sz == 1 + lstrlenW(buffer), "sz wrong\n"); + + /* before XP SP2, buffer is "server; server" */ + ok( !lstrcmpW(szExpect, buffer) || !lstrcmpW(szServer, buffer), "cookie data wrong\n"); + + sz = sizeof(buffer); + r = InternetQueryOptionA(NULL, INTERNET_OPTION_CONNECTED_STATE, buffer, &sz); + ok(r == TRUE, "ret %d\n", r); +} + +/* ############################### */ + +START_TEST(internet) +{ + test_InternetCanonicalizeUrlA(); + test_InternetQueryOptionA(); + test_get_cookie(); + test_null(); +} diff --git a/rostests/winetests/wininet/testlist.c b/rostests/winetests/wininet/testlist.c new file mode 100644 index 00000000000..df55646f40a --- /dev/null +++ b/rostests/winetests/wininet/testlist.c @@ -0,0 +1,23 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define WIN32_LEAN_AND_MEAN +#include + +#define STANDALONE +#include "wine/test.h" + +extern void func_ftp(void); +extern void func_generated(void); +extern void func_http(void); +extern void func_internet(void); +extern void func_url(void); + +const struct test winetest_testlist[] = +{ + { "ftp", func_ftp }, + { "generated", func_generated }, + { "http", func_http }, + { "internet", func_internet }, + { "url", func_url }, + { 0, 0 } +}; diff --git a/rostests/winetests/wininet/url.c b/rostests/winetests/wininet/url.c new file mode 100644 index 00000000000..729be538586 --- /dev/null +++ b/rostests/winetests/wininet/url.c @@ -0,0 +1,735 @@ +/* + * Wininet - URL tests + * + * Copyright 2002 Aric Stewart + * Copyright 2004 Mike McCormack + * Copyright 2005 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wininet.h" + +#include "wine/test.h" + +#define TEST_URL "http://www.winehq.org/site/about" +#define TEST_URL_HOST "www.winehq.org" +#define TEST_URL_PATH "/site/about" +#define TEST_URL2 "http://www.myserver.com/myscript.php?arg=1" +#define TEST_URL2_SERVER "www.myserver.com" +#define TEST_URL2_PATH "/myscript.php" +#define TEST_URL2_PATHEXTRA "/myscript.php?arg=1" +#define TEST_URL2_EXTRA "?arg=1" +#define TEST_URL3 "file:///C:/Program%20Files/Atmel/AVR%20Tools/STK500/STK500.xml" + +#define CREATE_URL1 "http://username:password@www.winehq.org/site/about" +#define CREATE_URL2 "http://username@www.winehq.org/site/about" +#define CREATE_URL3 "http://username:" +#define CREATE_URL4 "http://www.winehq.org/site/about" +#define CREATE_URL5 "http://" +#define CREATE_URL6 "nhttp://username:password@www.winehq.org:80/site/about" +#define CREATE_URL7 "http://username:password@www.winehq.org:42/site/about" +#define CREATE_URL8 "https://username:password@www.winehq.org/site/about" +#define CREATE_URL9 "about:blank" +#define CREATE_URL10 "about://host/blank" +#define CREATE_URL11 "about:" +#define CREATE_URL12 "http://www.winehq.org:65535" + +static inline void copy_compsA( + URL_COMPONENTSA *src, + URL_COMPONENTSA *dst, + DWORD scheLen, + DWORD hostLen, + DWORD userLen, + DWORD passLen, + DWORD pathLen, + DWORD extrLen ) +{ + *dst = *src; + dst->dwSchemeLength = scheLen; + dst->dwHostNameLength = hostLen; + dst->dwUserNameLength = userLen; + dst->dwPasswordLength = passLen; + dst->dwUrlPathLength = pathLen; + dst->dwExtraInfoLength = extrLen; + SetLastError(0xfaceabad); +} + +static inline void zero_compsA( + URL_COMPONENTSA *dst, + DWORD scheLen, + DWORD hostLen, + DWORD userLen, + DWORD passLen, + DWORD pathLen, + DWORD extrLen ) +{ + ZeroMemory(dst, sizeof(URL_COMPONENTSA)); + dst->dwStructSize = sizeof(URL_COMPONENTSA); + dst->dwSchemeLength = scheLen; + dst->dwHostNameLength = hostLen; + dst->dwUserNameLength = userLen; + dst->dwPasswordLength = passLen; + dst->dwUrlPathLength = pathLen; + dst->dwExtraInfoLength = extrLen; + SetLastError(0xfaceabad); +} + +static void InternetCrackUrl_test(void) +{ + URL_COMPONENTSA urlSrc, urlComponents; + char protocol[32], hostName[1024], userName[1024]; + char password[1024], extra[1024], path[1024]; + BOOL ret, firstret; + DWORD GLE, firstGLE; + + ZeroMemory(&urlSrc, sizeof(urlSrc)); + urlSrc.dwStructSize = sizeof(urlSrc); + urlSrc.lpszScheme = protocol; + urlSrc.lpszHostName = hostName; + urlSrc.lpszUserName = userName; + urlSrc.lpszPassword = password; + urlSrc.lpszUrlPath = path; + urlSrc.lpszExtraInfo = extra; + + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 2048, 1024); + ret = InternetCrackUrl(TEST_URL, 0,0,&urlComponents); + ok( ret, "InternetCrackUrl failed, error %d\n",GetLastError()); + ok((strcmp(TEST_URL_PATH,path) == 0),"path cracked wrong\n"); + + /* Bug 1805: Confirm the returned lengths are correct: */ + /* 1. When extra info split out explicitly */ + zero_compsA(&urlComponents, 0, 1, 0, 0, 1, 1); + ok(InternetCrackUrlA(TEST_URL2, 0, 0, &urlComponents),"InternetCrackUrl failed, error %d\n", GetLastError()); + ok(urlComponents.dwUrlPathLength == strlen(TEST_URL2_PATH),".dwUrlPathLength should be %d, but is %d\n", (DWORD)strlen(TEST_URL2_PATH), urlComponents.dwUrlPathLength); + ok(!strncmp(urlComponents.lpszUrlPath,TEST_URL2_PATH,strlen(TEST_URL2_PATH)),"lpszUrlPath should be %s but is %s\n", TEST_URL2_PATH, urlComponents.lpszUrlPath); + ok(urlComponents.dwHostNameLength == strlen(TEST_URL2_SERVER),".dwHostNameLength should be %d, but is %d\n", (DWORD)strlen(TEST_URL2_SERVER), urlComponents.dwHostNameLength); + ok(!strncmp(urlComponents.lpszHostName,TEST_URL2_SERVER,strlen(TEST_URL2_SERVER)),"lpszHostName should be %s but is %s\n", TEST_URL2_SERVER, urlComponents.lpszHostName); + ok(urlComponents.dwExtraInfoLength == strlen(TEST_URL2_EXTRA),".dwExtraInfoLength should be %d, but is %d\n", (DWORD)strlen(TEST_URL2_EXTRA), urlComponents.dwExtraInfoLength); + ok(!strncmp(urlComponents.lpszExtraInfo,TEST_URL2_EXTRA,strlen(TEST_URL2_EXTRA)),"lpszExtraInfo should be %s but is %s\n", TEST_URL2_EXTRA, urlComponents.lpszHostName); + + /* 2. When extra info is not split out explicitly and is in url path */ + zero_compsA(&urlComponents, 0, 1, 0, 0, 1, 0); + ok(InternetCrackUrlA(TEST_URL2, 0, 0, &urlComponents),"InternetCrackUrl failed with GLE %d\n",GetLastError()); + ok(urlComponents.dwUrlPathLength == strlen(TEST_URL2_PATHEXTRA),".dwUrlPathLength should be %d, but is %d\n", (DWORD)strlen(TEST_URL2_PATHEXTRA), urlComponents.dwUrlPathLength); + ok(!strncmp(urlComponents.lpszUrlPath,TEST_URL2_PATHEXTRA,strlen(TEST_URL2_PATHEXTRA)),"lpszUrlPath should be %s but is %s\n", TEST_URL2_PATHEXTRA, urlComponents.lpszUrlPath); + ok(urlComponents.dwHostNameLength == strlen(TEST_URL2_SERVER),".dwHostNameLength should be %d, but is %d\n", (DWORD)strlen(TEST_URL2_SERVER), urlComponents.dwHostNameLength); + ok(!strncmp(urlComponents.lpszHostName,TEST_URL2_SERVER,strlen(TEST_URL2_SERVER)),"lpszHostName should be %s but is %s\n", TEST_URL2_SERVER, urlComponents.lpszHostName); + ok(urlComponents.nPort == INTERNET_DEFAULT_HTTP_PORT,"urlComponents->nPort should have been 80 instead of %d\n", urlComponents.nPort); + ok(urlComponents.nScheme == INTERNET_SCHEME_HTTP,"urlComponents->nScheme should have been INTERNET_SCHEME_HTTP instead of %d\n", urlComponents.nScheme); + + zero_compsA(&urlComponents, 1, 1, 1, 1, 1, 1); + ok(InternetCrackUrlA(TEST_URL, strlen(TEST_URL), 0, &urlComponents),"InternetCrackUrl failed with GLE %d\n",GetLastError()); + ok(urlComponents.dwUrlPathLength == strlen(TEST_URL_PATH),".dwUrlPathLength should be %d, but is %d\n", lstrlenA(TEST_URL_PATH), urlComponents.dwUrlPathLength); + ok(!strncmp(urlComponents.lpszUrlPath,TEST_URL_PATH,strlen(TEST_URL_PATH)),"lpszUrlPath should be %s but is %s\n", TEST_URL_PATH, urlComponents.lpszUrlPath); + ok(urlComponents.dwHostNameLength == strlen(TEST_URL_HOST),".dwHostNameLength should be %d, but is %d\n", lstrlenA(TEST_URL_HOST), urlComponents.dwHostNameLength); + ok(!strncmp(urlComponents.lpszHostName,TEST_URL_HOST,strlen(TEST_URL_HOST)),"lpszHostName should be %s but is %s\n", TEST_URL_HOST, urlComponents.lpszHostName); + ok(urlComponents.nPort == INTERNET_DEFAULT_HTTP_PORT,"urlComponents->nPort should have been 80 instead of %d\n", urlComponents.nPort); + ok(urlComponents.nScheme == INTERNET_SCHEME_HTTP,"urlComponents->nScheme should have been INTERNET_SCHEME_HTTP instead of %d\n", urlComponents.nScheme); + ok(!urlComponents.lpszUserName, ".lpszUserName should have been set to NULL\n"); + ok(!urlComponents.lpszPassword, ".lpszPassword should have been set to NULL\n"); + ok(!urlComponents.lpszExtraInfo, ".lpszExtraInfo should have been set to NULL\n"); + ok(!urlComponents.dwUserNameLength,".dwUserNameLength should be 0, but is %d\n", urlComponents.dwUserNameLength); + ok(!urlComponents.dwPasswordLength,".dwPasswordLength should be 0, but is %d\n", urlComponents.dwPasswordLength); + ok(!urlComponents.dwExtraInfoLength,".dwExtraInfoLength should be 0, but is %d\n", urlComponents.dwExtraInfoLength); + + /*3. Check for %20 */ + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 2048, 1024); + ok(InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents),"InternetCrackUrl failed with GLE %d\n",GetLastError()); + + /* Tests for lpsz* members pointing to real strings while + * some corresponding length members are set to zero. + * As of IE7 (wininet 7.0*?) all members are checked. So we + * run the first test and expect the outcome to be the same + * for the first four (scheme, hostname, username and password). + * The last two (path and extrainfo) are the same for all versions + * of the wininet.dll. + */ + copy_compsA(&urlSrc, &urlComponents, 0, 1024, 1024, 1024, 2048, 1024); + SetLastError(0xdeadbeef); + firstret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); + firstGLE = GetLastError(); + + copy_compsA(&urlSrc, &urlComponents, 32, 0, 1024, 1024, 2048, 1024); + SetLastError(0xdeadbeef); + ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); + GLE = GetLastError(); + ok(ret==firstret && (GLE==firstGLE), "InternetCrackUrl returned %d with GLE=%d (expected to return %d)\n", + ret, GetLastError(), firstret); + + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 0, 1024, 2048, 1024); + SetLastError(0xdeadbeef); + ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); + GLE = GetLastError(); + ok(ret==firstret && (GLE==firstGLE), "InternetCrackUrl returned %d with GLE=%d (expected to return %d)\n", + ret, GetLastError(), firstret); + + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 0, 2048, 1024); + SetLastError(0xdeadbeef); + ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); + GLE = GetLastError(); + ok(ret==firstret && (GLE==firstGLE), "InternetCrackUrl returned %d with GLE=%d (expected to return %d)\n", + ret, GetLastError(), firstret); + + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 0, 1024); + SetLastError(0xdeadbeef); + ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); + GLE = GetLastError(); + todo_wine + ok(ret==0 && (GLE==ERROR_INVALID_HANDLE || GLE==ERROR_INSUFFICIENT_BUFFER), + "InternetCrackUrl returned %d with GLE=%d (expected to return 0 and ERROR_INVALID_HANDLE or ERROR_INSUFFICIENT_BUFFER)\n", + ret, GLE); + + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 2048, 0); + SetLastError(0xdeadbeef); + ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); + GLE = GetLastError(); + todo_wine + ok(ret==0 && (GLE==ERROR_INVALID_HANDLE || GLE==ERROR_INSUFFICIENT_BUFFER), + "InternetCrackUrl returned %d with GLE=%d (expected to return 0 and ERROR_INVALID_HANDLE or ERROR_INSUFFICIENT_BUFFER)\n", + ret, GLE); + + copy_compsA(&urlSrc, &urlComponents, 0, 0, 0, 0, 0, 0); + ret = InternetCrackUrlA(TEST_URL3, 0, ICU_DECODE, &urlComponents); + GLE = GetLastError(); + todo_wine + ok(ret==0 && GLE==ERROR_INVALID_PARAMETER, + "InternetCrackUrl returned %d with GLE=%d (expected to return 0 and ERROR_INVALID_PARAMETER)\n", + ret, GLE); + + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 2048, 1024); + ret = InternetCrackUrl("about://host/blank", 0,0,&urlComponents); + ok(ret, "InternetCrackUrl failed with %d\n", GetLastError()); + ok(!strcmp(urlComponents.lpszScheme, "about"), "lpszScheme was \"%s\" instead of \"about\"\n", urlComponents.lpszScheme); + ok(!strcmp(urlComponents.lpszHostName, "host"), "lpszHostName was \"%s\" instead of \"host\"\n", urlComponents.lpszHostName); + ok(!strcmp(urlComponents.lpszUrlPath, "/blank"), "lpszUrlPath was \"%s\" instead of \"/blank\"\n", urlComponents.lpszUrlPath); + + /* try a NULL lpszUrl */ + SetLastError(0xdeadbeef); + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 2048, 1024); + ret = InternetCrackUrl(NULL, 0, 0, &urlComponents); + GLE = GetLastError(); + ok(ret == FALSE, "Expected InternetCrackUrl to fail\n"); + ok(GLE == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", GLE); + + /* try an empty lpszUrl, GetLastError returns 12006, whatever that means + * we just need to fail and not return success + */ + SetLastError(0xdeadbeef); + copy_compsA(&urlSrc, &urlComponents, 32, 1024, 1024, 1024, 2048, 1024); + ret = InternetCrackUrl("", 0, 0, &urlComponents); + GLE = GetLastError(); + ok(ret == FALSE, "Expected InternetCrackUrl to fail\n"); + ok(GLE != 0xdeadbeef && GLE != ERROR_SUCCESS, "Expected GLE to represent a failure\n"); +} + +static void InternetCrackUrlW_test(void) +{ + WCHAR url[] = { + 'h','t','t','p',':','/','/','1','9','2','.','1','6','8','.','0','.','2','2','/', + 'C','F','I','D','E','/','m','a','i','n','.','c','f','m','?','C','F','S','V','R', + '=','I','D','E','&','A','C','T','I','O','N','=','I','D','E','_','D','E','F','A', + 'U','L','T', 0 }; + static const WCHAR url2[] = { '.','.','/','R','i','t','z','.','x','m','l',0 }; + URL_COMPONENTSW comp; + WCHAR scheme[20], host[20], user[20], pwd[20], urlpart[50], extra[50]; + DWORD error; + BOOL r; + + urlpart[0]=0; + scheme[0]=0; + extra[0]=0; + host[0]=0; + user[0]=0; + pwd[0]=0; + memset(&comp, 0, sizeof comp); + comp.dwStructSize = sizeof comp; + comp.lpszScheme = scheme; + comp.dwSchemeLength = sizeof scheme; + comp.lpszHostName = host; + comp.dwHostNameLength = sizeof host; + comp.lpszUserName = user; + comp.dwUserNameLength = sizeof user; + comp.lpszPassword = pwd; + comp.dwPasswordLength = sizeof pwd; + comp.lpszUrlPath = urlpart; + comp.dwUrlPathLength = sizeof urlpart; + comp.lpszExtraInfo = extra; + comp.dwExtraInfoLength = sizeof extra; + + SetLastError(0xdeadbeef); + r = InternetCrackUrlW(NULL, 0, 0, &comp ); + error = GetLastError(); + ok( !r, "InternetCrackUrlW succeeded unexpectedly\n"); + ok( error == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER got %u\n", error); + + SetLastError(0xdeadbeef); + r = InternetCrackUrlW(url, 0, 0, NULL ); + error = GetLastError(); + ok( !r, "InternetCrackUrlW succeeded unexpectedly\n"); + ok( error == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER got %u\n", error); + + r = InternetCrackUrlW(url, 0, 0, &comp ); + ok( r, "failed to crack url\n"); + ok( comp.dwSchemeLength == 4, "scheme length wrong\n"); + ok( comp.dwHostNameLength == 12, "host length wrong\n"); + ok( comp.dwUserNameLength == 0, "user length wrong\n"); + ok( comp.dwPasswordLength == 0, "password length wrong\n"); + ok( comp.dwUrlPathLength == 15, "url length wrong\n"); + ok( comp.dwExtraInfoLength == 29, "extra length wrong\n"); + + urlpart[0]=0; + scheme[0]=0; + extra[0]=0; + host[0]=0; + user[0]=0; + pwd[0]=0; + memset(&comp, 0, sizeof comp); + comp.dwStructSize = sizeof comp; + comp.lpszHostName = host; + comp.dwHostNameLength = sizeof host; + comp.lpszUrlPath = urlpart; + comp.dwUrlPathLength = sizeof urlpart; + + r = InternetCrackUrlW(url, 0, 0, &comp ); + ok( r, "failed to crack url\n"); + ok( comp.dwSchemeLength == 0, "scheme length wrong\n"); + ok( comp.dwHostNameLength == 12, "host length wrong\n"); + ok( comp.dwUserNameLength == 0, "user length wrong\n"); + ok( comp.dwPasswordLength == 0, "password length wrong\n"); + ok( comp.dwUrlPathLength == 44, "url length wrong\n"); + ok( comp.dwExtraInfoLength == 0, "extra length wrong\n"); + + urlpart[0]=0; + scheme[0]=0; + extra[0]=0; + host[0]=0; + user[0]=0; + pwd[0]=0; + memset(&comp, 0, sizeof comp); + comp.dwStructSize = sizeof comp; + comp.lpszHostName = host; + comp.dwHostNameLength = sizeof host; + comp.lpszUrlPath = urlpart; + comp.dwUrlPathLength = sizeof urlpart; + comp.lpszExtraInfo = NULL; + comp.dwExtraInfoLength = sizeof extra; + + r = InternetCrackUrlW(url, 0, 0, &comp ); + ok( r, "failed to crack url\n"); + ok( comp.dwSchemeLength == 0, "scheme length wrong\n"); + ok( comp.dwHostNameLength == 12, "host length wrong\n"); + ok( comp.dwUserNameLength == 0, "user length wrong\n"); + ok( comp.dwPasswordLength == 0, "password length wrong\n"); + ok( comp.dwUrlPathLength == 15, "url length wrong\n"); + ok( comp.dwExtraInfoLength == 29, "extra length wrong\n"); + + urlpart[0]=0; + scheme[0]=0; + extra[0]=0; + host[0]=0; + user[0]=0; + pwd[0]=0; + memset(&comp, 0, sizeof(comp)); + comp.dwStructSize = sizeof(comp); + comp.lpszScheme = scheme; + comp.dwSchemeLength = sizeof(scheme)/sizeof(scheme[0]); + comp.lpszHostName = host; + comp.dwHostNameLength = sizeof(host)/sizeof(host[0]); + comp.lpszUserName = user; + comp.dwUserNameLength = sizeof(user)/sizeof(user[0]); + comp.lpszPassword = pwd; + comp.dwPasswordLength = sizeof(pwd)/sizeof(pwd[0]); + comp.lpszUrlPath = urlpart; + comp.dwUrlPathLength = sizeof(urlpart)/sizeof(urlpart[0]); + comp.lpszExtraInfo = extra; + comp.dwExtraInfoLength = sizeof(extra)/sizeof(extra[0]); + + r = InternetCrackUrlW(url2, 0, 0, &comp); + todo_wine { + ok(!r, "InternetCrackUrl should have failed\n"); + ok(GetLastError() == ERROR_INTERNET_UNRECOGNIZED_SCHEME, + "InternetCrackUrl should have failed with error ERROR_INTERNET_UNRECOGNIZED_SCHEME instead of error %d\n", + GetLastError()); + } +} + +static void fill_url_components(LPURL_COMPONENTS lpUrlComponents) +{ + static CHAR http[] = "http", + winehq[] = "www.winehq.org", + username[] = "username", + password[] = "password", + site_about[] = "/site/about", + empty[] = ""; + + lpUrlComponents->dwStructSize = sizeof(URL_COMPONENTS); + lpUrlComponents->lpszScheme = http; + lpUrlComponents->dwSchemeLength = strlen(lpUrlComponents->lpszScheme); + lpUrlComponents->nScheme = INTERNET_SCHEME_HTTP; + lpUrlComponents->lpszHostName = winehq; + lpUrlComponents->dwHostNameLength = strlen(lpUrlComponents->lpszHostName); + lpUrlComponents->nPort = 80; + lpUrlComponents->lpszUserName = username; + lpUrlComponents->dwUserNameLength = strlen(lpUrlComponents->lpszUserName); + lpUrlComponents->lpszPassword = password; + lpUrlComponents->dwPasswordLength = strlen(lpUrlComponents->lpszPassword); + lpUrlComponents->lpszUrlPath = site_about; + lpUrlComponents->dwUrlPathLength = strlen(lpUrlComponents->lpszUrlPath); + lpUrlComponents->lpszExtraInfo = empty; + lpUrlComponents->dwExtraInfoLength = strlen(lpUrlComponents->lpszExtraInfo); +} + +static void InternetCreateUrlA_test(void) +{ + URL_COMPONENTS urlComp; + LPSTR szUrl; + DWORD len = -1; + BOOL ret; + static CHAR empty[] = "", + nhttp[] = "nhttp", + http[] = "http", + https[] = "https", + winehq[] = "www.winehq.org", + username[] = "username", + password[] = "password", + site_about[] = "/site/about", + about[] = "about", + blank[] = "blank", + host[] = "host"; + + /* test NULL lpUrlComponents */ + SetLastError(0xdeadbeef); + ret = InternetCreateUrlA(NULL, 0, NULL, &len); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + ok(len == -1, "Expected len -1, got %d\n", len); + + /* test zero'ed lpUrlComponents */ + ZeroMemory(&urlComp, sizeof(URL_COMPONENTS)); + SetLastError(0xdeadbeef); + ret = InternetCreateUrlA(&urlComp, 0, NULL, &len); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + ok(len == -1, "Expected len -1, got %d\n", len); + + /* test valid lpUrlComponets, NULL lpdwUrlLength */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + ret = InternetCreateUrlA(&urlComp, 0, NULL, NULL); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + ok(len == -1, "Expected len -1, got %d\n", len); + + /* test valid lpUrlComponets, emptry szUrl + * lpdwUrlLength is size of buffer required on exit, including + * the terminating null when GLE == ERROR_INSUFFICIENT_BUFFER + */ + SetLastError(0xdeadbeef); + ret = InternetCreateUrlA(&urlComp, 0, NULL, &len); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + ok(len == 51, "Expected len 51, got %d\n", len); + + /* test correct size, NULL szUrl */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + ret = InternetCreateUrlA(&urlComp, 0, NULL, &len); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + ok(len == 51, "Expected len 51, got %d\n", len); + + /* test valid lpUrlComponets, alloced szUrl, small size */ + SetLastError(0xdeadbeef); + szUrl = HeapAlloc(GetProcessHeap(), 0, len); + len -= 2; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected ERROR_INSUFFICIENT_BUFFER, got %d\n", GetLastError()); + ok(len == 51, "Expected len 51, got %d\n", len); + + /* alloced szUrl, NULL lpszScheme + * shows that it uses nScheme instead + */ + SetLastError(0xdeadbeef); + urlComp.lpszScheme = NULL; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 50, "Expected len 50, got %d\n", len); + ok(!strcmp(szUrl, CREATE_URL1), "Expected %s, got %s\n", CREATE_URL1, szUrl); + + /* alloced szUrl, invalid nScheme + * any nScheme out of range seems ignored + */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + urlComp.nScheme = -3; + len++; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 50, "Expected len 50, got %d\n", len); + + /* test valid lpUrlComponets, alloced szUrl */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + len = 51; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 50, "Expected len 50, got %d\n", len); + ok(strstr(szUrl, "80") == NULL, "Didn't expect to find 80 in szUrl\n"); + ok(!strcmp(szUrl, CREATE_URL1), "Expected %s, got %s\n", CREATE_URL1, szUrl); + + /* valid username, NULL password */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + urlComp.lpszPassword = NULL; + len = 42; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 41, "Expected len 41, got %d\n", len); + ok(!strcmp(szUrl, CREATE_URL2), "Expected %s, got %s\n", CREATE_URL2, szUrl); + + /* valid username, empty password */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + urlComp.lpszPassword = empty; + len = 51; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 50, "Expected len 50, got %d\n", len); + ok(!strcmp(szUrl, CREATE_URL3), "Expected %s, got %s\n", CREATE_URL3, szUrl); + + /* valid password, NULL username + * if password is provided, username has to exist + */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + urlComp.lpszUserName = NULL; + len = 42; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(!ret, "Expected failure\n"); + ok(GetLastError() == ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError()); + ok(len == 42, "Expected len 42, got %d\n", len); + ok(!strcmp(szUrl, CREATE_URL3), "Expected %s, got %s\n", CREATE_URL3, szUrl); + + /* valid password, empty username + * if password is provided, username has to exist + */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + urlComp.lpszUserName = empty; + len = 51; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 50, "Expected len 50, got %d\n", len); + ok(!strcmp(szUrl, CREATE_URL5), "Expected %s, got %s\n", CREATE_URL5, szUrl); + + /* NULL username, NULL password */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + urlComp.lpszUserName = NULL; + urlComp.lpszPassword = NULL; + len = 42; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 32, "Expected len 32, got %d\n", len); + ok(!strcmp(szUrl, CREATE_URL4), "Expected %s, got %s\n", CREATE_URL4, szUrl); + + /* empty username, empty password */ + fill_url_components(&urlComp); + SetLastError(0xdeadbeef); + urlComp.lpszUserName = empty; + urlComp.lpszPassword = empty; + len = 51; + ret = InternetCreateUrlA(&urlComp, 0, szUrl, &len); + ok(ret, "Expected success\n"); + ok(GetLastError() == 0xdeadbeef, + "Expected 0xdeadbeef, got %d\n", GetLastError()); + ok(len == 50, "Expected len 50, got %d\n", len); + ok(!strcmp(szUrl, CREATE_URL5), "Expected %s, got %s\n", CREATE_URL5, szUrl); + + /* shows that nScheme is ignored, as the appearance of the port number + * depends on lpszScheme and the string copied depends on lpszScheme. + */ + fill_url_components(&urlComp); + HeapFree(GetProcessHeap(), 0, szUrl); + urlComp.lpszScheme = nhttp; + urlComp.dwSchemeLength = strlen(urlComp.lpszScheme); + len = strlen(CREATE_URL6) + 1; + szUrl = HeapAlloc(GetProcessHeap(), 0, len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == strlen(CREATE_URL6), "Expected len %d, got %d\n", lstrlenA(CREATE_URL6) + 1, len); + ok(!strcmp(szUrl, CREATE_URL6), "Expected %s, got %s\n", CREATE_URL6, szUrl); + + /* if lpszScheme != "http" or nPort != 80, display nPort */ + HeapFree(GetProcessHeap(), 0, szUrl); + urlComp.lpszScheme = http; + urlComp.dwSchemeLength = strlen(urlComp.lpszScheme); + urlComp.nPort = 42; + szUrl = HeapAlloc(GetProcessHeap(), 0, ++len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == 53, "Expected len 53, got %d\n", len); + ok(strstr(szUrl, "42") != NULL, "Expected to find 42 in szUrl\n"); + ok(!strcmp(szUrl, CREATE_URL7), "Expected %s, got %s\n", CREATE_URL7, szUrl); + + HeapFree(GetProcessHeap(), 0, szUrl); + + memset(&urlComp, 0, sizeof(urlComp)); + urlComp.dwStructSize = sizeof(URL_COMPONENTS); + urlComp.lpszScheme = http; + urlComp.dwSchemeLength = 0; + urlComp.nScheme = INTERNET_SCHEME_HTTP; + urlComp.lpszHostName = winehq; + urlComp.dwHostNameLength = 0; + urlComp.nPort = 80; + urlComp.lpszUserName = username; + urlComp.dwUserNameLength = 0; + urlComp.lpszPassword = password; + urlComp.dwPasswordLength = 0; + urlComp.lpszUrlPath = site_about; + urlComp.dwUrlPathLength = 0; + urlComp.lpszExtraInfo = empty; + urlComp.dwExtraInfoLength = 0; + len = strlen(CREATE_URL1); + szUrl = HeapAlloc(GetProcessHeap(), 0, ++len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == strlen(CREATE_URL1), "Expected len %d, got %d\n", lstrlenA(CREATE_URL1), len); + ok(!strcmp(szUrl, CREATE_URL1), "Expected %s, got %s\n", CREATE_URL1, szUrl); + + HeapFree(GetProcessHeap(), 0, szUrl); + + memset(&urlComp, 0, sizeof(urlComp)); + urlComp.dwStructSize = sizeof(URL_COMPONENTS); + urlComp.lpszScheme = https; + urlComp.dwSchemeLength = 0; + urlComp.nScheme = INTERNET_SCHEME_HTTP; + urlComp.lpszHostName = winehq; + urlComp.dwHostNameLength = 0; + urlComp.nPort = 443; + urlComp.lpszUserName = username; + urlComp.dwUserNameLength = 0; + urlComp.lpszPassword = password; + urlComp.dwPasswordLength = 0; + urlComp.lpszUrlPath = site_about; + urlComp.dwUrlPathLength = 0; + urlComp.lpszExtraInfo = empty; + urlComp.dwExtraInfoLength = 0; + len = strlen(CREATE_URL8); + szUrl = HeapAlloc(GetProcessHeap(), 0, ++len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == strlen(CREATE_URL8), "Expected len %d, got %d\n", lstrlenA(CREATE_URL8), len); + ok(!strcmp(szUrl, CREATE_URL8), "Expected %s, got %s\n", CREATE_URL8, szUrl); + + HeapFree(GetProcessHeap(), 0, szUrl); + + memset(&urlComp, 0, sizeof(urlComp)); + urlComp.dwStructSize = sizeof(URL_COMPONENTS); + urlComp.lpszScheme = about; + urlComp.dwSchemeLength = 5; + urlComp.lpszUrlPath = blank; + urlComp.dwUrlPathLength = 5; + len = strlen(CREATE_URL9); + len++; /* work around bug in native wininet */ + szUrl = HeapAlloc(GetProcessHeap(), 0, ++len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == strlen(CREATE_URL9), "Expected len %d, got %d\n", lstrlenA(CREATE_URL9), len); + ok(!strcmp(szUrl, CREATE_URL9), "Expected %s, got %s\n", CREATE_URL9, szUrl); + + HeapFree(GetProcessHeap(), 0, szUrl); + + memset(&urlComp, 0, sizeof(urlComp)); + urlComp.dwStructSize = sizeof(URL_COMPONENTS); + urlComp.lpszScheme = about; + urlComp.lpszHostName = host; + urlComp.lpszUrlPath = blank; + len = strlen(CREATE_URL10); + len++; /* work around bug in native wininet */ + szUrl = HeapAlloc(GetProcessHeap(), 0, ++len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == strlen(CREATE_URL10), "Expected len %d, got %d\n", lstrlenA(CREATE_URL10), len); + ok(!strcmp(szUrl, CREATE_URL10), "Expected %s, got %s\n", CREATE_URL10, szUrl); + + HeapFree(GetProcessHeap(), 0, szUrl); + + memset(&urlComp, 0, sizeof(urlComp)); + urlComp.dwStructSize = sizeof(URL_COMPONENTS); + urlComp.nPort = 8080; + urlComp.lpszScheme = about; + len = strlen(CREATE_URL11); + szUrl = HeapAlloc(GetProcessHeap(), 0, ++len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == strlen(CREATE_URL11), "Expected len %d, got %d\n", lstrlenA(CREATE_URL11), len); + ok(!strcmp(szUrl, CREATE_URL11), "Expected %s, got %s\n", CREATE_URL11, szUrl); + + HeapFree(GetProcessHeap(), 0, szUrl); + + memset(&urlComp, 0, sizeof(urlComp)); + urlComp.dwStructSize = sizeof(URL_COMPONENTS); + urlComp.lpszScheme = http; + urlComp.dwSchemeLength = 0; + urlComp.nScheme = INTERNET_SCHEME_HTTP; + urlComp.lpszHostName = winehq; + urlComp.dwHostNameLength = 0; + urlComp.nPort = 65535; + len = strlen(CREATE_URL12); + szUrl = HeapAlloc(GetProcessHeap(), 0, ++len); + ret = InternetCreateUrlA(&urlComp, ICU_ESCAPE, szUrl, &len); + ok(ret, "Expected success\n"); + ok(len == strlen(CREATE_URL12), "Expected len %d, got %d\n", lstrlenA(CREATE_URL12), len); + ok(!strcmp(szUrl, CREATE_URL12), "Expected %s, got %s\n", CREATE_URL12, szUrl); + + HeapFree(GetProcessHeap(), 0, szUrl); +} + +START_TEST(url) +{ + InternetCrackUrl_test(); + InternetCrackUrlW_test(); + InternetCreateUrlA_test(); +} diff --git a/rostests/winetests/wininet/wininet.rbuild b/rostests/winetests/wininet/wininet.rbuild new file mode 100644 index 00000000000..059de03ce26 --- /dev/null +++ b/rostests/winetests/wininet/wininet.rbuild @@ -0,0 +1,18 @@ + + + + . + 0x600 + 0x600 + wine + wininet + ws2_32 + kernel32 + ntdll + ftp.c + generated.c + http.c + internet.c + url.c + testlist.c + diff --git a/rostests/winetests/wininet/wininet_test.h b/rostests/winetests/wininet/wininet_test.h new file mode 100644 index 00000000000..c6ca43daab8 --- /dev/null +++ b/rostests/winetests/wininet/wininet_test.h @@ -0,0 +1,84 @@ +/* + * Unit test suite for wininet functions + * + * Copyright 2004 Francois Gouget + * + * 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 + */ + +/* The Windows headers don't define A/W types for any of the following + * structures and pointers :-( + * Because for these structures there's no alignment or packing difference + * between the A and W versions, we just define a set of macros so the + * generated tests work anyway. + */ +#ifndef GOPHER_ABSTRACT_ATTRIBUTE_TYPE +#define GOPHER_ABSTRACT_ATTRIBUTE_TYPEA GOPHER_ABSTRACT_ATTRIBUTE_TYPE +#define GOPHER_ABSTRACT_ATTRIBUTE_TYPEW GOPHER_ABSTRACT_ATTRIBUTE_TYPE + +#define GOPHER_ADMIN_ATTRIBUTE_TYPEA GOPHER_ADMIN_ATTRIBUTE_TYPE +#define GOPHER_ADMIN_ATTRIBUTE_TYPEW GOPHER_ADMIN_ATTRIBUTE_TYPE +#define GOPHER_ASK_ATTRIBUTE_TYPEA GOPHER_ASK_ATTRIBUTE_TYPE +#define GOPHER_ASK_ATTRIBUTE_TYPEW GOPHER_ASK_ATTRIBUTE_TYPE +#define GOPHER_ATTRIBUTE_ENUMERATORA GOPHER_ATTRIBUTE_ENUMERATOR +#define GOPHER_ATTRIBUTE_ENUMERATORW GOPHER_ATTRIBUTE_ENUMERATOR +#define GOPHER_ATTRIBUTE_TYPEA GOPHER_ATTRIBUTE_TYPE +#define GOPHER_ATTRIBUTE_TYPEW GOPHER_ATTRIBUTE_TYPE +#define GOPHER_LOCATION_ATTRIBUTE_TYPEA GOPHER_LOCATION_ATTRIBUTE_TYPE +#define GOPHER_LOCATION_ATTRIBUTE_TYPEW GOPHER_LOCATION_ATTRIBUTE_TYPE +#define GOPHER_ORGANIZATION_ATTRIBUTE_TYPEA GOPHER_ORGANIZATION_ATTRIBUTE_TYPE +#define GOPHER_ORGANIZATION_ATTRIBUTE_TYPEW GOPHER_ORGANIZATION_ATTRIBUTE_TYPE +#define GOPHER_PROVIDER_ATTRIBUTE_TYPEA GOPHER_PROVIDER_ATTRIBUTE_TYPE +#define GOPHER_PROVIDER_ATTRIBUTE_TYPEW GOPHER_PROVIDER_ATTRIBUTE_TYPE +#define GOPHER_SITE_ATTRIBUTE_TYPEA GOPHER_SITE_ATTRIBUTE_TYPE +#define GOPHER_SITE_ATTRIBUTE_TYPEW GOPHER_SITE_ATTRIBUTE_TYPE +#define GOPHER_UNKNOWN_ATTRIBUTE_TYPEA GOPHER_UNKNOWN_ATTRIBUTE_TYPE +#define GOPHER_UNKNOWN_ATTRIBUTE_TYPEW GOPHER_UNKNOWN_ATTRIBUTE_TYPE +#define GOPHER_VERSION_ATTRIBUTE_TYPEA GOPHER_VERSION_ATTRIBUTE_TYPE +#define GOPHER_VERSION_ATTRIBUTE_TYPEW GOPHER_VERSION_ATTRIBUTE_TYPE +#define GOPHER_VIEW_ATTRIBUTE_TYPEA GOPHER_VIEW_ATTRIBUTE_TYPE +#define GOPHER_VIEW_ATTRIBUTE_TYPEW GOPHER_VIEW_ATTRIBUTE_TYPE +#define INTERNET_CERTIFICATE_INFOA INTERNET_CERTIFICATE_INFO +#define INTERNET_CERTIFICATE_INFOW INTERNET_CERTIFICATE_INFO +#define INTERNET_PROXY_INFOA INTERNET_PROXY_INFO +#define INTERNET_PROXY_INFOW INTERNET_PROXY_INFO + +#define LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEA LPGOPHER_ABSTRACT_ATTRIBUTE_TYPE +#define LPGOPHER_ABSTRACT_ATTRIBUTE_TYPEW LPGOPHER_ABSTRACT_ATTRIBUTE_TYPE +#define LPGOPHER_ADMIN_ATTRIBUTE_TYPEA LPGOPHER_ADMIN_ATTRIBUTE_TYPE +#define LPGOPHER_ADMIN_ATTRIBUTE_TYPEW LPGOPHER_ADMIN_ATTRIBUTE_TYPE +#define LPGOPHER_ASK_ATTRIBUTE_TYPEA LPGOPHER_ASK_ATTRIBUTE_TYPE +#define LPGOPHER_ASK_ATTRIBUTE_TYPEW LPGOPHER_ASK_ATTRIBUTE_TYPE +#define LPGOPHER_ATTRIBUTE_TYPEA LPGOPHER_ATTRIBUTE_TYPE +#define LPGOPHER_ATTRIBUTE_TYPEW LPGOPHER_ATTRIBUTE_TYPE +#define LPGOPHER_LOCATION_ATTRIBUTE_TYPEA LPGOPHER_LOCATION_ATTRIBUTE_TYPE +#define LPGOPHER_LOCATION_ATTRIBUTE_TYPEW LPGOPHER_LOCATION_ATTRIBUTE_TYPE +#define LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEA LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPE +#define LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPEW LPGOPHER_ORGANIZATION_ATTRIBUTE_TYPE +#define LPGOPHER_PROVIDER_ATTRIBUTE_TYPEA LPGOPHER_PROVIDER_ATTRIBUTE_TYPE +#define LPGOPHER_PROVIDER_ATTRIBUTE_TYPEW LPGOPHER_PROVIDER_ATTRIBUTE_TYPE +#define LPGOPHER_SITE_ATTRIBUTE_TYPEA LPGOPHER_SITE_ATTRIBUTE_TYPE +#define LPGOPHER_SITE_ATTRIBUTE_TYPEW LPGOPHER_SITE_ATTRIBUTE_TYPE +#define LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEA LPGOPHER_UNKNOWN_ATTRIBUTE_TYPE +#define LPGOPHER_UNKNOWN_ATTRIBUTE_TYPEW LPGOPHER_UNKNOWN_ATTRIBUTE_TYPE +#define LPGOPHER_VERSION_ATTRIBUTE_TYPEA LPGOPHER_VERSION_ATTRIBUTE_TYPE +#define LPGOPHER_VERSION_ATTRIBUTE_TYPEW LPGOPHER_VERSION_ATTRIBUTE_TYPE +#define LPGOPHER_VIEW_ATTRIBUTE_TYPEA LPGOPHER_VIEW_ATTRIBUTE_TYPE +#define LPGOPHER_VIEW_ATTRIBUTE_TYPEW LPGOPHER_VIEW_ATTRIBUTE_TYPE +#define LPINTERNET_CERTIFICATE_INFOA LPINTERNET_CERTIFICATE_INFO +#define LPINTERNET_CERTIFICATE_INFOW LPINTERNET_CERTIFICATE_INFO +#define LPINTERNET_PROXY_INFOA LPINTERNET_PROXY_INFO +#define LPINTERNET_PROXY_INFOW LPINTERNET_PROXY_INFO +#endif