|
| 1 | +/* |
| 2 | + * PROJECT: M2-Team Common Library |
| 3 | + * FILE: M2Win32Helpers.cpp |
| 4 | + * PURPOSE: Implementation for the Win32 desktop helper functions |
| 5 | + * |
| 6 | + * LICENSE: The MIT License |
| 7 | + * |
| 8 | + * DEVELOPER: Mouri_Naruto (Mouri_Naruto AT Outlook.com) |
| 9 | + */ |
| 10 | + |
| 11 | +#include "stdafx.h" |
| 12 | + |
| 13 | +#include <Windows.h> |
| 14 | +#include <VersionHelpers.h> |
| 15 | + |
| 16 | +#include "M2Win32Helpers.h" |
| 17 | + |
| 18 | +/** |
| 19 | + * Obtain the best matching resource with the specified type and name in the |
| 20 | + * specified module. |
| 21 | + * |
| 22 | + * @param lpResourceInfo The resource info which contains the pointer and size. |
| 23 | + * @param hModule A handle to the module whose portable executable file or an |
| 24 | + * accompanying MUI file contains the resource. If this |
| 25 | + * parameter is NULL, the function searches the module used to |
| 26 | + * create the current process. |
| 27 | + * @param lpType The resource type. Alternately, rather than a pointer, this |
| 28 | + * parameter can be MAKEINTRESOURCE(ID), where ID is the integer |
| 29 | + * identifier of the given resource type. |
| 30 | + * @param lpName The name of the resource. Alternately, rather than a pointer, |
| 31 | + * this parameter can be MAKEINTRESOURCE(ID), where ID is the |
| 32 | + * integer identifier of the resource. |
| 33 | + * @return HRESULT. |
| 34 | + */ |
| 35 | +HRESULT M2LoadResource( |
| 36 | + _Out_ PM2_RESOURCE_INFO lpResourceInfo, |
| 37 | + _In_opt_ HMODULE hModule, |
| 38 | + _In_ LPCWSTR lpType, |
| 39 | + _In_ LPCWSTR lpName) |
| 40 | +{ |
| 41 | + if (nullptr == lpResourceInfo) |
| 42 | + return E_INVALIDARG; |
| 43 | + |
| 44 | + SetLastError(ERROR_SUCCESS); |
| 45 | + |
| 46 | + lpResourceInfo->Size = 0; |
| 47 | + lpResourceInfo->Pointer = nullptr; |
| 48 | + |
| 49 | + HRSRC ResourceFind = FindResourceExW( |
| 50 | + hModule, lpType, lpName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); |
| 51 | + if (nullptr != ResourceFind) |
| 52 | + { |
| 53 | + lpResourceInfo->Size = SizeofResource(hModule, ResourceFind); |
| 54 | + |
| 55 | + HGLOBAL ResourceLoad = LoadResource(hModule, ResourceFind); |
| 56 | + if (nullptr != ResourceLoad) |
| 57 | + { |
| 58 | + lpResourceInfo->Pointer = LockResource(ResourceLoad); |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + return __HRESULT_FROM_WIN32(GetLastError()); |
| 63 | +} |
| 64 | + |
| 65 | +/** |
| 66 | + * Retrieves the path of the shared Windows directory on a multi-user system. |
| 67 | + * |
| 68 | + * @param WindowsFolderPath The string of the path of the shared Windows |
| 69 | + * directory on a multi-user system. |
| 70 | + * @return HRESULT. If the function succeeds, the return value is S_OK. |
| 71 | + */ |
| 72 | +HRESULT M2GetWindowsDirectory( |
| 73 | + std::wstring& WindowsFolderPath) |
| 74 | +{ |
| 75 | + HRESULT hr = S_OK; |
| 76 | + |
| 77 | + do |
| 78 | + { |
| 79 | + UINT Length = GetSystemWindowsDirectoryW( |
| 80 | + nullptr, |
| 81 | + 0); |
| 82 | + if (0 == Length) |
| 83 | + { |
| 84 | + hr = M2GetLastHRESULTErrorKnownFailedCall(); |
| 85 | + break; |
| 86 | + } |
| 87 | + |
| 88 | + WindowsFolderPath.resize(Length - 1); |
| 89 | + |
| 90 | + Length = GetSystemWindowsDirectoryW( |
| 91 | + &WindowsFolderPath[0], |
| 92 | + static_cast<UINT>(Length)); |
| 93 | + if (0 == Length) |
| 94 | + { |
| 95 | + hr = M2GetLastHRESULTErrorKnownFailedCall(); |
| 96 | + break; |
| 97 | + } |
| 98 | + if (WindowsFolderPath.size() != Length) |
| 99 | + { |
| 100 | + hr = E_UNEXPECTED; |
| 101 | + break; |
| 102 | + } |
| 103 | + |
| 104 | + } while (false); |
| 105 | + |
| 106 | + if (FAILED(hr)) |
| 107 | + { |
| 108 | + WindowsFolderPath.clear(); |
| 109 | + } |
| 110 | + |
| 111 | + return hr; |
| 112 | +} |
| 113 | + |
| 114 | +/** |
| 115 | + * Starts a service if not started and retrieves the current status of the |
| 116 | + * specified service. |
| 117 | + * |
| 118 | + * @param lpServiceName The name of the service to be started. This is the name |
| 119 | + * specified by the lpServiceName parameter of the |
| 120 | + * CreateService function when the service object was |
| 121 | + * created, not the service display name that is shown by |
| 122 | + * user interface applications to identify the service. |
| 123 | + * The maximum string length is 256 characters. The |
| 124 | + * service control manager database preserves the case of |
| 125 | + * the characters, but service name comparisons are always |
| 126 | + * case insensitive. Forward-slash (/) and backslash () |
| 127 | + * are invalid service name characters. |
| 128 | + * @param lpServiceStatus Contains process status information for a service. |
| 129 | + * @return HRESULT. If the function succeeds, the return value is S_OK. |
| 130 | + */ |
| 131 | +HRESULT M2StartService( |
| 132 | + _In_ LPCWSTR lpServiceName, |
| 133 | + _Out_ LPSERVICE_STATUS_PROCESS lpServiceStatus) |
| 134 | +{ |
| 135 | + M2::CServiceHandle hSCM; |
| 136 | + M2::CServiceHandle hService; |
| 137 | + |
| 138 | + DWORD nBytesNeeded = 0; |
| 139 | + DWORD nOldCheckPoint = 0; |
| 140 | + ULONGLONG nLastTick = 0; |
| 141 | + bool bStartServiceWCalled = false; |
| 142 | + |
| 143 | + hSCM = OpenSCManagerW( |
| 144 | + nullptr, |
| 145 | + nullptr, |
| 146 | + SC_MANAGER_CONNECT); |
| 147 | + if (!hSCM) |
| 148 | + return M2GetLastHRESULTErrorKnownFailedCall(); |
| 149 | + |
| 150 | + hService = OpenServiceW( |
| 151 | + hSCM, |
| 152 | + lpServiceName, |
| 153 | + SERVICE_QUERY_STATUS | SERVICE_START); |
| 154 | + if (!hService) |
| 155 | + return M2GetLastHRESULTErrorKnownFailedCall(); |
| 156 | + |
| 157 | + while (QueryServiceStatusEx( |
| 158 | + hService, |
| 159 | + SC_STATUS_PROCESS_INFO, |
| 160 | + reinterpret_cast<LPBYTE>(lpServiceStatus), |
| 161 | + sizeof(SERVICE_STATUS_PROCESS), |
| 162 | + &nBytesNeeded)) |
| 163 | + { |
| 164 | + if (SERVICE_STOPPED == lpServiceStatus->dwCurrentState) |
| 165 | + { |
| 166 | + // Failed if the service had stopped again. |
| 167 | + if (bStartServiceWCalled) |
| 168 | + return E_FAIL; |
| 169 | + |
| 170 | + if (!StartServiceW(hService, 0, nullptr)) |
| 171 | + return M2GetLastHRESULTErrorKnownFailedCall(); |
| 172 | + |
| 173 | + bStartServiceWCalled = true; |
| 174 | + } |
| 175 | + else if ( |
| 176 | + SERVICE_STOP_PENDING == lpServiceStatus->dwCurrentState || |
| 177 | + SERVICE_START_PENDING == lpServiceStatus->dwCurrentState) |
| 178 | + { |
| 179 | + ULONGLONG nCurrentTick = GetTickCount64(); |
| 180 | + |
| 181 | + if (!nLastTick) |
| 182 | + { |
| 183 | + nLastTick = nCurrentTick; |
| 184 | + nOldCheckPoint = lpServiceStatus->dwCheckPoint; |
| 185 | + |
| 186 | + // Same as the .Net System.ServiceProcess, wait 250ms. |
| 187 | + SleepEx(250, FALSE); |
| 188 | + } |
| 189 | + else |
| 190 | + { |
| 191 | + // Check the timeout if the checkpoint is not increased. |
| 192 | + if (lpServiceStatus->dwCheckPoint <= nOldCheckPoint) |
| 193 | + { |
| 194 | + ULONGLONG nDiff = nCurrentTick - nLastTick; |
| 195 | + if (nDiff > lpServiceStatus->dwWaitHint) |
| 196 | + { |
| 197 | + return __HRESULT_FROM_WIN32(ERROR_TIMEOUT); |
| 198 | + } |
| 199 | + } |
| 200 | + |
| 201 | + // Continue looping. |
| 202 | + nLastTick = 0; |
| 203 | + } |
| 204 | + } |
| 205 | + else |
| 206 | + { |
| 207 | + break; |
| 208 | + } |
| 209 | + } |
| 210 | + |
| 211 | + return S_OK; |
| 212 | +} |
| 213 | + |
| 214 | +/** |
| 215 | + * Loads the specified module with the optimization of the mitigation of DLL |
| 216 | + * preloading attacks into the address space of the calling process safely. The |
| 217 | + * specified module may cause other modules to be loaded. |
| 218 | + * |
| 219 | + * @param ModuleHandle If the function succeeds, this parameter's value is a |
| 220 | + * handle to the loaded module. You should read the |
| 221 | + * documentation about LoadLibraryEx API for further |
| 222 | + * information. |
| 223 | + * @param LibraryFileName A string that specifies the file name of the module |
| 224 | + * to load. You should read the documentation about |
| 225 | + * LoadLibraryEx API for further information. |
| 226 | + * @param Flags The action to be taken when loading the module. You should read |
| 227 | + * the documentation about LoadLibraryEx API for further |
| 228 | + * information. |
| 229 | + * @return HRESULT. |
| 230 | + */ |
| 231 | +HRESULT M2LoadLibraryEx( |
| 232 | + _Out_ HMODULE& ModuleHandle, |
| 233 | + _In_ LPCWSTR LibraryFileName, |
| 234 | + _In_ DWORD Flags) |
| 235 | +{ |
| 236 | + ModuleHandle = LoadLibraryExW(LibraryFileName, nullptr, Flags); |
| 237 | + if (!ModuleHandle) |
| 238 | + { |
| 239 | + const size_t BufferLength = 32768; |
| 240 | + wchar_t Buffer[BufferLength]; |
| 241 | + |
| 242 | + if ((Flags & LOAD_LIBRARY_SEARCH_SYSTEM32) && |
| 243 | + (M2GetLastErrorKnownFailedCall() == ERROR_INVALID_PARAMETER)) |
| 244 | + { |
| 245 | + if (!wcschr(LibraryFileName, (wchar_t)'\\')) |
| 246 | + { |
| 247 | + UINT NewLength = GetSystemDirectoryW( |
| 248 | + Buffer, |
| 249 | + BufferLength); |
| 250 | + |
| 251 | + Buffer[NewLength++] = '\\'; |
| 252 | + |
| 253 | + for (; *LibraryFileName; ++LibraryFileName) |
| 254 | + { |
| 255 | + Buffer[NewLength++] = *LibraryFileName; |
| 256 | + } |
| 257 | + |
| 258 | + Buffer[NewLength] = L'\0'; |
| 259 | + |
| 260 | + LibraryFileName = Buffer; |
| 261 | + } |
| 262 | + |
| 263 | + ModuleHandle = LoadLibraryExW(LibraryFileName, nullptr, Flags); |
| 264 | + } |
| 265 | + } |
| 266 | + |
| 267 | + return ModuleHandle ? S_OK : M2GetLastHRESULTErrorKnownFailedCall(); |
| 268 | +} |
0 commit comments