Autosyncing with Wine HEAD
[reactos.git] / rostests / winetests / mlang / 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 }