Autosyncing with Wine HEAD
authorThe Wine Synchronizer <winesync@svn.reactos.org>
Thu, 29 Nov 2007 12:54:54 +0000 (12:54 +0000)
committerThe Wine Synchronizer <winesync@svn.reactos.org>
Thu, 29 Nov 2007 12:54:54 +0000 (12:54 +0000)
svn path=/trunk/; revision=30896

106 files changed:
rostests/winetests/advpack/advpack.c [new file with mode: 0644]
rostests/winetests/advpack/advpack.rbuild [new file with mode: 0644]
rostests/winetests/advpack/files.c [new file with mode: 0644]
rostests/winetests/advpack/install.c [new file with mode: 0644]
rostests/winetests/advpack/testlist.c [new file with mode: 0644]
rostests/winetests/browseui/autocomplete.c [new file with mode: 0644]
rostests/winetests/browseui/browseui.rbuild [new file with mode: 0644]
rostests/winetests/browseui/testlist.c [new file with mode: 0644]
rostests/winetests/cabinet/cabinet.rbuild
rostests/winetests/cabinet/extract.c
rostests/winetests/cabinet/fdi.c [new file with mode: 0644]
rostests/winetests/cabinet/testlist.c
rostests/winetests/comcat/comcat.c [new file with mode: 0644]
rostests/winetests/comcat/comcat.rbuild [new file with mode: 0644]
rostests/winetests/comcat/testlist.c [new file with mode: 0644]
rostests/winetests/comdlg32/comdlg32.rbuild
rostests/winetests/comdlg32/filedlg.c
rostests/winetests/comdlg32/printdlg.c
rostests/winetests/comdlg32/testlist.c
rostests/winetests/directory.rbuild
rostests/winetests/lz32/lz32.rbuild
rostests/winetests/lz32/lzexpand_main.c
rostests/winetests/mapi32/imalloc.c [new file with mode: 0644]
rostests/winetests/mapi32/mapi32.rbuild [new file with mode: 0644]
rostests/winetests/mapi32/prop.c [new file with mode: 0644]
rostests/winetests/mapi32/testlist.c [new file with mode: 0644]
rostests/winetests/mapi32/util.c [new file with mode: 0644]
rostests/winetests/mlang/mlang.c
rostests/winetests/mlang/mlang.rbuild
rostests/winetests/netapi32/access.c [new file with mode: 0644]
rostests/winetests/netapi32/apibuf.c [new file with mode: 0644]
rostests/winetests/netapi32/ds.c [new file with mode: 0644]
rostests/winetests/netapi32/netapi32.rbuild [new file with mode: 0644]
rostests/winetests/netapi32/testlist.c [new file with mode: 0644]
rostests/winetests/netapi32/wksta.c [new file with mode: 0644]
rostests/winetests/odbccp32/misc.c [new file with mode: 0644]
rostests/winetests/odbccp32/odbccp32.rbuild [new file with mode: 0644]
rostests/winetests/odbccp32/testlist.c [new file with mode: 0644]
rostests/winetests/ole32/clipboard.c [new file with mode: 0644]
rostests/winetests/ole32/compobj.c [new file with mode: 0644]
rostests/winetests/ole32/dragdrop.c [new file with mode: 0644]
rostests/winetests/ole32/errorinfo.c [new file with mode: 0644]
rostests/winetests/ole32/hglobalstream.c [new file with mode: 0644]
rostests/winetests/ole32/marshal.c [new file with mode: 0644]
rostests/winetests/ole32/moniker.c [new file with mode: 0644]
rostests/winetests/ole32/ole2.c [new file with mode: 0644]
rostests/winetests/ole32/ole32.rbuild [new file with mode: 0644]
rostests/winetests/ole32/propvariant.c [new file with mode: 0644]
rostests/winetests/ole32/stg_prop.c [new file with mode: 0644]
rostests/winetests/ole32/storage32.c [new file with mode: 0644]
rostests/winetests/ole32/testlist.c [new file with mode: 0644]
rostests/winetests/ole32/usrmarshal.c [new file with mode: 0644]
rostests/winetests/oleaut32/oleaut32.rbuild [new file with mode: 0644]
rostests/winetests/oleaut32/olefont.c [new file with mode: 0644]
rostests/winetests/oleaut32/olepicture.c [new file with mode: 0644]
rostests/winetests/oleaut32/safearray.c [new file with mode: 0644]
rostests/winetests/oleaut32/test_tlb.idl [new file with mode: 0644]
rostests/winetests/oleaut32/testlist.c [new file with mode: 0644]
rostests/winetests/oleaut32/tmarshal.c [new file with mode: 0644]
rostests/winetests/oleaut32/tmarshal.idl [new file with mode: 0644]
rostests/winetests/oleaut32/tmarshal.rc [new file with mode: 0644]
rostests/winetests/oleaut32/tmarshal_dispids.h [new file with mode: 0644]
rostests/winetests/oleaut32/typelib.c [new file with mode: 0644]
rostests/winetests/oleaut32/usrmarshal.c [new file with mode: 0644]
rostests/winetests/oleaut32/varformat.c [new file with mode: 0644]
rostests/winetests/oleaut32/vartest.c [new file with mode: 0644]
rostests/winetests/oleaut32/vartype.c [new file with mode: 0644]
rostests/winetests/riched20/editor.c [new file with mode: 0644]
rostests/winetests/riched20/riched20.rbuild [new file with mode: 0644]
rostests/winetests/riched20/testlist.c [new file with mode: 0644]
rostests/winetests/rsabase/rsabase.c [new file with mode: 0644]
rostests/winetests/rsabase/rsabase.rbuild [new file with mode: 0644]
rostests/winetests/rsabase/testlist.c [new file with mode: 0644]
rostests/winetests/rsaenh/rsaenh.c [new file with mode: 0644]
rostests/winetests/rsaenh/rsaenh.rbuild [new file with mode: 0644]
rostests/winetests/rsaenh/testlist.c [new file with mode: 0644]
rostests/winetests/shlwapi/ordinal.c
rostests/winetests/shlwapi/path.c
rostests/winetests/shlwapi/shlwapi.rbuild
rostests/winetests/shlwapi/shreg.c
rostests/winetests/shlwapi/string.c
rostests/winetests/shlwapi/testlist.c
rostests/winetests/shlwapi/url.c [new file with mode: 0644]
rostests/winetests/urlmon/generated.c [new file with mode: 0644]
rostests/winetests/urlmon/misc.c [new file with mode: 0644]
rostests/winetests/urlmon/protocol.c [new file with mode: 0644]
rostests/winetests/urlmon/stream.c [new file with mode: 0644]
rostests/winetests/urlmon/testlist.c [new file with mode: 0644]
rostests/winetests/urlmon/url.c [new file with mode: 0644]
rostests/winetests/urlmon/urlmon.rbuild [new file with mode: 0644]
rostests/winetests/uxtheme/system.c [new file with mode: 0644]
rostests/winetests/uxtheme/testlist.c [new file with mode: 0644]
rostests/winetests/uxtheme/uxtheme.rbuild [new file with mode: 0644]
rostests/winetests/version/info.c
rostests/winetests/version/install.c [new file with mode: 0644]
rostests/winetests/version/testlist.c
rostests/winetests/version/version.rbuild
rostests/winetests/version/version.rc [new file with mode: 0644]
rostests/winetests/wininet/ftp.c [new file with mode: 0644]
rostests/winetests/wininet/generated.c [new file with mode: 0644]
rostests/winetests/wininet/http.c [new file with mode: 0644]
rostests/winetests/wininet/internet.c [new file with mode: 0644]
rostests/winetests/wininet/testlist.c [new file with mode: 0644]
rostests/winetests/wininet/url.c [new file with mode: 0644]
rostests/winetests/wininet/wininet.rbuild [new file with mode: 0644]
rostests/winetests/wininet/wininet_test.h [new file with mode: 0644]

diff --git a/rostests/winetests/advpack/advpack.c b/rostests/winetests/advpack/advpack.c
new file mode 100644 (file)
index 0000000..b09a9c6
--- /dev/null
@@ -0,0 +1,597 @@
+/*
+ * Unit tests for advpack.dll
+ *
+ * Copyright (C) 2005 Robert Reif
+ * Copyright (C) 2005 Sami Aario
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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();
+}
diff --git a/rostests/winetests/advpack/advpack.rbuild b/rostests/winetests/advpack/advpack.rbuild
new file mode 100644 (file)
index 0000000..baabd33
--- /dev/null
@@ -0,0 +1,17 @@
+<?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>
diff --git a/rostests/winetests/advpack/files.c b/rostests/winetests/advpack/files.c
new file mode 100644 (file)
index 0000000..71c1741
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * Unit tests for advpack.dll file functions
+ *
+ * Copyright (C) 2006 James Hawkins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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();
+}
diff --git a/rostests/winetests/advpack/install.c b/rostests/winetests/advpack/install.c
new file mode 100644 (file)
index 0000000..6075963
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Unit tests for advpack.dll install functions
+ *
+ * Copyright (C) 2006 James Hawkins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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();
+}
diff --git a/rostests/winetests/advpack/testlist.c b/rostests/winetests/advpack/testlist.c
new file mode 100644 (file)
index 0000000..3999a51
--- /dev/null
@@ -0,0 +1,19 @@
+/* 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 }
+};
diff --git a/rostests/winetests/browseui/autocomplete.c b/rostests/winetests/browseui/autocomplete.c
new file mode 100644 (file)
index 0000000..ba52b66
--- /dev/null
@@ -0,0 +1,320 @@
+/* Unit tests for autocomplete
+ *
+ * Copyright 2007 Mikolaj Zalewski
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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();
+}
diff --git a/rostests/winetests/browseui/browseui.rbuild b/rostests/winetests/browseui/browseui.rbuild
new file mode 100644 (file)
index 0000000..66b7b4d
--- /dev/null
@@ -0,0 +1,15 @@
+<?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>
diff --git a/rostests/winetests/browseui/testlist.c b/rostests/winetests/browseui/testlist.c
new file mode 100644 (file)
index 0000000..89bfdeb
--- /dev/null
@@ -0,0 +1,15 @@
+/* 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 }
+};
index e87353b..c119392 100644 (file)
@@ -1,9 +1,15 @@
-<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>
index b409287..2cf5154 100644 (file)
@@ -20,7 +20,8 @@
 
 #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 */
@@ -144,9 +143,10 @@ static INT_PTR fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
     DWORD dwAccess = 0;
     DWORD dwShareMode = 0;
     DWORD dwCreateDisposition = OPEN_EXISTING;
-
+    
     dwAccess = GENERIC_READ | GENERIC_WRITE;
-    dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+    /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */
+    dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
 
     if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
         dwCreateDisposition = OPEN_EXISTING;
@@ -166,7 +166,7 @@ static UINT fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
     HANDLE handle = (HANDLE)hf;
     DWORD dwRead;
     BOOL res;
-
+    
     res = ReadFile(handle, memory, cb, &dwRead, NULL);
     ok(res, "Failed to ReadFile\n");
 
@@ -197,7 +197,7 @@ static long fci_seek(INT_PTR hf, long dist, int seektype, int *err, void *pv)
 {
     HANDLE handle = (HANDLE)hf;
     DWORD ret;
-
+    
     ret = SetFilePointer(handle, dist, NULL, seektype);
     ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
 
@@ -247,7 +247,7 @@ static INT_PTR get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
 
     res = GetFileInformationByHandle(handle, &finfo);
     ok(res, "Expected GetFileInformationByHandle to succeed\n");
-
+   
     FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
     FileTimeToDosDateTime(&filetime, pdate, ptime);
 
@@ -288,6 +288,10 @@ static void create_cab_file(void)
     CCAB cabParams;
     HFCI hfci;
     ERF erf;
+    static CHAR a_txt[]         = "a.txt",
+                b_txt[]         = "b.txt",
+                testdir_c_txt[] = "testdir\\c.txt",
+                testdir_d_txt[] = "testdir\\d.txt";
     BOOL res;
 
     set_cab_parameters(&cabParams);
@@ -298,10 +302,10 @@ static void create_cab_file(void)
 
     ok(hfci != NULL, "Failed to create an FCI context\n");
 
-    add_file(hfci, "a.txt");
-    add_file(hfci, "b.txt");
-    add_file(hfci, "testdir\\c.txt");
-    add_file(hfci, "testdir\\d.txt");
+    add_file(hfci, a_txt);
+    add_file(hfci, b_txt);
+    add_file(hfci, testdir_c_txt);
+    add_file(hfci, testdir_d_txt);
 
     res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
     ok(res, "Failed to flush the cabinet\n");
@@ -310,10 +314,26 @@ static void create_cab_file(void)
     ok(res, "Failed to destroy the cabinet\n");
 }
 
