Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Теллес М. - Borland C++ Builder. Библиотека программиста - 1998

.pdf
Скачиваний:
790
Добавлен:
13.08.2013
Размер:
4.35 Mб
Скачать

Borland C++ Builder (+CD). Библиотека программиста 261

MessageBox(pAddress->GetLastName(), " Фамилия ", MB_OK );

}

Как видно из этого кода, мы создаем новый экземпляр класса TAdress (заголовочный файл для него вы должны добавить к исходному файлу mainfrm.cpp). Этот объект играет роль интерфейса между приложением MFC и DLL CBuilder. После того как объект создан, мы передаем указатель на него в оберточную функцию, прототип которой был описан в заголовочном файле Address.h. Функция вызывается, и наша форма отображается в виде модального диалога. Когда пользователь нажимает кнопку OK, форма закрывается и объект возвращается приложению MFC. Чтобы удостовериться в том, что данные были переданы корректно, мы после возврата управления от функции отображаем на экране окно сообщения, содержащее фамилию, введенную пользователем в форму.

Вот и все. Теперь вы знаете, что требуется для передачи данных между формой CBuilder и приложением MFC — на самом деле, не так уж и много всего.

Последний пример: работа с базами данных

Без сомнения, вы теперь захотите использовать в своих приложениях MFC все те развитые возможности работы с базами данных, которые могут предоставить объекты VCL. В последнем примере взаимодействия MFC и VCL мы посмотрим, как превратить предыдущий пример в приложение-записную книжку, использующую базу данных для хранения информации, введенной пользователем.

На рис. 12.4 показана новая форма, которую нам надо добавить в DLL. При этом для работы с новой DLL нам не придется ничего менять в приложении Visual C++, созданном в предыдущем примере, поскольку интерфейс останется неизменным.

Рис. 12.4. Форма выбора записей

Если вы достаточно давно работаете в Visual C++, то скорее всего предположите, что для работы с базами данных через объекты VCL сначала надо произвести некую их инициализацию. К счастью, вы будете не правы. VCL использует статические библиотеки, которым никакая инициализация не нужна, а компоненты VCL сами знают, когда надо открыть, закрыть или инициализировать машину баз данных. Короче говоря, все это происходит, так сказать, за кулисами и не требует вашего вмешательства.

В данном примере мы предоставим пользователю возможность искать и просматривать значения в базе данных. Пользователь сможет ввести некоторое значение (в том числе использующее

Borland C++ Builder (+CD). Библиотека программиста 262

символы ? и * для поиска множества значений), а потом выбрать нужный адрес из списка найденных адресов.

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

void __fastcall TForm1::Button2Click(TObject *Sender)

{

AnsiString strSearch = ""; AnsiString s = Edit1->Text;

// Проверяем каждое поле ввода if ( Edit1->Text.Length() )

{

strSearch += "LASTNAME = '"; strSearch += Edit1->Text; strSearch += "'";

}

if ( Edit2->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "FIRSTNAME = '"; strSearch += Edit2->Text; strSearch += "'";

}

if ( Edit3->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "ADDRESS_1 = '"; strSearch += Edit3->Text; strSearch += "'";

}

if ( Edit4->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "ADDRESS_2 = '"; strSearch += Edit4->Text; strSearch += "'";

}

if ( Edit5->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "CITY = '"; strSearch += Edit5->Text; strSearch += "'";

}

if ( Edit6->Text.Length() )

{

Borland C++ Builder (+CD). Библиотека программиста 263

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "STATE = '"; strSearch += Edit6->Text; strSearch += "'";

}

if ( Edit7->Text.Length() )

{

if ( strSearch.Length() != 0 ) strSearch += " AND "; strSearch += "ZIP_CODE = '"; strSearch += Edit7->Text; strSearch += "'";

}

TForm2 *pForm = new TForm2(NULL); pForm->SetSearchString(strSearch); pForm->ShowModal();

// Копируем значения из базы данных

Edit1->Text = pForm->Table1->FieldValues["LASTNAME"]; Edit2->Text = pForm->Table1->FieldValues["FIRSTNAME"]; Edit3->Text = pForm->Table1->FieldValues["ADDRESS_1"]; Edit4->Text = pForm->Table1->FieldValues["ADDRESS_2"]; Edit5->Text = pForm->Table1->FieldValues["CITY"]; Edit6->Text = pForm->Table1->FieldValues["STATE"]; Edit7->Text = pForm->Table1->FieldValues["ZIP_CODE"]; delete pForm;

}

