From 632b3953eed0fb5ffec1b265d7620d3c25ae8053 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Fri, 28 Feb 2025 15:45:21 -0600 Subject: [PATCH 1/5] Adds proper callback/monitoring of OBS for deeplinking launching OBS, opening owned products window, etc.. --- loader/main.cpp | 66 +++++++++++++++++++++++++++++++++++++-- src/elgato-cloud-data.cpp | 24 ++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/loader/main.cpp b/loader/main.cpp index 5028fd1..89a777b 100644 --- a/loader/main.cpp +++ b/loader/main.cpp @@ -144,6 +144,66 @@ int send_auth_to_obs(std::string payload) { return 0; } +int open_obs_mp_window() +{ + int pipe_number = 0; + std::string pipe_name = "elgato_cloud"; + std::string base_name = "\\\\.\\pipe\\" + pipe_name; + std::string attempt_name; + HANDLE pipe = INVALID_HANDLE_VALUE; + SECURITY_ATTRIBUTES sa; + SECURITY_DESCRIPTOR sd; + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + int connect_attempts_remaining = 60; + + printf("Waiting for OBS to launch..."); + + while (connect_attempts_remaining-- > 0 && + pipe == INVALID_HANDLE_VALUE) { + pipe_number = 0; + attempt_name = base_name + std::to_string(pipe_number); + printf("Attempting %s\n", attempt_name.c_str()); + pipe = CreateFileA(attempt_name.c_str(), GENERIC_WRITE, + 0, &sa, OPEN_EXISTING, 0, NULL); + if (pipe != INVALID_HANDLE_VALUE) { + printf("Success\n"); + break; + } + if (pipe == INVALID_HANDLE_VALUE) { + Sleep(1000); + } + } + if (pipe == INVALID_HANDLE_VALUE) { + printf("Could not connect to OBS!"); + return 1; + } + DWORD mode = PIPE_READMODE_MESSAGE; + auto success = SetNamedPipeHandleState(pipe, &mode, NULL, NULL); + if (!success) { + CloseHandle(pipe); + printf("Could not configure named pipe!"); + return 1; + } + + DWORD written = 0; + std::string payload = "elgatolink://open"; + success = WriteFile(pipe, payload.c_str(), static_cast(payload.size()), &written, + NULL); + if (!success || written < payload.size()) { + printf("Failed to write to named pipe!"); + CloseHandle(pipe); + return 1; + } + + CloseHandle(pipe); + return 0; +} + int launch_obs() { DWORD buffer_size = BUFFER_SIZE; @@ -216,8 +276,10 @@ int main(int argc, char *argv[]) { // Send auth to OBS which requested it. return send_auth_to_obs(payload); } else { - return launch_obs(); + int resp = launch_obs(); + open_obs_mp_window(); + //return open_obs_mp_window(); } printf("No obs found to launch."); - return 1; + //return 1; } diff --git a/src/elgato-cloud-data.cpp b/src/elgato-cloud-data.cpp index 873b9e1..b6b3cbc 100644 --- a/src/elgato-cloud-data.cpp +++ b/src/elgato-cloud-data.cpp @@ -169,6 +169,7 @@ void ElgatoCloud::_Listen() { _listenThread = std::thread([this]() { listen_on_pipe("elgato_cloud", [this](std::string d) { + obs_log(LOG_INFO, "Pipe received: %s", d.c_str()); if (d.find("elgatolink://auth") == 0) { if (mainWindowOpen && window) { QMetaObject::invokeMethod( @@ -242,6 +243,29 @@ void ElgatoCloud::_Listen() authorizing = false; return; } + else if (d.find("elgatolink://open") == 0) + { + obs_log(LOG_INFO, "OPEN COMMAND RECEIEVED!"); + if (mainWindowOpen && window) { + QMetaObject::invokeMethod( + QCoreApplication::instance() + ->thread(), + [this]() { + //window->setLoading(); + window->show(); + window->raise(); + window->activateWindow(); + LoadPurchasedProducts(); + }); + } else { + QMetaObject::invokeMethod( + QCoreApplication::instance() + ->thread(), + [this]() { + OpenElgatoCloudWindow(); + }); + } + } }); }); From 6fbe36fb113bf846e7f3acf1930829707cba2004 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Thu, 13 Mar 2025 14:51:57 -0500 Subject: [PATCH 2/5] Adds handling of open command via pipe. --- src/elgato-cloud-data.cpp | 27 +++++++++++++++++++++++++++ src/elgato-cloud-data.hpp | 4 ++++ 2 files changed, 31 insertions(+) diff --git a/src/elgato-cloud-data.cpp b/src/elgato-cloud-data.cpp index b6b3cbc..ef54c35 100644 --- a/src/elgato-cloud-data.cpp +++ b/src/elgato-cloud-data.cpp @@ -59,18 +59,41 @@ std::unique_lock *GetElgatoCloudLoopLock() ElgatoCloud::ElgatoCloud(obs_module_t *m) { + _openOnLaunch = false; + _obsReady = false; _modulePtr = m; //_translate = t; _securerand = QRandomGenerator::securelySeeded(); + obs_frontend_add_event_callback(ElgatoCloud::FrontEndEventHandler, this); _Initialize(); _Listen(); } ElgatoCloud::~ElgatoCloud() { + obs_frontend_remove_event_callback(ElgatoCloud::FrontEndEventHandler, this); obs_data_release(_config); } +void ElgatoCloud::FrontEndEventHandler(enum obs_frontend_event event, void* data) +{ + auto ec = static_cast(data); + switch (event) { + case OBS_FRONTEND_EVENT_FINISHED_LOADING: + ec->_obsReady = true; + if (ec->_openOnLaunch) { + ec->_openOnLaunch = false; + QMetaObject::invokeMethod( + QCoreApplication::instance() + ->thread(), + [ec]() { + OpenElgatoCloudWindow(); + }); + } + break; + } +} + obs_data_t *ElgatoCloud::GetConfig() { obs_data_addref(_config); @@ -246,6 +269,10 @@ void ElgatoCloud::_Listen() else if (d.find("elgatolink://open") == 0) { obs_log(LOG_INFO, "OPEN COMMAND RECEIEVED!"); + if (!_obsReady) { + _openOnLaunch = true; + return; + } if (mainWindowOpen && window) { QMetaObject::invokeMethod( QCoreApplication::instance() diff --git a/src/elgato-cloud-data.hpp b/src/elgato-cloud-data.hpp index ad33836..c3064fb 100644 --- a/src/elgato-cloud-data.hpp +++ b/src/elgato-cloud-data.hpp @@ -19,6 +19,7 @@ with this program. If not, see #pragma once #include +#include #include #include @@ -72,6 +73,7 @@ class ElgatoCloud { bool mainWindowOpen = false; ElgatoCloudWindow *window = nullptr; inline bool MakerToolsOnStart() const { return _makerToolsOnStart; } + static void FrontEndEventHandler(enum obs_frontend_event event, void* data); private: void _Initialize(); @@ -95,6 +97,8 @@ class ElgatoCloud { std::string _skipUpdate; obs_data_t *_config; bool _makerToolsOnStart; + bool _openOnLaunch; + bool _obsReady; }; class ElgatoCloudThread : public QThread { From 49b0ff3540f423c63f64ffe7444eff4655d45b63 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Thu, 13 Mar 2025 15:29:28 -0500 Subject: [PATCH 3/5] Adds more user friendly output for helper app, and does not automatically close if there is an issue. --- loader/main.cpp | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/loader/main.cpp b/loader/main.cpp index 89a777b..e685816 100644 --- a/loader/main.cpp +++ b/loader/main.cpp @@ -47,7 +47,7 @@ DWORD GetProcessIdFromExe(const std::wstring& exeName) { // Create a snapshot of all running processes HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { - printf("Error taking snapshot of processes."); + std::cerr << "[ERROR]: Error taking snapshot of processes." << std::endl; return 0; } @@ -100,17 +100,17 @@ int send_auth_to_obs(std::string payload) { sa.bInheritHandle = FALSE; int connect_attempts_remaining = 6; - + std::cout << "Attempting to connect to Marketplace Connect Plugin" << std::flush; while (connect_attempts_remaining-- > 0 && pipe == INVALID_HANDLE_VALUE) { pipe_number = 0; while (pipe_number < 10) { attempt_name = base_name + std::to_string(pipe_number); - printf("Attempting %s\n", attempt_name.c_str()); + std::cout << "." << std::flush; pipe = CreateFileA(attempt_name.c_str(), GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, NULL); if (pipe != INVALID_HANDLE_VALUE) { - printf("Success\n"); + std::cout << "\nSuccess" << std::endl; break; } pipe_number++; @@ -120,14 +120,14 @@ int send_auth_to_obs(std::string payload) { } } if (pipe == INVALID_HANDLE_VALUE) { - printf("Could not open named pipe!"); + std::cerr << "[ERROR] Could not find connection Marketplace Connect Plugin." << std::endl; return 1; } DWORD mode = PIPE_READMODE_MESSAGE; auto success = SetNamedPipeHandleState(pipe, &mode, NULL, NULL); if (!success) { CloseHandle(pipe); - printf("Could not configure named pipe!"); + std::cerr << "[ERROR] Could not negotiate connection with Marketplace Connect Plugin." << std::endl; return 1; } @@ -135,7 +135,7 @@ int send_auth_to_obs(std::string payload) { success = WriteFile(pipe, payload.c_str(), static_cast(payload.size()), &written, NULL); if (!success || written < payload.size()) { - printf("Failed to write to named pipe!"); + std::cerr << "[ERROR] Could not send data to Marketplace Connect Plugin." << std::endl; CloseHandle(pipe); return 1; } @@ -161,17 +161,17 @@ int open_obs_mp_window() int connect_attempts_remaining = 60; - printf("Waiting for OBS to launch..."); + std::cout << "Waiting for OBS to launch" << std::flush; while (connect_attempts_remaining-- > 0 && pipe == INVALID_HANDLE_VALUE) { pipe_number = 0; attempt_name = base_name + std::to_string(pipe_number); - printf("Attempting %s\n", attempt_name.c_str()); + std::cout << "." << std::flush; pipe = CreateFileA(attempt_name.c_str(), GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, NULL); if (pipe != INVALID_HANDLE_VALUE) { - printf("Success\n"); + std::cout << "\nConnected To OBS" << std::endl; break; } if (pipe == INVALID_HANDLE_VALUE) { @@ -179,14 +179,14 @@ int open_obs_mp_window() } } if (pipe == INVALID_HANDLE_VALUE) { - printf("Could not connect to OBS!"); + std::cerr << "[ERROR] Could not find connection Marketplace Connect Plugin." << std::endl; return 1; } DWORD mode = PIPE_READMODE_MESSAGE; auto success = SetNamedPipeHandleState(pipe, &mode, NULL, NULL); if (!success) { CloseHandle(pipe); - printf("Could not configure named pipe!"); + std::cerr << "[ERROR] Could not negotiate connection with Marketplace Connect Plugin." << std::endl; return 1; } @@ -195,7 +195,7 @@ int open_obs_mp_window() success = WriteFile(pipe, payload.c_str(), static_cast(payload.size()), &written, NULL); if (!success || written < payload.size()) { - printf("Failed to write to named pipe!"); + std::cerr << "[ERROR] Could not send data to Marketplace Connect Plugin." << std::endl; CloseHandle(pipe); return 1; } @@ -229,12 +229,12 @@ int launch_obs() STARTUPINFOW si = { sizeof(STARTUPINFO) }; PROCESS_INFORMATION pi; - printf("Try create %ls with %ls\n", launch_path.c_str(), - wd.c_str()); + //printf("Try create %ls with %ls\n", launch_path.c_str(), + // wd.c_str()); //WCHAR lpCommandLine[] = L""; if (CreateProcess(launch_path.c_str(), NULL, NULL, NULL, FALSE, 0, NULL, wd.c_str(), &si, &pi) == 0) { - printf("Failed to launch OBS."); + std::cerr << "[ERROR] Failed to launch OBS." << std::endl; return 1; } @@ -245,7 +245,7 @@ int launch_obs() } else if (running) { DWORD processId = GetProcessIdFromExe(L"obs64.exe"); if (processId == 0) { - printf("Process not found!"); + std::cerr << "[ERROR] OBS appears to have quit." << std::endl; return 1; } EnumWindows(WindowToForeground, processId); @@ -258,15 +258,13 @@ bool obs_is_running(std::wstring name) { if (!iswalnum(name[i])) name[i] = L'_'; } - - printf("CHECKING %ls\n", name.c_str()); HANDLE h = OpenMutexW(SYNCHRONIZE, false, name.c_str()); return !!h; } int main(int argc, char *argv[]) { if (argc < 2) { - printf("Please provide an argument"); + std::cerr << "[ERROR] No argument provided." << std::endl; return 1; } @@ -277,9 +275,9 @@ int main(int argc, char *argv[]) { return send_auth_to_obs(payload); } else { int resp = launch_obs(); - open_obs_mp_window(); - //return open_obs_mp_window(); + if (resp == 1) { + return 1; + } + return open_obs_mp_window(); } - printf("No obs found to launch."); - //return 1; } From 56d230e94e080b7959d2248c8461af8e99857909 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Thu, 13 Mar 2025 15:30:07 -0500 Subject: [PATCH 4/5] Updates version to 1.0.2 --- buildspec.json | 2 +- loader/main.cpp | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/buildspec.json b/buildspec.json index 2f306cd..5c37151 100644 --- a/buildspec.json +++ b/buildspec.json @@ -40,7 +40,7 @@ "displayName": "Elgato Marketplace Connect", "versionMajor": 1, "versionMinor": 0, - "versionPatch": 1, + "versionPatch": 2, "buildNumber": 0, "releaseType": "release", "author": "Elgato", diff --git a/loader/main.cpp b/loader/main.cpp index e685816..0669a11 100644 --- a/loader/main.cpp +++ b/loader/main.cpp @@ -265,19 +265,24 @@ bool obs_is_running(std::wstring name) { int main(int argc, char *argv[]) { if (argc < 2) { std::cerr << "[ERROR] No argument provided." << std::endl; + system("pause"); return 1; } + int resp; std::string payload = argv[1]; if (payload.find("elgatolink://auth") == 0) { // Send auth to OBS which requested it. - return send_auth_to_obs(payload); + resp = send_auth_to_obs(payload); } else { - int resp = launch_obs(); - if (resp == 1) { - return 1; - } - return open_obs_mp_window(); + resp = launch_obs(); + if(resp == 0) resp = open_obs_mp_window(); } + + if (resp == 1) { + system("pause"); + } + + return resp; } From ede3ba17b412b92ea9e56ed582e038c5c1ea0ba8 Mon Sep 17 00:00:00 2001 From: FiniteSingularity Date: Fri, 14 Mar 2025 15:10:51 -0500 Subject: [PATCH 5/5] Adds friendly name to mp-connect-loader app. --- cmake/windows/helpers.cmake | 3 + .../elgato-marketplace-connect-loader.rc.in | 110 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 cmake/windows/resources/elgato-marketplace-connect-loader.rc.in diff --git a/cmake/windows/helpers.cmake b/cmake/windows/helpers.cmake index 6887eb4..d224660 100644 --- a/cmake/windows/helpers.cmake +++ b/cmake/windows/helpers.cmake @@ -60,6 +60,9 @@ function(set_target_properties_plugin target) configure_file(cmake/windows/resources/resource.rc.in "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.rc") target_sources(${CMAKE_PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.rc") + + configure_file(cmake/windows/resources/elgato-marketplace-connect-loader.rc.in "${CMAKE_CURRENT_BINARY_DIR}/elgato-marketplace-connect-loader.rc") + target_sources(${CMAKE_PROJECT_NAME}-loader PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/elgato-marketplace-connect-loader.rc") endfunction() # Helper function to add resources into bundle diff --git a/cmake/windows/resources/elgato-marketplace-connect-loader.rc.in b/cmake/windows/resources/elgato-marketplace-connect-loader.rc.in new file mode 100644 index 0000000..f414444 --- /dev/null +++ b/cmake/windows/resources/elgato-marketplace-connect-loader.rc.in @@ -0,0 +1,110 @@ +// Microsoft Visual C++ generated resource script. +// +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by elgato-marketplace-connect-loader.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 + PRODUCTVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "${PLUGIN_AUTHOR}" + VALUE "FileDescription", "Marketplace Connect for OBS" + VALUE "FileVersion", "${PROJECT_VERSION}" + VALUE "InternalName", "elgato-m.exe" + VALUE "LegalCopyright", "(C) ${CURRENT_YEAR} ${PLUGIN_AUTHOR}" + VALUE "OriginalFilename", "elgato-m.exe" + VALUE "ProductName", "Marketplace Connect for OBS" + VALUE "ProductVersion", "${PROJECT_VERSION}" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED +