• #### Hidden message in a T-shirt, it's been done before

While everybody is trying to figure out the hidden message in Joe Belfiore's T-shirt,¹ I figure I'd give you an easier puzzle.

Here is the pattern of 0's and 1's printed on the T-shirt handed given out at the Windows 8 kick-off meeting. Because you don't have a project until you have a T-shirt.

```              001 01101110 0110
01110011 00100000 01010111
10 01100100 01101111 01110111 011
0000 01001100 01101001 01110110 01100
00000 01001001 01101110 01110100 011001
110010 01101110           1110100 0100010
1111000 0111000             101111 0111001
1100101 011100               01001 0110111
100100 0110111             110011 0010000
010111 01101001           1100100 011011
10111 01110011 00     0 01001100 01101
110 01100101 00100000 01001001 011
0 01100101 01110010 01101110 0
000101 01111000 011100
1 01110010 01100101 0111001001
1001 01101110 01100100 01101111 0111
110011 00100000 01    11 01101001 011011
1100100 0110111            1110011 0010000
01001100 01101                00101 00100000
01001001 0110                  0101 01110010
01101110 0110                  0101 01111000
01110000 01101                10010 01100101
0111001001010111             101110 01100100
1101111 01110111 01110011 00100000 0101011
101001 01101110 01100100 01101111 011101
0011 00100000 01001100 01101001 0111
1 00100000 01001001 01101110 0
10010 01101110 0110
```

The actual shirt clipped many of the digits to make the shape come out smoother. I've filled in the partial digits.

There are at least two typos in the shirt.

It didn't take a room full of developers long to decode the message.

The digits are merely the binary encoding of ASCII characters.

```              001 01101110 0110              // Windo
01110011 00100000 01010111          // ws Wi
10 01100100 01101111 01110111 011      // ndows
0000 01001100 01101001 01110110 01100    //  Live
00000 01001001 01101110 01110100 011001   //  Inte
110010 01101110           1110100 0100010  // rnetE
1111000 0111000             101111 0111001  // xplor
1100101 011100               01001 0110111  // er In
100100 0110111             110011 0010000  // dows
010111 01101001           1100100 011011   // Windo
10111 01110011 00     0 01001100 01101    // ws Li
110 01100101 00100000 01001001 011      // ve In
0 01100101 01110010 01101110 0        // terne
000101 01111000 011100            // tExpl
1 01110010 01100101 0111001001        // orer  typo
1001 01101110 01100100 01101111 0111     // indow
110011 00100000 01    11 01101001 011011   // s Win
1100100 0110111            1110011 0010000  // dows
01001100 01101                00101 00100000 // Live
01001001 0110                  0101 01110010 // Inter
01101110 0110                  0101 01111000 // netEx
01110000 01101                10010 01100101 // plore
0111001001010111             101110 01100100 // r[?nd typo
1101111 01110111 01110011 00100000 0101011  // ows L
101001 01101110 01100100 01101111 011101   // indow
0011 00100000 01001100 01101001 0111     // s Liv
1 00100000 01001001 01101110 0        // e Int
10010 01101110 0110              // ernet
```

¹ Looks like they figured it out.

• #### Why is there an invisible U+202A at the start of my file name?

 IMG31415 Properties ×
General
Security
Details

Object name:       C:\Users\Bob\Desktop\IMG31415.jpg
Group or user names:
 👤  👤 SYSTEM 👤 Bob 👤  👤 Administrators
To change permissions, click Edit. Edit...
 Permissions for SYSTEM Allow Deny Full control ✓ Modify ✓ Read & execute ✓ Read ✓ Write ✓ Special permissions
Apply Cancel OK

Okay, that was a trick question, because the thing that's strange is not visible to the eye.

Use the mouse to click in the object name field (the thing with the file path), then press Home, followed by Shift+End to select the entire text, then Ctrl+C to copy it to the clipboard.

Now things get interesting.

