7

I'm sure most Windows developers are familiar with this error message, usually when trying to mix 32- and 64-bit executables. In particular Python and Java can both get it.

%1 is not a valid Win32 application.

It's clear that %1 represents the first argument to the failing command - i.e. the executable that is trying to be loaded - but why does it not get filled in with the actual path?

Is it something that the caller is doing wrong, or is it a basic failing of some Windows subsystem that cannot be fixed for compatibility reasons?

Community
  • 1
  • 1
OrangeDog
  • 36,653
  • 12
  • 122
  • 207
  • 1
    Do you have a specific situation you can point us to that generates the error? I can't say that I've ever seen it. – Mark Ransom Apr 25 '17 at 15:40
  • Take a look at the "Related" list or just search SO for it -> – OrangeDog Apr 25 '17 at 15:41
  • @MarkRansom the two I've been recently annoyed by involve loading a native Python module that was built with the wrong architecture, and an Apache project installing itself with 32-bit commons-daemon on a 64-bit JVM. – OrangeDog Apr 25 '17 at 15:50

2 Answers2

10

The error message comes from Windows itself, you can see the complete list at System Error Codes (0-499). You translate an error code returned by the API into a message using FormatMessage, which has an optional Arguments array; any %1 in the message will be replaced by the first element in this array. If nothing is passed for the arguments, the %1 will be left unchanged if the FORMAT_MESSAGE_IGNORE_INSERTS flag was used or the FormatMessage will fail if it wasn't (thanks to IInspectable for that information).

As an example of how this might get missed, consider code where an error code gets converted immediately to an exception. If the exception contains the error code but nothing else, then there is no context for knowing what to pass to FormatMessage.

Community
  • 1
  • 1
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 2
    So it's basically thousands of Windows developers not formatting error messages correctly? – OrangeDog Apr 25 '17 at 15:51
  • 4
    Just about to post this answer, and agree with your previous comment that, despite having done lots of Windows development, I've never seen this message. You have to do something wrong in order to see it. It is not a failing of a Windows subsystem. – Cody Gray - on strike Apr 25 '17 at 15:52
  • @OrangeDog exactly. You're expected to know from the context of the system call that failed what argument you need to pass, but since it's optional it will either get missed or will be called at a place where the context has been lost. – Mark Ransom Apr 25 '17 at 15:53
  • @CodyGray the question is not why do you get the error, but why it rarely tells you what the path was – OrangeDog Apr 25 '17 at 15:54
  • 2
    Yeah, I believe I understood the question. You asked, *"Is it something that the caller is doing wrong, or is it a basic failing of some Windows subsystem that cannot be fixed for compatibility reasons?"*, and I am saying that it is the former, rather than the latter. @orange – Cody Gray - on strike Apr 25 '17 at 15:57
  • @CodyGray in that case I misunderstood what you meant by "this message". – OrangeDog Apr 25 '17 at 16:02
  • Two up-votes for a proposed answer, that doesn't even address the question asked. One down-vote for one that does. This place is in a pretty ill state. @CodyGray: How would the caller know, whether it should pass a file name, or a kernel handle? A string? Or a `DWORD`? They can't. And that's why you see placeholders pop up. The caller is doing everything right. Anyone who has ever used this API does know about this. I'm puzzled, that **you** don't. – IInspectable Apr 25 '17 at 16:04
6

The caller is doing everything right. They are calling FormatMessage, passing along the FORMAT_MESSAGE_IGNORE_INSERTS flag1), like everyone should. The caller is not in control of the message that gets created, and has no way of knowing, that it should pass additional arguments, what types they should be or how many.

This was an early design bug in the Windows error reporting system, and you'll see those placeholders in every well-behaved application.


1) See The importance of the FORMAT_MESSAGE_IGNORE_INSERTS flag.
Eric
  • 95,302
  • 53
  • 242
  • 374
IInspectable
  • 46,945
  • 8
  • 85
  • 181
  • 1
    The `FORMAT_MESSAGE_IGNORE_INSERTS` flag is documented as: "Insert sequences in the message definition are to be ignored and passed through to the output buffer unchanged. This flag is useful for fetching a message for later formatting." That doesn't seem to agree with your answer. So either they've documented what you're calling an "early design bug", or the flag is merely for *deferring* the formatting. – Cody Gray - on strike Apr 25 '17 at 15:54
  • @CodyGray: That flag is for deferring formatting to never. There is no infrastructure that allows code to know, what arguments to pass, in what order, or what types. The placeholders were meant to be used by `FormatMessage`, but it turns out, that it doesn't work (again, how do you know that you should pass a fully qualified path name vs. a window handle?). – IInspectable Apr 25 '17 at 15:56
  • 1
    @IInspectable presumably you're supposed to pass the same arguments as you did to the function that just failed. – OrangeDog Apr 25 '17 at 16:05
  • @OrangeDog: This specific error code is presumably one of those that [CreateProcess](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425.aspx) can return. This API can be called with `nullptr` as its first argument (and frequently is). Yes, that was a nice plan, back in the day. It simply didn't work out. – IInspectable Apr 25 '17 at 16:08
  • Then it sounds like that's the real reason. Even if you used the API correctly, the message template doesn't account for the `lpCommandLine`. – OrangeDog Apr 25 '17 at 16:19
  • 4
    I think the expectation is that caller is doing a specific `case` for each *expected* error and add the required insert parameters. For the `default` case pass the `FORMAT_MESSAGE_IGNORE_INSERTS`, of course. – Remus Rusanu Apr 25 '17 at 16:37
  • @RemusRusanu: That's the sort of kludge you'd have to do to get anywhere. Nothing you can (easily) do in a generic way, which leaves error reporting code sprinkled throughout your code. – IInspectable Apr 25 '17 at 19:14
  • 1
    You *only* need to check the error code and provide the argument if you've just performed an API call that might fail in this particular way. That's not exactly unusual, I guess, e.g., you should take this into account if you're loading a user-provided plugin, or if you're an interpreter loading a DLL on behalf of the code you're running, but it's not like you need to do this every single time you process a Win32 error. – Harry Johnston Apr 26 '17 at 03:59
  • 2
    @HarryJohnston: The messages returned from `FormatMessage` are outside your control. You cannot query the system for count, type, and semantics of placeholder variables. If you're suggesting to filter out **one** single error code, at one place in your code, just to wind up with the error message *"foo.dll is not a valid Win32 application."* then you might as well roll your own, and produce a **really** helpful error message (the one you've carefully crafted certainly isn't). Nothing contradicts the statement, that the API's design is broken, or justifies a down vote. – IInspectable Apr 26 '17 at 08:59
  • Certainly you can present your own message, you're not obliged to use FormatMessage and nowadays there's probably not much point in doing so. That doesn't mean the API design is broken, just obsolete, and only in this one particular case. Nor does it mean that a well-behaved application ought to present an error message containing a placeholder, not if the possibility of that particular error occurring should have been obvious. – Harry Johnston Apr 26 '17 at 09:57
  • @HarryJohnston: `FormatMessage` has **always** been broken, due to not providing a programmatic interface to query for its arguments. It offers services you have no way of using. I call that *"broken design"*. If you wish to call it something else, make sure you justify that decision. Besides, there are **numerous** standard error messages with placeholders (e.g. `ERROR_WRONG_DISK`, `ERROR_INVALID_ORDINAL`, `ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY`, etc.). It's not just broken *"in this one particular case"*. – IInspectable Apr 26 '17 at 10:22
  • @HarryJohnston: You could argue this back and forth: The API **was** broken. There's nothing built into the [message compiler](https://msdn.microsoft.com/en-us/library/windows/desktop/aa385638.aspx), that allows you to embed meta data useful in formatting. In fact, there isn't even a check, that placeholders are consistent across different localizations. The initial idea, to pass the same arguments to `FormatMessage` as in the call that failed never scaled. A solution that doesn't scale also goes by the name *"broken design"*. You want to disagree? Do make it convincing. You haven't so far. – IInspectable Apr 26 '17 at 10:59
  • @HarryJohnston: I just explained, why it wasn't practicable 20 years ago either. It **never** worked sufficiently well. It was always lacking meta data to make it generally useful, or a scalable and enforceable contract on parameter ordering. It was a best effort, and that was never anywhere near *good*, or even *good enough*. I recall seeing placeholders in **system** error dialogs in 16-bit Windows. That's telling. – IInspectable Apr 26 '17 at 11:19
  • ... yeah, I've just realized that I'm arguing about FormatMessage itself, whereas you were talking about the error handling mechanism as a whole. My mistake. Personally, I still think the error handling mechanism was as good as could be reasonably expected at the time; the sort of solution you're talking about wouldn't IMO have been practical. That's a matter of opinion, so feel free to differ. – Harry Johnston Apr 26 '17 at 11:38
  • @HarryJohnston: Like with `printf`, you wouldn't pass a format string outside your control to `FormatMessage`. Now the difference is, that with `FormatMessage`, passing a format string you do no controls is basically the default mode of operation. That's what makes `FormatMessage` broken. I'm not suggesting, that adding meta data would have been the proper solution. I'm arguing, that not offering placeholders would have. – IInspectable Apr 26 '17 at 17:19
  • OK, I think that's the basic source of our disagreement as far as `FormatMessage` itself goes. I believe that when `FormatMessage` was written, the primary purpose was to, well, format messages, not to translate unknown error codes into messages. Not supporting placeholders wouldn't have been an option, since IMO they were why the function existed in the first place. (The ability to reference the system message table instead of providing a format string would have been an obvious feature to add, in order to save executable space.) – Harry Johnston Apr 26 '17 at 21:31
  • ... however, it was probably predictable that programmers would also use it to translate unknown error codes into text, so perhaps they should have provided an alternate version of those system message texts containing placeholders. That would have been easy enough, I think. – Harry Johnston Apr 26 '17 at 21:31