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