├── .clang-format ├── .gitattributes ├── .gitignore ├── LICENSE ├── MainDlg.cpp ├── MainDlg.h ├── README.md ├── finddlg.h ├── libraries ├── dia │ ├── cvconst.h │ ├── dia2.h │ ├── diacreate.h │ └── lib │ │ ├── amd64 │ │ └── diaguids.lib │ │ ├── arm │ │ └── diaguids.lib │ │ ├── arm64 │ │ └── diaguids.lib │ │ └── diaguids.lib └── wil │ ├── Tracelogging.h │ ├── _version.txt │ ├── com.h │ ├── com_apartment_variable.h │ ├── common.h │ ├── coroutine.h │ ├── cppwinrt.h │ ├── cppwinrt_authoring.h │ ├── cppwinrt_helpers.h │ ├── cppwinrt_wrl.h │ ├── filesystem.h │ ├── nt_result_macros.h │ ├── registry.h │ ├── registry_helpers.h │ ├── resource.h │ ├── result.h │ ├── result_macros.h │ ├── result_originate.h │ ├── rpc_helpers.h │ ├── safecast.h │ ├── stl.h │ ├── token_helpers.h │ ├── traceloggingconfig.h │ ├── win32_helpers.h │ ├── win32_result_macros.h │ ├── winrt.h │ ├── wistd_config.h │ ├── wistd_functional.h │ ├── wistd_memory.h │ ├── wistd_type_traits.h │ └── wrl.h ├── packages.config ├── res ├── manifest.manifest └── windhawk-symbol-helper.ico ├── resource.h ├── screenshot.png ├── stdafx.cpp ├── stdafx.h ├── symbol_enum.cpp ├── symbol_enum.h ├── symbols_from_binary.cpp ├── symbols_from_binary.h ├── view.h ├── windhawk-symbol-helper.cpp ├── windhawk-symbol-helper.h ├── windhawk-symbol-helper.rc ├── windhawk-symbol-helper.sln ├── windhawk-symbol-helper.vcxproj └── windhawk-symbol-helper.vcxproj.filters /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Chromium 2 | IndentWidth: 4 3 | 4 | # Reference: https://github.com/chromium/chromium/blob/3d90e395a5e87e305e567c097c434549f0d0874e/.clang-format 5 | # Make sure code like: 6 | # BEGIN_MESSAGE_MAP() 7 | # MESSAGE_HANDLER(WidgetHostViewHost_Update, OnUpdate) 8 | # END_MESSAGE_MAP() 9 | # gets correctly indented. 10 | MacroBlockBegin: "^\ 11 | BEGIN_MSG_MAP|\ 12 | BEGIN_MSG_MAP_EX|\ 13 | BEGIN_DLGRESIZE_MAP$" 14 | MacroBlockEnd: "^\ 15 | END_MSG_MAP|\ 16 | END_DLGRESIZE_MAP$" 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | -------------------------------------------------------------------------------- /MainDlg.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "MainDlg.h" 4 | 5 | #include "symbol_enum.h" 6 | #include "symbols_from_binary.h" 7 | 8 | #define WSH_VERSION L"1.0.8" 9 | 10 | namespace { 11 | 12 | struct InitialUIValues { 13 | CString engineDir; 14 | CString symbolsDir; 15 | CString symbolServer; 16 | CString targetExecutable; 17 | }; 18 | 19 | InitialUIValues GetInitialUIValues() { 20 | InitialUIValues values; 21 | 22 | std::filesystem::path iniFilePath = wil::GetModuleFileName(); 23 | iniFilePath.replace_filename(L"windhawk-symbol-helper.ini"); 24 | 25 | std::filesystem::path fallbackIniFilePath1 = iniFilePath; 26 | fallbackIniFilePath1.replace_filename(L"windhawk.ini"); 27 | 28 | std::filesystem::path fallbackIniFilePath2 = 29 | wil::ExpandEnvironmentStrings( 30 | LR"(%ProgramFiles%\Windhawk\windhawk.ini)"); 31 | 32 | WCHAR buffer[MAX_PATH]; 33 | constexpr DWORD bufferSize = static_cast(std::size(buffer)); 34 | 35 | if (GetPrivateProfileString(L"Config", L"EnginePath", L"", buffer, 36 | bufferSize, iniFilePath.c_str())) { 37 | values.engineDir = 38 | wil::ExpandEnvironmentStrings(buffer).c_str(); 39 | } else if (GetPrivateProfileString(L"Storage", L"EnginePath", L"", buffer, 40 | bufferSize, 41 | fallbackIniFilePath1.c_str())) { 42 | auto expanded = wil::ExpandEnvironmentStrings(buffer); 43 | values.engineDir = 44 | (fallbackIniFilePath1.parent_path() / expanded).c_str(); 45 | } else if (GetPrivateProfileString(L"Storage", L"EnginePath", L"", buffer, 46 | bufferSize, 47 | fallbackIniFilePath2.c_str())) { 48 | auto expanded = wil::ExpandEnvironmentStrings(buffer); 49 | values.engineDir = 50 | (fallbackIniFilePath2.parent_path() / expanded).c_str(); 51 | } else { 52 | values.engineDir = LR"(C:\Program Files\Windhawk\Engine\1.4.1)"; 53 | } 54 | 55 | if (GetPrivateProfileString(L"Config", L"SymbolsPath", L"", buffer, 56 | bufferSize, iniFilePath.c_str())) { 57 | values.symbolsDir = 58 | wil::ExpandEnvironmentStrings(buffer).c_str(); 59 | } else if (GetPrivateProfileString(L"Storage", L"AppDataPath", L"", buffer, 60 | bufferSize, 61 | fallbackIniFilePath1.c_str())) { 62 | auto expanded = wil::ExpandEnvironmentStrings(buffer); 63 | values.symbolsDir = (fallbackIniFilePath1.parent_path() / expanded / 64 | L"Engine" / L"Symbols") 65 | .c_str(); 66 | } else if (GetPrivateProfileString(L"Storage", L"AppDataPath", L"", buffer, 67 | bufferSize, 68 | fallbackIniFilePath2.c_str())) { 69 | auto expanded = wil::ExpandEnvironmentStrings(buffer); 70 | values.symbolsDir = (fallbackIniFilePath2.parent_path() / expanded / 71 | L"Engine" / L"Symbols") 72 | .c_str(); 73 | } else { 74 | values.symbolsDir = LR"(C:\ProgramData\Windhawk\Engine\Symbols)"; 75 | } 76 | 77 | if (GetPrivateProfileString(L"Config", L"SymbolServer", L"", buffer, 78 | bufferSize, iniFilePath.c_str())) { 79 | values.symbolServer = buffer; 80 | } else { 81 | values.symbolServer = L"https://msdl.microsoft.com/download/symbols"; 82 | } 83 | 84 | if (GetPrivateProfileString(L"Config", L"TargetExecutable", L"", buffer, 85 | bufferSize, iniFilePath.c_str())) { 86 | values.targetExecutable = 87 | wil::ExpandEnvironmentStrings(buffer).c_str(); 88 | } else { 89 | values.targetExecutable = LR"(C:\Windows\Explorer.exe)"; 90 | } 91 | 92 | return values; 93 | } 94 | 95 | void OpenUrl(HWND hWnd, PCWSTR url) { 96 | if ((INT_PTR)ShellExecute(hWnd, L"open", url, nullptr, nullptr, 97 | SW_SHOWNORMAL) <= 32) { 98 | CString errorMsg; 99 | errorMsg.Format( 100 | L"Failed to open link, you can copy it with Ctrl+C and open it in " 101 | L"a browser manually:\n\n%s", 102 | url); 103 | MessageBox(hWnd, errorMsg, nullptr, MB_ICONHAND); 104 | } 105 | } 106 | 107 | } // namespace 108 | 109 | BOOL CMainDlg::PreTranslateMessage(MSG* pMsg) { 110 | if (m_resultsEdit.PreTranslateMessage(pMsg)) { 111 | return TRUE; 112 | } 113 | 114 | if (m_accelerator && ::TranslateAccelerator(m_hWnd, m_accelerator, pMsg)) { 115 | return TRUE; 116 | } 117 | 118 | return CWindow::IsDialogMessage(pMsg); 119 | } 120 | 121 | BOOL CMainDlg::OnInitDialog(CWindow wndFocus, LPARAM lInitParam) { 122 | // Center the dialog on the screen. 123 | CenterWindow(); 124 | 125 | // Set icons. 126 | HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, 127 | ::GetSystemMetrics(SM_CXICON), 128 | ::GetSystemMetrics(SM_CYICON)); 129 | SetIcon(hIcon, TRUE); 130 | HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, 131 | ::GetSystemMetrics(SM_CXSMICON), 132 | ::GetSystemMetrics(SM_CYSMICON)); 133 | SetIcon(hIconSmall, FALSE); 134 | 135 | // Bind keys... 136 | m_accelerator = AtlLoadAccelerators(IDR_MAINFRAME); 137 | 138 | // Register object for message filtering and idle updates. 139 | CMessageLoop* pLoop = _Module.GetMessageLoop(); 140 | ATLASSERT(pLoop != nullptr); 141 | pLoop->AddMessageFilter(this); 142 | 143 | // Populate values. 144 | auto initialUIValues = GetInitialUIValues(); 145 | CEdit(GetDlgItem(IDC_ENGINE_DIR)).SetWindowText(initialUIValues.engineDir); 146 | CEdit(GetDlgItem(IDC_SYMBOLS_DIR)) 147 | .SetWindowText(initialUIValues.symbolsDir); 148 | CEdit(GetDlgItem(IDC_SYMBOL_SERVER)) 149 | .SetWindowText(initialUIValues.symbolServer); 150 | CEdit(GetDlgItem(IDC_TARGET_EXECUTABLE)) 151 | .SetWindowText(initialUIValues.targetExecutable); 152 | 153 | CButton(GetDlgItem(IDC_UNDECORATED)).SetCheck(BST_CHECKED); 154 | 155 | // Init edit control. 156 | auto resultsPlaceholderControl = GetDlgItem(IDC_RESULTS_PLACEHOLDER); 157 | 158 | CRect rc; 159 | resultsPlaceholderControl.GetClientRect(&rc); 160 | resultsPlaceholderControl.MapWindowPoints(m_hWnd, rc); 161 | m_resultsEdit.Create(m_hWnd, rc, nullptr, 162 | WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | 163 | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | 164 | ES_NOHIDESEL | WS_VSCROLL | WS_HSCROLL, 165 | WS_EX_CLIENTEDGE, IDC_RESULTS); 166 | 167 | m_resultsEdit.SendMessage(EM_SETEXTENDEDSTYLE, ES_EX_ZOOMABLE, 168 | ES_EX_ZOOMABLE); 169 | 170 | UpdateResultsEditFont(); 171 | 172 | // Init resizing. 173 | DlgResize_Init(); 174 | 175 | return TRUE; 176 | } 177 | 178 | void CMainDlg::OnDestroy() { 179 | m_enumSymbolsThread.reset(); 180 | 181 | CMessageLoop* pLoop = _Module.GetMessageLoop(); 182 | ATLASSERT(pLoop != nullptr); 183 | pLoop->RemoveMessageFilter(this); 184 | 185 | PostQuitMessage(0); 186 | } 187 | 188 | void CMainDlg::OnDpiChanged(UINT nDpiX, UINT nDpiY, PRECT pRect) { 189 | UpdateResultsEditFont(); 190 | } 191 | 192 | void CMainDlg::OnDropFiles(HDROP hDropInfo) { 193 | if (::DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0) == 1) { 194 | WCHAR fileName[MAX_PATH]; 195 | ::DragQueryFile(hDropInfo, 0, fileName, MAX_PATH); 196 | 197 | SetDlgItemText(IDC_TARGET_EXECUTABLE, fileName); 198 | } else { 199 | MessageBox(L"Please drop one file at a time", L"Unsupported", 200 | MB_ICONINFORMATION); 201 | } 202 | 203 | ::DragFinish(hDropInfo); 204 | } 205 | 206 | void CMainDlg::OnPickFile(UINT uNotifyCode, int nID, CWindow wndCtl) { 207 | // https://learn.microsoft.com/en-us/windows/win32/shell/common-file-dialog#basic-usage 208 | 209 | CComPtr pfd; 210 | HRESULT hr = pfd.CoCreateInstance(CLSID_FileOpenDialog, nullptr, 211 | CLSCTX_INPROC_SERVER); 212 | if (FAILED(hr)) { 213 | return; 214 | } 215 | 216 | // Get options. 217 | DWORD dwFlags; 218 | hr = pfd->GetOptions(&dwFlags); 219 | if (FAILED(hr)) { 220 | return; 221 | } 222 | 223 | // Get shell items only for file system items. 224 | hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM); 225 | if (FAILED(hr)) { 226 | return; 227 | } 228 | 229 | // Set the file types. 230 | const COMDLG_FILTERSPEC fileTypes[] = { 231 | {L"Executable files (*.dll;*.exe)", L"*.dll;*.exe"}, 232 | {L"All files (*.*)", L"*.*"}, 233 | }; 234 | 235 | hr = pfd->SetFileTypes(ARRAYSIZE(fileTypes), fileTypes); 236 | if (FAILED(hr)) { 237 | return; 238 | } 239 | 240 | hr = pfd->SetTitle(L"Browse"); 241 | if (FAILED(hr)) { 242 | return; 243 | } 244 | 245 | // Show the dialog. 246 | hr = pfd->Show(m_hWnd); 247 | if (FAILED(hr)) { 248 | return; 249 | } 250 | 251 | CComPtr psiResult; 252 | hr = pfd->GetResult(&psiResult); 253 | if (SUCCEEDED(hr)) { 254 | CComHeapPtr pszFilePath; 255 | hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath); 256 | if (SUCCEEDED(hr)) { 257 | // Set the file name on the textbox. 258 | SetDlgItemText(IDC_TARGET_EXECUTABLE, pszFilePath); 259 | } 260 | } 261 | } 262 | 263 | void CMainDlg::OnAppAbout(UINT uNotifyCode, int nID, CWindow wndCtl) { 264 | PCWSTR content = 265 | L"A tool to get symbols from executables the same way Windhawk mods do " 266 | L"with the symbols API.\n\n" 267 | L"The tool was created to help with Windhawk mod development.\n\n" 268 | L"Windhawk can be downloaded at windhawk.net."; 270 | 271 | TASKDIALOGCONFIG taskDialogConfig{ 272 | .cbSize = sizeof(taskDialogConfig), 273 | .hwndParent = m_hWnd, 274 | .hInstance = _Module.GetModuleInstance(), 275 | .dwFlags = TDF_ENABLE_HYPERLINKS | TDF_ALLOW_DIALOG_CANCELLATION | 276 | TDF_POSITION_RELATIVE_TO_WINDOW, 277 | .pszWindowTitle = L"About", 278 | .pszMainIcon = MAKEINTRESOURCE(IDR_MAINFRAME), 279 | .pszMainInstruction = L"Windhawk Symbol Helper v" WSH_VERSION, 280 | .pszContent = content, 281 | .pfCallback = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, 282 | LONG_PTR lpRefData) -> HRESULT { 283 | switch (msg) { 284 | case TDN_HYPERLINK_CLICKED: 285 | OpenUrl(hwnd, (PCWSTR)lParam); 286 | break; 287 | } 288 | 289 | return S_OK; 290 | }, 291 | }; 292 | 293 | ::TaskDialogIndirect(&taskDialogConfig, nullptr, nullptr, nullptr); 294 | } 295 | 296 | void CMainDlg::OnAppExit(UINT uNotifyCode, int nID, CWindow wndCtl) { 297 | DestroyWindow(); 298 | } 299 | 300 | void CMainDlg::OnOK(UINT uNotifyCode, int nID, CWindow wndCtl) { 301 | if (m_enumSymbolsThread) { 302 | m_enumSymbolsThread->request_stop(); 303 | GetDlgItem(IDOK).EnableWindow(FALSE); 304 | return; 305 | } 306 | 307 | SymbolsFromBinaryOptions options; 308 | 309 | GetDlgItemText(IDC_ENGINE_DIR, options.engineDir); 310 | GetDlgItemText(IDC_SYMBOLS_DIR, options.symbolsDir); 311 | GetDlgItemText(IDC_SYMBOL_SERVER, options.symbolServer); 312 | GetDlgItemText(IDC_TARGET_EXECUTABLE, options.targetExecutable); 313 | options.undecorated = 314 | CButton(GetDlgItem(IDC_UNDECORATED)).GetCheck() != BST_UNCHECKED; 315 | options.decorated = 316 | CButton(GetDlgItem(IDC_DECORATED)).GetCheck() != BST_UNCHECKED; 317 | bool log = CButton(GetDlgItem(IDC_LOG)).GetCheck() != BST_UNCHECKED; 318 | 319 | SetDlgItemText(IDOK, L"Cancel"); 320 | 321 | m_enumSymbolsThread = std::jthread( 322 | [options = std::move(options), &enumSymbolsResult = m_enumSymbolsResult, 323 | hWnd = m_hWnd, log](std::stop_token stopToken) { 324 | CStringA logOutput; 325 | 326 | try { 327 | enumSymbolsResult = SymbolsFromBinary( 328 | std::move(options), log ? &logOutput : nullptr, 329 | [hWnd](int progress) { 330 | CWindow(hWnd).PostMessage(UWM_PROGRESS, 331 | (WPARAM)progress); 332 | }, 333 | &stopToken); 334 | } catch (const std::exception& e) { 335 | enumSymbolsResult.Format(L"Error: %S\r\n%S", e.what(), 336 | logOutput.GetString()); 337 | } 338 | 339 | CWindow(hWnd).PostMessage(UWM_ENUM_SYMBOLS_DONE); 340 | }); 341 | } 342 | 343 | void CMainDlg::OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl) { 344 | int result = IDYES; 345 | 346 | TASKDIALOGCONFIG taskDialogConfig{ 347 | .cbSize = sizeof(taskDialogConfig), 348 | .hwndParent = m_hWnd, 349 | .hInstance = _Module.GetModuleInstance(), 350 | .dwFlags = 351 | TDF_ALLOW_DIALOG_CANCELLATION | TDF_POSITION_RELATIVE_TO_WINDOW, 352 | .dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON, 353 | .pszWindowTitle = L"Exit", 354 | .pszMainIcon = MAKEINTRESOURCE(IDR_MAINFRAME), 355 | .pszContent = L"Are you sure you want to exit?", 356 | .nDefaultButton = IDNO, 357 | }; 358 | 359 | if (FAILED(::TaskDialogIndirect(&taskDialogConfig, &result, nullptr, 360 | nullptr)) || 361 | result == IDYES) { 362 | DestroyWindow(); 363 | } 364 | } 365 | 366 | LRESULT CMainDlg::OnProgress(UINT uMsg, WPARAM wParam, LPARAM lParam) { 367 | int progress = (int)wParam; 368 | 369 | CString text; 370 | text.Format(L"[%d%%] Cancel", progress); 371 | 372 | SetDlgItemText(IDOK, text); 373 | 374 | return 0; 375 | } 376 | 377 | LRESULT CMainDlg::OnEnumSymbolsDone(UINT uMsg, WPARAM wParam, LPARAM lParam) { 378 | m_enumSymbolsThread.reset(); 379 | 380 | SetDlgItemText(IDC_RESULTS, m_enumSymbolsResult); 381 | m_enumSymbolsResult.Empty(); 382 | 383 | SetDlgItemText(IDOK, L"Get &symbols"); 384 | GetDlgItem(IDOK).EnableWindow(TRUE); 385 | 386 | return 0; 387 | } 388 | 389 | void CMainDlg::UpdateResultsEditFont() { 390 | CLogFont fontAttributes(AtlGetDefaultGuiFont()); 391 | wcscpy_s(fontAttributes.lfFaceName, L"Consolas"); 392 | if (fontAttributes.lfHeight > 0) { 393 | fontAttributes.lfHeight = 394 | MulDiv(fontAttributes.lfHeight, ::GetDpiForWindow(m_hWnd), 96); 395 | } else if (fontAttributes.lfHeight < 0) { 396 | fontAttributes.lfHeight = 397 | -MulDiv(-fontAttributes.lfHeight, ::GetDpiForWindow(m_hWnd), 96); 398 | } 399 | 400 | m_resultsEditFont = fontAttributes.CreateFontIndirect(); 401 | m_resultsEdit.SetFont(m_resultsEditFont); 402 | } 403 | -------------------------------------------------------------------------------- /MainDlg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "resource.h" 4 | #include "view.h" 5 | 6 | class CMainDlg : public CDialogImpl, 7 | public CMessageFilter, 8 | public CDialogResize { 9 | public: 10 | enum { IDD = IDD_MAINDLG }; 11 | 12 | enum { 13 | UWM_PROGRESS = WM_APP, 14 | UWM_ENUM_SYMBOLS_DONE, 15 | }; 16 | 17 | BEGIN_DLGRESIZE_MAP(CMainDlg) 18 | DLGRESIZE_CONTROL(IDC_STATIC_ENGINE_DIR, DLSZ_SIZE_X) 19 | DLGRESIZE_CONTROL(IDC_ENGINE_DIR, DLSZ_SIZE_X) 20 | DLGRESIZE_CONTROL(IDC_STATIC_SYMBOLS_DIR, DLSZ_SIZE_X) 21 | DLGRESIZE_CONTROL(IDC_SYMBOLS_DIR, DLSZ_SIZE_X) 22 | DLGRESIZE_CONTROL(IDC_STATIC_SYMBOL_SERVER, DLSZ_SIZE_X) 23 | DLGRESIZE_CONTROL(IDC_SYMBOL_SERVER, DLSZ_SIZE_X) 24 | DLGRESIZE_CONTROL(IDC_STATIC_TARGET_EXECUTABLE, DLSZ_SIZE_X) 25 | DLGRESIZE_CONTROL(IDC_TARGET_EXECUTABLE, DLSZ_SIZE_X) 26 | DLGRESIZE_CONTROL(IDC_PICKFILE, DLSZ_MOVE_X) 27 | DLGRESIZE_CONTROL(IDC_STATIC_RESULTS, DLSZ_SIZE_X) 28 | DLGRESIZE_CONTROL(IDC_RESULTS, DLSZ_SIZE_X | DLSZ_SIZE_Y) 29 | DLGRESIZE_CONTROL(IDOK, DLSZ_MOVE_Y) 30 | DLGRESIZE_CONTROL(ID_APP_ABOUT, DLSZ_CENTER_X | DLSZ_MOVE_Y) 31 | DLGRESIZE_CONTROL(ID_APP_EXIT, DLSZ_MOVE_X | DLSZ_MOVE_Y) 32 | END_DLGRESIZE_MAP() 33 | 34 | private: 35 | BEGIN_MSG_MAP(CMainDlg) 36 | CHAIN_MSG_MAP(CDialogResize) 37 | MSG_WM_INITDIALOG(OnInitDialog) 38 | MSG_WM_DESTROY(OnDestroy) 39 | MSG_WM_DPICHANGED(OnDpiChanged) 40 | MSG_WM_DROPFILES(OnDropFiles) 41 | COMMAND_ID_HANDLER_EX(IDC_PICKFILE, OnPickFile) 42 | COMMAND_ID_HANDLER_EX(ID_APP_ABOUT, OnAppAbout) 43 | COMMAND_ID_HANDLER_EX(ID_APP_EXIT, OnAppExit) 44 | COMMAND_ID_HANDLER_EX(IDOK, OnOK) 45 | COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) 46 | MESSAGE_HANDLER_EX(UWM_PROGRESS, OnProgress) 47 | MESSAGE_HANDLER_EX(UWM_ENUM_SYMBOLS_DONE, OnEnumSymbolsDone) 48 | CHAIN_COMMANDS_MEMBER(m_resultsEdit) 49 | END_MSG_MAP() 50 | 51 | BOOL PreTranslateMessage(MSG* pMsg) override; 52 | 53 | BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); 54 | void OnDestroy(); 55 | void OnDpiChanged(UINT nDpiX, UINT nDpiY, PRECT pRect); 56 | void OnDropFiles(HDROP hDropInfo); 57 | void OnPickFile(UINT uNotifyCode, int nID, CWindow wndCtl); 58 | void OnAppAbout(UINT uNotifyCode, int nID, CWindow wndCtl); 59 | void OnAppExit(UINT uNotifyCode, int nID, CWindow wndCtl); 60 | void OnOK(UINT uNotifyCode, int nID, CWindow wndCtl); 61 | void OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl); 62 | LRESULT OnProgress(UINT uMsg, WPARAM wParam, LPARAM lParam); 63 | LRESULT OnEnumSymbolsDone(UINT uMsg, WPARAM wParam, LPARAM lParam); 64 | 65 | void UpdateResultsEditFont(); 66 | 67 | HACCEL m_accelerator = nullptr; 68 | CEditView m_resultsEdit; 69 | CFont m_resultsEditFont; 70 | 71 | std::optional m_enumSymbolsThread; 72 | CString m_enumSymbolsResult; 73 | }; 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Windhawk Symbol Helper 2 | 3 | A tool to get symbols from executables the same way Windhawk mods do with the 4 | symbols API. 5 | 6 | The tool was created to help with Windhawk mod development. 7 | 8 | For more information about Windhawk, visit 9 | [windhawk.net](https://windhawk.net/). 10 | 11 | ![Screenshot](screenshot.png) 12 | -------------------------------------------------------------------------------- /finddlg.h: -------------------------------------------------------------------------------- 1 | #ifndef __FindReplaceDialogWithMessageFilter_h__ 2 | #define __FindReplaceDialogWithMessageFilter_h__ 3 | 4 | #pragma once 5 | 6 | #ifndef __cplusplus 7 | #error ATL requires C++ compilation (use a .cpp suffix) 8 | #endif 9 | 10 | #ifndef __ATLAPP_H__ 11 | #error FindReplaceDialogWithMessageFilter.h requires atlapp.h to be included first 12 | #endif 13 | 14 | #ifndef __ATLWIN_H__ 15 | #error FindReplaceDialogWithMessageFilter.h requires atlwin.h to be included first 16 | #endif 17 | 18 | class CFindReplaceDialogWithMessageFilter : 19 | public CFindReplaceDialogImpl, 20 | public CMessageFilter 21 | { 22 | protected: 23 | CMessageLoop* m_messageLoop; 24 | 25 | constexpr static WORD kFindReplaceBufferSize = MAXWORD; // 64K buffer size. 26 | std::wstring m_findText = std::wstring(kFindReplaceBufferSize, L'\0'); 27 | std::wstring m_replaceText = std::wstring(kFindReplaceBufferSize, L'\0'); 28 | 29 | public: 30 | CFindReplaceDialogWithMessageFilter(CMessageLoop* messageLoop) : 31 | m_messageLoop(messageLoop) { 32 | // Override buffers and sizes to have a larger limit. The default limit is 33 | // 128 bytes, not enough. 34 | m_fr.lpstrFindWhat = m_findText.data(); 35 | m_fr.wFindWhatLen = kFindReplaceBufferSize; 36 | m_fr.lpstrReplaceWith = m_replaceText.data(); 37 | m_fr.wReplaceWithLen = kFindReplaceBufferSize; 38 | } 39 | 40 | public: 41 | BOOL PreTranslateMessage(MSG* pMsg) 42 | { 43 | HWND hWndFocus = ::GetFocus(); 44 | if((m_hWnd == hWndFocus) || this->IsChild(hWndFocus)) 45 | return this->IsDialogMessage(pMsg); 46 | 47 | return FALSE; 48 | } 49 | 50 | virtual void OnFinalMessage(HWND /*hWnd*/) 51 | { 52 | delete this; 53 | } 54 | 55 | BEGIN_MSG_MAP(CFindReplaceDialogWithMessageFilter) 56 | MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) 57 | MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 58 | END_MSG_MAP() 59 | 60 | LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) 61 | { 62 | if(m_messageLoop) 63 | m_messageLoop->AddMessageFilter(this); 64 | 65 | bHandled = FALSE; 66 | return 0; 67 | } 68 | 69 | LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) 70 | { 71 | if(m_messageLoop) 72 | m_messageLoop->RemoveMessageFilter(this); 73 | 74 | bHandled = FALSE; 75 | return 0; 76 | } 77 | }; 78 | 79 | #endif //__FindReplaceDialogWithMessageFilter_h__ 80 | -------------------------------------------------------------------------------- /libraries/dia/diacreate.h: -------------------------------------------------------------------------------- 1 | // diacreate.h - creation helper functions for DIA initialization 2 | //----------------------------------------------------------------- 3 | // 4 | // Copyright Microsoft Corporation. All Rights Reserved. 5 | // 6 | //--------------------------------------------------------------- 7 | #ifndef _DIACREATE_H_ 8 | #define _DIACREATE_H_ 9 | 10 | // 11 | // Create a dia data source object from the dia dll (by dll name - does not access the registry). 12 | // 13 | 14 | HRESULT STDMETHODCALLTYPE NoRegCoCreate( const __wchar_t *dllName, 15 | REFCLSID rclsid, 16 | REFIID riid, 17 | void **ppv); 18 | 19 | #ifndef _NATIVE_WCHAR_T_DEFINED 20 | #ifdef __cplusplus 21 | 22 | HRESULT STDMETHODCALLTYPE NoRegCoCreate( const wchar_t *dllName, 23 | REFCLSID rclsid, 24 | REFIID riid, 25 | void **ppv) 26 | { 27 | return NoRegCoCreate( (const __wchar_t *)dllName, rclsid, riid, ppv ); 28 | } 29 | 30 | #endif 31 | #endif 32 | 33 | 34 | 35 | // 36 | // Create a dia data source object from the dia dll (looks up the class id in the registry). 37 | // 38 | HRESULT STDMETHODCALLTYPE NoOleCoCreate( REFCLSID rclsid, 39 | REFIID riid, 40 | void **ppv); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /libraries/dia/lib/amd64/diaguids.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramensoftware/windhawk-symbol-helper/82ba32fbe8e22ca8f1495c2786010ea9bc3a79f3/libraries/dia/lib/amd64/diaguids.lib -------------------------------------------------------------------------------- /libraries/dia/lib/arm/diaguids.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramensoftware/windhawk-symbol-helper/82ba32fbe8e22ca8f1495c2786010ea9bc3a79f3/libraries/dia/lib/arm/diaguids.lib -------------------------------------------------------------------------------- /libraries/dia/lib/arm64/diaguids.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramensoftware/windhawk-symbol-helper/82ba32fbe8e22ca8f1495c2786010ea9bc3a79f3/libraries/dia/lib/arm64/diaguids.lib -------------------------------------------------------------------------------- /libraries/dia/lib/diaguids.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramensoftware/windhawk-symbol-helper/82ba32fbe8e22ca8f1495c2786010ea9bc3a79f3/libraries/dia/lib/diaguids.lib -------------------------------------------------------------------------------- /libraries/wil/_version.txt: -------------------------------------------------------------------------------- 1 | https://github.com/microsoft/wil/tree/a6c9f2d8520e830b04c47c3c395bac428696cc0d 2 | + 3 | https://github.com/microsoft/wil/pull/357 4 | -------------------------------------------------------------------------------- /libraries/wil/com_apartment_variable.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_COM_APARTMENT_VARIABLE_INCLUDED 12 | #define __WIL_COM_APARTMENT_VARIABLE_INCLUDED 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "com.h" 22 | #include "cppwinrt.h" 23 | #include "result_macros.h" 24 | #include "win32_helpers.h" 25 | 26 | #ifndef WIL_ENABLE_EXCEPTIONS 27 | #error This header requires exceptions 28 | #endif 29 | 30 | namespace wil 31 | { 32 | // Determine if apartment variables are supported in the current process context. 33 | // Prior to build 22365, the APIs needed to create apartment variables (e.g. RoGetApartmentIdentifier) 34 | // failed for unpackaged processes. For MS people, see http://task.ms/31861017 for details. 35 | // APIs needed to implement apartment variables did not work in non-packaged processes. 36 | inline bool are_apartment_variables_supported() 37 | { 38 | unsigned long long apartmentId{}; 39 | return RoGetApartmentIdentifier(&apartmentId) != HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE); 40 | } 41 | 42 | // COM will implicitly rundown the apartment registration when it invokes a handler 43 | // and blocks calling unregister when executing the callback. So be careful to release() 44 | // this when callback is invoked to avoid a double free of the cookie. 45 | using unique_apartment_shutdown_registration = unique_any; 46 | 47 | struct apartment_variable_platform 48 | { 49 | static unsigned long long GetApartmentId() 50 | { 51 | unsigned long long apartmentId{}; 52 | FAIL_FAST_IF_FAILED(RoGetApartmentIdentifier(&apartmentId)); 53 | return apartmentId; 54 | } 55 | 56 | static auto RegisterForApartmentShutdown(IApartmentShutdown* observer) 57 | { 58 | unsigned long long id{}; 59 | shutdown_type cookie; 60 | THROW_IF_FAILED(RoRegisterForApartmentShutdown(observer, &id, cookie.put())); 61 | return cookie; 62 | } 63 | 64 | static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie) 65 | { 66 | FAIL_FAST_IF_FAILED(RoUnregisterForApartmentShutdown(cookie)); 67 | } 68 | 69 | static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) 70 | { 71 | return wil::CoInitializeEx(coinitFlags); 72 | } 73 | 74 | // disable the test hook 75 | inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = INFINITE; 76 | 77 | using shutdown_type = wil::unique_apartment_shutdown_registration; 78 | }; 79 | 80 | enum class apartment_variable_leak_action { fail_fast, ignore }; 81 | 82 | // "pins" the current module in memory by incrementing the module reference count and leaking that. 83 | inline void ensure_module_stays_loaded() 84 | { 85 | static INIT_ONCE s_initLeakModule{}; // avoiding magic statics 86 | wil::init_once_failfast(s_initLeakModule, []() 87 | { 88 | HMODULE result{}; 89 | FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, L"", &result)); 90 | return S_OK; 91 | }); 92 | } 93 | 94 | namespace details 95 | { 96 | // For the address of data, you can detect global variables by the ability to resolve the module from the address. 97 | inline bool IsGlobalVariable(const void* moduleAddress) noexcept 98 | { 99 | wil::unique_hmodule moduleHandle; 100 | return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast(moduleAddress), &moduleHandle) != FALSE; 101 | } 102 | 103 | struct any_maker_base 104 | { 105 | std::any(*adapter)(void*); 106 | void* inner; 107 | 108 | WI_NODISCARD std::any operator()() const 109 | { 110 | return adapter(inner); 111 | } 112 | }; 113 | 114 | template 115 | struct any_maker : any_maker_base 116 | { 117 | any_maker() 118 | { 119 | adapter = [](auto) -> std::any { return T{}; }; 120 | } 121 | 122 | any_maker(T(*maker)()) 123 | { 124 | adapter = [](auto maker) -> std::any { return reinterpret_cast(maker)(); }; 125 | inner = reinterpret_cast(maker); 126 | } 127 | 128 | template 129 | any_maker(F&& f) 130 | { 131 | adapter = [](auto maker) -> std::any { return reinterpret_cast(maker)[0](); }; 132 | inner = std::addressof(f); 133 | } 134 | }; 135 | 136 | template 138 | struct apartment_variable_base 139 | { 140 | inline static winrt::slim_mutex s_lock; 141 | 142 | struct apartment_variable_storage 143 | { 144 | apartment_variable_storage(apartment_variable_storage&& other) noexcept = default; 145 | apartment_variable_storage(const apartment_variable_storage& other) = delete; 146 | 147 | apartment_variable_storage(typename test_hook::shutdown_type&& cookie_) : cookie(std::move(cookie_)) 148 | { 149 | } 150 | 151 | winrt::apartment_context context; 152 | typename test_hook::shutdown_type cookie; 153 | // Variables are stored using the address of the apartment_variable_base<> as the key. 154 | std::unordered_map*, std::any> variables; 155 | }; 156 | 157 | // Apartment id -> variables storage. 158 | inline static wil::object_without_destructor_on_shutdown< 159 | std::unordered_map> 160 | s_apartmentStorage; 161 | 162 | constexpr apartment_variable_base() = default; 163 | ~apartment_variable_base() 164 | { 165 | // Global variables (object with static storage duration) 166 | // are run down when the process is shutting down or when the 167 | // dll is unloaded. At these points it is not possible to start 168 | // an async operation and the work performed is not needed, 169 | // the apartments with variable have been run down already. 170 | const auto isGlobal = details::IsGlobalVariable(this); 171 | if (!isGlobal) 172 | { 173 | clear_all_apartments_async(); 174 | } 175 | 176 | if constexpr (leak_action == apartment_variable_leak_action::fail_fast) 177 | { 178 | if (isGlobal && !ProcessShutdownInProgress()) 179 | { 180 | // If you hit this fail fast it means the storage in s_apartmentStorage will be leaked. 181 | // For apartment variables used in .exes, this is expected and 182 | // this fail fast should be disabled using 183 | // wil::apartment_variable 184 | // 185 | // For DLLs, if this is expected, disable this fail fast using 186 | // wil::apartment_variable 187 | // 188 | // Use of apartment variables in DLLs only loaded by COM will never hit this case 189 | // as COM will unload DLLs before apartments are rundown, 190 | // providing the opportunity to empty s_apartmentStorage. 191 | // 192 | // But DLLs loaded and unloaded to call DLL entry points (outside of COM) may 193 | // create variable storage that can't be cleaned up as the DLL lifetime is 194 | // shorter that the COM lifetime. In these cases either 195 | // 1) accept the leaks and disable the fail fast as describe above 196 | // 2) disable module unloading by calling wil::ensure_module_stays_loaded 197 | // 3) CoCreate an object from this DLL to make COM aware of the DLL 198 | FAIL_FAST_IF(!s_apartmentStorage.get().empty()); 199 | } 200 | } 201 | } 202 | 203 | // non-copyable, non-assignable 204 | apartment_variable_base(apartment_variable_base const&) = delete; 205 | void operator=(apartment_variable_base const&) = delete; 206 | 207 | // get current value or throw if no value has been set 208 | std::any& get_existing() 209 | { 210 | if (auto any = get_if()) 211 | { 212 | return *any; 213 | } 214 | THROW_HR(E_NOT_SET); 215 | } 216 | 217 | static apartment_variable_storage* get_current_apartment_variable_storage() 218 | { 219 | auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId()); 220 | if (storage != s_apartmentStorage.get().end()) 221 | { 222 | return &storage->second; 223 | } 224 | return nullptr; 225 | } 226 | 227 | apartment_variable_storage* ensure_current_apartment_variables() 228 | { 229 | auto variables = get_current_apartment_variable_storage(); 230 | if (variables) 231 | { 232 | return variables; 233 | } 234 | 235 | struct ApartmentObserver : public winrt::implements 236 | { 237 | void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override 238 | { 239 | // This code runs at apartment rundown so be careful to avoid deadlocks by 240 | // extracting the variables under the lock then release them outside. 241 | auto variables = [apartmentId]() 242 | { 243 | auto lock = winrt::slim_lock_guard(s_lock); 244 | return s_apartmentStorage.get().extract(apartmentId); 245 | }(); 246 | WI_ASSERT(variables.key() == apartmentId); 247 | // The system implicitly releases the shutdown observer 248 | // after invoking the callback and does not allow calling unregister 249 | // in the callback. So release the reference to the registration. 250 | variables.mapped().cookie.release(); 251 | } 252 | }; 253 | auto shutdownRegistration = test_hook::RegisterForApartmentShutdown(winrt::make().get()); 254 | return &s_apartmentStorage.get().insert({ test_hook::GetApartmentId(), apartment_variable_storage(std::move(shutdownRegistration)) }).first->second; 255 | } 256 | 257 | // get current value or custom-construct one on demand 258 | template 259 | std::any& get_or_create(any_maker && creator) 260 | { 261 | apartment_variable_storage* variable_storage = nullptr; 262 | 263 | { // scope for lock 264 | auto lock = winrt::slim_lock_guard(s_lock); 265 | variable_storage = ensure_current_apartment_variables(); 266 | 267 | auto variable = variable_storage->variables.find(this); 268 | if (variable != variable_storage->variables.end()) 269 | { 270 | return variable->second; 271 | } 272 | } // drop the lock 273 | 274 | // create the object outside the lock to avoid reentrant deadlock 275 | auto value = creator(); 276 | 277 | auto insert_lock = winrt::slim_lock_guard(s_lock); 278 | // The insertion may fail if creator() recursively caused itself to be created, 279 | // in which case we return the existing object and the falsely-created one is discarded. 280 | return variable_storage->variables.insert({ this, std::move(value) }).first->second; 281 | } 282 | 283 | // get pointer to current value or nullptr if no value has been set 284 | std::any* get_if() 285 | { 286 | auto lock = winrt::slim_lock_guard(s_lock); 287 | 288 | if (auto variable_storage = get_current_apartment_variable_storage()) 289 | { 290 | auto variable = variable_storage->variables.find(this); 291 | if (variable != variable_storage->variables.end()) 292 | { 293 | return &(variable->second); 294 | } 295 | } 296 | return nullptr; 297 | } 298 | 299 | // replace or create the current value, fail fasts if the value is not already stored 300 | void set(std::any value) 301 | { 302 | // release value, with the swapped value, outside of the lock 303 | { 304 | auto lock = winrt::slim_lock_guard(s_lock); 305 | auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId()); 306 | FAIL_FAST_IF(storage == s_apartmentStorage.get().end()); 307 | auto& variable_storage = storage->second; 308 | auto variable = variable_storage.variables.find(this); 309 | FAIL_FAST_IF(variable == variable_storage.variables.end()); 310 | variable->second.swap(value); 311 | } 312 | } 313 | 314 | // remove any current value 315 | void clear() 316 | { 317 | auto lock = winrt::slim_lock_guard(s_lock); 318 | if (auto variable_storage = get_current_apartment_variable_storage()) 319 | { 320 | variable_storage->variables.erase(this); 321 | if (variable_storage->variables.size() == 0) 322 | { 323 | s_apartmentStorage.get().erase(test_hook::GetApartmentId()); 324 | } 325 | } 326 | } 327 | 328 | winrt::Windows::Foundation::IAsyncAction clear_all_apartments_async() 329 | { 330 | // gather all the apartments that hold objects we need to destruct 331 | // (do not gather the objects themselves, because the apartment might 332 | // destruct before we get around to it, and we should let the apartment 333 | // destruct the object while it still can). 334 | 335 | std::vector contexts; 336 | { // scope for lock 337 | auto lock = winrt::slim_lock_guard(s_lock); 338 | for (auto& [id, storage] : s_apartmentStorage.get()) 339 | { 340 | auto variable = storage.variables.find(this); 341 | if (variable != storage.variables.end()) 342 | { 343 | contexts.push_back(storage.context); 344 | } 345 | } 346 | } 347 | 348 | if (contexts.empty()) 349 | { 350 | co_return; 351 | } 352 | 353 | wil::unique_mta_usage_cookie mta_reference; // need to extend the MTA due to async cleanup 354 | FAIL_FAST_IF_FAILED(CoIncrementMTAUsage(mta_reference.put())); 355 | 356 | // From a background thread hop into each apartment to run down the object 357 | // if it's still there. 358 | co_await winrt::resume_background(); 359 | 360 | // This hook enables testing the case where execution of this method loses the race with 361 | // apartment rundown by other means. 362 | if constexpr (test_hook::AsyncRundownDelayForTestingRaces != INFINITE) 363 | { 364 | Sleep(test_hook::AsyncRundownDelayForTestingRaces); 365 | } 366 | 367 | for (auto&& context : contexts) 368 | { 369 | try 370 | { 371 | co_await context; 372 | clear(); 373 | } 374 | catch (winrt::hresult_error const& e) 375 | { 376 | // Ignore failure if apartment ran down before we could clean it up. 377 | // The object already ran down as part of apartment cleanup. 378 | if ((e.code() != RPC_E_SERVER_DIED_DNE) && 379 | (e.code() != RPC_E_DISCONNECTED)) 380 | { 381 | throw; 382 | } 383 | } 384 | catch (...) 385 | { 386 | FAIL_FAST(); 387 | } 388 | } 389 | } 390 | 391 | static const auto& storage() 392 | { 393 | return s_apartmentStorage.get(); 394 | } 395 | 396 | static size_t current_apartment_variable_count() 397 | { 398 | auto lock = winrt::slim_lock_guard(s_lock); 399 | if (auto variable_storage = get_current_apartment_variable_storage()) 400 | { 401 | return variable_storage->variables.size(); 402 | } 403 | return 0; 404 | } 405 | }; 406 | } 407 | 408 | // Apartment variables enable storing COM objects safely in globals 409 | // (objects with static storage duration) by creating a unique copy 410 | // in each apartment and managing their lifetime based on apartment rundown 411 | // notifications. 412 | // They can also be used for automatic or dynamic storage duration but those 413 | // cases are less common. 414 | // This type is also useful for storing references to apartment affine objects. 415 | // 416 | // Note, that apartment variables hosted in a COM DLL need to integrate with 417 | // the DllCanUnloadNow() function to include the ref counts contributed by 418 | // C++ WinRT objects. This is automatic for DLLs that host C++ WinRT objects 419 | // but WRL projects will need to be updated to call winrt::get_module_lock(). 420 | 421 | template 423 | struct apartment_variable : details::apartment_variable_base 424 | { 425 | using base = details::apartment_variable_base; 426 | 427 | constexpr apartment_variable() = default; 428 | 429 | // Get current value or throw if no value has been set. 430 | T& get_existing() { return std::any_cast(base::get_existing()); } 431 | 432 | // Get current value or default-construct one on demand. 433 | T& get_or_create() 434 | { 435 | return std::any_cast(base::get_or_create(details::any_maker())); 436 | } 437 | 438 | // Get current value or custom-construct one on demand. 439 | template 440 | T& get_or_create(F&& f) 441 | { 442 | return std::any_cast(base::get_or_create(details::any_maker(std::forward(f)))); 443 | } 444 | 445 | // get pointer to current value or nullptr if no value has been set 446 | T* get_if() { return std::any_cast(base::get_if()); } 447 | 448 | // replace or create the current value, fail fasts if the value is not already stored 449 | template void set(V&& value) { return base::set(std::forward(value)); } 450 | 451 | // Clear the value in the current apartment. 452 | using base::clear; 453 | 454 | // Asynchronously clear the value in all apartments it is present in. 455 | using base::clear_all_apartments_async; 456 | 457 | // For testing only. 458 | // 1) To observe the state of the storage in the debugger assign this to 459 | // a temporary variable (const&) and watch its contents. 460 | // 2) Use this to test the implementation. 461 | using base::storage; 462 | // For testing only. The number of variables in the current apartment. 463 | using base::current_apartment_variable_count; 464 | }; 465 | } 466 | 467 | #endif // __WIL_COM_APARTMENT_VARIABLE_INCLUDED 468 | -------------------------------------------------------------------------------- /libraries/wil/cppwinrt.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_CPPWINRT_INCLUDED 12 | #define __WIL_CPPWINRT_INCLUDED 13 | 14 | #include "common.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to 21 | // understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to 22 | // C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below - 23 | // into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global 24 | // function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and 25 | // 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined. 26 | 27 | /// @cond 28 | namespace wil::details 29 | { 30 | // Since the C++/WinRT version macro is a string... 31 | // For example: "2.0.221104.6" 32 | inline constexpr int version_from_string(const char* versionString) 33 | { 34 | int result = 0; 35 | while ((*versionString >= '0') && (*versionString <= '9')) 36 | { 37 | result = result * 10 + (*versionString - '0'); 38 | ++versionString; 39 | } 40 | 41 | return result; 42 | } 43 | 44 | inline constexpr int major_version_from_string(const char* versionString) 45 | { 46 | return version_from_string(versionString); 47 | } 48 | 49 | inline constexpr int minor_version_from_string(const char* versionString) 50 | { 51 | int dotCount = 0; 52 | while ((*versionString != '\0')) 53 | { 54 | if (*versionString == '.') 55 | { 56 | ++dotCount; 57 | } 58 | 59 | ++versionString; 60 | if (dotCount == 2) 61 | { 62 | return version_from_string(versionString); 63 | } 64 | } 65 | 66 | return 0; 67 | } 68 | } 69 | /// @endcond 70 | 71 | #ifdef CPPWINRT_VERSION 72 | // Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of 73 | // 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer 74 | // problematic, so only emit an error when using a version of C++/WinRT prior to 2.0 75 | static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, 76 | "Please include wil/cppwinrt.h before including any C++/WinRT headers"); 77 | #endif 78 | 79 | // NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed 80 | #ifdef WINRT_EXTERNAL_CATCH_CLAUSE 81 | #define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1 82 | #else 83 | #define WINRT_EXTERNAL_CATCH_CLAUSE \ 84 | catch (const wil::ResultException& e) \ 85 | { \ 86 | return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \ 87 | } 88 | #endif 89 | 90 | #include "result_macros.h" 91 | #include 92 | 93 | #if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 94 | static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, 95 | "C++/WinRT external catch clause already defined outside of WIL"); 96 | #endif 97 | 98 | // In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid 99 | // linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to 100 | // use it unless the version of C++/WinRT is high enough 101 | extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept; 102 | 103 | // The same is true with this function pointer as well, except that the version must be 2.X or higher. 104 | extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept; 105 | 106 | /// @cond 107 | namespace wil::details 108 | { 109 | inline void MaybeGetExceptionString( 110 | const winrt::hresult_error& exception, 111 | _Out_writes_opt_(debugStringChars) PWSTR debugString, 112 | _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) 113 | { 114 | if (debugString) 115 | { 116 | StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str()); 117 | } 118 | } 119 | 120 | inline HRESULT __stdcall ResultFromCaughtException_CppWinRt( 121 | _Inout_updates_opt_(debugStringChars) PWSTR debugString, 122 | _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, 123 | _Inout_ bool* isNormalized) noexcept 124 | { 125 | if (g_pfnResultFromCaughtException) 126 | { 127 | try 128 | { 129 | throw; 130 | } 131 | catch (const ResultException& exception) 132 | { 133 | *isNormalized = true; 134 | MaybeGetExceptionString(exception, debugString, debugStringChars); 135 | return exception.GetErrorCode(); 136 | } 137 | catch (const winrt::hresult_error& exception) 138 | { 139 | MaybeGetExceptionString(exception, debugString, debugStringChars); 140 | return exception.to_abi(); 141 | } 142 | catch (const std::bad_alloc& exception) 143 | { 144 | MaybeGetExceptionString(exception, debugString, debugStringChars); 145 | return E_OUTOFMEMORY; 146 | } 147 | catch (const std::out_of_range& exception) 148 | { 149 | MaybeGetExceptionString(exception, debugString, debugStringChars); 150 | return E_BOUNDS; 151 | } 152 | catch (const std::invalid_argument& exception) 153 | { 154 | MaybeGetExceptionString(exception, debugString, debugStringChars); 155 | return E_INVALIDARG; 156 | } 157 | catch (...) 158 | { 159 | auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); 160 | if (FAILED(hr)) 161 | { 162 | return hr; 163 | } 164 | } 165 | } 166 | else 167 | { 168 | try 169 | { 170 | throw; 171 | } 172 | catch (const ResultException& exception) 173 | { 174 | *isNormalized = true; 175 | MaybeGetExceptionString(exception, debugString, debugStringChars); 176 | return exception.GetErrorCode(); 177 | } 178 | catch (const winrt::hresult_error& exception) 179 | { 180 | MaybeGetExceptionString(exception, debugString, debugStringChars); 181 | return exception.to_abi(); 182 | } 183 | catch (const std::bad_alloc& exception) 184 | { 185 | MaybeGetExceptionString(exception, debugString, debugStringChars); 186 | return E_OUTOFMEMORY; 187 | } 188 | catch (const std::out_of_range& exception) 189 | { 190 | MaybeGetExceptionString(exception, debugString, debugStringChars); 191 | return E_BOUNDS; 192 | } 193 | catch (const std::invalid_argument& exception) 194 | { 195 | MaybeGetExceptionString(exception, debugString, debugStringChars); 196 | return E_INVALIDARG; 197 | } 198 | catch (const std::exception& exception) 199 | { 200 | MaybeGetExceptionString(exception, debugString, debugStringChars); 201 | return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); 202 | } 203 | catch (...) 204 | { 205 | // Fall through to returning 'S_OK' below 206 | } 207 | } 208 | 209 | // Tell the caller that we were unable to map the exception by succeeding... 210 | return S_OK; 211 | } 212 | } 213 | /// @endcond 214 | 215 | namespace wil 216 | { 217 | inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept 218 | { 219 | // C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't 220 | // have accurate file/line/etc. information 221 | return static_cast(details::ReportFailure_CaughtException(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress))); 222 | } 223 | 224 | inline void __stdcall winrt_throw_hresult(uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept 225 | { 226 | void* callerReturnAddress{nullptr}; PCSTR code{nullptr}; 227 | wil::details::ReportFailure_Hr(__R_FN_CALL_FULL __R_COMMA result); 228 | } 229 | 230 | inline void WilInitialize_CppWinRT() 231 | { 232 | details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt; 233 | if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2) 234 | { 235 | WI_ASSERT(winrt_to_hresult_handler == nullptr); 236 | winrt_to_hresult_handler = winrt_to_hresult; 237 | 238 | if constexpr (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122) 239 | { 240 | WI_ASSERT(winrt_throw_hresult_handler == nullptr); 241 | winrt_throw_hresult_handler = winrt_throw_hresult; 242 | } 243 | } 244 | } 245 | 246 | /// @cond 247 | namespace details 248 | { 249 | #ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS 250 | WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0") 251 | WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, [] 252 | { 253 | ::wil::WilInitialize_CppWinRT(); 254 | return 1; 255 | }); 256 | #else 257 | WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1") 258 | #endif 259 | } 260 | /// @endcond 261 | 262 | // Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type. 263 | inline long verify_hresult(winrt::hresult hr) noexcept 264 | { 265 | return hr; 266 | } 267 | 268 | // Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience. 269 | template 270 | auto get_abi(T const& object) noexcept 271 | { 272 | return winrt::get_abi(object); 273 | } 274 | 275 | inline auto get_abi(winrt::hstring const& object) noexcept 276 | { 277 | return static_cast(winrt::get_abi(object)); 278 | } 279 | 280 | inline auto str_raw_ptr(const winrt::hstring& str) noexcept 281 | { 282 | return str.c_str(); 283 | } 284 | 285 | template 286 | auto put_abi(T& object) noexcept 287 | { 288 | return winrt::put_abi(object); 289 | } 290 | 291 | inline auto put_abi(winrt::hstring& object) noexcept 292 | { 293 | return reinterpret_cast(winrt::put_abi(object)); 294 | } 295 | 296 | inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept 297 | { 298 | return static_cast<::IUnknown*>(winrt::get_abi(ptr)); 299 | } 300 | 301 | // Needed to power wil::cx_object_from_abi that requires IInspectable 302 | inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept 303 | { 304 | return static_cast<::IInspectable*>(winrt::get_abi(ptr)); 305 | } 306 | 307 | // Taken from the docs.microsoft.com article 308 | template 309 | T convert_from_abi(::IUnknown* from) 310 | { 311 | T to{ nullptr }; // `T` is a projected type. 312 | winrt::check_hresult(from->QueryInterface(winrt::guid_of(), winrt::put_abi(to))); 313 | return to; 314 | } 315 | 316 | // For obtaining an object from an interop method on the factory. Example: 317 | // winrt::InputPane inputPane = wil::capture_interop(&IInputPaneInterop::GetForWindow, hwnd); 318 | // If the method produces something different from the factory type: 319 | // winrt::IAsyncAction action = wil::capture_interop(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd); 320 | template 321 | auto capture_interop(HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args) 322 | { 323 | auto interop = winrt::get_activation_factory(); 324 | return winrt::capture(interop, method, std::forward(args)...); 325 | } 326 | 327 | // For obtaining an object from an interop method on an instance. Example: 328 | // winrt::UserActivitySession session = wil::capture_interop(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd); 329 | template 330 | auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args) 331 | { 332 | return winrt::capture(o.as(), method, std::forward(args)...); 333 | } 334 | 335 | /** Holds a reference to the host C++/WinRT module to prevent it from being unloaded. 336 | Normally, this is done by being in an IAsyncOperation coroutine or by holding a strong 337 | reference to a C++/WinRT object hosted in the same module, but if you have neither, 338 | you will need to hold a reference explicitly. For the WRL equivalent, see wrl_module_reference. 339 | 340 | This can be used as a base, which permits EBO: 341 | ~~~~ 342 | struct NonWinrtObject : wil::winrt_module_reference 343 | { 344 | int value; 345 | }; 346 | 347 | // DLL will not be unloaded as long as NonWinrtObject is still alive. 348 | auto p = std::make_unique(); 349 | ~~~~ 350 | 351 | Or it can be used as a member (with [[no_unique_address]] to avoid 352 | occupying any memory): 353 | ~~~~ 354 | struct NonWinrtObject 355 | { 356 | int value; 357 | 358 | [[no_unique_address]] wil::winrt_module_reference module_ref; 359 | }; 360 | 361 | // DLL will not be unloaded as long as NonWinrtObject is still alive. 362 | auto p = std::make_unique(); 363 | ~~~~ 364 | 365 | If using it to prevent the host DLL from unloading while a thread 366 | or threadpool work item is still running, create the object before 367 | starting the thread, and pass it to the thread. This avoids a race 368 | condition where the host DLL could get unloaded before the thread starts. 369 | ~~~~ 370 | std::thread([module_ref = wil::winrt_module_reference()]() { do_background_work(); }); 371 | 372 | // Don't do this (race condition) 373 | std::thread([]() { wil::winrt_module_reference module_ref; do_background_work(); }); // WRONG 374 | ~~~~ 375 | 376 | Also useful in coroutines that neither capture DLL-hosted COM objects, nor are themselves 377 | DLL-hosted COM objects. (If the coroutine returns IAsyncAction or captures a get_strong() 378 | of its containing WinRT class, then the IAsyncAction or strong reference will itself keep 379 | a strong reference to the host module.) 380 | ~~~~ 381 | winrt::fire_and_forget ContinueBackgroundWork() 382 | { 383 | // prevent DLL from unloading while we are running on a background thread. 384 | // Do this before switching to the background thread. 385 | wil::winrt_module_reference module_ref; 386 | 387 | co_await winrt::resume_background(); 388 | do_background_work(); 389 | }; 390 | ~~~~ 391 | */ 392 | struct [[nodiscard]] winrt_module_reference 393 | { 394 | winrt_module_reference() 395 | { 396 | ++winrt::get_module_lock(); 397 | } 398 | 399 | winrt_module_reference(winrt_module_reference const&) : winrt_module_reference() {} 400 | 401 | ~winrt_module_reference() 402 | { 403 | --winrt::get_module_lock(); 404 | } 405 | }; 406 | 407 | /** Implements a C++/WinRT class where some interfaces are conditionally supported. 408 | ~~~~ 409 | // Assume the existence of a class "Version2" which says whether 410 | // the IMyThing2 interface should be supported. 411 | struct Version2 { static bool IsEnabled(); }; 412 | 413 | // Declare implementation class which conditionally supports IMyThing2. 414 | struct MyThing : wil::winrt_conditionally_implements, 415 | Version2, IMyThing2> 416 | { 417 | // implementation goes here 418 | }; 419 | 420 | ~~~~ 421 | 422 | If `Version2::IsEnabled()` returns `false`, then the `QueryInterface` 423 | for `IMyThing2` will fail. 424 | 425 | Any interface not listed as conditional is assumed to be enabled unconditionally. 426 | 427 | You can add additional Version / Interface pairs to the template parameter list. 428 | Interfaces may be conditionalized on at most one Version class. If you need a 429 | complex conditional, create a new helper class. 430 | 431 | ~~~~ 432 | // Helper class for testing two Versions. 433 | struct Version2_or_greater { 434 | static bool IsEnabled() { return Version2::IsEnabled() || Version3::IsEnabled(); } 435 | }; 436 | 437 | // This implementation supports IMyThing2 if either Version2 or Version3 is enabled, 438 | // and supports IMyThing3 only if Version3 is enabled. 439 | struct MyThing : wil::winrt_conditionally_implements, 440 | Version2_or_greater, IMyThing2, Version3, IMyThing3> 441 | { 442 | // implementation goes here 443 | }; 444 | ~~~~ 445 | */ 446 | template 447 | struct winrt_conditionally_implements : Implements 448 | { 449 | using Implements::Implements; 450 | 451 | void* find_interface(winrt::guid const& iid) const noexcept override 452 | { 453 | static_assert(sizeof...(Rest) % 2 == 0, "Extra template parameters should come in groups of two"); 454 | if (is_enabled<0, std::tuple>(iid)) 455 | { 456 | return Implements::find_interface(iid); 457 | } 458 | return nullptr; 459 | } 460 | 461 | private: 462 | template 463 | static bool is_enabled(winrt::guid const& iid) 464 | { 465 | if constexpr (index >= std::tuple_size_v) 466 | { 467 | return true; 468 | } 469 | else 470 | { 471 | check_no_duplicates<1, index + 1, Tuple>(); 472 | return (iid == winrt::guid_of>()) ? 473 | std::tuple_element_t::IsEnabled() : 474 | is_enabled(iid); 475 | } 476 | } 477 | 478 | template 479 | static constexpr void check_no_duplicates() 480 | { 481 | if constexpr (index < upto) 482 | { 483 | static_assert(!std::is_same_v, std::tuple_element_t>, 484 | "Duplicate interfaces found in winrt_conditionally_implements"); 485 | check_no_duplicates(); 486 | } 487 | } 488 | }; 489 | } 490 | 491 | #endif // __WIL_CPPWINRT_INCLUDED 492 | -------------------------------------------------------------------------------- /libraries/wil/cppwinrt_authoring.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | 12 | namespace wil 13 | { 14 | #ifndef __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED 15 | #define __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED 16 | namespace details 17 | { 18 | template 19 | struct single_threaded_property_storage 20 | { 21 | T m_value{}; 22 | single_threaded_property_storage() = default; 23 | single_threaded_property_storage(const T& value) : m_value(value) {} 24 | operator T& () { return m_value; } 25 | operator T const& () const { return m_value; } 26 | template auto operator=(Q&& q) 27 | { 28 | m_value = wistd::forward(q); 29 | return *this; 30 | } 31 | }; 32 | } 33 | 34 | template 35 | struct single_threaded_property : std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T> 36 | { 37 | single_threaded_property() = default; 38 | template single_threaded_property(TArgs&&... value) : base_type(std::forward(value)...) {} 39 | 40 | using base_type = std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T>; 41 | 42 | T operator()() const 43 | { 44 | return *this; 45 | } 46 | 47 | template auto& operator()(Q&& q) 48 | { 49 | *this = std::forward(q); 50 | return *this; 51 | } 52 | 53 | template auto& operator=(Q&& q) 54 | { 55 | static_cast(*this) = std::forward(q); 56 | return *this; 57 | } 58 | }; 59 | 60 | template 61 | struct single_threaded_rw_property : single_threaded_property 62 | { 63 | using base_type = single_threaded_property; 64 | template single_threaded_rw_property(TArgs&&... value) : base_type(std::forward(value)...) {} 65 | 66 | using base_type::operator(); 67 | 68 | // needed in lieu of deducing-this 69 | template auto& operator()(Q&& q) 70 | { 71 | return *this = std::forward(q); 72 | } 73 | 74 | // needed in lieu of deducing-this 75 | template auto& operator=(Q&& q) 76 | { 77 | base_type::operator=(std::forward(q)); 78 | return *this; 79 | } 80 | }; 81 | 82 | #endif // __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED 83 | 84 | #if !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H) // WinRT / XAML helpers 85 | #define __WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION 86 | namespace details 87 | { 88 | template 89 | struct event_base { 90 | winrt::event_token operator()(const T& handler) 91 | { 92 | return m_handler.add(handler); 93 | } 94 | 95 | auto operator()(const winrt::event_token& token) noexcept 96 | { 97 | return m_handler.remove(token); 98 | } 99 | 100 | template 101 | auto invoke(TArgs&&... args) 102 | { 103 | return m_handler(std::forward(args)...); 104 | } 105 | private: 106 | winrt::event m_handler; 107 | }; 108 | } 109 | 110 | /** 111 | * @brief A default event handler that maps to [Windows.Foundation.EventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.eventhandler-1). 112 | * @tparam T The event data type. 113 | */ 114 | template 115 | struct untyped_event : wil::details::event_base> {}; 116 | 117 | /** 118 | * @brief A default event handler that maps to [Windows.Foundation.TypedEventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.typedeventhandler-2). 119 | * @tparam T The event data type. 120 | * @details Usage example: 121 | * @code 122 | * // In IDL, this corresponds to: 123 | * // event Windows.Foundation.TypedEventHandler OkClicked; 124 | * wil::typed_event OkClicked; 125 | * @endcode 126 | */ 127 | template 128 | struct typed_event : wil::details::event_base> {}; 129 | 130 | #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H) 131 | 132 | #if !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) // INotifyPropertyChanged helpers 133 | #define __WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA 134 | namespace details 135 | { 136 | #ifdef WINRT_Microsoft_UI_Xaml_Data_H 137 | using Xaml_Data_PropertyChangedEventHandler = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler; 138 | using Xaml_Data_PropertyChangedEventArgs = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventArgs; 139 | #elif defined(WINRT_Windows_UI_Xaml_Data_H) 140 | using Xaml_Data_PropertyChangedEventHandler = winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler; 141 | using Xaml_Data_PropertyChangedEventArgs = winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs; 142 | #endif 143 | } 144 | 145 | /** 146 | * @brief Helper base class to inherit from to have a simple implementation of [INotifyPropertyChanged](https://docs.microsoft.com/uwp/api/windows.ui.xaml.data.inotifypropertychanged). 147 | * @tparam T CRTP type 148 | * @details When you declare your class, make this class a base class and pass your class as a template parameter: 149 | * @code 150 | * struct MyPage : MyPageT, wil::notify_property_changed_base 151 | * { 152 | * wil::single_threaded_notifying_property MyInt; 153 | * MyPage() : INIT_NOTIFYING_PROPERTY(MyInt, 42) { } 154 | * // or 155 | * WIL_NOTIFYING_PROPERTY(int, MyInt, 42); 156 | * }; 157 | * @endcode 158 | */ 159 | template 162 | struct notify_property_changed_base 163 | { 164 | using Type = T; 165 | auto PropertyChanged(Xaml_Data_PropertyChangedEventHandler const& value) 166 | { 167 | return m_propertyChanged.add(value); 168 | } 169 | 170 | void PropertyChanged(winrt::event_token const& token) 171 | { 172 | m_propertyChanged.remove(token); 173 | } 174 | 175 | Type& self() 176 | { 177 | return *static_cast(this); 178 | } 179 | 180 | /** 181 | * @brief Raises a property change notification event 182 | * @param name The name of the property 183 | * @return 184 | * @details Usage example\n 185 | * C++ 186 | * @code 187 | * void MyPage::DoSomething() 188 | * { 189 | * // modify MyInt 190 | * // MyInt = ... 191 | * 192 | * // now send a notification to update the bound UI elements 193 | * RaisePropertyChanged(L"MyInt"); 194 | * } 195 | * @endcode 196 | */ 197 | auto RaisePropertyChanged(std::wstring_view name) 198 | { 199 | return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{ name }); 200 | } 201 | protected: 202 | winrt::event m_propertyChanged; 203 | }; 204 | 205 | /** 206 | * @brief Implements a property type with notifications 207 | * @tparam T the property type 208 | * @details Use the #INIT_NOTIFY_PROPERTY macro to initialize this property in your class constructor. This will set up the right property name, and bind it to the `notify_property_changed_base` implementation. 209 | */ 210 | template 213 | struct single_threaded_notifying_property : single_threaded_rw_property 214 | { 215 | using Type = T; 216 | using base_type = single_threaded_rw_property; 217 | using base_type::operator(); 218 | 219 | template auto& operator()(Q&& q) 220 | { 221 | return *this = std::forward(q); 222 | } 223 | 224 | template auto& operator=(Q&& q) 225 | { 226 | if (q != this->operator()()) 227 | { 228 | static_cast(*this) = std::forward(q); 229 | if (auto strong = m_sender.get(); (m_npc != nullptr) && (strong != nullptr)) 230 | { 231 | (*m_npc)(strong, Xaml_Data_PropertyChangedEventArgs{ m_name }); 232 | } 233 | } 234 | return *this; 235 | } 236 | 237 | template 238 | single_threaded_notifying_property( 239 | winrt::event* npc, 240 | const winrt::Windows::Foundation::IInspectable& sender, 241 | std::wstring_view name, 242 | TArgs&&... args) : 243 | single_threaded_rw_property(std::forward(args)...), 244 | m_name(name), 245 | m_npc(npc), 246 | m_sender(sender) 247 | {} 248 | 249 | single_threaded_notifying_property(const single_threaded_notifying_property&) = default; 250 | single_threaded_notifying_property(single_threaded_notifying_property&&) = default; 251 | std::wstring_view Name() const noexcept { return m_name; } 252 | private: 253 | std::wstring_view m_name; 254 | winrt::event* m_npc; 255 | winrt::weak_ref m_sender; 256 | }; 257 | 258 | /** 259 | * @def WIL_NOTIFYING_PROPERTY 260 | * @brief use this to stamp out a property that calls RaisePropertyChanged upon changing its value. This is a zero-storage alternative to wil::single_threaded_notifying_property 261 | * @details You can pass an initializer list for the initial property value in the variadic arguments to this macro. 262 | */ 263 | #define WIL_NOTIFYING_PROPERTY(type, name, ...) \ 264 | type m_##name{__VA_ARGS__}; \ 265 | auto name() const noexcept { return m_##name; } \ 266 | auto& name(type value) \ 267 | { \ 268 | if (m_##name != value) \ 269 | { \ 270 | m_##name = std::move(value); \ 271 | RaisePropertyChanged(L"" #name); \ 272 | } \ 273 | return *this; \ 274 | } \ 275 | 276 | /** 277 | * @def INIT_NOTIFYING_PROPERTY 278 | * @brief use this to initialize a wil::single_threaded_notifying_property in your class constructor. 279 | */ 280 | #define INIT_NOTIFYING_PROPERTY(NAME, VALUE) \ 281 | NAME(&m_propertyChanged, *this, L"" #NAME, VALUE) 282 | 283 | #endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) 284 | } // namespace wil 285 | -------------------------------------------------------------------------------- /libraries/wil/cppwinrt_helpers.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | 12 | #ifndef __WIL_CPPWINRT_HELPERS_DEFINED 13 | #define __WIL_CPPWINRT_HELPERS_DEFINED 14 | 15 | /// @cond 16 | namespace wil::details 17 | { 18 | struct dispatcher_RunAsync 19 | { 20 | template 21 | static void Schedule(Dispatcher const& dispatcher, Args&&... args) 22 | { 23 | dispatcher.RunAsync(std::forward(args)...); 24 | } 25 | }; 26 | 27 | struct dispatcher_TryEnqueue 28 | { 29 | template 30 | static void Schedule(Dispatcher const& dispatcher, Args&&... args) 31 | { 32 | dispatcher.TryEnqueue(std::forward(args)...); 33 | } 34 | }; 35 | 36 | template struct dispatcher_traits; 37 | } 38 | 39 | #if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) 40 | #include 41 | namespace wil::details 42 | { 43 | template using coroutine_handle = std::experimental::coroutine_handle; 44 | } 45 | #elif defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L) 46 | #include 47 | namespace wil::details 48 | { 49 | template using coroutine_handle = std::coroutine_handle; 50 | } 51 | #endif 52 | /// @endcond 53 | 54 | #if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || (defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) 55 | /// @cond 56 | namespace wil::details 57 | { 58 | struct dispatched_handler_state 59 | { 60 | details::coroutine_handle<> handle{}; 61 | bool orphaned = false; 62 | }; 63 | 64 | struct dispatcher_handler 65 | { 66 | dispatcher_handler(dispatched_handler_state* state) : m_state(state) { } 67 | dispatcher_handler(dispatcher_handler&& other) noexcept : m_state(std::exchange(other.m_state, {})) {} 68 | 69 | ~dispatcher_handler() 70 | { 71 | if (m_state && m_state->handle) 72 | { 73 | m_state->orphaned = true; 74 | Complete(); 75 | } 76 | } 77 | void operator()() 78 | { 79 | Complete(); 80 | } 81 | 82 | void Complete() 83 | { 84 | auto state = std::exchange(m_state, nullptr); 85 | std::exchange(state->handle, {}).resume(); 86 | } 87 | 88 | dispatched_handler_state* m_state; 89 | }; 90 | } 91 | /// @endcond 92 | 93 | namespace wil 94 | { 95 | //! Resumes coroutine execution on the thread associated with the dispatcher, or throws 96 | //! an exception (from an arbitrary thread) if unable. Supported dispatchers are 97 | //! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue, 98 | //! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher, 99 | //! but you must include the corresponding header before including 100 | //! wil\cppwinrt_helpers.h. It is okay to include wil\cppwinrt_helpers.h multiple times: 101 | //! support will be enabled for any winrt/Namespace.h headers that were included since 102 | //! the previous inclusion of wil\cppwinrt_headers.h. 103 | template 104 | [[nodiscard]] auto resume_foreground(Dispatcher const& dispatcher, 105 | typename details::dispatcher_traits::Priority priority = details::dispatcher_traits::Priority::Normal) 106 | { 107 | using Traits = details::dispatcher_traits; 108 | using Priority = typename Traits::Priority; 109 | using Handler = typename Traits::Handler; 110 | 111 | struct awaitable 112 | { 113 | awaitable(Dispatcher const& dispatcher, Priority priority) noexcept : 114 | m_dispatcher(dispatcher), 115 | m_priority(priority) 116 | { 117 | } 118 | bool await_ready() const noexcept { return false; } 119 | 120 | void await_suspend(details::coroutine_handle<> handle) 121 | { 122 | m_state.handle = handle; 123 | Handler handler{ details::dispatcher_handler(&m_state) }; 124 | try 125 | { 126 | // The return value of Schedule is not reliable. Use the dispatcher_handler destructor 127 | // to detect whether the work item failed to run. 128 | Traits::Scheduler::Schedule(m_dispatcher, m_priority, handler); 129 | } 130 | catch (...) 131 | { 132 | m_state.handle = nullptr; // the exception will resume the coroutine, so the handler shouldn't do it 133 | throw; 134 | } 135 | } 136 | 137 | void await_resume() const 138 | { 139 | if (m_state.orphaned) 140 | { 141 | throw winrt::hresult_error(static_cast(0x800701ab)); // HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE) 142 | } 143 | } 144 | 145 | private: 146 | Dispatcher const& m_dispatcher; 147 | Priority const m_priority; 148 | details::dispatched_handler_state m_state; 149 | }; 150 | return awaitable{ dispatcher, priority }; 151 | } 152 | } 153 | #endif // Coroutines are supported 154 | 155 | #endif // __WIL_CPPWINRT_HELPERS_DEFINED 156 | 157 | /// @cond 158 | #if defined(WINRT_Windows_UI_Core_H) && !defined(__WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS) 159 | #define __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS 160 | namespace wil::details 161 | { 162 | template<> 163 | struct dispatcher_traits 164 | { 165 | using Priority = winrt::Windows::UI::Core::CoreDispatcherPriority; 166 | using Handler = winrt::Windows::UI::Core::DispatchedHandler; 167 | using Scheduler = dispatcher_RunAsync; 168 | }; 169 | } 170 | #endif // __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS 171 | 172 | #if defined(WINRT_Windows_System_H) && !defined(__WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS) 173 | #define __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS 174 | namespace wil::details 175 | { 176 | template<> 177 | struct dispatcher_traits 178 | { 179 | using Priority = winrt::Windows::System::DispatcherQueuePriority; 180 | using Handler = winrt::Windows::System::DispatcherQueueHandler; 181 | using Scheduler = dispatcher_TryEnqueue; 182 | }; 183 | } 184 | #endif // __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS 185 | 186 | #if defined(WINRT_Microsoft_System_H) && !defined(__WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS) 187 | #define __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS 188 | namespace wil::details 189 | { 190 | template<> 191 | struct dispatcher_traits 192 | { 193 | using Priority = winrt::Microsoft::System::DispatcherQueuePriority; 194 | using Handler = winrt::Microsoft::System::DispatcherQueueHandler; 195 | using Scheduler = dispatcher_TryEnqueue; 196 | }; 197 | } 198 | #endif // __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS 199 | 200 | #if defined(WINRT_Microsoft_UI_Dispatching_H) && !defined(__WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS) 201 | #define __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS 202 | namespace wil::details 203 | { 204 | template<> 205 | struct dispatcher_traits 206 | { 207 | using Priority = winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority; 208 | using Handler = winrt::Microsoft::UI::Dispatching::DispatcherQueueHandler; 209 | using Scheduler = dispatcher_TryEnqueue; 210 | }; 211 | } 212 | #endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS 213 | /// @endcond 214 | 215 | #if defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS) 216 | #define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS 217 | namespace wil 218 | { 219 | /// @cond 220 | namespace details 221 | { 222 | template struct is_winrt_vector_like { 223 | private: 224 | template ().GetMany(std::declval().Size(), 226 | winrt::array_view().GetAt(0))>{}))> 227 | static constexpr bool get_value(int) { return true; } 228 | template static constexpr bool get_value(...) { return false; } 229 | public: 230 | static constexpr bool value = get_value(0); 231 | }; 232 | 233 | template struct is_winrt_iterator_like { 234 | private: 235 | template ().GetMany(winrt::array_view().Current())>{}))> 237 | static constexpr bool get_value(int) { return true; } 238 | template static constexpr bool get_value(...) { return false; } 239 | public: 240 | static constexpr bool value = get_value(0); 241 | }; 242 | 243 | template constexpr T empty() noexcept 244 | { 245 | if constexpr (std::is_base_of_v) 246 | { 247 | return nullptr; 248 | } 249 | else 250 | { 251 | return {}; 252 | } 253 | } 254 | } 255 | /// @endcond 256 | 257 | /** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the 258 | collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively 259 | processing the collection. 260 | ~~~ 261 | winrt::IVector collection = GetCollection(); 262 | std::vector allData = wil::to_vector(collection); // read all data from collection 263 | for (winrt::hstring const& item : allData) 264 | { 265 | // use item 266 | } 267 | ~~~ 268 | Can be used for IVector, IVectorView, IIterable, IIterator, and any type or 269 | interface that C++/WinRT projects those interfaces for (PropertySet, IMap, etc.) 270 | Iterable-only types fetch content in units of 64. When used with an iterator, the returned 271 | vector contains the iterator's current position and any others after it. 272 | */ 273 | template auto to_vector(TSrc const& src) 274 | { 275 | if constexpr (details::is_winrt_vector_like::value) 276 | { 277 | using T = decltype(src.GetAt(0)); 278 | std::vector result; 279 | if (auto expected = src.Size()) 280 | { 281 | result.resize(expected + 1, details::empty()); 282 | auto actual = src.GetMany(0, result); 283 | if (actual > expected) 284 | { 285 | throw winrt::hresult_changed_state(); 286 | } 287 | result.resize(actual, details::empty()); 288 | } 289 | return result; 290 | } 291 | else if constexpr (details::is_winrt_iterator_like::value) 292 | { 293 | using T = decltype(src.Current()); 294 | std::vector result; 295 | constexpr uint32_t chunkSize = 64; 296 | while (true) 297 | { 298 | auto const lastSize = result.size(); 299 | result.resize(lastSize + chunkSize, details::empty()); 300 | auto fetched = src.GetMany({result.data() + lastSize, result.data() + lastSize + chunkSize }); 301 | if (fetched < chunkSize) 302 | { 303 | result.resize(lastSize + fetched, details::empty()); 304 | break; 305 | } 306 | } 307 | return result; 308 | } 309 | else 310 | { 311 | return to_vector(src.First()); 312 | } 313 | } 314 | } 315 | #endif 316 | 317 | #if defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS) 318 | #define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS 319 | #if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX) 320 | #pragma push_macro("ABI") 321 | #undef ABI 322 | #define ABI 323 | #endif 324 | 325 | namespace wil 326 | { 327 | #if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU) 328 | //! The following methods require that you include both 329 | //! before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION 330 | //! is at least NTDDI_WIN10_CU. It is okay to include wil\cppwinrt_helpers.h multiple times: 331 | //! support will be enabled for any headers that were included since the previous inclusion 332 | //! of wil\cppwinrt_headers.h. 333 | inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd) 334 | { 335 | ABI::Windows::UI::WindowId abiWindowId; 336 | winrt::check_hresult(::GetWindowIdFromWindow(hwnd, &abiWindowId)); 337 | return winrt::Windows::UI::WindowId{ abiWindowId.Value }; 338 | } 339 | 340 | inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId) 341 | { 342 | HWND hwnd; 343 | winrt::check_hresult(::GetWindowFromWindowId({ windowId.Value }, &hwnd)); 344 | return hwnd; 345 | } 346 | #endif /*defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)*/ 347 | } 348 | 349 | #if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX) 350 | #pragma pop_macro("ABI") 351 | #endif 352 | #endif // __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS 353 | -------------------------------------------------------------------------------- /libraries/wil/cppwinrt_wrl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_CPPWINRT_WRL_INCLUDED 12 | #define __WIL_CPPWINRT_WRL_INCLUDED 13 | 14 | #include "cppwinrt.h" 15 | #include 16 | 17 | #include "result_macros.h" 18 | #include 19 | 20 | // wil::wrl_factory_for_winrt_com_class provides interopability between a 21 | // C++/WinRT class and the WRL Module system, allowing the winrt class to be 22 | // CoCreatable. 23 | // 24 | // Usage: 25 | // - In your cpp, add: 26 | // CoCreatableCppWinRtClass(className) 27 | // 28 | // - In the dll.cpp (or equivalent) for the module containing your class, add: 29 | // CoCreatableClassWrlCreatorMapInclude(className) 30 | // 31 | namespace wil 32 | { 33 | namespace details 34 | { 35 | template 36 | class module_count_wrapper : public TCppWinRTClass 37 | { 38 | public: 39 | module_count_wrapper() 40 | { 41 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 42 | { 43 | modulePtr->IncrementObjectCount(); 44 | } 45 | } 46 | 47 | virtual ~module_count_wrapper() 48 | { 49 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 50 | { 51 | modulePtr->DecrementObjectCount(); 52 | } 53 | } 54 | }; 55 | } 56 | 57 | template 58 | class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> 59 | { 60 | public: 61 | IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try 62 | { 63 | *object = nullptr; 64 | RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr); 65 | 66 | return winrt::make>().as(riid, object); 67 | } 68 | CATCH_RETURN() 69 | }; 70 | } 71 | 72 | #define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) 73 | 74 | #endif // __WIL_CPPWINRT_WRL_INCLUDED 75 | -------------------------------------------------------------------------------- /libraries/wil/nt_result_macros.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_NT_RESULTMACROS_INCLUDED 12 | #define __WIL_NT_RESULTMACROS_INCLUDED 13 | 14 | #include "result_macros.h" 15 | 16 | // Helpers for return macros 17 | #define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0) 18 | #define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0) 19 | 20 | //***************************************************************************** 21 | // Macros for returning failures as NTSTATUS 22 | //***************************************************************************** 23 | 24 | // Always returns a known result (NTSTATUS) - always logs failures 25 | #define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status) 26 | 27 | // Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure 28 | #define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__) 29 | 30 | // Conditionally returns failures (NTSTATUS) - always logs failures 31 | #define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 32 | 33 | // Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure 34 | #define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) 35 | 36 | //***************************************************************************** 37 | // Macros to catch and convert exceptions on failure 38 | //***************************************************************************** 39 | 40 | // Use these macros *within* a catch (...) block to handle exceptions 41 | #define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr)) 42 | #define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) 43 | 44 | // Use these macros in place of a catch block to handle exceptions 45 | #define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); } 46 | #define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } 47 | 48 | 49 | namespace wil 50 | { 51 | //***************************************************************************** 52 | // Public Helpers that catch -- mostly only enabled when exceptions are enabled 53 | //***************************************************************************** 54 | 55 | // StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally 56 | // it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type 57 | // the function will fail fast. 58 | // 59 | // try 60 | // { 61 | // // Code 62 | // } 63 | // catch (...) 64 | // { 65 | // status = wil::StatusFromCaughtException(); 66 | // } 67 | _Always_(_Post_satisfies_(return < 0)) 68 | __declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT 69 | { 70 | bool isNormalized = false; 71 | NTSTATUS status = STATUS_SUCCESS; 72 | if (details::g_pfnResultFromCaughtExceptionInternal) 73 | { 74 | status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status; 75 | } 76 | if (FAILED_NTSTATUS(status)) 77 | { 78 | return status; 79 | } 80 | 81 | // Caller bug: an unknown exception was thrown 82 | __WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions); 83 | return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); 84 | } 85 | 86 | namespace details 87 | { 88 | template 89 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); 90 | template 91 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList); 92 | 93 | namespace __R_NS_NAME 94 | { 95 | #ifdef WIL_ENABLE_EXCEPTIONS 96 | __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT 97 | { 98 | __R_FN_LOCALS; 99 | return wil::details::ReportStatus_CaughtException(__R_DIRECT_FN_CALL_ONLY); 100 | } 101 | 102 | __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT 103 | { 104 | va_list argList; 105 | va_start(argList, formatString); 106 | __R_FN_LOCALS; 107 | return wil::details::ReportStatus_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); 108 | } 109 | #endif 110 | } 111 | 112 | template 113 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) 114 | { 115 | wchar_t message[2048]; 116 | message[0] = L'\0'; 117 | return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status; 118 | } 119 | 120 | template<> 121 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) 122 | { 123 | wchar_t message[2048]; 124 | message[0] = L'\0'; 125 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); 126 | } 127 | 128 | template<> 129 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) 130 | { 131 | wchar_t message[2048]; 132 | message[0] = L'\0'; 133 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); 134 | } 135 | 136 | template 137 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) 138 | { 139 | // Pre-populate the buffer with our message, the exception message will be added to it... 140 | wchar_t message[2048]; 141 | PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); 142 | StringCchCatW(message, ARRAYSIZE(message), L" -- "); 143 | return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status; 144 | } 145 | 146 | template<> 147 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) 148 | { 149 | // Pre-populate the buffer with our message, the exception message will be added to it... 150 | wchar_t message[2048]; 151 | PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); 152 | StringCchCatW(message, ARRAYSIZE(message), L" -- "); 153 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); 154 | } 155 | 156 | template<> 157 | __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) 158 | { 159 | // Pre-populate the buffer with our message, the exception message will be added to it... 160 | wchar_t message[2048]; 161 | PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); 162 | StringCchCatW(message, ARRAYSIZE(message), L" -- "); 163 | RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); 164 | } 165 | } 166 | } 167 | 168 | #endif // __WIL_NT_RESULTMACROS_INCLUDED 169 | -------------------------------------------------------------------------------- /libraries/wil/result_originate.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | 12 | // Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating 13 | // a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match, 14 | // then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the 15 | // per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors 16 | // simply because the HRESULTs match. 17 | // 18 | // For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is 19 | // caught and re-thrown. 20 | // 21 | // For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions 22 | // -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must 23 | // capture the entire stack and some additional data. 24 | 25 | #ifndef __WIL_RESULT_ORIGINATE_INCLUDED 26 | #define __WIL_RESULT_ORIGINATE_INCLUDED 27 | 28 | #include "result.h" 29 | #include // RestrictedErrorInfo uses BSTRs :( 30 | #include 31 | #include "resource.h" 32 | #include "com.h" 33 | #include 34 | 35 | namespace wil 36 | { 37 | namespace details 38 | { 39 | // Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame. 40 | inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT 41 | { 42 | if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception)) 43 | { 44 | bool shouldOriginate = true; 45 | 46 | wil::com_ptr_nothrow restrictedErrorInformation; 47 | if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) 48 | { 49 | // This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are 50 | // observing right now. 51 | wil::unique_bstr descriptionUnused; 52 | HRESULT existingHr = failure.hr; 53 | wil::unique_bstr restrictedDescriptionUnused; 54 | wil::unique_bstr capabilitySidUnused; 55 | if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused))) 56 | { 57 | shouldOriginate = (failure.hr != existingHr); 58 | } 59 | } 60 | 61 | if (shouldOriginate) 62 | { 63 | #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) 64 | wil::unique_hmodule errorModule; 65 | if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule)) 66 | { 67 | auto pfn = reinterpret_cast(GetProcAddress(errorModule.get(), "RoOriginateErrorW")); 68 | if (pfn != nullptr) 69 | { 70 | pfn(failure.hr, 0, failure.pszMessage); 71 | } 72 | } 73 | #else // DESKTOP | SYSTEM 74 | ::RoOriginateErrorW(failure.hr, 0, failure.pszMessage); 75 | #endif // DESKTOP | SYSTEM 76 | } 77 | else if (restrictedErrorInformation) 78 | { 79 | // GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present, 80 | // then we need to restore the error information for later observation. 81 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 82 | } 83 | } 84 | } 85 | 86 | // This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT 87 | // matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will 88 | // result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and 89 | // the calling method fails fast the same way it always has. 90 | inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT 91 | { 92 | wil::com_ptr_nothrow restrictedErrorInformation; 93 | if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) 94 | { 95 | wil::unique_bstr descriptionUnused; 96 | HRESULT existingHr = failure.hr; 97 | wil::unique_bstr restrictedDescriptionUnused; 98 | wil::unique_bstr capabilitySidUnused; 99 | if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) && 100 | (existingHr == failure.hr)) 101 | { 102 | // GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext 103 | // so we must restore it via SetRestrictedErrorInfo first. 104 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 105 | RoFailFastWithErrorContext(existingHr); 106 | } 107 | else 108 | { 109 | // The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast 110 | // in this method, so it is available in the debugger just-in-case. 111 | SetRestrictedErrorInfo(restrictedErrorInformation.get()); 112 | } 113 | } 114 | } 115 | } // namespace details 116 | } // namespace wil 117 | 118 | // Automatically call RoOriginateError upon error origination by including this file 119 | WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] 120 | { 121 | ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); 122 | ::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback); 123 | return 1; 124 | }) 125 | 126 | #endif // __WIL_RESULT_ORIGINATE_INCLUDED 127 | -------------------------------------------------------------------------------- /libraries/wil/rpc_helpers.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_RPC_HELPERS_INCLUDED 12 | #define __WIL_RPC_HELPERS_INCLUDED 13 | 14 | #include "result.h" 15 | #include "resource.h" 16 | #include "wistd_functional.h" 17 | #include "wistd_type_traits.h" 18 | 19 | namespace wil 20 | { 21 | 22 | /// @cond 23 | namespace details 24 | { 25 | // This call-adapter template converts a void-returning 'wistd::invoke' into 26 | // an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated 27 | // with 'if constexpr' when C++17 is in wide use. 28 | template struct call_adapter 29 | { 30 | template static HRESULT call(TArgs&& ... args) 31 | { 32 | return wistd::invoke(wistd::forward(args)...); 33 | } 34 | }; 35 | 36 | template<> struct call_adapter 37 | { 38 | template static HRESULT call(TArgs&& ... args) 39 | { 40 | wistd::invoke(wistd::forward(args)...); 41 | return S_OK; 42 | } 43 | }; 44 | 45 | // Some RPC exceptions are already HRESULTs. Others are in the regular Win32 46 | // error space. If the incoming exception code isn't an HRESULT, wrap it. 47 | constexpr HRESULT map_rpc_exception(DWORD code) 48 | { 49 | return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code); 50 | } 51 | } 52 | /// @endcond 53 | 54 | /** Invokes an RPC method, mapping structured exceptions to HRESULTs 55 | Failures encountered by the RPC infrastructure (such as server crashes, authentication 56 | errors, client parameter issues, etc.) are emitted by raising a structured exception from 57 | within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, 58 | RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual 59 | flow control machinery to use. 60 | 61 | Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates 62 | the result of the _work_. HRESULTs returned by a successful completion of the _call_ are 63 | returned as-is. 64 | 65 | RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_ 66 | completes successfully. 67 | 68 | For example, consider an RPC interface method defined in idl as: 69 | ~~~ 70 | HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state); 71 | ~~~ 72 | To call this method, use: 73 | ~~~ 74 | wil::unique_rpc_binding binding = // typically gotten elsewhere; 75 | wil::unique_midl_ptr state; 76 | HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put()); 77 | RETURN_IF_FAILED(hr); 78 | ~~~ 79 | */ 80 | template HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT 81 | { 82 | RpcTryExcept 83 | { 84 | // Note: this helper type can be removed with C++17 enabled via 85 | // 'if constexpr(wistd::is_same_v)' 86 | using result_t = typename wistd::__invoke_of::type; 87 | RETURN_IF_FAILED(details::call_adapter::call(wistd::forward(args)...)); 88 | return S_OK; 89 | } 90 | RpcExcept(RpcExceptionFilter(RpcExceptionCode())) 91 | { 92 | RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); 93 | } 94 | RpcEndExcept 95 | } 96 | 97 | /** Invokes an RPC method, mapping structured exceptions to HRESULTs 98 | Failures encountered by the RPC infrastructure (such as server crashes, authentication 99 | errors, client parameter issues, etc.) are emitted by raising a structured exception from 100 | within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, 101 | RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual 102 | flow control machinery to use. 103 | 104 | Some RPC methods return results (such as a state enumeration or other value) directly in 105 | their signature. This adapter writes that result into a caller-provided object then 106 | returns S_OK. 107 | 108 | For example, consider an RPC interface method defined in idl as: 109 | ~~~ 110 | GUID GetKittenId([in, ref, string] const wchar_t* name); 111 | ~~~ 112 | To call this method, use: 113 | ~~~ 114 | wil::unique_rpc_binding binding = // typically gotten elsewhere; 115 | GUID id; 116 | HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy"); 117 | RETURN_IF_FAILED(hr); 118 | ~~~ 119 | */ 120 | template HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT 121 | { 122 | RpcTryExcept 123 | { 124 | result = wistd::invoke(wistd::forward(args)...); 125 | return S_OK; 126 | } 127 | RpcExcept(RpcExceptionFilter(RpcExceptionCode())) 128 | { 129 | RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); 130 | } 131 | RpcEndExcept 132 | } 133 | 134 | namespace details 135 | { 136 | // Provides an adapter around calling the context-handle-close method on an 137 | // RPC interface, which itself is an RPC call. 138 | template 139 | struct rpc_closer_t 140 | { 141 | static void Close(TStorage arg) WI_NOEXCEPT 142 | { 143 | LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg)); 144 | } 145 | }; 146 | } 147 | 148 | /** Manages explicit RPC context handles 149 | Explicit RPC context handles are used in many RPC interfaces. Most interfaces with 150 | context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets 151 | the server close out the context handle. As the close method itself is an RPC call, 152 | it can fail and raise a structured exception. 153 | 154 | This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow` 155 | helper, ensuring correct cleanup and lifecycle management. 156 | ~~~ 157 | // Assume the interface has two methods: 158 | // HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*); 159 | // HRESULT UseFoo([in] FOO_CONTEXT context; 160 | // void CloseFoo([in, out] PFOO_CONTEXT); 161 | using unique_foo_context = wil::unique_rpc_context_handle; 162 | unique_foo_context context; 163 | RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put())); 164 | RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get())); 165 | context.reset(); 166 | ~~~ 167 | */ 168 | template 169 | using unique_rpc_context_handle = unique_any::Close), details::rpc_closer_t::Close>; 170 | 171 | #ifdef WIL_ENABLE_EXCEPTIONS 172 | /** Invokes an RPC method, mapping structured exceptions to C++ exceptions 173 | See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_ 174 | and those returned by the _method_ are mapped to HRESULTs and thrown inside a 175 | wil::ResultException. Using the example RPC method provided above: 176 | ~~~ 177 | wil::unique_midl_ptr state; 178 | wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put()); 179 | // use 'state' 180 | ~~~ 181 | */ 182 | template void invoke_rpc(TCall&& ... args) 183 | { 184 | THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward(args)...)); 185 | } 186 | 187 | /** Invokes an RPC method, mapping structured exceptions to C++ exceptions 188 | See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the 189 | _call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the 190 | example RPC method provided above: 191 | ~~~ 192 | GUID id = wil::invoke_rpc_result(GetKittenId, binding.get()); 193 | // use 'id' 194 | ~~~ 195 | */ 196 | template auto invoke_rpc_result(TCall&& ... args) 197 | { 198 | using result_t = typename wistd::__invoke_of::type; 199 | result_t result{}; 200 | THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward(args)...)); 201 | return result; 202 | } 203 | #endif 204 | } 205 | 206 | #endif 207 | -------------------------------------------------------------------------------- /libraries/wil/stl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_STL_INCLUDED 12 | #define __WIL_STL_INCLUDED 13 | 14 | #include "common.h" 15 | #include "resource.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #if _HAS_CXX17 21 | #include 22 | #endif 23 | 24 | #ifndef WI_STL_FAIL_FAST_IF 25 | #define WI_STL_FAIL_FAST_IF FAIL_FAST_IF 26 | #endif 27 | 28 | #if defined(WIL_ENABLE_EXCEPTIONS) 29 | 30 | namespace wil 31 | { 32 | /** Secure allocator for STL containers. 33 | The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating 34 | memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`, 35 | `wil::secure_string`, and `wil::secure_wstring`. */ 36 | template 37 | struct secure_allocator 38 | : public std::allocator 39 | { 40 | template 41 | struct rebind 42 | { 43 | using other = secure_allocator; 44 | }; 45 | 46 | secure_allocator() 47 | : std::allocator() 48 | { 49 | } 50 | 51 | ~secure_allocator() = default; 52 | 53 | secure_allocator(const secure_allocator& a) 54 | : std::allocator(a) 55 | { 56 | } 57 | 58 | template 59 | secure_allocator(const secure_allocator& a) 60 | : std::allocator(a) 61 | { 62 | } 63 | 64 | T* allocate(size_t n) 65 | { 66 | return std::allocator::allocate(n); 67 | } 68 | 69 | void deallocate(T* p, size_t n) 70 | { 71 | SecureZeroMemory(p, sizeof(T) * n); 72 | std::allocator::deallocate(p, n); 73 | } 74 | }; 75 | 76 | //! `wil::secure_vector` will be securely zeroed before deallocation. 77 | template 78 | using secure_vector = std::vector>; 79 | //! `wil::secure_wstring` will be securely zeroed before deallocation. 80 | using secure_wstring = std::basic_string, wil::secure_allocator>; 81 | //! `wil::secure_string` will be securely zeroed before deallocation. 82 | using secure_string = std::basic_string, wil::secure_allocator>; 83 | 84 | /// @cond 85 | namespace details 86 | { 87 | template<> struct string_maker 88 | { 89 | HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try 90 | { 91 | m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0'); 92 | return S_OK; 93 | } 94 | catch (...) 95 | { 96 | return E_OUTOFMEMORY; 97 | } 98 | 99 | wchar_t* buffer() { return &m_value[0]; } 100 | 101 | HRESULT trim_at_existing_null(size_t length) { m_value.erase(length); return S_OK; } 102 | 103 | std::wstring release() { return std::wstring(std::move(m_value)); } 104 | 105 | static PCWSTR get(const std::wstring& value) { return value.c_str(); } 106 | 107 | private: 108 | std::wstring m_value; 109 | }; 110 | } 111 | /// @endcond 112 | 113 | // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. 114 | // This is the overload for std::wstring. Other overloads available in resource.h. 115 | inline PCWSTR str_raw_ptr(const std::wstring& str) 116 | { 117 | return str.c_str(); 118 | } 119 | 120 | #if _HAS_CXX17 121 | /** 122 | zstring_view. A zstring_view is identical to a std::string_view except it is always nul-terminated (unless empty). 123 | * zstring_view can be used for storing string literals without "forgetting" the length or that it is nul-terminated. 124 | * A zstring_view can be converted implicitly to a std::string_view because it is always safe to use a nul-terminated 125 | string_view as a plain string view. 126 | * A zstring_view can be constructed from a std::string because the data in std::string is nul-terminated. 127 | */ 128 | template 129 | class basic_zstring_view : public std::basic_string_view 130 | { 131 | using size_type = typename std::basic_string_view::size_type; 132 | 133 | public: 134 | constexpr basic_zstring_view() noexcept = default; 135 | constexpr basic_zstring_view(const basic_zstring_view&) noexcept = default; 136 | constexpr basic_zstring_view& operator=(const basic_zstring_view&) noexcept = default; 137 | 138 | constexpr basic_zstring_view(const TChar* pStringData, size_type stringLength) noexcept 139 | : std::basic_string_view(pStringData, stringLength) 140 | { 141 | if (pStringData[stringLength] != 0) { WI_STL_FAIL_FAST_IF(true); } 142 | } 143 | 144 | template 145 | constexpr basic_zstring_view(const TChar(&stringArray)[stringArrayLength]) noexcept 146 | : std::basic_string_view(&stringArray[0], length_n(&stringArray[0], stringArrayLength)) 147 | { 148 | } 149 | 150 | // Construct from nul-terminated char ptr. To prevent this from overshadowing array construction, 151 | // we disable this constructor if the value is an array (including string literal). 152 | template::value && !std::is_array::value>* = nullptr> 154 | constexpr basic_zstring_view(TPtr&& pStr) noexcept 155 | : std::basic_string_view(std::forward(pStr)) {} 156 | 157 | constexpr basic_zstring_view(const std::basic_string& str) noexcept 158 | : std::basic_string_view(&str[0], str.size()) {} 159 | 160 | // basic_string_view [] precondition won't let us read view[view.size()]; so we define our own. 161 | WI_NODISCARD constexpr const TChar& operator[](size_type idx) const noexcept 162 | { 163 | WI_ASSERT(idx <= this->size() && this->data() != nullptr); 164 | return this->data()[idx]; 165 | } 166 | 167 | WI_NODISCARD constexpr const TChar* c_str() const noexcept 168 | { 169 | WI_ASSERT(this->data() == nullptr || this->data()[this->size()] == 0); 170 | return this->data(); 171 | } 172 | 173 | private: 174 | // Bounds-checked version of char_traits::length, like strnlen. Requires that the input contains a null terminator. 175 | static constexpr size_type length_n(_In_reads_opt_(buf_size) const TChar* str, size_type buf_size) noexcept 176 | { 177 | const std::basic_string_view view(str, buf_size); 178 | auto pos = view.find_first_of(TChar()); 179 | if (pos == view.npos) { WI_STL_FAIL_FAST_IF(true); } 180 | return pos; 181 | } 182 | 183 | // The following basic_string_view methods must not be allowed because they break the nul-termination. 184 | using std::basic_string_view::swap; 185 | using std::basic_string_view::remove_suffix; 186 | }; 187 | 188 | using zstring_view = basic_zstring_view; 189 | using zwstring_view = basic_zstring_view; 190 | #endif // _HAS_CXX17 191 | 192 | } // namespace wil 193 | 194 | #endif // WIL_ENABLE_EXCEPTIONS 195 | 196 | #endif // __WIL_STL_INCLUDED 197 | -------------------------------------------------------------------------------- /libraries/wil/traceloggingconfig.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //********************************************************* 3 | // 4 | // Copyright (c) Microsoft. All rights reserved. 5 | // This code is licensed under the MIT License. 6 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 7 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 8 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 9 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 10 | // 11 | //********************************************************* 12 | 13 | #ifndef __WIL_TRACELOGGING_CONFIG_H 14 | #define __WIL_TRACELOGGING_CONFIG_H 15 | 16 | // Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition 17 | // in this file configures the provider as a normal (non-telemetry) provider. 18 | #define TraceLoggingOptionMicrosoftTelemetry() \ 19 | // Empty definition for TraceLoggingOptionMicrosoftTelemetry 20 | 21 | // Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition 22 | // in this file configures the provider as a normal (non-telemetry) provider. 23 | #define TraceLoggingOptionWindowsCoreTelemetry() \ 24 | // Empty definition for TraceLoggingOptionWindowsCoreTelemetry 25 | 26 | // Event privacy tags. Use the PDT macro values for the tag parameter, e.g.: 27 | // TraceLoggingWrite(..., 28 | // TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage), 29 | // ...); 30 | #define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags") 31 | #define PDT_BrowsingHistory 0x0000000000000002u 32 | #define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u 33 | #define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u 34 | #define PDT_ProductAndServicePerformance 0x0000000001000000u 35 | #define PDT_ProductAndServiceUsage 0x0000000002000000u 36 | #define PDT_SoftwareSetupAndInventory 0x0000000080000000u 37 | 38 | // Event categories specified via keywords, e.g.: 39 | // TraceLoggingWrite(..., 40 | // TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), 41 | // ...); 42 | #define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47 43 | #define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46 44 | #define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45 45 | #define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment) 46 | 47 | // Event categories specified via event tags, e.g.: 48 | // TraceLoggingWrite(..., 49 | // TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY), 50 | // ...); 51 | #define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000 52 | #define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000 53 | #define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000 54 | #define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000 55 | #define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000 56 | #define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000 57 | #define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000 58 | #define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000 59 | #define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000 60 | #define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000 61 | #define MICROSOFT_EVENTTAG_DROP_PII 0x02000000 62 | #define MICROSOFT_EVENTTAG_HASH_PII 0x04000000 63 | #define MICROSOFT_EVENTTAG_MARK_PII 0x08000000 64 | 65 | // Field categories specified via field tags, e.g.: 66 | // TraceLoggingWrite(..., 67 | // TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII), 68 | // ...); 69 | #define MICROSOFT_FIELDTAG_DROP_PII 0x04000000 70 | #define MICROSOFT_FIELDTAG_HASH_PII 0x08000000 71 | #endif // __WIL_TRACELOGGING_CONFIG_H -------------------------------------------------------------------------------- /libraries/wil/win32_result_macros.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_WIN32_RESULTMACROS_INCLUDED 12 | #define __WIL_WIN32_RESULTMACROS_INCLUDED 13 | 14 | #include "result_macros.h" 15 | 16 | // Helpers for return macros 17 | #define __WIN32_RETURN_WIN32(error, str) __WI_SUPPRESS_4127_S do { const auto __error = (error); if (FAILED_WIN32(__error)) { __R_FN(Return_Win32)(__R_INFO(str) __error); } return __error; } __WI_SUPPRESS_4127_E while ((void)0, 0) 18 | #define __WIN32_RETURN_GLE_FAIL(str) return __R_FN(Win32_Return_GetLastError)(__R_INFO_ONLY(str)) 19 | 20 | FORCEINLINE long __WIN32_FROM_HRESULT(HRESULT hr) 21 | { 22 | if (SUCCEEDED(hr)) 23 | { 24 | return ERROR_SUCCESS; 25 | } 26 | return HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr; 27 | } 28 | 29 | //***************************************************************************** 30 | // Macros for returning failures as WIN32 error codes 31 | //***************************************************************************** 32 | 33 | // Always returns a known result (WIN32 error code) - always logs failures 34 | #define WIN32_RETURN_WIN32(error) __WIN32_RETURN_WIN32(wil::verify_win32(error), #error) 35 | #define WIN32_RETURN_LAST_ERROR() __WIN32_RETURN_GLE_FAIL(nullptr) 36 | 37 | // Conditionally returns failures (WIN32 error code) - always logs failures 38 | #define WIN32_RETURN_IF_WIN32_ERROR(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { __WIN32_RETURN_WIN32(__errorRet, #error); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 39 | #define WIN32_RETURN_WIN32_IF(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 40 | #define WIN32_RETURN_WIN32_IF_NULL(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 41 | #define WIN32_RETURN_LAST_ERROR_IF(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_GLE_FAIL(#condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 42 | #define WIN32_RETURN_LAST_ERROR_IF_NULL(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_GLE_FAIL(#ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 43 | 44 | // Conditionally returns failures (WIN32 error code) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern 45 | #define WIN32_RETURN_IF_WIN32_ERROR_EXPECTED(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { return __errorRet; }} __WI_SUPPRESS_4127_E while ((void)0, 0) 46 | #define WIN32_RETURN_WIN32_IF_EXPECTED(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 47 | #define WIN32_RETURN_WIN32_IF_NULL_EXPECTED(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 48 | #define WIN32_RETURN_LAST_ERROR_IF_EXPECTED(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 49 | #define WIN32_RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __WI_SUPPRESS_4127_E while ((void)0, 0) 50 | 51 | 52 | //***************************************************************************** 53 | // Macros to catch and convert exceptions on failure 54 | //***************************************************************************** 55 | 56 | // Use these macros *within* a catch (...) block to handle exceptions 57 | #define WIN32_RETURN_CAUGHT_EXCEPTION() return __R_FN(Win32_Return_CaughtException)(__R_INFO_ONLY(nullptr)) 58 | 59 | // Use these macros in place of a catch block to handle exceptions 60 | #define WIN32_CATCH_RETURN() catch (...) { WIN32_RETURN_CAUGHT_EXCEPTION(); } 61 | 62 | namespace wil 63 | { 64 | //***************************************************************************** 65 | // Public Helpers that catch -- mostly only enabled when exceptions are enabled 66 | //***************************************************************************** 67 | 68 | // Win32ErrorFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally 69 | // it re-throws and catches the exception to convert it to a WIN32 error code. If an exception is of an unrecognized type 70 | // the function will fail fast. 71 | // 72 | // try 73 | // { 74 | // // Code 75 | // } 76 | // catch (...) 77 | // { 78 | // status = wil::Win32ErrorFromCaughtException(); 79 | // } 80 | _Always_(_Post_satisfies_(return > 0)) 81 | __declspec(noinline) inline long Win32ErrorFromCaughtException() WI_NOEXCEPT 82 | { 83 | return __WIN32_FROM_HRESULT(ResultFromCaughtException()); 84 | } 85 | 86 | namespace details::__R_NS_NAME 87 | { 88 | #ifdef WIL_ENABLE_EXCEPTIONS 89 | __R_DIRECT_METHOD(long, Win32_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT 90 | { 91 | __R_FN_LOCALS; 92 | return __WIN32_FROM_HRESULT(wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY)); 93 | } 94 | #endif 95 | 96 | __R_DIRECT_METHOD(long, Win32_Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT 97 | { 98 | __R_FN_LOCALS; 99 | return __WIN32_FROM_HRESULT(wil::details::ReportFailure_GetLastErrorHr(__R_DIRECT_FN_CALL_ONLY)); 100 | } 101 | } 102 | } 103 | 104 | #endif // __WIL_WIN32_RESULTMACROS_INCLUDED 105 | -------------------------------------------------------------------------------- /libraries/wil/wistd_functional.h: -------------------------------------------------------------------------------- 1 | // -*- C++ -*- 2 | //===------------------------ functional ----------------------------------===// 3 | // 4 | // The LLVM Compiler Infrastructure 5 | // 6 | // This file is dual licensed under the MIT and the University of Illinois Open 7 | // Source Licenses. See LICENSE.TXT for details. 8 | // 9 | //===----------------------------------------------------------------------===// 10 | 11 | // STL common functionality 12 | // 13 | // Some aspects of STL are core language concepts that should be used from all C++ code, regardless 14 | // of whether exceptions are enabled in the component. Common library code that expects to be used 15 | // from exception-free components want these concepts, but including STL headers directly introduces 16 | // friction as it requires components not using STL to declare their STL version. Doing so creates 17 | // ambiguity around whether STL use is safe in a particular component and implicitly brings in 18 | // a long list of headers (including ) which can create further ambiguity around throwing new 19 | // support (some routines pulled in may expect it). Secondarily, pulling in these headers also has 20 | // the potential to create naming conflicts or other implied dependencies. 21 | // 22 | // To promote the use of these core language concepts outside of STL-based binaries, this file is 23 | // selectively pulling those concepts *directly* from corresponding STL headers. The corresponding 24 | // "std::" namespace STL functions and types should be preferred over these in code that is bound to 25 | // STL. The implementation and naming of all functions are taken directly from STL, instead using 26 | // "wistd" (Windows Implementation std) as the namespace. 27 | // 28 | // Routines in this namespace should always be considered a reflection of the *current* STL implementation 29 | // of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. 30 | // 31 | // New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. 32 | // Only code that is not exception-based and libraries that expect to be utilized across both exception 33 | // and non-exception based code should utilize this functionality. 34 | 35 | #ifndef _WISTD_FUNCTIONAL_H_ 36 | #define _WISTD_FUNCTIONAL_H_ 37 | 38 | // DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage 39 | #include "wistd_memory.h" 40 | #include // For __fastfail 41 | #include // For placement new 42 | 43 | #if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 44 | #pragma GCC system_header 45 | #endif 46 | 47 | #pragma warning(push) 48 | #pragma warning(disable: 4324) 49 | #pragma warning(disable: 4800) 50 | 51 | /// @cond 52 | namespace wistd // ("Windows Implementation" std) 53 | { 54 | // wistd::function 55 | // 56 | // All of the code below is in direct support of wistd::function. This class is identical to std::function 57 | // with the following exceptions: 58 | // 59 | // 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported) 60 | // 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit) 61 | // 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation) 62 | 63 | template 64 | struct __invoke_void_return_wrapper 65 | { 66 | #ifndef __WI_LIBCPP_CXX03_LANG 67 | template 68 | static _Ret __call(_Args&&... __args) { 69 | return __invoke(wistd::forward<_Args>(__args)...); 70 | } 71 | #else 72 | template 73 | static _Ret __call(_Fn __f) { 74 | return __invoke(__f); 75 | } 76 | 77 | template 78 | static _Ret __call(_Fn __f, _A0& __a0) { 79 | return __invoke(__f, __a0); 80 | } 81 | 82 | template 83 | static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) { 84 | return __invoke(__f, __a0, __a1); 85 | } 86 | 87 | template 88 | static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){ 89 | return __invoke(__f, __a0, __a1, __a2); 90 | } 91 | #endif 92 | }; 93 | 94 | template <> 95 | struct __invoke_void_return_wrapper 96 | { 97 | #ifndef __WI_LIBCPP_CXX03_LANG 98 | template 99 | static void __call(_Args&&... __args) { 100 | (void)__invoke(wistd::forward<_Args>(__args)...); 101 | } 102 | #else 103 | template 104 | static void __call(_Fn __f) { 105 | __invoke(__f); 106 | } 107 | 108 | template 109 | static void __call(_Fn __f, _A0& __a0) { 110 | __invoke(__f, __a0); 111 | } 112 | 113 | template 114 | static void __call(_Fn __f, _A0& __a0, _A1& __a1) { 115 | __invoke(__f, __a0, __a1); 116 | } 117 | 118 | template 119 | static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) { 120 | __invoke(__f, __a0, __a1, __a2); 121 | } 122 | #endif 123 | }; 124 | 125 | //////////////////////////////////////////////////////////////////////////////// 126 | // FUNCTION 127 | //============================================================================== 128 | 129 | // bad_function_call 130 | 131 | __WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY 132 | void __throw_bad_function_call() 133 | { 134 | __fastfail(7); // FAST_FAIL_FATAL_APP_EXIT 135 | } 136 | 137 | template class __WI_LIBCPP_TEMPLATE_VIS function; // undefined 138 | 139 | namespace __function 140 | { 141 | 142 | template 143 | struct __maybe_derive_from_unary_function 144 | { 145 | }; 146 | 147 | template 148 | struct __maybe_derive_from_unary_function<_Rp(_A1)> 149 | : public unary_function<_A1, _Rp> 150 | { 151 | }; 152 | 153 | template 154 | struct __maybe_derive_from_binary_function 155 | { 156 | }; 157 | 158 | template 159 | struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)> 160 | : public binary_function<_A1, _A2, _Rp> 161 | { 162 | }; 163 | 164 | template 165 | __WI_LIBCPP_INLINE_VISIBILITY 166 | bool __not_null(_Fp const&) { return true; } 167 | 168 | template 169 | __WI_LIBCPP_INLINE_VISIBILITY 170 | bool __not_null(_Fp* __ptr) { return __ptr; } 171 | 172 | template 173 | __WI_LIBCPP_INLINE_VISIBILITY 174 | bool __not_null(_Ret _Class::*__ptr) { return __ptr; } 175 | 176 | template 177 | __WI_LIBCPP_INLINE_VISIBILITY 178 | bool __not_null(function<_Fp> const& __f) { return !!__f; } 179 | 180 | } // namespace __function 181 | 182 | #ifndef __WI_LIBCPP_CXX03_LANG 183 | 184 | namespace __function { 185 | 186 | template class __base; 187 | 188 | template 189 | class __base<_Rp(_ArgTypes...)> 190 | { 191 | __base(const __base&); 192 | __base& operator=(const __base&); 193 | public: 194 | __WI_LIBCPP_INLINE_VISIBILITY __base() {} 195 | __WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {} 196 | virtual void __clone(__base*) const = 0; 197 | virtual void __move(__base*) = 0; 198 | virtual void destroy() WI_NOEXCEPT = 0; 199 | virtual _Rp operator()(_ArgTypes&& ...) = 0; 200 | }; 201 | 202 | template class __func; 203 | 204 | template 205 | class __func<_Fp, _Rp(_ArgTypes...)> 206 | : public __base<_Rp(_ArgTypes...)> 207 | { 208 | _Fp __f_; 209 | public: 210 | __WI_LIBCPP_INLINE_VISIBILITY 211 | explicit __func(_Fp&& __f) 212 | : __f_(wistd::move(__f)) {} 213 | 214 | __WI_LIBCPP_INLINE_VISIBILITY 215 | explicit __func(const _Fp& __f) 216 | : __f_(__f) {} 217 | 218 | virtual void __clone(__base<_Rp(_ArgTypes...)>*) const; 219 | virtual void __move(__base<_Rp(_ArgTypes...)>*); 220 | virtual void destroy() WI_NOEXCEPT; 221 | virtual _Rp operator()(_ArgTypes&& ... __arg); 222 | }; 223 | 224 | template 225 | void 226 | __func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const 227 | { 228 | ::new (__p) __func(__f_); 229 | } 230 | 231 | template 232 | void 233 | __func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p) 234 | { 235 | ::new (__p) __func(wistd::move(__f_)); 236 | } 237 | 238 | template 239 | void 240 | __func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT 241 | { 242 | __f_.~_Fp(); 243 | } 244 | 245 | template 246 | _Rp 247 | __func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg) 248 | { 249 | typedef __invoke_void_return_wrapper<_Rp> _Invoker; 250 | return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...); 251 | } 252 | 253 | // 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects 254 | // that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12 255 | // pointers (__base vtable takes an additional one). 256 | constexpr const size_t __buffer_size = 13 * sizeof(void*); 257 | 258 | } // __function 259 | 260 | // NOTE: The extra 'alignas' here is to work around the x86 compiler bug mentioned in 261 | // https://github.com/microsoft/STL/issues/1533 to force alignment on the stack 262 | template 263 | class __WI_LIBCPP_TEMPLATE_VIS __WI_ALIGNAS(typename aligned_storage<__function::__buffer_size>::type) 264 | function<_Rp(_ArgTypes...)> 265 | : public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>, 266 | public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)> 267 | { 268 | using __base = __function::__base<_Rp(_ArgTypes...)>; 269 | __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS 270 | typename aligned_storage<__function::__buffer_size>::type __buf_; 271 | __base* __f_; 272 | 273 | __WI_LIBCPP_NO_CFI static __base *__as_base(void *p) { 274 | return static_cast<__base*>(p); 275 | } 276 | 277 | template 278 | struct __callable_imp 279 | { 280 | static const bool value = is_same::value || 281 | is_convertible::type, 282 | _Rp>::value; 283 | }; 284 | 285 | template 286 | struct __callable_imp<_Fp, false> 287 | { 288 | static constexpr bool value = false; 289 | }; 290 | 291 | template 292 | struct __callable 293 | { 294 | static const bool value = __callable_imp<_Fp, __lazy_and< 295 | integral_constant, function>::value>, 296 | __invokable<_Fp&, _ArgTypes...> 297 | >::value>::value; 298 | }; 299 | 300 | template 301 | using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type; 302 | public: 303 | using result_type = _Rp; 304 | 305 | // construct/copy/destroy: 306 | __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS 307 | function() WI_NOEXCEPT : __f_(0) {} 308 | 309 | __WI_LIBCPP_INLINE_VISIBILITY 310 | function(nullptr_t) WI_NOEXCEPT : __f_(0) {} 311 | function(const function&); 312 | function(function&&); 313 | template> 314 | function(_Fp); 315 | 316 | function& operator=(const function&); 317 | function& operator=(function&&); 318 | function& operator=(nullptr_t) WI_NOEXCEPT; 319 | template> 320 | function& operator=(_Fp&&); 321 | 322 | ~function(); 323 | 324 | // function modifiers: 325 | void swap(function&); 326 | 327 | // function capacity: 328 | __WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY 329 | __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;} 330 | 331 | // deleted overloads close possible hole in the type system 332 | template 333 | bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete; 334 | template 335 | bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete; 336 | public: 337 | // function invocation: 338 | _Rp operator()(_ArgTypes...) const; 339 | 340 | // NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than 341 | // 'std' so all functions requiring RTTI have been removed 342 | }; 343 | 344 | template 345 | __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS 346 | function<_Rp(_ArgTypes...)>::function(const function& __f) 347 | { 348 | if (__f.__f_ == nullptr) 349 | __f_ = 0; 350 | else 351 | { 352 | __f_ = __as_base(&__buf_); 353 | __f.__f_->__clone(__f_); 354 | } 355 | } 356 | 357 | template 358 | __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS 359 | function<_Rp(_ArgTypes...)>::function(function&& __f) 360 | { 361 | if (__f.__f_ == nullptr) 362 | __f_ = 0; 363 | else 364 | { 365 | __f_ = __as_base(&__buf_); 366 | __f.__f_->__move(__f_); 367 | __f.__f_->destroy(); 368 | __f.__f_ = 0; 369 | } 370 | } 371 | 372 | template 373 | template 374 | __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS 375 | function<_Rp(_ArgTypes...)>::function(_Fp __f) 376 | : __f_(nullptr) 377 | { 378 | if (__function::__not_null(__f)) 379 | { 380 | typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF; 381 | static_assert(sizeof(_FF) <= sizeof(__buf_), 382 | "The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture."); 383 | __f_ = ::new(static_cast(&__buf_)) _FF(wistd::move(__f)); 384 | } 385 | } 386 | 387 | template 388 | function<_Rp(_ArgTypes...)>& 389 | function<_Rp(_ArgTypes...)>::operator=(const function& __f) 390 | { 391 | *this = nullptr; 392 | if (__f.__f_) 393 | { 394 | __f_ = __as_base(&__buf_); 395 | __f.__f_->__clone(__f_); 396 | } 397 | return *this; 398 | } 399 | 400 | template 401 | function<_Rp(_ArgTypes...)>& 402 | function<_Rp(_ArgTypes...)>::operator=(function&& __f) 403 | { 404 | *this = nullptr; 405 | if (__f.__f_) 406 | { 407 | __f_ = __as_base(&__buf_); 408 | __f.__f_->__move(__f_); 409 | __f.__f_->destroy(); 410 | __f.__f_ = 0; 411 | } 412 | return *this; 413 | } 414 | 415 | template 416 | function<_Rp(_ArgTypes...)>& 417 | function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT 418 | { 419 | __base* __t = __f_; 420 | __f_ = 0; 421 | if (__t) 422 | __t->destroy(); 423 | return *this; 424 | } 425 | 426 | template 427 | template 428 | function<_Rp(_ArgTypes...)>& 429 | function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) 430 | { 431 | *this = nullptr; 432 | if (__function::__not_null(__f)) 433 | { 434 | typedef __function::__func::type, _Rp(_ArgTypes...)> _FF; 435 | static_assert(sizeof(_FF) <= sizeof(__buf_), 436 | "The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture."); 437 | __f_ = ::new(static_cast(&__buf_)) _FF(wistd::move(__f)); 438 | } 439 | 440 | return *this; 441 | } 442 | 443 | template 444 | function<_Rp(_ArgTypes...)>::~function() 445 | { 446 | if (__f_) 447 | __f_->destroy(); 448 | } 449 | 450 | template 451 | void 452 | function<_Rp(_ArgTypes...)>::swap(function& __f) 453 | { 454 | if (wistd::addressof(__f) == this) 455 | return; 456 | if (__f_ && __f.__f_) 457 | { 458 | typename aligned_storage::type __tempbuf; 459 | __base* __t = __as_base(&__tempbuf); 460 | __f_->__move(__t); 461 | __f_->destroy(); 462 | __f_ = 0; 463 | __f.__f_->__move(__as_base(&__buf_)); 464 | __f.__f_->destroy(); 465 | __f.__f_ = 0; 466 | __f_ = __as_base(&__buf_); 467 | __t->__move(__as_base(&__f.__buf_)); 468 | __t->destroy(); 469 | __f.__f_ = __as_base(&__f.__buf_); 470 | } 471 | else if (__f_) 472 | { 473 | __f_->__move(__as_base(&__f.__buf_)); 474 | __f_->destroy(); 475 | __f_ = 0; 476 | __f.__f_ = __as_base(&__f.__buf_); 477 | } 478 | else if (__f.__f_) 479 | { 480 | __f.__f_->__move(__as_base(&__buf_)); 481 | __f.__f_->destroy(); 482 | __f.__f_ = 0; 483 | __f_ = __as_base(&__buf_); 484 | } 485 | } 486 | 487 | template 488 | _Rp 489 | function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const 490 | { 491 | if (__f_ == nullptr) 492 | __throw_bad_function_call(); 493 | return (*__f_)(wistd::forward<_ArgTypes>(__arg)...); 494 | } 495 | 496 | template 497 | inline __WI_LIBCPP_INLINE_VISIBILITY 498 | bool 499 | operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;} 500 | 501 | template 502 | inline __WI_LIBCPP_INLINE_VISIBILITY 503 | bool 504 | operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;} 505 | 506 | template 507 | inline __WI_LIBCPP_INLINE_VISIBILITY 508 | bool 509 | operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;} 510 | 511 | template 512 | inline __WI_LIBCPP_INLINE_VISIBILITY 513 | bool 514 | operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;} 515 | 516 | // Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work 517 | template 518 | inline __WI_LIBCPP_INLINE_VISIBILITY 519 | void 520 | swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) 521 | {return __x.swap(__y);} 522 | 523 | template 524 | inline __WI_LIBCPP_INLINE_VISIBILITY 525 | void 526 | swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) 527 | {return __x.swap(__y);} 528 | 529 | // std::invoke 530 | template 531 | typename __invoke_of<_Fn, _Args...>::type 532 | invoke(_Fn&& __f, _Args&&... __args) 533 | __WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value)) 534 | { 535 | return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...); 536 | } 537 | 538 | #else // __WI_LIBCPP_CXX03_LANG 539 | 540 | #error wistd::function and wistd::invoke not implemented for pre-C++11 541 | 542 | #endif 543 | } 544 | /// @endcond 545 | 546 | #pragma warning(pop) 547 | 548 | #endif // _WISTD_FUNCTIONAL_H_ 549 | -------------------------------------------------------------------------------- /libraries/wil/wrl.h: -------------------------------------------------------------------------------- 1 | //********************************************************* 2 | // 3 | // Copyright (c) Microsoft. All rights reserved. 4 | // This code is licensed under the MIT License. 5 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 6 | // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 7 | // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 8 | // PARTICULAR PURPOSE AND NONINFRINGEMENT. 9 | // 10 | //********************************************************* 11 | #ifndef __WIL_WRL_INCLUDED 12 | #define __WIL_WRL_INCLUDED 13 | 14 | #include 15 | #include "result.h" 16 | #include "common.h" // wistd type_traits helpers 17 | #include // GetModuleHandleW 18 | 19 | /// @cond 20 | EXTERN_C IMAGE_DOS_HEADER __ImageBase; 21 | /// @endcond 22 | 23 | namespace wil 24 | { 25 | 26 | #ifdef WIL_ENABLE_EXCEPTIONS 27 | #pragma region Object construction helpers that throw exceptions 28 | 29 | /** Used to construct a RuntimeClass based object that uses 2 phase construction. 30 | Construct a RuntimeClass based object that uses 2 phase construction (by implementing 31 | RuntimeClassInitialize() and returning error codes for failures. 32 | ~~~~ 33 | // SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize() 34 | auto someClass = MakeAndInitializeOrThrow(L"input", true); 35 | ~~~~ */ 36 | 37 | template 38 | Microsoft::WRL::ComPtr MakeAndInitializeOrThrow(TArgs&&... args) 39 | { 40 | Microsoft::WRL::ComPtr obj; 41 | THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&obj, Microsoft::WRL::Details::Forward(args)...)); 42 | return obj; 43 | } 44 | 45 | /** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does 46 | not require 2 phase construction). 47 | ~~~~ 48 | // SomeClass uses exceptions for error handling in its constructor. 49 | auto someClass = MakeOrThrow(L"input", true); 50 | ~~~~ */ 51 | 52 | template 53 | Microsoft::WRL::ComPtr MakeOrThrow(TArgs&&... args) 54 | { 55 | // This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use. 56 | // Unfortunately this produces false positives as all RuntimeClass derived classes have 57 | // a RuntimeClassInitialize() method from their base class. 58 | // static_assert(!std::is_member_function_pointer::value, 59 | // "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead"); 60 | auto obj = Microsoft::WRL::Make(Microsoft::WRL::Details::Forward(args)...); 61 | THROW_IF_NULL_ALLOC(obj.Get()); 62 | return obj; 63 | } 64 | #pragma endregion 65 | 66 | #endif // WIL_ENABLE_EXCEPTIONS 67 | 68 | /** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>. 69 | Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC() 70 | from wil\result.h to test the result. */ 71 | template 72 | ::Microsoft::WRL::ComPtr MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT 73 | { 74 | using namespace Microsoft::WRL; 75 | return Callback, TDelegateInterface, FtmBase>>(wistd::forward(args)...); 76 | } 77 | 78 | #ifdef WIL_ENABLE_EXCEPTIONS 79 | template 80 | ::Microsoft::WRL::ComPtr MakeAgileCallback(Args&&... args) 81 | { 82 | auto result = MakeAgileCallbackNoThrow(wistd::forward(args)...); 83 | THROW_IF_NULL_ALLOC(result); 84 | return result; 85 | } 86 | #endif // WIL_ENABLE_EXCEPTIONS 87 | 88 | /** Holds a reference to the host WRL module to prevent it from being unloaded. 89 | Normally, the reference is held implicitly because you are a member function 90 | of a DLL-hosted COM object, or because you retain a strong reference 91 | to some DLL-hosted COM object, but if those do not apply to you, then you 92 | will need to hold a reference explicitly. For examples (and for the C++/WinRT 93 | equivalent), see winrt_module_reference. 94 | */ 95 | struct [[nodiscard]] wrl_module_reference 96 | { 97 | wrl_module_reference() 98 | { 99 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 100 | { 101 | modulePtr->IncrementObjectCount(); 102 | } 103 | else 104 | { 105 | #ifdef GET_MODULE_HANDLE_EX_FLAG_PIN 106 | // If this assertion fails, then you are using wrl_module_reference 107 | // from a DLL that does not host WRL objects, and the module reference 108 | // has no effect. 109 | WI_ASSERT(reinterpret_cast(&__ImageBase) == GetModuleHandleW(nullptr)); 110 | #endif 111 | } 112 | } 113 | 114 | wrl_module_reference(wrl_module_reference const&) : wrl_module_reference() {} 115 | 116 | ~wrl_module_reference() 117 | { 118 | if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) 119 | { 120 | modulePtr->DecrementObjectCount(); 121 | } 122 | } 123 | }; 124 | 125 | } // namespace wil 126 | 127 | #endif // __WIL_WRL_INCLUDED 128 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /res/manifest.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | Windhawk Symbol Helper 10 | 11 | 12 | 20 | 21 | 22 | 23 | 24 | true 25 | PerMonitorV2 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 51 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /res/windhawk-symbol-helper.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramensoftware/windhawk-symbol-helper/82ba32fbe8e22ca8f1495c2786010ea9bc3a79f3/res/windhawk-symbol-helper.ico -------------------------------------------------------------------------------- /resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by windhawk-symbol-helper.rc 4 | // 5 | #define IDR_MAINFRAME 128 6 | #define IDD_MAINDLG 129 7 | #define IDC_STATIC_ENGINE_DIR 1000 8 | #define IDC_ENGINE_DIR 1001 9 | #define IDC_STATIC_SYMBOLS_DIR 1002 10 | #define IDC_SYMBOLS_DIR 1003 11 | #define IDC_STATIC_SYMBOL_SERVER 1004 12 | #define IDC_SYMBOL_SERVER 1005 13 | #define IDC_STATIC_TARGET_EXECUTABLE 1006 14 | #define IDC_TARGET_EXECUTABLE 1007 15 | #define IDC_PICKFILE 1008 16 | #define IDC_UNDECORATED 1009 17 | #define IDC_DECORATED 1010 18 | #define IDC_LOG 1011 19 | #define IDC_STATIC_RESULTS 1012 20 | #define IDC_RESULTS 1013 21 | #define IDC_RESULTS_PLACEHOLDER 1014 22 | 23 | // Next default values for new objects 24 | // 25 | #ifdef APSTUDIO_INVOKED 26 | #ifndef APSTUDIO_READONLY_SYMBOLS 27 | #define _APS_NEXT_RESOURCE_VALUE 202 28 | #define _APS_NEXT_COMMAND_VALUE 32776 29 | #define _APS_NEXT_CONTROL_VALUE 1015 30 | #define _APS_NEXT_SYMED_VALUE 101 31 | #endif 32 | #endif 33 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramensoftware/windhawk-symbol-helper/82ba32fbe8e22ca8f1495c2786010ea9bc3a79f3/screenshot.png -------------------------------------------------------------------------------- /stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | -------------------------------------------------------------------------------- /stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Change these values to use different versions 4 | #define WINVER _WIN32_WINNT_WIN10 5 | #define _WIN32_WINNT _WIN32_WINNT_WIN10 6 | #define _WIN32_IE _WIN32_IE_IE110 7 | #define _RICHEDIT_VER 0x0500 8 | 9 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 10 | #define NOMINMAX 11 | 12 | // WTL 13 | 14 | #define _WTL_NO_CSTRING 15 | #define _WTL_NO_WTYPES 16 | #define _WTL_NO_UNION_CLASSES 17 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | extern CAppModule _Module; 25 | 26 | #include 27 | 28 | #include // must be included before atlfind.h 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | // Windows 36 | 37 | #include 38 | 39 | // STL 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | // Libraries 52 | 53 | #include // must be included before other wil includes 54 | 55 | #include 56 | #include 57 | 58 | #include 59 | #include 60 | -------------------------------------------------------------------------------- /symbol_enum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class SymbolEnum { 4 | public: 5 | enum class UndecorateMode { 6 | Default = 0, 7 | OldVersionCompatible, 8 | None, 9 | }; 10 | 11 | struct Callbacks { 12 | std::function queryCancel; 13 | std::function notifyProgress; 14 | std::function notifyLog; 15 | }; 16 | 17 | SymbolEnum(HMODULE moduleBase, 18 | PCWSTR enginePath, 19 | PCWSTR symbolsPath, 20 | PCWSTR symbolServer, 21 | UndecorateMode undecorateMode, 22 | Callbacks callbacks = {}); 23 | SymbolEnum(PCWSTR modulePath, 24 | HMODULE moduleBase, 25 | PCWSTR enginePath, 26 | PCWSTR symbolsPath, 27 | PCWSTR symbolServer, 28 | UndecorateMode undecorateMode, 29 | Callbacks callbacks = {}); 30 | 31 | struct Symbol { 32 | void* address; 33 | PCWSTR name; 34 | PCWSTR nameUndecoratedPrefix1; 35 | PCWSTR nameUndecoratedPrefix2; 36 | PCWSTR nameUndecorated; 37 | }; 38 | 39 | std::optional GetNextSymbol(); 40 | 41 | // https://ntdoc.m417z.com/image_chpe_range_entry 42 | typedef struct _IMAGE_CHPE_RANGE_ENTRY { 43 | union { 44 | ULONG StartOffset; 45 | struct { 46 | ULONG NativeCode : 1; 47 | ULONG AddressBits : 31; 48 | } DUMMYSTRUCTNAME; 49 | } DUMMYUNIONNAME; 50 | 51 | ULONG Length; 52 | } IMAGE_CHPE_RANGE_ENTRY, *PIMAGE_CHPE_RANGE_ENTRY; 53 | 54 | private: 55 | void InitModuleInfo(PCWSTR modulePath); 56 | wil::com_ptr LoadMsdia(); 57 | 58 | static constexpr enum SymTagEnum kSymTags[] = { 59 | SymTagPublicSymbol, 60 | SymTagFunction, 61 | SymTagData, 62 | }; 63 | 64 | struct ModuleInfo { 65 | WORD magic; 66 | bool isHybrid; 67 | std::vector chpeRanges; 68 | }; 69 | 70 | HMODULE m_moduleBase; 71 | UndecorateMode m_undecorateMode; 72 | ModuleInfo m_moduleInfo; 73 | wil::unique_hmodule m_msdiaModule; 74 | wil::com_ptr m_diaGlobal; 75 | wil::com_ptr m_diaSymbols; 76 | size_t m_symTagIndex = 0; 77 | wil::unique_bstr m_currentSymbolName; 78 | wil::unique_bstr m_currentSymbolNameUndecorated; 79 | }; 80 | -------------------------------------------------------------------------------- /symbols_from_binary.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "symbols_from_binary.h" 4 | 5 | #include "symbol_enum.h" 6 | 7 | CString SymbolsFromBinary(SymbolsFromBinaryOptions options, 8 | CStringA* logOutput, 9 | std::function progressCallback, 10 | std::stop_token* stopToken) { 11 | CString enumSymbolsResult; 12 | 13 | // Use a set for sorting and to avoid duplicate chunks in the output. 14 | // Duplicates can happen if the same symbol is listed with multiple types, 15 | // such as SymTagFunction and SymTagPublicSymbol, or if two variants of a 16 | // decorated name are identical when undecorated. 17 | std::set resultListChunks; 18 | size_t resultListTotalLength = 0; 19 | size_t count = 0; 20 | 21 | { 22 | SymbolEnum::Callbacks callbacks; 23 | 24 | if (stopToken) { 25 | callbacks.queryCancel = [&stopToken]() { 26 | return stopToken->stop_requested(); 27 | }; 28 | } 29 | 30 | if (progressCallback) { 31 | callbacks.notifyProgress = progressCallback; 32 | } 33 | 34 | if (logOutput) { 35 | callbacks.notifyLog = [&logOutput](PCSTR message) { 36 | CStringA messageStr = message; 37 | // Convert all newlines to CRLF and trim trailing newlines. 38 | messageStr.Replace("\r\n", "\n"); 39 | messageStr.Replace('\r', '\n'); 40 | messageStr.TrimRight("\n"); 41 | messageStr.Replace("\n", "\r\n"); 42 | 43 | *logOutput += messageStr; 44 | *logOutput += "\r\n"; 45 | }; 46 | } 47 | 48 | CString addressPrefix; 49 | CString chunk; 50 | 51 | SymbolEnum symbolEnum( 52 | options.targetExecutable.GetString(), nullptr, 53 | options.engineDir.GetString(), options.symbolsDir.GetString(), 54 | options.symbolServer.GetString(), 55 | options.undecorated ? SymbolEnum::UndecorateMode::Default 56 | : SymbolEnum::UndecorateMode::None, 57 | callbacks); 58 | 59 | while (auto iter = symbolEnum.GetNextSymbol()) { 60 | if (stopToken && stopToken->stop_requested()) { 61 | throw std::runtime_error("Canceled"); 62 | } 63 | 64 | if (!options.decorated && !options.undecorated) { 65 | count++; 66 | continue; 67 | } 68 | 69 | if (!iter->name && !iter->nameUndecorated) { 70 | continue; 71 | } 72 | 73 | addressPrefix.Format(L"[%08" TEXT(PRIXPTR) L"] ", iter->address); 74 | 75 | chunk.Empty(); 76 | 77 | if (options.decorated) { 78 | chunk += addressPrefix; 79 | chunk += iter->name ? iter->name : L""; 80 | chunk += L"\r\n"; 81 | } 82 | 83 | if (options.undecorated) { 84 | chunk += addressPrefix; 85 | if (iter->nameUndecorated) { 86 | chunk += iter->nameUndecoratedPrefix1; 87 | chunk += iter->nameUndecoratedPrefix2; 88 | chunk += iter->nameUndecorated; 89 | } else { 90 | chunk += L""; 91 | } 92 | chunk += L"\r\n"; 93 | } 94 | 95 | auto [_, inserted] = resultListChunks.insert(chunk); 96 | if (inserted) { 97 | resultListTotalLength += chunk.GetLength(); 98 | count++; 99 | } 100 | } 101 | } 102 | 103 | enumSymbolsResult.Preallocate((logOutput ? logOutput->GetLength() : 0) + 104 | static_cast(resultListTotalLength) + 105 | 128); 106 | 107 | enumSymbolsResult.Format(L"Found %zu symbols\r\n", count); 108 | 109 | if (logOutput) { 110 | enumSymbolsResult += *logOutput; 111 | } 112 | 113 | for (const auto& chunk : resultListChunks) { 114 | enumSymbolsResult += chunk; 115 | } 116 | 117 | return enumSymbolsResult; 118 | } 119 | -------------------------------------------------------------------------------- /symbols_from_binary.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct SymbolsFromBinaryOptions { 4 | CString engineDir; 5 | CString symbolsDir; 6 | CString symbolServer; 7 | CString targetExecutable; 8 | bool undecorated; 9 | bool decorated; 10 | }; 11 | 12 | CString SymbolsFromBinary(SymbolsFromBinaryOptions options, 13 | CStringA* logOutput, 14 | std::function progressCallback, 15 | std::stop_token* stopToken); 16 | -------------------------------------------------------------------------------- /view.h: -------------------------------------------------------------------------------- 1 | #include "finddlg.h" 2 | 3 | class CEditView : 4 | public CWindowImpl, 5 | public CEditCommands, 6 | public CEditFindReplaceImpl 7 | { 8 | protected: 9 | typedef CEditView thisClass; 10 | typedef CEditCommands editCommandsClass; 11 | typedef CEditFindReplaceImpl findReplaceClass; 12 | 13 | public: 14 | DECLARE_WND_SUPERCLASS(NULL, CEdit::GetWndClassName()) 15 | 16 | BOOL PreTranslateMessage(MSG* pMsg) 17 | { 18 | // In non Multi-thread SDI cases, CFindReplaceDialogWithMessageFilter will add itself to the 19 | // global message filters list. In our case, we'll call it directly. 20 | if(m_pFindReplaceDialog != NULL) 21 | { 22 | if(m_pFindReplaceDialog->PreTranslateMessage(pMsg)) 23 | return TRUE; 24 | } 25 | return FALSE; 26 | } 27 | 28 | BEGIN_MSG_MAP(CEditView) 29 | MESSAGE_HANDLER(WM_CREATE, OnCreate) 30 | 31 | MSG_WM_GETDLGCODE(OnGetDlgCode) 32 | 33 | CHAIN_MSG_MAP_ALT(editCommandsClass, 1) 34 | 35 | CHAIN_MSG_MAP_ALT(findReplaceClass, 1) 36 | END_MSG_MAP() 37 | 38 | LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) 39 | { 40 | LRESULT lRet = DefWindowProc(uMsg, wParam, lParam); 41 | 42 | LimitText(0); 43 | 44 | return lRet; 45 | } 46 | 47 | UINT OnGetDlgCode(LPMSG lpMsg) 48 | { 49 | // Prevent selecting all text when focused. 50 | // https://devblogs.microsoft.com/oldnewthing/20031114-00/?p=41823 51 | return DefWindowProc() & ~DLGC_HASSETSEL; 52 | } 53 | 54 | // Overrides from CEditFindReplaceImpl 55 | CFindReplaceDialogWithMessageFilter* CreateFindReplaceDialog(BOOL bFindOnly, // TRUE for Find, FALSE for FindReplace 56 | LPCTSTR lpszFindWhat, 57 | LPCTSTR lpszReplaceWith = NULL, 58 | DWORD dwFlags = FR_DOWN, 59 | HWND hWndParent = NULL) 60 | { 61 | // In non Multi-Threaded SDI cases, we'd pass in the message loop to CFindReplaceDialogWithMessageFilter. 62 | // In our case, we'll call PreTranslateMessage directly from this class. 63 | //CFindReplaceDialogWithMessageFilter* findReplaceDialog = 64 | // new CFindReplaceDialogWithMessageFilter(_Module.GetMessageLoop()); 65 | CFindReplaceDialogWithMessageFilter* findReplaceDialog = 66 | new CFindReplaceDialogWithMessageFilter(NULL); 67 | 68 | if(findReplaceDialog == NULL) 69 | { 70 | ::MessageBeep(MB_ICONHAND); 71 | } 72 | else 73 | { 74 | HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly, 75 | lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent); 76 | if(hWndFindReplace == NULL) 77 | { 78 | delete findReplaceDialog; 79 | findReplaceDialog = NULL; 80 | } 81 | else 82 | { 83 | findReplaceDialog->SetActiveWindow(); 84 | findReplaceDialog->ShowWindow(SW_SHOW); 85 | } 86 | } 87 | 88 | return findReplaceDialog; 89 | } 90 | 91 | DWORD GetFindReplaceDialogFlags(void) const 92 | { 93 | DWORD dwFlags = FR_HIDEWHOLEWORD; 94 | 95 | if(m_bFindDown) 96 | dwFlags |= FR_DOWN; 97 | if(m_bMatchCase) 98 | dwFlags |= FR_MATCHCASE; 99 | 100 | return dwFlags; 101 | } 102 | }; 103 | -------------------------------------------------------------------------------- /windhawk-symbol-helper.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "MainDlg.h" 4 | 5 | #include "symbols_from_binary.h" 6 | 7 | CAppModule _Module; 8 | 9 | namespace { 10 | 11 | struct CommandLineOptions { 12 | SymbolsFromBinaryOptions symbolsFromBinaryOptions; 13 | std::wstring outputFile; 14 | }; 15 | 16 | std::optional OptionsFromCommandLine() { 17 | if (__argc != 8) { 18 | return std::nullopt; 19 | } 20 | 21 | const auto is_true = [](const WCHAR* value) { 22 | return _wcsicmp(value, L"true") == 0 || _wcsicmp(value, L"1") == 0; 23 | }; 24 | 25 | return CommandLineOptions{ 26 | .symbolsFromBinaryOptions = 27 | { 28 | .engineDir = __wargv[1], 29 | .symbolsDir = __wargv[2], 30 | .symbolServer = __wargv[3], 31 | .targetExecutable = __wargv[4], 32 | .undecorated = is_true(__wargv[5]), 33 | .decorated = is_true(__wargv[6]), 34 | }, 35 | .outputFile = __wargv[7], 36 | }; 37 | } 38 | 39 | // https://stackoverflow.com/a/69410299 40 | std::string WideStringToString(PCWSTR wide_string) { 41 | int wide_string_len = static_cast(wcslen(wide_string)); 42 | int size_needed = WideCharToMultiByte( 43 | CP_UTF8, 0, wide_string, wide_string_len, nullptr, 0, nullptr, nullptr); 44 | if (size_needed <= 0) { 45 | throw std::runtime_error("WideCharToMultiByte() failed: " + 46 | std::to_string(size_needed)); 47 | } 48 | 49 | std::string result(size_needed, 0); 50 | WideCharToMultiByte(CP_UTF8, 0, wide_string, wide_string_len, result.data(), 51 | size_needed, nullptr, nullptr); 52 | return result; 53 | } 54 | 55 | bool WriteContentsToFile(PCWSTR path, const void* data, DWORD size) { 56 | HANDLE hFile = CreateFile(path, GENERIC_WRITE, FILE_SHARE_READ, nullptr, 57 | CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); 58 | if (hFile == INVALID_HANDLE_VALUE) { 59 | return false; 60 | } 61 | 62 | DWORD dwBytesWritten; 63 | bool written = WriteFile(hFile, data, size, &dwBytesWritten, nullptr); 64 | CloseHandle(hFile); 65 | 66 | return written; 67 | } 68 | 69 | } // namespace 70 | 71 | int WINAPI _tWinMain(HINSTANCE hInstance, 72 | HINSTANCE /*hPrevInstance*/, 73 | LPTSTR /*lpstrCmdLine*/, 74 | int nCmdShow) { 75 | if (auto options = OptionsFromCommandLine()) { 76 | try { 77 | auto result = 78 | SymbolsFromBinary(std::move(options->symbolsFromBinaryOptions), 79 | nullptr, nullptr, nullptr); 80 | 81 | auto resultUtf8 = WideStringToString(result); 82 | 83 | DWORD resultUtf8Size = static_cast(resultUtf8.size()); 84 | if (resultUtf8Size < resultUtf8.size()) { 85 | throw std::runtime_error("Size too large"); 86 | } 87 | 88 | if (!WriteContentsToFile(options->outputFile.c_str(), 89 | resultUtf8.data(), resultUtf8Size)) { 90 | throw std::runtime_error("WriteFile() failed"); 91 | } 92 | 93 | return 0; 94 | } catch (const std::exception&) { 95 | return 1; 96 | } 97 | } 98 | 99 | HRESULT hRes = ::CoInitialize(nullptr); 100 | ATLASSERT(SUCCEEDED(hRes)); 101 | 102 | AtlInitCommonControls( 103 | ICC_BAR_CLASSES); // add flags to support other controls 104 | 105 | hRes = _Module.Init(nullptr, hInstance); 106 | ATLASSERT(SUCCEEDED(hRes)); 107 | 108 | CMessageLoop theLoop; 109 | _Module.AddMessageLoop(&theLoop); 110 | 111 | CMainDlg dlgMain; 112 | int nRet = 0; 113 | 114 | if (dlgMain.Create(nullptr)) { 115 | dlgMain.ShowWindow(nCmdShow); 116 | nRet = theLoop.Run(); 117 | } else { 118 | ATLTRACE(L"Main dialog creation failed!\n"); 119 | } 120 | 121 | _Module.RemoveMessageLoop(); 122 | 123 | _Module.Term(); 124 | ::CoUninitialize(); 125 | 126 | return nRet; 127 | } 128 | -------------------------------------------------------------------------------- /windhawk-symbol-helper.h: -------------------------------------------------------------------------------- 1 | // windhawk-symbol-helper.h 2 | -------------------------------------------------------------------------------- /windhawk-symbol-helper.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "atlres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""atlres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\0" 42 | END 43 | 44 | #endif // APSTUDIO_INVOKED 45 | 46 | 47 | ///////////////////////////////////////////////////////////////////////////// 48 | // 49 | // Icon 50 | // 51 | 52 | // Icon with lowest ID value placed first to ensure application icon 53 | // remains consistent on all systems. 54 | IDR_MAINFRAME ICON "res\\windhawk-symbol-helper.ico" 55 | 56 | 57 | ///////////////////////////////////////////////////////////////////////////// 58 | // 59 | // Dialog 60 | // 61 | 62 | IDD_MAINDLG DIALOGEX 0, 0, 277, 296 63 | STYLE DS_SETFONT | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME 64 | EXSTYLE WS_EX_ACCEPTFILES 65 | CAPTION "Windhawk Symbol Helper" 66 | FONT 9, "Segoe UI", 0, 0, 0x0 67 | BEGIN 68 | DEFPUSHBUTTON "Get &symbols",IDOK,7,275,50,14 69 | PUSHBUTTON "&About",ID_APP_ABOUT,114,275,50,14 70 | PUSHBUTTON "Exi&t",ID_APP_EXIT,220,275,50,14 71 | LTEXT "&Engine folder that contains symsrv_windhawk.dll and msdia140_windhawk.dll:",IDC_STATIC_ENGINE_DIR,7,7,263,8 72 | EDITTEXT IDC_ENGINE_DIR,7,17,263,12,ES_AUTOHSCROLL 73 | LTEXT "Symbols &folder:",IDC_STATIC_SYMBOLS_DIR,7,34,263,8 74 | EDITTEXT IDC_SYMBOLS_DIR,7,44,263,12,ES_AUTOHSCROLL 75 | LTEXT "Symbol ser&ver:",IDC_STATIC_SYMBOL_SERVER,7,61,263,8 76 | EDITTEXT IDC_SYMBOL_SERVER,7,71,263,12,ES_AUTOHSCROLL 77 | LTEXT "Target e&xecutable:",IDC_STATIC_TARGET_EXECUTABLE,7,88,263,8 78 | EDITTEXT IDC_TARGET_EXECUTABLE,7,98,210,12,ES_AUTOHSCROLL 79 | PUSHBUTTON "&Browse...",IDC_PICKFILE,220,97,50,14 80 | CONTROL "&Undecorated",IDC_UNDECORATED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,115,55,10 81 | CONTROL "&Decorated",IDC_DECORATED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,65,115,47,10 82 | CONTROL "&Log",IDC_LOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,116,115,27,10 83 | LTEXT "&Results:",IDC_STATIC_RESULTS,7,130,263,8 84 | LTEXT "Static",IDC_RESULTS_PLACEHOLDER,7,140,263,129,NOT WS_VISIBLE 85 | END 86 | 87 | 88 | ///////////////////////////////////////////////////////////////////////////// 89 | // 90 | // DESIGNINFO 91 | // 92 | 93 | #ifdef APSTUDIO_INVOKED 94 | GUIDELINES DESIGNINFO 95 | BEGIN 96 | IDD_MAINDLG, DIALOG 97 | BEGIN 98 | LEFTMARGIN, 7 99 | RIGHTMARGIN, 270 100 | TOPMARGIN, 7 101 | BOTTOMMARGIN, 289 102 | END 103 | END 104 | #endif // APSTUDIO_INVOKED 105 | 106 | 107 | ///////////////////////////////////////////////////////////////////////////// 108 | // 109 | // Accelerator 110 | // 111 | 112 | IDR_MAINFRAME ACCELERATORS 113 | BEGIN 114 | "F", ID_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT 115 | VK_F3, ID_EDIT_REPEAT, VIRTKEY, NOINVERT 116 | VK_F3, ID_EDIT_REPEAT, VIRTKEY, SHIFT, NOINVERT 117 | "H", ID_EDIT_REPLACE, VIRTKEY, CONTROL, NOINVERT 118 | "R", ID_EDIT_REPLACE, VIRTKEY, CONTROL, NOINVERT 119 | END 120 | 121 | 122 | ///////////////////////////////////////////////////////////////////////////// 123 | // 124 | // Version 125 | // 126 | 127 | VS_VERSION_INFO VERSIONINFO 128 | FILEVERSION 1,0,8,0 129 | PRODUCTVERSION 1,0,8,0 130 | FILEFLAGSMASK 0x3fL 131 | #ifdef _DEBUG 132 | FILEFLAGS 0x1L 133 | #else 134 | FILEFLAGS 0x0L 135 | #endif 136 | FILEOS 0x4L 137 | FILETYPE 0x2L 138 | FILESUBTYPE 0x0L 139 | BEGIN 140 | BLOCK "StringFileInfo" 141 | BEGIN 142 | BLOCK "040904b0" 143 | BEGIN 144 | VALUE "FileDescription", "Windhawk Symbol Helper" 145 | VALUE "FileVersion", "1.0.8.0" 146 | VALUE "InternalName", "windhawk-symbol-helper" 147 | VALUE "LegalCopyright", "Copyright Ramen Software" 148 | VALUE "OriginalFilename", "windhawk-symbol-helper.exe" 149 | VALUE "ProductName", "Windhawk Symbol Helper" 150 | VALUE "ProductVersion", "1.0.8.0" 151 | END 152 | END 153 | BLOCK "VarFileInfo" 154 | BEGIN 155 | VALUE "Translation", 0x409, 1200 156 | END 157 | END 158 | 159 | 160 | ///////////////////////////////////////////////////////////////////////////// 161 | // 162 | // AFX_DIALOG_LAYOUT 163 | // 164 | 165 | IDD_MAINDLG AFX_DIALOG_LAYOUT 166 | BEGIN 167 | 0 168 | END 169 | 170 | 171 | ///////////////////////////////////////////////////////////////////////////// 172 | // 173 | // String Table 174 | // 175 | 176 | STRINGTABLE 177 | BEGIN 178 | IDR_MAINFRAME "windhawk-symbol-helper" 179 | END 180 | 181 | STRINGTABLE 182 | BEGIN 183 | ID_FILE_NEW "Create a new document\nNew" 184 | ID_FILE_OPEN "Open an existing document\nOpen" 185 | ID_FILE_CLOSE "Close the active document\nClose" 186 | ID_FILE_SAVE "Save the active document\nSave" 187 | ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" 188 | ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" 189 | ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" 190 | ID_FILE_PRINT "Print the active document\nPrint" 191 | ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" 192 | END 193 | 194 | STRINGTABLE 195 | BEGIN 196 | ID_APP_ABOUT "Display program information, version number and copyright\nAbout" 197 | ID_APP_EXIT "Quit the application; prompts to save documents\nExit" 198 | END 199 | 200 | STRINGTABLE 201 | BEGIN 202 | ID_NEXT_PANE "Switch to the next window pane\nNext Pane" 203 | ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" 204 | END 205 | 206 | STRINGTABLE 207 | BEGIN 208 | ID_WINDOW_NEW "Open another window for the active document\nNew Window" 209 | ID_WINDOW_ARRANGE "Arrange icons at the bottom of the window\nArrange Icons" 210 | ID_WINDOW_CASCADE "Arrange windows so they overlap\nCascade Windows" 211 | ID_WINDOW_TILE_HORZ "Arrange windows as non-overlapping tiles\nTile Windows" 212 | ID_WINDOW_TILE_VERT "Arrange windows as non-overlapping tiles\nTile Windows" 213 | ID_WINDOW_SPLIT "Split the active window into panes\nSplit" 214 | END 215 | 216 | STRINGTABLE 217 | BEGIN 218 | ID_EDIT_CLEAR "Erase the selection\nErase" 219 | ID_EDIT_CLEAR_ALL "Erase everything\nErase All" 220 | ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" 221 | ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" 222 | ID_EDIT_FIND "Find the specified text\nFind" 223 | ID_EDIT_PASTE "Insert Clipboard contents\nPaste" 224 | ID_EDIT_REPEAT "Repeat the last action\nRepeat" 225 | ID_EDIT_REPLACE "Replace specific text with different text\nReplace" 226 | ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" 227 | ID_EDIT_UNDO "Undo the last action\nUndo" 228 | ID_EDIT_REDO "Redo the previously undone action\nRedo" 229 | END 230 | 231 | STRINGTABLE 232 | BEGIN 233 | ATL_IDS_SCSIZE "Change the window size" 234 | ATL_IDS_SCMOVE "Change the window position" 235 | ATL_IDS_SCMINIMIZE "Reduce the window to an icon" 236 | ATL_IDS_SCMAXIMIZE "Enlarge the window to full size" 237 | ATL_IDS_SCNEXTWINDOW "Switch to the next document window" 238 | ATL_IDS_SCPREVWINDOW "Switch to the previous document window" 239 | ATL_IDS_SCCLOSE "Close the active window and prompts to save the documents" 240 | END 241 | 242 | STRINGTABLE 243 | BEGIN 244 | ATL_IDS_SCRESTORE "Restore the window to normal size" 245 | ATL_IDS_SCTASKLIST "Activate Task List" 246 | ATL_IDS_MDICHILD "Activate this window" 247 | END 248 | 249 | STRINGTABLE 250 | BEGIN 251 | ATL_IDS_IDLEMESSAGE "Ready" 252 | END 253 | 254 | STRINGTABLE 255 | BEGIN 256 | ATL_IDS_MRU_FILE "Open this document" 257 | END 258 | 259 | #endif // English (United States) resources 260 | ///////////////////////////////////////////////////////////////////////////// 261 | 262 | 263 | -------------------------------------------------------------------------------- /windhawk-symbol-helper.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34031.279 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "windhawk-symbol-helper", "windhawk-symbol-helper.vcxproj", "{5087A507-4250-43E8-8763-31C652DC50AA}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {5087A507-4250-43E8-8763-31C652DC50AA}.Debug|x64.ActiveCfg = Debug|x64 17 | {5087A507-4250-43E8-8763-31C652DC50AA}.Debug|x64.Build.0 = Debug|x64 18 | {5087A507-4250-43E8-8763-31C652DC50AA}.Debug|x86.ActiveCfg = Debug|Win32 19 | {5087A507-4250-43E8-8763-31C652DC50AA}.Debug|x86.Build.0 = Debug|Win32 20 | {5087A507-4250-43E8-8763-31C652DC50AA}.Release|x64.ActiveCfg = Release|x64 21 | {5087A507-4250-43E8-8763-31C652DC50AA}.Release|x64.Build.0 = Release|x64 22 | {5087A507-4250-43E8-8763-31C652DC50AA}.Release|x86.ActiveCfg = Release|Win32 23 | {5087A507-4250-43E8-8763-31C652DC50AA}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {739833D1-B751-4A94-8D4B-794BBB640A2B} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /windhawk-symbol-helper.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 17.0 23 | {5087A507-4250-43E8-8763-31C652DC50AA} 24 | 10.0 25 | 26 | 27 | 28 | Application 29 | true 30 | v143 31 | Unicode 32 | 33 | 34 | Application 35 | false 36 | v143 37 | Unicode 38 | 39 | 40 | Application 41 | true 42 | v143 43 | Unicode 44 | 45 | 46 | Application 47 | false 48 | v143 49 | Unicode 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | true 71 | $(ProjectDir)libraries;$(IncludePath) 72 | $(ProjectDir)libraries;$(LibraryPath) 73 | 74 | 75 | true 76 | $(ProjectDir)libraries;$(IncludePath) 77 | $(ProjectDir)libraries;$(LibraryPath) 78 | 79 | 80 | false 81 | $(ProjectDir)libraries;$(IncludePath) 82 | $(ProjectDir)libraries;$(LibraryPath) 83 | 84 | 85 | false 86 | $(ProjectDir)libraries;$(IncludePath) 87 | $(ProjectDir)libraries;$(LibraryPath) 88 | 89 | 90 | 91 | Use 92 | Level3 93 | MultiThreadedDebug 94 | EditAndContinue 95 | EnableFastChecks 96 | Disabled 97 | _WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions) 98 | stdcpp20 99 | /d1trimfile:"$(SolutionDir)\" %(AdditionalOptions) 100 | 101 | 102 | Windows 103 | true 104 | dia/lib/amd64/diaguids.lib;%(AdditionalDependencies) 105 | 106 | 107 | 0x0409 108 | $(IntDir);%(AdditionalIncludeDirectories) 109 | _DEBUG;%(PreprocessorDefinitions) 110 | 111 | 112 | false 113 | _DEBUG;%(PreprocessorDefinitions) 114 | windhawk-symbol-helper.h 115 | windhawk-symbol-helper_i.c 116 | windhawk-symbol-helper_p.c 117 | true 118 | $(IntDir)/windhawk-symbol-helper.tlb 119 | 120 | 121 | 122 | res/manifest.manifest %(AdditionalManifestFiles) 123 | 124 | 125 | 126 | 127 | Use 128 | Level3 129 | MultiThreadedDebug 130 | EditAndContinue 131 | EnableFastChecks 132 | Disabled 133 | WIN32;_WINDOWS;STRICT;_DEBUG;%(PreprocessorDefinitions) 134 | stdcpp20 135 | /d1trimfile:"$(SolutionDir)\" %(AdditionalOptions) 136 | 137 | 138 | Windows 139 | true 140 | dia/lib/diaguids.lib;%(AdditionalDependencies) 141 | 142 | 143 | 0x0409 144 | $(IntDir);%(AdditionalIncludeDirectories) 145 | _DEBUG;%(PreprocessorDefinitions) 146 | 147 | 148 | false 149 | Win32 150 | _DEBUG;%(PreprocessorDefinitions) 151 | windhawk-symbol-helper.h 152 | windhawk-symbol-helper_i.c 153 | windhawk-symbol-helper_p.c 154 | true 155 | $(IntDir)/windhawk-symbol-helper.tlb 156 | 157 | 158 | 159 | res/manifest.manifest %(AdditionalManifestFiles) 160 | 161 | 162 | 163 | 164 | Use 165 | Level3 166 | MultiThreaded 167 | Sync 168 | 169 | WIN32;_WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions) 170 | stdcpp20 171 | /d1trimfile:"$(SolutionDir)\" %(AdditionalOptions) 172 | 173 | 174 | Windows 175 | dia/lib/diaguids.lib;%(AdditionalDependencies) 176 | false 177 | 178 | 179 | 0x0409 180 | $(IntDir);%(AdditionalIncludeDirectories) 181 | NDEBUG;%(PreprocessorDefinitions) 182 | 183 | 184 | false 185 | Win32 186 | NDEBUG;%(PreprocessorDefinitions) 187 | windhawk-symbol-helper.h 188 | windhawk-symbol-helper_i.c 189 | windhawk-symbol-helper_p.c 190 | true 191 | $(IntDir)/windhawk-symbol-helper.tlb 192 | 193 | 194 | 195 | res/manifest.manifest %(AdditionalManifestFiles) 196 | 197 | 198 | 199 | 200 | Use 201 | Level3 202 | MultiThreaded 203 | Sync 204 | 205 | _WINDOWS;STRICT;NDEBUG;%(PreprocessorDefinitions) 206 | stdcpp20 207 | /d1trimfile:"$(SolutionDir)\" %(AdditionalOptions) 208 | 209 | 210 | Windows 211 | dia/lib/amd64/diaguids.lib;%(AdditionalDependencies) 212 | false 213 | 214 | 215 | 0x0409 216 | $(IntDir);%(AdditionalIncludeDirectories) 217 | NDEBUG;%(PreprocessorDefinitions) 218 | 219 | 220 | false 221 | NDEBUG;%(PreprocessorDefinitions) 222 | windhawk-symbol-helper.h 223 | windhawk-symbol-helper_i.c 224 | windhawk-symbol-helper_p.c 225 | true 226 | $(IntDir)/windhawk-symbol-helper.tlb 227 | 228 | 229 | 230 | res/manifest.manifest %(AdditionalManifestFiles) 231 | 232 | 233 | 234 | 235 | 236 | Create 237 | Create 238 | Create 239 | Create 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 270 | 271 | 272 | 273 | -------------------------------------------------------------------------------- /windhawk-symbol-helper.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {1f659fff-1524-49ec-ae52-eb976679c22a} 6 | cpp;c;cxx;def;odl;idl;hpj;bat;asm 7 | 8 | 9 | {01d3f90e-c5c1-4a89-9508-00eb58603f6e} 10 | h;hpp;hxx;hm;inl;inc 11 | 12 | 13 | {ef0d9713-3f11-4940-8978-ae6d3a24e1f8} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | 58 | 59 | Resource Files 60 | 61 | 62 | 63 | 64 | Resource Files 65 | 66 | 67 | 68 | 69 | 70 | --------------------------------------------------------------------------------