Holy cow, I wrote a book!
Often, in response to some sort of design decision, people will say, "Well, sure, you made this decision because it would allow applications to do Bad Thing, but why not expose it as a setting the user can select? For example, let the user pick a Topper Than Topmost Awesome Top Window Super Top (Extra Super edition), and keep that window on top regardless of what any application does."
Because anything the user can do, an application can do.
Suppose there was a new context menu item for a window called Make this Topper Than Topmost Awesome Top Window Super Top (Extra Super edition). Well, an application could just programmatically send the WM_SYSCOMMAND message with wParam set to SC_TOPPERTHANTOPMOSTAWESOMETOPWINDOWSUPERTOPEXTRASUPER.
WM_SYSCOMMAND
wParam
SC_TOPPERTHANTOPMOSTAWESOMETOPWINDOWSUPERTOPEXTRASUPER
If you say, "Nope, that context menu item is super secret and has a random command ID so nobody knows what its ID is", well, the program could just call GetSystemMenu and enumerate the menu items and then extract the ID from the one whose name is Make this Topper Than Topmost Awesome Top Window Super Top (Extra Super edition).
GetSystemMenu
If you say, "Nope, that menu item will be hidden from enumeration, so programs which enumerate their system menu can't see it", well, the program could just use Accessibility to programmatically open its system menu, and then programmatically click the Make this Topper Than Topmost Awesome Top Window Super Top Super (Extra Super edition) button.
Anything the user can do, a program can do by simply pretending to be the user.
They were both wrong.
A customer using Visual Studio wanted to know how to obtain the current value of the x64 RSP register from a C/C++ function. They noted that on the x86, you can drop to inline assembly, and on the ia64, you can use the __getReg intrinsic to retrieve the value of any register. There is no corresponding __getReg intrinsic on x64.
__getReg
There's no really good way of doing this. The customer can sort of get close with _AddressOfReturnAddress(). Other approximations would be to call _alloca(0), or to call a helper function which allocates a local variable and returns its address.
_AddressOfReturnAddress()
_alloca(0)
Now, if you were the one answering this question and you stopped there, then you'd be guilty of answering the question without first understanding the problem. There must be some larger problem they are trying to solve, and they managed to break it down into two parts, the easy part and the impossible part, and they wanted help with the impossible part.
The customer was kind enough to elaborate.
"We are using RSP to generate a system-wide unique value."
Whoa, there. RSP is not all that unique. After all, if the function gets called twice, the RSP value might be the same the second time. (Maybe they clean up the unique value when the function returns, so this isn't a problem.) And of course, since each process has its own address space, another process can perform the same operation with the same value of RSP.
If you want a system-wide unique 64-bit value, use the function AllocateLocallyUniqueId.
AllocateLocallyUniqueId
Many years ago, I visited the office of a colleague who worked on Internet Explorer in order to work on some problem or other. As we investigated the issue, we took notes on a 5"×7" tear-off notepad which bore the logo Forms³.
My colleague then pointed out to me that we were taking notes on the most expensive notepads in Microsoft history.
Forms³ (pronounced "forms-cubed") was the code name for the project that was the precursor to Trident, the layout engine that powers Internet Explorer. As I recall the story as it was told to me, project management thought it would be cool to have custom notepads made for their project. The people responsible for converting this idea into reality designed a logo, laid it out, and found a vendor to produce the notepads. But there was some sort of misunderstanding (maybe the asked for too many colors? didn't order enough notepads to trigger the bulk discount?), and the price for the notepads ended up being something ridiculous like $10 for a 50-page notepad.
That's how the most expensive notepads in Microsoft history came to be.
Bonus chatter: Oh, by the way, the word "precursor" I used up there? Yeah, that was a euphemism for "cancelled (but used as a learning opportunity)." Microsoft engineers are fond of black humor when it comes to software (especially software made by Microsoft) as a way of coping with adversity, and after the Forms³ project was cancelled, there was a subculture of engineers who morbidly called it Forms tubed.
I dreamed that I was living in a nursing home in some Brazil-like dystopic future.
In this future, people had become so horribly disfigured that they wore flesh-colored suits under their clothes all the time just so they would look good "naked". This vanity extended only to people under the age of around 40. The old people in the home were just your average old people, with spotted, wrinkly skin. Nothing particularly ugly about them; just your average old people. In the dream, I was my current age, but I was living in the home anyway, probably because I became prematurely senile.
A dentist spontaneously appeared to give me a futuristic dental procedure. ("Hey, how'd you get here and what is that zappy thing in my mouth?" Did I mention that I was senile?) After she was done, the dentist said, "I fixed your back four teeth."
"You made them last longer?" I asked.
"No, they'll fail at about the same time as before. They'll just fail differently now."
"What did you do?" I asked.
"I'd rather not say. You can look in the mirror."
With trepidation, I looked in the mirror. My eyes were no good, so I had to ask somebody to tell me what happened.
"Dear God, she installed leeches in your mouth! Mind you, she did a really nice job, getting them to criss-cross like that."
Bonus details: The dentist and dental assistant were hideously disfigured, and I did not notice until later that each had one arm and one stump. The dentist's and assistant's stumps snapped together like a garden hose, making them a grotesque conjoined dental crew.
Today's Little Program takes the symbolic name for a shell property and returns a string suitable for display to the end-user, translated into the user's specified display language.
#include <windows.h> #include <ole2.h> #include <propsys.h> #include <propkey.h> #include <atlbase.h> #include <atlalloc.h> int __cdecl wmain(int argc, PWSTR argv[]) { CCoInitialize init; if (SUCCEEDED(init) && argc == 2) { CComPtr<IPropertyDescription> spdesc; if (SUCCEEDED(PSGetPropertyDescriptionByName( argv[1], IID_PPV_ARGS(&spdesc)))) { CComHeapPtr<wchar_t> spszName; if (SUCCEEDED(spdesc->GetDisplayName(&spszName))) { wprintf(L"%ls\n", static_cast<PWSTR>(spszName)); } } } return 0; }
Run this program with the string System.Music.AlbumArtist on the command line, and the result is the message Album artist on English-language systems.
The actual workings of the program is pretty straightward. We ask the property system for an interface that describes the property name, and ask that interface to give us the display name, which we print out.
Nothing fancy here. The trick is just knowing that the function exists in the first place.
In 2010, a group of scam artists pretended that they were making a film, appropriately titled A Landscape of Lies. They did this so that they could claim over £2.7 million in tax credits intended to boost the British film industry. Her Majesty's Revenue and Customs began to suspect something was up when no apparent progress was being made on the project and the film company's "office" was an empty room. The scammers tried to make the project look legitimate by actually making a movie for £84,000 and releasing it on DVD. (You can watch the trailer here.) The fake movie was convincing enough to trick one film festival into giving it an award (which it later had to take back.)
Related: The claimed DHS-based television show turns out also to have been a scam.
Well, first of all, what's so wrong with that? You have to jump through a lot of hoops when you are in an unusual situation. But by definition, most people are not in an unusual situation, so it's an instance of the Pay for Play principle: The simple case should be easy, and it's okay for the complicated case to be hard. (It's usually difficult to make the complicated case easy; that's why it's called the complicated case.)
The complexity mostly comes from managing the general-purpose PROC_THREAD_ATTRIBUTE_LIST, which is used for things other than just controlling inherited handles. It's a generic way of passing up to N additional parameters to CreateProcess without having to create 2ᴺ different variations of CreateProcess.
PROC_THREAD_ATTRIBUTE_LIST
CreateProcess
The CreateProcessWithExplicitHandles function was just one of the N special-purpose functions that the PROC_THREAD_ATTRIBUTE_LIST tried to avoid having to create. And the special-purpose function naturally takes the special-purpose case and applies the general solution to it. It's complicated because you are now doing something complicated.
CreateProcessWithExplicitHandles
That said, here's one attempt to make it less complicated: By putting all the complicated stuff closer to the complicated function:
typedef struct PROCTHREADATTRIBUTE { DWORD_PTR Attribute; PVOID lpValue; SIZE_T cbSize; } PROCTHREADATTRIBUTE; BOOL CreateProcessWithAttributes( __in_opt LPCTSTR lpApplicationName, __inout_opt LPTSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCTSTR lpCurrentDirectory, __in LPSTARTUPINFO lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation, // here is the new stuff __in DWORD cAttributes, __in_ecount(cAttributes) const PROCTHREADATTRIBUTE rgAttributes[]) { BOOL fSuccess; BOOL fInitialized = FALSE; SIZE_T size = 0; LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL; fSuccess = InitializeProcThreadAttributeList(NULL, cAttributes, 0, &size) || GetLastError() == ERROR_INSUFFICIENT_BUFFER; if (fSuccess) { lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST> (HeapAlloc(GetProcessHeap(), 0, size)); fSuccess = lpAttributeList != NULL; } if (fSuccess) { fSuccess = InitializeProcThreadAttributeList(lpAttributeList, cAttributes, 0, &size); } if (fSuccess) { fInitialized = TRUE; for (DWORD index = 0; index < cAttributes && fSuccess; index++) { fSuccess = UpdateProcThreadAttribute(lpAttributeList, 0, rgAttributes[index].Attribute, rgAttributes[index].lpValue, rgAttributes[index].cbSize, NULL, NULL); } } if (fSuccess) { STARTUPINFOEX info; ZeroMemory(&info, sizeof(info)); info.StartupInfo = *lpStartupInfo; info.StartupInfo.cb = sizeof(info); info.lpAttributeList = lpAttributeList; fSuccess = CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags | EXTENDED_STARTUPINFO_PRESENT, lpEnvironment, lpCurrentDirectory, &info.StartupInfo, lpProcessInformation); } if (fInitialized) DeleteProcThreadAttributeList(lpAttributeList); if (lpAttributeList) HeapFree(GetProcessHeap(), 0, lpAttributeList); return fSuccess; }
There, now the complexity is there because you're a generic complex function, so you have no reason to complain.
A caller of this function might go like this:
HANDLE handles[2] = { handle1, handle2 }; const PROCTHREADATTRIBUTE attributes[] = { { PROC_THREAD_ATTRIBUTE_HANDLE_LIST, handles, sizeof(handles), }, }; fSuccess = CreateProcessWithAttributes( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, ARRAYSIZE(attributes), attributes);
Adam hates the "chained success" style and prefers the "goto" style; on the other hand, other people hate gotos. So to be fair, I will choose a coding style that nobody likes. That way everybody is equally unhappy.
BOOL CreateProcessWithAttributes( __in_opt LPCTSTR lpApplicationName, __inout_opt LPTSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCTSTR lpCurrentDirectory, __in LPSTARTUPINFO lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation, // here is the new stuff __in DWORD cAttributes, __in_ecount(cAttributes) const PROCTHREADATTRIBUTE rgAttributes[]) { BOOL fSuccess = FALSE; SIZE_T size = 0; if (InitializeProcThreadAttributeList(NULL, cAttributes, 0, &size) || GetLastError() == ERROR_INSUFFICIENT_BUFFER) { LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST> (HeapAlloc(GetProcessHeap(), 0, size)); if (lpAttributeList != NULL) { if (InitializeProcThreadAttributeList(lpAttributeList, cAttributes, 0, &size)) { DWORD index; for (index = 0; index < cAttributes && UpdateProcThreadAttribute(lpAttributeList, 0, rgAttributes[index].Attribute, rgAttributes[index].lpValue, rgAttributes[index].cbSize, NULL, NULL); index++) { } if (index >= cAttributes) { STARTUPINFOEX info; ZeroMemory(&info, sizeof(info)); info.StartupInfo = *lpStartupInfo; info.StartupInfo.cb = sizeof(info); info.lpAttributeList = lpAttributeList; fSuccess = CreateProcess( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags | EXTENDED_STARTUPINFO_PRESENT, lpEnvironment, lpCurrentDirectory, &info.StartupInfo, lpProcessInformation); } DeleteProcThreadAttributeList(lpAttributeList); } HeapFree(GetProcessHeap(), 0, lpAttributeList); } } return fSuccess; }
Those who are really adventuresome could try a version of CreateProcessWithAttributes that uses varargs or std::initializer_list.
CreateProcessWithAttributes
std::initializer_list
Is the InterlockedIncrement function broken? One person seemed to think so.
InterlockedIncrement
We're finding that the InterlockedIncrement is producing duplicate values. Are there are any know bugs in InterlockedIncrement?
Because of course when something doesn't work, it's because you are the victim of a vast conspiracy. There is a fundamental flaw in the InterlockedIncrement function that only you can see. You are not a crackpot.
LONG g_lNextAvailableId = 0; DWORD GetNextId() { // Increment atomically InterlockedIncrement(&g_lNextAvailableId); // Subtract 1 from the current value to get the value // before the increment occurred. return (DWORD)g_lNextAvailableId - 1; }
Recall that InterlockedIncrement function increments a value atomically and returns the incremented value. If you are interested in the result of the increment, you need to use the return value directly and not try to read the variable you incremented, because that variable may have been modified by another thread in the interim.
Consider what happens when two threads call GetNextId simultaneously (or nearly so). Suppose the initial value of g_lNextAvailableId is 4.
GetNextId
g_lNextAvailableId
Result: Both calls to GetNextId return 5. Interpretation: "InterlockedIncrement is broken."
Actually, InterlockedIncrement is working just fine. What happened is that the code threw away the unique information that InterlockedIncrement returned and instead went back to the shared variable, even though the shared variable changed its value in the meantime.
Since this code cares about the result of the increment, it needs to use the value returned by InterlockedIncrement.
DWORD GetNextId() { // Increment atomically and subtract 1 from the // incremented value to get the value before the // increment occurred. return (DWORD)InterlockedIncrement(&g_lNextAvailableId) - 1; }
Exercise: Criticize this implementation of IUnknown::Release:
IUnknown::Release
STDMETHODIMP_(ULONG) CObject::Release() { InterlockedDecrement(&m_cRef); if (m_cRef == 0) { delete this; return 0; } return m_cRef; }
Here are some strange but legal declarations in C/C++:
int typedef a; short unsigned typedef b;
By convention, the typedef keyword comes at the beginning of the line, but this is not actually required by the language. The above declarations are equivalent to
typedef
typedef int a; typedef short unsigned b;
The C language (but not C++) also permits you to say typedef without actually defining a type!
typedef enum { c }; // legal in C, not C++
In the above case, the typedef is ignored, and it's the same as just declaring the enum the plain boring way.
enum
enum { c };
Other weird things you can do with typedef in C:
typedef; typedef int; typedef int short;
None of the above statements do anything, but they are technically legal in pre-C89 versions of the C language. They are just alternate manifestations of the quirk in the grammar that permits you to say typedef without actually defining a type. (In C89, this loophole was closed: Clause 6.7 Constraint 2 requires that "A declaration shall declare at least a declarator, a tag, or the members of an enumeration.")
That last example of typedef int short; is particularly misleading, since at first glance it sounds like it's redefining the short data type. But then you realize that int short and short int are equivalent, and this is just an empty declaration of the short int data type. It doesn't actually widen your shorts. If you need to widen your shorts, go see a tailor.¹
typedef int short;
short
int short
short int
Note that just because it's legal doesn't mean it's recommended. You should probably stick to using typedef the way most people use it, unless you're looking to enter the IOCCC.
¹ The primary purpose of this article was to tell that one stupid joke. And it's not even my joke!