Quantcast
Channel: Planet Object Pascal
Viewing all 1725 articles
Browse latest View live

Andy's Blog and Tools: How to install JCL and JVCL for XE4

$
0
0

As the JCL and JVCL transition from Sourceforge to GitHub isn’t finished yet (JCL already on GitHub, JVCL still on Sourceforge) you have to do some extra work to get a working XE4 version. There is no official release that you could download. Also the “jedi.inc” sub project on Sourceforge isn’t updated to XE4 but the new https://github.com/project-jedi/jedi is. Unfortunately, the JVCL has the old jedi.inc as an svn:external, causing the JVCL’s svn head to have an outdated jvcl/common/jedi/jedi.inc that must be replaced by the updated one.

Here is a example command line “script” that you can use to install the JCL and JVCL.

git clone git://github.com/project-jedi/jcl.git jcl
cd jcl
git submodule init
git submodule update
cd ..

svn co https://jvcl.svn.sourceforge.net/svnroot/jvcl/trunk/jvcl jvcl
copy /Y jcl\jcl\source\include\jedi\*.inc jvcl\common\jedi

cd jcl\jcl
install.bat

cd ..\..\jvcl
install.bat
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

 


The Wiert Corner - irregular stream of stuff: jpluimers

$
0
0

Every once in a while an AV in Delphi manifests itself in a very odd way.

Usually it is refactoring, code completion or any of the ‘insight’ features doing odd things.

This time, it got me into a Product or License Validation Error | General:

Product or License Validation Error

Your Embarcadero product or license can’t be validated.

If you see this page when you’re using a valid license and official version of the software, please submit an installation support case for further assistance.

If you need a trial license and official trial software download, visit our Trial Downloads page.

To purchase a product license and receive a certified download, please see our How to Buy page.

Too bad the error hard-quits Delphi, thereby loosing all your work since the last save. Even more reason to safe often.

None of the reasons mentioned in Starting Delphi or C++ Builder results in Product or License Validation Error applied.

Restarting Delphi XE2 solved the problem.

–jeroen


Filed under: Delphi, Delphi XE2, Development, Software Development

PARMAJA: New slogan for some open source projects

$
0
0

1- Keep the code as it.
2- Your patch is welcome, but maybe we will not accept it
3- Send your patch, and bless for it.
4- If you hate it dont use it.
5- Write once, Compile any where, is it work?

The road to Delphi: Hosting Preview Handlers in Delphi VCL Applications

$
0
0

In this post i will show you, how you can host an existing Preview Handler in your Delphi VCL App. Preview handlers are a lightweight and read-only preview of a file contents that are bound to a the preview pane window of the explorer or a another window, all this is done without launching the file’s associated application.

The Preview Handlers was introduced in Windows Vista and are used mainly by the Windows Explorer and other applications like MS Outlook. Hosting an existing preview handler in your application will able to display a preview of most major office document formats, media files, CAD files and so on.

To host a preview handler, first we need find the CLSID of the preview associated to a file extension, this info is located in the windows registry, the default value of the {8895b1c6-b41f-4c1c-a562-0d564250836f} subkey is the class identifier (CLSID) of the handler. An example of the extfile ProgID subkey is shown here, associating a handler of CLSID {11111111-2222-3333-4444-555555555555}.

HKEY_CLASSES_ROOT
   extfile
      shellex
         {8895b1c6-b41f-4c1c-a562-0d564250836f}
            (Default) = [REG_SZ] {11111111-2222-3333-4444-555555555555}

So you can wrote a method like this to get the CLSID of the preview handler associated to a file.

function GetPreviewHandlerCLSID(const AFileName: string): string;
var
  LRegistry: TRegistry;
  LKey: String;
begin
  LRegistry := TRegistry.Create();
  try
    LRegistry.RootKey := HKEY_CLASSES_ROOT;
    LKey := ExtractFileExt(AFileName) + '\shellex\{8895b1c6-b41f-4c1c-a562-0d564250836f}';
    if LRegistry.KeyExists(LKey) then
    begin
      LRegistry.OpenKeyReadOnly(LKey);
      Result:=LRegistry.ReadString('');
      LRegistry.CloseKey;
    end
    else
      Result := '';
  finally
    LRegistry.Free;
  end;
end;

Now with the proper CLSID we can create an instance the IPreviewHandler interface

var
    FPreviewHandler : IPreviewHandler;
begin
  ...
  ...
  FPreviewHandler := CreateComObject(LPreviewGUID) As IPreviewHandler;

The next step is determine how the preview handler was implemented using a IInitializeWithStream.Initialize, IInitializeWithFile, or IInitializeWithItem interface and then call the proper Initialize method.

  if FPreviewHandler.QueryInterface(IInitializeWithFile, LInitializeWithFile) = S_OK then
    LInitializeWithFile.Initialize(StringToOleStr(FFileName), STGM_READ)
  else
  if FPreviewHandler.QueryInterface(IInitializeWithStream, LInitializeWithStream) = S_OK then
  begin
    LFileStream := TFileStream.Create(FFileName, fmOpenRead);
    LIStream := TStreamAdapter.Create(LFileStream, soOwned) as IStream;
    LInitializeWithStream.Initialize(LIStream, STGM_READ);
  end
  else
  if FPreviewHandler.QueryInterface(IInitializeWithItem, LInitializeWithItem) = S_OK then
  begin
    SHCreateItemFromParsingName(PChar(FileName), nil, StringToGUID(GUID_ISHELLITEM), LShellItem);
    LInitializeWithItem.Initialize(LShellItem, 0);
  end;

Finally we need to call the SetWindow (passing the proper host window handle and TRect) and the DoPreview methods of the IPreviewHandler interface.

I encapsulate all the above code in a component called THostPreviewHandler and this is the source code.

