home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c081_11 / 8.ddi / DOC.ZIP / WINPRIMR.DOC < prev   
Encoding:
Text File  |  1991-02-13  |  41.2 KB  |  1,122 lines

  1. HOW TO USE THIS FILE: This file has a table of contents
  2. that refers to "page numbers" in this file. If your editor
  3. has a search facility, you can use it to search for the page
  4. numbers listed in the table of contents. The phrase "Page n"
  5. (where n represents the actual page number) appears at the
  6. bottom left of the "page" it refers to. Thus, at the bottom
  7. of page 1, you'll find "Page 1" as the last item on that "page."
  8.  
  9.                   A PRIMER ON C++ AND WINDOWS
  10.                        TABLE OF CONTENTS
  11. ___________________________________________________________________
  12.  
  13.  
  14. Chapter 1  A primer on Windows         Defining the base class . 13
  15.            and C++             1       Defining the derived
  16. Object-oriented thinking . . . 1       class . . . . . . . . . . 14
  17. A Windows vocabulary . . . . . 2       Creating and painting the
  18. C and Windows  . . . . . . . . 4       window  . . . . . . . . . 15
  19. The WHELLO application . . .  11     Pointers to Windows . . . . 17
  20.   Directives and                     The callback function . . . 18
  21.   intialization  . . . . . .  11     Putting it all together with
  22.   Defining the Main class  .  12     WinMain . . . . . . . . . . 19
  23.   Defining Windows classes .  13
  24.  
  25.                                  i
  26.  
  27.  
  28.                                         A primer on Windows and C++
  29.  
  30.  
  31. This chapter demonstrates the use of C++ concepts and constructs in
  32. building an object-oriented Windows application. It is not an
  33. exhaustive orientation to Windows programming. If you are new to
  34. Windows programming, you will gain some familiarity with Windows by
  35. reading this primer. If you are new to C++, you should first read
  36. chapters 4 and 5 in Getting Started.
  37.  
  38.  
  39. Object-oriented thinking
  40.  
  41. In the same manner as object-oriented programming and for much the
  42. same benefits, Windows seeks to mimic the way we think and work by
  43. presenting us with objects that represent both data and a way to
  44. manipulate that data. A window is an object, because it
  45. encapsulates data and the functions that act on that data. Windows
  46. are polymorphic in that they have the same basic definition but can
  47. change their behavior in response to different conditions. The
  48. displayed windows can inherit from more basic structures. So, if
  49. you've learned object-oriented programming in general, as you learn
  50. to program Windows, a number of concepts should "feel" familiar to
  51. you; you've already accomplished some of the change in perspective
  52. that programming for Windows requires.
  53.  
  54. The venture of programming in Windows benefits not only from the
  55. concepts of object-oriented programming, but also from the specific
  56. features of C++. C++ lends itself to Windows programming because
  57. graphic objects, such as windows, involve complex data structures
  58. and functions which are best thought of and handled together. In
  59. fact, when you define a window under Windows, you initialize a
  60. structure that contains information about the window, and that
  61. structure includes a pointer to a function that itself is called
  62. when another process acts on the window. In C, this is a structure
  63. that includes a pointer. In C++ you can simply design a class to
  64. accomplish the same thing. Under Windows, you would design the
  65. class as a "wrapper" for the structure. All of your future
  66. interactions with the structure could then be through the class.
  67.  
  68.  
  69.  
  70. Chapter 1 Page 1
  71.  
  72.  
  73. Furthermore, specific C++ features will make your programming life
  74. more pleasant while you're programming for Windows. Windows is a
  75. message-based system. Put simply, this means that Windows sends out
  76. messages to your application to tell it that some event has
  77. occurred. At the heart of every Windows application, you write a
  78. message loop to wait for messages. As the message loop gets
  79. messages, it sends them off to a function that decides how to
  80. respond to the message. The decision process embodied in the
  81. message-handling function usually takes the form of a large,
  82. unwieldy switch statement. Although this switch cannot be entirely
  83. avoided, virtual functions make the process somewhat easier to
  84. manage. Used wisely, this has the effect of making code more
  85. efficient and more readable.
  86.  
  87.  
  88. A Windows vocabulary
  89.  
  90. To get you up to speed fast, this section introduces some of the
  91. Windows words and concepts.
  92.  
  93. module or executable
  94.  
  95. For documentation purposes, the term module is used generically to
  96. mean one of two things: an application (.EXE) or a dynamic linked
  97. library (.DLL). Sometimes the term "executable" is also used in
  98. this generic sense. Module can also be used in the more specific
  99. sense of a self-contained piece of a program (for instance, one of
  100. the many modules linked together when you build the program). An
  101. application runs under Windows and probably calls functions from
  102. DLLs. DLLs contain functions that can be linked to your application
  103. at run time (hence the name dynamic link library as opposed to
  104. regular libraries which are linked when the program is built). We
  105. explain DLLs in greater depth later in this chapter.
  106.  
  107. application
  108.  
  109. A Windows application is a program intended to be called by and run
  110. under Windows. It can call functions from static libraries and from
  111. DLLs; it can also launch other applications. The run-time input to
  112. an application, via its window, is retrieved by Windows, then
  113. passed as a message to the window function (usually called WndProc)
  114. for the active window. Similarly, output from an application to a
  115. window occurs through Windows. Most DOS applications can basically
  116. take over the machine, assume that all the resources belong to
  117. them, tell a relatively passive DOS what to do, and so on. In a
  118. Windows application, Windows itself is very active, and very much
  119. involved in the workings of your program.
  120.  
  121. DLLs
  122.  
  123. DLLs are library modules, specifically dynamic link libraries. DLLs
  124. are dynamically linked to an application or another DLL at run-
  125.  
  126.  
  127.  
  128. Chapter 1 Page 2
  129.  
  130.  
  131. time, instead of statically linked at build time. For instance,
  132. let's say your application calls a function from one of the run-
  133. time libraries. When your code is being linked, the linker gets a
  134. copy of that function and places the entire copy in your
  135. executable. If you call many functions from libraries, this can
  136. quickly enlarge your code. If you call a function from a DLL,
  137. however, a single copy of that function serves all applications
  138. which refer to it and is accessed in the DLL at run-time. The
  139. function itself never becomes part of your executable. Furthermore,
  140. if another .EXE also uses the .DLL, Windows uses the copy already
  141. in memory; it doesn't load another copy.
  142.  
  143. windows.h
  144.  
  145. Every Windows module must be compiled with the windows.h header
  146. file. windows.h includes prototypes for all the Windows API
  147. functions, defines data types and structures, and defines a number
  148. of global variables. You must include windows.h in any module that
  149. uses any Windows function, structure, or data type, or tests any of
  150. the defined variables. If you're new to Windows programming, you
  151. will find it useful to browse through windows.h.
  152.  
  153. stub
  154.  
  155. Windows must be loaded before you can run a program compiled and
  156. linked for Windows. Windows programs include a stub program that
  157. runs if the program was invoked from DOS (that is, DOS without
  158. Windows), and, if so, terminates the application with an error
  159. message. A default stub that does exactly that is linked in
  160. automatically by the linker. You can create and include your own
  161. stub, if you like. More sophisticated stub programs could, for
  162. example, start Windows if it is not running, or run a DOS version
  163. of the program.
  164.  
  165. functions under Windows
  166.  
  167. Besides all the usual things you need to know about a function--
  168. whether it is near or far, what type of data it returns and
  169. expects, how it should be called and named--now you need to know
  170. one more thing: Is it a callback function, an exported function, or
  171. an imported function?
  172.  
  173. exported functions
  174.  
  175. This term relates to how a function must be declared in one module
  176. so that it can be accessed by other modules. Exported functions are
  177. those functions from a given module that can be called from outside
  178. that module--either by Windows or by another module. These
  179. functions must be handled in a certain way so that they will
  180. correctly establish the appropriate data segment when they are
  181. called. A DLL, because it provides functions for other modules to
  182. call, is comprised of exported functions for each of the callable
  183.  
  184.  
  185.  
  186. Chapter 1 Page 3
  187.  
  188.  
  189. entry points in the DLL. An application, on the other hand,
  190. typically has few, and all of these are of the special subclass of
  191. exported functions called callback functions, described next. The
  192. handling of exported functions is described in more detail in the
  193. User's Guide, Chapter 3.
  194.  
  195. callback functions
  196.  
  197. A callback function is a special kind of exported function.
  198. Callback functions are those functions that the Windows environment
  199. calls directly. An application must have at least one callback
  200. function, which Windows invokes when messages are to be processed
  201. by the application. This function corresponds to an active window
  202. and is called a window procedure (or WndProc). Since most
  203. applications also create at least one window, and Windows needs to
  204. send messages to that window, the function that receives those
  205. messages must be called by Windows (when we walk through the
  206. program, you'll see how Windows knows exactly which function to
  207. call for which window).
  208.  
  209. imported functions
  210.  
  211. Imported functions are those functions available to your module
  212. from libraries (DLLs). When the IDE links a Windows application, it
  213. automatically includes a file named IMPORT.LIB that allows your
  214. program to call the functions from the Windows API (Application
  215. Program Interface). The linked file is an import library. Import
  216. libraries provide the link between an application and the functions
  217. available to it from one or more DLLs.
  218.  
  219.  
  220. C and Windows
  221.  
  222. This section illustrates programming for Windows using C. The next
  223. section will walk you through a similar program that uses C++
  224. instead.
  225.  
  226.  
  227.   /*  Windows HELLO WORLD - C Version */
  228.  
  229.   #include <windows.h>
  230.   #include <string.h>
  231.  
  232.   long FAR PASCAL WndProc( HWND hWnd, WORD message,
  233.                            WORD wParam, LONG lParam );
  234.  
  235.   void Paint( HWND hWnd );
  236.  
  237.   char szClassName[] = "Hello, World!";
  238.  
  239.  
  240.  
  241. Chapter 1 Page 4
  242.  
  243.  
  244. The preceding lines of code include the Windows header file
  245. windows.h and the header file string.h (for access to strlen). The
  246. prototypes are given for WndProc and Paint.
  247.  
  248. The string szClassName, used in WinMain and Paint, globally defines
  249. the name of the main window class.
  250.  
  251.   int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance,
  252.                       LPSTR lpszCmdLine, int nCmdShow )
  253.  
  254. When Windows calls WinMain, it passes it four arguments. HANDLE is
  255. defined in windows.h as an unsigned integer (16 bit); LPSTR is
  256. defined as a far character pointer (long pointer to a string).
  257. Because several instances of an application can be running at the
  258. same time, the hInstance (instance handle) is needed to provide a
  259. unique identifier for each instance of a program. hPrevInstance is
  260. the instance handle of the most recent previous instance of the
  261. same application. hPrevInstance is set to NULL if there is no
  262. previous instance.
  263.  
  264. lpszCmdLine is a far pointer to a null-terminated string containing
  265. the command-line parameters with which the application was invoked.
  266.  
  267. nCmdShow is a number indicating how to display the application when
  268. it is launched. For instance, the application can be launched as
  269. minimized and inactive.
  270.  
  271.   {
  272.     HWND hWnd;   // handle to a window (unsigned int)
  273.     MSG msg;     // structure used to accept and manipulate
  274.                  //  Windows messages
  275.     if ( !hPrevInstance )
  276.     {
  277.       WNDCLASS wndclass;   /* Structure to register window class.  */
  278.       wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  279.       wndclass.lpfnWndProc   = WndProc;
  280.       wndclass.cbClsExtra    = 0;
  281.       wndclass.cbWndExtra    = 0;
  282.       wndclass.hInstance     = hInstance;
  283.       wndclass.hIcon         = LoadIcon( NULL, IDI_APPLICATION );
  284.       wndclass.hCursor       = LoadCursor( NULL, IDC_ARROW );
  285.       wndclass.hbrBackground = GetStockObject( WHITE_BRUSH );
  286.       wndclass.lpszMenuName  = NULL;
  287.       wndclass.lpszClassName = szClassName;
  288.  
  289.       if ( ! RegisterClass( &wndclass ) )
  290.          return FALSE;
  291.     }
  292.  
  293. Windows creates a window according to information contained in a
  294. window class. The window class not only defines display
  295.  
  296.  
  297.  
  298. Chapter 1 Page 5
  299.  
  300.  
  301. characteristics of the window, it also names the window and
  302. specifies the window procedure that Windows must use to pass
  303. messages to the window (to the application).
  304.  
  305. Every instance of an application uses the same definition of a main
  306. window; therefore, each window class only needs to be defined once
  307. (although an application may have multiple window classes). This
  308. code tests for a previous instance, and registers the window class
  309. if there is no previous instance.
  310.  
  311. Let's examine each member of wndclass (note that the online help
  312. provides a complete reference to the WNDCLASS structure as well as
  313. the other structures defined in windows.h):
  314.  
  315.    wndclass.style = CS_HREDRAW | CS_VREDRAW;
  316.  
  317. wndclass.style can be set to include one or more of any of 11
  318. styles. CS_HREDRAW asks Windows to redraw the client area of this
  319. window when the horizontal size of the window changes; CS_VREDRAW
  320. asks for a redraw when the vertical size changes.
  321.  
  322.    wndclass.lpfnWndProc = WndProc;
  323.  
  324. lpfnWndProc contains a pointer to the message-handling function,
  325. called the window procedure. This function is often named WndProc.
  326. The window procedure function handles incoming messages. Windows
  327. calls WinMain when it starts your application, but thereafter it
  328. calls the window procedure. (Note that there may be more than one
  329. window procedure; for example, a window procedure can exist for
  330. each different window class and Windows will call the appropriate
  331. one whenever it wants to deliver a message to the corresponding
  332. window.) When Windows gets a messages, either from the environment,
  333. from another application, or from your application, it calls the
  334. window procedure function and passes the message to that function.
  335.  
  336. The window procedure WndProc handles all messages to any window
  337. created from this window class.
  338.  
  339.    wndclass.cbClsExtra = 0;
  340.  
  341. wndclass.cbClsExtra specifies the number of extra bytes Windows is
  342. to maintain in the window class (cb stands for count of bytes).
  343. This allows you to store application-specific information in the
  344. window class to use for your own purposes. None are reserved in
  345. this window class.
  346.  
  347.    wndclass.cbWndExtra = 0;
  348.  
  349. wndclass.cbWndExtra specifies the number of extra bytes Windows is
  350. to maintain in the window structure. None are reserved in this
  351. window structure.
  352.  
  353.  
  354.  
  355.  
  356. Chapter 1 Page 6
  357.  
  358.  
  359.    wndclass.hInstance = hInstance;
  360.  
  361. wndclass.hInstance normally takes the hInstance value of the
  362. application (an argument passed by Windows to WinMain).
  363.  
  364.    wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
  365.  
  366. wndclass.hIcon is set to the handle for an icon. In this case,
  367. LoadIcon is called to obtain the handle of the predefined icon
  368. IDI_APPLICATION. If you had defined an icon resource for your
  369. application, and compiled the resources to the application, you
  370. would call LoadIcon with the current hInstance and with a pointer
  371. to the icon resource name.
  372.  
  373.    wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
  374.  
  375. LoadCursor returns a handle to a cursor type. IDC_ARROW is one of
  376. the predefined mouse cursors.
  377.  
  378.    wndclass.hbrBackground = GetStockObject( WHITE_BRUSH );
  379.  
  380. This statement sets wndclass.hbrBackground to a white brush. The
  381. background of all windows based on this window class will be
  382. painted according to the handle of the brush set in
  383. wndclass.hbrBackground; in this case, solid white.
  384.  
  385.    wndclass.lpszMenuName = NULL;
  386.  
  387. This statement sets wndclass.lpszMenuName to the name of the
  388. application's menu. Since this application has no menu, it is set
  389. to NULL.
  390.  
  391.    wndclass.lpszClassName = szClassName;
  392.  
  393. The wndclass.lpszClassName member gives the window class itself a
  394. name. This one is named "Hello, World!"
  395.  
  396. Once the wndclass structure is filled, RegisterClass must be called
  397. to register the window class with Windows. If another window class
  398. of the same name already exists, RegisterClass fails.
  399.  
  400.     hWnd = CreateWindow( szClassName,  // name of window class
  401.         szClassName,                   // caption for this window
  402.         WS_OVERLAPPEDWINDOW,           // type of window to create
  403.         CW_USEDEFAULT,                 // starting upper-left corner (x)
  404.         0,                             // starting upper-left corner (y)
  405.         CW_USEDEFAULT,                 // starting width
  406.         0,                             // starting height
  407.         NULL,                          // handle of parent window
  408.         NULL,                          // handle of window menu
  409.  
  410.  
  411.  
  412. Chapter 1 Page 7
  413.  
  414.  
  415.         hInstance,                     // application instance handle
  416.         NULL );                        // create parameters
  417.     if ( !hWnd )
  418.        return FALSE;
  419.  
  420. After the window class is registered, a specific instance of that
  421. window can be created with CreateWindow. CreateWindow takes 11
  422. arguments; each argument is briefly described in the comment
  423. following it. See the online help for CreateWindow for more
  424. information.
  425.  
  426.     ShowWindow( hWnd, nCmdShow );
  427.     UpdateWindow( hWnd );
  428.  
  429. ShowWindow displays the window according to the value of nCmdShow.
  430. (nCmdShow is one of the parameters passed to WinMain). If nCmdShow
  431. is equal to SW_SHOWNORMAL, the window is displayed normally; if
  432. nCmdShow is equal to SW_SHOWMINNOACTIVE, then the window is
  433. displayed as an icon. If the value of nCmdShow tells ShowWindow to
  434. display the window normally, then the client area of the window is
  435. erased with the background brush specified by the window class's
  436. hbrBackground (in this case white). This sets the stage for
  437. UpdateWindow, which causes the client area to be repainted.
  438.  
  439. UpdateWindow sends a message to that window's window procedure. The
  440. window procedure, you may recall, is specified with the
  441. lpfnWndProc; for this window class it is WndProc. WndProc handles
  442. incoming messages, reacting according to the message sent.
  443.  
  444.     while( GetMessage( &msg, NULL, 0, 0 ) )
  445.     {
  446.       TranslateMessage( &msg );
  447.       DispatchMessage( &msg );
  448.     }
  449.     return msg.wParam;
  450.   }
  451.  
  452. These four lines of code comprise the main message loop, which is
  453. the heart of a Windows application. Windows keeps a message queue
  454. for each running application. When a key or a mouse button is
  455. pressed (input events), Windows translates the input event into a
  456. message, then places the message in that program's message queue.
  457. The application retrieves these messages with the message loop.
  458.  
  459. GetMessage gives Windows a far pointer to msg, Windows fills the
  460. fields of msg with the next message from the queue. The other three
  461. parameters of GetMessage tell Windows what messages to get for
  462. which windows; values of NULL, 0, and 0 indicate that GetMessage
  463. wants all messages for all windows created by this application.
  464. GetMessage examines msg and returns zero only if the message member
  465.  
  466.  
  467.  
  468.  
  469. Chapter 1 Page 8
  470.  
  471.  
  472. of msg is WM_QUIT. When WM_QUIT is encountered, the program
  473. terminates, returning the value of msg.wParam.
  474.  
  475. TranslateMessage sends the message back to Windows for translation,
  476. and DispatchMessage sends the message to Windows yet again. When
  477. Windows receives  the message from DispatchMessage, it dispatches
  478. the message to the window's window procedure. In this application,
  479. Windows will dispatch the message to WndProc.
  480.  
  481. Before we start examining WndProc, we should take a look at this
  482. ubiquitous msg message.  msg was declared to be of type MSG, a
  483. structure defined in windows.h as follows:
  484.  
  485.   /* Message structure */
  486.   typedef struct tagMSG
  487.   {
  488.     HWND      hwnd;        // handle to the current window
  489.     WORD      message;     // a number identifying the message --
  490.                            //  all window message identifiers are
  491.                            //  defined in windows.h (look for the WM_
  492.                            //  prefix)
  493.     WORD      wParam;      // 16-bit parameter specific to message
  494.     LONG      lParam;      // 32-bit parameter specific to message
  495.     DWORD     time;        // time message was placed in queue
  496.     POINT     pt;          // x, y mouse coordinates at message
  497.   time
  498.   } MSG;
  499.  
  500. Note that the window procedure takes as arguments the first four
  501. fields of the message structure.
  502.  
  503.   long FAR PASCAL WndProc( HWND hWnd, WORD message,
  504.                            WORD wParam, LONG lParam )
  505.   {
  506.     switch (message)
  507.     {
  508.       case WM_PAINT:
  509.         Paint( hWnd );           // Paint is defined later in program
  510.           break;
  511.  
  512.       case WM_DESTROY:
  513.         PostQuitMessage( 0 );    // Sends WM_QUIT message to application
  514.           break;
  515.  
  516.       default:                     // called for all other messages
  517.         return DefWindowProc( hWnd, message, wParam, lParam );
  518.     }
  519.  
  520.     return 0L;
  521.  
  522.   }
  523.  
  524.  
  525. Chapter 1 Page 9
  526.  
  527.  
  528.  
  529.  
  530. The window procedure simply examines the value of message and calls
  531. other functions based on what it finds. Paint is called if a
  532. message was received to repaint the window. PostQuitMessage sends
  533. the WM_QUIT message to the application; WM_QUIT is handled in the
  534. message loop of WinMain. The argument to PostQuitMessage is the
  535. application's exit code; it becomes the wParam parameter of the
  536. WM_QUIT message.
  537.  
  538. DefWindowProc is called for all messages other than WM_PAINT and
  539. WM_QUIT. DefWindowProc, supplied by Windows, provides default
  540. processing.
  541.  
  542.   void Paint( HWND hWnd )
  543.   {
  544.     PAINTSTRUCT ps;
  545.     RECT rect;
  546.     int strsize = strlen( szClassName );
  547.  
  548.     BeginPaint( hWnd, &ps );
  549.     SetTextAlign( ps.hDC, TA_CENTER );
  550.     GetClientRect( hWnd, (LPRECT) &rect );
  551.     TextOut( ps.hDC,
  552.              rect.right / 2, rect.bottom / 2,
  553.              szClassName,
  554.              strsize );
  555.     EndPaint( hWnd, &ps );
  556.   }
  557.  
  558. BeginPaint is called with the current window handle hWnd and passed
  559. the address of the structure ps. BeginPaint fills ps and returns a
  560. handle to something called a device context. A device context is
  561. actually a data structure that specifies display device attributes
  562. to Windows Graphics Device Interface (GDI) functions.
  563.  
  564. PAINTSTRUCT is defined as follows.
  565.  
  566.   typedef struct tagPAINTSTRUCT
  567.   {
  568.     HDC  hDC;                // handle to a device context
  569.     BOOL fErase;             // set to True if Windows has erased
  570.                              //  background of invalid rectangle
  571.     RECT rcPaint;            // invalid rectangle -- Windows knows
  572.                              //  which part of the client area needs
  573.                              //  to be repainted
  574.     BOOL fRestore;           // used by Windows
  575.     BOOL fIncUpdate;         // used by Windows
  576.     BYTE rgbReserved[16];    // used by Windows
  577.   } PAINTSTRUCT;
  578.  
  579.  
  580.  
  581.  
  582. Chapter 1 Page 10
  583.  
  584.  
  585. GetClientRect sets the four fields of rect to the dimensions of the
  586. client area of the window. left and top are always set to 0; right
  587. and bottom are set to the width and height in pixels. For this
  588. example, the rectangle defining the client area was necessary only
  589. to calculate the center of that area for the call to TextOut.
  590.  
  591. When SetTextAlign is called with the TA_CENTER flag, a subsequent
  592. call to TextOut changes the meaning of the coordinates passed to
  593. TextOut (the second and third parameters) such that those
  594. coordinates are used as the center of the string to be printed, not
  595. the beginning.
  596.  
  597. TextOut prints strsize characters of string szClassName, on the
  598. device indicated by ps.hDC, using the two coordinates rect.right/2
  599. and rect.bottom/2.
  600.  
  601. EndPaint marks the ending of painting in this window by releasing
  602. the handle to the device context and validating the window.
  603.  
  604.  
  605. The WHELLO application
  606.  
  607. The main source file for the WHELLO application is WHELLO.CPP. We
  608. discuss each part of this program in the following sections.
  609.  
  610.  
  611.   Directives and initialization
  612. The following segment of code comments on the contents of the file,
  613. necessary header files, and provides the function prototypes.
  614.  
  615.   //  Windows HELLO WORLD - C++ Version 1
  616.  
  617.   #include <windows.h>
  618.  
  619.   #include <stdlib.h>
  620.   #include <string.h>
  621.  
  622.   long FAR PASCAL _export WndProc( HWND hWnd, WORD iMessage,
  623.                                    WORD wParam, LONG lParam );
  624.  
  625. Three things are triggered by flagging a function with the _export
  626. keyword: the function is compiled as exportable, the function is
  627. linked as an exported function, and the function name is not
  628. mangled.
  629.  
  630. Any functions exported by your Windows program and not flagged with
  631. the _export keyword would need to be exported by their C++ mangled
  632. name (not a fun job!). If you choose to export functions only by
  633. listing them in the module definition file, then you will need to
  634. declare those functions extern "C", or use the mangled name in the
  635. module definition file. Chapter 3 in the User's Guide discusses the
  636. various methods of exporting functions in greater detail.
  637.  
  638.  
  639.  
  640. Chapter 1 Page 11
  641.  
  642.  
  643. Borland has licensed windows.h from Microsoft; however, our version
  644. of this file differs from Microsoft's in a few ways. In particular,
  645. you don't have to write
  646.  
  647.   extern "C" {
  648.   #include <windows.h>
  649.   }
  650.  
  651. because the appropriate extern statements appear inside the header
  652. file.
  653.  
  654.  
  655.     Defining the
  656.       Main class
  657.       public:
  658.        static HANDLE hInstance;
  659.        static HANDLE hPrevInstance;
  660.        static int nCmdShow;
  661.        static int MessageLoop( void );
  662.   };
  663.   HANDLE Main::hInstance = 0;
  664.   HANDLE Main::hPrevInstance = 0;
  665.   int Main::nCmdShow = 0;
  666.  
  667.   int Main::MessageLoop( void )
  668.   {
  669.       MSG msg;
  670.  
  671.       while( GetMessage( &msg, NULL, 0, 0 ) )
  672.       {
  673.           TranslateMessage( &msg );
  674.           DispatchMessage( &msg );
  675.       }
  676.       return msg.wParam;
  677.   }
  678.  
  679. The class Main serves as a wrapper around WinMain's arguments. Main
  680. contains static data members that correspond to the arguments of
  681. WinMain. We define these as static members since we only need one
  682. copy of each of these values. Because they are static, we can use
  683. these values without defining an object of class Main. Declaring
  684. these as public members enables access to these values wherever we
  685. want. Alternatively, we could have made these data members private
  686. or protected, then declared the functions or classes that need the
  687. data members as friends of the class Main. (If the WinMain argument
  688. lpszCmdLine is used in a program, you might also define and use a
  689. static member of type LPSTR in a similar fashion.)
  690.  
  691. Since the Windows message loop is more or less the same in every
  692. Windows application, placing it in the static member function
  693. Main::MessageLoop shortens WinMain without obscuring any important
  694. details. For instance, we could later write a program that uses a
  695.  
  696.  
  697.  
  698. Chapter 1 Page 12
  699.  
  700.  
  701. keyboard accelerator table, requiring a function call to
  702. "TranslateAccelerator" to be added to MessageLoop.
  703.  
  704. We made this a static member function because it does not use any
  705. (non-static) member data of the class.
  706.  
  707.  
  708. Defining Windows
  709.          classes
  710.   {
  711.       protected:
  712.           HWND hWnd;
  713.       public:
  714.           // Provide (read) access to the window's handle in case
  715.           // it is needed elsewhere.
  716.           HWND GetHandle() { return hWnd; }
  717.  
  718.           BOOL Show( int nCmdShow ) { return ShowWindow( hWnd,
  719.   nCmdShow ); }
  720.           void Update() { UpdateWindow( hWnd ); }
  721.           // Pure virtual function makes Window an abstract class.
  722.           virtual long WndProc( WORD iMessage, WORD wParam,
  723.       LONG lParam ) = 0;
  724.   };
  725.  
  726. This example also takes advantage of the C++ notion of inheritance.
  727. Each window has a handle and there is a common set of actions (such
  728. as Show and Update) that are performed upon each window. Therefore,
  729. it makes sense to put these common actions and common data in a
  730. class Window from which we derive our MainWindow. In general, if we
  731. only add members to Window that may be used by typically any
  732. window, we might derive all our window classes from Window. (By
  733. "class", we usually mean "C++ class", though the distinctions
  734. between C++ and Windows classes is fuzzy since our C++ window
  735. classes should parallel and contain the notions related to Windows
  736. window classes.)
  737.  
  738.  
  739.   Defining the base class
  740.  
  741. Instead of calling ShowWindow and UpdateWindow, we now call the member
  742. functions Show and Update. These in turn call the associated
  743. Windows functions.
  744.  
  745. Using member functions in this fashion has the advantages of
  746. simplifying the code by requiring fewer arguments in each function
  747. call. Instead of requiring a handle to a window as an argument in
  748. order to know upon which window to perform the action, the window
  749. (an object of class MainWindow) is identified by the object making
  750. the function call.
  751.  
  752. For example, if we created an object "MainWnd" of class MainWindow
  753. by
  754.  
  755.  
  756.  
  757. Chapter 1 Page 13
  758.  
  759.  
  760.   MainWindow MainWnd;
  761.  
  762. then we might make a call such as
  763.  
  764.   MainWnd.Show( nCmdShow );
  765.  
  766. (think of this as sending the message Show to the object MainWnd;
  767. in C++, we can think of invoking a class's member function for a
  768. particular object as sending that object a message).
  769.  
  770. The handle is member data of the object so it need not appear as an
  771. argument to the method.
  772.  
  773. (In the example WHELLO.CPP, there is a call to the member function
  774. Show in the constructor of MainWindow). Also, Show and Update are
  775. inline member functions of the class MainWindow, and hence, no
  776. extra code is generated in WinMain by defining these new functions.
  777.  
  778.  
  779. Defining the derived class
  780.  
  781. class MainWindow : public Window
  782. {
  783.       private:
  784.           static char szClassName[14];
  785.           // Helper function used by Paint function; it is used as a
  786.           // callback function by LineDDA.
  787.           static void FAR PASCAL LineFunc( int X, int Y, LPSTR lpData );
  788.       public:
  789.           // Register the class only AFTER WinMain assigns appropriate
  790.           // values to static members of Main and only if no previous
  791.           // instances of the program exist (a previous instance would
  792.           // have already performed the registration).
  793.           static void Register( void )
  794.           {
  795.               WNDCLASS wndclass;   // Structure used to register
  796.                                    //   Windows class.
  797.               wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  798.               wndclass.lpfnWndProc   = ::WndProc;
  799.               wndclass.cbClsExtra    = 0;
  800.               // Reserve extra bytes for each instance of the window;
  801.               // we will use these bytes to store a pointer to the C++
  802.               // (MainWindow) object corresponding to the window.
  803.               // The size of a 'this' pointer depends on the memory model.
  804.               wndclass.cbWndExtra    = sizeof( MainWindow * );
  805.  
  806.  
  807.  
  808. Chapter 1 Page 14
  809.  
  810.  
  811.               wndclass.hInstance     = Main::hInstance;
  812.               wndclass.hIcon         = LoadIcon( Main::hInstance, "whello" );
  813.               wndclass.hCursor       = LoadCursor( NULL, IDC_ARROW );
  814.               wndclass.hbrBackground = GetStockObject( WHITE_BRUSH );
  815.               wndclass.lpszMenuName  = NULL;
  816.               wndclass.lpszClassName = szClassName;
  817.  
  818.               if ( ! RegisterClass( &wndclass ) )
  819.                   exit( FALSE );
  820.           }
  821.  
  822. Windows needs to have information on the different window classes
  823. used in your program. You pass this information along by filling in
  824. a struct of type WNDCLASS (defined in windows.h) and calling
  825. RegisterClass. You should only do this once for each Windows class;
  826. in particular, a previous instance of the same program will have
  827. already registered the classes with Windows. This can be
  828. accomplished by including a static member function (Register in
  829. WHELLO1.CPP) to fill the fields of a WNDCLASS object in the C++
  830. class defined to represent a Windows class. This function is then
  831. called in WinMain to handle Windows class registration if
  832. necessary.
  833.  
  834. Because the window procedure may be responsible for many different
  835. actions, what we would like to do is call an appropriate member
  836. function for each Windows message. Unfortunately, this is not as
  837. straightforward as we would like, primarily because the function
  838. WndProc is passed a handle to identify a window when a pointer to
  839. the C++ object corresponding to the window is what we need to call
  840. an object's member functions.
  841.  
  842. One way to handle this is to use the extra bytes available in the
  843. window class. We filled in a WNDCLASS structure defining the
  844. attributes of the window class when we registered it with Windows.
  845. One field, cbWndExtra, tells Windows to allocate that number of
  846. extra bytes for each window instance. If you think of a window
  847. class as analogous to a C++ class, then window extra bytes are
  848. analogous to a class's member data (just as a class's extra bytes,
  849. defined by the cbClsExtra field of WNDCLASS, are analogous to a
  850. class's static member data). In our constructor of MainWindow, we
  851. make a call to CreateWindow with the last argument set to this.
  852.  
  853.  
  854.     Creating andction of code should be read with attention to the
  855.     painting theplanation of the callback function, following in a
  856.           windowxplains some of the techniques employed here.
  857.  
  858.           // Do not create unless previously registered.
  859.           MainWindow( void )
  860.  
  861.  
  862.  
  863. Chapter 1 Page 15
  864.  
  865.  
  866.           {
  867.               // Pass 'this' pointer in lpParam of CreateWindow().
  868.               hWnd = CreateWindow( szClassName,
  869.                   szClassName,
  870.                   WS_OVERLAPPEDWINDOW,
  871.                   CW_USEDEFAULT,
  872.                   0,
  873.                   CW_USEDEFAULT,
  874.                   0,
  875.                   NULL,
  876.                   NULL,
  877.                   Main::hInstance,
  878.                   (LPSTR) this );
  879.               if ( ! hWnd )
  880.                   exit( FALSE );
  881.  
  882.               Show( Main::nCmdShow );
  883.               Update();
  884.           }
  885.           long WndProc( WORD iMessage, WORD wParam, LONG lParam );
  886.  
  887.           // Print a message in the client rectangle.
  888.           void Paint( void );
  889.           // Struct used by Paint to pass information to callback function
  890.           // used by LineDDA
  891.           struct LINEFUNCDATA
  892.           {
  893.               HDC hDC;
  894.               char FAR *LineMessage;
  895.               int MessageLength;
  896.               LINEFUNCDATA( char *Message )
  897.               {
  898.                   hDC = 0;
  899.                   MessageLength = strlen( Message );
  900.                   LineMessage = new char far [MessageLength+1];
  901.                   lstrcpy( LineMessage, Message );
  902.               };
  903.               ~LINEFUNCDATA( void ) { delete LineMessage; }
  904.           };
  905.   };
  906.   char MainWindow::szClassName[] = "Hello, World!";
  907.  
  908.   void MainWindow::Paint( void )
  909.   {
  910.       PAINTSTRUCT ps;
  911.       RECT rect;
  912.       FARPROC lpLineFunc;
  913.       LINEFUNCDATA LineFuncData( "Borland C++ welcomes you to the
  914.   Wonderful World of Windows Programming!" );
  915.  
  916.  
  917.  
  918.  
  919.  
  920. Chapter 1 Page 16
  921.  
  922.  
  923.       lpLineFunc = MakeProcInstance( (FARPROC)
  924.   MainWindow::LineFunc, Main::hInstance );
  925.       BeginPaint( hWnd, &ps );
  926.       GetClientRect( hWnd, (LPRECT) &rect );
  927.       LineFuncData.hDC = ps.hdc;
  928.       SetTextAlign( ps.hdc, TA_BOTTOM );
  929.       LineDDA( 0, 0, 0,
  930.                rect.bottom / 2, lpLineFunc, (LPSTR) &LineFuncData
  931.   );
  932.       EndPaint( hWnd, &ps );
  933.       FreeProcInstance( lpLineFunc );
  934.   }
  935.  
  936.  
  937.   void FAR PASCAL MainWindow::LineFunc( int X, int Y, LPSTR lpData
  938.   )
  939.   {
  940.       LINEFUNCDATA FAR * lpLineFuncData = (LINEFUNCDATA FAR *) lpData;
  941.       TextOut( lpLineFuncData->hDC, X, Y,
  942.                lpLineFuncData->LineMessage, lpLineFuncData->MessageLength );
  943.   }
  944.  
  945.   long MainWindow::WndProc( WORD iMessage, WORD wParam, LONG lParam
  946.   )
  947.   {
  948.       switch (iMessage)
  949.       {
  950.           case WM_CREATE:
  951.               break;
  952.           case WM_PAINT:
  953.               Paint();
  954.               break;
  955.           case WM_DESTROY:
  956.               PostQuitMessage( 0 );
  957.               break;
  958.           default:
  959.               return DefWindowProc( hWnd, iMessage, wParam, lParam );
  960.       }
  961.   }
  962.  
  963. Pointers to Windows
  964.  
  965. This section gives an example of how to handle pointers to Windows.
  966.  
  967.   // If data pointers are near pointers
  968.   #if defined(__SMALL__) || defined(__MEDIUM__)
  969.   inline Window *GetPointer( HWND hWnd )
  970.   {
  971.       return (Window *) GetWindowWord( hWnd, 0 );
  972.   }
  973.   inline void SetPointer( HWND hWnd, Window *pWindow )
  974.  
  975.  
  976.  
  977. Chapter 1 Page 17
  978.  
  979.  
  980.   {
  981.       SetWindowWord( hWnd, 0, (WORD) pWindow );
  982.   }
  983.  
  984.   // else pointers are far
  985.   #elif defined(__LARGE__) || defined(__COMPACT__)
  986.   inline Window *GetPointer( HWND hWnd )
  987.   {
  988.       return (Window *) GetWindowLong( hWnd, 0 );
  989.   }
  990.   inline void SetPointer( HWND hWnd, Window *pWindow )
  991.   {
  992.       SetWindowLong( hWnd, 0, (LONG) pWindow );
  993.   }
  994.  
  995.   #else
  996.       #error Choose another memory model!
  997.   #endif
  998.  
  999.  
  1000.   The callback function
  1001.  
  1002. The call to CreateWindow in the constructor of MainWindow sends
  1003. several messages to the global WndProc, one of which is WM_CREATE.
  1004. The lparam passed with WM_CREATE is actually a far pointer to a
  1005. CREATESTRUCT (defined in windows.h), and the lpCreateParams field
  1006. of this struct is filled with the last argument set in the call to
  1007. CreateWindow. Thus, when the global WndProc processes the WM_CREATE
  1008. message, this value is set in the extra bytes of the window
  1009. specified by hWnd.
  1010.  
  1011. When the global WndProc is called thereafter, we can use the handle
  1012. to the window to extract this pointer from our window object, and
  1013. we're then all set to use our associated C++ object; for example,
  1014. the WM_PAINT message is converted into a call to the MainWindow
  1015. member function Paint(). We do have to be a careful about messages
  1016. passed to the WndProc before this pointer has been correctly set
  1017. (ie, messages that may be sent before WM_CREATE has been
  1018. processed); these messages either should be passed to DefWindowProc
  1019. or should be handled without using the pointer.
  1020.  
  1021. The pointer pWindow will have an invalid value if the WM_CREATE
  1022. message has not yet been processed. The pointer pWindow will have
  1023. an invalid value if the WM_CREATE message has not yet been
  1024. processed. The program responds to the WM_CREATE message by setting
  1025. the extra bytes to be a pointer to the C++ object corresponding to
  1026. the Window identified by hWnd. The messages that precede WM_CREATE
  1027. must be processed without using pWindow so we pass them to
  1028. DefWindowProc. How do we know if the pointer pWindow is invalid? We
  1029. know because Windows allocates the window extra bytes using
  1030. LocalAlloc which zero-initializes memory; thus, pWindow will have a
  1031. value of zero before we set the window extra bytes to the this
  1032. pointer. Keep in mind this caveat: the fact that LocalAlloc will
  1033.  
  1034.  
  1035.  
  1036.  
  1037. Chapter 1 Page 18
  1038.  
  1039.  
  1040. zero-initialize the window extra bytes is not documented in the
  1041. SDK; therefore, it could change in the future.
  1042.  
  1043.   long FAR PASCAL _export WndProc( HWND hWnd, WORD iMessage,
  1044.                                    WORD wParam, LONG lParam )
  1045.   {
  1046.       // Pointer to the (C++ object that is the) window.
  1047.       Window *pWindow = GetPointer( hWnd );
  1048.  
  1049.       if ( pWindow == 0 )
  1050.       {
  1051.           if ( iMessage == WM_CREATE )
  1052.           {
  1053.               LPCREATESTRUCT lpcs;
  1054.  
  1055.               lpcs = (LPCREATESTRUCT) lParam;
  1056.               pWindow = (Window *) lpcs->lpCreateParams;
  1057.  
  1058.               // Store a pointer to this object in the window's extra bytes;
  1059.               // this will enable to access this object (and its member
  1060.               // functions) in WndProc where we are
  1061.               // given only a handle to identify the window.
  1062.               SetPointer( hWnd, pWindow );
  1063.               // Now let the object perform whatever
  1064.               // initialization it needs for WM_CREATE in its own
  1065.               // WndProc.
  1066.               return pWindow->WndProc( iMessage, wParam, lParam );
  1067.           }
  1068.           else
  1069.               return DefWindowProc( hWnd, iMessage, wParam, lParam
  1070.   );
  1071.       }
  1072.       else
  1073.           return pWindow->WndProc( iMessage, wParam, lParam );
  1074.   }
  1075.  
  1076.  
  1077.   Putting it all together with WinMain
  1078.  
  1079. Note that the WinMain function in the C++ version of "Hello, World"
  1080. shorter than (though equivalent to) the C version because such
  1081. actions as the registration of the window class, the creation of
  1082. the window, and the message loop have been moved into member
  1083. functions of various C++ classes.
  1084.  
  1085.   // Turn off warning: Parameter 'lpszCmdLine' is never used in function
  1086.  
  1087.   WinMain(unsigned int,unsigned int,char far*,int)
  1088.   #pragma argsused
  1089.  
  1090.   // Turn off warning: 'MainWnd' is assigned a value that is never
  1091.   // used in function
  1092.  
  1093.   WinMain(unsigned int,unsigned int,char far*,int)
  1094.   #pragma option -w-aus
  1095.  
  1096.  
  1097.  
  1098. Chapter 1 Page 19
  1099.  
  1100.  
  1101.   int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance, LPSTR
  1102.   lpszCmdLine, int nCmdShow )
  1103.   {
  1104.       Main::hInstance = hInstance;
  1105.       Main::hPrevInstance = hPrevInstance;
  1106.       Main::nCmdShow = nCmdShow;
  1107.  
  1108.       // A Windows class should be registered with Windows before
  1109.       // any windows of that type are created. Register here all
  1110.       // Windows classes that will be used in the program.
  1111.       // Windows classes should not be registered if an instance of
  1112.       // the program is already running.
  1113.       if ( ! Main::hPrevInstance ) {
  1114.           MainWindow::Register();
  1115.       }
  1116.  
  1117.       MainWindow MainWnd;
  1118.  
  1119.       return Main::MessageLoop();
  1120.   }
  1121.  
  1122.