How does SHLoadIndirectString() work internally? - winapi

I have been trying to get display names (user friendly name of an application) of all UWP applications installed on a system from a desktop application. I am trying to use SHLoadIndirectString() on the resource string obtained from the registry entry corresponding to those apps. Let us take the case of windows calculator.
SHLoadIndirectString() Usage
It's resource string can be obtained from HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\‌​Repository\Packages\‌​Microsoft.WindowsCal‌​culator_10.1705.1301‌​.0_x64__8wekyb3d8bbw‌​e\DisplayName registry entry.
The resource string on my system for it is #{Microsoft.WindowsCalculator_10.1705.1301.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.WindowsCalculator/Resources/AppStoreName}
To get the display name I would do SHLoadIndirectString(#{Microsoft.WindowsCalculator_10.1705.1301.0_x64__8wekyb3d8bbwe?ms-resource://Microsoft.WindowsCalculator/Resources/AppStoreName}).
Experimental observation
(Experiment 1) I used SHLoadIndirectString() for two different users (U1, and U2). U1's language was set to english, U2's language was set to french (FR-fr). When SHLoadIndirectString() was run from U1 it returned Windows Calculator, and for U2 I got Calculatrice Windows. Thus the value returned for the same resource string depends on language setting of current user.
(Experiment 2) I installed a UWP app in U2, and did SHLoadIndirectString() to the resource string in DiplayName. I got error in U1, but in U2, it gave me the required string correctly.
(Experiment 3) When I added path to resource file (resources.pri), to resource string, I got no error in U1. The resource string earlier was #{DJiT.edjing-DJmixerconsolestudio-PlayMixRecordShar_5.1.12.0_x64__3nf5xjt6s13jt?ms-resource://DJiT.edjing-DJmixerconsolestudio-PlayMixRecordShar/Resources/AppName}, which I later modified to #{C:\\Program Files\\WindowsApps\\DJiT.edjing-DJmixerconsolestudio-PlayMixRecordShar_5.1.12.0_x64__3nf5xjt6s13jt\\resources.pri?ms-resource://DJiT.edjing-DJmixerconsolestudio-PlayMixRecordShar/Resources/AppName} before passing to SHLoadIndirectString().
Finding solutions
SHLoadIndirectString()'s documentation says that its returned value depends on Shell environment or ResourceContext, without giving much details for each.
Two projects which try to re create windows NT API to run windows applications are ReactOS and wine. I looked through their source code to find implementation of SHLoadIndirectString(), but all that the codes seems to do is to do a LoadLibrary() on the resource string, after removing the # symbol at the beginning. This doesn't make any sense, why would there be such a dll in a system, as the resource string for every app is different?
SHLoadIndirectString() implementation in ReactOS
SHLoadIndirectString() implementation in wine

From Windows XP to Windows 7 SHLoadIndirectString only used LoadLibrary with the #filename.dll,resource syntax documented on MSDN. The MUIVerb entry for file type registrations is perhaps the most common usage seen in the wild.
In Windows 8 it was extended to support other sources, specifically .PRI files (Package Resource Index) used by WinRT/Modern/Store apps.
To find out exactly how it works you can single-step it in a debugger but these implementation details are not something you should depend on, you should only use the documented API.
On Windows 8 it uses various functions in MrmCoreR.dll (mrmcorer!Microsoft::Resources::Runtime::CResource*) to extract the package name, then it builds a path with KERNEL32!PackageIdFromFullName+KERNEL32!GetPackagePath+"\resources.pri" and then it calls Bcp47Langs!Windows::Internal::CLanguagesListFactory::GetUserLanguages to get a list of preferred languages. It then builds a string from the path (it converts \ to %5) so it can check if the resource string is cached under HKCU\Software\Classes\Local Settings\MrtCache. If not it reads the string with the resource manager. The exact details of how a ResourceContext works (language, DPI scale etc.) and how it finds the true source from the .pri file is probably out of scope for this question and a much larger topic in itself.

Related

Can you make a Windows desktop app in Rust and winapi?

I've got a Windows application with a GUI written in Rust and winapi. Despite its GUI, it behaves like a console application. When the exe file is started, a Command Prompt window pops up, and the application is run from it. This is not what I want; a main window should open instead, as in all real desktop apps. How can I achieve this goal in Rust with winapi?
I've investigated some options. You can develop Windows desktop applications using Tauri or gtk-rs, but both of these techniques have drawbacks when used for Windows apps. More options may be found here. I've also tried the windows-rs samples available on the internet, but they're all console apps with a graphical user interface, which isn't what I'm looking for.
I also note that C++ desktop applications use the function int APIENTRY wWinMain(...) as the entry point while console applications use int main(...), and wWinMain doesn't seem available in rust winapi.
Whether the system allocates a console for a newly created process is controlled by the Subsystem field in the Windows-specific optional PE header. The field is populated through the linker's /SUBSYSTEM command line option. The only relevant arguments for desktop applications are CONSOLE and WINDOWS. The former instructs the system to allocate a console on launch, whereas the latter won't.
You can instruct the linker to target the WINDOWS subsystem from Rust code by placing the per-module
#![windows_subsystem = "windows"]
attribute (see windows-subsystem) inside the main module of your application.
You'll find an example of this in the core_app sample of the windows crate.
This is the most convenient way to target the WINDOWS subsystem. You can also explicitly pass the linker flag along, e.g. by placing the following override into .cargo/config.toml:
[build]
rustflags = [
"-C", "link-arg=/SUBSYSTEM:WINDOWS",
]
This may or may not work, depending on the linker you happen to be using. Since the linker isn't part of the Rust toolchain, making sure that this works and has the intended effect is on you.
A note on the entry point's function name: It is irrelevant as far as the OS loader is concerned. It never even makes it into the final executable image anyway. The PE image only stores the (image-base-relative) AddressOfEntryPoint, and that symbol could have been named anything.
The concrete name is only relevant to the build tools involved in generating the respective linker input.
More info here: WinMain is just the conventional name for the Win32 process entry point. The underlying principles apply to Rust just the same, particularly the aspect that the user-defined entry point (fn main()) isn't actually the executable's entry point.

How to create a 'sandbox' with a virtualised registry for an application?

We have a 3rd party native application (written in C I believe) which we want to run multiple instances of on a machine.
however the application reads and writes from one particular registry key in order to find the location of a config file. It reads this location continuously during its running. The registry key is in HKLM. this means that if we try and run 2 different instances of the app with 2 different locations for the config file the processes tread on each others toes.
Is it possible to 'virtualise' the registry (or run each process in a sandbox) that the processes are using so that they can both think they are writing to a single location, but actually they are writing and reading from somewhere different and they won't step on each others toes?
There are several options to virtualize a program:
https://en.wikipedia.org/wiki/Portable_application_creators
Creating your own virtualization software is much more complicated and would require an entire coarse on programming and hooking library calls using the windows SDK.
However an easier option that doesn't require setting up and running additional software for each copy of the program I suggest creating multiple copies of the program and hex editing each executable.
Make as many copies of the application as you need to run, then open the application file in a hex editor and search for the name of the registry key, ie:
HKLM\System\CurrentControlSet\Control\Session Manager
Then change the last byte to a digit for each different version (1 byte, 0-9) ie:
HKLM\System\CurrentControlSet\Control\Session Manage1
HKLM\System\CurrentControlSet\Control\Session Manage2
HKLM\System\CurrentControlSet\Control\Session Manage3
For more than 10 differences (2 bytes, 00-99) use the last two bytes:
HKLM\System\CurrentControlSet\Control\Session Manag01
HKLM\System\CurrentControlSet\Control\Session Manag02
HKLM\System\CurrentControlSet\Control\Session Manag03
While the solution from Joshua will work for this particular application, it might not work for others (f.e. where the registry path is constructed in code or when the application is signed).
Therefore, I would suggest using DLL injection and intercept calls to RegOpenKey(Ex), RegCreateKey(Ex), etc. That way, you can fiddle with the registry path before passing the call down to the real Windows Advapi32.dll.
Some great articles about API hooking:
API Hooking and DLL Injection on Windows
API Hooking with MS Detours
Yes, Sandboxie can run multiple instances of an app, each in it's own "Sandbox" which it believes to be the entire universe. But you can also access the data directly through the normal ways if you need to.
So in other words, Sandboxie lets you see all the registry changes that were made in the app's operations, and you can roll them back if you like.
Yes, you can virtualize the application, this technology is called Application Virtualization.
Try http://www.cameyo.com/. Cameyo is a software used to build virtual application.
A virtual application is a single EXE file that holds an entire application including files, DLLs and registry. Virtual apps are isolated from your system and can be copied & moved from one computer to another without installation.

Identifying file type in Windows

Linux operating system identifies files by looking at its magic number at the starting of the header. How does windows do it ? Does it also have some kind of magic number mechanism or does it only rely on the file extension ?
It relies only on the extension, as provided by the filesystem; the contents of the file are not examined. See e.g. this article - it talks about Windows XP, but AFAIK the general behavior is shared by all released versions of Windows so far: http://support.microsoft.com/kb/307859

Achieving application portability within Windows and Delphi?

We have this application that does not write to the Windows registry or store its configuration files (such as an INI file) in the user's profile; instead, it stores its configuration files in the program's directory. Wikipedia has this statement
A portable application (portable app) is a computer software program designed to run independently from an operating system. This type of application is stored on a removable storage device such as a CD, USB flash drive, flash card - storing its program files, configuration information and data on the storage medium alone.
so our question is, does this make our application a true portable application (portable app)?
I should point out that if the application is on a write protected medium we use the function below, so it doesn't try to write to that medium.
function GetTempFile(): string;
var
Buffer: array[0..MAX_PATH] of Char;
begin
Windows.ZeroMemory(#Buffer, System.SizeOf(Buffer));
SysUtils.StrPCopy(Buffer, SysUtils.ExcludeTrailingBackslash(SysUtils.ExtractFilePath(System.ParamStr(0))));
Windows.GetTempFileName(Buffer, '~', 0, Buffer);
Result := string(Buffer);
end;
function IsMediumWriteProtected(): Boolean;
var
ErrorMode: Word;
hHandle: THandle;
begin
ErrorMode := Windows.SetErrorMode(SEM_FAILCRITICALERRORS);
try
hHandle := Windows.CreateFile(PChar(GetTempFile()), GENERIC_WRITE, 0, nil,
CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or FILE_FLAG_DELETE_ON_CLOSE, 0);
try
Result := (hHandle = INVALID_HANDLE_VALUE);
finally
Windows.CloseHandle(hHandle);
end;
finally
Windows.SetErrorMode(ErrorMode);
end;
end;
A truly portable application, if that is truly your question, should:
Not require any installation whatsoever.
Work anywhere you copy it to.
My own gestalt definition of a portable app does not always require that the app work when it is located on a read-only place, and other than this wikipedia reference, I am not sure that it's standard in the wild. It's great that you're trying to follow the letter of the law as spelled out in Wikipedia articles. It irks me that the quote you have given from the Wikipedia article contains a bit of an internal contradiction; if the definition both expects that a CDROM must be supported (or your app is not portable) and also specifies that the data should be stored on that media alone. You have run into that contradiction, and are now violating one part of the letter of the law, to avoid violating the first part of it. So the definition either prevents you from saving anything at all in that case, or that definition isn't in fact, a law unto itself, and you're free to do as you like, provided the two points I mention above are followed.
A minimalist definition of portable app is the one I gave above, and what you do beyond those two points, should be whatever is right for your users.
I really don't see how anyone on SO can give you a green light on portability. You can only truly verify portability by testing, and ideally testing by a group of people, such as beta test or fieldtest users. I also don't see StackOverflow as a good place for requests for help testing binaries. This is a site for asking programming questions, not for forming beta tests, field-tests etc, or for promoting your app. In fact I don't see how we could safely download a binary from you and not be risking our systems running binary code we downloaded from links here. I recommend you remove the link to the binaries.
Tests I would do:
Put it on a usb key, and run it on several different PCs on several different clean windows systems, including XP, Vista, Win 7, Windows Server 2008 at several service pack levels (no SP, Sp1, Sp2, Sp3 on XP etc), and on several different dirty systems (systems with visual studio installed, delphi installed, office installed, and various anti-virus systems).
Be sure you require no external runtime BPLs/DLLs or other runtime stuff, except those located in the same folder as your executable (side by side configuration). That way the user's path does not have to be modified for your app to work, and you don't have to worry about DLL hell confusion.
Check that no registry writes or filesystem writes outside the app folder occur in your app. SysInternals Process Monitor is great for this.
Do not use Windows.GetCurrentDirectory() to retrieve the executable file's directory. It returns the process's current working directory, which can change dynamically during the process's lifetime, so you are not guaranteed to get the correct directory every time. To get the app's directory reliably, use Sysutils.ExtractFilePath(Sysutils.ParamStr(0)) instead. Internally, this uses Windows.GetModuleFileName(nil) to get the full path of your app and then truncates off the executable name, leaving the desired directory path.

Are Extended-Length Paths safe to use?

I just stumbled on this article on MSDN that says a path can be 259 characters + NUL termination, but if you prefix it with "\\?\" WinAPI allows you to use the
maximum total path length of 32,767 characters.
Eager to see it working I tried using that prefix from Explorer (On XP SP3) but it doesn't work at all (on any path). If you put \\?\C:\Path\to\an\existing.file on explorer's bar, it will give the "file not found" error.
So I'm confused. Can I code something for (non-ancient) Windows that makes full use of the mentioned path size on NTFS? Why Explorer doesn't use it?
There is a SET of API calls that are work with extened-paths and some that do not. The MSDN usually mentions this.
Not that if you just type that path into windows explorerunder xp this does not work, because the extened path syntax is just an escape sequence for the WIn32 API and not for windows explorer. Now, In Win7 this does work because many people expected this to work.
Also for long paths, it does help if you change the working directory or open up explorer with a sub-directory as a root.
Before someone tells me to RTFM...
Note that these examples are intended for use with the Windows API functions and do not all necessarily work with Windows shell applications such as Windows Explorer.
[...]
For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to disable all string parsing and to send the string that follows it straight to the file system. For example, if the file system supports large paths and file names, you can exceed the MAX_PATH limits that are otherwise enforced by the Windows APIs.
On a secondary note, this makes me wonder about the possibilities of hiding files (or finding such files) from explorer by using illegal file names.
Are you asking why all components in Windows do not support it, or are you asking whether it's legal to use these long paths?
You can definitely safely use them, but you may irritate someone who wants to use tools like Explorer to browse them. We see paths like this all the time in the wild. Sometimes people are pretty surprised when they can't use MY_FAVORITE_TOOL to delete it...

Resources