Win32 应用程序在启动时不运行

Posted

技术标签:

【中文标题】Win32 应用程序在启动时不运行【英文标题】:Win32 application doesn't run on startup 【发布时间】:2015-11-19 15:38:04 【问题描述】:

我在 Visual Studio 2015 中创建了一个 C++ Win32 应用程序。我的目标是让它在启动时运行。我在注册表中添加了一个新条目(HKCU\Software\Microsoft\Windows\CurrentVersion\Run)。由于未知原因,它不起作用。

但是,它在“启动”选项卡的任务管理器中正确显示,但在“进程”选项卡中却没有显示,这意味着它根本没有运行。我试图将它包装在一个bat文件中。 bat 运行,但应用程序似乎在启动时失败(或完全忽略,我无法准确判断)。如果我从 Program Files 目录手动启动,它会正常运行。

这是一个朋友的旧 Visual 项目,它是在当前 Visual (2015) 中转换的。如果我从头开始创建一个新项目,它就可以工作。但这种行为是没有理由的。

最后的想法 - 我成功解决了我的问题。我的程序无法找到 SQLite 文件,因为 Windows 在启动时启动的任何程序都使用“c:\Windows\System32”作为当前目录。我刚刚应用了 Marko Popovic 的解决方案。这只是一个简单的当前目录问题。

PS - Marko Popovic 的解决方案很好,但@IInspectable 警告此方法可能出现的传入错误。他建议只使用完全限定的路径名​​而不更改目录

【问题讨论】:

问题大概是“如何诊断故障模式?” 它是否可能与它的启动目录有关,可能是从当前目录加载 DLL 并且该目录不在路径中?会不会是在读取尚未设置的环境变量? @Matt:这往往会导致更大的故障。但这并非完全不可能。 你说你从一个批处理文件开始。如果您打开 cmd.exe 控制台,cd 到 c:\ (cd /d c:\ ) 并从那里启动带有完整路径的批处理文件,它会启动吗?如果您进入系统目录 (cd /d c:\Windows),同样的问题? 我从批处理文件中运行以进行测试。它既不能与 bat 一起使用,也不能直接使用。 【参考方案1】:

当应用程序使用“运行”键启动时,其工作目录不是其映像文件 (*.exe) 所在的目录。您可以通过在 main 函数的最开始调用 Win32 函数 SetCurrentDirectory 来更改此设置。这是一个解决方案的框架:

// Use GetModuleFileName to retrieve path of the executable file
TCHAR pszPathToSelf[MAX_PATH];
DWORD dwPathLength = GetModuleFileName(NULL, pszPathToSelf, MAX_PATH);
if(dwPathLength > 0)

    // Code that extracts directory path from pszPathToSelf and places it into variable pszNewWorkingDirectory of type TCHAR
    ...

    BOOL bSuccess = SetCurrentDirectory(pszNewWorkingDirectory);
    if(0 == bSuccess)
    
    // SetCurrentDirectory failed for some reason, handle how you see fit
    ....
    
 

这样,您的函数将更改其工作目录并加载必要的 DLL。

编辑: 正如用户 IInspectable 指出的那样,上述方法在默认情况下不起作用,而仅在某些特殊情况下起作用。更好的方法是在App Paths 注册表项下注册您的应用程序。如果您的应用程序名为 my_app.exe,请创建以下注册表项: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\App Paths\my_app.exe 然后创建一个名为PathREG_SZ 值,其中将包含保存my_app.exe 的目录的路径。

更多详情,请参考以下 MSDN 页面:

https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121%28v=vs.85%29.aspx

抱歉最初的错误!

【讨论】:

这无济于事,除非应用程序使用/DELAYLOAD 链接器选项。默认情况下,所有 DLL 导入都会在您的任何代码运行之前解析。无论如何设置工作目录都是错误的。 SetDllDirectory/AddDllDirectory 是适当的 API,它们的工作并非巧合。 @IInspectable 嗯,你是对的!我忘了导入之前已经解决了。这仅适用于延迟加载的 DLL,或者如果它们是使用 LoadLibrary 动态加载的。 感谢@MarkoPopovic,这解决了我的问题。它不是 DLL,而是 SQLite 文件。我的程序在 Windows 目录中找不到或创建此文件。但是我发现我的程序在它自己的目录中查找 DLL。我猜这是因为动态链接的工作方式。 @Spiralwise:SetCurrentDirectory 不能解决您的问题。 CWD 是每个进程的属性。任何线程和任何库都可以随时更改 CWD(例如,通用文件对话框会这样做)。您的问题的解决方案是使用完全限定的路径名​​。是的,加载应用程序的目录位于Dynamic-Link Library Search Order 的顶部,所以这个答案既错误又没有任何帮助。 因为我的DLL和可执行文件在同一个目录下,所以这部分没有问题。由于@MarkoPopovic 的方法,我得到了完全限定的路径名​​,所以我不太明白到底出了什么问题。 (不过,我会更正我的帖子更新。)

以上是关于Win32 应用程序在启动时不运行的主要内容,如果未能解决你的问题,请参考以下文章

Flyway Spring Boot应用程序在启动时不应用插入脚本

LaunchFullTrustProcessForCurrentAppAsync杀死win32应用程序

如何防止系统在 Win32 服务中关闭或重新启动?

Delphi Tokyo 10.2.2 - Win XP 在运行时不加载包

如何关闭win7 开启的无用服务

Swift Scene Delegate 在首次启动时不运行代码