Unpleasant problem of interaction of COM in VSIX

For some time, I observed an intermittent COM problem in my VSIX package for Visual Studio 2010. Attempting to subscribe to one of the IDE COM-based event streams randomly causes the following error:

"A COM object that has been separated from its base RCW cannot be used

The copy case comes down to this code (which should be used on VSIX, obviously):

using System; using EnvDTE; using EnvDTE80; class Test { private readonly Events _events; private readonly Events2 _events2; private readonly BuildEvents _buildEvents; private readonly ProjectItemsEvents _projectItemsEvents; public Test(IServiceProvider provider) { var dte = (DTE)provider.GetService(typeof(DTE)); var dte2 = (DTE2)dte; // Store all references in fields as a GC precaution. _events = dte.Events; _events2 = (Events2)dte2.Events; _buildEvents = _events.BuildEvents; _projectItemsEvents = _events2.ProjectItemsEvents; // Proceed to subscribe to event sinks. _buildEvents.OnBuildBegin += BuildBeginHandler; // BOOM! _projectItemsEvents.ItemAdded += ItemAddedHandler; } private void ItemAddedHandler(ProjectItem projectItem) { } private void BuildBeginHandler(vsBuildScope scope, vsBuildAction action) { } } 

I learned about a possible reason from the numerous descriptions of similar problems that can be found on the network. This is basically a side effect of how Runtime Callable Wrappers and GC interact during COM interactions. Here is a link with a similar problem complete with an explanation.

I am well versed in this explanation, especially because it offers a simple workaround - storing a reference to the event receiver in a field to prevent its premature GC. Indeed, many people seem to have solved the problem this way.

It bothers me that it does not work in my case . I am very surprised why. As you can clearly see, I already kept all references to objects in the fields as a precaution. However, the error is still happening. I tried to be more explicit using GC.KeepAlive() calls at the end of ctor, but to no avail. Is there anything else to do?

Without a solution, my VSIX does not accidentally load, leaving the user with one option: restart Visual Studio and hope that this does not happen next time.

Any help would be really appreciated!

+4
source share
2 answers

Well, I gave up and just did the only thing that crossed my mind. I figured that since this is obviously a race condition, I cannot influence the predictable manner, I could enter the race again if I lose.

So, I moved the subscription lines to the while , which try .. catch calls them and repeats them after the Thread.Sleep() bit. The cycle ends when both subscriptions succeed or when I constantly lose the race for more than 2 seconds.

Kicker, I have not lost the race once since I implemented the change. Real Heisenbug if I ever saw him.

In any case, I will adhere to this until the right solution occurs or the error reappears.

+1
source

I suspect your problem is that you are connecting event handlers too quickly. Usually you need to do such things in the Initialize method of your package / tool window / independently - generally speaking, if you need to use the service you need to do, if you definitely do not need to do this in the constructor of your package after calling the Initialize method.

(This is just a hunch - your Test class does not implement VSX interfaces and therefore cannot see from your sample when calling the constructor)

0
source

All Articles