How to monitor file changes via Win API - c++

I need to monitor changes of particular set of files (or just one file) and let Windows report to my application.
It's likely, that most of the files will be in same directory, but I'd prefer per-file monitoring system.
I found this example http://codewee.com/view.php?idx=20
but the example monitors only special, Desktop folder.
First by calling SHGetSpecialFolderLocation, then using resultant LPITEMIDLIST
in SHChangeNotifyRegister function (via SHChangeNotifyEntry struct)
I was not able to generalize it to arbitrary directory.
MS Docs says that SHGetSpecialFolderLocation will not be supported in future anyway,
SHGetFolderLocation should be used instead.
But again, SHGetFolderLocation is deprecated, not even mentioning it has no string/path
parameter.
Is there any convenient function which takes directory path or complete file name
and produces LPITEMIDLIST, which can be then sticked into SHChangeNotifyRegister?

FindFirstChangeNotification et al.
ReadDirectoryChangesW, ReadDirectoryChangesExW
SHParseDisplayName can be used to convert a file path to PIDL, for use with SHChangeNotifyRegister

Related

Is there such a thing as a posix lstatat call?

I am working on a FUSE and I have a file descriptor to the directory prior to mounting the fuse on top. I want to use that handle to read/write files with state information underneath the FUSE mounted file system, and then to be able to access that data next time I mount it. So I cannot use the normal lstat call since it won't see the files I want to access, but the files FUSE exposes instead. What I need is the equivalent of fstatat that works for symbolic links, since fstatat apparently gives the the stat info on the file the symbolic link points to, not the symbolic link itself. Yet I cannot find documentation for such a function. Does it exist? Am I thinking of an incorrect name?
There is no lstatat() function in POSIX, however, fstatat()
takes a flag argument which can be AT_SYMLINK_NOFOLLOW,
which may do what you're looking for.

Writing a log file with app running in non-admin mode in windows 7

My app needs to write and maintain a log file and its not running in admin mode. My question is what path could my app write to in such a situation. How could I obtain that path ?
There are two good options:
Use the Windows Event Log. You can easily create your own log for your application (if you expect to generate a lot of messages), or you can just add the messages to the standard logs (if you expect to generate only a few, occasional messages).
Since this is a built-in feature, any technical person is going to know about it and be able to locate your log files easily. It's also very interoperable with centralized management systems.
Write to a text file saved in the Application Data directory. This is where applications are supposed to store non-user data files, since, as you mentioned, the application directory is not something you can assume write privileges to.
For a log file about stuff that is specific to a particular computer, I'd say that this is local (non-roaming) application data, so you want the Local App Data folder. I'm sure that there is a Qt wrapper for this, but in Win32, you would call the SHGetKnownFolderPath function, specifying the KNOWNFOLDERID value FOLDERID_LocalAppData.
Remember that this function allocates memory to store the returned string—you must free it with a call to CoTaskMemFree when you are finished.
Sample code:
// Retrieve the path to the local App Data folder.
wchar_t* pszPath = 0;
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &pszPath);
// Make a copy of that path.
std::wstring path(pszPath);
// Free the memory now, so you don't forget!
CoTaskMemFree(static_cast<void*>(pszPath));
Refer to the SHGetKnownFolderPath API, probably using the FOLDERID_LocalAppData option.

SHGetFolderPath Deprecated: What is alternative to retrieve path for Windows folders?

