Creating an API Application


The easiest way to begin creating an API application in Delphi is to create a console application project and then make the following changes to the generated source code:

  1. Remove the $APPTYPE compiler directive.

  2. Change the uses list from "uses SysUtils;" to "uses Windows;".

Now that you've made these changes, you are ready to create your first API application. The simplest API application you can create is an application that displays a message box to the user. To display a message box in an API application, you should use the MessageBox function declared in the Windows unit:

function MessageBox(hWnd: HWND; lpText,    lpCaption: PChar; uType: UINT): Integer; stdcall;

The following listing shows how to display a message box using the MessageBox function. Figure 23-1 shows how the API application looks at run time.

image from book
Figure 23-1: Our first API application

Listing 23-1: A very simple API application

image from book
program Generic; uses   Windows; begin   MessageBox(0, 'Hello from an API application!', 'Hello', MB_OK); end.
image from book

Although a real API application is more complex than the one we just created, this application allows us to note several differences between VCL and API applications:

  • The Designer Surface is not available when you're creating API applications, so you have to do everything in code.

  • API applications require you to write a lot more code than VCL applications, as you'll see shortly.

  • If you look at the produced executable, you'll notice that API applications are smaller in size than their VCL counterparts but immensely harder to write (an API application that displays an empty window compiles to 15 KB, while a VCL application that does the same compiles to 350 KB).

Now it's time to create a real API application by doing the following:

  1. Create the main window.

  2. Create a procedure that receives and processes all messages sent to the window.

  3. Enter the message loop.

Windows are, at the API level, created with a call to the CreateWindow or the CreateWindowEx function. Window creation with these functions is very simple if you're creating child windows like buttons, list boxes, or other, already registered, window classes. But when you're creating the main window, you have to do the following:

  1. Create the window's class by defining the window's properties like icon, background color, menu, etc.

  2. Register the window's class since the CreateWindow function only works with registered classes.

  3. Finally, call the CreateWindow or the CreateWindowEx function to create the window.

Defining and Registering the Window Class

Creating a window class is as easy as filling in a WNDCLASS record. The WNDCLASS record is declared in the Windows unit and looks like this:

WNDCLASSA = tagWNDCLASSA; tagWNDCLASSA = packed record     style: UINT;     lpfnWndProc: TFNWndProc;     cbClsExtra: Integer;     cbWndExtra: Integer;     hInstance: HINST;     hIcon: HICON;     hCursor: HCURSOR;     hbrBackground: HBRUSH;     lpszMenuName: PAnsiChar;     lpszClassName: PAnsiChar;   end;

Although the WNDCLASS record has a large number of fields, only two are extremely important: the lpfnWndProc and the lpszClassName fields. The lpfnWndProc field must point to the window procedure (the procedure that receives and processes messages) and the lpszClassName field is the name under which the window class is to be registered.

The style field is used to define the class's style. The constants that can be assigned to this field start with CS_. In the Windows unit, there are several of these constants, but you'll most often want to use CS_VREDRAW and CS_HREDRAW, which cause the window to be repainted when its size is updated. There are also three more pretty interesting, and sometimes useful, constants: CS_DBLCLKS, CS_NOCLOSE, and CS_DROPSHADOW. The CS_DBLCLKS constant enables the window to process double clicks; the CS_NOCLOSE constant disables the Close button on the window's title bar, the Close option on the window's menu, and the Alt+F4 shortcut; and the CS_DROPSHADOW constant displays the shadow under the window (note that the application will fail to run on Windows 98 if you use the CS_DROPSHADOW style). If you want to assign several styles to the window class, combine them with the Boolean or operator. Here's the code so far:

program Generic; uses   Windows, Messages; var   wc: WNDCLASS; begin   wc.style := CS_VREDRAW or CS_HREDRAW or CS_DROPSHADOW; end.

The lpfnWndProc field is extremely important as it links the window with the window procedure that processes its messages. The window procedure is actually a function that must accept four parameters and return an integer result. The parameters are: the handle to the window, the message, and the wParam and LParam parameters of the message.

Here's the empty skeleton of the window procedure and the line of code that assigns the window procedure to the lpfnWndProc field of the window class:

function WindowProc(Wnd: HWND; Msg, WParam, LParam: LongInt): LongInt; stdcall; begin end; wc.lpfnWndProc := @WindowProc;

The next two fields can be used to allocate extra bytes in the window class. Since this is rarely done, feel free to set both of these fields to 0:

wc.cbClsExtra := 0;  wc.cbWndExtra := 0;

