Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronous IO with overlapped moves file pointer. #1309

Merged

Conversation

macdice
Copy link
Contributor

@macdice macdice commented Sep 27, 2022

The documentation gives the impression that the file pointer of a synchronous file handle is updated by WriteFile/ReadFile only if lpOverlapped is NULL. In fact it is also updated if it is not NULL. That is surprising, because (1) in this case the operation doesn't use the file position itself, it uses the offset from the overlapped object instead, and (2) the documentation tells you it only updates the overlapped object afterwards (in contrast with respective earlier paragraphs about the NULL case). A reader might have the impression that this is a drop-in replacement for POSIX pread/pwrite, but it's not, due to this side-effect.

Example:

int
main(int argc, char *argv[])
{
	DWORD written;
	HANDLE handle;
	OVERLAPPED overlapped;

	handle = CreateFile("test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (handle == INVALID_HANDLE_VALUE) {
		printf("CreateFile failed\n");
		return EXIT_FAILURE;
	}
	printf("before pointer %d\n", SetFilePointer(handle, 0, NULL, FILE_CURRENT));
	memset(&overlapped, 0, sizeof(overlapped));
	overlapped.Offset = 0;
	if (!WriteFile(handle, "hello world\n", 12, NULL, &overlapped)) {
		printf("WriteFile failed\n");
		return EXIT_FAILURE;
	}
	printf("after pointer %d\n", SetFilePointer(handle, 0, NULL, FILE_CURRENT));
	return EXIT_SUCCES;
}

The documentation gives the impression that the file pointer of a
synchronous file handle is updated by WriteFile/ReadFile only if
lpOverlapped is NULL.  In fact it is also updated if it is not NULL.
That is surprising, because (1) in this case the operation doesn't use
the file position itself, it uses the offset from the overlapped object
instead, and (2) the documentation tells you it only updates the
overlapped object afterwards (in contrast with respective earlier
paragraphs about the NULL case).  A reader might have the impression
that this is a drop-in replacement for POSIX pread/pwrite, but it's not,
due to this side-effect.

Example:

int
main(int argc, char *argv[])
{
	DWORD written;
	HANDLE handle;
	OVERLAPPED overlapped;

	handle = CreateFile("test.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (handle == INVALID_HANDLE_VALUE) {
		printf("CreateFile failed\n");
		return EXIT_FAILURE;
	}
	printf("before pointer %d\n", SetFilePointer(handle, 0, NULL, FILE_CURRENT));
	memset(&overlapped, 0, sizeof(overlapped));
	overlapped.Offset = 0;
	if (!WriteFile(handle, "hello world\n", 12, NULL, &overlapped)) {
		printf("WriteFile failed\n");
		return EXIT_FAILURE;
	}
	printf("after pointer %d\n", SetFilePointer(handle, 0, NULL, FILE_CURRENT));
	return EXIT_SUCCES;
}
@prmerger-automator
Copy link

@macdice : Thanks for your contribution! The author(s) have been notified to review your proposed change.

Copy link
Contributor

@alvinashcraft alvinashcraft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for investigating this and taking the time to update the docs. It looks like you are correct about the behavior.

@alvinashcraft alvinashcraft merged commit cd8a867 into MicrosoftDocs:docs Sep 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants