【问题标题】:CreateProcess doesn't pass command line argumentsCreateProcess 不传递命令行参数
【发布时间】:2010-11-11 06:28:26
【问题描述】:

您好,我有以下代码,但它没有按预期工作,无法弄清楚问题所在。

基本上,我正在执行一个进程(一个 .NET 进程)并传递命令行参数,它由 CreateProcess() 成功执行,但 CreateProcess() 没有传递命令行参数

我在这里做错了什么??

int main(int argc, char* argv[])
{
    PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter

    STARTUPINFO StartupInfo; //This is an [in] parameter

    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
    StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field

    LPTSTR cmdArgs = "name@example.com";

    if(CreateProcess("D:\\email\\smtp.exe", cmdArgs, 
        NULL,NULL,FALSE,0,NULL,
        NULL,&StartupInfo,&ProcessInfo))
    { 
        WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
        CloseHandle(ProcessInfo.hThread);
        CloseHandle(ProcessInfo.hProcess);

        printf("Yohoo!");
    }  
    else
    {
        printf("The process could not be started...");
    }

    return 0;
}

编辑:嘿,还有一件事,如果我像这样传递我的cmdArgs

// a space as the first character
LPTSTR cmdArgs = " name@example.com";

然后我得到错误,然后 CreateProcess 返回 TRUE 但我的目标进程没有执行。

Object reference not set to an instance of an object

【问题讨论】:

  • 如何检测到参数没有通过?
  • 我检查执行进程的输出,如果没有传递任何参数,目标进程会打印错误并终止
  • 我想可能还有其他问题。你能在程序启动的开头插入一个延迟,并在它启动后附加一个调试器吗?
  • 在程序的开头(静态 void Main() 函数)你插入类似 System.Threading.Thread.Sleep(20000);并在该语句之后的行上设置断点。然后编译 C# 程序并使您的主程序启动 C# 程序。当 C# 程序启动时,它将在该语句上暂停 20 秒 - 这足以让您在 VisualStudio 中执行 Tools->Debug Processes 并附加到 C# 程序。 20 秒后,C# 程序将在调试器中停止,然后您可以逐步调试它。
  • 微软的这个解释可能对我有帮助,但对我来说也是一样的困惑。 support.microsoft.com/kb/175986

标签: c++ winapi createprocess


【解决方案1】:

您应该在参数中指定模块名称:LPTSTR cmdArgs = "App name@example.com"; 它应该是整个命令行(包括 argv[0])。

【讨论】:

  • 我收到错误 Object reference not set to an instance of an object
  • 这有帮助。如果我没有遇到这个答案,它永远不会知道。
  • 不得不说MSDN对此并不太清楚:“因为argv[0]是模块名,C程序员一般会重复模块名作为命令行的第一个记号。”和“如果 lpApplicationName 为 NULL,则命令行的第一个空格分隔标记指定模块名称。”后者似乎并不适用,因为提供了lpApplicationName
  • 确实,许多程序都是从 argv [1] 中的参数求值开始的,因为 argv [0] 通常包含模块名称。如果模块名称不在命令行中,则缺少此名称。最终你失去了第一个 Prameter,这是一个大问题,我花了太多时间来解决它。
【解决方案2】:

如果CreateProcess() 的第一个参数不为NULL,它将使用它来定位要启动的图像。

如果为 NULL,它将解析第二个参数以尝试从第一个令牌启动可执行文件。

在任何一种情况下,C 运行时都将使用第二个参数来填充argv 数组。因此,该参数的第一个标记显示在 argv[0] 中。

您可能需要类似以下的内容(我已将 smtp.exe 程序更改为 echoargs.exe - 我必须帮助解决此类问题的一个简单实用程序):

int main(int argc, char* argv[])
{
    PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter

    STARTUPINFO StartupInfo; //This is an [in] parameter
    char cmdArgs[] = "echoargs.exe name@example.com";

    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
    StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field


    if(CreateProcess("C:\\util\\echoargs.exe", cmdArgs, 
        NULL,NULL,FALSE,0,NULL,
        NULL,&StartupInfo,&ProcessInfo))
    { 
        WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
        CloseHandle(ProcessInfo.hThread);
        CloseHandle(ProcessInfo.hProcess);

        printf("Yohoo!");
    }  
    else
    {
        printf("The process could not be started...");
    }

    return 0;
}

这是我从那个程序得到的输出:

echoargs.exe name@example.com
[0]: echoargs.exe
[1]: name@example.com

Yohoo!

【讨论】:

  • 他是对的。我什至将此解决方案用于 win32 createprocess() 它按预期工作。谢谢老兄。
  • 第二个参数应该是指向可修改内存的指针
【解决方案3】:

您似乎没有正确使用 CreateProcess,请参阅 http://msdn.microsoft.com/en-us/library/ms682425%28VS.85%29.aspx

  • 要执行的命令行。此字符串的最大长度为 32,768 个字符,包括 Unicode 终止空字符。如果 lpApplicationName 为 NULL,则 lpCommandLine 的模块名称部分仅限于 MAX_PATH 字符。

  • lpCommandLine 参数可以为 NULL。在这种情况下,函数使用 lpApplicationName 指向的字符串作为命令行。

  • 如果lpApplicationName和lpCommandLine都非NULL,则lpApplicationName指向的空终止字符串指定要执行的模块,lpCommandLine指向的空终止字符串指定命令行强>。新进程可以使用 GetCommandLine 来检索整个命令行。用 C 编写的控制台进程可以使用 argc 和 argv 参数来解析命令行。因为 argv[0] 是模块名,所以 C 程序员一般会重复模块名作为命令行中的第一个标记。

