windows-nt/Source/XPSP1/NT/windows/appcompat/slayerui/win2k/shellextensions.cpp
2020-09-26 16:20:57 +08:00

828 lines
21 KiB
C++

//+-------------------------------------------------------------------------
//
// 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"
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, MAX_PATH);
if (g_szLayerStorage[lstrlen(g_szLayerStorage) - 1] == _T('\\')) {
g_szLayerStorage[lstrlen(g_szLayerStorage) - 1] = 0;
}
lstrcat(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;
lstrcpy(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;
lstrcpy(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 cbSize
)
{
HRESULT hres;
IShellLink* psl = NULL;
IPersistFile* pPf = NULL;
WIN32_FIND_DATA wfd;
TCHAR szArg[MAX_PATH];
BOOL bSuccess = FALSE;
IShellLinkDataList* psldl;
EXP_DARWIN_LINK* pexpDarwin;
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,
cbSize,
&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) {
lstrcat(pszExe, _T("\" "));
lstrcat(pszExe, szArg);
} else {
lstrcat(pszExe, _T("\""));
}
bSuccess = TRUE;
cleanup:
if (pPf != NULL) {
pPf->Release();
}
psl->Release();
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,
sizeof(szExe) / sizeof(TCHAR));
LogMsg(_T("\tlink \"%s\".\n"), szExe);
if (!GetExeFromLnk(szExe, m_szFile, MAX_PATH * sizeof(TCHAR))) {
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 = (DLGPROC)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;
}