Fire up Notepad, paste the path into the Notepad document, and save it to the desktop with the name tricky.txt.

Huh? Notepad says, "This file contains characters in Unicode format which will be lost if you save this as an ANSI encoded text file."

What Unicode characters are we talking about? There are no accented letters here. All the characters in the file name fit in the ASCII repertoire.

Go to a command prompt and type

```C:\Users\Bob> copy "
```

and then paste the path from the clipboard, then close the quotation mark, and hit Enter.

```C:\Users\Bob> copy "?C:\Users\Bob\Desktop\IMG31415.jpg"
The filename, directory name, or volume label syntax is incorrect.
```

Wait, what? Where did that rogue question mark come from?

The answers to the two questions are the same: The mysterious Unicode character, which is invisible in Notepad, and which appears as a question mark on the command line, is U+202A (LEFT-TO-RIGHT EMBEDDING).

We saw some time ago that you can, as a last resort, insert the character U+202B (RIGHT-TO-LEFT EMBEDDING) to force text to be interpreted as right-to-left. The converse character is U+202A (LEFT-TO-RIGHT EMBEDDING), which forces text to be interpreted as left-to-right.

The Security dialog box inserts that control character in the file name field in order to ensure that the path components are interpreted in the expected manner. Unfortunately, it also means that if you try to copy the text out of the dialog box, the Unicode formatting control character comes along for a ride. Since the character is normally invisible, it can create all sorts of silent confusion.

