Советы пользователям Delphi и C++Builder

Часть 5. Снова о внешних устройствах и операционной системе

Наталия Елманова
Компьютер Пресс - CD, 1999, N 10
© Copyright N.Elmanova & ComputerPress Magazine.

Во второй статье данного цикла ("Компьютер Пресс", 1999, N 3) были рассмотрены некоторые из множества проблем, возникающие при использовании внешних устройств в приложениях Delphi и C++Builder, а также вопросы получения информации об операционной системе. В соответствии с многочисленными пожеланиями читателей мы снова возвращаемся к этой теме.

Совет 15. Инициирование перезагрузки Windows 95/98

Иногда в процессе работы приложения следует произвести перезагрузку операционной системы. Это бывает необходимо, если в процессе работы приложения были произведены изменения в переменных окружения, изменено сетевое имя компьютера, и др.

Для перезагрузки операционной системы можно использовать функцию Windows API ExitWindowsEx, первый из параметров которой определяет способ завершения работы Windows 95/98.

Создадим простейший пример, использующий эту функцию. Для этого на форму приложения поместим три кнопки.

 

Создадим обработчики событий, связанных с нажатием на кнопки:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
ExitWindowsEx(EWX_LOGOFF,0);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
ExitWindowsEx(EWX_SHUTDOWN,0); 
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button3Click(TObject *Sender)
{
ExitWindowsEx(EWX_REBOOT,0);         
}
//---------------------------------------------------------------------------

В случае Delphi эти же обработчики событий выглядят так:

procedure TForm1.Button1Click(Sender: TObject);
begin
ExitWindowsEx(EWX_LOGOFF,0);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
ExitWindowsEx(EWX_SHUTDOWN,0); 
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
ExitWindowsEx(EWX_REBOOT,0);         
end;

Запустив приложение, мы можем выбрать способ завершения работы Windows либо сеанса пользователя Windows.

Отметим, что завершение работы или сеанса пользователя вWindows 95/98 действительно может быть осуществлено всеми тремя способами. В Windows NT же в общем случае работоспособен только первый обработчик события. Дело в том, что в соответствии с соображениями безопасности не всякое приложение имеет право произвести завершение работы Windows NT (например, хотя бы потому, что под управлением этой операционной системы могут выполняться различные сетевые и иные сервисы, жизненно важные для функционирования других компьютеров сети). Если же тем не менее возникнет необходимость произвести завершение работы Windows NT, а не просто завершение сеанса пользователя, следует воспользоваться функцией Windows API AdjustTokenPrivileges для предоставления данному приложению права остановки или перезагрузки операционной системы.

Совет 16. Отключение хранителя экрана

Нередко разработчики приложений, выполняющих какие-либо длительные действия (например, обработку большого количества данных или пересылку данных на удаленный компьютер с помощью низкоскоростной линии связи), сталкиваются с проблемами, создаваемыми так называемыми хранителями экрана (screen savers). Хранители экрана представляют собой, по существу, такие же Windows-приложения, нередко потребляющие немалое количество самых разнообразных ресурсов, за исключением собственно люминофора, которым покрыта внутренняя поверхность экрана монитора. Поэтому при запущенном хранителе экрана в общем случае производительность любого другого приложения, выполняющего какие-либо длительные действия, может существенно снизиться.

Чтобы понять, как предотвратить запуск хранителя экрана, следует вспомнить, что операционные системы с графическим пользовательским интерфейсом (в том числе Windows) основаны на посылке и обработке сообщений, а также на обработке событий. Windows постоянно обрабатывает разнообразные события, в том числе события, связанные с перемещением мыши или нажатием клавиш. Хранитель экрана запускается операционной системой, если по истечении заранее заданного (то есть установленного пользователем операционной системы) промежутка времени не происходило двух указанных выше типов событий. Для этого операционная система посылает активному приложению сообщение WM_SYSCOMMAND с параметром WPARAM, равным SC_SCREENSAVE. Именно это сообщение и следует перехватить в нашем приложении.

Перехват сообщения можно осуществить, создав обработчик события OnMessage объекта TApplication, наступающего при получении приложением очередного сообщения Windows. Соответствующая функция имеет два параметра: имя сообщения и параметр handled, указывающий, обрабатывается ли такое сообщение данным приложением. Если он установлен равным false, сообщение обрабатывается операционной системой. В противном случае оно обрабатывается приложением.

Создадим простейший пример, иллюстрирующий перехват такого сообщения. Для этого поместим на форму радиогруппу из двух элементов.

 

