VS 2017 defines this interface in Microsoft.VisualStudio.Shell.Interop.15.0.DesignTime.dll:
[Guid("A459C228-5617-4136-BCBE-C282DF6D9A62")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IVsSolutionEvents7
{
void OnAfterCloseFolder(string folderPath);
void OnAfterLoadAllDeferredProjects();
void OnAfterOpenFolder(string folderPath);
void OnBeforeCloseFolder(string folderPath);
void OnQueryCloseFolder(string folderPath, ref int pfCancel);
}
I want to implement that interface in my extension so that I can respond to those events, but I want the same extension assembly to be compatible with Visual Studio 2015, so I don't want a dependency on that VS 2017 DLL. Therefore, I copy-paste that definition into my code.
I can get that definition from the documentation, or from Visual Studio itself via F12 when I add a reference to that DLL, or from JustDecompile. They all give roughly the same definition of the interface.
But that interface definition doesn't work:
- The methods are in the wrong order, so the wrong ones get called.
- The strings aren't passed in properly - my guess is that they are assumed to be BStrs but these are LPWStrs.
- I often get an access violation after a call - my guess is an incorrect calling convention.
If I apply a pile of attributes to the interface, so it becomes this:
[ComVisible(true)]
[ComImport]
[Guid("A459C228-5617-4136-BCBE-C282DF6D9A62")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IVsSolutionEvents7
{
[PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
int OnAfterOpenFolder([In, MarshalAs(UnmanagedType.LPWStr)] string folderPath);
...
and put the methods in the right order, then it works.
How are you supposed to know which of those attributes to use, or which order to to put the methods in? The documentation is not necessarily useful - the documentation for that interface, for example, adds nothing to the simple broken definition above.
Even when I have access to an assembly that defines the interface (Microsoft.VisualStudio.Shell.Interop.15.0.DesignTime.dll in this case), where do these attributes go? When I look at the definition in that DLL in either Visual Studio or JustDecompile, I only see that simple definition, and yet if I use the interface via a reference to that DLL, it works. The attributes are somehow there but invisible, or are being defaulted differently from when I define the interface myself.
I cobbled together the attributes I'm using from cargo-cult copying, examining the IDL, and blind trial and error, and as such I don't really trust them. How should I have done it?