xref: /reactos/dll/win32/mlang/mlang.c (revision 8e659192)
1 /*
2  *    MLANG Class Factory
3  *
4  * Copyright 2002 Lionel Ulmer
5  * Copyright 2003,2004 Mike McCormack
6  * Copyright 2004,2005 Dmitry Timoshkov
7  * Copyright 2009 Detlef Riekenberg
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #define WIN32_NO_STATUS
25 #define _INC_WINDOWS
26 
27 #include <config.h>
28 
29 #include <stdarg.h>
30 //#include <stdio.h>
31 
32 #define COBJMACROS
33 
34 #include <windef.h>
35 #include <winbase.h>
36 #include <wingdi.h>
37 //#include "winuser.h"
38 #include <ole2.h>
39 //#include "objbase.h"
40 #include <rpcproxy.h>
41 #include <mlang.h>
42 #include <mimeole.h>
43 
44 #include <wine/list.h>
45 #include <wine/unicode.h>
46 #include <wine/debug.h>
47 
48 WINE_DEFAULT_DEBUG_CHANNEL(mlang);
49 
50 //#include "initguid.h"
51 
52 static HRESULT MultiLanguage_create(IUnknown *pUnkOuter, LPVOID *ppObj);
53 static HRESULT MLangConvertCharset_create(IUnknown *outer, void **obj);
54 static HRESULT EnumRfc1766_create(LANGID LangId, IEnumRfc1766 **ppEnum);
55 
56 static HINSTANCE instance;
57 static DWORD MLANG_tls_index; /* to store various per thead data */
58 
59 /* FIXME:
60  * Under what circumstances HKEY_CLASSES_ROOT\MIME\Database\Codepage and
61  * HKEY_CLASSES_ROOT\MIME\Database\Charset are used?
62  */
63 
64 typedef struct
65 {
66     const char *description;
67     UINT cp;
68     DWORD flags;
69     const char *web_charset;
70     const char *header_charset;
71     const char *body_charset;
72     const WCHAR *alias;
73 } MIME_CP_INFO;
74 
75 /* These data are based on the codepage info in libs/unicode/cpmap.pl */
76 /* FIXME: Add 28604 (Celtic), 28606 (Balkan) */
77 
78 static const MIME_CP_INFO arabic_cp[] =
79 {
80     { "Arabic (864)",
81       864, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
82            MIMECONTF_MIME_LATEST,
83       "ibm864", "ibm864", "ibm864" },
84     { "Arabic (1006)",
85       1006, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
86             MIMECONTF_MIME_LATEST,
87       "ibm1006", "ibm1006", "ibm1006" },
88     { "Arabic (Windows)",
89       1256, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
90             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
91             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
92       "windows-1256", "windows-1256", "windows-1256" },
93     { "Arabic (ISO)",
94       28596, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
95              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
96              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
97              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
98       "iso-8859-6", "iso-8859-6", "iso-8859-6" }
99 };
100 static const MIME_CP_INFO baltic_cp[] =
101 {
102     { "Baltic (DOS)",
103       775, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
104            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
105       "ibm775", "ibm775", "ibm775" },
106     { "Baltic (Windows)",
107       1257, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
108             MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
109             MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
110             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
111       "windows-1257", "windows-1257", "windows-1257" },
112     { "Baltic (ISO)",
113       28594, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
114              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
115              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
116              MIMECONTF_MIME_LATEST,
117       "iso-8859-4", "iso-8859-4", "iso-8859-4" },
118     { "Estonian (ISO)",
119       28603, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
120              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
121       "iso-8859-13", "iso-8859-13", "iso-8859-13" }
122 };
123 static const MIME_CP_INFO chinese_simplified_cp[] =
124 {
125     { "Chinese Simplified (Auto-Select)",
126       50936, MIMECONTF_IMPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
127              MIMECONTF_MIME_LATEST,
128       "_autodetect_chs", "_autodetect_chs", "_autodetect_chs" },
129     { "Chinese Simplified (GB2312)",
130       936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
131            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID |
132            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
133            MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
134       "gb2312", "gb2312", "gb2312" },
135     { "Chinese Simplified (GB2312-80)",
136       20936, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
137              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
138       "x-cp20936", "x-cp20936", "x-cp20936" },
139     { "Chinese Simplified (HZ)",
140       52936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
141              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
142              MIMECONTF_VALID | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
143              MIMECONTF_MIME_LATEST,
144       "hz-gb-2312", "hz-gb-2312", "hz-gb-2312" },
145     { "Chinese Simplified (GB18030)",
146       54936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
147              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
148              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
149              MIMECONTF_MIME_LATEST,
150       "GB18030", "GB18030", "GB18030" },
151     { "Chinese Simplified (GBK)",
152       936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
153            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
154            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
155            MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
156       "gbk", "gbk", "gbk" }
157 };
158 static const MIME_CP_INFO chinese_traditional_cp[] =
159 {
160     { "Chinese Traditional (Auto-Select)",
161       50950, MIMECONTF_IMPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
162              MIMECONTF_MIME_LATEST,
163       "_autodetect_cht", "_autodetect_cht", "_autodetect_cht" },
164     { "Chinese Traditional (Big5)",
165       950, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
166            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
167            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
168            MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
169       "big5", "big5", "big5" },
170     { "Chinese Traditional (CNS)",
171       20000, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
172              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
173       "x-Chinese-CNS", "x-Chinese-CNS", "x-Chinese-CNS" }
174 };
175 static const MIME_CP_INFO central_european_cp[] =
176 {
177     { "Central European (DOS)",
178       852, MIMECONTF_BROWSER | MIMECONTF_IMPORT | MIMECONTF_SAVABLE_BROWSER |
179            MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
180            MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
181       "ibm852", "ibm852", "ibm852" },
182     { "Central European (Windows)",
183       1250, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
184             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
185             MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
186             MIMECONTF_MIME_LATEST,
187       "windows-1250", "windows-1250", "windows-1250" },
188     { "Central European (Mac)",
189       10029, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
190              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
191       "x-mac-ce", "x-mac-ce", "x-mac-ce" },
192     { "Central European (ISO)",
193       28592, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
194              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
195              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
196              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
197       "iso-8859-2", "iso-8859-2", "iso-8859-2" }
198 };
199 static const MIME_CP_INFO cyrillic_cp[] =
200 {
201     { "OEM Cyrillic",
202       855, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
203            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
204       "ibm855", "ibm855", "ibm855" },
205     { "Cyrillic (DOS)",
206       866, MIMECONTF_BROWSER | MIMECONTF_IMPORT | MIMECONTF_SAVABLE_BROWSER |
207            MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
208            MIMECONTF_MIME_LATEST,
209       "cp866", "cp866", "cp866" },
210 #if 0 /* Windows has 20866 as an official code page for KOI8-R */
211     { "Cyrillic (KOI8-R)",
212       878, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
213            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
214       "koi8-r", "koi8-r", "koi8-r" },
215 #endif
216     { "Cyrillic (Windows)",
217       1251, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
218             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
219             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
220       "windows-1251", "windows-1251", "windows-1251" },
221     { "Cyrillic (Mac)",
222       10007, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
223              MIMECONTF_MIME_LATEST,
224       "x-mac-cyrillic", "x-mac-cyrillic", "x-mac-cyrillic" },
225     { "Cyrillic (KOI8-R)",
226       20866, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
227              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
228              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
229              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
230       "koi8-r", "koi8-r", "koi8-r" },
231     { "Cyrillic (KOI8-U)",
232       21866, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
233              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
234              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
235              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
236       "koi8-u", "koi8-u", "koi8-u" },
237     { "Cyrillic (ISO)",
238       28595, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
239              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
240              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
241              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
242       "iso-8859-5", "iso-8859-5", "iso-8859-5" }
243 };
244 static const MIME_CP_INFO greek_cp[] =
245 {
246     { "Greek (DOS)",
247       737, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
248            MIMECONTF_MIME_LATEST,
249       "ibm737", "ibm737", "ibm737" },
250     { "Greek, Modern (DOS)",
251       869, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
252            MIMECONTF_MIME_LATEST,
253       "ibm869", "ibm869", "ibm869" },
254     { "IBM EBCDIC (Greek Modern)",
255       875, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
256            MIMECONTF_MIME_LATEST,
257       "cp875", "cp875", "cp875" },
258     { "Greek (Windows)",
259       1253, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
260             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
261             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
262       "windows-1253", "windows-1253", "windows-1253" },
263     { "Greek (Mac)",
264       10006, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
265              MIMECONTF_MIME_LATEST,
266       "x-mac-greek", "x-mac-greek", "x-mac-greek" },
267     { "Greek (ISO)",
268       28597, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
269              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
270              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
271              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
272       "iso-8859-7", "iso-8859-7", "iso-8859-7" }
273 };
274 static const MIME_CP_INFO hebrew_cp[] =
275 {
276     { "Hebrew (424)",
277       424, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
278            MIMECONTF_MIME_LATEST,
279       "ibm424", "ibm424", "ibm424" },
280     { "Hebrew (856)",
281       856, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
282            MIMECONTF_MIME_LATEST,
283       "cp856", "cp856", "cp856" },
284     { "Hebrew (DOS)",
285       862, MIMECONTF_BROWSER | MIMECONTF_MINIMAL | MIMECONTF_IMPORT |
286            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
287            MIMECONTF_MIME_LATEST,
288       "dos-862", "dos-862", "dos-862" },
289     { "Hebrew (Windows)",
290       1255, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
291             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
292             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
293       "windows-1255", "windows-1255", "windows-1255" },
294     { "Hebrew (ISO-Visual)",
295       28598, MIMECONTF_BROWSER | MIMECONTF_MINIMAL | MIMECONTF_IMPORT |
296              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
297              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
298       "iso-8859-8", "iso-8859-8", "iso-8859-8" }
299 };
300 static const MIME_CP_INFO japanese_cp[] =
301 {
302     { "Japanese (Auto-Select)",
303       50932, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
304              MIMECONTF_IMPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
305              MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
306       "_autodetect", "_autodetect", "_autodetect" },
307     { "Japanese (EUC)",
308       51932, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
309              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
310              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
311              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
312       "euc-jp", "euc-jp", "euc-jp" },
313     { "Japanese (JIS)",
314       50220, MIMECONTF_IMPORT | MIMECONTF_MAILNEWS | MIMECONTF_EXPORT |
315              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID_NLS |
316              MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST |
317              MIMECONTF_MIME_IE4,
318       "iso-2022-jp","iso-2022-jp","iso-2022-jp"},
319     { "Japanese (JIS 0208-1990 and 0212-1990)",
320       20932, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
321              MIMECONTF_VALID | MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
322       "EUC-JP","EUC-JP","EUC-JP"},
323     { "Japanese (JIS-Allow 1 byte Kana)",
324       50221, MIMECONTF_MAILNEWS | MIMECONTF_EXPORT | MIMECONTF_SAVABLE_BROWSER |
325              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID_NLS |
326              MIMECONTF_VALID | MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
327       "csISO2022JP","iso-2022-jp","iso-2022-jp"},
328     { "Japanese (JIS-Allow 1 byte Kana - SO/SI)",
329       50222, MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_VALID |
330              MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
331       "iso-2022-jp","iso-2022-jp","iso-2022-jp"},
332     { "Japanese (Mac)",
333       10001, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
334              MIMECONTF_VALID | MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
335       "x-mac-japanese","x-mac-japanese","x-mac-japanese"},
336     { "Japanese (Shift-JIS)",
337       932, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
338            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
339            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
340            MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
341       "shift_jis", "iso-2022-jp", "iso-2022-jp" }
342 };
343 static const MIME_CP_INFO korean_cp[] =
344 {
345     { "Korean",
346       949, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
347            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
348            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
349            MIMECONTF_MIME_LATEST,
350       "ks_c_5601-1987", "ks_c_5601-1987", "ks_c_5601-1987" }
351 };
352 static const MIME_CP_INFO thai_cp[] =
353 {
354     { "Thai (Windows)",
355       874, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_MIME_LATEST,
356       "ibm-thai", "ibm-thai", "ibm-thai" }
357 };
358 static const MIME_CP_INFO turkish_cp[] =
359 {
360     { "Turkish (DOS)",
361       857, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
362            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
363       "ibm857", "ibm857", "ibm857" },
364     { "IBM EBCDIC (Turkish Latin-5)",
365       1026, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
366             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
367       "ibm1026", "ibm1026", "ibm1026" },
368     { "Turkish (Windows)",
369       1254, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
370             MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
371             MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
372             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
373       "windows-1254", "windows-1254", "windows-1254" },
374     { "Turkish (Mac)",
375       10081, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
376              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
377       "x-mac-turkish", "x-mac-turkish", "x-mac-turkish" },
378     { "Latin 3 (ISO)",
379       28593, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT |
380              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_EXPORT | MIMECONTF_VALID |
381              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
382       "iso-8859-3", "iso-8859-3", "iso-8859-3" },
383     { "Turkish (ISO)",
384       28599, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
385              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
386              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
387              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
388       "iso-8859-9", "iso-8859-9", "iso-8859-9" }
389 };
390 static const MIME_CP_INFO vietnamese_cp[] =
391 {
392     { "Vietnamese (Windows)",
393       1258, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
394             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
395             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
396             MIMECONTF_MIME_LATEST,
397       "windows-1258", "windows-1258", "windows-1258" }
398 };
399 
400 static const WCHAR asciiW[] = {'a','s','c','i','i',0};
401 
402 static const MIME_CP_INFO western_cp[] =
403 {
404     { "IBM EBCDIC (US-Canada)",
405       37, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
406           MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
407       "ibm037", "ibm037", "ibm037" },
408     { "OEM United States",
409       437, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
410            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
411       "ibm437", "ibm437", "ibm437" },
412     { "IBM EBCDIC (International)",
413       500, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
414            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
415       "ibm500", "ibm500", "ibm500" },
416     { "Western European (DOS)",
417       850, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
418            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
419       "ibm850", "ibm850", "ibm850" },
420     { "Portuguese (DOS)",
421       860, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
422            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
423       "ibm860", "ibm860", "ibm860" },
424     { "Icelandic (DOS)",
425       861, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
426            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
427       "ibm861", "ibm861", "ibm861" },
428     { "French Canadian (DOS)",
429       863, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
430            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
431       "ibm863", "ibm863", "ibm863" },
432     { "Nordic (DOS)",
433       865, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
434            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
435       "ibm865", "ibm865", "ibm865" },
436     { "Western European (Windows)",
437       1252, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
438             MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
439             MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
440             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
441       "windows-1252", "windows-1252", "iso-8859-1" },
442     { "Western European (Mac)",
443       10000, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
444              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
445       "macintosh", "macintosh", "macintosh" },
446     { "Icelandic (Mac)",
447       10079, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
448              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
449       "x-mac-icelandic", "x-mac-icelandic", "x-mac-icelandic" },
450     { "US-ASCII",
451       20127, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT | MIMECONTF_EXPORT |
452              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID |
453              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
454       "us-ascii", "us-ascii", "us-ascii", asciiW },
455     { "Western European (ISO)",
456       28591, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
457              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
458              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
459              MIMECONTF_MIME_LATEST,
460       "iso-8859-1", "iso-8859-1", "iso-8859-1" },
461     { "Latin 9 (ISO)",
462       28605, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT |
463              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
464              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
465              MIMECONTF_MIME_LATEST,
466       "iso-8859-15", "iso-8859-15", "iso-8859-15" }
467 };
468 static const MIME_CP_INFO unicode_cp[] =
469 {
470     { "Unicode",
471       CP_UNICODE, MIMECONTF_MINIMAL | MIMECONTF_IMPORT |
472                   MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
473                   MIMECONTF_VALID | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
474                   MIMECONTF_MIME_LATEST,
475       "unicode", "unicode", "unicode" },
476     { "Unicode (UTF-7)",
477       CP_UTF7, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT |
478                MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_EXPORT | MIMECONTF_VALID |
479                MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
480       "utf-7", "utf-7", "utf-7" },
481     { "Unicode (UTF-8)",
482       CP_UTF8, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
483                MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
484                MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
485                MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
486       "utf-8", "utf-8", "utf-8" }
487 };
488 
489 static const struct mlang_data
490 {
491     const char *description;
492     UINT family_codepage;
493     UINT number_of_cp;
494     const MIME_CP_INFO *mime_cp_info;
495     const char *fixed_font;
496     const char *proportional_font;
497     SCRIPT_ID sid;
498 } mlang_data[] =
499 {
500     { "Arabic",1256,sizeof(arabic_cp)/sizeof(arabic_cp[0]),arabic_cp,
501       "Simplified Arabic Fixed","Simplified Arabic", sidArabic },
502     { "Baltic",1257,sizeof(baltic_cp)/sizeof(baltic_cp[0]),baltic_cp,
503       "Courier New","Arial", sidAsciiLatin },
504     { "Chinese Simplified",936,sizeof(chinese_simplified_cp)/sizeof(chinese_simplified_cp[0]),chinese_simplified_cp,
505       "Simsun","Simsun", sidHan },
506     { "Chinese Traditional",950,sizeof(chinese_traditional_cp)/sizeof(chinese_traditional_cp[0]),chinese_traditional_cp,
507       "MingLiu","New MingLiu", sidBopomofo },
508     { "Central European",1250,sizeof(central_european_cp)/sizeof(central_european_cp[0]),central_european_cp,
509       "Courier New","Arial", sidAsciiLatin },
510     { "Cyrillic",1251,sizeof(cyrillic_cp)/sizeof(cyrillic_cp[0]),cyrillic_cp,
511       "Courier New","Arial", sidCyrillic },
512     { "Greek",1253,sizeof(greek_cp)/sizeof(greek_cp[0]),greek_cp,
513       "Courier New","Arial", sidGreek },
514     { "Hebrew",1255,sizeof(hebrew_cp)/sizeof(hebrew_cp[0]),hebrew_cp,
515       "Miriam Fixed","David", sidHebrew },
516     { "Japanese",932,sizeof(japanese_cp)/sizeof(japanese_cp[0]),japanese_cp,
517       "MS Gothic","MS PGothic", sidKana },
518     { "Korean",949,sizeof(korean_cp)/sizeof(korean_cp[0]),korean_cp,
519       "GulimChe","Gulim", sidHangul },
520     { "Thai",874,sizeof(thai_cp)/sizeof(thai_cp[0]),thai_cp,
521       "Tahoma","Tahoma", sidThai },
522     { "Turkish",1254,sizeof(turkish_cp)/sizeof(turkish_cp[0]),turkish_cp,
523       "Courier New","Arial", sidAsciiLatin },
524     { "Vietnamese",1258,sizeof(vietnamese_cp)/sizeof(vietnamese_cp[0]),vietnamese_cp,
525       "Courier New","Arial", sidAsciiLatin },
526     { "Western European",1252,sizeof(western_cp)/sizeof(western_cp[0]),western_cp,
527       "Courier New","Arial", sidAsciiLatin },
528     { "Unicode",CP_UNICODE,sizeof(unicode_cp)/sizeof(unicode_cp[0]),unicode_cp,
529       "Courier New","Arial" }
530 };
531 
532 struct font_list
533 {
534     struct list list_entry;
535     HFONT base_font;
536     HFONT font;
537     UINT charset;
538 };
539 
540 static struct list font_cache = LIST_INIT(font_cache);
541 static CRITICAL_SECTION font_cache_critical;
542 static CRITICAL_SECTION_DEBUG font_cache_critical_debug =
543 {
544     0, 0, &font_cache_critical,
545     { &font_cache_critical_debug.ProcessLocksList, &font_cache_critical_debug.ProcessLocksList },
546     0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_critical") }
547 };
548 static CRITICAL_SECTION font_cache_critical = { &font_cache_critical_debug, -1, 0, 0, 0, 0 };
549 
550 static void fill_cp_info(const struct mlang_data *ml_data, UINT index, MIMECPINFO *mime_cp_info);
551 
552 static LONG dll_count;
553 
554 /*
555  * Japanese Detection and Conversion Functions
556  */
557 
558 #define HANKATA(A)  ((A >= 161) && (A <= 223))
559 #define ISEUC(A)    ((A >= 161) && (A <= 254))
560 #define NOTEUC(A,B) (((A >= 129) && (A <= 159)) && ((B >= 64) && (B <= 160)))
561 #define SJIS1(A)    (((A >= 129) && (A <= 159)) || ((A >= 224) && (A <= 239)))
562 #define SJIS2(A)    ((A >= 64) && (A <= 252))
563 #define ISMARU(A)   ((A >= 202) && (A <= 206))
564 #define ISNIGORI(A) (((A >= 182) && (A <= 196)) || ((A >= 202) && (A <= 206)))
565 
566 static UINT DetectJapaneseCode(LPCSTR input, DWORD count)
567 {
568     UINT code = 0;
569     DWORD i = 0;
570     unsigned char c1,c2;
571 
572     while ((code == 0 || code == 51932) && i < count)
573     {
574         c1 = input[i];
575         if (c1 == 0x1b /* ESC */)
576         {
577             i++;
578             if (i >= count)
579                 return code;
580             c1 = input[i];
581             if (c1 == '$')
582             {
583                 i++;
584                 if (i >= count)
585                     return code;
586                 c1 = input[i];
587                 if (c1 =='B' || c1 == '@')
588                     code = 50220;
589             }
590             if (c1 == 'K')
591                 code = 50220;
592         }
593         else if (c1 >= 129)
594         {
595             i++;
596             if (i >= count)
597                 return code;
598             c2 = input[i];
599             if NOTEUC(c1,c2)
600                 code = 932;
601             else if (ISEUC(c1) && ISEUC(c2))
602                 code = 51932;
603             else if (((c1 == 142)) && HANKATA(c2))
604                 code = 51932;
605         }
606         i++;
607     }
608     return code;
609 }
610 
611 static inline void jis2sjis(unsigned char *p1, unsigned char *p2)
612 {
613     unsigned char c1 = *p1;
614     unsigned char c2 = *p2;
615     int row = c1 < 95 ? 112 : 176;
616     int cell = c1 % 2 ? 31 + (c2 > 95) : 126;
617 
618     *p1 = ((c1 + 1) >> 1) + row;
619     *p2 = c2 + cell;
620 }
621 
622 static inline void sjis2jis(unsigned char *p1, unsigned char *p2)
623 {
624     unsigned char c1 = *p1;
625     unsigned char c2 = *p2;
626     int shift = c2 < 159;
627     int row = c1 < 160 ? 112 : 176;
628     int cell = shift ? (31 + (c2 > 127)): 126;
629 
630     *p1 = ((c1 - row) << 1) - shift;
631     *p2 -= cell;
632 }
633 
634 static int han2zen(unsigned char *p1, unsigned char *p2)
635 {
636     BOOL maru = FALSE;
637     BOOL nigori = FALSE;
638     static const unsigned char char1[] = {129,129,129,129,129,131,131,131,131,
639         131,131,131,131,131,131,129,131,131,131,131,131,131,131,131,131,131,
640         131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
641         131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
642         131,129,129 };
643     static const unsigned char char2[] = {66,117,118,65,69,146,64,66,68,70,
644         72,131,133,135,98,91,65,67,69,71,73,74,76,78,80,82,84,86,88,90,92,94,
645         96,99,101,103,105,106,107,108,109,110,113,116,119,122,125,126,128,
646         129,130,132,134,136,137,138,139,140,141,143,147,74,75};
647 
648     if (( *p2 == 222) && ((ISNIGORI(*p1) || (*p1 == 179))))
649             nigori = TRUE;
650     else if ((*p2 == 223) && (ISMARU(*p1)))
651             maru = TRUE;
652 
653     if (*p1 >= 161 && *p1 <= 223)
654     {
655         unsigned char index = *p1 - 161;
656         *p1 = char1[index];
657         *p2 = char2[index];
658     }
659 
660     if (maru || nigori)
661     {
662         if (nigori)
663         {
664             if (((*p2 >= 74) && (*p2 <= 103)) || ((*p2 >= 110) && (*p2 <= 122)))
665                 (*p2)++;
666             else if ((*p1 == 131) && (*p2 == 69))
667                 *p2 = 148;
668         }
669         else if ((maru) && ((*p2 >= 110) && (*p2 <= 122)))
670             *p2+= 2;
671 
672         return 1;
673     }
674 
675     return 0;
676 }
677 
678 
679 static UINT ConvertJIS2SJIS(LPCSTR input, DWORD count, LPSTR output)
680 {
681     DWORD i = 0;
682     int j = 0;
683     unsigned char p2,p;
684     BOOL shifted = FALSE;
685 
686     while (i < count)
687     {
688         p = input[i];
689         if (p == 0x1b /* ESC */)
690         {
691             i++;
692             if (i >= count)
693                 return 0;
694             p2 = input[i];
695             if (p2 == '$' || p2 =='(')
696                 i++;
697             if (p2 == 'K' || p2 =='$')
698                 shifted = TRUE;
699             else
700                 shifted = FALSE;
701         }
702         else
703         {
704             if (shifted)
705             {
706                 i++;
707                 if (i >= count)
708                     return 0;
709                 p2 = input[i];
710                 jis2sjis(&p,&p2);
711                 output[j++]=p;
712                 output[j++]=p2;
713             }
714             else
715             {
716                 output[j++] = p;
717             }
718         }
719         i++;
720     }
721     return j;
722 }
723 
724 static inline int exit_shift(LPSTR out, int c)
725 {
726     if (out)
727     {
728         out[c] = 0x1b;
729         out[c+1] = '(';
730         out[c+2] = 'B';
731     }
732     return 3;
733 }
734 
735 static inline int enter_shift(LPSTR out, int c)
736 {
737     if (out)
738     {
739         out[c] = 0x1b;
740         out[c+1] = '$';
741         out[c+2] = 'B';
742     }
743     return 3;
744 }
745 
746 
747 static UINT ConvertSJIS2JIS(LPCSTR input, DWORD count, LPSTR output)
748 {
749     DWORD i = 0;
750     int j = 0;
751     unsigned char p2,p;
752     BOOL shifted = FALSE;
753 
754     while (i < count)
755     {
756         p = input[i] & 0xff;
757         if (p == 10 || p == 13) /* NL and CR */
758         {
759             if (shifted)
760             {
761                 shifted = FALSE;
762                 j += exit_shift(output,j);
763             }
764             if (output)
765                 output[j++] = p;
766             else
767                 j++;
768         }
769         else
770         {
771             if (SJIS1(p))
772             {
773                 i++;
774                 if (i >= count)
775                     return 0;
776                 p2 = input[i] & 0xff;
777                 if (SJIS2(p2))
778                 {
779                     sjis2jis(&p,&p2);
780                     if (!shifted)
781                     {
782                         shifted = TRUE;
783                         j+=enter_shift(output,j);
784                     }
785                 }
786 
787                 if (output)
788                 {
789                     output[j++]=p;
790                     output[j++]=p2;
791                 }
792                 else
793                     j+=2;
794             }
795             else
796             {
797                 if (HANKATA(p))
798                 {
799                     if ((i+1) >= count)
800                         return 0;
801                     p2 = input[i+1] & 0xff;
802                     i+=han2zen(&p,&p2);
803                     sjis2jis(&p,&p2);
804                     if (!shifted)
805                     {
806                         shifted = TRUE;
807                         j+=enter_shift(output,j);
808                     }
809                     if (output)
810                     {
811                         output[j++]=p;
812                         output[j++]=p2;
813                     }
814                     else
815                         j+=2;
816                 }
817                 else
818                 {
819                     if (shifted)
820                     {
821                         shifted = FALSE;
822                         j += exit_shift(output,j);
823                     }
824                     if (output)
825                         output[j++]=p;
826                     else
827                         j++;
828                 }
829             }
830         }
831         i++;
832     }
833     if (shifted)
834         j += exit_shift(output,j);
835     return j;
836 }
837 
838 static UINT ConvertJISJapaneseToUnicode(LPCSTR input, DWORD count,
839                                         LPWSTR output, DWORD out_count)
840 {
841     CHAR *sjis_string;
842     UINT rc = 0;
843     sjis_string = HeapAlloc(GetProcessHeap(),0,count);
844     rc = ConvertJIS2SJIS(input,count,sjis_string);
845     if (rc)
846     {
847         TRACE("%s\n",debugstr_an(sjis_string,rc));
848         if (output)
849             rc = MultiByteToWideChar(932,0,sjis_string,rc,output,out_count);
850         else
851             rc = MultiByteToWideChar(932,0,sjis_string,rc,0,0);
852     }
853     HeapFree(GetProcessHeap(),0,sjis_string);
854     return rc;
855 
856 }
857 
858 static UINT ConvertUnknownJapaneseToUnicode(LPCSTR input, DWORD count,
859                                             LPWSTR output, DWORD out_count)
860 {
861     CHAR *sjis_string;
862     UINT rc = 0;
863     int code = DetectJapaneseCode(input,count);
864     TRACE("Japanese code %i\n",code);
865 
866     switch (code)
867     {
868     case 0:
869         if (output)
870             rc = MultiByteToWideChar(CP_ACP,0,input,count,output,out_count);
871         else
872             rc = MultiByteToWideChar(CP_ACP,0,input,count,0,0);
873         break;
874 
875     case 932:
876         if (output)
877             rc = MultiByteToWideChar(932,0,input,count,output,out_count);
878         else
879             rc = MultiByteToWideChar(932,0,input,count,0,0);
880         break;
881 
882     case 51932:
883         if (output)
884             rc = MultiByteToWideChar(20932,0,input,count,output,out_count);
885         else
886             rc = MultiByteToWideChar(20932,0,input,count,0,0);
887         break;
888 
889     case 50220:
890         sjis_string = HeapAlloc(GetProcessHeap(),0,count);
891         rc = ConvertJIS2SJIS(input,count,sjis_string);
892         if (rc)
893         {
894             TRACE("%s\n",debugstr_an(sjis_string,rc));
895             if (output)
896                 rc = MultiByteToWideChar(932,0,sjis_string,rc,output,out_count);
897             else
898                 rc = MultiByteToWideChar(932,0,sjis_string,rc,0,0);
899         }
900         HeapFree(GetProcessHeap(),0,sjis_string);
901         break;
902     }
903     return rc;
904 }
905 
906 static UINT ConvertJapaneseUnicodeToJIS(LPCWSTR input, DWORD count,
907                                         LPSTR output, DWORD out_count)
908 {
909     CHAR *sjis_string;
910     INT len;
911     UINT rc = 0;
912 
913     len = WideCharToMultiByte(932,0,input,count,0,0,NULL,NULL);
914     sjis_string = HeapAlloc(GetProcessHeap(),0,len);
915     WideCharToMultiByte(932,0,input,count,sjis_string,len,NULL,NULL);
916     TRACE("%s\n",debugstr_an(sjis_string,len));
917 
918     rc = ConvertSJIS2JIS(sjis_string, len, NULL);
919     if (out_count >= rc)
920     {
921         ConvertSJIS2JIS(sjis_string, len, output);
922     }
923     HeapFree(GetProcessHeap(),0,sjis_string);
924     return rc;
925 
926 }
927 
928 /*
929  * Dll lifetime tracking declaration
930  */
931 static void LockModule(void)
932 {
933     InterlockedIncrement(&dll_count);
934 }
935 
936 static void UnlockModule(void)
937 {
938     InterlockedDecrement(&dll_count);
939 }
940 
941 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
942 {
943     switch(fdwReason) {
944         case DLL_PROCESS_ATTACH:
945             instance = hInstDLL;
946             MLANG_tls_index = TlsAlloc();
947             DisableThreadLibraryCalls(hInstDLL);
948 	    break;
949 	case DLL_PROCESS_DETACH:
950             if (lpv) break;
951             TlsFree(MLANG_tls_index);
952 	    break;
953     }
954     return TRUE;
955 }
956 
957 HRESULT WINAPI ConvertINetMultiByteToUnicode(
958     LPDWORD pdwMode,
959     DWORD dwEncoding,
960     LPCSTR pSrcStr,
961     LPINT pcSrcSize,
962     LPWSTR pDstStr,
963     LPINT pcDstSize)
964 {
965     INT src_len = -1;
966 
967     TRACE("%p %d %s %p %p %p\n", pdwMode, dwEncoding,
968           debugstr_a(pSrcStr), pcSrcSize, pDstStr, pcDstSize);
969 
970     if (!pcDstSize)
971         return E_FAIL;
972 
973     if (!pcSrcSize)
974         pcSrcSize = &src_len;
975 
976     if (!*pcSrcSize)
977     {
978         *pcDstSize = 0;
979         return S_OK;
980     }
981 
982     /* forwarding euc-jp to EUC-JP */
983     if (dwEncoding == 51932)
984         dwEncoding = 20932;
985 
986     switch (dwEncoding)
987     {
988     case CP_UNICODE:
989         if (*pcSrcSize == -1)
990             *pcSrcSize = lstrlenW((LPCWSTR)pSrcStr);
991         *pcDstSize = min(*pcSrcSize, *pcDstSize);
992         *pcSrcSize *= sizeof(WCHAR);
993         if (pDstStr)
994             memmove(pDstStr, pSrcStr, *pcDstSize * sizeof(WCHAR));
995         break;
996 
997     case 50220:
998     case 50221:
999     case 50222:
1000         *pcDstSize = ConvertJISJapaneseToUnicode(pSrcStr,*pcSrcSize,pDstStr,*pcDstSize);
1001         break;
1002     case 50932:
1003         *pcDstSize = ConvertUnknownJapaneseToUnicode(pSrcStr,*pcSrcSize,pDstStr,*pcDstSize);
1004         break;
1005 
1006     default:
1007         if (*pcSrcSize == -1)
1008             *pcSrcSize = lstrlenA(pSrcStr);
1009 
1010         if (pDstStr)
1011             *pcDstSize = MultiByteToWideChar(dwEncoding, 0, pSrcStr, *pcSrcSize, pDstStr, *pcDstSize);
1012         else
1013             *pcDstSize = MultiByteToWideChar(dwEncoding, 0, pSrcStr, *pcSrcSize, NULL, 0);
1014         break;
1015     }
1016 
1017     if (!*pcDstSize)
1018         return E_FAIL;
1019 
1020     return S_OK;
1021 }
1022 
1023 HRESULT WINAPI ConvertINetUnicodeToMultiByte(
1024     LPDWORD pdwMode,
1025     DWORD dwEncoding,
1026     LPCWSTR pSrcStr,
1027     LPINT pcSrcSize,
1028     LPSTR pDstStr,
1029     LPINT pcDstSize)
1030 {
1031     INT destsz, size;
1032     INT src_len = -1;
1033 
1034     TRACE("%p %d %s %p %p %p\n", pdwMode, dwEncoding,
1035           debugstr_w(pSrcStr), pcSrcSize, pDstStr, pcDstSize);
1036 
1037     if (!pcDstSize)
1038         return S_OK;
1039 
1040     if (!pcSrcSize)
1041         pcSrcSize = &src_len;
1042 
1043     destsz = (pDstStr) ? *pcDstSize : 0;
1044     *pcDstSize = 0;
1045 
1046     if (!pSrcStr || !*pcSrcSize)
1047         return S_OK;
1048 
1049     if (*pcSrcSize == -1)
1050         *pcSrcSize = lstrlenW(pSrcStr);
1051 
1052     /* forwarding euc-jp to EUC-JP */
1053     if (dwEncoding == 51932)
1054         dwEncoding = 20932;
1055 
1056     if (dwEncoding == CP_UNICODE)
1057     {
1058         if (*pcSrcSize == -1)
1059             *pcSrcSize = lstrlenW(pSrcStr);
1060 
1061         size = min(*pcSrcSize, destsz) * sizeof(WCHAR);
1062         if (pDstStr)
1063             memmove(pDstStr, pSrcStr, size);
1064 
1065         if (size >= destsz)
1066             goto fail;
1067     }
1068     else if (dwEncoding == 50220 || dwEncoding == 50221 || dwEncoding == 50222)
1069     {
1070         size = ConvertJapaneseUnicodeToJIS(pSrcStr, *pcSrcSize, NULL, 0);
1071         if (!size)
1072             goto fail;
1073 
1074         if (pDstStr)
1075         {
1076             size = ConvertJapaneseUnicodeToJIS(pSrcStr, *pcSrcSize, pDstStr,
1077                                                destsz);
1078             if (!size)
1079                 goto fail;
1080         }
1081 
1082     }
1083     else
1084     {
1085         size = WideCharToMultiByte(dwEncoding, 0, pSrcStr, *pcSrcSize,
1086                                    NULL, 0, NULL, NULL);
1087         if (!size)
1088             goto fail;
1089 
1090         if (pDstStr)
1091         {
1092             size = WideCharToMultiByte(dwEncoding, 0, pSrcStr, *pcSrcSize,
1093                                        pDstStr, destsz, NULL, NULL);
1094             if (!size)
1095                 goto fail;
1096         }
1097     }
1098 
1099     *pcDstSize = size;
1100     return S_OK;
1101 
1102 fail:
1103     *pcSrcSize = 0;
1104     *pcDstSize = 0;
1105     return E_FAIL;
1106 }
1107 
1108 HRESULT WINAPI ConvertINetString(
1109     LPDWORD pdwMode,
1110     DWORD dwSrcEncoding,
1111     DWORD dwDstEncoding,
1112     LPCSTR pSrcStr,
1113     LPINT pcSrcSize,
1114     LPSTR pDstStr,
1115     LPINT pcDstSize
1116 )
1117 {
1118     TRACE("%p %d %d %s %p %p %p\n", pdwMode, dwSrcEncoding, dwDstEncoding,
1119           debugstr_a(pSrcStr), pcSrcSize, pDstStr, pcDstSize);
1120 
1121     if (dwSrcEncoding == CP_UNICODE)
1122     {
1123         INT cSrcSizeW;
1124         if (pcSrcSize && *pcSrcSize != -1)
1125         {
1126             cSrcSizeW = *pcSrcSize / sizeof(WCHAR);
1127             pcSrcSize = &cSrcSizeW;
1128         }
1129         return ConvertINetUnicodeToMultiByte(pdwMode, dwDstEncoding, (LPCWSTR)pSrcStr, pcSrcSize, pDstStr, pcDstSize);
1130     }
1131     else if (dwDstEncoding == CP_UNICODE)
1132     {
1133         HRESULT hr = ConvertINetMultiByteToUnicode(pdwMode, dwSrcEncoding, pSrcStr, pcSrcSize, (LPWSTR)pDstStr, pcDstSize);
1134         *pcDstSize *= sizeof(WCHAR);
1135         return hr;
1136     }
1137     else
1138     {
1139         INT cDstSizeW;
1140         LPWSTR pDstStrW;
1141         HRESULT hr;
1142 
1143         TRACE("convert %s from %d to %d\n", debugstr_a(pSrcStr), dwSrcEncoding, dwDstEncoding);
1144 
1145         hr = ConvertINetMultiByteToUnicode(pdwMode, dwSrcEncoding, pSrcStr, pcSrcSize, NULL, &cDstSizeW);
1146         if (hr != S_OK)
1147             return hr;
1148 
1149         pDstStrW = HeapAlloc(GetProcessHeap(), 0, cDstSizeW * sizeof(WCHAR));
1150         hr = ConvertINetMultiByteToUnicode(pdwMode, dwSrcEncoding, pSrcStr, pcSrcSize, pDstStrW, &cDstSizeW);
1151         if (hr == S_OK)
1152             hr = ConvertINetUnicodeToMultiByte(pdwMode, dwDstEncoding, pDstStrW, &cDstSizeW, pDstStr, pcDstSize);
1153 
1154         HeapFree(GetProcessHeap(), 0, pDstStrW);
1155         return hr;
1156     }
1157 }
1158 
1159 static HRESULT GetFamilyCodePage(
1160     UINT uiCodePage,
1161     UINT* puiFamilyCodePage)
1162 {
1163     UINT i, n;
1164 
1165     TRACE("%u %p\n", uiCodePage, puiFamilyCodePage);
1166 
1167     if (!puiFamilyCodePage) return S_FALSE;
1168 
1169     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
1170     {
1171         for (n = 0; n < mlang_data[i].number_of_cp; n++)
1172         {
1173             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
1174             {
1175                 *puiFamilyCodePage = mlang_data[i].family_codepage;
1176                 return S_OK;
1177             }
1178         }
1179     }
1180 
1181     return S_FALSE;
1182 }
1183 
1184 HRESULT WINAPI IsConvertINetStringAvailable(
1185     DWORD dwSrcEncoding,
1186     DWORD dwDstEncoding)
1187 {
1188     UINT src_family, dst_family;
1189 
1190     TRACE("%d %d\n", dwSrcEncoding, dwDstEncoding);
1191 
1192     if (GetFamilyCodePage(dwSrcEncoding, &src_family) != S_OK ||
1193         GetFamilyCodePage(dwDstEncoding, &dst_family) != S_OK)
1194         return S_FALSE;
1195 
1196     if (src_family == dst_family) return S_OK;
1197 
1198     /* we can convert any codepage to/from unicode */
1199     if (src_family == CP_UNICODE || dst_family == CP_UNICODE) return S_OK;
1200 
1201     return S_FALSE;
1202 }
1203 
1204 static inline HRESULT lcid_to_rfc1766A( LCID lcid, LPSTR rfc1766, INT len )
1205 {
1206     CHAR buffer[MAX_RFC1766_NAME];
1207     INT n = GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, buffer, MAX_RFC1766_NAME);
1208     INT i;
1209 
1210     if (n)
1211     {
1212         i = PRIMARYLANGID(lcid);
1213         if ((((i == LANG_ENGLISH) || (i == LANG_CHINESE) || (i == LANG_ARABIC)) &&
1214             (SUBLANGID(lcid) == SUBLANG_DEFAULT)) ||
1215             (SUBLANGID(lcid) > SUBLANG_DEFAULT)) {
1216 
1217             buffer[n - 1] = '-';
1218             i = GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, buffer + n, MAX_RFC1766_NAME - n);
1219             if (!i)
1220                 buffer[n - 1] = '\0';
1221         }
1222         else
1223             i = 0;
1224 
1225         LCMapStringA( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, buffer, n + i, rfc1766, len );
1226         return ((n + i) > len) ? E_INVALIDARG : S_OK;
1227     }
1228     return E_FAIL;
1229 }
1230 
1231 static inline HRESULT lcid_to_rfc1766W( LCID lcid, LPWSTR rfc1766, INT len )
1232 {
1233     WCHAR buffer[MAX_RFC1766_NAME];
1234     INT n = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME, buffer, MAX_RFC1766_NAME);
1235     INT i;
1236 
1237     if (n)
1238     {
1239         i = PRIMARYLANGID(lcid);
1240         if ((((i == LANG_ENGLISH) || (i == LANG_CHINESE) || (i == LANG_ARABIC)) &&
1241             (SUBLANGID(lcid) == SUBLANG_DEFAULT)) ||
1242             (SUBLANGID(lcid) > SUBLANG_DEFAULT)) {
1243 
1244             buffer[n - 1] = '-';
1245             i = GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME, buffer + n, MAX_RFC1766_NAME - n);
1246             if (!i)
1247                 buffer[n - 1] = '\0';
1248         }
1249         else
1250             i = 0;
1251 
1252         LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, buffer, n + i, rfc1766, len);
1253         return ((n + i) > len) ? E_INVALIDARG : S_OK;
1254     }
1255     return E_FAIL;
1256 }
1257 
1258 HRESULT WINAPI LcidToRfc1766A(
1259     LCID lcid,
1260     LPSTR pszRfc1766,
1261     INT nChar)
1262 {
1263     TRACE("%04x %p %u\n", lcid, pszRfc1766, nChar);
1264     if (!pszRfc1766)
1265         return E_INVALIDARG;
1266 
1267     return lcid_to_rfc1766A(lcid, pszRfc1766, nChar);
1268 }
1269 
1270 HRESULT WINAPI LcidToRfc1766W(
1271     LCID lcid,
1272     LPWSTR pszRfc1766,
1273     INT nChar)
1274 {
1275     TRACE("%04x %p %u\n", lcid, pszRfc1766, nChar);
1276     if (!pszRfc1766)
1277         return E_INVALIDARG;
1278 
1279     return lcid_to_rfc1766W(lcid, pszRfc1766, nChar);
1280 }
1281 
1282 static HRESULT lcid_from_rfc1766(IEnumRfc1766 *iface, LCID *lcid, LPCWSTR rfc1766)
1283 {
1284     RFC1766INFO info;
1285     ULONG num;
1286 
1287     while (IEnumRfc1766_Next(iface, 1, &info, &num) == S_OK)
1288     {
1289         if (!strcmpiW(info.wszRfc1766, rfc1766))
1290         {
1291             *lcid = info.lcid;
1292             return S_OK;
1293         }
1294         if (strlenW(rfc1766) == 2 && !memcmp(info.wszRfc1766, rfc1766, 2 * sizeof(WCHAR)))
1295         {
1296             *lcid = PRIMARYLANGID(info.lcid);
1297             return S_OK;
1298         }
1299     }
1300 
1301     return E_FAIL;
1302 }
1303 
1304 HRESULT WINAPI Rfc1766ToLcidW(LCID *pLocale, LPCWSTR pszRfc1766)
1305 {
1306     IEnumRfc1766 *enumrfc1766;
1307     HRESULT hr;
1308 
1309     TRACE("(%p, %s)\n", pLocale, debugstr_w(pszRfc1766));
1310 
1311     if (!pLocale || !pszRfc1766)
1312         return E_INVALIDARG;
1313 
1314     hr = EnumRfc1766_create(0, &enumrfc1766);
1315     if (FAILED(hr))
1316         return hr;
1317 
1318     hr = lcid_from_rfc1766(enumrfc1766, pLocale, pszRfc1766);
1319     IEnumRfc1766_Release(enumrfc1766);
1320 
1321     return hr;
1322 }
1323 
1324 HRESULT WINAPI Rfc1766ToLcidA(LCID *lcid, LPCSTR rfc1766A)
1325 {
1326     WCHAR rfc1766W[MAX_RFC1766_NAME + 1];
1327 
1328     if (!rfc1766A)
1329         return E_INVALIDARG;
1330 
1331     MultiByteToWideChar(CP_ACP, 0, rfc1766A, -1, rfc1766W, MAX_RFC1766_NAME);
1332     rfc1766W[MAX_RFC1766_NAME] = 0;
1333 
1334     return Rfc1766ToLcidW(lcid, rfc1766W);
1335 }
1336 
1337 static HRESULT map_font(HDC hdc, DWORD codepages, HFONT src_font, HFONT *dst_font)
1338 {
1339     struct font_list *font_list_entry;
1340     CHARSETINFO charset_info;
1341     HFONT new_font, old_font;
1342     LOGFONTW font_attr;
1343     DWORD mask, Csb[2];
1344     BOOL found_cached;
1345     UINT charset;
1346     BOOL ret;
1347     UINT i;
1348 
1349     if (hdc == NULL || src_font == NULL) return E_FAIL;
1350 
1351     for (i = 0; i < 32; i++)
1352     {
1353         mask = (DWORD)(1 << i);
1354         if (codepages & mask)
1355         {
1356             Csb[0] = mask;
1357             Csb[1] = 0x0;
1358             ret = TranslateCharsetInfo(Csb, &charset_info, TCI_SRCFONTSIG);
1359             if (!ret) continue;
1360 
1361             /* use cached font if possible */
1362             found_cached = FALSE;
1363             EnterCriticalSection(&font_cache_critical);
1364             LIST_FOR_EACH_ENTRY(font_list_entry, &font_cache, struct font_list, list_entry)
1365             {
1366                 if (font_list_entry->charset == charset_info.ciCharset &&
1367                     font_list_entry->base_font == src_font)
1368                 {
1369                     if (dst_font != NULL)
1370                         *dst_font = font_list_entry->font;
1371                     found_cached = TRUE;
1372                 }
1373             }
1374             LeaveCriticalSection(&font_cache_critical);
1375             if (found_cached) return S_OK;
1376 
1377             GetObjectW(src_font, sizeof(font_attr), &font_attr);
1378             font_attr.lfCharSet = (BYTE)charset_info.ciCharset;
1379             font_attr.lfWidth = 0;
1380             font_attr.lfFaceName[0] = 0;
1381             new_font = CreateFontIndirectW(&font_attr);
1382             if (new_font == NULL) continue;
1383 
1384             old_font = SelectObject(hdc, new_font);
1385             charset = GetTextCharset(hdc);
1386             SelectObject(hdc, old_font);
1387             if (charset == charset_info.ciCharset)
1388             {
1389                 font_list_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*font_list_entry));
1390                 if (font_list_entry == NULL) return E_OUTOFMEMORY;
1391 
1392                 font_list_entry->base_font = src_font;
1393                 font_list_entry->font = new_font;
1394                 font_list_entry->charset = charset;
1395 
1396                 EnterCriticalSection(&font_cache_critical);
1397                 list_add_tail(&font_cache, &font_list_entry->list_entry);
1398                 LeaveCriticalSection(&font_cache_critical);
1399 
1400                 if (dst_font != NULL)
1401                     *dst_font = new_font;
1402                 return S_OK;
1403             }
1404         }
1405     }
1406 
1407     return E_FAIL;
1408 }
1409 
1410 static HRESULT release_font(HFONT font)
1411 {
1412     struct font_list *font_list_entry;
1413     HRESULT hr;
1414 
1415     hr = E_FAIL;
1416     EnterCriticalSection(&font_cache_critical);
1417     LIST_FOR_EACH_ENTRY(font_list_entry, &font_cache, struct font_list, list_entry)
1418     {
1419         if (font_list_entry->font == font)
1420         {
1421             list_remove(&font_list_entry->list_entry);
1422             DeleteObject(font);
1423             HeapFree(GetProcessHeap(), 0, font_list_entry);
1424             hr = S_OK;
1425             break;
1426         }
1427     }
1428     LeaveCriticalSection(&font_cache_critical);
1429 
1430     return hr;
1431 }
1432 
1433 static HRESULT clear_font_cache(void)
1434 {
1435     struct font_list *font_list_entry;
1436     struct font_list *font_list_entry2;
1437 
1438     EnterCriticalSection(&font_cache_critical);
1439     LIST_FOR_EACH_ENTRY_SAFE(font_list_entry, font_list_entry2, &font_cache, struct font_list, list_entry)
1440     {
1441         list_remove(&font_list_entry->list_entry);
1442         DeleteObject(font_list_entry->font);
1443         HeapFree(GetProcessHeap(), 0, font_list_entry);
1444     }
1445     LeaveCriticalSection(&font_cache_critical);
1446 
1447     return S_OK;
1448 }
1449 
1450 /******************************************************************************
1451  * MLANG ClassFactory
1452  */
1453 typedef struct {
1454     IClassFactory IClassFactory_iface;
1455     LONG ref;
1456     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
1457 } IClassFactoryImpl;
1458 
1459 static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
1460 {
1461     return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
1462 }
1463 
1464 struct object_creation_info
1465 {
1466     const CLSID *clsid;
1467     LPCSTR szClassName;
1468     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
1469 };
1470 
1471 static const struct object_creation_info object_creation[] =
1472 {
1473     { &CLSID_CMultiLanguage, "CLSID_CMultiLanguage", MultiLanguage_create },
1474     { &CLSID_CMLangConvertCharset, "CLSID_CMLangConvertCharset", MLangConvertCharset_create }
1475 };
1476 
1477 static HRESULT WINAPI MLANGCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj)
1478 {
1479     TRACE("%s\n", debugstr_guid(riid) );
1480 
1481     if (IsEqualGUID(riid, &IID_IUnknown)
1482 	|| IsEqualGUID(riid, &IID_IClassFactory))
1483     {
1484 	IClassFactory_AddRef(iface);
1485         *ppobj = iface;
1486 	return S_OK;
1487     }
1488 
1489     *ppobj = NULL;
1490     WARN("(%p)->(%s,%p), not found\n", iface, debugstr_guid(riid), ppobj);
1491     return E_NOINTERFACE;
1492 }
1493 
1494 static ULONG WINAPI MLANGCF_AddRef(IClassFactory *iface)
1495 {
1496     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
1497     return InterlockedIncrement(&This->ref);
1498 }
1499 
1500 static ULONG WINAPI MLANGCF_Release(IClassFactory *iface)
1501 {
1502     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
1503     ULONG ref = InterlockedDecrement(&This->ref);
1504 
1505     if (ref == 0)
1506     {
1507         TRACE("Destroying %p\n", This);
1508 	HeapFree(GetProcessHeap(), 0, This);
1509     }
1510 
1511     return ref;
1512 }
1513 
1514 static HRESULT WINAPI MLANGCF_CreateInstance(IClassFactory *iface, IUnknown *pOuter,
1515         REFIID riid, void **ppobj)
1516 {
1517     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
1518     HRESULT hres;
1519     LPUNKNOWN punk;
1520 
1521     TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
1522 
1523     *ppobj = NULL;
1524     hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk);
1525     if (SUCCEEDED(hres)) {
1526         hres = IUnknown_QueryInterface(punk, riid, ppobj);
1527         IUnknown_Release(punk);
1528     }
1529     TRACE("returning (%p) -> %x\n", *ppobj, hres);
1530     return hres;
1531 }
1532 
1533 static HRESULT WINAPI MLANGCF_LockServer(IClassFactory *iface, BOOL dolock)
1534 {
1535     if (dolock)
1536         LockModule();
1537     else
1538         UnlockModule();
1539 
1540     return S_OK;
1541 }
1542 
1543 static const IClassFactoryVtbl MLANGCF_Vtbl =
1544 {
1545     MLANGCF_QueryInterface,
1546     MLANGCF_AddRef,
1547     MLANGCF_Release,
1548     MLANGCF_CreateInstance,
1549     MLANGCF_LockServer
1550 };
1551 
1552 /******************************************************************
1553  *		DllGetClassObject (MLANG.@)
1554  */
1555 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
1556 {
1557     unsigned int i;
1558     IClassFactoryImpl *factory;
1559 
1560     TRACE("%s %s %p\n",debugstr_guid(rclsid), debugstr_guid(iid), ppv);
1561 
1562     if ( !IsEqualGUID( &IID_IClassFactory, iid )
1563 	 && ! IsEqualGUID( &IID_IUnknown, iid) )
1564 	return E_NOINTERFACE;
1565 
1566     for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
1567     {
1568 	if (IsEqualGUID(object_creation[i].clsid, rclsid))
1569 	    break;
1570     }
1571 
1572     if (i == sizeof(object_creation)/sizeof(object_creation[0]))
1573     {
1574 	FIXME("%s: no class found.\n", debugstr_guid(rclsid));
1575 	return CLASS_E_CLASSNOTAVAILABLE;
1576     }
1577 
1578     TRACE("Creating a class factory for %s\n",object_creation[i].szClassName);
1579 
1580     factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory));
1581     if (factory == NULL) return E_OUTOFMEMORY;
1582 
1583     factory->IClassFactory_iface.lpVtbl = &MLANGCF_Vtbl;
1584     factory->ref = 1;
1585 
1586     factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
1587 
1588     *ppv = &factory->IClassFactory_iface;
1589 
1590     TRACE("(%p) <- %p\n", ppv, &factory->IClassFactory_iface);
1591 
1592     return S_OK;
1593 }
1594 
1595 
1596 /******************************************************************************/
1597 
1598 typedef struct tagMLang_impl
1599 {
1600     IMLangFontLink IMLangFontLink_iface;
1601     IMultiLanguage IMultiLanguage_iface;
1602     IMultiLanguage3 IMultiLanguage3_iface;
1603     IMLangFontLink2 IMLangFontLink2_iface;
1604     IMLangLineBreakConsole IMLangLineBreakConsole_iface;
1605     LONG ref;
1606     DWORD total_cp, total_scripts;
1607 } MLang_impl;
1608 
1609 /******************************************************************************/
1610 
1611 typedef struct tagEnumCodePage_impl
1612 {
1613     IEnumCodePage IEnumCodePage_iface;
1614     LONG ref;
1615     MIMECPINFO *cpinfo;
1616     DWORD total, pos;
1617 } EnumCodePage_impl;
1618 
1619 static inline EnumCodePage_impl *impl_from_IEnumCodePage( IEnumCodePage *iface )
1620 {
1621     return CONTAINING_RECORD( iface, EnumCodePage_impl, IEnumCodePage_iface );
1622 }
1623 
1624 static HRESULT WINAPI fnIEnumCodePage_QueryInterface(
1625         IEnumCodePage* iface,
1626         REFIID riid,
1627         void** ppvObject)
1628 {
1629     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1630 
1631     TRACE("%p -> %s\n", This, debugstr_guid(riid) );
1632 
1633     if (IsEqualGUID(riid, &IID_IUnknown)
1634 	|| IsEqualGUID(riid, &IID_IEnumCodePage))
1635     {
1636 	IEnumCodePage_AddRef(iface);
1637         TRACE("Returning IID_IEnumCodePage %p ref = %d\n", This, This->ref);
1638 	*ppvObject = &This->IEnumCodePage_iface;
1639         return S_OK;
1640     }
1641 
1642     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
1643     return E_NOINTERFACE;
1644 }
1645 
1646 static ULONG WINAPI fnIEnumCodePage_AddRef(
1647         IEnumCodePage* iface)
1648 {
1649     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1650     return InterlockedIncrement(&This->ref);
1651 }
1652 
1653 static ULONG WINAPI fnIEnumCodePage_Release(
1654         IEnumCodePage* iface)
1655 {
1656     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1657     ULONG ref = InterlockedDecrement(&This->ref);
1658 
1659     TRACE("%p ref = %d\n", This, ref);
1660     if (ref == 0)
1661     {
1662         TRACE("Destroying %p\n", This);
1663         HeapFree(GetProcessHeap(), 0, This->cpinfo);
1664         HeapFree(GetProcessHeap(), 0, This);
1665     }
1666 
1667     return ref;
1668 }
1669 
1670 static HRESULT WINAPI fnIEnumCodePage_Clone(
1671         IEnumCodePage* iface,
1672         IEnumCodePage** ppEnum)
1673 {
1674     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1675     FIXME("%p %p\n", This, ppEnum);
1676     return E_NOTIMPL;
1677 }
1678 
1679 static  HRESULT WINAPI fnIEnumCodePage_Next(
1680         IEnumCodePage* iface,
1681         ULONG celt,
1682         PMIMECPINFO rgelt,
1683         ULONG* pceltFetched)
1684 {
1685     ULONG i;
1686     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1687 
1688     TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched);
1689 
1690     if (!pceltFetched) return S_FALSE;
1691     *pceltFetched = 0;
1692 
1693     if (!rgelt) return S_FALSE;
1694 
1695     if (This->pos + celt > This->total)
1696         celt = This->total - This->pos;
1697 
1698     if (!celt) return S_FALSE;
1699 
1700     memcpy(rgelt, This->cpinfo + This->pos, celt * sizeof(MIMECPINFO));
1701     *pceltFetched = celt;
1702     This->pos += celt;
1703 
1704     for (i = 0; i < celt; i++)
1705     {
1706         TRACE("#%u: %08x %u %u %s %s %s %s %s %s %d\n",
1707               i, rgelt[i].dwFlags, rgelt[i].uiCodePage,
1708               rgelt[i].uiFamilyCodePage,
1709               wine_dbgstr_w(rgelt[i].wszDescription),
1710               wine_dbgstr_w(rgelt[i].wszWebCharset),
1711               wine_dbgstr_w(rgelt[i].wszHeaderCharset),
1712               wine_dbgstr_w(rgelt[i].wszBodyCharset),
1713               wine_dbgstr_w(rgelt[i].wszFixedWidthFont),
1714               wine_dbgstr_w(rgelt[i].wszProportionalFont),
1715               rgelt[i].bGDICharset);
1716     }
1717     return S_OK;
1718 }
1719 
1720 static HRESULT WINAPI fnIEnumCodePage_Reset(
1721         IEnumCodePage* iface)
1722 {
1723     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1724 
1725     TRACE("%p\n", This);
1726 
1727     This->pos = 0;
1728     return S_OK;
1729 }
1730 
1731 static  HRESULT WINAPI fnIEnumCodePage_Skip(
1732         IEnumCodePage* iface,
1733         ULONG celt)
1734 {
1735     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1736 
1737     TRACE("%p %u\n", This, celt);
1738 
1739     if (celt >= This->total) return S_FALSE;
1740 
1741     This->pos += celt;
1742     return S_OK;
1743 }
1744 
1745 static const IEnumCodePageVtbl IEnumCodePage_vtbl =
1746 {
1747     fnIEnumCodePage_QueryInterface,
1748     fnIEnumCodePage_AddRef,
1749     fnIEnumCodePage_Release,
1750     fnIEnumCodePage_Clone,
1751     fnIEnumCodePage_Next,
1752     fnIEnumCodePage_Reset,
1753     fnIEnumCodePage_Skip
1754 };
1755 
1756 static HRESULT EnumCodePage_create( MLang_impl* mlang, DWORD grfFlags,
1757                      LANGID LangId, IEnumCodePage** ppEnumCodePage )
1758 {
1759     EnumCodePage_impl *ecp;
1760     MIMECPINFO *cpinfo;
1761     UINT i, n;
1762 
1763     TRACE("%p, %08x, %04x, %p\n", mlang, grfFlags, LangId, ppEnumCodePage);
1764 
1765     if (!grfFlags) /* enumerate internal data base of encodings */
1766         grfFlags = MIMECONTF_MIME_LATEST;
1767 
1768     ecp = HeapAlloc( GetProcessHeap(), 0, sizeof (EnumCodePage_impl) );
1769     ecp->IEnumCodePage_iface.lpVtbl = &IEnumCodePage_vtbl;
1770     ecp->ref = 1;
1771     ecp->pos = 0;
1772     ecp->total = 0;
1773     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
1774     {
1775         for (n = 0; n < mlang_data[i].number_of_cp; n++)
1776         {
1777             if (mlang_data[i].mime_cp_info[n].flags & grfFlags)
1778                 ecp->total++;
1779         }
1780     }
1781 
1782     ecp->cpinfo = HeapAlloc(GetProcessHeap(), 0,
1783                             sizeof(MIMECPINFO) * ecp->total);
1784     cpinfo = ecp->cpinfo;
1785 
1786     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
1787     {
1788         for (n = 0; n < mlang_data[i].number_of_cp; n++)
1789         {
1790             if (mlang_data[i].mime_cp_info[n].flags & grfFlags)
1791                 fill_cp_info(&mlang_data[i], n, cpinfo++);
1792         }
1793     }
1794 
1795     TRACE("enumerated %d codepages with flags %08x\n", ecp->total, grfFlags);
1796 
1797     *ppEnumCodePage = &ecp->IEnumCodePage_iface;
1798 
1799     return S_OK;
1800 }
1801 
1802 /******************************************************************************/
1803 
1804 typedef struct tagEnumScript_impl
1805 {
1806     IEnumScript IEnumScript_iface;
1807     LONG ref;
1808     SCRIPTINFO *script_info;
1809     DWORD total, pos;
1810 } EnumScript_impl;
1811 
1812 static inline EnumScript_impl *impl_from_IEnumScript( IEnumScript *iface )
1813 {
1814     return CONTAINING_RECORD( iface, EnumScript_impl, IEnumScript_iface );
1815 }
1816 
1817 static HRESULT WINAPI fnIEnumScript_QueryInterface(
1818         IEnumScript* iface,
1819         REFIID riid,
1820         void** ppvObject)
1821 {
1822     EnumScript_impl *This = impl_from_IEnumScript( iface );
1823 
1824     TRACE("%p -> %s\n", This, debugstr_guid(riid) );
1825 
1826     if (IsEqualGUID(riid, &IID_IUnknown)
1827         || IsEqualGUID(riid, &IID_IEnumScript))
1828     {
1829         IEnumScript_AddRef(iface);
1830         TRACE("Returning IID_IEnumScript %p ref = %d\n", This, This->ref);
1831         *ppvObject = &This->IEnumScript_iface;
1832         return S_OK;
1833     }
1834 
1835     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
1836     return E_NOINTERFACE;
1837 }
1838 
1839 static ULONG WINAPI fnIEnumScript_AddRef(
1840         IEnumScript* iface)
1841 {
1842     EnumScript_impl *This = impl_from_IEnumScript( iface );
1843     return InterlockedIncrement(&This->ref);
1844 }
1845 
1846 static ULONG WINAPI fnIEnumScript_Release(
1847         IEnumScript* iface)
1848 {
1849     EnumScript_impl *This = impl_from_IEnumScript( iface );
1850     ULONG ref = InterlockedDecrement(&This->ref);
1851 
1852     TRACE("%p ref = %d\n", This, ref);
1853     if (ref == 0)
1854     {
1855         TRACE("Destroying %p\n", This);
1856         HeapFree(GetProcessHeap(), 0, This->script_info);
1857         HeapFree(GetProcessHeap(), 0, This);
1858     }
1859 
1860     return ref;
1861 }
1862 
1863 static HRESULT WINAPI fnIEnumScript_Clone(
1864         IEnumScript* iface,
1865         IEnumScript** ppEnum)
1866 {
1867     EnumScript_impl *This = impl_from_IEnumScript( iface );
1868     FIXME("%p %p: stub!\n", This, ppEnum);
1869     return E_NOTIMPL;
1870 }
1871 
1872 static  HRESULT WINAPI fnIEnumScript_Next(
1873         IEnumScript* iface,
1874         ULONG celt,
1875         PSCRIPTINFO rgelt,
1876         ULONG* pceltFetched)
1877 {
1878     EnumScript_impl *This = impl_from_IEnumScript( iface );
1879 
1880     TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched);
1881 
1882     if (!pceltFetched || !rgelt) return E_FAIL;
1883 
1884     *pceltFetched = 0;
1885 
1886     if (This->pos + celt > This->total)
1887         celt = This->total - This->pos;
1888 
1889     if (!celt) return S_FALSE;
1890 
1891     memcpy(rgelt, This->script_info + This->pos, celt * sizeof(SCRIPTINFO));
1892     *pceltFetched = celt;
1893     This->pos += celt;
1894 
1895     return S_OK;
1896 }
1897 
1898 static HRESULT WINAPI fnIEnumScript_Reset(
1899         IEnumScript* iface)
1900 {
1901     EnumScript_impl *This = impl_from_IEnumScript( iface );
1902 
1903     TRACE("%p\n", This);
1904 
1905     This->pos = 0;
1906     return S_OK;
1907 }
1908 
1909 static  HRESULT WINAPI fnIEnumScript_Skip(
1910         IEnumScript* iface,
1911         ULONG celt)
1912 {
1913     EnumScript_impl *This = impl_from_IEnumScript( iface );
1914 
1915     TRACE("%p %u\n", This, celt);
1916 
1917     if (celt >= This->total) return S_FALSE;
1918 
1919     This->pos += celt;
1920     return S_OK;
1921 }
1922 
1923 static const IEnumScriptVtbl IEnumScript_vtbl =
1924 {
1925     fnIEnumScript_QueryInterface,
1926     fnIEnumScript_AddRef,
1927     fnIEnumScript_Release,
1928     fnIEnumScript_Clone,
1929     fnIEnumScript_Next,
1930     fnIEnumScript_Reset,
1931     fnIEnumScript_Skip
1932 };
1933 
1934 static HRESULT EnumScript_create( MLang_impl* mlang, DWORD dwFlags,
1935                      LANGID LangId, IEnumScript** ppEnumScript )
1936 {
1937     EnumScript_impl *es;
1938     UINT i;
1939 
1940     TRACE("%p, %08x, %04x, %p\n", mlang, dwFlags, LangId, ppEnumScript);
1941 
1942     if (!dwFlags) /* enumerate all available scripts */
1943         dwFlags = SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM;
1944 
1945     es = HeapAlloc( GetProcessHeap(), 0, sizeof (EnumScript_impl) );
1946     es->IEnumScript_iface.lpVtbl = &IEnumScript_vtbl;
1947     es->ref = 1;
1948     es->pos = 0;
1949     /* do not enumerate unicode flavours */
1950     es->total = sizeof(mlang_data)/sizeof(mlang_data[0]) - 1;
1951     es->script_info = HeapAlloc(GetProcessHeap(), 0, sizeof(SCRIPTINFO) * es->total);
1952 
1953     for (i = 0; i < es->total; i++)
1954     {
1955         es->script_info[i].ScriptId = i;
1956         es->script_info[i].uiCodePage = mlang_data[i].family_codepage;
1957         MultiByteToWideChar(CP_ACP, 0, mlang_data[i].description, -1,
1958             es->script_info[i].wszDescription, MAX_SCRIPT_NAME);
1959         MultiByteToWideChar(CP_ACP, 0, mlang_data[i].fixed_font, -1,
1960             es->script_info[i].wszFixedWidthFont, MAX_MIMEFACE_NAME);
1961         MultiByteToWideChar(CP_ACP, 0, mlang_data[i].proportional_font, -1,
1962             es->script_info[i].wszProportionalFont, MAX_MIMEFACE_NAME);
1963     }
1964 
1965     TRACE("enumerated %d scripts with flags %08x\n", es->total, dwFlags);
1966 
1967     *ppEnumScript = &es->IEnumScript_iface;
1968 
1969     return S_OK;
1970 }
1971 
1972 /******************************************************************************/
1973 
1974 static inline MLang_impl *impl_from_IMLangFontLink( IMLangFontLink *iface )
1975 {
1976     return CONTAINING_RECORD( iface, MLang_impl, IMLangFontLink_iface );
1977 }
1978 
1979 static HRESULT WINAPI fnIMLangFontLink_QueryInterface(
1980         IMLangFontLink* iface,
1981         REFIID riid,
1982         void** ppvObject)
1983 {
1984     MLang_impl *This = impl_from_IMLangFontLink( iface );
1985     return IMultiLanguage3_QueryInterface( &This->IMultiLanguage3_iface, riid, ppvObject );
1986 }
1987 
1988 static ULONG WINAPI fnIMLangFontLink_AddRef(
1989         IMLangFontLink* iface)
1990 {
1991     MLang_impl *This = impl_from_IMLangFontLink( iface );
1992     return IMultiLanguage3_AddRef( &This->IMultiLanguage3_iface );
1993 }
1994 
1995 static ULONG WINAPI fnIMLangFontLink_Release(
1996         IMLangFontLink* iface)
1997 {
1998     MLang_impl *This = impl_from_IMLangFontLink( iface );
1999     return IMultiLanguage3_Release( &This->IMultiLanguage3_iface );
2000 }
2001 
2002 static HRESULT WINAPI fnIMLangFontLink_GetCharCodePages(
2003         IMLangFontLink* iface,
2004         WCHAR ch_src,
2005         DWORD* codepages)
2006 {
2007     MLang_impl *This = impl_from_IMLangFontLink( iface );
2008     return IMLangFontLink2_GetCharCodePages(&This->IMLangFontLink2_iface, ch_src, codepages);
2009 }
2010 
2011 static HRESULT WINAPI fnIMLangFontLink_GetStrCodePages(
2012         IMLangFontLink* iface,
2013         const WCHAR* src,
2014         LONG src_len,
2015         DWORD priority_cp,
2016         DWORD* codepages,
2017         LONG* ret_len)
2018 {
2019     MLang_impl *This = impl_from_IMLangFontLink( iface );
2020     return IMLangFontLink2_GetStrCodePages(&This->IMLangFontLink2_iface, src, src_len, priority_cp,
2021         codepages, ret_len);
2022 }
2023 
2024 static HRESULT WINAPI fnIMLangFontLink_CodePageToCodePages(
2025         IMLangFontLink* iface,
2026         UINT codepage,
2027         DWORD* codepages)
2028 {
2029     MLang_impl *This = impl_from_IMLangFontLink( iface );
2030     return IMLangFontLink2_CodePageToCodePages(&This->IMLangFontLink2_iface, codepage, codepages);
2031 }
2032 
2033 static HRESULT WINAPI fnIMLangFontLink_CodePagesToCodePage(
2034         IMLangFontLink* iface,
2035         DWORD codepages,
2036         UINT def_codepage,
2037         UINT* codepage)
2038 {
2039     MLang_impl *This = impl_from_IMLangFontLink(iface);
2040     return IMLangFontLink2_CodePagesToCodePage(&This->IMLangFontLink2_iface, codepages,
2041         def_codepage, codepage);
2042 }
2043 
2044 static HRESULT WINAPI fnIMLangFontLink_GetFontCodePages(
2045         IMLangFontLink* iface,
2046         HDC hdc,
2047         HFONT hfont,
2048         DWORD* codepages)
2049 {
2050     MLang_impl *This = impl_from_IMLangFontLink(iface);
2051     return IMLangFontLink2_GetFontCodePages(&This->IMLangFontLink2_iface, hdc, hfont, codepages);
2052 }
2053 
2054 static HRESULT WINAPI fnIMLangFontLink_MapFont(
2055         IMLangFontLink* iface,
2056         HDC hDC,
2057         DWORD dwCodePages,
2058         HFONT hSrcFont,
2059         HFONT* phDestFont)
2060 {
2061     TRACE("(%p)->%p %08x %p %p\n",iface, hDC, dwCodePages, hSrcFont, phDestFont);
2062 
2063     return map_font(hDC, dwCodePages, hSrcFont, phDestFont);
2064 }
2065 
2066 static HRESULT WINAPI fnIMLangFontLink_ReleaseFont(
2067         IMLangFontLink* iface,
2068         HFONT hFont)
2069 {
2070     TRACE("(%p)->%p\n",iface, hFont);
2071 
2072     return release_font(hFont);
2073 }
2074 
2075 static HRESULT WINAPI fnIMLangFontLink_ResetFontMapping(
2076         IMLangFontLink* iface)
2077 {
2078     TRACE("(%p)\n",iface);
2079 
2080     return clear_font_cache();
2081 }
2082 
2083 
2084 static const IMLangFontLinkVtbl IMLangFontLink_vtbl =
2085 {
2086     fnIMLangFontLink_QueryInterface,
2087     fnIMLangFontLink_AddRef,
2088     fnIMLangFontLink_Release,
2089     fnIMLangFontLink_GetCharCodePages,
2090     fnIMLangFontLink_GetStrCodePages,
2091     fnIMLangFontLink_CodePageToCodePages,
2092     fnIMLangFontLink_CodePagesToCodePage,
2093     fnIMLangFontLink_GetFontCodePages,
2094     fnIMLangFontLink_MapFont,
2095     fnIMLangFontLink_ReleaseFont,
2096     fnIMLangFontLink_ResetFontMapping,
2097 };
2098 
2099 /******************************************************************************/
2100 
2101 static inline MLang_impl *impl_from_IMultiLanguage( IMultiLanguage *iface )
2102 {
2103     return CONTAINING_RECORD( iface, MLang_impl, IMultiLanguage_iface );
2104 }
2105 
2106 static HRESULT WINAPI fnIMultiLanguage_QueryInterface(
2107     IMultiLanguage* iface,
2108     REFIID riid,
2109     void** obj)
2110 {
2111     MLang_impl *This = impl_from_IMultiLanguage( iface );
2112     return IMultiLanguage3_QueryInterface(&This->IMultiLanguage3_iface, riid, obj);
2113 }
2114 
2115 static ULONG WINAPI fnIMultiLanguage_AddRef( IMultiLanguage* iface )
2116 {
2117     MLang_impl *This = impl_from_IMultiLanguage( iface );
2118     return IMultiLanguage3_AddRef(&This->IMultiLanguage3_iface);
2119 }
2120 
2121 static ULONG WINAPI fnIMultiLanguage_Release( IMultiLanguage* iface )
2122 {
2123     MLang_impl *This = impl_from_IMultiLanguage( iface );
2124     return IMultiLanguage3_Release(&This->IMultiLanguage3_iface);
2125 }
2126 
2127 static HRESULT WINAPI fnIMultiLanguage_GetNumberOfCodePageInfo(
2128     IMultiLanguage* iface,
2129     UINT* cp)
2130 {
2131     MLang_impl *This = impl_from_IMultiLanguage( iface );
2132     TRACE("(%p, %p)\n", This, cp);
2133     return IMultiLanguage3_GetNumberOfCodePageInfo(&This->IMultiLanguage3_iface, cp);
2134 }
2135 
2136 static HRESULT WINAPI fnIMultiLanguage_GetCodePageInfo(
2137     IMultiLanguage* iface,
2138     UINT uiCodePage,
2139     PMIMECPINFO pCodePageInfo)
2140 {
2141     UINT i, n;
2142     MLang_impl *This = impl_from_IMultiLanguage( iface );
2143 
2144     TRACE("%p, %u, %p\n", This, uiCodePage, pCodePageInfo);
2145 
2146     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2147     {
2148         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2149         {
2150             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
2151             {
2152                 fill_cp_info(&mlang_data[i], n, pCodePageInfo);
2153                 return S_OK;
2154             }
2155         }
2156     }
2157 
2158     return S_FALSE;
2159 }
2160 
2161 static HRESULT WINAPI fnIMultiLanguage_GetFamilyCodePage(
2162     IMultiLanguage* iface,
2163     UINT cp,
2164     UINT* family_cp)
2165 {
2166     MLang_impl *This = impl_from_IMultiLanguage( iface );
2167     return IMultiLanguage3_GetFamilyCodePage(&This->IMultiLanguage3_iface, cp, family_cp);
2168 }
2169 
2170 static HRESULT WINAPI fnIMultiLanguage_EnumCodePages(
2171     IMultiLanguage* iface,
2172     DWORD grfFlags,
2173     IEnumCodePage** ppEnumCodePage)
2174 {
2175     MLang_impl *This = impl_from_IMultiLanguage( iface );
2176 
2177     TRACE("%p %08x %p\n", This, grfFlags, ppEnumCodePage);
2178 
2179     return EnumCodePage_create( This, grfFlags, 0, ppEnumCodePage );
2180 }
2181 
2182 static HRESULT WINAPI fnIMultiLanguage_GetCharsetInfo(
2183     IMultiLanguage* iface,
2184     BSTR Charset,
2185     PMIMECSETINFO pCharsetInfo)
2186 {
2187     MLang_impl *This = impl_from_IMultiLanguage( iface );
2188     return IMultiLanguage3_GetCharsetInfo( &This->IMultiLanguage3_iface, Charset, pCharsetInfo );
2189 }
2190 
2191 static HRESULT WINAPI fnIMultiLanguage_IsConvertible(
2192     IMultiLanguage* iface,
2193     DWORD src_enc,
2194     DWORD dst_enc)
2195 {
2196     MLang_impl *This = impl_from_IMultiLanguage( iface );
2197     return IMultiLanguage3_IsConvertible(&This->IMultiLanguage3_iface, src_enc, dst_enc);
2198 }
2199 
2200 static HRESULT WINAPI fnIMultiLanguage_ConvertString(
2201     IMultiLanguage* iface,
2202     DWORD* mode,
2203     DWORD src_enc,
2204     DWORD dst_enc,
2205     BYTE* src,
2206     UINT* src_size,
2207     BYTE* dest,
2208     UINT* dest_size)
2209 {
2210     MLang_impl *This = impl_from_IMultiLanguage( iface );
2211     return IMultiLanguage3_ConvertString(&This->IMultiLanguage3_iface, mode, src_enc,
2212         dst_enc, src, src_size, dest, dest_size);
2213 }
2214 
2215 static HRESULT WINAPI fnIMultiLanguage_ConvertStringToUnicode(
2216     IMultiLanguage* iface,
2217     DWORD* mode,
2218     DWORD src_enc,
2219     CHAR* src,
2220     UINT* src_size,
2221     WCHAR* dest,
2222     UINT* dest_size)
2223 {
2224     MLang_impl *This = impl_from_IMultiLanguage( iface );
2225     return IMultiLanguage3_ConvertStringToUnicode(&This->IMultiLanguage3_iface,
2226         mode, src_enc, src, src_size, dest, dest_size);
2227 }
2228 
2229 static HRESULT WINAPI fnIMultiLanguage_ConvertStringFromUnicode(
2230     IMultiLanguage* iface,
2231     DWORD* mode,
2232     DWORD encoding,
2233     WCHAR* src,
2234     UINT* src_size,
2235     CHAR* dest,
2236     UINT* dest_size)
2237 {
2238     MLang_impl *This = impl_from_IMultiLanguage(iface);
2239     return IMultiLanguage3_ConvertStringFromUnicode(&This->IMultiLanguage3_iface,
2240         mode, encoding, src, src_size, dest, dest_size);
2241 }
2242 
2243 static HRESULT WINAPI fnIMultiLanguage_ConvertStringReset(
2244     IMultiLanguage* iface)
2245 {
2246     MLang_impl *This = impl_from_IMultiLanguage( iface );
2247     return IMultiLanguage3_ConvertStringReset(&This->IMultiLanguage3_iface);
2248 }
2249 
2250 static HRESULT WINAPI fnIMultiLanguage_GetRfc1766FromLcid(
2251     IMultiLanguage* iface,
2252     LCID lcid,
2253     BSTR* pbstrRfc1766)
2254 {
2255     MLang_impl *This = impl_from_IMultiLanguage(iface);
2256     return IMultiLanguage3_GetRfc1766FromLcid(&This->IMultiLanguage3_iface, lcid, pbstrRfc1766);
2257 }
2258 
2259 static HRESULT WINAPI fnIMultiLanguage_GetLcidFromRfc1766(
2260     IMultiLanguage* iface,
2261     LCID* locale,
2262     BSTR rfc1766)
2263 {
2264     MLang_impl *This = impl_from_IMultiLanguage(iface);
2265     return IMultiLanguage3_GetLcidFromRfc1766(&This->IMultiLanguage3_iface, locale, rfc1766);
2266 }
2267 
2268 /******************************************************************************/
2269 
2270 typedef struct tagEnumRfc1766_impl
2271 {
2272     IEnumRfc1766 IEnumRfc1766_iface;
2273     LONG ref;
2274     RFC1766INFO *info;
2275     DWORD total, pos;
2276 } EnumRfc1766_impl;
2277 
2278 static inline EnumRfc1766_impl *impl_from_IEnumRfc1766( IEnumRfc1766 *iface )
2279 {
2280     return CONTAINING_RECORD( iface, EnumRfc1766_impl, IEnumRfc1766_iface );
2281 }
2282 
2283 static HRESULT WINAPI fnIEnumRfc1766_QueryInterface(
2284         IEnumRfc1766 *iface,
2285         REFIID riid,
2286         void** ppvObject)
2287 {
2288     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2289 
2290     TRACE("%p -> %s\n", This, debugstr_guid(riid) );
2291 
2292     if (IsEqualGUID(riid, &IID_IUnknown)
2293         || IsEqualGUID(riid, &IID_IEnumRfc1766))
2294     {
2295         IEnumRfc1766_AddRef(iface);
2296         TRACE("Returning IID_IEnumRfc1766 %p ref = %d\n", This, This->ref);
2297         *ppvObject = &This->IEnumRfc1766_iface;
2298         return S_OK;
2299     }
2300 
2301     WARN("(%p) -> (%s,%p), not found\n",This,debugstr_guid(riid),ppvObject);
2302     return E_NOINTERFACE;
2303 }
2304 
2305 static ULONG WINAPI fnIEnumRfc1766_AddRef(
2306         IEnumRfc1766 *iface)
2307 {
2308     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2309     return InterlockedIncrement(&This->ref);
2310 }
2311 
2312 static ULONG WINAPI fnIEnumRfc1766_Release(
2313         IEnumRfc1766 *iface)
2314 {
2315     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2316     ULONG ref = InterlockedDecrement(&This->ref);
2317 
2318     TRACE("%p ref = %d\n", This, ref);
2319     if (ref == 0)
2320     {
2321         TRACE("Destroying %p\n", This);
2322         HeapFree(GetProcessHeap(), 0, This->info);
2323         HeapFree(GetProcessHeap(), 0, This);
2324     }
2325     return ref;
2326 }
2327 
2328 static HRESULT WINAPI fnIEnumRfc1766_Clone(
2329         IEnumRfc1766 *iface,
2330         IEnumRfc1766 **ppEnum)
2331 {
2332     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2333 
2334     FIXME("%p %p\n", This, ppEnum);
2335     return E_NOTIMPL;
2336 }
2337 
2338 static  HRESULT WINAPI fnIEnumRfc1766_Next(
2339         IEnumRfc1766 *iface,
2340         ULONG celt,
2341         PRFC1766INFO rgelt,
2342         ULONG *pceltFetched)
2343 {
2344     ULONG i;
2345     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2346 
2347     TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched);
2348 
2349     if (!pceltFetched) return S_FALSE;
2350     *pceltFetched = 0;
2351 
2352     if (!rgelt) return S_FALSE;
2353 
2354     if (This->pos + celt > This->total)
2355         celt = This->total - This->pos;
2356 
2357     if (!celt) return S_FALSE;
2358 
2359     memcpy(rgelt, This->info + This->pos, celt * sizeof(RFC1766INFO));
2360     *pceltFetched = celt;
2361     This->pos += celt;
2362 
2363     for (i = 0; i < celt; i++)
2364     {
2365         TRACE("#%u: %08x %s %s\n",
2366               i, rgelt[i].lcid,
2367               wine_dbgstr_w(rgelt[i].wszRfc1766),
2368               wine_dbgstr_w(rgelt[i].wszLocaleName));
2369     }
2370     return S_OK;
2371 }
2372 
2373 static HRESULT WINAPI fnIEnumRfc1766_Reset(
2374         IEnumRfc1766 *iface)
2375 {
2376     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2377 
2378     TRACE("%p\n", This);
2379 
2380     This->pos = 0;
2381     return S_OK;
2382 }
2383 
2384 static  HRESULT WINAPI fnIEnumRfc1766_Skip(
2385         IEnumRfc1766 *iface,
2386         ULONG celt)
2387 {
2388     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2389 
2390     TRACE("%p %u\n", This, celt);
2391 
2392     if (celt >= This->total) return S_FALSE;
2393 
2394     This->pos += celt;
2395     return S_OK;
2396 }
2397 
2398 static const IEnumRfc1766Vtbl IEnumRfc1766_vtbl =
2399 {
2400     fnIEnumRfc1766_QueryInterface,
2401     fnIEnumRfc1766_AddRef,
2402     fnIEnumRfc1766_Release,
2403     fnIEnumRfc1766_Clone,
2404     fnIEnumRfc1766_Next,
2405     fnIEnumRfc1766_Reset,
2406     fnIEnumRfc1766_Skip
2407 };
2408 
2409 struct enum_locales_data
2410 {
2411     RFC1766INFO *info;
2412     DWORD total, allocated;
2413 };
2414 
2415 static BOOL CALLBACK enum_locales_proc(LPWSTR locale)
2416 {
2417     WCHAR *end;
2418     struct enum_locales_data *data = TlsGetValue(MLANG_tls_index);
2419     RFC1766INFO *info;
2420 
2421     TRACE("%s\n", debugstr_w(locale));
2422 
2423     if (data->total >= data->allocated)
2424     {
2425         data->allocated += 32;
2426         data->info = HeapReAlloc(GetProcessHeap(), 0, data->info, data->allocated * sizeof(RFC1766INFO));
2427         if (!data->info) return FALSE;
2428     }
2429 
2430     info = &data->info[data->total];
2431 
2432     info->lcid = strtolW(locale, &end, 16);
2433     if (*end) /* invalid number */
2434         return FALSE;
2435 
2436     info->wszRfc1766[0] = 0;
2437     lcid_to_rfc1766W( info->lcid, info->wszRfc1766, MAX_RFC1766_NAME );
2438 
2439     info->wszLocaleName[0] = 0;
2440     GetLocaleInfoW(info->lcid, LOCALE_SLANGUAGE, info->wszLocaleName, MAX_LOCALE_NAME);
2441     TRACE("ISO639: %s SLANGUAGE: %s\n", wine_dbgstr_w(info->wszRfc1766), wine_dbgstr_w(info->wszLocaleName));
2442 
2443     data->total++;
2444 
2445     return TRUE;
2446 }
2447 
2448 static HRESULT EnumRfc1766_create(LANGID LangId, IEnumRfc1766 **ppEnum)
2449 {
2450     EnumRfc1766_impl *rfc;
2451     struct enum_locales_data data;
2452 
2453     TRACE("%04x, %p\n", LangId, ppEnum);
2454 
2455     rfc = HeapAlloc( GetProcessHeap(), 0, sizeof(EnumRfc1766_impl) );
2456     rfc->IEnumRfc1766_iface.lpVtbl = &IEnumRfc1766_vtbl;
2457     rfc->ref = 1;
2458     rfc->pos = 0;
2459     rfc->total = 0;
2460 
2461     data.total = 0;
2462     data.allocated = 160;
2463     data.info = HeapAlloc(GetProcessHeap(), 0, data.allocated * sizeof(RFC1766INFO));
2464     if (!data.info)
2465     {
2466         HeapFree(GetProcessHeap(), 0, rfc);
2467         return E_OUTOFMEMORY;
2468     }
2469 
2470     TlsSetValue(MLANG_tls_index, &data);
2471     EnumSystemLocalesW(enum_locales_proc, 0/*LOCALE_SUPPORTED*/);
2472     TlsSetValue(MLANG_tls_index, NULL);
2473 
2474     TRACE("enumerated %d rfc1766 structures\n", data.total);
2475 
2476     if (!data.total)
2477     {
2478         HeapFree(GetProcessHeap(), 0, data.info);
2479         HeapFree(GetProcessHeap(), 0, rfc);
2480         return E_FAIL;
2481     }
2482 
2483     rfc->info = data.info;
2484     rfc->total = data.total;
2485 
2486     *ppEnum = &rfc->IEnumRfc1766_iface;
2487     return S_OK;
2488 }
2489 
2490 static HRESULT WINAPI fnIMultiLanguage_EnumRfc1766(
2491     IMultiLanguage *iface,
2492     IEnumRfc1766 **ppEnumRfc1766)
2493 {
2494     MLang_impl *This = impl_from_IMultiLanguage( iface );
2495 
2496     TRACE("%p %p\n", This, ppEnumRfc1766);
2497 
2498     return EnumRfc1766_create(0, ppEnumRfc1766);
2499 }
2500 
2501 /******************************************************************************/
2502 
2503 static HRESULT WINAPI fnIMultiLanguage_GetRfc1766Info(
2504     IMultiLanguage* iface,
2505     LCID Locale,
2506     PRFC1766INFO pRfc1766Info)
2507 {
2508     LCTYPE type = LOCALE_SLANGUAGE;
2509 
2510     TRACE("(%p, 0x%04x, %p)\n", iface, Locale, pRfc1766Info);
2511 
2512     if (!pRfc1766Info)
2513         return E_INVALIDARG;
2514 
2515     if ((PRIMARYLANGID(Locale) == LANG_ENGLISH) ||
2516         (PRIMARYLANGID(Locale) == LANG_CHINESE) ||
2517         (PRIMARYLANGID(Locale) == LANG_ARABIC)) {
2518 
2519         if (!SUBLANGID(Locale))
2520             type = LOCALE_SENGLANGUAGE; /* suppress country */
2521     }
2522     else
2523     {
2524         if (!SUBLANGID(Locale)) {
2525             TRACE("SUBLANGID missing in 0x%04x\n", Locale);
2526             return E_FAIL;
2527         }
2528     }
2529 
2530     pRfc1766Info->lcid = Locale;
2531     pRfc1766Info->wszRfc1766[0] = 0;
2532     pRfc1766Info->wszLocaleName[0] = 0;
2533 
2534     if ((!lcid_to_rfc1766W(Locale, pRfc1766Info->wszRfc1766, MAX_RFC1766_NAME)) &&
2535         (GetLocaleInfoW(Locale, type, pRfc1766Info->wszLocaleName, MAX_LOCALE_NAME) > 0))
2536             return S_OK;
2537 
2538     /* Locale not supported */
2539     return E_INVALIDARG;
2540 }
2541 
2542 static HRESULT WINAPI fnIMultiLanguage_CreateConvertCharset(
2543     IMultiLanguage* iface,
2544     UINT src_cp,
2545     UINT dst_cp,
2546     DWORD prop,
2547     IMLangConvertCharset** convert_charset)
2548 {
2549     MLang_impl *This = impl_from_IMultiLanguage(iface);
2550     return IMultiLanguage3_CreateConvertCharset(&This->IMultiLanguage3_iface, src_cp, dst_cp, prop, convert_charset);
2551 }
2552 
2553 static const IMultiLanguageVtbl IMultiLanguage_vtbl =
2554 {
2555     fnIMultiLanguage_QueryInterface,
2556     fnIMultiLanguage_AddRef,
2557     fnIMultiLanguage_Release,
2558     fnIMultiLanguage_GetNumberOfCodePageInfo,
2559     fnIMultiLanguage_GetCodePageInfo,
2560     fnIMultiLanguage_GetFamilyCodePage,
2561     fnIMultiLanguage_EnumCodePages,
2562     fnIMultiLanguage_GetCharsetInfo,
2563     fnIMultiLanguage_IsConvertible,
2564     fnIMultiLanguage_ConvertString,
2565     fnIMultiLanguage_ConvertStringToUnicode,
2566     fnIMultiLanguage_ConvertStringFromUnicode,
2567     fnIMultiLanguage_ConvertStringReset,
2568     fnIMultiLanguage_GetRfc1766FromLcid,
2569     fnIMultiLanguage_GetLcidFromRfc1766,
2570     fnIMultiLanguage_EnumRfc1766,
2571     fnIMultiLanguage_GetRfc1766Info,
2572     fnIMultiLanguage_CreateConvertCharset,
2573 };
2574 
2575 
2576 /******************************************************************************/
2577 
2578 static inline MLang_impl *impl_from_IMultiLanguage3( IMultiLanguage3 *iface )
2579 {
2580     return CONTAINING_RECORD( iface, MLang_impl, IMultiLanguage3_iface );
2581 }
2582 
2583 static HRESULT WINAPI fnIMultiLanguage3_QueryInterface(
2584     IMultiLanguage3* iface,
2585     REFIID riid,
2586     void** obj)
2587 {
2588     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2589 
2590     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2591 
2592     if (IsEqualGUID(riid, &IID_IUnknown) ||
2593         IsEqualGUID(riid, &IID_IMultiLanguage))
2594     {
2595         *obj = &This->IMultiLanguage_iface;
2596     }
2597     else if (IsEqualGUID(riid, &IID_IMLangCodePages) ||
2598              IsEqualGUID(riid, &IID_IMLangFontLink))
2599     {
2600         *obj = &This->IMLangFontLink_iface;
2601     }
2602     else if (IsEqualGUID(riid, &IID_IMLangFontLink2))
2603     {
2604         *obj = &This->IMLangFontLink2_iface;
2605     }
2606     else if (IsEqualGUID(riid, &IID_IMultiLanguage2) ||
2607              IsEqualGUID(riid, &IID_IMultiLanguage3))
2608     {
2609         *obj = &This->IMultiLanguage3_iface;
2610     }
2611     else if (IsEqualGUID(riid, &IID_IMLangLineBreakConsole))
2612     {
2613         *obj = &This->IMLangLineBreakConsole_iface;
2614     }
2615     else
2616     {
2617         WARN("(%p)->(%s,%p),not found\n", This, debugstr_guid(riid), obj);
2618         *obj = NULL;
2619         return E_NOINTERFACE;
2620     }
2621 
2622     IMultiLanguage3_AddRef(iface);
2623     return S_OK;
2624 }
2625 
2626 static ULONG WINAPI fnIMultiLanguage3_AddRef( IMultiLanguage3* iface )
2627 {
2628     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2629     return InterlockedIncrement(&This->ref);
2630 }
2631 
2632 static ULONG WINAPI fnIMultiLanguage3_Release( IMultiLanguage3* iface )
2633 {
2634     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2635     ULONG ref = InterlockedDecrement(&This->ref);
2636 
2637     TRACE("(%p)->(%d)\n", This, ref);
2638     if (ref == 0)
2639     {
2640         HeapFree(GetProcessHeap(), 0, This);
2641         UnlockModule();
2642     }
2643 
2644     return ref;
2645 }
2646 
2647 static HRESULT WINAPI fnIMultiLanguage3_GetNumberOfCodePageInfo(
2648     IMultiLanguage3* iface,
2649     UINT* pcCodePage)
2650 {
2651     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2652 
2653     TRACE("%p, %p\n", This, pcCodePage);
2654 
2655     if (!pcCodePage) return E_INVALIDARG;
2656 
2657     *pcCodePage = This->total_cp;
2658     return S_OK;
2659 }
2660 
2661 static void fill_cp_info(const struct mlang_data *ml_data, UINT index, MIMECPINFO *mime_cp_info)
2662 {
2663     CHARSETINFO csi;
2664 
2665     if (TranslateCharsetInfo((DWORD*)(DWORD_PTR)ml_data->family_codepage, &csi,
2666                              TCI_SRCCODEPAGE))
2667         mime_cp_info->bGDICharset = csi.ciCharset;
2668     else
2669         mime_cp_info->bGDICharset = DEFAULT_CHARSET;
2670 
2671     mime_cp_info->dwFlags = ml_data->mime_cp_info[index].flags;
2672     mime_cp_info->uiCodePage = ml_data->mime_cp_info[index].cp;
2673     mime_cp_info->uiFamilyCodePage = ml_data->family_codepage;
2674     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].description, -1,
2675                         mime_cp_info->wszDescription, sizeof(mime_cp_info->wszDescription)/sizeof(WCHAR));
2676     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].web_charset, -1,
2677                         mime_cp_info->wszWebCharset, sizeof(mime_cp_info->wszWebCharset)/sizeof(WCHAR));
2678     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].header_charset, -1,
2679                         mime_cp_info->wszHeaderCharset, sizeof(mime_cp_info->wszHeaderCharset)/sizeof(WCHAR));
2680     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].body_charset, -1,
2681                         mime_cp_info->wszBodyCharset, sizeof(mime_cp_info->wszBodyCharset)/sizeof(WCHAR));
2682 
2683     MultiByteToWideChar(CP_ACP, 0, ml_data->fixed_font, -1,
2684         mime_cp_info->wszFixedWidthFont, sizeof(mime_cp_info->wszFixedWidthFont)/sizeof(WCHAR));
2685     MultiByteToWideChar(CP_ACP, 0, ml_data->proportional_font, -1,
2686         mime_cp_info->wszProportionalFont, sizeof(mime_cp_info->wszProportionalFont)/sizeof(WCHAR));
2687 
2688     TRACE("%08x %u %u %s %s %s %s %s %s %d\n",
2689           mime_cp_info->dwFlags, mime_cp_info->uiCodePage,
2690           mime_cp_info->uiFamilyCodePage,
2691           wine_dbgstr_w(mime_cp_info->wszDescription),
2692           wine_dbgstr_w(mime_cp_info->wszWebCharset),
2693           wine_dbgstr_w(mime_cp_info->wszHeaderCharset),
2694           wine_dbgstr_w(mime_cp_info->wszBodyCharset),
2695           wine_dbgstr_w(mime_cp_info->wszFixedWidthFont),
2696           wine_dbgstr_w(mime_cp_info->wszProportionalFont),
2697           mime_cp_info->bGDICharset);
2698 }
2699 
2700 static HRESULT WINAPI fnIMultiLanguage3_GetCodePageInfo(
2701     IMultiLanguage3* iface,
2702     UINT uiCodePage,
2703     LANGID LangId,
2704     PMIMECPINFO pCodePageInfo)
2705 {
2706     UINT i, n;
2707     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2708 
2709     TRACE("%p, %u, %04x, %p\n", This, uiCodePage, LangId, pCodePageInfo);
2710 
2711     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2712     {
2713         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2714         {
2715             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
2716             {
2717                 fill_cp_info(&mlang_data[i], n, pCodePageInfo);
2718                 return S_OK;
2719             }
2720         }
2721     }
2722 
2723     return S_FALSE;
2724 }
2725 
2726 static HRESULT WINAPI fnIMultiLanguage3_GetFamilyCodePage(
2727     IMultiLanguage3* iface,
2728     UINT uiCodePage,
2729     UINT* puiFamilyCodePage)
2730 {
2731     return GetFamilyCodePage(uiCodePage, puiFamilyCodePage);
2732 }
2733 
2734 static HRESULT WINAPI fnIMultiLanguage3_EnumCodePages(
2735     IMultiLanguage3* iface,
2736     DWORD grfFlags,
2737     LANGID LangId,
2738     IEnumCodePage** ppEnumCodePage)
2739 {
2740     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2741 
2742     TRACE("%p %08x %04x %p\n", This, grfFlags, LangId, ppEnumCodePage);
2743 
2744     return EnumCodePage_create( This, grfFlags, LangId, ppEnumCodePage );
2745 }
2746 
2747 static HRESULT WINAPI fnIMultiLanguage3_GetCharsetInfo(
2748     IMultiLanguage3* iface,
2749     BSTR Charset,
2750     PMIMECSETINFO pCharsetInfo)
2751 {
2752     UINT i, n;
2753     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2754 
2755     TRACE("%p %s %p\n", This, debugstr_w(Charset), pCharsetInfo);
2756 
2757     if (!pCharsetInfo) return E_FAIL;
2758 
2759     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2760     {
2761         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2762         {
2763             WCHAR csetW[MAX_MIMECSET_NAME];
2764 
2765             MultiByteToWideChar(CP_ACP, 0, mlang_data[i].mime_cp_info[n].web_charset, -1, csetW, MAX_MIMECSET_NAME);
2766             if (!lstrcmpiW(Charset, csetW))
2767             {
2768                 pCharsetInfo->uiCodePage = mlang_data[i].family_codepage;
2769                 pCharsetInfo->uiInternetEncoding = mlang_data[i].mime_cp_info[n].cp;
2770                 strcpyW(pCharsetInfo->wszCharset, csetW);
2771                 return S_OK;
2772             }
2773             if (mlang_data[i].mime_cp_info[n].alias && !lstrcmpiW(Charset, mlang_data[i].mime_cp_info[n].alias))
2774             {
2775                 pCharsetInfo->uiCodePage = mlang_data[i].family_codepage;
2776                 pCharsetInfo->uiInternetEncoding = mlang_data[i].mime_cp_info[n].cp;
2777                 strcpyW(pCharsetInfo->wszCharset, mlang_data[i].mime_cp_info[n].alias);
2778                 return S_OK;
2779             }
2780         }
2781     }
2782 
2783     /* FIXME:
2784      * Since we do not support charsets like iso-2022-jp and do not have
2785      * them in our database as a primary (web_charset) encoding this loop
2786      * does an attempt to 'approximate' charset name by header_charset.
2787      */
2788     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2789     {
2790         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2791         {
2792             WCHAR csetW[MAX_MIMECSET_NAME];
2793 
2794             MultiByteToWideChar(CP_ACP, 0, mlang_data[i].mime_cp_info[n].header_charset, -1, csetW, MAX_MIMECSET_NAME);
2795             if (!lstrcmpiW(Charset, csetW))
2796             {
2797                 pCharsetInfo->uiCodePage = mlang_data[i].family_codepage;
2798                 pCharsetInfo->uiInternetEncoding = mlang_data[i].mime_cp_info[n].cp;
2799                 strcpyW(pCharsetInfo->wszCharset, csetW);
2800                 return S_OK;
2801             }
2802         }
2803     }
2804 
2805     return E_FAIL;
2806 }
2807 
2808 static HRESULT WINAPI fnIMultiLanguage3_IsConvertible(
2809     IMultiLanguage3* iface,
2810     DWORD dwSrcEncoding,
2811     DWORD dwDstEncoding)
2812 {
2813     return IsConvertINetStringAvailable(dwSrcEncoding, dwDstEncoding);
2814 }
2815 
2816 static HRESULT WINAPI fnIMultiLanguage3_ConvertString(
2817     IMultiLanguage3* iface,
2818     DWORD* pdwMode,
2819     DWORD dwSrcEncoding,
2820     DWORD dwDstEncoding,
2821     BYTE* pSrcStr,
2822     UINT* pcSrcSize,
2823     BYTE* pDstStr,
2824     UINT* pcDstSize)
2825 {
2826     return ConvertINetString(pdwMode, dwSrcEncoding, dwDstEncoding,
2827         (LPCSTR)pSrcStr, (LPINT)pcSrcSize, (LPSTR)pDstStr, (LPINT)pcDstSize);
2828 }
2829 
2830 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringToUnicode(
2831     IMultiLanguage3* iface,
2832     DWORD* pdwMode,
2833     DWORD dwEncoding,
2834     CHAR* pSrcStr,
2835     UINT* pcSrcSize,
2836     WCHAR* pDstStr,
2837     UINT* pcDstSize)
2838 {
2839     return ConvertINetMultiByteToUnicode(pdwMode, dwEncoding,
2840         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
2841 }
2842 
2843 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringFromUnicode(
2844     IMultiLanguage3* iface,
2845     DWORD* pdwMode,
2846     DWORD dwEncoding,
2847     WCHAR* pSrcStr,
2848     UINT* pcSrcSize,
2849     CHAR* pDstStr,
2850     UINT* pcDstSize)
2851 {
2852     return ConvertINetUnicodeToMultiByte(pdwMode, dwEncoding,
2853         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
2854 }
2855 
2856 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringReset(
2857     IMultiLanguage3* iface)
2858 {
2859     FIXME("\n");
2860     return E_NOTIMPL;
2861 }
2862 
2863 static HRESULT WINAPI fnIMultiLanguage3_GetRfc1766FromLcid(
2864     IMultiLanguage3* iface,
2865     LCID lcid,
2866     BSTR* pbstrRfc1766)
2867 {
2868     WCHAR buf[MAX_RFC1766_NAME];
2869 
2870     TRACE("%p %04x %p\n", iface, lcid, pbstrRfc1766);
2871     if (!pbstrRfc1766)
2872         return E_INVALIDARG;
2873 
2874     if (!lcid_to_rfc1766W( lcid, buf, MAX_RFC1766_NAME ))
2875     {
2876         *pbstrRfc1766 = SysAllocString( buf );
2877         return S_OK;
2878     }
2879     return E_FAIL;
2880 }
2881 
2882 static HRESULT WINAPI fnIMultiLanguage3_GetLcidFromRfc1766(
2883     IMultiLanguage3* iface,
2884     LCID* pLocale,
2885     BSTR bstrRfc1766)
2886 {
2887     HRESULT hr;
2888     IEnumRfc1766 *rfc1766;
2889 
2890     TRACE("%p %p %s\n", iface, pLocale, debugstr_w(bstrRfc1766));
2891 
2892     if (!pLocale || !bstrRfc1766)
2893         return E_INVALIDARG;
2894 
2895     hr = IMultiLanguage3_EnumRfc1766(iface, 0, &rfc1766);
2896     if (FAILED(hr))
2897         return hr;
2898 
2899     hr = lcid_from_rfc1766(rfc1766, pLocale, bstrRfc1766);
2900 
2901     IEnumRfc1766_Release(rfc1766);
2902     return hr;
2903 }
2904 
2905 static HRESULT WINAPI fnIMultiLanguage3_EnumRfc1766(
2906     IMultiLanguage3* iface,
2907     LANGID LangId,
2908     IEnumRfc1766** ppEnumRfc1766)
2909 {
2910     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2911 
2912     TRACE("%p %p\n", This, ppEnumRfc1766);
2913 
2914     return EnumRfc1766_create(LangId, ppEnumRfc1766);
2915 }
2916 
2917 static HRESULT WINAPI fnIMultiLanguage3_GetRfc1766Info(
2918     IMultiLanguage3* iface,
2919     LCID Locale,
2920     LANGID LangId,
2921     PRFC1766INFO pRfc1766Info)
2922 {
2923     static LANGID last_lang = -1;
2924     LCTYPE type = LOCALE_SLANGUAGE;
2925 
2926     TRACE("(%p, 0x%04x, 0x%04x, %p)\n", iface, Locale, LangId, pRfc1766Info);
2927 
2928     if (!pRfc1766Info)
2929         return E_INVALIDARG;
2930 
2931     if ((PRIMARYLANGID(Locale) == LANG_ENGLISH) ||
2932         (PRIMARYLANGID(Locale) == LANG_CHINESE) ||
2933         (PRIMARYLANGID(Locale) == LANG_ARABIC)) {
2934 
2935         if (!SUBLANGID(Locale))
2936             type = LOCALE_SENGLANGUAGE; /* suppress country */
2937     }
2938     else
2939     {
2940         if (!SUBLANGID(Locale)) {
2941             TRACE("SUBLANGID missing in 0x%04x\n", Locale);
2942             return E_FAIL;
2943         }
2944     }
2945 
2946     pRfc1766Info->lcid = Locale;
2947     pRfc1766Info->wszRfc1766[0] = 0;
2948     pRfc1766Info->wszLocaleName[0] = 0;
2949 
2950     if ((PRIMARYLANGID(LangId) != LANG_ENGLISH) &&
2951         (last_lang != LangId)) {
2952         FIXME("Only English names supported (requested: 0x%04x)\n", LangId);
2953         last_lang = LangId;
2954     }
2955 
2956     if ((!lcid_to_rfc1766W(Locale, pRfc1766Info->wszRfc1766, MAX_RFC1766_NAME)) &&
2957         (GetLocaleInfoW(Locale, type, pRfc1766Info->wszLocaleName, MAX_LOCALE_NAME) > 0))
2958             return S_OK;
2959 
2960     /* Locale not supported */
2961     return E_INVALIDARG;
2962 }
2963 
2964 static HRESULT WINAPI fnIMultiLanguage3_CreateConvertCharset(
2965     IMultiLanguage3* iface,
2966     UINT src_cp,
2967     UINT dst_cp,
2968     DWORD prop,
2969     IMLangConvertCharset** convert_charset)
2970 {
2971     HRESULT hr;
2972 
2973     TRACE("(%u %u 0x%08x %p)\n", src_cp, dst_cp, prop, convert_charset);
2974 
2975     hr = MLangConvertCharset_create(NULL, (void**)convert_charset);
2976     if (FAILED(hr)) return hr;
2977 
2978     return IMLangConvertCharset_Initialize(*convert_charset, src_cp, dst_cp, prop);
2979 }
2980 
2981 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringInIStream(
2982     IMultiLanguage3* iface,
2983     DWORD* pdwMode,
2984     DWORD dwFlag,
2985     WCHAR* lpFallBack,
2986     DWORD dwSrcEncoding,
2987     DWORD dwDstEncoding,
2988     IStream* pstmIn,
2989     IStream* pstmOut)
2990 {
2991     char *src, *dst = NULL;
2992     INT srclen, dstlen;
2993     STATSTG stat;
2994     HRESULT hr;
2995 
2996     TRACE("%p %0x8 %s %u %u %p %p\n",
2997           pdwMode, dwFlag, debugstr_w(lpFallBack), dwSrcEncoding, dwDstEncoding, pstmIn, pstmOut);
2998 
2999     FIXME("dwFlag and lpFallBack not handled\n");
3000 
3001     hr = IStream_Stat(pstmIn, &stat, STATFLAG_NONAME);
3002     if (FAILED(hr)) return hr;
3003 
3004     if (stat.cbSize.QuadPart > MAXLONG) return E_INVALIDARG;
3005     if (!(src = HeapAlloc(GetProcessHeap(), 0, stat.cbSize.QuadPart))) return E_OUTOFMEMORY;
3006 
3007     hr = IStream_Read(pstmIn, src, stat.cbSize.QuadPart, (ULONG *)&srclen);
3008     if (FAILED(hr)) goto exit;
3009 
3010     hr = ConvertINetString(pdwMode, dwSrcEncoding, dwDstEncoding, src, &srclen, NULL, &dstlen);
3011     if (FAILED(hr)) goto exit;
3012 
3013     if (!(dst = HeapAlloc(GetProcessHeap(), 0, dstlen)))
3014     {
3015         hr = E_OUTOFMEMORY;
3016         goto exit;
3017     }
3018     hr = ConvertINetString(pdwMode, dwSrcEncoding, dwDstEncoding, src, &srclen, dst, &dstlen);
3019     if (FAILED(hr)) goto exit;
3020 
3021     hr = IStream_Write(pstmOut, dst, dstlen, NULL);
3022 
3023 exit:
3024     HeapFree(GetProcessHeap(), 0, src);
3025     HeapFree(GetProcessHeap(), 0, dst);
3026     return hr;
3027 }
3028 
3029 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringToUnicodeEx(
3030     IMultiLanguage3* iface,
3031     DWORD* pdwMode,
3032     DWORD dwEncoding,
3033     CHAR* pSrcStr,
3034     UINT* pcSrcSize,
3035     WCHAR* pDstStr,
3036     UINT* pcDstSize,
3037     DWORD dwFlag,
3038     WCHAR* lpFallBack)
3039 {
3040     if (dwFlag || lpFallBack)
3041         FIXME("Ignoring dwFlag (0x%x/%d) and lpFallBack (%p)\n",
3042                 dwFlag, dwFlag, lpFallBack);
3043 
3044     return ConvertINetMultiByteToUnicode(pdwMode, dwEncoding,
3045         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
3046 }
3047 
3048 /*****************************************************************************
3049  * MultiLanguage2::ConvertStringToUnicodeEx
3050  *
3051  * Translates the multibyte string from the specified code page to Unicode.
3052  *
3053  * PARAMS
3054  *   see ConvertStringToUnicode
3055  *   dwFlag
3056  *   lpFallBack if dwFlag contains MLCONVCHARF_USEDEFCHAR, lpFallBack string used
3057  *              instead unconvertible characters.
3058  *
3059  * RETURNS
3060  *   S_OK     Success.
3061  *   S_FALSE  The conversion is not supported.
3062  *   E_FAIL   Some error has occurred.
3063  *
3064  * TODO: handle dwFlag and lpFallBack
3065 */
3066 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringFromUnicodeEx(
3067     IMultiLanguage3* This,
3068     DWORD* pdwMode,
3069     DWORD dwEncoding,
3070     WCHAR* pSrcStr,
3071     UINT* pcSrcSize,
3072     CHAR* pDstStr,
3073     UINT* pcDstSize,
3074     DWORD dwFlag,
3075     WCHAR* lpFallBack)
3076 {
3077     FIXME("\n");
3078     return ConvertINetUnicodeToMultiByte(pdwMode, dwEncoding,
3079         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
3080 }
3081 
3082 static HRESULT WINAPI fnIMultiLanguage3_DetectCodepageInIStream(
3083     IMultiLanguage3* iface,
3084     DWORD dwFlag,
3085     DWORD dwPrefWinCodePage,
3086     IStream* pstmIn,
3087     DetectEncodingInfo* lpEncoding,
3088     INT* pnScores)
3089 {
3090     FIXME("\n");
3091     return E_NOTIMPL;
3092 }
3093 
3094 static HRESULT WINAPI fnIMultiLanguage3_DetectInputCodepage(
3095     IMultiLanguage3* iface,
3096     DWORD dwFlag,
3097     DWORD dwPrefWinCodePage,
3098     CHAR* pSrcStr,
3099     INT* pcSrcSize,
3100     DetectEncodingInfo* lpEncoding,
3101     INT* pnScores)
3102 {
3103     FIXME("\n");
3104     return E_NOTIMPL;
3105 }
3106 
3107 static HRESULT WINAPI fnIMultiLanguage3_ValidateCodePage(
3108     IMultiLanguage3* iface,
3109     UINT uiCodePage,
3110     HWND hwnd)
3111 {
3112     return IMultiLanguage3_ValidateCodePageEx(iface,uiCodePage,hwnd,0);
3113 }
3114 
3115 static HRESULT WINAPI fnIMultiLanguage3_GetCodePageDescription(
3116     IMultiLanguage3* iface,
3117     UINT uiCodePage,
3118     LCID lcid,
3119     LPWSTR lpWideCharStr,
3120     int cchWideChar)
3121 {
3122     /* Find first instance */
3123     unsigned int i,n;
3124 
3125     TRACE ("%u, %04x, %p, %d\n", uiCodePage, lcid, lpWideCharStr, cchWideChar);
3126     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3127     {
3128         for (n = 0; n < mlang_data[i].number_of_cp; n++)
3129         {
3130             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
3131             {
3132                 MultiByteToWideChar(CP_ACP, 0,
3133                                     mlang_data[i].mime_cp_info[n].description,
3134                                     -1, lpWideCharStr, cchWideChar);
3135                 return S_OK;
3136             }
3137         }
3138     }
3139 
3140     return S_FALSE;
3141 }
3142 
3143 static HRESULT WINAPI fnIMultiLanguage3_IsCodePageInstallable(
3144     IMultiLanguage3* iface,
3145     UINT uiCodePage)
3146 {
3147     TRACE("%u\n", uiCodePage);
3148 
3149     /* FIXME: the installable set is usually larger than the set of valid codepages */
3150     return IMultiLanguage3_ValidateCodePageEx(iface, uiCodePage, NULL, CPIOD_PEEK);
3151 }
3152 
3153 static HRESULT WINAPI fnIMultiLanguage3_SetMimeDBSource(
3154     IMultiLanguage3* iface,
3155     MIMECONTF dwSource)
3156 {
3157     FIXME("0x%08x\n", dwSource);
3158     return S_OK;
3159 }
3160 
3161 static HRESULT WINAPI fnIMultiLanguage3_GetNumberOfScripts(
3162     IMultiLanguage3* iface,
3163     UINT* pnScripts)
3164 {
3165     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3166 
3167     TRACE("%p %p\n", This, pnScripts);
3168 
3169     if (!pnScripts) return S_FALSE;
3170 
3171     *pnScripts = This->total_scripts;
3172     return S_OK;
3173 }
3174 
3175 static HRESULT WINAPI fnIMultiLanguage3_EnumScripts(
3176     IMultiLanguage3* iface,
3177     DWORD dwFlags,
3178     LANGID LangId,
3179     IEnumScript** ppEnumScript)
3180 {
3181     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3182 
3183     TRACE("%p %08x %04x %p\n", This, dwFlags, LangId, ppEnumScript);
3184 
3185     return EnumScript_create( This, dwFlags, LangId, ppEnumScript );
3186 }
3187 
3188 static HRESULT WINAPI fnIMultiLanguage3_ValidateCodePageEx(
3189     IMultiLanguage3* iface,
3190     UINT uiCodePage,
3191     HWND hwnd,
3192     DWORD dwfIODControl)
3193 {
3194     unsigned int i;
3195     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3196 
3197     TRACE("%p %u %p %08x\n", This, uiCodePage, hwnd, dwfIODControl);
3198 
3199     /* quick check for kernel32 supported code pages */
3200     if (IsValidCodePage(uiCodePage))
3201         return S_OK;
3202 
3203     /* check for mlang supported code pages */
3204     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3205     {
3206         UINT n;
3207         for (n = 0; n < mlang_data[i].number_of_cp; n++)
3208         {
3209             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
3210                 return S_OK;
3211         }
3212     }
3213 
3214     if (dwfIODControl != CPIOD_PEEK)
3215         FIXME("Request to install codepage language pack not handled\n");
3216 
3217     return S_FALSE;
3218 }
3219 
3220 static HRESULT WINAPI fnIMultiLanguage3_DetectOutboundCodePage(
3221     IMultiLanguage3 *iface,
3222     DWORD dwFlags,
3223     LPCWSTR lpWideCharStr,
3224     UINT cchWideChar,
3225     UINT *puiPreferredCodePages,
3226     UINT nPreferredCodePages,
3227     UINT *puiDetectedCodePages,
3228     UINT *pnDetectedCodePages,
3229     WCHAR *lpSpecialChar)
3230 {
3231     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3232 
3233     FIXME("(%p)->(%08x %s %p %u %p %p(%u) %s)\n", This, dwFlags, debugstr_w(lpWideCharStr),
3234           puiPreferredCodePages, nPreferredCodePages, puiDetectedCodePages,
3235           pnDetectedCodePages, pnDetectedCodePages ? *pnDetectedCodePages : 0,
3236           debugstr_w(lpSpecialChar));
3237 
3238     if (!puiDetectedCodePages || !pnDetectedCodePages || !*pnDetectedCodePages)
3239         return E_INVALIDARG;
3240 
3241     puiDetectedCodePages[0] = CP_UTF8;
3242     *pnDetectedCodePages = 1;
3243     return S_OK;
3244 }
3245 
3246 static HRESULT WINAPI fnIMultiLanguage3_DetectOutboundCodePageInIStream(
3247     IMultiLanguage3 *iface,
3248     DWORD dwFlags,
3249     IStream *pStrIn,
3250     UINT *puiPreferredCodePages,
3251     UINT nPreferredCodePages,
3252     UINT *puiDetectedCodePages,
3253     UINT *pnDetectedCodePages,
3254     WCHAR *lpSpecialChar)
3255 {
3256     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3257 
3258     FIXME("(%p)->(%08x %p %p %u %p %p(%u) %s)\n", This, dwFlags, pStrIn,
3259           puiPreferredCodePages, nPreferredCodePages, puiDetectedCodePages,
3260           pnDetectedCodePages, pnDetectedCodePages ? *pnDetectedCodePages : 0,
3261           debugstr_w(lpSpecialChar));
3262 
3263     if (!puiDetectedCodePages || !pnDetectedCodePages || !*pnDetectedCodePages)
3264         return E_INVALIDARG;
3265 
3266     puiDetectedCodePages[0] = CP_UTF8;
3267     *pnDetectedCodePages = 1;
3268     return S_OK;
3269 }
3270 
3271 static const IMultiLanguage3Vtbl IMultiLanguage3_vtbl =
3272 {
3273     fnIMultiLanguage3_QueryInterface,
3274     fnIMultiLanguage3_AddRef,
3275     fnIMultiLanguage3_Release,
3276     fnIMultiLanguage3_GetNumberOfCodePageInfo,
3277     fnIMultiLanguage3_GetCodePageInfo,
3278     fnIMultiLanguage3_GetFamilyCodePage,
3279     fnIMultiLanguage3_EnumCodePages,
3280     fnIMultiLanguage3_GetCharsetInfo,
3281     fnIMultiLanguage3_IsConvertible,
3282     fnIMultiLanguage3_ConvertString,
3283     fnIMultiLanguage3_ConvertStringToUnicode,
3284     fnIMultiLanguage3_ConvertStringFromUnicode,
3285     fnIMultiLanguage3_ConvertStringReset,
3286     fnIMultiLanguage3_GetRfc1766FromLcid,
3287     fnIMultiLanguage3_GetLcidFromRfc1766,
3288     fnIMultiLanguage3_EnumRfc1766,
3289     fnIMultiLanguage3_GetRfc1766Info,
3290     fnIMultiLanguage3_CreateConvertCharset,
3291     fnIMultiLanguage3_ConvertStringInIStream,
3292     fnIMultiLanguage3_ConvertStringToUnicodeEx,
3293     fnIMultiLanguage3_ConvertStringFromUnicodeEx,
3294     fnIMultiLanguage3_DetectCodepageInIStream,
3295     fnIMultiLanguage3_DetectInputCodepage,
3296     fnIMultiLanguage3_ValidateCodePage,
3297     fnIMultiLanguage3_GetCodePageDescription,
3298     fnIMultiLanguage3_IsCodePageInstallable,
3299     fnIMultiLanguage3_SetMimeDBSource,
3300     fnIMultiLanguage3_GetNumberOfScripts,
3301     fnIMultiLanguage3_EnumScripts,
3302     fnIMultiLanguage3_ValidateCodePageEx,
3303     fnIMultiLanguage3_DetectOutboundCodePage,
3304     fnIMultiLanguage3_DetectOutboundCodePageInIStream
3305 };
3306 
3307 /******************************************************************************/
3308 
3309 static inline MLang_impl *impl_from_IMLangFontLink2( IMLangFontLink2 *iface )
3310 {
3311     return CONTAINING_RECORD( iface, MLang_impl, IMLangFontLink2_iface );
3312 }
3313 
3314 static HRESULT WINAPI fnIMLangFontLink2_QueryInterface(
3315     IMLangFontLink2 * iface,
3316     REFIID riid,
3317     void** ppvObject)
3318 {
3319     MLang_impl *This = impl_from_IMLangFontLink2( iface );
3320     return IMultiLanguage3_QueryInterface( &This->IMultiLanguage3_iface, riid, ppvObject );
3321 }
3322 
3323 static ULONG WINAPI fnIMLangFontLink2_AddRef( IMLangFontLink2* iface )
3324 {
3325     MLang_impl *This = impl_from_IMLangFontLink2( iface );
3326     return IMultiLanguage3_AddRef( &This->IMultiLanguage3_iface );
3327 }
3328 
3329 static ULONG WINAPI fnIMLangFontLink2_Release( IMLangFontLink2* iface )
3330 {
3331     MLang_impl *This = impl_from_IMLangFontLink2( iface );
3332     return IMultiLanguage3_Release( &This->IMultiLanguage3_iface );
3333 }
3334 
3335 static HRESULT WINAPI fnIMLangFontLink2_GetCharCodePages( IMLangFontLink2* iface,
3336         WCHAR ch_src, DWORD *ret_codepages)
3337 {
3338     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3339     unsigned int i;
3340 
3341     TRACE("(%p)->(%s %p)\n", This, debugstr_wn(&ch_src, 1), ret_codepages);
3342 
3343     *ret_codepages = 0;
3344 
3345     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3346     {
3347         BOOL used_dc;
3348         CHAR buf;
3349 
3350         WideCharToMultiByte(mlang_data[i].family_codepage, WC_NO_BEST_FIT_CHARS,
3351             &ch_src, 1, &buf, 1, NULL, &used_dc);
3352 
3353         /* If default char is not used, current codepage include the given symbol */
3354         if (!used_dc)
3355         {
3356             DWORD codepages;
3357 
3358             IMLangFontLink2_CodePageToCodePages(iface,
3359                 mlang_data[i].family_codepage, &codepages);
3360             *ret_codepages |= codepages;
3361         }
3362     }
3363     return S_OK;
3364 }
3365 
3366 static HRESULT WINAPI fnIMLangFontLink2_GetStrCodePages( IMLangFontLink2* iface,
3367         const WCHAR *src, LONG src_len, DWORD priority_cp,
3368         DWORD *codepages, LONG *ret_len)
3369 {
3370     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3371     LONG i;
3372     DWORD cps = 0;
3373 
3374     TRACE("(%p)->(%s:%d %x %p %p)\n", This, debugstr_wn(src, src_len), src_len, priority_cp,
3375         codepages, ret_len);
3376 
3377     if (codepages) *codepages = 0;
3378     if (ret_len) *ret_len = 0;
3379 
3380     if (!src || !src_len || src_len < 0)
3381         return E_INVALIDARG;
3382 
3383     for (i = 0; i < src_len; i++)
3384     {
3385         DWORD cp;
3386         HRESULT ret;
3387 
3388         ret = IMLangFontLink2_GetCharCodePages(iface, src[i], &cp);
3389         if (ret != S_OK) return E_FAIL;
3390 
3391         if (!cps) cps = cp;
3392         else cps &= cp;
3393 
3394         /* FIXME: not tested */
3395         if (priority_cp & cps) break;
3396     }
3397 
3398     if (codepages) *codepages = cps;
3399     if (ret_len) *ret_len = min( i + 1, src_len );
3400     return S_OK;
3401 }
3402 
3403 static HRESULT WINAPI fnIMLangFontLink2_CodePageToCodePages(IMLangFontLink2* iface,
3404         UINT codepage,
3405         DWORD *codepages)
3406 {
3407     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3408     CHARSETINFO cs;
3409     BOOL rc;
3410 
3411     TRACE("(%p)->(%u %p)\n", This, codepage, codepages);
3412 
3413     rc = TranslateCharsetInfo((DWORD*)(DWORD_PTR)codepage, &cs, TCI_SRCCODEPAGE);
3414     if (rc)
3415     {
3416         *codepages = cs.fs.fsCsb[0];
3417         TRACE("resulting codepages 0x%x\n", *codepages);
3418         return S_OK;
3419     }
3420 
3421     TRACE("codepage not found\n");
3422     *codepages = 0;
3423     return E_FAIL;
3424 }
3425 
3426 static HRESULT WINAPI fnIMLangFontLink2_CodePagesToCodePage(IMLangFontLink2* iface,
3427         DWORD codepages, UINT def_codepage, UINT *codepage)
3428 {
3429     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3430     DWORD mask = 0;
3431     CHARSETINFO cs;
3432     BOOL rc;
3433     UINT i;
3434 
3435     TRACE("(%p)->(0x%x %u %p)\n", This, codepages, def_codepage, codepage);
3436 
3437     *codepage = 0;
3438 
3439     rc = TranslateCharsetInfo((DWORD*)(DWORD_PTR)def_codepage, &cs, TCI_SRCCODEPAGE);
3440     if (rc && (codepages & cs.fs.fsCsb[0]))
3441     {
3442         TRACE("Found Default Codepage\n");
3443         *codepage = def_codepage;
3444         return S_OK;
3445     }
3446 
3447     for (i = 0; i < 32; i++)
3448     {
3449         mask = 1 << i;
3450         if (codepages & mask)
3451         {
3452             DWORD Csb[2];
3453             Csb[0] = mask;
3454             Csb[1] = 0x0;
3455             rc = TranslateCharsetInfo(Csb, &cs, TCI_SRCFONTSIG);
3456             if (!rc)
3457                 continue;
3458 
3459             TRACE("Falling back to least significant found CodePage %u\n",
3460                     cs.ciACP);
3461             *codepage = cs.ciACP;
3462             return S_OK;
3463         }
3464     }
3465 
3466     TRACE("no codepage found\n");
3467     return E_FAIL;
3468 }
3469 
3470 static HRESULT WINAPI fnIMLangFontLink2_GetFontCodePages(IMLangFontLink2 *iface,
3471         HDC hdc, HFONT hfont, DWORD *codepages)
3472 {
3473     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3474     FONTSIGNATURE fontsig;
3475     HFONT old_font;
3476 
3477     TRACE("(%p)->(%p %p %p)\n", This, hdc, hfont, codepages);
3478 
3479     old_font = SelectObject(hdc, hfont);
3480     GetTextCharsetInfo(hdc, &fontsig, 0);
3481     SelectObject(hdc, old_font);
3482 
3483     *codepages = fontsig.fsCsb[0];
3484     TRACE("ret 0x%x\n", fontsig.fsCsb[0]);
3485 
3486     return S_OK;
3487 }
3488 
3489 static HRESULT WINAPI fnIMLangFontLink2_ReleaseFont(IMLangFontLink2* This,
3490         HFONT hFont)
3491 {
3492     TRACE("(%p)->%p\n",This, hFont);
3493 
3494     return release_font(hFont);
3495 }
3496 
3497 static HRESULT WINAPI fnIMLangFontLink2_ResetFontMapping(IMLangFontLink2* This)
3498 {
3499     TRACE("(%p)\n",This);
3500 
3501     return clear_font_cache();
3502 }
3503 
3504 static HRESULT WINAPI fnIMLangFontLink2_MapFont(IMLangFontLink2* This,
3505         HDC hDC, DWORD dwCodePages, WCHAR chSrc, HFONT *pFont)
3506 {
3507     HFONT old_font;
3508 
3509     TRACE("(%p)->%p %08x %04x %p\n",This, hDC, dwCodePages, chSrc, pFont);
3510 
3511     if (!hDC) return E_FAIL;
3512 
3513     if (dwCodePages != 0)
3514     {
3515         old_font = GetCurrentObject(hDC, OBJ_FONT);
3516         return map_font(hDC, dwCodePages, old_font, pFont);
3517     }
3518     else
3519     {
3520         if (pFont == NULL) return E_INVALIDARG;
3521         FIXME("the situation where dwCodepages is set to zero is not implemented\n");
3522         return E_FAIL;
3523     }
3524 }
3525 
3526 static HRESULT WINAPI fnIMLangFontLink2_GetFontUnicodeRanges(IMLangFontLink2* This,
3527         HDC hDC, UINT *puiRanges, UNICODERANGE *pUranges)
3528 {
3529     DWORD size;
3530     GLYPHSET *gs;
3531 
3532     TRACE("(%p)->%p %p %p\n", This, hDC, puiRanges, pUranges);
3533 
3534     if (!puiRanges) return E_INVALIDARG;
3535     if (!(size = GetFontUnicodeRanges(hDC, NULL))) return E_FAIL;
3536     if (!(gs = HeapAlloc(GetProcessHeap(), 0, size))) return E_OUTOFMEMORY;
3537 
3538     GetFontUnicodeRanges(hDC, gs);
3539     *puiRanges = gs->cRanges;
3540     if (pUranges)
3541     {
3542         UINT i;
3543         for (i = 0; i < gs->cRanges; i++)
3544         {
3545             if (i >= *puiRanges) break;
3546             pUranges[i].wcFrom = gs->ranges[i].wcLow;
3547             pUranges[i].wcTo   = gs->ranges[i].wcLow + gs->ranges[i].cGlyphs;
3548         }
3549         *puiRanges = i;
3550     }
3551     HeapFree(GetProcessHeap(), 0, gs);
3552     return S_OK;
3553 }
3554 
3555 static HRESULT WINAPI fnIMLangFontLink2_GetScriptFontInfo(IMLangFontLink2* This,
3556         SCRIPT_ID sid, DWORD dwFlags, UINT *puiFonts,
3557         SCRIPTFONTINFO *pScriptFont)
3558 {
3559     UINT i, j;
3560 
3561     TRACE("(%p)->%u %x %p %p\n", This, sid, dwFlags, puiFonts, pScriptFont);
3562 
3563     if (!dwFlags) dwFlags = SCRIPTCONTF_PROPORTIONAL_FONT;
3564 
3565     for (i = 0, j = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3566     {
3567         if (sid == mlang_data[i].sid)
3568         {
3569             if (pScriptFont)
3570             {
3571                 if (j >= *puiFonts) break;
3572 
3573                 pScriptFont[j].scripts = 1 << mlang_data[i].sid;
3574                 if (dwFlags == SCRIPTCONTF_FIXED_FONT)
3575                 {
3576                     MultiByteToWideChar(CP_ACP, 0, mlang_data[i].fixed_font, -1,
3577                         pScriptFont[j].wszFont, MAX_MIMEFACE_NAME);
3578                 }
3579                 else if (dwFlags == SCRIPTCONTF_PROPORTIONAL_FONT)
3580                 {
3581                     MultiByteToWideChar(CP_ACP, 0, mlang_data[i].proportional_font, -1,
3582                         pScriptFont[j].wszFont, MAX_MIMEFACE_NAME);
3583                 }
3584             }
3585             j++;
3586         }
3587     }
3588     *puiFonts = j;
3589     return S_OK;
3590 }
3591 
3592 static HRESULT WINAPI fnIMLangFontLink2_CodePageToScriptID(IMLangFontLink2* This,
3593         UINT uiCodePage, SCRIPT_ID *pSid)
3594 {
3595     UINT i;
3596 
3597     TRACE("(%p)->%i %p\n", This, uiCodePage, pSid);
3598 
3599     if (uiCodePage == CP_UNICODE) return E_FAIL;
3600 
3601     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3602     {
3603         if (uiCodePage == mlang_data[i].family_codepage)
3604         {
3605             if (pSid) *pSid = mlang_data[i].sid;
3606             return S_OK;
3607         }
3608     }
3609     return E_FAIL;
3610 }
3611 
3612 static const IMLangFontLink2Vtbl IMLangFontLink2_vtbl =
3613 {
3614     fnIMLangFontLink2_QueryInterface,
3615     fnIMLangFontLink2_AddRef,
3616     fnIMLangFontLink2_Release,
3617     fnIMLangFontLink2_GetCharCodePages,
3618     fnIMLangFontLink2_GetStrCodePages,
3619     fnIMLangFontLink2_CodePageToCodePages,
3620     fnIMLangFontLink2_CodePagesToCodePage,
3621     fnIMLangFontLink2_GetFontCodePages,
3622     fnIMLangFontLink2_ReleaseFont,
3623     fnIMLangFontLink2_ResetFontMapping,
3624     fnIMLangFontLink2_MapFont,
3625     fnIMLangFontLink2_GetFontUnicodeRanges,
3626     fnIMLangFontLink2_GetScriptFontInfo,
3627     fnIMLangFontLink2_CodePageToScriptID
3628 };
3629 
3630 /******************************************************************************/
3631 
3632 static inline MLang_impl *impl_from_IMLangLineBreakConsole( IMLangLineBreakConsole *iface )
3633 {
3634     return CONTAINING_RECORD( iface, MLang_impl, IMLangLineBreakConsole_iface );
3635 }
3636 
3637 static HRESULT WINAPI fnIMLangLineBreakConsole_QueryInterface(
3638     IMLangLineBreakConsole* iface,
3639     REFIID riid,
3640     void** ppvObject)
3641 {
3642     MLang_impl *This = impl_from_IMLangLineBreakConsole( iface );
3643     return IMultiLanguage3_QueryInterface( &This->IMultiLanguage3_iface, riid, ppvObject );
3644 }
3645 
3646 static ULONG WINAPI fnIMLangLineBreakConsole_AddRef(
3647     IMLangLineBreakConsole* iface )
3648 {
3649     MLang_impl *This = impl_from_IMLangLineBreakConsole( iface );
3650     return IMultiLanguage3_AddRef( &This->IMultiLanguage3_iface );
3651 }
3652 
3653 static ULONG WINAPI fnIMLangLineBreakConsole_Release(
3654     IMLangLineBreakConsole* iface )
3655 {
3656     MLang_impl *This = impl_from_IMLangLineBreakConsole( iface );
3657     return IMultiLanguage3_Release( &This->IMultiLanguage3_iface );
3658 }
3659 
3660 static HRESULT WINAPI fnIMLangLineBreakConsole_BreakLineML(
3661     IMLangLineBreakConsole* iface,
3662     IMLangString* pSrcMLStr,
3663     LONG lSrcPos,
3664     LONG lSrcLen,
3665     LONG cMinColumns,
3666     LONG cMaxColumns,
3667     LONG* plLineLen,
3668     LONG* plSkipLen)
3669 {
3670     FIXME("(%p)->%p %i %i %i %i %p %p\n", iface, pSrcMLStr, lSrcPos, lSrcLen, cMinColumns, cMaxColumns, plLineLen, plSkipLen);
3671     return E_NOTIMPL;
3672 }
3673 
3674 static HRESULT WINAPI fnIMLangLineBreakConsole_BreakLineW(
3675     IMLangLineBreakConsole* iface,
3676     LCID locale,
3677     const WCHAR* pszSrc,
3678     LONG cchSrc,
3679     LONG cMaxColumns,
3680     LONG* pcchLine,
3681     LONG* pcchSkip )
3682 {
3683     FIXME("(%p)->%i %s %i %i %p %p\n", iface, locale, debugstr_wn(pszSrc,cchSrc), cchSrc, cMaxColumns, pcchLine, pcchSkip);
3684 
3685     *pcchLine = cchSrc;
3686     *pcchSkip = 0;
3687     return S_OK;
3688 }
3689 
3690 static HRESULT WINAPI fnIMLangLineBreakConsole_BreakLineA(
3691     IMLangLineBreakConsole* iface,
3692     LCID locale,
3693     UINT uCodePage,
3694     const CHAR* pszSrc,
3695     LONG cchSrc,
3696     LONG cMaxColumns,
3697     LONG* pcchLine,
3698     LONG* pcchSkip)
3699 {
3700     LONG i, line = cchSrc, skip = 0;
3701 
3702     FIXME("(%p)->%i %i %s %i %i %p %p\n", iface, locale, uCodePage, debugstr_an(pszSrc,cchSrc), cchSrc, cMaxColumns, pcchLine, pcchSkip);
3703 
3704     if (uCodePage == CP_USASCII && cchSrc > cMaxColumns)
3705     {
3706         for (line = cMaxColumns, i = cMaxColumns - 1; i >= 0; i--)
3707         {
3708             if (pszSrc[i] == ' ')
3709             {
3710                 while (i >= 0 && pszSrc[i] == ' ')
3711                 {
3712                     i--;
3713                     line--;
3714                     skip++;
3715                 }
3716                 break;
3717             }
3718         }
3719     }
3720     *pcchLine = line;
3721     *pcchSkip = skip;
3722     return S_OK;
3723 }
3724 
3725 static const IMLangLineBreakConsoleVtbl IMLangLineBreakConsole_vtbl =
3726 {
3727     fnIMLangLineBreakConsole_QueryInterface,
3728     fnIMLangLineBreakConsole_AddRef,
3729     fnIMLangLineBreakConsole_Release,
3730     fnIMLangLineBreakConsole_BreakLineML,
3731     fnIMLangLineBreakConsole_BreakLineW,
3732     fnIMLangLineBreakConsole_BreakLineA
3733 };
3734 
3735 struct convert_charset {
3736     IMLangConvertCharset IMLangConvertCharset_iface;
3737     LONG ref;
3738 
3739     UINT src_cp;
3740     UINT dst_cp;
3741 };
3742 
3743 static inline struct convert_charset *impl_from_IMLangConvertCharset(IMLangConvertCharset *iface)
3744 {
3745     return CONTAINING_RECORD(iface, struct convert_charset, IMLangConvertCharset_iface);
3746 }
3747 
3748 static HRESULT WINAPI MLangConvertCharset_QueryInterface(IMLangConvertCharset *iface, REFIID riid, void **obj)
3749 {
3750     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3751 
3752     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
3753 
3754     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMLangConvertCharset))
3755     {
3756         *obj = &This->IMLangConvertCharset_iface;
3757         IMLangConvertCharset_AddRef(iface);
3758         return S_OK;
3759     }
3760 
3761     *obj = NULL;
3762     return E_NOINTERFACE;
3763 }
3764 
3765 static ULONG WINAPI MLangConvertCharset_AddRef(IMLangConvertCharset *iface)
3766 {
3767     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3768     ULONG ref = InterlockedIncrement(&This->ref);
3769     TRACE("(%p)->(%u)\n", This, ref);
3770     return ref;
3771 }
3772 
3773 static ULONG WINAPI MLangConvertCharset_Release(IMLangConvertCharset *iface)
3774 {
3775     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3776     ULONG ref = InterlockedDecrement(&This->ref);
3777 
3778     TRACE("(%p)->(%u)\n", This, ref);
3779     if (!ref)
3780     {
3781         HeapFree(GetProcessHeap(), 0, This);
3782         UnlockModule();
3783     }
3784 
3785     return ref;
3786 }
3787 
3788 static HRESULT WINAPI MLangConvertCharset_Initialize(IMLangConvertCharset *iface,
3789     UINT src_cp, UINT dst_cp, DWORD prop)
3790 {
3791     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3792 
3793     TRACE("(%p)->(%u %u 0x%08x)\n", This, src_cp, dst_cp, prop);
3794 
3795     prop &= ~MLCONVCHARF_USEDEFCHAR;
3796     if (prop)
3797         FIXME("property 0x%08x not supported\n", prop);
3798 
3799     This->src_cp = src_cp;
3800     This->dst_cp = dst_cp;
3801 
3802     return S_OK;
3803 }
3804 
3805 static HRESULT WINAPI MLangConvertCharset_GetSourceCodePage(IMLangConvertCharset *iface, UINT *src_cp)
3806 {
3807     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3808 
3809     TRACE("(%p)->(%p)\n", This, src_cp);
3810 
3811     if (!src_cp) return E_INVALIDARG;
3812     *src_cp = This->src_cp;
3813     return S_OK;
3814 }
3815 
3816 static HRESULT WINAPI MLangConvertCharset_GetDestinationCodePage(IMLangConvertCharset *iface, UINT *dst_cp)
3817 {
3818     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3819 
3820     TRACE("(%p)->(%p)\n", This, dst_cp);
3821 
3822     if (!dst_cp) return E_INVALIDARG;
3823     *dst_cp = This->dst_cp;
3824     return S_OK;
3825 }
3826 
3827 static HRESULT WINAPI MLangConvertCharset_GetProperty(IMLangConvertCharset *iface, DWORD *prop)
3828 {
3829     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3830     FIXME("(%p)->(%p): stub\n", This, prop);
3831     return E_NOTIMPL;
3832 }
3833 
3834 static HRESULT WINAPI MLangConvertCharset_DoConversion(IMLangConvertCharset *iface, BYTE *src,
3835     UINT *src_size, BYTE *dest, UINT *dest_size)
3836 {
3837     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3838     FIXME("(%p)->(%p %p %p %p): stub\n", This, src, src_size, dest, dest_size);
3839     return E_NOTIMPL;
3840 }
3841 
3842 static HRESULT WINAPI MLangConvertCharset_DoConversionToUnicode(IMLangConvertCharset *iface, CHAR *src,
3843     UINT *src_size, WCHAR *dest, UINT *dest_size)
3844 {
3845     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3846     TRACE("(%p)->(%p %p %p %p)\n", This, src, src_size, dest, dest_size);
3847     return ConvertINetMultiByteToUnicode(NULL, This->src_cp, src, (INT*)src_size, dest, (INT*)dest_size);
3848 }
3849 
3850 static HRESULT WINAPI MLangConvertCharset_DoConversionFromUnicode(IMLangConvertCharset *iface,
3851     WCHAR *src, UINT *src_size, CHAR *dest, UINT *dest_size)
3852 {
3853     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3854     TRACE("(%p)->(%p %p %p %p)\n", This, src, src_size, dest, dest_size);
3855     return ConvertINetUnicodeToMultiByte(NULL, This->dst_cp, src, (INT*)src_size, dest, (INT*)dest_size);
3856 }
3857 
3858 static const IMLangConvertCharsetVtbl MLangConvertCharsetVtbl =
3859 {
3860     MLangConvertCharset_QueryInterface,
3861     MLangConvertCharset_AddRef,
3862     MLangConvertCharset_Release,
3863     MLangConvertCharset_Initialize,
3864     MLangConvertCharset_GetSourceCodePage,
3865     MLangConvertCharset_GetDestinationCodePage,
3866     MLangConvertCharset_GetProperty,
3867     MLangConvertCharset_DoConversion,
3868     MLangConvertCharset_DoConversionToUnicode,
3869     MLangConvertCharset_DoConversionFromUnicode
3870 };
3871 
3872 static HRESULT MultiLanguage_create(IUnknown *pUnkOuter, LPVOID *ppObj)
3873 {
3874     MLang_impl *mlang;
3875     UINT i;
3876 
3877     TRACE("Creating MultiLanguage object\n");
3878 
3879     if( pUnkOuter )
3880         return CLASS_E_NOAGGREGATION;
3881 
3882     mlang = HeapAlloc( GetProcessHeap(), 0, sizeof (MLang_impl) );
3883     mlang->IMLangFontLink_iface.lpVtbl = &IMLangFontLink_vtbl;
3884     mlang->IMultiLanguage_iface.lpVtbl = &IMultiLanguage_vtbl;
3885     mlang->IMultiLanguage3_iface.lpVtbl = &IMultiLanguage3_vtbl;
3886     mlang->IMLangFontLink2_iface.lpVtbl = &IMLangFontLink2_vtbl;
3887     mlang->IMLangLineBreakConsole_iface.lpVtbl = &IMLangLineBreakConsole_vtbl;
3888 
3889     mlang->total_cp = 0;
3890     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3891         mlang->total_cp += mlang_data[i].number_of_cp;
3892 
3893     /* do not enumerate unicode flavours */
3894     mlang->total_scripts = sizeof(mlang_data)/sizeof(mlang_data[0]) - 1;
3895 
3896     mlang->ref = 1;
3897     *ppObj = &mlang->IMultiLanguage_iface;
3898     TRACE("returning %p\n", mlang);
3899 
3900     LockModule();
3901 
3902     return S_OK;
3903 }
3904 
3905 static HRESULT MLangConvertCharset_create(IUnknown *outer, void **obj)
3906 {
3907     struct convert_charset *convert;
3908 
3909     if (outer)
3910         return CLASS_E_NOAGGREGATION;
3911 
3912     *obj = NULL;
3913 
3914     convert = HeapAlloc(GetProcessHeap(), 0, sizeof(struct convert_charset));
3915     if (!convert) return E_OUTOFMEMORY;
3916 
3917     convert->IMLangConvertCharset_iface.lpVtbl = &MLangConvertCharsetVtbl;
3918     convert->ref = 1;
3919 
3920     *obj = &convert->IMLangConvertCharset_iface;
3921 
3922     LockModule();
3923 
3924     return S_OK;
3925 }
3926 
3927 /******************************************************************************/
3928 
3929 HRESULT WINAPI DllCanUnloadNow(void)
3930 {
3931     return dll_count == 0 ? S_OK : S_FALSE;
3932 }
3933 
3934 
3935 /***********************************************************************
3936  *		DllRegisterServer (MLANG.@)
3937  */
3938 HRESULT WINAPI DllRegisterServer(void)
3939 {
3940     return __wine_register_resources( instance );
3941 }
3942 
3943 /***********************************************************************
3944  *		DllUnregisterServer (MLANG.@)
3945  */
3946 HRESULT WINAPI DllUnregisterServer(void)
3947 {
3948     return __wine_unregister_resources( instance );
3949 }
3950 
3951 HRESULT WINAPI GetGlobalFontLinkObject(void **unknown)
3952 {
3953     if (!unknown) return E_INVALIDARG;
3954 
3955     FIXME("%p: stub\n", unknown);
3956 
3957     return S_FALSE;
3958 }
3959