Click here to Skip to main content
Click here to Skip to main content

SafeCOMWrapper - Managed Disposable Strongly Typed safe wrapper to late bound COM

By , 19 Sep 2005
 

Sample Image - SafeCOMWrapper.gif

Introduction

There are several problems using COM from .NET:

  • You cannot implement the Dispose pattern by utilizing the "using" block in order to safely dispose COM references.
  • You cannot guaranty COM references are finalized. There's no way to implement "~Destructor()" for COM references.
  • COM reference is not released when a call to Marshal.ReleaseComObject is skipped due to an Exception.
  • When you "Add Reference..." to a COM library, the reference is version specific. So, if you add a reference to the Office 2003 COM library, it does not work properly when deployed to Office 2000.
  • The only solution to version independent COM is to use Late Bound operations but you miss all the features of a strongly typed language.

Let's solve all these problems. We want to use a Managed strongly typed approach to Late Bound COM operations and also utilize the Dispose pattern on COM objects. The solution proposed here works for any COM object which can be Microsoft Office COM libraries, IE & DHTML objects and even your own COM objects. You should use this approach whenever you are dealing with any type of COM library.

Before going to the solution, let’s learn a bit of background on RealProxy and method interception.

Update

  • September 9, 2005: COM Event Support added. Thanks to Richard Deeming for adding the COM Event support and also the ByRef parameter support. He has made this a complete solution.

Introducing RealProxy and method interception

System.Runtime.Remoting.Proxies namespace offers us a class called RealProxy. You can use this class to create a proxy to any class and provide method interception. Method interception means you can intercept any method call to the target object. You can override RealProxy's Invoke method and any method called to the actual object is intercepted by the Invoke method of the proxy. Inside the Invoke method, you can either call the actual object's method, or can call a different method. You are free to do anything you like in this Invoke method.

Figure 1: How proxy works

TransparentProxy is another type of proxy which is dynamically generated for a given type by the runtime. The actual method interception is done by the TransparentProxy. It is a hidden thing. You will never see its existence in the code. But this is the proxy which intercepts all method/property calls on a given type and then redirects the call to RealProxy. RealProxy itself does not intercept any method calls; the TransparentProxy actually provides this service of capturing a method call and calling the Invoke method of RealProxy.

RealProxy class provides a method GetTransparentProxy which you can use to create a transparent proxy for a given type. For example, if you are interested to intercept all method calls to the IList interface, you can create a TransparentProxy for the IList interface and all methods declared in the IList interface is intercepted by the TransparentProxy.

You can learn a lot about RealProxy from this MSDN TV show.

Extending RealProxy

You can extend RealProxy and make custom intercepting classes which intercept method calls to custom objects. For example, if you have a class named MyObject which is a regular class, you can create a MyObjectProxy extending the RealProxy and intercept all calls to MyObject’s instances and provide some custom services like logging, security check, resource cleanup etc.

Let’s see a simple example of method interception:

public class MyObject : MarshalByRefObject
{
        public void DoSomething()
        {
               Debug.WriteLine("DoSomething called");
        }
}

This is a simple object. Note: The object extends MarhalByRefObject. This is a problem because we do not want to destroy our object model by extending from this class. We will find a solution to this problem soon.

Now we will be creating the proxy class:

public class MyObjectProxy : RealProxy
{
        private MyObject _ActualObject;

        public static MyObject Create()
        {
               MyObject obj = new MyObject();
               MyObjectProxy proxy = new MyObjectProxy( obj );
               return proxy.GetTransparentProxy() as MyObject;
        }

        public MyObjectProxy(MyObject obj) : base( typeof(MyObject) )
        {
               _ActualObject = obj;
        }

Here we are creating a proxy object which remembers the reference to the actual object. We will need this reference when we will be calling the actual method during interception. When the proxy is created, we generate a dynamic transparent proxy and return the reference to the proxy instead of the actual object.

Figure 2: Client sees the interface to the object, but it is actually interface to proxy.

Here’s how we use the class:

[STAThread]
public static void Main()
{
        MyObject obj = MyObjectProxy.Create();
        obj.DoSomething();
}

Now let’s look at the Invoke method where the actual work is done:

public override IMessage Invoke(IMessage msg)
{
        Debug.WriteLine(" -- Intercepted -- ");
        
        // We are assuming it is a method call
        IMethodCallMessage callMessage = msg as IMethodCallMessage;

        // Do a lot of things here. Log the method call. You will get all the
        // arguments from the msg. Do security checks. Validate arguments.
        // Anything you can do here. The world is yours.

        // Call the actual method
        this._ActualObject.DoSomething();

        // Construct a return message which contains the return value 
        ReturnMessage returnMessage = new ReturnMessage( null, null, 
               0, callMessage.LogicalCallContext,
               callMessage );

        return returnMessage;
}

Let’s see what happens in this method:

  • Invoke method is called for any method or property access.
  • Information about the call is available in the IMessage message.
  • IMessage is converted to IMethodCallMessage which contains all the information about a method call.
  • We can write anything we want in this method before and after calling the actual object’s method.
  • Finally construct a return message which contains information about the return value (void functions have null return value) and all the out parameters.

No more MarshalByRef

We do not want to inherit our objects from the MarshalByRef object but still want to use RealProxy. The solution is to create an interface for the class we want to intercept. So, for our simple class, we will be creating an interface which declares all the public methods and properties:

public interface IMyObject
{
    void DoSomething();
}
public class MyObject : IMyObject
{
    public void DoSomething()
    {
        Debug.WriteLine("DoSomething called");
    }
}

See, no more MarshalByRef.

Now we need to modify the Real Proxy a bit:

public class MyObjectProxy : RealProxy
{
    private IMyObject _ActualObject;

    public static IMyObject Create()
    {
        MyObject obj = new MyObject();
        MyObjectProxy proxy = new MyObjectProxy( obj );
        return proxy.GetTransparentProxy() as IMyObject;
    }

    public MyObjectProxy(IMyObject obj) : base( typeof(IMyObject) )
    {
        _ActualObject = obj;
    }

The changes are pretty simple. We have replaced all MyObject with IMyObject.

So, the usage will also be changed this way:

IMyObject obj = MyObjectProxy.Create();
obj.DoSomething();

Although introducing interface for each concrete class seems like a bad idea, actually it’s a very good idea to always have interfaces for your concrete classes and write code against the interfaces, not the concrete classes. All the design pattern books, object oriented purists will tell you the same. There is a long list of benefits of writing code against an interface than a concrete class. Hundreds of scenarios can be shown in favor of this idea. However, that’s not our discussion topic.

Making managed, disposable, strongly typed but late bound COM wrappers

Now I will be introducing an interesting concept which may be hard to grasp but is very simple. Here it goes:

We will make our own hand coded interfaces for COM objects.

Instead of adding references to COM objects from Visual Studio, we will create a similar interface by ourselves. So, let’s say we want to use Outlook’s COM interface. Instead of adding a reference to the Outlook COM library, we will make our own interface for the methods and properties we want to use. Hard to grasp? An example will make it easier.

Let’s make an interface for the Outlook.Application object:

public interface Application : IDisposable
{
   string Name { get; }
   void Quit();
}

We are making a subset of the actual complex Outlook.Application object. There are three reasons for doing so:

