PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/juno/utils/process.d

http://github.com/JesseKPhillips/Juno-Windows-Class-Library
D | 432 lines | 220 code | 63 blank | 149 comment | 45 complexity | 9c99681120dcf81229e6604552434813 MD5 | raw file
  1. module juno.utils.process;
  2. import juno.base.core,
  3. juno.base.string,
  4. juno.base.threading,
  5. juno.base.native,
  6. juno.com.core;
  7. static import juno.io.path;
  8. import std.c.stdlib : malloc, realloc, free;
  9. debug import std.stdio : writefln;
  10. private extern(C) int _wcsicmp(in wchar*, in wchar*);
  11. private class ProcessInfo {
  12. uint processId;
  13. string processName;
  14. }
  15. class ProcessStart {
  16. string fileName;
  17. string arguments;
  18. string userName;
  19. string password;
  20. string domain;
  21. bool useShellExecute = true;
  22. this() {
  23. }
  24. this(string fileName, string arguments) {
  25. this.fileName = fileName;
  26. this.arguments = arguments;
  27. }
  28. }
  29. class Process {
  30. private Optional!(Handle) handle_;
  31. private string machineName_;
  32. private bool isRemote_;
  33. private Optional!(uint) id_;
  34. private string processName_;
  35. private ProcessInfo processInfo_;
  36. // Process.start parameters
  37. ProcessStart start_;
  38. this() {
  39. machineName_ = ".";
  40. }
  41. private this(string machineName, bool isRemote, uint id, ProcessInfo processInfo) {
  42. machineName_ = machineName;
  43. isRemote_ = isRemote;
  44. id_ = id;
  45. processInfo_ = processInfo;
  46. }
  47. ~this() {
  48. close();
  49. }
  50. final void close() {
  51. if (handle_.hasValue && (handle_.value != Handle.init)) {
  52. CloseHandle(handle_.value);
  53. // Re-initialize so handle_.hasValue returns false.
  54. //handle_ = (Optional!(Handle)).init;
  55. }
  56. processInfo_ = null;
  57. }
  58. static Process start(string fileName) {
  59. return start(fileName, null);
  60. }
  61. static Process start(string fileName, string arguments) {
  62. return start(new ProcessStart(fileName, arguments));
  63. }
  64. static Process start(ProcessStart start) {
  65. auto process = new Process;
  66. process.start_ = start;
  67. if (process.start())
  68. return process;
  69. return null;
  70. }
  71. final bool start() {
  72. close();
  73. if (start_.useShellExecute) {
  74. SHELLEXECUTEINFO sei;
  75. sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI | SEE_MASK_FLAG_DDEWAIT;
  76. sei.nShow = /*SW_SHOWNORMAL*/ 1;
  77. sei.lpFile = start_.fileName.toUtf16z();
  78. sei.lpParameters = start_.arguments.toUtf16z();
  79. if (!ShellExecuteEx(sei))
  80. throw new Win32Exception;
  81. if (sei.hProcess != Handle.init) {
  82. handle_ = sei.hProcess;
  83. return true;
  84. }
  85. return false;
  86. }
  87. STARTUPINFO startupInfo;
  88. PROCESS_INFORMATION processInfo;
  89. Handle processHandle;
  90. string commandLine = "\"" ~ start_.fileName ~ "\"";
  91. if (start_.arguments != null)
  92. commandLine ~= " " ~ start_.arguments;
  93. auto pCommandLine = commandLine.toUtf16z();
  94. auto pWorkingDirectory = juno.io.path.currentDirectory().toUtf16z();
  95. uint creationFlags = 0;
  96. /*if (start_.userName != null) {
  97. wchar* pPassword;
  98. if (start_.password !is null)
  99. pPassword = secureStringToUnicode(start_.password);
  100. uint logonFlags = 0;
  101. if (!CreateProcessWithLogonW(start_.userName.toUtf16z(), pPassword, start_.domain.toUtf16z(), logonFlags, null, pCommandLine, creationFlags, null, pWorkingDirectory, &startupInfo, &processInfo))
  102. throw new Win32Exception;
  103. processHandle = processInfo.hProcess;
  104. // Not interested in the returned thread.
  105. CloseHandle(processInfo.hThread);
  106. if (pPassword != null)
  107. CoTaskMemFree(pPassword);
  108. }
  109. else*/ {
  110. if (!CreateProcess(null, pCommandLine, null, null, TRUE, creationFlags, null, pWorkingDirectory, startupInfo, processInfo))
  111. throw new Win32Exception;
  112. processHandle = processInfo.hProcess;
  113. // Not interested in the returned thread.
  114. CloseHandle(processInfo.hThread);
  115. }
  116. if (processHandle != Handle.init) {
  117. handle_ = processHandle;
  118. id_ = processInfo.dwProcessId;
  119. }
  120. return false;
  121. }
  122. final void kill() {
  123. ensureProcessId();
  124. Handle handle;
  125. if (!handle_.hasValue) {
  126. handle = OpenProcess(PROCESS_TERMINATE, FALSE, id_.value);
  127. if (handle_.value == Handle.init)
  128. throw new Win32Exception;
  129. }
  130. else {
  131. /*Handle waitHandle;
  132. DuplicateHandle(GetCurrentProcess(), handle_.value, GetCurrentProcess(), waitHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
  133. WaitForSingleObjectEx(waitHandle, 0, 1);
  134. CloseHandle(waitHandle);*/
  135. handle = handle_.value;
  136. }
  137. if (!TerminateProcess(handle, -1))
  138. throw new Win32Exception;
  139. CloseHandle(handle);
  140. }
  141. static Process current() {
  142. return new Process(".", false, GetCurrentProcessId(), null);
  143. }
  144. static Process[] getProcesses() {
  145. auto processInfos = getProcessInfos();
  146. auto processes = new Process[processInfos.length];
  147. foreach (i, processInfo; processInfos)
  148. processes[i] = new Process(".", false, processInfo.processId, processInfo);
  149. return processes;
  150. }
  151. private void ensureProcessId() {
  152. if (!id_.hasValue) {
  153. PROCESS_BASIC_INFORMATION info;
  154. int status = NtQueryInformationProcess(handle_.value, PROCESS_INFORMATION_CLASS.ProcessBasicInformation, &info, info.sizeof, null);
  155. if (status != 0)
  156. throw new InvalidOperationException;
  157. id_ = info.uniqueProcessId;
  158. }
  159. }
  160. private void ensureProcessInfo() {
  161. ensureProcessId();
  162. if (processInfo_ is null) {
  163. auto processInfos = getProcessInfos();
  164. foreach (processInfo; processInfos) {
  165. if (processInfo.processId == id_.value) {
  166. processInfo_ = processInfo;
  167. break;
  168. }
  169. }
  170. }
  171. }
  172. final Handle handle() {
  173. ensureProcessId();
  174. if (!handle_.hasValue) {
  175. handle_ = OpenProcess(PROCESS_ALL_ACCESS, FALSE, id_.value);
  176. if (handle_.value == Handle.init)
  177. throw new Win32Exception;
  178. }
  179. return handle_.value;
  180. }
  181. final string processName() {
  182. if (processName_ == null) {
  183. ensureProcessInfo();
  184. return processInfo_.processName;
  185. }
  186. return processName_;
  187. }
  188. final string machineName() {
  189. return machineName_;
  190. }
  191. final uint id() {
  192. ensureProcessId();
  193. return id_.value;
  194. }
  195. private static ProcessInfo[] getProcessInfos() {
  196. string getProcessName(wchar* name, uint length) {
  197. wchar* str = name, period = name, slash = name;
  198. int i;
  199. while (*str != 0) {
  200. if (*str == '.') period = str;
  201. if (*str == '\\') slash = str;
  202. str++, i++;
  203. if (i >= length)
  204. break;
  205. }
  206. if (period == name) period = str;
  207. else if (_wcsicmp(period, ".exe") != 0) period = str;
  208. if (*slash == '\\') slash++;
  209. return toUtf8(slash, 0, period - slash);
  210. }
  211. ProcessInfo[uint] processInfos;
  212. uint bufferSize = 128 * 1024;
  213. uint neededSize;
  214. ubyte* buffer;
  215. int status;
  216. do {
  217. buffer = cast(ubyte*)realloc(buffer, bufferSize);
  218. status = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessInformation, buffer, bufferSize, &neededSize);
  219. if (status == STATUS_INFO_LENGTH_MISMATCH)
  220. bufferSize = neededSize + 10 * 1024;
  221. } while (status == STATUS_INFO_LENGTH_MISMATCH);
  222. if (status < 0)
  223. throw new InvalidOperationException;
  224. scope(exit) free(buffer);
  225. int offset;
  226. while (true) {
  227. auto pProcessInfo = cast(SYSTEM_PROCESS_INFORMATION*)(buffer + offset);
  228. auto processInfo = new ProcessInfo;
  229. processInfo.processId = cast(uint)pProcessInfo.uniqueProcessId;
  230. if (pProcessInfo.nameBuffer != null)
  231. processInfo.processName = getProcessName(pProcessInfo.nameBuffer, pProcessInfo.nameLength / 2);
  232. processInfos[processInfo.processId] = processInfo;
  233. if (pProcessInfo.nextEntryOffset == 0)
  234. break;
  235. offset += pProcessInfo.nextEntryOffset;
  236. }
  237. return processInfos.values;
  238. }
  239. }
  240. /+enum ServiceControllerStatus {
  241. Stopped = SERVICE_STOPPED,
  242. StartPending = SERVICE_START_PENDING,
  243. StopPending = SERVICE_STOP_PENDING,
  244. Running = SERVICE_RUNNING,
  245. ContinuePending = SERVICE_CONTINUE_PENDING,
  246. PausePending = SERVICE_PAUSE_PENDING,
  247. Paused = SERVICE_PAUSED
  248. }
  249. class ServiceController {
  250. private Handle serviceManagerHandle_;
  251. private string name_;
  252. private string machineName_ = ".";
  253. private Optional!(ServiceControllerStatus) status_;
  254. this() {
  255. }
  256. this(string name) {
  257. name_ = name;
  258. }
  259. this(string name, string machineName) {
  260. name_ = name;
  261. machineName_ = machineName;
  262. }
  263. final void close() {
  264. if (serviceManagerHandle_ != Handle.init) {
  265. CloseServiceHandle(serviceManagerHandle_);
  266. serviceManagerHandle_ = Handle.init;
  267. }
  268. }
  269. final void start(string[] args = null) {
  270. ensureServiceManagerHandle();
  271. Handle serviceHandle = getServiceHandle(SERVICE_START);
  272. scope(exit) CloseServiceHandle(serviceHandle);
  273. auto pArgs = cast(wchar**)LocalAlloc(LMEM_FIXED, args.length * (wchar*).sizeof);
  274. foreach (i, arg; args)
  275. pArgs[i] = arg.toUtf16z();
  276. scope(exit) LocalFree(pArgs);
  277. if (StartService(serviceHandle, args.length, pArgs) != TRUE)
  278. throw new Win32Exception;
  279. }
  280. final void pause() {
  281. ensureServiceManagerHandle();
  282. Handle serviceHandle = getServiceHandle(SERVICE_PAUSE_CONTINUE);
  283. scope(exit) CloseServiceHandle(serviceHandle);
  284. SERVICE_STATUS status;
  285. if (ControlService(serviceHandle, SERVICE_CONTROL_PAUSE, status) != TRUE)
  286. throw new Win32Exception;
  287. }
  288. final void stop() {
  289. ensureServiceManagerHandle();
  290. Handle serviceHandle = getServiceHandle(SERVICE_STOP);
  291. scope(exit) CloseServiceHandle(serviceHandle);
  292. SERVICE_STATUS status;
  293. if (ControlService(serviceHandle, SERVICE_CONTROL_STOP, status) != TRUE)
  294. throw new Win32Exception;
  295. }
  296. final void refresh() {
  297. status_ = (Optional!(ServiceControllerStatus)).init;
  298. }
  299. final void waitForStatus(ServiceControllerStatus desiredStatus) {
  300. refresh();
  301. while (status != desiredStatus) {
  302. sleep(250);
  303. refresh();
  304. }
  305. }
  306. final string serviceName() {
  307. return name_;
  308. }
  309. final ServiceControllerStatus status() {
  310. if (!status_.hasValue) {
  311. ensureServiceManagerHandle();
  312. Handle serviceHandle = getServiceHandle(SERVICE_QUERY_STATUS);
  313. scope(exit) CloseServiceHandle(serviceHandle);
  314. SERVICE_STATUS status;
  315. if (QueryServiceStatus(serviceHandle, status) != TRUE)
  316. throw new Win32Exception;
  317. status_ = cast(ServiceControllerStatus)status.dwCurrentState;
  318. }
  319. return status_.value;
  320. }
  321. private void ensureServiceManagerHandle() {
  322. if (serviceManagerHandle_ == Handle.init) {
  323. if (machineName_ == "." || machineName_ == null)
  324. serviceManagerHandle_ = OpenSCManager(null, null, SC_MANAGER_CONNECT);
  325. else
  326. serviceManagerHandle_ = OpenSCManager(machineName_.toUtf16z(), null, SC_MANAGER_CONNECT);
  327. if (serviceManagerHandle_ == Handle.init)
  328. throw new Win32Exception;
  329. }
  330. }
  331. private Handle getServiceHandle(uint access) {
  332. ensureServiceManagerHandle();
  333. Handle serviceHandle = OpenService(serviceManagerHandle_, serviceName().toUtf16z(), access);
  334. if (serviceHandle == Handle.init)
  335. throw new Win32Exception;
  336. return serviceHandle;
  337. }
  338. }+/