www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Silent error when using hashmap

reply FatalCatharsis <wykennedy gmail.com> writes:
I apologize, I'm not sure if this is expected behavior, a bug in 
the compiler, or a bug in the core windows libraries, so I'll 
post this here until pointed elsewhere.

I've done this trick with win32 for awhile in other languages 
where I pass a reference to a specific class of my own that 
represents an instance of window to the CreateWindowEx function, 
and then use a static router function to send messages to the 
specific instance. I've made the most minimal example I can in 
this gist.

https://gist.github.com/FatalCatharsis/d3cc6ec621f0600975806fe23610ae32

When I compile this and run this, nothing is printed and no 
window is created. I've tried putting try catches around 
everything (including the inside of the static constructor), but 
nothing is caught.

However, when I comment out the hash lookup on line 54, the 
compiled program runs fine and creates a window, (but only for a 
moment since there is not a message handling loop). The expected 
printout of "start" and "end" occurs just fine.

What is happening here that causes the program not execute at 
all, with no output and no exceptions? Is this a bug with my 
code, a bug with the core.sys.windows.windows library, or a bug 
with the compiler? Any info about how to debug this further is 
greatly appreciated.
Jul 26 2017
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
FatalCatharsis wrote:

 I apologize, I'm not sure if this is expected behavior, a bug in the 
 compiler, or a bug in the core windows libraries, so I'll post this here 
 until pointed elsewhere.

 I've done this trick with win32 for awhile in other languages where I 
 pass a reference to a specific class of my own that represents an 
 instance of window to the CreateWindowEx function, and then use a static 
 router function to send messages to the specific instance. I've made the 
 most minimal example I can in this gist.

 https://gist.github.com/FatalCatharsis/d3cc6ec621f0600975806fe23610ae32

 When I compile this and run this, nothing is printed and no window is 
 created. I've tried putting try catches around everything (including the 
 inside of the static constructor), but nothing is caught.

 However, when I comment out the hash lookup on line 54, the compiled 
 program runs fine and creates a window, (but only for a moment since 
 there is not a message handling loop). The expected printout of "start" 
 and "end" occurs just fine.

 What is happening here that causes the program not execute at all, with 
 no output and no exceptions? Is this a bug with my code, a bug with the 
 core.sys.windows.windows library, or a bug with the compiler? Any info 
 about how to debug this further is greatly appreciated.
'cause `WM_CREATE` is not the first message window receiving. check if hwnd is in hash with `in` first.
Jul 26 2017
prev sibling next sibling parent Mike Parker <aldacron gmail.com> writes:
On Wednesday, 26 July 2017 at 16:09:30 UTC, FatalCatharsis wrote:
 

 When I compile this and run this, nothing is printed and no 
 window is created. I've tried putting try catches around 
 everything (including the inside of the static constructor), 
 but nothing is caught.
You're casting this to void*, which is correct, but then when you fetch it back you're casting to a WinThing*, which is not correct. this is a reference, not a pointer. Cast to WinThing instead of WinThing*.
Jul 26 2017
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/26/17 12:09 PM, FatalCatharsis wrote:
 I apologize, I'm not sure if this is expected behavior, a bug in the 
 compiler, or a bug in the core windows libraries, so I'll post this here 
 until pointed elsewhere.
 
 I've done this trick with win32 for awhile in other languages where I 
 pass a reference to a specific class of my own that represents an 
 instance of window to the CreateWindowEx function, and then use a static 
 router function to send messages to the specific instance. I've made the 
 most minimal example I can in this gist.
 
 https://gist.github.com/FatalCatharsis/d3cc6ec621f0600975806fe23610ae32
 
 When I compile this and run this, nothing is printed and no window is 
 created. I've tried putting try catches around everything (including the 
 inside of the static constructor), but nothing is caught.
Note that in D when a thread crashes besides the main thread, you may not get a stack trace printout. You may get nothing. -Steve
Jul 26 2017
parent reply FatalCatharsis <wykennedy gmail.com> writes:
On Wednesday, 26 July 2017 at 16:39:15 UTC, ketmar wrote:>
 'cause `WM_CREATE` is not the first message window receiving. 
 check if hwnd is in hash with `in` first.
I figured this was the case. WM_NCCREATE is probably sent first and the lookup fails. I'm more concerned with why there was no exceptions/debug output of any kind. On Wednesday, 26 July 2017 at 22:08:45 UTC, Steven Schveighoffer wrote:
 Note that in D when a thread crashes besides the main thread, 
 you may not get a stack trace printout. You may get nothing.

 -Steve
This example doesn't create any threads explicitly. Are there other threads that dlang generates? How do I obtain debug output and proper exception handling from these threads?
Jul 26 2017
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
FatalCatharsis wrote:

 On Wednesday, 26 July 2017 at 16:39:15 UTC, ketmar wrote:>
 'cause `WM_CREATE` is not the first message window receiving. check if 
 hwnd is in hash with `in` first.
I figured this was the case. WM_NCCREATE is probably sent first and the lookup fails. I'm more concerned with why there was no exceptions/debug output of any kind.
'cause windows' event handler called from darkes depths of windows code, and D just can't make sense of the stack to unwind it properly and to show you error message and stack trace. never ever rely on getting proper stack traces for exceptions thrown in windows callbacks: it *may* work sometimes, but it is not guaranteed.
Jul 26 2017
parent reply FatalCatharsis <wykennedy gmail.com> writes:
On Thursday, 27 July 2017 at 00:12:20 UTC, ketmar wrote:
 'cause windows' event handler called from darkes depths of 
 windows code, and D just can't make sense of the stack to 
 unwind it properly and to show you error message and stack 
 trace. never ever rely on getting proper stack traces for 
 exceptions thrown in windows callbacks: it *may* work 
 sometimes, but it is not guaranteed.
Is there a way to get it to not fail so hard so that prints that occur before and after the failure aren't lost? The "begin" and "end" in the main don't even get output when the failure happens.
Jul 26 2017
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
FatalCatharsis wrote:

 On Thursday, 27 July 2017 at 00:12:20 UTC, ketmar wrote:
 'cause windows' event handler called from darkes depths of windows code, 
 and D just can't make sense of the stack to unwind it properly and to 
 show you error message and stack trace. never ever rely on getting 
 proper stack traces for exceptions thrown in windows callbacks: it *may* 
 work sometimes, but it is not guaranteed.
Is there a way to get it to not fail so hard so that prints that occur before and after the failure aren't lost? The "begin" and "end" in the main don't even get output when the failure happens.
wrap the whole event handler function in `try/catch` block, and print it there. after all, this is what Dmain does, and so can you. having *full* stack trace has no sense there anyway, as you know for sure that event handler is called by windows, not by you (and usually from your event loop anyway, so detailed stack trace has little useful info).
Jul 26 2017
parent reply FatalCatharsis <wykennedy gmail.com> writes:
On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:
 wrap the whole event handler function in `try/catch` block, and 
 print it there. after all, this is what Dmain does, and so can 
 you. having *full* stack trace has no sense there anyway, as 
 you know for sure that event handler is called by windows, not 
 by you (and usually from your event loop anyway, so detailed 
 stack trace has little useful info).
I tried that like so: https://gist.github.com/FatalCatharsis/39c5f35ae78ecd5399eebe0fb2491004 I put exception blocks both around main and around the invocation of the hash lookup and still get no printouts anywhere, including at the beginning and end of main.
Jul 26 2017
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
FatalCatharsis wrote:

 On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:
 wrap the whole event handler function in `try/catch` block, and print it 
 there. after all, this is what Dmain does, and so can you. having *full* 
 stack trace has no sense there anyway, as you know for sure that event 
 handler is called by windows, not by you (and usually from your event 
 loop anyway, so detailed stack trace has little useful info).
I tried that like so: https://gist.github.com/FatalCatharsis/39c5f35ae78ecd5399eebe0fb2491004 I put exception blocks both around main and around the invocation of the hash lookup and still get no printouts anywhere, including at the beginning and end of main.
'cause you never printed anything.
Jul 26 2017
parent reply FatalCatharsis <wykennedy gmail.com> writes:
On Thursday, 27 July 2017 at 00:48:48 UTC, ketmar wrote:
 FatalCatharsis wrote:

 On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:
 wrap the whole event handler function in `try/catch` block, 
 and print it there. after all, this is what Dmain does, and 
 so can you. having *full* stack trace has no sense there 
 anyway, as you know for sure that event handler is called by 
 windows, not by you (and usually from your event loop anyway, 
 so detailed stack trace has little useful info).
