Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert DECLARE_HANDLE and subsequent consts from windef.h to Delphi

Tags:

winapi

delphi

The following code is from the Windows 10 Anniversary Update SDK. I need the constant handles in order to use the API in Delphi because as of today, these headers are not included in Delphi.

  DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);

  #define DPI_AWARENESS_CONTEXT_UNAWARE           ((DPI_AWARENESS_CONTEXT)-1)
  #define DPI_AWARENESS_CONTEXT_SYSTEM_AWARE      ((DPI_AWARENESS_CONTEXT)-2)
  #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE ((DPI_AWARENESS_CONTEXT)-3)

When I use GetThreadDpiAwarenessContext in various DPI scenarios, I have learned what the values as NativeUInts are:

  DPI_AWARENESS_CONTEXT_UNAWARE = 16;  
  DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17;
  DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18;

But I would like to be 100% certain that these values are future-proof. They do work in SetThreadDpiAwarenessContext calls and have the desired effect, but I am unclear on how these values are arrived at. I could not duplicate a header construct in Delphi that produced those results, aside for the explicit integer declaration.

like image 261
Brandon Staggs Avatar asked Jan 17 '17 23:01

Brandon Staggs


2 Answers

For use in SetThreadDpiAwarenessContext you should declare it as

type
  DPI_AWARENESS_CONTEXT = type THandle;
const
  DPI_AWARENESS_CONTEXT_UNAWARE          = DPI_AWARENESS_CONTEXT(-1);
  DPI_AWARENESS_CONTEXT_SYSTEM_AWARE     = DPI_AWARENESS_CONTEXT(-2);
  DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE= DPI_AWARENESS_CONTEXT(-3);

but when you get response from GetThreadDpiAwarenessContext you need to use GetAwarenessFromDpiAwarenessContext on received value and compare it with DPI_AWARENESS enum.

You cannot directly compare DPI_AWARENESS_CONTEXT since it contains multiple pieces of information and Microsoft may change it in the future.

like image 132
EugeneK Avatar answered Nov 15 '22 21:11

EugeneK


Delphi does not use C/C++ include files at all, so it doesn't matter if the Windows SDK is shipped with the IDE or not, it won't be used in Delphi projects. Now, if you are worried about Delphi not providing Pascal translations of the C header files, that is another story.

DECLARE_HANDLE() is a C preprocessor macro that declares a new data type alias based on the value of the input parameter and whether or not the preprocessor STRICT conditional is defined:

#ifdef STRICT
typedef void *HANDLE;
#if 0 && (_MSC_VER > 1000)
#define DECLARE_HANDLE(name) struct name##__; typedef struct name##__ *name
#else
#define DECLARE_HANDLE(name) struct name##__{int unused;}; typedef struct name##__ *name
#endif
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif

STRICT provides more type-safety to C/C++ code. Many Win32 handle types are opaque types in user code. Declaring them using DECLARE_HANDLE() allows the compiler to treat them as distinct data types when STRICT is defined, thus prevents different handle types from being mixed together erroneously (ie, passing an HBITMAP where an HWND is expected, etc). A lot of developers made that kind of mistake before STRICT was introduced, since all handle types were effectively void*, preventing any compile-time validations.

This means that DECLARE_HANDLE(DPI_AWARENESS_CONTEXT) will declare DPI_AWARENESS_CONTEXT like this:

  1. when STRICT is defined:

    struct DPI_AWARENESS_CONTEXT__{int unused;};
    typedef struct DPI_AWARENESS_CONTEXT__ *DPI_AWARENESS_CONTEXT;
    
  2. when STRICT is not defined:

    typedef void* DPI_AWARENESS_CONTEXT;
    

So, DPI_AWARENESS_CONTEXT is declared as either a pointer to a record type or as an untyped pointer, depending on STRICT.

That kind of name resolution cannot be translated to Delphi, since it does not support preprocessor macros like C/C++ does. The closest translation would be to declare DPI_AWARENESS_CONTEXT directly instead:

type
  {$IFDEF STRICT}
  DPI_AWARENESS_CONTEXT__ = record
  end; 
  DPI_AWARENESS_CONTEXT = ^DPI_AWARENESS_CONTEXT__;
  {$ELSE}
  DPI_AWARENESS_CONTEXT = Pointer;
  {$ENDIF}

Or simply forget the existence of STRICT altogether in Delphi:

type
  DPI_AWARENESS_CONTEXT__ = record
  end; 
  DPI_AWARENESS_CONTEXT = ^DPI_AWARENESS_CONTEXT__;

Either way, once DPI_AWARENESS_CONTEXT is declared, you can then declare its constant values:

const
  DPI_AWARENESS_CONTEXT_UNAWARE = DPI_AWARENESS_CONTEXT(-1);
  DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = DPI_AWARENESS_CONTEXT(-2);
  DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = DPI_AWARENESS_CONTEXT(-3);

Update: based on comments, you can alternatively use this instead:

type
  DPI_AWARENESS_CONTEXT = type THandle;

const
  DPI_AWARENESS_CONTEXT_UNAWARE = DPI_AWARENESS_CONTEXT(-1);  
  DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = DPI_AWARENESS_CONTEXT(-2);
  DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = DPI_AWARENESS_CONTEXT(-3);

Note that these constants are only used for input to SetThreadDpiAwarenessContext(), NOT for output from GetThreadDpiAwarenessContext(). The latter returns an opaque handle to a private memory structure. You need to use GetAwarenessFromDpiAwarenessContext() to retrieve the actual DPI_AWARENESS value from that structure.

like image 26
Remy Lebeau Avatar answered Nov 15 '22 21:11

Remy Lebeau