VB 6应用程序如何确定它是否在Windows 10上运行?

科迪·格雷

我希望我的VB 6应用程序能够检测并显示正在运行的Windows版本。

我曾尝试这个代码另一个堆栈溢出的问题,但它并没有为我工作。它在较旧版本的Windows(例如Windows XP和Vista)上显示正确的版本号,但无法检测到Windows10。出于某种原因,它表示Windows 10是Windows 8。

我以为Windows 10的主要版本为“ 10”,次要版本为“ 0”,并且此Windows版本号图表证实了这一点。那么,为什么GetVersionEx函数实际上从不返回10.0版本呢?

如何准确区分Windows 8,Windows 8.1和Windows 10?

科迪·格雷

为什么旧代码坏了?

其他答案中的代码适用于Windows的较早版本。特别是,它可以处理Windows 8(6.2版)之前的所有问题。但是,您已经注意到,Windows 8.1(6.3版)和Windows 10(10.0版)开始出现问题。该代码看起来应该可以工作,但是对于Windows 8之后的任何版本,它的版本都为6.2。

原因是Microsoft已决定更改Windows向应用程序报告其版本号的方式。为了防止旧程序错误地决定不在这些最新版本的Windows上运行,操作系统已“大声疾呼”其版本号为6.2。尽管Windows 8.1和10的内部版本号分别为6.3和10.0,但它们继续向较旧的应用程序报告其版本号为6.2。这个想法本质上是“您无法处理真相”,因此它将被您拒之门外。后台,在您调用这些API函数时,您的应用程序与系统之间存在兼容性伪装,这些伪造负责伪造版本号。

这些特殊的兼容性垫片最初是在Windows 8.1中引入的,并且影响了多个版本信息检索API。在Windows 10中,兼容性填充会开始影响几乎所有可以检索版本号的方式,包括尝试直接从系统文件中读取版本号的方法。

实际上,这些旧版本的信息检索API(例如该GetVersionEx其他答案使用功能)已被Microsoft正式“弃用”。在新代码中,应该使用Version Helper函数来确定Windows的基础版本。但是这些功能有两个问题:

  1. 其中有很多(可检测Windows的每个版本,包括“点”版本),并且它们不会从任何系统DLL中导出。而是它们是随Windows SDK一起分发的C / C ++头文件中定义的内联函数。这对于C和C ++程序员来说非常有用,但是谦虚的VB 6程序员应该做什么呢?您不能从VB 6中调用任何这些“帮助器”功能。

  2. 即使您可以从VB 6调用它们,Windows 10也会扩展兼容性垫片的范围(如上所述),因此,甚至IsWindows8Point1OrGreaterandIsWindows10OrGreater函数也将对您不利。

兼容性清单

理想的解决方案,以及一个链接的SDK文档暗指,被嵌入到应用程序的EXE清单与兼容性信息。清单文件最初是作为将元数据与应用程序捆绑在一起的一种方式在Windows XP中引入的,清单文件中可包含的信息量随着Windows的每个新版本的增加而增加。

清单文件的相关部分是一个名为的部分compatibility它可能看起来像这样(清单只是遵循特定格式的XML文件):

<!-- Declare support for various versions of Windows -->
<ms_compatibility:compatibility xmlns:ms_compatibility="urn:schemas-microsoft-com:compatibility.v1" xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <ms_compatibility:application>
    <!-- Windows Vista/Server 2008 -->
    <ms_compatibility:supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
    <!-- Windows 7/Server 2008 R2 -->
    <ms_compatibility:supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
    <!-- Windows 8/Server 2012 -->
    <ms_compatibility:supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
    <!-- Windows 8.1/Server 2012 R2 -->
    <ms_compatibility:supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
    <!-- Windows 10 -->
    <ms_compatibility:supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
  </ms_compatibility:application>
</ms_compatibility:compatibility>

它的工作方式是Windows的每个版本(自Vista开始)都具有GUID,并且如果清单包含该GUID作为supportedOS,则系统会知道您在发布该版本编写了该应用程序因此,假定您已准备好应对其重大更改和新功能,因此兼容性垫片不会应用于您的应用程序。当然,包括原始代码GetVersionEx使用功能