Теперь создадим функцию-член класса TForm1 (в C++Builder 4 для этого есть специальный эксперт, встроенный в Code Explorer, однако не возбраняется создать определение функции вручную).

 

Создадим текст этой функции, а также обработчик события, связанный с созданием формы приложения и указывающий, какая функция должна вызываться при получении сообщения данным приложением:

void __fastcall TForm1::ProcessMess(TMsg & msg, bool &handled)
{
    //TODO: Add your source code here
    if (msg.message==WM_SYSCOMMAND && msg.wParam==SC_SCREENSAVE
        && RadioGroup1->ItemIndex==1)
handled=true;
else
handled=false;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)
{
Application->OnMessage = ProcessMess;
}

Если функция создана вручную, следует не забыть создать ее определение в h-файле:

class TForm1 : public TForm
{
__published:	// IDE-managed Components
        TRadioGroup *RadioGroup1;
        void __fastcall FormCreate(TObject *Sender);
private:
    void __fastcall ProcessMess(TMsg & msg, bool &handled);
  public:		// User declarations
        __fastcall TForm1(TComponent* Owner);
};

Аналогичный код для Delphi имеет вид:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;
  
type
  TForm1 = class(TForm)
    RadioGroup1: TRadioGroup;
    procedure FormCreate(Sender: TObject);
      private
      procedure ProcessMess(var msg:TMsg; var handled:boolean) ;
    { Private declarations }
  public
    { Public declarations }
  end;
  
var
  Form1: TForm1;
  
implementation

{$R *.DFM}

{ TForm1 }
procedure TForm1.ProcessMess(var msg: TMsg; var handled: boolean);
begin
    if (msg.message=WM_SYSCOMMAND) and
   (msg.wParam=SC_SCREENSAVE)
    and (RadioGroup1.ItemIndex=1)    then
handled:=true
else
handled:=false;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage:=ProcessMess;
end;

end.

Если созданное таким образом приложение активно, хранитель экрана будет запускаться (или не запускаться) в зависимости от того, какая опция выбрана в радиогруппе.

Совет 17. Получение сведений об операционной системе

Нередко при разработке приложений требуется определить, какова версия операционной системы, где расположены системные каталоги, каковы значения переменных окружения. Для этой цели используются функции Windows API, такие как GetVersionEx (определение типа операционной системы), GetComputerName(определение сетевого имени компьютера), GetUserName (определение имени пользователя), GetSystemDirectory (определение местоположения системного каталога), GetWindowsDirectory (определение каталога Windows), ExpandEnvironmentStrings (определение значений переменных окружения), а также структуры Windows (например, OSVERSIONINFO). Подробные сведения об этих функциях и структурах содержатся в справке по Windows API, входящей в комплект поставки как C++Builder, так и Delphi.

Приведем простейший пример использования некоторых из этих функций, являющийся в значительной степени вариацией на тему стандартных примеров использования Windows API. Поместим на главную форму вновь созданного приложения несколько меток и кнопку и создадим обработчик события, связанный с нажатием на кнопку и отображающий системную информацию в метках:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
LPTSTR lpszSystemInfo;
DWORD cchBuff = 256;
TCHAR tchBuffer2[1000];
DWORD dwResult;
TCHAR tchBuffer[1000];
int nSize;

lpszSystemInfo = tchBuffer2;

GetComputerName(lpszSystemInfo, &cchBuff);
nSize = sprintf(tchBuffer, "Компьютер:  %s",  lpszSystemInfo);
Label1->Caption=AnsiString(tchBuffer);

GetUserName(lpszSystemInfo, &cchBuff);
nSize = sprintf(tchBuffer, "Пользователь:  %s", lpszSystemInfo);
Label2->Caption=AnsiString(tchBuffer);

nSize = GetSystemDirectory(lpszSystemInfo, MAX_PATH);
nSize = sprintf(tchBuffer, "System directory:  %s", lpszSystemInfo);
Label3->Caption=AnsiString(tchBuffer);

nSize = GetWindowsDirectory(lpszSystemInfo, MAX_PATH);
nSize = sprintf(tchBuffer, "Windows directory:  %s", lpszSystemInfo);
Label4->Caption=AnsiString(tchBuffer);
Label5->Caption="Переменные окружения :";

dwResult = ExpandEnvironmentStrings("OS=%OS% ", lpszSystemInfo, 500);
Label6->Caption=AnsiString(lpszSystemInfo);