{**************************************************************************************************}
{                                                                                                  }
{ Unit uHostPreview                                                                                }
{ component for host preview handlers                                                              }
{                                                                                                  }
{ The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
{ you may not use this file except in compliance with the License. You may obtain a copy of the    }
{ License at http://www.mozilla.org/MPL/                                                           }
{                                                                                                  }
{ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
{ ANY KIND, either express or implied. See the License for the specific language governing rights  }
{ and limitations under the License.                                                               }
{                                                                                                  }
{ The Original Code is uHostPreview.pas.                                                           }
{                                                                                                  }
{ The Initial Developer of the Original Code is Rodrigo Ruz V.   Copyright (C) 2013.               }
{ All Rights Reserved.                                                                             }
{                                                                                                  }
{**************************************************************************************************}

unit uHostPreview;

interface

uses
  ShlObj,
  Classes,
  Messages,
  Controls;

type
  THostPreviewHandler = class(TCustomControl)
  private
    FFileStream     : TFileStream;
    FPreviewGUIDStr : string;
    FFileName: string;
    FLoaded :Boolean;
    FPreviewHandler : IPreviewHandler;
    procedure SetFileName(const Value: string);
    procedure LoadPreviewHandler;
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  protected
    procedure Paint; override;
  public
    property FileName: string read FFileName write SetFileName;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;


implementation

uses
 SysUtils,
 Windows,
 Graphics,
 ComObj,
 ActiveX,
 Registry,
 PropSys;

constructor THostPreviewHandler.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FPreviewHandler:=nil;
  FPreviewGUIDStr:='';
  FFileStream:=nil;
end;

procedure THostPreviewHandler.Paint;
const
  Msg = 'No preview available.';
var
  lpRect: TRect;
begin
 if (FPreviewGUIDStr<>'') and (FPreviewHandler<>nil) and not FLoaded then
 begin
  FLoaded:=True;
  FPreviewHandler.DoPreview;
  FPreviewHandler.SetFocus;
 end
 else
 if FPreviewGUIDStr='' then
 begin
   lpRect:=Rect(0, 0, Self.Width, Self.Height);
   Canvas.Brush.Style :=bsClear;
   Canvas.Font.Color  :=clWindowText;
   DrawText(Canvas.Handle, PChar(Msg) ,Length(Msg), lpRect, DT_VCENTER or DT_CENTER or DT_SINGLELINE);
 end;
end;

destructor THostPreviewHandler.Destroy;
begin
  if (FPreviewHandler<>nil) then
    FPreviewHandler.Unload;

  if FFileStream<>nil then
    FFileStream.Free;

  inherited;
end;

function GetPreviewHandlerCLSID(const AFileName: string): string;
var
  LRegistry: TRegistry;
  LKey: String;
begin
  LRegistry := TRegistry.Create();
  try
    LRegistry.RootKey := HKEY_CLASSES_ROOT;
    LKey := ExtractFileExt(AFileName) + '\shellex\{8895b1c6-b41f-4c1c-a562-0d564250836f}';
    if LRegistry.KeyExists(LKey) then
    begin
      LRegistry.OpenKeyReadOnly(LKey);
      Result:=LRegistry.ReadString('');
      LRegistry.CloseKey;
    end
    else
      Result := '';
  finally
    LRegistry.Free;
  end;
end;

procedure THostPreviewHandler.LoadPreviewHandler;
const
  GUID_ISHELLITEM = '{43826d1e-e718-42ee-bc55-a1e261c37bfe}';
var
  prc                   : TRect;
  LPreviewGUID          : TGUID;
  LInitializeWithFile   : IInitializeWithFile;
  LInitializeWithStream : IInitializeWithStream;
  LInitializeWithItem   : IInitializeWithItem;
  LIStream              : IStream;
  LShellItem            : IShellItem;
begin

  FLoaded:=False;
  FPreviewGUIDStr:=GetPreviewHandlerCLSID(FFileName);
  if FPreviewGUIDStr='' then exit;

  if FFileStream<>nil then
    FFileStream.Free;

  LPreviewGUID:= StringToGUID(FPreviewGUIDStr);

  FPreviewHandler := CreateComObject(LPreviewGUID) As IPreviewHandler;
  if (FPreviewHandler = nil) then
    exit;

  if FPreviewHandler.QueryInterface(IInitializeWithFile, LInitializeWithFile) = S_OK then
    LInitializeWithFile.Initialize(StringToOleStr(FFileName), STGM_READ)
  else
  if FPreviewHandler.QueryInterface(IInitializeWithStream, LInitializeWithStream) = S_OK then
  begin
      FFileStream := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyNone);
      LIStream := TStreamAdapter.Create(FFileStream, soOwned) as IStream;
      LInitializeWithStream.Initialize(LIStream, STGM_READ);
  end
  else
  if FPreviewHandler.QueryInterface(IInitializeWithItem, LInitializeWithItem) = S_OK then
  begin
    SHCreateItemFromParsingName(PChar(FileName), nil, StringToGUID(GUID_ISHELLITEM), LShellItem);
    LInitializeWithItem.Initialize(LShellItem, 0);
  end
  else
  begin
    FPreviewHandler.Unload;
    FPreviewHandler:=nil;
    exit;
  end;

  prc := ClientRect;
  FPreviewHandler.SetWindow(Self.Handle, prc);
end;

procedure THostPreviewHandler.SetFileName(const Value: string);
begin
  FFileName := Value;
  HandleNeeded;
  LoadPreviewHandler;
end;

procedure THostPreviewHandler.WMSize(var Message: TWMSize);
var
  prc  : TRect;
begin
  inherited;
  if FPreviewHandler<>nil then
  begin
    prc := ClientRect;
    FPreviewHandler.SetRect(prc);
  end;
end;

end.

And you can use it in this way

  FPreview := THostPreviewHandler.Create(Self);
  FPreview.Top := 0;
  FPreview.Left := 0;
  FPreview.Width  := Panel1.ClientWidth;
  FPreview.Height := Panel1.ClientHeight;
  FPreview.Parent := Panel1;
  FPreview.Align  := alClient;
  FPreview.FileName:=FileName;

This is a sample image of a preview handler hosted in a VCL Application.

previewhost

You can download the sample application (XE2) from here.


Behind the connection: DWScript: OpenSource scripting engine for Delphi

$
0
0
Delphi Web Script (DWScript) is an OpenSource object-oriented scripting engine for Delphi based on Delphi language. Contrary to what its name suggest, DWScript is more of a general scripting engine than a web script engine. DWScript provides a few Delphi component you can use to add scripting to your own application. You can expose functions, variable, constants, classes, object instances and

The Wiert Corner - irregular stream of stuff: jpluimers

$
0
0

Wow, I didn’t know that IBM had their own DOS based Pascal compiler for the PC in 1981, but they did, and BitSavers just uploaded their manual
IBM_Pascal_Compiler_Aug81.pdf

Edit:
document moved from http://bitsavers.trailing-edge.com/pdf/ibm/pc/dos/IBM_Pascal_Compiler_Aug81.pdf
to: http://bitsavers.trailing-edge.com/pdf/ibm/pc/languages/IBM_Pascal_Compiler_Aug81.pdf

It is part of their “Personal Computer Computer Language Series”

From the era of DOS Pascal compilers before Turbo Pascal.

–jeroen

via Index of /pdf/ibm/pc/dos.


Filed under: Delphi, Development, History, IBM Pascal, Pascal, Software Development

jed-software.com: XE Plus Pack Release 12 (XE4 Support)

$
0
0

Release 12 of XE Plus Pack is now available for download which includes XE4 support.

Users can download the required version from the download page.

To try XE4 Plus Pack download the trial version.

Users of XE2 Plus Pack and XE3 Plus Pack can user their existing registration details to unlock XE4 Plus Pack.

New in Release 12 (for all)

Smart Desktops

Allows you to synchronize your desktops between computers and installations. This is just the first part of the Smart Desktop feature, with additional features being added in future releases.

http://www.jed-software.com/help/SmartDesktops/SmartDesktops.html

New in Release 12 (for XE4 only)

- Component editor for the TTabControl FireMonkey component that allows you to switch between pages using the form designer context menu, or a link at the bottom of the object inspector.

Firebird News: Windows Azure Web Sites and Firebird

$
0
0
Jiří Činčura wrote about his experience with Firebird Embedded and Azure Websites To sum up. Firebird Embedded works well in Windows Azure Websites. If you need fully featured SQL database for zero extra-money with option to go hassle-free to full server if the need comes, it’s a way to go.

The Wiert Corner - irregular stream of stuff: jpluimers

$
0
0

In pre-Galileo versions of Delphi it was easy to run a .BAT or .CMD file as a main project file: just press F9.

Thanks to iManBiglary for posting how to do this in modern Delphi versions. Paraphrased:

Add the file path to cmd.exe (easieist is to add $(ComSpec) which expands the %ComSpec% environment variable) in the tools menu, with /c$EDNAME as the parameter.
In addition, you can tell the IDE to save your file before running the external tool with the $SAVE macro

One of the things you can do with this is add a project containing a batch file that starts to assemble your build results to create a deployment set.

–jeroen

via: Run batch file from Delphi IDE – Stack Overflow.


Filed under: Delphi, Delphi 2005, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi XE, Delphi XE2, Delphi XE3, Development, Software Development

The road to Delphi: Enabling XPath (selectNode, selectNodes) methods in Vcl and FireMonkey Apps

$
0
0

The TXMLDocument class allow you to manipulate XML files in VCL and FireMonkey Apps, but this class doesn’t implements a direct way to call the XPath related methods (selectNode, selectNodes) , so you must write a set of helper functions to call these methods.

Normally you can write something like so

 
function selectSingleNode(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNode;
var
  LDomNodeSelect : IDomNodeSelect;
begin
  if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then
   Exit;
  //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect);
  Result:=LDomNodeSelect.selectNode(nodePath);
end;

 
function SelectNodes(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNodeList;
var
  LDomNodeSelect : IDomNodeSelect;
begin
  if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then
   Exit;
  //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect);
  Result:=LDomNodeSelect.selectNodes(nodePath);
end;

And use like so.

 
var
  XmlDoc: IXMLDocument;
  LNode : IDOMNode;
  i : Integer;
begin
  XmlDoc := TXMLDocument.Create(nil);
  XmlDoc.Active := True;
  XmlDoc.Options := XmlDoc.Options + [doNodeAutoIndent];
  XmlDoc.Version := '1.0';
  ...
  ...
  LNode:=selectSingleNode(XmlDoc.DOMDocument,XPathExpr);

The above code will works fine under Windows using the MSXML provider as Default DOM Vendor, but in a FireMonkey Application which must run in OSX and Windows you must set the Default DOM Vendor to ADOM (OpenXML).

 
 DefaultDOMVendor := OpenXML4Factory.Description;

Now if you try to use the above functions (selectSingleNode, SelectNodes) under the ADOM vendor you will get an awfull exception

 
EOleException Catastrophic failure 8000FFFF

The root of this issue is located in the Tox4DOMNode.selectNode and Tox4DOMNode.selectNodes implementation of these methods, check the next code.

 
function Tox4DOMNode.selectNode(const nodePath: WideString): IDOMNode;
var
  xpath: TXpathExpression;
  xdomText: TDomText;
begin
  Result := nil;
  if not Assigned(WrapperDocument) or not Assigned(WrapperDocument.WrapperDOMImpl) then
    Exit;

  xpath := WrapperDocument.WrapperDOMImpl.FXpath; //here the xpath is set with a nil value because the FXpath was no initialized  
  xpath.ContextNode := NativeNode; //Here the App crash because xpath is nil

The FXpath field is initialized in the Tox4DOMImplementation.InitParserAgent method which is never call at least which you uses the Tox4DOMImplementation.loadFromStream or Tox4DOMImplementation.loadxml methods. So to fix this issue you must call the Tox4DOMImplementation.InitParserAgent function before to call the selectNode and selectNodes methods.

 
function selectSingleNode(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNode;
var
  LDomNodeSelect : IDomNodeSelect;
begin
  if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then
   Exit;
  //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect);
  if (DefaultDOMVendor = OpenXML4Factory.Description) then
    Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent;
  Result:=LDomNodeSelect.selectNode(nodePath);
end;
 
function SelectNodes(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNodeList;
var
  LDomNodeSelect : IDomNodeSelect;
begin
  if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then
   Exit;
  //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect);
  if (DefaultDOMVendor = OpenXML4Factory.Description) then
    Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent;
  Result:=LDomNodeSelect.selectNodes(nodePath);
end;

Now with these changes you will able to evaluate XPath expressions in VCL and FireMonkey Apps using the ADOM vendor.

This is a demo console App tested in Windows and OSX (XE2 and XE4)

 
{$APPTYPE CONSOLE}

uses
  {$IFDEF MSWINDOWS}
  System.Win.ComObj,
  Winapi.ActiveX,
  {$ENDIF}
  System.SysUtils,
  Xml.XMLIntf,
  Xml.adomxmldom,
  Xml.XMLDom,
  Xml.XMLDoc;

function selectSingleNode(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNode;
var
  LDomNodeSelect : IDomNodeSelect;
begin
  if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then
   Exit;
  //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect);
  if (DefaultDOMVendor = OpenXML4Factory.Description) then
    Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent;
  Result:=LDomNodeSelect.selectNode(nodePath);
end;

function SelectNodes(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNodeList;
var
  LDomNodeSelect : IDomNodeSelect;
begin
  if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then
   Exit;
  //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect);
  if (DefaultDOMVendor = OpenXML4Factory.Description) then
    Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent;
  Result:=LDomNodeSelect.selectNodes(nodePath);
end;

procedure  TestXPath;
var
  XmlDoc: IXMLDocument;
  Root, Book, Author, Publisher : IXMLNode;
  LNodeList : IDOMNodeList;
  LNode : IDOMNode;
  i : Integer;
begin
  XmlDoc := TXMLDocument.Create(nil);
  XmlDoc.Active := True;
  XmlDoc.Options := XmlDoc.Options + [doNodeAutoIndent];
  XmlDoc.Version := '1.0';

  Root := XmlDoc.CreateNode('BookStore');
  Root.Attributes['url'] := 'http://www.amazon.com';
  XmlDoc.DocumentElement := Root;

  Book := XmlDoc.CreateNode('Book');
  Book.Attributes['Name'] := 'Steve Jobs';
  Author := XmlDoc.CreateNode('Author');
  Author.Text := 'Walter Isaacson';
  Publisher := XmlDoc.CreateNode('Publisher');
  Publisher.Text := 'Simon Schuster (October 24, 2011)';
  Root.ChildNodes.Add(Book);
  Book.ChildNodes.Add(Author);
  Book.ChildNodes.Add(Publisher);

  Book := XmlDoc.CreateNode('Book');
  Book.Attributes['Name'] := 'Clean Code: A Handbook of Agile Software Craftsmanship';
  Author := XmlDoc.CreateNode('Author');
  Author.Text := 'Robert C. Martin';
  Publisher := XmlDoc.CreateNode('Publisher');
  Publisher.Text := 'Prentice Hall; 1 edition (August 11, 2008)';
  Root.ChildNodes.Add(Book);
  Book.ChildNodes.Add(Author);
  Book.ChildNodes.Add(Publisher);

  Book := XmlDoc.CreateNode('Book');
  Book.Attributes['Name'] := 'Paradox Lost';
  Author := XmlDoc.CreateNode('Author');
  Author.Text := 'Kress, Peter';
  Publisher := XmlDoc.CreateNode('Publisher');
  Publisher.Text := 'Prentice Hall; 1 edition (February 2, 2000)';
  Root.ChildNodes.Add(Book);
  Book.ChildNodes.Add(Author);
  Book.ChildNodes.Add(Publisher);

  Writeln(XmlDoc.XML.Text);

  Writeln('selectSingleNode');
  LNode:=selectSingleNode(XmlDoc.DOMDocument,'/BookStore/Book[2]/Author["Robert C. Martin"]');
  if LNode<>nil then
   Writeln(LNode.firstChild.nodeValue);

  Writeln;

  Writeln('SelectNodes');
  LNodeList:=SelectNodes(XmlDoc.DOMDocument,'//BookStore/Book/Author');
  if LNodeList<>nil then
    for i := 0 to LNodeList.length-1 do
      Writeln(LNodeList[i].firstChild.nodeValue);
end;

begin
 try
    ReportMemoryLeaksOnShutdown:=True;
    DefaultDOMVendor := OpenXML4Factory.Description;
    {$IFDEF MSWINDOWS}CoInitialize(nil);{$ENDIF}
    try
      TestXPath;
    finally
    {$IFDEF MSWINDOWS}CoUninitialize;{$ENDIF}
    end;
 except
    {$IFDEF MSWINDOWS}
    on E:EOleException do
        Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    {$ENDIF}
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln;
 Writeln('Press Enter to exit');
 Readln;
end.

OSXXPATH


Firebird News: Advanced Data Generator 3 released

$
0
0
Upscene Productions announces the release of: “Advanced Data Generator 3″ A fast test-data generator tool that comes with a library of real-life data, can generate data to your database, SQL script or CSV files, many filling options, presets and much more. If you develop database applications, you need data for testing purposes! New feature highlights: [...]

DelphiTools.info: Performance issue in NextGen ARC model

$
0
0
Weak references and their zeroing is managed in Delphi NextGen through a global hash table, with a global lock, see Arnaud Bouchez take on it: Performance-issue-in-NextGen-ARC-model Also apart from the global locking which itself is bad enough, there are a couple further performance issues: If the lock are really through TMonitor, rather than through OS-supported [...]

It's a blong, blong, blong road...: Delphi for iOS - some notes

$
0
0

The typical current requirements of the customers I work with, so they tell me, are to stick with Windows + VCL thanks to the long-term projects they have ongoing. Because of this I’ve not really spent too much time getting to know FireMonkey in excruciating detail since it was introduced. However, given that the quintessence of the XE4 release has been iOS support and this typically revolves around using FireMonkey, I figured maybe it was about time to roll the sleeves up and get stuck in.

This post is a summary run-though of the things I bumped into, how I got on, what I figured out, what problems I encountered and how I achieved a few things whilst checking out the latest platform-targeting in the Delphi product line.

Environment setup

First things typically come first, so top of the agenda was setting up the development environment to work on iOS projects. This was straightforward enough. The online documentation (to be honest I don’t install the help – I just use Embo’s docwiki site) covers the setup quite thoroughly. There’s a general run-through and then also in the iOS tutorials page there are links to detailed setup for the Mac and setup for Windows.

To develop apps for Mac (OS X) or for iDevices (iOS) requires the use of a Mac – Apple make this a requirement as they supply some necessary tools for parts of the job (such as signing the app or simulating an iDevice) and those come with Apple’s Xcode and only run on a Mac. There’s no real need to dash out and buy a hugely over-priced MacBook Pro unless you have spare cash at your disposal; a Mac Mini is more than adequate and much more sensibly priced.

Delphi or RAD Studio will run on a Windows machine, and a helper app called PAServer (the Platform Assistant) runs on the Mac to:

  • liaise with any Mac tools that are required
  • automate the iPhone/iPad Simulator when running iOS apps against the simulator
  • act as a remote debugger when debugging a Mac or iOS application (either on a device or in the simulator)

My first attempt at iOS development had my development environment (Delphi XE4) on a Windows laptop, talking across my network to the Mac Mini. This actually worked just fine, though the deployment step took about 10-20 seconds each time, sending the app files and the debug symbols across the network. Hardly a long time, but it got annoying when I kept needing to make small changes and see their effect.

I ended up settling on a Windows virtual machine running on the Mac Mini via VMWare Fusion and installed Delphi XE4 in that. This made the deployment step joyfully rapid. I also then had the luxury of working directly with the Mac Mini, or working remotely on my laptop using VNC. I can have one session connected to the Windows virtual machine (VMWare Fusion acts can have each virtual machine optionally act as a VNC server) and another session talking to the Mac itself to see apps running in the simulator.

I should point out I don’t have any iDevices – all my apps are just tested in the simulator and that’s that for the time being.

To build iOS apps for the device and the simulator the IDE makes use of two of the five Delphi compilers shipped in RAD Studio XE4 (or Delphi XE4 with the mobile add-on), which are the latter two in this compiler list:

  • dcc32 – Embarcadero Delphi for Win32 compiler version 25.0 – your regular Win32 Delphi, the latest version updated over many versions since Delphi 2
  • dcc64 – Embarcadero Delphi for Win64 compiler version 25.0 – the 64-bit Windows targeting compiler
  • dccosx – Embarcadero Delphi for Mac OS X compiler version 25.0 – the Delphi compiler that generates 32-bit OS X apps
  • dccios32 – Embarcadero Delphi Next Generation for iPhone Simulator compiler version 25.0 – the NextGen Delphi compiler that builds Intel executables that run in the iOS simulator
  • dcciosarm - Embarcadero Delphi Next Generation for iPhone compiler version 25.0 – the NextGen Delphi compiler that builds ARM executables that run on iOS devices

Simple FireMonkey apps

To check that FireMonkey behaves basically the same as VCL I created a FireMonkey mobile application and threw some code on the form. It did as expected, and I noted the pervasive StyleLookup property that lets you customise all the controls. For example with a button there are pre-defined styles to make the button look like the various buttons that commonly crop up in iOS applications.

This style system helps you put together a UI that will fit in with other apps in the world of iOS. This is an important issue. When you build an iOS app you’ll be creating a UI specifically for the mobile devices – laid out to look appropriate on an iPhone and/or iPad. Apple are very specific about how UIs on their OSs should look and have comprehensive User Experience guidelines both for OS X and for iOS. You should digest these guidelines and take note of how existing applications look to ensure your app doesn’t stick out like a sore thumb. The UI should be carefully designed for the target platform.

<soapbox>Consequently, the repeated platitude we hear from the bods at Embo about building an app once and recompiling it for different platforms should be taken for what it is: marketing fluff. Sure, back-end code could follow that ideal, but anything tightly related to the UI will doubtless need to be re-implemented for each target platform. You owe it to your users to respect them enough to do the right thing with your app’s UI.</soapbox>

All that notwithstanding, it was a breeze to build a simple iPhone app and have it run in the simulator. Seemed very much akin to building a VCL application at the basic level I was working at, which was quite encouraging.

Compiler changes

If you’re looking to share a bunch of existing code with an iOS Delphi application you should remember that the iOS compiler is a next generation Delphi compiler, with a few language changes in. I gave a list of the changes (including proposed changes currently represented by warnings) in the compiler in a previous blog post here.

The removal of some of the string types and some of the other obvious changes in it might make it more difficult than you expect to pull in a lot of old code, but obviously your mileage will vary on this one. If you want to write new code that will be shared across platforms, the fact that the next-gen compiler employs ARC for all Delphi objects (not just strings, dynamic arrays and interfaces) means that the shared code will need to call Free, which is a no-op on iOS. iOS-only code can forget all about calling Free, which is nice.

Certainly what I hear from clients I work with is that their primary goal for the iOS compiler is to build some small standalone applications that will, via some connectivity option or other, perhaps connection to a web service or DataSnap server, present some representation of a portion of an existing desktop application to users, or provide reports and stats from an existing system to management types. That seems like a very achievable and sensible plan to me.

New dependency keyword

A new keyword that slipped in under the radar in the NextGen ARM compiler is dependency. This is really a convenience that precludes the need to tweak the options to get certain apps to work. Specifically this keyword lets you tell the linker of additional libraries to link in when declaring an external library function. So, for example, the MIDAS static link library midas.a has a dependency on the Standard C++ library libstdc++.dylib. A dylib on a Mac can be dynamically loaded at runtime or statically linked. Apple decrees that iOS apps cannot use any custom dynamic libraries, so any library code must be statically linked into the app.

This means that any app that uses the MIDAS library must statically link the Standard C++ library in as well. Without the dependency keyword this would involve modifying the ARM linker options. This could be done by going into the project options (Ctrl+Shift+F11) and doing either of these:

  • selecting Delphi Compiler, Compiling in the options tree, then selecting Other options, Additional options to pass to the compiler and entering a suitable portion of a dcciosarm.exe comand-line like:

    --linker-option:"-L $(BDSLIB)\$(Platform)\$(Config) -lstdc++"

  • selecting Delphi Compiler, Linking in the options tree, then selecting Options passed to the LD linker and entering:

    -L $(BDSLIB)\$(Platform)\$(Config) -lstdc++

However, dependency means you can forget all that and write your import declaration like this one, from DataSnap.DSIntf.pas:

{$IF DEFINED(IOS) and DEFINED(CPUARM)}
function DllGetDataSnapClassObject(const [REF] CLSID,
  [REF] IID: TGUID; var Obj): HResult; cdecl;
  external 'libmidas.a' name 'DllGetDataSnapClassObject'
  dependency 'stdc++';
{$ENDIF IOS and CPUARM}

This approach is used more widely for the InterBase DBExpress client code in Data.DbxInterbase.pas. Here we find a variety of imported functions coming from libsqlib.a, which have a dependency on both Standard C++ and also libibtogo.dylib. So there are a whole bunch of declarations like:

function DBXBase_Close(
  Handle: TDBXCommonHandle): TDBXErrorCode; cdecl;
  external 'libsqlib.a' name 'DBXBase_Close'
  dependency 'ibtogo', 'stdc++';

FireMonkey versus CocoaTouch

If you spend some time browsing through the FireMonkey and iOS-specific RTL source you’ll become familiar (at one level or another) with the relationship between FireMonkey and the iOS Objective-C API. Very similar to how the VCL presents nice components, but those components are built on the Windows UI infrastructure of windows and window handles and how VCL applications operate by participating in the regular Windows message processing behaviour, FireMonkey iOS applications also immerse themselves in the regular iOS infrastructure.

What this means is that a FireMonkey form is based on an Objective-C UIView or, more specifically, a GLKView in the case of a GPU-backed form. The application start-up is done through a call to UIApplicationMain and there is an application delegate (UIApplicationDelegate protocol implementation) used to deal with when the app starts, goes to the background, comes back to the foreground and terminates.

Here and there you might find it useful to call upon some iOS APIs, which requires a heads-up on some basics. The following sections endeavour to give you such a heads-up.

Working with NSStrings

Objective-C uses NSString objects where Delphi uses strings. If you need to pass a string to an iOS API that expects an NSString you can use the NSStr function in iOSapi.Foundation.pas to translate it over.

You can also do the reverse by borrowing (ok, copying) the NSStrToStr function that is (inexplicably) tucked away in the implementation section of FMX.MediaLibrary.iOS.pas.

Additionally, any time you are presented with a UTF8 string from an Objective-C method (which would be represented as type _PAnsiChr), such as the UTF8String method of NSString or the systemVersion method of a UIDevice, you can turn it into a Delphi string with UTF8ToString.

Delphi’s representation of Objective-C objects

Much as Delphi's classes all stem from TObject, Objective-C classes all have a common ancestor of NSObject. The large number of classes in the full tree are split into branches of classes called frameworks. For example UIKit is the framework containing all the basic UI-related stuff and MapKit wraps up all the mapping stuff.

Delphi represents these Objective-C classes by interfaces. For example, NSObject is declared in iOSapi.CocoaTypes.pas and contains declarations of the instance methods of an Objective-C object of type NSObject. Where Delphi has an interface for an Objective-C class's instance methods there is also another interface for all the class methods. In the case of NSObject it's called NSObjectClass.

In order to make use of these interfaces a helper class is defined alongside the instance interface and class interface for the Objective-C type. The helper class is a generic class that inherits from TOCGenericImport<C,T> and wraps up the process of importing the Objective-C class into Delphi. For NSObject the helper class is TNSObject and gives you a means to create an Objective-C object of type NSObject and also wrap up an object id for such an Objective-C object into an NSObject interface reference. That sets out the pattern of types used to represent imported Objective-C objects.

In Objective-C constructing an object is a two stage process. The memory is allocated by a call to the class alloc method and then the returned object is initialised through a call to a class initialiser, such as the default init or maybe a custom one. Various classes have custom initialisers, for example controls have one called initWithFrame. We may be more familiar with the one-stop construction achieved by calling a Delphi class constructor, such as Create, but of course behind the scenes the same two steps have to occur - the memory for the instance is allocated and then the body of Create initialises that instance.

Let's take an example of creating an alert view - basically an iOS message box. Clearly in a FireMonkey project you would just use ShowMessage or MessageDlg, which will work out how to do all this on iOS platform, but for the sake of example let's follow it through. The alert view is represented in iOS by UIAlertView, so iOSapi.UIKit.pas defines interfaces UIAlertView and UIAlertViewClass as well as class TUIAlertView. If you want to construct an instance of UIAlertView using alloc and the default init initialiser then a call to TUIAlertView.Create will do the job and return an interface reference to a UIAlertView. You can then tailor the alert view by calling its setTitle, setMessage and addButtonWithTitle methods and display it by calling show. Something like this:

var
  alertView: UIAlertView;
...
  alertView := TUIAlertView.Create;
  alertView.setTitle(NSStr('Delphi for CocoaTouch'));
  alertView.setMessage(NSStr('Hello world'));
  alertView.setCancelButtonIndex(
    alertView.addButtonWithTitle(NSStr('OK')));
  alertView.show;
  alertView.release;

However if you want to use a custom initialiser you can allocate an uninitialised instance by calling the helper class's Alloc method and then call the custom intialiser on that. However the custom intialiser will return an actual Objective-C object (as opposed to an interface that represents it), which is represented as a raw pointer. This pointer can be considered the Objective-C object id. To turn this object id back into a usable Delphi interface reference you feed it into the helper class's Wrap method (just be careful with Wrap as it does not check for nil being passed in and so will happily crash if given the chance - see QC 115791). So we could rewrite the above like this:

var
  alertView: UIAlertView;
...
  alertView := TUIAlertView.Alloc;
  alertView := TUIAlertView.Wrap(alertView.initWithTitle(
    NSStr('Delphi for CocoaTouch'), //title
    NSStr('Hello world'), //message
    nil, //delegate
    NSStr('OK'), //cancel button caption
    nil)); //other button captions
  alertView.show;
  alertView.release;

So that shows how to create a new instance of an existing Objective-C class and call instance methods. To call class methods you use the OCClass property. The UIDevice iOS class has a currentDevice read-only property that is documented to return a UIDevice instance that represents the current device. Similarly UIScreen has a mainScreen read-only property that returns a UIScreen instance that represents the main screen (i.e. not an external screen).

var
  currentDevice: UIDevice;
  mainScreen: UIScreen;
  iOSversionStr, screenDimensionsStr: string;
...
  currentDevice := TUIDevice.Wrap(
    TUIDevice.OCClass.currentDevice);
  iOSversionStr := UTF8ToString(
    currentDevice.systemVersion.UTF8String);
  mainScreen := TUIScreen.Wrap(TUIScreen.OCClass.mainScreen);
  screenDimensionsStr := Format('%d x %d',
    [Round(mainScreen.bounds.size.width),
     Round(mainScreen.bounds.size.height)]);

From a given interface reference to an Objective-C object, obj, you can access its object id (the pointer to the actual Objective-C object) using this:

(obj as ILocalObject).GetObjectID

Objective-C properties

Objective-C objects offer various properties just like Delphi objects do. These are implemented with getter and setter functions as you'd expect. However if building a Delphi iOS app you should know that all the Delphi interfaces representing Objective-C classes don't have these properties brought through. Instead you'll need to call the setter procedure (e.g. setFrame() for a UIView object's frame property or setStatusBarHidden() for the UIApplication class statusBarHidden property) or getter function (e.g. frame() for the frame property or isStatusBarHidden() for the statusBarHidden property.

Yeah, as you may notice there the property getter either has the same name as the property or uses an is prefix... You can read the formal documentation on property accessor naming on Apple's site here.

Objective-C method names

As explained in various places on the Arpanet (or Internet as we now call it), such as this method naming post from Ry's Objective-C tutorial, Objective-C has an interesting approach to method naming. The parameter names become part of the method name to aid self-description and to minimise opportunities for ambiguity. Let's have a look at some example methods defined in the UIApplicationDelegate protocol. Firstly the method that triggers after the application has started up:

(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

This is a function method that returns a Boolean and takes two arguments, a reference to a UIApplication and a reference to an NSDictionary. To the person implementing the method the arguments are called application and launchOptions but the arguments are described in the full method name, which is formed from all items that have colon suffixes. The Objective-C method name is application:didFinishLaunchingWithOptions: and this differentiates it from other methods in the same protocol, such as application:shouldSaveApplicationState: and application:willChangeStatusBarFrame:.

Problems start cropping up over on the Delphi side when you try to translate these methods to a Delphi representation. Here are a selection of methods from this protocol that we might wish to translate:

(BOOL)application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
(BOOL)application:(UIApplication *)application
  shouldSaveApplicationState:(NSCoder *)coder
(void)application:(UIApplication *)application
  willChangeStatusBarFrame:(CGRect)newStatusBarFrame

Now here is the best we can do in Delphi to translate them over accurately:

function application(application: UIApplication;
  didFinishLaunchingWithOptions:NSDictionary): Boolean; overload;
function application(application: UIApplication;
  shouldSaveApplicationState: NSCoder): Boolean; overload;
procedure application(application: UIApplication;
  willChangeStatusBarFrame: CGRect); overload;

You'll notice each method has the same name and so requires overloading. But we're okay so far as each overload has a unique signature. Let's add in some more methods:

(BOOL)application:(UIApplication *)application
  shouldRestoreApplicationState:(NSCoder *)coder
(void)application:(UIApplication *)application
  didChangeStatusBarFrame:(CGRect)oldStatusBarFrame

You can probably see the problem rearing its head now. When we translate these into additional methods in a Delphi UIApplicationDelegate interface the compiler won't accept the overloads as we have the same signatures as already defined:

function application(application: UIApplication;
  shouldRestoreApplicationState: NSCoder): Boolean; overload;
procedure application(application: UIApplication;
  didChangeStatusBarFrame: CGRect); overload;

So this leads to the realisation that the general case is that we cannot adequately represent any given Objective-C class or protocol by using a Delphi interface. You can do some of it, but as soon as matching signatures come along this approach falls down. If you hunt out the UIApplicationDelegate interface in iOSapi.UIKit.pas you'll see a number o the methods commented out for this exact reason.

This puts a bit of a downer on the whole affair until you find out that given enough desire you can work around this limitation of Delphi's method representations and set things up manually. In fact that's exactly what FireMonkey does in order to set up an application delegate on iOS application start-up. It uses low-level Objective-C Runtime routines to set up Delphi routines to implement given Objective-C methods. If you look in FMX.Platform.iOS.pas and locate TPlatformCocoaTouch.Create you can see this being done.

It's a bit of a faff, but does allow Delphi to work with any Objective-C objects. I've used the approach myself in a couple of test applications and can confirm that it's workable, but really it's not entirely productive to do so. I plan to write more on the subject at a later point, but for now I'll leave the FireMonkey source as an adequate reference.

ARC and Delphi and Objective-C

When Delphi first arrived it supported the string type. A string is really a pointer to a data structure that is automatically memory-managed for you. This memory management includes reference counting to cater for when strings are passed into subroutine calls and assigned to variables. When nothing references a string any longer the code generated by the compiler ensures all the memory occupied by the string data structure is freed.

When interfaces were added to Delphi 3 we again enjoyed reference counted memory management. The compiler worked out when to call the _AddRef and _Release methods on interface references based on assignments, parameter passing and scope and if _Release caused the internal reference count to reach 0 the object destroyed itself.

With the NextGen Delphi compiler the programmer is afforded the luxury of automatic reference counting (ARC) for all objects, as detailed in this Dr Dobbs article by Embo's JT and Marco Cantù. The compiler will identify where to make calls to TObject's __ObjAddRef and __ObjRelease for you. Again, when the internal reference count gets to 0 the object destroys itself. Hence there is no longer a need to worry about calling Free and wrapping construction and destruction code up in a pair inside a try/finally statement. That said, it doesn't matter if you do call Free as in the NextGen compiler TObject.Free simply sets the object reference to nil, hastening the decrement of the object reference.

When you program iOS apps in Objective-C you are also blessed with ARC. Just as with Delphi it's a compiler thing - the compiler works out where it's appropriate to make calls to retain and release to ensure the internal reference counts will drop to 0 when a given object is no longer used by anything.

So the Delphi (NextGen) compiler does ARC against all Delphi objects and the Objective-C compiler does ARC against all Objective-C objects, but what about Objective-C objects in a Delphi program?

Unfortunately there is no ARC for the iOS objects represented by the import wrapper class and the interfaces discussed above. When dealing with iOS objects manually you will need to call retain and release yourself at the correct points. Allocating an iOS object starts the reference count off at 1; calling release will drop it to 0 and have the object destroy itself.

Apple’s Instruments app

A really useful tool supplied as part of Xcode (or possibly just shipped a part of OS X, I’m not positive) is Instruments. If you read up on Instruments you’ll see that this can help identify memory leaks in your OS X or iOS apps. I've been using it to check on iOS object leaks in FireMonkey applications and also in apps written in Delphi that use no FireMonkey business at all. It's been quite illuminating as I've happened upon a couple of simple shortcomings in the FireMonkey code.

The principle I've used is to have all the code that does stuff I want to leak-check in secondary forms launched from a main form. In the case of FireMonkey I'm just using regular forms and launching them with ShowModal. I launch the app in the iOS simulator and then launch Instruments and choose the Leaks mode. Next I use the Choose Target dropdown and from the Attach to Process submenu I select my app process from the System list. Pressing the Record button then starts tracking object and memory allocations. For any given iOS object type you can see how many instances still exist, look at a list of them all and for any instance see the allocation and all the retain/release calls.

Going through this process with a simple FireMonkey app that launched a secondary form showed that a UIView and various other helper objects were being allocated each time the form was displayed but not freed when it was closed. Tracking the problem eventually yielded some fixes to the FireMonkey source that resolve the problem, as documented in QC 115914. In short you need to:

  1. copy FMX.Platform.iOS.pas from $(BDS)\source\fmx
  2. change TPlatformCocoaTouch.DestroyWindow to be:

    procedure TPlatformCocoaTouch.DestroyWindow(
      const AForm: TCommonCustomForm);
    begin
      if Assigned(AForm.Handle) then
      begin
        WindowHandleToPlatform(AForm.Handle).View.
          removeFromSuperview;
        WindowHandleToPlatform(AForm.Handle).View.release;
      end;
    end;
  3. in TPlatformCocoaTouch.ShowWindowModal change the finally part of the try/finally statement to:

    finally
      BackView.removeFromSuperview;
      BackView.release;
    end;

Further experimentation showed that the iOS implementation of the message box routines (ShowMessage, MessageDlg and InputQuery) were also leaking objects; in this case UIAlertView references. QC 115966 contains the report and a proposed source fix. As above you change a local copy of FMX.Platform.iOS.pas. This time you change TPlatformCocoaTouch.MessageDialog. At the end of it add this statement:

AlertView.release;

Now in TPlatformCocoaTouch.InputQuery, imediately after the assignment: Result := Delegate.Result = mrOk

add in the statement:

AlertView.release;

Accessing the Objective-C shared application object

iOS apps often need access to the Objective-C shared application object (a singleton UIApplication object that represents the running application). One example would be when using a TWebBrowser object you can indicate to the user that network activity is taking place as the web page is brought down by using a property of the application object to control the iDevice's network activity icon on the status bar. The browser object's OnDidStartLoad event marks the start if the page download and OnDidFinishLoad or OnDidFailLoadWithError tell you when it's over. The application object has a networkActivityIndicatorVisible property that controls the status bar icon.

In iOS you retrieve the shared application object by using the sharedApplication class function of the UIApplication class.

Here is how you could build a helper function to expose the shared Objective-C application object. Note that a function just like this is implemented in (although not exposed from) both these iOS FireMonkey units: FMX.MediaLibrary.iOS.pas and FMS.Pickers.iOS.

uses
  iOSapi.UIKit;

function GetSharedApplication: UIApplication;
begin
  Result := TUIApplication.Wrap(
    TUIApplication.OCClass.sharedApplication);
end;

Given this function you could write event handlers for a web browser component like this:

procedure TBrowserForm.WebBrowserDidStartLoad(ASender: TObject);
begin
  GetSharedApplication.setNetworkActivityIndicatorVisible(True);
end;

procedure TBrowserForm.WebBrowserDidFinishLoad(
  ASender: TObject);
begin
  GetSharedApplication.setNetworkActivityIndicatorVisible(
    False);
end;

procedure TBrowserForm.WebBrowserDidFailLoadWithError(
  ASender: TObject);
begin
  GetSharedApplication.setNetworkActivityIndicatorVisible(True);
  ShowMessage('Web page failed to load for an unknown reason');
end;

In this case the failure event handler cannot tell the user what the failure is as the NSError object given the equivalent iOS event handler is not surfaced to FireMonkey, even in some abstract manner. I've reported this to Quality Central as QC 115652.

Logging to the OS X Console app

It is common in iOS apps to emit logging statements that are picked up by OS X’s Console application using the NSLog API. This is a global function, not a method, and takes an NSString, but because it is outside the scope of the Delphi helper objects and interfaces it expects a PNSString (a pointer to an NSString) - an Objective-C object id for the string in question. To make it easy to turn a Delphi string into a PNSString you can build a helper function like this:

uses
  iOSapi.Foundation, Macapi.ObjectiveC;
...
function PNSStr(const AStr: String): PNSString;
begin
  Result := (NSStr(AStr) as ILocalObject).GetObjectID
end;

While you are at it you could build a more usable Log function:

procedure Log(const AStr: String); overload;
procedure Log(const AStr: String; const Args: array of const); overload;
...
procedure Log(const AStr: String);
begin
{$IFDEF IOS}
  NSLog(PNSStr(AStr));
{$ELSE}
  {$MESSAGE WARN 'Only currently implemented for iOS'}
{$ENDIF}
end;

procedure Log(const AStr: String; const Args: array of const);
begin
  Log(Format(AStr, Args))
end;

Numeric type definitions

For the most part you'll not bump into this being an issue, but the initial release of XE4 has NSInteger and NSUInteger both incorrectly defined in iOSapi.CocoaTypes.pas. NSInteger is defined to be Integer and NSUInteger to be LongWord, which are both always 32-bits regardless of the underlying platform. The Apple documentation for NSInteger and NSUInteger clearly states that on 32-bit platforms the types are 32 bits and on 64-bit platforms they are 64 bits.

It would be rare to find these wrong definitions a problem, but I did bump into it as an issue when implementing a C calling convention (cdecl) routine that was called by Objective-C and returned an NSInteger. When the stack was cleared up by the Objective-C side the differing size of the return value meant the function's return address was in "the wrong place" and so the app immediately crashed. I sorted this in the project by defining local versions of the types that were correct.

This has been reported as QC 115789.

Debug source stepping

The keener among us Delphi users like to step through the RTL and VCL or FMX source. With iOS apps the installation neglects to add one of the key iOS RTL directories to the browsing path in the initial XE4 release. This results in various iOS units not being found when trying to step through the source. The issue has been reported on QC, and I submitted a duplicate report before learning of that initial post.

To fix the problem go to Tools, Options..., Delphi Options, Library. Now add $(BDS)\source\rtl\ios to the Browsing path


the reason for the use of typically is that you can actually build a regular CocoaTouch application using Delphi for iOS if you really want to. I've done this myself with a few little apps, working through a variety of technical stumbling blocks along the way. I'm in the process of writing up my findings on the subject, so I'll hopefully blog about an article on the subject in the near future.

DelphiTools.info: DWScript showcase: Graphics32 interactive demo

$
0
0
Graphics32 is a rich, high-performance 32bits graphics library for Delphi, with extensive capability. This showcase was submitted by Christian-W Budde, who is an active contributor to the Graphics32 project. This is an interactive demo/testing tool where you can write GR32 code and immediately see its output. This can help develop complex graphics from code. While writing [...]

Delphi Code Monkey: I have split away from the sourceforge TComPort project and will maintain my own TComPort 5.0 AnsiString Version

$
0
0
One component I have depended on for serial port programming over the years is TComPort, originally by Dejan Crnila.

My own private fork of it is located on BitBucket now. Known issues with my last version (5.0, last touched in 2010) include that the Packet component doesn't work properly.

Some of those bugs are fixed in the latest version on SourceForge, which Brian Gochnauer has taken over maintaining. The reason for me wanting to maintain my own version is that I believe there will be two possible schools of thought out there.

Brian's thinking, and perhaps others may agree, is that a "regular String" should be used for String read/write methods. My thinking is that AnsiString should be used in both ansi and unicode Delphi versions, because serial port protocols are either binary (8 bit) or ascii (7 bit or 8 bit) and there are NO 16 bit serial port protocols that I know of.

So that means maybe you have to cast here and there to avoid implicit cast warnings, and I say, so be it.

Anyways, the other thing that I think is weird about the latest sourceforge version is that the Async read/write method appears to do things which might cause memory corruption or crashes depending on your luck.  My code used GetMem and FreeMem and allocated and copied information into distinct non-delphi-string-heap areas before they were involved in any Win32 Async API operations, to avoid unpleasantness.  That appears not to be the case anymore.  So I am publically disavowing that codebase, and leaving it to whoever wants to maintain it to maintain it.  I am no longer formally involved in that code.

I will retain sourceforge project membership so that in the event that Brian does not resume development and someone wants to repair the current UnicodeString based codebase, I can add them as a comitter. I bear no ill will to Brian, and thank him for fixing and maintaining the project on sourceforge when I had no time to do it. But I also feel that since I was publically associated with the project for many years, I need to say, I'm not happy with the code, and I think it stinks, but I'm not going to fix it either.

I'm going back to my last version, and I'll re-add any patches that seem necessary to add that have been contributed since I stopped maintaining the project actively.   Really, if I'm blaming anyone here, I'm blaming myself for not reviewing the changes as they happened to AsyncPro, for sanity and correctness. My bad.

Anyways, carry on programming serial ports with AsyncPro or with TComPort, use whatever you like, but be aware that I think there are some serious issues with the latest SourceForge version of TComPort.









Firebird News: ADO.NET provider for Firebird with Entity Framework 6 support preview

$
0
0
ADO.NET provider for Firebird with Entity Framework 6 support preview

Delphi Haven: TMS releases iCL native iOS control toolkit for XE4

$
0
0

Now this looks interesting – TMS have just released iCL, a native iOS control toolkit for Delphi XE4 (link). Given I’ve only just seen the news I don’t have much more to say, however it definitely looks like a product to check out if you’re interested in writing iOS apps using Delphi.


The road to Delphi: Getting System information in OSX and iOS using Delphi (XE2, XE3, XE4) Part 1

$
0
0

In the following article you can learn how to use the sysctl, sysctlbyname and sysctlnametomib functions to get system information (kernel values, hardware, networking, file system, machine specific and user related data) under the OSX and iOS systems.

sysctl

int sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);

The sysctl allows you retrieves and set system information, the data returned from the sysctl() function consists of integers (int32, int64), strings(AnsiStrings) and structs (records). this function is defined in the Posix.SysSysctl (SysSysctlAPI.inc) unit

Note : The Posix.SysSysctl unit is a partial translation the sysctl.h file.

function sysctl(name: PInteger; namelen: cardinal; oldp: Pointer; oldlen: Psize_t; newp: Pointer; newlen: size_t): Integer; cdecl; external libc name _PU + 'sysctl';

name : This parameter receive a pointer to a Management Information Base (MIB) style value, which is only a array of integers. Each element of this array must be filled with the values related to the info to read or write.

The number of elements of this array, depends of the data to be obtained or modified, most of times only we need to pass and fill a mib with 2 elements (integer values). The first element indicates the level(location) of the info and the second element indicates the value to retrieve.

These are the possible values for the first element of the mib.

           Name           Value            Description
           CTL_DEBUG      $00000005        Debugging
           CTL_VFS        $00000003        File system
           CTL_HW         $00000006        Generic CPU, I/O
           CTL_KERN       $00000001        High kernel limits
           CTL_MACHDEP    $00000007        Machine dependent
           CTL_NET        $00000004        Networking
           CTL_USER       $00000008        User-level
           CTL_VM         $00000002        Virtual memory 

Note: All these values are already defined in the Posix.SysSysctl unit.

So if we want access to the kernel related values we must fill the mib like so

var
  mib : array[0..1] of Integer;
...
...
 mib[0] := CTL_KERN;

The second element value is related to the first level and the possible values are defined in the Posix.SysSysctl unit as well, for example to get the max processes supported by the system we must use the KERN_MAXPROC($00000006) value.

var
  mib : array[0..1] of Integer;
...
...
 mib[0] := CTL_KERN;
 mib[1] := KERN_MAXPROC;

namelen : This parameter is the length of the mib structure.
oldp: Must be filled with a pointer to the buffer to receive. The info which can be a integer, int64, a AnsiString(MarshaledAString) or a record.
oldlen: Indicates the size of the oldp parameter.
newp: This parameter must be filled with a pointer to the buffer to with the info to set up, when you don’t modify the data you must pass a nil value.
newlen: Indicates the size of the newp parameter.

So with all the above info now you can write a function to retrieve the max processes supported, check this sample code which retrieves a integer value.

function MaxProcesses : Integer;
var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
begin

 mib[0] := CTL_KERN;
 mib[1] := KERN_MAXPROC;

 len := sizeof(Result);
 res:=sysctl(@mib, Length(mib), @Result, @len, nil, 0);
 if res<>0 then
  RaiseLastOSError;
end;

To get a Int64 value the code is very similar, check this sample which get the size of the memory installed on the system using the CTL_HW level and the HW_MEMSIZE value.

function MemSize : Int64;
var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
begin
 mib[0] := CTL_HW;
 mib[1] := HW_MEMSIZE;

 len := sizeof(Result);
 res := sysctl(@mib, Length(mib), @Result, @len, nil, 0);
 if res<>0 then
  RaiseLastOSError;
end;

If you want retrieve a string value, you must get the length of the value before to allocate the buffer, to do this you must pass a nil value in the oldp parameter like so.

sysctl(@mib, Length(mib), nil, @len, nil, 0)

The next code shows how to get a string(ascii) type using the sysctl function.

function KernelVersion : AnsiString;
var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
  p   : MarshaledAString;//in XE2 you can use the PAnsiChar type
begin
 mib[0] := CTL_KERN;
 mib[1] := KERN_VERSION;
 //get the length of the buffer 
 res := sysctl(@mib, Length(mib), nil, @len, nil, 0);
 if res<>0 then
   RaiseLastOSError;
 //allocates the buffer
 GetMem(p, len);
 try
   res := sysctl(@mib, Length(mib), p, @len, nil, 0);
   if res<>0 then
     RaiseLastOSError;
   Result:=p;
 finally
   FreeMem(p);
 end;
end;

Finally we can use the sysctl function to retrieve complex structures(records) passing a pointer to the record to hold the data.

Try this sample which get the clock rate values from the kernel.

procedure GetClockInfo;
type
 clockinfo = record
	hz      : Integer;
	tick    : Integer;
	tickadj : Integer;
	stathz  : Integer;
	profhz  : Integer;
  end;

(*
struct clockinfo {
	int	hz;	   	/* clock frequency */
	int	tick;		/* micro-seconds per hz tick */
	int	tickadj;/* clock skew rate for adjtime() */
	int	stathz;		/* statistics clock frequency */
	int	profhz;		/* profiling clock frequency */
};
*)

var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
  clock : clockinfo;
begin
 FillChar(clock, sizeof(clock), 0);
 mib[0] := CTL_KERN;
 mib[1] := KERN_CLOCKRATE;
 len := sizeof(clock);
 res:=sysctl(@mib, Length(mib), @clock, @len, nil, 0);
 if res<>0 then
   RaiseLastOSError;

 Writeln(Format('clock frequency             %d',[clock.hz]));
 Writeln(Format('micro-seconds per hz tick   %d',[clock.tick]));
 Writeln(Format('clock skew rate for adjtime %d',[clock.tickadj]));
 Writeln(Format('statistics clock frequency  %d',[clock.stathz]));
 Writeln(Format('profiling clock frequency   %d',[clock.profhz]));
end;

sysctlbyname

int sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);

The sysctlbyname works in the same way which the sysctl, the only difference is which the values are accessed using a alias string. because that you don’t need pass a mib structure and length to this function.

function sysctlbyname(Name: MarshaledAString; oldp: Pointer; oldlen: Psize_t; newp: Pointer; newlen: size_t): Integer; cdecl; external libc name _PU + 'sysctlbyname';

name: this parameter is the alias for the info to access and is composed by the level splus the string representation of the value to get, because that you don’t need pass a mib structure.

These are the possible values for the first level element

           Name           string 
           CTL_DEBUG      debug
           CTL_VFS        vfs
           CTL_HW         hw
           CTL_KERN       kern
           CTL_MACHDEP    machdep
           CTL_NET        net
           CTL_USER       user
           CTL_VM         vm

This is a sample list of some of the values which you can use in the name parameter of the sysctlbyname function.

     Name                            Type           
     kern.ostype                     string       
     kern.osrelease                  string       
     kern.osrevision                 integer      
     kern.version                    string       
     kern.maxvnodes                  integer       
     kern.maxproc                    integer       
     kern.maxfiles                   integer       
     kern.argmax                     integer      
     kern.securelevel                integer       
     kern.hostname                   string        
     kern.hostid                     integer       
     kern.clockrate                  struct       
     kern.posix1version              integer      
     kern.ngroups                    integer      
     kern.job_control                integer      
     kern.saved_ids                  integer      
     kern.link_max                   integer      
     kern.max_canon                  integer      
     kern.max_input                  integer      
     kern.name_max                   integer      
     kern.path_max                   integer      
     kern.pipe_buf                   integer      
     kern.chown_restricted           integer      
     kern.no_trunc                   integer      
     kern.vdisable                   integer      
     kern.boottime                   struct       
     vm.loadavg                      struct       
     vm.swapusage                    struct       
     machdep.console_device          dev_t        
     net.inet.ip.forwarding          integer       
     net.inet.ip.redirect            integer       
     net.inet.ip.ttl                 integer       
     net.inet.icmp.maskrepl          integer      
     net.inet.udp.checksum           integer       
     hw.machine                      string       
     hw.model                        string       
     hw.ncpu                         integer      
     hw.byteorder                    integer      
     hw.physmem                      integer      
     hw.usermem                      integer      
     hw.memsize                      integer      
     hw.pagesize                     integer      
     user.cs_path                    string       
     user.bc_base_max                integer      
     user.bc_dim_max                 integer      
     user.bc_scale_max               integer      
     user.bc_string_max              integer      
     user.coll_weights_max           integer      
     user.expr_nest_max              integer      
     user.line_max                   integer      
     user.re_dup_max                 integer      
     user.posix2_version             integer      
     user.posix2_c_bind              integer      
     user.posix2_c_dev               integer      
     user.posix2_char_term           integer      
     user.posix2_fort_dev            integer      
     user.posix2_fort_run            integer      
     user.posix2_localedef           integer      
     user.posix2_sw_dev              integer      
     user.posix2_upe                 integer      

Note : You can get a full list of the supported values running the sysctl -A command from a Terminal.

Finally this code shows how use the SysCtlByName function to retrieve the number of cpus installed.

function NumberOfCPU: Integer;
var
  res : Integer;
  len : size_t;
begin
  len := SizeOf(Result);
  res:=SysCtlByName('hw.ncpu', @Result, @len, nil, 0);
  if res<>0 then
    RaiseLastOSError;
end;

Note : The sysctl function runs in about a third the time as the same request made via the sysctlbyname, so when is possible uses sysctl instead.

sysctlnametomib

int sysctlnametomib(const char *name, int *mibp, size_t *sizep);

The sysctlnametomib function fill a mib structure using a alias a string. this function is intended for use by apps that want to repeatedly use the same variable.

function sysctlnametomib(name: MarshaledAString; mibp: PInteger; sizep: Psize_t): Integer; cdecl; external libc name _PU + 'sysctlnametomib';

name: ASCII representation of the value to retrieve.
mibp: pointer to the mib structure to fill.
sizep: pointer to the length of the mib structure to fill.

var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
begin
  len := Length(mib);
  sysctlnametomib('hw.physicalcpu', @mib, @len);
  //now the mib structure is filled with the proper values to call the sysctl function. 

Error Handling

All the above functions returns a 0 values when the execution was succefull, otherwise an error code is returned, this code can be obtained using the errno (Posix.Errno) or the GetLastError function.

The following errors may be reported (theses values and meanings are defined in the osx/ErrnoTypes.inc file)

     [EFAULT]           The buffer name, oldp, newp, or length pointer oldlenp contains an invalid address.
     [EINVAL]           The name array is less than two or greater than CTL_MAXNAME.
     [EINVAL]           A non-null newp is given and its specified length in newlen is too large or too
                        small.
     [ENOMEM]           The length pointed to by oldlenp is too short to hold the requested value.
     [ENOMEM]           The smaller of either the length pointed to by oldlenp or the estimated size of the
                        returned data exceeds the system limit on locked memory.
     [ENOMEM]           Locking the buffer oldp, or a portion of the buffer if the estimated size of the
                        data to be returned is smaller, would cause the process to exceed its per-process
                        locked memory limit.
     [ENOTDIR]          The name array specifies an intermediate rather than terminal name.
     [EISDIR]           The name array specifies a terminal name, but the actual name is not terminal.
     [ENOENT]           The name array specifies a value that is unknown.
     [EPERM]            An attempt is made to set a read-only value.
     [EPERM]            A process without appropriate privilege attempts to set a value.

This a full sample console app which summarizes this article.

{$APPTYPE CONSOLE}

uses
  //System.Classes,
  //System.Types,
  //Posix.Errno,
  Posix.SysTypes,
  Posix.SysSysctl,
  System.SysUtils;

//https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/sysctl.3.html
//https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/sysctl.8.html


function NumberOfCPU: Integer;
var
  res : Integer;
  len : size_t;
begin
  len := SizeOf(Result);
  res:=SysCtlByName('hw.ncpu', @Result, @len, nil, 0);
  if res<>0 then
    RaiseLastOSError;
end;


function MaxProcesses : Integer;
var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
begin

 mib[0] := CTL_KERN;
 mib[1] := KERN_MAXPROC;

 len := sizeof(Result);
 res:=sysctl(@mib, Length(mib), @Result, @len, nil, 0);
 if res<>0 then
  RaiseLastOSError;
end;

function MemSize : Int64;
var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
begin
 mib[0] := CTL_HW;
 mib[1] := HW_MEMSIZE;

 len := sizeof(Result);
 res := sysctl(@mib, Length(mib), @Result, @len, nil, 0);
 if res<>0 then
  RaiseLastOSError;
end;


function KernelVersion : AnsiString;
var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
  p   : MarshaledAString;//in XE2 use  PAnsiChar
begin
 mib[0] := CTL_KERN;
 mib[1] := KERN_VERSION;
 res := sysctl(@mib, Length(mib), nil, @len, nil, 0);
 if res<>0 then
   RaiseLastOSError;
 GetMem(p, len);
 try
   res := sysctl(@mib, Length(mib), p, @len, nil, 0);
   if res<>0 then
     RaiseLastOSError;
   Result:=p;
 finally
   FreeMem(p);
 end;
end;


procedure GetClockInfo;
type
 clockinfo = record
	hz      : Integer;
	tick    : Integer;
	tickadj : Integer;
	stathz  : Integer;
	profhz  : Integer;
  end;

(*
struct clockinfo {
	int	hz;	   	/* clock frequency */
	int	tick;		/* micro-seconds per hz tick */
	int	tickadj;/* clock skew rate for adjtime() */
	int	stathz;		/* statistics clock frequency */
	int	profhz;		/* profiling clock frequency */
};
*)

var
  mib : array[0..1] of Integer;
  res : Integer;
  len : size_t;
  clock : clockinfo;
begin
 FillChar(clock, sizeof(clock), 0);
 mib[0] := CTL_KERN;
 mib[1] := KERN_CLOCKRATE;
 len := sizeof(clock);
 res:=sysctl(@mib, Length(mib), @clock, @len, nil, 0);
 if res<>0 then
   RaiseLastOSError;

 Writeln(Format('clock frequency             %d',[clock.hz]));
 Writeln(Format('micro-seconds per hz tick   %d',[clock.tick]));
 Writeln(Format('clock skew rate for adjtime %d',[clock.tickadj]));
 Writeln(Format('statistics clock frequency  %d',[clock.stathz]));
 Writeln(Format('profiling clock frequency   %d',[clock.profhz]));
end;

begin
  try
    Writeln(Format('max processes     %d',[MaxProcesses]));
    Writeln(Format('number of cpus    %d',[NumberOfCPU]));
    Writeln(Format('physical ram size %s',[FormatFloat('#,', MemSize)]));
    Writeln(Format('Kernel Version    %s',[KernelVersion]));
    GetClockInfo;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Delphi Code Monkey: The most awesome thing about Delphi is that it is not C++.

$
0
0

Recently, I was trying to debug some code on my weekend spare time, to help  out a former employer who needs a bug fixed in a C++ application.   I removed an ad-hoc shared memory implementation previously, and substituted a well known and trusted shared memory implementation, called Boost::Interprocess.

I really don't see a reasonable alternative to Boost::Interprocess for C++ shared memory, but I really haven't gained any love for C++ during this exercise.  In fact, my antipathy towards C++ has grown towards what you might call full blown hatred of Modern Boost::style::C++.  I think that I would like to make a T-Shirt that says....

Is that a ...

boost::intrusive::detail::tree_algorithms<
   boost::intrusive::rbtree_node_traits<boost::interprocess::offset_ptr<void,int,unsigned int,0>,1> >::find<
            int,boost::intrusive::detail::key_nodeptr_comp<boost::container::container_detail::rbtree<int,std::pair<int const ,DATAPOINT>,
            boost::container::container_detail::select1st<std::pair<int const ,DATAPOINT> >,std::less<int>,boost::interprocess::allocator<std::pair<int const ,DATAPOINT>,
            boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,
            boost::interprocess::iset_index> > >::key_node_compare<boost::container::container_detail::tree_value_compare<int,std::pair<int const ,DATAPOINT>,std::less<int>,
            boost::container::container_detail::select1st<std::pair<int const ,DATAPOINT> > > >,
            boost::intrusive::rbtree_impl<boost::intrusive::setopt<boost::intrusive::detail::base_hook_traits<boost::container::container_detail::rbtree_node<std::pair<int const ,DATAPOINT>,
            boost::interprocess::offset_ptr<void,int,unsigned int,0> >,boost::intrusive::rbtree_node_traits<boost::interprocess::offset_ptr<void,int,unsigned int,0>,1>,0,
            boost::intrusive::default_tag,3>,boost::container::container_detail::node_compare<boost::container::container_detail::tree_value_compare<int,std::pair<int const ,DATAPOINT>,
            std::less<int>,boost::container::container_detail::select1st<std::pair<int const ,DATAPOINT> > >,boost::container::container_detail::rbtree_node<std::pair<int const ,DATAPOINT>,
            boost::interprocess::offset_ptr<void,int,unsigned int,0> > >,unsigned int,1> > > >(const boost::interprocess::offset_ptr<boost::intrusive::compact_rbtree_node<boost::interprocess::offset_ptr<void,int,unsigned int,0> > const ,
            int,unsigned int,0> & header={...}, const int & key=3, boost::intrusive::detail::key_nodeptr_comp<boost::container::container_detail::rbtree<int,std::pair<int const ,DATAPOINT>,
            boost::container::container_detail::select1st<std::pair<int const ,DATAPOINT> >,std::less<int>,boost::interprocess::allocator<std::pair<int const ,DATAPOINT>,
            boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,int,unsigned int,0>,0>,
            boost::interprocess::iset_index> > >::key_node_compare<boost::container::container_detail::tree_value_compare<int,std::pair<int const ,DATAPOINT>,std::less<int>,
            boost::container::container_detail::select1st<std::pair<int const ,DATAPOINT> > > >,
            boost::intrusive::rbtree_impl<boost::intrusive::setopt<boost::intrusive::detail::base_hook_traits<boost::container::container_detail::rbtree_node<std::pair<int const ,DATAPOINT>,
            boost::interprocess::offset_ptr<void,int,unsigned int,0> >,boost::intrusive::rbtree_node_traits<boost::interprocess::offset_ptr<void,int,unsigned i comp={...}) 

... in your call stack or are you just happy to see me?



How would you feel debugging a line of call-stack items of the above level of complexity, where the above is simply 1 of about 20 similar levels of function calls. Why even print that crap, we may as well go back to debugging via memory addreses in the EXE file, and naming these things checkpoint Fred, checkpoint Charlie and checkpoint Dave.

Anyways, before you go absolutely crazy with Spring4D and try to create an equivalent mess in Delphi now that we have generics which are a lot less powerful than modern C++ templates, but still powerful enough to create a lot of trouble, ask yourself, do you really really want to debug a call stack that has a few dozen   TFactoryProxy<
   TDependencyInversionContainer<
    TRedBlackTreeImplementation,
    TMyFactoryProxyClassBeanMetaClass>>>
expressions when you break it?

Behind the connection: Installing ICS for FireMonkey in Delphi XE4

$
0
0
Arno Garrels recorded a tutorial video showing the installation steps required to make ICS v8 works with FireMonkey in Delphi XE4. Full resolution video here (70 MB, zipped) Download ICS Follow me on Twitter Follow me on LinkedIn Follow me on Google+ Visit my website: http://www.overbyte.be
Viewing all 1725 articles
Browse latest View live