Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print Operating System version to log #5870

Merged
merged 1 commit into from Apr 25, 2019
Merged

Conversation

MSuih
Copy link
Member

@MSuih MSuih commented Apr 20, 2019

The idea behind this is to get some more data about the operating system RPCS3 is ran on, so that issues like #5621 and #5799 we could potentially determine which system updates are causing issues. I also added an implementation for Linux/BSD/Mac, maybe that will prove useful some day. Also includes a check for compatibility modes, in case someone decides to use one for any reason.

@JohnHolmesII
Copy link
Contributor

Have you looked into this? It seems like using a manifest, even tho it would work, is still deprecated. Getting the string from the kernel seems like plenty sufficient for what we need in rpcs3.

"To obtain the full version number for the operating system, call the GetFileVersionInfo function on one of the system DLLs, such as Kernel32.dll, then call VerQueryValue to obtain the \StringFileInfo\\ProductVersion subblock of the file version information."

@MSuih
Copy link
Member Author

MSuih commented Apr 23, 2019

The GetVersionEx function is deprecated because developers used it to check whether the operating system would support certain feature ("it's Win10 or newer, it must support X"), and Microsoft hated that as it wants to reserve right to introduce or remove OS features with any update. You can ignore the warning and use that function regardless as long as you know the limitations it has, it's not going to be removed afaik.

And thanks for that tip, I'll check it out once I got some time.

@danilaml
Copy link
Contributor

@MSuih Does rpcs3 manifest exes? If not, GetVersionEx would return wrong version.

@MSuih
Copy link
Member Author

MSuih commented Apr 23, 2019

Nope, but if someone were kind enough to create one I woudn't have to juggle through this mess of a C API just to get a version identifier out of it. Sheesh, 3 functions that each require an error check and a bunch of temporary variables just for one version string.

@ZeeWanderer
Copy link
Contributor

ZeeWanderer commented Apr 23, 2019

Actually it looks like manifest is embedded in rpcs3.exe as a resource by default.
rpcs3->Properties->Manifest Tool->Input and Output.
But after extracting manifest from exe i got extracted.exe.manifest.txt (.txt appended to not zip it into archive) which does not contain any OS compatibility info.

EDIT: Added manifest.zip to rpcs3->Properties->Manifest Tool->Input and Output->Additional manifest files.

DxDiag Os string: Windows 10 Education 64-bit (10.0, Build 17763) (17763.rs5_release.180914-1434)
Log without additional manifest: RPCS3_no_cmp_manifest.log.gz
Log with additional manifest: RPCS3_with_cmp_manifest.log.gz
Don't know if i have written manifest properly but it works for me. Maybe it will be sufficient for this pr.

@MSuih
Copy link
Member Author

MSuih commented Apr 23, 2019

Damn, was it really that easy? I thought I'd have to modify the .rc file and do all sorts of other changes. Thank you man, I got it to compile on my end.

Since I don't have Visual Studio installed I'll push my current changes and see tomorrow how MSVC reacts to this when compiling with the solution files. It probably still needs some tinkering so that compiler will use the manifest file properly.

@ZeeWanderer
Copy link
Contributor

ZeeWanderer commented Apr 23, 2019

@MSuih for MSVC you need to add additional manifest file trough properties. Here is redacted rpcs3_vcxproj.zip vcxproj file.

@ZeeWanderer
Copy link
Contributor

ZeeWanderer commented Apr 23, 2019

I think I found a more lightweight solution and it does not require manifest.

const DWORD peb_offset = 0x60; // PEB64 offset in GS register
const INT_PTR PPEB     = __readgsqword(peb_offset);

const DWORD OSMajorVersion  = *reinterpret_cast<const DWORD*>(PPEB + 0x118); //	0x118
const DWORD OSMinorVersion  = *reinterpret_cast<const DWORD*>(PPEB + 0x11c); //	0x11c
const WORD OSBuildNumber    = *reinterpret_cast<const WORD*>(PPEB + 0x120);  //	0x120
const WORD OSCSDVersion     = *reinterpret_cast<const WORD*>(PPEB + 0x122);  //	0x120

fmt::append(output, "Operating system: Windows, Major: %d, Minor: %d, Build: %d, SpVersion: %d", OSMajorVersion, OSMinorVersion, OSBuildNumber, OSCSDVersion);

struct PEB64
PEB table

Used PEB members are supported since first WinXP version so unless someone manages to run rpcs3 on something older this will always get the correct version.

Also as a bonus, this will return correct version of windows even when run in compatibility mode while GetVersionEx will return target of compatibility version.

RPCS3_from_PEB.log.gz

EDIT: I am not sure how to interpret service pack version member OSCSDVersion since GetVersionEx returns Major and Minor version for SP. Considering SP version numbers are usually low, first BYTE can be Major and second Minor. Needs to be tested on Windows version with SP.

@JohnHolmesII
Copy link
Contributor

That looks very promising! I do wonder about that last paragraph tho. The goal here is to know what users are doing exactly, so I don't think hiding them running compat mode should help. We would want to know. So as long as it produces the true winver AND tells whether or not compat mode for the app is running, it seems quite good to me.

@JohnHolmesII
Copy link
Contributor

