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

It's a blong, blong, blong road...: More on FireDAC

$
0
0

I’ve yet to pull down and start playing with FireDAC, but it was featured in an Embarcadero event in London yesterday (Thursday). Some points with it were clarified by DavidI, which I thought were quite enlightening:

  • As well as Embarcadero acquiring the intellectual property and all that goes with AnyDAC from DASoft, the original AnyDAC developer, Dmitriy Arefief, is now on a very long-term contract with Embarcadero to keep the development of the product moving forwards.
  • FireDAC is intended as the replacement for dbExpress (DBX). Ultimately dbExpress will be deprecated, but doubtless will still be available for many versions to come (after all, WebSnap is still shipped in the box and hasn’t been focused on for a long time).
  • DataSnap currently relies on dbExpress and Indy. Over time DataSnap will be worked on to make use of FireDAC instead of DBX.
  • FireDAC was attractive to Embarcadero as it covers a much wider range of databases when compared with DBX: Oracle, DB2, SQL Server, InterBase/Firebird, PostgreSQL, MySQL, SQLite, Sybase SQL Anywhere, Advantage DB and Access, along with support for ODBC and dbExpress drivers. It also has a number of useful features, including a performance monitor (ADMonitor), a database explorer (ADExplorer) and a migration tool to convert BDE apps to use FireDAC components.

Additionally, I see in the FireDAC FAQ that if you are not yet an XE3 user you can still make use of FireDAC in older IDEs by installing it with a particular command-line parameter. According to Marco Cantù the command-line for installation into XE2 is /IDE:DXE2. However I’m not sure how many versions back are supported.

Another change with the AnyDAC –> FireDAC transition is the dropping of support for Lazarus/Free Pascal Compiler, so Linux targeting is now out of the question. If you’re a Delphi customer, this is unlikely to ruffle your feathers, but I guess Lazarus users might be more rightfully irked.


See Different: Goodbye Pascal

$
0
0

At 1993, I've started to learn Pascal. It was the first programming language that I actually enjoyed using, and programming was really fun.

I started with TP 5.5, later on with TP 7.0 that I bought myself, and a book. Later it was Delphi (2, 5 and 7).

Since my appearance at the internet, I found myself constantly in a need to "defend" my language of choice, because people didn't understand the language, but had a lot of wrong ideas what it is, and mostly what it can't do.

So I became by my own choice an evangelist for the language itself, and even helped people get started with it.

The problem is that I'm no longer believe in the path of the language, you can call it Delphi, you can call it Object Pascal, it does not matter. I think it goes the wrong way, and instead of able to talk about it with fellow developers, there is constantly arguments, and bickering about it.

I was told that if I raised an issue, it is actually attacking the people, not the features, and it seems no one car to listen inside the "community".

It's not about specific person, but the picture in whole, that I'm deciding (at least for now), for the first time since I first started to use the language, to stop using it.

All my Pascal related work on github remains "as-is", and people can use them if they wish, continue the work etc… But at least for now I will not longer do anything related to it.

So goodbye Pascal, and thanks for the fishes.


Filed under: Delphi, FPC, Object Pascal, חברה, טכנולוגיה, פיתוח, קוד פתוח, תוכנה Tagged: object pascal

TPersistent: Have your PIE and eat it too!

$
0
0

If you consider yourself a developer with principles, then consider this;  the use of TDataSet descendants violates the principle of Encapsulation that is foundational to OOP.

Why?  Well the moment you drop a dataset onto a form and use an event handler, that form is now tied to that dataset.  You might think the fix is to use a datamodule - great idea!  The moment you reference the datamodule from your form, the two are tied inextricably together.  All you have really accomplished is not cluttering up the form with data access components, and introducing the requirement to reference the datamodule in every data related line of code, or start using the dreaded with and lose the ability to mouse hover to inspect code values.  If you think you have separated your data access layer (DAL) from your UI, you have - but only visually.  Want proof? Just try to change your DAL components without changing any behaviors in your forms, or one line of form code.

What are the benefits of OOP?  If you don’t know, then perhaps you’re still writing procedural code.  Delphi’s RAD approach to database development encourages writing such code.  Just because a framework uses objects or components doesn’t mean you have to write object oriented code.  There are 3 pieces to the PIE when it comes to OOP: Polymorphism, Inheritance, and Encapsulation.  Encapsulation enables developers to modify an object in the system without introducing side effects (read code breakage) as long as they don’t change the class interface.  Encapsulation is required to write maintainable code.  No one can remember all the details of a system even if they wrote it, so without encapsulation, breakage is inevitable.

In a typical TDataSet based application, a central datamodule is used and datasets are filtered dynamically to meet specific needs throughout an application.  With the use of data aware controls, the current record of a dataset becomes a significant state to maintain across different forms.  Using field and dataset events further complicates the code base, scattering business logic throughout forms, requiring all methods to be aware of the events used, and why, as well as preserving the call chain if they need to tap into an event.  In short, it leads to an unmaintainable spider web of undocumented dependencies.

Think you are never going to change your DAL so what does it matter?  I guess that means no more Delphi updates for you….

Delphi Haven: Im speechless

$
0
0

Back last July, I blogged about terrible example code posted by Stephen Ball, an Embarcadero ‘Product Evangelist’. Ultimately, the critique of Ball’s code was really just a lead-off for pointing out how the same anti-pattern used had appeared prominently in the FMX source too. Happily, XE3 RTM saw most of that removed (though not all of it). However, for reasons I don’t understand, Ball has now proudly turned his blog post into a YouTube video:

Honestly, view it and weep. I expect his defence will be ‘but I’m only illustrating class helpers’, but if so, that would be pathetic given he’s already been warned the example makes him look foolish [on his original post, the automated pingback from my blog was accepted, but my actual comment - 'I’ve just posted a critical (but friendly) commentary here' - never got past the moderation queue. His reply implied he still read it though]. Moreover, it’s perfectly possible to demonstrate class helpers without writing rubbish – check out the relevant page on Lachlan Gemmell’s TIndex for examples. (*)

That said, the class helper anti-example wasn’t the first time Ball had put out crap – if you want something just as bad, check out his ‘white paper’ on packaging a FMX form into a DLL or dylib, which was also something he originally put out in the XE2 timeframe and has now recently re-promoted. The example used in it is an image file picker, which is fair enough, but here’s how he writes his exports:

  function SelectPicture(AFolder : PChar): PChar; cdecl;
  var
    ResultStr : string;
    Temp: PWideChar;
  begin
    ResultStr := '';
    try
      ResultStr := TfrmImages.SelectPicture(AFolder);
    finally
      Result := StrAlloc(Length(ResultStr));
      Temp := Addr(ResultStr[1]);
      StrCopy(Result,Temp);
    end;
  end;

  procedure DisposePicture(SelectPictureResult : PChar); cdecl;
  begin
    StrDispose(SelectPictureResult);
  end;

If that doesn’t embody the mentality of ‘It compiles, so ship it!’, I don’t know what does.

(*) PS – the David Glassborow articles linked to on the TIndex are now found here and here– I’ve posted the corrections to the TIndex blog, so hopefully the links might even be fixed by the time you read this post.


Delphi Haven: How to survive without image lists in a FireMonkey project

$
0
0

In the VCL, toolbar icons (glyphs) are set up using the TImageList component. In FireMonkey however, TImageList does not exist, and even TToolBar itself is half the component it is in the VCL – rather than manage its own buttons, it’s just a simple container control like TPanel. To add buttons to a FMX TToolBar, you therefore drop TSpeedButtons onto it; and to add glyphs to the buttons, you add a TImage control to each one. On the face of it this leads to a simpler situation that had with the VCL; on the other though, it causes maintenance hassles in non-trivial applications if the same glyph is supposed to be used by different buttons, or if you come to want to change the icon set to a different one, given each image must be changed separately.

Being bothered about this issue, I’ve found relief is at hand from the FireMonkey styling system though. Let’s see it in action:

  • Create a new FireMonkey HD application.
  • Drop a TSpeedButton onto the form, and clear its Text property. Next, add a TPanel (sic.) to the button. Since TSpeedButton won’t let you do this directly, reparent the panel by dragging its node onto the button’s node in the Structure pane (top left).
  • Set the panel’s HitTest property to False, and its Align property to alClient. If you want to be able to drag the button around at designtime, set either the panel’s Padding property to (2, 2, 2, 2) or the button’s Margins property to (2, 2, 2, 2). Note the meaning of Padding and Margins is (weirdly) flipped round compared to the VCL – this is something I still haven’t got used to myself!
  • Add a TLayout to the form, and set its Visible property to False.
  • Add a TImage to the layout, load a suitable ‘Open File’ glyph into its Bitmap property, and set its StyleName to ‘FileOpenGlyph’. For ease of identification in the designer, set its Name property to ‘FileOpenGlyph’ too.
  • Set the panel’s StyleLookup property to ‘FileNewGlyph’. Note you’ll have to type it, as the name won’t be in the drop-down list.

At the end of all that, you should have something like this:

GlyphStyleDemo

How this works is that TPanel, as a styled control (TStyledControl descendant), gets its visual appearance from another control, or more exactly, a copy of another control – the ‘style’. This other control can be anything – all that is crucial is that its StyleName property is set to a value matched by the value of the StyleLookup property of the intended style subject(s).

Normally, custom styles are set up via a TStyleBook component. Using a style book means using the IDE’s FireMonkey style designer though, which is something only a programming masochist can enjoy. What isn’t so obvious is that you don’t actually need to use TStyleBook in the first place however – any control that has its StyleName property assigned will be available as a custom style. So, doing just that is what we did in the demo.

Now, we placed the source TImage on a hidden TLayout given we didn’t want it to be shown at runtime – setting its own Visible property to False wasn’t an alternative, since that would have caused the control it styled to be invisible at runtime too! However, it may be the case that you would prefer not to clutter a form with the source glyphs at all. In a VCL scenario, this would mean putting the application’s image lists onto a data module, but given TImage instances are controls, we can’t do that here. Instead, we need to put the glyph images onto a dummy form, auto-created but not ever visible. In my own case I had a further requirement: the ability to switch at runtime to a different glyph set. This was necessary because I’m looking to support both dark and light UI styles, and currently working with monochrome icons. While that may change later, there was no harm in ensuring everything would work if it didn’t.

So, continuing with the demo, add another form to the project, name it ‘StdGlyphsForm’, and save its unit as App.Glyphs.Std.pas. Then, go back to the first form, cut the TImage to the clipboard, then paste it onto StdGlyphsForm. Because we do not want the new form to become the application’s ‘main’ form in the VCL/FMX sense, do not have it auto-created in the normal fashion (Project|Options, Forms). Instead, it needs to be created explicitly when the application starts up, and directly (i.e., using TGlyphsForm.Create not Application.CreateForm).

Before we get to that though, let’s set up an alternative icon set to test with. To do that, save everything, then duplicate App.Glyphs.Std.pas via File|Save As…; save it as App.Glyphs.Alt.pas, then rename the form to AltGlyphsForm. After that, change the contents of the image, then add the original version of the unit (App.Glyphs.Std.pas) back to the project as well. In principle, we can now choose which icon set to use at runtime by instantiating the appropriate form class (StdGlyphsForm or AltGlyphsForm); if we wanted to then switch sets at runtime, we can just free the first glyphs form, instantiate the second, and ask all ‘normal’ forms to update their styling.

When targting OS X pursuing such a plan works fine, but when targeting Windows there’s a problem: specifically, at one point in FMX.Platform.Win.pas, Screen.Forms[0] is used in lieu of Application.MainForm when the latter cannot be guaranteed to be initialised yet. Because of this, the first form that gets created – regardless of how – becomes the ‘owner window’ for subsequent forms at the Windows API level. If the first form created were to be one of our glyph container forms, bad things will then result.

One way to get around this is to ensure the real ‘main form’ creates the glyph container. However, this assumes you have a ‘main’ form in the first place, which when targeting OS X especially isn’t necessarily the case. After fiddling about the problem, my preferred solution at present is to create the glyph container, move its contents to something else that isn’t a form too (just a bare TComponent instance will do), then immediately free it. So, let’s do that with the demo, wrapping the necessary code in a simple singleton type.

For this, add a new unit to the project, and call it App.Glyphs.pas. Then, copy the following code into its interface section:

uses
  System.Classes;

type
  TGlyphsStyle = (gsStandard, gsAlternative);

  GlyphsManager = record
  strict private class var
    FGlyphsOwner: TComponent;
    FGlyphsStyle: TGlyphsStyle;
    class constructor Initialize;
    class destructor Finalize;
    class procedure SetGlyphsStyle(Value: TGlyphsStyle); static;
  public
    class property GlyphsStyle: TGlyphsStyle read FGlyphsStyle write SetGlyphsStyle;
  end;

