Is it possible to somehow create a template for WinAPI functions? For example, there are two similar functions (LookupPrivilegeName and LookupPrivilegeDisplayName), but with different set of parameters. I call both functions like this: the first call retrieves required buffer size, the second call returns desired value. If this is not possible, are there any alternative ways to make the code more compact?
You likely want something like:
template<typename CharT>
bool LookupPriviledgeDisplayName(CharT const *, CharT const *, DWORD *, std::basic_string<CharT> & strDisplayName);
template<>
bool LookupPriviledgeDisplayName<char>(char const * pszSystem, char const * pszName, DWORD * pdwLangID, std::basic_string <char> & strDisplayName)
{
DWORD dwLength = 0;
if(!LookupPrivilegeDisplayNameA(pszSystem, pszName, nullptr, &dwLength, pdwLangID))
return false;
std::vector<char> buffer(dwLength + 1);
if(!LookupPrivilegeDisplayNameA(pszSystem, pszName, &buffer[0], &dwLength, pdwLangID))
return false;
strDisplayName.assign(&buffer[0], &buffer[0] + dwLength);
return true;
}
template<>
bool LookupPriviledgeDisplayName<wchar_t>(wchar_t const * pszSystem, wchar_t const * pszName, DWORD * pdwLangID, std::basic_string <wchar_t> & strDisplayName)
{
DWORD dwLength = 0;
if(!LookupPrivilegeDisplayNameW(pszSystem, pszName, nullptr, &dwLength, pdwLangID))
return false;
std::vector<wchar_t> buffer(dwLength + 1);
if(!LookupPrivilegeDisplayNameW(pszSystem, pszName, &buffer[0], &dwLength, pdwLangID))
return false;
strDisplayName.assign(&buffer[0], &buffer[0] + dwLength);
return true;
}
Something like this. The main idea is to separate code by functionality avoiding duplication.
#include <memory>
#include <string>
#include <iostream>
#include <system_error>
#include <cassert>
// Error handler.
void
On_Error(const ::DWORD error_code)
{
throw ::std::system_error{static_cast<int>(error_code), ::std::system_category()};
}
// Error check.
void
Validate_LookupPrivilegeNameSuccess(const ::BOOL result, const bool expect_insufficient_buffer)
{
if(FALSE == result)
{
auto const error_code{::GetLastError()};
if((ERROR_INSUFFICIENT_BUFFER == error_code) && (!expect_insufficient_buffer))
{
On_Error(error_code);
}
}
}
enum class
t_PrivilegeNameCategoryId
{
name
, display_name
};
// Helper class calling WinAPI methods, checking input and output.
template<t_PrivilegeNameCategoryId> class
t_LookupPrivilegeNameImpl;
template<> class
t_LookupPrivilegeNameImpl<t_PrivilegeNameCategoryId::name> final
{
public: static ::std::size_t
Lookup(const ::LPCWSTR psz_system_name, ::LUID & luid, const ::LPWSTR p_buffer, ::DWORD buffer_capacity_items_count)
{
assert((0 == buffer_capacity_items_count) || (nullptr != p_buffer));
Validate_LookupPrivilegeNameSuccess
(
::LookupPrivilegeNameW
(
psz_system_name
, ::std::addressof(luid)
, p_buffer
, ::std::addressof(buffer_capacity_items_count)
)
, nullptr == p_buffer
);
return(buffer_capacity_items_count);
}
};
template<> class
t_LookupPrivilegeNameImpl<t_PrivilegeNameCategoryId::display_name> final
{
public: static ::std::size_t
Lookup(const ::LPCWSTR psz_system_name, const ::LPCWSTR psz_display_name, const ::LPWSTR p_buffer, ::DWORD buffer_capacity_items_count)
{
assert(psz_display_name);
assert(L'\0' != psz_display_name[0]);
assert((0 == buffer_capacity_items_count) || (nullptr != p_buffer));
::DWORD language_id{};
Validate_LookupPrivilegeNameSuccess
(
::LookupPrivilegeDisplayNameW
(
psz_system_name
, psz_display_name
, p_buffer
, ::std::addressof(buffer_capacity_items_count)
, ::std::addressof(language_id)
)
, nullptr == p_buffer
);
return(buffer_capacity_items_count);
}
};
// Lookup function implementing get size -> resize buffer -> get data algorithm.
template<t_PrivilegeNameCategoryId name_category_id, typename... TArgs> void
Lookup_PrivilegeName(::std::wstring & name, TArgs &&... args)
{
try
{
name.resize(t_LookupPrivilegeNameImpl<name_category_id>::Lookup(::std::forward<TArgs>(args)..., ::LPWSTR{}, ::DWORD{}));
t_LookupPrivilegeNameImpl<name_category_id>::Lookup(::std::forward<TArgs>(args)..., name.data(), static_cast<::DWORD>(name.size()));
if(name.empty() || (L'\0' != name.back()))
{
On_Error(ERROR_UNIDENTIFIED_ERROR);
}
name.pop_back();
}
catch(...)
{
name.clear();
throw;
}
}
int main()
{
::LPCWSTR psz_system_name{};
::LUID privilege_luid{5}; // a wild guess
::std::wstring privilege_name{};
Lookup_PrivilegeName<t_PrivilegeNameCategoryId::name>(privilege_name, psz_system_name, privilege_luid);
::std::wstring privilege_display_name{};
Lookup_PrivilegeName<t_PrivilegeNameCategoryId::display_name>(privilege_display_name, psz_system_name, privilege_name.c_str());
::std::wcout << privilege_name << L"\n" << privilege_display_name << ::std::endl;
return(0);
}
My Russian friend explained to me the limitations of templates. Instead of them, he suggested using functions with context switching. In my situation it will be something like this:
BOOL LookupData(DWORD context, PLUID luid, wstring input, wstring & output) {
BOOL status = TRUE;
DWORD buflen = 0, lang = 0;
switch (context) {
case 0: status = LookupPrivilegeName(NULL, luid, NULL, &buflen); break;
case 1: status = LookupPrivilegeDisplayName(NULL, input.c_str(), NULL, &buflen, &lang); break;
default: return FALSE;
}
if (!status && ERROR_INSUFFICIENT_BUFFER != GetLastError()) return status;
auto buffer = make_unique<wchar_t[]>(buflen);
switch (context) {
case 0: status = LookupPrivilegeName(NULL, luid, buffer.get(), &buflen); break;
case 1: status = LookupPrivilegeDispayName(NULL, input.c_str(), buffer.get(), &buflen, &lang); break;
}
if (!status) {
buf.release();
return status;
}
output = buf.get();
buf.release();
return status;
}
Now I can write in cycle after getting TOKEN_PRIVILEGES structures:
std::wstring name, desription;
if (!LookupData(0, tp->Privipeges[i].Luid, L"", name)) break;
if (!LookupData(1, NULL, name, description)) break;
std::wcout << name << L" - " << std::endl;
Of course, it's dirty trick but it works.
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
i have some c++ project after a release of support c++20, i want to upgrade my makefile std support 17 to 20 after that point my compiler (gcc10.2) give me a error like this ;
Error
In file included from /usr/local/lib/gcc10/include/c++/bits/node_handle.h:39,
from /usr/local/lib/gcc10/include/c++/bits/stl_tree.h:72,
from /usr/local/lib/gcc10/include/c++/map:60,
from AsyncSQL.h:10,
from AsyncSQL.cpp:4:
/usr/local/lib/gcc10/include/c++/optional: In function 'constexpr std::strong_ordering std::operator<=>(const std::optional<_Tp>&, std::nullopt_t)':
/usr/local/lib/gcc10/include/c++/optional:1052:24: error: invalid operands of types 'bool' and 'int' to binary 'operator<=>'
1052 | { return bool(__x) <=> false; }
| ~~~~~~~~~ ^~~
| |
| bool
gmake[2]: *** [Makefile:23: AsyncSQL.o] Error 1
This is my AsyncSQL.cpp ;
#include <sys/time.h>
#include <cstdlib>
#include <cstring>
#include "AsyncSQL.h"
#define MUTEX_LOCK(mtx) pthread_mutex_lock(mtx)
#define MUTEX_UNLOCK(mtx) pthread_mutex_unlock(mtx)
CAsyncSQL::CAsyncSQL(): m_stHost (""), m_stUser (""), m_stPassword (""), m_stDB (""), m_stLocale (""), m_iMsgCount (0), m_iPort (0), m_bEnd (false), m_hThread (0), m_mtxQuery (NULL), m_mtxResult (NULL), m_iQueryFinished (0), m_ulThreadID (0), m_bConnected (false), m_iCopiedQuery (0)
{
memset (&m_hDB, 0, sizeof (m_hDB));
m_aiPipe[0] = 0;
m_aiPipe[1] = 0;
}
CAsyncSQL::~CAsyncSQL()
{
Quit();
Destroy();
}
void CAsyncSQL::Destroy()
{
if (m_hDB.host)
{
sys_log (0, "AsyncSQL: closing mysql connection.");
mysql_close (&m_hDB);
m_hDB.host = NULL;
}
if (m_mtxQuery)
{
pthread_mutex_destroy (m_mtxQuery);
delete m_mtxQuery;
m_mtxQuery = NULL;
}
if (m_mtxResult)
{
pthread_mutex_destroy (m_mtxResult);
delete m_mtxResult;
m_mtxQuery = NULL;
}
}
void* AsyncSQLThread (void* arg)
{
CAsyncSQL* pSQL = ((CAsyncSQL*) arg);
if (!pSQL->Connect())
{
return NULL;
}
pSQL->ChildLoop();
return NULL;
}
bool CAsyncSQL::QueryLocaleSet()
{
if (0 == m_stLocale.length())
{
sys_err ("m_stLocale == 0");
return true;
}
if (mysql_set_character_set (&m_hDB, m_stLocale.c_str()))
{
sys_err ("cannot set locale %s by 'mysql_set_character_set', errno %u %s", m_stLocale.c_str(), mysql_errno (&m_hDB) , mysql_error (&m_hDB));
return false;
}
sys_log (0, "\t--mysql_set_character_set(%s)", m_stLocale.c_str());
return true;
}
bool CAsyncSQL::Connect()
{
if (0 == mysql_init (&m_hDB))
{
fprintf (stderr, "mysql_init failed\n");
return false;
}
if (!m_stLocale.empty())
{
if (mysql_options (&m_hDB, MYSQL_SET_CHARSET_NAME, m_stLocale.c_str()) != 0)
{
fprintf (stderr, "mysql_option failed : MYSQL_SET_CHARSET_NAME %s ", mysql_error(&m_hDB));
}
}
if (!mysql_real_connect (&m_hDB, m_stHost.c_str(), m_stUser.c_str(), m_stPassword.c_str(), m_stDB.c_str(), m_iPort, NULL, CLIENT_MULTI_STATEMENTS))
{
fprintf (stderr, "mysql_real_connect: %s\n", mysql_error(&m_hDB));
return false;
}
my_bool reconnect = true;
if (0 != mysql_options (&m_hDB, MYSQL_OPT_RECONNECT, &reconnect))
{
fprintf (stderr, "mysql_option: %s\n", mysql_error(&m_hDB));
}
m_ulThreadID = mysql_thread_id (&m_hDB);
m_bConnected = true;
return true;
}
bool CAsyncSQL::Setup (CAsyncSQL* sql, bool bNoThread)
{
return Setup (sql->m_stHost.c_str(), sql->m_stUser.c_str(), sql->m_stPassword.c_str(), sql->m_stDB.c_str(), sql->m_stLocale.c_str(), bNoThread, sql->m_iPort);
}
bool CAsyncSQL::Setup (const char* c_pszHost, const char* c_pszUser, const char* c_pszPassword, const char* c_pszDB, const char* c_pszLocale, bool bNoThread, int iPort)
{
m_stHost = c_pszHost;
m_stUser = c_pszUser;
m_stPassword = c_pszPassword;
m_stDB = c_pszDB;
m_iPort = iPort;
if (c_pszLocale)
{
m_stLocale = c_pszLocale;
sys_log (0, "AsyncSQL: locale %s", m_stLocale.c_str());
}
if (!bNoThread)
{
m_mtxQuery = new pthread_mutex_t;
m_mtxResult = new pthread_mutex_t;
if (0 != pthread_mutex_init (m_mtxQuery, NULL))
{
perror ("pthread_mutex_init");
exit (0);
}
if (0 != pthread_mutex_init (m_mtxResult, NULL))
{
perror ("pthread_mutex_init");
exit (0);
}
pthread_create (&m_hThread, NULL, AsyncSQLThread, this);
return true;
}
else
{
return Connect();
}
}
void CAsyncSQL::Quit()
{
m_bEnd = true;
m_sem.Release();
if (m_hThread)
{
pthread_join (m_hThread, NULL);
m_hThread = NULL;
}
}
SQLMsg* CAsyncSQL::DirectQuery (const char* c_pszQuery)
{
if (m_ulThreadID != mysql_thread_id (&m_hDB))
{
sys_log (0, "MySQL connection was reconnected. querying locale set");
while (!QueryLocaleSet());
m_ulThreadID = mysql_thread_id (&m_hDB);
}
SQLMsg* p = new SQLMsg;
p->m_pkSQL = &m_hDB;
p->iID = ++m_iMsgCount;
p->stQuery = c_pszQuery;
if (mysql_real_query (&m_hDB, p->stQuery.c_str(), p->stQuery.length()))
{
char buf[1024];
snprintf (buf, sizeof(buf), "AsyncSQL::DirectQuery : mysql_query error: %s\nquery: %s", mysql_error (&m_hDB), p->stQuery.c_str());
sys_err (buf);
p->uiSQLErrno = mysql_errno (&m_hDB);
}
p->Store();
return p;
}
void CAsyncSQL::AsyncQuery (const char* c_pszQuery)
{
auto p = new SQLMsg;
p->m_pkSQL = &m_hDB;
p->iID = ++m_iMsgCount;
p->stQuery = c_pszQuery;
PushQuery (p);
}
void CAsyncSQL::ReturnQuery (const char* c_pszQuery, void* pvUserData)
{
auto p = new SQLMsg;
p->m_pkSQL = &m_hDB;
p->iID = ++m_iMsgCount;
p->stQuery = c_pszQuery;
p->bReturn = true;
p->pvUserData = pvUserData;
PushQuery (p);
}
void CAsyncSQL::PushResult (SQLMsg* p)
{
MUTEX_LOCK (m_mtxResult);
m_queue_result.push (p);
MUTEX_UNLOCK (m_mtxResult);
}
bool CAsyncSQL::PopResult(SQLMsg** pp)
{
MUTEX_LOCK (m_mtxResult);
if (m_queue_result.empty())
{
MUTEX_UNLOCK (m_mtxResult);
return false;
}
*pp = m_queue_result.front();
m_queue_result.pop();
MUTEX_UNLOCK (m_mtxResult);
return true;
}
void CAsyncSQL::PushQuery (SQLMsg* p)
{
MUTEX_LOCK (m_mtxQuery);
m_queue_query.push (p);
m_sem.Release();
MUTEX_UNLOCK (m_mtxQuery);
}
bool CAsyncSQL::PeekQuery (SQLMsg** pp)
{
MUTEX_LOCK (m_mtxQuery);
if (m_queue_query.empty())
{
MUTEX_UNLOCK (m_mtxQuery);
return false;
}
*pp = m_queue_query.front();
MUTEX_UNLOCK (m_mtxQuery);
return true;
}
bool CAsyncSQL::PopQuery (int iID)
{
MUTEX_LOCK (m_mtxQuery);
if (m_queue_query.empty())
{
MUTEX_UNLOCK (m_mtxQuery);
return false;
}
m_queue_query.pop();
MUTEX_UNLOCK (m_mtxQuery);
return true;
}
bool CAsyncSQL::PeekQueryFromCopyQueue (SQLMsg** pp)
{
if (m_queue_query_copy.empty())
{
return false;
}
*pp = m_queue_query_copy.front();
return true;
}
int CAsyncSQL::CopyQuery()
{
MUTEX_LOCK (m_mtxQuery);
if (m_queue_query.empty())
{
MUTEX_UNLOCK (m_mtxQuery);
return -1;
}
while (!m_queue_query.empty())
{
SQLMsg* p = m_queue_query.front();
m_queue_query_copy.push (p);
m_queue_query.pop();
}
int count = m_queue_query_copy.size();
MUTEX_UNLOCK (m_mtxQuery);
return count;
}
bool CAsyncSQL::PopQueryFromCopyQueue()
{
if (m_queue_query_copy.empty())
{
return false;
}
m_queue_query_copy.pop();
return true;
}
int CAsyncSQL::GetCopiedQueryCount()
{
return m_iCopiedQuery;
}
void CAsyncSQL::ResetCopiedQueryCount()
{
m_iCopiedQuery = 0;
}
void CAsyncSQL::AddCopiedQueryCount (int iCopiedQuery)
{
m_iCopiedQuery += iCopiedQuery;
}
DWORD CAsyncSQL::CountQuery()
{
return m_queue_query.size();
}
DWORD CAsyncSQL::CountResult()
{
return m_queue_result.size();
}
void __timediff (struct timeval* a, struct timeval* b, struct timeval* rslt)
{
if (a->tv_sec < b->tv_sec)
{
rslt->tv_sec = rslt->tv_usec = 0;
}
else if (a->tv_sec == b->tv_sec)
{
if (a->tv_usec < b->tv_usec)
{
rslt->tv_sec = rslt->tv_usec = 0;
}
else
{
rslt->tv_sec = 0;
rslt->tv_usec = a->tv_usec - b->tv_usec;
}
}
else
{
rslt->tv_sec = a->tv_sec - b->tv_sec;
if (a->tv_usec < b->tv_usec)
{
rslt->tv_usec = a->tv_usec + 1000000 - b->tv_usec;
rslt->tv_sec--;
}
else
{
rslt->tv_usec = a->tv_usec - b->tv_usec;
}
}
}
class cProfiler
{
public:
cProfiler()
{
m_nInterval = 0 ;
memset (&prev, 0, sizeof (prev));
memset (&now, 0, sizeof (now));
memset (&interval, 0, sizeof (interval));
Start();
}
cProfiler (int nInterval = 100000)
{
m_nInterval = nInterval;
memset (&prev, 0, sizeof (prev));
memset (&now, 0, sizeof (now));
memset (&interval, 0, sizeof (interval));
Start();
}
void Start()
{
gettimeofday (&prev , (struct timezone*) 0);
}
void Stop()
{
gettimeofday (&now, (struct timezone*) 0);
__timediff (&now, &prev, &interval);
}
bool IsOk()
{
if (interval.tv_sec > (m_nInterval / 1000000))
{
return false;
}
if (interval.tv_usec > m_nInterval)
{
return false;
}
return true;
}
struct timeval* GetResult()
{
return &interval;
}
long GetResultSec()
{
return interval.tv_sec;
}
long GetResultUSec()
{
return interval.tv_usec;
}
private:
int m_nInterval;
struct timeval prev;
struct timeval now;
struct timeval interval;
};
void CAsyncSQL::ChildLoop()
{
cProfiler profiler(500000);
while (!m_bEnd)
{
m_sem.Wait();
int count = CopyQuery();
if (count <= 0)
{
continue;
}
AddCopiedQueryCount (count);
SQLMsg* p;
while (count--)
{
profiler.Start();
if (!PeekQueryFromCopyQueue (&p))
{
continue;
}
if (m_ulThreadID != mysql_thread_id (&m_hDB))
{
sys_log (0, "MySQL connection was reconnected. querying locale set");
while (!QueryLocaleSet());
m_ulThreadID = mysql_thread_id (&m_hDB);
}
if (mysql_real_query (&m_hDB, p->stQuery.c_str(), p->stQuery.length()))
{
p->uiSQLErrno = mysql_errno (&m_hDB);
sys_err ("AsyncSQL: query failed: %s (query: %s errno: %d)", mysql_error (&m_hDB), p->stQuery.c_str(), p->uiSQLErrno);
switch (p->uiSQLErrno)
{
case CR_SOCKET_CREATE_ERROR:
case CR_CONNECTION_ERROR:
case CR_IPSOCK_ERROR:
case CR_UNKNOWN_HOST:
case CR_SERVER_GONE_ERROR:
case CR_CONN_HOST_ERROR:
case ER_NOT_KEYFILE:
case ER_CRASHED_ON_USAGE:
case ER_CANT_OPEN_FILE:
case ER_HOST_NOT_PRIVILEGED:
case ER_HOST_IS_BLOCKED:
case ER_PASSWORD_NOT_ALLOWED:
case ER_PASSWORD_NO_MATCH:
case ER_CANT_CREATE_THREAD:
case ER_INVALID_USE_OF_NULL:
m_sem.Release();
sys_err ("AsyncSQL: retrying");
continue;
}
}
profiler.Stop();
if (!profiler.IsOk())
{
sys_log (0, "[QUERY : LONG INTERVAL(OverSec %ld.%ld)] : %s", profiler.GetResultSec(), profiler.GetResultUSec(), p->stQuery.c_str());
}
PopQueryFromCopyQueue();
if (p->bReturn)
{
p->Store();
PushResult (p);
}
else
{
delete p;
}
++m_iQueryFinished;
}
}
SQLMsg* p;
while (PeekQuery (&p))
{
if (m_ulThreadID != mysql_thread_id (&m_hDB))
{
sys_log (0, "MySQL connection was reconnected. querying locale set");
while (!QueryLocaleSet());
m_ulThreadID = mysql_thread_id (&m_hDB);
}
if (mysql_real_query (&m_hDB, p->stQuery.c_str(), p->stQuery.length()))
{
p->uiSQLErrno = mysql_errno (&m_hDB);
sys_err ("AsyncSQL::ChildLoop : mysql_query error: %s:\nquery: %s", mysql_error (&m_hDB), p->stQuery.c_str());
switch (p->uiSQLErrno)
{
case CR_SOCKET_CREATE_ERROR:
case CR_CONNECTION_ERROR:
case CR_IPSOCK_ERROR:
case CR_UNKNOWN_HOST:
case CR_SERVER_GONE_ERROR:
case CR_CONN_HOST_ERROR:
case ER_NOT_KEYFILE:
case ER_CRASHED_ON_USAGE:
case ER_CANT_OPEN_FILE:
case ER_HOST_NOT_PRIVILEGED:
case ER_HOST_IS_BLOCKED:
case ER_PASSWORD_NOT_ALLOWED:
case ER_PASSWORD_NO_MATCH:
case ER_CANT_CREATE_THREAD:
case ER_INVALID_USE_OF_NULL:
continue;
}
}
sys_log (0, "QUERY_FLUSH: %s", p->stQuery.c_str());
PopQuery (p->iID);
if (p->bReturn)
{
p->Store();
PushResult (p);
}
else
{
delete p;
}
++m_iQueryFinished;
}
}
int CAsyncSQL::CountQueryFinished()
{
return m_iQueryFinished;
}
void CAsyncSQL::ResetQueryFinished()
{
m_iQueryFinished = 0;
}
MYSQL* CAsyncSQL::GetSQLHandle()
{
return &m_hDB;
}
size_t CAsyncSQL::EscapeString (char* dst, size_t dstSize, const char* src, size_t srcSize)
{
if (0 == srcSize)
{
memset (dst, 0, dstSize);
return 0;
}
if (0 == dstSize)
{
return 0;
}
if (dstSize < srcSize * 2 + 1)
{
char tmp[256];
size_t tmpLen = sizeof (tmp) > srcSize ? srcSize : sizeof (tmp);
strlcpy (tmp, src, tmpLen);
sys_err ("FATAL ERROR!! not enough buffer size (dstSize %u srcSize %u src%s: %s)", dstSize, srcSize, tmpLen != srcSize ? "(trimmed to 255 characters)" : "", tmp);
dst[0] = '\0';
return 0;
}
return mysql_real_escape_string (GetSQLHandle(), dst, src, srcSize);
}
void CAsyncSQL2::SetLocale (const std::string & stLocale)
{
m_stLocale = stLocale;
QueryLocaleSet();
}
This is my AsyncSQL.h
#ifndef __INC_METIN_II_ASYNCSQL_H__
#define __INC_METIN_II_ASYNCSQL_H__
#include "../../libthecore/src/stdafx.h"
#include "../../libthecore/src/log.h"
#include "../../Ayarlar.h"
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <mysql/server/mysql.h>
#include <mysql/server/errmsg.h>
#include <mysql/server/mysqld_error.h>
#include "Semaphore.h"
typedef struct _SQLResult
{
_SQLResult(): pSQLResult (NULL), uiNumRows (0), uiAffectedRows (0), uiInsertID (0) {}
~_SQLResult()
{
if (pSQLResult)
{
mysql_free_result (pSQLResult);
pSQLResult = NULL;
}
}
MYSQL_RES* pSQLResult;
uint32_t uiNumRows;
uint32_t uiAffectedRows;
uint32_t uiInsertID;
} SQLResult;
typedef struct _SQLMsg
{
_SQLMsg() : m_pkSQL (NULL), iID (0), uiResultPos (0), pvUserData (NULL), bReturn (false), uiSQLErrno (0) {}
~_SQLMsg()
{
auto first = vec_pkResult.begin();
auto past = vec_pkResult.end();
while (first != past)
{
delete * (first++);
}
vec_pkResult.clear();
}
void Store()
{
do
{
SQLResult* pRes = new SQLResult;
pRes->pSQLResult = mysql_store_result (m_pkSQL);
pRes->uiInsertID = mysql_insert_id (m_pkSQL);
pRes->uiAffectedRows = mysql_affected_rows (m_pkSQL);
if (pRes->pSQLResult)
{
pRes->uiNumRows = mysql_num_rows (pRes->pSQLResult);
}
else
{
pRes->uiNumRows = 0;
}
vec_pkResult.push_back (pRes);
}
while (!mysql_next_result (m_pkSQL));
}
SQLResult* Get()
{
if (uiResultPos >= vec_pkResult.size())
{
return NULL;
}
return vec_pkResult[uiResultPos];
}
bool Next()
{
if (uiResultPos + 1 >= vec_pkResult.size())
{
return false;
}
++uiResultPos;
return true;
}
MYSQL* m_pkSQL;
int iID;
std::string stQuery;
std::vector<SQLResult *> vec_pkResult;
unsigned int uiResultPos;
void* pvUserData;
bool bReturn;
unsigned int uiSQLErrno;
} SQLMsg;
class CAsyncSQL
{
public:
CAsyncSQL();
virtual ~CAsyncSQL();
void Quit();
bool Setup (const char* c_pszHost, const char* c_pszUser, const char* c_pszPassword, const char* c_pszDB, const char* c_pszLocale, bool bNoThread = false, int iPort = 0);
bool Setup (CAsyncSQL* sql, bool bNoThread = false);
bool Connect();
bool IsConnected()
{
return m_bConnected;
}
bool QueryLocaleSet();
void AsyncQuery (const char* c_pszQuery);
void ReturnQuery (const char* c_pszQuery, void* pvUserData);
SQLMsg* DirectQuery (const char* c_pszQuery);
DWORD CountQuery();
DWORD CountResult();
void PushResult (SQLMsg* p);
bool PopResult (SQLMsg** pp);
void ChildLoop();
MYSQL* GetSQLHandle();
int CountQueryFinished();
void ResetQueryFinished();
size_t EscapeString (char* dst, size_t dstSize, const char* src, size_t srcSize);
protected:
void Destroy();
void PushQuery (SQLMsg* p);
bool PeekQuery (SQLMsg** pp);
bool PopQuery (int iID);
bool PeekQueryFromCopyQueue (SQLMsg** pp );
INT CopyQuery();
bool PopQueryFromCopyQueue();
public:
int GetCopiedQueryCount();
void ResetCopiedQueryCount();
void AddCopiedQueryCount (int iCopiedQuery);
protected:
MYSQL m_hDB;
std::string m_stHost;
std::string m_stUser;
std::string m_stPassword;
std::string m_stDB;
std::string m_stLocale;
int m_iMsgCount;
int m_aiPipe[2];
int m_iPort;
std::queue<SQLMsg*> m_queue_query;
std::queue<SQLMsg*> m_queue_query_copy;
std::queue<SQLMsg*> m_queue_result;
volatile bool m_bEnd;
pthread_t m_hThread;
pthread_mutex_t* m_mtxQuery;
pthread_mutex_t* m_mtxResult;
CSemaphore m_sem;
int m_iQueryFinished;
unsigned long m_ulThreadID;
bool m_bConnected;
int m_iCopiedQuery;
};
class CAsyncSQL2 : public CAsyncSQL
{
public:
void SetLocale (const std::string & stLocale);
};
#endif
And this is the function the reason of the error ;
optional:1052 ;
#ifdef __cpp_lib_three_way_comparison
template<typename _Tp>
constexpr strong_ordering
operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept
{ return bool(__x) <=> false; }
#else
After a see a document the microsoft release i'm gonna try <= > false; like this and take a error again..
Best Regards.
I ve no idea why it looks is getting bool(__x) <=> false as an bool and int comparison.
I would think you got some strange macro in your files included before to include the header that is going to break the standard code.
I would suggest you try to move above the standard headers and below them your 'user defined' headers.
#include <string>
#include <queue>
#include <vector>
#include <map>
#include <mysql/server/mysql.h>
#include <mysql/server/errmsg.h>
#include <mysql/server/mysqld_error.h>
#include "../../libthecore/src/stdafx.h"
#include "../../libthecore/src/log.h"
#include "../../Ayarlar.h"
#include "Semaphore.h"
EDIT:
i ve found the cause of the problem.
a macro defined in "libthrecore/stdafx.h" (i own the files that is using the author, they are public).
#ifndef false
#define false 0
#define true (!false)
#endif
it is causing false to be read as a int and is causing the spaceship operator to fails with the error shown by the author. Move up the standard headers or remove the macro to solve the error.
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 need to replace an ActiveX control with a new one for use in an existing application, without modifying the existing application.
I have the original ocx file and the code for the wrapper class that creates and uses the ActiveX control in the existing application.
I have created a test console app in VS 2005, using the wrapper class from the existing application, which can create and use the old ActiveX control.
I have created a new ActiveX control with the same interface as the old, modified all the Class Ids to match and registered the new ocx. The new control can be created, but on calling any of the methods I get a Catastrophic failure with error code 7fff0001.
I took a step back and created another new ActiveX control with the same interface, but didn't modify the Class Ids. I then built a new test console app using the wrapper class and compiled it for this new control. But I still get a Catastrophic failure when calling the control's methods.
I have searched for an answer, but most refer to problems calling ActiveX methods from managed c# applications and don't provide any help for my situation.
Please see the code for the wrapper and ActiveX control import files below, any help would be appreciated thanks.
ClientWrapper.h
#if !defined(AFX_CLIENTWRAPPER_H__D8C23AC2_6F22_11D6_9F45_0003B3008F24__INCLUDED_)
#define AFX_CLIENTWRAPPER_H__D8C23AC2_6F22_11D6_9F45_0003B3008F24__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#import <Msxml3.dll>
#import "ACDCLIENTX.OCX"
#include <atlbase.h>
#include <atlconv.h>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <map>
#include <assert.h>
#ifdef ASSERT
#undef ASSERT
#endif
#define ASSERT assert
class CParsedResponse
{
public:
CParsedResponse():m_bIsApproved(false),m_bIsCaptured(false),
m_bIsSuccess(false),m_bIsError(false){;}
virtual ~CParsedResponse(){;};
CParsedResponse(const CParsedResponse& copy){*this = copy;}
CParsedResponse& operator = (const CParsedResponse& that)
{
if(this == &that ) return *this;
m_strErrorNo = that.m_strErrorNo;
m_strVerbage = that.m_strVerbage;
m_strBalance = that.m_strBalance;
m_strPrePaidExp = that.m_strPrePaidExp;
m_strAuthCode = that.m_strAuthCode;
m_strReferenceNo = that.m_strReferenceNo;
m_strAcctNo = that.m_strAcctNo;
m_strExpDate = that.m_strExpDate;
m_strPurchaseAmount = that.m_strPurchaseAmount;
m_strInvoiceNo = that.m_strInvoiceNo;
m_strOpId = that.m_strOpId;
m_strAcqRefData = that.m_strAcqRefData;
m_strOrigin = that.m_strOrigin;
m_bIsApproved = that.m_bIsApproved;
m_bIsCaptured = that.m_bIsCaptured;
m_bIsSuccess = that.m_bIsSuccess;
m_bIsError = that.m_bIsError;
return *this;
}
void Empty()
{
m_strErrorNo =
m_strVerbage =
m_strBalance =
m_strPrePaidExp =
m_strAuthCode =
m_strReferenceNo =
m_strAcctNo =
m_strExpDate =
m_strPurchaseAmount =
m_strInvoiceNo =
m_strOpId =
m_strAcqRefData =
m_strOrigin = "";
m_bIsApproved =
m_bIsCaptured =
m_bIsSuccess =
m_bIsError = false;
}
std::string m_strErrorNo,
m_strVerbage,
m_strBalance,
m_strPrePaidExp,
m_strAuthCode,
m_strReferenceNo,
m_strAcctNo,
m_strExpDate,
m_strPurchaseAmount,
m_strInvoiceNo,
m_strOpId,
m_strAcqRefData,
m_strOrigin;
bool m_bIsApproved,
m_bIsCaptured,
m_bIsSuccess,
m_bIsError;
};
class CClientWrapper
{
std::string m_strName;
bool LooksLikeIp(const std::string IPorHostName);
std::string GetIPAddressFromHostName(const std::string& Ip);
bool GetIPFromXML(const std::string XML,std::string& Ip);
void AddToStore(std::string & s,const std::string & ip);
int GetNumIPStoredIPFromXML(const std::string XML);
std::string BuildErrorResponse(const int ErrNo, const std::string strErrText);
std::string BuildErrorResponse(const std::string ErrNo, const std::string strErrText);
std::string CClientWrapper::BuildErrorResponse(const _com_error ComError);
public:
std::string m_strError;
std::string m_strServerPassword;
CParsedResponse ExtractTransResponse(const std::string XmlResponse);
std::string SendRequestReturnXML(const std::string strXML);
CParsedResponse SendRequest(const std::string strXML);
bool DoServerIp(const std::vector<std::string>& IpList);
std::string PingStoredServerListReturnXML();
private:
ACDCLIENTXLib::_DACDCLientXPtr m_pClient;
bool m_bComInit;
bool m_bControlInit;
public:
void Destroy();
bool Create();
void Cancel();
CClientWrapper();
virtual ~CClientWrapper();
int SetConnectTimeout(const int t);
int SetResponseTimeout(const int t);
CParsedResponse PingStoredServerList();
// 0 dialog // 1 no dialog // 2 not currently supported in wrapper
SHORT m_nDialogControl;
};
#endif // !defined(AFX_CLIENTWRAPPER_H__D8C23AC2_6F22_11D6_9F45_0003B3008F24__INCLUDED_)
ClientWrapper.cpp
#include "StdAfx.h"
#include "ClientWrapper.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define WRAP_ERROR "900999"
CClientWrapper::CClientWrapper():m_bComInit(false),m_bControlInit(false),
m_nDialogControl(0),m_strServerPassword("")
{
}
CClientWrapper::~CClientWrapper()
{
Destroy();
}
void CClientWrapper::Destroy()
{
if(m_bControlInit)
{
m_pClient.Release();
m_bControlInit = false;
}
if(m_bComInit)
{
::CoUninitialize();
m_bComInit = false;
}
}
bool CClientWrapper::Create()
{
USES_CONVERSION;
Destroy();
ASSERT(!m_bComInit);
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr))
{
m_strError = "CoInitialize Failed - Fatal Error!";
return false;
}
m_bComInit = true;
ASSERT(!m_bControlInit);
hr = m_pClient.CreateInstance(__uuidof(ACDCLIENTXLib::ACDCLientX));
if(FAILED(hr) )
{
m_strError = "Failed to Create ACDCLientX.ocx you may need to reinstall the product - Fatal Error!";
return false;
}
m_bControlInit = true;
// make sure xml parser is available
try
{
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
}
catch(_com_error e)
{
m_strError = "Msxml3.dll required for this application. You may need to reinstall the product - Fatal Error!";
return false;
}
return true;
}
#pragma warning(disable:4786)
std::string CClientWrapper::GetIPAddressFromHostName(const std::string& Ip)
{
std::string rv;
USES_CONVERSION;
_bstr_t strIp(Ip.c_str());
try
{
rv = W2T(m_pClient->GetIPAddressFromHostName(strIp,m_nDialogControl));
}
catch(const _com_error &e)
{
throw e;
}
return rv;
}
bool CClientWrapper::DoServerIp(const std::vector<std::string>& IpList)
{
std::vector<std::string>::const_iterator i;
USES_CONVERSION;
assert(m_bComInit && m_bControlInit);
////
////
try
{
std::string IpStore("");
i = IpList.begin();
while(i != IpList.end())
{
std::string Ip = *i;
if(!Ip.length() )
{
++ i;
continue;
}
if(!LooksLikeIp(Ip) )
{
std::string xml = GetIPAddressFromHostName(Ip);
if(GetIPFromXML(xml,Ip) )
{
AddToStore(IpStore,Ip);
}
}
else
{
AddToStore(IpStore,Ip);
}
++ i;
}
if(!IpStore.length())
{
m_strError = "No Resolved Ip Address.";
return false;
}
_bstr_t strIpStore(IpStore.c_str());
std::string xml = W2T(m_pClient->ServerIPConfig(strIpStore,m_nDialogControl));
if(GetNumIPStoredIPFromXML(xml) <= 0 )
{
m_strError = "No Resolved Ip Address.";
return false;
}
//defaults to 10 seconds connect - 300 seconds response
#ifdef _DEBUG
ASSERT(m_pClient->SetConnectTimeout(5) == 5);
#else
m_pClient->SetConnectTimeout(5);
#endif
// VERIFY(m_pClient->SetConnectTimeout(5) == 5);
//VERIFY(m_pClient->SetResponseTimeout(300) == 300);
// you may wish to 'ping' the servers at this point
// using PingStoredServerList(..)
// or you could ping each before you store the ServerIpConfig(..)
// using PingServer(..)
// it is up to you whether you want your
// app not to start or give some other message because a server is down
// if you decide not to ping AND the Server is DOWN when you attempt
// an authorization the control will return an appropriate error.
}
catch (const _com_error &ComError)
{
m_strError = ComError.ErrorMessage() + std::string(" Fatal Error!");
return false;
}
catch (...)
{
m_strError = "ATL/COM Error" + std::string(" Fatal Error!");
return false;
}
char nameBuffer[101] = {0};
DWORD size = 100;
if(::GetUserName(nameBuffer,&size) )
m_strName = nameBuffer;
return true;
}
bool CClientWrapper::LooksLikeIp(const std::string IPorHostName)
{
std::map<int,std::string> Map;
std::map<int,std::string>::const_iterator iter;
const std::string& s = IPorHostName;
int x(0),len = s.length();
char c,fsep = '.';
std::string fieldText;
int NumOctets (0);
while(x < len)
{
c = s[x];
if(c != fsep)
fieldText += c;
else
{
Map[NumOctets++]=fieldText;
fieldText = "";
}
x++;
}
if(fieldText.length() )
Map[NumOctets++]=fieldText;
if(NumOctets != 4)
return false;
for(x = 0; x < 4 ; x ++ )
{
std::string octect;
if((iter = Map.find(x))==Map.end() )
return false;
octect = (*iter).second;
int val = _ttoi(octect.c_str());
if(val == 0 )
{
//octect.TrimLeft();
int len = octect.length();
std::string temp("");
for(int xx= 0; xx < len ; xx ++ )
{
char c = octect[xx];
if(temp.length() || c != ' ' )
{
temp += c;
}
}
octect = temp;
if(!octect.length() || octect[0] < 0x30 || octect[0] > 0x39)
return false;
}
if(val < 0 || val > 255)
return false;
}
return true;
}
bool CClientWrapper::GetIPFromXML(const std::string XML,std::string& Ip)
{
USES_CONVERSION;
try
{
_bstr_t strXML (XML.c_str());
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
VARIANT_BOOL varResult = pDOMDoc->loadXML(strXML);
// any error is bad
if (VARIANT_FALSE == varResult)
return false;
MSXML2::IXMLDOMNodePtr pNode = pDOMDoc->selectSingleNode("//CmdStatus");
if(0 == pNode )
return false;
std::string Result = W2T(pNode->Gettext() );
if("Success" != Result)
return false;
pNode = pDOMDoc->selectSingleNode("//IpAddress");
if(0 == pNode )
return false;
Ip = W2T(pNode->Gettext());
return true;
}
catch(_com_error e)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetIPFromXML") << std::endl;
#endif
}
catch(...)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetIPFromXML") << std::endl;
#endif
}
return false;
}
void CClientWrapper::AddToStore(std::string & s,const std::string& ip)
{
// if ip is empty ignore it
if(!ip.length() )
return;
// if the store is not empty it is ';' separated
if(s.length() )
s += std::string(";") + ip;
else
s = ip;
}
int CClientWrapper::GetNumIPStoredIPFromXML(const std::string XML)
{
USES_CONVERSION;
try
{
_bstr_t strXML (XML.c_str());
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
VARIANT_BOOL varResult = pDOMDoc->loadXML(strXML);
// any error is bad
if (VARIANT_FALSE == varResult)
return 0;
MSXML2::IXMLDOMNodePtr pNode = pDOMDoc->selectSingleNode("//CmdStatus");
if(0 == pNode )
return 0;
std::string Result = W2T(pNode->Gettext() );
if("Success" != Result)
return 0;
pNode = pDOMDoc->selectSingleNode("//TextResponse");
if(0 == pNode )
return 0;
return _ttoi(W2T(pNode->Gettext() ) );
}
catch(_com_error e)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetNumIPStoredIPFromXML") << std::endl;
#endif
}
catch(...)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetNumIPStoredIPFromXML") << std::endl;
#endif
}
return 0;
}
std::string CClientWrapper::BuildErrorResponse(const _com_error ComError)
{
std::stringstream ssErrNo;
ssErrNo << ComError.Error();
return BuildErrorResponse(ssErrNo.str(), ComError.ErrorMessage());
}
std::string CClientWrapper::BuildErrorResponse(const std::string ErrNo, const std::string strErrText)
{
return BuildErrorResponse(_ttoi(ErrNo.c_str()), strErrText);
}
std::string CClientWrapper::BuildErrorResponse(const int ErrNo, const std::string strErrText)
{
#define TAB "\t"
#define CRLF "\r\n"
std::ostringstream oss;
oss << "<?xml version=\"1.0\"?>"CRLF
"<RStream>"CRLF
TAB "<CmdResponse>"CRLF
TAB TAB "<ACDXReturnCode>"
<< std::setw(6) << std::setfill('0') << ErrNo
<< "</ACDXReturnCode>"CRLF
TAB TAB "<CmdStatus>Error</CmdStatus>"CRLF
TAB TAB "<TextResponse>"
<< strErrText.c_str()
<< "</TextResponse>"CRLF
TAB "</CmdResponse>"CRLF
"</RStream>"CRLF
<< std::endl;
std::string rv(oss.str());
return rv;
}
std::string CClientWrapper::PingStoredServerListReturnXML()
{
USES_CONVERSION;
std::string rv;
try
{
rv = W2T(m_pClient->PingStoredServerList("",m_nDialogControl) );
}
catch (const _com_error &ComError)
{
rv = BuildErrorResponse(ComError);
}
catch (...)
{
rv = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
}
return rv;
}
void CClientWrapper::Cancel()
{
try
{
m_pClient->CancelRequest();
}
catch (const _com_error &ComError)
{
std::string s = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
#ifdef _DEBUG
std :: cerr << s << std::endl;
#endif
}
catch (...)
{
std::string s = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
#ifdef _DEBUG
std :: cerr << s << std::endl;
#endif
}
}
CParsedResponse CClientWrapper::SendRequest(const std::string strXML)
{
return ExtractTransResponse(SendRequestReturnXML(strXML) );
}
std::string CClientWrapper::SendRequestReturnXML(const std::string strXML)
{
USES_CONVERSION;
std::string rv;
try
{
_bstr_t strXml(strXML.c_str());
_bstr_t strPasswd(m_strServerPassword.c_str());
_bstr_t strName(m_strName.c_str());
rv = W2T(m_pClient->ProcessTransaction(
strXml,
m_nDialogControl,
strPasswd,
strName) );
}
catch (const _com_error &ComError)
{
rv = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
}
catch (...)
{
rv = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
}
return rv;
}
CParsedResponse CClientWrapper::ExtractTransResponse(const std::string XmlResponse)
{
CParsedResponse rv;
USES_CONVERSION;
try
{
_bstr_t strXML (XmlResponse.c_str());
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
VARIANT_BOOL varResult = pDOMDoc->loadXML(strXML);
if (VARIANT_FALSE == varResult)
{
MSXML2::IXMLDOMParseErrorPtr pParseError = pDOMDoc->GetparseError();
long dwError = pParseError->GeterrorCode();
_bstr_t bstrReason = pParseError->Getreason();
long num = pParseError->Getline();
std::ostringstream oss;
std::string strReason = W2T(bstrReason);
oss << "XML Parse Error at line#:" << num << "\r\n" << strReason << std::endl;
std::string error (oss.str() );
rv.m_strErrorNo = WRAP_ERROR;
rv.m_strVerbage = error;
rv.m_bIsError = true;
return rv;
}
MSXML2::IXMLDOMNodePtr pNode = pDOMDoc->selectSingleNode("//TextResponse");
if(pNode)
{
rv.m_strVerbage = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//Balance");
if(pNode)
{
rv.m_strBalance = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//PrePaidExp");
if(pNode)
{
rv.m_strPrePaidExp = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//ResponseOrigin");
if(pNode)
{
rv.m_strOrigin = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//ACDXReturnCode");
if(pNode)
{
rv.m_strErrorNo = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//CmdStatus");
if(pNode)
{
std::string strText;
strText = W2T(pNode->Gettext() );
if("Approved" == strText )
{
rv.m_bIsApproved = true;
rv.m_strVerbage = "APPROVED";
pNode = pDOMDoc->selectSingleNode("//AuthCode");
if(pNode)
{
rv.m_strAuthCode = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//CaptureStatus");
if(pNode)
{
#if defined(__USING__MFC)
if("Captured" == CString(W2T(pNode->Gettext()) ) )
#else
if("Captured" == std::string(W2T(pNode->Gettext()) ) )
#endif
{
rv.m_bIsCaptured = true;
}
}
pNode = pDOMDoc->selectSingleNode("//RefNo");
if(pNode)
{
rv.m_strReferenceNo = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//AccountNo");
if(pNode)
{
rv.m_strAcctNo = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//ExpDate");
if(pNode)
{
rv.m_strExpDate = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//Purchase");
if(pNode)
{
rv.m_strPurchaseAmount = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//InvoiceNo");
if(pNode)
{
rv.m_strInvoiceNo = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//OperatorId");
if(pNode)
{
rv.m_strOpId = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//AcqRefData");
if(pNode)
{
rv.m_strAcqRefData = W2T(pNode->Gettext());
}
}
else if("Declined" == strText)
{
rv.m_bIsApproved = false;
rv.m_bIsCaptured = false;
}
else if("Success" == strText )
{
rv.m_bIsSuccess =true;
}
else if("Error" == strText )
{
rv.m_bIsError = true;
}
else
{
rv.m_strVerbage = "Received Invalid Result from ACDClientX Control.- \r\n"+ XmlResponse;
rv.m_strErrorNo = WRAP_ERROR;
}
}
}
catch(_com_error e)
{
#ifdef _DEBUG
std::cerr<< std::string("Caught Exception: ExtractTransResponse")<< std::endl;
#endif
rv.m_strErrorNo = WRAP_ERROR;
rv.m_strVerbage = e.ErrorMessage();
rv.m_bIsError = true;
}
catch(...)
{
#ifdef _DEBUG
std::cerr<< std::string("Caught Exception: ExtractTransResponse")<< std::endl;
#endif
rv.m_strErrorNo = WRAP_ERROR;
rv.m_strVerbage = "Msxml3 Open Error";
rv.m_bIsError = true;
}
return rv;
}
CParsedResponse CClientWrapper::PingStoredServerList()
{
return ExtractTransResponse(PingStoredServerListReturnXML() );
}
int CClientWrapper::SetResponseTimeout(const int t)
{
USES_CONVERSION;
int rv;
try
{
rv = m_pClient->SetResponseTimeout(t);
}
catch (const _com_error &ComError)
{
m_strError = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
rv = -1;
}
catch (...)
{
m_strError = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
rv = -1;
}
return rv;
}
int CClientWrapper::SetConnectTimeout(const int t)
{
USES_CONVERSION;
int rv;
try
{
rv = m_pClient->SetConnectTimeout(t);
}
catch (const _com_error &ComError)
{
m_strError = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
rv = -1;
}
catch (...)
{
m_strError = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
rv = -1;
}
return rv;
}
acdclientx.tlh
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace ACDCLIENTXLib {
//
// Forward references and typedefs
//
struct __declspec(uuid("87476f1d-9a0f-4e4e-bd38-0378b35d71b8"))
/* LIBID */ __ACDCLIENTXLib;
struct __declspec(uuid("4ff86764-9448-4e1d-a160-4e55c7151c3d"))
/* dispinterface */ _DACDCLientX;
struct __declspec(uuid("fd6514db-1fa1-43c2-982a-9bf3a4f63c71"))
/* dispinterface */ _DACDCLientXEvents;
struct /* coclass */ ACDCLientX;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(_DACDCLientX, __uuidof(_DACDCLientX));
_COM_SMARTPTR_TYPEDEF(_DACDCLientXEvents, __uuidof(_DACDCLientXEvents));
//
// Type library items
//
struct __declspec(uuid("4ff86764-9448-4e1d-a160-4e55c7151c3d"))
_DACDCLientX : IDispatch
{
//
// Wrapper methods for error-handling
//
// Methods:
_bstr_t ProcessTransaction (
_bstr_t XMLCommand,
short ProcessControl,
_bstr_t ClientServerPassword,
_bstr_t UserTraceData );
_bstr_t ServerIPConfig (
_bstr_t HostList,
short ProcessControl );
_bstr_t PingStoredServerList (
_bstr_t IpPort,
short ProcessControl );
long CancelRequest ( );
_bstr_t GetIPAddressFromHostName (
_bstr_t HostName,
short ProcessControl );
_bstr_t PingServer (
_bstr_t IpAddress,
_bstr_t IpPort,
short ProcessControl );
short SetConnectTimeout (
short TimeoutSec );
short SetResponseTimeout (
short TimeoutSec );
_bstr_t ProcessCanadianTransaction (
_bstr_t XMLCommand,
short ProcessControl,
_bstr_t ClientServerPassword,
_bstr_t UserTraceData );
};
struct __declspec(uuid("fd6514db-1fa1-43c2-982a-9bf3a4f63c71"))
_DACDCLientXEvents : IDispatch
{};
struct __declspec(uuid("d76e402e-c138-4480-a8a3-bc840d6a2a4e"))
ACDCLientX;
// [ default ] dispinterface _DACDCLientX
// [ default, source ] dispinterface _DACDCLientXEvents
//
// Wrapper method implementations
//
#include "acdclientx.tli"
} // namespace ACDCLIENTXLib
#pragma pack(pop)
acdclientx.tli
#pragma once
//
// dispinterface _DACDCLientX wrapper method implementations
//
inline _bstr_t _DACDCLientX::ProcessTransaction ( _bstr_t XMLCommand, short ProcessControl, _bstr_t ClientServerPassword, _bstr_t UserTraceData ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x1, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002\x0008\x0008", (BSTR)XMLCommand, ProcessControl, (BSTR)ClientServerPassword, (BSTR)UserTraceData);
return _bstr_t(_result, false);
}
inline _bstr_t _DACDCLientX::ServerIPConfig ( _bstr_t HostList, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x2, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002", (BSTR)HostList, ProcessControl);
return _bstr_t(_result, false);
}
inline _bstr_t _DACDCLientX::PingStoredServerList ( _bstr_t IpPort, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x3, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002", (BSTR)IpPort, ProcessControl);
return _bstr_t(_result, false);
}
inline long _DACDCLientX::CancelRequest ( ) {
long _result = 0;
_com_dispatch_method(this, 0x4, DISPATCH_METHOD, VT_I4, (void*)&_result, NULL);
return _result;
}
inline _bstr_t _DACDCLientX::GetIPAddressFromHostName ( _bstr_t HostName, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x5, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002", (BSTR)HostName, ProcessControl);
return _bstr_t(_result, false);
}
inline _bstr_t _DACDCLientX::PingServer ( _bstr_t IpAddress, _bstr_t IpPort, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x6, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0008\x0002", (BSTR)IpAddress, (BSTR)IpPort, ProcessControl);
return _bstr_t(_result, false);
}
inline short _DACDCLientX::SetConnectTimeout ( short TimeoutSec ) {
short _result = 0;
_com_dispatch_method(this, 0x7, DISPATCH_METHOD, VT_I2, (void*)&_result,
L"\x0002", TimeoutSec);
return _result;
}
inline short _DACDCLientX::SetResponseTimeout ( short TimeoutSec ) {
short _result = 0;
_com_dispatch_method(this, 0x8, DISPATCH_METHOD, VT_I2, (void*)&_result,
L"\x0002", TimeoutSec);
return _result;
}
inline _bstr_t _DACDCLientX::ProcessCanadianTransaction ( _bstr_t XMLCommand, short ProcessControl, _bstr_t ClientServerPassword, _bstr_t UserTraceData ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x9, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002\x0008\x0008", (BSTR)XMLCommand, ProcessControl, (BSTR)ClientServerPassword, (BSTR)UserTraceData);
return _bstr_t(_result, false);
}
Found the answer in:
http://support.microsoft.com/kb/146120/EN-US
Needed to define this function in my ActiveX Control
BOOL CMyOleControl::IsInvokeAllowed (DISPID)
{
// You can check to see if COleControl::m_bInitialized is FALSE
// in your automation functions to limit access.
return TRUE;
}
For the life of me I can't figure out why this is not working. Basically, I created the pipe with the inherit bit set to true, and created two child processes, and used the STARTUPINFO structure to set the input and output handles as one would need, but the pipe seems broken (the second process writes no output to the console, even though output is expected)
I know the problem does not lie in my test program (BitTwiddler.exe) because I performed the same operation using CMD.exe, and everything works as expected.
Below is a minimal reproduction of what I have. What have I done incorrectly?
#include "windows.h"
int main()
{
PROCESS_INFORMATION piSource, piDest;
HANDLE hPipeIn, hPipeOut;
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
STARTUPINFOW suSource, suDest;
ZeroMemory(&suSource, sizeof(suSource));
ZeroMemory(&suDest, sizeof(suDest));
suSource.cb = suDest.cb = sizeof(STARTUPINFOW);
suSource.dwFlags = suDest.dwFlags = STARTF_USESTDHANDLES;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = TRUE;
if (CreatePipe(&hPipeIn, &hPipeOut, &sa, 0) == 0)
{
return GetLastError();
}
suSource.hStdInput = hIn;
suSource.hStdError = suSource.hStdOutput = hPipeIn;
suDest.hStdInput = hPipeOut;
suDest.hStdError = suDest.hStdOutput = hOut;
std::wstring cmdLineA(L"BitTwiddler 1"), cmdLineB(L"BitTwiddler 0");
cmdLineA.push_back(0); cmdLineB.push_back(0);
if (CreateProcessW(0, &cmdLineA[0], 0, 0, TRUE, 0, 0, 0, &suSource, &piSource) == 0)
{
return GetLastError();
}
CloseHandle(piSource.hThread);
if (CreateProcessW(0, &cmdLineB[0], 0, 0, TRUE, 0, 0, 0, &suDest, &piDest) == 0)
{
return GetLastError();
}
CloseHandle(piDest.hThread);
HANDLE hArray[2];
hArray[0] = piSource.hProcess;
hArray[1] = piDest.hProcess;
WaitForMultipleObjects(2, hArray, TRUE, INFINITE);
CloseHandle(hArray[0]);
CloseHandle(hArray[1]);
return 0;
}
(In case anyone's interested, BitTwiddler is:
#include <windows.h>
#include <sstream>
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
std::size_t opt = 0;
argc--; argv++;
if (argc == 0)
{
return 0;
}
else
{
std::istringstream converter(*argv);
converter >> opt;
}
switch(opt)
{
case 0:
{
std::wstring currentLine;
while(std::getline(std::wcin, currentLine))
{
std::wcout << "Got somepin: " << currentLine << std::endl;
}
}
break;
case 1:
for (;;)
{
std::wcout << L"Hello World!" << std::endl;
Sleep(1000);
}
break;
case 2:
return -1;
default:
std::wcout << "Unknown option.";
return 0;
}
return 0;
}
), but I really don't think that matters.
You misplaced the read and write ends :)
CreatePipe has the prototype
BOOL CreatePipe(
PHANDLE hReadPipe, // can only read from this
PHANDLE hWritePipe, // can only write to this
LPSECURITY_ATTRIBUTES lpPipeAttributes,
DWORD nSize
);
You can't call ReadFile (or in your case std::getline) from a Write-only handle, and vice versa. If you replaced your std::getline calls with a simple ReadFile call, you'd get an ACCESS_DENIED error, confirming this fact, since your STD_INPUT_HANDLE in the child process was not opened for GENERIC_READ.
The fix is as follows:
suSource.hStdError = suSource.hStdOutput = hPipeOut; // must be the write pipe!
suDest.hStdInput = hPipeIn; // must be the read pipe.
Perhaps the names you assigned are confusing. If you called them as per the formal parameters, the error would be clearer:
suSource.hStdError = suSource.hStdOutput = hReadPipe; // clearly wrong.
suDest.hStdInput = hWritePipe; // as above -- expects a read-handle.
I upvoted your question as it is a very important one. Since I learnt Windows programming from FamTrinli's demos years ago, CreateProcess has been one of the harddest functions of the Win32 API. If not, the harddest.
CreateProcess has alot of arguments. Each argument requires careful reading of the documentation to properly use. Alot of arguments are complicated structures; such as SECURITY_ATTRIBUTES and STARTUPINFO. The names for the HANDLE(s) in STARTUPINFO structure can be confusing. As you were confused. The steps that are involved in using CreateProcess. whether or not you need to immediately close the handles(nb: you don't). And the actual communication between the processes can cause deadlocks.
After using CreateProcess for years I did an extensive research and testing of the API. I then designed a library that encapsulates all that knowledge. This library is apart of my Jav/win32 library. This library is similar to Java's Process class.
I am not able to post every last bit of source code here. If you are interested in the source code I can email it to you. Keeping Cpp a goto for programming.
Jav/win32/Process.h
#ifndef JAV_WIN32_PROCESS_HPP
#define JAV_WIN32_PROCESS_HPP
#include <Jav/File.h>
#include <Jav/string/cstring.h>
#include <Jav/error/error.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/container/flat_map.hpp>
#include <sstream>
#include <Jav/win32/win32.h>
namespace Jav { namespace win32 {
/** class Process makes handling win32 CreateProcess a bit easier.
Parent Process refers to the process that is calling CreateProcess.
Child Process refers to the process being created.
Note: CreateProcess uses the parent process's CurrentDirectory and
EnvironmentVariable when deducing relative filepaths used in AppName and CommandLine String.
Thus I have no idea the purpose of passing a current directory and environment to the CreateProcess.
Note: Rather than using Process::setCurrentDir and Process::addEnv and Process::addParentEnv; which
seems to make no sense at all as CreateProcess doesn't use these settings to deduce relative filepaths
passed to CreateProcess; instead use Process::addPathArg to pass absolute filepaths on the commandline.
Note: Using Process::addEnv or Process::addParentEnv will temporarily adjust parent process Environment,
and pass the adjusted Environment to the child process as well. This allows you to set Path variable
so that CreateProcess deduces relative paths. However using Process::addPath is probably better.
Note: Asynchronous read and write functions are not implemented.
Probably synchronous read and writes are good enough.
To properly read and write the child process, be sure to know when it makes read and write request.
If you sync read a process that never writes to stdout or stderror your program will deadlock.
If you write to a process that didn't read stdinput that makes no sense. You are just filling up the buffer.
Also ensure to read out data written by child process so that the buffer is not clogged up causing deadlock.
*/
class ProcessA
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::string &l,const std::string &r)const; };
using EnvList = boost::container::flat_map<std::string,std::ostringstream,ICompare>;
std::ostringstream cmd;
EnvList env_list;
Jav::cstring app_name;
Jav::cstring current_dir;
STARTUPINFOA si = {sizeof(STARTUPINFOA)};
};
public:
ProcessA(const char *cmd=NULL,const char *current_dir=NULL,const char *app_name=NULL);
~ProcessA();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const char*);
bool read_async(void*,size_t);
bool readError_async(void*,size_t);
bool write_async(const void*,size_t);
bool write_async(const char*);
/** Set up process for creation */
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
bool addParentEnv();
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const char*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int); //SW_SHOW, SW_HIDE, etc
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstring toString();
Jav::cstring getError() { return error; }
private:
Jav::cstring buildEnvironment();
Jav::cstring getCombinedEnvVar(const std::string &name);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstring getEnvVarA(const char *name);
Jav::cstring FindFileExtExeA(const char *ext);
Jav::cstring FindFileNameExeA(const char *fname);
void setEnvironmentStringsA(const char *env);
class ProcessW
{
public:
enum { INVALID_EXIT_CODE = -1 };
enum { AUTO_KILL=1, WAIT=2, DEBUG=4, ADD_PARENT_ENV=8 };
struct Builder
{
struct ICompare { bool operator()(const std::wstring &l,const std::wstring &r)const; };
using EnvList = boost::container::flat_map<std::wstring,std::wostringstream,ICompare>;
std::wostringstream cmd;
EnvList env_list;
Jav::cstringw app_name;
Jav::cstringw current_dir;
STARTUPINFOW si = {sizeof(STARTUPINFOW)};
};
public:
ProcessW(const wchar_t *cmd=NULL,const wchar_t *current_dir=NULL,const wchar_t *app_name=NULL);
~ProcessW();
public:
bool launch();
int stop();
int wait(uint time=INFINITE);
void pause();
void resume();
bool isOpen();
bool isValid() { return info.hProcess; }
size_t getMsgSize();
size_t getErrorMsgSize();
size_t read(void*,size_t);
size_t readError(void*,size_t);
size_t write(const void*,size_t);
size_t write(const wchar_t*);
size_t write(const char*);
size_t read_async(void*,size_t);
size_t readError_async(void*,size_t);
size_t write_async(const void*,size_t);
size_t write_async(const wchar_t*);
size_t write_async(const char*);
void setAppName(const wchar_t*);
void setCurrentDir(const wchar_t*);
void addArg(const wchar_t*);
void addQuotedArg(const wchar_t*);
void addPathArg(const wchar_t *parent_dir,const wchar_t *child_name);
void addEnv(const wchar_t *var,const wchar_t *val);
bool addParentEnv();
void setAppName(const char*);
void setCurrentDir(const char*);
void addArg(const char*);
void addQuotedArg(const char*);
void addPathArg(const char *parent_dir,const char *child_name);
void addEnv(const char *var,const char *val);
template<class ...ARGS>
void addArgEx(const wchar_t*,ARGS ...rest);
template<class ...ARGS>
void addArgEx(const char*,ARGS ...rest);
void addArgEx(const wchar_t*);
void addArgEx(const char*);
void setConsoleTitle(const wchar_t*);
void setConsoleTitle(const char*);
void setConsoleWidth(int);
void setConsoleHeight(int);
void setConsoleTextAndFillColor(int);
void setWindowVisibility(int);
void setWindowXpos(int);
void setWindowYpos(int);
void setWindowWidth(int);
void setWindowHeight(int);
void setWaitTime(uint);
void setParentMode(uint);
void addParentMode(uint);
void removeParentMode(uint);
Jav::cstringw toString();
Jav::cstring getError() { return error; }
private:
Jav::cstringw buildEnvironment();
Jav::cstringw getCombinedEnvVar(const std::wstring &name);\
Jav::cstringw towstring(const char *s);
private:
PROCESS_INFORMATION info = {};
Jav::rFile m_read_end;
Jav::rFile m_error_end;
Jav::wFile m_write_end;
OVERLAPPED async_struct;
uint flags = 0;
uint waitTime = INFINITE;
Builder *builder;
Jav::Error error = "";
};
Jav::cstringw getEnvVarW(const wchar_t *name);
Jav::cstringw findFileExtExeW(const wchar_t *ext);
Jav::cstringw findFileNameExeW(const wchar_t *fname);
void setEnvironmentStringsW(const wchar_t *env);
///________________///___________
template<class ...ARGS>
void ProcessA::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
inline void ProcessA::addArgEx(const char *s)
{
builder->cmd << ' ' << s;
}
template<class ...ARGS>
void ProcessW::addArgEx(const wchar_t *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << first;
addArgEx(rest...);
}
template<class ...ARGS>
void ProcessW::addArgEx(const char *first,ARGS ...rest)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << towstring(first);
addArgEx(rest...);
}
inline void ProcessW::addArgEx(const wchar_t *s)
{
builder->cmd << ' ' << s;
}
inline void ProcessW::addArgEx(const char *s)
{
builder->cmd << ' ' << towstring(s);
}
}}
#endif // JAV_WIN32_PROCESS_HPP
src/Jav/win32/Process.cpp
#include <Jav/error/error.h>
#include <Jav/string.h>
#include <Jav/win32/Process.h>
#include <Jav/win32/debug.h>
#include <Jav/win32/registry.h>
#include <shlwapi.h>
namespace Jav { namespace win32 {
ProcessA::ProcessA(const char *cmd,const char *current_dir,const char *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessA::~ProcessA()
{
stop();
delete builder;
}
bool ProcessA::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsA();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableA(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsA();
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsA(parent_env);
FreeEnvironmentStringsA(child_env);
FreeEnvironmentStringsA(parent_env);
}
else
{
if( !CreateProcessA(builder->app_name,&cmd[0],NULL,NULL,true,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
/*
if( WaitForInputIdle(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::DefferedError("Error: Failed to wait for input idle\n");
return false;
}
if( WaitForSingleObject(info.hProcess,INFINITE) == WAIT_FAILED)
{
error = Jav::InternalError("InternalError: Failed to wait for object\n");
return false;
}
*/
return true;
}
int ProcessA::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessA::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessA::pause()
{
SuspendThread(info.hThread);
}
void ProcessA::resume()
{
ResumeThread(info.hThread);
}
bool ProcessA::isOpen()
{
if(!info.hProcess) { return false; }
/*
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
*/
switch(WaitForSingleObject(info.hProcess,0))
{
case WAIT_TIMEOUT: return true;
case WAIT_FAILED: throw Jav::Error("Error: Failed to wait for object\n");
case WAIT_ABANDONED: throw Jav::Error("Error: wait abondoned\n");
case WAIT_OBJECT_0: rep("WAIT_OBJECT_0"); return false;
default: throw Jav::Error("Error: Invalid Error Code, %s\n",__func__);
}
}
size_t ProcessA::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessA::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
size_t ProcessA::readError(void *buf,size_t sz)
{
return m_error_end.read(buf,sz);
}
size_t ProcessA::write(const void *data,size_t sz)
{
return m_write_end.write(data,sz);
}
size_t ProcessA::write(const char *data)
{
return m_write_end.write(data);
}
void ProcessA::setAppName(const char *name)
{
builder->app_name = name;
}
void ProcessA::setCurrentDir(const char *dir)
{
builder->current_dir = dir;
}
void ProcessA::addArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << arg;
}
void ProcessA::addQuotedArg(const char *arg)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"' << arg << '\"';
}
void ProcessA::addPathArg(const char *parent_dir,const char *child_name)
{
if(builder->cmd.tellp()) builder->cmd << ' ';
builder->cmd << '\"'<< parent_dir << '\\' << child_name << '\"';
}
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
pos == 0 ? stream << val : stream << ';' << val;
}
/*
void ProcessA::addEnv(const char *var,const char *val)
{
auto &stream = builder->env_list[var];
llong pos = stream.tellp();
if(pos == 0) stream << '\"' << val << '\"';
else
{
stream.seekp(pos-1);
stream << ';' << val << '\"';
}
}
*/
bool ProcessA::addParentEnv()
{
flags |= ADD_PARENT_ENV;
}
/*
bool ProcessA::addParentEnv()
{
LPCH parent_env = GetEnvironmentStringsA();
if(parent_env == NULL)
{
error = Jav::Error("Error: GetEnvironmentStrings failed\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
for(auto it = Jav::MultiStringIterator(parent_env); it.has(); it.next())
{
auto var_end = Jav::nextPosOf('=',it.get()).it;
if(!var_end)
{
error = Jav::Error("Error: Invalid EnvironmentString\n");
FreeEnvironmentStringsA(parent_env);
return false;
}
Jav::cstring var(it.get(),var_end++);
addEnv(var,var_end);
}
FreeEnvironmentStringsA(parent_env);
return true;
}
*/
void ProcessA::setConsoleTitle(const char *title)
{
builder->si.lpTitle = (char*)title;
}
void ProcessA::setConsoleWidth(int w)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwXCountChars = w;
}
void ProcessA::setConsoleHeight(int h)
{
builder->si.dwFlags |= STARTF_USECOUNTCHARS;
builder->si.dwYCountChars = h;
}
void ProcessA::setConsoleTextAndFillColor(int color)
{
builder->si.dwFlags |= STARTF_USEFILLATTRIBUTE;
builder->si.dwFillAttribute = color;
}
void ProcessA::setWindowVisibility(int visibility)
{
builder->si.dwFlags |= STARTF_USESHOWWINDOW;
builder->si.wShowWindow = visibility;
}
void ProcessA::setWindowXpos(int x)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwX = x;
}
void ProcessA::setWindowYpos(int y)
{
builder->si.dwFlags |= STARTF_USEPOSITION;
builder->si.dwY = y;
}
void ProcessA::setWindowWidth(int w)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwXSize = w;
}
void ProcessA::setWindowHeight(int h)
{
builder->si.dwFlags |= STARTF_USESIZE;
builder->si.dwYSize = h;
}
void ProcessA::setWaitTime(uint time)
{
waitTime = time;
}
void ProcessA::setParentMode(uint mode)
{
flags = mode;
}
void ProcessA::addParentMode(uint mode)
{
Jav::bitOn(flags,mode);
}
void ProcessA::removeParentMode(uint mode)
{
Jav::bitOff(flags,mode);
}
Jav::cstring ProcessA::toString()
{
if(!builder) return "NOTHING TO DEBUG";
std::ostringstream s;
auto env = buildEnvironment();
s << "AppName: " << builder->app_name << '\n';
if(env) s << "Env: " << Jav::MultiString{env} << '\n';
else s << "Env: " << "NO ENVIRONMENT" << '\n';
s << "CurrentDir: " << builder->current_dir << '\n';
s << "cmd: " << builder->cmd.str() << '\n';
return s.str().c_str();
}
Jav::cstring ProcessA::buildEnvironment()
{
if(!builder->env_list.size()) return NULL;
std::string env;
for(auto &elem : builder->env_list)
{
env += elem.first + '=' + elem.second.str() + '\0';
}
env += '\0';
return Jav::cstring(&env[0],&env[env.size()]);
}
Jav::cstring ProcessA::getCombinedEnvVar(const std::string &name)
{
Jav::cstring parent_val;
std::string child_val;
auto elem = builder->env_list.find(name);
if(elem != builder->env_list.end()) child_val = elem->second.str();
SetLastError(0);
auto sz = GetEnvironmentVariableA(name.c_str(),NULL,0);
if(sz == 0)
{
parent_val = GetLastError() ? NULL : "";
return child_val.empty() ? parent_val : Jav::cstring(child_val.c_str());
}
GetEnvironmentVariableA(name.c_str(),parent_val,sz);
if(child_val.empty()) return parent_val;
Jav::cstring val( strlen(parent_val + child_val.size() + 2) );
sprintf(val,"%s;%s",parent_val.str(),child_val.c_str());
return val;
}
bool ProcessA::Builder::ICompare::operator()(const std::string &l,const std::string &r)const
{
return boost::ilexicographical_compare<std::string,std::string>(l,r);
}
Jav::cstring getEnvVarA(const char *name)
{
SetLastError(0);
auto sz = GetEnvironmentVariableA(name,NULL,0);
if(sz == 0) return GetLastError() ? NULL : "";
Jav::cstring val(sz);
GetEnvironmentVariableA(name,val,sz);
return val;
}
Jav::cstring FindFileNameExe(const char *fname)
{
Jav::cstring exe_name(MAX_PATH);
auto e = (int)FindExecutableA(fname,NULL,exe_name);
if(e <= 32) throw Jav::Error("Error: unable to find association\n");
return exe_name;
}
/*
Jav::cstring FindFileExtExe(const char *ext)
{
Jav::cstring exe_name;
Jav::win32::RegistryKeyReader key(".cpp",HKEY_CLASSES_ROOT);
if(!key || !key.getString(NULL,exe_name))
throw Jav::DefferedError("Error: No program Associated with file");
return exe_name;
}
*/
/* Note uses AssocQueryString which doesn't seem to be compatible on winxp*/
Jav::cstring FindFileExtExe(const char *ext)
{
DWORD sz=0;
auto hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,NULL,&sz);
if( hr != S_FALSE)
throw Jav::InternalError("Error Failed to obtain extension association name length\n");
Jav::cstring exe_name(sz);
hr = AssocQueryStringA(ASSOCF_INIT_IGNOREUNKNOWN,ASSOCSTR_EXECUTABLE,ext,NULL,exe_name,&sz);
if( hr != S_OK)
throw Jav::InternalError("Error Failed to obtain extension association\n");
return exe_name;
}
void setEnvironmentStringsA(const char *env)
{
if(env == NULL) throw Jav::Error("Error: environment string is null\n");
for(auto it = Jav::MultiStringIterator(env); it.has(); )
{
auto begin = it.get();
auto end = it.next('\0');
auto var_end = Jav::nextPosOf('=',{begin,end});
if(!var_end) throw Jav::Error("Error: Invalid EnvironmentString\n");
Jav::cstring var(begin,var_end.it++);
SetEnvironmentVariableA(var,var_end);
}
}
ProcessW::ProcessW(const wchar_t *cmd,const wchar_t *current_dir,const wchar_t *app_name)
: builder(new Builder())
{
if(cmd) builder->cmd << cmd;
builder->app_name = app_name;
builder->current_dir = current_dir;
}
ProcessW::~ProcessW()
{
stop();
delete builder;
}
bool ProcessW::launch()
{
Jav::rFile child_read_end;
Jav::wFile child_error_end;
Jav::wFile child_write_end;
if(!Jav::createPipe(m_read_end,child_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
//Jav::createPipe(m_error_end,child_error_end);
if(!Jav::createPipe(child_read_end,m_write_end)) throw InternalError("InternalError: Failed to create pipe\n");
builder->si.dwFlags |= STARTF_USESTDHANDLES;
builder->si.hStdInput = (HANDLE)child_read_end;
builder->si.hStdOutput = (HANDLE)child_write_end;
builder->si.hStdError = (HANDLE)child_write_end;
auto env = buildEnvironment();
auto cmd = builder->cmd.str();
if(flags & ADD_PARENT_ENV)
{
auto parent_env = GetEnvironmentStringsW();
for(auto &elem : builder->env_list)
{
auto val = getCombinedEnvVar(elem.first);
SetEnvironmentVariableW(elem.first.c_str(),val);
}
auto child_env = GetEnvironmentStringsW();
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,child_env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
setEnvironmentStringsW(parent_env);
FreeEnvironmentStringsW(child_env);
FreeEnvironmentStringsW(parent_env);
}
else
{
if( !CreateProcessW(builder->app_name,&cmd[0],NULL,NULL,false,0,env,builder->current_dir,&builder->si,&info) )
{
error = Jav::DefferedError("Error: Failed to create process\nCause: %s",Jav::win32::errorString().str());
return false;
}
}
if( !(flags & DEBUG) )
{
delete builder;
builder = NULL;
}
return true;
}
int ProcessW::wait(uint time)
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
int ProcessW::stop()
{
DWORD exitCode = INVALID_EXIT_CODE;
if(!info.hProcess) return exitCode;
if(flags & WAIT) WaitForSingleObject(info.hProcess,waitTime);
if(!GetExitCodeProcess(info.hProcess,&exitCode)) error = Jav::InternalError("InternalError: %s: Failed to get exit code\n",__func__);
if(flags & AUTO_KILL && exitCode == STILL_ACTIVE) TerminateProcess(info.hProcess,1);
CloseHandle(info.hThread);
CloseHandle(info.hProcess);
info = {};
return exitCode;
}
void ProcessW::pause()
{
SuspendThread(info.hThread);
}
void ProcessW::resume()
{
ResumeThread(info.hThread);
}
bool ProcessW::isOpen()
{
if(!info.hProcess) return false;
DWORD exitCode;
if(!GetExitCodeProcess(info.hProcess,&exitCode))
throw Jav::InternalError("InternalError: Failed to GetExitCodeProcess\n");
return exitCode == STILL_ACTIVE;
}
size_t ProcessW::getMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_read_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::getErrorMsgSize()
{
DWORD avail;
if(!PeekNamedPipe(m_error_end,NULL,0,NULL,&avail,NULL))
{
error = Jav::Error("Error: Failed to peekNamedPipe\nCause: %s\n",errorString().str());
return 0;
}
return avail;
}
size_t ProcessW::read(void *buf,size_t sz)
{
return m_read_end.read(buf,sz);
}
...
...
...
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