WriteLn для C++Builder (исходники)

Источник: CppBuilder
Kent Reisdorph

Содержание

Введение

Программисты Delphi уже в течение долгого времени используют функцию WriteLn в качестве отладочного инструмента. Текст, переданный во WriteLn, направляется в консольное окно, превращая его в лог-файл реального времени. Используя подобный подход, вы можете отслеживать поведение своей программы в секциях кода, критичных с точки зрения времени, где обычные точки останова (breakpoints) не будут работать. К сожалению, C++Builder не включает в себя функцию WriteLn. Тем не менее, нет никаких причин для того, чтобы не реализовать ее самостоятельно. В этой статье я объясню вам, как это сделать. Как часть объяснения, мы рассмотрим также функции AllocConsole, GetStdHandle и WriteConsole Windows API.

WriteLn по-дельфийски

Использование функции WriteLn в Delphi состоит из двух шагов. Сначала вы выделяете консольное окно для вашего приложения, а затем уже вызываете WriteLn. Код выглядит примерно следующим образом:

AllocConsole();
WriteLn(`Entering Critical Section');
{ Тут что-то делаем }
WriteLn(`Exiting Critical Section');

AllocConsole - это функция Win32 API, и, как и подразумевает ее имя, она выделяет консольное окно для приложения. Каждое приложение может иметь свое собственное консольное окно - но только одно. Это справедливо как для GUI-приложений, так и для стандартных консольных приложений.

После того, как приложению выделено окно консоли, функция WriteLn выводит в него текст. Система простая, но чрезвычайно эффективная. Фактически, в некоторых ситуациях при отладке достаточно только ее.

Консольный вывод в C++

У C++Builder есть инструменты отладки, похожие на WriteLn. Диагностические макросы TRACE и WARN, а также функция OutputDebugString делают, в первом приближении, то же самое, что и функция WriteLn. Но их использование, тем не менее, страдает одним недостатком: их вывод направляется в log-файл, который не обновляется до тех пор, пока приложение не задержится на точке останова, или пока оно не завершится. Это ограничивает использование вышеупомянутых инструментов в некоторых ситуациях.

Но хорошие новости, кроме того, что WriteLn легко реализовать в C++Builder, состоят также в том, что вы можете включить в ее реализацию непосредственно функции управления консолью, что, в итоге, сделает использование вашей собственной функции WriteLn намного проще. WriteLn требует дескриптор выходного буфера для консольного окна, который вы можете получить посредством функции GetStdHandle. В целом это все будет выглядеть примерно так:

AllocConsole();
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsole(handle, "Test", 4, 0, 0);

Давайте рассмотрим код внимательнее. Вызов AllocConsole, очевидно, выделяет консольное окно вашему приложению. Во второй строке функция GetStdHandle извлекает дескриптор стандартного выходного буфера. Вы передаете значение STD_OUTPUT_HANDLE в эту функцию, чтобы "попросить" Windows выдать вам дескриптор стандартного буфера.

Как только вы получили дескриптор стандартного выходного буфера, вы можете вызвать функцию WriteConsole для записи текста в консольное окно. Первый параметр функции WriteConsole представляет собой дескриптор стандартного буфера вывода, второй параметр - текст, который отправляется в консоль, третий параметр - количество выводимых символов.

WriteLn для C++Builder

Теперь, зная все необходимое, вы можете создать-таки вашу собственную функцию WriteLn. Для начала, давайте объявим ее прототип. Он очень прост:

void WriteLn(String text);

Как видите, функция принимает параметр типа String и ничего не возвращает. Разумеется, значение параметра text и будет тем самым выводимым в консоль текстом. Давайте теперь взглянем на WriteLn целиком:

void WriteLn(String text)
{
  static HANDLE handle;
  if(!handle) {
    AllocConsole();
    handle = GetStdHandle(STD_OUTPUT_HANDLE);
  }
  text += "\n";
  WriteConsole(handle, 
    text.c_str(), text.Length(), 0, 0);
}

Сначала вы объявляете статическую переменную handle - она подскажет вам, не было ли консольное окно уже выделено вашему приложению (вспомните, что статическая переменная имеет начальное значение, равное нулю, и сохраняет свое значение между вызовами функции). Если значение переменной handle равно нулю, то, значит, консольное окно еще не было выделено. Таким образом, вы можете вызвать функцию AllocConsole. Затем вы вызываете GetStdHandle для получения дескриптора стандартного выходного буфера. Далее, вы добавляете символ перевода новой строки в конец переданного в функцию текста. Это гарантирует, что каждая строка будет начинаться с новой строки. Наконец, вы вызываете WriteConsole для вывода текста в консольное окно. Обратите внимание, что вы передаете длину строки текста в качестве третьего параметра функции WriteConsole, чтобы строка полностью была выведена в консоль.

Пример

Листинг ниже является программой, использующей функцию WriteLn. Это главная форма с одной кнопкой. Когда вы нажимаете на кнопку, цикл вызывает функцию WriteLn для вывода двадцати строк текста в консольное окно.

//--------------------------------------------
#include 
#pragma hdrstop
 
#include "WriteLnU.h"
//--------------------------------------------
#pragma resource "*.dfm"
 
TForm1 *Form1;
 
// The WriteLn function.
void WriteLn(String text)
{
  static HANDLE handle;
  if (!handle) {
    AllocConsole();
    handle = GetStdHandle(STD_OUTPUT_HANDLE);
  }
  text += "\n";
  WriteConsole(handle,
    text.c_str(), text.Length(), 0, 0);
}
 
//--------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) 
  : TForm(Owner)
{
}
//--------------------------------------------
void __fastcall 
TForm1::Button1Click(TObject *Sender)
{
  for (int i=0;i<20;i++) {
    Application->ProcessMessages();
    WriteLn("Iteration: " + String(i));
    Sleep(100);
  }
}

Заключение

Функция WriteLn может быть замечательным инструментом отладки. Вы можете нечасто ее использовать, но, когда она нужна на самом деле, то ей нет замены.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=18354