Основной поток программы freereason вызвал критическую ошибку
Юрий Балыкин
дата публикации 15-07-2008 03:39
Данная статья предназначена для начинающих программистов, которые никогда не работали с потоками, и хотели бы узнать основы работы с ними. Желательно, чтоб читатель знал основы ООП и имел какой-нибудь опыт работы в Delphi. Для начала давайте определимся, что под словом "поток" я подразумеваю именно Thread, который еще имеет название "нить".
Нередко встречал на форумах мнения, что потоки не нужны вообще, любую программу можно написать так, что она будет замечательно работать и без них. Конечно, если не делать ничего серьёзней "Hello World" это так и есть, но если постепенно набирать опыт, рано или поздно любой начинающий программист упрётся в возможности "плоского" кода, возникнет необходимость распараллелить задачи. А некоторые задачи вообще нельзя реализовать без использования потоков, например работа с сокетами, COM-портом, длительное ожидание каких-либо событий, и т.д.
Всем известно, что Windows система многозадачная. Попросту говоря, это означает, что несколько программ могут работать одновременно под управлением ОС. Все мы открывали диспетчер задач и видели список процессов. Процесс - это экземпляр выполняемого приложения. На самом деле сам по себе он ничего не выполняет, он создаётся при запуске приложения, содержит в себе служебную информацию, через которую система с ним работает, так же ему выделяется необходимая память под код и данные. Для того, чтобы программа заработала, в нём создаётся поток. Любой процесс содержит в себе хотя бы один поток, и именно он отвечает за выполнение кода и получает на это процессорное время. Этим и достигается мнимая параллельность работы программ, или, как её еще называют, псевдопараллельность. Почему мнимая? Да потому, что реально процессор в каждый момент времени может выполнять только один участок кода. Windows раздаёт процессорное время всем потокам в системе по очереди, тем самым создаётся впечатление, что они работают одновременно. Реально работающие параллельно потоки могут быть только на машинах с двумя и более процессорами.
Для создания дополнительных потоков в Delphi существует базовый класс TThread, от него мы и будем наследоваться при реализации своих потоков. Для того, чтобы создать "скелет" нового класса, можно выбрать в меню File - New - Thread Object, Delphi создаст новый модуль с заготовкой этого класса. Я же для наглядности опишу его в модуле формы. Как видите, в этой заготовке добавлен один метод - Execute. Именно его нам и нужно переопределить, код внутри него и будет работать в отдельном потоке. И так, попробуем написать пример - запустим в потоке бесконечный цикл:
Запустите пример на выполнение и нажмите кнопку. Вроде ничего не происходит — форма не зависла, реагирует на перемещения. На самом деле это не так - откройте диспетчер задач и вы увидите, что процессор загружен по-полной. Сейчас в процессе вашего приложения работает два потока - один был создан изначально, при запуске приложения. Второй, который так грузит процессор - мы создали по нажатию кнопки. Итак, давайте разберём, что же означает код в Button1Click:
- tpTimeCritical - критический
- tpHighest - очень высокий
- tpHigher - высокий
- tpNormal - средний
- tpLower - низкий
- tpLowest - очень низкий
- tpIdle - поток работает во время простоя системы
Думаю, теперь вам понятно, как создаются потоки. Заметьте, ничего сложного. Но не всё так просто. Казалось бы - пишем любой код внутри метода Execute и всё, а нет, потоки имеют одно неприятное свойство - они ничего не знают друг о друге. И что такого? - спросите вы. А вот что: допустим, вы пытаетесь из другого потока изменить свойство какого-нибудь компонента на форме. Как известно, VCL однопоточна, весь код внутри приложения выполняется последовательно. Допустим, в процессе работы изменились какие-то данные внутри классов VCL, система отбирает время у основного потока, передаёт по кругу остальным потокам и возвращает обратно, при этом выполнение кода продолжается с того места, где приостановилось. Если мы из своего потока что-то меняем, к примеру, на форме, задействуется много механизмов внутри VCL (напомню, выполнение основного потока пока "приостановлено"), соответственно за это время успеют измениться какие-либо данные. И тут вдруг время снова отдаётся основному потоку, он спокойно продолжает своё выполнение, но данные уже изменены! К чему это может привести - предугадать нельзя. Вы можете проверить это тысячу раз, и ничего не произойдёт, а на тысяча первый программа рухнет. И это относится не только к взаимодействию дополнительных потоков с главным, но и к взаимодействию потоков между собой. Писать такие ненадёжные программы конечно нельзя.
Вот мы и подошли к очень важному вопросу — синхронизации потоков.
Если вы создали шаблон класса автоматически, то, наверное, заметили комментарий, который дружелюбная Delphi поместила в новый модуль. Он гласит: "Methods and properties of objects in visual components can only be used in a method called using Synchronize". Это значит, что обращение к визуальным компонентам возможно только путём вызова процедуры Synchronize. Давайте рассмотрим пример, но теперь наш поток не будет разогревать процессор впустую, а будет делать что-нибудь полезное, к примеру, прокручивать ProgressBar на форме. В качестве параметра в процедуру Synchronize передаётся метод нашего потока, но сам он передаётся без параметров. Параметры можно передать, добавив поля нужного типа в описание нашего класса. У нас будет одно поле - тот самый прогресс:
Вот теперь ProgressBar двигается, и это вполне безопасно. А безопасно вот почему: процедура Synchronize на время приостанавливает выполнение нашего потока, и передаёт управление главному потоку, т.е. SetProgress выполняется в главном потоке. Это нужно запомнить, потому что некоторые допускают ошибки, выполняя внутри Synchronize длительную работу, при этом, что очевидно, форма зависает на длительное время. Поэтому используйте Synchronize для вывода информации - то самое двигание прогресса, обновления заголовков компонентов и т.д.
Вы наверное заметили, что внутри цикла мы используем процедуру Sleep. В однопоточном приложении Sleep используется редко, а вот в потоках его использовать очень удобно. Пример - бесконечный цикл, пока не выполнится какое-нибудь условие. Если не вставить туда Sleep мы будем просто нагружать систему бесполезной работой.
Теперь мы немного изменим, можно сказать даже упростим, реализацию метода Execute нашего потока:
Вот, в принципе, мы и рассмотрели основные способы работы с компонентами VCL из потоков. А как быть, если в нашей программе не один новый поток, а несколько? И нужно организовать работу с одними и теми же данными? Тут нам на помощь приходят другие способы синхронизации. Один из них мы и рассмотрим. Для его реализации нужно добавить в проект модуль SyncObjs.
Самый интересный способ, на мой взгляд — критические секции
Работают они следующим образом: внутри критической секции может работать только один поток, другие ждут его завершения. Чтобы лучше понять, везде приводят сравнение с узкой трубой: представьте, с одной стороны "толпятся" потоки, но в трубу может "пролезть" только один, а когда он "пролезет" - начнёт движение второй, и так по порядку. Еще проще понять это на примере и тем же ProgressBar'ом. Итак, запустите один из примеров, приведённых ранее. Нажмите на кнопку, подождите несколько секунд, а затем нажмите еще раз. Что происходит? ProgressBar начал прыгать. Прыгает потому, что у нас работает не один поток, а два, и каждый из них передаёт разные значения прогресса. Теперь немного переделаем код, в событии onCreate формы создадим критическую секцию:
У TCriticalSection есть два нужных нам метода, Enter и Leave, соответственно вход и выход из неё. Поместим наш код в критическую секцию:
Попробуйте запустить приложение и нажать несколько раз на кнопку, а потом посчитайте, сколько раз пройдёт прогресс. Понятно, в чем суть? Первый раз, нажимая на кнопку, мы создаём поток, он занимает критическую секцию и начинает работу. Нажимаем второй - создаётся второй поток, но критическая секция занята, и он ждёт, пока её не освободит первый. Третий, четвёртый - все пройдут только по-очереди.
Критические секции удобно использовать при обработке одних и тех же данных (списков, массивов) разными потоками. Поняв, как они работают, вы всегда найдёте им применение.
В этой небольшой статье рассмотрены не все способы синхронизации, есть еще события (TEvent), а так же объекты системы, такие как мьютексы (Mutex), семафоры (Semaphore), но они больше подходят для взаимодействия между приложениями. Остальное, что касается использования класса TThread, вы можете узнать самостоятельно, в help'е всё довольно подробно описано. Цель этой статьи - показать начинающим, что не всё так сложно и страшно, главное разобраться, что есть что. И побольше практики — самое главное опыт!
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.
. when altering one's mind becomes as easy as programming a computer, what does it mean to be human.
8 июня 2010 г.
Как узнать, почему зависла программа?
Сегодняшняя статья будет посвящена нескольким подходам к отладке зависаний программы.
Состоит она из шести частей:
- Подготовка - общие действия для всех случаев отладки.
- Delphi - отладка зависания в Delphi.
- EurekaLog - поиск причины зависания в EurekaLog.
- Process Explorer - поиск причины зависания утилитой Process Explorer.
- Threads Snapshot - поиск причины зависания утилитой Threads Snapshot.
- Практический пример - пример с искусственным зависанием в программе.
Ну, прежде чем приступать к отладке программы, вам надо бы её пересобрать (делайте Build, а не просто Compile), добавив в неё отладочную информацию. Для разных подходов требуется разные форматы отладочной информации, поэтому лучше включить всё сразу, чтобы не думать :) Это необходимо сделать, чтобы отладочные средства могли показывать вам читабельные названия процедур и номера строк в исходниках. В противном случае, вам придётся иметь дело с машинным кодом и смещениями.
Итак, вам нужно включить (Project/Options/Linking): "MAP file" - Detailed, "Debug Information" (в старых версиях Delphi она называлась "Include TD32 debug info") - True, "Include remote debug symbols" - True:
Кроме отладочной информации, полезно включить опции "Stack Frames" и "Use Debug DCUs" на вкладке "Compiling":
Уж позвольте мне не повторяться, что делают эти опции.
Не забываем сделать Build после изменения опций. Понятно, что если у вас несколько проектов (DLL, BPL и т.п.), то менять опции и пересобирать надо все. Также, если вы используете сборку с run-time пакетами - по возможности, выключите их на время тестирования, ибо пакеты сильно всё усложняют.
Вы можете подумать: "но я не могу воспроизвести это под отладчиком!" или "на проблемной машине у меня не стоит Delphi!". Но не спешите переходить к следующему разделу.
Во-первых, вам необязательно запускать программу в Delphi. Вы можете запустить программу как обычно, вне среды, и работать с ней, пока она не зависнет - после чего подключить к ней отладчик. Во-вторых, если на машине не стоит Delphi - вы можете установить на неё удалённый отладчик. Подробнее об удалённом отладчике - см. справку или мою статью (большой объём, вот вариант в PDF) - раздел 2, в конце секции 2.1.1. про работу с отладчиком.
Для отладки зависшего проекта в Delphi его, конечно же, нужно открыть. Предварительно он должен быть скомпилирован - с установленными опциями отладки, как мы сделали выше. Кроме того, для локальной машины желательно, чтобы вы запускали программу из Output path, указанном в опциях проекта (т.е. не перемещали бы скомпилированный exe перед запуском).
Итак, вы запустили свою программу, она сколько-то там поработала и зависла. Открываем Delphi, загружаем нужный проект и делаем Run/Attach to process:
Из списка выбираем вашу программу (введя при необходимости имя удалённой машины, если Delphi и программа находятся на разных машинах), устанавливаем галочку "Pause after attach" и жмём "Attach".
Отладчик подключится к процессу и установит его на паузу - путём возбуждения точки останова в потоке отладчика:
Вы можете игнорировать поток отладчика - просто перейдите на окно Threads и выберите любой свой поток для его отладки. Вы можете просмотреть стек вызовов, переключаться между потоками, анализировать переменные, делать пошаговое выполнение и т.п. - в общем, пользоваться отладчиком Delphi как обычно. Если вы включали отладочную информацию, то вы можете закрыть окно CPU и пользоваться отладкой по исходному тексту.
Напомню, что при возможности отладку зависаний стоит производить в ОС Vista и выше - потому что в этих системах появилась новая возможность для отладчиком: Wait Chain Traversal. Отладчик Delphi последних версий поддерживает WCT. Поэтому, если вы используете BDS 2009 или выше и Windows Vista или выше, то в окне Threads напротив каждого потока в колонке "Wait Chain" можно увидеть его статус, чего он ждёт, есть ли взаимоблокировка и т.п.:
В EurekaLog есть фишка "Anti-Freeze". Собственно, её я уже разбирал в отдельной статье, поэтому не буду останавливаться сейчас - у нас и так сегодня куча материала.
Если же по каким-то причинам среда Delphi для вас недоступна - вам придётся производить отладку руками.
Перед тем, как производить отладку, надо подготовить Process Explorer. Здесь будет два шага - оба опциональных, но для максимального удобства лучше сделать оба.
Шаг первый - настроить Process Explorer на загрузку отладочных символов. Дело в том, что Windows поставляется с обычными исполняемыми модулями без отладочной информации. Для своих программ мы только что включили генерацию отладочной информации (см. первый пункт), то как мы можем сделать это для Windows, чьи исходники нам недоступны? Ну, Microsoft позаботилась об этом: она распространяет отладочную информацию для своих программ отдельно. Вы можете скачать её и получить читабельные стеки вызовов.
Для начала вам понадобится скачать и установить Windows Debugging Tools. Затем, вам нужно решить, качать ли всю отладочную информацию скопом или же пусть она качается по запросу отладочной программы. Если вы выбрали первый путь - то вперёд, качаем и устанавливаем. Лично я выбираю второй путь.
Для второго способа вам нужно создать папку на своей машине с правом чтения-записи файлов и папок в ней. После чего остаётся только настроить Process Explorer:
В первом поле указывается путь к DbgHelp.dll - если вы устанавливали Windows Debugging Tools, то берите библиотеку оттуда. Если нет - то берите C:\Windows\System32\dbghelp.dll (однако я не уверен, будет ли это работать).
После того, как вы это настроили, Process Explorer будет пытаться получить отладочную информацию о каждом необходимом файле и кэшировать её в указанной папке. Поэтому, когда вы просматриваете потоки процесса или их стек вызовов, вы можете иногда видеть надпись "Loading symbols for ABC.exe+0xXYZ. ". В общем, после этого вам станет доступно больше информации для системных модулей.
Второй момент, который нужно сделать - сконвертировать map-файл вашего проекта в формат, понимаемый Process Explorer. Дело в том, что Delphi создаёт только различные Borland-ские форматы отладочной информации, а Process Explorer, как утилита Microsoft, понимает только Microsoft-ские форматы отладочной информации. Я уже говорил об этом. Сделать это можно утилитой map2dbg. Это простая консольная утилитка. Качаете архив, распаковываете, открываете консоль и пишете:
По файлам Project1.exe и Project1.map утилита сделает вам файл Project1.dbg, который может быть использован в Microsoft-ских утилитах.
Что-ж, я тут много чего сказал. Давайте я продемонстрирую, что вы получаете, выполнив указанные выше вещи. Ниже - три скриншота. Слева направо: вид стека вызовов без выполнения обоих пунктов (т.е. без системной и без проектной отладочной информацией), вид стека вызовов с первым пунктом (с системной, но без проектной отладочной информацией) и вид стека вызовов с обоими пунктами (с системной и с проектной отладочной информацией):
Как видите, подключение отладочной информации даёт вам три вещи:
- Более читабельный стек вызовов (вместо имя-модуля+смещение вы получаете имя-модуля!процедура+смещение)
- Более полный стек вызовов (без отладочной информации эвристика трассировки стека может опускать вызовы)
- Более правдивый стек вызовов (анализатор может неверно определять имя функции, если идёт вызов внутренней функции, которая не имеет публичного имени, но рядом находится другая функция, которая как-раз таки имеет публичное имя - поэтому анализатор может посчитать внутреннюю функцию частью публичной)
Если вы не будете (или не сможете) подключать отладочную информацию - вам придётся работать со смещениями. Вы конечно, можете искать смещения в map-файле руками, но это весьма хлопотно. Гораздо проще просто выписать на бумажку все числа, приписав имя модуля. Затем запускаете проект у себя, ставите его на паузу и используете команду Search/Goto address. Вводите адрес - и Delphi переводит вас на строчку в исходном тексте, а если это невозможно - то открывает окно CPU. Какой вводить адрес? Два примера. Вы выписали Project1.exe + $1234 и Project2.dll + $4321. Базовый адрес exe обычно не меняется и равен $400000. Вы загрузили проект у себя. Базовый адрес exe тот-же - $400000, а вот DLL оказалась загруженной по адресу $50000000 (вы можете выяснить это в окне Modules: View/Debug Windows/Modules). Тогда вас интересуют адреса $400000 + $1234 = $401234 и $50000000 + $4321 = $50004321.
Фух, разобрались. Как видите, намного удобнее подключать отладочную информацию, чем работать без неё :)
Теперь, что вы собственно должны делать для отладки зависания. Ну, вы запускаете свою программу и работаете с ней, пока она не зависнет. Потом вы запускаете Process Explorer, выбираете в списке процессов свою программу, правый щелчок -> Properties (свойства). В свойствах процесса переходим на вкладку Threads (потоки):
Здесь вы можете посмотреть, чем занимаются потоки вашей программы. Кто кушает процессор, кто чего-то ждёт и т.п. Выберите поток и нажмите кнопку "Stack" для просмотра его стека вызова (пример окна - см. три скриншота чуть выше).
Конечно, вы не сможете посмотреть переменные или что-то такое - только состояние и точки выполнения потоков. Так что вам придётся использовать свои телепатические способности, чтобы определить причину зависания.
Ну, использование Process Explorer хотя и несложно, но не выглядит таковым. Для новичка тут много работы и новых понятий. Да и вся эта возня с отладочной информацией не слишком удобна. Поэтому я написал простую утилитку (сейчас - часть EurekaLog Tools), которая позволяет вам выбрать запущенный процесс и дампит в текстовый файл информацию по всем потокам, включая:
- Базовая инфа: ID, приоритет и т.п.
- Стек вызова. Используются следующие источники отладочной информации: Borland-ские, JCL, EurekaLog и madExcept. Чуть позже добавлю поддержку Microsoft-ских и закачку по запросу, как у Process Explorer.
- Информация от Wait Chain Traversal (на Windows Vista и выше).
- Контекст потока - регистры и флаги.
Собственно, вы запускаете свою проблемную программу и работаете в ней, пока она не зависнет. Потом запускаете утилиту Threads snapshot и тыркаете её на зависший процесс. Она снимет вам снимок потоков, который вы сможете проанализировать.
Как видите - достаточно простая и удобная альтернатива ручной отладке с Process Explorer. Дополнительный плюс - вы можете попросить вашего клиента снять вам снимок процесса на его машине. В случае же с Process Explorer-ом - навряд ли вы сможете объяснить клиенту, что куда ставить и где жать. Не забудьте только передать все необходимые файлы (map-файлы, например).
Я написал эту утилиту буквально только что за два дня. Поэтому она "немножко" сыровата. Нормальная и отлаженная версия этой утилиты должна войти в EurekaLog v7.
Я написал простую программу с двумя кнопками. Вы можете использовать её как обучающий пример. Первая кнопка создаёт несколько потоков, которые ждут друг друга. Вторая кнопка вызывает порчу памяти, приводящую к зависанию. Запустите программу, нажмите на кнопку и попробуйте выяснить причину зависания. Посмотрим, как мы сможем отладить эти два случая.
В Delphi: если у вас есть поддержка WCT, то отладка первой кнопки вообще не представляет сложностей: запустили, нажали, поставили программу на паузу и смотрим окно Threads:
Если же WCT у вас нет, то придётся поработать головой и руками. Вы должны проанализировать стеки вызова каждого потока: щёлкаете по потокам в окне "Threads" и смотрите стеки вызовов:
После переключения на поток открывается окно CPU с текущей выполняемой инструкцией, но вы можете щёлкать по строчкам в окне "Call stack", чтобы посмотреть исходный код.
Проанализировав стеки вызовов всех трёх потоков, вы составите картину произошедшего.
Как эта же ситуация выглядит в Process Explorer и Threads snapshot? Ну, вы запускаете Process Explorer и смотрите стеки потоков (у меня первый поток открывался около минуты, не знаю, с чем связано - потом пошло гладко):
Хотя вы не видите здесь номеров строк, но вы видите имена функций и последовательность вызовов - например, в примере выше обратите внимание на Thread1Func, различные dispatch-функции, CriticalSection.Aquire и т.п. И снова: вы смотрите стеки вызова всех потоков и реконструируете ситуацию. Сделать это будет сложнее, чем используя Delphi (ибо нет номеров строк), но с известной долей телепатии - вполне возможно.
Что касается Threads snapshot, то она даёт такой лог (я отрезал лишние части для уменьшения лога):
К сожалению, вывод WCT пока не очень форматирован (это моё домашнее задание! :) ), но цепочку ожиданий увидеть можно. Плюс стеки потоков (к сожалению, без системной отладочной информации - это моё второе домашнее задание!) самым решительным образом намекают на происходящее.
Второй случай сложнее. Потому что зависание -лишь внешнее проявление другой проблемы.
Итак, в Delphi вы запускаете программу, она виснет, мы ставим её на паузу. У нас только один поток, поэтому WCT нам не помощник, даже если он есть. Поэтому сразу переключаемся на главный поток и смотрим стек вызовов:
Опять-таки: щёлкая по стеку вызовов, мы видим исходный код.
В этот раз, простой анализ стека вызовов нам не помогает. Пока что неясно, что же случилось. Тем более, что программа вроде бы не висит, а что-то делает (т.е., строго говоря, у нас не зависание, а зацикливание). Поэтому мы начинаем пошаговый прогон программы. Пройдясь по коду мы видим, что код постоянно крутит цикл со Sleep, проверяя некий флаг. Мы видим, что этот код - код менеджера памяти FastMM. Почитав комментарии в коде, мы узнаём, что FastMM пытается получить блокировку. А проверяемый флаг - это признак занят/свободен. Поскольку FastMM проверяет этот флаг уже полчаса - ясно, что тут что-то не так. Этот же вывод следует из того, что в нашёй программе всего один поток - т.е. ждать-то и вовсе некого, кроме нас в программе никого нет. Иными словами, состояние флага не соответствует действительности - т.е. он испорчен вследствие повреждения памяти.
К сожалению, найти повреждение памяти не так-то просто и это отдельная тема, которую я недавно закончил обсуждать.
Поэтому, задача анализа зависания - выяснить причину. Мы её нашли: это - повреждение памяти. Дальше, анализ зависания закончен, но проблема пока не найдена и не устранена - мы переходим к анализу и поиску проблем с памятью.
Собственно, второй случай выглядит примерно аналогично везде (в Delphi, Process Exporer и Threads snapshot): мы получаем примерно один и тот же стек вызовов, который может меняться время от времени, но всегда это будет цикл и с вероятностью в 99% - стоять на Sleep. Делается просто несколько проверок, чтобы в этом убедиться. Для примера - вот как это выглядит в Process Explorer:
Напомню, что из этого мы вынесем только вывод (не однозначный, конечно же, а всего лишь обоснованное предположение), что у нас есть повреждение памяти. Дальше - это уже другая история.
Фух. На сегодня у меня всё. Надеюсь, этот материал был полезен. Если хотите, вот ещё дополнение - презентация по ручному поиску места ошибки по адресу (см. "How to find the exception source line"). На английском, но может пригодится или будет интересно.
Закрытие дочернего окна вызывает закрытие программы
Здравствуйте! Не могу никак разобраться, как сделать так, чтобы дочернее окно при его закрытии не.
Закрытие файла вызывает падение программы
Подскажите, в чём дело. Создаю два двоичных файла со списками слов, перед каждым словом - пишу.
еременная которой нигде нет(не описана) не вызывает ошибку в большом инете, а на локале вызывает ошибку
Совсем я ничего не понимаю. Переменная которой нигде нет(не описана) не вызывает ошибку в большом.
DateTime имеет формат MM.MM.yyyy H:mm:ss, что и вызывает ошибку
Проблема в том, что при вводе даты в формате 13.05.2014 16:13:34 появляется ошибка: При вводе.
Пока до таймера дело не доходит.
- Не загружен шрифт FontID(1)
- StartDrawing/StopDrawing не определён, куда рисовать то?
Все ошибки пишет среда PUreBasic внизу экрана, просто читайте и исправляйте.
В том то и дело, что код проходит проверку синтаксиса и после запуска никаких ошибок, а тупо вылет.
Значит я пока настолько тупой, что не понял принцип рисования в окне. (((
Буду разбираться. Подскажите, направите на путь правильный?
Спасибо!
StartDrawing выполняется довольно медленно, потому желательно выносить по возможности за пределы длинных циклов,
внутри циклов только сами операторы рисования.
Кол-во StartDrawing должно быть равно кол-ву StopDrawing. Нет. Спасибо! Буду изучать. Хотя у меня там много намешано.
Стыдно весь код представить ) Ожидание запуска исполняемого файла.
Тип исполняемого файла: Windows - x86 (32bit, Unicode)
Запущен исполняемый файл.
[ОШИБКА] Строка: 27
[ОШИБКА] StartDrawing() должна быть вызвана перед другой 2DDrawing функцией. Всё получилось. Спасибо!
Только не придумал как сделать пропорции для часовой и минутной стрелки от разрешения экрана. При небольших размерах вроде нормально выглядят, а на высоких часовая слишком длинная.
Это самая первая моя программа на PureBasic.
Clock_acco.7z Не знал. Изучал коды и везде видел объявление переменной. Пришёл к выводу, что все примеры очень древние и их приходится допиливать. Так и изучаю PB ) Так же есть встроенные функции перевод градусов в радианы и обратно. Тут просто - Бейсик вычисления делает в радианах, а не в градусах. Даже на советских калькуляторах был такой переключатель "рад-гр", там тоже в формулах обычно радианы использовались.
Непонятно ведёт себя при выборе данного скринсейвера и при нажатии кнопки Просмотр - запускается два раза. Тут есть специфика построения screensaver. Строго говоря не достаточно просто переименовать exe в scr.
Помимо некоторых правил, то как выход по нажатии любой кнопки, мышки, и запрета на запуск второй копии, ещё
есть некая внутренняя структура, которая управляется с ком. строки(ProgramParameter()), которую нужно правильно обработать:
Вот код с английского форума, демонстрирующий выбор. Т.е система командует параметрами, а ваша программа должна их правильно выполнять! Не система организует Просмотр, а вы должны это действие предусмотреть, как и конфигурирование параметров. Или всё правильно игнорировать, если нет настроек.
В интернете много информации на это счет, разберётесь.
Не критично, StartDrawing вызывается не в главном цикле, а по таймеру.
Но последний StopDrawing() явно лишний!
в строке 1 создаю изображение, начинаю рисование, потом в строке 3 завершаю рисование.а в строке 7 опять начинаю. Но зачем? Чтобы получить что? В хэлпе об этом ничего не сказано ((( в строке 1 создаю изображение, начинаю рисование, потом в строке 3 завершаю рисование.а в строке 7 опять начинаю. Но зачем? Первое рисование по сути бессмысленно у вас. Там нужно только CreateImage, а Box по таймеру рисуется дальше в коде.Width, Heght тоже в начале кода уже вычислили, заново нет смысла его вычислять.
Начало может быть таким:
Переписал в отдельные процедуры рисование циферблата и рисование стрелок. Рисование стрелок вызываю по таймеру. Но приходится вызывать и рисование циферблата, и рисование стрелок каждую секунду.
И задумался. А можно ли один раз нарисовать один циферблат, поместить на него мой логотип, а сверху расположить Image с прозрачным фоном и на нём каждую секунду перерисовывать стрелки?
Чтобы каждую секунду не перерисовывать всё окно чтобы стереть старые стрелки (Создание Image, наложение логотипа, рисование циферблата)?
Попытался, но изображение каждую секунду моргает. (((
ну так есть же грабёж, грабим маленький участок под стрелкой и возвращаем его
есть CustomFilterCallback(), что будет проще или удобней в этом случае?, надо пробовать оба варианта и делать свой выбор
стрелки кривоватые, может стрелки вектором рисовать?
если не вектором, то найти как рисуется линия со Зглажыванием
Пользователи опасаются, что из-за задержки с вводом в эксплуатацию нового трубопровода "Газпром" этой зимой не поставит европейским потребителям достаточно топлива.
"Северный поток-2" полностью построен (строительство завершилось в сентябре), и его даже заполнили газом, но эксплуатация газопровода может начаться только после сертификации в Германии. А этот процесс приостановлен, поскольку, согласно немецким законам, оператору предстоит создать и зарегистрировать дочернюю компанию для части линии, работающей в Германии.
Эта операционная компания должна соответствовать немецкому законодательству, прежде чем проект стоимостью 10 миллиардов евро получит сертификацию.
Немецкий регулирующий орган заявил, что процедура сертификации будет приостановлена до тех пор, пока швейцарская материнская компания Nord Stream 2 не передаст основные активы и людские ресурсы своей дочерней компании в Германии, которая владеет и управляет немецкой частью трубопровода.
Решение, вероятно, отложит сертификацию на несколько месяцев, но и это еще не все: "Северный поток-2" должен получить зеленый свет от Европейской комиссии.
Немецкие предприятия вложили значительные средства в трубопровод протяженностью 1225 км, и бывший канцлер Герхард Шредер сыграл большую роль в его развитии.
"Северный поток-2", проходящий по дну Балтийского моря, удвоит экспорт газа из Москвы в Германию, но при этом идет в обход Украины, которая во многом полагается на существующие трубопроводы для получения доходов и сильно пострадает от потери транзитных сборов.
Между тем, по данным немецкого сетевого оператора Gascade, поток российского газа по трубопроводу "Ямал-Европа" (один из основных маршрутов экспорта российского газа в Европу) в Германию в среду утром был стабильным и превысил уровень выходных.
Однако, несмотря на это, декабрьский нейтральный индекс цен на газ виртуальной бирже TTF (Title Transfer Facility), по которому рассчитывается средневзвешенная по объему цена на газ в Европе, к утру среды вырос на 6%.
Правда, рост цен на газ начался не вчера. Холодная зима в Европе в прошлом году отразилась на объемах поставок, и в результате уровень хранимого газа намного ниже обычного.
Политическое оружие
Критики опасаются, что трубопровод увеличит энергетическую зависимость Европы от России.
Украина выступала против "Северного потока-2", который президент Владимир Зеленский назвал "опасным геополитическим оружием".
Многие начинающие пользователи сталкиваются с такой проблемой:
"Прекращена работа программы . "
И многих эта проблема раздражает.
Сейчас я вам расскажу,как справится с этой проблемой.
Подробности
Для начала разберёмся с возможными вариантами,из-за чего эта трабла возникает :
1. Установлено много стороннего ПО,которое "ест" ресурсы системы.
2. Программе не хватает оперативной памяти.
3. В системе не установлено необходимое ПО для "правильной" работы программы.
5. Проблема в самой программе.
6. При запуске программа обращается к какому-нибудь системному файлу,который может быть повреждён.
Теперь пройдёмся по каждому этому варианту:
1. Посмотрите будет ли программа вылетать в режиме "чистой" загрузки ,если в этом режиме всё нормально работает,то попробуем выявит виновника,среди всего установленного ПО, с помощью метода "половинного деления".
Зайдите в Конфигурацию системы -> Службы и включите половину служб и перезагрузитесь. Если проблема не появляется, причина в оставшихся отключенных службах. Если проблема воспроизводится, причина во включенных службах — отключите половину из них и снова перезагрузитесь. Тоже самое и для ПО в Автозагрузке.
2. Убедитесь,что у вас включён файл подкачки,для этого:
а) Нажмите Пуск –> Панель управления –> Система –> Все элементы панели управления –> Дополнительные параметры системы -> Дополнительно:
б) В разделе Быстродействие нажмите Параметр,откройте вкладку Дополнительно и нажмите Изменить;
в) И посмотрите,чтобы стояла галочка напротив надписи "Автоматически выбирать объём файла подкачки".
3. Убедитесь,что у вас установлено следующее ПО:
Для 32 (x86) bit'ных систем :
Для 64 bit'ных систем :
Потом после их установки установите все обновления,которые будут в Центре обновления Windows !
4. Проверьте систему на наличие "зловредов" с помощью Dr.Web CureIt.
5. Проблема может быть в самой программе:
а) Если у вас установлена пиратская версия программы (взломанная , RePack),то обращайтесь к тому,у кого вы ею скачали;
б) Если у вас установлена Beta-версия программы,удалите её и найдите законченную версию программы у разработчика :
в) Если у вас лицензионная версия программы,то обращайтесь в тех. поддержку производителя.
6. Определим,кто виноват в вылете программы,для этого:
а) Скачайте программу ProcDump и распакуйте её в папку C:\ProcDump;
б) Откройте командную строку от имени администратора и выполните:
- C:\ProcDump\procdump.exe -accepteula -e -w [имя сбойного приложения] C:\ProcDump\
в) Как определить имя сбойного приложения:
1) зайдите в Панель управления -> Все элементы панели управления -> Центр поддержки ->Монитор стабильности системы -> Отчеты о проблемах.
2) Найдите событие,когда вылетело проблемное приложение,щёлкните по нему 2 раза левой кнопкой мыши и там вы увидите надпись "Имя приложения:
в) Запустите это приложение и дождитесь вылета.
г) После этого у вас появится файл с расширением .dmp в C:\ProcDump
д) Теперь заглянем в это дам (заглядывать в него можно также,как и и в дампы синих экранов Анализ причин возникновения BSOD при помощи Debugging Tools for Windows (только команда выгладит по другому: Kdfe -v [путь к дампу]).
е) Как определите,что за файл виноват - определите системный ли он или принадлежит сторонней программе (для этого достаточно его "погуглить ") ,если к сторонней программе,то определите к какой и удалит её.
Если файл системный,то запустите командную строку от имени администратора и выполните команду:
Дождитесь конца проверки и:
Если в конце проверки будет написано,что все файлы были восстановлены,то перезагрузитесь для их полного восстановления.
Если в конце проверки будет написано,что не все файлы были восстановлены,то:
Если у вас Windows 8/8.1,то вам достаточно в командной строке,запущенной от имени администратора, при подключённом интернете , выполнить команду:
Если у вас Windows 7,то обратимся к другой статье ( пишется ) за помощью.
Читайте также: