Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi NonVCL: SplashScreen (https://www.delphipraxis.net/168798-nonvcl-splashscreen.html)

wicht 12. Jun 2012 08:50

NonVCL: SplashScreen
 
Guten Morgen!

Am Wochenende war mir langweilig und ich dachte, ich baue mal einen Splash-Screen. Weil er "animiert" sein soll, während im Hintergrund das Main-Formular lädt, habe ich mich entschieden, einen TThread-Nachfahren zu bauen, der das Fenster erstellt und verwaltet. Ja, ich weiß, dass der Hauptthread im optimalen Falle den Splash erstellt und zum Laden der Daten ein Thread benutzt werden sollte, aber ein bisschen mit der API spielen macht ja auch mal Spass. Ich erstelle also das Fenster und habe meine WindowProc, die poste ich mal:

Delphi-Quellcode:
function WindowProc(hwn, msg, wpr, lpr: Longint): Longint; stdcall;
var
  H: HWND;
  Val: Cardinal;
  TC: Cardinal;
  Msg2: TMsg;
  P: TPoint;
begin
  case msg of
    WM_TIMER:
      begin
        TC := GetTickCount;
        if AnimationStart = 0 then
          AnimationStart := GetTickCount;

        H := GetMainWindowWindow;
        case State of
          stFadeIn:
            begin
              Val := Trunc(((TC - AnimationStart) / FADE_TIME) * 254);

              if TC - AnimationStart < FADE_TIME then
              begin
                BlendFunction.SourceConstantAlpha := Val;
                UpdateLayeredWindow(SplashWndHandle, 0, nil, @BitmapSize, SplashBitmap.Canvas.Handle, @SplashBitmapPos, 0, @BlendFunction, ULW_ALPHA);
              end else
              begin
                WaitingStarted := TC;
                State := stWaiting;
              end;
            end;
          stWaiting:
            begin
              if ((H > 0) and IsWindowVisible(H)) or ((TC - AnimationStart) > 2000) then
              begin
                State := stFadeOut;
                AnimationStart := 0;
              end;
            end;
          stFadeOut:
            begin
              Val := 254 - Trunc(((TC - AnimationStart) / FADE_TIME) * 254);

              if TC - AnimationStart < FADE_TIME then
              begin
                BlendFunction.SourceConstantAlpha := Val;
                // Das hier gibt manchmal Fehler 317 bei GetLastError() zurück?
                UpdateLayeredWindow(SplashWndHandle, 0, nil, @BitmapSize, SplashBitmap.Canvas.Handle, @SplashBitmapPos, 0, @BlendFunction, ULW_ALPHA);
              end else
              begin
                SplashBitmap.Free;
                KillTimer(SplashWndHandle, 0);
                Killed := True;
                if H > 0 then
                  SetForegroundWindow(H);
              end;
            end;
        end;
        SetWindowPos(SplashWndHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE);
      end;
  end;
  Result := DefWindowProc(hwn, msg, wpr, lpr);
end;
Wie man sieht läuft das Fadein/Fadeout mit einem Timer. Das Problem habe ich mit einem Kommentar markiert. Der Fadein funktioniert gut, aber beim Fadeout gibt es manchmal den Fehler 317, was immer der hier bedeuten mag... Kann dazu vielleicht jemand etwas mehr sagen? Es sieht eben blöd aus, wenn der Screen schön einfadet aber beim Ausblenden bei 50% Transparenz "stehen bleibt" und dann direkt verschwindet, wenn das Hauptfenster da ist.
Eine andere Sache ist, ob man so etwas überhaupt mit einer WindowProc machen sollte. Ich habe in einem Beispiel gesehen, dass CreateWindowEx() aufgerufen wurde und dann direkt mit UpdateLayeredWindow() gearbeitet wurde. Ist das sinniger, wenn man nicht mehr als Fadein/Fadeout machen möchte? Und ist das ungefährlich?


Liebe Grüße und danke im Vorraus für eventuelle Antworten :)

