I'm using Visual Studio 2017 to debug C++ code on a VM running Windows via VS Remote Debugging. As part of my code, I'm executing commands via std::system
, which must run as administrator to be successful.
When running VS as administrator, and debugging the code locally -- all works fine.
But, when debugging remotely on a VM, the commands aren't executed as administrator. I know that for a fact, because some of the commands require that, and the output explicitly states that it's not the case. Is there a way to make it work?
I don't mind using a different API, std::system
is just seems as the "default" for command execution.
Thanks to Barrnet's answer, I've looked into ShellExecuteEx
and its "runas" verb.
Here's a full solution, for running a cmd as admin and retrieving the cmd output as a string: (UAC has to be set to minimum, in order to avoid a pop-up)
#include <Windows.h>
#include <vector>
#include <string>
#include <fstream>
inline void Validate (const bool expression)
{
if (!expression)
throw std::exception();
}
class TemporaryFile
{
public:
TemporaryFile()
{
std::vector<char> tmpPath(MAX_PATH + 1, 0);
Validate(0 != GetTempPathA(MAX_PATH, tmpPath.data()));
std::vector<char> tmpFilename(MAX_PATH + 1, 0);
Validate(0 != GetTempFileNameA(tmpPath.data(), "tmp", 0, tmpFilename.data()));
_fullPath = std::string(tmpFilename.data());
}
~TemporaryFile()
{
DeleteFileA(_fullPath.c_str());
}
std::string GetPath()
{
return _fullPath;
}
private:
std::string _fullPath{};
};
std::string ReadFileContents(const std::string& filename)
{
std::ifstream ifs(filename);
return std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
}
std::string RunCmdAsAdmin(const std::string& cmd)
{
// file to hold cmd output
TemporaryFile output;
// the cmd will be passed to cmd.exe via '/c' flag, output will be written to tempfile
const std::string modifiedCmd = "/c " + cmd + " > \"" + output.GetPath() + "\"";
// create and launch cmd (async operation -- a seperate process is launched)
SHELLEXECUTEINFO shellExecInfo{};
shellExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
shellExecInfo.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS;
shellExecInfo.lpVerb = "runas";
shellExecInfo.lpFile = "cmd.exe";
shellExecInfo.lpParameters = modifiedCmd.c_str();
shellExecInfo.nShow = SW_SHOWNORMAL;
Validate(TRUE == ShellExecuteEx(&shellExecInfo));
// wait for cmd to finish running and retrieve output
static const DWORD MAX_WAIT_MS{ 5000 };
Validate(WAIT_OBJECT_0 == WaitForSingleObject(shellExecInfo.hProcess, MAX_WAIT_MS));
CloseHandle(shellExecInfo.hProcess);
return ReadFileContents(output.GetPath());
}
int main()
{
const auto& cmdOutput = RunCmdAsAdmin("ipconfig");
}