Jump to page: 1 2
Thread overview
WindowsAPI - Problem with DECLARE_HANDLE definition
Sep 08, 2013
Stewart Gordon
Sep 09, 2013
Mike Parker
Sep 09, 2013
Vladimir Panteleev
Sep 09, 2013
Vladimir Panteleev
Sep 11, 2013
Kagamin
Sep 11, 2013
Andrej Mitrovic
Sep 19, 2013
Kagamin
Sep 19, 2013
Andrej Mitrovic
Sep 19, 2013
Kagamin
Sep 11, 2013
Simen Kjaeraas
Sep 19, 2013
Kagamin
Sep 20, 2013
Simen Kjaeraas
September 08, 2013
It has just come to my attention that there's a problem with the DECLARE_HANDLE template in the Win32 bindings.

This is the definition in MinGW:

    #define DECLARE_HANDLE(n) typedef struct n##__{int i;}*n

And this is the definition in our bindings:

    package template DECLARE_HANDLE(string name, base = HANDLE) {
        mixin ("struct " ~ name ~ " {
            " ~ base.stringof ~ " h;
            alias h this;
        }");
    }

which when mixed in becomes something like

    struct HWND {
        HANDLE h;
        alias h this;
    }

The idea behind this was to effectively create a taxonomy of handle types, each implicitly convertible to handle types higher up the chain.  This was straightforward when we had typedefs.

The problem is that null no longer works.  How to fix?  Ideas that come to mind:


1. Define a hierarchy of dummy classes for the handle types.  No actual objects will exist of these types, but since classes are reference types they can be set to null.

But there's a nasty bug lurking in this: if somebody tries to compare handles using ==, it will dereference the pointer, and look in vain for the vtable and the opEquals method defined therewithin ... cue major chaos.


2. Do 1, but use pointers to these classes as the handle types.

    class HANDLE_ {}
    alias const(HANDLE_)* HANDLE;
    class HWND_ : HANDLE_ {}
    alias const(HWND_)* HWND;

This would avoid the dereferencing behaviour.  It's to be hoped that all Windows programmers know that, although handles are declared as pointer types, they cannot meaningfully be dereferenced.  But what would the GC do, especially given that there are two levels of indirection neither of which points to an appropriate memory location?

Moreover, will defining classes in the bindings cause object code to be generated for them, which the program will later rely on in order to link?  This is something I am trying to get rid of completely.


3. Keep the current implementation, but implement an enum member NULL in each handle type, like this:

    struct HWND {
        HANDLE h;
        alias h this;
        enum HWND NULL = cast(HWND) 0;
    }

Programmers still can't use null, but writing HWND.NULL might be acceptable as the next best thing.


4. Abandon this hierarchy idea and just define DECLARE_HANDLE the same way as the MinGW C headers do.


What do people think we should do?

Stewart.

-- 
My email address is valid but not my primary mailbox and not checked regularly.  Please keep replies on the 'group where everybody may benefit.
September 09, 2013
On 9/9/2013 8:52 AM, Stewart Gordon wrote:
>
> What do people think we should do?
>

Eliminate declare handle and alias all HANDLE types to void*.
September 09, 2013
On Sunday, 8 September 2013 at 23:52:46 UTC, Stewart Gordon wrote:
> 1. Define a hierarchy of dummy classes for the handle types.  No actual objects will exist of these types, but since classes are reference types they can be set to null.
>
> But there's a nasty bug lurking in this: if somebody tries to compare handles using ==, it will dereference the pointer, and look in vain for the vtable and the opEquals method defined therewithin ... cue major chaos.

This is a showstopper. It breaks existing code in subtle ways.

> 2. Do 1, but use pointers to these classes as the handle types.
>
>     class HANDLE_ {}
>     alias const(HANDLE_)* HANDLE;
>     class HWND_ : HANDLE_ {}
>     alias const(HWND_)* HWND;
>
> This would avoid the dereferencing behaviour.  It's to be hoped that all Windows programmers know that, although handles are declared as pointer types, they cannot meaningfully be dereferenced.  But what would the GC do, especially given that there are two levels of indirection neither of which points to an appropriate memory location?

This is fine as far as the GC goes. Error messages are not great, though: "cannot implicitly convert expression (h) of type const(HANDLE_)* to const(HWND_)*". Not sure about object code - one possibility would be to fix DMD to support class declarations (e.g. "class HWND_ : HANDLE_;" with no body).

> 3. Keep the current implementation, but implement an enum member NULL in each handle type, like this:
>
>     struct HWND {
>         HANDLE h;
>         alias h this;
>         enum HWND NULL = cast(HWND) 0;
>     }
>
> Programmers still can't use null, but writing HWND.NULL might be acceptable as the next best thing.

