TFS Build server and COM references - does this work? - com

On my Developer PC i have registered the according dll´s and referenced them. (Add reference -> COM)
On the TFS build server this of course causes errors since the references are not registered there and wont be in the future.
What is the best way to circumvent this?

Using tlbimp.exe directly is not necessary. Try replacing any <COMReference> items in the project file with <COMFileReference>. An example would look like this:
<ItemGroup>
<COMFileReference Include="MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>
The COM dll doesn't need to be registered on the machine for this to work.
Each COMFileReference item can also have a WrapperTool attribute but the default seems to work fine. The EmbedInteropTypes attribute is not documented as being applicable to COMFileReference, but it seems to work as intended.
See https://learn.microsoft.com/en-ca/visualstudio/msbuild/common-msbuild-project-items#comfilereference for a little more detail. This MSBuild item has been available since .NET 3.5.

OK, the answer was more easy than one might think.
Just use "tlbimp.exe" to create an according intrerop.dll which you include in your project and reference to this dll instead to the COM objects.
You may save the time using tlbimp.exe because Visual Studio creates this dll automatically when you reference a COM object.
But it does not upload it to TFS. So finally I just used the automatically created dll and the TFS build Server was happy.
One may find the automatically created Interop dll in: "ProjectFolder\obj\x86\Development"

For anyone who's having trouble with namespace:
tl;dr: use tlbImp /namespace: YOUR_NAMESPACE to give your dll a namespace
This solution worked for Azure Pipeline too. But at first I was having trouble with COMFileReference. When I replaced COMReference with COMFileReference my VS build was failing saying "Namespace can't be found". Finally found the solution here.
I used tlbImp to generate a dll of the COM file using /namespace attribute following this article. Only then you can use the dll in COMFileReference. Here's a bit elaborate example:
<ItemGroup>
<COMFileReference Include="My/Folder/MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>
or
<ItemGroup>
<COMFileReference Include="..\My\parent\folder\MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>

Related

How does msbuild resolve assembly from a multi-framework nuget package?

I have a solution with a console app ConsoleApp-net461 that references a library ClassLibrary-net452.
Both projects reference a multi-framework nuget package NuGetPackage-net452-net461.
When I build, I end up with the nuget package's net461 dll in the output folder. At runtime, I am getting errors like:
System.TypeLoadException: Inheritance security rules violated by type: 'ClassA'.
Derived types must either match the security accessibility of the base type or be less accessible.
I am suspecting this is because my ClassLibrary-net452 ends up having a dependency on the nuget package dll with the net461 framework.
Is this normal behavior? What are the best ways to ensure the net452 dll is resolved from the nuget package? BindingRedirects don't seem to let you choose a target framework.
Not 100% sure this will work, but might be worth trying:
(Relies on using SDK csproj format, this link might work for packages.config, but I know less about the area.)
It looks like what you want is something like this. However the dotnet/SDK team haven't got round to implementing it yet.
The suggested work around is:
- Grab the dll path from your package reference
- Tell the package reference to not copy compile assets (dlls).
- Use the dll path to generate a similar path for a non-package reference that redirects to the Net452 version of the package.
Explanatory links here and here.
Summary is that your csproj should contain something like this:
<ItemGroup>
<PackageReference Include="*MyPackageName*" ExcludeAssets="Compile" GeneratePathProperty="true">
<Version>x.y.z</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="*NameYouWantToGiveThisReference(suggest dll name)*">
<HintPath>$(Pkg*MyPackageName*)lib\net452\*DLLname*.dll</HintPath>
</Reference>
Everything surrounded by ** needs to be named according to whatever your projects are actually called.
The two MyPackageNames must match up (with the caveat that all "."s in the first one should be replaced with "_" in the second one).

Accessing InstallShield Task in MSBuild

I'm trying to use MSBuild to create a target that will create an installer with InstallShield 2012. I'm having difficultly understanding how to access InstallShield. I ran across this mentioning an InstallShield task but I'm not sure how to get access to it. I think I need a UsingTask directive, but not sure what to import. Can someone give me a pointer on how to get this going? Thanks.
You need to import the targets file. Took me a while to figure that out as well since it's in the msbuild extensions directory together with a dll. Here's a basic sample of how to use it (note this is for 2012Spring but you get the idea):
<Import Project="$(MSBuildExtensionsPath32)\InstallShield\2012Spring\InstallShield.targets" />
<Target Name="BuildInstaller">
<InstallShield.Tasks.InstallShield
Project="/path/to/my.ism"
ProductConfiguration="Package"
ReleaseConfiguration="MSI" />
</Target>
Btw if this doesn't work out for some reason, you can always invoke ISCmdBld.exe in an Exec task, it will do the job just as fine.
InstallShield provides Visual Studio integration. When you create an InstallShield project in VS it creates a project file (.ISPROJ) which imports the InstallShield.targets file for that version of InstallShield. The project file contains plenty of examples on how to build a particular configuration and pass in such things as merge module path, properties, path variable overrides and so on.
Please note that building InstallShield requires the x86 MSBuild platform due to COM components.

