Load 2 versions of the same DLL in the same process - c++

I want to do exactly what it is described here, but the accepted solution does not work for me. I suppose the reason is explained here :
If a DLL with dependencies is loaded
by specifying a full path, the system
searches for the DLL's dependent DLLs
as if they were loaded with just their
module names.
If a DLL with the same module name is
already loaded in memory, the system
checks only for redirection and a
manifest before resolving to the
loaded DLL, no matter which directory
it is in. The system does not search
for the DLL.
I wish to have my application in the following structure.
c:\Exe
|
|----- c:\DLL\DLL.dll, c:\DLL\common.dll
|
|----- c:\DLL2\DLL2.dll, c:\DLL2\common.dll
My EXE will load the DLLs through
LoadLibrary("c:\\DLL\\DLL.dll");
LoadLibraryEx("c:\\DLL2\\DLL2.dll");
common is implicitly loaded in both cases.
I tried the SetDllDirectory option, but there is always only one common.dll loaded.
I added version information in common.dll. c:\DLL\common.dll has version 2.0.1.0 while c:\DLL2\DLL2.dll has version 4.0.1.0
I embedded the following manifest with the corresponding version info, but it did not help.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="common" version="4.0.1.0" processorArchitecture="x86"></assemblyIdentity>
</dependentAssembly>
</dependency>
</assembly>
Is there a solution to this problem ?

Where have you embedded the manifest? The EXE or the DLLs?
You have two basic ways of doing this, both involve turning "common" into a private SxS assembly by creating a manifest for it.
Then:
If DLL and DLL2 contain manifests listing dependent assemblies, then you need to add a dependentAssembly to their manifests specifying "acme.common" (for example) as a dependent assembly. As dependent assemblies are always searched for, by default, in the loading modules folder, each DLL will load its own local copy of common.
If you are just relying on the applications default activation context to do most of the heavy lifting, then you can try using the ActivationContext API.
Call CreateActCtx twice, specifying two the two different folders as the base folder for the resulting context.
In pseudo code:
HACTCTX h1 = CreateActCtx( ... for DLL ... );
HACTCTX h2 = CreateActCtx( ... for DLL2 ...);
ActivateActCtx(h1,...);
LoadLibrary("C:\\DLL\\DLL1.DLL");
DeactivateActCtx();
ActivateActCtx(h2,...);
LoadLibrary("C:\\DLL2\\DLL2.DLL");
DeactivateActCtx...
If the dlls already contain their own manifests the system will use those. If not, this will let you specify a search directory for private assemblies without modifying the dll's themselves.
To implement Option 1:
First, I don't recommend trying to use the dll name as the assembly name. So, create a manifest that looks like this in each folder:
<!-- acme.common.manifest -->
<assembly manifestVersion="1.0">
<assemblyIdentity type="Win32" name="acme.common" version="1.0.0.0" processorArchitecture="x86"/>
<file name="common.dll"/>
</assembly>
You can fix the version number to match common.dll's version in each folder, but thats not important.
Then, either the manifest you list, or a directive like this if you are using Visual Studio
#pragma comment(linker, "/manifestdependency:\"acme.common'"\
" processorArchitecture='*' version='1.0.0.0' type='win32'\"")
Just make sure the dependent assembly versions match the versions of the corresponding 'acme.common' assembly.

Related

How to enable "Long Path Aware" behavior for setting the current directory in a C++ windows console app

