diff --git a/rostests/winetests/directory.rbuild b/rostests/winetests/directory.rbuild
index 27255cafcd7..7f852f45a04 100644
--- a/rostests/winetests/directory.rbuild
+++ b/rostests/winetests/directory.rbuild
@@ -55,15 +55,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rostests/winetests/inetcomm/inetcomm.rbuild b/rostests/winetests/inetcomm/inetcomm.rbuild
new file mode 100644
index 00000000000..9f2b9eb6a83
--- /dev/null
+++ b/rostests/winetests/inetcomm/inetcomm.rbuild
@@ -0,0 +1,13 @@
+
+ -Wno-format
+ .
+ mimeintl.c
+ mimeole.c
+ testlist.c
+ wine
+ inetcomm
+ oleaut32
+ ole32
+ kernel32
+ ntdll
+
diff --git a/rostests/winetests/inetcomm/mimeintl.c b/rostests/winetests/inetcomm/mimeintl.c
new file mode 100644
index 00000000000..563943f663b
--- /dev/null
+++ b/rostests/winetests/inetcomm/mimeintl.c
@@ -0,0 +1,304 @@
+/*
+ * MimeInternational tests
+ *
+ * Copyright 2008 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windows.h"
+#include "ole2.h"
+#include "ocidl.h"
+
+#include "mimeole.h"
+
+#include "initguid.h"
+#include "mlang.h"
+
+#include
+#include
+
+#include "wine/test.h"
+
+static void test_create(void)
+{
+ IMimeInternational *internat, *internat2;
+ HRESULT hr;
+ ULONG ref;
+
+ hr = MimeOleGetInternat(&internat);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = MimeOleGetInternat(&internat2);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ /* Under w2k8 it's no longer a singleton */
+ if(internat == internat2)
+ {
+ /* test to show that the object is a singleton with
+ a reference held by the dll. */
+ ref = IMimeInternational_Release(internat2);
+ ok(ref == 2 ||
+ ref == 1, /* win95 - object is a static singleton */
+ "got %d\n", ref);
+
+ ref = IMimeInternational_Release(internat);
+ ok(ref == 1, "got %d\n", ref);
+ }
+ else
+ {
+ ref = IMimeInternational_Release(internat2);
+ ok(ref == 0, "got %d\n", ref);
+
+ ref = IMimeInternational_Release(internat);
+ ok(ref == 0, "got %d\n", ref);
+ }
+
+}
+
+static inline HRESULT get_mlang(IMultiLanguage **ml)
+{
+ return CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
+ &IID_IMultiLanguage, (void **)ml);
+}
+
+static HRESULT mlang_getcsetinfo(const char *charset, MIMECSETINFO *mlang_info)
+{
+ DWORD len = MultiByteToWideChar(CP_ACP, 0, charset, -1, NULL, 0);
+ BSTR bstr = SysAllocStringLen(NULL, len - 1);
+ HRESULT hr;
+ IMultiLanguage *ml;
+
+ MultiByteToWideChar(CP_ACP, 0, charset, -1, bstr, len);
+
+ hr = get_mlang(&ml);
+
+ if(SUCCEEDED(hr))
+ {
+ hr = IMultiLanguage_GetCharsetInfo(ml, bstr, mlang_info);
+ IMultiLanguage_Release(ml);
+ }
+ SysFreeString(bstr);
+ if(FAILED(hr)) hr = MIME_E_NOT_FOUND;
+ return hr;
+}
+
+static HRESULT mlang_getcodepageinfo(UINT cp, MIMECPINFO *mlang_cp_info)
+{
+ HRESULT hr;
+ IMultiLanguage *ml;
+
+ hr = get_mlang(&ml);
+
+ if(SUCCEEDED(hr))
+ {
+ hr = IMultiLanguage_GetCodePageInfo(ml, cp, mlang_cp_info);
+ IMultiLanguage_Release(ml);
+ }
+ return hr;
+}
+
+static HRESULT mlang_getcsetinfo_from_cp(UINT cp, CHARSETTYPE charset_type, MIMECSETINFO *mlang_info)
+{
+ MIMECPINFO mlang_cp_info;
+ WCHAR *charset_name;
+ HRESULT hr;
+ IMultiLanguage *ml;
+
+ hr = mlang_getcodepageinfo(cp, &mlang_cp_info);
+ if(FAILED(hr)) return hr;
+
+ switch(charset_type)
+ {
+ case CHARSET_BODY:
+ charset_name = mlang_cp_info.wszBodyCharset;
+ break;
+ case CHARSET_HEADER:
+ charset_name = mlang_cp_info.wszHeaderCharset;
+ break;
+ case CHARSET_WEB:
+ charset_name = mlang_cp_info.wszWebCharset;
+ break;
+ }
+
+ hr = get_mlang(&ml);
+
+ if(SUCCEEDED(hr))
+ {
+ hr = IMultiLanguage_GetCharsetInfo(ml, charset_name, mlang_info);
+ IMultiLanguage_Release(ml);
+ }
+ return hr;
+}
+
+static void test_charset(void)
+{
+ IMimeInternational *internat;
+ HRESULT hr;
+ HCHARSET hcs, hcs_windows_1252, hcs_windows_1251;
+ INETCSETINFO cs_info;
+ MIMECSETINFO mlang_cs_info;
+
+ hr = MimeOleGetInternat(&internat);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeInternational_FindCharset(internat, "non-existent", &hcs);
+ ok(hr == MIME_E_NOT_FOUND, "got %08x\n", hr);
+
+ hr = IMimeInternational_FindCharset(internat, "windows-1252", &hcs_windows_1252);
+ ok(hr == S_OK, "got %08x\n", hr);
+ hr = IMimeInternational_FindCharset(internat, "windows-1252", &hcs);
+ ok(hr == S_OK, "got %08x\n", hr);
+ ok(hcs_windows_1252 == hcs, "got different hcharsets for the same name\n");
+ hr = IMimeInternational_FindCharset(internat, "WiNdoWs-1252", &hcs);
+ ok(hr == S_OK, "got %08x\n", hr);
+ ok(hcs_windows_1252 == hcs, "got different hcharsets for the same name\n");
+
+ hr = IMimeInternational_FindCharset(internat, "windows-1251", &hcs_windows_1251);
+ ok(hr == S_OK, "got %08x\n", hr);
+ ok(hcs_windows_1252 != hcs_windows_1251, "got the same hcharset for the different names\n");
+
+ hr = IMimeInternational_GetCharsetInfo(internat, hcs_windows_1252, &cs_info);
+ ok(hr == S_OK, "got %08x\n", hr);
+
+ hr = mlang_getcsetinfo("windows-1252", &mlang_cs_info);
+ ok(hr == S_OK, "got %08x\n", hr);
+ ok(cs_info.cpiWindows == mlang_cs_info.uiCodePage, "cpiWindows %d while mlang uiCodePage %d\n",
+ cs_info.cpiWindows, mlang_cs_info.uiCodePage);
+ ok(cs_info.cpiInternet == mlang_cs_info.uiInternetEncoding, "cpiInternet %d while mlang uiInternetEncoding %d\n",
+ cs_info.cpiInternet, mlang_cs_info.uiInternetEncoding);
+ ok(cs_info.hCharset == hcs_windows_1252, "hCharset doesn't match requested\n");
+ ok(!strcmp(cs_info.szName, "windows-1252"), "szName doesn't match requested\n");
+
+ hr = IMimeInternational_GetCodePageCharset(internat, 1252, CHARSET_BODY, &hcs);
+ ok(hr == S_OK, "got %08x\n", hr);
+ hr = IMimeInternational_GetCharsetInfo(internat, hcs, &cs_info);
+ ok(hr == S_OK, "got %08x\n", hr);
+
+ hr = mlang_getcsetinfo_from_cp(1252, CHARSET_BODY, &mlang_cs_info);
+ ok(hr == S_OK, "got %08x\n", hr);
+ ok(cs_info.cpiWindows == mlang_cs_info.uiCodePage, "cpiWindows %d while mlang uiCodePage %d\n",
+ cs_info.cpiWindows, mlang_cs_info.uiCodePage);
+ ok(cs_info.cpiInternet == mlang_cs_info.uiInternetEncoding, "cpiInternet %d while mlang uiInternetEncoding %d\n",
+ cs_info.cpiInternet, mlang_cs_info.uiInternetEncoding);
+
+ IMimeInternational_Release(internat);
+}
+
+static void test_defaultcharset(void)
+{
+ IMimeInternational *internat;
+ HRESULT hr;
+ HCHARSET hcs_default, hcs, hcs_windows_1251;
+
+ hr = MimeOleGetInternat(&internat);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeInternational_GetDefaultCharset(internat, &hcs_default);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeInternational_GetCodePageCharset(internat, GetACP(), CHARSET_BODY, &hcs);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(hcs_default == hcs, "Unexpected default charset\n");
+
+ hr = IMimeInternational_FindCharset(internat, "windows-1251", &hcs_windows_1251);
+ ok(hr == S_OK, "got %08x\n", hr);
+ hr = IMimeInternational_SetDefaultCharset(internat, hcs_windows_1251);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeInternational_GetDefaultCharset(internat, &hcs);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(hcs == hcs_windows_1251, "didn't retrieve recently set default\n");
+ /* Set the old default back again */
+ hr = IMimeInternational_SetDefaultCharset(internat, hcs_default);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ IMimeInternational_Release(internat);
+}
+
+static void test_convert(void)
+{
+ IMimeInternational *internat;
+ HRESULT hr;
+ BLOB src, dst;
+ ULONG read;
+ PROPVARIANT prop_in, prop_out;
+ static char test_string[] = "test string";
+ static WCHAR test_stringW[] = {'t','e','s','t',' ','s','t','r','i','n','g',0};
+
+ hr = MimeOleGetInternat(&internat);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ src.pBlobData = (BYTE*)test_string;
+ src.cbSize = sizeof(test_string);
+ hr = IMimeInternational_ConvertBuffer(internat, 1252, 28591, &src, &dst, &read);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(read == sizeof(test_string), "got %d\n", read);
+ ok(dst.cbSize == sizeof(test_string), "got %d\n", dst.cbSize);
+ CoTaskMemFree(dst.pBlobData);
+
+ src.cbSize = 2;
+ hr = IMimeInternational_ConvertBuffer(internat, 1252, 28591, &src, &dst, &read);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(read == 2, "got %d\n", read);
+ ok(dst.cbSize == 2, "got %d\n", dst.cbSize);
+ CoTaskMemFree(dst.pBlobData);
+
+ prop_in.vt = VT_LPWSTR;
+ prop_in.u.pwszVal = test_stringW;
+ hr = IMimeInternational_ConvertString(internat, CP_UNICODE, 1252, &prop_in, &prop_out);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(prop_out.vt == VT_LPSTR, "got %d\n", prop_out.vt);
+ ok(!strcmp(prop_out.u.pszVal, test_string), "got %s\n", prop_out.u.pszVal);
+ PropVariantClear(&prop_out);
+
+ /* If in.vt is VT_LPWSTR, ignore cpiSrc */
+ prop_in.vt = VT_LPWSTR;
+ prop_in.u.pwszVal = test_stringW;
+ hr = IMimeInternational_ConvertString(internat, 28591, 1252, &prop_in, &prop_out);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(prop_out.vt == VT_LPSTR, "got %d\n", prop_out.vt);
+ ok(!strcmp(prop_out.u.pszVal, test_string), "got %s\n", prop_out.u.pszVal);
+ PropVariantClear(&prop_out);
+
+ prop_in.vt = VT_LPSTR;
+ prop_in.u.pszVal = test_string;
+ hr = IMimeInternational_ConvertString(internat, 28591, CP_UNICODE, &prop_in, &prop_out);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(prop_out.vt == VT_LPWSTR, "got %d\n", prop_out.vt);
+ ok(!lstrcmpW(prop_out.u.pwszVal, test_stringW), "mismatched strings\n");
+ PropVariantClear(&prop_out);
+
+ /* If in.vt is VT_LPSTR and cpiSrc is CP_UNICODE, use another multibyte codepage (probably GetACP()) */
+ prop_in.vt = VT_LPSTR;
+ prop_in.u.pszVal = test_string;
+ hr = IMimeInternational_ConvertString(internat, CP_UNICODE, CP_UNICODE, &prop_in, &prop_out);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(prop_out.vt == VT_LPWSTR, "got %d\n", prop_out.vt);
+ ok(!lstrcmpW(prop_out.u.pwszVal, test_stringW), "mismatched strings\n");
+ PropVariantClear(&prop_out);
+
+ IMimeInternational_Release(internat);
+}
+
+START_TEST(mimeintl)
+{
+ OleInitialize(NULL);
+ test_create();
+ test_charset();
+ test_defaultcharset();
+ test_convert();
+ OleUninitialize();
+}
diff --git a/rostests/winetests/inetcomm/mimeole.c b/rostests/winetests/inetcomm/mimeole.c
new file mode 100644
index 00000000000..7edef676f50
--- /dev/null
+++ b/rostests/winetests/inetcomm/mimeole.c
@@ -0,0 +1,344 @@
+/*
+ * MimeOle tests
+ *
+ * Copyright 2007 Huw Davies
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "initguid.h"
+#include "windows.h"
+#include "ole2.h"
+#include "ocidl.h"
+
+#include "mimeole.h"
+
+#include
+#include
+
+#include "wine/test.h"
+
+static char msg1[] =
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/mixed;\r\n"
+ " boundary=\"------------1.5.0.6\";\r\n"
+ " stuff=\"du;nno\";\r\n"
+ " morestuff=\"so\\\\me\\\"thing\\\"\"\r\n"
+ "foo: bar\r\n"
+ "From: Huw Davies \r\n"
+ "From: Me \r\n"
+ "To: wine-patches \r\n"
+ "Cc: Huw Davies ,\r\n"
+ " \"Fred Bloggs\" \r\n"
+ "foo: baz\r\n"
+ "bar: fum\r\n"
+ "\r\n"
+ "This is a multi-part message in MIME format.\r\n"
+ "--------------1.5.0.6\r\n"
+ "Content-Type: text/plain; format=fixed; charset=UTF-8\r\n"
+ "Content-Transfer-Encoding: 8bit\r\n"
+ "\r\n"
+ "Stuff\r\n"
+ "--------------1.5.0.6\r\n"
+ "Content-Type: text/plain; charset=\"us-ascii\"\r\n"
+ "Content-Transfer-Encoding: 7bit\r\n"
+ "\r\n"
+ "More stuff\r\n"
+ "--------------1.5.0.6--\r\n";
+
+static void test_CreateVirtualStream(void)
+{
+ HRESULT hr;
+ IStream *pstm;
+
+ hr = MimeOleCreateVirtualStream(&pstm);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ IStream_Release(pstm);
+}
+
+static void test_CreateSecurity(void)
+{
+ HRESULT hr;
+ IMimeSecurity *sec;
+
+ hr = MimeOleCreateSecurity(&sec);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ IMimeSecurity_Release(sec);
+}
+
+static void test_CreateBody(void)
+{
+ HRESULT hr;
+ IMimeBody *body;
+ HBODY handle = (void *)0xdeadbeef;
+ IStream *in;
+ LARGE_INTEGER off;
+ ULARGE_INTEGER pos;
+ ENCODINGTYPE enc;
+ ULONG count, found_param, i;
+ MIMEPARAMINFO *param_info;
+ IMimeAllocator *alloc;
+ BODYOFFSETS offsets;
+
+ hr = CoCreateInstance(&CLSID_IMimeBody, NULL, CLSCTX_INPROC_SERVER, &IID_IMimeBody, (void**)&body);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeBody_GetHandle(body, &handle);
+ ok(hr == MIME_E_NO_DATA, "ret %08x\n", hr);
+ ok(handle == NULL, "handle %p\n", handle);
+
+ hr = CreateStreamOnHGlobal(NULL, TRUE, &in);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ IStream_Write(in, msg1, sizeof(msg1) - 1, NULL);
+ off.QuadPart = 0;
+ IStream_Seek(in, off, STREAM_SEEK_SET, NULL);
+
+ /* Need to call InitNew before Load otherwise Load crashes with native inetcomm */
+ hr = IMimeBody_InitNew(body);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeBody_GetCurrentEncoding(body, &enc);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(enc == IET_7BIT, "encoding %d\n", enc);
+
+ hr = IMimeBody_Load(body, in);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ off.QuadPart = 0;
+ IStream_Seek(in, off, STREAM_SEEK_CUR, &pos);
+ ok(pos.u.LowPart == 359, "pos %u\n", pos.u.LowPart);
+
+ hr = IMimeBody_IsContentType(body, "multipart", "mixed");
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeBody_IsContentType(body, "text", "plain");
+ ok(hr == S_FALSE, "ret %08x\n", hr);
+ hr = IMimeBody_IsContentType(body, NULL, "mixed");
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeBody_IsType(body, IBT_EMPTY);
+ ok(hr == S_OK, "got %08x\n", hr);
+
+ hr = IMimeBody_SetData(body, IET_8BIT, "text", "plain", &IID_IStream, in);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeBody_IsContentType(body, "text", "plain");
+ todo_wine
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeBody_GetCurrentEncoding(body, &enc);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(enc == IET_8BIT, "encoding %d\n", enc);
+
+ memset(&offsets, 0xcc, sizeof(offsets));
+ hr = IMimeBody_GetOffsets(body, &offsets);
+ ok(hr == MIME_E_NO_DATA, "ret %08x\n", hr);
+ ok(offsets.cbBoundaryStart == 0, "got %d\n", offsets.cbBoundaryStart);
+ ok(offsets.cbHeaderStart == 0, "got %d\n", offsets.cbHeaderStart);
+ ok(offsets.cbBodyStart == 0, "got %d\n", offsets.cbBodyStart);
+ ok(offsets.cbBodyEnd == 0, "got %d\n", offsets.cbBodyEnd);
+
+ hr = IMimeBody_IsType(body, IBT_EMPTY);
+ ok(hr == S_FALSE, "got %08x\n", hr);
+
+ hr = MimeOleGetAllocator(&alloc);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeBody_GetParameters(body, "nothere", &count, ¶m_info);
+ ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
+ ok(count == 0, "got %d\n", count);
+ ok(!param_info, "got %p\n", param_info);
+
+ hr = IMimeBody_GetParameters(body, "bar", &count, ¶m_info);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(count == 0, "got %d\n", count);
+ ok(!param_info, "got %p\n", param_info);
+
+ hr = IMimeBody_GetParameters(body, "Content-Type", &count, ¶m_info);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ todo_wine /* native adds a charset parameter */
+ ok(count == 4, "got %d\n", count);
+ ok(param_info != NULL, "got %p\n", param_info);
+
+ found_param = 0;
+ for(i = 0; i < count; i++)
+ {
+ if(!strcmp(param_info[i].pszName, "morestuff"))
+ {
+ found_param++;
+ ok(!strcmp(param_info[i].pszData, "so\\me\"thing\""),
+ "got %s\n", param_info[i].pszData);
+ }
+ else if(!strcmp(param_info[i].pszName, "stuff"))
+ {
+ found_param++;
+ ok(!strcmp(param_info[i].pszData, "du;nno"),
+ "got %s\n", param_info[i].pszData);
+ }
+ }
+ ok(found_param == 2, "matched %d params\n", found_param);
+
+ hr = IMimeAllocator_FreeParamInfoArray(alloc, count, param_info, TRUE);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ IMimeAllocator_Release(alloc);
+
+ IStream_Release(in);
+ IMimeBody_Release(body);
+}
+
+static void test_Allocator(void)
+{
+ HRESULT hr;
+ IMimeAllocator *alloc;
+
+ hr = MimeOleGetAllocator(&alloc);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ IMimeAllocator_Release(alloc);
+}
+
+static void test_CreateMessage(void)
+{
+ HRESULT hr;
+ IMimeMessage *msg;
+ IStream *stream;
+ LARGE_INTEGER pos;
+ LONG ref;
+ HBODY hbody;
+ IMimeBody *body;
+ BODYOFFSETS offsets;
+ ULONG count;
+ FINDBODY find_struct;
+ HCHARSET hcs;
+
+ char text[] = "text";
+ HBODY *body_list;
+ PROPVARIANT prop;
+ static char att_pritype[] = "att:pri-content-type";
+
+ hr = MimeOleCreateMessage(NULL, &msg);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ CreateStreamOnHGlobal(NULL, TRUE, &stream);
+ IStream_Write(stream, msg1, sizeof(msg1) - 1, NULL);
+ pos.QuadPart = 0;
+ IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
+
+ hr = IMimeMessage_Load(msg, stream);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeMessage_CountBodies(msg, HBODY_ROOT, TRUE, &count);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(count == 3, "got %d\n", count);
+
+ hr = IMimeMessage_CountBodies(msg, HBODY_ROOT, FALSE, &count);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(count == 3, "got %d\n", count);
+
+ hr = IMimeMessage_BindToObject(msg, HBODY_ROOT, &IID_IMimeBody, (void**)&body);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeBody_GetOffsets(body, &offsets);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(offsets.cbBoundaryStart == 0, "got %d\n", offsets.cbBoundaryStart);
+ ok(offsets.cbHeaderStart == 0, "got %d\n", offsets.cbHeaderStart);
+ ok(offsets.cbBodyStart == 359, "got %d\n", offsets.cbBodyStart);
+ ok(offsets.cbBodyEnd == 666, "got %d\n", offsets.cbBodyEnd);
+ IMimeBody_Release(body);
+
+ hr = IMimeMessage_GetBody(msg, IBL_ROOT, NULL, &hbody);
+
+ PropVariantInit(&prop);
+ hr = IMimeMessage_GetBodyProp(msg, hbody, att_pritype, 0, &prop);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(prop.vt == VT_LPSTR, "vt %08x\n", prop.vt);
+ ok(!strcasecmp(prop.u.pszVal, "multipart"), "got %s\n", prop.u.pszVal);
+ PropVariantClear(&prop);
+
+ hr = IMimeMessage_GetBody(msg, IBL_FIRST, hbody, &hbody);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeMessage_BindToObject(msg, hbody, &IID_IMimeBody, (void**)&body);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeBody_GetOffsets(body, &offsets);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(offsets.cbBoundaryStart == 405, "got %d\n", offsets.cbBoundaryStart);
+ ok(offsets.cbHeaderStart == 428, "got %d\n", offsets.cbHeaderStart);
+ ok(offsets.cbBodyStart == 518, "got %d\n", offsets.cbBodyStart);
+ ok(offsets.cbBodyEnd == 523, "got %d\n", offsets.cbBodyEnd);
+
+ hr = IMimeBody_GetCharset(body, &hcs);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ todo_wine
+ {
+ ok(hcs != NULL, "Expected non-NULL charset\n");
+ }
+
+ IMimeBody_Release(body);
+
+ hr = IMimeMessage_GetBody(msg, IBL_NEXT, hbody, &hbody);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeMessage_BindToObject(msg, hbody, &IID_IMimeBody, (void**)&body);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ hr = IMimeBody_GetOffsets(body, &offsets);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(offsets.cbBoundaryStart == 525, "got %d\n", offsets.cbBoundaryStart);
+ ok(offsets.cbHeaderStart == 548, "got %d\n", offsets.cbHeaderStart);
+ ok(offsets.cbBodyStart == 629, "got %d\n", offsets.cbBodyStart);
+ ok(offsets.cbBodyEnd == 639, "got %d\n", offsets.cbBodyEnd);
+ IMimeBody_Release(body);
+
+ find_struct.pszPriType = text;
+ find_struct.pszSubType = NULL;
+
+ hr = IMimeMessage_FindFirst(msg, &find_struct, &hbody);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeMessage_FindNext(msg, &find_struct, &hbody);
+ ok(hr == S_OK, "ret %08x\n", hr);
+
+ hr = IMimeMessage_FindNext(msg, &find_struct, &hbody);
+ ok(hr == MIME_E_NOT_FOUND, "ret %08x\n", hr);
+
+ hr = IMimeMessage_GetAttachments(msg, &count, &body_list);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ ok(count == 2, "got %d\n", count);
+ CoTaskMemFree(body_list);
+
+ hr = IMimeMessage_GetCharset(body, &hcs);
+ ok(hr == S_OK, "ret %08x\n", hr);
+ todo_wine
+ {
+ ok(hcs != NULL, "Expected non-NULL charset\n");
+ }
+
+ IMimeMessage_Release(msg);
+
+ ref = IStream_AddRef(stream);
+ ok(ref == 2 ||
+ broken(ref == 1), /* win95 */
+ "ref %d\n", ref);
+ IStream_Release(stream);
+
+ IStream_Release(stream);
+}
+
+START_TEST(mimeole)
+{
+ OleInitialize(NULL);
+ test_CreateVirtualStream();
+ test_CreateSecurity();
+ test_CreateBody();
+ test_Allocator();
+ test_CreateMessage();
+ OleUninitialize();
+}
diff --git a/rostests/winetests/inetcomm/testlist.c b/rostests/winetests/inetcomm/testlist.c
new file mode 100644
index 00000000000..fd53432c649
--- /dev/null
+++ b/rostests/winetests/inetcomm/testlist.c
@@ -0,0 +1,17 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_mimeintl(void);
+extern void func_mimeole(void);
+
+const struct test winetest_testlist[] =
+{
+ { "mimeintl", func_mimeintl },
+ { "mimeole", func_mimeole },
+ { 0, 0 }
+};
diff --git a/rostests/winetests/inetmib1/inetmib1.rbuild b/rostests/winetests/inetmib1/inetmib1.rbuild
new file mode 100644
index 00000000000..9e13b26521e
--- /dev/null
+++ b/rostests/winetests/inetmib1/inetmib1.rbuild
@@ -0,0 +1,10 @@
+
+ -Wno-format
+ .
+ main.c
+ testlist.c
+ wine
+ snmpapi
+ kernel32
+ ntdll
+
diff --git a/rostests/winetests/inetmib1/main.c b/rostests/winetests/inetmib1/main.c
new file mode 100644
index 00000000000..13613fd9d42
--- /dev/null
+++ b/rostests/winetests/inetmib1/main.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2008 Juan Lang
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#include
+#include
+#include
+#include
+#include
+
+#include "wine/test.h"
+
+static HMODULE inetmib1;
+
+static void testInit(void)
+{
+ BOOL (WINAPI *pInit)(DWORD, HANDLE *, AsnObjectIdentifier *);
+ BOOL ret;
+ HANDLE event;
+ AsnObjectIdentifier oid;
+
+ pInit = (void *)GetProcAddress(inetmib1, "SnmpExtensionInit");
+ if (!pInit)
+ {
+ skip("no SnmpExtensionInit\n");
+ return;
+ }
+ /* Crash
+ ret = pInit(0, NULL, NULL);
+ ret = pInit(0, NULL, &oid);
+ ret = pInit(0, &event, NULL);
+ */
+ ret = pInit(0, &event, &oid);
+ ok(ret, "SnmpExtensionInit failed: %d\n", GetLastError());
+ ok(!strcmp("1.3.6.1.2.1.1", SnmpUtilOidToA(&oid)),
+ "Expected 1.3.6.1.2.1.1, got %s\n", SnmpUtilOidToA(&oid));
+}
+
+static void testQuery(void)
+{
+ BOOL (WINAPI *pQuery)(BYTE, SnmpVarBindList *, AsnInteger32 *,
+ AsnInteger32 *);
+ BOOL ret, moreData, noChange;
+ SnmpVarBindList list;
+ AsnInteger32 error, index;
+ UINT bogus[] = { 1,2,3,4 };
+ UINT mib2System[] = { 1,3,6,1,2,1,1 };
+ UINT mib2If[] = { 1,3,6,1,2,1,2 };
+ UINT mib2IfTable[] = { 1,3,6,1,2,1,2,2 };
+ UINT mib2IfDescr[] = { 1,3,6,1,2,1,2,2,1,2 };
+ UINT mib2IfAdminStatus[] = { 1,3,6,1,2,1,2,2,1,7 };
+ UINT mib2IfOperStatus[] = { 1,3,6,1,2,1,2,2,1,8 };
+ UINT mib2IpAddr[] = { 1,3,6,1,2,1,4,20,1,1 };
+ UINT mib2IpRouteTable[] = { 1,3,6,1,2,1,4,21,1,1 };
+ UINT mib2UdpTable[] = { 1,3,6,1,2,1,7,5,1,1 };
+ SnmpVarBind vars[3], vars2[3], vars3[3];
+ UINT entry;
+
+ pQuery = (void *)GetProcAddress(inetmib1, "SnmpExtensionQuery");
+ if (!pQuery)
+ {
+ skip("couldn't find SnmpExtensionQuery\n");
+ return;
+ }
+ /* Crash
+ ret = pQuery(0, NULL, NULL, NULL);
+ ret = pQuery(0, NULL, &error, NULL);
+ ret = pQuery(0, NULL, NULL, &index);
+ ret = pQuery(0, &list, NULL, NULL);
+ ret = pQuery(0, &list, &error, NULL);
+ */
+
+ /* An empty list succeeds */
+ list.len = 0;
+ error = 0xdeadbeef;
+ index = 0xdeadbeef;
+ ret = pQuery(SNMP_PDU_GET, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOERROR,
+ "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
+ ok(index == 0, "expected index 0, got %d\n", index);
+
+ /* Oddly enough, this "succeeds," even though the OID is clearly
+ * unsupported.
+ */
+ vars[0].name.idLength = sizeof(bogus) / sizeof(bogus[0]);
+ vars[0].name.ids = bogus;
+ vars[0].value.asnType = 0;
+ list.len = 1;
+ list.list = vars;
+ SetLastError(0xdeadbeef);
+ error = 0xdeadbeef;
+ index = 0xdeadbeef;
+ ret = pQuery(SNMP_PDU_GET, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOERROR ||
+ error == ERROR_FILE_NOT_FOUND /* Win9x */,
+ "expected SNMP_ERRORSTATUS_NOERROR or ERROR_FILE_NOT_FOUND, got %d\n",
+ error);
+ if (error == SNMP_ERRORSTATUS_NOERROR)
+ ok(index == 0, "expected index 0, got %d\n", index);
+ else if (error == ERROR_FILE_NOT_FOUND)
+ ok(index == 1, "expected index 1, got %d\n", index);
+ /* The OID isn't changed either: */
+ ok(!strcmp("1.2.3.4", SnmpUtilOidToA(&vars[0].name)),
+ "expected 1.2.3.4, got %s\n", SnmpUtilOidToA(&vars[0].name));
+
+ /* The table is not an accessible variable, so it fails */
+ vars[0].name.idLength = sizeof(mib2IfTable) / sizeof(mib2IfTable[0]);
+ vars[0].name.ids = mib2IfTable;
+ SetLastError(0xdeadbeef);
+ error = 0xdeadbeef;
+ index = 0xdeadbeef;
+ ret = pQuery(SNMP_PDU_GET, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOSUCHNAME,
+ "expected SNMP_ERRORSTATUS_NOSUCHNAME, got %d\n", error);
+ /* The index is 1-based rather than 0-based */
+ ok(index == 1, "expected index 1, got %d\n", index);
+
+ /* A Get fails on something that specifies a table (but not a particular
+ * entry in it)...
+ */
+ vars[0].name.idLength = sizeof(mib2IfDescr) / sizeof(mib2IfDescr[0]);
+ vars[0].name.ids = mib2IfDescr;
+ vars[1].name.idLength =
+ sizeof(mib2IfAdminStatus) / sizeof(mib2IfAdminStatus[0]);
+ vars[1].name.ids = mib2IfAdminStatus;
+ vars[2].name.idLength =
+ sizeof(mib2IfOperStatus) / sizeof(mib2IfOperStatus[0]);
+ vars[2].name.ids = mib2IfOperStatus;
+ list.len = 3;
+ SetLastError(0xdeadbeef);
+ error = 0xdeadbeef;
+ index = 0xdeadbeef;
+ ret = pQuery(SNMP_PDU_GET, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOSUCHNAME,
+ "expected SNMP_ERRORSTATUS_NOSUCHNAME, got %d\n", error);
+ ok(index == 1, "expected index 1, got %d\n", index);
+ /* but a GetNext succeeds with the same values, because GetNext gets the
+ * entry after the specified OID, not the entry specified by it. The
+ * successor to the table is the first entry in the table.
+ * The OIDs need to be allocated, because GetNext modifies them to indicate
+ * the end of data.
+ */
+ SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
+ SnmpUtilOidCpy(&vars2[1].name, &vars[1].name);
+ SnmpUtilOidCpy(&vars2[2].name, &vars[2].name);
+ list.list = vars2;
+ moreData = TRUE;
+ noChange = FALSE;
+ entry = 0;
+ do {
+ SetLastError(0xdeadbeef);
+ error = 0xdeadbeef;
+ index = 0xdeadbeef;
+ ret = pQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOERROR,
+ "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
+ ok(index == 0, "expected index 0, got %d\n", index);
+ if (!ret)
+ moreData = FALSE;
+ else if (error)
+ moreData = FALSE;
+ else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
+ vars[0].name.idLength))
+ moreData = FALSE;
+ else if (SnmpUtilOidNCmp(&vars2[1].name, &vars[1].name,
+ vars[1].name.idLength))
+ moreData = FALSE;
+ else if (SnmpUtilOidNCmp(&vars2[2].name, &vars[2].name,
+ vars[2].name.idLength))
+ moreData = FALSE;
+ else if (!SnmpUtilOidCmp(&vars[0].name, &vars2[0].name) ||
+ !SnmpUtilOidCmp(&vars[0].name, &vars2[0].name) ||
+ !SnmpUtilOidCmp(&vars[0].name, &vars2[0].name))
+ {
+ /* If the OID isn't modified, the function isn't implemented on this
+ * platform, skip the remaining tests.
+ */
+ noChange = TRUE;
+ }
+ if (moreData)
+ {
+ UINT lastID;
+
+ /* Check the OIDs. For these types of values (display strings and
+ * integers) they should increase by 1 for each element of the table
+ * according to RFC 1158. Windows sometimes has a weird value in the
+ * table, so allow any value as long as it's greater than the previous
+ * value on Windows.
+ */
+ ok(vars2[0].name.idLength == vars[0].name.idLength + 1,
+ "expected length %d, got %d\n", vars[0].name.idLength + 1,
+ vars2[0].name.idLength);
+ lastID = vars2[0].name.ids[vars2[0].name.idLength - 1];
+ ok(lastID == entry + 1 || broken(lastID > entry),
+ "expected %d, got %d\n", entry + 1, lastID);
+ ok(vars2[1].name.idLength == vars[1].name.idLength + 1,
+ "expected length %d, got %d\n", vars[1].name.idLength + 1,
+ vars2[1].name.idLength);
+ lastID = vars2[1].name.ids[vars2[1].name.idLength - 1];
+ ok(lastID == entry + 1 || broken(lastID > entry),
+ "expected %d, got %d\n", entry + 1, lastID);
+ ok(vars2[2].name.idLength == vars[2].name.idLength + 1,
+ "expected length %d, got %d\n", vars[2].name.idLength + 1,
+ vars2[2].name.idLength);
+ lastID = vars2[2].name.ids[vars2[2].name.idLength - 1];
+ ok(lastID == entry + 1 || broken(lastID > entry),
+ "expected %d, got %d\n", entry + 1, lastID);
+ entry = lastID;
+ /* Check the types while we're at it */
+ ok(vars2[0].value.asnType == ASN_OCTETSTRING,
+ "expected ASN_OCTETSTRING, got %02x\n", vars2[0].value.asnType);
+ ok(vars2[1].value.asnType == ASN_INTEGER,
+ "expected ASN_INTEGER, got %02x\n", vars2[1].value.asnType);
+ ok(vars2[2].value.asnType == ASN_INTEGER,
+ "expected ASN_INTEGER, got %02x\n", vars2[2].value.asnType);
+ }
+ else if (noChange)
+ skip("no change in OID, no MIB2 IF table implementation\n");
+ } while (moreData && !noChange);
+ SnmpUtilVarBindFree(&vars2[0]);
+ SnmpUtilVarBindFree(&vars2[1]);
+ SnmpUtilVarBindFree(&vars2[2]);
+
+ /* Even though SnmpExtensionInit says this DLL supports the MIB2 system
+ * variables, on recent systems (at least Win2k) the first variable it
+ * returns a value for is the first interface.
+ */
+ vars[0].name.idLength = sizeof(mib2System) / sizeof(mib2System[0]);
+ vars[0].name.ids = mib2System;
+ SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
+ vars2[0].value.asnType = 0;
+ list.len = 1;
+ list.list = vars2;
+ moreData = TRUE;
+ noChange = FALSE;
+ ret = pQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOERROR,
+ "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
+ ok(index == 0, "expected index 0, got %d\n", index);
+ vars3[0].name.idLength = sizeof(mib2If) / sizeof(mib2If[0]);
+ vars3[0].name.ids = mib2If;
+ ok(!SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name, vars[0].name.idLength) ||
+ !SnmpUtilOidNCmp(&vars2[0].name, &vars3[0].name, vars3[0].name.idLength),
+ "expected 1.3.6.1.2.1.1 or 1.3.6.1.2.1.2, got %s\n",
+ SnmpUtilOidToA(&vars2[0].name));
+ SnmpUtilVarBindFree(&vars2[0]);
+
+ /* Check the type and OIDs of the IP address table */
+ vars[0].name.idLength = sizeof(mib2IpAddr) / sizeof(mib2IpAddr[0]);
+ vars[0].name.ids = mib2IpAddr;
+ SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
+ vars2[0].value.asnType = 0;
+ list.len = 1;
+ list.list = vars2;
+ moreData = TRUE;
+ do {
+ ret = pQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOERROR,
+ "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
+ ok(index == 0, "expected index 0, got %d\n", index);
+ if (!ret)
+ moreData = FALSE;
+ else if (error)
+ moreData = FALSE;
+ else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
+ vars[0].name.idLength))
+ moreData = FALSE;
+ else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name))
+ {
+ /* If the OID isn't modified, the function isn't implemented on this
+ * platform, skip the remaining tests.
+ */
+ noChange = TRUE;
+ }
+ if (moreData)
+ {
+ /* Make sure the size of the OID is right.
+ * FIXME: don't know if IPv6 addrs are shared with this table.
+ * Don't think so, but I'm not certain.
+ */
+ ok(vars2[0].name.idLength == vars[0].name.idLength + 4,
+ "expected length %d, got %d\n", vars[0].name.idLength + 4,
+ vars2[0].name.idLength);
+ /* Make sure the type is right */
+ ok(vars2[0].value.asnType == ASN_IPADDRESS,
+ "expected type ASN_IPADDRESS, got %02x\n",
+ vars2[0].value.asnType);
+ if (vars2[0].value.asnType == ASN_IPADDRESS)
+ {
+ UINT i;
+
+ /* This looks uglier than it is: the base OID for the IP
+ * address, 1.3.6.1.2.1.4.20.1.1, is appended with the IP
+ * address of the entry. So e.g. the loopback address is
+ * identified in MIB2 as 1.3.6.1.2.1.4.20.1.1.127.0.0.1
+ */
+ for (i = 0; i < vars2[0].value.asnValue.address.length; i++)
+ {
+ ok(vars2[0].value.asnValue.address.stream[i] ==
+ vars2[0].name.ids[vars2[0].name.idLength - 4 + i],
+ "expected ident byte %d to be %d, got %d\n", i,
+ vars2[0].value.asnValue.address.stream[i],
+ vars2[0].name.ids[vars2[0].name.idLength - 4 + i]);
+ }
+ }
+ }
+ else if (noChange)
+ skip("no change in OID, no MIB2 IP address table implementation\n");
+ } while (moreData && !noChange);
+ SnmpUtilVarBindFree(&vars2[0]);
+
+ /* Check the type and OIDs of the IP route table */
+ vars[0].name.idLength = DEFINE_SIZEOF(mib2IpRouteTable);
+ vars[0].name.ids = mib2IpRouteTable;
+ SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
+ vars2[0].value.asnType = 0;
+ list.len = 1;
+ list.list = vars2;
+ moreData = TRUE;
+ noChange = FALSE;
+ do {
+ ret = pQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ ok(error == SNMP_ERRORSTATUS_NOERROR,
+ "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
+ ok(index == 0, "expected index 0, got %d\n", index);
+ if (!ret)
+ moreData = FALSE;
+ else if (error)
+ moreData = FALSE;
+ else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
+ vars[0].name.idLength))
+ moreData = FALSE;
+ else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name))
+ {
+ /* If the OID isn't modified, the function isn't implemented on this
+ * platform, skip the remaining tests.
+ */
+ noChange = TRUE;
+ }
+ if (moreData)
+ {
+ /* Make sure the size of the OID is right.
+ * FIXME: don't know if IPv6 addrs are shared with this table.
+ * Don't think so, but I'm not certain.
+ */
+ ok(vars2[0].name.idLength == vars[0].name.idLength + 4,
+ "expected length %d, got %d\n", vars[0].name.idLength + 4,
+ vars2[0].name.idLength);
+ /* Make sure the type is right */
+ ok(vars2[0].value.asnType == ASN_IPADDRESS,
+ "expected type ASN_IPADDRESS, got %02x\n",
+ vars2[0].value.asnType);
+ if (vars2[0].value.asnType == ASN_IPADDRESS)
+ {
+ UINT i;
+
+ /* The base OID for the route table, 1.3.6.1.2.1.4.21.1.1, is
+ * appended with the dest IP address of the entry. So e.g. a
+ * route entry for 224.0.0.0 is identified in MIB2 as
+ * 1.3.6.1.2.1.4.21.1.1.224.0.0.0
+ */
+ for (i = 0; i < vars2[0].value.asnValue.address.length; i++)
+ {
+ ok(vars2[0].value.asnValue.address.stream[i] ==
+ vars2[0].name.ids[vars2[0].name.idLength - 4 + i],
+ "expected ident byte %d to be %d, got %d\n", i,
+ vars2[0].value.asnValue.address.stream[i],
+ vars2[0].name.ids[vars2[0].name.idLength - 4 + i]);
+ }
+ }
+ }
+ else if (noChange)
+ skip("no change in OID, no MIB2 IP route table implementation\n");
+ } while (moreData && !noChange);
+ SnmpUtilVarBindFree(&vars2[0]);
+
+ /* Check the type and OIDs of the UDP table */
+ vars[0].name.idLength = DEFINE_SIZEOF(mib2UdpTable);
+ vars[0].name.ids = mib2UdpTable;
+ SnmpUtilOidCpy(&vars2[0].name, &vars[0].name);
+ vars2[0].value.asnType = 0;
+ list.len = 1;
+ list.list = vars2;
+ moreData = TRUE;
+ noChange = FALSE;
+ do {
+ ret = pQuery(SNMP_PDU_GETNEXT, &list, &error, &index);
+ ok(ret, "SnmpExtensionQuery failed: %d\n", GetLastError());
+ /* FIXME: error and index aren't checked here because the UDP table is
+ * the last OID currently supported by Wine, so the last GetNext fails.
+ * todo_wine is also not effective because it will succeed for all but
+ * the last GetNext. Remove the if (0) if any later OID is supported
+ * by Wine.
+ */
+ if (0) {
+ ok(error == SNMP_ERRORSTATUS_NOERROR,
+ "expected SNMP_ERRORSTATUS_NOERROR, got %d\n", error);
+ ok(index == 0, "expected index 0, got %d\n", index);
+ }
+ if (!ret)
+ moreData = FALSE;
+ else if (error)
+ moreData = FALSE;
+ else if (SnmpUtilOidNCmp(&vars2[0].name, &vars[0].name,
+ vars[0].name.idLength))
+ moreData = FALSE;
+ else if (!SnmpUtilOidCmp(&vars2[0].name, &vars[0].name))
+ {
+ /* If the OID isn't modified, the function isn't implemented on this
+ * platform, skip the remaining tests.
+ */
+ noChange = TRUE;
+ }
+ if (moreData)
+ {
+ /* Make sure the size of the OID is right. */
+ ok(vars2[0].name.idLength == vars[0].name.idLength + 5,
+ "expected length %d, got %d\n", vars[0].name.idLength + 5,
+ vars2[0].name.idLength);
+ /* Make sure the type is right */
+ ok(vars2[0].value.asnType == ASN_IPADDRESS,
+ "expected type ASN_IPADDRESS, got %02x\n",
+ vars2[0].value.asnType);
+ if (vars2[0].value.asnType == ASN_IPADDRESS)
+ {
+ UINT i;
+
+ /* Again with the ugly: the base OID for the UDP table,
+ * 1.3.6.1.2.1.7.5.1, is appended with the local IP address and
+ * port number of the entry. So e.g. an entry for
+ * 192.168.1.1:4000 is identified in MIB2 as
+ * 1.3.6.1.2.1.7.5.1.192.168.1.1.4000
+ */
+ for (i = 0; i < vars2[0].value.asnValue.address.length; i++)
+ {
+ ok(vars2[0].value.asnValue.address.stream[i] ==
+ vars2[0].name.ids[vars2[0].name.idLength - 5 + i],
+ "expected ident byte %d to be %d, got %d\n", i,
+ vars2[0].value.asnValue.address.stream[i],
+ vars2[0].name.ids[vars2[0].name.idLength - 5 + i]);
+ }
+ }
+ }
+ else if (noChange)
+ skip("no change in OID, no MIB2 UDP table implementation\n");
+ } while (moreData && !noChange);
+ SnmpUtilVarBindFree(&vars2[0]);
+}
+
+START_TEST(main)
+{
+ inetmib1 = LoadLibraryA("inetmib1");
+ testInit();
+ testQuery();
+}
diff --git a/rostests/winetests/inetmib1/testlist.c b/rostests/winetests/inetmib1/testlist.c
new file mode 100644
index 00000000000..6f0c3974819
--- /dev/null
+++ b/rostests/winetests/inetmib1/testlist.c
@@ -0,0 +1,15 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_main(void);
+
+const struct test winetest_testlist[] =
+{
+ { "main", func_main },
+ { 0, 0 }
+};
diff --git a/rostests/winetests/itss/data.chm b/rostests/winetests/itss/data.chm
new file mode 100644
index 00000000000..53b32804221
Binary files /dev/null and b/rostests/winetests/itss/data.chm differ
diff --git a/rostests/winetests/itss/itss.rbuild b/rostests/winetests/itss/itss.rbuild
new file mode 100644
index 00000000000..5a95a1dd54d
--- /dev/null
+++ b/rostests/winetests/itss/itss.rbuild
@@ -0,0 +1,11 @@
+
+ -Wno-format
+ .
+ protocol.c
+ rsrc.rc
+ testlist.c
+ wine
+ ole32
+ kernel32
+ ntdll
+
diff --git a/rostests/winetests/itss/protocol.c b/rostests/winetests/itss/protocol.c
new file mode 100644
index 00000000000..b52e3d32224
--- /dev/null
+++ b/rostests/winetests/itss/protocol.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright 2006 Jacek Caban for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define COBJMACROS
+
+#include
+#include
+
+#include "windef.h"
+#include "winbase.h"
+#include "initguid.h"
+#include "ole2.h"
+#include "urlmon.h"
+#include "shlwapi.h"
+
+#define DEFINE_EXPECT(func) \
+ static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+
+#define SET_EXPECT(func) \
+ expect_ ## func = TRUE
+
+#define CHECK_EXPECT(func) \
+ do { \
+ ok(expect_ ##func, "unexpected call " #func "\n"); \
+ expect_ ## func = FALSE; \
+ called_ ## func = TRUE; \
+ }while(0)
+
+#define CHECK_EXPECT2(func) \
+ do { \
+ ok(expect_ ##func, "unexpected call " #func "\n"); \
+ called_ ## func = TRUE; \
+ }while(0)
+
+#define SET_CALLED(func) \
+ expect_ ## func = called_ ## func = FALSE
+
+#define CHECK_CALLED(func) \
+ do { \
+ ok(called_ ## func, "expected " #func "\n"); \
+ SET_CALLED(func); \
+ }while(0)
+
+DEFINE_GUID(CLSID_ITSProtocol,0x9d148291,0xb9c8,0x11d0,0xa4,0xcc,0x00,0x00,0xf8,0x01,0x49,0xf6);
+
+DEFINE_EXPECT(GetBindInfo);
+DEFINE_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
+DEFINE_EXPECT(ReportProgress_SENDINGREQUEST);
+DEFINE_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
+DEFINE_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
+DEFINE_EXPECT(ReportProgress_DIRECTBIND);
+DEFINE_EXPECT(ReportData);
+DEFINE_EXPECT(ReportResult);
+
+static HRESULT expect_hrResult;
+static IInternetProtocol *read_protocol = NULL;
+static DWORD bindf;
+
+static const WCHAR blank_url1[] = {'i','t','s',':',
+ 't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR blank_url2[] = {'m','S','-','i','T','s',':',
+ 't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR blank_url3[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
+ 't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR blank_url4[] = {'i','t','s',':',
+ 't','e','s','t','.','c','h','m',':',':','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR blank_url5[] = {'i','t','s',':',
+ 't','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR blank_url6[] = {'i','t','s',':',
+ 't','e','s','t','.','c','h','m',':',':','/','%','6','2','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR blank_url7[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
+ 't','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR blank_url8[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
+ 't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l','/',0};
+
+static enum {
+ ITS_PROTOCOL,
+ MK_PROTOCOL
+} test_protocol;
+
+static const WCHAR cache_file1[] =
+ {'t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR cache_file2[] =
+ {'t','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR cache_file3[] =
+ {'t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l','/',0};
+static const WCHAR *cache_file = cache_file1;
+
+static HRESULT WINAPI ProtocolSink_QueryInterface(IInternetProtocolSink *iface, REFIID riid, void **ppv)
+{
+ if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
+ *ppv = iface;
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ProtocolSink_AddRef(IInternetProtocolSink *iface)
+{
+ return 2;
+}
+
+static ULONG WINAPI ProtocolSink_Release(IInternetProtocolSink *iface)
+{
+ return 1;
+}
+
+static HRESULT WINAPI ProtocolSink_Switch(IInternetProtocolSink *iface, PROTOCOLDATA *pProtocolData)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ProtocolSink_ReportProgress(IInternetProtocolSink *iface, ULONG ulStatusCode,
+ LPCWSTR szStatusText)
+{
+ static const WCHAR blank_html[] = {'b','l','a','n','k','.','h','t','m','l',0};
+ static const WCHAR text_html[] = {'t','e','x','t','/','h','t','m','l',0};
+
+ switch(ulStatusCode) {
+ case BINDSTATUS_BEGINDOWNLOADDATA:
+ CHECK_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
+ ok(!szStatusText, "szStatusText != NULL\n");
+ break;
+ case BINDSTATUS_SENDINGREQUEST:
+ CHECK_EXPECT(ReportProgress_SENDINGREQUEST);
+ if(test_protocol == ITS_PROTOCOL)
+ ok(!lstrcmpW(szStatusText, blank_html), "unexpected szStatusText\n");
+ else
+ ok(szStatusText == NULL, "szStatusText != NULL\n");
+ break;
+ case BINDSTATUS_MIMETYPEAVAILABLE:
+ CHECK_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
+ ok(!lstrcmpW(szStatusText, text_html), "unexpected szStatusText\n");
+ break;
+ case BINDSTATUS_CACHEFILENAMEAVAILABLE:
+ CHECK_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
+ ok(!lstrcmpW(szStatusText, cache_file), "unexpected szStatusText\n");
+ break;
+ case BINDSTATUS_DIRECTBIND:
+ CHECK_EXPECT(ReportProgress_DIRECTBIND);
+ ok(!szStatusText, "szStatusText != NULL\n");
+ break;
+ default:
+ ok(0, "unexpected ulStatusCode %d\n", ulStatusCode);
+ break;
+ }
+
+ return S_OK;
+}
+
+static HRESULT WINAPI ProtocolSink_ReportData(IInternetProtocolSink *iface, DWORD grfBSCF, ULONG ulProgress,
+ ULONG ulProgressMax)
+{
+ CHECK_EXPECT(ReportData);
+
+ ok(ulProgress == ulProgressMax, "ulProgress != ulProgressMax\n");
+ if(test_protocol == ITS_PROTOCOL)
+ ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE), "grcf = %08x\n", grfBSCF);
+ else
+ ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION), "grcf = %08x\n", grfBSCF);
+
+ if(read_protocol) {
+ BYTE buf[100];
+ DWORD cb = 0xdeadbeef;
+ HRESULT hres;
+
+ hres = IInternetProtocol_Read(read_protocol, buf, sizeof(buf), &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 13, "cb=%u expected 13\n", cb);
+ ok(!memcmp(buf, "", 13), "unexpected data\n");
+ }
+
+ return S_OK;
+}
+
+static HRESULT WINAPI ProtocolSink_ReportResult(IInternetProtocolSink *iface, HRESULT hrResult,
+ DWORD dwError, LPCWSTR szResult)
+{
+ CHECK_EXPECT(ReportResult);
+
+ ok(hrResult == expect_hrResult, "expected: %08x got: %08x\n", expect_hrResult, hrResult);
+ ok(dwError == 0, "dwError = %d\n", dwError);
+ ok(!szResult, "szResult != NULL\n");
+
+ return S_OK;
+}
+
+static IInternetProtocolSinkVtbl protocol_sink_vtbl = {
+ ProtocolSink_QueryInterface,
+ ProtocolSink_AddRef,
+ ProtocolSink_Release,
+ ProtocolSink_Switch,
+ ProtocolSink_ReportProgress,
+ ProtocolSink_ReportData,
+ ProtocolSink_ReportResult
+};
+
+static IInternetProtocolSink protocol_sink = {
+ &protocol_sink_vtbl
+};
+
+static HRESULT WINAPI BindInfo_QueryInterface(IInternetBindInfo *iface, REFIID riid, void **ppv)
+{
+ if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetBindInfo, riid)) {
+ *ppv = iface;
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI BindInfo_AddRef(IInternetBindInfo *iface)
+{
+ return 2;
+}
+
+static ULONG WINAPI BindInfo_Release(IInternetBindInfo *iface)
+{
+ return 1;
+}
+
+static HRESULT WINAPI BindInfo_GetBindInfo(IInternetBindInfo *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
+{
+ CHECK_EXPECT(GetBindInfo);
+
+ ok(grfBINDF != NULL, "grfBINDF == NULL\n");
+ if(grfBINDF)
+ ok(!*grfBINDF, "*grfBINDF != 0\n");
+ ok(pbindinfo != NULL, "pbindinfo == NULL\n");
+ ok(pbindinfo->cbSize == sizeof(BINDINFO), "wrong size of pbindinfo: %d\n", pbindinfo->cbSize);
+
+ *grfBINDF = bindf;
+ return S_OK;
+}
+
+static HRESULT WINAPI BindInfo_GetBindString(IInternetBindInfo *iface, ULONG ulStringType, LPOLESTR *ppwzStr,
+ ULONG cEl, ULONG *pcElFetched)
+{
+ ok(0, "unexpected call\n");
+ return E_NOTIMPL;
+}
+
+static IInternetBindInfoVtbl bind_info_vtbl = {
+ BindInfo_QueryInterface,
+ BindInfo_AddRef,
+ BindInfo_Release,
+ BindInfo_GetBindInfo,
+ BindInfo_GetBindString
+};
+
+static IInternetBindInfo bind_info = {
+ &bind_info_vtbl
+};
+
+static void test_protocol_fail(IInternetProtocol *protocol, LPCWSTR url, HRESULT expected_hres)
+{
+ HRESULT hres;
+
+ SET_EXPECT(GetBindInfo);
+ SET_EXPECT(ReportResult);
+
+ expect_hrResult = expected_hres;
+ hres = IInternetProtocol_Start(protocol, url, &protocol_sink, &bind_info, 0, 0);
+ ok(hres == expected_hres, "expected: %08x got: %08x\n", expected_hres, hres);
+
+ CHECK_CALLED(GetBindInfo);
+ CHECK_CALLED(ReportResult);
+}
+
+#define protocol_start(p,u,e) _protocol_start(__LINE__,p,u,e)
+static HRESULT _protocol_start(unsigned line, IInternetProtocol *protocol, LPCWSTR url, BOOL expect_mime)
+{
+ HRESULT hres;
+
+ SET_EXPECT(GetBindInfo);
+ if(test_protocol == MK_PROTOCOL)
+ SET_EXPECT(ReportProgress_DIRECTBIND);
+ SET_EXPECT(ReportProgress_SENDINGREQUEST);
+ if(expect_mime)
+ SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
+ if(test_protocol == MK_PROTOCOL)
+ SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
+ SET_EXPECT(ReportData);
+ if(test_protocol == ITS_PROTOCOL)
+ SET_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
+ SET_EXPECT(ReportResult);
+ expect_hrResult = S_OK;
+
+ hres = IInternetProtocol_Start(protocol, url, &protocol_sink, &bind_info, 0, 0);
+
+ if(FAILED(hres)) {
+ SET_CALLED(GetBindInfo);
+ if(test_protocol == MK_PROTOCOL)
+ SET_CALLED(ReportProgress_DIRECTBIND);
+ SET_CALLED(ReportProgress_SENDINGREQUEST);
+ if(expect_mime)
+ SET_CALLED(ReportProgress_MIMETYPEAVAILABLE);
+ if(test_protocol == MK_PROTOCOL)
+ SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
+ SET_CALLED(ReportData);
+ if(test_protocol == ITS_PROTOCOL)
+ SET_CALLED(ReportProgress_BEGINDOWNLOADDATA);
+ SET_CALLED(ReportResult);
+ }else {
+ CHECK_CALLED(GetBindInfo);
+ if(test_protocol == MK_PROTOCOL)
+ SET_CALLED(ReportProgress_DIRECTBIND);
+ CHECK_CALLED(ReportProgress_SENDINGREQUEST);
+ if(expect_mime)
+ CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE);
+ if(test_protocol == MK_PROTOCOL)
+ SET_EXPECT(ReportProgress_CACHEFILENAMEAVAIABLE);
+ CHECK_CALLED(ReportData);
+ if(test_protocol == ITS_PROTOCOL)
+ CHECK_CALLED(ReportProgress_BEGINDOWNLOADDATA);
+ CHECK_CALLED(ReportResult);
+ }
+
+ return hres;
+}
+
+static void test_protocol_url(IClassFactory *factory, LPCWSTR url, BOOL expect_mime)
+{
+ IInternetProtocol *protocol;
+ BYTE buf[512];
+ ULONG cb, ref;
+ HRESULT hres;
+
+ hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
+ ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ hres = protocol_start(protocol, url, expect_mime);
+ if(FAILED(hres)) {
+ IInternetProtocol_Release(protocol);
+ return;
+ }
+
+ hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 13, "cb=%u expected 13\n", cb);
+ ok(!memcmp(buf, "", 13), "unexpected data\n");
+ ref = IInternetProtocol_Release(protocol);
+ ok(!ref, "protocol ref=%d\n", ref);
+
+ hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
+ ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ cb = 0xdeadbeef;
+ hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
+ ok(hres == (test_protocol == ITS_PROTOCOL ? INET_E_DATA_NOT_AVAILABLE : E_FAIL),
+ "Read returned %08x\n", hres);
+ ok(cb == 0xdeadbeef, "cb=%u expected 0xdeadbeef\n", cb);
+
+ protocol_start(protocol, url, expect_mime);
+ hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 2, "cb=%u expected 2\n", cb);
+ hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 11, "cb=%u, expected 11\n", cb);
+ hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
+ ok(hres == S_FALSE, "Read failed: %08x expected S_FALSE\n", hres);
+ ok(cb == 0, "cb=%u expected 0\n", cb);
+ hres = IInternetProtocol_UnlockRequest(protocol);
+ ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
+ ref = IInternetProtocol_Release(protocol);
+ ok(!ref, "protocol ref=%d\n", ref);
+
+ hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
+ ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ protocol_start(protocol, url, expect_mime);
+ hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ hres = IInternetProtocol_LockRequest(protocol, 0);
+ ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
+ hres = IInternetProtocol_UnlockRequest(protocol);
+ ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
+ hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 11, "cb=%u, expected 11\n", cb);
+ ref = IInternetProtocol_Release(protocol);
+ ok(!ref, "protocol ref=%d\n", ref);
+
+ hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
+ ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ protocol_start(protocol, url, expect_mime);
+ hres = IInternetProtocol_LockRequest(protocol, 0);
+ ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
+ hres = IInternetProtocol_Terminate(protocol, 0);
+ ok(hres == S_OK, "Terminate failed: %08x\n", hres);
+ hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 2, "cb=%u, expected 2\n", cb);
+ hres = IInternetProtocol_UnlockRequest(protocol);
+ ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
+ hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 2, "cb=%u, expected 2\n", cb);
+ hres = IInternetProtocol_Terminate(protocol, 0);
+ ok(hres == S_OK, "Terminate failed: %08x\n", hres);
+ hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
+ ok(hres == S_OK, "Read failed: %08x\n", hres);
+ ok(cb == 2, "cb=%u expected 2\n", cb);
+ ref = IInternetProtocol_Release(protocol);
+ ok(!ref, "protocol ref=%d\n", ref);
+
+ hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&read_protocol);
+ ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ protocol_start(read_protocol, url, expect_mime);
+ ref = IInternetProtocol_Release(read_protocol);
+ ok(!ref, "protocol ref=%d\n", ref);
+ read_protocol = NULL;
+}
+
+static const WCHAR rel_url1[] =
+ {'t','e','s','t','.','h','t','m','l',0};
+static const WCHAR rel_url2[] =
+ {'t','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
+static const WCHAR rel_url3[] =
+ {'/','t','e','s','t','.','h','t','m','l',0};
+static const WCHAR rel_url4[] =
+ {'t','e',':','t','.','h','t','m','l',0};
+static const WCHAR rel_url5[] =
+ {'d','i','r','/','t','e','s','t','.','h','t','m','l',0};
+
+static const WCHAR base_url1[] = {'i','t','s',':',
+ 't','e','s','t',':','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR base_url2[] = {'i','t','s',':','t','e','s','t','.','c','h','m',
+ ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR base_url3[] = {'m','s','-','i','t','s',':','t','e','s','t','.','c','h','m',
+ ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR base_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
+ 't','e','s','t','.','c','h','m',':',':','/','d','i','r','/',
+ 'b','l','a','n','k','.','h','t','m','l',0};
+static const WCHAR base_url5[] = {'x','x','x',':','t','e','s','t','.','c','h','m',
+ ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
+
+static const WCHAR combined_url1[] = {'i','t','s',':',
+ 't','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
+static const WCHAR combined_url2[] = {'i','t','s',':',
+ 't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
+static const WCHAR combined_url3[] = {'i','t','s',':',
+ 't','e','s','t',':','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
+static const WCHAR combined_url4[] = {'i','t','s',':','t','e','s','t','.','c','h','m',
+ ':',':','b','l','a','n','k','.','h','t','m','l','t','e','s','t','.','h','t','m','l',0};
+static const WCHAR combined_url5[] = {'m','s','-','i','t','s',':',
+ 't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
+static const WCHAR combined_url6[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
+ 't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
+
+static const struct {
+ LPCWSTR base_url;
+ LPCWSTR rel_url;
+ DWORD flags;
+ HRESULT hres;
+ LPCWSTR combined_url;
+} combine_tests[] = {
+ {blank_url1, blank_url1, 0, STG_E_INVALIDNAME, NULL},
+ {blank_url2, blank_url2, 0, STG_E_INVALIDNAME, NULL},
+ {blank_url1, rel_url1, 0, S_OK, combined_url1},
+ {blank_url1, rel_url2, 0, STG_E_INVALIDNAME, NULL},
+ {blank_url1, rel_url3, 0, S_OK, combined_url1},
+ {blank_url1, rel_url4, 0, STG_E_INVALIDNAME, NULL},
+ {blank_url1, rel_url3, URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO, S_OK, combined_url1},
+ {blank_url1, rel_url5, 0, S_OK, combined_url2},
+ {rel_url1, rel_url2, 0, 0x80041001, NULL},
+ {base_url1, rel_url1, 0, S_OK, combined_url3},
+ {base_url2, rel_url1, 0, S_OK, combined_url2},
+ {blank_url4, rel_url1, 0, S_OK, combined_url4},
+ {base_url3, rel_url1, 0, S_OK, combined_url5},
+ {base_url4, rel_url1, 0, S_OK, combined_url6},
+ {base_url5, rel_url1, 0, INET_E_USE_DEFAULT_PROTOCOLHANDLER, NULL},
+ {base_url2, rel_url3, 0, S_OK, combined_url1},
+};
+
+static void test_its_protocol_info(IInternetProtocol *protocol)
+{
+ IInternetProtocolInfo *info;
+ WCHAR buf[1024];
+ DWORD size, i;
+ HRESULT hres;
+
+ hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetProtocolInfo, (void**)&info);
+ ok(hres == S_OK, "Could not get IInternetProtocolInfo interface: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ for(i = PARSE_CANONICALIZE; i <= PARSE_UNESCAPE; i++) {
+ if(i != PARSE_CANONICALIZE && i != PARSE_SECURITY_URL) {
+ hres = IInternetProtocolInfo_ParseUrl(info, blank_url1, i, 0, buf,
+ sizeof(buf)/sizeof(buf[0]), &size, 0);
+ ok(hres == INET_E_DEFAULT_ACTION,
+ "[%d] failed: %08x, expected INET_E_DEFAULT_ACTION\n", i, hres);
+ }
+ }
+
+ for(i=0; i < sizeof(combine_tests)/sizeof(combine_tests[0]); i++) {
+ size = 0xdeadbeef;
+ memset(buf, 0xfe, sizeof(buf));
+ hres = IInternetProtocolInfo_CombineUrl(info, combine_tests[i].base_url,
+ combine_tests[i].rel_url, combine_tests[i].flags, buf,
+ sizeof(buf)/sizeof(WCHAR), &size, 0);
+ ok(hres == combine_tests[i].hres, "[%d] CombineUrl returned %08x, expected %08x\n",
+ i, hres, combine_tests[i].hres);
+ ok(size == (combine_tests[i].combined_url ? lstrlenW(combine_tests[i].combined_url)+1
+ : 0xdeadbeef), "[%d] unexpected size=%d\n", i, size);
+ if(combine_tests[i].combined_url)
+ ok(!lstrcmpW(combine_tests[i].combined_url, buf), "[%d] unexpected result\n", i);
+ else
+ ok(buf[0] == 0xfefe, "buf changed\n");
+ }
+
+ size = 0xdeadbeef;
+ memset(buf, 0xfe, sizeof(buf));
+ hres = IInternetProtocolInfo_CombineUrl(info, blank_url1, rel_url1, 0, buf,
+ 1, &size, 0);
+ ok(hres == E_OUTOFMEMORY, "CombineUrl failed: %08x\n", hres);
+ ok(size == sizeof(combined_url1)/sizeof(WCHAR), "size=%d\n", size);
+ ok(buf[0] == 0xfefe, "buf changed\n");
+
+ IInternetProtocolInfo_Release(info);
+}
+
+static void test_its_protocol(void)
+{
+ IInternetProtocolInfo *info;
+ IClassFactory *factory;
+ IUnknown *unk;
+ ULONG ref;
+ HRESULT hres;
+
+ static const WCHAR wrong_url1[] =
+ {'i','t','s',':','t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','.','h','t','m','l',0};
+ static const WCHAR wrong_url2[] =
+ {'i','t','s',':','t','e','s','.','c','h','m',':',':','b','/','l','a','n','k','.','h','t','m','l',0};
+ static const WCHAR wrong_url3[] =
+ {'i','t','s',':','t','e','s','t','.','c','h','m','/','b','l','a','n','k','.','h','t','m','l',0};
+ static const WCHAR wrong_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r',':',
+ 't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
+ static const WCHAR wrong_url5[] = {'f','i','l','e',':',
+ 't','e','s','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
+
+ test_protocol = ITS_PROTOCOL;
+
+ hres = CoGetClassObject(&CLSID_ITSProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void**)&unk);
+ ok(hres == S_OK ||
+ broken(hres == REGDB_E_CLASSNOTREG), /* Some W95 and NT4 */
+ "CoGetClassObject failed: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&info);
+ ok(hres == E_NOINTERFACE, "Could not get IInternetProtocolInfo: %08x\n", hres);
+
+ hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&factory);
+ ok(hres == S_OK, "Could not get IClassFactory interface\n");
+ if(SUCCEEDED(hres)) {
+ IInternetProtocol *protocol;
+
+ hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
+ ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
+ if(SUCCEEDED(hres)) {
+ test_its_protocol_info(protocol);
+
+ test_protocol_fail(protocol, wrong_url1, STG_E_FILENOTFOUND);
+ test_protocol_fail(protocol, wrong_url2, STG_E_FILENOTFOUND);
+ test_protocol_fail(protocol, wrong_url3, STG_E_FILENOTFOUND);
+
+ hres = IInternetProtocol_Start(protocol, wrong_url4, &protocol_sink, &bind_info, 0, 0);
+ ok(hres == INET_E_USE_DEFAULT_PROTOCOLHANDLER,
+ "Start failed: %08x, expected INET_E_USE_DEFAULT_PROTOCOLHANDLER\n", hres);
+
+ hres = IInternetProtocol_Start(protocol, wrong_url5, &protocol_sink, &bind_info, 0, 0);
+ ok(hres == INET_E_USE_DEFAULT_PROTOCOLHANDLER,
+ "Start failed: %08x, expected INET_E_USE_DEFAULT_PROTOCOLHANDLER\n", hres);
+
+ ref = IInternetProtocol_Release(protocol);
+ ok(!ref, "protocol ref=%d\n", ref);
+
+ test_protocol_url(factory, blank_url1, TRUE);
+ test_protocol_url(factory, blank_url2, TRUE);
+ test_protocol_url(factory, blank_url3, TRUE);
+ test_protocol_url(factory, blank_url4, TRUE);
+ test_protocol_url(factory, blank_url5, TRUE);
+ test_protocol_url(factory, blank_url6, TRUE);
+ test_protocol_url(factory, blank_url8, TRUE);
+ bindf = BINDF_FROMURLMON | BINDF_NEEDFILE;
+ test_protocol_url(factory, blank_url1, TRUE);
+ }
+
+ IClassFactory_Release(factory);
+ }
+
+ IUnknown_Release(unk);
+}
+
+static void test_mk_protocol(void)
+{
+ IClassFactory *cf;
+ HRESULT hres;
+
+ test_protocol = MK_PROTOCOL;
+
+ hres = CoGetClassObject(&CLSID_MkProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory,
+ (void**)&cf);
+ ok(hres == S_OK ||
+ broken(hres == REGDB_E_CLASSNOTREG), /* Some W95 and NT4 */
+ "CoGetClassObject failed: %08x\n", hres);
+ if(FAILED(hres))
+ return;
+
+ cache_file = cache_file1;
+ test_protocol_url(cf, blank_url3, TRUE);
+ cache_file = cache_file2;
+ test_protocol_url(cf, blank_url7, TRUE);
+ cache_file = cache_file3;
+ test_protocol_url(cf, blank_url8, FALSE);
+
+ IClassFactory_Release(cf);
+}
+
+static BOOL create_chm(void)
+{
+ HANDLE file;
+ HRSRC src;
+ DWORD size;
+
+ file = CreateFileA("test.chm", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ ok(file != INVALID_HANDLE_VALUE, "Could not create test.chm file\n");
+ if(file == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ src = FindResourceA(NULL, MAKEINTRESOURCEA(60), MAKEINTRESOURCEA(60));
+
+ WriteFile(file, LoadResource(NULL, src), SizeofResource(NULL, src), &size, NULL);
+ CloseHandle(file);
+
+ return TRUE;
+}
+
+static void delete_chm(void)
+{
+ BOOL ret;
+
+ ret = DeleteFileA("test.chm");
+ ok(ret, "DeleteFileA failed: %d\n", GetLastError());
+}
+
+START_TEST(protocol)
+{
+ OleInitialize(NULL);
+
+ if(!create_chm())
+ return;
+
+ test_its_protocol();
+ test_mk_protocol();
+
+ delete_chm();
+ OleUninitialize();
+}
diff --git a/rostests/winetests/itss/rsrc.rc b/rostests/winetests/itss/rsrc.rc
new file mode 100644
index 00000000000..0e8eba03916
--- /dev/null
+++ b/rostests/winetests/itss/rsrc.rc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2006 Jacek Caban for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "winbase.h"
+#include "windef.h"
+
+/*
+ * This is a simple .chm file compiled by hhc.exe from files:
+ * ----------------- test.hhp:
+ * [FILES]
+ * blank.html
+ * ----------------- blank.html:
+ *
+ * -----------------
+ */
+
+/* @makedep: data.chm */
+60 60 data.chm
diff --git a/rostests/winetests/itss/testlist.c b/rostests/winetests/itss/testlist.c
new file mode 100644
index 00000000000..8794b7ca517
--- /dev/null
+++ b/rostests/winetests/itss/testlist.c
@@ -0,0 +1,15 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_protocol(void);
+
+const struct test winetest_testlist[] =
+{
+ { "protocol", func_protocol },
+ { 0, 0 }
+};
diff --git a/rostests/winetests/localspl/localmon.c b/rostests/winetests/localspl/localmon.c
new file mode 100644
index 00000000000..23a3608d649
--- /dev/null
+++ b/rostests/winetests/localspl/localmon.c
@@ -0,0 +1,1457 @@
+/*
+ * Unit test suite for localspl API functions: local print monitor
+ *
+ * Copyright 2006-2007 Detlef Riekenberg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wingdi.h"
+#include "winreg.h"
+
+#include "winspool.h"
+#include "ddk/winsplp.h"
+
+#include "wine/test.h"
+
+
+/* ##### */
+
+static HMODULE hdll;
+static HMODULE hlocalmon;
+static LPMONITOREX (WINAPI *pInitializePrintMonitor)(LPWSTR);
+
+static LPMONITOREX pm;
+static BOOL (WINAPI *pEnumPorts)(LPWSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD);
+static BOOL (WINAPI *pOpenPort)(LPWSTR, PHANDLE);
+static BOOL (WINAPI *pOpenPortEx)(LPWSTR, LPWSTR, PHANDLE, struct _MONITOR *);
+static BOOL (WINAPI *pStartDocPort)(HANDLE, LPWSTR, DWORD, DWORD, LPBYTE);
+static BOOL (WINAPI *pWritePort)(HANDLE hPort, LPBYTE, DWORD, LPDWORD);
+static BOOL (WINAPI *pReadPort)(HANDLE hPort, LPBYTE, DWORD, LPDWORD);
+static BOOL (WINAPI *pEndDocPort)(HANDLE);
+static BOOL (WINAPI *pClosePort)(HANDLE);
+static BOOL (WINAPI *pAddPort)(LPWSTR, HWND, LPWSTR);
+static BOOL (WINAPI *pAddPortEx)(LPWSTR, DWORD, LPBYTE, LPWSTR);
+static BOOL (WINAPI *pConfigurePort)(LPWSTR, HWND, LPWSTR);
+static BOOL (WINAPI *pDeletePort)(LPWSTR, HWND, LPWSTR);
+static BOOL (WINAPI *pGetPrinterDataFromPort)(HANDLE, DWORD, LPWSTR, LPWSTR, DWORD, LPWSTR, DWORD, LPDWORD);
+static BOOL (WINAPI *pSetPortTimeOuts)(HANDLE, LPCOMMTIMEOUTS, DWORD);
+static BOOL (WINAPI *pXcvOpenPort)(LPCWSTR, ACCESS_MASK, PHANDLE phXcv);
+static DWORD (WINAPI *pXcvDataPort)(HANDLE, LPCWSTR, PBYTE, DWORD, PBYTE, DWORD, PDWORD);
+static BOOL (WINAPI *pXcvClosePort)(HANDLE);
+
+static HANDLE hXcv;
+static HANDLE hXcv_noaccess;
+
+/* ########################### */
+
+static const WCHAR cmd_AddPortW[] = {'A','d','d','P','o','r','t',0};
+static const WCHAR cmd_ConfigureLPTPortCommandOKW[] = {'C','o','n','f','i','g','u','r','e',
+ 'L','P','T','P','o','r','t',
+ 'C','o','m','m','a','n','d','O','K',0};
+static WCHAR cmd_DeletePortW[] = {'D','e','l','e','t','e','P','o','r','t',0};
+static WCHAR cmd_GetTransmissionRetryTimeoutW[] = {'G','e','t',
+ 'T','r','a','n','s','m','i','s','s','i','o','n',
+ 'R','e','t','r','y','T','i','m','e','o','u','t',0};
+
+static WCHAR cmd_MonitorUIW[] = {'M','o','n','i','t','o','r','U','I',0};
+static WCHAR cmd_MonitorUI_lcaseW[] = {'m','o','n','i','t','o','r','u','i',0};
+static WCHAR cmd_PortIsValidW[] = {'P','o','r','t','I','s','V','a','l','i','d',0};
+static WCHAR does_not_existW[] = {'d','o','e','s','_','n','o','t','_','e','x','i','s','t',0};
+static CHAR emptyA[] = "";
+static WCHAR emptyW[] = {0};
+static WCHAR LocalPortW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
+static WCHAR Monitors_LocalPortW[] = {
+ 'S','y','s','t','e','m','\\',
+ 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+ 'C','o','n','t','r','o','l','\\',
+ 'P','r','i','n','t','\\',
+ 'M','o','n','i','t','o','r','s','\\',
+ 'L','o','c','a','l',' ','P','o','r','t',0};
+
+static CHAR num_0A[] = "0";
+static WCHAR num_0W[] = {'0',0};
+static CHAR num_1A[] = "1";
+static WCHAR num_1W[] = {'1',0};
+static CHAR num_999999A[] = "999999";
+static WCHAR num_999999W[] = {'9','9','9','9','9','9',0};
+static CHAR num_1000000A[] = "1000000";
+static WCHAR num_1000000W[] = {'1','0','0','0','0','0','0',0};
+
+static WCHAR portname_comW[] = {'C','O','M',0};
+static WCHAR portname_com1W[] = {'C','O','M','1',':',0};
+static WCHAR portname_com2W[] = {'C','O','M','2',':',0};
+static WCHAR portname_fileW[] = {'F','I','L','E',':',0};
+static WCHAR portname_lptW[] = {'L','P','T',0};
+static WCHAR portname_lpt1W[] = {'L','P','T','1',':',0};
+static WCHAR portname_lpt2W[] = {'L','P','T','2',':',0};
+static WCHAR server_does_not_existW[] = {'\\','\\','d','o','e','s','_','n','o','t','_','e','x','i','s','t',0};
+
+static CHAR TransmissionRetryTimeoutA[] = {'T','r','a','n','s','m','i','s','s','i','o','n',
+ 'R','e','t','r','y','T','i','m','e','o','u','t',0};
+
+static CHAR WinNT_CV_WindowsA[] = {'S','o','f','t','w','a','r','e','\\',
+ 'M','i','c','r','o','s','o','f','t','\\',
+ 'W','i','n','d','o','w','s',' ','N','T','\\',
+ 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+ 'W','i','n','d','o','w','s',0};
+static WCHAR wineW[] = {'W','i','n','e',0};
+
+static WCHAR tempdirW[MAX_PATH];
+static WCHAR tempfileW[MAX_PATH];
+
+#define PORTNAME_PREFIX 3
+#define PORTNAME_MINSIZE 5
+#define PORTNAME_MAXSIZE 10
+static WCHAR have_com[PORTNAME_MAXSIZE];
+static WCHAR have_lpt[PORTNAME_MAXSIZE];
+static WCHAR have_file[PORTNAME_MAXSIZE];
+
+/* ########################### */
+
+static DWORD delete_port(LPWSTR portname)
+{
+ DWORD res;
+
+ if (pDeletePort) {
+ res = pDeletePort(NULL, 0, portname);
+ }
+ else
+ {
+ res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) portname, (lstrlenW(portname) + 1) * sizeof(WCHAR), NULL, 0, NULL);
+ }
+ return res;
+}
+
+/* ########################### */
+
+static void find_installed_ports(void)
+{
+ PORT_INFO_1W * pi = NULL;
+ WCHAR nameW[PORTNAME_MAXSIZE];
+ DWORD needed;
+ DWORD returned;
+ DWORD res;
+ DWORD id;
+
+ have_com[0] = '\0';
+ have_lpt[0] = '\0';
+ have_file[0] = '\0';
+
+ if (!pEnumPorts) return;
+
+ res = pEnumPorts(NULL, 1, NULL, 0, &needed, &returned);
+ if (!res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
+ pi = HeapAlloc(GetProcessHeap(), 0, needed);
+ }
+ res = pEnumPorts(NULL, 1, (LPBYTE) pi, needed, &needed, &returned);
+
+ if (!res) {
+ skip("no ports found\n");
+ HeapFree(GetProcessHeap(), 0, pi);
+ return;
+ }
+
+ id = 0;
+ while (id < returned) {
+ res = lstrlenW(pi[id].pName);
+ if ((res >= PORTNAME_MINSIZE) && (res < PORTNAME_MAXSIZE) &&
+ (pi[id].pName[res-1] == ':')) {
+ /* copy only the prefix ("LPT" or "COM") */
+ memcpy(&nameW, pi[id].pName, PORTNAME_PREFIX * sizeof(WCHAR));
+ nameW[PORTNAME_PREFIX] = '\0';
+
+ if (!have_com[0] && (lstrcmpiW(nameW, portname_comW) == 0)) {
+ memcpy(&have_com, pi[id].pName, (res+1) * sizeof(WCHAR));
+ }
+
+ if (!have_lpt[0] && (lstrcmpiW(nameW, portname_lptW) == 0)) {
+ memcpy(&have_lpt, pi[id].pName, (res+1) * sizeof(WCHAR));
+ }
+
+ if (!have_file[0] && (lstrcmpiW(pi[id].pName, portname_fileW) == 0)) {
+ memcpy(&have_file, pi[id].pName, (res+1) * sizeof(WCHAR));
+ }
+ }
+ id++;
+ }
+
+ HeapFree(GetProcessHeap(), 0, pi);
+}
+
+/* ########################### */
+
+static void test_AddPort(void)
+{
+ DWORD res;
+
+ /* moved to localui.dll since w2k */
+ if (!pAddPort) return;
+
+ if (0)
+ {
+ /* NT4 crash on this test */
+ res = pAddPort(NULL, 0, NULL);
+ }
+
+ /* Testing-Results (localmon.dll from NT4.0):
+ - The Servername is ignored
+ - Case of MonitorName is ignored
+ */
+
+ SetLastError(0xdeadbeef);
+ res = pAddPort(NULL, 0, emptyW);
+ ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pAddPort(NULL, 0, does_not_existW);
+ ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
+
+}
+
+/* ########################### */
+
+static void test_AddPortEx(void)
+{
+ PORT_INFO_2W pi;
+ DWORD res;
+
+ if (!pAddPortEx) {
+ skip("AddPortEx\n");
+ return;
+ }
+ if ((!pDeletePort) && (!hXcv)) {
+ skip("No API to delete a Port\n");
+ return;
+ }
+
+ /* start test with clean ports */
+ delete_port(tempfileW);
+
+ pi.pPortName = tempfileW;
+ if (0) {
+ /* tests crash with native localspl.dll in w2k,
+ but works with native localspl.dll in wine */
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 1, (LPBYTE) &pi, LocalPortW);
+ trace("returned %u with %u\n", res, GetLastError() );
+ ok( res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
+
+ /* port already exists: */
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 1, (LPBYTE) &pi, LocalPortW);
+ trace("returned %u with %u\n", res, GetLastError() );
+ ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
+ "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
+ res, GetLastError());
+ delete_port(tempfileW);
+
+
+ /* NULL for pMonitorName is documented for Printmonitors, but
+ localspl.dll fails always with ERROR_INVALID_PARAMETER */
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 1, (LPBYTE) &pi, NULL);
+ trace("returned %u with %u\n", res, GetLastError() );
+ ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
+ "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
+ res, GetLastError());
+ if (res) delete_port(tempfileW);
+
+
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 1, (LPBYTE) &pi, emptyW);
+ trace("returned %u with %u\n", res, GetLastError() );
+ ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
+ "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
+ res, GetLastError());
+ if (res) delete_port(tempfileW);
+
+
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 1, (LPBYTE) &pi, does_not_existW);
+ trace("returned %u with %u\n", res, GetLastError() );
+ ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
+ "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
+ res, GetLastError());
+ if (res) delete_port(tempfileW);
+ }
+
+ pi.pPortName = NULL;
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 1, (LPBYTE) &pi, LocalPortW);
+ ok( !res && (GetLastError() == ERROR_INVALID_PARAMETER),
+ "got %u with %u (expected '0' with ERROR_INVALID_PARAMETER)\n",
+ res, GetLastError());
+
+ /* level 2 is documented as supported for Printmonitors,
+ but localspl.dll fails always with ERROR_INVALID_LEVEL */
+
+ pi.pPortName = tempfileW;
+ pi.pMonitorName = LocalPortW;
+ pi.pDescription = wineW;
+ pi.fPortType = PORT_TYPE_WRITE;
+
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 2, (LPBYTE) &pi, LocalPortW);
+ ok( !res && (GetLastError() == ERROR_INVALID_LEVEL),
+ "got %u with %u (expected '0' with ERROR_INVALID_LEVEL)\n",
+ res, GetLastError());
+ if (res) delete_port(tempfileW);
+
+
+ /* invalid levels */
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 0, (LPBYTE) &pi, LocalPortW);
+ ok( !res && (GetLastError() == ERROR_INVALID_LEVEL),
+ "got %u with %u (expected '0' with ERROR_INVALID_LEVEL)\n",
+ res, GetLastError());
+ if (res) delete_port(tempfileW);
+
+
+ SetLastError(0xdeadbeef);
+ res = pAddPortEx(NULL, 3, (LPBYTE) &pi, LocalPortW);
+ ok( !res && (GetLastError() == ERROR_INVALID_LEVEL),
+ "got %u with %u (expected '0' with ERROR_INVALID_LEVEL)\n",
+ res, GetLastError());
+ if (res) delete_port(tempfileW);
+
+ /* cleanup */
+ delete_port(tempfileW);
+}
+
+/* ########################### */
+
+static void test_ClosePort(void)
+{
+ HANDLE hPort;
+ HANDLE hPort2;
+ LPWSTR nameW = NULL;
+ DWORD res;
+ DWORD res2;
+
+
+ if (!pOpenPort || !pClosePort) return;
+
+ if (have_com[0]) {
+ nameW = have_com;
+
+ hPort = (HANDLE) 0xdeadbeef;
+ res = pOpenPort(nameW, &hPort);
+ hPort2 = (HANDLE) 0xdeadbeef;
+ res2 = pOpenPort(nameW, &hPort2);
+
+ if (res2 && (hPort2 != hPort)) {
+ SetLastError(0xdeadbeef);
+ res2 = pClosePort(hPort2);
+ ok(res2, "got %u with %u (expected '!= 0')\n", res2, GetLastError());
+ }
+
+ if (res) {
+ SetLastError(0xdeadbeef);
+ res = pClosePort(hPort);
+ ok(res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
+ }
+ }
+
+
+ if (have_lpt[0]) {
+ nameW = have_lpt;
+
+ hPort = (HANDLE) 0xdeadbeef;
+ res = pOpenPort(nameW, &hPort);
+ hPort2 = (HANDLE) 0xdeadbeef;
+ res2 = pOpenPort(nameW, &hPort2);
+
+ if (res2 && (hPort2 != hPort)) {
+ SetLastError(0xdeadbeef);
+ res2 = pClosePort(hPort2);
+ ok(res2, "got %u with %u (expected '!= 0')\n", res2, GetLastError());
+ }
+
+ if (res) {
+ SetLastError(0xdeadbeef);
+ res = pClosePort(hPort);
+ ok(res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
+ }
+ }
+
+
+ if (have_file[0]) {
+ nameW = have_file;
+
+ hPort = (HANDLE) 0xdeadbeef;
+ res = pOpenPort(nameW, &hPort);
+ hPort2 = (HANDLE) 0xdeadbeef;
+ res2 = pOpenPort(nameW, &hPort2);
+
+ if (res2 && (hPort2 != hPort)) {
+ SetLastError(0xdeadbeef);
+ res2 = pClosePort(hPort2);
+ ok(res2, "got %u with %u (expected '!= 0')\n", res2, GetLastError());
+ }
+
+ if (res) {
+ SetLastError(0xdeadbeef);
+ res = pClosePort(hPort);
+ ok(res, "got %u with %u (expected '!= 0')\n", res, GetLastError());
+ }
+
+ }
+
+ if (0) {
+ /* an invalid HANDLE crash native localspl.dll */
+
+ SetLastError(0xdeadbeef);
+ res = pClosePort(NULL);
+ trace("got %u with %u\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pClosePort( (HANDLE) 0xdeadbeef);
+ trace("got %u with %u\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pClosePort(INVALID_HANDLE_VALUE);
+ trace("got %u with %u\n", res, GetLastError());
+ }
+
+}
+
+/* ########################### */
+
+static void test_ConfigurePort(void)
+{
+ DWORD res;
+
+ /* moved to localui.dll since w2k */
+ if (!pConfigurePort) return;
+
+ if (0)
+ {
+ /* NT4 crash on this test */
+ res = pConfigurePort(NULL, 0, NULL);
+ }
+
+ /* Testing-Results (localmon.dll from NT4.0):
+ - Case of Portname is ignored
+ - "COM1:" and "COM01:" are the same (Compared by value)
+ - Portname without ":" => Dialog "Nothing to configure" comes up; Success
+ - "LPT1:", "LPT0:" and "LPT:" are the same (Numbers in "LPT:" are ignored)
+ - Empty Servername (LPT1:) => Dialog comes up (Servername is ignored)
+ - "FILE:" => Dialog "Nothing to configure" comes up; Success
+ - Empty Portname => => Dialog "Nothing to configure" comes up; Success
+ - Port "does_not_exist" => Dialog "Nothing to configure" comes up; Success
+ */
+ if (winetest_interactive > 0) {
+
+ SetLastError(0xdeadbeef);
+ res = pConfigurePort(NULL, 0, portname_com1W);
+ trace("returned %d with %u\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pConfigurePort(NULL, 0, portname_lpt1W);
+ trace("returned %d with %u\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pConfigurePort(NULL, 0, portname_fileW);
+ trace("returned %d with %u\n", res, GetLastError());
+ }
+}
+
+/* ########################### */
+
+static void test_DeletePort(void)
+{
+ DWORD res;
+
+ /* moved to localui.dll since w2k */
+ if (!pDeletePort) return;
+
+ if (0)
+ {
+ /* NT4 crash on this test */
+ res = pDeletePort(NULL, 0, NULL);
+ }
+
+ /* Testing-Results (localmon.dll from NT4.0):
+ - Case of Portname is ignored (returned '1' on Success)
+ - "COM1:" and "COM01:" are different (Compared as string)
+ - server_does_not_exist (LPT1:) => Port deleted, Success (Servername is ignored)
+ - Empty Portname => => FALSE (LastError not changed)
+ - Port "does_not_exist" => FALSE (LastError not changed)
+ */
+
+ SetLastError(0xdeadbeef);
+ res = pDeletePort(NULL, 0, emptyW);
+ ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pDeletePort(NULL, 0, does_not_existW);
+ ok(!res, "returned %d with %u (expected '0')\n", res, GetLastError());
+
+}
+
+/* ########################### */
+
+static void test_EnumPorts(void)
+{
+ DWORD res;
+ DWORD level;
+ LPBYTE buffer;
+ DWORD cbBuf;
+ DWORD pcbNeeded;
+ DWORD pcReturned;
+
+ if (!pEnumPorts) return;
+
+ /* valid levels are 1 and 2 */
+ for(level = 0; level < 4; level++) {
+
+ cbBuf = 0xdeadbeef;
+ pcReturned = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pEnumPorts(NULL, level, NULL, 0, &cbBuf, &pcReturned);
+
+ /* use only a short test, when we test with an invalid level */
+ if(!level || (level > 2)) {
+ /* NT4 fails with ERROR_INVALID_LEVEL (as expected)
+ XP succeeds with ERROR_SUCCESS () */
+ ok( (cbBuf == 0) && (pcReturned == 0),
+ "(%d) returned %d with %u and %d, %d (expected 0, 0)\n",
+ level, res, GetLastError(), cbBuf, pcReturned);
+ continue;
+ }
+
+ ok( !res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER),
+ "(%d) returned %d with %u and %d, %d (expected '0' with "
+ "ERROR_INSUFFICIENT_BUFFER)\n",
+ level, res, GetLastError(), cbBuf, pcReturned);
+
+ buffer = HeapAlloc(GetProcessHeap(), 0, cbBuf * 2);
+ if (buffer == NULL) continue;
+
+ pcbNeeded = 0xdeadbeef;
+ pcReturned = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pEnumPorts(NULL, level, buffer, cbBuf, &pcbNeeded, &pcReturned);
+ ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
+ level, res, GetLastError(), pcbNeeded, pcReturned);
+ /* We can compare the returned Data with the Registry / "win.ini",[Ports] here */
+
+ pcbNeeded = 0xdeadbeef;
+ pcReturned = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pEnumPorts(NULL, level, buffer, cbBuf+1, &pcbNeeded, &pcReturned);
+ ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
+ level, res, GetLastError(), pcbNeeded, pcReturned);
+
+ pcbNeeded = 0xdeadbeef;
+ pcReturned = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pEnumPorts(NULL, level, buffer, cbBuf-1, &pcbNeeded, &pcReturned);
+ ok( !res && (GetLastError() == ERROR_INSUFFICIENT_BUFFER),
+ "(%d) returned %d with %u and %d, %d (expected '0' with "
+ "ERROR_INSUFFICIENT_BUFFER)\n",
+ level, res, GetLastError(), pcbNeeded, pcReturned);
+
+ if (0)
+ {
+ /* The following tests crash this app with native localmon/localspl */
+ res = pEnumPorts(NULL, level, NULL, cbBuf, &pcbNeeded, &pcReturned);
+ res = pEnumPorts(NULL, level, buffer, cbBuf, NULL, &pcReturned);
+ res = pEnumPorts(NULL, level, buffer, cbBuf, &pcbNeeded, NULL);
+ }
+
+ /* The Servername is ignored */
+ pcbNeeded = 0xdeadbeef;
+ pcReturned = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pEnumPorts(emptyW, level, buffer, cbBuf+1, &pcbNeeded, &pcReturned);
+ ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
+ level, res, GetLastError(), pcbNeeded, pcReturned);
+
+ pcbNeeded = 0xdeadbeef;
+ pcReturned = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pEnumPorts(server_does_not_existW, level, buffer, cbBuf+1, &pcbNeeded, &pcReturned);
+ ok( res, "(%d) returned %d with %u and %d, %d (expected '!= 0')\n",
+ level, res, GetLastError(), pcbNeeded, pcReturned);
+
+ HeapFree(GetProcessHeap(), 0, buffer);
+ }
+}
+
+/* ########################### */
+
+
+static void test_InitializePrintMonitor(void)
+{
+ LPMONITOREX res;
+
+ SetLastError(0xdeadbeef);
+ res = pInitializePrintMonitor(NULL);
+ /* The Parameter was unchecked before w2k */
+ ok( res || (GetLastError() == ERROR_INVALID_PARAMETER),
+ "returned %p with %u\n (expected '!= NULL' or: NULL with "
+ "ERROR_INVALID_PARAMETER)\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pInitializePrintMonitor(emptyW);
+ ok( res || (GetLastError() == ERROR_INVALID_PARAMETER),
+ "returned %p with %u\n (expected '!= NULL' or: NULL with "
+ "ERROR_INVALID_PARAMETER)\n", res, GetLastError());
+
+
+ /* Every call with a non-empty string returns the same Pointer */
+ SetLastError(0xdeadbeef);
+ res = pInitializePrintMonitor(Monitors_LocalPortW);
+ ok( res == pm,
+ "returned %p with %u (expected %p)\n", res, GetLastError(), pm);
+}
+
+
+/* ########################### */
+
+static void test_OpenPort(void)
+{
+ HANDLE hPort;
+ HANDLE hPort2;
+ LPWSTR nameW = NULL;
+ DWORD res;
+ DWORD res2;
+
+ if (!pOpenPort || !pClosePort) return;
+
+ if (have_com[0]) {
+ nameW = have_com;
+
+ hPort = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pOpenPort(nameW, &hPort);
+ ok( res, "got %u with %u and %p (expected '!= 0')\n",
+ res, GetLastError(), hPort);
+
+ /* the same HANDLE is returned for a second OpenPort in native localspl */
+ hPort2 = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res2 = pOpenPort(nameW, &hPort2);
+ ok( res2, "got %u with %u and %p (expected '!= 0')\n",
+ res2, GetLastError(), hPort2);
+
+ if (res) pClosePort(hPort);
+ if (res2 && (hPort2 != hPort)) pClosePort(hPort2);
+ }
+
+ if (have_lpt[0]) {
+ nameW = have_lpt;
+
+ hPort = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pOpenPort(nameW, &hPort);
+ ok( res || (GetLastError() == ERROR_ACCESS_DENIED),
+ "got %u with %u and %p (expected '!= 0' or '0' with ERROR_ACCESS_DENIED)\n",
+ res, GetLastError(), hPort);
+
+ /* the same HANDLE is returned for a second OpenPort in native localspl */
+ hPort2 = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res2 = pOpenPort(nameW, &hPort2);
+ ok( res2 || (GetLastError() == ERROR_ACCESS_DENIED),
+ "got %u with %u and %p (expected '!= 0' or '0' with ERROR_ACCESS_DENIED)\n",
+ res2, GetLastError(), hPort2);
+
+ if (res) pClosePort(hPort);
+ if (res2 && (hPort2 != hPort)) pClosePort(hPort2);
+ }
+
+ if (have_file[0]) {
+ nameW = have_file;
+
+ hPort = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pOpenPort(nameW, &hPort);
+ ok( res, "got %u with %u and %p (expected '!= 0')\n",
+ res, GetLastError(), hPort);
+
+ /* a different HANDLE is returned for a second OpenPort */
+ hPort2 = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res2 = pOpenPort(nameW, &hPort2);
+ ok( res2 && (hPort2 != hPort),
+ "got %u with %u and %p (expected '!= 0' and '!= %p')\n",
+ res2, GetLastError(), hPort2, hPort);
+
+ if (res) pClosePort(hPort);
+ if (res2 && (hPort2 != hPort)) pClosePort(hPort2);
+ }
+
+ if (0) {
+ /* this test crash native localspl (w2k+xp) */
+ if (nameW) {
+ hPort = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pOpenPort(nameW, NULL);
+ trace("got %u with %u and %p\n", res, GetLastError(), hPort);
+ }
+ }
+
+ hPort = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pOpenPort(does_not_existW, &hPort);
+ ok (!res && (hPort == (HANDLE) 0xdeadbeef),
+ "got %u with 0x%x and %p (expected '0' and 0xdeadbeef)\n", res, GetLastError(), hPort);
+ if (res) pClosePort(hPort);
+
+ hPort = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pOpenPort(emptyW, &hPort);
+ ok (!res && (hPort == (HANDLE) 0xdeadbeef),
+ "got %u with 0x%x and %p (expected '0' and 0xdeadbeef)\n", res, GetLastError(), hPort);
+ if (res) pClosePort(hPort);
+
+
+ /* NULL as name crash native localspl (w2k+xp) */
+ if (0) {
+ hPort = (HANDLE) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pOpenPort(NULL, &hPort);
+ trace("got %u with %u and %p\n", res, GetLastError(), hPort);
+ }
+
+}
+
+/* ########################### */
+
+static void test_XcvClosePort(void)
+{
+ DWORD res;
+ HANDLE hXcv2;
+
+
+ if (0)
+ {
+ /* crash with native localspl.dll (w2k+xp) */
+ res = pXcvClosePort(NULL);
+ res = pXcvClosePort(INVALID_HANDLE_VALUE);
+ }
+
+
+ SetLastError(0xdeadbeef);
+ hXcv2 = (HANDLE) 0xdeadbeef;
+ res = pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv2);
+ ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
+
+ if (res) {
+ SetLastError(0xdeadbeef);
+ res = pXcvClosePort(hXcv2);
+ ok( res, "returned %d with %u (expected '!= 0')\n", res, GetLastError());
+
+ if (0)
+ {
+ /* test for "Double Free": crash with native localspl.dll (w2k+xp) */
+ res = pXcvClosePort(hXcv2);
+ }
+ }
+}
+
+/* ########################### */
+
+static void test_XcvDataPort_AddPort(void)
+{
+ DWORD res;
+
+
+ /*
+ * The following tests crash with native localspl.dll on w2k and xp,
+ * but it works, when the native dll (w2k and xp) is used in wine.
+ * also tested (same crash): replacing emptyW with portname_lpt1W
+ * and replacing "NULL, 0, NULL" with "buffer, MAX_PATH, &needed"
+ *
+ * We need to use a different API (AddPortEx) instead
+ */
+ if (0)
+ {
+ /* create a Port for a normal, writable file */
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, NULL);
+
+ /* add our testport again */
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, NULL);
+
+ /* create a well-known Port */
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) portname_lpt1W, (lstrlenW(portname_lpt1W) + 1) * sizeof(WCHAR), NULL, 0, NULL);
+
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) portname_lpt1W, (lstrlenW(portname_lpt1W) + 1) * sizeof(WCHAR), NULL, 0, NULL);
+ /* native localspl.dll on wine: ERROR_ALREADY_EXISTS */
+
+ /* ERROR_ALREADY_EXISTS is also returned from native localspl.dll on wine,
+ when "RPT1:" was already installed for redmonnt.dll:
+ res = pXcvDataPort(hXcv, cmd_AddPortW, (PBYTE) portname_rpt1W, ...
+ */
+
+ /* cleanup */
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, NULL);
+ }
+
+}
+
+/* ########################### */
+
+static void test_XcvDataPort_ConfigureLPTPortCommandOK(void)
+{
+ CHAR org_value[16];
+ CHAR buffer[16];
+ HKEY hroot = NULL;
+ DWORD res;
+ DWORD needed;
+
+
+ /* Read the original value from the registry */
+ res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsA, 0, KEY_ALL_ACCESS, &hroot);
+ if (res == ERROR_ACCESS_DENIED) {
+ skip("ACCESS_DENIED\n");
+ return;
+ }
+
+ if (res != ERROR_SUCCESS) {
+ /* unable to open the registry: skip the test */
+ skip("got %d\n", res);
+ return;
+ }
+ org_value[0] = '\0';
+ needed = sizeof(org_value)-1 ;
+ res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) org_value, &needed);
+ ok( (res == ERROR_SUCCESS) || (res == ERROR_FILE_NOT_FOUND),
+ "returned %u and %u for \"%s\" (expected ERROR_SUCCESS or "
+ "ERROR_FILE_NOT_FOUND)\n", res, needed, org_value);
+
+ RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
+
+ /* set to "0" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_0W, sizeof(num_0W), NULL, 0, &needed);
+ if (res == ERROR_INVALID_PARAMETER) {
+ skip("'ConfigureLPTPortCommandOK' not supported\n");
+ return;
+ }
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+ needed = sizeof(buffer)-1 ;
+ res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
+ ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_0A) == 0),
+ "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
+ res, buffer, num_0A);
+
+
+ /* set to "1" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_1W, sizeof(num_1W), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+ needed = sizeof(buffer)-1 ;
+ res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
+ ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_1A) == 0),
+ "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
+ res, buffer, num_1A);
+
+ /* set to "999999" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_999999W, sizeof(num_999999W), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+ needed = sizeof(buffer)-1 ;
+ res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
+ ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_999999A) == 0),
+ "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
+ res, buffer, num_999999A);
+
+ /* set to "1000000" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_ConfigureLPTPortCommandOKW, (PBYTE) num_1000000W, sizeof(num_1000000W), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+ needed = sizeof(buffer)-1 ;
+ res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) buffer, &needed);
+ ok( (res == ERROR_SUCCESS) && (lstrcmpA(buffer, num_1000000A) == 0),
+ "returned %d and '%s' (expected ERROR_SUCCESS and '%s')\n",
+ res, buffer, num_1000000A);
+
+ /* using cmd_ConfigureLPTPortCommandOKW with does_not_existW:
+ the string "does_not_exist" is written to the registry */
+
+
+ /* restore the original value */
+ RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
+ if (org_value[0]) {
+ res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)org_value, lstrlenA(org_value)+1);
+ ok(res == ERROR_SUCCESS, "unable to restore original value (got %u): %s\n", res, org_value);
+ }
+
+ RegCloseKey(hroot);
+
+}
+
+/* ########################### */
+
+static void test_XcvDataPort_DeletePort(void)
+{
+ DWORD res;
+ DWORD needed;
+
+
+ /* cleanup: just to make sure */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
+ ok( !res || (res == ERROR_FILE_NOT_FOUND),
+ "returned %d with %u (expected ERROR_SUCCESS or ERROR_FILE_NOT_FOUND)\n",
+ res, GetLastError());
+
+
+ /* ToDo: cmd_AddPortW for tempfileW, then cmd_DeletePortW for the existing Port */
+
+
+ /* try to delete a nonexistent Port */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_DeletePortW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
+ ok( res == ERROR_FILE_NOT_FOUND,
+ "returned %d with %u (expected ERROR_FILE_NOT_FOUND)\n", res, GetLastError());
+
+ /* emptyW as Portname: ERROR_FILE_NOT_FOUND is returned */
+ /* NULL as Portname: Native localspl.dll crashed */
+
+}
+
+/* ########################### */
+
+static void test_XcvDataPort_GetTransmissionRetryTimeout(void)
+{
+ CHAR org_value[16];
+ HKEY hroot = NULL;
+ DWORD buffer[2];
+ DWORD res;
+ DWORD needed;
+ DWORD len;
+
+
+ /* ask for needed size */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, NULL, 0, &needed);
+ if (res == ERROR_INVALID_PARAMETER) {
+ skip("'GetTransmissionRetryTimeout' not supported\n");
+ return;
+ }
+ len = sizeof(DWORD);
+ ok( (res == ERROR_INSUFFICIENT_BUFFER) && (needed == len),
+ "returned %d with %u and %u (expected ERROR_INSUFFICIENT_BUFFER "
+ "and '%u')\n", res, GetLastError(), needed, len);
+ len = needed;
+
+ /* Read the original value from the registry */
+ res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WinNT_CV_WindowsA, 0, KEY_ALL_ACCESS, &hroot);
+ if (res == ERROR_ACCESS_DENIED) {
+ skip("ACCESS_DENIED\n");
+ return;
+ }
+
+ if (res != ERROR_SUCCESS) {
+ /* unable to open the registry: skip the test */
+ skip("got %d\n", res);
+ return;
+ }
+
+ org_value[0] = '\0';
+ needed = sizeof(org_value)-1 ;
+ res = RegQueryValueExA(hroot, TransmissionRetryTimeoutA, NULL, NULL, (PBYTE) org_value, &needed);
+ ok( (res == ERROR_SUCCESS) || (res == ERROR_FILE_NOT_FOUND),
+ "returned %u and %u for \"%s\" (expected ERROR_SUCCESS or "
+ "ERROR_FILE_NOT_FOUND)\n", res, needed, org_value);
+
+ /* Get default value (documented as 90 in the w2k reskit, but that is wrong) */
+ RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
+ needed = (DWORD) 0xdeadbeef;
+ buffer[0] = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
+ ok( (res == ERROR_SUCCESS) && (buffer[0] == 45),
+ "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
+ "for '45')\n", res, GetLastError(), needed, buffer[0]);
+
+ /* the default timeout is returned, when the value is empty */
+ res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)emptyA, 1);
+ needed = (DWORD) 0xdeadbeef;
+ buffer[0] = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
+ ok( (res == ERROR_SUCCESS) && (buffer[0] == 45),
+ "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
+ "for '45')\n", res, GetLastError(), needed, buffer[0]);
+
+ /* the dialog is limited (1 - 999999), but that is done somewhere else */
+ res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_0A, lstrlenA(num_0A)+1);
+ needed = (DWORD) 0xdeadbeef;
+ buffer[0] = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
+ ok( (res == ERROR_SUCCESS) && (buffer[0] == 0),
+ "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
+ "for '0')\n", res, GetLastError(), needed, buffer[0]);
+
+
+ res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_1A, lstrlenA(num_1A)+1);
+ needed = (DWORD) 0xdeadbeef;
+ buffer[0] = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
+ ok( (res == ERROR_SUCCESS) && (buffer[0] == 1),
+ "returned %d with %u and %u for %d\n (expected 'ERROR_SUCCESS' "
+ "for '1')\n", res, GetLastError(), needed, buffer[0]);
+
+ res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_999999A, lstrlenA(num_999999A)+1);
+ needed = (DWORD) 0xdeadbeef;
+ buffer[0] = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
+ ok( (res == ERROR_SUCCESS) && (buffer[0] == 999999),
+ "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
+ "for '999999')\n", res, GetLastError(), needed, buffer[0]);
+
+
+ res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)num_1000000A, lstrlenA(num_1000000A)+1);
+ needed = (DWORD) 0xdeadbeef;
+ buffer[0] = 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_GetTransmissionRetryTimeoutW, NULL, 0, (PBYTE) buffer, len, &needed);
+ ok( (res == ERROR_SUCCESS) && (buffer[0] == 1000000),
+ "returned %d with %u and %u for %d\n (expected ERROR_SUCCESS "
+ "for '1000000')\n", res, GetLastError(), needed, buffer[0]);
+
+ /* restore the original value */
+ RegDeleteValueA(hroot, TransmissionRetryTimeoutA);
+ if (org_value[0]) {
+ res = RegSetValueExA(hroot, TransmissionRetryTimeoutA, 0, REG_SZ, (PBYTE)org_value, lstrlenA(org_value)+1);
+ ok(res == ERROR_SUCCESS, "unable to restore original value (got %u): %s\n", res, org_value);
+ }
+
+ RegCloseKey(hroot);
+}
+
+/* ########################### */
+
+static void test_XcvDataPort_MonitorUI(void)
+{
+ DWORD res;
+ BYTE buffer[MAX_PATH + 2];
+ DWORD needed;
+ DWORD len;
+
+
+ /* ask for needed size */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, NULL, 0, &needed);
+ if (res == ERROR_INVALID_PARAMETER) {
+ skip("'MonitorUI' nor supported\n");
+ return;
+ }
+ ok( (res == ERROR_INSUFFICIENT_BUFFER) && (needed <= MAX_PATH),
+ "returned %d with %u and 0x%x (expected 'ERROR_INSUFFICIENT_BUFFER' "
+ " and '<= MAX_PATH')\n", res, GetLastError(), needed);
+
+ if (needed > MAX_PATH) {
+ skip("buffer overflow (%u)\n", needed);
+ return;
+ }
+ len = needed;
+
+ /* the command is required */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, emptyW, NULL, 0, NULL, 0, &needed);
+ ok( res == ERROR_INVALID_PARAMETER, "returned %d with %u and 0x%x "
+ "(expected 'ERROR_INVALID_PARAMETER')\n", res, GetLastError(), needed);
+
+ if (0) {
+ /* crash with native localspl.dll (w2k+xp) */
+ res = pXcvDataPort(hXcv, NULL, NULL, 0, buffer, MAX_PATH, &needed);
+ res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, NULL, len, &needed);
+ res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len, NULL);
+ }
+
+
+ /* hXcv is ignored for the command "MonitorUI" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(NULL, cmd_MonitorUIW, NULL, 0, buffer, len, &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
+ "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
+
+
+ /* pszDataName is case-sensitive */
+ memset(buffer, 0, len);
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_MonitorUI_lcaseW, NULL, 0, buffer, len, &needed);
+ ok( res == ERROR_INVALID_PARAMETER, "returned %d with %u and 0x%x "
+ "(expected 'ERROR_INVALID_PARAMETER')\n", res, GetLastError(), needed);
+
+ /* off by one: larger */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len+1, &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
+ "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
+
+
+ /* off by one: smaller */
+ /* the buffer is not modified for NT4, w2k, XP */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len-1, &needed);
+ ok( res == ERROR_INSUFFICIENT_BUFFER, "returned %d with %u and 0x%x "
+ "(expected 'ERROR_INSUFFICIENT_BUFFER')\n", res, GetLastError(), needed);
+
+ /* Normal use. The DLL-Name without a Path is returned */
+ memset(buffer, 0, len);
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_MonitorUIW, NULL, 0, buffer, len, &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
+ "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
+
+
+ /* small check without access-rights: */
+ if (!hXcv_noaccess) return;
+
+ /* The ACCESS_MASK is ignored for "MonitorUI" */
+ memset(buffer, 0, len);
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv_noaccess, cmd_MonitorUIW, NULL, 0, buffer, sizeof(buffer), &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u and 0x%x "
+ "(expected 'ERROR_SUCCESS')\n", res, GetLastError(), needed);
+}
+
+/* ########################### */
+
+static void test_XcvDataPort_PortIsValid(void)
+{
+ DWORD res;
+ DWORD needed;
+
+ /* normal use: "LPT1:" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, &needed);
+ if (res == ERROR_INVALID_PARAMETER) {
+ skip("'PostIsValid' not supported\n");
+ return;
+ }
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+
+
+ if (0) {
+ /* crash with native localspl.dll (w2k+xp) */
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, NULL, 0, NULL, 0, &needed);
+ }
+
+
+ /* hXcv is ignored for the command "PortIsValid" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(NULL, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, NULL);
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+
+ /* needed is ignored */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, NULL);
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+
+
+ /* cbInputData is ignored */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, 0, NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, 1, NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W) -1, NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W) -2, NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+
+ /* an empty name is not allowed */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) emptyW, sizeof(emptyW), NULL, 0, &needed);
+ ok( res == ERROR_PATH_NOT_FOUND,
+ "returned %d with %u and 0x%x (expected ERROR_PATH_NOT_FOUND)\n",
+ res, GetLastError(), needed);
+
+
+ /* a directory is not allowed */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) tempdirW, (lstrlenW(tempdirW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
+ /* XP(admin): ERROR_INVALID_NAME, XP(user): ERROR_PATH_NOT_FOUND, w2k ERROR_ACCESS_DENIED */
+ ok( (res == ERROR_INVALID_NAME) || (res == ERROR_PATH_NOT_FOUND) ||
+ (res == ERROR_ACCESS_DENIED), "returned %d with %u and 0x%x "
+ "(expected ERROR_INVALID_NAME, ERROR_PATH_NOT_FOUND or ERROR_ACCESS_DENIED)\n",
+ res, GetLastError(), needed);
+
+
+ /* test more valid well known Ports: */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_lpt2W, sizeof(portname_lpt2W), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_com1W, sizeof(portname_com1W), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_com2W, sizeof(portname_com2W), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) portname_fileW, sizeof(portname_fileW), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+
+ /* a normal, writable file is allowed */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv, cmd_PortIsValidW, (PBYTE) tempfileW, (lstrlenW(tempfileW) + 1) * sizeof(WCHAR), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS,
+ "returned %d with %u and 0x%x (expected ERROR_SUCCESS)\n",
+ res, GetLastError(), needed);
+
+
+ /* small check without access-rights: */
+ if (!hXcv_noaccess) return;
+
+ /* The ACCESS_MASK from XcvOpenPort is ignored in "PortIsValid" */
+ needed = (DWORD) 0xdeadbeef;
+ SetLastError(0xdeadbeef);
+ res = pXcvDataPort(hXcv_noaccess, cmd_PortIsValidW, (PBYTE) portname_lpt1W, sizeof(portname_lpt1W), NULL, 0, &needed);
+ ok( res == ERROR_SUCCESS, "returned %d with %u (expected ERROR_SUCCESS)\n", res, GetLastError());
+
+}
+
+/* ########################### */
+
+static void test_XcvOpenPort(void)
+{
+ DWORD res;
+ HANDLE hXcv2;
+
+
+ if (0)
+ {
+ /* crash with native localspl.dll (w2k+xp) */
+ res = pXcvOpenPort(NULL, SERVER_ACCESS_ADMINISTER, &hXcv2);
+ res = pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, NULL);
+ }
+
+
+ /* The returned handle is the result from a previous "spoolss.dll,DllAllocSplMem" */
+ SetLastError(0xdeadbeef);
+ hXcv2 = (HANDLE) 0xdeadbeef;
+ res = pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv2);
+ ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
+ if (res) pXcvClosePort(hXcv2);
+
+
+ /* The ACCESS_MASK is not checked in XcvOpenPort */
+ SetLastError(0xdeadbeef);
+ hXcv2 = (HANDLE) 0xdeadbeef;
+ res = pXcvOpenPort(emptyW, 0, &hXcv2);
+ ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
+ if (res) pXcvClosePort(hXcv2);
+
+
+ /* A copy of pszObject is saved in the Memory-Block */
+ SetLastError(0xdeadbeef);
+ hXcv2 = (HANDLE) 0xdeadbeef;
+ res = pXcvOpenPort(portname_lpt1W, SERVER_ALL_ACCESS, &hXcv2);
+ ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
+ if (res) pXcvClosePort(hXcv2);
+
+ SetLastError(0xdeadbeef);
+ hXcv2 = (HANDLE) 0xdeadbeef;
+ res = pXcvOpenPort(portname_fileW, SERVER_ALL_ACCESS, &hXcv2);
+ ok(res, "returned %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv2);
+ if (res) pXcvClosePort(hXcv2);
+
+}
+
+/* ########################### */
+
+#define GET_MONITOR_FUNC(name) \
+ if(numentries > 0) { \
+ numentries--; \
+ p##name = pm->Monitor.pfn##name ; \
+ }
+
+
+START_TEST(localmon)
+{
+ DWORD numentries;
+ DWORD res;
+
+ LoadLibraryA("winspool.drv");
+ /* This DLL does not exist on Win9x */
+ hdll = LoadLibraryA("localspl.dll");
+ if (!hdll) {
+ skip("localspl.dll cannot be loaded, most likely running on Win9x\n");
+ return;
+ }
+
+ tempdirW[0] = '\0';
+ tempfileW[0] = '\0';
+ res = GetTempPathW(MAX_PATH, tempdirW);
+ ok(res != 0, "with %u\n", GetLastError());
+ res = GetTempFileNameW(tempdirW, wineW, 0, tempfileW);
+ ok(res != 0, "with %u\n", GetLastError());
+
+ pInitializePrintMonitor = (void *) GetProcAddress(hdll, "InitializePrintMonitor");
+
+ if (!pInitializePrintMonitor) {
+ /* The Monitor for "Local Ports" was in a separate dll before w2k */
+ hlocalmon = LoadLibraryA("localmon.dll");
+ if (hlocalmon) {
+ pInitializePrintMonitor = (void *) GetProcAddress(hlocalmon, "InitializePrintMonitor");
+ }
+ }
+ if (!pInitializePrintMonitor) return;
+
+ /* Native localmon.dll / localspl.dll need a valid Port-Entry in:
+ a) since xp: HKLM\Software\Microsoft\Windows NT\CurrentVersion\Ports
+ b) up to w2k: Section "Ports" in win.ini
+ or InitializePrintMonitor fails. */
+ pm = pInitializePrintMonitor(Monitors_LocalPortW);
+ if (pm) {
+ numentries = (pm->dwMonitorSize ) / sizeof(VOID *);
+ /* NT4: 14, since w2k: 17 */
+ ok( numentries == 14 || numentries == 17,
+ "dwMonitorSize (%d) => %d Functions\n", pm->dwMonitorSize, numentries);
+
+ GET_MONITOR_FUNC(EnumPorts);
+ GET_MONITOR_FUNC(OpenPort);
+ GET_MONITOR_FUNC(OpenPortEx);
+ GET_MONITOR_FUNC(StartDocPort);
+ GET_MONITOR_FUNC(WritePort);
+ GET_MONITOR_FUNC(ReadPort);
+ GET_MONITOR_FUNC(EndDocPort);
+ GET_MONITOR_FUNC(ClosePort);
+ GET_MONITOR_FUNC(AddPort);
+ GET_MONITOR_FUNC(AddPortEx);
+ GET_MONITOR_FUNC(ConfigurePort);
+ GET_MONITOR_FUNC(DeletePort);
+ GET_MONITOR_FUNC(GetPrinterDataFromPort);
+ GET_MONITOR_FUNC(SetPortTimeOuts);
+ GET_MONITOR_FUNC(XcvOpenPort);
+ GET_MONITOR_FUNC(XcvDataPort);
+ GET_MONITOR_FUNC(XcvClosePort);
+
+ if ((pXcvOpenPort) && (pXcvDataPort) && (pXcvClosePort)) {
+ SetLastError(0xdeadbeef);
+ res = pXcvOpenPort(emptyW, SERVER_ACCESS_ADMINISTER, &hXcv);
+ ok(res, "hXcv: %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv);
+
+ SetLastError(0xdeadbeef);
+ res = pXcvOpenPort(emptyW, 0, &hXcv_noaccess);
+ ok(res, "hXcv_noaccess: %d with %u and %p (expected '!= 0')\n", res, GetLastError(), hXcv_noaccess);
+ }
+ }
+
+ test_InitializePrintMonitor();
+
+ find_installed_ports();
+
+ test_AddPort();
+ test_AddPortEx();
+ test_ClosePort();
+ test_ConfigurePort();
+ test_DeletePort();
+ test_EnumPorts();
+ test_OpenPort();
+
+ if ( !hXcv ) {
+ skip("Xcv not supported\n");
+ }
+ else
+ {
+ test_XcvClosePort();
+ test_XcvDataPort_AddPort();
+ test_XcvDataPort_ConfigureLPTPortCommandOK();
+ test_XcvDataPort_DeletePort();
+ test_XcvDataPort_GetTransmissionRetryTimeout();
+ test_XcvDataPort_MonitorUI();
+ test_XcvDataPort_PortIsValid();
+ test_XcvOpenPort();
+
+ pXcvClosePort(hXcv);
+ }
+ if (hXcv_noaccess) pXcvClosePort(hXcv_noaccess);
+
+ /* Cleanup our temporary file */
+ DeleteFileW(tempfileW);
+}
diff --git a/rostests/winetests/localspl/localspl.rbuild b/rostests/winetests/localspl/localspl.rbuild
new file mode 100644
index 00000000000..64e056ece75
--- /dev/null
+++ b/rostests/winetests/localspl/localspl.rbuild
@@ -0,0 +1,10 @@
+
+ -Wno-format
+ .
+ localmon.c
+ testlist.c
+ wine
+ kernel32
+ advapi32
+ ntdll
+
diff --git a/rostests/winetests/localspl/testlist.c b/rostests/winetests/localspl/testlist.c
new file mode 100644
index 00000000000..5e1945fded4
--- /dev/null
+++ b/rostests/winetests/localspl/testlist.c
@@ -0,0 +1,15 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_localmon(void);
+
+const struct test winetest_testlist[] =
+{
+ { "localmon", func_localmon },
+ { 0, 0 }
+};
diff --git a/rostests/winetests/localui/localui.c b/rostests/winetests/localui/localui.c
new file mode 100644
index 00000000000..cb7a1cff248
--- /dev/null
+++ b/rostests/winetests/localui/localui.c
@@ -0,0 +1,329 @@
+/*
+ * Unit test suite for the Local Printmonitor User Interface
+ *
+ * Copyright 2007 Detlef Riekenberg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include
+#include
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wingdi.h"
+#include "winnls.h"
+#include "winreg.h"
+
+#include "winspool.h"
+#include "ddk/winsplp.h"
+
+#include "wine/test.h"
+
+
+/* ##### */
+
+static HMODULE hdll;
+static PMONITORUI (WINAPI *pInitializePrintMonitorUI)(VOID);
+static PMONITORUI pui;
+static BOOL (WINAPI *pAddPortUI)(PCWSTR, HWND, PCWSTR, PWSTR *);
+static BOOL (WINAPI *pConfigurePortUI)(PCWSTR, HWND, PCWSTR);
+static BOOL (WINAPI *pDeletePortUI)(PCWSTR, HWND, PCWSTR);
+
+static WCHAR does_not_existW[] = {'d','o','e','s','_','n','o','t','_','e','x','i','s','t',0};
+static WCHAR emptyW[] = {0};
+static CHAR fmt_comA[] = {'C','O','M','%','u',':',0};
+static CHAR fmt_lptA[] = {'L','P','T','%','u',':',0};
+static WCHAR localportW[] = {'L','o','c','a','l',' ','P','o','r','t',0};
+static WCHAR portname_fileW[] = {'F','I','L','E',':',0};
+
+static LPBYTE pi_buffer;
+static DWORD pi_numports;
+static DWORD pi_needed;
+
+static PORT_INFO_2W * lpt_present;
+static PORT_INFO_2W * com_present;
+static PORT_INFO_2W * file_present;
+
+static LPWSTR lpt_absent;
+static LPWSTR com_absent;
+
+/* ########################### */
+
+static PORT_INFO_2W * find_portinfo2(LPWSTR pPort)
+{
+ PORT_INFO_2W * pi;
+ DWORD res;
+
+ if (!pi_buffer) {
+ res = EnumPortsW(NULL, 2, NULL, 0, &pi_needed, &pi_numports);
+ pi_buffer = HeapAlloc(GetProcessHeap(), 0, pi_needed);
+ SetLastError(0xdeadbeef);
+ res = EnumPortsW(NULL, 2, pi_buffer, pi_needed, &pi_needed, &pi_numports);
+ }
+ if (pi_buffer) {
+ pi = (PORT_INFO_2W *) pi_buffer;
+ res = 0;
+ while (pi_numports > res) {
+ if (lstrcmpiW(pi->pPortName, pPort) == 0) {
+ return pi;
+ }
+ pi++;
+ res++;
+ }
+ }
+ return NULL;
+}
+
+
+/* ########################### */
+
+static LPCSTR load_functions(void)
+{
+ LPCSTR ptr;
+
+ ptr = "localui.dll";
+ hdll = LoadLibraryA(ptr);
+ if (!hdll) return ptr;
+
+ ptr = "InitializePrintMonitorUI";
+ pInitializePrintMonitorUI = (VOID *) GetProcAddress(hdll, ptr);
+ if (!pInitializePrintMonitorUI) return ptr;
+
+ return NULL;
+}
+
+/* ###########################
+ * strdupW [internal]
+ */
+
+static LPWSTR strdupW(LPCWSTR strW)
+{
+ LPWSTR ptr;
+
+ ptr = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(strW) + 1) * sizeof(WCHAR));
+ if (ptr) {
+ lstrcpyW(ptr, strW);
+ }
+ return ptr;
+}
+
+/* ########################### */
+
+static void test_AddPortUI(void)
+{
+ DWORD res;
+ LPWSTR new_portname;
+
+ /* not present before w2k */
+ if (!pAddPortUI) {
+ skip("AddPortUI not found\n");
+ return;
+ }
+
+ SetLastError(0xdeadbeef);
+ res = pAddPortUI(NULL, NULL, NULL, NULL);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pAddPortUI(NULL, NULL, emptyW, NULL);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pAddPortUI(NULL, NULL, does_not_existW, NULL);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+
+ if (winetest_interactive) {
+ SetLastError(0xdeadbeef);
+ new_portname = NULL;
+ /*
+ * - On MSDN, you can read, that no dialogs should be displayed, when hWnd
+ * is NULL, but native localui does not care
+ * - when the new port already exist,
+ * TRUE is returned, but new_portname is NULL
+ * - when the new port starts with "COM" or "LPT",
+ * FALSE is returned with ERROR_NOT_SUPPORTED in windows
+ */
+ res = pAddPortUI(NULL, NULL, localportW, &new_portname);
+ ok( res ||
+ (GetLastError() == ERROR_CANCELLED) ||
+ (GetLastError() == ERROR_ACCESS_DENIED) ||
+ (GetLastError() == ERROR_NOT_SUPPORTED),
+ "got %d with %u and %p (expected '!= 0' or '0' with: "
+ "ERROR_CANCELLED, ERROR_ACCESS_DENIED or ERROR_NOT_SUPPORTED)\n",
+ res, GetLastError(), new_portname);
+
+ GlobalFree(new_portname);
+ }
+}
+
+/* ########################### */
+
+static void test_ConfigurePortUI(void)
+{
+ DWORD res;
+
+ /* not present before w2k */
+ if (!pConfigurePortUI) {
+ skip("ConfigurePortUI not found\n");
+ return;
+ }
+
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, NULL);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, emptyW);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+
+
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, does_not_existW);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+
+ if (winetest_interactive && lpt_present) {
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, lpt_present->pPortName);
+ ok( res ||
+ (GetLastError() == ERROR_CANCELLED) || (GetLastError() == ERROR_ACCESS_DENIED),
+ "got %d with %u (expected '!= 0' or '0' with: ERROR_CANCELLED or "
+ "ERROR_ACCESS_DENIED)\n", res, GetLastError());
+ }
+
+ if (lpt_absent) {
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, lpt_absent);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+ }
+
+ if (winetest_interactive && com_present) {
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, com_present->pPortName);
+ ok( res ||
+ (GetLastError() == ERROR_CANCELLED) || (GetLastError() == ERROR_ACCESS_DENIED),
+ "got %d with %u (expected '!= 0' or '0' with: ERROR_CANCELLED or "
+ "ERROR_ACCESS_DENIED)\n", res, GetLastError());
+ }
+
+ if (com_absent) {
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, com_absent);
+ ok( !res &&
+ ((GetLastError() == ERROR_UNKNOWN_PORT) || (GetLastError() == ERROR_INVALID_PRINTER_NAME)),
+ "got %d with %u (expected '0' with: ERROR_UNKNOWN_PORT or "
+ "ERROR_INVALID_PRINTER_NAME)\n", res, GetLastError());
+
+ }
+
+ if (winetest_interactive && file_present) {
+ SetLastError(0xdeadbeef);
+ res = pConfigurePortUI(NULL, NULL, portname_fileW);
+ ok( !res &&
+ ((GetLastError() == ERROR_CANCELLED) || (GetLastError() == ERROR_ACCESS_DENIED)),
+ "got %d with %u (expected '0' with: ERROR_CANCELLED or "
+ "ERROR_ACCESS_DENIED)\n", res, GetLastError());
+ }
+}
+
+/* ########################### */
+
+START_TEST(localui)
+{
+ LPCSTR ptr;
+ DWORD numentries;
+ PORT_INFO_2W * pi2;
+ WCHAR bufferW[16];
+ CHAR bufferA[16];
+ DWORD id;
+
+ /* localui.dll does not exist before w2k */
+ ptr = load_functions();
+ if (ptr) {
+ skip("%s not found\n", ptr);
+ return;
+ }
+
+ pui = pInitializePrintMonitorUI();
+ if (pui) {
+ numentries = (pui->dwMonitorUISize - sizeof(DWORD)) / sizeof(VOID *);
+ ok( numentries == 3,
+ "dwMonitorUISize (%d) => %d Functions\n", pui->dwMonitorUISize, numentries);
+
+ if (numentries > 2) {
+ pAddPortUI = pui->pfnAddPortUI;
+ pConfigurePortUI = pui->pfnConfigurePortUI;
+ pDeletePortUI = pui->pfnDeletePortUI;
+ }
+ }
+
+ /* find installed Ports */
+
+ id = 0;
+ /* "LPT1:" - "LPT9:" */
+ while (((lpt_present == NULL) || (lpt_absent == NULL)) && id < 9) {
+ id++;
+ sprintf(bufferA, fmt_lptA, id);
+ MultiByteToWideChar( CP_ACP, 0, bufferA, -1, bufferW, sizeof(bufferW)/sizeof(WCHAR) );
+ pi2 = find_portinfo2(bufferW);
+ if (pi2 && (lpt_present == NULL)) lpt_present = pi2;
+ if (!pi2 && (lpt_absent == NULL)) lpt_absent = strdupW(bufferW);
+ }
+
+ id = 0;
+ /* "COM1:" - "COM9:" */
+ while (((com_present == NULL) || (com_absent == NULL)) && id < 9) {
+ id++;
+ sprintf(bufferA, fmt_comA, id);
+ MultiByteToWideChar( CP_ACP, 0, bufferA, -1, bufferW, sizeof(bufferW)/sizeof(WCHAR) );
+ pi2 = find_portinfo2(bufferW);
+ if (pi2 && (com_present == NULL)) com_present = pi2;
+ if (!pi2 && (com_absent == NULL)) com_absent = strdupW(bufferW);
+ }
+
+ /* "FILE:" */
+ file_present = find_portinfo2(portname_fileW);
+
+ test_AddPortUI();
+ test_ConfigurePortUI();
+
+ /* cleanup */
+ HeapFree(GetProcessHeap(), 0, lpt_absent);
+ HeapFree(GetProcessHeap(), 0, com_absent);
+ HeapFree(GetProcessHeap(), 0, pi_buffer);
+}
diff --git a/rostests/winetests/localui/localui.rbuild b/rostests/winetests/localui/localui.rbuild
new file mode 100644
index 00000000000..b7ae0bd9957
--- /dev/null
+++ b/rostests/winetests/localui/localui.rbuild
@@ -0,0 +1,10 @@
+
+ -Wno-format
+ .
+ localui.c
+ testlist.c
+ wine
+ kernel32
+ winspool
+ ntdll
+
diff --git a/rostests/winetests/localui/testlist.c b/rostests/winetests/localui/testlist.c
new file mode 100644
index 00000000000..241a4dd483b
--- /dev/null
+++ b/rostests/winetests/localui/testlist.c
@@ -0,0 +1,15 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#define STANDALONE
+#include "wine/test.h"
+
+extern void func_localui(void);
+
+const struct test winetest_testlist[] =
+{
+ { "localui", func_localui },
+ { 0, 0 }
+};