I've searched to do this but I can't find what am I doing incorrectly.I'm trying to make this function appends data every time it's called but it always do it once. If the file doesn't exist it creates a new one and write on file for ONLY once if the file exist it does nothing (or maybe overwrite)
void WriteToFile (char data[],wchar_t filename[] )
{
HANDLE hFile;
DWORD dwBytesToWrite = (DWORD)strlen(data);
DWORD dwBytesWritten ;
BOOL bErrorFlag = FALSE;
hFile = CreateFile((LPCWSTR)filename, // name of the write
GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_NEW, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
DisplayError(TEXT("CreateFile"));
_tprintf(TEXT("Terminal failure: Unable to open file \"%s\" for write.\n"), filename);
return;
}
bErrorFlag = WriteFile(
hFile, // open file handle
data, // start of data to write
dwBytesToWrite, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL); // no overlapped structure
if (FALSE == bErrorFlag)
{
DisplayError(TEXT("WriteFile"));
printf("Terminal failure: Unable to write to file.\n");
}
else
{
if (dwBytesWritten != dwBytesToWrite)
{
// This is an error because a synchronous write that results in
// success (WriteFile returns TRUE) should write all data as
// requested. This would not necessarily be the case for
// asynchronous writes.
printf("Error: dwBytesWritten != dwBytesToWrite\n");
}
else
{
_tprintf(TEXT("Wrote %d bytes to %s successfully.\n"), dwBytesWritten, filename);
}
}
CloseHandle(hFile);
}
And this is where I call the function in WM_COMMAND
//When a menu item selected execute this code
case IDM_FILE_SAVE:
saveBool = true;
char Str[] = "this is my own data";
wchar_t filename[] = L"data.txt";
WriteToFile(Str, filename);
break;
if the file exist it does nothing
As it should be. Per the CreateFile() documentation:
CREATE_NEW
1
Creates a new file, only if it does not already exist.
If the specified file exists, the function fails and the last-error code is set to ERROR_FILE_EXISTS (80).
If the specified file does not exist and is a valid path to a writable location, a new file is created.
For what you are attempting to do, use OPEN_ALWAYS instead:
OPEN_ALWAYS
4
Opens a file, always.
If the specified file exists, the function succeeds and the last-error code is set to ERROR_ALREADY_EXISTS (183).
If the specified file does not exist and is a valid path to a writable location, the function creates a file and the last-error code is set to zero.
You can use the FILE_APPEND_DATA access specifier to have CreateFile() automatically seek to the end of the file after creating/opening it (otherwise, you have to seek manually using SetFilePointer/Ex()) before you then write new data to the file.
Try this:
void WriteToFile (char *data, wchar_t *filename)
{
HANDLE hFile;
DWORD dwBytesToWrite = strlen(data);
DWORD dwBytesWritten ;
BOOL bErrorFlag = FALSE;
hFile = CreateFileW(filename, // name of the write
FILE_APPEND_DATA, // open for appending
FILE_SHARE_READ, // share for reading only
NULL, // default security
OPEN_ALWAYS, // open existing file or create new file
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
if (hFile == INVALID_HANDLE_VALUE)
{
DisplayError(TEXT("CreateFile"));
wprintf(L"Terminal failure: Unable to create/open file \"%s\" for writing.\n", filename);
return;
}
while (dwBytesToWrite > 0)
{
bErrorFlag = WriteFile(
hFile, // open file handle
data, // start of data to write
dwBytesToWrite, // number of bytes to write
&dwBytesWritten, // number of bytes that were written
NULL); // no overlapped structure
if (!bErrorFlag)
{
DisplayError(TEXT("WriteFile"));
printf("Terminal failure: Unable to write to file.\n");
break;
}
wprintf(L"Wrote %u bytes to \"%s\" successfully.\n", dwBytesWritten, filename);
data += dwBytesWritten;
dwBytesToWrite -= dwBytesWritten;
}
CloseHandle(hFile);
}
Related
When I run this with gcc using code:blocks, it creates the registration.txt on F if it doesn't exist and writes the password and username, but when I use this in my project using Microsoft Visual Studio's compiler it does nothing.
For example if I call this function such as: Write("JohnDoe", "password123"),
in the file registration.txt should appear in a line: JohnDoe, password123.
const char *FILEPATH = "F:\\registration.txt";
int Write(char *username, char *password) {
if (username == NULL || password == NULL) {
return -1;
}
BOOL error = TRUE;
size_t lengthUsername = strlen(username);
size_t lengthPassword = strlen(password);
LPDWORD bytesUsernameWritten = 0;
char comma[2] = ",";
char newLine[3] = "\r\n";
LPDWORD bytesPasswordWritten = 0;
LPDWORD bytesWrittenComma = 0;
//if the file doesn't exist, we create it
HANDLE file = CreateFile((LPCWSTR)FILEPATH, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE) {
if (GetLastError() != ERROR_FILE_EXISTS) {
printf("0x%x", GetLastError());
CloseHandle(file);
return -1;
} //the file exist, we try to create it
file = CreateFile((LPCWSTR)FILEPATH, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE) {
printf("Couldn't open the file. Error : 0x%x", GetLastError());
CloseHandle(file);
return -1;
}
}
//We try to write the username and the password in file, each combination on each line, in this format: username, password
error = WriteFile(file, username, (DWORD)lengthUsername, bytesUsernameWritten, NULL);
if (error == FALSE) {
printf("The username couldn't have been written. Error 0x%x\n", GetLastError());
CloseHandle(file);
return -1;
}
error = WriteFile(file, comma, 1, bytesWrittenComma, NULL);
if (error == FALSE) {
printf("The comma couldn't have been written. Error 0x%x\n", GetLastError());
CloseHandle(file);
return -1;
}
error = WriteFile(file, password, (DWORD)lengthPassword, bytesPasswordWritten, NULL);
if (error == FALSE) {
printf("The password couldn't have been written. Error 0x%x\n", GetLastError());
CloseHandle(file);
return -1;
}
error = WriteFile(file, newLine, 2, bytesPasswordWritten, NULL);
if (error == FALSE) {
printf("The endline couldn't have been written. Error 0x%x\n", GetLastError());
CloseHandle(file);
return -1;
}
CloseHandle(file);
return 0;
}
Your main problem is confusion between using Unicode and ASCII.
All windows API functions that take string parameters have two versions:
One that works with LPCSTR and one that works with LPCWSTR.
You can cast char * to LPCSTR and use the ASCII version CreateFileA, but you can not cast it to LPCWSTR and use the CreateFileW - the Unicode version of CreateFile as it expects strings in UCS-16 encoding where each character takes 2 bytes.
Which version of the function is called depends on a compiler flag. For CodeBlocks on Windows the default is to use ASCII versions, so your function works.
For VS the default is Unicode, so the file path string gets messed up and the file is not created.
Also, you have two other erros:
You are using WriteFile incorrectly.
The 4th parameter is a pointer, where WriteFile stores number of bytes written.
You are passing a NULL pointer, because you set variables such as bytesUsernameWritten to 0. But according to MS documentation, you can only use NULL there if the last parameter, lpOverlapped is not NULL.
What you should do, is declare bytesUsernameWritten to be DWORD and pass its address using & operator.
Otherwise, even if the function creates the file successfully, you will not get the number of bytes that were written.
You are trying to close INVALID_HANDLE_VALUE
This is unnecessary, but fortunately it should not crash you program.
Finally, there is no reason to try to call CreateFile twice.
Just use one call with OPEN_ALWAYS parameter.
This will open an existing file, but if the file does not exist it will create it automatically instead of failing.
I am using the following code to check if a file is being used by another application:
HANDLE fh = CreateFile("D:\\1.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (fh == INVALID_HANDLE_VALUE)
{
MessageBox(NULL, "The file is in use", "Error", 0);
}
If the file is being used by another application, the message box is displayed. However, the message box is also displayed if the file does not exists!
So what should I do to solve this problem, should I also check if the file exists (using another function), or can the parameters of CreateFile() be changed to only return INVALID_HANDLE_VALUE if the file is in use and does exists?
If you wish to find out, which process has a file open, use the Restart Manager. The procedure consists of the following steps (as outlined in Raymond Chen's blog entry How do I find out which process has a file open?):
Create a Restart Manager session (RmStartSession).
Add a file resource to the session (RmRegisterResource).
Ask for a list of all processes affected by that resource (RmGetList).
Close the session (RmEndSession).
Sample code:
#include <Windows.h>
#include <RestartManager.h>
#pragma comment(lib, "Rstrtmgr.lib")
bool IsFileLocked( const wchar_t* PathName ) {
bool isFileLocked = false;
DWORD dwSession = 0x0;
wchar_t szSessionKey[CCH_RM_SESSION_KEY + 1] = { 0 };
if ( RmStartSession( &dwSession, 0x0, szSessionKey ) == ERROR_SUCCESS ) {
if ( RmRegisterResources( dwSession, 1, &PathName,
0, NULL, 0, NULL ) == ERROR_SUCCESS ) {
DWORD dwReason = 0x0;
UINT nProcInfoNeeded = 0;
UINT nProcInfo = 0;
if ( RmGetList( dwSession, &nProcInfoNeeded,
&nProcInfo, NULL, &dwReason ) == ERROR_MORE_DATA ) {
isFileLocked = ( nProcInfoNeeded != 0 );
}
}
RmEndSession( dwSession );
}
return isFileLocked;
}
You need to use GetLastError() to know why CreateFile() failed, eg:
// this is requesting exclusive access to the file, so it will
// fail if the file is already open for any reason. That condition
// is detected by a sharing violation error due to conflicting
// sharing rights...
HANDLE fh = CreateFile("D:\\1.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (fh == INVALID_HANDLE_VALUE)
{
switch (GetLastError())
{
case ERROR_PATH_NOT_FOUND:
case ERROR_FILE_NOT_FOUND:
MessageBox(NULL, "The file does not exist", "Error", 0);
break;
case ERROR_SHARING_VIOLATION:
MessageBox(NULL, "The file is in use", "Error", 0);
break;
//...
default:
MessageBox(NULL, "Error opening the file", "Error", 0);
break;
}
}
else
{
// the file exists and was not in use.
// don't forget to close the handle...
CloseHandle(fh);
}
i have tried this code it works when i read a sector from an USB flash drive but it does'nt work with any partiton on hard drive , so i want to know if it's the same thing when you try to read from usb or from hard drive
int ReadSector(int numSector,BYTE* buf){
int retCode = 0;
BYTE sector[512];
DWORD bytesRead;
HANDLE device = NULL;
device = CreateFile("\\\\.\\H:", // Drive to open
GENERIC_READ, // Access mode
FILE_SHARE_READ, // Share Mode
NULL, // Security Descriptor
OPEN_EXISTING, // How to create
0, // File attributes
NULL); // Handle to template
if(device != NULL)
{
SetFilePointer (device, numSector*512, NULL, FILE_BEGIN) ;
if (!ReadFile(device, sector, 512, &bytesRead, NULL))
{
printf("Error in reading disk\n");
}
else
{
// Copy boot sector into buffer and set retCode
memcpy(buf,sector, 512);
retCode=1;
}
CloseHandle(device);
// Close the handle
}
return retCode;}
The problem is the sharing mode. You have specified FILE_SHARE_READ which means that nobody else is allowed to write to the device, but the partition is already mounted read/write so it isn't possible to give you that sharing mode. If you use FILE_SHARE_READ|FILE_SHARE_WRITE it will work. (Well, provided the disk sector size is 512 bytes, and provided the process is running with administrator privilege.)
You're also checking for failure incorrectly; CreateFile returns INVALID_HANDLE_VALUE on failure rather than NULL.
I tested this code successfully:
#include <windows.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
int retCode = 0;
BYTE sector[512];
DWORD bytesRead;
HANDLE device = NULL;
int numSector = 5;
device = CreateFile(L"\\\\.\\C:", // Drive to open
GENERIC_READ, // Access mode
FILE_SHARE_READ|FILE_SHARE_WRITE, // Share Mode
NULL, // Security Descriptor
OPEN_EXISTING, // How to create
0, // File attributes
NULL); // Handle to template
if(device == INVALID_HANDLE_VALUE)
{
printf("CreateFile: %u\n", GetLastError());
return 1;
}
SetFilePointer (device, numSector*512, NULL, FILE_BEGIN) ;
if (!ReadFile(device, sector, 512, &bytesRead, NULL))
{
printf("ReadFile: %u\n", GetLastError());
}
else
{
printf("Success!\n");
}
return 0;
}
I am a student so I apologize up front for not using the correct forum protocols. I am new to C, and really new to Win32 API. My assignment is to write a small C program that copies the contents of an existing file to a new file, using only Win32 I/O system calls: CreateFile(), ReadFile(), WriteFile(), etc... File names are to be specified on the command line. Right now I'm just trying to get the basics functions in place, I will then focus on error handling. This code compiles, creates a new file, but the data does not get copied to it. Any advice? thanks for taking a look!
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main(int argc, char *argv[])
{
char buff[4096];
DWORD dwBytesRead, dwBytesWritten;
DWORD dwBytesToWrite = (DWORD)strlen(buff);
//open source file and read it
HANDLE source;
// Create a handle for the source file
source=CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// Check for errors
if ( source == INVALID_HANDLE_VALUE ){
printf("Error, source file not opened.");
exit(EXIT_FAILURE);
}
else printf("The source file is %s\n", argv[1]);
//create a new file
HANDLE target;
target = CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if ( target == INVALID_HANDLE_VALUE ){
printf("Error, target file not created.");
exit(EXIT_FAILURE);
}
else printf("The source file is %s\n", argv[2]);
//copy contents
ReadFile(source, buff, 4096, &dwBytesRead, NULL);
WriteFile(target, buff, dwBytesToWrite, &dwBytesWritten, NULL);
//copy complete
CloseHandle(source);
CloseHandle(target);
return 0;
}
As mentioned in comments, your code has a few mistakes in it. Try something more like this instead:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main(int argc, char *argv[])
{
char buff[4096];
DWORD dwBytesRead, dwBytesWritten;
// Open the source file
HANDLE source = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// Check for error
if (source == INVALID_HANDLE_VALUE) {
printf("Source file not opened. Error %u", GetLastError());
return EXIT_FAILURE;
}
printf("The source file is %s\n", argv[1]);
// Create a new file
HANDLE target = CreateFileA(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
// Check for error
if (target == INVALID_HANDLE_VALUE) {
printf("Target file not created. Error %u", GetLastError());
CloseHandle(source);
return EXIT_FAILURE;
}
printf("The target file is %s\n", argv[2]);
// Copy contents
bool ok = true;
do {
// Read file, check for error
if (!ReadFile(source, buff, sizeof(buff), &dwBytesRead, NULL)) {
printf("Source file not read from. Error %u", GetLastError());
ok = false;
break;
}
// Check for EOF reached
if (dwBytesRead == 0) {
break;
}
// Write file, check for error
if (!WriteFile(target, buff, dwBytesRead, &dwBytesWritten, NULL)) {
printf("Target file not written to. Error %u", GetLastError());
ok = false;
break;
}
}
while (true);
// Copy complete
CloseHandle(source);
CloseHandle(target);
// Check for error
if (!ok) {
DeleteFileA(argv[2]);
return EXIT_FAILURE;
}
// all OK
return 0;
}
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