Добрый день.Подскажите пожалуйста,как сделать,чтобы при закрытии программы программа снималась из памяти,наподобие того,как ее снимает Диспетчер задач
Заранее спасибо
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
30-04-2008 13:42 | Комментарий к предыдущим ответам
По-моему, моего телепатора никто не послушал. А ведь мне кажется, он правильно мыслит. То есть надо искать ошибку в программе, что она не освобождает, куда суется, что процесс остается висеть. Вот Word например, если кто-то им попользуется, а потом корректно не освободит, то он остается висеть в памяти. Вот что с ним делать? Ответ - правильно писать программы и тщательнее отлаживать.
30-04-2008 07:20 | Комментарий к предыдущим ответам
С событиями понятно - в критической секции для ожидания использует событие (Event). Проверка захвата критической секции осуществляется функцией EnterCriticalSection. При необходимости, она "переинициализирует" критическую секцию.
Если использовать напрямую Event, то это сделать некому.
30-04-2008 07:09 | Комментарий к предыдущим ответам
Да в точности та же ситуация, о которой мы говорим, только замените критические секции на любой другой объект синхронизации.
Любой? А если взять мьютекс?
30-04-2008 06:45 | Комментарий к предыдущим ответам
Во, у меня ещё остался тестовый код:
library Project1;
uses
Windows, SysUtils;
var
// Test: TRTLCriticalSection;
Test: THandle;
NeverHappen: Boolean;
procedure MyDLLProc(Reason: Integer);
begin
if (Reason = DLL_PROCESS_DETACH) or (Reason = DLL_THREAD_DETACH) then
begin
OutputDebugString('Ready?');
// EnterCriticalSection(Test);
if WaitForSingleObject(Test, INFINITE) <> WAIT_OBJECT_0 then
OutputDebugString(PChar(SysErrorMessage(GetLastError)));
OutputDebugString('Passed');
// Сюда мы доходим только в случае критической секции
OutputDebugString('Exit from DllProc');
end;
end;
function TestThread(P: DWord): DWord; stdcall;
var
X: Integer;
begin
OutputDebugString('Before Lock');
// EnterCriticalSection(Test);
if WaitForSingleObject(Test, INFINITE) <> WAIT_OBJECT_0 then
OutputDebugString(PChar(SysErrorMessage(GetLastError)));
OutputDebugString('Locked');
SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_LOWEST);
X := 0;
while not NeverHappen do
X := X + 1;
OutputDebugString('Oops!');
Result := X;
end;
Суть та же: если в приложении имеются работающие потоки, вызывать ExitProcess не безопасно, т.к. может возникнуть deadlock из-за того, что поток может быть прерван в то время, как он держит блокировку.
30-04-2008 05:24 | Комментарий к предыдущим ответам
>>> Например.
Что значит "например"? Вы что, не знаете других объектов синхронизации, помимо критических секций? :) События, например. Да и вообще любые объекты ядра, которые можно использовать с WaitForSingleObject.
30-04-2008 04:19 | Комментарий к предыдущим ответам
От себя замечу, что проблемы это не решает, т.к. во-первых, другие способы блокировки всё же приведут к блокировке процесса.
Например.
А во-вторых, вместо deadlock мы получаем, что глобальные ресурсы, которые были защищены критической секцией, во время ExitProcess оказываются в испорченном состоянии.
Поэтому в Windows Vista вместо имитации успешного захвата секции сразу вызывается NtTerminateProcess.
30-04-2008 04:15 | Комментарий к предыдущим ответам
Провёл проверку: действительно, критические секции во время ExitProcess не блокируются.
От себя замечу, что проблемы это не решает, т.к. во-первых, другие способы блокировки всё же приведут к блокировке процесса. А во-вторых, вместо deadlock мы получаем, что глобальные ресурсы, которые были защищены критической секцией, во время ExitProcess оказываются в испорченном состоянии.
30-04-2008 01:26 | Комментарий к предыдущим ответам
Не исключён сценарий, когда поток останавливается TerminateThread в момент вызова GetMem (т.е. в момент блокировки менеджера памяти), а затем при DLL_PROCESS_DETACH происходит попытка освобождения памяти => мы получаем блокировку и повисание процесса при выходе.
Чтобы избежать блокировки на критической секции, захваченной другим потоком во время ExitProcess, в функцию EnterCriticalSection был добавлен код, обрабатывающий эту ситуацию.
Начиная с Windows XP EnterCriticalSection проверяет захвачена ли секция и, в случае если захвачена, сверяет идентификатор текущего потока с идентификатором захватчика.
Windows XP/Windows 2003: Вместо бесконечного ожидания, текущий поток назначается владельцем критической секции, так как если бы она была захвачена нормальным способом. Тем самым программа сможет продолжить выполнение вплоть до финального вызова NtTerminateProcess.
Но данное решение проблемы оказалось не очень безопасным. Поэтому в Windows Vista вместо имитации успешного захвата секции сразу вызывается NtTerminateProcess.
Я не раз встречал такое со своим проектом, но при одних и тех же обстоятельствах.
Например, взять FIBDataset и сделать по умолчанию опцию FetchAll = True (порядка 500 записей). Взять Devrace SQL Monitor. При запуске проекта с запущенным SQL Monitor-ом проект нормально не выгружается.
Без SQL Monitor-а все отлично выгружается.
27-04-2008 11:58 | Комментарий к предыдущим ответам
>>> не проще ли вызвать ExitProcess
Кстати говоря, это не всегда может быть безопасно:
All of the threads in the process, except the calling thread, terminate their execution. The entry-point functions of all loaded dynamic-link libraries (DLLs) are called with DLL_PROCESS_DETACH. After all attached DLLs have executed any process termination code, this function terminates the current process, including the calling thread.
Т.е. при ExitProcess вызывается TerminateThread для каждого потока. Затем вызываются DLL с DLL_PROCESS_DETACH. Не исключён сценарий, когда поток останавливается TerminateThread в момент вызова GetMem (т.е. в момент блокировки менеджера памяти), а затем при DLL_PROCESS_DETACH происходит попытка освобождения памяти => мы получаем блокировку и повисание процесса при выходе.
Я бы стал искать ошибку в первую очередь в OnClose, OnCloseQuery, OnDestroy главной формы, а также временно удалил бы все сторонние компоненты, в том числе и самопальные. Потом можно приступить к комментированию процедур, функций, методов, которые что-то занимают или создают (резервируют место в памяти, создают потоки и так далее) Когда найдете код, вызывающий ошибки, публикуйте его - будем думать.
25-04-2008 03:33 | Комментарий к предыдущим ответам
Откуда вы это взяли? При принудительном уничтожении процесса просто не приходят Я вел речь о потоках, а не процессах. Пример. Приложение работает с СОМ-портом, используя отдельный поток, в котором никаких окон. Закрываем приложение нормальным Close-ом, а перед этим делаем TerminateThread потоку порта и закрытие самого порта - получаем "мертвое" окно в таскбаре. Которое впрочем при щелчке на нем (если не ошибаюсь - 10 лет прошло) пропадает.
25-04-2008 03:11 | Комментарий к предыдущим ответам
>>> В 9Х наличие кнопки на таскбаре означает, что какие-то ресурсы для этой задачи Винда дала, но обратно не получила.
Откуда вы это взяли? При принудительном уничтожении процесса просто не приходят HSHELL_WINDOWDESTROYED для его окон. В итоге в этом случае остаются «мертвые» кнопки на таскбаре.
25-04-2008 02:56 | Комментарий к предыдущим ответам
Не уловил связь между кнопкой на панели задач и стеком потока. В 9Х наличие кнопки на таскбаре означает, что какие-то ресурсы для этой задачи Винда дала, но обратно не получила. Висит в памяти исполняемый код (который то ли выполняется, то ли вообще не получает тиков) либо остались неосвобожденными ресурсы - без разницы! След от задачи остался - завершение некорректно. Множественный перезапуск сродни DOS-атаке, т.е. разрушение ОС по исчерпании ресурсов. Сильно сказал! :-)
Вопрос автора вполне актуален был для меня недавно.
В программе есть таймер, который по срабатыванию должен выгрузить приложение (свое).
В программе рутинная работа с файлами и MemTable.
Так вот довольно сложно сделать Application.terminate из таймера когда "идет работа".
И тут самый простой способ:
var
HWND_P,HWND_Proc,pid:Cardinal;
begin
HWND_P:=form1.handle;
GetWindowThreadProcessId(HWND_P,pid);
HWND_Proc := OpenProcess(PROCESS_TERMINATE, FALSE, pid);
TerminateProcess(HWND_Proc,4);
end;
25-04-2008 02:09 | Комментарий к предыдущим ответам
2 NS
Читая пост Василия под "следами" после завершения программы при некорректной работе с потоками я подумал про то, что процесс остается висеть в памяти (по различным причинам).
К проблеме "кнопок на панели задач" мой пост [25-04-2008 00:49] не относится.
Этого не наблюдал и утверждать не могу.
2 BlackHole
>>>Пример того, что после закрытия программы она остается висеть в списке процессов?
Я не знаю. Василий сказал
>>>Раньше (в 9Х виндах) если неграмотно завершать потоки - оставались "следы" от программ
Вы сказали
>>>И в w2k/хр такая ситуация возможна
Я не понял, о каких следах речь, вот и попросил пояснить. Ну не обязательно пример, хотя-бы описание ситуации. О кнопке на панели задач Александр Алексеев уже сказал. Еще что-то?
25-04-2008 01:50 | Комментарий к предыдущим ответам
остается висеть в списке процессов
Ждем автора, т.к. телепатор именно это предположил при прочтении фразы чтобы при закрытии программы программа снималась из памяти.
25-04-2008 01:46 | Комментарий к предыдущим ответам
>>> после закрытия программы она остается висеть в списке процессов
Хм, значит программа ещё не завершилась. Скорее всего, либо она сама повисла в процессе обычного выхода, либо ей что-то мешает выйти, например:
TerminateProcess initiates termination and returns immediately. This stops execution of all threads within the process and requests cancellation of all pending I/O. The terminated process cannot exit until all pending I/O has been completed or canceled.
25-04-2008 01:40 | Комментарий к предыдущим ответам
Если поток, сгенеренный CreateThread-ом, завершить TerminateThread-ом (естественно после закрытия основной программы) - в ТаскБаре задача оставалась (в смысле панелька). Я так понял тогда - из-за некорректного высвобождения стека под поток.
Не уловил связь между кнопкой на панели задач и стеком потока.
При использовании TerminateThread стек потока не освобождается.
Он освобождается при завершении программы.
25-04-2008 01:27 | Комментарий к предыдущим ответам
>>> из-за некорректного высвобождения стека под поток.
Неверно. При завершении программы, вся занятая память освобождается. После закрытия программы никаких стеков потоков существовать не может. В этот момент единственное, что может остаться от программы - открытые описатели её процесса, которые можно использовать только для того, чтобы получить код завершения процесса.
>>> в ТаскБаре задача оставалась
И как это связано с самой программой? Просто Shell проморгал момент завершения программы и не убрал панельку.
25-04-2008 01:19 | Комментарий к предыдущим ответам
Раньше (в 9Х виндах) если неграмотно завершать потоки - оставались "следы" Какие? Один пример...Если поток, сгенеренный CreateThread-ом, завершить TerminateThread-ом (естественно после закрытия основной программы) - в ТаскБаре задача оставалась (в смысле панелька). Я так понял тогда - из-за некорректного высвобождения стека под поток.
25-04-2008 00:49 | Комментарий к предыдущим ответам
2 Василий
что делают?
Я имел ввиду - сами завершаются при закрытии без какой-либо посторонней помощи.
Раньше (в 9Х виндах) если неграмотно завершать потоки - оставались "следы" от программ, а в w2k и хр - не видел этого.
И в w2k/хр такая ситуация возможна.
25-04-2008 00:42 | Комментарий к предыдущим ответам
Все добропорядочные программы так и делают. - что делают? То что написано в предыдущих постах??? В жизни этого не делал и вообще удивлен наличием этой проблемы. Раньше (в 9Х виндах) если неграмотно завершать потоки - оставались "следы" от программ, а в w2k и хр - не видел этого.
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.