浏览代码

Added "Keep Native Console Open" option

Brian Fiete 1 年之前
父节点
当前提交
8052066ab0

+ 3 - 3
BeefLibs/corlib/src/Console.bf

@@ -90,13 +90,13 @@ namespace System
 
 		static function void(StringView str) OutString = => OutString_Simple;
 
-		[CLink, CallingConvention(.Cdecl)]
-		static extern void putchar(char8 c);
+		public static extern void PutChar(char8 c);
+		public static extern void ReopenHandles();
 
 		static void OutString_Simple(StringView str)
 		{
 			for (var c in str.RawChars)
-				putchar(c);
+				PutChar(c);
 		}
 
 		static void OutString_Ex(StringView str)

+ 3 - 0
BeefLibs/corlib/src/Windows.bf

@@ -1496,6 +1496,9 @@ namespace System
 		[CLink, CallingConvention(.Stdcall)]
 		public static extern IntBool GetExitCodeThread(Handle process, out int32 exitCode);
 
+		[Import("psapi.lib"), CLink, CallingConvention(.Stdcall)]
+		public static extern int32 GetModuleFileNameExW(Handle process, Handle module, char16* filename, int32 size);
+
 		[CLink, CallingConvention(.Stdcall)]
 		public static extern int32 ResumeThread(Handle thread);
 

+ 7 - 0
BeefRT/rt/BfObjects.h

@@ -181,6 +181,13 @@ namespace bf
 			Beefy::String GetTypeName();
 		};
 
+		class Console
+		{
+		public:
+			BFRT_EXPORT static void PutChar(char c);
+			BFRT_EXPORT static void ReopenHandles();
+		};
+
 		class Exception : public Object
 		{
 

+ 20 - 0
BeefRT/rt/Internal.cpp

@@ -292,6 +292,26 @@ static void NTAPI TlsFreeFunc(void* ptr)
 	gBfRtCallbacks.Thread_Exiting();
 }
 
+void bf::System::Console::PutChar(char c)
+{
+	putchar(c);
+}
+
+void bf::System::Console::ReopenHandles()
+{
+ 	FILE* fDummy;
+ 	freopen_s(&fDummy, "CONOUT$", "w", stdout);
+	freopen_s(&fDummy, "CONOUT$", "w", stderr);
+	freopen_s(&fDummy, "CONIN$", "r", stdin);	
+// 
+// 	// std::wcout, std::wclog, std::wcerr, std::wcin
+ 	HANDLE hConOut = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ 	HANDLE hConIn = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ 	SetStdHandle(STD_OUTPUT_HANDLE, hConOut);
+ 	SetStdHandle(STD_ERROR_HANDLE, hConOut);
+ 	SetStdHandle(STD_INPUT_HANDLE, hConIn);	
+}
+
 void bf::System::Runtime::Init(int version, int flags, BfRtCallbacks* callbacks)
 {
 	BfpSystemInitFlags sysInitFlags = BfpSystemInitFlag_InstallCrashCatcher;

+ 131 - 20
BeefTools/BeefCon/src/Program.bf

@@ -12,14 +12,20 @@ class Program
 	BeefConConsoleProvider.Pipe mPipe ~ delete _;
 	WinNativeConsoleProvider mProvider ~ delete _;
 	int32 mPid;
+	int32 mAttachedPid;
+	Windows.ProcessHandle mAttachedProcess;
+	String mAttachedName = new .() ~ delete _;
 	int32 mConid;
 	String mExecStr = new .() ~ delete _;
 	SpawnedProcess mSpawnedProcess ~ delete _;
 
 	public ~this()
 	{
-		mSpawnedProcess.Kill();
-		mSpawnedProcess.WaitFor();
+		if (mSpawnedProcess != null)
+		{
+			mSpawnedProcess.Kill();
+			mSpawnedProcess.WaitFor();
+		}
 	}
 
 	static mixin GET<T>(var ptr)
@@ -105,6 +111,16 @@ class Program
 				case .Update:
 					bool paused = GET!<bool>(ptr);
 					mProvider.Update(paused);
+				case .Attached:
+					int32 pid = GET!<int32>(ptr);
+					Process process = scope .();
+					if (process.GetProcessById(pid) case .Ok)
+					{
+						mAttachedPid = pid;
+						mAttachedName.Set(process.ProcessName);
+						WinNativeConsoleProvider.SetConsoleTitleW(mAttachedName.ToScopedNativeWChar!());
+						mAttachedProcess = Windows.OpenProcess(Windows.PROCESS_ALL_ACCESS, false, mAttachedPid);
+					}
 				default:
 				}
 			case .Err(let err):
@@ -113,22 +129,111 @@ class Program
 		}
 	}
 
+	public void ClearConsoleTitle()
+	{
+		WinNativeConsoleProvider.SetConsoleTitleW("BeefIDE Debug Console".ToScopedNativeWChar!());
+	}
+
+	/*[CLink]
+	static extern void* freopen (char8 * filename, char8 * mode, void* stream );
+	[CLink]
+	static extern void* stdout;*/
+
 	public void Run()
 	{
 		mPipe = new .();
 		mPipe.Listen(mPid, mConid);
 
 		mProvider = new .();
-		//mProvider.mHideNativeConsole = false;
+		mProvider.mHideNativeConsole = !mExecStr.IsEmpty;
 		mProvider.Attach();
+		Console.ReopenHandles();
 
-		ProcessStartInfo procInfo = scope ProcessStartInfo();
-		procInfo.UseShellExecute = false;
-		procInfo.SetFileName(mExecStr);
+		if (!mExecStr.IsEmpty)
+		{
+			ProcessStartInfo procInfo = scope ProcessStartInfo();
+			procInfo.UseShellExecute = false;
+			procInfo.SetFileName(mExecStr);
 
-		mSpawnedProcess = new SpawnedProcess();
-		if (mSpawnedProcess.Start(procInfo) case .Err)
-			return;
+			mSpawnedProcess = new SpawnedProcess();
+			if (mSpawnedProcess.Start(procInfo) case .Err)
+				return;
+		}
+		else
+		{
+			ClearConsoleTitle();
+		}
+
+		while (true)
+		{
+			// Check BeefIDE process
+			{
+				var process = Platform.BfpProcess_GetById(null, mPid, null);
+				if (process == null)
+				{
+					Console.Error.WriteLine("Process closed");
+					return;
+				}
+				Platform.BfpProcess_Release(process);
+			}
+
+			MessageLoop();
+
+			if (mPipe.mFailed)
+			{
+				if (mSpawnedProcess == null)
+				{
+					DeleteAndNullify!(mPipe);
+					mPipe = new .();
+					mPipe.Listen(mPid, mConid);
+				}	
+				else
+					return;
+			}
+
+			if (!mPipe.mConnected)
+				Thread.Sleep(20);
+
+			if (mSpawnedProcess != null)
+			{
+				if (mSpawnedProcess.WaitFor(0))
+					return;
+			}
+
+			if (mAttachedPid != 0)
+			{
+				if ((Windows.GetExitCodeProcess(mAttachedProcess, var exitCode)) && (exitCode != 259))
+				{
+					ClearConsoleTitle();
+
+					Console.WriteLine();
+					Console.WriteLine(scope $"{mAttachedName} (process {mAttachedPid}) exited with code {exitCode}.");
+					Console.WriteLine("Press any key to close this window...");
+
+					mAttachedProcess.Close();
+					mAttachedProcess = default;
+					mAttachedPid = 0;
+					mAttachedName.Clear();
+				}
+			}
+
+			if ((mSpawnedProcess == null) && (mAttachedPid == 0))
+			{
+				if (Console.KeyAvailable)
+					return;
+			}
+		}
+	}
+
+	/*public void RunHost()
+	{
+		mPipe = new .();
+		mPipe.Listen(mPid, 0);
+
+
+
+		WinNativeConsoleProvider.AllocConsole();
+		WinNativeConsoleProvider.SetConsoleTitleW("BeefIDE Debug Console".ToScopedNativeWChar!());
 
 		while (true)
 		{
@@ -146,26 +251,32 @@ class Program
 
 			if (!mPipe.mConnected)
 				Thread.Sleep(20);
-
-			if (mSpawnedProcess.WaitFor(0))
-				return;
 		}
-	}
+	}*/
 
 	public static int Main(String[] args)
 	{
-		if (args.Count < 2)
+		if (args.Count < 1)
 		{
-			Console.Error.WriteLine("Usage: BeefCon <pid> <conid> <exe>");
+			Console.Error.WriteLine("Usage: BeefCon <pid> [conid] [exe]");
 			return 1;
 		}
 
 		Program pg = scope .();
-		pg.mPid = int32.Parse(args[0]);
-		pg.mConid = int32.Parse(args[1]);
-		pg.mExecStr.Set(args[2]);
-		pg.Run();
-
+		if (args.Count >= 3)
+		{
+			pg.mPid = int32.Parse(args[0]);
+			pg.mConid = int32.Parse(args[1]);
+			pg.mExecStr.Set(args[2]);
+			pg.Run();
+		}
+		else
+		{
+			pg.mPid = int32.Parse(args[0]);
+			pg.mConid = -Process.CurrentId;
+			pg.Run();
+		}
+		
 		return 0;
 	}
 }

+ 1 - 1
BeefySysLib/platform/win/Platform.cpp

@@ -1500,7 +1500,7 @@ BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char
 			OUTRESULT(BfpProcessResult_UnknownError);
 			return;
 		}
-		WCHAR wName[MAX_PATH];
+		WCHAR wName[MAX_PATH] = { 0 };
 		::GetModuleFileNameExW(hProc, NULL, wName, MAX_PATH);
 		::CloseHandle(hProc);
 		String name = UTF8Encode(wName);

+ 13 - 3
IDE/src/Debugger/DebugManager.bf

@@ -270,7 +270,7 @@ namespace IDE.Debugger
 		static extern char8* Debugger_GetCollectionContinuation(char8* continuationData, int32 callStackIdx, int32 count);
 
 		[CallingConvention(.Stdcall),CLink]
-		static extern void Debugger_ForegroundTarget();
+		static extern void Debugger_ForegroundTarget(int32 altProcessId);
 
 		[CallingConvention(.Stdcall),CLink]
 		static extern void CallStack_Update();
@@ -314,6 +314,9 @@ namespace IDE.Debugger
 		[CallingConvention(.Stdcall),CLink]
 		static extern char8* Debugger_GetProcessInfo();
 
+		[CallingConvention(.Stdcall),CLink]
+		static extern int32 Debugger_GetProcessId();
+
 		[CallingConvention(.Stdcall),CLink]
 		static extern char8* Debugger_GetThreadInfo();
 
@@ -912,9 +915,9 @@ namespace IDE.Debugger
 			outVal.Append(result);
 		}
 
-		public void ForegroundTarget()
+		public void ForegroundTarget(int32 altProcessId)
 		{
-			Debugger_ForegroundTarget();
+			Debugger_ForegroundTarget(altProcessId);
 		}
 
 		public void UpdateCallStack()
@@ -1079,6 +1082,13 @@ namespace IDE.Debugger
 			outProcessInfo.Append(strPtr);
 		}
 
+		public int32 GetProcessId()
+		{
+			if (!mIsRunning)
+				return 0;
+			return Debugger_GetProcessId();
+		}
+
 		public void GetThreadInfo(String outThreadInfo)
 		{
 			if (!mIsRunning)

+ 120 - 1
IDE/src/IDEApp.bf

@@ -426,8 +426,15 @@ namespace IDE
 			public int mSampleRate;
 		}
 
+		class OpenDebugConsoleCmd : ExecutionCmd
+		{
+
+		}
+
         class StartDebugCmd : ExecutionCmd
         {
+			public bool mConnectedToConsole;
+			public int32 mConsoleProcessId;
 			public bool mWasCompiled;
 			public bool mHotCompileEnabled;
 
@@ -9248,6 +9255,73 @@ namespace IDE
 					}
 				}
 
+				if (let startDebugCmd = next as StartDebugCmd)
+				{
+#if BF_PLATFORM_WINDOWS
+					if ((mSettings.mDebugConsoleKind == .Native) && (mSettings.mKeepNativeConsoleOpen))
+					{
+						if (!startDebugCmd.mConnectedToConsole)
+						{
+							if (startDebugCmd.mConsoleProcessId == 0)
+							{
+								int32 processId = 0;
+
+								List<Process> processList = scope .();
+								Process.GetProcesses(processList);
+								defer processList.ClearAndDeleteItems();
+
+								for (var process in processList)
+								{
+									if ((process.ProcessName.Contains("BeefCon.exe")) || (process.ProcessName.Contains("BeefCon_d.exe")))
+									{
+										var title = process.GetMainWindowTitle(.. scope .());
+										if (title.EndsWith("Debug Console"))
+										{
+											processId = process.Id;
+										}
+									}
+								}
+
+								if (processId == 0)
+								{
+									var beefConExe = scope $"{gApp.mInstallDir}/BeefCon.exe";
+
+									ProcessStartInfo procInfo = scope ProcessStartInfo();
+									procInfo.UseShellExecute = false;
+									procInfo.SetFileName(beefConExe);
+									procInfo.SetArguments(scope $"{Process.CurrentId}");
+									procInfo.ActivateWindow = false;
+
+									var process = scope SpawnedProcess();
+									if (process.Start(procInfo) case .Ok)
+									{
+										processId = (.)process.ProcessId;
+									}
+								}
+
+								startDebugCmd.mConsoleProcessId = processId;
+							}
+
+							if (startDebugCmd.mConsoleProcessId != 0)
+							{
+								if (WinNativeConsoleProvider.AttachConsole(startDebugCmd.mConsoleProcessId))
+								{
+									// Worked
+									WinNativeConsoleProvider.ClearConsole();
+									mConsolePanel.mBeefConAttachState = .Attached(startDebugCmd.mConsoleProcessId);
+									startDebugCmd.mConnectedToConsole = true;
+								}
+								else
+								{
+									// Keep trying to attach
+									return;
+								}
+							}
+						}
+					}
+#endif
+				}
+
 				defer delete next;
                 mExecutionQueue.RemoveAt(0);
 
@@ -9336,6 +9410,19 @@ namespace IDE
 	                    }
 	                    else
 	                        OutputLine("Failed to start debugger");
+
+/*#if BF_PLATFORM_WINDOWS
+						if ((mSettings.mDebugConsoleKind == .Native) && (mSettings.mKeepNativeConsoleOpen))
+						{
+							BeefConConsoleProvider.Pipe pipe = scope .();
+							//pipe.Connect(Process.CurrentId, )
+							pipe.Connect(123, -startDebugCmd.mConsoleProcessId).IgnoreError();
+
+							pipe.StartMessage(.Attached);
+							pipe.Stream.Write((int32)mDebugger.GetProcessId());
+							pipe.EndMessage();
+						}
+#endif*/
 					}
                 }
                 else if (next is ExecutionQueueCmd)
@@ -13781,7 +13868,12 @@ namespace IDE
                     if (mForegroundTargetCountdown > 0)
                     {
                         if ((--mForegroundTargetCountdown == 0) && (mDebugger.mIsRunning))
-                            mDebugger.ForegroundTarget();
+						{
+							if (mConsolePanel.mBeefConAttachState case .Connected(let processId))
+                            	mDebugger.ForegroundTarget(processId);
+							else
+								mDebugger.ForegroundTarget(0);
+						}
                     }
 
                     if ((mDebugger != null) && (mExecutionPaused) && (mDebugger.mIsRunning))
@@ -13845,6 +13937,33 @@ namespace IDE
 				DeleteAndNullify!(mLaunchData);
 
 			mErrorsPanel?.UpdateAlways();
+
+			if ((mConsolePanel != null) && (mConsolePanel.mBeefConAttachState case .Attached(let consoleProcessId)))
+			{
+				if (!mDebugger.mIsRunning)
+				{
+					mConsolePanel.Detach();
+				}
+				else
+				{
+					int32 debugProcessId = mDebugger.GetProcessId();
+					if (debugProcessId != 0)
+					{
+						BeefConConsoleProvider.Pipe pipe = scope .();
+						pipe.Connect(Process.CurrentId, -consoleProcessId).IgnoreError();
+						//pipe.Connect(Process.CurrentId, )
+						//pipe.Connect(123, -consoleProcessId).IgnoreError();
+
+						pipe.StartMessage(.Attached);
+						pipe.Stream.Write(debugProcessId);
+						pipe.EndMessage();
+						mConsolePanel.Detach();
+
+						mConsolePanel.mBeefConAttachState = .Connected(consoleProcessId);
+						mDebugger.ForegroundTarget(consoleProcessId);
+					}
+				}
+			}
         }
 
         public void ShowPassOutput(BfPassInstance bfPassInstance)

+ 3 - 0
IDE/src/Settings.bf

@@ -1122,6 +1122,7 @@ namespace IDE
 		public String mWindowsTerminal = new .("Powershell") ~ delete _;
 		public ConsoleKind mDebugConsoleKind;
 		public bool mAlwaysEnableConsole;
+		public bool mKeepNativeConsoleOpen;
 		public String mEmscriptenPath = new .() ~ delete _;
 		public bool mEnableDevMode;
 		public TutorialsFinished mTutorialsFinished = .();
@@ -1184,6 +1185,7 @@ namespace IDE
 				sd.Add("WindowsTerminal", mWindowsTerminal);
 				sd.Add("DebugConsole", mDebugConsoleKind);
 				sd.Add("AlwaysEnableConsole", mAlwaysEnableConsole);
+				sd.Add("KeepNativeConsoleOpen", mKeepNativeConsoleOpen);
 			}
 			using (sd.CreateObject("Wasm"))
 				sd.Add("EmscriptenPath", mEmscriptenPath);
@@ -1279,6 +1281,7 @@ namespace IDE
 				sd.Get("WindowsTerminal", mWindowsTerminal);
 				mDebugConsoleKind = sd.GetEnum<ConsoleKind>("DebugConsole", .Native);
 				mAlwaysEnableConsole = sd.GetBool("AlwaysEnableConsole");
+				mKeepNativeConsoleOpen = sd.GetBool("KeepNativeConsoleOpen");
 			}
 			using (sd.Open("Wasm"))
 				sd.Get("EmscriptenPath", mEmscriptenPath);

+ 16 - 0
IDE/src/ui/ConsolePanel.bf

@@ -281,6 +281,13 @@ class ConsolePanel : Panel
 		}
 	}
 
+	public enum BeefConAttachState
+	{
+		case None;
+		case Attached(int32 processId);
+		case Connected(int32 processId);
+	}
+
 	public ConsoleProvider mConsoleProvider ~ delete _;
 	public DarkCheckBox mMousePassthroughCB;
 	public DarkCheckBox mPauseCB;
@@ -290,6 +297,7 @@ class ConsolePanel : Panel
 	public int32 mCellHeight;
 	public (Position start, Position end)? mSelection;
 	public Position? mClickPos;
+	public BeefConAttachState mBeefConAttachState;
 
 	public bool Paused
 	{
@@ -781,6 +789,14 @@ class ConsolePanel : Panel
 
 	public void Detach()
 	{
+		if (mBeefConAttachState case .Attached)
+		{
+#if BF_PLATFORM_WINDOWS
+			WinNativeConsoleProvider.FreeConsole();
+#endif
+			mBeefConAttachState = .None;
+		}
+
 		if (!mConsoleProvider.Attached)
 			return;
 		mConsoleProvider.Detach();

+ 1 - 0
IDE/src/ui/SettingsDialog.bf

@@ -181,6 +181,7 @@ namespace IDE.ui
 			AddPropertiesItem(category, "Windows Terminal", "mWindowsTerminal");
 			AddPropertiesItem(category, "Debug Console", "mDebugConsoleKind");
 			AddPropertiesItem(category, "Always Enable Console", "mAlwaysEnableConsole");
+			AddPropertiesItem(category, "Keep Native Console Open", "mKeepNativeConsoleOpen");
 			category.Open(true, true);
 		}
 

+ 68 - 3
IDE/src/util/ConsoleProvider.bf

@@ -250,10 +250,13 @@ class WinNativeConsoleProvider : ConsoleProvider
 
 #if BF_PLATFORM_WINDOWS
 	[CLink, CallingConvention(.Stdcall)]
-	public static extern void AllocConsole();
+	public static extern Windows.IntBool AllocConsole();
 
 	[CLink, CallingConvention(.Stdcall)]
-	public static extern void AttachConsole(int processId);
+	public static extern void SetConsoleTitleW(char16* title);
+
+	[CLink, CallingConvention(.Stdcall)]
+	public static extern Windows.IntBool AttachConsole(int processId);
 
 	[CLink, CallingConvention(.Stdcall)]
 	public static extern void FreeConsole();
@@ -290,6 +293,15 @@ class WinNativeConsoleProvider : ConsoleProvider
 
 	[CLink]
 	public static extern Windows.IntBool ReadConsoleInputW(Windows.Handle handle, INPUT_RECORD* eventsPtr, int32 eventCount, out int32 numEventsRead);
+
+	[CLink]
+	public static extern Windows.IntBool SetConsoleCursorPosition(Windows.Handle handle, POINT pos);
+
+	[CLink]
+	public static extern Windows.IntBool FillConsoleOutputCharacterW(Windows.Handle handle, char16 char, int32 length, POINT writeCoord, out int32 numCharsWritten);
+
+	[CLink]
+	public static extern Windows.IntBool FillConsoleOutputAttribute(Windows.Handle handle, uint16 attribute, int32 length, POINT writeCoord, out int32 numCharsWritten);
 #endif
 
 	ScreenInfo mScreenInfo ~ delete _;
@@ -789,6 +801,58 @@ class WinNativeConsoleProvider : ConsoleProvider
 	{
 		return (mScreenInfo != null) ? mScreenInfo.mColorTable[i] : 0xFF000000;
 	}
+
+	public static void ClearConsole()
+	{
+		var outHandle = Console.[Friend]GetStdHandle(Console.STD_OUTPUT_HANDLE);
+
+		POINT coordScreen = default;    // home for the cursor
+		int32 cCharsWritten;
+		CONSOLE_SCREEN_BUFFER_INFOEX csbi = default;
+		csbi.mSize = sizeof(CONSOLE_SCREEN_BUFFER_INFOEX);
+
+		// Get the number of character cells in the current buffer.
+		if (!GetConsoleScreenBufferInfoEx(outHandle, ref csbi))
+		{
+		    return;
+		}
+
+		let dwConSize = (int32)csbi.mWidth * csbi.mHeight;
+
+		// Fill the entire screen with blanks.
+		if (!FillConsoleOutputCharacterW(outHandle,        // Handle to console screen buffer
+		                                ' ',      // Character to write to the buffer
+		                                dwConSize,       // Number of cells to write
+		                                coordScreen,     // Coordinates of first cell
+		                                out cCharsWritten)) // Receive number of characters written
+		{
+		    return;
+		}
+
+		// Get the current text attribute.
+		if (!GetConsoleScreenBufferInfoEx(outHandle, ref csbi))
+		{
+		    return;
+		}
+
+		// Set the buffer's attributes accordingly.
+		if (!FillConsoleOutputAttribute(outHandle,         // Handle to console screen buffer
+		                                csbi.wAttributes, // Character attributes to use
+		                                dwConSize,        // Number of cells to set attribute
+		                                coordScreen,      // Coordinates of first cell
+		                                out cCharsWritten))  // Receive number of characters written
+		{
+		    return;
+		}
+
+		// Put the cursor at its home coordinates.
+		SetConsoleCursorPosition(outHandle, coordScreen);
+	}
+
+	public void Clear()
+	{
+		ClearConsole();
+	}
 }
 
 class BeefConConsoleProvider : ConsoleProvider
@@ -807,7 +871,8 @@ class BeefConConsoleProvider : ConsoleProvider
 		MouseUp,
 		MouseWheel,
 		ScrollTo,
-		Update
+		Update,
+		Attached
 	}
 
 	public class Pipe

+ 6 - 1
IDEHelper/Compiler/CeDebugger.cpp

@@ -409,7 +409,7 @@ void CeDebugger::ContinueDebugEvent()
 	mCeMachine->mDebugEvent.Set();
 }
 
-void CeDebugger::ForegroundTarget()
+void CeDebugger::ForegroundTarget(int altProcessId)
 {
 }
 
@@ -4361,6 +4361,11 @@ String CeDebugger::GetProcessInfo()
 	return String();
 }
 
+int CeDebugger::GetProcessId()
+{
+	return 0;
+}
+
 DebugVisualizerEntry* CeDebugger::FindVisualizerForType(BfType* dbgType, Array<String>* wildcardCaptures)
 {
 	auto ceModule = mCeMachine->mCeModule;

+ 2 - 1
IDEHelper/Compiler/CeDebugger.h

@@ -324,7 +324,7 @@ public:
 	virtual String GetDbgAllocInfo() override;
 	virtual void Update() override;
 	virtual void ContinueDebugEvent() override;
-	virtual void ForegroundTarget() override;
+	virtual void ForegroundTarget(int altProcessId) override;
 	virtual Breakpoint* CreateBreakpoint(const StringImpl& fileName, int lineNum, int wantColumn, int instrOffset) override;
 	virtual Breakpoint* CreateMemoryBreakpoint(intptr addr, int byteCount) override;
 	virtual Breakpoint* CreateSymbolBreakpoint(const StringImpl& symbolName) override;
@@ -359,6 +359,7 @@ public:
 	virtual String GetAutoLocals(int callStackIdx, bool showRegs) override;
 	virtual String CompactChildExpression(const StringImpl& expr, const StringImpl& parentExpr, int callStackIdx) override;
 	virtual String GetProcessInfo() override;
+	virtual int GetProcessId() override;
 	virtual String GetThreadInfo() override;
 	virtual void SetActiveThread(int threadId) override;
 	virtual int GetActiveThread() override;

+ 7 - 2
IDEHelper/DebugManager.cpp

@@ -1363,9 +1363,9 @@ BF_EXPORT const char* BF_CALLTYPE Debugger_GetCollectionContinuation(const char*
 	return outString.c_str();
 }
 
-BF_EXPORT void BF_CALLTYPE Debugger_ForegroundTarget()
+BF_EXPORT void BF_CALLTYPE Debugger_ForegroundTarget(int altProcessId)
 {
-	gDebugger->ForegroundTarget();
+	gDebugger->ForegroundTarget(altProcessId);
 
 	//BOOL worked = EnumThreadWindows(gDebugger->mProcessInfo.dwThreadId, WdEnumWindowsProc, 0);
 	//BF_ASSERT(worked);
@@ -1378,6 +1378,11 @@ BF_EXPORT const char* BF_CALLTYPE Debugger_GetProcessInfo()
 	return outString.c_str();
 }
 
+BF_EXPORT int BF_CALLTYPE Debugger_GetProcessId()
+{
+	return gDebugger->GetProcessId();
+}
+
 BF_EXPORT const char* BF_CALLTYPE Debugger_GetThreadInfo()
 {
 	String& outString = *gTLStrReturn.Get();

+ 2 - 1
IDEHelper/Debugger.h

@@ -285,7 +285,7 @@ public:
 	virtual String GetDbgAllocInfo() = 0;
 	virtual void Update() = 0;
 	virtual void ContinueDebugEvent() = 0;
-	virtual void ForegroundTarget() = 0;
+	virtual void ForegroundTarget(int altProcessId) = 0;
 	virtual Breakpoint* CreateBreakpoint(const StringImpl& fileName, int lineNum, int wantColumn, int instrOffset) = 0;
 	virtual Breakpoint* CreateMemoryBreakpoint(intptr addr, int byteCount) = 0;
 	virtual Breakpoint* CreateSymbolBreakpoint(const StringImpl& symbolName) = 0;
@@ -320,6 +320,7 @@ public:
 	virtual String GetAutoLocals(int callStackIdx, bool showRegs) = 0;
 	virtual String CompactChildExpression(const StringImpl& expr, const StringImpl& parentExpr, int callStackIdx) = 0;
 	virtual String GetProcessInfo() = 0;
+	virtual int GetProcessId() = 0;
 	virtual String GetThreadInfo() = 0;
 	virtual void SetActiveThread(int threadId) = 0;
 	virtual int GetActiveThread() = 0;

+ 18 - 4
IDEHelper/WinDebugger.cpp

@@ -3055,6 +3055,8 @@ void WinDebugger::ContinueDebugEvent()
 
 static BOOL CALLBACK WdEnumWindowsProc(HWND hwnd, LPARAM lParam)
 {
+	int wantProcessId = lParam;
+
 	HWND owner = GetWindow(hwnd, GW_OWNER);
 	if (!IsWindowVisible(hwnd))
 		return TRUE;
@@ -3062,7 +3064,7 @@ static BOOL CALLBACK WdEnumWindowsProc(HWND hwnd, LPARAM lParam)
 	DWORD processId = 0;
 	DWORD threadId = GetWindowThreadProcessId(hwnd, &processId);
 
-	if (processId != ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId)
+	if (processId != wantProcessId)
 		return TRUE;
 	
 	while (true)
@@ -3080,8 +3082,12 @@ static BOOL CALLBACK WdEnumWindowsProc(HWND hwnd, LPARAM lParam)
 	return TRUE;
 }
 
-void WinDebugger::ForegroundTarget()
+void WinDebugger::ForegroundTarget(int altProcessId)
 {
+	int wantProcessId = altProcessId;
+	if (wantProcessId == 0)
+		wantProcessId = ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId;
+
 	HWND hwnd = ::GetForegroundWindow();
 	if (hwnd != INVALID_HANDLE_VALUE)
 	{
@@ -3090,8 +3096,8 @@ void WinDebugger::ForegroundTarget()
 		if (processId == ((WinDebugger*)gDebugger)->mProcessInfo.dwProcessId)
 			return; // Already good
 	}
-
-	EnumWindows(WdEnumWindowsProc, 0);
+	
+	EnumWindows(WdEnumWindowsProc, wantProcessId);
 }
 
 static int gFindLineDataAt = 0;
@@ -10977,6 +10983,14 @@ String WinDebugger::GetProcessInfo()
 	return retStr;
 }
 
+int WinDebugger::GetProcessId()
+{
+	AutoCrit autoCrit(mDebugManager->mCritSect);
+	if (!mThreadList.IsEmpty())
+		return mThreadList[0]->mProcessId;
+	return mDbgProcessId;
+}
+
 String WinDebugger::GetThreadInfo()
 {
 	AutoCrit autoCrit(mDebugManager->mCritSect);

+ 2 - 1
IDEHelper/WinDebugger.h

@@ -598,7 +598,7 @@ public:
 	virtual String GetDbgAllocInfo() override;
 	virtual void Update() override;
 	virtual void ContinueDebugEvent() override;
-	virtual void ForegroundTarget() override;
+	virtual void ForegroundTarget(int altProcessId) override;
 	virtual Breakpoint* CreateBreakpoint(const StringImpl& fileName, int lineNum, int wantColumn, int instrOffset) override;
 	virtual Breakpoint* CreateMemoryBreakpoint(intptr addr, int byteCount) override;
 	virtual Breakpoint* CreateSymbolBreakpoint(const StringImpl& symbolName) override;
@@ -630,6 +630,7 @@ public:
 	virtual String GetAutoLocals(int callStackIdx, bool showRegs) override;
 	virtual String CompactChildExpression(const StringImpl& expr, const StringImpl& parentExpr, int callStackIdx) override;
 	virtual String GetProcessInfo() override;
+	virtual int GetProcessId() override;
 	virtual String GetThreadInfo() override;
 	virtual void SetActiveThread(int threadId) override;
 	virtual int GetActiveThread() override;