GExperts so far comes with a special stand alone executable GExpertsGrep that does nothing else but load the GExperts dll and call the entry point ShowGrep. Having this additional executable isn’t really necessary because Windows already comes with a tool that does exactly that: Load a DLL and call an entry point: rundll32.exe

rundll32 path\to\your.dll,EntryPoint additional parameters go here


In order for a dll to be called in this manner it needs to export an entry point with the following definition:

procedure EntryPoint(_HWnd: HWND; _HInstance: HINST; _CmdLine: PAnsiChar; CmdShow: integer); stdcall;


Note that the calling convention must be stdcall otherwise bad things happen.

Since Windows NT (so for all modern versions of Windows) there are two additional ways to declare the entry point:

procedure EntryPointA(_HWnd: HWND; _HInstance: HINST; _CmdLine: PAnsiChar; CmdShow: integer); stdcall;
procedure EntryPointW(_HWnd: HWND; _HInstance: HINST; _CmdLine: PWideChar; CmdShow: integer); stdcall;


Note that EntryPoint and EntryPointA are still assumed to have a PAnsiChar parameter, while EntryPointW is assumed to have a PWideChar parameter. A Dll only needs to export one of these entry points.

If RunDll32 is called with “EntryPoint”, it will look for “EntryPointW” first, “EntryPointA” second and “EntryPoint” last. Note that you don’t have to specify the “A” or “W” suffix, it is automatically added by RunDll32.

For a change, we even have an advantage when writing the DLL in Delphi rather than in MS Visual C++: Delphi does not do any name mangling, so the entry point will be called exactly what you write in the source code.

So, assuming the GExperts DLL exports the following procedure:

procedure ShowGrepEx(_HWnd: HWND; _HInstance: HINST; _CmdLine: PAnsiChar; CmdShow: integer); stdcall;


RunDll32 could be called like this:

rundll32 path\to\GExperts.dll,ShowGrepEx Some\Directory\here


The DLL would take the _CmdLine parameter, use it as the base directory for a directory search and show you the dialog as above. Pretty cool, eh?

We could even go a step further and write the following to the registry to add an entry to the context menu of each folder in Windows Explorer.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Folder\shell\GExpertsGrep]
"Icon"="path\\to\\GExperts.dll"
"MUIVerb"="GExperts Grep"

[HKEY_CLASSES_ROOT\Folder\shell\GExpertsGrep\Command]
@="\"c:\\windows\\system32\\rundll32.exe\" path\\to\\GExperts.dll,ShowGrepEx %1"


(Note: Don’t just copy that to a .reg file! You need to replace both instances of “path\\to\\GExperts.dll” with the actual absolute path to your GExperts DLL. And don’t forget that the current release does not even export the entry point ShowGrepEx at all.)

And this is what you’d get:

Unfortunately there is a drawback: RunDll32 must be given the correct path and file name of the DLL to load. That’s fine if you only have one GExperts version installed. The current GExpertsGrep stand alone executable searches for the first GExperts DLL in descending order, so it will always load the one for the latest Delphi version it can find. So I am not sure whether I want to drop the stand alone executable in favor of RunDLL32 right now.

Various people have reported an error message regarding rtl240.bpl not being found when calling the stand alone version of GExperts Grep. I could never reproduce it but my recent insight on SaveLoadLibrary now also solved that one:

The stand alone version of GExperts Grep tries to load any GExperts dll, regardless which version. The idea being that I don’t have to maintain multiple versions of the executable just to load the dll which does all the work anyway. So it tries to load the dlls in descending order, starting with the latest Delphi 10.1 (Berlin) one (I added 10.2 yesterday). But since GExperts is an IDE expert and therefore needs to use the rtl and various other packages, it of course requires them even if it is being used stand alone. And if you for whatever reason have GExperts installed for a Delphi version which you don’t have installed (or for which the packages are not in the search path), it fails to load and the error dialog described in the linked article was displayed.

It no longer does, thanks to David Heffernan’s hint.

