Obtaining a list of resolved reference paths in msbuild - msbuild

I'm trying to use ILMerge to merge multiple assemblies into one plugin assembly, which will be dynamically loaded by another application.
Using the community tasks, I've so far got:
<Target Name="CombineAssemblies" AfterTargets="MvcBuildViews">
<ItemGroup>
<MergePaths Include="#(_ResolvedProjectReferencePaths)" />
<!--<MergePaths Include="%(Reference.ResolvedPath)" /> This doesn't work! -->
</ItemGroup>
<ILMerge
DebugInfo="true"
LogFile="log.txt"
InputAssemblies="#(MergePaths)"
OutputFile=""$(TargetPath)""
CopyAttributes="true"
TargetPlatformVersion="v4"/>
</Target>
Using #(_ResolvedProjectReferencePaths) works fine for the project references, and their paths are passesd correctly through to the ILMerge task.
When running this through MSBuild, ILMerge.exe errors out. Looking at the log file, I get:
Unresolved assembly reference not allowed: System.Web.Mvc.
at System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
at System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)
at System.Compiler.Ir2md.VisitReferencedType(TypeNode type)
at System.Compiler.Ir2md.VisitMethod(Method method)
at System.Compiler.Ir2md.VisitClass(Class Class)
at System.Compiler.Ir2md.VisitModule(Module module)
at System.Compiler.Ir2md.SetupMetadataWriter(String debugSymbolsLocation)
at System.Compiler.Ir2md.WritePE(Module module, String debugSymbolsLocation, BinaryWriter writer)
at System.Compiler.Writer.WritePE(String location, Boolean writeDebugSymbols, Module module, Boolean delaySign, String keyFileName, String keyName)
at System.Compiler.Writer.WritePE(CompilerParameters compilerParameters, Module module)
at ILMerging.ILMerge.Merge()
at ILMerging.ILMerge.Main(String[] args)
I'm referencing the MVC libraries, which from the log file I assume could not be resolved by ILMerge, even though they are resolved correctly in the actual compilation process.
I therefore want to pass the location of these libraries through to ILMerge, ideally using the References item so I don't have to manually add a bunch of reference paths which would have to change if I added or removed references. Is there some equivalent to #(_ResolvedProjectReferencePaths) for normal references?
Thanks!

Try #(ReferencePath), which should be used after target ResolveAssemblyReferences.

Related

Registering .net assembly for COM succeeds with regasm but fails using RegistrationServices.RegisterAssembly