Приведенный код достаточно объемен, но абсолютно несложен. Мы проверяем каждое поле редактирования формы на предмет того, ввел туда пользователь какое-нибудь значение или нет. Если да, то мы делаем добавления в строку поиска, которая будет передана базе данных для использования в качестве фильтра. То есть мы формируем строку вида:

FIELD='Value' AND FIELD='Value'

где FIELD одно из полей нашей базы данных, а 'Value' строка, введенная пользователем, представляющая собой значение для данного поля, по которому пользователь хочет осуществить поиск. Короче говоря, мы создаем форму QBE (Query By Example, запрос по примеру), которая добавит эти возможности в приложение Visual C++. В принципе, можно создавать запросы QBE и в Visual C++, но это малоприятное занятие. Оно представляет собой либо создание запросов SQL времени исполнения с последующим получением из них требуемых полей, либо написание совершенно ужасных параметрических запросов. А в CBuilder мы создали все, что нам было нужно, с помощью нескольких простейших строк кода, представляющих собой сопоставление имен полей и введенных значений.

Теперь, когда строка для поиска создана, надо отфильтровать базу данных и показать пользователю все записи, удовлетворяющие введенным им ограничениям. Как раз для этого мы и используем новую форму (Form2). На этой форме содержится окно списка, в котором будут отображаться отфильтрованные записи, из которых пользователь и будет делать окончательный выбор.

Borland C++ Builder (+CD). Библиотека программиста 264

Создание формы выбора записей

Форма выбора записей содержит два элемента окно списка (list box) и кнопку OK. Список будет содержать записи, из которых пользователь будет осуществлять выбор. По нажатии кнопки OK форма будет закрываться, сообщая вызывающей форме, что запись из базы данных была благополучно выбрана.

Первое, что надо сделать в этой форме, — обеспечить загрузку списка записей. Мы не можем сделать это непосредственно при создании формы, так как на момент ее создания строка поиска еще не будет определена. Зато мы можем определить метод, который вызывающая форма (Form1) может использовать для задания строки поиска. Давайте посмотрим на код этого метода:

void TForm2::SetSearchString(AnsiString strSearch)

{

Table1->Filter = strSearch; Table1->Filtered = true;

// Грузим элементы в список

Table1->First(); while ( !Table1->Eof )

{

AnsiString s = Table1->FieldValues["LASTNAME"]; s += ",";

s += Table1->FieldValues["FIRSTNAME"]; ListBox1->Items->Add( s ); Table1->Next();

}

}

В этом коде таблица базы данных фильтруется (в качестве фильтра используется строка поиска, определенная нами в обработчике для кнопки Поиск в форме Form1), а затем в цикле некая информация из каждой записи помещается в список. В данном случае в список загружается строка вида ФАМИЛИЯ, ИМЯ (поля таблицы LASTNAME, FIRSTNAME), но при желании вы, конечно, можете загружать в него и другую информацию. Возможно, было бы даже удобнее отображать не список, а сетку, содержащую все данные, но для нашего примера хватит и этого. Обратите внимание на то, что нам нет необходимости очищать список перед загрузкой в него данных, поскольку для каждого нового поиска форма создается заново, а потом уничтожается.

Последнее, что нам надо обработать, — это ситуацию, когда пользователь выбирает какую-то запись из списка и нажимает кнопку OK. Все, что нам надо при этом сделать, — это установить указатель в базе данных (не путайте с указателями-адресами переменных, к которым вы привыкли в C, — в базах данных этот термин обозначает совершенно другое) на выбранную запись. На данный момент таблица все еще «видна» вызывающей форме (это объект, определенный как __published), так что мы без труда можем прочитать из нее необходимую информацию.