This is not much better than writing HWND.init (which is how I've been working around the issues).

> 4. Abandon this hierarchy idea and just define DECLARE_HANDLE the same way as the MinGW C headers do.

Supporting the functionality of the STRICT define would be a good start; I don't think that involves any hierarchies. I think an important goal is to avoid breaking most correct code and maximize compatibility with C code, so using a special literal for null is out.

By the way, another problem with the current implementation is casts. At the moment, they can get quite verbose, and confusing to implement (one needs to look up the hierarchy of the handle types).

For example, calling LocalFree with a LPCWSTR as when using FormatMessage now looks like this:

	LocalFree(HLOCAL(HANDLE(lpMsgBuf)));

Having the handle types be some kind of pointer would allow using a more conventional casting syntax.
September 09, 2013
On Monday, 9 September 2013 at 01:25:00 UTC, Mike Parker wrote:
> On 9/9/2013 8:52 AM, Stewart Gordon wrote:
> >
> > What do people think we should do?
> >
>
> Eliminate declare handle and alias all HANDLE types to void*.

I think this is the lazy and shortsighted answer. Even the official Windows headers have a strict typing mode (the STRICT define), which is recommended for new applications:

http://msdn.microsoft.com/en-us/library/windows/desktop/aa383731(v=vs.85).aspx

Most code would benefit from additional compile-time checks, as the number of places where a cast is required is small. Stewart is attempting to reduce this number even further by defining a handle type hierarchy, something which isn't possible in C but might be possible in D.
September 11, 2013
On Sunday, 8 September 2013 at 23:52:46 UTC, Stewart Gordon wrote:
> The problem is that null no longer works.  How to fix?

I'd say, strong handles shouldn't act as pointers (and shouldn't contain pointers), so null shouldn't work, I use HANDLE(0) instead of null. Use void* for maximum compatibility (weak handles).
September 11, 2013
On 9/11/13, Kagamin <spam@here.lot> wrote:
> I'd say, strong handles shouldn't act as pointers (and shouldn't contain pointers), so null shouldn't work.

"NULL" is already used in a ton of WinAPI C/C++ code and MSDN documentation, it would be a major pain in the ass to have to use e.g. HWND(0) instead.
September 11, 2013
On 2013-09-11, 20:29, Andrej Mitrovic wrote:

> On 9/11/13, Kagamin <spam@here.lot> wrote:
>> I'd say, strong handles shouldn't act as pointers (and shouldn't
>> contain pointers), so null shouldn't work.
>
> "NULL" is already used in a ton of WinAPI C/C++ code and MSDN
> documentation, it would be a major pain in the ass to have to use e.g.
> HWND(0) instead.

How's about

  enum NULL = HANDLE(0);

Honestly I hate that, and am convinced the correct way to fix this is
for D to add opImplicitCastFrom.

-- 
  Simen
September 19, 2013
On Wednesday, 11 September 2013 at 18:29:09 UTC, Andrej Mitrovic wrote:
> On 9/11/13, Kagamin <spam@here.lot> wrote:
>> I'd say, strong handles shouldn't act as pointers (and shouldn't
>> contain pointers), so null shouldn't work.
>
> "NULL" is already used in a ton of WinAPI C/C++ code and MSDN
> documentation, it would be a major pain in the ass to have to use e.g.
> HWND(0) instead.

In my experience writing a winapi call already takes some effort and specifying HANDLE(0) instead of NULL doesn't add any comparable difficulty.
September 19, 2013
On Wednesday, 11 September 2013 at 20:20:13 UTC, Simen Kjaeraas wrote:
> On 2013-09-11, 20:29, Andrej Mitrovic wrote:
>
>> On 9/11/13, Kagamin <spam@here.lot> wrote:
>>> I'd say, strong handles shouldn't act as pointers (and shouldn't
>>> contain pointers), so null shouldn't work.
>>
>> "NULL" is already used in a ton of WinAPI C/C++ code and MSDN
>> documentation, it would be a major pain in the ass to have to use e.g.
>> HWND(0) instead.
>
> How's about
>
>   enum NULL = HANDLE(0);
>
> Honestly I hate that, and am convinced the correct way to fix this is
> for D to add opImplicitCastFrom.

Or multiple alias this:

struct _NULL
{
	HANDLE zero;
	void* ptr;
	alias zero this;
	alias ptr this;
}
September 19, 2013
On 9/19/13, Kagamin <spam@here.lot> wrote:
> In my experience writing a winapi call already takes some effort and specifying HANDLE(0) instead of NULL doesn't add any comparable difficulty.

It adds difficulty in having to fix existing code that will be broken.
« First   ‹ Prev
1 2