This is one of the strangest issue I have encountered.
There is a .net assembly, which is exposed to COM.
If you register it with regasm /codebase my.dll - it is sucessfully registered, and can be used.
However, if you register it from code using RegistrationServices.RegisterAssembly() :
[...]
RegistrationServices regSvcs = new RegistrationServices();
Assembly assembly = Assembly.LoadFrom(path);
// must call this before overriding registry hives to prevent binding failures on exported types during RegisterAssembly
assembly.GetExportedTypes();
using (RegistryHarvester registryHarvester = new RegistryHarvester(true))
{
// ******** this throws *********
regSvcs.RegisterAssembly(assembly, AssemblyRegistrationFlags.SetCodeBase);
}
Then it throws exception:
Could not load file or assembly 'Infragistics2.Win.UltraWinTree.v9.2, Version=9.2.20092.2083,
Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb' or one of its dependencies.
Provider type not defined. (Exception from HRESULT: 0x80090017)
This error has very little resource on the net, and looks like related to some security(?) cryptography(?) feature.
After long-long hours, I figured out what causes this (but don't know why):
If there is a public class with a public constructor in the assembly with a parameter UltraTree (from the referenced assembly 'Infragistics2.Win.UltraWinTree.v9.2'), then you cannot register from code, but with regasm only.
When I changed the have a public function Init(UltraTree tree), then it works, I can register from code. So:
// regasm: OK / RegistrationServices.RegisterAssembly(): exception
public class Foo
{
public Foo(UltraWinTree tree) { .. }
}
Foo foo = new Foo(_tree);
-------------- vs --------------
// regasm: OK / RegistrationServices.RegisterAssembly(): OK
public class Foo
{
public Foo() {}
public void Init(UltraWinTree tree) { .. }
}
Foo foo = new Foo();
foo.Init(_tree);
So I could workaround by passing UltraWinTree in a new Init() function instead of constructor, but this is not nice, and I want to know the reason, what the heck is going on?
Anyone has any idea? Thanks.
PS:
Okay, but why we want to register from code? As we use Wix to create installer, which uses heat.exe to harvest registry entries (which are added during asm registration), so heat.exe does assembly registration from code.
I've been dealing with this for years so this is the only answer you need to read:
Heat calls regasm /regfile. So does InstallShield when you tell it to. If you read this page:
https://learn.microsoft.com/en-us/dotnet/framework/tools/regasm-exe-assembly-registration-tool
There's a very important caveat in the remarks section.
You can use the /regfile option to generate a .reg file that contains
the registry entries instead of making the changes directly to the
registry. You can update the registry on a computer by importing the
.reg file with the Registry Editor tool (Regedit.exe). The .reg file
does not contain any registry updates that can be made by user-defined
register functions. The /regfile option only emits registry entries
for managed classes. This option does not emit entries for TypeLibIDs
or InterfaceIDs.
So what to do? Use Heat to generate most of the metadata. Then on a clean machine, (snapshot VM best) us a registry snapshot and compare tool such as InCntrl3 or InstallWatch Pro and sniff out what additional meta regasm writes to the registry. Finally massage that into your Wxs code.
Then on a cleam machine test the install. The result should work and not require any custom actions in the install.

Deleted database & migration file, can't make new migrations nor revert old migrations, what now?

My workflow with my ASP.NET Core app: Whenever I make changes that affect the database, I revert the old migration, make a new migration, apply it. Because this is still early development I think it makes sense to not pollute the project with lots of migration files. Usually I call this temporary migration file Initial, hoping that one of them will actually become the initial migration file.
As a result, I typically have 3 files in my Data/Migrations subfolder:
00000000000000_CreateIdentitySchema.cs that was provided for me a long time ago by the project template;
somenumbers_Initial.cs;
ApplicationDbContextModelSnapshot.cs
So the commands I would type in this workflow would look like this, if I remember correctly:
Update-Database CreateIdentitySchema
Remove-Migration
Add-Migration Initial
Update-Database
This time I made a mistake... I don't remember what exactly happened, I typed something incorrect instead of the above commands and then I was silly enough that (a) manually removing the Initial migration file and (b) Dropping the database would be enough to restart the test db from scratch.
I was wrong.
My hopes were that Add-Migration Initial would create a valid migration file corresponding to the current state of the models and a subsequent Update-Database would recreate the db. Instead this is what happens:
PM> Add-Migration initial
Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.0.0 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
The name 'initial' is used by an existing migration.
??? Really? Well let's try to remove this migration:
Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.0.0 initialized 'ApplicationDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer' with options: None
No file named '20190727172711_initial.cs' was found. You must manually remove the migration class 'initial'.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.Literal(String value)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GeneratePropertyAnnotations(IProperty property, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateProperty(String builderName, IProperty property, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateProperties(String builderName, IEnumerable`1 properties, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateEntityType(String builderName, IEntityType entityType, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateEntityTypes(String builderName, IReadOnlyList`1 entityTypes, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.Generate(String builderName, IModel model, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGenerator.GenerateSnapshot(String modelSnapshotNamespace, Type contextType, String modelSnapshotName, IModel model)
at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.RemoveMigration(String projectDir, String rootNamespace, Boolean force, String language)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.RemoveMigration(String contextType, Boolean force)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.RemoveMigrationImpl(String contextType, Boolean force)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.RemoveMigration.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Object reference not set to an instance of an object.
???? I must manually remove the migration class ?? Where is this migration class?
Let's do some text searching in the project dir... The only interesting file that shows up is myprojectname.csproj where I have stuff like this:
<Compile Remove="Data\Migrations\20190412182930_initial.cs" />
<Compile Remove="Data\Migrations\20190412182930_initial.Designer.cs" />
<Compile Remove="Data\Migrations\20190628205310_initial.cs" />
<Compile Remove="Data\Migrations\20190628205310_initial.Designer.cs" />
<Compile Remove="Data\Migrations\20190720205411_initial.cs" />
<Compile Remove="Data\Migrations\20190720205411_initial.Designer.cs" />
<Compile Remove="Data\Migrations\20190720205837_initial.cs" />
<Compile Remove="Data\Migrations\20190720205837_initial.Designer.cs" />
I don't think this is relevant here (the numbers strings do not match).
I looked into ApplicationDbCOntextModelSnapshot but there seems to be no such class called initial there...
What to do now? How to proceed?
If you dropped the database then it's easy. Delete the migrations folder.
Then run
add-migration Initial
update-database
Also, I tend to remove any <Compile Remove="" /> from the project file.

Use a C++/CLI DLL using afxwinforms.h as Reference of another C++/CLI DLL also using afxwinforms.h

In Visual C++ 2010 i added a reference from a C++/CLI DLL (ControlWrapper.dll) to another C++/CLI DLL (CliLibrary.dll).
Both are including afxwinforms.h in the stdafx.h.
When i try to compile i get these errors:
error C2011: 'Microsoft::VisualC::MFC::CWin32Window' : 'class' type redefinition
error C2011: 'Microsoft::VisualC::MFC::CWinFormsEventsHelper' : 'class' type redefinition
If i turn of the Option Reference Assembly Output and add #using "CliLibrary.dll" to the using .cpp File i get the following warnings:
1>ControlWrapper.dll : warning C4944: 'CWin32Window' : cannot import symbol from 'c:\dev\trunk\CliLibrary.dll': as 'Microsoft::VisualC::MFC::CWin32Window' already exists in the current scope
1> C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\include\afxwinforms.h(83) : see declaration of 'Microsoft::VisualC::MFC::CWin32Window'
1> This diagnostic occurred while importing type 'Microsoft.VisualC.MFC.CWin32Window' from assembly 'CliLibrary, Version=1.0.4843.17337, Culture=neutral, PublicKeyToken=null'.
1>ControlWrapper.dll : warning C4944: 'CWinFormsEventsHelper' : cannot import symbol from 'c:\dev\sfirm\trunk\sfclrlib\debug\sfclrlib.dll': as 'Microsoft::VisualC::MFC::CWinFormsEventsHelper' already exists in the current scope
1> C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\include\afxwinforms.h(122) : see declaration of 'Microsoft::VisualC::MFC::CWinFormsEventsHelper'
1> This diagnostic occurred while importing type 'Microsoft.VisualC.MFC.CWinFormsEventsHelper' from assembly 'CliLibrary, Version=1.0.4843.17337, Culture=neutral, PublicKeyToken=null'.
How could i solve the error?
Well, this is a painful problem. It certainly explains why you are the first programmer I ever encountered that actually uses this. The problem is caused by this declaration in afxwinforms.h:
public ref class CWin32Window : public System::Windows::Forms::IWin32Window
// etc..
The public keyword is the killer, that adds the class to the manifest of your assembly. So when you reference it in another project that also includes the header then there are two definitions of the class. The mix of both native and managed classes in that header prevents a clean solution.
I think you already found the best solution, using the #include, with #pragma comment(disable:4944) to shutup the compiler. Including the header inside a namespace might be another viable hack, it renames the namespace of CWin32Window, but I'd expect trouble when linking mfcm90.lib. Restructuring your solution and keeping all winforms code inside one project is the only thing I can recommend.
Are you using the types in afxwinforms.h?
If not (like in my case), the solution is to comment the include and add this two lines:
#using <System.Windows.Forms.dll>
#using <System.Drawing.dll>
or add a reference to that two assemblies in your Project.

How do I pass MSBuild arguments from the build definition to a MSBuild workflow Activity

I've defined an MSBuild activity in a TFS Workflow template, but currently I have to hard code the 'property' command line arguments directly into the template.
I'd like to be able to specify the arguments in the build definition, via the advanced setting, 'MSBuild Arguments'
I can see that I may have to build up the command line with string replace/concat, as mentioned here, but I can't see what I need to put, maybe something like this:
This is what the default MsBuild task uses:
String.Format("/p:SkipInvalidConfigurations=true {0}", MSBuildArguments)
You can change the MSBuildArguments variable in the build process template in multiple steps. For example, I added a Run Architecture Validation property to the process template and then edited the workflow to simply append /ValidateArchitecture=true to the MSBuildArguments before they're being passed to the MsBuild activity.
<If Condition="[PerformArchitectureValidation]" DisplayName="Configure Architecture Validation MSBuild Arguments">
<If.Then>
<Assign>
<Assign.To>
<OutArgument x:TypeArguments="x:String">[MSBuildArguments]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:String">[MSBuildArguments + " /p:ValidateArchitecture=true"]</InArgument>
</Assign.Value>
</Assign>
</If.Then>
</If>
The PerformArchitectureValidation variable is defined as a Property on the Build Process Template level of type Boolean.
Update: Wrote a blogpost that explains this with steps and screenshots

ilmerge problem with System.Web.Mvc

I have a build script which builds and merges 5 projects into one dll. One of the projects has a reference to System.Web.Mvc DLL which is residing within my Dependencies folder. I copied this file from the ASP.NET MVC3 folder into my project folder.
Here is the error after I run ILMerge.
Merging Primary Assembly
An exception occurred during merging:
Unresolved assembly reference not allowed: System.Web.Mvc.
at System.Compiler.Ir2md.GetAssemblyRefIndex(AssemblyNode assembly)
at System.Compiler.Ir2md.GetTypeRefIndex(TypeNode type)
at System.Compiler.Ir2md.WriteTypeDefOrRefEncoded(BinaryWriter target, TypeNode type)
at System.Compiler.Ir2md.WriteTypeSignature(BinaryWriter target, TypeNode type, Boolean instantiateGenericTypes)
at System.Compiler.Ir2md.WriteTypeSignature(BinaryWriter target, TypeNode type, Boolean instantiateGenericTypes)
at System.Compiler.Ir2md.GetBlobIndex(TypeNode type)
at System.Compiler.Ir2md.GetTypeSpecIndex(TypeNode type)
at System.Compiler.Ir2md.VisitReferencedType(TypeNode type)
at System.Compiler.Ir2md.VisitMethod(Method method)
at System.Compiler.Ir2md.VisitClass(Class Class)
at System.Compiler.Ir2md.VisitModule(Module module)
at System.Compiler.Ir2md.SetupMetadataWriter(String debugSymbolsLocation)
at System.Compiler.Ir2md.WritePE(Module module, String debugSymbolsLocation, BinaryWriter writer)
at System.Compiler.Writer.WritePE(String location, Boolean writeDebugSymbols, Module module, Boolean delaySign, String keyFileName, String keyName)
at System.Compiler.Writer.WritePE(CompilerParameters compilerParameters, Module module)
at ILMerging.ILMerge.Merge()
at ILMerging.ILMerge.Main(String[] args)
Rereferencing Merged Assembly
Any ideas why I am getting this?
Or you could use ILMarge /lib option in your script like:
ILMerge.exe
/lib:C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/lib:"c:\Program Files (x86)\Microsoft
ASP.NET\ASP.NET MVC 3\Assemblies"