How to solve: Custom MSBuild task requires assembly outside of AppBase - msbuild

I have a custom Task that I want to execute when building my C# projects. This task is located in MyTask.dll, which references another assembly, MyCommon.DLL.
The problem is that MyCommon.dll is located at "..\Common\MyCommon.dll" relative to MyTask.dll, which puts it outside the AppBase dir for MSBuild process. I've confirmed that this is indeed the problem by analyzing MSBuild's log and seeing Fusion's report about the binding failure.
What can I do to make Fusion find MyCommon.dll during the build process? Note that moving the assembly would break my app, which also depends on it.
UPDATE: Well, it seems I'll go with using a copy afterall. Other solutions all require system-wide modifications, which isn't really warranted here.

So copy it instead? Just a thought. Have a copy there just to support the build that you delete once you're done with it.

I see multiple solutions :
1st : Add the assembly in the GAC (your assembly must have a strong name)
gacutil /I <assembly name>
2nd : Locate the assembly through Codebases or Probing, in your machine.config file or in msbuild.exe.config .
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="MyCommon"
publicKeyToken="32ab4ba45e0a69a1"
culture="neutral" />
<codeBase version="2.0.0.0"
href="file://C:/yourpath/MyCommon.DLL"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
3rd : copy the assembly in the same directory before and delete it after, like David M said.

All of these "solutions" create more dependencies which complicate the environment. There should be an easier way to update the probing path at runtime..
Specifically MSBuild should allow you to add probing paths in your .proj file, or to specify the dependant dlls
You can define a custom UsingTask:
<UsingTask TaskName="Task" AssemblyFile="Assembly.dll" />
but you cant add dependencies? it should be included... here with something like
<UsingTask TaskName="Task" AssemblyFile="Assembly.dll">
<DependantAssembly AssemblyFile="dependant.dll"/>
</UsingTask>
But, no this isn't supported...

An option is to use ILMerge to merge the dependency into the task assembly.

Related

Load assembly from folder and specific version

In a VB.net solution I need to load some dll from a specific folder. I have read
Could not load an assembly from specific folder and many others but in my case no luck. I have tried to add in app.config lines to reference to the file I need :
<dependentAssembly>
<assemblyIdentity name="Eplan.EplApi.Starteru" publicKeyToken="57aaa27e22f7b107" />
<publisherPolicy apply="yes" />
<codeBase version="1.0.0.0" href="C:\Program Files\EPLAN\Platform\2023.0.3\Bin\Eplan.EplApi.Starteru.dll"/>
</dependentAssembly>
But when I try to start my solution I have a System.InvalidOperationException without any other message. I have also tried this solution https://social.technet.microsoft.com/wiki/contents/articles/53408.vb-net-dynamically-load-assemblies.aspx without luck.Any idea for solving?

Azure shareclient works in one project and throws exception in another

I have a vb.net solution with a separate project containing a file class to access Azure files and 2 projects. In both I call the file class which contains this code
Dim mascThis as ShareClient
... mascThis is initialized ...
If mascThis.Exists.Value then ... do something ..
In one project this works, in the other I get the message
Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
I've seen this problem discussed before (assembly issues), but all projects are in the same solution in to which I added Azure.Storage.Files.Shares 12.8.0 via Nuget and only the "File class" project has a reference to it, the other projects don't.
So what am I missing here?
Please check if any of the below is your case.
Sometimes, we will have the situation where different parts(projects) of our solution depend on different versions of the same DLL i.e; assemblies with the same assembly name.
The error
Could not load file or assembly
'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its
dependencies. The located assembly's manifest definition does not
match the assembly reference. (Exception from HRESULT: 0x80131040)
suggests , your project is looking for assembly version 4.0.4.1
Work arounds
i. Usually Nuget Package 4.5.3 contains assembly version 4.0.4.1.
Please check if both the projects in your solution can be worked on that assembly version and add that particular version as new version(ex:4.0.4.1) through binding redirect in the project where you are getting error. And keep the old version in place of old version.
Also Right click on the package reference and set 'specific version' to false under its properties
Example:
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" />
</dependentAssembly>
In this example ,this way of adding specifies that the runtime should use version 4.0.6.0 in the Assembly versions between olderversion range 0.0.0.0-4.0.6.0
Else If above is not the case, the solution might need different versions.
ii.
Right click in the project properties and choose the ApplicationConfiguration file and App.config
Add the following settings in the app.config file.
To configure multiple assemblies with the same name through codeBases.
Sample code for some package say “A” :
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="A " publicKeyToken="3d67ed1f87d44c89" />
<codeBase version="3.0" href="...\A.dll"/>
<codeBase version="5.0" href="...\A.dll"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
iii. See if you could solve that problem by using extern alias.
iv. Binding redirects are added if your app or its components reference more than one version of the same assembly .See Enable or disable autogenerated binding redirects | Microsoft Docs .If you do it manually ,you need to disable under project properties.
Manually you can add <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> in csproj file in property group.
Ex:
<PropertyGroup>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>
References that can be helpful.
SO reference
Referencing Multiple Versions
load same assembly of different version-SO

