6

This follows on from my answer to this Q:

Can I change the display format for strings in the watch list?

It turns out that at some point between D7 and XE3, the implementation of the IDE's Watch Window changed from using a TListView to a TVirtualStringTree.

Although I posted an update to my answer that works with XE4 by ignoring the VST and getting the watch value from the clipboard, I'd still like to be able to get the watch value from the VST if I can. I think I know how to do that once I have a reference to the VST but the problem is that my attempt to get one fails.

Following is an MCVE of the code I'm using in my custom package. Hopefully, what it does is self-explanatory. The problem is that the code in the block

  if WatchWindow.Components[i] is TVirtualStringTree then begin
    [...]
  end;

never executes, DESPITE the classname "TVirtualStringTree" appearing in Memo1. Obviously the component with that classname fails the "is" test. I'm guessing that the reason is that the TVirtualTreeView compiled into the IDE is a different version that the one I'm using, v.5.3.0, which is the nearest predecessor I could find to XE4.

So, my question is, is that the likely explanation, and is there anything I can do about it? I suspect if someone can flourish the version of TVirtualStringTree that was used for XE4 from a hat, that might solve my problem.

type
  TOtaMenuForm = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    WatchWindow : TForm;
    VST : TVirtualStringTree;
  end;

procedure TOtaMenuForm.FormCreate(Sender: TObject);
var
  i : Integer;
  S : String;
begin

  WatchWindow := Nil;
  VST := Nil;

  // Iterate the IDE's forms to find the Watch Window
  for i := 0 to Screen.FormCount - 1 do begin
    S := Screen.Forms[i].Name;
    if CompareText(S, 'WatchWindow') = 0 then begin
      WatchWindow := Screen.Forms[i];
      Break;
    end;
  end;

  Assert(WatchWindow <> Nil);

  if WatchWindow <> Nil then begin
    Memo1.Lines.Add('Looking for VST');
    for i := 0 to WatchWindow.ComponentCount - 1 do begin
      Memo1.Lines.Add(IntToStr(i) + ':' + WatchWindow.Components[i].ClassName);
      if WatchWindow.Components[i] is TVirtualStringTree then begin
         VST := TVirtualStringTree(WatchWindow.Components[i]);
         Memo1.Lines.Add('found VST');
         Break;
      end;
    end;
    if VST = Nil then
      Memo1.Lines.Add('VST not found');
  end;
end;

Btw, I realise that solutions that depend of implementational details of IDE are likely to be fragile, but this is just for amusement (I liked the challenge of getting string data out of a component that goes out of its way to avoid storing any).

Community
  • 1
  • 1
MartynA
  • 30,454
  • 4
  • 32
  • 73
  • 3
    The VST version is not the only problem. The `is` operator fails if your code is compiled with different RTTI than the IDE's, so a VST component inside your binary is different than a VST component inside the IDE. Even if your code uses the exact same VST version, the RTTI will still not match. This is where runtime packages come into play. Your binary has to link with runtime package so it shares the same RTL as the IDE, and it has to link to the same package that the IDE uses for its VST component. But if VST is compiled directly into the IDE and not imported from a package, you are SOL... – Remy Lebeau Oct 16 '15 at 18:54
  • 3
    ... you would have to forgo the `is` operator and use a `ClassName()` comparison instead. That will at least allow you to detect the IDE's VST component. But *accessing* it will not be safe unless you have the EXACT same version so the memory layouts still match. – Remy Lebeau Oct 16 '15 at 18:56
  • @RemyLebeau: Thanks. In fact, I'd tried ignoring the "i" result and hard-casting it on the basis of the classname, but that AVs when I try to call the object's methods. Fwiw, I used the same "is" technique in D7 where the Watch Window is a TListView and that worked fine, in terms of getting the reference and using it. – MartynA Oct 16 '15 at 18:59
  • The AV is likely due to you using a different VST version with different member offsets and method parameters than the VST version that the IDE uses. `TListView` "worked" because you and the IDE were likely linking to the same package that implements the standard `TListView` component. – Remy Lebeau Oct 16 '15 at 19:04
  • I'm not sure, but the VST was probably introduced in the IDE for version Delphi-2005. – LU RD Oct 16 '15 at 19:48
  • Why do stupid tricks when you can do a Custom Visualizer instead? http://docwiki.embarcadero.com/RADStudio/XE8/en/Debugger_Visualizers – Warren P Oct 16 '15 at 20:19
  • @WarrenP: Thanks for the interest, but I was interested in finding out how to do this across Delphi versions. And ime, XE8SP1 is the least stable Delphi IDE I've ever had the misfortune to come across. And the OP in the q I was trying to answer was asking about XE3. Feel free to answer this one http://stackoverflow.com/questions/33106754/debugging-the-xe8-ide-as-an-application. – MartynA Oct 16 '15 at 20:23
  • It's possible that what you're trying to do is effectively rendered impossible due to these new plugin/visualizer architectures. So this is relevant to you. – Warren P Oct 16 '15 at 20:24
  • According to the documentation and associated video, debug visualizers were introduced in Delphi 2010. I think that means there are at least 10 Delphi versions with that feature at your disposal; there's nothing special about XE8 in this regard. – Rob Kennedy Oct 16 '15 at 22:04
  • @RobKennedy: At your and WarrenP's prompting, I've taken quite an extended look at the XE8 sample visualizers, and nice though they are, I can't find an obvious way for a user to invoke one *selectively* and silently at the level of the Watch Window context menu to do its stuff on only one Watch variable instance. F.i. the TStrings visualizer require going to the "Visualizers" submenu entry which the IDE adds when there any any registered. But that's an entirely different topic than the subject of my q. – MartynA Oct 17 '15 at 18:41
  • What can you do with a reference to an object obtained that way? You've no grounds to be able to call any of its methods. Do you understand why your `is` fails yet? – David Heffernan Oct 18 '15 at 06:59
  • @DavidHeffernan: Yes, of course I do, thanks. I'd overlooked the point Remy made in his first comment. – MartynA Oct 18 '15 at 08:18
  • So it's all sorted then. Good. – David Heffernan Oct 18 '15 at 08:19

1 Answers1

0

May be You can try to use only published properties of embedded into IDE TVirtualStringTree implementation through RTTI methods to do what you want?

Nashev
  • 490
  • 4
  • 10
  • Actually, I solved this problem by using what I describe in my second answer to this q: http://stackoverflow.com/questions/33121397/can-i-change-the-display-format-for-strings-in-the-watch-list/33127943#33127943 – MartynA Feb 16 '16 at 12:58