  • We are not interested to use all the functionality exposed by the COM object.
  • We want to make a version independent interface for Outlook. Only the methods and properties that we expect in all versions of Outlook will be in this interface.
  • We need the IDisposable interface so that we can use the using construct. If we add a reference to Outlook’s library, we cannot modify it and extend the interfaces from the IDisposable interface.

Now comes the great DisposableCOMProxy. It has four responsibilities:

  • Create COM objects.
  • Provide a disposable interface for COM so that you can use the COM object inside a using block.
  • Safely dispose COM reference.
  • Provide strongly typed interfaces to COM type but still perform late bound calls to the actual COM object.
public class DisposableCOMProxy : RealProxy
{
        public object COM;

        /// <summary>
        /// We will be using late bound COM operations. The COM object
        /// is created from the Prog ID instead of CLSID which makes it
        /// a version independent approach to instantiate COM.
        /// </summary>
        /// <param name="progID">Prog ID e.g. Outlook.Application</param>
        /// <returns></returns>
        private static object CreateCOM( string progID )
        {
               // Instantiate the COM object using late bound
               Type comType = Type.GetTypeFromProgID( progID, true );
               return Activator.CreateInstance( comType );
        }

        public static IDisposable Create( string progID, Type interfaceType )
        {       
               object theCOM = CreateCOM( progID );
               DisposableCOMProxy wrapper = 
                  new DisposableCOMProxy( theCOM, interfaceType );
               return wrapper.GetTransparentProxy() as IDisposable;
        }

        public DisposableCOMProxy( object theCOM, 
               Type interfaceType ) :base( interfaceType )
        {
               this.COM = theCOM;
        }

The Create static method takes a ProgID and an interface type (e.g. Application) which defines the methods and properties of the COM. Then it creates a wrapper proxy which holds the COM reference.

Now, we need to intercept all method calls to the Application interface and delegate the call to the actual COM reference.

The Invoke method does the following:

  • Checks what the method name is. If it is Dispose then it releases the COM reference by calling Marshal.RelaseComObject and exits.
  • For any other method, it redirects the call to the actual COM object.

Here’s the code of the Invoke method:

public override IMessage Invoke(IMessage msg)
{
        IMethodCallMessage callMessage = msg as IMethodCallMessage;

        object returnValue = null;

        MethodInfo method = callMessage.MethodBase as MethodInfo;
        
        // We intercept all method calls on the interface and delegate the method
        // call to the COM reference.
        // Only exception is for "Dispose" which needs to be called on this class
        // in order to release the COM reference.
        // COM reference does not have Dispose method
        if( method.Name == "Dispose" )
        {
               this.Release();
        }
        else
        {
               object invokeObject = this.COM;
               Type invokeType = this.COM.GetType();

               // Get Property called: Retrieve property value
               if( method.Name.StartsWith("get_") )
               {
                       string propertyName = method.Name.Substring(4);
                       returnValue = invokeType.InvokeMember( propertyName, 
                               BindingFlags.GetProperty, null,
                               invokeObject, callMessage.InArgs );
               }
                       // Set Property Called: Set the property value
               else if( method.Name.StartsWith("set_") )
               {
                       string propertyName = method.Name.Substring(4);
                       returnValue = invokeType.InvokeMember( propertyName, 
                                     BindingFlags.SetProperty, null,
                                     invokeObject, callMessage.InArgs );
               }
                       // Regular method call
               else
               {
                       returnValue = invokeType.InvokeMember( method.Name, 
                                     BindingFlags.InvokeMethod, null,
                                     invokeObject, callMessage.Args );
               }
        }

        // Construct a return message which contains the return value 
        ReturnMessage returnMessage = new ReturnMessage( returnValue, null, 
               0, callMessage.LogicalCallContext,
               callMessage );

        return returnMessage;
}

Remember all property calls are also method calls. .NET runtime dynamically generates “get_PropertyName” and “set_PropertyName” methods on the proxy in order to intercept property calls.

So, we have our disposable COM wrapper, now we can happily use COM objects without worrying about memory leaks:

public static void Main()
{
        // Instantiate Outlook.Application and wrap the COM with the Application
        // interface so that we have a strongly type managed wrapper to late bound 
        // COM
        using( Application app = 
             ( Application)DisposableCOMProxy.Create( "Outlook.Application", 
              typeof( Application ) ) )
        {
               Debug.WriteLine( app.Name );
        }
}

The release of COM reference is ensured in two ways:

  • Dispose method of the COM wrapper releases the COM reference.
  • Destructor of the COM wrapper releases the COM reference. Even if you forget to dispose, the reference will be properly released when the object is finalized by the garbage collector.
/// <summary>
/// Safely release the COM object
/// </summary>
private void Release()
{
        if( null == this.COM ) return;

        Marshal.ReleaseComObject( this.COM );
        this.COM = null;

        Debug.WriteLine( "COM released successfully" );
}

~DisposableCOMProxy()
{
        this.Release();
}

So, by doing all these, we are actually simulating an IDisposable interface on a COM object.

Now you have a version independent managed Outlook wrapper which runs on all versions of Outlook. Try this on Outlook 2000, 2002, XP and 2003.

However, this tiny application interface actually provides nothing useful. You need a full fledged interface collection for the entire Outlook library. Moreover we need to address another issue:

How to handle objects returned by any property or method of a COM object? The returned objects are pure COM objects with no wrapper. For example, if you call the ActiveExplorer() method on the Application object, it returns the instance of the running Explorer. We need to provide a COM wrapper for the returned object in order to write strongly typed code and also implement the Disposable pattern.

The solution is simple. Inside the Invoke method, we analyze what is in the returnValue. If it is an object, then definitely it is a COM object. So, we need to do the following for all returned types which are object:

  • Get the method definition from the Type of the interface the proxy is intercepting (e.g. Application).
  • Find out what is defined as the return type of the method or property being called, e.g., Explorer ActiveExplorer().
  • If the return type is an interface, then we make a COM wrapper on the returned object using the interface type.

So, the Invoke method gets the following additional code:

// Now check if the method return value is also an interface. if it is an 
// interface, then we are interested to intercept that too
if( method.ReturnType.IsInterface && null != returnValue )
{       
        // Return a intercepting wrapper for the com object
        DisposableCOMProxy proxy = 
            new DisposableCOMProxy( returnValue, method.ReturnType );
        returnValue = proxy.GetTransparentProxy();
}

We are also modifying the interfaces we have defined in order to introduce the Explorer type.

public interface Application : IDisposable
{
        string Name { get; }
        Explorer ActiveExplorer();
        void Quit();
}
public interface Explorer : IDisposable
{
        string Caption { get; }
        void Close();
        void Display();
        void Activate();
}

This way, we can use more features of Outlook in a safe managed, strongly typed, disposable way which still works over late bound calls to COM and thus makes it a lot safer.

public static void Main()
{
        using( Application app = 
             ( Application)DisposableCOMProxy.Create( "Outlook.Application", 
               typeof( Application ) ) )
        {
               Debug.WriteLine( app.Name );

               using( Explorer explorer = app.ActiveExplorer() )
               {
                       Debug.WriteLine( explorer.Caption );
               }
        }
}

What is in the Source Code

You will get a complete interface collection of Outlook library. I have decompiled the entire interop assembly generated from Outlook, cleaned every single method and property to get rid of those nasty attributes.

So, instead of this:

[ComImport, TypeLibType((short) 4160), 
            Guid("00063001-0000-0000-C000-000000000046")]
public interface _Application
{
      [DispId(61440)]
      Application Application { [return: MarshalAs(UnmanagedType.Interface)] 
                  [MethodImpl(MethodImplOptions.InternalCall, 
                  MethodCodeType=MethodCodeType.Runtime), DispId(61440)] get; }
      [DispId(61450)]
      OlObjectClass Class { [MethodImpl(MethodImplOptions.InternalCall, 
                  MethodCodeType=MethodCodeType.Runtime), DispId(61450)] get; }
      [DispId(0xf00b)]
      NameSpace Session { [return: MarshalAs(UnmanagedType.Interface)] 
                  [MethodImpl(MethodImplOptions.InternalCall, 
                  MethodCodeType=MethodCodeType.Runtime), DispId(0xf00b)] get; }
      [DispId(0xf001)]
      object Parent { [return: MarshalAs(UnmanagedType.IDispatch)] 
                  [MethodImpl(MethodImplOptions.InternalCall, 
                  MethodCodeType=MethodCodeType.Runtime), DispId(0xf001)] get; }
      [DispId(0x114)]
      Assistant Assistant { [return: MarshalAs(UnmanagedType.Interface)] 
                  [MethodImpl(MethodImplOptions.InternalCall, 
                  MethodCodeType=MethodCodeType.Runtime), DispId(0x114)] get; }

You will find the following in the provided source code:

public interface Common : IDisposable
{
        Application Application { get; }
        NameSpace Session { get; }
        object Parent { get; }
        OlObjectClass Class { get; }
}

public interface Application : Common
{
        string Name { get; }

