• The Old New Thing

    When was the WM_COPYDATA message introduced, and was it ported downlevel?

    • 9 Comments

    Gabe wondered when the WM_COPY­DATA message was introduced.

    The WM_COPY­DATA message was introduced by Win32. It did not exist in 16-bit Windows.

    But it was there all along.

    The The WM_COPY­DATA message was carefully designed so that it worked in 16-bit Windows automatically. In other words, you retained your source code compatibility between 16-bit and 32-bit Windows without having to do a single thing. Phew, one fewer breaking change between 16-bit and 32-bit Windows.

    As Neil noted, there's nothing stopping you from sending message 0x004A in 16-bit Windows with a window handle in the wParam and a pointer to a COPY­DATA­STRUCT in the lParam. Since all 16-bit applications ran in the same address space, the null marshaller successfully marshals the data between the two processes.

    In a sense, support for the WM_COPY­DATA message was ported downlevel even before the message existed!

  • The Old New Thing

    Why Johnny can't read music

    • 9 Comments

    In the book He Bear, She Bear, the musical instrument identified as a tuba is clearly a sousaphone.

    (For those who are wondering what the title has to do with the topic of musical instrument identification: It's a reference to the classic book Why Johnny Can't Read.)

  • The Old New Thing

    Why does PrintWindow hate CS_PARENTDC? redux

    • 9 Comments

    Why does Print­Window hate CS_PARENT­DC? Because everybody hates CS_PARENT­DC!

    Commenter kero claims that it's "easy to fix" the problem with Print­Window and CS_PARENT­DC. You just remove the CS_PARENT­DC style temporarily, then do the normal Print­Window, then restore the CS_PARENT­DC style. The question is then why Print­Window simply doesn't do this.

    The question assumes that the described workaround actually works. It may work in limited situations, but it certainly doesn't work in general.

    Since the CS_PARENT­DC style is a class style, removing the style affects all windows of that class, not merely the window you are trying to print. Suppose there are two windows of the class running on different threads, and you remove the CS_PARENT­DC style in anticipation of doing a Print­Window. While that's going on, the other window gets a WM_PAINT message. Since the CS_PARENT­DC style was temporarily removed, that window will be painting with an incorrectly-clipped DC. Result: Incorrect pixels on the screen.

    The proposed workaround doesn't actually work reliably, which means that it probably shouldn't be done at all. (Random reinforcement breeds superstition.)

  • The Old New Thing

    2013 Q3 link clearance: Microsoft blogger edition

    • 9 Comments

    It's that time again: Linking to other Microsoft bloggers, and once again, the links are all from the excellent NT Debugging blog.

  • The Old New Thing

    What's the difference between CopyIcon and DuplicateIcon?

    • 9 Comments

    There are two functions that can be used to create one icon that is identical to another. One of them is Copy­Icon. The other is Duplicate­Icon. What's the difference?

    There isn't any difference. Both functions clone an icon. In fact, their implementations are basically line-for-line identical.

    Originally, there was just one function to clone an icon: Copy­Icon.

    Windows 3.0 introduced Program Manager, and the developers of Program Manager wrote their own function called Duplicate­Icon. Why? I have no idea. My guess is that they didn't realize that such a function already existed, so they inadvertently reinvented the wheel.

    Windows NT 3.1 came along, and the team that ported Program Manager to 32-bit Windows also ported the Duplicate­Icon function, and they figured, "This function is so useful, we'll export it for anybody to use!"

    Meanwhile, the original Copy­Icon function is sitting there saying, "What am I, chopped liver?"

    Anyway, it's a sad story, but that's how we ended up with two functions that do exactly the same thing. Personally, I would recommend using the Copy­Icon function. It's in user32.dll, which you are almost certainly already linked to if you're doing anything with icons in the first place, so the incremental cost is much lower.

    Update: Joshua points out that the two functions are not identical. Duplicate­Icon takes an extra instance handle parameter. Now it makes sense. The shell version is an enhancement to the user version in that it can also transfer icon ownership to another module. (Hence the new first parameter.) This was important in 16-bit Windows because icons were resources which were associated with modules. If you wanted to use an icon after the module was unloaded, you needed to copy it and transfer ownership. But this ownership transfer step is not needed in Win32 because, as we saw yesterday, icons are no longer tied to the underlying resources. So the functions started out different but now they're the same.

  • The Old New Thing

    Logging the foreground process as it changes

    • 9 Comments

    Today's Little Program simply logs all changes to the foreground window by recording the path to the application the user switched to. You might use this as part of a usability study to monitor what applications users spend most of their time in.

    Most of this code is just taking things we already know and snapping them together.

    1. Using accessibility to monitor events, specifically to monitor foreground changes.
    2. Get­Window­Thread­Process­Id to get the process ID from a window.
    3. Open­Process to get a handle to a process given the process ID.
    4. Query­Full­Process­ImageName to get the path to the application from the handle. (For Windows XP, you can use Get­Process­Image­File­Name.)

    Take our scratch program and make these changes:

    BOOL QueryWindowFullProcessImageName(
        HWND hwnd,
        DWORD dwFlags,
        PTSTR lpExeName,
        DWORD dwSize)
    {
     DWORD pid = 0;
     BOOL fRc = FALSE;
     if (GetWindowThreadProcessId(hwnd, &pid)) {
      HANDLE hProcess = OpenProcess(
              PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
      if (hProcess) {
       fRc = QueryFullProcessImageName(
              hProcess, dwFlags, lpExeName, &dwSize);
       CloseHandle(hProcess);
      }
     }
     return fRc;
    }
    

    The Query­Window­Full­Process­Image­Name function is the meat of the program, performing steps 2 through 4 above.

    Now we just hook this up in our event callback function. This should look really familiar, since we did pretty much the same thing earlier this year.

    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     g_hwndChild = CreateWindow(TEXT("listbox"), NULL,
         LBS_HASSTRINGS | WS_CHILD | WS_VISIBLE | WS_VSCROLL,
         0, 0, 0, 0, hwnd, NULL, g_hinst, 0);
     if (!g_hwndChild) return FALSE;
     return TRUE;
    }
    
    void CALLBACK WinEventProc(
        HWINEVENTHOOK hWinEventHook,
        DWORD event,
        HWND hwnd,
        LONG idObject,
        LONG idChild,
        DWORD dwEventThread,
        DWORD dwmsEventTime
    )
    {
     if (event == EVENT_SYSTEM_FOREGROUND &
         idObject == OBJID_WINDOW &&
         idChild == CHILDID_SELF)
     {
      PCTSTR pszMsg;
      TCHAR szBuf[MAX_PATH];
      if (hwnd) {
       DWORD cch = ARRAYSIZE(szBuf);
       if (QueryWindowFullProcessImageName(hwnd, 0,
                          szBuf, ARRAYSIZE(szBuf))) {
        pszMsg = szBuf;
       } else {
        pszMsg = TEXT("<unknown>");
       }
      } else {
       pszMsg = TEXT("<none>");
      }
    
      ListBox_AddString(g_hwndChild, pszMsg);
     }
    }
    
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
    {
     ...
      ShowWindow(hwnd, nShowCmd);
    
     HWINEVENTHOOK hWinEventHook = SetWinEventHook(
         EVENT_SYSTEM_FOREGROUND,
         EVENT_SYSTEM_FOREGROUND,
         NULL, WinEventProc, 0, 0,
         WINEVENT_OUTOFCONTEXT);
    
      while (GetMessage(&msg, NULL, 0, 0)) {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
      }
    
      if (hWinEventHook) UnhookWinEvent(hWinEventHook);
    ...
    }
    

    The main program installs an accessibility hook for the EVENT_SYSTEM_FOREGROUND event, and each time the event fires, it extracts the process name and logs it to the screen. Since the notification is asynchronous, the foreground window may have been destroyed by the time the notification is received, so we have to be prepared for that case.

  • The Old New Thing

    When something gets added to a queue, it takes time for it to come out the front of the queue

    • 9 Comments

    A customer wanted to know why the input they were simulating with Send­Input is not being reported by Get­Async­Key­State. Isn't that supposed to reflect the instantaneous keyboard state? I just pushed the key down (or at least simulated it), but when I ask if the key is down, I'm told "Nope." What's the deal?

    INPUT input = { 0 };
    input.type = INPUT_KEYBOARD;
    input.ki.wVk = 'A';
    input.ki.wScan = 'A';
    input.ki.dwFlags = 0; // key down
    SendInput(1, &input, sizeof(INPUT));
    
    assert(GetAsyncKeyState('A') < 0);
    

    The Send­Input call simulates pressing the A key, and the code immediately checks whether the key is down.

    But sometimes the assertion fires. How can that be?

    Because you're asking the question before the window manager has fully processed the input. Here's a little diagram.

    Mouse Keyboard Hardware
    SendInput Hardware
    Input
    Queue
     
    Dequeue
    Raw Input Thread
    Low
    Level
    Hooks
    Applications
    Update
    Input
    State
    Raw Input Thread
    App 1 App 2 App 3

    When you call Send­Input, you're putting input packets into the system hardware input queue. (Note: Not the official term. That's just what I'm calling it today.) This is the same input queue that the hardware device driver stack uses when physical devices report events.

    The message goes into the hardware input queue, where the Raw Input Thread picks them up. The Raw Input Thread runs at high priority, so it's probably going to pick it up really quickly, but on a multi-core machine, your code can keep running while the second core runs the Raw Input Thread. And the Raw Input thread has some stuff it needs to do once it dequeues the event. If there are low-level input hooks, it has to call each of those hooks to see if any of them want to reject the input. (And those hooks can take who-knows-how-long to decide.) Only after all the low-level hooks sign off on the input is the Raw Input Thread allowed to modify the input state and cause Get­Async­Key­State to report that the key is down.

    And if you manage to look before all this happens, your code will see that the key isn't down yet.

    It's like dropping a letter in the mailbox and then calling somebody to say, "Did you get my letter yet?" Okay, the Raw Input Thread is faster than the Postal Service, but you still have to give it a chance to get the message, query each of the low-level input hooks, decide who the message should be delivered to, and put it in their message queue.

  • The Old New Thing

    Logging the contents of every message box dialog via automation

    • 9 Comments

    Today's Little Program logs the contents of every message box dialog, or anything that vaguely resembles a message box dialog. (Since there's no way for sure to know whether a dialog box is a message box or not.)

    using System.Windows.Automation;
    
    class Program
    {
     [System.STAThread]
     public static void Main(string[] args)
     {
      Automation.AddAutomationEventHandler(
       WindowPattern.WindowOpenedEvent,
       AutomationElement.RootElement,
       TreeScope.Descendants,
       (sender, e) => {
        var element = sender as AutomationElement;
        if (element.GetCurrentPropertyValue(
         AutomationElement.ClassNameProperty) as string != "#32770") {
         return;
        }
    
        var text = element.FindFirst(TreeScope.Children,
         new PropertyCondition(AutomationElement.AutomationIdProperty, "65535"));
        if (text != null) {
         System.Console.WriteLine(text.Current.Name);
        }
       });
      System.Console.ReadLine();
      Automation.RemoveAllEventHandlers();
     }
    }
    

    This is the same pattern as the program we wrote last week, but with different guts when the window opens.

    This time, we see if the class name is #32770, which UI Spy tells us is the class name for dialog boxes. (That this is the numerical value of WC_DIALOG is no coincidence.)

    If we have a dialog, then we look for a child element whose automation ID is 65535, which UI Spy tells us is the automation ID for the text inside a message box dialog. (That the traditional control ID for static controls is -1 and 65535 is the the numerical value of (WORD)-1, is no coincidence.)

    If so, then we print the text.

    If we were cleverer, we could also confirm that the only buttons are OK, Cancel, and so on. Otherwise, we can get faked out by other dialog boxes that contain static text.

  • The Old New Thing

    The changing name of the Microsoft event held in conjunction with Martin Luther King, Jr. Day

    • 9 Comments

    Today is Martin Luther King, Jr. Day, a federal holiday in the United States honoring the civil rights leader and formally serving as a day to reflect on the principles of racial equality and nonviolent social change and more generally to honor Dr. King's legacy through service.

    At Microsoft, the day has been recognized with an event whose name is, um, well, the name keeps changing. Here are the names from recent years:

    • 2006: Martin Luther King, Jr. Day Event
    • 2007: Martin Luther King, Jr. Celebration
    • 2008: Dr. Martin Luther King, Jr. Day Event
    • 2009: MLK Day Event
    • 2010: Celebration to Honor Dr. Martin Luther King, Jr.
    • 2011: Martin Luther King Day of Celebration
    • 2012: Martin Luther King, Jr. Day of Celebration
    • 2013: Dr. Martin Luther King, Jr. Day of Celebration Event

    I like to think this is done intentionally just to keep people on their toes.

    Ironically, although the event "celebrates diversity and inclusion," the email announcing the event inadvertently excludes people with visual impairments because it consists of a giant JPG with no ALT text.

    (No dream report from me today. That would be disrespectful to the man himself.)

  • The Old New Thing

    Using accessibility to monitor windows as they come and go

    • 9 Comments
    Today's Little Program monitors windows as they come and go. When people contemplate doing this, they come up with ideas like installing a WH_CBT hook or a WH_SHELL hook, but one of the major problems with those types of hooks is that they are injected hooks. Injection is bad for a number of reasons.

    • It forces the hook to be in a DLL so it can be injected.
    • Hook activities need to be marshaled back to the main program.
    • Your DLL will capture events only in processes of the same bitness, because you cannot load a 32-bit DLL into a 64-bit process or vice versa.
    • You can inject into an elevated process only if your process is also elevated. If your process is non-elevated, then you will not capture events for windows belonging to elevated processes.

    This is where accessibility comes in handy, because accessibility lets you specify whether you want your hook to be an injected or non-injected one. And if you're non-injected, then the programming model is much simpler because everything happens in your process (indeed, on a single thread).

    Take the scratch program and make the following changes:

    #include <strsafe.h>
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     g_hwndChild = CreateWindow(TEXT("listbox"), NULL,
         LBS_HASSTRINGS | WS_CHILD | WS_VISIBLE | WS_VSCROLL,
         0, 0, 0, 0, hwnd, NULL, g_hinst, 0);
     if (!g_hwndChild) return FALSE;
     return TRUE;
    }
    
    void CALLBACK WinEventProc(
        HWINEVENTHOOK hWinEventHook,
        DWORD event,
        HWND hwnd,
        LONG idObject,
        LONG idChild,
        DWORD dwEventThread,
        DWORD dwmsEventTime
    )
    {
     if (hwnd &&
         idObject == OBJID_WINDOW &&
         idChild == CHILDID_SELF)
     {
      PCTSTR pszAction = NULL;
      TCHAR szBuf[80];
      switch (event) {
      case EVENT_OBJECT_CREATE:
       pszAction = TEXT("created");
       break;
      case EVENT_OBJECT_DESTROY:
       pszAction = TEXT("destroyed");
       break;
      }
      if (pszAction) {
       TCHAR szClass[80];
       TCHAR szName[80];
       szClass[0] = TEXT('\0');
       szName[0] = TEXT('\0');
       if (IsWindow(hwnd)) {
        GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
        GetWindowText(hwnd, szName, ARRAYSIZE(szName));
       }
       TCHAR szBuf[80];
       StringCchPrintf(szBuf, ARRAYSIZE(szBuf),
                       TEXT("%p %s \"%s\" (%s)"), hwnd, pszAction,
                       szName, szClass);
       ListBox_AddString(g_hwndChild, szBuf);
      }
     }
    }
    
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
    {
     ...
      ShowWindow(hwnd, nShowCmd);
    
     HWINEVENTHOOK hWinEventHook = SetWinEventHook(
         EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY,
         NULL, WinEventProc, 0, 0,
         WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
    
      while (GetMessage(&msg, NULL, 0, 0)) {
       TranslateMessage(&msg);
       DispatchMessage(&msg);
      }
    
      if (hWinEventHook) UnhookWinEvent(hWinEventHook);
    ...
    }
    

    This is a generalization of our earlier program which waits for a specific window to be destroyed, except that we now are watching all windows for creation and destruction.

    When you run this program, you see that there is a lot of window activity, but maybe you are interested only in windows when they are shown and hidden. No problem, that's a small change:

      switch (event) {
      case EVENT_OBJECT_SHOW:
       pszAction = TEXT("shown");
       break;
      case EVENT_OBJECT_HIDE:
       pszAction = TEXT("hidden");
       break;
      }
    ...
    
     HWINEVENTHOOK hWinEventHook = SetWinEventHook(
         EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE,
         NULL, WinEventProc, 0, 0,
         WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
    

    Notice that these notifications are received for windows from both 32-bit and 64-bit processes, and that they are received even for windows belonging to elevated processes. You can't do that with an injected hook.

Page 374 of 457 (4,569 items) «372373374375376»