How do I get the effect of CW_USEDEFAULT positioning on a window I’ve already created?


A customer wanted to know how to get the effect of CW_USE­DEFAULT positioning on a window that already exists. In particular, they wanted to be able to reposition a dialog box to get the CW_USE­DEFAULT cascade effect, but since you can't actually pass CW_USE­DEFAULT in a dialog template, the repositioning has to be done after the fact. (Presumably in the WM_INIT­DIALOG handler, which runs before the dialog is visible, so that there is no visible flicker.)

The solution here is simple: Create a temporary invisible window with CW_USE­DEFAULT as its position and the same height and width as your dialog box. See where the window manager puts that temporary window and move your dialog box to match that position. Then destroy the temporary window.

Comments (37)
  1. Anonymous Coward says:

    I dare say the customer thought of that, but just wanted to know if there's a way that isn't a blood-ugly hack.

  2. j b says:

    IF the custoomer, as A C suggests, thinks this is a blodd-ugly hack, he has my full support.

  3. Another AC says:

    If that's the solution, I'd rather do without the feature.

  4. ErikF says:

    As hacks go, it's not that bad at all: it only relies on putting a string of documented features together (CreateWindow -> GetWindowRect -> DestroyWindow). They're even being used for their intended purposes! If I recall, this is sometimes also known as "programming."

  5. hmm says:

    Interesting.  I guess since usually dialog boxes have an associated owner window and aren't usually used like normal overlapped windows, it looks like there simply aren't any parameters in any of the dialog creation functions to control/direct dialog-box positioning; it looks like you are basically expected to explicitly reposition the dialog yourself in your own code.

    The temporary window solution may be a little hacky, but then again, Windows already use hidden windows for all manner of things like receiving cross-apartment STA COM calls, so how bad is a temporary window really?

    @Another AC: my guess is the customer wants to use a dialog as their application's main window, in which case it makes perfect sense that they'd want to support as much of the "normal" window-positioning behaviors as feasible.

  6. James Curran says:

    @ErikF — I don't see how creating & destroying a window without ever displaying it qualifies as "being used for their intended purposes"

  7. SimonRev says:

    As hmm mentioned there are numerous reasons to create a window without displaying it.  Windows to receive broadcast messages.  Windows for STA COM calls.  Windows for other types of simple IPC.

    While creating a window just to see where it is positioned is a bit hokey, it is hardly the worst thing in the world, and as Erik pointed out — totally documented.

  8. Nick says:

    Hey guys – welcome to programming. Putting some blocks together, in this case a hidden window and a bunch of documented APIs. It is certainly not a hack. It would take about 5 minutes to write a wrapper function that does all of this, i.e. CreateDefaultWindowPosition(…), and now whatever might have the potential to look ugly is tucked away elegantly.

  9. Clever. For some reason, I am cursing myself for not realizing it.

  10. Antonio 'Grijan' says:

    I hope the customer has great reasons for doing this. But IMHO, with big displays and multimonitor setups, the only window that should be left at WM_DEFAULT position is the application's main window, and only if the user hasn't resized or moved it in a previous session (in which case, we should infer the user wants it to be *there*). Dialog boxes should be placed over their owner, maybe centered on it, but I have come to prefer them just bellow the title bar's center. Palettes and other secondary windows should be placed relative to the main window, too, in coherent places (for example, next to the left or right border). I know this is not exactly what Windows UI guidelines say, but these rules aren't incoherent with them, and I think they can greatly help the user.

    All of this is my own opinion, grown over the years out of watching my customers using my applications, and watered with a bit of common sense.

  11. Matt says:

    It *is* a hack, because it's doing a very large amount of work in order to get information that is trivially exposed for use by the Window Manager in kernel-mode, but hidden from view for user-mode programs.

    Create window class -> syscall -> add an entry to the atom table -> create Window -> syscall -> create window handle -> create preemptive caches of everything a window might want -> ask the window manager to come up with a CW_USEDEFAULT position -> inform users over RDP that a new window has been made -> register window with window station -> begin window pump -> attach to thread -> return to user some time later -> syscall to get window position -> Destroy Window -> tear everything down -> window pump a WM_DESTROY and WM_NCDESTROY -> destroy window handle -> destroy window class -> tell RDP that the window has been destroyed.

    It's maybe a hundred transitions from user to kernel if we include the windows pump, huge amounts of state being constructed and then immediately destroyed – when all you wanted was to ask the window manager to come up with a CW_USEDEFAULT position for your window.

  12. Neil says:

    I once wanted to create an application which consisted entirely of a modal dialog but in such a way that it would appear in Windows 95's task list.

    I ended up creating a visible minimised window which owned an invisible window which was the owner of the dialog. This meant that the dialog was associated with the minimised window in the task list, but opened at the "default" position.

  13. @Matt says:

    Right, because that REALLY slows down the application, compared to the other 30 windows the application is going to create anyway.

  14. Azarien says:

    I do think this is an ugly hack.

    It should be possible to pass CW_USEDEFAULT to MoveWindow (or call it MW_USEDEFAULT for that matter).

  15. ErikF says:

    @Matt (and others): If calling an additional function to get a value is considered hacky, then you must absolutely hate the "allocate until the buffer is large enough" approach used by functions like RegQueryValueEx! :-)

  16. Iain Clarke says:

    @James Curran

    > @ErikF — I don't see how creating & destroying a window without ever displaying it qualifies as "being used for their intended purposes".

    Invisible windows are used for all sorts of purposes. I think you're focussing too much on one use of a window – something having a shape to restrict a device context, when it's also very useful as something to associate a message queue with on a finer scale than a process.

    *Cough* COM, *cough* Marshalling…

    I'm sure whoever wrote some of that code found it non-trivial too, but I'm sure they didn't call it an ugly hack.

  17. voo says:

    @ErikF: You don't? There's something inherently hacky about dual-purpose functions and the Win32 API has some of the worst offenders in that regard (to name MultiByteToWideChar as just one example that immediately comes to mind).

    On the other hand I don't see anything inherently wrong about the proposed approach here – that's not really something people need every day is it and the work around is well documented and hardly that much additional work.

  18. 640k says:

    Using GUI objects for IPC is a hack. Although many do it because there's no better (convenient, performant, simpel) way to do it. Still a hack though.

  19. Sven2 says:

    Doesn't this solution have concurrency issues when other processes create dialogs?

    E.g.: Your process creates its dummy window and it goes to e.g. position (100,100). But before you can create the real window, the task scheduler switches to a second process which creates a dialog the normal way, which is cascaded down to e.g. position 130,130. After that, your process gets CPU time and creates its window at 100,100.

    In the end, the window at 100,100 is on top of the window at 130,130. Although it was supposed to be the other way around.

    [This race condition existed even if you had a direct method. Your application spends too much time in its WM_INIT­DIALOG handler and another thread creates a dialog at 130,130. You then show your initialized dialog, and it appears at 100,100. -Raymond]
  20. Joker_vD says:

    @ErikF The problem with those "Provide us buffer large enough" functions is that they introduce races. While it's okay for MultiByteToWideChar — you pass your own string in, so don't be your own enemy and don't change it between two calls, — it's not okay for, say, GetTcpTable: the number of connections can easily grow up before the second call to it. Really, can't the system allocate some internal buffer on its own?

    And do you how netstat circumvents that problem with GetTcpTable? Oh, it calls some un-documented API function that basically calls GetTcpTable three more times on ERROR_INSUFFICIENT_BUFFER until it gives up. Yeaaaaaaah, totally not a hack.

  21. ErikF says:

    I'm not saying that the "provide your own buffer and hope it's big enough" method isn't wonderful (it's not, and I'd rather have the system create the buffer for me.) I was just trying to argue that compared with something like my example, having to create a hidden window to get dimensions for a dialogue box is not nearly as bad!

  22. voo says:

    @Joker_vD: There's no race condition or bug in any of those "first ask for input size then call it again with that buffer" functions. Assuming that you don't want to let the Kernel/library allocate memory for you (for a low-level API such as Win32 that makes sense to some degree although it complicates the interface naturally) there's not many ways around a loop to solve the problem (at least not if the kernel's involved, you can't it a function pointer and expect it to execute the code there after all). Low level but not ugly given the constraints.

    Higher level libraries abstract all those details away anyhow and at least there are good reasons to do so. But using the same function to ask for the necessary buffer size and populating it? Ugly and unnecessary.

  23. GregM says:

    "Doesn't this solution have concurrency issues when other processes create dialogs?"

    Yes, and this happens right now with different processes, so it's not really a big deal.  It's no different than the process being swapped out after the window is created but before it is shown.

  24. Joker_vD says:

    @voo: The kernel knows the exact amount of memory it needs when a function is called. It can allocate the buffer and copy the needed data right there, in one pass. Instead we do two passes, and it does open a window to DoS attacks.

    "But using the same function to ask for the necessary buffer size and populating it? Ugly and unnecessary." — do you really propose doubling the amount of functions in public API? What would be the benefit? Okay, assume I have both DWORD GetTcpTableSize() and BOOL GetTcpTableData(PMIB_TCPTABLE pTcpTable, _In_ DWORD pdwSize, _In_ BOOL bOrder). So, if the GetTcpData fails, I would have to call GetTcpTableSize() again, because GetTcpTableData must not convey the needed size (that's the whole point of separation, otherwise GetXxxCount() is redundant), so I still do two function calls in normal scenario, but twice as many for a re-try. Where is the gain?

  25. Joker_vD says:

    @Eric Brown: Who is going to free it? Either a) the kernel itself, for example the next time your the thread calls this function again; b) you when you're done with it. As for the "from where" question — oh, there are TONS of function just to allocate memory: GlobalAlloc, LocalAlloc, HeapAlloc, SysAllocSomething, pick yours.

    Really, the question "but who's gonna call CloseHandle on the returned handle?" has pretty much the same nature yet somehow people manage to solve it.

    [Heaps are in user mode. Kernel mode cannot allocate memory from user-mode heaps. Preallocation is the only possible way. Close­Handle has exactly the same problem, and you solve it by making HANDLE a fixed size so that everybody knows exactly how much space to preallocate. -Raymond]
  26. Gabe says:

    Maurits: The problem is that somebody could change the registry value between the two calls to RegQueryValueEx, causing even the second call to fail.

  27. voo says:

    @Joker_vD: Sure the kernel can allocate memory for you.. now the question is – where? Even the Win32 API had historically more than one heap, then there's malloc, new, if you use a higher level language it may want to do its own memory allocation and so on. Now if it's purely on the user side you could just pass a function pointer for allocation around, but clearly that's impossible if you call the Kernel.

    Yes you do twice as many function calls in the generally exceedingly rare scenario where you have to retry – designing your APIs in such a way that they're a bit more efficient in some rare cornercases is the best way how you end up with horrible APIs. Go ahead and measure in some large program how often you actually have to retry, for the vast majority of functions I'll guarantee you that that will be exceedingly rare.

  28. Eric Brown says:

    @Joker_vD:  And who, exactly, is going to *free* that memory that the API allocated?  And *where* is that memory going to be allocated *from*?  The kernel doesn't allocate user-mode memory, and kernel-mode memory isn't accessible from user-mode…

  29. @ErikF: You don't need more than two calls to RegQueryValueEx.

    msdn.microsoft.com/…/ms724911(v=vs.85).aspx

    If the buffer specified by lpData parameter is not large enough to hold the data, the function returns ERROR_MORE_DATA ***and stores the required buffer size in the variable pointed to by lpcbData***

  30. Joker_vD says:

    @voo: "Even the Win32 API had historically more than one heap, then there's malloc, new, if you use a higher level language it may want to do its own memory allocation and so on. Now if it's purely on the user side you could just pass a function pointer for allocation around, but clearly that's impossible if you call the Kernel."

    Where? Somewhere, I don't quite care: all I want to do is to have read permission for this area of memory. Every language's runtime somehow has to cope with using memory it doesn't own, so it's not a huge problem that you can't pass your own "malloc" arounf. Also consider this scheme: the API returns you the pointer to the data AND the pointer to the function which you have to call on the data pointer when you're done with it. And the best thing is, you don't even need to return the second pointer — just write in the docs that "The returned pointer must be freed with the call to LocalFree". FormatMessage follows this, though it's arguably the function which doesn't suffer from "data may change their size between two calls".

    Also, just yesterday evening I stumbled upon "Identifying and Exploiting Windows Kernel Race Conditions via Memory Access Patterns" by M. Jurczyk and G. Coldwind. Which tells me that I can trick the kernel into believing the buffer provided to it is bigger than it actually is, and exploit it to log keypresses or elevate rights. Hmmm.

    [You're missing the fact that the implementation is in kernel mode. Kernel mode cannot call a callback function in user mode to allocate user-mode memory. It's in the wrong mode. -Raymond]
  31. Joker_vD says:

    Damn. The KeUserModeCallback is a lie.

    Speaking seriously, I really wish I knew the perfect solution. The kernel knows how much memory it will need, but it can't allocate it. The user can allocate memory, but doesn't know how much he has to. And when the kernel reports the size needed, that information, in general case, becomes stale immediately. Making two consecutive calls {GetDataSize();GetActualSize();} an atomic operation is prohibitively expensive, as I'd imagine. What's the answer? I don't know it.

    [It's not a lie, but it's not what you want either. You cannot hold any resources when issuing the callout because the callout may hang or raise an exception. So either way, it's a retry loop. -Raymond]
  32. Anonymous Coward says:

    In theory, the system could have been designed in such a way that kernel mode could ask user mode for memory. I believe this should in theory be possible in NT-based Windows, but I'd be happy to be proven wrong.

    Lacking that, the retry loop could have been hidden by the API. I've seen a lot of code with potential bugs involving memory allocation for return data, so I think it would have been much better if Microsoft had implemented this the right way once and for all.

    Regardless, from the perspective of an application developer these look the same. And remind us of IMalloc.

  33. Sven2 says:

    @Anonymous "the retry loop could have been hidden by the API.":

    At the moment, the user can pass a buffer that's usually big enough in the first try and just reallocating in the rare case data doesn't fit. That may be an important performance optimization in some cases.

  34. Gabe says:

    All the kernel has to do is create a snapshot of the data in paged pool, then call out to your user-mode callback to allocate the memory, copy the data from the paged pool into the pointer returned from the callback, and free the paged pool allocation.

    It's so simple it makes you wonder why all APIs aren't implemented that way!

    Of course, you'd still have to have the current mechanism that lets you get the data without memory allocation, an additional copy, and another kernel transition. Do you add a memory allocation callback function pointer to every call or do you create a separate version of each API that requires it?

    Wait, that mechanism won't work if your data is too big for a paged pool allocation. In that case maybe the kernel should just map pages into your address space so you can do the allocation and copy yourself if need be, then call VirtualFree when you're done. But your address space may be taken up by your heap and there may not be enough contiguous addresses to allocate, in which case you'd have to fallback to the heap allocation anyway.

    Hmm…maybe it's not so simple afterall.

    [And what if the user-mode callback raises an exception or calls Exit­Thread? Yay! Kernel pool memory leak! -Raymond]
  35. Anonymous Coward says:

    >That may be an important performance optimization in some cases.

    I have yet to see them. But I've seen many many sometimes subtle API return value allocation errors.

    >Yay! Kernel pool memory leak!

    Exiting a thread will always land you back in kernel mode, and there is no reason to believe the kernel couldn't clean up after itself.

    Alternatively, have the kernel ask for user mode memory.

    [So there would be some per-thread list of "memory I need to free if this thread is terminated", and anybody who allocates kernel memory and then calls another function that could possibly result in a callout to user mode code must add the memory to that list, then remove it from the list when the memory is freed. And that list can get arbitrarily long. (Consider a user callback that calls another kernel function that needs to allocate memory.) This is getting really complicated and fragile. ("Oh, sorry, when I wrote function X and had it call function Y, I didn't realize that function Y calls function Z which calls function Q which calls function R which calls to user mode. That's why we have a memory leak that crashes your server after 14 days of uptime.") -Raymond]
  36. What happened to CW_USEDEFAULT? says:

    > AllocateMemoryForKernelApi(size_t size) … woe unto you…

    AllocateMemoryForKernelApi should take an LPVOID * parameter for the allocated buffer and instead return an error code. That way if your callback cannot satisfy the kernel's requested memory allocation size you can return an error (and maybe set the output parameter to nullptr if that's your thang). Then the kernel can retry your function in chunks with a smaller requested buffer size, or assume that you've taken corrective action after the first call and retry with the (maybe same if nothing else changed) size, or just fail out.

    [What if somebody fails to read that part of the documentation and raises an exception anyway? What if somebody writes an intentionally misbehaving AllocateMemoryForKernelApi function? What if somebody does a Sleep(INFINITE) inside AllocateMemoryForKernelApi? -Raymond]
  37. Anonymous Coward says:

    In order for that to be an issue, the main continuation of the thread would have to be the memory allocation callback, which means the return value of the original function is never used (and it is hence only called in order to initiate the callback). A rather contrived scenario since it assumes that the system would allow this in the first place (and that your stack is infinite and some other weirdities). It won't be a problem.

    I still prefer my idea of allocating user memory though. It's simpler since you have less to take into account since any screw up will only affect the user process.

    [I'm referring to something like this:
    void *AllocateMemoryForKernelApi(size_t size) {
     if (logging_enabled) {
      auto logfile_name = RegQueryValueAlloc(hkey, AllocateMemoryForKernelApiNoLogging);
      blah blah;
      delete[] (char*)logfile_name;
     }
     return new char[size];
    }
    Notice that while kernel is allocating memory, we call back into kernel to allocate more memory. So now you need to keep a list of "all the memory temporarily allocated by kernel waiting to be copied back into user space" just in case a stack overflow or bad_alloc occurs. Plus there's the difficulty of getting this right on the kernel side since you now cannot simply get the answer and copy it to the buffer, then release your locks. Now you have to get the answer, copy it to a temporary buffer, drop all locks, allocate memory from user space, copy the result, the free the temporary buffer. And woe unto you if you have to do this more than once in a single call. -Raymond
    ]

Comments are closed.