Update Gtk+2 text view widget from another thread - c

I am writing a chat client in GTK. The client has the main GTK loop, plus a spawned thread that sits and blocks at the read() function waiting for input from a file descriptor connected to a socket.
Once the read function gets past the blocking, it has a char buffer of text that I would like to append to the GTK Text View widget, however, this is in a thread that is different than the main GTK loop.
How can I most quickly update the GUI from that other thread? In Java, I would have used the SwingUtilities.invokeLater(new Runnable()) method to cause that method to be called from the main thread. I want similar behavior in C and using GTK.
Here is the function that is called from the new thread...
void* messageReceived(void* data)
{
struct ClientWindow* localVar = (struct ClientWindow*)data;
while(TRUE)
{
char buf[256];
int bytesRead = read(localVar->socketFileDescriptor, buf, 256);
GtkTextBuffer* tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(localVar->chatHistoryTextView));
GtkTextIter end;
//This code needs to execute in the main thread
gtk_text_buffer_get_end_iter(tb, &end);
gtk_text_buffer_insert(tb, &end, buf, -1);
}
}

The solution I came up with is using the g_idle_add() function. I don't know if I am missing something because the solution is very simple but no one else identified it, so it makes me a bit worried.
void* messageReceived(void* data)
{
struct ClientWindow* localVar = (struct ClientWindow*)data;
char* message = NULL;
int bytesRead = 0;
do
{
message = bufferedRead(localVar->socketFileDescriptor, 4, &bytesRead);
struct UpdateGUIMessage* updateGui = malloc(sizeof(struct UpdateGUIMessage));
memset(updateGui, 0, sizeof(struct UpdateGUIMessage));
updateGui->clientWindow = localVar;
updateGui->message = message;
updateGui->bytesRead = bytesRead;
g_idle_add(G_SOURCE_FUNC(updateGUI), updateGui);
}while(message != NULL);
}
bool updateGUI(void* data)
{
struct UpdateGUIMessage* localVar = (struct UpdateGUIMessage*)data;
GtkTextBuffer* tb = gtk_text_view_get_buffer(GTK_TEXT_VIEW(localVar->clientWindow->chatHistoryTextView));
GtkTextIter end;
gtk_text_buffer_get_end_iter(tb, &end);
gtk_text_buffer_insert(tb, &end, localVar->message, localVar->bytesRead);
free(localVar->message);
free(data);
return FALSE; //So it only gets called once and then is removed
}

Related

Function outputting sound through alsa not working when called via pthread create: no sound, 100% CPU usage

