Chapter 15: Optimizing Procedure-Oriented Applications and System Services

image from book  Download CD Content

This chapter will focus on principles of using the C++ .NET 2003 inline assembler in procedure-oriented Windows applications and system services. Using the assembler for each of these task types has distinct features, which will be discussed here.

We will begin with a classical procedure-oriented Windows application.

Suppose an application should display the difference between two integers in the application s work area. The frame of such an application can be easily obtained with the Visual C++ .NET Application Wizard. Suppose the difference should be displayed by left clicking on the application window.

The source code of the application is shown in Listing 15.1.

Listing 15.1: A procedure-oriented Windows application that computes the difference between two integers
image from book
 // WIN_CLASSIC.cpp : Defines the entry point for the application  #include "stdafx.h"  #include "WIN_CLASSIC.h"  #include <stdio.h>  #define MAX_LOADSTRING 100  // Global Variables:  HINSTANCE hInst;                      // Current instance  TCHAR szTitle[MAX_LOADSTRING];        // The title bar text  TCHAR szWindowClass [MAX_LOADSTRING]; // The main window class name  // Forward declarations of functions included in this code module:  ATOM                     MyRegisterClass (HINSTANCE hInstance);  BOOL                     InitInstance (HINSTANCE, int);  LRESULT CALLBACK       WndProc (HWND, UINT, WPARAM, LPARAM);  LRESULT CALLBACK       About (HWND, UINT, WPARAM, LPARAM);  int APIENTRY _tWinMain(HINSTANCE hInstance,                       HINSTANCE hPrevInstance,                       LPTSTR    lpCmdLine,                       int       nCmdShow)  {  // TODO: Place code here.  MSG msg;  HACCEL hAccelTable;  // Initialize global strings  LoadString (hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);  LoadString (hInstance, IDC_WIN_CLASSIC, szWindowClass, MAX_LOADSTRING);  MyRegisterClass (hInstance);  // Perform application initialization:  if (!InitInstance (hInstance, nCmdShow))   {    return FALSE;   }  hAccelTable = LoadAccelerators (hInstance, (LPCTSTR) IDC_WIN_CLASSIC);  // Main message loop:  while (GetMessage(&msg, NULL, 0, 0))   {    if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg))     {      TranslateMessage (&msg);      DispatchMessage (&msg);     }    }    return (int) msg.wParam;  }  // FUNCTION: MyRegisterClass ()  // PURPOSE: Registers the window class.  // COMMENTS:  // This function and its usage are only necessary if you want this code  // to be compatible with Win32systems prior to the 'RegisterClassEx'  // function that was added to Windows 95. It is important to call this  // function so that the application will get 'well formed' small icons  // associated with it.  ATOM MyRegisterClass (HINSTANCE hInstance)  {    WNDCLASSEX wcex;    wcex.cbSize = sizeof (WNDCLASSEX);    wcex.style = CS_HREDRAW  CS_VREDRAW;    wcex.Ipf nWndProc = (WNDPROC)WndProc;    wcex.cbClsExtra = 0;    wcex.cbWndExtra = 0;    wcex.hInstance = hInstance;    wcex.hIcon = LoadIcon(hInstance, (LPCTSTR) IDI_WIN_CLASSIC);    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW-1);    wcex.lpszMenuName = (LPCTSTR) IDC_WIN_CLASSIC;    wcex.IpszClassName = szWindowClass;    wcex.hIconSm = LoadIcon (wcex.hInstance, (LPCTSTR) IDI_SMALL);    return RegisterClassEx (&wcex);  }  // FUNCTION: InitInstance (HANDLE, int)  // PURPOSE: Saves instance handle and creates main window  // COMMENTS:  // In this function, we save the instance handle in a global variable and  // create and display the main program window.  BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)  {    HWND hWnd;    hInst = hInstance; // Store instance handle in our global variable    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,                                                CW_USEDEFAULT, 0,                                                CW_USEDEFAULT, 0,                                                NULL, NULL, hInstance,                                                NULL) ;    if (!hWnd)    {     return FALSE;    }    ShowWindow(hWnd, nCmdShow) ;    UpdateWindow(hWnd) ;    return TRUE;    }  // FUNCTION: WndProc (HWND, unsigned, WORD, LONG)  // PURPOSE: Processing messages for the main window  // WM_COMMAND - Process the application menu.  // WM_PAINT - Paint the main window.  // WM_DESTROY - Post a quit message and return.  LRESULT CALLBACK WndProc (HWND hWnd, UINT message,                            WPARAM wParam, LPARAM lParam)  {   int wmId, wmEvent;   PAINTSTRUCT ps;   HDC hdc;   RECT rc;   int i1, i2, ires;   char buf[32] = i1  i2 = ;   switch (message)   {   case WM_COMMAND:        wmId    = LOWORD(wParam);        wmEvent = HIWORD(wParam);  // Parse the menu selections:        switch (wmId)         {           case IDM_ABOUT:              DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX,                                hWnd, (DLGPROC)About);              break;           case IDM_EXIT:              DestroyWindow(hWnd);              break;           default:               return DefWindowProc(hWnd, message, wParam, lParam);          }         break;   case WM_PAINT:     hdc = BeginPaint(hWnd, &ps);  // TODO: Add any drawing code here   EndPaint(hWnd, &ps);      break;  // Added code    case WM_LBUTTONDOWN:      i1 = 45;      i2 = 98;      hdc = GetDC(hWnd);      GetClientRect(hWnd, &rc);     _asm{          mov EAX, i1          sub EAX, i2          mov ires, EAX          }     sprintf (&buf [9], "%d", ires);     TextOut (hdc, (rc.right-rc.left)/3, (rc.bottom-rc.top)/2  ,  buf, 12);     InvalidateRect (hWnd, &rc, FALSE);     ReleaseDC(hWnd, hdc);     break;   case WM_DESTROY:     PostQuitMessage (0);     break;   default:      return DefWindowProc (hWnd, message, wParam, lParam);    }   return 0;  }  // Message handler for about box.  LRESULT CALLBACK About (HWND hDlg, UINT message, WPARAM wParam, LPARAM  lParam)  {  switch (message)   {   case WM_INITDIALOG:     return TRUE;   case WM_COMMAND:    if (LOWORD(wParam) == IDOK  LOWORD (wParam) == IDCANCEL)        {          EndDialog (hDlg, LOWORD (wParam));        return TRUE;        }    break;    }  return FALSE;  } 
