I maintain a VSCode debugger extension for Roku's BrightScript language. Roku doesn't have a formal debug protocol, so my debugger uses the telnet command-line utility to issue various debugging commands (like print variable, continue, step, etc...). Since the Roku is a network device, and we are doing string parsing, every command is fairly slow compared to debugging an on-machine program like node or C#. As such, we try to lazy load data instead of eagerly loading data whenever possible.
Take this object for example:
business = {
employeeNames: ["Michael", "Jim", "Pam", "Dwight"],
name: "Dunder Mifflin Paper Company"
}
When a user adds business to the watch panel, vscode issues an evaluateRequest for "business". Behind the scenes, I run a telnet command print business, which returns the following:
<Component: roAssociativeArray> =
{
employeeNames: <Component: roArray>
name: "Dunder Mifflin Paper Company"
}
At this point, we know that employeeNames is an array, but we don't know its size, and shouldn't eagerly look up its size because that requires a separate command that would slow down the entire debugging experience.
Next, in the VSCode watch panel, the user expands the employeeNames property, and vscode calls variablesRequest. My debug extension runs the telnet command print business.employeeNames, and it returns this:
<Component: roArray> =
[
"Michael"
"Jim"
"Pam"
"Dwight"
]
At this point, I want to update employeeNames type to show the array count, like this:
Here's the DebugProtocol.Variable that I would change in the debug extension...but I don't know how to send this to VSCode when VSCode hasn't asked for updates on this variable.
//hardcoded for the sake of this question...is dynamic in actual code
var variableName = "business.employeeNames"
var arrayValues= getArrayValues(variableName); // ["Michael", "Jim", "Pam", "Dwight"]
let variable: DebugProtocol.Variable = {
name: "employeeNames";
evaluateName: "business.employeeNames";
type: `roArray (${arrayValues})`;
value: arrayValues;
children: [];
}
I tried to trick vscode into re-requesting everything, but VSCode didn't request new variables because it was smart enough to realize that the StoppedEvent was at the same location where it was previously stopped.
//send a continue
this.sendEvent(new ContinuedEvent(0, true));
//wait a short time
setTimeout(()=>{
//send a stopped event
this.sendEvent(new StoppedEvent('exception', threadId, exception.message));
}, 10);
As a debug extension author, from the debug extension code, how do I tell VSCode that a variable in the watch panel has changed, and needs to be refreshed?
Related
I'm looking for some insight and guidance on using the Foundation.Process type with a PTY (Psuedo Terminal) so that the subprocess can accept input and behave as if it was running via a terminal.
The reason for needing a PTY is that for programs like ssh or in my case (xcodes) which ask for user input including passwords, running these via Foundation.Process does not display the prompts to the user as the output is usually buffered (this works fine in the Xcode debugger console but when running via a real terminal that is buffered the prompts are never displayed in the terminal)
Looking at other threads it seems like correct approach here is create a PTY and use the filehandles to attach to the Process.
While I've got this to work to the point where prompts are now shown, I cant seem to figure out how to pass input back to the process as these are being controlled by the PTY.
Here is my Process setup:
let process = Process()
// Setup the process with path, args, etc...
// Setup the PTY handles
var parentDescriptor: Int32 = 0
var childDescriptor: Int32 = 0
guard Darwin.openpty(&parentDescriptor, &childDescriptor, nil, nil, nil) != -1 else {
fatalError("Failed to spawn PTY")
}
parentHandle = FileHandle(fileDescriptor: parentDescriptor, closeOnDealloc: true)
childHandle = FileHandle(fileDescriptor: childDescriptor, closeOnDealloc: true)
process.standardInput = childHandle
process.standardOutput = childHandle
process.standardError = childHandle
With this setup I then read the parent handle and output any result it gets (such as the input prompts):
parentHandle?.readabilityHandler = { handle in
guard let line = String(data: handle.availableData, encoding: .utf8), !line.isEmpty else {
return
}
logger.notice("\(line)")
}
When process.run() is executed the program runs and I can see it asks for Apple ID: input in my terminal, however, when typing input into the terminal the process does not seem to react to this input.
I've tried forwarding the FileHandle.standardInput:
FileHandle.standardInput.readabilityHandler = { handle in
parentHandle?.write(handle.availableData)
}
But this doesn't seem to work either.
What is the recommended way to setup a PTY with Foundation.Process for executing arbitrary programs and having them behave as if they were being run in a terminal context?
Most of the resources I found online are about other languages and I'd like to stick with Foundation.Process vs. doing anything custom in C/C++ if possible as it just makes it easier to reason about / maintain. The resources for Swift on this topic are very lacking and I've checked out some open source projects that claim to do this but most require manually sending input to the PTY handle vs. accepting them from the user in a terminal.
Any insight / help is very much appreciated!
I'm attempting to create a plugin that fetches additional code from a server before the user plays the game on roblox studio.
Basically, the user will use something like blockly to create luau code on a website and I want to sent that code to roblox studio. I've seen some plugins that fetch new data from a server from time to time and I've been able to do that, but I'd like to see if there's a way to only fetch the new code when the user clicks the play button because it could be expensive to request new data every 5 seconds or so.
Below is a simple plugin that attempts to send a request to the server when the game loads, but the script never goes beyond game.Loaded:Wait()
Main file:
local Request = require(script.Parent.Request)
local URL = "http://localhost:3333"
local toolbar = plugin:CreateToolbar("Test")
local button = toolbar:CreateButton("Test", "Test", "rbxassetid://4458901886")
local isListening = false
local request = Request.new()
local ok
local json
local function onClick ()
isListening = not isListening
if (isListening == false) then
return print("Not listening")
end
print("Listening")
if not game:IsLoaded() then
print(game.Loaded)
game.Loaded:Wait()
print("Game has started")
ok, json = request:Get(URL)
print(ok, json)
end
end
button.Click:Connect(onClick)
Request file:
local Request = {}
Request.__index = Request
function Request.new()
return setmetatable({}, Request)
end
function Request:Get(URL)
local ok, result = pcall(game.HttpService.GetAsync, game.HttpService, URL)
local json = game.HttpService:JSONDecode(result)
return ok, json
end
return Request
There isn't an explicit signal to detect when a game is about to start.
But, whenever you hit the Play button, the Edit session ends and the Play session begins. When a session ends, all of the plugins are unloaded. So you could use the plugin.Unloading signal to detect when the Edit session is ending, but it will also fire when the user closes the place, when you stop play testing, or when the plugin is disabled or uninstalled.
You could combine that signal with the RunService:IsEdit() function so that the behavior only triggers when exiting Edit mode, but this is still a really hazy signal.
So in a Script in your plugin, you could do something like this :
local RunService = game:GetService("RunService")
local Request = require(script.Parent.Request)
local URL = "<YOUR URL>"
-- listen for when sessions end
plugin.Unloading:Connect(function()
-- disregard sessions that aren't Edit Mode
if not RunService:IsEdit() end
return
end
print("Game about to start... maybe. The game might also be closing, or the plugin might be disabled from the PluginManager.")
local ok, json = request:Get(URL)
print(ok, json)
end)
Debugging this may be difficult as the Output console is cleared any time you start a Play session, so you won't see any of your print statements. But if you close the place, your logs will be preserved on the Welcome Screen. Just go View > Output to open the Output window.
I`m trying to create custom HID device with STM32F103C8, IDE that i choose is STM32CubeIDE and the tutorial that i was following is at ST youtube official channel.
ST offers great tool "Device configuration tool" where i can configure microcontroler, and a lot of code based on my configuration will be generated. That generated code has "user code parts" where user creates his logic, and if he needs to reconfigure microcontroller "Device configuration tool" will not remove that parts of code.
Problem:
To configure custom usb HID i need to change code generated by "Device configuration tool" in places where is no place for user code and that changes will be removed if i run "Device configuration tool" again.
Fields that i only can set in "Device configuration tool" are this:
But that is not enough i also need to change CUSTOM_HID_EPIN_SIZE and CUSTOM_HID_EPOUT_SIZE defines which represent amount of bytes device and host send to each other at once, and also if i change the size of "data pack" i will need to change the default generated callback function that receives that data and works with it, for example the tool generates code like this:
{
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;
if (hhid->IsReportAvailable == 1U)
{
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);
hhid->IsReportAvailable = 0U;
}
return USBD_OK;
}
but i need the pointer to "Report_buf" not the copy of its first 2 elements, and the default generated code pass only copy of 2 first bytes, and i cant change this in "Device configuration tool".
My current solution:
Actually i solved this issue, but i don`t think i solved it the right way and it works. I have changed the template files which are here "STM32CubeIDE_1.3.0\STM32CubeIDE\plugins\com.st.stm32cube.common.mx_5.6.0.202002181639\db\templates"
And also changed files at "STM32CubeIDE_1.3.0\en.stm32cubef1.zip_expanded\STM32Cube_FW_F1_V1.8.0\Middlewares\ST\STM32_USB_Device_Library\Class\HID"
I don`t think this is the right way to do it, does any one know the right way to do this thing ?
I also found same question on ST forum here but it was not resolved.
what you want to achieve is exactly what is explained by st trainer on this link.
https://www.youtube.com/watch?v=3JGRt3BFYrM
Trainer is step by step explaining how to change the code to use the pointer to buffer
if (hhid->IsReportAvailable == 1U)
{
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(hhid->Report_buf);
hhid->IsReportAvailable = 0U;
}
I am trying to call an existing VFP 6 application using Jacob which is a COM bridge for Java.
val vfp = new Application(new ActiveXComponent("VisualFoxPro.Application").getProperty("Application").toDispatch())
vfp.setVisible(false)
try {
vfp.doCmd("do my.exe with myconfig.txt")
} catch {
case t: Throwable => t.printStackTrace
} finally {
vfp.doCmd("close data")
vfp.doCmd("clear all")
vfp.doCmd("clear")
vfp.quit
vfp.safeRelease
}
When there are no error conditions this code executes well and generates the expected .dbfs. The problem is that when an error occurs (.dbf not found, file in use by another user, etc) a GUI window pops up and stops execution of the program until I use the mouse to cancel it. I want this program to run on a server with no user interaction so this won't work.
How can I gracefully handle the errors preferably without making a change to the VFP 6 program?
Since you have the source code for VFP6, I would suggest looking into
SYS(2335,0)
Sys 2335 is used to identify if the program is running in an "unattended" mode, any such popup dialog boxes will throw an error and prevent an actual "hit" ok/cancel/whatever button to continue. This includes popup window prompting user to pick a table.
I'm not positive of when it was made available as I had limited use of it. Like you, when dealing with a COM server under IIS and obviously nobody there to respond.
Start JVM in headless mode, catch HeadlessException or something. Or, write a Java program that will execute your GUI program using Runtime, and restart in a case of parsed errors in console.
I'm writing an application that can be started either as a standard WinForms app or in unattended mode from the command-line. The application was built using the VS 2k5 standard WinForms template.
When the application is executed from the command-line, I want it to output information that can be captured by the script executing the application. When I do this directly from Console.WriteLine(), the output does not appear, although it can be captured by piping to a file.
On the other hand, I can force the application to pop up a second console by doing a P/Invoke on AllocConsole() from kernel32. This is not what I want, though. I want the output to appear in the same window the application was called from.
This is the salient code that allows me to pop up a console from the command line:
<STAThread()> Public Shared Sub Main()
If My.Application.CommandLineArgs.Count = 0 Then
Dim frm As New ISECMMParamUtilForm()
frm.ShowDialog()
Else
Try
ConsoleControl.AllocConsole()
Dim exMan As New UnattendedExecutionManager(ConvertArgs())
IsInConsoleMode = True
OutputMessage("Application started.")
If Not exMan.SetSettings() Then
OutputMessage("Execution failed.")
End If
Catch ex As Exception
Console.WriteLine(ex.ToString())
Finally
ConsoleControl.FreeConsole()
End Try
End If
End Sub
Public Shared Sub OutputMessage(ByVal msg As String, Optional ByVal isError As Boolean = False)
Trace.WriteLine(msg)
If IsInConsoleMode Then
Console.WriteLine(msg)
End If
If isError Then
EventLog.WriteEntry("ISE CMM Param Util", msg, EventLogEntryType.Error)
Else
EventLog.WriteEntry("ISE CMM Param Util", msg, EventLogEntryType.Information)
End If
End Sub
Raymond Chen recently posted (a month after the question was posted here on SO) a short article about this:
How do I write a program that can be run either as a console or a GUI application?
You can't, but you can try to fake it.
Each PE application contains a field
in its header that specifies which
subsystem it was designed to run
under. You can say
IMAGE_SUBSYSTEM_WINDOWS_GUI to mark
yourself as a Windows GUI application,
or you can say
IMAGE_SUBSYSTEM_WINDOWS_CUI to say
that you are a console application. If
you are GUI application, then the
program will run without a console.
The subsystem determines how the
kernel prepares the execution
environment for the program. If the
program is marked as running in the
console subsystem, then the kernel
will connect the program's console to
the console of its parent, creating a
new console if the parent doesn't have
a console. (This is an incomplete
description, but the details aren't
relevant to the discussion.) On the
other hand, if the program is marked
as running as a GUI application, then
the kernel will run the program
without any console at all.
In that article he points to another by Junfeng Zhang that discusses how a couple of programs (Visual Studio and ildasm) implement this behavior:
How to make an application as both GUI and Console application?
In VisualStudio case, there are actually two binaries: devenv.com and devenv.exe. Devenv.com is a Console app. Devenv.exe is a GUI app. When you type devenv, because of the Win32 probing rule, devenv.com is executed. If there is no input, devenv.com launches devenv.exe, and exits itself. If there are inputs, devenv.com handles them as normal Console app.
In ildasm case, there is only one binary: ildasm.exe. It is first compiled as a GUI application. Later editbin.exe is used to mark it as console subsystem. In its main method it determines if it needs to be run as console mode or GUI mode. If need to run as GUI mode, it relaunches itself as a GUI app.
In the comments to Raymond Chen's article, laonianren has this to add to Junfeng Zhang's brief description of how Visual Studio works:
devenv.com is a general purpose console-mode stub application. When it runs it creates three pipes to redirect the console's stdin, stdout and stderr. It then finds its own name (usually devenv.com), replaces the ".com" with ".exe" and launches the new app (i.e. devenv.exe) using the read end of the stdin pipe and the write ends of the stdout and stderr pipes as the standard handles. Then it just sits and waits for devenv.exe to exit and copies data between the console and the pipes.
Thus even though devenv.exe is a gui app it can read and write the "parent" console using its standard handles.
And you could use devenv.com yourself for myapp.exe by renaming it to myapp.com. But you can't in practise because it belongs to MS.
Update 1:
As said in Michael Burr answer, Raymond Chen recently posted a short article about this. I am happy to see that my guess was not totally wrong.
Update 0:
Disclaimer: This "answer" is mostly speculation. I post it only because enough time has passed to establish that not many people have the answer to what look like a fundamental question.
I think that the "decision" if the application is gui or console is made at compile time and not at runtime. So if you compile your application as gui application, even if you don't display the gui, its still a gui application and doesn't have console. If you choose to compile it as console application then at minimum you will have a console windows flashing before moving to gui "mode". And I don't know if it is possible in managed code.
The problem is fundamental, I think, Because a console application has to take "control" of the calling console application. And it has to do so before the code of the child application is running.
If you want to check if your app is started from the command line in .NET, you can use Console.GetCursorPosition().
The reason that this works is that when you start it from the command line, the cursor moves away from the initial point ((0, 0)) because you typed something in the terminal (the name of the app).
You can do this with an equality check (code in C#):
class Program
{
public static void Main
{
if (Console.GetCursorPosition() == (0, 0))
{
//something GUI
}
else
{
//something not GUI
}
}
}
Note: You must set the output type to Console Application as other output types will make Console.GetCursorPosition() throw an exception.