mlang/tests: Reduce the size of the test output a little.
[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 "mlang.h"
29
30 #include "wine/test.h"
31
32 #ifndef CP_UNICODE
33 #define CP_UNICODE 1200
34 #endif
35
36 #if 0
37 #define DUMP_CP_INFO
38 #define DUMP_SCRIPT_INFO
39
40 #if defined DUMP_CP_INFO || defined DUMP_SCRIPT_INFO
41 #include "wine/debug.h"
42 #endif
43 #endif /* 0 */
44
45 #define TRACE_2 OutputDebugStringA
46
47 static CHAR string1[MAX_PATH], string2[MAX_PATH];
48
49 #define ok_w2(format, szString1, szString2) \
50 \
51     if (lstrcmpW(szString1, szString2) != 0) \
52     { \
53         WideCharToMultiByte(CP_ACP, 0, szString1, -1, string1, MAX_PATH, NULL, NULL); \
54         WideCharToMultiByte(CP_ACP, 0, szString2, -1, string2, MAX_PATH, NULL, NULL); \
55         ok(0, format, string1, string2); \
56     }
57
58 static BOOL (WINAPI *pGetCPInfoExA)(UINT,DWORD,LPCPINFOEXA);
59
60 static void test_multibyte_to_unicode_translations(IMultiLanguage2 *iML2)
61 {
62     /* these APIs are broken regarding constness of the input buffer */
63     char stringA[] = "Just a test string\0"; /* double 0 for CP_UNICODE tests */
64     WCHAR stringW[] = {'J','u','s','t',' ','a',' ','t','e','s','t',' ','s','t','r','i','n','g',0};
65     char bufA[256];
66     WCHAR bufW[256];
67     UINT lenA, lenW, expected_len;
68     HRESULT ret;
69     HMODULE hMlang;
70     FARPROC pConvertINetMultiByteToUnicode;
71     FARPROC pConvertINetUnicodeToMultiByte;
72
73     hMlang = LoadLibraryA("mlang.dll");
74     ok(hMlang != 0, "couldn't load mlang.dll\n");
75
76     pConvertINetMultiByteToUnicode = GetProcAddress(hMlang, "ConvertINetMultiByteToUnicode");
77     ok(pConvertINetMultiByteToUnicode != NULL, "couldn't resolve ConvertINetMultiByteToUnicode\n");
78     pConvertINetUnicodeToMultiByte = GetProcAddress(hMlang, "ConvertINetUnicodeToMultiByte");
79     ok(pConvertINetUnicodeToMultiByte != NULL, "couldn't resolve ConvertINetUnicodeToMultiByte\n");
80
81     /* IMultiLanguage2_ConvertStringToUnicode tests */
82
83     memset(bufW, 'x', sizeof(bufW));
84     lenA = 0;
85     lenW = sizeof(bufW)/sizeof(bufW[0]);
86     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
87     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
88     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
89     ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
90     ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
91
92     memset(bufW, 'x', sizeof(bufW));
93     lenA = -1;
94     lenW = sizeof(bufW)/sizeof(bufW[0]);
95     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
96     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
97     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
98     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
99     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
100     if (lenW < sizeof(bufW)/sizeof(bufW[0])) {
101        /* can only happen if the convert call fails */
102        ok(bufW[lenW] != 0, "buf should not be 0 terminated\n");
103        bufW[lenW] = 0; /* -1 doesn't include 0 terminator */
104     }
105     ok(!lstrcmpW(bufW, stringW), "bufW/stringW mismatch\n");
106
107     memset(bufW, 'x', sizeof(bufW));
108     lenA = -1;
109     lenW = 5;
110     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
111     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, bufW, &lenW);
112     ok(ret == E_FAIL, "IMultiLanguage2_ConvertStringToUnicode should fail: %08x\n", ret);
113     ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
114     /* still has to do partial conversion */
115     ok(!memcmp(bufW, stringW, 5 * sizeof(WCHAR)), "bufW/stringW mismatch\n");
116
117     memset(bufW, 'x', sizeof(bufW));
118     lenA = -1;
119     lenW = sizeof(bufW)/sizeof(bufW[0]);
120     TRACE_2("Call IMultiLanguage2_ConvertStringToUnicode\n");
121     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, CP_UNICODE, stringA, &lenA, bufW, &lenW);
122     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
123     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
124     ok(lenW == lstrlenW(stringW)/(int)sizeof(WCHAR), "wrong lenW %u\n", lenW);
125     ok(bufW[lenW] != 0, "buf should not be 0 terminated\n");
126     bufW[lenW] = 0; /* -1 doesn't include 0 terminator */
127     ok(!lstrcmpA((LPCSTR)bufW, stringA), "bufW/stringA mismatch\n");
128
129     memset(bufW, 'x', sizeof(bufW));
130     lenA = lstrlenA(stringA);
131     lenW = 0;
132     ret = IMultiLanguage2_ConvertStringToUnicode(iML2, NULL, 1252, stringA, &lenA, NULL, &lenW);
133     ok(ret == S_OK, "IMultiLanguage2_ConvertStringToUnicode failed: %08x\n", ret);
134     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
135     expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
136     ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
137
138     memset(bufW, 'x', sizeof(bufW));
139     lenA = lstrlenA(stringA);
140     lenW = sizeof(bufW)/sizeof(bufW[0]);
141     ret = pConvertINetMultiByteToUnicode(NULL, 1252, stringA, &lenA, NULL, &lenW);
142     ok(ret == S_OK, "ConvertINetMultiByteToUnicode failed: %08x\n", ret);
143     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
144     expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
145     ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
146
147     memset(bufW, 'x', sizeof(bufW));
148     lenA = lstrlenA(stringA);
149     lenW = 0;
150     ret = pConvertINetMultiByteToUnicode(NULL, 1252, stringA, &lenA, NULL, &lenW);
151     ok(ret == S_OK, "ConvertINetMultiByteToUnicode failed: %08x\n", ret);
152     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
153     expected_len = MultiByteToWideChar(1252, 0, stringA, lenA, NULL, 0);
154     ok(lenW == expected_len, "expected lenW %u, got %u\n", expected_len, lenW);
155
156     /* IMultiLanguage2_ConvertStringFromUnicode tests */
157
158     memset(bufA, 'x', sizeof(bufA));
159     lenW = 0;
160     lenA = sizeof(bufA);
161     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
162     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
163     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
164     ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
165     ok(lenW == 0, "expected lenW 0, got %u\n", lenW);
166
167     memset(bufA, 'x', sizeof(bufA));
168     lenW = -1;
169     lenA = sizeof(bufA);
170     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
171     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
172     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
173     ok(lenA == lstrlenA(stringA), "expected lenA %u, got %u\n", lstrlenA(stringA), lenA);
174     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
175     ok(bufA[lenA] != 0, "buf should not be 0 terminated\n");
176     bufA[lenA] = 0; /* -1 doesn't include 0 terminator */
177     ok(!lstrcmpA(bufA, stringA), "bufA/stringA mismatch\n");
178
179     memset(bufA, 'x', sizeof(bufA));
180     lenW = -1;
181     lenA = 5;
182     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
183     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, bufA, &lenA);
184     ok(ret == E_FAIL, "IMultiLanguage2_ConvertStringFromUnicode should fail: %08x\n", ret);
185     ok(lenA == 0, "expected lenA 0, got %u\n", lenA);
186     /* still has to do partial conversion */
187     ok(!memcmp(bufA, stringA, 5), "bufW/stringW mismatch\n");
188
189     memset(bufA, 'x', sizeof(bufA));
190     lenW = -1;
191     lenA = sizeof(bufA);
192     TRACE_2("Call IMultiLanguage2_ConvertStringFromUnicode\n");
193     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, CP_UNICODE, stringW, &lenW, bufA, &lenA);
194     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
195     ok(lenA == lstrlenA(stringA) * (int)sizeof(WCHAR), "wrong lenA %u\n", lenA);
196     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
197     ok(bufA[lenA] != 0 && bufA[lenA+1] != 0, "buf should not be 0 terminated\n");
198     bufA[lenA] = 0; /* -1 doesn't include 0 terminator */
199     bufA[lenA+1] = 0; /* sizeof(WCHAR) */
200     ok(!lstrcmpW((LPCWSTR)bufA, stringW), "bufA/stringW mismatch\n");
201
202     memset(bufA, 'x', sizeof(bufA));
203     lenW = lstrlenW(stringW);
204     lenA = 0;
205     ret = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, 1252, stringW, &lenW, NULL, &lenA);
206     ok(ret == S_OK, "IMultiLanguage2_ConvertStringFromUnicode failed: %08x\n", ret);
207     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
208     expected_len = WideCharToMultiByte(1252, 0, stringW, lenW, NULL, 0, NULL, NULL);
209     ok(lenA == expected_len, "expected lenA %u, got %u\n", expected_len, lenA);
210
211     memset(bufA, 'x', sizeof(bufA));
212     lenW = lstrlenW(stringW);
213     lenA = sizeof(bufA);
214     ret = pConvertINetUnicodeToMultiByte(NULL, 1252, stringW, &lenW, NULL, &lenA);
215     ok(ret == S_OK, "ConvertINetUnicodeToMultiByte failed: %08x\n", ret);
216     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
217     expected_len = WideCharToMultiByte(1252, 0, stringW, lenW, NULL, 0, NULL, NULL);
218     ok(lenA == expected_len, "expected lenA %u, got %u\n", expected_len, lenA);
219
220     memset(bufA, 'x', sizeof(bufA));
221     lenW = lstrlenW(stringW);
222     lenA = 0;
223     ret = pConvertINetUnicodeToMultiByte(NULL, 1252, stringW, &lenW, NULL, &lenA);
224     ok(ret == S_OK, "ConvertINetUnicodeToMultiByte failed: %08x\n", ret);
225     ok(lenW == lstrlenW(stringW), "expected lenW %u, got %u\n", lstrlenW(stringW), lenW);
226     expected_len = WideCharToMultiByte(1252, 0, stringW, lenW, NULL, 0, NULL, NULL);
227     ok(lenA == expected_len, "expected lenA %u, got %u\n", expected_len, lenA);
228 }
229
230 static inline void cpinfo_cmp(MIMECPINFO *cpinfo1, MIMECPINFO *cpinfo2)
231 {
232     ok(cpinfo1->dwFlags == cpinfo2->dwFlags, "dwFlags mismatch: %08x != %08x\n", cpinfo1->dwFlags, cpinfo2->dwFlags);
233     ok(cpinfo1->uiCodePage == cpinfo2->uiCodePage, "uiCodePage mismatch: %u != %u\n", cpinfo1->uiCodePage, cpinfo2->uiCodePage);
234     ok(cpinfo1->uiFamilyCodePage == cpinfo2->uiFamilyCodePage, "uiFamilyCodePage mismatch: %u != %u\n", cpinfo1->uiFamilyCodePage, cpinfo2->uiFamilyCodePage);
235     ok(!lstrcmpW(cpinfo1->wszDescription, cpinfo2->wszDescription), "wszDescription mismatch\n");
236     ok(!lstrcmpW(cpinfo1->wszWebCharset, cpinfo2->wszWebCharset), "wszWebCharset mismatch\n");
237     ok(!lstrcmpW(cpinfo1->wszHeaderCharset, cpinfo2->wszHeaderCharset), "wszHeaderCharset mismatch\n");
238     ok(!lstrcmpW(cpinfo1->wszBodyCharset, cpinfo2->wszBodyCharset), "wszBodyCharset mismatch\n");
239     ok(!lstrcmpW(cpinfo1->wszFixedWidthFont, cpinfo2->wszFixedWidthFont), "wszFixedWidthFont mismatch\n");
240     ok(!lstrcmpW(cpinfo1->wszProportionalFont, cpinfo2->wszProportionalFont), "wszProportionalFont mismatch\n");
241     ok(cpinfo1->bGDICharset == cpinfo2->bGDICharset, "bGDICharset mismatch: %d != %d\n", cpinfo1->bGDICharset, cpinfo2->bGDICharset);
242 }
243
244 #ifdef DUMP_CP_INFO
245 static const char *dump_mime_flags(DWORD flags)
246 {
247     static char buf[1024];
248
249     buf[0] = 0;
250
251     if (flags & MIMECONTF_MAILNEWS) strcat(buf, " MIMECONTF_MAILNEWS");
252     if (flags & MIMECONTF_BROWSER) strcat(buf, " MIMECONTF_BROWSER");
253     if (flags & MIMECONTF_MINIMAL) strcat(buf, " MIMECONTF_MINIMAL");
254     if (flags & MIMECONTF_IMPORT) strcat(buf, " MIMECONTF_IMPORT");
255     if (flags & MIMECONTF_SAVABLE_MAILNEWS) strcat(buf, " MIMECONTF_SAVABLE_MAILNEWS");
256     if (flags & MIMECONTF_SAVABLE_BROWSER) strcat(buf, " MIMECONTF_SAVABLE_BROWSER");
257     if (flags & MIMECONTF_EXPORT) strcat(buf, " MIMECONTF_EXPORT");
258     if (flags & MIMECONTF_PRIVCONVERTER) strcat(buf, " MIMECONTF_PRIVCONVERTER");
259     if (flags & MIMECONTF_VALID) strcat(buf, " MIMECONTF_VALID");
260     if (flags & MIMECONTF_VALID_NLS) strcat(buf, " MIMECONTF_VALID_NLS");
261     if (flags & MIMECONTF_MIME_IE4) strcat(buf, " MIMECONTF_MIME_IE4");
262     if (flags & MIMECONTF_MIME_LATEST) strcat(buf, " MIMECONTF_MIME_LATEST");
263     if (flags & MIMECONTF_MIME_REGISTRY) strcat(buf, " MIMECONTF_MIME_REGISTRY");
264
265     return buf;
266 }
267 #endif
268
269 static BOOL check_convertible(IMultiLanguage2 *iML2, UINT from, UINT to)
270 {
271     CHAR convert[MAX_PATH];
272     BYTE dest[MAX_PATH];
273     HRESULT hr;
274     UINT srcsz, destsz;
275
276     static WCHAR strW[] = {'a','b','c',0};
277
278     srcsz = -1;
279     destsz = MAX_PATH;
280     hr = IMultiLanguage2_ConvertStringFromUnicode(iML2, NULL, from, strW,
281                                                   &srcsz, convert, &destsz);
282     if (hr != S_OK)
283         return FALSE;
284
285     srcsz = -1;
286     destsz = MAX_PATH;
287     hr = IMultiLanguage2_ConvertString(iML2, NULL, from, to, (BYTE *)convert,
288                                        &srcsz, dest, &destsz);
289     if (hr != S_OK)
290         return FALSE;
291
292     return TRUE;
293 }
294
295 static void test_EnumCodePages(IMultiLanguage2 *iML2, DWORD flags)
296 {
297     IEnumCodePage *iEnumCP = NULL;
298     MIMECPINFO *cpinfo;
299     MIMECPINFO cpinfo2;
300     HRESULT ret;
301     ULONG i, n;
302     UINT total;
303
304     total = 0;
305     TRACE_2("Call IMultiLanguage2_GetNumberOfCodePageInfo\n");
306     ret = IMultiLanguage2_GetNumberOfCodePageInfo(iML2, &total);
307     ok(ret == S_OK && total != 0, "IMultiLanguage2_GetNumberOfCodePageInfo: expected S_OK/!0, got %08x/%u\n", ret, total);
308
309     trace("total mlang supported codepages %u\n", total);
310
311     TRACE_2("Call IMultiLanguage2_EnumCodePages\n");
312     ret = IMultiLanguage2_EnumCodePages(iML2, flags, LANG_NEUTRAL, &iEnumCP);
313     trace("IMultiLanguage2_EnumCodePages = %08x, iEnumCP = %p\n", ret, iEnumCP);
314     ok(ret == S_OK && iEnumCP, "IMultiLanguage2_EnumCodePages: expected S_OK/!NULL, got %08x/%p\n", ret, iEnumCP);
315
316     TRACE_2("Call IEnumCodePage_Reset\n");
317     ret = IEnumCodePage_Reset(iEnumCP);
318     ok(ret == S_OK, "IEnumCodePage_Reset: expected S_OK, got %08x\n", ret);
319     n = 65536;
320     TRACE_2("Call IEnumCodePage_Next\n");
321     ret = IEnumCodePage_Next(iEnumCP, 0, NULL, &n);
322     ok(n == 0 && ret == S_FALSE, "IEnumCodePage_Next: expected 0/S_FALSE, got %u/%08x\n", n, ret);
323     TRACE_2("Call IEnumCodePage_Next\n");
324     ret = IEnumCodePage_Next(iEnumCP, 0, NULL, NULL);
325     ok(ret == S_FALSE, "IEnumCodePage_Next: expected S_FALSE, got %08x\n", ret);
326
327     cpinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*cpinfo) * total * 2);
328
329     n = total * 2;
330     TRACE_2("Call IEnumCodePage_Next\n");
331     ret = IEnumCodePage_Next(iEnumCP, 0, cpinfo, &n);
332     trace("IEnumCodePage_Next = %08x, n = %u\n", ret, n);
333     ok(ret == S_FALSE && n == 0, "IEnumCodePage_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
334
335     n = total * 2;
336     TRACE_2("Call IEnumCodePage_Next\n");
337     ret = IEnumCodePage_Next(iEnumCP, n, cpinfo, &n);
338     ok(ret == S_OK && n != 0, "IEnumCodePage_Next: expected S_OK/!0, got %08x/%u\n", ret, n);
339
340     trace("flags %08x, enumerated codepages %u\n", flags, n);
341
342     if (!flags)
343     {
344         ok(n == total, "IEnumCodePage_Next: expected %u, got %u\n", total, n);
345
346         flags = MIMECONTF_MIME_LATEST;
347     }
348
349     total = n;
350
351     for (i = 0; i < n; i++)
352     {
353         CHARSETINFO csi;
354         MIMECSETINFO mcsi;
355         BOOL convertible;
356         HRESULT check = S_OK;
357         static const WCHAR autoW[] = {'_','a','u','t','o',0};
358
359 #ifdef DUMP_CP_INFO
360         trace("MIMECPINFO #%u:\n"
361               "dwFlags %08x %s\n"
362               "uiCodePage %u\n"
363               "uiFamilyCodePage %u\n"
364               "wszDescription %s\n"
365               "wszWebCharset %s\n"
366               "wszHeaderCharset %s\n"
367               "wszBodyCharset %s\n"
368               "wszFixedWidthFont %s\n"
369               "wszProportionalFont %s\n"
370               "bGDICharset %d\n\n",
371               i,
372               cpinfo[i].dwFlags, dump_mime_flags(cpinfo[i].dwFlags),
373               cpinfo[i].uiCodePage,
374               cpinfo[i].uiFamilyCodePage,
375               wine_dbgstr_w(cpinfo[i].wszDescription),
376               wine_dbgstr_w(cpinfo[i].wszWebCharset),
377               wine_dbgstr_w(cpinfo[i].wszHeaderCharset),
378               wine_dbgstr_w(cpinfo[i].wszBodyCharset),
379               wine_dbgstr_w(cpinfo[i].wszFixedWidthFont),
380               wine_dbgstr_w(cpinfo[i].wszProportionalFont),
381               cpinfo[i].bGDICharset);
382 #endif
383         ok(cpinfo[i].dwFlags & flags, "enumerated flags %08x do not include requested %08x\n", cpinfo[i].dwFlags, flags);
384
385         if (TranslateCharsetInfo((DWORD *)cpinfo[i].uiFamilyCodePage, &csi, TCI_SRCCODEPAGE))
386             ok(cpinfo[i].bGDICharset == csi.ciCharset, "%d != %d\n", cpinfo[i].bGDICharset, csi.ciCharset);
387         else
388             trace("TranslateCharsetInfo failed for cp %u\n", cpinfo[i].uiFamilyCodePage);
389
390         trace("%u: codepage %u family %u\n", i, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
391
392         /* Win95 does not support UTF-7 */
393         if (cpinfo[i].uiCodePage == CP_UTF7) continue;
394
395         /* support files for some codepages might be not installed, or
396          * the codepage is just an alias.
397          */
398         if (IsValidCodePage(cpinfo[i].uiCodePage))
399         {
400             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
401             ret = IMultiLanguage2_IsConvertible(iML2, cpinfo[i].uiCodePage, CP_UNICODE);
402             ok(ret == S_OK, "IMultiLanguage2_IsConvertible(%u -> CP_UNICODE) = %08x\n", cpinfo[i].uiCodePage, ret);
403             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
404             ret = IMultiLanguage2_IsConvertible(iML2, CP_UNICODE, cpinfo[i].uiCodePage);
405             ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UNICODE -> %u) = %08x\n", cpinfo[i].uiCodePage, ret);
406
407             convertible = check_convertible(iML2, cpinfo[i].uiCodePage, CP_UTF8);
408             if (!convertible)
409                 check = S_FALSE;
410
411             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
412             ret = IMultiLanguage2_IsConvertible(iML2, cpinfo[i].uiCodePage, CP_UTF8);
413             ok(ret == check, "IMultiLanguage2_IsConvertible(%u -> CP_UTF8) = %08x\n", cpinfo[i].uiCodePage, ret);
414             TRACE_2("Call IMultiLanguage2_IsConvertible\n");
415             ret = IMultiLanguage2_IsConvertible(iML2, CP_UTF8, cpinfo[i].uiCodePage);
416             ok(ret == check, "IMultiLanguage2_IsConvertible(CP_UTF8 -> %u) = %08x\n", cpinfo[i].uiCodePage, ret);
417         }
418         else
419             trace("IsValidCodePage failed for cp %u\n", cpinfo[i].uiCodePage);
420
421         ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszWebCharset, &mcsi);
422         /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
423         if (memcmp(cpinfo[i].wszWebCharset, autoW, 5 * sizeof(WCHAR)))
424         {
425             ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
426 #ifdef DUMP_CP_INFO
427             trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszWebCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
428 #endif
429             ok(!lstrcmpiW(cpinfo[i].wszWebCharset, mcsi.wszCharset),
430 #ifdef DUMP_CP_INFO
431                 "%s != %s\n",
432                 wine_dbgstr_w(cpinfo[i].wszWebCharset), wine_dbgstr_w(mcsi.wszCharset));
433 #else
434                 "wszWebCharset mismatch\n");
435 #endif
436
437         if (0)
438         {
439             /* native mlang returns completely messed up encodings in some cases */
440             ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
441                 "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
442             ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
443                 "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
444         }
445         }
446
447         ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszHeaderCharset, &mcsi);
448         /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
449         if (memcmp(cpinfo[i].wszHeaderCharset, autoW, 5 * sizeof(WCHAR)))
450         {
451             ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
452 #ifdef DUMP_CP_INFO
453             trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszHeaderCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
454 #endif
455             ok(!lstrcmpiW(cpinfo[i].wszHeaderCharset, mcsi.wszCharset),
456 #ifdef DUMP_CP_INFO
457                 "%s != %s\n",
458                 wine_dbgstr_w(cpinfo[i].wszHeaderCharset), wine_dbgstr_w(mcsi.wszCharset));
459 #else
460                 "wszHeaderCharset mismatch\n");
461 #endif
462
463         if (0)
464         {
465             /* native mlang returns completely messed up encodings in some cases */
466             ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
467                 "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
468             ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
469                 "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
470         }
471         }
472
473         ret = IMultiLanguage2_GetCharsetInfo(iML2, cpinfo[i].wszBodyCharset, &mcsi);
474         /* _autoxxx charsets are a fake and GetCharsetInfo fails for them */
475         if (memcmp(cpinfo[i].wszBodyCharset, autoW, 5 * sizeof(WCHAR)))
476         {
477             ok (ret == S_OK, "IMultiLanguage2_GetCharsetInfo failed: %08x\n", ret);
478 #ifdef DUMP_CP_INFO
479             trace("%s: %u %u %s\n", wine_dbgstr_w(cpinfo[i].wszBodyCharset), mcsi.uiCodePage, mcsi.uiInternetEncoding, wine_dbgstr_w(mcsi.wszCharset));
480 #endif
481             ok(!lstrcmpiW(cpinfo[i].wszBodyCharset, mcsi.wszCharset),
482 #ifdef DUMP_CP_INFO
483                 "%s != %s\n",
484                 wine_dbgstr_w(cpinfo[i].wszBodyCharset), wine_dbgstr_w(mcsi.wszCharset));
485 #else
486                 "wszBodyCharset mismatch\n");
487 #endif
488
489         if (0)
490         {
491             /* native mlang returns completely messed up encodings in some cases */
492             ok(mcsi.uiInternetEncoding == cpinfo[i].uiCodePage || mcsi.uiInternetEncoding == cpinfo[i].uiFamilyCodePage,
493                 "%u != %u || %u\n", mcsi.uiInternetEncoding, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
494             ok(mcsi.uiCodePage == cpinfo[i].uiCodePage || mcsi.uiCodePage == cpinfo[i].uiFamilyCodePage,
495                 "%u != %u || %u\n", mcsi.uiCodePage, cpinfo[i].uiCodePage, cpinfo[i].uiFamilyCodePage);
496         }
497         }
498     }
499
500     /* now IEnumCodePage_Next should fail, since pointer is at the end */
501     n = 1;
502     ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
503     ok(ret == S_FALSE && n == 0, "IEnumCodePage_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
504
505     ret = IEnumCodePage_Reset(iEnumCP);
506     ok(ret == S_OK, "IEnumCodePage_Reset: expected S_OK, got %08x\n", ret);
507     n = 0;
508     ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
509     ok(n == 1 && ret == S_OK, "IEnumCodePage_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
510     cpinfo_cmp(&cpinfo[0], &cpinfo2);
511
512     if (0)
513     {
514     /* Due to a bug in MS' implementation of IEnumCodePage_Skip
515      * it's not used here.
516      */
517     ret = IEnumCodePage_Skip(iEnumCP, 1);
518     ok(ret == S_OK, "IEnumCodePage_Skip: expected S_OK, got %08x\n", ret);
519     }
520     for (i = 0; i < total - 1; i++)
521     {
522         n = 0;
523         ret = IEnumCodePage_Next(iEnumCP, 1, &cpinfo2, &n);
524         ok(n == 1 && ret == S_OK, "IEnumCodePage_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
525         cpinfo_cmp(&cpinfo[i + 1], &cpinfo2);
526     }
527
528     HeapFree(GetProcessHeap(), 0, cpinfo);
529     IEnumCodePage_Release(iEnumCP);
530 }
531
532 static inline void scriptinfo_cmp(SCRIPTINFO *sinfo1, SCRIPTINFO *sinfo2)
533 {
534     ok(sinfo1->ScriptId == sinfo2->ScriptId, "ScriptId mismatch: %d != %d\n", sinfo1->ScriptId, sinfo2->ScriptId);
535     ok(sinfo1->uiCodePage == sinfo2->uiCodePage, "uiCodePage mismatch: %u != %u\n", sinfo1->uiCodePage, sinfo2->uiCodePage);
536     ok(!lstrcmpW(sinfo1->wszDescription, sinfo2->wszDescription), "wszDescription mismatch\n");
537     ok(!lstrcmpW(sinfo1->wszFixedWidthFont, sinfo2->wszFixedWidthFont), "wszFixedWidthFont mismatch\n");
538     ok(!lstrcmpW(sinfo1->wszProportionalFont, sinfo2->wszProportionalFont), "wszProportionalFont mismatch\n");
539 }
540
541 static void test_EnumScripts(IMultiLanguage2 *iML2, DWORD flags)
542 {
543     IEnumScript *iEnumScript = NULL;
544     SCRIPTINFO *sinfo;
545     SCRIPTINFO sinfo2;
546     HRESULT ret;
547     ULONG i, n;
548     UINT total;
549
550     total = 0;
551     TRACE_2("Call IMultiLanguage2_GetNumberOfScripts\n");
552     ret = IMultiLanguage2_GetNumberOfScripts(iML2, &total);
553     ok(ret == S_OK && total != 0, "IMultiLanguage2_GetNumberOfScripts: expected S_OK/!0, got %08x/%u\n", ret, total);
554
555     trace("total mlang supported scripts %u\n", total);
556
557     TRACE_2("Call IMultiLanguage2_EnumScripts\n");
558     ret = IMultiLanguage2_EnumScripts(iML2, flags, LANG_NEUTRAL, &iEnumScript);
559     trace("IMultiLanguage2_EnumScripts = %08x, iEnumScript = %p\n", ret, iEnumScript);
560     ok(ret == S_OK && iEnumScript, "IMultiLanguage2_EnumScripts: expected S_OK/!NULL, got %08x/%p\n", ret, iEnumScript);
561
562     TRACE_2("Call IEnumScript_Reset\n");
563     ret = IEnumScript_Reset(iEnumScript);
564     ok(ret == S_OK, "IEnumScript_Reset: expected S_OK, got %08x\n", ret);
565     n = 65536;
566     TRACE_2("Call IEnumScript_Next\n");
567     ret = IEnumScript_Next(iEnumScript, 0, NULL, &n);
568     ok(n == 65536 && ret == E_FAIL, "IEnumScript_Next: expected 65536/E_FAIL, got %u/%08x\n", n, ret);
569     TRACE_2("Call IEnumScript_Next\n");
570     ret = IEnumScript_Next(iEnumScript, 0, NULL, NULL);
571     ok(ret == E_FAIL, "IEnumScript_Next: expected E_FAIL, got %08x\n", ret);
572
573     sinfo = HeapAlloc(GetProcessHeap(), 0, sizeof(*sinfo) * total * 2);
574
575     n = total * 2;
576     TRACE_2("Call IEnumScript_Next\n");
577     ret = IEnumScript_Next(iEnumScript, 0, sinfo, &n);
578     ok(ret == S_FALSE && n == 0, "IEnumScript_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
579
580     n = total * 2;
581     TRACE_2("Call IEnumScript_Next\n");
582     ret = IEnumScript_Next(iEnumScript, n, sinfo, &n);
583     ok(ret == S_OK && n != 0, "IEnumScript_Next: expected S_OK, got %08x/%u\n", ret, n);
584
585     trace("flags %08x, enumerated scripts %u\n", flags, n);
586
587     if (!flags)
588     {
589         ok(n == total, "IEnumScript_Next: expected %u, got %u\n", total, n);
590         flags = SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM;
591     }
592
593     total = n;
594
595     for (i = 0; pGetCPInfoExA && i < n; i++)
596     {
597         CPINFOEXA cpinfoex;
598 #ifdef DUMP_SCRIPT_INFO
599         trace("SCRIPTINFO #%u:\n"
600               "ScriptId %08x\n"
601               "uiCodePage %u\n"
602               "wszDescription %s\n"
603               "wszFixedWidthFont %s\n"
604               "wszProportionalFont %s\n\n",
605               i,
606               sinfo[i].ScriptId,
607               sinfo[i].uiCodePage,
608               wine_dbgstr_w(sinfo[i].wszDescription),
609               wine_dbgstr_w(sinfo[i].wszFixedWidthFont),
610               wine_dbgstr_w(sinfo[i].wszProportionalFont));
611 #endif
612         trace("%u codepage %u\n", i, sinfo[i].uiCodePage);
613     }
614
615     /* now IEnumScript_Next should fail, since pointer is at the end */
616     n = 1;
617     ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
618     ok(ret == S_FALSE && n == 0, "IEnumScript_Next: expected S_FALSE/0, got %08x/%u\n", ret, n);
619
620     ret = IEnumScript_Reset(iEnumScript);
621     ok(ret == S_OK, "IEnumScript_Reset: expected S_OK, got %08x\n", ret);
622     n = 0;
623     ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
624     ok(n == 1 && ret == S_OK, "IEnumScript_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
625     scriptinfo_cmp(&sinfo[0], &sinfo2);
626
627     if (0)
628     {
629     /* Due to a bug in MS' implementation of IEnumScript_Skip
630      * it's not used here.
631      */
632     ret = IEnumScript_Skip(iEnumScript, 1);
633     ok(ret == S_OK, "IEnumScript_Skip: expected S_OK, got %08x\n", ret);
634     }
635     for (i = 0; i < total - 1; i++)
636     {
637         n = 0;
638         ret = IEnumScript_Next(iEnumScript, 1, &sinfo2, &n);
639         ok(n == 1 && ret == S_OK, "IEnumScript_Next: expected 1/S_OK, got %u/%08x\n", n, ret);
640         scriptinfo_cmp(&sinfo[i + 1], &sinfo2);
641     }
642
643     HeapFree(GetProcessHeap(), 0, sinfo);
644     IEnumScript_Release(iEnumScript);
645 }
646
647 static void IMLangFontLink_Test(IMLangFontLink* iMLFL)
648 {
649     DWORD   dwCodePages = 0;
650     DWORD   dwManyCodePages = 0;
651     UINT    CodePage = 0;
652
653     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 932, &dwCodePages)==S_OK,
654             "IMLangFontLink_CodePageToCodePages failed\n");
655     ok (dwCodePages != 0, "No CodePages returned\n");
656     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwCodePages, 1035,
657                 &CodePage)==S_OK, 
658             "IMLangFontLink_CodePagesToCodePage failed\n");
659     ok(CodePage == 932, "Incorrect CodePage Returned (%i)\n",CodePage);
660
661     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 1252, &dwCodePages)==S_OK,
662             "IMLangFontLink_CodePageToCodePages failed\n");
663     dwManyCodePages = dwManyCodePages | dwCodePages;
664     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 1256, &dwCodePages)==S_OK,
665             "IMLangFontLink_CodePageToCodePages failed\n");
666     dwManyCodePages = dwManyCodePages | dwCodePages;
667     ok(IMLangFontLink_CodePageToCodePages(iMLFL, 874, &dwCodePages)==S_OK,
668             "IMLangFontLink_CodePageToCodePages failed\n");
669     dwManyCodePages = dwManyCodePages | dwCodePages;
670
671     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 1256,
672                 &CodePage)==S_OK, 
673             "IMLangFontLink_CodePagesToCodePage failed\n");
674     ok(CodePage == 1256, "Incorrect CodePage Returned (%i)\n",CodePage);
675
676     ok(IMLangFontLink_CodePagesToCodePage(iMLFL, dwManyCodePages, 936,
677                 &CodePage)==S_OK, 
678             "IMLangFontLink_CodePagesToCodePage failed\n");
679     ok(CodePage == 1252, "Incorrect CodePage Returned (%i)\n",CodePage);
680 }
681
682 /* copied from libs/wine/string.c */
683 WCHAR *strstrW(const WCHAR *str, const WCHAR *sub)
684 {
685     while (*str)
686     {
687         const WCHAR *p1 = str, *p2 = sub;
688         while (*p1 && *p2 && *p1 == *p2) { p1++; p2++; }
689         if (!*p2) return (WCHAR *)str;
690         str++;
691     }
692     return NULL;
693 }
694
695 static void test_rfc1766(IMultiLanguage2 *iML2)
696 {
697     IEnumRfc1766 *pEnumRfc1766;
698     RFC1766INFO info;
699     ULONG n;
700     HRESULT ret;
701     BSTR rfcstr;
702
703     ret = IMultiLanguage2_EnumRfc1766(iML2, LANG_NEUTRAL, &pEnumRfc1766);
704     ok(ret == S_OK, "IMultiLanguage2_EnumRfc1766 error %08x\n", ret);
705
706     while (1)
707     {
708         ret = IEnumRfc1766_Next(pEnumRfc1766, 1, &info, &n);
709         if (ret != S_OK) break;
710
711 #ifdef DUMP_CP_INFO
712         trace("lcid %04x rfc_name %s locale_name %s\n",
713               info.lcid, wine_dbgstr_w(info.wszRfc1766), wine_dbgstr_w(info.wszLocaleName));
714 #endif
715
716         ok(n == 1, "couldn't fetch 1 RFC1766INFO structure\n");
717
718         /* verify the Rfc1766 value */
719         ret = IMultiLanguage2_GetRfc1766FromLcid(iML2, info.lcid, &rfcstr);
720         ok(ret == S_OK, "Expected S_OK, got %08x\n", ret);
721
722         /* not an exact 1:1 correspondence between lcid and rfc1766 in the
723          * mlang database, e.g., nb-no -> 1044 -> no */
724         ok(strstrW(info.wszRfc1766, rfcstr) != NULL,
725            "Expected matching locale names\n");
726
727         SysFreeString(rfcstr);
728     }
729     IEnumRfc1766_Release(pEnumRfc1766);
730 }
731
732 static void test_GetLcidFromRfc1766(IMultiLanguage2 *iML2)
733 {
734     LCID lcid;
735     HRESULT ret;
736
737     static WCHAR e[] = { 'e',0 };
738     static WCHAR en[] = { 'e','n',0 };
739     static WCHAR empty[] = { 0 };
740     static WCHAR dash[] = { '-',0 };
741     static WCHAR e_dash[] = { 'e','-',0 };
742     static WCHAR en_gb[] = { 'e','n','-','g','b',0 };
743     static WCHAR en_us[] = { 'e','n','-','u','s',0 };
744     static WCHAR en_them[] = { 'e','n','-','t','h','e','m',0 };
745     static WCHAR english[] = { 'e','n','g','l','i','s','h',0 };
746
747     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, NULL, en);
748     ok(ret == E_INVALIDARG, "GetLcidFromRfc1766 returned: %08x\n", ret);
749
750     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, NULL);
751     ok(ret == E_INVALIDARG, "GetLcidFromRfc1766 returned: %08x\n", ret);
752
753     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, e);
754     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
755
756     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, empty);
757     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
758
759     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, dash);
760     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
761
762     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, e_dash);
763     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
764
765     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_them);
766     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
767
768     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, english);
769     ok(ret == E_FAIL, "GetLcidFromRfc1766 returned: %08x\n", ret);
770
771     lcid = 0;
772
773     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en);
774     ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
775     ok(lcid == 9, "got wrong lcid: %04x\n", lcid);
776
777     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_gb);
778     ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
779     ok(lcid == 0x809, "got wrong lcid: %04x\n", lcid);
780
781     ret = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, en_us);
782     ok(ret == S_OK, "GetLcidFromRfc1766 returned: %08x\n", ret);
783     ok(lcid == 0x409, "got wrong lcid: %04x\n", lcid);
784 }
785
786 static void test_GetRfc1766FromLcid(IMultiLanguage2 *iML2)
787 {
788     HRESULT hr;
789     BSTR rfcstr;
790     LCID lcid;
791
792     static WCHAR kok[] = {'k','o','k',0};
793
794     hr = IMultiLanguage2_GetLcidFromRfc1766(iML2, &lcid, kok);
795     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
796
797     hr = IMultiLanguage2_GetRfc1766FromLcid(iML2, lcid, &rfcstr);
798     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
799     ok_w2("Expected \"%s\",  got \"%s\"n", kok, rfcstr);
800
801     SysFreeString(rfcstr);
802 }
803
804 START_TEST(mlang)
805 {
806     IMultiLanguage2 *iML2 = NULL;
807     IMLangFontLink  *iMLFL = NULL;
808     HRESULT ret;
809
810     pGetCPInfoExA = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetCPInfoExA");
811
812     CoInitialize(NULL);
813     TRACE_2("Call CoCreateInstance\n");
814     ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
815                            &IID_IMultiLanguage2, (void **)&iML2);
816
817     trace("ret = %08x, MultiLanguage2 iML2 = %p\n", ret, iML2);
818     if (ret != S_OK || !iML2) return;
819
820     test_rfc1766(iML2);
821     test_GetLcidFromRfc1766(iML2);
822     test_GetRfc1766FromLcid(iML2);
823
824     test_EnumCodePages(iML2, 0);
825     test_EnumCodePages(iML2, MIMECONTF_MIME_LATEST);
826     test_EnumCodePages(iML2, MIMECONTF_BROWSER);
827     test_EnumCodePages(iML2, MIMECONTF_MINIMAL);
828     test_EnumCodePages(iML2, MIMECONTF_VALID);
829     /* FIXME: why MIMECONTF_MIME_REGISTRY returns 0 of supported codepages? */
830     /*test_EnumCodePages(iML2, MIMECONTF_MIME_REGISTRY);*/
831
832     test_EnumScripts(iML2, 0);
833     test_EnumScripts(iML2, SCRIPTCONTF_SCRIPT_USER);
834     test_EnumScripts(iML2, SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM);
835
836     TRACE_2("Call IMultiLanguage2_IsConvertible\n");
837     ret = IMultiLanguage2_IsConvertible(iML2, CP_UTF8, CP_UNICODE);
838     ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UTF8 -> CP_UNICODE) = %08x\n", ret);
839     TRACE_2("Call IMultiLanguage2_IsConvertible\n");
840     ret = IMultiLanguage2_IsConvertible(iML2, CP_UNICODE, CP_UTF8);
841     ok(ret == S_OK, "IMultiLanguage2_IsConvertible(CP_UNICODE -> CP_UTF8) = %08x\n", ret);
842
843     test_multibyte_to_unicode_translations(iML2);
844
845     IMultiLanguage2_Release(iML2);
846
847     ret = CoCreateInstance(&CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
848                            &IID_IMLangFontLink, (void **)&iMLFL);
849
850     trace("ret = %08x, IMLangFontLink iMLFL = %p\n", ret, iMLFL);
851     if (ret != S_OK || !iML2) return;
852
853     IMLangFontLink_Test(iMLFL);
854     IMLangFontLink_Release(iMLFL);
855     
856     CoUninitialize();
857 }