Safe event hooking and unhooking (for Delphi IDE plugins)

 Delphi, GExperts  Comments Off on Safe event hooking and unhooking (for Delphi IDE plugins)
Oct 122015
 

In the comments to my Google+ post announcing my blog post on Hacking the Delphi 10 Project Options dialog Ondrej Kelle said:

If two or more plugins hijack the same global event handler then they must restore the previous (from their point of view) handler in reverse order, however this is not guaranteed and therefore this technique is likely to cause trouble.

and

API should be written, safe for plugins to use instead of the event handlers

to which Heinz Toskano added

This scenario remembers me when I wrote interrupt handlers for DOS. The civilized way was to hook and return the original handler, except when you replace the interrupt, in that case you was obliged to expose the original interface, just to not broke something else. Something similar must be done with IDE plugins, otherwise it will become bad coding.

I did a quick Google search but found no existing standard so I decided to simply describe one and publish it in the hope that others will find it useful and adhere to it.

NOTE:
I have moved the code and description to it’s own static page. I will improve it as I go along and blog about these improvements.

I post a link to this article on Google+. If you want to leave any comments, use this post.

How to disable update checking in TortoiseSVN?

 TortoiseSVN, Windows  Comments Off on How to disable update checking in TortoiseSVN?
Oct 122015
 

The latest 1.9.x versions of TortoiseSVN no longer support Windows XP (1.8.12 was the last one that did). Since some of the computers I work with are stuck with XP there isn’t much sense in having it check for updates automatically.

Older versions of TortoiseSVN used to have a check box on the settings “General” page, but that apparently was removed later. Now, it is on the “Advanced” page and called “VersionCheck”. Setting it to “false” disables the check.

TortoiseSVN Settings Advanced VersionCheck

Hacking the Delphi 10 Project Options dialog

 Delphi, GExperts  Comments Off on Hacking the Delphi 10 Project Options dialog
Oct 112015
 

Today I wanted to do something I did before: Add a drop file handler to an edit control in a dialog of the Delphi IDE. The dialog in question is the the Project Options dialog.

I thought it would be pretty simple to do that:

  1. Get the dialog instance from Screen.Forms list.
  2. Get the edit control using the form’s FindComponent method
  3. Attach a drop file handler to it.

Pretty standard for somebody who has written IDE plugins for Delphi before and is somewhat acquainted with its internals. Little did I know how difficult it would become.

First, the Project Options dialog is created dynamically (many other dialogs are created at program start). So, how do I access it?

It turns out that the global Screen object has got a useful event for this: Screen.OnActiveFormChange which is called every time the active form in the IDE changes. So my plugin could just assign an event handler to it and wait for the Project Options form to become the active one. This works, but there are two pitfalls:

  1. The IDE internally uses this event.
  2. How do I know which form is the one I am looking for?

To solve the first I have to hook it and call the original event handler.

To solve the second, I had to enhance my Delphi IDE Explorer to dynamically update its display. It also hooks Screen.OnActiveFormChange for this. This gave me the name and class of the Project Options dialog:

DelphiProjectOptionsDialog: TDelphiProjectOptionsDialog

So, now I had the dialog instance and needed the edit control for the debugging host application. Again, using the IDE explorer, I found its name and class name:

SourcePathInput: THistoryPropComboBox

So, it’s just a matter of calling FindComponent, check the class type to make sure it is the correct control and be done with it, right?

Not so, the Dialog also contains some frames that own their controls rather than the form. So I had to traverse the control hierarchy in order to find the control. It looks like this:

DelphiProjectOptionsDialog: TDelphiProjectOptionsDialog
  [3] Panel2: TPanel
    [1] PropertySheetControl1: TPropertySheetControl
      [0] : TDebuggerLocalPage
        [2] Panel1: TPanel
          [0] HostAppGroupBox: TGroupBox
            [0] HostAppInput: THistoryPropComboBox
          [2] CDWGroupBox: TGroupBox
            [0] CWDInput: THistoryPropComboBox
          [3] SourcePathGroupBox: TGroupBox
            [0] SourcePathInput: THistoryPropComboBox

      [2] : TDebuggerSymbolTablePage
        [1] gbSymbolTables: TGroupBox
          [1] ddDebugSymbolSearchPath: THistoryPropComboBox
          [4] cbLoadAllSymbols: TCheckBox
          [3] Panel1: TPanel
            [0] ListViewPanel: TPanel
              [0] SymbolTablesListView: THintListView

The numbers in front of the controls are its index in the parent’s Controls array.

The last challenge was the control’s class: THistoryPropComboBox. Whatever it is, it is not a TCombobBox but at least it derives from TCustomComboBox, so I could typecast it to that. But how do I access the Text property to fill it with the name of the file dropped on it? It is a protected field in TCustomComboBox. I took a gamble and assumed that it just publishes this field like TComboBox does and hard type casted it to TComboBox. It worked.

So, after several hours of hacking I can now drop an executable from the Windows Explorer on the Host application input field and its filename will automagically appear there.

As you can seen from the table above, there are some other interesting input fields for drop file support. They will come next, but for today, one is enough.

Entry Point Not Found in dbkdebugide220.bpl

 Delphi, GExperts  Comments Off on Entry Point Not Found in dbkdebugide220.bpl
Oct 112015
 

Note to self: If you encounter the error

bds.exe Entry Point Not Found

bds.exe – Entry Point Not Found
The procedure entry point
@System@Sysutils@Exception@GetBaseException$qqrv
could not be located in the dynamic link library
C:\Delphi\DelphiXE8\bin\dbkdebugide220.bpl.

while trying to debug an IDE Expert for Delphi 10 Seattle:

  1. Check the host application you put into the Run -> Parameters dialog! Chances are you are calling the Delphi XE8 IDE there rather than Delphi 10 Seattle.
    Project Options for GExpertsRS10 release
  2. If that looks fine, check that you looked in the right place. Did you really look in the Debug Win32 configuration?
    Project Options for GExpertsRS10 debug

Experimental GExperts Version 1.38 2015-10-10 released

 Delphi, GExperts  Comments Off on Experimental GExperts Version 1.38 2015-10-10 released
Oct 102015
 

There is nothing new about the formatter code. But there are two new GExpert functions and I also improved several GExperts dialogs:

The first new functionality is hiding the navigation bar in Delphi 10 Seattle. The code was kindly donated by Achim Kalwa. You can find the option in the GExperts configuration dialog on the Code Editor tab:

GExperts Configuration: Hide Navigation Bar

The second new functionality is for all supported Delphi versions(*). It enables auto complete and dropping directories from the explorer in the edit control used for adding directories to a search path. To enable it, set the corresponding check box in the GExperts configuration dialog on the IDE tab:

GExperts Configuration: enhance search paths

This dialog is used in several places in the IDE, e.g. the search path in project options, the library path and the browsing path in the general options.

(*) Actually the dialog is also used in several other places where it does not manage search path’s but something else, so I had to filter for the dialog caption in addition to the dialog class. I am sure it works with the English Delphi versions but there might be French and German versions with a different dialog caption than the one I check for. I would also have liked to support the Japanese version, but nobody from Japan answered me when I asked for the dialog captions on Google+. So, if it does not work for you, head for the linked G+ post and add the caption of your dialog there. I’ll add those new captions to the next release.

I also added auto complete and file/directory drop support from the explorer to the following GExperts dialogs:

  • Configuration dialog, “General” tab, for VCL source directory, GExperts storage directroy and Help file.
  • Grep Search, the directory to search in when doing a directory search
  • Backup Project for additional files and directories; Also, the corresponding configuration dialog got an enhanced input for the directory where to save the files.
  • Clean Directory for additional directories and file extensions
  • Code Librarian Options for the Code Librarian Storage Location

There are probably other dialogs to enhance. I will get to them given time.

If you have been using my previous experimental version with Delphi 10 Seattle, you will have to uninstall it from the IDE first before installing the new version, because the dll name has changed. Erik decided to simply call it GExpertsRS10.dll.

Head over to the Experimental GExperts page to download it.

Batch wizardry: Finding a subdir in all parent dirs

 Batch, Windows  Comments Off on Batch wizardry: Finding a subdir in all parent dirs
Oct 042015
 

As an addition to Using my buildtools here is a batch subroutine to find a subdirectory in any of the parent directories of a script:

:FindInParents
@rem search all parent directories for a subdir and return
@rem the full path to that directory in %result%
setlocal
set parentdir=%1%
set subdir=%2%
:loop
call :GetDir %parentdir%
set parentdir=%result%
if exist %parentdir%\%subdir% goto found
goto loop
:found
endlocal & set result=%parentdir%\%subdir%
goto :eof

:GetDir
rem extract path
setlocal
set result=%~dp1%
rem remove any quotes
set result=%result:"=%
rem add quotes
set result="%result%"
rem remove \ before the closing quote
set result=%result:\"="%
rem remove any quotes
set result=%result:"=%
endlocal & set result=%result%
goto :eof

To use it, you first have to copy it to the script that wants to use it. (You could probably also put it into its own file an call that file but for my purpose that would not work.) Then you call it like this:

call :FindInParents %0% buildtools
@rem The result is returned in the %result% environment variable.

As you can see, it needs two parameters. The first one is the directory from where to start searching. Lazy bastard™ that I am I just take the %0% implicit parameter of the batch file that contains its name. While that actually is a file name and not a directory it will work nonetheless because %0%\buildtools does not exist. The second parameter is the name of the subdirectory to find. So the above looks for a subdirectory called “buildtools” in any of the directories contained in the full filename of the current batch file. Don’t forget to add a

goto :eof

to the end of the main batch code so it will not execute the subroutine as well.

The full batch file I use in GExperts to call a central doBuildProject.cmd script located in the buildtools directory looks like this:

@rem Searches the parent dirctories for the buildtools and calls the doBuildProject.cmd there
@echo off
setlocal
call :FindInParents %0% buildtools
call %result%\doBuildProject.cmd
pause
goto :eof

:FindInParents
@rem search all parent directories for a subdir and return
@rem the full path to that directory in %result%
setlocal
set parentdir=%1%
set subdir=%2%
:loop
call :GetDir %parentdir%
set parentdir=%result%
if exist %parentdir%\%subdir% goto found
goto loop
:found
endlocal & set result=%parentdir%\%subdir%
goto :eof

:GetDir
rem extract path
setlocal
set result=%~dp1%
rem remove any quotes
set result=%result:"=%
rem add quotes
set result="%result%"
rem remove \ before the closing quote
set result=%result:\"="%
rem remove any quotes
set result=%result:"=%
endlocal & set result=%result%
goto :eof