Why can't a single thread open the same file tqize using CreateFile and an exclusive file lock to the process? The example below will fail on the second attempt to open the file by the same thread with a ERROR_SHARING_VIOLATION exception:
ERROR_SHARING_VIOLATION 32 (0x20) The process cannot access the file
because it is being used by another process.
Emphasis on the word "process" above; it is the same process (and even the same thread) that tries to open the same file twize.
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hOutputFile1 = CreateFile(
// File name
L"test.dat",
// Requested access to the file
GENERIC_READ | GENERIC_WRITE,
// Share mode. 0 equals exclusive lock for the process
0,
// Pointer to a security attribute structure
NULL,
// Action to take on file
OPEN_ALWAYS,
// File attributes and flags
FILE_ATTRIBUTE_NORMAL,
// Template file
NULL
);
if (hOutputFile1 == INVALID_HANDLE_VALUE)
{
// Error
DWORD lastError = GetLastError();
return (int)lastError;
}
// opening the same file for the second time will return a ERROR_SHARING_VIOLATION exception
HANDLE hOutputFile2 = CreateFile(
// File name
L"test.dat",
// Requested access to the file
GENERIC_READ | GENERIC_WRITE,
// Share mode. 0 equals exclusive lock by the process
0,
// Pointer to a security attribute structure
NULL,
// Action to take on file
OPEN_ALWAYS,
// File attributes and flags
FILE_ATTRIBUTE_NORMAL,
// Template file
NULL
);
if (hOutputFile2 == INVALID_HANDLE_VALUE)
{
// Error
DWORD lastError = GetLastError();
return (int)lastError;
}
return 0;
}
The error message text is a bit misleading, but the fact that the two calls to CreateFile are made from the same thread in the same process doesn't change anything. Once the first call to CreateFile has been made, then subsequent calls to CreateFile, irrespective of where they originate, must obey the sharing rules.
I guess the error message text tries to capture the most common source of a sharing violation. Namely two processes competing for the same file. But the simple fact is that once you have opened a file with exclusive sharing, then no other attempts to open the file can succeed.
That old message is misleading. It doesn't matter what process or thread is opening a file. File sharing is handle-based.
From MSDN:
dwShareMode [in]:
If this parameter is zero and CreateFile succeeds, the file or device
cannot be shared and cannot be opened again until the handle to the
file or device is closed.
This is just talking about opening the file or device and the error message is something general and confusing.
Instead of trying to re-open the file, you should hold the handle and do your job.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
this program is supposed to write to shared memory using win32 API. it is a program given as it is in text book but when i try to execute it it fails. it crashes as i click on execute
the program is supposed to write a string to shared memory
#include<windows.h>
#include<stdio.h>
int main(int argc, char *argv[])
{
HANDLE hFile, hMapFile;
LPVOID lpMapAddress;
//mapping of memory
hFile=CreateFile("temp.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0,0, TEXT("SharedObject"));
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
//writing into shared memory
sprintf((char*)lpMapAddress,"shared memory writing");
UnmapViewOfFile(lpMapAddress);
CloseHandle(hFile);
CloseHandle(hMapFile);
}
the reason it is crashing is because the file must be created with GENERIC_READ and GENERIC_WRITE access rights since in the CreateFileMapping() function your code specifies PAGE_READWRITE as its third argument(flProtect). This is from MSDN documentation of CreateFileMapping:
The file must be opened with access rights that are compatible with
the protection flags that the flProtect parameter specifies
PAGE_READWRITE=>Gives read/write access to a specific region of pages.
The file that hFile specifies must be created with the GENERIC_READ
and GENERIC_WRITE access rights.
so change
hFile=CreateFile("temp.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
to
hFile=CreateFile("temp.txt",GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
One more thing you can not map a file with a size of zero. Here is from MSDN documentation of CreateFileMapping() function:
If this parameter(dwMaximumSizeLow) and dwMaximumSizeHigh are 0 (zero), the maximum size
of the file mapping object is equal to the current size of the file
that hFile identifies.
An attempt to map a file with a length of 0 (zero) fails with an error
code of ERROR_FILE_INVALID. Applications should test for files with a
length of 0 (zero) and reject those files.
If an application specifies a size for the file mapping object that is
larger than the size of the actual named file on disk, the file on
disk is increased to match the specified size of the file mapping
object.
so in your case since the file you are trying to map has initially a size of 0, the CreateFileMapping() function will fail unless you specify the size of a file mapping object in the dwMaximumSizeLow/dwMaximumSizeHigh parameters of CreateFileMapping(). You could do something like this...
HANDLE hFile, hMapFile;
LPVOID lpMapAddress;
//mapping of memory
hFile=CreateFile(L"temp.txt",GENERIC_WRITE | GENERIC_READ,0,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile!=INVALID_HANDLE_VALUE)
{
char* str="shared data to be written";//the data you want to write to the file
int strLen=::strlen(str);//get the string length of the data you want to write
hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0,strLen, TEXT("SharedObject")); //here you also specify the size of the mapping object to be equal to the size of data you want to write
if (hMapFile != NULL && hMapFile != INVALID_HANDLE_VALUE)
{
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
//writing into shared memory
if(lpMapAddress!=NULL)
sprintf((char*)lpMapAddress,"%s","shared file write");
else
printf("error");//error message MapViewOfFile() failed
UnmapViewOfFile(lpMapAddress);
CloseHandle(hMapFile);
}
else
printf("error");//error message CreateFileMapping() failed
CloseHandle(hFile);
}
else
printf("error");//error message CreateFile() failed
Here's my code in which I've got on an infinite loop (to my knowledge)
while(true) {
DWORD TitleID = XamGetCurrentTitleId();
std::ostringstream titleMessageSS;
titleMessageSS << "Here's the current title we're on : " << TitleID << "\n\n";
std::string titleMessage = titleMessageSS.str(); // get the string from the stream
DWORD dwBytesToWrite = (DWORD)titleMessage.size();
DWORD dwBytesWritten = 0;
BOOL bErrorFlag = FALSE;
HANDLE logFile = CreateFile( "Hdd:\\LOGFile.txt", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
bErrorFlag = WriteFile(logFile, titleMessage.c_str(), dwBytesToWrite, &dwBytesWritten, NULL);
CloseHandle(logFile);
Sleep(30000);
}
return NULL;
Does anyone see a reason as to why this only writes just once? I've waited over 5 minutes to see if it does anything in the end to no avail.
The Flag CREATE_NEW in CreateFile prevents the update of the file because CreateFile fail with ERROR_FILE_EXISTS. Use OPEN_ALWAYS instead.
Also it will always truncate. Replace GENERIC_WRITE with FILE_APPEND_DATA if you want to add a new line at the end of your logfile.
The whole CreateFile line should be:
HANDLE logFile = CreateFile( "Hdd:\\LOGFile.txt", FILE_APPEND_DATA , 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
Read CreateFile documentation carefully, it worth it, because it has a central role in the windows IO universe:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
look also add:
https://stackoverflow.com/a/9891875/1922748
As Martin James mentioned, from MSDN:
CREATE_NEW
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.
So it seems that the handle is invalid after the first call, and hence WriteFile() fails.
I'm trying to capture the output (stdout/stderr) of a child process, in particular so that if it exits abnormally, I may get some useful error output.
I create an output file, and set this (and STARTF_USESTDHANDLES) on the STARTUPINFO structure that I subsequently pass to CreateProcess().
Now I get the following behaviour, for different actions in my child process:
(1) No flush
printf("Hello world!\n");
Result: after child terminated from parent (via TerminateProcess): no output in file.
(2) Use fflsuh() after each printf call
printf("Hello world!\n");
fflush(stdout);
Result: after child terminated from parent (via TerminateProcess): "Hello World!" (as expected)
(3) Abnormal termination
printf("Hello world!\n");
int *p = NULL; *p = 5; // Access violation
Result: no output in file.
I could try adding fflush() calls after every printf() (ick!), or trying to get an alternative to TerminateProcess() that ensures the file handles are closed first. But that's not going to help with an abnormal exit - which is the main point of wanting to capture stdout/stderr in the first place.
The other thing I've tried is to create the file with the FILE_FLAG_NO_BUFFERING flag set, but my attempts to do that aren't working - CreateFile() fails (with error 87 "The parameter is incorrect"). Any idea what I'm doing wrong? Or if there's a different way to achieve this?
I've tried 3 different options for the CreateFile() dwFlagsAndAttributes value, with the following results.
FILE_ATTRIBUTE_NORMAL - file is created fine, but I get the behaviour described above (so I miss data on abnormal termination).
FILE_FLAG_NO_BUFFERING or FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING - CreateFile() fails, with GetLastError = 87 and FormatMessage returning "The parameter is incorrect". Am I just using the flags wrongly here? Or could it be that this option isn't supported on my filesystem?
Here's the code I'm using to create my output file, and the child process. I've flagged below where I've tried a few different options on for the CreateFile() dwFlagsAndAttributes value.
SECURITY_ATTRIBUTES security = {sizeof(SECURITY_ATTRIBUTES), NULL, true}; // Allow the handle to be inherited by child process
HANDLE myfile = CreateFile(myfilename,
FILE_APPEND_DATA,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
&security,
OPEN_ALWAYS,
FILE_FLAG_NO_BUFFERING, // I've tried 3 different things here - see above
NULL);
STARTUPINFO startupInfo = {0};
startupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
startupInfo.wShowWindow = SW_HIDE;
startupInfo.hStdError = myfile;
startupInfo.hStdOutput = myfile;
PROCESS_INFORMATION processInfo = {0};
CreateProcess(NULL,
executable,
NULL, NULL,
true, // Allow stdout/err file handle to be inherited
0, NULL, NULL,
&startupInfo,
&processInfo))
CloseHandle(myfile); // The child owns this now
Thanks for your help!
Thanks to Igor & Harry for suggesting this approach in the comments.
Don't use the FILE_FLAG_NO_BUFFERING flag. This is only for when you need direct hardware control.
Calling setvbuf(stdout, NULL, _IONBF, 0) at the start of the child process is roughly equivalent to calling fflush after each printf call. Also call setvbuf for stderr.
I am trying to open a file for writing and reading simultaneously, in windows.
I have one program which writes (every one second) to the file and one that reads from it. In unix it works prefectly but it doesn't work in windows (I can't open an already opened file).
I open the file with fopen().
How can I solve this problem?
EDIT2:
check out _fsopen it uses FILE *, and set the share flag accordingly.
EDIT:
First of all, some code: this is how I used to open the file
FILE* f = NULL;
int res = fopen_s(&f, "c:\\temp\\File1.txt", "w");
if (res != 0) return;
while (true) {
Sleep(1000);
fprintf_s(f , "Some data");
}
fclose(f);
The read was in other applicaiton, but it did fscanf instead.
The fixed code:
char d[] = "data";
HANDLE h = CreateFile("c:\\temp\\f.txt", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CREATE_ALWAYS, /*FILE_ATTRIBUTE_NORMAL*/ FILE_FLAG_WRITE_THROUGH, NULL);
if (h == INVALID_HANDLE_VALUE) return 0;
DWORD bytesW;
while(true) {
Sleep(100);
WriteFile(h, d, strlen(d), &bytesW, NULL);
}
CloseHandle(h);
return 0;
Both Windows and Linux have a default way of opening a file, which fopen uses by default.
In Windows, that means blocking (only one process can open a file at a time).
In Linux, it means non-blocking.
fopen is a high-level API. To choose yourself the blocking policy on the file, for Windows you should use OpenFile from WinAPI. In particular, have a look at the OF_SHARE_* flags.
The old contents are not being wiped out.
Instead the data is being written over, so I still see old contents.
What did I not do?
hFile = CreateFile(fname, // open testfile.txt
GENERIC_WRITE, // open for reading
0, // do not share
NULL, // default security
OPEN_ALWAYS, //
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attribute template
dwBytesToWrite = buff.GetLength();
WriteFile(hFile, buff.GetBuffer(100), dwBytesToWrite, &dwBytesWritten, NULL);
You have specified the wrong value for dwCreationDisposition. You need to specify CREATE_ALWAYS.
Creates a new file, always.
If the specified file exists and is writable, the function overwrites the file, the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS (183).
If the specified file does not exist and is a valid path, a new file is created, the function succeeds, and the last-error code is set to zero.
In dwCreationDisposition you need to specify CREATE_ALWAYS.
You need dwCreationDisposition = TRUNCATE_EXISTING. This however:
Opens a file and truncates it so that its size is zero bytes, only if
it exists. If the specified file does not exist, the function fails
and the last-error code is set to ERROR_FILE_NOT_FOUND (2). The
calling process must open the file with the GENERIC_WRITE bit set as
part of the dwDesiredAccess parameter.
So I would try and open it first with TRUNCATE_EXISTING. If it fails with ERROR_FILE_NOT_FOUND, then open it with CREATE_NEW.
hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, TRUNCATE_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if ((hFile == NULL) && (GetLastError() == ERROR_FILE_NOT_FOUND))
{
hFile = CreateFile(fname, GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
}
EDIT: This is not the best way to do this. CREATE_ALWAYS is the dwCreationDisposition you want to use. See David Heffernan's answer.