2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
23 Change History (most recent first):
25 $Log: ExplorerPlugin.cpp,v $
26 Revision 1.8 2005/06/30 18:01:54 shersche
27 <rdar://problem/4130635> Cause IE to rebuild cache so we don't have to reboot following an install.
29 Revision 1.7 2005/02/23 02:00:45 shersche
30 <rdar://problem/4014479> Delete all the registry entries when component is unregistered
32 Revision 1.6 2005/01/25 17:56:45 shersche
33 <rdar://problem/3911084> Load resource DLLs, get icons and bitmaps from resource DLLs
36 Revision 1.5 2004/09/15 10:33:54 shersche
37 <rdar://problem/3721611> Install XP toolbar button (8 bit mask) if running on XP platform, otherwise install 1 bit mask toolbar button
40 Revision 1.4 2004/07/13 21:24:21 rpantos
41 Fix for <rdar://problem/3701120>.
43 Revision 1.3 2004/06/26 14:12:07 shersche
44 Register the toolbar button
46 Revision 1.2 2004/06/24 20:09:39 shersche
48 Submitted by: herscher
50 Revision 1.1 2004/06/18 04:34:59 rpantos
51 Move to Clients from mDNSWindows
53 Revision 1.1 2004/01/30 03:01:56 bradley
54 Explorer Plugin to browse for DNS-SD advertised Web and FTP servers from within Internet Explorer.
60 // The following 2 includes have to be in this order and INITGUID must be defined here, before including the file
61 // that specifies the GUID(s), and nowhere else. The reason for this is that initguid.h doesn't provide separate
62 // define and declare macros for GUIDs so you have to #define INITGUID in the single file where you want to define
63 // your GUID then in all the other files that just need the GUID declared, INITGUID must not be defined.
67 #include "ExplorerPlugin.h"
72 #include "CommonServices.h"
73 #include "DebugServices.h"
75 #include "ClassFactory.h"
78 #include "loclibrary.h"
85 static char THIS_FILE
[] = __FILE__
;
89 #pragma mark == Prototypes ==
92 //===========================================================================================================================
94 //===========================================================================================================================
98 extern "C" BOOL WINAPI
DllMain( HINSTANCE inInstance
, DWORD inReason
, LPVOID inReserved
);
102 DEBUG_LOCAL OSStatus
MFCDLLProcessAttach( HINSTANCE inInstance
);
103 DEBUG_LOCAL
void MFCDLLProcessDetach( HINSTANCE inInstance
);
104 DEBUG_LOCAL
void MFCDLLThreadDetach( HINSTANCE inInstance
);
108 DEBUG_LOCAL OSStatus
RegisterServer( HINSTANCE inInstance
, CLSID inCLSID
, LPCTSTR inName
);
109 DEBUG_LOCAL OSStatus
RegisterCOMCategory( CLSID inCLSID
, CATID inCategoryID
, BOOL inRegister
);
110 DEBUG_LOCAL OSStatus
UnregisterServer( CLSID inCLSID
);
111 DEBUG_LOCAL OSStatus
MyRegDeleteKey( HKEY hKeyRoot
, LPTSTR lpSubKey
);
113 // Stash away pointers to our resource DLLs
115 static HINSTANCE g_nonLocalizedResources
= NULL
;
116 static CString g_nonLocalizedResourcesName
;
117 static HINSTANCE g_localizedResources
= NULL
;
120 GetNonLocalizedResources()
122 return g_nonLocalizedResources
;
126 GetLocalizedResources()
128 return g_localizedResources
;
131 // This is the class GUID for an undocumented hook into IE that will allow us to register
132 // and have IE notice our new ExplorerBar without rebooting.
133 // {8C7461EF-2B13-11d2-BE35-3078302C2030}
135 DEFINE_GUID(CLSID_CompCatCacheDaemon
,
136 0x8C7461EF, 0x2b13, 0x11d2, 0xbe, 0x35, 0x30, 0x78, 0x30, 0x2c, 0x20, 0x30);
140 #pragma mark == Globals ==
143 //===========================================================================================================================
145 //===========================================================================================================================
147 HINSTANCE gInstance
= NULL
;
148 int gDLLRefCount
= 0;
153 #pragma mark == DLL Exports ==
156 //===========================================================================================================================
158 //===========================================================================================================================
160 BOOL WINAPI
DllMain( HINSTANCE inInstance
, DWORD inReason
, LPVOID inReserved
)
165 DEBUG_UNUSED( inReserved
);
170 case DLL_PROCESS_ATTACH
:
171 gInstance
= inInstance
;
172 debug_initialize( kDebugOutputTypeWindowsEventLog
, "DNSServices Bar", inInstance
);
173 debug_set_property( kDebugPropertyTagPrintLevel
, kDebugLevelTrace
);
174 dlog( kDebugLevelTrace
, "\nDllMain: process attach\n" );
176 err
= MFCDLLProcessAttach( inInstance
);
177 ok
= ( err
== kNoErr
);
178 require_noerr( err
, exit
);
181 case DLL_PROCESS_DETACH
:
182 dlog( kDebugLevelTrace
, "DllMain: process detach\n" );
183 MFCDLLProcessDetach( inInstance
);
186 case DLL_THREAD_ATTACH
:
187 dlog( kDebugLevelTrace
, "DllMain: thread attach\n" );
190 case DLL_THREAD_DETACH
:
191 dlog( kDebugLevelTrace
, "DllMain: thread detach\n" );
192 MFCDLLThreadDetach( inInstance
);
196 dlog( kDebugLevelTrace
, "DllMain: unknown reason code (%d)\n",inReason
);
204 //===========================================================================================================================
206 //===========================================================================================================================
208 STDAPI
DllCanUnloadNow( void )
210 dlog( kDebugLevelTrace
, "DllCanUnloadNow (refCount=%d)\n", gDLLRefCount
);
212 return( gDLLRefCount
== 0 );
215 //===========================================================================================================================
217 //===========================================================================================================================
219 STDAPI
DllGetClassObject( REFCLSID inCLSID
, REFIID inIID
, LPVOID
*outResult
)
223 ClassFactory
* factory
;
225 dlog( kDebugLevelTrace
, "DllGetClassObject\n" );
229 // Check if the class ID is supported.
231 ok
= IsEqualCLSID( inCLSID
, CLSID_ExplorerBar
);
232 require_action_quiet( ok
, exit
, err
= CLASS_E_CLASSNOTAVAILABLE
);
234 // Create the ClassFactory object.
239 factory
= new ClassFactory( inCLSID
);
243 // Do not let exception escape.
245 require_action( factory
, exit
, err
= E_OUTOFMEMORY
);
247 // Query for the specified interface. Release the factory since QueryInterface retains it.
249 err
= factory
->QueryInterface( inIID
, outResult
);
256 //===========================================================================================================================
258 //===========================================================================================================================
260 STDAPI
DllRegisterServer( void )
262 IRunnableTask
* pTask
= NULL
;
267 dlog( kDebugLevelTrace
, "DllRegisterServer\n" );
269 ok
= s
.LoadString( IDS_NAME
);
270 require_action( ok
, exit
, err
= E_UNEXPECTED
);
272 err
= RegisterServer( gInstance
, CLSID_ExplorerBar
, s
);
273 require_noerr( err
, exit
);
275 err
= RegisterCOMCategory( CLSID_ExplorerBar
, CATID_InfoBand
, TRUE
);
276 require_noerr( err
, exit
);
278 // <rdar://problem/4130635> Clear IE cache so it will rebuild the cache when it runs next. This
279 // will allow us to install and not reboot
281 err
= CoCreateInstance(CLSID_CompCatCacheDaemon
, NULL
, CLSCTX_INPROC
, IID_IRunnableTask
, (void**) &pTask
);
282 require_noerr( err
, exit
);
291 //===========================================================================================================================
292 // DllUnregisterServer
293 //===========================================================================================================================
295 STDAPI
DllUnregisterServer( void )
299 dlog( kDebugLevelTrace
, "DllUnregisterServer\n" );
301 err
= RegisterCOMCategory( CLSID_ExplorerBar
, CATID_InfoBand
, FALSE
);
302 require_noerr( err
, exit
);
304 err
= UnregisterServer( CLSID_ExplorerBar
);
305 require_noerr( err
, exit
);
313 #pragma mark == MFC Support ==
316 //===========================================================================================================================
317 // MFCDLLProcessAttach
318 //===========================================================================================================================
320 DEBUG_LOCAL OSStatus
MFCDLLProcessAttach( HINSTANCE inInstance
)
322 wchar_t resource
[MAX_PATH
];
324 _AFX_THREAD_STATE
* threadState
;
325 AFX_MODULE_STATE
* previousModuleState
;
332 // Simulate what is done in dllmodul.cpp.
334 threadState
= AfxGetThreadState();
335 check( threadState
);
336 previousModuleState
= threadState
->m_pPrevModuleState
;
338 ok
= AfxWinInit( inInstance
, NULL
, TEXT( "" ), 0 );
339 require_action( ok
, exit
, err
= kUnknownErr
);
342 require_action( ok
, exit
, err
= kNotInitializedErr
);
344 // Before we load the resources, let's load the error string
346 // errorMessage.LoadString( IDS_REINSTALL );
347 // errorCaption.LoadString( IDS_REINSTALL_CAPTION );
351 res
= PathForResource( inInstance
, L
"ExplorerPluginResources.dll", resource
, MAX_PATH
);
353 err
= translate_errno( res
!= 0, kUnknownErr
, kUnknownErr
);
354 require_noerr( err
, exit
);
356 g_nonLocalizedResources
= LoadLibrary( resource
);
357 translate_errno( g_nonLocalizedResources
, GetLastError(), kUnknownErr
);
358 require_noerr( err
, exit
);
360 g_nonLocalizedResourcesName
= resource
;
362 res
= PathForResource( inInstance
, L
"ExplorerPluginLocalized.dll", resource
, MAX_PATH
);
363 err
= translate_errno( res
!= 0, kUnknownErr
, kUnknownErr
);
364 require_noerr( err
, exit
);
366 g_localizedResources
= LoadLibrary( resource
);
367 translate_errno( g_localizedResources
, GetLastError(), kUnknownErr
);
368 require_noerr( err
, exit
);
370 AfxSetResourceHandle( g_localizedResources
);
372 ok
= app
->InitInstance();
373 require_action( ok
, exit
, err
= kUnknownErr
);
375 threadState
->m_pPrevModuleState
= previousModuleState
;
377 AfxInitLocalData( inInstance
);
391 threadState
->m_pPrevModuleState
= previousModuleState
;
396 //===========================================================================================================================
397 // MFCDLLProcessDetach
398 //===========================================================================================================================
400 DEBUG_LOCAL
void MFCDLLProcessDetach( HINSTANCE inInstance
)
404 // Simulate what is done in dllmodul.cpp.
413 if( AfxGetModuleThreadState()->m_nTempMapLock
!= 0 )
415 dlog( kDebugLevelWarning
, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock
);
420 AfxUnlockTempMaps( -1 );
422 // Terminate the library before destructors are called.
425 AfxTermLocalData( inInstance
, TRUE
);
428 //===========================================================================================================================
430 //===========================================================================================================================
432 DEBUG_LOCAL
void MFCDLLThreadDetach( HINSTANCE inInstance
)
434 // Simulate what is done in dllmodul.cpp.
437 if( AfxGetModuleThreadState()->m_nTempMapLock
!= 0 )
439 dlog( kDebugLevelWarning
, "Warning: Temp map lock count non-zero (%ld).\n", AfxGetModuleThreadState()->m_nTempMapLock
);
444 AfxUnlockTempMaps( -1 );
445 AfxTermThread( inInstance
);
450 #pragma mark == Utilities ==
453 //===========================================================================================================================
455 //===========================================================================================================================
457 DEBUG_LOCAL OSStatus
RegisterServer( HINSTANCE inInstance
, CLSID inCLSID
, LPCTSTR inName
)
459 typedef struct RegistryBuilder RegistryBuilder
;
460 struct RegistryBuilder
469 LPWSTR clsidWideString
;
470 TCHAR clsidString
[ 64 ];
475 TCHAR keyName
[ MAX_PATH
];
476 TCHAR moduleName
[ MAX_PATH
] = TEXT( "" );
477 TCHAR data
[ MAX_PATH
];
478 RegistryBuilder entries
[] =
480 { HKEY_CLASSES_ROOT
, TEXT( "CLSID\\%s" ), NULL
, inName
},
481 { HKEY_CLASSES_ROOT
, TEXT( "CLSID\\%s\\InprocServer32" ), NULL
, moduleName
},
482 { HKEY_CLASSES_ROOT
, TEXT( "CLSID\\%s\\InprocServer32" ), TEXT( "ThreadingModel" ), TEXT( "Apartment" ) }
485 OSVERSIONINFO versionInfo
;
487 // Convert the CLSID to a string based on the encoding of this code (ANSI or Unicode).
489 err
= StringFromIID( inCLSID
, &clsidWideString
);
490 require_noerr( err
, exit
);
491 require_action( clsidWideString
, exit
, err
= kNoMemoryErr
);
494 lstrcpyn( clsidString
, clsidWideString
, sizeof_array( clsidString
) );
495 CoTaskMemFree( clsidWideString
);
497 nChars
= WideCharToMultiByte( CP_ACP
, 0, clsidWideString
, -1, clsidString
, sizeof_array( clsidString
), NULL
, NULL
);
498 err
= translate_errno( nChars
> 0, (OSStatus
) GetLastError(), kUnknownErr
);
499 CoTaskMemFree( clsidWideString
);
500 require_noerr( err
, exit
);
503 // Register the CLSID entries.
505 nChars
= GetModuleFileName( inInstance
, moduleName
, sizeof_array( moduleName
) );
506 err
= translate_errno( nChars
> 0, (OSStatus
) GetLastError(), kUnknownErr
);
507 require_noerr( err
, exit
);
509 n
= sizeof_array( entries
);
510 for( i
= 0; i
< n
; ++i
)
512 wsprintf( keyName
, entries
[ i
].subKey
, clsidString
);
513 err
= RegCreateKeyEx( entries
[ i
].rootKey
, keyName
, 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_WRITE
, NULL
, &key
, NULL
);
514 require_noerr( err
, exit
);
516 size
= (DWORD
)( ( lstrlen( entries
[ i
].data
) + 1 ) * sizeof( TCHAR
) );
517 err
= RegSetValueEx( key
, entries
[ i
].valueName
, 0, REG_SZ
, (LPBYTE
) entries
[ i
].data
, size
);
519 require_noerr( err
, exit
);
522 // If running on NT, register the extension as approved.
524 versionInfo
.dwOSVersionInfoSize
= sizeof( versionInfo
);
525 GetVersionEx( &versionInfo
);
526 if( versionInfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
)
528 lstrcpyn( keyName
, TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved" ), sizeof_array( keyName
) );
529 err
= RegCreateKeyEx( HKEY_LOCAL_MACHINE
, keyName
, 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_WRITE
, NULL
, &key
, NULL
);
530 require_noerr( err
, exit
);
532 lstrcpyn( data
, inName
, sizeof_array( data
) );
533 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
534 err
= RegSetValueEx( key
, clsidString
, 0, REG_SZ
, (LPBYTE
) data
, size
);
538 // register toolbar button
539 lstrcpyn( keyName
, TEXT( "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\{7F9DB11C-E358-4ca6-A83D-ACC663939424}"), sizeof_array( keyName
) );
540 err
= RegCreateKeyEx( HKEY_LOCAL_MACHINE
, keyName
, 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_WRITE
, NULL
, &key
, NULL
);
541 require_noerr( err
, exit
);
543 lstrcpyn( data
, L
"Yes", sizeof_array( data
) );
544 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
545 RegSetValueEx( key
, L
"Default Visible", 0, REG_SZ
, (LPBYTE
) data
, size
);
547 lstrcpyn( data
, inName
, sizeof_array( data
) );
548 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
549 RegSetValueEx( key
, L
"ButtonText", 0, REG_SZ
, (LPBYTE
) data
, size
);
551 lstrcpyn( data
, L
"{E0DD6CAB-2D10-11D2-8F1A-0000F87ABD16}", sizeof_array( data
) );
552 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
553 RegSetValueEx( key
, L
"CLSID", 0, REG_SZ
, (LPBYTE
) data
, size
);
555 lstrcpyn( data
, clsidString
, sizeof_array( data
) );
556 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
557 RegSetValueEx( key
, L
"BandCLSID", 0, REG_SZ
, (LPBYTE
) data
, size
);
559 // check if we're running XP or later
560 if ( ( versionInfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) &&
561 ( versionInfo
.dwMajorVersion
== 5 ) &&
562 ( versionInfo
.dwMinorVersion
>= 1 ) )
564 wsprintf( data
, L
"%s,%d", (LPCTSTR
) g_nonLocalizedResourcesName
, IDI_BUTTON_XP
);
565 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
566 RegSetValueEx( key
, L
"Icon", 0, REG_SZ
, (LPBYTE
) data
, size
);
568 wsprintf( data
, L
"%s,%d", (LPCTSTR
) g_nonLocalizedResourcesName
, IDI_BUTTON_XP
);
569 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
570 RegSetValueEx( key
, L
"HotIcon", 0, REG_SZ
, (LPBYTE
) data
, size
);
574 wsprintf( data
, L
"%s,%d", (LPCTSTR
) g_nonLocalizedResourcesName
, IDI_BUTTON_2K
);
575 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
576 RegSetValueEx( key
, L
"Icon", 0, REG_SZ
, (LPBYTE
) data
, size
);
578 wsprintf( data
, L
"%s,%d", (LPCTSTR
) g_nonLocalizedResourcesName
, IDI_BUTTON_2K
);
579 size
= (DWORD
)( ( lstrlen( data
) + 1 ) * sizeof( TCHAR
) );
580 RegSetValueEx( key
, L
"HotIcon", 0, REG_SZ
, (LPBYTE
) data
, size
);
589 //===========================================================================================================================
590 // RegisterCOMCategory
591 //===========================================================================================================================
593 DEBUG_LOCAL OSStatus
RegisterCOMCategory( CLSID inCLSID
, CATID inCategoryID
, BOOL inRegister
)
598 err
= CoInitialize( NULL
);
599 require( SUCCEEDED( err
), exit
);
601 err
= CoCreateInstance( CLSID_StdComponentCategoriesMgr
, NULL
, CLSCTX_INPROC_SERVER
, IID_ICatRegister
, (LPVOID
*) &cat
);
602 check( SUCCEEDED( err
) );
603 if( SUCCEEDED( err
) )
607 err
= cat
->RegisterClassImplCategories( inCLSID
, 1, &inCategoryID
);
612 err
= cat
->UnRegisterClassImplCategories( inCLSID
, 1, &inCategoryID
);
624 //===========================================================================================================================
626 //===========================================================================================================================
628 DEBUG_LOCAL OSStatus
UnregisterServer( CLSID inCLSID
)
631 LPWSTR clsidWideString
;
632 TCHAR clsidString
[ 64 ];
634 TCHAR keyName
[ MAX_PATH
* 2 ];
635 OSVERSIONINFO versionInfo
;
637 // Convert the CLSID to a string based on the encoding of this code (ANSI or Unicode).
639 err
= StringFromIID( inCLSID
, &clsidWideString
);
640 require_noerr( err
, exit
);
641 require_action( clsidWideString
, exit
, err
= kNoMemoryErr
);
644 lstrcpyn( clsidString
, clsidWideString
, sizeof_array( clsidString
) );
645 CoTaskMemFree( clsidWideString
);
647 nChars
= WideCharToMultiByte( CP_ACP
, 0, clsidWideString
, -1, clsidString
, sizeof_array( clsidString
), NULL
, NULL
);
648 err
= translate_errno( nChars
> 0, (OSStatus
) GetLastError(), kUnknownErr
);
649 CoTaskMemFree( clsidWideString
);
650 require_noerr( err
, exit
);
653 wsprintf( keyName
, L
"CLSID\\%s", clsidString
);
654 MyRegDeleteKey( HKEY_CLASSES_ROOT
, keyName
);
656 // If running on NT, de-register the extension as approved.
658 versionInfo
.dwOSVersionInfoSize
= sizeof( versionInfo
);
659 GetVersionEx( &versionInfo
);
660 if( versionInfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
)
662 lstrcpyn( keyName
, TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved" ), sizeof_array( keyName
) );
663 err
= RegCreateKeyEx( HKEY_LOCAL_MACHINE
, keyName
, 0, NULL
, REG_OPTION_NON_VOLATILE
, KEY_WRITE
, NULL
, &key
, NULL
);
664 require_noerr( err
, exit
);
666 RegDeleteValue( key
, clsidString
);
668 err
= RegCloseKey( key
);
669 require_noerr( err
, exit
);
672 // de-register toolbar button
674 lstrcpyn( keyName
, TEXT( "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\{7F9DB11C-E358-4ca6-A83D-ACC663939424}"), sizeof_array( keyName
) );
675 MyRegDeleteKey( HKEY_LOCAL_MACHINE
, keyName
);
683 //===========================================================================================================================
685 //===========================================================================================================================
687 DEBUG_LOCAL OSStatus
MyRegDeleteKey( HKEY hKeyRoot
, LPTSTR lpSubKey
)
692 TCHAR szName
[MAX_PATH
];
696 // First, see if we can delete the key without having to recurse.
698 err
= RegDeleteKey( hKeyRoot
, lpSubKey
);
705 err
= RegOpenKeyEx( hKeyRoot
, lpSubKey
, 0, KEY_READ
, &hKey
);
706 require_noerr( err
, exit
);
708 // Check for an ending slash and add one if it is missing.
710 lpEnd
= lpSubKey
+ lstrlen(lpSubKey
);
712 if ( *( lpEnd
- 1 ) != TEXT( '\\' ) )
719 // Enumerate the keys
722 err
= RegEnumKeyEx(hKey
, 0, szName
, &dwSize
, NULL
, NULL
, NULL
, &ftWrite
);
728 lstrcpy (lpEnd
, szName
);
730 if ( !MyRegDeleteKey( hKeyRoot
, lpSubKey
) )
737 err
= RegEnumKeyEx( hKey
, 0, szName
, &dwSize
, NULL
, NULL
, NULL
, &ftWrite
);
748 // Try again to delete the key.
750 err
= RegDeleteKey(hKeyRoot
, lpSubKey
);
751 require_noerr( err
, exit
);