-1

I figured out how to open a process using CreateProcessAsUserA() from this:

example code: A service calls CreateProcessAsUser() I want the process to run in the user's session, not session 0

Now, I need to add process arguments to run the program correctly, I mean arguments like -steam.

I can't find any solution to do it on Google. Please help me.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Hitchance
  • 13
  • 4
  • Is this is a C question or a C++ question. Don't tag with languages you aren't using. Removed [tag:c]. Replace [tag:c++] with [tag:c] if it's more appropriate. – ikegami Aug 13 '20 at 20:32
  • 1
    @ikegami: The question is about an API. As long as the language has bindings for it, the specific one is irrelevant. Nothing about the question or its answers hinges on whether the caller is using C or C++. – Adrian McCarthy Aug 13 '20 at 20:37

2 Answers2

2

The API is defined as so:

BOOL CreateProcessAsUserA(
  HANDLE                hToken,
  LPCSTR                lpApplicationName,
  LPSTR                 lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCSTR                lpCurrentDirectory,
  LPSTARTUPINFOA        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

You can just tack the command line on to the end of the target EXE.

So, using the example from above, it would look like this:

rc = CreateProcessAsUserA(hUserToken,               // user token
                       0,                           // app name
                       (LPSTR)"c:\\foo.exe -steam", // command line
                       0,                           // process attributes
                       0,                           // thread attributes
                       FALSE,                       // don't inherit handles
                       DETACHED_PROCESS,            // flags
                       0,                           // environment block
                       0,                           // current dir
                       &si,                         // startup info
                       &pi);

The "A" version of this method doesn't need to be non-const for lpCommandLine, the "W" version, on the other hand, does.

If the path to the executable has spaces in it, you will want to surround it in quotes:

(LPSTR)"\"c:\\my files\\foo.exe\" -steam"

ETA:

There was some confusion about how the commandline would be generated for the target program. To keep things C-style (argv[0] being the path to the target executable), you should not use the lpApplication parameter, and if you do, you would still want the lpCommandLine to look as it does above.

Info from the docs:

If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module to execute, and *lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.

Andy
  • 12,859
  • 5
  • 41
  • 56
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/219782/discussion-on-answer-by-andy-how-to-add-process-arguments-using-createprocessasu). – Samuel Liew Aug 14 '20 at 02:05
0

If you find it tricky to get the quotes right, you could create a function to do it.

C++17:

#include <initializer_list>
#include <string_view>
#include <utility>

// A function to prepare a commandline for execution by quoting
auto prep_cmd(std::initializer_list<std::string_view> args) {
    if(args.size()==0) throw std::runtime_error("No command, no fun");

    auto it = args.begin();

    std::string AppName(*it);                 // AppName is returned unchanged
    std::string CmdLine('"' + AppName + '"'); // but quoted when used in the commandline

    for(++it; it != args.end(); ++it) {
        CmdLine += ' ' + std::string(*it);            // add argument unquoted
        // CmdLine += " \"" + std::string(*it) + '"'; // or quote the argument too
    }
    return std::pair{AppName, CmdLine};               // return the result
}

Then call it:

auto[AppName, CmdLine] = prep_cmd({
    R"aw(C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\csgo.exe)aw",
    "-steam"
});

Use the result:

    CreateProcessAsUserA(
        hToken,
        AppName.c_str(),   // const char*
        CmdLine.data(),    // char*
        ...
    );
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • @Hirchance That's odd. Did you print out the two strings to check for errors? – Ted Lyngmo Aug 15 '20 at 17:00
  • @Hitchance That's `ERROR_INVALID_HANDLE`. You need to check that you get a valid handle from `LogonUser` or `DuplicateTokenEx` before calling `CreateProcessAsUserA`. Did the strings returned by `prep_cmd` look correct? Can you paste them in as a comment here? – Ted Lyngmo Aug 16 '20 at 15:23