Tag Archives: TorqueScript

VSTorqueScript #6

I've spent the last two days working on the Debug Engine and I've made some progress:

  • Tracepoint Support
  • Breakpoint Support
  • Support for the Watch window (I might have enlisted this already once but now support is more complete)

I'm not going to post any screenshots this time, instead I'll try to create an alpha package for you to test. This will probably take some time, too, because I want to make sure that you can install and uninstall it cleanly. I've been using the experimental hive so far, so I have to make sure it doesn't corrupt anyone's installation accidentially... :-)

A few implementation notes for interested people as always:

  • If you ever have to marshal an object to an IntPtr, be very careful, because doing the wrong thing will certainly crash your application - I had that happen to me and it took me quite some time to figure out why Visual Studio was suddenly  'experiencing internal problems' and had to be closed' all the time:
    When you have to convert an IntPtr to a wrapped object, use

    MyObject = (MyType) GetObjectFromIUnknown( intPtr ); // or As MyType;
    

    As you see, the intPtr is a COM object and every COM object implements IUnknown, so the call is pretty safe. The conversion afterwards is already 'managed', so safe, too.
    If you want to convert an IntPtr from an object, you have to be more careful though. The Marshal namespace offers at least two methods for it:

    • public static IntPtr GetComInterfaceForObject(
      	Object o,
      	Type T
      )
      
    • public static IntPtr GetIUnknownForObject(
      	Object o
      )
      

    Don't use the latter for anything except if you really need an IUnknown interface IntPtr pointer!
    The method really only returns a pointer to the IUnknown interface of the object, which usually isn't what the unmanaged code expects - which probably will result in misinterpreting some uninitialized memory as a vtable that is not there.
    You have to use GetComInterfaceForObject and don't forget this. Because you're only working with IntPtrs in the end, there is nothing to warn you about your misstep.

  • If you want to use an object with System.Collection.Generic.Dictionary<K,V>, it's not sufficient to implement IEquatable<K> as stated in the documentation - you also have to override GetHashCode (and override the normal Equals, too, then while you're at it), because Dictionary uses GetHashCode for the bucket assignments and you can implement IEquatable as hard as you want, GetHashCode might simply assign two 'equal' objects to different buffers and you have a problem..
  • One of the benefits of COM is that no one cares about your implementation.. take advantage of it!
    I had to remember this the hard way, when I implemented my breakpoint code. At first I wrote a PendingBreakpoint class, which implemented IDebugPendingBreakpoint2, and then a BoundBreakpoint one, which implemented IDebugBoundBreakpoint2. It was unnecessary messy and today I sighed and rewrote the breakpoint code to use just one Breakpoint class that implements both interfaces and it's a lot more elegant now.
  • Funny fact: An enum that is even mentioned in MSDN and the SDK docs is missing from the Interops library:
    public enum enum_BP_UNBOUND_REASON
    {
        BPUR_UNKNOWN = 0x0000,
        BPUR_CODE_UNLOADED = 0x0002,
        BPUR_BREAKPOINT_REBIND = 0x0003,
        BPUR_BREAKPOINT_ERROR = 0x0004
    };
    
  • The initialization process for the DE is as follows:
    1. DebugEngine and Program Creation Events
    2. Load Complete Event
    3. Call CreatePendingBreakpoint for all known breakpoints
    4. First Continue Call
    5. Entry Point Event
    6. Either the second Continue call or the SDM enters break mode

I had to write a few wrapper classes, too, which I might post about next time around. It's enough for today though.

Cheers,
Black

VSTorqueScript #5

It took me quite long to make progess in the last days. Partially this is due me being in Munich again and working in my uni job part-time again - and I'm stunned each time how tired I am when I come home.

It also is due to me experimenting with my TelnetDebuggerClient implementation that connects my debug engine to Torque's Telnet Debugger. I've tried quite a few different approaches for synchronous and asynchronous communication before I could settle for the simplest and the more advanced ones reside in the repository's history. I'll probably have to switch to them sooner or later because I only implement asynchronous communication at the moment which is fine for most things except when you need more advanced debugging info like for displaying objects properly in the debug window for example.

I've also had to read the "Definitive ANTLR Reference" in one night because I had to return it to uni's library the next day, so I was busy for 7 hours reading 350 pages of a parser reference. It was interesting though and tonight I'll try to write a QuakeC grammar - that's the plan at least.

