Showing posts with label Delphi. Show all posts
Showing posts with label Delphi. Show all posts

Monday, October 27, 2014

Strip Invalid Identifier Characters

I don't do much Delphi anymore (any really) but this has been hanging around in my inbox for a while so I cleaned it up. This is a simple function that is a modification of IsValidIdent to strip invalid characters from a string.

function StripInvalidCharacters(const Ident: string): string;

  function Alpha(C: Char): Boolean; inline;
  begin
    Result := TCharacter.IsLetter(C) or (C = '_');
  end;

  function AlphaNumeric(C: Char): Boolean; inline;
  begin
    Result := TCharacter.IsLetterOrDigit(C) or (C = '_');
  end;

var
  I, L: Integer;
begin
  L := Length(Ident);
  if (L > 0) and Alpha(Ident[1]) then
    Result := Ident[1];

  for I := 2 to L do
    if AlphaNumeric(Ident[I]) then

  Result := Result + Ident[I];
end;

var
  LocalVal: string;
  LocalHash: Integer;
begin
  if IsValidIdent(Value) then
  LocalVal := StripInvalidCharacters(Value)
else

Wednesday, December 21, 2011

Thom on Windows 8 using Delphi

Thom has some great preliminary information on Windows 8 using Delphi. Check out his blog here

p.s. COM will never die.

Tuesday, December 13, 2011

Delphi Mac file system helper functions

I still have a few Delphi posts in the queue that I've been going through and cleaning up. Now that Delphi Mac support has shipped, there were a couple RTL functions that I wrote that may be useful to some people out there interacting with POSIX.

StringToFileSystemString (Mac only)
FileSystemStringToString (Mac only)

Mac stores file names internally as decomposed UTF-8 strings. This means that the diacritic will be a separate character after the character the diacritic is applied to. This is different than other POSIX platforms or Windows. Because of this all strings that are returned from file system functions need to be translated to a Delphi UnicodeString for use with the rest of the application. StringToFileSystemString and FileSystemStringToString will convert between a UnicodeString and a UTF-8 decomposed string.

Friday, November 18, 2011

Delphi Post Build Process

I was just going through the many posts that I haven't posted for various reasons and here was one that I found that someone may find interesting concerning Delphi XE but it should apply equally to Delphi XE2.

Since the beginning the EXE was build in the project directory right next to the .dpr. Now it lives in [Project Name]\Debug\Win32\[Project Name].exe which causes problems with my muscle memory at times so I've added a post build process to create a symlink.

Wednesday, January 5, 2011

Delphi Box2D

Last month I was looking at the physics engine Box2D and ran across a Delphi port of it you can find here. Overall it is pretty cool to have the ability to link it right in to your app. One major problem however is performance. The exact same app written in C takes hardly any processor cycles while the Delphi version takes quite a bit more. I don't have any scientific tests or any explanations of the cause, but I look forward to watching this open source project mature!

Wednesday, December 1, 2010

New Delphi RTL symlink functions

Symbolic links (symlinks) have been around on *nix for some time but only recently introduced to Windows in Vista with CreateSymbolicLink. Windows 2000 had introduced junction points which is half way there by allowing a link to directory, also known as a soft link (if the file moves the link points nowhere) but does not allow a link to a file.

Windows has a few idiosyncrasies that everyone should be made aware of. Here is a link to Microsoft symlink documentation but there are a few key differences between Windows and *nix. Symlinks in *nix are simply a file with a link target. The target file can be either a directory or a file and could actually change. Windows on the other hand has symlinks to files and symlinks to directories. They are different and cannot be interchanged as they can with *nix.

In the Delphi RTL the goal was to do what made the most sense for the developer. In most cases this is treating the symlink as a file but in other cases as a directory. The only real problem comes with POSIX when checking if a DirectoryExists and trying to find out information on the symlink. The following are notes that I made of the changes to the Delphi RTL functions in SysUtils.pas and IOUtils.pas.

IsRelativePath\TDirectory.IsRelativePath (new)

IsRelativePath returns a boolean value that indicates whether the specified
path is a relative path.


FileCreateSymLink\TFile.CreateSymLink (new, Windows Vista and above)

FileCreateSymLink creates a symbolic link. The parameter Link is the name of
the symbolic link created and Target is the string contained in the symbolic
link. On Windows the target directory must exist at the time of calling FileCreateSymLink.


FileGetSymLinkTarget\TFile.GetSymLinkTarget (new, Windows Vista and above)

FileGetSymLinkTarget reads the contents of a symbolic link. The result is
returned in the symbolic link record given by SymLinkRec.

Note: The access rights of symlinks are unpredictable over network drives. It is
therefore not recommended to create symlinks over a network drive. To enable
remote access of symlinks under Windows Vista and Windows 7 use the command:
"fsutil behavior set SymlinkEvaluation R2R:1 R2L:1"


FileGetSymLinkTarget\TFile.GetSymLinkTarget (new, Windows Vista and above) overload that takes a string

FileGetSymLinkTarget returns the target of a symbolic link.


FileAge

FileAge retrieves the date-and-time stamp of the specified file as a
TDateTime. This version supports all valid NTFS date-and-time stamps
and returns a boolean value that indicates whether the specified
file exists. If the specified file is a symlink the function is performed on
the target file. If FollowLink is false then the date-and-time of the
symlink file is returned.


FileExists

FileExists returns a boolean value that indicates whether the specified
file exists. If the specified file is a symlink the function is performed on
the target file. If FollowLink is false then the symlink file is used
regardless if the link is broken.

files

case 1: file.txt FollowLink = true returns true
case 2: file.txt FollowLink = false returns true
case 3: [does not exist] FollowLink = true returns false
case 4: [does not exist] FollowLink = false returns false

symlink to file

case 5: link.txt -> file.txt FollowLink = true returns true
case 6: link.txt -> file.txt FollowLink = false returns true
case 7: link.txt -> [does not exist] FollowLink = true returns false
case 8: link.txt -> [does not exist] FollowLink = false returns true

directories

case 9: dir FollowLink = true returns false
case 10: dir FollowLink = false returns false

symlink to directories

case 11: link -> dir FollowLink = true returns false
case 12: link -> dir FollowLink = false returns true
case 13: link -> [does not exist] FollowLink = true returns false
case 14: link -> [does not exist] FollowLink = false returns true


DirectoryExists

DirectoryExists returns a boolean value that indicates whether the
specified directory exists (and is actually a directory). If the specified
file is a symlink the function is performed on the target file. If FollowLink
is false then the symlink file is used. If the link is broken DirectoryExists
will always return false.

Notes:
On Windows there are directory symlinks and file symlinks. On POSIX a symlink is created
as a file that just happens to point to a directory.

directories

case 1: dir FollowLink = true returns true
case 2: dir FollowLink = false returns true
case 3: [does not exist] FollowLink = true returns false
case 4: [does not exist] FollowLink = false returns false

symlink to directories

case 5: link -> dir FollowLink = true returns true
case 6: link -> dir FollowLink = false returns true
case 7: link -> [does not exist] FollowLink = true returns false
case 8: link -> [does not exist] FollowLink = false returns true

files

case 9: file.txt FollowLink = true returns false
case 10 file.txt FollowLink = false returns false
case 11: link -> file.txt FollowLink = true returns false
case 12: link -> file.txt FollowLink = false returns false
case 13: file -> [does not exist] FollowLink = true returns false
case 14: file -> [does not exist] FollowLink = false returns true


FileGetAttr

FileGetAttr returns the file attributes of the file given by FileName. The
attributes can be examined by AND-ing with the faXXXX constants defined
above. A return value of -1 indicates that an error occurred. If the
specified file is a symlink then the function is performed on the target file.
If FollowLink is false then the symlink file is used.


FileSetAttr (Windows only)

FileSetAttr sets the file attributes of the file given by FileName to the
value given by Attr. The attribute value is formed by OR-ing the
appropriate faXXXX constants. The return value is zero if the function was
successful. Otherwise the return value is a system error code. If the
specified file is a symlink then the function is performed on the target file.
If FollowLink is false then the symlink file is used.