image from book
 

Since we deal with a classical procedure-oriented Windows application, the WM_LBUTTONDOWN message handler is used for displaying the result:

 case WM_LBUTTONDOWN :      i1 = 45;      i2 = 98;      hdc = GetDC(hWnd);      GetClientRect (hWnd, &rc);     _asm{          mov EAX, i1          sub EAX, i2          mov ires, EAX         }    sprintf(&buf[9], "%d", ires);    TextOut(hdc, (rc.right-rc.left)/3, (rc.bottom-rc.top)/2 r buf  ,  12);    InvalidateRect(hWnd, &rc, FALSE);    ReleaseDC(hWnd, hdc);    break; 

Put the code of the onBnClicked handler, for example, after the WM_PAINT handler in the callback function. We will look at this code more closely.

The first two statements assign values to variables ( i1 ˜ i2 ). In our case, these are i1 = 45 and i2 = 98 .

The variables are declared in the wndProc callback function. This function also declares auxiliary variables rc , ires , and buf .

To display the result in the application window, you should first access the display device context. This is done with the following statement:

 hdc = GetDC(hWnd) 

To get the coordinates of the client rectangle of the application window, use the following statement:

 GetClientRect(hWnd, &rc) 

You will need these coordinates to position and display the text.

The assembly block does the main job in this handler: It computes the difference between two numbers . The result is stored in the ires variable.

To display the value of the ires integer variable, convert it to a text string. The sprintf function is suitable for this purpose. The function is defined in the stdio.h header file; therefore, this file must be included in the source code. The sprintf function has the following form:

 sprintf(&buf[9], "%d", ires) 

After the ires integer variable is converted to a character string (the buf variable), the result can be output to the client rectangle. This is done with the function:

 TextOut(hdc, (rc.right-rc.left)/3, (rc.bottom-rc.top)/2, buf, i2) 

