I'm not great at C/C++ and I'm trying to write a function that returns console output like "dir". I am returning the exit code as the main return of the function. I pass the command line as one of the arguments as well as two other variables that I am wanting to store stdout and stderr inside of. The information appears to be getting stored in the variables but it looks like the information is destroyed before I can return it. All I'm looking for is information on returning the info in the variables I have passed. I am sure that the code I post will be awful. Ignore that for now, please. Most of my C/C++ coding isn't usually this extensive and is limited to numbers rather than strings.
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to){
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
}
return str;
}
std::string stdoutbuf, stderrbuf = "";
int pipecom(char* cmd, char* qstdout, char* qstderr){
stdoutbuf = "";
stderrbuf = "";
BOOL ok = TRUE;
HANDLE hStdOutPipeRead, hStdOutPipeWrite, hStdReadPipeError, hStdOutPipeError;
SECURITY_ATTRIBUTES sa = {};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = 0;
sa.bInheritHandle = 1;
if(CreatePipe(&hStdOutPipeRead, &hStdOutPipeWrite, &sa, 0) == FALSE){
return -1;
}
STARTUPINFO si = {};
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = hStdOutPipeError;
si.hStdOutput = hStdOutPipeWrite;
si.hStdInput = NULL;
PROCESS_INFORMATION procinfo = {};
LPSTR lpApplicationName = NULL;
LPSECURITY_ATTRIBUTES lpProcessAttributes = NULL;
LPSECURITY_ATTRIBUTES lpThreadAttributes = NULL;
char * fullcmd = (char *) malloc(1 + strlen(cmd) + strlen((LPSTR)"cmd /c "));
strcpy(fullcmd, (LPSTR)"cmd /c");
strcat(fullcmd, cmd);
LPSTR lpCommandLine = (LPSTR)fullcmd;
BOOL bInheritHandles = TRUE;
DWORD dwCreationFlags = CREATE_NO_WINDOW;
LPSTR lpEnvironment = NULL;
LPSTR lpCurrentDirectory = NULL;
ok = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, &si, &procinfo);
if(ok == FALSE){
return -1;
}
CloseHandle(hStdOutPipeWrite);
CloseHandle(hStdOutPipeError);
char buf[4096+1] = {};
DWORD dwRead = 0;
while((ReadFile(hStdOutPipeRead, &buf, 4096, &dwRead, NULL) != 0) && (dwRead > 0)){
buf[dwRead] = '\0';
stdoutbuf.append(buf);
}
memset(buf, 0, 4096+1);
dwRead = 0;
while((ReadFile(hStdReadPipeError, &buf, 4096, &dwRead, NULL) !=0) && (dwRead > 0)){
buf[dwRead] = '\0';
stderrbuf.append(buf);
}
DWORD exit_code, ex_stat;
if(WaitForSingleObject(procinfo.hProcess, INFINITE) != WAIT_FAILED){
if(GetExitCodeProcess(procinfo.hProcess, &exit_code)){
ex_stat = 1;
}
}
CloseHandle(hStdOutPipeRead);
CloseHandle(hStdReadPipeError);
//this is the area of concern
stdoutbuf = ReplaceAll(stdoutbuf, "\r", "\0");
stderrbuf = ReplaceAll(stderrbuf, "\r", "\0");
qstdout = (char *)malloc(1+strlen(stdoutbuf.c_str()));
qstderr = (char *)malloc(1+strlen(stderrbuf.c_str()));
strcpy(qstdout, stdoutbuf.c_str());
strcpy(qstderr, stderrbuf.c_str());
stdoutbuf.clear();
stderrbuf.clear();
if(ex_stat == 1){
return exit_code;
}
else{
return -1;
}
}
change this
pipecom(char* cmd, char* qstdout, char* qstderr){
to
pipecom(char* cmd, char** qstdout, char** qstderr){
*qstdout = (char *)malloc(1+strlen(stdoutbuf.c_str()));
*qstderr = (char *)malloc(1+strlen(stderrbuf.c_str()));
then dereference and alloc using malloc. otherwise it is just a copy of pointer which will get destroyed once you come out of the function.
Related
I tried to create a pwntools-like program for windows. I implemented a send and recv to send data to stdin of a and to receive data from stdout. I did that using pipes.
#include <iostream>
#include <cstdio>
#include <windows.h>
void spawn();
void pwn_send(CONST CHAR chBuf[]);
CHAR* pwn_recv(SIZE_T sz);
CHAR* pwn_recv();
HANDLE stdout_write = NULL;
HANDLE stdout_read = NULL;
HANDLE stdin_write = NULL;
HANDLE stdin_read = NULL;
int main() {
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if(!CreatePipe(&stdout_read, &stdout_write, &saAttr, 0)) return -1;
if(!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) return -1;
if(!CreatePipe(&stdin_read, &stdin_write, &saAttr, 0)) return -1;
if(!SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0)) return -1;
spawn();
CHAR chBuf[] = "mkdir C:\\Users\\comma\\Desktop\\x\r\n";
pwn_send(chBuf);
std::cout << pwn_recv(20) << std::endl;
return 0;
}
void spawn() {
TCHAR cmd[] = TEXT("cmd.exe");
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.hStdError = stdout_write;
si.hStdOutput = stdout_write;
si.hStdInput = stdin_read;
si.dwFlags |= STARTF_USESTDHANDLES;
ZeroMemory(&pi, sizeof(pi));
if(!CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) exit(-1);
return;
}
void pwn_send(CONST CHAR chBuf[]) {
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
while(1) {
bSuccess = WriteFile(stdin_write, chBuf, strlen(chBuf), &dwWritten, NULL);
if (bSuccess) break;
}
CloseHandle(stdin_write);
return;
}
CHAR* pwn_recv(SIZE_T sz) {
HANDLE hHeap = GetProcessHeap();
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
CHAR* chBuf = (CHAR*)HeapAlloc(hHeap, 0, sz+1);
while (1) {
std::cout << "XXX" << std::endl;
bSuccess = ReadFile(stdout_read, chBuf, sz, &dwRead, NULL);
std::cout << "YYY" << std::endl;
if (bSuccess) break;
}
return chBuf;
}
CHAR* pwn_recv() {
HANDLE hHeap = GetProcessHeap();
DWORD dwRead = 0, dwWritten = 0;
BOOL bSuccess = FALSE;
CHAR* chBuf = (CHAR*)HeapAlloc(hHeap, 0, 0x1000);
while (1) {
bSuccess = ReadFile(stdout_read, chBuf, 0x1000, &dwWritten, NULL);
if (bSuccess) break;
}
return chBuf;
}
So the first issue here is that the program I started with spawn(cmd.exe) doesn't properly receive the command I send to stdin. No folder is created anywhere. CreateProcess succeeds. Receiving output doesn't work either, because ReadFile seems to stuck. XXX is displayed in the console, butYYY never. Any ideas?
I am trying to get the base string in the DLL using EvtFormatMessage but no matter what I do its not working. The windows help page made it sound like you can use the values and valuecount parameters to change the default behavior but when I change them nothing is printed.
Another attempt I made was to go through the publishers metadata and match it with the correct eventId and version but the EvtOpenEventMetadatEnum has no items for it to iterate through for all events publishers. It works for time change events but not for other events.
This is the code I have to return the message string. hMetadata is the publisher metadata and hEvent is the event metadata
LPWSTR GetMessageString(EVT_HANDLE hMetadata, EVT_HANDLE hEvent, EVT_FORMAT_MESSAGE_FLAGS FormatId, DWORD MsgID)
{
LPWSTR pBuffer = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
if (!fpEvtFormatMessage(hMetadata, hEvent, MsgID, 0, NULL, FormatId, dwBufferSize, pBuffer, &dwBufferUsed))
{
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status)
{
// An event can contain one or more keywords. The function returns keywords
// as a list of keyword strings. To process the list, you need to know the
// size of the buffer, so you know when you have read the last string, or you
// can terminate the list of strings with a second null terminator character
// as this example does.
if ((EvtFormatMessageKeyword == FormatId))
pBuffer[dwBufferSize - 1] = L'\0';
else
dwBufferSize = dwBufferUsed;
pBuffer = (LPWSTR)malloc(dwBufferSize * sizeof(WCHAR));
if (pBuffer)
{
fpEvtFormatMessage(hMetadata, hEvent, MsgID, 0, NULL, FormatId, dwBufferSize, pBuffer, &dwBufferUsed);
// Add the second null terminator character.
if ((EvtFormatMessageKeyword == FormatId))
pBuffer[dwBufferUsed - 1] = L'\0';
}
else
{
TRACE5(_T("malloc failed\n"));
}
}
else if (ERROR_EVT_MESSAGE_NOT_FOUND == status || ERROR_EVT_MESSAGE_ID_NOT_FOUND == status)
;
else
{
TRACE5(_T("EvtFormatMessage failed with %u\n"), status);
}
}
This is the code that is supposed to match the event I have with the template event in the dll
hEvents = fpEvtOpenEventMetadataEnum(g_hMetadata, 0);
if (NULL == hEvents)
{
TRACE5(_T("EvtOpenEventMetadataEnum failed with %lu\n"), GetLastError());
goto cleanup;
}
// Enumerate the events and print each event's metadata.
while (run)
{
hEvent = fpEvtNextEventMetadata(hEvents, 0);
if (NULL == hEvent)
{
if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
{
TRACE5(_T("EvtNextEventMetadata failed with %lu\n"), status);
}
break;
}
msgId = getEventID(g_hMetadata, hEvent, &tempVersion);
if (dwMsgID == msgId && tempVersion == version) {
PEVT_VARIANT pProperty = NULL; // Contains a metadata value
PEVT_VARIANT pTemp = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status2 = ERROR_SUCCESS;
if (!fpEvtGetEventMetadataProperty(hEvent, EventMetadataEventMessageID, 0, dwBufferSize, pProperty, &dwBufferUsed))
{
status2 = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status2)
{
dwBufferSize = dwBufferUsed;
pTemp = (PEVT_VARIANT)realloc(pProperty, dwBufferSize);
if (pTemp)
{
pProperty = pTemp;
pTemp = NULL;
fpEvtGetEventMetadataProperty(hEvent, EventMetadataEventMessageID, 0, dwBufferSize, pProperty, &dwBufferUsed);
}
else
{
TRACE5(_T("realloc failed\n"));
status2 = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status2 = GetLastError()))
{
TRACE5(_T("EvtGetEventMetadataProperty failed with %d\n"), GetLastError());
goto cleanup;
}
}
if (-1 == pProperty->UInt32Val)
{
*pStrNonExpLibMsg = "Message string: \n";
}
else
{
*pStrNonExpLibMsg = GetMessageString(g_hMetadata, NULL, EvtFormatMessageId, pProperty->UInt32Val);
if (pMessage)
{
free(pMessage);
}
}
run = false;
break;
}
fpEvtClose(hEvent);
hEvent = NULL;
}
cleanup:
if (hEvents)
fpEvtClose(hEvents);
if (hEvent)
fpEvtClose(hEvent);
return status;
}
DWORD getEventID(EVT_HANDLE g_hMetadata, EVT_HANDLE hEvent, DWORD *evtVersion)
{
PEVT_VARIANT pProperty = NULL; // Contains a metadata value
PEVT_VARIANT pTemp = NULL;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD status = ERROR_SUCCESS;
DWORD retValue = NULL;
// Get the specified metadata property. If the pProperty buffer is not big enough, reallocate the buffer.
for (int i = 0; i < 2; i++)
{
if (!fpEvtGetEventMetadataProperty(hEvent, (EVT_EVENT_METADATA_PROPERTY_ID)i, 0, dwBufferSize, pProperty, &dwBufferUsed))
{
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status)
{
dwBufferSize = dwBufferUsed;
pTemp = (PEVT_VARIANT)realloc(pProperty, dwBufferSize);
if (pTemp)
{
pProperty = pTemp;
pTemp = NULL;
fpEvtGetEventMetadataProperty(hEvent, (EVT_EVENT_METADATA_PROPERTY_ID)i, 0, dwBufferSize, pProperty, &dwBufferUsed);
}
else
{
TRACE5(_T("realloc failed\n"));
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
TRACE5(_T("EvtGetEventMetadataProperty failed with %d\n"), GetLastError());
goto cleanup;
}
}
if (i == 0)
{
retValue = pProperty->UInt32Val;
}
else
{
*evtVersion = pProperty->UInt32Val;
}
RtlZeroMemory(pProperty, dwBufferUsed);
}
Actually I am working with Qt printing and need to send the RAW command (ESCP command) to printer. After did some search and I found that I need to use the Windows API to do it.
Refer to this thread : win32-c-print-string-to-printer
I have created the code like below :
const QString pName = "EPSON LX-300+ /II";
LPBYTE lpData;
BOOL bStatus = FALSE;
HANDLE hPrinter = NULL;
DOC_INFO_1 DocInfo;
DWORD dwPrtJob = 0L;
DWORD dwBytesWritten = 0L;
LPTSTR printerName = new wchar_t[pName.length() + 1];
pName.toWCharArray(printerName);
printerName[pName.length()] = '\0';
QString so = "\x1b#Is it works?";
QByteArray ba = so.toUtf8();
lpData = (unsigned char*)(ba.data());
DWORD dwCount = ba.length();
qDebug() << so;
bStatus = OpenPrinter(printerName, &hPrinter, NULL);
if(bStatus) {
DocInfo.pDocName = (LPTSTR)_T("My Document");
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = (LPTSTR)_T("RAW");
dwPrtJob = StartDocPrinter (
hPrinter,
1,
(LPBYTE)&DocInfo);
qDebug() << GetLastError();
if (dwPrtJob > 0) {
qDebug() << "COMMAND";
// Send the data to the printer.
bStatus = WritePrinter (
hPrinter,
lpData,
dwCount,
&dwBytesWritten);
}
qDebug() << dwCount;
qDebug() << dwBytesWritten;
EndDocPrinter (hPrinter);
// Close the printer handle.
bStatus = ClosePrinter(hPrinter);
qDebug() << bStatus;
}
if (!bStatus || (dwCount != dwBytesWritten)) {
bStatus = FALSE;
} else {
bStatus = TRUE;
}
delete printerName;
And the code failed on StartDocPrinter, it returning 0 which mean failed. And using the GetLastError(), the function return 1804. And refer to this, the error is ERROR_INVALID_DATATYPE. I am not sure what error is it. And i try to use different DocInfo.pDatatype to "RAW", "TEXT", and "XPS_PASS", the result is the same.
Is there anything I can do how to fix it?
DOC_INFO_1 DocInfo;
DocInfo.pDocName = (LPTSTR)_T("My Document");
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = (LPTSTR)_T("RAW");
This casting boils down to
DocInfo.pDocName = (wchar_t*)L"My Document";
...
Where pDocName is declared as wchar_t* This is wrong and the cast simply hides the errors and warnings. Try to avoid Microsoft T macro altogether, these macros are outdated and useless. Use C++ char and wchar_t* instead. To declare UTF16 wide char string in Windows, use the L prefix. The correct usage is as follows:
DOC_INFO_1 DocInfo;
wchar_t docName[100], dataType[100];
wcscpy_s(docName, 100, L"Print Job");
wcscpy_s(dataType, 100, L"RAW");
DocInfo.pDocName = docName;
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = dataType;
Example:
const QString pName = "EPSON LX-300+ /II";
wchar_t printerName[100];
pName.toWCharArray(printerName);
pName[pName.length()] = '\0';
HANDLE hprinter;
if (OpenPrinter(printerName, &hprinter, NULL))
{
DOC_INFO_1 DocInfo;
wchar_t docName[100], dataType[100];
wcscpy_s(docName, 100, L"Print Job");
wcscpy_s(dataType, 100, L"RAW");
DocInfo.pDocName = docName;
DocInfo.pOutputFile = NULL;
DocInfo.pDatatype = dataType;
DWORD printJob = StartDocPrinter(hprinter, 1, (LPBYTE)&DocInfo);
if (printJob && StartPagePrinter(hprinter))
{
MessageBox(0, L"StartPagePrinter OKAY", 0, 0);
DWORD written = 0;
int buflen = 100;
char *buf = new char[buflen];
strcpy_s(buf, buflen, "123");
if (WritePrinter(hprinter, buf, 3, &written))
MessageBox(0, L"OKAY", 0, 0);
delete[]buf;
EndPagePrinter(hprinter);
EndDocPrinter(hprinter);
}
ClosePrinter(hprinter);
}
I am running into memory errors when I try to run my C++ program in Visual Studio 2012. I am thinking that this code is the cause (since when I remove it, it runs fine):
void GetMachineHash(CString &strHashHex) {
CMD5 cMD5;
BYTE *szHash = (BYTE*)malloc(48);
LPBYTE szMachineNameHash, szNetworkAddressHash, szVolumeIdHash;
TCHAR szMachineId[100];
DWORD nMachineIdLen = 100;
TCHAR szNetworkAddress[13];
IP_ADAPTER_INFO *pAdapterInfo, *pAdapter = NULL;
DWORD dwRetVal = 0;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
TCHAR szVolumeId[20];
TCHAR szVolumeName[MAX_PATH];
TCHAR szFileSystemName[MAX_PATH];
DWORD dwSerialNumber = 0;
DWORD dwMaxComponentLen = 0;
DWORD dwFileSystemFlags = 0;
ZeroMemory(szHash, 48);
ZeroMemory(szMachineId, 100);
ZeroMemory(szVolumeId, 20);
ZeroMemory(szVolumeName, MAX_PATH);
ZeroMemory(szFileSystemName, MAX_PATH);
ZeroMemory(szNetworkAddress, 13);
GetComputerName(szMachineId, &nMachineIdLen);
cMD5.Calculate(szMachineId);
szMachineNameHash = cMD5.Hash();
pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
if (pAdapterInfo == NULL) {
TRACE(_T("Error allocating memory needed to call GetAdaptersinfo()"));
szNetworkAddressHash = NULL;
}
// Make an initial call to GetAdaptersInfo to get the necessary size into the ulOutBufLen variable
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
if (pAdapterInfo == NULL) {
TRACE(_T("Error allocating memory needed to call GetAdaptersinfo()"));
szNetworkAddressHash = NULL;
}
}
if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
pAdapter = pAdapterInfo;
while (pAdapter) {
if (pAdapter->Type != MIB_IF_TYPE_LOOPBACK) {
_stprintf_s(szNetworkAddress, 13, _T("%.2X%.2X%.2X%.2X%.2X%.2X"),
pAdapter->Address[0],
pAdapter->Address[1],
pAdapter->Address[2],
pAdapter->Address[3],
pAdapter->Address[4],
pAdapter->Address[5]
);
break;
}
pAdapter = pAdapter->Next;
}
} else {
TRACE(_T("GetAdaptersInfo() call failed"));
szNetworkAddressHash = NULL;
}
cMD5.Calculate(szNetworkAddress);
szNetworkAddressHash = cMD5.Hash();
if (GetVolumeInformation(
NULL,
szVolumeName,
sizeof(szVolumeName),
&dwSerialNumber,
&dwMaxComponentLen,
&dwFileSystemFlags,
szFileSystemName,
sizeof(szFileSystemName))) {
_stprintf_s(szVolumeId, 20, _T("%lu"), dwSerialNumber);
}
cMD5.Calculate(szVolumeId);
szVolumeIdHash = cMD5.Hash();
// Calculate hash from hashes
memcpy(szHash, szMachineNameHash, 16);
memcpy(szHash+16, szNetworkAddressHash, 16);
memcpy(szHash+32, szVolumeIdHash, 16);
cMD5.Calculate(szHash, 48);
strHashHex.Preallocate(33);
strHashHex = cMD5.HexHash();
free(szHash);
free(pAdapterInfo);
return;
}
And then if I leave the function and just remove this code:
strHashHex.Preallocate(33);
strHashHex = cMD5.HexHash();
Then it will work fine as well. So I am wondering if that is the code that's causing the memory problems, and if it is, how can I fix it?
Here's the CMD5 class (which utilizes the Windows API to generate a MD5 sum):
class CMD5
{
public:
CMD5() {
if(CryptAcquireContext(&m_hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET) == 0){
if(GetLastError() == NTE_EXISTS){
CryptAcquireContext(&m_hCryptProv, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0);
}
}
}
~CMD5() {
if(m_hCryptProv)
CryptReleaseContext(m_hCryptProv, 0);
m_hCryptProv = NULL;
free(m_szHash);
}
bool Calculate(LPCTSTR szText) {
DWORD dwLen = sizeof(TCHAR) * _tcslen(szText);
DWORD dwHashLen;
DWORD dwHashLenSize = sizeof(DWORD);
if (CryptCreateHash(m_hCryptProv, CALG_MD5, 0, 0, &m_hHash)) {
if (CryptHashData(m_hHash, (const BYTE*)szText, dwLen, 0)) {
if (CryptGetHashParam(m_hHash, HP_HASHSIZE, (BYTE *)&dwHashLen, &dwHashLenSize, 0)) {
if(m_szHash = (BYTE*)malloc(dwHashLen)) {
if (CryptGetHashParam(m_hHash, HP_HASHVAL, (BYTE*)m_szHash, &dwHashLen, 0)) {
CryptDestroyHash(m_hHash);
}
}
}
}
}
return false;
}
bool Calculate(const LPBYTE szText, DWORD dwLen) {
DWORD dwHashLen;
DWORD dwHashLenSize = sizeof(DWORD);
if (CryptCreateHash(m_hCryptProv, CALG_MD5, 0, 0, &m_hHash)) {
if (CryptHashData(m_hHash, (const BYTE*)szText, dwLen, 0)) {
if (CryptGetHashParam(m_hHash, HP_HASHSIZE, (BYTE *)&dwHashLen, &dwHashLenSize, 0)) {
if(m_szHash = (BYTE*)malloc(dwHashLen)) {
if (CryptGetHashParam(m_hHash, HP_HASHVAL, (BYTE*)m_szHash, &dwHashLen, 0)) {
CryptDestroyHash(m_hHash);
}
}
}
}
}
return false;
}
LPBYTE Hash() const {
LPBYTE szHash = new BYTE[16];
ZeroMemory(szHash, 16);
memcpy(szHash, m_szHash, 16);
return szHash;
}
LPTSTR HexHash() const {
LPTSTR szBuf = new TCHAR[33];
ZeroMemory(szBuf, 33);
for (int i=0; i<16; i++)
_stprintf_s(szBuf+i*2, 33, _T("%02X"), m_szHash[i]);
szBuf[32]=0;
return szBuf;
}
private:
BYTE *m_szHash;
DWORD m_hHash;
HCRYPTPROV m_hCryptProv;
};
Also, the error I get from VS2012 is Critical error detected c0000374 and the call stack ends with a call to HeapAlloc() from _heap_alloc. Not sure if it matters but this code is being called in a DLL.
It looks like I was able to solve the memory allocation problems by changing the CMD5::HexHash() function to
void HexHash(CString &strHash) {
for (int i=0; i<16; i++)
strHash += StringFormat(_T("%02X"), m_szHash[i]);
return;
}
and call it via cMD5.HexHash(strHashHex);
Background:
I'm working on a program that needs to be able to capture the stdout, stderr and return values of a program. Ideally, I would like to capture these in a string that I store inside of my object that holds details of the process. I currently have some code that works by saving the output into a file using some (in my opinion) archaic C file handle magic. Any time I want to output the results, I open up that file and I print the contents.
Sometimes (when a process I spawn is left running) the next execution of my executable will break down because it cannot open the file for writing.
Problem Statement:
I'm looking for a way to save the output from stdout of a created process in windows to one string and the stderr to another in a safer, more modern fashion. That way I could print those contents any time I feel like outputting the result of each created process.
My ugly code:
main chunk-
int stdoutold = _dup(_fileno(stdout)); //make a copy of stdout
int stderrold = _dup(_fileno(stdout)); //make a copy of stderr
FILE *f;
if(!fopen_s(&f, "name_of_my_file", "w")){ //make sure I can write to the file
_dup2(_fileno(f), _fileno(stdout)); //make stdout point to f
_dup2(_fileno(f), _fileno(stderr)); //make stderr point to f
fork("command_I_want_to_run", &pi); //run my fake fork (see below)
}
else{
...//error handling
}
_close(_fileno(stdout)); //close tainted stdout
_close(_fileno(stderr)); //close tainted stderr
_close(_fileno(f)); //close f
_dup2(stdoutold, _fileno(stdout)); //fix stdout
_dup2(stderrold, _fileno(stderr)); //fix stderr
fork- (you can think of this as just CreateProcess, but just in case anyone needs to see what happens here)
int fork(std::string s, PROCESS_INFORMATION* pi){
char infoBuf[INFO_BUFFER_SIZE];
int bufCharCount =
ExpandEnvironmentStrings(s.c_str(), infoBuf, INFO_BUFFER_SIZE );
...
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( pi, sizeof(*pi) );
LPSTR str = const_cast<char *>(infoBuf);
if(!CreateProcess(NULL,
str,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&si,
pi)
){
int err = GetLastError();
printf("CreateProcess failed (%d).\n", err);
CloseHandle((*pi).hProcess);
CloseHandle((*pi).hThread);
return err;
}
return 0;
}
Notes:
I'm using VS 2010
I want to remain using multiple processes, not threads because I need what I run to have the freedom of its own process
Edit:
An extra note: I also try to wait for the process to finish right after calling the function that runs the code given, so the results of stdout and stderr are available to me at that time.
Eddy Luten's answer led me in a good direction, but the MSDN documentation (while elaborate) had some issues. Mainly, you need to ensure you close all handles you don't use. Also it just has code it expects the user to understand.
So instead, here's my wall of code I expect people to just understand :D
#include <string>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#pragma warning( disable : 4800 ) // stupid warning about bool
#define BUFSIZE 4096
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;
PROCESS_INFORMATION CreateChildProcess(void);
void ReadFromPipe(PROCESS_INFORMATION);
int main(int argc, char *argv[]){
SECURITY_ATTRIBUTES sa;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDERR.
if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
exit(1);
}
// Ensure the read handle to the pipe for STDERR is not inherited.
if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
exit(1);
}
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
exit(1);
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
exit(1);
}
// Create the child process.
PROCESS_INFORMATION piProcInfo = CreateChildProcess();
// Read from pipe that is the standard output for child process.
printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
ReadFromPipe(piProcInfo);
printf("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application,
// close handles explicitly.
return 0;
}
// Create a child process that uses the previously created pipes
// for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(){
// Set the text I want to run
char szCmdline[]="test --log_level=all --report_level=detailed";
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
bool bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDERR and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_ERR_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
CloseHandle(g_hChildStd_ERR_Wr);
CloseHandle(g_hChildStd_OUT_Wr);
// If an error occurs, exit the application.
if ( ! bSuccess ) {
exit(1);
}
return piProcInfo;
}
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
void ReadFromPipe(PROCESS_INFORMATION piProcInfo) {
DWORD dwRead;
CHAR chBuf[BUFSIZE];
bool bSuccess = FALSE;
std::string out = "", err = "";
for (;;) {
bSuccess=ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
std::string s(chBuf, dwRead);
out += s;
}
dwRead = 0;
for (;;) {
bSuccess=ReadFile( g_hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
std::string s(chBuf, dwRead);
err += s;
}
std::cout << "stdout:" << out << std::endl;
std::cout << "stderr:" << err << std::endl;
}
Shawn Blakesley code is good rework of Microsoft sample code but it has a bit of a problem when there is massive stdout and stderr interleaved streams that are out of order. And some handles are leaked (which is OK for the sample code). Having background thread and PeekNamedPipe() calls makes sure the the code behave more similar to POSIX system call:
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C } // extern "C"
#define null nullptr
#else
#define BEGIN_C
#define END_C
#define null ((void*)0)
#endif
BEGIN_C
int system_np(const char* command, int timeout_milliseconds,
char* stdout_data, int stdout_data_size,
char* stderr_data, int stderr_data_size, int* exit_code);
typedef struct system_np_s {
HANDLE child_stdout_read;
HANDLE child_stderr_read;
HANDLE reader;
PROCESS_INFORMATION pi;
const char* command;
char* stdout_data;
int stdout_data_size;
char* stderr_data;
int stderr_data_size;
int* exit_code;
int timeout; // timeout in milliseconds or -1 for INIFINTE
} system_np_t;
static char stdout_data[16 * 1024 * 1024];
static char stderr_data[16 * 1024 * 1024];
int main(int argc, char *argv[]) {
int bytes = 1;
for (int i = 1; i < argc; i++) {
bytes += (int)strlen(argv[i]) + 1;
}
char* command = (char*)alloca(bytes);
command[0] = 0;
char* p = command;
for (int i = 1; i < argc; i++) {
int n = (int)strlen(argv[i]);
memcpy(p, argv[i], n); p += n;
*p = (i == argc - 1) ? 0x00 : 0x20;
p++;
}
int exit_code = 0;
if (command[0] == 0) {
command = (char*)"cmd.exe /c \"dir /w /b\"";
}
int r = system_np(command, 100 * 1000, stdout_data, sizeof(stdout_data), stderr_data, sizeof(stderr_data), &exit_code);
if (r != 0) {
fprintf(stderr, "system_np failed: %d 0x%08x %s", r, r, strerror(r));
return r;
} else {
fwrite(stdout_data, strlen(stdout_data), 1, stdout);
fwrite(stderr_data, strlen(stderr_data), 1, stderr);
return exit_code;
}
}
static int peek_pipe(HANDLE pipe, char* data, int size) {
char buffer[4 * 1024];
DWORD read = 0;
DWORD available = 0;
bool b = PeekNamedPipe(pipe, null, sizeof(data), null, &available, null);
if (!b) {
return -1;
} else if (available > 0) {
int bytes = min(sizeof(buffer), available);
b = ReadFile(pipe, buffer, bytes, &read, null);
if (!b) {
return -1;
}
if (data != null && size > 0) {
int n = min(size - 1, (int)read);
memcpy(data, buffer, n);
data[n + 1] = 0; // always zero terminated
return n;
}
}
return 0;
}
static DWORD WINAPI read_from_all_pipes_fully(void* p) {
system_np_t* system = (system_np_t*)p;
unsigned long long milliseconds = GetTickCount64(); // since boot time
char* out = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data : null;
char* err = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data : null;
int out_bytes = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data_size - 1 : 0;
int err_bytes = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data_size - 1 : 0;
for (;;) {
int read_stdout = peek_pipe(system->child_stdout_read, out, out_bytes);
if (read_stdout > 0 && out != null) { out += read_stdout; out_bytes -= read_stdout; }
int read_stderr = peek_pipe(system->child_stderr_read, err, err_bytes);
if (read_stderr > 0 && err != null) { err += read_stderr; err_bytes -= read_stderr; }
if (read_stdout < 0 && read_stderr < 0) { break; } // both pipes are closed
unsigned long long time_spent_in_milliseconds = GetTickCount64() - milliseconds;
if (system->timeout > 0 && time_spent_in_milliseconds > system->timeout) { break; }
if (read_stdout == 0 && read_stderr == 0) { // nothing has been read from both pipes
HANDLE handles[2] = {system->child_stdout_read, system->child_stderr_read};
WaitForMultipleObjects(2, handles, false, 1); // wait for at least 1 millisecond (more likely 16)
}
}
if (out != null) { *out = 0; }
if (err != null) { *err = 0; }
return 0;
}
static int create_child_process(system_np_t* system) {
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true;
sa.lpSecurityDescriptor = null;
HANDLE child_stdout_write = INVALID_HANDLE_VALUE;
HANDLE child_stderr_write = INVALID_HANDLE_VALUE;
if (!CreatePipe(&system->child_stderr_read, &child_stderr_write, &sa, 0) ) {
return GetLastError();
}
if (!SetHandleInformation(system->child_stderr_read, HANDLE_FLAG_INHERIT, 0) ){
return GetLastError();
}
if (!CreatePipe(&system->child_stdout_read, &child_stdout_write, &sa, 0) ) {
return GetLastError();
}
if (!SetHandleInformation(system->child_stdout_read, HANDLE_FLAG_INHERIT, 0) ){
return GetLastError();
}
// Set the text I want to run
STARTUPINFO siStartInfo = {0};
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = child_stderr_write;
siStartInfo.hStdOutput = child_stdout_write;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
siStartInfo.wShowWindow = SW_HIDE;
bool b = CreateProcessA(null,
(char*)system->command,
null, // process security attributes
null, // primary thread security attributes
true, // handles are inherited
CREATE_NO_WINDOW, // creation flags
null, // use parent's environment
null, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&system->pi); // receives PROCESS_INFORMATION
int err = GetLastError();
CloseHandle(child_stderr_write);
CloseHandle(child_stdout_write);
if (!b) {
CloseHandle(system->child_stdout_read); system->child_stdout_read = INVALID_HANDLE_VALUE;
CloseHandle(system->child_stderr_read); system->child_stderr_read = INVALID_HANDLE_VALUE;
}
return b ? 0 : err;
}
int system_np(const char* command, int timeout_milliseconds,
char* stdout_data, int stdout_data_size,
char* stderr_data, int stderr_data_size, int* exit_code) {
system_np_t system = {0};
if (exit_code != null) { *exit_code = 0; }
if (stdout_data != null && stdout_data_size > 0) { stdout_data[0] = 0; }
if (stderr_data != null && stderr_data_size > 0) { stderr_data[0] = 0; }
system.timeout = timeout_milliseconds > 0 ? timeout_milliseconds : -1;
system.command = command;
system.stdout_data = stdout_data;
system.stderr_data = stderr_data;
system.stdout_data_size = stdout_data_size;
system.stderr_data_size = stderr_data_size;
int r = create_child_process(&system);
if (r == 0) {
system.reader = CreateThread(null, 0, read_from_all_pipes_fully, &system, 0, null);
if (system.reader == null) { // in theory should rarely happen only when system super low on resources
r = GetLastError();
TerminateProcess(system.pi.hProcess, ECANCELED);
} else {
bool thread_done = WaitForSingleObject(system.pi.hThread, timeout_milliseconds) == 0;
bool process_done = WaitForSingleObject(system.pi.hProcess, timeout_milliseconds) == 0;
if (!thread_done || !process_done) {
TerminateProcess(system.pi.hProcess, ETIME);
}
if (exit_code != null) {
GetExitCodeProcess(system.pi.hProcess, (DWORD*)exit_code);
}
CloseHandle(system.pi.hThread);
CloseHandle(system.pi.hProcess);
CloseHandle(system.child_stdout_read); system.child_stdout_read = INVALID_HANDLE_VALUE;
CloseHandle(system.child_stderr_read); system.child_stderr_read = INVALID_HANDLE_VALUE;
WaitForSingleObject(system.reader, INFINITE); // join thread
CloseHandle(system.reader);
}
}
if (stdout_data != null && stdout_data_size > 0) { stdout_data[stdout_data_size - 1] = 0; }
if (stderr_data != null && stderr_data_size > 0) { stderr_data[stderr_data_size - 1] = 0; }
return r;
}
END_C
You'll have to use pipes to capture the contents of your process's stdout stream. There's an elaborate example on MSDN on how to accomplish this:
MSDN: Creating a Child Process with Redirected Input and Output