Вот как выглядит код, обрабатывающий нажатие кнопки OK:

void __fastcall TForm2::Button1Click(TObject *Sender)

{

if ( ListBox1->ItemIndex == -1 ) return;

// Перемещаем указатель до нужной записи

Table1->First();

for ( int i=0; i<ListBox1->ItemIndex; ++i )

Borland C++ Builder (+CD). Библиотека программиста 265

Table1->Next(); Close();

}

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

Сразу после закрытия этой формы функция ShowModal возвратит управление вызывающей форме. Здесь хочется отметить, что вместо использования метода Close можно было бы просто присвоить свойству ModalResult второй формы значение, отличное от 0, при этом форма бы точно так же закрылась и передала это значение в вызывающую форму.

Возвратившись в вызывающую форму, мы читаем данные из таблицы и заносим их в соответствующие поля редактирования. Это позволит пользователю отредактировать их перед тем, как форма будет закрыта и они будут переданы в вызывающую программу Visual C++. Последнее, что должен обеспечить код формы Form1, — это удалить объект Form2, поскольку именно в этом коде выделялась под него память. Если этого не сделать, произойдет утечка оперативной памяти (утечка ресурсов), поскольку этот объект уже никогда не будет удален из памяти после завершения работы программы. Утечка ресурсов может привести к сбою операционной системы, так что давайте сами удалять за собой все, что создавали.

Теперь мне хотелось бы остановиться на некоторых моментах, связанных с нашим небольшим приложением. Во-первых, как мы увидели, инициализация базы данных производится непосредственно внутри компонентов, работающих с базой. Советую вам перенести этот подход на все свои компоненты. Если вы подразумева ете, что что-то уже установлено (определено, инициализировано и т.п.), но это мо жет быть не так, устанавливайте это в своих компонентах сами. Не ждите, что конечный пользователь сам установит за вас какие-либо глобальные переменные или вызовет глобальные функции, ведь если он этого все же не сделает, вся ваша система, базирующаяся на ваших компонентах, не будет работать так, как вы (и конечный пользователь) ожидаете. В компонентах все необходимые для их работы вещи должны определяться внутри них не надо ничего оставлять на пользователя.

Далее хотелось бы отметить, что формы можно создавать не только в оберточной функции вашей DLL, но и в других формах. При этом надо твердо помнить, что память, которую вы потребляете, принадлежит вызывающему приложению, и не забывать освобождать ее. Под объект, передаваемый в оберточную функцию DLL (у нас это TAddress), функциями DLL память не выделяется, так что об этом должны позаботиться вы сами.

И последнее, о чем хочется сказать по поводу нашего приложения, — если во время исполнения произойдет ошибка, система обработки исключительных ситуаций CBuilder автоматически перехватит ее, даже если вами не определен специальный обработчик для этой ситуации. Это может быть весьма важно для программиста, разрабатывающего приложения. Вы попрежнему можете использовать в своем коде блоки try ... catch и даже смело рассчитывать на то, что исключительные ситуации, не обработанные вами, обработает лежащая в основе вашего кода VCL. Если исключительная ситуация произошла в DLL, то, как правило, можно считать, что метод всетаки будет выполнен до конца. Это относится, естественно, только к тем исключительным ситуациям, которые не являются фатальными; эти тут же прекратят выполнение функции. Постарайтесь обработать в своем коде как можно больше исключительных ситуаций,

чтобы возможно меньшее их количество проходило мимо вашего внимания и сбивало работу

Borland C++ Builder (+CD). Библиотека программиста 266

вашего приложения.

На этом наш пример завершен. Отметьте, что мы не внесли никаких изменений ни в оберточную функцию DLL, ни в вызывающее приложение MFC по сравнению с предыдущим примером. Все изменения заключены внутри одной из форм DLL, так что больше никому знать о них не надо. Вспомните ведь нам даже не пришлось генерировать новую библиотеку импорта для нашей DLL. Правда, здорово?

