tests: Don't depend on the static uuid libraries in the tests.
[wine] / dlls / mlang / tests / mlang.c
1 /*
2  * Unit test suite for MLANG APIs.
3  *
4  * Copyright 2004 Dmitry Timoshkov
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include <stdarg.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "initguid.h"
29 #include "mlang.h"
30
31 #include "wine/test.h"
32
33 #ifndef CP_UNICODE
34 #define CP_UNICODE 1200
35 #endif
36
37 #if 0
38 #define DUMP_CP_INFO
39 #define DUMP_SCRIPT_INFO
40
41 #if defined DUMP_CP_INFO || defined DUMP_SCRIPT_INFO
42 #include "wine/debug.h"
43 #endif
44 #endif /* 0 */
45
46 static BOOL (WINAPI *pGetCPInfoExA)(UINT, DWORD, LPCPINFOEXA);
47 static HRESULT (WINAPI *pConvertINetMultiByteToUnicode)(LPDWORD, DWORD, LPCSTR,
48                                                         LPINT, LPWSTR, LPINT);
49 static HRESULT (WINAPI *pConvertINetUnicodeToMultiByte)(LPDWORD, DWORD, LPCWSTR,
50                                                         LPINT, LPSTR, LPINT);
51
52 static BOOL init_function_ptrs(void)
53 {
54     HMODULE hMlang;
55
56     hMlang = LoadLibraryA("mlang.dll");
57     if (!hMlang)
58     {
59         skip("mlang not available\n");
60         return FALSE;
61     }
62
63     pConvertINetMultiByteToUnicode = (void *)GetProcAddress(hMlang, "ConvertINetMultiByteToUnicode");
64     pConvertINetUnicodeToMultiByte = (void *)GetProcAddress(hMlang, "ConvertINetUnicodeToMultiByte");
65
66     pGetCPInfoExA = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetCPInfoExA");
67
68     return TRUE;
69 }
70
71 #define TRACE_2 OutputDebugStringA
72
73 static CHAR string1[MAX_PATH], string2[MAX_PATH];
74
75 #define ok_w2(format, szString1, szString2) \
76 \
77     if (lstrcmpW(szString1, szString2) != 0) \
78     { \
79         WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
80         WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
81         ok(0, format, string1, string2); \
82     }
83
84 static void test_multibyte_to_unicode_translations(IMultiLanguage2 *iML2)
85 {
86     /* these APIs are broken regarding constness of the input buffer */
87     char stringA[] = "Just a test string\0"; /* double 0 for CP_UNICODE tests */
88     WCHAR stringW[] = {'J','u','s','t',' ','a',' ','t','e','s','t',' ','s','t','r','i','n','g',0};
89     char bufA[256];
90     WCHAR bufW[256];
91     UINT lenA, lenW, expected_len;
92     HRESULT ret;
93
94     /* IMultiLanguage2_ConvertStringToUnicode tests */
95
96     memset(bufW, 'x', sizeof(bufW));
97     lenA = 0;
98     lenW = sizeof(bufW)/sizeof(bufW[0]);
99     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
100     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
101     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
102     ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
103     ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
104
105     memset(bufW, 'x', sizeof(bufW));
106     lenA = -1;
107     lenW = sizeof(bufW)/sizeof(bufW[0]);
108     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
109     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
110     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
111     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
112     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
113     if (lenW < sizeof(bufW)/sizeof(bufW[0])) {
114        /* can only happen if the convert call fails */
115        ok(bufW[lenW] != 0, "buf should not be 0 terminated\n");
116        bufW[lenW] = 0; /* -1 doesn't include 0 terminator */
117     }
118     ok(!lstrcmpW(bufW, stringW), "bufW/stringW mismatch\n");
119
120     memset(bufW, 'x', sizeof(bufW));
121     lenA = -1;
122     lenW = 5;
123     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
124     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
125     ok(ret == E_FAIL, "IMultiLanguage2_ConvertStringToUnicode should fail: %08x\n", ret);
126     ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
127     /* still has to do partial conversion */
128     ok(!memcmp(bufW, stringW, 5 * sizeof(WCHAR)), "bufW/stringW mismatch\n");
129
130     memset(bufW, 'x', sizeof(bufW));
131     lenA = -1;
132     lenW = sizeof(bufW)/sizeof(bufW[0]);
133     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
134     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, CP_UNICODE, stringA, &lenA, bufW, &lenW);
135     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
136     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
137     ok(lenW == lstrlenW(stringW)/(int)sizeof(WCHAR), "wrong lenW %u\n", lenW);
138     ok(bufW[lenW] != 0, "buf should not be 0 terminated\n");
139     bufW[lenW] = 0; /* -1 doesn't include 0 terminator */
140     ok(!lstrcmpA((LPCSTR)bufW, stringA), "bufW/stringA mismatch\n");
141
142     memset(bufW, 'x', sizeof(bufW));
143     lenA = lstrlenA(stringA);
144     lenW = 0;
145     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, NULL, &lenW);
146     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
147     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
148     expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
149     ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
150
151     memset(bufW, 'x', sizeof(bufW));
152     lenA = lstrlenA(stringA);
153     lenW = sizeof(bufW)/sizeof(bufW[0]);
154     ret = pConvertINetMultiByteToUnicode(NULL, 1252, stringA, (INT *)&lenA, NULL, (INT *)&lenW);
155     ok(ret == S_OK, "ConvertINetMultiByteToUnicode failed: %08x\n", ret);
156     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
157     expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
158     ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
159
160     memset(bufW, 'x', sizeof(bufW));
161     lenA = lstrlenA(stringA);
162     lenW = 0;
163     ret = pConvertINetMultiByteToUnicode(NULL, 1252, stringA, (INT *)&lenA, NULL, (INT *)&lenW);
164     ok(ret == S_OK, "ConvertINetMultiByteToUnicode failed: %08x\n", ret);
165     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
166     expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
167     ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
168
169     /* IMultiLanguage2_ConvertStringFromUnicode tests */
170
171     memset(bufA, 'x', sizeof(bufA));
172     lenW = 0;
173     lenA = sizeof(bufA);
174     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
175     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
176     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
177     ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
178     ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
179
180     memset(bufA, 'x', sizeof(bufA));
181     lenW = -1;
182     lenA = sizeof(bufA);
183     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
184     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
185     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
186     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
187     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
188     ok(bufA[lenA] != 0, "buf should not be 0 terminated\n");
189     bufA[lenA] = 0; /* -1 doesn't include 0 terminator */
190     ok(!lstrcmpA(bufA, stringA), "bufA/stringA mismatch\n");
191
192     memset(bufA, 'x', sizeof(bufA));
193     lenW = -1;
194     lenA = 5;
195     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
196     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
197     ok(ret == E_FAIL, "IMultiLanguage2_ConvertStringFromUnicode should fail: %08x\n", ret);
198     ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
199     /* still has to do partial conversion */
200     ok(!memcmp(bufA, stringA, 5), "bufW/stringW mismatch\n");
201
202     memset(bufA, 'x', sizeof(bufA));
203     lenW = -1;
204     lenA = sizeof(bufA);
205     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
206     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, CP_UNICODE, stringW, &lenW, bufA, &lenA);
207     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
208     ok(lenA == lstrlenA(stringA) * (int)sizeof(WCHAR), "wrong lenA %u\n", lenA);
209     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
210     ok(bufA[lenA] != 0 && bufA[lenA+1] != 0, "buf should not be 0 terminated\n");
211     bufA[lenA] = 0; /* -1 doesn't include 0 terminator */
212     bufA[lenA+1] = 0; /* sizeof(WCHAR) */
213     ok(!lstrcmpW((LPCWSTR)bufA, stringW), "bufA/stringW mismatch\n");
214
215     memset(bufA, 'x', sizeof(bufA));
216     lenW = lstrlenW(stringW);
217     lenA = 0;
218     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, NULL, &lenA);
219     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
220     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
221     expected_len = WideCharToMultiByte(1252, 0, stringW, lenW, NULL, 0, NULL, NULL);
222     ok(lenA == expected_len, "expected lenA %u, got %u\n", expected_len, lenA);
223 }
224
225 static inline void cpinfo_cmp(MIMECPINFO *cpinfo1, MIMECPINFO *cpinfo2)
226 {
227     ok(cpinfo1->dwFlags == cpinfo2->dwFlags, "dwFlags mismatch: %08x != %08x\n", cpinfo1->dwFlags, cpinfo2->dwFlags);
228     ok(cpinfo1->uiCodePage == cpinfo2->uiCodePage, "uiCodePage mismatch: %u != %u\n", cpinfo1->uiCodePage, cpinfo2->uiCodePage);
229     ok(cpinfo1->uiFamilyCodePage == cpinfo2->uiFamilyCodePage, "uiFamilyCodePage mismatch: %u != %u\n", cpinfo1->uiFamilyCodePage, cpinfo2->uiFamilyCodePage);
230     ok(!lstrcmpW(cpinfo1->wszDescription, cpinfo2->wszDescription), "wszDescription mismatch\n");
231     ok(!lstrcmpW(cpinfo1->wszWebCharset, cpinfo2->wszWebCharset), "wszWebCharset mismatch\n");
232     ok(!lstrcmpW(cpinfo1->wszHeaderCharset, cpinfo2->wszHeaderCharset), "wszHeaderCharset mismatch\n");
233     ok(!lstrcmpW(cpinfo1->wszBodyCharset, cpinfo2->wszBodyCharset), "wszBodyCharset mismatch\n");
234     ok(!lstrcmpW(cpinfo1->wszFixedWidthFont, cpinfo2->wszFixedWidthFont), "wszFixedWidthFont mismatch\n");
235     ok(!lstrcmpW(cpinfo1->wszProportionalFont, cpinfo2->wszProportionalFont), "wszProportionalFont mismatch\n");
236     ok(cpinfo1->bGDICharset == cpinfo2->bGDICharset, "bGDICharset mismatch: %d != %d\n", cpinfo1->bGDICharset, cpinfo2->bGDICharset);
237 }
238
239 #ifdef DUMP_CP_INFO
240 static const char *dump_mime_flags(DWORD flags)
241 {
242     static char buf[1024];
243
244     buf[0] = 0;
245
246     if (flags & MIMECONTF_MAILNEWS) strcat(buf, " MIMECONTF_MAILNEWS");
247     if (flags & MIMECONTF_BROWSER) strcat(buf, " MIMECONTF_BROWSER");
248     if (flags & MIMECONTF_MINIMAL) strcat(buf, " MIMECONTF_MINIMAL");
249     if (flags & MIMECONTF_IMPORT) strcat(buf, " MIMECONTF_IMPORT");
250     if (flags & MIMECONTF_SAVABLE_MAILNEWS) strcat(buf, " MIMECONTF_SAVABLE_MAILNEWS");
251     if (flags & MIMECONTF_SAVABLE_BROWSER) strcat(buf, " MIMECONTF_SAVABLE_BROWSER");
252     if (flags & MIMECONTF_EXPORT) strcat(buf, " MIMECONTF_EXPORT");
253     if (flags & MIMECONTF_PRIVCONVERTER) strcat(buf, " MIMECONTF_PRIVCONVERTER");
254     if (flags & MIMECONTF_VALID) strcat(buf, " MIMECONTF_VALID");
255     if (flags & MIMECONTF_VALID_NLS) strcat(buf, " MIMECONTF_VALID_NLS");
256     if (flags & MIMECONTF_MIME_IE4) strcat(buf, " MIMECONTF_MIME_IE4");
257     if (flags & MIMECONTF_MIME_LATEST) strcat(buf, " MIMECONTF_MIME_LATEST");
258     if (flags & MIMECONTF_MIME_REGISTRY) strcat(buf, " MIMECONTF_MIME_REGISTRY");
259
260     return buf;
261 }
262 #endif
263
264 static BOOL check_convertible(IMultiLanguage2 *iML2, UINT from, UINT to)
265 {
266     CHAR convert[MAX_PATH];
267     BYTE dest[MAX_PATH];
268     HRESULT hr;
269     UINT srcsz, destsz;
270
271     static WCHAR strW[] = {'a','b','c',0};
272
273     srcsz = lstrlenW(strW) + 1;
274     destsz = MAX_PATH;
275     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, from, strW,
276                                                   &srcsz, convert, &destsz);
277     if (hr != S_OK)
278         return FALSE;
279
280     srcsz = -1;
281     destsz = MAX_PATH;
282     hr = IMultiLanguage2_ConvertString(iML2, NULL, from, to, (BYTE *)convert,
283                                        &srcsz, dest, &destsz);
284     if (hr != S_OK)
285         return FALSE;
286
287     return TRUE;
288 }
289
290 static void test_EnumCodePages(IMultiLanguage2 *iML2, DWORD flags)
291 {
292     IEnumCodePage *iEnumCP = NULL;
293     MIMECPINFO *cpinfo;
294     MIMECPINFO cpinfo2;
295     HRESULT ret;
296     ULONG i, n;
297     UINT total;
298
299     total = 0;
300     TRACE_2("Call IMultiLanguage2_GetNumberOfCodePageInfo\n");
301     ret = IMultiLanguage2_GetNumberOfCodePageInfo(iML2, &total);
302     ok(ret == S_OK && total != 0, "IMultiLanguage2_GetNumberOfCodePageInfo: expected S_OK/!0, got %08x/%u\n", ret, total);
303
304     trace("total mlang supported codepages %u\n", total);
305
306     TRACE_2("Call IMultiLanguage2_EnumCodePages\n");
307     ret = IMultiLanguage2_EnumCodePages(iML2, flags, LANG_NEUTRAL, &iEnumCP);
308     trace("IMultiLanguage2_EnumCodePages = %08x, iEnumCP = %p\n", ret, iEnumCP);
309     ok(ret == S_OK && iEnumCP, "IMultiLanguage2_EnumCodePages: expected S_OK/!NULL, got %08x/%p\n", ret, iEnumCP);
310
311     TRACE_2("Call IEnumCodePage_Reset\n");
312     ret = IEnumCodePage_Reset(iEnumCP);
313     ok(ret == S_OK, "IEnumCodePage_Reset: expected S_OK, got %08x\n", ret);
314     n = 65536;
315     TRACE_2("Call IEnumCodePage_Next\n");
316     ret = IEnumCodePage_Next(iEnumCP, 0, NULL, &n);
317     ok(n == 0 && ret == S_FALSE, "IEnumCodePage_Next: expected 0/S_FALSE, got %u/%08x\n", n, ret);
318     TRACE_2("Call IEnumCodePage_Next\n");
319     ret = IEnumCodePage_Next(iEnumCP, 0, NULL, NULL);
320     ok(ret == S_FALSE, "IEnumCodePage_Next: expected S_FALSE, got %08x\n", ret);
321
322     cpinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*cpinfo) * total * 2);
323
324     n = total * 2;
325     TRACE_2("Call IEnumCodePage_Next\n");
326     ret = IEnumCodePage_Next(iEnumCP, 0, cpinfo, &n);
327     trace("IEnumCodePage_Next = %08x, n = %u\n", ret, n);
328     ok(ret == S_FALSE && n == 0, "IEnumCodePage_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
329
330     n = total * 2;
331     TRACE_2("Call IEnumCodePage_Next\n");
332     ret = IEnumCodePage_Next(iEnumCP, n, cpinfo, &n);
333     ok(ret == S_OK && n != 0, "IEnumCodePage_Next: expected S_OK/!0, got %08x/%u\n", ret, n);
334
335     trace("flags %08x, enumerated codepages %u\n", flags, n);
336
337     if (!flags)
338     {
339         ok(n == total, "IEnumCodePage_Next: expected %u, got %u\n", total, n);
340
341         flags = MIMECONTF_MIME_LATEST;
342     }
343
344     total = n;
345
346     for (i = 0; i < n; i++)
347     {
348         CHARSETINFO csi;
349         MIMECSETINFO mcsi;
350         BOOL convertible;
351         HRESULT check = S_OK;
352         static const WCHAR autoW[] = {'_','a','u','t','o',0};
353
354 #ifdef DUMP_CP_INFO
355         trace("MIMECPINFO #%u:\n"
356               "dwFlags %08x %s\n"
357               "uiCodePage %u\n"
358               "uiFamilyCodePage %u\n"
359               "wszDescription %s\n"
360               "wszWebCharset %s\n"
361               "wszHeaderCharset %s\n"
362               "wszBodyCharset %s\n"
363               "wszFixedWidthFont %s\n"
364               "wszProportionalFont %s\n"
365               "bGDICharset %d\n\n",
366               i,
367               cpinfo[i].dwFlags, dump_mime_flags(cpinfo[i].dwFlags),
368               cpinfo[i].uiCodePage,
369               cpinfo[i].uiFamilyCodePage,
370               wine_dbgstr_w(cpinfo[i].wszDescription),
371               wine_dbgstr_w(cpinfo[i].wszWebCharset),
372               wine_dbgstr_w(cpinfo[i].wszHeaderCharset),
373               wine_dbgstr_w(cpinfo[i].wszBodyCharset),
374               wine_dbgstr_w(cpinfo[i].wszFixedWidthFont),
375               wine_dbgstr_w(cpinfo[i].wszProportionalFont),
376               cpinfo[i].bGDICharset);
377 #endif
378         ok(cpinfo[i].dwFlags & flags, "enumerated flags %08x do not include requested %08x\n", cpinfo[i].dwFlags, flags);
379
380         if (TranslateCharsetInfo((DWORD *)cpinfo[i].uiFamilyCodePage, &csi, TCI_SRCCODEPAGE))
381             ok(cpinfo[i].bGDICharset == csi.ciCharset, "%d != %d\n", cpinfo[i].bGDICharset, csi.ciCharset);
382         else
383             trace("TranslateCharsetInfo failed for cp %u\n", cpinfo[i].uiFamilyCodePage);
384
385         trace("%u: codepage %u family %u\n", i, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
386
387         /* Win95 does not support UTF-7 */
388         if (cpinfo[i].uiCodePage == CP_UTF7) continue;
389
390         /* support files for some codepages might be not installed, or
391          * the codepage is just an alias.
392          */
393         if (IsValidCodePage(cpinfo[i].uiCodePage))
394         {
395             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
396             ret = IMultiLanguage2_IsConvertible(iML2, cpinfo[i].uiCodePage, CP_UNICODE);
397             ok(ret == S_OK, "IMultiLanguage2_IsConvertible(%u -> CP_UNICODE) = %08x\n", cpinfo[i].uiCodePage, ret);
398             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
399             ret = IMultiLanguage2_IsConvertible(iML2, CP_UNICODE, cpinfo[i].uiCodePage);
400             ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UNICODE -> %u) = %08x\n", cpinfo[i].uiCodePage, ret);
401
402             convertible = check_convertible(iML2, cpinfo[i].uiCodePage, CP_UTF8);
403             if (!convertible)
404                 check = S_FALSE;
405
406             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
407             ret = IMultiLanguage2_IsConvertible(iML2, cpinfo[i].uiCodePage, CP_UTF8);
408             ok(ret == check, "IMultiLanguage2_IsConvertible(%u -> CP_UTF8) = %08x\n", cpinfo[i].uiCodePage, ret);
409             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
410             ret = IMultiLanguage2_IsConvertible(iML2, CP_UTF8, cpinfo[i].uiCodePage);
411             ok(ret == check, "IMultiLanguage2_IsConvertible(CP_UTF8 -> %u) = %08x\n", cpinfo[i].uiCodePage, ret);
412         }
413         else
414             trace("IsValidCodePage failed for cp %u\n", cpinfo[i].uiCodePage);
415
416         ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszWebCharset, &mcsi);
417         /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
418         if (memcmp(cpinfo[i].wszWebCharset, autoW, 5 * sizeof(WCHAR)))
419         {
420             ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
421 #ifdef DUMP_CP_INFO
422             trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszWebCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
423 #endif
424             ok(!lstrcmpiW(cpinfo[i].wszWebCharset, mcsi.wszCharset),
425 #ifdef DUMP_CP_INFO
426                 "%s != %s\n",
427                 wine_dbgstr_w(cpinfo[i].wszWebCharset), wine_dbgstr_w(mcsi.wszCharset));
428 #else
429                 "wszWebCharset mismatch\n");
430 #endif
431
432         if (0)
433         {
434             /* native mlang returns completely messed up encodings in some cases */
435             ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
436                 "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
437             ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
438                 "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
439         }
440         }
441
442         ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszHeaderCharset, &mcsi);
443         /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
444         if (memcmp(cpinfo[i].wszHeaderCharset, autoW, 5 * sizeof(WCHAR)))
445         {
446             ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
447 #ifdef DUMP_CP_INFO
448             trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszHeaderCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
449 #endif
450             ok(!lstrcmpiW(cpinfo[i].wszHeaderCharset, mcsi.wszCharset),
451 #ifdef DUMP_CP_INFO
452                 "%s != %s\n",
453                 wine_dbgstr_w(cpinfo[i].wszHeaderCharset), wine_dbgstr_w(mcsi.wszCharset));
454 #else
455                 "wszHeaderCharset mismatch\n");
456 #endif
457
458         if (0)
459         {
460             /* native mlang returns completely messed up encodings in some cases */
461             ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
462                 "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
463             ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
464                 "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
465         }
466         }
467
468         ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszBodyCharset, &mcsi);
469         /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
470         if (memcmp(cpinfo[i].wszBodyCharset, autoW, 5 * sizeof(WCHAR)))
471         {
472             ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
473 #ifdef DUMP_CP_INFO
474             trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszBodyCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
475 #endif
476             ok(!lstrcmpiW(cpinfo[i].wszBodyCharset, mcsi.wszCharset),
477 #ifdef DUMP_CP_INFO
478                 "%s != %s\n",
479                 wine_dbgstr_w(cpinfo[i].wszBodyCharset), wine_dbgstr_w(mcsi.wszCharset));
480 #else
481                 "wszBodyCharset mismatch\n");
482 #endif
483
484         if (0)
485         {
486             /* native mlang returns completely messed up encodings in some cases */
487             ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
488                 "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
489             ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
490                 "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
491         }
492         }
493     }
494
495     /* now IEnumCodePage_Next should fail, since pointer is at the end */
496     n = 1;
497     ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
498     ok(ret == S_FALSE && n == 0, "IEnumCodePage_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
499
500     ret = IEnumCodePage_Reset(iEnumCP);
501     ok(ret == S_OK, "IEnumCodePage_Reset: expected S_OK, got %08x\n", ret);
502     n = 0;
503     ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
504     ok(n == 1 && ret == S_OK, "IEnumCodePage_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
505     cpinfo_cmp(&cpinfo[0], &cpinfo2);
506
507     if (0)
508     {
509     /* Due to a bug in MS' implementation of IEnumCodePage_Skip
510      * it's not used here.
511      */
512     ret = IEnumCodePage_Skip(iEnumCP, 1);
513     ok(ret == S_OK, "IEnumCodePage_Skip: expected S_OK, got %08x\n", ret);
514     }
515     for (i = 0; i < total - 1; i++)
516     {
517         n = 0;
518         ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
519         ok(n == 1 && ret == S_OK, "IEnumCodePage_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
520         cpinfo_cmp(&cpinfo[i + 1], &cpinfo2);
521     }
522
523     HeapFree(GetProcessHeap(), 0, cpinfo);
524     IEnumCodePage_Release(iEnumCP);
525 }
526
527 static inline void scriptinfo_cmp(SCRIPTINFO *sinfo1, SCRIPTINFO *sinfo2)
528 {
529     ok(sinfo1->ScriptId == sinfo2->ScriptId, "ScriptId mismatch: %d != %d\n", sinfo1->ScriptId, sinfo2->ScriptId);
530     ok(sinfo1->uiCodePage == sinfo2->uiCodePage, "uiCodePage mismatch: %u != %u\n", sinfo1->uiCodePage, sinfo2->uiCodePage);
531     ok(!lstrcmpW(sinfo1->wszDescription, sinfo2->wszDescription), "wszDescription mismatch\n");
532     ok(!lstrcmpW(sinfo1->wszFixedWidthFont, sinfo2->wszFixedWidthFont), "wszFixedWidthFont mismatch\n");
533     ok(!lstrcmpW(sinfo1->wszProportionalFont, sinfo2->wszProportionalFont), "wszProportionalFont mismatch\n");
534 }
535
536 static void test_EnumScripts(IMultiLanguage2 *iML2, DWORD flags)
537 {
538     IEnumScript *iEnumScript = NULL;
539     SCRIPTINFO *sinfo;
540     SCRIPTINFO sinfo2;
541     HRESULT ret;
542     ULONG i, n;
543     UINT total;
544
545     total = 0;
546     TRACE_2("Call IMultiLanguage2_GetNumberOfScripts\n");
547     ret = IMultiLanguage2_GetNumberOfScripts(iML2, &total);
548     ok(ret == S_OK && total != 0, "IMultiLanguage2_GetNumberOfScripts: expected S_OK/!0, got %08x/%u\n", ret, total);
549
550     trace("total mlang supported scripts %u\n", total);
551
552     TRACE_2("Call IMultiLanguage2_EnumScripts\n");
553     ret = IMultiLanguage2_EnumScripts(iML2, flags, LANG_NEUTRAL, &iEnumScript);
554     trace("IMultiLanguage2_EnumScripts = %08x, iEnumScript = %p\n", ret, iEnumScript);
555     ok(ret == S_OK && iEnumScript, "IMultiLanguage2_EnumScripts: expected S_OK/!NULL, got %08x/%p\n", ret, iEnumScript);
556
557     TRACE_2("Call IEnumScript_Reset\n");
558     ret = IEnumScript_Reset(iEnumScript);
559     ok(ret == S_OK, "IEnumScript_Reset: expected S_OK, got %08x\n", ret);
560     n = 65536;
561     TRACE_2("Call IEnumScript_Next\n");
562     ret = IEnumScript_Next(iEnumScript, 0, NULL, &n);
563     ok(n == 65536 && ret == E_FAIL, "IEnumScript_Next: expected 65536/E_FAIL, got %u/%08x\n", n, ret);
564     TRACE_2("Call IEnumScript_Next\n");
565     ret = IEnumScript_Next(iEnumScript, 0, NULL, NULL);
566     ok(ret == E_FAIL, "IEnumScript_Next: expected E_FAIL, got %08x\n", ret);
567
568     sinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*sinfo) * total * 2);
569
570     n = total * 2;
571     TRACE_2("Call IEnumScript_Next\n");
572     ret = IEnumScript_Next(iEnumScript, 0, sinfo, &n);
573     ok(ret == S_FALSE && n == 0, "IEnumScript_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
574
575     n = total * 2;
576     TRACE_2("Call IEnumScript_Next\n");
577     ret = IEnumScript_Next(iEnumScript, n, sinfo, &n);
578     ok(ret == S_OK && n != 0, "IEnumScript_Next: expected S_OK, got %08x/%u\n", ret, n);
579
580     trace("flags %08x, enumerated scripts %u\n", flags, n);
581
582     if (!flags)
583     {
584         ok(n == total, "IEnumScript_Next: expected %u, got %u\n", total, n);
585         flags = SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM;
586     }
587
588     total = n;
589
590     for (i = 0; pGetCPInfoExA && i < n; i++)
591     {
592 #ifdef DUMP_SCRIPT_INFO
593         trace("SCRIPTINFO #%u:\n"
594               "ScriptId %08x\n"
595               "uiCodePage %u\n"
596               "wszDescription %s\n"
597               "wszFixedWidthFont %s\n"
598               "wszProportionalFont %s\n\n",
599               i,
600               sinfo[i].ScriptId,
601               sinfo[i].uiCodePage,
602               wine_dbgstr_w(sinfo[i].wszDescription),
603               wine_dbgstr_w(sinfo[i].wszFixedWidthFont),
604               wine_dbgstr_w(sinfo[i].wszProportionalFont));
605 #endif
606         trace("%u codepage %u\n", i, sinfo[i].uiCodePage);
607     }
608
609     /* now IEnumScript_Next should fail, since pointer is at the end */
610     n = 1;
611     ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
612     ok(ret == S_FALSE && n == 0, "IEnumScript_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
613
614     ret = IEnumScript_Reset(iEnumScript);
615     ok(ret == S_OK, "IEnumScript_Reset: expected S_OK, got %08x\n", ret);
616     n = 0;
617     ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
618     ok(n == 1 && ret == S_OK, "IEnumScript_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
619     scriptinfo_cmp(&sinfo[0], &sinfo2);
620
621     if (0)
622     {
623     /* Due to a bug in MS' implementation of IEnumScript_Skip
624      * it's not used here.
625      */
626     ret = IEnumScript_Skip(iEnumScript, 1);
627     ok(ret == S_OK, "IEnumScript_Skip: expected S_OK, got %08x\n", ret);
628     }
629     for (i = 0; i < total - 1; i++)
630     {
631         n = 0;
632         ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
633         ok(n == 1 && ret == S_OK, "IEnumScript_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
634         scriptinfo_cmp(&sinfo[i + 1], &sinfo2);
635     }
636
637     HeapFree(GetProcessHeap(), 0, sinfo);
638     IEnumScript_Release(iEnumScript);
639 }
640
641 static void IMLangFontLink_Test(IMLangFontLink* iMLFL)
642 {
643     DWORD   dwCodePages = 0;
644     DWORD   dwManyCodePages = 0;
645     UINT    CodePage = 0;
646
647     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 932, &dwCodePages)==S_OK,
648             "IMLangFontLink_CodePageToCodePages failed\n");
649     ok (dwCodePages != 0, "No CodePages returned\n");
650     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwCodePages, 1035,
651                 &CodePage)==S_OK, 
652             "IMLangFontLink_CodePagesToCodePage failed\n");
653     ok(CodePage == 932, "Incorrect CodePage Returned (%i)\n",CodePage);
654
655     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 1252, &dwCodePages)==S_OK,
656             "IMLangFontLink_CodePageToCodePages failed\n");
657     dwManyCodePages = dwManyCodePages | dwCodePages;
658     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 1256, &dwCodePages)==S_OK,
659             "IMLangFontLink_CodePageToCodePages failed\n");
660     dwManyCodePages = dwManyCodePages | dwCodePages;
661     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 874, &dwCodePages)==S_OK,
662             "IMLangFontLink_CodePageToCodePages failed\n");
663     dwManyCodePages = dwManyCodePages | dwCodePages;
664
665     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 1256,
666                 &CodePage)==S_OK, 
667             "IMLangFontLink_CodePagesToCodePage failed\n");
668     ok(CodePage == 1256, "Incorrect CodePage Returned (%i)\n",CodePage);
669
670     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 936,
671                 &CodePage)==S_OK, 
672             "IMLangFontLink_CodePagesToCodePage failed\n");
673     ok(CodePage == 1252, "Incorrect CodePage Returned (%i)\n",CodePage);
674 }
675
676 /* copied from libs/wine/string.c */
677 WCHAR *strstrW(const WCHAR *str, const WCHAR *sub)
678 {
679     while (*str)
680     {
681         const WCHAR *p1 = str, *p2 = sub;
682         while (*p1 && *p2 && *p1 == *p2) { p1++; p2++; }
683         if (!*p2) return (WCHAR *)str;
684         str++;
685     }
686     return NULL;
687 }
688
689 static void test_rfc1766(IMultiLanguage2 *iML2)
690 {
691     IEnumRfc1766 *pEnumRfc1766;
692     RFC1766INFO info;
693     ULONG n;
694     HRESULT ret;
695     BSTR rfcstr;
696
697     ret = IMultiLanguage2_EnumRfc1766(iML2, LANG_NEUTRAL, &pEnumRfc1766);
698     ok(ret == S_OK, "IMultiLanguage2_EnumRfc1766 error %08x\n", ret);
699
700     while (1)
701     {
702         ret = IEnumRfc1766_Next(pEnumRfc1766, 1, &info, &n);
703         if (ret != S_OK) break;
704
705 #ifdef DUMP_CP_INFO
706         trace("lcid %04x rfc_name %s locale_name %s\n",
707               info.lcid, wine_dbgstr_w(info.wszRfc1766), wine_dbgstr_w(info.wszLocaleName));
708 #endif
709
710         ok(n == 1, "couldn't fetch 1 RFC1766INFO structure\n");
711
712         /* verify the Rfc1766 value */
713         ret = IMultiLanguage2_GetRfc1766FromLcid(iML2, info.lcid, &rfcstr);
714         ok(ret == S_OK, "Expected S_OK, got %08x\n", ret);
715
716         /* not an exact 1:1 correspondence between lcid and rfc1766 in the
717          * mlang database, e.g., nb-no -> 1044 -> no */
718         ok(strstrW(info.wszRfc1766, rfcstr) != NULL,
719            "Expected matching locale names\n");
720
721         SysFreeString(rfcstr);
722     }
723     IEnumRfc1766_Release(pEnumRfc1766);
724 }
725
726 static void test_GetLcidFromRfc1766(IMultiLanguage2 *iML2)
727 {
728     LCID lcid;
729     HRESULT ret;
730
731     static WCHAR e[] = { 'e',0 };
732     static WCHAR en[] = { 'e','n',0 };
733     static WCHAR empty[] = { 0 };
734     static WCHAR dash[] = { '-',0 };
735     static WCHAR e_dash[] = { 'e','-',0 };
736     static WCHAR en_gb[] = { 'e','n','-','g','b',0 };
737     static WCHAR en_us[] = { 'e','n','-','u','s',0 };
738     static WCHAR en_them[] = { 'e','n','-','t','h','e','m',0 };
739     static WCHAR english[] = { 'e','n','g','l','i','s','h',0 };
740
741     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, NULL, en);
742     ok(ret == E_INVALIDARG, "GetLcidFromRfc1766 returned: %08x\n", ret);
743
744     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, NULL);
745     ok(ret == E_INVALIDARG, "GetLcidFromRfc1766 returned: %08x\n", ret);
746
747     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, e);
748     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
749
750     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, empty);
751     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
752
753     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, dash);
754     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
755
756     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, e_dash);
757     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
758
759     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_them);
760     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
761
762     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, english);
763     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
764
765     lcid = 0;
766
767     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en);
768     ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
769     ok(lcid == 9, "got wrong lcid: %04x\n", lcid);
770
771     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_gb);
772     ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
773     ok(lcid == 0x809, "got wrong lcid: %04x\n", lcid);
774
775     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_us);
776     ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
777     ok(lcid == 0x409, "got wrong lcid: %04x\n", lcid);
778 }
779
780 static void test_GetRfc1766FromLcid(IMultiLanguage2 *iML2)
781 {
782     HRESULT hr;
783     BSTR rfcstr;
784     LCID lcid;
785
786     static WCHAR kok[] = {'k','o','k',0};
787
788     hr = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, kok);
789     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
790
791     hr = IMultiLanguage2_GetRfc1766FromLcid(iML2, lcid, &rfcstr);
792     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
793     ok_w2("Expected \"%s\",  got \"%s\"n", kok, rfcstr);
794
795     SysFreeString(rfcstr);
796 }
797
798 static void test_IMultiLanguage2_ConvertStringFromUnicode(IMultiLanguage2 *iML2)
799 {
800     CHAR dest[MAX_PATH];
801     CHAR invariate[MAX_PATH];
802     UINT srcsz, destsz;
803     HRESULT hr;
804
805     static WCHAR src[] = {'a','b','c',0};
806
807     memset(invariate, 'x', sizeof(invariate));
808
809     /* pSrcStr NULL */
810     memset(dest, 'x', sizeof(dest));
811     srcsz = lstrlenW(src) + 1;
812     destsz = sizeof(dest);
813     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, NULL,
814                                                   &srcsz, dest, &destsz);
815     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
816     ok(srcsz == lstrlenW(src) + 1,
817        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
818     ok(!memcmp(dest, invariate, sizeof(dest)),
819        "Expected dest to be unchanged, got %s\n", dest);
820     ok(destsz == 0, "Expected 0, got %u\n", destsz);
821
822     /* pcSrcSize NULL */
823     memset(dest, 'x', sizeof(dest));
824     destsz = sizeof(dest);
825     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
826                                                   NULL, dest, &destsz);
827     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
828     ok(!strncmp(dest, "abc", 3),
829        "Expected first three chars to be \"abc\"\n");
830     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
831        "Expected rest of dest to be unchanged, got %s\n", dest);
832     ok(destsz == lstrlenW(src),
833        "Expected %u, got %u\n", lstrlenW(src), destsz);
834
835     /* both pSrcStr and pcSrcSize NULL */
836     memset(dest, 'x', sizeof(dest));
837     destsz = sizeof(dest);
838     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, NULL,
839                                                   NULL, dest, &destsz);
840     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
841     ok(!memcmp(dest, invariate, sizeof(dest)),
842        "Expected dest to be unchanged, got %s\n", dest);
843     ok(destsz == 0, "Expected 0, got %u\n", destsz);
844
845     /* pDstStr NULL */
846     memset(dest, 'x', sizeof(dest));
847     srcsz = lstrlenW(src) + 1;
848     destsz = sizeof(dest);
849     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
850                                                   &srcsz, NULL, &destsz);
851     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
852     ok(srcsz == lstrlenW(src) + 1,
853        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
854     ok(destsz == lstrlenW(src) + 1,
855        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
856
857     /* pcDstSize NULL */
858     memset(dest, 'x', sizeof(dest));
859     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
860                                                   &srcsz, dest, NULL);
861     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
862     ok(srcsz == lstrlenW(src) + 1,
863        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
864     ok(!memcmp(dest, invariate, sizeof(dest)),
865        "Expected dest to be unchanged, got %s\n", dest);
866
867     /* pcSrcSize is 0 */
868     memset(dest, 'x', sizeof(dest));
869     srcsz = 0;
870     destsz = sizeof(dest);
871     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
872                                                   &srcsz, dest, &destsz);
873     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
874     ok(srcsz == 0, "Expected 0, got %u\n", srcsz);
875     ok(!memcmp(dest, invariate, sizeof(dest)),
876        "Expected dest to be unchanged, got %s\n", dest);
877     ok(destsz == 0, "Expected 0, got %u\n", destsz);
878
879     /* pcSrcSize does not include NULL terminator */
880     memset(dest, 'x', sizeof(dest));
881     srcsz = lstrlenW(src);
882     destsz = sizeof(dest);
883     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
884                                                   &srcsz, dest, &destsz);
885     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
886     ok(srcsz == lstrlenW(src),
887        "Expected %u, got %u\n", lstrlenW(src), srcsz);
888     ok(!strncmp(dest, "abc", 3), "Expected first three chars to be \"abc\"\n");
889     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
890        "Expected rest of dest to be unchanged, got %s\n", dest);
891     ok(destsz == lstrlenW(src),
892        "Expected %u, got %u\n", lstrlenW(src), destsz);
893
894     /* pcSrcSize includes NULL terminator */
895     memset(dest, 'x', sizeof(dest));
896     srcsz = lstrlenW(src) + 1;
897     destsz = sizeof(dest);
898     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
899                                                   &srcsz, dest, &destsz);
900     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
901     ok(srcsz == lstrlenW(src) + 1, "Expected 3, got %u\n", srcsz);
902     ok(!lstrcmpA(dest, "abc"), "Expected \"abc\", got \"%s\"\n", dest);
903     ok(destsz == lstrlenW(src) + 1,
904        "Expected %u, got %u\n", lstrlenW(src) + 1, destsz);
905
906     /* pcSrcSize is -1 */
907     memset(dest, 'x', sizeof(dest));
908     srcsz = -1;
909     destsz = sizeof(dest);
910     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
911                                                   &srcsz, dest, &destsz);
912     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
913     ok(srcsz == lstrlenW(src),
914        "Expected %u, got %u\n", lstrlenW(src), srcsz);
915     ok(!strncmp(dest, "abc", 3), "Expected first three chars to be \"abc\"\n");
916     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
917        "Expected rest of dest to be unchanged, got %s\n", dest);
918     ok(destsz == lstrlenW(src),
919        "Expected %u, got %u\n", lstrlenW(src), destsz);
920
921     /* pcDstSize is 0 */
922     memset(dest, 'x', sizeof(dest));
923     srcsz = lstrlenW(src) + 1;
924     destsz = 0;
925     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
926                                                   &srcsz, dest, &destsz);
927     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
928     ok(srcsz == lstrlenW(src) + 1,
929        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
930     ok(!memcmp(dest, invariate, sizeof(dest)),
931        "Expected dest to be unchanged, got %s\n", dest);
932     ok(destsz == lstrlenW(src) + 1,
933        "Expected %u, got %u\n", lstrlenW(src) + 1, destsz);
934
935     /* pcDstSize is not large enough */
936     memset(dest, 'x', sizeof(dest));
937     srcsz = lstrlenW(src) + 1;
938     destsz = lstrlenW(src);
939     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
940                                                   &srcsz, dest, &destsz);
941     ok(hr == E_FAIL, "Expected E_FAIL, got %08x\n", hr);
942     ok(srcsz == 0, "Expected 0, got %u\n", srcsz);
943     ok(!strncmp(dest, "abc", 3), "Expected first three chars to be \"abc\"\n");
944     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
945        "Expected rest of dest to be unchanged, got %s\n", dest);
946     ok(destsz == 0, "Expected 0, got %u\n", srcsz);
947
948     /* pcDstSize (bytes) does not leave room for NULL terminator */
949     memset(dest, 'x', sizeof(dest));
950     srcsz = lstrlenW(src) + 1;
951     destsz = lstrlenW(src) * sizeof(WCHAR);
952     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, src,
953                                                   &srcsz, dest, &destsz);
954     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
955     ok(srcsz == lstrlenW(src) + 1,
956        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
957     ok(!lstrcmpA(dest, "abc"), "Expected \"abc\", got \"%s\"\n", dest);
958     ok(destsz == lstrlenW(src) + 1,
959        "Expected %u, got %u\n", lstrlenW(src) + 1, destsz);
960 }
961
962 static void test_ConvertINetUnicodeToMultiByte(void)
963 {
964     CHAR dest[MAX_PATH];
965     CHAR invariate[MAX_PATH];
966     INT srcsz, destsz;
967     HRESULT hr;
968
969     static WCHAR src[] = {'a','b','c',0};
970
971     memset(invariate, 'x', sizeof(invariate));
972
973     /* lpSrcStr NULL */
974     memset(dest, 'x', sizeof(dest));
975     srcsz = lstrlenW(src) + 1;
976     destsz = sizeof(dest);
977     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, NULL, &srcsz, dest, &destsz);
978     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
979     ok(srcsz == lstrlenW(src) + 1,
980        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
981     ok(!memcmp(dest, invariate, sizeof(dest)),
982        "Expected dest to be unchanged, got %s\n", dest);
983     ok(destsz == 0, "Expected 0, got %u\n", destsz);
984
985     /* lpnWideCharCount NULL */
986     memset(dest, 'x', sizeof(dest));
987     destsz = sizeof(dest);
988     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, NULL, dest, &destsz);
989     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
990     ok(!strncmp(dest, "abc", 3),
991        "Expected first three chars to be \"abc\"\n");
992     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
993        "Expected rest of dest to be unchanged, got %s\n", dest);
994     ok(destsz == lstrlenW(src),
995        "Expected %u, got %u\n", lstrlenW(src), destsz);
996
997     /* both lpSrcStr and lpnWideCharCount NULL */
998     memset(dest, 'x', sizeof(dest));
999     destsz = sizeof(dest);
1000     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, NULL, NULL, dest, &destsz);
1001     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1002     ok(!memcmp(dest, invariate, sizeof(dest)),
1003        "Expected dest to be unchanged, got %s\n", dest);
1004     ok(destsz == 0, "Expected 0, got %u\n", destsz);
1005
1006     /* lpDstStr NULL */
1007     memset(dest, 'x', sizeof(dest));
1008     srcsz = lstrlenW(src) + 1;
1009     destsz = sizeof(dest);
1010     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, NULL, &destsz);
1011     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1012     ok(srcsz == lstrlenW(src) + 1,
1013        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
1014     ok(destsz == lstrlenW(src) + 1,
1015        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
1016
1017     /* lpnMultiCharCount NULL */
1018     memset(dest, 'x', sizeof(dest));
1019     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, NULL);
1020     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1021     ok(srcsz == lstrlenW(src) + 1,
1022        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
1023     ok(!memcmp(dest, invariate, sizeof(dest)),
1024        "Expected dest to be unchanged, got %s\n", dest);
1025
1026     /* lpnWideCharCount is 0 */
1027     memset(dest, 'x', sizeof(dest));
1028     srcsz = 0;
1029     destsz = sizeof(dest);
1030     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, &destsz);
1031     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1032     ok(srcsz == 0, "Expected 0, got %u\n", srcsz);
1033     ok(!memcmp(dest, invariate, sizeof(dest)),
1034        "Expected dest to be unchanged, got %s\n", dest);
1035     ok(destsz == 0, "Expected 0, got %u\n", destsz);
1036
1037     /* lpnWideCharCount does not include NULL terminator */
1038     memset(dest, 'x', sizeof(dest));
1039     srcsz = lstrlenW(src);
1040     destsz = sizeof(dest);
1041     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, &destsz);
1042     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1043     ok(srcsz == lstrlenW(src),
1044        "Expected %u, got %u\n", lstrlenW(src), srcsz);
1045     ok(!strncmp(dest, "abc", 3), "Expected first three chars to be \"abc\"\n");
1046     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
1047        "Expected rest of dest to be unchanged, got %s\n", dest);
1048     ok(destsz == lstrlenW(src),
1049        "Expected %u, got %u\n", lstrlenW(src), destsz);
1050
1051     /* lpnWideCharCount includes NULL terminator */
1052     memset(dest, 'x', sizeof(dest));
1053     srcsz = lstrlenW(src) + 1;
1054     destsz = sizeof(dest);
1055     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, &destsz);
1056     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1057     ok(srcsz == lstrlenW(src) + 1, "Expected 3, got %u\n", srcsz);
1058     ok(!lstrcmpA(dest, "abc"), "Expected \"abc\", got \"%s\"\n", dest);
1059     ok(destsz == lstrlenW(src) + 1,
1060        "Expected %u, got %u\n", lstrlenW(src) + 1, destsz);
1061
1062     /* lpnWideCharCount is -1 */
1063     memset(dest, 'x', sizeof(dest));
1064     srcsz = -1;
1065     destsz = sizeof(dest);
1066     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, &destsz);
1067     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1068     ok(srcsz == lstrlenW(src),
1069        "Expected %u, got %u\n", lstrlenW(src), srcsz);
1070     ok(!strncmp(dest, "abc", 3), "Expected first three chars to be \"abc\"\n");
1071     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
1072        "Expected rest of dest to be unchanged, got %s\n", dest);
1073     ok(destsz == lstrlenW(src),
1074        "Expected %u, got %u\n", lstrlenW(src), destsz);
1075
1076     /* lpnMultiCharCount is 0 */
1077     memset(dest, 'x', sizeof(dest));
1078     srcsz = lstrlenW(src) + 1;
1079     destsz = 0;
1080     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, &destsz);
1081     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1082     ok(srcsz == lstrlenW(src) + 1,
1083        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
1084     ok(!memcmp(dest, invariate, sizeof(dest)),
1085        "Expected dest to be unchanged, got %s\n", dest);
1086     ok(destsz == lstrlenW(src) + 1,
1087        "Expected %u, got %u\n", lstrlenW(src) + 1, destsz);
1088
1089     /* lpnMultiCharCount is not large enough */
1090     memset(dest, 'x', sizeof(dest));
1091     srcsz = lstrlenW(src) + 1;
1092     destsz = lstrlenW(src);
1093     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, &destsz);
1094     ok(hr == E_FAIL, "Expected E_FAIL, got %08x\n", hr);
1095     ok(srcsz == 0, "Expected 0, got %u\n", srcsz);
1096     ok(!strncmp(dest, "abc", 3), "Expected first three chars to be \"abc\"\n");
1097     ok(!memcmp(&dest[3], invariate, sizeof(dest) - 3),
1098        "Expected rest of dest to be unchanged, got %s\n", dest);
1099     ok(destsz == 0, "Expected 0, got %u\n", srcsz);
1100
1101     /* lpnMultiCharCount (bytes) does not leave room for NULL terminator */
1102     memset(dest, 'x', sizeof(dest));
1103     srcsz = lstrlenW(src) + 1;
1104     destsz = lstrlenW(src) * sizeof(WCHAR);
1105     hr = pConvertINetUnicodeToMultiByte(NULL, 1252, src, &srcsz, dest, &destsz);
1106     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1107     ok(srcsz == lstrlenW(src) + 1,
1108        "Expected %u, got %u\n", lstrlenW(src) + 1, srcsz);
1109     ok(!lstrcmpA(dest, "abc"), "Expected \"abc\", got \"%s\"\n", dest);
1110     ok(destsz == lstrlenW(src) + 1,
1111        "Expected %u, got %u\n", lstrlenW(src) + 1, destsz);
1112 }
1113
1114 START_TEST(mlang)
1115 {
1116     IMultiLanguage2 *iML2 = NULL;
1117     IMLangFontLink  *iMLFL = NULL;
1118     HRESULT ret;
1119
1120     if (!init_function_ptrs())
1121         return;
1122
1123     CoInitialize(NULL);
1124     TRACE_2("Call CoCreateInstance\n");
1125     ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
1126                            &IID_IMultiLanguage2, (void **)&iML2);
1127
1128     trace("ret = %08x, MultiLanguage2 iML2 = %p\n", ret, iML2);
1129     if (ret != S_OK || !iML2) return;
1130
1131     test_rfc1766(iML2);
1132     test_GetLcidFromRfc1766(iML2);
1133     test_GetRfc1766FromLcid(iML2);
1134
1135     test_EnumCodePages(iML2, 0);
1136     test_EnumCodePages(iML2, MIMECONTF_MIME_LATEST);
1137     test_EnumCodePages(iML2, MIMECONTF_BROWSER);
1138     test_EnumCodePages(iML2, MIMECONTF_MINIMAL);
1139     test_EnumCodePages(iML2, MIMECONTF_VALID);
1140     /* FIXME: why MIMECONTF_MIME_REGISTRY returns 0 of supported codepages? */
1141     /*test_EnumCodePages(iML2, MIMECONTF_MIME_REGISTRY);*/
1142
1143     test_EnumScripts(iML2, 0);
1144     test_EnumScripts(iML2, SCRIPTCONTF_SCRIPT_USER);
1145     test_EnumScripts(iML2, SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM);
1146
1147     TRACE_2("Call IMultiLanguage2_IsConvertible\n");
1148     ret = IMultiLanguage2_IsConvertible(iML2, CP_UTF8, CP_UNICODE);
1149     ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UTF8 -> CP_UNICODE) = %08x\n", ret);
1150     TRACE_2("Call IMultiLanguage2_IsConvertible\n");
1151     ret = IMultiLanguage2_IsConvertible(iML2, CP_UNICODE, CP_UTF8);
1152     ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UNICODE -> CP_UTF8) = %08x\n", ret);
1153
1154     test_multibyte_to_unicode_translations(iML2);
1155     test_IMultiLanguage2_ConvertStringFromUnicode(iML2);
1156
1157     IMultiLanguage2_Release(iML2);
1158
1159     test_ConvertINetUnicodeToMultiByte();
1160
1161     ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
1162                            &IID_IMLangFontLink, (void **)&iMLFL);
1163
1164     trace("ret = %08x, IMLangFontLink iMLFL = %p\n", ret, iMLFL);
1165     if (ret != S_OK || !iML2) return;
1166
1167     IMLangFontLink_Test(iMLFL);
1168     IMLangFontLink_Release(iMLFL);
1169
1170     CoUninitialize();
1171 }