Why is there no supported way to get the command line of another process?


Commenter Francisco Moraes wonders whether there is a supported way of getting the command line of another process. Although there are certainly unsupported ways of doing it or ways that work with the assistance of a debugger, there's nothing that is supported for programmatic access to another process's command line, at least nothing provided by the kernel. (The WMI folks have come up with Win32_Process.CommandLine. I have no idea how they get that. You'll have to ask them yourself.)

That there isn't is a consequence of the principle of not keeping track of information which you don't need. The kernel has no need to obtain the command line of another process. It takes the command line passed to the CreateProcess function and copies it into the address space of the process being launched, in a location where the GetCommandLine function can retrieve it. Once the process can access its own command line, the kernel's responsibilities are done.

Since the command line is copied into the process's address space, the process might even write to the memory that holds the command line and modify it. If that happens, then the original command line is lost forever; the only known copy got overwritten.

Comments (28)
  1. John says:

    Doesn’t this fall into the category of things you can "recalculate"?  I mean, why not something like:

    LPTSTR GetProcessCommandLine(DWORD dwProcessId)

    {

      // open the process and read the command line from the peb; copy with LocalAlloc(), free with LocalFree() (or whatever)

    }

    I suspect this is how everyone does it.  Granted it doesn’t solve the "lost forever" problem, but given the design / architecture there is only so much you can do.

  2. someone else says:

    Process Explorer doesn’t seem to have a problem with that either …

  3. Joseph Koss says:

    When there are known cases where programs are in fact doing this, its probably time to either go ahead and define a method in which to do this or declare that they cannot (and then enforce that.)

    Seeing that this is Raymonds blog, obviously compatability is king here, so denying this behavior is (at least for now) unacceptable.

    So make it an API, make peoples lives easier in the process, and eventualy the direct peeking method will become far less of a future compatability (…yet-another-shim…) concern.

  4. arousedboat says:

    "Process Explorer doesn’t seem to have a problem with that either …"

    Congratulations – you’ve discovered that Process Explorer doesn’t always use supported methods of getting the information it shows you. Not exactly surprising…

  5. asf says:

    …but there is nothing stopping you from looking at the WMI source and helping the world…

    [Um, first I have to gain access to the WMI source code. No wait, first I have to find the WMI source code. No wait, first I have to actually care. -Raymond]
  6. John says:

    Um, I’m pretty sure WMI uses the same method to read the command line from the PEB.  In fact, I just verified it with a test program that modifies its own command line.  Using the same test, it is evident that Process Explorer also uses the same method.  Everybody is doing it this way, so why not provide an API that does it?

    [Um, the API is WMI. -Raymond]
  7. quotemstr says:

    Incidentally, the command-line is available in unixland. One neat trick is to deliberately overwrite it with status information — that way, when ps displays a process’ command line (reading it out of the process’ memory), the listing will include information about what the process is actually doing.

  8. John says:

    I meant a plain old Win32 function; I thought that was made evident by my previous comment.  Who wants to deal with all the COM setup, teardown, and other junk required for using WMI in C?

    [I’m sure somebody can write a wrapper function for you. I doubt the kernel will ever expose such a function since people would expect it to be accurate, which it isn’t. (See last paragraph of article.) -Raymond]
  9. Tsukrov says:

    Hi!

    The first idea, that I got, was to launch a remote thread in the target process and call this GetCommandLine();

    Then I need to read it back from my process.

  10. John Muller says:

    So how about the "Command Line" column in Task Manager built into Vista?

    If the kernel dosn’t track it, perhaps Win32/Shell does… something does.

  11. Technage says:

    Blech remoting gets so messy, of course there are some things it does well.

  12. porter says:

    > Incidentally, the command-line is available in unixland. One neat trick is to deliberately overwrite it with status information…

    Hence it is actually not available, eg if you change it then the original command line is not available, all you are refering to is the current values of the argv array.

    Also the behaviour you are observing is not portable amongst all UNIX derivations.

  13. Xepol says:

    You can recall the function and get the same info again, there for the OS DOES track this information.  The fact that an APP will copy the info into user space to modify it has no bearing on that.

    Clearly, the information is being not just remembered, but tracked in relationship to the process ID (so that the process can get ITS command line, not just the last one used)

    So all of your arguments fall apart here.

    One could describe this as a security feature except that it is so easily cirumvented and is not supported by the poor design of the interprocess isolation model in windows.

    The honest answer here folks is “no one thought to write a function for it”

    [The only “tracking” the OS does is remembering where it copied the string so it can be returned when you call GetCommandLine. It’s not tracked in relationship to the ID; it’s “tracked” in the process’s address space itself. Once the process address space disappears, so too does the command line. Note that there is no synchronization on this buffer – if an application happens to be modifying the buffer while you’re reading from it, the results are indeterminate (you might even crash because the application stomped the null terminator, resulting in your code that tries to copy the string running off the end). -Raymond]
  14. brone says:

    GetCommandLine returns a non-const pointer. Presumably this points into some writeable process-specific storage, initialized by the program loader, that the app can munge any way it sees fit. No need for the app to take a copy, and no reason for the loader to store another copy.

    So perhaps, more precisely, you could say that one can retrieve a pointer to the buffer that the process’s command line was originally stored in — but there’s no guarantee that this buffer will actually contain anything useful.

  15. Brian says:

    Xepol: Huh? I assume by "the function" you mean "GetCommandLine()" in which case, it’s simply returning a pointer to some bit of memory in the processes address space.  The OS copies the command line to that spot on process launch, then it throws it away.

    If you call GetCommandLine(), modify the result, then call GetCommandLine() again, it will happily return you the modified version.  Here’s a quick program that demonstrates this:

    int WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow ) {

    MessageBox( NULL, lpCmdLine, "lpCmdLine (before)", MB_OK );
    
    MessageBox( NULL, GetCommandLine(), "GetCommandLine() (before)", MB_OK );
    
    strcpy( lpCmdLine, "hello" );
    
    MessageBox( NULL, lpCmdLine, "lpCmdLine (after)", MB_OK );
    
    MessageBox( NULL, GetCommandLine(), "GetCommandLine() (after)", MB_OK );
    
    return (int)0;
    

    }

  16. Anonymous Coward says:

    Thanks for this insight. I have been toying around recently with a bit of a puzzle, but had trouble getting it to work reliably. I wanted Notepad to open a file that wasn’t actually a real file, and I had to pass through Notepad which not so real file I wanted it to read. (That was the tricky bit.) Now I simply pass it on the commandline, do my little tricks, modify the commandline to resemble a ‘tradional’ Notepad commandline and presto – problem solved.

    This is why I keep reading The Old New Thing. There are so many things that really should be obvious, in retrospect, but that I just didn’t know. Every now and then a post makes me go ‘Aha!’ and that’s why I love it.

  17. Stewrat says:

    This seems like a question that gets a lot of attention. Is it just me that doesn’t understand why? Is it a really common thing for unix apps to perform some rudimentary IPC using their command lines or something. I’ve never yet had a need to query a running processes command line. I can see why Process Explorer might want to do it, because it is for debugging, but process explorer already exists, so I assume people aren’t rewriting that.

  18. quotemstr says:

    "I’ve never yet had a need to query a running processes command line."

    It actually comes in handy quite often. Say you want to restart a running process; you can use ps(1) to look at its command line and ensure you re-launch it with the same arguments. Not only is the command-line available, by the way, but the environment too: image you need to figure out whether a given process was given FOO_DEBUG=1 in its environment; you can use ps for that too.

    Also, I’ve never seen a process use command-line modification for IPC. It’s more a courtesy to the administrator than a robust communication mechanism.

  19. porter says:

    > Now I simply pass it on the commandline, do my little tricks, modify the commandline to resemble a ‘tradional’ Notepad commandline and presto – problem solved.

    Was that really easier than creating a window with an edit control? I wonder what the problem really was.

  20. Stewart says:

    "It actually comes in handy quite often. Say you want to restart a running process; you can use ps(1) to look at its command line and ensure you re-launch it with the same arguments. Not only is the command-line available, by the way, but the environment too: image you need to figure out whether a given process was given FOO_DEBUG=1 in its environment; you can use ps for that too."

    So the people that need to do this are developers then? People who work in the command prompt a lot. I have a build job running right now that I wish I could see the environment for, because I can’t remember if I set it up right, but is it really the OS’s job to do that. Fundamentally windows is a GUI OS and most users don’t know or care what a command line is. I say let the OS worry about tracking the stuff that they care about, and let me worry about my mistakes.

    Also, what do you do if the process changed its command line, which I understand is possible.

  21. Anonymous says:

    Stewart:

    I believe in Linux at least, the kernel keeps a separate buffer for the command line.  If a process changes its copy, the one that was passed into execve() can still be retrieved using the /proc filesystem.

    There’s another interesting point about this, though.  In Linux, at least the last time I tried this, I found a process can lie about its name.  If you call execve() with argv[0] as something bogus, but the first argument of execve() points to a legitimate executable path, ps will display the "fake" value from argv[0] instead of the real executable name.

    By the way, I’ve never heard of being able to get a program’s environment with ps.  Though Linux /proc might expose that.

  22. porter says:

    > By the way, I’ve never heard of being able to get a program’s environment with ps.

    Have you never heard of "man ps"?

  23. AC says:

    > Have you never heard of "man ps"?

    Helpful suggestion. I typed that into my command prompt and it printed out the following though

    ‘man’ is not recognized as an internal or external command,

    operable program or batch file.

    Which version of windows did this goodness ship with?

  24. What’s the deal with the five stars next to the comment link?

  25. porter says:

    > Which version of windows did this goodness ship with?

    SFU 3.5, for example. :)

  26. Bur says:

    > What’s the deal with the five stars next to the comment link?

    Hover your mouse over the image and you’ll get enlightened.

  27. @Bur – nothing pops up.  ::shrug::

  28. me says:

    Seeing that Windows is, was or has once pretended to be, POSIX compatible, it’s not quite surprising that the information that is needed to support the required features, such as command line parameter handling, is in fact available.

Comments are closed.