Bookmark and Share

Notes on SampleDocViewEditor

The SampleDocViewEditor from the Visual Studio 2008 SDK is useful for figuring out how to create a designer view for an xml file. However, as noted by Nikita Frolov, there is some trickery involved that will cause it to not work for you if you use a different extension.

I found out that if I change the test extension, used in the sample, to something else (say, .testaddin), it would not receive "View Code | View Designer" options in following situation:

1. Create a new project
2. Add ".testaddin" file to the project
3. Exclude it from the project
4. Close the solution and remove ".suo" file (apparently it keeps some info there), or create a new solution
5. Select "Show All Files" and include it back.
Now, the file will be in project, but without "View Code | View Designer" options (no SubType Designer element in csproj).

Simpliest way to change the extension is to take SampleDocViewEditor sample, run Replace on entire solution, replace all entries of "addin" with "testaddin" and then rename "Templates/Sample.addin".

Sample documentation says: "The XML EditorFactory takes care of setting the VSHPROPID_SubItemType property for us. We just need to register that we are a designer view for a particular type of XML file with the XML Editor." Exploring that, I found out that ".addin" type is an existing type registered under Xml Editor in these keys:

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0Exp\Configuration\ Editors\{FA3CD31E-987B-443A-9B81-186104E8DAC1}\Extensions]

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0Exp\Configuration\ Editors\{412B8852-4F21-413B-9B47-0C9751D3EBFB}\Extensions]

This registration is there after regular VS install and is not affected by this sample. If it is removed, the issue starts to happen for .addin files too. Rebuilding the sample in its original state does not restores these keys.

There's an easy way to add those keys. The ProvideEditorExtension attribute modifies its behavior based on the properties you assign. If you leave off ProjectGuid, it will only place the extension under the Editor's guid in the registry and add the requisite values to the key.

[ProvideEditorExtension("{412B8852-4F21-413B-9B47-0C9751D3EBFB}", ".testaddin", 32)]
[
ProvideEditorExtension("{FA3CD31E-987B-443A-9B81-186104E8DAC1}", ".testaddin", 32)]

However, this didn't seem to solve the problem completely. I still had the same issue. I searched through the registry for relevant entries and discovered that ".addin" was also under Languages/Extensions. This can be placed in the registry by using ProvideLanguageExtensionAttribute.

[ProvideLanguageExtension("{f6819a78-a205-47b5-be1c-675b3c7f0b8e}", ".testaddin")]

While we're on the topic, I should mention the two RegistrationAttribute subclasses provided by the sample: EditorFactoryNotifyForProjectAttribute and XmlDesignerViewAttribute. These should be renamed to follow the convention found in Microsoft.VisualStudio.Shell. I recommend ProvideEditorFactoryNotifyAttribute and  ProvideXmlDesignerAttribute. ProvideEditorExtensionAttribute sets the same keys and values as ProvideEditorFactoryNotifyAttribute, but there's no way to prevent it from setting a few undesirable entries. The custom attribute is required surgically set the EditorFactoryNotify for the XmlEditor. ProvideXmlDesignerAttribute sets keys under XmlDesigners, and there doesn't appear to be a class built into Microsoft.VisualStudio.Shell to provide this functionality.

kick it on DotNetKicks.com

Bookmark and Share

MVP Global Summit

I attended the MVP Global Summit 2009 in Seattle/Redmond, Washington a couple of weeks ago. I had a pretty good time hanging out with other MVPs and the Managed Languages team. I learned a lot, and I had a blast every evening. One of those evenings was at the Experience Music Project, and I promise I will post Steve Andrews, "I love TFS" if I can find the video.

Here's the opening video for the event, followed by a behind the scenes look with Karen Young.

 

Bookmark and Share

Package Attribute Guid Confusion

One of the things that confused me about the attributes used to decorate a Visual Studio were the hardcoded guids in the examples. I didn't know where they came from or what I could use. In fact, I had the impression I had to generate my own guid for each definition like how you need to assign one for an editor factory.

It turns out that these are defined guids that you must use for certain activities. They're defined in Microsoft.VisualStudio.VSConstants as static readonly. Like String.Empty, this means you can't use them in an attribute decoration and is why you see string literals instead. Since I couldn't find a list of the actual guids, and I needed them, I wrote a quick console app to retrieve them from the VSConstants class. I then placed them in constant, string form in my own class. Here they are in case you need them as well.