Note: It is suggested to use TFile.SetAttributes because it is cross platform.


DeleteFile

DeleteFile deletes the file given by FileName. The return value is True if
the file was successfully deleted, or False if an error occurred. DeleteFile
can delete a symlinks and symlinks to directories.


RemoveDir

RemoveDir deletes an existing empty directory. The return value is
True if the directory was successfully deleted, or False if an error
occurred. If the given directory is a symlink to a directory then the
symlink is deleted. On Windows the link can be broken and the symlink
can still be verified to be a symlink.


FileIsReadOnly

FileIsReadOnly tests whether a given file is read-only for the current
process and effective user id. If the file does not exist, the
function returns False. (Check FileExists before calling FileIsReadOnly)
This function is platform portable. If the file specified is a symlink
then the function is performed on the target file.


FileSetReadOnly

FileSetReadOnly sets the read only state of a file. The file must
exist and the current effective user id must be the owner of the file.
On Unix systems, FileSetReadOnly attempts to set or remove
all three (user, group, and other) write permissions on the file.
If you want to grant partial permissions (writeable for owner but not
for others), use platform specific functions such as chmod.
The function returns True if the file was successfully modified,
False if there was an error. This function is platform portable. If the
specified file is a symlink then the function is performed on the target
file.


FileSetDate

FileSetDate sets the OS date-and-time stamp of the file given by FileName
to the value given by Age. The DateTimeToFileDate function can be used to
convert a TDateTime value to an OS date-and-time stamp. The return value
is zero if the function was successful. Otherwise the return value is a
system error code. If the specified file is a symlink then the function is
performed on the target file. If FollowLink is false then the symlink file
is used.


RenameFile

RenameFile renames the file given by OldName to the name given by NewName.
The return value is True if the file was successfully renamed, or False if
an error occurred. If the file specified is a symlink then the function is
performed on the symlink.


FileGetDateTime (new)

Returns all file times associated with a file. If the specified file is a
symlink then the function is performed on the target file. If FollowLink is
false then the symlink file is used.


TSearchRec = record (updated)

Added TimeStamp property


TSymLinkRec = record (new)


TDirectory.Exists

Added FollowLink parameter


TDirectory.GetAttributes

Added FollowLink parameter


TPath.GetAttributes

Added FollowLink parameter


TFile.Exists

Added FollowLink parameter


TFile.GetAttributes

Added FollowLink parameter


FileGetDateTime (new)

Returns the TDateTime of the file's creation time, last access time and last write time.


FileGetDateTimeInfo

FileGetDateTimeInfo returns the date-and-time stamp of the specified file
and supports all valid NTFS date-and-time stamps. A boolena value is returned
indicating whether the specified file exists. If the specified file is a
symlink the function is performed on the target file. If FollowLink is false
then the date-and-time of the symlink file is returned.


TDateTimeInfoRec = record

Used by FileGetDateTimeInfo to return the various date times on demand. It is a public record but only consumable.

Tuesday, November 30, 2010

New Property - TBitButton.StylusHotImageIndex

TBitButton.StylusHotImageIndex is a new property in Delphi XE. -1 is the default and it will no longer pulsate. Choose an image index to pulsate to. If you want the old behavior which was a bug then add a blank image to the image list and choose that. Click here for the official documentation.

Thursday, November 18, 2010

RAD Studio XE Examples on Sourceforge

New to XE is all demos on on Sourceforge.

http://sourceforge.net/projects/radstudiodemos/

You can also update any demo to the latest from within the IDE by opening the project and updating using the SVN integration.

Monday, November 8, 2010

Suggestion Box: Implementing Custom Gestures

"Today I try to implement custom Gesture on my application. I try to create the form that the user can customize Gesture for "Close" action at runtime, I don't know how to do this. Two week before I designed "C" gesture for "Close" action but the user want to design it himself.
Thank you.
-paracet-"


Great question. Go watch Hands-On: Gestures in the VCL by Seppy Bloom. That should answer all the questions you have about creating and using custom gestures in your applications.