Next, make its implementation section look like this:

uses
  FMX.Types, FMX.Forms, FMX.Styles, App.Glyphs.Std, App.Glyphs.Alt;

class constructor GlyphsManager.Initialize;
begin
  FGlyphsOwner := TComponent.Create(nil);
  FGlyphsStyle := gsAlternative;
  SetGlyphsStyle(gsStandard);
end;

class destructor GlyphsManager.Finalize;
begin
  FGlyphsOwner.Free;
end;

class procedure GlyphsManager.SetGlyphsStyle(Value: TGlyphsStyle);
var
  Comp: TComponent;
  I: Integer;
  SourceClass: TComponentClass;
  Source: TComponent;
begin
  if Value = FGlyphsStyle then Exit;
  case Value of
    gsStandard: SourceClass := TStdGlyphsForm;
    gsAlternative: SourceClass := TAltGlyphsForm;
  else
    Assert(False);
    SourceClass := nil; //avoid compiler warning
  end;
  FGlyphsOwner.DestroyComponents;
  Source := SourceClass.Create(nil);
  try
    for I := Source.ComponentCount - 1 downto 0 do
    begin
      Comp := Source.Components[I];
      FGlyphsOwner.InsertComponent(Comp);
      if Comp is TFmxObject then
      begin
        TFmxObject(Comp).Parent := nil;
        if Comp is TControl then
          TControl(Comp).SetNewScene(nil); //see below
      end;
    end;
  finally
    Source.Free;
  end;
  FGlyphsStyle := Value;
  { next lines only necessary because we aren't updating
    the style as such as well }
  for I := Screen.FormCount - 1 downto 0 do
    Screen.Forms[I].UpdateStyle;
end;

The code here should be pretty straightforward – when the glyph set is to be changed, the old glyphs are first destroyed, the new ones loaded, and the application’s forms asked to refresh their and their controls’ styling. The only slightly tricky thing is how we change an existing component’s owner – since the Owner property is read-only, we need to call InsertComponent on the new owner instead, the implementation of which first deassigns any existing owner. Further, since a FireMonkey control, like a VCL one, has both a parent and an owner, and both will destroy the control when the parent or owner is itself destroyed, we need to clear the parent too. Lastly, due to an oversight in the FireMonkey source, a further reference – to the ‘scene’ – needs to be manually cleared as well. If you check out FMX.Forms.pas, you’ll find TCustomForm calls SetNewScene(Self) in DoAddObject, but not SetNewScene(nil) in DoRemoveObject. The resulting dangling reference then causes access violations if not cleared.

To put the new unit in action, head back to the main form of the demo (i.e., the one with the button on it), remove the now empty TLayout, and add a couple of radio boxes called rdoStandard and rdoAlternative. Set the first’s IsChecked property to True in the Object Inspector, then handle its OnChange event as thus:

procedure TForm1.rdoStandardChange(Sender: TObject);
begin
  if rdoStandard.IsChecked then
    GlyphsManager.GlyphsStyle := gsStandard;
end;

Similarly, handle rdoAlternative’s OnChange event like this:

procedure TForm1.rdoStandardChange(Sender: TObject);
begin
  if rdoAlternative.IsChecked then
    GlyphsManager.GlyphsStyle := gsAlternative;
end;

Finally, add App.Glyphs to the form’s implementation section uses clause (Alt+F11) and run the application. If all goes well, clicking on the radio boxes should toggle the button glyph:

GlyphStyleDemo - standard
GlyphStyleDemo - alternative

One thing to watch out for is that if you save everything, close and then reopen the project and have the form open in the IDE without either StdGlyphsForm or AltGlyphsForm too, the button’s styling will revert to a normal TPanel. This is nothing to worry about – close the form, open one of the glyph forms, then reopen the form, and the image will be back.

At the end of the day, I’m pretty happy with all this. While there was the odd irritating oversight in the FireMonkey source to work around, overall I find the style system here producing a more convenient approach that the VCL – in particular, it’s much nicer to be able to refer to a glyph by name rather than a meaningless numerical index. Moreover, the cosmetic issue of the custom style apparently being ‘lost’ when the main form is open without one of the glyph forms alongside is just that – cosmetic. In contrast, if the form were a VCL one with a TToolBar referencing a TImageList on a data module, the IDE would silently clear the actual reference!

Postscript – using TSubImage

As a final note, there is one variant of my approach that you might wish to consider. This is to replace the individual TImage instances with individual TSubImage ones, which then reference a single big TImage (TSubImage isn’t automatically registered, annoyingly enough, so you’ll need to do what Jeremy North did for TStyleTag here first). The reason is that when a control is used as a style, a copy of it is taken – and one aspect of the copy in a TImage case will be the contents of its Bitmap property.

If this were the VCL that wouldn’t be an issue due to the fact TBitmap there implements a reference counting system internally. In other words, when you do DestBitmap.Assign(SourceBitmap) in a VCL application, no ‘deep copy’ of the source bitmap is made, similar to how DelphiString2 := DelphiString1 doesn’t cause character data to be duplicated ‘there and then’ – rather, copying only happens when one of the two bitmaps or strings is modified later (the jargon for this is ‘copy on write semantics’). Unfortunately, the FMX TBitmap does not implement this behaviour though, and because of that, doing DestBitmap.Assign(SourceBitmap) causes an immediate ‘deep copy’ of the source, duplicating the bitmap bits in memory. For a small application with a small number of glyphs that won’t matter, and perhaps it won’t matter much either in a bigger program. As an application grows you might want to at least consider swapping out the individual TImage controls for TSubImage ones though. Thanks to the flexibility of the styling system, the task shouldn’t be too hard – all existing style associations can be kept intact.


Delphi Haven: Review: Getting Started with Lazarus IDE by Roderick Person

$
0
0

Recently I came across a new Lazarus book, Getting Started with Lazarus IDE, by Roderick Person (that’s not a typo by the way – there is no definite article in the title). This is a small book (only a hundred pages) put out by Packt, a small publisher with a growing catalogue. While you can purchase just the ebook, I bought the printed/ebook package from the Packt website, which still only cost £15.

The book is presented as being for Delphi programmers interested in Lazarus in the first instance, and any other developers used to RAD environments in the second. As such, it begins by walking through how to install Lazarus on Linux, Windows, OS X and FreeBSD. Following this, the next thirty-five pages or so (up until half way) are taken up by a quick tour of the IDE itself, brief descriptions of the core components of the Lazarus Component Library (LCL), discussions of how to create ‘hello world’ console and GUI applications, an overview of the Lazarus debugger, and an introduction to the IDE’s rename refactoring.

