Wednesday, March 19, 2008

Compile DPK files using DCC32

Using Delphi command lines compiler DCC32 to compile programs allow us to set some switches to get various kind of output. The most common usage is compile a debugged or non-debugged release. For example, we may use dcc32 -B -$O-,D+,L+,YD to compile dcu/dcp with debug information and dcc32 -B -$O-,D-,L-,Y- to compile dcu/dcp without debug information. However, you may notice that using such switches has no effect on DPK (Delphi package) files. The reason is simple but yet difficult to discover. If you study DPK files, you may see the following:
package MyPackage;

{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION OFF}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DESCRIPTION 'E Stream Software - Runtime Library'}
{$LIBPREFIX 'SQL.'}
{$RUNONLY}
{$IMPLICITBUILD OFF}
{$DEFINE DEBUG}
The DPK files already has built in compiler directives. It has priority over the switches you specified in command lines. As stated by Allen Bauer in the post:
The IDE supports a very little know feature where you can continue to control these options while in the IDE, yet allow the command-line be able to also control the options. In a package file, all the options listed there are propagated to all the contained units. This is different than a normal .dpr program/library file. In order to do this, all you need to do is replace the '$' with a ' ' (that's a ). Then when you open the dpk in the IDE, you can toggle those options as much as you like, but when you compile on the command-line, you can still control them. This is how we build all our packages for the product itself. All of the Borland built packages have DEBUGINFO, STACKFRAMES, ASSERTIONS, OPTIMIZATION, LOCALSYMBOLS setup this way because like you, we wan't to be able to control those options from the command-line. The developers will typically do a complete "debug" build, whereas the integration team will not. $DEFINES will sort of work the same way, in that if you replace the '$' with a ' ', the IDE will still recognize the defines and apply them, but any other modifications to the dpk source will re-insert the '$' character.
The solution is replace $ with a space character (' ') for all compiler directives only:
package MyPackage;

{$R *.res}
{$ALIGN 8}
{ ASSERTIONS ON}
{ BOOLEVAL OFF}
{ DEBUGINFO ON}
{ EXTENDEDSYNTAX ON}
{ IMPORTEDDATA ON}
{ IOCHECKS ON}
{ LOCALSYMBOLS ON}
{ LONGSTRINGS ON}
{ OPENSTRINGS ON}
{ OPTIMIZATION OFF}
{ OVERFLOWCHECKS OFF}
{ RANGECHECKS OFF}
{ REFERENCEINFO ON}
{ SAFEDIVIDE OFF}
{ STACKFRAMES OFF}
{ TYPEDADDRESS OFF}
{ VARSTRINGCHECKS ON}
{ WRITEABLECONST OFF}
{ MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DESCRIPTION 'E Stream Software - Runtime Library'}
{$LIBPREFIX 'SQL.'}
{$RUNONLY}
{$IMPLICITBUILD OFF}
{$DEFINE DEBUG}
By doing so, both the IDE and DCC32 will respect the switches and yield expected results and. The output files (DLL, BPL or EXE) do not contain any debugging information even if you turn on the debugging switches. Those debugging information are stored in DCU or DCP files. To keep the debug information in DLL, BPL or EXE, turn on the Include TD32 debug info or use -V switch with DCC32.

Saturday, March 08, 2008

Retrieve Shell Folders

Windows has some special folders like My Documents, SendTo, Desktop or Favorites. The location of this folders are keep in the following registry key: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders However, it is not encourage to retrieve the location of special folders using registry. The recommended way to retrieve is via windows API function SHGetFolderPath:
uses SHFolder;

var sDir: string;
begin
  SetLength(sDir, MAX_PATH);
  ZeroMemory(@sDir[1], MAX_PATH);
  if Succeeded(SHGetFolderPath(0, CSIDL_PERSONAL, 0, 0, PAnsiChar(sDir))) then
    ShowMessage(sDir);
end;

Tuesday, March 04, 2008

Beware of XML Content contain CRLF always cause problem with Google API

We may consume the Google API to post entry of event kind. Those entry are mostly encoded in XML content. It is a common habit for windows user to create the XML content in windows notepad. If we pass the XML content from notepad directly to the Google API, we will mostly encounter error such as HTTP 400 Bad Request. The reason is simple but yet difficult to discover. CRLF (0x0D 0x0A) is default line separator in windows operating system. The Google API or perhaps the content type we specify in the HTTP request header is application/atom+xml may not recognize the CRLF and cause the HTTP 400 Bad request error. Always remember to well form the XML content before consume the API services. For example, replace all CRLF to a space character:
NewXMLText := StringReplace(XMLText, #13#10, ' ', [rfReplaceAll, rfIgnoreCase]);

Using Indy HTTPS client to consume Google API service

Indy VCL 10 distribution supports SSL via OpenSSL but it didn't bundle the DLL library. We have to download the Win32 OpenDDL from here. After install the Win32 OpenSSL library, there are 2 .DLL files we are interested may be found in %windir%\system32: libssl32.dll and libeay32.dll. However, the Indy Open SSL unit IdSSLOpenSSLHeaders.pas are coding as: SSL_DLL_name = 'ssleay32.dll'; {Do not localize} SSLCLIB_DLL_name = 'libeay32.dll'; {Do not localize} To make the Indy works with OpenSSL, we have to rename one of the DLL file libssl32.dll to ssleay32.dll. Here is a sample code using Indy VCL to authenticate with Google Calendar service:
var S: TStringList;
   M: TStream;
begin
 S := TStringList.Create;
 M := TMemoryStream.Create;
 try
   S.Values['Email'] := 'your google account';
   S.Values['Passwd'] := 'your password';
   S.Values['source'] := 'estream-sqloffice-1.1.1.1';
   S.Values['service'] := 'cl';

   IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
   IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
   IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', S, M);
   Memo1.Lines.Add(Format('Response Code: %d', [IdHTTP1.ResponseCode]));
   Memo1.Lines.Add(Format('Response Text: %s', [IdHTTP1.ResponseText]));

   M.Position := 0;
   S.LoadFromStream(M);
   Memo1.Lines.AddStrings(S);
 finally
   S.Free;
   M.Free;
 end;
end;