【问题标题】:ShellExecute in _beginthread_beginthread 中的 ShellExecute
【发布时间】:2017-01-18 15:17:59
【问题描述】:

我需要运行例如:

ShellExecute(NULL, "open", "program.exe", NULL, NULL, SW_HIDE);

作为新线程,但我不知道如何。 我试过这个:

HANDLE hThread = (HANDLE) _beginthread(ShellExecute(NULL, "open", "program.exe", NULL, NULL, SW_HIDE), 0, NULL);
WaitForSingleObject( hThread, INFINITE );

但显然是错误的,无法编译。我该怎么做?

【问题讨论】:

  • 您不太可能需要一个新线程。 ShellExecute 是异步的。它会立即返回。它不会阻塞其他进程。如果您要开始一个新流程,CreateProcess 通常是首选。如果您想在新线程中执行此操作,请使用此签名 void( __cdecl *start_address )( void * ) 创建一个调用 ShellExecute 的函数。然后将该函数传递给_beginthread。如果您使用的是 C++,那么使用std::thread 肯定会更有意义。
  • 你想在自己的线程中运行ShellExecute(或ShellExecuteEx)的唯一原因是,你需要在那个线程上初始化COM,它可能会与调用线程的公寓。但是,您甚至没有尝试这样做,因此完全不清楚为什么您认为需要启动一个线程,然后阻止它完成执行。这并没有实现任何有用的东西。

标签: c++ winapi shellexecute beginthread


【解决方案1】:

你所尝试的确实明显是错误的,但问题是你是否理解它的问题所在。 _beginthread指向函数的指针(具有特定的原型和调用约定)作为其第一个参数。

当你写作时

HANDLE hThread = (HANDLE) _beginthread(ShellExecute(NULL, "open", "program.exe", NULL, NULL, SW_HIDE), 0, NULL);

你正试图传递_beginthread 调用ShellExecute(在当前线程中)的结果,这是一个HINSTANCE,而_beginthread 需要一个void( __cdecl *)( void * )(指针到一个__cdecl 函数,接受一个void* 参数并返回void)。

不仅您的代码不起作用,因为您试图传递一个 HINSTANCE ,其中需要一个指向指针的函数,这没有任何意义。你读过_beginthread documentation吗?那里有例子。复数。

你的意思是:

HANDLE hThread = (HANDLE) _beginthread(ThreadFunc, 0, NULL);

给定:

void __cdecl ThreadFunc(void*) {
    ShellExecute(NULL, "open", "program.exe", NULL, NULL, SW_HIDE);
}

或者,以更紧凑和易于阅读的形式:

HANDLE hThread = (HANDLE)
                 _beginthread([](void*) {
                     ShellExecute(NULL, "open", "program.exe", NULL, NULL, SW_HIDE);
                 },
                 0, NULL);

除非你做的事情超出我们在这里看到的,否则 David 的评论可能是正确的,你应该使用 std::threadstd::async

另请注意,将_beginthread 的结果(与_beginthreadexCreateThread 的结果相反)置于不安全状态,因为它可能无效,如文档中所述。不仅如此,_beginthread 的返回值也不是真正的HANDLE(它有点麻烦,但不是HANDLE!),所以你不能在上面WaitForSingleObject

_beginthread 相比,_beginthreadex 函数让您可以更好地控制线程的创建方式。 _endthreadex 函数也更加灵活。例如,使用_beginthreadex,您可以使用安全信息,设置线程的初始状态(运行或挂起),并获取新创建线程的线程标识符。 您还可以将_beginthreadex 返回的线程句柄与同步API 一起使用,而_beginthread 则无法做到这一点

使用_beginthreadex 比使用_beginthread 更安全。如果_beginthread 生成的线程快速退出,则返回给_beginthread 调用者的句柄可能无效或指向另一个线程。但是_beginthreadex返回的句柄必须被_beginthreadex的调用者关闭,所以如果_beginthreadex没有返回错误,就保证它是一个有效的句柄。

由于这个线程只调用一个函数并退出,它几乎最大化了这个句柄无效的机会。即使是,你仍然无法使用它来WaitForSingleObject

【讨论】:

  • ThreadFunc 定义和 lambda 函数都缺少各自的调用约定。更重要的是,这个答案甚至没有质疑启动新线程的必要性,即使问题中绝对没有任何内容表明这是有益的。
  • _cdecl is the default calling convention in MSVC。我认为这是常识,但也许我错了。虽然没有明确说明情况,但由于 OP 使用的是 MSVC CRT 中的函数,我推断他使用的是 MSVC 或兼容的编译器和工具链。
  • 答案并没有质疑创建新线程的必要性,原因有以下三个:(1) answer 不是这个地方,而是 cmets 区域,确实已经用于它; (2) 这与答案无关,并且 (3) OP 表示他“需要[s] 为 example 运行:ShellExecute(...)”。例如。也许他真的想经营别的东西。也许他执行各种任务,这只是其中之一。这是一个示例忽略示例的普遍问题是如何在新线程上调用函数。这是我回答的。
  • @IInspectable:至少在 VC++ 和可能的其他编译器中,当无捕获 lambda 隐式转换为函数指针时,它会采用所需的任何调用约定(编译器为所有调用约定生成代码,然后链接器会删除任何未使用的代码)。见What's the default calling convention of a C++ lambda function?Non-capturing C++ lambdas can be converted to a pointer to function, but what about the calling convention?
猜你喜欢
  • 2011-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-05
  • 2010-09-20
  • 1970-01-01
  • 2010-09-07
  • 1970-01-01
相关资源
最近更新 更多