Thursday, November 12, 2009

DialogBox + Accelerators

Can't be done. Sorry. You have to write the modal loop yourself.

With the help of The Old New Thing (Yes, its not thread safe. Who even does UI from multiple threads anyway), this function does a standard modal dialog, except it dispatches F1 as IDHELP.

Big caveat! You can't call EndDialog() in your dialog proc - in this, you have to replace EndDialog() with s_ModalDialogResult = . You could also write a function to emulate EndDialog pretty trivially (along with making it thread safe)

static S32 s_ModalDialogResult = -1;
S32 DialogBoxWithHelp(LPCSTR i_Template, LPARAM i_Param, HWND i_Owner, DLGPROC i_Proc)
{
MSG msg;
s_ModalDialogResult = -1;

HWND hDialog = CreateDialogParam(hInstance, i_Template, i_Owner, i_Proc, i_Param);
if (hDialog == 0) return -1;

ShowWindow(hDialog, SW_SHOW);
EnableWindow(i_Owner, FALSE);

ACCEL helper[] =
{
{ FVIRTKEY, VK_F1, IDHELP }
};
HACCEL hAccel = CreateAcceleratorTable(helper, 1);

while (s_ModalDialogResult == -1)
{
S32 msgresult = GetMessage(&msg, 0, 0, 0);
if (msgresult > 0)
{
if (TranslateAccelerator(hDialog, hAccel, &msg) == 0 &&
IsDialogMessage(hDialog, &msg) == 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else
{
// We received a WM_QUIT message; bail out!
// Re-post the message that we retrieved
if (msgresult == 0) PostQuitMessage(msg.wParam);

// otherwise it was an error, bail.
break;
}
}

DestroyAcceleratorTable(hAccel);
EnableWindow(i_Owner, TRUE);
DestroyWindow(hDialog);

return s_ModalDialogResult;
}

References:
http://blogs.msdn.com/oldnewthing/archive/2005/02/18/376080.aspx
http://blogs.msdn.com/oldnewthing/archive/2005/02/24/379635.aspx
http://blogs.msdn.com/oldnewthing/archive/2005/02/22/378018.aspx

Monday, November 9, 2009

Generic Output Functions and Inlining

Today at work I encountered the following warning in release mode:

warning C4789: destination of memory copy is too small

The errant line was a standard function call - nothing crazy going on. For the sake of argument the function did something of the form:

GetValueByName("name of value", &OutputValue);

The function is declared GetValueByName(const char*, void*), and in the function it looked something like this:

ValueDescriptor Desc = FindValueDescriptor(PassedInNameOfValue);
return GetValueByDesc(Desc, PtrToOutputValue);

GetValueByDesc() worked somewhat like:

switch (Descriptor.Value)
case SomeFloatProperty: *(float*)PtrToOutputValue = MyFloatProperty;
case SomeVectorProperty: *(vector*)PtrToOutputValue = MyVectorProperty;
..etc..

This had been working for months (years actually) without error. And now when we compile in release, we get the aforementioned warning.

The compiler, as it happens, decided on this one callsite to inline *both* levels, and drop the switch statement in to the caller. As a result, in the code generation, there was a case that could write 12 bytes (via the vector dereference) to the output pointer, which triggered the warning.