mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-08 02:50:31 +00:00
Add new API calls for embedding renderdoc in apps
* This will easily let an application tell if an instance of the replay UI is currently connected, and if not launch it and connect. Good for integrating renderdoc somewhere where you can trigger a capture, and then have the replay UI pop up to either connect live, or open the most recently log.
This commit is contained in:
@@ -199,6 +199,17 @@ enum InAppOverlay
|
||||
eOverlay_None = 0,
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// !!!! IMPORTANT NOTE !!!! //
|
||||
// //
|
||||
// This API is pretty much experimental and //
|
||||
// still in flux. The only thing guaranteed //
|
||||
// to remain compatible is a call to //
|
||||
// RENDERDOC_GetAPIVersion which must exactly //
|
||||
// match the version you expect. //
|
||||
// It will be bumped on breaking changes. //
|
||||
////////////////////////////////////////////////
|
||||
|
||||
// API breaking change history:
|
||||
// Version 1 -> 2 - strings changed from wchar_t* to char* (UTF-8)
|
||||
// Version 2 -> 3 - StartFrameCapture, EndFrameCapture and SetActiveWindow take
|
||||
@@ -260,5 +271,11 @@ typedef void (RENDERDOC_CC *pRENDERDOC_SetCaptureKeys)(KeyButton *keys, int num)
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_InitRemoteAccess(uint32_t *ident);
|
||||
typedef void (RENDERDOC_CC *pRENDERDOC_InitRemoteAccess)(uint32_t *ident);
|
||||
|
||||
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_IsRemoteAccessConnected();
|
||||
typedef uint32_t (RENDERDOC_CC *pRENDERDOC_IsRemoteAccessConnected)();
|
||||
|
||||
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_LaunchReplayUI(uint32_t connectRemoteAccess, const char *cmdline);
|
||||
typedef uint32_t (RENDERDOC_CC *pRENDERDOC_LaunchReplayUI)(uint32_t connectRemoteAccess, const char *cmdline);
|
||||
|
||||
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_UnloadCrashHandler();
|
||||
typedef void (RENDERDOC_CC *pRENDERDOC_UnloadCrashHandler)();
|
||||
|
||||
@@ -394,6 +394,12 @@ bool RenderDoc::EndFrameCapture(void *dev, void *wnd)
|
||||
return it->second.FrameCapturer->EndFrameCapture(dev, wnd);
|
||||
}
|
||||
|
||||
bool RenderDoc::IsRemoteAccessConnected()
|
||||
{
|
||||
SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock);
|
||||
return !RenderDoc::Inst().m_SingleClientName.empty();
|
||||
}
|
||||
|
||||
void RenderDoc::Tick()
|
||||
{
|
||||
static bool prev_focus = false;
|
||||
|
||||
@@ -232,6 +232,7 @@ class RenderDoc
|
||||
void GetCurrentDriver(RDCDriver &driver, string &name);
|
||||
|
||||
uint32_t GetRemoteAccessIdent() const { return m_RemoteIdent; }
|
||||
bool IsRemoteAccessConnected();
|
||||
|
||||
void Tick();
|
||||
|
||||
|
||||
@@ -34,13 +34,176 @@
|
||||
|
||||
#include "serialise/string_utils.h"
|
||||
|
||||
pid_t RunProcess(const char *app, const char *workingDir, const char *cmdLine, char *const *envp)
|
||||
{
|
||||
if(!app) return (pid_t)0;
|
||||
|
||||
int argc = 0;
|
||||
char *emptyargv[] = { NULL };
|
||||
char **argv = emptyargv;
|
||||
|
||||
const char *c = cmdLine;
|
||||
|
||||
// parse command line into argv[], similar to how bash would
|
||||
if(cmdLine)
|
||||
{
|
||||
argc = 1;
|
||||
|
||||
// get a rough upper bound on the number of arguments
|
||||
while(*c)
|
||||
{
|
||||
if(*c == ' ' || *c == '\t') argc++;
|
||||
c++;
|
||||
}
|
||||
|
||||
argv = new char*[argc+2];
|
||||
|
||||
c = cmdLine;
|
||||
|
||||
string a;
|
||||
|
||||
argc = 0; // current argument we're fetching
|
||||
|
||||
// argv[0] is the application name, by convention
|
||||
size_t len = strlen(app)+1;
|
||||
argv[argc] = new char[len];
|
||||
strcpy(argv[argc], app);
|
||||
|
||||
argc++;
|
||||
|
||||
bool dquot = false, squot = false; // are we inside ''s or ""s
|
||||
while(*c)
|
||||
{
|
||||
if(!dquot && !squot && (*c == ' ' || *c == '\t'))
|
||||
{
|
||||
if(!a.empty())
|
||||
{
|
||||
// if we've fetched some number of non-space characters
|
||||
argv[argc] = new char[a.length()+1];
|
||||
memcpy(argv[argc], a.c_str(), a.length()+1);
|
||||
argc++;
|
||||
}
|
||||
|
||||
a = "";
|
||||
}
|
||||
else if(!dquot && *c == '"')
|
||||
{
|
||||
dquot = true;
|
||||
}
|
||||
else if(!squot && *c == '\'')
|
||||
{
|
||||
squot = true;
|
||||
}
|
||||
else if(dquot && *c == '"')
|
||||
{
|
||||
dquot = false;
|
||||
}
|
||||
else if(squot && *c == '\'')
|
||||
{
|
||||
squot = false;
|
||||
}
|
||||
else if(squot)
|
||||
{
|
||||
// single quotes don't escape, just copy literally until we leave single quote mode
|
||||
a.push_back(*c);
|
||||
}
|
||||
else if(dquot)
|
||||
{
|
||||
// handle escaping
|
||||
if(*c == '\\')
|
||||
{
|
||||
c++;
|
||||
if(*c)
|
||||
{
|
||||
a.push_back(*c);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Malformed command line:\n%s", cmdLine);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a.push_back(*c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a.push_back(*c);
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
if(!a.empty())
|
||||
{
|
||||
// if we've fetched some number of non-space characters
|
||||
argv[argc] = new char[a.length()+1];
|
||||
memcpy(argv[argc], a.c_str(), a.length()+1);
|
||||
argc++;
|
||||
}
|
||||
|
||||
argv[argc] = NULL;
|
||||
|
||||
if(squot || dquot)
|
||||
{
|
||||
RDCERR("Malformed command line\n%s", cmdLine);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
pid_t childPid = fork();
|
||||
if(childPid == 0)
|
||||
{
|
||||
if(workingDir)
|
||||
{
|
||||
chdir(workingDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
string exedir = app;
|
||||
chdir(dirname((char *)exedir.c_str()));
|
||||
}
|
||||
|
||||
execve(app, argv, envp);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
char **argv_delete = argv;
|
||||
|
||||
if(argv != emptyargv)
|
||||
{
|
||||
while(*argv)
|
||||
{
|
||||
delete[] *argv;
|
||||
argv++;
|
||||
}
|
||||
|
||||
delete[] argv_delete;
|
||||
}
|
||||
|
||||
return childPid;
|
||||
}
|
||||
|
||||
uint32_t Process::InjectIntoProcess(uint32_t pid, const char *logfile, const CaptureOptions *opts, bool waitForExit)
|
||||
{
|
||||
RDCUNIMPLEMENTED("Injecting into already running processes on linux");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Process::CreateAndInjectIntoProcess(const char *app, const char *workingDir, const char *cmdLine,
|
||||
uint32_t Process::LaunchProcess(const char *app, const char *workingDir, const char *cmdLine)
|
||||
{
|
||||
if(app == NULL || app[0] == 0)
|
||||
{
|
||||
RDCERR("Invalid empty 'app'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (uint32_t)RunProcess(app, workingDir, cmdLine, environ);
|
||||
}
|
||||
|
||||
uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workingDir, const char *cmdLine,
|
||||
const char *logfile, const CaptureOptions *opts, bool waitForExit)
|
||||
{
|
||||
if(app == NULL || app[0] == 0)
|
||||
@@ -160,140 +323,11 @@ uint32_t Process::CreateAndInjectIntoProcess(const char *app, const char *workin
|
||||
envp[i] = NULL;
|
||||
}
|
||||
|
||||
int argc = 0;
|
||||
char *emptyargv[] = { NULL };
|
||||
char **argv = emptyargv;
|
||||
|
||||
const char *c = cmdLine;
|
||||
|
||||
// parse command line into argv[], similar to how bash would
|
||||
if(cmdLine)
|
||||
{
|
||||
argc = 1;
|
||||
|
||||
// get a rough upper bound on the number of arguments
|
||||
while(*c)
|
||||
{
|
||||
if(*c == ' ' || *c == '\t') argc++;
|
||||
c++;
|
||||
}
|
||||
|
||||
argv = new char*[argc+2];
|
||||
|
||||
c = cmdLine;
|
||||
|
||||
string a;
|
||||
|
||||
argc = 0; // current argument we're fetching
|
||||
|
||||
// argv[0] is the application name, by convention
|
||||
size_t len = strlen(app)+1;
|
||||
argv[argc] = new char[len];
|
||||
strcpy(argv[argc], app);
|
||||
|
||||
argc++;
|
||||
|
||||
bool dquot = false, squot = false; // are we inside ''s or ""s
|
||||
while(*c)
|
||||
{
|
||||
if(!dquot && !squot && (*c == ' ' || *c == '\t'))
|
||||
{
|
||||
if(!a.empty())
|
||||
{
|
||||
// if we've fetched some number of non-space characters
|
||||
argv[argc] = new char[a.length()+1];
|
||||
memcpy(argv[argc], a.c_str(), a.length()+1);
|
||||
argc++;
|
||||
}
|
||||
|
||||
a = "";
|
||||
}
|
||||
else if(!dquot && *c == '"')
|
||||
{
|
||||
dquot = true;
|
||||
}
|
||||
else if(!squot && *c == '\'')
|
||||
{
|
||||
squot = true;
|
||||
}
|
||||
else if(dquot && *c == '"')
|
||||
{
|
||||
dquot = false;
|
||||
}
|
||||
else if(squot && *c == '\'')
|
||||
{
|
||||
squot = false;
|
||||
}
|
||||
else if(squot)
|
||||
{
|
||||
// single quotes don't escape, just copy literally until we leave single quote mode
|
||||
a.push_back(*c);
|
||||
}
|
||||
else if(dquot)
|
||||
{
|
||||
// handle escaping
|
||||
if(*c == '\\')
|
||||
{
|
||||
c++;
|
||||
if(*c)
|
||||
{
|
||||
a.push_back(*c);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("Malformed command line:\n%s", cmdLine);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a.push_back(*c);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a.push_back(*c);
|
||||
}
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
if(!a.empty())
|
||||
{
|
||||
// if we've fetched some number of non-space characters
|
||||
argv[argc] = new char[a.length()+1];
|
||||
memcpy(argv[argc], a.c_str(), a.length()+1);
|
||||
argc++;
|
||||
}
|
||||
|
||||
argv[argc] = NULL;
|
||||
|
||||
if(squot || dquot)
|
||||
{
|
||||
RDCERR("Malformed command line\n%s", cmdLine);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pid_t childPid = RunProcess(app, workingDir, cmdLine, envp);
|
||||
|
||||
int ret = 0;
|
||||
|
||||
pid_t childPid = fork();
|
||||
if(childPid == 0)
|
||||
{
|
||||
if(workingDir)
|
||||
{
|
||||
chdir(workingDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
string exedir = app;
|
||||
chdir(dirname((char *)exedir.c_str()));
|
||||
}
|
||||
|
||||
execve(app, argv, envp);
|
||||
exit(0);
|
||||
}
|
||||
else if(childPid > 0)
|
||||
if(childPid != (pid_t)0)
|
||||
{
|
||||
// wait for child to have /proc/<pid> and read out tcp socket
|
||||
usleep(1000);
|
||||
@@ -343,20 +377,8 @@ uint32_t Process::CreateAndInjectIntoProcess(const char *app, const char *workin
|
||||
}
|
||||
}
|
||||
|
||||
char **argv_delete = argv;
|
||||
char **envp_delete = envp;
|
||||
|
||||
if(argv != emptyargv)
|
||||
{
|
||||
while(*argv)
|
||||
{
|
||||
delete[] *argv;
|
||||
argv++;
|
||||
}
|
||||
|
||||
delete[] argv_delete;
|
||||
}
|
||||
|
||||
while(*envp)
|
||||
{
|
||||
delete[] *envp;
|
||||
|
||||
@@ -39,10 +39,16 @@
|
||||
|
||||
#include <iconv.h>
|
||||
|
||||
// for dladdr
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "common/string_utils.h"
|
||||
#include "common/threading.h"
|
||||
using std::string;
|
||||
|
||||
// gives us an address to identify this so with
|
||||
static int soLocator=0;
|
||||
|
||||
namespace Keyboard
|
||||
{
|
||||
void Init()
|
||||
@@ -147,6 +153,55 @@ namespace FileIO
|
||||
selfName = string(path);
|
||||
}
|
||||
|
||||
string GetReplayAppFilename()
|
||||
{
|
||||
// look up the shared object's path via dladdr
|
||||
Dl_info info;
|
||||
dladdr((void *)&soLocator, &info);
|
||||
string path = info.dli_fname ? info.dli_fname : "";
|
||||
path = dirname(path);
|
||||
string replay = path + "/qrenderdoc";
|
||||
|
||||
FILE *f = FileIO::fopen(replay.c_str(), "r");
|
||||
if(f)
|
||||
{
|
||||
FileIO::fclose(f);
|
||||
return replay;
|
||||
}
|
||||
|
||||
// if it's not in the same directory, try in a sibling /bin
|
||||
// e.g. /foo/bar/lib/librenderdoc.so -> /foo/bar/bin/qrenderdoc
|
||||
replay = path + "/../bin/qrenderdoc";
|
||||
|
||||
f = FileIO::fopen(replay.c_str(), "r");
|
||||
if(f)
|
||||
{
|
||||
FileIO::fclose(f);
|
||||
return replay;
|
||||
}
|
||||
|
||||
// random guesses!
|
||||
const char *guess[] = {
|
||||
"/opt/renderdoc/qrenderdoc",
|
||||
"/opt/renderdoc/bin/qrenderdoc",
|
||||
"/usr/local/bin/qrenderdoc",
|
||||
"/usr/bin/qrenderdoc"
|
||||
};
|
||||
|
||||
for(size_t i=0; i < ARRAY_COUNT(guess); i++)
|
||||
{
|
||||
f = FileIO::fopen(guess[i], "r");
|
||||
if(f)
|
||||
{
|
||||
FileIO::fclose(f);
|
||||
return guess[i];
|
||||
}
|
||||
}
|
||||
|
||||
// out of ideas
|
||||
return "";
|
||||
}
|
||||
|
||||
void GetDefaultFiles(const char *logBaseName, string &capture_filename, string &logging_filename, string &target)
|
||||
{
|
||||
char path[2048] = {0};
|
||||
|
||||
@@ -47,7 +47,8 @@ namespace Process
|
||||
{
|
||||
void StartGlobalHook(const char *pathmatch, const char *logfile, const CaptureOptions *opts);
|
||||
uint32_t InjectIntoProcess(uint32_t pid, const char *logfile, const CaptureOptions *opts, bool waitForExit);
|
||||
uint32_t CreateAndInjectIntoProcess(const char *app, const char *workingDir, const char *cmdLine,
|
||||
uint32_t LaunchProcess(const char *app, const char *workingDir, const char *cmdLine);
|
||||
uint32_t LaunchAndInjectIntoProcess(const char *app, const char *workingDir, const char *cmdLine,
|
||||
const char *logfile, const CaptureOptions *opts, bool waitForExit);
|
||||
void *GetFunctionAddress(const char *module, const char *function);
|
||||
uint32_t GetCurrentPID();
|
||||
@@ -174,6 +175,7 @@ namespace FileIO
|
||||
{
|
||||
void GetDefaultFiles(const char *logBaseName, string &capture_filename, string &logging_filename, string &target);
|
||||
string GetAppFolderFilename(string filename);
|
||||
string GetReplayAppFilename();
|
||||
|
||||
void GetExecutableFilename(string &selfName);
|
||||
|
||||
|
||||
@@ -181,6 +181,74 @@ void InjectFunctionCall(HANDLE hProcess, uintptr_t renderdoc_remote, const char
|
||||
VirtualFreeEx(hProcess, remoteMem, dataLen, MEM_RELEASE);
|
||||
}
|
||||
|
||||
static PROCESS_INFORMATION RunProcess(const char *app, const char *workingDir, const char *cmdLine)
|
||||
{
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
SECURITY_ATTRIBUTES pSec;
|
||||
SECURITY_ATTRIBUTES tSec;
|
||||
|
||||
RDCEraseEl(pi);
|
||||
RDCEraseEl(si);
|
||||
RDCEraseEl(pSec);
|
||||
RDCEraseEl(tSec);
|
||||
|
||||
pSec.nLength = sizeof(pSec);
|
||||
tSec.nLength = sizeof(tSec);
|
||||
|
||||
wstring workdir = L"";
|
||||
|
||||
if(workingDir != NULL && workingDir[0] != 0)
|
||||
workdir = StringFormat::UTF82Wide(string(workingDir));
|
||||
else
|
||||
workdir = StringFormat::UTF82Wide(dirname(string(app)));
|
||||
|
||||
wchar_t *paramsAlloc = NULL;
|
||||
|
||||
wstring wapp = StringFormat::UTF82Wide(string(app));
|
||||
|
||||
// CreateProcessW can modify the params, need space.
|
||||
size_t len = wapp.length()+10;
|
||||
|
||||
wstring wcmd = L"";
|
||||
|
||||
if(cmdLine != NULL && cmdLine[0] != 0)
|
||||
{
|
||||
wcmd = StringFormat::UTF82Wide(string(cmdLine));
|
||||
len += wcmd.length();
|
||||
}
|
||||
|
||||
paramsAlloc = new wchar_t[len];
|
||||
|
||||
RDCEraseMem(paramsAlloc, len*sizeof(wchar_t));
|
||||
|
||||
wcscpy_s(paramsAlloc, len, L"\"");
|
||||
wcscat_s(paramsAlloc, len, wapp.c_str());
|
||||
wcscat_s(paramsAlloc, len, L"\"");
|
||||
|
||||
if(cmdLine != NULL && cmdLine[0] != 0)
|
||||
{
|
||||
wcscat_s(paramsAlloc, len, L" ");
|
||||
wcscat_s(paramsAlloc, len, wcmd.c_str());
|
||||
}
|
||||
|
||||
BOOL retValue = CreateProcessW(NULL, paramsAlloc,
|
||||
&pSec, &tSec, false, CREATE_SUSPENDED,
|
||||
NULL, workdir.c_str(), &si, &pi);
|
||||
|
||||
SAFE_DELETE_ARRAY(paramsAlloc);
|
||||
|
||||
if (!retValue)
|
||||
{
|
||||
RDCERR("Process %s could not be loaded.", app);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
RDCEraseEl(pi);
|
||||
}
|
||||
|
||||
return pi;
|
||||
}
|
||||
|
||||
uint32_t Process::InjectIntoProcess(uint32_t pid, const char *logfile, const CaptureOptions *opts, bool waitForExit)
|
||||
{
|
||||
CaptureOptions options;
|
||||
@@ -353,7 +421,21 @@ uint32_t Process::InjectIntoProcess(uint32_t pid, const char *logfile, const Cap
|
||||
return remoteident;
|
||||
}
|
||||
|
||||
uint32_t Process::CreateAndInjectIntoProcess(const char *app, const char *workingDir, const char *cmdLine,
|
||||
uint32_t Process::LaunchProcess(const char *app, const char *workingDir, const char *cmdLine)
|
||||
{
|
||||
PROCESS_INFORMATION pi = RunProcess(app, workingDir, cmdLine);
|
||||
|
||||
if(pi.dwProcessId == 0)
|
||||
return 0;
|
||||
|
||||
ResumeThread(pi.hThread);
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
return pi.dwProcessId;
|
||||
}
|
||||
|
||||
uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workingDir, const char *cmdLine,
|
||||
const char *logfile, const CaptureOptions *opts, bool waitForExit)
|
||||
{
|
||||
void *func = GetProcAddress(GetModuleHandleA("renderdoc.dll"), "RENDERDOC_SetLogFile");
|
||||
@@ -364,70 +446,14 @@ uint32_t Process::CreateAndInjectIntoProcess(const char *app, const char *workin
|
||||
return 0;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
SECURITY_ATTRIBUTES pSec;
|
||||
SECURITY_ATTRIBUTES tSec;
|
||||
PROCESS_INFORMATION pi = RunProcess(app, workingDir, cmdLine);
|
||||
|
||||
RDCEraseEl(pi);
|
||||
RDCEraseEl(si);
|
||||
RDCEraseEl(pSec);
|
||||
RDCEraseEl(tSec);
|
||||
|
||||
pSec.nLength = sizeof(pSec);
|
||||
tSec.nLength = sizeof(tSec);
|
||||
|
||||
wstring workdir = L"";
|
||||
|
||||
if(workingDir != NULL && workingDir[0] != 0)
|
||||
workdir = StringFormat::UTF82Wide(string(workingDir));
|
||||
else
|
||||
workdir = StringFormat::UTF82Wide(dirname(string(app)));
|
||||
|
||||
wchar_t *paramsAlloc = NULL;
|
||||
|
||||
wstring wapp = StringFormat::UTF82Wide(string(app));
|
||||
|
||||
// CreateProcessW can modify the params, need space.
|
||||
size_t len = wapp.length()+10;
|
||||
|
||||
wstring wcmd = L"";
|
||||
|
||||
if(cmdLine != NULL && cmdLine[0] != 0)
|
||||
{
|
||||
wcmd = StringFormat::UTF82Wide(string(cmdLine));
|
||||
len += wcmd.length();
|
||||
}
|
||||
|
||||
paramsAlloc = new wchar_t[len];
|
||||
|
||||
RDCEraseMem(paramsAlloc, len*sizeof(wchar_t));
|
||||
|
||||
wcscpy_s(paramsAlloc, len, L"\"");
|
||||
wcscat_s(paramsAlloc, len, wapp.c_str());
|
||||
wcscat_s(paramsAlloc, len, L"\"");
|
||||
|
||||
if(cmdLine != NULL && cmdLine[0] != 0)
|
||||
{
|
||||
wcscat_s(paramsAlloc, len, L" ");
|
||||
wcscat_s(paramsAlloc, len, wcmd.c_str());
|
||||
}
|
||||
|
||||
BOOL retValue = CreateProcessW(NULL, paramsAlloc,
|
||||
&pSec, &tSec, false, CREATE_SUSPENDED,
|
||||
NULL, workdir.c_str(), &si, &pi);
|
||||
|
||||
SAFE_DELETE_ARRAY(paramsAlloc);
|
||||
if(pi.dwProcessId == 0)
|
||||
return 0;
|
||||
|
||||
// don't need it
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
if (!retValue)
|
||||
{
|
||||
RDCERR("Process %s could not be loaded.", app);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ret = InjectIntoProcess(pi.dwProcessId, logfile, opts, false);
|
||||
|
||||
if(ret == 0)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "os/os_specific.h"
|
||||
#include "api/app/renderdoc_app.h"
|
||||
#include "serialise/string_utils.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
@@ -159,6 +160,56 @@ namespace FileIO
|
||||
selfName = StringFormat::Wide2UTF8(wstring(curFile));
|
||||
}
|
||||
|
||||
string GetReplayAppFilename()
|
||||
{
|
||||
HMODULE hModule = NULL;
|
||||
GetModuleHandleEx(
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCTSTR)&dllLocator,
|
||||
&hModule);
|
||||
wchar_t curFile[512] = {0};
|
||||
GetModuleFileNameW(hModule, curFile, 511);
|
||||
|
||||
string path = StringFormat::Wide2UTF8(wstring(curFile));
|
||||
path = dirname(path);
|
||||
string exe = path + "/renderdocui.exe";
|
||||
|
||||
FILE *f = FileIO::fopen(exe.c_str(), "rb");
|
||||
if(f)
|
||||
{
|
||||
FileIO::fclose(f);
|
||||
return exe;
|
||||
}
|
||||
|
||||
// if renderdocui.exe doesn't live in the same dir, we must be in x86/
|
||||
// so look one up the tree.
|
||||
exe = path + "/../renderdocui.exe";
|
||||
|
||||
f = FileIO::fopen(exe.c_str(), "rb");
|
||||
if(f)
|
||||
{
|
||||
FileIO::fclose(f);
|
||||
return exe;
|
||||
}
|
||||
|
||||
// if we didn't find the exe at all, we must not be in a standard
|
||||
// distributed renderdoc package. On windows we can check in the registry
|
||||
// to try and find the installed path.
|
||||
|
||||
DWORD type = 0;
|
||||
DWORD dataSize = sizeof(curFile);
|
||||
RDCEraseEl(curFile);
|
||||
RegGetValueW(HKEY_CLASSES_ROOT, L"RenderDoc.RDCCapture.1\\DefaultIcon", NULL, RRF_RT_ANY,
|
||||
&type, (void *)curFile, &dataSize);
|
||||
|
||||
if(type == REG_EXPAND_SZ || type == REG_SZ)
|
||||
{
|
||||
return StringFormat::Wide2UTF8(wstring(curFile));
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void GetDefaultFiles(const char *logBaseName, string &capture_filename, string &logging_filename, string &target)
|
||||
{
|
||||
wchar_t temp_filename[MAX_PATH];
|
||||
|
||||
@@ -236,7 +236,7 @@ extern "C" RENDERDOC_API
|
||||
uint32_t RENDERDOC_CC RENDERDOC_ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine,
|
||||
const char *logfile, const CaptureOptions *opts, bool32 waitForExit)
|
||||
{
|
||||
return Process::CreateAndInjectIntoProcess(app, workingDir, cmdLine, logfile, opts, waitForExit != 0);
|
||||
return Process::LaunchAndInjectIntoProcess(app, workingDir, cmdLine, logfile, opts, waitForExit != 0);
|
||||
}
|
||||
|
||||
extern "C" RENDERDOC_API
|
||||
@@ -369,6 +369,27 @@ void RENDERDOC_CC RENDERDOC_InitRemoteAccess(uint32_t *ident)
|
||||
if(ident) *ident = RenderDoc::Inst().GetRemoteAccessIdent();
|
||||
}
|
||||
|
||||
extern "C" RENDERDOC_API
|
||||
uint32_t RENDERDOC_CC RENDERDOC_IsRemoteAccessConnected()
|
||||
{
|
||||
return RenderDoc::Inst().IsRemoteAccessConnected();
|
||||
}
|
||||
|
||||
extern "C" RENDERDOC_API
|
||||
uint32_t RENDERDOC_CC RENDERDOC_LaunchReplayUI(uint32_t connectRemoteAccess, const char *cmdline)
|
||||
{
|
||||
string replayapp = FileIO::GetReplayAppFilename();
|
||||
|
||||
if(replayapp.empty())
|
||||
return 0;
|
||||
|
||||
string cmd = cmdline ? cmdline : "";
|
||||
if(connectRemoteAccess)
|
||||
cmd += StringFormat::Fmt(" --remoteaccess localhost:%u", RenderDoc::Inst().GetRemoteAccessIdent());
|
||||
|
||||
return Process::LaunchProcess(replayapp.c_str(), "", cmd.c_str());
|
||||
}
|
||||
|
||||
extern "C" RENDERDOC_API
|
||||
uint32_t RENDERDOC_CC RENDERDOC_EnumerateRemoteConnections(const char *host, uint32_t *idents)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user