windows-nt/Source/XPSP1/NT/inetsrv/iis/setup/osrc/browsedi.cpp
2020-09-26 16:20:57 +08:00

750 lines
30 KiB
C++

//
// BrowseDir.cpp
//
// Functionality for the "Browse Directory" dialog.
// Requires dialog resource IDD_BROWSEDIRECTORY.
//
// History:
//
// 10/04/95 KenSh Created
// 10/09/95 KenSh Fixed bugs, removed globals and statics
//
#include "stdafx.h"
#include <dlgs.h>
#include <direct.h>
//*** Custom window messages
//
#define CM_UPDATEEDIT (WM_USER + 42) // Update text in the edit (sent to the dialog)
//*** Dialog control IDs
//
#define IDC_FILENAME edt1 // Edit box w/ current directory
#define IDC_FILELIST lst2 // Listbox with current directory hierarchy
#define IDC_DRIVECOMBO cmb2 // Combo-box with current drive
#define IDC_NETWORK psh14 // Network button (added at runtime)
//*** Window property names
//
const TCHAR c_szOFNProp[] = _T("OFNStruct");
const TCHAR c_szRedrawProp[] = _T("Redraw");
//*** Globals
//
// Note: Use of thse globals assumes that any multiple threads using
// our subclass at the same time always have the same original edit/
// combo window procs. I think this is true.
//
WNDPROC g_pfnPrevEditProc; // original edit proc (before subclass)
WNDPROC g_pfnPrevComboProc; // original combo proc (before subclass)
//*** Local function declarations
//
BOOL CALLBACK BrowseDirDlgHook( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
BOOL BrowseDir_OnOK( HWND hwnd );
// This struct is used internally and kept as a window property,
// in lieu of using static variables or MFC.
typedef struct _OFNINFO
{
OPENFILENAME ofn; // OFN struct passed to GetOpenFileName
TCHAR szLastDirName[_MAX_PATH]; // last known good directory
BOOL fAllowSetText; // should we allow WM_SETTEXT for the Edit
// BOOL fNetworking; // is Connect Network Drive dialog open
} OFNINFO, *LPOFNINFO;
//----------------------------------------------------------------------------
// Procedure BrowseForDirectory
//
// Purpose Displays a dialog that lets the user choose a directory
// name, either local or UNC.
//
// Parameters hwndParent Parent window for the dialog
// pszInitialDir Directory to use as the default
// pszBuf Where to store the answer
// cchBuf Number of characters in this buffer
// pszDialogTitle Title for the dialog
//
// Returns nonzero if successful, zero if not. If successful, pszBuf
// will be filled with the full pathname of the chosen directory.
//
// History 10/06/95 KenSh Created
// 10/09/95 KenSh Use lCustData member instead of global
//
CString strSelectDir;
BOOL BrowseForDirectory(
HWND hwndParent,
LPCTSTR pszInitialDir,
LPTSTR pszBuf,
int cchBuf,
LPCTSTR pszDialogTitle,
BOOL bRemoveTrailingBackslash )
{
TCHAR szInitialDir[MAX_PATH];
OFNINFO ofnInfo;
pszBuf[0] = _T('\0');
// Prepare the initial directory... add a backslash if it's
// a 2-character path
_tcscpy( szInitialDir, pszInitialDir );
if( !szInitialDir[2] )
{
szInitialDir[2] = _T('\\');
szInitialDir[3] = _T('\0');
}
if( pszDialogTitle )
{
ofnInfo.ofn.lpstrTitle = pszDialogTitle;
}
else
{
MyLoadString( IDS_SELECT_DIR, strSelectDir );
ofnInfo.ofn.lpstrTitle = (LPCTSTR)strSelectDir;
}
ofnInfo.ofn.lStructSize = sizeof(OPENFILENAME);
ofnInfo.ofn.hwndOwner = hwndParent;
ofnInfo.ofn.hInstance = (HINSTANCE) g_MyModuleHandle;
ofnInfo.ofn.lpstrFilter = NULL;
ofnInfo.ofn.lpstrCustomFilter = NULL;
ofnInfo.ofn.nMaxCustFilter = 0;
ofnInfo.ofn.nFilterIndex = 0;
ofnInfo.ofn.lpstrFile = pszBuf;
ofnInfo.ofn.nMaxFile = cchBuf;
ofnInfo.ofn.lpstrFileTitle = NULL;
ofnInfo.ofn.nMaxFileTitle = 0;
ofnInfo.ofn.lpstrInitialDir = szInitialDir;
ofnInfo.ofn.nFileOffset = 0;
ofnInfo.ofn.nFileExtension = 0;
ofnInfo.ofn.lpstrDefExt = NULL;
ofnInfo.ofn.lCustData = (LPARAM)&ofnInfo;
ofnInfo.ofn.lpfnHook = (LPOFNHOOKPROC)BrowseDirDlgHook;
ofnInfo.ofn.lpTemplateName = MAKEINTRESOURCE( IDD_BROWSEDIRECTORY );
ofnInfo.ofn.Flags = OFN_ENABLEHOOK | OFN_PATHMUSTEXIST |
OFN_NONETWORKBUTTON | OFN_ENABLETEMPLATE |
OFN_HIDEREADONLY;
iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("comdlg32:GetOpenFileName().Start.")));
int nResult = ::GetOpenFileName( &ofnInfo.ofn );
iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("comdlg32:GetOpenFileName().End.")));
DWORD dw = 0;
if (nResult == 0)
{
iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("comdlg32:CommDlgExtendedError().Start.")));
dw = CommDlgExtendedError();
iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("comdlg32:CommDlgExtendedError().End.")));
}
return (BOOL)( IDOK == nResult );
}
//----------------------------------------------------------------------------
// Procedure pszSkipDriveSpec
//
// Purpose Returns a pointer to whatever comes after the drive part
// of a filename. For example:
// c:\foo\bar.bat \\server\share\file.txt
// ^ ^
//
// Returns Pointer to the appropriate part of the string, or a pointer
// to the end of the string if it's not in the right format
//
// History 10/06/95 KenSh Created
//
LPTSTR pszSkipDriveSpec( LPTSTR pszPathName )
{
LPTSTR pch = NULL;
if( pszPathName[0] == _T('\\') && pszPathName[1] == _T('\\') )
{
pch = _tcschr(pszPathName+2, _T('\\'));
if( NULL != pch)
{
LPTSTR pchResult;
pchResult = _tcschr( pch, _T('\\') );
if( pchResult )
{
pchResult = _tcschr( pchResult+1, _T('\\') );
}
if( pchResult )
{
return pchResult;
}
else
{
return _tcschr( pch, _T('\0') );
}
}
else
{
return _tcschr( pszPathName, _T('\0') );
}
}
else
{
pch = _tcschr( pszPathName, _T(':') );
if( pch )
{
return _tcsinc(pch);
}
else
{
return _tcschr( pszPathName, _T('\0') );
}
}
}
//----------------------------------------------------------------------------
// Procedure BrowseDirEditProc
//
// Purpose Subclassed window proc for the edit control in the Browse
// for Directory dialog. We override the WM_SETTEXT message
// to control when the window text can be programatically
// changed.
//
// Parameters standard
//
// Returns standard
//
// History 10/06/95 KenSh Created
// 10/09/95 KenSh Moved most code into UpdateEditText()
//
LRESULT CALLBACK BrowseDirEditProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
// In order to prevent default functionality from setting the Edit's text
// to strings like "*.*", we capture WM_SETTEXT and only allow it to occur
// if the fAllowSetText flag is specified in the OFNINFO struct.
case WM_SETTEXT:
{
LPOFNINFO lpOFNInfo = (LPOFNINFO)GetProp( GetParent(hwnd), c_szOFNProp );
if( lpOFNInfo->fAllowSetText )
{
break; // default processing
}
else
{
return 0L; // suppress the urge to change the text
}
}
default:
break;
}
return CallWindowProc( g_pfnPrevEditProc, hwnd, message, wParam, lParam );
}
//----------------------------------------------------------------------------
// Procedure BrowseDirComboProc
//
// Purpose Subclassed window proc for the combo box in the Browse
// Directory dialog. We need to subclass so we can catch the
// change to selection after the Network button is used to
// switch drives.
//
// Parameters standard
//
// Returns standard
//
// History 10/09/95 KenSh Created
//
LRESULT CALLBACK BrowseDirComboProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
// We catch the WM_SETREDRAW message so that we ignore CB_SETCURSEL
// messages when the combo is not supposed to be updated. This is
// pretty much of a hack due to the ordering of messages after the
// Network button is clicked.
case WM_SETREDRAW:
{
if( wParam )
{
SetProp( hwnd, c_szRedrawProp, (HANDLE)1 );
}
else
{
if( GetProp( hwnd, c_szRedrawProp ) )
{
RemoveProp( hwnd, c_szRedrawProp );
}
}
break; // continue with default processing
}
// We catch CB_SETCURSEL and use it to update our edit box after the
// Network button has been pressed.
case CB_SETCURSEL:
{
LPOFNINFO lpOFNInfo = (LPOFNINFO)GetProp( GetParent(hwnd), c_szOFNProp );
#ifdef NEVER
// If "Network" was pressed and redraw has been re-enabled...
if( lpOFNInfo->fNetworking && GetProp( hwnd, c_szRedrawProp ) )
{
TCHAR szBuf[_MAX_PATH];
LRESULT lResult = CallWindowProc( g_pfnPrevComboProc, hwnd, message, wParam, lParam );
// Turn off the networking flag
lpOFNInfo->fNetworking = FALSE;
// Force the selected drive to be the current drive and
// get the current directory on that drive
GetWindowText( hwnd, szBuf, _MAX_PATH );
_chdrive( _totupper(szBuf[0]) - 'A' + 1 );
GetCurrentDirectory( _MAX_PATH, szBuf );
// Update the edit box with the new text.
lpOFNInfo->fAllowSetText = TRUE;
SetDlgItemText( GetParent(hwnd), IDC_FILENAME, szBuf );
lpOFNInfo->fAllowSetText = FALSE;
SendDlgItemMessage( GetParent(hwnd), IDC_FILENAME, EM_SETSEL, 0, (LPARAM)-1 );
return lResult;
}
#endif
break;
}
case WM_DESTROY:
{
if( GetProp( hwnd, c_szRedrawProp ) )
{
RemoveProp( hwnd, c_szRedrawProp );
}
break; // continue with default processing
}
default:
break;
}
return CallWindowProc( g_pfnPrevComboProc, hwnd, message, wParam, lParam );
}
//----------------------------------------------------------------------------
// Procedure UpdateEditText
//
// Purpose Based on which control has focus and the current state of
// the edit control, calculates the new "current directory" and
// sets the Edit control's text to match.
//
// Parameters hwndDlg handle to the dialog window. This function will
// access the lpOFNInfo window property.
//
// Returns nothing
//
// History 10/09/95 KenSh Created
//
VOID UpdateEditText( HWND hwndDlg )
{
HWND hwndLB = GetDlgItem(hwndDlg, IDC_FILELIST);
int nCurSel = (int)SendMessage(hwndLB, LB_GETCURSEL, 0, 0);
TCHAR szDirName[_MAX_PATH];
LPTSTR pchStart = NULL;
LPOFNINFO lpOFNInfo = (LPOFNINFO)GetProp( hwndDlg, c_szOFNProp );
int i;
HWND hwndFocus = GetFocus();
int idFocus = GetDlgCtrlID( hwndFocus );
if( idFocus == IDC_FILELIST )
// Listbox: build name up through current LB selection
{
// First get the top entry in the listbox, this will tell us
// if it's a connected drive or a UNC drive
SendMessage( hwndLB, LB_GETTEXT, 0, (LPARAM)szDirName );
// Run down the entries in the listbox appending them
// and stop when we get to the current selection.
if( szDirName[0] == _T('\\') && szDirName[1] == _T('\\') )
pchStart = _tcschr(szDirName+2, _T('\0'));
else
pchStart = _tcsinc(szDirName) + 1; // skip 2 chars, first may be MBCS
if (NULL != pchStart)
{
for( i = 1; i <= nCurSel; i++ )
{
if( *pchStart != _T('\\') )
*pchStart = _T('\\');
pchStart = _tcsinc(pchStart);
SendMessage( hwndLB, LB_GETTEXT, i, (LPARAM)pchStart );
pchStart = _tcschr(pchStart, _T('\0'));
}
}
}
else if( idFocus == IDC_DRIVECOMBO )
// Combo box: use current working directory
{
GetCurrentDirectory( _MAX_PATH, szDirName );
}
else if( idFocus == IDC_FILENAME )
// Edit control: build the new path using the edit contents
{
TCHAR szBuf[_MAX_PATH];
GetDlgItemText( hwndDlg, IDC_FILENAME, szBuf, _MAX_PATH );
if( szBuf[0] == _T('\\') )
{
if( szBuf[1] == _T('\\') )
// full UNC path was specified
{
_tcscpy( szDirName, szBuf );
}
else
// new directory on the current drive
{
_tcscpy( szDirName, lpOFNInfo->szLastDirName );
LPTSTR pch = pszSkipDriveSpec(szDirName);
_tcscpy( pch, szBuf );
}
}
else if( *_tcsinc(szBuf) == _T(':') )
{
// Change to the requested drive and use the current directory
// on that drive.
if( 0 == _chdrive( _totupper(szBuf[0]) - 'A' + 1 ) &&
szBuf[2] != _T('\\') )
{
// It's a legal drive and no directory was specified,
// so use the current default.
GetCurrentDirectory( _MAX_PATH, szDirName );
}
else
{
// A directory was specified or the drive does not exist.
// Copy the text verbatim to test it and possibly display
// an error message.
_tcscpy( szDirName, szBuf );
}
}
else
// Subdirectory of the current directory
{
// Start with the current directory
_tcscpy( szDirName, lpOFNInfo->szLastDirName );
// Append a backslash if there isn't already one there
LPTSTR pch = _tcsrchr( szDirName, _T('\\') );
if (pch)
{
if( *_tcsinc(pch) )
{
pch = _tcschr( pch, _T('\0') );
if (pch)
{
*pch = _T('\\');
}
}
pch = _tcsinc(pch);
// Append the new directory name
_tcscpy( pch, szBuf );
}
// Attempt to change into the new directory
if( SetCurrentDirectory(szDirName) )
{
// The directory exists, get the official name and use that
// instead of whatever we're using now
GetCurrentDirectory( _MAX_PATH, szDirName );
}
}
}
else
{
// Some other control, likely an error message is going on
_tcscpy( szDirName, lpOFNInfo->szLastDirName );
}
lpOFNInfo->fAllowSetText = TRUE;
SetDlgItemText( hwndDlg, IDC_FILENAME, szDirName );
lpOFNInfo->fAllowSetText = FALSE;
}
//----------------------------------------------------------------------------
// Procedure BrowseDirDlgHook
//
// Purpose Dialog proc for the Browse for Directory subclassed common
// dialog. We spend most of our effort trying to get the right
// string into the edit control (IDC_FILENAME).
//
// Parameters standard
//
// Returns TRUE to halt further processing, FALSE to do the default.
//
// History 10/06/95 KenSh Created
//
BOOL CALLBACK BrowseDirDlgHook( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
switch(message)
{
case WM_INITDIALOG:
{
LPOFNINFO lpOFNInfo = (LPOFNINFO)((LPOPENFILENAME)lParam)->lCustData;
// Store the OFN struct pointer as a window property
SetProp( hwnd, c_szOFNProp, (HANDLE)lpOFNInfo );
// Initialize the OFNInfo struct
_tcscpy( lpOFNInfo->szLastDirName, lpOFNInfo->ofn.lpstrInitialDir );
lpOFNInfo->fAllowSetText = FALSE;
//lpOFNInfo->fNetworking = FALSE;
// Start our edit off with the initial directory
SetDlgItemText( hwnd, IDC_FILENAME, lpOFNInfo->ofn.lpstrInitialDir );
// Subclass the edit to let us display only what we want to display
g_pfnPrevEditProc = (WNDPROC)SetWindowLongPtr(
GetDlgItem(hwnd, IDC_FILENAME),
GWLP_WNDPROC,
(LONG_PTR)BrowseDirEditProc );
// Subclass the combo so we know when the Network button has been used.
g_pfnPrevComboProc = (WNDPROC)SetWindowLongPtr(
GetDlgItem(hwnd, IDC_DRIVECOMBO),
GWLP_WNDPROC,
(LONG_PTR)BrowseDirComboProc );
return TRUE; // set default focus
}
case WM_DESTROY:
{
// Clean up.
RemoveProp( hwnd, c_szOFNProp );
return FALSE; // continue with default processing
}
case WM_COMMAND:
{
switch( LOWORD(wParam) )
{
case IDOK:
{
return BrowseDir_OnOK( hwnd );
}
case IDC_FILELIST: //directory listbox.
{
if( HIWORD(wParam) == LBN_DBLCLK )
{
// Post this message telling us to change the edit box.
PostMessage( hwnd, CM_UPDATEEDIT, 0, 0L );
}
return FALSE; // continue with default processing.
}
case IDC_DRIVECOMBO: // drive combo box:
{
if( HIWORD(wParam) == CBN_SELCHANGE )
{
// Post this message telling us to change the edit box.
PostMessage( hwnd, CM_UPDATEEDIT, 0, 0L );
}
return FALSE; // continue with default processing.
}
#ifdef NEVER
case IDC_NETWORK: // "Network..." button
{
// Set the fNetworking flag which the combo box looks for when
// processing CB_SETCURSEL.
LPOFNINFO lpOFNInfo = (LPOFNINFO)GetProp( hwnd, c_szOFNProp );
lpOFNInfo->fNetworking = TRUE;
return FALSE; // default processing.
}
#endif
default:
return FALSE; // default processing.
}
}
case CM_UPDATEEDIT: //update edit box with directory
{
LPOFNINFO lpOFNInfo = (LPOFNINFO)GetProp( hwnd, c_szOFNProp );
// Change the text of the edit control.
UpdateEditText( hwnd );
SendDlgItemMessage( hwnd, IDC_FILENAME, EM_SETSEL, 0, (LPARAM)(INT)-1 );
// Store this text as the "last known good" directory
GetDlgItemText( hwnd, IDC_FILENAME, lpOFNInfo->szLastDirName, _MAX_PATH );
return TRUE;
}
default:
return FALSE; //default processing
}
}
//----------------------------------------------------------------------------
// Procedure BrowseDir_OnOK
//
// Purpose Handles a click of the OK button in the Browse Directory
// dialog. We have to do some sneaky stuff here with checking
// which control has focus, because we want the dialog to go
// away enter when the OK button is clicked directly (i.e. just
// hitting Enter doesn't count).
//
// Parameters hwnd The dialog window
//
// Returns TRUE if processing should halt, FALSE if default processing
// should still happen.
//
// History 10/09/95 KenSh Created
//
BOOL BrowseDir_OnOK( HWND hwnd )
{
LPOFNINFO lpOFNInfo = (LPOFNINFO)GetProp( hwnd, c_szOFNProp );
HWND hwndFocus = GetFocus();
int idFocus = GetDlgCtrlID(hwndFocus);
if( idFocus != IDC_FILENAME && idFocus != IDC_FILELIST && idFocus != IDC_DRIVECOMBO )
{
ASSERT( lpOFNInfo->ofn.lpstrFile != NULL );
//UpdateEditText( hwnd );
GetDlgItemText( hwnd, IDC_FILENAME, lpOFNInfo->ofn.lpstrFile, lpOFNInfo->ofn.nMaxFile );
EndDialog( hwnd, IDOK );
return TRUE; // halt processing here.
}
else
{
// We don't want the dialog to go away at this point.
// Because the default functionality is expecting
// a file to be found, not a directory, we must make
// sure what's been typed in is actually a directory
// before we hand this message to the default handler.
TCHAR szBuf[_MAX_PATH];
// Calculate the new current directory and put it into the Edit
UpdateEditText( hwnd );
// Read the newly generated directory name
GetDlgItemText( hwnd, IDC_FILENAME, szBuf, _MAX_PATH );
// Update our "last good" directory
_tcscpy( lpOFNInfo->szLastDirName, szBuf );
// Post this message to update the edit after the default handler
// updates the list box
PostMessage( hwnd, CM_UPDATEEDIT, 0, 0 );
return FALSE; // let the original common dialog handle it.
}
}
BOOL BrowseForFile(
HWND hwndParent,
LPCTSTR pszInitialDir,
LPTSTR pszBuf,
int cchBuf)
{
TCHAR szInitialDir[MAX_PATH];
TCHAR szFileExtension[_MAX_PATH] = _T("");
LPTSTR p = NULL;
TCHAR szFileFilter[_MAX_PATH];
CString csTitle;
OFNINFO ofnInfo;
// Prepare the initial directory... add a backslash if it's
// a 2-character path
_tcscpy( szInitialDir, pszInitialDir );
if( !szInitialDir[2] )
{
szInitialDir[2] = _T('\\');
szInitialDir[3] = _T('\0');
}
// calculate file extension
p = _tcsrchr(pszBuf, _T('.'));
if (p) {
p = _tcsinc(p);
if (*p) {
_tcscpy(szFileExtension, _T("*."));
_tcscat(szFileExtension, p);
}
}
memset( (PVOID)szFileFilter, 0, sizeof(szFileFilter));
p = szFileFilter;
if (*szFileExtension) {
_tcscpy(p, szFileExtension);
p = _tcsninc(p, _tcslen(szFileExtension) + 1);
_tcscpy(p, szFileExtension);
p = _tcsninc(p, _tcslen(szFileExtension) + 1);
}
_tcscpy(p, _T("*.*"));
p = _tcsninc(p, _tcslen(_T("*.*")) + 1);
_tcscpy(p, _T("*.*"));
// dialog title "Locate File"
MyLoadString(IDS_LOCATE_FILE, csTitle);
// fill in the OFNINFO struct
ofnInfo.ofn.lStructSize = sizeof(OPENFILENAME);
ofnInfo.ofn.hwndOwner = hwndParent;
ofnInfo.ofn.hInstance = (HINSTANCE) g_MyModuleHandle;
ofnInfo.ofn.lpstrFilter = szFileFilter; // extention of the file we're looking for
ofnInfo.ofn.lpstrCustomFilter = NULL;
ofnInfo.ofn.nMaxCustFilter = 0;
ofnInfo.ofn.nFilterIndex = 1;
ofnInfo.ofn.lpstrFile = pszBuf; // Buffer for file name
ofnInfo.ofn.nMaxFile = cchBuf;
ofnInfo.ofn.lpstrFileTitle = NULL;
ofnInfo.ofn.nMaxFileTitle = 0;
ofnInfo.ofn.lpstrInitialDir = szInitialDir;
ofnInfo.ofn.lpstrTitle = (LPCTSTR)csTitle;
ofnInfo.ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_EXPLORER |
OFN_NOCHANGEDIR | OFN_LONGNAMES | OFN_NONETWORKBUTTON;
ofnInfo.ofn.nFileOffset = 0;
ofnInfo.ofn.nFileExtension = 0;
ofnInfo.ofn.lpstrDefExt = NULL;
ofnInfo.ofn.lCustData = (LPARAM)&ofnInfo;
ofnInfo.ofn.lpfnHook = NULL;
ofnInfo.ofn.lpTemplateName = NULL;
int nResult = ::GetOpenFileName( &ofnInfo.ofn );
DWORD dw = 0;
if (nResult == 0) {
dw = CommDlgExtendedError();
}
if (nResult == IDOK) {
iisDebugOut((LOG_TYPE_TRACE, _T("pszBuf=%s\n"), pszBuf));
*(pszBuf + ofnInfo.ofn.nFileOffset - 1) = _T('\0');
}
return (BOOL)( IDOK == nResult );
}