The hInstance field needs to point to the application instance. You only have to assign the global HInstance variable to this field and you're done:

wc.hInstance := HInstance;

The hIcon field specifies the window's icon. If you set this value to 0, the operating system displays the default icon. You can also assign a custom or a standard icon to the window by using the LoadIcon function:

function LoadIcon(hInstance: HINST; lpIconName: PChar): HICON; stdcall;

To load a standard system icon, pass 0 as the hInstance parameter and pass one of the IDI_ constants as the lpIconName parameter:

IDI_APPLICATION = MakeIntResource(32512); IDI_HAND = MakeIntResource(32513); IDI_QUESTION = MakeIntResource(32514); IDI_EXCLAMATION = MakeIntResource(32515); IDI_ASTERISK = MakeIntResource(32516); IDI_WINLOGO = MakeIntResource(32517); IDI_WARNING = IDI_EXCLAMATION; IDI_ERROR = IDI_HAND; IDI_INFORMATION = IDI_ASTERISK; wc.hIcon := LoadIcon(0, IDI_INFORMATION);

The hCursor field defines the mouse cursor that will be displayed when the mouse is over the window. To assign a standard cursor to the window, you need to call the LoadCursor function, pass 0 as the hInstance parameter, and pass one of the IDC_ constants as the second parameter. The following line loads the standard arrow cursor:

wc.hCursor := LoadCursor(0, IDC_ARROW);

The hbrBackground field is a handle to a brush that is used to fill the client area of the window (the background color). If you want to use system colors, you can use the COLOR_ constants like COLOR_BTNFACE or COLOR_DESKTOP. If you use the COLOR_ constants, you have to add 1 to the selected color:

wc.hbrBackground := COLOR_BTNFACE + 1;

The operating system also has several stock brushes that you can use here. To do so, you have to call the GetStockObject function and pass the appropriate constant, such as WHITE_BRUSH or BLACK_BRUSH:

wc.hbrBackground := GetStockObject(BLACK_BRUSH);

The lpszMenuName field can be used to load a main menu from a resource file. Since we haven't yet created a resource file that contains a menu, you can set the lpszMenuName field to nil:

wc.lpszMenuName := nil;

Finally, to completely define the window class, you have to assign a class name to the lpszClassName field. Because we're going to use the same class name in the call to CreateWindow, it's best to create a constant for the class name. The class name can be anything you like, but you should at least try to give it a name that's somewhat related to the application you're creating:

const  MY_CLASS = 'DelphiAPIWindow';  ...  wc.lpszClassName := MY_CLASS;

Now that you've defined the window class, you have to register it by passing it to the RegisterClass function. If the RegisterClass function fails to register the class, it returns 0. So, before creating the main window based on your class, you should check the RegisterClass function's return value and terminate the application if the return value is 0:

if RegisterClass(wc) = 0 then Exit;

Creating and Displaying the Main Window

To create the window that you've described using the WNDCLASS record, you have to call the CreateWindow function. If you want to create windows with the CreateWindowEx function, you have to use the WNDCLASSEX record.

Here's the declaration of the CreateWindow function:

function CreateWindow(lpClassName: PChar; lpWindowName: PChar;   dwStyle: DWORD; X, Y, nWidth, nHeight: Integer; hWndParent: HWND;   hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND;

And here's what you have to do when you're calling the CreateWindow function to create the window:

  1. Pass the name of the registered class as the lpClassName parameter.

  2. Pass the window's caption as the lpWindowName parameter.

  3. Define the window style by passing one of the WS_ constants as the dwStyle parameter (for instance, the WS_OVERLAPPEDWINDOW constant creates a standard resizable window).

  4. Set the location and the size of the window with the X, Y, nWidth, and nHeight parameters. (If you want, you can pass CW_USEDEFAULT to all four parameters to let the OS position and resize your window.)

  5. Pass the handle of the parent window as the hWndParent parameter. (If you're creating the main window, pass 0.)

  6. Pass 0 as the hMenu parameter since the window has no menu.

  7. Pass the global HInstance variable as the hInstance parameter.

  8. Pass nil as the lpParam parameter.

After a call to the CreateWindow function, you still have to call two functions to display the window: ShowWindow and UpdateWindow. The ShowWindow function is used to specify how the window is to be shown, and the UpdateWindow function updates the client area of the window and forces it to display itself:

 MainWnd: HWND;  ... MainWnd := CreateWindow(MY_CLASS, 'Win32 API Application',    WS_OVERLAPPEDWINDOW, Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),    Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT), 0, 0, HInstance, nil);  ShowWindow(MainWnd, SW_SHOWNORMAL);  UpdateWindow(MainWnd);

If you run the application now, you'll see the main window for a moment before the application closes and produces a memory leak (because we haven't yet written any code that destroys the main window). To create a completely functional API application, we still have to do two more things: create and enter the message loop, and fully implement the window procedure.

Entering the Message Loop

The message loop is a simple mechanism that retrieves messages and sends them to the application. It is an infinite while loop that keeps the application running until we close it. The message loop is actually terminated by a WM_QUIT message.

To create the message loop, you have to declare a variable of type TMsg to store the received message and then write the following while loop:

var   Msg: TMsg;   ...   while GetMessage(Msg, 0, 0, 0) do begin   TranslateMessage(Msg);   DispatchMessage(Msg); end; 

The GetMessage function retrieves messages from the application's message queue and stores them in the Msg variable. If the GetMessage function retrieves a WM_QUIT message, the application terminates. If the GetMessage function retrieves any other message, the message is passed to the TranslateMessage and DispatchMessage functions for processing.

The TranslateMessage function actually does very little processing. It is simply meant to translate virtual keyboard codes to more understandable characters.

The DispatchMessage function is much more important. The job of this function is to dispatch the received message to the window procedure, where the application's logic resides. The application's window procedure is its heart, and everything that you've done so far is utility code that can be reused in all subsequent API applications.

The Window Procedure

Finally, the last thing to do is write the implementation of the window procedure. There are two things that you have to do in the window procedure:

  1. Respond to whatever message you need.

  2. Call the DefWindowProc function for messages that you didn't process. (This function provides default processing for messages not processed by the window procedure.)

To create a fully functional application, you'll have to process at least the WM_DESTROY message, which is sent to the application when the user selects Close to terminate the application. In response to the WM_DESTROY message, you have to call the PostQuitMessage function to post the
WM_QUIT message and actually terminate the application:

function WindowProc(Wnd: HWND; Msg, WParam, LParam: LongInt): LongInt; stdcall; begin   { most messages require you to return 0 }   Result := 0;   case Msg of     WM_DESTROY: begin       PostQuitMessage(0);     end;         // WM_DESTROY   else     Result := DefWindowProc(Wnd, Msg, WParam, LParam);   end;          // case end;

The Entire Application

Now that you've seen all the pieces that make up an API application, it's time to see the complete code for the API application. The source code is displayed in Listing 23-2.

Listing 23-2: The simple API application that only displays the main window

image from book
program Generic; uses   Windows, Messages; const   MY_CLASS = 'DelphiAPIWindow'; var   Msg: TMsg;   MainWnd: HWND;   wc: WNDCLASS; function WindowProc(Wnd: HWND; Msg, WParam, LParam: LongInt): LongInt; stdcall; begin   { most messages require you to return 0 }   Result := 0;   case Msg of     WM_DESTROY: begin       PostQuitMessage(0);     end;         // WM_DESTROY   else     Result := DefWindowProc(Wnd, Msg, WParam, LParam);   end;          // case end; begin   wc.style := CS_VREDRAW or CS_HREDRAW;   wc.lpfnWndProc := @WindowProc;   wc.cbClsExtra := 0;   wc.cbWndExtra := 0;   wc.hInstance := HInstance;   wc.hIcon := LoadIcon(0, IDI_INFORMATION);   wc.hCursor := LoadCursor(0, IDC_ARROW);   wc.hbrBackground := COLOR_BTNFACE + 1;   wc.lpszMenuName := nil;   wc.lpszClassName := MY_CLASS;   if RegisterClass(wc) = 0 then Exit; 
{ create the window } MainWnd := CreateWindow(MY_CLASS, 'Win32 API Application',   WS_OVERLAPPEDWINDOW, Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT),   Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT), 0, 0, HInstance, nil);   { display the main window }   ShowWindow(MainWnd, SW_SHOWNORMAL);   UpdateWindow(MainWnd);  { message loop }   while GetMessage(Msg, 0, 0, 0) do   begin     TranslateMessage(Msg);     DispatchMessage(Msg);   end; end.
image from book

The API application is displayed in Figure 23-2.

image from book
Figure 23-2: The API application



Inside Delphi 2006
Inside Delphi 2006 (Wordware Delphi Developers Library)
ISBN: 1598220039
EAN: 2147483647
Year: 2004
Pages: 212
Authors: Ivan Hladni

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net