xref: /vim-8.2.3635/src/if_ole.cpp (revision 01a6c216)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * VIM - Vi IMproved		by Bram Moolenaar
4  *
5  * Do ":help uganda"  in Vim to read copying and usage conditions.
6  * Do ":help credits" in Vim to see a list of people who contributed.
7  */
8 
9 #if defined(FEAT_OLE) && defined(FEAT_GUI_W32)
10 /*
11  * OLE server implementation.
12  *
13  * See os_mswin.c for the client side.
14  */
15 
16 /*
17  * We have some trouble with order of includes here.  For Borland it needs to
18  * be different from MSVC...
19  */
20 #ifndef __BORLANDC__
21 extern "C" {
22 # include "vim.h"
23 }
24 #endif
25 
26 #include <windows.h>
27 #include <oleauto.h>
28 
29 extern "C" {
30 #ifdef __BORLANDC__
31 # include "vim.h"
32 #endif
33 extern HWND s_hwnd;
34 extern HWND vim_parent_hwnd;
35 }
36 
37 #if (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L)
38 # define FINAL final
39 #else
40 # define FINAL
41 #endif
42 
43 #if (defined(_MSC_VER) && _MSC_VER < 1300) || !defined(MAXULONG_PTR)
44 /* Work around old versions of basetsd.h which wrongly declares
45  * UINT_PTR as unsigned long */
46 # undef UINT_PTR
47 # define UINT_PTR UINT
48 #endif
49 
50 #include "if_ole.h"	// Interface definitions
51 #include "iid_ole.c"	// UUID definitions (compile here)
52 
53 /* Supply function prototype to work around bug in Mingw oleauto.h header */
54 #ifdef __MINGW32__
55 WINOLEAUTAPI UnRegisterTypeLib(REFGUID libID, WORD wVerMajor,
56 	    WORD wVerMinor, LCID lcid, SYSKIND syskind);
57 #endif
58 
59 /*****************************************************************************
60  1. Internal definitions for this file
61 *****************************************************************************/
62 
63 class CVim;
64 class CVimCF;
65 
66 /* Internal data */
67 // The identifier of the registered class factory
68 static unsigned long cf_id = 0;
69 
70 // The identifier of the running application object
71 static unsigned long app_id = 0;
72 
73 // The single global instance of the class factory
74 static CVimCF *cf = 0;
75 
76 // The single global instance of the application object
77 static CVim *app = 0;
78 
79 /* GUIDs, versions and type library information */
80 #define MYCLSID CLSID_Vim
81 #define MYLIBID LIBID_Vim
82 #define MYIID IID_IVim
83 
84 #define MAJORVER 1
85 #define MINORVER 0
86 #define LOCALE 0x0409
87 
88 #define MYNAME "Vim"
89 #define MYPROGID "Vim.Application.1"
90 #define MYVIPROGID "Vim.Application"
91 
92 #define MAX_CLSID_LEN 100
93 
94 /*****************************************************************************
95  2. The application object
96 *****************************************************************************/
97 
98 /* Definition
99  * ----------
100  */
101 
102 class CVim FINAL : public IVim
103 {
104 public:
105     virtual ~CVim();
106     static CVim *Create(int *pbDoRestart);
107 
108     // IUnknown members
109     STDMETHOD(QueryInterface)(REFIID riid, void ** ppv);
110     STDMETHOD_(unsigned long, AddRef)(void);
111     STDMETHOD_(unsigned long, Release)(void);
112 
113     // IDispatch members
114     STDMETHOD(GetTypeInfoCount)(UINT *pCount);
115     STDMETHOD(GetTypeInfo)(UINT iTypeInfo, LCID, ITypeInfo **ppITypeInfo);
116     STDMETHOD(GetIDsOfNames)(const IID &iid, OLECHAR **names, UINT n, LCID, DISPID *dispids);
117     STDMETHOD(Invoke)(DISPID member, const IID &iid, LCID, WORD flags, DISPPARAMS *dispparams, VARIANT *result, EXCEPINFO *excepinfo, UINT *argerr);
118 
119     // IVim members
120     STDMETHOD(SendKeys)(BSTR keys);
121     STDMETHOD(Eval)(BSTR expr, BSTR *result);
122     STDMETHOD(SetForeground)(void);
123     STDMETHOD(GetHwnd)(UINT_PTR *result);
124 
125 private:
126     // Constructor is private - create using CVim::Create()
127     CVim() : ref(0), typeinfo(0) {};
128 
129     // Reference count
130     unsigned long ref;
131 
132     // The object's TypeInfo
133     ITypeInfo *typeinfo;
134 };
135 
136 /* Implementation
137  * --------------
138  */
139 
140 CVim *CVim::Create(int *pbDoRestart)
141 {
142     HRESULT hr;
143     CVim *me = 0;
144     ITypeLib *typelib = 0;
145     ITypeInfo *typeinfo = 0;
146 
147     *pbDoRestart = FALSE;
148 
149     // Create the object
150     me = new CVim();
151     if (me == NULL)
152     {
153 	MessageBox(0, "Cannot create application object", "Vim Initialisation", 0);
154 	return NULL;
155     }
156 
157     // Load the type library from the registry
158     hr = LoadRegTypeLib(MYLIBID, 1, 0, 0x00, &typelib);
159     if (FAILED(hr))
160     {
161 	HKEY hKey;
162 
163 	// Check we can write to the registry.
164 	// RegCreateKeyEx succeeds even if key exists. W.Briscoe W2K 20021011
165 	if (RegCreateKeyEx(HKEY_CLASSES_ROOT, MYVIPROGID, 0, NULL,
166 		  REG_OPTION_NON_VOLATILE,
167 		  KEY_ALL_ACCESS, NULL, &hKey, NULL))
168 	{
169 	    delete me;
170 	    return NULL; // Unable to write to registry. Quietly fail.
171 	}
172 	RegCloseKey(hKey);
173 
174 	if (MessageBox(0, "Cannot load registered type library.\nDo you want to register Vim now?",
175 		    "Vim Initialisation", MB_YESNO | MB_ICONQUESTION) != IDYES)
176 	{
177 	    delete me;
178 	    return NULL;
179 	}
180 
181 	RegisterMe(FALSE);
182 
183 	// Load the type library from the registry
184 	hr = LoadRegTypeLib(MYLIBID, 1, 0, 0x00, &typelib);
185 	if (FAILED(hr))
186 	{
187 	    MessageBox(0, "You must restart Vim in order for the registration to take effect.",
188 						     "Vim Initialisation", 0);
189 	    *pbDoRestart = TRUE;
190 	    delete me;
191 	    return NULL;
192 	}
193     }
194 
195     // Get the type info of the vtable interface
196     hr = typelib->GetTypeInfoOfGuid(MYIID, &typeinfo);
197     typelib->Release();
198 
199     if (FAILED(hr))
200     {
201 	MessageBox(0, "Cannot get interface type information",
202 						     "Vim Initialisation", 0);
203 	delete me;
204 	return NULL;
205     }
206 
207     // Save the type information
208     me->typeinfo = typeinfo;
209     return me;
210 }
211 
212 CVim::~CVim()
213 {
214     if (typeinfo && vim_parent_hwnd == NULL)
215 	typeinfo->Release();
216     typeinfo = 0;
217 }
218 
219 STDMETHODIMP
220 CVim::QueryInterface(REFIID riid, void **ppv)
221 {
222     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDispatch) || IsEqualIID(riid, MYIID))
223     {
224 	AddRef();
225 	*ppv = this;
226 	return S_OK;
227     }
228 
229     *ppv = 0;
230     return E_NOINTERFACE;
231 }
232 
233 STDMETHODIMP_(ULONG)
234 CVim::AddRef()
235 {
236     return ++ref;
237 }
238 
239 STDMETHODIMP_(ULONG)
240 CVim::Release()
241 {
242     // Don't delete the object when the reference count reaches zero, as there
243     // is only a single application object, and its lifetime is controlled by
244     // the running instance, not by its reference count.
245     if (ref > 0)
246 	--ref;
247     return ref;
248 }
249 
250 STDMETHODIMP
251 CVim::GetTypeInfoCount(UINT *pCount)
252 {
253     *pCount = 1;
254     return S_OK;
255 }
256 
257 STDMETHODIMP
258 CVim::GetTypeInfo(UINT iTypeInfo, LCID, ITypeInfo **ppITypeInfo)
259 {
260     *ppITypeInfo = 0;
261 
262     if (iTypeInfo != 0)
263 	return DISP_E_BADINDEX;
264 
265     typeinfo->AddRef();
266     *ppITypeInfo = typeinfo;
267     return S_OK;
268 }
269 
270 STDMETHODIMP
271 CVim::GetIDsOfNames(
272 	const IID &iid,
273 	OLECHAR **names,
274 	UINT n,
275 	LCID,
276 	DISPID *dispids)
277 {
278     if (iid != IID_NULL)
279 	return DISP_E_UNKNOWNINTERFACE;
280 
281     return typeinfo->GetIDsOfNames(names, n, dispids);
282 }
283 
284 STDMETHODIMP
285 CVim::Invoke(
286 	DISPID member,
287 	const IID &iid,
288 	LCID,
289 	WORD flags,
290 	DISPPARAMS *dispparams,
291 	VARIANT *result,
292 	EXCEPINFO *excepinfo,
293 	UINT *argerr)
294 {
295     if (iid != IID_NULL)
296 	return DISP_E_UNKNOWNINTERFACE;
297 
298     ::SetErrorInfo(0, NULL);
299     return typeinfo->Invoke(static_cast<IDispatch*>(this),
300 			    member, flags, dispparams,
301 			    result, excepinfo, argerr);
302 }
303 
304 STDMETHODIMP
305 CVim::GetHwnd(UINT_PTR *result)
306 {
307     *result = (UINT_PTR)s_hwnd;
308     return S_OK;
309 }
310 
311 STDMETHODIMP
312 CVim::SetForeground(void)
313 {
314     /* Make the Vim window come to the foreground */
315     gui_mch_set_foreground();
316     return S_OK;
317 }
318 
319 STDMETHODIMP
320 CVim::SendKeys(BSTR keys)
321 {
322     int len;
323     char *buffer;
324     char_u *str;
325     char_u *ptr;
326 
327     /* Get a suitable buffer */
328     len = WideCharToMultiByte(CP_ACP, 0, keys, -1, 0, 0, 0, 0);
329     buffer = (char *)alloc(len+1);
330 
331     if (buffer == NULL)
332 	return E_OUTOFMEMORY;
333 
334     len = WideCharToMultiByte(CP_ACP, 0, keys, -1, buffer, len, 0, 0);
335 
336     if (len == 0)
337     {
338 	vim_free(buffer);
339 	return E_INVALIDARG;
340     }
341 
342     /* Translate key codes like <Esc> */
343     str = replace_termcodes((char_u *)buffer, &ptr, FALSE, TRUE, FALSE);
344 
345     /* If ptr was set, then a new buffer was allocated,
346      * so we can free the old one.
347      */
348     if (ptr)
349 	vim_free((char_u *)(buffer));
350 
351     /* Reject strings too long to fit in the input buffer. Allow 10 bytes
352      * space to cover for the (remote) possibility that characters may enter
353      * the input buffer between now and when the WM_OLE message is actually
354      * processed. If more that 10 characters enter the input buffer in that
355      * time, the WM_OLE processing will simply fail to insert the characters.
356      */
357     if ((int)(STRLEN(str)) > (vim_free_in_input_buf() - 10))
358     {
359 	vim_free(str);
360 	return E_INVALIDARG;
361     }
362 
363     /* Pass the string to the main input loop. The memory will be freed when
364      * the message is processed.  Except for an empty message, we don't need
365      * to post it then.
366      */
367     if (*str == NUL)
368 	vim_free(str);
369     else
370 	PostMessage(NULL, WM_OLE, 0, (LPARAM)str);
371 
372     return S_OK;
373 }
374 
375 STDMETHODIMP
376 CVim::Eval(BSTR expr, BSTR *result)
377 {
378 #ifdef FEAT_EVAL
379     int len;
380     char *buffer;
381     char *str;
382     wchar_t *w_buffer;
383 
384     /* Get a suitable buffer */
385     len = WideCharToMultiByte(CP_ACP, 0, expr, -1, 0, 0, 0, 0);
386     if (len == 0)
387 	return E_INVALIDARG;
388 
389     buffer = (char *)alloc((unsigned)len);
390 
391     if (buffer == NULL)
392 	return E_OUTOFMEMORY;
393 
394     /* Convert the (wide character) expression to an ASCII string */
395     len = WideCharToMultiByte(CP_ACP, 0, expr, -1, buffer, len, 0, 0);
396     if (len == 0)
397 	return E_INVALIDARG;
398 
399     /* Evaluate the expression */
400     ++emsg_skip;
401     str = (char *)eval_to_string((char_u *)buffer, NULL, TRUE);
402     --emsg_skip;
403     vim_free(buffer);
404     if (str == NULL)
405 	return E_FAIL;
406 
407     /* Convert the result to wide characters */
408     MultiByteToWideChar_alloc(CP_ACP, 0, str, -1, &w_buffer, &len);
409     vim_free(str);
410     if (w_buffer == NULL)
411 	return E_OUTOFMEMORY;
412 
413     if (len == 0)
414     {
415 	vim_free(w_buffer);
416 	return E_FAIL;
417     }
418 
419     /* Store the result */
420     *result = SysAllocString(w_buffer);
421     vim_free(w_buffer);
422 
423     return S_OK;
424 #else
425     return E_NOTIMPL;
426 #endif
427 }
428 
429 /*****************************************************************************
430  3. The class factory
431 *****************************************************************************/
432 
433 /* Definition
434  * ----------
435  */
436 
437 class CVimCF FINAL : public IClassFactory
438 {
439 public:
440     static CVimCF *Create();
441     virtual ~CVimCF() {};
442 
443     STDMETHOD(QueryInterface)(REFIID riid, void ** ppv);
444     STDMETHOD_(unsigned long, AddRef)(void);
445     STDMETHOD_(unsigned long, Release)(void);
446     STDMETHOD(CreateInstance)(IUnknown *punkOuter, REFIID riid, void ** ppv);
447     STDMETHOD(LockServer)(BOOL lock);
448 
449 private:
450     // Constructor is private - create via Create()
451     CVimCF() : ref(0) {};
452 
453     // Reference count
454     unsigned long ref;
455 };
456 
457 /* Implementation
458  * --------------
459  */
460 
461 CVimCF *CVimCF::Create()
462 {
463     CVimCF *me = new CVimCF();
464 
465     if (me == NULL)
466 	MessageBox(0, "Cannot create class factory", "Vim Initialisation", 0);
467 
468     return me;
469 }
470 
471 STDMETHODIMP
472 CVimCF::QueryInterface(REFIID riid, void **ppv)
473 {
474     if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
475     {
476 	AddRef();
477 	*ppv = this;
478 	return S_OK;
479     }
480 
481     *ppv = 0;
482     return E_NOINTERFACE;
483 }
484 
485 STDMETHODIMP_(ULONG)
486 CVimCF::AddRef()
487 {
488     return ++ref;
489 }
490 
491 STDMETHODIMP_(ULONG)
492 CVimCF::Release()
493 {
494     // Don't delete the object when the reference count reaches zero, as there
495     // is only a single application object, and its lifetime is controlled by
496     // the running instance, not by its reference count.
497     if (ref > 0)
498 	--ref;
499     return ref;
500 }
501 
502 /*ARGSUSED*/
503 STDMETHODIMP
504 CVimCF::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
505 {
506     return app->QueryInterface(riid, ppv);
507 }
508 
509 /*ARGSUSED*/
510 STDMETHODIMP
511 CVimCF::LockServer(BOOL lock)
512 {
513     return S_OK;
514 }
515 
516 /*****************************************************************************
517  4. Registry manipulation code
518 *****************************************************************************/
519 
520 // Internal use only
521 static void SetKeyAndValue(const char *path, const char *subkey, const char *value);
522 static void GUIDtochar(const GUID &guid, char *GUID, int length);
523 static void RecursiveDeleteKey(HKEY hKeyParent, const char *child);
524 static const int GUID_STRING_SIZE = 39;
525 
526 // Register the component in the registry
527 // When "silent" is TRUE don't give any messages.
528 
529 extern "C" void RegisterMe(int silent)
530 {
531     BOOL ok = TRUE;
532 
533     // Get the application startup command
534     char module[MAX_PATH];
535 
536     ::GetModuleFileName(NULL, module, MAX_PATH);
537 
538     // Unregister first (quietly)
539     UnregisterMe(FALSE);
540 
541     // Convert the CLSID into a char
542     char clsid[GUID_STRING_SIZE];
543     GUIDtochar(MYCLSID, clsid, sizeof(clsid));
544 
545     // Convert the LIBID into a char
546     char libid[GUID_STRING_SIZE];
547     GUIDtochar(MYLIBID, libid, sizeof(libid));
548 
549     // Build the key CLSID\\{...}
550     char Key[MAX_CLSID_LEN];
551     strcpy(Key, "CLSID\\");
552     strcat(Key, clsid);
553 
554     // Add the CLSID to the registry
555     SetKeyAndValue(Key, NULL, MYNAME);
556     SetKeyAndValue(Key, "LocalServer32", module);
557     SetKeyAndValue(Key, "ProgID", MYPROGID);
558     SetKeyAndValue(Key, "VersionIndependentProgID", MYVIPROGID);
559     SetKeyAndValue(Key, "TypeLib", libid);
560 
561     // Add the version-independent ProgID subkey under HKEY_CLASSES_ROOT
562     SetKeyAndValue(MYVIPROGID, NULL, MYNAME);
563     SetKeyAndValue(MYVIPROGID, "CLSID", clsid);
564     SetKeyAndValue(MYVIPROGID, "CurVer", MYPROGID);
565 
566     // Add the versioned ProgID subkey under HKEY_CLASSES_ROOT
567     SetKeyAndValue(MYPROGID, NULL, MYNAME);
568     SetKeyAndValue(MYPROGID, "CLSID", clsid);
569 
570     wchar_t w_module[MAX_PATH];
571     MultiByteToWideChar(CP_ACP, 0, module, -1, w_module, MAX_PATH);
572 
573     ITypeLib *typelib = NULL;
574     if (LoadTypeLib(w_module, &typelib) != S_OK)
575     {
576 	if (!silent)
577 	    MessageBox(0, "Cannot load type library to register",
578 						       "Vim Registration", 0);
579 	ok = FALSE;
580     }
581     else
582     {
583 	if (RegisterTypeLib(typelib, w_module, NULL) != S_OK)
584 	{
585 	    if (!silent)
586 		MessageBox(0, "Cannot register type library",
587 						       "Vim Registration", 0);
588 	    ok = FALSE;
589 	}
590 	typelib->Release();
591     }
592 
593     if (ok && !silent)
594 	MessageBox(0, "Registered successfully", "Vim", 0);
595 }
596 
597 // Remove the component from the registry
598 //
599 // Note: There is little error checking in this code, to allow incomplete
600 // or failed registrations to be undone.
601 extern "C" void UnregisterMe(int bNotifyUser)
602 {
603     // Unregister the type library
604     ITypeLib *typelib;
605     if (SUCCEEDED(LoadRegTypeLib(MYLIBID, MAJORVER, MINORVER, LOCALE, &typelib)))
606     {
607 	TLIBATTR *tla;
608 	if (SUCCEEDED(typelib->GetLibAttr(&tla)))
609 	{
610 	    UnRegisterTypeLib(tla->guid, tla->wMajorVerNum, tla->wMinorVerNum,
611 			      tla->lcid, tla->syskind);
612 	    typelib->ReleaseTLibAttr(tla);
613 	}
614 	typelib->Release();
615     }
616 
617     // Convert the CLSID into a char
618     char clsid[GUID_STRING_SIZE];
619     GUIDtochar(MYCLSID, clsid, sizeof(clsid));
620 
621     // Build the key CLSID\\{...}
622     char Key[MAX_CLSID_LEN];
623     strcpy(Key, "CLSID\\");
624     strcat(Key, clsid);
625 
626     // Delete the CLSID Key - CLSID\{...}
627     RecursiveDeleteKey(HKEY_CLASSES_ROOT, Key);
628 
629     // Delete the version-independent ProgID Key
630     RecursiveDeleteKey(HKEY_CLASSES_ROOT, MYVIPROGID);
631 
632     // Delete the ProgID key
633     RecursiveDeleteKey(HKEY_CLASSES_ROOT, MYPROGID);
634 
635     if (bNotifyUser)
636 	MessageBox(0, "Unregistered successfully", "Vim", 0);
637 }
638 
639 /****************************************************************************/
640 
641 // Convert a GUID to a char string
642 static void GUIDtochar(const GUID &guid, char *GUID, int length)
643 {
644     // Get wide string version
645     LPOLESTR wGUID = NULL;
646     StringFromCLSID(guid, &wGUID);
647 
648     // Covert from wide characters to non-wide
649     wcstombs(GUID, wGUID, length);
650 
651     // Free memory
652     CoTaskMemFree(wGUID);
653 }
654 
655 // Delete a key and all of its descendants
656 static void RecursiveDeleteKey(HKEY hKeyParent, const char *child)
657 {
658     // Open the child
659     HKEY hKeyChild;
660     LONG result = RegOpenKeyEx(hKeyParent, child, 0,
661 			       KEY_ALL_ACCESS, &hKeyChild);
662     if (result != ERROR_SUCCESS)
663 	return;
664 
665     // Enumerate all of the descendants of this child
666     FILETIME time;
667     char buffer[1024];
668     DWORD size = 1024;
669 
670     while (RegEnumKeyEx(hKeyChild, 0, buffer, &size, NULL,
671 			NULL, NULL, &time) == S_OK)
672     {
673 	// Delete the descendants of this child
674 	RecursiveDeleteKey(hKeyChild, buffer);
675 	size = 256;
676     }
677 
678     // Close the child
679     RegCloseKey(hKeyChild);
680 
681     // Delete this child
682     RegDeleteKey(hKeyParent, child);
683 }
684 
685 // Create a key and set its value
686 static void SetKeyAndValue(const char *key, const char *subkey, const char *value)
687 {
688     HKEY hKey;
689     char buffer[1024];
690 
691     strcpy(buffer, key);
692 
693     // Add subkey name to buffer.
694     if (subkey)
695     {
696 	strcat(buffer, "\\");
697 	strcat(buffer, subkey);
698     }
699 
700     // Create and open key and subkey.
701     long result = RegCreateKeyEx(HKEY_CLASSES_ROOT,
702 				 buffer,
703 				 0, NULL, REG_OPTION_NON_VOLATILE,
704 				 KEY_ALL_ACCESS, NULL,
705 				 &hKey, NULL);
706     if (result != ERROR_SUCCESS)
707 	return;
708 
709     // Set the value
710     if (value)
711 	RegSetValueEx(hKey, NULL, 0, REG_SZ, (BYTE *)value,
712 		      (DWORD)STRLEN(value)+1);
713 
714     RegCloseKey(hKey);
715 }
716 
717 /*****************************************************************************
718  5. OLE Initialisation and shutdown processing
719 *****************************************************************************/
720 extern "C" void InitOLE(int *pbDoRestart)
721 {
722     HRESULT hr;
723 
724     *pbDoRestart = FALSE;
725 
726     // Initialize the OLE libraries
727     hr = OleInitialize(NULL);
728     if (FAILED(hr))
729     {
730 	MessageBox(0, "Cannot initialise OLE", "Vim Initialisation", 0);
731 	goto error0;
732     }
733 
734     // Create the application object
735     app = CVim::Create(pbDoRestart);
736     if (app == NULL)
737 	goto error1;
738 
739     // Create the class factory
740     cf = CVimCF::Create();
741     if (cf == NULL)
742 	goto error1;
743 
744     // Register the class factory
745     hr = CoRegisterClassObject(
746 	MYCLSID,
747 	cf,
748 	CLSCTX_LOCAL_SERVER,
749 	REGCLS_MULTIPLEUSE,
750 	&cf_id);
751 
752     if (FAILED(hr))
753     {
754 	MessageBox(0, "Cannot register class factory", "Vim Initialisation", 0);
755 	goto error1;
756     }
757 
758     // Register the application object as active
759     hr = RegisterActiveObject(
760 	app,
761 	MYCLSID,
762 	0,
763 	&app_id);
764 
765     if (FAILED(hr))
766     {
767 	MessageBox(0, "Cannot register application object", "Vim Initialisation", 0);
768 	goto error1;
769     }
770 
771     return;
772 
773     // Errors: tidy up as much as needed and return
774 error1:
775     UninitOLE();
776 error0:
777     return;
778 }
779 
780 extern "C" void UninitOLE()
781 {
782     // Unregister the application object
783     if (app_id)
784     {
785 	RevokeActiveObject(app_id, NULL);
786 	app_id = 0;
787     }
788 
789     // Unregister the class factory
790     if (cf_id)
791     {
792 	CoRevokeClassObject(cf_id);
793 	cf_id = 0;
794     }
795 
796     // Shut down the OLE libraries
797     OleUninitialize();
798 
799     // Delete the application object
800     if (app)
801     {
802 	delete app;
803 	app = NULL;
804     }
805 
806     // Delete the class factory
807     if (cf)
808     {
809 	delete cf;
810 	cf = NULL;
811     }
812 }
813 #endif /* FEAT_OLE */
814