UI: Warn user if multiple instances of the UI are open

Uses a named mutex to detect if multiple instances of the program are
open, and if so warns the user.  When running in portable mode, uses a
separate unique mutex name mapped to the user's config directory to
ensure that no two portable builds use the same config directory.  This
way, portable builds do not conflict with normal builds or other
separate portable builds.
master
jp9000 2017-05-14 15:25:34 -07:00
parent 99f5ae3059
commit 96ce9633e0
5 changed files with 126 additions and 1 deletions

View File

@ -72,6 +72,11 @@ RemuxRecordings="Remux Recordings"
Next="Next"
Back="Back"
# warning if program already open
AlreadyRunning.Title="OBS is already running"
AlreadyRunning.Text="OBS is already running! Unless you meant to do this, please shut down any existing instances of OBS before trying to run a new instance. If you have OBS set to minimize to the system tray, please check to see if it's still running there."
AlreadyRunning.LaunchAnyway="Launch Anyway"
# copy filters
Copy.Filters="Copy Filters"
Paste.Filters="Paste Filters"

View File

@ -61,7 +61,7 @@ static log_handler_t def_log_handler;
static string currentLogFile;
static string lastLogFile;
static bool portable_mode = false;
bool portable_mode = false;
static bool log_verbose = false;
static bool unfiltered_log = false;
bool opt_start_streaming = false;
@ -1327,6 +1327,46 @@ static int run_program(fstream &logFile, int argc, char *argv[])
program.installTranslator(&translator);
#ifdef _WIN32
/* --------------------------------------- */
/* check and warn if already running */
bool already_running = false;
RunOnceMutex rom = GetRunOnceMutex(already_running);
if (already_running) {
blog(LOG_WARNING, "\n================================");
blog(LOG_WARNING, "Warning: OBS is already running!");
blog(LOG_WARNING, "================================\n");
QMessageBox::StandardButtons buttons(
QMessageBox::Yes | QMessageBox::Cancel);
QMessageBox mb(QMessageBox::Question,
QTStr("AlreadyRunning.Title"),
QTStr("AlreadyRunning.Text"),
buttons,
nullptr);
mb.setButtonText(QMessageBox::Yes,
QTStr("AlreadyRunning.LaunchAnyway"));
mb.setButtonText(QMessageBox::Cancel, QTStr("Cancel"));
mb.setDefaultButton(QMessageBox::Cancel);
QMessageBox::StandardButton button;
button = (QMessageBox::StandardButton)mb.exec();
if (button == QMessageBox::Cancel) {
blog(LOG_INFO, "User shut down the program "
"because OBS was already "
"running");
return 0;
}
blog(LOG_WARNING, "User is now running a secondary "
"instance of OBS!");
}
/* --------------------------------------- */
#endif
if (!program.OBSInit())
return 0;

View File

@ -176,6 +176,7 @@ static inline int GetProfilePath(char *path, size_t size, const char *file)
return window->GetProfilePath(path, size, file);
}
extern bool portable_mode;
extern bool opt_start_streaming;
extern bool opt_start_recording;
extern bool opt_start_replaybuffer;

View File

@ -34,6 +34,7 @@ using namespace std;
#include <mmdeviceapi.h>
#include <audiopolicy.h>
#include <util/windows/WinHandle.hpp>
#include <util/windows/HRError.hpp>
#include <util/windows/ComPtr.hpp>
@ -271,3 +272,65 @@ uint64_t CurrentMemoryUsage()
return (uint64_t)pmc.WorkingSetSize;
}
struct RunOnceMutexData {
WinHandle handle;
inline RunOnceMutexData(HANDLE h) : handle(h) {}
};
RunOnceMutex::RunOnceMutex(RunOnceMutex &&rom)
{
delete data;
data = rom.data;
rom.data = nullptr;
}
RunOnceMutex::~RunOnceMutex()
{
delete data;
}
RunOnceMutex &RunOnceMutex::operator=(RunOnceMutex &&rom)
{
delete data;
data = rom.data;
rom.data = nullptr;
return *this;
}
RunOnceMutex GetRunOnceMutex(bool &already_running)
{
string name;
if (!portable_mode) {
name = "OBSStudioCore";
} else {
char path[500];
*path = 0;
GetConfigPath(path, sizeof(path), "");
name = "OBSStudioPortable";
name += path;
}
BPtr<wchar_t> wname;
os_utf8_to_wcs_ptr(name.c_str(), name.size(), &wname);
if (wname) {
wchar_t *temp = wname;
while (*temp) {
if (!iswalnum(*temp))
*temp = L'_';
temp++;
}
}
HANDLE h = OpenMutexW(SYNCHRONIZE, false, wname.Get());
already_running = !!h;
if (!already_running)
h = CreateMutexW(nullptr, false, wname.Get());
RunOnceMutex rom(h ? new RunOnceMutexData(h) : nullptr);
return rom;
}

View File

@ -44,6 +44,22 @@ void SetProcessPriority(const char *priority);
void SetWin32DropStyle(QWidget *window);
bool DisableAudioDucking(bool disable);
uint64_t CurrentMemoryUsage();
struct RunOnceMutexData;
class RunOnceMutex {
RunOnceMutexData *data = nullptr;
public:
RunOnceMutex(RunOnceMutexData *data_) : data(data_) {}
RunOnceMutex(const RunOnceMutex &rom) = delete;
RunOnceMutex(RunOnceMutex &&rom);
~RunOnceMutex();
RunOnceMutex &operator=(const RunOnceMutex &rom) = delete;
RunOnceMutex &operator=(RunOnceMutex &&rom);
};
RunOnceMutex GetRunOnceMutex(bool &already_running);
#endif
#ifdef __APPLE__