The SHGetFolderPath() function is deprecated beginning with Windows Vista: http://msdn.microsoft.com/en-us/library/bb762181%28v=VS.85%29.aspx
What is the alternative way to retrieve the path to the Application Folder in Windows?
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szPath)
Aside from that, why do I get those errors when using this function:
Error 1 error C2065: 'CSIDL_COMMON_APPDATA' : undeclared identifier
Error 2 error C3861: 'SHGetFolderPath': identifier not found
The alternative is described in the documentation to which you link. Namely it is SHGetKnownFolderPath.
However, SHGetKnownFolderPath is only available on Vista or later. So if you use load time linking, and run a program that calls SHGetKnownFolderPath on XP, then that program will fail to start. This is clearly a problem if you wish to support XP.
Now, you could switch to run time linking of SHGetKnownFolderPath. Carry out a version check before you call it, and if the function is not available, then fall back to SHGetFolderPath.
Personally, I would not let this deprecation worry you excessively. Microsoft are renowned for maintaining backwards compatibility. Don't expect SHGetFolderPath to disappear any time soon. You will find that SHGetFolderPath exists in Windows 8 and I would expect it still to be present in whatever Windows is current 10 years from now. My advice is to stick to load time linking, and only switch to SHGetKnownFolderPath when you give up supporting XP.
Your other question, that you ask in an edit, is how to call SHGetFolderPath. You need to respect the requirements which are laid out at the bottom of the MSDN documentation topic which you linked to in your question. Specifically, include Shlobj.h and pass Shlobj.lib to the linker.
It is linked right at the top, SHGetKnownFolderPath.
CSIDL_COMMON_APPDATA is replaced by FOLDERID_ProgramData in the new API.
I faced the same set of errors when I added few new header files to my already working solution.
I was already calling SHGetFolderPath and had also included #include <ShlObj.h> but it was in a different header file. The solution was compiling without any errors before I added new library header files to it.
I tried replacing SHGetFolderPath() with SHGetKnownFolderPath() but this just redirected the identifier not found error to SHGetKnownFolderPath.
On adding #include <ShlObj.h> to the header file of the class calling SHGetFolderPath, the
errors ceased and the solution compiled successfully again.
As mentioned in this page, calling SHGetFolderPath on Windows Vista or a higher OS, will internally call SHGetKnownFolderPath.
I have tested using the SHGetFolderPath() with Visual Studio 2015 Enterprise on a Windows 10 PC and it compiled and worked just fine to find the current user's home folder. In the Windows Dev Center page on SHGetFolderPath() SHGetFolderPath function there is the following note:
Note As of Windows Vista, this function is merely a wrapper for
SHGetKnownFolderPath. The CSIDL value is translated to its associated
KNOWNFOLDERID and then SHGetKnownFolderPath is called. New
applications should use the known folder system rather than the older
CSIDL system, which is supported only for backward compatibility.
As David Heffman pointed out in his answer, Microsoft has a history of keeping backwards compatibility for years especially when they can take the older function and just redirect it to the new function with the appropriate arguments. The CSIDL values seem to have a corresponding KNOWNFOLDERID value. See this table of the CSIDL constants with brief annotations and the corresponding KNOWNFOLDERID value.
An example of the use of the function follows. This use retrieves the current user's user folder (e.g. "C:\Users\myuser\Documents" under Windows 7) and then adds a folder name to the end of the path using the PathAppend() function.
TCHAR achDevice[MAX_PATH];
HRESULT hr;
// include file ShlObj.h contains list of CSIDL defines however only a subset
// are supported with Windows 7 and later.
// for the 3rd argument, hToken, can be a specified Access Token or SSID for
// a user other than the current user. Using NULL gives us the current user.
if (SUCCEEDED(hr = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, achDevice))) {
// append a folder name to the user's Documents directory.
// the Path Handling functions are pretty handy.
PathAppend(achDevice, L"xxx");
}
One possible failure is one or more invalid arguments (hr == E_INVALIDARG). A returned value of S_OK indicates the call succeeded.
There are a few CSIDL constants that can be used to modify the results of the function such as CSIDL_FLAG_CREATE by using the bitwise OR operator. I am not sure how well those operators will work with Windows 7 and later.
There are limits on the supported CSIDL constants with Windows 7 and later. It also looks like there may be possible issues to overcome in complex, remote mounted, redirected, and/or shared folders in an Active Directory or similar environment.
See also KNOWNFOLDERID which includes a table that indicates some of the limitations of CSIDL and SHGetFolderPath(). Some examples from the table of CSIDL constants that may be useful.
CSIDL_LOCAL_APPDATA - %USERPROFILE%\AppData\Local
CSIDL_MYDOCUMENTS - %USERPROFILE%\Document
CSIDL_PERSONAL - %USERPROFILE%\Documents
CSIDL_FONTS - %windir%\Fonts
CSIDL_MYMUSIC - %USERPROFILE%\Music
CSIDL_MYPICTURES - %USERPROFILE%\Pictures
CSIDL_COMMON_APPDATA - %ALLUSERSPROFILE% (%ProgramData%, %SystemDrive%\ProgramData)
CSIDL_COMMON_DOCUMENTS - %PUBLIC%\Documents
By the way, the Shell Path Handling Functions are a nice library of methods for manipulating file paths.
See also Where to put common writable application files?
From microsoft, the altenate is "SHGetKnownFolderPath"
https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpatha
From my point of view, these functions are for c, c++ and similar languages.
From powershell, I just read the registry:
PS> cd hkcu:\Software\Microsoft\Windows\CurrentVersion\Explorer\
PS> dir
Here peek at "Shell Folders" and "User Shell Folders".
btw: These are for getting the values. I'd say that's fairly safe. For setting the values, better not use the registry direct, as it will ruin your day. Using the explorer -> properties of these 'directories' to move them, will also move the contents. Unfortunately, I donnot know a hook to use that in powershell.