+static BOOL check_list(struct FILELIST **node, const char *filename, BOOL do_extract)
+{
+    if (!*node)
+        return FALSE;
+
+    if (lstrcmpA((*node)->FileName, filename))
+        return FALSE;
+
+    if ((*node)->DoExtract != do_extract)
+        return FALSE;
+
+    *node = (*node)->next;
+    return TRUE;
+}
+
 static void test_Extract(void)
 {
-    EXTRACTDEST extractDest;
+    SESSION session;
     HRESULT res;
+    struct FILELIST *node;
 
     /* native windows crashes if
     *   - invalid parameters are sent in
@@ -322,58 +342,341 @@ static void test_Extract(void)
     */
 
     /* try to extract all files */
-    ZeroMemory(&extractDest, sizeof(EXTRACTDEST));
-    lstrcpyA(extractDest.directory, "dest");
-    extractDest.flags = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
-    res = pExtract(&extractDest, "extract.cab");
-    ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+    ZeroMemory(&session, sizeof(SESSION));
+    lstrcpyA(session.Destination, "dest");
+    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
+    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
+       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
     ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
+    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
 
     /* try fill file list operation */
-    ZeroMemory(&extractDest, sizeof(EXTRACTDEST));
-    lstrcpyA(extractDest.directory, "dest");
-    extractDest.flags = EXTRACT_FILLFILELIST;
-    res = pExtract(&extractDest, "extract.cab");
-    ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+    ZeroMemory(&session, sizeof(SESSION));
+    lstrcpyA(session.Destination, "dest");
+    session.Operation = EXTRACT_FILLFILELIST;
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
+    ok(session.Operation == EXTRACT_FILLFILELIST,
+       "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
-    ok(extractDest.filecount == 4, "Expected 4 files, got %ld\n", extractDest.filecount);
-    ok(!lstrcmpA(extractDest.lastfile, "dest\\testdir\\d.txt"),
-        "Expected last file to be dest\\testdir\\d.txt, got %s\n", extractDest.lastfile);
+    ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
+    ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
 
     /* try extract files operation once file list is filled */
-    extractDest.flags = EXTRACT_EXTRACTFILES;
-    res = pExtract(&extractDest, "extract.cab");
-    ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+    session.Operation = EXTRACT_EXTRACTFILES;
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
+    ok(session.Operation == EXTRACT_EXTRACTFILES,
+       "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
     ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
     ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
     ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
+    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
 
     /* Extract does not extract files if the dest dir does not exist */
-    res = pExtract(&extractDest, "extract.cab");
-    ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
+    ok(session.Operation == EXTRACT_EXTRACTFILES,
+       "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
     ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
+    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
     ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
+    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
+    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
 
     /* remove two of the files in the list */
-    extractDest.filelist->next = extractDest.filelist->next->next;
-    extractDest.filelist->next->next = NULL;
+    session.FileList->next = session.FileList->next->next;
+    session.FileList->next->next = NULL;
+    session.FilterList = NULL;
     CreateDirectoryA("dest", NULL);
-    res = pExtract(&extractDest, "extract.cab");
-    ok(res == S_OK, "Expected S_OK, got %ld\n", res);
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 4, "Expected 4, got %d\n", session.FileCount);
+    ok(session.Operation == EXTRACT_EXTRACTFILES,
+       "Expected EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
+    ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
+    ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
+    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
+    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
+    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
+    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n");
+
+    session.Operation = EXTRACT_FILLFILELIST;
+    session.FileList = NULL;
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
+    ok(session.Operation == EXTRACT_FILLFILELIST,
+       "Expected EXTRACT_FILLFILELIST, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
+    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
+    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
+    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
+    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
+    ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
+    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(!check_list(&node, "a.txt", FALSE), "list entry wrong\n");
+
+    session.Operation = 0;
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
+    ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
+    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
+    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
+    ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
+    ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
+    ok(check_list(&node, "testdir\\d.txt", TRUE), "list entry wrong\n");
+    ok(check_list(&node, "testdir\\c.txt", TRUE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", TRUE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
+
+    session.Operation = 0;
+    session.FilterList = session.FileList;
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    ok(res == S_OK, "Expected S_OK, got %d\n", res);
+    ok(session.FileSize == 40, "Expected 40, got %d\n", session.FileSize);
+    ok(session.Error.erfOper == FDIERROR_NONE,
+       "Expected FDIERROR_NONE, got %d\n", session.Error.erfOper);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == FALSE, "Expected FALSE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 8, "Expected 8, got %d\n", session.FileCount);
+    ok(session.Operation == 0, "Expected 0, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\d.txt"),
+       "Expected dest\\testdir\\d.txt, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
     ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
     ok(DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to exist\n");
+    ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
+    ok(DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to exist\n");
+    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
+    node = session.FilterList;
+    ok(check_list(&node, "testdir\\d.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", FALSE), "list entry wrong\n");
+
+    /* cabinet does not exist */
+    ZeroMemory(&session, sizeof(SESSION));
+    lstrcpyA(session.Destination, "dest");
+    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
+    res = pExtract(&session, "nonexistent.cab");
+    node = session.FileList;
+    ok(res == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+       "Expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got %08x\n", res);
+    ok(session.Error.erfOper == FDIERROR_CABINET_NOT_FOUND,
+       "Expected FDIERROR_CABINET_NOT_FOUND, got %d\n", session.Error.erfOper);
+    ok(session.FileSize == 0, "Expected 0, got %d\n", session.FileSize);
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
+    ok(session.FileCount == 0, "Expected 0, got %d\n", session.FileCount);
+    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
+       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!*session.CurrentFile, "Expected empty string, got %s\n", session.CurrentFile);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
+    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
     ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
+    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
     ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
+    ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
+    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n");
+    ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n");
+    ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n");
+
+    /* first file exists */
+    createTestFile("dest\\a.txt");
+    SetFileAttributes("dest\\a.txt", FILE_ATTRIBUTE_READONLY);
+    ZeroMemory(&session, sizeof(SESSION));
+    lstrcpyA(session.Destination, "dest");
+    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
+    res = pExtract(&session, "extract.cab");
+    node = session.FileList;
+    todo_wine
+    {
+        ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL,
+           "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res);
+        ok(session.FileSize == 6, "Expected 6, got %d\n", session.FileSize);
+        ok(session.Error.erfOper == FDIERROR_USER_ABORT,
+           "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper);
+        ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
+        ok(session.FileCount == 1, "Expected 1, got %d\n", session.FileCount);
+        ok(!lstrcmpA(session.CurrentFile, "dest\\a.txt"),
+           "Expected dest\\a.txt, got %s\n", session.CurrentFile);
+    }
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
+       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
+    ok(!DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to not exist\n");
+    todo_wine
+    {
+        ok(!DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to not exist\n");
+        ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
+        ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
+        ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
+        ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry should not exist\n");
+        ok(!check_list(&node, "b.txt", FALSE), "list entry should not exist\n");
+    }
+    ok(!check_list(&node, "a.txt", FALSE), "list entry should not exist\n");
+
+    SetFileAttributesA("dest\\a.txt", FILE_ATTRIBUTE_NORMAL);
+    DeleteFileA("dest\\a.txt");
+
+    /* third file exists */
+    createTestFile("dest\\testdir\\c.txt");
+    SetFileAttributes("dest\\testdir\\c.txt", FILE_ATTRIBUTE_READONLY);
+    ZeroMemory(&session, sizeof(SESSION));
+    lstrcpyA(session.Destination, "dest");
+    session.Operation = EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES;
+    res = pExtract(&session, "extract.cab");
+    todo_wine
+    {
+        ok(res == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) || res == E_FAIL,
+           "Expected HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) or E_FAIL, got %08x\n", res);
+        ok(session.FileSize == 26, "Expected 26, got %d\n", session.FileSize);
+        ok(session.Error.erfOper == FDIERROR_USER_ABORT,
+           "Expected FDIERROR_USER_ABORT, got %d\n", session.Error.erfOper);
+        ok(session.Error.fError == TRUE, "Expected TRUE, got %d\n", session.Error.fError);
+        ok(session.FileCount == 3, "Expected 3, got %d\n", session.FileCount);
+        ok(!lstrcmpA(session.CurrentFile, "dest\\testdir\\c.txt"),
+           "Expected dest\\c.txt, got %s\n", session.CurrentFile);
+    }
+    ok(session.Error.erfType == 0, "Expected 0, got %d\n", session.Error.erfType);
+    ok(session.Operation == (EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES),
+       "Expected EXTRACT_FILLFILELIST | EXTRACT_EXTRACTFILES, got %d\n", session.Operation);
+    ok(!lstrcmpA(session.Destination, "dest"), "Expected dest, got %s\n", session.Destination);
+    ok(!*session.Reserved, "Expected empty string, got %s\n", session.Reserved);
+    ok(!session.FilterList, "Expected empty filter list\n");
+    ok(DeleteFileA("dest\\a.txt"), "Expected dest\\a.txt to exist\n");
+    ok(DeleteFileA("dest\\b.txt"), "Expected dest\\b.txt to exist\n");
+    ok(!DeleteFileA("dest\\testdir\\c.txt"), "Expected dest\\testdir\\c.txt to not exist\n");
+    todo_wine
+    {
+        ok(!DeleteFileA("dest\\testdir\\d.txt"), "Expected dest\\testdir\\d.txt to not exist\n");
+    }
+    ok(!check_list(&node, "testdir\\d.txt", FALSE), "list entry should not exist\n");
+    ok(!check_list(&node, "testdir\\c.txt", FALSE), "list entry wrong\n");
+    ok(!check_list(&node, "b.txt", FALSE), "list entry wrong\n");
+    ok(check_list(&node, "a.txt", TRUE), "list entry wrong\n");
+
+    SetFileAttributesA("dest\\testdir\\c.txt", FILE_ATTRIBUTE_NORMAL);
+    DeleteFileA("dest\\testdir\\c.txt");
+
     ok(RemoveDirectoryA("dest\\testdir"), "Expected dest\\testdir to exist\n");
-    ok(RemoveDirectoryA("dest"), "Expected dest\\testdir to exist\n");
+    ok(RemoveDirectoryA("dest"), "Expected dest to exist\n");
 }
 
 START_TEST(extract)
diff --git a/rostests/winetests/cabinet/fdi.c b/rostests/winetests/cabinet/fdi.c
new file mode 100644 (file)
index 0000000..7c10b3d
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * Unit tests for the File Decompression Interface
+ *
+ * Copyright (C) 2006 James Hawkins
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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();
+}
index b837786..992cd35 100644 (file)
@@ -1,3 +1,5 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
@@ -5,9 +7,11 @@
 #include "wine/test.h"
 
 extern void func_extract(void);
+extern void func_fdi(void);
 
 const struct test winetest_testlist[] =
 {
     { "extract", func_extract },
+    { "fdi", func_fdi },
     { 0, 0 }
 };
diff --git a/rostests/winetests/comcat/comcat.c b/rostests/winetests/comcat/comcat.c
new file mode 100644 (file)
index 0000000..914cbe8
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * tests for comcat functions
+ *
+ * Copyright 2006 Aric Stewart for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <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(); 
+}
diff --git a/rostests/winetests/comcat/comcat.rbuild b/rostests/winetests/comcat/comcat.rbuild
new file mode 100644 (file)
index 0000000..7fe8a38
--- /dev/null
@@ -0,0 +1,15 @@
+<?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>
diff --git a/rostests/winetests/comcat/testlist.c b/rostests/winetests/comcat/testlist.c
new file mode 100644 (file)
index 0000000..ba94e96
--- /dev/null
@@ -0,0 +1,15 @@
+/* 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 }
+};
index 2b7ed26..fc3c02e 100644 (file)
@@ -1,15 +1,15 @@
-<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>
index 698af1a..03192ba 100644 (file)
-/*\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();
+
+}
index 2748248..783773f 100644 (file)
-/*\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();
+}
index abbda30..339b352 100644 (file)
@@ -1,17 +1,17 @@
-/* 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 }
+};
index d4cc460..686d51c 100644 (file)
@@ -2,9 +2,18 @@
 <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>
@@ -23,6 +32,9 @@
 <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>
index dd2500a..abe12d9 100644 (file)
@@ -1,9 +1,13 @@
-<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>
index a5d0e59..b51067b 100644 (file)
 
 #include "wine/test.h"
 
+/* Compressed file names end with underscore. */
+static char filename [] = "testfile.xxx";
 static char filename_[] = "testfile.xx_";
-#if 0
-static char filename[] = "testfile.xxx";
-#endif
+static WCHAR filenameW [] = {'t','e','s','t','f','i','l','e','.','x','x','x',0};
+static WCHAR filenameW_[] = {'t','e','s','t','f','i','l','e','.','x','x','_',0};
+
+static char dotless [] = "dotless";
+static char dotless_[] = "dotless._";
+static WCHAR dotlessW [] = {'d','o','t','l','e','s','s', 0};
+static WCHAR dotlessW_[] = {'d','o','t','l','e','s','s','.','_', 0};
+
+static char extless [] = "extless.";
+static char extless_[] = "extless._";
+static WCHAR extlessW [] = {'e','x','t','l','e','s','s','.', 0};
+static WCHAR extlessW_[] = {'e','x','t','l','e','s','s','.','_', 0};
+
+static char _terminated [] = "_terminated.xxxx_";
+static char _terminated_[] = "_terminated.xxxx_";
+static WCHAR _terminatedW [] = {'_','t','e','r','m','i','n','a','t','e','d','.','x','x','x','x','_', 0};
+static WCHAR _terminatedW_[] = {'_','t','e','r','m','i','n','a','t','e','d','.','x','x','x','x','_', 0};
+
 static char filename2[] = "testfile.yyy";
 
 /* This is the hex string representation of the file created by compressing
    a simple text file with the contents "This is a test file."
-
    The file was created using COMPRESS.EXE from the Windows Server 2003
    Resource Kit from Microsoft.  The resource kit was retrieved from the
-   following URL:
+   following URL:  
 
    http://www.microsoft.com/downloads/details.aspx?FamilyID=9d467a69-57ff-4ae7-96ee-b18c4790cffd&displaylang=en
  */
-static const unsigned char compressed_file[] =
+static const unsigned char compressed_file[] = 
   {0x53,0x5A,0x44,0x44,0x88,0xF0,0x27,0x33,0x41,
    0x74,0x75,0x14,0x00,0x00,0xDF,0x54,0x68,0x69,
    0x73,0x20,0xF2,0xF0,0x61,0x20,0xFF,0x74,0x65,
@@ -54,67 +71,404 @@ static const DWORD uncompressed_data_size = sizeof(uncompressed_data) - 1;
 
 static char *buf;
 
-static void test_lzopenfile(void)
+static void full_file_path_name_in_a_CWD(const char *src, char *dst, BOOL expect_short)
+{
+  DWORD retval;
+  char shortname[MAX_PATH];
+
+  retval = GetCurrentDirectoryA(MAX_PATH, dst);
+  ok(retval > 0, "GetCurrentDirectoryA returned %d, GLE=%d\n",
+     retval, GetLastError());
+  if(dst[retval-1] != '\\')
+    /* Append backslash only when it's missing */
+      lstrcatA(dst, "\\");
+  lstrcatA(dst, src);
+  if(expect_short) 
+  {
+    memcpy(shortname, dst, MAX_PATH);
+    retval = GetShortPathName(shortname, dst, MAX_PATH-1);
+    ok(retval > 0, "GetShortPathName returned %d for '%s', GLE=%d\n",
+       retval, dst, GetLastError());
+  }
+}
+
+static void create_file(char *fname)
+{
+  INT file;
+  OFSTRUCT ofs;
+  DWORD retval;
+
+  file = LZOpenFileA(fname, &ofs, OF_CREATE);
+  ok(file >= 0, "LZOpenFileA failed to create '%s'\n", fname);
+  LZClose(file);
+  retval = GetFileAttributesA(fname);
+  ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA('%s'): error %d\n", ofs.szPathName, GetLastError());
+}
+
+static void delete_file(char *fname)
+{
+  INT file;
+  OFSTRUCT ofs;
+  DWORD retval;
+
+  file = LZOpenFileA(fname, &ofs, OF_DELETE);
+  ok(file >= 0, "LZOpenFileA failed to delete '%s'\n", fname);
+  LZClose(file);
+  retval = GetFileAttributesA(fname);
+  ok(retval == INVALID_FILE_ATTRIBUTES, "GetFileAttributesA succeeded on deleted file ('%s')\n", ofs.szPathName);
+}
+
+static void test_LZOpenFileA_existing_compressed(void)
+{
+  OFSTRUCT test;
+  INT file;
+  char expected[MAX_PATH];
+  char short_expected[MAX_PATH];
+  char filled_0xA5[OFS_MAXPATHNAME];
+
+  /* Try to open existing compressed files: */
+  create_file(filename_);
+  create_file(dotless_);
+  create_file(extless_);
+  create_file(_terminated_);
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(filename_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* a, using 8.3-conformant file name. */
+  file = LZOpenFileA(filename, &test, OF_EXIST);
+  /* If the file "foo.xxx" does not exist, LZOpenFileA should then
+     check for the file "foo.xx_" and open that -- at least on some
+     operating systems.  Doesn't seem to on my copy of Win98.   
+   */
+  if(file != LZERROR_BADINHANDLE) {
+    ok(file >= 0, "LZOpenFileA returns negative file descriptor for '%s'\n", filename);
+    ok(test.cBytes == sizeof(OFSTRUCT), 
+       "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+    ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", 
+       test.nErrCode);
+    ok(lstrcmpA(test.szPathName, expected) == 0, 
+       "LZOpenFileA returned '%s', but was expected to return '%s'\n", 
+       test.szPathName, expected);
+    LZClose(file);
+  } else { /* Win9x */
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+       "GetLastError() returns %d\n", GetLastError());
+    ok(test.cBytes == 0xA5, 
+       "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+    ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+       "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+    ok(strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, 
+       "LZOpenFileA returned '%s', but was expected to return '%s'\n", 
+       test.szPathName, filled_0xA5);
+  }
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(dotless_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* b, using dotless file name. */
+  file = LZOpenFileA(dotless, &test, OF_EXIST);
+  if(file != LZERROR_BADINHANDLE) {
+    ok(file >= 0, "LZOpenFileA returns negative file descriptor for '%s'\n", dotless);
+    ok(test.cBytes == sizeof(OFSTRUCT), 
+       "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+    ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", 
+       test.nErrCode);
+    ok(lstrcmpA(test.szPathName, expected) == 0, 
+       "LZOpenFileA returned '%s', but was expected to return '%s'\n", 
+       test.szPathName, expected);
+    LZClose(file);
+  } else { /* Win9x */
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+       "GetLastError() returns %d\n", GetLastError());
+    todo_wine
+    ok(test.cBytes == 0xA5, 
+       "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+    ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+       "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+    todo_wine
+    ok(strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, 
+       "LZOpenFileA returned '%s', but was expected to return '%s'\n", 
+       test.szPathName, filled_0xA5);
+  }
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(extless_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* c, using extensionless file name. */
+  file = LZOpenFileA(extless, &test, OF_EXIST);
+  if(file != LZERROR_BADINHANDLE) {
+    ok(file >= 0, "LZOpenFileA returns negative file descriptor for '%s'\n", extless);
+    ok(test.cBytes == sizeof(OFSTRUCT), 
+       "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+    ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", 
+       test.nErrCode);
+    ok(lstrcmpA(test.szPathName, expected) == 0, 
+       "LZOpenFileA returned '%s', but was expected to return '%s'\n", 
+       test.szPathName, expected);
+    LZClose(file);
+  } else { /* Win9x */
+    ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+       "GetLastError() returns %d\n", GetLastError());
+    ok(test.cBytes == 0xA5, 
+       "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+    ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+       "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+    ok(strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, 
+       "LZOpenFileA returned '%s', but was expected to return '%s'\n", 
+       test.szPathName, filled_0xA5);
+  }
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(_terminated_, expected, FALSE);
+  full_file_path_name_in_a_CWD(_terminated_, short_expected, TRUE);
+
+  /* d, using underscore-terminated file name. */
+  file = LZOpenFileA(_terminated, &test, OF_EXIST);
+  ok(file >= 0, "LZOpenFileA failed on switching to a compressed file name\n");
+  ok(test.cBytes == sizeof(OFSTRUCT), 
+     "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == 0, "LZOpenFileA set test.nErrCode to %d\n", 
+     test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0 ||
+     lstrcmpA(test.szPathName, short_expected) == 0, /* Win9x */
+     "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, short_expected);
+  LZClose(file);
+
+  delete_file(filename_);
+  delete_file(dotless_);
+  delete_file(extless_);
+  delete_file(_terminated_);
+}
+
+static void test_LZOpenFileA_nonexisting_compressed(void)
+{
+  OFSTRUCT test;
+  INT file;
+  char expected[MAX_PATH];
+  char filled_0xA5[OFS_MAXPATHNAME];
+
+  /* Try to open nonexisting compressed files: */
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(filename_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* a, using 8.3-conformant file name. */
+  file = LZOpenFileA(filename, &test, OF_EXIST);
+  /* If the file "foo.xxx" does not exist, LZOpenFileA should then
+     check for the file "foo.xx_" and open that -- at least on some
+     operating systems.  Doesn't seem to on my copy of Win98.   
+   */
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileA succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0 ||
+     strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */
+     "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, filled_0xA5);
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(dotless_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* b, using dotless file name. */
+  file = LZOpenFileA(dotless, &test, OF_EXIST);
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileA succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0 ||
+     strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */
+     "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, filled_0xA5);
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(extless_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* c, using extensionless file name. */
+  file = LZOpenFileA(extless, &test, OF_EXIST);
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileA succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0 ||
+     strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */
+     "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, filled_0xA5);
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(_terminated_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* d, using underscore-terminated file name. */
+  file = LZOpenFileA(_terminated, &test, OF_EXIST);
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileA succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0 ||
+     strncmp(test.szPathName, filled_0xA5, OFS_MAXPATHNAME) == 0, /* Win9x */
+     "LZOpenFileA returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, filled_0xA5);
+}
+
+static void test_LZOpenFileA(void)
 {
   OFSTRUCT test;
   DWORD retval;
   INT file;
+  static char badfilename_[] = "badfilename_";
+  char expected[MAX_PATH];
+  char short_expected[MAX_PATH];
 
+  SetLastError(0xfaceabee);
   /* Check for nonexistent file. */
-  file = LZOpenFile("badfilename_", &test, OF_READ);
-  ok(file == LZERROR_BADINHANDLE,
-     "LZOpenFile succeeded on nonexistent file\n");
+  file = LZOpenFileA(badfilename_, &test, OF_READ);
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileA succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND, 
+     "GetLastError() returns %d\n", GetLastError());
   LZClose(file);
 
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(filename_, expected, FALSE);
+
   /* Create an empty file. */
-  file = LZOpenFile(filename_, &test, OF_CREATE);
-  ok(file >= 0, "LZOpenFile failed on creation\n");
+  file = LZOpenFileA(filename_, &test, OF_CREATE);
+  ok(file >= 0, "LZOpenFileA failed on creation\n");
+  ok(test.cBytes == sizeof(OFSTRUCT),
+     "LZOpenFileA set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS,
+     "LZOpenFileA set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileA returned '%s', but was expected to return '%s'\n",
+     test.szPathName, expected);
   LZClose(file);
-  retval = GetFileAttributes(filename_);
-  ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributes: error %ld\n",
+
+  retval = GetFileAttributesA(filename_);
+  ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA: error %d\n", 
      GetLastError());
 
-  /* Check various opening options. */
-  file = LZOpenFile(filename_, &test, OF_READ);
-  ok(file >= 0, "LZOpenFile failed on read\n");
-  LZClose(file);
-  file = LZOpenFile(filename_, &test, OF_WRITE);
-  ok(file >= 0, "LZOpenFile failed on write\n");
+  /* 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;
@@ -123,15 +477,15 @@ static void test_lzread(void)
   BOOL retok;
 
   /* Create the compressed file. */
-  file = CreateFile(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
+  file = CreateFileA(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
   ok(file != INVALID_HANDLE_VALUE, "Could not create test file\n");
   retok = WriteFile(file, compressed_file, compressed_file_size, &ret, 0);
-  ok( retok, "WriteFile: error %ld\n", GetLastError());
+  ok( retok, "WriteFile: error %d\n", GetLastError());
   ok(ret == compressed_file_size, "Wrote wrong number of bytes with WriteFile?\n");
   CloseHandle(file);
 
-  cfile = LZOpenFile(filename_, &test, OF_READ);
-  ok(cfile > 0, "LZOpenFile failed\n");
+  cfile = LZOpenFileA(filename_, &test, OF_READ);
+  ok(cfile > 0, "LZOpenFileA failed\n");
 
   ret = LZRead(cfile, buf, uncompressed_data_size);
   ok(ret == uncompressed_data_size, "Read wrong number of bytes\n");
@@ -148,11 +502,11 @@ static void test_lzread(void)
 
   LZClose(cfile);
 
-  ret = DeleteFile(filename_);
-  ok(ret, "DeleteFile: error %ld\n", GetLastError());
+  ret = DeleteFileA(filename_);
+  ok(ret, "DeleteFileA: error %d\n", GetLastError());
 }
 
-static void test_lzcopy(void)
+static void test_LZCopy(void)
 {
   HANDLE file;
   DWORD ret;
@@ -161,18 +515,18 @@ static void test_lzcopy(void)
   BOOL retok;
 
   /* Create the compressed file. */
-  file = CreateFile(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
-  ok(file != INVALID_HANDLE_VALUE,
-     "CreateFile: error %ld\n", GetLastError());
+  file = CreateFileA(filename_, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0);
+  ok(file != INVALID_HANDLE_VALUE, 
+     "CreateFileA: error %d\n", GetLastError());
   retok = WriteFile(file, compressed_file, compressed_file_size, &ret, 0);
-  ok( retok, "WriteFile error %ld\n", GetLastError());
+  ok( retok, "WriteFile error %d\n", GetLastError());
   ok(ret == compressed_file_size, "Wrote wrong number of bytes\n");
   CloseHandle(file);
 
-  source = LZOpenFile(filename_, &stest, OF_READ);
-  ok(source >= 0, "LZOpenFile failed on compressed file\n");
-  dest = LZOpenFile(filename2, &dtest, OF_CREATE);
-  ok(dest >= 0, "LZOpenFile failed on creating new file %d\n", dest);
+  source = LZOpenFileA(filename_, &stest, OF_READ);
+  ok(source >= 0, "LZOpenFileA failed on compressed file\n");
+  dest = LZOpenFileA(filename2, &dtest, OF_CREATE);
+  ok(dest >= 0, "LZOpenFileA failed on creating new file %d\n", dest);
 
   ret = LZCopy(source, dest);
   ok(ret > 0, "LZCopy error\n");
@@ -180,29 +534,353 @@ static void test_lzcopy(void)
   LZClose(source);
   LZClose(dest);
 
-  file = CreateFile(filename2, GENERIC_READ, 0, NULL, OPEN_EXISTING,
-                   0, 0);
+  file = CreateFileA(filename2, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
   ok(file != INVALID_HANDLE_VALUE,
-     "CreateFile: error %ld\n", GetLastError());
+     "CreateFileA: error %d\n", GetLastError());
 
   retok = ReadFile(file, buf, uncompressed_data_size*2, &ret, 0);
-  ok( retok && ret == uncompressed_data_size, "ReadFile: error %ld\n", GetLastError());
+  ok( retok && ret == uncompressed_data_size, "ReadFile: error %d\n", GetLastError());
   /* Compare what we read with what we think we should read. */
   ok(!memcmp(buf, uncompressed_data, uncompressed_data_size),
      "buffer contents mismatch\n");
   CloseHandle(file);
 
-  ret = DeleteFile(filename_);
-  ok(ret, "DeleteFile: error %ld\n", GetLastError());
-  ret = DeleteFile(filename2);
-  ok(ret, "DeleteFile: error %ld\n", GetLastError());
+  ret = DeleteFileA(filename_);
+  ok(ret, "DeleteFileA: error %d\n", GetLastError());
+  ret = DeleteFileA(filename2);
+  ok(ret, "DeleteFileA: error %d\n", GetLastError());
 }
 
+static void create_fileW(WCHAR *fnameW)
+{
+  INT file;
+  OFSTRUCT ofs;
+  DWORD retval;
+
+  file = LZOpenFileW(fnameW, &ofs, OF_CREATE);
+  ok(file >= 0, "LZOpenFileW failed on creation\n");
+  LZClose(file);
+  retval = GetFileAttributesW(fnameW);
+  ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributesW('%s'): error %d\n", ofs.szPathName, GetLastError());
+}
+
+static void delete_fileW(WCHAR *fnameW)
+{
+  INT file;
+  OFSTRUCT ofs;
+  DWORD retval;
+
+  file = LZOpenFileW(fnameW, &ofs, OF_DELETE);
+  ok(file >= 0, "LZOpenFileW failed on delete\n");
+  LZClose(file);
+  retval = GetFileAttributesW(fnameW);
+  ok(retval == INVALID_FILE_ATTRIBUTES, "GetFileAttributesW succeeded on deleted file ('%s')\n", ofs.szPathName);
+}
+
+static void test_LZOpenFileW_existing_compressed(void)
+{
+  OFSTRUCT test;
+  INT file;
+  char expected[MAX_PATH];
+
+  /* Try to open existing compressed files: */
+  create_fileW(filenameW_);
+  create_fileW(dotlessW_);
+  create_fileW(extlessW_);
+  create_fileW(_terminatedW_);
+
+  full_file_path_name_in_a_CWD(filename_, expected, FALSE);
+  memset(&test, 0xA5, sizeof(test));
+
+  /* a, using 8.3-conformant file name. */
+  file = LZOpenFileW(filenameW, &test, OF_EXIST);
+  /* If the file "foo.xxx" does not exist, LZOpenFileW should then
+     check for the file "foo.xx_" and open that.
+   */
+  ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n");
+  ok(test.cBytes == sizeof(OFSTRUCT), 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", 
+     test.nErrCode);
+  /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */
+  ok(lstrcmpA(test.szPathName, expected) == 0, 
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n", 
+     test.szPathName, expected);
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(dotless_, expected, FALSE);
+
+  /* b, using dotless file name. */
+  file = LZOpenFileW(dotlessW, &test, OF_EXIST);
+  ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n");
+  ok(test.cBytes == sizeof(OFSTRUCT), 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", 
+     test.nErrCode);
+  /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */
+  ok(lstrcmpA(test.szPathName, expected) == 0, 
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n", 
+     test.szPathName, expected);
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(extless_, expected, FALSE);
+
+  /* c, using extensionless file name. */
+  file = LZOpenFileW(extlessW, &test, OF_EXIST);
+  ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n");
+  ok(test.cBytes == sizeof(OFSTRUCT), 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", 
+     test.nErrCode);
+  /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */
+  ok(lstrcmpA(test.szPathName, expected) == 0, 
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n", 
+     test.szPathName, expected);
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(_terminated_, expected, FALSE);
+
+  /* d, using underscore-terminated file name. */
+  file = LZOpenFileW(_terminatedW, &test, OF_EXIST);
+  ok(file >= 0, "LZOpenFileW failed on switching to a compressed file name\n");
+  ok(test.cBytes == sizeof(OFSTRUCT), 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS, "LZOpenFileW set test.nErrCode to %d\n", 
+     test.nErrCode);
+  /* Note that W-function returns A-string by a OFSTRUCT.szPathName: */
+  ok(lstrcmpA(test.szPathName, expected) == 0, 
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n", 
+     test.szPathName, expected);
+  LZClose(file);
+
+  delete_fileW(filenameW_);
+  delete_fileW(dotlessW_);
+  delete_fileW(extlessW_);
+  delete_fileW(_terminatedW_);
+}
+
+static void test_LZOpenFileW_nonexisting_compressed(void)
+{
+  OFSTRUCT test;
+  INT file;
+  char expected[MAX_PATH];
+  char filled_0xA5[OFS_MAXPATHNAME];
+
+  /* Try to open nonexisting compressed files: */
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(filename_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* a, using 8.3-conformant file name. */
+  file = LZOpenFileW(filenameW, &test, OF_EXIST);
+  /* If the file "foo.xxx" does not exist, LZOpenFileA should then
+     check for the file "foo.xx_" and open that -- at least on some
+     operating systems.  Doesn't seem to on my copy of Win98.   
+   */
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileW succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, filled_0xA5);
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(dotless_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* b, using dotless file name. */
+  file = LZOpenFileW(dotlessW, &test, OF_EXIST);
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileW succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, filled_0xA5);
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(extless_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* c, using extensionless file name. */
+  file = LZOpenFileW(extlessW, &test, OF_EXIST);
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileW succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n", 
+     test.szPathName, expected, filled_0xA5);
+
+  memset(&filled_0xA5, 0xA5, OFS_MAXPATHNAME);
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(_terminated_, expected, FALSE);
+  SetLastError(0xfaceabee);
+
+  /* d, using underscore-terminated file name. */
+  file = LZOpenFileW(_terminatedW, &test, OF_EXIST);
+  ok(file == LZERROR_BADINHANDLE, 
+     "LZOpenFileW succeeded on nonexistent file\n");
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND,
+     "GetLastError() returns %d\n", GetLastError());
+  todo_wine
+  ok(test.cBytes == 0xA5, 
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_FILE_NOT_FOUND, 
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s' or '%s'\n",
+     test.szPathName, expected, filled_0xA5);
+}
+
+static void test_LZOpenFileW(void)
+{
+  OFSTRUCT test;
+  DWORD retval;
+  INT file;
+  static WCHAR badfilenameW[] = {'b','a','d','f','i','l','e','n','a','m','e','.','x','t','n',0};
+  char expected[MAX_PATH];
+
+  SetLastError(0xfaceabee);
+  /* Check for nonexistent file. */
+  file = LZOpenFileW(badfilenameW, &test, OF_READ);
+  ok(GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
+     "GetLastError() returns %d\n", GetLastError());
+  if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+  {
+    trace("LZOpenFileW call not implemented, skipping rest of the test\n");
+    return;
+  }
+  ok(file == LZERROR_BADINHANDLE, "LZOpenFileW succeeded on nonexistent file\n");
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+  full_file_path_name_in_a_CWD(filename_, expected, FALSE);
+
+  /* Create an empty file. */
+  file = LZOpenFileW(filenameW_, &test, OF_CREATE);
+  ok(file >= 0, "LZOpenFile failed on creation\n");
+  ok(test.cBytes == sizeof(OFSTRUCT),
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS,
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n",
+     test.szPathName, expected);
+  LZClose(file);
+
+  retval = GetFileAttributesW(filenameW_);
+  ok(retval != INVALID_FILE_ATTRIBUTES, "GetFileAttributes: error %d\n", 
+    GetLastError());
+
+  /* Check various opening options: */
+  memset(&test, 0xA5, sizeof(test));
+
+  /* a, for reading. */
+  file = LZOpenFileW(filenameW_, &test, OF_READ);
+  ok(file >= 0, "LZOpenFileW failed on read\n");
+  ok(test.cBytes == sizeof(OFSTRUCT),
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS,
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n",
+     test.szPathName, expected);
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+
+  /* b, for writing. */
+  file = LZOpenFileW(filenameW_, &test, OF_WRITE);
+  ok(file >= 0, "LZOpenFileW failed on write\n");
+  ok(test.cBytes == sizeof(OFSTRUCT),
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS,
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n",
+     test.szPathName, expected);
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+
+  /* c, for reading and writing. */
+  file = LZOpenFileW(filenameW_, &test, OF_READWRITE);
+  ok(file >= 0, "LZOpenFileW failed on read/write\n");
+  ok(test.cBytes == sizeof(OFSTRUCT),
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS,
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n",
+     test.szPathName, expected);
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+
+  /* d, for checking file existence. */
+  file = LZOpenFileW(filenameW_, &test, OF_EXIST);
+  ok(file >= 0, "LZOpenFileW failed on read/write\n");
+  ok(test.cBytes == sizeof(OFSTRUCT),
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS,
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n",
+     test.szPathName, expected);
+  LZClose(file);
+
+  memset(&test, 0xA5, sizeof(test));
+
+  /* Delete the file then make sure it doesn't exist anymore. */
+  file = LZOpenFileW(filenameW_, &test, OF_DELETE);
+  ok(file >= 0, "LZOpenFileW failed on delete\n");
+  ok(test.cBytes == sizeof(OFSTRUCT),
+     "LZOpenFileW set test.cBytes to %d\n", test.cBytes);
+  ok(test.nErrCode == ERROR_SUCCESS,
+     "LZOpenFileW set test.nErrCode to %d\n", test.nErrCode);
+  ok(lstrcmpA(test.szPathName, expected) == 0,
+     "LZOpenFileW returned '%s', but was expected to return '%s'\n",
+     test.szPathName, expected);
+  LZClose(file);
+
+  retval = GetFileAttributesW(filenameW_);
+  ok(retval == INVALID_FILE_ATTRIBUTES, 
+     "GetFileAttributesW succeeded on deleted file\n");
+
+  test_LZOpenFileW_existing_compressed();
+  test_LZOpenFileW_nonexisting_compressed();
+}
+
+
 START_TEST(lzexpand_main)
 {
   buf = malloc(uncompressed_data_size * 2);
-  test_lzopenfile();
-  test_lzread();
-  test_lzcopy();
+  test_LZOpenFileA();
+  test_LZOpenFileW();
+  test_LZRead();
+  test_LZCopy();
   free(buf);
 }
diff --git a/rostests/winetests/mapi32/imalloc.c b/rostests/winetests/mapi32/imalloc.c
new file mode 100644 (file)
index 0000000..63c66f2
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Unit test suite for MAPI IMalloc functions
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winnt.h"
+#include "mapiutil.h"
+
+static HMODULE hMapi32 = 0;
+
+static SCODE    (WINAPI *pScInitMapiUtil)(ULONG);
+static LPMALLOC (WINAPI *pMAPIGetDefaultMalloc)(void);
+
+static void test_IMalloc(void)
+{
+    LPVOID lpMem;
+    ULONG ulRef;
+    int iRet;
+    HRESULT hRet;
+    LPMALLOC lpMalloc;
+    LPVOID lpVoid;
+
+    pMAPIGetDefaultMalloc = (void*)GetProcAddress(hMapi32,
+                                                  "MAPIGetDefaultMalloc@0");
+    if (!pMAPIGetDefaultMalloc)
+        return;
+
+    lpMalloc = pMAPIGetDefaultMalloc();
+    if (!lpMalloc)
+        return;
+
+    lpVoid = NULL;
+    hRet = IMalloc_QueryInterface(lpMalloc, &IID_IUnknown, &lpVoid);
+    ok (hRet == S_OK && lpVoid != NULL,
+        "IID_IUnknown: expected S_OK, non-null, got 0x%08x, %p\n",
+        hRet, lpVoid);
+
+    lpVoid = NULL;
+    hRet = IMalloc_QueryInterface(lpMalloc, &IID_IMalloc, &lpVoid);
+    ok (hRet == S_OK && lpVoid != NULL,
+        "IID_IIMalloc: expected S_OK, non-null, got 0x%08x, %p\n",
+        hRet, lpVoid);
+
+    /* Prove that native mapi uses LocalAlloc/LocalFree */
+    lpMem = IMalloc_Alloc(lpMalloc, 61);
+    ok (lpMem && IMalloc_GetSize(lpMalloc, lpMem) ==
+        LocalSize((HANDLE)lpMem),
+        "Expected non-null, same size, got %p, %s size\n", lpMem,
+        lpMem ? "different" : "same");
+
+    iRet = IMalloc_DidAlloc(lpMalloc, lpMem);
+    ok (iRet == -1, "DidAlloc, expected -1. got %d\n", iRet);
+
+    IMalloc_HeapMinimize(lpMalloc);
+    LocalFree(lpMem);
+    ulRef = IMalloc_AddRef(lpMalloc);
+    ok (ulRef == 1u, "AddRef expected 1, returned %d\n", ulRef); 
+    
+    ulRef = IMalloc_Release(lpMalloc);
+    ok (ulRef == 1u, "AddRef expected 1, returned %d\n", ulRef);
+
+    IMalloc_Release(lpMalloc);
+}
+
+START_TEST(imalloc)
+{
+    SCODE ret;
+
+    hMapi32 = LoadLibraryA("mapi32.dll");
+
+    pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4");
+    if (!pScInitMapiUtil)
+    {
+        skip("ScInitMapiUtil is not available\n");
+        FreeLibrary(hMapi32);
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = pScInitMapiUtil(0);
+    if ((ret != S_OK) && (GetLastError() == ERROR_PROC_NOT_FOUND))
+    {
+        skip("ScInitMapiUtil is not implemented\n");
+        FreeLibrary(hMapi32);
+        return;
+    }
+
+    test_IMalloc();
+
+    FreeLibrary(hMapi32);
+}
diff --git a/rostests/winetests/mapi32/mapi32.rbuild b/rostests/winetests/mapi32/mapi32.rbuild
new file mode 100644 (file)
index 0000000..8712bb2
--- /dev/null
@@ -0,0 +1,15 @@
+<?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>
diff --git a/rostests/winetests/mapi32/prop.c b/rostests/winetests/mapi32/prop.c
new file mode 100644 (file)
index 0000000..d78eb36
--- /dev/null
@@ -0,0 +1,1381 @@
+/*
+ * Unit test suite for MAPI property functions
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winnt.h"
+#include "mapiutil.h"
+#include "mapitags.h"
+
+static HMODULE hMapi32 = 0;
+
+static SCODE        (WINAPI *pScInitMapiUtil)(ULONG);
+static SCODE        (WINAPI *pPropCopyMore)(LPSPropValue,LPSPropValue,ALLOCATEMORE*,LPVOID);
+static ULONG        (WINAPI *pUlPropSize)(LPSPropValue);
+static BOOL         (WINAPI *pFPropContainsProp)(LPSPropValue,LPSPropValue,ULONG);
+static BOOL         (WINAPI *pFPropCompareProp)(LPSPropValue,ULONG,LPSPropValue);
+static LONG         (WINAPI *pLPropCompareProp)(LPSPropValue,LPSPropValue);
+static LPSPropValue (WINAPI *pPpropFindProp)(LPSPropValue,ULONG,ULONG);
+static SCODE        (WINAPI *pScCountProps)(INT,LPSPropValue,ULONG*);
+static SCODE        (WINAPI *pScCopyProps)(int,LPSPropValue,LPVOID,ULONG*);
+static SCODE        (WINAPI *pScRelocProps)(int,LPSPropValue,LPVOID,LPVOID,ULONG*);
+static LPSPropValue (WINAPI *pLpValFindProp)(ULONG,ULONG,LPSPropValue);
+static BOOL         (WINAPI *pFBadRglpszA)(LPSTR*,ULONG);
+static BOOL         (WINAPI *pFBadRglpszW)(LPWSTR*,ULONG);
+static BOOL         (WINAPI *pFBadRowSet)(LPSRowSet);
+static ULONG        (WINAPI *pFBadPropTag)(ULONG);
+static ULONG        (WINAPI *pFBadRow)(LPSRow);
+static ULONG        (WINAPI *pFBadProp)(LPSPropValue);
+static ULONG        (WINAPI *pFBadColumnSet)(LPSPropTagArray);
+static SCODE        (WINAPI *pCreateIProp)(LPCIID,ALLOCATEBUFFER*,ALLOCATEMORE*,
+                                           FREEBUFFER*,LPVOID,LPPROPDATA*);
+static SCODE        (WINAPI *pMAPIAllocateBuffer)(ULONG, LPVOID);
+static SCODE        (WINAPI *pMAPIAllocateMore)(ULONG, LPVOID, LPVOID);
+static SCODE        (WINAPI *pMAPIFreeBuffer)(LPVOID);
+
+static BOOL InitFuncPtrs(void)
+{
+    hMapi32 = LoadLibraryA("mapi32.dll");
+
+    pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4");
+    pMAPIAllocateBuffer = (void*)GetProcAddress(hMapi32, "MAPIAllocateBuffer");
+    pMAPIAllocateMore = (void*)GetProcAddress(hMapi32, "MAPIAllocateMore");
+    pMAPIFreeBuffer = (void*)GetProcAddress(hMapi32, "MAPIFreeBuffer");
+    if(pScInitMapiUtil && pMAPIAllocateBuffer && pMAPIAllocateMore && pMAPIFreeBuffer)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+static ULONG ptTypes[] = {
+    PT_I2, PT_I4, PT_R4, PT_R8, PT_CURRENCY, PT_APPTIME, PT_SYSTIME,
+    PT_ERROR, PT_BOOLEAN, PT_I8, PT_CLSID, PT_STRING8, PT_BINARY,
+    PT_UNICODE
+};
+
+static inline int strcmpW(const WCHAR *str1, const WCHAR *str2)
+{
+    while (*str1 && (*str1 == *str2)) { str1++; str2++; }
+    return *str1 - *str2;
+}
+
+static void test_PropCopyMore(void)
+{
+    static char szHiA[] = "Hi!";
+    static WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
+    SPropValue *lpDest = NULL, *lpSrc = NULL;
+    ULONG i;
+    SCODE scode;
+
+    pPropCopyMore = (void*)GetProcAddress(hMapi32, "PropCopyMore@16");
+
+    if (!pPropCopyMore)
+        return;
+
+    scode = pMAPIAllocateBuffer(sizeof(LPSPropValue), (LPVOID *)lpDest);
+    if (FAILED(scode))
+        return;
+
+    scode = pMAPIAllocateMore(sizeof(LPSPropValue), lpDest, (LPVOID *)lpSrc);
+    if (FAILED(scode))
+        return;
+
+    for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
+    {
+        lpSrc->ulPropTag = ptTypes[i];
+
+        switch (ptTypes[i])
+        {
+        case PT_STRING8:
+            lpSrc->Value.lpszA = szHiA;
+            break;
+        case PT_UNICODE:
+            lpSrc->Value.lpszW = szHiW;
+            break;
+        case PT_BINARY:
+            lpSrc->Value.bin.cb = 4;
+            lpSrc->Value.bin.lpb = (LPBYTE)szHiA;
+            break;
+        }
+
+        memset(lpDest, 0xff, sizeof(SPropValue));
+
+        scode = pPropCopyMore(lpDest, lpSrc, (ALLOCATEMORE*)pMAPIAllocateMore, lpDest);
+        ok(!scode && lpDest->ulPropTag == lpSrc->ulPropTag,
+           "PropCopyMore: Expected 0x0,%d, got 0x%08x,%d\n",
+           lpSrc->ulPropTag, scode, lpDest->ulPropTag);
+        if (SUCCEEDED(scode))
+        {
+            switch (ptTypes[i])
+            {
+            case PT_STRING8:
+                ok(lstrcmpA(lpDest->Value.lpszA, lpSrc->Value.lpszA) == 0,
+                   "PropCopyMore: Ascii string differs\n");
+                break;
+            case PT_UNICODE:
+                ok(strcmpW(lpDest->Value.lpszW, lpSrc->Value.lpszW) == 0,
+                   "PropCopyMore: Unicode string differs\n");
+                break;
+            case PT_BINARY:
+                ok(lpDest->Value.bin.cb == 4 &&
+                   !memcmp(lpSrc->Value.bin.lpb, lpDest->Value.bin.lpb, 4),
+                   "PropCopyMore: Binary array  differs\n");
+                break;
+            }
+        }
+    }
+
+    /* Since all allocations are linked, freeing lpDest frees everything */
+    pMAPIFreeBuffer(lpDest);
+}
+
+static void test_UlPropSize(void)
+{
+    static char szHiA[] = "Hi!";
+    static WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
+    LPSTR  buffa[2];
+    LPWSTR buffw[2];
+    SBinary buffbin[2];
+    ULONG pt, exp, res;
+
+    pUlPropSize = (void*)GetProcAddress(hMapi32, "UlPropSize@4");
+
+    if (!pUlPropSize)
+        return;
+
+    for (pt = 0; pt < PROP_ID_INVALID; pt++)
+    {
+        SPropValue pv;
+
+        memset(&pv, 0 ,sizeof(pv));
+        pv.ulPropTag = pt;
+
+        exp = 1u; /* Default to one item for non-MV properties */
+
+        switch (PROP_TYPE(pt))
+        {
+        case PT_MV_I2:       pv.Value.MVi.cValues = exp = 2;
+        case PT_I2:          exp *= sizeof(USHORT); break;
+        case PT_MV_I4:       pv.Value.MVl.cValues = exp = 2;
+        case PT_I4:          exp *= sizeof(LONG); break;
+        case PT_MV_R4:       pv.Value.MVflt.cValues = exp = 2;
+        case PT_R4:          exp *= sizeof(float); break;
+        case PT_MV_DOUBLE:   pv.Value.MVdbl.cValues = exp = 2;
+        case PT_R8:          exp *= sizeof(double); break;
+        case PT_MV_CURRENCY: pv.Value.MVcur.cValues = exp = 2;
+        case PT_CURRENCY:    exp *= sizeof(CY); break;
+        case PT_MV_APPTIME:  pv.Value.MVat.cValues = exp = 2;
+        case PT_APPTIME:     exp *= sizeof(double); break;
+        case PT_MV_SYSTIME:  pv.Value.MVft.cValues = exp = 2;
+        case PT_SYSTIME:     exp *= sizeof(FILETIME); break;
+        case PT_ERROR:       exp = sizeof(SCODE); break;
+        case PT_BOOLEAN:     exp = sizeof(USHORT); break;
+        case PT_OBJECT:      exp = 0; break;
+        case PT_MV_I8:       pv.Value.MVli.cValues = exp = 2;
+        case PT_I8:          exp *= sizeof(LONG64); break;
+#if 0
+        /* My version of native mapi returns 0 for PT_MV_CLSID even if a valid
+         * array is given. This _has_ to be a bug, so Wine does
+         * the right thing(tm) and we don't test it here.
+         */
+        case PT_MV_CLSID:    pv.Value.MVguid.cValues = exp = 2;
+#endif
+        case PT_CLSID:       exp *= sizeof(GUID); break;
+        case PT_STRING8:
+            pv.Value.lpszA = szHiA;
+            exp = 4;
+            break;
+        case PT_UNICODE:
+            pv.Value.lpszW = szHiW;
+            exp = 4 * sizeof(WCHAR);
+            break;
+        case PT_BINARY:
+            pv.Value.bin.cb = exp = 19;
+            break;
+        case PT_MV_STRING8:
+            pv.Value.MVszA.cValues = 2;
+            pv.Value.MVszA.lppszA = buffa;
+            buffa[0] = szHiA;
+            buffa[1] = szHiA;
+            exp = 8;
+            break;
+        case PT_MV_UNICODE:
+            pv.Value.MVszW.cValues = 2;
+            pv.Value.MVszW.lppszW = buffw;
+            buffw[0] = szHiW;
+            buffw[1] = szHiW;
+            exp = 8 * sizeof(WCHAR);
+            break;
+        case PT_MV_BINARY:
+            pv.Value.MVbin.cValues = 2;
+            pv.Value.MVbin.lpbin = buffbin;
+            buffbin[0].cb = 19;
+            buffbin[1].cb = 1;
+            exp = 20;
+            break;
+        default:
+            exp = 0;
+        }
+
+        res = pUlPropSize(&pv);
+        ok(res == exp, "pt= %d: Expected %d, got %d\n", pt, exp, res);
+    }
+}
+
+static void test_FPropContainsProp(void)
+{
+    static char szFull[] = "Full String";
+    static char szFullLower[] = "full string";
+    static char szPrefix[] = "Full";
+    static char szPrefixLower[] = "full";
+    static char szSubstring[] = "ll St";
+    static char szSubstringLower[] = "ll st";
+    SPropValue pvLeft, pvRight;
+    ULONG pt;
+    BOOL bRet;
+
+    pFPropContainsProp = (void*)GetProcAddress(hMapi32, "FPropContainsProp@12");
+
+    if (!pFPropContainsProp)
+        return;
+
+    /* Ensure that only PT_STRING8 and PT_BINARY are handled */
+    for (pt = 0; pt < PROP_ID_INVALID; pt++)
+    {
+        if (pt == PT_STRING8 || pt == PT_BINARY)
+            continue; /* test these later */
+
+        memset(&pvLeft, 0 ,sizeof(pvLeft));
+        memset(&pvRight, 0 ,sizeof(pvRight));
+        pvLeft.ulPropTag = pvRight.ulPropTag = pt;
+
+        bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
+        ok(bRet == FALSE, "pt= %d: Expected FALSE, got %d\n", pt, bRet);
+    }
+
+    /* test the various flag combinations */
+    pvLeft.ulPropTag = pvRight.ulPropTag = PT_STRING8;
+    pvLeft.Value.lpszA = szFull;
+    pvRight.Value.lpszA = szFull;
+
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
+    ok(bRet == TRUE, "(full,full)[] match failed\n");
+    pvRight.Value.lpszA = szPrefix;
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
+    ok(bRet == FALSE, "(full,prefix)[] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == TRUE, "(full,prefix)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == TRUE, "(full,prefix)[SUBSTRING] match failed\n");
+    pvRight.Value.lpszA = szPrefixLower;
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == FALSE, "(full,prefixlow)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == FALSE, "(full,prefixlow)[SUBSTRING] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
+    ok(bRet == TRUE, "(full,prefixlow)[PREFIX|IGNORECASE] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
+    ok(bRet == TRUE, "(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n");
+    pvRight.Value.lpszA = szSubstring;
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
+    ok(bRet == FALSE, "(full,substr)[] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == FALSE, "(full,substr)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == TRUE, "(full,substr)[SUBSTRING] match failed\n");
+    pvRight.Value.lpszA = szSubstringLower;
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == FALSE, "(full,substrlow)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == FALSE, "(full,substrlow)[SUBSTRING] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
+    ok(bRet == FALSE, "(full,substrlow)[PREFIX|IGNORECASE] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
+    ok(bRet == TRUE, "(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n");
+    pvRight.Value.lpszA = szFullLower;
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE);
+    ok(bRet == TRUE, "(full,fulllow)[IGNORECASE] match failed\n");
+
+    pvLeft.ulPropTag = pvRight.ulPropTag = PT_BINARY;
+    pvLeft.Value.bin.lpb = (LPBYTE)szFull;
+    pvRight.Value.bin.lpb = (LPBYTE)szFull;
+    pvLeft.Value.bin.cb = pvRight.Value.bin.cb = strlen(szFull);
+
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
+    ok(bRet == TRUE, "bin(full,full)[] match failed\n");
+    pvRight.Value.bin.lpb = (LPBYTE)szPrefix;
+    pvRight.Value.bin.cb = strlen(szPrefix);
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
+    ok(bRet == FALSE, "bin(full,prefix)[] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == TRUE, "bin(full,prefix)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == TRUE, "bin(full,prefix)[SUBSTRING] match failed\n");
+    pvRight.Value.bin.lpb = (LPBYTE)szPrefixLower;
+    pvRight.Value.bin.cb = strlen(szPrefixLower);
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
+    ok(bRet == FALSE, "bin(full,prefixlow)[PREFIX|IGNORECASE] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
+    ok(bRet == FALSE, "bin(full,prefixlow)[SUBSTRING|IGNORECASE] match failed\n");
+    pvRight.Value.bin.lpb = (LPBYTE)szSubstring;
+    pvRight.Value.bin.cb = strlen(szSubstring);
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING);
+    ok(bRet == FALSE, "bin(full,substr)[] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == FALSE, "bin(full,substr)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == TRUE, "bin(full,substr)[SUBSTRING] match failed\n");
+    pvRight.Value.bin.lpb = (LPBYTE)szSubstringLower;
+    pvRight.Value.bin.cb = strlen(szSubstringLower);
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX);
+    ok(bRet == FALSE, "bin(full,substrlow)[PREFIX] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING);
+    ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_PREFIX|FL_IGNORECASE);
+    ok(bRet == FALSE, "bin(full,substrlow)[PREFIX|IGNORECASE] match failed\n");
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_SUBSTRING|FL_IGNORECASE);
+    ok(bRet == FALSE, "bin(full,substrlow)[SUBSTRING|IGNORECASE] match failed\n");
+    pvRight.Value.bin.lpb = (LPBYTE)szFullLower;
+    pvRight.Value.bin.cb = strlen(szFullLower);
+    bRet = pFPropContainsProp(&pvLeft, &pvRight, FL_FULLSTRING|FL_IGNORECASE);
+    ok(bRet == FALSE, "bin(full,fulllow)[IGNORECASE] match failed\n");
+}
+
+typedef struct tagFPropCompareProp_Result
+{
+    SHORT lVal;
+    SHORT rVal;
+    ULONG relOp;
+    BOOL  bRet;
+} FPropCompareProp_Result;
+
+static const FPropCompareProp_Result FPCProp_Results[] =
+{
+    { 1, 2, RELOP_LT, TRUE },
+    { 1, 1, RELOP_LT, FALSE },
+    { 2, 1, RELOP_LT, FALSE },
+    { 1, 2, RELOP_LE, TRUE },
+    { 1, 1, RELOP_LE, TRUE },
+    { 2, 1, RELOP_LE, FALSE },
+    { 1, 2, RELOP_GT, FALSE },
+    { 1, 1, RELOP_GT, FALSE },
+    { 2, 1, RELOP_GT, TRUE },
+    { 1, 2, RELOP_GE, FALSE },
+    { 1, 1, RELOP_GE, TRUE },
+    { 2, 1, RELOP_GE, TRUE },
+    { 1, 2, RELOP_EQ, FALSE },
+    { 1, 1, RELOP_EQ, TRUE },
+    { 2, 1, RELOP_EQ, FALSE }
+};
+
+static const char *relops[] = { "RELOP_LT", "RELOP_LE", "RELOP_GT", "RELOP_GE", "RELOP_EQ" };
+
+static void test_FPropCompareProp(void)
+{
+    SPropValue pvLeft, pvRight;
+    GUID lguid, rguid;
+    char lbuffa[2], rbuffa[2];
+    WCHAR lbuffw[2], rbuffw[2];
+    ULONG i, j;
+    BOOL bRet, bExp;
+
+    pFPropCompareProp = (void*)GetProcAddress(hMapi32, "FPropCompareProp@12");
+
+    if (!pFPropCompareProp)
+        return;
+
+    lbuffa[1] = '\0';
+    rbuffa[1] = '\0';
+    lbuffw[1] = '\0';
+    rbuffw[1] = '\0';
+
+    for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
+    {
+        pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i];
+
+        for (j = 0; j < sizeof(FPCProp_Results)/sizeof(FPCProp_Results[0]); j++)
+        {
+            SHORT lVal = FPCProp_Results[j].lVal;
+            SHORT rVal = FPCProp_Results[j].rVal;
+
+            bExp = FPCProp_Results[j].bRet;
+
+            switch (ptTypes[i])
+            {
+            case PT_BOOLEAN:
+                /* Boolean values have no concept of less or greater than, only equality */
+                if ((lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_LT) ||
+                    (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_LE)||
+                    (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_GT)||
+                    (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_GE)||
+                    (lVal == 1 && rVal == 2 && FPCProp_Results[j].relOp == RELOP_EQ)||
+                    (lVal == 2 && rVal == 1 && FPCProp_Results[j].relOp == RELOP_EQ))
+                    bExp = !bExp;
+                    /* Fall through ... */
+            case PT_I2:
+                pvLeft.Value.i = lVal;
+                pvRight.Value.i = rVal;
+                break;
+            case PT_ERROR:
+            case PT_I4:
+                pvLeft.Value.l = lVal;
+                pvRight.Value.l = rVal;
+                break;
+            case PT_R4:
+                pvLeft.Value.flt = lVal;
+                pvRight.Value.flt = rVal;
+                break;
+            case PT_APPTIME:
+            case PT_R8:
+                pvLeft.Value.dbl = lVal;
+                pvRight.Value.dbl = rVal;
+                break;
+            case PT_CURRENCY:
+                pvLeft.Value.cur.int64 = lVal;
+                pvRight.Value.cur.int64 = rVal;
+                break;
+            case PT_SYSTIME:
+                pvLeft.Value.ft.dwLowDateTime = lVal;
+                pvLeft.Value.ft.dwHighDateTime = 0;
+                pvRight.Value.ft.dwLowDateTime = rVal;
+                pvRight.Value.ft.dwHighDateTime = 0;
+                break;
+            case PT_I8:
+                pvLeft.Value.li.u.LowPart = lVal;
+                pvLeft.Value.li.u.HighPart = 0;
+                pvRight.Value.li.u.LowPart = rVal;
+                pvRight.Value.li.u.HighPart = 0;
+                break;
+            case PT_CLSID:
+                memset(&lguid, 0, sizeof(GUID));
+                memset(&rguid, 0, sizeof(GUID));
+                lguid.Data4[7] = lVal;
+                rguid.Data4[7] = rVal;
+                pvLeft.Value.lpguid = &lguid;
+                pvRight.Value.lpguid = &rguid;
+                break;
+            case PT_STRING8:
+                pvLeft.Value.lpszA = lbuffa;
+                pvRight.Value.lpszA = rbuffa;
+                lbuffa[0] = '0' + lVal;
+                rbuffa[0] = '0' + rVal;
+                break;
+            case PT_UNICODE:
+                pvLeft.Value.lpszW = lbuffw;
+                pvRight.Value.lpszW = rbuffw;
+                lbuffw[0] = '0' + lVal;
+                rbuffw[0] = '0' + rVal;
+                break;
+            case PT_BINARY:
+                pvLeft.Value.bin.cb = 1;
+                pvRight.Value.bin.cb = 1;
+                pvLeft.Value.bin.lpb = (LPBYTE)lbuffa;
+                pvRight.Value.bin.lpb = (LPBYTE)rbuffa;
+                lbuffa[0] = lVal;
+                rbuffa[0] = rVal;
+                break;
+            }
+
+            bRet = pFPropCompareProp(&pvLeft, FPCProp_Results[j].relOp, &pvRight);
+            ok(bRet == bExp, "pt %d (%d,%d,%s): expected %d, got %d\n", ptTypes[i],
+               FPCProp_Results[j].lVal, FPCProp_Results[j].rVal,
+               relops[FPCProp_Results[j].relOp], bExp, bRet);
+        }
+    }
+}
+
+typedef struct tagLPropCompareProp_Result
+{
+    SHORT lVal;
+    SHORT rVal;
+    INT   iRet;
+} LPropCompareProp_Result;
+
+static const LPropCompareProp_Result LPCProp_Results[] =
+{
+    { 1, 2, -1 },
+    { 1, 1, 0 },
+    { 2, 1, 1 },
+};
+
+static void test_LPropCompareProp(void)
+{
+    SPropValue pvLeft, pvRight;
+    GUID lguid, rguid;
+    char lbuffa[2], rbuffa[2];
+    WCHAR lbuffw[2], rbuffw[2];
+    ULONG i, j;
+    INT iRet, iExp;
+
+    pLPropCompareProp = (void*)GetProcAddress(hMapi32, "LPropCompareProp@8");
+
+    if (!pLPropCompareProp)
+        return;
+
+    lbuffa[1] = '\0';
+    rbuffa[1] = '\0';
+    lbuffw[1] = '\0';
+    rbuffw[1] = '\0';
+
+    for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
+    {
+        pvLeft.ulPropTag = pvRight.ulPropTag = ptTypes[i];
+
+        for (j = 0; j < sizeof(LPCProp_Results)/sizeof(LPCProp_Results[0]); j++)
+        {
+            SHORT lVal = LPCProp_Results[j].lVal;
+            SHORT rVal = LPCProp_Results[j].rVal;
+
+            iExp = LPCProp_Results[j].iRet;
+
+            switch (ptTypes[i])
+            {
+            case PT_BOOLEAN:
+                /* Boolean values have no concept of less or greater than, only equality */
+                if (lVal && rVal)
+                    iExp = 0;
+                    /* Fall through ... */
+            case PT_I2:
+                pvLeft.Value.i = lVal;
+                pvRight.Value.i = rVal;
+                break;
+            case PT_ERROR:
+            case PT_I4:
+                pvLeft.Value.l = lVal;
+                pvRight.Value.l = rVal;
+                break;
+            case PT_R4:
+                pvLeft.Value.flt = lVal;
+                pvRight.Value.flt = rVal;
+                break;
+            case PT_APPTIME:
+            case PT_R8:
+                pvLeft.Value.dbl = lVal;
+                pvRight.Value.dbl = rVal;
+                break;
+            case PT_CURRENCY:
+                pvLeft.Value.cur.int64 = lVal;
+                pvRight.Value.cur.int64 = rVal;
+                break;
+            case PT_SYSTIME:
+                pvLeft.Value.ft.dwLowDateTime = lVal;
+                pvLeft.Value.ft.dwHighDateTime = 0;
+                pvRight.Value.ft.dwLowDateTime = rVal;
+                pvRight.Value.ft.dwHighDateTime = 0;
+                break;
+            case PT_I8:
+                pvLeft.Value.li.u.LowPart = lVal;
+                pvLeft.Value.li.u.HighPart = 0;
+                pvRight.Value.li.u.LowPart = rVal;
+                pvRight.Value.li.u.HighPart = 0;
+                break;
+            case PT_CLSID:
+                memset(&lguid, 0, sizeof(GUID));
+                memset(&rguid, 0, sizeof(GUID));
+                lguid.Data4[7] = lVal;
+                rguid.Data4[7] = rVal;
+                pvLeft.Value.lpguid = &lguid;
+                pvRight.Value.lpguid = &rguid;
+                break;
+            case PT_STRING8:
+                pvLeft.Value.lpszA = lbuffa;
+                pvRight.Value.lpszA = rbuffa;
+                lbuffa[0] = '0' + lVal;
+                rbuffa[0] = '0' + rVal;
+                break;
+            case PT_UNICODE:
+                pvLeft.Value.lpszW = lbuffw;
+                pvRight.Value.lpszW = rbuffw;
+                lbuffw[0] = '0' + lVal;
+                rbuffw[0] = '0' + rVal;
+                break;
+            case PT_BINARY:
+                pvLeft.Value.bin.cb = 1;
+                pvRight.Value.bin.cb = 1;
+                pvLeft.Value.bin.lpb = (LPBYTE)lbuffa;
+                pvRight.Value.bin.lpb = (LPBYTE)rbuffa;
+                lbuffa[0] = lVal;
+                rbuffa[0] = rVal;
+                break;
+            }
+
+            iRet = pLPropCompareProp(&pvLeft, &pvRight);
+            ok(iRet == iExp, "pt %d (%d,%d): expected %d, got %d\n", ptTypes[i],
+               LPCProp_Results[j].lVal, LPCProp_Results[j].rVal, iExp, iRet);
+        }
+    }
+}
+
+static void test_PpropFindProp(void)
+{
+    SPropValue pvProp, *pRet;
+    ULONG i;
+
+    pPpropFindProp = (void*)GetProcAddress(hMapi32, "PpropFindProp@12");
+
+    if (!pPpropFindProp)
+        return;
+
+    for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
+    {
+        pvProp.ulPropTag = ptTypes[i];
+
+        pRet = pPpropFindProp(&pvProp, 1u, ptTypes[i]);
+        ok(pRet == &pvProp, "PpropFindProp[%d]: Didn't find existing propery\n",
+           ptTypes[i]);
+
+        pRet = pPpropFindProp(&pvProp, 1u, i ? ptTypes[i-1] : ptTypes[i+1]);
+        ok(pRet == NULL, "PpropFindProp[%d]: Found nonexistent propery\n",
+           ptTypes[i]);
+    }
+
+    pvProp.ulPropTag = PROP_TAG(PT_I2, 1u);
+    pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 0u));
+    ok(pRet == NULL, "PpropFindProp[UNSPECIFIED]: Matched on different id\n");
+    pRet = pPpropFindProp(&pvProp, 1u, PROP_TAG(PT_UNSPECIFIED, 1u));
+    ok(pRet == &pvProp, "PpropFindProp[UNSPECIFIED]: Didn't match id\n");
+}
+
+static void test_ScCountProps(void)
+{
+    static char szHiA[] = "Hi!";
+    static WCHAR szHiW[] = { 'H', 'i', '!', '\0' };
+    static const ULONG ULHILEN = 4; /* chars in szHiA/W incl. NUL */
+    LPSTR  buffa[3];
+    LPWSTR buffw[3];
+    SBinary buffbin[3];
+    GUID iids[4], *iid = iids;
+    SCODE res;
+    ULONG pt, exp, ulRet;
+    int success = 1;
+
+    pScCountProps = (void*)GetProcAddress(hMapi32, "ScCountProps@12");
+
+    if (!pScCountProps)
+        return;
+
+    for (pt = 0; pt < PROP_ID_INVALID && success; pt++)
+    {
+        SPropValue pv;
+
+        memset(&pv, 0 ,sizeof(pv));
+        pv.ulPropTag = PROP_TAG(pt, 1u);
+
+        switch (PROP_TYPE(pt))
+        {
+        case PT_I2:
+        case PT_I4:
+        case PT_R4:
+        case PT_R8:
+        case PT_CURRENCY:
+        case PT_APPTIME:
+        case PT_SYSTIME:
+        case PT_ERROR:
+        case PT_BOOLEAN:
+        case PT_OBJECT:
+        case PT_I8:
+            exp = sizeof(pv);
+            break;
+        case PT_CLSID:
+            pv.Value.lpguid = iid;
+            exp = sizeof(GUID) + sizeof(pv);
+            break;
+        case PT_STRING8:
+            pv.Value.lpszA = szHiA;
+            exp = 4 + sizeof(pv);
+            break;
+        case PT_UNICODE:
+            pv.Value.lpszW = szHiW;
+            exp = 4 * sizeof(WCHAR) + sizeof(pv);
+            break;
+        case PT_BINARY:
+            pv.Value.bin.cb = 2;
+            pv.Value.bin.lpb = (LPBYTE)iid;
+            exp = 2 + sizeof(pv);
+            break;
+        case PT_MV_I2:
+            pv.Value.MVi.cValues = 3;
+            pv.Value.MVi.lpi = (SHORT*)iid;
+            exp = 3 * sizeof(SHORT) + sizeof(pv);
+            break;
+        case PT_MV_I4:
+            pv.Value.MVl.cValues = 3;
+            pv.Value.MVl.lpl = (LONG*)iid;
+            exp = 3 * sizeof(LONG) + sizeof(pv);
+            break;
+        case PT_MV_I8:
+            pv.Value.MVli.cValues = 3;
+            pv.Value.MVli.lpli = (LARGE_INTEGER*)iid;
+            exp = 3 * sizeof(LARGE_INTEGER) + sizeof(pv);
+            break;
+        case PT_MV_R4:
+            pv.Value.MVflt.cValues = 3;
+            pv.Value.MVflt.lpflt = (float*)iid;
+            exp = 3 * sizeof(float) + sizeof(pv);
+            break;
+        case PT_MV_APPTIME:
+        case PT_MV_R8:
+            pv.Value.MVdbl.cValues = 3;
+            pv.Value.MVdbl.lpdbl = (double*)iid;
+            exp = 3 * sizeof(double) + sizeof(pv);
+            break;
+        case PT_MV_CURRENCY:
+            pv.Value.MVcur.cValues = 3;
+            pv.Value.MVcur.lpcur = (CY*)iid;
+            exp = 3 * sizeof(CY) + sizeof(pv);
+            break;
+        case PT_MV_SYSTIME:
+            pv.Value.MVft.cValues = 3;
+            pv.Value.MVft.lpft = (FILETIME*)iid;
+            exp = 3 * sizeof(CY) + sizeof(pv);
+            break;
+        case PT_MV_STRING8:
+            pv.Value.MVszA.cValues = 3;
+            pv.Value.MVszA.lppszA = buffa;
+            buffa[0] = szHiA;
+            buffa[1] = szHiA;
+            buffa[2] = szHiA;
+            exp = ULHILEN * 3 + 3 * sizeof(char*) + sizeof(pv);
+            break;
+        case PT_MV_UNICODE:
+            pv.Value.MVszW.cValues = 3;
+            pv.Value.MVszW.lppszW = buffw;
+            buffw[0] = szHiW;
+            buffw[1] = szHiW;
+            buffw[2] = szHiW;
+            exp = ULHILEN * 3 * sizeof(WCHAR) + 3 * sizeof(WCHAR*) + sizeof(pv);
+            break;
+        case PT_MV_BINARY:
+            pv.Value.MVbin.cValues = 3;
+            pv.Value.MVbin.lpbin = buffbin;
+            buffbin[0].cb = 17;
+            buffbin[0].lpb = (LPBYTE)&iid;
+            buffbin[1].cb = 2;
+            buffbin[1].lpb = (LPBYTE)&iid;
+            buffbin[2].cb = 1;
+            buffbin[2].lpb = (LPBYTE)&iid;
+            exp = 20 + sizeof(pv) + sizeof(SBinary) * 3;
+            break;
+        default:
+            exp = 0;
+        }
+
+        ulRet = 0xffffffff;
+        res = pScCountProps(1, &pv, &ulRet);
+        if (!exp) {
+            success = res == MAPI_E_INVALID_PARAMETER && ulRet == 0xffffffff;
+            ok(success, "pt= %d: Expected failure, got %d, ret=0x%08X\n",
+               pt, ulRet, res);
+        }
+        else {
+            success = res == S_OK && ulRet == exp;
+            ok(success, "pt= %d: Expected %d, got %d, ret=0x%08X\n",
+               pt, exp, ulRet, res);
+        }
+    }
+
+}
+
+static void test_ScCopyRelocProps(void)
+{
+    static char szTestA[] = "Test";
+    char buffer[512], buffer2[512], *lppszA[1];
+    SPropValue pvProp, *lpResProp = (LPSPropValue)buffer;
+    ULONG ulCount;
+    SCODE sc;
+
+    pScCopyProps = (void*)GetProcAddress(hMapi32, "ScCopyProps@16");
+    pScRelocProps = (void*)GetProcAddress(hMapi32, "ScRelocProps@20");
+
+    if (!pScCopyProps || !pScRelocProps)
+        return;
+
+    pvProp.ulPropTag = PROP_TAG(PT_MV_STRING8, 1u);
+
+    lppszA[0] = szTestA;
+    pvProp.Value.MVszA.cValues = 1;
+    pvProp.Value.MVszA.lppszA = lppszA;
+    ulCount = 0;
+
+    sc = pScCopyProps(1, &pvProp, buffer, &ulCount);
+    ok(sc == S_OK, "wrong ret %d\n", sc);
+    ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %x\n",lpResProp->ulPropTag);
+    ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %d\n", lpResProp->Value.MVszA.cValues);
+    ok(lpResProp->Value.MVszA.lppszA[0] == buffer + sizeof(SPropValue) + sizeof(char*),
+       "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]);
+    ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5, "wrong count %d\n", ulCount);
+    ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
+       "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]);
+
+    memcpy(buffer2, buffer, sizeof(buffer));
+
+    /* Clear the data in the source buffer. Since pointers in the copied buffer
+     * refer to the source buffer, this proves that native always assumes that
+     * the copied buffers pointers are bad (needing to be relocated first).
+     */
+    memset(buffer, 0, sizeof(buffer));
+    ulCount = 0;
+
+    sc = pScRelocProps(1, (LPSPropValue)buffer2, buffer, buffer2, &ulCount);
+    lpResProp = (LPSPropValue)buffer2;
+
+    ok(sc == S_OK, "wrong ret %d\n", sc);
+    ok(lpResProp->ulPropTag == pvProp.ulPropTag, "wrong tag %x\n",lpResProp->ulPropTag);
+    ok(lpResProp->Value.MVszA.cValues == 1, "wrong cValues %d\n", lpResProp->Value.MVszA.cValues);
+    ok(lpResProp->Value.MVszA.lppszA[0] == buffer2 + sizeof(SPropValue) + sizeof(char*),
+       "wrong lppszA[0] %p\n",lpResProp->Value.MVszA.lppszA[0]);
+    /* Native has a bug whereby it calculates the size correctly when copying
+     * but when relocating does not (presumably it uses UlPropSize() which
+     * ignores multivalue pointers). Wine returns the correct value.
+     */
+    ok(ulCount == sizeof(SPropValue) + sizeof(char*) + 5 || ulCount == sizeof(SPropValue) + 5,
+       "wrong count %d\n", ulCount);
+    ok(!strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA),
+       "wrong string '%s'\n", lpResProp->Value.MVszA.lppszA[0]);
+
+    /* Native crashes with lpNew or lpOld set to NULL so skip testing this */
+}
+
+static void test_LpValFindProp(void)
+{
+    SPropValue pvProp, *pRet;
+    ULONG i;
+
+    pLpValFindProp = (void*)GetProcAddress(hMapi32, "LpValFindProp@12");
+
+    if (!pLpValFindProp)
+        return;
+
+    for (i = 0; i < sizeof(ptTypes)/sizeof(ptTypes[0]); i++)
+    {
+        pvProp.ulPropTag = PROP_TAG(ptTypes[i], 1u);
+
+        pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 1u), 1u, &pvProp);
+        ok(pRet == &pvProp, "LpValFindProp[%d]: Didn't find existing propery id/type\n",
+           ptTypes[i]);
+
+        pRet = pLpValFindProp(PROP_TAG(ptTypes[i], 0u), 1u, &pvProp);
+        ok(pRet == NULL, "LpValFindProp[%d]: Found nonexistent propery id\n",
+           ptTypes[i]);
+
+        pRet = pLpValFindProp(PROP_TAG(PT_NULL, 0u), 1u, &pvProp);
+        ok(pRet == NULL, "LpValFindProp[%d]: Found nonexistent propery id/type\n",
+           ptTypes[i]);
+
+        pRet = pLpValFindProp(PROP_TAG(PT_NULL, 1u), 1u, &pvProp);
+        ok(pRet == &pvProp, "LpValFindProp[%d]: Didn't find existing propery id\n",
+           ptTypes[i]);
+    }
+}
+
+static void test_FBadRglpszA(void)
+{
+    LPSTR lpStrs[4];
+    static CHAR szString[] = "A String";
+    BOOL bRet;
+
+    pFBadRglpszA = (void*)GetProcAddress(hMapi32, "FBadRglpszA@8");
+    if (!pFBadRglpszA)
+        return;
+
+    bRet = pFBadRglpszA(NULL, 10);
+    ok(bRet == TRUE, "FBadRglpszA(Null): expected TRUE, got FALSE\n");
+
+    lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
+    bRet = pFBadRglpszA(lpStrs, 4);
+    ok(bRet == TRUE, "FBadRglpszA(Nulls): expected TRUE, got FALSE\n");
+
+    lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
+    bRet = pFBadRglpszA(lpStrs, 3);
+    ok(bRet == FALSE, "FBadRglpszA(valid): expected FALSE, got TRUE\n");
+
+    bRet = pFBadRglpszA(lpStrs, 4);
+    ok(bRet == TRUE, "FBadRglpszA(1 invalid): expected TRUE, got FALSE\n");
+}
+
+static void test_FBadRglpszW(void)
+{
+    LPWSTR lpStrs[4];
+    static WCHAR szString[] = { 'A',' ','S','t','r','i','n','g','\0' };
+    BOOL bRet;
+
+    pFBadRglpszW = (void*)GetProcAddress(hMapi32, "FBadRglpszW@8");
+    if (!pFBadRglpszW)
+        return;
+
+    bRet = pFBadRglpszW(NULL, 10);
+    ok(bRet == TRUE, "FBadRglpszW(Null): expected TRUE, got FALSE\n");
+
+    lpStrs[0] = lpStrs[1] = lpStrs[2] = lpStrs[3] = NULL;
+    bRet = pFBadRglpszW(lpStrs, 4);
+    ok(bRet == TRUE, "FBadRglpszW(Nulls): expected TRUE, got FALSE\n");
+
+    lpStrs[0] = lpStrs[1] = lpStrs[2] = szString;
+    bRet = pFBadRglpszW(lpStrs, 3);
+    ok(bRet == FALSE, "FBadRglpszW(valid): expected FALSE, got TRUE\n");
+
+    bRet = pFBadRglpszW(lpStrs, 4);
+    ok(bRet == TRUE, "FBadRglpszW(1 invalid): expected TRUE, got FALSE\n");
+}
+
+static void test_FBadRowSet(void)
+{
+    ULONG ulRet;
+
+    pFBadRowSet = (void*)GetProcAddress(hMapi32, "FBadRowSet@4");
+    if (!pFBadRowSet)
+        return;
+
+    ulRet = pFBadRowSet(NULL);
+    ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
+
+    /* FIXME */
+}
+
+static void test_FBadPropTag(void)
+{
+    ULONG pt, res;
+
+    pFBadPropTag = (void*)GetProcAddress(hMapi32, "FBadPropTag@4");
+    if (!pFBadPropTag)
+        return;
+
+    for (pt = 0; pt < PROP_ID_INVALID; pt++)
+    {
+        BOOL bBad = TRUE;
+
+        switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
+        {
+        case PT_UNSPECIFIED:
+        case PT_NULL: case PT_I2: case PT_I4: case PT_R4:
+        case PT_R8: case PT_CURRENCY: case PT_APPTIME:
+        case PT_ERROR: case PT_BOOLEAN: case PT_OBJECT:
+        case PT_I8: case PT_STRING8: case PT_UNICODE:
+        case PT_SYSTIME: case PT_CLSID: case PT_BINARY:
+            bBad = FALSE;
+        }
+
+        res = pFBadPropTag(pt);
+        if (bBad)
+            ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt);
+        else
+            ok(res == 0, "pt= %d: Expected zero, got %d\n", pt, res);
+    }
+}
+
+static void test_FBadRow(void)
+{
+    ULONG ulRet;
+
+    pFBadRow = (void*)GetProcAddress(hMapi32, "FBadRow@4");
+    if (!pFBadRow)
+        return;
+
+    ulRet = pFBadRow(NULL);
+    ok(ulRet != 0, "FBadRow(null): Expected non-zero, got 0\n");
+
+    /* FIXME */
+}
+
+static void test_FBadProp(void)
+{
+    static WCHAR szEmpty[] = { '\0' };
+    GUID iid;
+    ULONG pt, res;
+    SPropValue pv;
+
+    pFBadProp = (void*)GetProcAddress(hMapi32, "FBadProp@4");
+    if (!pFBadProp)
+        return;
+
+    for (pt = 0; pt < PROP_ID_INVALID; pt++)
+    {
+        BOOL bBad = TRUE;
+
+        memset(&pv, 0, sizeof(pv));
+        pv.ulPropTag = pt;
+
+        /* Note that MV values are valid below because their array count is 0,
+         * so no pointers are validated.
+         */
+        switch (PROP_TYPE(pt))
+        {
+        case (MV_FLAG|PT_UNSPECIFIED):
+        case PT_UNSPECIFIED:
+        case (MV_FLAG|PT_NULL):
+        case PT_NULL:
+        case PT_MV_I2:
+        case PT_I2:
+        case PT_MV_I4:
+        case PT_I4:
+        case PT_MV_I8:
+        case PT_I8:
+        case PT_MV_R4:
+        case PT_R4:
+        case PT_MV_R8:
+        case PT_R8:
+        case PT_MV_CURRENCY:
+        case PT_CURRENCY:
+        case PT_MV_APPTIME:
+        case PT_APPTIME:
+        case (MV_FLAG|PT_ERROR):
+        case PT_ERROR:
+        case (MV_FLAG|PT_BOOLEAN):
+        case PT_BOOLEAN:
+        case (MV_FLAG|PT_OBJECT):
+        case PT_OBJECT:
+        case PT_MV_STRING8:
+        case PT_MV_UNICODE:
+        case PT_MV_SYSTIME:
+        case PT_SYSTIME:
+        case PT_MV_BINARY:
+        case PT_BINARY:
+        case PT_MV_CLSID:
+            bBad = FALSE;
+            break;
+        case PT_STRING8:
+        case PT_UNICODE:
+            pv.Value.lpszW = szEmpty;
+            bBad = FALSE;
+            break;
+        case PT_CLSID:
+            pv.Value.lpguid = &iid;
+            bBad = FALSE;
+            break;
+        }
+
+        res = pFBadProp(&pv);
+        if (bBad)
+            ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt);
+        else
+            ok(res == 0, "pt= %d: Expected zero, got %d\n", pt, res);
+    }
+}
+
+static void test_FBadColumnSet(void)
+{
+    SPropTagArray pta;
+    ULONG pt, res;
+
+    pFBadColumnSet = (void*)GetProcAddress(hMapi32, "FBadColumnSet@4");
+    if (!pFBadColumnSet)
+        return;
+
+    res = pFBadColumnSet(NULL);
+    ok(res != 0, "(null): Expected non-zero, got 0\n");
+
+    pta.cValues = 1;
+
+    for (pt = 0; pt < PROP_ID_INVALID; pt++)
+    {
+        BOOL bBad = TRUE;
+
+        pta.aulPropTag[0] = pt;
+
+        switch (pt & (~MV_FLAG & PROP_TYPE_MASK))
+        {
+        case PT_UNSPECIFIED:
+        case PT_NULL:
+        case PT_I2:
+        case PT_I4:
+        case PT_R4:
+        case PT_R8:
+        case PT_CURRENCY:
+        case PT_APPTIME:
+        case PT_BOOLEAN:
+        case PT_OBJECT:
+        case PT_I8:
+        case PT_STRING8:
+        case PT_UNICODE:
+        case PT_SYSTIME:
+        case PT_CLSID:
+        case PT_BINARY:
+            bBad = FALSE;
+        }
+        if (pt == (MV_FLAG|PT_ERROR))
+            bBad = FALSE;
+
+        res = pFBadColumnSet(&pta);
+        if (bBad)
+            ok(res != 0, "pt= %d: Expected non-zero, got 0\n", pt);
+        else
+            ok(res == 0, "pt= %d: Expected zero, got %d\n", pt, res);
+    }
+}
+
+
+static void test_IProp(void)
+{
+    IPropData *lpIProp;
+    LPMAPIERROR lpError;
+    LPSPropProblemArray lpProbs;
+    LPSPropValue lpProps;
+    LPSPropTagArray lpTags;
+    SPropValue pvs[2];
+    SizedSPropTagArray(2,tags);
+    ULONG access[2], count;
+    SCODE sc;
+
+    pCreateIProp = (void*)GetProcAddress(hMapi32, "CreateIProp@24");
+
+    if (!pCreateIProp)
+        return;
+
+    memset(&tags, 0 , sizeof(tags));
+
+    /* Create the object */
+    lpIProp = NULL;
+    sc = pCreateIProp(&IID_IMAPIPropData, (ALLOCATEBUFFER *)pMAPIAllocateBuffer, (ALLOCATEMORE*)pMAPIAllocateMore,
+                      (FREEBUFFER *)pMAPIFreeBuffer, NULL, &lpIProp);
+    ok(sc == S_OK && lpIProp,
+       "CreateIProp: expected S_OK, non-null, got 0x%08X,%p\n", sc, lpIProp);
+
+    if (sc != S_OK || !lpIProp)
+        return;
+
+    /* GetLastError - No errors set */
+    lpError = NULL;
+    IPropData_GetLastError(lpIProp, E_INVALIDARG, 0, &lpError);
+    ok(sc == S_OK && !lpError,
+       "GetLastError: Expected S_OK, null, got 0x%08X,%p\n", sc, lpError);
+
+    /* Get prop tags - succeeds returning 0 items */
+    lpTags = NULL;
+    sc = IPropData_GetPropList(lpIProp, 0, &lpTags);
+    ok(sc == S_OK && lpTags && lpTags->cValues == 0,
+       "GetPropList(empty): Expected S_OK, non-null, 0, got 0x%08X,%p,%d\n",
+        sc, lpTags, lpTags ? lpTags->cValues : 0);
+    if (lpTags)
+        pMAPIFreeBuffer(lpTags);
+
+    /* Get props - succeeds returning 0 items */
+    lpProps = NULL;
+    count = 0;
+    tags.cValues = 1;
+    tags.aulPropTag[0] = PR_IMPORTANCE;
+    sc = IPropData_GetProps(lpIProp, (LPSPropTagArray)&tags, 0, &count, &lpProps);
+    ok(sc == MAPI_W_ERRORS_RETURNED && lpProps && count == 1,
+       "GetProps(empty): Expected ERRORS_RETURNED, non-null, 1, got 0x%08X,%p,%d\n",
+       sc, lpProps, count);
+    if (lpProps && count > 0)
+    {
+        ok(lpProps[0].ulPropTag == CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR),
+           "GetProps(empty): Expected %x, got %x\n",
+           CHANGE_PROP_TYPE(PR_IMPORTANCE,PT_ERROR), lpProps[0].ulPropTag);
+
+        pMAPIFreeBuffer(lpProps);
+    }
+
+    /* Add (NULL) - Can't add NULLs */
+    lpProbs = NULL;
+    pvs[0].ulPropTag = PROP_TAG(PT_NULL,0x01);
+    sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
+    ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
+       "SetProps(): Expected INVALID_PARAMETER, null, got 0x%08X,%p\n",
+       sc, lpProbs);
+
+    /* Add (OBJECT) - Can't add OBJECTs */
+    lpProbs = NULL;
+    pvs[0].ulPropTag = PROP_TAG(PT_OBJECT,0x01);
+    sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
+    ok(sc == MAPI_E_INVALID_PARAMETER && !lpProbs,
+       "SetProps(OBJECT): Expected INVALID_PARAMETER, null, got 0x%08X,%p\n",
+       sc, lpProbs);
+
+    /* Add - Adds value */
+    lpProbs = NULL;
+    pvs[0].ulPropTag = PR_IMPORTANCE;
+    sc = IPropData_SetProps(lpIProp, 1, pvs, &lpProbs);
+    ok(sc == S_OK && !lpProbs,
+       "SetProps(ERROR): Expected S_OK, null, got 0x%08X,%p\n", sc, lpProbs);
+
+    /* Get prop list - returns 1 item */
+    lpTags = NULL;
+    IPropData_GetPropList(lpIProp, 0, &lpTags);
+    ok(sc == S_OK && lpTags && lpTags->cValues == 1,
+       "GetPropList: Expected S_OK, non-null, 1, got 0x%08X,%p,%d\n",
+        sc, lpTags, lpTags ? lpTags->cValues : 0);
+    if (lpTags && lpTags->cValues > 0)
+    {
+        ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
+           "GetPropList: Expected %x, got %x\n",
+           PR_IMPORTANCE, lpTags->aulPropTag[0]);
+        pMAPIFreeBuffer(lpTags);
+    }
+
+    /* Set access to read and write */
+    sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
+    ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
+
+    tags.cValues = 1;
+    tags.aulPropTag[0] = PR_IMPORTANCE;
+
+    /* Set item access (bad access) - Fails */
+    access[0] = 0;
+    sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
+    ok(sc == MAPI_E_INVALID_PARAMETER,
+       "SetPropAcess(0): Expected INVALID_PARAMETER got 0x%08X\n",sc);
+    access[0] = IPROP_READWRITE;
+    sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
+    ok(sc == MAPI_E_INVALID_PARAMETER,
+       "SetPropAcess(RW): Expected INVALID_PARAMETER got 0x%08X\n",sc);
+    access[0] = IPROP_CLEAN;
+    sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
+    ok(sc == MAPI_E_INVALID_PARAMETER,
+       "SetPropAcess(C): Expected INVALID_PARAMETER got 0x%08X\n",sc);
+
+    /* Set item access to read/write/clean */
+    tags.cValues = 1;
+    tags.aulPropTag[0] = PR_IMPORTANCE;
+    access[0] = IPROP_READWRITE|IPROP_CLEAN;
+    sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
+    ok(sc == S_OK, "SetPropAcess(RW/C): Expected S_OK got 0x%08X\n",sc);
+
+    /* Set object access to read only */
+    sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READONLY);
+    ok(sc == S_OK, "SetObjAcess(READ): Expected S_OK got 0x%08X\n", sc);
+
+    /* Set item access to read/write/dirty - doesn't care about RO object */
+    access[0] = IPROP_READONLY|IPROP_DIRTY;
+    sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
+    ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
+
+    /* Delete any item when set to read only - Error */
+    lpProbs = NULL;
+    tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
+    sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
+    ok(sc == E_ACCESSDENIED && !lpProbs,
+       "DeleteProps(nonexistent): Expected E_ACCESSDENIED null got 0x%08X %p\n",
+       sc, lpProbs);
+
+    /* Set access to read and write */
+    sc = IPropData_HrSetObjAccess(lpIProp, IPROP_READWRITE);
+    ok(sc == S_OK, "SetObjAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
+
+    /* Delete nonexistent item - No error */
+    lpProbs = NULL;
+    tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
+    sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
+    ok(sc == S_OK && !lpProbs,
+       "DeleteProps(nonexistent): Expected S_OK null got 0x%08X %p\n",
+       sc, lpProbs);
+
+    /* Delete existing item (r/o) - No error, but lpProbs populated */
+    lpProbs = NULL;
+    tags.aulPropTag[0] = PR_IMPORTANCE;
+    sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
+    ok(sc == S_OK && lpProbs,
+       "DeleteProps(RO): Expected S_OK non-null got 0x%08X %p\n", sc, lpProbs);
+
+    if (lpProbs && lpProbs->cProblem > 0)
+    {
+        ok(lpProbs->cProblem == 1 &&
+           lpProbs->aProblem[0].ulIndex == 0 &&
+           lpProbs->aProblem[0].ulPropTag == PR_IMPORTANCE &&
+           lpProbs->aProblem[0].scode == E_ACCESSDENIED,
+           "DeleteProps(RO): Expected (1,0,%x,%x) got (%d,%x,%x)\n",
+            PR_IMPORTANCE, E_ACCESSDENIED,
+            lpProbs->aProblem[0].ulIndex, lpProbs->aProblem[0].ulPropTag,
+            lpProbs->aProblem[0].scode);
+        pMAPIFreeBuffer(lpProbs);
+    }
+
+    lpProbs = NULL;
+    tags.cValues = 1;
+    tags.aulPropTag[0] = PR_RESPONSE_REQUESTED;
+    IPropData_HrAddObjProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
+    ok(sc == S_OK && !lpProbs,
+       "AddObjProps(RO): Expected S_OK null got 0x%08X %p\n", sc, lpProbs);
+
+    /* Get prop list - returns 1 item */
+    lpTags = NULL;
+    IPropData_GetPropList(lpIProp, 0, &lpTags);
+    ok(sc == S_OK && lpTags && lpTags->cValues == 1,
+       "GetPropList: Expected S_OK, non-null, 1, got 0x%08X,%p,%d\n",
+        sc, lpTags, lpTags ? lpTags->cValues : 0);
+    if (lpTags && lpTags->cValues > 0)
+    {
+        ok(lpTags->aulPropTag[0] == PR_IMPORTANCE,
+           "GetPropList: Expected %x, got %x\n",
+           PR_IMPORTANCE, lpTags->aulPropTag[0]);
+        pMAPIFreeBuffer(lpTags);
+    }
+
+    /* Set item to r/w again */
+    access[0] = IPROP_READWRITE|IPROP_DIRTY;
+    sc = IPropData_HrSetPropAccess(lpIProp, (LPSPropTagArray)&tags, access);
+    ok(sc == S_OK, "SetPropAcess(WRITE): Expected S_OK got 0x%08X\n", sc);
+
+    /* Delete existing item (r/w) - No error, no problems */
+    lpProbs = NULL;
+    sc = IPropData_DeleteProps(lpIProp, (LPSPropTagArray)&tags, &lpProbs);
+    ok(sc == S_OK && !lpProbs,
+       "DeleteProps(RO): Expected S_OK null got 0x%08X %p\n", sc, lpProbs);
+
+    /* Free the list */
+    IPropData_Release(lpIProp);
+}
+
+START_TEST(prop)
+{
+    SCODE ret;
+
+    if(!InitFuncPtrs())
+    {
+        skip("Needed functions are not available\n");
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = pScInitMapiUtil(0);
+    if ((ret != S_OK) && (GetLastError() == ERROR_PROC_NOT_FOUND))
+    {
+        skip("ScInitMapiUtil is not implemented\n");
+        FreeLibrary(hMapi32);
+        return;
+    }
+
+    test_PropCopyMore();
+    test_UlPropSize();
+    test_FPropContainsProp();
+    test_FPropCompareProp();
+    test_LPropCompareProp();
+    test_PpropFindProp();
+    test_ScCountProps();
+    test_ScCopyRelocProps();
+    test_LpValFindProp();
+    test_FBadRglpszA();
+    test_FBadRglpszW();
+    test_FBadRowSet();
+    test_FBadPropTag();
+    test_FBadRow();
+    test_FBadProp();
+    test_FBadColumnSet();
+
+    test_IProp();
+    FreeLibrary(hMapi32);
+}
diff --git a/rostests/winetests/mapi32/testlist.c b/rostests/winetests/mapi32/testlist.c
new file mode 100644 (file)
index 0000000..97ef6c9
--- /dev/null
@@ -0,0 +1,19 @@
+/* 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 }
+};
diff --git a/rostests/winetests/mapi32/util.c b/rostests/winetests/mapi32/util.c
new file mode 100644 (file)
index 0000000..2ae42f1
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Unit test suite for MAPI utility functions
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winnt.h"
+#include "mapiutil.h"
+#include "mapitags.h"
+
+static HMODULE hMapi32 = 0;
+
+static SCODE (WINAPI *pScInitMapiUtil)(ULONG);
+static void  (WINAPI *pSwapPword)(PUSHORT,ULONG);
+static void  (WINAPI *pSwapPlong)(PULONG,ULONG);
+static void  (WINAPI *pHexFromBin)(LPBYTE,int,LPWSTR);
+static void  (WINAPI *pFBinFromHex)(LPWSTR,LPBYTE);
+static UINT  (WINAPI *pUFromSz)(LPCSTR);
+static ULONG (WINAPI *pUlFromSzHex)(LPCSTR);
+static ULONG (WINAPI *pCbOfEncoded)(LPCSTR);
+static BOOL  (WINAPI *pIsBadBoundedStringPtr)(LPCSTR,ULONG);
+
+static void test_SwapPword(void)
+{
+    USHORT shorts[3];
+
+    pSwapPword = (void*)GetProcAddress(hMapi32, "SwapPword@8");
+    if (!pSwapPword)
+        return;
+
+    shorts[0] = 0xff01;
+    shorts[1] = 0x10ff;
+    shorts[2] = 0x2001;
+    pSwapPword(shorts, 2);
+    ok(shorts[0] == 0x01ff && shorts[1] == 0xff10 && shorts[2] == 0x2001,
+       "Expected {0x01ff,0xff10,0x2001}, got {0x%04x,0x%04x,0x%04x}\n",
+       shorts[0], shorts[1], shorts[2]);
+}
+
+static void test_SwapPlong(void)
+{
+    ULONG longs[3];
+
+    pSwapPlong = (void*)GetProcAddress(hMapi32, "SwapPlong@8");
+    if (!pSwapPlong)
+        return;
+
+    longs[0] = 0xffff0001;
+    longs[1] = 0x1000ffff;
+    longs[2] = 0x20000001;
+    pSwapPlong(longs, 2);
+    ok(longs[0] == 0x0100ffff && longs[1] == 0xffff0010 && longs[2] == 0x20000001,
+       "Expected {0x0100ffff,0xffff0010,0x20000001}, got {0x%08x,0x%08x,0x%08x}\n",
+       longs[0], longs[1], longs[2]);
+}
+
+static void test_HexFromBin(void)
+{
+    static char res[] =       { "000102030405060708090A0B0C0D0E0F101112131415161"
+      "718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B"
+      "3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F6"
+      "06162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F8081828384"
+      "85868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A"
+      "9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCD"
+      "CECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F"
+      "2F3F4F5F6F7F8F9FAFBFCFDFE\0X" };
+    BYTE data[255];
+    WCHAR strw[256];
+    BOOL bOk;
+    int i;
+
+    pHexFromBin = (void*)GetProcAddress(hMapi32, "HexFromBin@12");
+    pFBinFromHex = (void*)GetProcAddress(hMapi32, "FBinFromHex@8");
+    if (!pHexFromBin || !pFBinFromHex)
+        return;
+
+    for (i = 0; i < 255; i++)
+        data[i] = i;
+    memset(strw, 'X', sizeof(strw));
+    pHexFromBin(data, sizeof(data), strw);
+
+    ok(memcmp(strw, res, sizeof(res) - 1) == 0, "HexFromBin: Result differs\n");
+
+    memset(data, 0, sizeof(data));
+    pFBinFromHex((LPWSTR)res, data);
+    bOk = TRUE;
+    for (i = 0; i < 255; i++)
+        if (data[i] != i)
+            bOk = FALSE;
+    ok(bOk == TRUE, "FBinFromHex: Result differs\n");
+}
+
+static void test_UFromSz(void)
+{
+    pUFromSz = (void*)GetProcAddress(hMapi32, "UFromSz@4");
+    if (!pUFromSz)
+        return;
+
+    ok(pUFromSz("105679") == 105679u,
+       "UFromSz: expected 105679, got %d\n", pUFromSz("105679"));
+
+    ok(pUFromSz(" 4") == 0, "UFromSz: exected 0. got %d\n",
+       pUFromSz(" 4"));
+}
+
+static void test_UlFromSzHex(void)
+{
+    pUlFromSzHex = (void*)GetProcAddress(hMapi32, "UlFromSzHex@4");
+    if (!pUlFromSzHex)
+        return;
+
+    ok(pUlFromSzHex("fF") == 0xffu,
+       "UlFromSzHex: expected 0xff, got 0x%x\n", pUlFromSzHex("fF"));
+
+    ok(pUlFromSzHex(" c") == 0, "UlFromSzHex: exected 0x0. got 0x%x\n",
+       pUlFromSzHex(" c"));
+}
+
+static void test_CbOfEncoded(void)
+{
+    char buff[129];
+    unsigned int i;
+
+    pCbOfEncoded = (void*)GetProcAddress(hMapi32, "CbOfEncoded@4");
+    if (!pCbOfEncoded)
+        return;
+
+    for (i = 0; i < sizeof(buff) - 1; i++)
+    {
+        ULONG ulRet, ulExpected = (((i | 3) >> 2) + 1) * 3;
+
+        memset(buff, '\0', sizeof(buff));
+        memset(buff, '?', i);
+        ulRet = pCbOfEncoded(buff);
+        ok(ulRet == ulExpected, "CbOfEncoded(length %d): expected %d, got %d\n",
+           i, ulExpected, ulRet);
+    }
+}
+
+static void test_IsBadBoundedStringPtr(void)
+{
+    pIsBadBoundedStringPtr = (void*)GetProcAddress(hMapi32, "IsBadBoundedStringPtr@8");
+    if (!pIsBadBoundedStringPtr)
+        return;
+
+    ok(pIsBadBoundedStringPtr(NULL, 0) == TRUE, "IsBadBoundedStringPtr: expected TRUE\n");
+    ok(pIsBadBoundedStringPtr("TEST", 4) == TRUE, "IsBadBoundedStringPtr: expected TRUE\n");
+    ok(pIsBadBoundedStringPtr("TEST", 5) == FALSE, "IsBadBoundedStringPtr: expected FALSE\n");
+}
+
+START_TEST(util)
+{
+    SCODE ret;
+
+    hMapi32 = LoadLibraryA("mapi32.dll");
+
+    pScInitMapiUtil = (void*)GetProcAddress(hMapi32, "ScInitMapiUtil@4");
+
+    if (!pScInitMapiUtil)
+    {
+        skip("ScInitMapiUtil is not available\n");
+        FreeLibrary(hMapi32);
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = pScInitMapiUtil(0);
+    if ((ret != S_OK) && (GetLastError() == ERROR_PROC_NOT_FOUND))
+    {
+        skip("ScInitMapiUtil is not implemented\n");
+        FreeLibrary(hMapi32);
+        return;
+    }
+
+    test_SwapPword();
+    test_SwapPlong();
+    test_HexFromBin();
+    test_UFromSz();
+    test_UlFromSzHex();
+    test_CbOfEncoded();
+    test_IsBadBoundedStringPtr();
+
+    FreeLibrary(hMapi32);
+}
index 76c9f28..c036f7a 100644 (file)
@@ -629,7 +629,7 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL)
             "IMLangFontLink_CodePageToCodePages failed\n");
     ok (dwCodePages != 0, "No CodePages returned\n");
     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwCodePages, 1035,
-                &CodePage)==S_OK,
+                &CodePage)==S_OK, 
             "IMLangFontLink_CodePagesToCodePage failed\n");
     ok(CodePage == 932, "Incorrect CodePage Returned (%i)\n",CodePage);
 
@@ -644,12 +644,12 @@ static void IMLangFontLink_Test(IMLangFontLink* iMLFL)
     dwManyCodePages = dwManyCodePages | dwCodePages;
 
     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 1256,
-                &CodePage)==S_OK,
+                &CodePage)==S_OK, 
             "IMLangFontLink_CodePagesToCodePage failed\n");
     ok(CodePage == 1256, "Incorrect CodePage Returned (%i)\n",CodePage);
 
     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 936,
-                &CodePage)==S_OK,
+                &CodePage)==S_OK, 
             "IMLangFontLink_CodePagesToCodePage failed\n");
     ok(CodePage == 1252, "Incorrect CodePage Returned (%i)\n",CodePage);
 }
@@ -677,6 +677,7 @@ static void test_rfc1766(IMultiLanguage2 *iML2)
         ok(n == 1, "couldn't fetch 1 RFC1766INFO structure\n");
         ok(IsValidLocale(info.lcid, LCID_SUPPORTED), "invalid lcid %04x\n", info.lcid);
     }
+    IEnumRfc1766_Release(pEnumRfc1766);
 }
 
 static void test_GetLcidFromRfc1766(IMultiLanguage2 *iML2)
@@ -783,6 +784,6 @@ START_TEST(mlang)
 
     IMLangFontLink_Test(iMLFL);
     IMLangFontLink_Release(iMLFL);
-
+    
     CoUninitialize();
 }
index e3bf83c..748cb29 100644 (file)
@@ -1,9 +1,9 @@
-<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>
diff --git a/rostests/winetests/netapi32/access.c b/rostests/winetests/netapi32/access.c
new file mode 100644 (file)
index 0000000..3861768
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Conformance test of the access functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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);
+}
diff --git a/rostests/winetests/netapi32/apibuf.c b/rostests/winetests/netapi32/apibuf.c
new file mode 100644 (file)
index 0000000..920be6a
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Conformance test of the network buffer function.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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);
+}
diff --git a/rostests/winetests/netapi32/ds.c b/rostests/winetests/netapi32/ds.c
new file mode 100644 (file)
index 0000000..9111915
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2005 Paul Vriens
+ *
+ * Conformance test of the ds functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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);
+}
diff --git a/rostests/winetests/netapi32/netapi32.rbuild b/rostests/winetests/netapi32/netapi32.rbuild
new file mode 100644 (file)
index 0000000..1baa392
--- /dev/null
@@ -0,0 +1,17 @@
+<?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>
diff --git a/rostests/winetests/netapi32/testlist.c b/rostests/winetests/netapi32/testlist.c
new file mode 100644 (file)
index 0000000..5d8d227
--- /dev/null
@@ -0,0 +1,21 @@
+/* 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 }
+};
diff --git a/rostests/winetests/netapi32/wksta.c b/rostests/winetests/netapi32/wksta.c
new file mode 100644 (file)
index 0000000..ba68eb4
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Conformance test of the workstation functions.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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);
+}
diff --git a/rostests/winetests/odbccp32/misc.c b/rostests/winetests/odbccp32/misc.c
new file mode 100644 (file)
index 0000000..f5ea536
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2007 Bill Medland
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <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();
+}
diff --git a/rostests/winetests/odbccp32/odbccp32.rbuild b/rostests/winetests/odbccp32/odbccp32.rbuild
new file mode 100644 (file)
index 0000000..d3550a3
--- /dev/null
@@ -0,0 +1,14 @@
+<?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>
diff --git a/rostests/winetests/odbccp32/testlist.c b/rostests/winetests/odbccp32/testlist.c
new file mode 100644 (file)
index 0000000..a92d75f
--- /dev/null
@@ -0,0 +1,15 @@
+/* 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 }
+};
diff --git a/rostests/winetests/ole32/clipboard.c b/rostests/winetests/ole32/clipboard.c
new file mode 100644 (file)
index 0000000..0127ce5
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Clipboard unit tests
+ *
+ * Copyright 2006 Kevin Koltzau
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include <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();
+}
diff --git a/rostests/winetests/ole32/compobj.c b/rostests/winetests/ole32/compobj.c
new file mode 100644 (file)
index 0000000..89cf9b9
--- /dev/null
@@ -0,0 +1,1025 @@
+/*
+ * Component Object Tests
+ *
+ * Copyright 2005 Robert Shearman
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+#define CONST_VTABLE
+
+#include <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();
+}