MSBuild UsingTask and resolving references

I am using NuGet to pull in YUICompressor.NET.MSBuild, which in turn pulls in YUICompressor.NET and EcmaScript.Net.
It also adds references to all three DLLs in my web project.
I can then add the UsingTask reference like this:
<UsingTask TaskName="CssCompressorTask" AssemblyFile="$(SolutionDir)\packages\YUICompressor.NET.MSBuild.2.3.0.0\lib\NET20\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
And then use the task to compress my Javascript/CSS in an AfterBuild target
However, I don't want to have to reference these DLLs in my web project (as they are not actually part of what I ship)... but when I remove the references, then CssCompressorTask can no longer find the other two dependent DLLs (they are in different folders off the /packages folder - it works ok if they are alongside Yahoo.Yui.Compressor.Build.MsBuild.dll)
Believe it or not, I have spent hours trying to get the dependencies to resolve:
First I tried to modify AssemblySearchPaths to include all the other /packages subfolders, as here:
https://github.com/BenPhegan/NuGetAutoTransitiveDependencies
This didn't work, so I tried to create an inline assembly resolver, as here:
MSBuild UsingTask Resolve References
This doesn't work either. At the moment, I am thinking the only choice I have is to get the dependent DLLs alongside the UsingTask dll, one way or another... but this feels like my MSBuild foo is failing me :/
Any ideas much appreciated! Thanks.
I needed my custom task to use the JSON API (NewtonSoft). MSBuild did not load the external dll until I ran gacutil /i [path to dll].
Ok, v2.4.0.0+ now include the DLLs

Targetting different Frameworks using MSBuild gives problems with dependencies

