|
|
// WizDir.cpp : implementation file
//
#include "stdafx.h"
#include "WizDir.h"
#include <shlobj.h>
#include "icanon.h"
#include <macfile.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__; #endif
void OpenBrowseDialog(IN HWND hwndParent, IN LPCTSTR lpszComputer, OUT LPTSTR lpszDir);
BOOL IsValidLocalAbsolutePath( IN LPCTSTR lpszPath );
BOOL VerifyDirectory( IN LPCTSTR lpszServer, IN LPCTSTR lpszDir );
/////////////////////////////////////////////////////////////////////////////
// CWizFolder property page
IMPLEMENT_DYNCREATE(CWizFolder, CPropertyPageEx)
CWizFolder::CWizFolder() : CPropertyPageEx(CWizFolder::IDD, 0, IDS_HEADERTITLE_FOLDER, IDS_HEADERSUBTITLE_FOLDER) { //{{AFX_DATA_INIT(CWizFolder)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_psp.dwFlags |= PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; }
CWizFolder::~CWizFolder() { }
void CWizFolder::DoDataExchange(CDataExchange* pDX) { CPropertyPageEx::DoDataExchange(pDX); //{{AFX_DATA_MAP(CWizFolder)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CWizFolder, CPropertyPageEx) //{{AFX_MSG_MAP(CWizFolder)
ON_BN_CLICKED(IDC_BROWSEFOLDER, OnBrowsefolder) //}}AFX_MSG_MAP
ON_MESSAGE(WM_SETPAGEFOCUS, OnSetPageFocus) END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CWizFolder message handlers
BOOL CWizFolder::OnInitDialog() { CPropertyPageEx::OnInitDialog();
CShrwizApp *pApp = (CShrwizApp *)AfxGetApp();
SetDlgItemText(IDC_COMPUTER, pApp->m_cstrTargetComputer);
GetDlgItem(IDC_FOLDER)->SendMessage(EM_LIMITTEXT, _MAX_DIR - 1, 0); return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
LRESULT CWizFolder::OnWizardNext() { CWaitCursor wait; Reset(); // init all related place holders
CShrwizApp *pApp = (CShrwizApp *)AfxGetApp();
CString cstrFolder; GetDlgItemText(IDC_FOLDER, cstrFolder); cstrFolder.TrimLeft(); cstrFolder.TrimRight(); if (cstrFolder.IsEmpty()) { CString cstrField; cstrField.LoadString(IDS_FOLDER_LABEL); DisplayMessageBox(::GetActiveWindow(), MB_OK|MB_ICONERROR, 0, IDS_TEXT_REQUIRED, cstrField); GetDlgItem(IDC_FOLDER)->SetFocus(); return -1; }
// Removing the ending backslash, otherwise, GetFileAttribute/NetShareAdd will fail.
int iLen = cstrFolder.GetLength(); if (cstrFolder[iLen - 1] == _T('\\') && cstrFolder[iLen - 2] != _T(':')) cstrFolder.SetAt(iLen - 1, _T('\0'));
if (!IsValidLocalAbsolutePath(cstrFolder)) { DisplayMessageBox(::GetActiveWindow(), MB_OK|MB_ICONERROR, 0, IDS_INVALID_FOLDER); GetDlgItem(IDC_FOLDER)->SetFocus(); return -1; }
//
// need to exclude reserved MS-DOS device name
//
if (pApp->m_pfnIsDosDeviceName) { LPTSTR pszPath = const_cast<LPTSTR>(static_cast<LPCTSTR>(cstrFolder)); LPTSTR pszStart = pszPath + 3;
ULONG ulRet = 0; if (*pszStart) { TCHAR *pchCurrent = NULL; TCHAR *pchNext = NULL;
while (0 == (ulRet = pApp->m_pfnIsDosDeviceName(pszPath))) { pchNext = _tcsrchr(pszStart, _T('\\'));
if (pchCurrent) *pchCurrent = _T('\\');
if (!pchNext) break;
pchCurrent = pchNext; *pchNext = _T('\0'); }
if (0 != ulRet && pchCurrent) *pchCurrent = _T('\\'); }
if (0 != ulRet) { DisplayMessageBox(::GetActiveWindow(), MB_OK|MB_ICONERROR, 0, IDS_ISDOSDEVICENAME); GetDlgItem(IDC_FOLDER)->SetFocus(); return -1; } }
if (!VerifyDirectory(pApp->m_cstrTargetComputer, cstrFolder)) { GetDlgItem(IDC_FOLDER)->SetFocus(); return -1; }
pApp->m_cstrFolder = cstrFolder;
return CPropertyPageEx::OnWizardNext(); }
void CWizFolder::OnBrowsefolder() { CShrwizApp *pApp = (CShrwizApp *)AfxGetApp(); LPTSTR lpszComputer = const_cast<LPTSTR>(static_cast<LPCTSTR>(pApp->m_cstrTargetComputer)); CString cstrPath; TCHAR szDir[MAX_PATH * 2] = _T(""); // double the size in case the remote path is itself close to MAX_PATH
OpenBrowseDialog(m_hWnd, lpszComputer, szDir); if (szDir[0]) { if (pApp->m_bIsLocal) cstrPath = szDir; else { // szDir is in the form of \\server\share or \\server\share\path....
LPTSTR pShare = _tcschr(szDir + 2, _T('\\')); pShare++; LPTSTR pLeftOver = _tcschr(pShare, _T('\\')); if (pLeftOver && *pLeftOver) *pLeftOver++ = _T('\0');
SHARE_INFO_2 *psi = NULL; if (NERR_Success == NetShareGetInfo(lpszComputer, pShare, 2, (LPBYTE *)&psi)) { cstrPath = psi->shi2_path; if (pLeftOver && *pLeftOver) { if (_T('\\') != cstrPath.Right(1)) cstrPath += _T('\\'); cstrPath += pLeftOver; } NetApiBufferFree(psi); } } }
if (!cstrPath.IsEmpty()) SetDlgItemText(IDC_FOLDER, cstrPath); }
BOOL CWizFolder::OnSetActive() { CShrwizApp *pApp = (CShrwizApp *)AfxGetApp();
((CPropertySheet *)GetParent())->SetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT);
if (!pApp->m_bFolderPathPageInitialized) { // in re-run case, reset button behaviors that have been introduced by the last page
GetParent()->SetDlgItemText(ID_WIZNEXT, pApp->m_cstrNextButtonText); GetParent()->GetDlgItem(ID_WIZBACK)->ShowWindow(SW_SHOW); GetParent()->GetDlgItem(IDCANCEL)->EnableWindow(TRUE);
SetDlgItemText(IDC_FOLDER, pApp->m_cstrFolder);
pApp->m_bFolderPathPageInitialized = TRUE; }
BOOL fRet = CPropertyPageEx::OnSetActive();
PostMessage(WM_SETPAGEFOCUS, 0, 0L);
return fRet; }
//
// Q148388 How to Change Default Control Focus on CPropertyPageEx
//
LRESULT CWizFolder::OnSetPageFocus(WPARAM wParam, LPARAM lParam) { GetDlgItem(IDC_FOLDER)->SetFocus(); return 0; }
void CWizFolder::Reset() { CShrwizApp *pApp = (CShrwizApp *)AfxGetApp();
pApp->m_cstrFolder.Empty(); }
////////////////////////////////////////////////////////////
// OpenBrowseDialog
//
//
// 7/11/2001 LinanT bug#426953
// Since connection made by Terminal Service may bring some client side resources
// (disks, serial ports, etc.) into "My Computer" namespace, we want to disable
// the OK button when browsing to a non-local folder. We don't have this problem
// when browsing a remote machine.
//
#define DISK_ENTRY_LENGTH 4 // Drive letter, colon, whack, NULL
#define DISK_NAME_LENGTH 2 // Drive letter, colon
//
// This function determines if pszDir sits on any of
// the local logical drives.
// Contents in pszLocalDrives look like: c:\<null>d:\<null><null>
//
BOOL InDiskList(IN LPCTSTR pszDir, IN TCHAR *pszLocalDrives) { if (!pszDir || !*pszDir || !pszLocalDrives || !*pszLocalDrives) return FALSE;
DWORD i = 0; PTSTR pszDisk = pszLocalDrives; while (*pszDisk) { if (!_tcsnicmp(pszDisk, pszDir, DISK_NAME_LENGTH)) return TRUE;
pszDisk += DISK_ENTRY_LENGTH; }
return FALSE; }
int CALLBACK BrowseCallbackProc( IN HWND hwnd, IN UINT uMsg, IN LPARAM lp, IN LPARAM pData ) { switch(uMsg) { case BFFM_SELCHANGED: { // enable the OK button if the selected path is local to that computer.
BOOL bEnableOK = FALSE; TCHAR szDir[MAX_PATH]; if (SHGetPathFromIDList((LPITEMIDLIST) lp ,szDir)) { if (pData) { // we're looking at a local computer, verify if szDir is on a local disk
bEnableOK = InDiskList(szDir, (TCHAR *)pData); } else { // no such problem when browsing at a remote computer, always enable OK button.
bEnableOK = TRUE; } } SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM)bEnableOK); break; } case BFFM_VALIDATEFAILED: { DisplayMessageBox(hwnd, MB_OK|MB_ICONERROR, 0, IDS_BROWSE_FOLDER_INVALID); return 1; } default: break; }
return 0; }
//
// Since the buffer contents looks like c:\<null>d:\<null><null>,
// we're defining the buffer length to be 4*26+1.
//
#define LOGICAL_DRIVES_BUFFER_LENGTH (4 * 26 + 1)
//
// This function retrieves logical drive letters, filters out
// remote drives, and returns drive letters on the local machine
// in the form of: c:\<null>d:\<null><null>
//
HRESULT GetLocalLogicalDriveStrings ( UINT nCharsInBuffer, // number of total tchars in the buffer, including the terminating null char
PTSTR pszBuffer ) { HRESULT hr = S_OK; TCHAR szLocalDrives[LOGICAL_DRIVES_BUFFER_LENGTH]; DWORD nChars = GetLogicalDriveStrings( LOGICAL_DRIVES_BUFFER_LENGTH - 1, // in TCHARs, this size does NOT include the terminating null char.
szLocalDrives); //
// MSDN:
// If the function above succeeds, the return value is the length,
// in characters, of the strings copied to the buffer, not including
// the terminating null character.
// If the function fails, the return value is zero.
//
if (0 == nChars) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { if ((nChars + 1 ) > nCharsInBuffer) { hr = E_INVALIDARG; // treat small buffer as invalid parameter
} else { ZeroMemory(pszBuffer, nCharsInBuffer * sizeof(TCHAR));
PTSTR pszDrive = szLocalDrives; while (*pszDrive) { if (DRIVE_REMOTE != GetDriveType(pszDrive)) { lstrcpyn(pszBuffer, pszDrive, DISK_ENTRY_LENGTH); pszBuffer += DISK_ENTRY_LENGTH; }
pszDrive += DISK_ENTRY_LENGTH; } } }
return hr; }
void OpenBrowseDialog(IN HWND hwndParent, IN LPCTSTR lpszComputer, OUT LPTSTR lpszDir) { ASSERT(lpszComputer && *lpszComputer);
HRESULT hr = S_OK;
TCHAR szLocalDrives[LOGICAL_DRIVES_BUFFER_LENGTH]; ZeroMemory(szLocalDrives, sizeof(szLocalDrives));
CString cstrComputer; if (*lpszComputer != _T('\\') || *(lpszComputer + 1) != _T('\\')) { cstrComputer = _T("\\\\"); cstrComputer += lpszComputer; } else { cstrComputer = lpszComputer; }
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (SUCCEEDED(hr)) { LPMALLOC pMalloc; hr = SHGetMalloc(&pMalloc); if (SUCCEEDED(hr)) { LPSHELLFOLDER pDesktopFolder; hr = SHGetDesktopFolder(&pDesktopFolder); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlRoot; if (IsLocalComputer(lpszComputer)) { hr = SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidlRoot); if (SUCCEEDED(hr)) { //
// 7/11/2001 LinanT bug#426953
// Since connection made by Terminal Service may bring some client side resources
// (disks, serial ports, etc.) into "My Computer" namespace, we want to disable
// the OK button when browsing to a non-local folder. We don't have this problem
// when browsing a remote machine.
//
//
// Get an array of local disk names, this information is later used
// in the browse dialog to disable OK button if non-local path is selected.
//
// bug#714842: to work around the problem that NetServerDiskEnum
// requires admin privilege, we call GetLogicalDriveStrings and
// filter out remote drives.
//
hr = GetLocalLogicalDriveStrings( LOGICAL_DRIVES_BUFFER_LENGTH, // in TCHARs, including the terminating null char.
szLocalDrives); } } else { hr = pDesktopFolder->ParseDisplayName(hwndParent, NULL, const_cast<LPTSTR>(static_cast<LPCTSTR>(cstrComputer)), NULL, &pidlRoot, NULL); } if (SUCCEEDED(hr)) { CString cstrLabel; cstrLabel.LoadString(IDS_BROWSE_FOLDER);
BROWSEINFO bi; ZeroMemory(&bi,sizeof(bi)); bi.hwndOwner = hwndParent; bi.pszDisplayName = 0; bi.lpszTitle = cstrLabel; bi.pidlRoot = pidlRoot; bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_SHAREABLE | BIF_USENEWUI | BIF_VALIDATE; bi.lpfn = BrowseCallbackProc; if (szLocalDrives[0]) bi.lParam = (LPARAM)szLocalDrives; // pass the structure to the browse dialog
LPITEMIDLIST pidl = SHBrowseForFolder(&bi); if (pidl) { SHGetPathFromIDList(pidl, lpszDir); pMalloc->Free(pidl); } pMalloc->Free(pidlRoot); } pDesktopFolder->Release(); } pMalloc->Release(); }
CoUninitialize(); }
if (FAILED(hr)) DisplayMessageBox(::GetActiveWindow(), MB_OK|MB_ICONWARNING, hr, IDS_CANNOT_BROWSE_FOLDER, lpszComputer); }
BOOL IsValidLocalAbsolutePath( IN LPCTSTR lpszPath ) { DWORD dwPathType = 0; DWORD dwStatus = I_NetPathType( NULL, const_cast<LPTSTR>(lpszPath), &dwPathType, 0); if (dwStatus) return FALSE;
if (dwPathType ^ ITYPE_PATH_ABSD) return FALSE;
return TRUE; }
//+---------------------------------------------------------------------------
//
// Function: IsAnExistingFolder
//
// Synopsis: Check if pszPath is pointing at an existing folder.
//
// S_OK: The specified path points to an existing folder.
// S_FALSE: The specified path doesn't point to an existing folder.
// hr: Failed to get info on the specified path, or
// the path exists but doesn't point to a folder.
// The function reports error msg for both failures if desired.
//----------------------------------------------------------------------------
HRESULT IsAnExistingFolder( IN HWND hwnd, IN LPCTSTR pszPath, IN BOOL bDisplayErrorMsg ) { if (!hwnd) hwnd = GetActiveWindow();
HRESULT hr = S_OK; DWORD dwRet = GetFileAttributes(pszPath);
if (-1 == dwRet) { DWORD dwErr = GetLastError(); if (ERROR_PATH_NOT_FOUND == dwErr || ERROR_FILE_NOT_FOUND == dwErr) { // the specified path doesn't exist
hr = S_FALSE; } else { hr = HRESULT_FROM_WIN32(dwErr);
if (ERROR_NOT_READY == dwErr) { // fix for bug#358033/408803: ignore errors from GetFileAttributes in order to
// allow the root of movable drives to be shared without media inserted in.
int len = _tcslen(pszPath); if (len > 3 && pszPath[len - 1] == _T('\\') && pszPath[len - 2] == _T(':')) { // pszPath is pointing at the root of the drive, ignore the error
hr = S_OK; } }
if ( FAILED(hr) && bDisplayErrorMsg ) DisplayMessageBox(hwnd, MB_OK, dwErr, IDS_FAILED_TO_GETINFO_FOLDER, pszPath); } } else if ( 0 == (dwRet & FILE_ATTRIBUTE_DIRECTORY) ) { // the specified path is not pointing to a folder
if (bDisplayErrorMsg) DisplayMessageBox(hwnd, MB_OK|MB_ICONERROR, 0, IDS_PATH_NOT_FOLDER, pszPath); hr = E_FAIL; }
return hr; }
// create the directories layer by layer
HRESULT CreateLayeredDirectory( IN LPCTSTR lpszServer, IN LPCTSTR lpszDir ) { ASSERT(IsValidLocalAbsolutePath(lpszDir));
BOOL bLocal = IsLocalComputer(lpszServer);
CString cstrFullPath; GetFullPath(lpszServer, lpszDir, cstrFullPath);
// add prefix to skip the CreateDirectory limit of 248 chars
CString cstrFullPathNoParsing = (bLocal ? _T("\\\\?\\") : _T("\\\\?\\UNC")); cstrFullPathNoParsing += (bLocal ? cstrFullPath : cstrFullPath.Right(cstrFullPath.GetLength() - 1));
HRESULT hr = IsAnExistingFolder(NULL, cstrFullPathNoParsing, FALSE); ASSERT(S_FALSE == hr);
LPTSTR pch = _tcschr(cstrFullPathNoParsing, (bLocal ? _T(':') : _T('$'))); ASSERT(pch);
// pszPath holds "\\?\C:\a\b\c\d" or "\\?\UNC\server\share\a\b\c\d"
// pszLeft holds "a\b\c\d"
LPTSTR pszPath = const_cast<LPTSTR>(static_cast<LPCTSTR>(cstrFullPathNoParsing)); LPTSTR pszLeft = pch + 2; LPTSTR pszRight = NULL;
ASSERT(pszLeft && *pszLeft);
//
// this loop will find out the 1st non-existing sub-dir to create, and
// the rest of non-existing sub-dirs
//
while (pch = _tcsrchr(pszLeft, _T('\\'))) // backwards search for _T('\\')
{ *pch = _T('\0'); hr = IsAnExistingFolder(NULL, pszPath, TRUE); if (FAILED(hr)) return S_FALSE; // errormsg has already been reported by IsAnExistingFolder().
if (S_OK == hr) { //
// pszPath is pointing to the parent dir of the 1st non-existing sub-dir.
// Once we restore the _T('\\'), pszPath will point at the 1st non-existing subdir.
//
*pch = _T('\\'); break; } else { //
// pszPath is pointing to a non-existing folder, continue with the loop.
//
if (pszRight) *(pszRight - 1) = _T('\\'); pszRight = pch + 1; } }
// We're ready to create directories:
// pszPath points to the 1st non-existing dir, e.g., "C:\a\b" or "\\server\share\a\b"
// pszRight points to the rest of non-existing sub dirs, e.g., "c\d"
//
do { if (!CreateDirectory(pszPath, NULL)) return HRESULT_FROM_WIN32(GetLastError());
if (!pszRight || !*pszRight) break;
*(pszRight - 1) = _T('\\'); if (pch = _tcschr(pszRight, _T('\\'))) // forward search for _T('\\')
{ *pch = _T('\0'); pszRight = pch + 1; } else { pszRight = NULL; } } while (1);
return S_OK; }
BOOL VerifyDirectory( IN LPCTSTR lpszServer, IN LPCTSTR lpszDir ) { ASSERT(lpszDir && *lpszDir); ASSERT(IsValidLocalAbsolutePath(lpszDir));
HWND hwnd = ::GetActiveWindow();
BOOL bLocal = IsLocalComputer(lpszServer); HRESULT hr = VerifyDriveLetter(lpszServer, lpszDir); if (FAILED(hr)) { /*
// fix for bug#351212: ignore error and leave permission checkings to NetShareAdd apis
DisplayMessageBox(hwnd, MB_OK, hr, IDS_FAILED_TO_VALIDATE_FOLDER, lpszDir); return FALSE; */ hr = S_OK; } else if (S_OK != hr) { DisplayMessageBox(hwnd, MB_OK|MB_ICONERROR, 0, IDS_INVALID_DRIVE, lpszDir); return FALSE; }
// warn if user has choosen to share out the whole volume
if (3 == lstrlen(lpszDir) && _T(':') == lpszDir[1] && _T('\\') == lpszDir[2]) { if (IDNO == DisplayMessageBox(hwnd, MB_YESNO|MB_DEFBUTTON2|MB_ICONWARNING, 0, IDS_WARNING_WHOLE_VOLUME, lpszDir)) return FALSE; }
if (!bLocal) { hr = IsAdminShare(lpszServer, lpszDir); if (FAILED(hr)) { DisplayMessageBox(hwnd, MB_OK|MB_ICONERROR, hr, IDS_FAILED_TO_VALIDATE_FOLDER, lpszDir); return FALSE; } else if (S_OK != hr) { // there is no matching $ shares, hence, no need to call GetFileAttribute, CreateDirectory,
// assume lpszDir points to an existing directory
return TRUE; } }
CString cstrPath; GetFullPath(lpszServer, lpszDir, cstrPath);
// add prefix to skip the GetFileAttribute limit when the path is on a remote server
CString cstrPathNoParsing = (bLocal ? _T("\\\\?\\") : _T("\\\\?\\UNC")); cstrPathNoParsing += (bLocal ? cstrPath : cstrPath.Right(cstrPath.GetLength() - 1));
hr = IsAnExistingFolder(hwnd, cstrPathNoParsing, TRUE); // error has already been reported.
if (FAILED(hr) || S_OK == hr) return (S_OK == hr);
if ( IDYES != DisplayMessageBox(hwnd, MB_YESNO|MB_ICONQUESTION, 0, IDS_CREATE_NEW_DIR, cstrPath) ) return FALSE;
// create the directories layer by layer
hr = CreateLayeredDirectory(lpszServer, lpszDir); if (FAILED(hr)) DisplayMessageBox(hwnd, MB_OK|MB_ICONERROR, hr, IDS_FAILED_TO_CREATE_NEW_DIR, cstrPath);
return (S_OK == hr); }
|