如果您是一个认真的Windows开发人员,则很有可能已经在VB 6应用程序中嵌入了清单。您需要清单来获取主题控件(通过显式选择加入ComCtl32.dll版本6),防止UAC虚拟化(仅请求asInvoker特权),甚至防止DPI虚拟化(通过将自己标记为具有高DPI意识) )。您可以在线找到许多有关应用程序清单中的这些设置和其他设置如何工作的信息。

如果您已经在应用程序中嵌入了清单文件,则只需将Windows 8.1和Windows 10 GUID添加到现有清单中即可。这将切入OS版本的谎言。

如果尚未嵌入清单文件,那么您需要做一些工作。VB 6是在构思清单之前几年发布的,因此,IDE没有任何内置功能来处理它们。您必须自己处理。有关在VB 6中嵌入清单文件的提示,请参见此处总而言之,它们只是文本文件,因此您可以在记事本中创建一个文件,然后使用mt.exeWindows SDK的一部分将其嵌入到您的EXE中有多种方法可以使此过程自动化,或者您可以在完成构建后手动进行。

替代解决方案

如果您不想大惊小怪的清单,还有另一种解决方案。它仅涉及将代码添加到VB 6项目中,不需要任何清单即可工作。

您可以调用另一个鲜为人知的API函数来检索真实的OS版本。GetVersionExVerifyVersionInfo函数实际上是内部内核模式函数但是,当直接调用它时,避免了通常会应用的兼容性填充,这意味着您将获得真实的,未经过滤的版本信息。

将该函数称为RtlGetVersion,并且如链接文档所建议的那样,它是供驱动程序使用的运行时例程。但是,由于VB 6具有动态调用本机API函数的神奇功能,因此我们可以在应用程序中使用它。以下模块显示了如何使用它:

'==================================================================================
' RealWinVer.bas     by Cody Gray, 2016
' 
' (Freely available for use and modification, provided that credit is given to the
' original author. Including a comment in the code with my name and/or a link to
' this Stack Overflow answer is sufficient.)
'==================================================================================

Option Explicit

''''''''''''''''''''''''''''''''''''''''''''''''''
' Windows SDK Constants, Types, & Functions
''''''''''''''''''''''''''''''''''''''''''''''''''

Private Const cbCSDVersion As Long = 128 * 2

Private Const STATUS_SUCCESS As Long = 0

Private Const VER_PLATFORM_WIN32s As Long        = 0
Private Const VER_PLATFORM_WIN32_WINDOWS As Long = 1
Private Const VER_PLATFORM_WIN32_NT As Long      = 2

Private Const VER_NT_WORKSTATION As Byte       = 1
Private Const VER_NT_DOMAIN_CONTROLLER As Byte = 2
Private Const VER_NT_SERVER As Byte            = 3

Private Const VER_SUITE_PERSONAL As Integer = &H200

Private Type RTL_OSVERSIONINFOEXW
   dwOSVersionInfoSize As Long
   dwMajorVersion      As Long
   dwMinorVersion      As Long
   dwBuildNumber       As Long
   dwPlatformId        As Long
   szCSDVersion        As String * cbCSDVersion
   wServicePackMajor   As Integer
   wServicePackMinor   As Integer
   wSuiteMask          As Integer
   wProductType        As Byte
   wReserved           As Byte
End Type

Private Declare Function RtlGetVersion Lib "ntdll" _
    (lpVersionInformation As RTL_OSVERSIONINFOEXW) As Long


''''''''''''''''''''''''''''''''''''''''''''''''''
' Internal Helper Functions
''''''''''''''''''''''''''''''''''''''''''''''''''

Private Function IsWinServerVersion(ByRef ver As RTL_OSVERSIONINFOEXW) As Boolean
   ' There are three documented values for "wProductType".
   ' Two of the values mean that the OS is a server versions,
   ' while the other value signifies a home/workstation version.
   Debug.Assert ver.wProductType = VER_NT_WORKSTATION Or _
                ver.wProductType = VER_NT_DOMAIN_CONTROLLER Or _
                ver.wProductType = VER_NT_SERVER

   IsWinServerVersion = (ver.wProductType <> VER_NT_WORKSTATION)
End Function

