barcode source code ref System.Array custom in Visual C#

Printer Code39 in Visual C# ref System.Array custom

ref System.Array custom
Code 39 Encoder In Visual C#.NET
Using Barcode encoder for .NET Control to generate, create Code 3/9 image in VS .NET applications.
Scanning Code 39 In Visual C#.NET
Using Barcode reader for Visual Studio .NET Control to read, scan read, scan image in .NET applications.
ApplicationObject = (_DTE)application; AddInInstance = (AddIn)addInInst; // Your tool window must have a unique GUID. String guid = "{E16579A4-5E96-4d84-8905-566988322B37}" ; // This'll contain the VSNetToolHostShim on output. Object RefObj = null ; // Create the main tool window by loading the host shim. TheToolWindow = ApplicationObject.Windows. CreateToolWindow , "VSNetToolHostShim.VSNetToolWinShim", "Scratch , guid , ref ); // Make the window visible. You must do this before calling // the HostUserControl method or things won't get hooked // up right. TheToolWindow.Visible = true ; // Get the shim. (This is a class level variable): // private VSNetToolHostShimLib.IVSNetToolWinShim ShimObj ; ShimObj = (VSNetToolHostShimLib.VSNetToolWinShimClass) RefObj ; // Get this assembly so I can pass the location to the shim. 385 RefObj Pad Window" ( AddInInstance
Painting Barcode In C#
Using Barcode drawer for VS .NET Control to generate, create barcode image in .NET applications.
Bar Code Decoder In Visual C#
Using Barcode scanner for .NET framework Control to read, scan read, scan image in VS .NET applications.
System.Reflection.Assembly CurrAsm = System.Reflection.Assembly.GetExecutingAssembly ; // Get the directory to this Add-In and append the name of // the resources DLL to the path so I can load the tab // button. StringBuilder StrSatDll = new StringBuilder ( ) ; String StrTemp = CurrAsm.Location.ToLower ( ) ; int iPos = StrTemp.IndexOf ( "simpletoolwindow.dll" ) ; StrSatDll.Append ( CurrAsm.Location.Substring ( 0 , iPos )); StrSatDll.Append ( "SimpleToolWindowResources.DLL" ) ; // Load the managed control into the ActiveX control and // have it load the bitmap. ShimObj.HostUserControl2 ( TheToolWindow CurrAsm.Location StrSatDll.ToString ( ) 1 ); } catch ( System.Exception eEx ) { MessageBox.Show ( eEx.Message + "\r\n" + eEx.StackTrace.ToString ( ) "ExceptBion in OnConnection" } } , ) ; , , , ( )
Painting Code 3 Of 9 In VS .NET
Using Barcode printer for ASP.NET Control to generate, create Code 3/9 image in ASP.NET applications.
Drawing Code 39 Extended In .NET Framework
Using Barcode creator for .NET framework Control to generate, create Code39 image in VS .NET applications.
"SimpleToolWindow.ScratchPadControl" ,
USS Code 39 Creator In VB.NET
Using Barcode creator for .NET framework Control to generate, create Code 39 Full ASCII image in .NET framework applications.
Drawing 2D Barcode In Visual C#
Using Barcode encoder for VS .NET Control to generate, create Matrix Barcode image in VS .NET applications.
Creating Options Property Pages with Managed Code Creating managed tool windows is relatively easy. Trying to create managed property pages that plug into the Options dialog box is a little weirder. It's important to get your pages into the Options dialog box because that's the common place users will look to modify your add-in settings, and it gives you a polished look. Figure 9-5 shows the SettingsMaster options property page.
GTIN - 128 Encoder In C#
Using Barcode generation for .NET framework Control to generate, create USS-128 image in VS .NET applications.
QR-Code Maker In Visual C#.NET
Using Barcode drawer for VS .NET Control to generate, create QR Code ISO/IEC18004 image in .NET applications.
Figure 9-5: SettingsMaster Options property page As you've probably guessed by now, an options property page is an ActiveX control that implements the IDTToolsOptionsPage interface. Visual Studio .NET finds out whether you have an options property page by looking in the add-in registry key. Under the main add-in key, it looks for an Options key. Under the Options key will be one or more keys that will be added as top-level nodes to the Options dialog box tree. By convention, you'll have one key and that's the name of the add-in. Under that key will be another set of keys that will form the subnodes underneath the top tree node. By convention, the first value will be General. Inside each final key will be a string value, Control, which contains the ProgID of the ActiveX control to create in order to show the property page. It's probably easiest to show a complete key as an example. For the SettingsMaster property page in Figure 9-5, the registry keys are as follows: HKEY_CURRENT_USER\ Software\ Microsoft\ VisualStudio\ 7.1\ AddIns\ SettingsMaster\ Options\ SettingsMaster\ General SettingsMaster Value item inside the General key Control REG_SZ SettingsMasterShim.SettingsMasterOption <-- Add-In key <-- Options key <-- Root node in Options dialog <-Sub node under
Bar Code Creator In Visual C#
Using Barcode generator for VS .NET Control to generate, create barcode image in VS .NET applications.
USPS Confirm Service Barcode Maker In Visual C#.NET
Using Barcode maker for .NET Control to generate, create USPS Confirm Service Barcode image in .NET framework applications.
The Options dialog box, not your add-in, controls creation and management of the property pages, which is a small problem when it comes to writing your individual property pages in managed code. The problem is that because the specific control started up is the ActiveX control specified in the Control string value. This means the ActiveX control created will have to have a priori knowledge of the managed control you want to show. I was really scratching my head over this one when the February 2002 issue of MSDN Magazine landed on my desk and Leo Notenboom had an excellent solution for the problem in an article. Leo's trick was to write the C++ ActiveX shim control so that it did all the work. Because there's no possible way to write a generic ActiveX control for option property pages, as you can for tool windows, you're going to have to create a new control for each project. Fortunately, all you really need to do is lift Leo's code, change the GUID and name of the control in the control's .RGS files and, in the C++ code, change the GUID of the control to load. You'll want to read Leo's excellent article on add-ins for a complete description of how his code works. I took Leo's code and added a few assertions and some more error handling to make it easier to find problems. If you borrow either SuperSaverOptionsShim or SettingsMasterShim from this book's sample files, search for the k_HOSTCLSID string in the main .CPP files and replace the GUID that is embedded in the string with the GUID of your particular option page. Of course, change the control's name and GUID in the .RGS files. When I first got my option pages to show up, I thought life was good. When I moved my add-in to my laptop and looked at the option property page, I realized something was wrong because my option property page didn't look anything like the other property pages in the Options dialog box. I'd used a little known trick on my laptop to get dialog boxes and tool windows to show up better (as my laptop has a pretty insane screen resolution): I changed the Dialogs And Tool Windows font in the Environment folder, Fonts And Colors node. Since managed controls default to a fixed Microsoft Sans Serif 8.25-point font instead of asking their host for the correct font, I needed to do the work myself to find the hostspecified font. Look in the SuperSaver project and see the control named OptionPropPageBase.CS. It's a base class that looks up the current dialog box font and size and applies those settings to all the controls on the page. You would think getting the font and size would be a trivial matter, but late-binding properties such as these are mostly undocumented. That's why the unsupported Extensibility Browser I mentioned earlier in the chapter is something very worth using. Once I figured out the magic incantations, I was home free. Listing 9-4 shows the magic in the OptionPropPageBase.OnAfterCreated method that gets the appropriate font, creates it, and sets the dialog box and all controls.
ANSI/AIM Code 39 Generator In Java
Using Barcode encoder for Java Control to generate, create Code 3/9 image in Java applications.
Paint UPC A In None
Using Barcode printer for Office Word Control to generate, create UPC-A Supplement 2 image in Microsoft Word applications.
Listing 9-4: Getting and setting the fonts for an option property page public virtual void OnAfterCreated ( DTE DTEObject ) { // To ensure this option property page looks right, I need to // set all the fonts to what the user chose as the Dialog and // Tool Windows font. I'll use the late-binding stuff to get // the values out of the DTE properties. Properties Props = DTEObject.get_Properties ( "FontsAndColors", 388
Generate USS Code 39 In None
Using Barcode creation for Office Word Control to generate, create Code-39 image in Office Word applications.
Paint Code 128 In Java
Using Barcode generator for Android Control to generate, create Code 128C image in Android applications.
"Dialogs and Tool Windows" ); String FntName = (String)Props.Item ( "FontFamily" ).Value ; Object ObjTemp = Props.Item ( "FontSize" ).Value ; Int32 FntSize = Convert.ToInt32 ( ObjTemp ) ;
Recognize UPC-A In Java
Using Barcode scanner for Java Control to read, scan read, scan image in Java applications.
PDF417 Creator In Java
Using Barcode creator for Android Control to generate, create PDF417 image in Android applications.
// Create the font. Font DlgFont = new Font ( FntName FntSize GraphicsUnit.Point // Set the font on the dialog. this.Font = DlgFont ; // Loop through all the controls on the dialog and set their // fonts as well. Some controls will pick the above up, but // not all so that's why I need to do this manually. foreach ( Control Ctl in this.Controls ) { Ctl.Font = DlgFont ; } } , , ) ;
UCC.EAN - 128 Creation In None
Using Barcode generation for Font Control to generate, create USS-128 image in Font applications.
Data Matrix Recognizer In .NET Framework
Using Barcode reader for .NET framework Control to read, scan read, scan image in .NET applications.
Of course, simply setting the font is only part of the final job. Although the label controls can be set to automatic sizing, most controls can't be, so you'll need to loop through and increase the sizes for any controls that don't automatically resize. You can look at my SuperSaverOptions.OnAfterCreated method to see how I handle resizing for a specific dialog box. You might be wondering why I don't have any code hooked up to change the dialog box fonts on the fly after someone requests they change. The good news is that for dialog box fonts, they can be changed just by restarting the IDE. Interestingly, Visual Studio .NET lets you change all other fonts on the fly, except dialog box fonts. My cool OptionPropPageBase.CS code will take care of some work for you, but it does expose a bug in Visual Studio .NET that makes the tool very difficult to use. If you derive your option control from OptionPropPageBase, the IDE will no longer open your option control in design mode and will simply treat it as a straight text file. What you'll need to do is temporarily set the base class to your option control to System.Windows.Forms.UserControl so that Visual Studio .NET can load the control into the design view, allowing you to edit it with the designer. I certainly hope Microsoft gets this fixed in a service pack or a future version.
Common Debugging Question: I have an assembly that's loaded only in my add-in. Do I have to install it in the global assembly cache (GAC) The GAC is a special place, and you shouldn't install anything in it unless absolutely necessary. Fortunately, the Visual Studio .NET IDE designers were thinking smart, and under the <VS.NET Installation Dir>\Common7\IDE directory are two directories for add-in or macro-only assemblies: PublicAssemblies and PrivateAssemblies. If you want to allow other add-ins or macros to call code in your assembly, place the assembly in the PublicAssemblies directory. If you want the assembly callable only by your add-in, put it in the PrivateAssemblies directory.
Common Debugging Question: Are there easier ways of debugging add-ins since they can load into the IDE that you're using to debug Debugging an add-in can be a huge pain because you have to remove the add-in's registry key, open the add-in project in the target IDE, and restore the add-in keys so that spawned instances of the IDE you want to debug will have everything set up. Though not too onerous a task, you can easily mess these steps up. Additionally, if you need to compile the add-in because you fixed a bug, and the add-in gets loaded by the IDE, your build will never work Fortunately, there's an undocumented command-line switch, /rootsuffix, that comes to the rescue. What /rootsuffix does is tell Visual Studio .NET to append a suffix to the normal registry key and load all packages, add-ins, and settings from that registry key instead of from the default. It's almost like having two separate installs of Visual Studio .NET on your machine. The first thing you need to do is start up REGEDIT.EXE and scoot to the key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1. After selecting the key, select Export from the File menu and save the registry keys to a file. Once you've saved the file, open it in NOTEPAD.EXE and replace all instances of "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\ 7.1" with "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\ 7.1NoAddIns." Notice the "NoAddIns" added as the suffix to the registry key. After replacing all the keys, save the file. Back in REGEDIT.EXE, select Import from the File menu, and import the changed file into the registry. If you'd like to move your user settings over to the same export (minus any add-ins, of course!), change and import steps with HKEY_CURRENT_USER\Software\ Microsoft\VisualStudio\7.1. To start Visual Studio .NET and have it use the key with the NoAddIns suffix, simply start Visual Studio.NET in the following way: devenv /rootsuffix NoAddIns This allows you to have a copy of Visual Studio .NET running in which you can play all you want with your add-in code without running into any problems.
The SuperSaver Add-In Now that you have some idea of the issues associated with add-ins, I thought it best to discuss some real-world add-ins because they offer the best way to learn. The first add-in I created was SuperSaver, which originally appeared in a Bugslayer column I wrote in MSDN Magazine. However, the add-in version in this book is completely and radically different 390
from the original and is an excellent example of the trials and tribulations associated with writing add-ins. SuperSaver's job in life is to solve two problems. The first is to add a background automatic save to the Visual Studio .NET IDE so that you don't lose any changed documents. The second is to trim white space off the ends of lines when saving files. With C++ and C# files, the Visual Studio .NET IDE contradicts the computer gods and leaves the white space on the ends of lines. In order to make everything right with the world, I simply had to fix that. Solving the background file saving was almost too easy. I set up a background timer, and whenever the timer triggers, I execute the command associated with the File Save All menu option, File.SaveAll. The problem was implementing the white space trimming. As I played around with saving the active item, I noticed that the command that actually executed was File.SaveSelectedItems. Interestingly, instead of saving just the current document being edited, that command also saved the project as well as any items that were selected in Solution Explorer. (You can select multiple items with a Ctrl+left mouse click.) Since I wanted my command to be a drop-in replacement for File.SaveSelectedItems, I needed to mimic that behavior. My initial design was the following algorithm: get the DTE.SelectedItems collection foreach item in the selected items if it's a document and not saved get the text document call ReplacePattern on the text document end if call Save on the item next An algorithm that seems so simple certainly caused all sorts of problems for me. In all, I ended up writing seven completely unique versions of SuperSaver trying to work around issues. The first problem I ran into was related to the projects themselves. Not all project types support the Save method. The setup projects are a good example. This meant there was no guarantee I could save all the projects. I tried to special-case the calls to the Save method, but there was no way I could properly handle all the future project types that might not support the save. The second issue I stumbled into was a bug in the TextDocument object. I was calling the ReplacePattern method with "[ \t]+$" (without the quotes) as the regular expression search pattern and an empty string as the replacement text. Life was good, but that expression also will clear any blank lines that are simply indented white space. So if I saved the active file with the cursor indented 20 characters using tabs or spaces, the save would remove the automatic indenting and I'd end up having to type it all back in. Though I like my tab key as much as the next guy, I thought it would be better to fix SuperSaver to trim the white space only on the lines that actually contained text. After many hours of messing around with regular expressions, I found the ultimate search expression, "{[^ \t]+}{[ \t]#$}", and replacement expression, "\1". Subexpression replacement like this is one of the coolest things about regular expressions. In case you're not a regular expression maven, the search expression says to match any expression in the first group (delineated by the first set of braces) that contains any characters other than a space or a tab. This ensures that the match will occur only on lines that have characters in them. The 391
second group (delineated by the second set of braces) says to match one or more space or tab characters at the end of a line. These two groupings will match only when there is white space at the end of the line. The replacement string "\1" tells the regular expression parser to replace the text with that matching the first grouping. In this case, the matching text is the characters at the end of the string without any white space. Thus, the white space is removed from the string. I changed the parameter for TextDocument.ReplacePattern to my new search expression and let a save rip. The result was that any lines ending with extra spaces now ended with the ending text plus a "\1" that replaced the extra spaces! This was an interesting result, but the idea of actually corrupting the code probably would not make SuperSaver that useful. Come to find out, TextDocument.ReplacePattern has a bug in it that prevents subexpression substitution from working. If I couldn't get regular expression subexpression substitution to work, SuperSaver wasn't going to be that useful. While playing around with different workaround possibilities, I recorded a find-and-replace macro that used a global Find object and worked with subexpression substitution. Although not as clean as the ReplacePattern method, it was at least the start of a workaround. The first thing I realized about using the Find object was that because it's a global object, any changes I make to it while doing my regular expression subexpression substitution inside SuperSaver modifies the current values the user leaves in the Find dialog box. I created the SafeFindObject to wrap the Find object so that I could save and restore all the values, preventing the user from losing any settings. An additional issue I found when using the global Find object was that when a Windows Forms designer was the active document, I could get the TextDocument object for the document. However, the Find.Execute method, which does the actual search and replace, caused an exception. In SafeFindObject, I decided that the only thing I could do was to eat any exceptions thrown out when calling Find.Execute. Everything was moving along when I decided I really wanted the auto save to strip trailing white space as well. After all the major work of simply trying to get the active document properly stripped and saved, I thought I was home free. Unfortunately, if I told the global Find object to do its magic on all files and any read-only files were open, things were not so good. Therefore, I simply looped through all open files and, if any of them were marked as read-only, I didn't call my SafeFindObject to do the trim white-space save. Although I thought I had something going with the auto save, I noticed a problem when I had "virgin files," which are files created with File New but not saved. Calling the Save method on those files brought up the Save File As dialog box. I thought that since the dialog box was up, I'd find a blocking call inside the Save method. However, there wasn't a blocking call, so if I left the IDE running, I'd eventually have a bunch of Save File As dialog boxes up and life would become unhappy in the IDE. At this point, I became a little obsessed with getting auto file saving to work in a way that would strip the white space off the end of lines. You might want to pull up TrimAndSave.CS and move to the TrimAndSave.SaveAll method to follow along with this discussion. Look for the commented out region marked "Original Attempt." I'll discuss why this code does not work in a moment. Since I had the Find object working on the current file, I thought I could pop each file that needed saving to the foreground and save it quickly. That seemed reasonable until I ran into a whopper of a problem. If I had a Windows Forms document open in both design view 392
and code view, calling the Active method for the document always activated the design view, even when the code view was the active window for that Windows Forms code. That meant that while I was typing along in the code view for a Windows form, an auto save would kick off, and I'd end up staring at the design view. Amazingly, there's no way to activate a code view from a document in the automation model. My quest was to find the active document caption, i.e., the active window under the tab strip. Although you can call the DTE.ActiveWindow, doing so returns the window that currently has focus in all the IDE windows, not the window in which you're editing or designing. After a lot of poking, I saw that the Window command bar happens to always have the active document caption in the menu option that starts with "&1" (the ampersand indicates the item in the menu is to be underlined). It's really ugly to poke through a menu to get the actual active document caption, but there was no way to get it in the automation model. Armed with the active document caption string as well as the value returned by DTE.ActiveWindow, I could finally consider how to do the saving because I could at least restore the current active document window and the actual focus window. As I looped through the documents, I needed to do a couple of things before I could save the file. The first was to determine whether the file was a virgin file by looking at the first character in the filename. If the first character was a ~ (tilde), the file was created but never saved. Because I wanted TrimAndSave.SaveAll to behave like a real auto save (see SlickEdit's Visual SlickEdit for the perfect auto save), I had the option of telling TrimAndSave.SaveAll not to save virgin files or read-only files. Doing that would allow me to avoid being inundated with Save File As dialog boxes each time the auto save was triggered. I could specify in SuperSaver's option dialog box that I wanted to skip virgin files and read-only files. If the file was a virgin file or a read-only file, TrimAndSave.SaveAll would skip the current file and loop back for the next one. After I determined that the document needed saving, it was time to bring the document to the foreground so that the DTE.Find object could work on it. Since I needed to ensure that the text editing window of the file got brought to the foreground, I had to look for a window caption that had the same name as the document. If I found that window, I could finally strip the white space from the lines. If I didn't find a text window, I simply moved on to save the file. If the file is a virgin file, I do my own Save File As dialog box, which is no big deal. If the file already has a name, I can simply call the Document.Save method. Interestingly, the Document.Save method is a classic example of how not to design your exception handling. If the file is read-only, Document.Save will pop up the dialog box that asks whether you want to overwrite the read-only file or save the file to a new name. If you click Cancel to skip saving the file, Document.Save throws a COMException class whose message is "User pressed escape out of save dialog." Because this is a normal user interaction, it should have been reported through a return value. After winding through all the documents, I could finally turn to restoring the original active document window as well as the active window itself. After restoring the windows, I could turn to saving the projects and the solution. With a project that needs saving, the first action is to determine whether the project is readonly. There's no property on a Project object that will tell you whether a file is read-only. Consequently, I have to get the project's file attributes and check them. If the project is read-only and the user doesn't want to be prompted on auto saves, I won't save that project.
If the project needs to be saved, some more fun begins. As I mentioned back in the "Problems with Projects" section, the project object model in Visual Studio .NET isn't completely thought out or well documented. Because the Project object doesn't map well onto a VCProject in particular, I attempt to get the VCProject out of a project by first checking the project's language. If the project is a C++ project, I call Project.Object and cast the return to a VCProject. Armed with the VCProject, I can call VCProject.Save with confidence. If the project isn't a VCProject, I attempt first to call Save, and if calling Save causes an exception, I call SaveAs, passing the full project name in each case. Because Microsoft hasn't fully documented the different types of projects, this is the best I can do to get the project saved. Once the projects are taken care of, I can finally save the solution, if necessary. Like projects and documents, when the solution is read-only and the user doesn't want to be bothered with Save File As dialog boxes, I don't save the solution. While I thought I had a working implementation, a little bit of testing quickly disabused me of that notion. As I tested the auto save a little bit with the white space strip option turned on, I thought there was too much flashing going on because of all the text windows being brought to the foreground. I remembered reading that the DTE object supported a SuppressUI property that, if set to true, blocked UI display when code was running. Figuring that SuppressUI would solve the flashing taskbar issues, I set it to true near the beginning of TrimAndSave.SaveAll. Alas, that seemed to have no effect whatsoever; the flashing continued unabated. While I could have lived with the flashing, the other problem with using the Window.Active method was that it attempted to bring the whole IDE to the foreground, not just activating a particular document window. Additionally, if the IDE was minimized, Window.Active restored the window. The final problem was that by using the Find object in the background save, which occurs on a different thread because of the timer, seems to mess up its state. Calling SuperSaver.SuperSaverSave, which I assigned to Ctrl+S worked fine. However, after a background save, whenever I used SuperSaver.SuperSaverSave, the Find/Replace message box that pops up after you've used the Find dialog box started appearing. While I loathe requiring you to turn off the Find/Replace message boxes to use SuperSaver, I was willing to consider it. You can turn off the Find/Replace message box by unchecking Show message boxes in the Options dialog box, Environment folder, Documents property page. With the Find/Replace message boxes turned off, I heard the default beep, like you do with the Find dialog box, every time SuperSaver.SuperSaverSave executed. At this point, I was extremely frustrated, but bound and determined to get something working. Fortunately, my final attempt, while not perfect, got me mostly what I wanted. Since I was stuck using the Find object with the vsFindTarget.vsFindTargetOpenDocuments option, which dictates to search and replace in open documents only, I had to be careful. I could safely strip only white space in the background only if there were no read-only or virgin files in the active documents. While I would have really liked white space stripping on all files when doing a background save, this was the best I could do. To handle the save itself, the only option I had was to call the real File.SaveAll. Because I still wanted the option of not facing Save File As dialogs or overwrite warning message boxes popping up, I will not call File.SaveAll if the user has unchecked Save New And Read-only Files When Auto Saving in the SuperSaver options; there are no read-only files that need saving or virgin files in the active documents.
Of course, even though the above paragraph describes a fairly straight forward algorithm, I would have to run into one more bug. The Document.ReadOnly property, which is supposed to return true if the file is read-only, does not work. I had to manually check the file read-only state with the File.GetAttributes method. I finally had two commands in my add-in, SuperSaver.SuperSaverSaveAll and SuperSaver.SuperSaverSave, that I thought were working fairly well. I turned my attention to creating a command toolbar for them and ran into the bitmap masking issues that I discussed earlier. After fixing those, I ran into the final problem with SuperSaver. Since my intention was to write replacement commands for File.SaveSelectedItems and File.SaveAll, I wanted to make sure my toolbar buttons reacted in the same way they do on real toolbars. With lots of experimentation, I noticed that only the File.SaveSelectedItems button greyed out to indicate it was disabled. I tried everything I could think of to get my SuperSaver.SuperSaverSave toolbar button to behave the same way. Since the active state was controlled by what was selected in the current solution and project, I could not find the magic incantation of checks that File.SaveSelectedItems was performing to enable and disable its button. Just as I was about to give up, it dawned on me that I certainly didn't need to go about it the hard way. All I had to do was retrieve the File.SaveSelectedItems command object and check whether the IsAvailable property was true; if it was, the toolbar button was enabled. Consequently, in my IDTCommandTarget.QueryStatus method, when the File.SaveSelectedItems command is not active, I return vsCommandStatus.vsCommandStatusUnsupported and all is right with my buttons and the world. SuperSaver was a total pain in the neck to develop, but I'm glad I did it. Not only did it teach me a tremendous amount about the foibles of add-ins and the Visual Studio .NET IDE automation model, but I made the programming gods very happy by killing those spaces at the end of lines. In comments in SuperSaver, I left all the algorithms of what should work so that you can implement the commands again using the fixed versions of the automation problems in future versions of Visual Studio .NET.
The SettingsMaster Add-In After all the fun I had on SuperSaver, I really wasn't looking forward to the next add-in I had to write for this chapter. In the end, SettingsMaster was not only problem-free but one of the most useful tools I've ever written. I certainly hope you find it useful as well. As the name implies, SettingsMaster's purpose in life is to get all your settings straight. By settings, I mean all your build settings. Many of the bugs I've worked on over the years have come down to build problems, so I wanted some way, once and for all, to ensure the proper settings were actually in a project. Additionally, Visual Studio .NET is pretty poor when it comes to team development; the only way to set the build settings for multiple projects worked on by multiple developers is manually. This is a huge and very troubling hole in Visual Studio .NET. I wanted to solve these two build settings problems for both .NET and native C++ projects. SettingsMaster adds two commands to the IDE. The first, SettingsMaster. CorrectCurrentSolution, uses the default configuration file (more on this file later) to automatically apply the settings you want. The second command, SettingsMaster.
CustomProjectUpdate, prompts you with the Open File dialog box to select a configuration file for updating the project currently selected in Solution Explorer. The idea is that you'll put all your common team settings in the common files, and whenever anyone creates a new project, he or she can click the SettingsMaster.CorrectCurrentSolution button and immediately start the project with the correct team settings. The SettingsMaster. CustomProjectUpdate command is for custom updating a project as you're prompted for the input file that contains the changes you want to apply. For example, if you decide to have a new define value in your C# projects, you can easily add that define value to them all. The SettingsMaster property page in the Options dialog box, shown earlier (in Figure 9-5), allows you to set the default language files for each supported language. The initial version I provide supports C#, Visual Basic .NET, and native C++. It's an excellent exercise for the read to add J# to the mix. You can also choose to have the SettingsMaster commands automatically save the projects it updates after it fixes the build settings. The configuration files that contain the settings are relatively simple XML files for ease of parsing and to be buzzword-compliant. Because the nature of the project systems for the .NET languages and native C++ is so different, each has different schemas. The basic idea is that the configuration files are language-specific and define the individual settings for each project configuration for that language. For .NET projects, the basic schema is as follows. Table 9-2 lists the individual fields and what they mean. <Configurations> <ProgLanguage></ProgLanguage> <Configuration> <ConfigName></ConfigName> <Properties> <Property> <PropertyName></PropertyName> <PropertyType></PropertyType> <PropertyValue></PropertyValue> </Property> </Properties> </Configuration> </Configurations>
Table 9-2: .NET Project Configuration Schema Node <Configurations> <ProgLanguage> Description The main element that contains one or more configurations. Contains the string that describes the GUID string for the programming language supported by this file. Example: <ProgLanguage> 396
Table 9-2: .NET Project Configuration Schema Node Description {B5E9BD34-6D3E-4B5D-925E-8A43B79820B4} </ProgLanguage> <Configuration> <ConfigName> The collection of properties for a single build configuration. The name of the configuration. Corresponds to a target configuration in the Visual Studio .NET IDE configuration manager. Example: <ConfigName>Debug</ConfigName> <Properties> <Property> <PropertyName> The collection of properties for this configuration. The description of an individual property. The name of a Project object property. This property must exist in the specific language's Project automation object. Example: <PropertyName>CheckForOverflowUnderflow</PropertyName> <PropertyType> Indicates the type for the property name. This can be only Boolean, String, or Enum. If the type is String, you must include an attribute type OpType, either Overwrite or Append, which determines how the string value will be changed. If the type is Enum, you must include an attribute type Name, which is the name of the enumerated type utilized by the specific Project property. Example: <PropertyType>Boolean</PropertyType> Example: <PropertyType Name="prjWarningLevel"> Enum</PropertyType> <PropertyValue> The value you want the property to have. For Boolean types, this is either 1 or 0. For String types, it is the string you want either appended or overwritten. For Enum types, it is the numeric value of the enumeration. Example: <PropertyValue>1</PropertyValue> Probably the easiest way to illustrate what a .NET configuration looks like is to show two stripped-down examples. Listing 9-5 shows the minimal configuration file necessary to turn on incremental building in a debug build and turn it off for a release build of a Visual Basic .NET project. Listing 9-6 shows how to set the warning level to prjWarningLevel4 in a C# release build project only. Listing 9-5: Visual Basic .NET SettingsMaster project for turning on incremental linking in a debug build and off in a release build <Configurations> 397
<ProgLanguage>{B5E9BD33-6D3E-4B5D-925E8A43B79820B4}</ProgLanguage> <Configuration> <ConfigName>Debug</ConfigName> <Properties> <Property> <!--Turn on (/incremental+)--> <PropertyName>IncrementalBuild</PropertyName> <PropertyType>Boolean</PropertyType> <PropertyValue>1</PropertyValue> </Property> </Properties> </Configuration> <Configuration> <ConfigName>Release</ConfigName> <Properties> <Property> <!--Turn off (/incremental-)--> <PropertyName>IncrementalBuild</PropertyName> <PropertyType>Boolean</PropertyType> <PropertyValue>0</PropertyValue> </Property> </Properties> </Configuration> </Configurations>
Listing 9-6: C# SettingsMaster project for turning on warning level 4 in a release build <Configurations> <ProgLanguage>{B5E9BD34-6D3E-4B5D-925E8A43B79820B4}</ProgLanguage> <Configuration> <ConfigName>Release</ConfigName> <Properties> <Property> <!--Turn on to level 4--> <PropertyName>WarningLevel</PropertyName> <PropertyType Name="prjWarningLevel">Enum</PropertyType> <PropertyValue>4</PropertyValue> 398
Copyright © . All rights reserved.