At the time of this writing, this is still an unsolved case, but I am posting it anyway with the hope that some day someone can solve it.
Since I included support for Visual Studio 2010 in my add-in MZ-Tools 6.0, I have received bug reports from three customers with this exception:
System.InvalidCastException: Specified cast is not valid.
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode)
at Microsoft.VisualStudio.PlatformUI.Automation.CommandBarCustomizer.Remove(ControlCustomizer control)
at Microsoft.VisualStudio.PlatformUI.Automation.CommandBarControl.Delete(Object Temporary)
at Microsoft.VisualStudio.PlatformUI.Automation.CommandBarControl._Marshaler.<>c__DisplayClass10.<Delete>b__f()
at Microsoft.VisualStudio.Shell.ThreadHelper.Invoke(Action action)
at Microsoft.VisualStudio.PlatformUI.Automation.CommandBarControl._Marshaler.Delete(Object Temporary)
at EnvDTE.Command.Delete()
The exception happens when I delete an EnvDTE.Command of the add-in, which in turn deletes the CommandBarButtons created from it, a technique that I described in the article:
HOWTO: Prevent dead CommandBarButtons when Visual Studio or an add-in crashes.
http://www.mztools.com/articles/2009/MZ2009002.aspx
The problem is not reproducible and it has only happened to three customers, always in Visual Studio 2010, not in Visual Studio 2005 or 2008, so it is related to the new WPF-based commandbars of Visual Studio 2010. So, I used Reflector for .NET (freeware, soon to be paid version) to see what could cause the InvalidCastException in the Microsoft.VisualStudio.PlatformUI.Automation.CommandBarCustomizer.Remove(ControlCustomizer control) method. The Microsoft.VisualStudio.PlatformUI.Automation.CommandBarCustomizer class resides in the Microsoft.VisualStudio.Shell.UI.Internal.dll assembly of the folder C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE and the Remove method is like this:
public void Remove(ControlCustomizer control)
{
ErrorHandler.ThrowOnFailure(base.Wrapped.Remove(control.Wrapped));
}
The ControlCustomizer class is like this:
public class ControlCustomizer : InterfaceWrapper<IVsControlCustomizerPrivate>, IDisposable
And the InterfaceWrapper<T> interface is like this:
public abstract class InterfaceWrapper<T>
{
// Fields
private readonly T _wrapped;
// Methods
protected InterfaceWrapper(T wrapped);
// Properties
public T Wrapped { get; }
}
So, on the one hand, control.Wrapped returns something of the IVsControlCustomizerPrivate type.
On the other hand, the base of the CommandBarCustomizer is of InterfaceWrapper<IVsCommandBarCustomizerPrivate> type, the Wrapped property returns something of IVsCommandBarCustomizerPrivate type and its Remove method is like this:
IVsCommandBarCustomizerPrivate[PreserveSig]
int Remove([In, MarshalAs(UnmanagedType.Interface)] IVsControlCustomizerPrivate pControl);
which expects a parameter of the IVsControlCustomizerPrivate type, the same type that we are passing!. So, how is that an InvalidCastException can happen? The only scenario where I have seen that two types with the same names (name, full name, assembly names) can’t be cast is because they belong to an assembly that is loaded twice from different locations. I am not sure if somehow this is happening here.