Private Function GetWinVerNumber(ByRef ver As RTL_OSVERSIONINFOEXW) As String
   Debug.Assert ver.dwPlatformId = VER_PLATFORM_WIN32_NT

   GetWinVerNumber = ver.dwMajorVersion & "." & _
                     ver.dwMinorVersion & "." & _
                     ver.dwBuildNumber
End Function

Private Function GetWinSPVerNumber(ByRef ver As RTL_OSVERSIONINFOEXW) As String
   Debug.Assert ver.dwPlatformId = VER_PLATFORM_WIN32_NT

   If (ver.wServicePackMajor > 0) Then
      If (ver.wServicePackMinor > 0) Then
         GetWinSPVerNumber = "SP" & CStr(ver.wServicePackMajor) & "." & CStr(ver.wServicePackMinor)
         Exit Function
      Else
         GetWinSPVerNumber = "SP" & CStr(ver.wServicePackMajor)
         Exit Function
      End If
   End If
End Function

Private Function GetWinVerName(ByRef ver As RTL_OSVERSIONINFOEXW) As String
   Debug.Assert ver.dwPlatformId = VER_PLATFORM_WIN32_NT

   Select Case ver.dwMajorVersion
      Case 3
         If IsWinServerVersion(ver) Then
            GetWinVerName = "Windows NT 3.5 Server"
            Exit Function
         Else
            GetWinVerName = "Windows NT 3.5 Workstation"
            Exit Function
         End If
      Case 4
         If IsWinServerVersion(ver) Then
            GetWinVerName = "Windows NT 4.0 Server"
            Exit Function
         Else
            GetWinVerName = "Windows NT 4.0 Workstation"
            Exit Function
         End If
      Case 5
         Select Case ver.dwMinorVersion
            Case 0
               If IsWinServerVersion(ver) Then
                  GetWinVerName = "Windows 2000 Server"
                  Exit Function
               Else
                  GetWinVerName = "Windows 2000 Workstation"
                  Exit Function
               End If
            Case 1
               If (ver.wSuiteMask And VER_SUITE_PERSONAL) Then
                  GetWinVerName = "Windows XP Home Edition"
                  Exit Function
               Else
                  GetWinVerName = "Windows XP Professional"
                  Exit Function
               End If
            Case 2
               If IsWinServerVersion(ver) Then
                  GetWinVerName = "Windows Server 2003"
                  Exit Function
               Else
                  GetWinVerName = "Windows XP 64-bit Edition"
                  Exit Function
               End If
            Case Else
               Debug.Assert False
         End Select
      Case 6
         Select Case ver.dwMinorVersion
            Case 0
               If IsWinServerVersion(ver) Then
                  GetWinVerName = "Windows Server 2008"
                  Exit Function
               Else
                  GetWinVerName = "Windows Vista"
                  Exit Function
               End If
            Case 1
               If IsWinServerVersion(ver) Then
                  GetWinVerName = "Windows Server 2008 R2"
                  Exit Function
               Else
                  GetWinVerName = "Windows 7"
                  Exit Function
               End If
            Case 2
               If IsWinServerVersion(ver) Then
                  GetWinVerName = "Windows Server 2012"
                  Exit Function
               Else
                  GetWinVerName = "Windows 8"
                  Exit Function
               End If
            Case 3
               If IsWinServerVersion(ver) Then
                  GetWinVerName = "Windows Server 2012 R2"
                  Exit Function
               Else
                  GetWinVerName = "Windows 8.1"
                  Exit Function
               End If
            Case Else
               Debug.Assert False
         End Select
      Case 10
         If IsWinServerVersion(ver) Then
            GetWinVerName = "Windows Server 2016"
            Exit Function
         Else
            GetWinVerName = "Windows 10"
            Exit Function
         End If
      Case Else
         Debug.Assert False
   End Select

   GetWinVerName = "Unrecognized Version"
End Function


''''''''''''''''''''''''''''''''''''''''''''''''''
' Public Functions
''''''''''''''''''''''''''''''''''''''''''''''''''

