
Question:
In my Visual Studio extension, I'm going to read the text that is in the navigation bar. Therefore I listen to window created events and from the IVsCodeWindow object I get the IVsDropdownBar to get the current selection in the dropdown bar. This works fine. Then I'm using the following code snippet to extract the text of the current selection:
string text;
barClient.GetEntryText(MembersDropdown, curSelection, out text);
if (hr == VSConstants.S_OK)
{
Debug.WriteLine("Text: " + text);
} else {
Debug.WriteLine("No text found!");
}
However, this does not work. My extension crashes with an unhandled exception in the second line of the code snippet. I read the documentation and could find the following note:
<blockquote>The text buffer returned in ppszText is typically created by the IVsDropdownBarClient object and the buffer must persist for the life of the IVsDropdownBarClient object. If you are implementing this interface in managed code and you need to have the string disposed of by the caller, implement the IVsCoTaskMemFreeMyStrings interface on the IVsDropdownBarClient interface.
</blockquote>I assume that this is part of my problem, but I can't really understand what I have to change in my code to get it working. Any hints?
Answer1:I'm pretty sure now that the Visual Studio SDK Interop DLLs have the wrong marshalling information for IVsDropDownbarClient.GetEntryText
and that there's no way to call that method using that interface.
The best workaround I've found so far is:
<ol><li>Use the tlbimp
tool to generate an alternate Interop DLL for textmgr. (You can safely ignore the dozens of warnings including the one about GetTextEntry.)
tlbimp "c:\Program Files (x86)\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Common\Inc\textmgr.tlb"
</li> <li>(Optional) If you're using source control, you'll probably want to copy the resulting file (TextManagerInternal.dll) to a subdirectory of your extension project and check it in as an external dependency.
</li> <li>In your Visual Studio extension project, add a reference to the file (TextManagerInternal.dll).
</li> <li>Add the following method that should properly handle the string marshalling.
static public string HackHackGetEntryText(IVsDropdownBarClient client, int iCombo, int iIndex)
{
TextManagerInternal.IVsDropdownBarClient hackHackClient = (TextManagerInternal.IVsDropdownBarClient) client;
string szText = null;
IntPtr ppszText = IntPtr.Zero;
try
{
ppszText = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)));
if(ppszText == IntPtr.Zero)
throw new Exception("Unable to allocate memory for IVsDropDownBarClient.GetTextEntry string marshalling.");
hackHackClient.GetEntryText(iCombo, iIndex, ppszText);
IntPtr pszText = Marshal.ReadIntPtr(ppszText);
szText = Marshal.PtrToStringUni(pszText);
}
finally
{
if(ppszText != IntPtr.Zero)
Marshal.FreeCoTaskMem(ppszText);
}
return szText;
}
}
</li> </ol>