|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000
//
// File: ShellExtensions.cpp
//
// Contents: object to implement propertypage extensions
// for Win2k shim layer
//
// History: 23-september-00 clupu Created
//
//
//--------------------------------------------------------------------------
#include "stdafx.h"
#include "resource.h"
#include "ShellExtensions.h"
#include "strsafe.h"
UINT g_DllRefCount = 0; BOOL g_bExtEnabled = FALSE; TCHAR g_szLayerStorage[MAX_PATH] = _T("");
//////////////////////////////////////////////////////////////////////////
// InitLayerStorage
//
// Get the name of the file that will be used to store
// information about which EXEs/LNKs are layered.
void InitLayerStorage( BOOL bDelete ) { GetSystemWindowsDirectory(g_szLayerStorage, ARRAYSIZE(g_szLayerStorage)); if (g_szLayerStorage[lstrlen(g_szLayerStorage) - 1] == _T('\\')) { g_szLayerStorage[lstrlen(g_szLayerStorage) - 1] = 0; } StringCchCat(g_szLayerStorage, ARRAYSIZE(g_szLayerStorage), _T("\\AppPatch\\LayerStorage.dat"));
if (bDelete) { DeleteFile(g_szLayerStorage); } }
//////////////////////////////////////////////////////////////////////////
// CheckForRights
//
#define APPCOMPAT_KEY _T("System\\CurrentControlSet\\Control\\Session Manager\\AppCompatibility")
#define APPCOMPAT_TEST_SUBKEY _T("12181969-7036745")
void CheckForRights( void ) { HKEY hkey = NULL, hkeyTest = NULL; LONG lRes;
g_bExtEnabled = FALSE; lRes = RegOpenKey(HKEY_LOCAL_MACHINE, APPCOMPAT_KEY, &hkey); if (lRes != ERROR_SUCCESS) { LogMsg(_T("[CheckForRights] cannot open the appcompat key.\n") _T("The appcompat shell extension will be disabled\n")); return; } lRes = RegCreateKey(hkey, APPCOMPAT_TEST_SUBKEY, &hkeyTest);
if (lRes != ERROR_SUCCESS) { LogMsg(_T("[CheckForRights] cannot create test registry key.\n") _T("The appcompat shell extension will be disabled\n")); goto cleanup; } RegCloseKey(hkeyTest); hkeyTest = NULL; lRes = RegDeleteKey(hkey, APPCOMPAT_TEST_SUBKEY); if (lRes != ERROR_SUCCESS) { LogMsg(_T("[CheckForRights] cannot delete test registry key.\n") _T("The appcompat shell extension will be disabled\n")); goto cleanup; } g_bExtEnabled = TRUE;
cleanup: if (hkey != NULL) { RegCloseKey(hkey); } }
//////////////////////////////////////////////////////////////////////////
// CreateLayeredStorage
//
// Create the file for layer storage.
void CreateLayeredStorage( LPWSTR pszItem, DWORD dwFlags ) { HANDLE hFile;
hFile = CreateFile(g_szLayerStorage, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) { LogMsg(_T("[CreateLayeredStorage] cannot create the storage file!\n")); return; }
LayerStorageHeader Header; LayeredItem Item;
Header.dwItemCount = 1; Header.dwMagic = LS_MAGIC;
GetLocalTime(&Header.timeLast);
ZeroMemory(&Item, sizeof(Item));
Item.dwFlags = dwFlags; StringCchCopy(Item.szItemName, ARRAYSIZE(Item.szItemName), pszItem);
DWORD dwBytesWritten = 0;
WriteFile(hFile, &Header, sizeof(Header), &dwBytesWritten, NULL); WriteFile(hFile, &Item, sizeof(Item), &dwBytesWritten, NULL); LogMsg(_T("[CreateLayeredStorage] storage file \"%s\" initialized\n"), g_szLayerStorage); CloseHandle(hFile); }
//////////////////////////////////////////////////////////////////////////
// LayeredItemOperation
//
// Add/Delete/Query items in the layer storage
void LayeredItemOperation( LPWSTR pszItem, DWORD dwOp, LPDWORD lpdwFlags ) { LogMsg(_T("[LayeredItemOperation] op %d item \"%s\"\n"), dwOp, pszItem);
HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hFileMapping = NULL; DWORD dwFileSize; PBYTE pData = NULL; PLayerStorageHeader pHeader = NULL; PLayeredItem pItems; PLayeredItem pCrtItem = NULL; int nLeft, nRight, nMid, nItem; BOOL bShrinkFile = FALSE; //
// Make sure we don't corrupt the layer storage.
//
if (lstrlenW(pszItem) + 1 > MAX_PATH) { pszItem[MAX_PATH - 1] = 0; } hFile = CreateFile(g_szLayerStorage, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) { LogMsg(_T("[LayeredItemOperation] the layer storage doesn't exist\n")); if (dwOp == LIO_READITEM) { *lpdwFlags = 0; return; }
if (dwOp == LIO_DELETEITEM) { LogMsg(_T("[LayeredItemOperation] cannot delete item\n")); return; }
//
// The file doesn't exist and the operation is LIO_ADDITEM.
// Create the file, write the item and get out.
//
CreateLayeredStorage(pszItem, *lpdwFlags); return; }
//
// The file already exists. Create a file mapping that will allow
// for adding/deleting/querying the item.
//
dwFileSize = GetFileSize(hFile, NULL);
hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwFileSize + (dwOp == LIO_ADDITEM ? sizeof(LayeredItem) : 0), NULL);
if (hFileMapping == NULL) { LogMsg(_T("[LayeredItemOperation] CreateFileMapping failed 0x%X\n"), GetLastError()); goto done; }
pData = (PBYTE)MapViewOfFile(hFileMapping, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); if (pData == NULL) { LogMsg(_T("[LayeredItemOperation] MapViewOfFile failed 0x%X\n"), GetLastError()); goto done; }
pHeader = (PLayerStorageHeader)pData;
pItems = (PLayeredItem)(pData + sizeof(LayerStorageHeader));
//
// Make sure it's our file.
//
if (dwFileSize < sizeof(LayerStorageHeader) || pHeader->dwMagic != LS_MAGIC) { LogMsg(_T("[LayeredItemOperation] invalid file magic 0x%0X\n"), pHeader->dwMagic); goto done; }
//
// Get the last access time.
//
GetLocalTime(&pHeader->timeLast); //
// First search for the item. The array is sorted so we do binary search.
//
nItem = -1, nLeft = 0, nRight = (int)pHeader->dwItemCount - 1;
while (nLeft <= nRight) { int nVal; nMid = (nLeft + nRight) / 2;
pCrtItem = pItems + nMid; nVal = lstrcmpi(pszItem, pCrtItem->szItemName); if (nVal == 0) { nItem = nMid; break; } else if (nVal < 0) { nRight = nMid - 1; } else { nLeft = nMid + 1; } }
if (nItem == -1) { LogMsg(_T("[LayeredItemOperation] the item was not found in the file.\n"));
if (dwOp == LIO_DELETEITEM) { goto done; } if (dwOp == LIO_READITEM) { *lpdwFlags = 0; goto done; } if (pHeader->dwItemCount == 0) { pCrtItem = pItems; } else { MoveMemory(pItems + nLeft + 1, pItems + nLeft, ((int)pHeader->dwItemCount - nLeft) * sizeof(LayeredItem));
pCrtItem = pItems + nLeft; } ZeroMemory(pCrtItem, sizeof(LayeredItem));
pCrtItem->dwFlags = *lpdwFlags; StringCchCopy(pCrtItem->szItemName, ARRAYSIZE(pCrtItem->szItemName), pszItem);
(pHeader->dwItemCount)++; } else { //
// The item is already in the file.
//
LogMsg(_T("[LayeredItemOperation] the item is in the file\n"));
if (dwOp == LIO_READITEM) { *lpdwFlags = pCrtItem->dwFlags; goto done; } if (dwOp == LIO_DELETEITEM) { MoveMemory(pItems + nItem, pItems + nItem + 1, ((int)pHeader->dwItemCount - nItem - 1) * sizeof(LayeredItem)); (pHeader->dwItemCount)--; } else { //
// Update the item's flags.
//
pCrtItem->dwFlags = *lpdwFlags; } //
// We've found the item so shrink the file by one item.
//
bShrinkFile = TRUE; } done:
if (pData != NULL) { UnmapViewOfFile(pData); }
if (hFileMapping != NULL) { CloseHandle(hFileMapping); }
if (bShrinkFile) { SetFilePointer(hFile, - (int)sizeof(LayeredItem), NULL, FILE_END); SetEndOfFile(hFile); } if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); } }
//////////////////////////////////////////////////////////////////////////
// LayerSelection
//
// The user changed the selection in the combo-box with the layers.
// Persist the user's selection to the layer storage.
void LayerSelection( HWND hdlg, LPWSTR pszItem ) { //
// See which layer is selected.
//
LPARAM lSel = SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_GETCURSEL, 0, 0);
if (lSel == CB_ERR) { LogMsg(_T("[LayerSelection] couldn't get the current selection\n")); } else {
DWORD dwFlags;
switch (lSel) { case 0: dwFlags = LI_WIN95; break; case 1: dwFlags = LI_WIN98; break; case 2: dwFlags = LI_NT4; break; default: LogMsg(_T("[LayerSelection] bad selection. default to Win9x\n")); dwFlags = LI_WIN95; break; }
LayeredItemOperation(pszItem, LIO_ADDITEM, &dwFlags); } }
//////////////////////////////////////////////////////////////////////////
// LayerPageDlgProc
//
// The dialog proc for the layer property page.
INT_PTR CALLBACK LayerPageDlgProc( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { int wCode = LOWORD(wParam); int wNotifyCode = HIWORD(wParam);
switch (uMsg) { case WM_INITDIALOG: { PROPSHEETPAGE* ppsp = (PROPSHEETPAGE*)lParam; DWORD dwFlags = 0; CLayerUIPropPage* pPropPage = (CLayerUIPropPage*)ppsp->lParam; LogMsg(_T("[LayerPageDlgProc] WM_INITDIALOG - item \"%s\"\n"), pPropPage->m_szFile); //
// Store the name of the EXE/LNK in the dialog.
//
SetWindowLong(hdlg, GWL_USERDATA, (LPARAM)pPropPage->m_szFile); //
// Add the names of the layers.
//
SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_ADDSTRING, 0, (LPARAM)_T("Windows 95 Compatibility Layer")); SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_ADDSTRING, 0, (LPARAM)_T("Windows 98 Compatibility Layer")); SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_ADDSTRING, 0, (LPARAM)_T("Windows NT4 SP5 Compatibility Layer")); //
// Read the layer storage for info on this item.
//
LayeredItemOperation(pPropPage->m_szFile, LIO_READITEM, &dwFlags); //
// Select the appropriate layer for this item. If no info
// is available in the layer store, default to the Win9x layer.
//
BOOL bEnable; switch (dwFlags) { case LI_WIN95: SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_SETCURSEL, 0, 0); bEnable = TRUE; break; case LI_WIN98: SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_SETCURSEL, 1, 0); bEnable = TRUE; break; case LI_NT4: SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_SETCURSEL, 2, 0); bEnable = TRUE; break; default: SendDlgItemMessage(hdlg, IDC_LAYER_NAME, CB_SETCURSEL, 0, 0); bEnable = FALSE; }
if (bEnable) { EnableWindow(GetDlgItem(hdlg, IDC_LAYER_NAME), TRUE); SendDlgItemMessage(hdlg, IDC_USE_LAYER, BM_SETCHECK, BST_CHECKED, 0); } break; }
case WM_COMMAND: { LPWSTR pszItem; pszItem = (LPTSTR)GetWindowLong(hdlg, GWL_USERDATA); switch (wNotifyCode) { case CBN_SELCHANGE: LayerSelection(hdlg, pszItem); return TRUE; } switch (wCode) { case IDC_USE_LAYER: if (SendDlgItemMessage(hdlg, IDC_USE_LAYER, BM_GETCHECK, 0, 0) == BST_CHECKED) { EnableWindow(GetDlgItem(hdlg, IDC_LAYER_NAME), TRUE); LayerSelection(hdlg, pszItem);
} else { EnableWindow(GetDlgItem(hdlg, IDC_LAYER_NAME), FALSE); LayeredItemOperation(pszItem, LIO_DELETEITEM, NULL); } break;
default: return FALSE; } break; }
default: return FALSE; }
return TRUE; }
//////////////////////////////////////////////////////////////////////////
// LayerPageCallbackProc
//
// The callback for the property page.
UINT CALLBACK LayerPageCallbackProc( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp ) { switch (uMsg) { case PSPCB_RELEASE: if (ppsp->lParam != 0) { CLayerUIPropPage* pPropPage = (CLayerUIPropPage*)(ppsp->lParam); LogMsg(_T("[LayerPageCallbackProc] releasing CLayerUIPropPage\n")); pPropPage->Release(); } break; } return 1; }
BOOL GetExeFromLnk( TCHAR* pszLnk, TCHAR* pszExe, int cchSize ) { HRESULT hres; IShellLink* psl = NULL; IPersistFile* pPf = NULL; WIN32_FIND_DATA wfd; TCHAR szArg[MAX_PATH]; BOOL bSuccess = FALSE; IShellLinkDataList* psldl = NULL; EXP_DARWIN_LINK* pexpDarwin = NULL; hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); if (FAILED(hres)) { LogMsg(_T("[GetExeFromLnk] CoCreateInstance failed\n")); return FALSE; }
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&pPf); if (FAILED(hres)) { LogMsg(_T("[GetExeFromLnk] QueryInterface for IPersistFile failed\n")); goto cleanup; }
//
// Load the link file.
//
hres = pPf->Load(pszLnk, STGM_READ); if (FAILED(hres)) { LogMsg(_T("[GetExeFromLnk] failed to load link \"%s\"\n"), pszLnk); goto cleanup; }
//
// See if this is a DARWIN link.
//
hres = psl->QueryInterface(IID_IShellLinkDataList, (LPVOID*)&psldl); if (FAILED(hres)) { LogMsg(_T("[GetExeFromLnk] failed to get IShellLinkDataList.\n")); } else { hres = psldl->CopyDataBlock(EXP_DARWIN_ID_SIG, (void**)&pexpDarwin); if (SUCCEEDED(hres)) { LogMsg(_T("[GetExeFromLnk] this is a DARWIN link \"%s\".\n"), pszLnk); goto cleanup; } } //
// Resolve the link.
//
hres = psl->Resolve(NULL, SLR_NOTRACK | SLR_NOSEARCH | SLR_NO_UI | SLR_NOUPDATE); if (FAILED(hres)) { LogMsg(_T("[GetExeFromLnk] failed to resolve the link \"%s\"\n"), pszLnk); goto cleanup; } pszExe[0] = _T('\"'); //
// Get the path to the link target.
//
hres = psl->GetPath(pszExe + 1, cchSize, &wfd, SLGP_UNCPRIORITY); if (FAILED(hres)) { LogMsg(_T("[GetExeFromLnk] failed to get the path for link \"%s\"\n"), pszLnk); goto cleanup; }
szArg[0] = 0;
hres = psl->GetArguments(szArg, MAX_PATH);
if (SUCCEEDED(hres) && szArg[0] != 0) { StringCchCat(pszExe, cchSize, _T("\" ")); StringCchCat(pszExe, cchSize, szArg); } else { StringCchCat(pszExe, cchSize, _T("\"")); }
bSuccess = TRUE;
cleanup: if (pPf != NULL) { pPf->Release(); } if (psl) { psl->Release(); }
if (psldl) { psldl->Release(); }
if (pexpDarwin) { LocalFree(pexpDarwin); } return bSuccess; }
//////////////////////////////////////////////////////////////////////////
// CLayerUIPropPage
CLayerUIPropPage::CLayerUIPropPage() { LogMsg(_T("[CLayerUIPropPage::CLayerUIPropPage]\n")); }
CLayerUIPropPage::~CLayerUIPropPage() { LogMsg(_T("[CLayerUIPropPage::~CLayerUIPropPage]\n")); }
//////////////////////////////////////////////////////////////////////////
// IShellExtInit methods
STDMETHODIMP CLayerUIPropPage::Initialize( LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID ) { LogMsg(_T("[CLayerUIPropPage::Initialize]\n"));
if (!g_bExtEnabled) { return NOERROR; } if (pDataObj == NULL) { LogMsg(_T("\t failed. bad argument.\n")); return E_INVALIDARG; }
//
// Store a pointer to the data object
//
m_spDataObj = pDataObj;
//
// If a data object pointer was passed in, save it and
// extract the file name.
//
STGMEDIUM medium; UINT uCount; FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
if (SUCCEEDED(m_spDataObj->GetData(&fe, &medium))) { //
// Get the file name from the CF_HDROP.
//
uCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0); if (uCount > 0) { TCHAR szExe[MAX_PATH]; DragQueryFile((HDROP)medium.hGlobal, 0, szExe, ARRAYSIZE(szExe)); LogMsg(_T("\tlink \"%s\".\n"), szExe);
if (!GetExeFromLnk(szExe, m_szFile, ARRAYSIZE(m_szFile))) { m_szFile[0] = 0; } LogMsg(_T("\tfile \"%s\".\n"), m_szFile); }
ReleaseStgMedium(&medium); } else { LogMsg(_T("\t failed to get the data.\n")); } return NOERROR; }
//////////////////////////////////////////////////////////////////////////
// IShellPropSheetExt methods
STDMETHODIMP CLayerUIPropPage::AddPages( LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam ) { PROPSHEETPAGE psp; HPROPSHEETPAGE hPage;
LogMsg(_T("[CLayerUIPropPage::AddPages]\n")); if (!g_bExtEnabled || m_szFile[0] == 0) { return S_OK; } psp.dwSize = sizeof(psp); psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE | PSP_USECALLBACK; psp.hInstance = _Module.m_hInst; psp.pszTemplate = MAKEINTRESOURCE(IDD_LAYER_PROPPAGE); psp.hIcon = 0; psp.pszTitle = _T("Compatibility"); psp.pfnDlgProc = LayerPageDlgProc; psp.pcRefParent = &g_DllRefCount; psp.pfnCallback = LayerPageCallbackProc; psp.lParam = (LPARAM)this;
LogMsg(_T("\titem \"%s\".\n"), m_szFile); LogMsg(_T("\tg_DllRefCount %d.\n"), g_DllRefCount); AddRef(); hPage = CreatePropertySheetPage(&psp); if (hPage != NULL) { if (lpfnAddPage(hPage, lParam)) { return S_OK; } else { DestroyPropertySheetPage(hPage); Release(); return S_OK; } } else { return E_OUTOFMEMORY; } return E_FAIL; }
STDMETHODIMP CLayerUIPropPage::ReplacePage( UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplacePage, LPARAM lParam ) { LogMsg(_T("[CLayerUIPropPage::ReplacePage]\n")); return S_OK; }
|