• The Old New Thing

    The C runtime library cannot be mixed and matched


    In 2011, a customer had an application written in C++ with Visual Studio 2003 that consumes a static library provided by a third party, let's call it contoso.lib. Now, contoso.lib is a static library compiled with Visual C++ 6. The customer is migrating from Visual Studio 2003 to Visual Studio 2008, but they are still using that old contoso.lib library that was compiled with Visual C++ 6. They were afraid that they would encounter some unresolved externals due to name mangling issues, but they were pleasantly surprised when there were no such issues.

    Now the questions.

    1. Is it correct to link a VC6 static library into a VS2008 project?
    2. Even if the linking is successful, do you see any issues or disadvantages with this approach?

    The customer liaison's opinion was "Due to missing security features like SAFESEH, GS, DYNAMICBASE, and NXCOMPAT, there may be a lot of drawbacks to using VC6 libraries in a VS2008 project. What do you think?"

    It's nice that you're thinking about the security features added in recent versions of Visual Studio, using a generous definition of recent to mean less than nine years old. But the issue is more fundamental than just security. The issue is correctness.

    You cannot mix libraries across compiler versions. That you're trying to mix libraries with compiler versions that are nineteen years apart in age is mind-boggling. The Win32 ABI does not extend into compiler-specific behavior, like its internal lookup tables for exception dispatching, private helper functions for RTTI, class member layout, the order of base classes, the implementations of STL classes, the layout of various internal structures, or what happens if you return FALSE from Dll­Main.

    Name mangling will not catch any of these issues. If you modify a class, say by adding a new member variable or base class, the mangled name does not change, even though the new class is probably incompatible with the old one.

    Assuming you manage to dodge all the link errors, what will happen is that these discrepancies will manifest themselves as random failures or memory corruption at run time.

    You will have to go back to Contoso and ask them for a version of the library that is compatible with Visual Studio 2008.

    (Then again, since this question was asked in 2011, they may want to go straight to Visual Studio 2010, which was the most recent version of Visual Studio available at the time.)

    Bonus chatter: Another solution is to create a project in Visual Studio 2003 whose sole job is to wrap the static library in a DLL. The rest of your program can be developed in Visual Studio 2008, using the DLL interface to access the static library.

  • The Old New Thing

    How do you get network connectivity from the worst PC in the world?


    Some time ago, I wrote about the two worst PCs ever. The worst PC of all time, according to PC World magazine was the Packard Bell PC. As installed at the factory, the computer came with every single expansion slot filled. Need to add a peripheral to your computer? Ha-ha, you can't!

    Now, this was in the days before motherboards had integrated network adapters, and a network adapter was not one of the expansion cards that came preinstalled. But I needed to get network access in order to install the latest builds, and I teased at the end of the article that I had to resort to other devious means of obtaining network connectivity.

    Nobody ever asked me to follow up on that teaser, but I'm going to answer it anyway. "So, Raymond, how did you get network connectivity on a computer that had no network adapter and nowhere to plug in a network adapter card?"

    Of course, the cheat would be to unplug one of the existing expansion cards (the dial-up modem was a good candidate), but that would remove a piece of hardware that the senior executive's identical home PC was using.

    My solution was to use a little-known feature of Windows 95 known as Direct Cable Connection, or DCC. DCC allowed you to use your parallel port as a network adapter. (I am not making this up.) I obtained a special cable from the manager of the DCC project and hooked up one end to the Packed Bell PC and the other end to my main development machine, which acted as a bridge between the Packard Bell PC and the rest of the corporate network.

    I installed new builds of Windows 95 this way, which was a great source of amusement (and by amusement, I mean frustration) to the Windows 95 setup team, who found themselves dealing with failures that occurred on a network configuration most of them had never heard of. (But which, realistically, was one of the flakiest network configurations in the world.)

    I also ran nightly stress tests this way, which offered a similar degree of amusement/frustration to whatever developer had to investigate the failures turned up by the accursed machine. For example, one of the things that made debugging difficult was that if you broke into the kernel debugger for too long, the DCC network connection dropped, and you lost network connectivity.

    I think it's kind of fitting that the worst PC in the world also offered the worst debugging experience in the world.

  • The Old New Thing

    Raymond's Windows Universal Samples API concordance


    If you want to find an SDK sample that uses a particular feature of the Universal Windows Platform, you can try my Windows Universal Samples API concordance on http://oldnewthing.github.io/Windows-universal-samples/. Expand the tree view to find the thing you're interested in, and it will provide links to every line of C# code that uses it.

    The following deep links are also supported:

    I will try to keep this site in sync with the latest release of the Windows Universal Samples repo. The site is kind of ugly right now, but it works just barely well enough to do what I needed.

    Note that the concordance is not complete. There are holes in the analysis which I hope to fill in over time. But at least it's a decent start.

    Eventually, I'll blog about the program I used to generate this information. The short answer is Roslyn. That's why it searches only in C# samples.

  • The Old New Thing

    Cynical interpretations of various project milestones

    Official name Cynical interpretation
    Coding milestone Writing new bugs
    Integration milestone Merging bugs from other teams
    Stabilization milestone Fixing bugs
  • The Old New Thing

    I guess this explains why Warren Buffett hasn't retired


    Microsoft provides to its employees a retirement calculator that is more detailed than the kinds you can find on the Internet. After gathering all sorts of information, including your savings and spending patterns, it draws some nice bar charts and tells you how many years you should continue working before you can retire.

    Even if I tell the calculator that I have a billion dollars in savings, it still tells me, "You should work one more year."

    Clarifying note: I don't actually have a billion dollars in savings.

  • The Old New Thing

    Using an intermediate library to make the main library retargetable


    A customer was developing a static library targetting both Windows XP Win32 applications and universal Windows apps. (This was before Windows XP reached end-of-life.)

    Our library uses critical sections, but unfortunately there is no version Initialize­Critical­Section that is available to both Windows XP Win32 applications and universal Windows apps. Universal Windows apps must use Initialize­Critical­Section­Ex, but that function is not available to Windows XP Win32 applications. Is there a way to dynamically target both Windows XP Win32 applications and universal Windows apps, pass WACK validation, and still have one library?

    We thought we could use Get­Module­Handle and Get­Proc­Address to detect which platform we are one, but Get­Module­Handle is not allowed in universal Windows apps, so we're back where we started.

    Are we stuck having two versions of our library, one for Windows XP Win32 applications and one for universal Windows apps?

    Runtime dynamic linking (Load­Library, Get­Proc­Address) is not permitted in universal Windows apps, which means that for universal Windows apps, you must have an entry for Initialize­Critical­Section­Ex in your import table. But if that function is in your input table, then it won't load on Windows XP.

    (You might think that you could have a second library to be used by Windows XP clients that implements the Initialize­Critical­Section­Ex function. Unfortunately, you will run afoul of dllimport.)

    You are going to have to have separate libraries at some point, but you don't have to have two versions of your library. You could build your library to call, say, Contoso­Initialize­Critical­Section, and have two helper libraries, one for Windows XP Win32 applications and one for universal Windows apps, each of which implement the Contoso­Initialize­Critical­Section function in a manner appropriate to the target.

    In other words, people targeting Windows XP would link to ContosoCore.dll and ContosoXPSupport.dll. People writing universal Windows apps would link to ContosoCore.dll and ContosoStoreSupport.dll.

    This approach has a few advantages:

    • It's simple, works (because it's so simple), and everybody understands it.
    • All the files in your core library need to be compiled only once.

    The second clause pays off if your library is large, or if you need to add new operating system targets.

    Update: I guess I didn't make it clear. My suggestion is that Contoso­Core.dll link to the nonexistent Contoso­Support.dll. If your program targets Windows XP, then rename Contoso­XP­Support.dll to Contoso­Support.dll. If your program is a universal Windows app, then rename Contoso­Store­Support.dll to Contoso­Support.dll.

    This technique also works with static libraries. You have a single Contoso­Core.lib which calls a Contoso­Initialize­Critical­Section function. There are two implementations of Contoso­Initialize­Critical­Section, one in Contoso­XP­Support.lib and another in Contoso­Store­Support.lib. Each application chooses which support library to link in.

  • The Old New Thing

    When I change the icon in my shortcut, why doesn't it update on the screen?


    A customer was having trouble updating the icon in one of their shortcuts. Here's what they shared with us:

        i_shell_link->SetIconLocation(icon_file.value().c_str(), 0);

    "Changing the icon from the shortcut property sheet works, but it's not working from our code. Is the shortcut property sheet using a different API from IShell­Link::Set­Icon­Location? In desperation, we added


    but that didn't help. Did we get the flags to SHChange­Notification wrong?"

    The property sheet does use the IShell­Link::Set­Icon­Location method to change the shortcut icon. What the customer forgot was to save their changes!

        i_persist_file->Save(NULL, TRUE);

    where i_persist_file is the IPersist­File that they used to load the shortcut, or they can use Query­Interface to get a new pointer.

    The SHCNE_ASSOC­CHANGED notification is unnecessary, and in fact it's overkill. That's like saying, "I want to change the color of my sofa, so I'm going to demolish my house, rebuild it, and then refurnish it with a new sofa."

    If you want to send a notification to say, "Hey, I updated this file, please go refresh any data you have cached about it," you can do a

        SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, fullpath, NULL);
  • The Old New Thing

    I saved some files into the Program Files directory, and now they're gone!


    A customer reported that they saved some files in the Program Files directory, but when they went back to check, the files were gone! What happened?

    What most likely happened is UAC Virtualization.

    UAC Virtualization kicks in for applications which were designed for versions of Windows prior to Windows Vista. These applications often assume that they are running with administrative privileges, and when they try to write to file or registry locations that are normally restricted to administrators, they expect them to succeed. So UAC Virtualization lets those writes succeed by secretly redirecting them to another location inside the user profile. The reverse happens when the application later tries to read the file or registry key: If the file or key exists in the redirected location, it is used instead of the one in the administrative location.

    You can read the above-linked TechNet article for details. The short version is that you can find the files in the %LOCAL­APP­DATA%\Virtual­Store directory and the keys under the HKEY_CURRENT_USER\Software\Classes\Virtual­Store key.

    Bonus chatter: There used to be a button in Explorer called Compatibility Files that appears if you are looking at a directory that has a corresponding Virtual Store, and clicking it takes you to that Virtual Store. The button was removed in Windows 8 based on telemetry feedback that showed that very nearly nobody ever clicked on it or even cared about it. Furthermore, in the years since UAC was originally introduced, the number of applications which require UAC Virtualization has dropped significantly, so the need to access items in the Virtual Store dropped correspondingly.

  • The Old New Thing

    Microspeak: move the needle


    The phrase move the needle is part of general business jargon, but it is very popular here at Microsoft. You need to know what it means, and more importantly, you need to be willing to throw it around yourself in order to sound more hip and with-it.

    In general business speak, move the needle means generates a reaction, but at Microsoft, it has the more general sense of provide a perceptible improvement.

    The metaphor here is that there is some sort of meter, like a speedometer or VU meter. Back in the old days, these meters were analog rather than digital, and they consisted of a calibrated arc with a needle that pointed at the current value.

    To move the needle is to have a noticeable effect, presumably positive.

    Here are some citations.

    It's clear that we will need to make some additional improvements if we are to move the needle on performance for customers with high traffic.
    By investing in this solution we move the needle forward on reducing friction and increasing speed to measurable value.

    That second one wins the buzzword award for today.

  • The Old New Thing

    How do I enumerate remembered connections that are not currently connected?


    Harry Johnston wanted to know how to get a list of remembered (but not currently connected) drive mappings.

    The idea here is to make a tweak to the Little Program. Start with what we had and make these changes:

    int __cdecl main(int, char **)
     HANDLE hEnum;

    This changes the program from enumerating connected resources to enumerating remembered resources.

    The last step is to skip the remembered resources that are also connected. But this part is not Win32 programming; it's just programming, For each remembered resource, check if the lpLocal­Name is non-null and matches an lpLocal­Name that came out of an enumeration of connected resources.

    So let's do it. We start with the header files:

    #define UNICODE
    #define _UNICODE
    #define STRICT
    #include <windows.h>
    #include <stdio.h> // horrors! Mixing C and C++ I/O!
    #include <string>
    #include <set>
    #include <memory>
    #include <winnetwk.h>

    Since we are using classes like std::set which throw exceptions, we need to wrap our resources inside RAII classes. Here's one for network resource enumeration:

    class CNetEnumerator
     CNetEnumerator() = default;
     ~CNetEnumerator() { if (m_hEnum) WNetCloseEnum(m_hEnum); }
     operator HANDLE() { return m_hEnum; }
     HANDLE* operator&() { return &m_hEnum; }
     HANDLE m_hEnum = nullptr;

    Here is our function to enumerate all network resources. It uses a callback because arghhhhhhhhhhh wishes it were so.

    template<typename Callback>
    void for_each_network_resource(
        DWORD dwScope,
        DWORD dwType,
        DWORD dwUsage,
        LPNETRESOURCE pnrIn,
        Callback callback)
     CNetEnumerator hEnum;
     WNetOpenEnum(dwScope, dwType, dwUsage, pnrIn, &hEnum);
     const DWORD elements = 65536 / sizeof(NETRESOURCE);
     static_assert(elements > 1, "Must have room for data");
     std::unique_ptr<NETRESOURCE> buffer(new NETRESOURCE[elements]);
     DWORD err;
     do {
      DWORD cEntries = INFINITE;
      DWORD cb = elements * sizeof(NETRESOURCE);
      err = WNetEnumResource(hEnum, &cEntries, buffer.get(), &cb);
      if (err == NO_ERROR || err == ERROR_MORE_DATA) {
       for (DWORD i = 0; i < cEntries; i++) {
     } while (err == ERROR_MORE_DATA);

    There is a bit of trickery to get the enumeration buffer into a form that C++ likes. We had previously used Local­Alloc, which is guaranteed to return memory suitably aligned for NETRESOURCE. However, we can't do it for new BYTE[], since that returns only byte-aligned data. We solve this problem by explicitly allocating NETRESOURCE objects, but choosing a number so that the result is close to our desired buffer size.¹

    We need another helper class so we can create a case-insensitive set.

    struct CaseInsensitiveWstring
     bool operator()(const std::wstring& a, const std::wstring& b) const {
      return CompareStringOrdinal(a.c_str(), a.length(),
                                  b.c_str(), b.length(), TRUE) == CSTR_LESS_THAN;

    Okay, now we can start doing actual work:

    void report(PCWSTR pszLabel, PCWSTR pszValue)
     printf("%ls = %ls\n", pszLabel, pszValue ? pszValue : L"(null)");
    int __cdecl wmain(int, wchar_t **)
     std::set<std::wstring, CaseInsensitiveWstring> connected;
     // Collect the local resources which are already connected.
      RESOURCETYPE_DISK, 0, nullptr, [&](LPNETRESOURCE pnr) {
       if (pnr->lpLocalName != nullptr) {
     // Now look for remembered resources that are not connected.
      RESOURCETYPE_DISK, 0, nullptr, [&](LPNETRESOURCE pnr) {
       if (pnr->lpLocalName == nullptr ||
           connected.find(pnr->lpLocalName) == connected.end()) {
        report(L"localName", pnr->lpLocalName);
        report(L"remoteName", pnr->lpRemoteName);
        report(L"provider", pnr->lpProvider);
     return 0;

    Not exciting. Mostly consists of boring typing. But hey, that's what programming is like most of the time.

    ¹ If we were being super-weenies about the buffer size, we could have written

     union EnumBuffer {
      BYTE bytes[65536];
     std::unique_ptr<EnumBuffer> buffer(new EnumBuffer());
     LPNETRESOURCE pnr = &buffer->nr;
      DWORD cb = sizeof(EnumBuffer);
Page 3 of 460 (4,598 items) 12345»