Building a project with MSBUILD builds up-to-date, not specified projects (.NET 4) - msbuild

I have a solution I am migrating from Visual Studio 2005 to 2010. Inside the IDE on developers machines, everything works fine, but my build server (with CC.NET) is giving me an error.
I have around 10 projects, 3 of which are not strong-named due to using unsigned references.
One of them (signed) has an InternalsVisibleTo another of the signed projects.
In the IDE I use Debug/Release configuration, which delay-sign all of the projects that are to be strong-named.
I also created Debug_Signed and Debug_Unsigned configurations (and the corresponding Release_*), which only build the corresponding projects.
In my build server I use the actual private key to build first the Debug_Signed configuration, and then call MSBUILD again on the Debug_Unsigned configuration with SingAssembly=false.
This worked perfectly fine using .NET 2.0 SDK, but now the first MSBUILD works fine, but then the second MSBUILD tries to rebuild the signed projects (since they are referenced by the unsigned projectes), and I get an error about one assembly having an InternalsVisibleTo a signed assembly while the generated assembly is not signed:
error CS0281: Friend access was granted to 'Sic.Gateway.Customers, PublicKey=0024...4bbe', but the output assembly is named 'Sic.Gateway.Customers, Version=2.0.0.796, Culture=neutral, PublicKeyToken=null'. Try adding a reference to 'Sic.Gateway.Customers, PublicKey=0024...4bbe' or changing the output assembly name to match.
What I would like is the second MSBUILD to use the reference assemblys that were just built by the first MSBUILD instead of trying to rebuild them.
The build server just has the .NET 4.0 runtime (not the SDK) and I had to copy two .targets files (for SQL/CLR and web applications). But since everything seems to build fine, I don't think that would be a problem.
Does anyone have any idea of what I can do?
Thanks,
Luis Alonso Ramos