        Explorer ActiveExplorer();
        Explorers Explorers{ get; }

        string Version { get; }
        Inspector ActiveInspector();
        object CreateItem(OlItemType ItemType);
        object CreateItemFromTemplate(string TemplatePath, 
                                         object InFolder);
        object CreateObject(string ObjectName);
        NameSpace GetNamespace(string Type);
        
        void Quit();
}

An entire collection of nice and clean interfaces.

I have also modified the interfaces in order to provide some sort of generalization. The object model (or you can say interface model) is as follows:

In the source code, you will find the following:

  • COMWrapper.cs - This is the final wrapper that you will use.
  • TestOutlook.cs – A complete test of various operations on Outlook.
  • MyObject.cs – Simple example of method interception.
  • OfficeWrappers.cs – Almost complete Outlook 2003 interface set.
  • SimpleOutlookWrapper.cs – All the code you have seen so far in this article.

How to make your own COM wrapper

Here are the steps for making your own COM wrapper:

  • Create an interface which defines the signatures of the methods and properties of the COM object, e.g.: Application.
  • Create all other related interfaces and enumerations, e.g., Explorer, olItemType.
  • Use the DisposableCOMProxy.Create method to create the first COM object.
  • Use all objects inside using( ... ) block to ensure they are properly disposed.

Conclusion

The COMWrapper proxy is a generic COM wrapper. It does not depend on Office. An Office wrapper is shown as an example. You can use this wrapper on any COM object including IE Browser Control reference, DHTML object library, any Office Application’s COM library and even your own COM library. You should use it everywhere whenever you are dealing with COM objects. Do not trust your instinct that you will never forget to call Marshal.RelaseComObject. Use the proven Dispose Pattern on COM objects.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Omar Al Zabir
Architect BT, UK (ex British Telecom)
United Kingdom United Kingdom
Member

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionAutomatically dispose dependent objectsmemberellarr26 Jul '12 - 8:31 
As is, this set of classes will handle properly disposing the COM instances you wrap with a using statement or manually dispose of. However, the dependent COM instances that are created as a result of retrieving objects from accessors, etc still end up not being disposed until the garbage collector reclaims the object instance.
 
I prefer to have the top level object and all objects created as a result of making calls to the top level object disposed at the same time (at the end of my using statement). In order to do so, I made the following changes.
 
I added the following to the using of COMWrapper
using System.Collections.Generic;
I added the following to the private member variable section of COMWrapper
private List<IDisposable> _childWrappers = new List<IDisposable>();
I added the following to the COMWrapper.Dispose(bool) method
_childWrappers.ForEach(w => w.Dispose());
_childWrappers.Clear();
And at each call to COMWrapper.Wrap, I add the result to the _childWrappers list before returning it.
 
If you're using the enumerator code posted earlier (Thanks Sefatoma!), you probably want to make the ComEnumerator class implement IDisposable, use the same disposable pattern then add the ComEnumerator to the _childWrappers of COMWrapper.
 
If you implement this change, you should make sure that you do not retain references to dependent objects after the end of the using statement as the COM objects associated with such references will already be released.
QuestionSupport DispID invocation on accessors/modifiersmemberellarr26 Jul '12 - 8:06 
This has been an extremely helpful set of classes. I recently ran into an issue that could only be resolved by invoking the accessors/modifiers by DispID value. I figured others might find this useful, so...
 
First, I added an attribute that can be added to our intercept interfaces
using System;
using System.Reflection;
 
namespace ManagedOffice
{
    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
    public sealed class ComDispIdAttribute : Attribute
    {
        private int _value;
 
        public static ComDispIdAttribute GetAttribute(MethodInfo methodInfo)
        {
            if (null == methodInfo) throw new ArgumentNullException("methodInfo");
 
            Type attributeType = typeof(ComDispIdAttribute);
            object[] attributes = methodInfo.GetCustomAttributes(attributeType, false);
 
            if (null == attributes || 0 == attributes.Length) return null;
            return (ComDispIdAttribute)attributes[0];
        }
 
        public ComDispIdAttribute(int value)
        {
            _value = value;
        }
 
