Тъмната страна на приложение. ProcessMessages

Статия, внесена от Маркус Джунглас

При програмиране на манипулатор на събития в Delphi (като OnClick събитие на TButton), идва моментът, в който приложението ви трябва да бъде заето за известно време, напр. кодът трябва да напише голям файл или да компресира някои данни.

Ако направите това, ще забележите това изглежда, че приложението ви е заключено. Формата ви не може да бъде преместена повече и бутоните не показват никакъв признак на живот. Изглежда, че е сринат.

Причината е, че приложението Delpi е с една нишка. Кодът, който пишете, представлява само куп процедури, които се извикват от основната нишка на Delphi всеки път, когато се случи събитие. През останалото време основната тема е обработка на системни съобщения и други неща като функции за обработка на формуляри и компоненти.

Така че, ако не завършите обработката на вашето събитие, извършвайки продължителна работа, ще попречите на приложението да обработва тези съобщения.

Често срещано решение за такъв тип проблеми е да се обадите „Приложение. ProcessMessages ". "Application" е глобален обект от класа TApplication.

instagram viewer

Приложението. Processmessages обработва всички чакащи съобщения като движение на прозореца, натискане на бутони и т.н. Обикновено се използва като просто решение, за да поддържа приложението ви „работещо“.

За съжаление механизмът зад „ProcessMessages“ има свои собствени характеристики, които могат да причинят голямо объркване!

Какво означава ProcessMessages?

PprocessMessages обработва всички чакащи системни съобщения в опашката за съобщения на приложенията. Windows използва съобщения, за да „говори“ с всички работещи приложения. Взаимодействието на потребителя се довежда до формата чрез съобщения и „ProcessMessages“ се справя с тях.

Ако мишката слиза на TButton, например, ProgressMessages прави всичко, което трябва да се случи на това събитие, като например пребоядисване на бутона до "натиснато" състояние и, разбира се, повикване към процедурата за обработка на OnClick (), ако сте назначили такъв.

Това е проблемът: всяко обаждане към ProcessMessages може да съдържа рекурсивно повикване към всеки обработващ събитие отново. Ето пример:

Използвайте следния код за равномерния манипулатор на OnClick на бутон ("работа"). Фор-инструкцията симулира дълъг процес на обработка с някои обаждания към ProcessMessages от време на време.

Това е опростено за по-добра четимост:

{в MyForm:}
WorkLevel: цяло число;
{OnCreate:}
WorkLevel: = 0;
процедура TForm1.WorkBtnClick (Подател: TObject);
Var
цикъл: цяло число;
започвам
inc (WorkLevel);
за цикъл: = 1 да се 5 правя
започвам
Memo1.Lines. Добавяне ('- Работа' + IntToStr (WorkLevel) + ', цикъл' + IntToStr (цикъл);
Приложение. ProcessMessages;
сън (1000); // или някаква друга работа
край;
Memo1.Lines. Добавяне („Работа“ + IntToStr (WorkLevel) + „приключи.“);
dec (WorkLevel);
край;

БЕЗ "ProcessMessages" в бележката се записват следните редове, ако бутонът е натиснат ДВАМА за кратко време:

 - Работа 1, цикъл 1
- Работа 1, цикъл 2
- Работа 1, цикъл 3
- Работа 1, цикъл 4
- Работа 1, цикъл 5
Работа 1 приключи.
- Работа 1, цикъл 1
- Работа 1, цикъл 2
- Работа 1, цикъл 3
- Работа 1, цикъл 4
- Работа 1, цикъл 5
Работа 1 приключи.

Докато процедурата е заета, формулярът не показва никаква реакция, но второто щракване е поставено в опашката за съобщения от Windows. Веднага след приключването на "OnClick" той ще бъде извикан отново.

Включително "ProcessMessages", изходът може да бъде много различен:

 - Работа 1, цикъл 1
- Работа 1, цикъл 2
- Работа 1, цикъл 3
- Работа 2, цикъл 1
- Работа 2, цикъл 2
- Работа 2, цикъл 3
- Работа 2, цикъл 4
- Работа 2, цикъл 5
Работа 2 приключи.
- Работа 1, цикъл 4
- Работа 1, цикъл 5
Работа 1 приключи.

Този път формата изглежда работи отново и приема всяко потребителско взаимодействие. Така бутонът се натиска наполовина по време на първата ви функция „работник“ ПРОТИВ, която ще се обработва незабавно. Всички входящи събития се обработват като всяко друго извикване на функция.

На теория по време на всяко обаждане към „ProgressMessages“ може да се случи БЯЛО количество кликвания и потребителски съобщения „на място“.

Така че бъдете внимателни с вашия код!

Различен пример (в прост псевдо-код!):

процедура OnClickFileWrite ();
Var myfile: = TFileStream;
започвам
myfile: = TFileStream.create ('myOutput.txt');
опитвам
докато BytesReady> 0 правя
започвам
моятФайл. Пишете (DataBlock);
dec (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {тестов ред 1}
Приложение. ProcessMessages;
DataBlock [2]: = # 13; {тестов ред 2}
край;
накрая
myfile.free;
край;
край;

Тази функция записва голямо количество данни и се опитва да „отключи“ приложението, като използва „ProcessMessages“ всеки път, когато се записва блок данни.

Ако потребителят натисне бутона отново, ще бъде изпълнен същия код, докато файлът все още се записва. Така файлът не може да бъде отворен 2-ри път и процедурата се проваля.

Може би приложението ви ще направи известно възстановяване на грешки като освобождаване на буферите.

Като възможен резултат "Datablock" ще бъде освободен и първият код "внезапно" ще повдигне "Нарушение на достъпа", когато той получи достъп. В този случай: тестовата линия 1 ще работи, тестовата линия 2 ще се срине.

По-добрият начин:

За да се улесни, можете да зададете целия Формуляр "активиран: = фалшив", който блокира целия потребителски вход, но НЕ показва това на потребителя (всички бутони не са в сиво).

По-добър начин би бил да зададете всички бутони на „деактивирани“, но това може да е сложно, ако искате да запазите един бутон „Отказ“ например. Също така трябва да преминете през всички компоненти, за да ги деактивирате и когато те са активирани отново, трябва да проверите дали трябва да има някои останали в състояние с деактивирани.

Можеш деактивирайте контролите за дете на контейнер, когато се промени свойството Enabled.

Както подсказва името на класа "TNotifyEvent", то трябва да се използва само за краткосрочни реакции на събитието. За отнемащ време код най-добрият начин е IMHO да поставите целия "бавен" код в собствена нишка.

Относно проблемите с "PrecessMessages" и / или активирането и деактивирането на компоненти, използването на a втора нишка изглежда изобщо не е твърде сложно.

Не забравяйте, че дори прости и бързи редове от код могат да висят за секунди, напр. отварянето на файл на дисково устройство може да се наложи да изчака, докато завърши завъртането на устройството. Не изглежда много добре, ако приложението ви изглежда да се срине, защото устройството е твърде бавно.

Това е. Следващият път, когато добавите „Приложение. ProcessMessages ", помислете два пъти;)

instagram story viewer