' Returns a string that contains the name of the underlying version of Windows,
' the major version of the most recently installed service pack, and the actual
' version number (in "Major.Minor.Build" format).
'
' For example: "Windows Server 2003 SP2 (v5.2.3790)" or
'              "Windows 10 (v10.0.14342)"
'
' This function returns the *real* Windows version, and works correctly on all
' operating systems, including Windows 10, regardless of whether or not the
' application includes a manifest. It calls the native NT version-info function
' directly in order to bypass compatibility shims that would otherwise lie to
' you about the real version number.
Public Function GetActualWindowsVersion() As String
   Dim ver As RTL_OSVERSIONINFOEXW
   ver.dwOSVersionInfoSize = Len(ver)

   If (RtlGetVersion(ver) <> STATUS_SUCCESS) Then
      GetActualWindowsVersion = "Failed to retrieve Windows version"
   End If

   ' The following version-parsing logic assumes that the operating system
   ' is some version of Windows NT. This assumption will be true if you
   ' are running any version of Windows released in the past 15 years,
   ' including several that were released before that.
   Debug.Assert ver.dwPlatformId = VER_PLATFORM_WIN32_NT

   GetActualWindowsVersion = GetWinVerName(ver) & " " & GetWinSPVerNumber(ver) & _
                             " (v" & GetWinVerNumber(ver) & ")"
End Function

预期的公共接口是一个名为的函数GetActualWindowsVersion,该函数返回一个字符串,其中包含Windows实际基础版本的名称例如,它可能会返回“ Windows Server 2003 SP2(v5.2.3790)”“ Windows 10(v10.0.14342)”
(经过全面测试并可以在Windows 10上使用!)

该模块的公共函数调用了一些内部帮助器函数,这些函数从本地RTL_OSVERSIONINFOEXW数据结构中解析出信息,从而略微简化了代码。如果您想花一些时间来修改代码以提取代码,则在此结构中甚至可以找到更多信息。例如,有一个wSuiteMask成员包含标志,这些标志的存在指示某些功能或产品类型。GetWinVerName帮助功能中将显示一个如何使用此信息的示例,在该功能中,VER_SUITE_PERSONAL检查标志以查看它是Windows XP Home还是Pro。

最后的想法

在线上还存在针对此问题的其他几种“解决方案”。我建议避免这些。

一种流行的建议是尝试从注册表中读取版本号。这是一个可怕的主意。该注册表既不作为程序的公共接口,也不作为文件的公共接口。这意味着此类代码依赖于随时更改的实现细节,从而使您陷入崩溃的境地,而这正是我们首先要解决的问题!查询注册表比调用已记录的API函数从来没有任何优势。

另一个经常建议的选项是使用WMI检索操作系统版本信息。这是比注册表更好的主意,因为它实际上是一个文档化的公共接口,但是仍然不是理想的解决方案。一方面,WMI是一个非常严重的依赖项。并非所有系统都将运行WMI,因此您需要确保已启用WMI,否则您的代码将无法工作。而且,如果这是您唯一需要使用WMI的东西,它将非常慢,因为您必须等待WMI首先启动并运行。此外,很难从VB 6以编程方式查询WMI。我们没有像PowerShell的那些人那么容易!但是,无论如何,如果使用WMI,这将是获取人类可读的OS版本字符串的便捷方法。您可以通过查询来做到这一点Win32_OperatingSystem.Name

我什至看过其他技巧,例如从流程的PEB块中读取版本当然,那是给Delphi的,而不是VB 6的,而且由于VB 6中没有内联程序集,我什至不确定是否可以提出与VB 6等效的产品。但是即使在Delphi中,这也是一个非常糟糕的主意,因为它也过于依赖实现细节。只是……不。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何确定应用程序是否正在库存ROM上运行

来自分类Dev

如何确定特定NSScreen上是否正在运行全屏应用程序

来自分类Dev

我的应用程序如何知道它是否在Chrome中运行,还是在Android设备上运行?

来自分类Dev

如何确定应用程序是否在Android M上运行(开发人员预览)

来自分类Dev

如何检测我的应用程序是否在Windows 10上运行

来自分类Dev

如何在Windows 10企业版上以外壳替换形式运行应用程序

来自分类Dev

VB6应用程序能否在Windows 10上运行

来自分类Dev

如何在Windows 10 IoT上的树莓派2上运行空白应用程序

来自分类Dev

在Windows 10上运行dot net 3.5应用程序

来自分类Dev

Windows 10上的VB6通用控件

来自分类Dev

