From 42b30b1bf8622ddb06979d669f1b43e25377c312 Mon Sep 17 00:00:00 2001 From: Hane Date: Tue, 6 Aug 2024 09:45:25 +0200 Subject: [PATCH] Og & wip: names --- qtest.pro | 2 +- src/back/backsessionclasses.cpp | 236 ++++++++++++++++++++++++++++---- src/back/backsessionclasses.h | 10 +- src/global.h | 2 + src/qt/qtclasses.cpp | 5 +- 5 files changed, 227 insertions(+), 28 deletions(-) diff --git a/qtest.pro b/qtest.pro index 303f059..b85fa53 100644 --- a/qtest.pro +++ b/qtest.pro @@ -1,4 +1,4 @@ -QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview +QMAKE_CXXFLAGS += --target=x86_64-w64-mingw32 -g -gcodeview -Og QMAKE_LFLAGS += --target=x86_64-w64-mingw32 -g -Wl,-pdb= -v LIBS += -LC:/capybara/libclang/x86_64-w64-mingw32/lib -lWinmm -lodbc32 -lodbccp32 -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 -lpropsys -static -stdlib=libc++ -lunwind #"kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" -luuid -loleaut32 -lole32 -lshell32 -ladvapi32 -lcomdlg32 -lwinspool -lgdi32 -luser32 -lkernel32 diff --git a/src/back/backsessionclasses.cpp b/src/back/backsessionclasses.cpp index ba0eaa6..41bf733 100644 --- a/src/back/backsessionclasses.cpp +++ b/src/back/backsessionclasses.cpp @@ -126,9 +126,12 @@ Session::Session(Endpoint* ep, IAudioSessionControl2* sessionControl, size_t idx else { LPWSTR sessionDisplayName; this->sessionControl->GetDisplayName(&sessionDisplayName); - if (!wcscmp(sessionDisplayName, L"")) - this->sessionName = this->fetchProcessName(pid); - else + if (!wcscmp(sessionDisplayName, L"")) { + std::wstring exePath; + if (getExePath(pid, &exePath)) + fetchName(exePath, pid); + else this->sessionName = std::wstring(LSTRING_UNNAMED_SESSION); + } else this->sessionName = std::wstring(sessionDisplayName); CoTaskMemFree(sessionDisplayName); } @@ -192,22 +195,20 @@ void Session::setMute(NGuid guid, bool muted) { if(FAILED(sessionVolume->SetMute(muted, &tempMsGuid))) { log_wdebugcpp(std::wstring(L"SessionVolume null?")); }; } -std::wstring Session::fetchProcessName(DWORD pid) { +bool Session::getExePath(DWORD pid, std::wstring *exePath) { /* * https://learn.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot * https://stackoverflow.com/questions/11843368/how-to-get-process-description * https://notes.indezine.com/2018/05/microsoft-locale-ids.html#:~:text=Wait%2C%201033%20is%20the%20decimal,ID%20for%20English%20%E2%80%93%20United%20States. * https://stackoverflow.com/questions/64321036/c-win32-getting-app-name-using-pid-and-executable-path */ - - /* Executable path retrieval */ - std::wstring exePath = L""; - std::wstring msixName; + + //std::wstring msixName; HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); if (processList == INVALID_HANDLE_VALUE) { log_wdebugcpp(L"aye no procname."); - return exePath; + return false; } MODULEENTRY32W me32w; @@ -215,7 +216,7 @@ std::wstring Session::fetchProcessName(DWORD pid) { if(Module32FirstW(processList, &me32w)) { do { if (me32w.th32ProcessID == pid) { - exePath = std::wstring(me32w.szExePath); + *exePath = std::wstring(me32w.szExePath); break; /* * However, if the calling process is a 32-bit process, you must call the @@ -226,7 +227,10 @@ std::wstring Session::fetchProcessName(DWORD pid) { } while(Module32NextW(processList, &me32w)); } CloseHandle(processList); + return true; +} +bool Session::fetchNameViaFD(std::wstring exePath, DWORD pid, std::wstring *sessionName) { /* File description retrieval: size and available lang-codepages */ struct LANGANDCODEPAGE { WORD wLanguage; @@ -236,16 +240,16 @@ std::wstring Session::fetchProcessName(DWORD pid) { DWORD filler; DWORD fileVersionInfoSize = GetFileVersionInfoSizeExW (FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, exePath.c_str(), &filler); - if (!fileVersionInfoSize) return exePath; + if (!fileVersionInfoSize) return false; void* fileVersionInfo = malloc(fileVersionInfoSize); if(!GetFileVersionInfoExW(FILE_VER_GET_LOCALISED | FILE_VER_GET_NEUTRAL, exePath.c_str(),0,fileVersionInfoSize, fileVersionInfo)) - return exePath; + return false; UINT translationArrayLen = 0; if (!VerQueryValueW(fileVersionInfo, L"\\VarFileInfo\\Translation", (LPVOID*)&translationArray, &translationArrayLen)) - return exePath; + return false; //File descriptor parsing //TODO: https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserpreferreduilanguages @@ -254,7 +258,7 @@ std::wstring Session::fetchProcessName(DWORD pid) { * sysem is put in place, I'll come finish this up. */ uint64_t availableLangs = (translationArrayLen / sizeof(LANGANDCODEPAGE)); - if (!availableLangs) return exePath; + if (!availableLangs) { free(fileVersionInfo); return false; } int8_t syslangIdx = -1; wchar_t metadataStringKey[256]; @@ -275,19 +279,30 @@ std::wstring Session::fetchProcessName(DWORD pid) { translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage); if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize) && metadataString[0] != '\0') { - return std::wstring(metadataString); + free(fileVersionInfo); + *sessionName = std::wstring(metadataString); + return true; } swprintf(metadataStringKey, L"\\StringFileInfo\\%04x%04x\\ProductName", translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wLanguage, translationArray[(syslangIdx < 0 ? 0 : syslangIdx)].wCodePage); if (VerQueryValueW(fileVersionInfo, metadataStringKey, (LPVOID*)&metadataString, &metadataStringSize) && metadataString[0] != '\0') { - return std::wstring(metadataString); + free(fileVersionInfo); + *sessionName = std::wstring(metadataString); + return true; } - //MSIX? + if(fileVersionInfo) + free(fileVersionInfo); + + return false; +} + + +bool Session::fetchNameViaMSIX(std::wstring exePath, DWORD pid, std::wstring *sessionName) { HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); - if(!process) return exePath; + if(!process) return false; //constant missing in mingw64. TB removed when I upgrade to a mingw64 ver that has it #define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130 @@ -295,7 +310,7 @@ std::wstring Session::fetchProcessName(DWORD pid) { PWSTR userModelId = (PWSTR)malloc(length * sizeof(wchar_t)); if(GetApplicationUserModelId(process, &length, userModelId) != ERROR_SUCCESS) { CloseHandle(process); - return exePath; + return false; } CloseHandle(process); @@ -323,17 +338,190 @@ std::wstring Session::fetchProcessName(DWORD pid) { LPWSTR humanName = nullptr; si->GetDisplayName(SIGDN_NORMALDISPLAY, &humanName); if(humanName && humanName[0] != '\0') { - msixName = std::wstring(humanName); + *sessionName = std::wstring(humanName); CoTaskMemFree(humanName); } if(si) si->Release(); - if (msixName.length() > 0) - return msixName; - else return exePath; - //free(fileVersionInfo); + if (sessionName->length() > 0) + return true; + else return false; + + } +BOOL test(HWND hwnd, LPARAM lParam) { + int length = GetWindowTextLength(hwnd); + wchar_t* buffer = new wchar_t[length + 1]; + GetWindowTextW(hwnd, buffer, length + 1); + std::wstring windowTitle(buffer); + delete[] buffer; + + // List visible windows with a non-empty title + if (IsWindowVisible(hwnd) && length != 0) { + //std::wcout << hwnd << ": " << windowTitle << std::endl; + log_wdebugcpp(windowTitle); + return FALSE; + } + return TRUE; + + /* + * auto pParams = (DWORD)(lParam); + * + * DWORD processId; + * if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams) { + * // Stop enumerating + * //SetLastError(-1); + * //pParams->first = hwnd; + * //return FALSE; + * } + * + * // Continue enumerating + * return TRUE; + */ +} + + +bool Session::fetchNameViaWindowName(std::wstring exePath, DWORD pid, std::wstring *sessionName) { + std::pair params = { 0, pid }; + + BOOL result = EnumWindows(test, NULL); + + /* + * if (!params.first) goto msix; + */ + + + /* + * if (!result && GetLastError() == -1 && params.first) { + * return params.first; + * } + */ +} + +void Session::fetchName(std::wstring exePath, DWORD pid) { + if(fetchNameViaWindowName(exePath, pid, &this->sessionName)) + return; + else if(fetchNameViaMSIX(exePath, pid, &this->sessionName)) + return; + else if(!fetchNameViaFD(exePath, pid, &this->sessionName)) + this->sessionName = exePath; + + /* + * std::thread ttest(&Session::fetchNameViaWindowName, this, exePath, pid, &this->sessionName); + * ttest.join(); + */ +} + +/* + * std::wstring Session::fetchProcessName(DWORD pid) { + * /\* Executable path retrieval *\/ + * std::wstring exePath = L""; + * std::wstring msixName; + * + * HANDLE processList = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, pid); + * if (processList == INVALID_HANDLE_VALUE) { + * log_wdebugcpp(L"aye no procname."); + * return exePath; + * } + * + * MODULEENTRY32W me32w; + * me32w.dwSize = sizeof(MODULEENTRY32W); + * if(Module32FirstW(processList, &me32w)) { + * do { + * if (me32w.th32ProcessID == pid) { + * exePath = std::wstring(me32w.szExePath); + * break; + * /\* + * * However, if the calling process is a 32-bit process, you must call the + * * QueryFullProcessImageName function to retrieve the full path of the + * * executable file for a 64-bit process. + * *\/ + * } + * } while(Module32NextW(processList, &me32w)); + * } + * CloseHandle(processList); + * + * + * //No FD info available. Window name? + * nofdinfo: + * if(fileVersionInfo) + * free(fileVersionInfo); + * + * std::pair params = { 0, pid }; + * BOOL result = EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL { + * auto pParams = (std::pair*)(lParam); + * + * DWORD processId; + * if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second) { + * // Stop enumerating + * SetLastError(-1); + * pParams->first = hwnd; + * return FALSE; + * } + * + * // Continue enumerating + * return TRUE; + * }, (LPARAM)¶ms); + * + * if (!params.first) goto msix; + * + * + * /\* + * * if (!result && GetLastError() == -1 && params.first) { + * * return params.first; + * * } + * *\/ + * + * //No window info. MSIX? + * HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); + * if(!process) return exePath; + * + * //constant missing in mingw64. TB removed when I upgrade to a mingw64 ver that has it + * #define APPLICATION_USER_MODEL_ID_MAX_LENGTH 130 + * uint32_t length = APPLICATION_USER_MODEL_ID_MAX_LENGTH; + * PWSTR userModelId = (PWSTR)malloc(length * sizeof(wchar_t)); + * if(GetApplicationUserModelId(process, &length, userModelId) != ERROR_SUCCESS) { + * CloseHandle(process); + * return exePath; + * } + * CloseHandle(process); + * + * static constexpr wchar_t* prefix = L"shell:appsfolder\\"; + * uint32_t prefixLen = wcslen(prefix); + * uint32_t userModelIdLen = wcslen(userModelId); + * wchar_t* fullName; + * fullName = (prefixLen + userModelIdLen < length) + * ? (wchar_t*)malloc(length * sizeof(wchar_t)) + * : (wchar_t*)malloc((length * 2) * sizeof(wchar_t)); + * for (int32_t i = prefixLen - 1; i >= 0; i--) { + * fullName[i] = prefix[i]; + * } + * for (uint32_t i = 0; i < userModelIdLen + 1; i++) { + * fullName[prefixLen + i] = userModelId[i]; + * } + * + * IShellItem* si; + * HRESULT hr = SHCreateItemFromParsingName(fullName, + * nullptr, + * IID_IShellItem, + * (void**)&si + * ); + * free(fullName); + * LPWSTR humanName = nullptr; + * si->GetDisplayName(SIGDN_NORMALDISPLAY, &humanName); + * if(humanName && humanName[0] != '\0') { + * msixName = std::wstring(humanName); + * CoTaskMemFree(humanName); + * } + * if(si) si->Release(); + * + * if (msixName.length() > 0) + * return msixName; + * else return exePath; + * } + */ + //todo: conflicting names. change callback name void Session::setState(SessionState state) { sessionState = state; diff --git a/src/back/backsessionclasses.h b/src/back/backsessionclasses.h index 2b5db99..9e0f7a8 100644 --- a/src/back/backsessionclasses.h +++ b/src/back/backsessionclasses.h @@ -4,6 +4,7 @@ #include "global.h" #include "contclasses.h" +#include class Endpoint; class SessionStateCallback : public IAudioSessionEvents { @@ -47,13 +48,18 @@ class Session { //uint32_t getChannelCount(); private: - std::wstring fetchProcessName(DWORD pid); + bool getExePath(DWORD pid, /*out*/ std::wstring *exePath); + void fetchName(std::wstring exePath, DWORD pid); + bool fetchNameViaFD(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); + bool fetchNameViaMSIX(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); + bool fetchNameViaWindowName(std::wstring exePath, DWORD pid, /*out*/ std::wstring *sessionName); + /* std::wstring fetchProcessName(DWORD pid); */ std::wstring sessionName; SessionState sessionState; Endpoint* ep; IAudioSessionControl2* sessionControl = nullptr; IAudioMeterInformation* meterInformation = nullptr; ISimpleAudioVolume* sessionVolume = nullptr; - size_t idx; + size_t idx; }; diff --git a/src/global.h b/src/global.h index b8b9bcb..5d327dc 100644 --- a/src/global.h +++ b/src/global.h @@ -30,6 +30,8 @@ #define STRING_CP "Open Control Panel" #define STRING_ABOUT "About" #define STRING_STARTUP "Run at startup" + +#define LSTRING_UNNAMED_SESSION L"Unnamed session" //INIT BACK enum AudioChannel { diff --git a/src/qt/qtclasses.cpp b/src/qt/qtclasses.cpp index 48b248c..cbe8106 100644 --- a/src/qt/qtclasses.cpp +++ b/src/qt/qtclasses.cpp @@ -326,6 +326,7 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) muteButton = new QCheckBox(this); mainLabel = new QLabel(QString::fromStdWString(sh->getName()), this); + mainLabel->setToolTip(QString::fromStdWString(sh->getName())); mainSlider = new MeterSlider(Qt::Horizontal, this); //mainLabel->setMaximumWidth(150 /*1/16ish 1080p*/); @@ -368,8 +369,10 @@ SessionWidget::SessionWidget(uint64_t idx, SessionHandler* sh, QWidget *parent) connect(volumePoller, &QTimer::timeout, [this, sh](){ //if (memcmp(osh->callbackInfo[idx]->caller, osh->getGuid(), sizeof(NGuid)) == 0) return; CHECK IF THIS PROGRAM GENERATED THE FUNSIES IS NO LONGER IN USE FOR NOW. //todo: global + constexpr + ratio - if (sh->getVolumeInfo()->isNameChanged) + if (sh->getVolumeInfo()->isNameChanged) { mainLabel->setText(QString::fromStdWString(sh->getName())); + mainLabel->setToolTip(QString::fromStdWString(sh->getName())); + } const float roundingFactor = 0.005; mainSlider->blockSignals(true); muteButton->blockSignals(true);