Frequently Asked Questions: Visual C++ .NET
Question: How can I change the title on the message box instead of using the application name?
Answer: By default, the message box window uses the application name as the window title. You can change this by adding a second text string to the MessageBox function call. The first string is always the message to be displayed, and the second string is used as the window title. For example, the OnClickedHello function would look like:
// Say hello to the user MessageBox("Hello. This is my first Visual C++ Application!", "My First Application");
Question: How does the debugger work? How does it know where in my code the application execution is?
Answer: In a debug build, the compiler adds extra information that doesn't get executed when running the application. This information is mixed in with the executable code. The information specifies what source code file and line of code each set of machine instructions correlates to. It also contains information about each of the variables in the code and where each of them is stored in memory while the application is running. This is all information that the debugger uses to display the correct code as you are stepping through your application.
Question: I don't want my breakpoint to be triggered each time that my application hits it. Is there some way of getting it to trigger only when certain values are reached?
Answer: If you open the Breakpoints pane, you can view the properties of each of your breakpoints. Within the Breakpoint Properties dialog box, you can place conditions on the breakpoint, such as stopping only when a certain variable is equal to a specific value.
Question: When I specified the object IDs of the controls on the window, three controls had the same IDIDC_STATIC. These controls were the text at the top of the window and the two group boxes. The other two static text controls started out with this same ID until I changed them. How can these controls have the same ID, and why did I have to change the ID on the two static texts where I did change them?
Answer: All controls that don't normally have any user interaction, such as static text and group boxes, are, by default, given the same object ID. This works fine as long as your application doesn't need to perform any actions on these controls. If you do need to interact with one of these controls, as you did with the static text prompts for the edit box and the combo box, you need to give that control a unique ID. In this case, you needed the unique ID to be able to retrieve the control object so that you could enable or disable and show or hide the control. You also need to assign it a unique ID if you want to attach a variable to the control so that you can dynamically alter the text on the control.
The application behaves in a somewhat unpredictable way if you try to alter any of the static controls that share the same ID. As a general rule of thumb, you can allow static controls to share the same object ID if you aren't going to alter the controls at all. If you need to perform any interaction with the controls, you need to assign each one a unique object ID.
Question: Is there any way to manipulate the controls other than retrieving the control objects using their object IDs?
Answer: Using the Add Member Variable Wizard, you can declare variables for your controls by specifying Control for the variable category. This basically gives you an variable that is the control's MFC class, providing you with a direct way of altering and interacting with the control. You can then call all CWnd class functions on the control, as you did to enable or disable and show or hide the controls in your application. Or, you can call the control class methods, enabling you to do things in the code that are specific to that type of control. For example, if you add another variable to the combo box control and specify that it's a Control category variable, you can use it to add items to the drop-down list on the control.
Question: How can you tell whether the Shift or Ctrl keys are being held down when you receive the WM_KEYDOWN message?
Answer: You can call another function, ::GetKeyState, with a specific key code to determine whether that key is being held down. If the return value of the ::GetKeyState function is negative, the key is being held down. If the return value is not negative, the key isn't being held down. For instance, if you want to determine whether the Shift key is being held down, you can use this code:
if (::GetKeyState(VK_SHIFT) < 0) MessageBox("Shift key is down!");
A number of virtual key codes are defined in Windows for all the special keys. These codes let you look for special keys without worrying about OEM scan codes or other key sequences. You can use these virtual key codes in the ::GetKeyState function and pass them to the OnKeyDown function as the nChar argument. Refer to the Visual C++ documentation for a list of the virtual key codes.
Question: What's the interval range that I can set for timers in my applications?
Answer: The available range that you can set for timers in your applications is around 55 milliseconds on the short end to 232 1 milliseconds, or around 49 1/2 days, on the long end.
Question: Is there any way to trigger my application to perform some work when it's idle rather than use a timer to trigger the work when I think my app might be idle?
Answer: Yes, there is. All Windows applications have an OnIdle function that can be used to trigger idle processing.
Question: What happens if I specify two or more button combinations in the same MessageBox function call?
Answer: Your application compiles just fine, but when the MessageBox function is called, sometimes nothing happens and sometimes one of the button combinations selected is displayed. Keep in mind that only one set of buttons will be displayed, so there is no reason to OR two or more sets of buttons together.
Question: How can I integrate the File Open dialog box into my application where it opens in a specific directory that I specify?
Answer: The CFileDialog class has a public property called m_ofn. This property is a structure that contains numerous attributes of the File Open dialog box, including the initial directory. This structure is defined as the OPENFILENAME structure in Listing 1.
Listing 1: The OPENFILENAME Structure
typedef struct tagOFN { // ofn DWORD lStructSize; HWND hwndOwner; HINSTANCE hInstance; LPCTSTR lpstrFilter; LPTSTR lpstrCustomFilter; DWORD nMaxCustFilter; DWORD nFilterIndex; LPTSTR lpstrFile; DWORD nMaxFile; LPTSTR lpstrFileTitle; DWORD nMaxFileTitle; LPCTSTR lpstrInitialDir; LPCTSTR lpstrTitle; DWORD Flags; WORD nFileOffset; WORD nFileExtension; LPCTSTR lpstrDefExt; DWORD lCustData; LPOFNHOOKPROC lpfnHook; LPCTSTR lpTemplateName; } OPENFILENAME;
You can set any of these attributes before calling the DoModal class method to control the behavior of the File Open dialog box. For instance, if you set the starting directory to C:\Temp before calling the DoModal method, as in Listing 2, the File Open dialog box opens in that directory.
Listing 2: The Revised OnBnClickedBfileopen Function
void CDialogsDlg::OnBnClickedBfileopen(void) { // TODO: Add your control notification handler code here CFileDialog ldFile(TRUE); // Set the current directory ldFile.m_ofn.lpstrInitialDir = "C:\\Temp\\"; // Show the File Open dialog and capture the result if (ldFile.DoModal() == IDOK) { // Get the file name selected m_strResults = ldFile.GetFileName(); // Update the dialog UpdateData(FALSE); } }
Question: Do I have to give my menu items the same names that everyone else uses? For example, a lot of applications use File and Help. Can I name my menus something else?
Answer: You can name your top-level menus anything you want. However, there are accepted menu name conventions that place all file-oriented functionality under a menu labeled File and all help-related functionality under a menu labeled Help. If you have a menu with entries such as Broccoli, Corn, and Carrots, you will probably want to call the menu Vegetables, although an equally valid label would be Food or Plants. In general, if you want to make your application easy for your users to learn, you will want to use menu labels that make sense for the entries on the pull-down portion of the menu.
Question: How can I limit the fonts in my list to just the TrueType fonts?
Answer: You can check the nFontType argument to your callback function to determine the font type. For instance, if you want to include only TrueType fonts in your list of fonts, you modify your callback function to mask the nFontType argument with the TRUETYPE_FONTTYPE constant and check to see if the resulting value equals the TRUETYPE_FONTTYPE value, as in the following listing:
int CALLBACK EnumFontFamProc(LPENUMLOGFONT lpelf, LPNEWTEXTMETRIC lpntm, DWORD nFontType, long lParam) { // Create a pointer to the dialog window CTextFontsDlg* pWnd = (CTextFontsDlg*) lParam; // Limit the list to TrueType fonts if (nFontType & TRUETYPE_FONTTYPE) { // Add the font name to the list box pWnd->m_ctlFontList.AddString( lpelf->elfLogFont.lfFaceName); } // Return 1 to continue font enumeration return 1; }
Question: Why do I need to specify both a pen and a brush if I want to display just one?
Answer: You are always drawing with both when you draw any object that's filled in. The pen draws the outline, and the brush fills in the interior. You can't choose to use one or the other; you have to use both. If you only want to display one, you need to take special steps.
Question: Why do all of the pen styles become solid when I increase the pen width above 1?
Answer: When you increase the pen width, you are increasing the size of the dot that's used to draw with. When you first try to draw by capturing each spot that the mouse covers, all you draw are a bunch of dots. Well, after you increase the size of the dots that you are drawing the line with, the gaps between the dots are filled in from both sides, providing an unbroken line.
Question: In some applications, toolbars have the option of showing text, as in Internet Explorer. How can I add text to my toolbar buttons?
Answer: Unfortunately, the toolbar designer provides no way of adding text to the toolbar buttons. This means that you have to add the text to the buttons in your application code, similar to specifying that all the color toolbar buttons should behave as radio buttons. Use the SetButtonText function to set the text on each toolbar button individually. This function takes two arguments: the button's index number and the button's text. If you really want to place text on the toolbar buttons, you also have to resize the toolbar to allow room for the text to be displayed.
Question: How can I set the text in the first section of the status bar, other than by using menu and toolbar prompts?
Answer: You can use SetWindowText to set the text in the first pane of the status bar. As a default setting, the first pane is a separator that automatically expands to fill the width of the status bar with the other panes right-justified on the bar. The SetWindowText function, called on the status bar variable, sets the text in the first pane only. If you want to set the text in any other pane, at any time other than in the ON_UPDATE_COMMAND_UI event handler, you can use the SetPaneText function. There are two ways that you can set the text in the main part of the status bar. The first is:
CString myString = "This is my string" m_wndStatusBar.SetWindowText(myString);
The other method is:
CString myString = "This is my string" m_wndStatusBar.SetPaneText(0, myString);
Question: Why do I need to change the version number in the IMPLEMENT_SERIAL macro if I change the Serialize function in the record custom class?
Answer: Whether you need to increment the version number depends on the type of change you make. For example, if you add a calculated field in the record class and you add the code to calculate this new variable from the values that you read in the variables from the CArchive object, you don't really need to increment the version number because the variables and order of the variables that you are writing to and reading from the archive didn't change. However, if you add a new field to the record class and add the new field into the I/O stream being written to and read from the CArchive object, what you are writing to and reading from the archive will have changed, and you do need to increment the version number. If you don't increment the version number, reading files created using the previous version of your application will result in an "Unexpected File Format" message instead of the file being read. Once you increment the version number and you read a file written with the old version number, you get the same message, but you have the option of writing your own code to handle the exception and redirecting the archive to a conversion routine to convert the file to the new file format.
Question: When do I need to recompile the applications that use my DLLs?
Answer: Whenever you change any exported function calls. Changing, adding, or removing arguments to any of these functions would mean that you need to recompile the applications that use the DLL. If you are working with an MFC extension DLL, the applications that uses the DLL needs to be recompiled if the public interface for the exported classes changes or a new function or variable is added or removed. It doesn't matter that the application isn't using any changed functions; it's still good practice to recompile the applications, just to be sure.
Question: Why is Print Preview not included on the default menus when I choose CHtmlView as the base class for my view class?
Answer: The printing for the CHtmlView class is performed by the browser, not the view class. You don't have Print Preview because the browser doesn't support it.
Question: How can I get the HTML source code from the browser so that I can see or edit it?
Answer: The CHtmlView class has a member function, GetSource, that takes a CString variable as its only parameter. This CString parameter is populated with the HTML source code.
Question: How do Internet applications work?
Answer: Most Internet applications have a script of messages that are passed back and forth. The messages consist of a command and the data that needs to accompany that command. The server reads the command and processes the data appropriately, sending back a status code to let the client know the success or failure of the command. If you want to learn more about how Internet applications do this, several books cover this subject area in detail. You can also find the RFC documents that specify the communication protocol for various standardized applications at the Web site for the Internet Engineering Task Force at http://www.ietf.org. RFC documents are the specification documents for various applications, file formats, and other standards that are used throughout the Internet.
Question: How does a server application handle a large number of simultaneous connections from clients?
Answer: With a full-strength server, connection sockets aren't declared as class variables. The server instead uses some sort of dynamic allocation of sockets in an array or link list to create sockets for the clients as the connection requests come in. Another approach often taken by servers is to spin off a separate thread for each connection request. This allows the application to have a single socket connection per thread, making it much easier to keep track of the sockets. In any case, server applications don't normally have a single connection socket variable. Keep in mind, however, that if too many threads are running, the number of context switches will degrade server performance significantly, so the separate thread per connection may not be an appropriate approach for a heavy-load server application.
Question: How can I mix managed and unmanaged C++ in my applications?
Answer: In the same way that you use the project properties to mark the entire MFC project to be compiled for the CLR, you can mark individual files in the project. This enables you to mix parts of your application, marking some components to be managed while keeping others as unmanaged. Usually, you'll want to add wrapper components in separate managed files, providing access to the unmanaged components by other CLR objects and applications.
Question: When adding the parameters for the COM interface method, what were the check boxes (in, out, retval) above where the parameters were added used for?
Answer: Those check boxes added the direction for each parameter in the IDL definition of the COM interface. If you examine the IDL language in the header file for the COM interface class, you'll find that the interface definition of the method contains [in] in front of the parameters that you checked the in box on, and [out] on the parameters that you checked the out box on. This specifies the direction and purpose for each of these parameters in the method. [in] specifies that a parameter is passed in to the method. [out] specifies that the parameter is replaced with a result value in the method that needs to be returned to the calling function. You can have both [in] and [out] specified for a parameter, signaling that the parameter's value is imported both when the method is being called and when returning from the method. If you checked the retval box, this parameter signals that this parameter will be returned as the method result instead of the success or failure indicator.
Question: In the unmanaged C++ client, why couldn't I put the __gc modifier on the managed class?
Answer: The managed class in an unmanaged client has to be accessible by the unmanaged code in the application. When a managed class is marked for garbage collection, it can't be accessed by any unmanaged code. The reverse situation is seen when you have a managed, garbage-collected class accessing an unmanaged class. It seems that managed, garbage-collected objects can reach outside the managed sandbox to access unmanaged objects. It's just that the garbage-collected objects are located on the CLR-managed heap, which can't be accessed from the outside. But objects on the managed CLR heap can use nongarbage-collected pointers to access objects on the outside.