In a C++ console application on windows, i'm trying to break the MAX_PATH restriction for the SetCurrentDirectoryW function.
There are many similar questions already asked but none got a usable answer:
How to enable "Long Path Aware" behavior via manifest in a C++ executable?
Are long path behavior per app can be enable via the manifest?
Doc Research
Apparently this might be possible by using application manifest files. The docs for SetCurrentDirectoryW state:
Tip Starting with Windows 10, version 1607, for the unicode version
of this function (SetCurrentDirectoryW), you can opt-in to remove the
MAX_PATH limitation. See the "Maximum Path Length Limitation" section
of Naming Files, Paths, and Namespaces for details.
And from the general docs about Manifests:
Manifests are XML files that accompany and describe side-by-side
assemblies or isolated applications.
...
Application Manifests describe isolated applications. They are used to
manage the names and versions of shared side-by-side assemblies that
the application should bind to at run time. Application manifests are
copied into the same folder as the application executable file or
included as a resource in the application's executable file.
The docs about Assembly Manifests point out the difference to Application Manifests once more:
As a resource in a DLL, the assembly is available for the private use
of the DLL. An assembly manifest cannot be included as a resource in
an EXE. An EXE file may include an Application Manifests as a resource.
The docs about Application Manifests list the assembly and assemblyIdentity elements as required:
The assembly element requires exactly one attribute:
manifestVersion
The manifestVersion attribute must be set to 1.0.
The assemblyIdentity element requires the following attributes:
type
The value must be Win32 and all in lower case
name
Use the following format for the name: Organization.Division.Name. For example Microsoft.Windows.mysampleApp.
version
Specifies the application or assembly version. Use the four-part version format: mmmmm.nnnnn.ooooo.ppppp. Each of the parts separated by periods can be 0-65535 inclusive. For more information, see Assembly Versions.
All other elements and attributes seem to be optional.
Additional requirements for the assembly element are:
Its first subelement must be a noInherit or assemblyIdentity element.
The assembly element must be in the namespace
"urn:schemas-microsoft-com:asm.v1". Child elements of the assembly
must also be in this namespace, by inheritance or by tagging.
Finally, there's the longPathAware element which is optional but which should hopefully allow SetCurrentDirectoryW to use long paths:
Enables long paths that exceed MAX_PATH in length. This element is
supported in Windows 10, version 1607, and later. For more
information, see this article.
The section in the docs shows this example xml manifest:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
...
<asmv3:application>
<asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</asmv3:windowsSettings>
</asmv3:application>
...
</assembly>
It doesn't seem to exactly follow the rules from the assembly element:
The assembly element must be in the namespace "urn:schemas-microsoft-com:asm.v1".
Child elements of the assembly must also be in this namespace, by inheritance or by tagging.
Tests
The test environment is:
Windows 10 21H2 x64 19044.1586
VS2022 17.1.1
Windows SDK Version 10.0.20348.0
The test application is a new c++ console application where i made the following change to the Additional Manifest Files:
The source code is very simple; it's also on godbolt but without the manifest:
#include <iostream>
#include <string>
#include <windows.h>
int main() {
std::wstring const path = LR"(H:\test\longPaths\manySmallLongPaths\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\12345678\)";
std::wstring const path2 = LR"(\\?\)" + path;
if (!SetCurrentDirectoryW(path.c_str())) {
printf("Exe SetCurrentDirectory failed 1 - (%d)\n", GetLastError());
if (!SetCurrentDirectoryW(path2.c_str()))
printf("Exe SetCurrentDirectory failed 2 - (%d)\n", GetLastError());
}
}
Trying to put this all together, i think the following file might be a valid Application Manifest:
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns:asmv1='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<asmv1:assemblyIdentity type='win32' name='my.test.app' version='1.0.0.0' />
<asmv1:application>
<asmv1:windowsSettings>
<asmv1:longPathAware>true</asmv1:longPathAware>
</asmv1:windowsSettings>
</asmv1:application>
</assembly>
But compiling and starting the application results in the following error:
The application has failed to start because its side-by-side
configuration is incorrect. Please see the application event log or
use the command-line sxstrace.exe tool for more detail.
Using sxstrace.exe reveals:
INFO: Parsing Manifest File C:\test\longPaths.exe.
INFO: Manifest Definition Identity is my.test.app,type="win32",version="1.0.0.0".
ERROR: Line 2: The element ws1:longPathAware appears as a child of element urn:schemas-microsoft-com:asm.v1^windowsSettings which is not supported by this version of Windows.
ERROR: Activation Context generation failed.
Maybe
Child elements of the assembly must also be in this namespace
is not entirely true (anymore) or i interpreted this wrong. Trying with a completed example from the longPathAware element:
<assembly xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv1:assemblyIdentity type='win32' name='my.test.app' version='1.0.0.0' />
<asmv3:application>
<asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
runs the application successfully, but without long path awareness (windows error code 206 = ERROR_FILENAME_EXCED_RANGE):
Exe SetCurrentDirectory failed 1 - 206
Exe SetCurrentDirectory failed 2 - 206
I checked the final embedded resource but it's definitively there:
To finish
i can only say that i don't know what else to test or if adding longPathAware element to the manifest is even possible with the type of application i'm trying to achieve that.
Maybe there's another api to change the current working folder of my application to a long path, and i would be fine with it, but at least _chdir and std::filesystem::current_path have the same limitations.
Workarounds
Using short names aka. 8.3 aliases might provide a limited work around.
For my cases this is often not feasible because short paths don't need to exists; they can be controlled system wide or per volume:
The general state can be queried with fsutil 8dot3name query
The per volume setting can be queried with fsutil behavior query disable8dot3 c:
Sidenotes
The manifest can be embedded in the executable or a dll.
It will be ignored when the dll containing it is delay loaded.
It will not be ignored by the delay loaded dll when the executable is containing it.
Because the Manifest is "additional" in the project settings, it doesn't need the assemblyIdentity element.
The manifest applies to your application, it allows you to opt in to long path support.
However, long path support must also be enabled system wide. This is the group policy "Computer Configuration > Administrative Templates > System > Filesystem > Enable Win32 long paths".
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
"LongPathsEnabled"=dword:00000001
This design makes no sense but it is what it is. You can't argue that it is in the name of compatibility because one could create long paths with \\?\ since at least Windows 2000.

C++: Load dll in sub directory - manifest

I would like to use manifest to load c++ dll in sub directory of the exe directory. For example: the folder structure like below:
LoadApp depends on LoadDll, both are native c++ projects. LoadDll.dll is in the sub directory "sdk". Run LoadApp.exe, it says "Can not find the LoadDll.dll". I have embedded the sdk.manifest fie to LoadApp.exe, but seems it does not work. Here is content of sdk.manifest:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<file name="sdk\LoadDll.dll">
</file>
</assembly>
I know using LoadLibrary or SetDllDirectory or Environment path can resolve the dll loading problem. But I would like to know what is wrong with manifest solution? Is it able to use manifest file for native c++ projects?

