barcode code 39 c# format in Visual C#.NET

Creator USS Code 39 in Visual C#.NET format

Make Code 39 Full ASCII In Visual C#.NET
Using Barcode maker for VS .NET Control to generate, create USS Code 39 image in .NET applications.
Scan Code 39 Full ASCII In C#.NET
Using Barcode reader for .NET Control to read, scan read, scan image in VS .NET applications.
With the PROCESS_INFORMATION structure, I'm interested in looking at the hProcess and hThread values, so my autoexpand rule would be _PROCESS_INFORMATION =hProcess=<hProcess,X> hThread=<hThread,X>. I use the ,X format specifiers because I always want to see the values as hexadecimal values. Figure 7-4 shows the autoexpand rule for _PROCESS_INFORMATION showing up in a data tip in the source window.
Printing Barcode In Visual C#
Using Barcode creation for Visual Studio .NET Control to generate, create bar code image in Visual Studio .NET applications.
Decoding Barcode In Visual C#.NET
Using Barcode scanner for .NET Control to read, scan read, scan image in VS .NET applications.
Figure 7-4: Autoexpand in a data tip When entering my new autoexpand rule, I must place it after the section of the AUTOEXP.DAT file delineated by [AutoExpand]. Your best bet is to place your values right after [AutoExpand] so that you can find them easily and not mess up the techniques I'll discuss in the next section. The good news is that unlike the managed debugging autoexpands that are read only when you start Visual Studio .NET, the AUTOEXP.DAT file is read in each time you debug, so developing native autoexpand rules is much easier. One special formatting code you'll see in the file is <,t>. This code tells the debugger to put in the type name of the most derived type. For example, if you have a base class A with a derived class B and only A has an autoexpand rule, the autoexpand for a variable of type B will be the class name B followed by the autoexpand rule for class A. The <,t> format is very helpful for keeping your classes straight.
Code 39 Full ASCII Printer In VS .NET
Using Barcode generation for ASP.NET Control to generate, create Code-39 image in ASP.NET applications.
USS Code 39 Maker In VS .NET
Using Barcode generation for .NET framework Control to generate, create Code 39 image in .NET applications.
Adding Your Own HRESULT Values In addition to expanding your types, the Visual Studio .NET Watch window now has provisions to show your custom HRESULT values as text instead of as some hard-todecipher number. The magical AUTOEXP.DAT also holds these values. At the end of the AUTOEXP.DAT file, add a new section named [hresult] and add each custom HRESULT using the following pattern: "<unsigned decimal value>=<HRESULT text>." The code that follows is an example that includes some of the values not handled automatically by the debugger. If you'd like to see the actual HRESULT value for one of the built-in conversions or one you've added to the [hresult] section, take the HRESULT variable and append ,u or ,x to the variable. That will force the variable to be displayed as an unsigned integer or a hexadecimal value, respectively. [hresult] 2147500051=CO_E_CANT_REMOTE 2147500056=CO_E_CREATEPROCESS_FAILURE 2147500059=CO_E_LAUNCH_PERMSSION_DENIED Adding Super Customized Display to the Watch Window A major enhancement to the Watch window that's shown up for native debugging is the Expression Evaluator Add-In (EEAddIn). What EEAddIn allows you to do is to have the debugger call one of your DLLs when the Watch window is evaluating a specific type. This gives you an excellent opportunity to provide calculations that will display data in a more relevant way. For example, the Watch window will display a SYSTEMTIME structure (which represents the Win32 date and time) as a bunch of hexadecimal numbers, making it impossible for you to determine the time. When you use an EEAddIn, the Watch window displays a readable string such as {5/13/2002 12:51 AM} instead. To tell the Watch window you have an EEAddIn DLL you'd like to load, you place an entry for each type you want to evaluate in the ubiquitous AUTOEXP.DAT file. Under the [AutoExpand] section, you'll indicate the expansion for a type using the following syntax: type name=$ADDIN(dll name,exported function) The type name is, as with the autoexpand rules, the name for the type the Watch window displays in the Type column for the variable. The DLL name is the name of the DLL. The documentation for the EEAddIn, which is just sample Visual Studio .NET project named, appropriately, EEAddIn, indicates that the DLL name just needs to be the name of the DLL because you're supposed to put your EEAddIns in the same directory as AUTOEXP.DAT. However, I've found that you should indicate the complete path to the DLL as part of the DLL name to ensure proper loading. The exported function is the function you want called to process your custom display for the given type. Since your EEAddIns run in the address space of the debugger, you need to ensure that you properly handle any possible exceptions because you'll crash the debugger if you don't. The individual exported functions must match the CUSTOMVIEWER prototype, as shown in Listing 7-1. When your function is called, it will receive as parameters the address of the type; a pointer to a helper structure, DEBUGHELPER; the numeric base currently selected (decimal or hexadecimal); a Boolean value indicating whether the debugger is expecting UNICODE strings (which in Visual Studio .NET is ignored as it always expects ANSI characters returned); the string buffer to write the result to; and the maximum length of the string buffer. The helper structure, also shown in Listing 7-1, has a few pointers to functions you can call to get information about the values at the address for the type. The most important are GetRealAddress and ReadDebuggeeMemoryEx. You'll pass the address 251
Code-39 Maker In Visual Basic .NET
Using Barcode drawer for VS .NET Control to generate, create Code 39 Full ASCII image in .NET framework applications.
Bar Code Generation In Visual C#.NET
Using Barcode maker for .NET framework Control to generate, create bar code image in Visual Studio .NET applications.
handed to your exported function, stored in GetRealAddress, to get the real address for the variable. You'll pass that value on to ReadDebuggeeMemoryEx in order to get the bytes for the type. The beauty of the helper class is that it completely hides the magic necessary to get the data out of local and remote debuggee processes.
Bar Code Generation In Visual C#.NET
Using Barcode drawer for .NET Control to generate, create bar code image in .NET applications.
Printing PDF417 In Visual C#
Using Barcode generation for VS .NET Control to generate, create PDF-417 2d barcode image in .NET framework applications.
Listing 7-1: EEAddIn export prototype and helper structure /*-------------------------------------------------------------------The only definition of the Expression Evaluator AddIns Lifted from The EEAddIn Sample Project --------------------------------------------------------------------*/ typedef struct tagDEBUGHELPER { DWORD dwVersion ; BOOL (WINAPI *ReadDebuggeeMemory)( struct tagDEBUGHELPER * pThis , DWORD dwAddr , DWORD nWant , VOID * pWhere , DWORD * ); // from here only when dwVersion >= 0x20000 DWORDLONG (WINAPI *GetRealAddress)( struct tagDEBUGHELPER *pThis ) ; BOOL (WINAPI *ReadDebuggeeMemoryEx)( struct tagDEBUGHELPER *pThis , DWORDLONG qwAddr , DWORD nWant , VOID* pWhere , DWORD * nGot ; 252 ); int (WINAPI *GetProcessorType)( struct tagDEBUGHELPER *pThis ) nGot
Printing Code 39 Full ASCII In Visual C#
Using Barcode maker for Visual Studio .NET Control to generate, create Code39 image in .NET framework applications.
Generating ISBN In Visual C#
Using Barcode creator for .NET framework Control to generate, create International Standard Book Number image in .NET applications.
} DEBUGHELPER ; // The prototype each of your functions must be. typedef HRESULT (WINAPI *CUSTOMVIEWER)( DWORD , DEBUGHELPER * , int , BOOL , char * , size_t , DWORD ) ; reserved max pResult bUniStrings nBase dwAddress pHelper
Code128 Creator In Objective-C
Using Barcode creation for iPad Control to generate, create Code 128 Code Set B image in iPad applications.
Printing Quick Response Code In Java
Using Barcode creation for Java Control to generate, create QR Code image in Java applications.
Your exported function's job is to convert those bytes read in from the debuggee into something displayable in the Watch window. Since you can easily read the memory out of the debuggee, you'll be working with a copy of the information. When I first got a glimpse of the EEAddIn architecture, I immediately thought of a million cool displays I would love to have. The first was one that would take an HINSTANCE or HMODULE and show the value followed by the name of the DLL at that location. Of course, reality then intruded. Converting an HINSTANCE or HMODULE into a DLL name required a handle to the process. The DEBUGHELPER structure in Listing 7-1 gives you a way to read memory but not get the process handle. Of course, that's when I realized that if my EEAddIn function was working on a process being debugged remotely, even having the process handle wouldn't help because I couldn't do anything with that handle on the machine the debugger was running on. Maybe a future version of Visual Studio .NET will offer a means of querying information from the process that needs handle values. Even with the restriction that you can read only the debuggee's memory, plenty of excellent opportunities to put better displays in the Watch window are still open to you so that you can debug faster. Included with this book's sample code is my current EEAddIn, BSU_ExpEval_AddIn. At the time I wrote this paragraph, I incorporated the _SYSTEMTIME and _FILETIME displays from the sample provided by Visual Studio, but I put error handling around them as well as the following structure expansions: _OSVERSIONINFOA, _OSVERSIONINFOW, _OSVERSIONINFOEXA, and _OSVERSIONINFOEXW. Now when you have one of the structures handled by GetVersionEx, you can see them displayed as shown in Figure 7-5, which shows some of the output of the test program for BSU_ExpEval_AddIn. Listing 7-2 shows the work necessary to expand the _OSVERSIONINFOA structure.
DataMatrix Recognizer In .NET Framework
Using Barcode recognizer for .NET framework Control to read, scan read, scan image in VS .NET applications.
Make QR Code 2d Barcode In Objective-C
Using Barcode generation for iPad Control to generate, create QR Code 2d barcode image in iPad applications.
Figure 7-5: EEAddIns at work One debugging tip with EEAddIn DLLs is that if you return E_FAIL from your function, the Watch window will display " ", so you might want to return S_OK and set the result text to " " so that your output matches the normal Watch window display. This can help you debug the DLL as well. Another tip is to consider putting failure results in the result text of your debug versions to make your debugger extensions easier to debug. Finally, if enough of us start sharing our EEAddIns, we can get much better debugging information than ever before from the IDE. I'd encourage you to look at any structures or classes you can from Win32, MFC, and ATL and see whether you can provide better output.
Generate Bar Code In None
Using Barcode maker for Font Control to generate, create bar code image in Font applications.
Code 128B Generation In Java
Using Barcode maker for BIRT Control to generate, create Code 128 Code Set B image in BIRT reports applications.
Listing 7-2: EEAddIn example for _OSVERSIONINFOA // This touches only the first 5 DWORDS in the structs, so you can pass // both the ANSI and UNICODE versions in. static int ConvertBaseOSV ( LPOSVERSIONINFOA pOSVA , char * szStr ) { int iCurrPos = 0 ; if ( ( pOSVA->dwMajorVersion == 4 ) && ( pOSVA->dwMinorVersion ==0)) { if ( pOSVA->dwPlatformId == VER_PLATFORM_WIN32_NT ) { iCurrPos = wsprintf ( szStr , _T ( "Windows NT 4.0 " ) ) ; } else { iCurrPos = wsprintf ( szStr , _T ( "Windows 95 " ) ) ; } } else if ( ( pOSVA->dwMajorVersion == 4 { iCurrPos = wsprintf ( szStr , _T ( "Windows 98 " ) ) ; } else if ( ( pOSVA->dwMajorVersion == 4 ) && ) ( pOSVA->dwMinorVersion == 90 ) 254 ) && ) ( pOSVA->dwMinorVersion == 10 )
EAN / UCC - 13 Printer In None
Using Barcode maker for Word Control to generate, create EAN / UCC - 13 image in Office Word applications.
Code 39 Extended Encoder In None
Using Barcode printer for Font Control to generate, create Code 3 of 9 image in Font applications.
{ iCurrPos = wsprintf ( szStr , _T ( "Windows Me " ) ) ; } else if ( ( pOSVA->dwMajorVersion == 5 ( pOSVA->dwMinorVersion == 0 { iCurrPos = wsprintf ( szStr , _T ( "Windows 2000 " ) ) ; } else if ( ( pOSVA->dwMajorVersion == 5 ( pOSVA->dwMinorVersion == 1 { iCurrPos = wsprintf ( szStr , _T ( "Windows XP " ) ) ; } else if ( ( pOSVA->dwMajorVersion == 5 ( pOSVA->dwMinorVersion == 2 { iCurrPos = wsprintf ( szStr , _T ( "Windows Server 2003 " ) ) ; } else { // Beats me! iCurrPos = 0 ; } return ( iCurrPos ) ; } // Again, this function uses the shared field between the A and W // versions, so you can use it for both. static int ConvertBuildNumber ( LPOSVERSIONINFOA pOSVA , char * szStr ) { int iCurrPos = 0 ; if ( VER_PLATFORM_WIN32_NT == pOSVA->dwPlatformId ) { iCurrPos = wsprintf ( szStr _T ( "(%d) " ) pOSVA->dwBuildNumber } else if ( VER_PLATFORM_WIN32_WINDOWS == pOSVA->dwPlatformId ) { 255 , , ) ; ) && ) ) ) && ) ) ) && ) )
WORD wBuild = LOWORD ( pOSVA->dwBuildNumber ) ; iCurrPos = wsprintf ( szStr , _T ( "(%d) " ) , wBuild ) ; } return ( iCurrPos ) ; } ADDIN_API HRESULT WINAPI AddIn_OSVERSIONINFOA ( DWORD int BOOL char * size_t DWORD { if ( pHelper->dwVersion < 0x20000 ) { // I'm not touching less than VS.NET. return ( E_FAIL ) ; } HRESULT hRet = E_FAIL ; __try { DWORDLONG pHelper ); DWORD nGot dwRealAddr = 0 ; = pHelper->GetRealAddress ( /*dwAddress*/ /*nBase*/ pResult /*max*/ /*reserved*/ , , , , , ) DEBUGHELPER* pHelper
/*bUniStrings*/ ,
// Try and read in the structure. if ( S_OK == pHelper-> ReadDebuggeeMemoryEx , dwRealAddr , sizeof ) , ( OSVERSIONINFOA ( pHelper
&stOSA , &nGot )) { // Make sure I got all of it. if ( nGot == sizeof ( OSVERSIONINFOA ) ) { // Do the dance... char * pCurr = pResult ; int iCurr = ConvertBaseOSV ( &stOSA , pCurr ) ; if ( 0 != iCurr ) { pCurr += iCurr ; iCurr = ConvertBuildNumber ( &stOSA , pCurr ) ; pCurr += iCurr ; if ( '\0' != stOSA.szCSDVersion[0] ) { wsprintf ( pCurr _T ( "%s" ) stOSA.szCSDVersion } } else { _tcscpy ( pResult , _T ( "..." ) ) ; } } hRet = S_OK ; } } __except ( EXCEPTION_EXECUTE_HANDLER ) { hRet = E_FAIL ; } return ( hRet ) ; } 257 , , ) ;
Common Debugging Question: Has the 255-character debug limit problem been fixed YES! In versions of Visual Studio prior to Visual Studio .NET, the native debugging information was limited to a maximum of 255 characters. This wasn't a problem in the C days, but the advent of templates completely blew past 255 characters for even the simplest types. Visual Studio .NET can have arbitrary length debug symbols, so you should see complete expansion. This also means that the old C4786 informational message (debug information greater than 255 characters), which stopped compiles when treating warnings as errors, has finally been buried once and for all! We've been blessed! Remote Debugging Remote debugging of native applications works almost as seamlessly as remote debugging of managed applications. Simply install the remote debugging components as described in 6, ensure your account is set up as a member of the remote machine's Administrators group as well as the Debugger Users group, and you can connect and debug all you want through the new DCOM transport layer. This is the perfect way to attach and detach from those long-running server processes. In addition to the DCOM transport layer, Visual Studio .NET 2003 offers two remote debugging options: Pipes and TCP/IP. The TCP/IP option has been around since Visual C++ 6, but it's not as secure as Pipes. Where TCP/IP remote debugging allows anyone to connect to the machine, the new Pipes allows you to specify exactly which user(s) you'll allow to connect and debug. Pipe debugging is now the default, though it is slower than TCP/IP. Although not as convenient as DCOM, the Pipes and TCP/IP debugging can be a great tool for certain debugging challenges. One particularly nice new feature is that you can start processes with Pipes and TCP/IP debugging. Additionally, you can set up your Visual Studio .NET solutions to always start the process for remote debugging. This is especially helpful for heavy client-side applications such as DirectX games. A much-needed new feature is the ability to allow multiple connections to the remote machine so that you can debug multiple processes if necessary. Another fine feature is that if you're going to be doing only native debugging, you don't have to go through the complete Remote Components Setup to install just the Pipes and TCP/IP debugging. To get Pipes and TCP/IP debugging set up, you can copy the necessary binaries from a machine that has Visual Studio .NET installed to a directory on the remote machine. Table 7-5 lists the binaries and where you can find them on the Visual Studio .NET machine. Also keep in mind that the Visual C++ 6 version of MSVCMON.EXE cannot be used with Visual Studio .NET.
Table 7-5: Pipe and TCP/IP Remote Debugging Components File MSVCR71.DLL MSVCI71.DLL MSVCP71.DLL MSVCMON.EXE Copy From Location %SYSTEMROOT%\SYSTEM32 %SYSTEMROOT%\SYSTEM32 %SYSTEMROOT%\SYSTEM32 <Visual Studio .NET Dir>\COMMON7\PACKAGES\DEBUGGER 258 Installation
Table 7-5: Pipe and TCP/IP Remote Debugging Components File NATDBGDM.DLL NATDBGTLNET.DLL Copy From Location <Visual Studio .NET Dir>\COMMON7\PACKAGES\DEBUGGER <Visual Studio .NET Dir>\COMMON7\PACKAGES\DEBUGGER Installation Installation
Before you start remote debugging, it's a very good idea to do a little planning to ensure that your remote debugging session will be successful. The Pipe and TCP/IP version of remote debugging with Visual Studio .NET is much less temperamental than the Visual C++ 6 version. The main trick is ensuring your symbols are locatable by Visual Studio .NET on the local machine, which is where the symbols are loaded. For the operating system symbols, your best bet is to have your symbol server set up as I described in 2. If you're working on a local build of your product, it's best to have the program you're going to debug installed in the same directories on both the remote and local machines. That way there's no confusion as to where things are supposed to be. Finally, it's an excellent idea to ensure that you can start your program on the remote machine, because nothing's worse than finding out that a DLL is missing right as you start remote debugging. To start remote debugging with Pipe connections, you'll need to log in on the remote machine and run MSVCMON.EXE. By default, starting MSVCMON.EXE means that you must connect to the remote machine from the machine you're running the Visual Studio .NET IDE on, using the same account that you logged on to on the remote machine. If you're willing to open the remote machine up a little more, you can start MSVCMON.EXE with the -u <domain\group or user> command-line switch to specify which users and groups you're willing to let to the machine to start and debug processes. Setting up the machine the Visual Studio .NET IDE runs on is pretty simple. It just entails setting a few items in the Debugging property page of the project properties. In the Action section, the Command and Working Directory fields must be filled out with the locations on the remote machine. Optionally, you can specify that you want to attach to the remote process by setting Attach to Yes. The last thing you might want to set in the Action section is the Symbol Path field if you don't have the binaries installed in the same places on both machines. In the Remote Settings section, set Connection to Remote Via Pipe (Native Only). In the Remote Machine field, enter the name or the IP address of the machine hosting MSVCMON.EXE. You can also try to enter the machine name, but the IP address will always work. It's a good idea to test the connection to the remote machine by using PING.EXE to determine whether you can reach it. If you can reach it through the name of the remote machine, you can use that name, but the IP address will always work. Finally, the Remote Command field must contain the same complete path and name as specified in the Command field in the Action section. Figure 7-6 shows an example project with all the fields filled out.
Figure 7-6: A project set up for Pipe debugging Once the fields are filled in, you know fairly quickly whether you made a good connection. The console window in which MSVCMON.EXE is running will show you the name of the user making the connection, and you'll start debugging as you normally would. If there's a problem, you'll know what you need to do to fix it because Visual Studio .NET error messages are much better than messages in prior releases. If you're debugging into a machine running terminal server and multiple users could be doing remote Pipes debugging, the MSVCMON.EXE s <suffix> switch allows you to specify a unique suffix onto the named pipe. Since the first user that starts doing remote Pipe debugging gets the default pipe name, subsequent users debugging into the same machine will need to uniquely identify the instance of MSVCMON.EXE they want to connect to. Once you've started MSVCMON.EXE with the s option, you'll specify the suffix in the Remote Machine field of the Debugging property page of the project properties dialog, by appending the suffix to the machine name separated with an octothorpe (#). For example, if you run MSVCMON s pam on the machine ZENO, you'd specify the machine name as ZENO#pam. As I mentioned earlier, Pipe debugging is slower than TCP/IP debugging, though more secure. If you need the speed, you can turn on TCP/IP debugging with -tcpip command line switch. To tell the solution that you want to use TCP/IP, in the Debugging property page of the project properties dialog, you'll select Remote Via TCP/IP (Native Only) from the Remote Settings Section. There are a few TCP/IP specific command-line options to MSVCMON.EXE you might be interested in. The first is anyuser, which allows you to let anyone connect to the machine with no security. The second is maxsessions, which specifies the maximum number of debugging sessions you'll allow at any time. The third option is timeout, which you can use to tell MSVCMON.EXE how long you're willing to let it wait for a connection before timing out. 260
Tips and Tricks In this section I want to cover a few tips and tricks necessary to make the most of your native debugging. Debugging Injected Code One of the interesting new features in Visual C++ .NET is the new attributed programming model. This new model can make COM development much easier because it allows you to combine IDL attributes with your source file so that you must have only a single file to make a COM object. If you'd like to see a real example of attributed COM programming, check out the Tester object from 16. Additionally, attributed programming offers a consistent way to provide unified message handling for your applications. All the attributes work by injecting source code into your source file. You've got several ways of debugging this injected code. When in the debugger, to see the source code, move to the Disassembly window and right-click to select Show Source Code from the context menu. However, an easier way to see what's happening with the injected source code is to compile with the /Fx switch for CL.EXE. You can turn this switch on within the Visual Studio environment by opening the Property Pages dialog box, expanding the C/C++ folder, selecting the Output Files property page, and setting Expand Attributed Source to Yes. This will create a file named sourcename.MRG.CPP in the same directory as the CPP file. You can open the file and look at the injected (merged) source code. If you'd like, you can also compile the merged file so that you can see how everything works in the source window when debugging. The Memory Window and Auto Memory Evaluation One huge improvement in native debugging is the Memory windows. For one thing, there's more than one, but more importantly, the Memory window no longer has the weird built-in Artificial Intelligence that made it monitor your eye movements to determine which address you were looking at and then move that address the next time you looked at the Memory window. It's also gained all sorts of additional memory display formats, so you should not have any issues seeing memory how you'd like it. Right-click in the Memory window to choose the display format. The new ability to evaluate Unicode text was a long time in coming. Finally, the Memory window makes it much easier to automatically reevaluate changes to the memory block you're watching. In the Memory window, click the button to the right of the address field, and the debugger will keep the Memory window updated to the latest values. This functionality is especially valuable when you enter ESP (the stack pointer) so that you can monitor the stack as it changes. Later in the chapter, I'll discuss watching the stack. Exception Monitoring One of the biggest performance drains in native applications is the unnecessary exception. Since a native exception involves a trip to kernel mode every time it's triggered, you want to avoid it at all costs. Although the transition from user mode to kernel mode is relatively quick, all the extra work that occurs to process the exception in kernel mode eats tons of time. To help narrow down those performance bottlenecks, the Visual Studio .NET debugger's Exception dialog box allows you to control exactly how the debugger will process any exceptions. By properly understanding how to use this dialog box, you can more quickly track down your unnecessary exceptions. 261
Before I jump into discussing the Exception dialog box, I need to clarify what happens when an exception is encountered by a native code debugger. The instant an exception occurs, the operating system suspends the process (which means all threads stop), points at the spot where the exception occurred, and notifies the debugger that an exception occurred. This is called the first chance exception because it's the first time the debugger has an opportunity to handle it. The debugger has two choices: it can handle the exception so that the debuggee never sees the exception, or it can pass the exception on to the debuggee. The idea that the debugger can handle, or eat, the exception might strike you as odd. However, as you saw in 4, setting a breakpoint in native code entails setting the instruction at the location to INT 3, the breakpoint opcode. In the breakpoint case, the debugger is causing the exception in the debuggee, so the debugger must handle those exceptions. If the exception wasn't caused by the debugger, the debugger tells the operating system that it doesn't want to handle the exception, and the exception is passed back to the debuggee. The debugger also emits a message to the Output window indicating that a first chance exception occurred. The debuggee restarts and, if the debuggee has exception handling set up, the exception is processed and the debuggee continues on its merry way. If the debuggee doesn't have exception handling set up, the exception will propagate up to the final exception handlers inside NTDLL.DLL. At that point, the operating system will suspend the debuggee again and tell the debugger the second chance exception occurred for the exception. This means that the process is going to die from an unhandled exception. The important issue to note about native exception handling is that when you see the first chance exception message in the Output window, an exception has occurred in your process. As I pointed out, exceptions are a performance bottleneck, so if you're seeing lots of "First-chance exception at " messages when you run your process, you have performance issues. The insidious problem here is that C++ exception handling is implemented with structured exception handling (SEH) behind the scenes, so using C++ exceptions can kill your performance. Exceptions are for exceptional conditions. Avoid C++ exceptions in native applications for general development. To track down performance problems related to exceptions, you can always look for exceptions in code reviews. However, that can sometimes be a daunting task on a large code base. The Exceptions dialog box in Visual Studio .NET can make stopping immediately where exceptions occur and finding where they are handled a complete piece of cake. Figure 7-7 shows the Exceptions dialog box that's accessible from the Debug menu or by pressing Ctrl+Alt+E with the default keyboard mappings.
Figure 7-7: Exceptions dialog box The Exceptions dialog box is a little confusing in that the native exceptions are split between two top level nodes, C++ Exceptions and Win32 Exceptions. The default settings are that the debugger will stop only on Control-C (0x40010005) and Control-Break (0x40010008) exceptions when debugging console applications. To tell the debugger to stop whenever any particular exception occurs, select the exception in the tree and, in the When The Exception Is Thrown (that is, on the first chance exception) group, select Break Into The Debugger. The glyph on the selected item will change to a large red ball with an X in it. In the dialog box, smaller grey balls denote exceptions that inherit their settings from the parent. A larger gray ball indicates the option is to continue on first chance exceptions. Finally, a small red ball says the parent node breaks on first chance exceptions and the child node inherits from the parent. The exception settings are stored on a per-solution basis. What I like to do is set the Win32 Exceptions and C++ Exceptions nodes to Break Into The Debugger for both the When The Exception Is Thrown and If The Exception Is Not Handled options. That way, whenever any native exception of any kind occurs, the process will stop and allow me to determine whether the exception is legitimate. When you have either all exceptions or a single exception type set to stop, you'll see the dialog box in Figure 7-8, which shows a first chance C++ exception. If you click the Break button, you'll be dropped to the first function on the stack that has source code, which is generally directly in your code where the exception occurred. If you click Step Over or Step Into at this point, the debugger will prompt you with a message box asking whether you want to pass the exception on to the debuggee. Click Yes, and you'll immediately stop in the exception handler for the exception. This is fantastic for determining who's handling your exceptions. There are plenty of bugs for which the wrong exception handlers are handling exceptions. 263
Figure 7-8: First chance exception dialog box Clicking Continue in the first chance exception dialog box will pass the exception on to the debuggee and continue execution. The Ignore button is a little different and depends on the type of exception and whether the exception is listed in the Exceptions dialog box. If the exception is a hard exception generated by the CPU (such as an access violation), clicking the Ignore button will attempt to reexecute the offending instruction, which will pop up the first chance exception dialog box all over again. If the exception is generated by calling RaiseException (such as "0xC0000008, Invalid HANDLE was specified"), execution will continue as if the exception never occurred. Since C++ exceptions are generated through a call to RaiseException, your execution will perform as though the throw never occurred. More Symbol Handling Tips As I described in 2, Visual Studio .NET advanced symbol handling with the new symbol server and symbol store technology is absolutely out of this world. For native debugging, you can also set additional symbol paths inside the project, so you can have per-project symbol locations outside your normal symbol server. In the project Property Pages dialog box\Configurations Properties\Debugging property page is a Symbol Path field. Here you can enter the specific symbol path for the project. The good news is that this is appended to any settings you have in the _NT_SYMBOL_PATH environment variable and thus doesn't overwrite them. Detaching from Windows 2000 Processes As you should know by now, you can detach from processes when you're debugging under Windows XP as well as Windows Server 2003. However, if you're still supporting Windows 2000, you're stuck once you start debugging, you're debugging that process for life, which is especially irksome when debugging production server applications. Fortunately, Microsoft realized that not everyone was going to upgrade to the latest and greatest operating systems all at once and came up with a good solution for detaching from Windows 2000 processes. Installed as part of Visual Studio .NET and the Remote Components Setup is a Win32 service named DBGPROXY, which stands for debugger proxy. This proxy service will run as the debugger on Windows 2000. This means you can easily attach and detach all you want on Windows 2000! In fact, once you execute DBGPROXY with the command NET START DBGPROXY, you don't need to do anything else. Visual Studio .NET automatically performs the magic for you, so you're simply debugging, and the detach options are available. Of course, if DBGPROXY does stop for some reason, all processes it was debugging are terminated. I'd highly recommend setting the DBGPROXY service to automatic start-up so that you can start benefiting from it!
Handling Dump Files Back in 3 I discussed the SUPERASSERT dialog box and its Create Mini Dump button, which allows you to snap to disk the current state of the process so that you can load it up in the debugger later. Visual Studio .NET makes it easy to read any dump files you create. Opening a dump file in Visual Studio .NET is as simple as opening a regular solution. After starting Visual Studio .NET, select Open Solution from the File menu. In the Open Solution dialog box, navigate to the directory where your dump file is stored. Dump files traditionally have the extension DMP, which is already in the Files Of Type combo box, so you can either choose it or enter *.DMP in the File Name edit control. Select your DMP file, and click the Open button. As always, Visual Studio .NET will create the ubiquitous solution file necessary to do anything in the environment. Press any of the debug keys Step Into, Step, or Debug to receive the prompt for saving the solution and to load the dump file. If you're working on the machine where the dump file was created and your binaries were compiled, Visual Studio .NET will automagically find the source and symbols to match up with the dump file. To get the operating symbols lined up, either set the _NT_SYMBOL_PATH to include the symbol store for your location, or after starting debugging, open the Modules window, right-click on the modules without symbols, and browse over to the correct symbols. To help the debugger specify where to find the modules, you've got two ways of telling the debugger where to look. The easiest way is to specify the module directories in the MODPATH environment variable. Simply add each directory separated by semicolons to the MODPATH environment variable as you would for the PATH environment variable. If you'd like to set the module look up path globally, you can specify them to the SZ_REG value GlobalModPath in either of the following two registry keys. Use the HKEY_LOCAL_MACHINE if you want the path available to all users on the machine. HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\7.1\NativeDE\Dump s\ HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.1\NativeDE\Dum ps\ Unfortunately, the Visual Studio .NET debugger won't read binaries out of your symbol server directly, where WinDBG can. Therefore, if you're working with dump files from customer sites, you're probably better off using WinDBG, discussed in the next chapter, to handle those dump files. In addition to opening dump files, Visual Studio .NET can also create them. At first that might not sound very exciting, but from a Bugslayer debugging standpoint, we now have another great technique for solving problems. By creating dump files at various stages during a hard-core debugging session, you instantly have a post mortem trail you can follow that leads up to the problem. This opens up excellent opportunities to grab program states to show others on the team as well as to verify behavior over time. I end up doing so many snapshots during hard debugging sessions that I can quickly fill up whole hard disks. However, hard disks are dirt cheap compared to the costs that can be incurred as a result of not fixing a bug. Writing dump files is easy when you're in the middle of a debugging session. Select the Save Dump As menu option from the bottom of the Debug menu. That brings up a File Save dialog box in which you can specify exactly where the dump file should be created. Visual Studio .NET lets you write two types of dump files. Visual Studio .NET calls the first type simply minidump. This file contains the operating system version information, the stack 265
walk of all threads, and the file version information of each module loaded in the process. The regular minidump files are quite small. I have a debugger attached to Word 2002 as I write this chapter and the minidump file is only 38 K when the working set is over 16 MB. The second type of dump file you can write is a minidump with heap. This file writes out the same information as a minidump along with all allocated memory in the process. With this dump file format you can now follow pointer values through the entire address space. Of course, that extra information comes at a much bigger cost. The minidump with heap for Microsoft Word 2002 with this entire chapter loaded is 96 MB! In general, I mostly stick with the straight minidump version because I've rarely needed to wind through multiple pointer layers. However, it's nice to know it's there. Unfortunately, Visual Studio .NET doesn't offer an option to write out the most useful dump format, the minidump with handles. The argument could be made that since Visual Studio .NET does not offer a means for viewing handle information, there's no need to write that information out. However, WinDBG does allow you to view handle information, as you'll see in the next chapter. Having the handle information is the difference between tracking down a multithreaded deadlock and not solving it. Since the handle information is so important, the SUPERASSERT dialog box does write it out. Finally, make sure you read about WinDBG's dump file handling. Although WinDBG is harder to use, the debugger is better suited to reading the dump files you get from customers primarily because it can load the binaries out of your symbol store. With its better symbol handling and extra informational commands, you can determine the cause of those customer problems more easily. Common Debugging Question: How do I set breakpoints in DLLs that aren't loaded yet One big problem with Visual Studio 6 was that trying to get breakpoints set in a DLL that was dynamically loaded in the Additional DLL dialog box was, to say it charitably, a disaster. Remote debugging was especially problematic. However, you might not have even noticed the change with Visual Studio .NET because Microsoft fixed the breakpoints so that they automatically rearm themselves when the module containing the source file comes into the address space. In the Breakpoint window, disarmed breakpoints have a white question mark in the red dot. The Additional DLL dialog box is gone, and good riddance! x86 Assembly Language In many cases, when your native application crashes, the real difference between solving the bug and screaming in frustration comes down to how well you can read a little assembly language. Although we'd all prefer our crashes to occur in a module with source code and a complete call stack, many crashes just don't happen that way. When you do crash, you're generally left looking at the Disassembly window in the Visual Studio .NET debugger and wondering how you're going to figure out where you are in the program, let alone why you crashed. By no means am I saying that you need to know assembly language well enough to write all your programs using Microsoft Macro Assembler (MASM). The key is to learn enough assembly language to be comfortable reading it. My goal for this section is to present the information you need to have a working knowledge of assembly language. By the time you finish reading this section and practice for a couple of hours, you'll know more than enough assembly language to get by. This small investment of time can be the difference between flailing around in the debugger practicing your primal scream therapy and fixing your bugs. 266
For those of you who have done assembly language programming in the past, keep in mind that everything I'm going to discuss here is in relation to what you'll see in the Disassembly window. You might remember more concise ways of doing some of these operations, but the important issue is getting familiar with how Visual Studio .NET displays assembly language. Developers are sometimes wary of learning assembly language because they think some sort of black magic is involved. There's really nothing mysterious about assembly language, though; a single assembly language instruction does one thing and one thing only. Once you see the pattern and understand how the CPU carries out instructions, you'll realize that assembly language is actually quite elegant. If you want to look at black magic, take a look at any program that uses STL heavily. Those magical STL inline expansions can result in a call to 30 or 40 different functions and make an incredible number of assumptions. To me, STL is sometimes far more mystifying than assembly language. After introducing you to assembly language, I'll turn back to the Visual Studio .NET debugger and show you how to survive in the Disassembly window. For example, I'll show you how to look up parameters on the stack and navigate within the Disassembly window. I'll also explain the relationship between the Memory window and the Disassembly window as well as supply you with tips and tricks that will help you debug at the assembly-language level. Before we jump into assembly language, I need to issue one warning. Some of you are really going to get into assembly language. That's great, but it can lead to a problem for your career. Your bosses have already spoken with me and have asked that you not start jumping into assembly language every chance you get. It's not portable and can make maintenance much harder. The Basics of the CPU The Intel instruction set has been around for quite a while and has its roots in the 8086 CPU that Intel first released in 1978. In the days of MS-DOS and 16-bit Microsoft Windows, assembly language used to be a little quirky and hard to use because of the way the CPU handled memory, which was through 64 KB blocks of memory called segments. Fortunately, today on Windows operating systems, the CPU has direct access to the entire address space, which means that assembly language is much easier to deal with. The assembly language that I'll be introducing here will be the basic 32-bit instruction set that is compatible across all x86 architecture CPUs from both Intel and AMD and is also referred to as IA32. The advanced features on the Intel Pentiums, such as MMX, aren't generally an issue because Windows uses relatively few such features. I won't get into the real grungy parts of assembly-language instruction formats such as the ModR/M and SIB bytes, which both indicate ways to access memory. For the purposes of this chapter, memory access is memory access. I also won't be covering floating-point instructions. Operations on the Intel CPU floating-point unit (FPU) are similar to normal instructions. The main differences are that the FPU has its own set of registers and the floating-point instructions use a register stack based architecture. If this chapter inspires you to learn more about the Intel family of CPUs and I hope it does you should download the threevolume Intel Architecture Software Developer's Manual Adobe PDF files from The most important manual is Volume 2, the Instruction Set Reference. Volumes 1 and 3 are for basic CPU architecture information and operating systems developers, respectively. For the price of a phone call, you can even get the actual Intel CPU reference manuals from Intel free. Although you don't really need the actual manuals, they sure do make you look smart when they're sitting on your bookshelf!
One key point to remember is that the x86 CPUs are very flexible and provide you with many ways to carry out similar operations. Fortunately for us, the Microsoft compilers do a good job of picking the fastest way to do an operation and reusing that construct wherever applicable, so recognizing what a section of code is doing is easier. In the following sections, I'll cover the most commonly used instructions you'll see in assembly language. If you're interested in all the assembly-language instructions, you can consult the Intel manuals. Registers The first topic I want to cover is the registers. Because every bit of data that your application handles passes through the registers at one time or another, knowing the purpose of each register can help you recognize code gone awry. x86 CPUs have eight general-purpose registers (EAX, EBX, ECX, EDX, ESI, EDI, ESP, and EBP), six segment registers (CS, DS, ES, SS, FS, and GS), an instruction pointer (EIP), and a flags register (EFLAGS). The CPU has other registers as well, such as the debug and machine control registers, but they're special-purpose registers and you won't encounter them in normal user-mode debugging. Figure 7-9 shows the layout of a general-purpose register. The thing to remember is that some of the registers allow mnemonics to access different portions of the complete 32-bit register. The complete breakdown of all generalpurpose registers is listed in Table 7-6. The only segment register of interest for this discussion is the FS register, which holds the thread information block (TIB) that describes the currently executing thread. The other segment registers are used, but the operating system configures them in such a way that they're transparent to normal operation. The instruction pointer holds the address of the currently executing instruction.
Figure 7-9: General.purpose register layout The flags register, EFLAGS, contains the status flags and the control flags. Various instructions set bits in EFLAGS to indicate the result of those instructions. For example, the ZF (Zero Flag) bit is set to 1 when the result of an instruction is 0. In 4, I described setting the CPU to single-step mode, which involved setting the TF (Trap Flag) in the EFLAGS register. Figure 7-10 shows the Registers window from the Visual Studio .NET debugger. The Registers window displays the EFLAGS register as EFL. Notice that I'm not showing floating-point registers or any of the other special registers such as MMX or 3DNow! in the Registers window. You can choose the registers you want to see by rightclicking in the Registers window and selecting the registers you're interested in from the context menu.
Table 7-6: General.Purpose Registers 32-Bit Register 16-Bit Acces s Low-Byte Access (Bits 0 7) HighByte Access (Bits 8 15) AH BH CH DH Loop instruction counters use this register for counting. The high 32 bits of 64-bit values are stored here. In memory move or compare instructions, the source address is stored here. In memory move or compare instructions, the destination address is stored here. The stack pointer. This register is changed implicitly when calling functions, returning from functions, making room on the stack for local variables, and cleaning up the stack. Base/frame pointer. This register holds the stack frame for a procedure. Special Uses
Copyright © . All rights reserved.