        public int Value
        {
            get { return _value; }
        }
    }
}
 
Then, in the COMWrapper.Invoke method, just before the declaration of the returnValue object, I added
ComDispIdAttribute dispIdAttribute = ComDispIdAttribute.GetAttribute(method);
 
Then further down in the COMWrapper.Invoke method, I added the following *immediately* before the call to invokeMember
if (dispIdAttribute != null)
{
    methodName = String.Format("[DispID={0}]", dispIdAttribute.Value);
    flags = flags | BindingFlags.Instance;
}
 
Once that's done, you should be able to declare your intercept interface with the ComDispId attribute like so:
[ComProgId("RFComAPI.FaxServer")]
public interface MyObject : IDisposable
{
    MyOtherObject this[int Handle] { [ComDispId(13)] get; }
}
 

QuestionEvent Handling with Excel 2010memberbuehlert4 Apr '12 - 21:46 
Hello,
I would like to ask if anybody has implemented a working solution which uses the great SafeComWrapper classes with Excel that needs to fire events.
I am currently running into problems that I receive an exception "Specified cast is not valid" when trying to use events. I checked the Outlook sample (which works great) how to setup the events.
Before I post some code as well I'd like to know if there's anything special about the Excel event handling or if it should work exactly the same way as in the Outlook sample.
 
Thanks in advance,
Tom
GeneralMy vote of 5memberlRUSHl3 Apr '12 - 16:55 
In this article I found answers on some my questions concerning COM Interop in .NET
 
Thank you, Omar!
QuestionExecuting Macrosmemberbuehlert8 Mar '12 - 22:16 
Dear Omar,
 
Thanks for the excellent article. I needed to automate MS PowerPoint and found your article...it is just amazing to use Office stuff without DCOM relationships.
 
Now I'd like to ask you if you have ever tried to execute a VBA Macro function with the same method.
 
What I did was to add a method into my PowerPointWrapper Interface:
 
object Run(string macroName, params object[] p);
 
I wrote a quite simple Addin with three methods:
1. Show "Hello" in a MessageBox (test)
2. Return a string "Hello" (test2)
3. Expects a string parameter and will return it again (test3)
 
The call to the macro looks like this:
 

1. app.Run("MyAddin.ppam!test", new Object[] {}");
 
This works perfectly -> There is a Message Box coming up with "Hello"
 
2. string r = (string)app.Run("MyAddin.ppam!test2", new Object[] {}");
 
This is my first problem -> r is always null
 
3. string r = (string)app.Run("MyAddin.ppam!test3", new Object[] { "Hello" }");
 
This becomes even worse: There is an exception saying "Invalid request, sub or function not defined".
 
Any hints from your side?
 
Thanks a lot in advance.
 
Best regards,
Tom
GeneralMy vote of 5membermanoj kumar choubey28 Feb '12 - 18:14 
Nice
QuestionLock being held on DLL [modified]memberdendle22 Sep '10 - 22:14 
Hello Omar,
 
This is a very impressive piece of work, and it helped me very much! I needed to target .NET v2 in my app, and now using COM is very easy! Thank you!
 
I am however facing a small problem that I hope you can help me with.
 
I am using your DisposeableCOMProxy in the usual way - invoking a COM object and calling Dispose().
 
However, straight after I call one of my COM objects, and Dispose it, i install and MSI which updates that COM object (replacing a DLL with a newer one, and re-registering it)
 
The MSI is reporting that my app still has a lock on the dll file, even though i've called dispose on the DisposableCOMProxy - I even forced a garbage collection and called WaitForPendingFinalizers.
 
Here is an extract from the MSI log:
MSI (s) (40:74) [14:38:06:141]: RESTART MANAGER: Will attempt to shut down and restart applications in no UI modes.
MSI (s) (40:74) [14:38:06:141]: RESTART MANAGER: Detected that application with id 3676, friendly name 'Updater App', of type RmUnknownApp and status 1 holds file[s] in use.
MSI (s) (40:74) [14:38:06:141]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (40:74) [14:38:06:141]: Note: 1: 2262 2: Error 3: -2147287038
MSI (c) (B0:F8) [14:38:06:172]: RESTART MANAGER: Session opened.
MSI (s) (40:74) [14:38:06:203]: RESTART MANAGER: Successfully shut down all applications in the service's session that held files in use.
MSI (c) (B0:F8) [14:38:06:203]: RESTART MANAGER: Successfully shut down all applications that held files in use.

 
Im not sure how I can remove this lock - can I somehow unload this dll from the AppDomain?
 
Any help you can provide would be greatly appreciated!
 
Kind Regards,
 
Matt

modified on Friday, September 24, 2010 4:10 AM

QuestionRe: Lock being held on DLLmemberthomasholme27 Sep '10 - 5:14 
I have the same problem. Did you solve it?
/Thomas
AnswerRe: Lock being held on DLLmemberthomasholme29 Sep '10 - 23:42 
We found the solution in Google:
 
Add this to COMWrapper class.
[DllImport("ole32.dll")]
static extern void CoFreeUnusedLibraries();
 
Call it in Dispose line 130
 
this.Dispose(true);
CoFreeUnusedLibraries();
GC.SuppressFinalize(this);
 
Smile | :)
GeneralMy vote of 5memberagzis200810 Aug '10 - 4:39 
Great article!!!
GeneralExplorer.Selections vs. Explorer.Selectionmemberyarivtal17 Nov '09 - 22:28 
I've been trying to use the Selections property in the Explorer interface, but got an exception indicating there is no such method name in the COM object.
I used the Reflector app to check the office PIA I have (of outlook 2003), and found out that there is a Selection property (in singular instead of plural), that returns a Selection interface instead of a collection of Items.
Is this an office versions issue? Did the interface change between outlook versions? If so, does anyone know which version supports which interface?
Questionlicense?memberyarivtal13 Nov '09 - 8:43 
Great stuff!
 
I was about to try and solve this myself for my app, but no where near as elegant.
 
One thing bothers me though:
There's no license attached, so it's not clear if I can use your code for shareware or commercial stuff?
AnswerRe: license?mvpOmar Al Zabir14 Nov '09 - 0:20 
Applied Code Project license, whatever that is.
 
Regards,
Omar AL Zabir
Visual C# MVP

QuestionInvoking COMObject overloaded method [modified]memberbbelliveau448 Jun '09 - 12:11 
Hi, I am running into an issue where the COMObject that I am wrapping exposes an overloaded method that I want to call.
 
Basically, the COMObject implements 2 interfaces.
Both of these interfaces expose a method by the same name, just with different parameter signatures.
I want to call 1 implementation of this method.
 
When trying to invoke the method, an exception is being returned.
I am able to call other non-overridden methods on the object.
 
Is this due to the fact that the COMWrapper class is not invoking a QI directly?
In other words, is the COMWrapper class creating an instance of the COMObject and handing back a reference to the default interface implemented by the class?
 
Any idea how to resolve this?
 
Thanks in advance.
 
modified on Wednesday, June 10, 2009 9:32 AM

QuestionHow can you cast object to different interfaces?membersafepage29 May '09 - 8:51 
Hi,
 
I am currently wrapping MSHTML like so:
 
static Guid CLSID_WebBrowser = new Guid("8856F961-340A-11D0-A96B-00C04FD705A2");
 
// get Type for webbrowser COM object using CLSID
Type webBrowserType = Type.GetTypeFromCLSID(CLSID_WebBrowser, true);
 
// use Activator to create an instance (like CoCreateInstance would) of IUnknown
objet pUnknown = Activator.CreateInstance(webBrowserType);
 
// Webbrowser COM object should support many interfaces, e.g. IOleObject and IWebBrowser2
IOleObject pOleObject = _pUnknown as IOleObject;
IWebBrowser2 pWebBrowser2 = _pUnknown as IWebBrowser2;
 
Any idea how can achieve this with your approach?
 
If I create the WebBrowser using:
IWebBrowser webBrowser = (IWebBrowser)COMWrapper.CreateInstance(typeof(IWebBrowser))
 
where IWebBrowser is [ComProgId("Shell.Explorer")]
 
How can I tell webBrowser to start serving up the IOleObject interface instead?
 
Thanks
GeneralHot to connect ActiveExplorermember_rush_22 Apr '09 - 7:54 
Hi,
 
very nice work. Smile | :)
Please could you tell me how can i connect the app.ActiveExplorer()?
I want to read out the Mailsubject of the actualy active mailwindow.
With the Microsoft.Office.Interop.Outlook.dll i use the following code code.
Is it possible to do this?
c#
 
{
             Microsoft.Office.Interop.Outlook.Application app = new Microsoft.Office.Interop.Outlook.ApplicationClass();
             Microsoft.Office.Interop.Outlook.NameSpace NS = app.GetNamespace("MAPI");
             Microsoft.Office.Interop.Outlook.MAPIFolder objFolder = NS.GetDefaultFolder(Microsoft.Office.Interop.Outlook.OlDefaultFolders.olFolderInbox);
             Microsoft.Office.Interop.Outlook.MailItem objMail;
             Microsoft.Office.Interop.Outlook.Items oItems = objFolder.Items;
           
                if (app.ActiveExplorer() != null && app.ActiveInspector() != null)
                {
                    for (int i = 1; i <= app.ActiveExplorer().Selection.Count; i++)
                    {
 
                       objMail = (Microsoft.Office.Interop.Outlook.MailItem)app.ActiveExplorer().Selection[i];
                      
                        MessageBox.Show("Subject  : " + objMail.Subject.ToString());
                       
                    }
                }
}
 
thank you very much
Peter
GeneralExcellent!memberMoim Hossain9 Feb '09 - 23:16 
Omar,
 
It's to late to congratulate you for this awesome job!
 
Recently, I am doing some COM interop stuffs, and I am planning I will use this code, with some modifications so that it can fit my case more better.
 
I believe, you won't mind if I use this- ofcourse keeping your copyright notice into the source file. Smile | :)
 
Thanks for the nice articles!
 
Moim Hossain
R&D Project Manager
BlueCielo ECM Solutions BV

QuestionContactItem??memberMember 431105830 Jun '08 - 19:28 
This is great, but how can I make it work with Outlook ContactItems? I can't find a definition for ContactItem, just MailItem.
GeneralBrilliant workmvpSacha Barber15 May '08 - 4:00 
Well done here Omar. This is great
 
Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralAdd an attribute to signal US CultureInfo is requiredmemberJeff Byrne11 Mar '08 - 4:20 
This article has been fantastic. I have been able to write code that reliably works in multiple Office versions. This code has made it easy to solve a problem that plagues the Interop Assembly approach.
 
Some Office products require that some methods/properties are called with the US CultureInfo. This defect is explained at http://support.microsoft.com/kb/320369[^]. With Interop Assemblies, I had to place "protection" code throughout my solution in order to ensure the CurrentThreadCulture was "en-US" for the certain members. This was very messy.
 
Thanks to the SafeCOMWrapper approach I have been able to clean this up considerably. First I created an attribute to signal that a member requires the US CultureInfo.
	/// <summary>
	/// An attribute to indicate member requires the US CultureInfo in late-binding
	/// </summary>
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method, Inherited=false, AllowMultiple=false)]
	public sealed class RequireUSCultureAttribute : Attribute
	{
		public RequireUSCultureAttribute()
		{
		}
	}
 
Then, I added code to COMWrappers.Invoke() to use "en-US" if the RequireUSCultureAttribute attribute is found.
 
