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
Related
I need to call ZwCreateFile with a relative path, with the path being relative to the root of a \Device\HarddiskVolumeSomeNumber using InitializeObjectAttributes.
For example, I would like to use the string "path.txt" as ObjectName and a handle to "\Device\HarddiskVolume5" as RootDirectory.
My problem is obtaining a HANDLE object for the device directory. It seems ZwOpenFile and ZwOpenDirectory are not able to open these paths, even though I am able to open a file such as \Device\HarddiskVolume5\hello.txt
How must I obtain a HANDLE object for use as RootDirectory in OBJECT_ATTRIBUTES?
Edit: Changed title to say 5 instead of 1. I am aware that some numbered Harddisks such as 1 are not normal filesystems, such as partition data.
I am trying to identify when a file is PNG or JPG to apply it as a wallpaper. I am using the SHGetFileInfo to get the type name with the .szTypeName variable, but I just realized that it changes if the OS is in another language.
This is my code:
SHFILEINFOW fileInfo;
UINT sizeFile = sizeof(fileInfo);
UINT Flags = SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES;
// Getting file info to find out if it has JPG or PNG format
SHGetFileInfoW(argv[1], 0, &fileInfo, sizeFile, Flags);
This is how I am validating:
if (wcscmp(fileInfo.szTypeName, L"JPG File") == 0)
{
//Code here
}
When the OS is in spanish, the value changes to "Archivo JPG" so I would have to validate against all language, and does not make sense.
Any idea what other function I can use?
This API is meant to be used to produce a user-facing string representation for known file types1). It is not meant to be used to implement code logic.
More importantly, it doesn't try to parse the file contents. It works off of the file extension alone. If you rename an Excel workbook MySpreadsheet.xlsx to MySpreadsheet.png, it will happily report, that this is a "PNG File".
The solution to your problem is simple: You don't have to do anything, other than filtering on the file extension. Use PathFindExtension (or PathCchFindExtension for Windows 8 and above) to get the file extension from a fully qualified path name.
This can fail, in case the user appended the wrong file extension. Arguably, this isn't something your application should fix, though.
As an aside, you pass SHGFI_USEFILEATTRIBUTES to SHGetFileInfoW but decided to not pass any file attributes (second argument) to the call. This is a bug. See What does SHGFI_USEFILEATTRIBUTES mean? for details.
1) It is the moral equivalent of SHGFI_DISPLAYNAME. The only thing you can do with display names is display them.
I have a simple method to move a folder to a new directory
Dim firstshare As String = "\\myshare\users\" & frmDeparture.txtUsername.Text
Dim destination As String = "\\secondshare\userarchives$\" & frmDeparture.txtUsername.Text
Try
If Directory.Exists(firstshare) Then
Directory.Move(firstshare, destination)
MsgBox("Folder moved from \\firstshare\users")
End If
Catch ex As Exception
MsgBox("Error finding folder")
End Try
This works fine if I set "destination" as a path like "\path\whatever", but if it's a hidden path (with the $) it doesn't work. Is there something special I have to do in order to access a hidden share programatically?
You are most likely trying to move a directory from one volume/partition to another, and you are getting this error :
Source and destination path must have identical roots. Move will not
work across volumes
An explanation of why this is not possible is found Here. The only way you could move directories across different volumes is to create a new directory in the destination volume and copy the files from the source. You could then delete the original files if you wish.
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.
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(...).