|
|
//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1993-1994
//
// File: init.c
//
// This file contains the library entry points
//
// Usage and assumptions used in this DLL.
//
// 1) Message crackers are used. See windowsx.h and windowsx.txt.
//
// 2) Many functions are considered "member functions" of a
// particular class. Because this is not C++, the function
// names follow a special naming convention: "Class_Name".
// In addition, it is common practice that the first
// argument for these type of functions is a "this" pointer
// to the particular object.
//
// History:
// 08-06-93 ScottH Transferred from twin code
//
//---------------------------------------------------------------------------
///////////////////////////////////////////////////// INCLUDES
#include "brfprv.h" // common headers
#include <brfcasep.h>
#define INITGUID // Initialize GUIDs
#include <initguid.h>
#include <oleguid.h>
#include <coguid.h>
#include <shlguid.h>
#include <shguidp.h> // Contains CLSID_Briefcase
#include "res.h"
#include "recact.h"
#ifdef DEBUG
#include <debugstr.h>
#endif
//---------------------------------------------------------------------------
// Per instance data
//---------------------------------------------------------------------------
HINSTANCE g_hinst = 0; TWINRESULT g_tr = TR_SUCCESS;
// Debugging variables
UINT g_uBreakFlags = 0; // Controls when to int 3
UINT g_uTraceFlags = 0; // Controls what trace messages are spewed
UINT g_uDumpFlags = 0; // Controls what structs get dumped
// The delay mutex and the cs that protects the cRef is per-instance
HANDLE g_hMutexDelay = NULL; static UINT g_cRefMutex = 0; static CRITICAL_SECTION s_csDelay = { 0 };
//---------------------------------------------------------------------------
// Global data
//---------------------------------------------------------------------------
CRITICAL_SECTION g_csSyncUI = { 0 }; DEBUG_CODE( UINT g_cRefSyncUI = 0; ) UINT g_cfBriefObj = 0; BOOL g_bMirroredOS = FALSE; // Use the helper macros in brfprv.h
UINT g_cBusyRef = 0; // Semaphore
UINT g_cBriefRef = 0; // Semaphore
// Metrics
int g_cxIconSpacing = 0; int g_cyIconSpacing = 0; int g_cxBorder = 0; int g_cyBorder = 0; int g_cxIcon = 0; int g_cyIcon = 0; int g_cxIconMargin = 0; int g_cyIconMargin = 0; int g_cxLabelMargin = 0; int g_cyLabelSpace = 0; int g_cxMargin = 0;
// System colors
COLORREF g_clrHighlightText = 0; COLORREF g_clrHighlight = 0; COLORREF g_clrWindowText = 0; COLORREF g_clrWindow = 0;
HBRUSH g_hbrHighlight = 0; HBRUSH g_hbrWindow = 0;
// Strings
TCHAR g_szDBName[MAXPATHLEN]; TCHAR g_szDBNameShort[MAXPATHLEN];
// Get the system metrics we need
void PRIVATE GetMetrics(WPARAM wParam) // wParam from WM_WININICHANGE
{ if ((wParam == 0) || (wParam == SPI_SETNONCLIENTMETRICS)) { g_cxIconSpacing = GetSystemMetrics( SM_CXICONSPACING ); g_cyIconSpacing = GetSystemMetrics( SM_CYICONSPACING );
g_cxBorder = GetSystemMetrics(SM_CXBORDER); g_cyBorder = GetSystemMetrics(SM_CYBORDER);
g_cxIcon = GetSystemMetrics(SM_CXICON); g_cyIcon = GetSystemMetrics(SM_CYICON);
g_cxIconMargin = g_cxBorder * 8; g_cyIconMargin = g_cyBorder * 2; g_cyLabelSpace = g_cyIconMargin + (g_cyBorder * 2); g_cxLabelMargin = (g_cxBorder * 2); g_cxMargin = g_cxBorder * 5; } }
// Initializes colors
void PRIVATE InitGlobalColors() { g_clrWindowText = GetSysColor(COLOR_WINDOWTEXT); g_clrWindow = GetSysColor(COLOR_WINDOW); g_clrHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT); g_clrHighlight = GetSysColor(COLOR_HIGHLIGHT);
g_hbrWindow = GetSysColorBrush(COLOR_WINDOW); g_hbrHighlight = GetSysColorBrush(COLOR_HIGHLIGHT); }
// Initialize global strings
void PRIVATE InitGlobalStrings() { SzFromIDS(IDS_BC_DATABASE, g_szDBName, ARRAYSIZE(g_szDBName)); SzFromIDS(IDS_BC_DATABASE_SHORT, g_szDBNameShort, ARRAYSIZE(g_szDBNameShort)); }
// Initialize the DLL on the first PROCESS_ATTACH
BOOL PRIVATE InitializeFirstTime(void) { BOOL bRet = FALSE;
InitCommonControls();
GetMetrics(0);
CPATH_InitCS(); CBS_InitCS(); CRL_InitCS();
if (!Atom_Init()) goto Init_Cleanup;
if (!CPATH_Init()) goto Init_Cleanup;
// We do not load the engine DLL until we really need it.
// Initialize our global imagelist
//
g_cfBriefObj = RegisterClipboardFormat(CFSTR_BRIEFOBJECT); if (g_cfBriefObj == 0) goto Init_Cleanup;
bRet = TRUE;
Init_Cleanup: if (bRet == FALSE) { Atom_Term(); }
return bRet; }
// Register window classes per process
BOOL PRIVATE InitWindowClasses(HINSTANCE hinst) { return RecAct_Init(hinst); }
// Terminate DLL on the last PROCESS_DETACH
void PRIVATE FinalTerminate(HINSTANCE hinst) { CPATH_Term(); Atom_Term();
CRL_DeleteCS(); CBS_DeleteCS(); CPATH_DeleteCS();
Mem_Terminate(); }
// Unregister window classes per process
void PRIVATE TermWindowClasses(HINSTANCE hinst) { RecAct_Term(hinst); }
// Purpose: Obtain ownership of the delay-calculation mutex
// Returns: reference count
UINT PUBLIC Delay_Own(void) { UINT cRef;
EnterCriticalSection(&s_csDelay); { if (0 == g_cRefMutex++) { // Obtain ownership of the mutex. This will get released
// when Delay_Release is called.
LeaveCriticalSection(&s_csDelay); { MsgWaitObjectsSendMessage(1, &g_hMutexDelay, INFINITE); } EnterCriticalSection(&s_csDelay);
TRACE_MSG(TF_GENERAL, TEXT("Set delay mutex")); } cRef = g_cRefMutex; } LeaveCriticalSection(&s_csDelay);
return cRef; }
/*----------------------------------------------------------
Purpose: Release ownership of the delay-calculation mutex
Returns: reference count Cond: -- */ UINT PUBLIC Delay_Release(void) { UINT cRef;
EnterCriticalSection(&s_csDelay); { ASSERT(0 < g_cRefMutex);
if (0 < g_cRefMutex) { if (0 == --g_cRefMutex) { ReleaseMutex(g_hMutexDelay);
TRACE_MSG(TF_GENERAL, TEXT("Release delay mutex")); } } cRef = g_cRefMutex; } LeaveCriticalSection(&s_csDelay);
return cRef; }
void PUBLIC Brief_EnterExclusive(void) { EnterCriticalSection(&g_csSyncUI); #ifdef DEBUG
g_cRefSyncUI++; #endif
}
void PUBLIC Brief_LeaveExclusive(void) { #ifdef DEBUG
g_cRefSyncUI--; #endif
LeaveCriticalSection(&g_csSyncUI); }
BOOL ProcessAttach(HINSTANCE hDll) { DWORD dwLayout = 0; BOOL bSuccess = TRUE;
SHFusionInitializeFromModule(hDll);
bSuccess = InitializeCriticalSectionAndSpinCount(&g_csSyncUI, 0); if (bSuccess) { bSuccess = InitializeCriticalSectionAndSpinCount(&s_csDelay, 0); if (bSuccess) { g_hinst = hDll;
#ifdef DEBUG
// We do this simply to load the debug .ini flags
ProcessIniFile(); DEBUG_BREAK(BF_ONPROCESSATT); #endif
// Under NT, we need to initialize on every process attach, not just the first
bSuccess = InitializeFirstTime();
if (bSuccess) { // security: Changing to unnamed mutex to avoid any possible
// squatting issues. Handle is global and accessible
// across process.
g_hMutexDelay = CreateMutex(NULL, FALSE, NULL); bSuccess = (NULL != g_hMutexDelay); }
if (bSuccess) { // (Only do this if we succeeded above)
//
// Do the following for every process
bSuccess = InitWindowClasses(hDll); }
InitGlobalColors(); InitGlobalStrings();
//Bug 199701, 199647, 199699
//Apparently this is either broke or never worked: g_bMirroredOS = IS_MIRRORING_ENABLED();
GetProcessDefaultLayout(&dwLayout); if (dwLayout == LAYOUT_RTL) { g_bMirroredOS = TRUE; } //End bug 199701, 199647, 199699
} } return bSuccess; }
BOOL ProcessDetach(HINSTANCE hDll) { BOOL bSuccess = TRUE;
ASSERT(hDll == g_hinst);
DEBUG_CODE( DEBUG_BREAK(BF_ONPROCESSDET); )
ASSERT(0 == g_cRefMutex);
if (g_hMutexDelay) { CloseHandle(g_hMutexDelay); g_hMutexDelay = NULL; }
FinalTerminate(g_hinst);
Sync_ReleaseVTable();
DeleteCriticalSection(&g_csSyncUI);
DeleteCriticalSection(&s_csDelay);
TermWindowClasses(hDll);
SHFusionUninitialize();
return bSuccess; }
BOOL APIENTRY LibMain(HANDLE hDll, DWORD dwReason, void *lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: ProcessAttach(hDll); #ifndef DEBUG
DisableThreadLibraryCalls(hDll); #endif
break;
case DLL_PROCESS_DETACH: ProcessDetach(hDll); break;
case DLL_THREAD_ATTACH:
#ifdef DEBUG
// We do this simply to load the debug .ini flags
ProcessIniFile(); #endif
break;
case DLL_THREAD_DETACH:
#ifdef DEBUG
DEBUG_BREAK(BF_ONTHREADDET); #endif
break;
default: break; }
return TRUE; }
/*----------------------------------------------------------
Purpose: Registers property sheet and context menu extensions for the briefcase.
Returns: TRUE on success Cond: -- */ BOOL PRIVATE RegisterShellExtension(void) { const static TCHAR c_szPage[] = STRREG_SHEX_PROPSHEET TEXT("\\BriefcasePage"); const static TCHAR c_szCM[] = STRREG_SHEX_MENUHANDLER TEXT("\\BriefcaseMenu"); const static TCHAR c_szFolder[] = TEXT("Folder"); const static TCHAR c_szStar[] = TEXT("*"); const static TCHAR c_szFmt[] = TEXT("SOFTWARE\\Classes\\%s\\%s"); // This must be per instance, else it will cause a fixup in
// shared data segment.
const static LPCTSTR rgpsz[2] = { c_szFolder, c_szStar }; TCHAR sz[MAXBUFLEN]; int i;
for (i = 0; i < ARRAYSIZE(rgpsz); i++) { // Add briefcase page extension
wnsprintf(sz, ARRAYSIZE(sz), c_szFmt, (LPCTSTR)rgpsz[i], (LPCTSTR)c_szPage); RegSetValue(HKEY_LOCAL_MACHINE, sz, REG_SZ, c_szCLSID, lstrlen(c_szCLSID));
// Add briefcase context menu extension
wnsprintf(sz, ARRAYSIZE(sz), c_szFmt, (LPCTSTR)rgpsz[i], (LPCTSTR)c_szCM); RegSetValue(HKEY_LOCAL_MACHINE, sz, REG_SZ, c_szCLSID, lstrlen(c_szCLSID)); } return TRUE; }
/*----------------------------------------------------------
Purpose: Create a briefcase at the specified location.
Returns: TRUE on success Cond: -- */ BOOL PRIVATE CreateTheBriefcase(HWND hwnd, LPCTSTR pszNewPath) { BOOL bRet = FALSE; TCHAR szParent[MAX_PATH]; TCHAR szTmp[MAX_PATH];
DEBUG_CODE( TRACE_MSG(TF_ALWAYS, TEXT("Creating %s"), (LPCTSTR)pszNewPath); )
// We do not allow briefcases to be created inside other briefcases.
lstrcpyn(szParent, pszNewPath, ARRAYSIZE(szParent)); PathRemoveFileSpec(szParent);
// Is this inside another briefcase?
if (PL_FALSE != PathGetLocality(szParent, szTmp, ARRAYSIZE(szTmp))) { // Yes; don't do it!
MsgBox(hwnd, MAKEINTRESOURCE(IDS_ERR_CREATE_INANOTHER), MAKEINTRESOURCE(IDS_CAP_CREATE), NULL, MB_WARNING); } else if (CreateDirectory(pszNewPath, NULL)) { // Mark the briefcase as a system directory
//
if (!SetFileAttributes(pszNewPath, FILE_ATTRIBUTE_READONLY)) { TRACE_MSG(TF_ALWAYS, TEXT("Cannot make %s a system directory"), (LPCTSTR)pszNewPath); RemoveDirectory(pszNewPath);
MsgBox(hwnd, MAKEINTRESOURCE(IDS_ERR_CANTCREATEBC), MAKEINTRESOURCE(IDS_CAP_CREATE), NULL, MB_ERROR, pszNewPath); } else { const static TCHAR c_szConfirmFileOp[] = TEXT("ConfirmFileOp"); HBRFCASE hbrf; TWINRESULT tr; LPCTSTR pszDBName; DECLAREHOURGLASS;
if (PathsTooLong(pszNewPath, c_szDesktopIni) || PathsTooLong(pszNewPath, g_szDBName) || PathsTooLong(pszNewPath, g_szDBNameShort)) { MsgBox(hwnd, MAKEINTRESOURCE(IDS_ERR_CREATE_TOOLONG), MAKEINTRESOURCE(IDS_CAP_CREATE), NULL, MB_ERROR); } else { // Write in the desktop.ini the briefcase class ID
PathCombine(szTmp, pszNewPath, c_szDesktopIni); // (First, flush the cache to make sure the desktop.ini
// file is really created.)
WritePrivateProfileString(NULL, NULL, NULL, szTmp); WritePrivateProfileString(STRINI_CLASSINFO, c_szIniKeyCLSID, c_szCLSID, szTmp); WritePrivateProfileString(STRINI_CLASSINFO, c_szConfirmFileOp, TEXT("0"), szTmp);
// Make wizard run the first time it is opened.
WritePrivateProfileString(STRINI_CLASSINFO, c_szRunWizard, TEXT("1"), szTmp);
// Hide the desktop.ini since the shell does not selectively hide it.
// Also make it readonly so that the user can't customize the briefcase.
if (!SetFileAttributes(szTmp, FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) { TRACE_MSG(TF_ALWAYS, TEXT("Cannot hide and/or make read-only %s"), (LPCTSTR)szTmp); }
RegisterShellExtension();
PathNotifyShell(pszNewPath, NSE_MKDIR, TRUE);
// Create the database file
SetHourglass();
if (IsLFNDrive(pszNewPath)) pszDBName = g_szDBName; else pszDBName = g_szDBNameShort;
PathCombine(szTmp, pszNewPath, pszDBName); if (Sync_QueryVTable()) { tr = Sync_OpenBriefcase(szTmp, OB_FL_OPEN_DATABASE | OB_FL_TRANSLATE_DB_FOLDER | OB_FL_ALLOW_UI, hwnd, &hbrf); if (TR_SUCCESS == tr) { // (Don't really care about errors here)
Sync_SaveBriefcase(hbrf); Sync_CloseBriefcase(hbrf); } } ResetHourglass();
bRet = TRUE; } } } else { // Could not create the directory. Is it because a briefcase
// already exists at this location?
if (PathExists(pszNewPath)) { // Yes
TRACE_MSG(TF_ALWAYS, TEXT("Briefcase already exists at this location: %s"), (LPCTSTR)pszNewPath); } else { // No
MsgBox(hwnd, MAKEINTRESOURCE(IDS_ERR_CANTCREATEBC), MAKEINTRESOURCE(IDS_CAP_CREATE), NULL, MB_ERROR, pszNewPath); } }
return bRet; }
/*----------------------------------------------------------
Purpose: Adds the briefcase at pszPath to the SendTo folder
Returns: standard result Cond: -- */ HRESULT PRIVATE AddBriefcaseToSendToFolder(HWND hwnd, LPCTSTR pszPath) { HRESULT hres = E_OUTOFMEMORY; TCHAR szSendTo[MAX_PATH];
Shell_GetImageLists( NULL, NULL ); // make sure icon cache is around
if (SHGetSpecialFolderPath(hwnd, szSendTo, CSIDL_SENDTO, FALSE)) { LPITEMIDLIST pidl = ILCreateFromPath(pszPath); if (pidl) { LPITEMIDLIST pidlParent = ILClone(pidl); if (pidlParent) { IShellFolder * psf; IShellFolder * psfDesktop;
ILRemoveLastID(pidlParent);
hres = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hres)) { hres = psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlParent, NULL, &IID_IShellFolder, &psf);
if (SUCCEEDED(hres)) { IDataObject *pdtobj; LPCITEMIDLIST pidlName = ILFindLastID(pidl);
hres = psf->lpVtbl->GetUIObjectOf(psf, hwnd, 1, &pidlName, &IID_IDataObject, NULL, &pdtobj); if (SUCCEEDED(hres)) { SHCreateLinks(hwnd, szSendTo, pdtobj, 0, NULL); pdtobj->lpVtbl->Release(pdtobj); } psf->lpVtbl->Release(psf); } psfDesktop->lpVtbl->Release(psfDesktop); } ILFree(pidlParent); } ILFree(pidl); } } return hres; }
/*----------------------------------------------------------
Purpose: Creates a briefcase in the specified directory.
Returns: -- Cond: -- */ void WINAPI Briefcase_CreateInDirectory(HWND hwnd, HWND hwndCabinet, LPCTSTR pszPath) { if (CreateTheBriefcase(hwnd, pszPath)) { // Select the newly created item to edit it
LPITEMIDLIST pidl = ILCreateFromPath(pszPath); if (pidl) { SelectItemInCabinet(hwndCabinet, ILFindLastID(pidl), TRUE); ILFree(pidl); } } }
/*----------------------------------------------------------
Purpose: Creates a briefcase on the desktop.
Returns: -- Cond: -- */ void WINAPI Briefcase_CreateOnDesktop(HWND hwnd) { // Place it on the desktop
TCHAR szPath[MAX_PATH];
if (SHGetSpecialFolderPath(hwnd, szPath, CSIDL_DESKTOPDIRECTORY, FALSE)) { int cch; UINT ids;
if (IsLFNDrive(szPath)) ids = IDS_BC_NAME; else ids = IDS_BC_NAME_SHORT;
StrCatBuff(szPath, TEXT("\\"), ARRAYSIZE(szPath)); cch = lstrlen(szPath); LoadString(g_hinst, ids, &szPath[cch], ARRAYSIZE(szPath)-cch); if (CreateTheBriefcase(hwnd, szPath)) { // Add a shortcut of this briefcase to the SendTo folder
AddBriefcaseToSendToFolder(hwnd, szPath); } } }
/*----------------------------------------------------------
Purpose: Create a briefcase folder in the specified directory or on the desktop. Returns: -- Cond: -- */ void WINAPI _export Briefcase_Create_Common(HWND hwnd, HINSTANCE hAppInstance, LPTSTR pszCmdLine, int nCmdShow) { DEBUG_CODE( DEBUG_BREAK(BF_ONRUNONCE); )
// Command line should be of format "xxxx path" where <path>
// is the fully qualified pathname of the briefcase to create,
// and <xxxx> is the explorer hwnd.
if (pszCmdLine && *pszCmdLine) { LPTSTR psz; HWND hwndCabinet;
// Get hwnd
hwndCabinet = IntToPtr(AnsiToInt(pszCmdLine)); psz = StrChr(pszCmdLine, TEXT(' ')); if (NULL != hwndCabinet && NULL != psz) { Briefcase_CreateInDirectory(hwnd, hwndCabinet, CharNext(psz)); return; } } Briefcase_CreateOnDesktop(hwnd); }
void WINAPI _export Briefcase_Create(HWND hwnd, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow) { UINT iLength = lstrlenA(pszCmdLine)+1; LPWSTR lpwszCmdLine;
lpwszCmdLine = (LPWSTR)LocalAlloc(LPTR, iLength *SIZEOF(TCHAR)); if (lpwszCmdLine) { MultiByteToWideChar(CP_ACP, 0, pszCmdLine, -1, lpwszCmdLine, iLength);
Briefcase_Create_Common(hwnd, hAppInstance, lpwszCmdLine, nCmdShow); LocalFree((HANDLE)lpwszCmdLine); } }
void WINAPI _export Briefcase_CreateW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR pwszCmdLine, int nCmdShow) { Briefcase_Create_Common(hwnd, hAppInstance, pwszCmdLine, nCmdShow); }
/*----------------------------------------------------------
Purpose: Display the introduction "wizard". (It's really not a wizard since it is not making anything for us.)
NOTE: This function serves double duty for both the ansi and unicode versions. It never uses the command line.
Returns: -- Cond: -- */ void WINAPI _export Briefcase_Intro( HWND hwnd, HINSTANCE hAppInstance, LPTSTR lpszCmdLine, int nCmdShow) { Intro_DoModal(hwnd); }
//---------------------------------------------------------------------------
// DLL entry-points
//---------------------------------------------------------------------------
/*----------------------------------------------------------
Purpose: This function is called back from within IClassFactory::CreateInstance() of the default class factory object, which is created by SHCreateClassObject.
Returns: standard Cond: -- */ HRESULT CALLBACK DllFactoryCallback(IUnknown *punkOuter, REFIID riid, void **ppvOut) { HRESULT hres;
if (IsEqualIID(riid, &IID_IShellExtInit)) { hres = BriefExt_CreateInstance(punkOuter, riid, ppvOut); } else if (IsEqualIID(riid, &IID_IBriefcaseStg)) { hres = BriefStg_CreateInstance(punkOuter, riid, ppvOut); } else { hres = E_NOINTERFACE; *ppvOut = NULL; } return hres; }
/*----------------------------------------------------------
Purpose: Standard OLE 2.0 entry-point
Returns: standard Cond: -- */ STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppvOut) { HRESULT hres;
if (IsEqualIID(rclsid, &CLSID_Briefcase)) { // We are supposed return the class object for this class. Instead
// of fully implementing it in this DLL, we just call a helper
// function in the shell DLL which creates a default class factory
// object for us. When its CreateInstance member is called, it
// will call back our create instance function.
hres = SHCreateDefClassObject( riid, // Interface ID
ppvOut, // Non-null to aggregate
DllFactoryCallback, // callback function
&g_cBusyRef, // reference count of this DLL
NULL); // init interface
} else { hres = REGDB_E_CLASSNOTREG; *ppvOut = NULL; }
return hres; }
/*----------------------------------------------------------
Purpose: "Can Unload Now" entry point. Called by the shell DLL task handler list. Returns: S_OK to unload Cond: -- */ STDAPI DllCanUnloadNow(void) { HRESULT hr;
// We only unload when:
// 2) We are not busy processing anything else *and*
// 3) No briefcases are currently open
//
ENTEREXCLUSIVE(); { if (!IsBusySemaphore() && !IsOpenBriefSemaphore()) { DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("DllCanUnloadNow says OK (Busy=%d, Brief=%d)"), g_cBusyRef, g_cBriefRef); )
hr = S_OK; } else { DEBUG_CODE( TRACE_MSG(TF_GENERAL, TEXT("DllCanUnloadNow says FALSE (Busy=%d, Brief=%d)"), g_cBusyRef, g_cBriefRef); )
hr = S_FALSE; } } LEAVEEXCLUSIVE();
return hr; }
|