I tried that like so: https://gist.github.com/FatalCatharsis/39c5f35ae78ecd5399eebe0fb2491004 I put exception blocks both around main and around the invocation of the hash lookup and still get no printouts anywhere, including at the beginning and end of main.
'cause you never printed anything.
the writeln("start"); and writeln("end"); in main. This is what I meant by printing. These do not appear in the output. The programs starts and immediately ends without printing "start" and "end". I did not put an output in the exception handler of the WndProc because writeln can throw and the function is marked nothrow. All I as trying to do there was get it to recover.
Jul 26 2017
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
FatalCatharsis wrote:

 On Thursday, 27 July 2017 at 00:48:48 UTC, ketmar wrote:
 FatalCatharsis wrote:

 On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:
 wrap the whole event handler function in `try/catch` block, and print 
 it there. after all, this is what Dmain does, and so can you. having 
 *full* stack trace has no sense there anyway, as you know for sure 
 that event handler is called by windows, not by you (and usually from 
 your event loop anyway, so detailed stack trace has little useful 
 info).
I tried that like so: https://gist.github.com/FatalCatharsis/39c5f35ae78ecd5399eebe0fb2491004 I put exception blocks both around main and around the invocation of the hash lookup and still get no printouts anywhere, including at the beginning and end of main.
'cause you never printed anything.
the writeln("start"); and writeln("end"); in main. This is what I meant by printing. These do not appear in the output. The programs starts and immediately ends without printing "start" and "end". I did not put an output in the exception handler of the WndProc because writeln can throw and the function is marked nothrow. All I as trying to do there was get it to recover.
'cause, as i said, RangeError is not an exception. i thought that you already know it, as this is "general", not "learning" NG.
Jul 26 2017
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/26/17 8:51 PM, FatalCatharsis wrote:
 On Thursday, 27 July 2017 at 00:48:48 UTC, ketmar wrote:
 FatalCatharsis wrote:

 On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:
 wrap the whole event handler function in `try/catch` block, and 
 print it there. after all, this is what Dmain does, and so can you. 
 having *full* stack trace has no sense there anyway, as you know for 
 sure that event handler is called by windows, not by you (and 
 usually from your event loop anyway, so detailed stack trace has 
 little useful info).
I tried that like so: https://gist.github.com/FatalCatharsis/39c5f35ae78ecd5399eebe0fb2491004 I put exception blocks both around main and around the invocation of the hash lookup and still get no printouts anywhere, including at the beginning and end of main.
'cause you never printed anything.
the writeln("start"); and writeln("end"); in main. This is what I meant by printing. These do not appear in the output. The programs starts and immediately ends without printing "start" and "end".
try flushing the output. In some cases the output streams do not flush on newlines.
 I did not put an 
 output in the exception handler of the WndProc because writeln can throw 
 and the function is marked nothrow. All I as trying to do there was get 
 it to recover.
Just surround writeln with try/catch(Exception), should work. You could also just use good old printf. -Steve
Jul 26 2017
parent reply FatalCatharsis <wykennedy gmail.com> writes:
On Thursday, 27 July 2017 at 01:21:40 UTC, Steven Schveighoffer 
wrote:
 the writeln("start"); and writeln("end"); in main. This is 
 what I meant by printing. These do not appear in the output. 
 The programs starts and immediately ends without printing 
 "start" and "end".
try flushing the output. In some cases the output streams do not flush on newlines.
 I did not put an output in the exception handler of the 
 WndProc because writeln can throw and the function is marked 
 nothrow. All I as trying to do there was get it to recover.
Just surround writeln with try/catch(Exception), should work. You could also just use good old printf. -Steve
This appears to be it. When an error is thrown, stdout does not flush. When I put stdout.flush() immediately after the writeln("start") at the beginning it is printed correctly. However, when I try to catch the error and then do stdout.flush() in main, it does not print. Do you know of a way to make D always flush the output buffer when an error is thrown? On Thursday, 27 July 2017 at 01:05:28 UTC, ketmar wrote:
 'cause, as i said, RangeError is not an exception. i thought 
 that you already know it, as this is "general", not "learning" 
 NG.
