WINAPI: File exists check fail - c++

I want to check if a certain file exists in the specified location. I have been trying multiple solutions for that but seem like none of them work properly, since all of them return false.
There is no doubt that the file exists in the specified location.
Executable is being run as administrator, so I'm having the appropriate permissions.
Code I used:
#include <io.h>
#include <string>
#include <Shlwapi.h>
std::string str = "C:\WINDOWS\System32\iluminated.dll";
unsigned long attrib = GetFileAttributes(str.c_str());
bool exists1 = (attrib != INVALID_FILE_ATTRIBUTES &&
!(attrib & FILE_ATTRIBUTE_DIRECTORY)) &&
GetLastError() != ERROR_FILE_NOT_FOUND; // false
bool exists2 = ( _access( str.c_str(), 0 ) != -1 ); // false
bool exists3 = PathFileExists(str.c_str()) != 0; // false
Is there anything I'm doing wrong?

You should use double back slashes for paths, since if you use single back slashes in a string they are interpreted as command symbols (line \n, for example):
"C:\\WINDOWS\\System32\\iluminated.dll"
Alternatively, you can use forward slashes, they work on most operating systems:
"C:/WINDOWS/System32/iluminated.dll"

I found the answer. Turns out Windows is always redirecting system32 to syswow64 while trying to access in 64-bit Windows. I had to use SysNative directory, even though it doesnt exist - Windows redirects it to the proper system32 directory.
Since Visual Studio 2012, application projects default to “Any CPU
32-bit preferred”. If you run such an executable on a 64-bit Windows
operating system, then it will start as a 32-bit process and be
affected by WOW64 file system redirection.
When a 32-bit process on 64-bit Windows tries to access
"C:\Windows\System32", WOW64 redirects it to "C:\Windows\SysWOW64".
There are several ways to access the real "C:\Windows\System32"
directory:
Use "C:\Windows\SysNative", which WOW64 redirects to "C:\Windows\System32" even though it does not appear in directory
listings. This is an easy way and unlikely to cause problems.
Use Wow64DisableWow64FsRedirection and Wow64RevertWow64FsRedirection.
Use a 64-bit process.
Source: https://social.msdn.microsoft.com/Forums/en-US/c54f8368-035e-478e-b988-b180a3c7e3da/file-not-found-for-existing-file-in-system32-directory?forum=csharpgeneral

Related

boost::filesystem How to handle directories with no permissions

I am running into an error while trying to enumerate a directory using boost::filesystem.
The error appears when I try to enumerate a directory for which I have no permissions on an SMB shared directory - ie, I have RW on the share but a subdirectory on the share is access denied.
Platform etc:
Windows 10.
C++ 17 code.
Visual Studio 2019.
Unicode VS project (thus the use of std::wstring).
Shared folders from a Samba server running on Linux.
Sample code below, then details of the fail.
int enumerate(const std::wstring dir_to_enumerate) {
if (directory.size() == 0)
return -1;
boost::filesystem::path dir_to_enumerate(dir_to_enumerate);
boost::system::error_code ec;
if ( ! boost::filesystem::exists(dir_to_enumerate, ec))
return -2;
if (ec.value())
return -4;
if ( ! boost::filesystem::is_directory(dir_to_enumerate))
return -3;
boost::filesystem::directory_iterator it{ dir_to_enumerate };
while (it != boost::filesystem::directory_iterator{}) {
// ... do stuff
}
return 0;
}
Consider a folder C:\temp2 with permissions removed for my account. The test
if ( ! boost::filesystem::exists(dir_to_enumerate, ec))
return -2;
reliably detects C:\temp2 is not accessible.
Consider a folder "temp2" located on a Samba share T:. This folder on the Linux box is /data/temp2. So the directory passed into the function is L"T:\temp2", and that corresponds to location /data/temp2 on the Linux filesystem.
Samba itself shares the location R/W.
If on the Linux server the EXT4 permissions of the directory /data/temp2 are R/W (mode 700, or 750, or 755 you get the idea) for my account then everything works as expected, I can enumerate the contents of T:\temp2
The problem shows up detecting the following case reliably:
If I set the EXT4 permissions on the directory /data/temp2 to denied (mode 000) then the above code behaves unpredictably...
Sometimes, most of the time, the test
if ( ! boost::filesystem::exists(dir_to_enumerate, ec))
return -2;
detects T:\temp2 is not accessible.
But occasionally exists() returns true, and we fall thru to testing the boost::filesystem::error_code value.
However the error_code never indicates an error.
In this case the line
boost::filesystem::directory_iterator it{ dir_to_enumerate };
always throws. I can't figure out exactly what it is throwing.
I'm pulling my hair out. Can anyone tell me what I am missing, or even just what the directory_iterator initializer is throwing?
(And... if I may be so forward: please do not tell me to just use std::filesystem. On Visual Studio 2019, and AFAIK C++17 itself, std::filesystem is incomplete. Boost::filesystem works better.)

Compiling 32-bit programs and calling 64-bit programs on 64-bit systems

My system is 64 bit.
I have a program calls a command "bcdedit.exe"
c++ code:
ShellExecuteA(NULL, "open", "cmd.exe", "/c bcdedit.exe /?", NULL, SW_SHOWNORMAL);
I compiled to 32 bit
When I run it back "file not find"
When I compiled to 64 bit, run passed
The same problem exists in go
go code:
cmd := exec.Command("cmd.exe","/c","bcdedit.exe /?")
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(err)
}
fmt.Println(string(out))
I found "bcdedit.exe" in another directory:
C:\\Windows\\WinSxS\\amd64_microsoft-windows-b..iondata-cmdlinetool_31bf3856ad364e35_10.0.17134.471_none_69b0e05efb5a4702\\bcdedit.exe
When I call the command in this directory, all passed
This directory is different on every PC
How do I run the 32-bit compiled program for this command on each PC
So your code attempts to launch "bcdedit.exe". From the command line, the only location of bcdedit.exe in your PATH environment is the Windows System directory, c:\Windows\System32.
When you compile your code as 32-bit and run it on a 64-bit system, your process's view of the file system will change. Namely, the process view of the C:\Windows\System32 is replaced by the contents of C:\Windows\SysWOW64 - where only 32-bit DLLs and EXEs are located. However.... There is no 32-bit version bcdedit.exe located in this folder. (If you want to simulate this, go run c:\windows\syswow64\cmd.exe - you won't be able to find bcdedit.exe in the c:\windows\system32 folder anymore).
You probably need something like this: How to retrieve correct path of either system32 or SysWOW64?
Format your ShellExecute function to directly specificy the SysWow64 path for both bcdedit.exe and cmd.exe. Or as others have suggested, just compile for 64-bit.

Not able to get the correct path using getsystemdirectory() in 64 bit machine

I have an application running under 64bit OS (Windows 7). I was expecting GetSystemDirectory to return "C:\Windows\SysWOW64". Instead, it returns "C:\Windows\system32".
How do I get it to return "C:\Windows\SysWOW64"?
I was expecting GetSystemDirectory to return "C:\Windows\SysWOW64". Instead, it returns "C:\Windows\system32".
As it should be, because system32 is the official system folder, even for a 32bit app running on 64bit Windows. In that latter case, any files a 32bit app tries to access in system32 are silently redirected to SysWOW64 by the WOW64 emulator. You don't need to do anything special in your code to get that behavior. So keep using system32 whether your app is 32bit or 64bit.
If you want to get the path of the SysWOW64 folder specifically, use GetSystemWow64Directory() instead.
#include <Windows.h>
int main(int argv, char* args[])
{
TCHAR sysDir[MAX_PATH];
GetSystemWow64Directory(sysDir, MAX_PATH);
std::cout << sysDir << std::endl;
return 0;
}
OUTPUT:
C:\Windows\SysWOW64

I want to open Msconfig from a button on my GUI

In my line of work I use msconfig a lot and I'm wanting to create a tool with various buttons on it so I can easily access my most commonly used programs. I'm using the Win32API to create the GUI and buttons, however I am having trouble getting msconfig to run. In my program I have
void callMsconfig()
{
ShellExecute(NULL,(LPCWSTR)L"open", (LPCWSTR)L"C:\\Windows\\System32\\msconfig.exe", NULL, NULL, SW_SHOWDEFAULT);
}
and an action statement so when the button is pressed the function is called. I've tried replacing ShellExecute() with system() and CreateProcess(). I've also replaced "open" with "runas" and the most I've gotten is the error saying C:\Windows\System32\msconfig.exe cannot be found when I know it is there because I've checked. What am I doing wrong? I'm using Windows 10 Home if that helps any.
If you have created a 32bit app that is running inside of the WOW64 emulator on 64bit systems, the C:\Windows\System32\ folder is silently redirected by WOW64 to the C:\Windows\SysWOW64\ folder, which does not have an msconfig.exe file. You need to account for that. Either create a 64bit executable, or use the Sysnative alias in 32bit code that runs inside of WOW64. Sysnative is documented on MSDN:
File System Redirector:
32-bit applications can access the native system directory by substituting %windir%\Sysnative for %windir%\System32. WOW64 recognizes Sysnative as a special alias used to indicate that the file system should not redirect the access. This mechanism is flexible and easy to use, therefore, it is the recommended mechanism to bypass file system redirection. Note that 64-bit applications cannot use the Sysnative alias as it is a virtual directory not a real one.
Try something more like this:
#include <shlwapi.h>
void callMsconfig()
{
BOOL IsWow64 = FALSE;
WCHAR szCmdLine[MAX_PATH] = {0};
IsWow64Process(GetCurrentProcess(), &IsWow64);
if (IsWow64)
{
GetWindowsDirectoryW(szCmdLine, MAX_PATH);
PathAppendW(szCmdLine, L"Sysnative");
}
else
{
GetSystemDirectoryW(szCmdLine, MAX_PATH);
}
PathAppendW(szCmdLine, L"msconfig.exe");
ShellExecuteW(NULL, NULL, szCmdLine, NULL, NULL, SW_SHOWDEFAULT);
}
However, do note that if UAC is enabled and you try to launch msconfig.exe from a 32bit process running inside of WOW64, the 32bit process MUST be running elevated or else ShellExecute() (and CreateProcess()) will fail to find the file correctly. I don't know why, but that is how it works. UAC Elevation is not required when launching msconfig.exe from a 64bit process.
If you don't want to elevate your entire program, you will have to make it launch a separate elevated process that can then launch msconfig.exe. You could just have the program launch a second copy of itself with a command-line parameter so it knows to just launch msconfig.exe and then exit itself. To launch an elevated process, you can use ShellExecute() specifying the runas verb.

system() c++ wont run in VirtualBox

I'm trying to compile and run the app, which was created 4 years ago. It was developed for Windows in Embarcadero RAD Studio C++ builder. Now I try to compile and run it in Windows in VirtualBox using the latest version of RAD Studio. I have a system call to another app in my app:
system("dot.exe -Tjpg -o nfa.jpg NFA_graph.txt");
It keeps returning 1 and the file is not created. I also tried
system("Echo %CD% >> z:\log.txt");
and the file is not created. I also tried like this:
FILE *fpipe;
char *command = "Echo %CD% >> z:\log.txt";
char line[256];
if (0 == (fpipe = (FILE*)_popen(command, "r")))
{
perror("popen() failed.");
exit(1);
}
while (fread(line, sizeof line, 1, fpipe))
{
ShowMessage(line);
}
_pclose(fpipe);
And nothing I get. I wonder if the reason of such strange behaviour is that I'm running this all in VirtualBox?
You're not escaping your \ characters. You should use / in file paths, or \\ if you must. In addition, Windows 7 won't let you write to the root directory of a hard drive w/o administrator access.
To determine if a command environment is available, first do this:
if (!system(NULL)) {
// Can't make any system() calls
}
If your command environment is available, then you need to fully specify the path, making sure to escape the \'s like I mentioned above, and don't write anything to a drive's root directory. Also make note that opening files does not default create directories.
No, it's very unlikely. I see few issues with your code: you did not check errno if system() returns 1. It can help you to spot a real problem. Also, all backslashes must be Esc'ed.
I'm suggesting that dot.exe is not in PATH environment variable, that's the reason of the system() failure.