There is no new release yet, not sure when I will have one ready.

Jeroen Wiert Pluimers has notified me in his comment on Google+ that the RSS feed for GExperts related posts doesn’t work. I apparently forgot a forward slash in the url:

correct:

http://blog.dummzeuch.de/category/gexperts/feed/

wrong:
http://blog.dummzeuch.de/category/gexpertsfeed/

It’s fixed now, sorry about that.

I have been asked to provide GExperts for Delphi 10.2 Tokyo, multiple times, via various channels.

Apparently not everybody subscribed to the GExperts Community on Google+, where I posted this on 2017-04-01:

No April fools’ joke: GExperts 1.38 experimental 2017-04-01 for Delphi 10.2.

This version should solve several bugs that resulted in Access Violations in the IDE or made the IDE hang.

Again: This is not for the faint of heart. It is barely tested. Download only if you promise not to blame me for the work you lost.

Please report any bugs on sourceforge https://sourceforge.net/p/gexperts/bugs/

There seem to be multiple download links on the web for GExperts for Delphi 10.2 Tokyo that want to charge for a “VIP membership”. These have not been authorized by me at all.

To those who already downloaded it back then: This is still the same version. I have started working on a major change in the way dialog changes and bugfixes are handled internally, which currently is a mess. That change is far from finished, so it doesn’t make much sense to build a new installer right now.

Two weeks ago I added the Donations for GExperts page. A few days later I received the first money through PayPal. Thanks for that!

I have now used that credit for donations to other open source software that I use daily:

I had always wanted to donate to those projects but never came around actually doing that. So, having some ready to use credits on PayPal made me finally do it.

If you want to say thank you for my work on GExperts, there are the following options:

Contribute to the GExperts quality
You can either contribute good bug reports or – even better – bug fixes to GExperts. I’d actually prefer that over getting money. Good bug reports include the Delphi version (and language), the Windows version, the GExperts version, the settings used and steps to reliably reproduce the problem. Screen shots might also help. Use the bug report form on Sourceforge for that.

You can do even more by taking over the maintenance of GExperts for a particular Delphi version. I am currently using Delphi 2007 for most of my work, with a bit of Delphi XE2 and the latest version sprinkled in. So most of the other versions don’t really get tested. If you regularly use e.g. Delphi 6, 7, 2005, 2006, 2009 or 2010, why not take over testing and possibly bug fixing GExperts for these versions? For now, I still have working installations of these Delphi versions, but I am not sure that I will go to much effort to keep them working in the future. If one of them breaks, I might decide to drop GExperts support for them. By taking on the responsibility for them, you ensure they will still be around in a few years.

Contribute new features to GExperts
Contributing features to GExperts involves more than just suggestions (even though I am open to these too, please use the feature requests form page on SourceForge for that.)

It’s also not only about implementing them and sending me patches. It’s about thinking about how to integrate them into the existing code and UI. If you want to contribute code, please feel free to contact me.

Donate money
If you can’t do any of the above, you can donate money. I suggest the following amounts:

• 50 Euros per year for private use via or
• 100 Euros per developer per year for commercial use via or

It’s up to you of course. If you think it’s worth more, I definitely won’t complain, but if you can’t afford these, smaller amounts are also OK.

A while ago I stopped using the formatter branch and officially took over the trunk of the GExperts repository. When I announced that on G+, Stefan Glienke asked me about my plans for GExperts.

My answer today is still the same as back then:

I will release new GExperts versions when I feel like it. And I will continue to call them experimental. There will be no rigorous testing, since nobody has volunteered to take on that responsibility. I will be using the latest development version in my daily work, so for now you can assume that at least the Delphi 2007 and XE2 versions are reasonably stable. This will change once I move on to newer versions, but unfortunately I don’t see that in the near future.

So, if you don’t like this, I would welcome volunteers to step forward and assume the responsibility of testing GExperts preferably with the Delphi versions they use every day. Your reward will be a place in the contributor list and possibly the pain of working with a not quite stable IDE.

Some people have started pestering me about making a new release (you know how you are!). Don’t think that this has done anything to actually make me do it, I simply thought it to be the right time with Delphi 10.2 Tokyo apparently right around the corner.

I have created installers for all supported Delphi versions.

I don’t remember all the new features and bug fixes that went into this release, but I am pretty sure it is more stable and has more features than the previous one.

But anyway:

Please be aware that I mostly work with Delphi 2007, so this version can be regarded as tested quite well, followed by Delphi XE2. The others are only known to compile and new features are usually tested superficially with all versions. This is particularly true for Delphi 6/7 and 2005/2006.

The GExperts Macro Library expert can now display and edit keyboard macros that have been recorded in the Delphi IDE.

It uses the information I described in my article on Interpreting Delphi IDE Keyboard Macros.

You get to this dialog via the context menu of a macro.

It allows you not only to display and edit text.

But also special keys.

And, on top of that, it works with Unicode, in Unicode aware IDEs (>= Delphi 2005).

(The above are Arabic characters, but I have no idea whether it means anything. I just typed a few keys.)

Just in case you are interested in these two keyboard macros:

Save these files anywhere and load them into the Macro Library expert.

They are supposed to generate a property for a class’ field: Copy the field definition from the private section to the public section. Position the cursor on the first character and start the macro.

Keyboard macros have been part of the Delphi IDE since basically forever (I remember using them in Delphi 5 but I wouldn’t rule it out that they already existed in Delphi 1 which I never used.)

GExperts also has had the Macro Library Expert since I know about it to overcome the Delphi IDE’s shortcoming of having only one keyboard macro.

This expert handles the macros as a black box, storing them in a binary stream which is used to get them from and pass them to the IDE and to save and load them from files.

So far I haven’t seen any documentation of the format these macros are stored in. That’s a real shame because it prevented the Macro Library Expert to become more useful, by allowing to edit existing macros.

I have had a look at data stored in its configuration files and tried to interpret them. Here is what I found:

A keyboard macro is a stream of bytes. It always seems to start with the four bytes 54 50 45 52 (ASCII “TPOR”, no idea what it means. TP looks suspiciously like Turbo Pascal). This seems to be a kind of signature. I have not tried to modify it to see what the IDE makes of it, I just want to understand the actual macros. This signature is followed by a single byte that is either 00 or 01. 00 seems to mark the end of the macro. 01 is followed by 4 bytes, which seem to belong together as two separate words, e.g. 61 00 00 00, and describe a key press. Each such 4 byte sequence is again followed by a single byte that is either 00 or 01. 00 again, marks the end of the macro, 01 means that another another key press follows etc.

54 50 54 52
01  61 00  00 00
00


would be a keyboard macro that simply types the lower case letter “a”.

54 50 54 52
01  61 00  00 00
01  62 00  00 00
00


would be “ab”.

Let’s have a closer look at the four bytes describing a key press:

The first word describes the main key, the second word describes modifier keys. The modifier word can have the following values:

• 00 00 – meaning the main key is a normal character
• 88 00 – meaning the main key is a special key like the arrow keys or insert / del / page up/down etc.
• 10 00 added to it means that the Ctrl key is being pressed
• 20 00 added to it means that the Shift key is being pressed
• 40 00 added to it means that the Alt key is being pressed

I have not seen any other values.

Please keep in mind that I represent the bytes in the order they show up in the stream. When interpreting two consecutive bytes as words, you have to switch them around according to Intel convention, so the bytes 10 00 actually mean the the number $0010, and 88 00 is the number$0088.

