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