CopyReaderToClientDataSet, Blob and the message “Feature not implemented”

If you have used the method TDBXDataSetReader.CopyReaderToClientDataSet, you probably came across with the message “Feature not implemented”, this exception occurs because the method CopyReaderToClientDataSet can’t copy Blob fields from the DBXReader to the ClientDataSet.

This is no longer a problem, Delphi XE2 Update 2 changed this behavior and now supports Blob text fields when using the CopyReaderToClientDataSet method. The Blog binary support is not implemented yet.

To learn how to use the method CopyReaderToClientDataSet read the article “Copying data from ClientDataSet to DBXReader

Connecting to PostgreSQL through dbExpress ODBC driver

Delphi XE2 and C + + Builder XE2  introduce the new dbExpress ODBC driver, which enable FireMonkey and VCL applications to connect to any database that provides a ODBC driver.

In order to show how it works, I created a FireMonkey project sample that connect to a PostgreSQL database.

As first step, you must install the PostgreSQL ODBC driver on the machine that will run the application. Second, applications compiled as 32-bit, requires the 32-bit ODBC driver, 64-bit applications will require the 64-bit ODBC driver.

Onde installed the driver, create and configure a User DSN or System DSN on the ODBC Data Source Administrator (odbcad32.exe):

Instructions for windows 64-bit

  • For 32-bit applications use c:\Windows\SysWOW64\odbcad32.exe
  • For 64-bit applications use o c:\Windows\system32\odbcad32.exe

Instructions for windows 32-bit

  • Use  c:\Windows\system32\odbcad32.exe

The next step is to create an dbExpress ODBC alias on Data Explorer, where the Database Name parameter is the name of the Data Source created earlier, in my example the Data Source is called PostgreSQL35W. The database, username and password was already defined in the Data Source, but you can override the username and password parameters through the SQLConnection.

Assuming you already know how to use the SQLConnection, DataSetProvider and ClientDataSet or SimpleDataSet ,and DataSource components, just connect all  the components as you usually do. The SQLConnection will look like this:

  object SQLConnection1: TSQLConnection
    ConnectionName = 'PostgreSQLOdbc'
    DriverName = 'ODBC'
    LoginPrompt = False
    Params.Strings = (
      'drivername=ODBC'
      'Database=PostgreSQL35W')
  end

You can use the dbExpress framework when connected to a database through dbExpress ODBC driver. In XE2 the ODBC driver is compatible with Windows, we are evaluating the possibility to support this driver on Mac.

The figure below shows the FireMonkey application connected to PostgreSQL via dbExpress ODBC driver. Now you can connect to any other other database not supported natively by dbExpress.

Download the source code here.

Update 2 for Delphi XE2, C++Builder XE2 and RadPHP XE2 available

Categories:C++Builder, Delphi, PHPTags: , ,

The Update 2 for the XE2 release of Delphi, C++Builder and RadPHP are available on the following links.

You can download Update 2 for Delphi and C++Builder through the “Check for Updates” option.

If you have your Delphi XE2 or C++Builder XE2 IDE installed on XP, you have to download the Update from the registered user page.

RAD Studio XE2 Update 2

RAD Studio XE2 Help Update 1 - http://cc.embarcadero.com/item/28613

RadPHP XE2 Update 2

Creating XML files with Delphi and FireMonkey on Mac

Categories:Delphi, FireMonkeyTags: ,

I got some questions about how to manipulate XML files in FireMonkey applications when running on Mac.

One of the solution for that is to use the Mac API (Cocoa), there are several class and different ways to work with XML on Mac, in this post I’m going to show one way to create an XML file, if you are interested to learn more about the XML class available for Mac, visit the Apple documentation website.

Cocoa provide several classes/interfaces to work with XML, the sample project used on this post use NSXMLDocument, NSXMLElement, NSXMLNode and NSData interfaces and related classes, all of them can be found in the Macapi.Foundation unit.

The NSXMLDocument interface and TNSXMLDocument class are the starting point to create the XML Document, where we define the version and encoding for the XML file.

procedure TFrmXml.CreateXMLButtonClick(Sender: TObject);
var
  XmlDoc: NSXMLDocument;
  Root, Book, Author, Publisher: NSXMLElement;
  Data: NSData;
