mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-19 14:27:55 +08:00
219 lines
4.7 KiB
C++
219 lines
4.7 KiB
C++
#include "InstallationFolder.h"
|
|
|
|
#include <fstream>
|
|
#include <set>
|
|
#include <Windows.h>
|
|
#include <common/utils/winapi_error.h>
|
|
|
|
using namespace std;
|
|
using std::filesystem::directory_iterator;
|
|
using std::filesystem::path;
|
|
|
|
wstring GetVersion(path filePath)
|
|
{
|
|
DWORD verHandle = 0;
|
|
UINT size = 0;
|
|
LPVOID lpBuffer = nullptr;
|
|
DWORD verSize = GetFileVersionInfoSize(filePath.c_str(), &verHandle);
|
|
wstring version = L"None";
|
|
|
|
if (verSize != 0)
|
|
{
|
|
LPSTR verData = new char[verSize];
|
|
|
|
if (GetFileVersionInfo(filePath.c_str(), verHandle, verSize, verData))
|
|
{
|
|
if (VerQueryValue(verData, L"\\", &lpBuffer, &size))
|
|
{
|
|
if (size)
|
|
{
|
|
VS_FIXEDFILEINFO* verInfo = static_cast<VS_FIXEDFILEINFO*>(lpBuffer);
|
|
if (verInfo->dwSignature == 0xfeef04bd)
|
|
{
|
|
version =
|
|
std::to_wstring((verInfo->dwFileVersionMS >> 16) & 0xffff) + L"." +
|
|
std::to_wstring((verInfo->dwFileVersionMS >> 0) & 0xffff) + L"." +
|
|
std::to_wstring((verInfo->dwFileVersionLS >> 16) & 0xffff) + L"." +
|
|
std::to_wstring((verInfo->dwFileVersionLS >> 0) & 0xffff);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete[] verData;
|
|
}
|
|
|
|
return version;
|
|
}
|
|
|
|
optional<path> GetRootPath()
|
|
{
|
|
WCHAR modulePath[MAX_PATH];
|
|
if (!GetModuleFileName(NULL, modulePath, MAX_PATH))
|
|
{
|
|
return nullopt;
|
|
}
|
|
|
|
path rootPath = path(modulePath);
|
|
rootPath = rootPath.remove_filename();
|
|
rootPath = rootPath.append("..");
|
|
return std::filesystem::canonical(rootPath);
|
|
}
|
|
|
|
wstring GetChecksum(path filePath)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
HCRYPTPROV hProv = 0;
|
|
HCRYPTHASH hHash = 0;
|
|
HANDLE hFile = NULL;
|
|
constexpr int bufferSize = 1024;
|
|
BYTE rgbFile[bufferSize];
|
|
DWORD cbRead = 0;
|
|
constexpr int md5Length = 16;
|
|
BYTE rgbHash[md5Length];
|
|
DWORD cbHash = 0;
|
|
CHAR rgbDigits[] = "0123456789abcdef";
|
|
LPCWSTR filename = filePath.c_str();
|
|
hFile = CreateFile(filename,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
return L"CreateFile() failed. " + get_last_error_or_default(GetLastError());
|
|
}
|
|
|
|
// Get handle to the crypto provider
|
|
if (!CryptAcquireContext(&hProv,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT))
|
|
{
|
|
CloseHandle(hFile);
|
|
return L"CryptAcquireContext() failed. " + get_last_error_or_default(GetLastError());
|
|
}
|
|
|
|
if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
|
|
{
|
|
CloseHandle(hFile);
|
|
CryptReleaseContext(hProv, 0);
|
|
return L"CryptCreateHash() failed. " + get_last_error_or_default(GetLastError());
|
|
}
|
|
|
|
bResult = ReadFile(hFile, rgbFile, bufferSize, &cbRead, NULL);
|
|
while (bResult)
|
|
{
|
|
if (0 == cbRead)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!CryptHashData(hHash, rgbFile, cbRead, 0))
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
CryptDestroyHash(hHash);
|
|
CloseHandle(hFile);
|
|
return L"CryptHashData() failed. " + get_last_error_or_default(GetLastError());;
|
|
}
|
|
|
|
bResult = ReadFile(hFile, rgbFile, bufferSize, &cbRead, NULL);
|
|
}
|
|
|
|
if (!bResult)
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
CryptDestroyHash(hHash);
|
|
CloseHandle(hFile);
|
|
return L"ReadFile() failed. " + get_last_error_or_default(GetLastError());;
|
|
}
|
|
|
|
cbHash = md5Length;
|
|
std::wstring result = L"";
|
|
if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
|
|
{
|
|
for (DWORD i = 0; i < cbHash; i++)
|
|
{
|
|
result += rgbDigits[rgbHash[i] >> 4];
|
|
result += rgbDigits[rgbHash[i] & 0xf];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = L"CryptGetHashParam() failed. " + get_last_error_or_default(GetLastError());;
|
|
}
|
|
|
|
CryptDestroyHash(hHash);
|
|
CryptReleaseContext(hProv, 0);
|
|
CloseHandle(hFile);
|
|
|
|
return result;
|
|
}
|
|
|
|
class Reporter
|
|
{
|
|
private:
|
|
std::wofstream os;
|
|
std::wofstream GetOutputStream(const path& tmpDir)
|
|
{
|
|
auto path = tmpDir;
|
|
path += "installationFolderStructure.txt";
|
|
std::wofstream out_s = std::wofstream(path);
|
|
return out_s;
|
|
}
|
|
public:
|
|
Reporter(const path& tmpDir)
|
|
{
|
|
os = GetOutputStream(tmpDir);
|
|
}
|
|
|
|
void Report(path dirPath, int indentation = 0)
|
|
{
|
|
set<pair<path, bool>> paths;
|
|
try
|
|
{
|
|
directory_iterator end_it;
|
|
for (directory_iterator it(dirPath); it != end_it; ++it)
|
|
{
|
|
paths.insert({ it->path(), it->is_directory() });
|
|
}
|
|
}
|
|
catch (filesystem::filesystem_error err)
|
|
{
|
|
os << err.what() << endl;
|
|
}
|
|
|
|
for (auto filePair : paths)
|
|
{
|
|
auto filePath = filePair.first;
|
|
auto isDirectory = filePair.second;
|
|
|
|
auto fileName = filePath.wstring().substr(dirPath.wstring().size() + 1);
|
|
os << wstring(indentation, ' ') << fileName << " ";
|
|
if (!isDirectory)
|
|
{
|
|
os << GetVersion(filePath) << " " << GetChecksum(filePath);
|
|
}
|
|
|
|
os << endl;
|
|
if (isDirectory)
|
|
{
|
|
Report(filePath, indentation + 2);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
void InstallationFolder::ReportStructure(const path& tmpDir)
|
|
{
|
|
auto rootPath = GetRootPath();
|
|
if (rootPath)
|
|
{
|
|
Reporter(tmpDir).Report(rootPath.value());
|
|
}
|
|
}
|