tags:

views:

40

answers:

0

Hi,

I have written a DirectShow source filter in C# that works like a charm but there's one problem: in GraphEdit (and similar tools), the connection between my source filter's output pin and the downstream filter's input pin isn't drawn, although both pins' properties indicate that they're connected and also the graph works as it should. This doesn't have any impact on the functionality but it's still a cosmetic imperfection that needs to be addressed.

I think I could track down the problem, which is probably the following (at least the C# implementation DSGraphEdit works that way): GraphEdit iterates through the filters and their pins, and compares the pins they're connected to with all pins in the graph (pinA.ConnectedTo() == pinB). On a match, it draws the line between them. Now the problem is that this comparison by reference fails although both variables reference the same COM-object.

Here's what is happening in detail:
filterA: my C# source filter (implements COM-interfaces)
outputA: filterA's output pin
filterB: downstream filter (ordinary COM-object)
inputB: filterB's input pin

  1. DirectShow GraphManager calls outputA.Connect(inputB)
  2. outputA accepts the connection and stores inputB locally
  3. ...
  4. GraphEdit enumerates filterA's pins and gets outputA
  5. GraphEdit calls outputA.ConnectedTo(out connectedPin)
  6. GraphEdit iterates through filterB's pins
  7. at some point it checks connectedPin == inputB which retuns false, where connectedPin is in fact the same as inputB (which came from the locally stored reference in outputA)

My guess it that this has something to do with the managed <-> unmanaged transition layer and it's COM wrappers. When the reference to inputB enters the managed code, the runtime creates a COM callable wrapper (CCW) around inputB and hands it to my method as an IPin instance. MSDN says that every instance of a COM object is getting only one wrapper, now matter from where it gets called, but then how can this be possible:

connectedPin == inputB; // false
connectedPin.GetHashCode() == inputB.GetHashCode(); // false
IntPtr pCP = Marshal.GetComInterfaceForObject(connectedPin, typeof(IPin));
IntPtr pIP = Marshal.GetComInterfaceForObject(inputB, typeof(IPin));
pCP.ToInt64() == pIP.ToInt64(); // false

MSDN clearly states that Marshal.GetComInterfaceForObject() returns a pointer to the CCW, and since every COM object can only have one CCW, how can it be that the addresses differ?

I'm sorry for the long post but I hope that it at least makes sense to someone...

Thanks!

Update:

The problem may be that the two pointers are obtained in different threads/processes. The first pointer is obtained in the filter graph thread:

// now we are in the filter graph thread
outputA.Connect(inputB); // called by the GraphManager, here we obtain the first pointer to inputB and store it locally

Some time later, GraphEdit (a separate process) connects to the very same filter graph through the Running Objects Table and queries for the connected pin:

// in GraphEdit
IPin inputB;
outputA.ConnectedTo(out inputB); // here we aquire the reference to the target pin directly from the source pin (the variable that we stored above)

Then, GraphEdit iterates through all pins of the target filter and checks if one of the pins corresponds to the target of the source pin (which was queried above):

// in GraphEdit
foreach(IPin pin in filterB.Pins) { // pseudocode
    if(pin == inputB) {
        // pin equals inputB and therefore outputA is connected with inputB
        // WE NEVER GET IN HERE
    }
}

If I execute the above code from the filter graph thread, it works.