I'm working in a C project, windows environment using WinAPI.
My function execs a command (e.g. : "dir C:\Users") it received as a param, and returns its output.
It is ran from a DLL so it must not spawn a cmd.exe windows, which means I can't use _popen. So I use CreateProcessA, and a pipe to catch stdout.
Here is the code :
char *runExec(char *command, int *contentLen) {
char *ret=NULL,*tmp=NULL;
DWORD readBytes;
int size=0;
char buffer[128],cmdBuf[4096];
HANDLE StdOutHandles[2];
CreatePipe(&StdOutHandles[0], &StdOutHandles[1], NULL, 4096);
STARTUPINFOA si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = StdOutHandles[1];
si.hStdError = StdOutHandles[1];
PROCESS_INFORMATION pi;
snprintf(cmdBuf,4096,"cmd /C %s", command);
if (!CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, CREATE_NO_WINDOW | DETACHED_PROCESS, NULL, NULL, &si, &pi)) {
printf("Error createProcess : %d\n",GetLastError());
return NULL;
}
CloseHandle(StdOutHandles[1]);
printf("Before read\n");
while (ReadFile(StdOutHandles[0], buffer, 127, &readBytes, NULL)){
printf("IN WHILE\n");
buffer[readBytes] = 0;
printf("read %d bytes\n", readBytes);
size += readBytes;
tmp = (char *)realloc(ret, size + 1);
if (tmp == NULL) {
free(ret);
return NULL;
}
ret = tmp;
strncpy(ret + (size - readBytes), buffer, readBytes);
ret[size] = 0;
}
printf("Readfile returned with %d, read %d\n", GetLastError(),readBytes);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(StdOutHandles[0]);
printf("#%s#\n", ret);
return ret;
}
And the output is :
Before read
Readfile returned with 109, read 0
#(null)#
So from that I understand that :
- My loop isn't even executed once
- ReadFile reads nothing and returns Broken Pipe immediately
I have read various stackoverflow and MSDN pages, but I can't seem to make it work.
As pointed out in the comment, handles must be inheritable, which means replacing the CreatePipe like this :
SECURITY_ATTRIBUTES pipeAttrib;
memset(&pipeAttrib, 0, sizeof(pipeAttrib));
pipeAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
pipeAttrib.bInheritHandle = NULL;
pipeAttrib.bInheritHandle = TRUE;
if (!CreatePipe(&StdOutHandles[0], &StdOutHandles[1],&pipeAttrib, 4096)) {
printf("Create pipe error %d\n", GetLastError());
return NULL;
}
And setting CreateProcess 5th parameter (bInheritHandles) to TRUE instead of FALSE.
Related
I am trying to find a way to wait until xcopy cmd finished copying files from a disk. I tried 2 ways. First way is basically waiting for hprocess from the created process.
But this waitForSingleObject never resolves.
void copyFileWait(){
STARTinfP777.cb = sizeof(STARTinfP777);
char allCmd[MAX_PATH] = "C:\\Windows\\System32\\cmd.exe /k copy /y \""; // = drive letter; xcopy working fine
printf("%s\n", allCmd);
DWORD safsdkasf = 0;
if(CreateProcess(NULL, allCmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &STARTinfP777, &procInf7777) == 0){
printf("%d\n", GetLastError());
MessageBox(0, "Hata", "Dikkat!", MB_OK);
exit(0);
}
WaitForSingleObject( procInf7777.hProcess, INFINITE );
CloseHandle( procInf7777.hProcess );
CloseHandle( procInf7777.hThread );
}
In this function, i check the exitcode of the process after ran with GetExitCodeProcess() and also set a timer on the code. But it always equals to 259 i dont notice any change after even the files are completely written.
void copyFileWait();
void gggPaths();
DWORD WINAPI GetDirsSEVENthreadFunc ();
//void cpyToPES(char fromWhereToCopy[MAX_PATH]);
char *WndCmpath = "C:\\Windows\\System32\\cmd.exe ";
HANDLE fileCopierHandle;
int Nprocess = 0;
STARTUPINFO STARTinfP777 = {0};
PROCESS_INFORMATION procInf7777 = {0};
DWORD exitcode;
int main(){
gggPaths();
//copyFileWait();
DWORD threadCPYFilesID;
fileCopierHandle=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)GetDirsSEVENthreadFunc,(LPVOID)10,0,&threadCPYFilesID); //updating 0 olana kadar bekler
WaitForSingleObject(fileCopierHandle,INFINITE);
printf("done.");
return 0;
}
void copyFileWait(){
STARTinfP777.cb = sizeof(STARTinfP777);
char allCmd[MAX_PATH] = "C:\\Windows\\System32\\cmd.exe /k copy /y \""; // = drive letter; //xcopy files bla bla works fine
printf("%s\n", allCmd);
DWORD safsdkasf = 0;
if(exitcode != 259){
if(CreateProcess(NULL, allCmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL, &STARTinfP777, &procInf7777) == 0){
printf("%d\n", GetLastError());
MessageBox(0, "Hata", "Dikkat!", MB_OK);
exit(0);
}
}
BOOL result = GetExitCodeProcess(procInf7777.hProcess, &exitcode);
if(!result){
memset( &STARTinfP777, '\0', sizeof STARTinfP777 );
memset( &procInf7777, '\0', sizeof procInf7777 );
printf("damn..\n");
printf("%d\n", GetLastError());
}else{
}
printf("exitcode => %d", exitcode);
//if() exıtcode TASK_COMPLETE forexample... kill timer etc.
}
DWORD WINAPI GetDirsSEVENthreadFunc (){
HWND tempHW;
SetTimer(tempHW, 0, 5500,(TIMERPROC) ©FileWait);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
What would be a way to accomplish this task ?
I tried CopyFile() But it didnt find the path when i linked other volume letters, or external devices. Which I am working on.
Switching /k to /c solved the problem.
I made a function where you get the output of a command. It works but it doesn't preserve the status of the previous command, for example if you change directory (cd dir) it isn't changed in the new command. Can I change the function such that if I change the directory it is preserved in the next command? This is my function:
int execute(char *command, char *output)
{
int rc;
char buffer[4096], *cmd;
DWORD byte, bytes;
HANDLE in_read, in_write, out_read, out_write;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
STARTUPINFO si;
cmd = malloc(sizeof(char) * (strlen(command) + 8));
strcpy(cmd, "cmd /c ");
strcat(cmd, command);
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
ZeroMemory(&si, sizeof(STARTUPINFO));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
rc = CreatePipe(&out_read, &out_write, &sa, 0);
check(rc != 0)
rc = SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0);
check(rc != 0)
rc = CreatePipe(&in_read, &in_write, &sa, 0);
check(rc != 0)
rc = SetHandleInformation(in_write, HANDLE_FLAG_INHERIT, 0);
check(rc != 0)
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdError = out_write;
si.hStdOutput = out_write;
si.hStdInput = in_read;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
rc = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW,
NULL, NULL, &si, &pi);
check(rc != 0)
CloseHandle(out_write);
bytes = 0;
while (ReadFile(out_read, &buffer, 4096, &byte, NULL)) {
if (bytes == 0) {
bytes = bytes + byte;
check(bytes > sizeof(output))
strcpy(output, buffer);
ZeroMemory(&buffer, sizeof(buffer));
} else {
bytes = bytes + byte;
check(bytes > sizeof(output))
strcat(output, buffer);
ZeroMemory(&buffer, sizeof(buffer));
}
};
CloseHandle(in_read);
CloseHandle(in_write);
CloseHandle(out_read);
free(cmd);
return 1;
error:
free(cmd);
return -1;
}
The CreateProcess starts a brand new process that is running independently from yours.
If you make that independent process change its own current working directory, then it will be changed only in the new process, and not in your process running your program.
I am trying to capture output from cmd.exe when running commands. I am trying to capture Unicode output to support other languages. My code works right now for getting unicode back from built in commands to cmd.exe. Such as Dir. But if I try to run external commands such as "net user" or "ipconfig" it does not output unicode.
I did reading and found out it is due to my pipes being file pipes. Ipconfig.exe determines if it is printing to the console or not. If it is printing to console, it uses WriteFileW and outputs unicode. Else it just prints ANSI and I get ???????? for anything Unicode.
#include <winsock.h>
#include <Windows.h>
#include <stdio.h>
void ExecCmd() {
HANDLE hPipeRead;
HANDLE hPipeWrite;
SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES) };
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = { 0 };
BOOL fSuccess;
BOOL bProcessEnded = FALSE;
size_t total = 0;
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 0)) {
return;
}
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hPipeWrite;
si.hStdError = hPipeWrite;
si.wShowWindow = SW_HIDE;
WCHAR k[] = L"cmd.exe /u /c net user";
fSuccess = CreateProcessW(NULL, k, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
if (!fSuccess) {
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
return;
}
for (; !bProcessEnded;) {
bProcessEnded = WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0;
while (TRUE) {
DWORD dwRead = 0;
DWORD dwAvail = 0;
WCHAR *buffer = NULL;
if (!PeekNamedPipe(hPipeRead, NULL, NULL, NULL, &dwAvail, NULL)) {
break;
}
if (!dwAvail) {
break;
}
buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (dwAvail + 2));
if (buffer == NULL) {
break;
}
if (!ReadFile(hPipeRead, buffer, dwAvail, &dwRead, NULL) || !dwRead) {
break;
}
total += dwRead;
}
}
CloseHandle(hPipeWrite);
CloseHandle(hPipeRead);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return;
}
int main() {
ExecCmd();
return 0;
}
My question is how can I change the way my pipes are getting information back from external commands like ipconfig so that it will support unicode.
I'm having strange issues spawning a process, i found almost nothing related to this...
I need to spawn a command-line child process and be capable of getting its output and sending input before it ends.
I can do it fine with cmd.exe, and runs smooth...
BUT when i try to do it with external command-line applications they doesn't output nothing to my program until end of execution. I can send input, it processes normally, the number of times i want to, and at finish of child i get ALL the accumulated output.
The related code is:
SECURITY_ATTRIBUTES secatr;
secatr.nLength = sizeof(SECURITY_ATTRIBUTES);
secatr.bInheritHandle = TRUE;
secatr.lpSecurityDescriptor = NULL;
CreatePipe(&stdin_read, &stdin_write, &secatr, 0);
CreatePipe(&stdout_read, &stdout_write, &secatr, 0);
SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0);
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdError = si.hStdOutput = stdout_write;
si.hStdInput = stdin_read;
CreateProcess(NULL,c:\\\\app.exe, NULL, NULL, TRUE, 0, NULL, "c:\\\\", &si, &pi);
handleproceso = pi.hProcess;
handlethread = (HANDLE)_beginthreadex(NULL,0,&Read,NULL,0,NULL);
_beginthreadex(NULL,0,&Write,NULL,0,NULL);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
unsigned __stdcall Read(void *a){
char buff[100];
while(1){
memset(buff,0,sizeof(buff));
ReadFile( stdout_read, buff, sizeof(buff)-1, &dwRead, NULL);
send(socketcliente,buff,strlen(buff),0);
if(exit1){
exit1 = false;
break;
}
}
return 0;
}
unsigned __stdcall Write(void *a){
char ddd[100];
while(1){
memset(ddd,0,sizeof(ddd));
if(recv(socketcliente,ddd,sizeof(ddd)-1,0) == SOCKET_ERROR){
CloseHandle(handlethread);
TerminateProcess(handleproceso,1);
break;
}
WriteFile(stdin_write, ddd, strlen(ddd), &dwWritten, NULL);
if(strncmp(ddd,"exit",4) == 0){
exit1 = true;
//CloseHandle(handlethread);
break;
}
}
return 0;
}
I tried the MSDN example of How to spawn console processes with redirected standard handles , with the child process fflushing output buffer it receives the output fine...if I remove that fflush i get the same problem of other programs.
The portion of code looks as:
printf("Child echoing [%s]\n",szInput);
fflush(NULL); // Must flush output buffers or else redirection
// will be problematic.
What can I do if the program I want to spawn doesn't behave that way?Can I force its output or something?
I cannot find any response to that in the whole internet...Thank you in advance and apologies for my basic english.
Try:
fflush(stdout);
or at the beginning of your program just disable buffering:
setbuf(stdout, NULL);
I'm (synchronously) reading serial input in Windows using ReadFile(), but instead of waiting for the serial port to have input then returning that as I thought it should, ReadFile() instead returns immediately with a value of FALSE, and a GetLastError() of 0. (Yes, I'm certain I have the right error code and am not making syscalls in between).
The ReadFile() documentation says that when the function "is completing asynchronously, the return value is zero (FALSE)." How is it that a synchronous read can be completing asychronously? Why would this be an error? It's worth noting that the data read is garbage data, as one might expect.
More generally, how can I force ReadFile() to behave like a simple synchronous read of a serial port, or at least behave something like the UNIX read()?
Edit: Here is some source code:
HANDLE my_connect(char *port_name)
{
DCB dcb;
COMMTIMEOUTS timeouts;
HANDLE hdl = CreateFile(port_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
GetCommState(port_name, &dcb);
dcb.BaudRate = 115200;
dcb.ByteSize = 8;
dcb.StopBits = ONESTOPBIT;
dcb.Parity = NOPARITY;
if(SetCommState(hdl, &dcb) == 0)
{
fprintf(stderr, "SetCommState failed with error code %d.\n",
GetLastError());
return (HANDLE) -1;
}
/* TODO: Set a variable timeout. */
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 5000; /* wait 5s for input */
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 5000;
if(SetCommTimeouts(hdl, &timeouts) == 0)
{
fprintf(stderr, "SetCommTimeouts failed with error code %d.\n",
GetLastError());
return (HANDLE) -1;
}
return hdl;
}
int my_disconnect(HANDLE hdl)
{
return CloseHandle(hdl);
}
int my_send(HANDLE hdl, char *cmd)
{
DWORD nb = 0;
if(WriteFile(hdl, cmd, strlen(cmd), &nb, NULL) == 0)
{
fprintf(stderr, "WriteFile failed with error code %d.\n",
GetLastError());
return -1;
}
return (int) nb;
}
int my_receive(HANDLE hdl, char *dst, int dstlen)
{
int i;
DWORD r;
BOOL err;
char c = '\0';
for (i = 0; i < dstlen; err = ReadFile(hdl, &c, 1, &r, NULL))
{
if (err == 0)
{
fprintf(stderr, "ReadFile failed with error code %d.\n",
GetLastError());
return -1;
}
if (r > 0)
{
dst[i++] = c;
if (c == '\n') break;
}
}
if (i == dstlen)
{
fprintf(stderr, "Error: read destination buffer not large enough.\
Recommended size: 256B. Your size: %dB.\n", dstlen);
return -1;
}
else
{
dst[i] = '\0'; /* null-terminate the string. */
}
return i;
}
And my test code:
HANDLE hdl = my_connect("COM4");
char *cmd = "/home\n"; /* basic command */
char reply[256];
my_send(hdl, cmd);
my_receive(hdl, reply, 256);
puts(reply);
It's not completing asynchronously. If it were, GetLastError would return ERROR_IO_PENDING.
To do synchronous I/O, open the file without FILE_FLAG_OVERLAPPED.
It should not be possible for ReadFile to fail without a valid GetLastError code. ReadFile only returns false when the driver sets a non-success status code.