I finished some more work on the new JNI Wrapper. It now has methods for all method calls and get/set methods for regular all static and non-static fields. You can download a copy here: http://www.ibeblog.com/files/JNIForDelphi%200.3.rar
Category: Programming
All posts relating to programming.
VB.NET: Client Window Selection
I wrote up a client window selection example for VB.NET, it uses a crosshair to let you select a window and calculates your mouse coordinates relative to the selected window. You can download the source here: http://ibeblog.com/files/WndSelExampleVB.rar
JNI Wrapper v0.2
I’ve managed to get a tiny bit of work done on the new JNI wrapper I was working on. It now allows you to get and set non-static fields. Also methods to call non-static object methods and static boolean, byte and char methods have been added. You can download a copy here: http://www.ibeblog.com/files/JNIForDelphi%200.2.rar
Parsing Pascal Language Syntax
On occasion people will want to parse the syntax of a programming language, to create manuals, syntax highlighters, etc… This however isn’t always easy to achieve. The easiest way to do this, is to use regular expressions. I’ve written up an expression to handle the syntax of the pascal language. In this expression I will be using capture groups for each part of the syntax, because of this, you will be able to determine what part of the syntax was matched by checking what group has a content. Note that the order of matching the syntax is very important.
(?:(//(?:(?!$).)*)|(\{\$[^\}]*\})|(\{(?:.*?\}|.*))|(\(\*(?:.*?\*\)|.*))|(‘(?:(?!$|’).)*’?)|(#\d+)|(\d+(?:\.\d+)?|\$[0-9A-F]+)|([A-Z_][A-Z0-9_]*))
Capture groups:
- 1: // One line comments
- 2: {$COMPILER DIRECTIVES}
- 3: {Multi line comments}
- 4: (*Multi line comments*)
- 5: ‘Strings’
- 6: Characters (#125)
- 7: Numbers (268 or 569.56)
- 8: Words (compare with list of keywords afterwards)
You can easily create similar regexes for other languages which work the same way. Note that you have to set the regex engine to have dot match newlines and match caseless for this regex to work.
C++: System-Wide Font Smoothing
The Win32 API contains a method called SystemParametersInfo, it is used to get and set system-wide parameters. In this case we will be using it to to get and set system-wide font smoothing. In the following piece of code, font smoothing is toggled and restored after 5 seconds.
#include <windows.h> int main(int argc, char* argv[]) { int i; SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &i, 0); SystemParametersInfo(SPI_SETFONTSMOOTHING, !i, 0, 0); Sleep(5000); SystemParametersInfo(SPI_SETFONTSMOOTHING, i, 0, 0); return 0; }
Delphi: Components
I am composing a list of free and/or open-source Delphi components, this will hopefully help people find them easier as there’s a great deal of them available on the internet. You can find the list here: http://ibeblog.com/?page_id=174
Delphi: Required package “rtl” not found
A while back I got this interesting error when Installing a package into a newly installed copy of Delphi 2010 saying “required package rtl not found”. I recall having had this problem before once when I had Delphi 2009 and 2010 installed at the same time, it just so happens that now I had 2010 and XE installed. Here’s an easy solution to the problem:
Add “$(BDS)\lib” and “$(BDSCOMMONDIR)\dcp” to your library path. This solution will probably only work for later versions of Delphi, though for all versions it should simply be a matter of adding your lib path and dcp output path to the library path.
In my case the cause of this problem is fairly simple. Whenever you install Delphi, the installer will set up all paths like the library path in the windows registry. However, if a path is already present, it will use that, since the user might just be reinstalling the IDE and wouldn’t want to loose their settings. Some component installers allow you to install the components for IDE’s that aren’t actually installed on your computer yet, they will append their library paths to the non-existing path for the IDE which means they will simply create it. This results in a library path that does not contain any of the paths to the standard VCL library and components included with Delphi.
A less delicate solution to this problem would be to just erase all registry content for the version of Delphi you’re trying to install and then install it. This will ensure you have all paths set up correctly, because the previous solution will only ensure VCL works correctly, not the components that come with the IDE like the Indy library. The registry values for the IDEs are located in HKEY_CURRENT_USER\Software\Borland for old releases, HKEY_CURRENT_USER\Software\CodeGear for all releases up to Delphi 2010 and HKEY_CURRENT_USER\Software\Embarcadero for XE and newer.
Delphi: FileSystem Searching
The windows api comes with several functions to search through files in a computer’s filesystem such as FindFirstFile, FindNextFile, … The RTL library in Delphi encapsulates these functions in 3 easy to use functions. These are FindFirst, FindNext and FindClose. Unlike what you may be used to from the win32 api, these functions return 0 not if something went wrong, but if they were successful. You have to pass the function a mask for the files, this consists out of a filemask to search in the current directory or a filemask including a folderpath. An example would be “c:\*”, this would return all files or folders in c:\ depending on what flags you pass to the FindFirst function. Note that the functions are not recursive, so they will only return the content of a folder and not it’s subfolders. The second parameter of the FindFirst function specifies flags that tell the functions what it is searching for. A few flags are faNormal, faHidden, faSysFile, faDirectory, … These can also be combined to select several types of files/directories. The following example shows you how to loop through all files and directories in the c:\ folder:
program FindTest; {$APPTYPE CONSOLE} uses SysUtils; var sRec: TSearchRec; begin if FindFirst('c:*', faNormal or faDirectory, sRec) = 0 then begin repeat WriteLn(sRec.Name + ' - ' + IntToStr(sRec.Size)); until FindNext(sRec) <> 0; FindClose(sRec); end; ReadLn; // Prevent window from closing end.
If you would for example change your mask to “c:\*.txt”, it would only return the txt files.
Some of you will also want to be able to recurse this, this can easily be achieved by setting it up in a recursive algoritm:
program FindTest; {$APPTYPE CONSOLE} uses SysUtils; procedure RecurseFileSystem(const Folder, Mask: string); var sRec: TSearchRec; t: string; begin t := IncludeTrailingPathDelimiter(Folder); if FindFirst(t + Mask, faNormal or faDirectory, sRec) = 0 then begin repeat if ((sRec.Attr and faDirectory) <> 0) and (sRec.Name <> '.') and (sRec.Name <> '..') then RecurseFileSystem(t + sRec.Name, Mask) else WriteLn(t + sRec.Name + ' - ' + IntToStr(sRec.Size)); until FindNext(sRec) <> 0; FindClose(sRec); end; end; begin RecurseFileSystem('c:', '*'); ReadLn; // Prevent window from closing end.
This example will loop through the entire filesystem on the c drive and show you all of the regular files including their path and size.
Delphi: Collections
Like Java and other languages, Delphi supports collections. This support was however only added after Delphi 7. You can find all of the included generic collection classes in the unit Generics.Collections. In this post I’ll show you 2 collectiontypes from the unit which are commonly used. You can use these in your applications to easily manage various types of data.
The TList class is an item collection which uses integer indices as keys for your collectionitems. The TList class allows you to perform any variety of actions on your collections such as inserting, deleting, moving and finding items. The TList class like all collection classes in the unit is a generic class, this means it’s not specific to a certain type, you have to bind it to a certain type while defining the instance of the collection. Any type can be used with these collections including basetypes. Here’s an example that stores and outputs integer values:
program CollectionsTest; {$APPTYPE CONSOLE} uses SysUtils, Generics.Collections; var Items: TList<Integer>; i, l: Integer; begin Items := TList<Integer>.Create; Items.Add(5); Items.Add(9); Items.Add(1); Items.Add(52); Items.Add(24); l := Items.Count; for i := 0 to l - 1 do WriteLn(Items[i]); Items.Free; ReadLn; // Prevent window from closing. end.
The next collection type I would like to discuss is the dictionary. The dictionary collection is the same as a hashmap in Java. It uses hashes and lineair probing to find items, You can use many different types as a key to bind to your data, the most commonly used type for keys are strings. Here’s an example:
program CollectionsTest; {$APPTYPE CONSOLE} uses SysUtils, Generics.Collections; var Items: TDictionary<string, string>; Enum: TDictionary<string, string>.TPairEnumerator; begin Items := TDictionary<string, string>.Create; Items.Add('first', 'item1'); Items.Add('second', 'item2'); Items.Add('third', 'item3'); Enum := Items.GetEnumerator; while Enum.MoveNext do WriteLn('Key: ' + Enum.Current.Key + ' - Value: ' + Enum.Current.Value); Items.Free; ReadLn; // Prevent window from closing. end.
As you can see in this last example, collections also allow you to use various enumerators to access their data in a convenient way. Also take note that the TDictionary type does not keep the stored data in the order it was entered.
Delphi: FastShareMem
FastShareMem is a very lightweight memory manager for Delphi that was designed to replace the default memory manager that required the inclusion of a dll with applications. However, due to the addition of unicode support to Delphi 2009/2010, the include is no longer compatible with dlls compiled with older versions of Delphi using it. So I’ve written a copy that that will work in Delphi 2009/2010 and still is compatible with the older versions. Due take in mind this version of the include isn’t written for older versions of Delphi. For an older version use the original you can find here: http://www.codexterity.com/fastsharemem.htm
unit FastShareMem; // By Frederic Hannes (http://ibeblog.com) // Based on FastShareMem by Emil M. Santos (http://www.codexterity.com) interface var GetAllocMemCount: function: Integer; GetAllocMemSize: function: Integer; implementation uses Windows; const ClassName = '_com.codexterity.fastsharemem.dataclass'; type TFastShareMem = record MemoryManager: TMemoryManagerEx; _GetAllocMemSize: function: Integer; _GetAllocMemCount: function: Integer; end; function _GetAllocMemCount: Integer; var State: TMemoryManagerState; i: Integer; begin GetMemoryManagerState(State); Result := 0; for i := 0 to High(State.SmallBlockTypeStates) do Inc(Result, State.SmallBlockTypeStates[i].AllocatedBlockCount); Inc(Result, State.AllocatedMediumBlockCount + State.AllocatedLargeBlockCount); end; function _GetAllocMemSize: Integer; var State: TMemoryManagerState; i: Integer; begin GetMemoryManagerState(State); Result := 0; for i := 0 to High(State.SmallBlockTypeStates) do Inc(Result, State.SmallBlockTypeStates[i].AllocatedBlockCount * State.SmallBlockTypeStates[i].UseableBlockSize); Inc(Result, State.TotalAllocatedMediumBlockSize + State.TotalAllocatedLargeBlockSize); end; var wc: TWndClassA; IsHost: Boolean; ShareMem: TFastShareMem; initialization if not GetClassInfoA(HInstance, ClassName, wc) then begin GetMemoryManager(ShareMem.MemoryManager); ShareMem._GetAllocMemCount := @_GetAllocMemCount; ShareMem._GetAllocMemSize := @_GetAllocMemSize; GetAllocMemCount := @_GetAllocMemCount; GetAllocMemSize := @_GetAllocMemSize; FillChar(wc, SizeOf(wc), 0); wc.lpszClassName := ClassName; wc.style := CS_GLOBALCLASS; wc.hInstance := HInstance; wc.lpfnWndProc := @ShareMem; if RegisterClassA(wc) = 0 then begin MessageBox(0, 'Shared Memory Allocator setup failed: Cannot register class.', 'FastShareMem', 0); Halt; end; IsHost := True; end else begin SetMemoryManager(TFastShareMem(wc.lpfnWndProc^).MemoryManager); GetAllocMemCount := TFastShareMem(wc.lpfnWndProc^)._GetAllocMemCount; GetAllocMemSize := TFastShareMem(wc.lpfnWndProc^)._GetAllocMemSize; IsHost := False; end; finalization if IsHost then UnregisterClassA(ClassName, HInstance); end.