How to use app.config file with a C++/CLI to configure assembly binding in VS 2012?

I am currently using the Microsoft BCL Async library (here) across a project with a large number of interdependent assemblies all compiled against .NET 4 Full Profile, I have had to use assembly binding redirect in each project to get it to compile (as per issue 2 here).
The problem I now have is that I consume one of these libraries from a C++/CLR DLL project, it does not actually need to use async but I have the following compilation error:
2>C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets(1578,5): warning MSB3268: The primary reference "ImInterface.dll" could not be resolved because it has an indirect dependency on the framework assembly "System.Runtime, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" which could not be resolved in the currently targeted framework. ".NETFramework,Version=v4.0". To resolve this problem, either remove the reference "ImInterface.dll" or retarget your application to a framework version which contains "System.Runtime, Version=1.5.11.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
I have added the same app.config file that I have used in all of the C# projects but it does not seem to be having any effect in the C++ project. Do I have to place it in a specific directory or perform any other steps to enable the app.config file to be recognised?
The content of my app.config file is:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.5.11.0" newVersion="2.5.11.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.5.11.0" newVersion="2.5.11.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Any suggestions are appreciated!
Regards,
Anthony
I had the same problem and as it turned out, <SpecificVersion>True</SpecificVersion> helped.
Before you continue reading, note that the problem seems to only happen when the C++/CLI project is built using the "Visual Studio 2012 - Windows XP (v110_xp)" platform toolset! That is if you don't need to create a DLL which is compatible with WinXP, switch to "Visual Studio 2012 (v110)" instead and you should be fine.
Let me elaborate my scenario:
I have a C++/CLI console application project (ConsoleAppCLI) targeted for WinXP (v110_xp). ConsoleAppCLI references a library (AsyncLib) which has Microsoft.Bcl.Async installed via NuGet:
[ConsoleAppCLI.vcxproj] (C++/CLI, .NET 4.0 v110_xp)
|
|
[AsyncLib.csproj] (C#, .NET 4.0)
|
|
[Microsoft.Bcl.Async] (via NuGet)
I had the exact same problem and the app.config didn't help either, but what did help was the following:
Unload ConsoleAppCli.vcxproj
Edit the vcxproj File directly
Find the reference to the library which has the Async nuget package installed (in my case, a <ProjectReference> to AsyncLib.csproj)
Between <ProjectReference> and </ProjectReference>, ADD THE FOLLOWING: <SpecificVersion>True</SpecificVersion>
Note however, that ConsoleAppCLI was my top-level project in the dependency tree. If you have Assemblies which depend upon the C++/CLI assembly which produced MSB3268, you likewise have to add <SpecificVersion>True</SpecificVersion> to the reference (e.g. if i had an additional C# library which has ConsoleAppCli as dependency, i would have to make the reference to ConsoleAppCli <SpecificVersion>True</SpecificVersion> as well).
This solution is coming from over here, by the way.

Specifying a DLL reference

I'm having trouble setting the path to a DLL that is not in the same directory as the executable.
I have a reference to dllA.dll. At present, everything is just copied into the same directory and all is well; however, I need to move the executable to another directory while still referencing the DLL in the original directory.
So, it's setup like:
C:\Original\Dir
program.exe
dllA.dll
dllB.dll
dllC.dll
But I need to have it setup like:
C:\New\Dir
program.exe
dllB.dll
dllC.dl
Such that it is still able to reference dllA.dll in C:\Original\dir
I tried the following, but to no avail:
Set the "Copy Local" value to false for dllA.dll because I want it to be referenced in its original location.
Under "Tools > Options > Projects and Solutions > VC++ Directories" I have added the path to "C:\Original\Dir"
Added "C:\Original\Dir" to both the PATH and LIB environment variables
At runtime, it informs me that it cannot locate dllA.dll Maybe the above steps I took only matter at compile time?
I was able to find this
C# : Specifying a location for Dll reference
But I was thinking that my above method should've worked.
Any ideas?
Your compile-time settings won't affect the run-time path. Try adding C:\Original\dir
to the system-wide path, and you should see that it picks up the DLL correctly. If so, then your solutions appear to be:
1) modify the system path permanently. May or may not be feasible.
2) alter the environment path at run-time.
3) use relative paths when referring to the DLL.
4) record the path to the DLL at installation time, perhaps in the registry, so that your exe can load it explicitly.
While I may still foolishly believe this can be accomplished through specifying the proper path variables, I was able to overcome this issue by adding some entries to my app.config
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="dllA" publicKeyToken="blah" culture="neutral" />
<codeBase version="blah" href="file:///C:/Original/Dir/dllA.dll" />
</dependentAssembly>
</runtime>
Working from the above answer (Thanks Karl), it wasn't clear to me about the syntax for href, especially for relative paths:
<dependentAssembly>
<assemblyIdentity name="SVDInterface" culture="neutral" />
<codeBase version="1.0.3114.29282" href="./System/SVD/SVDInterface.dll" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Math Functions" culture="neutral" />
<codeBase version="1" href="./System/SVD/Math Functions.dll" />
</dependentAssembly>
The version number for the 1st dll I got from the references tab.
Having looked into lots of ways of specifying where a dll is, this was by far the easiest way - still took me all day to get it to work fully.
The last nugget of information is that the App.config entries above get written out to yourappname.exe.config which you must copy with your app to its final destination otherwise it will never find your dlls.
Hopefully of use to someone.
Kristian