Also, the current version with the updated manifest still doesn't report the right version on win10.

@MSuih
Copy link
Member Author

MSuih commented Apr 24, 2019

The manifest itself is working, it's just that it's only recognized if you're compiling by using cmake which requires an slightly uncommon development environment. (Or maybe you're using some other compiler which doesn't add the manifest to the exe automatically?) I'll look into the solution file once I have some time, that should get it to work for most people.

As for the PEB stuff, I still prefer my current implementation over that. But I'm thinking about adding it as an additional check and comparing the two values to detect compatibility mode or missing manifest file.

@ZeeWanderer
Copy link
Contributor

ZeeWanderer commented Apr 24, 2019

typedef unsigned __int64 QWORD;
...
const QWORD AppCompatFlags     = *reinterpret_cast<const QWORD*>(PPEB + 0x02C8); //	0x02C8
const QWORD AppCompatFlagsUser = *reinterpret_cast<const QWORD*>(PPEB + 0x02D0); //	0x02D0

Either can be used to determine if in compat mode. If any flags are set then compat mode is enabled.

I tested for all modes available for this executable (Vista, Vista SP1, Vista SP2, 7, 8). AppCompatFlagsUser: 19 and 28 bits are always set for any tested mode. AppCompatFlags: 22 bit is always set for any tested mode. Both are 0 when compat mode is disabled.

P.S. Anyone knows where is QWORD define? It is apparently not defined when including <windows.h> wich is a bit strange since there is official doc for it.

EDIT: It does not matter which member to use for compat but AppCompatFlagsUser is for user mode and the other is for kernel mode so it would be more logical to use user mode one.

@MSuih
Copy link
Member Author

MSuih commented Apr 25, 2019

Still not done, but it's fairly close now. I decided to use the PEB version in the end, seems like it provides everything we need and doesn't require the use of depricated functionality.

I don't know what is going on with the OSCSDVersion struct member. My first suspection was that it might be a pointer to utf-16 string, similar to szCSDVersion in OSVERSIONINFOX struct, but I coudn't get it to print properly when attempting that. It seems way too short to store an actual service pack version number.

Edit: So yeah I'll look more into this later, but if you have suggestions/comments feel free to state them. Would also love to get the version string from someone who's using a Windows version with service pack installed.

@MSuih MSuih force-pushed the osVersion branch 2 times, most recently from 03e4b35 to 0c368de Compare April 25, 2019 13:09
@MSuih MSuih changed the title [WIP] Print Operating System version to log Print Operating System version to log Apr 25, 2019
@MSuih
Copy link
Member Author

MSuih commented Apr 25, 2019

Might as well remove the WIP label since I don't really know what to add anymore. I can't really test the service pack stuff on my own anyways.

@ZeeWanderer
Copy link
Contributor

ZeeWanderer commented Apr 25, 2019

@MSuih Try CSDVersion member at 0x02E8. Type _UNICODE_STRING (table).

#include "subauth.h" // For _UNICODE_STRING
...
const DWORD peb_offset = 0x60;
const INT_PTR peb = __readgsqword(peb_offset);

const DWORD version_major          = *reinterpret_cast<const DWORD*>(peb + 0x0118);
const DWORD version_minor          = *reinterpret_cast<const DWORD*>(peb + 0x011c);
const WORD build                   = *reinterpret_cast<const WORD*>(peb + 0x0120);
const _UNICODE_STRING service_pack = *reinterpret_cast<const _UNICODE_STRING*>(peb + 0x02E8);
const u64 compatibility_mode       = *reinterpret_cast<const u64*>(peb + 0x02D0);

char service_pack_str[32]{"Not Installed"}; // Arbitrary buffer size; Need to test what is Buffer value on Windows with SP
if (service_pack.Length > 0u)
{
	wcstombs(service_pack_str, service_pack.Buffer, service_pack.Length);// If no SP then it points to L'\0'
	service_pack_str[service_pack.Length] = '\0';
}

fmt::append(
	output, "Operating system: Windows, Major: %u, Minor: %u, Build: %u, Service Pack: %s, Compatibility mode: %llu", version_major, version_minor, build, service_pack_str, compatibility_mode);

In my case, the underlying Buffer is '\0'. Needs to be tested on Win with Sp to be sure it has correct value.
Not sure how to print Buffer to log without conversion since %S and %ls doesn't work and %s prints only first wchar in decimal format padded with 0.

Utilities/sysinfo.cpp Outdated Show resolved Hide resolved
@MSuih
Copy link
Member Author

MSuih commented Apr 25, 2019

This mismatch of various strings is making my head hurt, hopefully I didn't mess anything up when converting.

@danilaml
Copy link
Contributor

Btw, it can be trivially implemented in a cross-platform way using Qt if you can afford such dependency for Utilities.

@MSuih
Copy link
Member Author

MSuih commented Apr 25, 2019

Qt is intentionally kept separate from rest of the emulator in case f.ex. someone wants to do UI-less version of rpcs3 or switch to another UI framework.

@Nekotekina Nekotekina merged commit be6d9af into RPCS3:master Apr 25, 2019
@MSuih MSuih deleted the osVersion branch April 25, 2019 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants