xref: /reactos/dll/win32/msctf/msctf.c (revision 8a978a17)
1 /*
2  * MSCTF Server DLL
3  *
4  * Copyright 2008 Aric Stewart, CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 #include <stdio.h>
23 
24 #define COBJMACROS
25 
26 #include "wine/debug.h"
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winreg.h"
30 #include "shlwapi.h"
31 #include "shlguid.h"
32 #include "comcat.h"
33 #include "olectl.h"
34 #include "rpcproxy.h"
35 #include "msctf.h"
36 #include "inputscope.h"
37 
38 #include "msctf_internal.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
41 
42 static HINSTANCE MSCTF_hinstance;
43 
44 typedef struct
45 {
46     DWORD id;
47     DWORD magic;
48     LPVOID data;
49 } CookieInternal;
50 
51 typedef struct {
52     TF_LANGUAGEPROFILE      LanguageProfile;
53     ITfTextInputProcessor   *pITfTextInputProcessor;
54     ITfThreadMgrEx          *pITfThreadMgrEx;
55     ITfKeyEventSink         *pITfKeyEventSink;
56     TfClientId              tid;
57 } ActivatedTextService;
58 
59 typedef struct
60 {
61     struct list entry;
62     ActivatedTextService *ats;
63 } AtsEntry;
64 
65 static CookieInternal *cookies;
66 static UINT id_last;
67 static UINT array_size;
68 
69 static struct list AtsList = LIST_INIT(AtsList);
70 static UINT activated = 0;
71 
72 DWORD tlsIndex = 0;
73 TfClientId processId = 0;
74 ITfCompartmentMgr *globalCompartmentMgr = NULL;
75 
76 const WCHAR szwSystemTIPKey[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\','C','T','F','\\','T','I','P',0};
77 const WCHAR szwSystemCTFKey[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\','C','T','F',0};
78 
79 typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut);
80 
81 static const struct {
82     REFCLSID clsid;
83     LPFNCONSTRUCTOR ctor;
84 } ClassesTable[] = {
85     {&CLSID_TF_ThreadMgr, ThreadMgr_Constructor},
86     {&CLSID_TF_InputProcessorProfiles, InputProcessorProfiles_Constructor},
87     {&CLSID_TF_CategoryMgr, CategoryMgr_Constructor},
88     {&CLSID_TF_LangBarMgr, LangBarMgr_Constructor},
89     {&CLSID_TF_DisplayAttributeMgr, DisplayAttributeMgr_Constructor},
90     {NULL, NULL}
91 };
92 
93 typedef struct tagClassFactory
94 {
95     IClassFactory IClassFactory_iface;
96     LONG   ref;
97     LPFNCONSTRUCTOR ctor;
98 } ClassFactory;
99 
100 static inline ClassFactory *impl_from_IClassFactory(IClassFactory *iface)
101 {
102     return CONTAINING_RECORD(iface, ClassFactory, IClassFactory_iface);
103 }
104 
105 static void ClassFactory_Destructor(ClassFactory *This)
106 {
107     TRACE("Destroying class factory %p\n", This);
108     HeapFree(GetProcessHeap(),0,This);
109 }
110 
111 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppvOut)
112 {
113     *ppvOut = NULL;
114     if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown)) {
115         IClassFactory_AddRef(iface);
116         *ppvOut = iface;
117         return S_OK;
118     }
119 
120     WARN("Unknown interface %s\n", debugstr_guid(riid));
121     return E_NOINTERFACE;
122 }
123 
124 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
125 {
126     ClassFactory *This = impl_from_IClassFactory(iface);
127     return InterlockedIncrement(&This->ref);
128 }
129 
130 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
131 {
132     ClassFactory *This = impl_from_IClassFactory(iface);
133     ULONG ret = InterlockedDecrement(&This->ref);
134 
135     if (ret == 0)
136         ClassFactory_Destructor(This);
137     return ret;
138 }
139 
140 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID iid, LPVOID *ppvOut)
141 {
142     ClassFactory *This = impl_from_IClassFactory(iface);
143     HRESULT ret;
144     IUnknown *obj;
145 
146     TRACE("(%p, %p, %s, %p)\n", iface, punkOuter, debugstr_guid(iid), ppvOut);
147     ret = This->ctor(punkOuter, &obj);
148     if (FAILED(ret))
149         return ret;
150     ret = IUnknown_QueryInterface(obj, iid, ppvOut);
151     IUnknown_Release(obj);
152     return ret;
153 }
154 
155 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
156 {
157     ClassFactory *This = impl_from_IClassFactory(iface);
158 
159     TRACE("(%p)->(%x)\n", This, fLock);
160 
161     return S_OK;
162 }
163 
164 static const IClassFactoryVtbl ClassFactoryVtbl = {
165     /* IUnknown */
166     ClassFactory_QueryInterface,
167     ClassFactory_AddRef,
168     ClassFactory_Release,
169 
170     /* IClassFactory*/
171     ClassFactory_CreateInstance,
172     ClassFactory_LockServer
173 };
174 
175 static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut)
176 {
177     ClassFactory *This = HeapAlloc(GetProcessHeap(),0,sizeof(ClassFactory));
178     This->IClassFactory_iface.lpVtbl = &ClassFactoryVtbl;
179     This->ref = 1;
180     This->ctor = ctor;
181     *ppvOut = &This->IClassFactory_iface;
182     TRACE("Created class factory %p\n", This);
183     return S_OK;
184 }
185 
186 /*************************************************************************
187  * DWORD Cookie Management
188  */
189 DWORD generate_Cookie(DWORD magic, LPVOID data)
190 {
191     UINT i;
192 
193     /* try to reuse IDs if possible */
194     for (i = 0; i < id_last; i++)
195         if (cookies[i].id == 0) break;
196 
197     if (i == array_size)
198     {
199         if (!array_size)
200         {
201             cookies = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CookieInternal) * 10);
202             if (!cookies)
203             {
204                 ERR("Out of memory, Unable to alloc cookies array\n");
205                 return 0;
206             }
207             array_size = 10;
208         }
209         else
210         {
211             CookieInternal *new_cookies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cookies,
212                                                       sizeof(CookieInternal) * (array_size * 2));
213             if (!new_cookies)
214             {
215                 ERR("Out of memory, Unable to realloc cookies array\n");
216                 return 0;
217             }
218             cookies = new_cookies;
219             array_size *= 2;
220         }
221     }
222 
223     cookies[i].id = i + 1; /* a return of 0 is used for failure */
224     cookies[i].magic = magic;
225     cookies[i].data = data;
226 
227     if (i == id_last)
228         id_last++;
229 
230     return cookies[i].id;
231 }
232 
233 DWORD get_Cookie_magic(DWORD id)
234 {
235     UINT index = id - 1;
236 
237     if (index >= id_last)
238         return 0;
239 
240     if (cookies[index].id == 0)
241         return 0;
242 
243     return cookies[index].magic;
244 }
245 
246 LPVOID get_Cookie_data(DWORD id)
247 {
248     UINT index = id - 1;
249 
250     if (index >= id_last)
251         return NULL;
252 
253     if (cookies[index].id == 0)
254         return NULL;
255 
256     return cookies[index].data;
257 }
258 
259 LPVOID remove_Cookie(DWORD id)
260 {
261     UINT index = id - 1;
262 
263     if (index >= id_last)
264         return NULL;
265 
266     if (cookies[index].id == 0)
267         return NULL;
268 
269     cookies[index].id = 0;
270     return cookies[index].data;
271 }
272 
273 DWORD enumerate_Cookie(DWORD magic, DWORD *index)
274 {
275     unsigned int i;
276     for (i = *index; i < id_last; i++)
277         if (cookies[i].id != 0 && cookies[i].magic == magic)
278         {
279             *index = (i+1);
280             return cookies[i].id;
281         }
282     return 0x0;
283 }
284 
285 HRESULT advise_sink(struct list *sink_list, REFIID riid, DWORD cookie_magic, IUnknown *unk, DWORD *cookie)
286 {
287     Sink *sink;
288 
289     sink = HeapAlloc(GetProcessHeap(), 0, sizeof(*sink));
290     if (!sink)
291         return E_OUTOFMEMORY;
292 
293     if (FAILED(IUnknown_QueryInterface(unk, riid, (void**)&sink->interfaces.pIUnknown)))
294     {
295         HeapFree(GetProcessHeap(), 0, sink);
296         return CONNECT_E_CANNOTCONNECT;
297     }
298 
299     list_add_head(sink_list, &sink->entry);
300     *cookie = generate_Cookie(cookie_magic, sink);
301     TRACE("cookie %x\n", *cookie);
302     return S_OK;
303 }
304 
305 static void free_sink(Sink *sink)
306 {
307     list_remove(&sink->entry);
308     IUnknown_Release(sink->interfaces.pIUnknown);
309     HeapFree(GetProcessHeap(), 0, sink);
310 }
311 
312 HRESULT unadvise_sink(DWORD cookie)
313 {
314     Sink *sink;
315 
316     sink = remove_Cookie(cookie);
317     if (!sink)
318         return CONNECT_E_NOCONNECTION;
319 
320     free_sink(sink);
321     return S_OK;
322 }
323 
324 void free_sinks(struct list *sink_list)
325 {
326     while(!list_empty(sink_list))
327     {
328         Sink* sink = LIST_ENTRY(sink_list->next, Sink, entry);
329         free_sink(sink);
330     }
331 }
332 
333 /*****************************************************************************
334  * Active Text Service Management
335  *****************************************************************************/
336 static HRESULT activate_given_ts(ActivatedTextService *actsvr, ITfThreadMgrEx *tm)
337 {
338     HRESULT hr;
339 
340     /* Already Active? */
341     if (actsvr->pITfTextInputProcessor)
342         return S_OK;
343 
344     hr = CoCreateInstance (&actsvr->LanguageProfile.clsid, NULL, CLSCTX_INPROC_SERVER,
345         &IID_ITfTextInputProcessor, (void**)&actsvr->pITfTextInputProcessor);
346     if (FAILED(hr)) return hr;
347 
348     hr = ITfTextInputProcessor_Activate(actsvr->pITfTextInputProcessor, (ITfThreadMgr *)tm, actsvr->tid);
349     if (FAILED(hr))
350     {
351         ITfTextInputProcessor_Release(actsvr->pITfTextInputProcessor);
352         actsvr->pITfTextInputProcessor = NULL;
353         return hr;
354     }
355 
356     actsvr->pITfThreadMgrEx = tm;
357     ITfThreadMgrEx_AddRef(tm);
358     return hr;
359 }
360 
361 static HRESULT deactivate_given_ts(ActivatedTextService *actsvr)
362 {
363     HRESULT hr = S_OK;
364 
365     if (actsvr->pITfTextInputProcessor)
366     {
367         hr = ITfTextInputProcessor_Deactivate(actsvr->pITfTextInputProcessor);
368         ITfTextInputProcessor_Release(actsvr->pITfTextInputProcessor);
369         ITfThreadMgrEx_Release(actsvr->pITfThreadMgrEx);
370         actsvr->pITfTextInputProcessor = NULL;
371         actsvr->pITfThreadMgrEx = NULL;
372     }
373 
374     return hr;
375 }
376 
377 static void deactivate_remove_conflicting_ts(REFCLSID catid)
378 {
379     AtsEntry *ats, *cursor2;
380 
381     LIST_FOR_EACH_ENTRY_SAFE(ats, cursor2, &AtsList, AtsEntry, entry)
382     {
383         if (IsEqualCLSID(catid,&ats->ats->LanguageProfile.catid))
384         {
385             deactivate_given_ts(ats->ats);
386             list_remove(&ats->entry);
387             HeapFree(GetProcessHeap(),0,ats->ats);
388             HeapFree(GetProcessHeap(),0,ats);
389             /* we are guaranteeing there is only 1 */
390             break;
391         }
392     }
393 }
394 
395 HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp)
396 {
397     ActivatedTextService *actsvr;
398     ITfCategoryMgr *catmgr;
399     AtsEntry *entry;
400     ITfThreadMgrEx *tm = TlsGetValue(tlsIndex);
401     ITfClientId *clientid;
402 
403     if (!tm) return E_UNEXPECTED;
404 
405     actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService));
406     if (!actsvr) return E_OUTOFMEMORY;
407 
408     ITfThreadMgrEx_QueryInterface(tm, &IID_ITfClientId, (void **)&clientid);
409     ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid);
410     ITfClientId_Release(clientid);
411 
412     if (!actsvr->tid)
413     {
414         HeapFree(GetProcessHeap(),0,actsvr);
415         return E_OUTOFMEMORY;
416     }
417 
418     actsvr->pITfTextInputProcessor = NULL;
419     actsvr->LanguageProfile = *lp;
420     actsvr->pITfKeyEventSink = NULL;
421 
422     /* get TIP category */
423     if (SUCCEEDED(CategoryMgr_Constructor(NULL,(IUnknown**)&catmgr)))
424     {
425         static const GUID *list[3] = {&GUID_TFCAT_TIP_SPEECH, &GUID_TFCAT_TIP_KEYBOARD, &GUID_TFCAT_TIP_HANDWRITING};
426 
427         ITfCategoryMgr_FindClosestCategory(catmgr,
428                 &actsvr->LanguageProfile.clsid, &actsvr->LanguageProfile.catid,
429                 list, 3);
430 
431         ITfCategoryMgr_Release(catmgr);
432     }
433     else
434     {
435         ERR("CategoryMgr construction failed\n");
436         actsvr->LanguageProfile.catid = GUID_NULL;
437     }
438 
439     if (!IsEqualGUID(&actsvr->LanguageProfile.catid,&GUID_NULL))
440         deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid);
441 
442     if (activated > 0)
443         activate_given_ts(actsvr, tm);
444 
445     entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry));
446 
447     if (!entry)
448     {
449         HeapFree(GetProcessHeap(),0,actsvr);
450         return E_OUTOFMEMORY;
451     }
452 
453     entry->ats = actsvr;
454     list_add_head(&AtsList, &entry->entry);
455 
456     return S_OK;
457 }
458 
459 BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile)
460 {
461     AtsEntry *ats;
462 
463     LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
464     {
465         if (IsEqualCLSID(rclsid,&ats->ats->LanguageProfile.clsid))
466         {
467             if (profile)
468                 *profile = ats->ats->LanguageProfile;
469             return TRUE;
470         }
471     }
472     return FALSE;
473 }
474 
475 HRESULT activate_textservices(ITfThreadMgrEx *tm)
476 {
477     HRESULT hr = S_OK;
478     AtsEntry *ats;
479 
480     activated ++;
481     if (activated > 1)
482         return S_OK;
483 
484     LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
485     {
486         hr = activate_given_ts(ats->ats, tm);
487         if (FAILED(hr))
488             FIXME("Failed to activate text service\n");
489     }
490     return hr;
491 }
492 
493 HRESULT deactivate_textservices(void)
494 {
495     AtsEntry *ats;
496 
497     if (activated > 0)
498         activated --;
499 
500     if (activated == 0)
501         LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
502             deactivate_given_ts(ats->ats);
503 
504     return S_OK;
505 }
506 
507 CLSID get_textservice_clsid(TfClientId tid)
508 {
509     AtsEntry *ats;
510 
511     LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
512         if (ats->ats->tid == tid)
513             return ats->ats->LanguageProfile.clsid;
514     return GUID_NULL;
515 }
516 
517 HRESULT get_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown **sink)
518 {
519     AtsEntry *ats;
520 
521     if (!IsEqualCLSID(iid,&IID_ITfKeyEventSink))
522         return E_NOINTERFACE;
523 
524     LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
525         if (ats->ats->tid == tid)
526         {
527             *sink = (IUnknown*)ats->ats->pITfKeyEventSink;
528             return S_OK;
529         }
530 
531     return E_FAIL;
532 }
533 
534 HRESULT set_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown* sink)
535 {
536     AtsEntry *ats;
537 
538     if (!IsEqualCLSID(iid,&IID_ITfKeyEventSink))
539         return E_NOINTERFACE;
540 
541     LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
542         if (ats->ats->tid == tid)
543         {
544             ats->ats->pITfKeyEventSink = (ITfKeyEventSink*)sink;
545             return S_OK;
546         }
547 
548     return E_FAIL;
549 }
550 
551 /*************************************************************************
552  * MSCTF DllMain
553  */
554 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad)
555 {
556     TRACE("%p 0x%x %p\n", hinst, fdwReason, fImpLoad);
557     switch (fdwReason)
558     {
559         case DLL_WINE_PREATTACH:
560             return FALSE;   /* prefer native version */
561         case DLL_PROCESS_ATTACH:
562             MSCTF_hinstance = hinst;
563             tlsIndex = TlsAlloc();
564             break;
565         case DLL_PROCESS_DETACH:
566             if (fImpLoad) break;
567             TlsFree(tlsIndex);
568             break;
569     }
570     return TRUE;
571 }
572 
573 /*************************************************************************
574  *              DllCanUnloadNow (MSCTF.@)
575  */
576 HRESULT WINAPI DllCanUnloadNow(void)
577 {
578     return S_FALSE;
579 }
580 
581 /***********************************************************************
582  *              DllGetClassObject (MSCTF.@)
583  */
584 HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID *ppvOut)
585 {
586     int i;
587 
588     *ppvOut = NULL;
589     if (!IsEqualIID(iid, &IID_IUnknown) && !IsEqualIID(iid, &IID_IClassFactory))
590         return E_NOINTERFACE;
591 
592     for (i = 0; ClassesTable[i].clsid != NULL; i++)
593         if (IsEqualCLSID(ClassesTable[i].clsid, clsid)) {
594             return ClassFactory_Constructor(ClassesTable[i].ctor, ppvOut);
595         }
596     FIXME("CLSID %s not supported\n", debugstr_guid(clsid));
597     return CLASS_E_CLASSNOTAVAILABLE;
598 }
599 
600 /***********************************************************************
601  *		DllRegisterServer (MSCTF.@)
602  */
603 HRESULT WINAPI DllRegisterServer(void)
604 {
605     return __wine_register_resources( MSCTF_hinstance );
606 }
607 
608 /***********************************************************************
609  *		DllUnregisterServer (MSCTF.@)
610  */
611 HRESULT WINAPI DllUnregisterServer(void)
612 {
613     return __wine_unregister_resources( MSCTF_hinstance );
614 }
615 
616 /***********************************************************************
617  *              TF_CreateThreadMgr (MSCTF.@)
618  */
619 HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim)
620 {
621     TRACE("\n");
622     return ThreadMgr_Constructor(NULL,(IUnknown**)pptim);
623 }
624 
625 /***********************************************************************
626  *              TF_GetThreadMgr (MSCTF.@)
627  */
628 HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim)
629 {
630     TRACE("\n");
631     *pptim = TlsGetValue(tlsIndex);
632 
633     if (*pptim)
634         ITfThreadMgr_AddRef(*pptim);
635 
636     return S_OK;
637 }
638 
639 /***********************************************************************
640  *              SetInputScope(MSCTF.@)
641  */
642 HRESULT WINAPI SetInputScope(HWND hwnd, InputScope inputscope)
643 {
644     FIXME("STUB: %p %i\n",hwnd,inputscope);
645     return S_OK;
646 }
647 
648 /***********************************************************************
649  *              SetInputScopes(MSCTF.@)
650  */
651 HRESULT WINAPI SetInputScopes(HWND hwnd, const InputScope *pInputScopes,
652                               UINT cInputScopes, WCHAR **ppszPhraseList,
653                               UINT cPhrases, WCHAR *pszRegExp, WCHAR *pszSRGS)
654 {
655     UINT i;
656     FIXME("STUB: %p ... %s %s\n",hwnd, debugstr_w(pszRegExp), debugstr_w(pszSRGS));
657     for (i = 0; i < cInputScopes; i++)
658         TRACE("\tScope[%u] = %i\n",i,pInputScopes[i]);
659     for (i = 0; i < cPhrases; i++)
660         TRACE("\tPhrase[%u] = %s\n",i,debugstr_w(ppszPhraseList[i]));
661 
662     return S_OK;
663 }
664 
665 /***********************************************************************
666  *              TF_CreateInputProcessorProfiles(MSCTF.@)
667  */
668 HRESULT WINAPI TF_CreateInputProcessorProfiles(
669                         ITfInputProcessorProfiles **ppipr)
670 {
671     return InputProcessorProfiles_Constructor(NULL,(IUnknown**)ppipr);
672 }
673 
674 /***********************************************************************
675  *              TF_InvalidAssemblyListCacheIfExist(MSCTF.@)
676  */
677 HRESULT WINAPI TF_InvalidAssemblyListCacheIfExist(void)
678 {
679     FIXME("Stub\n");
680     return S_OK;
681 }
682 
683 /***********************************************************************
684  *              TF_CreateLangBarMgr (MSCTF.@)
685  */
686 HRESULT WINAPI TF_CreateLangBarMgr(ITfLangBarMgr **pppbm)
687 {
688     TRACE("\n");
689     return LangBarMgr_Constructor(NULL,(IUnknown**)pppbm);
690 }
691 
692 HRESULT WINAPI TF_CreateLangBarItemMgr(ITfLangBarItemMgr **pplbim)
693 {
694     FIXME("stub %p\n", pplbim);
695     *pplbim = NULL;
696 
697     return E_NOTIMPL;
698 }
699 
700 /***********************************************************************
701  *              TF_InitMlngInfo (MSCTF.@)
702  */
703 HRESULT WINAPI TF_InitMlngInfo(void)
704 {
705     FIXME("stub\n");
706     return S_OK;
707 }
708