However yesterday, I had time to work on the debug engine some more and here's a picture that tells it all:

VSTorqueScript now supports displaying the callstack, too

VSTorqueScript now supports displaying the callstack, too

As you see you can now break a program and it will retrieve the callstack from Torque and update the callstack window automatically and open the right file and jump to the correct file position, too - or rather as I've just noticed one-line off because VS line indicies have base 0 and TorqueScript uses base 1 >_>...

A few implementation notes as always:

  • I t seems, if your Debug Engine doesn't implement IDebugEngineLaunch2, your Program Provider has to implement IDebugProgramProvider2.WatchForProviderEvents
  • Moreover, if you don't implement IDebugEngineLaunch2, which in turn forces you to implement IDebugProcess2 (and maybe IDebugProcess3, too), suddenly some "deprecated" methods in your Program class (your IDebugProgram2 implementation) will be called, which, if you follow MSDN and the debug engine sampe, will result in failed asserts and the nice fail message "This function is not called by the debugger." :-)
    It's kind of logical that IDebugProgram2.Execute() is going to be called because the debugger has no IDebugProcess2 interface to call Execute on..
  • What is enum_FRAMEINFO_FLAGS_VALUES? It's undocumented and I suspect it's used for the undocumented m_dwFlags field in FRAMEINFO...
  • I'm totally stunned by how stable Visual Studio is. Most the interfaces I implement are only half implemented and return E_NOTIMPL on most calls and Visual Studio runs and runs well and only displays the information it can in a clean way.
    I guess this is one of the advantages of the COM programming/design paradigma and I think it's great. Being able to implement interfaces bit by bit and add functionality slowly makes debugging easier and also helps you understand things better - which is good because even though a lot is documented, you still need some practice and trial & error to get things right eventually.

Enough is enough though for now, and I'll head to uni in a bit.

Only one last remark: I've started reading the book "The Pragmatic Programmer" and so far it's really entertaining and, I guess, good.
I'll write more on it, when I've finished it and can actually assess its qualities..

Cheers,
Andreas

Visual TorqueScript #2

I haven't found time to add IntelliSense to the language service yet, so I've only added a basic parser and enabled brace highlighting/matching which was easier to do.

I've been told that a working debugger would be the most important feature, so I try to get started with it, but before I can do that I have to add a custom project type to support the standard .cs extension for TorqueScript and to offer a specialized debug options property page for Torque's telnet debugger.

Here you can see the progress I've made so far:
I've added a new custom project type and changed everything to support TorqueScript files that use the usual .cs extension. There is no conflict with C# whatsoever. (At least from what I know..)

Next I'll look into removing the MSBuild faff (if possible at all) and adding the debug configuration page.

Last but not least I've found another neat tool that I can only recommend and want to share with whoever might read this:
It's called VSSDK Assist and it's a so-called Guidance Package - it provides you with a neat assistance window that helps you do common tasks using the Visual Studio SDK in the preferred way (it supplies you with lots of dialog-based wizards for all kinds of things like custom pages/tools/editors/projects/...).

You can grab it here.

More to come.
Cheers,
Black

Visual TorqueScript #1

I've spent most of this week learning as much about Visual Studio's extensibility features as possible and I've decided to start a tiny project to see how easy it is to add a new language service to Visual Studio.

My conclusion so far is: it's not easy and that obvious but it certainly is easier than writing your own IDE. There is also the advantage that you have access to a nice debugging environment if you manage to write your own debugger service.

Anyway, I've started to work on adding basic support for TorqueScript to Visual Studio and after much learning, I've finished implementing the colorizer for TorqueScript today:

As you see I have to use the extension .tscript for now, because as you know .cs is already reserved for C#. I'm going to fix this later by adding a TorqueScript project type that overrides this link locally for your TorqueScript projects (I could live with that).

Creating your own language services is pretty easy, if you are ok with using C# and ManagedBabel. You just have to supply the specific grammar files for the managed lexer and parser generators and you're pretty much done with the basic stuff.

The Visual Studio SDK is pretty neat (you can grab it from here if you want) and another neat thing no one has probably heard about is that Microsoft gives away the Visual Studio Shell for free - yeah for free. That means you can build your own custom tools that have the Visual Studio look and feel and you don't have to pay Microsoft a dime for it.

A good place to start in general if you're interested is the VS Extensibility Center.

Stay tuned for more features (syntax parsing and debugging featues are the next thing on my plan).
Cheers,
BlackHC