If the modifier word is $0000, the main key is in its simplest form the ASCII code of a character, e.g. like you can see in GExperts ASCII Chart: I have yet to check whether the value is actually a WideChar. update: It definitely is a WideChar, so the code below won’t work correctly for input that uses the high byte of the character. If the modifier value is <>$0000 it describes a special key, e.g.
$0020 is the Space key,$0021 is PgUp, $0022 is PgDn,$002D is Ins key, $002E is Del etc. update: The above applies to all Delphi versions that I checked. That includes Delphi 6, 7, 2005, 2006, 2007 and 10.1. Note that Delphi 6 and 7 apparently do not support Unicode characters in keyboard macros. I tried to enter some Arabic characters (Unicode$30xx) and they were converted to characters in $00xx range. Based on my observations I have so far written the following code to interpret a keyboard macro: type TMenuKeyCap = ( mkcBkSp, mkcTab, mkcEsc, mkcEnter, mkcSpace, mkcPgUp, mkcPgDn, mkcEnd, mkcHome, mkcLeft, mkcUp, mkcRight, mkcDown, mkcIns, mkcDel, mkcShift, mkcCtrl, mkcAlt); var // these resource strings are declared in unit Consts MenuKeyCaps: array[TMenuKeyCap] of string = ( SmkcBkSp, SmkcTab, SmkcEsc, SmkcEnter, SmkcSpace, SmkcPgUp, SmkcPgDn, SmkcEnd, SmkcHome, SmkcLeft, SmkcUp, SmkcRight, SmkcDown, SmkcIns, SmkcDel, SmkcShift, SmkcCtrl, SmkcAlt); function KeyCodeToText(Code: Word; Modifier: Word): string; var LoByte: Byte; begin Result := Format('unkown (%.4x)', [Code]); if (Modifier and$88) <> 0 then begin
// special keys
Result := '';
if (Modifier and $10) <> 0 then Result := Result + MenuKeyCaps[mkcCtrl]; if (Modifier and$40) <> 0 then
if (Modifier and $20) <> 0 then Result := Result + MenuKeyCaps[mkcAlt]; LoByte := (Code and$FF);
case LoByte of
// $00..$07
$08,$09: // backspace / tab
Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + LoByte - $08)];$0D: Result := Result + MenuKeyCaps[mkcEnter];
$1B: Result := Result + MenuKeyCaps[mkcEsc]; //$1B..$1F ?$20..$28: // space and various special characters Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcSpace) + LoByte -$20)];
// $29..$2C ?
$2D..$2E: // Ins, Del
Result := Result + MenuKeyCaps[TMenuKeyCap(Ord(mkcIns) + LoByte - $2D)]; //$2F ?
$30..$39: // 0..9
Result := Result + Chr(LoByte - $30 + Ord('0'));$41..$5A: // A..Z Result := Result + Chr(LoByte -$41 + Ord('A'));
$60..$69: Result := Result + Chr(LoByte - $60 + Ord('0'));$70..$87: Result := Result + 'F' + IntToStr(LoByte -$6F);
end;
end else begin
LoByte := (Code and $FF); case LoByte of //$00..$07$08, $09: // backspace / tab Result := MenuKeyCaps[TMenuKeyCap(Ord(mkcBkSp) + LoByte -$08)];
$0D: Result := MenuKeyCaps[mkcEnter];$1B: Result := MenuKeyCaps[mkcEsc];
// $1B..$1F ?
$20..$7E: Result := Chr(LoByte);
$7F: Result := MenuKeyCaps[mkcDel];$80..$FF: Result := Chr(LoByte); end; end; end; function TMacroInfo.TryDecode(AStrings: TStrings): Boolean; function Read(var Buffer; Count: Longint): Boolean; begin Result := (FStream.Read(Buffer, Count) = Count); end; var Magic: Longword; Flag: Byte; HiWord: Word; LoWord: Word; s: string; begin Result := False; FStream.Position := 0; if not Read(Magic, SizeOf(Magic)) or (Magic <>$524F5054) then
Exit;
Exit;
while Flag = \$01 do begin
Exit;
Exit;
s := KeyCodeToText(LoWord, HiWord);
Exit;
end;
Result := True;
end;


It’s not finished and might never be. It’s also far from perfect. I am posting this here in the hope that it might be useful to somebody. I might use it to extend the Macro Library Expert in GExperts to allow limited editing of keyboard macros.