所以在你的情况下,你需要这个作为命令参数,并且可能应该为第一个参数传递一个 NULL 以获得你想要的行为。

// NOTE THE Null-Terminated string too!
LPTSTR cmdArgs = "D:\\email\\smtp.exe name@example.com\0";

【讨论】:

  • 您的代码 sn-p 最后定义了一个带有两个空终止符的字符串 - 无需显式放入第二个。
  • 我收到错误 Object reference not set to an instance of an object
  • @Earwicker,很久没做C++了,结果还是一样...
【解决方案4】:

以下是Zeus IDE 用于运行外部进程的代码的精简版:

bool createProcess(const char *pszTitle, const char *pszCommand)
{
  STARTUPINFO StartInfo;

  memset(&StartInfo, 0, sizeof(StartInfo));

  StartInfo.cb      = sizeof(StartInfo);
  StartInfo.lpTitle = (pszTitle) ? (char *)pszTitle : (char *)pszCommand;

  StartInfo.wShowWindow = SW_NORMAL;
  StartInfo.dwFlags    |= STARTF_USESHOWWINDOW;

  if (CreateProcess(0, (char *)pszCommand, 
                    0, 0, TRUE,
                    CREATE_NEW_PROCESS_GROUP, 0, 0, 
                    &StartInfo, &ProcessInfo))
  {
    lErrorCode = 0;
  }
  else
  {
    lErrorCode = GetLastError();
  }

  return (lErrorCode == 0);
}

pszCommand 将是完整的可执行路径、文件名和参数,例如:

pszCommand = "D:\\email\\smtp.exe name@example.com";

据我所知,两者之间唯一真正的区别在于,在 Zeus 示例中,dwCreationFlags 参数设置为 CREATE_NEW_PROCESS_GROUP强> 价值。

【讨论】:

  • const char * 转换为char * 是危险的并且会调用UB
  • 我想说在这种情况下没关系。基本原理:CreateProcess 的第二个参数是LPTSTR(不是LPCTSTR)大概是因为main(int argc, char *argv[])。 API 不会复制参数。底线,只要被调用的进程不违反其argv[] (char*) 应该没问题。
  • @jussij,pszTitle 由什么组成?它存储什么数据?
【解决方案5】:

您可以在 cmdArgs 字符串的第一个字符中添加一个空格:

LPTSTR cmdArgs = " name@example.com";

显然,Windows 将第二个参数字符串附加到由第一个参数表示的应用程序名称,并将结果作为命令行参数传递给可执行文件。因此,添加空格将正确分隔参数。

【讨论】:

    【解决方案6】:

    试试这个:

    LPTSTR cmdArgs = "name@example.com";
    CString szcmdline("D:\\email\\smtp.exe");
    szcmdline += _T(" ") + cmdArgs ;
    
    //Leave first param empty and pass path + argms in 
        if(CreateProcess(NULL, szcmdline, second
    

    【讨论】:

    • 该代码中的错误,您将值放入 szcmdline_in 但使用 szcmdline。有什么理由将 MFC 字符串带入示例中?
    • 没有具体原因,只是象征性的。我将 szcmdline_in 更改为 szcmdline。谢谢。
    【解决方案7】:

    此函数的 Unicode 版本 CreateProcessW 可以修改此字符串的内容。因此,此参数不能是指向只读内存的指针(例如 const 变量或文字字符串)。如果此参数是一个常量字符串,该函数可能会导致访问冲突。

    因此您可以尝试使用LPTSTR cmdArgs = _tcsdup("name@example.com")

    另一个问题是:目标进程如何读取参数?使用 argv[0] 作为应用程序名称?然后你也应该将应用程序名称附加为第一个参数。

    【讨论】:

    • 或者把内存管理职责放回它所属的地方,编译器:TCHAR[] cmdArgs = _T("name@example.com");
    • 这里有虚假的安全感。问问自己,为什么你必须绕开 API 而微软已经 30 年左右没有修复它了。正如我之前提到的,由于int main(int argc, char* argv[]),Windows 可能需要char*(或LPTSTR)。想想吧。如果被调用者写信给argv[],Win32 API 没有真正的解决方案。实际上,通过声明 LPTSTR 可以选择离开。
    • 新进程运行在不同的地址空间;它对argv 的作用不会以任何方式影响CreateProcess() 的调用者。
    【解决方案8】:

    你没有为你的字符串分配内存。

    代替:

    LPTSTR cmdArgs = "name@example.com";
    

    尝试:

    TCHAR cmdArgs[] = "name@example.com";
    

    编辑: 然后调用:

     CreateProcess("D:\\email\\smtp.exe", &cmdArgs[0], ...
    

    这将在堆栈上创建一个本地数组,然后传递一个指向该数组的指针。

    【讨论】:

    • error C2440: 'initializing' : cannot convert from 'char (*)[20]' to 'char *'
    • 其实只要cmdArgs就可以了; &cmdArgs[0] 只是表达同一件事的更复杂的方式。
    • 清除你的概念常量字符串字面量不需要像数组 char str[] 那样的内存分配,你也可以使用像 char *str = "Arguements" 这样的指针,这里确实是更好的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 2016-03-25
    • 1970-01-01
    • 1970-01-01
    • 2020-08-24
    • 2018-04-07
    • 1970-01-01
    相关资源
    最近更新 更多