Friday, November 5, 2010

TDirect2DCanvas Performance

A question about the performance of TDirect2DCanvas was brought to my attention on Stack Overflow the other day and I decided to answer it. Check it out here. Hopefully this solves some of the mystery around the performance issues of Direct2D. Direct2D really is cool.

Tuesday, October 26, 2010

Turbo Pascal 5.5 on iPhone 4

Apple just approved the DOS emulator iDOS and we were interested to see if some of the old DOS versions of Turbo Pascal from the Borland museum worked. So rotate your head 90 degrees to the right and take a look below (image screen capture straight from iPhone 4 without modification):


Everything except the debugger appears to be working. Without the debugger it'd be a little difficult to use.

Friday, August 13, 2010

Sneak Peek - COM Registration

The menu items Run | Register ActiveX Server and Run | Unregister ActiveX Server have been changed. There is now one menu item under the Run menu called ActiveX Server with three sub menu items:

Register
Register for Current User
Unregister

Register for Current User will do just as it says. Handy if your IDE is installed on a UAC system or you need to test in an environment that will be running under UAC. The entire COM registration system has been adapted to account for things registered for current users. This means if you register something for current user then go to import it it will show up in the list.

Another handy feature is the above menu items handle local servers. No longer do you have to set the run parameters with /regserver.

tregsvr.exe now has the parameter -s for "Register or unregister for current user".

Thursday, August 12, 2010

More Sneak Peek

Earlier today I posted a list of some of the features new to Delphi XE and C++Builder XE. One oldie by goodie feature is the Install Component wizard:

Sneak Peeks of Delphi XE and C++Builder XE

So it's sneak peek time and there's lots to talk about. I'll post as much as I can over the next few days when I get a chance so things are probably gonna be posted sporadically like now where I'm between builds.

The first obvious thing is the name. Yeah, the name changed.

Oh, almost forgot, I'm also supposed to link to RAD Studio XE Preview and mention that I have been given permission to discuss or show new features of a pre-release version by Embarcadero Technologies.

So the features, because that's what everyone is interested in, right?

SVN version control is now integrated into the IDE. This really is more exciting that it sounds and here's why. ALL the source to it is public! If you use Tortoise or CallabNet then keep using them, just know that you can switch between them using whatever is the most useful. I still use use Tortoise and CallabNet, but I've started using the SVN IDE integration too.

Next we've got regular expressions. A lot of people are probably going to write about them.

There are a couple new RTL functions for creating symlinks that I'll talk about at a later time.

C++ had a major COM change. ATL was replaced by DAX which is pretty slick. You can still use ATL if you desire so more on this later.

COM registration now supports per user registration. I'll have a post on how this works later.

If you do COM then the Registered Type Libraries view is more useful than ever.

The DataSnap wizards have been given a huge facelift making them much easier to use.

That's all I got time for at the moment. Stay tuned for more.

Friday, June 25, 2010

Life before Interfaces

Way way back this is how IUnknown was defined before Delphi added the interface language feature:

IUnknown = class
public
{ IUnknown }
  function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall; abstract;
  function _AddRef: Integer; virtual; stdcall; abstract;
  function _Release: Integer; virtual; stdcall; abstract;
end;


Here's IUnknown now:
IUnknown = interface
['{00000000-0000-0000-C000-000000000046}']
  function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  function _AddRef: Integer; stdcall;
  function _Release: Integer; stdcall;
end;

There are so many advantages it's hard to list them all and now we just take them for granted, but here are a few advantages:

  • implement multiple interfaces on the same object
  • compiler support for lifetime management
  • assign an interface to a GUID

Monday, March 29, 2010

The Move

This past weekend Embarcadero moved us out of the old Borland building to a new office space a very short distance away. Something about saving some money or something. To my surprise this is what my office cube looked like when I arrived.


Nothing but an Asus EE Touch Top to work with plugged in and ready to go with awesome touch goodness. It didn't take long to realize this was a practical joke. Over the next couple hours my equipment started showing up and here is what my cube should have looked like.

Thursday, February 4, 2010

Hacking the TTouchKeyboard Part IV