Yes, I understand this. This is not my question. My question is why all previous output to the console does not occur before the error is thrown. As Steve said, this is because the output buffer is not flushed when an error occurs.
Jul 26 2017
next sibling parent FoxyBrown <Foxy Brown.IPT> writes:
On Thursday, 27 July 2017 at 02:30:17 UTC, FatalCatharsis wrote:
 On Thursday, 27 July 2017 at 01:21:40 UTC, Steven Schveighoffer 
 wrote:
 [...]
This appears to be it. When an error is thrown, stdout does not flush. When I put stdout.flush() immediately after the writeln("start") at the beginning it is printed correctly. However, when I try to catch the error and then do stdout.flush() in main, it does not print. Do you know of a way to make D always flush the output buffer when an error is thrown? On Thursday, 27 July 2017 at 01:05:28 UTC, ketmar wrote:
 [...]
Yes, I understand this. This is not my question. My question is why all previous output to the console does not occur before the error is thrown. As Steve said, this is because the output buffer is not flushed when an error occurs.
I just noticed this without an exception. I changed my writeln's to write and printed a "\n" but it did not flush. Had to flush it manually. Not sure if that is by design or what.
Jul 26 2017
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/26/17 10:30 PM, FatalCatharsis wrote:
 On Thursday, 27 July 2017 at 01:21:40 UTC, Steven Schveighoffer wrote:
 the writeln("start"); and writeln("end"); in main. This is what I 
 meant by printing. These do not appear in the output. The programs 
 starts and immediately ends without printing "start" and "end".
try flushing the output. In some cases the output streams do not flush on newlines.
 I did not put an output in the exception handler of the WndProc 
 because writeln can throw and the function is marked nothrow. All I 
 as trying to do there was get it to recover.
Just surround writeln with try/catch(Exception), should work. You could also just use good old printf.
This appears to be it. When an error is thrown, stdout does not flush. When I put stdout.flush() immediately after the writeln("start") at the beginning it is printed correctly. However, when I try to catch the error and then do stdout.flush() in main, it does not print. Do you know of a way to make D always flush the output buffer when an error is thrown?
Are you sure the error is being caught? I thought the event loop was run in a separate thread. Not sure, because I don't do windows development. Try catching the error right in the event handler. -Steve
Jul 27 2017
prev sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
FatalCatharsis wrote:

 On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:
 wrap the whole event handler function in `try/catch` block, and print it 
 there. after all, this is what Dmain does, and so can you. having *full* 
 stack trace has no sense there anyway, as you know for sure that event 
 handler is called by windows, not by you (and usually from your event 
 loop anyway, so detailed stack trace has little useful info).
I tried that like so: https://gist.github.com/FatalCatharsis/39c5f35ae78ecd5399eebe0fb2491004 I put exception blocks both around main and around the invocation of the hash lookup and still get no printouts anywhere, including at the beginning and end of main.
besides, RangeError is not Exception.
Jul 26 2017
prev sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Thursday, 27 July 2017 at 00:07:39 UTC, FatalCatharsis wrote:

 I figured this was the case. WM_NCCREATE is probably sent first 
 and the lookup fails. I'm more concerned with why there was no 
 exceptions/debug output of any kind.
