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