I'm in the process of porting a C++ library from Linux to Windows, and am having problems with getuid(), which is not supported in Windows.
Any ideas what I can use in its place?
The Windows equivilent is actually the user's SID. You can get this by using the "GetTokenInformation" call and querying for the TokenUser information class.
To call GetTokenInformation, you need a handle to the users token, which you can get by calling OpenProcessToken (or OpenThreadToken if you're impersonating someone).
You can retrieves the name of the user associated with the current thread with GetUserName :
// ANSI version
string GetWindowsUserNameA()
{
char buffer[UNLEN + 1] = {0};
DWORD buffer_len = UNLEN + 1;
if (!::GetUserNameA(buffer, & buffer_len))
{
// error handling
}
return string(buffer);
}
Windows' closest equivalent of a UID is (probably) a SID. GetUserName followed by LookupAccountName should get you the user's SID.
This is what I came up with.
#include <stdint.h>
#include <stdlib.h>
#include <Windows.h>
#include <sddl.h>
#include <iostream>
#include <iomanip>
#include <memory>
struct heap_delete
{
typedef LPVOID pointer;
void operator()(LPVOID p)
{
::HeapFree(::GetProcessHeap(), 0, p);
}
};
typedef std::unique_ptr<LPVOID, heap_delete> heap_unique_ptr;
struct handle_delete
{
typedef HANDLE pointer;
void operator()(HANDLE p)
{
::CloseHandle(p);
}
};
typedef std::unique_ptr<HANDLE, handle_delete> handle_unique_ptr;
typedef uint32_t uid_t;
BOOL GetUserSID(HANDLE token, PSID* sid)
{
if (
token == nullptr || token == INVALID_HANDLE_VALUE
|| sid == nullptr
)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
DWORD tokenInformationLength = 0;
::GetTokenInformation(
token, TokenUser, nullptr, 0, &tokenInformationLength);
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return FALSE;
}
heap_unique_ptr data(
::HeapAlloc(
::GetProcessHeap(), HEAP_ZERO_MEMORY,
tokenInformationLength));
if (data.get() == nullptr)
{
return FALSE;
}
BOOL getTokenInfo = ::GetTokenInformation(
token, TokenUser, data.get(),
tokenInformationLength, &tokenInformationLength);
if (! getTokenInfo)
{
return FALSE;
}
PTOKEN_USER pTokenUser = (PTOKEN_USER)(data.get());
DWORD sidLength = ::GetLengthSid(pTokenUser->User.Sid);
heap_unique_ptr sidPtr(
::HeapAlloc(
GetProcessHeap(), HEAP_ZERO_MEMORY, sidLength));
PSID sidL = (PSID)(sidPtr.get());
if (sidL == nullptr)
{
return FALSE;
}
BOOL copySid = ::CopySid(sidLength, sidL, pTokenUser->User.Sid);
if (! copySid)
{
return FALSE;
}
if (!IsValidSid(sidL))
{
return FALSE;
}
*sid = sidL;
sidPtr.release();
return TRUE;
}
uid_t GetUID(HANDLE token)
{
PSID sid = nullptr;
BOOL getSID = GetUserSID(token, &sid);
if (! getSID || ! sid)
{
return -1;
}
heap_unique_ptr sidPtr((LPVOID)(sid));
LPWSTR stringSid = nullptr;
BOOL convertSid = ::ConvertSidToStringSidW(
sid, &stringSid);
if (! convertSid)
{
return -1;
}
uid_t ret = -1;
LPCWSTR p = ::wcsrchr(stringSid, L'-');
if (p && ::iswdigit(p[1]))
{
++p;
ret = ::_wtoi(p);
}
::LocalFree(stringSid);
return ret;
}
uid_t getuid()
{
HANDLE process = ::GetCurrentProcess();
handle_unique_ptr processPtr(process);
HANDLE token = nullptr;
BOOL openToken = ::OpenProcessToken(
process, TOKEN_READ|TOKEN_QUERY_SOURCE, &token);
if (! openToken)
{
return -1;
}
handle_unique_ptr tokenPtr(token);
uid_t ret = GetUID(token);
return ret;
}
uid_t geteuid()
{
HANDLE process = ::GetCurrentProcess();
HANDLE thread = ::GetCurrentThread();
HANDLE token = nullptr;
BOOL openToken = ::OpenThreadToken(
thread, TOKEN_READ|TOKEN_QUERY_SOURCE, FALSE, &token);
if (! openToken && ::GetLastError() == ERROR_NO_TOKEN)
{
openToken = ::OpenThreadToken(
thread, TOKEN_READ|TOKEN_QUERY_SOURCE, TRUE, &token);
if (! openToken && ::GetLastError() == ERROR_NO_TOKEN)
{
openToken = ::OpenProcessToken(
process, TOKEN_READ|TOKEN_QUERY_SOURCE, &token);
}
}
if (! openToken)
{
return -1;
}
handle_unique_ptr tokenPtr(token);
uid_t ret = GetUID(token);
return ret;
}
int main()
{
uid_t uid = getuid();
uid_t euid = geteuid();
std::cout
<< "uid: " << std::setbase(10) << uid << std::endl
<< "euid: " << std::setbase(10) << euid << std::endl
<< std::endl;
return EXIT_SUCCESS;
}
Note that the answer given by Larry Osterman was very helpful. It got me started in the correct direction.
Check out Microsoft's recommendations on porting with the Interix (also known as Services for UNIX 3.0) library. Overkill for what you want though.
in DotNet - Environment.UserName
The right api is SHGetUID(), exported from Shell
Related
I'm trying to get the size of allocated are for a process, descriminated by image, private and mapped. I'm using QueryWorkingSet to get the Working Set Information and then the Working Set Block.
When i called it for the first time, the GetLastError method return 24 which is to be expected so the next time I call QueryWorkingSet I set a diferent size for the block but then im getting an error code of 998.
Am I using QueryWorkingSet wrong or Im getting the handle for the process with the wrong access rights or Im the resize is not enough?
#include "pch.h"
#include <Windows.h>
#include<WinDef.h>
#include <psapi.h>
#include <iostream>
typedef struct {
DWORD img;
DWORD map;
DWORD prv;
} CommitCounters, *PCommitCounters;
BOOL GetCommitCountersFromProcess(_In_ int pid, _Out_ PCommitCounters committedCounter ) {
int numberOfTries = 3;
SYSTEM_INFO si;
GetSystemInfo(&si);
DWORD pageSz = si.dwPageSize;
PSAPI_WORKING_SET_INFORMATION wsi, *pwsi;
pwsi = &wsi;
DWORD ws_size;
MEMORY_BASIC_INFORMATION mbi, *pmbi;
pmbi = &mbi;
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
wsi.NumberOfEntries = 0;
QueryWorkingSet(processHandle, &wsi, sizeof(pwsi));
BOOL res = false;
committedCounter->img = 0;
committedCounter->map = 0;
committedCounter->prv = 0;
while (numberOfTries > 0) {
DWORD lastError = GetLastError();
//ERROR_BAD_LENGTH
if (lastError == 24) {
ws_size = sizeof(wsi) + sizeof(PSAPI_WORKING_SET_INFORMATION) + sizeof(PSAPI_WORKING_SET_BLOCK) * wsi.NumberOfEntries;
pwsi = (PSAPI_WORKING_SET_INFORMATION*) malloc(ws_size);
pwsi->NumberOfEntries = wsi.NumberOfEntries;
BOOL resQws = QueryWorkingSet(processHandle, &wsi, ws_size);
DWORD teste = sizeof(wsi);
if (resQws) {
for (int i = 0; i < pwsi->NumberOfEntries; i++) {
PSAPI_WORKING_SET_BLOCK ws_block = pwsi->WorkingSetInfo[1];
//Access page information.
SIZE_T size = VirtualQuery((LPCVOID*)ws_block.VirtualPage, &mbi, 1);
if (size != 0 && pmbi ->State == 0x1000) {
switch (pmbi->Type)
{
case 0x1000000: // MEM_IMAGE
committedCounter->img += pageSz;
break;
case 0x40000: //MEM_MAPPED
committedCounter->map += pageSz;
break;
case 0x20000: //MEM_PRIVATE
committedCounter->prv += pageSz;
break;
}
}
else if (size == 0) {
return res;
}
}
CloseHandle(processHandle);
res = true;
return res;
}
free(pwsi);
}
numberOfTries--;
}
CloseHandle(processHandle);
return false;
}
You have a typo in your code. Just change:
BOOL resQws = QueryWorkingSet(processHandle, &wsi, ws_size);
to:
BOOL resQws = QueryWorkingSet(processHandle, pwsi, ws_size);
And then the call succeeds.
There may be further errors but I did not investigate those.
I am trying to setup the Interactive Brokers API on Ubuntu (18.04). I have installed both the IB Gateway, which is used for communicating with exchanges, as well as other API software for developing trading algorithms in Java, C++, C# and Python. (Which you can find here). The API is written in both Java and C++, and as stated prior it offers support for both. However when attempting to compile an example from their source code there is an error in the EReader.cpp file. I have dealt with several other C++ errors in their code however this one I cannot figure out. Here is the code:
#include "StdAfx.h"
#include "shared_ptr.h"
#include "Contract.h"
#include "EDecoder.h"
#include "EMutex.h"
#include "EReader.h"
#include "EClientSocket.h"
#include "EPosixClientSocketPlatform.h"
#include "EReaderSignal.h"
#include "EMessage.h"
#include "DefaultEWrapper.h"
#define IN_BUF_SIZE_DEFAULT 8192
static DefaultEWrapper defaultWrapper;
EReader::EReader(EClientSocket *clientSocket, EReaderSignal *signal)
: processMsgsDecoder_(clientSocket->EClient::serverVersion(), clientSocket->getWrapper(), clientSocket) {
m_isAlive = true;
m_pClientSocket = clientSocket;
m_pEReaderSignal = signal;
m_needsWriteSelect = false;
m_nMaxBufSize = IN_BUF_SIZE_DEFAULT;
m_buf.reserve(IN_BUF_SIZE_DEFAULT);
}
EReader::~EReader(void) {
m_isAlive = false;
#if defined(IB_WIN32)
WaitForSingleObject(m_hReadThread, INFINITE);
#endif
}
void EReader::checkClient() {
m_needsWriteSelect = !m_pClientSocket->getTransport()-
isOutBufferEmpty();
}
void EReader::start() {
#if defined(IB_POSIX)
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create( &thread, &attr, readToQueueThread, this );
pthread_attr_destroy(&attr);
#elif defined(IB_WIN32)
m_hReadThread = CreateThread(0, 0, readToQueueThread, this, 0, 0);
#else
# error "Not implemented on this platform"
#endif
}
#if defined(IB_POSIX)
void * EReader::readToQueueThread(void * lpParam)
#elif defined(IB_WIN32)
DWORD WINAPI EReader::readToQueueThread(LPVOID lpParam)
#else
# error "Not implemented on this platform"
#endif
{
EReader *pThis = reinterpret_cast<EReader *>(lpParam);
pThis->readToQueue();
return 0;
}
void EReader::readToQueue() {
EMessage *msg = 0;
while (m_isAlive) {
if (m_buf.size() == 0 && !processNonBlockingSelect() && m_pClientSocket->isSocketOK())
continue;
if (!putMessageToQueue())
break;
}
m_pClientSocket->handleSocketError();
m_pEReaderSignal->issueSignal(); //letting client know that socket was closed
}
bool EReader::putMessageToQueue() {
EMessage *msg = 0;
if (m_pClientSocket->isSocketOK())
msg = readSingleMsg();
if (msg == 0)
return false;
m_csMsgQueue.Enter();
m_msgQueue.push_back(ibapi::shared_ptr<EMessage>(msg));
m_csMsgQueue.Leave();
m_pEReaderSignal->issueSignal();
return true;
}
bool EReader::processNonBlockingSelect() {
fd_set readSet, writeSet, errorSet;
struct timeval tval;
tval.tv_usec = 100 * 1000; //100 ms
tval.tv_sec = 0;
if( m_pClientSocket->fd() >= 0 ) {
FD_ZERO( &readSet);
errorSet = writeSet = readSet;
FD_SET( m_pClientSocket->fd(), &readSet);
if (m_needsWriteSelect)
FD_SET( m_pClientSocket->fd(), &writeSet);
FD_SET( m_pClientSocket->fd(), &errorSet);
int ret = select( m_pClientSocket->fd() + 1, &readSet, &writeSet, &errorSet, &tval);
if( ret == 0) { // timeout
return false;
}
if( ret < 0) { // error
m_pClientSocket->eDisconnect();
return false;
}
if( m_pClientSocket->fd() < 0)
return false;
if( FD_ISSET( m_pClientSocket->fd(), &errorSet)) {
// error on socket
m_pClientSocket->onError();
}
if( m_pClientSocket->fd() < 0)
return false;
if( FD_ISSET( m_pClientSocket->fd(), &writeSet)) {
// socket is ready for writing
onSend();
}
if( m_pClientSocket->fd() < 0)
return false;
if( FD_ISSET( m_pClientSocket->fd(), &readSet)) {
// socket is ready for reading
onReceive();
}
return true;
}
return false;
}
void EReader::onSend() {
m_pEReaderSignal->issueSignal();
}
void EReader::onReceive() {
int nOffset = m_buf.size();
m_buf.resize(m_nMaxBufSize);
int nRes = m_pClientSocket->receive(m_buf.data() + nOffset, m_buf.size() - nOffset);
if (nRes <= 0)
return;
m_buf.resize(nRes + nOffset);
}
bool EReader::bufferedRead(char *buf, int size) {
while (size > 0) {
while (m_buf.size() < size && m_buf.size() < m_nMaxBufSize)
if (!processNonBlockingSelect() && !m_pClientSocket->isSocketOK())
return false;
int nBytes = (std::min<unsigned int>)(m_nMaxBufSize, size);
std::copy(m_buf.begin(), m_buf.begin() + nBytes, buf);
std::copy(m_buf.begin() + nBytes, m_buf.end(), m_buf.begin());
m_buf.resize(m_buf.size() - nBytes);
size -= nBytes;
buf += nBytes;
}
return true;
}
EMessage * EReader::readSingleMsg() {
if (m_pClientSocket->usingV100Plus()) {
int msgSize;
if (!bufferedRead((char *)&msgSize, sizeof(msgSize)))
return 0;
msgSize = htonl(msgSize);
if (msgSize <= 0 || msgSize > MAX_MSG_LEN)
return 0;
std::vector<char> buf = std::vector<char>(msgSize);
if (!bufferedRead(buf.data(), buf.size()))
return 0;
return new EMessage(buf);
}
else {
const char *pBegin = 0;
const char *pEnd = 0;
int msgSize = 0;
while (msgSize == 0)
{
if (m_buf.size() >= m_nMaxBufSize * 3/4)
m_nMaxBufSize *= 2;
if (!processNonBlockingSelect() && !m_pClientSocket->isSocketOK())
return 0;
pBegin = m_buf.data();
pEnd = pBegin + m_buf.size();
msgSize = EDecoder(m_pClientSocket->EClient::serverVersion(), &defaultWrapper).parseAndProcessMsg(pBegin, pEnd);
}
std::vector<char> msgData(msgSize);
if (!bufferedRead(msgData.data(), msgSize))
return 0;
if (m_buf.size() < IN_BUF_SIZE_DEFAULT && m_buf.capacity() > IN_BUF_SIZE_DEFAULT)
{
m_buf.resize(m_nMaxBufSize = IN_BUF_SIZE_DEFAULT);
m_buf.shrink_to_fit();
}
EMessage * msg = new EMessage(msgData);
return msg;
}
}
ibapi::shared_ptr<EMessage> EReader::getMsg(void) {
m_csMsgQueue.Enter();
if (m_msgQueue.size() == 0) {
m_csMsgQueue.Leave();
return ibapi::shared_ptr<EMessage>();
}
ibapi::shared_ptr<EMessage> msg = m_msgQueue.front();
m_msgQueue.pop_front();
m_csMsgQueue.Leave();
return msg;
}
void EReader::processMsgs(void) {
m_pClientSocket->onSend();
checkClient();
ibapi::shared_ptr<EMessage> msg = getMsg();
if (!msg.get())
return;
const char *pBegin = msg->begin();
while (processMsgsDecoder_.parseAndProcessMsg(pBegin, msg->end()) > 0)
{
msg = getMsg();
if (!msg.get())
break;
pBegin = msg->begin();
}
}
The error I get it is the following:
error: 'min' was not declared in this scope int nBytes =
min(m_nMaxBuffSize, size);
I have had to do other things such as editing other source code and makefiles, I am stuck here. Any insight would be appreciated.
In my version 973 source at that line I have
int nBytes = (std::min<unsigned int>)(m_nMaxBufSize, size);
Make sure you are using the latest version. The problem may be an example of what happens here Why is "using namespace std" considered bad practice?
I have a working code that returns all files in all sub-directories given a directory with a wild card. For example: "C://*". This works perfectly fine. Now, I was wondering if it is possible to recursively iterate over all files using a certain file extension. For example: "C://*.png", without altering the code. I was looking for sth like: "C://*/../*.png", but I could not find a solution. Are there any wildcard tricks I could use?
And here is an example using FindFirstFile and FindNextFile that can recursively find files having a given extension.
#include "stdafx.h"
#include <Windows.h>
#include <atlpath.h>
#include <list>
#include <iostream>
#ifdef _UNICODE
#define cout wcout
#endif
void FindFiles(
const CString& strRootPath,
const CString& strExt,
std::list<CString>& listFiles,
bool bRecursive = true)
{
CString strFileToFind = strRootPath;
ATLPath::Append(CStrBuf(strFileToFind, MAX_PATH), _T("*.*"));
WIN32_FIND_DATA findData = { 0 };
HANDLE hFileFind = ::FindFirstFile(strFileToFind, &findData);
if (INVALID_HANDLE_VALUE != hFileFind)
{
do
{
CString strFileName = findData.cFileName;
if ((strFileName == _T(".")) || (strFileName == _T("..")))
continue;
CString strFilePath = strRootPath;
ATLPath::Append(CStrBuf(strFilePath, MAX_PATH), strFileName);
if (bRecursive && (ATLPath::IsDirectory(strFilePath)))
{
FindFiles(strFilePath, strExt, listFiles);
}
else
{
CString strFoundExt = ATLPath::FindExtension(strFilePath);
if (! strExt.CompareNoCase(strFoundExt))
istFiles.push_back(strFilePath);
}
} while (::FindNextFile(hFileFind, &findData));
::FindClose(hFileFind);
}
}
int main()
{
std::list<CString> listFiles;
FindFiles(_T("e:\\tests"), _T(".cpp"), listFiles);
for (const auto& strFile : listFiles)
std::cout << strFile.GetString() << std::endl;
return 0;
}
Note: to make things easier, I've used ATL stuff like ATL::CString and ATLPath functions. There is no problem to use them in a Win32 or Console application.
You need to search recursively for each subdirectory. I happen to have some code to do this, the following code might help.
#include <functional>
#include <io.h>
enum enumflags {
ENUM_FILE = 1,
ENUM_DIR,
ENUM_BOTH
};
//return value:
// False means that the searching has been aborted by the callback function.
// It will return true otherwise.
bool enumsubfiles(
const std::wstring &dir_with_back_slant, //for example: L"C:\\", L"E:\\test\\"
const std::wstring &filename, //for example: L"123.txt", L"*.exe", L"123.???"
unsigned int maxdepth, //0 means not searching subdirectories, 1 means maximum depth of subdirectories is 1,
// pass -1 to search all the subdirectories.
enumflags flags, //search files, directories, or both.
std::function<bool(const std::wstring &dir_with_back_slant, _wfinddata_t &attrib)> callback
)
{
_wfinddata_t dat;
size_t hfile;
std::wstring fullname = dir_with_back_slant + filename;
std::wstring tmp;
bool ret = true;
hfile = _wfindfirst(fullname.c_str(), &dat);
if (hfile == -1) goto a;
do {
if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
if (((dat.attrib&_A_SUBDIR) && (!(flags&ENUM_DIR))) || ((!(dat.attrib&_A_SUBDIR)) && (!(flags&ENUM_FILE)))) continue;
ret = callback(dir_with_back_slant, dat);
if (!ret) {
_findclose(hfile);
return ret;
}
} while (_wfindnext(hfile, &dat) == 0);
_findclose(hfile);
a:
if (!maxdepth) return ret;
tmp = dir_with_back_slant + L"*";
hfile = _wfindfirst(tmp.c_str(), &dat);
if (hfile == -1) return ret;
do {
if (!(wcscmp(L".", dat.name) && wcscmp(L"..", dat.name))) continue;
if (!(dat.attrib&_A_SUBDIR)) continue;
tmp = dir_with_back_slant + dat.name + L"\\";
ret = enumsubfiles(tmp, filename, maxdepth - 1, flags, callback);
if (!ret) {
_findclose(hfile);
return ret;
}
} while (_wfindnext(hfile, &dat) == 0);
_findclose(hfile);
return ret;
}
Here is an example of the usage of the function above:
int _tmain(int argc, _TCHAR* argv[])
{
using namespace std;
//the default code page of my console window is 936
setlocale(CP_ACP, ".936");
enumsubfiles(L"C:\\", L"*.exe", 1, ENUM_FILE, [](const std::wstring &dir_with_back_slant, _wfinddata_t &attrib)->bool
{
std::wcout << dir_with_back_slant << attrib.name << '\n';
return true; //return true to continue, return false to abort searching.
});
return 0;
}
And you will get the following output:
C:\OpenSSL-Win64\unins000.exe
C:\putty\PAGEANT.EXE
C:\putty\PLINK.EXE
C:\putty\PSCP.EXE
C:\putty\PSFTP.EXE
C:\putty\PUTTY.EXE
C:\putty\PUTTYGEN.EXE
C:\Windows\ampa.exe
C:\Windows\bfsvc.exe
C:\Windows\explorer.exe
C:\Windows\HelpPane.exe
C:\Windows\hh.exe
C:\Windows\notepad.exe
C:\Windows\regedit.exe
C:\Windows\RtCRU64.exe
C:\Windows\SECOH-QAD.exe
C:\Windows\splwow64.exe
C:\Windows\winhlp32.exe
C:\Windows\write.exe
C:\测试\测试.exe
The maxdepth I passed to the function is 1. Pass -1 to search all the subdirectories.
I'm trying to remove directories using this code, but this wouldn't work. And, I couldn't found the issue. Maybe lack of privileges on the deletion operation on Windows?
Here is the code that I'm using:
#define _CRT_SECURE_NO_DEPRECATE
#include <string>
#include <iostream>
#include <windows.h>
#include <conio.h>
int DeleteDirectory(const std::string &refcstrRootDirectory,
bool bDeleteSubdirectories = true)
{
bool bSubdirectory = false; // Flag, indicating whether
// subdirectories have been found
HANDLE hFile; // Handle to directory
std::string strFilePath; // Filepath
std::string strPattern; // Pattern
WIN32_FIND_DATA FileInformation; // File information
strPattern = refcstrRootDirectory + "\\*.*";
hFile = ::FindFirstFile((LPCWSTR)strPattern.c_str(), &FileInformation);
if (hFile != INVALID_HANDLE_VALUE)
{
do
{
if (FileInformation.cFileName[0] != '.')
{
strFilePath.erase();
strFilePath = refcstrRootDirectory + "\\" + (char*)FileInformation.cFileName;
if (FileInformation.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if (bDeleteSubdirectories)
{
// Delete subdirectory
int iRC = DeleteDirectory(strFilePath, bDeleteSubdirectories);
if (iRC)
return iRC;
}
else
bSubdirectory = true;
}
else
{
// Set file attributes
if (::SetFileAttributes((LPCWSTR)strFilePath.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete file
if (::DeleteFile((LPCTSTR)strFilePath.c_str()) == FALSE)
return ::GetLastError();
}
}
} while (::FindNextFile(hFile, &FileInformation) == TRUE);
// Close handle
::FindClose(hFile);
DWORD dwError = ::GetLastError();
if (dwError != ERROR_NO_MORE_FILES)
return dwError;
else
{
if (!bSubdirectory)
{
// Set directory attributes
if (::SetFileAttributes((LPCWSTR)refcstrRootDirectory.c_str(),
FILE_ATTRIBUTE_NORMAL) == FALSE)
return ::GetLastError();
// Delete directory
if (::RemoveDirectory((LPCWSTR)refcstrRootDirectory.c_str()) == FALSE)
return ::GetLastError();
}
}
}
return 0;
}
int main()
{
int iRC = 0;
std::string strDirectoryToDelete = "C:\\Users\\AbysCo\\Desktop\\del";
// Delete 'c:\mydir' without deleting the subdirectories
iRC = DeleteDirectory(strDirectoryToDelete, false);
if (iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Delete 'c:\mydir' and its subdirectories
iRC = DeleteDirectory(strDirectoryToDelete);
if (iRC)
{
std::cout << "Error " << iRC << std::endl;
return -1;
}
// Wait for keystroke
_getch();
return 0;
}
OS: Windows 7 SP1.
Any brilliant idea, please?
The Windows API already comes with a function to delete entire directories. That is SHFileOperation. And on Vista or later you use IFileOperation for the same purpose.
Not only does this make the process trivial, not much more than a one liner, but it has other benefits:
You can put the deleted directory into the recycle bin if you choose.
You can show the standard system progress dialog if you choose.
We need to programatically burn files to CD in a C\C++ Windows XP/Vista application we are developing using Borlands Turbo C++.
What is the simplest and best way to do this? We would prefer a native windows API (that doesnt rely on MFC) so as not to rely on any third party software/drivers if one is available.
We used the following:
Store files in the directory returned by GetBurnPath, then write using Burn. GetCDRecordableInfo is used to check when the CD is ready.
#include <stdio.h>
#include <imapi.h>
#include <windows.h>
struct MEDIAINFO {
BYTE nSessions;
BYTE nLastTrack;
ULONG nStartAddress;
ULONG nNextWritable;
ULONG nFreeBlocks;
};
//==============================================================================
// Description: CD burning on Windows XP
//==============================================================================
#define CSIDL_CDBURN_AREA 0x003b
SHSTDAPI_(BOOL) SHGetSpecialFolderPathA(HWND hwnd, LPSTR pszPath, int csidl, BOOL fCreate);
SHSTDAPI_(BOOL) SHGetSpecialFolderPathW(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate);
#ifdef UNICODE
#define SHGetSpecialFolderPath SHGetSpecialFolderPathW
#else
#define SHGetSpecialFolderPath SHGetSpecialFolderPathA
#endif
//==============================================================================
// Interface IDiscMaster
const IID IID_IDiscMaster = {0x520CCA62,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
const CLSID CLSID_MSDiscMasterObj = {0x520CCA63,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
typedef interface ICDBurn ICDBurn;
// Interface ICDBurn
const IID IID_ICDBurn = {0x3d73a659,0xe5d0,0x4d42,{0xaf,0xc0,0x51,0x21,0xba,0x42,0x5c,0x8d}};
const CLSID CLSID_CDBurn = {0xfbeb8a05,0xbeee,0x4442,{0x80,0x4e,0x40,0x9d,0x6c,0x45,0x15,0xe9}};
MIDL_INTERFACE("3d73a659-e5d0-4d42-afc0-5121ba425c8d")
ICDBurn : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetRecorderDriveLetter(
/* [size_is][out] */ LPWSTR pszDrive,
/* [in] */ UINT cch) = 0;
virtual HRESULT STDMETHODCALLTYPE Burn(
/* [in] */ HWND hwnd) = 0;
virtual HRESULT STDMETHODCALLTYPE HasRecordableDrive(
/* [out] */ BOOL *pfHasRecorder) = 0;
};
//==============================================================================
// Description: Get burn pathname
// Parameters: pathname - must be at least MAX_PATH in size
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int GetBurnPath(char *path)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
BOOL flag;
if (pICDBurn->HasRecordableDrive(&flag) == S_OK) {
if (SHGetSpecialFolderPath(0, path, CSIDL_CDBURN_AREA, 0)) {
strcat(path, "\\");
}
else {
ret = 1;
}
}
else {
ret = 2;
}
pICDBurn->Release();
}
else {
ret = 3;
}
return ret;
}
//==============================================================================
// Description: Get CD pathname
// Parameters: pathname - must be at least 5 bytes in size
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int GetCDPath(char *path)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
BOOL flag;
WCHAR drive[5];
if (pICDBurn->GetRecorderDriveLetter(drive, 4) == S_OK) {
sprintf(path, "%S", drive);
}
else {
ret = 1;
}
pICDBurn->Release();
}
else {
ret = 3;
}
return ret;
}
//==============================================================================
// Description: Burn CD
// Parameters: None
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int Burn(void)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
if (pICDBurn->Burn(NULL) != S_OK) {
ret = 1;
}
pICDBurn->Release();
}
else {
ret = 2;
}
return ret;
}
//==============================================================================
bool GetCDRecordableInfo(long *FreeSpaceSize)
{
bool Result = false;
IDiscMaster *idm = NULL;
IDiscRecorder *idr = NULL;
IEnumDiscRecorders *pEnumDiscRecorders = NULL;
ULONG cnt;
long type;
long mtype;
long mflags;
MEDIAINFO mi;
try {
CoCreateInstance(CLSID_MSDiscMasterObj, 0, CLSCTX_ALL, IID_IDiscMaster, (void**)&idm);
idm->Open();
idm->EnumDiscRecorders(&pEnumDiscRecorders);
pEnumDiscRecorders->Next(1, &idr, &cnt);
pEnumDiscRecorders->Release();
idr->OpenExclusive();
idr->GetRecorderType(&type);
idr->QueryMediaType(&mtype, &mflags);
idr->QueryMediaInfo(&mi.nSessions, &mi.nLastTrack, &mi.nStartAddress, &mi.nNextWritable, &mi.nFreeBlocks);
idr->Release();
idm->Close();
idm->Release();
Result = true;
}
catch (...) {
Result = false;
}
if (Result == true) {
Result = false;
if (mtype == 0) {
// No Media inserted
Result = false;
}
else {
if ((mflags & 0x04) == 0x04) {
// Writable Media
Result = true;
}
else {
Result = false;
}
if (Result == true) {
*FreeSpaceSize = (mi.nFreeBlocks * 2048);
}
else {
*FreeSpaceSize = 0;
}
}
}
return Result;
}
To complement the accepted answer, we added this helper function to programatically change the burn directory on the fly as this was a requirement of ours.
typedef HMODULE (WINAPI * SHSETFOLDERPATHA)( int , HANDLE , DWORD , LPCTSTR );
int SetBurnPath( char * cpPath )
{
SHSETFOLDERPATHA pSHSetFolderPath;
HANDLE hShell = LoadLibraryA( "shell32.dll" );
if( hShell == NULL )
return -2;
DWORD dwOrdinal = 0x00000000 + 231;
pSHSetFolderPath = (SHSETFOLDERPATHA)GetProcAddress( hShell, (LPCSTR)dwOrdinal );
if( pSHSetFolderPath == NULL )
return -3;
if( pSHSetFolderPath( CSIDL_CDBURN_AREA, NULL, 0, cpPath ) == S_OK )
return 0;
return -1;
}
This is the information for IMAPI in MSDN site http://msdn.microsoft.com/en-us/library/aa939967.aspx
You should be able to use the shell's ICDBurn interface. Back in the XP day MFC didn't even have any classes for cd burning. I'll see if I can find some examples for you, but it's been a while since I looked at this.