While nicely written, I found this section a little uneven – words are spent on introducing the Object Inspector, for instance, when this is something that works identically to the Delphi version. Conversely, the Project Inspector – Lazarus’s substitute for what on the face of it is Delphi’s rather more fully featured Project Manager – is not mentioned at all, despite actually appearing in a screenshot.

As an aside, while this is no reflection on the author, I did find Person’s description of how to create a Lazarus console application remarkable given how much code was needed simply to output ‘Hello World’. While good ol’

program HelloWorld;

begin
  WriteLn('Hello world');
end.

is still possible, the IDE defaults to bringing in a rather overwrought console application framework. This leads Person having to devote six pages to walking though how to write a console-based ‘Hello World’ followed by less than two for the GUI version!

Once the book gets to half way, chapter 4 tackles the topic of how to convert a Delphi/VCL application to Lazarus and the LCL. I found this chapter very well pitched, with many useful tips presented. Following it, chapter 5 provides an introduction to Lazarus custom component writing. I confess I came out of this being slightly amazed at how the Lazarus team have managed to keep their photocopiers working even when it comes to core parts of Delphi’s Open Tools API! The final two chapters of the book then provide a valuable overview of the Lazarus Documentation Editor (LazDE), followed by a somewhat less obviously useful account of rebuilding the Lazarus IDE to use the GDK+ widgetset on Windows rather than native Win32 controls.

Overall, I found the book easy to read, and with two very solid chapters (4 and 6). At other times I found it a little inconsistent in its focus however – in particular, while the best chapters were clearly aimed towards the book’s stated primary audience, namely Delphi developers, others seemed to assume very little prior Delphi knowledge. Because of that, parts of the book can cover things that most experienced Delphi developers will know off of the back of their hands. A related issue is that the author doesn’t appear to have used any modern version of Delphi. For example, when first presenting the Lazarus IDE, a comment is made about how similar it looks to the Delphi IDE – which is true, but only if you are using Delphi 7 or earlier. Likewise, mention is made when discusing 64 bit compatibility that the PtrInt/PtrUInt types should be used instead of Integer when you need to typecast from a pointer to an integer or vice versa. While that’s good advice in itself, Person doesn’t seem to be aware that Delphi defines NativeInt/NativeUInt types for the same purpose, and has done so for quite a few versions now.

Nonetheless, this shouldn’t detract from the fact that when the book maintains a clear focus, it presents its material well. Also, while it is small, it is also cheap, so if you’re interested in Lazarus, check it out.


From Zero To One: Example of Cromis Library usage

$
0
0

What good is a Library of code if you have no use for it, or if the use is so clumsy you don’t want to use it. Almost all the code in Cromis Library was written out of my personal need to solve some problems. The main thing I strive to achieve besides speed and efficiency is ease of use and high level abstraction. I like code that on one hand writes almost like a script language and still gives you full power down to the metal on the other. Today I had to quickly write a program to solve the following problem:

I had two directories full of XML files.  Each XML represents a survey. In one directory I had completed surveys and in the other I had sessions of those surveys. Each survey can be made in one or more sessions. The only ID I could match between them was SampleID (incremental number of the survey). I had some missing data for some external data linking, so I had to pull session ID from session XML files to the finished survey files. The steps are like that.

  1. Go through all the session files
  2. Sort them by save time
  3. Store the file names in cardinal hash table. If two or more exist with same SampleID, only use the one that is the oldest. That is why the sort.
  4. Go through all the finished surveys files
  5. For each XML check if the data is missing and if it is get it from hash table
  6. If file was changed write it back to HD

Ok you may think this should be a lot of code to write. Well it is not if you use my tools as I did. I uses AnyValue, IAnyArray, SimpleStorage and Cardinal HashTable.

procedure TForm2.Button1Click(Sender:TObject);var
  TempHashList: TCardinalHashTable;
  TempArray: IAnyArray;
  ValueItem: PAnyValue;
  Document: IDocument;
  AnyValue: TAnyValue;
  SampleID:Integer;
  Answer: IElement;begin
  TempHashList := TCardinalHashTable.Create;try
    TempArray := CreateAnyArray; 
    CreateCollection(eSourceTemp.Text).Get('Parameters/SampleID',procedure(const Document: IDocument;const Element: IElement;const Data:Pointer)begin
        AnyValue := TAnyValue.Null;
        AnyValue['Timestamp']:= Document.Data.GetAttr('SaveDate').AsDateTime;
        AnyValue['SampleID']:= Document.Data.Get(['Parameters','SampleID']).AsInteger;
        AnyValue['FullName']:= Document.Path;
        AnyValue['Filename']:= Document.Name;
        TempArray.Push(AnyValue);end,nil); 
    TempArray.Sort(function(Item1, Item2: PAnyValue):Integerbegin
        Result := CompareDateTime(Item1^['Timestamp'].AsDateTime,
                                  Item2^['Timestamp'].AsDateTime);end); 
    for ValueItem in TempArray.Enum.Reversedo
      TempHashList.Item[ValueItem^['SampleID'].AsInteger]:= ValueItem^; 
    for Document in CreateCollection(eSourceData.Text)dobeginifnot Document.Data.Exists(['Answers','ZAPIS_WID'])thenbegin
        SampleID := Document.Data.Get(['Parameters','SampleID']).AsInteger; 
        if TempHashList.ContainsKey(SampleID)thenbegin
          Answer := Document.Data.Ensure(['Answers','ZAPIS_WID']);
          Answer.EnsureAttr('et').AsInteger:=0;
          Answer.EnsureAttr('date').AsDateTime:= Now;
          Answer.EnsureAttr('source').AsString:='setvar';
          Answer.EnsureAttr('srcid').AsInteger:=2;
          Answer.AsString:= TempHashList.Item[SampleID]['Filename'];
          Document.SaveChanges;end;end;end;finally
    TempHashList.Free;end;end;

Sure this could be done with something else also. Probably there are tools out there to achieve the same, or not. I don’t know. I wrote this as an example, on how to use the code, to solve problems with as little code as possible. The only thing I still lack, as I solve XML problems very often is a true script interface for all this so no actual compile would be needed. It would be nice to have :) By the way do you even see in the code that I work with XML files in a directory or that I work with XML at all? Also the array of TAnyValue is very abstract, no a simple dynamic array.

And as I said in previous posts, TAnyValue is becoming the central powerhouse for all this. Tomorrow I will post the new version of TAnyValue and IAnyArray that now uses the new improved sliced array data structure.

The Wiert Corner - irregular stream of stuff: jpluimers

$
0
0

Few people know about a Delphi language feature that has been present since Delphi 1: prepending the type definition with a type keyword to make the type getting a new identity.

Each time I use it, I have to do some browsing for the consequences, and this time I wrote down some notes and created a small example program (source is also below).

This time I needed it when writing class wrappers on top of the Delphi bindings for WebSphere MQ.

WebSphere MQ has Queues where you can put and get messages. It also has Queue Managers to which you connect, and that provide queuing services and manages queues.

Both Queues and Queue Managers have names that can be up to 48 (single byte) characters long.
Those names mean totally different things, so though the have similar data types, they have a different identity.

The same holds for 20 byte character arrays (they can be used as names for ChannelNameShortConnectionName and MCAName). The 264 byte character array is so far used for ConnectionName only.

Distinguishing those types: That’s what “type types” in Delphi are all about.

The DocWiki tries to explain this in the Type Compatibility and Identity (Delphi) topic:

  • Type Identity: if you add the keyword “type” in a declaration, that type gets a new identity
  • Type Compatibility: there are rules about type compatibility
  • Assignment Compatibility: variables of incompatible types can be assigned from an expression if the type of that expression is compatible when the type of the variable.

Type Identity has to do with RTTI: in the example below, MQCHAR48, TMQQueueManagerName and TMQQueueName have different identities, so there is different RTTI generated for them.

Assignment compatibility has to do both with the RTTI, and how relaxed some fundamental types are handled by the compiler.

The documentation on assignment incompatibility is incomplete:

  • There is a very important difference between string constants and string variables:
    • You can assign the various character array type values to a string variable, but not vice versa
    • You can assign an implicit string constant to  any of the character array type variables
  • The character array type variables are not assignment compatible with each other

The latter was very important to me for two reasons:

  1. now I can make sure you can not assign TMQQueue.Name to TMQQueueManager.Name (even if instances of these two have similar names),
  2. and the different RTTI guarantees I can hook different property editors to each property.

A few notes:

  • This is not limited to my examples that uses character arrays (as that’s what I needed in my particular case).
    You can do the same with other types like integers, characters. pointers, etc.
  • For integer type types the assignment compatibility is less restrict than for character arrays (example “TImageIndex = type Integer;”)
  • There is a very special usage when doing “type AnsiString(CodePage)“. Without the “type” the result would not have identity and would be assignment compatible. With CodePages, you want to limit assignment compatibility.
    These types declared in the RTL each have their own identity:

–jeroen

via:

program TypeIdentity;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils;

type
  Range20 = 0..19;
  Range48 = 0..47;
  Range264 = 0..263;
  SingleByteChar = AnsiChar; // Single-Byte Character; WebSphereMQ does not support Unicode
  MQCHAR = SingleByteChar;
  MQCHAR20 = array[Range20] of MQCHAR;
  MQCHAR48 = array[Range48] of MQCHAR;
  MQCHAR264 = array[Range264] of MQCHAR;

  TMQConnectionName = type MQCHAR264;
  TMQChannelName = type MQCHAR20;
  TMQQueueManagerName = type MQCHAR48;
  TMQQueueName = type MQCHAR48;