使用advapi32.dll cryptverifySignature的VB代码可在Vista SP2上运行,而不能在Windows 10 64位上运行

来自分类Dev

在Windows Phone 10上运行Ionic 2应用程序

来自分类Dev

在Windows 10上安装VB6应用程序时缺少DLL

来自分类Dev

是否可以在VB.NET中的Raspbian上运行服务?

来自分类Dev

如何确定play应用程序是否在heroku的工人dyno上运行?

来自分类Dev

发布Vb.net10应用程序

来自分类Dev

带有ComCtl32.ocx ListView的VB6表单导致Windows 8上的应用程序崩溃

来自分类Dev

如何确定在特定的NSScreen上是否正在运行全屏应用程序

来自分类Dev

使vb.net应用程序能够在安装了.net的任何PC上运行

来自分类Dev

在不安装或注册dll的情况下运行VB6应用程序

来自分类Dev

VB6-如何从作为服务运行的应用程序中打开文件

来自分类Dev

如何检测我的应用程序是否在Windows 10上运行

来自分类Dev

通用Windows 10应用程序。如何使用后退按钮?VB.NET

来自分类Dev

VB.net-Openfilepicker在Windows Mobile 10应用程序中不起作用

来自分类Dev

使用advapi32.dll cryptverifySignature的VB代码可在Vista SP2上运行,而不能在Windows 10 64位上运行

来自分类Dev

Windows 10上VB6应用的兼容性问题

来自分类Dev

如何在用户无法运行的情况下从计划任务运行 VB6 应用程序

来自分类Dev

在 vb6 上运行 .net 2.0 dll 的问题

来自分类Dev

在 Windows 10 上开发 Django 应用程序并在 Ubuntu 上运行生产服务器是否危险?

Related 相关文章

  1. 1

    如何确定应用程序是否正在库存ROM上运行

  2. 2

    如何确定特定NSScreen上是否正在运行全屏应用程序

  3. 3

    我的应用程序如何知道它是否在Chrome中运行,还是在Android设备上运行?

  4. 4

    如何确定应用程序是否在Android M上运行(开发人员预览)

  5. 5

    如何检测我的应用程序是否在Windows 10上运行

  6. 6

    如何在Windows 10企业版上以外壳替换形式运行应用程序

  7. 7

    VB6应用程序能否在Windows 10上运行

  8. 8

    如何在Windows 10 IoT上的树莓派2上运行空白应用程序

  9. 9

    在Windows 10上运行dot net 3.5应用程序

  10. 10

    Windows 10上的VB6通用控件

  11. 11

    使用advapi32.dll cryptverifySignature的VB代码可在Vista SP2上运行,而不能在Windows 10 64位上运行

  12. 12

    在Windows Phone 10上运行Ionic 2应用程序

  13. 13

    在Windows 10上安装VB6应用程序时缺少DLL

  14. 14

    是否可以在VB.NET中的Raspbian上运行服务?

  15. 15

    如何确定play应用程序是否在heroku的工人dyno上运行?

  16. 16

    发布Vb.net10应用程序

  17. 17

    带有ComCtl32.ocx ListView的VB6表单导致Windows 8上的应用程序崩溃

  18. 18

    如何确定在特定的NSScreen上是否正在运行全屏应用程序

  19. 19

    使vb.net应用程序能够在安装了.net的任何PC上运行

  20. 20

    在不安装或注册dll的情况下运行VB6应用程序

  21. 21

    VB6-如何从作为服务运行的应用程序中打开文件

  22. 22

    如何检测我的应用程序是否在Windows 10上运行

  23. 23

    通用Windows 10应用程序。如何使用后退按钮?VB.NET

  24. 24

    VB.net-Openfilepicker在Windows Mobile 10应用程序中不起作用

  25. 25

    使用advapi32.dll cryptverifySignature的VB代码可在Vista SP2上运行,而不能在Windows 10 64位上运行

  26. 26

    Windows 10上VB6应用的兼容性问题

  27. 27

    如何在用户无法运行的情况下从计划任务运行 VB6 应用程序

  28. 28

    在 vb6 上运行 .net 2.0 dll 的问题

  29. 29

    在 Windows 10 上开发 Django 应用程序并在 Ubuntu 上运行生产服务器是否危险?

热门标签

归档