Compile a version agnostic DLL in .NET - c#

Scenario
I have two wrappers around Microsoft Office, one for 2003 and one for 2007. Since having two versions of Microsoft Office running side by side is "not officially possible" nor recommended by Microsoft, we have two boxes, one with Office 2003 and the other with Office 2007. We compile the wrappers separately. The DLLs are included in our solution, each box has the same checkout but with either Office 2003 or 2007 "unloaded" so it doesn't attempt to compile that particular DLL. Failure to do that will throw errors on compilation due to the Office COM DLLs not available.
We use .NET 2.0 and Visual Studio 2008.
Facts
Since Microsoft mysteriously changed the Office 2003 API in 2007, renaming and changing some methods (sigh) thus making them not backwards compatible, we need the two wrappers.
We have each build machine with the solution and one Office DLL activated. E.g.: the machine with Office 2003 has the "Office 2007" DLL unloaded, therefore not compiling it. The other box is the same idea but the other way around. All this because we can't have 2 different Office in the same box for programming purposes. (you could technically have two Office together according to Microsoft) but not for programming and not without some issues.
Problem
When we change the Application Version (from 1.5.0.1 to 1.5.0.2 for example) we need to recompile the DLL to match the new version of the application, this is automatically done, because the Office wrapper is included in the solution. Since the wrappers are contained in the solution, those inherit the APP Version, but we have to do it twice and then "copy" the other DLL to the machine that creates the installer. (A Pain…)
Question
Is it possible to compile a DLL that will work with any version of the application, despite being "older"? I've read something about manifests but I have never had to interact with those. Any pointers will be appreciated.
The secret reason for this is that we haven't changed our wrappers in "ages" and neither did Microsoft with their ancient APIs, yet we are recompiling the DLL to match the app version on every release we make. I'd like to automate this process instead of having to rely on two machines.
I can't remove the DLL from the project (neither of them) because there are dependencies.
I could create a third "master wrapper" but haven't thought about it yet.
Any ideas? Anyone else with the same requirement?
UPDATE
Clarifying:
I have 1 solution with N projects.
"Application" + Office11Wrapper.dll + Office12Wrapper.dll.
Both "wrappers" use dependencies for application + other libraries in the solution (datalayer, businesslayer, framework, etc.)
Each wrapper has references for the respective Office package (2003 and 2007).
If I compile and don't have office 12 installed, I get errors from Office12Wrapper.dll not finding the Office 2007 libraries.
So what I have are two building machines, one with Office 2003, one with Office 2007. After a full SVN update + compile on each machine, we simply use office12.dll in the "installer" to have the wrapper compiled against the "same code, same version".
Note: The Office 2007 Build Machine, has the Wrapper for Office 2003 "unloaded" and viceversa.
Thanks in advance.

When the .NET assembly resolver is unable to find a referenced assembly at runtime (in this case, it cannot find the particular wrapper DLL version the application was linked against), its default behavior is to fail and essentially crash the application. However, this behavior can be overridden by hooking the AppDomain.AssemblyResolve event. This event is fired whenever a referenced assembly cannot be found, and it gives you the opportunity to substitute another assembly in place of the missing one (provided that they are compatible). So, for instance, you could substitute an older version of the wrapper DLL that you load yourself.
The best way I've found to do this is to add a static constructor on the main class of the application that hooks the event, e.g.:
using System.Reflection;
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
{
AssemblyName requestedName = new AssemblyName(e.Name);
if (requestedName.Name == "Office11Wrapper")
{
// Put code here to load whatever version of the assembly you actually have
return Assembly.LoadFile("Office11Wrapper.DLL");
}
else
{
return null;
}
}
}
By putting this in a static constructor of the main application class, it is guaranteed to run before any code attempts to access anything in the wrapper DLL, ensuring that the hook is in place ahead of time.
You can also use policy files to do version redirection, but that tends to be more complex.

Just a thought - could you use TlbExp to create two interop assemblies (with different names and assemblies), and use an interface/factory to code against the two via your own interface? Once you have the interop dll, you don't need the COM dependency (except of course for testing etc).
TlbImp has a /asmversion for the version, so it could be done as part of the build script; but I'm sure you even need this: just make sure that "specific version" is false in the reference (solution explorer)?
Also - I know it doesn't help, but C# 4.0 with dynamic and/or "No PIA" might help here (in the future; maybe).

