Tuesday, December 6. 2011
See my entry here on the ADUG shared blog.
Wednesday, August 24. 2011
TXMLDocument is an excellent VCL component for manipulating XML. But its not perfect, and there is a couple of “gotchas” that is worth noting. Let’s save you have a document structure like so …
CODE: <?xml version="1.0" encoding="UTF-8"?>
<MyApp>
<Options>
<Option Id="Align" Value="taRightJustify"/> <!-- Value is type TAlignment -->
<Option Id="Width" Value="16"/> <!-- Value is integer -->
</Options>
Here is how I would read the Align and Width values from the above ...
(read on)
Continue reading "Tricks and Traps with TXMLDocument"
Friday, December 3. 2010
So how do you pass strings to and from an Inno script to/from a DLL function or windows API call? I dont have all the answers, and I you have something to add, please leave a comment.
Caveat Emptor. Here, I am only concerned about the UNICODE version of the Inno install engine. Most every thing you see or read about Inno calling DLL's is in relation to the classic ansi version of Inno. The rules for the unicode version are a lot different and more difficult. My environment for this question is as follows. You milage may vary.
- Inno Unicode 5.4.0
- Graham Murt's Inno IDE (but that shouldn't matter)
- Compiler for the DLL - Delphi 2010 for win32 (but that shouldn't matter)
- Target o/s: Win 7 / 64-bit
Continue reading "Passing Strings between Inno Unicode and a DLL"
Sunday, November 14. 2010
What happens when you want your FinalBuilder 6 script to compile a C++ project? Problems -- that's what happens.
I am using FB 6.3.0.2172 with CodeGear C++Builder 2007 Enterprise R2 version 11.0.2902.10471.
The first thing that one would want to do with a scripted build is to be able to specify the output directory for the exe or dll product. One would expect to be able to do this in the "C++Builder 2006/7/8 Project" action easily. But no! It's such a basic fundamental need, that it is very surprising and dissapointing that FB cannot do this easily.
My solution is to fiddle with the MSBuild file just prior to compilation with an XPath action.
CODE: XML | Edit XML File | Edit XML | XML File | XML Source File = CHECKED
XML | Edit XML File | Edit XML | XML File | XML Source File | file = %WorkArea%\%CProject%.cbproj
XML | Edit XML File | Edit XML | Edit XML | XPath = /n:Project/n:PropertyGroup[@Condition="'$$(Cfg_1)'!=''"]/n:FinalOutputDir
XML | Edit XML File | Edit XML | Edit XML | NewValue = %OutputDir%
XML | Edit XML File | Edit XML | Edit XML | Treat new value as text = CHECKED
XML | Edit XML File | Edit XML | Edit XML | Preserve WhiteSpace = UNCHECKED
XML | Edit XML File | Edit XML | Edit XML | Insert new Value in CDATA = UNCHECKED
XML | Edit XML File | MXMLParser | XMLParser | Highest available = CHECKED
XML | Edit XML File | MXMLParser | XMLParser | XML Namespaces | Automatically use namespace prefixs declared in the document root node = CHECKED
XML | Edit XML File | MXMLParser | XMLParser | XML Namespaces | Identifier prefix for default namespace = n
Note: this only works when the C++ project is saved with Debug selected as the active configuration. You could do something similiar
and make it work on all configurations by adding a final node to provide a final override for the FinalOutputDir property, instead of editing the Cfg_1 one, but the problem with that is that the FB 6
action for creating a node with non-empty namespace is broken.
I think this problem has gone away with FB 7. (I dont have FB7 ) . But for those of you like me, still on FB 6, I hope this helps.
Tuesday, October 26. 2010
In Delphi, anonymous methods use a mysterious unseen object called the Call Frame object. May be you could also call it the enclosure. Then Anonomous Method reference is a method of this object.
Here is a bit of naughty hacker black magic to get a hold of the Call Frame object.
CODE: function FindCallFrame( const ReferenceToRoutine; var DataSize: integer): TInterfacedObject;
// Input:
// ReferenceToRoutine is a anonymous method reference
// (declared as a variable of 'reference to function/procedure').
// Assume ReferenceToRoutine is not nil.
// Output:
// Result is the Call Frame object (also known as the enclosure) of
// ReferenceToRoutine.
// DataSize is total size of the data members of the Call Frame object
// including 4 bytes for the RefCount and all the enclosed variables.
var
p: pointer;
Ok: boolean;
Ofs: integer;
Obj: TObject;
begin
{$IF CompilerVersion <> 21.00}
WRONG COMPILER! This code has only been validated for Delphi 2010
{$IFEND}
try
p := @ReferenceToRoutine;
Ofs := 256 - pbyte( pinteger( pinteger( p^)^ + $0C)^ + 2)^;
// Oh, The juicy naughtiness of the preceding line! So naughty!
Obj := TObject( integer( p^) - Ofs);
Ok := assigned( Obj) and (Obj is TInterfacedObject) and
(Obj.ClassParent = TInterfacedObject) and
(AnsiPos( '$ActRec', Obj.ClassName) > 0)
except
Ok := False
end;
if Ok then
begin
result := TInterfacedObject( Obj);
DataSize := Ofs - 8
end
else
begin
result := nil;
DataSize := 0
end
end;
Tuesday, October 12. 2010
Guess what my next private project is? ...
I want to develop an Android application, tentatively called "Happy Snapper".
Happy Snapper will be a camera, photo organiser and social media connector all in one.
The application will be written in Androidish Java for smart phones and tablets, and priced
at around USD$10.
Camera features will include:
- Use forward or reverse camera or both with one inset into a corner of the other;
- Integrate with other Camera apps.
Organiser features will include:
- Annotations for each photo/video. View/edit Name, notes, tags and privacy marks.
- Automatic geo-tagging, which can be disabled.
- Flickr like organisation into sets and collections.
- Flick or slide-show views of sets.
Social media connectivity features will include:
- Upload/download to any of
[**] Flickr
[**] Picassa
[**] Photo Bucket
[**] FaceBook
[**] Your PC (via BlueTooth)
- Publish to blog (a variety of blog engines supported)
- TweetPic
- YouTube, Viddler, Vimeo (for videos only)
- Send to another HappySnapper user (BlueTooth or Internet)
Wheh!
Hurray !!!
I have officially release TurboPower LockBox 3.0.0 It is in an Open Beta phase which
will continue until 1-Dec-2010.
Postive changes (not necessarily new features), include but are not limited to:
- Standards compliant implementation of AES (LB2 offered Rinjdael instead);
- Support for Delphi 2010 and Delphi 2007 (LB2 support Delphi 3 .. 7)
- Unicode support, in the Delphi 2010+ case.
- Additional block chaining modes: PCBC, CFB (block), CFB (8-bit), OFB & CTR.
- Convenient stream-mode interface to private key ciphers. In LB2 you had only a block-mode interface and you had to roll your own block padding solution.
- Automatic salting of block mode ciphers
- Automatic management of IV's for block mode ciphers
- RSA key length is unconstrainted and unlimited. LB2 was limited to 128, 256, 512, 768, 1024, which are cryptographically broken sizes.
- In LB2 RSA key generation was broken in more ways than the need for brevity allows me to write here. All that is fixed in LB3.
- Major speed improvements, especially in asymetric key generation.
- Cheap and easy extensibility for block chaining modes. (Not possible in LB2).
- Cheap and easy extensibility for block mode ciphers. (Not possible in LB2).
- Cheap and easy extensibility for hashes. (Not possible in LB2).
- White box visibility to D-Unit tests to provide quality assurance to developers
- Implementation and relevant standards share the same nomenclature, so that a reader can easily compare the two to gain confidence that there are no hidden back doors in the code.
- The LB3 code base is Object-Oriented (as opposed to LB2 which was procedural)
- DES and Triple DES
Check it out at http://lockbox.seanbdurkin.id.au/
Sunday, September 19. 2010
You want an efficient CRC rountine? Naturally, your first step is to google around and grab some-one elses pre-fab open source implementation. After all CRC is so basic, it must have been done a billion times before! Now here's the rub ...
There are lots of free CRC implementations, (written in C naturally), but EVERY friggen one them is written for a big-endien machine. They do NOT work on a little-endien machine. Curses!
So I had to develop my own implementation of CRC-DNP for little-endien. Isn't it just mind boggling, that something so basic, so common, was not already developed and published as open source? Or maybe I am just lousy at searching? Feel free to comment.
Any way, for the reader's benefit, please find my solution for an efficient CRC-DNP little-endien. Please feel free to copy it. I declare it all common domain and free of copyright restrictions.
CODE: var
crc_tabdnp_init: boolean = False;
crc_tabdnp: array[ 0..255 ] of uint16;
const
RP_DNP = $BCA6;
function Compute_CrcDnp_Xform( Datum: byte): word;
// Internal routine.
var
j: integer;
crc: uint16;
isSet: boolean;
begin
Crc := Datum shl 8;
for j := 0 to 7 do
begin
isSet := (Crc and $0100) <> 0;
Crc := ((Crc shr 1) or (Crc shl 15)) and $FF7F;
if isSet then
Crc := Crc xor RP_DNP
end;
result := Crc
end;
procedure Init_CrcDnp_Tab;
// Internal routine.
var
i: integer;
b: boolean;
begin
if crc_tabdnp_init then exit;
for i := 0 to 255 do
crc_tabdnp[ i] := Compute_CrcDnp_Xform( i);
crc_tabdnp_init := True
end;
function Crc_DNP( const Memory; Length: integer): uint16;
// Compute CRC-DNP of a block of memory of length bytes.
var
Iterator: PByte;
i: integer;
Datum: byte;
resultLo, resultHi: byte;
LookUp: word;
begin
Init_CrcDnp_Tab;
resultLo := $00;
resultHi := $00;
Iterator := @Memory;
for i := Length -1 downto 0 do
begin
Datum := Iterator^;
resultLo := resultLo xor Datum;
LookUp := crc_tabdnp[ resultLo];
resultLo := WordRec( LookUp).Hi xor resultHi;
resultHi := WordRec( LookUp).Lo;
Inc( Iterator)
end;
WordRec( result).Lo := resultLo;
WordRec( result).Hi := resultHi;
result := not result
end;
function Check_CRC_DNP( const Memory; Length: integer; CRC: uint16): boolean;
// Return True iff the CRC-DNP of a block of memory of length bytes matches the expected value.
begin
result := Crc_DNP( Memory, Length) = CRC
end;
function Check_Tail_CRC_DNP( const Memory; Length: integer): boolean;
// Assume a structure of a block of memory of length bytes (to which CRC applies), followed by the declared CRC value.
// Return True iff the CRC-DNP of a block of memory matches the declared value at the tail.
var
pntrCRC: ^uint16;
begin
pntrCRC := pointer( integer( Memory) + Length);
result := Check_CRC_DNP( Memory, Length, pntrCRC^)
end;
Sunday, March 28. 2010
Given a stream of characters, such as found in a generic text file,
what is the character set (windows code page based character set (improperly but commonly known as "ansi") or unicode)?
And given the character set, what encoding is it (eg: unicode could be UTF16-LE, UTF-8 or many other options)?
This article addresses those questions.
A nice starting point is the wikipedia article on byte order marks.
Next, here is an incomplete solution from Troy Wolbrink's TNT unicode controls package..
CODE: {*****************************************************************************}
{ }
{ Tnt Delphi Unicode Controls }
{ http://www.tntware.com/delphicontrols/unicode/ }
{ Version: 2.2.7 }
{ }
{ Copyright (c) 2002-2006, Troy Wolbrink (troy.wolbrink@tntware.com) }
{ }
{*****************************************************************************}
unit TntClasses
type
TTntStreamCharSet = (csAnsi, csUnicode, csUnicodeSwapped, csUtf8);
const
{ Each Unicode stream should begin with the code U+FEFF, }
{ which the standard defines as the <strong>byte order mark*. }
UNICODE_BOM = WideChar($FEFF);
UNICODE_BOM_SWAPPED = WideChar($FFFE);
UTF8_BOM = AnsiString(#$EF#$BB#$BF);
function AutoDetectCharacterSet(Stream: TStream): TTntStreamCharSet;
var
ByteOrderMark: WideChar;
BytesRead: Integer;
Utf8Test: array[0..2] of AnsiChar;
begin
// Byte Order Mark
ByteOrderMark := #0;
if (Stream.Size - Stream.Position) >= SizeOf(ByteOrderMark) then begin
BytesRead := Stream.Read(ByteOrderMark, SizeOf(ByteOrderMark));
if (ByteOrderMark <> UNICODE_BOM) and (ByteOrderMark <> UNICODE_BOM_SWAPPED) then begin
ByteOrderMark := #0;
Stream.Seek(-BytesRead, soFromCurrent);
if (Stream.Size - Stream.Position) >= Length(Utf8Test) </strong> SizeOf(AnsiChar) then begin
BytesRead := Stream.Read(Utf8Test[0], Length(Utf8Test) * SizeOf(AnsiChar));
if Utf8Test <> UTF8_BOM then
Stream.Seek(-BytesRead, soFromCurrent);
end;
end;
end;
// Test Byte Order Mark
if ByteOrderMark = UNICODE_BOM then
Result := csUnicode
else if ByteOrderMark = UNICODE_BOM_SWAPPED then
Result := csUnicodeSwapped
else if Utf8Test = UTF8_BOM then
Result := csUtf8
else
Result := csAnsi;
end;
In short, Troy's algorithm to detect the character set and encoding works like this:
-
Look for a BOM at the start of the stream. The BOM can take different encodings
so look for them in all possible encodings. Unfortunately, Troy only checks for
UTF-16LE, UTF-16-BE and UTF-8. But the same principle could be extended for other
encodings.
- If there was no BOM, then assume ansi (with some possibility of error). If it is ansi,
there is no way of determining the code page, just from looking at the content.
You would think that the Delphi VCL would have some similar functionality, but no it does
not. The closest to the mark would be the DetectUTF8Encoding function in unit WideStrUtils
(Delphi 2010). This function assumes that the text (contained in an ansistring variable
(UTF8String is just ansistring renamed)) is either US-ASCII or non-US-ASCII ansi or UTF8.
CODE: type TEncodeType = (etUSASCII, etUTF8, etANSI);
function DetectUTF8Encoding(const s : UTF8String): TEncodeType;
Basically, Embarcadero's algorithm goes like this:
- Ignore the possibility of a BOM.
- Scan the stream, byte by byte. If all the bytes are less than $80, then its US-ASCII
- Otherwise, scan the stream, as if it was UTF-8, codepoint by codepoint. If the length
of the content is consistent with the byte length allowed by the UTF-8 encoding then
it is assumed to be UTF-8. Quiet stupidly, invalid UTF-8 encodings are not made use of
to make a sensible decision on the encoding.
- Otherwise it is ansi
What would be a comprehensive character set and character encoding algorithm?
How about this idea:
- Like Troy's code, look for a BOM at the start of the stream. But unlike Troy's code,
extend the range of BOMs to all possible encodings. If a BOM is found, then exit.
- Iterate through all possible character sets and encodings from most instrinsically
likely to least intrinsically likely. For each encoding, parse the stream, encoded
code-point by code-point. Search for invalid code-points or truncated code-points.
When the first valid parse is found, then assume this encoding and exit.
As far as ansi is concerned, assume that the system code page applies.
For example:
- In the case of ansi text with a far-eastern code page, there will be a range of
valid lead bytes. Examine lead byte and test if it falls within the valid range
for the code page. See the VCL for an example of how to compute the set of valid
lead bytes.
- In the case of UTF-8, wikipedia gives an adequate
description of invalid encodings of code-points. For example $C1 can never be the
lead byte of a valid UTF-8 sequence, so if you find this, the content is not UTF-8.
Tuesday, March 2. 2010
Hey, I got a mention on Nick Hodges' blog:
|