Error adding policy file to GAC

I'm trying to add a publisher policy file to the gac as per this thread but I'm having problems when I try and add the file on my test server.
I get "A module specified in the manifest of assembly 'policy.3.0.assemblyname.dll' could not be found"
My policy file looks like this:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="*assemblyname*"
publicKeyToken="7a19eec6f55e2f84"
culture="neutral" />
<bindingRedirect oldVersion="3.0.0.0"
newVersion="3.0.0.1"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Please help!
Thanks
Ben
I've recreated the problem from scratch with a new assembly that has no dependancies (apart from the defaults) itself - all works fine on my local development machine (and redirects fine too) but gives the same error adding the policy file to the GAC on the server!
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="TestAsm"
publicKeyToken="5f55456fdcc9b528"
culture="neutral" />
<bindingRedirect oldVersion="3.0.0.0"
newVersion="3.0.0.1"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
linked in the following way
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\al.exe /link:PublisherPolicy.xml /out:policy.3.0.TestAsm.dll /keyfile:..\..\key.snk /version:3.0.0.0
pause
Please help!
Wow - ok got it.
I should have paid more attention to exactly what this meant
(MSDN) How to: Create a Publisher Policy
Important Note: The publisher policy
assembly cannot be added to the global
assembly cache unless the original
publisher policy file is located in
the same directory as the assembly .
That requirement is, frankly, so bizarre that it didn't register. The original policy file, that was compiled into the assembly i'm trying to add to the gac, has to be in the same folder as the policy assembly as you add the policy assembly.
Ok...just want to check some basics....
You definitely have got both versions of the dependent assembly installed to GAC?
And have you verified that the version numbers in the [assembly: AssemblyVersion()] attribute are correct.
And you did use [assembly: AssemblyVersion()] and NOT [assembly: AssemblyFileVersion("1.0.0.1")].
Update: My mistake, you only need the latest version of the assembly in the GAC. I just tried that here and it works. My only other thoughts are to check that the public key tokens are the same and that you've not misspelled the assembly name.
Also when you generate the policy file make sure you use the /version switch in the assembly linker to explicitly set the version number to 3.0.0.0 AND don't specify the /platform switch. e.g.
al.exe /link:assembly.config /out:policy.3.0.assembly.dll
/keyfile:mykey.snk /version:3.0.0.0
To add policy assemblies to the GAC using Wise, you do the same thing as you do to add the assembly the policy is for. So you add the policy assembly to the "Global Assembly Cache" in Wise, and as long as you have the policy file (.config) in the same location on the machine, Wise will automatically add it to GAC as well.