Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 43 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
43
Dung lượng
278,47 KB
Nội dung
The code for the SetEnvironmentVariable() function is surprisingly simple, which is why it’s a wonder that Microsoft didn’t add it to the .NET Framework. As you can see, the function requires a key/value pair input. The first string contains the key, while the second string contains the value. Figure 6.6 shows the output from this example. Figure 6.6: Setting environmental strings is easy— just supply a key and a value. Using Cursors and Fonts Many people think that they’re stuck with the cursor and font originally presented in the console window—others simply don’t think too much about the option of changing either item. However, it pays to know about both cursor and font manipulation for those times when you want to emphasize something on screen. The GetConsoleCursorInfo() and SetConsoleCursorInfo() functions help you with the console cursor. The GetConsoleFontSize() function enables you to work with the font. You can specify the index of the current font or ask for the dimensions of any font supported by the console window. This function can help you perform tasks like size the console window for a specific font. When you only want to know about the current font, you can use the GetCurrentConsoleFont() function. You also need to use the GetCurrentConsoleFont() function to obtain a font index number for the GetConsoleFontSize() function. The GetCurrentConsoleFont() function can retrieve the current console font or the font for the maximum window supported by the current window settings and the machine. If you feel that the console needs a bit of color, you can use the FillConsoleOutputAttribute() or the SetConsoleTextAttribute() functions. You’ll use the cursor−related functions relatively often because the console offers little in the way of console control otherwise. The example in Listing 6.1 shows you how to move the cursor around. The example in this section will show you how to obtain and change the cursor characteristics. Console applications can use these functions to modify the appearance of the cursor for emphasis, such as when the application goes from insert to overwrite mode. Note Unlike a GUI window, you can’t change the size of the font within a console window directly because the console window has specific limits placed on it. For example, the console window uses a monospaced font. You can learn more about the criteria for console fonts at http://support.microsoft.com/default.aspx?scid=kb;en−us;Q247815. The window size and the number of rows and columns of text determine the size of the console font. If you want to change the size of the font, then you need to change one of the associated font factors. For example, retaining the current number of rows and columns, while increasing the size of the window, will also increase the size of the font. Modifying font characteristics also presents an opportunity for emphasizing information. For example, you can present the text using a different color—red for danger or green for success. If the font is large enough, you can also add features such as underline (the underline still works with small fonts, but you can’t really see Using Cursors and Fonts 118 it). Listing 6.7 demonstrates the various font and cursor functions discussed in this section. You can find the example code in the \Chapter 06\C#\CursorAndFont and \Chapter 06\VB\CursorAndFont folders on the CD. (The source listing in Listing 6.7 is incomplete—check the source code on the CD for functions and structures discussed in previous sections of the chapter.) Listing 6.7: Examples of How to Use the Cursor and Font Functions // Obtains the current cursor settings. [DllImport("kernel32.dll", SetLastError=true)] public static extern bool GetConsoleCursorInfo( IntPtr hConsoleOutput, ref CONSOLE_CURSOR_INFO lpConsoleCursorInfo); // Modifies the cursor settings. [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetConsoleCursorInfo( IntPtr hConsoleOutput, ref CONSOLE_CURSOR_INFO lpConsoleCursorInfo); // The data structure used to get or set the cursor information. public struct CONSOLE_CURSOR_INFO { public UInt32 dwSize; // Percentage of character cell. public bool bVisible; // Is it visible? } // Function for obtaining the current console font. The font // can represent either the current window size or the maximum // window size for the machine. [DllImport("kernel32.dll", SetLastError=true)] public static extern bool GetCurrentConsoleFont( IntPtr hConsoleOutput, bool bMaximumWindow, ref CONSOLE_FONT_INFO lpConsoleCurrentFont); // This structure contains the console font information. public struct CONSOLE_FONT_INFO { public UInt32 nFont; // The font number. public COORD dwFontSize; // The font size. } // This function obtains the font size specified by the font // index (not necessarily the current font). [DllImport("kernel32.dll", SetLastError=true)] public static extern COORD GetConsoleFontSize( IntPtr hConsoleOutput, UInt32 nFont); // This function changes the text attributes. [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetConsoleTextAttribute( IntPtr hConsoleOutput, CharacterAttributes wAttributes); // This enumeration lists all of the character attributes. You // can combine attributes to achieve specific effects. public enum CharacterAttributes { FOREGROUND_BLUE = 0x0001, Using Cursors and Fonts 119 FOREGROUND_GREEN = 0x0002, FOREGROUND_RED = 0x0004, FOREGROUND_INTENSITY = 0x0008, BACKGROUND_BLUE = 0x0010, BACKGROUND_GREEN = 0x0020, BACKGROUND_RED = 0x0040, BACKGROUND_INTENSITY = 0x0080, COMMON_LVB_LEADING_BYTE = 0x0100, COMMON_LVB_TRAILING_BYTE = 0x0200, COMMON_LVB_GRID_HORIZONTAL = 0x0400, COMMON_LVB_GRID_LVERTICAL = 0x0800, COMMON_LVB_GRID_RVERTICAL = 0x1000, COMMON_LVB_REVERSE_VIDEO = 0x4000, COMMON_LVB_UNDERSCORE = 0x8000 } [STAThread] static void Main(string[] args) { IntPtr hOut; // Handle to the output device. CONSOLE_CURSOR_INFO CCI; // The current cursor information. CONSOLE_CURSOR_INFO NewCCI; // The new cursor information. CONSOLE_FONT_INFO CFI; // The console font information. COORD FontSize; // The size of the requested font. // Obtain a handle to the console screen. hOut = GetStdHandle(StdHandleEnum.STD_OUTPUT_HANDLE); // Get the cursor information. CCI.bVisible = false; CCI.dwSize = 0; GetConsoleCursorInfo(hOut, ref CCI); // Display the results. if (CCI.bVisible) Console.WriteLine("The cursor is displayed at {0}% of the" + " cell height.", CCI.dwSize); else Console.WriteLine("The cursor is invisible."); // Modify the cursor appearance. NewCCI.bVisible = true; NewCCI.dwSize = 100; if (SetConsoleCursorInfo(hOut, ref NewCCI)) { Console.WriteLine("\r\nThe new cursor settings are in effect."); Console.Write("Press any key when ready "); Console.ReadLine(); // Reset the cursor to its original size. Console.WriteLine("Returning the cursor to normal."); SetConsoleCursorInfo(hOut, ref CCI); } else // The call failed, normally due to an out of range value. Console.WriteLine("The cursor settings couldn’t be changed."); // Obtain the current font information. CFI.nFont = 0; CFI.dwFontSize.X = 0; CFI.dwFontSize.Y = 0; Using Cursors and Fonts 120 FontSize.X = 0; FontSize.Y = 0; GetCurrentConsoleFont(hOut, false, ref CFI); FontSize = GetConsoleFontSize(hOut, CFI.nFont); Console.WriteLine("\r\nThe Current Font Information:"); Console.WriteLine(" Font Number: {0}\r\n FontSize: {1} X {2}", CFI.nFont, FontSize.X, FontSize.Y); // Display the list of available font sizes. Console.WriteLine("\r\nThe List of Fonts Includes:"); GetCurrentConsoleFont(hOut, true, ref CFI); for (UInt32 Counter = 0; Counter <= CFI.nFont; Counter++) { FontSize = GetConsoleFontSize(hOut, Counter); Console.WriteLine(" {0} X {1}", FontSize.X, FontSize.Y); } // Display the text using various colors and attributes. Console.WriteLine("\r\nTesting Character Attributes:"); SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_BLUE | CharacterAttributes.FOREGROUND_GREEN | CharacterAttributes.FOREGROUND_INTENSITY); Console.WriteLine("This text is in turquoise."); SetConsoleTextAttribute(hOut, CharacterAttributes.BACKGROUND_BLUE | CharacterAttributes.BACKGROUND_GREEN | CharacterAttributes.BACKGROUND_RED); Console.WriteLine("This text is reverse video."); SetConsoleTextAttribute(hOut, CharacterAttributes.FOREGROUND_BLUE | CharacterAttributes.FOREGROUND_GREEN | CharacterAttributes.FOREGROUND_RED); // Wait until the user is done viewing the information. Console.Write("\r\nPress any key when ready "); Console.Read(); } As you can see from the source listing, most of the Win32 API functions in this example return a bool value indicating success. The exception is GetConsoleFontSize(), which returns a COORD value containing the size of the font. The use of a bool return value makes it easy to perform a quick check of call success. The code begins by displaying the current cursor information. If the bVisible variable is true, then the cursor is visible and the code displays the cursor size. Note that dwSize contains the size of the cursor as a percentage of the character size. The code then uses the SetConsoleCursorInfo() function to change the size of the cursor. Again, you need to specify the size of the cursor as a percentage of the character size. The example stops at this point so you can see the new cursor size. When you press Enter, the code returns the cursor to normal. The font information write−up in the Platform SDK documentation is unclear because it leads you to believe that the GetCurrentConsoleFont() function returns the font size in the dwFontSize variable of the CONSOLE_FONT_INFO structure. What you actually receive in the dwFontSize variable is the number of characters on screen. The default character settings allow for 80 characters across by 25 characters down. Consequently, you still need to use the GetConsoleFontSize() function to retrieve the actual size of the characters in pixels. Using Cursors and Fonts 121 Another problem with the documentation is that it tells you that you can retrieve the font values for the maximum window size using the GetCurrentConsoleFont(), without defining the term "maximum window size." As shown by the example, the maximum window size is determined by the current window size as well as machine limitations. Increase the current window size and the font index returned by this function will almost certainly increase. In short, the list returned by the code only reflects those fonts available in the current window, not the fonts available to Windows as a whole. In addition, the returned sizes only apply to the selected font, which is the raster font set in most cases. Windows also comes with a Lucida font for console windows, which changes the output from the example quite a bit. The character attributes work much as you think they might. However, you must provide a complete set of attributes for every call to SetConsoleTextAttribute(). Any value you don’t supply is automatically reset to nothing (black when working with colors). Therefore, if you want both foreground and background colors, you must supply both background and foreground attributes as part of the call. In addition, the attributes that begin with COMMON_LVB only apply to a double−byte character set (DBCS). If you want to see underlined text on screen, you need to use a DBCS font. Unfortunately, the standard version of Windows sold in English−speaking countries doesn’t include a DBCS font. Figure 6.7 shows the output from this example. Figure 6.7: The cursor and font example shows just some of what you can do in a console application. Determining the Console Window Title Windows will assign a default title to the console window when you create it—normally the name of the execution including path information. In some cases, the default title works just fine. However, there are times when you might want to personalize the window title to reflect the current application. To check the current title you’ll use the GetConsoleTitle() function—the SetConsoleTitle() function enables you to change the current title into something more appropriate. Listing 6.8 shows how to use these two functions. You’ll find the example code in the \Chapter 06\C#\WindowTitle and the \Chapter 06\VB\WindowTitle folders on the CD. Listing 6.8: Changing and Restoring the Console Window Title // This function retrieves the current window title. [DllImport("kernel32.dll", SetLastError=true)] public static extern Int32 GetConsoleTitle(StringBuilder lpConsoleTitle, Int32 nSize); // This function sets a new window title. [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetConsoleTitle(String lpConsoleTitle); [STAThread] static void Main(string[] args) { Determining the Console Window Title 122 StringBuilder WindowTitle; // The current window title. Int32 TitleSize; // The size of the buffer. // Obtain the current window title. WindowTitle = new StringBuilder(256); TitleSize = GetConsoleTitle(WindowTitle, 256); Console.WriteLine("The Old Window Title Is: \r\n" + WindowTitle.ToString()); // Create a new console window title. Console.WriteLine("\r\nSetting a new console window title."); SetConsoleTitle("A New Window Title"); Console.Write("Press any key to restore the old title "); Console.ReadLine(); // Restore the old console window title. SetConsoleTitle(WindowTitle.ToString()); // Wait until the user is done viewing the information. Console.Write("\r\nPress any key when ready "); Console.Read(); } Like other examples of Win32 API functions that modify a string buffer, the GetConsole−Title() function relies on a StringBuilder variable to hold the returned string value. Of course, you have to allocate the buffer—something you can do when declaring the variable or as a separate step for clarity. You must also provide the size of the buffer as part of the call. The code could have used a StringBuilder variable for the SetConsoleTitle() function as well, but a String works fine in this case. Changing the title produces the result shown in Figure 6.8. The only inconvenient aspect of using a string for the SetConsoleTitle() function is that you need to convert the StringBuilder variable to a string—something that’s easily done. Figure 6.8: Changing the console window title is easy using the Win32 API functions. Manipulating the Console Screen Buffer What precisely is a screen buffer? It’s the area of memory set aside to represent the contents of the screen. Windows applications don’t write directly to video memory, so they require some area of regular memory in which to place their data. When Windows updates that actual video memory, it considers the content of the screen buffer for each application. Consequently, knowing something about the console screen buffer can help you create a better user environment for your application. Manipulating the Console Screen Buffer 123 Sometimes you need to move some text around on screen. The easiest way to do this is to use the ScrollConsoleScreenBuffer() function to move the text. You can move any part of the screen buffer to any other part of the screen buffer, making this function exceptionally useful when displaying text on screen. Of course, the movement of text is limited by the clipping rectangle for the window. If part of the text will appear in an off−screen area as a result of the move, then the function merely clips the text to fit. You need to validate that any text movement you perform will stay on screen (unless you actually want to clip the text to fit within certain confines). An application isn’t limited to one screen buffer, but it must have at least one screen buffer to write any text to the console. You create a new screen buffer using the CreateConsoleScreenBuffer() function. The interesting part about creating a new screen buffer is that you can select a level of sharing for the buffer, which means two processes could potentially work with the same buffer. Use the SetConsoleActiveScreenBuffer() function to set the screen buffer that Windows uses for display purposes. This function also enables you to draw the content of the console screen in the background, and then display it in the foreground— creating a type of animation for the console. Finally, you can use the SetConsoleScreenBufferSize() function to change the number of rows and columns displayed by the console. This function is especially handy when the default console window is too small or large for the task at hand. Listing 6.9 demonstrates some of the functions described in this section. You can find the source code in the \Chapter 06\C#\ScreenBuffer and \Chapter 06\VB\ScreenBuffer folders on the CD. (The source listing in Listing 6.9 is incomplete—check the source code on the CD for functions and structures discussed in previous sections of the chapter.) Listing 6.9: Methods for Working with the Console Screen Buffer // This function enables you to move part of the screen buffer // to another location. [DllImport("kernel32.dll", SetLastError=true)] public static extern bool ScrollConsoleScreenBuffer( IntPtr hConsoleOutput, ref SMALL_RECT lpScrollRectangle, ref SMALL_RECT lpClipRectangle, COORD dwDestinationOrigin, ref CHAR_INFO lpFill); // This function enables you to move part of the screen buffer // to another location. [DllImport("kernel32.dll", SetLastError=true)] public static extern bool ScrollConsoleScreenBuffer( IntPtr hConsoleOutput, ref SMALL_RECT lpScrollRectangle, IntPtr NoClipRectangle, COORD dwDestinationOrigin, ref CHAR_INFO lpFill); // This structure defines a rectangular area on the screen // consisting of an upper left and a lower right corner. [StructLayout(LayoutKind.Sequential)] public struct SMALL_RECT { public Int16 Left; public Int16 Top; public Int16 Right; public Int16 Bottom; } Manipulating the Console Screen Buffer 124 [StructLayout(LayoutKind.Sequential)] public struct CHAR_INFO { public Char Character; public CharacterAttributes Attributes; } [STAThread] static void Main(string[] args) { IntPtr hOut; // Handle to the output device. SMALL_RECT ScrollRect; // The area to scroll on screen. COORD Dest; // The scrolled area destination. CHAR_INFO FillData; // The data to put in the scrolled area. // Obtain a handle to the console screen. hOut = GetStdHandle(StdHandleEnum.STD_OUTPUT_HANDLE); // Display some data on screen. Console.WriteLine("This is some data to scroll."); Console.Write("Press any key to scroll "); Console.ReadLine(); // Initialize the variables. ScrollRect.Top = 0; ScrollRect.Left = 0; ScrollRect.Right = 15; ScrollRect.Bottom = 5; Dest.X = 20; Dest.Y = 10; FillData.Attributes = CharacterAttributes.FOREGROUND_BLUE | CharacterAttributes.FOREGROUND_RED | CharacterAttributes.FOREGROUND_INTENSITY; FillData.Character = ‘A’; // Scroll an area of the screen. if (!ScrollConsoleScreenBuffer(hOut, ref ScrollRect, IntPtr.Zero, Dest, ref FillData)) Console.WriteLine("Couldn’t scroll the screen buffer."); // Wait until the user is done viewing the information. Console.Write("\r\nPress any key when ready "); Console.Read(); } This example shows you a couple of new tricks to use when working with Win32 API calls. The first problem you need to overcome is allowing the clipping rectangle to contain a null value. Unfortunately, neither C# nor Visual Basic will allow you to assign a null value (Nothing) to a structure, so you need to devise another way to overcome the problem. There are a number of ways to do this, but the example code shows the most convenient way. Simply create two declarations of the function. The first contains a place for the clipping rectangle, while the second doesn’t. Manipulating the Console Screen Buffer 125 The second problem is one that doesn’t even appear in the code, but could cause a problem if you don’t look for it in the Platform SDK documentation. The CHAR_INFO structure contains a union as shown in the following code: typedef struct_CHAR_INFO { union { WCHAR UnicodeChar; CHAR AsciiChar; } Char; WORD Attributes; } CHAR_INFO, *PCHAR_INFO; A union is a special class type for C++ that says you must supply one of the data types to fill the structure, but only one of the data types. The CHAR_INFO structure can accept either an ASCII character or a Unicode character as its first data member. In this case, we don’t need to worry about the union because the .NET language takes care of this concern automatically. However, in cases where the language doesn’t resolve the problem for you, you’ll need to come up with a creative solution. In many cases, the best solution is to create multiple versions of the data structure—one for each of the types listed in the union. The actual mechanics of the ScrollConsoleScreenBuffer() function are straightforward. The code shows how to fill out the data structures. The example moves some text that originally appears at the top of the screen to a location around the middle of the screen as shown in Figure 6.9. The addition of a clipping rectangle makes it easy to move text within a certain area of the display. Any text that appears outside of the clipping rectangle at the time of the scroll remains untouched. Any text that would appear outside of the clipping rectangle after the scroll is clipped. Figure 6.9: Moving text around on screen is easy when using the ScrollConsoleScreenBuffer() function. Where Do You Go from Here? This chapter has shown you some of the things you can do to make your next console application a little easier to use and more functional. Of course, a console application will never have the same level of user interface functionality that you’ll find in a GUI application, but the fact remains that most console applications today are decidedly Spartan. Fortunately, you can still access the Win32 API to fill the holes in coverage left by the .NET Framework. One of the things you should do now that you know how to accomplish the tasks presented in this chapter is look at some of your current applications. Ask yourself whether some of the dialog−based utilities that you currently provide as part of your application would work better as a console−based application. Remember that the main reason to use a console−based application is to provide an easy method for administrators to script the application and to reduce the dependence of the application on the GUI. You’ll also want to spruce Where Do You Go from Here? 126 up your existing console applications. Make sure the applications provide a reliable and useful appearance. Even console applications should work well and keep the user’s needs in mind. It’s important to begin creating toolkits of missing Win32 API functions—essentially DLLs that contain functions you can call quickly from managed code and know that the function will work immediately without an understanding of the underlying Win32 API calls. Console applications present a great opportunity for building such DLLs because the number of functions is limited and working with the console screen is somewhat easier than working with the GUI. Of course, you’ll want to create toolkits for your GUI applications too, but now might be a good time to experiment with some of the console functions presented in this chapter. Chapter 7 continues the search for ways to plug the holes in the .NET Framework coverage of the Win32 API. In this next chapter we’ll discuss easy access to hardware—especially lower−level hardware such as the serial and parallel ports. The chapter concentrates on fairly generic hardware—you won’t find instructions for creating an interface to specific hardware such as a certain model of camera. It’s also important to note that this chapter will concentrate on standardized access using existing technology—you won’t learn how to create a device driver with .NET (something that .NET is ill−equipped to handle in any event). Where Do You Go from Here? 127 [...]... about their status, but you might find other types of printers lacking in some areas Creating the Wrapper DLL The wrapper DLL does most of the work of retrieving the printer information from the Win32 API in this case There are times when you’ll need to write much of your Win32 API access code with the wrapper, instead of the application, in mind In this case, the functionality needed to access the printer... retrieve a single specific bit of information such as the default spool directory The main function is GetPrinter(), which relies on a host of data structures to tell you everything from the printer name, to the provider setup, to the current printer status, including the condition of the paper tray Of course, the printer has to provide the information you need Most laser printers provide a wealth of information... provide the same level of access as the Win32 API functions found in this chapter The following sections will explore several serial port examples The main purpose of these examples is to help you gain full access to the serial port using a combination of the NET Framework and the Win32 API I won’t discuss a full−fledged communication program because that topic is discussed in better detail in other books... chapter) You’ll find the source code for this portion of the example in the \Chapter 07\PrinterAccess folder on the CD Listing 7.3: The Wrapper Code Performs Most of the Work in this Example // This is the Level1 data structure gc struct PrinterInfo1 { UInt32 Flags; String *pDescription; String *pName; String *pComment; }; static bool GetPrinterLevel1Data(String* PrinterName, PrinterInfo1 **PI1) { LPTSTR... folders on the CD Listing 7 .4: Obtaining the Printer Status is a Matter of Choosing an Information Level public enum PrintFlags { PRINTER_ENUM_EXPAND PRINTER_ENUM_CONTAINER PRINTER_ENUM_ICON1 PRINTER_ENUM_ICON2 PRINTER_ENUM_ICON3 PRINTER_ENUM_ICON4 PRINTER_ENUM_ICON5 PRINTER_ENUM_ICON6 PRINTER_ENUM_ICON7 PRINTER_ENUM_ICON8 } = = = = = = = = = = 0x000 040 00, 0x00008000, 0x00010000, 0x00020000, 0x00 040 000,... away with is the tools required to create any type of serial access, not just the access used of data exchange Creating a Serial Stream For those of us who’ve worked with the Win32 API for a while, the idea of needing to use the Win32 API to do something as simple as open the serial port might seem a bit strange In fact, the NET framework did provide a means to open the serial port using a FileStream... Change the char into an Int32, and then convert the resulting number into a string Figure 7.3 shows the output from this example Figure 7.3: The example application shows the status of the serial port and attached modem, if any Parallel Port Access Examples One of the problems with working with the Win32 API is that it’s like an onion—there are several layers of code between the developer and the device... bad information to the user, it’s often best to ignore flags marked reserved for future use Viewing the Second Level of Printer Information If you thought the level 1 printer information was sparse, you’ll be pleased by the level 2 information You can find out almost every piece of information about the printer using the level 2 version of GetPrinter() In fact, it’s easy to go into information overload... everything from the printer name, to its security settings, to the printer statistics For example, you can find out the current form type and the size of the paper use, as well as the number of pages the printer can output per minute Note You’ll find the source code for the second level of this example in the \Chapter 07\C#\Parallel2 and \Chapter 07\VB\Parallel2 folders on the CD Make sure you also view the. .. PI1.pComment); // Interpret the flags if ((PI1.Flags & (UInt32)PrintFlags.PRINTER_ENUM_EXPAND) == (UInt32)PrintFlags.PRINTER_ENUM_EXPAND) SB.Append("\r\nThe provider has other objects to enumerate."); if ((PI1.Flags & (UInt32)PrintFlags.PRINTER_ENUM_CONTAINER) == (UInt32)PrintFlags.PRINTER_ENUM_CONTAINER) SB.Append("\r\nThis object contains other enumerable objects."); if ((PI1.Flags & (UInt32)PrintFlags.PRINTER_ENUM_ICON1) . who’ve worked with the Win32 API for a while, the idea of needing to use the Win32 API to do something as simple as open the serial port might seem a bit strange. In fact, the .NET framework did provide. the Console Window Title 122 StringBuilder WindowTitle; // The current window title. Int32 TitleSize; // The size of the buffer. // Obtain the current window title. WindowTitle = new StringBuilder(256); . accessing many of the pieces of hardware that the .NET Framework doesn’t support. The examples will provide the information you need to interact with other pieces of hardware on the system. Of course,