Hacking the TTouchKeyboard Part I
Hacking the TTouchKeyboard Part II
Hacking the TTouchKeyboard Part III
Hacking the TTouchKeyboard Part IV
Hacking the TTouchKeyboard Part V (not published yet)

Here is a quick off the top of my head documentation for the XML layout that is used by the keyboard compiler. If something doesn't behave as expected drop me a line with a comment or email so I can get it fixed.


This describes the schema and the attributes for the keyboard layout XML file.

<keyboard keyboardtype="Standard" keyboardname="Standard101" width=768" height="228" maxwidth="0" maxheight="0" minwidth="0" minheight="0">

keyboardtype - this allows multiple keyboard layouts to contribute to an overall keyboard type. For instance, for Standard we have the Standard101, Standard102 and Standard 106 keyboard layouts. Each one specifies a different layout for the Standard keyboard layout depending on the keyboard's language. This becomes the value of the TTouchKeyboard.KeyboardLayout property.

keyboardname - this is only used for humans to distinguish which keyboard layout is being used.

width - this is the default width of the keyboard layout.

height - is the default height of the keyboard layout.

maxwidth - is the maxwidth constraint of the keyboard layout.

minwidth - is the minwidth constraint of the keyboard layout.

maxheight - is the maxheight constraint of the keyboard layout.

minheight - is the minheight constraint of the keyboard layout.


<language languagename="en" />

languagename - specifies the two letter language code for the languages that this keyboard layout applies. When the system is searching for a keyboard layout for the current keyboard language it starts from the last keyboard layout registered, to allow developers to override the defaults, once a keyboardtype and language are found that keyboard layout is used.


<row topmargin="0" bottommargin="2">

topmargin - this is the padding between the key rows.

bottommargin - this is the padding between the key rows.


<key scancode="1" vk="200" caption="Esc" width="65" stretch="True" modifiername="fn" imageindex="3" toggle="True" mapscancode="42" mapvk="0" margin="5" doubletall="True" groupindex="1" combokeys="vk:12;vk:12;scancode:24;scancode:24">

scancode - this is the Scan Code that is sent to the active window. The default value is 0. A valid scan code is greater than zero. Any value greater than zero will be sent as a key. If the value is 0 then it is key that does not have a scan code. Any key that does not have a scan code and or a vk is not a real key. If the value is less than 0 then it is also a fake key. This is useful for modifier keys.

vk - Same as scancode above only for the vk. It is important to note that either one or both can be provided. If one isn't provided then the other will be determined by the OS.

caption - this is the key caption to display.

imageindex - this is a image that is displayed. -1 is the default which means the key caption is text. tab=0, shift=1, enter=2, backspace=3, up=4, down=5, left=6, right=7.

width - this is how wide the key is. Since the keyboard is flow layout from the upper left hand following to the right and down it just gets the next available location. The width and height of the key will be the hit test area. The button will be drawn inside this region offset by the margins.

height - Set the height of the key. The only valid value here is double the height of the row. Only one tall key is valid per row and only on the right hand side.

leftmargin - is the margin to the left of the key.

rightmargin - is the margin to the right of the key.

stretch - in flow layout when resizing setting the stretch to True will tell the resize function to allow this key to take up all available space. Keys that are not stretch keys must have their appropriate proportions no matter the size of the keyboard while keys that are stretch keys will take up the available space.

toggle - if a key is a toggle key then it can be pressed and stay's down. A second press will release the key and draw it normally.

groupindex - toggle keys can be in a group. If two ore more keys are in the same group only one of them can be toggled at a time. When pressing a toggle key all the other toggle keys in the same group will be drawn normally while the newly pressed key will be pressed.

combokeys - there are times when it is handy to generate a key sequence. The format is vk:# or scancode:# separated by a semicolon. The first time the vk or scancode is encountered it will trigger a down, the second occurrence it will be an up.

mapscancode, mapvk - keys like the left and right shift keys or left and right ctrl keys have different scan codes and vks, so to show the same state on these keys one can provide an alternate vk or scancode values.

modifiername - this tells the keyboard that this is a modifier key. When this key is toggled and a key has a modifier attribute then the key can change it's caption, scancode, vk and imageindex.

publishedname - this is the name of the key caption override that is displayed in the object inspector under the CaptionOverrides property. This caption will override any and all captions for this key. Note that the developer can provide keyboard locale specific overrides programatically.


<language languagename="fr" scancode="100" vk="2" imageindex="1" caption="AltGr" />

languagename - specifies a language override for the key. If the keyboard language is fr then the scancode, vk, imageindex and caption will overridden with the values provided.


<modifier scancode="44" vk="0" scancode="20" vk="20" imageindex="2" caption="foo" modifiername="caps">

modifiername - specifies an override for the key. If the modifier is toggled then the scancode, vk, imageindex and caption will overridden with the values provided.


<language languagename="fr" scancode="100" vk="2" imageindex="1" caption="AltGr" />
</modifier>
</key>
</row> </keyboard>

Tuesday, February 2, 2010

Hacking the TTouchKeyboard Part III

Hacking the TTouchKeyboard Part I
Hacking the TTouchKeyboard Part II
Hacking the TTouchKeyboard Part III
Hacking the TTouchKeyboard Part IV
Hacking the TTouchKeyboard Part V (not published yet)

This post is going to be short because it is just an example application to illustrate how to convert an XML touch keyboard layout the binary version that you can then add as a resource and use from your touch applications. You can download the source to the command line utility called kcc (stands for Keyboard Command line Compiler).

My next post will be the documentation for the XML layout and the final post on hacking the TTouchKeyboard will walk you through the steps to make this all work.

Tuesday, January 5, 2010

Hacking the TTouchKeyboard Part II

Hacking the TTouchKeyboard Part I
Hacking the TTouchKeyboard Part II
Hacking the TTouchKeyboard Part III
Hacking the TTouchKeyboard Part IV
Hacking the TTouchKeyboard Part V (not published yet)

This post is going to be short because it is just an example application that you can download source code to illustrate how to access and save a touch keyboard layout as an XML file.

My next post will be an example application illustrating generate a resource file from the XML file so you can make your own keyboard layouts.

Monday, December 21, 2009

Hacking the TTouchKeyboard Part I

Hacking the TTouchKeyboard Part I
Hacking the TTouchKeyboard Part II
Hacking the TTouchKeyboard Part III
Hacking the TTouchKeyboard Part IV
Hacking the TTouchKeyboard Part V (not published yet)

The TTouchKeyboard is a a fairly flexible on screen keyboard that supports multiple keyboard layouts but only shipped with the number pad and the standard keyboard layout for a whole lot of input languages. How to create these keyboard layouts has been a mystery, until now. Over the next few posts I'll provide some tools, documentation and sample code to build everything you need to hack the TTouchKeyboard.

The first bit of code I'm going to provide demonstrates listing all the keyboard layouts that are available. It's pretty simple really. A keyboard layout is just a resource with the string "KEYBOARD" in the name.

procedure GetKeyboardLayoutNames(ANames: TStrings);

function EnumResNames(Module: HMODULE; ResType, ResName: PChar;
Nothing: Pointer): Integer; stdcall;
const
sResourceName = 'KEYBOARD';
begin
if Pos(sResourceName, ResName) > 0 then
LayoutNames.Add(ResName);
Result := 1;
end;

begin
LayoutNames := TStringList.Create;
try
EnumResourceNames(HInstance, RT_RCDATA, @EnumResNames, 0);
ANames.Clear;
ANames.AddStrings(LayoutNames);
finally
FreeAndNil(LayoutNames);
end;
end;


Once you have a layout you can load it by calling this little function.

function LoadLayout(const LayoutName: string): TVirtualKeyLayout;
var
Stream: TResourceStream;
TempStream: TStream;
begin
Result := nil;
Stream := TResourceStream.Create(HInstance, LayoutName, RT_RCDATA);
try
Result := TVirtualKeyLayout.Create;
Result.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;


Next I'll show how to iterate the data structures and save it to an XML file for easy text manipulation.