summaryrefslogtreecommitdiff
path: root/zen/IFileOperation/file_op.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zen/IFileOperation/file_op.cpp')
-rw-r--r--zen/IFileOperation/file_op.cpp541
1 files changed, 0 insertions, 541 deletions
diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp
deleted file mode 100644
index 27a2565b..00000000
--- a/zen/IFileOperation/file_op.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
-// **************************************************************************
-
-#include "file_op.h"
-#include <algorithm>
-#include <string>
-#include <vector>
-
-#define WIN32_LEAN_AND_MEAN
-#include <zen/com_ptr.h>
-#include <zen/com_error.h>
-#include <zen/scope_guard.h>
-#include <zen/stl_tools.h>
-#include <zen/file_handling.h>
-
-#include <boost/thread/tss.hpp>
-
-#include <RestartManager.h>
-#pragma comment(lib, "Rstrtmgr.lib")
-
-#define STRICT_TYPED_ITEMIDS //better type safety for IDLists
-#include <Shlobj.h>
-#include <shobjidl.h>
-#include <shellapi.h> //shell constants such as FO_* values
-
-using namespace zen;
-
-
-namespace
-{
-std::vector<std::wstring> getLockingProcesses(const wchar_t* filename); //throw SysError
-
-
-class RecyclerProgressCallback : public IFileOperationProgressSink
-{
- //Sample implementation: %ProgramFiles%\Microsoft SDKs\Windows\v7.1\Samples\winui\shell\appplatform\FileOperationProgressSink
-
- ~RecyclerProgressCallback() {} //private: do not allow stack usage "thanks" to IUnknown lifetime management!
-
-public:
- RecyclerProgressCallback(fileop::RecyclerCallback callback, void* sink) :
- cancellationRequested(false),
- callback_(callback),
- sink_(sink),
- refCount(1) {}
-
- //IUnknown: reference implementation according to: http://msdn.microsoft.com/en-us/library/office/cc839627.aspx
- virtual ULONG STDMETHODCALLTYPE AddRef()
- {
- return ::InterlockedIncrement(&refCount);
- }
-
- virtual ULONG STDMETHODCALLTYPE Release()
- {
- ULONG newRefCount = ::InterlockedDecrement(&refCount);
- if (newRefCount == 0) //race condition caveat: do NOT check refCount, which might have changed already!
- delete this;
- return newRefCount;
- }
-
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR* __RPC_FAR* ppvObject)
- {
- if (!ppvObject)
- return E_INVALIDARG;
-
- if (riid == IID_IUnknown || riid == IID_IFileOperationProgressSink)
- {
- *ppvObject = this;
- AddRef();
- return S_OK;
- }
- *ppvObject = NULL;
- return E_NOINTERFACE;
- }
-
- //IFileOperationProgressSink
- virtual HRESULT STDMETHODCALLTYPE StartOperations() { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE FinishOperations(HRESULT hrResult) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PreRenameItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PostRenameItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_string LPCWSTR pszNewName, HRESULT hrRename, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PreMoveItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PostMoveItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrMove, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PreCopyItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PostCopyItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiItem, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, HRESULT hrCopy, __RPC__in_opt IShellItem* psiNewlyCreated) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PreNewItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName) { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PostNewItem (DWORD dwFlags, __RPC__in_opt IShellItem* psiDestinationFolder, __RPC__in_opt_string LPCWSTR pszNewName, __RPC__in_opt_string LPCWSTR pszTemplateName, DWORD dwFileAttributes, HRESULT hrNew, __RPC__in_opt IShellItem* psiNewItem) { return S_OK; }
-
- virtual HRESULT STDMETHODCALLTYPE PreDeleteItem(DWORD dwFlags, __RPC__in_opt IShellItem* psiItem)
- {
- if (psiItem)
- {
- LPWSTR itemPath = nullptr;
- if (SUCCEEDED(psiItem->GetDisplayName(SIGDN_FILESYSPATH, &itemPath))) //will fail for long file paths > MAX_PATH!
- {
- ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(itemPath));
- currentItem = itemPath;
- }
- else if (SUCCEEDED(psiItem->GetDisplayName(SIGDN_NORMALDISPLAY, &itemPath))) //short name only; should work even for long file paths!
- {
- ZEN_ON_SCOPE_EXIT(::CoTaskMemFree(itemPath));
- currentItem = itemPath;
- }
- else
- currentItem = L"<unknown file>"; //give some indication that file name determination failed (rather than leaving the name empty!)
- }
- //"Returns S_OK if successful, or an error value otherwise. In the case of an error value, the delete operation
- //and all subsequent operations pending from the call to IFileOperation are canceled."
- return cancellationRequested ? HRESULT_FROM_WIN32(ERROR_CANCELLED) : S_OK;
- }
-
- virtual HRESULT STDMETHODCALLTYPE PostDeleteItem(DWORD dwFlags,
- __RPC__in_opt IShellItem* psiItem,
- HRESULT hrDelete,
- __RPC__in_opt IShellItem* psiNewlyCreated)
- {
- if (FAILED(hrDelete))
- lastError = make_unique<std::pair<std::wstring, HRESULT>>(currentItem, hrDelete);
-
- currentItem.clear();
- //"Returns S_OK if successful, or an error value otherwise. In the case of an error value,
- //all subsequent operations pending from the call to IFileOperation are canceled."
- return cancellationRequested ? HRESULT_FROM_WIN32(ERROR_CANCELLED) : S_OK;
- }
-
- virtual HRESULT STDMETHODCALLTYPE UpdateProgress(UINT iWorkTotal, UINT iWorkSoFar)
- {
- if (callback_)
- try
- {
- if (!callback_(currentItem.c_str(), sink_)) //should not throw!
- cancellationRequested = true;
- }
- catch (...) { return E_UNEXPECTED; }
- //"If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code."
- //-> this probably means, we cannot rely on returning a custom error code here and have IFileOperation::PerformOperations() fail with same
- //=> defer cancellation to PreDeleteItem()/PostDeleteItem()
- return S_OK;
- }
- virtual HRESULT STDMETHODCALLTYPE ResetTimer () { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE PauseTimer () { return S_OK; }
- virtual HRESULT STDMETHODCALLTYPE ResumeTimer() { return S_OK; }
-
- //call after IFileOperation::PerformOperations()
- const std::pair<std::wstring, HRESULT>* getLastError() const { return lastError.get(); } //(file path, error code)
-
-private:
- std::wstring currentItem;
- bool cancellationRequested;
-
- std::unique_ptr<std::pair<std::wstring, HRESULT>> lastError;
-
- //file_op user callback
- fileop::RecyclerCallback callback_;
- void* sink_;
-
- //support IUnknown
- LONG refCount;
-};
-
-
-void moveToRecycleBin(const wchar_t* fileNames[], //throw SysError
- size_t fileCount,
- fileop::RecyclerCallback callback,
- void* sink)
-{
- ComPtr<IFileOperation> fileOp;
- ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw SysError
- nullptr,
- CLSCTX_ALL,
- IID_PPV_ARGS(fileOp.init())));
-
- // Set the operation flags. Turn off all UI from being shown to the user during the
- // operation. This includes error, confirmation and progress dialogs.
- ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_ALLOWUNDO |
- FOF_NOCONFIRMATION |
- FOF_SILENT | //no progress dialog box
- FOF_NOERRORUI |
- FOFX_EARLYFAILURE |
- //without FOFX_EARLYFAILURE, IFileOperationProgressSink::PostDeleteItem() will always report success, even if deletion failed!!? WTF!?
- //PerformOperations() will still succeed but set the uselessly generic GetAnyOperationsAborted() instead :(((
- //=> always set FOFX_EARLYFAILURE since we prefer good error messages over "doing as much as possible"
- //luckily for FreeFileSync we don't expect failures on individual files anyway: FreeFileSync moves files to be
- //deleted to a temporary folder first, so there is no reason why a second move (the recycling itself) should fail
- FOF_NO_CONNECTED_ELEMENTS));
- //use FOFX_RECYCLEONDELETE when Windows 8 is available!?
-
- ComPtr<RecyclerProgressCallback> opProgress;
- *opProgress.init() = new (std::nothrow) RecyclerProgressCallback(callback, sink);
- if (!opProgress)
- throw SysError(formatComError(L"Error creating RecyclerProgressCallback.", E_OUTOFMEMORY));
-
- DWORD callbackID = 0;
- ZEN_COM_CHECK(fileOp->Advise(opProgress.get(), &callbackID));
- ZEN_ON_SCOPE_EXIT(fileOp->Unadvise(callbackID)); //RecyclerProgressCallback might outlive current scope, so cut access to "callback, sink"
-
- int operationCount = 0;
-
- for (size_t i = 0; i < fileCount; ++i)
- {
- //SHCreateItemFromParsingName() physically checks file existence => callback
- if (callback)
- {
- bool continueExecution = false;
- try
- {
- continueExecution = callback(fileNames[i], sink); //should not throw!
- }
- catch (...) { throw SysError(formatComError(L"Unexpected exception in callback.", E_UNEXPECTED)); }
-
- if (!continueExecution)
- throw SysError(formatComError(L"Operation cancelled.", HRESULT_FROM_WIN32(ERROR_CANCELLED)));
- }
-
- //create file/folder item object
- ComPtr<IShellItem> psiFile;
- HRESULT hr = ::SHCreateItemFromParsingName(fileNames[i],
- nullptr,
- IID_PPV_ARGS(psiFile.init()));
- if (FAILED(hr))
- {
- if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || //file not existing anymore
- hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
- {
- //make sure the file really is not there: Win32 by default strips trailing spaces, so we might end up here in error!
- //on the other hand, shell layer does not support \\?\ prefix to prevent this!
- if (!somethingExists(fileNames[i]))
- continue;
- }
- throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\"" + fileNames[i] + L"\".", hr));
- }
-
- ZEN_COM_CHECK(fileOp->DeleteItem(psiFile.get(), nullptr));
-
- ++operationCount;
- }
-
- if (operationCount == 0) //calling PerformOperations() without anything to do would yield E_UNEXPECTED
- return;
-
- //perform planned operations
- try
- {
- ZEN_COM_CHECK(fileOp->PerformOperations());
- }
- catch (const SysError&)
- {
- //first let's check if we have more detailed error information available
- if (const std::pair<std::wstring, HRESULT>* lastError = opProgress->getLastError())
- {
- std::vector<std::wstring> processes; //create an even better error message if we detect a locking issue:
- try { processes = getLockingProcesses(lastError->first.c_str()); /*throw SysError*/ }
- catch (const SysError&) {}
-
- if (!processes.empty())
- {
- std::wstring errorMsg = L"The file \"" + lastError->first + L"\" is locked by another process:";
- std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { errorMsg += L'\n'; errorMsg += proc; });
- throw SysError(errorMsg); //message is descriptive enough, no need to evaluate HRESULT!
- }
- throw SysError(formatComError(std::wstring(L"Error during \"PerformOperations\" for file:\n") + L"\"" + lastError->first + L"\".", lastError->second));
- }
- throw;
- }
-
- //if FOF_NOERRORUI without FOFX_EARLYFAILURE is set, PerformOperations() can return with success despite errors, but sets the following "aborted" flag instead
- BOOL pfAnyOperationsAborted = FALSE;
- ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
-
- if (pfAnyOperationsAborted == TRUE)
- throw SysError(L"Operation did not complete successfully.");
-}
-
-
-void copyFile(const wchar_t* sourceFile, //throw SysError
- const wchar_t* targetFile)
-{
- ComPtr<IFileOperation> fileOp;
- ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw SysError
- nullptr,
- CLSCTX_ALL,
- IID_PPV_ARGS(fileOp.init())));
-
- // Set the operation flags. Turn off all UI
- // from being shown to the user during the
- // operation. This includes error, confirmation
- // and progress dialogs.
- ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw SysError
- FOF_SILENT |
- FOFX_EARLYFAILURE |
- FOF_NOERRORUI));
- //create source object
- ComPtr<IShellItem> psiSourceFile;
- {
- HRESULT hr = ::SHCreateItemFromParsingName(sourceFile,
- nullptr,
- IID_PPV_ARGS(psiSourceFile.init()));
- if (FAILED(hr))
- throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\"" + sourceFile + L"\".", hr));
- }
-
- const size_t pos = std::wstring(targetFile).find_last_of(L'\\');
- if (pos == std::wstring::npos)
- throw SysError(L"Target filename does not contain a path separator.");
-
- const std::wstring targetFolder(targetFile, pos);
- const std::wstring targetFileNameShort = targetFile + pos + 1;
-
- //create target folder object
- ComPtr<IShellItem> psiTargetFolder;
- {
- HRESULT hr = ::SHCreateItemFromParsingName(targetFolder.c_str(),
- nullptr,
- IID_PPV_ARGS(psiTargetFolder.init()));
- if (FAILED(hr))
- throw SysError(formatComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for folder:\n") + L"\"" + targetFolder + L"\".", hr));
- }
-
- //schedule file copy operation
- ZEN_COM_CHECK(fileOp->CopyItem(psiSourceFile.get(), psiTargetFolder.get(), targetFileNameShort.c_str(), nullptr));
-
- //perform actual operations
- ZEN_COM_CHECK(fileOp->PerformOperations());
-
- //check if errors occured: if FOFX_EARLYFAILURE is not used, PerformOperations() can return with success despite errors!
- BOOL pfAnyOperationsAborted = FALSE;
- ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
-
- if (pfAnyOperationsAborted == TRUE)
- throw SysError(L"Operation did not complete successfully.");
-}
-
-
-void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw SysError
-{
- ComPtr<IShellFolder> desktopFolder;
- ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw SysError
-
- PIDLIST_RELATIVE pidlFolder = nullptr;
- ZEN_COM_CHECK(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd,
- nullptr, // [in] IBindCtx *pbc,
- const_cast<LPWSTR>(dirname), // [in] LPWSTR pszDisplayName,
- nullptr, // [out] ULONG *pchEaten,
- &pidlFolder, // [out] PIDLIST_RELATIVE* ppidl,
- nullptr)); // [in, out] ULONG *pdwAttributes
- ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree
-
- ComPtr<IPersist> persistFolder;
- ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl,
- nullptr, // [in] IBindCtx *pbc,
- IID_PPV_ARGS(persistFolder.init()))); //throw SysError
-
- ZEN_COM_CHECK(persistFolder->GetClassID(&pathCLSID)); //throw SysError
-}
-
-
-std::vector<std::wstring> getLockingProcesses(const wchar_t* filename) //throw SysError
-{
- DWORD sessionHandle = 0;
- {
- wchar_t sessionKey[CCH_RM_SESSION_KEY + 1] = {}; //fixes two bugs: http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx
- DWORD rv1 = ::RmStartSession(&sessionHandle, //__out DWORD *pSessionHandle,
- 0, //__reserved DWORD dwSessionFlags,
- sessionKey); //__out WCHAR strSessionKey[ ]
- if (rv1 != ERROR_SUCCESS)
- throw SysError(formatSystemError(L"RmStartSession", rv1));
- }
- ZEN_ON_SCOPE_EXIT(::RmEndSession(sessionHandle));
-
- {
- DWORD rv2 = ::RmRegisterResources(sessionHandle, //__in DWORD dwSessionHandle,
- 1, //__in UINT nFiles,
- &filename, //__in_opt LPCWSTR rgsFilenames[ ],
- 0, //__in UINT nApplications,
- nullptr, //__in_opt RM_UNIQUE_PROCESS rgApplications[ ],
- 0, //__in UINT nServices,
- nullptr); //__in_opt LPCWSTR rgsServiceNames[ ]
- if (rv2 != ERROR_SUCCESS)
- throw SysError(formatSystemError(L"RmRegisterResources", rv2));
- }
-
- std::vector<RM_PROCESS_INFO> procInfo;
- {
- UINT procInfoSize = 0;
- UINT procInfoSizeNeeded = 0;
- DWORD rebootReasons = 0;
- DWORD rv3 = ::RmGetList(sessionHandle, &procInfoSizeNeeded, &procInfoSize, nullptr, &rebootReasons); //get procInfoSizeNeeded
- if (rv3 == ERROR_SUCCESS)
- return std::vector<std::wstring>();
- if (rv3 != ERROR_MORE_DATA)
- throw SysError(formatSystemError(L"RmGetList", rv3));
- //C:\pagefile.sys fails with ERROR_SHARING_VIOLATION!
-
- if (procInfoSizeNeeded == 0)
- return std::vector<std::wstring>();
-
- procInfoSize = procInfoSizeNeeded;
- procInfo.resize(procInfoSizeNeeded);
-
- rv3 = ::RmGetList(sessionHandle, //__in DWORD dwSessionHandle,
- &procInfoSizeNeeded, //__out UINT *pnProcInfoNeeded,
- &procInfoSize, //__inout UINT *pnProcInfo,
- &procInfo[0], //__inout_opt RM_PROCESS_INFO rgAffectedApps[ ],
- &rebootReasons); //__out LPDWORD lpdwRebootReasons
- if (rv3 != ERROR_SUCCESS)
- throw SysError(formatSystemError(L"RmGetList", rv3));
- procInfo.resize(procInfoSize);
- }
-
- std::vector<std::wstring> output;
- for (auto iter = procInfo.begin(); iter != procInfo.end(); ++iter)
- {
- std::wstring processName = iter->strAppName;
-
- //try to get process path
- HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, //__in DWORD dwDesiredAccess,
- false, //__in BOOL bInheritHandle,
- iter->Process.dwProcessId); //__in DWORD dwProcessId
- if (hProcess)
- {
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hProcess));
-
- FILETIME creationTime = {};
- FILETIME exitTime = {};
- FILETIME kernelTime = {};
- FILETIME userTime = {};
- if (::GetProcessTimes(hProcess, //__in HANDLE hProcess,
- &creationTime, //__out LPFILETIME lpCreationTime,
- &exitTime, //__out LPFILETIME lpExitTime,
- &kernelTime, //__out LPFILETIME lpKernelTime,
- &userTime)) //__out LPFILETIME lpUserTime
- if (::CompareFileTime(&iter->Process.ProcessStartTime, &creationTime) == 0)
- {
- DWORD bufferSize = MAX_PATH;
- std::vector<wchar_t> buffer(bufferSize);
- if (::QueryFullProcessImageName(hProcess, //__in HANDLE hProcess,
- 0, //__in DWORD dwFlags,
- &buffer[0], //__out LPTSTR lpExeName,
- &bufferSize)) //__inout PDWORD lpdwSize
- if (bufferSize < buffer.size())
- processName += std::wstring(L", ") + L"\"" + &buffer[0] + L"\"";
- }
- }
- output.push_back(processName);
- }
- return output;
-}
-
-
-boost::thread_specific_ptr<std::wstring> lastErrorMessage; //use "thread_local" in C++11
-}
-
-
-bool fileop::moveToRecycleBin(const wchar_t* fileNames[],
- size_t fileCount,
- RecyclerCallback callback,
- void* sink)
-{
- try
- {
- ::moveToRecycleBin(fileNames, fileCount, callback, sink); //throw SysError
- return true;
- }
- catch (const SysError& e)
- {
- lastErrorMessage.reset(new std::wstring(e.toString()));
- return false;
- }
-}
-
-
-bool fileop::copyFile(const wchar_t* sourceFile,
- const wchar_t* targetFile)
-{
- try
- {
- ::copyFile(sourceFile, targetFile); //throw SysError
- return true;
- }
- catch (const SysError& e)
- {
- lastErrorMessage.reset(new std::wstring(e.toString()));
- return false;
- }
-}
-
-
-bool fileop::checkRecycler(const wchar_t* dirname, bool& isRecycler)
-{
- try
- {
- CLSID clsid = {};
- getFolderClsid(dirname, clsid); //throw SysError
- isRecycler = ::IsEqualCLSID(clsid, CLSID_RecycleBin) == TRUE; //silence perf warning
- return true;
- }
- catch (const SysError& e)
- {
- lastErrorMessage.reset(new std::wstring(e.toString()));
- return false;
- }
-}
-
-
-const wchar_t* fileop::getLastError()
-{
- return !lastErrorMessage.get() ? L"" : lastErrorMessage->c_str();
-}
-
-
-bool fileop::getLockingProcesses(const wchar_t* filename, const wchar_t*& procList)
-{
- try
- {
- std::vector<std::wstring> processes = ::getLockingProcesses(filename); //throw SysError
-
- std::wstring buffer;
- std::for_each(processes.begin(), processes.end(), [&](const std::wstring& proc) { buffer += proc; buffer += L'\n'; });
- if (!processes.empty())
- buffer.resize(buffer.size() - 1); //remove last line break
-
- auto tmp = new wchar_t [buffer.size() + 1]; //bad_alloc ?
- ::wmemcpy(tmp, buffer.c_str(), buffer.size() + 1); //include 0-termination
- procList = tmp; //ownership passed
-
- return true;
- }
- catch (const SysError& e)
- {
- lastErrorMessage.reset(new std::wstring(e.toString()));
- return false;
- }
-}
-
-
-void fileop::freeString(const wchar_t* str)
-{
- delete [] str;
-}
bgstack15