/ tools / BugReportTool / BugReportTool / InstallationFolder.cpp
InstallationFolder.cpp
  1  #include "InstallationFolder.h"
  2  
  3  #include <fstream>
  4  #include <set>
  5  #include <Windows.h>
  6  #include <common/utils/winapi_error.h>
  7  
  8  using namespace std;
  9  using std::filesystem::directory_iterator;
 10  using std::filesystem::path;
 11  
 12  wstring GetVersion(path filePath)
 13  {
 14  	DWORD  verHandle = 0;
 15  	UINT   size = 0;
 16  	LPVOID lpBuffer = nullptr;
 17  	DWORD  verSize = GetFileVersionInfoSize(filePath.c_str(), &verHandle);
 18  	wstring version = L"None";
 19  
 20  	if (verSize != 0)
 21  	{
 22  		LPSTR verData = new char[verSize];
 23  
 24  		if (GetFileVersionInfo(filePath.c_str(), verHandle, verSize, verData))
 25  		{
 26  			if (VerQueryValue(verData, L"\\", &lpBuffer, &size))
 27  			{
 28  				if (size)
 29  				{
 30  					VS_FIXEDFILEINFO* verInfo = static_cast<VS_FIXEDFILEINFO*>(lpBuffer);
 31  					if (verInfo->dwSignature == 0xfeef04bd)
 32  					{
 33  						version =
 34  							std::to_wstring((verInfo->dwFileVersionMS >> 16) & 0xffff) + L"." +
 35  							std::to_wstring((verInfo->dwFileVersionMS >> 0) & 0xffff) + L"." +
 36  							std::to_wstring((verInfo->dwFileVersionLS >> 16) & 0xffff) + L"." +
 37  							std::to_wstring((verInfo->dwFileVersionLS >> 0) & 0xffff);
 38  					}
 39  				}
 40  			}
 41  		}
 42  
 43  		delete[] verData;
 44  	}
 45  
 46  	return version;
 47  }
 48  
 49  optional<path> GetRootPath()
 50  {
 51  	WCHAR modulePath[MAX_PATH];
 52  	if (!GetModuleFileName(NULL, modulePath, MAX_PATH))
 53  	{
 54  		return nullopt;
 55  	}
 56  
 57  	path rootPath = path(modulePath);
 58  	rootPath = rootPath.remove_filename();
 59  	rootPath = rootPath.append("..");
 60  	return std::filesystem::canonical(rootPath);
 61  }
 62  
 63  wstring GetChecksum(path filePath)
 64  {
 65  	BOOL bResult = FALSE;
 66  	HCRYPTPROV hProv = 0;
 67  	HCRYPTHASH hHash = 0;
 68  	HANDLE hFile = NULL;
 69  	constexpr int bufferSize = 1024;
 70  	BYTE rgbFile[bufferSize];
 71  	DWORD cbRead = 0;
 72  	constexpr int md5Length = 16;
 73  	BYTE rgbHash[md5Length];
 74  	DWORD cbHash = 0;
 75  	CHAR rgbDigits[] = "0123456789abcdef";
 76  	LPCWSTR filename = filePath.c_str();
 77  	hFile = CreateFile(filename,
 78  		GENERIC_READ,
 79  		FILE_SHARE_READ,
 80  		NULL,
 81  		OPEN_EXISTING,
 82  		FILE_FLAG_SEQUENTIAL_SCAN,
 83  		NULL);
 84  
 85  	if (INVALID_HANDLE_VALUE == hFile)
 86  	{
 87  		return L"CreateFile() failed. " + get_last_error_or_default(GetLastError());
 88  	}
 89  
 90  	// Get handle to the crypto provider
 91  	if (!CryptAcquireContext(&hProv,
 92  		NULL,
 93  		NULL,
 94  		PROV_RSA_FULL,
 95  		CRYPT_VERIFYCONTEXT))
 96  	{
 97  		CloseHandle(hFile);
 98  		return L"CryptAcquireContext() failed. " + get_last_error_or_default(GetLastError());
 99  	}
100  
101  	if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash))
102  	{
103  		CloseHandle(hFile);
104  		CryptReleaseContext(hProv, 0);
105  		return L"CryptCreateHash() failed. " + get_last_error_or_default(GetLastError());
106  	}
107  
108  	bResult = ReadFile(hFile, rgbFile, bufferSize, &cbRead, NULL);
109  	while (bResult)
110  	{
111  		if (0 == cbRead)
112  		{
113  			break;
114  		}
115  
116  		if (!CryptHashData(hHash, rgbFile, cbRead, 0))
117  		{
118  			CryptReleaseContext(hProv, 0);
119  			CryptDestroyHash(hHash);
120  			CloseHandle(hFile);
121  			return L"CryptHashData() failed. " + get_last_error_or_default(GetLastError());;
122  		}
123  
124  		bResult = ReadFile(hFile, rgbFile, bufferSize, &cbRead, NULL);
125  	}
126  
127  	if (!bResult)
128  	{
129  		CryptReleaseContext(hProv, 0);
130  		CryptDestroyHash(hHash);
131  		CloseHandle(hFile);
132  		return L"ReadFile() failed. " + get_last_error_or_default(GetLastError());;
133  	}
134  
135  	cbHash = md5Length;
136  	std::wstring result = L"";
137  	if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
138  	{
139  		for (DWORD i = 0; i < cbHash; i++)
140  		{
141  			result += rgbDigits[rgbHash[i] >> 4];
142  			result += rgbDigits[rgbHash[i] & 0xf];
143  		}
144  	}
145  	else
146  	{
147  		result = L"CryptGetHashParam() failed. " + get_last_error_or_default(GetLastError());;
148  	}
149  
150  	CryptDestroyHash(hHash);
151  	CryptReleaseContext(hProv, 0);
152  	CloseHandle(hFile);
153  
154  	return result;
155  }
156  
157  class Reporter
158  {
159  private:
160  	std::wofstream os;
161  	std::wofstream GetOutputStream(const path& tmpDir)
162  	{
163  		auto path = tmpDir;
164  		path += "installationFolderStructure.txt";
165  		std::wofstream out_s = std::wofstream(path);
166  		return out_s;
167  	}
168  public:
169  	Reporter(const path& tmpDir)
170  	{
171  		os = GetOutputStream(tmpDir);
172  	}
173  
174  	void Report(path dirPath, int indentation = 0)
175  	{
176  		set<pair<path, bool>> paths;
177  		try
178  		{
179  			directory_iterator end_it;
180  			for (directory_iterator it(dirPath); it != end_it; ++it)
181  			{
182  				paths.insert({ it->path(), it->is_directory() });
183  			}
184  		}
185  		catch (filesystem::filesystem_error err)
186  		{
187  			os << err.what() << endl;
188  		}
189  
190  		for (auto filePair : paths)
191  		{
192  			auto filePath = filePair.first;
193  			auto isDirectory = filePair.second;
194  
195  			auto fileName = filePath.wstring().substr(dirPath.wstring().size() + 1);
196  			os << wstring(indentation, ' ') << fileName << " ";
197  			if (!isDirectory)
198  			{
199  				os << GetVersion(filePath) << " " << GetChecksum(filePath);
200  			}
201  
202  			os << endl;
203  			if (isDirectory)
204  			{
205  				Report(filePath, indentation + 2);
206  			}
207  		}
208  	}
209  };
210  
211  void InstallationFolder::ReportStructure(const path& tmpDir)
212  {
213  	auto rootPath = GetRootPath();
214  	if (rootPath)
215  	{
216  		Reporter(tmpDir).Report(rootPath.value());
217  	}
218  }