Question

I'm stuck with .Net 1.1 application (i.e. I can not use the generics goodies from 2.0 for now), and I was trying to optimize some parts of the code. As it deals a lot with runtime callable wrappers, which need to be released, I ended up to create a utility method which loops until all references are released. The signature of the method is:

void ReleaseObject(object comObject)

After releasing all comObjects, I call GC.Collect and GC.WaitForPendingFinalizers (don't ask - anybody dealing with Office interop knows).

And ... as usual, I hit a corner case - if I do not assign the corresponding managed reference to null before the GC.Collect call, it does not cleanup properly.

So, my code looks like:

ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...

As, there are a bunch of xxx=null, I decided to put this in the util method, but as there is a difference between passing by reference, and passing a reference parameter, obviously I had to change the method to:

void ReleaseObject(out object comObject)
{
   //do release
   comObject = null;
}

and edit the caller to:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

This fails with a message: "Cannot convert from 'out MyComClass' to 'out object'"

While I can think of why it can be a problem (i.e. the reverse cast from object to MyComClass is not implicit, and there is no guarantee what the method will do), I was wondering if there is a workaround, or I need to stay with my hundreds assignments of nulls.

Note: I have a bunch of different COM objects types, thats why I need a "object" parameter, and not a type safe one.

Was it helpful?

Solution

Why is it better to call a method than to just set the variable to null? They're both single line calls, and the latter is a lot simpler.

It does sound very odd that you need to set them to null in the first place though. Are these static variables, or instance variables whose values need to be released earlier than their containing object? If the variable is just a local variable which will go out of scope anyway, setting it to null shouldn't make any difference (in release).

Do the RCWs not implement IDisposable? If so, calling Dispose (preferably via a using statement) would be the best bet.

(After discussions in comments.)

These are local variables, which aren't referenced later in the method. That means the garbage collector will realise that they don't need to be treated as "root" references - so setting them to null shouldn't make any difference.

To answer the original question directly, however: no, you can't pass a variable by reference unless the method parameter is of exactly the same type, so you're out of luck here. (With generics it would be possible, but you've said you're limited to .NET 1.1.)

OTHER TIPS

Sunny, ref and out are a marshalling hints + contract to the compiler. Ref and out are a carryover to COM days - the marshalling hints for objects when sent over the wire / between processes.

The out contract

void foo( out MyClass x)
  1. foo() will set x to something before it returns.
  2. x has no value when foo() is entered, and you get a compiler error if you attempt to use x before setting it. (use of unassigned out parameter x)

The ref contract

void foo( ref MyClass x)
  1. ref allows changing the callers reference.
  2. x has to be assignable
    • you cannot cast something to an intermediate variable foo( ref (object) something)
    • x can not be a property

The reality of the last two points are likely to stop you doing what you're trying to do, because in effect they make no sense when you understand what references really are. If you want to know that, ask Jon Skeet (he wrote a book).

When marshalling ref, it says that in addition to the return value, bring back ref values as well. When marshalling out, it says don't bother sending the out value when the method is called, but remember to bring back the out value in addition to the return value.


DISCLAIMER DISCLAIMER DISCLAIMER

As others point out, something fishy is going on. It appears the brute-force code you are maintaining has some subtle bugs and suffers from coding by coincidence. The best solution is probably to add another layer of indirection. i.e. A wrapper to the wrapper class that ensures deterministic cleanup where you can write the messy code once and only once instead of peppering it throughout your codebase.


That said ..

Alternative 1

Ref won't do the trick unless you provide overloads for every type of (com) object you will call it with.

// need a remove method for each type. 
void Remove( ref Com1 x ) { ...; x = null; }
void Remove( ref Con2 x ) { ...; x = null; }
void Remove( ref Com3 x ) { ...; x = null; }

// a generics version using ref.
void RemoveComRef<ComT>(ref ComT t) where ComT : class
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
    t = null; 
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
Remove( ref c1 );
RemoveComRef(ref c2); // the generics version again.

Alternative 2

If you don't want to do that, return null from the Remove() method and cast back to the type of object.

class Remover
{
    // .net 1.1 must cast if assigning
    public static object Remove(object x)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(x);
        return null;
    }

    // uses generics.
    public static ComT RemoveCom<ComT>(ComT t) where ComT : class
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
        return null;
    }   
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
c1 = (Com1)Remover.Remove(c1); // no reliance on generics
c2 = Remover.RemoveCom(c2); // relies on generics

* I added generic versions for comparison.

The above code has the effect that when looking at code you become suspicious when you see a call to Remove(x) without the assignment (making wrong code look wrong). You could even Grep through the codebase looking for calls to Remove where assignment doesn't take place.


DISCLAIMER - all the above is predicated on your needing to set the reference to null manually, which (normally) isn't necessary.

In my opinion you won't be able to set those objects to null in another method (BTW you would need to use ref parameter instead of out to make it working, anyway you would hit the same problem with "Cannot convert..." error.) I'd recommend to create and array of objects and than iterate through that array, calling the ReleaseObject method and setting those objects to null. Something like:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...

You should be calling Marshal.ReleaseComObject, which AFAIK was available in 1.1.

You probably mean "ref":

static void ReleaseObject(ref object comObject)
{
   if(comObject != null)
   {
     //do release
     comObject = null;
   }
}

[edit re comments] however, this will only work for untyped objects, so not much use without generics! Oh for C# 2.0...

Re the "ref"; if the variables are truly variables (meaning: method variables), then they will go out of scope shortly and get collected. The "ref" would only be useful to release fields. But to be honest, it would be simpler just to set them to null...

A typical COM pattern is:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top