Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

639 lines
19 KiB

#include "shellprv.h"
#pragma hdrstop
#ifdef WINNT
#define CXIMAGEGAP 0 // NT doesn't align menu items no more
#else
#define CXIMAGEGAP 6
#endif
#define MAXEXTSIZE (PATH_CCH_EXT + 2) // Max length of file extension.
#define SRCSTENCIL 0x00B8074AL
typedef struct {
TCHAR szMenuText[CCH_KEYMAX];
TCHAR szExt[MAXEXTSIZE];
TCHAR szClass[CCH_KEYMAX];
DWORD dwFlags;
int iImage;
} NEWOBJECTINFO, * LPNEWOBJECTINFO;
LPNEWOBJECTINFO g_lpnoiLast = NULL;
#define NEWTYPE_DATA 1
#define NEWTYPE_FILE 2
#define NEWTYPE_NULL 3
#define NEWTYPE_COMMAND 4
typedef struct {
int type;
LPVOID lpData;
DWORD cbData;
HKEY hkeyNew;
} NEWFILEINFO, *LPNEWFILEINFO;
// this is basically the same as filemenu_getitemdata
LPNEWOBJECTINFO WINAPI NewObjMenu_GetItemData(HMENU hmenu, UINT iItem)
{
MENUITEMINFO mii;
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_DATA | MIIM_STATE;
mii.cch = 0; // just in case...
if (GetMenuItemInfo(hmenu, iItem, TRUE, &mii))
return (LPNEWOBJECTINFO)mii.dwItemData;
return NULL;
}
extern TCHAR const c_szFileName[];
extern TCHAR const c_szCommand[];
extern TCHAR const c_szConfig[];
TCHAR const c_szShellNew[] = TEXT("ShellNew");
TCHAR const c_szData[] = TEXT("Data");
TCHAR const c_szNullFile[] = TEXT("NullFile");
TCHAR const c_szNoExt[] = TEXT("NoExtension");
// ShellNew config flags
#define SNCF_DEFAULT 0x0000
#define SNCF_NOEXT 0x0001
void GetConfigFlags(HKEY hkey, DWORD * pdwFlags)
{
DWORD cbData;
TCHAR szTemp[MAX_PATH];
*pdwFlags = SNCF_DEFAULT;
if (cbData = ARRAYSIZE(szTemp), (RegQueryValueEx(hkey, (LPTSTR)c_szNoExt, 0, NULL, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS)) {
*pdwFlags |= SNCF_NOEXT;
}
}
BOOL GetNewFileInfoForKey(HKEY hkeyExt, LPNEWFILEINFO lpnfi, DWORD * pdwFlags)
{
BOOL fRet = FALSE;
HKEY hKey; // this gets the \\.ext\progid key
HKEY hkeyNew;
TCHAR szProgID[80];
LONG lSize = SIZEOF(szProgID);
// open the Newcommand
if (RegQueryValue(hkeyExt, NULL, szProgID, &lSize) != ERROR_SUCCESS) {
return FALSE;
}
if (RegOpenKey(hkeyExt, szProgID, &hKey) != ERROR_SUCCESS) {
hKey = hkeyExt;
}
if (RegOpenKey(hKey, c_szShellNew, &hkeyNew) == ERROR_SUCCESS) {
DWORD dwType;
DWORD cbData;
TCHAR szTemp[MAX_PATH];
HKEY hkeyConfig;
// Are there any config flags?
if (pdwFlags) {
if (RegOpenKey(hkeyNew, c_szConfig, &hkeyConfig) == ERROR_SUCCESS) {
GetConfigFlags(hkeyConfig, pdwFlags);
RegCloseKey(hkeyConfig);
} else
*pdwFlags = 0;
}
if (cbData = SIZEOF(szTemp), (RegQueryValueEx(hkeyNew, (LPTSTR)c_szNullFile, 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS)) {
fRet = TRUE;
if (lpnfi)
lpnfi->type = NEWTYPE_NULL;
} else if (cbData = SIZEOF(szTemp), (RegQueryValueEx(hkeyNew, (LPTSTR)c_szFileName, 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS) && (dwType == REG_SZ)) {
fRet = TRUE;
if (lpnfi) {
lpnfi->type = NEWTYPE_FILE;
lpnfi->hkeyNew = hkeyNew; // store this away so we can find out which one held the file easily
Assert((LPTSTR*)lpnfi->lpData == NULL);
lpnfi->lpData = (void*)LocalAlloc(LPTR, (lstrlen(szTemp) + 1) * SIZEOF(TCHAR));
lstrcpy((LPTSTR)lpnfi->lpData, szTemp);
hkeyNew = NULL;
}
} else if (cbData = SIZEOF(szTemp), (RegQueryValueEx(hkeyNew, (LPTSTR)c_szCommand, 0, &dwType, (LPBYTE)szTemp, &cbData) == ERROR_SUCCESS) && (dwType == REG_SZ)) {
fRet = TRUE;
if (lpnfi) {
lpnfi->type = NEWTYPE_COMMAND;
lpnfi->hkeyNew = hkeyNew; // store this away so we can find out which one held the command easily
Assert((LPTSTR*)lpnfi->lpData == NULL);
lpnfi->lpData = (void*)LocalAlloc(LPTR, (lstrlen(szTemp) + 1) * SIZEOF(TCHAR));
lstrcpy((LPTSTR)lpnfi->lpData, szTemp);
hkeyNew = NULL;
}
} else if ((RegQueryValueEx(hkeyNew, (LPTSTR)c_szData, 0, &dwType, NULL, &cbData) == ERROR_SUCCESS) && cbData) {
// yes! the data for a new file is stored in the registry
fRet = TRUE;
// do they want the data?
if (lpnfi)
{
lpnfi->type = NEWTYPE_DATA;
lpnfi->cbData = cbData;
lpnfi->lpData = (void*)LocalAlloc(LPTR, cbData);
#ifdef UNICODE
if (lpnfi->lpData)
{
if (dwType == REG_SZ)
{
LPWSTR pszTemp;
//
// Get the Unicode data from the registry.
//
pszTemp = (LPWSTR)LocalAlloc(LPTR, cbData);
if (pszTemp)
{
RegQueryValueEx(hkeyNew, (LPTSTR)c_szData, 0, &dwType, (LPBYTE)pszTemp, &cbData);
lpnfi->cbData =
WideCharToMultiByte( CP_ACP,
0,
pszTemp,
-1,
lpnfi->lpData,
cbData,
NULL,
NULL );
if (lpnfi->cbData == 0)
{
LocalFree(lpnfi->lpData);
lpnfi->lpData = NULL;
}
LocalFree(pszTemp);
}
else
{
LocalFree(lpnfi->lpData);
lpnfi->lpData = NULL;
}
}
else
{
RegQueryValueEx(hkeyNew, (LPTSTR)c_szData, 0, &dwType, lpnfi->lpData, &cbData);
}
}
#else
if (lpnfi->lpData)
{
RegQueryValueEx(hkeyNew, (LPTSTR)c_szData, 0, &dwType, lpnfi->lpData, &cbData);
}
#endif
}
}
if (hkeyNew)
RegCloseKey(hkeyNew);
}
if (hKey != hkeyExt) {
RegCloseKey(hKey);
}
return fRet;
}
BOOL GetNewFileInfoForExtension(LPTSTR lpszExt, LPTSTR lpszClass, LPNEWFILEINFO lpnfi, DWORD * pdwFlags)
{
TCHAR szSubKey[128];
TCHAR szValue[80];
LONG lSize = SIZEOF(szValue);
HKEY hkeyNew;
BOOL fRet = FALSE;;
// check the new keys under the class id (if any)
wsprintf(szSubKey, c_szSSlashS, lpszClass, c_szCLSID);
lSize = SIZEOF(szValue);
if (RegQueryValue(HKEY_CLASSES_ROOT, szSubKey, szValue, &lSize) == ERROR_SUCCESS) {
wsprintf(szSubKey, c_szSSlashS, c_szCLSID, szValue);
lSize = SIZEOF(szValue);
if (RegOpenKey(HKEY_CLASSES_ROOT, szSubKey, &hkeyNew) == ERROR_SUCCESS) {
fRet = GetNewFileInfoForKey(hkeyNew, lpnfi, pdwFlags);
RegCloseKey(hkeyNew);
}
}
// otherwise check under the type extension... do the extension, not the type
// so that multi-ext to 1 type will work right
if (!fRet && (RegOpenKey(HKEY_CLASSES_ROOT, lpszExt, &hkeyNew) == ERROR_SUCCESS)) {
fRet = GetNewFileInfoForKey(hkeyNew, lpnfi, pdwFlags);
RegCloseKey(hkeyNew);
}
return fRet;
}
void NewObjMenu_Fill(HMENU hmenu, UINT id, int iStart)
{
TCHAR szExt[MAXEXTSIZE];
TCHAR szClass[CCH_KEYMAX];
TCHAR szDisplayName[CCH_KEYMAX];
int i;
DWORD dwFlags;
for (i = 0; RegEnumKey(HKEY_CLASSES_ROOT, i, szExt, ARRAYSIZE(szExt)) == ERROR_SUCCESS; i++)
{
LONG lSize = SIZEOF(szClass);
// find .ext that have proper class descriptions with them.
if ((szExt[0] == TEXT('.')) &&
RegQueryValue(HKEY_CLASSES_ROOT, szExt, szClass, &lSize) == ERROR_SUCCESS && (lSize > 0) &&
GetNewFileInfoForExtension(szExt, szClass, NULL, &dwFlags) &&
GetClassDescription(HKEY_CLASSES_ROOT, szClass, szDisplayName, ARRAYSIZE(szDisplayName),
GCD_MUSTHAVEOPENCMD))
{
LPNEWOBJECTINFO lpnoi = Alloc(SIZEOF(NEWOBJECTINFO));
if (lpnoi)
{
lpnoi->dwFlags = dwFlags;
lstrcpy(lpnoi->szExt, szExt);
lstrcpy(lpnoi->szClass, szClass);
lstrcpy(lpnoi->szMenuText, szDisplayName);
//BUGBUG, fix this later
lpnoi->iImage = -1; //FS_ExtensionSystemImageIndex(szExt);
// BUGBUG: if this fails we have a leak
AppendMenu(hmenu, MF_OWNERDRAW, id, (LPTSTR)lpnoi);
}
}
}
DebugMsg(DM_TRACE, TEXT("sh TR - NewObj_Fill: filed (%x, %d)"),
hmenu, GetMenuItemCount(hmenu));
// remove dups.
// need to call GetMenuItemCount each time because
// we're removing things...
for (i = iStart; i < GetMenuItemCount(hmenu); i++) {
int j;
LPNEWOBJECTINFO lpnoi = NewObjMenu_GetItemData(hmenu, i);
for (j = GetMenuItemCount(hmenu) - 1; j > i; j--) {
LPNEWOBJECTINFO lpnoi2 = NewObjMenu_GetItemData(hmenu, j);
if ( !lstrcmpi(lpnoi->szMenuText, lpnoi2->szMenuText)) {
DeleteMenu(hmenu, j, MF_BYPOSITION);
Free(lpnoi2);
}
}
}
DebugMsg(DM_TRACE, TEXT("sh TR - NewObj_Fill: dup removed (%x, %d)"),
hmenu, GetMenuItemCount(hmenu));
}
void WINAPI NewObjMenu_DrawItem(DRAWITEMSTRUCT *lpdi)
{
if ((lpdi->itemAction & ODA_SELECT) || (lpdi->itemAction & ODA_DRAWENTIRE))
{
DWORD dwRop;
int x, y;
SIZE sz;
LPNEWOBJECTINFO lpnoi = (LPNEWOBJECTINFO)lpdi->itemData;
// Draw the image (if there is one).
if (lpnoi->iImage != -1)
{
extern HIMAGELIST himlIconsSmall;
x = lpdi->rcItem.left+CXIMAGEGAP;
y = (lpdi->rcItem.bottom+lpdi->rcItem.top-g_cySmIcon)/2;
ImageList_Draw(himlIconsSmall, lpnoi->iImage, lpdi->hDC, x, y, ILD_TRANSPARENT);
x += g_cxSmIcon;
} else {
BITMAP bm;
HBITMAP hbmCheck;
hbmCheck = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECK));
GetObject(hbmCheck, SIZEOF(bm), &bm);
x = lpdi->rcItem.left + bm.bmWidth + CXIMAGEGAP;
DeleteObject(hbmCheck);
}
GetTextExtentPoint(lpdi->hDC, lpnoi->szMenuText, lstrlen(lpnoi->szMenuText), &sz);
if (lpdi->itemState & ODS_SELECTED)
{
SetBkColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(lpdi->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
// REVIEW HACK - keep track of the last selected item.
g_lpnoiLast = lpnoi;
dwRop = SRCSTENCIL;
}
else
{
dwRop = SRCAND;
}
// Draw the text.
y = (lpdi->rcItem.bottom+lpdi->rcItem.top-sz.cy)/2;
ExtTextOut(lpdi->hDC, x, y, ETO_OPAQUE, &lpdi->rcItem, lpnoi->szMenuText,
lstrlen(lpnoi->szMenuText), NULL);
}
}
BOOL WINAPI NewObjMenu_InitMenuPopup(HMENU hmenu, int iStart)
{
UINT id;
LPNEWOBJECTINFO lpnoi;
lpnoi = NewObjMenu_GetItemData(hmenu, iStart);
if (lpnoi) // already initialized.
return FALSE;
id = GetMenuItemID(hmenu , iStart);
DeleteMenu(hmenu, iStart, MF_BYPOSITION);
NewObjMenu_Fill(hmenu, id, iStart);
return TRUE;
}
LRESULT WINAPI NewObjMenu_MeasureItem(MEASUREITEMSTRUCT *lpmi)
{
HDC hdc;
HFONT hfont, hfontOld;
NONCLIENTMETRICS ncm;
LRESULT lres = FALSE;
LPNEWOBJECTINFO lpnoi;
lpnoi = (LPNEWOBJECTINFO)lpmi->itemData;
if (lpnoi)
{
// Get the rough height of an item so we can work out when to break the
// menu. User should really do this for us but that would be useful.
hdc = GetDC(NULL);
if (hdc)
{
// REVIEW cache out the menu font?
ncm.cbSize = SIZEOF(NONCLIENTMETRICS);
if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, SIZEOF(ncm), &ncm, FALSE))
{
hfont = CreateFontIndirect(&ncm.lfMenuFont);
if (hfont)
{
SIZE sz;
hfontOld = SelectObject(hdc, hfont);
GetTextExtentPoint(hdc, lpnoi->szMenuText, lstrlen(lpnoi->szMenuText), &sz);
lpmi->itemHeight = max (g_cySmIcon, sz.cy);
// lpmi->itemWidth = g_cxSmIcon + 2*CXIMAGEGAP + sz.cx;
lpmi->itemWidth = 2*CXIMAGEGAP + sz.cx;
SelectObject(hdc, hfontOld);
DeleteObject(hfont);
lres = TRUE;
}
}
ReleaseDC(NULL, hdc);
}
}
else
{
DebugMsg(DM_ERROR, TEXT("fm_mi: Filemenu is invalid."));
}
return lres;
}
void WINAPI NewObjMenu_Destroy(HMENU hmenu, int iStart)
{
int i;
LPNEWOBJECTINFO lpnoi;
DebugMsg(DM_TRACE, TEXT("sh TR - NewObj_Destroy: called (%x, %d, %d)"),
hmenu, iStart, GetMenuItemCount(hmenu));
for (i = GetMenuItemCount(hmenu) - 1; i >= iStart; i--) {
lpnoi = NewObjMenu_GetItemData(hmenu, i);
if (lpnoi)
Free(lpnoi);
//
// We need to remove them in case for File->New
//
DeleteMenu(hmenu, i, MF_BYPOSITION);
}
}
BOOL CreateWriteCloseFile(HWND hwnd, LPTSTR szFileName, LPVOID lpData, DWORD cbData)
{
HFILE hfile = Win32_lcreat(szFileName, 0);
if (hfile != HFILE_ERROR) {
if (cbData) {
_lwrite(hfile, lpData, cbData);
}
_lclose(hfile);
return TRUE;
} else {
PathRemoveExtension(szFileName);
SHSysErrorMessageBox(hwnd, NULL, IDS_CANNOTCREATEFILE,
GetLastError(), PathFindFileName(szFileName),
MB_OK | MB_ICONEXCLAMATION);
}
return FALSE;
}
HRESULT NewObjMenu_RunCommand(HWND hwnd, LPTSTR pszPath, LPTSTR pszRun)
{
HRESULT hres;
SHELLEXECUTEINFO ei;
TCHAR szCommand[MAX_PATH];
LPTSTR pszArgs;
lstrcpy(szCommand, pszRun);
PathRemoveArgs(szCommand);
// Attempt to include the hwnd and path in the command line
pszArgs = ShellConstructMessageString(HINST_THISDLL, PathGetArgs(pszRun), (DWORD)hwnd, (LPTSTR)pszPath);
if (pszArgs) {
FillExecInfo(ei, hwnd, NULL, szCommand, pszArgs, NULL, SW_SHOWNORMAL);
if (ShellExecuteEx(&ei)) {
// Return S_FALSE because ShellExecuteEx is not atomic
hres = S_FALSE;
}
else
hres = E_FAIL;
SHFree(pszArgs);
} else
hres = E_OUTOFMEMORY;
return hres;
}
//
// This function create a new file by copying a template file.
//
// Parameters:
// hwnd -- Specifies the parent/owner window
// szPath -- Specifies the name of new file to be created.
// lpnfi -- Specifies the new file info
//
// Returns:
// Returns:
// S_OK, if succeeded.
// HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), if failed because the template file does not exist.
// E_FAIL, if failed for other reason.
//
HRESULT NewObjMenu_CopyTemplate(HWND hwnd, LPTSTR szPath, LPNEWFILEINFO lpnfi)
{
TCHAR szSrc[MAX_PATH + 1];
TCHAR szFileName[MAX_PATH +1];
// now do the actual restore.
SHFILEOPSTRUCT sFileOp =
{
hwnd,
FO_COPY,
szSrc,
szPath,
FOF_NOCONFIRMATION | FOF_MULTIDESTFILES | FOF_SILENT,
} ;
lstrcpy(szFileName, (LPTSTR)lpnfi->lpData);
if (PathIsFileSpec(szFileName)) {
if (!SHGetSpecialFolderPath(NULL, szSrc, CSIDL_TEMPLATES, FALSE))
return FALSE;
PathAppend(szSrc, szFileName);
} else {
lstrcpy(szSrc, szFileName);
}
if (!PathFileExists(szSrc)) {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
szSrc[lstrlen(szSrc) + 1] = TEXT('\0'); // double null terminate;
return ((SHFileOperation(&sFileOp) == 0) && !sFileOp.fAnyOperationsAborted) ?
S_OK : E_FAIL;
}
//
// This function creates a new file.
//
// HACK: Some parameters (extension, class, ...) are passed via global
// variable (g_lpnoLast).
//
// Parameters:
// hwnd -- Specifies the parent/owner window
// pidlParent -- Specifies the parent folder
// ppidl -- Specifies the pointer to output pidl
//
// Return:
// S_OK, if succeeded.
//
HRESULT WINAPI NewObjMenu_DoItToMe(HWND hwnd, LPCITEMIDLIST pidlParent, LPITEMIDLIST *ppidl)
{
TCHAR szPath[MAX_PATH];
TCHAR szFileSpec[MAX_PATH+80]; // Add some slop incase we overflow
NEWFILEINFO nfi;
DWORD dwError;
HRESULT hres = NOERROR;
nfi.lpData = NULL;
nfi.hkeyNew = NULL;
if (g_lpnoiLast == NULL)
return ResultFromScode(E_FAIL);
SHGetPathFromIDList(pidlParent,szPath);
if (IsLFNDrive(szPath)) {
LoadString(HINST_THISDLL, IDS_NEW, szFileSpec, ARRAYSIZE(szFileSpec));
lstrcat(szFileSpec, g_lpnoiLast->szMenuText);
if ( !(g_lpnoiLast->dwFlags & SNCF_NOEXT) )
lstrcat(szFileSpec, g_lpnoiLast->szExt);
} else {
lstrcpy(szFileSpec, g_lpnoiLast->szMenuText);
if ( !(g_lpnoiLast->dwFlags & SNCF_NOEXT) )
lstrcat(szFileSpec, g_lpnoiLast->szExt);
PathCleanupSpec(szPath, szFileSpec);
}
if (!PathYetAnotherMakeUniqueName(szPath, szPath, szFileSpec, szFileSpec))
{
dwError = ERROR_FILENAME_EXCED_RANGE;
goto Error;
}
if (!GetNewFileInfoForExtension(g_lpnoiLast->szExt, g_lpnoiLast->szClass, &nfi, NULL))
{
dwError = ERROR_BADKEY;
goto Error;
}
switch (nfi.type) {
case NEWTYPE_NULL:
if (!NewObjMenu_TryNullFileHack(hwnd, szPath)) {
// do some sort of error
hres = ResultFromScode(E_FAIL);
}
break;
case NEWTYPE_DATA:
if (!CreateWriteCloseFile(hwnd, szPath, nfi.lpData, nfi.cbData)) {
hres = ResultFromScode(E_FAIL);
}
break;
case NEWTYPE_FILE:
hres = NewObjMenu_CopyTemplate(hwnd, szPath, &nfi);
if (hres == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
dwError = ERROR_FILE_NOT_FOUND;
goto Error;
}
break;
case NEWTYPE_COMMAND:
hres = NewObjMenu_RunCommand(hwnd, szPath, (LPTSTR)nfi.lpData);
break;
}
if (ppidl) {
if (hres == S_OK) {
*ppidl = SHSimpleIDListFromPath(szPath);
SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPath, NULL);
} else
*ppidl = NULL;
}
if (nfi.lpData)
LocalFree((HLOCAL)nfi.lpData);
if (nfi.hkeyNew)
RegCloseKey(nfi.hkeyNew);
return hres;
Error:
SHSysErrorMessageBox(hwnd, NULL, IDS_CANNOTCREATEFILE,
dwError, szFileSpec,
MB_OK | MB_ICONEXCLAMATION);
return ResultFromScode(E_FAIL);
}