(copied as answer from Luis' comment above)
When running MSBUILD for either the unsigned or signed assemblies I was specifying three properties:
SignAssembly=true\false;DelaySign=False;AssemblyOriginatorKeyFile=D:\blah\blah.s‌​nk
The thing is these properties applied to the whole solution, overriding the settings from individual projects. What I did was specify only the DelaySign and key file properties, without specifying SignAssembly. That way, the assemblies that are to be signed are still signed (with the new non-delay-signing settings), and those that shouldn't be signed are not.

Related

How do I expose a .netstandard2.0 library with COM for use in VB6?

I have a dotnet core library, a framework 4.7.2 library and a vb6 application.
I want to write a common library for them all to access and so choose .netstandard2.0
I tried a the 4.7.2 framework wrapper library between .netstandard2.0 library and vb6.
However I ran into assembly binding problems
Looking at the docs I see
In .NET Core, the process for exposing your .NET objects to COM has been significantly streamlined in comparison to .NET Framework.
However no mention .netstandard2.0
I decided to try following the docs anyway even though my project is using .netstandard2.0
I got up to the instructions on Generating the COM Host in which case the output files ProjectName.dll, ProjectName.deps.json, ProjectName.runtimeconfig.json and ProjectName.comhost.dll should build.
However the ProjectName.comhost.dll and ProjectName.runtimeconfig.json do not create.
I see in this dotnet standard issue that Microsoft plans on having tooling support in "Preview 4"
I am running VS 16.4.5
[Update]
I decided to try making a .net core wrapper library and enabling it for com.
I was able to add my .netstandard to the wrapper library via a nuget package (I build the .netstandard library using azure devops)
When I build my wrapper library the .dll, .deps.json, .pdb, .runtimeconfig.dev.json and .runtimeconfig.json files are created in a bin\Debug\netcoreapp3.1 folder.
However none of the .netstandard library files appear in the bin\debug folder.
I copied the .netstandard library and the .netcore wrapper libraries to the same folder and ran
regsvr32 MyCoreComWrapper.comhost.dll
However no .tlb file is created which I need to be able to use from VB6
I note the following in the docs
Unlike in .NET Framework, there is no support in .NET Core for
generating a COM Type Library (TLB) from a .NET Core assembly. The
guidance is to either manually write an IDL file or a C/C++ header for
the native declarations of the COM interfaces.
I found some information on github but would love a step by step guide to making the .tlb
I thought about using latebinding instead but am unsure of how to use it with a com library.
[Update]
I put a sample project on GitHub including some VB6 files.
With VB6 referencing the .tlb referenced with the framework library.
When I try to run that I get
Could not load file or assembly 'Microsoft.EntityFrameworkCore, Version=3.1.2.0,
Culture=neutral, PublicKeyToken=adb9793829ddae60' or one of its dependencies. The system cannot find the file specified.
So I copied all the files from my framework test project to my vb6 folder, rebuilt and ran.
Then I got the error
Could not load file or assembly 'Microsoft.Extensions.DependencyInjection.Abstractions, Version=3.1.0.0,
Culture=neutral, PublicKeyToken=adb9793829ddae60' or one of its dependencies. The system cannot find the file specified.
I see the file Microsoft.Extensions.DependencyInjection.dll is present with File version 3.100.220.6706
Regarding the .NET standard, I may be wrong but I think this is not applicable here because the COM interop stuff are at a higher level than the one .NET standard is targeting; we can only talk about either .NET Core or .NET Framework for COM interop.
If you want to generate a type library, you have few options.
By far, the easiest method is just to use .NET Framework. The fact that you are wanting to create a type library negates the advantages of .NET Core already because several COM, especially the "Automation" features are Windows-only. Using framework will be fine at least until .NET Core 5 comes out.
That said, if you have a business reason for using .NET Core but still need COM support, including the type library, then based on this GitHub comment, you should be able to compile your own IDL. Note that requires you to install C++ build tools because the MIDL compiler is not really a standalone thing that you can get without the rest of the C++ build tools.
It is strongly suggested to have had read the documentation on how .NET Core handles COM activation.
Assuming having the C++ build tools is not a barrier for you, the steps would be the following:
1) Create a .idl file that defines all your COM interfaces in the IDL format. That requires some translation between the .NET interface and the COM interface. Here's a partial example of how you'd need to translate between your C# interface and COM interface as defined in IDL:
[
Guid("<some gooey>"),
InterfaceType(ComInterfaceType.InterfaceIsDual)
]
public interface IFoo
{
string Bar { get; }
string Baz(int Fizz);
}
Would be translated into IDL:
[
uuid(<assembly gooey>),
version(1.0)
]
library myFoo
{
[
uuid(<some gooey>),
object,
dual
]
interface IFoo : IDispatch {
[propget] HRESULT Bar([out, retval] BSTR* retVal);
HRESULT Baz([in] long Fizz, [out, retval] BSTR* retVal);
}
}
Once you've defined the .idl file and it is an accurate representation, you can then use MIDL to compile the .idl file into a .tlb file. Usually something like midl foo.idl /tlb: foo.tlb. You should make use of the MIDL language reference to help you write the .idl file. As a quick way to get started, you could copy your C# interfaces to a .NET framework project, use tlbexp, then use oleview (available via Visual Studio Developer Command Prompt) or olewoo to view the resulting IDL file to get you started.
The next step is to then create registry keys so that your CLSID can reference the type library. You will need to have your assembly's GUID handy and it must be used as the library's uuid in the .idl file as well.
Using IFoo interface example, you would need to create the registry similar to below (using .reg format for easy sharing/comprehension and assuming per-user installation, rather than per-machine):
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}]
#="IFoo"
[HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}\ProxyStubClsid32]
#="{00020424-0000-0000-C000-000000000046}"
[HKEY_CURRENT_USER\Software\Classes\Interface\{<some gooey>}\TypeLib]
#="{assembly gooey}"
"Version"="1.0"
You will also need to create the registry in the CLSID, Interface, TypeLib, and Record as needed. This article provides a good overview of all registry keys but keep in mind it's assuming .NET framework, not .NET Core, so not all keys are applicable, especially under the CLSID branch.
Note that when you run the regsvr32, it will normally create the keys in the CLSID and Interface branches but you will need to add the TypeLib keys under the Interface's branch and also an entry to the TypeLib branch. You also will need to create the ProgId keys, too if you want to support CreateObject functionality.
Initially, you can start with just a .reg file that you can manually update & maintain but if you have several objects, then it becomes desirable to automate this. This can be also managed via the DllRegisterServer call so that when you execute regsvr32, it will take care of registering the keys. On the other hand, you're now polluting your codebase with registration code. Some elect to use installers to do the registry keys write instead.
I hope that helps you get started!
The issue is due to assembly binding resolution that fails when ran from VB6 (IDE or compiled .exe file).
Here are the steps to solve it:
Compile the VB project, for example, let's assume the compiled file is Project1.exe.
Copy all .NET assemblies (including x86 and x64 directories, and languages directory if localized version is important) aside the compiled VB6 file
Now run Project1.exe, you will get an error like this:
The error is clearly a mismatch between the version of your assemblies aside the Project1.exe file and the version of referenced assemblies (not references you've created yourself but reference embedded in these assemblies... ). You don't see that when you start a .NET program because resolution is a very complex process that depends on a lot of parameters (and it's not getting any better with .NET Core, Framework, Standard, nugets, etc.).
To futher check it's a mismatch error, you can also use the Fuslogvw.exe (Assembly Binding Log Viewer) tool from the SDK.
Now we know it's an assembly version mismatch issue, what you can do is create a file named Project1.exe.config aside Project1.exe and add assembly binding redirects to it.
The easiest way to configure it is to redirect all possible versions to the ones present in the directory that contains your program, so in your case (and as of today, as all these can evolve...), it would be something like this, possibly for every assembly you reference directly or indirectly:
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
...
<dependentAssembly>
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" />
<!-- 3.1.2.0 is the version of the assembly you ship -->
<bindingRedirect oldVersion="0.0.0.0-65535.65535.65535.65535" newVersion="3.1.2.0" />
</dependentAssembly>
...
</assemblyBinding>
</runtime>
</configuration>
Unfortunately, there are many satellite assemblies, and it's a bit tedious to create all redirects with correct information, so I've created a tool that creates a .config file with the redirects configured automatically for all .NET assemblies in a given directory: https://github.com/smourier/BindingRedirectGenerator.
If you want it to work for the VB6 IDE too, you'll have to use the same procedure in a VB6.exe.config file aside VB6.exe.
A reminder to myself
Use a demo UI to access the original DLL to confirm the call works. ( if you can't get it to work skip to making the App.Config for the unit test project using BindingRedirectGenerator )
Add a unit test in the com visible project to confirm the
call works.
Copy all the dlls created by both projects to the
release folder
For each com visible dll run as Administrator
c:\windows\microsoft.net\framework\v4.0.30319\regasm /verbose /codebase /tlb:MyLibrary.tlb c:\myproject\releasedlls\MyLibrary.dll
Install BindingRedirectGenerator to c:\brg say
At the command prompt change directory to c:\brg
BindingRedirectGenerator c:\myproject\releasedlls App.config
Rename App.config to MyVB6Project.exe.config and copy it to the same folder as MyVB6Project.exe
Remember to set up the files for the vb6.exe folder if you want to run it in the vb6 ide
Put the whole process in a script for future use ( I used a .bat)
Keep an eye on what nuget has put in app.config
Pay attention to the yellow warnings at build time!

ClickOnce app built via Jenkins throwing CS1704 - An assembly with the same simple name {whatever} has already been imported

I have a winforms app, which is deployed via click-once publishing.
Assuming my tests are correct, the only way to have this same app installed more than once in the same machine, is the each publish be made for a different Assembly Name.
My problem is that I am running the msbuild via jenkins, and, to accomplish the above, I would add /p:AssemblyName={whatever}, which will rename all assemblies generated by the build to this same {whatever} value. If the output of my build is 1 exe and 5 dlls, all 6 files will be named {whatever}.
Which in turn raises CS1704: An assembly with the same simple name {whatever} has already been imported.
Is the only way out of this to perform all csc.exe calls that msbuild generates, myself, and see if I can set different assembly names per project ?
Or are there other ways to solve this ?
The problem here is that every property passed to MSBuild as a command line argument is considered a global property and will override the property on all projects built in that invocation. The solution is to introduce an intermediate property that only the "main" project consumes.
Edit the app's csproj file to include this (in the place where AssemblyName is already defined - assuming here that WindowsFormsApp1 is the name of the application):
<AssemblyName>WindowsFormsApp1</AssemblyName>
<AssemblyName Condition="'$(OverrideAssemblyName)' != ''">$(OverrideAssemblyName)</AssemblyName>
you can then use the msbuild commands using /p:OverrideAssemblyName=AnotherName without creating conflicting assembly names on referenced libraries. The property is still defined globally, but only one project consumes it.

Cannot find the reference specified-Visual Basic 2013

I've got a new system. I am trying to connect to and run the system through a 64-bit Windows 7 PC. The company sent me a Visual Basic project for that. It uses .NET framework 4 and Windows Form Applications. The target CPU is x86.
In the References tab, next to three of the COM references there are "The system cannot find the reference specified" statements. When built, the project gives the namespace errors and warnings because of these objects. The other references work fine. I've then seen those .dll files in the debug folder. I think they are generated by the company before sending me. They also exist in the bin\debug folder.
I've removed the three from the references list in the project and tried the following separately:
1) In the project, refer to the .dll files that exist in the debug folder. (Seems a fake way, but removes all the errors when the project is built. When debug, it gives the "Retrieving the COM class factory for component with CLSID ... failed due to the following error: 80040154" error.)
2) Copy the .dll files in the debug folder to C:\Windows\SysWOW64 folder, then register through regsvr32 "name.dll" in the console as admin. This way, I get the ".dll was loaded, but the DllRegisterServer entry point was not found" error. (In the regedit window, the registry editor cannot find the ID's of those references that are seen in the project properties in VB.)
3) Do the item (2), but with the C:\Windows\System32 folder instead of SysWOW64. The same error when registering.
4) Changed the solution platform to x86 and tried the items 1,2, and 3. Still the same errors.
Do I have to register these dll files? And, am I supposed to register exactly these dll files that exist in the debug folder? Or are they supposed to be generated after build by referring to some 'missing' type libraries in the references tab? I don't have these type libraries, though.
All suggestions are welcome ASAP. Thanks in advance.
You have three Interop dlls for your project, correct? These dlls just allow you to use a COM dll without having to talk directly to the COM dlls yourself.
Referencing the dlls in the bin/Debug directory should be fine, since those are probably the correct dlls that the project was built with. However, you will want to move them to another folder before continuing.
What you need to get from the company that sent you the project is the original COM dlls and their dependencies, if any. Usually an interop dll is called Microsoft.Phone.Interop.dll where it is performing interop with Microsoft.Phone.dll. Microsoft.Phone.dll needs to be registered on your computer using regsvr32 and then you just need a reference to the interop dll.

