Add-In: Problem with restoring Command.Bindings
-
Sunday, January 15, 2012 12:35 PM
I create an add-in for Visual Studio 2005/2008/2010 using Visual Studio 2008 as well as 2010 (SP1) and .NET 3.5.
Overview
The add-in uses the so called temporary UI approach, which means to delete all its UI when Visual Studio is closed and recreate it the next time Visual Studio is started. This actually is a workaround to prevent dead UI elements.
To remove every single UI element of a command, the command has to be deleted. Visual Studio removes any associated UI of the command automatically. Afterwards, the command is created again.
In order to not lose existing shortcut bindings, I backup the command bindings before I delete the command and assign the same bindings when I create the command again, basically it works like this code:
Command existingCommand = ...; // keep a reference to bindings, so they don't get lost object[] commandBindingsBackup = (object[])existingCommand.Bindings; // delete the command and any associated UI controls existingCommand.Delete(); // re-create the command Command newCommand = CreateCommandButton(...); // re-assign the backuped bindings newCommand.Bindings = commandBindingsBackup;
Problem description
The problem is when I re-assign the old bindings in Visual Studio 2010. Bindings have to be specified using their localized names, at least in Visual Studio 2005 and 2008. In 2010, this seems to have changed.
The old binding (commandBindingsBackup[0]) equals "Global::Strg+Umschalt+Alt+O", this is what I get back from the Command.Bindings member. It's the german version of "Global::Ctrl+Shift+Alt+O". However, when I re-assign this same binding to the freshly created command, an ArgumentException is thrown: Wrong Parameter. (Exception of HRESULT: 0x80070057 (E_INVALIDARG)).
When I assign the english version "Global::Ctrl+Shift+Alt+O" instead, it works as expected.
My question
How can I keep existing bindings when I re-create a command in Visual Studio 2010? I don't think it's an option to translate whatever I get from Command.Bindings to english and then assign it.
Thanks in advance
Peter
All Replies
-
Monday, January 16, 2012 6:01 AMModerator
Can you double check this actually worked on version prior to 2010? This code didn't change in 2010 and I see things like this in the code:
if(!_wcsicmp(pszTempCurrent, L"CTRL"))
*pfCTRL = TRUE;
else if(!_wcsicmp(pszTempCurrent, L"ALT"))
*pfALT = TRUE;
else if(!_wcsicmp(pszTempCurrent, L"SHIFT"))
*pfSHIFT = TRUE;So I can't imagine this ever could have worked, unless it was returning non-localized binding text in the past (but as I said none of this code changed in 2010).
This definetly seems to be a bug, and I can't see any work around since the parsing code is using hard coded string literals to look for 'special' tokens in the input stream from what I can tell via code inspection (I haven't debugged into this, but unless we have a back mapping from 'localized version of CTRL -> "CTRL"' I don't see this code being correct).
Ryan
-
Monday, January 16, 2012 6:20 PM
Hi Ryan, I double checked with a german version of Visual Studio 2008 and you are right. Modifiers (Ctrl, Alt, Shift) must be specified in english. However, the scope (Global, Text Editor, etc) must be specified using the localized name. For example it's "Text Editor" in english and "Text-Editor" in german (with a dash in the middle).
Can you think of any workaround that would do the job? I don't try to win a beauty contest, it should just get the job right.
-
Monday, January 16, 2012 6:41 PMModerator
Well, it won't win any beauty contests, but you could do something like this
IVsShell shell = (IVsShell)GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SVsShell)); Guid taskListPgGuid = new Guid("4A9B7E50-AA16-11d0-A8C5-00A0C921A4D2"); uint globalKeybindingScopeNameId = 13018U; string globalKeybindingScopeName ; int res = shell.LoadPackageString(ref taskListPgGuid, globalKeybindingScopeNameId, out globalKeybindingScopeName); uint textEditorKeybindingScopeNameId = 13022U; string textEditorKeybindingScopeName; res = shell.LoadPackageString(ref taskListPgGuid, textEditorKeybindingScopeNameId, out textEditorKeybindingScopeName); uint localizedCtrlKeyId = 13358U; string localizedCtrlKeyText; res = shell.LoadPackageString(ref taskListPgGuid, localizedCtrlKeyId, out localizedCtrlKeyText); uint localizedAltKeyId = 13359U; string localizedAltKeyText; res = shell.LoadPackageString(ref taskListPgGuid, localizedAltKeyId, out localizedAltKeyText); uint localizedShiftKeyId = 13360U; string localizedShiftKeyText; res = shell.LoadPackageString(ref taskListPgGuid, localizedShiftKeyId, out localizedShiftKeyText);
Then you could use the given localized Global or Text Editor namespace scope fetched above, and use the localized versions of Ctrl, Alt and/or Shift to check the keybinding and then backmap to hard coded English variants in your string before you set the binding.Ryan
- Edited by Ryan MoldenMicrosoft Employee, Moderator Monday, January 16, 2012 6:42 PM
- Proposed As Answer by lucy-liuModerator Tuesday, January 17, 2012 5:41 AM
- Marked As Answer by Peter Schraut Tuesday, January 17, 2012 7:50 PM
-
Tuesday, January 17, 2012 7:50 PM
Hi Ryan, thanks for the code. I have implemented it as you suggested and it works, hooray!
What I didn't think about, you can also assign special keys like Delete, Return and Up Arrow for example. These need to be specified in english as well to get this whole thing working. Luckily the translated strings can be found via LoadPackageString as well, so it's just a bit more backmapping.
Thanks for the help
Peter
- Edited by Peter Schraut Tuesday, January 17, 2012 7:51 PM
-
Tuesday, January 17, 2012 8:54 PMModerator
Glad it works, I opened a bug about this internally, we should take English or localized text really. My gut says it may not be approved for fix as we are fairly late in the release cycle and there is a work around, even though it is kind of ugly.
The main problem is it relies on the tasklist package being inside msenv.dll and thus sharing its resource dll, it also relies on the resource ids not changing. Neither of those things would change for the next release for any reason I can imagine, and seem unlikely to change even in the release after that (moving packages between dlls has to have an actual reason), but ideally you wouldn't need to do any of this gross stuff. Sorry about that.
Ryan
-
Sunday, September 09, 2012 7:23 PMModerator
FWIW, I have opened a bug about this externally through Microsoft Connect:
It affects several add-ins, including mine.
MZ-Tools: Productivity add-ins for Visual Studio: http://www.mztools.com. My blog about developing add-ins: http://msmvps.com/blogs/carlosq/

