【问题标题】:Which reasons could make ShellExecute fail?哪些原因可能导致 ShellExecute 失败?
【发布时间】:2010-11-14 16:46:00
【问题描述】:

我有一个 VB6 应用程序,它使用以下方法打开文件及其关联的应用程序:

ShellExecute(0, "open", filename, params, vbNullString, vbNormalFocus)

这非常有效。

现在我有一个客户(使用 Adob​​e Reader 运行 XP)无法使用上述命令打开任何 PDF 文件。但是当从 Windows 资源管理器中双击同一个文件时,它打开时没有任何问题。我还在我的机器上测试了文件名/路径组合以排除这些问题。

我正在寻找有关我可以检查以确保 ShellExecute 正常工作的任何提示。或者是什么导致 ShellExecute 以这种方式失败?

【问题讨论】:

  • 我很好奇你为什么要在文件名中添加 vbNullString。有什么理由吗?如果您使用它有什么改变吗?
  • 我认为,在字符串末尾添加 'vbNullString' 是不必要的。但是,如果我错了,您也需要将其添加到“打开”中。
  • 真的不需要。某天刚刚添加了它,现在它仍然存在。然后我添加了它,因为文档说该函数采用以空字符结尾的字符串。
  • 我删除了它们,它使代码更具可读性。
  • vbNullString 不是 不仅是 NUL 字符(您可能想要 vbNullChar),所有 VB 字符串值都已经在内部以 NUL 结尾。 “文件名”是完全限定的路径吗? “参数”中有什么(以及为什么要传递它)?但我同意返回值在这里应该是有意义的。

标签: winapi file vb6 registry shellexecute


【解决方案1】:

ShellExecute 的返回值是多少?如果是0x0000001f(== 31,意思是SE_ERR_NOASSOC),比根据shellapi.h “没有应用关联 给定的文件扩展名。”,这意味着 .pdf 文件扩展名的注册不知何故丢失了。重新安装 Adob​​e Reader 可能会有所帮助。

【讨论】:

  • 不过,这很奇怪。为什么 PDF 可以在桌面上工作,但不能在 ShellExecute 上工作?很奇怪。
  • 可能是 Explorer 使用了一些 COM voodoo ;)
  • windows 至少有两种方式来决定哪个应用程序调用哪个文件扩展名:一种对机器上的所有用户都有效,另一种是用户特定的。也许用户摆弄了 pdf 文件关联(资源管理器:文件夹选项 - 文件类型)。
  • 不幸的是我没有返回值,只有在将新版本部署到客户端机器后才能得到它。但至少我可以建议重新安装 Adob​​e Reader。
【解决方案2】:

在 Thomas 的 answer 之外,这里有一些可能返回值 ShellExecute 的 VB6 常量,以及可能的解释(我想我最初是从 MSDN page,返回值部分获取这些)。返回值等于或小于 32 表示调用失败。返回的具体值表明出了什么问题。

Const ERROR_BAD_FORMAT = 11&
Const ERROR_FILE_NOT_FOUND = 2&          
Const ERROR_PATH_NOT_FOUND = 3&          ' The specified path was not found. '
Const SE_ERR_ACCESSDENIED = 5            ' The operating system denied access to the specified file. '
Const SE_ERR_ASSOCINCOMPLETE = 27        ' The file name association is incomplete or invalid. '
Const SE_ERR_DDEBUSY = 30                ' The Dynamic Data Exchange (DDE) transaction could not be completed because other DDE transactions were being processed. '
Const SE_ERR_DDEFAIL = 29                ' The DDE transaction failed. '
Const SE_ERR_DDETIMEOUT = 28             ' The DDE transaction could not be completed because the request timed out. '
Const SE_ERR_DLLNOTFOUND = 32            ' The specified dynamic-link library (DLL) was not found. '
Const SE_ERR_FNF = 2                     ' The specified file was not found. '
Const SE_ERR_NOASSOC = 31                ' There is no application associated with the given file name extension. '
Const SE_ERR_OOM = 8                     '  out of memory '
Const SE_ERR_PNF = 3                     '  path not found '
Const SE_ERR_SHARE = 26                  ' A sharing violation occurred. '