begin

  // Create Xml Document
  XmlDoc := TNSXMLDocument.Create;
  XmlDoc.setVersion(NSSTR('1.0'));
  XmlDoc.setCharacterEncoding(NSSTR('UTF-8'));

After that we need to define what XML elements (nodes) will be added to the XML, since I’m going to create several elements I created three helper methods (CreateElement and Attribute) to help me with that.

The CreateElement method has two implementations, the first one is very simple, just create a new TNSXMLElement based on the name and value parameters.

function TFrmXml.CreateElement(Name, Value: String): NSXMLElement;
begin
  Result := TNSXMLElement.Create;
  Result.setName(NSSTR(Name));
  Result.setStringValue(NSSTR(Value));
end;

The second CreateElement implementation creates a new element which can contain attributes, in order to pass a NSXMLNode that represent a attribute, we will use the Attribute helper method.

function TFrmXml.CreateElement(Name: String; Attr: NSXMLNode): NSXMLElement;
begin
  Result := TNSXMLElement.Create;
  Result.initWithName(NSSTR(Name));

  if Assigned(Attr) then
    Result.addAttribute(Attr);
end;

Below a short overview about the NSXMLNode class extracted from Apple documentation:

Objects of the NSXMLNode class are nodes in the abstract, logical tree structure that represents an XML document. Node objects can be of different kinds, corresponding to the following markup constructs in an XML document: element, attribute, text, processing instruction, namespace, and comment. In addition, a document-node object (specifically, an instance of NSXMLDocument) represents an XML document in its entirety. NSXMLNode objects can also represent document type declarations as well as declarations in Document Type Definitions (DTDs). Class factory methods of NSXMLNode enable you to create nodes of each kind. Only document, element, and DTD nodes may have child nodes.

Among the NSXML family of classes—that is, the Foundation classes with the prefix “NSXML” (excluding NSXMLParser)—the NSXMLNode class is the base class. Inheriting from it are the classes NSXMLElement, NSXMLDocument, NSXMLDTD, and NSXMLDTDNode. NSXMLNode specifies the interface common to all XML node objects and defines common node behavior and attributes, for example hierarchy level, node name and value, tree traversal, and the ability to emit representative XML markup text.

NSXMLNode could be a comment, text, element and other types in Cocoa, the Attribute helper method ensure we will receive a NSXMLNode of type attribute.

function TFrmXml.Attribute(Name, Value: String): NSXMLNode;
var
  Node: Pointer;
begin
  Node := TNSXMLNode.OCClass.attributeWithName(NSSTR(Name), NSSTR(Value));
  Result := TNSXMLNode.Wrap(Node);
end;

 

Now is time to use the helper methods to create the XML document and at the end use the NSData object to format and save the XML file. This is what the following code does:

  // Create the root doc element including one attribute
  Root := CreateElement('BookStore', Attribute('url', 'http://www.amazon.com'));
  XmlDoc.initWithRootElement(Root);

  // Create the first Book node
  Book := CreateElement('Book', Attribute('Name', 'Steve Jobs'));

  // Create the Author and Publisher elements
  Author := CreateElement('Author', 'Walter Isaacson');
  Publisher := CreateElement('Publisher', 'Simon Schuster (October 24, 2011)');

  // Add the elements to the XML
  Root.addChild(Book);
  Book.addChild(Author);
  Book.addChild(Publisher);

  // Create the second Book node
  Book := CreateElement('Book', Attribute('Name',
    'Clean Code: A Handbook of Agile Software Craftsmanship'));
  Author := CreateElement('Author', 'Robert C. Martin');
  Publisher := CreateElement('Publisher',
    'Prentice Hall; 1 edition (August 11, 2008)');

  // Add the elements from the second Book node to the XML
  Root.addChild(Book);
  Book.addChild(Author);
  Book.addChild(Publisher);

  // Makes the Xml output more human-readable inserting
  // carriage returns and indenting nested elements
  Data := XmlDoc.XMLDataWithOptions(NSXMLNodePrettyPrint);
  Data.writeToFile(NSSTR(XMLLocation.Text), true);

  XmlContent.Lines.LoadFromFile(XMLLocation.Text);

end;

end.

Here the resulting app on Mac OS X:

