digitalmars.D - Silent error when using hashmap
- FatalCatharsis (23/23) Jul 26 2017 I apologize, I'm not sure if this is expected behavior, a bug in
- ketmar (3/23) Jul 26 2017 'cause `WM_CREATE` is not the first message window receiving. check if h...
- Mike Parker (5/10) Jul 26 2017 You're casting this to void*, which is correct, but then when you
- Steven Schveighoffer (4/19) Jul 26 2017 Note that in D when a thread crashes besides the main thread, you may
- FatalCatharsis (9/14) Jul 26 2017 I figured this was the case. WM_NCCREATE is probably sent first
- ketmar (6/12) Jul 26 2017 'cause windows' event handler called from darkes depths of windows code,...
- FatalCatharsis (4/10) Jul 26 2017 Is there a way to get it to not fail so hard so that prints that
- ketmar (6/15) Jul 26 2017 wrap the whole event handler function in `try/catch` block, and print it...
- FatalCatharsis (6/12) Jul 26 2017 I tried that like so:
- ketmar (2/13) Jul 26 2017 'cause you never printed anything.
- FatalCatharsis (7/24) Jul 26 2017 the writeln("start"); and writeln("end"); in main. This is what I
- ketmar (3/29) Jul 26 2017 'cause, as i said, RangeError is not an exception. i thought that you
- Steven Schveighoffer (6/34) Jul 26 2017 try flushing the output. In some cases the output streams do not flush
- FatalCatharsis (14/29) Jul 26 2017 This appears to be it. When an error is thrown, stdout does not
- FoxyBrown (4/20) Jul 26 2017 I just noticed this without an exception. I changed my writeln's
- Steven Schveighoffer (5/29) Jul 27 2017 Are you sure the error is being caught? I thought the event loop was run...
- ketmar (2/13) Jul 26 2017 besides, RangeError is not Exception.
- Mike Parker (85/88) Jul 27 2017 There are a few things going on with your code. I'll break it
- FatalCatharsis (22/55) Jul 27 2017 Figures that the different calling conventions inside the depths
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
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
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
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
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. -SteveThis 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
FatalCatharsis wrote:On Wednesday, 26 July 2017 at 16:39:15 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.'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.
Jul 26 2017
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
FatalCatharsis wrote:On Thursday, 27 July 2017 at 00:12:20 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).'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
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
FatalCatharsis wrote:On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:'cause you never printed anything.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
On Thursday, 27 July 2017 at 00:48:48 UTC, ketmar wrote:FatalCatharsis 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". 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.On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:'cause you never printed anything.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
FatalCatharsis wrote:On Thursday, 27 July 2017 at 00:48:48 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.FatalCatharsis 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". 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.On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:'cause you never printed anything.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
On 7/26/17 8:51 PM, FatalCatharsis wrote:On Thursday, 27 July 2017 at 00:48:48 UTC, ketmar wrote:try flushing the output. In some cases the output streams do not flush on newlines.FatalCatharsis 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".On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:'cause you never printed anything.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.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
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: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'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
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: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.[...]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.
Jul 26 2017
On 7/26/17 10:30 PM, FatalCatharsis wrote:On Thursday, 27 July 2017 at 01:21:40 UTC, Steven Schveighoffer wrote: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. -SteveThis 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?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.
Jul 27 2017
FatalCatharsis wrote:On Thursday, 27 July 2017 at 00:34:28 UTC, ketmar wrote:besides, RangeError is not Exception.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
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
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