Finally, tell the operating system to redraw the client rectangle of the application window. This is done with the function:

 InvalidateRect(hWnd, &rc, FALSE); 

Before you exit the handler, release the device context:

 ReleaseDC(hWnd, hdc); 

Using assembly blocks and commands in a procedure-oriented application is simple. Simply put the code into the handler of an event. Keep in mind that the results of mathematical calculations should be converted to the character type before displaying or printing them. Functions such as TextOut operate with character data; therefore, such a conversion is required.

The window of the application is shown in Fig. 15.1.

image from book
Fig. 15.1: Window of a procedure-oriented Windows application that displays the difference between two integers

Assembly language is an effective tool for optimization when developing Windows system services. These are the most mysterious part of the operating system, and at the same time the most important. You could roughly define a system service as a program that runs outside the user s context.

System services are supported only in Windows NT/2000/XP/2003 operating systems. In Windows, the services are used for implementing the most important tasks related to operating system management and monitoring. They provide access to drivers of the file and disk system of a computer and are an intermediate link for accessing the computer s hardware.

Most of the services are exact to the time of execution, especially when they work with the computer s hardware. Using the assembler for writing crucial pieces of code for such services is often the only way of providing their correct work within strict time limitations.

Suppose an application receives a data stream from the USB or COM port of a PC and writes it to a file for future processing. With particular time intervals, a system service reads the data from this file and processes it in background. Suppose that the data stream written to the file from the USB port is a sequence of integers (which often is the case). Processing such data by a system service can involve looking for the maximum value in the data received and written to the file at this moment.

Such a software configuration is typical for data measurement and processing systems in industry and scientific research. A driver reads data from a physical device and performs some preliminary processing. After this processing, the data are usually saved in a file. The subsequent manipulations with data are done by another program that is often implemented as a system service.

A device driver works in real time, and it can make a few thousands or even tens of thousands data samples per second. For storing and preliminarily processing the data, a large amount of memory and a lot of processor time might be required. If the number of received bytes is not large, or time limitations are strict, the use of the computer memory is reasonable. However, this is true only in this case. It is very convenient to save data in a file (the space on hard disks makes it possible to do this), but this creates a problem: A high-level application slowly processes data in a file. Even if the driver writes the data on the disk very fast (which is quite achievable), high-level processing, analysis, and displaying of the results in real time may be a very difficult task. Redundancy of the code of the system service at the data processing stage will make the whole data collecting and analysis system completely inoperable.

In such cases, the code of the Windows service should be as compact and fast as possible. Also keep in mind that Windows NT/2000/XP/2003 operating systems are not real-time systems, and data processing can take much more time than planned. The assembler is useful in this case. A well-designed code fragment or function can provide required performance in crucial parts .

We will look at two examples of developing Windows system services that use the assembler to improve performance. Taking into account the complexity and importance of the topic, we will begin with some theoretical information on the basic principles of system service development. We will focus on just the main issues, so that you can understand the examples in this chapter and write your own.

From a software developer s point of view, system services are programs, usually console ones, that are related to the operating system s Service Control Manager (SCM) in a special manner. SCM is an internal component of Windows that controls system services and device drivers. SCM interacts with the user via Services Control Panel (Windows NT 4.0) or one of Microsoft Management Console (MMC) modules in Windows 2000/XP/2003.

Like any other console application, the system service has the main function that can support the work of several services. In the following examples, applications for one service are considered . The console application of a service must contain the entry point of the service that is usually named ServiceStart or ServiceMain . This entry point is associated with the text identifier of the service.

For interaction between a service and the system, a service control handler should be registered. The service usually runs in background and requires a separate thread. You can develop code of a system service either manually or with Visual C++ .NET 2003 Application Wizard.

Regardless of which tools you are using, you should stick to the following procedure:

  1. Define the main () entry point that will register the service in SCM. Tell the Service Control Dispatcher the entry point ( ServiceStart ) and name of the service. This is done with the startServiceCtrlDispatcher function.

  2. Pass control to the ServiceStart function to register the service control handler. This is done with the RegisterServiceCtrlHandler function. In addition, the ServiceStart function usually starts the main thread of the service with one of the following functions: _beginthread , _beginthreadex , or CreateThread .

  3. Create the service control handler.

Now, we will continue with practical examples. The first one models a small data collecting and processing system. Unfortunately, we cannot illustrate its work in real time in this example. The goal of this demonstration is to show you how assembly language can be used in a Windows system service that processes data from a file. The task of the system service is simple: It looks for the maximum value among integers saved in a file named test .

Instead of a driver, we will use a test program that will write five random integers in the test file and display the contents of the file every time you hit the <Enter> key. Also, the maximum read from the testmax file will be displayed.

This virtual data processing system starts a system service (we will name it FileWriter ) that reads the contents of the test file every 30 seconds, finds the maximum, and writes it in the testmax file.

Now, we will examine how the maximum is looked for with an assembly block in the FileWriter system service. To develop the service, create a project of a console application without source files (i.e., an empty project) and add the filewriter.cpp source file to it (Listing 15.2).

Listing 15.2: The FileWriter system service that looks for the maximum element in a binary file
image from book
 #include <windows.h>  #include <stdio.h>  #include <string.h>  #include <time.h>  #include <process.h>  SERVICE_STATUS           Service1Status;  SERVICE_STATUS_HANDLE    Service1StatusHandle;  int modtime=30;  // Interval = 30 seconds  HANDLE thread;  BOOL manual=FALSE;  int rx[256];   int imax;   FILE* fp;   void findmax (void *)   {   time_t timeval;   while (Service1Status.dwCurrentState != SERVICE_STOPPED)   {   if (Service1Status.dwCurrentState != SERVICE_PAUSED)   {   time (&timeval);   if ((timeval%modtime) ==0 manual)   {   if (!(fp = fopen("d:\test", "a+b")))   exit(1);   int fres = fseek(fp, 0, SEEK_END);   int fsize = ftell(fp);   fres = fseek(fp, 0, SEEK_SET);   fread(&rx, sizeof(int), fsize, fp);   fclose (fp) ;   _asm{   mov ECX, fsize   shr ECX, 2   lea ESI, rx   mov EAX, DWORD PTR [ESI]   again:   add ESI, 4   cmp EAX, DWORD PTR [ESI]   jge next_int   xchg EAX, DWORD PTR [ESI]   next_int:   dec ECX   jnz again   mov imax, EAX   };   if (!(fp = fopen ("d:\testmax", "w+b")))   exit(1);   fwrite(&imax, sizeof(int), 1, fp);   fclose (fp) ;  }        manual = FALSE;     }   Sleep(500);    }  }  VOID __ stdcall CtrlHandler (DWORD Opcode)  {    DWORD status;    switch (Opcode)     {      case SERVICE_CONTROL_PAUSE:  // Do whatever it takes to pause here.         Service1Status.dwCurrentState = SERVICE_PAUSED;         break;     case SERVICE_CONTROL_CONTINUE:  // Do whatever it takes to continue here.        Service1Status.dwCurrentState = SERVICE_RUNNING;        manual = TRUE;        break;    case SERVICE_CONTROL_STOP:  // Do whatever it takes to stop here.        Service1Status.dwWin32ExitCode = 0;        Service1Status.dwCurrentState = SERVICE_STOPPED;        Service1Status.dwCheckPoint = 0;        Service1Status.dwWaitHint = 0;        if (!SetServiceStatus (Service1StatusHandle, &Service1Status))             status = GetLastError ();        return;    case SERVICE_CONTROL_INTERROGATE:       break;     default:      }  // Send current status.   if (!SetServiceStatus (Service1StatusHandle, &Service1Status))      status = GetLastError ();   return;  }  void __ stdcall Service1Start (DWORD argc, LPTSTR *argv)  {   DWORD status;   DWORD specificError;   if (argc > 1) modtime = atoi (argv[1]);   if (modtime == 0) modtime = 1;   Service1Status.dwServiceType      = SERVICE_WIN32;   Service1Status.dwCurrentState     = SERVICE_START_PENDING;   Service1Status.dwControlsAccepted = SERVICE_ACCEPT_STOP                                        SERVICE_ACCEPT_PAUSE_CONTINUE;   Service1Status. dwWin32ExitCode   = 0;   Service1Status.dwServiceSpecificExitCode = 0;   Service1Status.dwCheckPoint       = 0;   Service1Status.dwWaitHint         = 0;   Service1StatusHandle = RegisterServiceCtrlHandler (TEXT (FileWriter) ,                                                      CtrlHandler);   if (Service1StatusHandle = (SERVICE_STATUS_HANDLE)0)      return;  // Initialization code goes here.   status=NO_ERROR;  // Handle error condition   if (status != NO_ERROR)   {    Service1Status.dwCurrentState  = SERVICE_STOPPED;    Service1Status.dwCheckPoint    = 0;    Service1Status.dwWaitHint      = 0;    Service1Status.dwWin32ExitCode = status;    Service1Status.dwServiceSpecificExitCode = specificError;    SetServiceStatus (Service1StatusHandle, &Service1Status);    return;   }  // Initialization complete - report running status.     Service1Status.dwCurrentState = SERVICE_RUNNING;     Service1Status.dwCheckPoint   = 0;     Service1Status.dwWaitHint     = 0;     if (!SetServiceStatus (Service1StatusHandle, &Service1Status))         status = GetLastError();  // This is where the service does its work.  thread= (HANDLE)_beginthread (findmax, 0, NULL);  return;  }  void main(int argc, char *argv[])  {  SERVICE_TABLE_ENTRY DispatchTable [] =   {   { TEXT(FileWriter), Service1Start   },   { NULL, NULL                          }   };  if (argc>l && !stricmp(argv[1]f "delete"))    {     SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);     if (!scm)      {        printf("Cant open SCM\n");        exit(1);      }     SC_HANDLE svc = OpenService(scm, "FileWriter", DELETE);     if (!svc)      {        printf("Cant open service\n");        exit (2);      }     if (!DeleteService(svc))      {         printf("Cant delete service\n");         exit(3);      }     printf("Service deleted\n");     CloseServiceHandle(svc);     CloseServiceHandle(scm);        exit (0);   }     if (argc>l && !stricmp(argv[1], "setup"))      {       char pname[1024];       pname[0] = '"';       GetModuleFileName(NULL, pname+1, 1023);       strcat(pname, "\"");       SC_HANDLE scm = OpenSCManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE, svc)       if (!scm)          {           printf ("Cant open SCM\n");           exit(1);          }       if (! (svc = CreateService (scm, "FileWriter", "FileWriter",                                   SERVICE_ALL_ACCESS,                                   SERVICE_WIN32_OWN_PROCESS,                                   SERVICE_DEMAND_START,                                   SERVI CE_ERROR_NORMAL,                                   pname, NULL, NULL,                                   NULL, NULL, NULL)))          {          printf ("Registration error!\n");          exit (2);          }   printf ("Successfully registered\n");   CloseServiceHandle(svc);   CloseServiceHandle(scm);   exit(0);  }  if (!StartServiceCtrlDispatcher (DispatchTable))  } 
