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.
I created a test COM project for .Net 4.0. Then I register it with the regasm:
RegAsm /codebase TestCom.dll /TLB
And it works correctly in JavaScript:
var app = new ActiveXObject("TestCom.TestClass");
app.Message1("123");
I want to use TestCom.TestClass from another C# project for .Net 3.5, but when I try to add the reference to this project, I get an error about higher framework version. The "Add Reference" dialog(section COM) only shows reference to a tlb file, not dll.
Is this the way it should be? When I try to add a reference to the tlb file I get the error:
"Add a reference to the .NET assembly instead"
How can I create an instance of TestCom.TestClass from another C# project for .Net 3.5?
The IDE can tell from the type library that you selected that the COM server is implemented in .NET. And it just refuses to let you add it, doing this doesn't make sense. It will work just as well, in fact better, when you add a reference to the .NET assembly instead.
It's not like you can't fool the machine, you can use late-binding with the dynamic keyword. No way for the IDE to interfere with that. But you are not actually testing the COM server the way an unmanaged client is going to use the server. The CLR will discover at runtime that the COM server is in fact a .NET server and shortcuts the plumbing. In other words, it will not create an RCW for the server. And the server won't create a CCW, this now works the exact same way it would have worked if you had added the assembly reference.
So it really does make no sense to do it this way. Just add the assembly reference and enjoy the many advantages you get from having IntelliSense support and static type checking. And test your COM server code the way you test any .NET library. What you don't test, and fundamentally cannot test, is the COM interop layers. Which is okay, it's not like it ever really is a problem. And if it is some for some mysterious reason then it is not like you can ever do something about it, the COM interop inside the CLR is a black box anyway.
All you need to do in addition to testing the server code is to perform an integration test. Just make sure that the actual unmanaged client can in fact use the server. You did, you already know it works well from Javascript.
I want to explore this path of working with Excel dynamically.
I want to use Excel in my C# application without including dlls and stuff. I would just check first if the required Excel version is installed and run it.
First I want to get all the types I need but I can't get hold of them:
// works
Type typeExcel = Type.GetTypeFromProgID("Excel.Application");
object excel = Activator.CreateInstance(typeExcel);
object workbooks = typeExcel.InvokeMember("Workbooks", BindingFlags.GetProperty, null, excel, null);
// this doesn't work, returns null
Type typeWorkbooks = Type.GetTypeFromProgID("Excel.Workbooks");
Without the correct types I can't invoke members. So what am I doing wrong ? How do I load all types I need and know that they are there ? My current Excel version is 2003.
Reasons for this: If I include COM Libraries that are not installed on the target system my application wont start. If I load them dynamically I can check for their existence and notify the user about missing functionality.
Use dynamic.
Type typeExcel = Type.GetTypeFromProgID("Excel.Application");
dynamic excel = Activator.CreateInstance(typeExcel);
excel.Visible = true;
dynamic workbooks = excel.Workbooks;
workbooks.Add();
workbooks.Add();
Also see this answer.
If I include COM Libraries that are not installed on the target system my application wont start.
The cure you are looking for is considerably worse than the problem. There isn't anything that pretty about late-binding Office code as you are attempting in your snippet. The dynamic keyword supported in C# version 4 certainly makes the syntax a lot friendlier. There's however nothing friendly about it at code-writing time, you won't get any IntelliSense. And nothing friendly at run-time, the mistakes you make at coding-time will cause exceptions and late-binding has considerable overhead.
There are simple counter-measures to ensure the interop assemblies are available:
Ask your user to install the Office Primary Interop Assemblies (PIAs).
It is quite rare to actually need a PIA, it is only necessary when you expose an Office type in your own public methods and use it in another assembly. Select the Microsoft.Office.Interop assemblies in your Reference node and set their Copy Local property to true. Rebuild and you'll get those assemblies in your build directory. Copy them along with your own executables to the client machine.
VS2010 and up have the Golden Solution to this deployment detail. Select the interop assembly in your Reference node and set the Embed Interop Types property to True. The interop declarations will now be merged into your own executable and you don't have to deploy the interop assemblies or PIA anymore.
Since you accepted the dynamic solution, available on VS2010 and up, the last bullet is the one you want.
I was in the process of adding a reference to a dll when I noticed that the vast majority instances of the Microsoft namespace has a uppercase M and on rare occasions they have a lower case m.
Is there a reason or any logic for this?
Does anyone know the reasoning for this decision by Microsoft?
Those entries you pointed out are not normal .NET framework assemblies. And they are auto-generated by tooling. Two good reasons they don't follow .NET framework naming conventions. They are PIAs, Primary Interop Assemblies. They contain the declarations retrieved from a COM component's type library, converted into .NET metadata to make it easy for the CLR to interop with the COM component. The types in these PIAs have the [ComImport] attribute.
Tlbimp.exe is the tool used to auto-generate these assemblies, the /primary command line option generates a PIA. The ones you got from Microsoft are slightly different from the ones you'll get when you run Tlbimp.exe yourself. For one, Microsoft includes a version resource in the assembly. For another, the names for these PIAs are not the default name that Tlbimp.exe generates. So you are seeing what the build engineer at Microsoft typed for the /out command line option. Clearly he's not paying much attention to casing.
Microsoft.msxml is the PIA for c:\windows\system32\msxml3.dll, very commonly used in older code to read and write XML documents. Microsoft.mshtml is the PIA for c:\windows\system32\mshtml.tlb, the type library for the DOM interface supported by Internet Explorer and one you'll need when you want to dig through the HTML elements of a web page. You can look at these type libraries in their "native" format with the Oleview.exe tool, File + View Typelib. What you'll see otherwise looks very similar to what you'll see with Object Browser, except that they are expressed in IDL, Interface Description Language, the language that was originally used to generate these type libraries.
PIAs are mostly an historical artifact, the Embed Interop Type feature available since .NET 4 made them unnecessary.
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!