I want to show the stdout of commandline program in MFC edit control. So I start a worker thread by AfxBeginThread to update the UI by PostMessage (this part works great), and the worker thread communicates with the commandline child process by a pipe. But my worker thread cannot read anything from the pipe (ReadFile always return FALSE) and my worker cannot quit even if the child process quits. So please help me.
Here is my code.
create child process part:
BOOL CMFCApplication3Dlg::execCmd(LPCSTR pCmdArg)
{
//ASSERT(s_hCmdProcess == NULL);
if (s_hCmdProcess != NULL)
{
return FALSE;
}
STARTUPINFO si; // specifies startup parameters for child process.
ZeroMemory(&si, sizeof(STARTUPINFO));
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; // STARTF_USESTDHANDLES is Required.
si.hStdOutput = s_cmdout_ChildSide; // Requires STARTF_USESTDHANDLES in dwFlags.
si.hStdError = s_cmdout_ChildSide; // Requires STARTF_USESTDHANDLES in dwFlags.
// si.hStdInput remains null.
si.wShowWindow = SW_HIDE; // Prevents cmd window from flashing. Requires STARTF_USESHOWWINDOW in dwFlags.
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
// Create the child process.
BOOL result = CreateProcess(
NULL,
(LPSTR)pCmdArg, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // TRUE=handles are inherited. Required.
CREATE_NEW_CONSOLE, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&si, // __in, STARTUPINFO pointer
&pi); // __out, receives PROCESS_INFORMATION
if (result)
{
s_hCmdProcess = pi.hProcess;
s_hCmdProcessThread = pi.hThread;
}
return result;
}
create pipe part:
LRESULT CMFCApplication3Dlg::createPipe()
{
ASSERT(s_cmdout_ChildSide == NULL && s_cmdout_MainSide == NULL);
if (s_cmdout_ChildSide != NULL || s_cmdout_MainSide != NULL) return FALSE;
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
return CreatePipe(&s_cmdout_MainSide, &s_cmdout_ChildSide, &saAttr, 0);
}
the worker thread part:
void CMFCApplication3Dlg::startWorkThread()
{
if (s_hThread == NULL)
{
if (s_hThreadEvent != NULL) CloseHandle(s_hThreadEvent);
s_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
//s_hThread = AfxBeginThread(TestThreadProc, this);
s_hThread = AfxBeginThread(ReadCmdOutProc, this);
}
}
UINT ReadCmdOutProc(LPVOID pParam)
{
CWnd *hDlg = (CWnd *)pParam;
//start the child process
BOOL result = CMFCApplication3Dlg::execCmd("ping 127.0.0.1 -n 99");
ASSERT(CMFCApplication3Dlg::s_hCmdProcess != NULL);
for(;;)
{
DWORD dwRetVal_cmd;
//wait for child process quit.
dwRetVal_cmd = WaitForSingleObject(CMFCApplication3Dlg::s_hCmdProcess,100);
DWORD dwRead = 0;
CHAR chBuf[4096];
BOOL readResult = ReadFile(CMFCApplication3Dlg::s_cmdout_MainSide, chBuf, 4096, &dwRead, NULL);
if (readResult)
{
chBuf[dwRead] = 0;
TRACE("read: %s\n",chBuf);
CString* cmdread = new CString(chBuf, dwRead);
//send ui thread update message.
MsgWrapper::Post(hDlg, WM_APP + 11, (WPARAM)cmdread);
}
if (dwRetVal_cmd != WAIT_TIMEOUT)
{
TRACE("thread quit\n");
//release the resource
CMFCApplication3Dlg::releaseCmd();
GetExitCodeThread(CMFCApplication3Dlg::s_hThread, &dwExitCode);
AfxEndThread(dwExitCode);
CMFCApplication3Dlg::s_hThread = NULL;
break;
}
}
}
Related
In the past, when not operating as a debugger, I have used this approach to inject a DLL and Create Thread In a Process which has worked well for me. Note: I need this to work on Windows XP 32-bit only (Although prefer methods that also work on latested OS):
#include <iostream>
#include <Windows.h>
#include <Psapi.h>
#include <pathcch.h>
#include "log.h"
#include <wchar.h>
#pragma comment(lib,"Pathcch.lib")
typedef void (WINAPI* PHookInit)();
HMODULE WINAPI GetRemoteModuleHandle(HANDLE hProcess, LPCWSTR lpModuleName)
{
HMODULE* ModuleArray = NULL;
DWORD ModuleArraySize = 100;
DWORD NumModules = 0;
WCHAR lpModuleNameCopy[MAX_PATH] = { 0 };
WCHAR ModuleNameBuffer[MAX_PATH] = { 0 };
if (lpModuleName == NULL) return NULL;
ModuleArray = new HMODULE[ModuleArraySize];
if (ModuleArray == NULL) return NULL;
if (!EnumProcessModulesEx(hProcess, ModuleArray,
ModuleArraySize * sizeof(HMODULE), &NumModules, LIST_MODULES_ALL))
{
DWORD dwResult = GetLastError();
LOG_E("Unable to get modules in process Error %i", dwResult);
}
else
{
NumModules /= sizeof(HMODULE);
if (NumModules > ModuleArraySize)
{
delete[] ModuleArray;
ModuleArray = NULL;
ModuleArray = new HMODULE[NumModules];
if (ModuleArray != NULL)
{
ModuleArraySize = NumModules;
if (EnumProcessModulesEx(
hProcess,
ModuleArray,
ModuleArraySize * sizeof(HMODULE),
&NumModules,
LIST_MODULES_ALL))
{
NumModules /= sizeof(HMODULE);
}
}
}
}
for (DWORD i = 0; i <= NumModules; ++i)
{
GetModuleBaseNameW(hProcess, ModuleArray[i],
ModuleNameBuffer, MAX_PATH);
LOG_I("Module = '%s'", ModuleNameBuffer);
if (_wcsicmp(ModuleNameBuffer, lpModuleName) == 0)
{
LOG_I("Target module found!");
HMODULE TempReturn = ModuleArray[i];
delete[] ModuleArray;
return TempReturn;
}
}
if (ModuleArray != NULL)
delete[] ModuleArray;
return NULL;
}
int wmain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, INT nCmdShow)
{
LPWSTR* argv;
int argc;
argv = CommandLineToArgvW(GetCommandLineW(), &argc);
LOG_I(L"LaunchAndInject Started");
wchar_t CurrentProcessDirectory[MAX_PATH];
wchar_t TargetDllFilename[MAX_PATH];
#ifdef _WIN64
wchar_t TargetDllName[] = L"HookInit64.dll";
#else
wchar_t TargetDllName[] = L"HookInit32.dll";
#endif
char TargetFunctionName[] = "HookInit";
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwTimeOut = 60000;
if (argc < 2)
{
LOG_E(L"No command line parameters specified.");
return 1;
}
wchar_t* cmd_pos = wcsstr(GetCommandLine(), argv[1]) - 1;
if (cmd_pos)
{
if (cmd_pos[0] != L'"')
{
cmd_pos += 1;
}
}
LOG_I(L"Command Line='%s'", cmd_pos);
DWORD dwResult = GetModuleFileNameW(NULL, CurrentProcessDirectory, MAX_PATH);
PathCchRemoveFileSpec(CurrentProcessDirectory, MAX_PATH);
PathCchCombine(TargetDllFilename, MAX_PATH, CurrentProcessDirectory, TargetDllName);
LOG_I(L"Current Directory='%s' Result='%i'", CurrentProcessDirectory, dwResult);
LOG_I(L"Target DLL='%s'", TargetDllFilename);
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_SUSPENDED, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
LOG_I(L"Suspended Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
// load DLL in this process first so we can calculate function offset
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
pi.hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
pi.hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
pi.hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(pi.hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
LOG_I("Resuming threads in target process");
ResumeThread(pi.hThread);
LOG_I("Process Resumed. Waiting for process to exit");
dwResult = WaitForSingleObject(pi.hProcess, INFINITE);
DWORD exitCode = 0;
if (GetExitCodeProcess(pi.hProcess, &exitCode))
{
LOG_I("Process Terminated with exit code %i", exitCode);
}
else
{
LOG_W("Process terminated, unable to determine Exit Code");
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
However in this case I need to capture various debug events of process, and hook the process via Image Execution Debugger registry key (as I don't have control of its launch), including for child processes. While the inject code works fine with previous approach, when launched as a debugger I'm trying to work out how to create the remote thread (and have it complete) before resuming main application execution. While I can create the remote thread fine, it hangs when trying to wait for its completion when using the approach below. I'm trying to work out what method to use to create my remote thread and wait for it to complete before resuming main application.
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
cmd_pos, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
DEBUG_PROCESS, // Debug
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si,
&pi)
)
{
dwResult = GetLastError();
LOG_E(L"CreateProcess Failed with Error #%i", dwResult);
return 1;
}
DebugSetProcessKillOnExit(TRUE);
DebugActiveProcess(pi.dwProcessId);
DEBUG_EVENT DebugEv = { 0 };
DWORD dwContinueStatus = DBG_CONTINUE; // exception continuation
LOG_I(L"Debug Process created with PID '%i'", pi.dwProcessId);
LOG_I("Loading Target DLL");
/*
// load DLL in this process first so we can calculate function offset
*/
for (;;)
{
// Wait for a debugging event to occur. The second parameter indicates
// that the function does not return until a debugging event occurs.
WaitForDebugEvent(&DebugEv, INFINITE);
// Process the debugging event code.
switch (DebugEv.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
// Process the exception code. When handling
// exceptions, remember to set the continuation
// status parameter (dwContinueStatus). This value
// is used by the ContinueDebugEvent function.
OutputDebugString(L"EXCEPTION\r\n");
switch (DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_BREAKPOINT:
// First chance: Display the current
// instruction and register values.
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
case EXCEPTION_SINGLE_STEP:
// First chance: Update the display of the
// current instruction and register values.
break;
case DBG_CONTROL_C:
// First chance: Pass this on to the system.
// Last chance: Display an appropriate error.
break;
default:
// Handle other exceptions.
break;
}
break;
case CREATE_THREAD_DEBUG_EVENT:
OutputDebugString(L"CREATETHREAD\r\n");
// dwContinueStatus = OnCreateThreadDebugEvent(&DebugEv);
break;
case CREATE_PROCESS_DEBUG_EVENT:
dwContinueStatus = OnCreateProcessDebugEvent(&DebugEv);
break;
case EXIT_THREAD_DEBUG_EVENT:
// Display the thread's exit code.
OutputDebugString(L"EXITTHREAD\r\n");
// dwContinueStatus = OnExitThreadDebugEvent(&DebugEv);
break;
case EXIT_PROCESS_DEBUG_EVENT:
// Display the process's exit code.
OutputDebugString(L"EXITPROCESS\r\n");
// dwContinueStatus = OnExitProcessDebugEvent(&DebugEv);
break;
case LOAD_DLL_DEBUG_EVENT:
// Read the debugging information included in the newly
// loaded DLL. Be sure to close the handle to the loaded DLL
// with CloseHandle.
OutputDebugString(L"LOADDLL\r\n");
// dwContinueStatus = OnLoadDllDebugEvent(&DebugEv);
break;
case UNLOAD_DLL_DEBUG_EVENT:
// Display a message that the DLL has been unloaded.
OutputDebugString(L"UNLOADDLL\r\n");
// dwContinueStatus = OnUnloadDllDebugEvent(&DebugEv);
break;
case OUTPUT_DEBUG_STRING_EVENT:
OutputDebugString(L"OUTPUTDEBUG\r\n");
// Display the output debugging string.
// dwContinueStatus = OnOutputDebugStringEvent(&DebugEv);
break;
case RIP_EVENT:
OutputDebugString(L"RIP\r\n");
// dwContinueStatus = OnRipEvent(&DebugEv);
break;
}
// Resume executing the thread that reported the debugging event.
OutputDebugString(L"CONTINUE\r\n");
ContinueDebugEvent(DebugEv.dwProcessId,
DebugEv.dwThreadId,
dwContinueStatus);
}
DWORD OnCreateProcessDebugEvent(const LPDEBUG_EVENT DebugEv)
{
DWORD dwResult;
HMODULE hModuleTargetDll = LoadLibraryW(TargetDllFilename);
__int64 iTargetProcAddress = 0;
__int64 iTargetOffset = 0;
if (hModuleTargetDll != NULL)
{
iTargetProcAddress = (__int64)GetProcAddress(hModuleTargetDll, TargetFunctionName);
iTargetOffset = iTargetProcAddress - (__int64)hModuleTargetDll;
LOG_I("Function Target Offset = %i", iTargetOffset);
}
HMODULE hModuleKernel32 = GetModuleHandle(L"kernel32.dll");
LPVOID pLoadLibraryAddress = NULL;
if (hModuleKernel32 != NULL)
{
pLoadLibraryAddress = (LPVOID)GetProcAddress(hModuleKernel32, "LoadLibraryW");
}
else
{
LOG_E("Unable to get module handle for kernel32.dll");
}
if (pLoadLibraryAddress == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to find LoadLibraryW in Kernel32.dll Error: %i", dwResult);
}
// allocate space for LoadLibrary arguments in target process
size_t iTargetDllSize = (wcslen(TargetDllFilename) + 1) * sizeof(wchar_t);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, DebugEv->dwProcessId);
LPVOID pLoadLibraryArguments = (LPVOID)VirtualAllocEx(
hProcess,
NULL,
iTargetDllSize,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pLoadLibraryArguments == NULL) {
dwResult = GetLastError();
LOG_E(L"ERROR: Unable to allocate %i bytes in target process Error: %i",
iTargetDllSize,
dwResult);
}
else
{
if (!WriteProcessMemory(
hProcess,
pLoadLibraryArguments,
TargetDllFilename,
iTargetDllSize,
NULL))
{
dwResult = GetLastError();
LOG_E("Unable to write bytes into target process address space. Error %i", dwResult);
}
else
{
LOG_I("LoadLibrary Arguments Successfully written to target process address space.");
HANDLE hThread = NULL;
if (pLoadLibraryAddress != NULL)
{
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)pLoadLibraryAddress,
pLoadLibraryArguments,
NULL,
NULL);
}
if (hThread == NULL) {
dwResult = GetLastError();
LOG_E("The remote thread calling LoadLibrary could not be created. Error %i", dwResult);
}
else {
LOG_I("Remote Thread for LoadLibrary successfully created.");
ResumeThread(hThread);
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for LoadLibrary Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for LoadLibrary in hung state");
}
HMODULE hInjected = GetRemoteModuleHandle(hProcess, TargetDllName);
PHookInit pHookInit = NULL;
if (hInjected == NULL)
{
LOG_E("Unable to get module handle in target process");
}
else
{
pHookInit = (PHookInit)((__int64)hInjected + iTargetOffset);
}
if (pHookInit != NULL)
{
LOG_I("Running HookInit function!");
hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pHookInit, NULL, NULL, NULL);
if (hThread == NULL)
{
dwResult = GetLastError();
LOG_E("The remote thread calling HookInit could not be created. Error %i", dwResult);
}
else
{
LOG_I("HookInit function started!");
dwResult = WaitForSingleObject(hThread, dwTimeOut);
if (dwResult == WAIT_FAILED)
{
dwResult = GetLastError();
LOG_I("Remote Thread for HookInit Failed Error %i", dwResult);
}
if (dwResult == WAIT_TIMEOUT)
{
LOG_E("Remote Thread for HookInit in hung state");
}
}
}
}
}
}
return DBG_CONTINUE;
}
I am using VS 2013 Professional in an MFC project
I have been using my software to receive data from the com port for some time, but recently needed to add transmission capability
The Init code is:
BOOL PASCAL FAR SetupConnect(pCONNECTION pCon, pCOMCONFIG pCfg)
{
DCB dcb;
pSERBUF pSB = pCon->BufStruct;
// pSERBUF *ppSB = (pSERBUF*)pCon->BufStruct;
// pSB = *ppSB;
dcb.DCBlength = sizeof(DCB);
CheckComs(); // Gets available COM ports
pCon->Port = pNames[0].PortNames[3] - 0x30;
if (pCon->BufStruct == NULL) // This is a personal Communications structure
{ // Init
pCon->hSB = GlobalAlloc(GHND, sizeof(SERBUF));
if (pCon->hSB == NULL)
{
// return INVALID_HANDLE_VALUE;
return 0;
}
pSB = (pSERBUF)GlobalLock(pCon->hSB);
pSB->idComDev = INVALID_HANDLE_VALUE;
pCon->BufStruct = pSB;
}
else return (0);
if (pSB->idComDev == INVALID_HANDLE_VALUE)
{
pSB->idComDev = CreateFile(pNames[0].PortNames, GENERIC_READ | GENERIC_WRITE,
0, //exclusive access
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
}
// Current configuration
GetCommState(pSB->idComDev, &dcb);
// Setup baudrate, parity, etc.
dcb.BaudRate = pCfg->dwBaudRate;
dcb.ByteSize = pCfg->bDataBits;
dcb.Parity = pCfg->bParity;
dcb.StopBits = pCfg->bStopBits;
// Setup Flow Control
dcb.fOutxDsrFlow = pCfg->handshake_DTR;
dcb.fDtrControl = DTR_CONTROL_ENABLE; // DTR high while port open
dcb.fOutxCtsFlow = pCfg->handshake_RTS;
dcb.fRtsControl = RTS_CONTROL_DISABLE; // Toggle RTS with EscapeCommFunction
// XON/XOFF Not Used
dcb.fInX = FALSE;
dcb.fOutX = FALSE;
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
//return TRUE if everything looks cool
return (SetCommState(pSB->idComDev, &dcb));
}
And:
CSerCom::CSerCom()
{
pCon = &Con;
pCfg = &Cfg;
m_SerHwnd = this;
pCfg->dwBaudRate = 115200;
pCfg->bDataBits = 8;
pCfg->bParity = NOPARITY;
pCfg->bStopBits = TWOSTOPBITS;
// here
SetupConnect(pCon, pCfg);
pSERBUF pSB = pCon->BufStruct; // pSB is set in SetUpConnect
if (pSB->idComDev == INVALID_HANDLE_VALUE)
{
// device open failure
// hardware not there or someone else controls it!
GlobalUnlock(pCon->hSB);
GlobalFree(pCon->hSB);
pCon->BufStruct = NULL;
// TODO stop this from going any further
HandleFailure();
}
else // Only continue if Port is available
{
// Clear Buffer
SetupComm(pSB->idComDev, 4096, 4096);
PurgeComm(pSB->idComDev, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
// create the overlapped events
memset(&(pSB->osRead), 0, sizeof(OVERLAPPED));
memset(&(pSB->osWrite), 0, sizeof(OVERLAPPED));
pSB->osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
pSB->osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if ((pSB->osRead.hEvent == NULL) || (pSB->osWrite.hEvent == NULL))
{
ReleaseNetResources(pCon);
CloseHandle(pSB->idComDev);
pSB->idComDev = INVALID_HANDLE_VALUE;
HandleFailure();
// return (pSB->idComDev);
}
// allocate & lock the mem
// (used to contain data points to & from the MODBUS
// as well as the receive buffer for incoming serial data)
pSB->hRcv = GlobalAlloc(GHND, MAX_RX_LEN);
if (pSB->hRcv == NULL)
{
ReleaseNetResources(pCon);
CloseHandle(pSB->idComDev);
pSB->idComDev = INVALID_HANDLE_VALUE;
HandleFailure();
// return (pSB->idComDev);
}
pSB->pRcv = (char *)GlobalLock(pSB->hRcv);
pSB->hTx = (char *)GlobalAlloc(GHND, MAX_TX_LEN);
if (pSB->hTx == NULL)
{
ReleaseNetResources(pCon);
CloseHandle(pSB->idComDev);
pSB->idComDev = INVALID_HANDLE_VALUE;
HandleFailure();
// return (pSB->idComDev);
}
pSB->pTx = (char *)GlobalLock(pSB->hTx);
// remember the setup params
pSB->TimeOut = 3; //CalculateTimeOut(pCfg->dwBaudRate);
// pSB->TimerId = TimerId;
// initialize the status counters
// pSB->ValidCt = 0;
// pSB->InvalidCt = 0;
pSB->RxInIdx = 0;
// pSB->RTS_Delay[0] = pCfg->RTS_Delay[0];
// pSB->RTS_Delay[1] = pCfg->RTS_Delay[1];
pSB->RTS_Delay[0] = 100;
pSB->RTS_Delay[1] = 100;
// setup the Comm Timeouts
CommTimeOuts.ReadIntervalTimeout = 0xffffffff;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
CommTimeOuts.ReadTotalTimeoutConstant = 1000;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
CommTimeOuts.WriteTotalTimeoutConstant = 1000;
SetCommTimeouts(pSB->idComDev, &CommTimeOuts);
// if everything looks good to here
// create the Receive Thread & return the CONNECT handle
pSB->hIOThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)SerProc,
(LPVOID)pCon,
0,
&dwThreadID);
if (pSB->hIOThread == NULL)
{
ReleaseNetResources(pCon);
CloseHandle(pSB->idComDev);
pSB->idComDev = INVALID_HANDLE_VALUE;
HandleFailure();
// return (pSB->idComDev);
}
hIOT = pSB->hIOThread;
}
}
So with that set up, I enter a thread loop in which I have the following
// wait indefinitely for somthing to happen
WaitCommEvent(pSB->idComDev, &dwEvtMask, NULL);
// Catch Rx event
if ((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
{
Edit1_txt.Format(_T("Rx'd"));
E1->SetWindowText(Edit1_txt);
CMFCView->UpdateWindow();
// only try to read number of bytes in queue
ClearCommError(pSB->idComDev, &dwErrorFlags, &ComStat);
dwLength = ComStat.cbInQue;
// Read data bytes into connection Rcv Buffer at current RxInIdx
if (dwLength > 0)
{
fReadStat = ReadFile(pSB->idComDev,
&(pSB->pRcv[pSB->RxInIdx]),
dwLength,
&bytesread,
&(pSB->osRead));
if (!fReadStat)
{
if (GetLastError() == ERROR_IO_PENDING)
{
// We have to wait for read to complete.
while (!GetOverlappedResult(pSB->idComDev,
&(pSB->osRead), &bytesread, FALSE))
{
dwErrorFlags = GetLastError();
if (dwErrorFlags != ERROR_IO_INCOMPLETE)
// an error occurred, try to recover
ClearCommError(pSB->idComDev, &dwErrorFlags, &ComStat);
}
}
else
{
// some other error occurred
dwLength = 0;
ClearCommError(pSB->idComDev, &dwErrorFlags, &ComStat);
}
} // End of Read Error
} // End of Read Char
if (ComStat.cbInQue < 500)
{
// update the receive index
pSB->RxInIdx += dwLength;
wSleepime = GetTickCount(); // hkk 7/16/99 for console app
ParseAPI(pSB);
}
else
ComStat.cbInQue = 0;
}
// At some point in the program pSB->TxOutIdx is set to some positive value
if (pSB->TxOutIdx > 0)
{
dwLength = pSB->TxOutIdx;
fWriteStat = WriteFile(pSB->idComDev,
&(pSB->pTx[pSB->TxOutIdx]),
dwLength,
&byteswritten,
&(pSB->osWrite));
if (!fWriteStat)
{
if (GetLastError() == ERROR_IO_PENDING)
{
while (!GetOverlappedResult(pSB->idComDev,
&(pSB->osWrite), &byteswritten, FALSE))
{
dwErrorFlags = GetLastError();
if (dwErrorFlags != ERROR_IO_INCOMPLETE)
// an error occurred, try to recover
ClearCommError(pSB->idComDev, &dwErrorFlags, &ComStat);
}
}
pSB->TxOutIdx -= byteswritten;
}
}
}
This detects the Tx buffer full, (pSB->TxOutIdx > 0)
and transmits the data
The transmit fails with an IO pending error, but after execution of GetOverlappedResult, bytes written show the length desired.
However, no data comes out the port. I have checked, and the port found and used is correct.
Wassup?
I want to start "telnet" through cmd.exe and write the result of this command execution into file. When I run "ipconfig" command I get all information I need, but after running of "telnet" command I get just empty file.
Here is my code:
#include "windows.h"
#include "iostream"
void SaveResult(const wchar_t *fileName, const wchar_t *commandLine)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE h = CreateFile(fileName, FILE_WRITE_DATA, FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return;
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { sizeof(si) };
si.dwFlags |= STARTF_USESTDHANDLES;
si.hStdInput = NULL;
si.hStdError = h;
si.hStdOutput = h;
wchar_t *writable_cmdline = _wcsdup(commandLine);
BOOL success = CreateProcess(NULL, writable_cmdline,
NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
bool finished = false;
//wait for 1 second
for (int i = 0; i < 10; i++)
{
if (WaitForSingleObject(pi.hProcess, 100) <= 0)
{
finished = true;
break;
}
}
if (success)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
CloseHandle(h);
free(writable_cmdline);
if (!finished)
printf("Process didn't finish\n");
}
int main()
{
SaveResult(L"telnet.txt", L"C:\\windows\\system32\\cmd.exe /c telnet test.com");
SaveResult(L"ipconfig.txt", L"C:\\windows\\system32\\cmd.exe /c ipconfig");
return 0;
}
Have you run telnet command directly in cmd console? Is there anything output? if so, The empty file may because the buffer has not been refreshed to the file. As#David pointed out,
you just wait for 1s, but if there be more than 1s to connect, then you will close the handler without refresh the buffer. Try to add the FlushFileBuffers(h) before CloseHandle(h).
For WaitForSingleObject, you could try WaitForSingleObject(pi.hProcess, INFINITE), wait until the telnet process exit.
I want to get the primary token so that I can get the access of OpenInputDesktop() and do my necessary things.
I browsed all over the sites for help and found the conclusive code as below but I got an error on calling DuplicateTokenEx() is 998 which means invalid access to memory location.
HANDLE GetCurrentUserToken()
{
HANDLE currentToken = 0;
PHANDLE primaryToken = 0;
unsigned int winlogonPid = 0;
int dwSessionId = 0;
PHANDLE hUserToken = 0;
PHANDLE hTokenDup = 0;
PWTS_SESSION_INFO pSessionInfo = 0;
DWORD dwCount = 0;
WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
&pSessionInfo, &dwCount);
//TestLog("Error on WTSEnumerateSessions(): %d",GetLastError());
int dataSize = sizeof(WTS_SESSION_INFO);
for (DWORD i = 0; i < dwCount; ++i)
{
WTS_SESSION_INFO si = pSessionInfo[i];
if (WTSActive == si.State)
{
dwSessionId = si.SessionId;
break;
}
}
WTSFreeMemory(pSessionInfo);
array<Process^>^localByName = Process::GetProcessesByName( "winlogon" );
for (int i=0;i<localByName->Length;i++)
{
Process ^ p1 = (Process^)(localByName->GetValue(i));
if ((unsigned int)p1->SessionId == dwSessionId)
{
winlogonPid = (unsigned int)p1->Id;
}
}
// obtain a handle to the winlogon process
HANDLE hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
TestLog("Error on OpenProcess():",GetLastError());
// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ¤tToken))
{
TestLog("Error on OpenProcessToken():",GetLastError());
CloseHandle(hProcess);
return false;
}
BOOL bRet ;
// bRet = DuplicateTokenEx(currentToken,
// MAXIMUM_ALLOWED /*TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS*/,
// NULL/*0*/,
// SecurityImpersonation, TokenImpersonation, primaryToken);
bRet = DuplicateTokenEx(currentToken,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
NULL, SecurityImpersonation,
TokenPrimary, primaryToken);
TestLog("Error on DuplicateTokenEx():",GetLastError());
TestLog("return value of DuplicateTokenEx()",bRet);
int errorcode = GetLastError();
if (bRet == false)
{
return 0;
}
return primaryToken;
}
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hello World");
TestLog("**Start TestLaunchExeOneTime**",0);
HANDLE hTokenNew = NULL, hTokenDup = NULL;
HMODULE hmod = LoadLibrary(L"kernel32.dll");
hTokenDup = GetCurrentUserToken();
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si,0,sizeof(STARTUPINFO));
si.cb = sizeof( STARTUPINFO );
si.lpDesktop = L"winsta0\\default";
LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
HMODULE hModule = LoadLibrary(L"Userenv.dll");
if(hModule )
{
if(CreateEnvironmentBlock(&pEnv,hTokenDup,FALSE))
{
//WriteToLog("CreateEnvironmentBlock Ok");
dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT;
}
else
{
TestLog("Error on CreateEnvironmentBlock():",GetLastError());
pEnv = NULL;
}
}
//
if ( !CreateProcessAsUser( hTokenDup,
NULL,
L"C:\\temp\\DesktopDuplicationmilliseconds.exe",
NULL,
NULL,
FALSE,
dwCreationFlag,
pEnv,
NULL,
&si,
&pi
))
{
}
else
{
TestLog("Error on CreateProcessAsUser():",GetLastError());
// printf("error : %d",GetLastError());
}
return 0;
}
You haven't allocated any memory for the primary token. The primaryToken variable is a pointer to a handle, but you haven't actually pointed it to anything. (You've also declared GetCurrentUserToken as a function that returns a handle, but are actually returning a pointer to a handle.)
You need to either explicitly allocate the memory for the handle:
primaryToken = malloc(sizeof(HANDLE));
[...]
return *primaryToken;
or, more sensibly, define primaryToken as a HANDLE rather than a pointer and pass a reference to it in the appropriate place:
HANDLE primaryToken;
[...]
bRet = DuplicateTokenEx(currentToken,
TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
NULL, SecurityImpersonation,
TokenPrimary, &primaryToken);
I am seeking example code:
For a service calls CreateProcessAsUser() I want the process to run in the user's session, not session 0
thus far the created process is only running like a service in session 0
This was stripped from some old code that launched a console app from a service. It worked under NT4 but I haven't tested it with a modern version of Windows so can't guarantee it will work as it did on NT4.
EDIT: No, that's not going to work as-is. You need to add the code found here to create a desktop, set the SID, etc.
if (!LogonUser(userId,
domain,
password,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT,
&hUserToken))
{
return GetLastError();
}
if (!ImpersonateLoggedOnUser(hUserToken))
{
DWORD rc = GetLastError();
CloseHandle(hUserToken);
return rc;
}
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.cb = sizeof(si);
rc = CreateProcessAsUser(hUserToken, // user token
0, // app name
"foo.exe", // command line
0, // process attributes
0, // thread attributes
FALSE, // don't inherit handles
DETACHED_PROCESS, // flags
0, // environment block
0, // current dir
&si, // startup info
&pi); // process info gets put here
if (!rc)
{
DWORD rc = GetLastError();
RevertToSelf();
CloseHandle(hUserToken);
return rc;
}
RevertToSelf();
CloseHandle(hUserToken);
return 0;
I know this is an ancient post but I happen to be working on this so here's some code that works for me.
Determine the session ID of the currently logged-on user
DWORD GetCurrentSessionId ()
{
WTS_SESSION_INFO *pSessionInfo;
DWORD n_sessions = 0;
BOOL ok = WTSEnumerateSessions (WTS_CURRENT_SERVER, 0, 1, &pSessionInfo, &n_sessions);
if (!ok)
return 0;
DWORD SessionId = 0;
for (DWORD i = 0; i < n_sessions; ++i)
{
if (pSessionInfo [i].State == WTSActive)
{
SessionId = pSessionInfo [i].SessionId;
break;
}
}
WTSFreeMemory (pSessionInfo);
return SessionId;
}
Launch process as the currently logged-on user
bool LaunchProcess (const char *process_path)
{
DWORD SessionId = GetCurrentSessioId ();
if (SessionId == 0) // no-one logged in
return false;
HANDLE hToken;
BOOL ok = WTSQueryUserToken (SessionId, &hToken);
if (!ok)
return false;
void *environment = NULL;
ok = CreateEnvironmentBlock (&environment, hToken, TRUE);
if (!ok)
{
CloseHandle (hToken);
return false;
}
STARTUPINFO si = { sizeof (si) } ;
PROCESS_INFORMATION pi = { } ;
si.lpDesktop = "winsta0\\default";
// Do NOT want to inherit handles here
DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT;
ok = CreateProcessAsUser (hToken, process_path, NULL, NULL, NULL, FALSE,
dwCreationFlags, environment, NULL, &si, &pi);
DestroyEnvironmentBlock (environment);
CloseHandle (hToken);
if (!ok)
return false;
CloseHandle (pi.hThread);
CloseHandle (pi.hProcess);
return true;
}