You can download the source code directly from the RAD Studio demos repository here.

I originally mention that TXMLDocument was not available for Mac, and this is incorrect, thanks Chris Rolliston for the heads up and to migrate my sample to TXMLDocument.

When you drop a TXMLDocument component on your design the default DOMVendor MSXML, you have to change to ADOM XML v4 to support cross-platform. In case you have the unit Xml.Win.msxmldom added on your form you won’t be able to compile for Mac, after you set the DOMVendor to ADOM XML v4 remove that unit from your code and everything will be fine.

Here the version using TXMLDocument:

unit MainForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants, XML.XMLDoc, FMX.Dialogs,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Layouts,
  FMX.Memo, FMX.Edit, FMX.Effects, FMX.Objects, Xml.xmldom, Xml.XMLIntf;

type
  TFrmXml = class(TForm)
    CreateXMLButton: TButton;
    XmlContent: TMemo;
    XMLLocation: TEdit;
    Label1: TLabel;
    Image1: TImage;
    procedure CreateXMLButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  end;

var
  FrmXml: TFrmXml;

implementation

{$R *.fmx}

procedure TFrmXml.CreateXMLButtonClick(Sender: TObject);
var
  XmlDoc: IXMLDocument;
  Root, Book, Author, Publisher: IXMLNode;
begin
  // Create Xml Document
  XmlDoc := TXMLDocument.Create(nil);
  XmlDoc.Active := True;
  XmlDoc.Options := XmlDoc.Options + [doNodeAutoIndent];
  XmlDoc.Version := '1.0';

  // Create the root doc element with one attributes
  Root := XmlDoc.CreateNode('BookStore');
  Root.Attributes['url'] := 'http://www.amazon.com';
  XmlDoc.DocumentElement := Root;

  // Create the first Book node
  Book := XmlDoc.CreateNode('Book');
  Book.Attributes['Name'] := 'Steve Jobs';

  // Create the Author and Publisher elements
  Author := XmlDoc.CreateNode('Author');
  Author.Text := 'Walter Isaacson';
  Publisher := XmlDoc.CreateNode('Publisher');
  Publisher.Text := 'Simon Schuster (October 24, 2011)';

  // Add the elements to the XML
  Root.ChildNodes.Add(Book);
  Book.ChildNodes.Add(Author);
  Book.ChildNodes.Add(Publisher);

  // Create the second Book node
  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)';

  // Add the elements from the second Book node to the XML
  Root.ChildNodes.Add(Book);
  Book.ChildNodes.Add(Author);
  Book.ChildNodes.Add(Publisher);

  XmlDoc.SaveToFile(XMLLocation.Text);

  XmlContent.Lines.LoadFromFile(XMLLocation.Text);

end;

procedure TFrmXml.FormCreate(Sender: TObject);
begin
  XMLLocation.Text := IncludeTrailingPathDelimiter(GetHomePath) + 'create.xml';
end;

end.

With this you have everything you need to manipulate XML file on Windows and Mac.

Publishing providers when registering DataSnap Server Class in runtime

Categories:DelphiTags: ,

A while ago I published a blog post about How to register DataSnap Server Class in runtime with Delphi, after that I got some feedback mentioning the source code I provided just publishes the Server Methods, and not the Providers as part of the Server Module, in other words the client application couldn’t see the Providers available on the DataSnap Server.

The solution for this issue is very simple, we just need to register the TDSProviderDataModuleAdapter class, which is used internally to allow a TProviderDataModule to be exposed to DataSnap clients. TDSProviderDataModuleAdapter acts as a proxy between a server-side TProviderDataModule and client-sides TDSProviderConnection.

We could just update the GetDSClass method implementation as below, it works, but it would not be the best implementation, just because the following code will always publish the IAppServer interface methods to the client side, in case your ServerModule doesn’t have any Provider, why you will publish the IAppServer? Doesn’t make sense, right?

Making the publication parameterizable will bring more flexibility and control to the application and the developer, it will be your decision to publish the Providers or not. A new parameter in the TSimpleServerClass constructor is everything we need.

function TSimpleServerClass.GetDSClass: TDSClass;

begin
  Result := TDSClass.Create(FPersistentClass, False);
  Result := TDSClass.Create(TDSProviderDataModuleAdapter, Result)