himitsu 12. Jun 2012 09:08

AW: NonVCL: SplashScreen
 
Wenn du den SplashScreen im Thread erstellst (nicht im Hauptthread) und im Threrad natürlich eine MessageLoop (Nachtichtenverarbeitung) enthalten ist, dann läuft das Fenster vollkommen unabhängig von der VCL und es gibt auch keine Probleme mit dem Multithreading und vorallem nicht mit eventuellen langen Pausen im Huptthread.

Mit dem Thread wo das CrerateWindow aufgerufen wurde, wird die Messagebehandlung des Controls verbunden und alles was über die MessageQueue läuft, läuft dann in der MessageQueue dieses Threads und auch die ganzen internen Prozesse deiser Komponente laufen dann in diesem Thread.

VCL-Komponenten müssen nur im Hauptthread laufen, da sie intern gemeinsame und nicht threadsichere Resourcen der VCL verwenden, wie z.B. irgendwelche Listen wo sie sich registrieren oder gemeinsam genutzte Fonts usw.

Luckie 12. Jun 2012 09:44

AW: NonVCL: SplashScreen
 
Code:
C:\Dokumente und Einstellungen\mp>net helpmsg 317
317 ist keine gültige Windows-Netzwerknachrichtsnummer.

himitsu 12. Jun 2012 10:25

AW: NonVCL: SplashScreen
 
Delphi-Referenz durchsuchenSysErrorMessage oder Delphi-Referenz durchsuchenRaiseLastOSError (Delphi-Referenz durchsuchenRaiseLastWin32Error)

Zitat:

ERROR_MR_MID_NOT_FOUND

317 (0x13D)

The system cannot find message text for message number 0x%1 in the message file for %2.

Luckie 12. Jun 2012 11:42

AW: NonVCL: SplashScreen
 
ja, das wollte ich jetzt nicht noch mal extra programmieren. Ich dachte net helpmsg würde auch helfen.

wicht 12. Jun 2012 11:43

AW: NonVCL: SplashScreen
 
Danke für die Antworten!

"ERROR_MR_MID_NOT_FOUND" habe ich dazu auch gefunden - aber was soll mir das im Zusammenhang mit UpdateLayeredWindow() sagen? Das macht für mich überhaupt keinen Sinn...

himitsu 12. Jun 2012 12:01

AW: NonVCL: SplashScreen
 
Eventuell ja nur ein Threaddingfehler oder ein Folgefehler?

wicht 12. Jun 2012 12:11

AW: NonVCL: SplashScreen
 
Mein Code dazu sah so aus:

Delphi-Quellcode:
if not UpdateLayeredWindow(SplashWndHandle, 0, nil, @BitmapSize, SplashBitmap.Canvas.Handle, @SplashBitmapPos, 0, @BlendFunction, ULW_ALPHA) then
  OutputDebugString(PChar(IntToStr(GetLastError)));

himitsu 12. Jun 2012 13:17

AW: NonVCL: SplashScreen
 
Folgefehler: Innerhalb eines API-Aufrufs werden ja oftmals auch wieder weitere APIs/Funktionen aufgerufen ... der Folgefehler muß also nicht in deinem Code liegen.


Ansonsten macht sich direkt der Fehlertext einfach schöner.
Delphi-Quellcode:
OutputDebugString(PChar(SysErrorMessage(GetLastError)));

// bzw.

E := GetLastError;
OutputDebugString(PChar(IntToStr(E) + ': ' + SysErrorMessage(E))); // IntToStr und SysErrorMessage rufen auch WinAPIs auf

wicht 12. Jun 2012 14:08

AW: NonVCL: SplashScreen
 
Ja, da hast du wohl recht.. nur dem auf die Schliche zu kommen macht das natürlich nicht einfacher... :(


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:42 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz