diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 4400506cd..58d396563 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -487,7 +487,7 @@ struct IRemoteServer virtual rdctype::array ListFolder(const char *path) = 0; virtual uint32_t ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, - const CaptureOptions *opts) = 0; + void *env, const CaptureOptions *opts) = 0; virtual void TakeOwnershipCapture(const char *filename) = 0; virtual rdctype::str CopyCaptureToRemote(const char *filename, float *progress) = 0; @@ -529,7 +529,7 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_ListFolder( extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RemoteServer_ExecuteAndInject(RemoteServer *remote, const char *app, const char *workingDir, - const char *cmdLine, const CaptureOptions *opts); + const char *cmdLine, void *env, const CaptureOptions *opts); extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_TakeOwnershipCapture(RemoteServer *remote, const char *filename); @@ -628,7 +628,7 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartGlobalHook(const char const char *logfile, const CaptureOptions *opts); extern "C" RENDERDOC_API uint32_t RENDERDOC_CC -RENDERDOC_ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, +RENDERDOC_ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, void *env, const char *logfile, const CaptureOptions *opts, bool32 waitForExit); extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_InjectIntoProcess( uint32_t pid, const char *logfile, const CaptureOptions *opts, bool32 waitForExit); @@ -647,3 +647,11 @@ extern "C" RENDERDOC_API const char *RENDERDOC_CC RENDERDOC_GetCommitHash(); extern "C" RENDERDOC_API const char *RENDERDOC_CC RENDERDOC_GetConfigSetting(const char *name); extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetConfigSetting(const char *name, const char *value); + +extern "C" RENDERDOC_API void *RENDERDOC_CC RENDERDOC_MakeEnvironmentModificationList(int numElems); + +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetEnvironmentModification( + void *mem, int idx, const char *variable, const char *value, EnvironmentModificationType type, + EnvironmentSeparator separator); + +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_FreeEnvironmentModificationList(void *mem); diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 29c0cd062..706879729 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -523,3 +523,18 @@ enum TargetControlMessageType eTargetControlMsg_RegisterAPI, eTargetControlMsg_NewChild, }; + +enum EnvironmentModificationType +{ + eEnvMod_Set, + eEnvMod_Append, + eEnvMod_Prepend, +}; + +enum EnvironmentSeparator +{ + eEnvSep_Platform, + eEnvSep_SemiColon, + eEnvSep_Colon, + eEnvSep_None, +}; diff --git a/renderdoc/core/remote_server.cpp b/renderdoc/core/remote_server.cpp index 5bff644cb..29d2cc4ff 100644 --- a/renderdoc/core/remote_server.cpp +++ b/renderdoc/core/remote_server.cpp @@ -50,6 +50,22 @@ void Serialiser::Serialise(const char *name, DirectoryFile &el) Serialise("flags", el.flags); } +template <> +string ToStrHelper::Get(const Process::ModificationType &el) +{ + return "<...>"; +} + +template <> +void Serialiser::Serialise(const char *name, Process::EnvironmentModification &el) +{ + ScopedContext scope(this, name, "Process::EnvironmentModification", 0, true); + + Serialise("type", el.type); + Serialise("name", el.name); + Serialise("value", el.value); +} + enum RemoteServerPacket { eRemoteServer_Noop, @@ -372,18 +388,27 @@ static void ActiveRemoteClientThread(void *data) recvser->Serialise("cmdLine", cmdLine); recvser->Serialise("opts", opts); + uint64_t envListSize = 0; + Process::EnvironmentModification *env = NULL; + recvser->Serialise("envListSize", envListSize); + + if(envListSize > 0) + recvser->SerialiseComplexArray("env", env, envListSize); + uint32_t ident = eReplayCreate_NetworkIOFailed; if(threadData->allowExecution) { ident = Process::LaunchAndInjectIntoProcess(app.c_str(), workingDir.c_str(), - cmdLine.c_str(), "", &opts, false); + cmdLine.c_str(), env, "", &opts, false); } else { RDCWARN("Requested to execute program - disallowing based on configuration"); } + SAFE_DELETE_ARRAY(env); + sendType = eRemoteServer_ExecuteAndInject; sendSer.Serialise("ident", ident); } @@ -769,7 +794,7 @@ public: return ret; } - uint32_t ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, + uint32_t ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, void *env, const CaptureOptions *opts) { CaptureOptions capopts = opts ? *opts : CaptureOptions(); @@ -778,11 +803,35 @@ public: string workstr = workingDir && workingDir[0] ? workingDir : ""; string cmdstr = cmdLine && cmdLine[0] ? cmdLine : ""; + Process::EnvironmentModification *envList = (Process::EnvironmentModification *)env; + Serialiser sendData("", Serialiser::WRITING, false); sendData.Serialise("app", appstr); sendData.Serialise("workingDir", workstr); sendData.Serialise("cmdLine", cmdstr); sendData.Serialise("opts", capopts); + + uint64_t envListSize = 0; + if(envList) + { + Process::EnvironmentModification *it = envList; + for(;;) + { + if(it->name == "") + break; + envListSize++; + it++; + } + + // include terminator + envListSize++; + } + + sendData.Serialise("envListSize", envListSize); + + if(envListSize > 0) + sendData.SerialiseComplexArray("env", envList, envListSize); + Send(eRemoteServer_ExecuteAndInject, sendData); RemoteServerPacket type = eRemoteServer_ExecuteAndInject; @@ -1019,9 +1068,9 @@ RemoteServer_RemoteSupportedReplays(RemoteServer *remote, rdctype::arrayExecuteAndInject(app, workingDir, cmdLine, opts); + return remote->ExecuteAndInject(app, workingDir, cmdLine, env, opts); } extern "C" RENDERDOC_API void RENDERDOC_CC RemoteServer_TakeOwnershipCapture(RemoteServer *remote, diff --git a/renderdoc/os/os_specific.h b/renderdoc/os/os_specific.h index 953c584a2..4e26f69f6 100644 --- a/renderdoc/os/os_specific.h +++ b/renderdoc/os/os_specific.h @@ -52,17 +52,17 @@ enum ModificationType eEnvModification_Replace = 0, // prepend/append options will replace if there is no existing variable - eEnvModification_Append, // append with no separators - eEnvModification_AppendColon, // append, separated by colons - eEnvModification_AppendSemiColon, // append, separated by semi-colons eEnvModification_AppendPlatform, // append, separated by colons for linux & semi-colons for // windows + eEnvModification_AppendSemiColon, // append, separated by semi-colons + eEnvModification_AppendColon, // append, separated by colons + eEnvModification_Append, // append with no separators - eEnvModification_Prepend, // prepend with no separators - eEnvModification_PrependColon, // prepend, separated by colons - eEnvModification_PrependSemiColon, // prepend, separated by semi-colons eEnvModification_PrependPlatform, // prepend, separated by colons for linux & semi-colons for // windows + eEnvModification_PrependSemiColon, // prepend, separated by semi-colons + eEnvModification_PrependColon, // prepend, separated by colons + eEnvModification_Prepend, // prepend with no separators }; struct EnvironmentModification { @@ -84,8 +84,8 @@ uint32_t InjectIntoProcess(uint32_t pid, const char *logfile, const CaptureOptio bool waitForExit); 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); + EnvironmentModification *env, const char *logfile, + const CaptureOptions *opts, bool waitForExit); void *LoadModule(const char *module); void *GetFunctionAddress(void *module, const char *function); uint32_t GetCurrentPID(); diff --git a/renderdoc/os/posix/posix_process.cpp b/renderdoc/os/posix/posix_process.cpp index 3fa9468c7..6ea776708 100644 --- a/renderdoc/os/posix/posix_process.cpp +++ b/renderdoc/os/posix/posix_process.cpp @@ -361,8 +361,9 @@ uint32_t Process::LaunchProcess(const char *app, const char *workingDir, const c } uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workingDir, - const char *cmdLine, const char *logfile, - const CaptureOptions *opts, bool waitForExit) + const char *cmdLine, EnvironmentModification *envList, + const char *logfile, const CaptureOptions *opts, + bool waitForExit) { if(app == NULL || app[0] == 0) { @@ -375,6 +376,22 @@ uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workin map env = EnvStringToEnvMap((const char **)currentEnvironment); vector &modifications = GetEnvModifications(); + if(envList) + { + for(;;) + { + EnvironmentModification e = *envList; + e.name = trim(e.name); + + if(e.name == "") + break; + + modifications.push_back(e); + + envList++; + } + } + if(logfile == NULL) logfile = ""; diff --git a/renderdoc/os/win32/win32_process.cpp b/renderdoc/os/win32/win32_process.cpp index 92a69d505..80cfc95b1 100644 --- a/renderdoc/os/win32/win32_process.cpp +++ b/renderdoc/os/win32/win32_process.cpp @@ -171,6 +171,35 @@ extern "C" __declspec(dllexport) void __cdecl RENDERDOC_SetLogFile(const char *l if(log) RenderDoc::Inst().SetLogFile(log); } + +static Process::EnvironmentModification tempEnvMod; + +extern "C" __declspec(dllexport) void __cdecl RENDERDOC_EnvModName(const char *name) +{ + if(name) + tempEnvMod.name = name; +} + +extern "C" __declspec(dllexport) void __cdecl RENDERDOC_EnvModValue(const char *value) +{ + if(value) + tempEnvMod.value = value; +} + +extern "C" __declspec(dllexport) void __cdecl RENDERDOC_EnvMod(Process::ModificationType *type) +{ + if(type) + { + tempEnvMod.type = *type; + Process::RegisterEnvironmentModification(tempEnvMod); + } +} + +extern "C" __declspec(dllexport) void __cdecl RENDERDOC_ApplyEnvMods(void *ignored) +{ + Process::ApplyEnvironmentModification(); +} + void InjectDLL(HANDLE hProcess, wstring libName) { wchar_t dllPath[MAX_PATH + 1] = {0}; @@ -591,8 +620,9 @@ uint32_t Process::LaunchProcess(const char *app, const char *workingDir, const c } uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workingDir, - const char *cmdLine, const char *logfile, - const CaptureOptions *opts, bool waitForExit) + const char *cmdLine, EnvironmentModification *env, + const char *logfile, const CaptureOptions *opts, + bool waitForExit) { void *func = GetProcAddress(GetModuleHandleA("renderdoc.dll"), "RENDERDOC_SetLogFile"); @@ -607,18 +637,53 @@ uint32_t Process::LaunchAndInjectIntoProcess(const char *app, const char *workin if(pi.dwProcessId == 0) return 0; - // don't need it - CloseHandle(pi.hProcess); - uint32_t ret = InjectIntoProcess(pi.dwProcessId, logfile, opts, false); if(ret == 0) { ResumeThread(pi.hThread); CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); return 0; } + if(env) + { + uintptr_t loc = FindRemoteDLL(pi.dwProcessId, L"renderdoc.dll"); + + if(loc == 0) + { + RDCERR("Can't locate renderdoc.dll in remote PID %d", pi.dwProcessId); + } + else + { + for(;;) + { + string name = trim(env->name); + string value = env->value; + ModificationType type = env->type; + + if(name == "") + break; + + InjectFunctionCall(pi.hProcess, loc, "RENDERDOC_EnvModName", (void *)name.c_str(), + name.size() + 1); + InjectFunctionCall(pi.hProcess, loc, "RENDERDOC_EnvModValue", (void *)value.c_str(), + value.size() + 1); + InjectFunctionCall(pi.hProcess, loc, "RENDERDOC_EnvMod", &type, sizeof(type)); + + env++; + } + + // parameter is unused + InjectFunctionCall(pi.hProcess, loc, "RENDERDOC_ApplyEnvMods", env, + sizeof(EnvironmentModification)); + } + } + + // done with it + CloseHandle(pi.hProcess); + ResumeThread(pi.hThread); if(waitForExit) diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index ecb41a8b3..e11551c25 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -252,6 +252,33 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetConfigSetting(const char RenderDoc::Inst().SetConfigSetting(name, value); } +extern "C" RENDERDOC_API void *RENDERDOC_CC RENDERDOC_MakeEnvironmentModificationList(int numElems) +{ + return new Process::EnvironmentModification[numElems + 1]; // last one is a terminator +} + +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetEnvironmentModification( + void *mem, int idx, const char *variable, const char *value, EnvironmentModificationType type, + EnvironmentSeparator separator) +{ + Process::EnvironmentModification *mods = (Process::EnvironmentModification *)mem; + + Process::ModificationType modType = Process::eEnvModification_Replace; + + if(type == eEnvMod_Append) + modType = Process::ModificationType(Process::eEnvModification_AppendPlatform + (int)separator); + if(type == eEnvMod_Prepend) + modType = Process::ModificationType(Process::eEnvModification_PrependPlatform + (int)separator); + + mods[idx] = Process::EnvironmentModification(modType, variable, value); +} + +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_FreeEnvironmentModificationList(void *mem) +{ + Process::EnvironmentModification *mods = (Process::EnvironmentModification *)mem; + delete[] mods; +} + extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_LogText(const char *text) { RDCLOG("%s", text); @@ -336,10 +363,11 @@ RENDERDOC_CreateReplayRenderer(const char *logfile, float *progress, ReplayRende } extern "C" RENDERDOC_API uint32_t RENDERDOC_CC -RENDERDOC_ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, +RENDERDOC_ExecuteAndInject(const char *app, const char *workingDir, const char *cmdLine, void *env, const char *logfile, const CaptureOptions *opts, bool32 waitForExit) { - return Process::LaunchAndInjectIntoProcess(app, workingDir, cmdLine, logfile, opts, + return Process::LaunchAndInjectIntoProcess(app, workingDir, cmdLine, + (Process::EnvironmentModification *)env, logfile, opts, waitForExit != 0); } diff --git a/renderdoccmd/renderdoccmd.cpp b/renderdoccmd/renderdoccmd.cpp index 7e016cffe..1e240c94e 100644 --- a/renderdoccmd/renderdoccmd.cpp +++ b/renderdoccmd/renderdoccmd.cpp @@ -303,7 +303,7 @@ struct CaptureCommand : public Command uint32_t ident = RENDERDOC_ExecuteAndInject( executable.c_str(), workingDir.empty() ? "" : workingDir.c_str(), - cmdLine.empty() ? "" : cmdLine.c_str(), logFile.empty() ? "" : logFile.c_str(), &opts, + cmdLine.empty() ? "" : cmdLine.c_str(), NULL, logFile.empty() ? "" : logFile.c_str(), &opts, parser.exist("wait-for-exit")); if(ident == 0) diff --git a/renderdocui/Code/RenderManager.cs b/renderdocui/Code/RenderManager.cs index 17f2eec99..66fbeaf69 100644 --- a/renderdocui/Code/RenderManager.cs +++ b/renderdocui/Code/RenderManager.cs @@ -98,15 +98,15 @@ namespace renderdocui.Code while (m_Thread.IsAlive && !Running) ; } - public UInt32 ExecuteAndInject(string app, string workingDir, string cmdLine, string logfile, CaptureOptions opts) + public UInt32 ExecuteAndInject(string app, string workingDir, string cmdLine, EnvironmentModification[] env, string logfile, CaptureOptions opts) { if (m_Remote == null) { - return StaticExports.ExecuteAndInject(app, workingDir, cmdLine, logfile, opts); + return StaticExports.ExecuteAndInject(app, workingDir, cmdLine, env, logfile, opts); } else { - return m_Remote.ExecuteAndInject(app, workingDir, cmdLine, opts); + return m_Remote.ExecuteAndInject(app, workingDir, cmdLine, env, opts); } } diff --git a/renderdocui/Interop/Enums.cs b/renderdocui/Interop/Enums.cs index 455f8734a..75ac00e9d 100644 --- a/renderdocui/Interop/Enums.cs +++ b/renderdocui/Interop/Enums.cs @@ -536,6 +536,21 @@ namespace renderdoc NewChild, }; + public enum EnvironmentModificationType + { + Set, + Append, + Prepend, + }; + + public enum EnvironmentSeparator + { + Platform, + SemiColon, + Colon, + None, + }; + public static class EnumString { [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] @@ -865,5 +880,19 @@ namespace renderdoc return "Unknown"; } + + public static string Str(this EnvironmentSeparator sep) + { + switch (sep) + { + case EnvironmentSeparator.Platform: return "Platform style"; + case EnvironmentSeparator.SemiColon: return "Semi-colon (;)"; + case EnvironmentSeparator.Colon: return "Colon (:)"; + case EnvironmentSeparator.None: return "No Separator"; + default: break; + } + + return "Unknown"; + } } } diff --git a/renderdocui/Interop/ReplayRenderer.cs b/renderdocui/Interop/ReplayRenderer.cs index 727683cf1..bafc7714a 100644 --- a/renderdocui/Interop/ReplayRenderer.cs +++ b/renderdocui/Interop/ReplayRenderer.cs @@ -29,6 +29,45 @@ using System.Collections.Generic; namespace renderdoc { + // this isn't an Interop struct, since it needs to be passed C# -> C++ and + // it's not POD which we don't support. It's here just as a utility container + public class EnvironmentModification + { + public string variable; + public string value; + + public EnvironmentModificationType type; + public EnvironmentSeparator separator; + + public string GetTypeString() + { + string ret; + + if (type == EnvironmentModificationType.Append) + ret = String.Format("Append, {0}", separator.Str()); + else if (type == EnvironmentModificationType.Prepend) + ret = String.Format("Prepend, {0}", separator.Str()); + else + ret = "Set"; + + return ret; + } + + public string GetDescription() + { + string ret; + + if (type == EnvironmentModificationType.Append) + ret = String.Format("Append {0} with {1} using {2}", variable, value, separator.Str()); + else if (type == EnvironmentModificationType.Prepend) + ret = String.Format("Prepend {0} with {1} using {2}", variable, value, separator.Str()); + else + ret = String.Format("Set {0} to {1}", variable, value); + + return ret; + } + } + [StructLayout(LayoutKind.Sequential)] public class TargetControlMessage { @@ -864,7 +903,7 @@ namespace renderdoc private static extern void RemoteServer_ListFolder(IntPtr real, IntPtr path, IntPtr outlist); [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - private static extern UInt32 RemoteServer_ExecuteAndInject(IntPtr real, IntPtr app, IntPtr workingDir, IntPtr cmdLine, CaptureOptions opts); + private static extern UInt32 RemoteServer_ExecuteAndInject(IntPtr real, IntPtr app, IntPtr workingDir, IntPtr cmdLine, IntPtr env, CaptureOptions opts); [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern void RemoteServer_TakeOwnershipCapture(IntPtr real, IntPtr filename); @@ -879,6 +918,17 @@ namespace renderdoc [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern void RemoteServer_CloseCapture(IntPtr real, IntPtr rendPtr); + // static exports for lists of environment modifications + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr RENDERDOC_MakeEnvironmentModificationList(int numElems); + + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern void RENDERDOC_SetEnvironmentModification(IntPtr mem, int idx, IntPtr variable, IntPtr value, + EnvironmentModificationType type, EnvironmentSeparator separator); + + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern void RENDERDOC_FreeEnvironmentModificationList(IntPtr mem); + private IntPtr m_Real = IntPtr.Zero; public RemoteServer(IntPtr real) { m_Real = real; } @@ -966,13 +1016,28 @@ namespace renderdoc return ret; } - public UInt32 ExecuteAndInject(string app, string workingDir, string cmdLine, CaptureOptions opts) + public UInt32 ExecuteAndInject(string app, string workingDir, string cmdLine, EnvironmentModification[] env, CaptureOptions opts) { IntPtr app_mem = CustomMarshal.MakeUTF8String(app); IntPtr workingDir_mem = CustomMarshal.MakeUTF8String(workingDir); IntPtr cmdLine_mem = CustomMarshal.MakeUTF8String(cmdLine); - UInt32 ret = RemoteServer_ExecuteAndInject(m_Real, app_mem, workingDir_mem, cmdLine_mem, opts); + IntPtr env_mem = RENDERDOC_MakeEnvironmentModificationList(env.Length); + + for (int i = 0; i < env.Length; i++) + { + IntPtr var_mem = CustomMarshal.MakeUTF8String(env[i].variable); + IntPtr val_mem = CustomMarshal.MakeUTF8String(env[i].value); + + RENDERDOC_SetEnvironmentModification(env_mem, i, var_mem, val_mem, env[i].type, env[i].separator); + + CustomMarshal.Free(var_mem); + CustomMarshal.Free(val_mem); + } + + UInt32 ret = RemoteServer_ExecuteAndInject(m_Real, app_mem, workingDir_mem, cmdLine_mem, env_mem, opts); + + RENDERDOC_FreeEnvironmentModificationList(env_mem); CustomMarshal.Free(app_mem); CustomMarshal.Free(workingDir_mem); diff --git a/renderdocui/Interop/StaticExports.cs b/renderdocui/Interop/StaticExports.cs index dd91c2777..5052c979a 100644 --- a/renderdocui/Interop/StaticExports.cs +++ b/renderdocui/Interop/StaticExports.cs @@ -58,7 +58,7 @@ namespace renderdoc private static extern void RENDERDOC_StartGlobalHook(IntPtr pathmatch, IntPtr logfile, CaptureOptions opts); [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] - private static extern UInt32 RENDERDOC_ExecuteAndInject(IntPtr app, IntPtr workingDir, IntPtr cmdLine, + private static extern UInt32 RENDERDOC_ExecuteAndInject(IntPtr app, IntPtr workingDir, IntPtr cmdLine, IntPtr env, IntPtr logfile, CaptureOptions opts, bool waitForExit); [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] @@ -100,6 +100,16 @@ namespace renderdoc [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] private static extern bool RENDERDOC_GetThumbnail(IntPtr filename, byte[] outmem, ref UInt32 len); + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr RENDERDOC_MakeEnvironmentModificationList(int numElems); + + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern void RENDERDOC_SetEnvironmentModification(IntPtr mem, int idx, IntPtr variable, IntPtr value, + EnvironmentModificationType type, EnvironmentSeparator separator); + + [DllImport("renderdoc.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] + private static extern void RENDERDOC_FreeEnvironmentModificationList(IntPtr mem); + public static bool SupportLocalReplay(string logfile, out string driverName) { IntPtr mem = CustomMarshal.Alloc(typeof(templated_array)); @@ -145,14 +155,29 @@ namespace renderdoc CustomMarshal.Free(pathmatch_mem); } - public static UInt32 ExecuteAndInject(string app, string workingDir, string cmdLine, string logfile, CaptureOptions opts) + public static UInt32 ExecuteAndInject(string app, string workingDir, string cmdLine, EnvironmentModification[] env, string logfile, CaptureOptions opts) { IntPtr app_mem = CustomMarshal.MakeUTF8String(app); IntPtr workingDir_mem = CustomMarshal.MakeUTF8String(workingDir); IntPtr cmdLine_mem = CustomMarshal.MakeUTF8String(cmdLine); IntPtr logfile_mem = CustomMarshal.MakeUTF8String(logfile); - UInt32 ret = RENDERDOC_ExecuteAndInject(app_mem, workingDir_mem, cmdLine_mem, logfile_mem, opts, false); + IntPtr env_mem = RENDERDOC_MakeEnvironmentModificationList(env.Length); + + for(int i=0; i < env.Length; i++) + { + IntPtr var_mem = CustomMarshal.MakeUTF8String(env[i].variable); + IntPtr val_mem = CustomMarshal.MakeUTF8String(env[i].value); + + RENDERDOC_SetEnvironmentModification(env_mem, i, var_mem, val_mem, env[i].type, env[i].separator); + + CustomMarshal.Free(var_mem); + CustomMarshal.Free(val_mem); + } + + UInt32 ret = RENDERDOC_ExecuteAndInject(app_mem, workingDir_mem, cmdLine_mem, env_mem, logfile_mem, opts, false); + + RENDERDOC_FreeEnvironmentModificationList(env_mem); CustomMarshal.Free(app_mem); CustomMarshal.Free(workingDir_mem); diff --git a/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs b/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs index d8a141610..4def92b11 100644 --- a/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs +++ b/renderdocui/Windows/Dialogs/CaptureDialog.Designer.cs @@ -34,6 +34,8 @@ System.Windows.Forms.ColumnHeader pid; System.Windows.Forms.ColumnHeader name; System.Windows.Forms.Label label5; + System.Windows.Forms.Label label3; + System.Windows.Forms.Label label6; this.globalLabel = new System.Windows.Forms.Label(); this.actionsGroup = new System.Windows.Forms.GroupBox(); this.actionsFlow = new System.Windows.Forms.FlowLayoutPanel(); @@ -68,7 +70,6 @@ this.saveDialog = new System.Windows.Forms.SaveFileDialog(); this.loadDialog = new System.Windows.Forms.OpenFileDialog(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); - this.label3 = new System.Windows.Forms.Label(); this.cmdline = new System.Windows.Forms.TextBox(); this.workDirBrowse = new System.Windows.Forms.Button(); this.workDirPath = new System.Windows.Forms.TextBox(); @@ -76,6 +77,8 @@ this.exePath = new System.Windows.Forms.TextBox(); this.pidRefresh = new System.Windows.Forms.Button(); this.pidList = new System.Windows.Forms.ListView(); + this.environmentDisplay = new System.Windows.Forms.TextBox(); + this.setEnv = new System.Windows.Forms.Button(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); this.programGroup = new System.Windows.Forms.GroupBox(); this.processGroup = new System.Windows.Forms.GroupBox(); @@ -85,6 +88,8 @@ pid = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); label5 = new System.Windows.Forms.Label(); + label3 = new System.Windows.Forms.Label(); + label6 = new System.Windows.Forms.Label(); this.actionsGroup.SuspendLayout(); this.actionsFlow.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.queuedCapFrame)).BeginInit(); @@ -135,10 +140,30 @@ | System.Windows.Forms.AnchorStyles.Right))); label5.Location = new System.Drawing.Point(9, 16); label5.Name = "label5"; - label5.Size = new System.Drawing.Size(475, 23); + label5.Size = new System.Drawing.Size(478, 23); label5.TabIndex = 3; label5.Text = "NOTE: Injecting only works when the process has not used the target API"; // + // label3 + // + label3.AutoSize = true; + label3.Location = new System.Drawing.Point(6, 74); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(126, 13); + label3.TabIndex = 7; + label3.Text = "Command-line Arguments"; + this.toolTip.SetToolTip(label3, "The command-line that will be passed to the executable on launch"); + // + // label6 + // + label6.AutoSize = true; + label6.Location = new System.Drawing.Point(6, 103); + label6.Name = "label6"; + label6.Size = new System.Drawing.Size(112, 13); + label6.TabIndex = 8; + label6.Text = "Environment Variables"; + this.toolTip.SetToolTip(label6, "The command-line that will be passed to the executable on launch"); + // // globalLabel // this.globalLabel.AutoSize = true; @@ -158,10 +183,10 @@ this.actionsGroup.AutoSize = true; this.actionsGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.actionsGroup.Controls.Add(this.actionsFlow); - this.actionsGroup.Location = new System.Drawing.Point(10, 558); + this.actionsGroup.Location = new System.Drawing.Point(10, 584); this.actionsGroup.Margin = new System.Windows.Forms.Padding(10); this.actionsGroup.Name = "actionsGroup"; - this.actionsGroup.Size = new System.Drawing.Size(197, 65); + this.actionsGroup.Size = new System.Drawing.Size(200, 65); this.actionsGroup.TabIndex = 11; this.actionsGroup.TabStop = false; this.actionsGroup.Text = "Actions"; @@ -218,10 +243,10 @@ this.globalGroup.AutoSize = true; this.globalGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.globalGroup.Controls.Add(this.globalFlow); - this.globalGroup.Location = new System.Drawing.Point(10, 643); + this.globalGroup.Location = new System.Drawing.Point(10, 669); this.globalGroup.Margin = new System.Windows.Forms.Padding(10); this.globalGroup.Name = "globalGroup"; - this.globalGroup.Size = new System.Drawing.Size(197, 80); + this.globalGroup.Size = new System.Drawing.Size(200, 80); this.globalGroup.TabIndex = 12; this.globalGroup.TabStop = false; this.globalGroup.Text = "Global Process Hook"; @@ -257,10 +282,10 @@ this.capOptsGroup.AutoSize = true; this.capOptsGroup.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; this.capOptsGroup.Controls.Add(this.capOptsFlow); - this.capOptsGroup.Location = new System.Drawing.Point(10, 425); + this.capOptsGroup.Location = new System.Drawing.Point(10, 451); this.capOptsGroup.Margin = new System.Windows.Forms.Padding(10); this.capOptsGroup.Name = "capOptsGroup"; - this.capOptsGroup.Size = new System.Drawing.Size(197, 113); + this.capOptsGroup.Size = new System.Drawing.Size(200, 113); this.capOptsGroup.TabIndex = 4; this.capOptsGroup.TabStop = false; this.capOptsGroup.Text = "Capture Options"; @@ -369,8 +394,8 @@ this.APIValidation.Size = new System.Drawing.Size(130, 20); this.APIValidation.TabIndex = 12; this.APIValidation.Text = "Enable API validation"; - this.toolTip.SetToolTip(this.APIValidation, "Initialise the graphics API with built-in validation enabled - allows capturing and reading of errors and war" + - "nings generated by the API's own debugging system"); + this.toolTip.SetToolTip(this.APIValidation, "Initialise the graphics API with built-in validation enabled - allows capturing a" + + "nd reading of errors and warnings generated by the API\'s own debugging system"); this.APIValidation.UseVisualStyleBackColor = true; // // HookIntoChildren @@ -443,9 +468,9 @@ this.panel2.Controls.Add(this.save); this.panel2.Controls.Add(this.close); this.panel2.Controls.Add(this.capture); - this.panel2.Location = new System.Drawing.Point(3, 736); + this.panel2.Location = new System.Drawing.Point(3, 762); this.panel2.Name = "panel2"; - this.panel2.Size = new System.Drawing.Size(211, 26); + this.panel2.Size = new System.Drawing.Size(214, 26); this.panel2.TabIndex = 8; // // load @@ -475,7 +500,7 @@ // close // this.close.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.close.Location = new System.Drawing.Point(164, 3); + this.close.Location = new System.Drawing.Point(167, 3); this.close.Margin = new System.Windows.Forms.Padding(0); this.close.Name = "close"; this.close.Size = new System.Drawing.Size(41, 23); @@ -487,7 +512,7 @@ // capture // this.capture.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.capture.Location = new System.Drawing.Point(107, 3); + this.capture.Location = new System.Drawing.Point(110, 3); this.capture.Margin = new System.Windows.Forms.Padding(0); this.capture.Name = "capture"; this.capture.Size = new System.Drawing.Size(52, 23); @@ -522,30 +547,20 @@ this.loadDialog.FileName = "Settings.cap"; this.loadDialog.Filter = "Capture settings|*.cap"; // - // label3 - // - this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(6, 74); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(126, 13); - this.label3.TabIndex = 7; - this.label3.Text = "Command-line Arguments"; - this.toolTip.SetToolTip(this.label3, "The command-line that will be passed to the executable on launch"); - // // cmdline // this.cmdline.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.cmdline.Location = new System.Drawing.Point(137, 71); this.cmdline.Name = "cmdline"; - this.cmdline.Size = new System.Drawing.Size(54, 20); + this.cmdline.Size = new System.Drawing.Size(57, 20); this.cmdline.TabIndex = 4; this.toolTip.SetToolTip(this.cmdline, "The command-line that will be passed to the executable on launch"); // // workDirBrowse // this.workDirBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.workDirBrowse.Location = new System.Drawing.Point(167, 45); + this.workDirBrowse.Location = new System.Drawing.Point(170, 45); this.workDirBrowse.Name = "workDirBrowse"; this.workDirBrowse.Size = new System.Drawing.Size(24, 20); this.workDirBrowse.TabIndex = 3; @@ -560,7 +575,7 @@ | System.Windows.Forms.AnchorStyles.Right))); this.workDirPath.Location = new System.Drawing.Point(137, 45); this.workDirPath.Name = "workDirPath"; - this.workDirPath.Size = new System.Drawing.Size(24, 20); + this.workDirPath.Size = new System.Drawing.Size(27, 20); this.workDirPath.TabIndex = 2; this.toolTip.SetToolTip(this.workDirPath, "The working directory the executable will be launched in"); this.workDirPath.TextChanged += new System.EventHandler(this.workDirPath_TextChanged); @@ -570,7 +585,7 @@ // exeBrowse // this.exeBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.exeBrowse.Location = new System.Drawing.Point(167, 18); + this.exeBrowse.Location = new System.Drawing.Point(170, 18); this.exeBrowse.Name = "exeBrowse"; this.exeBrowse.Size = new System.Drawing.Size(24, 20); this.exeBrowse.TabIndex = 1; @@ -586,7 +601,7 @@ | System.Windows.Forms.AnchorStyles.Right))); this.exePath.Location = new System.Drawing.Point(137, 19); this.exePath.Name = "exePath"; - this.exePath.Size = new System.Drawing.Size(24, 20); + this.exePath.Size = new System.Drawing.Size(27, 20); this.exePath.TabIndex = 0; this.toolTip.SetToolTip(this.exePath, "The executable file to launch"); this.exePath.TextChanged += new System.EventHandler(this.exePath_TextChanged); @@ -596,7 +611,7 @@ // pidRefresh // this.pidRefresh.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.pidRefresh.Location = new System.Drawing.Point(412, 180); + this.pidRefresh.Location = new System.Drawing.Point(415, 180); this.pidRefresh.Name = "pidRefresh"; this.pidRefresh.Size = new System.Drawing.Size(75, 23); this.pidRefresh.TabIndex = 6; @@ -619,12 +634,37 @@ this.pidList.Location = new System.Drawing.Point(6, 42); this.pidList.MultiSelect = false; this.pidList.Name = "pidList"; - this.pidList.Size = new System.Drawing.Size(185, 129); + this.pidList.Size = new System.Drawing.Size(188, 129); this.pidList.TabIndex = 5; this.toolTip.SetToolTip(this.pidList, "Select the process to inject into - must not yet have utilised the target API"); this.pidList.UseCompatibleStateImageBehavior = false; this.pidList.View = System.Windows.Forms.View.Details; // + // environmentDisplay + // + this.environmentDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.environmentDisplay.BackColor = System.Drawing.SystemColors.Control; + this.environmentDisplay.Cursor = System.Windows.Forms.Cursors.IBeam; + this.environmentDisplay.Location = new System.Drawing.Point(137, 97); + this.environmentDisplay.Name = "environmentDisplay"; + this.environmentDisplay.ReadOnly = true; + this.environmentDisplay.Size = new System.Drawing.Size(27, 20); + this.environmentDisplay.TabIndex = 9; + this.toolTip.SetToolTip(this.environmentDisplay, "The working directory the executable will be launched in"); + // + // setEnv + // + this.setEnv.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.setEnv.Location = new System.Drawing.Point(170, 96); + this.setEnv.Name = "setEnv"; + this.setEnv.Size = new System.Drawing.Size(24, 20); + this.setEnv.TabIndex = 10; + this.setEnv.Text = "..."; + this.toolTip.SetToolTip(this.setEnv, "Browse for a working directory"); + this.setEnv.UseVisualStyleBackColor = true; + this.setEnv.Click += new System.EventHandler(this.setEnv_Click); + // // tableLayoutPanel2 // this.tableLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) @@ -650,14 +690,17 @@ this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel2.Size = new System.Drawing.Size(217, 765); + this.tableLayoutPanel2.Size = new System.Drawing.Size(220, 791); this.tableLayoutPanel2.TabIndex = 8; // // programGroup // this.programGroup.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.programGroup.Controls.Add(this.label3); + this.programGroup.Controls.Add(this.setEnv); + this.programGroup.Controls.Add(this.environmentDisplay); + this.programGroup.Controls.Add(label6); + this.programGroup.Controls.Add(label3); this.programGroup.Controls.Add(label2); this.programGroup.Controls.Add(label1); this.programGroup.Controls.Add(this.cmdline); @@ -668,7 +711,7 @@ this.programGroup.Location = new System.Drawing.Point(10, 10); this.programGroup.Margin = new System.Windows.Forms.Padding(10); this.programGroup.Name = "programGroup"; - this.programGroup.Size = new System.Drawing.Size(197, 108); + this.programGroup.Size = new System.Drawing.Size(200, 134); this.programGroup.TabIndex = 10; this.programGroup.TabStop = false; this.programGroup.Text = "Program"; @@ -680,10 +723,10 @@ this.processGroup.Controls.Add(label5); this.processGroup.Controls.Add(this.pidList); this.processGroup.Controls.Add(this.pidRefresh); - this.processGroup.Location = new System.Drawing.Point(10, 138); + this.processGroup.Location = new System.Drawing.Point(10, 164); this.processGroup.Margin = new System.Windows.Forms.Padding(10); this.processGroup.Name = "processGroup"; - this.processGroup.Size = new System.Drawing.Size(197, 209); + this.processGroup.Size = new System.Drawing.Size(200, 209); this.processGroup.TabIndex = 9; this.processGroup.TabStop = false; this.processGroup.Text = "Process"; @@ -697,10 +740,10 @@ this.vulkanLayerWarn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.vulkanLayerWarn.Image = global::renderdocui.Properties.Resources.information; this.vulkanLayerWarn.ImageAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.vulkanLayerWarn.Location = new System.Drawing.Point(3, 367); + this.vulkanLayerWarn.Location = new System.Drawing.Point(3, 393); this.vulkanLayerWarn.Margin = new System.Windows.Forms.Padding(3, 10, 3, 10); this.vulkanLayerWarn.Name = "vulkanLayerWarn"; - this.vulkanLayerWarn.Size = new System.Drawing.Size(211, 38); + this.vulkanLayerWarn.Size = new System.Drawing.Size(214, 38); this.vulkanLayerWarn.TabIndex = 13; this.vulkanLayerWarn.Text = "Warning: Vulkan capture is not configured. Click here to set up Vulkan capture."; this.vulkanLayerWarn.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; @@ -714,7 +757,7 @@ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScroll = true; this.AutoScrollMinSize = new System.Drawing.Size(215, 0); - this.ClientSize = new System.Drawing.Size(214, 738); + this.ClientSize = new System.Drawing.Size(230, 754); this.Controls.Add(this.tableLayoutPanel2); this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Name = "CaptureDialog"; @@ -774,7 +817,6 @@ private System.Windows.Forms.FlowLayoutPanel capOptsFlow; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; private System.Windows.Forms.GroupBox programGroup; - private System.Windows.Forms.Label label3; private System.Windows.Forms.TextBox cmdline; private System.Windows.Forms.Button workDirBrowse; private System.Windows.Forms.TextBox workDirPath; @@ -794,6 +836,8 @@ private System.Windows.Forms.GroupBox globalGroup; private System.Windows.Forms.Label globalLabel; private System.Windows.Forms.Button vulkanLayerWarn; + private System.Windows.Forms.Button setEnv; + private System.Windows.Forms.TextBox environmentDisplay; } } \ No newline at end of file diff --git a/renderdocui/Windows/Dialogs/CaptureDialog.cs b/renderdocui/Windows/Dialogs/CaptureDialog.cs index 54059ffd2..3eee7e48d 100644 --- a/renderdocui/Windows/Dialogs/CaptureDialog.cs +++ b/renderdocui/Windows/Dialogs/CaptureDialog.cs @@ -53,8 +53,11 @@ namespace renderdocui.Windows.Dialogs public string Executable = ""; public string WorkingDir = ""; public string CmdLine = ""; + public EnvironmentModification[] Environment = new EnvironmentModification[0]; } + private EnvironmentModification[] m_EnvModifications = new EnvironmentModification[0]; + private bool workDirHint = true; private Core m_Core; @@ -69,6 +72,8 @@ namespace renderdocui.Windows.Dialogs workDirPath.Text = settings.WorkingDir; cmdline.Text = settings.CmdLine; + SetEnvironmentModifications(settings.Environment); + workDirPath_Leave(null, null); AllowFullscreen.Checked = settings.Options.AllowFullscreen; @@ -109,6 +114,8 @@ namespace renderdocui.Windows.Dialogs ret.WorkingDir = RealWorkDir; ret.CmdLine = cmdline.Text; + ret.Environment = m_EnvModifications; + ret.Options.AllowFullscreen = AllowFullscreen.Checked; ret.Options.AllowVSync = AllowVSync.Checked; ret.Options.HookIntoChildren = HookIntoChildren.Checked; @@ -195,7 +202,7 @@ namespace renderdocui.Windows.Dialogs #region Callbacks - public delegate LiveCapture OnCaptureMethod(string exe, string workingDir, string cmdLine, CaptureOptions opts); + public delegate LiveCapture OnCaptureMethod(string exe, string workingDir, string cmdLine, EnvironmentModification[] env, CaptureOptions opts); public delegate LiveCapture OnInjectMethod(UInt32 PID, string name, CaptureOptions opts); private OnCaptureMethod m_CaptureCallback = null; @@ -285,7 +292,7 @@ namespace renderdocui.Windows.Dialogs string cmdLine = cmdline.Text; - var live = m_CaptureCallback(exe, workingDir, cmdLine, GetSettings().Options); + var live = m_CaptureCallback(exe, workingDir, cmdLine, GetSettings().Environment, GetSettings().Options); if (queueFrameCap.Checked && live != null) live.QueueCapture((int)queuedCapFrame.Value); @@ -452,6 +459,36 @@ namespace renderdocui.Windows.Dialogs workDirPath.ForeColor = SystemColors.WindowText; } + private void setEnv_Click(object sender, EventArgs e) + { + EnvironmentEditor envEditor = new EnvironmentEditor(); + + foreach (var mod in m_EnvModifications) + envEditor.AddModification(mod, true); + + DialogResult res = envEditor.ShowDialog(this); + + if (res == DialogResult.OK) + SetEnvironmentModifications(envEditor.Modifications); + } + + private void SetEnvironmentModifications(EnvironmentModification[] modifications) + { + m_EnvModifications = modifications; + + string envModText = ""; + + foreach (var mod in modifications) + { + if (envModText != "") + envModText += ", "; + + envModText += mod.GetDescription(); + } + + environmentDisplay.Text = envModText; + } + private void workDirPath_TextChanged(object sender, EventArgs e) { if(Directory.Exists(workDirPath.Text)) diff --git a/renderdocui/Windows/Dialogs/CaptureDialog.resx b/renderdocui/Windows/Dialogs/CaptureDialog.resx index 54cac7c45..d73a9cc19 100644 --- a/renderdocui/Windows/Dialogs/CaptureDialog.resx +++ b/renderdocui/Windows/Dialogs/CaptureDialog.resx @@ -135,6 +135,12 @@ False + + False + + + False + 17, 17 diff --git a/renderdocui/Windows/Dialogs/EnvironmentEditor.Designer.cs b/renderdocui/Windows/Dialogs/EnvironmentEditor.Designer.cs new file mode 100644 index 000000000..a93ee6556 --- /dev/null +++ b/renderdocui/Windows/Dialogs/EnvironmentEditor.Designer.cs @@ -0,0 +1,337 @@ +namespace renderdocui.Windows.Dialogs +{ + partial class EnvironmentEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + TreelistView.TreeListColumn treeListColumn1 = new TreelistView.TreeListColumn("Name", "Name"); + TreelistView.TreeListColumn treeListColumn2 = new TreelistView.TreeListColumn("Modification", "Modification"); + TreelistView.TreeListColumn treeListColumn3 = new TreelistView.TreeListColumn("Value", "Value"); + System.Windows.Forms.GroupBox groupBox2; + System.Windows.Forms.Label label2; + System.Windows.Forms.Label label1; + System.Windows.Forms.GroupBox groupBox1; + System.Windows.Forms.Button ok; + System.Windows.Forms.Button cancel; + this.variables = new TreelistView.TreeListView(); + this.varValue = new System.Windows.Forms.TextBox(); + this.varName = new System.Windows.Forms.TextBox(); + this.pendSeparator = new System.Windows.Forms.ComboBox(); + this.prependType = new System.Windows.Forms.RadioButton(); + this.appendType = new System.Windows.Forms.RadioButton(); + this.setType = new System.Windows.Forms.RadioButton(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.addUpdate = new System.Windows.Forms.Button(); + this.delete = new System.Windows.Forms.Button(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + groupBox2 = new System.Windows.Forms.GroupBox(); + label2 = new System.Windows.Forms.Label(); + label1 = new System.Windows.Forms.Label(); + groupBox1 = new System.Windows.Forms.GroupBox(); + ok = new System.Windows.Forms.Button(); + cancel = new System.Windows.Forms.Button(); + tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.variables)).BeginInit(); + groupBox2.SuspendLayout(); + groupBox1.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.flowLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + tableLayoutPanel1.ColumnCount = 2; + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + tableLayoutPanel1.Controls.Add(this.variables, 0, 0); + tableLayoutPanel1.Controls.Add(groupBox2, 0, 1); + tableLayoutPanel1.Controls.Add(groupBox1, 1, 1); + tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 1, 2); + tableLayoutPanel1.Controls.Add(this.flowLayoutPanel2, 1, 3); + tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + tableLayoutPanel1.Name = "tableLayoutPanel1"; + tableLayoutPanel1.RowCount = 4; + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + tableLayoutPanel1.Size = new System.Drawing.Size(673, 474); + tableLayoutPanel1.TabIndex = 2; + // + // variables + // + treeListColumn1.AutoSizeMinSize = 0; + treeListColumn1.Width = 160; + treeListColumn2.AutoSizeMinSize = 0; + treeListColumn2.Width = 170; + treeListColumn3.AutoSize = true; + treeListColumn3.AutoSizeMinSize = 100; + treeListColumn3.Width = 50; + this.variables.Columns.AddRange(new TreelistView.TreeListColumn[] { + treeListColumn1, + treeListColumn2, + treeListColumn3}); + this.variables.ColumnsOptions.LeftMargin = 0; + tableLayoutPanel1.SetColumnSpan(this.variables, 2); + this.variables.Cursor = System.Windows.Forms.Cursors.Arrow; + this.variables.Dock = System.Windows.Forms.DockStyle.Fill; + this.variables.Font = new System.Drawing.Font("Consolas", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.variables.Location = new System.Drawing.Point(3, 3); + this.variables.Name = "variables"; + this.variables.RowOptions.ShowHeader = false; + this.variables.Size = new System.Drawing.Size(667, 257); + this.variables.TabIndex = 11; + this.variables.Text = "treeListView1"; + this.variables.ViewOptions.HoverHandTreeColumn = false; + this.variables.ViewOptions.PadForPlusMinus = false; + this.variables.ViewOptions.ShowLine = false; + this.variables.ViewOptions.ShowPlusMinus = false; + this.variables.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.variables_AfterSelect); + // + // groupBox2 + // + groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox2.Controls.Add(label2); + groupBox2.Controls.Add(this.varValue); + groupBox2.Controls.Add(label1); + groupBox2.Controls.Add(this.varName); + groupBox2.Location = new System.Drawing.Point(3, 266); + groupBox2.Name = "groupBox2"; + groupBox2.Size = new System.Drawing.Size(489, 122); + groupBox2.TabIndex = 4; + groupBox2.TabStop = false; + groupBox2.Text = "Environment Variable"; + // + // label2 + // + label2.AutoSize = true; + label2.Location = new System.Drawing.Point(9, 68); + label2.Name = "label2"; + label2.Size = new System.Drawing.Size(34, 13); + label2.TabIndex = 3; + label2.Text = "Value"; + // + // varValue + // + this.varValue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.varValue.Location = new System.Drawing.Point(9, 84); + this.varValue.Name = "varValue"; + this.varValue.Size = new System.Drawing.Size(473, 20); + this.varValue.TabIndex = 2; + this.varValue.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.varField_KeyPress); + // + // label1 + // + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(9, 16); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(35, 13); + label1.TabIndex = 1; + label1.Text = "Name"; + // + // varName + // + this.varName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.varName.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest; + this.varName.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource; + this.varName.Location = new System.Drawing.Point(9, 32); + this.varName.Name = "varName"; + this.varName.Size = new System.Drawing.Size(473, 20); + this.varName.TabIndex = 1; + this.varName.TextChanged += new System.EventHandler(this.varName_TextChanged); + this.varName.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.varField_KeyPress); + // + // groupBox1 + // + groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + groupBox1.Controls.Add(this.pendSeparator); + groupBox1.Controls.Add(this.prependType); + groupBox1.Controls.Add(this.appendType); + groupBox1.Controls.Add(this.setType); + groupBox1.Location = new System.Drawing.Point(498, 266); + groupBox1.Name = "groupBox1"; + groupBox1.Size = new System.Drawing.Size(172, 122); + groupBox1.TabIndex = 2; + groupBox1.TabStop = false; + groupBox1.Text = "Modification type"; + // + // pendSeparator + // + this.pendSeparator.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.pendSeparator.FormattingEnabled = true; + this.pendSeparator.Location = new System.Drawing.Point(7, 92); + this.pendSeparator.Name = "pendSeparator"; + this.pendSeparator.Size = new System.Drawing.Size(121, 21); + this.pendSeparator.TabIndex = 6; + // + // prependType + // + this.prependType.AutoSize = true; + this.prependType.Location = new System.Drawing.Point(7, 68); + this.prependType.Name = "prependType"; + this.prependType.Size = new System.Drawing.Size(95, 17); + this.prependType.TabIndex = 5; + this.prependType.TabStop = true; + this.prependType.Text = "Prepend Value"; + this.prependType.UseVisualStyleBackColor = true; + this.prependType.CheckedChanged += new System.EventHandler(this.modification_CheckedChanged); + // + // appendType + // + this.appendType.AutoSize = true; + this.appendType.Location = new System.Drawing.Point(7, 44); + this.appendType.Name = "appendType"; + this.appendType.Size = new System.Drawing.Size(92, 17); + this.appendType.TabIndex = 4; + this.appendType.TabStop = true; + this.appendType.Text = "Append Value"; + this.appendType.UseVisualStyleBackColor = true; + this.appendType.CheckedChanged += new System.EventHandler(this.modification_CheckedChanged); + // + // setType + // + this.setType.AutoSize = true; + this.setType.Location = new System.Drawing.Point(7, 20); + this.setType.Name = "setType"; + this.setType.Size = new System.Drawing.Size(71, 17); + this.setType.TabIndex = 3; + this.setType.TabStop = true; + this.setType.Text = "Set Value"; + this.setType.UseVisualStyleBackColor = true; + this.setType.CheckedChanged += new System.EventHandler(this.modification_CheckedChanged); + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel1.Controls.Add(this.addUpdate); + this.flowLayoutPanel1.Controls.Add(this.delete); + this.flowLayoutPanel1.Location = new System.Drawing.Point(498, 391); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(172, 28); + this.flowLayoutPanel1.TabIndex = 9; + // + // addUpdate + // + this.addUpdate.Location = new System.Drawing.Point(3, 3); + this.addUpdate.Name = "addUpdate"; + this.addUpdate.Size = new System.Drawing.Size(80, 23); + this.addUpdate.TabIndex = 7; + this.addUpdate.Text = "Add / Update"; + this.addUpdate.UseVisualStyleBackColor = true; + this.addUpdate.Click += new System.EventHandler(this.addUpdate_Click); + // + // delete + // + this.delete.Location = new System.Drawing.Point(89, 3); + this.delete.Name = "delete"; + this.delete.Size = new System.Drawing.Size(80, 23); + this.delete.TabIndex = 8; + this.delete.Text = "Delete"; + this.delete.UseVisualStyleBackColor = true; + this.delete.Click += new System.EventHandler(this.delete_Click); + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel2.Controls.Add(ok); + this.flowLayoutPanel2.Controls.Add(cancel); + this.flowLayoutPanel2.Location = new System.Drawing.Point(498, 445); + this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(3, 26, 3, 3); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(172, 26); + this.flowLayoutPanel2.TabIndex = 10; + // + // ok + // + ok.DialogResult = System.Windows.Forms.DialogResult.OK; + ok.Location = new System.Drawing.Point(3, 3); + ok.Name = "ok"; + ok.Size = new System.Drawing.Size(80, 23); + ok.TabIndex = 9; + ok.Text = "OK"; + ok.UseVisualStyleBackColor = true; + // + // cancel + // + cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + cancel.Location = new System.Drawing.Point(89, 3); + cancel.Name = "cancel"; + cancel.Size = new System.Drawing.Size(80, 23); + cancel.TabIndex = 10; + cancel.Text = "Cancel"; + cancel.UseVisualStyleBackColor = true; + // + // EnvironmentEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(673, 474); + this.Controls.Add(tableLayoutPanel1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "EnvironmentEditor"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Capture Environment Editor"; + this.Load += new System.EventHandler(this.EnvironmentEditor_Load); + tableLayoutPanel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.variables)).EndInit(); + groupBox2.ResumeLayout(false); + groupBox2.PerformLayout(); + groupBox1.ResumeLayout(false); + groupBox1.PerformLayout(); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TextBox varValue; + private System.Windows.Forms.TextBox varName; + private System.Windows.Forms.Button addUpdate; + private System.Windows.Forms.ComboBox pendSeparator; + private System.Windows.Forms.RadioButton prependType; + private System.Windows.Forms.RadioButton appendType; + private System.Windows.Forms.RadioButton setType; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Button delete; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private TreelistView.TreeListView variables; + } +} \ No newline at end of file diff --git a/renderdocui/Windows/Dialogs/EnvironmentEditor.cs b/renderdocui/Windows/Dialogs/EnvironmentEditor.cs new file mode 100644 index 000000000..d079486d0 --- /dev/null +++ b/renderdocui/Windows/Dialogs/EnvironmentEditor.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using renderdoc; + +namespace renderdocui.Windows.Dialogs +{ + public partial class EnvironmentEditor : Form + { + public EnvironmentEditor() + { + InitializeComponent(); + + pendSeparator.Items.AddRange(new object[] { + EnvironmentSeparator.Platform.Str(), + EnvironmentSeparator.SemiColon.Str(), + EnvironmentSeparator.Colon.Str(), + EnvironmentSeparator.None.Str() + }); + } + + private void EnvironmentEditor_Load(object sender, EventArgs e) + { + pendSeparator.SelectedIndex = (int)EnvironmentSeparator.Platform; + + setType.Checked = true; + varName.Select(); + varName_TextChanged(varName, new EventArgs()); + + variables.NodesSelection.Clear(); + } + + private int ExistingIndex() + { + int i=0; + foreach (var n in variables.Nodes) + { + if ((string)n[0] == varName.Text) + return i; + + i++; + } + + return -1; + } + + private void modification_CheckedChanged(object sender, EventArgs e) + { + pendSeparator.Enabled = !setType.Checked; + } + + private void varName_TextChanged(object sender, EventArgs e) + { + int idx = ExistingIndex(); + + if (idx >= 0) + { + addUpdate.Text = "Update"; + delete.Enabled = true; + variables.NodesSelection.Clear(); + variables.NodesSelection.Add(variables.Nodes[idx]); + } + else + { + addUpdate.Text = "Add"; + delete.Enabled = false; + + addUpdate.Enabled = (varName.Text.Trim() != ""); + } + } + + private void varField_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == '\n' || e.KeyChar == '\r') + { + addUpdate.PerformClick(); + } + } + + private void variables_AfterSelect(object sender, TreeViewEventArgs e) + { + if (variables.SelectedNode != null) + { + EnvironmentModification mod = variables.SelectedNode.Tag as EnvironmentModification; + + if (mod != null) + { + varName.Text = mod.variable; + varValue.Text = mod.value; + pendSeparator.SelectedIndex = (int)mod.separator; + + if (mod.type == EnvironmentModificationType.Set) + setType.PerformClick(); + else if (mod.type == EnvironmentModificationType.Append) + appendType.PerformClick(); + else if (mod.type == EnvironmentModificationType.Prepend) + prependType.PerformClick(); + } + } + } + + private void delete_Click(object sender, EventArgs e) + { + if (variables.SelectedNode != null) + { + variables.BeginUpdate(); + variables.Nodes.Remove(variables.SelectedNode); + variables.EndUpdate(); + variables.NodesSelection.Clear(); + } + } + + private void addUpdate_Click(object sender, EventArgs e) + { + EnvironmentModification mod = new EnvironmentModification(); + mod.variable = varName.Text; + mod.value = varValue.Text; + mod.separator = (EnvironmentSeparator)pendSeparator.SelectedIndex; + + if (appendType.Checked) + mod.type = EnvironmentModificationType.Append; + else if (prependType.Checked) + mod.type = EnvironmentModificationType.Prepend; + else + mod.type = EnvironmentModificationType.Set; + + AddModification(mod, false); + } + + public void AddModification(EnvironmentModification mod, bool silent) + { + TreelistView.Node node = new TreelistView.Node(); + int idx = ExistingIndex(); + if (idx >= 0) + node = variables.Nodes[idx]; + + if (mod.variable.Trim() == "") + { + if(!silent) + MessageBox.Show("Environment variable cannot be just whitespace", "Invalid variable", MessageBoxButtons.OK, MessageBoxIcon.Error); + + return; + } + + variables.BeginUpdate(); + + if (idx < 0) + variables.Nodes.Add(node); + + node.SetData(new object[] { mod.variable, mod.GetTypeString(), mod.value }); + node.Tag = mod; + + variables.EndUpdate(); + + variables.NodesSelection.Clear(); + variables.NodesSelection.Add(node); + + varName.AutoCompleteCustomSource.Clear(); + for (int i = 0; i < variables.Nodes.Count; i++) + varName.AutoCompleteCustomSource.Add((string)variables.Nodes[i][0]); + } + + public EnvironmentModification[] Modifications + { + get + { + EnvironmentModification[] ret = new EnvironmentModification[variables.Nodes.Count]; + for(int i=0; i < variables.Nodes.Count; i++) + ret[i] = variables.Nodes[i].Tag as EnvironmentModification; + return ret; + } + } + } +} diff --git a/renderdocui/Windows/Dialogs/EnvironmentEditor.resx b/renderdocui/Windows/Dialogs/EnvironmentEditor.resx new file mode 100644 index 000000000..c06d57d09 --- /dev/null +++ b/renderdocui/Windows/Dialogs/EnvironmentEditor.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + False + + + False + + + False + + + False + + + False + + + False + + \ No newline at end of file diff --git a/renderdocui/Windows/MainWindow.cs b/renderdocui/Windows/MainWindow.cs index 355adfd12..8c0ec8e59 100644 --- a/renderdocui/Windows/MainWindow.cs +++ b/renderdocui/Windows/MainWindow.cs @@ -833,14 +833,14 @@ namespace renderdocui.Windows return ""; } - private LiveCapture OnCaptureTrigger(string exe, string workingDir, string cmdLine, CaptureOptions opts) + private LiveCapture OnCaptureTrigger(string exe, string workingDir, string cmdLine, EnvironmentModification[] env, CaptureOptions opts) { if (!PromptCloseLog()) return null; string logfile = m_Core.TempLogFilename(Path.GetFileNameWithoutExtension(exe)); - UInt32 ret = m_Core.Renderer.ExecuteAndInject(exe, workingDir, cmdLine, logfile, opts); + UInt32 ret = m_Core.Renderer.ExecuteAndInject(exe, workingDir, cmdLine, env, logfile, opts); if (ret == 0) { diff --git a/renderdocui/renderdocui.csproj b/renderdocui/renderdocui.csproj index c4ff3d1bc..70a9b7897 100644 --- a/renderdocui/renderdocui.csproj +++ b/renderdocui/renderdocui.csproj @@ -223,6 +223,12 @@ ColumnSelector.cs + + Form + + + EnvironmentEditor.cs + Form @@ -400,6 +406,9 @@ ColumnSelector.cs + + EnvironmentEditor.cs + FindAllDialog.cs