Leaked source code of windows server 2003
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.
 
 
 
 
 
 

10278 lines
301 KiB

/*++
Copyright (c) 1990-1998, Microsoft Corporation All rights reserved.
Module Name:
fileopen.c
Abstract:
This module implements the Win32 fileopen dialogs.
Revision History:
--*/
// precompiled headers
#include "precomp.h"
#pragma hdrstop
#include "fileopen.h"
#include "util.h"
//
// Constant Declarations.
//
#define WNTYPE_DRIVE 1
#define MIN_DEFEXT_LEN 4
#define BMPHIOFFSET 9
//
// hbmpDirs array index values.
// Note: Two copies: for standard background, and hilite.
// Relative order is important.
//
#define OPENDIRBMP 0
#define CURDIRBMP 1
#define STDDIRBMP 2
#define FLOPPYBMP 3
#define HARDDRVBMP 4
#define CDDRVBMP 5
#define NETDRVBMP 6
#define RAMDRVBMP 7
#define REMDRVBMP 8
//
// If the following disktype is passed to AddDisk, then bTmp will be
// set to true in the DISKINFO structure (if the disk is new).
//
#define TMPNETDRV 9
#define MAXDOSFILENAMELEN (12 + 1) // 8.3 filename + 1 for NULL
//
// Maximum number of filters on one filter line.
//
#define MAXFILTERS 36
//
// File exclusion bits (don't show files of these types).
//
#define EXCLBITS (FILE_ATTRIBUTE_HIDDEN)
//
// Global Variables.
//
//
// Caching drive list.
//
extern DWORD dwNumDisks;
extern OFN_DISKINFO gaDiskInfo[MAX_DISKS];
extern TCHAR g_szInitialCurDir[MAX_PATH];
DWORD dwNumDlgs = 0;
//
// Used to update the dialogs after coming back from the net dlg button.
//
BOOL bGetNetDrivesSync = FALSE;
LPTSTR lpNetDriveSync = NULL;
BOOL bNetworkInstalled = TRUE;
//
// Following array is used to send messages to all dialog box threads
// that have requested enumeration updating from the worker
// thread. The worker thread sends off a message to each slot
// in the array that is non-NULL.
//
HWND gahDlg[MAX_THREADS];
//
// Strings for Filter Parsing.
//
const static TCHAR szSemiColonSpaceTab[] = TEXT("; \t");
const static TCHAR szSemiColonTab[] = TEXT(";\t");
//
// For WNet apis.
//
HANDLE hLNDThread = NULL;
WNDPROC lpLBProc = NULL;
WNDPROC lpOKProc = NULL;
//
// Drive/Dir bitmap dimensions.
//
LONG dxDirDrive = 0;
LONG dyDirDrive = 0;
//
// REARCHITECT: This needs to be on a per dialog basis for multi-threaded apps.
//
WORD wNoRedraw = 0;
UINT msgWOWDIRCHANGE;
UINT msgLBCHANGEA;
UINT msgSHAREVIOLATIONA;
UINT msgFILEOKA;
UINT msgLBCHANGEW;
UINT msgSHAREVIOLATIONW;
UINT msgFILEOKW;
BOOL bInChildDlg;
BOOL bFirstTime;
BOOL bInitializing;
//
// Used by the worker thread to enumerate network disk resources.
//
extern DWORD cbNetEnumBuf;
extern LPTSTR gpcNetEnumBuf;
//
// List Net Drives global variables.
//
extern HANDLE hLNDEvent;
BOOL bLNDExit = FALSE;
extern CRITICAL_SECTION g_csLocal;
extern CRITICAL_SECTION g_csNetThread;
extern DWORD g_tlsiCurDlg;
extern HDC hdcMemory;
extern HBITMAP hbmpOrigMemBmp;
HBITMAP hbmpDirDrive = HNULL;
//
// Static Declarations.
//
static WORD cLock = 0;
//
// Not valid RGB color.
//
static DWORD rgbWindowColor = 0xFF000000;
static DWORD rgbHiliteColor = 0xFF000000;
static DWORD rgbWindowText = 0xFF000000;
static DWORD rgbHiliteText = 0xFF000000;
static DWORD rgbGrayText = 0xFF000000;
static DWORD rgbDDWindow = 0xFF000000;
static DWORD rgbDDHilite = 0xFF000000;
TCHAR szCaption[TOOLONGLIMIT + WARNINGMSGLENGTH];
TCHAR szWarning[TOOLONGLIMIT + WARNINGMSGLENGTH];
LPOFNHOOKPROC glpfnFileHook = 0;
//
// REARCHITECT:
// Of course, in the case where there is a multi-threaded process
// that has > 1 threads simultaneously calling GetFileOpen, the
// following globals may cause problems.
//
static LONG dyItem = 0;
static LONG dyText;
static BOOL bChangeDir = FALSE;
static BOOL bCasePreserved;
//
// Used for formatting long unc names (ex. banyan).
//
static DWORD dwAveCharPerLine = 10;
//
// Context Help IDs.
//
const static DWORD aFileOpenHelpIDs[] =
{
edt1, IDH_OPEN_FILENAME,
stc3, IDH_OPEN_FILENAME,
lst1, IDH_OPEN_FILENAME,
stc1, IDH_OPEN_PATH,
lst2, IDH_OPEN_PATH,
stc2, IDH_OPEN_FILETYPE,
cmb1, IDH_OPEN_FILETYPE,
stc4, IDH_OPEN_DRIVES,
cmb2, IDH_OPEN_DRIVES,
chx1, IDH_OPEN_READONLY,
pshHelp, IDH_HELP,
psh14, IDH_PRINT_NETWORK,
0, 0
};
const static DWORD aFileSaveHelpIDs[] =
{
edt1, IDH_OPEN_FILENAME,
stc3, IDH_OPEN_FILENAME,
lst1, IDH_OPEN_FILENAME,
stc1, IDH_OPEN_PATH,
lst2, IDH_OPEN_PATH,
stc2, IDH_SAVE_FILETYPE,
cmb1, IDH_SAVE_FILETYPE,
stc4, IDH_OPEN_DRIVES,
cmb2, IDH_OPEN_DRIVES,
chx1, IDH_OPEN_READONLY,
pshHelp, IDH_HELP,
psh14, IDH_PRINT_NETWORK,
0, 0
};
//
// Function Prototypes.
//
SHORT
GetFileTitleX(
LPTSTR lpszFile,
LPTSTR lpszTitle,
WORD wBufSize);
BOOL
GetFileName(
POPENFILEINFO pOFI,
DLGPROC qfnDlgProc);
BOOL_PTR CALLBACK
FileOpenDlgProc(
HWND hDlg,
UINT wMsg,
WPARAM wParam,
LPARAM lParam);
BOOL_PTR CALLBACK
FileSaveDlgProc(
HWND hDlg,
UINT wMsg,
WPARAM wParam,
LPARAM lParam);
BOOL_PTR
InitFileDlg(
HWND hDlg,
WPARAM wParam,
POPENFILEINFO pOFI);
int
InitTlsValues(
POPENFILEINFO pOFI);
DWORD
InitFilterBox(
HANDLE hDlg,
LPCTSTR lpszFilter);
VOID
InitCurrentDisk(
HWND hDlg,
POPENFILEINFO pOFI,
WORD cmb);
VOID
vDeleteDirDriveBitmap();
BOOL
LoadDirDriveBitmap();
void
SetRGBValues();
BOOL
FSetUpFile();
BOOL_PTR
FileOpenCmd(
HANDLE hDlg,
WPARAM wParam,
LPARAM lParam,
POPENFILEINFO pOFI,
BOOL bSave);
BOOL
UpdateListBoxes(
HWND hDlg,
POPENFILEINFO pOFI,
LPTSTR lpszFilter,
WORD wMask);
BOOL
OKButtonPressed(
HWND hDlg,
POPENFILEINFO pOFI,
BOOL bSave);
BOOL
MultiSelectOKButton(
HWND hDlg,
POPENFILEINFO pOFI,
BOOL bSave);
LRESULT WINAPI
dwOKSubclass(
HWND hOK,
UINT msg,
WPARAM wParam,
LPARAM lParam);
LRESULT WINAPI
dwLBSubclass(
HWND hLB,
UINT msg,
WPARAM wParam,
LPARAM lParam);
int
InvalidFileWarning(
HWND hDlg,
LPTSTR szFile,
DWORD wErrCode,
UINT mbType);
VOID
MeasureItem(
HWND hDlg,
LPMEASUREITEMSTRUCT mis);
int
Signum(
int nTest);
VOID
DrawItem(
POPENFILEINFO pOFI,
HWND hDlg,
WPARAM wParam,
LPDRAWITEMSTRUCT lpdis,
BOOL bSave);
BOOL
SpacesExist(
LPTSTR szFileName);
void
StripFileName(
HANDLE hDlg,
BOOL bWowApp);
LPTSTR
lstrtok(
LPTSTR lpStr,
LPCTSTR lpDelim);
LPTSTR
ChopText(
HWND hwndDlg,
int idStatic,
LPTSTR lpch);
BOOL
FillOutPath(
HWND hList,
POPENFILEINFO pOFI);
BOOL
ShortenThePath(
LPTSTR pPath);
int
FListAll(
POPENFILEINFO pOFI,
HWND hDlg,
LPTSTR pszSpec,
int cchSpec);
int
ChangeDir(
HWND hDlg,
LPCTSTR lpszDir,
BOOL bForce,
BOOL bError);
BOOL
IsFileSystemCasePreserving(
LPTSTR lpszDisk);
BOOL
IsLFNDriveX(
HWND hDlg,
LPTSTR szPath);
int
DiskAddedPreviously(
TCHAR wcDrive,
LPTSTR lpszName);
int
AddDisk(
TCHAR wcDrive,
LPTSTR lpName,
LPTSTR lpProvider,
DWORD dwType);
VOID
EnableDiskInfo(
BOOL bValid,
BOOL bDoUnc);
VOID
FlushDiskInfoToCmb2();
BOOL
CallNetDlg(
HWND hWnd);
UINT
GetDiskType(
LPTSTR lpszDisk);
DWORD
GetUNCDirectoryFromLB(
HWND hDlg,
WORD nLB,
POPENFILEINFO pOFI);
VOID
SelDisk(
HWND hDlg,
LPTSTR lpszDisk);
VOID
LNDSetEvent(
HWND hDlg);
VOID
UpdateLocalDrive(
LPTSTR szDrive,
BOOL bGetVolName);
VOID
GetNetDrives(
DWORD dwScope);
VOID
ListNetDrivesHandler();
VOID
LoadDrives(
HWND hDlg);
DWORD
GetDiskIndex(
DWORD dwDriveType);
VOID
CleanUpFile();
VOID
FileOpenAbort();
VOID
TermFile();
//VOID // prototype in fileopen.h
//ThunkOpenFileNameA2WDelayed(
// POPENFILEINFO pOFI);
//BOOL // prototype in fileopen.h
//ThunkOpenFileNameA2W(
// POPENFILEINFO pOFI);
//BOOL // prototype in fileopen.h
//ThunkOpenFileNameW2A(
// POPENFILEINFO pOFI);
BOOL
GenericGetFileNameA(
LPOPENFILENAMEA pOFNA,
DLGPROC qfnDlgProc);
LPWSTR
ThunkANSIStrToWIDE(
LPWSTR pDestW,
LPSTR pSrcA,
int cChars);
LPWSTR
ThunkMultiANSIStrToWIDE(
LPWSTR pDestW,
LPSTR pSrcA,
int cChars);
BOOL
Multi_strcpyAtoW(
LPWSTR pDestW,
LPCSTR pSrcA,
int cChars);
INT
Multi_strlenA(
LPCSTR str);
// The Win9x code relies on calling SetCurrentDirectory wherever SheChangeDirEx is
// called. (Ideally SheChangeDirExA should be implemented).
// Ref: NT5 bug 161292 and Millenium bug 95478
////////////////////////////////////////////////////////////////////////////
//
// GetFileTitleA
//
// ANSI entry point for GetFileTitle when this code is built UNICODE.
//
////////////////////////////////////////////////////////////////////////////
SHORT WINAPI GetFileTitleA(
LPCSTR lpszFileA,
LPSTR lpszTitleA,
WORD cbBuf)
{
LPWSTR lpszFileW;
LPWSTR lpszTitleW;
BOOL fResult;
DWORD cbLen;
//
// Init File string.
//
if (lpszFileA)
{
cbLen = lstrlenA(lpszFileA) + 1;
if (!(lpszFileW = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
return (FALSE);
}
else
{
SHAnsiToUnicode((LPSTR)lpszFileA,lpszFileW,cbLen );
}
}
else
{
lpszFileW = NULL;
}
if (!(lpszTitleW = (LPWSTR)LocalAlloc(LPTR, (cbBuf * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
if (lpszFileW)
{
LocalFree(lpszFileW);
}
return (FALSE);
}
if (!(fResult = GetFileTitleW(lpszFileW, lpszTitleW, cbBuf)))
{
SHUnicodeToAnsi(lpszTitleW,lpszTitleA,cbBuf);
}
else if (fResult > 0)
{
//
// Buffer is too small - Ansi size needed (including null terminator).
// Get the offset to the filename.
//
SHORT nNeeded = (SHORT)(INT)LOWORD(ParseFile(lpszFileW, TRUE, FALSE, FALSE));
LPSTR lpA = (LPSTR)lpszFileA;
lpA += WideCharToMultiByte( CP_ACP,
0,
lpszFileW,
nNeeded,
NULL,
0,
NULL,
NULL );
fResult = lstrlenA(lpA) + 1;
if (fResult <= cbBuf)
{
// There is enough room.
EVAL(SUCCEEDED(StringCchCopyA(lpszTitleA, cbBuf, lpA)));
fResult = 0;
}
}
//
// Clean up memory.
//
LocalFree(lpszTitleW);
if (lpszFileW)
{
LocalFree(lpszFileW);
}
return ((SHORT)fResult);
}
////////////////////////////////////////////////////////////////////////////
//
// GetFileTitle
//
// The GetFileTitle function returns the name of the file identified
// by the lpCFile parameter. This is useful if the file name was
// received via some method other than GetOpenFileName
// (e.g. command line, drag drop).
//
// Returns: 0 on success
// < 0, Parsing failure (invalid file name)
// > 0, buffer too small, size needed (including NULL terminator)
//
////////////////////////////////////////////////////////////////////////////
SHORT WINAPI GetFileTitle(
LPCTSTR lpCFile,
LPTSTR lpTitle,
WORD cbBuf)
{
LPTSTR lpFile;
DWORD cchLen;
SHORT fResult;
//
// Init File string.
//
if (lpCFile)
{
cchLen = lstrlen(lpCFile) + 1;
if (!(lpFile = (LPTSTR)LocalAlloc(LPTR, (cchLen * sizeof(TCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
return (FALSE);
}
else
{
EVAL(SUCCEEDED(StringCchCopy(lpFile, cchLen, lpCFile))); // Always big enough.
}
}
else
{
lpFile = NULL;
}
fResult = GetFileTitleX(lpFile, lpTitle, cbBuf);
//
// Clean up memory.
//
if (lpFile)
{
LocalFree(lpFile);
}
return (fResult);
}
////////////////////////////////////////////////////////////////////////////
//
// GetFileTitleX
//
// Worker routine for the GetFileTitle api.
//
// Assumes: lpszFile points to NULL terminated DOS filename (may have path)
// lpszTitle points to buffer to receive NULL terminated file title
// wBufSize is the size of buffer pointed to by lpszTitle
//
// Returns: 0 on success
// < 0, Parsing failure (invalid file name)
// > 0, buffer too small, size needed (including NULL terminator)
//
////////////////////////////////////////////////////////////////////////////
SHORT GetFileTitleX(
LPTSTR lpszFile,
LPTSTR lpszTitle,
WORD cchBufSize)
{
SHORT nNeeded;
LPTSTR lpszPtr;
//
// New 32 bit apps will get a title based on the user's preferences.
//
if ((GetProcessVersion(0) >= 0x040000) && !(CDGetAppCompatFlags() & CDACF_FILETITLE))
{
SHFILEINFO info;
DWORD_PTR result;
if (!lpszFile || !*lpszFile)
{
return (PARSE_EMPTYSTRING);
}
//
// If we have a root directory name (eg. c:\), then we need to go
// to the old implementation so that it will return -1.
// SHGetFileInfo will return the display name for the directory
// (which is the volume name). This is incompatible with Win95
// and previous versions of NT.
//
if ((lstrlen(lpszFile) != 3) ||
(lpszFile[1] != CHAR_COLON) || (!ISBACKSLASH(lpszFile, 2)))
{
result = SHGetFileInfo( lpszFile,
FILE_ATTRIBUTE_NORMAL,
&info,
sizeof(info),
SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES );
if (result && (*info.szDisplayName))
{
UINT uDisplayLen = lstrlen(info.szDisplayName);
//
// If no buffer or insufficient size, return the required chars.
// Original GetFileTitle API did not copy on failure.
//
if (!lpszTitle || (uDisplayLen >= (UINT)cchBufSize))
{
return ( (SHORT)(uDisplayLen + 1) );
}
//
// We know it fits
//
EVAL(SUCCEEDED(StringCchCopy(lpszTitle, cchBufSize, info.szDisplayName)));
return (0);
}
}
}
//
// Use the old implementation.
//
nNeeded = (SHORT)(int)LOWORD(ParseFile(lpszFile, TRUE, FALSE, FALSE));
if (nNeeded >= 0)
{
//
// Is the filename valid?
//
lpszPtr = lpszFile + nNeeded;
if ((nNeeded = (SHORT)lstrlen(lpszPtr) + 1) <= (int)cchBufSize)
{
//
// ParseFile() fails if wildcards in directory, but OK if in name.
// Since they arent OK here, the check is needed here.
//
if (StrChr(lpszPtr, CHAR_STAR) || StrChr(lpszPtr, CHAR_QMARK))
{
nNeeded = PARSE_WILDCARDINFILE;
}
else
{
EVAL(SUCCEEDED(StringCchCopy(lpszTitle, cchBufSize, lpszPtr))); // We already checked that it's big enough.
//
// Remove trailing spaces.
//
lpszPtr = lpszTitle + lstrlen(lpszTitle) - 1;
while (*lpszPtr && *lpszPtr == CHAR_SPACE)
{
*lpszPtr-- = CHAR_NULL;
}
nNeeded = 0;
}
}
}
return (nNeeded);
}
////////////////////////////////////////////////////////////////////////////
//
// GetOpenFileNameA
//
// ANSI entry point for GetOpenFileName when this code is built UNICODE.
//
////////////////////////////////////////////////////////////////////////////
BOOL WINAPI GetOpenFileNameA(
LPOPENFILENAMEA pOFNA)
{
if (!pOFNA)
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
return ( GenericGetFileNameA(pOFNA, FileOpenDlgProc) );
}
////////////////////////////////////////////////////////////////////////////
//
// GetOpenFileName
//
// The GetOpenFileName function creates a system-defined dialog box
// that enables the user to select a file to open.
//
// Returns: TRUE if user specified name
// FALSE if not
//
////////////////////////////////////////////////////////////////////////////
BOOL WINAPI GetOpenFileName(
LPOPENFILENAME pOFN)
{
OPENFILEINFO OFI;
ZeroMemory(&OFI, sizeof(OPENFILEINFO));
if (!pOFN)
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
OFI.pOFN = pOFN;
OFI.ApiType = COMDLG_WIDE;
OFI.iVersion = OPENFILEVERSION;
return (GetFileName(&OFI, FileOpenDlgProc));
}
////////////////////////////////////////////////////////////////////////////
//
// GetSaveFileNameA
//
// ANSI entry point for GetSaveFileName when this code is built UNICODE.
//
////////////////////////////////////////////////////////////////////////////
BOOL WINAPI GetSaveFileNameA(
LPOPENFILENAMEA pOFNA)
{
return (GenericGetFileNameA(pOFNA, FileSaveDlgProc));
}
////////////////////////////////////////////////////////////////////////////
//
// GetSaveFileName
//
// The GetSaveFileName function creates a system-defined dialog box
// that enables the user to select a file to save.
//
// Returns: TRUE if user desires to save file and gave a proper name
// FALSE if not
//
////////////////////////////////////////////////////////////////////////////
BOOL WINAPI GetSaveFileName(
LPOPENFILENAME pOFN)
{
OPENFILEINFO OFI;
ZeroMemory(&OFI, sizeof(OPENFILEINFO));
OFI.pOFN = pOFN;
OFI.ApiType = COMDLG_WIDE;
OFI.iVersion = OPENFILEVERSION;
return ( GetFileName(&OFI, FileSaveDlgProc) );
}
////////////////////////////////////////////////////////////////////////////
//
// GetFileName
//
// This is the meat of both GetOpenFileName and GetSaveFileName.
//
// Returns: TRUE if user specified name
// FALSE if not
//
////////////////////////////////////////////////////////////////////////////
BOOL GetFileName(
POPENFILEINFO pOFI,
DLGPROC qfnDlgProc)
{
LPOPENFILENAME pOFN = pOFI->pOFN;
INT_PTR iRet = 0;
LPTSTR lpDlg;
HANDLE hRes, hDlgTemplate;
WORD wErrorMode;
HDC hdcScreen;
HBITMAP hbmpTemp;
LPCURDLG lpCurDlg;
static fFirstTime = TRUE;
UINT uiWOWFlag = 0;
LANGID LangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
if (!pOFN)
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
if (pOFN->lStructSize == OPENFILENAME_SIZE_VERSION_400)
{
// Note: We do not want to make a copy of the OFN structure passed in.
// This confuses all MFC based apps since they query
// MFC and end up getting stale data if we make a copy and endup updating only the
// copy until Comdlg api returns.
pOFI->iVersion = OPENFILEVERSION_NT4;
}
if ((pOFN->lStructSize != sizeof(OPENFILENAME)) &&
(pOFN->lStructSize != OPENFILENAME_SIZE_VERSION_400)
)
{
StoreExtendedError(CDERR_STRUCTSIZE);
return (FALSE);
}
if (pOFN->nMaxFile == 0)
{
//Bail out for NULL lpstrFile Only for NT5 and above applications
if (!IS16BITWOWAPP(pOFN) && (pOFI->iVersion >= OPENFILEVERSION_NT5))
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
}
// Some parameter validation... make sure none of the buffers are larger
// than the max handled by the strsafe string functions we use.
if ((pOFN->nMaxFile > STRSAFE_MAX_CCH) ||
(pOFN->lpstrFileTitle && (pOFN->nMaxFileTitle > STRSAFE_MAX_CCH)) ||
((pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter) && (pOFN->nMaxCustFilter > STRSAFE_MAX_CCH)))
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
//
// See if the application should get the new look.
//
// Do not allow the new look if they have hooks, templates, or
// multi select without the OFN_EXPLORER bit.
//
// Also don't allow the new look if we are in the context of
// a 16 bit process.
//
if ( ((pOFN->Flags & OFN_EXPLORER) ||
(!(pOFN->Flags & (OFN_ENABLEHOOK |
OFN_ENABLETEMPLATE |
OFN_ENABLETEMPLATEHANDLE |
OFN_ALLOWMULTISELECT)))) &&
(!IS16BITWOWAPP(pOFN)) )
{
BOOL fRet;
//
// To be used by the thunking routines for multi selection.
//
pOFI->bUseNewDialog = TRUE;
//
// Show the new explorer look.
//
StoreExtendedError(0);
g_bUserPressedCancel = FALSE;
if (qfnDlgProc == FileOpenDlgProc)
{
fRet = (NewGetOpenFileName(pOFI));
}
else
{
fRet = (NewGetSaveFileName(pOFI));
}
return fRet;
}
if (fFirstTime)
{
//
// Create a DC that is compatible with the screen and find the
// handle of the null bitmap.
//
hdcScreen = GetDC(HNULL);
if (!hdcScreen)
{
goto CantInit;
}
hdcMemory = CreateCompatibleDC(hdcScreen);
if (!hdcMemory)
{
goto ReleaseScreenDC;
}
hbmpTemp = CreateCompatibleBitmap(hdcMemory, 1, 1);
if (!hbmpTemp)
{
goto ReleaseMemDC;
}
hbmpOrigMemBmp = SelectObject(hdcMemory, hbmpTemp);
if (!hbmpOrigMemBmp)
{
goto ReleaseMemDC;
}
SelectObject(hdcMemory, hbmpOrigMemBmp);
DeleteObject(hbmpTemp);
ReleaseDC(HNULL, hdcScreen);
fFirstTime = FALSE;
}
if (pOFN->Flags & OFN_ENABLEHOOK)
{
if (!pOFN->lpfnHook)
{
StoreExtendedError(CDERR_NOHOOK);
return (FALSE);
}
}
else
{
pOFN->lpfnHook = NULL;
}
HourGlass(TRUE);
StoreExtendedError(0);
//
// Force re-compute for font changes between calls.
//
dyItem = dyText = 0;
g_bUserPressedCancel = FALSE;
if (!FSetUpFile())
{
StoreExtendedError(CDERR_INITIALIZATION);
goto TERMINATE;
}
if (pOFN->Flags & OFN_ENABLETEMPLATE)
{
if (!(hRes = FindResource( pOFN->hInstance,
pOFN->lpTemplateName,
RT_DIALOG )))
{
StoreExtendedError(CDERR_FINDRESFAILURE);
goto TERMINATE;
}
if (!(hDlgTemplate = LoadResource(pOFN->hInstance, hRes)))
{
StoreExtendedError(CDERR_LOADRESFAILURE);
goto TERMINATE;
}
LangID = GetDialogLanguage(pOFN->hwndOwner, hDlgTemplate);
}
else if (pOFN->Flags & OFN_ENABLETEMPLATEHANDLE)
{
hDlgTemplate = pOFN->hInstance;
LangID = GetDialogLanguage(pOFN->hwndOwner, hDlgTemplate);
}
else
{
if (pOFN->Flags & OFN_ALLOWMULTISELECT)
{
lpDlg = MAKEINTRESOURCE(MULTIFILEOPENORD);
}
else
{
lpDlg = MAKEINTRESOURCE(FILEOPENORD);
}
LangID = GetDialogLanguage(pOFN->hwndOwner, NULL);
if (!(hRes = FindResourceExFallback(g_hinst, RT_DIALOG, lpDlg, LangID)))
{
StoreExtendedError(CDERR_FINDRESFAILURE);
goto TERMINATE;
}
if (!(hDlgTemplate = LoadResource(g_hinst, hRes)))
{
StoreExtendedError(CDERR_LOADRESFAILURE);
goto TERMINATE;
}
}
//
// Warning! Warning! Warning!
//
// We have to set g_tlsLangID before any call for CDLoadString
//
TlsSetValue(g_tlsLangID, (LPVOID) LangID);
//
// No kernel network error dialogs.
//
wErrorMode = (WORD)SetErrorMode(SEM_NOERROR);
SetErrorMode(SEM_NOERROR | wErrorMode);
if (LockResource(hDlgTemplate))
{
if (pOFN->Flags & OFN_ENABLEHOOK)
{
glpfnFileHook = GETHOOKFN(pOFN);
}
if (IS16BITWOWAPP(pOFN))
{
uiWOWFlag = SCDLG_16BIT;
}
iRet = DialogBoxIndirectParamAorW( g_hinst,
(LPDLGTEMPLATE)hDlgTemplate,
pOFN->hwndOwner,
qfnDlgProc,
(DWORD_PTR)pOFI,
uiWOWFlag );
if (iRet == -1 || ((iRet == 0) && (!g_bUserPressedCancel) && (!GetStoredExtendedError())))
{
StoreExtendedError(CDERR_DIALOGFAILURE);
}
else
{
FileOpenAbort();
}
glpfnFileHook = 0;
}
else
{
StoreExtendedError(CDERR_LOCKRESFAILURE);
goto TERMINATE;
}
SetErrorMode(wErrorMode);
if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
{
// restore the thread list to the previous dialog (if any)
TlsSetValue(g_tlsiCurDlg, (LPVOID)lpCurDlg->next);
LocalFree(lpCurDlg->lpstrCurDir);
LocalFree(lpCurDlg);
}
TERMINATE:
CleanUpFile();
HourGlass(FALSE);
return (iRet == IDOK);
ReleaseMemDC:
DeleteDC(hdcMemory);
ReleaseScreenDC:
ReleaseDC(HNULL, hdcScreen);
CantInit:
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// FileHookCmd
//
// Called when a hook function processes a WM_COMMAND message.
// Called by FileOpenDlgProc and FileSaveDlgProc.
//
////////////////////////////////////////////////////////////////////////////
BOOL FileHookCmd(
HANDLE hDlg,
WPARAM wParam,
LPARAM lParam,
POPENFILEINFO pOFI)
{
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case ( IDCANCEL ) :
{
//
// Set global flag stating that the
// user pressed cancel.
//
g_bUserPressedCancel = TRUE;
// Fall Thru...
}
case ( IDOK ) :
case ( IDABORT ) :
{
//
// Apps that side-effect these messages may
// not have their internal unicode strings
// updated. They may also forget to gracefully
// exit the network enum'ing worker thread.
//
if (pOFI->ApiType == COMDLG_ANSI)
{
ThunkOpenFileNameA2W(pOFI);
}
break;
}
case ( cmb1 ) :
case ( cmb2 ) :
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case ( MYCBN_DRAW ) :
case ( MYCBN_LIST ) :
case ( MYCBN_REPAINT ) :
case ( MYCBN_CHANGEDIR ) :
{
//
// In case an app has a hook, and returns
// true for processing WM_COMMAND messages,
// we still have to worry about our
// internal message that came through via
// WM_COMMAND.
//
FileOpenCmd( hDlg,
wParam,
lParam,
pOFI,
FALSE );
break;
}
}
break;
}
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// FileOpenDlgProc
//
// Gets the name of a file to open from the user.
//
// edt1 = file name
// lst1 = list of files in current directory matching current pattern
// cmb1 = lists file patterns
// stc1 = is current directory
// lst2 = lists directories on current drive
// cmb2 = lists drives
// IDOK = is Open pushbutton
// IDCANCEL = is Cancel pushbutton
// chx1 = is for opening read only files
//
// Returns the normal dialog proc values.
//
////////////////////////////////////////////////////////////////////////////
BOOL_PTR CALLBACK FileOpenDlgProc(
HWND hDlg,
UINT wMsg,
WPARAM wParam,
LPARAM lParam)
{
POPENFILEINFO pOFI;
BOOL_PTR bRet, bHookRet;
if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
{
if (pOFI->pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFI->pOFN);
bHookRet = (*lpfnHook)(hDlg, wMsg, wParam, lParam);
if (bHookRet)
{
if (wMsg == WM_COMMAND)
{
return (FileHookCmd(hDlg, wParam, lParam, pOFI));
}
return (bHookRet);
}
}
}
else if (glpfnFileHook &&
(wMsg != WM_INITDIALOG) &&
(bHookRet = (*glpfnFileHook)(hDlg, wMsg, wParam, lParam)))
{
return (bHookRet);
}
switch (wMsg)
{
case ( WM_INITDIALOG ) :
{
pOFI = (POPENFILEINFO)lParam;
SetProp(hDlg, FILEPROP, (HANDLE)pOFI);
glpfnFileHook = 0;
//
// If we are being called from a Unicode app, turn off
// the ES_OEMCONVERT style on the filename edit control.
//
// if (pOFI->ApiType == COMDLG_WIDE)
{
LONG lStyle;
HWND hEdit = GetDlgItem(hDlg, edt1);
//
// Grab the window style.
//
lStyle = GetWindowLong(hEdit, GWL_STYLE);
//
// If the window style bits include ES_OEMCONVERT,
// remove this flag and reset the style.
//
if (lStyle & ES_OEMCONVERT)
{
lStyle &= ~ES_OEMCONVERT;
SetWindowLong(hEdit, GWL_STYLE, lStyle);
}
}
bInitializing = TRUE;
bRet = InitFileDlg(hDlg, wParam, pOFI);
bInitializing = FALSE;
HourGlass(FALSE);
return (bRet);
break;
}
case ( WM_ACTIVATE ) :
{
if (!bInChildDlg)
{
if (bFirstTime == TRUE)
{
bFirstTime = FALSE;
}
else if (wParam)
{
//
// If becoming active.
//
LNDSetEvent(hDlg);
}
}
return (FALSE);
break;
}
case ( WM_MEASUREITEM ) :
{
MeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam);
break;
}
case ( WM_DRAWITEM ) :
{
if (wNoRedraw < 2)
{
DrawItem(pOFI, hDlg, wParam, (LPDRAWITEMSTRUCT)lParam, FALSE);
}
break;
}
case ( WM_SYSCOLORCHANGE ) :
{
SetRGBValues();
LoadDirDriveBitmap();
break;
}
case ( WM_COMMAND ) :
{
return (FileOpenCmd(hDlg, wParam, lParam, pOFI, FALSE));
break;
}
case ( WM_SETFOCUS ) :
{
//
// This logic used to be in CBN_SETFOCUS in fileopencmd,
// but CBN_SETFOCUS is called whenever there is a click on
// the List Drives combo. This causes the worker thread
// to start up and flicker when the combo box is refreshed.
//
// But, refreshes are only needed when someone focuses out of
// the common dialog and then back in (unless someone is logged
// in remote, or there is a background thread busy connecting!)
// so fix the flicker by moving the logic here.
//
if (!wNoRedraw)
{
LNDSetEvent(hDlg);
}
return (FALSE);
break;
}
case ( WM_HELP ) :
{
if (IsWindowEnabled(hDlg))
{
WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
NULL,
HELP_WM_HELP,
(ULONG_PTR)(LPTSTR)aFileOpenHelpIDs );
}
break;
}
case ( WM_CONTEXTMENU ) :
{
if (IsWindowEnabled(hDlg))
{
WinHelp( (HWND)wParam,
NULL,
HELP_CONTEXTMENU,
(ULONG_PTR)(LPVOID)aFileOpenHelpIDs );
}
break;
}
default :
{
return (FALSE);
}
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// FileSaveDlgProc
//
// Obtains the name of the file that the user wants to save.
//
// Returns the normal dialog proc values.
//
////////////////////////////////////////////////////////////////////////////
BOOL_PTR CALLBACK FileSaveDlgProc(
HWND hDlg,
UINT wMsg,
WPARAM wParam,
LPARAM lParam)
{
POPENFILEINFO pOFI;
BOOL_PTR bRet, bHookRet;
TCHAR szTitle[64];
if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
{
if (pOFI->pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFI->pOFN);
bHookRet = (*lpfnHook)(hDlg, wMsg, wParam, lParam);
if (bHookRet)
{
if (wMsg == WM_COMMAND)
{
return (FileHookCmd(hDlg, wParam, lParam, pOFI));
}
return (bHookRet);
}
}
}
else if (glpfnFileHook &&
(wMsg != WM_INITDIALOG) &&
(bHookRet = (*glpfnFileHook)(hDlg, wMsg, wParam, lParam)))
{
return (bHookRet);
}
switch (wMsg)
{
case ( WM_INITDIALOG ) :
{
pOFI = (POPENFILEINFO)lParam;
if (!(pOFI->pOFN->Flags &
(OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
{
CDLoadString(g_hinst, iszFileSaveTitle, szTitle, ARRAYSIZE(szTitle));
SetWindowText(hDlg, szTitle);
CDLoadString(g_hinst, iszSaveFileAsType, szTitle, ARRAYSIZE(szTitle));
SetDlgItemText(hDlg, stc2, szTitle);
}
glpfnFileHook = 0;
SetProp(hDlg, FILEPROP, (HANDLE)pOFI);
//
// If we are being called from a Unicode app, turn off
// the ES_OEMCONVERT style on the filename edit control.
//
// if (pOFI->ApiType == COMDLG_WIDE)
{
LONG lStyle;
HWND hEdit = GetDlgItem(hDlg, edt1);
//
// Grab the window style.
//
lStyle = GetWindowLong(hEdit, GWL_STYLE);
//
// If the window style bits include ES_OEMCONVERT,
// remove this flag and reset the style.
//
if (lStyle & ES_OEMCONVERT)
{
lStyle &= ~ES_OEMCONVERT;
SetWindowLong (hEdit, GWL_STYLE, lStyle);
}
}
bInitializing = TRUE;
bRet = InitFileDlg(hDlg, wParam, pOFI);
bInitializing = FALSE;
HourGlass(FALSE);
return (bRet);
break;
}
case ( WM_ACTIVATE ) :
{
if (!bInChildDlg)
{
if (bFirstTime == TRUE)
{
bFirstTime = FALSE;
}
else if (wParam)
{
//
// If becoming active.
//
if (!wNoRedraw)
{
LNDSetEvent(hDlg);
}
}
}
return (FALSE);
break;
}
case ( WM_MEASUREITEM ) :
{
MeasureItem(hDlg, (LPMEASUREITEMSTRUCT)lParam);
break;
}
case ( WM_DRAWITEM ) :
{
if (wNoRedraw < 2)
{
DrawItem(pOFI, hDlg, wParam, (LPDRAWITEMSTRUCT)lParam, TRUE);
}
break;
}
case ( WM_SYSCOLORCHANGE ) :
{
SetRGBValues();
LoadDirDriveBitmap();
break;
}
case ( WM_COMMAND ) :
{
return (FileOpenCmd(hDlg, wParam, lParam, pOFI, TRUE));
break;
}
case ( WM_SETFOCUS ) :
{
//
// This logic used to be in CBN_SETFOCUS in fileopencmd,
// but CBN_SETFOCUS is called whenever there is a click on
// the List Drives combo. This causes the worker thread
// to start up and flicker when the combo box is refreshed.
//
// But, refreshes are only needed when someone focuses out of
// the common dialog and then back in (unless someone is logged
// in remote, or there is a background thread busy connecting!)
// so fix the flicker by moving the logic here.
//
if (!wNoRedraw)
{
LNDSetEvent(hDlg);
}
return (FALSE);
break;
}
case ( WM_HELP ) :
{
if (IsWindowEnabled(hDlg))
{
WinHelp( (HWND)((LPHELPINFO)lParam)->hItemHandle,
NULL,
HELP_WM_HELP,
(ULONG_PTR)(LPTSTR)aFileSaveHelpIDs );
}
break;
}
case ( WM_CONTEXTMENU ) :
{
if (IsWindowEnabled(hDlg))
{
WinHelp( (HWND)wParam,
NULL,
HELP_CONTEXTMENU,
(ULONG_PTR)(LPVOID)aFileSaveHelpIDs );
}
break;
}
default :
{
return (FALSE);
}
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// InitFileDlg
//
////////////////////////////////////////////////////////////////////////////
BOOL_PTR InitFileDlg(
HWND hDlg,
WPARAM wParam,
POPENFILEINFO pOFI)
{
DWORD lRet, nFilterIndex;
LPOPENFILENAME pOFN = pOFI->pOFN;
int nFileOffset, nExtOffset;
RECT rRect;
RECT rLbox;
BOOL_PTR bRet;
if (!InitTlsValues(pOFI))
{
//
// The extended error is set inside of the above call.
//
EndDialog(hDlg, FALSE);
return (FALSE);
}
lpLBProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(hDlg, lst2), GWLP_WNDPROC);
lpOKProc = (WNDPROC)GetWindowLongPtr(GetDlgItem(hDlg, IDOK), GWLP_WNDPROC);
if (!lpLBProc || !lpOKProc)
{
StoreExtendedError(FNERR_SUBCLASSFAILURE);
EndDialog(hDlg, FALSE);
return (FALSE);
}
//
// Save original directory for later restoration if necessary.
//
*pOFI->szCurDir = 0;
GetCurrentDirectory(MAX_FULLPATHNAME + 1, pOFI->szCurDir);
//
// Check out if the filename contains a path. If so, override whatever
// is contained in lpstrInitialDir. Chop off the path and put up only
// the filename.
//
if ( pOFN->lpstrFile &&
*pOFN->lpstrFile &&
!(pOFN->Flags & OFN_NOVALIDATE) )
{
if (DBL_BSLASH(pOFN->lpstrFile + 2) &&
((*(pOFN->lpstrFile + 1) == CHAR_COLON)))
{
// Turns "c:\\foo\bar" into "\\foo\bar" (in lpstrFile)
// Some backward compat thing?
StringCopyOverlap(pOFN->lpstrFile, pOFN->lpstrFile + 2);
}
lRet = ParseFile(pOFN->lpstrFile, TRUE, IS16BITWOWAPP(pOFN), FALSE);
nFileOffset = (int)(SHORT)LOWORD(lRet);
nExtOffset = (int)(SHORT)HIWORD(lRet);
//
// Is the filename invalid?
//
if ( (nFileOffset < 0) &&
(nFileOffset != PARSE_EMPTYSTRING) &&
(pOFN->lpstrFile[nExtOffset] != CHAR_SEMICOLON) )
{
StoreExtendedError(FNERR_INVALIDFILENAME);
EndDialog(hDlg, FALSE);
return (FALSE);
}
}
pOFN->Flags &= ~(OFN_FILTERDOWN | OFN_DRIVEDOWN | OFN_DIRSELCHANGED);
pOFI->idirSub = 0;
if (!(pOFN->Flags & OFN_SHOWHELP))
{
HWND hHelp;
EnableWindow(hHelp = GetDlgItem(hDlg, pshHelp), FALSE);
//
// Move the window out of this spot so that no overlap will be
// detected.
//
MoveWindow(hHelp, -8000, -8000, 20, 20, FALSE);
ShowWindow(hHelp, SW_HIDE);
}
if (pOFN->Flags & OFN_CREATEPROMPT)
{
pOFN->Flags |= (OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST);
}
else if (pOFN->Flags & OFN_FILEMUSTEXIST)
{
pOFN->Flags |= OFN_PATHMUSTEXIST;
}
if (pOFN->Flags & OFN_HIDEREADONLY)
{
HWND hReadOnly;
EnableWindow(hReadOnly = GetDlgItem(hDlg, chx1), FALSE);
//
// Move the window out of this spot so that no overlap will be
// detected.
//
MoveWindow(hReadOnly, -8000, -8000, 20, 20, FALSE);
ShowWindow(hReadOnly, SW_HIDE);
}
else
{
CheckDlgButton(hDlg, chx1, (pOFN->Flags & OFN_READONLY) != 0);
}
SendDlgItemMessage(hDlg, edt1, EM_LIMITTEXT, (WPARAM)MAX_PATH, 0L);
//
// Insert file specs into cmb1.
// Custom filter first.
// Must also check if filter contains anything.
//
if ( pOFN->lpstrFile &&
(StrChr(pOFN->lpstrFile, CHAR_STAR) ||
StrChr(pOFN->lpstrFile, CHAR_QMARK)) )
{
StringCchCopyEx(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFN->lpstrFile, NULL, NULL, STRSAFE_NULL_ON_FAILURE);
}
else
{
pOFI->szLastFilter[0] = CHAR_NULL;
}
if (pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter)
{
SHORT nLength;
SendDlgItemMessage( hDlg,
cmb1,
CB_INSERTSTRING,
0,
(LONG_PTR)pOFN->lpstrCustomFilter );
nLength = (SHORT)(lstrlen(pOFN->lpstrCustomFilter) + 1);
SendDlgItemMessage( hDlg,
cmb1,
CB_SETITEMDATA,
0,
(LONG)(nLength) );
SendDlgItemMessage( hDlg,
cmb1,
CB_LIMITTEXT,
(WPARAM)(pOFN->nMaxCustFilter),
0L );
if (pOFI->szLastFilter[0] == CHAR_NULL)
{
StringCchCopyEx(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFN->lpstrCustomFilter + nLength, NULL, NULL, STRSAFE_NULL_ON_FAILURE);
}
}
else
{
//
// Given no custom filter, the index will be off by one.
//
if (pOFN->nFilterIndex != 0)
{
pOFN->nFilterIndex--;
}
}
//
// Listed filters next.
//
if (pOFN->lpstrFilter && *pOFN->lpstrFilter)
{
if (pOFN->nFilterIndex > InitFilterBox(hDlg, pOFN->lpstrFilter))
{
pOFN->nFilterIndex = 0;
}
}
else
{
pOFN->nFilterIndex = 0;
}
pOFI->szSpecCur[0] = CHAR_NULL;
//
// If an entry exists, select the one indicated by nFilterIndex.
//
if ((pOFN->lpstrFilter && *pOFN->lpstrFilter) ||
(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter))
{
LPCTSTR lpFilter;
SendDlgItemMessage( hDlg,
cmb1,
CB_SETCURSEL,
(WPARAM)(pOFN->nFilterIndex),
0L );
nFilterIndex = pOFN->nFilterIndex;
SendMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS( cmb1,
GetDlgItem(hDlg, cmb1),
MYCBN_DRAW ) );
pOFN->nFilterIndex = nFilterIndex;
if (pOFN->nFilterIndex ||
!(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter))
{
lpFilter = pOFN->lpstrFilter +
SendDlgItemMessage( hDlg,
cmb1,
CB_GETITEMDATA,
(WPARAM)pOFN->nFilterIndex,
0L );
}
else
{
lpFilter = pOFN->lpstrCustomFilter +
lstrlen(pOFN->lpstrCustomFilter) + 1;
}
if (*lpFilter)
{
TCHAR szText[MAX_FULLPATHNAME];
if (SUCCEEDED(StringCchCopy(szText, ARRAYSIZE(szText), lpFilter)))
{
//
// Filtering is case-insensitive.
//
CharLower(szText);
if (pOFI->szLastFilter[0] == CHAR_NULL)
{
EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), szText)));
}
if (!(pOFN->lpstrFile && *pOFN->lpstrFile))
{
SetDlgItemText(hDlg, edt1, szText);
}
}
}
}
InitCurrentDisk(hDlg, pOFI, cmb2);
bFirstTime = TRUE;
bInChildDlg = FALSE;
SendMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS(cmb2, GetDlgItem(hDlg, cmb2), MYCBN_DRAW) );
SendMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS(cmb2, GetDlgItem(hDlg, cmb2), MYCBN_LIST) );
if (pOFN->lpstrFile && *pOFN->lpstrFile)
{
TCHAR szText[MAX_FULLPATHNAME];
lRet = ParseFile( pOFN->lpstrFile,
IsLFNDriveX(hDlg, pOFN->lpstrFile),
IS16BITWOWAPP(pOFN),
FALSE );
nFileOffset = (int)(SHORT)LOWORD(lRet);
nExtOffset = (int)(SHORT)HIWORD(lRet);
//
// Is the filename invalid?
//
if ( !(pOFN->Flags & OFN_NOVALIDATE) &&
(nFileOffset < 0) &&
(nFileOffset != PARSE_EMPTYSTRING) &&
(pOFN->lpstrFile[nExtOffset] != CHAR_SEMICOLON) )
{
StoreExtendedError(FNERR_INVALIDFILENAME);
EndDialog(hDlg, FALSE);
return (FALSE);
}
if (FAILED(StringCchCopy(szText, ARRAYSIZE(szText), pOFN->lpstrFile)) && !(pOFN->Flags & OFN_NOVALIDATE))
{
StoreExtendedError(FNERR_INVALIDFILENAME);
EndDialog(hDlg, FALSE);
return (FALSE);
}
SetDlgItemText(hDlg, edt1, szText);
}
SetWindowLongPtr(GetDlgItem(hDlg, lst2), GWLP_WNDPROC, (LONG_PTR)dwLBSubclass);
SetWindowLongPtr(GetDlgItem(hDlg, IDOK), GWLP_WNDPROC, (LONG_PTR)dwOKSubclass);
if (pOFN->lpstrTitle && *pOFN->lpstrTitle)
{
SetWindowText(hDlg, pOFN->lpstrTitle);
}
//
// By setting dyText to rRect.bottom/8, dyText defaults to 8 items showing
// in the listbox. This only matters if the applications hook function
// steals all WM_MEASUREITEM messages. Otherwise, dyText will be set in
// the MeasureItem() routine. Check for !dyItem in case message ordering
// has already sent WM_MEASUREITEM and dyText is already initialized.
//
if (!dyItem)
{
GetClientRect(GetDlgItem(hDlg, lst1), (LPRECT) &rRect);
if (!(dyText = (rRect.bottom / 8)))
{
//
// If no size to rectangle.
//
dyText = 8;
}
}
// The template has changed to make it extremely clear that
// this is not a combobox, but rather an edit control and a listbox. The
// problem is that the new templates try to align the edit box and listbox.
// Unfortunately, when listboxes add borders, they expand beyond their
// borders. When edit controls add borders, they stay within their
// borders. This makes it impossible to align the two controls strictly
// within the template. The code below will align the controls, but only
// if they are using the standard dialog template.
//
if (!(pOFN->Flags & (OFN_ENABLETEMPLATE | OFN_ENABLETEMPLATEHANDLE)))
{
GetWindowRect(GetDlgItem(hDlg, lst1), (LPRECT)&rLbox);
GetWindowRect(GetDlgItem(hDlg, edt1), (LPRECT)&rRect);
rRect.left = rLbox.left;
rRect.right = rLbox.right;
MapWindowPoints(NULL, hDlg, (LPPOINT)&rRect, 2);
SetWindowPos( GetDlgItem(hDlg, edt1),
0,
rRect.left,
rRect.top,
rRect.right - rRect.left,
rRect.bottom - rRect.top,
SWP_NOZORDER );
}
if (pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
if (pOFI->ApiType == COMDLG_ANSI)
{
ThunkOpenFileNameW2A(pOFI);
bRet = ((*lpfnHook)( hDlg,
WM_INITDIALOG,
wParam,
(LPARAM)pOFI->pOFNA ));
//
// Strange win 31 example uses lCustData to
// hold a temporary variable that it passes back to
// calling function.
//
ThunkOpenFileNameA2W(pOFI);
}
else
{
bRet = ((*lpfnHook)( hDlg,
WM_INITDIALOG,
wParam,
(LPARAM)pOFN ));
}
}
else
{
//
// Have to thunk A version even when there isn't a hook proc so it
// doesn't reset W version on delayed thunk back.
//
if (pOFI->ApiType == COMDLG_ANSI)
{
pOFI->pOFNA->Flags = pOFN->Flags;
}
bRet = TRUE;
}
//
// At first, assume there is net support !
//
if ((pOFN->Flags & OFN_NONETWORKBUTTON))
{
HWND hNet;
if (hNet = GetDlgItem(hDlg, psh14))
{
EnableWindow(hNet = GetDlgItem(hDlg, psh14), FALSE);
ShowWindow(hNet, SW_HIDE);
}
}
else
{
AddNetButton( hDlg,
((pOFN->Flags & OFN_ENABLETEMPLATE)
? pOFN->hInstance
: g_hinst),
FILE_BOTTOM_MARGIN,
(pOFN->Flags & (OFN_ENABLETEMPLATE |
OFN_ENABLETEMPLATEHANDLE))
? FALSE
: TRUE,
(pOFN->Flags & OFN_NOLONGNAMES)
? FALSE
: TRUE,
FALSE);
}
return (bRet);
}
////////////////////////////////////////////////////////////////////////////
//
// InitTlsValues
//
////////////////////////////////////////////////////////////////////////////
int InitTlsValues(
POPENFILEINFO pOFI)
{
//
// As long as we do not call TlsGetValue before this,
// everything should be ok.
//
LPCURDLG lpCurDlg, lpPrevDlg;
DWORD dwError;
LPTSTR lpCurDir;
if (dwNumDlgs == MAX_THREADS)
{
dwError = CDERR_INITIALIZATION;
goto ErrorExit0;
}
// alloc for the current directory
lpCurDir = (LPTSTR)LocalAlloc(LPTR, CCHNETPATH * sizeof(TCHAR));
if (lpCurDir)
{
GetCurrentDirectory(CCHNETPATH, lpCurDir);
if ( (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT) &&
(StrChr(lpCurDir, CHAR_SPACE)) )
{
GetShortPathName(lpCurDir, lpCurDir, CCHNETPATH);
}
}
else
{
dwError = CDERR_MEMALLOCFAILURE;
goto ErrorExit0;
}
// add a CurDlg struct to the list for this thread
lpCurDlg = (LPCURDLG)LocalAlloc(LPTR, sizeof(CURDLG));
if (lpCurDlg)
{
// get start of CURDLG list for this thread
// Note: lpPrevDlg will be NULL if there wasn't a previous dialog
lpPrevDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
// make sure TlsGetValue() actually succeeded (a NULL return could
// mean there wasn't a previous dialog in the list)
if (GetLastError() != NO_ERROR)
{
dwError = CDERR_INITIALIZATION;
goto ErrorExit2;
}
// push the new dlg to the front of the list
lpCurDlg->next = lpPrevDlg;
lpCurDlg->lpstrCurDir = lpCurDir;
if (!PathAddBackslash(lpCurDlg->lpstrCurDir)) // Could fail if path is already MAX_PATH long, w/o a blackslash.
{
dwError = CDERR_INITIALIZATION;
goto ErrorExit2;
}
EnterCriticalSection(&g_csLocal);
lpCurDlg->dwCurDlgNum = dwNumDlgs++;
LeaveCriticalSection(&g_csLocal);
// save the new head of the list for the thread
if (!TlsSetValue(g_tlsiCurDlg, (LPVOID)lpCurDlg))
{
dwError = CDERR_INITIALIZATION;
goto ErrorExit2;
}
}
else
{
dwError = CDERR_MEMALLOCFAILURE;
goto ErrorExit1;
}
return(TRUE);
ErrorExit2:
LocalFree(lpCurDlg);
ErrorExit1:
LocalFree(lpCurDir);
ErrorExit0:
StoreExtendedError(dwError);
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// InitFilterBox
//
// Places the double null terminated list of filters in the combo box.
// The list should consist of pairs of null terminated strings, with
// an additional null terminating the list.
//
////////////////////////////////////////////////////////////////////////////
DWORD InitFilterBox(
HANDLE hDlg,
LPCTSTR lpszFilter)
{
DWORD nOffset = 0;
DWORD nIndex = 0;
register WORD nLen;
while (*lpszFilter)
{
//
// First string put in as string to show.
//
nIndex = (DWORD) SendDlgItemMessage( hDlg,
cmb1,
CB_ADDSTRING,
0,
(LPARAM)lpszFilter );
nLen = (WORD)(lstrlen(lpszFilter) + 1);
(LPTSTR)lpszFilter += nLen;
nOffset += nLen;
//
// Second string put in as itemdata.
//
SendDlgItemMessage( hDlg,
cmb1,
CB_SETITEMDATA,
(WPARAM)nIndex,
nOffset );
//
// Advance to next element.
//
nLen = (WORD)(lstrlen(lpszFilter) + 1);
(LPTSTR)lpszFilter += nLen;
nOffset += nLen;
}
return (nIndex);
}
void TokenizeFilterString(LPTSTR pszFilterString, LPTSTR *ppszFilterArray, int cFilterArray, BOOL bLFN)
{
LPCTSTR pszDelim = bLFN ? szSemiColonTab : szSemiColonSpaceTab;
int nFilters = 0;
cFilterArray--; // Need one for the NULL at the end.
//
// Find the first filter in the string, and add it to the
// array.
//
ppszFilterArray[nFilters] = lstrtok(pszFilterString, pszDelim);
//
// Now we are going to loop through all the filters in the string
// parsing the one we already have, and then finding the next one
// and starting the loop over again.
//
while (ppszFilterArray[nFilters] && (nFilters < cFilterArray))
{
//
// Check to see if the first character is a space. If so, remove
// the spaces, and save the pointer back into the same spot. We
// need to do this because the FindFirstFile/Next api will still
// work on filenames that begin with a space since they also
// look at the short names. The short names will begin with the
// same first real letter as the long filename. For example, the
// long filename is " my document" the first letter of this short
// name is "m", so searching on "m*.*" or " m*.*" will yield the
// same results.
//
if (bLFN && (*ppszFilterArray[nFilters] == CHAR_SPACE))
{
LPTSTR pszTemp = ppszFilterArray[nFilters];
while ((*pszTemp == CHAR_SPACE) && *pszTemp)
{
pszTemp = CharNext(pszTemp);
}
ppszFilterArray[nFilters] = pszTemp;
}
//
// Ready to move on to the next filter. Find the next
// filter based upon the type of file system we're using.
//
ppszFilterArray[++nFilters] = lstrtok(NULL, pszDelim);
//
// In case we found a pointer to NULL, then look for the
// next filter.
//
while (ppszFilterArray[nFilters] && !*ppszFilterArray[nFilters])
{
ppszFilterArray[nFilters] = lstrtok(NULL, pszDelim);
}
}
}
BOOL FoundFilterMatch(LPCTSTR pszIn, BOOL bLFN)
{
TCHAR szFilter[MAX_FULLPATHNAME];
LPTSTR pszF[MAXFILTERS + 1];
BOOL fFoundMatches = FALSE;
int i;
if (SUCCEEDED(StringCchCopy(szFilter, ARRAYSIZE(szFilter), pszIn)))
{
TokenizeFilterString(szFilter, pszF, ARRAYSIZE(pszF), bLFN);
for (i = 0; i < ARRAYSIZE(pszF) && pszF[i] && !fFoundMatches; i++)
{
HANDLE hff;
WIN32_FIND_DATA FindFileData;
//
// Find First for each filter.
//
hff = FindFirstFile(pszF[i], &FindFileData);
if (hff == INVALID_HANDLE_VALUE)
{
continue;
}
do
{
if ((FindFileData.dwFileAttributes & EXCLBITS) ||
(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
continue;
}
fFoundMatches = TRUE;
break;
} while (FindNextFile(hff, &FindFileData));
FindClose(hff);
}
}
return fFoundMatches;
}
////////////////////////////////////////////////////////////////////////////
//
// GetAppOpenDir
//
////////////////////////////////////////////////////////////////////////////
void GetAppOpenDir(LPTSTR pszOut, DWORD cchOut, LPITEMIDLIST *ppidl)
{
BOOL fUseMyDocs = FALSE;
TCHAR szPersonal[MAX_PATH];
*pszOut = 0; // prepare to return empty string
if (ppidl)
*ppidl = NULL;
if (SHGetSpecialFolderPath(NULL, szPersonal, CSIDL_PERSONAL, FALSE))
{
TCHAR szPath[MAX_FULLPATHNAME];
if (GetCurrentDirectory(ARRAYSIZE(szPath), szPath)
&& (PathIsTemporary(szPath) || (0 == lstrcmpi(szPath, szPersonal))))
fUseMyDocs = TRUE;
}
if (fUseMyDocs)
{
EVAL(SUCCEEDED(StringCchCopy(pszOut, cchOut, szPersonal)));
if (ppidl)
{
SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, ppidl);
}
}
else
{
EVAL(SUCCEEDED(StringCchCopy(pszOut, cchOut, L"")));
}
}
////////////////////////////////////////////////////////////////////////////
//
// InitCurrentDisk
//
////////////////////////////////////////////////////////////////////////////
VOID InitCurrentDisk(HWND hDlg, POPENFILEINFO pOFI, WORD cmb)
{
TCHAR szPath[MAX_FULLPATHNAME];
//
// Clear out stale unc stuff from disk info.
// Unc \\server\shares are persistent through one popup session
// and then we resync with the system. This is to fix a bug
// where a user's startup dir is unc but the system no longer has
// a connection and hence the cmb2 appears blank.
//
EnableDiskInfo(FALSE, TRUE);
if (pOFI->pOFN->lpstrInitialDir)
{
//
// Notice that we force ChangeDir to succeed here
// but that TlsGetValue(g_tlsiCurDlg)->lpstrCurDir will return "" which
// when fed to SheChangeDirEx means GetCurrentDir will be called.
// So, the default cd behavior at startup is:
// 1. lpstrInitialDir
// 2. GetCurrentDir
//
szPath[0] = 0;
if ( (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT) &&
(StrChr(pOFI->pOFN->lpstrInitialDir, CHAR_SPACE)) &&
(GetShortPathName( pOFI->pOFN->lpstrInitialDir,
szPath,
MAX_FULLPATHNAME )) &&
(szPath[0] != 0) )
{
ChangeDir(hDlg, szPath, TRUE, FALSE);
}
else
{
ChangeDir(hDlg, pOFI->pOFN->lpstrInitialDir, TRUE, FALSE);
}
}
else
{
GetAppOpenDir(szPath, ARRAYSIZE(szPath), NULL);
ChangeDir(hDlg, szPath, TRUE, FALSE);
}
}
////////////////////////////////////////////////////////////////////////////
//
// vDeleteDirDriveBitmap
//
// Gets rid of bitmaps, if they exist.
//
////////////////////////////////////////////////////////////////////////////
VOID vDeleteDirDriveBitmap()
{
if (hbmpOrigMemBmp)
{
SelectObject(hdcMemory, hbmpOrigMemBmp);
if (hbmpDirDrive != HNULL)
{
DeleteObject(hbmpDirDrive);
hbmpDirDrive = HNULL;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// LoadDirDriveBitmap
//
// Creates the drive/directory bitmap. If an appropriate bitmap
// already exists, it just returns immediately. Otherwise, it
// loads the bitmap and creates a larger bitmap with both regular
// and highlight colors.
//
////////////////////////////////////////////////////////////////////////////
BOOL LoadDirDriveBitmap()
{
BITMAP bmp;
HANDLE hbmp, hbmpOrig;
HDC hdcTemp;
BOOL bWorked = FALSE;
if ( (hbmpDirDrive != HNULL) &&
(rgbWindowColor == rgbDDWindow) &&
(rgbHiliteColor == rgbDDHilite))
{
if (SelectObject(hdcMemory, hbmpDirDrive))
{
return (TRUE);
}
}
vDeleteDirDriveBitmap();
rgbDDWindow = rgbWindowColor;
rgbDDHilite = rgbHiliteColor;
if (!(hdcTemp = CreateCompatibleDC(hdcMemory)))
{
goto LoadExit;
}
if (!(hbmp = LoadAlterBitmap(bmpDirDrive, rgbSolidBlue, rgbWindowColor)))
{
goto DeleteTempDC;
}
GetObject(hbmp, sizeof(BITMAP), (LPTSTR)&bmp);
dyDirDrive = bmp.bmHeight;
dxDirDrive = bmp.bmWidth;
hbmpOrig = SelectObject(hdcTemp, hbmp);
hbmpDirDrive = CreateDiscardableBitmap(hdcTemp, dxDirDrive * 2, dyDirDrive);
if (!hbmpDirDrive)
{
goto DeleteTempBmp;
}
if (!SelectObject(hdcMemory, hbmpDirDrive))
{
vDeleteDirDriveBitmap();
goto DeleteTempBmp;
}
BitBlt(hdcMemory, 0, 0, dxDirDrive, dyDirDrive, hdcTemp, 0, 0, SRCCOPY);
SelectObject(hdcTemp, hbmpOrig);
DeleteObject(hbmp);
if (!(hbmp = LoadAlterBitmap(bmpDirDrive, rgbSolidBlue, rgbHiliteColor)))
{
goto DeleteTempDC;
}
hbmpOrig = SelectObject(hdcTemp, hbmp);
BitBlt(hdcMemory, dxDirDrive, 0, dxDirDrive, dyDirDrive, hdcTemp, 0, 0, SRCCOPY);
SelectObject(hdcTemp, hbmpOrig);
bWorked = TRUE;
DeleteTempBmp:
DeleteObject(hbmp);
DeleteTempDC:
DeleteDC(hdcTemp);
LoadExit:
return (bWorked);
}
////////////////////////////////////////////////////////////////////////////
//
// SetRGBValues
//
// This sets the various system colors in static variables. It's
// called at init time and when system colors change.
//
////////////////////////////////////////////////////////////////////////////
void SetRGBValues()
{
rgbWindowColor = GetSysColor(COLOR_WINDOW);
rgbHiliteColor = GetSysColor(COLOR_HIGHLIGHT);
rgbWindowText = GetSysColor(COLOR_WINDOWTEXT);
rgbHiliteText = GetSysColor(COLOR_HIGHLIGHTTEXT);
rgbGrayText = GetSysColor(COLOR_GRAYTEXT);
}
////////////////////////////////////////////////////////////////////////////
//
// FSetUpFile
//
// This loads in the resources & initializes the data used by the
// file dialogs.
//
// Returns: TRUE if successful
// FALSE if any bitmap fails
//
////////////////////////////////////////////////////////////////////////////
BOOL FSetUpFile()
{
if (cLock++)
{
return (TRUE);
}
SetRGBValues();
return (LoadDirDriveBitmap());
}
////////////////////////////////////////////////////////////////////////////
//
// GetPathOffset
//
// Returns the index of the last character of the drive or UNC specification
// e.g.:
// c:\foo will return 2 (\foo)
// \\foo\bar\hoo will return 9 (\hoo)
// But for \\foo\bar, there seems to be a bug in PathSkipRoot, where it will return \bar, and we'll return 4 (o\bar)
////////////////////////////////////////////////////////////////////////////
int GetPathOffset(LPTSTR lpszDir)
{
LPTSTR lpszSkipRoot;
if (!lpszDir || !*lpszDir)
{
return (-1);
}
lpszSkipRoot = PathSkipRoot(lpszDir);
if (lpszSkipRoot)
{
return (int)((lpszSkipRoot - 1) - lpszDir);
}
else
{
//
// Unrecognized format.
//
return (-1);
}
}
////////////////////////////////////////////////////////////////////////////
//
// FileOpenCmd
//
// Handles WM_COMMAND for Open & Save dlgs.
//
// edt1 = file name
// lst1 = list of files in current directory matching current pattern
// cmb1 = lists file patterns
// stc1 = is current directory
// lst2 = lists directories on current drive
// cmb2 = lists drives
// IDOK = is Open pushbutton
// IDCANCEL = is Cancel pushbutton
// chx1 = is for opening read only files
//
// Returns the normal dialog proc values.
//
////////////////////////////////////////////////////////////////////////////
BOOL_PTR FileOpenCmd(
HANDLE hDlg,
WPARAM wParam,
LPARAM lParam,
POPENFILEINFO pOFI,
BOOL bSave)
{
LPOPENFILENAME pOFN;
LPTSTR pch, pch2;
WORD i, sCount, len;
LRESULT wFlag;
BOOL_PTR bRet, bHookRet;
TCHAR szText[MAX_FULLPATHNAME];
HWND hwnd;
LPCURDLG lpCurDlg;
if (!pOFI)
{
return (FALSE);
}
pOFN = pOFI->pOFN;
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case ( IDOK ) :
{
//
// Apps that side-effect this message may not have their
// internal unicode strings updated (eg. Corel Mosaic).
//
// NOTE: Must preserve the internal flags.
//
if (pOFI->ApiType == COMDLG_ANSI)
{
DWORD InternalFlags = pOFN->Flags & OFN_ALL_INTERNAL_FLAGS;
ThunkOpenFileNameA2W(pOFI);
pOFN->Flags |= InternalFlags;
}
//
// If the focus is on the directory box, or if the selection
// within the box has changed since the last listing, give a
// new listing.
//
if (bChangeDir || ((GetFocus() == GetDlgItem(hDlg, lst2)) &&
(pOFN->Flags & OFN_DIRSELCHANGED)))
{
bChangeDir = FALSE;
goto ChangingDir;
}
else if ((GetFocus() == (hwnd = GetDlgItem(hDlg, cmb2))) &&
(pOFN->Flags & OFN_DRIVEDOWN))
{
//
// If the focus is on the drive or filter combobox, give
// a new listing.
//
SendDlgItemMessage(hDlg, cmb2, CB_SHOWDROPDOWN, FALSE, 0L);
break;
}
else if ((GetFocus() == (hwnd = GetDlgItem(hDlg, cmb1))) &&
(pOFN->Flags & OFN_FILTERDOWN))
{
SendDlgItemMessage(hDlg, cmb1, CB_SHOWDROPDOWN, FALSE, 0L);
lParam = (LPARAM)hwnd;
goto ChangingFilter;
}
else
{
//
// Visual Basic passes in an uninitialized lpstrDefExt string.
// Since we only have to use it in OKButtonPressed, update
// lpstrDefExt here along with whatever else is only needed
// in OKButtonPressed.
//
if (pOFI->ApiType == COMDLG_ANSI)
{
ThunkOpenFileNameA2WDelayed(pOFI);
}
if (OKButtonPressed(hDlg, pOFI, bSave))
{
bRet = TRUE;
if (pOFN->lpstrFile)
{
if (!(pOFN->Flags & OFN_NOVALIDATE))
{
if (pOFN->nMaxFile >= 3)
{
if ((pOFN->lpstrFile[0] == 0) ||
(pOFN->lpstrFile[1] == 0) ||
(pOFN->lpstrFile[2] == 0))
{
bRet = FALSE;
StoreExtendedError(FNERR_BUFFERTOOSMALL);
}
}
else
{
bRet = FALSE;
StoreExtendedError(FNERR_BUFFERTOOSMALL);
}
}
}
goto AbortDialog;
}
}
SendDlgItemMessage(hDlg, edt1, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
return (TRUE);
break;
}
case ( IDCANCEL ) :
{
bRet = FALSE;
g_bUserPressedCancel = TRUE;
goto AbortDialog;
}
case ( IDABORT ) :
{
bRet = (BYTE)lParam;
AbortDialog:
//
// Return the most recently used filter.
//
pOFN->nFilterIndex = (WORD)SendDlgItemMessage( hDlg,
cmb1,
CB_GETCURSEL,
(WPARAM)0,
(LPARAM)0 );
if (pOFN->lpstrCustomFilter)
{
len = (WORD)(lstrlen(pOFN->lpstrCustomFilter) + 1);
sCount = (WORD)lstrlen(pOFI->szLastFilter);
if (pOFN->nMaxCustFilter > (DWORD)(sCount + len))
{
EVAL(SUCCEEDED(StringCchCopy(pOFN->lpstrCustomFilter + len, pOFN->nMaxCustFilter - len, pOFI->szLastFilter))); // Always enough room
}
}
if (!pOFN->lpstrCustomFilter ||
(*pOFN->lpstrCustomFilter == CHAR_NULL))
{
pOFN->nFilterIndex++;
}
if (((GET_WM_COMMAND_ID(wParam, lParam)) == IDOK) && pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
if (pOFI->ApiType == COMDLG_ANSI)
{
ThunkOpenFileNameW2A(pOFI);
bHookRet = (*lpfnHook)( hDlg,
msgFILEOKA,
0,
(LPARAM)pOFI->pOFNA );
//
// For apps that side-effect pOFNA stuff and expect it to
// be preserved through dialog exit, update internal
// struct after the hook proc is called.
//
ThunkOpenFileNameA2W(pOFI);
}
else
{
bHookRet = (*lpfnHook)( hDlg,
msgFILEOKW,
0,
(LPARAM)pOFI->pOFN );
}
if (bHookRet)
{
HourGlass(FALSE);
break;
}
}
wNoRedraw = 0;
if (pOFI->pOFN->Flags & OFN_ENABLEHOOK)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
glpfnFileHook = lpfnHook;
}
RemoveProp(hDlg, FILEPROP);
EndDialog(hDlg, bRet);
if (pOFI)
{
if ((pOFN->Flags & OFN_NOCHANGEDIR) && *pOFI->szCurDir)
{
ChangeDir(hDlg, pOFI->szCurDir, TRUE, FALSE);
}
}
//
// WARNING:
// If the app subclasses ID_ABORT, the worker thread will never
// get exited. This will cause problems. Currently, there are
// no apps that do this, though.
//
return (TRUE);
break;
}
case ( edt1 ) :
{
if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE )
{
int iIndex, iCount;
HWND hLBox = GetDlgItem(hDlg, lst1);
WORD wIndex = (WORD)SendMessage(hLBox, LB_GETCARETINDEX, 0, 0);
szText[0] = CHAR_NULL;
if (wIndex == (WORD)LB_ERR)
{
break;
}
SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
WM_GETTEXT,
(WPARAM)MAX_FULLPATHNAME,
(LPARAM)szText );
if ((iIndex = (int)SendMessage( hLBox,
LB_FINDSTRING,
(WPARAM)(wIndex - 1),
(LPARAM)szText )) != LB_ERR)
{
RECT rRect;
iCount = (int)SendMessage(hLBox, LB_GETTOPINDEX, 0, 0L);
GetClientRect(hLBox, (LPRECT)&rRect);
if ((iIndex < iCount) ||
(iIndex >= (iCount + rRect.bottom / dyText)))
{
SendMessage(hLBox, LB_SETCARETINDEX, (WPARAM)iIndex, 0);
SendMessage(hLBox, LB_SETTOPINDEX, (WPARAM)iIndex, 0);
}
}
return (TRUE);
}
else if ( GET_WM_COMMAND_CMD(wParam, lParam) == EN_SETFOCUS )
{
SetModeBias(MODEBIASMODE_FILENAME);
}
else if ( GET_WM_COMMAND_CMD(wParam, lParam)== EN_KILLFOCUS )
{
SetModeBias(MODEBIASMODE_DEFAULT);
}
break;
}
case ( lst1 ) :
{
//
// A double click means OK.
//
if (GET_WM_COMMAND_CMD(wParam, lParam)== LBN_DBLCLK)
{
SendMessage(hDlg, WM_COMMAND, GET_WM_COMMAND_MPS(IDOK, 0, 0));
return (TRUE);
}
else if (pOFN && (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE))
{
if (pOFN->Flags & OFN_ALLOWMULTISELECT)
{
int *pSelIndex;
//
// Muliselection allowed.
//
sCount = (SHORT)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETSELCOUNT,
0,
0L );
if (!sCount)
{
//
// If nothing selected, clear edit control.
//
SetDlgItemText(hDlg, edt1, szNull);
}
else
{
DWORD cchMemBlockSize = 2048;
DWORD cchTotalLength = 0;
pSelIndex = (int *)LocalAlloc(LPTR, sCount * sizeof(int));
if (!pSelIndex)
{
goto LocalFailure1;
}
sCount = (SHORT)SendMessage(
GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETSELITEMS,
(WPARAM)sCount,
(LONG_PTR)(LPTSTR)pSelIndex );
pch2 = pch = (LPTSTR)
LocalAlloc(LPTR, cchMemBlockSize * sizeof(TCHAR));
if (!pch)
{
goto LocalFailure2;
}
for (*pch = CHAR_NULL, i = 0; i < sCount; i++)
{
len = (WORD)SendMessage(
GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETTEXTLEN,
(WPARAM)(*(pSelIndex + i)),
(LPARAM)0 );
//
// Add the length of the selected file to the
// total length of selected files. + 2 for the
// space that goes in between files and for the
// possible dot added at the end of the filename
// if the file does not have an extension.
//
cchTotalLength += (len + 2);
if (cchTotalLength > cchMemBlockSize)
{
LPTSTR pTemp;
UINT cchPrevLen = cchTotalLength - (len + 2);
cchMemBlockSize = cchMemBlockSize << 1;
pTemp = (LPTSTR)LocalReAlloc(
pch,
cchMemBlockSize * sizeof(TCHAR),
LMEM_MOVEABLE );
if (pTemp)
{
pch = pTemp;
pch2 = pch + cchPrevLen;
}
else
{
LocalFree(pch);
goto LocalFailure2;
}
}
SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETTEXT,
(WPARAM)(*(pSelIndex + i)),
(LONG_PTR)pch2 );
if (!StrChr(pch2, CHAR_DOT))
{
*(pch2 + len++) = CHAR_DOT;
}
pch2 += len;
*pch2++ = CHAR_SPACE;
}
if (pch2 != pch)
{
*--pch2 = CHAR_NULL;
}
SetDlgItemText(hDlg, edt1, pch);
LocalFree((HANDLE)pch);
LocalFailure2:
LocalFree((HANDLE)pSelIndex);
}
LocalFailure1:
if (pOFN->lpfnHook)
{
i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETCARETINDEX,
0,
0L );
if (!(i & 0x8000))
{
wFlag = (SendMessage(
GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETSEL,
(WPARAM)i,
0L )
? CD_LBSELADD
: CD_LBSELSUB);
}
else
{
wFlag = CD_LBSELNOITEMS;
}
}
}
else
{
//
// Multiselection is not allowed.
// Put the file name in the edit control.
//
szText[0] = CHAR_NULL;
i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETCURSEL,
0,
0L );
if (i != (WORD)LB_ERR)
{
i = (WORD)SendMessage( GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETTEXT,
(WPARAM)i,
(LONG_PTR)szText );
if (!StrChr(szText, CHAR_DOT))
{
if (i < MAX_FULLPATHNAME - 1)
{
szText[i] = CHAR_DOT;
szText[i + 1] = CHAR_NULL;
}
}
if (!bCasePreserved)
{
CharLower(szText);
}
SetDlgItemText(hDlg, edt1, szText);
if (pOFN->lpfnHook)
{
i = (WORD)SendMessage(
GET_WM_COMMAND_HWND(wParam, lParam),
LB_GETCURSEL,
0,
0L );
wFlag = CD_LBSELCHANGE;
}
}
}
if (pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
if (pOFI->ApiType == COMDLG_ANSI)
{
(*lpfnHook)( hDlg,
msgLBCHANGEA,
lst1,
MAKELONG(i, wFlag) );
}
else
{
(*lpfnHook)( hDlg,
msgLBCHANGEW,
lst1,
MAKELONG(i, wFlag) );
}
}
SendDlgItemMessage(hDlg, edt1, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
return (TRUE);
}
break;
}
case ( cmb1 ) :
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case ( CBN_DROPDOWN ) :
{
if (wWinVer >= 0x030A)
{
pOFN->Flags |= OFN_FILTERDOWN;
}
return (TRUE);
break;
}
case ( CBN_CLOSEUP ) :
{
PostMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS(cmb1, lParam, MYCBN_DRAW) );
return (TRUE);
break;
}
case ( CBN_SELCHANGE ) :
{
//
// Need to change the file listing in lst1.
//
if (pOFN->Flags & OFN_FILTERDOWN)
{
return (TRUE);
break;
}
}
case ( MYCBN_DRAW ) :
{
SHORT nIndex;
LPCTSTR lpFilter;
HourGlass(TRUE);
pOFN->Flags &= ~OFN_FILTERDOWN;
ChangingFilter:
nIndex = (SHORT)SendDlgItemMessage( hDlg,
cmb1,
CB_GETCURSEL,
0,
0L );
if (nIndex < 0)
{
//
// No current selection.
//
break;
}
//
// Must also check if filter contains anything.
//
if (nIndex ||
!(pOFN->lpstrCustomFilter && *pOFN->lpstrCustomFilter))
{
lpFilter = pOFN->lpstrFilter +
SendDlgItemMessage( hDlg,
cmb1,
CB_GETITEMDATA,
(WPARAM)nIndex,
0L );
}
else
{
lpFilter = pOFN->lpstrCustomFilter +
lstrlen(pOFN->lpstrCustomFilter) + 1;
}
if (*lpFilter)
{
GetDlgItemText( hDlg,
edt1,
szText,
MAX_FULLPATHNAME - 1 );
bRet = (!szText[0] ||
(StrChr(szText, CHAR_STAR)) ||
(StrChr(szText, CHAR_QMARK)));
if (SUCCEEDED(StringCchCopy(szText, ARRAYSIZE(szText), lpFilter)))
{
if (bRet)
{
CharLower(szText);
SetDlgItemText(hDlg, edt1, szText);
SendDlgItemMessage( hDlg,
edt1,
EM_SETSEL,
(WPARAM)0,
(LPARAM)-1 );
}
FListAll(pOFI, hDlg, szText, ARRAYSIZE(szText));
if (!bInitializing)
{
EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), szText))); // szText is smaller than pOFI->szLastFilter
//
// Provide dynamic lpstrDefExt updating
// when lpstrDefExt is user initialized.
//
if (StrChr((LPTSTR)lpFilter, CHAR_DOT) &&
pOFN->lpstrDefExt)
{
DWORD cbLen = MIN_DEFEXT_LEN - 1; // only 1st 3
LPTSTR lpTemp = (LPTSTR)(pOFN->lpstrDefExt);
while (*lpFilter++ != CHAR_DOT);
if (!(StrChr((LPTSTR)lpFilter, CHAR_STAR)) &&
!(StrChr((LPTSTR)lpFilter, CHAR_QMARK)))
{
while (cbLen--)
{
*lpTemp++ = *lpFilter++;
}
*lpTemp = CHAR_NULL;
}
}
}
}
}
if (pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
if (pOFI->ApiType == COMDLG_ANSI)
{
(*lpfnHook)( hDlg,
msgLBCHANGEA,
cmb1,
MAKELONG(nIndex, CD_LBSELCHANGE) );
}
else
{
(*lpfnHook)( hDlg,
msgLBCHANGEW,
cmb1,
MAKELONG(nIndex, CD_LBSELCHANGE) );
}
}
HourGlass(FALSE);
return (TRUE);
break;
}
default :
{
break;
}
}
break;
}
case ( lst2 ) :
{
if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE)
{
if (!(pOFN->Flags & OFN_DIRSELCHANGED))
{
if ((DWORD)SendDlgItemMessage( hDlg,
lst2,
LB_GETCURSEL,
0,
0L ) != pOFI->idirSub - 1)
{
StripFileName(hDlg, IS16BITWOWAPP(pOFN));
pOFN->Flags |= OFN_DIRSELCHANGED;
}
}
return (TRUE);
}
else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SETFOCUS)
{
EnableWindow(GetDlgItem(hDlg, IDOK), TRUE);
SendMessage( GetDlgItem(hDlg, IDCANCEL),
BM_SETSTYLE,
(WPARAM)BS_PUSHBUTTON,
(LPARAM)TRUE );
}
else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_KILLFOCUS)
{
if (pOFN && (pOFN->Flags & OFN_DIRSELCHANGED))
{
pOFN->Flags &= ~OFN_DIRSELCHANGED;
}
else
{
bChangeDir = FALSE;
}
}
else if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_DBLCLK)
{
TCHAR szNextDir[CCHNETPATH];
LPTSTR lpCurDir;
DWORD idir;
DWORD idirNew;
int cb;
LPTSTR pstrPath = NULL;
ChangingDir:
bChangeDir = FALSE;
pOFN->Flags &= ~OFN_DIRSELCHANGED;
idirNew = (DWORD)SendDlgItemMessage( hDlg,
lst2,
LB_GETCURSEL,
0,
0L );
//
// Can use relative path name.
//
*pOFI->szPath = 0;
if (idirNew >= pOFI->idirSub)
{
cb = (int) SendDlgItemMessage( hDlg,
lst2,
LB_GETTEXT,
(WPARAM)idirNew,
(LPARAM)pOFI->szPath );
//
// sanity check
//
if (!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
!(lpCurDir = lpCurDlg->lpstrCurDir))
{
break;
}
if (SUCCEEDED(StringCchCopy(szNextDir, ARRAYSIZE(szNextDir), lpCurDir)) && PathAddBackslash(szNextDir))
{
//
// Fix phenom with c:\\foobar - because of inconsistency
// in directory display guaranteed to have a valid
// lpCurDir here, right?
//
if (SUCCEEDED(StringCchCat(szNextDir, ARRAYSIZE(szNextDir), pOFI->szPath)))
{
pstrPath = szNextDir;
idirNew = pOFI->idirSub; // for msgLBCHANGE message
}
}
}
else
{
//
// Need full path name.
//
cb = (int) SendDlgItemMessage( hDlg,
lst2,
LB_GETTEXT,
0,
(LPARAM)pOFI->szPath );
//
// The following condition is necessary because wb displays
// \\server\share (the disk resource name) for unc, but
// for root paths (eg. c:\) for device conns, this in-
// consistency is hacked around here and in FillOutPath.
//
if (DBL_BSLASH(pOFI->szPath) && SUCCEEDED(StringCchCat(pOFI->szPath, ARRAYSIZE(pOFI->szPath), L"\\")))
{
cb++;
}
for (idir = 1; idir <= idirNew; ++idir)
{
cb += (int) SendDlgItemMessage(
hDlg,
lst2,
LB_GETTEXT,
(WPARAM)idir,
(LPARAM)&pOFI->szPath[cb] );
pOFI->szPath[cb++] = CHAR_BSLASH;
}
//
// The root is a special case.
//
if (idirNew)
{
pOFI->szPath[cb - 1] = CHAR_NULL;
}
pstrPath = pOFI->szPath;
}
if (!pstrPath || !*pstrPath ||
(ChangeDir(hDlg, pstrPath, FALSE, TRUE) == CHANGEDIR_FAILED))
{
break;
}
//
// List all directories under this one.
//
UpdateListBoxes(hDlg, pOFI, NULL, mskDirectory);
if (pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
if (pOFI->ApiType == COMDLG_ANSI)
{
(*lpfnHook)( hDlg,
msgLBCHANGEA,
lst2,
MAKELONG(LOWORD(idirNew), CD_LBSELCHANGE) );
}
else
{
(*lpfnHook)( hDlg,
msgLBCHANGEW,
lst2,
MAKELONG(LOWORD(idirNew), CD_LBSELCHANGE) );
}
}
return (TRUE);
}
break;
}
case ( cmb2 ) :
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case ( CBN_DROPDOWN ) :
{
pOFN->Flags |= OFN_DRIVEDOWN;
return (TRUE);
break;
}
case ( CBN_CLOSEUP ) :
{
//
// It would seem reasonable to merely do the update
// at this point, but that would rely on message
// ordering, which isnt a smart move. In fact, if
// you hit ALT-DOWNARROW, DOWNARROW, ALT-DOWNARROW,
// you receive CBN_DROPDOWN, CBN_SELCHANGE, and then
// CBN_CLOSEUP. But if you use the mouse to choose
// the same element, the last two messages trade
// places. PostMessage allows all messages in the
// sequence to be processed, and then updates are
// done as needed.
//
PostMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS(
cmb2,
GET_WM_COMMAND_HWND(wParam, lParam),
MYCBN_DRAW ) );
return (TRUE);
break;
}
case ( MYCBN_LIST ) :
{
LoadDrives(hDlg);
break;
}
case ( MYCBN_REPAINT ) :
{
int cchCurDir;
LPTSTR lpCurDir;
// sanity
if (!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
!(lpCurDir = lpCurDlg->lpstrCurDir))
{
break;
}
cchCurDir = GetPathOffset(lpCurDir);
if (cchCurDir != -1)
{
TCHAR szRepaintDir[CCHNETPATH];
HWND hCmb2 = (HWND)lParam;
if (SUCCEEDED(StringCchCopy(szRepaintDir, ARRAYSIZE(szRepaintDir), lpCurDir)))
{
szRepaintDir[cchCurDir] = CHAR_NULL;
SendMessage(hCmb2, CB_SELECTSTRING, (WPARAM)-1, (LPARAM)szRepaintDir);
}
}
break;
}
case ( CBN_SELCHANGE ) :
{
StripFileName(hDlg, IS16BITWOWAPP(pOFN));
//
// Version check not needed, since flag never set
// for versions not supporting CBN_CLOSEUP. Putting
// check at CBN_DROPDOWN is more efficient since it
// is less frequent than CBN_SELCHANGE.
if (pOFN->Flags & OFN_DRIVEDOWN)
{
//
// Don't fill lst2 while the combobox is down.
//
return (TRUE);
break;
}
}
case ( MYCBN_CHANGEDIR ) :
case ( MYCBN_DRAW ) :
{
TCHAR szTitle[WARNINGMSGLENGTH];
LPTSTR lpFilter;
int nDiskInd, nInd;
DWORD dwType = 0;
LPTSTR lpszPath = NULL;
LPTSTR lpszDisk = NULL;
HWND hCmb2;
OFN_DISKINFO *pofndiDisk = NULL;
static TCHAR szDrawDir[CCHNETPATH];
int nRet;
HourGlass(TRUE);
//
// Clear Flag for future CBN_SELCHANGE messeges.
//
pOFN->Flags &= ~OFN_DRIVEDOWN;
//
// Change the drive.
//
szText[0] = CHAR_NULL;
hCmb2 = (HWND)lParam;
if (hCmb2 != NULL)
{
nInd = (int) SendMessage(hCmb2, CB_GETCURSEL, 0, 0L);
if (nInd != CB_ERR)
{
SendMessage( hCmb2,
CB_GETLBTEXT,
nInd,
(LPARAM)szDrawDir );
}
if ((nInd == CB_ERR) || ((INT_PTR)pofndiDisk == CB_ERR))
{
if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
{
if (lpCurDlg->lpstrCurDir && (lstrlen(lpCurDlg->lpstrCurDir) < ARRAYSIZE(szDrawDir)))
{
// Don't touch szDrawDir unless there we are assured successful copy
StringCchCopy(szDrawDir, ARRAYSIZE(szDrawDir), lpCurDlg->lpstrCurDir);
}
}
}
CharLower(szDrawDir);
//
// Should always succeed.
//
nDiskInd = DiskAddedPreviously(0, szDrawDir);
if (nDiskInd != 0xFFFFFFFF)
{
pofndiDisk = &gaDiskInfo[nDiskInd];
}
else
{
//
// Skip update in the case where it fails.
//
return (TRUE);
}
dwType = pofndiDisk->dwType;
lpszDisk = pofndiDisk->lpPath;
}
if ((GET_WM_COMMAND_CMD(wParam, lParam)) == MYCBN_CHANGEDIR)
{
if (lpNetDriveSync)
{
lpszPath = lpNetDriveSync;
lpNetDriveSync = NULL;
}
else
{
if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
{
if (lpCurDlg->lpstrCurDir && (lstrlen(lpCurDlg->lpstrCurDir) < ARRAYSIZE(szDrawDir)))
{
// Don't touch szDrawDir unless we are assured a successful copy.
StringCchCopy(szDrawDir, ARRAYSIZE(szDrawDir), lpCurDlg->lpstrCurDir);
lpszPath = szDrawDir;
}
}
}
}
else
{
lpszPath = lpszDisk;
}
if (bInitializing)
{
lpFilter = szTitle;
if (pOFN->lpstrFile &&
(StrChr(pOFN->lpstrFile, CHAR_STAR) ||
StrChr(pOFN->lpstrFile, CHAR_QMARK)))
{
if (FAILED(StringCchCopy(szTitle, ARRAYSIZE(szTitle), pOFN->lpstrFile)))
{
goto NullSearch; // Don't use filter string.
}
}
else
{
HWND hcmb1 = GetDlgItem(hDlg, cmb1);
nInd = (int) SendMessage(hcmb1, CB_GETCURSEL, 0, 0L);
if (nInd == CB_ERR)
{
//
// No current selection.
//
goto NullSearch;
}
//
// Must also check if filter contains anything.
//
if (nInd ||
!(pOFN->lpstrCustomFilter &&
*pOFN->lpstrCustomFilter))
{
lpFilter = (LPTSTR)(pOFN->lpstrFilter);
lpFilter += SendMessage( hcmb1,
CB_GETITEMDATA,
(WPARAM)nInd,
0 );
}
else
{
lpFilter = pOFN->lpstrCustomFilter;
lpFilter += lstrlen(pOFN->lpstrCustomFilter) + 1;
}
}
}
else
{
NullSearch:
lpFilter = NULL;
}
//
// UpdateListBoxes cuts up filter string in place.
//
if (lpFilter)
{
// It's possible these overlap, since we have done a lpFilter = szTitle
if (SUCCEEDED(StringCchCopyOverlap(szTitle, ARRAYSIZE(szTitle), lpFilter)))
{
CharLower(szTitle);
}
else
{
lpFilter = NULL; // Don't use filter string.
}
}
if (dwType == REMDRVBMP)
{
DWORD err = WNetRestoreConnection(hDlg, lpszDisk);
if (err != WN_SUCCESS)
{
HourGlass(FALSE);
return (TRUE);
}
pofndiDisk->dwType = NETDRVBMP;
SendMessage(
hCmb2,
CB_SETITEMDATA,
(WPARAM)SendMessage(
hCmb2,
CB_SELECTSTRING,
(WPARAM)-1,
(LPARAM)(LPTSTR)pofndiDisk->lpAbbrName ),
(LPARAM)NETDRVBMP );
}
//
// Calls to ChangeDir will call SelDisk, so no need
// to update cmb2 on our own here (used to be after
// updatelistboxes).
//
if ((nRet = ChangeDir( hDlg,
lpszPath,
FALSE,
FALSE )) == CHANGEDIR_FAILED)
{
int mbRet;
while (nRet == CHANGEDIR_FAILED)
{
if (dwType == FLOPPYBMP)
{
mbRet = InvalidFileWarning(
hDlg,
lpszPath,
ERROR_NO_DISK_IN_DRIVE,
(UINT)(MB_RETRYCANCEL |
MB_ICONEXCLAMATION));
}
else if (dwType == CDDRVBMP)
{
mbRet = InvalidFileWarning(
hDlg,
lpszPath,
ERROR_NO_DISK_IN_CDROM,
(UINT)(MB_RETRYCANCEL |
MB_ICONEXCLAMATION) );
}
else
{
//
// See if it's a RAW volume.
//
if (dwType == HARDDRVBMP &&
GetLastError() == ERROR_UNRECOGNIZED_VOLUME)
{
mbRet = InvalidFileWarning(
hDlg,
lpszPath,
ERROR_UNRECOGNIZED_VOLUME,
(UINT)(MB_OK |
MB_ICONEXCLAMATION) );
}
else
{
mbRet = InvalidFileWarning(
hDlg,
lpszPath,
ERROR_DIR_ACCESS_DENIED,
(UINT)(MB_RETRYCANCEL |
MB_ICONEXCLAMATION) );
}
}
if (bFirstTime || (mbRet != IDRETRY))
{
lpszPath = NULL;
nRet = ChangeDir(hDlg, lpszPath, TRUE, FALSE);
}
else
{
nRet = ChangeDir(hDlg, lpszPath, FALSE, FALSE);
}
}
}
UpdateListBoxes( hDlg,
pOFI,
lpFilter ? szTitle : lpFilter,
(WORD)(mskDrives | mskDirectory) );
if (pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
nInd = (int) SendDlgItemMessage( hDlg,
cmb2,
CB_GETCURSEL,
0,
0 );
if (pOFI->ApiType == COMDLG_ANSI)
{
(*lpfnHook)( hDlg,
msgLBCHANGEA,
cmb2,
MAKELONG(LOWORD(nInd),
CD_LBSELCHANGE) );
}
else
{
(*lpfnHook)( hDlg,
msgLBCHANGEW,
cmb2,
MAKELONG(LOWORD(nInd),
CD_LBSELCHANGE) );
}
}
HourGlass(FALSE);
return (TRUE);
break;
}
default :
{
break;
}
}
break;
}
case ( pshHelp ) :
{
if (pOFI->ApiType == COMDLG_ANSI)
{
if (msgHELPA && pOFN->hwndOwner)
{
SendMessage( pOFN->hwndOwner,
msgHELPA,
(WPARAM)hDlg,
(DWORD_PTR)pOFN );
}
}
else
{
if (msgHELPW && pOFN->hwndOwner)
{
SendMessage( pOFN->hwndOwner,
msgHELPW,
(WPARAM)hDlg,
(DWORD_PTR)pOFN );
}
}
break;
}
case ( psh14 ) :
{
bGetNetDrivesSync = TRUE;
if (CallNetDlg(hDlg))
{
LNDSetEvent(hDlg);
}
else
{
bGetNetDrivesSync = FALSE;
}
break;
}
default :
{
break;
}
}
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// UpdateListBoxes
//
// Fills out File and Directory List Boxes in a single pass
// given (potentially) multiple filters
//
// It assumes the string of extensions are delimited by semicolons.
//
// hDlg Handle to File Open/Save dialog
// pOFI pointer to OPENFILEINFO structure
// lpszFilter pointer to filter, if NULL, use pOFI->szSpecCur
// wMask mskDirectory and/or mskDrives, or NULL
//
// Returns: TRUE if match
// FALSE if not
//
////////////////////////////////////////////////////////////////////////////
BOOL UpdateListBoxes(
HWND hDlg,
POPENFILEINFO pOFI,
LPTSTR lpszFilter,
WORD wMask)
{
LPTSTR lpszF[MAXFILTERS + 1];
LPTSTR lpszTemp;
SHORT i, nFilters;
HWND hFileList = GetDlgItem(hDlg, lst1);
HWND hDirList = GetDlgItem(hDlg, lst2);
BOOL bRet = FALSE;
TCHAR szSpec[MAX_FULLPATHNAME];
BOOL bDriveChange;
BOOL bFindAll = FALSE;
RECT rDirLBox;
BOOL bLFN;
HANDLE hff;
DWORD dwErr;
WIN32_FIND_DATA FindFileData;
TCHAR szBuffer[MAX_FULLPATHNAME]; // add one for CHAR_DOT
WORD wCount;
LPCURDLG lpCurDlg;
//
// Save the drive bit and then clear it out.
//
bDriveChange = wMask & mskDrives;
wMask &= ~mskDrives;
if (!lpszFilter)
{
lpszFilter = szSpec;
GetDlgItemText( hDlg,
edt1,
szSpec,
ARRAYSIZE(szSpec) - 1);
//
// If any directory or drive characters are in there, or if there
// are no wildcards, use the default spec.
//
if ( StrChr(szSpec, CHAR_BSLASH) ||
StrChr(szSpec, CHAR_SLASH) ||
StrChr(szSpec, CHAR_COLON) ||
(!((StrChr(szSpec, CHAR_STAR)) ||
(StrChr(szSpec, CHAR_QMARK)))) )
{
EVAL(SUCCEEDED(StringCchCopy(szSpec, ARRAYSIZE(szSpec), pOFI->szSpecCur)));
}
else
{
// szSpec comes from the editbox, which is limited to MAX_PATH chars, so this should always succeed.
EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), szSpec)));
}
}
//
// We need to find out what kind of a drive we are running
// on in order to determine if spaces are valid in a filename
// or not.
//
bLFN = IsLFNDriveX(hDlg, TEXT("\0"));
//
// Find the first filter in the string, and add it to the
// array.
//
if (bLFN)
{
lpszF[nFilters = 0] = lstrtok(lpszFilter, szSemiColonTab);
}
else
{
lpszF[nFilters = 0] = lstrtok(lpszFilter, szSemiColonSpaceTab);
}
//
// Now we are going to loop through all the filters in the string
// parsing the one we already have, and then finding the next one
// and starting the loop over again.
//
while (lpszF[nFilters] && (nFilters < MAXFILTERS))
{
//
// Check to see if the first character is a space.
// If so, remove the spaces, and save the pointer
// back into the same spot. Why? because the
// FindFirstFile/Next api will _still_ work on
// filenames that begin with a space because
// they also look at the short names. The
// short names will begin with the same first
// real letter as the long filename. For
// example, the long filename is " my document"
// the first letter of this short name is "m",
// so searching on "m*.*" or " m*.*" will yield
// the same results.
//
if (bLFN && (*lpszF[nFilters] == CHAR_SPACE))
{
lpszTemp = lpszF[nFilters];
while ((*lpszTemp == CHAR_SPACE) && *lpszTemp)
{
lpszTemp = CharNext(lpszTemp);
}
lpszF[nFilters] = lpszTemp;
}
//
// The original code used to do a CharUpper here to put the
// filter strings in upper case. EG: *.TXT However, this
// is not a good thing to do for Turkish. Capital 'i' does
// not equal 'I', so the CharUpper is being removed.
//
// CharUpper(lpszF[nFilters]);
//
// Compare the filter with *.*. If we find *.* then
// set the boolean bFindAll, and this will cause the
// files listbox to be filled in at the same time the
// directories listbox is filled. This saves time
// from walking the directory twice (once for the directory
// names and once for the filenames).
//
if (!lstrcmpi(lpszF[nFilters], szStarDotStar))
{
bFindAll = TRUE;
}
//
// Now we need to check if this filter is a duplicate
// of an already existing filter.
//
for (wCount = 0; wCount < nFilters; wCount++)
{
//
// If we find a duplicate, decrement the current
// index pointer by one so that the last location
// is written over (thus removing the duplicate),
// and break out of this loop.
//
if (!lstrcmpi(lpszF[nFilters], lpszF[wCount]))
{
nFilters--;
break;
}
}
//
// Ready to move on to the next filter. Find the next
// filter based upon the type of file system we're using.
//
if (bLFN)
{
lpszF[++nFilters] = lstrtok(NULL, szSemiColonTab);
}
else
{
lpszF[++nFilters] = lstrtok(NULL, szSemiColonSpaceTab);
}
//
// In case we found a pointer to NULL, then look for the
// next filter.
//
while (lpszF[nFilters] && !*lpszF[nFilters])
{
if (bLFN)
{
lpszF[nFilters] = lstrtok(NULL, szSemiColonTab);
}
else
{
lpszF[nFilters] = lstrtok(NULL, szSemiColonSpaceTab);
}
}
}
//
// Add NULL terminator only if needed.
//
if (nFilters >= MAXFILTERS)
{
lpszF[MAXFILTERS] = 0;
}
HourGlass(TRUE);
SendMessage(hFileList, WM_SETREDRAW, FALSE, 0L);
SendMessage(hFileList, LB_RESETCONTENT, 0, 0L);
if (wMask & mskDirectory)
{
wNoRedraw |= 2; // HACK!!! WM_SETREDRAW isn't complete
SendMessage(hDirList, WM_SETREDRAW, FALSE, 0L);
//
// LB_RESETCONTENT causes InvalidateRect(hDirList, 0, TRUE) to be
// sent as well as repositioning the scrollbar thumb and drawing
// it immediately. This causes flicker when the LB_SETCURSEL is
// made, as it clears out the listbox by erasing the background of
// each item.
//
SendMessage(hDirList, LB_RESETCONTENT, 0, 0L);
}
//
// Always open enumeration for *.*
//
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
SetCurrentDirectory(lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
hff = FindFirstFile(szStarDotStar, &FindFileData);
if ( hff == INVALID_HANDLE_VALUE)
{
//
// Error. Call GetLastError to determine what happened.
//
dwErr = GetLastError();
//
// With the ChangeDir logic handling AccessDenied for cds,
// if we are not allowed to enum files, that's ok, just get out.
//
if (dwErr == ERROR_ACCESS_DENIED)
{
wMask = mskDirectory;
goto Func4EFailure;
}
//
// For bad path of bad filename.
//
if (dwErr != ERROR_FILE_NOT_FOUND)
{
wMask = mskDrives;
goto Func4EFailure;
}
}
//
// A listing was made, even if empty.
//
bRet = TRUE;
wMask &= mskDirectory;
//
// GetLastError says no more files.
//
if (hff == INVALID_HANDLE_VALUE && dwErr == ERROR_FILE_NOT_FOUND)
{
//
// Things went well, but there are no files.
//
goto NoMoreFilesFound;
}
do
{
if (pOFI->pOFN->Flags & OFN_NOLONGNAMES)
{
UNICODE_STRING Name;
BOOLEAN fSpace = FALSE;
if (NT_SUCCESS(RtlInitUnicodeStringEx(&Name, FindFileData.cFileName)) && RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) && !fSpace)
{
//
// Legal 8.3 name and no spaces, so use the principal
// file name.
//
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName)));
}
else
{
if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
{
continue;
}
//
// Use the alternate file name.
//
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName)));
}
}
else
{
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName)));
}
if ((FindFileData.dwFileAttributes & EXCLBITS))
{
continue;
}
if ((pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT))
{
if (StrChr(szBuffer, CHAR_SPACE))
{
//
// HPFS does not support alternate filenames
// for multiselect, bump all spacey filenames.
//
if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
{
continue;
}
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName)));
}
}
if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (wMask & mskDirectory)
{
//
// Don't include the subdirectories "." and "..".
//
if (szBuffer[0] == CHAR_DOT)
{
if ((szBuffer[1] == CHAR_NULL) ||
((szBuffer[1] == CHAR_DOT) && (szBuffer[2] == CHAR_NULL)))
{
continue;
}
}
if (!bCasePreserved)
{
CharLower(szBuffer);
}
i = (WORD)SendMessage( hDirList,
LB_ADDSTRING,
0,
(DWORD_PTR)szBuffer );
}
}
else if (bFindAll)
{
if (!bCasePreserved)
{
CharLower(szBuffer);
}
SendMessage(hFileList, LB_ADDSTRING, 0, (DWORD_PTR)szBuffer);
}
} while (FindNextFile(hff, &FindFileData));
if (hff == INVALID_HANDLE_VALUE)
{
goto Func4EFailure;
}
FindClose(hff);
if (!bFindAll)
{
for (i = 0; lpszF[i]; i++)
{
if (!lstrcmpi(lpszF[i], szStarDotStar))
{
continue;
}
//
// Find First for each filter.
//
hff = FindFirstFile(lpszF[i], &FindFileData);
if (hff == INVALID_HANDLE_VALUE)
{
DWORD dwErr = GetLastError();
if ((dwErr == ERROR_FILE_NOT_FOUND) ||
(dwErr == ERROR_INVALID_NAME))
{
//
// Things went well, but there are no files.
//
continue;
}
else
{
wMask = mskDrives;
goto Func4EFailure;
}
}
do
{
if (pOFI->pOFN->Flags & OFN_NOLONGNAMES)
{
UNICODE_STRING Name;
BOOLEAN fSpace = FALSE;
if (NT_SUCCESS(RtlInitUnicodeStringEx(&Name, FindFileData.cFileName)) && RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) && !fSpace)
{
//
// Legal 8.3 name and no spaces, so use the principal
// file name.
//
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName)));
}
else
{
if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
{
continue;
}
//
// Use the alternate file name.
//
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName)));
}
}
else
{
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cFileName)));
if (pOFI->pOFN->Flags & OFN_ALLOWMULTISELECT)
{
if (StrChr(szBuffer, CHAR_SPACE))
{
//
// HPFS does not support alternate filenames
// for multiselect, bump all spacey filenames.
//
if (FindFileData.cAlternateFileName[0] == CHAR_NULL)
{
continue;
}
EVAL(SUCCEEDED(StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), FindFileData.cAlternateFileName)));
}
}
}
if ((FindFileData.dwFileAttributes & EXCLBITS) ||
(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
continue;
}
if (!bCasePreserved)
{
CharLower(szBuffer);
}
SendMessage(hFileList, LB_ADDSTRING, 0, (DWORD_PTR)szBuffer);
} while (FindNextFile(hff, &FindFileData));
if (hff != INVALID_HANDLE_VALUE)
{
FindClose(hff);
}
}
}
NoMoreFilesFound:
Func4EFailure:
if (wMask)
{
if (wMask == mskDirectory)
{
LPTSTR lpCurDir = NULL;
if (lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg))
{
lpCurDir = lpCurDlg->lpstrCurDir;
}
FillOutPath(hDirList, pOFI);
//
// The win31 way of chopping the text by just passing
// it on to user doesn't work for unc names since user
// doesn't see the drivelessness of them (thinks drive is
// a bslash char). So, special case it here.
//
EVAL(SUCCEEDED(StringCchCopy(pOFI->szPath, ARRAYSIZE(pOFI->szPath), lpCurDir)));
if (DBL_BSLASH(pOFI->szPath))
{
SetDlgItemText(hDlg, stc1, ChopText(hDlg, stc1, pOFI->szPath));
}
else
{
DlgDirList(hDlg, pOFI->szPath, 0, stc1, DDL_READONLY);
}
SendMessage(hDirList, LB_SETCURSEL, pOFI->idirSub - 1, 0L);
if (bDriveChange)
{
//
// The design here is to show the selected drive whenever the
// user changes drives, or whenever the number of
// subdirectories is sufficiently low to allow them to be
// shown along with the drive. Otherwise, show the
// immediate parent and all the children that can be shown.
// This all was done to meet the UITF spec.
//
i = 0;
}
else
{
//
// Show as many children as possible.
//
if ((i = (SHORT)(pOFI->idirSub - 2)) < 0)
{
i = 0;
}
}
//
// LB_SETTOPINDEX must be after LB_SETCURSEL, as LB_SETCURSEL will
// alter the top index to bring the current selection into view.
//
SendMessage(hDirList, LB_SETTOPINDEX, (WPARAM)i, 0L);
}
else
{
SetDlgItemText(hDlg, stc1, szNull);
}
wNoRedraw &= ~2;
SendMessage(hDirList, WM_SETREDRAW, TRUE, 0L);
GetWindowRect(hDirList, (LPRECT)&rDirLBox);
rDirLBox.left++, rDirLBox.top++;
rDirLBox.right--, rDirLBox.bottom--;
MapWindowPoints(NULL, hDlg, (LPPOINT)&rDirLBox, 2);
//
// If there are less than enough directories to fill the listbox,
// Win 3.0 doesn't clear out the bottom. Pass TRUE as the last
// parameter to demand a WM_ERASEBACKGROUND message.
//
InvalidateRect(hDlg, (LPRECT)&rDirLBox, (BOOL)(wWinVer < 0x030A));
}
SendMessage(hFileList, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hFileList, (LPRECT)0, (BOOL)TRUE);
#ifndef WIN32
ResetDTAAddress();
#endif
HourGlass(FALSE);
return (bRet);
}
// Stores <cch> in pOFN->lpstrFile
void StoreFileSizeInOFN(LPOPENFILENAME pOFN, UINT cch)
{
ASSERT(cch >= pOFN->nMaxFile);
if (pOFN->nMaxFile > 0)
pOFN->lpstrFile[0] = (TCHAR)LOWORD(cch);
if (pOFN->nMaxFile > 1)
pOFN->lpstrFile[1] = (TCHAR)HIWORD(cch);
if (pOFN->nMaxFile > 2)
pOFN->lpstrFile[2] = CHAR_NULL;
}
// Copies pszPath into pOFN->lpstrFile if room, stores lstrlen(pszPath) otherwise
void StorePathOrFileSizeInOFN(LPOPENFILENAME pOFN, LPTSTR pszPath)
{
if (pOFN->lpstrFile)
{
UINT cch = lstrlen(pszPath);
if (cch < pOFN->nMaxFile)
{
StringCchCopy(pOFN->lpstrFile, pOFN->nMaxFile, pszPath); // Can never fail.
}
else
{
StoreFileSizeInOFN(pOFN, cch+1);
}
}
}
//
// Fix bug where progman cannot OK a file being browsed for new
// item because it has Execute only permission.
// Returns FALSE if handle passed in was invalid, but not because of ERROR_ACCESS_DENIED
// Otherwise returns TRUE.
//
HANDLE ProgManBugCreateFile(HANDLE hFile, LPCTSTR szPathName, DWORD *dwErrCode)
{
if (hFile == INVALID_HANDLE_VALUE)
{
*dwErrCode = GetLastError();
if (*dwErrCode == ERROR_ACCESS_DENIED)
{
hFile = CreateFile( szPathName,
GENERIC_EXECUTE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (hFile == INVALID_HANDLE_VALUE)
{
*dwErrCode = GetLastError();
}
}
}
return hFile;
}
#define SP_NOERR 0
#define SP_INVALIDDRIVE 1
#define SP_PATHNOTFOUND 2
////////////////////////////////////////////////////////////////////////////
//
// OKButtonPressed
//
// Note: There are 4 cases for validation of a file name:
// 1) OFN_NOVALIDATE allows invalid characters
// 2) No validation flags No invalid characters, but path need not exist
// 3) OFN_PATHMUSTEXIST No invalid characters, path must exist
// 4) OFN_FILEMUSTEXIST No invalid characters, path & file must exist
//
////////////////////////////////////////////////////////////////////////////
BOOL OKButtonPressed(
HWND hDlg,
POPENFILEINFO pOFI,
BOOL bSave)
{
DWORD nErrCode = 0;
DWORD cch;
DWORD cchSearchPath;
LPOPENFILENAME pOFN = pOFI->pOFN;
int nFileOffset, nExtOffset;
HANDLE hFile;
BOOL bAddExt = FALSE;
BOOL bUNCName = FALSE;
int nTempOffset;
TCHAR szPathName[MAX_FULLPATHNAME];
DWORD lRet;
BOOL blfn;
LPCURDLG lpCurDlg;
TCHAR ch = 0;
cch = GetUNCDirectoryFromLB(hDlg, lst2, pOFI); // Is it a tree headed by a UNC share?
if (cch)
{
// If so, cch now points to the end of the entire name, and we might have
// pOFI->szPath be something like \\foo\bar\bar\bar
nTempOffset = (WORD)(DWORD)SendDlgItemMessage( hDlg,
lst2,
LB_GETTEXTLEN,
0,
0 );
// While nTempOffset points to the end of just the UNC part (so to \bar\bar)
}
else
{
nTempOffset = 0; // and of course cch == 0
}
GetDlgItemText(hDlg, edt1, pOFI->szPath + cch, ARRAYSIZE(pOFI->szPath) - 1 - cch);
if (cch)
{
//
// If a drive or new UNC was specified, forget the old UNC.
//
if ((pOFI->szPath[cch + 1] == CHAR_COLON) ||
(DBL_BSLASH(pOFI->szPath + cch)) )
{
StringCopyOverlap(pOFI->szPath, pOFI->szPath + cch);
}
else if ((ISBACKSLASH(pOFI->szPath, cch)) ||
(pOFI->szPath[cch] == CHAR_SLASH))
{
//
// If a directory from the root is given, put it immediately
// after the \\server\share listing.
//
// For example, if the directory is \\foo\bar\bar\ba, the user typed \x in the editbox,
// give us \\foo\bar\x
EVAL(SUCCEEDED(StringCchCopy(pOFI->szPath + nTempOffset, ARRAYSIZE(pOFI->szPath) - nTempOffset, pOFI->szPath + cch)));
}
}
if (pOFN->Flags & OFN_NOLONGNAMES)
{
blfn = FALSE;
}
else
{
blfn = IsLFNDriveX(hDlg, pOFI->szPath);
}
lRet = ParseFile(pOFI->szPath, blfn, IS16BITWOWAPP(pOFN), FALSE);
nFileOffset = (int)(SHORT)LOWORD(lRet);
nExtOffset = (int)(SHORT)HIWORD(lRet);
if (nFileOffset == PARSE_EMPTYSTRING)
{
UpdateListBoxes(hDlg, pOFI, NULL, 0);
return (FALSE);
}
else if ((nFileOffset != PARSE_DIRECTORYNAME) &&
(pOFN->Flags & OFN_NOVALIDATE))
{
pOFN->nFileOffset = (WORD)(nFileOffset >= 0 ? nFileOffset : lstrlen(pOFI->szPath)); // point at NULL in error case
pOFN->nFileExtension = (WORD)nExtOffset;
StorePathOrFileSizeInOFN(pOFN, pOFI->szPath);
return (TRUE);
}
else if ((pOFN->Flags & OFN_ALLOWMULTISELECT) &&
SpacesExist(pOFI->szPath))
{
return (MultiSelectOKButton(hDlg, pOFI, bSave));
}
else if (pOFI->szPath[nExtOffset] == CHAR_SEMICOLON)
{
pOFI->szPath[nExtOffset] = CHAR_NULL;
nFileOffset = (int)(SHORT)LOWORD(ParseFile( pOFI->szPath,
blfn,
IS16BITWOWAPP(pOFN),
FALSE ));
pOFI->szPath[nExtOffset] = CHAR_SEMICOLON;
if ( (nFileOffset >= 0) &&
(StrChr(pOFI->szPath + nFileOffset, CHAR_STAR) ||
StrChr(pOFI->szPath + nFileOffset, CHAR_QMARK)) )
{
EVAL(SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFI->szPath + nFileOffset)));
if (FListAll(pOFI, hDlg, pOFI->szPath, ARRAYSIZE(pOFI->szPath)) == CHANGEDIR_FAILED)
{
//
// Conform with cchSearchPath error code settings in
// PathCheck.
//
cchSearchPath = SP_PATHNOTFOUND;
goto PathCheck;
}
return (FALSE);
}
else
{
nFileOffset = PARSE_INVALIDCHAR;
goto Warning;
}
}
else if (nFileOffset == PARSE_DIRECTORYNAME)
{
//
// End with slash?
//
if ((ISBACKSLASH(pOFI->szPath, nExtOffset - 1)) ||
(pOFI->szPath[nExtOffset - 1] == CHAR_SLASH))
{
//
// ... and is not the root, get rid of the slash.
//
if ( (nExtOffset != 1) &&
(pOFI->szPath[nExtOffset - 2] != CHAR_COLON) &&
(nExtOffset != nTempOffset + 1) )
{
pOFI->szPath[nExtOffset - 1] = CHAR_NULL;
}
}
else if ((pOFI->szPath[nExtOffset - 1] == CHAR_DOT) &&
((pOFI->szPath[nExtOffset - 2] == CHAR_DOT) ||
(ISBACKSLASH(pOFI->szPath, nExtOffset - 2)) ||
(pOFI->szPath[nExtOffset - 2] == CHAR_SLASH)) &&
((DBL_BSLASH(pOFI->szPath)) ||
((*(pOFI->szPath + 1) == CHAR_COLON) &&
(DBL_BSLASH(pOFI->szPath + 2)))))
{
pOFI->szPath[nExtOffset] = CHAR_BSLASH;
pOFI->szPath[nExtOffset + 1] = CHAR_NULL;
}
//
// Fall through to Directory Checking.
//
}
else if (nFileOffset < 0)
{
//
// Put in nErrCode so that call can be used from other points.
//
nErrCode = (DWORD)nFileOffset;
Warning:
//
// If the disk is not a floppy and they tell me there's no
// disk in the drive, dont believe it. Instead, put up the error
// message that they should have given us.
// (Note that the error message is checked first since checking
// the drive type is slower.)
//
if (nErrCode == ERROR_ACCESS_DENIED)
{
if (bUNCName)
{
nErrCode = ERROR_NETWORK_ACCESS_DENIED;
}
else
{
szPathName[0] = CharLowerChar(szPathName[0]);
if (GetDiskType(szPathName) == DRIVE_REMOTE)
{
nErrCode = ERROR_NETWORK_ACCESS_DENIED;
}
else if (GetDiskType(szPathName) == DRIVE_REMOVABLE)
{
nErrCode = ERROR_NO_DISK_IN_DRIVE;
}
else if (GetDiskType(szPathName) == DRIVE_CDROM)
{
nErrCode = ERROR_NO_DISK_IN_CDROM;
}
}
}
if ((nErrCode == ERROR_WRITE_PROTECT) ||
(nErrCode == ERROR_CANNOT_MAKE) ||
(nErrCode == ERROR_NO_DISK_IN_DRIVE) ||
(nErrCode == ERROR_NO_DISK_IN_CDROM))
{
pOFI->szPath[0] = szPathName[0];
}
InvalidFileWarning(hDlg, pOFI->szPath, nErrCode, 0);
//
// Can't cd case (don't want WM_ACTIVATE to setevent to GetNetDrives!).
// Reset wNoRedraw.
//
wNoRedraw &= ~1;
return (FALSE);
}
bUNCName = ((DBL_BSLASH(pOFI->szPath)) ||
((*(pOFI->szPath + 1) == CHAR_COLON) &&
(DBL_BSLASH(pOFI->szPath + 2))));
ASSERT((nFileOffset >= 0 ) || (nFileOffset == PARSE_DIRECTORYNAME)); // PARSE_DIRECTORYNAME is the only error case handled below...
nTempOffset = nFileOffset;
//
// Get the fully-qualified path.
//
{
BOOL bSlash;
BOOL bRet;
WORD nNullOffset;
// Chop of the file part of the path in pOFI->szPath for the time being, to ensure we're dealing with a directory.
if (nFileOffset != PARSE_DIRECTORYNAME)
{
ch = *(pOFI->szPath + nFileOffset);
*(pOFI->szPath + nFileOffset) = CHAR_NULL;
nNullOffset = (WORD) nFileOffset;
}
//
// For files of the format c:filename where c is not the
// current directory, SearchPath does not return the curdir of c
// so, prefetch it - should searchpath be changed?
//
if (nFileOffset > 0)
{
if (*(pOFI->szPath + nFileOffset - 1) == CHAR_COLON)
{
//
// If it fails, fall through to the error generated below.
//
if (ChangeDir(hDlg, pOFI->szPath, FALSE, FALSE) != CHANGEDIR_FAILED)
{
//
// Replace old null offset.
//
*(pOFI->szPath + nFileOffset) = ch;
ch = *pOFI->szPath;
//
// Don't pass drive-colon into search path.
//
*pOFI->szPath = CHAR_NULL;
nNullOffset = 0;
}
}
}
bSlash = (*pOFI->szPath == CHAR_SLASH);
if (bSlash)
{
*pOFI->szPath = CHAR_BSLASH;
}
szPathName[0] = CHAR_NULL;
HourGlass(TRUE);
//
// REARCHITECT:
// Each wow thread can change the current directory.
// Since searchpath doesn't check current dirs on a per thread basis,
// reset it here and hope that we don't get interrupted between
// setting and searching...
//
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
SetCurrentDirectory(lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
if (pOFI->szPath[0] == TEXT('\0')) // space for name (pretend it's valid for now)
{
EVAL(SUCCEEDED(StringCchCopyEx(szPathName, ARRAYSIZE(szPathName), (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL), NULL, NULL, STRSAFE_IGNORE_NULLS)));
bRet = 1;
}
else
{
bRet = GetFullPathName( pOFI->szPath,
ARRAYSIZE(szPathName),
szPathName,
NULL );
}
// Now szPathName contains the current directory (if pOFI->szPath was empty), or pOFI->szPath (fully qualified path?)
// Except in the failure case, where bRet == FALSE... like say for example we entered a bad drive:
if (!bRet && (pOFI->szPath[1] == CHAR_COLON))
{
int nDriveIndex = DiskAddedPreviously(pOFI->szPath[0], NULL);
//
// If it's a remembered connection, try to reconnect it.
//
if (nDriveIndex != 0xFFFFFFFF &&
gaDiskInfo[nDriveIndex].dwType == REMDRVBMP)
{
DWORD err = WNetRestoreConnection( hDlg,
gaDiskInfo[nDriveIndex].lpPath );
if (err == WN_SUCCESS)
{
gaDiskInfo[nDriveIndex].dwType = NETDRVBMP;
nDriveIndex = (int) SendDlgItemMessage(
hDlg,
cmb2,
CB_SELECTSTRING,
(WPARAM)-1,
(LPARAM)(LPTSTR)gaDiskInfo[nDriveIndex].lpPath );
SendDlgItemMessage( hDlg,
cmb2,
CB_SETITEMDATA,
(WPARAM)nDriveIndex,
(LPARAM)NETDRVBMP );
bRet = GetFullPathName( pOFI->szPath,
MAX_FULLPATHNAME,
szPathName,
NULL);
}
}
}
HourGlass(FALSE);
if (nFileOffset != PARSE_DIRECTORYNAME)
{
*(pOFI->szPath + nNullOffset) = ch;
}
if (bSlash)
{
*pOFI->szPath = CHAR_SLASH;
}
if (bRet) // We got something.
{
cchSearchPath = SP_NOERR;
if (nFileOffset != PARSE_DIRECTORYNAME)
{
// Add file name to path
// (Can't use PathCombine, because it only fits MAX_PATH)
if (!ISBACKSLASH(szPathName, lstrlen(szPathName) - 1))
{
EVAL(SUCCEEDED(StringCchCat(szPathName, ARRAYSIZE(szPathName), L"\\"))); // Shouldn't be longer than MAX_FULLPATH
}
EVAL(SUCCEEDED(StringCchCat(szPathName, ARRAYSIZE(szPathName), (pOFI->szPath + nFileOffset)))); // Shouldn't be longer than MAX_FULLPATH
}
else // we were given a directory name, try to go to that directory.
{
//
// Hack to get around SearchPath inconsistencies.
//
// searching for c: returns c:
// searching for server share dir1 .. returns server share
// in these two cases bypass the regular ChangeDir call that
// uses szPathName and use the original pOFI->szPath instead
// OKButtonPressed needs to be simplified!
//
int cch = GetPathOffset(pOFI->szPath);
// cch now points to the last \ in "c:\" or the "\\foo\bar\"
if (cch > 0)
{
if (bUNCName)
{
//
// If this fails, how is szPathName used?
// szPathName's disk should equal pOFI->szPath's
// so the cch will be valid.
//
szPathName[cch] = CHAR_BSLASH;
szPathName[cch + 1] = CHAR_NULL; // Removing the file part of the path.
if (ChangeDir( hDlg,
pOFI->szPath,
FALSE,
TRUE ) != CHANGEDIR_FAILED)
{
goto ChangedDir;
}
}
else
{
// It's a drive letter. We only changedir if there is nothing after the drive letter.
if (!pOFI->szPath[cch])
{
if (ChangeDir( hDlg,
pOFI->szPath,
FALSE,
TRUE) != CHANGEDIR_FAILED)
{
goto ChangedDir;
}
}
}
}
}
}
else // (!bRet)
{
// Some kind of invalid path... perhaps a search pattern?
if (!(pOFN->Flags & OFN_PATHMUSTEXIST))
{
EVAL(SUCCEEDED(StringCchCopy(szPathName, ARRAYSIZE(szPathName), pOFI->szPath))); // Must fit in MAX_FULLPATH
}
nErrCode = GetLastError();
if ((nErrCode == ERROR_INVALID_DRIVE) ||
(pOFI->szPath[1] == CHAR_COLON))
{
cchSearchPath = SP_INVALIDDRIVE;
}
else
{
cchSearchPath = SP_PATHNOTFOUND;
}
}
}
//
// Full pattern?
//
if ( !cchSearchPath && (nFileOffset >= 0) &&
((StrChr(pOFI->szPath + nFileOffset, CHAR_STAR)) ||
(StrChr(pOFI->szPath + nFileOffset, CHAR_QMARK))) )
{
// Search pattern....
TCHAR szSameDirFile[MAX_FULLPATHNAME];
if (nTempOffset > 0)
{
//
// Must restore character in case it is part of the filename,
// e.g. nTempOffset is 1 for "\foo.txt".
//
ch = pOFI->szPath[nTempOffset];
pOFI->szPath[nTempOffset] = 0;
ChangeDir(hDlg, pOFI->szPath, FALSE, TRUE);
pOFI->szPath[nTempOffset] = ch;
}
// If there is a file extension, or if there wasn't, and we succeeded in putting a dot where the filename is (?), then
if (nExtOffset || SUCCEEDED(StringCchCat(pOFI->szPath + nFileOffset, ARRAYSIZE(pOFI->szPath) - nFileOffset, L".")))
{
if (SUCCEEDED(StringCchCopy(pOFI->szLastFilter, ARRAYSIZE(pOFI->szLastFilter), pOFI->szPath + nFileOffset)))
{
if (SUCCEEDED(StringCchCopy(szSameDirFile, ARRAYSIZE(szSameDirFile), pOFI->szPath + nFileOffset)))
{
// Redo list, based on new file spec:
if (FListAll(pOFI, hDlg, szSameDirFile, ARRAYSIZE(szSameDirFile)) < 0)
{
MessageBeep(0);
}
}
}
}
return (FALSE);
}
//
// We either have a file pattern or a real file.
// If its a directory
// (1) Add on default pattern
// (2) Act like its a pattern (goto pattern (1))
// Else if its a pattern
// (1) Update everything
// (2) display files in whatever dir were now in
// Else if its a file name!
// (1) Check out the syntax
// (2) End the dialog given OK
// (3) Beep/message otherwise
//
//
// Drive-letter:\dirpath ??
//
if (!cchSearchPath)
{
DWORD dwFileAttr = GetFileAttributes(szPathName);
if (dwFileAttr != 0xFFFFFFFF)
{
if (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY)
{
if (ChangeDir(hDlg, szPathName, FALSE, TRUE) != CHANGEDIR_FAILED)
{
ChangedDir:
SendDlgItemMessage(hDlg, edt1, WM_SETREDRAW, FALSE, 0L);
if (*pOFI->szLastFilter)
{
SetDlgItemText(hDlg, edt1, pOFI->szLastFilter);
}
else
{
SetDlgItemText(hDlg, edt1, szStarDotStar);
}
SendMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS( cmb1,
GetDlgItem(hDlg, cmb1),
CBN_CLOSEUP ) );
SendMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS( cmb2,
GetDlgItem(hDlg, cmb2),
MYCBN_CHANGEDIR ) );
SendDlgItemMessage(hDlg, edt1, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(GetDlgItem(hDlg, edt1), NULL, FALSE);
}
return (FALSE);
}
}
}
//
// Was there a path and did it fail?
//
if (nFileOffset && cchSearchPath && (pOFN->Flags & OFN_PATHMUSTEXIST))
{
PathCheck:
if (cchSearchPath == SP_PATHNOTFOUND)
{
nErrCode = ERROR_PATH_NOT_FOUND;
}
else if (cchSearchPath == SP_INVALIDDRIVE)
{
int nDriveIndex;
//
// Lowercase drive letters since DiskAddedPreviously is case
// sensitive.
//
CharLower(pOFI->szPath);
// We can get here without performing an OpenFile call. As such
// the szPathName can be filled with random garbage. Since we
// only need one character for the error message, set
// szPathName[0] to the drive letter.
//
if (pOFI->szPath[1] == CHAR_COLON)
{
nDriveIndex = DiskAddedPreviously(pOFI->szPath[0], NULL);
}
else
{
nDriveIndex = DiskAddedPreviously(0, pOFI->szPath);
}
if (nDriveIndex == 0xFFFFFFFF)
{
nErrCode = ERROR_NO_DRIVE;
}
else
{
if (bUNCName)
{
nErrCode = ERROR_NO_DRIVE;
}
else
{
switch (GetDiskType(pOFI->szPath))
{
case ( DRIVE_REMOVABLE ) :
{
szPathName[0] = pOFI->szPath[0];
nErrCode = ERROR_NO_DISK_IN_DRIVE;
break;
}
case ( DRIVE_CDROM ) :
{
szPathName[0] = pOFI->szPath[0];
nErrCode = ERROR_NO_DISK_IN_CDROM;
break;
}
default :
{
nErrCode = ERROR_PATH_NOT_FOUND;
}
}
}
}
}
else
{
// We can never get here, because cchSearchPath must equal 0, SP_PATHNOTFOUND or SP_INVALIDDRIVE.
ASSERT(FALSE);
nErrCode = ERROR_FILE_NOT_FOUND;
}
//
// If we don't set wNoRedraw here, then WM_ACTIVATE will set the
// GetNetDrives event.
//
wNoRedraw |= 1;
goto Warning;
}
if ((nFileOffset != PARSE_DIRECTORYNAME) && PortName(pOFI->szPath + nFileOffset))
{
nErrCode = ERROR_PORTNAME;
goto Warning;
}
//
// Add the default extension unless filename ends with period or no
// default extension exists. If the file exists, consider asking
// permission to overwrite the file.
//
// NOTE: When no extension given, default extension is tried 1st.
//
if ( (nFileOffset != PARSE_DIRECTORYNAME) &&
nExtOffset &&
!pOFI->szPath[nExtOffset] &&
pOFN->lpstrDefExt &&
*pOFN->lpstrDefExt &&
(((DWORD)nExtOffset + lstrlen(pOFN->lpstrDefExt)) < pOFN->nMaxFile) )
{
DWORD dwFileAttr;
int nExtOffset2 = lstrlen(szPathName);
if (AppendExt(pOFI->szPath, ARRAYSIZE(pOFI->szPath), pOFN->lpstrDefExt, FALSE) &&
AppendExt(szPathName, ARRAYSIZE(szPathName), pOFN->lpstrDefExt, FALSE))
{
bAddExt = TRUE;
//
// Directory may match default extension. Change to it as if it had
// been typed in. A dir w/o the extension would have been switched
// to in the logic above.
//
if ((dwFileAttr = GetFileAttributes(pOFI->szPath)) != 0xFFFFFFFF)
{
if (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY)
{
if (ChangeDir(hDlg, szPathName, FALSE, TRUE) != CHANGEDIR_FAILED)
{
goto ChangedDir;
}
}
}
hFile = CreateFile( szPathName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
hFile = ProgManBugCreateFile(hFile, szPathName, &nErrCode);
if (nErrCode == ERROR_SHARING_VIOLATION)
{
goto SharingViolationInquiry;
}
if (hFile != INVALID_HANDLE_VALUE)
{
if (!CloseHandle(hFile))
{
nErrCode = GetLastError();
goto Warning;
}
AskPermission:
//
// Is the file read-only?
//
if (pOFN->Flags & OFN_NOREADONLYRETURN)
{
int nRet;
if ((nRet = GetFileAttributes(szPathName)) != -1)
{
if (nRet & ATTR_READONLY)
{
nErrCode = ERROR_LAZY_READONLY;
goto Warning;
}
}
else
{
nErrCode = GetLastError();
goto Warning;
}
}
if ((bSave || (pOFN->Flags & OFN_NOREADONLYRETURN)) &&
(nErrCode == ERROR_ACCESS_DENIED))
{
goto Warning;
}
if (pOFN->Flags & OFN_OVERWRITEPROMPT)
{
if (bSave && !FOkToWriteOver(hDlg, szPathName))
{
PostMessage( hDlg,
WM_NEXTDLGCTL,
(WPARAM)GetDlgItem(hDlg, edt1),
(LPARAM)1L );
return (FALSE);
}
}
if (nErrCode == ERROR_SHARING_VIOLATION)
{
goto SharingViolationInquiry;
}
goto FileNameAccepted;
}
else
{
// Remove the file extension.
*(pOFI->szPath + nExtOffset) = CHAR_NULL;
szPathName[nExtOffset2] = CHAR_NULL;
}
}
}
// else bAddExt is still FALSE
hFile = CreateFile( szPathName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
hFile = ProgManBugCreateFile(hFile, szPathName, &nErrCode);
if (hFile != INVALID_HANDLE_VALUE)
{
if (!CloseHandle(hFile))
{
nErrCode = GetLastError();
goto Warning;
}
goto AskPermission;
}
else
{
if ((nErrCode == ERROR_FILE_NOT_FOUND) ||
(nErrCode == ERROR_PATH_NOT_FOUND))
{
//
// Figure out if the default extension should be tacked on.
//
// (Note: there is only one way in here where bAddExt is TRUE, and in that
// case we know for certain that AppendExt will succeed)
if (bAddExt)
{
EVAL(AppendExt(pOFI->szPath, ARRAYSIZE(pOFI->szPath), pOFN->lpstrDefExt, FALSE));
EVAL(AppendExt(szPathName, ARRAYSIZE(szPathName), pOFN->lpstrDefExt, FALSE));
}
}
else if (nErrCode == ERROR_SHARING_VIOLATION)
{
SharingViolationInquiry:
//
// If the app is "share aware", fall through.
// Otherwise, ask the hook function.
//
if (!(pOFN->Flags & OFN_SHAREAWARE))
{
if (pOFN->lpfnHook)
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
if (pOFI->ApiType == COMDLG_ANSI)
{
CHAR szPathNameA[MAX_FULLPATHNAME];
RtlUnicodeToMultiByteSize(
&cch,
szPathName,
lstrlenW(szPathName) * sizeof(TCHAR) );
SHUnicodeToAnsi(szPathName,(LPSTR)&szPathNameA[0],cch + 1);
cch = (DWORD)(*lpfnHook)( hDlg,
msgSHAREVIOLATIONA,
0,
(LONG_PTR)(LPSTR)szPathNameA );
}
else
{
cch = (DWORD)(*lpfnHook)( hDlg,
msgSHAREVIOLATIONW,
0,
(LONG_PTR)szPathName );
}
if (cch == OFN_SHARENOWARN)
{
return (FALSE);
}
else if (cch != OFN_SHAREFALLTHROUGH)
{
goto Warning;
}
}
else
{
goto Warning;
}
}
goto FileNameAccepted;
}
if (!bSave)
{
if ((nErrCode == ERROR_FILE_NOT_FOUND) ||
(nErrCode == ERROR_PATH_NOT_FOUND))
{
if (pOFN->Flags & OFN_FILEMUSTEXIST)
{
if (pOFN->Flags & OFN_CREATEPROMPT)
{
//
// Don't alter pOFI->szPath.
//
bInChildDlg = TRUE;
cch = (DWORD)CreateFileDlg(hDlg, pOFI->szPath);
bInChildDlg = FALSE;
if (cch == IDYES)
{
goto TestCreation;
}
else
{
return (FALSE);
}
}
goto Warning;
}
}
else
{
goto Warning;
}
}
//
// The file doesn't exist. Can it be created? This is needed because
// there are many extended characters which are invalid that won't be
// caught by ParseFile.
// Two more good reasons: Write-protected disks & full disks.
//
// BUT, if they dont want the test creation, they can request that we
// not do it using the OFN_NOTESTFILECREATE flag. If they want to
// create files on a share that has create-but-no-modify privileges,
// they should set this flag but be ready for failures that couldn't
// be caught, such as no create privileges, invalid extended
// characters, a full disk, etc.
//
TestCreation:
if ((pOFN->Flags & OFN_PATHMUSTEXIST) &&
(!(pOFN->Flags & OFN_NOTESTFILECREATE)))
{
//
// Must use the FILE_FLAG_DELETE_ON_CLOSE flag so that the
// file is automatically deleted when the handle is closed
// (no need to call DeleteFile). This is necessary in the
// event that the directory only has Add & Read access.
// The CreateFile call will succeed, but the DeleteFile call
// will fail. By adding the above flag to the CreateFile
// call, it overrides the access rights and deletes the file
// during the call to CloseHandle.
//
hFile = CreateFile( szPathName,
FILE_ADD_FILE,
0,
NULL,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
NULL );
if (hFile == INVALID_HANDLE_VALUE)
{
nErrCode = GetLastError();
}
if (hFile != INVALID_HANDLE_VALUE)
{
if (!CloseHandle(hFile))
{
nErrCode = GetLastError();
goto Warning;
}
}
else
{
//
// Unable to create it.
//
// If it's not write-protection, a full disk,
// network protection, or the user popping the drive door
// open, assume that the filename is invalid.
//
if ( (nErrCode != ERROR_WRITE_PROTECT) &&
(nErrCode != ERROR_CANNOT_MAKE) &&
(nErrCode != ERROR_NETWORK_ACCESS_DENIED) &&
(nErrCode != ERROR_ACCESS_DENIED) )
{
nErrCode = 0;
}
goto Warning;
}
}
}
FileNameAccepted:
HourGlass(TRUE);
lRet = ParseFile(szPathName, blfn, IS16BITWOWAPP(pOFN), FALSE);
nFileOffset = (int)(SHORT)LOWORD(lRet);
cch = (DWORD)HIWORD(lRet);
ASSERT(nFileOffset >= 0); // if the filename was accepted, we better parse it without error...
pOFN->nFileOffset = (WORD)nFileOffset;
if (nExtOffset || bAddExt)
{
pOFN->nFileExtension = LOWORD(cch);
}
else
{
pOFN->nFileExtension = 0;
}
pOFN->Flags &= ~OFN_EXTENSIONDIFFERENT;
if (pOFN->lpstrDefExt && pOFN->nFileExtension)
{
TCHAR szPrivateExt[4];
SHORT i;
for (i = 0; i < 3; i++)
{
szPrivateExt[i] = *(pOFN->lpstrDefExt + i);
}
szPrivateExt[3] = CHAR_NULL;
if (lstrcmpi(szPrivateExt, szPathName + cch))
{
pOFN->Flags |= OFN_EXTENSIONDIFFERENT;
}
}
//
// If we're called from wow, and the user hasn't changed
// directories, shorten the path to abbreviated 8.3 format.
//
if (pOFN->Flags & OFN_NOLONGNAMES)
{
ShortenThePath(szPathName);
//
// If the path was shortened, the offset might have changed so
// we must parse the file again.
//
lRet = ParseFile(szPathName, blfn, IS16BITWOWAPP(pOFN), FALSE);
nFileOffset = (int)(SHORT)LOWORD(lRet);
cch = (DWORD)HIWORD(lRet);
ASSERT(nFileOffset >= 0); // shortening the path better not introduce a parse error...
//
// When in Save dialog, the file may not exist yet, so the file
// name cannot be shortened. So, we need to test if it's an
// 8.3 filename and popup an error message if not.
//
if (bSave)
{
LPTSTR lptmp;
LPTSTR lpExt = NULL;
for (lptmp = szPathName + nFileOffset; *lptmp; lptmp++)
{
if (*lptmp == CHAR_DOT)
{
if (lpExt)
{
//
// There's more than one dot in the file, so it is
// invalid.
//
nErrCode = FNERR_INVALIDFILENAME;
goto Warning;
}
lpExt = lptmp;
}
if (*lptmp == CHAR_SPACE)
{
nErrCode = FNERR_INVALIDFILENAME;
goto Warning;
}
}
if (lpExt)
{
//
// There's an extension.
//
*lpExt = 0;
}
if ((lstrlen(szPathName + nFileOffset) > 8) ||
(lpExt && lstrlen(lpExt + 1) > 3))
{
if (lpExt)
{
*lpExt = CHAR_DOT;
}
nErrCode = FNERR_INVALIDFILENAME;
goto Warning;
}
if (lpExt)
{
*lpExt = CHAR_DOT;
}
}
}
StorePathOrFileSizeInOFN(pOFN, szPathName);
//
// File Title. Note that it's cut off at whatever the buffer length
// is, so if the buffer is too small, *no notice is given*.
//
if (pOFN->lpstrFileTitle && pOFN->nMaxFileTitle)
{
cch = lstrlen(szPathName + nFileOffset);
if (cch > pOFN->nMaxFileTitle)
{
szPathName[nFileOffset + pOFN->nMaxFileTitle - 1] = CHAR_NULL;
}
EVAL(SUCCEEDED(StringCchCopy(pOFN->lpstrFileTitle, pOFN->nMaxFileTitle, szPathName + nFileOffset)));
}
if (!(pOFN->Flags & OFN_HIDEREADONLY))
{
if (IsDlgButtonChecked(hDlg, chx1))
{
pOFN->Flags |= OFN_READONLY;
}
else
{
pOFN->Flags &= ~OFN_READONLY;
}
}
return (TRUE);
}
WCHAR c_szSpace[] = L" ";
////////////////////////////////////////////////////////////////////////////
//
// MultiSelectOKButton
//
////////////////////////////////////////////////////////////////////////////
BOOL MultiSelectOKButton(
HWND hDlg,
POPENFILEINFO pOFI,
BOOL bSave)
{
DWORD nErrCode;
LPTSTR lpCurDir;
LPTSTR lpchStart; // start of an individual filename
LPTSTR lpchEnd; // end of an individual filename
DWORD cch;
HANDLE hFile;
LPOPENFILENAME pOFN;
BOOL EOS = FALSE; // end of string flag
BOOL bRet;
TCHAR szPathName[MAX_FULLPATHNAME - 1];
LPCURDLG lpCurDlg;
pOFN = pOFI->pOFN;
//
// Check for space for first full path element.
//
if(!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
!(lpCurDir = lpCurDlg->lpstrCurDir))
{
return (FALSE);
}
EVAL(SUCCEEDED(StringCchCopy(pOFI->szPath, ARRAYSIZE(pOFI->szPath), lpCurDir)));
if (StrChr(pOFI->szPath, CHAR_SPACE))
{
GetShortPathName(pOFI->szPath, pOFI->szPath, ARRAYSIZE(pOFI->szPath));
}
if (!bCasePreserved)
{
CharLower(pOFI->szPath);
}
cch = (DWORD)(lstrlen(pOFI->szPath) + ARRAYSIZE(c_szSpace) + SendDlgItemMessage(hDlg, edt1, WM_GETTEXTLENGTH, 0, 0L));
if (pOFN->lpstrFile)
{
if (cch > pOFN->nMaxFile)
{
// Not enough room.
StoreFileSizeInOFN(pOFN, cch);
}
else // pOFN->nMaxFile >= cch
{
//
// Copy in the full path as the first element.
//
StringCchCopy(pOFN->lpstrFile, pOFN->nMaxFile, pOFI->szPath); // We know these both succeed.
StringCchCat(pOFN->lpstrFile, pOFN->nMaxFile, c_szSpace);
//
// Get the other files here.
//
cch = lstrlen(pOFN->lpstrFile); // cch is now the length of the path plus a space.
//
// The path is guaranteed to be less than 64K (actually, < 260).
//
pOFN->nFileOffset = LOWORD(cch);
lpchStart = pOFN->lpstrFile + cch;
// This shouldn't get truncated, assuming the text in the edit box is still the same length as above.
GetDlgItemText(hDlg, edt1, lpchStart, (pOFN->nMaxFile - cch - 1));
while (*lpchStart == CHAR_SPACE)
{
lpchStart = CharNext(lpchStart);
}
if (*lpchStart == CHAR_NULL)
{
return (FALSE);
}
//
// Go along file path looking for multiple filenames delimited by
// spaces. For each filename found, try to open it to make sure
// it's a valid file.
//
while (!EOS)
{
//
// Find the end of the filename.
//
lpchEnd = lpchStart;
while (*lpchEnd && *lpchEnd != CHAR_SPACE)
{
lpchEnd = CharNext(lpchEnd);
}
//
// Mark the end of the filename with a NULL.
//
if (*lpchEnd == CHAR_SPACE)
{
*lpchEnd = CHAR_NULL;
}
else
{
//
// Already NULL, found the end of the string.
//
EOS = TRUE;
}
//
// Check that the filename is valid.
//
bRet = GetFullPathName(lpchStart, ARRAYSIZE(szPathName), szPathName, NULL);
if (!bRet)
{
nErrCode = ERROR_FILE_NOT_FOUND;
goto MultiFileNotFound;
}
hFile = CreateFile(szPathName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
hFile = ProgManBugCreateFile(hFile, szPathName, &nErrCode);
if (hFile == INVALID_HANDLE_VALUE)
{
nErrCode = GetLastError();
MultiFileNotFound:
if ( ((pOFN->Flags & OFN_FILEMUSTEXIST) ||
(nErrCode != ERROR_FILE_NOT_FOUND)) &&
((pOFN->Flags & OFN_PATHMUSTEXIST) ||
(nErrCode != ERROR_PATH_NOT_FOUND)) &&
(!(pOFN->Flags & OFN_SHAREAWARE) ||
(nErrCode != ERROR_SHARING_VIOLATION)) )
{
if ( (nErrCode == ERROR_SHARING_VIOLATION) &&
pOFN->lpfnHook )
{
LPOFNHOOKPROC lpfnHook = GETHOOKFN(pOFN);
if (pOFI->ApiType == COMDLG_ANSI)
{
CHAR szPathNameA[MAX_FULLPATHNAME];
RtlUnicodeToMultiByteSize(
&cch,
szPathName,
lstrlenW(szPathName) * sizeof(TCHAR) );
SHUnicodeToAnsi(szPathName,(LPSTR)&szPathNameA[0],cch + 1);
cch = (DWORD)(*lpfnHook)( hDlg,
msgSHAREVIOLATIONA,
0,
(LONG_PTR)(LPSTR)szPathNameA );
}
else
{
cch = (DWORD)(*lpfnHook)( hDlg,
msgSHAREVIOLATIONW,
0,
(LONG_PTR)szPathName );
}
if (cch == OFN_SHARENOWARN)
{
return (FALSE);
}
else if (cch == OFN_SHAREFALLTHROUGH)
{
goto EscapedThroughShare;
}
}
else if (nErrCode == ERROR_ACCESS_DENIED)
{
szPathName[0] = CharLowerChar(szPathName[0]);
if (GetDiskType(szPathName) != DRIVE_REMOVABLE)
{
nErrCode = ERROR_NETWORK_ACCESS_DENIED;
}
}
if ((nErrCode == ERROR_WRITE_PROTECT) ||
(nErrCode == ERROR_CANNOT_MAKE) ||
(nErrCode == ERROR_ACCESS_DENIED))
{
*lpchStart = szPathName[0];
}
MultiWarning:
InvalidFileWarning(hDlg, lpchStart, nErrCode, 0);
return (FALSE);
}
}
EscapedThroughShare:
if (hFile != INVALID_HANDLE_VALUE)
{
if (!CloseHandle(hFile))
{
nErrCode = GetLastError();
goto MultiWarning;
}
if ((pOFN->Flags & OFN_NOREADONLYRETURN) &&
(GetFileAttributes(szPathName) & FILE_ATTRIBUTE_READONLY))
{
nErrCode = ERROR_LAZY_READONLY;
goto MultiWarning;
}
if ((bSave || (pOFN->Flags & OFN_NOREADONLYRETURN)) &&
(nErrCode == ERROR_ACCESS_DENIED))
{
goto MultiWarning;
}
if (pOFN->Flags & OFN_OVERWRITEPROMPT)
{
if (bSave && !FOkToWriteOver(hDlg, szPathName))
{
PostMessage( hDlg,
WM_NEXTDLGCTL,
(WPARAM)GetDlgItem(hDlg, edt1),
(LPARAM)1L );
return (FALSE);
}
}
}
//
// This file is valid, so check the next one.
//
if (!EOS)
{
lpchStart = lpchEnd + 1;
while (*lpchStart == CHAR_SPACE)
{
lpchStart = CharNext(lpchStart);
}
if (*lpchStart == CHAR_NULL)
{
EOS = TRUE;
}
else
{
//
// Not at end, replace NULL with SPACE.
//
*lpchEnd = CHAR_SPACE;
}
}
}
//
// Limit String.
//
*lpchEnd = CHAR_NULL;
}
}
//
// This doesn't really mean anything for multiselection.
//
pOFN->nFileExtension = 0;
pOFN->nFilterIndex = (int) SendDlgItemMessage(hDlg, cmb1, CB_GETCURSEL, 0, 0L);
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// dwOKSubclass
//
// Simulates a double click if the user presses OK with the mouse
// and the focus was on the directory listbox.
//
// The problem is that the UITF demands that when the directory
// listbox loses the focus, the selected directory should return
// to the current directory. But when the user changes the item
// selected with a single click, and then clicks the OK button to
// have the change take effect, the focus is lost before the OK button
// knows it was pressed. By setting the global flag bChangeDir
// when the directory listbox loses the focus and clearing it when
// the OK button loses the focus, we can check whether a mouse
// click should update the directory.
//
// Returns: Return value from default listbox procedure.
//
////////////////////////////////////////////////////////////////////////////
LRESULT WINAPI dwOKSubclass(
HWND hOK,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
HANDLE hDlg;
POPENFILEINFO pOFI;
if (msg == WM_KILLFOCUS)
{
if (bChangeDir)
{
if (pOFI = (POPENFILEINFO)GetProp(hDlg = GetParent(hOK), FILEPROP))
{
SendDlgItemMessage( hDlg,
lst2,
LB_SETCURSEL,
(WPARAM)(pOFI->idirSub - 1),
0L );
}
bChangeDir = FALSE;
}
}
return (CallWindowProc(lpOKProc, hOK, msg, wParam, lParam));
}
////////////////////////////////////////////////////////////////////////////
//
// dwLBSubclass
//
// Simulates a double click if the user presses OK with the mouse.
//
// The problem is that the UITF demands that when the directory
// listbox loses the focus, the selected directory should return
// to the current directory. But when the user changes the item
// selected with a single click, and then clicks the OK button to
// have the change take effect, the focus is lost before the OK button
// knows it was pressed. By simulating a double click, the change
// takes place.
//
// Returns: Return value from default listbox proceedure.
//
////////////////////////////////////////////////////////////////////////////
LRESULT WINAPI dwLBSubclass(
HWND hLB,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
HANDLE hDlg;
POPENFILEINFO pOFI;
if (msg == WM_KILLFOCUS)
{
hDlg = GetParent(hLB);
bChangeDir = (GetDlgItem(hDlg, IDOK) == (HWND)wParam) ? TRUE : FALSE;
if (!bChangeDir)
{
if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
{
SendMessage( hLB,
LB_SETCURSEL,
(WPARAM)(pOFI->idirSub - 1),
0L );
}
}
}
return (CallWindowProc(lpLBProc, hLB, msg, wParam, lParam));
}
////////////////////////////////////////////////////////////////////////////
//
// InvalidFileWarning
//
////////////////////////////////////////////////////////////////////////////
int InvalidFileWarning(
HWND hDlg,
LPTSTR szFile,
DWORD wErrCode,
UINT mbType)
{
SHORT isz;
BOOL bDriveLetter = FALSE;
int nRet = 0;
if (lstrlen(szFile) > TOOLONGLIMIT)
{
*(szFile + TOOLONGLIMIT) = CHAR_NULL;
}
switch (wErrCode)
{
case ( ERROR_NO_DISK_IN_DRIVE ) :
{
isz = iszNoDiskInDrive;
bDriveLetter = TRUE;
break;
}
case ( ERROR_NO_DISK_IN_CDROM ) :
{
isz = iszNoDiskInCDRom;
bDriveLetter = TRUE;
break;
}
case ( ERROR_NO_DRIVE ) :
{
isz = iszDriveDoesNotExist;
bDriveLetter = TRUE;
break;
}
case ( ERROR_TOO_MANY_OPEN_FILES ) :
{
isz = iszNoFileHandles;
break;
}
case ( ERROR_PATH_NOT_FOUND ) :
{
isz = iszPathNotFound;
break;
}
case ( ERROR_FILE_NOT_FOUND ) :
{
isz = iszFileNotFound;
break;
}
case ( ERROR_CANNOT_MAKE ) :
case ( ERROR_DISK_FULL ) :
{
isz = iszDiskFull;
bDriveLetter = TRUE;
break;
}
case ( ERROR_WRITE_PROTECT ) :
{
isz = iszWriteProtection;
bDriveLetter = TRUE;
break;
}
case ( ERROR_SHARING_VIOLATION ) :
{
isz = iszSharingViolation;
break;
}
case ( ERROR_CREATE_NO_MODIFY ) :
{
isz = iszCreateNoModify;
break;
}
case ( ERROR_NETWORK_ACCESS_DENIED ) :
{
isz = iszNetworkAccessDenied;
break;
}
case ( ERROR_PORTNAME ) :
{
isz = iszPortName;
break;
}
case ( ERROR_LAZY_READONLY ) :
{
isz = iszReadOnly;
break;
}
case ( ERROR_DIR_ACCESS_DENIED ) :
{
isz = iszDirAccessDenied;
break;
}
case ( ERROR_FILE_ACCESS_DENIED ) :
case ( ERROR_ACCESS_DENIED ) :
{
isz = iszFileAccessDenied;
break;
}
case ( ERROR_UNRECOGNIZED_VOLUME ) :
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL,
wErrCode,
GetUserDefaultLCID(),
szWarning,
WARNINGMSGLENGTH,
NULL);
goto DisplayError;
}
default :
{
isz = iszInvalidFileName;
break;
}
}
if (!CDLoadString( g_hinst,
isz,
szCaption,
WARNINGMSGLENGTH ))
{
StringCchPrintf( szWarning, ARRAYSIZE(szWarning),
TEXT("Error occurred, but error resource cannot be loaded.") );
}
else
{
StringCchPrintf( szWarning, ARRAYSIZE(szWarning),
szCaption,
bDriveLetter ? (LPTSTR)(CHAR)*szFile : szFile );
DisplayError:
GetWindowText(hDlg, szCaption, WARNINGMSGLENGTH);
if (!mbType)
{
mbType = MB_OK | MB_ICONEXCLAMATION;
}
nRet = MessageBox(hDlg, szWarning, szCaption, mbType);
}
if (isz == iszInvalidFileName)
{
PostMessage( hDlg,
WM_NEXTDLGCTL,
(WPARAM)GetDlgItem(hDlg, edt1),
(LPARAM)1L );
}
return (nRet);
}
////////////////////////////////////////////////////////////////////////////
//
// MeasureItem
//
////////////////////////////////////////////////////////////////////////////
VOID MeasureItem(
HWND hDlg,
LPMEASUREITEMSTRUCT mis)
{
if (!dyItem)
{
HDC hDC = GetDC(hDlg);
TEXTMETRIC TM;
HANDLE hFont;
hFont = (HANDLE)SendMessage(hDlg, WM_GETFONT, 0, 0L);
if (!hFont)
{
hFont = GetStockObject(SYSTEM_FONT);
}
hFont = SelectObject(hDC, hFont);
GetTextMetrics(hDC, &TM);
SelectObject(hDC, hFont);
ReleaseDC(hDlg, hDC);
dyText = TM.tmHeight;
dyItem = max(dyDirDrive, dyText);
}
if (mis->CtlID == lst1)
{
mis->itemHeight = dyText;
}
else
{
mis->itemHeight = dyItem;
}
}
////////////////////////////////////////////////////////////////////////////
//
// Signum
//
// Returns the sign of an integer:
// -1 if integer < 0
// 0 if integer = 0
// 1 if integer > 0
//
// Note: Signum *could* be defined as an inline macro, but that causes
// the C compiler to disable Loop optimization, Global register
// optimization, and Global optimizations for common subexpressions
// in any function that the macro would appear. The cost of a call
// to the function seemed worth the optimizations.
//
////////////////////////////////////////////////////////////////////////////
int Signum(
int nTest)
{
return ((nTest == 0) ? 0 : (nTest > 0) ? 1 : -1);
}
////////////////////////////////////////////////////////////////////////////
//
// DrawItem
//
// Draws the drive/directory pictures in the respective combo list boxes.
//
// lst1 is listbox for files
// lst2 is listbox for directories
// cmb1 is combobox for filters
// cmb2 is combobox for drives
//
////////////////////////////////////////////////////////////////////////////
VOID DrawItem(
POPENFILEINFO pOFI,
HWND hDlg,
WPARAM wParam,
LPDRAWITEMSTRUCT lpdis,
BOOL bSave)
{
HDC hdcList;
RECT rc;
TCHAR szText[MAX_FULLPATHNAME + 1];
int dxAcross;
LONG nHeight;
LONG rgbBack, rgbText, rgbOldBack, rgbOldText;
SHORT nShift = 1; // to shift directories right in lst2
BOOL bSel;
int BltItem;
int nBackMode;
if ((int)lpdis->itemID < 0)
{
DefWindowProc(hDlg, WM_DRAWITEM, wParam, (LPARAM)lpdis);
return;
}
*szText = CHAR_NULL;
if (lpdis->CtlID != lst1 && lpdis->CtlID != lst2 && lpdis->CtlID != cmb2)
{
return;
}
if (!pOFI)
{
return;
}
hdcList = lpdis->hDC;
if (lpdis->CtlID != cmb2)
{
SendDlgItemMessage( hDlg,
(int)lpdis->CtlID,
LB_GETTEXT ,
(WPARAM)lpdis->itemID,
(LONG_PTR)szText );
if (*szText == 0)
{
//
// If empty listing.
//
DefWindowProc(hDlg, WM_DRAWITEM, wParam, (LONG_PTR)lpdis);
return;
}
if (!bCasePreserved)
{
CharLower(szText);
}
}
nHeight = (lpdis->CtlID == lst1) ? dyText : dyItem;
CopyRect((LPRECT)&rc, (LPRECT)&lpdis->rcItem);
rc.bottom = rc.top + nHeight;
if (bSave && (lpdis->CtlID == lst1))
{
rgbBack = rgbWindowColor;
rgbText = rgbGrayText;
}
else
{
//
// Careful checking of bSel is needed here. Since the file
// listbox (lst1) can allow multiselect, only ODS_SELECTED needs
// to be set. But for the directory listbox (lst2), ODS_FOCUS
// also needs to be set.
//
bSel = (lpdis->itemState & (ODS_SELECTED | ODS_FOCUS));
if ((bSel & ODS_SELECTED) &&
((lpdis->CtlID != lst2) || (bSel & ODS_FOCUS)))
{
rgbBack = rgbHiliteColor;
rgbText = rgbHiliteText;
}
else
{
rgbBack = rgbWindowColor;
rgbText = rgbWindowText;
}
}
rgbOldBack = SetBkColor(hdcList, rgbBack);
rgbOldText = SetTextColor(hdcList, rgbText);
//
// Drives -- text is now in UI style, c: VolumeName/Server-Sharename.
//
if (lpdis->CtlID == cmb2)
{
HANDLE hCmb2 = GetDlgItem(hDlg, cmb2);
dxAcross = dxDirDrive / BMPHIOFFSET;
BltItem = (int) SendMessage(hCmb2, CB_GETITEMDATA, lpdis->itemID, 0);
SendMessage(hCmb2, CB_GETLBTEXT, lpdis->itemID, (LPARAM)szText);
if (bSel & ODS_SELECTED)
{
BltItem += BMPHIOFFSET;
}
}
else if (lpdis->CtlID == lst2)
{
//
// Directories.
//
dxAcross = dxDirDrive / BMPHIOFFSET;
if (lpdis->itemID > pOFI->idirSub)
{
nShift = (SHORT)pOFI->idirSub;
}
else
{
nShift = (SHORT)lpdis->itemID;
}
//
// Must be at least 1.
//
nShift++;
BltItem = 1 + Signum(lpdis->itemID + 1 - pOFI->idirSub);
if (bSel & ODS_FOCUS)
{
BltItem += BMPHIOFFSET;
}
}
else if (lpdis->CtlID == lst1)
{
//
// Prep for TextOut below.
//
dxAcross = -dxSpace;
}
if (bSave && (lpdis->CtlID == lst1) && !rgbText)
{
HBRUSH hBrush = CreateSolidBrush(rgbBack);
HBRUSH hOldBrush;
nBackMode = SetBkMode(hdcList, TRANSPARENT);
hOldBrush = SelectObject( lpdis->hDC,
hBrush
? hBrush
: GetStockObject(WHITE_BRUSH) );
FillRect(lpdis->hDC, (LPRECT)(&(lpdis->rcItem)), hBrush);
SelectObject(lpdis->hDC, hOldBrush);
if (hBrush)
{
DeleteObject(hBrush);
}
GrayString( lpdis->hDC,
GetStockObject(BLACK_BRUSH),
NULL,
(LPARAM)szText,
0,
lpdis->rcItem.left + dxSpace,
lpdis->rcItem.top,
0,
0 );
SetBkMode(hdcList, nBackMode);
}
else
{
//
// Draw the name.
//
ExtTextOut( hdcList,
rc.left + (WORD)(dxSpace + dxAcross) + dxSpace * nShift,
rc.top + (nHeight - dyText) / 2,
ETO_OPAQUE | ETO_CLIPPED,
(LPRECT)&rc,
szText,
lstrlen(szText),
NULL );
}
//
// Draw the picture.
//
if (lpdis->CtlID != lst1)
{
BitBlt( hdcList,
rc.left + dxSpace * nShift,
rc.top + (dyItem - dyDirDrive) / 2,
dxAcross,
dyDirDrive,
hdcMemory,
BltItem * dxAcross,
0,
SRCCOPY );
}
SetTextColor(hdcList, rgbOldText);
SetBkColor(hdcList, rgbBack);
if (lpdis->itemState & ODS_FOCUS)
{
DrawFocusRect(hdcList, (LPRECT)&lpdis->rcItem);
}
}
////////////////////////////////////////////////////////////////////////////
//
// SpacesExist
//
////////////////////////////////////////////////////////////////////////////
BOOL SpacesExist(
LPTSTR szFileName)
{
while (*szFileName)
{
if (*szFileName == CHAR_SPACE)
{
return (TRUE);
}
else
{
szFileName++;
}
}
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// StripFileName
//
// Removes all but the filename from editbox contents.
// This is to be called before the user makes directory or drive
// changes by selecting them instead of typing them.
//
////////////////////////////////////////////////////////////////////////////
void StripFileName(
HANDLE hDlg,
BOOL bWowApp)
{
TCHAR szText[MAX_FULLPATHNAME];
SHORT nFileOffset, cb;
if (GetDlgItemText(hDlg, edt1, szText, MAX_FULLPATHNAME - 1))
{
DWORD lRet;
lRet = ParseFile(szText, IsLFNDriveX(hDlg, szText), bWowApp, FALSE);
nFileOffset = (SHORT)LOWORD(lRet);
cb = HIWORD(lRet);
if (nFileOffset < 0)
{
//
// If there was a parsing error, check for CHAR_SEMICOLON
// delimeter.
//
if (szText[cb] == CHAR_SEMICOLON)
{
szText[cb] = CHAR_NULL;
lRet = ParseFile( szText,
IsLFNDriveX(hDlg, szText),
bWowApp,
FALSE );
nFileOffset = (SHORT)LOWORD(lRet);
szText[cb] = CHAR_SEMICOLON;
if (nFileOffset < 0)
{
//
// Still trouble, so Exit.
//
szText[0] = CHAR_NULL;
}
}
else
{
szText[0] = CHAR_NULL;
}
}
if (nFileOffset > 0)
{
StringCopyOverlap(szText, szText + nFileOffset);
}
if (nFileOffset)
{
SetDlgItemText(hDlg, edt1, szText);
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// lstrtok
//
////////////////////////////////////////////////////////////////////////////
LPTSTR lstrtok(
LPTSTR lpStr,
LPCTSTR lpDelim)
{
static LPTSTR lpString;
LPTSTR lpRetVal, lpTemp;
//
// If we are passed new string skip leading delimiters.
//
if (lpStr)
{
lpString = lpStr;
while (*lpString && StrChr(lpDelim, *lpString))
{
lpString = CharNext(lpString);
}
}
//
// If there are no more tokens, return NULL.
//
if (!*lpString)
{
return (CHAR_NULL);
}
//
// Save head of token.
//
lpRetVal = lpString;
//
// Find delimiter or end of string.
//
while (*lpString && !StrChr(lpDelim, *lpString))
{
lpString = CharNext(lpString);
}
//
// If we found a delimiter insert string terminator and skip.
//
if (*lpString)
{
lpTemp = CharNext(lpString);
*lpString = CHAR_NULL;
lpString = lpTemp;
}
//
// Return token.
//
return (lpRetVal);
}
////////////////////////////////////////////////////////////////////////////
//
// ChopText
//
////////////////////////////////////////////////////////////////////////////
LPTSTR ChopText(
HWND hwndDlg,
int idStatic,
LPTSTR lpch)
{
RECT rc;
register int cxField;
BOOL fChop = FALSE;
HWND hwndStatic;
HDC hdc;
TCHAR chDrv;
HANDLE hOldFont;
LPTSTR lpstrStart = lpch;
SIZE Size;
BOOL bRet;
//
// Get length of static field.
//
hwndStatic = GetDlgItem(hwndDlg, idStatic);
GetClientRect(hwndStatic, (LPRECT)&rc);
cxField = rc.right - rc.left;
//
// Chop characters off front end of text until short enough.
//
hdc = GetDC(hwndStatic);
hOldFont = NULL;
while ((bRet = GetTextExtentPoint(hdc, lpch, lstrlen(lpch), &Size)) &&
(cxField < Size.cx))
{
if (!fChop)
{
chDrv = *lpch;
//
// Proportional font support.
//
if (bRet = GetTextExtentPoint(hdc, lpch, 7, &Size))
{
cxField -= Size.cx;
}
else
{
break;
}
if (cxField <= 0)
{
break;
}
lpch += 7;
}
while (*lpch && (!ISBACKSLASH_P(lpstrStart, lpch)))
{
lpch++;
}
//Skip the backslash
lpch++;
fChop = TRUE;
}
ReleaseDC(hwndStatic, hdc);
//
// If any characters chopped off, replace first three characters in
// remaining text string with ellipsis.
//
if (fChop)
{
//Skip back to include the backslash
lpch--;
*--lpch = CHAR_DOT;
*--lpch = CHAR_DOT;
*--lpch = CHAR_DOT;
*--lpch = *(lpstrStart + 2);
*--lpch = *(lpstrStart + 1);
*--lpch = *lpstrStart;
}
return (lpch);
}
////////////////////////////////////////////////////////////////////////////
//
// FillOutPath
//
// Fills out lst2 given that the current directory has been set.
//
// Returns: TRUE if they DO NOT match
// FALSE if match
//
////////////////////////////////////////////////////////////////////////////
BOOL FillOutPath(
HWND hList,
POPENFILEINFO pOFI)
{
TCHAR szPath[CCHNETPATH];
LPTSTR lpCurDir;
LPTSTR lpB, lpF;
TCHAR wc;
int cchPathOffset;
LPCURDLG lpCurDlg;
if(!(lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) ||
!(lpCurDir = lpCurDlg->lpstrCurDir))
{
return (FALSE);
}
EVAL(SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), lpCurDir)));
lpF = szPath;
//
// Wow apps started from lfn dirs will set the current directory to an
// lfn, but only in the case where it is less than 8 chars.
//
if (pOFI->pOFN->Flags & OFN_NOLONGNAMES)
{
ShortenThePath(lpF);
}
*lpF = (TCHAR)CharLower((LPTSTR)*lpF);
cchPathOffset = GetPathOffset(lpF);
if (cchPathOffset == -1)
{
cchPathOffset = 0;
}
lpB = (lpF + cchPathOffset); // lpB now points to c:[here]\bar or \\foo\bar[here]\foo
//
// Hack to retain Winball display functionality.
// Drived disks are displayed as C:\ (the root dir).
// whereas unc disks are displayed as \\server\share (the disk).
// Hence, extend display of drived disks by one char.
//
if (*(lpF + 1) == CHAR_COLON)
{
++lpB;
wc = *(lpB); // For "c:\foo", wc = 'f', and lpF is now "c:\"
*lpB = CHAR_NULL;
}
else
{
//
// Since we use lpF over and over again to speed things
// up, and since GetCurrentDirectory returns the disk name
// for unc, but the root path for drives, we have the following hack
// for when we are at the root of the unc directory, and lpF
// contains old stuff out past cchPathOffset.
//
EVAL(PathAddBackslash(lpF)); // Make sure there is a backslash...
wc = 0;
*lpB = CHAR_NULL;
lpB++; // For "\\foo\bar\hoo" lpF is now "\\foo\bar" (no final backslash), lpB is "hoo"
}
//
// Insert the items for the path to the current dir
// Insert the root...
//
pOFI->idirSub = 0;
SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF); // Inserting the root ("c:\" or "\\foo\bar")
if (wc)
{
*lpB = wc; // Replace any missing character - so for "c:\foo", lpB is now "foo". For "\\foo\bar\hoo", lpB is now still 0 (followed by "hoo")
}
// For "\\foo\bar\hoo", lpB is now "hoo"
// For "c:\foo", lpB is now "foo"
for (lpF = lpB; *lpB; lpB++)
{
if ((ISBACKSLASH_P(szPath, lpB)) || (*lpB == CHAR_SLASH))
{
*lpB = CHAR_NULL;
SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF);
lpF = lpB + 1;
*lpB = CHAR_BSLASH;
}
}
//
// Assumes that a path always ends with one last un-delimited dir name.
// Check to make sure we have at least one.
//
if (lpF != lpB)
{
SendMessage(hList, LB_INSERTSTRING, pOFI->idirSub++, (LPARAM)lpF);
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// ShortenThePath
//
// Takes a pathname and converts all dirs to shortnames if they are
// not valid DOS 8.3 names.
//
// Returns: TRUE if pathname converted
// FALSE if ran out of space, buffer left alone
// Note: pPath is assumed to be at least MAX_PATH in length.
//
////////////////////////////////////////////////////////////////////////////
BOOL ShortenThePath(
LPTSTR pPath)
{
TCHAR szDest[MAX_PATH];
LPTSTR pSrcNextSpec, pReplaceSpec;
LPTSTR pDest, p;
LPTSTR pSrc;
int cchPathOffset;
HANDLE hFind;
WIN32_FIND_DATA FindData;
UINT i;
int nSpaceLeft = ARRAYSIZE(szDest) - 1;
UNICODE_STRING Name;
BOOLEAN fSpace = FALSE;
//
// Save pointer to beginning of buffer.
//
pSrc = pPath;
//
// Eliminate double quotes.
//
for (p = pDest = pSrc; *p; p++, pDest++)
{
if (*p == CHAR_QUOTE)
{
p++;
}
*pDest = *p;
}
*pDest = CHAR_NULL;
//
// Strip out leading spaces.
//
while (*pSrc == CHAR_SPACE)
{
pSrc++;
}
//
// Skip past \\foo\bar or <drive>:
//
pDest = szDest;
pSrcNextSpec = pSrc;
//
// Reuse shell32 internal api that calculates path offset.
// The cchPathOffset variable will be the offset that when added to
// the pointer will result in a pointer to the backslash before the
// first part of the path.
//
// NOTE: UNICODE only call.
//
cchPathOffset = GetPathOffset(pSrc);
//
// Check to see if it's valid. If pSrc is not of the \\foo\bar
// or <drive>: form we just do nothing.
//
if (cchPathOffset == -1)
{
return (TRUE);
}
//
// cchPathOffset will always be at least 1 and is the number of
// characters - 1 that we want to copy (that is, if 0 was
// permissible, it would denote 1 character).
//
do
{
*pDest++ = *pSrcNextSpec++;
if (!--nSpaceLeft)
{
return (FALSE);
}
} while (cchPathOffset--);
//
// At this point, we have just the filenames that we can shorten:
// \\foo\bar\it\is\here -> it\is\here
// c:\angry\lions -> angry\lions
//
while (pSrcNextSpec)
{
//
// pReplaceSpec holds the current spec we need to replace.
// By default, if we can't find the altname, then just use this.
//
pReplaceSpec = pSrcNextSpec;
//
// Search for trailing "\"
// pSrcNextSpec will point to the next spec to fix.
// (*pSrcNextSpec = NULL if done)
//
while (*pSrcNextSpec && (!ISBACKSLASH_P(pReplaceSpec, pSrcNextSpec)))
{
pSrcNextSpec++;
}
if (*pSrcNextSpec)
{
//
// If there is more, then pSrcNextSpec should point to it.
// Also delimit this spec.
//
*pSrcNextSpec = CHAR_NULL;
}
else
{
pSrcNextSpec = NULL;
}
hFind = FindFirstFile(pSrc, &FindData);
//
// We could exit as soon as this FindFirstFileFails,
// but there's the special case of having execute
// without read permission. This would fail since the lfn
// is valid for lfn apps.
//
if (hFind != INVALID_HANDLE_VALUE)
{
FindClose(hFind);
//
// See if it's not a legal 8.3 name or if there are spaces
// in the name. If either is true, use the alternate name.
//
if (NT_SUCCESS(RtlInitUnicodeStringEx(&Name, FindData.cFileName)))
{
if (!RtlIsNameLegalDOS8Dot3(&Name, NULL, &fSpace) || fSpace)
{
if (FindData.cAlternateFileName[0])
{
pReplaceSpec = FindData.cAlternateFileName;
}
}
}
}
i = lstrlen(pReplaceSpec);
nSpaceLeft -= i;
if (nSpaceLeft <= 0)
{
return (FALSE);
}
EVAL(SUCCEEDED(StringCchCopy(pDest, nSpaceLeft + 1, pReplaceSpec)));
pDest += i;
//
// Now replace the CHAR_NULL with a slash if necessary.
//
if (pSrcNextSpec)
{
*pSrcNextSpec++ = CHAR_BSLASH;
//
// Also add backslash to destination.
//
*pDest++ = CHAR_BSLASH;
nSpaceLeft--;
}
}
EVAL(SUCCEEDED(StringCchCopy(pPath, MAX_PATH, szDest)));
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// FListAll
//
// Given a file pattern, it changes the directory to that of the spec,
// and updates the display.
//
// notes: pszSpec must be a string less than MAX_FULLPATHNAME in size.
// cchSpec is the length of the buffer that contains pszSpec. It may
// have an extension appended to it.
//
////////////////////////////////////////////////////////////////////////////
int FListAll(
POPENFILEINFO pOFI,
HWND hDlg,
LPTSTR pszSpec,
int cchSpec)
{
LPTSTR pszPattern;
TCHAR chSave;
int nRet = 0;
TCHAR szDirBuf[MAX_FULLPATHNAME + 1];
BOOL bPattern = TRUE;
if (!bCasePreserved)
{
CharLower(pszSpec);
}
//
// No directory.
//
pszPattern = StrRChr(pszSpec, pszSpec + lstrlen(pszSpec), CHAR_BSLASH);
if (!pszPattern &&
!StrChr(pszSpec, CHAR_COLON))
{
EVAL(SUCCEEDED(StringCchCopy(pOFI->szSpecCur, ARRAYSIZE(pOFI->szSpecCur), pszSpec))); // Should always be enough room.
if (!bInitializing)
{
UpdateListBoxes(hDlg, pOFI, pszSpec, mskDirectory);
}
}
else
{
*szDirBuf = CHAR_NULL;
//
// Just root + pattern.
//
if (pszPattern == StrChr(pszSpec, CHAR_BSLASH))
{
if (!pszPattern)
{
//
// Didn't find a slash, must have drive.
//
pszPattern = CharNext(CharNext(pszSpec));
}
else if ((pszPattern == pszSpec) ||
((pszPattern - 2 == pszSpec) &&
(*(pszSpec + 1) == CHAR_COLON)))
{
pszPattern = CharNext(pszPattern);
}
else
{
goto KillSlash;
}
chSave = *pszPattern;
if (chSave != CHAR_DOT)
{
//
// If not c:.. or c:.
//
*pszPattern = CHAR_NULL;
}
EVAL(SUCCEEDED(StringCchCopy(szDirBuf, ARRAYSIZE(szDirBuf), pszSpec)));
if (chSave == CHAR_DOT)
{
int lenSpec = lstrlen(pszSpec);
pszPattern = pszSpec + lenSpec;
// Could potentially get truncated, bPattern reflects this:
bPattern = AppendExt(pszPattern, cchSpec - lenSpec, pOFI->pOFN->lpstrDefExt, TRUE);
}
else
{
*pszPattern = chSave;
}
}
else
{
KillSlash:
*pszPattern++ = 0;
EVAL(SUCCEEDED(StringCchCopy(szDirBuf, ARRAYSIZE(szDirBuf), pszSpec)));
}
if ((nRet = ChangeDir(hDlg, szDirBuf, TRUE, FALSE)) < 0)
{
return (nRet);
}
if (bPattern)
{
EVAL(SUCCEEDED(StringCchCopy(pOFI->szSpecCur, ARRAYSIZE(pOFI->szSpecCur), pszPattern)));
SetDlgItemText(hDlg, edt1, pOFI->szSpecCur);
SelDisk(hDlg, NULL);
if (!bInitializing)
{
SendMessage( hDlg,
WM_COMMAND,
GET_WM_COMMAND_MPS( cmb2,
GetDlgItem(hDlg, cmb2),
MYCBN_DRAW ) );
}
}
}
return (nRet);
}
////////////////////////////////////////////////////////////////////////////
//
// ChangeDir
//
// Changes the current directory and/or resource.
//
// lpszDir - Fully qualified, or partially qualified names.
// To change to another disk and cd automatically to the
// last directory as set in the shell's environment, specify
// only a disk name (i.e. c: or \\triskal\scratch - must not end
// in backslash).
// bForce - If True, then caller requires that ChangeDir successfully cd
// somewhere. Order of cding is as follows:
// 1. lpszDir
// 2. current dir for the current thread
// 3. root of current dir for the current thread
// 4. c:
// bError - if TRUE, then pop up an AccessDenied dialog at every step
// in the force.
//
// Returns an index into gaDiskInfo for new disk chosen or,
// the ADDDISK_error code.
// Returns ADDDISK_NOCHANGE in the event that it cannot cd to the root
// directory of the specific file.
//
////////////////////////////////////////////////////////////////////////////
int ChangeDir(
HWND hDlg,
LPCTSTR lpszDir,
BOOL bForce,
BOOL bError)
{
TCHAR szCurDir[CCHNETPATH];
LPTSTR lpCurDir;
int cchDirLen;
TCHAR wcDrive = 0;
int nIndex;
BOOL nRet;
LPCURDLG lpCurDlg;
//
// SheChangeDirEx will call GetCurrentDir, but will use what it
// gets only in the case where the path passed in was no good.
//
//
// 1st, try request.
//
if (lpszDir && *lpszDir)
{
if (SUCCEEDED(StringCchCopy(szCurDir, ARRAYSIZE(szCurDir), lpszDir)))
{
//
// Remove trailing spaces.
//
lpCurDir = szCurDir + lstrlen(szCurDir) - 1;
while (*lpCurDir && (*lpCurDir == CHAR_SPACE))
{
*lpCurDir-- = CHAR_NULL;
}
nRet = SheChangeDirEx(szCurDir);
if (nRet == ERROR_ACCESS_DENIED)
{
if (bError)
{
//
// Casting to LPTSTR is ok below - InvalidFileWarning will
// not change this string because the path is always
// guaranteed to be <= MAX_FULLPATHNAME.
//
InvalidFileWarning( hDlg,
(LPTSTR)lpszDir,
ERROR_DIR_ACCESS_DENIED,
0 );
}
if (!bForce)
{
return (CHANGEDIR_FAILED);
}
}
else
{
goto ChangeDir_OK;
}
}
}
//
// 2nd, try lpCurDlg->lpstrCurDir value (which we got above).
//
// !!! need to check for a null return value ???
//
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
nRet = SheChangeDirEx(lpCurDir);
if (nRet == ERROR_ACCESS_DENIED)
{
if (bError)
{
InvalidFileWarning( hDlg,
lpCurDir,
ERROR_DIR_ACCESS_DENIED,
0 );
}
}
else
{
goto ChangeDir_OK;
}
//
// 3rd, try root of lpCurDlg->lpstrCurDir or GetCurrentDir (sanity).
//
EVAL(SUCCEEDED(StringCchCopy(szCurDir, ARRAYSIZE(szCurDir), lpCurDir)));
cchDirLen = GetPathOffset(szCurDir);
//
// Sanity check - it's guaranteed not to fail ...
//
if (cchDirLen != -1)
{
szCurDir[cchDirLen] = CHAR_BSLASH;
szCurDir[cchDirLen + 1] = CHAR_NULL;
nRet = SheChangeDirEx(szCurDir);
if (nRet == ERROR_ACCESS_DENIED)
{
if (bError)
{
InvalidFileWarning( hDlg,
(LPTSTR)lpszDir,
ERROR_DIR_ACCESS_DENIED,
0 );
}
}
else
{
goto ChangeDir_OK;
}
}
//
// 4th, try c:
//
StringCchCopy(szCurDir, ARRAYSIZE(szCurDir), L"c:");
nRet = SheChangeDirEx(szCurDir);
if (nRet == ERROR_ACCESS_DENIED)
{
if (bError)
{
InvalidFileWarning( hDlg,
(LPTSTR)lpszDir,
ERROR_DIR_ACCESS_DENIED,
0 );
}
}
else
{
goto ChangeDir_OK;
}
return (CHANGEDIR_FAILED);
ChangeDir_OK:
GetCurrentDirectory(ARRAYSIZE(szCurDir), szCurDir);
nIndex = DiskAddedPreviously(0, szCurDir);
//
// If the disk doesn't exist, add it.
//
if (nIndex == -1)
{
HWND hCmb2 = GetDlgItem(hDlg, cmb2);
LPTSTR lpszDisk = NULL;
DWORD dwType;
TCHAR wc1, wc2;
if (szCurDir[1] == CHAR_COLON)
{
wcDrive = szCurDir[0];
}
else
{
lpszDisk = &szCurDir[0];
}
cchDirLen = GetPathOffset(szCurDir);
if (cchDirLen != -1)
{
wc1 = szCurDir[cchDirLen];
wc2 = szCurDir[cchDirLen + 1];
szCurDir[cchDirLen] = CHAR_BSLASH;
szCurDir[cchDirLen + 1] = CHAR_NULL;
}
dwType = GetDiskIndex(GetDiskType(szCurDir));
if (cchDirLen != -1)
{
szCurDir[cchDirLen] = CHAR_NULL;
}
nIndex = AddDisk(wcDrive, lpszDisk, NULL, dwType);
SendMessage(hCmb2, WM_SETREDRAW, FALSE, 0L);
wNoRedraw |= 1;
SendMessage( hCmb2,
CB_SETITEMDATA,
(WPARAM)SendMessage(
hCmb2,
CB_ADDSTRING,
(WPARAM)0,
(LPARAM)(LPTSTR)gaDiskInfo[nIndex].lpAbbrName ),
(LPARAM)gaDiskInfo[nIndex].dwType );
if ((dwType != NETDRVBMP) && (dwType != REMDRVBMP))
{
gaDiskInfo[nIndex].bCasePreserved =
IsFileSystemCasePreserving(gaDiskInfo[nIndex].lpPath);
}
wNoRedraw &= ~1;
SendMessage(hCmb2, WM_SETREDRAW, TRUE, 0L);
if (cchDirLen != -1)
{
szCurDir[cchDirLen] = wc1;
szCurDir[cchDirLen + 1] = wc2;
}
}
else
{
//
// Validate the disk if it has been seen before.
//
// For unc names that fade away, refresh the cmb2 box.
//
if (!gaDiskInfo[nIndex].bValid)
{
gaDiskInfo[nIndex].bValid = TRUE;
SendDlgItemMessage(
hDlg,
cmb2,
CB_SETITEMDATA,
(WPARAM)SendDlgItemMessage(
hDlg,
cmb2,
CB_ADDSTRING,
(WPARAM)0,
(LPARAM)(LPTSTR)gaDiskInfo[nIndex].lpAbbrName ),
(LPARAM)gaDiskInfo[nIndex].dwType );
}
}
//
// Update our global concept of Case.
//
if (nIndex >= 0)
{
//
// Send special WOW message to indicate the directory has
// changed.
//
SendMessage(hDlg, msgWOWDIRCHANGE, 0, 0);
//
// Get pointer to current directory.
//
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
if (!lpCurDlg || !lpCurDir)
{
return (CHANGEDIR_FAILED);
}
bCasePreserved = gaDiskInfo[nIndex].bCasePreserved;
//
// In case the unc name already has a drive letter, correct
// lst2 display.
//
cchDirLen = 0;
//
// Compare with szCurDir since it's been lowercased.
//
if (DBL_BSLASH(szCurDir) &&
(*gaDiskInfo[nIndex].lpAbbrName != szCurDir[0]))
{
if ((cchDirLen = GetPathOffset(szCurDir)) != -1)
{
szCurDir[--cchDirLen] = CHAR_COLON;
szCurDir[--cchDirLen] = *gaDiskInfo[nIndex].lpAbbrName;
}
}
if ((gaDiskInfo[nIndex].dwType == CDDRVBMP) ||
(gaDiskInfo[nIndex].dwType == FLOPPYBMP))
{
if (*lpCurDir != gaDiskInfo[nIndex].wcDrive)
{
TCHAR szDrive[5];
//
// Get new volume info - should always succeed.
//
szDrive[0] = gaDiskInfo[nIndex].wcDrive;
szDrive[1] = CHAR_COLON;
szDrive[2] = CHAR_BSLASH;
szDrive[3] = CHAR_NULL;
UpdateLocalDrive(szDrive, TRUE);
//
// Flush to the cmb before selecting the disk.
//
if ( lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg) )
{
gahDlg[lpCurDlg->dwCurDlgNum] = hDlg;
FlushDiskInfoToCmb2();
}
}
}
// lpCurDir came from lpstrCurDir, which is of length CCHNETPATH
EVAL(SUCCEEDED(StringCchCopy(lpCurDir, CCHNETPATH, (LPTSTR)&szCurDir[cchDirLen]))); // szCurDir not longer than CCH_NETPATH
PathAddBackslash(lpCurDir);
//
// If the worker thread is running, then trying to select here
// will just render the cmb2 blank, which is what we want;
// otherwise, it should successfully select it.
//
SelDisk(hDlg, gaDiskInfo[nIndex].lpPath);
}
return (nIndex);
}
////////////////////////////////////////////////////////////////////////////
//
// IsFileSystemCasePreserving
//
////////////////////////////////////////////////////////////////////////////
BOOL IsFileSystemCasePreserving(
LPTSTR lpszDisk)
{
TCHAR szPath[MAX_FULLPATHNAME];
DWORD dwFlags;
if (!lpszDisk)
{
return (FALSE);
}
if (SUCCEEDED(StringCchCopy(szPath, ARRAYSIZE(szPath), lpszDisk)))
{
if (SUCCEEDED(StringCchCat(szPath, ARRAYSIZE(szPath), L"\\")))
{
if (GetVolumeInformation( szPath,
NULL,
0,
NULL,
NULL,
&dwFlags,
NULL,
0 ))
{
return ((dwFlags & FS_CASE_IS_PRESERVED));
}
}
}
//
// Default to FALSE if there is an error.
//
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// IsLFNDriveX
//
////////////////////////////////////////////////////////////////////////////
BOOL IsLFNDriveX(
HWND hDlg,
LPTSTR pszPath)
{
TCHAR szRootPath[MAX_FULLPATHNAME];
DWORD dwVolumeSerialNumber;
DWORD dwMaximumComponentLength;
DWORD dwFileSystemFlags;
LPTSTR lpCurDir;
LPCURDLG lpCurDlg;
if (!pszPath[0] || !pszPath[1] ||
(pszPath[1] != CHAR_COLON && !(DBL_BSLASH(pszPath))))
{
//
// If the path is not a full path then get the directory path
// from the TLS current directory.
//
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
lpCurDir = (lpCurDlg ? lpCurDlg->lpstrCurDir : NULL);
EVAL(SUCCEEDED(StringCchCopy(szRootPath, ARRAYSIZE(szRootPath), lpCurDir)));
}
else
{
EVAL(SUCCEEDED(StringCchCopy(szRootPath, ARRAYSIZE(szRootPath), pszPath)));
}
if (szRootPath[1] == CHAR_COLON)
{
szRootPath[2] = CHAR_BSLASH;
szRootPath[3] = 0;
}
else if (DBL_BSLASH(szRootPath))
{
int i;
LPTSTR p;
//
// Stop at "\\foo\bar".
//
for (i = 0, p = szRootPath + 2; *p && i < 2; p++)
{
if (ISBACKSLASH_P(szRootPath, p))
{
i++;
}
}
switch (i)
{
case ( 0 ) :
{
return (FALSE);
}
case ( 1 ) :
{
if (lstrlen(szRootPath) < MAX_FULLPATHNAME - 2)
{
*p = CHAR_BSLASH;
*(p + 1) = CHAR_NULL;
}
else
{
return (FALSE);
}
break;
}
case ( 2 ) :
{
*p = CHAR_NULL;
break;
}
}
}
if (GetVolumeInformation( szRootPath,
NULL,
0,
&dwVolumeSerialNumber,
&dwMaximumComponentLength,
&dwFileSystemFlags,
NULL,
0 ))
{
if (dwMaximumComponentLength == (MAXDOSFILENAMELEN - 1))
{
return (FALSE);
}
else
{
return (TRUE);
}
}
return (FALSE);
}
////////////////////////////////////////////////////////////////////////////
//
// DiskAddedPreviously
//
// This routine checks to see if a disk resource has been previously
// added to the global structure.
//
// wcDrive - if this is set, then there is no lpszName comparison
// lpszName - if wcDrive is not set, but the lpszName is of the form
// "c:\" then set wcDrive = *lpszName and index by drive letter
// else assume lpszName is a unc name
//
// Returns: 0xFFFFFFFF failure (disk doesn't exist in list)
// 0 - 128 number of disk in list
//
////////////////////////////////////////////////////////////////////////////
int DiskAddedPreviously(
TCHAR wcDrive,
LPTSTR lpszName)
{
WORD i;
//
// There are two index schemes (by drive or by unc \\server\share).
// If it doesn't have a drive letter, assume unc.
//
if (wcDrive || (lpszName && (*(lpszName + 1) == CHAR_COLON)))
{
if (!wcDrive)
{
wcDrive = *lpszName;
wcDrive = (TCHAR)CharLower((LPTSTR)wcDrive);
}
for (i = 0; i < dwNumDisks; i++)
{
//
// See if the drive letters are the same.
//
if (wcDrive)
{
if (wcDrive == (TCHAR)CharLower((LPTSTR)gaDiskInfo[i].wcDrive))
{
return (i);
}
}
}
}
else if (lpszName)
{
DWORD cchDirLen;
TCHAR wc;
//
// Check remote name (\\server\share).
//
cchDirLen = GetPathOffset(lpszName);
//
// If we're given a unc path, get the disk name.
// Otherwise, assume the whole thing is a disk name.
//
if (cchDirLen != -1)
{
wc = *(lpszName + cchDirLen);
*(lpszName + cchDirLen) = CHAR_NULL;
}
for (i = 0; i < dwNumDisks; i++)
{
if (!lstrcmpi(gaDiskInfo[i].lpName, lpszName))
{
if (cchDirLen != -1)
{
*(lpszName + cchDirLen) = wc;
}
return (i);
}
}
if (cchDirLen != -1)
{
*(lpszName + cchDirLen) = wc;
}
}
return (0xFFFFFFFF);
}
////////////////////////////////////////////////////////////////////////////
//
// AddDisk
//
// Adds a disk to one of the global structures:
// gaNetDiskInfo
// gaLocalDiskInfo
//
// wcDrive - the drive to attach to (this parm should be 0 for unc)
// lpName - \\server\share name for remote disks
// volume name for local disks
// lpProvider - used for remote disks only, the name of the provider
// used with WNetFormatNetworkName api
// dwType - type of the bitmap to display
// except when we are adding a drive letter temporarily
// at startup this parameter can equal TMPNETDRV in which
// case we set the bitmap to NETDRVBMP
//
// Returns: -2 Cannot Add Disk
// -1 DiskInfo did not change
// 0 dwNumDisks - DiskInfo changed
//
////////////////////////////////////////////////////////////////////////////
int AddDisk(
TCHAR wcDrive,
LPTSTR lpName,
LPTSTR lpProvider,
DWORD dwType)
{
int nIndex, nRet;
DWORD cchMultiLen = 0;
DWORD cchAbbrLen = 0;
DWORD cchLen;
DWORD dwRet = 0;
LPTSTR lpBuff;
OFN_DISKINFO *pofndiDisk = NULL, *pgDI;
//
// Sanity check - wcDrive and/or lpName must be set.
//
if (!wcDrive && (!lpName || !*lpName))
{
return (ADDDISK_INVALIDPARMS);
}
nIndex = DiskAddedPreviously(wcDrive, lpName);
if (nIndex != 0xFFFFFFFF)
{
//
// Do not add a temporary drive letter if we already
// have something better (added, for example, in a previous call).
//
if (dwType == TMPNETDRV)
{
gaDiskInfo[nIndex].bValid = TRUE;
return (ADDDISK_NOCHANGE);
}
// Using a floating profile, there can be collisions between
// local and network drives in which case we take the former.
//
// Note: If the drive is remembered, we assume that getdrivetype
// will return false and that the drive is not added.
// But if it was added, then we overwrite anyway,
// since it's the desired behavior.
//
if ((dwType == REMDRVBMP) &&
(dwType != gaDiskInfo[nIndex].dwType))
{
return (ADDDISK_NOCHANGE);
}
//
// Update previous connections.
//
if (!lstrcmpi(lpName, gaDiskInfo[nIndex].lpName))
{
//
// Don't update a connection as remembered, unless it's been
// invalidated.
//
if (dwType != REMDRVBMP)
{
gaDiskInfo[nIndex].dwType = dwType;
}
gaDiskInfo[nIndex].bValid = TRUE;
return (ADDDISK_NOCHANGE);
}
else if (!*lpName && ((dwType == CDDRVBMP) || (dwType == FLOPPYBMP)))
{
//
// Guard against lazy calls to updatelocaldrive erasing current
// changed dir volume name (set via changedir).
//
return (ADDDISK_NOCHANGE);
}
}
if (dwNumDisks >= MAX_DISKS)
{
return (ADDDISK_MAXNUMDISKS);
}
//
// If there is a drive, then lpPath needs only 4.
// If it's unc, then lpPath just equals lpName.
//
if (wcDrive)
{
cchLen = 4;
}
else
{
cchLen = 0;
}
if (lpName && *lpName)
{
//
// Get the length of the standard (Remote/Local) name.
//
cchLen += (lstrlen(lpName) + 1);
if (lpProvider && *lpProvider &&
((dwType == NETDRVBMP) || (dwType == REMDRVBMP)))
{
//
// Get the length for the multiline name.
//
dwRet = WNetFormatNetworkName( lpProvider,
lpName,
NULL,
&cchMultiLen,
WNFMT_MULTILINE,
dwAveCharPerLine );
if (dwRet != ERROR_MORE_DATA)
{
return (ADDDISK_NETFORMATFAILED);
}
//
// Add 4 for <drive-letter>:\ and NULL (safeguard)
//
if (wcDrive)
{
cchMultiLen += 4;
}
dwRet = WNetFormatNetworkName( lpProvider,
lpName,
NULL,
&cchAbbrLen,
WNFMT_ABBREVIATED,
dwAveCharPerLine );
if (dwRet != ERROR_MORE_DATA)
{
return (ADDDISK_NETFORMATFAILED);
}
//
// Add 4 for <drive-letter>:\ and NULL (safeguard).
//
if (wcDrive)
{
cchAbbrLen += 4;
}
}
else
{
//
// Make enough room so that lpMulti and lpAbbr can point to
// 4 characters (drive letter + : + space + null) ahead of
// lpremote.
//
if (wcDrive)
{
cchLen += 4;
}
}
}
else
{
//
// Make enough room so that lpMulti and lpAbbr can point to
// 4 characters (drive letter + : + space + null) ahead of
// lpremote.
//
if (wcDrive)
{
cchLen += 4;
}
}
//
// Allocate a temp OFN_DISKINFO object to work with.
// When we are finished, we'll request the critical section
// and update the global array.
//
pofndiDisk = (OFN_DISKINFO *)LocalAlloc(LPTR, sizeof(OFN_DISKINFO));
if (!pofndiDisk)
{
//
// Can't alloc or realloc memory, return error.
//
nRet = ADDDISK_ALLOCFAILED;
goto AddDisk_Error;
}
lpBuff = (LPTSTR)LocalAlloc( LPTR,
(cchLen + cchMultiLen + cchAbbrLen) * sizeof(TCHAR));
if (!lpBuff)
{
//
// Can't alloc or realloc memory, return error.
//
nRet = ADDDISK_ALLOCFAILED;
goto AddDisk_Error;
}
if (dwType == TMPNETDRV)
{
pofndiDisk->dwType = NETDRVBMP;
}
else
{
pofndiDisk->dwType = dwType;
}
//
// Always set these slots, even though wcDrive can equal 0.
//
pofndiDisk->wcDrive = wcDrive;
pofndiDisk->bValid = TRUE;
pofndiDisk->cchLen = cchLen + cchAbbrLen + cchMultiLen;
//
// NOTE: lpAbbrName must always point to the head of lpBuff
// so that we can free the block later at DLL_PROCESS_DETACH
//
if (lpName && *lpName && lpProvider && *lpProvider &&
((dwType == NETDRVBMP) || (dwType == REMDRVBMP)))
{
//
// Create an entry for a network disk.
//
pofndiDisk->lpAbbrName = lpBuff;
if (wcDrive)
{
*lpBuff++ = wcDrive;
*lpBuff++ = CHAR_COLON;
*lpBuff++ = CHAR_SPACE;
cchAbbrLen -= 3;
}
dwRet = WNetFormatNetworkName( lpProvider,
lpName,
lpBuff,
&cchAbbrLen,
WNFMT_ABBREVIATED,
dwAveCharPerLine );
if (dwRet != WN_SUCCESS)
{
nRet = ADDDISK_NETFORMATFAILED;
LocalFree(lpBuff);
goto AddDisk_Error;
}
lpBuff += cchAbbrLen;
pofndiDisk->lpMultiName = lpBuff;
if (wcDrive)
{
*lpBuff++ = wcDrive;
*lpBuff++ = CHAR_COLON;
*lpBuff++ = CHAR_SPACE;
cchMultiLen -= 3;
}
dwRet = WNetFormatNetworkName(lpProvider, lpName,
lpBuff, &cchMultiLen, WNFMT_MULTILINE, dwAveCharPerLine);
if (dwRet != WN_SUCCESS)
{
nRet = ADDDISK_NETFORMATFAILED;
LocalFree(lpBuff);
goto AddDisk_Error;
}
//
// Note: this assumes that the lpRemoteName
// returned by WNetEnumResources is always in
// the form \\server\share (without a trailing bslash).
//
pofndiDisk->lpPath = lpBuff;
//
// if it's not unc.
//
if (wcDrive)
{
*lpBuff++ = wcDrive;
*lpBuff++ = CHAR_COLON;
*lpBuff++ = CHAR_NULL;
cchLen -= 3;
}
EVAL(SUCCEEDED(StringCchCopy(lpBuff, cchLen, lpName))); // Should always be enough room.
pofndiDisk->lpName = lpBuff;
pofndiDisk->bCasePreserved =
IsFileSystemCasePreserving(pofndiDisk->lpPath);
}
else
{
//
// Create entry for a local name, or a network one with
// no name yet.
//
pofndiDisk->lpAbbrName = pofndiDisk->lpMultiName = lpBuff;
if (wcDrive)
{
*lpBuff++ = wcDrive;
*lpBuff++ = CHAR_COLON;
*lpBuff++ = CHAR_SPACE;
cchLen -= 3;
}
if (lpName)
{
EVAL(SUCCEEDED(StringCchCopy(lpBuff, cchLen, lpName))); // Should always be enough room.
}
else
{
*lpBuff = CHAR_NULL;
}
pofndiDisk->lpName = lpBuff;
if (wcDrive)
{
lpBuff += lstrlen(lpBuff) + 1;
*lpBuff = wcDrive;
*(lpBuff + 1) = CHAR_COLON;
*(lpBuff + 2) = CHAR_NULL;
}
pofndiDisk->lpPath = lpBuff;
if ((dwType == NETDRVBMP) || (dwType == REMDRVBMP))
{
pofndiDisk->bCasePreserved =
IsFileSystemCasePreserving(pofndiDisk->lpPath);
}
else
{
pofndiDisk->bCasePreserved = FALSE;
}
}
//
// Now we need to update the global array.
//
if (nIndex == 0xFFFFFFFF)
{
nIndex = dwNumDisks;
}
pgDI = &gaDiskInfo[nIndex];
//
// Enter critical section and update data.
//
EnterCriticalSection(&g_csLocal);
pgDI->cchLen = pofndiDisk->cchLen;
pgDI->lpAbbrName = pofndiDisk->lpAbbrName;
pgDI->lpMultiName = pofndiDisk->lpMultiName;
pgDI->lpName = pofndiDisk->lpName;
pgDI->lpPath = pofndiDisk->lpPath;
pgDI->wcDrive = pofndiDisk->wcDrive;
pgDI->bCasePreserved = pofndiDisk->bCasePreserved;
pgDI->dwType = pofndiDisk->dwType;
pgDI->bValid = pofndiDisk->bValid;
LeaveCriticalSection(&g_csLocal);
if ((DWORD)nIndex == dwNumDisks)
{
dwNumDisks++;
}
nRet = nIndex;
AddDisk_Error:
if (pofndiDisk)
{
LocalFree(pofndiDisk);
}
return (nRet);
}
////////////////////////////////////////////////////////////////////////////
//
// EnableDiskInfo
//
////////////////////////////////////////////////////////////////////////////
VOID EnableDiskInfo(
BOOL bValid,
BOOL bDoUnc)
{
DWORD dwCnt = dwNumDisks;
EnterCriticalSection(&g_csLocal);
while (dwCnt--)
{
if (gaDiskInfo[dwCnt].dwType == NETDRVBMP)
{
if (!(DBL_BSLASH(gaDiskInfo[dwCnt].lpAbbrName)) || bDoUnc)
{
gaDiskInfo[dwCnt].bValid = bValid;
}
//
// Always re-invalidate remembered just in case someone
// escapes from fileopen, removes a connection
// overriding a remembered and comes back expecting to see
// the original remembered.
//
}
}
LeaveCriticalSection(&g_csLocal);
}
////////////////////////////////////////////////////////////////////////////
//
// FlushDiskInfoToCmb2
//
////////////////////////////////////////////////////////////////////////////
VOID FlushDiskInfoToCmb2()
{
DWORD dwDisk;
DWORD dwDlg;
for (dwDlg = 0; dwDlg < dwNumDlgs; dwDlg++)
{
if (gahDlg[dwDlg])
{
HWND hCmb2;
if (hCmb2 = GetDlgItem(gahDlg[dwDlg], cmb2))
{
wNoRedraw |= 1;
SendMessage(hCmb2, WM_SETREDRAW, FALSE, 0L);
SendMessage(hCmb2, CB_RESETCONTENT, 0, 0);
dwDisk = dwNumDisks;
while (dwDisk--)
{
if (gaDiskInfo[dwDisk].bValid)
{
SendMessage(
hCmb2,
CB_SETITEMDATA,
(WPARAM)SendMessage(
hCmb2,
CB_ADDSTRING,
(WPARAM)0,
(LPARAM)(LPTSTR)gaDiskInfo[dwDisk].lpAbbrName ),
(LPARAM)gaDiskInfo[dwDisk].dwType );
}
}
wNoRedraw &= ~1;
SendMessage(hCmb2, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hCmb2, NULL, FALSE);
SendMessage( gahDlg[dwDlg],
WM_COMMAND,
GET_WM_COMMAND_MPS(cmb2, hCmb2, MYCBN_REPAINT) );
}
gahDlg[dwDlg] = NULL;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// CallNetDlg
//
// Calls the appropriate network dialog in winnet driver.
//
// hwndParent - parent window of network dialog
//
// Returns: TRUE there are new drives to display
// FALSE there are no new drives to display
//
////////////////////////////////////////////////////////////////////////////
BOOL CallNetDlg(
HWND hWnd)
{
DWORD wRet;
HourGlass(TRUE);
wRet = WNetConnectionDialog(hWnd, WNTYPE_DRIVE);
if ((wRet != WN_SUCCESS) && (wRet != WN_CANCEL) && (wRet != 0xFFFFFFFF))
{
if (CDLoadString( g_hinst,
iszNoNetButtonResponse,
szCaption,
ARRAYSIZE(szCaption)))
{
EVAL(SUCCEEDED(StringCchPrintf(szWarning, ARRAYSIZE(szWarning), szCaption)));
GetWindowText(hWnd, szCaption, WARNINGMSGLENGTH);
MessageBox( hWnd,
szWarning,
szCaption,
MB_OK | MB_ICONEXCLAMATION );
}
}
HourGlass(FALSE);
return (wRet == WN_SUCCESS);
}
////////////////////////////////////////////////////////////////////////////
//
// GetDiskType
//
////////////////////////////////////////////////////////////////////////////
UINT GetDiskType(
LPTSTR lpszDisk)
{
//
// Unfortunately GetDriveType is not for deviceless connections.
// So assume all unc stuff is just "remote" - no way of telling
// if it's a cdrom or not.
//
if (DBL_BSLASH(lpszDisk))
{
return (DRIVE_REMOTE);
}
else
{
return (GetDriveType(lpszDisk));
}
}
////////////////////////////////////////////////////////////////////////////
//
// GetUNCDirectoryFromLB
//
// If lb contains a UNC listing, the function returns the full UNC path.
//
// Returns: 0 if no UNC listing in lb
// length of UNC listing string
// Note: this also fills in pOFI->szPath with the *full* UNC path (if UNC),
// or the name of the drive (if not UNC)
//
////////////////////////////////////////////////////////////////////////////
DWORD GetUNCDirectoryFromLB(
HWND hDlg,
WORD nLB,
POPENFILEINFO pOFI)
{
DWORD cch;
DWORD idir;
DWORD idirCurrent;
cch = (DWORD)SendDlgItemMessage( hDlg,
nLB,
LB_GETTEXT,
0,
(LPARAM)(LPTSTR)pOFI->szPath );
//
// If not UNC listing, return 0.
//
if (pOFI->szPath[0] != CHAR_BSLASH)
{
return (0);
}
idirCurrent = (WORD)(DWORD)SendDlgItemMessage( hDlg,
nLB,
LB_GETCURSEL,
0,
0L );
if (idirCurrent < (pOFI->idirSub - 1))
{
pOFI->idirSub = idirCurrent;
}
pOFI->szPath[cch++] = CHAR_BSLASH;
for (idir = 1; idir < pOFI->idirSub; ++idir)
{
cch += (DWORD)SendDlgItemMessage( hDlg,
nLB,
LB_GETTEXT,
(WPARAM)idir,
(LPARAM)(LPTSTR)&pOFI->szPath[cch] );
pOFI->szPath[cch++] = CHAR_BSLASH;
}
//
// Only add the subdirectory if it's not the \\server\share point.
//
if (idirCurrent && (idirCurrent >= pOFI->idirSub))
{
cch += (DWORD)SendDlgItemMessage( hDlg,
nLB,
LB_GETTEXT,
(WPARAM)idirCurrent,
(LPARAM)(LPTSTR)&pOFI->szPath[cch] );
pOFI->szPath[cch++] = CHAR_BSLASH;
}
pOFI->szPath[cch] = CHAR_NULL;
return (cch);
}
////////////////////////////////////////////////////////////////////////////
//
// SelDisk
//
// Selects the given disk in the combo drive list. Works for unc names,
// too.
//
////////////////////////////////////////////////////////////////////////////
VOID SelDisk(
HWND hDlg,
LPTSTR lpszDisk)
{
HWND hCmb = GetDlgItem(hDlg, cmb2);
if (lpszDisk)
{
CharLower(lpszDisk);
SendMessage( hCmb,
CB_SETCURSEL,
(WPARAM)SendMessage( hCmb,
CB_FINDSTRING,
(WPARAM)-1,
(LPARAM)lpszDisk ),
0L );
}
else
{
TCHAR szChangeSel[CCHNETPATH];
LPTSTR lpCurDir;
LPCURDLG lpCurDlg;
if ((lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg)) &&
(lpCurDir = lpCurDlg->lpstrCurDir))
{
int cch;
if (!GetCurrentDirectory(ARRAYSIZE(szChangeSel), szChangeSel))
{
EVAL(SUCCEEDED(StringCchCopy(szChangeSel, ARRAYSIZE(szChangeSel), lpCurDir)));
}
cch = GetPathOffset(szChangeSel);
if (cch != -1)
{
szChangeSel[cch] = CHAR_NULL;
}
SendMessage( hCmb,
CB_SETCURSEL,
(WPARAM)SendMessage( hCmb,
CB_FINDSTRING,
(WPARAM)-1,
(LPARAM)szChangeSel ),
0L );
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// LNDSetEvent
//
////////////////////////////////////////////////////////////////////////////
VOID LNDSetEvent(
HWND hDlg)
{
LPCURDLG lpCurDlg;
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
if ( lpCurDlg &&
hLNDEvent &&
!wNoRedraw &&
hLNDThread &&
bNetworkInstalled)
{
gahDlg[lpCurDlg->dwCurDlgNum] = hDlg;
SetEvent(hLNDEvent);
}
}
////////////////////////////////////////////////////////////////////////////
//
// UpdateLocalDrive
//
////////////////////////////////////////////////////////////////////////////
VOID UpdateLocalDrive(
LPTSTR szDrive,
BOOL bGetVolName)
{
DWORD dwFlags = 0;
DWORD dwDriveType;
TCHAR szVolLabel[MAX_PATH];
//
// No unc here - so bypass extra call to GetDiskType and call
// GetDriveType directly.
//
dwDriveType = GetDriveType(szDrive);
if ((dwDriveType != 0) && (dwDriveType != 1))
{
BOOL bRet = TRUE;
szVolLabel[0] = CHAR_NULL;
szDrive[1] = CHAR_COLON;
szDrive[2] = CHAR_NULL;
if ( bGetVolName ||
((dwDriveType != DRIVE_REMOVABLE) &&
(dwDriveType != DRIVE_CDROM) &&
(dwDriveType != DRIVE_REMOTE)) )
{
//
// Removing call to CharUpper since it causes trouble on
// turkish machines.
//
// CharUpper(szDrive);
if (GetFileAttributes(szDrive) != (DWORD)0xffffffff)
{
if (dwDriveType != DRIVE_REMOTE)
{
szDrive[2] = CHAR_BSLASH;
bRet = GetVolumeInformation( szDrive,
szVolLabel,
MAX_PATH,
NULL,
NULL,
&dwFlags,
NULL,
(DWORD)0 );
//
// The adddisk hack to prevent lazy loading from
// overwriting the current removable media's label
// with "" (because it never calls getvolumeinfo)
// is to not allow null lpnames to overwrite, so when
// the volume label really is null, we make it a space.
//
if (!szVolLabel[0])
{
szVolLabel[0] = CHAR_SPACE;
szVolLabel[1] = CHAR_NULL;
}
}
}
}
if (bRet)
{
int nIndex;
CharLower(szDrive);
if (dwDriveType == DRIVE_REMOTE)
{
nIndex = AddDisk( szDrive[0],
szVolLabel,
NULL,
TMPNETDRV );
}
else
{
nIndex = AddDisk( szDrive[0],
szVolLabel,
NULL,
GetDiskIndex(dwDriveType) );
}
if (nIndex != ADDDISK_NOCHANGE)
{
gaDiskInfo[nIndex].bCasePreserved =
(dwFlags & FS_CASE_IS_PRESERVED);
}
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// GetNetDrives
//
// Enumerates network disk resources and updates the global disk info
// structure.
//
// dwScope RESOURCE_CONNECTED or RESOURCE_REMEMBERED
//
// Returns the last connection that did not previously exist.
//
////////////////////////////////////////////////////////////////////////////
VOID GetNetDrives(
DWORD dwScope)
{
DWORD dwRet;
HANDLE hEnum = NULL;
//
// Guard against termination with the enum handle open.
//
dwRet = WNetOpenEnum( dwScope,
RESOURCETYPE_DISK,
RESOURCEUSAGE_CONNECTABLE,
NULL,
&hEnum );
if (dwRet == WN_SUCCESS)
{
while (dwRet == WN_SUCCESS)
{
DWORD dwCount = 0xffffffff;
DWORD cbSize = cbNetEnumBuf;
if (bLNDExit)
{
WNetCloseEnum(hEnum);
return;
}
dwRet = WNetEnumResource(hEnum, &dwCount, gpcNetEnumBuf, &cbSize);
switch (dwRet)
{
case ( WN_SUCCESS ) :
{
//
// Add the Entries to the listbox.
//
TCHAR wcDrive = 0;
NETRESOURCE *pNetRes;
WORD i;
pNetRes = (LPNETRESOURCE)gpcNetEnumBuf;
for (i = 0; dwCount; dwCount--, i++)
{
if (pNetRes[i].lpLocalName)
{
CharLower(pNetRes[i].lpLocalName);
wcDrive = *pNetRes[i].lpLocalName;
}
else
{
//
// Skip deviceless names that are not
// LanMan provided (or, in the case where there
// is no LanMan provider name, skip deviceless
// always).
//
wcDrive = 0;
}
if (!DBL_BSLASH(pNetRes[i].lpRemoteName))
{
continue;
}
//
// When bGetNetDrivesSync is TRUE, we are coming back
// from the Network button, so we want to cd to the
// last connected drive.
// (see last command in this routine)
//
if (bGetNetDrivesSync)
{
int nIndex;
WORD k;
nIndex = AddDisk( wcDrive,
pNetRes[i].lpRemoteName,
pNetRes[i].lpProvider,
(dwScope == RESOURCE_REMEMBERED)
? REMDRVBMP
: NETDRVBMP );
//
// If it's a new connection, update global state.
//
if (nIndex >= 0)
{
//
// Since flushdiskinfotocmb2 will clear out
// the array below, remember it's state here.
// It's a hack, but a nice way to find out
// exactly which of the many threads
// completed a net dlg operation.
//
for (k = 0; k < dwNumDlgs; k++)
{
if (gahDlg[k])
{
// Could encounter small problems with
// preemption here, but assume that
// user cannot simultaneously return
// from two different net dlg calls.
//
lpNetDriveSync = gaDiskInfo[nIndex].lpPath;
SendMessage(
gahDlg[k],
WM_COMMAND,
GET_WM_COMMAND_MPS(
cmb2,
GetDlgItem(gahDlg[k], cmb2),
MYCBN_CHANGEDIR ) );
}
}
}
}
else
{
AddDisk( wcDrive,
pNetRes[i].lpRemoteName,
pNetRes[i].lpProvider,
(dwScope == RESOURCE_REMEMBERED)
? REMDRVBMP
: NETDRVBMP );
}
}
break;
}
case ( WN_MORE_DATA ) :
{
LPTSTR pcTemp;
pcTemp = (LPTSTR)LocalReAlloc( gpcNetEnumBuf,
cbSize,
LMEM_MOVEABLE );
if (!pcTemp)
{
cbNetEnumBuf = 0;
}
else
{
gpcNetEnumBuf = pcTemp;
cbNetEnumBuf = cbSize;
dwRet = WN_SUCCESS;
break;
}
}
case ( WN_NO_MORE_ENTRIES ) :
case ( WN_EXTENDED_ERROR ) :
case ( WN_NO_NETWORK ) :
{
//
// WN_NO_MORE_ENTRIES is a success error code.
// It is special cased when we fall out of the loop.
//
break;
}
case ( WN_BAD_HANDLE ) :
default :
{
break;
}
}
}
WNetCloseEnum(hEnum);
//
// Flush once per event - there will always be a call with
// dwscope = connected.
//
if (dwScope == RESOURCE_CONNECTED)
{
FlushDiskInfoToCmb2();
}
if (bGetNetDrivesSync)
{
bGetNetDrivesSync = FALSE;
}
}
}
////////////////////////////////////////////////////////////////////////////
//
// ListNetDrivesHandler
//
////////////////////////////////////////////////////////////////////////////
VOID ListNetDrivesHandler()
{
BOOL bInit = TRUE;
HANDLE hEnum = NULL;
if (!gpcNetEnumBuf &&
!(gpcNetEnumBuf = (LPTSTR)LocalAlloc(LPTR, cbNetEnumBuf)))
{
hLNDThread = NULL;
return;
}
if (bLNDExit)
{
goto LNDExitThread1;
}
EnterCriticalSection(&g_csNetThread);
while (1)
{
if (bLNDExit)
{
goto LNDExitThread;
}
//
// hLNDEvent will always be valid since we have loaded ourself
// and FreeLibrary will not produce a DLL_PROCESS_DETACH.
//
WaitForSingleObject(hLNDEvent, INFINITE);
//
// In case this is the exit event.
//
if (bLNDExit)
{
goto LNDExitThread;
}
EnableDiskInfo(FALSE, FALSE);
if (bInit)
{
GetNetDrives(RESOURCE_REMEMBERED);
//
// In case this is the exit event.
//
if (bLNDExit)
{
goto LNDExitThread;
}
GetNetDrives(RESOURCE_CONNECTED);
//
// In case this is the exit event.
//
if (bLNDExit)
{
goto LNDExitThread;
}
bInit = FALSE;
}
else
{
//
// In case this is the exit event.
//
if (bLNDExit)
{
goto LNDExitThread;
}
GetNetDrives(RESOURCE_CONNECTED);
//
// In case this is the exit event.
//
if (bLNDExit)
{
goto LNDExitThread;
}
}
ResetEvent(hLNDEvent);
}
LNDExitThread:
bLNDExit = FALSE;
LeaveCriticalSection(&g_csNetThread);
LNDExitThread1:
FreeLibraryAndExitThread(g_hinst, 1);
//
// The ExitThread is implicit in this return.
//
return;
}
////////////////////////////////////////////////////////////////////////////
//
// LoadDrives
//
// Lists the current drives (connected) in the combo box.
//
////////////////////////////////////////////////////////////////////////////
VOID LoadDrives(
HWND hDlg)
{
//
// Hard-code this - It's internal && always cmb2/psh14.
//
HWND hCmb = GetDlgItem(hDlg, cmb2);
DWORD dwThreadID;
LPCURDLG lpCurDlg;
BOOL bFirstAttach = FALSE;
WORD wCurDrive;
TCHAR szDrive[5];
if (!hLNDEvent)
{
//
// Don't check if this succeeds since we can run without the net.
//
hLNDEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
bFirstAttach = TRUE;
}
else
{
//
// Assume all previous connections (except unc) are valid
// for first display - but only when they exist.
//
EnableDiskInfo(TRUE, FALSE);
}
//
// Set the hDlg into the refresh array before initially
// creating the thread so that the worker thread can hide/disable
// the net button in the event that there is no network.
//
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
// sanity check
if (!lpCurDlg)
{
return;
}
gahDlg[lpCurDlg->dwCurDlgNum] = hDlg;
//
// If there is no worker thread for network disk enumeration,
// start up here rather than in the dll, since it's only
// for the fileopen dlg.
//
// Always start a thread if the number of active fileopen dialogs
// goes from 0 to 1
//
if ((lpCurDlg->dwCurDlgNum == 0) && (!hLNDThread))
{
if (hLNDEvent && (bNetworkInstalled = IsNetworkInstalled()))
{
TCHAR szModule[MAX_PATH];
//
// Do this once when dialog thread count goes from 0 to 1.
//
GetModuleFileName(g_hinst, szModule, ARRAYSIZE(szModule));
if (LoadLibrary(szModule))
{
hLNDThread = CreateThread(
NULL,
(DWORD)0,
(LPTHREAD_START_ROUTINE)ListNetDrivesHandler,
(LPVOID)NULL,
(DWORD_PTR)NULL,
&dwThreadID );
}
}
else
{
HWND hNet = GetDlgItem(hDlg, psh14);
EnableWindow(hNet, FALSE);
ShowWindow(hNet, SW_HIDE);
}
}
// Fix for Millenium BUG #113035
// Putting the get drives information code instead of in the
// ListNetDriveHandler thread.
//
// Get the drive information for all drives.
//
// NOTE: If we don't redo all volume info, then a change in a volume
// label will never be caught by wow apps unless wowexec is
// killed and restarted. Therefore, information for all drives
// should be retrieved here.
//
for (wCurDrive = 0; wCurDrive <= 25; wCurDrive++)
{
szDrive[0] = (CHAR_A + (TCHAR)wCurDrive);
szDrive[1] = CHAR_COLON;
szDrive[2] = CHAR_BSLASH;
szDrive[3] = CHAR_NULL;
UpdateLocalDrive(szDrive, FALSE);
}
FlushDiskInfoToCmb2();
//
// Now invalidate all net conns and re-enum, but only if there is
// indeed a worker thread too.
//
if (!bFirstAttach)
{
EnableDiskInfo(FALSE, FALSE);
}
LNDSetEvent(hDlg);
}
////////////////////////////////////////////////////////////////////////////
//
// GetDiskIndex
//
////////////////////////////////////////////////////////////////////////////
DWORD GetDiskIndex(
DWORD dwDriveType)
{
if (dwDriveType == 1)
{
//
// Drive doesn't exist!
//
return (0);
}
else if (dwDriveType == DRIVE_CDROM)
{
return (CDDRVBMP);
}
else if (dwDriveType == DRIVE_REMOVABLE)
{
return (FLOPPYBMP);
}
else if (dwDriveType == DRIVE_REMOTE)
{
return (NETDRVBMP);
}
else if (dwDriveType == DRIVE_RAMDISK)
{
return (RAMDRVBMP);
}
return (HARDDRVBMP);
}
////////////////////////////////////////////////////////////////////////////
//
// CleanUpFile
//
// This releases the memory used by the system dialog bitmaps.
//
////////////////////////////////////////////////////////////////////////////
VOID CleanUpFile()
{
//
// Check if anyone else is around.
//
if (--cLock)
{
return;
}
//
// Select the null bitmap into our memory DC so that the
// DirDrive bitmap can be discarded.
//
SelectObject(hdcMemory, hbmpOrigMemBmp);
}
////////////////////////////////////////////////////////////////////////////
//
// FileOpenAbort
//
////////////////////////////////////////////////////////////////////////////
VOID FileOpenAbort()
{
LPCURDLG lpCurDlg;
lpCurDlg = (LPCURDLG)TlsGetValue(g_tlsiCurDlg);
if (lpCurDlg)
{
EnterCriticalSection(&g_csLocal);
if (dwNumDlgs > 0)
{
dwNumDlgs--;
}
if (dwNumDlgs == 0)
{
//
// If there are no more fileopen dialogs for this process,
// then signal the worker thread it's all over.
//
if (hLNDEvent && hLNDThread)
{
bLNDExit = TRUE;
SetEvent(hLNDEvent);
CloseHandle(hLNDThread);
hLNDThread = NULL;
}
}
LeaveCriticalSection(&g_csLocal);
}
}
////////////////////////////////////////////////////////////////////////////
//
// TermFile
//
////////////////////////////////////////////////////////////////////////////
VOID TermFile()
{
vDeleteDirDriveBitmap();
if (hdcMemory)
{
DeleteDC(hdcMemory);
}
if (hLNDEvent)
{
CloseHandle(hLNDEvent);
hLNDEvent = NULL;
}
if (gpcNetEnumBuf)
{
LocalFree(gpcNetEnumBuf);
}
while (dwNumDisks)
{
dwNumDisks--;
if (gaDiskInfo[dwNumDisks].lpAbbrName)
{
LocalFree(gaDiskInfo[dwNumDisks].lpAbbrName);
}
}
}
/*========================================================================*/
/* Ansi->Unicode Thunk routines */
/*========================================================================*/
#ifdef UNICODE
////////////////////////////////////////////////////////////////////////////
//
// ThunkOpenFileNameA2WDelayed
//
////////////////////////////////////////////////////////////////////////////
VOID ThunkOpenFileNameA2WDelayed(
POPENFILEINFO pOFI)
{
LPOPENFILENAMEA pOFNA = pOFI->pOFNA;
LPOPENFILENAMEW pOFNW = pOFI->pOFN;
if (pOFNA->lpstrDefExt)
{
//
// Make sure the default extension buffer is at least 4 characters
// in length.
//
DWORD cbLen = max(lstrlenA(pOFNA->lpstrDefExt) + 1, 4);
if (pOFNW->lpstrDefExt)
{
LocalFree((HLOCAL)pOFNW->lpstrDefExt);
}
if (!(pOFNW->lpstrDefExt = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
return;
}
else
{
if (pOFNA->lpstrDefExt)
{
SHAnsiToUnicode(pOFNA->lpstrDefExt,(LPWSTR)pOFNW->lpstrDefExt,cbLen );
}
}
}
//
// Need to thunk back to A value since Claris Filemaker side effects
// this in an ID_OK subclass without hooking at the very last moment.
// Do an |= instead of an = to preserve internal flags.
//
pOFNW->Flags &= OFN_ALL_INTERNAL_FLAGS;
pOFNW->Flags |= pOFNA->Flags;
}
////////////////////////////////////////////////////////////////////////////
//
// ThunkOpenFileNameA2W
//
////////////////////////////////////////////////////////////////////////////
BOOL ThunkOpenFileNameA2W(
POPENFILEINFO pOFI)
{
int nRet;
LPOPENFILENAMEA pOFNA = pOFI->pOFNA;
LPOPENFILENAMEW pOFNW = pOFI->pOFN;
pOFNW->Flags = pOFNA->Flags;
pOFNW->lCustData = pOFNA->lCustData;
// we actually can have the original ver1 structure passed in here
// so we need to check and make sure to only copy over the valid data
if ((pOFNA->lStructSize == SIZEOF(OPENFILENAMEA) && pOFNW->lStructSize == SIZEOF(OPENFILENAMEW))
)
{
pOFNW->pvReserved = pOFNA->pvReserved ;
pOFNW->dwReserved = pOFNA->dwReserved;
pOFNW->FlagsEx = pOFNA->FlagsEx;
}
//
// Various WOW apps change the strings and *ptrs* to the strings in the
// OPENFILENAME struct while processing messages with their hook procs.
// Handle that silliness here. (We probably don't want to promote this
// beyond WOW).
//
if (pOFNA->Flags & CD_WOWAPP)
{
pOFNW->lpstrFilter = (LPCWSTR)
ThunkMultiANSIStrToWIDE( (LPWSTR)pOFNW->lpstrFilter,
(LPSTR)pOFNA->lpstrFilter,
0 );
pOFNW->lpstrCustomFilter =
ThunkMultiANSIStrToWIDE( pOFNW->lpstrCustomFilter,
pOFNA->lpstrCustomFilter,
pOFNA->nMaxCustFilter );
pOFNW->lpstrFile =
ThunkANSIStrToWIDE( pOFNW->lpstrFile,
pOFNA->lpstrFile,
pOFNA->nMaxFile );
pOFNW->lpstrFileTitle =
ThunkANSIStrToWIDE( pOFNW->lpstrFileTitle,
pOFNA->lpstrFileTitle,
pOFNA->nMaxFileTitle );
pOFNW->lpstrInitialDir = (LPCWSTR)
ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrInitialDir,
(LPSTR)pOFNA->lpstrInitialDir,
0 );
pOFNW->lpstrTitle = (LPCWSTR)
ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrTitle,
(LPSTR)pOFNA->lpstrTitle,
0 );
pOFNW->lpstrDefExt = (LPCWSTR)
ThunkANSIStrToWIDE( (LPWSTR)pOFNW->lpstrDefExt,
(LPSTR)pOFNA->lpstrDefExt,
0 );
pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter;
pOFNW->nMaxFile = pOFNA->nMaxFile;
pOFNW->nMaxFileTitle = pOFNA->nMaxFileTitle;
pOFNW->nFileOffset = pOFNA->nFileOffset;
pOFNW->nFileExtension = pOFNA->nFileExtension;
}
else
{
if (pOFNW->lpstrFile)
{
if (pOFNA->lpstrFile)
{
nRet = SHAnsiToUnicode(pOFNA->lpstrFile,pOFNW->lpstrFile,pOFNW->nMaxFile );
if (nRet == 0)
{
return (FALSE);
}
}
}
if (pOFNW->lpstrFileTitle && pOFNW->nMaxFileTitle)
{
if (pOFNA->lpstrFileTitle)
{
nRet= MultiByteToWideChar(CP_ACP,
0,
pOFNA->lpstrFileTitle,
pOFNA->nMaxFileTitle,
pOFNW->lpstrFileTitle,
pOFNW->nMaxFileTitle);
if (nRet == 0)
{
return (FALSE);
}
}
}
if (pOFNW->lpstrCustomFilter)
{
if (pOFI->pasCustomFilter)
{
LPSTR psz = pOFI->pasCustomFilter->Buffer;
DWORD cch = 0;
if (*psz || *(psz + 1))
{
cch = 2;
while (*psz || *(psz + 1))
{
psz++;
cch++;
}
}
if (cch)
{
pOFI->pasCustomFilter->Length = cch;
nRet = MultiByteToWideChar(CP_ACP,
0,
pOFI->pasCustomFilter->Buffer,
pOFI->pasCustomFilter->Length,
pOFI->pusCustomFilter->Buffer,
pOFI->pusCustomFilter->MaximumLength );
if (nRet == 0)
{
return (FALSE);
}
}
}
}
}
pOFNW->nFilterIndex = pOFNA->nFilterIndex;
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// ThunkOpenFileNameW2A
//
////////////////////////////////////////////////////////////////////////////
BOOL ThunkOpenFileNameW2A(
POPENFILEINFO pOFI)
{
int nRet;
LPOPENFILENAMEW pOFNW = pOFI->pOFN;
LPOPENFILENAMEA pOFNA = pOFI->pOFNA;
LPWSTR pszW;
USHORT cch;
//
// Supposedly invariant, but not necessarily.
// Definition: invariant - changed by 16-bit apps frequently
//
pOFNA->Flags = pOFNW->Flags;
pOFNA->lCustData = pOFNW->lCustData;
// this way we can assert that we are covered.
DEBUG_CODE(pOFNA->nFileOffset = 0 );
// we actually can have the original ver1 structure passed in here
// so we need to check and make sure to only copy over the valid data
if (pOFNA->lStructSize == SIZEOF(OPENFILENAMEA) && pOFNW->lStructSize == SIZEOF(OPENFILENAMEW)
)
{
pOFNA->pvReserved = pOFNW->pvReserved;
pOFNA->dwReserved = pOFNW->dwReserved;
pOFNA->FlagsEx = pOFNW->FlagsEx;
}
if (pOFNA->lpstrFileTitle && pOFNA->nMaxFileTitle)
{
nRet = SHUnicodeToAnsi(pOFNW->lpstrFileTitle,pOFNA->lpstrFileTitle,pOFNA->nMaxFileTitle);
if (nRet == 0)
{
return (FALSE);
}
}
if (pOFNA->lpstrCustomFilter)
{
pszW = pOFI->pusCustomFilter->Buffer;
cch = 0;
if (*pszW || *(pszW + 1))
{
cch = 2;
while (*pszW || *(pszW + 1))
{
pszW++;
cch++;
}
}
if (cch)
{
pOFI->pusCustomFilter->Length = cch;
nRet = WideCharToMultiByte(CP_ACP,
0,
pOFI->pusCustomFilter->Buffer,
pOFI->pusCustomFilter->Length,
pOFI->pasCustomFilter->Buffer,
pOFI->pasCustomFilter->MaximumLength,
NULL,
NULL);
if (nRet == 0)
{
return (FALSE);
}
}
}
pOFNA->nFilterIndex = pOFNW->nFilterIndex;
if (pOFNA->lpstrFile && pOFNW->lpstrFile)
{
if (GetStoredExtendedError() == FNERR_BUFFERTOOSMALL)
{
//
// In the case where the lpstrFile buffer is too small,
// lpstrFile contains the size of the buffer needed for
// the string rather than the string itself.
//
pszW = pOFNW->lpstrFile;
switch (pOFNA->nMaxFile)
{
case ( 3 ) :
default :
{
pOFNA->lpstrFile[2] = CHAR_NULL;
// fall thru...
}
case ( 2 ) :
{
pOFNA->lpstrFile[1] = HIBYTE(*pszW);
// fall thru...
}
case ( 1 ) :
{
pOFNA->lpstrFile[0] = LOBYTE(*pszW);
// fall thru...
}
case ( 0 ) :
{
break;
}
}
}
else
{
LPWSTR pFileW = pOFNW->lpstrFile;
DWORD cchFile = 0;
// Find the length of string to be converted. This takes care of both single select (there will be only string)
// and multiselect case (there will multiple strings with double null termination)
while (*pFileW)
{
DWORD cch = lstrlenW(pFileW) +1;
cchFile +=cch;
pFileW += cch;
}
if (pOFNW->Flags & OFN_ALLOWMULTISELECT)
{
// for the double null terminator
cchFile++;
}
// need to copy the whole buffer after the initial directory
nRet = WideCharToMultiByte(CP_ACP,
0,
pOFNW->lpstrFile, cchFile,
pOFNA->lpstrFile, pOFNA->nMaxFile,
NULL, NULL);
if (nRet == 0)
{
return (FALSE);
}
if ((SHORT)pOFNW->nFileOffset > 0)
{
pOFNA->nFileOffset = (WORD) WideCharToMultiByte( CP_ACP,
0,
pOFNW->lpstrFile,
pOFNW->nFileOffset,
NULL,
0,
NULL,
NULL );
}
else
{
pOFNA->nFileOffset = pOFNW->nFileOffset;
}
if ((SHORT)pOFNW->nFileExtension > 0)
{
pOFNA->nFileExtension = (WORD) WideCharToMultiByte( CP_ACP,
0,
pOFNW->lpstrFile,
pOFNW->nFileExtension,
NULL,
0,
NULL,
NULL );
}
else
{
pOFNA->nFileExtension = pOFNW->nFileExtension;
}
}
}
else
{
pOFNA->nFileOffset = pOFNW->nFileOffset;
pOFNA->nFileExtension = pOFNW->nFileExtension;
}
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// GenericGetFileNameA
//
////////////////////////////////////////////////////////////////////////////
BOOL GenericGetFileNameA(
LPOPENFILENAMEA pOFNA,
DLGPROC qfnDlgProc)
{
LPOPENFILENAMEW pOFNW;
BOOL bRet = FALSE;
OFN_UNICODE_STRING usCustomFilter;
OFN_ANSI_STRING asCustomFilter;
DWORD cbLen;
LPSTR pszA;
DWORD cch;
LPBYTE pStrMem = NULL;
OPENFILEINFO OFI = {0};
if (!pOFNA)
{
StoreExtendedError(CDERR_INITIALIZATION);
return (FALSE);
}
//Set the Open File Version
OFI.iVersion = OPENFILEVERSION;
if (pOFNA->lStructSize == OPENFILENAME_SIZE_VERSION_400)
{
OFI.iVersion = OPENFILEVERSION_NT4;
}
// we allow both sizes because we allocate a full size one anyway
// and we want to preserve the original structure for notifies
if ((pOFNA->lStructSize != OPENFILENAME_SIZE_VERSION_400) &&
(pOFNA->lStructSize != sizeof(OPENFILENAMEA))
)
{
StoreExtendedError(CDERR_STRUCTSIZE);
return (FALSE);
}
if (!(pOFNW = (LPOPENFILENAMEW)LocalAlloc(LPTR, sizeof(OPENFILENAMEW))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
return (FALSE);
}
//
// Constant stuff.
//
pOFNW->lStructSize = sizeof(OPENFILENAMEW);
pOFNW->hwndOwner = pOFNA->hwndOwner;
pOFNW->hInstance = pOFNA->hInstance;
pOFNW->lpfnHook = pOFNA->lpfnHook;
// it will always be a valid structsize at this point
if (pOFNA->lStructSize != OPENFILENAME_SIZE_VERSION_400)
{
pOFNW->pvReserved = pOFNA->pvReserved;
pOFNW->dwReserved = pOFNA->dwReserved;
pOFNW->FlagsEx = pOFNA->FlagsEx;
}
//
// Init TemplateName constant.
//
if (pOFNA->Flags & OFN_ENABLETEMPLATE)
{
if (!IS_INTRESOURCE(pOFNA->lpTemplateName))
{
cbLen = lstrlenA(pOFNA->lpTemplateName) + 1;
if (!(pOFNW->lpTemplateName = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
else
{
SHAnsiToUnicode(pOFNA->lpTemplateName,(LPWSTR)pOFNW->lpTemplateName,cbLen);
}
}
else
{
(DWORD_PTR)pOFNW->lpTemplateName = (DWORD_PTR)pOFNA->lpTemplateName;
}
}
else
{
pOFNW->lpTemplateName = NULL;
}
//
// Initialize Initial Dir constant.
//
if (pOFNA->lpstrInitialDir)
{
cbLen = lstrlenA(pOFNA->lpstrInitialDir) + 1;
if (!(pOFNW->lpstrInitialDir = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
else
{
SHAnsiToUnicode(pOFNA->lpstrInitialDir,(LPWSTR)pOFNW->lpstrInitialDir,cbLen);
}
}
else
{
pOFNW->lpstrInitialDir = NULL;
}
//
// Initialize Title constant.
//
if (pOFNA->lpstrTitle)
{
cbLen = lstrlenA(pOFNA->lpstrTitle) + 1;
if (!(pOFNW->lpstrTitle = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
else
{
SHAnsiToUnicode(pOFNA->lpstrTitle,(LPWSTR)pOFNW->lpstrTitle,cbLen );
}
}
else
{
pOFNW->lpstrTitle = NULL;
}
//
// Initialize Def Ext constant.
//
if (pOFNA->lpstrDefExt)
{
//
// Make sure the default extension buffer is at least 4 characters
// in length.
//
cbLen = max(lstrlenA(pOFNA->lpstrDefExt) + 1, 4);
if (!(pOFNW->lpstrDefExt = (LPWSTR)LocalAlloc(LPTR, (cbLen * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
else
{
SHAnsiToUnicode(pOFNA->lpstrDefExt,(LPWSTR)pOFNW->lpstrDefExt,cbLen );
}
}
else
{
pOFNW->lpstrDefExt = NULL;
}
//
// Initialize Filter constant. Note: 16-bit apps change this.
//
if (pOFNA->lpstrFilter)
{
pszA = (LPSTR)pOFNA->lpstrFilter;
cch = 0;
if (*pszA || *(pszA + 1))
{
//
// Pick up trailing nulls.
//
cch = 2;
try
{
while (*pszA || *(pszA + 1))
{
pszA++;
cch++;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
StoreExtendedError(CDERR_INITIALIZATION);
goto GenericExit;
}
}
//
// Need to do cch + 1 in the Local Alloc rather than just cch.
// This is to make sure there is at least one extra null in the
// string so that if a filter does not have the second part of
// the pair, three nulls will be placed in the wide string.
//
// Example: "Print File (*.prn)\0\0\0"
//
if (!(pOFNW->lpstrFilter = (LPWSTR)LocalAlloc(LPTR, ((cch + 1) * sizeof(WCHAR)))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
else
{
MultiByteToWideChar(CP_ACP,
0,
pOFNA->lpstrFilter,
cch,
(LPWSTR)pOFNW->lpstrFilter,
cch);
}
}
else
{
pOFNW->lpstrFilter = NULL;
}
//
// Initialize File strings.
//
if (pOFNA->lpstrFile)
{
if (pOFNA->nMaxFile <= (DWORD)lstrlenA(pOFNA->lpstrFile))
{
StoreExtendedError(CDERR_INITIALIZATION);
goto GenericExit;
}
pOFNW->nMaxFile = pOFNA->nMaxFile;
if (!(pOFNW->lpstrFile = (LPWSTR)LocalAlloc(LPTR, pOFNW->nMaxFile * sizeof(WCHAR))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
}
else
{
//
// Conversion done in thunkofna2w.
//
pOFNW->nMaxFile = 0;
pOFNW->lpstrFile = NULL;
}
//
// Initialize File Title strings.
//
if (pOFNA->lpstrFileTitle && pOFNA->nMaxFileTitle)
{
//
// Calculate length of lpstrFileTitle.
//
pszA = pOFNA->lpstrFileTitle;
cch = 0;
try
{
while (*pszA++)
{
cch++;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
if (cch)
{
cch--;
}
(pOFNA->lpstrFileTitle)[cch] = CHAR_NULL;
}
if (pOFNA->nMaxFileTitle < cch)
{
//
// Override the incorrect length from the app.
// Make room for the null.
//
pOFNW->nMaxFileTitle = cch + 1;
}
else
{
pOFNW->nMaxFileTitle = pOFNA->nMaxFileTitle;
}
if (!(pOFNW->lpstrFileTitle = (LPWSTR)LocalAlloc(LPTR, pOFNW->nMaxFileTitle * sizeof(WCHAR))))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
}
else
{
//
// Conversion done in thunkofna2w.
//
pOFNW->nMaxFileTitle = 0;
pOFNW->lpstrFileTitle = NULL;
}
//
// Initialize custom filter strings.
//
if ((asCustomFilter.Buffer = pOFNA->lpstrCustomFilter))
{
pszA = pOFNA->lpstrCustomFilter;
cch = 0;
if (*pszA || *(pszA + 1))
{
cch = 2;
try
{
while (*pszA || *(pszA + 1))
{
pszA++;
cch++;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
StoreExtendedError(CDERR_INITIALIZATION);
goto GenericExit;
}
}
//
// JVert-inspired-wow-compatibility-hack-to-make-vbasic2.0-makeexe
// save-as-dialog-box-work-even-though-they-didn't-fill-in-
// the-whole-structure(nMaxCustFilter)-according-to-winhelp-spec fix
//
if (!(pOFNA->Flags & OFN_NOLONGNAMES))
{
if (((DWORD)cch >= pOFNA->nMaxCustFilter) ||
(pOFNA->nMaxCustFilter < 40))
{
StoreExtendedError(CDERR_INITIALIZATION);
goto GenericExit;
}
asCustomFilter.Length = cch;
asCustomFilter.MaximumLength = pOFNA->nMaxCustFilter;
pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter;
}
else
{
asCustomFilter.Length = cch;
if (pOFNA->nMaxCustFilter < cch)
{
asCustomFilter.MaximumLength = cch;
pOFNW->nMaxCustFilter = cch;
}
else
{
asCustomFilter.MaximumLength = pOFNA->nMaxCustFilter;
pOFNW->nMaxCustFilter = pOFNA->nMaxCustFilter;
}
}
usCustomFilter.MaximumLength = (asCustomFilter.MaximumLength + 1) * sizeof(WCHAR);
usCustomFilter.Length = asCustomFilter.Length * sizeof(WCHAR);
}
else
{
pOFNW->nMaxCustFilter = usCustomFilter.MaximumLength = 0;
pOFNW->lpstrCustomFilter = NULL;
}
if (usCustomFilter.MaximumLength > 0)
{
if (!(pStrMem = (LPBYTE)LocalAlloc(LPTR, usCustomFilter.MaximumLength)))
{
StoreExtendedError(CDERR_MEMALLOCFAILURE);
goto GenericExit;
}
else
{
pOFNW->lpstrCustomFilter = usCustomFilter.Buffer = (LPWSTR)pStrMem;
}
}
else
{
pStrMem = NULL;
}
OFI.pOFN = pOFNW;
OFI.pOFNA = pOFNA;
OFI.pasCustomFilter = &asCustomFilter;
OFI.pusCustomFilter = &usCustomFilter;
OFI.ApiType = COMDLG_ANSI;
//
// The following should always succeed.
//
if (!ThunkOpenFileNameA2W(&OFI))
{
StoreExtendedError(CDERR_INITIALIZATION);
goto GenericExit;
}
bRet = GetFileName(&OFI, qfnDlgProc);
if (g_bUserPressedCancel == FALSE)
{
ThunkOpenFileNameW2A(&OFI);
}
GenericExit:
if (pStrMem)
{
LocalFree(pStrMem);
}
if (!IS_INTRESOURCE(pOFNW->lpstrFile))
{
LocalFree((HLOCAL)pOFNW->lpstrFile);
}
if (!IS_INTRESOURCE(pOFNW->lpstrFileTitle))
{
LocalFree((HLOCAL)pOFNW->lpstrFileTitle);
}
if (!IS_INTRESOURCE(pOFNW->lpstrFilter))
{
LocalFree((HLOCAL)pOFNW->lpstrFilter);
}
if (!IS_INTRESOURCE(pOFNW->lpstrDefExt))
{
LocalFree((HLOCAL)pOFNW->lpstrDefExt);
}
if (!IS_INTRESOURCE(pOFNW->lpstrTitle))
{
LocalFree((HLOCAL)pOFNW->lpstrTitle);
}
if (!IS_INTRESOURCE(pOFNW->lpstrInitialDir))
{
LocalFree((HLOCAL)pOFNW->lpstrInitialDir);
}
if (!IS_INTRESOURCE(pOFNW->lpTemplateName))
{
LocalFree((HLOCAL)pOFNW->lpTemplateName);
}
LocalFree(pOFNW);
return (bRet);
}
////////////////////////////////////////////////////////////////////////////
//
// Multi_strlenA
//
// This is a strlen for ANSI string lists that have several strings that
// are *separated* by a NULL char and are *terminated* by two NULL chars.
//
// Returns length of string including all NULL *separators* but not the
// 2nd NULL *terminator*. (ie. cat0dog00 would return length = 8)
//
////////////////////////////////////////////////////////////////////////////
int Multi_strlenA(
LPCSTR str)
{
int ctr = 0;
if (str)
{
while (*str)
{
while (*str++)
{
ctr++;
}
ctr++; // count the NULL separator
}
}
return (ctr);
}
////////////////////////////////////////////////////////////////////////////
//
// Multi_strcpyAtoW
//
// This is a strcpy for string lists that have several strings that are
// *separated* by a NULL char and are *terminated* by two NULL chars.
// Returns FALSE if:
// 1. the wide buffer is determined to be too small
// 2. the ptr to either buffer is NULL
// Returns TRUE if the copy was successful.
//
////////////////////////////////////////////////////////////////////////////
BOOL Multi_strcpyAtoW(
LPWSTR pDestW,
LPCSTR pSrcA,
int cChars)
{
int off = 0;
int cb;
if (!pSrcA || !pDestW)
{
return (FALSE);
}
cChars = max(cChars, (Multi_strlenA(pSrcA) + 1));
if (LocalSize((HLOCAL)pDestW) < (cChars * sizeof(WCHAR)))
{
return (FALSE);
}
while (*pSrcA)
{
cb = lstrlenA(pSrcA) + 1;
off += MultiByteToWideChar(CP_ACP,0,pSrcA,cb,pDestW + off, cb);
pSrcA += cb;
}
pDestW[off] = L'\0';
return (TRUE);
}
////////////////////////////////////////////////////////////////////////////
//
// ThunkMultiANSIStrToWIDE
//
// Thunks an ANSI multi-string (a list of NULL *separated* strings with
// two NULLs *terminating* the list) to the equivalent WIDE multi-string.
//
// Note: If the original wide buffer is too small to contain the new list,
// it will be free'd and a new wide buffer will be allocated. If a
// new wide buffer can't be allocated, the ptr to the original wide
// buffer is returned with no changes to the contents.
//
// Returns: ptr to the original WIDE buffer
// OR ptr to a new wide buffer if original buffer was too small
// OR NULL if pSrcA is NULL.
//
////////////////////////////////////////////////////////////////////////////
LPWSTR ThunkMultiANSIStrToWIDE(
LPWSTR pDestW,
LPSTR pSrcA,
int cChars)
{
int size;
HLOCAL hBufW;
if (!pSrcA)
{
//
// The app doesn't want a buffer for this anymore.
//
if (pDestW)
{
LocalFree((HLOCAL)pDestW);
}
return (NULL);
}
//
// First try to copy to the existing wide buffer since most of the time
// there will be no change to the buffer ptr anyway.
//
if (!(Multi_strcpyAtoW(pDestW, pSrcA, cChars)))
{
//
// If the wide buffer is too small (or NULL or invalid), allocate
// a bigger buffer.
//
size = max(cChars, (Multi_strlenA(pSrcA) + 1));
cChars = size;
if (hBufW = LocalAlloc(LPTR, (size * sizeof(WCHAR))))
{
//
// Try to copy to the new wide buffer.
//
if ((Multi_strcpyAtoW((LPWSTR)hBufW, pSrcA, cChars)))
{
if (pDestW)
{
LocalFree((HLOCAL)pDestW);
}
pDestW = (LPWSTR)hBufW;
}
else
{
//
// Don't change anything.
//
LocalFree(hBufW);
}
}
}
return (pDestW);
}
////////////////////////////////////////////////////////////////////////////
//
// ThunkANSIStrToWIDE
//
// Thunks an ANSI string to WIDE.
//
// Note: If the original wide buffer is too small to contain the new
// string, it will be free'd and a new wide buffer will be allocated.
// If a new wide buffer can't be allocated, the ptr to the original
// wide buffer is returned with no changes to the contents.
//
// Returns: ptr to the original WIDE buffer
// OR ptr to a new wide buffer if original buffer was too small
// OR NULL if pSrcA is NULL.
//
////////////////////////////////////////////////////////////////////////////
LPWSTR ThunkANSIStrToWIDE(
LPWSTR pDestW,
LPSTR pSrcA,
int cChars)
{
HLOCAL hBufW;
int size;
if (!pSrcA)
{
//
// The app doesn't want a buffer for this anymore.
//
if (pDestW)
{
LocalFree((HLOCAL)pDestW);
}
return (NULL);
}
size = max(cChars, (lstrlenA(pSrcA) + 1));
cChars = size;
//
// If the wide buffer is too small (or NULL or invalid), allocate a
// bigger buffer.
//
if (LocalSize((HLOCAL)pDestW) < (size * sizeof(WCHAR)))
{
if (hBufW = LocalAlloc(LPTR, (size * sizeof(WCHAR))))
{
//
// Try to copy to the new wide buffer.
//
if (SHAnsiToUnicode(pSrcA,(LPWSTR)hBufW,cChars ))
{
if (pDestW)
{
LocalFree((HLOCAL)pDestW);
}
pDestW = (LPWSTR)hBufW;
}
else
{
//
// Don't change anything.
//
LocalFree(hBufW);
}
}
}
else
{
//
// Just use the original wide buffer.
//
SHAnsiToUnicode(pSrcA,pDestW, cChars);
}
return (pDestW);
}
#ifdef WINNT
////////////////////////////////////////////////////////////////////////////
//
// Ssync_ANSI_UNICODE_OFN_For_WOW
//
// Function to allow NT WOW to keep the ANSI & UNICODE versions of
// the OPENFILENAME structure in ssync as required by many 16-bit apps.
// See notes for Ssync_ANSI_UNICODE_Struct_For_WOW() in dlgs.c.
//
////////////////////////////////////////////////////////////////////////////
VOID Ssync_ANSI_UNICODE_OFN_For_WOW(
HWND hDlg,
BOOL f_ANSI_to_UNICODE)
{
POPENFILEINFO pOFI;
if (pOFI = (POPENFILEINFO)GetProp(hDlg, FILEPROP))
{
if (pOFI->pOFN && pOFI->pOFNA)
{
if (f_ANSI_to_UNICODE)
{
ThunkOpenFileNameA2W(pOFI);
}
else
{
ThunkOpenFileNameW2A(pOFI);
}
}
}
}
#endif
#endif