| 1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | |
| 5 | |
| 6 | // Exit macros |
| 7 | #define ReswExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) |
| 8 | #define ReswExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) |
| 9 | #define ReswExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) |
| 10 | #define ReswExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) |
| 11 | #define ReswExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) |
| 12 | #define ReswExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_RESWUTIL, x, s, __VA_ARGS__) |
| 13 | #define ReswExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) |
| 14 | #define ReswExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) |
| 15 | #define ReswExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_RESWUTIL, p, x, e, s, __VA_ARGS__) |
| 16 | #define ReswExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_RESWUTIL, p, x, s, __VA_ARGS__) |
| 17 | #define ReswExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_RESWUTIL, e, x, s, __VA_ARGS__) |
| 18 | #define ReswExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_RESWUTIL, g, x, s, __VA_ARGS__) |
| 19 | |
| 20 | #define RES_STRINGS_PER_BLOCK 16 |
| 21 | |
| 22 | // Internal data structure format for a string block in a resource table. |
| 23 | // Note: Strings are always stored as UNICODED. |
| 24 | typedef struct _RES_STRING_BLOCK |
| 25 | { |
| 26 | DWORD dwBlockId; |
| 27 | WORD wLangId; |
| 28 | LPWSTR rgwz[RES_STRINGS_PER_BLOCK]; |
| 29 | } RES_STRING_BLOCK; |
| 30 | |
| 31 | |
| 32 | // private functions |
| 33 | static HRESULT StringBlockInitialize( |
| 34 | __in_opt HINSTANCE hModule, |
| 35 | __in DWORD dwBlockId, |
| 36 | __in WORD wLangId, |
| 37 | __in RES_STRING_BLOCK* pStrBlock |
| 38 | ); |
| 39 | static void StringBlockUnitialize( |
| 40 | __in RES_STRING_BLOCK* pStrBlock |
| 41 | ); |
| 42 | static HRESULT StringBlockChangeString( |
| 43 | __in RES_STRING_BLOCK* pStrBlock, |
| 44 | __in DWORD dwStringId, |
| 45 | __in_z LPCWSTR szData |
| 46 | ); |
| 47 | static HRESULT StringBlockConvertToResourceData( |
| 48 | __in const RES_STRING_BLOCK* pStrBlock, |
| 49 | __deref_out_bcount(*pcbData) LPVOID* ppvData, |
| 50 | __out DWORD* pcbData |
| 51 | ); |
| 52 | static HRESULT StringBlockConvertFromResourceData( |
| 53 | __in RES_STRING_BLOCK* pStrBlock, |
| 54 | __in_bcount(cbData) LPCVOID pvData, |
| 55 | __in SIZE_T cbData |
| 56 | ); |
| 57 | |
| 58 | |
| 59 | /******************************************************************** |
| 60 | ResWriteString - sets the string into to the specified file's resource name |
| 61 | |
| 62 | ********************************************************************/ |
| 63 | extern "C" HRESULT DAPI ResWriteString( |
| 64 | __in_z LPCWSTR wzResourceFile, |
| 65 | __in DWORD dwDataId, |
| 66 | __in_z LPCWSTR wzData, |
| 67 | __in WORD wLangId |
| 68 | ) |
| 69 | { |
| 70 | Assert(wzResourceFile); |
| 71 | Assert(wzData); |
| 72 | |
| 73 | HRESULT hr = S_OK; |
| 74 | HINSTANCE hModule = NULL; |
| 75 | HANDLE hUpdate = NULL; |
| 76 | RES_STRING_BLOCK StrBlock = { }; |
| 77 | LPVOID pvData = NULL; |
| 78 | DWORD cbData = 0; |
| 79 | |
| 80 | DWORD dwBlockId = (dwDataId / RES_STRINGS_PER_BLOCK) + 1; |
| 81 | DWORD dwStringId = (dwDataId % RES_STRINGS_PER_BLOCK); |
| 82 | |
| 83 | hModule = LoadLibraryExW(wzResourceFile, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); |
| 84 | ReswExitOnNullWithLastError(hModule, hr, "Failed to load library: %ls", wzResourceFile); |
| 85 | |
| 86 | hr = StringBlockInitialize(hModule, dwBlockId, wLangId, &StrBlock); |
| 87 | ReswExitOnFailure(hr, "Failed to get string block to update."); |
| 88 | |
| 89 | hr = StringBlockChangeString(&StrBlock, dwStringId, wzData); |
| 90 | ReswExitOnFailure(hr, "Failed to update string block string."); |
| 91 | |
| 92 | hr = StringBlockConvertToResourceData(&StrBlock, &pvData, &cbData); |
| 93 | ReswExitOnFailure(hr, "Failed to convert string block to resource data."); |
| 94 | |
| 95 | ::FreeLibrary(hModule); |
| 96 | hModule = NULL; |
| 97 | |
| 98 | hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); |
| 99 | ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); |
| 100 | |
| 101 | if (!::UpdateResourceA(hUpdate, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId, pvData, cbData)) |
| 102 | { |
| 103 | ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); |
| 104 | } |
| 105 | |
| 106 | if (!::EndUpdateResource(hUpdate, FALSE)) |
| 107 | { |
| 108 | ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); |
| 109 | } |
| 110 | |
| 111 | hUpdate = NULL; |
| 112 | |
| 113 | LExit: |
| 114 | ReleaseMem(pvData); |
| 115 | |
| 116 | StringBlockUnitialize(&StrBlock); |
| 117 | |
| 118 | if (hUpdate) |
| 119 | { |
| 120 | ::EndUpdateResource(hUpdate, TRUE); |
| 121 | } |
| 122 | |
| 123 | if (hModule) |
| 124 | { |
| 125 | ::FreeLibrary(hModule); |
| 126 | } |
| 127 | |
| 128 | return hr; |
| 129 | } |
| 130 | |
| 131 | |
| 132 | /******************************************************************** |
| 133 | ResWriteData - sets the data into to the specified file's resource name |
| 134 | |
| 135 | ********************************************************************/ |
| 136 | extern "C" HRESULT DAPI ResWriteData( |
| 137 | __in_z LPCWSTR wzResourceFile, |
| 138 | __in_z LPCSTR szDataName, |
| 139 | __in PVOID pData, |
| 140 | __in DWORD cbData |
| 141 | ) |
| 142 | { |
| 143 | Assert(wzResourceFile); |
| 144 | Assert(szDataName); |
| 145 | Assert(pData); |
| 146 | Assert(cbData); |
| 147 | |
| 148 | HRESULT hr = S_OK; |
| 149 | HANDLE hUpdate = NULL; |
| 150 | |
| 151 | hUpdate = ::BeginUpdateResourceW(wzResourceFile, FALSE); |
| 152 | ReswExitOnNullWithLastError(hUpdate, hr, "Failed to ::BeginUpdateResourcesW."); |
| 153 | |
| 154 | if (!::UpdateResourceA(hUpdate, RT_RCDATA, szDataName, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), pData, cbData)) |
| 155 | { |
| 156 | ReswExitWithLastError(hr, "Failed to ::UpdateResourceA."); |
| 157 | } |
| 158 | |
| 159 | if (!::EndUpdateResource(hUpdate, FALSE)) |
| 160 | { |
| 161 | ReswExitWithLastError(hr, "Failed to ::EndUpdateResourceW."); |
| 162 | } |
| 163 | |
| 164 | hUpdate = NULL; |
| 165 | |
| 166 | LExit: |
| 167 | if (hUpdate) |
| 168 | { |
| 169 | ::EndUpdateResource(hUpdate, TRUE); |
| 170 | } |
| 171 | |
| 172 | return hr; |
| 173 | } |
| 174 | |
| 175 | |
| 176 | /******************************************************************** |
| 177 | ResImportDataFromFile - reads a file and sets the data into to the specified file's resource name |
| 178 | |
| 179 | ********************************************************************/ |
| 180 | extern "C" HRESULT DAPI ResImportDataFromFile( |
| 181 | __in_z LPCWSTR wzTargetFile, |
| 182 | __in_z LPCWSTR wzSourceFile, |
| 183 | __in_z LPCSTR szDataName |
| 184 | ) |
| 185 | { |
| 186 | HRESULT hr = S_OK; |
| 187 | HANDLE hFile = INVALID_HANDLE_VALUE; |
| 188 | DWORD cbFile = 0; |
| 189 | HANDLE hMap = NULL; |
| 190 | PVOID pv = NULL; |
| 191 | |
| 192 | hFile = ::CreateFileW(wzSourceFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
| 193 | if (INVALID_HANDLE_VALUE == hFile) |
| 194 | { |
| 195 | ReswExitWithLastError(hr, "Failed to CreateFileW for %ls.", wzSourceFile); |
| 196 | } |
| 197 | |
| 198 | cbFile = ::GetFileSize(hFile, NULL); |
| 199 | if (!cbFile) |
| 200 | { |
| 201 | ReswExitWithLastError(hr, "Failed to GetFileSize for %ls.", wzSourceFile); |
| 202 | } |
| 203 | |
| 204 | hMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); |
| 205 | ReswExitOnNullWithLastError(hMap, hr, "Failed to CreateFileMapping for %ls.", wzSourceFile); |
| 206 | |
| 207 | pv = ::MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, cbFile); |
| 208 | ReswExitOnNullWithLastError(pv, hr, "Failed to MapViewOfFile for %ls.", wzSourceFile); |
| 209 | |
| 210 | hr = ResWriteData(wzTargetFile, szDataName, pv, cbFile); |
| 211 | ReswExitOnFailure(hr, "Failed to ResSetData %s on file %ls.", szDataName, wzTargetFile); |
| 212 | |
| 213 | LExit: |
| 214 | if (pv) |
| 215 | { |
| 216 | ::UnmapViewOfFile(pv); |
| 217 | } |
| 218 | |
| 219 | if (hMap) |
| 220 | { |
| 221 | ::CloseHandle(hMap); |
| 222 | } |
| 223 | |
| 224 | ReleaseFile(hFile); |
| 225 | |
| 226 | return hr; |
| 227 | } |
| 228 | |
| 229 | |
| 230 | static HRESULT StringBlockInitialize( |
| 231 | __in_opt HINSTANCE hModule, |
| 232 | __in DWORD dwBlockId, |
| 233 | __in WORD wLangId, |
| 234 | __in RES_STRING_BLOCK* pStrBlock |
| 235 | ) |
| 236 | { |
| 237 | HRESULT hr = S_OK; |
| 238 | HRSRC hRsrc = NULL; |
| 239 | HGLOBAL hData = NULL; |
| 240 | LPCVOID pvData = NULL; // does not need to be freed |
| 241 | DWORD cbData = 0; |
| 242 | |
| 243 | hRsrc = ::FindResourceExA(hModule, RT_STRING, MAKEINTRESOURCE(dwBlockId), wLangId); |
| 244 | ReswExitOnNullWithLastError(hRsrc, hr, "Failed to ::FindResourceExW."); |
| 245 | |
| 246 | hData = ::LoadResource(hModule, hRsrc); |
| 247 | ReswExitOnNullWithLastError(hData, hr, "Failed to ::LoadResource."); |
| 248 | |
| 249 | cbData = ::SizeofResource(hModule, hRsrc); |
| 250 | if (!cbData) |
| 251 | { |
| 252 | ReswExitWithLastError(hr, "Failed to ::SizeofResource."); |
| 253 | } |
| 254 | |
| 255 | pvData = ::LockResource(hData); |
| 256 | ReswExitOnNullWithLastError(pvData, hr, "Failed to lock data resource."); |
| 257 | |
| 258 | pStrBlock->dwBlockId = dwBlockId; |
| 259 | pStrBlock->wLangId = wLangId; |
| 260 | |
| 261 | hr = StringBlockConvertFromResourceData(pStrBlock, pvData, cbData); |
| 262 | ReswExitOnFailure(hr, "Failed to convert string block from resource data."); |
| 263 | |
| 264 | LExit: |
| 265 | return hr; |
| 266 | } |
| 267 | |
| 268 | |
| 269 | static void StringBlockUnitialize( |
| 270 | __in RES_STRING_BLOCK* pStrBlock |
| 271 | ) |
| 272 | { |
| 273 | if (pStrBlock) |
| 274 | { |
| 275 | for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) |
| 276 | { |
| 277 | ReleaseNullMem(pStrBlock->rgwz[i]); |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | |
| 283 | static HRESULT StringBlockChangeString( |
| 284 | __in RES_STRING_BLOCK* pStrBlock, |
| 285 | __in DWORD dwStringId, |
| 286 | __in_z LPCWSTR szData |
| 287 | ) |
| 288 | { |
| 289 | HRESULT hr = S_OK; |
| 290 | LPWSTR pwzData = NULL; |
| 291 | size_t cchData = 0; |
| 292 | |
| 293 | hr = ::StringCchLengthW(szData, STRSAFE_MAX_LENGTH, &cchData); |
| 294 | ReswExitOnRootFailure(hr, "Failed to get block string length."); |
| 295 | |
| 296 | pwzData = static_cast<LPWSTR>(MemAlloc((cchData + 1) * sizeof(WCHAR), TRUE)); |
| 297 | ReswExitOnNull(pwzData, hr, E_OUTOFMEMORY, "Failed to allocate new block string."); |
| 298 | |
| 299 | hr = ::StringCchCopyW(pwzData, cchData + 1, szData); |
| 300 | ReswExitOnRootFailure(hr, "Failed to copy new block string."); |
| 301 | |
| 302 | ReleaseNullMem(pStrBlock->rgwz[dwStringId]); |
| 303 | |
| 304 | pStrBlock->rgwz[dwStringId] = pwzData; |
| 305 | pwzData = NULL; |
| 306 | |
| 307 | LExit: |
| 308 | ReleaseMem(pwzData); |
| 309 | |
| 310 | return hr; |
| 311 | } |
| 312 | |
| 313 | |
| 314 | static HRESULT StringBlockConvertToResourceData( |
| 315 | __in const RES_STRING_BLOCK* pStrBlock, |
| 316 | __deref_out_bcount(*pcbData) LPVOID* ppvData, |
| 317 | __out DWORD* pcbData |
| 318 | ) |
| 319 | { |
| 320 | HRESULT hr = S_OK; |
| 321 | DWORD cbData = 0; |
| 322 | LPVOID pvData = NULL; |
| 323 | WCHAR* pwz = NULL; |
| 324 | |
| 325 | for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) |
| 326 | { |
| 327 | cbData += (lstrlenW(pStrBlock->rgwz[i]) + 1); |
| 328 | } |
| 329 | cbData *= sizeof(WCHAR); |
| 330 | |
| 331 | pvData = MemAlloc(cbData, TRUE); |
| 332 | ReswExitOnNull(pvData, hr, E_OUTOFMEMORY, "Failed to allocate buffer to convert string block."); |
| 333 | |
| 334 | pwz = static_cast<LPWSTR>(pvData); |
| 335 | for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) |
| 336 | { |
| 337 | DWORD cch = lstrlenW(pStrBlock->rgwz[i]); |
| 338 | |
| 339 | *pwz = static_cast<WCHAR>(cch); |
| 340 | ++pwz; |
| 341 | |
| 342 | for (DWORD j = 0; j < cch; ++j) |
| 343 | { |
| 344 | *pwz = pStrBlock->rgwz[i][j]; |
| 345 | ++pwz; |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | *pcbData = cbData; |
| 350 | *ppvData = pvData; |
| 351 | pvData = NULL; |
| 352 | |
| 353 | LExit: |
| 354 | ReleaseMem(pvData); |
| 355 | |
| 356 | return hr; |
| 357 | } |
| 358 | |
| 359 | |
| 360 | static HRESULT StringBlockConvertFromResourceData( |
| 361 | __in RES_STRING_BLOCK* pStrBlock, |
| 362 | __in_bcount(cbData) LPCVOID pvData, |
| 363 | __in SIZE_T cbData |
| 364 | ) |
| 365 | { |
| 366 | UNREFERENCED_PARAMETER(cbData); |
| 367 | HRESULT hr = S_OK; |
| 368 | LPCWSTR pwzParse = static_cast<LPCWSTR>(pvData); |
| 369 | |
| 370 | for (DWORD i = 0; i < RES_STRINGS_PER_BLOCK; ++i) |
| 371 | { |
| 372 | DWORD cchParse = static_cast<DWORD>(*pwzParse); |
| 373 | ++pwzParse; |
| 374 | |
| 375 | pStrBlock->rgwz[i] = static_cast<LPWSTR>(MemAlloc((cchParse + 1) * sizeof(WCHAR), TRUE)); |
| 376 | ReswExitOnNull(pStrBlock->rgwz[i], hr, E_OUTOFMEMORY, "Failed to populate pStrBlock."); |
| 377 | |
| 378 | hr = ::StringCchCopyNExW(pStrBlock->rgwz[i], cchParse + 1, pwzParse, cchParse, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); |
| 379 | ReswExitOnFailure(hr, "Failed to copy parsed resource data into string block."); |
| 380 | |
| 381 | pwzParse += cchParse; |
| 382 | } |
| 383 | |
| 384 | LExit: |
| 385 | return hr; |
| 386 | } |