Is it possible to intercept (or be aware of) COM Reference counting on CLR objects exposed to COM

前端 未结 10 1073
礼貌的吻别
礼貌的吻别 2020-12-08 02:56

I have rephrased this question.

When .net objects are exposed to COM Clients through COM iterop, a CCW (COM Callable Wrapper) is created, this sits between

10条回答
  •  遥遥无期
    2020-12-08 03:50

    I've been struggling with this too, to try to get server lifetime correct for my Preview Handler, as described here: View Data Your Way With Our Managed Preview Handler Framework

    I needed to get it into an out of process server, and suddenly I had lifetime control issues.

    The way to get into an out of process server is described here for anyone interested: RegistrationSrvices.RegisterTypeForComClients community content which implies that you may be able to do it by implmenting IDispose, but that didn't work.

    I tried implementing a finalizer, which did eventually cause the object to be released, but because of the pattern of usage of the server calling my object, it meant my server hung around forever. I also tried spinning off a work item, and after a sleep, forcing a garbage collect, but that was really messy.

    Instead, it came down to hooking Release (and AddRef because the return value of Release couldn't be trusted).

    (Found via this post: http://blogs.msdn.com/b/oldnewthing/archive/2007/04/24/2252261.aspx#2269675)

    Here's what I did in my object's constructor:

    //  Get the CCW for the object
    _myUnknown = Marshal.GetIUnknownForObject(this);
    IntPtr _vtable = Marshal.ReadIntPtr(_myUnknown);
    
    // read out the AddRef/Release implementation
    _CCWAddRef = (OverrideAddRef)
        Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(_vtable, 1 * IntPtr.Size), typeof(OverrideAddRef));
    
    _CCWRelease = (OverrideRelease)
        Marshal.GetDelegateForFunctionPointer(Marshal.ReadIntPtr(_vtable, 2 * IntPtr.Size), typeof(OverrideRelease)); 
    _MyRelease = new OverrideRelease(NewRelease);
    _MyAddRef = new OverrideAddRef(NewAddRef);
    
    
    Marshal.WriteIntPtr(_vtable, 1 * IntPtr.Size, Marshal.GetFunctionPointerForDelegate(_MyAddRef)); 
    Marshal.WriteIntPtr(_vtable, 2 * IntPtr.Size, Marshal.GetFunctionPointerForDelegate(_MyRelease));
    
    and the declarations:
    
    
    int _refCount; 
    
    delegate int OverrideAddRef(IntPtr pUnknown);
    OverrideAddRef _CCWAddRef; 
    OverrideAddRef _MyAddRef;
    
    
    delegate int OverrideRelease(IntPtr pUnknown); 
    OverrideRelease _CCWRelease;
    OverrideRelease _MyRelease;
    
    IntPtr _myUnknown;
    
    protected int NewAddRef(IntPtr pUnknown) 
    {
        Interlocked.Increment(ref _refCount);
        return _CCWAddRef(pUnknown); 
    }
    
    
    protected int NewRelease(IntPtr pUnknown) 
    {
        int ret = _CCWRelease(pUnknown);
    
        if (Interlocked.Decrement(ref _refCount) == 0)
        {
            ret = _CCWRelease(pUnknown);
            ComServer.Unlock();
        }
    
        return ret; 
    }
    

提交回复
热议问题