image from book
 

How does the FileWriter system service work? When the ServiceStart function is called (which is the entry point to the service), the main thread containing the findmax function is started, which does the service s job:

 thread= (HANDLE)_beginthread (findmax, 0, NULL) 

In accordance with the value of the modtime variable, the assembly block of the findmax function is called every 30 seconds:

 _asm {         mov ECX, fsize         shr ECX, 2         lea ESI, rx         mov EAX, DWORD PTR [ESI]     again:         add ESI, 4         cmp EAX, DWORD PTR [ESI]         jge next_int         xchg EAX, DWORD PTR [ESI]     next_int:         dec ECX         jnz again         mov imax, EAX       }; 

This fragment of code implements the search for the maximum element in the memory buffer whose address is in the ESI register. ESI contains the address of the rx buffer that contains integers read from the test file. The found maximum value is stored in the imax variable. Then the

 if (!(fp=fopen("d:\testmax", "w+b")))        exit(1);  fwrite(&imax, sizeof(int), 1, fp);  fclose(fp); 

statements write the maximum value from the imax variable to the testmax file.

This operation repeats every 30 seconds. However, another interval can be specified in the modtime variable.

Now, we will provide a few words about starting the FileWriter system service. The first thing you should complete is the installation of the service in the operating system. For this purpose, execute the

 FileWriter setup 

