Komunikaty nie docierają, aplikacja zminimalizowana, Delphi

0

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ąć.

0

Przechwytywanie WM_QUERYENDSESSION ?

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));

omg! tak się to robi:

if not (Msg.message in [15, 280, 512, 160, 1125, 275, ....]) then 
  Memo2.Lines.Add(IntToStr(Msg.message));
0

Witam

Uwaga jak najbardziej słuszna - tak się powinno to robić dzięki za szybką reakcję.
Nadal jednak nie wiem nic więcej o moim podstawowym problemie.

Pozdrawiam z Wrocławia.

0

odpowiedź masz w podanym przeze mnie linku. tam forumowicz miał identyczny problem.

0

Sz.P.
Ale sprawdziłem kod z linka i nie rozwiązuje on mojego problemu.

Kompilator : DELPHI 4 Professional

System operacyjny : Win 7 Ultimate - ze względu na brak dostepu do pozostałych kompów testuję tylko na tym systemie.

Aktualnie kod programu zawiera :

type
  TForm1 = class(TForm)
  ...
  ...
private
    { Private declarations }
    procedure WMQueryEndSession(var Message: TMessage); message WM_QUERYENDSESSION;
    procedure WndProc(var Message: TMessage);override;
  public
   { Public declarations }
  end;

oraz sekcja implementation

implementation

uses Unit2;

procedure TForm1.WMQueryEndSession(var Message: TMessage);
  begin
     // SessionEnding := True;   - to wyremowałem bo mi kompilator wywalał error ->  Undeclared identifier: 'SessionEnding'
     Message.Result := 1;
     Memo1.Lines.Add(' WMQueryEndSession >>> WM_QUERYENDSESSION ');
  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.ResultLo:=1;
                                            End
                                            else
                                            Begin
                                             Memo1.Lines.Add(' WndProc - Message >>> WM_ENDSESSION');
                                             Message.Result:=0;
                                             //Application.Terminate;
                                            End;


   end;
   inherited WndProc(Message);
end;

Zachowanie programu jest identyczne jak wcześniej opisałem ( zminimalizowany odbiera tylko komunikat WM_ENDSESSION) ale wcześniej blokuje zamykanie systemu operacyjnego.

1

z tego, co widzę w internecie minimalizacja nie jest źródłem problemu, kłopoty sprawiać mogą dodatkowe biblioteki (np. http://www.logresource.com/Question/2122468/showall/#additionalAnswersMarker).
spróbuj stworzyć pusty program, który za zadanie będzie mieć tylko złapanie WM_QUERYENDSESSION plus logowanie tego lub wyświetlenie stosownego komunikatu. dalsza ścieżka działań zależy od tego, czy program zadziała.

rzuć też okiem na http://www.delphigroups.info/2/8/326023.html.

0

Witam

Zgodnie z Pana sugestią stworzyłem prosta aplikację : Forma a na niej :button + memo dwa checkBoxy .
Kod programu:

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    procedure WMQueryEndSession(var Message: TMessage); message WM_QUERYENDSESSION;
    procedure WndProc(var Message: TMessage);override;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  s : String;

implementation

procedure TForm1.WMQueryEndSession(var Message: TMessage);
  begin
    // SessionEnding := True;
     s:=ExtractFileDir(Application.ExeName);
     Memo1.Lines.Add(' WMQueryEndSession >>> WM_QUERYENDSESSION ');
     Memo1.Lines.SaveToFile(s+'\Test0.txt');
     if Checkbox1.Checked then Message.Result := 0
                          else Message.Result := 1;
  end;

{$R *.DFM}
procedure TForm1.WndProc(var Message: TMessage);
begin
  if (Message.Msg = (WM_QUERYENDSESSION)) or (Message.Msg = (WM_ENDSESSION)) then
    begin
       s:=ExtractFileDir(Application.ExeName);
      if Message.Msg = (WM_QUERYENDSESSION) then
                                            Begin
                                              Memo1.Lines.Add(' WndProc - Message >>> WM_QUERYENDSESSION');
                                              Memo1.Lines.SaveToFile(s+'\Test1.txt');
                                              if Checkbox1.Checked then Message.Result := 0
                                                                   else Message.Result := 1;
                                            End
                                            else
                                            Begin
                                             Memo1.Lines.Add(' WndProc - Message >>> WM_ENDSESSION');
                                             Memo1.Lines.SaveToFile(s+'\Test2.txt');
                                             if Checkbox2.Checked then Message.Result := 0
                                                                  else Message.Result := 1;
                                             //Application.Terminate;
                                            End;


   end;
   inherited WndProc(Message);
end;

Zgodnie z Pana oczekiwaniami i ku mojemu zdziwieniu aplikacja zachowuje się normalnie to znaczy zamyka się również gdy jest zminimalizowana.
Dodałem CheckBoxy dla kontroli odpowiedzi na komunikaty oraz zapis do pliku przychodzących komunikatów(tych które nas interesują).

Dzięki Pana pomocy jestem o kroczek dalej w rozpoznaniu problemu ale nadal nie wiem co z tą wiedzą mam teraz uczynić ?
Co Pan proponuje dalej z tym zrobić ?

P.S.
Mam jeszcze pytanie techniczne .
Dlaczego wklejany kod programu u mnie jest jednokolorowy, a Pana ma kolorowaną składnię ?

0

dodawaj do nowej aplikacji po kawałku kod z aplikacji starej. w którymś momencie reagowanie na komunikat przestanie działać - będzie wiadomo, który kawałek kodu jest za to odpowiedzialny. podejrzewam jakąś zewnętrzną bibliotekę.
nie zwracaj się do mnie per "Pan". Łukasz jestem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1