I'm trying to use GetDiskFreeSpaceEx in my C++ win32 application to get the total available bytes on the 'current' drive. I'm on Windows 7.
I'm using this sample code: http://support.microsoft.com/kb/231497
And it works! Well, almost. It works if I provide a drive, such as:
...
szDrive[0] = 'C'; // <-- specifying drive
szDrive[1] = ':';
szDrive[2] = '\\';
szDrive[3] = '\0';
pszDrive = szDrive;
...
fResult = pGetDiskFreeSpaceEx ((LPCTSTR)pszDrive,
(PULARGE_INTEGER)&i64FreeBytesToCaller,
(PULARGE_INTEGER)&i64TotalBytes,
(PULARGE_INTEGER)&i64FreeBytes);
fResult becomes true and i can go on to accurately calculate the number of free bytes available.
The problem, however, is that I was hoping to not have to specify the drive, but instead just use the 'current' one. The docs I found online (Here) state:
lpDirectoryName [in, optional]
A directory on the disk. If this parameter is NULL, the function uses the root of the current disk.
But if I pass in NULL for the Directory Name then GetDiskFreeSpaceEx ends up returning false and the data remains as garbage.
fResult = pGetDiskFreeSpaceEx (NULL,
(PULARGE_INTEGER)&i64FreeBytesToCaller,
(PULARGE_INTEGER)&i64TotalBytes,
(PULARGE_INTEGER)&i64FreeBytes);
//fResult == false
Is this odd? Surely I'm missing something? Any help is appreciated!
EDIT
As per JosephH's comment, I did a GetLastError() call. It returned the DWORD for:
ERROR_INVALID_NAME 123 (0x7B)
The filename, directory name, or volume label syntax is incorrect.
2nd EDIT
Buried down in the comments I mentioned:
I tried GetCurrentDirectory and it returns the correct absolute path, except it prefixes it with \\?\
it returns the correct absolute path, except it prefixes it with \\?\
That's the key to this mystery. What you got back is the name of the directory with the native api path name. Windows is an operating system that internally looks very different from what you are familiar with winapi programming. The Windows kernel has a completely different api, it resembles the DEC VMS operating system a lot. No coincidence, David Cutler used to work for DEC. On top of that native OS were originally three api layers, Win32, POSIX and OS/2. They made it easy to port programs from other operating systems to Windows NT. Nobody cared much for the POSIX and OS/2 layers, they were dropped at XP time.
One infamous restriction in Win32 is the value of MAX_PATH, 260. It sets the largest permitted size of a C string that stores a file path name. The native api permits much larger names, 32000 characters. You can bypass the Win32 restriction by using the path name using the native api format. Which is simply the same path name as you are familiar with, but prefixed with \\?\.
So surely the reason that you got such a string back from GetCurrentDirectory() is because your current directory name is longer than 259 characters. Extrapolating further, GetDiskFreeSpaceEx() failed because it has a bug, it rejects the long name it sees when you pass NULL. Somewhat understandable, it isn't normally asked to deal with long names. Everybody just passes the drive name.
This is fairly typical for what happens when you create directories with such long names. Stuff just starts falling over randomly. In general there is a lot of C code around that uses MAX_PATH and that code will fail miserably when it has to deal with path names that are longer than that. This is a pretty exploitable problem too for its ability to create stack buffer overflow in a C program, technically a carefully crafted file name could be used to manipulate programs and inject malware.
There is no real cure for this problem, that bug in GetDiskFreeSpaceEx() isn't going to be fixed any time soon. Delete that directory, it can cause lots more trouble, and write this off as a learning experience.
I am pretty sure you will have to retrieve the current drive and directory and pass that to the function. I remember attempting to use GetDiskFreeSpaceEx() with the directory name as ".", but that did not work.
Related
This post is not a duplicate of this one: dirent not working with unicode
Because here I'm using it on a different OS and I also don't want to do the same thing. The other thread is trying to simply count the files, and I want to access the file name which is more complex.
I'm trying to retrieve data information through files names on a windows 10 OS.
For this purpose I use dirent.h(external c library, but still very usefull also in c++).
DIR* directory = opendir(path);
struct dirent* direntStruct;
if (directory != NULL)
{
while (direntStruct = readdir(directory))
{
cout << direntStruct->d_name << endl;
}
}
This code is able to retrieve all files names located in a specific folder (one by one). And it works pretty well!
But when it encounter a file containing the character 'œ' then things are going crazy:
Example:
grosse blessure au cœur.txt
is read in my program as:
GUODU0~6.TXT
I'm not able to find the original data in the string name because as you can see my string variable has nothing to do with the current file name!
I can rename the file and it works, but I don't want to do this, I just need to read the data from that file name and it seems impossible. How can I do this?
On Windows you can use FindFirstFile() or FindFirstFileEx() followed by FindNextFile() to read the contents of a directory with Unicode in the returned file names.
Short File Name
The name you receive is the 8.3 short file name NTFS generates for non-ascii file names, so they can be accessed by programs that don't support unicode.
clinging to dirent
If dirent doesn't support UTF-16, your best bet may be to change your library.
However, depending on the implementation of the library you may have luck with:
adding / changing the manifest of your application to support UTF-8 in char-based Windows API's. This requires a very recent version of Windows 10.
see MSDN:
Use the UTF-8 code page under Windows - Apps - UWP - Design and UI - Usability - Globalization and localization.
setting the C++ Runtime's code page to UTF-8 using setlocale
I do not recommend this, and I don't know if this will work.
life is change
Use std::filesystem to enumerate directory content.
A simple example can be found here (see the "Update 2017").
Windows only
You can use FindFirstFileW and FindNextFileW as platform API's that support UTF16 strings. However, with std::filesystem there's little reason to do so (at least for your use case).
If you're in C, use the OS functions directly, specifically FindFirstFileW and FindNextFileW. Note the W at the end, you want to use the wide versions of these functions to get back the full non-ASCII name.
In C++ you have more options, specifically with Boost. You have classes like recursive_directory_iterator which allow cross-platform file searching, and they provide UTF-8/UTF-16 file names.
Edit: Just to be absolutely clear, the file name you get back from your original code is correct. Due to backwards compatibility in Windows filesystems (FAT32 and NTFS), every file has two names: the "full", Unicode aware name, and the "old" 8.3 name from DOS days.
You can absolutely use the 8.3 name if you want, just don't show it to your users or they'll be (correctly) confused. Or just use the proper, modern API to get the real name.
Supposing to use _findfirst in one of the two cases below
struct _finddata_t fd;
intptr_t search_handle;
// Either
search_handle = _findfirst( "file*.txt", &fd );
// Or
search_handle = _findfirst( ".\\file*.txt", &fd );
does the two have differences in result?
I didn't find yet if the first form can follow a specific search strategy other that the GetCurrentDirectory() one.
Can it make a difference with some filesystems (like with Unicode special names)?
From what I tested on a few PCs and different versions of Windows, it looked to be the same, but on a remote system that I can't access it looks like there can be some reason for it to fail -- maybe when the file name selected is long enough, however it's just a supposition I can't prove right now, and I still doubt it exceeds _MAX_PATH.
Short:
In my c++ project i need to read/write extended file properties. I managed it with using alternate data streams (ADS). My problem is, for opening the ADS i need to use the CreateFile API. But it is not fulfilling my needs. NtCreateFile will fullfill all my needs. (Or alternatively NtSetEaFile and NtQueryEaFile) But NtCreateFile is not directly accessible from a win32 console application.
I know i can use this function easily via GetProcAdress. But i like to know the opinion of you all, if i did miss something? Some other libs are using this pattern already, for example Chromium (https://github.com/chromium-googlesource-mirror/chromium/blob/1c1996b75d3611f56d14e2b30e7ae4eabc101486/src/sandbox/src/win_utils.cc function: ResolveNTFunctionPtr)
But im uncertain, because the c++ project is not a hobby project and i ask myself if it is dangerous or not.
I guess NtCreateFile is maybe the securest way to do, because it is well documented and supported by winternl.h header. Especially because this method is unchanged since windows 2000. But what is with NtSetEaFile, NtQueryEaFile which are fitting my needs perfectly. They are only half documented. A documentation for ZwSetEaFile and ZwQueryEaFile exist (unchanged since windows 2000).
Reason why i want to do that:
I want to write and read extended properties from files via ADS. But in case of writing the extended property of a given file the first time, i need to open the file with OPEN_ALWAYS. In case of file is not existing it will create a new file, even if i only access not the content stream of the file. To avoid this i get first the handle of the original file and check with this HANDLE if the file still exist.
But i dont want to blog any file with reduced access rights, because from my point of view that is a very bad pattern. The user needs to have full access to any file any time. Because of that we open all HANDLES with the flag FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE. And now i have the race.
auto hFile = CreateFileW(originalPath, …, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, …).
// this is the little race: if somebody at least rename originalPath the
// second CreateFileW call will cause the creation of a empty file with the
// path originalPath (the old path).
auto hADS = CreateFileW(originalPath + adsName, …, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_ALWAYS, …).
This is a main issue, especially because this happens from time to time in our tests. NtCreateFile will fix it, because i can create the second HANDLE with the help of the first HANDLE. Because of that no race. Or NtSetEaFile and NtQueryEaFile will help, because i only need one HANDLE.
The thing is, that the application needs not to be save for the future, because ADS works only on NTFS anyway. And who knows when NTFS will be exchanged. But i dont want a flaky behaviour. I want to trust this Methods. I I am fine if the API will change in the future and the software needs to adapt to it. But i want to be sure, that all Windows higher or equal then 7 can deal with it. Somebody some experience to share? I would like to hear them very much.
This question is wrong. Your proposed solution for your problem, is not using NtCreateFile, but use CreateFile with dwCreationDisposition set to the OPEN_EXISTING.
From documentation:
OPEN_EXISTING
Opens a file or device, only if it exists. If the specified file or
device does not exist, the function fails and the last-error code is
set to ERROR_FILE_NOT_FOUND.
Simply open file if exists and set whatever you want. If file is renamed, CreateFile returns ERROR_FILE_NOT_FOUND.
THE PROBLEM
Now, to your proposed solution, what is better method or why is not possible use ntdll.dll in win32 console application (???).
Again, your "better" method - GetProcAddress is "wrong" same as using linking against ntdll.dll. In Windows 11, or Windows 12 or Windows 3030 the function may be removed and both solutions (statical vs. dynamical import) will be fail.
It is not really unsecure to use this kind of APIs if their is a documentation. In case of NtSetEaFile, NtQueryEaFile and NtCreateFile you can find a description inside of Microsoft's Doc. (keep in mind NtXxx == ZwXxx)
But this API can change in the future and Microsoft does not guarantee that it will provides the same methods in the next Windows version. If you can, use the public API, because then you are safe. If not it is a case by case decision. In this case the three methods from the API are unchanged since Windows2000. Plus for example NtSetEaFile and NtQueryEaFile is used by Microsoft for WSL (Windows Subsystem for Linux). And especially NtCreateFile is used by a wide range of OpenSource Projects. So it is very unlikely that this API will change.
In my use case another aspect is important. Because I wanted to use ADS, but ADS is only supported by NTFS. So using ADS does not ensure future compatibility as well. So it was very clear for me using NtSetEaFile and NtQueryEaFile.
But how you can use this kind of APIs? Dynamic or static linking is possible. It depends on your needs what is better. In case of static linking you need to download the last WDK (Windows Driver Kit) and link against the ntdll.lib. In case of dynamic linking you can access the dll directly via GetModuleHandle and finding out the address of the method with GetProcAddress. Under Windows ntdll.dll is accessible from any application. In both cases you don't have directly a header file. You have to define the header file by yourself or use WDK to get them.
In my project dynamic linking was the best choice. The reason was, that on every windows the right implementation will be choosen and in case the method is not available i have the chance to deactivate the feature in my software instead of crash. Microsoft is recommending the dynamic way, because of the last reason.
Simple PseudoCode (dynamic case):
typedef struct _FILE_FULL_EA_INFORMATION {
ULONG NextEntryOffset;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
typedef struct _IO_STATUS_BLOCK {
union {
NTSTATUS Status;
PVOID Pointer;
};
ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
typedef NTSTATUS(WINAPI *NtSetEaFileFunction)(IN HANDLE FileHandle,
OUT PIO_STATUS_BLOCK
IoStatusBlock,
IN PVOID Buffer,
IN ULONG Length);
HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
NtSetEaFileFunction function = nullptr;
FARPROC *function_ptr = reinterpret_cast<FARPROC *>(&function);
*function_ptr = GetProcAddress(ntdll, "NtQueryEaFile");
// function could be used normally.
The other answer is incorrect. The reason is that the reason of my problem is, that I need to use OPEN_ALWAYS. Of course, if you don't need this flag, everything is fine. But in my case there is a point where I needed to create the ADS. And it will not be created without the OPEN_ALWAYS flag.
I've written a very simple service application based on this code example.
The application as part of its normal running assumes there exists a file in the directory it is found, or in its execution path.
When I 'install' the service and then subsequently 'start' the service from the service manager in control panel. The application fails because it can't find the file to open and read from (even though the file is in the same directory as the installed executable).
My question is when a windows service is run, which is the expected running path supposed to be?
When calling 'CreateService' there only seems to be a path parameter for the binary, not for execution. Is there someway to indicate where the binary should be executed from?
I've tried this on windows vista and windows 7. Getting the same issues.
Since Windows services are run from a different context than normal user-mode applications, it's best if you don't make any assumptions about working directories or relative paths. Aside from differences in working directories, a service could run using a completely different set of permissions, etc.
Using an absolute path to the file that your service needs should avoid this problem entirely. Absolute paths will be interpreted the same regardless of the working directory, so this should make the working directory of your service irrelevant. There are several ways to go about this:
Hard-code the absolute path - This is perhaps the easiest way to avoid the problem, however it's also the least flexible. This method is probably fine for basic development and testing work, but you probably want something a bit more sophisticated before other people start using your program.
Store the absolute path in an environment variable - This gives you an extra layer of flexibility since the path can now be set to any arbitrary value and changed as needed. Since a service can run as a different user with a different set of environment variables, there are still some gotchas with this approach.
Store an absolute path in the registry - This is probably the most fool-proof method. Retrieving the path from the registry will give you the same result for all user accounts, plus this is relatively easy to set up at install time.
By default, the current directory for your Windows service is the System32 folder.
A promising solution is creating an environment variable that keeps the full path of your input location and retrieving the path from this variable at runtime.
If you use the same path as binary, you could just read binary path and modify it accordingly. But this is rather quick-fix rather than designed-solution. If I were you, I would either create system-wide environment variable and store value there, or (even better) use windows registry to store service configuration.
Note:
You will need to add Yourself some privileges using AdjustTokenPrivileges function, you can see an example here in ModifyPrivilege function.
Also be sure to use HKEY_LOCAL_MACHINE and not HKEY_CURRENT_USER. Services ar running under different user account so it's HKCU's will be different than what you can see in your registry editor.
Today I solved this problem as it was needed for some software I was developing.
As people above have said; you can hardcode the directory to a specific file - but that would mean whatever config files are needed to load would have to be placed there.
For me, this service was being installed on > 50,000 computers.
We designed it to load from directory in which the service executable is running from.
Now, this is easy enough to set up and achieve as a non-system process (I did most of my testing as a non-system process). But the thing is that the system wrapper that you used (and I used as well) uses Unicode formatting (and depends on it) so traditional ways of doing it doesn't work as well.
Commented parts of the code should explain this. There are some redundancies, I know, but I just wanted a working version when I wrote this.
Fortunately, you can just use GetModuleFileNameA to process it in ASCII format
The code I used is:
char buffer[MAX_PATH]; // create buffer
DWORD size = GetModuleFileNameA(NULL, buffer, MAX_PATH); // Get file path in ASCII
std::string configLoc; // make string
for (int i = 0; i < strlen(buffer); i++) // iterate through characters of buffer
{
if (buffer[i] == '\\') // if buffer has a '\' in it, replace with doubles
{
configLoc = configLoc + "\\\\"; // doubles needed for parsing. 4 = 2(str)
}
else
{
configLoc = configLoc + buffer[i]; // else just add char as normal
}
}
// Complete location
configLoc = configLoc.substr(0, configLoc.length() - 17); //cut the .exe off the end
//(change this to fit needs)
configLoc += "\\\\login.cfg"; // add config file to end of string
From here on, you can simple parse configLoc into a new ifsteam - and then process the contents.
Use this function to adjust the working directory of the service to be the same as the working directory of the exe it's running.
void AdjustCurrentWorkingDir() {
TCHAR szBuff[1024];
DWORD dwRet = 0;
dwRet = GetModuleFileName(NULL, szBuff, 1024); //gets path of exe
if (dwRet != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
*(_tcsrchr(szBuff, '\\') + 1) = 0; //get parent directory of exe
if (SetCurrentDirectory(szBuff) == 0) {
//Error
}
}
}
Is there a C++ .NET function that I can call that will detect if my program is running in compatibility mode? If there is not one, could someone show me the code for one? Thanks.
For example:
Program loads up
Compatibility Mode check
if true then exit
else run
From another forum
After a few google searches went in
vain, I decided to experiment myself.
I found that the compatibility
settings for each executable are
stored - as I thought it would be - in
the windows registry.
The key where the settings are stored
is
HKEY_CURRENT_USER\Software\Microsoft\Windows
NT\CurrentVersion\AppCompatFlags\Layers
For each application that has its
compatibility settings specified,
there exists a value under that key
whose name is the path to the
executable and the data is a string
consisting of the compatibility
settings.
The keywords in the string that
specify the compatibility settings
are: WIN95 WIN98 NT4SP5
WIN2000 256COLOR 640X480
DISABLETHEMES DISABLECICERO
If multiple settings are specified (or
are to be specified), the data
consists of the settings above
separated by a space each. The first
four settings are mutually exclusive,
i.e. only one of them is to be
specified (if at all). I haven't
tested the consequences of specifying
multiple operating systems.
So, back to addressing your problem.
To check if an executable (let's say,
"C:\path\executable.exe") is set to
run in 256 color mode, there would be
a value named "C:\path\executable.exe"
(without the quotes, even if the path
contains spaces) under the key
[HKEY_CURRENT_USER\Software\Microsoft\Windows
NT\CurrentVersion\AppCompatFlags\Layers],
and the data associated with the value
would contain the string "256COLOR".
If it is also set to run in
compatibility mode under Windows
98/ME, the data would be "WIN98
256COLOR".
So, the approach is simple. Test if
there is a value with the full path of
the executable under the key I
mentioned above. If there isn't, the
executable has not been specified any
compatibility settings. If the value
exists, retrieve its data and check
for the presence of "256COLOR" in the
data. Accordingly, the presence of
"WIN95" or "WIN98" or "NT4SP5" or
"WIN2000" would mean that the
executable is set to run in
compatibility mode for that particular
operating system.
Get the version of the operation system that is returned from GetVersionEx and compare it to the file version on kernel32.dll. When in application compatibility mode GetVersionEx will always return the version of the operating system that is being 'emulated'. If both versions are different then you are in application compatibility mode.
The answer above helped me to get a "solution" for the question at hand. It is probably not the most elegant, but seems to work. Obviously you can get a bit more creative on the return type. Booleon does not suffice here. I think a native API would be good.
typedef VOID (NTAPI* TRtlGetNtVersionNumbers)(LPDWORD pdwMajorVersion, LPDWORD pdwMinorVersion, LPDWORD pdwBuildNumber);
bool IsRunningCompatMode()
{
TRtlGetNtVersionNumbers RtlGetNtVersionNumbers = (TRtlGetNtVersionNumbers)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetNtVersionNumbers");
assert(RtlGetNtVersionNumbers);
if(RtlGetNtVersionNumbers)
{
OSVERSIONINFO osInfo = {0};
osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osInfo);
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
RtlGetNtVersionNumbers(&dwMajorVersion, &dwMinorVersion, &dwBuildNumber);
dwBuildNumber &= 0x0000FFFF;
if(osInfo.dwBuildNumber != dwBuildNumber)
{
return true;
}
}
return false;
};