Is there a way to get the SearchPath API to not search in c:\windows when using the default search path (passing NULL as the first param)? I can't modify the caller to send in a specific path.
I have a system with an application ini file in c:\windows (which I don't want it to use, but for legacy reasons has to remain there). I put my copy of the same ini file in c:\users\public, and put c:\users\public at the front of my system path environment variable, but a call to SearchPath still finds the c:\windows version. If I delete that version, it then finds the c:\users\public version, so I know the path was set correctly.
I know this is very late, but having just run into this problem myself, I would propose a better solution.
The first argument to SearchPath, as you have found, can be used to specify the directories you want it to search, instead of the default order. You can retrieve and use the current user's PATH with GetEnvironmentVariable, and then search within that:
DWORD err = GetEnvironmentVariable("PATH", NULL, 0);
char* path = new char[err+1]; path[err] = 0;
GetEnvironmentVariable("PATH", path, err);
err = SearchPath(path, "application", ".ini", 0, NULL, NULL);
char* searchResult = new char[err+1]; searchResult[err] = 0;
err = SearchPath(path, "application", ".ini", err, searchResult, NULL);
According to MSDN, there's nothing you can do about this bar changing a system level (HKLM) registry entry (Which is a "bad thing"). The registry change would cause the search order to start with the current working directory, which you could set to the desired folder in a shortcut. (Again, I'm going to say; changing a Machine Level registry entry to do this - is potentially dangerous!)
Have you looked into application shims? This may be something that could work for you.
Try SetCurrentDirectory("c:\users\public") and then SearchPath(...).
Related
I am struggling to find out how the “SHParseDisplayName” function builds the output absolute pidl.
I absolutely need to control its output.
To understand what I mean I make two examples:
Executing SHParseDisplayName("C:\\Users\\Username\\Onedrive", IntPtr.Zero, out pidl, 0, out psfgaoOut) the absolute "pidl" output variable is composed by these pidls (obtained by executing in sequence SHGetNameFromIDList(pidl, SIGDN.PARENTRELATIVEPARSING, out name) and ILRemoveLastID(pidl) until I reach the root):
::{018D5C66-4533-4307-9B53-224DE2ED1FE6}
Desktop
Executing the same for "C:\Users\Username\Pictures" the result is:
::{24AD3AD4-A569-4530-98E1-AB02F9417AA8}
::{20D04FE0-3AEA-1069-A2D8-08002B30309D}
Desktop
At first, I thought that the absolute pidl was composed based on the “pszParsingName” value of the KNOWNFOLDER_DEFINITION of the relative known folder so I created a new known folder using IKnownFolderManager::RegisterFolder and setting a particular Shell namespace folder path in the “pszParsingName” value of the KNOWNFOLDER_DEFINITION.
Unfortunately the absolute pidl output was not the one expected. (And I added the new known folder path also in “user shell folders” registry key)
So I am asking your help to find out how I can control the building of the absolute pidl output of the “SHParseDisplayName” function.
Thanks a lot for any help
I am struggling to understand what may be causing the issue in my case.
I am using
ShellExecuteW('open', 'explorer.exe', '/select,[file_name]', None, win32con.SW_SHOW)
What I am trying to do is open the file in the OS, highlight it, and bring the File Explorer to the foreground. This works fine for most cases, but when I try to open a file that exceeds the MAX_PATH limit (260 characters), the file doesn't open, and instead it takes me to the "My Files" page.
I have tried prepending "\\?\" to the beginning of my file name, because that is what other Stack Overflow posts said to do with regards to overriding the MAX_PATH limit, but it has not changed the situation.
Does the ShellExecuteW function not allow for files that exceed MAX_PATH? And, if so, is there any workaround I could use?
I read some cases, about this issue. Find this article:Long Paths in .NET, Part 1 of 3 [Kim Hamilton]
If you prefix the file name with "\?\" and call the Unicode versions of the Windows APIs, then you can use file names up to 32K characters in length. In other words, the \?\ prefix is a way to enable long paths while working with the Windows file APIs.
and:
Long paths with the \?\ prefix can be used in most of the file-related Windows APIs, but not all Windows APIs.
I also test ShellExcuteW with \\?\,it failed.
Working well with SHOpenFolderAndSelectItems
CoInitialize(NULL);
LPCWSTR file_name ;//Change the path according to your needs
PIDLIST_ABSOLUTE pidl;
if (SUCCEEDED(SHParseDisplayName(file_name, 0, &pidl, 0, 0)))
{
ITEMIDLIST idNull = { 0 };
LPCITEMIDLIST pidlNull[1] = { &idNull };
SHOpenFolderAndSelectItems(pidl, 1, pidlNull, 0);
ILFree(pidl);
}
Note:CoInitialize or CoInitializeEx must be called before using SHOpenFolderAndSelectItems. Not doing so causes SHOpenFolderAndSelectItems to fail.
Looks like SHGetSetFolderCustomSettings allows you to set an icon, a tooltip, a web view template and stuff, but I could not find how to set the LocalizedResourceName in the associated desktop.ini (see SHFOLDERCUSTOMSETTINGS structure).
Therefore I am currently writing to desktop.ini directly, however this comes with a caveat:
Explorer does not properly update its views even when you tell it to refresh with F5 or Ctrl+R.
This is what I want to write, using Python (though non-Python code should be less of an issue):
[.ShellClassInfo]
LocalizedResourceName=My Folder Name
InfoTip=A customized folder
Any ideas how to set the folder name and have Explorer properly update it ?
I have tried with SHChangeNotify(SHCNE_ALLEVENTS, SHCNF_PATH, path, path), but this does not seem to update the display name (and also with SHCNE_RENAMEFOLDER, SHCNE_RENAMEITEM, SHCNE_UPDATEDIR, SHCNE_UPDATEITEM).
(The worst approach would probably be to edit the desktop.ini twice... once directly, then with that API function... rather not what I want).
About the why (I guess at least one of you will ask):
I am storing project data using GUIDs as folder names.
The user should however see a friendly name that can also be used for sorting (and maybe even be able to edit it without interfering with the internal name).
Furthermore, the low-level file system layout should be backwards-compatible with older versions of the software.
Use simple call of IShellFolder.SetNameOf:
procedure UpdateLocalizedResourceName(const ADirectory, ANewResourceName: UnicodeString);
var
Desktop: IShellFolder;
Eaten: DWORD;
DirIDList1, Child, NewChild: PItemIDList;
Attr: DWORD;
Folder: IShellFolder;
begin
OleCheck(SHGetDesktopFolder(Desktop));
try
Attr := 0;
OleCheck(Desktop.ParseDisplayName(0, nil, PWideChar(ADirectory), Eaten, DirIDList1, Attr));
try
OleCheck(SHBindToParent(DirIDList1, IShellFolder, Pointer(Folder), Child));
try
OleCheck(Folder.SetNameOf(0, Child, PWideChar(ANewResourceName), SHGDN_INFOLDER, NewChild));
CoTaskMemFree(NewChild);
finally
Folder := nil;
end;
finally
CoTaskMemFree(DirIDList1);
end;
finally
Desktop := nil;
end;
end;
UPDATE
Important notice! LocalizedResourceName parameter must exists in desktop.ini before you call UpdateLocalizedResourceName. Otherwise SetNameOf function fails.
I'm using the following function to delete a file to the recycle bin: (C++, MFC, Unicode)
bool DeleteFileToPaperbasket (CString filename)
{
TCHAR Buffer[2048+4];
_tcsncpy_s (Buffer, 2048+4, filename, 2048);
Buffer[_tcslen(Buffer)+1]=0; //Double-Null-Termination
SHFILEOPSTRUCT s;
s.hwnd = NULL;
s.wFunc = FO_DELETE;
s.pFrom = Buffer;
s.pTo = NULL;
s.fFlags = FOF_ALLOWUNDO | FOF_SILENT | FOF_NOERRORUI;
s.fAnyOperationsAborted = false;
s.hNameMappings = NULL;
s.lpszProgressTitle = NULL;
int rc = SHFileOperation(&s);
return (rc==0);
}
This works nicely for most files. But if path+filename exceed 255 characters (and still much shorter that 2048 characters), SHFileOperation returns 124. Which is DE_INVALIDFILES.
But what's wrong? I checked everything a million times. The path is double-null terminated, I'm not using \\?\ and it works for short filenames.
I'm totally out of ideas...
I think backwards comparability is biting you in the --- in several ways, and I'd need to actually see the paths your using and implement some error checking code to help. But here are some hints.
You would not get a DE_INVALIDFILES 0x7C "The path in the source or destination or both was invalid." for a max path violation, you'd get a DE_PATHTOODEEP 0x79 "The source or destination path exceeded or would exceed MAX_PATH."
These error codes(return value) do, can, and have changed over time, to be sure what your specific error code means, you need to check it with GetLastError function(msdn)
Also, taken from the SHFileOperation function documentation: "If you do not check fAnyOperationsAborted as well as the return value, you cannot know that the function accomplished the full task you asked of it and you might proceed under incorrect assumptions."
You should not be using this API for extremely long path names, it has been replaced in vista+ by IFileOperation interface
The explanation for why it may work in explorer and not thru this LEGACY api is - Taken from the msdn page on Naming Files, Paths, and Namespaces
The shell and the file system have different requirements. It is
possible to create a path with the Windows API that the shell user
interface is not able to interpret properly.
Hope this was helpful
The recycle bin doesn't support files whose paths exceed MAX_PATH in length. You can verify this for yourself by trying to recycle such a file in Explorer - you will get an error message about the path being too long.
What's the way to get default volume on Mac 64-bit?
I have a code like that:
GetVolParmsInfoBuffer buf_64 = { 0 };
status = FSGetVolumeParms(vol_ref, // use default volume
&buf_64, // write
req_count);
The problem is that I can't pass 0 in vol_ref. On Mac 32-bit I could write:
GetVolParmsInfoBuffer buf_32 = { 0 };
HParamBlockRec pb;
pb.ioParam.ioCompletion = NULL; // not doing async I/O
pb.ioParam.ioNamePtr = NULL; // we don't use path name
pb.ioParam.ioVRefNum = 0; // use default volume
pb.ioParam.ioBuffer = reinterpret_cast(&buf_32); // write data here
pb.ioParam.ioReqCount = req_count;
OSErr err = PBHGetVolParmsSync(&pb);
ASSERT_EQ(err, noErr);
Thanks in advance,
- Oleksii
In the File Manager docs, you'll notice a function group titled “Manipulating the Default Volume”. All of those functions are deprecated.
If you search Google for the functions therein, particularly HSetVol, you'll find this mailing list post by Eric Schlegel, which says HSetVol had the effect of setting the current working directory (expressed as a volume/directory pair) on Mac OS. He also says that it doesn't work on Mac OS X: It should work on File Manager functions, but does not set the working directory used for resolving relative paths in other APIs (e.g., open and fopen) like it did on Mac OS.
Moreover, those functions are not available in 64-bit Mac OS X. So the answer is: You don't, because there is no default volume.
The old meaning of it was analogous to the current working directory, so you can do the same thing by getting the CWD and resolving that path to an FSRef. However, for a Mac OS X application (particularly one that doesn't set the CWD by any means, as most don't), this is not terribly useful: The default CWD for an application is /, the root directory of the boot volume. On the other hand, if you run your executable directly or under Xcode's Debugger, its CWD will not be /, which means it could be some other volume—most probably, the one with your Home folder on it.
You should refer to the boot volume (or whatever volume you're interested in) specifically, not attempt to get or simulate getting the default (current working) directory.
For the boot volume, you might try kOnSystemDisk, which is one of the constants in the Folder Manager. If that doesn't work, use Folder Manager's FSFindFolder function to retrieve the System folder, then use File Manager's FSGetVolumeInfo function to get what volume it's on.
Well. I don't really know what "default volume" is. All I know is that Carbon manual (File Manager) says:
ioVRefNum
A volume reference number, 0 for the default volume, or a drive number.
Well, I seem to find the answer for my question.
FSVolumeInfoParam vol_info = { 0 };
vol_info.ioVRefNum = kFSInvalidVolumeRefNum; // will obtain it
vol_info.volumeIndex = 1; // XXX: is it the default volume as well?
vol_info.whichInfo = kFSVolInfoNone; // don't pass volume info
err = PBGetVolumeInfoSync(&vol_info);
The only thing I'm not sure of is if the 1st volume is the default one...
P.S. I guess the problem is that I don't quite understand what "default volume" really is ;-)