通过 CreateProcess() 运行批处理文件的多线程服务

发布于 2024-10-06 10:01:25 字数 3109 浏览 0 评论 0原文

请参阅最后更新

我正在尝试运行以下批处理文件(名为 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,但批处理文件未运行(我已通过将 boot_time.bat 的内容替换为简单的“start calc”来验证这一点,但它仍然拒绝运行)。有问题的代码:

//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

UPDATE 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 不会导致弹出消息框,直接从命令行运行会弹出消息框。

SEE UPDATE AT END

I am attempting to run the following batch file (named boot_time.bat, in the same directory as the .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%

The batch file takes one argument, it's current working directory. It's purpose is to retrieve the system boot time via the boot_time.vbs script and format it into a common date format. For completenesses sake, here are the contents of the vbs file:

set objWMI = GetObject("winmgmts:\\.\root\cimv2")
set colOS = objWMI.InstancesOf("Win32_OperatingSystem")
for each objOS in colOS
    Wscript.Echo objOS.LastBootUpTime
NEXT

While I appreciate comments on alternative (read: simpler) methods to retrieve the system boot time, rest assured I have explored all avenues (that I have come across) and none match the requirements for the system.

Now, the crux of the issue. When I attempt to run the .bat file via C++, CreateProcess() returns 1, but the batch file is not run (I have verified this by replacing the contents of boot_time.bat with a simple 'start calc' which still refuses to run). The offending code:

//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 );

The paths and command line arguments are all fine (I can debug print them and paste into cmd.exe, where they work as expected). Stepping through the code, CreateProcess() returns 1 and everything runs as normal, however the batch file never gets run.

In my many hours searching the interwebs I have come across whisperings of issues when it comes to running a batch file from a service, but I:

a) can no longer find said whisperings,
b) would think that CreateProcess("cmd.exe",...) is not running a batch file, but an exe, and leaving the rest up to the command prompt.

So, any idea what is going on?

Oh, I'm running VC++6

UPDATE 1:
If I tick 'Allow service to interact with desktop', and change CreateProcess() to not hide the console window (via CREATE_NEW_CONSOLE), I get a split second of the command prompt popping up. So, CreateProcess() is creating a cmd.exe process, but cmd.exe is refusing to run the batch file.

UPDATE 2:
After Gabe's suggestion I rethought my process and simplified it somewhat in an attempt to track down the culprit. I've removed the batch file, and consolodated it into the VBS file, which is now:

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

The c++ code is largely unchanged, just modified the command line to call WScript.exe instead of cmd.exe. I'm getting the fully qualified paths for all files, which leads to the following string:

"C:\WINDOWS\System32\Wscript.exe" //Nologo "c:\<repository_dir>\boot_time.vbs" >"C:\WINDOWS\TEMP\rts5FD.tmp"

(repository_dir is intentional to leave out names). Also, I'm only using Wscript to test, as it pops up a message box. Proper usage will be with Cscript.exe, with output redirected to the temp file.

CreateProcess does not cause the message box to pop up, running direct from command line does.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

始于初秋 2024-10-13 10:01:25

仔细阅读 MSDN 对 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 性能计数器,并从当前时间。

Read closely this line from MSDN's description of CreateProcess:

Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.

This imples that your code should be:

commandStream << "cmd.exe /C "

See also http://blogs.msdn.com/b/oldnewthing/archive/2006/05/15/597984.aspx.

However, I must add that I would shoot somebody I found using C++ to run a batch file to execute a VBScript in order to process its output. Since the batch file is doing nothing but string manipulation, why can't you do that string manipulation in VBS or C++?

Alternatively, you can avoid the batch file and just call cscript directly with this code:

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)

Another option is to query the System\System Up Time performance counter and subtract its value from the current time.

猛虎独行 2024-10-13 10:01:25

我设法找出 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 包含 COM 接口定义,您可以使用 midl 生成这些定义,甚至可以使用最新 SDK 中的标头。

不幸的是我什至没有安装vc6。

I managed to figure out the C++ code to do all this.

Like most C++ COM code it gets quite verbose. But here you go:

#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;
}

I think you can get this working on vc6. wbemidl.h contains COM interfaces definitions that you could probably generate with midl or perhaps even use the headers from a recent SDK.

Unfortunately I don't even have vc6 installed.

如日中天 2024-10-13 10:01:25

感谢您的意见,不过我已经设法解决了这个问题。问题似乎源于我调用 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""

感谢您的所有帮助!

Thanks for the input, however I've managed to fix the issue. It seems the problem stemmed from the fact that the service from which i'm calling CreateProcess() is running as LocalSystem. CreateProcess() creates the new process under the same user, where I needed it to be running as the logged in user. Here is the code to get the current user, and use their token in 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;

Where, in my case, command.c_str() points to the following string:

/C ""C:\WINDOWS\System32\Cscript.exe" //Nologo "c:\<repository_dir>\boot_time.vbs" >"C:\WINDOWS\TEMP\rts452.tmp""

Thanks for all the help!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文