Hail this great community
I'm having problems automatically converting ('\n') 0x0A to ('\n\r') 0x0D 0x0A when using a channel to redirect a child stdout to a file, the output of the child is bytes, not text.
First, I used these MSDN examples - creating a child process with redirected inputs and outputs and http://support.microsoft.com/kb/190351 ), and now I have this basic application, it creates a channel and redirects the child STDOUT to a binary file. All this in the Win32 Console Application in Visual C ++ 6.0 (yes, this is old, but it is a requirement).
#define BUFSIZE 256 HANDLE g_hChildStd_OUT_Rd = NULL; HANDLE g_hChildStd_OUT_Wr = NULL; int _tmain(int argc, TCHAR *argv[]) { SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) ) ErrorExit(TEXT("StdoutRd CreatePipe")); if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ) ErrorExit(TEXT("Stdout SetHandleInformation")); CreateChildProcess(); if (!CloseHandle(g_hChildStd_OUT_Wr)) ErrorExit("CloseHandle"); ReadFromPipe(); if (!CloseHandle(g_hChildStd_OUT_Rd)) ErrorExit("CloseHandle"); return 0; } void CreateChildProcess() { TCHAR szCmdline[]=TEXT("child.exe"); PROCESS_INFORMATION piProcInfo; STARTUPINFO siStartInfo; BOOL bSuccess = FALSE; ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) ); ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; 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 environment NULL, // use parent current directory &siStartInfo, // STARTUPINFO pointer &piProcInfo); // receives PROCESS_INFORMATION if ( ! bSuccess ) ErrorExit(TEXT("CreateProcess")); else { CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); } } void ReadFromPipe(void) { DWORD dwRead, dwWritten; CHAR chBuf[BUFSIZE]; BOOL bSuccess = FALSE; HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD nTotalBytesRead = 0; fstream filePk; filePk.open("result.out", ios::out | ios::trunc | ios::binary); for (;;) { bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL); if( ! bSuccess || dwRead == 0 ) { if (GetLastError() == ERROR_BROKEN_PIPE) break; // pipe done - normal exit path. else ErrorExit("ReadFile"); // Something bad happened. } filePk.write(chBuf, dwRead); nTotalBytesRead += dwRead; } filePk.close(); char ibuff[24]; sprintf(ibuff,"%d bytes." , (int)nTotalBytesRead); ::MessageBox(NULL, ibuff, "", 0); }
And in this child.cpp dummy, you will notice that if I set STDOUT to binary mode, everything will work fine (I only get 0x0A 0x0A!), But my real child is EXE, and I do not have access to this code.
int main(int argc, char* argv[]) { _setmode( _fileno( stdout ), _O_BINARY ); printf("\n"); unsigned char buffer[] = {'\n'}; fwrite(buffer, sizeof(unsigned char), sizeof(buffer), stdout); return 0; }
So, after searching for about 2 days and considering that I have basic C ++ knowledge, I ask: is there a way I could do _setmode for childs stdout from the parent, given that I do not have access to the child code.
As a solution, I seriously consider looking for all '0x0D' '0x0A' and replacing it with '0x0A' . I'm really crazy about this problem ... Therefore, if someone can help me, I will be very grateful.
A related question: Win32 thread handles - transition to binary mode , but it has access to the child code!
Edit
As a point of @librik, the final solution would be to replace every single occurrence of 0x0D 0x0A with 0x0A. For this to work, the contents of the file must be in memory. There are certain problems, but I can live with it (excess of allocated memory). Hope this will be helpful:
void ReadFromPipe(void) { DWORD dwRead, dwWritten; CHAR *chBuf = NULL, *chBufTmp = NULL; BOOL bSuccess = FALSE; HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD nTotalBytesRead = 0; fstream filePk; filePk.open("result.out", ios::out | ios::trunc | ios::binary); int nIter = 0; for (;;) { if(chBuf == NULL) { if((chBuf = (CHAR*)malloc(BUFSIZE*sizeof(CHAR))) == NULL) { ErrorExit("Malloc"); } } else { chBufTmp = chBuf; // save pointer in case realloc fails if((chBuf = (CHAR*)realloc(chBuf, (nIter+1)*(BUFSIZE*sizeof(CHAR)))) == NULL) { free(chBufTmp); // free original block ErrorExit("Realloc"); } } CHAR* chBufNew = chBuf+nTotalBytesRead; bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBufNew, BUFSIZE, &dwRead, NULL); if( ! bSuccess || dwRead == 0 ) { if (GetLastError() == ERROR_BROKEN_PIPE) { break; // pipe done - normal exit path. } else { ErrorExit("ReadFile"); // Something bad happened. } } nTotalBytesRead += dwRead; nIter ++; } // 0xD 0xA -> 0xA nTotalBytesRead = ClearBuffer(chBuf, nTotalBytesRead); filePk.write(chBuf, nTotalBytesRead); filePk.close(); free(chBuf); char ibuff[24]; sprintf(ibuff,"%d bytes." , (int)nTotalBytesRead); ::MessageBox(NULL, ibuff, "", 0); } int ClearBuffer(char *buffer, int bufferlength) { // lmiguelhm-es requerido que TODO el buffer esté en memoria int chdel = 0; for (int i = 0; (i+chdel) < bufferlength; i++) { char firstChar = buffer[i+chdel]; buffer[i] = firstChar; if (firstChar == 0x0D) { if ((i+chdel+1) < bufferlength) { char secondChar = buffer[i+chdel+1]; if (secondChar == 0x0A) { buffer[i] = secondChar; chdel++; } } } } return bufferlength - chdel; }