Reference VB.NET DLL in Kofax Document Validation Script

We are working on a validation script for Kofax Capture 9.0 / 10.0 in VB.NET 3.5.
We know how to create a script using the Admin Module, and how to get it operational.
The problem is that we need to reference a dll, located on a remote machine. (GAC is no option) This dll holds abstract classes we need in each validation script.
Even when putting the dlls locally (copy local), the Validation Module (index.exe) immediately throws the "cannot find reference" exception, even though the project compiled perfectly.
I guess the basic question comes down to: where do we put the dlls, in order for the Validation Module to find them?
The simple answer is to put the dll in the same folder as the application because this is one of the places which .NET will probe when trying to find it. The Validation module is run from the Capture bin directory which will be something like "C:\Program Files (x86)\Kofax\CaptureSS\ServLib\Bin\". This would need to be done on each client using Validation.
If you have a more complicated scenario, you could look implementing the AppDomain.AssemblyResolve Event and using Assembly.LoadFile to get the assembly from a custom location, but the using the bin path is less complicated.
If you end up having further trouble, you can troubleshoot by using the Assembly Binding Log Viewer (Fuslogvw.exe) which can tell you more details about why the assembly failed to load and where .NET tried to search for it. Assembly loading can fail for reasons other than just the path.
For more detail on how .NET loads assemblies, see the following:
How the Runtime Locates Assemblies
Locating the Assembly through Codebases or Probing
We found a solution: add all library files as "links" to the project. (Add --> Existing File --> small arrow next to "Add" --> Add as Link)
This ensures the files are compiled when you build the project. The Kofax Validation Module can now find the files, whereas when referencing the file, it could not. Why it could not, remains a mystery...

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