digitalmars.D - WindowsAPI - Problem with DECLARE_HANDLE definition
- Stewart Gordon (53/53) Sep 08 2013 It has just come to my attention that there's a problem with the DECLARE...
- Mike Parker (2/3) Sep 08 2013 Eliminate declare handle and alias all HANDLE types to void*.
- Vladimir Panteleev (10/15) Sep 08 2013 I think this is the lazy and shortsighted answer. Even the
- Vladimir Panteleev (23/52) Sep 08 2013 This is fine as far as the GC goes. Error messages are not great,
- Kagamin (5/6) Sep 11 2013 I'd say, strong handles shouldn't act as pointers (and shouldn't
- Andrej Mitrovic (4/6) Sep 11 2013 "NULL" is already used in a ton of WinAPI C/C++ code and MSDN
- Kagamin (5/13) Sep 19 2013 In my experience writing a winapi call already takes some effort
- Andrej Mitrovic (2/5) Sep 19 2013 It adds difficulty in having to fix existing code that will be broken.
- Kagamin (3/3) Sep 19 2013 I don't think strong handles are backwards compatible: legacy
- Simen Kjaeraas (7/13) Sep 11 2013 How's about
- Kagamin (10/25) Sep 19 2013 Or multiple alias this:
- Simen Kjaeraas (29/54) Sep 20 2013 If you want opaque handles, that does not solve the problem. What would
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 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.
Sep 08 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*.
Sep 08 2013
On Monday, 9 September 2013 at 01:25:00 UTC, Mike Parker wrote:On 9/9/2013 8:52 AM, Stewart Gordon wrote: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.What do people think we should do?Eliminate declare handle and alias all HANDLE types to void*.
Sep 08 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.
Sep 08 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).
Sep 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.
Sep 11 2013
On Wednesday, 11 September 2013 at 18:29:09 UTC, Andrej Mitrovic wrote:On 9/11/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.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.
Sep 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.
Sep 19 2013
I don't think strong handles are backwards compatible: legacy code may as well rely on HANLDE being void* or compatible with each other - exactly what strong handles break.
Sep 19 2013
On 2013-09-11, 20:29, Andrej Mitrovic wrote:On 9/11/13, Kagamin <spam here.lot> wrote: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. -- SimenI'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.
Sep 11 2013
On Wednesday, 11 September 2013 at 20:20:13 UTC, Simen Kjaeraas wrote:On 2013-09-11, 20:29, Andrej Mitrovic wrote:Or multiple alias this: struct _NULL { HANDLE zero; void* ptr; alias zero this; alias ptr this; }On 9/11/13, Kagamin <spam here.lot> wrote: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.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.
Sep 19 2013
On 2013-09-19, 10:06, Kagamin wrote:On Wednesday, 11 September 2013 at 20:20:13 UTC, Simen Kjaeraas wrote:If you want opaque handles, that does not solve the problem. What would solve the problem is something like this: struct DECLARE_HANDLE(int line = __LINE__, string file = __FILE__T) { private void* payload; private this(void* value) { payload = value; } ref DECLARE_HANDLE opAssign(typeof(null) value) { payload = null; return this; } DECLARE_HANDLE opImplicitCastFrom(typeof(null) value) { return DECLARE_HANDLE(null); } } alias DECLARE_HANDLE!(__LINE__, __FILE__) HANDLE; // BUG11074 alias DECLARE_HANDLE!(__LINE__, __FILE__) HWND; // BUG11074 void test() { void* voidPointer; HANDLE handle; HWND window; SomeWindowsFunctionTakingAHandle(null); // No problem! SomeWindowsFunctionTakingAHandle(handle); // No problem! SomeWindowsFunctionTakingAHandle(voidPointer); // Does not compile. SomeWindowsFunctionTakingAHandle(window); // Does not compile. } -- SimenOn 2013-09-11, 20:29, Andrej Mitrovic wrote:Or multiple alias this: struct _NULL { HANDLE zero; void* ptr; alias zero this; alias ptr this; }On 9/11/13, Kagamin <spam here.lot> wrote: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.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.
Sep 20 2013