internal class VSGuidStrings
{
    
public const string IID_IUnknown = "00000000-0000-0000-c000-000000000046";
    
public const string GUID_VSStandardCommandSet97 = "5efc7975-14bc-11cf-9b2b-00aa00573819";
    
public const string VSStd2K = "1496a755-94de-11d0-8c3f-00c04fc2aae2";
    
public const string GUID_VsUIHierarchyWindowCmds = "60481700-078b-11d1-aaf8-00a0c9055a90";
    
public const string CLSID_HtmDocData = "62c81794-a9ec-11d0-8198-00a0c91bbee3";
    
public const string CLSID_HtmedPackage = "1b437d20-f8fe-11d2-a6ae-00104bcc7269";
    
public const string CLSID_HtmlLanguageService = "58e975a0-f8fe-11d2-a6ae-00104bcc7269";
    
public const string GUID_HtmlEditorFactory = "c76d83f8-a489-11d0-8195-00a0c91bbee3";
    
public const string GUID_TextEditorFactory = "8b382828-6202-11d1-8870-0000f87579d2";
    
public const string GUID_HTMEDAllowExistingDocData = "5742d216-8071-4779-bf5f-a24d5f3142ba";
    
public const string CLSID_VsEnvironmentPackage = "da9fb551-c724-11d0-ae1f-00a0c90fffc3";
    
public const string GUID_VsNewProjectPseudoFolder = "dcf2a94a-45b0-11d1-adbf-00c04fb6be4c";
    
public const string CLSID_MiscellaneousFilesProject = "a2fe74e1-b743-11d0-ae1a-00a0c90fffc3";
    
public const string CLSID_SolutionItemsProject = "d1dcdb85-c5e8-11d2-bfca-00c04f990235";
    
public const string SID_SVsGeneralOutputWindowPane = "65482c72-defa-41b7-902c-11c091889c83";
    
public const string SID_SUIHostCommandDispatcher = "e69cd190-1276-11d1-9f64-00a0c911004f";
    
public const string CLSID_VsUIHierarchyWindow = "7d960b07-7af8-11d0-8e5e-00a0c911005a";
    
public const string GUID_DefaultEditor = "6ac5ef80-12bf-11d1-8e9b-00a0c911005a";
    
public const string GUID_ExternalEditor = "8137c9e8-35fe-4af2-87b0-de3c45f395fd";
    
public const string GUID_BuildOutputWindowPane = "1bd8a850-02d1-11d1-bee7-00a0c913d1f8";
    
public const string GUID_OutWindowDebugPane = "fc076020-078a-11d1-a7df-00a0c9110051";
    
public const string GUID_OutWindowGeneralPane = "3c24d581-5591-4884-a571-9fe89915cd64";
    
public const string BuildOrder = "2032b126-7c8d-48ad-8026-0e0348004fc0";
    
public const string BuildOutput = "1bd8a850-02d1-11d1-bee7-00a0c913d1f8";
    
public const string DebugOutput = "fc076020-078a-11d1-a7df-00a0c9110051";
    
public const string GUID_ItemType_PhysicalFile = "6bb5f8ee-4483-11d3-8bcf-00c04f8ec28c";
    
public const string GUID_ItemType_PhysicalFolder = "6bb5f8ef-4483-11d3-8bcf-00c04f8ec28c";
    
public const string GUID_ItemType_VirtualFolder = "6bb5f8f0-4483-11d3-8bcf-00c04f8ec28c";
    
public const string GUID_ItemType_SubProject = "ea6618e8-6e24-4528-94be-6889fe16485c";
    
public const string GUID_BrowseFilePage = "2483f435-673d-4fa3-8add-b51442f65349";
    
public const string guidCOMPLUSLibrary = "1ec72fd7-c820-4273-9a21-777a5c522e03";
    
public const string CLSID_ComPlusOnlyDebugEngine = "449ec4cc-30d2-4032-9256-ee18eb41b62b";
    
public const string GUID_VS_DEPTYPE_BUILD_PROJECT = "707d11b6-91ca-11d0-8a3e-00a0c91e2acd";
    
public const string GUID_ProjectDesignerEditor = "04b8ab82-a572-4fef-95ce-5222444b6b64";
    
public const string UICONTEXT_SolutionBuilding = "adfc4e60-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_Debugging = "adfc4e61-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_Dragging = "b706f393-2e5b-49e7-9e2e-b1825f639b63";
    
public const string UICONTEXT_FullScreenMode = "adfc4e62-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_DesignMode = "adfc4e63-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_NoSolution = "adfc4e64-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_SolutionExists = "f1536ef8-92ec-443c-9ed7-fdadf150da82";
    
public const string UICONTEXT_EmptySolution = "adfc4e65-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_SolutionHasSingleProject = "adfc4e66-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_SolutionHasMultipleProjects = "93694fa0-0397-11d1-9f4e-00a0c911004f";
    
public const string UICONTEXT_CodeWindow = "8fe2df1d-e0da-4ebe-9d5c-415d40e487b5";
    
public const string GUID_VsTaskListViewAll = "1880202e-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewUserTasks = "1880202f-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewShortcutTasks = "18802030-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewHTMLTasks = "36ac1c0d-fe86-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewCompilerTasks = "18802033-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewCommentTasks = "18802034-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewCurrentFileTasks = "18802035-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewCheckedTasks = "18802036-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string GUID_VsTaskListViewUncheckedTasks = "18802037-fc20-11d2-8bb1-00c04f8ec28c";
    
public const string CLSID_VsTaskList = "bc5955d5-aa0d-11d0-a8c5-00a0c921a4d2";
    
public const string CLSID_VsTaskListPackage = "4a9b7e50-aa16-11d0-a8c5-00a0c921a4d2";
    
public const string SID_SVsToolboxActiveXDataProvider = "35222106-bb44-11d0-8c46-00c04fc2aae2";
    
public const string CLSID_VsDocOutlinePackage = "21af45b0-ffa5-11d0-b63f-00a0c922e851";
    
public const string CLSID_VsCfgProviderEventsHelper = "99913f1f-1ee3-11d1-8a6e-00c04f682e21";
    
public const string GUID_COMPlusPage = "9a341d95-5a64-11d3-bff9-00c04f990235";
    
public const string GUID_COMClassicPage = "9a341d96-5a64-11d3-bff9-00c04f990235";
    
public const string GUID_SolutionPage = "9a341d97-5a64-11d3-bff9-00c04f990235";
    
public const string LOGVIEWID_Primary = "00000000-0000-0000-0000-000000000000";
    
public const string LOGVIEWID_Debugging = "7651a700-06e5-11d1-8ebd-00a0c90f26ea";
    
public const string LOGVIEWID_Code = "7651a701-06e5-11d1-8ebd-00a0c90f26ea";
    
public const string LOGVIEWID_Designer = "7651a702-06e5-11d1-8ebd-00a0c90f26ea";
    
public const string LOGVIEWID_TextView = "7651a703-06e5-11d1-8ebd-00a0c90f26ea";
    
public const string LOGVIEWID_UserChooseView = "7651a704-06e5-11d1-8ebd-00a0c90f26ea";
}

 

Bookmark and Share

IE8 Released

IE8 has been officially released and the download is available.

So why should you download IE8? Well, out of all the new features, accelerators are the best! No, it isn't some trick to load web pages faster. Instead, accelerators accelerate your own web browsing experience. For example, just yesterday, I had to find the meeting location where I was giving a presentation. I also need to pull up directions. After I found the address, here's what I would have to do in other browsers to get directions:

Click 1: highlight and select the address information
Click 2: copy that information (ctrl-c)
Click 3: open a new tab or window
Click 4: enter maps.live.com in the address bar
Click 5: paste the address information into that new site
Click 6: enter, and then view the results

Here's what I do in IE8:

Click 1: highlight and select the address information
Click 2: right-click and hover over "Map with LiveEarth" accelerator, click on directions.

This definitely accelerates my browsing experience.

Bookmark and Share

Visual Studio Eats Exceptions

The managed package framework for Visual Studio eats exceptions. I discovered this while building my own editor.

I started with the package that the wizard creates for you, but I needed different designer rather than the rich text box it gives you. I initially attempted to gut the designer and implement my own functionality. However, it kept throwing a strange exception on me that I could not correct.

I reverted back to the designer that was there, then created a DecisionTablePane class and implemented the interfaces that I thought I would need: IOleCommandTarget, IVsWindowPane, IVsTextLinesEvents. I left the NotImplementedExceptions in place thinking that I could then see what was being called. I changed the editor factory to instantiate the new editor. This worked, and I will refactor later to remove the classes I no longer need; I'm keeping them around to use as a guide.

I tried to open the file type I had registered with the editor, but nothing happened. I thought that maybe I missed an interface, but I couldn't find anything. I then stuck a breakpoint on the method I suspected was being called, and it got there. The exceptions were being consumed without any error reporting. This will make it difficult to debug my Visual Studio package. If something doesn't work, I may not even know it.

Bookmark and Share

Reset Experimental Hive

When developing plugins for Visual Studio 2008, your testing is done in what's known as an experimental hive. It's essentially Visual Studio running in a testing environment. This keeps your unfinished plugins from being added to your "real" Visual Studio environment.

Unfortunately, this experimental hive tends to become junky. After going through tutorials, you're bound to have all kinds of unnecessary elements added to the IDE. I spent some time trying to clean this out, and I discovered that the Visual Studio 2008 SDK includes a tool to do this. However, its parameters weren't exactly clear and cause me some grief until I got it right.

Here are the commands that work for me:

"%ProgramFiles%\Microsoft Visual Studio 2008 SDK\VisualStudioIntegration\Tools\Bin\vsregex.exe" GetOrig 9.0 exp RANU
"%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe"  /setup /rootsuffix Exp /ranu

The first command clears out your experimental hive then copies your regular registry keys and user profile files over. After I did this, I found that all my templates were missing. The second command fixes that problem.

Bookmark and Share

CustomDictionary Fail

When I ran code analysis on my project, I predictably had some complaints concerning naming. Obviously, kodefu is not a dictionary term. The solution is to add a custom dictionary to the project.

<Dictionary>
  <
Words>
    <
Recognized>
      <
Word>Kodefu</Word>
    </
Recognized>
  </
Words>
</
Dictionary>

I added this dictionary and reran code analysis. Complete fail; it still did not recognize kodefu.

Whoever wrote the msdn documentation forgot to mention an essential step for adding a custom dictionary to a project. Luckily, argt.home added community content to the article describing the steps necessary to get it working. You must change the item's build action to CodeAnalysisDictionary.

I hope this helps if you've experienced this fail yourself.

Bookmark and Share

IsSubclassOf

It appears that I missed an important method that belongs to Type when making the extension method IsType. Jason Olson pointed out that type.IsSubclassOf would provide the functionality necessary to fulfill my coworker's requirement. The functionality and implementation of IsType is a little different, however. IsType will check the current type then recursively checks each BaseType. IsSubclassOf will return false if the original check indicates that the types are identical. Also, it uses a while loop rather than recursively calling itself.

public virtual bool IsSubclassOf(Type c)
{
    
Type baseType = this;
    
if (baseType != c)
    {
        
while (baseType != null)
        {
            
if (baseType == c)
            {
                
return true;
            }
            baseType = baseType.BaseType;
        }
        
return false;
    }
    
return false;
}

I find this interesting. Leave a comment on whether you prefer recursive methods or loops.

I also discovered that the compiler modifies my code to the following:

public static bool IsType(this Type thisType, Type type)
{
    
if (thisType == null)
    {
        
return false;
    }
    
return ((thisType == type) || thisType.BaseType.IsType(type));
}

I prefer to keep the original style because I feel it is more readable. In addition to the if statement, I had the option to use the boolean trick or the ternary operator to perform the same function. I tend to think that if statements are easier to read, but I'm always looking to improve my coding style. Let me know what you prefer.

Bookmark and Share

IsType

A coworker approached me earlier with a scenario that involved using reflection to find all of the properties in a class that were derived from a specific type. I walked through using reflection to do this, but then I was unable to find a way to interrogate the inheritance chain to determine if the type derived from the type we cared about. You can't use the "is" operator because the type is Type, not an instance of the type.

I ended up making an extension method to do the job. Of course, one could use it like a typical helper method.

public static class TypeExtensions
{
    
public static bool IsType(this Type thisType, Type type)
    {
        
if (thisType == null)
        {
            
return false;
        }
        
else if (thisType == type)
        {
            
return true;
        }
        
else
        {
            
return IsType(thisType.BaseType, type);
        }
    }
}

This construct is pretty simple. We first make sure that we haven't reached the end of the inheritance chain. If so, we need to return that it isn't of the type specified. If the types are the same, we have a match. Otherwise, we need to interrogate the base type. I decided it was acceptable that this checked the original type as well, because one could call property.PropertyType.BaseType.IsType(someType) if the requirement was to check strictly for derived types.

Update:  Jason Olson pointed out that IsSubclassOf would provide the functionality necessary to fulfill my coworker's requirement.

Bookmark and Share

Future Vision Montage

I like Microsoft's vision of the future, but we seem to have given up on flying cars and robots that do all the work for us. Danger, KodefuGuru!

KodefuGuru.GetInfo()

Chris Eargle
LinkedIn Twitter Technorati Facebook

Chris Eargle
Telerik Developer Evangelist, C# MVP

JustCode

Telerik .NET Ninja

 

INETA Community Speakers Program

 

MVP - Visual C#

 

Friend of RedGate

World Map

Tag cloud

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.