📄 shellfileop.cpp
字号:
//////////////////////////////////////////////////////////////////////////
void CShellFileOp::Reset()
{
ResetInternalData();
}
//////////////////////////////////////////////////////////////////////
// The Go() function!
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// Function: CShellFileOp::Go
//
// Description:
// Validates data and starts a file operation.
//
// Input:
// lpbOperationStarted: [out] Pointer to a BOOL that receives TRUE if the
// SHFileOperation() API was called to start the
// operation, or FALSE if the API was not called.
// lpnAPIReturn: [out] Pointer to an int that receives the return value from
// SHFileOperation() if it was called. If the API is not called,
// the variable pointed to is not changed.
// lpbAnyOperationsAborted: [out] Pointer to a BOOL that receives TRUE if the
// user aborted the file operation, or FALSE if not.
//
// Returns:
// TRUE if and only if SHFileOperation() was called and it returned 0 (success).
//
//////////////////////////////////////////////////////////////////////////
// Updated in v1.1 - Changed the two 'new' calls to allocate BYTEs insetad
// of TCHARs.
//////////////////////////////////////////////////////////////////////////
BOOL CShellFileOp::Go ( BOOL* lpbOperationStarted,
int* lpnAPIReturn /*=NULL*/,
BOOL* lpbAnyOperationsAborted /*=NULL*/ )
{
TCHAR* szzSourceFiles = NULL;
TCHAR* szzDestFiles = NULL;
DWORD dwSourceBufferSize;
DWORD dwDestBufferSize;
int nAPIRet;
// Validate the pointers....
ASSERT ( AfxIsValidAddress ( lpbOperationStarted, sizeof(BOOL) ) );
ASSERT ( lpnAPIReturn == NULL ||
AfxIsValidAddress ( lpnAPIReturn, sizeof(int) ) );
ASSERT ( lpbAnyOperationsAborted == NULL ||
AfxIsValidAddress ( lpbAnyOperationsAborted, sizeof(BOOL) ) );
m_bGoCalledAPI = FALSE;
if ( NULL != lpbOperationStarted )
{
*lpbOperationStarted = FALSE;
}
// Do a bunch of validation before
// calling the API.
// 1. Did you call SetOperationFlags()?
if ( ! m_bFlagsSet )
{
TRACE0("Go() aborting because SetOperationFlags() was not called first.\n");
goto bailout;
}
// 2 Is the op type valid?
if ( ! ( m_rFOS.wFunc == FO_COPY || m_rFOS.wFunc == FO_DELETE ||
m_rFOS.wFunc == FO_MOVE || m_rFOS.wFunc == FO_RENAME ) )
{
TRACE0("Go() aborting because the operation type was invalid.\n");
goto bailout;
}
// 3 Is the source file list nonempty?
if ( m_lcstrSourceFiles.IsEmpty() )
{
TRACE0("Go() aborting because the source file list is empty.\n");
goto bailout;
}
// 4. Is the dest file list nonempty
// if the op needs dest files?
if ( m_rFOS.wFunc != FO_DELETE && m_lcstrDestFiles.IsEmpty() )
{
TRACE0("Go() aborting because the destination file list is empty.\n");
goto bailout;
}
// 5. Is the dest file list OK? There
// must either be one entry, or the same
// number of entries as in the source
// list.
if ( m_rFOS.wFunc != FO_DELETE )
{
if ( m_lcstrDestFiles.GetCount() != 1 &&
m_lcstrDestFiles.GetCount() != m_lcstrSourceFiles.GetCount() )
{
TRACE0("Go() aborting because the destination file list has the wrong number of strings.\n");
goto bailout;
}
}
// Everything checked out OK, so now
// build the big double-null-terminated
// buffers.
dwSourceBufferSize = GetRequiredBufferSize ( m_lcstrSourceFiles );
if ( m_rFOS.wFunc != FO_DELETE )
{
dwDestBufferSize = GetRequiredBufferSize ( m_lcstrDestFiles );
}
try{
szzSourceFiles = (LPTSTR) new BYTE [ dwSourceBufferSize ];
if ( m_rFOS.wFunc != FO_DELETE )
{
szzDestFiles = (LPTSTR) new BYTE [ dwDestBufferSize ];
}
}catch ( CMemoryException ){
TRACE0("Memory exception in CShellFileOp::Go()!\n");
throw;
}
FillSzzBuffer ( szzSourceFiles, m_lcstrSourceFiles );
if ( m_rFOS.wFunc != FO_DELETE )
{
FillSzzBuffer ( szzDestFiles, m_lcstrDestFiles );
}
// and now, the moment you've all been
// waiting for
m_rFOS.pFrom = szzSourceFiles;
m_rFOS.pTo = szzDestFiles;
m_rFOS.lpszProgressTitle = (LPCTSTR) m_cstrProgressDlgTitle;
// If there are 2 or more strings in
// the destination list, set the
// MULTIDESTFILES flag.
if ( m_lcstrDestFiles.GetCount() > 1 )
{
m_rFOS.fFlags |= FOF_MULTIDESTFILES;
}
m_bGoCalledAPI = TRUE;
if ( NULL != lpbOperationStarted )
{
*lpbOperationStarted = TRUE;
}
// drum roll please....
nAPIRet = SHFileOperation ( &m_rFOS ); // tah-dah!
// Save the return value from the API.
if ( NULL != lpnAPIReturn )
{
*lpnAPIReturn = nAPIRet;
}
// Check if the user cancelled the
// operation.
if ( NULL != lpbAnyOperationsAborted )
{
*lpbAnyOperationsAborted = m_rFOS.fAnyOperationsAborted;
}
bailout:
// If we got here via one of the gotos, fire off an assert.
// If this assert fires, check the debug window for a TRACE output
// line that describes the problem.
// ASSERT ( m_bGoCalledAPI );
// Free buffers.
if ( NULL != szzSourceFiles )
{
delete [] szzSourceFiles;
}
if ( NULL != szzDestFiles )
{
delete [] szzDestFiles;
}
return m_bGoCalledAPI && 0 == nAPIRet;
}
//////////////////////////////////////////////////////////////////////
// Private helper functions
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// Function: CShellFileOp::ResetInternalData
//
// Description:
// Clears the CShellFileOp object's member variables in preparation for a
// new file operation.
//
// Input:
// Nothing.
//
// Returns:
// Nothing.
//
//////////////////////////////////////////////////////////////////////////
void CShellFileOp::ResetInternalData()
{
// Empty the string lists
m_lcstrSourceFiles.RemoveAll();
m_lcstrDestFiles.RemoveAll();
// Reset state variables
m_bFlagsSet = FALSE;
m_bGoCalledAPI = FALSE;
// And clear out other stuff...
m_cstrProgressDlgTitle.Empty();
ZeroMemory ( &m_rFOS, sizeof ( m_rFOS ) );
}
//////////////////////////////////////////////////////////////////////////
//
// Function: CShellFileOp::GetRequiredBufferSize
//
// Description:
// Calculates the number of bytes required to hold the passed-in string
// list in double-null-terminated character array form.
//
// Input:
// list: [in] The string list to look at.
//
// Returns:
// The number of bytes required.
//
//////////////////////////////////////////////////////////////////////////
DWORD CShellFileOp::GetRequiredBufferSize ( const CStringList& list )
{
DWORD dwRetVal = 0;
POSITION pos;
CString cstr;
// If this assert fires, the passed-in list was empty. This ought to
// never fire, actually, since Go() won't even call this function if
// either list is empty.
ASSERT ( !list.IsEmpty() );
pos = list.GetHeadPosition();
while ( NULL != pos )
{
cstr = list.GetNext ( pos );
// **NOTE** The MFC docs for CString::GetLength() say that it returns
// the number of bytes in the string, but that's wrong!! In Unicode,
// it returns the number of characters (which is half the number of
// bytes). Thus the multiplication by sizeof(TCHAR).
dwRetVal += sizeof(TCHAR) * ( cstr.GetLength() + 1 );
}
return dwRetVal + sizeof(TCHAR); // add one more for the final null
}
//////////////////////////////////////////////////////////////////////////
//
// Function: CShellFileOp::FillSzzBuffer
//
// Description:
// Copies a string list into a character array, making a double-null-terminated
// list of strings.
//
// Input:
// pBuffer: [out] The buffer that will hold the strings.
// list: [in] The string list to read.
//
// Returns:
// Nothing.
//
//////////////////////////////////////////////////////////////////////////
void CShellFileOp::FillSzzBuffer ( TCHAR* pBuffer, const CStringList& list )
{
TCHAR* pCurrPos;
POSITION pos;
CString cstr;
// If this assert fires, the passed-in list was empty. This ought to
// never fire, actually, since Go() won't even call this function if
// either list is empty when it shouldn't be.
ASSERT ( !list.IsEmpty() );
ASSERT ( pBuffer != NULL );
pCurrPos = pBuffer;
pos = list.GetHeadPosition();
while ( NULL != pos )
{
cstr = list.GetNext ( pos );
_tcscpy ( pCurrPos, (LPCTSTR) cstr );
pCurrPos = _tcsinc ( _tcschr ( pCurrPos, '\0' ) );
}
// Tack on the final null
*pCurrPos = '\0';
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -