CreateFileW with \\?\ prefix and long file name returned INVALID_HANDLE_VALUE - winapi

#include <Windows.h>
#include <cassert>
int main()
{
auto h = CreateFileW(
LR"(\\?\C:\Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooog.txt)",
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL
);
assert(h != INVALID_HANDLE_VALUE);
CloseHandle(h);
}
What am I doing wrong here?
Project settings btw:
Running with admin doesn't solve the issue either. I am running windows 11 22621

You are running into an NTFS (presumably) limitation, where path components (including the file name) are limited to 255 UTF-16 code units. The file name you are supplying is 263 code units long, and you are getting an ERROR_INVALID_NAME error code ("The filename, directory name, or volume label syntax is incorrect.").
To solve this, you'll need to make the file name part no longer than 255 UTF-16 code units.

Related

Weird case of AccessViolation while using overlapped IO in Windows

I've been experimenting with Overlapped IO feature of Win32 API, namely ReadFileEx and WriteFileEx functions.
Here is a simplest example:
#include <iostream>
#include <string>
#include <Windows.h>
const DWORD blockSize = 512;
char buffer[blockSize];
bool done = false;
OVERLAPPED overlapped;
void Completion(DWORD error, DWORD read, LPOVERLAPPED overlapped) {
}
int _tmain(int argc, _TCHAR* argv[])
{
std::wstring fileName;
std::wcout << "Enter file name: ";
std::getline(std::wcin, fileName);
HANDLE file = CreateFile(
fileName.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
if (file == INVALID_HANDLE_VALUE) {
std::wcout << "Error while opening file: " << GetLastError() << std::endl;
std::cin.get();
return -1;
}
overlapped.Offset = overlapped.OffsetHigh = 0;
ReadFileEx(file, buffer, blockSize, &overlapped, (LPOVERLAPPED_COMPLETION_ROUTINE) &Completion);
while (!done)
SleepEx(1000, TRUE);
return 0;
}
As you can see, I'm starting overlapped reading with ReadFileEx and waiting for it to finish using SleepEx. Although Completion has empty body and the program should hang forever after reading first block, it does something completely different. In fact, it raises AccessViolation with code 0xC0000005 on the first call to SleepEx. This happens on Windows machines with Win 7 and Win Vista I've tried, including fresh 64 bit virtual machine with Windows 7. But, curiously, it does not fail on one Windows 8.1 machine and runs just as expected. This was compiled with Visual Studio 2010, but I don't think it matters.
I can not understand what have I done wrong, so if anyone knows the answer, please help me.
The callback must be of the following form, as stated clearly in the documentation:
VOID CALLBACK FileIOCompletionRoutine(
_In_    DWORD        dwErrorCode,
_In_    DWORD        dwNumberOfBytesTransfered,
_Inout_ LPOVERLAPPED lpOverlapped
);
Your function has the wrong calling convention. Your cast does not change that. It is simply a way for you to stop the compiler rejecting your ill-formed program. Don't lie to the compiler. Correct the function declaration by adding the CALLBACK calling convention.

Best way to have crash dumps generated when processes crash?

In Windows environments (XP and Win 7):
What is the best way to automatically have a crash dump generated when processes crash on the system?
Can an installer (MSI) package do this?
One of the best way to have an automatic dump for any/specific process on Windows is to configure a set of entries in the registry. I tried the below on Windows 7 64 bit.
Open notepad.exe, paste the below entry and save it as "EnableDump.reg". You can give any name you wish.
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps]
"DumpFolder"=hex(2):44,00,3a,00,5c,00,64,00,75,00,6d,00,70,00,00,00
"DumpCount"=dword:00000010
"DumpType"=dword:00000002
"CustomDumpFlags"=dword:00000000
Double click the "EnableDump.reg" and select 'Yes'. I have given the dump folder as 'd:\dump'. You can change it to whatever folder you wish.
Try to execute a crashing application, Windows will display the error dialog. Choose 'Close the Program' option. After that you will see the dump in the configured folder. The name of the dump file will be .exe..dmp.
For more details, you can refer the below link.
http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx
Below explanation is based on another answer, but the logic is mine (without attribution need, as said on my profile);
Having your own dump generation framework which automatically creates a process dump when any Unhandled exception is encountered, would avoid clients having to install WinDbg.
At the application start up use SetUnhandledExceptionFilter(...) Win32 API to register a callback (i.e. application level exception-handler).
Now the registered callback function is called whenever there is any exception which is not handled. You may then create the process dump using MiniDumpWriteDump(...) API from DbgHelp.dll.
C++ Sample (unicode-enabled)
header-file
#ifndef CRASH_REPORTER_H
#define CRASH_REPORTER_H
//Exclude rarely used content from the Windows headers.
#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
#else
# include <windows.h>
#endif
#include <tchar.h>
#include <DbgHelp.h>
class CrashReporter {
public:
inline CrashReporter() { Register(); }
inline ~CrashReporter() { Unregister(); }
inline static void Register() {
if(m_lastExceptionFilter != NULL) {
fprintf(stdout, "CrashReporter: is already registered\n");
fflush(stdout);
}
SetErrorMode(SEM_FAILCRITICALERRORS);
//ensures UnHandledExceptionFilter is called before App dies.
m_lastExceptionFilter = SetUnhandledExceptionFilter(UnHandledExceptionFilter);
}
inline static void Unregister() {
SetUnhandledExceptionFilter(m_lastExceptionFilter);
}
private:
static LPTOP_LEVEL_EXCEPTION_FILTER m_lastExceptionFilter;
static LONG WINAPI UnHandledExceptionFilter(_EXCEPTION_POINTERS *);
};
#endif // CRASH_REPORTER_H
source-file
#include "crash-report.h"
#include <stdio.h>
LPTOP_LEVEL_EXCEPTION_FILTER CrashReporter::m_lastExceptionFilter = NULL;
typedef BOOL (WINAPI *MiniDumpWriteDumpFunc)(HANDLE hProcess, DWORD ProcessId
, HANDLE hFile
, MINIDUMP_TYPE DumpType
, const MINIDUMP_EXCEPTION_INFORMATION *ExceptionInfo
, const MINIDUMP_USER_STREAM_INFORMATION *UserStreamInfo
, const MINIDUMP_CALLBACK_INFORMATION *Callback
);
LONG WINAPI CrashReporter::UnHandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtr)
{
//we load DbgHelp.dll dynamically, to support Windows 2000
HMODULE hModule = ::LoadLibraryA("DbgHelp.dll");
if (hModule) {
MiniDumpWriteDumpFunc dumpFunc = reinterpret_cast<MiniDumpWriteDumpFunc>(
::GetProcAddress(hModule, "MiniDumpWriteDump")
);
if (dumpFunc) {
//fetch system time for dump-file name
SYSTEMTIME SystemTime;
::GetLocalTime(&SystemTime);
//choose proper path for dump-file
wchar_t dumpFilePath[MAX_PATH] = {0};
_snwprintf_s(dumpFilePath, MAX_PATH, L"crash_%04d-%d-%02d_%d-%02d-%02d.dmp"
, SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay
, SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond
);
//create and open the dump-file
HANDLE hFile = ::CreateFileW( dumpFilePath, GENERIC_WRITE
, FILE_SHARE_WRITE
, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_HIDDEN
, NULL
);
if (hFile != INVALID_HANDLE_VALUE) {
_MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
exceptionInfo.ThreadId = GetCurrentThreadId();
exceptionInfo.ExceptionPointers = exceptionPtr;
exceptionInfo.ClientPointers = NULL;
//at last write crash-dump to file
bool ok = dumpFunc(::GetCurrentProcess(), ::GetCurrentProcessId()
, hFile, MiniDumpNormal
, &exceptionInfo, NULL, NULL
);
//dump-data is written, and we can close the file
CloseHandle(hFile);
if (ok) {
//Return from UnhandledExceptionFilter and execute the associated exception handler.
// This usually results in process termination.
return EXCEPTION_EXECUTE_HANDLER;
}
}
}
}
//Proceed with normal execution of UnhandledExceptionFilter.
// That means obeying the SetErrorMode flags,
// or invoking the Application Error pop-up message box.
return EXCEPTION_CONTINUE_SEARCH;
}
usage
#include "3rdParty/crash-report.h"
int main(int argc, char *argv[])
{
CrashReporter crashReporter;
(void)crashReporter; //prevents unused warnings
// [application main loop should be here]
return 0;
}
Windows XP:
The following steps enable automatic crash dumps:
1) Open a command prompt, running as administrator
2) Run drwtsn32 -i. This will install Doctor Watson as the default debugger when something crashes
3) Click Ok
4) From the command prompt, run drwtsn32
5) Set the Crash Dump path to your favorite directory, or leave the default.
6) Set the Crash Dump Type to mini. Note that under some circumstances, we may ask you for a full crash dump.
7) Make sure the Dump All Thread Contexts and Create Crash Dump File options are selected.
8) Click Ok
9) If a user.dmp file already exists in the Crash Dump path, delete it.
Windows 7: Location is:
C:\Users[Current User when app crashed]\AppData\Local\Microsoft\Windows\WER\ReportArchive

Why does CreateFile not open files ending in a dot in 64-bit program?

There is a strange difference in the behaviour of Windows CreateFile depending whether you compile your program as 32 or 64 bit. The former will happily open files ending in a dot (period) while the latter fails with ERROR_ACCESS_DENIED.
For example the following program will depend differently depending on the bitness:
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE h = CreateFile(
_T("\\bob."),
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
return h != INVALID_HANDLE_VALUE;
}
Am I doing something wrong or is it a Windows bug? Windows 7, if it makes a difference.

What is the purpose of the win32 CreateFile2 api call?

They have added a new API call in windows 8 called CreateFile2 which as far as I can tell does exactly the same as the existing CreateFile function only it packages up its parameters somewhat differently.
What has been added to make this necessary, as I can't see anything in the documentation.
CreateFile actually can do a lot more than just open a file. CreateFile2 was created to both restrict the 'surface area' of the function to just the functionality allowed for UWP apps, -and- because the WACK tool can't really distinguish between 'good' use and 'bad' use of an import function, just that it is being used at all.
The typical pattern I use in my C++ libraries is as follows. I use an RAII pattern for the file handle to support when C++ Exception Handling is used (in addition to just being good modern C++ coding practice):
#include <assert.h>
#include <memory>
struct handle_closer
{ void operator()(HANDLE h) noexcept { assert(h != INVALID_HANDLE_VALUE); if (h) CloseHandle(h); } };
using ScopedHandle = std::unique_ptr<void, handle_closer>;
inline HANDLE safe_handle(HANDLE h) noexcept { return (h == INVALID_HANDLE_VALUE) ? nullptr : h; }
The reason I have safe_handle is because CreateFile and CreateFile2 are defined to return INVALID_HANDLE_VALUE (-1) instead of returning 0 for a failure. Most other Win32 functions that return handles return 0 for a failure, and I've confirmed that there's no case where a '0' is a valid Win32 handle.
For reading I use:
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
ScopedHandle hFile(safe_handle(
CreateFile2(szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, nullptr)));
#else
ScopedHandle hFile(safe_handle(
CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, nullptr)));
#endif
if (!hFile)
// Error
It is important to use FILE_SHARE_READ instead of 0 for the dwShareMode parameter for reading. UWP apps don't have exclusive read access to existing files, so calls would fail if you used 'exclusive' share mode (i.e. 0).
And for writing a file:
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
ScopedHandle hFile(safe_handle(
CreateFile2(szFile, GENERIC_WRITE | DELETE, 0, CREATE_ALWAYS, nullptr)));
#else
ScopedHandle hFile(safe_handle(
CreateFileW(szFile, GENERIC_WRITE | DELETE, 0, nullptr, CREATE_ALWAYS, 0, nullptr)));
#endif
if (!hFile)
// Error!
For writing I request the DELETE permission because I use SetFileInformationByHandle with FILE_DISPOSITION_INFO to clean up if the file output process fails. See scoped.h.
See Dual-use Coding Techniques for Games for more information.
Due to the multiplexing of file creation flags, file attribute flags and
security QoS flags into a single DWORD (dwFlagsAndAttributes) parameter for
CreateFile, there is no way to add any more flags to CreateFile. Additional
flags for the create operation must be added to CreateFile2 only. For example FILE_FLAG_OPEN_REQUIRING_OPLOCK flag. This flag is documented FltCreateFile - kernel mode

Creating a file exceeding filepath limit

I have a test that creates a series of folders in a loop until it exceeds the MAX_PATH (260). This returns ERROR_PATH_NOT_FOUND(0x3). We have a build machine that runs this test but on the build machine it returns ERROR_FILENAME_EXCED_RANGE (0xce).
My machine is Windows 7 but the build machine is Vista. Could that be why they return different values? If not, does anyone know why this might happen?
EDIT: I am expecting to get an error, im testing a file system driver. I just do not understand why i am getting two different error codes from the same test on different machines.
Here is the code
homeDir << "C:\Users\me\TestFolder";
string childDir = "\\LongChildDirectoryName";
string dir = homeDir.str();
DWORD lastErr = ERROR_SUCCESS;
while(lastErr == ERROR_SUCCESS)
{
int len = dir.size();
if(len > (MAX_PATH - 12))
{
CuFail(tc, "Filepath greater than max allowed should be");
}
dir += childDir;
if(!CreateDirectory(dir.c_str(), NULL))
{
lastErr = GetLastError();
if (lastErr == ERROR_ALREADY_EXISTS)
lastErr = ERROR_SUCCESS;
}
}
CuAssert(tc, "Check error is ERROR_PATH_NOT_FOUND", lastErr == ERROR_PATH_NOT_FOUND);
The logic is flawed. If homeDir.str() returns a name that doesn't exist, the return value from CreateDirectory will be ERROR_PATH_NOT_FOUND. You can demonstrate the problem by simply doing this:
string childDir("\\LongChildDirectoryName");
string dir("foo");
The CreateDirectory call will then get the path foo\LongChildDirectoryName, and if foo doesn't exist, you get ERROR_PATH_NOT_FOUND. The fix is simply to add this before the while loop:
CreateDirectory(dir.c_str(), NULL);
You also need to move the length check after the strings have been concatenated, not before. Using the "\\?\" syntax Alex suggested would also be a good idea.
To use longer paths you need to use the "wide" version of CreateFile(), CreateFileW().
See this MSDN article on the topic:
HANDLE WINAPI CreateFile(
__in LPCTSTR lpFileName,
__in DWORD dwDesiredAccess,
__in DWORD dwShareMode,
__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
__in DWORD dwCreationDisposition,
__in DWORD dwFlagsAndAttributes,
__in_opt HANDLE hTemplateFile
);
lpFileName [in]
The name of the file or device to be created or opened.
In the ANSI version of this function, the name is limited to MAX_PATH characters.
To extend this limit to 32,767 wide characters, call the Unicode version of the
function and prepend "\\?\" to the path. For more information, see Naming Files,
Paths, and Namespaces.

Resources