I have a program that receives messages via a socket and starts or stops playing a certain sound file depending on the message. In order for the "stop" message to work, I need the sound to play from a separate thread. My solution is to play the sound using alsa from a function I invoke using pthread_create(), and upon receiving a stop message I end the thread using pthread_cancel(). The function that plays the sound is called play_sound(void *args);
Here's what works:
struct args *args;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
play_sound((void *) args);
but as soon as I try to run the function from within a new thread, I get no sound and 100% CPU usage on both my threads:
struct args *args;
int sound_thread;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
pthread_create(&sound_thread, NULL, (void *) play_sound, (void *) args);
I have no idea where to even begin troubleshooting.
My code looks as follows:
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <pthread.h>
#include "server.h"
#include "sound.h"
//#include "log.h"
int sound_thread;
struct args {
FILE *fp;
float volume;
};
void init_sound ()
{
sound_thread = -1;
}
void stop_sound ()
{
if (sound_thread != -1) {
pthread_cancel(sound_thread);
keep_playing = false;
sound_thread = -1;
}
}
void dispatch_sound (FILE *fp, float volume)
{
// this function serves to create a new thread for the
// sound to be played from. This is what's giving me
// headaches.
if (sound_thread != -1) {
stop_sound();
}
struct args *args = (struct args *) malloc(sizeof(struct args));
args->fp = fp;
args->volume = volume;
if (pthread_create(&sound_thread, NULL, (void *) play_sound, args) != 0)
sound_thread = -1;
}
}
bool play_sound (void *args)
{
// This function actually plays the sound using functions
// from the alsa lib. it works when invoked regularly without
// threading.
keep_playing = true;
FILE *fp;
int volume;
bool success;
unsigned int samplerate;
int bufsz;
char *buf;
snd_pcm_t *pcm;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
samplerate = SAMPLERATE;
fp = ((struct args *) args)->fp;
volume = ((struct args *) args)->volume;
// volume is not actually used right now, since I took out
// the function that sets the volume before playing the
// audio in order to make it easier to pinpoint the issue.
if (snd_pcm_open(&pcm, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
success = false;
}
snd_pcm_hw_params_alloca(&params);
snd_pcm_hw_params_any(pcm, params);
if (snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_channels(pcm, params, CHANNELS) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_rate_near(pcm, params, &samplerate, 0) < 0) {
success = false;
}ยด
if (snd_pcm_hw_params(pcm, params) < 0) {
success = false;
}
snd_pcm_hw_params_get_period_size(params, &frames, 0);
bufsz = frames * CHANNELS * SAMPLE_SIZE;
buf = (char *) malloc(bufsz);
while (keep_playing) {
while (fread(buf, bufsz, 1, fp) != 0 && keep_playing) {
int err;
if ((err = snd_pcm_writei(pcm, buf, frames)) == -EPIPE) {
snd_pcm_prepare(pcm);
}
}
rewind(fp);
}
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
free(buf);
return success;
}
From the man page of pthread_cancel:
On Linux, cancellation is implemented using signals. Under the NPTL threading implementation, the first real-time signal (i.e., signal 32)
is used for this purpose. On LinuxThreads, the second real-time signal is used, if real-time signals are available, otherwise SIGUSR2 is
used.
In your while(keep_playing) loop, you aren't yielding the thread enough to handle the cancel signal; in your main thread; you aren't waiting for the result of the cancel request, ergo both threads hog the cpu.
A small delay before you restart playing the sound and pthread_join() after you call pthread_cancel should fix your problem.

Which synchronization primitive should I employ here?

while(1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(mq_identifier, message_buffer, _mqueue_max_msg_size NULL);
if(message_len == -1) { /* error handling... */}
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if(ret) { /* error handling ... */}
}
void * handle_message (void * message) {
puts((char *) message);
return NULL;
}
The above example is not an MRE but it is extremely simple:
I've got a main thread with a loop that constantly consumes messages from a message queue. Once a new message is received, it is stored in the local message_buffer buffer. Then, a new thread is spawned to "take care" of said new message, and thus the message buffer's address is passed into handle_message, which the new thread subsequently executes.
The problem
Often, 2 threads will print the same message, even though I can verify with a 100% certainty that the messages in the queue were not the same.
I am not completely certain, but I think I understand why this is happening:
say that I push 2 different messages to the mqueue and only then I begin consuming them.
In the first iteration of the while loop, the message will get consumed from the queue and saved to message_buffer. A new thread will get spawned and the address of message_length passed to it. But that thread may not be fast enough to print the buffer's contents to the stream before the next message gets consumed (on the next iteration of the loop), and the contents of message_buffer subsequently overridden. Thus the first and second thread now print the same value.
My question is: what is the most efficient way to solve this? I'm pretty new to parallel programming and threading/pthreads and I'm pretty overwhelmed by the different synchronization primitives.
Mutex trouble
static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
while(1) {
char message_buffer[SIZE];
pthread_mutex_lock(&m);
ssize_t message_length = mq_receive(mq_identifier, message_buffer, _mqueue_max_msg_size NULL);
pthred_mutex_unlock(&m);
if(message_len == -1) { /* error handling... */}
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if(ret) { /* error handling ... */}
}
void * handle_message (void * message) {
char own_buffer[SIZE];
pthread_mutex_lock(&m);
strncpy(own_buffer, (char *) message, SIZE);
pthread_mutex_unlock(&m);
puts(own_buffer);
return NULL;
}
I don't think my current mutex implementation is right as the threads are still receiving duplicate messages. The main thread can lock the mutex, consume a message into the buffer, unlock the mutex, spawn a thread, but that thread still may hang and the main one could just rewrite the buffer again (as the buffer mutex was never locked by the new thread), effectively making my current mutex implementation useless? How do I overcome this?
The problem is that you end the loop that contains message_buffer before guaranteeing that the thread has finished with that memory.
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if (ret) { /* error handling */ }
/****** Can't go beyond here until thread is done with message_buffer. ******/
}
void * handle_message (void * message) {
char own_buffer[SIZE];
strncpy(own_buffer, (char *) message, SIZE);
/******* Only now can the caller loop back. ******/
puts(own_buffer);
return NULL;
}
You could use a semaphore or similar.
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static int copied = 0;
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, message_buffer);
if (ret) { /* error handling */ }
// Wait until threads is done with message_buffer.
pthread_mutex_lock(&mutex);
while (!copied) pthread_cond_wait(&cond, &mutex);
copied = 0;
pthread_mutex_unlock(&mutex);
}
void * handle_message (void * message) {
char own_buffer[SIZE];
strncpy(own_buffer, (char *) message, SIZE);
// Done with caller's buffer.
// Signal caller to continue.
pthread_mutex_lock(&mutex);
copied = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
puts(own_buffer);
return NULL;
}
(The added chunks effectively perform semaphore operations. See the last snippet of this answer for a more generic implementation.)
But there's a simpler solution: Make the copy before creating the thread.
while (1) {
char message_buffer[SIZE];
ssize_t message_length = mq_receive(...);
if (message_len == -1) { /* error handling */ }
pthread_t pt1;
int ret = pthread_create(&pt1, NULL, handle_message, strdup(message_buffer));
if (ret) { /* error handling */ }
}
void * handle_message (void * message) {
char * own_buffer = message;
puts(own_buffer);
free(own_buffer);
return NULL;
}

aio_write on Large Files

I'm trying to essentially mimic the functionality of sendfile(2) in an asynchronous fashion using aio_read(3) and aio_write(3).
Everything seems to be working fine, with the exception of testing large (> 150k) files.
I have a simple struct io_request I am using to keep track of the transfers:
struct io_request {
int status;
struct aiocb *aiocbp;
int sfd;
};
First, I build the aio_read() call:
struct io_request * ioreq = malloc(sizeof(struct io_request));
ioreq->status = EINPROGRESS;
ioreq->sfd = sfd;
struct aiocb * aiocbreq = malloc(sizeof(struct aiocb));
memset(aiocbreq, 0, sizeof(struct aiocb));
ioreq->aiocbp = aiocbreq;
ioreq->aiocbp->aio_fildes = ffd;
if (ioreq->aiocbp->aio_fildes == -1) {
perror("aio_fildes");
}
ioreq->aiocbp->aio_buf = malloc(st.st_size);
if (ioreq->aiocbp->aio_buf == NULL) {
perror("aio_buf malloc");
}
ioreq->aiocbp->aio_nbytes = st.st_size;
ioreq->aiocbp->aio_reqprio = 0;
ioreq->aiocbp->aio_offset = 0;
ioreq->aiocbp->aio_sigevent.sigev_signo = IO_READ_SIGNAL;
ioreq->aiocbp->aio_sigevent.sigev_value.sival_ptr = ioreq;
if (aio_read(ioreq->aiocbp) == -1) {
perror("aio_read");
}
Which then later is captured in a IO_READ_SIGNAL handler:
static void
aio_read_handler(int sig, siginfo_t *si, void *ucontext)
{
if (si->si_code == SI_ASYNCIO) {
struct io_request *ioreq = si->si_value.sival_ptr;
// Build the AIO write request
struct aiocb aiocbreq;
memset(&aiocbreq, 0, sizeof(struct aiocb));
aiocbreq.aio_fildes = ioreq->sfd;
aiocbreq.aio_buf = ioreq->aiocbp->aio_buf;
aiocbreq.aio_nbytes = ioreq->aiocbp->aio_nbytes;
aiocbreq.aio_sigevent.sigev_signo = IO_WRITE_SIGNAL;
aiocbreq.aio_sigevent.sigev_value.sival_ptr = ioreq;
if (aio_write((void *) &aiocbreq) == -1) {
perror("aio_write");
}
}
}
I can confirm that inside the handler, even for large files, the contents of ioreq->aiocbp->aio_buf is full and complete.
Later, the aio_write() is captured in a IO_WRITE_SIGNAL handler:
static void
aio_write_handler(int sig, siginfo_t *si, void *ucontext)
{
if (si->si_code == SI_ASYNCIO) {
struct io_request *ioreq = si->si_value.sival_ptr;
ssize_t bytes_written = aio_return(ioreq->aiocbp);
printf("Wrote %zu of %zu bytes\n", bytes_written, ioreq->aiocbp->aio_nbytes);
//free(ioreq->aiocbp);
//free(ioreq);
if (aio_error(ioreq->aiocbp) != 0) {
perror("aio_write_handler");
}
}
}
At this point aio_write() should have been completed. I check the return values and act accordingly. Both calls report the appropriate number of bytes have been written and no errors arose during the write.
The greater application is an HTTP server. I speculate that this problem arrises because the remote client cannot read fast enough to keep up with the aio_write(). When I had a sendfile() implementation of this, I had to call sendfile() multiple times to complete the file transfer.
Several direct questions:
Why does aio_return() and aio_error() not report any problems?
How can I fix this behavior?
Are there ways to buffer aio_write()? I was thinking of capping of n_bytes inside struct aiocb passed to aio_write(), and just calling aio_write() multiple times from inside aio_write_handler().
Thanks for your help!
If I understand correctly, you're using aio_return before aio_error. The aio_return man page says,
This function should be called only once for any given request, after
aio_error(3) returns something other than EINPROGRESS.

