I'm working on a WCF service. The WCF service interface and implementation are in separate projects. There's also a Windows service project to host the WCF service.
One of the things the WCF service implementation needs to do is call out to several external (SOAP) web services. The way we typically structure this is we create a separate class library project for the SOAP service(s); we'll create a Web reference and a factory/helper method in the class library.
The above detail may or may not be pertinent to the real problem. Which is that I get an error when building the WCF service implementation project (where X is one of the SOAP service wrapper assemblies):
referenced assembly 'X' does not have a strong name
But the WCF service implementation project isn't set to be signed (nor is the interface project). And at this point the only two things referencing it are the Windows service project and a unit test project -- and neither of those is signed either.
The WCF implementation also references other (pre-existing) web service wrapper projects, but it's only complaining about these two. I've pulled up an existing and a new project file in a text editor side by side... And I can's see any significant differences.
I've also checked whether any of the projects is importing a setting that requires it to be signed, as described in Stack Overflow question Remove signing from an assembly. That doesn't appear to be the case.
I was attempting to use AutoMapper -- 1.1 since we're still on .NET 3.5 -- in my WCF implementation. That is a signed assembly, so I can see where it might have a problem reflecting on my code and the SOAP service wrappers. But it seems to me that would be a run-time problem, not build time. But because I suspected it might be at least a contributing factor, I removed AutoMapper and the dependent code but I still get the same error.
I have researched the issue, most of the search results consist of instructions on how to sign (possibly third-party) assemblies.
I've tried removing and re-adding the references, restarted Visual Studio and my PC.
Visual Studio 2010 / .NET 3.5 on Windows 7 64-bit.
I'm sure I'm missing something fairly obvious ... I just can't figure out what.
Well ... this is embarrassing.
As I suspected, it was something simple. The service interface and implementation were in fact set to be signed; I was incorrect about that. However, this was done not in the project settings/file, but in the AssemblyInfo.cs file, via the directive
[assembly: AssemblyKeyFile("keyfile.snk")].
I was not familiar with this method of signing. I gather it's been deprecated since VS2005 -- partially because it's more straightforward to manage signing via the project properties, partially because putting this information into the AssemblyInfo is considered a security risk.
And I had in fact looked at at least one of the AssemblyInfo files ... but apparently I didn't scroll down far enough. (I said it was embarrassing.)
Hopefully someone else can benefit from this.
You can not reference an unsigned assembly from a signed assembly, the project which fails to build should be the one that has a strong name key tied to it.
It is possible that you have a signed version of one of your references in the Global Assembly Cache that MSBuild is picking up instead of the reference you selected.
Related
I have an assembly (MYASM.dll) targeting .NETFramework 4.0 (with a strong name)
I want to deploy this assembly in a way it is part of .NETFramework (or the whole system thinks it is) on target machine.
By that I mean:
.NET runtime sees it at it sees System.dll (no need to deploy locally or provide a reference path)
MSBuild sees it when I do <Reference Include="MYASM" /> without needing a hintpath
User is able to make Add reference in Visual Studio and that introduces <Reference Include="MYASM" /> without the strong/full name
I have solved 1. (and apparently 2.) by adding it to the GAC. But this is apparently not sufficient.
I have partially solved 3. by putting my assembly in a special folder ([INSTALLFOLDER]\lib) and set registryKey HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0\AssemblyFoldersEx\MyAssemblies
Then I can do Add reference, but then I get:
<Reference Include="MYASM, Version=1.1, Culture=neutral, ..." /> in my csproj instead of just <Reference Include="MYASM" /> as I'd like.
With the second approach, if I manually edit the csproj, everything is OK, but I can't ask my users to do that.
What should I do here?
[EDIT] apparently it’s not obvious I have my own MSI. But yes I have. I don’t control users machines with a magic wand
No, you've taken this as far as it can go. It isn't actually that obvious how VS figures out to put the partial assembly name into the project file. This is not public code and can't be tampered with. Pretty sure it does not use a white-list and it can't pay attention to the reference assembly location.
Most likely detail is the PublicKeyToken of the assembly. The framework assemblies always have to exact same value for them, b77a5c561934e089. Its value is even prescribed in the CLI spec (Ecma-335). Next most likely by a considerable distance is the signing certificate, identifying the assembly as owned by Microsoft. Both however present the exact same problem, you can't get the private key that is required to strong-name or sign the assembly. They are locked inside a vault in Redmond, only trusted build engineers have access to them.
There is another nasty little detail you are overlooking, you are not nearly scared enough of DLL Hell. Cold hard fact is that if you ever expose the assembly in the GAC on another machine that is not in your control then you can never change it again. You can no longer modify the public interface of the assembly. Can't add a new public method or type, can't modify the arguments and return type of a method, can't add an enum member, etc. Even harsher, something Microsoft worries about, is that you can't really change private and internal members either. Programmers have a knack for using Reflection to poke around, terrific bug fixing tool. But at least you can tell them "don't do that!".
Making such modification requires increasing the [AssemblyVersion]. Now you get a different kind of DLL Hell, the machine might not have been updated by your installer. Or worse, a solution uses projects that have different references. Microsoft had to solve this problem for framework assemblies, they did so by modifying the CLR. Automatically forwarding old versions to new ones. The basic reason why using an assembly built for .NET 2.0 can be used in a .NET 4.x project. You can't get that kind of service for your own DLL.
"Don't do it" is the only good advice, getting in DLL Hell trouble is however a terrific learning experience I can recommend for anybody. Hell has to be experienced to be feared.
Best advice is to publish a Nuget package. They do the exact opposite, never deployed in the GAC and version numbers change very rapidly. But always available when a programmer needs it.
There are a few ways...
1) is to create a new setup and package this for the framework you target. You can Package this and have it deployed using the domain controller. When your users log in the domain will update the packages, this way you'll be able to deploy your software to specific users and or user groups. Depending on your infrastructure you'll have a software management infrastructure that you can use (2 links included).
2) Create a NuGet package if you're targeting developers. If your organisation host your own NuGet server limiting the distribution. Add the Package source to Visual studio open the Options Page, type NuGet in the search field and set the URL/ UNC path.
3) use OneClick deployment, this allows you to have the application download the updated dll's and install them on the machine. It requires a Code Sign certificate but you're probably signing your code anyway (better for Anti-Virus tools if you do).
Now linking your MyAsam.dll will be done by the application linking definition or IoC container. Basically, if it finds the dll and no version is defined it will take the first one it finds I think the order is 1 AppFolder, 2 GAC, 3 Path, not sure. This "take what you find" is generally referred to as "DLL-Hell", The NuGet and OneClick solution works best in this as You will always get the Updated dll that works for the application. Placing the DLL in GAC is going to get problematic if you have moe than 1 application using your dll and both need the "right" version where the "right version" differes between them....
If you have the source code available for MYASM.dll, then I would prefer adding a project reference to your consuming application. When doing so, Visual Studio shall create a GUID for all the referenced project.
Scenario: Integrating external web application with secure information. Web site calls BizTalk WCF service with one (or more) fields that have been encrypted with web site's encryption class. Inside the publish map I need to convert the web encryption to BizTalk environment encryption (yes, the differences are necessary). This used to be done in the SOAP asmx web service data types code but now needs to be done either in the map or pipeline. Map seemed easiest to implement with scripting functoid reference to external assembly where I call decryptor for the one and re-encrypt with the other pretty much exactly like I did in old web service.
Problem: External assembly is relatively simple in that it consists of one class with default constructor no arguments, with one public method the takes a string and returns a string. Input is web encrypted valve, output is my encryption value. I reference 3 custom libraries for this encryption swap inside the method. None of the referenced assemblies are being called from the scripting functoid. All are added as references in map project. All are strong-named. All are GAC'd.
Yet, when I test the map it gives the following error:
Function 'ScriptNS0:Myfunction()' has failed. Exception has been thrown by the target of an invocation. Could not load file or assembly 'OurCompany.Project.WebEncryption, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123654789abcd' or one of its dependencies. The system cannot find the file specified.
I double checked versions etc in the GAC_MSIL folder and everything matches. What gives?
ARRRGGGG! Curse you Visual Studio! VS was not picking up the changes to the GAC and additionally I don't believe it liked my original dotted namespace/assembly name. I simplified to MapScripting for NS/Asm and restarted VS and it worked. UGH!
Background Information
I have taken ownership of 3 websites that share a number of internal assemblies, they each connect to a WCF Service hosted in IIS for basic CRUD type operations.
A couple of different developers could be working on each website at any one time, making changes/additions to the code that is used in the shared assemblies.
The Issue
Each developer then builds their code and ensure that their website is working correctly. However they are unaware that in changing the shared code they could have affected the functionality in the other websites.
To resolve this, I could build each shared assembly separately, copy the dll to a directory specific to each website and add a reference from there.
However this flags up a couple of issues:
This means constantly building the shared assembly and manually copying the dll to the directory it is required in.
When a developer gets the latest source code for the shared assembly, they get the changes any way.
I have thought about managing this using nuget packages, but this only really addresses issue #1 to make it slightly easier to manage specific versions.
I hope this makes sense...
Does anyone else have any advice/tips?
There's no real magic bullet to this one.
Either the developers have to take some responsibility for ensuring changes to shared assemblies don't impact other code that references those assemblies or you really shouldn't be sharing them across projects. If the things they're changing in these shared assemblies really happens that often, you might consider migrating those parts or the methods they're changing to each individual project. Sharing assemblies like this should really only be done for absolute core functions that should change very, very infrequently.
Situation
I run a build system that executes many builds for many project. To avoid one build impacting another we lock down the build user to only its workspace. Builds run as a non privileged users who only have write ability to the workspace.
Challenge
During our new build we need to use a legacy 3rdparty DLL that exposes its interface through COM. The dev team wants to register the build(regsrv32.exe) but our build security regime blocks this activity. If we relax the regime then the 3rdparty DLL will impact other builds and if I have two build which need two different versions I may have the wrong build compile against the wrong version (a very real possibility).
Question
Are there any other options besides registration to handle legacy DLLs which expose their interface via COM?
Thanks for the help
Peter
For my original answer to a similar question see: TFS Build server and COM references - does this work?
A good way to compile .NET code that references COM components without the COM components being registered on the build server is to use the COMFileReference reference item in your project/build files instead of COMReference. A COMFileReference item looks like this:
<ItemGroup>
<COMFileReference Include="MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>
Since Visual Studio provides no designer support for COMFileReference, you must edit the project/build file by hand.
During a build, MSBuild extracts the type library information from the COM DLL and creates an interop assembly that can be either standalone or embedded in the calling .NET assembly.
Each COMFileReference item can also have a WrapperTool attribute but the default seemed to work for me just 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.
It's a shame that no-one seems to know anything about this technique, which to me seems simpler than the alternatives. It's actually not surprising since I could only find just the one above reference to it on-line. I myself discovered this technique by digging into MSBuild's Microsoft.Common.targets file.
There's a walkthrough on registration-free COM here:
http://msdn.microsoft.com/en-us/library/ms973913.aspx
And excruciating detail here:
http://msdn.microsoft.com/en-us/library/aa376414
(the root of that document is actually here: http://msdn.microsoft.com/en-us/library/dd408052 )
Also, for building in general, you should be able to use Tlbimp or tlbexp to create a TLB file that you can use for building, assuming the point of registering is just to be able to compile successfully, and not to run specific tests.
Installation tools such as Installshield can extract the COM interfaces from the DLLs and add them to the registry. It can also use the self-registration process of the DLL (which I believe is what regsvr does), but this is not a Microsoft installer best practice.
in .NET COM is normally done thru Interop in order to register .DLL in .NET they are called Assemblies and that can be done several ways.. by adding references via VS IDE at the project level, or writing code that Loads and unloads the assembly.. by .Config file that haas the reference to the assembly as well as the using of that reference within the project... GAC.
If you have access to the 3rd party .DLL's you can GAC them, and reference them in your project
you can add a using to your .cs file header as well as add the reference to the project by right clicking on reference --> add Reference ...
you can also do the above step as well as set the copy local = true in the properties for that .dll.. I hope that this gives you some ideas.. keep in mind that .NET assemblies are Managed code so there are several ways to Consume those 3rd party .DLL's using other methods within C# like LoadFromAssembly ect..
Thanks for all the help.
We changed from early-binding to late-binding because we never really needed the DLL at compile time. This pushed the registration requirement from the build server to the integration test server (where we execute the installer which handles the registration). We try to keep the build system pristine and have easy-to-reset integration systems.
Thanks again
Peter
I have developed a WCF service that has been working fine for a few weeks now. I publish this service to IIS directly from the development server. I have a dev test app that includes this WCF service as a Service Reference in its own project.
My WCF class library also references another project within the Visual Studio solution, and many classes are in the referenced project that are used by the service and calling applications.
Today, after publishing some changes to the service (I did not make ANY changes to the referenced project or its classes), three of about 20 of the classes in the referenced project are no longer in the service reference of my test app. I cannot use them in code any longer, and they are no longer in the Reference.cs. I double checked, the classes in question are still decorated with [DataContract] and the properties are all still decorated with [DataMember] in the service solution.
Everything is building in Visual Studio OK.. I have rebuilt and republished everything, the missing classes are set to compile, I have tried IIS resets, completely deleted the IIS site and did a clean republish, no avail. I have tried deleting the service reference on the test app and getting a clean proxy every time as well. Nothing is working.
I then tried making a dummy class in the referenced project and built it in. Republished the service again, and even this dummy class does not appear in the Reference.cs. I am at a complete loss as to why these classes are not included in the .dll for the referenced project after it is published as it is evident that everything is building OK in Visual Studio.
I face such problem.
Following steps help you
1. Right click on Service ( In Service Reference Section of Project)
2. In Update Service Reference click on Advanced
3. Do as attached image.
Wanted to come back and post what was causing my problem:
I had made changes and none of my service methods used the 3 "missing" classes as a return type or parameter anymore. I simply had to un-comment the methods that used those classes to make them usable again. Makes sense now but I was convinced something else was wrong.
I faced similar issue and resolved it by doing the following.
Add the address of your end point with mexHttpsBinding.
E.g., Lets consider the address as mex.
While configuring service reference append /mex at the end of your service URL.
https://service.domain.com/serivce1.svc/mex
This will enable metadata exchange. So Reference.cs will be modified / added (if you are adding service reference for the first time).
Please Make sure that your service has an end point with mexHttpBinding / mexHttpsBinding
Edit : On analysis I found that this issue happens when the service is exposed via more than two end points (including mex end point)