Silent error when using hashmap

Mike Parker via Digitalmars-d digitalmars-d at puremagic.com
Thu Jul 27 07:09:37 PDT 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



More information about the Digitalmars-d mailing list