const
  SYSTEM_DEFAULT_MODEL_QUEUE = 'SYSTEM.DEFAULT.MODEL.QUEUE'; // Default model queue.

  SYSTEM_DEFAULT_MODEL_QUEUE_Name: TMQQueueName = ('S','Y','S','T','E','M','.','D','E','F','A','U','L','T','.','M','O','D','E','L','.','Q','U','E','U','E',
    ' ',' ', // http://publib.boulder.ibm.com/infocenter/wmqv7/v7r0/index.jsp?topic=%2Fcom.ibm.mq.csqzaj.doc%2Fsc10300_.htm
    ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
    ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ');

function PadRight(const Value: AnsiString; const ResultLength: Integer): AnsiString;
var
  Index: Integer;
begin
  Result := Value;
  SetLength(Result, ResultLength);
  for Index := Length(Value)+1 to ResultLength do
    Result[Index] := ' ';
end;

function ToMQQueueName(const Value: AnsiString): TMQQueueName;
var
  PaddedValue: AnsiString;
  ResultSize: Integer;
begin
  ResultSize := SizeOf(Result);
  PaddedValue := PadRight(Value, ResultSize);
  Move(PaddedValue[1], Result, ResultSize);
end;

var
  S: AnsiString;
  QueueName: TMQQueueName;
  QueueManagerName: TMQQueueManagerName;
  QueueName48: MQCHAR48;

begin
  try
    S := SYSTEM_DEFAULT_MODEL_QUEUE;
    S := SYSTEM_DEFAULT_MODEL_QUEUE_Name;

    QueueName := SYSTEM_DEFAULT_MODEL_QUEUE;
    QueueName := SYSTEM_DEFAULT_MODEL_QUEUE_Name;

    QueueManagerName := SYSTEM_DEFAULT_MODEL_QUEUE;
    QueueManagerName := SYSTEM_DEFAULT_MODEL_QUEUE_Name; // 68

    QueueName48 := SYSTEM_DEFAULT_MODEL_QUEUE;
    QueueName48 := SYSTEM_DEFAULT_MODEL_QUEUE_Name; // 71

    QueueManagerName := QueueName;   // 73
    QueueName := QueueManagerName;   // 74

    QueueName48 := QueueManagerName; // 76
    QueueManagerName := QueueName48; // 77

    S := QueueName;
    S := QueueManagerName;
    S := QueueName48;

    QueueName := ToMQQueueName(S);

    QueueName := S;         // 85
    QueueManagerName := S;  // 86
    QueueName48 := S;       // 87
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

[DCC Error] TypeIdentity.dpr(68): E2010 Incompatible types: 'TMQQueueManagerName' and 'TMQQueueName'
[DCC Error] TypeIdentity.dpr(71): E2010 Incompatible types: 'MQCHAR48' and 'TMQQueueName'
[DCC Error] TypeIdentity.dpr(73): E2010 Incompatible types: 'TMQQueueManagerName' and 'TMQQueueName'
[DCC Error] TypeIdentity.dpr(74): E2010 Incompatible types: 'TMQQueueName' and 'TMQQueueManagerName'
[DCC Error] TypeIdentity.dpr(76): E2010 Incompatible types: 'MQCHAR48' and 'TMQQueueManagerName'
[DCC Error] TypeIdentity.dpr(77): E2010 Incompatible types: 'TMQQueueManagerName' and 'MQCHAR48'
[DCC Error] TypeIdentity.dpr(85): E2010 Incompatible types: 'TMQQueueName' and 'AnsiString'
[DCC Error] TypeIdentity.dpr(86): E2010 Incompatible types: 'TMQQueueManagerName' and 'AnsiString'
[DCC Error] TypeIdentity.dpr(87): E2010 Incompatible types: 'MQCHAR48' and 'AnsiString'

–EOF–


Filed under: Delphi, Delphi 1, Delphi 2005, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi 3, Delphi 4, Delphi 5, Delphi 6, Delphi 7, Delphi 8, Delphi x64, Delphi XE, Delphi XE2, Delphi XE3, Development, Software Development

Firebird News: Daily Firebird builds and tests results

$
0
0
You can watch the for Daily Firebird builds and tests suite results created on a Ubuntu 12.04 LTS http://ci.ibphoenix.fr/ There are results for G++ compiler warnings Also the console output can be consulted for the running and finished QA tests

Firebird News: Firebird security fixes pushed to Fedora and EPEL 5,6

$
0
0
An important security fix for Firebird is pushed to Fedora and EPEL Please update your servers

Firebird News: Security Updates available for Mageia 2 and Cauldron

$
0
0
New Firebird packages that fix security bug #9322 in Mageia are available for Mageia 2 and Cauldron

Firebird News: Custom conventions in Entity Framework 6 helping Firebird – part 2

$
0
0
Few days ago I wrote a post “Custom conventions in Entity Framework 6 helping Firebird“.Arthur Vickers from Entity Framework team had a good question whether it works also for columns and tables that are generated by Entity Framework (like join tables for M:N, FK columns (if not in model), etc.). And it actually does not.  For this you have [...]

The road to Delphi: Added new vcl style hook to the Vcl Styles Utils to fix QC #108678, #108875 (XE2 and XE3)

$
0
0

I just added a new vcl style hook (TListViewStyleHookFix) for the TListView component in the Vcl Styles Utils project to fix the QC #108678, #108875 (XE2 and XE3)

The issue reported in both reports, is that the images are not displayed in the TListView header with the VCL Styles enabled.

When you uses the Windows Theme in a TListView with images in the header will look like so

LVWindows

But if you enable the Vcl Styles, the images in the header are lost.

LVStyles2

The issue is located in the TListViewStyleHook.DrawHeaderSection method, this method must paint the image and text of each section of the header of the ListView.

This is part of the code with the bug

  ...
  ...
  ImageList := SendMessage(Handle, HDM_GETIMAGELIST, 0, 0);
  Item.Mask := HDI_FORMAT or HDI_IMAGE;
  InflateRect(R, -2, -2);
  if (ImageList <> 0) and Header_GetItem(Handle, Index, Item) then
  begin
    if Item.fmt and HDF_IMAGE = HDF_IMAGE then
      ImageList_Draw(ImageList, Item.iImage, Canvas.Handle, R.Left, R.Top, ILD_TRANSPARENT);
    ImageList_GetIconSize(ImageList, IconWidth, IconHeight);
    Inc(R.Left, IconWidth + 5);
  end;
  ...
  ...

The problem with the above code is that the SendMessage function with the HDM_GETIMAGELIST message (which is used to get the current imagelist) is not using the proper Handle. The above code is passing the handle of the ListView, but must pass the handle of the Header control, the same applies to the call to the Header_GetItem method.

The TListViewStyleHookFix introduces a new DrawHeaderSection method which passes the handle of the header control and fix the issue. You can use this Stylehook adding Vcl.Styles.Fixes unit to you uses clause and then register the hook on this way.

initialization
   TStyleManager.Engine.RegisterStyleHook(TListView, TListViewStyleHookFix);

LVStylesFix


The Wiert Corner - irregular stream of stuff: jpluimers

$
0
0

Every once in a while I manage to check “Automatically close on successful compile” during compilation, the compiler progress disappears, and I loose my clue if compilation ended or not.

This is how to fix it:

  • find the registry portion of your Delphi version, under either of these
    - HKEY_CURRENT_USER\Software\Borland\BDS\#.0
    - HKEY_CURRENT_USER\Software\CodeGear\BDS\#.0
    - HKEY_CURRENT_USER\Software\Embarcadero\BDS\#.0
    Where #.0 is your version number from this Delphi Release Dates page.
  • Under the key “Compiling”, find the string value named “Auto Close Progress Dialog”  and change it from “True” to “False”

–jeroen

via: Embarcadero Newsgroup Archive :: embarcadero.public.delphi.ide :: Re: D2006 compiler progress.


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

From Zero To One: Improved Sliced Array implementation

$
0
0

If you don’t know what sliced array is, I suggest that you first read the previous post, where I presented the sliced array. There I presented the data structure that is similar to classical dynamic array, but is more efficient for deletes and inserts. Also, adding elements is a bit faster, because memory allocation is more efficient. But the downside was that accessing item by index was slower then with classical dynamic array. And because accessing the item by index is the most important feature of such a structure, I was not satisfied with the results. So as usual, I looked for a way to do it better while still being efficient for deletes and inserts. And I did it, I found a way. First lets see what changed from previous implementation.

SlicedArray2

As you can see a lot changed. I threw out most of the pointers for doubly linked lists that were actually not needed. I also changed how each slice is implemented. The biggest problem with the old implementation was, that the slices were of variable sizes. That proved it more difficult to access the slice by random index. I could find it very fast due to the tricks I wrote about, but not fast enough. 2-3 times slower access by index is not something I was happy about. Furthermore using the helper pointer to remember last accessed slice made even reading thread unsafe. That was unacceptable. So I though of another solution. The slices need to be of equal size that is a must, no way around that if I want to be super efficient. But how do we then solve deletes and inserts. If I have to maintain equal slice size, that means I have to move items in all slices if I delete or insert one item. Well at least in slices above the targeted one. That way I gain nothing. Then I thought about a neat solution, I remembered how a typewriter works :)

What if we add some buffer space bellow and above the slice limits. This way if we insert or delete an item, we only have to move one item per slice. To demonstrate, if we insert an item into the N-th slice, we have to move all the items in that slice for one item upwards. Then, we have to move the last item of the N-th slice to the next slice to maintain equal item count. But because we have some buffer space in each slice, we can simply append the item before next slice items and we don’t have to move other items at all! Then just repeat that for each slice to the end. So lets say we have 100 slices. if we insert an item into the 10-th slice we only have to move items in that slice and then just move one item in each of the 90 slices upwards. No need to move items in all 90 slices. I we run out of buffer space in one of the slices, then we move all items to the middle again to reposition them. The bigger the buffer the more operations we can do, before we must move again. Just like the typewriter. It shifts to the right and when we come to the end of the paper we reposition it again. We don’t reposition for each letter.

It turns out this is very efficient. We don’t have many slices. For instance typical setup I use in testing is for one million items. Each slice has 10.000 items, so I have 100 slices. Each slice has for 1000 items of buffer space, that is 10% and it means I have 500 items buffer bellow and above slice limits. I tested with 500 items buffer and the speed was still mostly the same. We don’t need a lot of buffer space. Its simple math really. If you have only 10 items of buffer on each side you already are 10 time more efficient on deletes and inserts. The record structure for the slice now come down to this:

   TArraySlice = record

    Last:Integer;
    Start:Integer;
    Index:Integer;
    Data: TAnyValues;end;

And the lookup control is now simply:

  PSliceData =^TSliceData;
  TSliceData =arrayof PArraySlice;

So quite simplified. The tests show great improvement. The next picture shows the same test as last time, for one million items (10.000 items per slice, 1.000 items buffer per slice). I only added sort test to see some real-time performance and changed Integer list with TValue list because TValue is something we compete against.

SpeedTest2

Nice, isn’t it. We retained around 10 times faster deletes and insert and we are now also very, very fast on indexed access. We got down from approximately 270 ms to around 110 ms. For 1.000.000 items look-up we are now only around 20 ms slower then pure dynamic array (eg. TList). You have to take into account that that 113 ms is mostly calling Random function for random indexed access. The true difference is seen in the iteration by index test. There it is 38 vs 29. I say this is more then acceptable. If that difference bothers you then well… :)

We can also note how slow variants and especially TValue are on deletes and inserts. 65 seconds on TValue against 0.1 with my implementation. Staggering difference. That is because TAnyValue is only 9 bytes in size on both 32 and 64 bit and TValue is just huge. That is why memory footprint is also very important. Few recognize it but when you move data around it shows. Its not just the pure memory consumption that hits you its also all operations. You need to work with so many more bytes in memory.

I have also done a stress and regression test for IAnyArray. I ran all basic operations in a loop for 5 hours, while checking the content of the array against TList after each cycle. If there was an inconsistency, I was informed. I ironed out all the major bugs I hope. Download section now has a separate AnyValue download entry, so you can download only AnyValue units. Also the updated code is already there, with all the tests and demos. Just don’t expect that you will know how to use the StressTest. That is only meant for me to test for bugs.

I truly think that this structure is better then TList in almost any way. The only downside is, it consumes more memory, but even that is not a given. I allocate memory in chunks of 10.000 items or whatever your slice size is and TList just doubles the size of the array each time it runs out of space. So it will quickly be less efficient even for that, at large item numbers. If you need an array with powerful interface and efficiency for large number of items then this is it.

If you find any bugs or have any thoughts on any of what I wrote, don’t be shy, drop a comment. Also I dare you all out there, to make it even faster if you can. What about a little competition? Take my code and lets see if you can squeeze something more out of it. :)


Dr.Bob's Delphi Notes: Monday March 18 - SDN Event featuring Marco Cantu, Bruno Fierens and Bob Swart

$
0
0
On Monday, March 18th, 2013, the SDN organised another event in Zeist (The Netherlands) including a full Delphi track with 5 sessions (2 from Marco Cantu, 2 by Bruno Fierens and one from your truly Bob Swart).

The Wiert Corner - irregular stream of stuff: jpluimers

$
0
0

Variant Records are a feature that has been in the Pascal language since Standard Pascal.

A cool page for historic perspective is R3R: Pascal Features in Popular Compilers, hopefully someone will update it to more modern versions of the mentioned compilers.

There is not much official documentation on the Delphi side on this, so below some parts of a case I used for a project that started in 1997 and is still in use to day.

The project involves communication between applications running on PCs (in 1997 there were many written in Borland Pascal 7 and a few in Delphi 3) and AS/400 (now called “System i” after an initial rename to “iSeries”) using CPI-C and APPC (both parts of SNA).

Back then, it was top notch for a few reasons:

  • Most of the LAN and WAN was already IP-based, but HOST (back then System/390, now called System z after an initial rename to “zSeries”), AS/400 and 3270 terminal emulation were still SNA based. Which meant using Microsoft SNA Server (meanwhile renamed to HIS or Host Integration Services).
  • Both an architect and the board were involved and because of strategic alliances, the solution had to make use of

    It was impossible to get this stable (the combination of these 3 requirements caused any cobination of high CPU usage, or undefined memory growth, or unreliable communications) which took 4 months to debug with overseas help form Microsoft, NetSoft and IBM to prove that direction – that worked in the labs of the 3 partners – was impractical in the real world.

  • After the initial failure, the AS/400 programmer and I sat together without the architect and the board to make up our minds.
    The AS/400 programmer was a very smart guy, and since we had a closet full of IBM Red Books, we decided to see what was available in the SNA protocol. Low and behold, it was very versatile, included authentication, automatic encryption over the wire (very useful for a huge financial), automatic release of resources used by broken sessions and much more.

The communications layer on the PC side is still in Delphi (as of mid 2012 in Delphi XE2) but in 2005 the DOS client software got rewritten in India by a company advertising to be CMM level 6 (impossible) and having deep experience with COOL:Gen (also named as IEF, Composer, COOL:Gen, Advantage Gen, AllFusion Gen, CA Gen) and C/C++.

In India, they had some C/C++ gurus that agreed it would be OK if the communications layer became a DLL and they would write a small C interface inbetween COOL:Gen and the DLL. It ended up that after half a week of me explaining how they should do that in Visual C++ 6 (modern as they were, they still opted to use the 1998 release while it was 2005), I wrote that portion for them as well.

The communication layer on the AS/400 side is still mainly COBOL with a bit of RPG IV.

In 2011, the whole communication part got rewritten on the PC side using a Delphi layer to MQ Series 5.3 (now called WebSphere MQ) that was officially already unsupported, and on the AS/400 side WebSphere MQ v6 with an interface layer still mostly in COBOL and RGP IV.

