--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <windows.h>
+#include <advpub.h>
+#include <assert.h>
+#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();
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="advpack_winetest" type="win32cui" installbase="bin" installname="advpack_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="advpack_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>cabinet</library>
+ <library>user32</library>
+ <library>advapi32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>advpack.c</file>
+ <file>files.c</file>
+ <file>install.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <windows.h>
+#include <advpub.h>
+#include <fci.h>
+#include "wine/test.h"
+
+/* make the max size large so there is only one cab file */
+#define MEDIA_SIZE 999999999
+#define FOLDER_THRESHOLD 900000
+
+/* 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();
+}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <windows.h>
+#include <advpub.h>
+#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();
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_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 }
+};
--- /dev/null
+/* 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 <assert.h>
+#include <stdarg.h>
+
+#include <windows.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+
+#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();
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="browseui_winetest" type="win32cui" installbase="bin" installname="browseui_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="browseui_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>ole32</library>
+ <library>user32</library>
+ <library>kernel32</library>
+ <library>uuid</library>
+ <library>ntdll</library>
+ <file>autocomplete.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_autocomplete(void);
+
+const struct test winetest_testlist[] =
+{
+ { "autocomplete", func_autocomplete },
+ { 0, 0 }
+};
-<module name="cabinet_winetest" type="win32cui" installbase="bin" installname="cabinet_winetest.exe" allowwarnings="true">
- <include base="cabinet_winetest">.</include>
- <define name="__USE_W32API" />
- <library>cabinet</library>
- <library>kernel32</library>
- <library>ntdll</library>
- <file>extract.c</file>
- <file>testlist.c</file>
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="cabinet_winetest" type="win32cui" installbase="bin" installname="cabinet_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="cabinet_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>cabinet</library>
+ <library>user32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>extract.c</file>
+ <file>fdi.c</file>
+ <file>testlist.c</file>
</module>
#include <stdio.h>
#include <windows.h>
-#include <fci.h>
+#include "fci.h"
+#include "fdi.h"
#include "wine/test.h"
/* make the max size large so there is only one cab file */
* 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 */
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;
HANDLE handle = (HANDLE)hf;
DWORD dwRead;
BOOL res;
-
+
res = ReadFile(handle, memory, cb, &dwRead, NULL);
ok(res, "Failed to ReadFile\n");
{
HANDLE handle = (HANDLE)hf;
DWORD ret;
-
+
ret = SetFilePointer(handle, dist, NULL, seektype);
ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
res = GetFileInformationByHandle(handle, &finfo);
ok(res, "Expected GetFileInformationByHandle to succeed\n");
-
+
FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
FileTimeToDosDateTime(&filetime, pdate, ptime);
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);
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");
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
*/
/* 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)
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <windows.h>
+#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();
+}
+/* Automatically generated file; DO NOT EDIT!! */
+
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#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 }
};
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <windows.h>
+
+#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();
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="comcat_winetest" type="win32cui" installbase="bin" installname="comcat_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="comcat_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>ole32</library>
+ <library>advapi32</library>
+ <library>kernel32</library>
+ <library>uuid</library>
+ <library>ntdll</library>
+ <file>comcat.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_comcat(void);
+
+const struct test winetest_testlist[] =
+{
+ { "comcat", func_comcat },
+ { 0, 0 }
+};
-<module name="comdlg32_winetest" type="win32cui" installbase="bin" installname="comdlg32_winetest.exe" allowwarnings="true">\r
- <include base="comdlg32_winetest">.</include>\r
- <define name="__USE_W32API" />\r
- <define name="_WIN32_IE">0x600</define>\r
- <define name="_WIN32_WINNT">0x501</define>\r
- <define name="WINVER">0x501</define>\r
- <library>wine</library>\r
- <library>comdlg32</library>\r
- <library>user32</library>\r
- <library>kernel32</library>\r
- <library>ntdll</library>\r
- <file>filedlg.c</file>\r
- <file>printdlg.c</file>\r
- <file>testlist.c</file>\r
-</module>\r
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="comdlg32_winetest" type="win32cui" installbase="bin" installname="comdlg32_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="comdlg32_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>comdlg32</library>
+ <library>user32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>filedlg.c</file>
+ <file>printdlg.c</file>
+ <file>testlist.c</file>
+</module>
-/*\r
- * Unit test suite for comdlg32 API functions: file dialogs\r
- *\r
- * Copyright 2007 Google (Lei Zhang)\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA\r
- *\r
- */\r
-\r
-#include <windows.h>\r
-#include <wine/test.h>\r
-\r
-\r
-/* ##### */\r
-\r
-static UINT CALLBACK OFNHookProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)\r
-{\r
- LPNMHDR nmh;\r
-\r
- if( msg == WM_NOTIFY)\r
- {\r
- nmh = (LPNMHDR) lParam;\r
- if( nmh->code == CDN_INITDONE)\r
- {\r
- PostMessage( GetParent(hDlg), WM_COMMAND, IDCANCEL, FALSE);\r
- }\r
- }\r
-\r
- return 0;\r
-}\r
-\r
-/* bug 6829 */\r
-static void test_DialogCancel(void)\r
-{\r
- OPENFILENAMEA ofn;\r
- BOOL result;\r
- char szFileName[MAX_PATH] = "";\r
-\r
- ZeroMemory(&ofn, sizeof(ofn));\r
-\r
- ofn.lStructSize = sizeof(ofn);\r
- ofn.hwndOwner = NULL;\r
- ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";\r
- ofn.lpstrFile = szFileName;\r
- ofn.nMaxFile = MAX_PATH;\r
- ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLEHOOK;\r
- ofn.lpstrDefExt = "txt";\r
- ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProc;\r
-\r
- PrintDlgA(NULL);\r
- ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n",\r
- CDERR_INITIALIZATION, CommDlgExtendedError());\r
-\r
- result = GetOpenFileNameA(&ofn);\r
- ok(0 == result, "expected %d, got %d\n", 0, result);\r
- ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0,\r
- CommDlgExtendedError());\r
-\r
- PrintDlgA(NULL);\r
- ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n",\r
- CDERR_INITIALIZATION, CommDlgExtendedError());\r
-\r
- SetLastError(0xdeadbeef);\r
- result = GetOpenFileNameW((LPOPENFILENAMEW) &ofn);\r
- if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\r
- skip("GetOpenFileNameW is not implemented\n");\r
- else\r
- {\r
- ok(0 == result, "expected %d, got %d\n", 0, result);\r
- ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0,\r
- CommDlgExtendedError());\r
- }\r
-\r
- PrintDlgA(NULL);\r
- ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n",\r
- CDERR_INITIALIZATION, CommDlgExtendedError());\r
-\r
- result = GetSaveFileNameA(&ofn);\r
- ok(0 == result, "expected %d, got %d\n", 0, result);\r
- ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0,\r
- CommDlgExtendedError());\r
-\r
- PrintDlgA(NULL);\r
- ok(CDERR_INITIALIZATION == CommDlgExtendedError(), "expected %d, got %d\n",\r
- CDERR_INITIALIZATION, CommDlgExtendedError());\r
-\r
- SetLastError(0xdeadbeef);\r
- result = GetSaveFileNameW((LPOPENFILENAMEW) &ofn);\r
- if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)\r
- skip("GetSaveFileNameW is not implemented\n");\r
- else\r
- {\r
- ok(0 == result, "expected %d, got %d\n", 0, result);\r
- ok(0 == CommDlgExtendedError(), "expected %d, got %d\n", 0,\r
- CommDlgExtendedError());\r
- }\r
-}\r
-\r
-\r
-START_TEST(filedlg)\r
-{\r
- test_DialogCancel();\r
-\r
-}\r
+/*
+ * 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 <windows.h>
+#include <wine/test.h>
+
+
+/* ##### */
+
+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();
+
+}
-/*\r
- * Unit test suite for comdlg32 API functions: printer dialogs\r
- *\r
- * Copyright 2006 Detlef Riekenberg\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Lesser General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2.1 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
- * Lesser General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Lesser General Public\r
- * License along with this library; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA\r
- *\r
- */\r
-\r
-#include <stdarg.h>\r
-\r
-#include "windef.h"\r
-#include "winbase.h"\r
-#include "winerror.h"\r
-#include "wingdi.h"\r
-#include "wingdi.h"\r
-#include "winuser.h"\r
-\r
-#include "cderr.h"\r
-#include "commdlg.h"\r
-\r
-#include "wine/test.h"\r
-\r
-\r
-/* ######## */\r
-\r
-static void test_PageSetupDlgA(void)\r
-{\r
- LPPAGESETUPDLGA pDlg;\r
- DWORD res;\r
-\r
- pDlg = HeapAlloc(GetProcessHeap(), 0, (sizeof(PAGESETUPDLGA)) * 2);\r
- if (!pDlg) return;\r
-\r
- SetLastError(0xdeadbeef);\r
- res = PageSetupDlgA(NULL);\r
- ok( !res && (CommDlgExtendedError() == CDERR_INITIALIZATION),\r
- "returned %u with %u and 0x%x (expected '0' and "\r
- "CDERR_INITIALIZATION)\n", res, GetLastError(), CommDlgExtendedError());\r
-\r
- ZeroMemory(pDlg, sizeof(PAGESETUPDLGA));\r
- pDlg->lStructSize = sizeof(PAGESETUPDLGA) -1;\r
- SetLastError(0xdeadbeef);\r
- res = PageSetupDlgA(pDlg);\r
- ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE),\r
- "returned %u with %u and 0x%x (expected '0' and "\r
- "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError());\r
-\r
- ZeroMemory(pDlg, sizeof(PAGESETUPDLGA));\r
- pDlg->lStructSize = sizeof(PAGESETUPDLGA) +1;\r
- pDlg->Flags = PSD_RETURNDEFAULT;\r
- SetLastError(0xdeadbeef);\r
- res = PageSetupDlgA(pDlg);\r
- ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE),\r
- "returned %u with %u and 0x%x (expected '0' and CDERR_STRUCTSIZE)\n",\r
- res, GetLastError(), CommDlgExtendedError());\r
-\r
-\r
- ZeroMemory(pDlg, sizeof(PAGESETUPDLGA));\r
- pDlg->lStructSize = sizeof(PAGESETUPDLGA);\r
- pDlg->Flags = PSD_RETURNDEFAULT;\r
- SetLastError(0xdeadbeef);\r
- res = PageSetupDlgA(pDlg);\r
- trace("after pagesetupdlga res = %d, le %d, ext error 0x%x\n",\r
- res, GetLastError(), CommDlgExtendedError());\r
- ok( res || (CommDlgExtendedError() == PDERR_NODEFAULTPRN),\r
- "returned %u with %u and 0x%x (expected '!= 0' or '0' and "\r
- "PDERR_NODEFAULTPRN)\n", res, GetLastError(), CommDlgExtendedError());\r
- if (!res && (CommDlgExtendedError() == PDERR_NODEFAULTPRN)) {\r
- skip("No printer configured.\n");\r
- HeapFree(GetProcessHeap(), 0, pDlg);\r
- return;\r
- }\r
- ok( pDlg->hDevMode && pDlg->hDevNames,\r
- "got %p and %p (expected '!= NULL' for both)\n",\r
- pDlg->hDevMode, pDlg->hDevNames);\r
-\r
- GlobalFree(pDlg->hDevMode);\r
- GlobalFree(pDlg->hDevNames);\r
-\r
- HeapFree(GetProcessHeap(), 0, pDlg);\r
-\r
-}\r
-\r
-/* ##### */\r
-\r
-static void test_PrintDlgA(void)\r
-{\r
- DWORD res;\r
- LPPRINTDLGA pDlg;\r
-\r
-\r
- pDlg = HeapAlloc(GetProcessHeap(), 0, (sizeof(PRINTDLGA)) * 2);\r
- if (!pDlg) return;\r
-\r
-\r
- /* will crash with unpatched wine */\r
- SetLastError(0xdeadbeef);\r
- res = PrintDlgA(NULL);\r
- ok( !res && (CommDlgExtendedError() == CDERR_INITIALIZATION),\r
- "returned %d with 0x%x and 0x%x (expected '0' and "\r
- "CDERR_INITIALIZATION)\n", res, GetLastError(), CommDlgExtendedError());\r
-\r
- ZeroMemory(pDlg, sizeof(PRINTDLGA));\r
- pDlg->lStructSize = sizeof(PRINTDLGA) - 1;\r
- SetLastError(0xdeadbeef);\r
- res = PrintDlgA(pDlg);\r
- ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE),\r
- "returned %d with 0x%x and 0x%x (expected '0' and "\r
- "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError());\r
-\r
- ZeroMemory(pDlg, sizeof(PRINTDLGA));\r
- pDlg->lStructSize = sizeof(PRINTDLGA) + 1;\r
- pDlg->Flags = PD_RETURNDEFAULT;\r
- SetLastError(0xdeadbeef);\r
- res = PrintDlgA(pDlg);\r
- ok( !res && (CommDlgExtendedError() == CDERR_STRUCTSIZE),\r
- "returned %u with %u and 0x%x (expected '0' and "\r
- "CDERR_STRUCTSIZE)\n", res, GetLastError(), CommDlgExtendedError());\r
-\r
-\r
- ZeroMemory(pDlg, sizeof(PRINTDLGA));\r
- pDlg->lStructSize = sizeof(PRINTDLGA);\r
- pDlg->Flags = PD_RETURNDEFAULT;\r
- SetLastError(0xdeadbeef);\r
- res = PrintDlgA(pDlg);\r
- ok( res || (CommDlgExtendedError() == PDERR_NODEFAULTPRN),\r
- "returned %d with 0x%x and 0x%x (expected '!= 0' or '0' and "\r
- "PDERR_NODEFAULTPRN)\n", res, GetLastError(), CommDlgExtendedError());\r
-\r
- HeapFree(GetProcessHeap(), 0, pDlg);\r
-\r
-}\r
-\r
-\r
-START_TEST(printdlg)\r
-{\r
- test_PageSetupDlgA();\r
- test_PrintDlgA();\r
-\r
-}\r
+/*
+ * 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 <stdarg.h>
+
+#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();
+}
-/* Automatically generated file; DO NOT EDIT!! */\r
-\r
-#define WIN32_LEAN_AND_MEAN\r
-#include <windows.h>\r
-\r
-#define STANDALONE\r
-#include "wine/test.h"\r
-\r
-extern void func_filedlg(void);\r
-extern void func_printdlg(void);\r
-\r
-const struct test winetest_testlist[] =\r
-{\r
- { "filedlg", func_filedlg },\r
- { "printdlg", func_printdlg },\r
- { 0, 0 }\r
-};\r
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#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 }
+};
<directory name="advapi32">
<xi:include href="advapi32/advapi32.rbuild" />
</directory>
+<directory name="advpack">
+ <xi:include href="advpack/advpack.rbuild" />
+</directory>
+<directory name="browseui">
+ <xi:include href="browseui/browseui.rbuild" />
+</directory>
<directory name="cabinet">
<xi:include href="cabinet/cabinet.rbuild" />
</directory>
+<directory name="comcat">
+ <xi:include href="comcat/comcat.rbuild" />
+</directory>
<directory name="comctl32">
<xi:include href="comctl32/comctl32.rbuild" />
</directory>
<directory name="lz32">
<xi:include href="lz32/lz32.rbuild" />
</directory>
+<directory name="mapi32">
+ <xi:include href="mapi32/mapi32.rbuild" />
+</directory>
<directory name="mlang">
<xi:include href="mlang/mlang.rbuild" />
</directory>
<directory name="msvcrt">
<xi:include href="msvcrt/msvcrt.rbuild" />
</directory>
+<directory name="netapi32">
+ <xi:include href="netapi32/netapi32.rbuild" />
+</directory>
<directory name="ntdll">
<xi:include href="ntdll/ntdll.rbuild" />
</directory>
+<directory name="odbccp32">
+ <xi:include href="odbccp32/odbccp32.rbuild" />
+</directory>
<directory name="psapi">
<xi:include href="psapi/psapi.rbuild" />
</directory>
<directory name="powrprof">
<xi:include href="powrprof/powrprof.rbuild" />
</directory>
+<directory name="riched20">
+ <xi:include href="riched20/riched20.rbuild" />
+</directory>
+<directory name="rsabase">
+ <xi:include href="rsabase/rsabase.rbuild" />
+</directory>
+<directory name="rsaenh">
+ <xi:include href="rsaenh/rsaenh.rbuild" />
+</directory>
<directory name="setupapi">
<xi:include href="setupapi/setupapi.rbuild" />
</directory>
<directory name="shlwapi">
<xi:include href="shlwapi/shlwapi.rbuild" />
</directory>
+<directory name="urlmon">
+ <xi:include href="urlmon/urlmon.rbuild" />
+</directory>
<directory name="user32">
<xi:include href="user32/user32.rbuild" />
</directory>
<directory name="usp10">
<xi:include href="usp10/usp10.rbuild" />
</directory>
+<directory name="uxtheme">
+ <xi:include href="uxtheme/uxtheme.rbuild" />
+</directory>
<directory name="version">
<xi:include href="version/version.rbuild" />
</directory>
+<directory name="wininet">
+ <xi:include href="wininet/wininet.rbuild" />
+</directory>
</group>
-<module name="lz32_winetest" type="win32cui" installbase="bin" installname="lz32_winetest.exe" allowwarnings="true">
- <include base="lz32_winetest">.</include>
- <define name="__USE_W32API" />
- <library>kernel32</library>
- <library>ntdll</library>
- <library>lz32</library>
- <file>testlist.c</file>
- <file>lzexpand_main.c</file>
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="lz32_winetest" type="win32cui" installbase="bin" installname="lz32_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="lz32_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>lz32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>lzexpand_main.c</file>
+ <file>testlist.c</file>
</module>
#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,
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");
+ /* 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);
- 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");
+
+ memset(&test, 0xA5, sizeof(test));
+
+ /* 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);
+ 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);
- /* 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");
+ 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);
-#endif
+
+ 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;
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");
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;
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");
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);
}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="mapi32_winetest" type="win32cui" installbase="bin" installname="mapi32_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="mapi32_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>kernel32</library>
+ <library>uuid</library>
+ <library>ntdll</library>
+ <file>imalloc.c</file>
+ <file>prop.c</file>
+ <file>util.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_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 }
+};
--- /dev/null
+/*
+ * 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);
+}
"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);
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);
}
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)
IMLangFontLink_Test(iMLFL);
IMLangFontLink_Release(iMLFL);
-
+
CoUninitialize();
}
-<module name="mlang_winetest" type="win32cui" installbase="bin" installname="mlang_winetest.exe" allowwarnings="true">
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="mlang_winetest" type="win32cui" installbase="bin" installname="mlang_winetest.exe" allowwarnings="true" entrypoint="0">
<include base="mlang_winetest">.</include>
- <define name="__USE_W32API" />
- <define name="_WIN32_IE">0x600</define>
- <define name="_WIN32_WINNT">0x501</define>
- <define name="WINVER">0x501</define>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
<library>wine</library>
<library>ole32</library>
<library>gdi32</library>
--- /dev/null
+/*
+ * 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 <stdarg.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winerror.h>
+#include <lmaccess.h>
+#include <lmerr.h>
+#include <lmapibuf.h>
+
+#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);
+}
--- /dev/null
+/*
+ * 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 <stdarg.h>
+
+#include "wine/test.h"
+#include <windef.h>
+#include <winbase.h>
+#include <winerror.h>
+#include <lmcons.h>
+#include <lmerr.h>
+#include <lmapibuf.h>
+#include <lmaccess.h>
+
+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);
+}
--- /dev/null
+/*
+ * 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 <stdarg.h>
+
+#include <wine/test.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winerror.h>
+#include <dsrole.h>
+
+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);
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="netapi32_winetest" type="win32cui" installbase="bin" installname="netapi32_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="netapi32_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>netapi32</library>
+ <library>advapi32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>access.c</file>
+ <file>apibuf.c</file>
+ <file>ds.c</file>
+ <file>wksta.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_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 }
+};
--- /dev/null
+/*
+ * 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 <stdarg.h>
+
+#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);
+}
--- /dev/null
+/*
+ * 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 <wine/test.h>
+#include <stdarg.h>
+
+#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();
+}
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE module SYSTEM "../../../tools/rbuild/project.dtd">
+<module name="odbccp32_winetest" type="win32cui" installbase="bin" installname="odbccp32_winetest.exe" allowwarnings="true" entrypoint="0">
+ <include base="odbccp32_winetest">.</include>
+ <define name="WINVER">0x600</define>
+ <define name="_WIN32_WINNT">0x600</define>
+ <library>wine</library>
+ <library>odbccp32</library>
+ <library>user32</library>
+ <library>kernel32</library>
+ <library>ntdll</library>
+ <file>misc.c</file>
+ <file>testlist.c</file>
+</module>
--- /dev/null
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_misc(void);
+
+const struct test winetest_testlist[] =
+{
+ { "misc", func_misc },
+ { 0, 0 }
+};
--- /dev/null
+/*
+ * 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 <stdarg.h>
+
+#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; i<This->fmtetc_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();
+}
--- /dev/null
+/*
+ * 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 <stdarg.h>
+
+#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();
+}