January 15th, 2026
likeintriguing4 reactions

When programs assume that the system will never change, episode 4: Stealing strings

A customer had a program that automated a workflow, and one of the steps was to run a console program that came with Windows, and that console program printed a confirmation prompt “Are you sure (Y/N)?“. They wanted to programmatically say Yes, but since the customer had employees around the world, they couldn’t hard code the letter Y into their program. (For example, in German, it would be J/N instead of Y/N.) They also realized that the characters for affirmative and negative replies were stored in resources in the executable, so instead of having a big table of affirmative and negative replies for each language, they could load those strings from the executable and use them to answer the questions. They wanted to know if this is acceptable and supported.

No, it’s neither supported nor acceptable.

Unless documented otherwise, resource strings are considered implementation details of the program. Absent any promises to the contrary, Windows reserves the right to change any resource string, or to move them to another location.

Indeed, one of my colleagues gave an example of how this actually happened in recent memory.

The translation team received a bug report that one of the programs had mistranslated “Y/N”. They fixed the translation, but since the fix was going out in a servicing release, they couldn’t change the existing incorrect string because that would invalidate Language Interface Packs. Instead, they had to abandon the old string and create a new one. All the languages would then translate the new string to Yes and No, which would just be copied from the existing translation, except for the language that had an incorrect translation, in which case it would be re-translated, but correctly this time. The program would switch to using the new better-translated string for determining whether the user responded in the affirmative or negative, and the old string would become orphaned, waiting to be cleaned up at the next major release.

If the customer had been extracting strings directly from the binary, they would continue extracting the yes/no string from its old location, even though the program was using the string in its new location.

The customer should provide appropriate affirmative and negative answers, or even better, avoid having to answer the question at all. I forget which program it was, but maybe it had a /F or /Q option to suppress the confirmation prompt. Even better would to avoid the console program at all and access the underlying functionality in a programmatic way. (For example, use Copy-Item or CopyFile instead of xcopy.exe; or use Set-ItemProperty or RegSetValueEx instead of reg.exe.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

26 comments

Discussion is closed. Login to edit/delete existing comments.

Sort by :
  • Daniel Flöijer

    I don't like calling executables programmatically either, but sometimes they provide something that's not available another way. Besides many 3rd party executables one example that I ran into is appcmd.exe that can shutdown an appool in IIS syncronously while the powershell command PS module WebAdministration only offers async. That means that if you're waiting for the apppool to shut down before doing something, like updating a database, you have to create a loop to check if the appool has finished shutting down. You then run into the issue of if you should wait between each check in the loop, creating...

    Read more
  • Ivan Kljajic

    Isn’t the solution to this to just have the program make a call to an online LLM to resolve the mismatch and act based on the response? The first letter of the acronym literally describes the issue…. ..

    • Richard Ward

      “Large”?

  • Mason Wright

    A useful reminder that resource strings are not an API surface and should never be treated as one. The example with LIPs and servicing updates illustrates the core risk clearly: even a “minor” localization fix can invalidate any logic that scrapes strings from binaries. From an automation and reliability standpoint, the only safe options are documented switches (e.g., /Q, /F), structured APIs, or higher-level tooling designed for non-interactive use. Anything else is effectively coupling to an implementation detail and will eventually break, often silently.

  • alan robinson · Edited

    you know, I can see both sides of this. Really the solution that would be kindest is some kind of global string table for common prompts that would potentially be improved or changed over time but still would have the same ID number so that your static reference could be unchanging for all time.

    Since that doesn’t exist of course people go looking for an equivalent like, you have. Without endorsing your solution I’m curious how often you’ve actually had to update your program because of breaking issues relating to this? It sounds like not at all.

    • Jan Ringoš

      Ah, I didn't notice this was directed at me.
      How often did I actually had to update any of my programs because Microsoft made a breaking change? Zero times!

      Or rather: Almost once...

      In one of my very old projects (embedded SW running on XP/POSReady09) I used a few strings from CMD.EXE in an administrative tool. This device run for 15 years without anyone ever updating the OS or touching anything, and then was scrapped. But once the client considered upgrading it to 8.1 (Industrial Embedded), and in early test I noticed some strings were not showing in the tool. They abandoned...

      Read more
      • Jan Ringoš · Edited

        Raymond, regarding me externalizing the costs, I perfectly well understand your sentiment here. But you are arguing something that hasn't been the case for maybe 15 years now.

        For one) Over my recent career I've never even heard of anyone successfully filling an appcompat bug with Microsoft, certainly not against highly custom software from a small company like ours. I don't even think it would occur to our customers to do that, instead of just reporting it to us. The joke about their printer, I made below, isn't exactly a joke.

        But I hear all over X and Reddit from people reporting...

        Read more
      • Raymond ChenMicrosoft employee Author · Edited

        : How often did I actually had to update any of my programs because Microsoft made a breaking change? Zero times

        Has it occurred to you that perhaps the reason is that the Windows team tried to change the string and got an app compat bug filed against them? You’re externalizing the cost of translation.

        If you want the word “bytes” you can use SHFormatByteSize.

    • Raymond ChenMicrosoft employee Author

      “It sounds like not at all.” I literally gave an example in the article.

      • Raymond ChenMicrosoft employee Author · Edited

        : Of the risks, yes. But not an actual example of a place where it broke.

        @alan robinson: The scenario I described in the article actually happened. It is not theoretical. (Went back and double-checked. Yup the article says “actually happened”.)

      • alan robinson

        Of the risks, yes. But not an actual example of a place where it broke? However, to be clear this comment was directed at Jan Ringoš, and Microsoft’s not very good blog system erroneously attached it to the top level of the thread, rather than the actual subpost. Or maybe I clicked “wrong”.

  • Frédéric B. · Edited

    >they couldn’t change the existing incorrect string because that would invalidate Language Interface Packs.
    Wait, I thought your article said that was the original string that was stuck this way, not the translated one. Try as I might, I can’t find a logical reason for the translated string to be stuck…

    Is this why the bad French translation for FILE_READ_ATTRIBUTES was never fixed?

    • Raymond ChenMicrosoft employee Author

      The original string stays with the bad translation. What the team can do is add a new string, translate the new string correctly, and switch over to the using new string.

      • Frédéric B. · Edited

        I'm understanding less and less.
        In the original article, you seemed to explain that that the "before translation" string could not be changed because language packs check for it. That much makes sense. It's easy to understand.

        What I don't understand is why (as you are now saying) the "after translation" string can't be changed either. Your article does not seem to explain that.
        That also means if one translation team fails a translation, fixing it involves mobilizing both the original dev team (to add the new string) and ALL translation teams, which sounds incredibly inefficient.

        Edit: Or did you mean some...

        Read more
  • Neil Rashbrook

    I’m half surprised they didn’t try parsing the output prompt from the console program…

  • Jan Ringoš

    I’m using number of strings from COMDLG32.DLL stringtables and if you change their ID (which remained the same since XP), I’ll just update my program.

    • Tom Lint

      But… why?

      • Jan Ringoš

        Those strings are already translated to the user's locale.

        For example I have a tiny tool that shows file name, icon and size. If it's smaller than 1 kB, then I just grab string 4113 from SHELL32 to display "bytes" instead of appending "B" which could end up being mistaken for a hexadecimal number.

        In another app I'm using IFileOpenDialog instead of GetOpenFileName. Sadly the former lacks OFN_HIDEREADONLY (or rather an option to not have it) so I just grab string 427 ("Open as read-only") from COMDLG32 and add the button myself. The dialog is already translated to the user's locale, so...

        Read more
    • Raymond ChenMicrosoft employee Author

      Do your customers understand that “Jan’s program may stop working at any update, please contact Jan to get a new version”? Can we send the app compat bugs to you?

      (And if the customer has a mix of systems on different update schedules, I guess you need to make the program detect which OS version it is running on, so it can get the string from the right place.)

      • raykoopa

        @Jan: Printers not working due to some log viewer program? I really hope everyone makes that connection. And looks for alternative software that doesn’t meddle with things it shouldn’t.

      • Jan Ringoš

        Funnily enough yeah, they all pretty much do.
        A printer stops working because someone updated from W10 to W11 and I get an e-mail asking if my 20 y/o LogViewer program could be the cause, because they have it installed.

        And yes, please, plug me into any flow of crash reports referencing binaries built by me or my company. A few years back I was looking for a way I could join that myself, but every way I found required buying expensive code signing certificate(s) to even start.

        Nevertheless, I test in (VMs of) all version and SKUs, starting with NT4, ending...

        Read more
  • Joshua Hudson

    I tried to think of how this could come to be that they would be invoking a command line program.

    I only came up with three good answers: all of which you _really_ don't want them looking for other solutions.

    1) dism.exe
    2) chkdsk.exe
    3) format.com (or is it format.exe now?)

    format.com was the worst in this regard; as it does have an option to suppress the prompt, this results in a lot worse runtime. /Q and /U were not miscable in all cases.

    I've used a similar technique with other programs; but I found it expedient to ship the binary myself to (among...

    Read more
    • alan robinson

      I think any extension is legal now but in fact there is no such thing as a COM file now that we live in PE Land. I am however not bothering to test this hypothesis.

  • 許恩嘉

    Perhaps the customer could temporarily change the code page before run the console program?
    chcp 65001

    • GL

      Console codepage should have nothing to do with translated UI strings, it only affects how the console interprets bytes communicated via non-Unicode API. I expect most programs coming with Windows talk to console in UTF-16 LE.

      • 許恩嘉

        It turns out I was wrong.

        My display language was Traditional Chinese (codepage 950), and when I called chcp 65001, the output changed to English.

        I tried setting the display language to German, and this time, even with chcp 20127 (US-ASCII (7-bit)), the output remained in German.