There are a few things going on with your code. I'll break it down one at a time. 1. You can't expect exceptions thrown in a callback called from C to be propagated through the C side back into the D side. That includes errors. It happens on some platforms, but not all. On Windows, it does not (at least, not with DMD -- I can't speak for LDC). 2. The error you're getting is because neither WM_CREATE nore WM_NCCREATE is the first message sent. You can see this by inserting the following into your WndProc: ``` static UINT first; if(msg == WM_NCCREATE) { try { if(first == 0) writeln("NCCREATE is first"); else writeln("First is ", first); } catch(Exception e){} } else { // window = winMap[hwnd]; if(first == 0) first = msg; } ``` This prints 36, which if you look it up (hexcode on MSDN, but I like the list at WineHQ [1]) will show you is WM_GETMINMAXINFO. That means that when you try to fetch the window instance from winMap, there's nothing there. When you try to access a non-extent element in an AA, you get a RangeError. Because of point 1 above, you're never seeing it and instead are getting a crash. You could try to catch the error and stash it for later, but better to do change the way you access the map: ``` if(auto pwin = hwnd in winMap) window = *pwin; ``` The in operator returns a pointer, so it needs to be dereferenced. 3. As I mentioned in another post in this thread, you are doing the wrong thing with your window reference. In your call to CreateWindowEx, you are correctly casting the reference to void*. Everywhere else, you're treating it as a pointer. That's wrong! To prove it, you can to this. Declare an instance of WinThing at the top of the file. ``` WinThing testMe; ``` Then, add this in the class constructor *before* the call to CreateWindowEx. ``` testMe = this; ``` Finally, where you handle the WM_NCCREATE message, do this: ``` try writeln("window is testMe = ", *window is testMe); catch(Exception e) {} ``` This will print false. Why? Because the window instance you created is a reference. In CreateWindowEx, you've treated it as such. But then when you fetch it out of lpCreateParams, you cast it to a WinThing*. For that to be correct, you would have to change CreateWindowEx to pass a pointer to the reference (i.e. cast(void*)&window). But actually, that's still not correct because you're taking the address of a local variable. So the correct thing to do is to leave CreateWindowEx as is, and change all every WinThing* to WinThing. ``` WinThing[HWND] winMap; WinThing window; window = cast(WinThing)createStruct.lpCreateParams; ``` Note that you don't have to dereference the createStruct pointer to access its fields. Fix these these issues and it should compile. One other thing, in case you are unaware (not an error, just a matter of style). ``` private static string BASE_CLASS = "BaseClass"; ``` There's no reason to make this static member or to call toUTFz when you use it. You can use a manifest constant with a wchar literal. Unlike char -> char*, whcar does not implicitly convert to wchar*, but you can use the .ptr property. enum baseClass = "BaseClass"w; wc.lpszClassName = baseClass.ptr; [1] https://wiki.winehq.org/List_Of_Windows_Messages
Jul 27 2017
parent FatalCatharsis <wykennedy gmail.com> writes:
On Thursday, 27 July 2017 at 14:09:37 UTC, Mike Parker wrote:
 1. You can't expect exceptions thrown in a callback called from 
 C to be propagated through the C side back into the D side. 
 That includes errors. It happens on some platforms, but not 
 all. On Windows, it does not (at least, not with DMD -- I can't 
 speak for LDC).
Figures that the different calling conventions inside the depths of windows wouldn't be unwound properly. I'll just make sure to do blanket try catches for all throwables in each of these extern windows functions so that the buffer flushes for debug output and then explicitly exit the program.
 You could try to catch the error and stash it for later, but 
 better to do change the way you access the map:

 ```
 if(auto pwin = hwnd in winMap) window = *pwin;
 ```
Knew about this but didn't think you could do this in one line. This is fantastic.
 3. As I mentioned in another post in this thread, you are doing 
 the wrong thing with your window reference. In your call to 
 CreateWindowEx, you are correctly casting the reference to 
 void*. Everywhere else, you're treating it as a pointer. That's 
 wrong! To prove it, you can to this. Declare an instance of 
 WinThing at the top of the file.
 
 In CreateWindowEx, you've treated it as such. But then when you 
 fetch it out of lpCreateParams, you cast it to a WinThing*. For 
 that to be correct, you would have to change CreateWindowEx to 
 pass a pointer to the reference (i.e. cast(void*)&window). But 
 actually, that's still not correct because you're taking the 
 address of a local variable. So the correct thing to do is to 
 leave CreateWindowEx as is, and change all every WinThing* to 
 WinThing.
So in the language, references to class objects are treated the same syntactically as pointers? So if we were in C++, me declaraing a WinThing** (reference to a reference to an object) is the same as WinThing* in D? Tried out your changes, that definately cleaned up the mess. Why can I not also do the same with the create struct like: CREATESTRUCT createStruct = cast(CREATESTRUCT) lparam; I take it this is because CREATESTRUCT is not a D class, but a struct somewhere?
 Note that you don't have to dereference the createStruct 
 pointer to access its fields.
Nice tip, didn't realize D implicitly dereferenced pointers when you apply the dot operator. Very nice.
 There's no reason to make this static member or to call toUTFz 
 when you use it. You can use a manifest constant with a wchar 
 literal. Unlike char -> char*, whcar does not implicitly 
 convert to wchar*, but you can use the .ptr property.

 enum baseClass = "BaseClass"w;
 wc.lpszClassName = baseClass.ptr;
Is the only difference between this and private static immutable values that the enum could not be an lvalue and has no reference at runtime?
Jul 27 2017