【讨论】:

    【解决方案3】:

    你有“open”作为动词,不要那样做,使用vbNullString作为动词(“open”表示打开动词,NULL表示默认动词(如果用户没有设置特定的默认值,默认是打开的,如果没有该文件类型的打开动词,ShellExecute 使用它找到的第一个动词))

    【讨论】:

    • 这似乎是一件奇怪的事情。如果用户将默认动词更改为“编辑”或“打印”,结果将不是我们想要的:查看 PDF 文档。
    • 这是他们的选择。它比使用可能不存在的动词更好。如果你想要两全其美,你可以先检查开放动词是否存在,如果不存在,使用 NULL (但这样你真的搞砸了你的用户,因为他们设置了默认值而你忽略了它)
    • 另外,问题以“哪些原因”开头,这是一个正当的理由,所以我不明白你为什么要投票给我。 (我在野外看到过这个问题)
    • 我没有投票给你。其他人一定不喜欢你的答案。我刚刚发表评论表达了我的疑问。
    • @Bob,并不是直接针对你。
    【解决方案4】:

    查看ShellExecute 调用的返回值。来自MSDN

    如果函数成功,则返回大于 32 的值。如果函数失败,则返回指示失败原因的错误值。为了向后兼容 16 位 Windows 应用程序,返回值被转换为 HINSTANCE。然而,这不是一个真正的 HINSTANCE。它只能转换为 int 并与 32 或以下错误代码进行比较。

    0:操作系统内存或资源不足。

    ERROR_FILE_NOT_FOUND: 找不到指定的文件。

    ERROR_PATH_NOT_FOUND: 找不到指定的路径

    (...)

    【讨论】:

    • 我会记录这个值,然后我会知道更多。
    【解决方案5】:

    我没有使用 ShellExecute 来“执行”PDF 文件,而是使用 FindExecutable API:

    Private Const ERROR_FILE_NO_ASSOCIATION     As Long = 31
    Private Const ERROR_FILE_NOT_FOUND          As Long = 2
    Private Const ERROR_PATH_NOT_FOUND          As Long = 3
    Private Const ERROR_FILE_SUCCESS            As Long = 32 
    Private Const ERROR_BAD_FORMAT              As Long = 11
    
    Private Declare Function FindExecutable Lib "shell32.dll" _
       Alias "FindExecutableA" _
      (ByVal lpFile As String, _
       ByVal lpDirectory As String, _
       ByVal sResult As String) As Long
    
    
    Private Sub OpenDocument(sFile as string, sPath as string)
         Dim sResult As String
         Dim lSuccess As Long, lPos as long
    
         sResult = Space$(MAX_PATH)
         lSuccess = FindExecutable(sFile, sPath), sResult)
         Select Case lSuccess
            Case ERROR_FILE_NO_ASSOCIATION
                If Right$(sFile, 3) = "pdf" Then
                    MsgBox "You must have a PDF viewer such as Acrobat Reader to view pdf files."
                Else
                    MsgBox "There is no registered program to open the selected file." & vbCrLf & sFile
                End If
            Case ERROR_FILE_NOT_FOUND: MsgBox "File not found: " & sFile
            Case ERROR_PATH_NOT_FOUND: MsgBox "Path not found: " & sPath
            Case ERROR_BAD_FORMAT:     MsgBox "Bad format."
            Case Is >= ERROR_FILE_SUCCESS:
               lPos = InStr(sResult, Chr$(0))
               If lPos Then sResult = Left$(sResult, lPos - 1)
               Shell sResult & " " & sPath & sFile, True), vbMaximizedFocus
        End Select
    
    End Sub
    

    【讨论】:

    • +1,很有趣。出于好奇,您为什么决定使用 FindExecutable 而不是 ShellExecute?您返回的错误代码看起来非常相似,只是您在实际尝试启动文件之前进行了这些检查吗?您是否对 ShellExecute 有一些使用 FindExecutable 帮助您解决的问题?
    • FindExecutable 会根据给定的文件扩展名查找已注册运行的程序。 ShellExecute 非常适合执行和等待调用(而不仅仅是 Shell),但它似乎使用了更暴力的执行方法,有时会导致错误。我使用vbnet.mvps.org/index.html?code/system/findexecutable.htm 作为我的来源。这是 VB-6 API 使用的绝佳资源。
    【解决方案6】:

    如果你正在使用

    CoInitializeEx(NULL, COINIT_MULTITHREADED)
    

    在你的代码中,那么你将不得不创建一个单独的线程来执行通过 壳牌执行。 在此处查看更多信息:Calling Shell Functions and Interfaces from a Multithreaded Apartment

    【讨论】:

    • 在一个 VB 问题中,我从前 Delphi 开发人员那里得到了 Delphi 答案。干得好。
    【解决方案7】:
    1. 卸载并重新安装 Acrobat Reader。
    2. 在“文档和设置”下,将“用户名”文件夹重命名为“用户名x”(您应该使用不同的管理员用户登录)。
    3. 以用户身份重新登录,它会创建一个带有新用户注册表的新“用户名”文件夹。
    4. 现在它应该可以工作了。

    您可以将文件从 usernamex 文件夹复制到新的用户名文件夹(桌面、文档等)

    【讨论】:

      【解决方案8】:

      我遇到了同样的问题,无法更改 VB6 代码。所以我不得不寻找另一个解决方案......

      在我的例子中,它是一个扩展名为“.xyz”的文件,但实际上它是一个 Microsoft Word 文件,就像一个 .doc 文件。

      第一次双击时,Windows 要求程序打开文件。之后双击工作正常。但是 ShellExecute 没有。问题是,ShellExecute 对文件执行“右键单击”->“打开”,而我的 .xyz 文件的上下文菜单中不存在“打开”。只有一个“编辑”......所以 ShellExecute 使用“编辑”,但没有使用“打开”作为第二个参数。

      因为我无法更改 VB6 代码,所以我用 regedit 打开了注册表。在路径“HKEY_CLASSES_ROOT\.doc”中,标准值是“Word.Document.8”,在“HKEY_CLASSES_ROOT\.xyz”中只有“xyz_auto_file”。所以我把这个值改成了“Word.Document.8”,一切都很完美。现在,当我右键单击我的 .xyz 文件时,我拥有与 .doc 文件相同的上下文菜单。

      而且 ShellExecute 也能完美运行...

      【讨论】:

      【解决方案9】:

      我在调用ShellExecute 函数时使用动词open 而不是NULL 的现有程序遇到了同样的问题。我可以通过使用注册表编辑器将open 动词(如described here)添加到.pdf 处理程序中(我的是HKEY_CLASSES_ROOT\pdf_auto_file)来解决这个问题。我认为这是 Adob​​e Reader 安装程序中的一个问题,有时在安装过程中没有添加 open 动词。

      这是我添加的注册表值的导出:

      [HKEY_CLASSES_ROOT\pdf_auto_file\shell\Open\command]
      @="\"C:\\Program Files\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe\" \"%1\""
      

      【讨论】:

        【解决方案10】:

        在从 W7x64 更新到 W10 公开版本后,我在编译的 Visual Foxpro 9 应用程序中遇到了与 OP 相同的问题。

        我安装了 Adob​​e Acrobat 以及 Adob​​e Reader。将默认的 .pdf 关联从 Reader 更改为 Acrobat 并且...一切正常!更改回 Reader 并出现原始故障(错误代码 31 -“没有与给定文件扩展名关联的应用程序。”)。打败我,但幸运的是我不必担心。我太老了,无暇顾及,将要求所有网站继续使用 W7。

        文件资源管理器中的任何一个关联都有效

        【讨论】:

          【解决方案11】:

          从仅支持 ANSII 的命令调用 Unicode 版本 (ShellExecuteW),体验过最新版本的 Inno Setup。 ShellExecuteW 适用于一些 ANSII 字符串参数,但在这种情况下不是必需的,返回 2(请参阅 below)。
          有趣的是,在 ANSII 或 Unicode 中,Inno 的内部函数 ShellExec 也因代码 5 失败,原因是编译过程仍然有一个打开的文件句柄。

          【讨论】:

            【解决方案12】:

            这是一个将 Windows 错误编号转换为文本的函数。您可以将返回值作为参数,并返回更友好的消息。

            Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" _
                (ByVal dwFlags As Long, lpSource As Long, ByVal dwMessageId As Long, _
                ByVal dwLanguageId As Long, ByVal lpBuffer As String, _
                ByVal nSize As Long, ByVal Arguments As Any) As Long
            
            Private Const FORMAT_MESSAGE_FROM_SYSTEM = &H1000
            Private Const FORMAT_MESSAGE_IGNORE_INSERTS = &H200
            Private Const MAX_PATH = 260
            
            Function TranslateDLLError(ByVal lngErrNum As Long) As String
               Dim sRtrnCode As String * MAX_PATH
               Dim lRet As Long
            
               On Error GoTo errTranslateDLLError(
            
               sRtrnCode = Space$(256)
               lRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, 0&, lngErrNum, 0&, sRtrnCode, Len(sRtrnCode), 0&)
               If lRet > 0 Then
                  Translate_DLL_Error = Replace$(Left(sRtrnCode, lRet), vbCrLf, "")
               Else
                  Translate_DLL_Error = "Error not found."
               End If
            
               Exit Function
            
            errTranslateDLLError(:
               TranslateDLLError( = "Unable to translate system error: " & CStr(lngErrNum)
            
            End Function
            

            【讨论】:

              【解决方案13】:

              试试这个。 您必须将 PDF 文件与任何程序(例如 Acrobat x)关联才能读取 PDF,然后您可以使用 ShellExecute 打开 PDF 文件。

              【讨论】:

              • OP 说它是从资源管理器打开的。它已经关联了。
              猜你喜欢
              • 2018-02-10
              • 1970-01-01
              • 2020-02-22
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-06-03
              • 2021-01-01
              相关资源
              最近更新 更多