How to Monitor Changes in a Directory on Mac OS X?

In the Windows API, when you monitor a directory, the callback provides what changed. How do I accomplish the same for Mac OS X?
I looked at the File System Events API and the Kernel Events API and it seems like they both watch a file descriptor and return what changes happened to that file descriptor. This is inconvenient because now I have to open watchers on the entire file structure and remember the previous names (in case of a rename).
Is there a better way (that doesn't involve a library)?
FSEvents watches a directory hierarchy (or multiple hierarchies), not a file descriptor. So you only need one watcher. But you will need to scan the directories to find out which files changed. There is no better way (that doesn't involve a library).

Wow64DisableWow64FsRedirection and GetNamedSecurityInfo - unavoidable ERROR_BAD_EXE_FORMAT?

I am using Wow64DisableWow64FsRedirection / Wow64RevertWow64FsRedirection to disable and restore WOW-64 file redirection (making system32\ to syswow64\ and some registry changes). The MSDN page warns that you should use these pairs very close together because they effect all I/O operations, including loading DLLs.
I have used these successfully for quite some time, but now have come up against a seemingly impossible situation. The function I am trying to call is GetNamedSecurityInfo which takes a file path. The file path will frequently be into the system32 folder so I need to disable redirection. However, if I disable redirection the function returns ERROR_BAD_EXE_FORMAT.
I have tried to pre-load the DLL it is in with LoadLibrary(TEXT("Advapi32.dll")) but that didn't help. My guess is that it is loading another DLL within GetNamedSecurityInfo but I don't know which.
So here is the question now. What is the best way to handle this situation? Should I just pre-load all possible DLLs before using Wow64DisableWow64FsRedirection? Is there a better way?
Thanks.
It's enough that you pre-load ntmarta.dll before calling Wow64DisableWow64FsRedirection (LoadLibrary("ntmarta.dll")).
In this way GetNamedSecurityInfo / SetNamedSecurityInfo API will not return ERROR_BAD_EXE_FORMAT before that module is preloaded before (see ADVAPI32!AccProvpLoadMartaFunctions function code).
In your application you should attempt to access the directory %SystemRoot%\SysNative instead of %SystemRoot%\System32. This disables the need for FS redirection. All 32-bit processes have access to this pseudo-directory. It is invisible to 64-bit processes.
32-bit cmd.exe, http://screencast.com/t/xbAQJ2XIzoT
64-bit cmd.exe, http://screencast.com/t/t9iFd9Ruc
Using the Sysnative directory is preferrable to disabling file system redirection because of the kind of problems you have run into.