最后看到更新
我正在尝试运行以下批处理文件(名为boot_time.bat,与.exe位于同一目录中):
@echo off
cd %1
For /F %%I in ('Cscript boot_time.vbs //Nologo') Do Set var=%%I
set DATETIME=%var:~0,4%/%var:~4,2%/%var:~6,2% %var:~8,2%:%var:~10,2%:%var:~12,2%.%var:~15,3%
echo %DATETIME%
批处理文件采用一个参数,它是当前的工作目录。它的目的是通过boot_time.vbs脚本检索系统引导时间并将其格式化为通用日期格式。为了完整起见,这里是vbs文件的内容:
set objWMI = GetObject("winmgmts:\\.\root\cimv2")
set colOS = objWMI.InstancesOf("Win32_OperatingSystem")
for each objOS in colOS
Wscript.Echo objOS.LastBootUpTime
NEXT
虽然我很欣赏有关替代(读取:更简单)方法的评论来检索系统启动时间,但请放心,我已经探索了所有途径(我遇到过)并且没有一个符合系统要求。
现在,问题的关键。当我尝试通过C ++运行.bat文件时,CreateProcess()返回1,但是批处理文件没有运行(我已经通过用一个简单的'start calc'替换boot_time.bat的内容来验证这一点,它仍然拒绝运行)。违规代码:
//run batch file
stringstream commandStream;
commandStream << "/C " //close window on termination
<< "\"" << batchFile.c_str() << "\" " //batch file path inside ""
<< "\"" << processPath.c_str() << "\" " //working directory as argument inside ""
<< ">" //redirect
<< "\"" << outFile.c_str() << "\""; //output file inside ""
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
BOOL ret = CreateProcess("cmd.exe", const_cast<char*>(commandStream.str().c_str()), NULL, NULL, TRUE, NULL, NULL, processPath.c_str(), &si, &pi);
WaitForSingleObject( pi.hProcess, INFINITE );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
路径和命令行参数都很好(我可以调试打印它们并粘贴到cmd.exe中,它们按预期工作)。单步执行代码,CreateProcess()返回1,一切都正常运行,但批处理文件永远不会运行。
在搜索互联网的几个小时内,我遇到了从服务运行批处理文件时遇到的问题,但是我:
a)再也找不到所说的耳语了 b)会认为CreateProcess(“cmd.exe”,...)没有运行批处理文件,而是运行exe,剩下的就是命令提示符。那么,知道发生了什么事吗?
哦,我正在运行VC ++ 6
更新1:
如果我勾选“允许服务与桌面交互”,并更改CreateProcess()以不隐藏控制台窗口(通过CREATE_NEW_CONSOLE),我会弹出命令提示符的瞬间。因此,CreateProcess()正在创建cmd.exe进程,但cmd.exe拒绝运行批处理文件。
更新2:
在加布的建议之后,我重新考虑了我的过程,并在某种程度上对其进行了简化,试图追查罪魁祸首。我删除了批处理文件,并将其合并到VBS文件中,现在是:
set objWMI = GetObject("winmgmts:\\.\root\cimv2")
set colOS = objWMI.InstancesOf("Win32_OperatingSystem")
Dim bootTime
for each objOS in colOS
bootTime = objOS.LastBootUpTime
bootTime = mid(bootTime,1,4) & "/" & Mid(bootTime,5,2) & "/" & Mid(bootTime,7,2) & " " & Mid(bootTime,9,2) & ":" & Mid(bootTime,11,2) & ":" & Mid(bootTime,13,2) & "." & Mid(bootTime,16,3)
Wscript.Echo bootTime
NEXT
c ++代码基本没有变化,只是修改了命令行来调用WScript.exe而不是cmd.exe。我正在获取所有文件的完全限定路径,这会导致以下字符串:
"C:\WINDOWS\System32\Wscript.exe" //Nologo "c:\<repository_dir>\boot_time.vbs" >"C:\WINDOWS\TEMP\rts5FD.tmp"
(repository_dir故意遗漏名字)。另外,我只是使用Wscript进行测试,因为它会弹出一个消息框。正确使用Cscript.exe,输出重定向到临时文件。
CreateProcess不会导致弹出消息框,直接从命令行运行。
答案 0 :(得分:0)
请仔细阅读MSDN's description of CreateProcess:
中的这一行因为argv [0]是模块名称,所以C程序员通常会重复模块名称作为命令行中的第一个标记。
这实现了您的代码应该是:
commandStream << "cmd.exe /C "
另见http://blogs.msdn.com/b/oldnewthing/archive/2006/05/15/597984.aspx。
但是,我必须补充一点,我会拍摄一些我发现使用C ++运行批处理文件来执行VBScript以处理其输出的人。由于批处理文件只执行字符串操作,为什么不能在VBS或C ++中进行字符串操作?
或者,您可以避免批处理文件,只需使用以下代码直接调用cscript
:
set objWMI = GetObject("winmgmts:\\.\root\cimv2")
set colOS = objWMI.InstancesOf("Win32_OperatingSystem")
for each objOS in colOS
t = objOS.LastBootUpTime
next
wscript.echo left(t, 4) & "/" & mid(t, 5, 2) & "/" & mid(t, 7, 2) & " " & _
mid(t, 9, 2) & ":" & mid(t, 11, 2) & ":" & mid(t, 13, 2) & "." & _
mid(t, 16, 3)
另一种选择是查询System\System Up Time
性能计数器并从当前时间中减去其值。
答案 1 :(得分:0)
我设法找出C ++代码来完成所有这些。
与大多数C ++ COM代码一样,它变得非常冗长。但是你走了:
#define _WIN32_DCOM
#include <iostream>
#include <exception>
#include <string>
#include <windows.h>
#include <wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")
void com_init() {
HRESULT hr;
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr)) {
throw std::runtime_error("COM Error");
}
hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
if (FAILED(hr)) {
CoUninitialize();
throw std::runtime_error("COM Error");
}
}
std::wstring wmi_last_boot_time() {
IWbemLocator *pLoc = NULL;
IWbemServices *pSvc = NULL;
IEnumWbemClassObject *pEnum = NULL;
IWbemClassObject *pEach = NULL;
std::wstring lastBootUpTime;
try {
HRESULT hr;
// Get WMI object
hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hr)) {
throw std::runtime_error("WMI: Unable to create WbemLocator");
}
hr = pLoc->ConnectServer(L"root\\cimv2", NULL, NULL, 0, NULL, 0, 0, &pSvc);
if (FAILED(hr)) {
throw std::runtime_error("WMI: Unable to connect");
}
// Exec query
hr = pSvc->ExecQuery(L"WQL", L"SELECT LastBootUpTime FROM Win32_OperatingSystem", 0, 0, &pEnum);
if (FAILED(hr)) {
throw std::runtime_error("WMI: Query failed");
}
// Fetch result
VARIANT value;
ULONG uCount;
hr = pEnum->Next(WBEM_INFINITE, 1, &pEach, &uCount);
if (FAILED(hr) || uCount == 0) {
throw std::runtime_error("WMI: Can't fetch result");
}
hr = pEach->Get(L"LastBootUpTime", 0, &value, 0, 0);
if (FAILED(hr)) {
throw std::runtime_error("WMI: Can't get LastBootUpTime");
}
if (value.vt != VT_BSTR) {
throw std::runtime_error("Expected string");
}
lastBootUpTime = value.bstrVal;
} catch (std::runtime_error &) {
if (pLoc) pLoc->Release();
if (pSvc) pSvc->Release();
if (pEnum) pEnum->Release();
if (pEach) pEach->Release();
throw;
}
pLoc->Release();
pSvc->Release();
pEnum->Release();
pEach->Release();
return lastBootUpTime;
}
int main() {
try {
// Initialize COM. Do this only once
com_init();
// Get last boot time
std::wstring str = wmi_last_boot_time();
// Reformat time
std::wcout << str.substr(0, 4) << "/" << str.substr(4, 2) << "/" << str.substr(6, 2) << " "
<< str.substr(8, 2) << ":" << str.substr(10, 2) << ":" << str.substr(12, 2) << "."
<< str.substr(15, 3) << std::endl;
// Uninitialize COM
CoUninitialize();
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
我认为你可以在vc6上使用它。 wbemidl.h包含您可能使用midl
生成的COM接口定义,或者甚至可能使用最近的SDK中的标头。
不幸的是我甚至没有安装vc6。
答案 2 :(得分:0)
感谢您的投入,但我已成功解决了这个问题。似乎问题源于我正在调用CreateProcess()的服务作为LocalSystem运行的事实。 CreateProcess()在同一个用户下创建新进程,我需要它以登录用户身份运行。以下是获取当前用户的代码,并在CreateProcessAsUser()中使用其标记:
//retrieve the user token via an open process
HANDLE hToken = NULL;
HANDLE hProcess = NULL;
DWORD dwProcessId = NULL;
//user 'explorer.exe' as the process to search for
PROCESSENTRY32 pe32;
ZeroMemory(&pe32,sizeof(pe32));
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL);
pe32.dwSize = sizeof(PROCESSENTRY32);
if(Process32First(hSnapshot,&pe32))
{
do
{
if(!strcmp(pe32.szExeFile,"explorer.exe"))
{
dwProcessId = pe32.th32ProcessID;
break;
}
}while(Process32Next(hSnapshot,&pe32));
}
if( dwProcessId )
{
hProcess = OpenProcess(PROCESS_ALL_ACCESS,TRUE, dwProcessId );
if( hProcess)
{
OpenProcessToken(hProcess, TOKEN_EXECUTE | TOKEN_READ | TOKEN_QUERY |
TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY_SOURCE |
TOKEN_WRITE | TOKEN_DUPLICATE,
&hToken);
CloseHandle( hProcess );
}
else
{
error = _T("Could not open process 'explorer.exe'");
return false;
}
}
else
{
error = _T("Could not retrieve process id for 'explorer.exe'");
return false;
}
if (hToken != NULL)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
if (CreateProcessAsUser(hToken, "cmd.exe", const_cast<char*>(command.c_str()), NULL, NULL, TRUE,
CREATE_NO_WINDOW, NULL, NULL, &si, &pi) == 0)
{
tstringstream err;
err << "CreateProcessAsUser() failed with error: " << GetLastError();
error = err.str();
CloseHandle(hToken);
return false;
}
WaitForSingleObject( pi.hProcess, INFINITE );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
CloseHandle( hToken );
}
else
{
error = _T("Token retrieved from 'explorer.exe' is NULL");
return false;
}
return true;
在我的情况下,command.c_str()指向以下字符串:
/C ""C:\WINDOWS\System32\Cscript.exe" //Nologo "c:\<repository_dir>\boot_time.vbs" >"C:\WINDOWS\TEMP\rts452.tmp""
感谢您的帮助!