I'm not sure I am completely following everything you stated, but let me try:
It sounds like you have one solution with 2(?) projects. One is the actual application, and the other is a wrapper for the Office API. Your application then has a project reference to your Office API wrapper. I've never programmed for office before, but it sounds like the programming APIs are a common component that you can only have one version of on a machine (ie. 2003 or 2007, not both). And maybe this is where the problem is, but because you have a project reference, the wrapper will be compiled first, copied to the bin directory of your application, where your application will be linked to that build of the wrapper. This will cause the manifest of the application to specifically request that version of the wrapper at run time.
If you had the wrapper in a separate solution, and added a reference to the compiled library rather than the project, you would always link your application to that version of the wrapper and you could avoid the problem.
Another possible choice is Assembly Binding Redirection. This is more advanced, and comes with it's own set of problems, but you can read about it here.
Or similar to Marc's idea, you could extract an interface and pull some common objects into a Framework library, and code your application against the interface and common objects. Then at runtime use reflection to load the assembly and instantiate the wrapper you want.
I think the key is to remove the project dependency if you can. It sounds like the wrapper is pretty stable and isn't changing, otherwise you wouldn't be asking to link to a previous version of it.

Installing Office 2003 and 2007 side-by-side on the same machine is definitely possible - we do it in our organisation even on end-user production workstations.
In that linked article, Microsoft recommend that you don't do this for actual use. But in your case it appears to be just for a single build machine, i.e. you're not going to actually use either version of Office on that machine. In this context, I would try to see if you can make the side-by-side installation work.
My assumption might be wrong, and you're attempting to do this for every developer's machine. In that case, you should ignore this answer :-)

Nice sleuthwork! I just threw together an implementation based on the concept presented above, and it works wonderfully:
static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string partialName = args.Name.Substring(0, args.Name.IndexOf(','));
return Assembly.Load(new AssemblyName(partialName));
}
Of course there is room for enhancement, but this does the trick for me!

Related

c# outlook addin vsto access OL2016 Interop while working with OL2010 PIA