Assembly identity names in SxS manifests

I have two questions regarding activation context API and SxS manifests. They look like very simple, but still I cannot find any good answer for them.
I have two dll libraries with filenames foo.dll and bar.dll, both with embedded manifests, foo depends on bar. Is there any way to assign these two with assemblyIdentity name other than their filename? For example if I want assembly identity of bar.dll to be CompanyName.Subsection.bar ? If I change it's identity like this - I get error while I try to load foo.dll "Dependent Assembly CompanyName.Subsection.bar,type="win32",version="0.1.2.3" could not be found". Of course if I specify name assembly identities as filenames (<assemblyIdentity name="bar") everything works, but is there a way to use these dotted names?
foo.dll
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="foo" version="0.1.2.3" type="win32"/>
<dependency>
<dependentAssembly>
<assemblyIdentity name="CompanyName.Subsection.bar" version="0.1.2.3" type="win32" />
</dependentAssembly>
</dependency>
<file name="foo.dll">
</file>
</assembly>
bar.dll
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="CompanyName.Subsection.bar" version="0.1.2.3" type="win32"/>
<file name="bar.dll">
</file>
</assembly>
It can be done if instead of embedding the manifest for bar.dll you place it in a separate folder together with the dll itself. I.e.:
foo.dll
CompanyName.Subsection.bar/
├── bar.dll
└── CompanyName.Subsection.bar.manifest
You still need to embed the first manifest into foo.dll. I'm confident that you can remove the following snippet from that manifest, because it's an embedded manifest and therefore needs not to specify what dlls belong to the assembly:
<file name="foo.dll">
</file>
The manifest for bar.dll is good, but be sure to name the folder and the manifest file exactly as specified in the assemblyIdentity element (see example folder structure above).
Also note, that you can diagnose problems related to loading SxS assemblies with the sxstrace command line tool. Step by step instructions:
Run sxstrace trace -logfile:mytrace.etl from a Visual Studio Command Line. The command will not return until you press ENTER. However, do not press ENTER yet.
Start the application that loads foo.dll to reproduce your issue
Then press ENTER in the Command Line window to stop sxstrace
You now have a file mytrace.etl, which is not human readable. Convert it to a readable format by running sxstrace parse -logfile:mytrace.etl -outfile:mytrace.txt. You get a file mytrace.txt which shows what happens, when you load foo.dll and where it fails to resolve some of its dependencies.

How does Windows 7 determine, if you need admin rights? (vc60)

Problem: I'm writing unittests for a Setup program in c++. The Setup needs admin rights, but the unittests do not. On starting the tests, I get asked for starting the test/program as Administrator.
This started happening when I included the rc-file of the original project.
Edit: I was upgrading this project to VS2010, but still using vc60, so there is no manifest support. This might be related.
Generally, the resources contain a manifest, and a portion of the manifest states whether the program needs administrator access. Since this is a setup utility, it probably has a manifest requiring administrator access and your tests picked it up when it tried to share the resource file.
(Other people are talking about the heuristics used to guess whether a program needs administrator access when it doesn't have a manifest. For example, if you have an old program that was made before manifest files were common, the OS might try to guess whether it's an installer by looking for certain phrases like "setup" in the file name. I believe there are other heuristics as well. This is a hack for older programs that were written before manifests made it possible to explicitly declare your need for administrator access.)
You can fix your problem by providing your own manifest. The easiest way is to use the /MANIFESTUAC linker option.
If you're using an older toolchain, you'll probably have to make the manifest file yourself and include it in your resources.
The manifest is a chunk of XML. The important bit for you would look something like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" name="yourprogram.exe" type="win32"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
To embed this in your resources, your .RC file should have:
1 RT_MANIFEST <filename>
If your SDK is so old that RT_MANIFEST is not defined, you can define it yourself:
#define RT_MANIFEST 24
One criteria for evaluating the needed rights for an application is the name.
So if the name contains the Word "Setup", you will be asked for admin rights.
Also, as this started with the rc file, another criteria are the names written in the rc-File, under "Version". In my case in "VS_VERSION_INFO".
The following entries are should not contain the word Setup:
FileDescription
InternalName
OriginalFilename
ProductName
You might change any occurance of "Setup" to "Settup". That would prevent the dialog.
Even though, you might not be able to change this in the "real" rc-file.

Specify version in "Assembly manifest" of private side by side assembly (c++)

I am creating a private side by side assembly and need to generate an Assembly manifest for that dll. The problem is that I don't know how I can specify a version to this manifest. (The version of dll/build is #defined in a header file)
I need to generate a manifest file like the one shown below. Present I uses a static manifest file with below content and specify it in project settings->Manifest Tool->Input and output->Additional Manifest Files. The only problem with this approach is that, I can't change the version.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="MyCompany.MyDll" version="1.0.0.0"
processorArchitecture="x86" ></assemblyIdentity>
</assembly>
Please advice, what is the correct procedure to create assembly file for private side by side dll.