Что мы узнали в этой главе?

В этой главе была охвачена достаточно значительная область программирования. Если вы программируете на Visual C++, вы узнали достаточно, чтобы иметь возможность использовать в своих программах компоненты VCL, правда косвенным образом. Так что вы теперь знаете, что у конкурирующих Microsoft Visual C++ и Borland C++Builder есть-таки точки соприкосновения. Кроме того, в этой главе были освещены следующие вопросы:

Мы увидели, как можно создавать оберточные (wrapper) функции, содержащие описание создания форм CBuilder, а затем включать эти функции в DLL для вызова из Visual C++.

Мы научились использовать утилиту Visual C++ dumpbin для просмотра всех экспортируемых функций DLL, созданной в CBuilder.

Мы научились также использовать программу lib для создания библиотеки импорта из DLL, созданной в CBuilder (а на самом деле точно так же можно работать с любыми DLL, а не только с созданными в CBuilder). Может быть, при этом программисты, использующие Visual C++, узнали что-то новое о своей системе.

Мы узнали, как создавать файл описания модуля (DEFфайл), который может быть использован для создания библиотеки импорта в Visual C++.

Мы увидели, как подпрограммы, написанные на Visual C++ и CBuilder, могут «общаться» между собой посредством передачи объектов.

Мы также увидели, что возможности CBuilder по части работы с базами данных могут быть привнесены и в Visual C++ — путем использования форм CBuilder в DLL.

Хоть это и не было сказано в данной главе напрямую, но на самом деле вы можете применить любые возможности CBuilder в приложениях Visual C++, создавая в CBuilder модули (unit), используемые посредством вызовов оберточных функций. В этих модулях не должно содержаться никакого визуального кода, но зато в них можно напрямую использовать компоненты VCL,

например компоненты, работающие с базами данных.

Если вам хочется попробовать сделать что-нибудь интересное, попробуйте напрямую создать компонент в диалоге Visual C++, чтобы понять, возможно ли это. Создайте оберточную функцию, которая позволит вам создать экземпляр компонента, а также указать окно диалога и позицию, в которых должен находиться этот компонент. Возможно, для того, чтобы заставить компонент работать, вам придется использовать в качестве его родителя NULL.

На этом заканчивается глава, посвященная интерфейсу с Visual C++. Я надеюсь, что теперь все программисты, использующие MFC, поняли, как много они теряют без VCL. Правда, я также надеюсь, что все осознали и то, что вопрос этот не является вопросом «быть иль не быть» — VCL все же можно использовать в приложениях на Visual C++, правда только косвенно.

Глава 13•Работа с потоками

Что такое поток?

Зачем нужны потоки?

Borland C++ Builder (+CD). Библиотека программиста 267

Создание потока

Синхронизация

Фоновый поиск

Когда я произношу слово thread (поток; дословно: нить), о чем вы думаете1 ? О небольших белых кусочках хлопка на вашем деловом костюме? О мотках для вязания с воткнутыми спицами? В компьютерной индустрии, однако, слово thread означает нечто совершенно другое.

Потоки (threads) в компьютерном мире обозначают параллельные процессы, которые работают внутри вашего приложения. Ваша программа состоит из потоков, хотя вы, наверное, никогда об этом не задумывались. Когда вы отображаете форму на экране, она работает в потоке. Обычно все формы приложения запущены в одном и том же потоке; такое приложение называется однопоточным (single-threaded). Когда вы используете более одного потока в приложении, ваша программа называется многопоточной или мультипоточной (multi-threaded). Вот так. Вы знаете все, что нужно, о потоках. Ну, не совсем. Есть еще парочка вопросов: Что такое на самом деле потоки? Зачем они мне нужны? Как их использовать? Ответы на все эти вопросы вы найдете здесь, в главе, посвященной потокам и их использованию в приложениях CBuilder.