dwResult = ExpandEnvironmentStrings("INCLUDE=%INCLUDE%",lpszSystemInfo,500);
Label7->Caption=AnsiString(lpszSystemInfo);

dwResult = ExpandEnvironmentStrings("CLASSPATH=%CLASSPATH%",lpszSystemInfo,500);
Label8->Caption=AnsiString(lpszSystemInfo);

dwResult = ExpandEnvironmentStrings("ComSpec=%ComSpec%",lpszSystemInfo,500);
Label9->Caption=AnsiString(lpszSystemInfo);

OSVERSIONINFO osvi;
char szVersion [80];
  
memset(&osvi, 0, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
GetVersionEx (&osvi);

if (osvi.dwPlatformId == VER_PLATFORM_WIN32s)
    wsprintf (szVersion, "Microsoft Win32s %d.%d (Build %d)",
        osvi.dwMajorVersion,
        osvi.dwMinorVersion,
        osvi.dwBuildNumber & 0xFFFF);

else if (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
    wsprintf (szVersion, "Microsoft Windows 95 %d.%d (Build %d)",

		osvi.dwMajorVersion,
        osvi.dwMinorVersion,
        osvi.dwBuildNumber & 0xFFFF);

else if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
    wsprintf (szVersion, "Microsoft Windows NT %d.%d (Build %d)",

		osvi.dwMajorVersion,
        osvi.dwMinorVersion,
        osvi.dwBuildNumber & 0xFFFF);
Label10->Caption="Операционная система: "+AnsiString(szVersion);
}

Соответствующий код для Delphi выглядит так:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
  
type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    Label10: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
  
var
  Form1: TForm1;
  
implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var osvi: TOSVersionInfo;  si: TSystemInfo;
s:string;
dwResult, nsize: Cardinal;
ch : array[0..1000] of Char;
begin

GetComputerName(ch, nsize);
Label1.Caption:='Компьютер: '+ch;

GetUserName(ch, nsize);
Label2.Caption:='Пользователь: '+ch;

nSize := GetSystemDirectory(ch, MAX_PATH);
Label3.Caption:='System directory: '+ch;

nSize := GetWindowsDirectory(ch, MAX_PATH);
Label4.Caption:='Windows directory: '+ch;

Label5.Caption:='Переменные окружения :';

dwResult:= ExpandEnvironmentStrings('OS=%OS% ', ch, 500);
Label6.Caption:=ch;

dwResult:= ExpandEnvironmentStrings('INCLUDE=%INCLUDE%',ch,500);
Label7.Caption:=ch;

dwResult:= ExpandEnvironmentStrings('CLASSPATH=%CLASSPATH%',ch,500);
Label8.Caption:=ch;

dwResult := ExpandEnvironmentStrings('ComSpec=%ComSpec%',ch,500);
Label9.Caption:=ch;

osvi.dwOSVersionInfoSize := sizeof (OSVERSIONINFO);
GetVersionEx (osvi);

if (osvi.dwPlatformId = VER_PLATFORM_WIN32s) then
s:= 'Microsoft Win32s '+IntToStr(osvi.dwMajorVersion)+'.'+
IntToStr(osvi.dwMinorVersion) +' Build '+
IntToStr(osvi.dwBuildNumber);

if (osvi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS)
then
s:= 'Microsoft Windows 95 '+IntToStr(osvi.dwMajorVersion)+'.'+
 IntToStr(osvi.dwMinorVersion) +' Build '+
IntToStr(osvi.dwBuildNumber);

if (osvi.dwPlatformId = VER_PLATFORM_WIN32_NT)
then
s:= 'Microsoft Windows NT '+IntToStr(osvi.dwMajorVersion)+'.'+
IntToStr(osvi.dwMinorVersion) +' Build '+
IntToStr(osvi.dwBuildNumber);

Label10.Caption:=' Операционная система: '+s;

end;

end.

Если запустить это приложение, оно отобразит сведения о системе и переменных окружения.

 

Отметим, что в настоящее время имеется немалое количество свободно распространяемых, условно-бесплатных и коммерческих компонентов для Delphi и C++Builder, предназначенных для получения системной информации (некоторые из них содержатся на компакт-диске в данном номере журнала). Все они так или иначе используют те же самые функции Windows API для установки или считывания значений своих свойств или выполнения методов, и основное их преимущество заключается главным образом в том, что они избавляют программиста от необходимости создания кода, подобного приведенному выше. Соответственно при разработке приложений можно использовать как непосредственно функции Windows API, как в приведенных выше примерах, так и готовые компоненты.