command from the command line.

To delete the system service, execute the following command:

 FileWriter delete 

Installation of the system service is shown in Fig. 15.2.

image from book
Fig. 15.2: Installation of the FileWriter system service in Windows

The installed Filewriter service can be started from the service control window (Fig. 15.3).

image from book
Fig. 15.3: Starting the Filewriter service

The program that tests the Filewriter system service writes five random values in the test binary file. The same program finds the maximum of the elements in the file. This program also opens the testmax file created by the Filewriter system service and checks the maximum value written to it. The testmax file will contain the maximum obtained with the system service before the next start of the test program. The source code of the test console program is shown in Listing 15.3.

Listing 15.3: A program that tests a system service
image from book
 // FILE_WRITE_ADD_EXM.cpp : Defines the entry point for the console  // application  #include "stdafx.h"  #include <stdlib.h>  #include <time.h>  int _tmain(int argc, _TCHAR* argv[])  {   FILE* fp;   int ix[25];   int rx[256];   int imax;   int imax1 = 0;   printf("SYSTEM SERVICE FILEWRITERS' TEST APPLICATION\n\n");   srand((unsigned)time(NULL));   for (int cnt = 0; cnt < 5; cnt++)       ix[cnt] = rand();   if (!(fp = fopen("d:\test", "a+b")))    {     printf("Cannot open file!\n");        exit(1);   }   fwrite(&ix, sizeof(int), 5, fp);   int fres = fseek(fp, 0, SEEK_END);   int fsize = ftell(fp);   printf("Size of file TEST = %d Bytes\n\n", fsize);   fres = fseek(fp, 0, SEEK_SET);   fread(&rx, sizeof(int), fsize, fp);   for (int cnt = 0; cnt < fsize/4; cnt++)   printf("%d", rx[cnt]);   fclose(fp);   _asm {         mov ECX, fsize         shr ECX, 2         lea ESI, rx         mov EAX, DWORD PTR [ESI]   again:         add ESI, 4         cmp EAX, DWORD PTR [ESI]         jge next_int         xchg EAX, DWORD PTR [ESI]  next_int:         dec ECX         jnz again         mov imax, EAX         };   printf("\nMaximum = %d\n", imax);   if (!(fp = fopen("d:\testmax", "a+b")))    {     printf ("Cannot open file!\n");     exit(1);  }  fread(&imaxl, sizeof(int), 1, fp);  printf("\nFile TESTMAX contains = %d", imaxl);  getchar();  return 0;  } 
image from book
 

The window of the application is shown in Fig. 15.4.

image from book
Fig. 15.4: Window of a program that tests a system service

The second example of using the assembler in a system service is more complicated. Suppose you want to count words in a text file periodically and display this number. Text information is written to or deleted from the file at random moments. Like in the previous example, a real-time data processing system is modeled here. Implementation of the program as a system service is best suited for monitoring changes in a file.

This time, use the Visual C++ .NET Application Wizard to create the frame of the service. Be very careful when making the following steps.

Select the Windows Service application type (Fig. 15.5).

image from book
Fig. 15.5: Selecting the Windows Service application type

We will name the project cwservice . After the sample system service is created, change the CWServiceWinService name given by the Wizard to CWservice and leave the other options unchanged (Fig. 15.6).

image from book
Fig. 15.6: Setting the name of the service

Add the installer for the service to your project. This is required for installation/deinstallation and for setting the start options of the service. You can add the installer in the project constructor area by clicking the right mouse button and selecting the Add Installer option (Fig. 15.7).

image from book
Fig. 15.7: Adding the installer to the project

Then, assign the CWservice name to the DisplayName field of the service installer (Fig. 15.8).

image from book
Fig. 15.8: Setting the DisplayName property

Note that generally , the names assigned to the DisplayName and ServiceName fields can be different. Leave the StartType field unchanged (the service is started manually).

The Account field of the process installer should contain the LocalSystem record (Fig. 15.9).

image from book
Fig. 15.9: Setting the Account property

The subsequent steps involve modulation of the code of the service. Recall that your service should periodically count words in a text file. To perform periodical operations, use the Timer component on the Toolbox panel. Put the component on the design work area. Set the timer properties as shown in Fig. 15.10.

image from book
Fig. 15.10: Setting the timer properties

As you can see from Fig. 15.10, the timer-triggering interval is set to 10 seconds, and the initial state of the timer is off. In addition, put the EventLog component on the work area. We will need it to display the number of words in the file. This component connects your service to the Windows event log. Set the properties of the EventLog component as shown in Fig. 15.11.

image from book
Fig. 15.11: Setting the properties of the EventLog component

We will continue with a few explanations concerning setting the properties of the EventLog component. The source of events to log is the CWservice service, and the events will be recorded in the event log file.

For the CWservice system service to do some useful work, the timer should be started in the Onstart event handler. After that, the OnTimer event handler will be able to perform required actions.

When the service stops, the timer should be stopped as well. This is done in the OnStop event handler. Below is the source code of the CWServiceWinService class that defines the behavior of your service (Listing 15.4).

Listing 15.4: The source code of the CWServiceWinService class
image from book
 #pragma once  using namespace System;  using namespace System::Collections;  using namespace System::ServiceProcess;  using namespace System::ComponentModel;  #include <windows.h>  #include <stdio.h>  using namespace System::Runtime::InteropServices;  [DllImport("CW.dll")]   extern "C" int countwords(char* path);  int intWords;  char buf[64];  int ibuf;  namespace CWService  {  // <summary>  // Summary for CWServiceWinService  // </summary>  //  // WARNING: If you change the name of this class,  // you will need to change the  // 'Resource File Name' property for the managed resource compiler tool  // associated with all .resx files this class depends on. Otherwise,  // the designers will not be able to interact properly with localized  // resources associated with this form.         public __gc class CWServiceWinService :   public System::ServiceProcess::ServiceBase        {         public:                CWServiceWinService ()                {                       InitializeComponent ();                 }                 // <summary>                 // Clean up any resources being used.                 // </summary>                 void Dispose (bool disposing)                 {                        if (disposing && components)                        {                               components   >Dispose ();                        }                          super:: Dispose (disposing);                 }                   protected:                 // <summary>                 // Set things in motion so your service can do its work.                 // </summary>                 void OnStart (String* args[])                 {                 // TODO: Add code here to start your service.                  timer1   >Enabled = true;                 }                 // <summary>                 // Stop this service.                 // </summary>                 void OnStop ()                 {  // TODO: Add code here to perform any tear-down necessary  // to stop your service.                 timer1   >Enabled = false;                }        private: System::Diagnostics::EventLog * eventLog1;        private: System::Timers::Timer * timer1 ;        private:               // <summary>                // Required designer variable.                // </summary>                System::ComponentModel::Container *components;                // <summary>                // Required method for Designer support - do not modify                // the contents of this method with the code editor.                // </summary>         void InitializeComponent(void)         {           this   >eventLogl = new System::Diagnostics::EventLog();           this   >timer1 = new System::Timers::Timer();           (__try_cast<System:: ComponentModel::ISupportInitialize * >  (this   >eventLogl))   >BeginInit();           (__try_cast<System::ComponentModel::ISupportInitialize * >  (this   >timer1))   >BeginInit();                       //                       // eventLog1                       //           this   >eventLogl   >Source = SCWService;                       //                      // timer1                       //          this   >timer1   >Interval = 10000;           this   >timer1   >Elapsed += new  System::Timers::ElapsedEventHandler(this, timer1_Elapsed);                       //                       // CWServiceWinService                       //           this   >CanPauseAndContinue = true;           this   >ServiceName = S"CWService";           (__try_cast<System::ComponentModel::ISupportInitialize * >  (this   >eventLogl))   >EndInit();           (__try_cast<System::ComponentModel::ISupportInitialize * >  (this   >timerl))   >EndInit();                }         private: System::Void timer1_Elapsed(System::Object * sender,  System::Timers::ElapsedEventArgs * e)               {                 ibuf = sprintf(buf, "%s", "Number of words in file = ")                 cntWords = countwords("d:\testfile");                    sprintf(buf + ibuf, "%d", cntWords);                    eventLog1   >WriteEntry("CWService", (LPSTR)buf);                   }          };  } 