1Последующее ниже рассуждение о нитках происходит из-за того, что в английском языке для термина поток применяется слово thread (нить). — Примеч. перев .

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

Зачем нужны потоки? В старые недобрые времена программирования под MS-DOS мы создавали TSR (Terminate and Stay Resident), то есть резидентные программы. TSR — фоновая программа, которая выполняется, пока операционная система занята другими вещами. Резидентные программы были удобны для перехвата нажатий определенных клавиш, фоновой работы с принтером, обновлении времени в часах на экране и т. д. Эти твари были очень ограниченными по возможностям, и их надо было писать в основном на ассемблере, чтобы они были как можно меньше и быстрее.

Когда наступила эра программирования под Windows, программы стали работать обычным образом. Менеджер процессов в Windows мог сказать программе, чтобы она остановилась, и запустить вторую, но пока от второй программы не приходило подтверждение запроса, продолжала выполняться первая. Windows 95 и Windows NT являются «господствующими» операционными системами и могут действительно управлять системой, в частности останавливать работающую программу. Тогда запускаются другие программы на уровне операционной системы. Потоки же являются самостоятельным образованием в том смысле, что вы можете временно приостановить поток и снова его запустить, но окончание его работы является исключительной прерогативой потока. Операционная система, конечно, может убить поток (как обычно называют завершение его работы) в любой момент времени. Ваша же программа, хотя она и породила этот поток, может только приостановить его.

В данной главе мы начинаем исследовать программирование потоков: зачем и для чего они вам

Borland C++ Builder (+CD). Библиотека программиста 268

могут понадобиться и как вы можете с ними общаться. Мы рассмотрим примеры потоков от простого отображения форм до сложных межпотоковых коммуникаций. В конце главы вы станете больше разбираться не только в том, как использовать потоки в вашем приложении, но также и в том, когда стоит их использовать.

Зачем нужны потоки?

Если вы некоторое время работали в Windows, то вы, вероятно, знакомы с фоновыми (background) процессами, которые выполняются во время простоя, когда ваша программа не обрабатывает ввод данных от пользователя и не совершает еще какое-нибудь действие по запросу пользователя. В системе CBuilder имеется встроенная поддержка обработки времени простоя (idle time), но использование потоков является предпочтительным методом для фоновой работы. У потока есть масса преимуществ перед системой обработки времени простоя. Во-первых, вам не нужно беспокоиться о времени, которое занимает какая-нибудь операция. Во-вторых, если вам нужно делать несколько вещей в фоновом режиме, то вам придется каким-то образом самому распределять время между этими задачами. Вам придется запускать первую задачу, останавливать ее, запускать вторую и т. д. Потоковая система уже имеет встроенное распределение времени и управление процессами.

Вторая причина, по которой стоит использовать потоки, состоит в том, что потоки не мешают нормальной обработке сообщений в вашем приложении. Если вы писали циклы фоновой обработки в стандартном приложении Windows (без использования библиотек классов типа MFC), то вы, наверное, замечали, что ваш алгоритм эквивалентен циклу обработки сообщений. Обычно циклы фоновой обработки выглядят примерно так:

while ( !done )

{

//Происходит какая-нибудь обработка

//Обрабатывать все ожидающие в очереди сообщения,

//иначе вся система остановится

while ( GetMessage(&msg) ) DispatchMessage(&msg);

}

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

Простой пример потока

В нашем первом примере мы начнем с внутренних особенностей работы с потоками. Мы создадим простую форму, которая использует поток для обновления текста на экране. Форма позволит вам приостанавливать (pause), вновь запускать (resume) и останавливать (stop) выполнение потока. В то же время поток будет отображать числа на экране. Мы исследуем процесс создания потока и добавления объекта «поток» в вашу программу.

На рис. 13.1 показана форма, которую мы будем использовать в данном примере потока. Как видите, на ней находится одна метка и три кнопки.

Borland C++ Builder (+CD). Библиотека программиста 269

Рис. 13.1. Форма простого примера потоков

Добавление нового потока

