I have this script that I used for years which stores emails from outlook in a folder on the hard disk for archiving. It always worked great, but no I changed to Office '21 64bit and I get an error message compiling. I have seen similar questions but I'm a total noob, so the question is, can somebody point me to what needs to be changed. The following lines get flagged:
**Private Declare Function lstrcat Lib "kernel32" Alias "lstrcatA" (ByVal lpString1 As String, ByVal lpString2 As String) As Long
Private Declare Function SHBrowseForFolder Lib "shell32" (lpbi As BrowseInfo) As Long
Private Declare Function SHGetPathFromIDList Lib "shell32" (ByVal pidList As Long, ByVal lpBuffer As String) As Long
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal hMem As Long)**
Any input really much appreciated...
To write code that can work in both new and older versions of Office, you can use a combination of the new VBA7 and Win64 conditional Compiler constants. The Vba7 conditional compiler constant is used to determine if code is running in version 7 of the VB editor (the VBA version that ships in Office 2010). The Win64 conditional compiler constant is used to determine which version (32-bit or 64-bit) of Office is running. For example:
#If Win64 Then
Public Declare PtrSafe Function GetTickCount Lib "Kernel32" Alias "GetTickCount64" () As LongPtr
#Else
Public Declare PtrSafe Function GetTickCount Lib "Kernel32" () As LongPtr
#End If
You may find the following articles helpful:
How to convert Windows API declarations in VBA for 64-bit
64-bit Visual Basic for Applications overview
Compatibility between the 32-bit and 64-bit versions of Office
Related
I would like to have a simple vbs script which runs on every Windows PC without additional apps and tools.
Below is a short script written in AHK:
A text string is extracted from a file and added as resource in an exe file by dllcalls.
Can this be reproduced in a simple way in vbs?
ExeFile = MyExe.exe
ScriptF = Script.txt
FileRead, Script, %ScriptF%
VarSetCapacity(Bin, BinScript_Len := StrPut(Script, "UTF-8") - 1)
StrPut(Script, &BinScript, "UTF-8")
Module := DllCall("BeginUpdateResource", "str", ExeFile, "uint", 0, "ptr")
DllCall("UpdateResource", "ptr", Module, "ptr", 10, "str", ">MY SCRIPT<"
, "ushort", 0x409, "ptr", &BinScript, "uint", BinScript_Len, "uint")
DllCall("EndUpdateResource", "ptr", Module, "uint", 0)
The short answer is No.
VBScript can only call DLLs that are exposed through COM (Component Object Model) and referenced in VBScript via the function CreateObject().
Here is an example of VBScript calling an external library;
Dim dll: Set dll = CreateObject("Scripting.FileSystemObject")
'Call the CreateFolder() method from FileSystemObject object in the Scripting Library.
dll.CreateFolder("C:\test")
Although this is technically a built-in component (part of the Scripting Runtime) it demonstrates the process and is housed in an external library (scrrun.dll).
In this case though as according to the documentation
From AutoHotKey Reference - DllCall()
DllFile may be omitted when calling a function that resides in User32.dll, Kernel32.dll, ComCtl32.dll, or Gdi32.dll. For example, "User32\IsWindowVisible" produces the same result as "IsWindowVisible".
the script is calling Windows System DLL function calls that are unlikely exposed to COM. The only option would be to write a COM DLL wrapper for the Windows System DLLs.
I would like my VB 6 application to detect and display the version of Windows that is running on.
I have tried this code from another Stack Overflow question, but it does not work for me. It displays the correct version number on older versions of Windows (like Windows XP and Vista), but it cannot detect Windows 10. For some reason, it says that Windows 10 is Windows 8.
I thought Windows 10 would have a major version of "10" and a minor version of "0", and this chart of Windows version numbers confirms that it does. Why, then, does the GetVersionEx function never actually return version 10.0?
How can I accurately distinguish between Windows 8, Windows 8.1, and Windows 10?
Why is the old code broken?
The code in that other answer works well for older versions of Windows. Specifically, it handles all the way up to Windows 8 (version 6.2) without a hitch. But as you've noticed, things start to go wrong on Windows 8.1 (version 6.3) and Windows 10 (version 10.0). The code looks like it should work, but it's getting version 6.2 for any version after Windows 8.
The reason for this is that Microsoft has decided to change how Windows reports its version number to applications. In an attempt to prevent old programs from erroneously deciding not to run on these latest versions of Windows, the operating system has "peaked out" its version number at 6.2. While Windows 8.1 and 10 still have internal version numbers of 6.3 and 10.0, respectively, they continue to report their version number as 6.2 to older applications. The idea is, essentially, "you cannot handle the truth", so it will be withheld from you. Under the hood, there are compatibility shims between your application and the system that are responsible for faking the version number whenever you call these API functions.
These particular compatibility shims were first introduced in Windows 8.1, and affected several of the version information retrieval APIs. In Windows 10, the compatibility shims begin to affect nearly all of the ways that a version number can be retrieved, including attempts to read the version number directly from system files.
In fact, these old version information retrieval APIs (like the GetVersionEx function used by that other answer) have been officially "deprecated" by Microsoft. In new code, you are supposed to use the Version Helper functions to determine the underlying version of Windows. But there are two problems with these functions:
There are a whole bunch of them—one to detect every version of Windows, including "point" versions—and they are not exported from any system DLL. Rather, they are inline functions defined in a C/C++ header file distributed with the Windows SDK. This works great for C and C++ programmers, but what is a humble VB 6 programmer to do? You can't call any of these "helper" functions from VB 6.
Even if you could call them from VB 6, Windows 10 extended the reach of the compatibility shims (as I mentioned above), so that even the IsWindows8Point1OrGreater and IsWindows10OrGreater functions will lie to you.
A Compatibility Manifest
The ideal solution, and the one that the linked SDK documentation alludes to, is to embed a manifest in your application's EXE with compatibility information. Manifest files were first introduced in Windows XP as a way of bundling metadata with an application, and the amount of information that can be included in a manifest file has increased with each new version of Windows.
The relevant portion of the manifest file is a section called compatibility. It might look something like this (a manifest is just an XML file that adheres to a specific format):
<!-- 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>
The way it works is each version of Windows (since Vista) has a GUID, and if your manifest includes that GUID as a supportedOS, then the system knows that you wrote the application after that version was released. It is therefore assumed that you are prepared to deal with its breaking changes and new features, so the compatibility shims are not applied to your application. Including, of course, the GetVersionEx function that is used by the original code.
Chances are, if you are a conscientious Windows developer, you are already embedding a manifest in your VB 6 app. You need a manifest to get themed controls (by explicitly opting-in to version 6 of ComCtl32.dll), to prevent UAC virtualization (by requesting only asInvoker privileges), and perhaps even to prevent DPI virtualization (by marking yourself as high-DPI aware). You can find lots of information online about how these and other settings in application manifests work.
If you are already embedding a manifest file in your app, then it is a simple matter of adding the Windows 8.1 and Windows 10 GUIDs to your existing manifest. This will cut through the OS-version lies.
If you are not already embedding a manifest file, then you have some work ahead of you. VB 6 was released several years before manifests had been conceived, and as such, the IDE does not have any built-in facility to deal with them. You have to deal with them yourself. See here for tips on embedding a manifest file in VB 6. The long and short is that they are just text files, so you can create one in Notepad and embed it into your EXE with mt.exe (part of the Windows SDK). There are various possibilities for automating this process, or you can do it manually after completing a build.
An Alternative Solution
If you don't want to fuss with a manifest, there is another solution. It involves only adding code to your VB 6 project and does not need a manifest of any kind to work.
There is another little-known API function that you can call to retrieve the true OS version. It is actually the internal kernel-mode function that the GetVersionEx and VerifyVersionInfo functions call down to. But when you call it directly, you avoid the compatibility shims that would normally be applied, which means that you get the real, unfiltered version information.
This function is called RtlGetVersion, and as the linked documentation suggests, it is a run-time routine intended for use by drivers. But thanks to the magic of VB 6's ability to dynamically call native API functions, we can use it from our application. The following module shows how it might be used:
'==================================================================================
' 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
The intended public interface is a single function called GetActualWindowsVersion, which returns a string containing the name of the actual underlying version of Windows. For example, it might return "Windows Server 2003 SP2 (v5.2.3790)" or "Windows 10 (v10.0.14342)".
(Fully tested and working on Windows 10!)
The module's public function calls a couple of internal helper functions that parse information out of the native RTL_OSVERSIONINFOEXW data structure, simplifying the code slightly. There is even more information available in this structure if you want to take the time to modify the code to extract it. For example, there is a wSuiteMask member that contains flags, whose presence indicate certain features or product types. An example of how this information might be used appears in the GetWinVerName helper function, where the VER_SUITE_PERSONAL flag is checked to see if it is Windows XP Home or Pro.
Final Thoughts
There are several other "solutions" to this problem floating around online. I recommend avoiding these.
One popular suggestion is to try and read the version number from the Registry. This is a terrible idea. The Registry is neither intended as nor documented as a public interface for programs. This means such code is relying on implementation details that are subject to change at any time, leaving you back in a situation of breakage—the very problem we are trying to solve in the first place! There is never an advantage in querying the Registry over calling a documented API function.
Another frequently suggested option is to use WMI to retrieve the OS version information. This is a better idea than the Registry, since it is actually a documented, public interface, but it is still not an ideal solution. For one thing, WMI is a very heavy dependency. Not all systems will have WMI running, so you will need to ensure that it is enabled, or your code will not work. And if this is the only thing you need to use WMI for, it will be very slow because you have to wait for WMI to get up and running first. Furthermore, querying WMI programmatically from VB 6 is difficult. We don't have it as easy as those PowerShell folks! However, if you are using WMI anyway, it would be a handy way to get a human-readable OS version string. You can do this by querying Win32_OperatingSystem.Name.
I've even seen other hacks like reading the version from the process's PEB block! Granted, that is for Delphi, not VB 6, and since there is no inline assembly in VB 6, I'm not even sure if you could come up with a VB 6 equivalent. But even in Delphi, this is a very bad idea because it too relies on implementation details. Just…don't.
As an adjunct to the above manifest solution for GetVersionEx, place the following after the case 6 block for osv.dwVerMajor in Cody's code:
Case 10 'Note: The following works only with updated manifest
Select Case osv.dwVerMinor
Case 0
GetWindowsVersion = "Windows 10/Server 2016"
Case Else
End Select
The word from MSDN: "GetVersionEx may be altered or unavailable for releases after Windows 8.1." is something to watch, however.
To add to Cody's answer: remember, if running from the VB 6 IDE, it will report the compatibility you selected to get VB 6 to run, for example, on Windows 11 from the IDE, it reports:
Windows XP Home Edition SP2 (V5.1.2600)
If I compile and run the executable on the same Windows 11 machine, it reports:
Windows 10 (v10.0.2200)
I have an application which runs on windows 2003, window 2008 and windows small business server.
There is a windows call that I make to reboot the system.All the calls mentioned below come from advapi32.dll
InitiateSystemshutdown - This is fine in windows 2003 and windows 2008 but for some reason not working in Windows aurora
InitiateShutdown - since the above call is not working in windows aurora we used this call and minimum supported OS for this call is windows 2008
Now my application fails to run in windows 2003 since the InitiateShutDown is not present in the advapi32.dll on Windows 2003
I get a failed to find procedure entry point for Initiateshutdown in advapi32.dll
I have already put a condition also so that the proper function calls are called with respect to the windows version.
Is there way to stop looking for the entry point in the dll when the application launches.The condition will make sure that the proper function call are called?
OR
I should be asking Microsoft why the old call InitiateSystemshutdown is not working properly ?
You have to use GetProcAddress and set your preprocessor variables for the earlier version of Windows. See http://msdn.microsoft.com/en-us/library/aa383745(VS.85).aspx#setting_winver_or__win32_winnt
Basically, you should:
Set WINVER to the earliest version of Windows you must support so you don't accidentally use something newer.
Some API calls and definitions won't work anymore (like InitiateShutdown in your case) in your code, because they aren't included by header files. For these, you must use them dynamically. Typically you use the GetProcAddress API and use a "typedef" to define the function's signature (since it isn't in the header files you're including anymore). An example is here: http://msdn.microsoft.com/en-us/library/ms683212(VS.85).aspx. In your case you would use the InitiateShutdown API instead of the given GetNativeSystemInfo. If the call to GetProcAddress fails then you can assume that the API is not supported on that version of Windows.
Rather that explicitly calling that function in your code (such that your app will fail to load if the function can't be loaded from the expected DLL), call it implicitly via LoadLibrary and GetProcAddress.
Do a "LoadLibrary" on advapi32.dll. And then call GetProcAddress for "InitiateShutdown". Fail gracefully if the function doesn't exist, otherwise cast it to an appropriate function pointer and
invoke it.
typedef DWORD (WINAPI *InitiateSystemShutdownTypeA) (char*, char*, DWORD, DWORD, DWORD);
typedef DWORD (WINAPI *InitiateSystemShutdownTypeW) (wchar_t*, wchar_t*, DWORD, DWORD, DWORD);
InitiateShutdownTypeA func = NULL;
HMODULE hMod = LoadLibrary("advapi32.dll");
if (hMod)
func = (InitiateShutdownTypeA)GetProcAddress("InitiateShutdownW");
if (func)
func(pwszMachineName, pwszMessage, dwGracePeriod, dwFlags, dwReason);
I want to detect any Windows versions from 95 to Win 7.
I also would like to display if the OS is 32-bit or 64-bit.
That's it; it's that simple. :) What code could I use to do this from within a VB 6 application?
Update: For code that correctly detects Windows 8.1 and Windows 10, see this answer.
The code below still works fine for older versions of Windows, but it will report anything newer than Windows 8 as being Windows 8.
The "bitness" testing code shown at the bottom (to see if the OS is 32-bit or 64-bit still works, even on Windows 10.
The following code will return a string value indicating the current version of Windows. Basically, all it's doing is getting the system version numbers from Windows using the GetVersionEx API function, and then matching those up to the known versions of Windows.
(Note that some things are not detected perfectly. For example, a 64-bit version of Windows XP would likely be reported as Server 2003. Code to determine whether the user is running Windows Vista or Server 2008, for example, has also not been written. But you can take this and tweak it as desired.)
Option Explicit
Private Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" _
(lpVersionInformation As OSVERSIONINFO) As Long
Private Type OSVERSIONINFO
OSVSize As Long
dwVerMajor As Long
dwVerMinor As Long
dwBuildNumber As Long
PlatformID As Long
szCSDVersion As String * 128
End Type
Private Const VER_PLATFORM_WIN32s = 0
Private Const VER_PLATFORM_WIN32_WINDOWS = 1
Private Const VER_PLATFORM_WIN32_NT = 2
' Returns the version of Windows that the user is running
Public Function GetWindowsVersion() As String
Dim osv As OSVERSIONINFO
osv.OSVSize = Len(osv)
If GetVersionEx(osv) = 1 Then
Select Case osv.PlatformID
Case VER_PLATFORM_WIN32s
GetWindowsVersion = "Win32s on Windows 3.1"
Case VER_PLATFORM_WIN32_NT
GetWindowsVersion = "Windows NT"
Select Case osv.dwVerMajor
Case 3
GetWindowsVersion = "Windows NT 3.5"
Case 4
GetWindowsVersion = "Windows NT 4.0"
Case 5
Select Case osv.dwVerMinor
Case 0
GetWindowsVersion = "Windows 2000"
Case 1
GetWindowsVersion = "Windows XP"
Case 2
GetWindowsVersion = "Windows Server 2003"
End Select
Case 6
Select Case osv.dwVerMinor
Case 0
GetWindowsVersion = "Windows Vista/Server 2008"
Case 1
GetWindowsVersion = "Windows 7/Server 2008 R2"
Case 2
GetWindowsVersion = "Windows 8/Server 2012"
Case 3
GetWindowsVersion = "Windows 8.1/Server 2012 R2"
End Select
End Select
Case VER_PLATFORM_WIN32_WINDOWS:
Select Case osv.dwVerMinor
Case 0
GetWindowsVersion = "Windows 95"
Case 90
GetWindowsVersion = "Windows Me"
Case Else
GetWindowsVersion = "Windows 98"
End Select
End Select
Else
GetWindowsVersion = "Unable to identify your version of Windows."
End If
End Function
Additionally, if you don't need to target the earliest versions of Windows, you can get more information by passing the OSVERSIONINFOEX structure instead. I just wrote that code in C++, and the documentation is surprisingly easy to follow.
Determining if the host OS is 32-bit or 64-bit from a VB 6 executable is a little trickier. The reason is because VB 6 can't compile 64-bit applications. Everything you write in VB 6 will run as a 32-bit application. And 32-bit applications run on 64-bit versions of Windows in the Windows-on-Windows (WOW64) subsystem. They will always report the current version of Windows as 32-bit, because that's what they see.
We can work around this by initially assuming that the host OS is 32-bit, and attempting to prove this wrong. Here's some sample code:
Private Declare Function GetProcAddress Lib "kernel32" _
(ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function GetModuleHandle Lib "kernel32" _
Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetCurrentProcess Lib "kernel32" () As Long
Private Declare Function IsWow64Process Lib "kernel32" _
(ByVal hProc As Long, ByRef bWow64Process As Boolean) As Long
Public Function IsHost64Bit() As Boolean
Dim handle As Long
Dim is64Bit As Boolean
' Assume initially that this is not a WOW64 process
is64Bit = False
' Then try to prove that wrong by attempting to load the
' IsWow64Process function dynamically
handle = GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process")
' The function exists, so call it
If handle <> 0 Then
IsWow64Process GetCurrentProcess(), is64Bit
End If
' Return the value
IsHost64Bit = is64Bit
End Function
There's also the WMI Tasks for Operating Systems.
strComputer = "."
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery ("Select * from Win32_OperatingSystem")
For Each objOperatingSystem in colOperatingSystems
Wscript.Echo objOperatingSystem.Caption & " " & objOperatingSystem.Version
Next
You can do something similar to the case statements provided by Cody Gray above to parse the Version value, or parse the plain text Caption value, which has listings like Microsoft(R) Windows(R) Server 2003, Standard Edition and Microsoft Windows 7 Professional.
You could try using the Microsoft Sysinfo control that comes with VB6 and check for OSPlatform, OSBuild and OSVersion propertys to match with the proper OS Version #
The accepted answer worked for my application until I tried it on Windows 10. Even after updating the code for version number details as listed here it reported the wrong Windows version. It turns out this is because:
Applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2). Once an application is manifested for a given operating system version, GetVersionEx will always return the version that the application is manifested for in future releases. To manifest your applications for Windows 8.1 or Windows 10, refer to Targeting your application for Windows.
So in order to get the correct Windows version to show up, it amounts to adding a section the application manifest:
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
And then the GetVersionInfo API works as expected. This manifest section was new as of Windows 7, I believe.
However, a very important caveat is that you must actually have tested your application on each operating system version that you list it as being compatible with. These settings affect certain Windows functions, not only the way Windows version information is reported.
Here is a very simple method I use to determine 32 vs. 64 bit operating system:
OSBits = IIf(Len(Environ$("PROGRAMFILES(X86)")) > 0, 64, 32)
In 64-bit Windows, the OS sets the environment variable "PROGRAMFILES(X86)" but it doesn't on 32-bit systems. It hasn't failed me yet...
Ah, found it! I don't personally use this class because for my needs it's overkill, but it's definitely the most thorough OpSys version example I've come across. Credit for this one goes to Kenneth Ives.
*I guess StackOverflow doesn't like enormous blocks of code, so the class (clsOperSystem.cls) is located in the KiCrypt Demo, an excellent compilation of hash and encryption algorithms.
WORK on WINDOWS 10
VB6 - not work in debug mode - work only on runtime
Private Declare Function RtlGetVersion Lib "ntdll" (ByRef lpVersionInformation As RTL_OSVERSIONINFOEX) As Long
Private Type RTL_OSVERSIONINFOEX
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
szCSDVersion As String * 128
End Type
call
Dim lpVersionInformation As RTL_OSVERSIONINFOEX
lpVersionInformation.dwOSVersionInfoSize = Len(lpVersionInformation)
RtlGetVersion(lpVersionInformation)
I've noticed that RTLMoveMemory seems to work just fine. But when I try to use RTLCopyMemory I get: "Can't find DLL entry point RtlCopyMemory in kernel32". Here is my declare:
Private Declare Sub CopyMem Lib "kernel32" Alias "RtlCopyMemory" ( _
ByVal dest As Long, _
ByVal source As Long, _
ByVal bytLen As Long)
RtlCopyMemory is provided inline. It is defined in winnt.h as memcpy. This means that it's not included in a Win32 DLL, it's part of the C runtime library. You could try importing memcpy from c:\windows\system32\msvcrt.dll.
Why not just use RtlMoveMemory? It works just like RtlCopyMemory except that it handles overlapped memory in a different fashion.
Bruce McKinney pioneered the use of RtlMoveMemory over 10 years ago and it's been standard for VB6 memory copying ever since.
I know this is an old question, but I had the same problem, so I thought I could answer.
RtlCopyMemory in kernel32.dll should be an Export Forward into ntdll,
but somehow guys at MS missed that one on x64 version of Vista (dunno how it's on x86) (see below).
You can try importing it directly from ntdll, if it's only for your needs.
EDIT: the method I mean is not exported, but with Symbols it's visible in ntdll as RtlCopyMemoryNonTemporal
EDIT2: Just to be sure I've checked some things, here's summary:
both RtlCopyMemory and RtlCopyMemoryNonTemporal are exported from ntdll.dll in x64 Vista's (plain, SP1, SP2)
there is export forward for RtlCopyMemory in kernel32.dll in x64 Vista's
there is unexported RtlCopyMemoryNonTemporal in ntdll.dll in x86 Vista's
So it all should be if you're writing x64 application.
P.S. I was wrong about x64 vs x86, because I was compiling x86 app, and running it on x64 (WOW mode),
so it used 32-bit version of kernel32, ntdll and not the x64 one.