|
http://www.gidforums.com/clientscript/vbulletin_md5.js
|
Thread Tools // | Search this Thread // | Rating: // |
#1
|
||||
|
||||
[TUTORIAL] Calling an external program in C (Linux)Executing programs with C(Linux)
Introduction CPP / C++ / C Code:
system("process1"); While this works, you loose all control of your program until this process finishes. Beyond that what if you want a more interactive approach where the calling process could send information to the called process? The standard posix C library provides several functions for calling and controlling processes. These commands are: fork(), execl(), pipe() and dup2(). Why is this so complex? C function: fork() CPP / C++ / C Code:
if(fork()) printf("I am the parent!"); /* A non-zero indicates the parent process */ else printf("I am the child!"); /* A zero indicates the child process */ Second, by using the exec family of functions, it is possible to replace an entire process with a brand new process. C function: execl() Quote:
Using the execl function without the fork process is possible, but the calling process is replaced by the new process. Therefore you can never come back to the original process. However, coupled with a call to fork, the execl call can replace the child process, while the parent continues happily along. There are many variations of exec, but for this sample, I have chosen to use execl. A typical call is something like: CPP / C++ / C Code:
execl("child","child","arg0","arg1",NULL); Several things of note. The first two parameters are the name of the process to execute. The last parameter to the function must be NULL, even if there are no arguments to be passed to the child. C function: pipe() CPP / C++ / C Code:
int commpipe[2]; pipe(commpipe); In the above example, commpipe[0] is the input side of the pipe and commpipe[1] is the output side of the pipe. Data can be put in and taken out of the pipe by using the read & write commands respectively with the proper file descriptors. This is a big advantage when coupled with the use of a fork. A pipe is an internal element that can only be known to the calling process. If I create a pipe prior to forking, this pipe will exist across both the parent and the child process, since the fork command duplicates everything about the calling process. C function: dup2() The file descriptors for stdin, stdout and stderr and 0,1 and 2 respectively. So to replace the stdin with the “in” side of the pipe is done by calling dup2 as such: CPP / C++ / C Code:
dup2(commpipe[0],0); Miscellaneous By defaulf, stdout is a line buffered process. Therefore, the buffer is not written until a new-line is encountered. While this is not apparent in a typical process, it is very apparent when using a pipe line for stdout. There are two ways to handle this problem. The first is to flush the stdout buffer whenever something is printed with it. The second way (and better way – from what I understand) is to change the stdout to be unbuffered. This can be accomplished with a call to setvbuf. CPP / C++ / C Code:
setvbuf(stdout,(char*)NULL,_IONBF,0); The two important arguments are stdout (the stream that we want to change the buffer on) and _IONBF (indicating not to use a buffer). The other two are used to assign a new buffer and size, neither of which we need because we are using non-buffered output. Lastly, there is a couple of calls to close. It is always a good idea to “close” the unused sides of the pipe. That way there is no chance of accidently reading from the write side, etc. Example Code:
parent.c CPP / C++ / C Code:
#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(){ pid_t pid; int rv; int commpipe[2]; /* This holds the fd for the input & output of the pipe */ /* Setup communication pipeline first */ if(pipe(commpipe)){ fprintf(stderr,"Pipe error!n"); exit(1); } /* Attempt to fork and check for errors */ if( (pid=fork()) == -1){ fprintf(stderr,"Fork error. Exiting.n"); /* something went wrong */ exit(1); } if(pid){ /* A positive (non-negative) PID indicates the parent process */ dup2(commpipe[1],1); /* Replace stdout with out side of the pipe */ close(commpipe[0]); /* Close unused side of pipe (in side) */ setvbuf(stdout,(char*)NULL,_IONBF,0); /* Set non-buffered output on stdout */ sleep(2); printf("Hellon"); sleep(2); printf("Goodbyen"); sleep(2); printf("exitn"); wait(&rv); /* Wait for child process to end */ fprintf(stderr,"Child exited with a %d valuen",rv); } else{ /* A zero PID indicates that this is the child process */ dup2(commpipe[0],0); /* Replace stdin with the in side of the pipe */ close(commpipe[1]); /* Close unused side of pipe (out side) */ /* Replace the child fork with a new process */ if(execl("child","child",NULL) == -1){ fprintf(stderr,"execl Error!"); exit(1); } } return 0; } child.c CPP / C++ / C Code:
#include <stdio.h> int main(){ char string[100]; printf("Child Processn"); printf("-------------n"); do{ printf("Enter Command: "); fflush(stdout); /* Must flush to see command prompt */ fgets(string,100,stdin); printf("%sn",string); /* No flush necessary because new line flushes */ }while(!strstr(string,"exit")); return 0; } Conclusion __________________
The best damn Sports Blog period.
|
||||
// http://pubads.g.doubleclick.net/gampad/ads?correlator=1288382455678&output=json_html&callback=GA_googleSetAdContentsBySlotForSync&impl=s&prev_afc=1&pstok=ohiBuKB5_ZEKBBCMzQM&client=ca-pub-6718186524237536&slotname=gidforums_incontent_728_90&page_slots=gidforums_top_right_728_90_programming%2Cgidforums_incontent_728_90&cust_params=ref_is_se%3D1%26f%3D41&cookie=ID%3D8f426dc9482b74cf%3AT%3D1288382455%3AS%3DALNI_MbJyF29vmNnzCrUsyppfBlY22quAw&cookie_enabled=1&ga_vid=390869917.1288382456&ga_sid=1288382456&ga_hid=714158732&url=http%3A%2F%2Fwww.gidforums.com%2Ft-3369.html&ref=http%3A%2F%2Fwww.google.hu%2Fsearch%3Fq%3Dstart%2Bprogram%2Bfrom%2BC%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a&lmt=1288382453&dt=1288382456325&cc=100&biw=1425&bih=695&ifi=2&adk=3104553200&color_link=22229C&channel=0551060857&u_tz=120&u_his=3&u_java=true&u_h=900&u_w=1440&u_ah=900&u_aw=1440&u_cd=24&u_nplug=2&u_nmime=35&flash=10.0.32
// |
#2
|
|||
|
|||
in windows, you can do this with the notoriously complicated Windows API. I am mostly learning this off MSDN as I go along, so bear with me.
first of all, you must include the windows.h header. there is one function, CreateProcess(), that runs an external program. However, like many functions in the Windows API, it requires an insane list of structures and other parameters. we will later use some of these for pipes and stuff. (I say “and stuff” because there is no one analog to the Linux “pipe” in Windows, or I don’t know about it). CPP / C++ / C Code:
#include <windows.h> #include <iostream> using namespace std; int main(int argc, char * argv[]) { if (argc != 2) { cout << "waaah! you didn't give me a program to execute." << endl; return 1; //bail out if there is no program to run } STARTUPINFO startinfo; //structure that allows you to, for example, run the program minimized (if it is a window program) ZeroMemory(&startinfo, sizeof(STARTUPINFO)); //initialize the memory and all the members startinfo.cb = sizeof(STARTUPINFO); startinfo.lpReserved = NULL; startinfo.lpDesktop = NULL; startinfo.lpTitle = NULL; startinfo.dwFlags = 0; startinfo.cbReserved2 = 0; startinfo.lpReserved2 = NULL; PROCESS_INFORMATION procinfo; //CreateProcess fills this structure with stuff you can pass to other winapi functions to control the child process ZeroMemory(&procinfo, sizeof(PROCESS_INFORMATION)); CreateProcess(NULL, argv[1], NULL, NULL, false, 0, NULL, NULL, &startinfo, &procinfo); //this is the most important line in the program. it runs the program specified in the command-line argument (argv[1]) CloseHandle(procinfo.hProcess); //and, clean up after Windows because I don't need the process handles it gives me CloseHandle(procinfo.hThread); } so, a bit more complicated than fork(), execl(), and dup2(), but it works. for more information, here are the MSDN pages on: someone feel free to correct me with a shorter and more intuitive version of this extremely unfriendly-looking and complicated code. (please!) |
#3
|
||||
|
||||
Hello,
There is a more intuitive way, but it’s not shorther. Rather, it contains extensive error-handling and command processing; such as the exe path, etc… An example code can be found here: C++ CreateProcess Example Or, a slight shorter version of your current code: CPP / C++ / C Code:
#include <windows.h> #include <iostream> using namespace std; int main(int argc, char *argv[]) { // Local variables PROCESS_INFORMATION pi; STARTUPINFO si; // Argument count if (argc != 2) return EXIT_FAILURE; // Initialize memset(&si,0,sizeof(si)); si.cb = sizeof(si); // Execute if(!CreateProcess(NULL, argv[1], NULL, NULL, false, 0, NULL,NULL,&si,&pi)) { // Failed cout << "Could not run the program." << endl; return EXIT_FAILURE; } // Finished return EXIT_SUCCESS; } – Stack Overflow __________________
Following the rules will ensure you get a prompt answer to your question. If posting code, please include BB [C] / [C++] tags. Your question may have been asked before, try the search facility.
|
#4
|
|||
|
|||
all right. I’m going to have to refer you to the MSDN sample code for “Creating Child Processes with Redirected Input and Output.”
Here The idea seems to be to create two pipes: One that the parent writes to and the child reads from (stdin/cin), and one that the child writes to (stdout/cout) and parent reads from. The parent process creates the pipes and therefore has the handles. It specifies that those handles should be inheritable, so that the child process can use them. But it also has to duplicate its own stdin/out/err handles so that the child doesn’t inherit those. MSDN also gives some Windows API stuff for the child process, but from what I understand the child could just be a regular console C or C++ program that uses stdin/stdout or cin/cout, respectively. Also their child program is a bad idea because it goes into an infinite loop, and uses an uninitialized variable (chBuf). |
#5
|
|||
|
|||
Well, here’s a sort of external command prompt launcher that utilizes pipes. Sort of like a shell. I’m sure it could be more efficient in many ways, but it’s a start.
CPP / C++ / C Code:
#include <windows.h> #include <stdio.h> #define MAX_BUFFER_SIZE 512 int EmulateCommandPrompt(LPSTR cmdline) { STARTUPINFO sti = { 0 }; SECURITY_ATTRIBUTES sats = { 0 }; PROCESS_INFORMATION pi = { 0 }; HANDLE pipin_w, pipin_r, pipout_w, pipout_r; BYTE buffer[MAX_BUFFER_SIZE]; DWORD writ, excode, read, available; int ret = 0; pipin_w = pipin_r = pipout_w = pipout_r = NULL; for(;;) { //set SECURITY_ATTRIBUTES struct fields sats.nLength = sizeof(sats); sats.bInheritHandle = TRUE; sats.lpSecurityDescriptor = NULL; //create child's stdout pipes if(!CreatePipe(&pipout_r, &pipout_w, &sats, 0)) break; //and its stdin pipes if(!CreatePipe(&pipin_r, &pipin_w, &sats, 0)) break; printf("Created pipesn"); //now set STARTUPINFO struct fields (from the child's point of view) sti.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; sti.wShowWindow = SW_HIDE; sti.hStdInput = pipin_r; sti.hStdOutput = pipout_w; sti.hStdError = pipout_w; //create the process... if(!CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &sti, π)) break; printf("Created process (%s)n", cmdline); //now have a continuous loop to get and recieve info for(;;) { //make sure process is still running GetExitCodeProcess(pi.hProcess, &excode); if(excode != STILL_ACTIVE) break; //printf("Process still runningn"); //give it time to set up/react Sleep(500); //now check to see if process has anything to say if(!PeekNamedPipe(pipout_r, buffer, sizeof(buffer), &read, &available, NULL)) ret = 10; //printf("Peekedn"); //is there anything to be read in the pipe? if(read) { do { ZeroMemory(buffer, sizeof(buffer)); //read it and print to stdout if(!ReadFile(pipout_r, buffer, sizeof(buffer), &read, NULL) || !read) ret = 7; buffer[read] = 0; fprintf(stdout, "%s", buffer); if(ret) break; } while(read >= sizeof(buffer)); } //make sure we didn't run into any errors if(!ret) { //get info and write it to pipe ZeroMemory(buffer, sizeof(buffer)); fgets(buffer, sizeof(buffer), stdin); if(!strnicmp(buffer, "exit", 4)) ret = 12; if(!WriteFile(pipin_w, buffer, strlen(buffer), &writ, NULL)) ret = 8; } if(ret) break; } break; } //clean up any unfinished business if(pipin_w != NULL) CloseHandle(pipin_w); if(pipin_r != NULL) CloseHandle(pipin_r); if(pipout_w != NULL) CloseHandle(pipout_w); if(pipout_r != NULL) CloseHandle(pipout_r); if(pi.hProcess != NULL) CloseHandle(pi.hProcess); if(pi.hThread != NULL) CloseHandle(pi.hThread); return ret; } int main(int argc, char *argv[]) { EmulateCommandPrompt(NULL); return 0; } |
Recent GIDBlogNot selected for officer school by crystalattice
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Mozilla Thunderbird | dsmith | Computer Software Forum – Linux | 9 | 01-Mar-2005 11:56 |
Need help with a C program (Long) | McFury | C Programming Language | 3 | 29-Apr-2004 20:06 |
Re: Piping and Redirection | WaltP | C Programming Language | 1 | 11-Apr-2004 08:01 |
Linux Kernel Upgrade Mini Howto | dsmith | Computer Software Forum – Linux | 3 | 05-Apr-2004 22:10 |
Call a C program through Linux shell script | nuwandee | C Programming Language | 3 | 29-Mar-2004 21:54 |
Network Sites: GIDNetwork · GIDWebHosts · GIDSearch · Learning Journal by J de Silva, The
//
// http://www.google-analytics.com/ga.js//
// http://ctxt.tribalfusion.com/ctxt/textlinks.js http://ctxtad.tribalfusion.com/ctxtad/Keywords?ads=5&link=http%3A//www.gidforums.com/t-3369.html&mode=prod&serveMode=Normal&type=textLinks&rq=2039755257.6741934