Первым делом в приложении, использующем потоки, было бы неплохо узнать, как же добавить поток. Вы, наверное, видели класс TThread в документации к CBuilder. Этот класс служит для представления потоков в системе. К сожалению, добавление нового потока немного сложнее, чем создание нового объекта «поток» оператором new.

Потоки содержат метод, который реализует всю необходимую обработку потока. Этот метод (Execute) вызывается при старте потока и должен работать, пока поток жив. Когда метод Execute возвращает управление, то поток выходит или умирает. Таким образом, вам нужно переопределить метод Execute так, чтобы он делал то, что вам нужно. Но чтобы можно было переопределить метод, вам необходимо унаследовать свой собственный класс от класса TThread, и именно этим мы и займемся. Вы определите класс, наследуемый от TThread, и напишете метод Execute для этого порожденного класса.

В CBuilder очень легко создавать классы, наследуемые от TThread, создавая новый объект «поток». Для этого выберите команду File д New и элемент Thread Object (потоковый объект) на первой странице диалога New Items. При выборе этого объекта появится вторичное окно диалога с запросом о новом имени вашего потокового класса. В данном случае укажите имя TCheckThread, так как мы будем проверять некоторое значение и отображать его. Когда вы нажмете на кнопку OK, класс TCheckThread будет сгенерирован в исходном файле Unit2.cpp (а также в заголовочном файле Unit2.h). Вот так выглядит этот класс, сгенерированный CBuilder:

//------------------------------------------------------

#include <vcl\vcl.h> #pragma hdrstop #include "Unit2.h"

//------------------------------------------------------

//Important: Methods and properties of objects in VCL can

//only be used in a method called using Synchronize,

//for example:

//Synchronize(UpdateCaption);

//where UpdateCaption could look like:

//

//void __fastcall TCheckThread::UpdateCaption()

//{

//Form1->Caption = "Updated in a thread";

//}

//-----------------------------------------------------

Borland C++ Builder (+CD). Библиотека программиста 270

__fastcall TCheckThread::TCheckThread(bool CreateSuspended) : TThread (CreateSuspended)

{

}

//-----------------------------------------------------

void __fastcall TCheckThread::Execute()

{

//---- Place thread code here ----

}

//-----------------------------------------------------

Как видите, потоковый класс, сгенерированный мастером, описан весьма полно. Он ничего не делает, но готов к работе. Чтобы он что-то делал, вам нужно только лишь написать метод Execute.

В нашем первом примере интересно было бы изучить разные методы класса потока. Именно поэтому наш метод Execute крайне прост, он считает до 10000 снова и снова. Мы хотим проиллюстрировать скорость работы кода потока, связь потока с окружающим миром и научить вас писать код потока. Держа все это в уме, добавьте следующий код в метод Execute:

void __fastcall TCheckThread::Execute()

{

while ( !Terminated )

{

Synchronize(UpdateLabel1);

}

MessageBox(NULL, "Все сделано!", "Информация", MB_OK);

}

Кроме метода Execute, нам нужно еще обновить два метода. Во-первых, конструктор класса. Вот код инициализации, который нужно добавить в конструктор:

__fastcall TCheckThread::TCheckThread(bool CreateSuspended) : TThread (CreateSuspended)

{

pLabel = NULL; nCount = 0;

}

Здесь мы устанавливаем начальные значения для указателя на метку, в которую мы будем передавать показания нашего счетчика, а также для самого счетчика, который будет содержать значения от 0 до 10 000 и будет постоянно увеличиваться. Кроме этого, нам нужен метод, чтобы присвоить значение нашему указателю на метку:

void __fastcall TCheckThread::AssignLabel(TLabel *pL)

{

pLabel = pL;

}

Метод Synchronize предоставляет вам способ избежать всех этих проблем, связанных с многопоточностью, когда один и тот же объект пытаются изменить из разных потоков в приложении. Когда вы работаете в главном потоке (самом приложении) с объектом VCL, то вам

Соседние файлы в предмете Программирование на C++