We have managed to build an outlook addin for 2010,2013 and 2016. We have created an OL2010 vsto project and fiddled with the .csproj file(1*). The problem is that few types were added in 2013/2016 and we can't access them. We only want to access them if the respective OL version is used of course.
As far as i understand the whole thing:
The Interop assemblies are delivered with the addin. So its theoretically possible to inject some IL code or provide additional assemblies with the same namespace that provide those missing types. Since those Interop assemblies are only COM Wrapper and the functionallity relies on unmanaged code which is provided by the installed Outlook version they should get loaded seamlessly when imported correctly.
Is that somehow feasible?
What could go wrong if i try this/deploy the addin to the client?
How could i proceed to determine if i need to use a higher version Interop Type?
How can i load the higher Interop Type?
How can i use it without conflicting existing types?
As i want Intellisense and static typing. I have to predefine those types and decorate them with Guid, CoClass, and TypeLibType. Will that be enough?
(1*)(we changed the DebugInfoExeName and the OfficeVersion in the ProjectName.csproj file and built the respective installer with different virtual machines where the corresponding outlook versions are installed. Maybe there is an easier way? -> let me know!...sadly we can't use addin-express because of some reasons.
I would preferably not use the complete OL 2016 Interop Assemblies since it is throwing exceptions when used from other Threads than the main threads. And we have some synchronization code which has to be run in the background.
Thank you for any answers!
"Creating interops manually: In Visual Studio, just add a COM reference to an application of the Office 2000 suite to the project. This automatically creates the interop you need. But it is the point where your problem begins: a great number of classes and events are inaccessible, because a number of identical bugs in Office type libraries make Studio create the interop that will not work for you. You can disassemble the interop, make unavailable classes and events public, and recompile it (ildasm.exe and ilasm.exe). This is exactly the way Add-in Express version-neutral interops were created."
source: https://social.msdn.microsoft.com/Forums/en-US/a95cd4e3-e619-4846-be2a-ce4c235ff457/is-it-possible-to-use-the-microsoftofficeinteropoutlook-that-comes-from-office-2010-with-all?forum=outlookdev
EDIT
Checkout this project
https://github.com/netoffice/NetOffice-NuGet
It contains version neutral interop assemblies though the structure and namespaces dont match... But still could be useful.

Embed Interop types issue

Of course the Embed interop types feature is a great thing, but I can't get it work with me even in simple scenario, so please advice
this is the error I get when I run my project on machine which didn't have powerPoint installed on it:
My code is very simple I just create object from powerPoint, create presentation and slid write something in it.
The libraries I embed is Office and Microsoft.Office.Interop.PowerPoint
converting build configuration to x68 didn't solve it,
I am building windows application and putting the code in button click as follows:
private void button1_Click(object sender, EventArgs e)
{
var pp = new powerpoint.Application();
var oPres=pp.Presentations;
pp.Visible = Office.MsoTriState.msoTrue;
powerpoint.Presentation oPre= oPres.Add(Office.MsoTriState.msoTrue);
powerpoint.Slides oSlides = oPre.Slides;
powerpoint.Slide oSlide = oSlides.Add(1, powerpoint.PpSlideLayout.ppLayoutText);
powerpoint.Shapes oShapes = oSlide.Shapes;
powerpoint.Shape oShape = oShapes[1];
powerpoint.TextFrame oTxtFrame = oShape.TextFrame;
powerpoint.TextRange oTxtRange = oTxtFrame.TextRange;
oTxtRange.Text = "All-In-One Code Framework";
string fileName = Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location) + "\\Sample1.pptx";
oPre.SaveAs(fileName,
powerpoint.PpSaveAsFileType.ppSaveAsOpenXMLPresentation,
Office.MsoTriState.msoTriStateMixed);
oPre.Close();
pp.Quit();
pp = null;
}
in the top I added
using powerpoint = Microsoft.Office.Interop.PowerPoint;
using Office = Microsoft.Office.Core;
using System.IO;
using System.Reflection;
Note: the program work fine at my end where I have office 2013 installed, but this error show up at my client pc
As already mentioned, the problem is that there is that Powerpoint is not installed on the client machine.
The powerpoint object is implemented in a COM type library and installed and registered when Powerpoint is installed. COM and .NET are totally different technologies. To use COM types from your .NET application, you don't use COM type directly but a special interop .NET assembly. This assembly doesn't contain any PPT functionality, it's just a wrapper that servers as a bridge between your .NET application and a COM type. An interop assembly does all the hard work for you and defines .NET types (from COM types) that you can use as other .NET classes, for example powerpoint.Application.
The interop assembly is just a normal .NET assembly. You can reference it as other .NET references (Embed Interop Types = false). In this case you need to distribute the interop DLL with your application. If you set Embed Interop Types = true, then the interop assembly is compiled and embedded directly to your application assembly. Moreover, only types and functions that are really used are embedded. So using this option has the advantage of optimized and single assembly.
But still, even when embedded, the interop info is just a wrapper over real COM type that must be installed on client machine. If it's not, you get the error.
See more details at https://msdn.microsoft.com/en-us/library/xwzy44e4.aspx
Your option is to force the client to install PPT or avoid Microsoft.Office.Interop.PowerPoint and use some third-party PPT library that you can distribute with your application.
"Embed interop types" does not mean "embed the Office program". You got this error for a very simple reason, Powerpoint is not installed on that machine.
I supposed that the term "interop types" could use an explanation. You automate an Office program by using COM, an interop technology that preceded .NET and has many similarities to .NET. It also has the notion of metadata, just like a .NET assembly has, the type information that a compiler uses when you add a reference to a .NET assembly. That metadata is called a "type library" in COM. The type library for Powerpoint is C:\Program Files (x86)\Microsoft Office\Office15\MSPPT.OLB for the Office 2013 version.
Type libraries are not exactly perfect, they suffer from ambiguity problems that don't matter much in an application written in C++ but do matter in .NET. So the .NET designers decided to not use a type library directly. And specified the Tlbimp.exe utility, it converts the library content to a .NET assembly. Directly usable by the CLR and managed language compilers without having to deal with the headache of interpreting the type library content.
By convention, the .NET assembly generated by Tlbimp.exe is called an "interop library". It is a one-to-one match with the type library. Most importantly in the context of this question, it only contains declarations, it does not contain executable code. The code remains in the COM component, written in a non-.NET language. Like the Office programs, written in C++.
The interop assembly is required at compile-time, the compiler uses it to know what types are implemented by the COM component so it can properly type-check the code you write and tell you when you get it wrong. It is also required at runtime, it is loaded by the CLR as soon as you use one of the COM types.
Having to deploy the interop assembly to the machine that executes your program is a burden. Not in the least because you often need a "Primary Interop Assembly", yet another mystical term that relates to solving the .NET type identity problem. A detail I'll skip here. The PIAs for Office are very large. It is often very murky exactly who is responsible for ensuring the PIAs are deployed on the machine. With programs failing when nobody takes care of it, a very common mishap.
A burden that Microsoft solved in .NET 4, two COM interface types are considered identical when they have the same [Guid], even when they came from a different assemblies. Which permitted a trick, the compiler can now copy the type declarations from the interop assembly into the output assembly. Only the ones you actually use. Solving both the deployment problem and the bulky interop library problem. You no longer need a PIA and don't have to deploy the interop assembly anymore. This is what "Embed interop types" means.
Long story short, metadata is not enough to execute code. You really do have to have the Office program installed on the target machine. And get this exact exception when it is not.
Funny how the most important information in an error message can be buried so deep its very difficult to see.
Class not Registered
This is your error. Basically the object you are trying to create does not exist or has not bee properly installed on the machine you are running your software on. Is powerpoint definitely installed?

