HANDLE hFile = CreateFile(LPCTSTR("filename"), // name of the write
GENERIC_READ | GENERIC_WRITE, // open for writing and reading
0, // do not share
NULL, // default security
OPEN_ALWAYS, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
printf("Success.\n");
}
CloseHandle(hFile);
The operation succeeds but i can't find 'filename' on the disk.
Does CreateFile() actually creates file on the disk?
I tried a minimal program containing only your code and ... the file was correctly created in current directory ! But only if the program is compiled in ANSI mode, because the LPCTSTR only converts the pointer to be a LPCTSTR but does not convert from ANSI to UNICODE. Only the _T macro does that.
You should use GetCurrentDirectory to control where you try to write the file, and use TCHAR to be UNICODE compatible :
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int main() {
LPTSTR dir;
DWORD cr = ::GetCurrentDirectory(0, NULL);
cr += 1;
dir = (LPTSTR) malloc(cr * sizeof(TCHAR));
cr = ::GetCurrentDirectory(cr, dir);
/* ::MessageBox(NULL, dir, _T("Current dir"), MB_OK); */
_tprintf(_T("Current dir : %s\n"), dir); // note the _tprintf and _T macro
free(dir);
HANDLE hFile = CreateFile(_T("filename"), // name of the write - _T
GENERIC_READ | GENERIC_WRITE, // open for writing and reading
0, // do not share
NULL, // default security
OPEN_ALWAYS, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL);
if(hFile != INVALID_HANDLE_VALUE)
{
printf("Success.\n");
}
CloseHandle(hFile);
return 0;
}
If I compile in UNICODE mode without the _T macro around "filename", the program does create a file, but its name is simply garbage
There is not much information, but here is a blind guess : your file is redirected to the VirtualStore.
Some locations, like "Program Files", are protected on the latest versions of Windows.
If your program is in a protected location and is not run as administrator, the read/write operations will be redirected to C:\Users\MYNAME\AppData\Local\VirtualStore\MYFOLDER.
Running your program as admin should correct this wherever it is located.
Related
I would like to capture printf output in a Win32 application (no console) without creating a temporary file or using ConsoleAlloc(). Is this possible? I've got a library that emits read/write status messages to stdout and displays them in a console. I want to create a Win32 app that uses this library and parses the data in real-time to update the app. I can't change the code in the library.
I can get it working with a temporary file, but this is slow and there is lots of disk IO activity:
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
freopen_s((FILE**)stdout, "tempfile.out", "w", stdout);
printf("Redirected this to a file.");
fclose(stdout);
}
And I can get it working with fprintf or fwrite as below, but regular print doesn't work. I think this is because the destination file descriptor is invalid in _dup2. It looks like printf is running as it returns the number of bytes in the string but it is pointing to stdout which is an invalid fd. If I step through the code DstHandle is -2, which is invalid. It still doesn't work if I replace the value with 1 (what _fileno(stdout) is supposed to be).
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
HANDLE hRead, hWrite;
CreatePipe(&hRead, &hWrite, NULL, 0);
//SetStdHandle(STD_OUTPUT_HANDLE, hWrite);
int fd = _open_osfhandle((intptr_t)hWrite, O_WRONLY);
FILE* f = _fdopen(fd, "w"); // Associate a stream f with a fd
const char* wbuffer = " --- Hello World! --- \n";
fwrite(wbuffer, 1, strlen(wbuffer), f);
printf("Testing printf\n");
fprintf(f, "Testing fprintf"); // This works
//*stdout = *fd;
int DstHandle = (_fileno)(stdout); // Get the stdout (destination) fd. Result undefined if stream doesn't specify an open file.
int res = _dup2(fd, DstHandle);
setvbuf(stdout, NULL, _IONBF, 0); // Unbuffered, no need for fflush()
//freopen_s(&f, "CONOUT$", "w", stdout); // Crash... there is no console here.
printf("Testing printf again\n");
//fflush(f);
fclose(f); // Also calls _close()
DWORD bytesRead = 0;
char buffer[BUFFSIZE + 1] = { 0 };
ReadFile(hRead, buffer, BUFFSIZE, &bytesRead, NULL);
return 0;
}
My problem seems to be around the file stream for stdout not being able to reassigned a new value or refreshed. For example, this works and requires freopen to reassign the stream, but has a console:
AllocConsole();
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
//HANDLE hConOut = CreateFile(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//SetStdHandle(STD_OUTPUT_HANDLE, hConOut);
printf("This works\n");
For reference, there is a similar question here:
All the answers use ConsoleAlloc(): How to view printf output in a Win32 application on Visual Studio 2010?
Get a C Runtime fd from a handle: Is there a Windows equivalent to fdopen for HANDLEs?
I am trying to create a file with GENERIC_WRITE permission with a path entered by the user.
To get user input, I am using fwgets function.
VOID DoCreateFile() {
SIZE_T sAlloc = sizeof(WCHAR) * (MAX_WPATH + 1); // allocation size
// allocating space and checking if actually allocated
LPWSTR lpPath = (LPWSTR)malloc(sAlloc);
LPWSTR lpContent = (LPWSTR)malloc(sAlloc);
if (lpPath == NULL || lpContent == NULL) {
PrintLastError(L"malloc()", TRUE);
}
wprintf(L"Enter path of file: ");
fgetws(lpPath, sAlloc, stdin); // read the contents of stdin with space
wprintf(L"Enter content (max 256 chars): ");
fgetws(lpContent, sAlloc, stdin);
/*
Documentation: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
*/
HANDLE hFile = CreateFileW(lpPath, // path of file
GENERIC_WRITE, // creating file with write permission
FILE_SHARE_READ, // allow other process to open file for reading
NULL, // disallow handle inheritance
CREATE_ALWAYS, // overwrite file if exists, otherwise create a new one
FILE_ATTRIBUTE_NORMAL, // do not set any file attributes
NULL // not using any file template
);
if (hFile == INVALID_HANDLE_VALUE) {
PrintLastError(L"CreateFileW()", TRUE);
}
CloseHandle(hFile);
}
Error message printed by PrintLastError function: CreateFileW() Failed! The filename, directory name, or volume label syntax is incorrect.
The input I have entered on the console is
Enter path of file: c:\file.txt
Enter content (max 256 chars): s
I have also tried file path \\.\C:\file.txt.
FYI, when I replace lpPath with a wide string literal L"C:\\Files.txt", the function succeeds.
First of all, I'm not a Windows programmer (not even a Windows user), I use cross-compiler on Linux to build also for Win32 and Win64. After digging the Net (and even asking a question here) I've managed to put a code fragment together which can open a windows console, and use it for stdin/stdout/stderr. It works well with Win32, but the program crashes on Win64. I guess the problem is the different long integer data type size, gcc even warns about this. However since I don't know the exact purpose and size of some windows API types, so I can't figure out what I should change. Surely, the best would be some win32/win64 independent solution. I also tried to use the "HANDLE" type for lStdHandle but then it even does not compile. Can anyone help about this?
int hConHandle;
long lStdHandle;
//HANDLE lStdHandle;
CONSOLE_SCREEN_BUFFER_INFO coninfo;
FILE *fp;
FreeConsole(); // be sure to release possible already allocated console
if (!AllocConsole()) {
ERROR_WINDOW("Cannot allocate windows console!");
return;
}
SetConsoleTitle("My Nice Console");
// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.Y = 1024;
//coninfo.dwSize.X = 100;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
// redirect unbuffered STDOUT to the console
lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stdout = *fp;
setvbuf( stdout, NULL, _IONBF, 0 );
// redirect unbuffered STDIN to the console
lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "r" );
*stdin = *fp;
setvbuf( stdin, NULL, _IONBF, 0 );
// redirect unbuffered STDERR to the console
lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
fp = _fdopen( hConHandle, "w" );
*stderr = *fp;
setvbuf( stderr, NULL, _IONBF, 0 );
// Set Con Attributes
//SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
I don't know exactly what the problem is. I'm not set up to build using MinGW on Linux. It could be related to an invalid file descriptor if you aren't building a console application. In this case the CRT initializes the file-descriptor to handle mapping as an invalid handle value, and also the standard FILE streams will be initialized to a -1 fileno. But I don't see a problem with that in your code.
However, your *stdin = *fp hack is not portable C. It works in older versions of MSVC, and probably also with msvcrt.dll (somewhat dubiously used by MinGW for lack of a better choice). However, it doesn't work with the new Universal CRT. A FILE in the new CRT is defined as follows:
typedef struct _iobuf
{
void* _Placeholder;
} FILE;
So assigning to *stdin just overwrites this _Placeholder pointer. The internal structure is actually as follows:
struct __crt_stdio_stream_data
{
union
{
FILE _public_file;
char* _ptr;
};
char* _base;
int _cnt;
long _flags;
long _file;
int _charbuf;
int _bufsiz;
char* _tmpfname;
CRITICAL_SECTION _lock;
};
So all you're really overwriting is its buffer _ptr.
The way to portably re-open a standard stream is via freopen. So what I do, which works but maybe someone else has a better solution, is to freopen the NUL device, which resets the stream to a valid file descriptor if it's a non-console application. Then use _dup2 to redirect the underlying file descriptor. For example:
#include <io.h>
#include <stdio.h>
#include <fcntl.h>
#include <Windows.h>
int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPWSTR lpCmdLine, int nCmdShow)
//int wmain(int argc, wchar_t **argv)
{
int fdStd;
HANDLE hStd;
CONSOLE_SCREEN_BUFFER_INFO coninfo;
printf("Goodbye, World!\n");
/* ensure references to current console are flushed and closed
* before freeing the console. To get things set up in case we're
* not a console application, first re-open the std streams to
* NUL with no buffering, and close invalid file descriptors
* 0, 1, and 2. The std streams will be redirected to the console
* once it's created. */
if (_get_osfhandle(0) < 0)
_close(0);
freopen("//./NUL", "r", stdin);
setvbuf(stdin, NULL, _IONBF, 0);
if (_get_osfhandle(1) < 0)
_close(1);
freopen("//./NUL", "w", stdout);
setvbuf(stdout, NULL, _IONBF, 0);
if (_get_osfhandle(2) < 0)
_close(2);
freopen("//./NUL", "w", stderr);
setvbuf(stderr, NULL, _IONBF, 0);
FreeConsole();
if (!AllocConsole()) {
//ERROR_WINDOW("Cannot allocate windows console!");
return 1;
}
SetConsoleTitle("My Nice Console");
// set the screen buffer to be big enough to let us scroll text
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
coninfo.dwSize.Y = 1024;
//coninfo.dwSize.X = 100;
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
// redirect unbuffered STDIN to the console
hStd = GetStdHandle(STD_INPUT_HANDLE);
fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT);
_dup2(fdStd, fileno(stdin));
SetStdHandle(STD_INPUT_HANDLE, (HANDLE)_get_osfhandle(fileno(stdin)));
_close(fdStd);
// redirect unbuffered STDOUT to the console
hStd = GetStdHandle(STD_OUTPUT_HANDLE);
fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT);
_dup2(fdStd, fileno(stdout));
SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(fileno(stdout)));
_close(fdStd);
// redirect unbuffered STDERR to the console
hStd = GetStdHandle(STD_ERROR_HANDLE);
fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT);
_dup2(fdStd, fileno(stderr));
SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(fileno(stderr)));
_close(fdStd);
// Set Con Attributes
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
FOREGROUND_GREEN | FOREGROUND_INTENSITY);
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE),
ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),
ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
printf("Hello, World!\n");
Sleep(10000);
return 0;
}
It is a handle so you should use the HANDLE type. Cast to INT_PTR (or SIZE_T if your SDK is really outdated) when you call _open_osfhandle, using long can truncate the value!
UPDATE Question: So I have been able to create the process and get the programs to compiled. However, I run into a new problem. When I try to pipe the Source program into the Filter program. It doesn't seem to feed in any input for the sink program. There are no error message. I have also test all of my standalone program using the pipe operator in Windows' cmd.
I'm trying to do this small project to learn about anonymous pipe and create process. I created 3 small standalone programs called Source, Filter, and Sink. These 3 are already compiled and run fine. Here's the descriptions for the 3 standalone programs.
Source: obtains a source text-file filename from its commandline, opens the file,and reads and copies the file contents one character at a time directly to standard output(stdout). When the file has been copied, Source terminates (closing all of its open file handles).
Filter program does not utilize any filename commandline parameters. Instead, Filter reads a text file from standard input (stdin) and writes to standard output (stdout) a copy of the input with all upper-case letters converted to lower-case. Filter specifically must be designed to read one character, convert it, output it, and then loop until the incoming data is finished.
Sink program obtains a destination text-file filename from its commandline, opens the file for writing, and then reads characters one at a time from the standard input file (stdin) and writes each incoming character directly to the destination sink file.
Next I'm driving a main driver program separately that creates 2 pipes and spawn 3 independent child with inputs and outputs configured to carry out the indicated concurrent execution and dataflow. Something like this:
srcfile -> Source -> pipe1 -> Filter -> Pipe2 -> Sink -> destfile
The driver program will requires 2 command line parameters:
C:\> Driver.exe srcfile destfile
where srcfile is an existing data text-file, and destfile is the filename of a new destination file that is to be created by the Sink application.
Here's my code for the driver program. It's not finished yet. But I encountered a hiccup when trying to create a process for the Source program.
UPDATED CODE:
#include <windows.h>
#include <WinBase.h>
#include <stdio.h>
#define DELAY_A_WHILE() {volatile long j; for(j = 1; j< 10000; j++) ; }
int main(int argc, char *argv[])
{
HANDLE hPipeRead, hPipeWrite, hPipeRead2, hPipeWrite2;
STARTUPINFO StartupInfoSource;
STARTUPINFO StartupInfoFilter;
STARTUPINFO StartupInfoSink;
PROCESS_INFORMATION ProcInfoSource;
PROCESS_INFORMATION ProcInfoFilter;
PROCESS_INFORMATION ProcInfoSink;
SECURITY_ATTRIBUTES PipeAttributes;
SECURITY_ATTRIBUTES PipeAttributes2;
char cmdline[200];
PipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
PipeAttributes.lpSecurityDescriptor = NULL; //ignore
PipeAttributes.bInheritHandle = TRUE; //child can inherit
//Create first pipe
if (!CreatePipe(&hPipeRead, &hPipeWrite, &PipeAttributes, 0)) {
fprintf(stderr, "Error creating pipe: %d\n", GetLastError());
exit(1);
}
sprintf_s(cmdline, 200, "Source.exe %s", argv[1]);
printf("Create process: %s\n", cmdline);
GetStartupInfo(&StartupInfoSource);
StartupInfoSource.dwFlags = StartupInfoSource.dwFlags | STARTF_USESTDHANDLES;
//Mapping
StartupInfoSource.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
StartupInfoSource.hStdOutput = hPipeWrite;
StartupInfoSource.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if (!CreateProcess(
NULL, cmdline, NULL, NULL,
TRUE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfoSource,
&ProcInfoSource))
{
fprintf(stderr, "Error creating child process: %d",GetLastError());
exit(1);
}
CloseHandle(hPipeWrite);
CloseHandle(ProcInfoSource.hProcess);
CloseHandle(ProcInfoSource.hThread);
PipeAttributes2.nLength = sizeof(SECURITY_ATTRIBUTES);
PipeAttributes2.lpSecurityDescriptor = NULL; //ignore
PipeAttributes2.bInheritHandle = TRUE; //child can inherit
//Create Second Pipe
if (!CreatePipe(&hPipeRead2, &hPipeWrite2, &PipeAttributes2, 0)) {
fprintf(stderr, "Error creating pipe: %d\n", GetLastError());
exit(1);
}
GetStartupInfo(&StartupInfoFilter);
StartupInfoFilter.dwFlags = StartupInfoFilter.dwFlags | STARTF_USESTDHANDLES;
//Mapping
StartupInfoFilter.hStdInput = hPipeRead;
StartupInfoFilter.hStdOutput = hPipeWrite2;
StartupInfoFilter.hStdError = GetStdHandle(STD_ERROR_HANDLE);
sprintf_s(cmdline, 200, "Filter.exe");
printf("Create process: %s\n", cmdline);
//Filter
GetStartupInfo(&StartupInfoFilter);
if (!CreateProcess(
NULL, cmdline, NULL, NULL,
TRUE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfoFilter,
&ProcInfoFilter))
{
fprintf(stderr, "Error creating child process: %d", GetLastError());
exit(1);
}
// int exitStatus;
// GetExitCodeProcess(ProcInfoFilter.hProcess, &exitStatus);
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite2);
CloseHandle(ProcInfoFilter.hProcess);
CloseHandle(ProcInfoFilter.hThread);
GetStartupInfo(&StartupInfoSink);
StartupInfoSink.dwFlags = StartupInfoSink.dwFlags | STARTF_USESTDHANDLES;
//Mapping
StartupInfoSink.hStdInput = hPipeRead2;
StartupInfoSink.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
StartupInfoSink.hStdError = GetStdHandle(STD_ERROR_HANDLE);
sprintf_s(cmdline, 200, "Sink.exe %s", argv[2]);
printf("Create process: %s\n", cmdline);
GetStartupInfo(&StartupInfoSink);
if (!CreateProcess(
NULL, cmdline, NULL, NULL,
TRUE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfoSink,
&ProcInfoSink))
{
fprintf(stderr, "Error creating child process: %d", GetLastError());
exit(1);
}
CloseHandle(hPipeRead2);
CloseHandle(ProcInfoSink.hProcess);
CloseHandle(ProcInfoSink.hThread);
return 0;
}
The program compiles fine. However, when it try to creates the Process, it always fails and exits. The cmdline value when it parse in is "Source.exe test.txt", which is exactly what I used to execute my standalone Source program. Can someone explain why my CreateProcess fail? Is it because I parse in the wrong parameter?
The only problem I can see here is the possibility that Source.exe app is not located in the same directory where Driver.exe is located. I tried your code and it was the only case when CreateProcess failed.
I have figure out why. My project's property was using the Unicode character set. When I switch to Multi-byte character set it works fine.
I'm trying to log the actions made by a service I wrote using the Windows-API & C-language, so I made a log file system.
The problem is that at each CreateFile call, the file is overridden instead of just opening it and write at the end of the file.
Here's the code of my WriteInLogfile function :
void WriteInLogFile(LPCTSTR log_string)
{
HANDLE hFile;
DWORD dBytesWritten;
if ((hFile = CreateFile(LOG_FILE_PATH, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_FILE_NOT_FOUND))
{
if ((hFile = CreateFile(LOG_FILE_PATH, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
{
if (!WriteFile(hFile, log_string, strlen(log_string), &dBytesWritten, NULL))
aff_error("WriteInLogFile");
CloseHandle(hFile);
}
}
else
{
if (!WriteFile(hFile, log_string, strlen(log_string), &dBytesWritten, NULL))
aff_error("WriteInLogFile");
CloseHandle(hFile);
}
}
Do someone know where the issue comes from ?
Thanks ;)
Even though you're opening the existing file you're not specifying that you want to append to it. Hence it opens as a generic write and you end up overwriting the contents. You need to pass the FILE_APPEND_DATA flag to the CreateFile method. This is best done by using the FILE_GENERIC_WRITE flag which includes FILE_APPEND_DATA
if ((hFile = CreateFile(LOG_FILE_PATH, FILE_GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_FILE_NOT_FOUND))
{
When you open a file, the pointer will always be positioned to the beginning of the file. To append, you need to explicitly seek to the end (SetFilePointer(hFile, 0, 0, FILE_END);).
Although it may not be causing your actual problem, I'd replace your current logic trying to use CreateFile with OPEN_EXSTING, then with CREATE_NEW if the first attempt fails. Instead, just pass the OPEN_ALWAYS flag, which pretty much automates that logic -- open an existing file if it exists, and create a new one if it doesn't.
You need to set the file pointer to the end of the file before writing with SetFilePointer. See the MSDN example.
I couldn't see anything obvious about opening for Append in the CreateFile documentation, but you could use the SetFilePointer
function to seek to the end of the file before writing.