39#include <audioclient.h>
53#define MOD_NAME "[utils/win] "
67 if (*com_initialized) {
68 MSG(WARNING,
"com_initialized should be initalized to false "
72 if (err_prefix == NULL) {
75 *com_initialized =
false;
77 HRESULT result = CoInitializeEx(NULL, COINIT_MULTITHREADED);
78 if (SUCCEEDED(result)) {
79 *com_initialized =
true;
82 if (result == RPC_E_CHANGED_MODE) {
89 (void) com_initialized, (
void) err_prefix;
97 if (!*com_initialized) {
100 *com_initialized =
false;
103 (void) com_initialized;
108const char *hresult_to_str(HRESULT res) {
109 _Thread_local
static char unknown[128];
110 const char *errptr = NULL;
112 if (HRESULT_FACILITY(res) == FACILITY_WIN32) {
113 return get_win32_error(HRESULT_CODE(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.";
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);
160get_win32_error(DWORD error)
162 _Thread_local
static char buf[1024] =
"(unknown)";
163 if (FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
166 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
171 char *end = buf + strlen(buf) - 1;
172 if (end > buf && *end ==
'\n') {
175 if (end > buf && *end ==
'\r') {
181 snprintf(buf,
sizeof buf,
"[Could not find a description for error # %#lx.]", error);
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 , res,
sizeof res - 1, NULL, NULL);
194 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
197 "win_wstr_to_str: Insufficient buffer length %zd. ",
207GetProcessInfo(HANDLE hSnapshot, DWORD processId, PROCESSENTRY32 *pe32)
209 if (!Process32First(hSnapshot, pe32)) {
214 if (pe32->th32ProcessID == processId) {
217 }
while (Process32Next(hSnapshot, pe32));
227win_has_ancestor_process(
const char *
name)
229 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
230 if (hSnapshot == INVALID_HANDLE_VALUE) {
233 DWORD pid = GetCurrentProcessId();
234 PROCESSENTRY32 pe32 = { .dwSize =
sizeof(PROCESSENTRY32) };
235 DWORD visited_ps[32];
236 unsigned visited_ps_cnt = 0;
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);
246 for (
unsigned i = 0; i < visited_ps_cnt; i++) {
247 if (visited_ps[i] == pe32.th32ParentProcessID) {
251 if (visited_ps_cnt ==
252 sizeof visited_ps /
sizeof visited_ps[0]) {
253 MSG(WARNING,
"Visited PS exhausted!\n");
256 visited_ps[visited_ps_cnt++] = pid;
258 pid = pe32.th32ParentProcessID;
260 CloseHandle(hSnapshot);
267 RTL_OSVERSIONINFOW osVersionInfo = { .dwOSVersionInfoSize =
268 sizeof(RTL_OSVERSIONINFOW) };
270 HMODULE hNtDll = GetModuleHandleW(L
"ntdll.dll");
271 if (hNtDll == NULL) {
272 MSG(VERBOSE,
"Cannot load ntdll.dll!\n");
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");
285 if (pRtlGetVersion(&osVersionInfo) != STATUS_SUCCESS) {
286 MSG(VERBOSE,
"Cannot get Windows version nfo!\n");
289 MSG(
DEBUG,
"Windows version: %lu.%lu (build %lu)\n",
290 osVersionInfo.dwMajorVersion, osVersionInfo.dwMinorVersion,
291 osVersionInfo.dwBuildNumber);
292 return osVersionInfo.dwBuildNumber;
296print_stacktrace_win()
299 unsigned short frames;
303 process = GetCurrentProcess();
304 SymInitialize(process, NULL,
TRUE);
306 frames = CaptureStackBackTrace(0, 100, stack, NULL);
308 (SYMBOL_INFO *) malloc(
sizeof(SYMBOL_INFO) + 256 *
sizeof(
char));
309 symbol->MaxNameLen = 255;
310 symbol->SizeOfStruct =
sizeof(SYMBOL_INFO);
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);
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));
void log_msg(int level, const char *format,...)
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_WARNING
less severe errors
#define snprintf_ch(str,...)
shortcut for snprintf(var, sizeof var...), var must be a char array
@ STR_LEN
"standard" string length placeholder
bool com_initialize(bool *com_initialized, const char *err_prefix)
void com_uninitialize(bool *com_initialized)
#define HRESULT_GET_ERROR_COMMON(res, errptr)