C pthread_create

typedef struct client
{
pthread thread;
Window_t *win
}client;
client * client_create(int ID)
{
client *new_Client = (client *) malloc(sizeof(client));
char title[16];
if (!new_Client) return NULL;
sprintf(title, "Client %d", ID);
/* Creates a window and set up a communication channel with it */
if ((new_Client->win = window_create(title)))
return new_Client;
else {
free(new_Client);
return NULL;
}
}
When the user inputs 'e' I try to create a new thread with a new client and window by doing this is my int_main.
The flag is just to tell me that the user entered e
if(eflag == 1)
{
client *c = NULL;
c=client_create(started);
pthread_create(&c.thread, NULL, client_create, (void *) &started);
started++;
eflag =0;
}
This is supposed to create a new client on a new thread on a new window but it doesn't do that.
I'm not sure what to put in my pthread_create, and also how am I supposed get a new instance of client, because the client_create function creates a new window. And when I try to create a new thread by doing pthread_create it also creates a new window... If this was java oeverytime the user pressed 'e' I would just create a new instance of the class client... but I can't really do that here. any suggestions?
The prototype of the pthread_create function is
int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);
your start_routine definition needs to match void *(*start_routine)(void*).. And it is executed in a new thread context.
void *my_client_routine(void *arg) {
while(1) {
// do what each client does.
}
}
Currently in your main() you are executing your create_client function twice. once before the call to pthread_create() and once as part of the new thread spawned from pthread_create. What you probably want to do is
c = create_client()
pthread_create(&c.thread, NULL, my_client_routine, &args_that_client_routine_needs);

How do I launch a process and obtain its output?

In the C language using the Windows API, how can I get the output of a process when I have its process information?
I have code like this:
STARTUPINFO si1;
ZeroMemory(&si1,sizeof(si1));
PROCESS_INFORMATION pi1;
ZeroMemory(&pi1,sizeof(pi1));
BOOL bRes1=CreateProcess(_T("C:\\User\\asd.exe"),cmd_line1,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL, &si1,&pi1);
and the process asd.exe prints a certain output, I want to get it to my process(the one i used the code above in).
This answer is probably a bit longer than you expected, but that's how the Windows API is sometimes. In any case, even though this takes more code than it initially seems like it should need, this at least provides a fairly clean, easy to use interface for programs that want to do things like this. The code is commented fairly liberally, explaining not only how to use the function it provides, but also how it's doing most of what it does, and what Windows requires if you decide to write code based on this instead of using it directly.
I should also point out that this is some fairly old code. It works well enough that I haven't had any reason to rewrite it, but if I was doing it again today, I'm pretty sure I'd do it quite a bit differently (for one thing, I'd undoubtedly use C++ instead of straight C, as I did here).
This does also contain some tidbits of code that are frequently useful for completely unrelated purposes (e.g., I've used system_error quite a few places -- it's Windows-only, but really incidental to spawning a child process).
Anyway, we'll start with spawn.h, which defines the interface to the code:
#ifndef SPAWN_H_INCLUDED_
#define SPAWN_H_INCLUDED_
// What to do if you ask to create a file and it already exists.
// We can fail to create it, overwrite the existing content, or append the
// new content to the existing content.
enum { FAIL, OVERWRITE, APPEND };
// This just specifies the type of a thread procedure to use to handle a stream
// to/from the child, if you decided to do that.
//
typedef unsigned long (__stdcall *ThrdProc)(void *);
// stream_info is the real core of the code. It's what lets you specify how
// to deal with a particular stream. When you call CreateDetchedProcess,
// you need to pass the address of an array of three stream_info objects
// that specify the handling for the child's standard input, standard
// output, and standard error streams respectively. If you specify a
// filename, that stream will be connected to the named file. If you set
// filename to NULL, you can instead specify a procedure that will be
// started in a thread that will provide data for that stream, or process
// the data coming from that stream. Toward the bottom of spawn.c there are
// a couple of sample handlers, one that processes standard error, and the
// other that processes standard output from a spawned child process.
//
typedef struct {
char *filename;
ThrdProc handler;
HANDLE handle;
} stream_info;
// Once you've filled in your stream_info structures, spawning the child is
// pretty easy: just pass the name of the executable for the child, and the
// address of the stream_info array. This handles most of the usual things:
// if you don't specify an extension for the file, it'll search for it with
// extensions of `.com", ".exe", ".cmd", and ".bat" in the current
// directory, and then in any directory specified by the PATH environment
// variable. It'll open/create any files you've specified in the
// stream_info structures, and create pipes for any streams that are to be
// directed to the parent, and start up threads to run any stream handlers
// specified.
//
HANDLE CreateDetachedProcess(char const *name, stream_info *streams);
#endif
Then the implementation of CreateDetachedProcess (along with some test/demo code):
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <ctype.h>
#include <io.h>
#include <fcntl.h>
#include <stdlib.h>
#include "spawn.h"
static void system_error(char const *name) {
// A function to retrieve, format, and print out a message from the
// last error. The `name' that's passed should be in the form of a
// present tense noun (phrase) such as "opening file".
//
char *ptr = NULL;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
GetLastError(),
0,
(char *)&ptr,
1024,
NULL);
fprintf(stderr, "%s\n", ptr);
LocalFree(ptr);
}
static void InitializeInheritableSA(SECURITY_ATTRIBUTES *sa) {
sa->nLength = sizeof *sa;
sa->bInheritHandle = TRUE;
sa->lpSecurityDescriptor = NULL;
}
static HANDLE OpenInheritableFile(char const *name) {
SECURITY_ATTRIBUTES sa;
HANDLE retval;
InitializeInheritableSA(&sa);
retval = CreateFile(
name,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (INVALID_HANDLE_VALUE == retval) {
char buffer[100];
sprintf(buffer, "opening file %s", name);
system_error(buffer);
return retval;
}
}
static HANDLE CreateInheritableFile(char const *name, int mode) {
SECURITY_ATTRIBUTES sa;
HANDLE retval;
DWORD FSmode = mode ? OPEN_ALWAYS : CREATE_NEW;
InitializeInheritableSA(&sa);
retval = CreateFile(
name,
GENERIC_WRITE,
FILE_SHARE_READ,
&sa,
FSmode,
FILE_ATTRIBUTE_NORMAL,
0);
if (INVALID_HANDLE_VALUE == retval) {
char buffer[100];
sprintf(buffer, "creating file %s", name);
system_error(buffer);
return retval;
}
if ( mode == APPEND )
SetFilePointer(retval, 0, 0, FILE_END);
}
enum inheritance { inherit_read = 1, inherit_write = 2 };
static BOOL CreateInheritablePipe(HANDLE *read, HANDLE *write, int inheritance) {
SECURITY_ATTRIBUTES sa;
InitializeInheritableSA(&sa);
if ( !CreatePipe(read, write, &sa, 0)) {
system_error("Creating pipe");
return FALSE;
}
if (!inheritance & inherit_read)
DuplicateHandle(
GetCurrentProcess(),
*read,
GetCurrentProcess(),
NULL,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (!inheritance & inherit_write)
DuplicateHandle(
GetCurrentProcess(),
*write,
GetCurrentProcess(),
NULL,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
return TRUE;
}
static BOOL find_image(char const *name, char *buffer) {
// Try to find an image file named by the user.
// First search for the exact file name in the current
// directory. If that's found, look for same base name
// with ".com", ".exe" and ".bat" appended, in that order.
// If we can't find it in the current directory, repeat
// the entire process on directories specified in the
// PATH environment variable.
//
#define elements(array) (sizeof(array)/sizeof(array[0]))
static char *extensions[] = {".com", ".exe", ".bat", ".cmd"};
int i;
char temp[FILENAME_MAX];
if (-1 != access(name, 0)) {
strcpy(buffer, name);
return TRUE;
}
for (i=0; i<elements(extensions); i++) {
strcpy(temp, name);
strcat(temp, extensions[i]);
if ( -1 != access(temp, 0)) {
strcpy(buffer, temp);
return TRUE;
}
}
_searchenv(name, "PATH", buffer);
if ( buffer[0] != '\0')
return TRUE;
for ( i=0; i<elements(extensions); i++) {
strcpy(temp, name);
strcat(temp, extensions[i]);
_searchenv(temp, "PATH", buffer);
if ( buffer[0] != '\0')
return TRUE;
}
return FALSE;
}
static HANDLE DetachProcess(char const *name, HANDLE const *streams) {
STARTUPINFO s;
PROCESS_INFORMATION p;
char buffer[FILENAME_MAX];
memset(&s, 0, sizeof s);
s.cb = sizeof(s);
s.dwFlags = STARTF_USESTDHANDLES;
s.hStdInput = streams[0];
s.hStdOutput = streams[1];
s.hStdError = streams[2];
if ( !find_image(name, buffer)) {
system_error("Finding Image file");
return INVALID_HANDLE_VALUE;
}
// Since we've redirected the standard input, output and error handles
// of the child process, we create it without a console of its own.
// (That's the `DETACHED_PROCESS' part of the call.) Other
// possibilities include passing 0 so the child inherits our console,
// or passing CREATE_NEW_CONSOLE so the child gets a console of its
// own.
//
if (!CreateProcess(
NULL,
buffer, NULL, NULL,
TRUE,
DETACHED_PROCESS,
NULL, NULL,
&s,
&p))
{
system_error("Spawning program");
return INVALID_HANDLE_VALUE;
}
// Since we don't need the handle to the child's thread, close it to
// save some resources.
CloseHandle(p.hThread);
return p.hProcess;
}
static HANDLE StartStreamHandler(ThrdProc proc, HANDLE stream) {
DWORD ignore;
return CreateThread(
NULL,
0,
proc,
(void *)stream,
0,
&ignore);
}
HANDLE CreateDetachedProcess(char const *name, stream_info *streams) {
// This Creates a detached process.
// First parameter: name of process to start.
// Second parameter: names of files to redirect the standard input, output and error
// streams of the child to (in that order.) Any file name that is NULL will be
// redirected to an anonymous pipe connected to the parent.
// Third Parameter: handles of the anonymous pipe(s) for the standard input, output
// and/or error streams of the new child process.
//
// Return value: a handle to the newly created process.
//
HANDLE child_handles[3];
HANDLE process;
int i;
// First handle the child's standard input. This is separate from the
// standard output and standard error because it's going the opposite
// direction. Basically, we create either a handle to a file the child
// will use, or else a pipe so the child can communicate with us.
//
if ( streams[0].filename != NULL ) {
streams[0].handle = NULL;
child_handles[0] = OpenInheritableFile(streams[0].filename);
}
else
CreateInheritablePipe(child_handles, &(streams[0].handle), inherit_read);
// Now handle the child's standard output and standard error streams. These
// are separate from the code above simply because they go in the opposite
// direction.
//
for ( i=1; i<3; i++)
if ( streams[i].filename != NULL) {
streams[i].handle = NULL;
child_handles[i] = CreateInheritableFile(streams[i].filename, APPEND);
}
else
CreateInheritablePipe(&(streams[i].handle), child_handles+i, inherit_write);
// Now that we've set up the pipes and/or files the child's going to use,
// we're ready to actually start up the child process:
process = DetachProcess(name, child_handles);
if (INVALID_HANDLE_VALUE == process)
return process;
// Now that we've started the child, we close our handles to its ends of the pipes.
// If one or more of these happens to a handle to a file instead, it doesn't really
// need to be closed, but it doesn't hurt either. However, with the child's standard
// output and standard error streams, it's CRUCIAL to close our handles if either is a
// handle to a pipe. The system detects the end of data on a pipe when ALL handles to
// the write end of the pipe are closed -- if we still have an open handle to the
// write end of one of these pipes, we won't be able to detect when the child is done
// writing to the pipe.
//
for ( i=0; i<3; i++) {
CloseHandle(child_handles[i]);
if ( streams[i].handler )
streams[i].handle =
StartStreamHandler(streams[i].handler, streams[i].handle);
}
return process;
}
#ifdef TEST
#define buf_size 256
unsigned long __stdcall handle_error(void *pipe) {
// The control (and only) function for a thread handling the standard
// error from the child process. We'll handle it by displaying a
// message box each time we receive data on the standard error stream.
//
char buffer[buf_size];
HANDLE child_error_rd = (HANDLE)pipe;
unsigned bytes;
while (ERROR_BROKEN_PIPE != GetLastError() &&
ReadFile(child_error_rd, buffer, 256, &bytes, NULL))
{
buffer[bytes+1] = '\0';
MessageBox(NULL, buffer, "Error", MB_OK);
}
return 0;
}
unsigned long __stdcall handle_output(void *pipe) {
// A similar thread function to handle standard output from the child
// process. Nothing special is done with the output - it's simply
// displayed in our console. However, just for fun it opens a C high-
// level FILE * for the handle, and uses fgets to read it. As
// expected, fgets detects the broken pipe as the end of the file.
//
char buffer[buf_size];
int handle;
FILE *file;
handle = _open_osfhandle((long)pipe, _O_RDONLY | _O_BINARY);
file = _fdopen(handle, "r");
if ( NULL == file )
return 1;
while ( fgets(buffer, buf_size, file))
printf("%s", buffer);
return 0;
}
int main(int argc, char **argv) {
stream_info streams[3];
HANDLE handles[3];
int i;
if ( argc < 3 ) {
fputs("Usage: spawn prog datafile"
"\nwhich will spawn `prog' with its standard input set to"
"\nread from `datafile'. Then `prog's standard output"
"\nwill be captured and printed. If `prog' writes to its"
"\nstandard error, that output will be displayed in a"
"\nMessageBox.\n",
stderr);
return 1;
}
memset(streams, 0, sizeof(streams));
streams[0].filename = argv[2];
streams[1].handler = handle_output;
streams[2].handler = handle_error;
handles[0] = CreateDetachedProcess(argv[1], streams);
handles[1] = streams[1].handle;
handles[2] = streams[2].handle;
WaitForMultipleObjects(3, handles, TRUE, INFINITE);
for ( i=0; i<3; i++)
CloseHandle(handles[i]);
return 0;
}
#endif
As I understand you are using windows (because you mentioned process informaiton). If you want to get the launched process output, you must capture its output stream. Here's an explanatory link which shoes how it can be done.
my2c

Resources