UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
windows.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2019-2023 CESNET
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef _WIN32
39#include <audioclient.h>
40#include <ntstatus.h>
41#include <objbase.h>
42#include <tlhelp32.h>
43#include <vfwmsgs.h>
44#include <dbghelp.h>
45#include <io.h>
46
47#include "utils/macros.h"
48#endif // defined _WIN32
49
50#include "debug.h"
51#include "utils/windows.h"
52
53#define MOD_NAME "[utils/win] "
54
65bool com_initialize(bool *com_initialized, const char *err_prefix)
66{
67 if (*com_initialized) {
68 MSG(WARNING, "com_initialized should be initalized to false "
69 "upon the call!\n");
70 }
71#ifdef _WIN32
72 if (err_prefix == NULL) {
73 err_prefix = "";
74 }
75 *com_initialized = false;
76 // Initialize COM on this thread
77 HRESULT result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
78 if (SUCCEEDED(result)) {
79 *com_initialized = true;
80 return true;
81 }
82 if (result == RPC_E_CHANGED_MODE) {
83 log_msg(LOG_LEVEL_WARNING, "%sCOM already intiialized with a different mode!\n", err_prefix);
84 return true;
85 }
86 log_msg(LOG_LEVEL_ERROR, "%sInitialize of COM failed - %s\n", err_prefix, hresult_to_str(result));
87 return false;
88#else
89 (void) com_initialized, (void) err_prefix;
90 return true;
91#endif
92}
93
94void com_uninitialize(bool *com_initialized)
95{
96#ifdef _WIN32
97 if (!*com_initialized) {
98 return;
99 }
100 *com_initialized = false;
101 CoUninitialize();
102#else
103 (void) com_initialized;
104#endif
105}
106
107#ifdef _WIN32
108const char *hresult_to_str(HRESULT res) {
109 _Thread_local static char unknown[128];
110 const char *errptr = NULL;
111
112 if (HRESULT_FACILITY(res) == FACILITY_WIN32) {
113 return get_win32_error(HRESULT_CODE(res));
114 }
115
116 HRESULT_GET_ERROR_COMMON(res, errptr)
117 if (errptr) {
118 return errptr;
119 }
120
121 switch (res) {
122 case RPC_E_CHANGED_MODE: return "A previous call to CoInitializeEx specified different concurrency model for this thread."; break; \
123 case CO_E_NOTINITIALIZED: return "CoInitialize has not been called.";
124 case AUDCLNT_E_ALREADY_INITIALIZED: return "The IAudioClient object is already initialized.";
125 case AUDCLNT_E_WRONG_ENDPOINT_TYPE: return "The AUDCLNT_STREAMFLAGS_LOOPBACK flag is set but the endpoint device is a capture device, not a rendering device.";
126 case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return "The requested buffer size is not aligned. This code can be returned for a render or a capture device if the caller specified AUDCLNT_SHAREMODE_EXCLUSIVE and the AUDCLNT_STREAMFLAGS_EVENTCALLBACK flags. The caller must call Initialize again with the aligned buffer size.";
127 case AUDCLNT_E_BUFFER_SIZE_ERROR: return "Indicates that the buffer duration value requested by an exclusive-mode client is out of range. The requested duration value for pull mode must not be greater than 500 milliseconds; for push mode the duration value must not be greater than 2 seconds.";
128 case AUDCLNT_E_CPUUSAGE_EXCEEDED: return "Indicates that the process-pass duration exceeded the maximum CPU usage. The audio engine keeps track of CPU usage by maintaining the number of times the process-pass duration exceeds the maximum CPU usage. The maximum CPU usage is calculated as a percent of the engine's periodicity. The percentage value is the system's CPU throttle value (within the range of 10%% and 90%%). If this value is not found, then the default value of 40%% is used to calculate the maximum CPU usage.)";
129 case AUDCLNT_E_DEVICE_INVALIDATED: return "The audio endpoint device has been unplugged, or the audio hardware or associated hardware resources have been reconfigured, disabled, removed, or otherwise made unavailable for use.";
130 case AUDCLNT_E_DEVICE_IN_USE: return "The endpoint device is already in use. Either the device is being used in exclusive mode, or the device is being used in shared mode and the caller asked to use the device in exclusive mode.";
131 case AUDCLNT_E_ENDPOINT_CREATE_FAILED: return "The method failed to create the audio endpoint for the render or the capture device. This can occur if the audio endpoint device has been unplugged, or the audio hardware or associated hardware resources have been reconfigured, disabled, removed, or otherwise made unavailable for use.";
132 case AUDCLNT_E_INVALID_DEVICE_PERIOD: return "Indicates that the device period requested by an exclusive-mode client is greater than 500 milliseconds.";
133 case AUDCLNT_E_UNSUPPORTED_FORMAT: return "The audio engine (shared mode) or audio endpoint device (exclusive mode) does not support the specified format.";
134 case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return "The caller is requesting exclusive-mode use of the endpoint device, but the user has disabled exclusive-mode use of the device.";
135 case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return "The AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag is set but parameters hnsBufferDuration and hnsPeriodicity are not equal.";
136 case AUDCLNT_E_SERVICE_NOT_RUNNING: return "The Windows audio service is not running.";
137 case VFW_E_CANNOT_CONNECT:
138 return "DirectShow: No combination of intermediate filters "
139 "could be found to make the connection.";
140 case VFW_E_NOT_CONNECTED:
141 return "DirectShow: The operation cannot be performed "
142 "because the pins are not connected.";
143 default:
144 snprintf(unknown, sizeof unknown, "(unknown: %ld S=%lu,R=%lu,C=%lu,facility=%lu,code=%lu)", res, ((unsigned long int) res) >> 31u, (res >> 30u) & 0x1, (res >> 29u) & 0x1, (res >> 16u) & 0x7ff, res & 0xff);
145 return unknown;
146 }
147}
148
159const char *
160get_win32_error(DWORD error)
161{
162 _Thread_local static char buf[1024] = "(unknown)";
163 if (FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags
164 NULL, // lpsource
165 error, // message id
166 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), // languageid
167 buf, // output buffer
168 sizeof buf, // size of msgbuf, bytes
169 NULL) // va_list of arguments
170 ) {
171 char *end = buf + strlen(buf) - 1;
172 if (end > buf && *end == '\n') { // skip LF
173 *end-- = '\0';
174 }
175 if (end > buf && *end == '\r') { // skip CR
176 *end-- = '\0';
177 }
178 return buf;
179 }
180
181 snprintf(buf, sizeof buf, "[Could not find a description for error # %#lx.]", error);
182
183 return buf;
184}
185
190const char *win_wstr_to_str(const wchar_t *wstr) {
191 _Thread_local static char res[1024];
192 int ret = WideCharToMultiByte(CP_UTF8, 0, wstr, -1 /* NULL-terminated */, res, sizeof res - 1, NULL, NULL);
193 if (ret == 0) {
194 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
195 bug_msg(
197 "win_wstr_to_str: Insufficient buffer length %zd. ",
198 sizeof res);
199 } else {
200 log_msg(LOG_LEVEL_ERROR, "win_wstr_to_str: %s\n", get_win32_error(GetLastError()));
201 }
202 }
203 return res;
204}
205
206static bool
207GetProcessInfo(HANDLE hSnapshot, DWORD processId, PROCESSENTRY32 *pe32)
208{
209 if (!Process32First(hSnapshot, pe32)) {
210 return false;
211 }
212
213 do {
214 if (pe32->th32ProcessID == processId) {
215 return true;
216 }
217 } while (Process32Next(hSnapshot, pe32));
218
219 return false;
220}
221
226bool
227win_has_ancestor_process(const char *name)
228{
229 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
230 if (hSnapshot == INVALID_HANDLE_VALUE) {
231 return false;
232 }
233 DWORD pid = GetCurrentProcessId();
234 PROCESSENTRY32 pe32 = { .dwSize = sizeof(PROCESSENTRY32) };
235 DWORD visited_ps[32];
236 unsigned visited_ps_cnt = 0;
237 bool cycle = false;
238 while (!cycle && GetProcessInfo(hSnapshot, pid, &pe32)) {
239 MSG(DEBUG, "%s: %s PID: %ld, PPID: %ld\n", __func__,
240 pe32.szExeFile, pid, pe32.th32ParentProcessID);
241 if (_stricmp(pe32.szExeFile, name) == 0) {
242 CloseHandle(hSnapshot);
243 return true;
244 }
245
246 for (unsigned i = 0; i < visited_ps_cnt; i++) {
247 if (visited_ps[i] == pe32.th32ParentProcessID) {
248 cycle = true;
249 }
250 }
251 if (visited_ps_cnt ==
252 sizeof visited_ps / sizeof visited_ps[0]) {
253 MSG(WARNING, "Visited PS exhausted!\n");
254 break;
255 }
256 visited_ps[visited_ps_cnt++] = pid;
257
258 pid = pe32.th32ParentProcessID;
259 }
260 CloseHandle(hSnapshot);
261 return false;
262}
263
264unsigned long
265get_windows_build()
266{
267 RTL_OSVERSIONINFOW osVersionInfo = { .dwOSVersionInfoSize =
268 sizeof(RTL_OSVERSIONINFOW) };
269
270 HMODULE hNtDll = GetModuleHandleW(L"ntdll.dll");
271 if (hNtDll == NULL) {
272 MSG(VERBOSE, "Cannot load ntdll.dll!\n");
273 return 0;
274 }
275
276 typedef NTSTATUS(WINAPI * RtlGetVersionFunc)(
277 PRTL_OSVERSIONINFOW lpVersionInformation);
278 RtlGetVersionFunc pRtlGetVersion =
279 (RtlGetVersionFunc) GetProcAddress(hNtDll, "RtlGetVersion");
280 if (pRtlGetVersion == NULL) {
281 MSG(VERBOSE, "Cannot get RtlGetVersion from ntdll.dll!\n");
282 return 0;
283 }
284
285 if (pRtlGetVersion(&osVersionInfo) != STATUS_SUCCESS) {
286 MSG(VERBOSE, "Cannot get Windows version nfo!\n");
287 return 0;
288 }
289 MSG(DEBUG, "Windows version: %lu.%lu (build %lu)\n",
290 osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion,
291 osVersionInfo.dwBuildNumber);
292 return osVersionInfo.dwBuildNumber;
293}
294
295void
296print_stacktrace_win()
297{
298 void *stack[100];
299 unsigned short frames;
300 SYMBOL_INFO *symbol;
301 HANDLE process;
302
303 process = GetCurrentProcess();
304 SymInitialize(process, NULL, TRUE);
305
306 frames = CaptureStackBackTrace(0, 100, stack, NULL);
307 symbol =
308 (SYMBOL_INFO *) malloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char));
309 symbol->MaxNameLen = 255;
310 symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
311
312 char backtrace_msg[] = "Backtrace:\n";
313 _write(STDERR_FILENO, backtrace_msg, strlen(backtrace_msg));
314 for (unsigned short i = 0; i < frames; i++) {
315 SymFromAddr(process, (DWORD64) (stack[i]), 0, symbol);
316 char buf[STR_LEN];
317 snprintf_ch(buf, "%i (%p): %s - 0x%0llX\n", frames - i - 1,
318 stack[i], symbol->Name, symbol->Address);
319 _write(STDERR_FILENO, buf, strlen(buf));
320 }
321
322 free(symbol);
323}
324
325#endif // defined _WIN32
326
327/* vim: set expandtab sw=8 tw=120: */
#define TRUE
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
Definition debug.cpp:379
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define MSG(l, fmt,...)
Definition debug.h:96
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define snprintf_ch(str,...)
shortcut for snprintf(var, sizeof var...), var must be a char array
Definition macros.h:101
@ STR_LEN
"standard" string length placeholder
Definition macros.h:134
#define DEBUG
Definition main.c:27
const char * name
Definition settings.cpp:193
bool com_initialize(bool *com_initialized, const char *err_prefix)
Definition windows.c:65
void com_uninitialize(bool *com_initialized)
Definition windows.c:94
#define HRESULT_GET_ERROR_COMMON(res, errptr)
Definition windows.h:60