In retrospect that should have been done a lot earlier, as in 2006 the AS/400 got moved about a 1000 miles away, not a distance that the SNA protocol would easily cope with, not even with an Enterprise Extender. Back then I recommended rewriting the middle layer based on a solution sitting on top of TCP/IP, but they didn’t want to as “it worked fine”.

It took another 4 years for major interruptions to occur and go away spontaneously, and finally not to go away in 2011.

In 2012 another, but shorter, refactoring round was done to bring the PC side to Windows 7 and WebSphere MQ version 7.x, and the AS/400 side to e WebSphere MQ 7.x (and also move the AS/400 a lot closer).

Between 2011 and 2012, there have not been any outages apart from a few configuration issues (where someone forgot to make a couple of AS/400 subsystems automatically start upon AS/400 LPAR reboot).

The really cool thing about the SNA to WebSphere MQ transition is that the communications layer instantly became twice as fast for “normal” communications, an order of magnitude faster on heavy traffic, and orders better in case of communications trouble (now there are even reasonable error messages to the end users).

Back to Variant Records (:

The goal of Variant Records is to map different kinds of memory structures onto the same memory. It is similar to unions in C/C++.

The Delphi implementation is very similar to the Standard Pascal one.

Variant records are like absolute (where you define two variables to have the same memory location):

You use the a variation of case to distinguish the different structures to start at the same memory position with the record.
As with a regular case, the tags need to be unique.

Both support for RTTI and Windows messages relies heavily on variant records.

A record can start as a regular record, and towards the and can have one variant part in a record definition.

The cool thing is that Variant Records are not going away in the NEXTGEN compiler, just look at the documentation of the TTypeData variant record to see the NEXTGEN conditional defines.

Before looking at the example code, lets first list a few limitations.

  • You cannot used managed types inside variant records (but you can use them in the regular portion), as the compiler would not know which of the tagged cases it should initialize/finalize.
    So these are out: Strings, Open Dynamic Arrays, Interfaces, Anonymous methods, Variants or Records containing other managed types.
    If you still try to, you get error messages like E2154 or E2418, or E2569.
  • I think you can create a generic record as long as you apply a record constraint on the generic parameter.
  • In Delphi .NET (now defunct) you could get the E2417 error when the compiler could not determine the record size in the first compile pass.

The example code below starts out with a few simple things:

  1. single byte character type TChar (we are dealing with DOS and COOL:Gen, they don’t get Unicode yet)
  2. single byte character arrays (zero based called TChar###, 1-based called T1Char###).
  3. the TVariantData part (simplified from the communications code, but still shows enough detail on how and why)
    the trick here is that the record needs to be exactly 33 bytes long, and the “Contents” portion takes care of that.
  4. the TVariantKey part (shows how ID’s were done in the SNA implementation and are now done in the MQ implementation)
    with this one, we could run both the SNA version and the MQ version at the same time and do refactoring and regression testing.

This is basically all you need to know: look at how the case portions are formulated, you can use it with any ordinal (Boolean, Integer, enumeration) tag.

Have fun with it!

–jeroen

unit VariantRecordUnit;

interface

{ First a few basic types}

const
  TGuidStringSize = 38;

type
  TChar = AnsiChar; { single byte character, as it interfaces with DOS and CoolGen programs through C interface }
  TChar2    = array[0..   1] of TChar;
  TChar8    = array[0..   7] of TChar;
  TChar10   = array[0..   9] of TChar;
  TChar20   = array[0..  19] of TChar;
  T1Char33 = array[1..33] of TChar; { 1-based because the DOS CAS sources expect this }
  TGuidChar   = array[0..TGuidStringSize-1] of TChar;
  TMessageId = array[0..23] of Byte;

{ now the record types }

type
  TVariantData = record
  case Boolean of
    False: (
      ProgramName: TChar10;
      InterChangeFormat: TChar10;
      FunctionCode: TChar2;
      ReturnCode: TChar2;
      ErrorCode: TChar2;
      Zero: TChar2);
    True: (Contents: T1Char33);
  end; { total: 33 bytes }

  TId = packed record
    NetBiosName: TChar20; { historically, as DOS app defined it wrongly }
    TimeStamp: TChar8; { HHMMSShh because a DOS directory name can be no longer than 8 characters }
  end; { total: 28 bytes }

  TVariantKey = packed record
  case Integer of
    0: ( // SNA
      ConversationId: TId; { 28 bytes }
      GuidChars: TGuidChar); { 38 bytes }
    2: ( // MQ
      ConversationIdFiller: TId;
      MessageId: TMessageID); // 24 bytes
  end; { total: 66 bytes }

  TPacket = packed record
    EntryType : Byte;
    ReturnKey : TVariantKey;
    DataType  : Byte;
    Data      : TVariantData;
  end;

implementation

end.

–eof–


Filed under: APPC, AS/400 / iSeries / System i, COBOL, Communications Development, CPI-C, Delphi, Delphi 1, Delphi 2005, Delphi 2006, Delphi 2007, Delphi 2009, Delphi 2010, Delphi 3, Delphi 4, Delphi 5, Delphi 6, Delphi 7, Delphi 8, Delphi XE, Delphi XE2, Delphi XE3, Development, HIS Host Integration Services, Internet protocol suite, MQ Message Queueing/Queuing, SNA, Software Development, TCP, WebSphere MQ

Australian Delphi User Group Members: The ADUG Autumn Symposium is less than a week away

$
0
0
Time really is running out to register for the number one event on the Australian Delphi Calendar. Registrations will close at 11pm, this coming Tuesday 19th March with the event itself held in Melbourne on Thursday 21st March and Sydney … Continue reading

Firebird News: Hopper v1.2.0 – stored procedure/trigger debugger – released

$
0
0
Upscene Productions announces a new release of: “Hopper – a Stored Code Debugger” Hopper is a Stored Routine and Trigger Debugger, available for Firebird, InterBase and MySQL. For more information and a trial download, see the news @ Upscene Productions, pricing information is available. Bugfixes include, but not limited to: SQL errors with certain regional [...]

Firebird News: Firebird Foundation will have Annual General Meeting at March 20, 13. Join FF now

$
0
0
Firebird Foundation, the non-profit organization which finances Firebird development, will have Annual General Meeting (AGM) at March 20, 2013. The AGM addresses the issues that are required to continue functioning from year to year: review the finances, hear what the committee(s) have been doing, elect a new committee. This is a good moment to join Firebird [...]
Viewing all 1725 articles
Browse latest View live