end;

The following code represents the new implementation for the TSimpleServerClass. The change adds a new parameter (ExposeProvider) to the constructor, which will allow the developer to publish the providers (or not).

type

unit SimpleServerClass;

interface

uses DSServer, Classes, DSCommonServer, DSReflect;

type

  TSimpleServerClass = class(TDSServerClass)
  private
    FPersistentClass: TPersistentClass;
    FExposeProvider : Boolean;
  protected
    function GetDSClass: TDSClass; override;
  public
    constructor Create(AOwner: TComponent; AServer: TDSCustomServer;
      AClass: TPersistentClass; ExposeProvider: Boolean; ALifeCycle: String);
      reintroduce; overload;
  end;

implementation

constructor TSimpleServerClass.Create(AOwner: TComponent;
  AServer: TDSCustomServer; AClass: TPersistentClass; ExposeProvider: Boolean;
  ALifeCycle: String);
begin
  inherited Create(AOwner);
  FPersistentClass := AClass;
  FExposeProvider  := ExposeProvider;
  Self.Server := AServer;
  Self.LifeCycle := ALifeCycle;

end;

function TSimpleServerClass.GetDSClass: TDSClass;
var
  isAdapted : Boolean;
begin
  isAdapted := FPersistentClass.InheritsFrom(TProviderDataModule);
  Result := TDSClass.Create(FPersistentClass, isAdapted);
  if FExposeProvider and isAdapted then
     Result := TDSClass.Create(TDSProviderDataModuleAdapter, Result)
end;

end.

The following code registers 3 classes, where only one exposes the Providers:

procedure RegisterServerClasses(AOwner: TComponent; AServer: TDSServer);
begin
  Assert(AServer.Started = false, 'Can''t add class to non active Server');

  TSimpleServerClass.Create(AOwner, AServer, TGlobal, False, TDSLifeCycle.Server);
  TSimpleServerClass.Create(AOwner, AServer, TCustomer, True, TDSLifeCycle.Session);
  TSimpleServerClass.Create(AOwner, AServer, TObjectPool, False, TDSLifeCycle.Invocation);
end;

ReportBuilder now available for Delphi XE2, VCL Win32 and Win64

Categories:DelphiTags:

Digital Metaphors the creator of ReportBuilder and our technology partner, announced the new release of Report Builder 14, this release support Delphi XE2, VCL Win32 and Win64.

The following text is an email from Digital Metaphors, which include all the information about this new release.

Subject: Digital Metaphors Corporation announces the release of ReportBuilder 14.0!

ReportBuilder 14.0 includes VCL support for Delphi XE2, Delphi XE, Delphi 2010, 2009, 2007, 2006, and Delphi 7.

Overview: http://www.digital-metaphors.com/rbwiki/General/What’s_New/RB_14

New Feature List: http://www.digital-metaphors.com/PDF/NewFeatures.pdf

Pricing: http://www.digital-metaphors.com/order

Upgrade here! https://www.digital-metaphors.com/secure

Version 14.0 – 10/21/2011
————————-
- Delphi XE2 support for VCL Win32
- Delphi XE2 support for VCL Win64

Design Layers
————-
- separate layout elements into logical layers
- load/save design layers (example std header/footer or watermark)
- prevent users from adding, selecting, moving, deleting elements
- prevent users from seeing, selecting, deleting layers
- LayerTypes: Band, Page
- Inactive Layers draw alpha blended behind active layer
- ComponentOptions: Add, Delete, Select, Move
- LayerOptions: Select, Rename, Visible, Delete, Load, Save

Preview
——-
- continuous page scrolling
- single and two-up page display
- background thread report generation
- hand scrolling
- PreviewFormSetings.PageDisplay property
- PreviewFormSettings.PageIncrement propery (control page generation)
- PreviewFormsSettings.SinglePageOnly (forces use of old screen device)
- Viewer.DeadSpace property (control amount of space outside of page)

Output Devices
————–
- HTML file device
- PDF/A device (http://en.wikipedia.org/wiki/PDF/A)
- PDFSettings.PDFA boolean property (force PDF to PDF/A standard)
- PDF image compression (automatic jpeg compression)
- PDF image scaling (PDFSettings.ImageCompressionLevel)
- PDF, high quality metafile to bitmap conversion
- DocForm device (exact positioning of lines and text)
- RTFSettings.UseTextBoxes boolean property (exact text positioning)
- RTF/Doc device, support for CodePage and Font.CharSet
- XLS support for AppName, Author, Subject, Title (XLSSEttings)
- Email, Extended Mapi plug-in
- avoid security warnings, Unicode and Html support
- Email, support for HTML messages via Indy 10 Extended Mapi
- Email, EmailSettings.HtmlBody, BodyHtmlAttachments properties
- Email, EmailSettings.StreamAttachments option
- Email, Unicode message body (Unicode VCL w/Indy 10 or Extended Mapi)
- new ScrollableScreenDevice, used by Viewer to implement continuous scrolling
- PrintToFile enhancements
- DefaultFileDeviceType defaults to PDF
- default TextFileName, ArchiveFileName ($MyDocuments)\Report.pdf
- Change default file device, auto-changes default file name
- change output format in print dialog, auto-changes default file name
- support for wildcards in default FileName, ($MyDocuments) is resolved
by print dialog for display to end-user
- change the extension in the print dialog, changes the file

International Language support
——————————
- added 64-bit language modules

TeeChart support
—————-
- enhanced TeeChart Package Builder for 64-bit

RCL

- Variable support for Int64 via dtLargeInt data type
- Groups, PreventOrphans
- prevent group header with no detail at bottom of page
- prevent group footer with no detail at top of page
- Columns, ColumnBalancing
- generate vertical columns in min number of rows
- ColumnFooter, AlignToBottom boolean
- enables column footer to generate immediately after detail
- Band.Background property for all bands
- DrawCommand.AsMetaFile feature for simpler RCL add-on support
- SummaryBand enhanced to print on same page as columns

Engine
——
- enhanced group generation – PreventOrphans management
- enhanced horizontal and vertical Column generation

EndUser
——-
- DesignLayers
- LayerOptions: use to lock layers, hide layers
- for example, create a visible/invisible water mark layer
- ComponentOptions: prevent users from moving, deleting etc.
- new OnCustomSaveQueryDoc event
- new ShowHelp method – fires Designer.OnHelp event
- ReportExplorer, invokes Help via new Designer.ShowHelp

RAP

- new Int64 data type
- compiler/parser support for // comments
- enhanced object ref scoping: childreport can access parent report objects
- File functions: FileExists, DirectoryExists, ExtractFileName, etc.
- RTTI for PrintDialog
- RTTI for FileDevice
- RTTI for ObjectList
- RTTI enhanced for Picture, Graphic
- enhanced run-time exception message to display subreport name

Server
——
- added 64-bit windows service (ReportBuilder Services)

*features may vary by ReportBuilder Edition and Delphi version
*RB 14 includes support for seven Delphi versions:
D7, D2006. D2007, D2009, D2010, DXE, DXE2.

Shell Extension for Windows 32-bit and 64-bit with Delphi XE2

Categories:DelphiTags: ,

Now that Delphi XE2 compiles for 64-bit, developers can go beyond Windows 32-bit and start creating Windows Shell Extension for Windows 64-bit. Shell Extensions are in-process COM objects which extends the abilities of Windows OS. In this post I’m going to add two new context menu items in Windows Explorer. The menus will allow users to upload files to Microsoft Azure and Amazon S3. This post will go through how to create the extension, register and compile for 32-bit and 64-bit. I’m preparing another post, in which I will explain how to upload files to Amazon S3 and Microsoft Azure.

Creating the CloudUpload Shell Extension

In order to start creating a Shell Extension in Delphi, you first need to create an ActiveX Library project and after that create a new Automation Object. In the example I use for this post, the project name is CloudUpload and the Automation Object is called TCloudUploadContext. The TCloudUploadContext class must implement the interfaces IShellExtInit and IContextMenu in order to integrate the Context Menu in Windows Explorer.

    { IShellExtInit Methods }
    { Initialize the context menu if a files was selected}
    function IShellExtInit.Initialize = ShellExtInitialize;
    function ShellExtInitialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
      hKeyProgID: HKEY): HResult; stdcall;

    { IContextMenu Methods }
    { Initializes the context menu and it decides which items appear in it,
      based on the flags you pass }
    function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast,
      uFlags: UINT): HResult; stdcall;

    { Execute the command, which will be the upload to Amazon or Azure}
    function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
    { Set help string on the Explorer status bar when the menu item is selected }
    function GetCommandString(idCmd: UINT_PTR; uFlags: UINT; pwReserved: PUINT;
      pszName: LPSTR; cchMax: UINT): HResult; stdcall;

The ShellExtInitialize defines if the Context Menu will appear or not in Windows Explorer. In this sample the context menu only shows up if one file has been selected, otherwise no Context Menu. In case only one file was selected the FFileName variable will receive the name of the file.

function TCloudUploadContextMenu.ShellExtInitialize(pidlFolder: PItemIDList;
  lpdobj: IDataObject; hKeyProgID: HKEY): HResult;
var
  DataFormat: TFormatEtc;
  StrgMedium: TStgMedium;
  Buffer: array [0 .. MAX_PATH] of Char;
begin
  Result := E_FAIL;

  { Check if an object was defined }
  if lpdobj = nil then
    Exit;

  { Prepare to get information about the object }
  DataFormat.cfFormat := CF_HDROP;
  DataFormat.ptd := nil;
  DataFormat.dwAspect := DVASPECT_CONTENT;
  DataFormat.lindex := -1;
  DataFormat.tymed := TYMED_HGLOBAL;

  if lpdobj.GetData(DataFormat, StrgMedium) <> S_OK then
    Exit;

  { The implementation now support only one file }
  if DragQueryFile(StrgMedium.hGlobal, $FFFFFFFF, nil, 0) = 1 then
  begin
    SetLength(FFileName, MAX_PATH);
    DragQueryFile(StrgMedium.hGlobal, 0, @Buffer, SizeOf(Buffer));
    FFileName := Buffer;
    Result := NOERROR;
  end
  else
  begin
    // Don't show the Menu if more then one file was selected
    FFileName := EmptyStr;
    Result := E_FAIL;
  end;

  { http://msdn.microsoft.com/en-us/library/ms693491(v=vs.85).aspx }
  ReleaseStgMedium(StrgMedium);

end;

After the context menu handler is initialized via the IShellExtInit interface, Windows uses the IContextMenu interface to call the other methods of our context menu handler. In this case it will call QueryContextMenu, GetCommandString and InvokeCommand.

The Context Menu options (Amazon S3, Microsoft Azure) will be created through the QueryContextMenu method.

function TCloudUploadContextMenu.QueryContextMenu(Menu: HMENU;
  indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult;
var
  CloudMenuItem: TMenuItemInfo;
  MenuCaption: String;
  SubMenu: HMENU;
  uId: UINT;
begin
  { only adding one menu CloudMenuItem, so generate the result code accordingly }
  Result := MakeResult(SEVERITY_SUCCESS, 0, 3);

  { store the menu CloudMenuItem index }
  FMenuItemIndex := indexMenu;

  { specify what the menu says, depending on where it was spawned }
  if (uFlags = CMF_NORMAL) then // from the desktop
    MenuCaption := 'Send file from Desktop to the Cloud'
  else if (uFlags and CMF_VERBSONLY) = CMF_VERBSONLY then // from a shortcut
    MenuCaption := 'Send file from Shourtcut to the Cloud'
  else if (uFlags and CMF_EXPLORE) = CMF_EXPLORE then // from explorer
    MenuCaption := 'Send file from Explorer to the Cloud'
  else
    { fail for any other value }
    Result := E_FAIL;

  if Result <> E_FAIL then
  begin

    SubMenu := CreatePopupMenu;

    uId := idCmdFirst;
    InsertMenu(SubMenu, AmazonIndex, MF_BYPOSITION, uId, TClouds[AmazonIndex]);

    Inc(uId);
    InsertMenu(SubMenu, AzureIndex, MF_BYPOSITION, uId, TClouds[AzureIndex]);

    FillChar(CloudMenuItem, SizeOf(TMenuItemInfo), #0);
    CloudMenuItem.cbSize := SizeOf(TMenuItemInfo);
    CloudMenuItem.fMask := MIIM_SUBMENU or MIIM_STRING or MIIM_ID;
    CloudMenuItem.fType := MFT_STRING;
    CloudMenuItem.wID := FMenuItemIndex;
    CloudMenuItem.hSubMenu := SubMenu;
    CloudMenuItem.dwTypeData := PWideChar(MenuCaption);
    CloudMenuItem.cch := Length(MenuCaption);

    InsertMenuItem(Menu, indexMenu, True, CloudMenuItem);
  end;
end;

When you are in the Windows Explorer and you pass the mouse over one of the Cloud menu items a short help message is displayed in the Windows Explorer status bar, it is defined on the implementation of the method GetCommandString, which returns a string to the Windows Explorer to display.

function TCloudUploadContextMenu.GetCommandString(idCmd: UINT_PTR; uFlags: UINT;

  pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult;
begin
  Result := E_INVALIDARG;

  { Set help string on the Explorer status bar when the menu item is selected }
  if (idCmd in [AmazonIndex, AzureIndex]) and (uFlags = GCS_HELPTEXT) then
  begin
    StrLCopy(PWideChar(pszName), PWideChar('Copy the selected file to ' +
      TClouds[idCmd]), cchMax);
    Result := NOERROR;
  end;

end;

As the user clicks in one of the Cloud menu items, the method InvokeCommand will be called and start the process to upload the selected file to the Cloud selected. At this point we already have the file name and based on the lpici parameters we can identify what menu item the user clicked.

function TCloudUploadContextMenu.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
var
  Item: Word;
begin
  Result := E_FAIL;

  if HiWord(Integer(lpici.lpVerb)) <> 0 then
    Exit;

  { if the index matches the index for the menu, show the cloud options }
  Item := LoWord(Integer(lpici.lpVerb));

  if Item in [AmazonIndex, AzureIndex] then
  begin
    try
      Upload(lpici.HWND, Item, FFileName);
    except
      on E: Exception do
        MessageBox(lpici.hwnd, PWideChar(E.Message), 'Cloud Upload', MB_ICONERROR);

    end;
    Result := NOERROR;
  end;

end;

In order for the COM object to be created whenever the CloudUpload is loaded, it’s necessary to create an instance of a class factory that specifically creates an instance of the shell extension object, the factory instance will be created on the initialization section, based on the following code, which is a replacement for the default code created by Delphi.

initialization
  TCloudUploadObjectFactory.Create(ComServer, TCloudUploadContextMenu, CLASS_CloudUploadContextMenu, ciMultiInstance, tmApartment);
end.

Since the class factory will be responsible to register/unregister the DLL, the methods ApproveShellExtension and UpdateRegistry will be invoked, it will happen when you use the regsvr32.exe.

  { the new class factory }
  TCloudUploadObjectFactory = class(TAutoObjectFactory)
  protected
    procedure ApproveShellExtension(&Register: Boolean; const ClsID: string);
    function GetProgID: string; override;
  public
    procedure UpdateRegistry(Register: Boolean); override;
  end;

{ TCloudUploadObjectFactory }

{ Required to registration for Windows NT/2000 }
procedure TCloudUploadObjectFactory.ApproveShellExtension(&Register: Boolean;
  const ClsID: string);
Const
  WinNTRegKey =
    'SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved';
var
  Reg: TRegistry;
begin
  Reg := TRegistry.Create;

  try
    Reg.RootKey := HKEY_LOCAL_MACHINE;

    if not Reg.OpenKey(WinNTRegKey, True) then
      Exit;

    { register the extension appropriately }
    if &Register then
      Reg.WriteString(ClsID, Description)
    else
      Reg.DeleteValue(ClsID);
  finally
    Reg.Free;
  end;

end;

function TCloudUploadObjectFactory.GetProgID: string;
begin
  { ProgID not required for shell extensions }
  Result := '';
end;

procedure TCloudUploadObjectFactory.UpdateRegistry(Register: Boolean);
Const
  ContextKey = '*\shellex\ContextMenuHandlers\%s';
begin
  { perform normal registration }
  inherited UpdateRegistry(Register);

  { Registration required for Windows NT/2000 }
  ApproveShellExtension(Register, GUIDToString(ClassID));

  { if this server is being registered, register the required key/values
    to expose it to Explorer }
  if Register then
    CreateRegKey(Format(ContextKey, [ClassName]), '', GUIDToString(ClassID),
      HKEY_CLASSES_ROOT)
  else
    DeleteRegKey(Format(ContextKey, [ClassName]));

end;

Compile for 32-bit or 64-bit platform

At this point we just need to compile the extension, for this sample the Win APIs and RTL methods are the same for both platforms, we don’t need any specific code. You can define the target platform through the Project Manager, by default your project target 32-bit Windows, right click on Target Platforms to add 64-bit Windows.

You can’t register 32-bit dll in 64-bit operation system, and you can not register 64-bit dll in 32-bit operation system. If you are using your develop machine to test, compile for the platform compatible with your OS.

 

Registering the CloudUpload Shell Extension

First, you must Run as Administrator the application you are going to use to register the shell extensions even if you are the Administrator user.

32-bit shell extensions can be registered through the IDE, and command line (cmd) can be used to register 32-bit and 64-bit.

Here the cmd line to register and unregister:

- Run as Administrator the cmd;

- Register the extension using the following command line: regsvr32 <PATH WHERE IS LOCATED THE DLL>CloudUpload.dll

- To unregister the extension using the following command line: regsvr32 <PATH WHERE IS LOCATED THE DLL>CloudUpload.dll /u

After register the DLL you can open the Windows Explorer, select one file and right click, you will see the new menu “Send file from Explorer to the Cloud”. The following screenshot show the shell extension on my Windows 7 64-bit.

You can download he source code in 2 different ways:

In the next post I will explain the Cloud API part of this sample, in the meantime you can play with full sample.

Delphi Certification Program and Delphi XE2 release

Categories:DelphiTags: ,

During the world tour, lots of Delphi developers ask me if the Delphi Certification Program is up to date with the Delphi XE2 release, the answer is YES.

The certification program tests your knowledge in Delphi, we recommend you to use Delphi XE or XE2 release in order to be prepared to answers questions related with the IDE, generics, anonymous methods, DataSnap, unicode, and other areas, which was implemented/improved after 2009 releases.

You can buy the Delphi Certification from our online shop or local partner, If you purchase or upgrade to Delphi XE2 or RAD Studio XE2 by September 30th 2011, you can get a free code to take the Delphi Developer Certification exam.

The Delphi Certification program allows you to distinguish yourself from all the other professionals in this increasingly competitive marketplace.

 

My next 2 RAD Studio XE2 Launch stops

Categories:EventsTags:

During the last 30 days I have been in the road launching the new RAD Studio XE2, so far 14 cities located in 6 different countries and almost 50 thousand miles flown.

Thank you everyone who attended our events around the world, developers are very excited and impressed with all of the new features in this release, like: 64-bit, VCL Styles, DataSnap Connectors, Web apps optimized for mobile devices with RadPHP, LiveBinding, FireMonkey and the ability do develop desktop applications for Windows, Mac and iOS, etc.

View My stops during Rad Studio XE2 World Tour in a larger map

My schedule for this weekend:

  • Tuesday (13/Sep) – Bogota, Colombia – registration and more info here
  • Thursday (15/Sep) – Denver, USA – registration and more info here
Here are some shots from the sessions:
Sao Paulo, Brazil – RAD Studio XE2 World Tour
Mexico City – RAD Studio XE2 World Tour
Guadalajara, Mexico – RAD Studio XE2 World Tour
Chicago, USA – RAD Studio XE2 World Tour

There are more 42 stops for the RAD Studio XE2 World Tour, check the list of cities/countries and register today for the city near you.

Trial, Datasheet, Feature Matrix and FAQ for XE2 release available

Is time to buy RAD Studio XE2 products including Delphi XE2, C++Builder XE2, Embarcadero Prism XE2 and RadPHP XE2, all available now from our online stores and available through our partners around the world.

 

DataSheet Feature Matrix FAQ Trial
RAD Studio XE2 PDF PDF Read Download
Delphi XE2 PDF PDF Read Download
C++Builder XE2 PDF PDF Read Download
RadPHP XE2 PDF PDF Read Download
Prism XE2 PDF PDF Read Download

We plan to have the web site live with all the new product information soon. The official announcement press releases for the tools and FireMonkey will go out then. We thought would be great to let you know that you can get the software a little early. Enjoy!