I have a little project, and I want to have 2 compiled versions of that project:
one that is targetting the .NET 2.0 framework
one that is targetting the .NET 3.5 framework
All is going well; I've put my project under continuous integration (using CC.NET), and I've created 2 CC.NET 'projects'. One project for each target-framework.
I won't go too much in (irrelevant) details, but my solution is set up to target the .NET 3.5 framework in VS.NET.
I have 2 msbuild-tasks:
one task that builds the solution for
.NET 3.5 (simple and easy)
one task that builds the solution for
.NET 2.0
In this Task, I call MSBuild, and I specify that the TargetFrameworkVersion should be v2.0. I also define some additional build-conditions (so that .NET3.5 specific code is not built in the assembly targetting .NET2.0).
So far, so good. Everything works fine.
Now, the problem however is this:
My solution has a few dependencies (references to 3rd party assemblies). In VS.NET, I've set 'copy local' to true for these dependencies.
When CC.NET builds my .NET3.5 version of the assembly, the 3rd party dependencies are indeed copied to my output-directory.
However, when CC.NET builds my .NET2.0 version of the assembly, the dependencies are not copied to my output-directory. (Then, this causes my unit-tests to fail).
My question now is:
How can I say to msbuild that certain of the 3rd party references have to be copied local when building my .NET2.0 version of my project ?
Or, is there any other way to achieve this, since, I wouldn't like to specify every dependency once again in my build-script. This would quickly become a maintenance nightmare, I guess.
I've been revisiting this problem again, since I do not like to have to manually change the csproj file.
(When I change my reference, I must not forget to adapt the csproj file again, to set the Private node to true again).
So, I've been digging into MSDN, and I stumbled upon this:
ResolveAssemblyReference.TargetFrameworkDirectories
Property
Remarks This property is required to determine the CopyLocal status for
resulting items.
If this property is not specified, no
resulting items will be have a
CopyLocal value of true unless they
explicitly have a Private metadata
value of true on their source item.
So, this means that there is yet another possibility, and that is to set the TargetFrameworkDirectories of the ResolveAssemblyReference task.
However, is there anybody out there who knows how to do this ?
I've been trying different things, but nothing seems to be working ...
I've tried this:
<ItemGroup>
<TargetFrameworkDir Include="$(SystemRoot)\Microsoft.NET\Framework\v2.0.50727" />
</ItemGroup>
<PropertyGroup>
<TargetDirsToUse>#(TargetFrameworkDir)</TargetDirsToUse>
</PropertyGroup>
<ResolveAssemblyReference TargetFrameworkDirectories="$(TargetDirsToUse)" />
But to no avail ...
Maybe someone else knows how to do this, or has a golden tip. (I've been spending way to much time on this f*cking issue).
I've been able to solve this problem by making sure that I do not reference assemblies from the GAC.
Instead, I've created a 'lib' directory in my project that contains the 3rd party assemblies.
In my solution, I reference the 3rd party assemblies from there, and set copy local==True.
Next to that, you must also make sure that in your csproj file, the referenced assemblies have a Private tag whose value is set to true.
Like this:
<Reference Include="...">
<SpecificVersion>False</SpecificVersion>
<HintPath>...</HintPath>
<Private>True</Private>
</Reference>
One question: what happens if you try to compile your 2.0 solution/projects inside VisualStudio? Are 3rd party references auto-copied or not?
It's odd that this would work for 3.5 and not for 2.0. I haven't done any parallel-building myself, but when I converted my projects from 2.0 to 3.5 all the 3rd party references were copied regardless of the .NET version.
BTW: I never reference 3rd party libraries from GAC (only those from Microsoft, and even not all of them). I always copy them to my lib directory structure, add them to the source control and reference them from there. Using assemblies from GAC is bad practice as far as I am concerned, since it represents an unnecessary dependency on a development machine setup.
Example of such a directory: http://code.google.com/p/projectpilot/source/browse/#svn/trunk/lib

MSBuild doesn't pick up references of the referenced project

I bumped into a strange situation with MSBuild just now. There's a solution which has three projects: LibX, LibY and Exe. Exe references LibX. LibX in its turn references LibY, has some content files, and also references to a third-party library (several pre-built assemblies installed in both GAC and local lib folder). The third-party library is marked as "Copy Local" ("private") and appears in the output of the LibX project, as the LibY's output and LibX's content files do. Now, Exe project's output has LibX project output, content files of the LibX project, LibY project output (coming from LibX), but NO third-party library's assemblies.
Now I worked this around by referencing the third-party library directly in Exe project, but I don't feel this is a "right" solution.
Anyone had this problem before?
There is a difference in behavior when building with MSBuild (i.e. command line, TFS Build and other tools) compared to building with Visual Studio. The secondary references are not included in the references variable sent into MSBuild compile tasks.
There are several extension points provided by MSBuild to change how references are to be resolved. I have successfully used AfterResolveReference to fix this issue for some of my projects - I have posted more info about the background on my blog.
The workaround is to add the following code into you vbproj or csproj files
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencyies-->
<ItemGroup>
<ReferencePath Include="#(ReferenceDependencyPaths)">
</ReferencePath>
</ItemGroup>
</Target>
Microsoft has stated that this is a won't fix on Connect
You can actually go into the Microsoft.CSharp.targets or Microsoft.VisualBasic.targets file (located in the framework directory, usually C:\Windows\Microsoft.NET\Framework\v3.5) and modify the csc or vbc task parameters to include additional reference dependencies. In the file (VB targets, line 166; C# targets, line 164) change:\
References="#(ReferencePath)"
to
References="#(ReferencePath);#(ReferenceDependencyPaths)"
This might cause other issues depending on how complicated things are and it may play tricks with the Visual Studio inproc compiler, but it's the only way to do it in MSBuild that I've found.
josant's answer almost worked for me; I kept getting an error in Visual Studio when I tried that:
A problem occurred while trying to set the "References" parameter for the IDE's in-process compiler. Error HRESULT E_FAIL has been returned from a call to a COM component
The solution to my problem was to put a condition on the ItemGroup, like this:
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencies-->
<ItemGroup Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">
<ReferencePath Include="#(ReferenceDependencyPaths)"></ReferencePath>
</ItemGroup>
</Target>
That caused Visual Studio to ignore the reference change completely, and the build works fine locally and on the build server.
Yes, I've had that problem, too. Though I'd love to say otherwise, I believe you must include all transitive dependencies as references in your build file.
I've combined Alex Yakunin's solution with one that will also copy native dll's.
The AfterResolveReferences method fails if you've got a directed graph not a tree with a "trying to deploy different copies of the dll" error. (cf. How to configure msbuild/MSVC to deploy dependent files of dependent assemblies)