RSC  0.17.1
Win32ProcessInfo.cpp
Go to the documentation of this file.
1 /* ============================================================
2  *
3  * This file is part of the RSC project
4  *
5  * Copyright (C) 2014 Jan Moringen
6  *
7  * This file may be licensed under the terms of the
8  * GNU Lesser General Public License Version 3 (the ``LGPL''),
9  * or (at your option) any later version.
10  *
11  * Software distributed under the License is distributed
12  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
13  * express or implied. See the LGPL for the specific language
14  * governing rights and limitations.
15  *
16  * You should have received a copy of the LGPL along with this
17  * program. If not, go to http://www.gnu.org/licenses/lgpl.html
18  * or write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  * The development of this software was supported by:
22  * CoR-Lab, Research Institute for Cognition and Robotics
23  * Bielefeld University
24  *
25  * ============================================================ */
26 
27 #include "ProcessInfo.h"
28 
29 #include <windows.h>
30 #include <Lmcons.h> // for UNLEN
31 #include <Psapi.h>
32 
33 #include <stdexcept>
34 
35 #include <boost/algorithm/string.hpp>
36 #include <boost/shared_array.hpp>
37 #include <boost/format.hpp>
38 #include <boost/date_time/posix_time/posix_time.hpp>
39 #include <boost/date_time/filetime_functions.hpp>
40 
41 #include "Win32Common.h"
42 
43 namespace rsc {
44 namespace os {
45 
47  return GetCurrentProcessId(); // Cannot fail according to MSDN
48 }
49 
50 std::string getProgramName(PID pid) {
51  return getExecutablePath(pid);
52 }
53 
54 std::string currentProgramName() {
55  return currentExecutablePath();
56 }
57 
58 std::string getExecutablePath(PID pid) {
59  // will usually only work with elevated privileges
60 
61  HANDLE processHandle = OpenProcess(
62  PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
63  if (processHandle == NULL) {
64  throw std::runtime_error(boost::str(boost::format("Could not"
65  " open process with PID"
66  " %1%: %2%") % pid % GetLastErrorString()));
67  }
68 
69  TCHAR filename[MAX_PATH];
70  DWORD ret = GetModuleFileNameEx(processHandle, NULL, filename, MAX_PATH);
71  CloseHandle(processHandle);
72  if (ret == 0) {
73  throw std::runtime_error(boost::str(boost::format("Could not"
74  " get executable"
75  " path: %1%") % GetLastErrorString()));
76  }
77 
78  return std::string(filename);
79 }
80 
81 std::string currentExecutablePath() {
82  char buffer[MAX_PATH];
83  int length;
84  if ((length = GetModuleFileName(NULL, buffer, MAX_PATH)) == 0) {
85  throw std::runtime_error(boost::str(boost::format("Could not"
86  " determine program"
87  " name: %1%") % GetLastErrorString()));
88  }
89  return std::string(buffer, length);
90 }
91 
92 typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
93  HANDLE ProcessHandle,
94  DWORD ProcessInformationClass,
95  PVOID ProcessInformation,
96  DWORD ProcessInformationLength,
97  PDWORD ReturnLength
98  );
99 
100 typedef struct _UNICODE_STRING {
101  USHORT Length;
103  PWSTR Buffer;
105 
109  ULONG_PTR AffinityMask;
111  ULONG_PTR UniqueProcessId;
112  ULONG_PTR ParentProcessId;
114 
115 PVOID GetPebAddress(boost::shared_ptr<void> processHandle) {
116  _NtQueryInformationProcess NtQueryInformationProcess =
117  (_NtQueryInformationProcess) GetProcAddress(
118  GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
120  NtQueryInformationProcess(processHandle.get(), 0, &pbi, sizeof(pbi), NULL);
121 
122  return pbi.PebBaseAddress;
123 }
124 
126 public:
127 
128  void operator()(void* handle) {
129  if (handle != 0) {
130  CloseHandle((HANDLE) handle);
131  }
132  }
133 
134 };
135 
136 std::vector<std::string> getCommandlineArguments(PID pid) {
137  // Will only work with elevated privileges for other processes
138  // than the current.
139  //
140  // Adapted from:
141  // http://wj32.org/wp/2009/01/24/howto-get-the-command-line-of-processes/
142  //
143  // Also note that this is not reliable:
144  // http://blogs.msdn.com/b/oldnewthing/archive/2009/11/25/9928372.aspx
145 
146  HANDLE rawProcessHandle = OpenProcess(
147  PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
148  if (rawProcessHandle == 0) {
149  throw std::runtime_error(boost::str(boost::format("Could not"
150  " open process with PID"
151  " %1%: %2%") % pid % GetLastErrorString()));
152  }
153  // Maintain the process handle in a shared_ptr so that it can be
154  // correctly closed even in case of exceptions.
155  boost::shared_ptr<void> processHandle(rawProcessHandle, DeleteHandle());
156 
157  PVOID pebAddress = GetPebAddress(processHandle);
158 
159  // get the address of ProcessParameters
160  PVOID rtlUserProcParamsAddress = 0;
161  if (!ReadProcessMemory(processHandle.get(), (PCHAR) pebAddress + 0x10,
162  &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) {
163  throw std::runtime_error(
164  boost::str(
165  boost::format(
166  "Could not read the address of ProcessParameters: %1%")
167  % GetLastErrorString()));
168  }
169 
170  // read the CommandLine UNICODE_STRING structure
171  UNICODE_STRING commandLine;
172  if (!ReadProcessMemory(processHandle.get(),
173  (PCHAR) rtlUserProcParamsAddress + 0x40, &commandLine,
174  sizeof(commandLine), NULL)) {
175  throw std::runtime_error(
176  boost::str(
177  boost::format("Could not read CommandLine: %1%")
178  % GetLastErrorString()));
179  }
180 
181  // allocate memory to hold the command line
182  boost::shared_array<WCHAR> commandLineContents(
183  new WCHAR[commandLine.Length]);
184 
185  // read the command line
186  if (!ReadProcessMemory(processHandle.get(), commandLine.Buffer,
187  commandLineContents.get(), commandLine.Length, NULL)) {
188  throw std::runtime_error(
189  boost::str(
190  boost::format(
191  "Could not read the command line string: %1%")
192  % GetLastErrorString()));
193  }
194 
195  std::wstring wideCommandLineString = commandLineContents.get();
196  // The conversion from wstring to string relies on truncating the
197  // multibytes while assuming that for common characters, the first
198  // byte matches ASCII: http://stackoverflow.com/a/12097772/283649
199  //
200  // Moreover, it seems there is no proper null termination here, so
201  // calculate the end manually.
202  std::string commandLineString(wideCommandLineString.begin(),
203  wideCommandLineString.begin() + commandLine.Length / 2);
204 
205  // tokenize the command line
206  std::vector<std::string> arguments;
207  boost::split(arguments, commandLineString, boost::is_any_of(" "),
208  boost::token_compress_on);
209 
210  return arguments;
211 }
212 
213 // Based on MSDN example for CommandLineToArgv
214 std::vector<std::string> currentCommandlineArguments() {
215  LPWSTR* arguments;
216  int numArguments;
217  if ((arguments = CommandLineToArgvW(GetCommandLineW(), &numArguments))
218  == NULL) {
219  throw std::runtime_error(boost::str(boost::format("Could not determine"
220  " commandline"
221  " arguments: %1%")
222  % GetLastErrorString()));
223  }
224 
225  std::vector<std::string> result;
226  for (int i = 0; i < numArguments; ++i) {
227  std::wstring arg = arguments[i];
228  // The conversion from wstring to string relies on truncating
229  // the multibytes while assuming that for common characters,
230  // the first byte matches ASCII:
231  // http://stackoverflow.com/a/12097772/283649
232  result.push_back(std::string(arg.begin(), arg.end()));
233  }
234  LocalFree(arguments);
235 
236  return result;
237 }
238 
239 boost::posix_time::ptime getProcessStartTime(PID pid) {
240  FILETIME creationTime;
241  FILETIME exitTime; // not used
242  FILETIME kernelTime; // not used
243  FILETIME userTime; // not used
244 
245  HANDLE processHandle = OpenProcess(
246  PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
247  if (processHandle == NULL) {
248  throw std::runtime_error(boost::str(boost::format("Could not"
249  " open process with PID"
250  " %1%: %2%") % pid % GetLastErrorString()));
251  }
252 
253  BOOL ret = GetProcessTimes(processHandle,
254  &creationTime, &exitTime, &kernelTime, &userTime);
255  CloseHandle(processHandle);
256  if (ret ==0) {
257  throw std::runtime_error(boost::str(boost::format("Could not"
258  " determine process"
259  " start time: %1%")
260  % GetLastErrorString()));
261  }
262 
263  return boost::date_time::time_from_ftime<boost::posix_time::ptime>(creationTime);
264 }
265 
266 boost::posix_time::ptime currentProcessStartTime() {
268 }
269 
270 std::string getExecutingUser(PID /*pid*/) {
271  throw std::runtime_error("Could not determine executing user:"
272  " not supported");
273 }
274 
275 std::string currentExecutingUser() {
276  TCHAR buffer[UNLEN + 1];
277  DWORD size = UNLEN + 1;
278  if (GetUserName((TCHAR*) buffer, &size)) {
279  std::wstring name((wchar_t*) buffer, size - 1);
280  return std::string(name.begin(), name.end());
281  } else {
282  throw std::runtime_error(boost::str(boost::format("Could not determine"
283  " executing user: %1%")
284  % GetLastErrorString()));
285  }
286 }
287 
288 }
289 }
std::vector< std::string > getCommandlineArguments(PID pid)
Return the list of commandline arguments of the process designated by pid.
struct rsc::os::_PROCESS_BASIC_INFORMATION PROCESS_BASIC_INFORMATION
boost::posix_time::ptime getProcessStartTime(PID pid)
Return the start time of the process designated by pid.
PID currentProcessId()
Return the id the current process.
boost::posix_time::ptime currentProcessStartTime()
Return the start time of the current process.
std::string getExecutablePath(PID pid)
Return the absolute path of the executable file that is executed in the process designated by pid...
struct rsc::os::_UNICODE_STRING UNICODE_STRING
std::vector< std::string > currentCommandlineArguments()
Return the list of commandline arguments of the current process.
std::string getExecutingUser(PID pid)
Return login- or account-name of the user executing pid.
struct rsc::os::_PROCESS_BASIC_INFORMATION * PPROCESS_BASIC_INFORMATION
std::string GetLastErrorString()
Definition: Win32Common.cpp:36
std::string currentExecutingUser()
Return the login- or account-name of the user executing the current process.
struct rsc::os::_UNICODE_STRING * PUNICODE_STRING
NTSTATUS(NTAPI * _NtQueryInformationProcess)(HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, DWORD ProcessInformationLength, PDWORD ReturnLength)
std::string getProgramName(PID pid)
Return the name of the program executed in the process designated by pid.
void operator()(void *handle)
unsigned int PID
Definition: ProcessInfo.h:47
std::string currentProgramName()
Return the name of the program executed in the current process.
PVOID GetPebAddress(boost::shared_ptr< void > processHandle)
std::string currentExecutablePath()
Return the absolute path of the executable file that is executed in the current process.