Jump to content

Sherlock

Moderators
  • Content Count

    1209
  • Joined

  • Last visited

  • Days Won

    25

Posts posted by Sherlock


  1. Well I am just glad, that my applications seem to be barely more complex than "Hello World" because I really have none of these issues. But then again I use no inline variables, bpl, dll, 3rd party frameworks or language enhancements or any other new agey stuff I'm not able to wrap my head around anyway. Just plain old Delphi plus generics. And that works really good.

    • Like 1

  2. 8 hours ago, Nigel Thomas said:

    And if it's not me being dense, how to I go about fixing it given that I can't modify TRegistry?

    That is easy and the solution comes in two parts: 1. Create an Issue on the quality portal. And b) Copy the source into your projects folder or any other folder where you would keep reusable units. Then fix the issue and add said folder to your search path to use the modified unit in your project.

    • Thanks 1

  3. 12 hours ago, Ian Branch said:

    Hi Guys,

    I am going to try each and see which is my best option.

     

    @Sherlock Where is this from - "Message.Unused", Delphi tells me "Message" is an undeclared identifier.

     

    Regards,

    Ian

    Sorry, turns you do have to be careful when you copy & paste, edit and post code all willy-nilly. That "Message" is just the parameter of the message handler so in this case the abbreviated "msg".


  4. Well, OnCloseQuery is simply not enough. You need to answer the OSes "Will shut down now" Message with a "Wait for me" and clear that "Wait for me" after you are done all the while keeping your main thread responsive to more OS messages that will come because Windows may not just take your word that you'll say "carry on" once you are ready, it will check on you...

    So what it boils down to is the following (if your application does not take longer than 30 seconds to do what needs to be done your golden, otherwise you'll need to be threading):

    type
      TForm1 = class(TForm)
        procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
      private
        procedure WMQueryEndSession(var Msg: TMessage); message WM_QUERYENDSESSION;
        procedure WMEndsession(var Msg: TMessage); message WM_ENDSESSION;
      public
        { Public-Deklarationen }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    var
      _ShutdownBlockReasonCreate: function(WindowHandle: HWND; Reason: LPCWSTR): UINT; stdcall;
      _ShutdownBlockReasonDestroy: procedure(WindowHandle: HWND); stdcall;
    
    function ShutdownBlockReasonCreate(WindowHandle: HWND; Reason: LPCWSTR): UINT;
    var
      UserLib: THandle;
    begin
      if @_ShutdownBlockReasonCreate = nil then
      begin
        UserLib := GetModuleHandle(Windows.User32);
        if UserLib <> 0 then
          @_ShutdownBlockReasonCreate := GetProcAddress(UserLib, 'ShutdownBlockReasonCreate');
      end;
      if @_ShutdownBlockReasonCreate <> nil then
        Result := _ShutdownBlockReasonCreate(WindowHandle, Reason)
      else
        Result := 1;
    end;
    
    procedure ShutdownBlockReasonDestroy(WindowHandle: HWND);
    var
      UserLib: THandle;
    begin
      if @_ShutdownBlockReasonDestroy = nil then
      begin
        UserLib := GetModuleHandle(Windows.User32);
        if UserLib <> 0 then
          @_ShutdownBlockReasonDestroy := GetProcAddress(UserLib, 'ShutdownBlockReasonDestroy');
      end;
      if @_ShutdownBlockReasonDestroy <> nil then
        _ShutdownBlockReasonDestroy(WindowHandle);
    end;
    
    { TForm1 }
    
    
    procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    begin
      // Whatever needs to be done... 
    end;
    
    procedure TForm1.WMEndsession(var Msg: TMessage);
    begin
      // Windows now informs you it will end your session
      // Free up your BlockReason
      ShutdownBlockReasonDestroy(Handle);
    end;
    
    procedure TForm1.WMQueryEndSession(var Msg: TMessage);
    const
      ENDSESSION_CLOSEAPP = $00000001; // lParam - WM_QUERYENDSESSION
      ENDSESSION_CRITICAL = $40000000;
      ENDSESSION_LOGOFF = $80000000;
    var
      CanClose: Boolean;
    begin
      // Windows asks if it may end your session, your response is
      // "No, and here's what you can tell the user why"
      if ((Message.Unused and ENDSESSION_CRITICAL) = 0) then
      begin
        if ShutdownBlockReasonCreate(Handle, 'Saving application data...') = 0 then
        begin
          SaveLog(SysErrorMessage(GetLastError));  //Assuming you have some kind of logging
        end;
        // Now do your thing i.e. call FormCloseQuery
        FormCloseQuery(Self, CanClose);
        If CanClose then
          Close; // Woohoo
      end
      else 
      begin
        // Forcefully shutdown...no time to wait
      end;
    end;

    I have something like this in an application and it has worked for me since Win7. Not tested on Win11 yet though.


  5. I wonder where you might want those dots to show up? After a package is installed, editing its path seems rather pointless. And that dialog looks just the same on my system. So I thought maybe you have some strange dpi settings, that confuse the dialog. But there seems to be everything OK with it. I'm checking for the official docs to see if they have a screenshot.

×