Test compatibility between DLL in .NET

I'm working with Visual Studio 2010 and WinForms, .Net 4.0 (C#). I'm building an application with a lot of DLL (150). When I provide the application to my client, it's :
The Executable (.exe)
Dll files (.dll)
Each Dll is related to a module of the application, for example :
Ado.dll (provide access to database)
AccesManagement.dll (this module allows to manage users in the application)
Import.dll (this module allows the user to import data to the application)
etc.
When my client find a bug in the application, I correct it and I provide him impacted DLLs (in order to avoid him to test all the application). It can be for example the Import Dll.
The thing is, after some deliveries, we can have compatibility problems between Dll (a method that doesn't exist anymore in a new DLL for example). To avoid this problem, I would like to find a tool capable of checking compatibility between differents DLL.
I would like something like :
I specify the directory of the program to analyse (executable + Dll)
I launch the analyse
The program tells me for example : Error between Import.dll and Ado.dll, there is a class xxx in Import.dll expecting a method named xxx in the class xxx of Ado.dll
I've found some tools able to compare two versions of a Dll and provide added and removed members (Libcheck, ApiChange), but it's too complicated for me to do that because there are to many changes.
I think you may have a configuration management problem here -- at least as much as you've got a "compatibility" problem.
I'd recommend you find a way to track what versions of which assemblies each of your customers is using so that (1) you know what they're using when you decide what to ship, and (2) when they report bugs, you can replicate their setup (and thus, replicate their bug). If that sounds like a lot of work, it is. This is why a lot of software development shops take steps to ensure that there's a limit to the variation in setups among customers. It's nearly certain that you'll end up with some variation from customer-to-customer, but anything you can do to manage that problem will be beneficial.
Beyond the process implications, if you really need to create a "pluggable" environment, you probably need to create some interfaces for your objects to control the points where they connect, and you should probably look at Microsoft's Managed Extensibility Framework (MEF). MEF can help you manage the way objects "demand" behaviors from other objects.
I finally found a solution to my problem.
Since I'm :
Using SourceSafe and adding labels with the version of the application I'm building
Tagging each of my DLL with the version of the application
I built a program which is capable of :
Opening each Dll of a folder to read the version of the application in it
Getting from SourceSafe each project for the version specified in the DLL (With the functionnality "Get Label")
Then I just have to build the projet. If there is any compilation error, there is a compatibility problem.
This solution can avoid big compatibility problems, but you can still have compatibility problems which can't be seen with a compilation...

How can C# use a legacy DLL simply without registration(regsvr32)

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

In .NET, is there a need to register the DLL?

Is it necessary to register a compiled DLL (written in C# .NET) on a target machine.
The target machine will have .NET installed, is it enough to simply drop the DLL onto the target machine?
I think you're confusing things a little. Registering a dll has never been needed in order to use it.
Using a dll requires only to load it (given a known location or if the library is in the system path) and get the address of the function you wanted to use.
Registering the dll was used when distributing COM or ActiveX objects which need to add certain entries to the windows registry. In order to use a COM service (for example) you need to reference a GUID — that is, a unique identifier — which allows you to get a handle to the dll that implements the service (or provide access to it). Sometimes you can make reference to a fully-qualified name and get the same results.
In order for all that to work the dll needed to be registered. This "registration" process just creates several entries in the registry, but mainly these two: one associating a GUID with the location of the dll (so that you can reference it through the GUID without knowing where is it exactly located) and a second one associating the full name with the GUID. But again, this is just for COM or ActiveX objects.
When you develop an application in .NET, the libraries referenced on your project are automatically loaded when they're needed without you having to worry about locating or loading them. In order to to that, the framework checks two locations for the referenced libraries.
The first location is the application path.
The second location is the GAC.
The GAC (Global Assembly Cache) allows you to effectively register a dll to be used throughout the system and works as an evolution of the old registering mechanism.
So basically you just need to put the dll in the same folder of the application.
You need to "drop" it into a directory where the application needing it will find it.
If there are multiple applications, or you want to "drop" the file somewhere other than the application directory, you generally need to either adjust the PATH variable, or register the assembly in the Global Assembly Cache (GAC).
It is usually enough to drop the dll into the folder of your app on the target machine.
If the dll must be available to other applications then you may want to consider the GAC.
If you wish to access the assembly via com+. An example would be using a type defined in a .NET assembly from a non .NET application, such as a VB6 winforms app.
If you plan on accessing the assembly from another .NET application, you don't have to do anything. If your assembly has a strong name, it probably is a good idea to drop it in the GAC. Otherwise, just drop it in the directory of the application that will be referencing it.
One of the great selling points of .NET for the Windows platform when it came onto the scene is that by default, .NET assembly DLLs don't have to be registered and can be consumed privately by an application by merely putting them in the same folder as the EXE file. That was a great stride forward because it enabled developers to avoid the fray of DLL/COM hell.
Shared DLL/COM modules proved to be one of the greatest design mistakes of Windows as it lead to instability of applications that users installed. Installing a new app could well screw up an app that had been working just fine - because the new app introduced newer versions of shared DLL/COM modules. (It proved in practice to be too much of a burden for developers to properly manage fine-grained version dependencies.)
It's one thing to manage versions of modules with a build repository system like Maven. Maven works extremely well doing what it does.
It's an entirely different matter, though, to deal with that problem in an end-user runtime environment spread across a population of millions of users.
The .NET GAC is by no means a sufficient solution to this age-old Windows problem.
Privately consumed DLL assemblies continue to be infinitely preferable. It's a no-brainer way to go as diskspace is extremely cheap these days (~$100 can by a terabyte drive at Fry's these days). There is nothing to be gained with sharing assemblies with other products - and yet company reputation to loose when things go south for the poor user.
Actually there is NO need to register a dll in .NET on the target machine.
If you reference a .dll in your application, click on the referenced .dll under references in your project, look at the properties and set Isolated to TRUE.
This will now automatically include this .dll in your project and your application will use the copy of the .dll included in your project without any need to register it on the target system.
To see a working Example of this look here:
http://code.msdn.microsoft.com/SEHE
The .dll in question will need to be registered on the system where you build your application for this to work properly. However once you build your project, there will not be any need to register the .dll in question on any system you deploy your application or program.
An additional benefit of using this method, is that even if in the future, another .dll is registered with the same name on the target system in question, your project will continue to use the .dll you deployed with. This is very handy where a .dll has many versions and you wish to maintain some stability, like using the one you tested with, yet all other applications will use the registered .dll unless they use the isolated = true method as well.
The example above is one of those cases, there are many versions of Skype4COM which is a Skype API .dll and can change often.
This method allows the above example to use the API .dll that the project was tested with, each time a user installs a new version of Skype, it is possible that a modified version of this .dll is installed.
Also, there are some Skype clients that do not install this .dll, the business version of the Skype client for example, is smaller, and does not include this .dll, so in this case, the project does not fail on that .dll missing and not being registered because it is included in the project as isolated = true.
An application can use a .NET dll by simply having it present in the same folder with the application.
However if you want other third-party applications to find the DLL and use it they would also have to include it in their distribution. This may not be desirable.
An alternative is to have the DLL registered in the GAC (Global Assembly Cache).

Categories