synopse / mORMot

Synopse mORMot 1 ORM/SOA/MVC framework - Please upgrade to mORMot 2 !
https://synopse.info
787 stars 325 forks source link

SynTaskDialog hyperlinks support; native implementation for non-themed Windows/applications #456

Open the-Arioch opened 3 weeks ago

the-Arioch commented 3 weeks ago

clipboard-202410012027-ujqag

made a "quick dirty change" to add native (not emulation) support for hyperlink and help events

works fine in delphi 2007 / windows 10 and i think should work with other Delphi verions too, the changes are simple

SynTaskDialog-2f92598c.zip


Also, made a funny function to use native TD when visual themes are turned off in the application or in Windows. By default windows only exposes CommCtl 5.8 then and your "normal" code does not find the implementation (that IS present in process memory)

Here is a code sample using two small pieces from JCL that make native implementation found and used even when UI theming tries ot prevent it. If anyone would care implanting it into the "mainline" STD should be straight-forward (back port to stock TStringList and copy/redo the function listing the loaded DLLs into TStrings).

uses JclSysInfo, JclStringLists, StrUtils;

{$EXTERNALSYM GetModuleHandleEx}
{$EXTERNALSYM GET_MODULE_HANDLE_EX_FLAG_PIN}
{$EXTERNALSYM GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT}
{$EXTERNALSYM GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS}
function GetModuleHandleEx(dwFlags: DWORD; lpModuleName: LPCTSTR; var phModule: HMODULE): BOOL; stdcall;
  external kernel32 name 'GetModuleHandleExA';
const
  GET_MODULE_HANDLE_EX_FLAG_PIN                = $00000001;
  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = $00000002;
  GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS       = $00000004;

procedure SynTaskDlgFindGhostComCtl6;
var
  libs: iJclStringList;
  i: integer;
  h: HMODULE;
  f: TFileName;
begin
// <legacy app name> only works with XP Themes off, otherwise it crashes
// This makes Delphi stock TaskDialog be turned off
// This also makes GetModuleHandle(comctl32) return DLL v. 5.80 that has no TaskDlg API
// However the close look at the process memory shows there are TWO DLLs loaded at time!!!

  if Assigned(SynTaskDialog.TaskDialogIndirect) then
     Exit; // Themes were enabled and we're already provided with ComCtl 6.x

  libs := JclStringList();  // Jedi CodeLib
  LoadedModulesList(libs.GetStringsRef, GetCurrentProcessId);  // Jedi CodeLib

  // observed in Win10 that comctl 6.1 is loaded later than conctl 5.8, getting 
  // a higher base address, hence we're scanning memory backwards
  i := libs.Count - 1;
  while i >= 0 do begin
    if Assigned(SynTaskDialog.TaskDialogIndirect) then
       Exit;
    f := libs[i];
    if EndsText(comctl32, f) then begin
       h := NativeUInt(Pointer(libs.Objects[i]));
       SynTaskDialog.TaskDialogIndirect := GetProcAddress(h,'TaskDialogIndirect');

       // now we should create a warranty this DLL never gets unloaded
       if Assigned(SynTaskDialog.TaskDialogIndirect) then
          Win32Check(  GetModuleHandleEx(
              GET_MODULE_HANDLE_EX_FLAG_PIN or GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
              pointer(h), h));
          // LoadLibrary(f); pre-XP way, https://stackoverflow.com/questions/14302483/winapi-getmodulehandle-and-increment-refcount
    end;

    Dec(i);
  end;
end;
the-Arioch commented 1 week ago

Updated flags, added new Microsoft options which your custom options erroneously overlapped with.

tdfNoSetForeground, tdfSizeToContent, SynTaskDialog.zip