image from book
 

If you only glance at the listing, you will wonder where the function that processes the file is. We put this function in the image from book  cw.dll dynamic link library and named it countwords . The function is written in the assembler and counts words in a file. The DLL is linked to the service with these lines:

 using namespace System::Runtime::InteropServices;  [DllImport("cw.dll")]   extern "C" int countwords (char* path) 

Counting words and writing the result to the log are done in the timer handler:

 ibuf = sprintf (buf , "%s", "Number of words in file = ");  cntWords = countwords ("d: \testfile");  sprintf(buf + ibuf, "%d", cntWords);  eventLog1   >WriteEntry ("CWService", (LPSTR)buf); 

For word count, a small text file named testfile is used. As noted earlier, the words are counted with the countwords function from the image from book  cw.dll library, and the result is stored in the cntWords variable. A formatted string with the result is written to the application log.

The source code of the image from book  cw.dll dynamic link library is shown in Listing 15.5.

Listing 15.5: The source code of cw.dll
image from book
 // cw.cpp : Defines the entry point for the DLL application  #include "stdafx.h"  #include <windows.h>  BOOL APIENTRY DllMain (HANDLE hModule,                          DWORD ul_reason_for_call,                          LPVOID lpReserved)  {      return TRUE;  }   extern "C" __declspec(dllexport) int countwords(char* path)    {      HANDLE hIn;      DWORD bytesRead, lenFile;      int cntWords = 0;      char buf[2048];      hIn = CreateFile(path, GENERIC_READ, 0,      NULL, OPEN_EXISTING, 0, NULL);      lenFile = GetFileSize (hln, NULL);      ReadFile(hIn, buf, lenFile, &bytesRead, NULL);      CloseHandle(hIn);      _asm {           lea  ESI, buf           mov  EDX, bytesRead           dec  ESI           inc  EDX           //  check_space:           inc  ESI           dec  EDX           jz   ex           cmp  BYTE PTR [ESI], ' '           je   check_space           cmp  BYTE PTR [ESI], Oxd           je   check_space           cmp  BYTE PTR [ESI], Oxa           je   check_space           jmp  next_char  next_char:           inc  ESI           dec  EDX           jz   ex           cmp  BYTE PTR [ESI], ' '           je   inc_cntWords           cmp  BYTE PTR [ESI], Oxd           je   inc_cntWords           cmp  BYTE PTR [ESI], Oxa           je   inc_cntWords           jmp  next_char  inc_cntWords:           inc  cntWords           jmp  check_space  ex:            mov  EAX, cntWords      }    } 
image from book
 

The parameter of the countwords function is a pointer to a character string containing the name of the text file. The contents of the file are written to a memory buffer with the size of 2K. Keep in mind that the size of the file must not be greater than the size of the buffer, and the latter can be chosen at will.

Then, the words in the buffer are looked for. The search algorithm assumes that the words are separated with spaces or carriage -return characters , which is the most frequent case. The source code of DLL is straightforward.

Note the following information about starting the CWService system service. It is installed from the command line:

 cwservice.exe -install 

and deinstalled with the command:

 cwservice.exe -install /u 

After installation, the service is started or stopped as always, i.e., with MMC. You can monitor the work of the CWService service by looking through the event log (Fig. 5.12).

image from book
Fig. 15.12: Message from the CWService system service

The contents of the testfile text file are shown in Fig. 15.13.

image from book
Fig. 15.13: Contents of the testfile file

In this chapter, we provided specific examples regarding use of the assembler for programming procedure-oriented applications and Windows system services. Despite their complexity, we believe they will be useful to you.



Visual C++ Optimization with Assembly Code
Visual C++ Optimization with Assembly Code
ISBN: 193176932X
EAN: 2147483647
Year: 2003
Pages: 50
Authors: Yury Magda

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