(We're lucky that the confusion was quickly detected by Notepad and the command prompt. But imagine if you had pasted the path into the source code to a C program!)

• #### What does it mean when the Advanced Security Settings dialog says that an ACE was inherited from "Parent Object" without naming the specific parent?

The Advanced Security Settings dialog shows the ACEs in an object's ACL, and one of the pieces of information is a column labeled Inherited from which identifies whether the ACE was inherited, and if so, from where. A customer observed that when they opened the Advanced Security Settings dialog, one of their objects had an ACE that showed Parent Object as the Inherited from.

Name: C:\dir1\dir2\dir3\dir4\file
 Type Principal Access Inherited from Applies to Allow Administrators Full control None This folder only Allow Administrators Full control C:\dir1\dir2\ This folder, subfolders and files Allow SYSTEM Full control C:\dir1\dir2\ This folder, subfolders and files Allow CREATOR OWNER Full control C:\dir1\dir2\ Subfolders and files only Allow Users Read & execute C:\dir1\dir2\ This folder, subfolders and files Allow Users Special C:\dir1\dir2\ This folder and subfolders Allow Authenticated Users Full control Parent Object ⇐ This folder, subfolders and files

However, when they went to the parent object `C:\dir1\dir2\dir3\dir4`, that ACE is nowhere to be found.

Name: C:\dir1\dir2\dir3\dir4
 Type Principal Access Inherited from Applies to Allow Administrators Full control None This folder only Allow Administrators Full control C:\dir1\dir2\ This folder, subfolders and files Allow SYSTEM Full control C:\dir1\dir2\ This folder, subfolders and files Allow CREATOR OWNER Full control C:\dir1\dir2\ Subfolders and files only Allow Users Read & execute C:\dir1\dir2\ This folder, subfolders and files Allow Users Special C:\dir1\dir2\ This folder and subfolders Allow Everyone Full control C:\dir1\dir2\ This folder, subfolders and files

How can an ACE be inherited from its parent, when it doesn't exist in the parent?

The Advanced Security Settings dialog is trying to be helpful, but in doing so, it implies a greater level of confidence than it actually offers.

ACEs do not specify where they were inherited from. There is merely a bit in the ACE called `INHERITED_ACE` which means, "This ACE was created via inheritance." Not only does this bit not tell you where the ACE was inherited from, but the bit might even be wrong! Anybody can go in and toggle the bit, and bingo, you now have forged the "I was created via inheritance" flag. Another way this flag could be out of sync is if the user started an ACL update operation and then canceled it partway through.

The Advanced Security Settings dialog uses the `Get­Inheritance­Source` function to determine the source of each ACE. That function walks up the parent chain looking for matching inheritable ACEs. If a match is found, then the Advanced Security Settings dialog shows that parent as the Inherited from. Otherwise, it shrugs its shoulders and says Parent Object.

The string Parent Object means "This ACE claims to have been inherited from somewhere, but I can't figure out where, so I'm just going to be vague and say that it came from some parent object somewhere." Perhaps a less confusing string would have been Ancestor Object or even simply Unknown.

The Advanced Security Settings dialog figured that it would go the extra mile and instead of merely saying Inherited = Yes, it would try to find a parent object that was the most likely source of the inheritance. But by doing that, you came to expect it, and then you got upset when it wasn't able to come through for you. No good deed goes unpunished.

• #### Creating a window that can be resized in only one direction

Today's Little Program shows a window that can be resized in only one direction, let's say vertically but not horizontally.

```UINT OnNcHitTest(HWND hwnd, int x, int y)
{
UINT ht = FORWARD_WM_NCHITTEST(hwnd, x, y, DefWindowProc);
switch (ht) {
case HTBOTTOMLEFT:  ht = HTBOTTOM; break;
case HTBOTTOMRIGHT: ht = HTBOTTOM; break;
case HTTOPLEFT:     ht = HTTOP;    break;
case HTTOPRIGHT:    ht = HTTOP;    break;
case HTLEFT:        ht = HTBORDER; break;
case HTRIGHT:       ht = HTBORDER; break;
}
return ht;
}

HANDLE_MSG(hwnd, WM_NCHITTEST, OnNcHitTest);
```

We accomplish this by removing horizontal resize behavior from the left and right edges and corners. For the corners, we remove the horizontal resizing, but leave the vertical resizing. For the edges, we remove resizing entirely by reporting that the left and right edges should act like an inert border.

Wait, we're not done yet. This handles resizing by grabbing the edges with the mouse, but it doesn't stop the user from hitting Alt+Space, followed by S (for Size), and then hitting the left or right arrow keys.

For that, we need to handle `WM_GET­MIN­MAX­INFO`.

```void OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO lpmmi)
{
RECT rc = { 0, 0, 500, 0 };
GetWindowExStyle(hwnd));

lpmmi->ptMaxSize.x =
lpmmi->ptMinTrackSize.x =
lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
}

HANDLE_MSG(hwnd, WM_GETMINMAXINFO, OnGetMinMaxInfo);
```

This works out great, except for the case of being maximized onto a secondary monitor, because we run into the mixed case of being small than the monitor in the horizontal direction, but larger than the monitor in the vertical direction.

```void OnGetMinMaxInfo(HWND hwnd, LPMINMAXINFO lpmmi)
{
RECT rc = { 0, 0, 500, 0 };
GetWindowExStyle(hwnd));

lpmmi->ptMaxSize.x =
lpmmi->ptMinTrackSize.x =
lpmmi->ptMaxTrackSize.x = rc.right - rc.left;

MONITORINFO mi = { sizeof(mi) };
GetMonitorInfo(MonitorFromWindow(hwnd,
MONITOR_DEFAULTTOPRIMARY), &mi);
lpmmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top
- lpmmi->ptMaxPosition.y + rc.bottom;
}
```

The math here is a little tricky. We want the window height to be the height of the work area of the window monitor, plus some extra goop in order to let the borders hang over the edge.

The first two terms are easy to explain: `mi.rcWork.bottom - mi.rcWork.top` is the height of the work area.

Next, we want to add the height consumed by the borders that hang off the top of the monitor. Fortunately, the window manager told us exactly how much the window is going to hang off the top of the monitor: It's in `lpmmi->ptMaxPosition.y`, but as a negative value since it is a coordinate that is off the top of the screen. We therefore have to negate it before adding it in.

Finally, we add the borders that hang off the bottom of the work area.

Yes, handling this mixed case (where the window is partly constrained and partly unconstrained) is annoying. Sorry.

• #### That's not how you start a boat

Tomorrow is Opening Day of the Seattle boating season. (Which, as I noted some time ago, is purely a social occasion with no legal significance.)

One of my colleagues is not much of a boat person, but his wife is. (In fact, she's a commercial fisherman.) They were on board some fancy boat or other as it sat docked. He was up on the top deck—this being a boat so fancy that it had an upstairs and a downstairs—and as the preparations were made for heading out, his wife called out to him to start the boat, since he's up there already.

Now, as I mentioned, my colleague isn't much of a boat person. But he figured, "These modern boats, how hard can it be? It's probably pushbutton nowadays." So he looked at the control panel and saw a bright red button. "Red button, that's probably the power button, right?"

The button looked like a letter V with a dot in the middle, or at least that's how it was described to me.

Immediately upon pressing the button, alarms rang on board the boat, and the Coast Guard called them on the radio.

It turns out that the V with a dot is not the power button. It is the "man overboard" button. The dot is the person's head, and the letter V represents the two flailing arms.

• #### How does the window manager adjust ptMaxSize and ptMaxPosition for multiple monitors?

There is a note down in the documentation for the `MIN­MAX­INFO` structure:

For systems with multiple monitors, the ptMaxSize and ptMaxPosition members describe the maximized size and position of the window on the primary monitor, even if the window ultimately maximizes onto a secondary monitor. In that case, the window manager adjusts these values to compensate for differences between the primary monitor and the monitor that displays the window.

People ask about the nature of this "compensation" and how they should deal with it.

When the window manager sends the `WM_GET­MIN­MAX­INFO` message to a top-level window, the `MIN­MAX­INFO` structure comes preinitialized with the following information:

Member Initialized To
ptMaxSize The dimensions of the primary monitor.
ptMaxPosition The upper left corner of the window if it were maximized on the primary monitor.
ptMinTrackSize The minimum size the user can resize the window.
ptMaxTrackSize The maximum size the user can resize the window (the union of all monitors, basically).

Two of these values are monitor-relative and are therefore subject to adjustment.

ptMax­Position is easy. The point is moved to a corresponding relative position on the window's actual monitor.

ptMax­Size is trickier. If the specified size is greater than or equal to the size of the primary monitor, then the ptMax­Size is adjusted to include the difference in size between the primary monitor and the actual monitor. In other words, if ptMax­Size is 20 pixels larger than the primary monitor, then it will be adjusted to being 20 pixels larger than the actual monitor. But if ptMax­Size does not completely cover the monitor, then its value is used as-is.

That is what the documentation is referring to when it says "In that case." The case is "if the window ultimately maximizes onto a secondary monitor."

There is a bit of a gotcha here: If your window is larger than the screen in one direction but not another. For example, you may have a narrow maximum width but a tall maximum height. (Think console windows.) In that case, you will need to use `Monitor­From­Window` to get the parameters for the actual monitor so you can set your maximum height appropriately.

We'll dig a little deeper into this gotcha next time.

• #### How can I tell if a file is an image of some type?

A customer wanted to know if there is some standard way of determining from the full path to a file whether it is a photo or image. They are writing a program that transfers a bunch of files to a back-end server, and they want to treat photos differently from other files.

You can use the `Assoc­Get­Perceived­Type` function to classify a file into one of a variety of categories. You can consult the document to see the full list, but it's things like "image", "audio", "video", "document". This information is obtained by studying the file extension and looking up the registered perceived type.

Let's take the function out for a spin:

```#define UNICODE
#define _UNICODE
#include <windows.h>
#include <shlwapi.h>
#include <stdio.h> // horrors! mixing C and C++!

int __cdecl wmain(int argc, wchar_t **argv)
{
PERCEIVED perceived;
PERCEIVEDFLAG flag;
PWSTR pszType;
if (argc > 1 && SUCCEEDED(AssocGetPerceivedType(
PathFindExtensionW(argv[1]),
&perceived, &flag, &pszType)))
{
wprintf(L"Type is %ls\n", pszType);
if (perceived == PERCEIVED_TYPE_IMAGE) {
wprintf(L"Hey, that's an image!\n");
}
}
return 0;
}
```

Run this program and give a file name, or just an extension (with the dot) as the command line parameter. It will tell you the perceived type and include a special message if the type is an image.

But let's look at the customer's question again. It's not clear whether they are trying to identify files by file format or by classification. For eaxmple, suppose the file in question is a TIFF image. The `Assoc­Get­Perceived­Type` function will report this as an image, because, well, it's an image. But that may not be a file format that their back-end server supports.

• If they wanted to know whether the file is a PNG, GIF, or JPG because those are the image formats supported by their back-end server, then they need to check for those specific extensions (and possibly even sniff file contents if they are paranoid).
• If they care only that the file represents some sort of image (possibly in a format their program does not know how to parse), because they want to, say, upload all images into a Pictures folder regardless of the image format, then they should use the Perceived Type.

The customer thanked us for the pointer to the `Assoc­Get­Perceived­Type` function and especially for the clarifying remarks. "It turns out that the feature specification was not clear on the definition of 'image file', so that's something we will need to resolve ourselves. But the information you provided will definitely solve our problem, once we figure out what our problem is!"

• #### Is the atom returned by RegisterClass(Ex) a "real" atom?

A customer was debugging some code that calls `Register­Class` on a class that's already been registered. In this case, it was registered by another DLL in the same process. Normally, this wouldn't be a problem, because each DLL passes its own instance handle to `Register­Class` so that there are no name collisions. However, in this case, both DLLs are passing the `CS_GLOBAL­CLASS` flag, which means that collisions can occur after all.

The customer found that the call to `ERROR_CLASS_EXISTS`, which makes sense in the case of a collision in the global class table. The code then calls `Find­Atom` to look up the class name, but `Find­Atom` fails, saying, "No such atom."

Does this mean that the class atom created by `Register­Class` isn't a real atom? How can you get the atom for a window class that somebody else has registered?

Okay, let's look at these questions in order.

Is the atom a real atom? Well, if you ask the atom, it'll say, "I feel real to me." But remember that there are many atom tables in the system.

• The global atom table.
• One local atom table for each process.
• The registered clipboard format atom table.
• The registered window class atom table.
• Possibly others, too.

The `Find­Atom` function searches the process's local atom table, so if you go looking for a registered window class atom there, you're going to come up empty.

There is no way to query the registered window class atom table directly. You only get indirect access to create items in it through `Register­Class`, and that atom can in turned by used as a synonym for the class name.

Now, it so happens that although the `Get­Class­Info` function formally returns a `BOOL`, i.e., an integer that is zero on failure or nonzero on success, the success case of `Get­Class­Info` actually returns the class atom. This behavior is not documented, but the ATL library relies on it, so if the undocumented behavior changes in the future, it'll have an app compat shim to keep ATL happy.

• #### Access to a file's attributes is controlled by two things

We saw some time ago that permission to delete a file is granted either

• if you have `DELETE` access on the file, or
• if you have `FILE_DELETE_CHILD` access on the containing directory.

File attributes behave in an analogous way.

Permission to read a file's attributes is granted either

• if you have `FILE_READ_ATTRIBUTES` access on the file, or
• if you have `FILE_LIST_DIRECTORY` access on the containing directory.

If you want the file's attributes, you could always get it by reading the directory, because one of the pieces of information you get from `Find­First­File` is the file attributes. Therefore, having permission to read a directory implicitly gives you permission to read the attributes of any file in that directory.

(Note, of course, that write permission on attributes is another story.)