	// Check for RequireUSCultureAttribute on properties and methods
	System.Globalization.CultureInfo cultureInfo = null;
	MemberInfo memberInfo = null;
	try
	{
		switch (flags)
		{
			case BindingFlags.SetProperty:
			case BindingFlags.GetProperty:
				memberInfo = method.DeclaringType.GetProperty(methodName);
				break;
			case BindingFlags.InvokeMethod:
				memberInfo = method;
				break;
		}
 
		if (memberInfo != null &&
			memberInfo.IsDefined(typeof(RequireUSCultureAttribute),true))
		{
			cultureInfo = new System.Globalization.CultureInfo("en-US");
		}
	}
	catch {}
 
	try
	{
		returnValue = invokeType.InvokeMember(methodName, 
			flags, null, invokeObject, args, argModifiers, cultureInfo, null);
	}
	catch(Exception ex)
	{
		return new ReturnMessage(ex, callMessage);
	}
 
Now I can simply add the RequireUSCultureAttribute to the methods/properties that need this special processing. Everything is in one central location, and all of the other "protection" code can be removed.
 
 
	public interface Workbooks : IDisposable, IEnumerable
	{
		int Count {get;}
		[RequireUSCulture]
		Workbook Add(object Template);
		Workbook this[object Index] {get;}
	}
 
- Jeff
GeneralHaving problems with IEnumerable and Excel [modified]memberJeff Byrne10 Mar '08 - 11:07 
I am having problems getting IEnumerable to work for Excel collections. I keep getting "Member not found". I think that I need to do some type of special marshaling to access the correct method in COM. Has anybody got IEnumarable to work with Excel wrappers? Do I need to convert to _NewEnum()?
 
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. -->
System.Runtime.InteropServices.COMException (0x80020003): Member not found.
 
modified on Monday, March 10, 2008 7:49 PM

GeneralRe: Having problems with IEnumerable and ExcelmemberJeff Byrne10 Mar '08 - 13:54 
I figured it out. For some reason GetEnumerator requires BindingFlags.GetProperty. I don't know if it will be like this for all of the interfaces I need to code. If I run into differences, I will have to add an attribute to signal when GetProperty is required.
 
I also used the IEnumerable code posted earlier, and everything works good.
 
- Jeff
QuestionTrusted COM add-ins to circumvent the Outlook security warningmembermatsch_o030 Aug '07 - 23:32 
Hi,
 
I build my Outlook synchronization application with the SafeCOMWrapper quite nicely. My work was greatly facilitated by it!
 
Now, I have the problem of the Outlook security warnings due to the Improved Security Model of Outlook each time I am reading Item information out of Outlook.
 
One solution I found is to build Trusted COM add-ins (source). Is it possible with the SafeCOMWrapper to achieve this? How can I do it?
 
Or: Do you know of any other possibilities to programmatically prevent the Outlook security warnings? (installing something else on the clients computer unfortunately is not an option)
 
Thanks and Best Regards,
matsch
GeneralSend a Messagemembergratajik10 Jul '07 - 11:40 
Didn't see this in the classes, so I added to the interface to be able to send a message (item was missing Send and To):
 
     public interface Item : Common
     {
            void Send();
            string To { get; set; }
            ..
 
Example sending with attachment:
 
     using( Application app = OutlookApplication.Create() )
            {                             
                  object oMsg = app.CreateItem(OlItemType.olMailItem);
 
                  object obj = app.CreateItem( OlItemType.olMailItem );
                  using( MailItem newMail = (MailItem)COMWrapper.Wrap( obj, typeof( MailItem ) ) )
                  {
                        newMail.Subject = sSubject;
                        newMail.To = sTo;
                        newMail.Body = sBody;
                             
                        newMail.Attachments.Add(sAttachment, OlAttachmentType.olEmbeddeditem , newMail.Body .Length + 1, sAttachmentName);
                        newMail.Send();                                                 
                        }
                  }
            }
GeneralRe: Send a MessagememberTrevorJobling5 Oct '07 - 1:00 
thanks! that was handy!
GeneralRe: Send a MessagememberDaniel A.10 Jun '08 - 22:26 
Thanks, I was looking for that. But schouldn't that be part of the MailItem Interface?
QuestionHow to split COM+ and Presentation tier and deploy it in two servers ?memberSylvester george27 Jun '07 - 21:17 
How to split COM+ and Presentation tier and deploy it in two servers ?
 
Regards,
Sylvester G
sylvester_g_m@yahoo.com

GeneralAttachmentsmemberSteve McKean14 Jun '07 - 6:03 
Omar,
 
Let me thank you for creating such a nice tool for working with Outlook.
 
My question is that I need to find and work with the attachments to messages and was not quite sure how to proceed. So, as I'm looping through the mail item how can I gain access to the attachment?
 
Many thanks,
 
Steve McKean
 
Steve McKean
GeneralRe: AttachmentsmemberShine100117 Jul '07 - 7:30 

string sAttachmentDestinationDir = "c:\temp"
foreach (Attachment Att in MailItem.Attachments) {
using (Common CommonItem = (Common)COMWrapper.Wrap (Att, typeof (Common))) {
if (CommonItem.Class == OlObjectClass.olAttachment) {
Attachment Attachment = (Attachment)COMWrapper.Wrap (Att, typeof (Attachment));
sAttachmentDestinationPath = System.IO.Path.Combine (sAttachmentDestinationDir, Att.FileName);
Attachment.SaveAsFile (sAttachmentDestinationPath);
}
}
}

GeneralHandling collections (IEnumerable) [modified]memberSefatoma13 Jun '07 - 9:59 
If you create an interface that implements IEnumerable, and then use foreach on an instance of it, the proxy will generate a call to the GetEnumerator. If this is passed to InvokeMember on the underlying COM object, then the return value will be an IEnumerator object that wraps the COM enumerator.
 
While this is a cool feature, it creates a problem because the default enumerator will return unwrapped COM objects.
 
I patched in something that will intercept GetEnumerator and then wrap the default enumerator so that it wraps return values if they are COM objects. There are two parts, which both go in the COMWrapper.cs source file:
 
The first part is in the Invoke method of the COMWrapper class, in the part that checks the return value. The first three lines of the snippet below represents the existing code, the new code begins at the else if:
 
                        // Wrap the returned value in an intercepting COM wrapper
                        if (Marshal.IsComObject(returnValue))
                            returnValue = COMWrapper.Wrap(returnValue, returnType);
                        else if ("GetEnumerator" == methodName && 0 == argCount && typeof(IEnumerator) == returnType)
                        {
                            IEnumerator enumerator = returnValue as IEnumerator;
                            if (null != enumerator)
                            {
                                MethodInfo getitem = this._InterceptType.GetMethod("get_Item");
                                if (null != getitem && getitem.ReturnType.IsInterface)
                                    returnValue = new ComEnumerator(enumerator, getitem.ReturnType);
                            }
                        }
 
Here is the private class ComEnumerator, which I just added to the end of the COMWrapper class definition:
 
        private class ComEnumerator : IEnumerator
        {
            IEnumerator _enumerator;
            Type _returnType;
 
            public ComEnumerator(IEnumerator enumerator, Type returnType)
            {
                _enumerator = enumerator;
                _returnType = returnType;
            }
 
            public object Current
            {
                get
                {
                    object returnValue = _enumerator.Current;
                    if (Marshal.IsComObject(returnValue))
                        return COMWrapper.Wrap(returnValue, _returnType);
                    return returnValue;
                }
            }
 
            public bool MoveNext()
            {
                return _enumerator.MoveNext();
            }
 
            public void Reset()
            {
                _enumerator.Reset();
            }
        }
 
Additionally, you will need to add the following to the using section at the top of the source file:
 
using System.Collections;
 
The code depends on the interface implementing an indexer in the format type this[type index], for example:
 
    public interface Common : IDisposable
    {
    }
 
    public interface Collection : Common, IEnumerable
    {
        int Count { get; }
    }
 
    [ComProgId("Microsoft.Update.UpdateColl")]
    public interface UpdateCollection : Collection
    {
        Update this[int index] { get; }
        void Add(Update update);
    }
 
The type of the indexer (Update in this case), determines what will be used to wrap objects returned by the enumerator.
 

-- modified at 18:17 Wednesday 13th June, 2007
QuestionSafeCOMWrapper with Custom COM Objectmemberj.jonez1 Feb '07 - 4:49 
I've been trying for 2 days not to apply this excellent tutorial to my own COM DLL server. I just can't seem to get things working though. I think something is wrong with my Interface but I'll try to describe what is going on, if anyone cares to lend a hand. Thanks! JJ
 
1) Integrated the files ComEventProvider.cs, ComEventsAttribute.cs, ComEventSink.cs, ComProgIdAttribute.cs, ComWrapper.cs.
2) Added MyApp.cs, equivalent to OutlookApplication.cs
3) Added TestMyApp.cs, equivalent to TestOutlook.as
4) Added MyAppWrapper.cs (very simple), equivalent to OfficeWrappers.cs
 
//code
[ComProgId("COMApi.Api")]
public interface IAPIFunctions : IDisposable
{
void gaGetAmoutOfRenderContexts( out int contextsAmount );
}
//end code
 
5) In OutlookApplication.cs I replaced the one line:
 
//code
return (IAPIFunctions)COMWrapper.CreateInstance(typeof(IAPIFunctions));
//end code
 
with the equivalent code found in the same file (though commented out)
 
//code
Type appType = Type.GetTypeFromProgID("Outlook.Application");
object outlookApplication = Activator.CreateInstance( appType );
return COMWrapper.Wrap( outlookApplication, typeof( Application ) ) as Application;
//end code
 

Here's what I've found stepping the code above.
Type.GetTypeFromProgID returns an object with the GUID I am expecting eliminating problems with COM object registration.
 
The object returned however indicates that that GUID is {0...0}.
 
After Activator.CreateInstance( appType ); Inspection of the variable appType in the debugger indicates the following exception:
 
- GenericParameterAttributes 'appType.GenericParameterAttributes' threw an exception of type 'System.InvalidOperationException' System.Reflection.GenericParameterAttributes {System.InvalidOperationException}
+ base {"Method may only be called on a Type for which Type.IsGenericParameter is true."} System.SystemException {System.InvalidOperationException}
 


GeneralNameSpace.Folders throws an InvalidCast exceptionmemberDerik Palacino15 Jan '07 - 6:51 
First of all, love the wrapper, great work.
 
I am having a bit of an issue using it to enum the loaded stores. After calling ns.AddStore("[pst file path]") I go into a foreach loop to walk the folders, I changed to walk as objects after the exception was thrown and did a manual invoke on get_Class. The value back was an int value of 2 which maps to the Folder type. When I try to cast this object to a folder I get the following error message.
 

Unable to cast COM object of type 'System.__ComObject' to interface type 'MAPIFolder'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{BE29396E-03DA-38D0-981A-57275EE927AD}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
 

Any help you can offer would be GREAT!
Thanks again for the wrapper.
QuestionMoving mail itemsmemberjpadair4 Jan '07 - 6:28 
Excellent tool and having great success integrating our app with Outlook using combination of your COMWrapper and interop. I am having a couple of issues that might have more to do with the proper way the proxies are used and wonder if you could point me in the direction of how best to debug/resolve these issues.
 
1) want to move an existing mail item to another folder:
using (MO.Application app = MO.OutlookApplication.Create())
{
using (MO.NameSpace ns = app.GetNamespace("MAPI"))
{
object itemObj = ns.GetItemFromID(entryId, null);
using (MO.MailItem item = MO.COMWrapper.Wrap(itemObj))
{
MO.MAPIFolder destFolder = GetDestinationFolder();
item.Move(destFolder);
}
}
}
All is fine until item.Move(...) throws a TargetInvocationException during invokeType.InvokeMember(...) On the other hand I can perform the same move operation successfully using the Microsoft.Office.Interop.Outlook proxies ( OL.MailItem item; OL.MAPIFolder destFolder; etc.)
 
2) as an aside when attempting to display a reply for existing email item:
using (MO.Application app = MO.OutlookApplication.Create())
{
using (MO.NameSpace ns = app.GetNamespace("MAPI"))
{
object itemObj = ns.GetItemFromID(entryId, null);
using (MO.MailItem item = (MO.MailItem)MO.COMWrapper.Wrap(itemObj))
{
item.Reply().Display(null);
}
}
}
Above works great! but formerly tried to do item.Display(null) to show the normal inspector allowing reply and forward etc. also gave the same exception as above. Just curious on what is going on at a lower level. Thanks for such a useful wrapper utility.

 
James P. Adair, Jr.
Software Consultant
Desert Stream Software, Inc.

GeneralGetting Inspectormemberacheyer13 Dec '06 - 15:41 

Every time I try to get the ActiveInspector, it always returns null. I'm trying to get the currently selected item.
Ideas?
 
Thanks! Adam
 

using( ManagedOffice.Application app = OutlookApplication.Create() )
{
if( 0 == app.Explorers.Count ) // Oops, we forgot to dispose "Explorers", no problem...
{
// Outlook is launched right now, let's add a visible interface
using( NameSpace ns = app.Session )
{
using( MAPIFolder inbox = ns.GetDefaultFolder( OlDefaultFolders.olFolderInbox ) )
{
inbox.Display();
}
}
}
using(Inspector inspector = app.ActiveInspector())
{
// problem: inspector always null!
GeneralRe: Getting InspectormemberOmar Al Zabir15 Dec '06 - 18:09 
May be inspector is null at that time?
 
AFAIK, Inspector returns the currently open window for a single item. For example, Mail view window.
 
For currently selected item, it's in the Explorer object.
 
Regards,
Omar AL Zabir
Visual C# MVP

GeneralRe: Getting Inspectormemberacheyer16 Dec '06 - 4:06 
Thanks for your speedy response! Yes, you're right, since I'm new to Outlook instrumentation, I was thrown off by the fact that in OfficeWrapper.cs, there was no Selection property in your Explorer interface, but there was a CurrentItem() property in the Inspector (and I wasn't sure what Inspector was). When I added the line:
 
Items Selections { get; }
 
to the Explorer interface, this solved my problem.
 
Thanks for an incredibly useful article!!!
 
- Adam

QuestionGreat article but didn't helped me...can you?memberPreky7 Sep '06 - 5:08 
Ok... I have Outlook add-in I have created.
I have interface that should expose methods and properties of that add-in.
 
From Client app I'm attaching to that add-in and calling exposed methods. Evrething works fine for methods/propetries returning simple type but when I try to return object type (eg. DataTable) I get __ComObject type and can't make cast to DataTable (exception is raised - specified cast exception error).
 
How can I use your COM wrapper to get my DataTable?
I have tried various approaches but didn't get too far. I have fealing that your idea should help but haven't figured out how? (my first time struggle with COM's and marshaling)
 
Thx!

 

 
Greatings,
 
Preky

QuestionHow to pass a System.Reflection.Missing.Value [modified]memberPascal Groulx28 Jun '06 - 11:12 
Hi, your example is SOOOO great !
 
I created an interface named FormDescription to allow me publishing a form into OlFormRegistry.olPersonalRegistry like I did with the Outlook API http://msdn.microsoft.com/library/en-us/vbaol11/html/olmthPublishForm_HV05247415.asp[^])
 
My code is :
 
// Create application
using(ManagedOffice.Application app = ManagedOffice.OutlookApplication.Create())
{					
	// Create explorer
	using(ManagedOffice.Explorer explorer = app.ActiveExplorer())
	{
		// Create namespace
		using(ManagedOffice.NameSpace ns = app.Session)
		{
			// Create contact folder
			using(ManagedOffice.MAPIFolder contactFolder = ns.GetDefaultFolder(ManagedOffice.OlDefaultFolders.olFolderContacts))
			{
				// Create contact
				object obj = app.CreateItemFromTemplate(this._oConfig.SumBusinessContact,contactFolder);
				if ( obj != null )
				{
					using(ManagedOffice.ContactItem contact = (ManagedOffice.ContactItem)ManagedOffice.COMWrapper.Wrap(obj,typeof(ManagedOffice.ContactItem)))
					{
						// Create form description
						object obj2 = contact.FormDescription;
						if ( obj2 != null )
						{
							using(ManagedOffice.FormDescription form = (ManagedOffice.FormDescription)ManagedOffice.COMWrapper.Wrap(obj2,typeof(ManagedOffice.FormDescription)))
							{
								form.Name = string.Format("SUM Business Contact {0}",form.Version);
								string MessageClass = contact.MessageClass + "." + form.Name;
								
								if ( MessageClass != this._oPublishFormInfo.Contact)
								{
									// Changes value to save in xml file
									this._oPublishFormInfo.Contact = MessageClass;
 
									// Publish form
									form.DisplayName = this._oPublishFormInfo.Contact;
									form.PublishForm(ManagedOffice.OlFormRegistry.olPersonalRegistry,Missing.Value);
 
									this._ContactTemplateVersionChanged = true;		
								}
							}
						}
 
						// Discard item created from the template
						contact.Close(ManagedOffice.OlInspectorClose.olDiscard);
					}
				}
			}
		}
	}
}
 
I got the following error (Missing parameter does not have a default value.\r\nParameter name: parameters) on line form.PublishForm(ManagedOffice.OlFormRegistry.olPersonalRegistry,Missing.Value);
 
Can someone help me with that ?
 

 
-- modified at 17:18 Wednesday 28th June, 2006
AnswerRe: How to pass a System.Reflection.Missing.ValuememberPascal Groulx29 Jun '06 - 2:55 
I found it..
 
I removed the last parameter and it's working
 
/// <summary>Outlook.FormDescription Wrapper</summary>
public interface FormDescription : Item
{
	string Name { get;set; }
	string DisplayName { get;set; }
	string Version { get;set; }
	void PublishForm(OlFormRegistry Registry);
}
 
I don't know why but that is the workaround I found
 

QuestionPolymorphism??membermb7726 Jun '06 - 23:13 
Great work... this wrapper is fantastic!!! Cool | :cool:
 
I have one issue however... I'm pretty new to remoting and COM interop so I might be missing something pretty obvious, but I'm trying to interop with a COM library that has a collection of abstract types and I need to be able to test for the different concrete types in the collection.
 
For example, I have a COM library with a collection called "AnimalBaseCollection" and a series of objects "Dog", "Cat" which inherit from "AnimalBase". I've got my .Net wrapper interface IAnimal and derived interfaces: IDog and ICat. The interface for the "Item" property of IAnimalBaseCollection, however, returns an "IAnimal", so my COMWrapper creates the transparent proxy to an "IAnimal". I can't see how to type test the interface against IDog or ICat???
 
Wouldn't the COMWrapper need to be created against the concrete type?? and if so, how can I map the COM objects to the .Net interfaces? I thought about putting a GuidAttribute on each of the .Net interfaces that matches the COM object GUID... and searching for an interface that matches the COM object GUID so I could create the COMWrapper against the concrete type, but I can't seem to be able to do that either?
 
Any ideas??
AnswerRe: Polymorphism??memberMarc Brady27 Jun '06 - 21:03 

Aha... found a solution. Let me apologise in advance for the VB in this Csharp article.... but my company requires VB.Net code... anyway....
 
Some other people in these comments have put up links to disassembler tools that can be used to retrieve the COM object type GUIDs. If the .NET interfaces that are intended to represent the COM objects are declared with the same GUID, then we can retrieve the COM objects GUID (for the concrete type) and check to see if there is an interface that matches.
 
If an interface exists that matches, we can wrap against the concrete type's interface, otherwise we wrap against the abstract type's interface as before.
 
Retrieving the COM object's GUID however, requires a bit of trickery... which I'll be quite honest and admit I haven't quite got my head around just yet. Anyway... if you declare an interface called IDispatch in your assembly as below, it seems (to me anyway) that ALL COM types are then castable to this interface type.
 
_
Public Interface IDispatch
Function GetTypeInfoCount(ByRef Count As Integer) As Integer Function GetTypeInfo( ByVal iTInfo As Integer, ByVal lcid As Integer, ByRef typeInfo As UCOMITypeInfo) As Integer Function GetIDsOfNames(ByRef riid As System.Guid, ByVal rgsNames() As String, ByVal cNames As Integer, ByVal lcid As Integer, ByVal rgDispId() As Integer) As Integer Function Invoke(ByVal dispIdMember As Integer, ByRef riid As System.Guid, ByVal lcid As System.UInt32, ByVal wFlags As System.UInt16, ByRef pDispParams As DISPPARAMS, ByVal pVarResult As Object, ByRef pExcepInfo As EXCEPINFO, ByVal pArgErr() As IntPtr) As Integer End Interface
  So when the Invoke method is called on the COMWrapper... we check to see if the returnValue from the call is a COM object type. If it is, then we retrieve the COM Object type attributes at runtime... which include lots of useful information about the COM object type including the GUID. WE then search the current assembly (assumes the .Net interfaces are in the same assembly) for any interfaces that match the GUID, and replace the returnType with that interface instead.   If (Not returnValue Is Nothing) Then ' Check the return types to see if we need to wrap the return object If returnType.IsInterface Then If Marshal.IsComObject(returnValue) Then ' If we are here then the return type is a COM object ' type and needs to be wrapped by a COMWrapper   ' We don't know the concrete type of the return type so ' we need to find the COM GUID of the return type and attempt ' to match it against the Type GUIDs of our interfaces Dim cd As IDispatch = DirectCast(returnValue, IDispatch) Dim cnt As Integer cd.GetTypeInfoCount(cnt)   If cnt = 1 Then ' Get the type info of the COM Object Dim ti As UCOMITypeInfo cd.GetTypeInfo(0, 0, ti) Dim pti As IntPtr ti.GetTypeAttr(pti)   ' Get the GUID of the COM Object type Dim ta As TYPEATTR = DirectCast(Marshal.PtrToStructure(pti, GetType(TYPEATTR)), TYPEATTR) Dim comGuid As System.Guid = ta.guid ti.ReleaseTypeAttr(pti)   ' Cycle through all our interfaces looking for one that matches the COM GUID ' If no GUID matches, then we assume the interface declared on this method call is the return type Dim assy As System.Reflection.Assembly assy = System.Reflection.Assembly.GetExecutingAssembly()   For Each typ As Type In assy.GetTypes If (typ.IsInterface And typ.GUID.Equals(comGuid)) Then returnType = typ Exit For End If Next End If   returnValue = ComWrapper.Wrap(returnValue, returnType) End If   ElseIf returnType.IsEnum Then returnValue = [Enum].Parse(returnType, returnValue.ToString)   End If End If
GeneralProblems with structsmemberRouslan Minasian14 Jun '06 - 5:07 

struct TestStruct
{
public int a1;
public int a2;
}
 
[ComProgId("com_server.TestObject")]
interface TestObj
{
void TestMethod();
void GetStruct(out TestStruct val);
}

 
Exception is raised when I try to call GetStruct(). The method does nothing - it fails somewhere before function entry point Confused | :confused:
 
Can you help me?
GeneralGeneric COMWrappermemberRegentZ22 Mar '06 - 14:53 
Great article, I found it very useful.
 
For working in .NET 2.0 I have added the following two (2) generic methods, this removes the need to cast the returned object which I think makes the code more readable.
 

public static T CreateInstance<T>()
{
return (T)CreateInstance(typeof(T));
}
 
public static T Wrap<T>(object comObject)
{
return (T)Wrap(comObject, typeof(T));
}

 
Create Excel example:
_excel = COMWrapper.CreateInstance<Excel.Application>();
 
Wrap Excel example:
_excel = COMWrapper.Wrap<Excel.Application>(excel);
 

- Des
 
PS. love your web site; I did something similar at UNI when IE4 first came out using script and IFrame's. I didn't have the boot screen and start menu though, definitely adds to the experience.
GeneralFANTASTIC CODE!memberAlon Hirsch16 Mar '06 - 21:07 
Hi,
 
The article is fantastic - thanx.
I am currently in a situation where I need to be able to send email messages from my C# application using Outlook.
The problem I have is that the clients are running different versions of Outlook, and unless I develop against Outlook 2000, I can not get the system to run correctly on the other versions.
 
This article, and the code provided, are exactly what I need to be able to accomplish what I want to.
 
I have had to make minor changes (such as a send method, TO, CC, BCC properties, Recipients etc.) but overall, the code is fantastic.
 
My next task is to write a wrapper for Excel, but using the sample you have provided, I do not feel this will be as daunting a task as I would have thought.
 
Thanx again,
Alon
GeneralContactItem are missingmemberdevinus20 Jan '06 - 9:26 
I have been taking a look at the sample code, but it seems that contacts are missing from it.
 
I would to know if anyone has a code that includes Contacts.
 
I'm still a newbie, and I'm still trying to learn.
 
Thnx in advance and sorry for my lame english.
GeneralRe: ContactItem are missingmemberdevinus21 Jan '06 - 7:50 
Ok I finally got it working Smile | :)
 
Now, another question, is there a way to get the contact groups?
GeneralDecompiling toolmemberdavewadd12 Jan '06 - 8:43 
I recently attempted to use the reflector Disassembler program along with the file disassembler add-on to create sourcecode to use for generating wrappers for Word and Excel. After going through several days of grief attempting to get the code to work, I came across the Aurigma disassembler. This product has been discontinued, but is available free of charge to download and use. It produces far better results than Lutz's product. I was able to very quickly get my Word library working using the definitions it produced. You can download it from http://www.aurigma.com/Products/COMtoNET/[^]
GeneralRe: Decompiling toolmemberOmar Al Zabir13 Jan '06 - 4:55 
Thank you. But the link leads to a missing page.
 
Regards,
Omar AL Zabir
Visual C# MVP
GeneralRe: Decompiling toolmemberdavewadd13 Jan '06 - 5:15 
That is very odd - I just tried it again from my browser by clickjing on the link in my post and it went to the download page. Maybe the page was down when you tried it. Here it is again:
 
http://www.aurigma.com/Products/ComToNet
 
Maybe that extra junk that came in with my cut and paste is causing problems in your browser that it isn't causing in mine. I typed it by hand this time.

GeneralAccessing tables in Wordmemberdavewadd16 Dec '05 - 10:01 
I've gone through the decompiling process for the interop libraries for Word 2003 and built a library following the instructions from this article. I've run into 1 problem - I cannot get any of the enumerated objects such as Documents, Tables, Etc. to work. My interface definition is:
 
public interface Tables : IEnumerable
{
Application Application { get; }
int Creator { get; }
object Parent { get; }
Table this[int Index] { get; }
}
 
When I attempt to access an individual table, I get an exception raised by the target of an invocation error. I've tried using GetEnumerator() and I've also tried Tables[idx] and Tables.Item(idx) - same error. Any ideas what could be wrong?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 19 Sep 2005
Article Copyright 2005 by Omar Al Zabir
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid