Dzień Dobry
Szanowni Państwo
Mam problem i zwracam się do was z proźbą o pomoc. (jestem amatorem programowania)
Kompilator : DELPHI 4 Professional
System operacyjny : Win XP Pro Sp2
Win XP Pro sp3
Win 7 Ultimate
Win 2008 64bit to wszystkie systemy na których sprawdzałem zachowanie aplikacji.
Opis problemu.
Aplikacja działa cały czas przetwarzając dane przychodzące po UDP (komponent NetMaster NMUDP). Po zaniku zasilania UPS wymusza zamknięcie systemu operacyjnego i wraz z nim wszystkich aplikacji. Aplikacja przed zamknięciem wykonuje zatrzymanie odbioru ramek UDP, wyłączenie socket'ów , zapis danych z buforów oraz zapis konfiguracji. Fragment kodu, który za to odpowiada :
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
Plik_Ini : TIniFile;
i1 : Byte;
begin
Plik_Ini:=TIniFile.Create(G_S2+'\KonfigKS.InI');
Plik_Ini.WriteString('PROGRAM','Wersja',Title);
Plik_Ini.WriteString('INTERNET','Port UDP',ComboBox1.Text);
Plik_Ini.WriteString('INTERNET','Port TCP',ComboBox2.Text);
NMUDP1.Destroy;
if ServerSocket1.Active then Plik_Ini.WriteBool('USTAWIENIA','Serwer' ,True)
else Plik_Ini.WriteBool('USTAWIENIA','Serwer' ,False);
Serversocket1.Close;
ServerSocket1.Destroy;
// Zapis ustawień aplikacji
Plik_Ini.WriteInteger('USTAWIENIA','Okno X',Form1.Left);
Plik_Ini.WriteInteger('USTAWIENIA','Okno Xs',Form1.width);
...
...
Plik_Ini.Free;
// Zapis ustawień aplikacji - KONIEC
if Form1.Wyjscie_Awaryjne = 0 then // gdy 0 to normalne zamknięcie programu
BEGIN // Opróżniam bufory z analizą danych w buforach
Speed:=1; // maksymalna prędkość analizy
Timer1.Enabled:=True; // uruchomienie analizy
While Dane_Bufora>0 do begin
Application.ProcessMessages;
ProgressBar1.Position:=Dane_Bufora;
Aktualizuj_Opis_Bufora;
end;
Timer1.Enabled:=False; // zatrtzymanie analizy po opróżnieniu bufora
END;
CanClose:=True;
end;
Całość działa doskonale jest jednak jedno ale :) ...
Gdy okno programu jest widoczne wszystko działa zgodnie z planem. Problem pojawia się gdy okno aplikacji jest zminimalizowane (dowolną metodą : pokaż pulpit , menu systemowe programu , menu paska zadań). Aplikacja blokuje wtedy zamknięcie systemu operacyjnego i nie wykonuje żadnych działań. Przeszukałem Forum i znalazłem kilka propozycji rozwiązania tej niedogodności jednak żadna nie działa prawidłowo. Dodałem do programu taki kod:
type
TForm1 = class(TForm)
...
...
private
{ Private declarations }
procedure WndProc(var Message: TMessage);override;
procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
public
{ Public declarations }
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
...
...
Application.OnMessage := AppMessage;
...
...
end;
procedure TForm1.WndProc(var Message: TMessage);
begin
if (Message.Msg = (WM_QUERYENDSESSION)) or (Message.Msg = (WM_ENDSESSION)) then
begin
if Message.Msg = (WM_QUERYENDSESSION) then
Begin
Memo1.Lines.Add(' WndProc - Message >>> WM_QUERYENDSESSION');
Message.Result:=1;
End
else
Begin
Memo1.Lines.Add(' WndProc - Message >>> WM_ENDSESSION');
Message.Result:=0;
//Application.Terminate;
End;
end;
inherited WndProc(Message);
end;
procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
if Msg.message = $0011 then
begin
Memo1.Lines.Add(' AppMessage >>> WM_QUERYENDSESSION = $0011');
Handled := True;
end
else if Msg.message<> 15 then
if Msg.message<>280 then
if Msg.message<>512 then
if Msg.message<>160 then
if Msg.message<>1125 then
if Msg.message<>275 then
if Msg.message<>49392 then
if Msg.message<>161 then
if Msg.message<>49395 then
if Msg.message<>5 then
if Msg.message<>49288 then
if Msg.message<>49394 then
if Msg.message<>49393 then
if Msg.message<>49392 then
if Msg.message<>513 then
if Msg.message<>514 then
if Msg.message<>260 then
if Msg.message<>49214 then
if Msg.message<>257 then
if Msg.message<>45057 then
if Msg.message<>45056 then
if Msg.message<>45082 then
if Msg.message<>274 then
if Msg.message<>49398 then Memo2.Lines.Add(IntToStr(Msg.message));
end;
Rozumiem że kolejność działań przy zamykaniu systemu operacyjnego lub wylogowywaniu usera jest taka:
- system wysyła do wszystkich aplikacji komunikat : WM_QUERYENDSESSION i oczekuje na odpowiedź.
- jeśli wszystkie aplikacje odpowiedzą TRUE - system wie, że może zacząć się zamykać i wysyła do wszystkich aplikacji komunikat WM_ENDSESION.
Do mojej aplikacji gdy jest zminimalizowana komunikat WM_QUERYENDSESSION nie dociera (lub nie potrafię go wykryć). Co ciekawe gdy system wyświetli monit że program blokuje zamknięcie systemu a user wybierze opcję anuluj w oknie aplikacji widać że dotarł do niej komunikat WM_ENDSESSION ??? - w jaki sposób skoro nie dotarł wcześniej WM_QUERYENDSESSION ?
Od czterech dni próbuję coś z tym zrobić i bez efektu. Nie wiem czy nie popełniam jakiegoś logicznego błędu w swoim rozumowaniu ze względu na niewielką znajomość zachowań systemu operacyjnego. Proszę o śmiałe odpowiedzi (nawet z bluzgami na temat mojej wiedzy w ww zakresie)
Proszę o ile to możliwe o informację:
Gdzie popełniam błąd ?
Jak doprowadzić program do stanu "normalności" zachowań?
Fragmenty kodu mile widziane.
P.S.
Kolega napisał podobny program w Delphi 7 i u niego nie ma takich problemów , niezależnie od stanu programu zawsze pozwala systemowi operacyjnemu się zamknąć.