1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include	"StdAfx.h"
19
20// The following 2 includes have to be in this order and INITGUID must be defined here, before including the file
21// that specifies the GUID(s), and nowhere else. The reason for this is that initguid.h doesn't provide separate
22// define and declare macros for GUIDs so you have to #define INITGUID in the single file where you want to define
23// your GUID then in all the other files that just need the GUID declared, INITGUID must not be defined.
24
25#define	INITGUID
26#include	<initguid.h>
27#include	"ExplorerPlugin.h"
28
29#include	<comcat.h>
30#include	<Shlwapi.h>
31
32#include	"CommonServices.h"
33#include	"DebugServices.h"
34
35#include	"ClassFactory.h"
36#include	"Resource.h"
37
38#include	"loclibrary.h"
39
40// MFC Debugging
41
42#ifdef _DEBUG
43#define new DEBUG_NEW
44#undef THIS_FILE
45static char THIS_FILE[] = __FILE__;
46#endif
47
48#if 0
49#pragma mark == Prototypes ==
50#endif
51
52//===========================================================================================================================
53//	Prototypes
54//===========================================================================================================================
55
56// Utilities
57
58DEBUG_LOCAL OSStatus	RegisterServer( HINSTANCE inInstance, CLSID inCLSID, LPCTSTR inName );
59DEBUG_LOCAL OSStatus	RegisterCOMCategory( CLSID inCLSID, CATID inCategoryID, BOOL inRegister );
60DEBUG_LOCAL OSStatus	UnregisterServer( CLSID inCLSID );
61DEBUG_LOCAL OSStatus	MyRegDeleteKey( HKEY hKeyRoot, LPTSTR lpSubKey );
62
63// Stash away pointers to our resource DLLs
64
65static HINSTANCE g_nonLocalizedResources	= NULL;
66static CString	 g_nonLocalizedResourcesName;
67static HINSTANCE g_localizedResources		= NULL;
68
69HINSTANCE
70GetNonLocalizedResources()
71{
72	return g_nonLocalizedResources;
73}
74
75HINSTANCE
76GetLocalizedResources()
77{
78	return g_localizedResources;
79}
80
81// This is the class GUID for an undocumented hook into IE that will allow us to register
82// and have IE notice our new ExplorerBar without rebooting.
83// {8C7461EF-2B13-11d2-BE35-3078302C2030}
84
85DEFINE_GUID(CLSID_CompCatCacheDaemon,
860x8C7461EF, 0x2b13, 0x11d2, 0xbe, 0x35, 0x30, 0x78, 0x30, 0x2c, 0x20, 0x30);
87
88
89#if 0
90#pragma mark == Globals ==
91#endif
92
93//===========================================================================================================================
94//	Globals
95//===========================================================================================================================
96
97HINSTANCE			gInstance		= NULL;
98int					gDLLRefCount	= 0;
99CExplorerPluginApp	gApp;
100
101#if 0
102#pragma mark -
103#pragma mark == DLL Exports ==
104#endif
105
106//===========================================================================================================================
107//	CExplorerPluginApp::CExplorerPluginApp
108//===========================================================================================================================
109
110IMPLEMENT_DYNAMIC(CExplorerPluginApp, CWinApp);
111
112CExplorerPluginApp::CExplorerPluginApp()
113{
114}
115
116
117//===========================================================================================================================
118//	CExplorerPluginApp::~CExplorerPluginApp
119//===========================================================================================================================
120
121CExplorerPluginApp::~CExplorerPluginApp()
122{
123}
124
125
126//===========================================================================================================================
127//	CExplorerPluginApp::InitInstance
128//===========================================================================================================================
129
130BOOL
131CExplorerPluginApp::InitInstance()
132{
133	wchar_t					resource[MAX_PATH];
134	OSStatus				err;
135	int						res;
136	HINSTANCE inInstance;
137
138	inInstance = AfxGetInstanceHandle();
139	gInstance = inInstance;
140
141	debug_initialize( kDebugOutputTypeWindowsEventLog, "DNSServices Bar", inInstance );
142	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace );
143	dlog( kDebugLevelTrace, "\nCCPApp::InitInstance\n" );
144
145	res = PathForResource( inInstance, L"ExplorerPluginResources.dll", resource, MAX_PATH );
146
147	err = translate_errno( res != 0, kUnknownErr, kUnknownErr );
148	require_noerr( err, exit );
149
150	g_nonLocalizedResources = LoadLibrary( resource );
151	translate_errno( g_nonLocalizedResources, GetLastError(), kUnknownErr );
152	require_noerr( err, exit );
153
154	g_nonLocalizedResourcesName = resource;
155
156	res = PathForResource( inInstance, L"ExplorerPluginLocalized.dll", resource, MAX_PATH );
157	err = translate_errno( res != 0, kUnknownErr, kUnknownErr );
158	require_noerr( err, exit );
159
160	g_localizedResources = LoadLibrary( resource );
161	translate_errno( g_localizedResources, GetLastError(), kUnknownErr );
162	require_noerr( err, exit );
163
164	AfxSetResourceHandle( g_localizedResources );
165
166exit:
167
168	return TRUE;
169}
170
171
172//===========================================================================================================================
173//	CExplorerPluginApp::ExitInstance
174//===========================================================================================================================
175
176int
177CExplorerPluginApp::ExitInstance()
178{
179	return 0;
180}
181
182
183
184//===========================================================================================================================
185//	DllCanUnloadNow
186//===========================================================================================================================
187
188STDAPI	DllCanUnloadNow( void )
189{
190	dlog( kDebugLevelTrace, "DllCanUnloadNow (refCount=%d)\n", gDLLRefCount );
191
192	return( gDLLRefCount == 0 );
193}
194
195//===========================================================================================================================
196//	DllGetClassObject
197//===========================================================================================================================
198
199STDAPI	DllGetClassObject( REFCLSID inCLSID, REFIID inIID, LPVOID *outResult )
200{
201	HRESULT				err;
202	BOOL				ok;
203	ClassFactory *		factory;
204
205	dlog( kDebugLevelTrace, "DllGetClassObject\n" );
206
207	*outResult = NULL;
208
209	// Check if the class ID is supported.
210
211	ok = IsEqualCLSID( inCLSID, CLSID_ExplorerBar );
212	require_action_quiet( ok, exit, err = CLASS_E_CLASSNOTAVAILABLE );
213
214	// Create the ClassFactory object.
215
216	factory = NULL;
217	try
218	{
219		factory = new ClassFactory( inCLSID );
220	}
221	catch( ... )
222	{
223		// Do not let exception escape.
224	}
225	require_action( factory, exit, err = E_OUTOFMEMORY );
226
227	// Query for the specified interface. Release the factory since QueryInterface retains it.
228
229	err = factory->QueryInterface( inIID, outResult );
230	factory->Release();
231
232exit:
233	return( err );
234}
235
236//===========================================================================================================================
237//	DllRegisterServer
238//===========================================================================================================================
239
240STDAPI	DllRegisterServer( void )
241{
242	IRunnableTask * pTask = NULL;
243	HRESULT			err;
244	BOOL			ok;
245	CString			s;
246
247	dlog( kDebugLevelTrace, "DllRegisterServer\n" );
248
249	ok = s.LoadString( IDS_NAME );
250	require_action( ok, exit, err = E_UNEXPECTED );
251
252	err = RegisterServer( gInstance, CLSID_ExplorerBar, s );
253	require_noerr( err, exit );
254
255	err = RegisterCOMCategory( CLSID_ExplorerBar, CATID_InfoBand, TRUE );
256	require_noerr( err, exit );
257
258	// <rdar://problem/4130635> Clear IE cache so it will rebuild the cache when it runs next.  This
259	// will allow us to install and not reboot
260
261	err = CoCreateInstance(CLSID_CompCatCacheDaemon, NULL, CLSCTX_INPROC, IID_IRunnableTask, (void**) &pTask);
262	require_noerr( err, exit );
263
264	pTask->Run();
265	pTask->Release();
266
267exit:
268	return( err );
269}
270
271//===========================================================================================================================
272//	DllUnregisterServer
273//===========================================================================================================================
274
275STDAPI	DllUnregisterServer( void )
276{
277	HRESULT		err;
278
279	dlog( kDebugLevelTrace, "DllUnregisterServer\n" );
280
281	err = RegisterCOMCategory( CLSID_ExplorerBar, CATID_InfoBand, FALSE );
282	require_noerr( err, exit );
283
284	err = UnregisterServer( CLSID_ExplorerBar );
285	require_noerr( err, exit );
286
287exit:
288	return( err );
289}
290
291
292#if 0
293#pragma mark -
294#pragma mark == Utilities ==
295#endif
296
297//===========================================================================================================================
298//	RegisterServer
299//===========================================================================================================================
300
301DEBUG_LOCAL OSStatus	RegisterServer( HINSTANCE inInstance, CLSID inCLSID, LPCTSTR inName )
302{
303	typedef struct	RegistryBuilder		RegistryBuilder;
304	struct	RegistryBuilder
305	{
306		HKEY		rootKey;
307		LPCTSTR		subKey;
308		LPCTSTR		valueName;
309		LPCTSTR		data;
310	};
311
312	OSStatus			err;
313	LPWSTR				clsidWideString;
314	TCHAR				clsidString[ 64 ];
315	DWORD				nChars;
316	size_t				n;
317	size_t				i;
318	HKEY				key;
319	TCHAR				keyName[ MAX_PATH ];
320	TCHAR				moduleName[ MAX_PATH ] = TEXT( "" );
321	TCHAR				data[ MAX_PATH ];
322	RegistryBuilder		entries[] =
323	{
324		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s" ),					NULL,						inName },
325		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s\\InprocServer32" ),	NULL,						moduleName },
326		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s\\InprocServer32" ),  	TEXT( "ThreadingModel" ),	TEXT( "Apartment" ) }
327	};
328	DWORD				size;
329	OSVERSIONINFO		versionInfo;
330
331	// Convert the CLSID to a string based on the encoding of this code (ANSI or Unicode).
332
333	err = StringFromIID( inCLSID, &clsidWideString );
334	require_noerr( err, exit );
335	require_action( clsidWideString, exit, err = kNoMemoryErr );
336
337	#ifdef UNICODE
338		lstrcpyn( clsidString, clsidWideString, sizeof_array( clsidString ) );
339		CoTaskMemFree( clsidWideString );
340	#else
341		nChars = WideCharToMultiByte( CP_ACP, 0, clsidWideString, -1, clsidString, sizeof_array( clsidString ), NULL, NULL );
342		err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr );
343		CoTaskMemFree( clsidWideString );
344		require_noerr( err, exit );
345	#endif
346
347	// Register the CLSID entries.
348
349	nChars = GetModuleFileName( inInstance, moduleName, sizeof_array( moduleName ) );
350	err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr );
351	require_noerr( err, exit );
352
353	n = sizeof_array( entries );
354	for( i = 0; i < n; ++i )
355	{
356		wsprintf( keyName, entries[ i ].subKey, clsidString );
357		err = RegCreateKeyEx( entries[ i ].rootKey, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL );
358		require_noerr( err, exit );
359
360		size = (DWORD)( ( lstrlen( entries[ i ].data ) + 1 ) * sizeof( TCHAR ) );
361		err = RegSetValueEx( key, entries[ i ].valueName, 0, REG_SZ, (LPBYTE) entries[ i ].data, size );
362		RegCloseKey( key );
363		require_noerr( err, exit );
364	}
365
366	// If running on NT, register the extension as approved.
367
368	versionInfo.dwOSVersionInfoSize = sizeof( versionInfo );
369	GetVersionEx( &versionInfo );
370	if( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
371	{
372		lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved" ), sizeof_array( keyName ) );
373		err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL );
374		require_noerr( err, exit );
375
376		lstrcpyn( data, inName, sizeof_array( data ) );
377		size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
378		err = RegSetValueEx( key, clsidString, 0, REG_SZ, (LPBYTE) data, size );
379		RegCloseKey( key );
380	}
381
382	// register toolbar button
383	lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\{7F9DB11C-E358-4ca6-A83D-ACC663939424}"), sizeof_array( keyName ) );
384	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL );
385	require_noerr( err, exit );
386
387	lstrcpyn( data, L"Yes", sizeof_array( data ) );
388	size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
389	RegSetValueEx( key, L"Default Visible", 0, REG_SZ, (LPBYTE) data, size );
390
391	lstrcpyn( data, inName, sizeof_array( data ) );
392	size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
393	RegSetValueEx( key, L"ButtonText", 0, REG_SZ, (LPBYTE) data, size );
394
395	lstrcpyn( data, L"{E0DD6CAB-2D10-11D2-8F1A-0000F87ABD16}", sizeof_array( data ) );
396	size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
397	RegSetValueEx( key, L"CLSID", 0, REG_SZ, (LPBYTE) data, size );
398
399	lstrcpyn( data, clsidString, sizeof_array( data ) );
400	size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
401	RegSetValueEx( key, L"BandCLSID", 0, REG_SZ, (LPBYTE) data, size );
402
403	// check if we're running XP or later
404	if ( ( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ) &&
405		 ( versionInfo.dwMajorVersion == 5 ) &&
406	     ( versionInfo.dwMinorVersion >= 1 ) )
407	{
408		wsprintf( data, L"%s,%d", (LPCTSTR) g_nonLocalizedResourcesName, IDI_BUTTON_XP );
409		size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
410		RegSetValueEx( key, L"Icon", 0, REG_SZ, (LPBYTE) data, size);
411
412		wsprintf( data, L"%s,%d", (LPCTSTR) g_nonLocalizedResourcesName, IDI_BUTTON_XP );
413		size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
414		RegSetValueEx( key, L"HotIcon", 0, REG_SZ, (LPBYTE) data, size);
415	}
416	else
417	{
418		wsprintf( data, L"%s,%d", (LPCTSTR) g_nonLocalizedResourcesName, IDI_BUTTON_2K );
419		size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
420		RegSetValueEx( key, L"Icon", 0, REG_SZ, (LPBYTE) data, size);
421
422		wsprintf( data, L"%s,%d", (LPCTSTR) g_nonLocalizedResourcesName, IDI_BUTTON_2K );
423		size = (DWORD)( ( lstrlen( data ) + 1 ) * sizeof( TCHAR ) );
424		RegSetValueEx( key, L"HotIcon", 0, REG_SZ, (LPBYTE) data, size);
425	}
426
427	RegCloseKey( key );
428
429exit:
430	return( err );
431}
432
433//===========================================================================================================================
434//	RegisterCOMCategory
435//===========================================================================================================================
436
437DEBUG_LOCAL OSStatus	RegisterCOMCategory( CLSID inCLSID, CATID inCategoryID, BOOL inRegister )
438{
439	HRESULT				err;
440	ICatRegister *		cat;
441
442	err = CoInitialize( NULL );
443	require( SUCCEEDED( err ), exit );
444
445	err = CoCreateInstance( CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (LPVOID *) &cat );
446	check( SUCCEEDED( err ) );
447	if( SUCCEEDED( err ) )
448	{
449		if( inRegister )
450		{
451			err = cat->RegisterClassImplCategories( inCLSID, 1, &inCategoryID );
452			check_noerr( err );
453		}
454		else
455		{
456			err = cat->UnRegisterClassImplCategories( inCLSID, 1, &inCategoryID );
457			check_noerr( err );
458		}
459		cat->Release();
460	}
461	CoUninitialize();
462
463exit:
464	return( err );
465}
466
467
468//===========================================================================================================================
469//	UnregisterServer
470//===========================================================================================================================
471
472DEBUG_LOCAL OSStatus	UnregisterServer( CLSID inCLSID )
473{
474	OSStatus			err = 0;
475	LPWSTR				clsidWideString;
476	TCHAR				clsidString[ 64 ];
477	HKEY				key;
478	TCHAR				keyName[ MAX_PATH * 2 ];
479	OSVERSIONINFO		versionInfo;
480
481	// Convert the CLSID to a string based on the encoding of this code (ANSI or Unicode).
482
483	err = StringFromIID( inCLSID, &clsidWideString );
484	require_noerr( err, exit );
485	require_action( clsidWideString, exit, err = kNoMemoryErr );
486
487	#ifdef UNICODE
488		lstrcpyn( clsidString, clsidWideString, sizeof_array( clsidString ) );
489		CoTaskMemFree( clsidWideString );
490	#else
491		nChars = WideCharToMultiByte( CP_ACP, 0, clsidWideString, -1, clsidString, sizeof_array( clsidString ), NULL, NULL );
492		err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr );
493		CoTaskMemFree( clsidWideString );
494		require_noerr( err, exit );
495	#endif
496
497	wsprintf( keyName, L"CLSID\\%s", clsidString );
498	MyRegDeleteKey( HKEY_CLASSES_ROOT, keyName );
499
500	// If running on NT, de-register the extension as approved.
501
502	versionInfo.dwOSVersionInfoSize = sizeof( versionInfo );
503	GetVersionEx( &versionInfo );
504	if( versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT )
505	{
506		lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved" ), sizeof_array( keyName ) );
507		err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL );
508		require_noerr( err, exit );
509
510		RegDeleteValue( key, clsidString );
511
512		err = RegCloseKey( key );
513		require_noerr( err, exit );
514	}
515
516	// de-register toolbar button
517
518	lstrcpyn( keyName, TEXT( "SOFTWARE\\Microsoft\\Internet Explorer\\Extensions\\{7F9DB11C-E358-4ca6-A83D-ACC663939424}"), sizeof_array( keyName ) );
519	MyRegDeleteKey( HKEY_LOCAL_MACHINE, keyName );
520
521exit:
522	return( err );
523}
524
525
526
527//===========================================================================================================================
528//	MyRegDeleteKey
529//===========================================================================================================================
530
531DEBUG_LOCAL OSStatus MyRegDeleteKey( HKEY hKeyRoot, LPTSTR lpSubKey )
532{
533    LPTSTR lpEnd;
534    OSStatus err;
535    DWORD dwSize;
536    TCHAR szName[MAX_PATH];
537    HKEY hKey;
538    FILETIME ftWrite;
539
540    // First, see if we can delete the key without having to recurse.
541
542    err = RegDeleteKey( hKeyRoot, lpSubKey );
543
544    if ( !err )
545	{
546		goto exit;
547	}
548
549    err = RegOpenKeyEx( hKeyRoot, lpSubKey, 0, KEY_READ, &hKey );
550	require_noerr( err, exit );
551
552    // Check for an ending slash and add one if it is missing.
553
554    lpEnd = lpSubKey + lstrlen(lpSubKey);
555
556    if ( *( lpEnd - 1 ) != TEXT( '\\' ) )
557    {
558        *lpEnd =  TEXT('\\');
559        lpEnd++;
560        *lpEnd =  TEXT('\0');
561    }
562
563    // Enumerate the keys
564
565    dwSize = MAX_PATH;
566    err = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite);
567
568    if ( !err )
569    {
570        do
571		{
572            lstrcpy (lpEnd, szName);
573
574            if ( !MyRegDeleteKey( hKeyRoot, lpSubKey ) )
575			{
576                break;
577            }
578
579            dwSize = MAX_PATH;
580
581            err = RegEnumKeyEx( hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite );
582
583        }
584		while ( !err );
585    }
586
587    lpEnd--;
588    *lpEnd = TEXT('\0');
589
590    RegCloseKey( hKey );
591
592    // Try again to delete the key.
593
594    err = RegDeleteKey(hKeyRoot, lpSubKey);
595	require_noerr( err, exit );
596
597exit:
598
599	return err;
600}
601