Jump to content
Nigel Thomas

TShellExecuteInfoA/W incorrectly translated?

Recommended Posts

This is the Delphi 12.1 (and as far back as 10.1 at least, but not in D2007) definition of ShellExecuteInfoW (the A version has the same issue):

_SHELLEXECUTEINFOW = record
    cbSize: DWORD;
    fMask: ULONG;
    Wnd: HWND;
    lpVerb: LPCWSTR;
    lpFile: LPCWSTR;
    lpParameters: LPCWSTR;
    lpDirectory: LPCWSTR;
    nShow: Integer;
    hInstApp: HINST;
    { Optional fields }
    lpIDList: Pointer;
    lpClass: LPCWSTR;
    hkeyClass: HKEY;
    dwHotKey: DWORD;
    case Integer of
      0: (
        hIcon: THandle);
      1: (
        hMonitor: THandle;
        hProcess: THandle;);
  end;

According to the definition in MSDN, the hProcess Handle is not part of the union:

typedef struct _SHELLEXECUTEINFOW {
  DWORD     cbSize;
  ULONG     fMask;
  HWND      hwnd;
  LPCWSTR   lpVerb;
  LPCWSTR   lpFile;
  LPCWSTR   lpParameters;
  LPCWSTR   lpDirectory;
  int       nShow;
  HINSTANCE hInstApp;
  void      *lpIDList;
  LPCWSTR   lpClass;
  HKEY      hkeyClass;
  DWORD     dwHotKey;
  union {
    HANDLE hIcon;
    HANDLE hMonitor;
  } DUMMYUNIONNAME;
  HANDLE    hProcess;
} SHELLEXECUTEINFOW, *LPSHELLEXECUTEINFOW;

Does that mean that the Delphi translation is faulty?

Share this post


Link to post
Posted (edited)

 

44 minutes ago, Nigel Thomas said:

Does that mean that the Delphi translation is faulty?

No.

In Delphi the variant part has to be the last in the record and, if you think about it, the hProcess field would have the same offset even if it was possible to declare it after the variant part.

 

https://docwiki.embarcadero.com/RADStudio/Athens/en/Structured_Types_(Delphi)#Variant_Parts_in_Records

Quote

The variant part must follow the other fields in the record declaration.

 

Edited by Anders Melander
  • Thanks 1

Share this post


Link to post
Posted (edited)
2 hours ago, Nigel Thomas said:

Does that mean that the Delphi translation is faulty?

Technically, no.  The layout is fine.  But, if they had done the "politically correct" thing by extracting the union into a separate type, eg:

_SHELLEXECUTEINFOW_Union = record
  case Integer of
    0: (hIcon: THandle);
    1: (hMonitor: THandle);
end;

_SHELLEXECUTEINFOW = record
  cbSize: DWORD;
  fMask: ULONG;
  Wnd: HWND;
  lpVerb: LPCWSTR;
  lpFile: LPCWSTR;
  lpParameters: LPCWSTR;
  lpDirectory: LPCWSTR;
  nShow: Integer;
  hInstApp: HINST;
  { Optional fields }
  lpIDList: Pointer;
  lpClass: LPCWSTR;
  hkeyClass: HKEY;
  dwHotKey: DWORD;
  u: _SHELLEXECUTEINFOW_Union; // DUMMYUNIONNAME maps to 'u' for C/C++
  hProcess: THandle;           // compilers that don't support nameless unions...
end;

Then referencing the hIcon and hMonitor handles would have to be done as SHELLEXECUTEINFO.u.hIcon instead of as SHELLEXECUTEINFO.hIcon, etc.   The variant part keeps the syntax a little cleaner.

 

Edited by Remy Lebeau
  • Thanks 1

Share this post


Link to post

In the end it comes down to Delphi limitation, that duplicate fields are not allowed in variant types... Because logical translation with being at the end would be:

    case Integer of
      0: (
        hIcon: THandle;
        hProcess: THandle;);
      1: (
        hMonitor: THandle;
        hProcess: THandle;);
  end;

But this duplicate hProcess is not allowed.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×