September 30, 2006

Enhanced Delphi DBImage replacement (handles wmf, emf and jpg)

Sebastian Mayora has created an excellent replacement for the DBImage control. EDBImage handles all the image formats that DBImage does PLUS jpg, wmf and emf as well. Download it here.

Posted by daen at 09:19 AM | Comments (0)

Catching dataset updates in Delphi

When you navigate away from an inserted or edited record in a DataSet in Delphi, an implicit post will occur before the navigation. Sometimes, it's useful to be able to trap that and handle it in different ways. For example, you might want to ask the user if they want to save or discard the changes, or cancel the navigation and keep making changes to the current record (they might just have hit the wrong key or clicked on the wrong spot with the mouse). The bad news is that it's far from clear how to do this in the Delphi documentation. The good news is that it turns out to be pretty simple to do.

The trick lies in using a special exception called EAbort (defined along with a load of other exceptions in SysUtils). EAbort is special because it doesn't display a dialog message in the default exception handler - it's "silent". The place to do this is in the DataSet's BeforePost event handler. Throwing the EAbort simply pulls the rug from under the whole record update machinery. There's a handy procedure called Abort which will throw EAbort for you.

procedure TForm1.CDSBeforePost(DataSet: TDataSet);
begin
  with DataSet as TClientDataSet do
  begin
    if DataSet.Modified then
    begin
      case MessageDlg('Save (yes) discard (no)'+
         'or continue editing (cancel)?',
         mtWarning,mbYesNoCancel,0) of
        mrNo:
        begin
          Cancel;
          Abort;
        end;


mrCancel: begin Abort; end; end; end; end; end;

There's one extra twist (not shown here) where implicit posts (generated by navigation and insert/delete events) need to be handled differently from explicit posts. This requires a boolean ExplicitPost flag that is set anytime before you call DataSet.Post from your code, and in the DBNavigator.BeforeAction event handler for the nbPost button. The flag is cleared in a transition to dsEdit or dsInsert which can be done in DataSource.OnStateChange. Then, in the DataSet.BeforePost event handler, the flag is checked and if set, the warning message is not displayed. This is not so clean, because you need to remember to set this flag before every explicit Post.

Thanks to Zarko Gajic at delphi.about.com for writing the article that put me on the right track.

Posted by daen at 08:47 AM | Comments (0)

August 15, 2006

MDL Draw control tips in Delphi

... although it should work generally. Note I'm using Viatcheslav V. Vassiliev's Managed VCL components for Delphi 7 ...

Assume you have configured a TClrControl and set the AssemblyName property to MDL.Draw.Renderer, Version=1.4.1000.20, Culture=neutral, PublicKeyToken=e922e3fd683b3777 (although the MDL.Draw.Renditor assembly will also do), and the ControlName property to MDL.Draw.Renderer.Renderer (although the MDL.Draw.Renditor.Editor control will also do).

Assume that you've read a molfile into variable MolFileString. You can set the molfile property of the Renderer control using the following code:

  ClrControl1.ClrObject.DispProperty['MolFileString']:=
   MolFileString

Assume you also have a TImage instance available that you want to capture the Renderer image into as a bitmap. You can easily do it like this:

  ClrControl1.ClrObject.DispProperty['MolFileString']:=
   ClientDataSet1.FieldByName('ConvMolfileString').AsString;
  Image1.Picture.Bitmap.Handle:=
   ClrControl1.ClrObject.DispProperty['Image'].GetHBitmap;

Posted by daen at 02:39 PM | Comments (0)

June 01, 2006

Rendering ChemDraw image as metafile in Delphi TImage control

Assume you've already set the ChemDraw control with SMILES/MOL etc and you also have a TImage on the form already called Image1 ...

procedure TForm1.ButtonRenderClick(Sender: TObject);
var
  WMFData: array of byte;
  MemStream: TMemoryStream;
begin
  WMFData:=CSChemDrawCtl1.Data['image/x-wmfs'];
  MemStream:=TMemoryStream.Create;
  MemStream.WriteBuffer(WMFData[0],Length(WMFData));
  MemStream.Seek(0,soFromBeginning);
  Image1.Picture.Metafile.LoadFromStream(MemStream);
  MemStream.Free;
end;

Posted by daen at 09:04 PM | Comments (0)

November 21, 2005

Delphi 7 help amusing spelling gaffe

ProcessPath procedure

arses a file name into its constituent parts.

Posted by daen at 12:15 PM

September 07, 2005

DAPUG workshop 6-7 September

I'm back from a two day workshop organised by DAPUG (the Danish Database Application Programmers Users Group). These workshops are held once or twice per year. The venue this time is Hotel Hessellet, which has been the location for many of these workshops over the last ten years. Hessellet is a four-star hotel situated by the beach at Nyborg, just over the Storebælt from Sjælland. The accomodation is sumptuous, the food is absolutely delicious and the location is wonderful. This workshop was titled "Using In-Memory Data Stores: Featuring ClientDataSets and .NET DataSets" and was expertly presented by Cary Jensen, long-time Delphi and database guru. I have quite a few ideas now as to how to use TClientDataSet in new ways, thanks to Cary. The next workshops are provisionally booked for May 2-3 and August 29-30 2006.

Here are some photos of the workshop and Hotel Hessellet.

Hotel Hessellet
Hotel Hessellet

Storebælt
Storebælt

Cary Jensen
Cary Jensen

DAPUGgers at dinner
DAPUGgers at dinner

Posted by daen at 11:26 PM

August 03, 2005

ClientDataSet and nested data sets, and general Delphi bling

TClientDataSet is a wrapper for a minimal portable database implementation known as MIDAS. Client data sets allow you to populate them with fields and data from other databases, or you can create your own schema at design or runtime. Data can be streamed into a proprietary binary file format (.CDS) or XML (although the XML schema is proprietary). What's really cool is that fields in a client data set can themselves be data sets, allowing master-detail relationships to be created relatively easily. I stress the relatively, because it isn't entirely clear from the Delphi documentation how to do it. Which is why people like Cary Jensen, Marco Cantu, Ray Konopka, Bob Swart, Charlie Calvert, Steve Teixeira (recently defected to MS, boo hiss!), Xavier Pacheco and all those TeamB people and other Delphi experts who post their knowledge where all and sundry can find it deserve medals. Cary Jensen is singled out in this post for his excellent and comprehensive article on nested data sets using TClientDataSet.

Posted by daen at 03:29 AM

June 18, 2005

Delphi, Web Services and statefulness ...

Advanced Web Services (Deepak Shenoy)

Posted by daen at 11:38 AM

May 07, 2005

Borland BDP, IIS and Oracle : more questions than answers?

It's not so straightforward to access Oracle using the Borland Data Provider (BDP) through IIS ... I kept getting "BdpException: Connection open failed". Turns out it's a permissioning problem, and it's far from resolved in my mind. I've currently granted full access to the entire Oracle hierarchy to the local ASPNET, IUSR... and IWAM... users (the Oracle DB is running on the same box as IIS). That works. This is actually a general problem relating to Oracle access through IIS (I googled up lots of articles on ADO.NET, ODBC, OleDB etc etc, all tackling the same problem).

But hmmm.

I think only the ASPNET user needs access, and read only (maybe execute?) permissions at that. I will try to remove access to the other accounts and see what happens. I also want to get ECO II working - I believe it should be fairly plain sailing now my ASP.NET app can see the database.

But, how, then, do you get access to an Oracle DB on another Windows box? Or, for that matter, one running on a Linux box?

Questions, questions ...

Links and a definitive resolution to these puzzles will follow ... I hope.

Posted by daen at 03:51 PM

March 17, 2005

Delphi 2005 Update 2

Delphi 2005 Update 2 now available ...

Posted by daen at 11:28 PM

February 21, 2005

Delphi 8 update 3 fixes .NET Framework 1.1 SP1 problems

Out-of-the-box, Delphi 8 VCL.NET projects do not compile under .NET framework 1.1 SP1. Period. It's taken me half a day to track down the official Borland fix for this, which is in Delphi 8 update 3. Bad bad bad.

Posted by daen at 01:42 PM

September 24, 2004

Unofficial Newsletter of Delphi Users

The Unofficial Newsletter of Delphi Users

See Grahame Marsh's series on Drag & Drop (eg Sep 99 - COM Interface based Drag and Drop Part 9 – Starting a Drag Source Operation and COM Interface-based Drag and Drop - Part 10).

Posted by daen at 12:43 AM

September 02, 2004

Thierry Coq's homepage - TP LEX and YACC

"Site personnel de Thierry/Thierry's Personal site"

Posted by daen at 10:51 PM

July 12, 2004

JNI array parameter issues

According to Matthew Mead (author of the JNI port for Delphi), it is entirely possible that the helper function ArgsToJValues doesn't work correctly for some types and some JVMs. The solution is to use the JNI array handling functions. This article on starting a JVM from C shows how to do this from within C.

Here's some example Delphi code, with the relevant array handling bits in bold:

program JavaFromDelphi;
{$APPTYPE CONSOLE}
uses
  SysUtils, JNI;
var
  Options: array [0..4] of JavaVMOption;
  VM_args: JavaVMInitArgs;
  JavaVM: TJavaVM;
  JNIEnv: TJNIEnv;
  Cls: JClass;
  Mid: JMethodID;
  Errcode: Integer;
  Obj: JObject;
  DelphiIntArray: array [0..8] of Integer;
  JavaIntArray: JIntArray;
  Loop: Integer;
begin
  try
    // Create the JVM (using a wrapper class)
    JavaVM := TJavaVM.Create;
    // Set the options for the VM
    Options[0].optionString := '-Djava.class.path=.';
    Options[1].optionString := '-Xcheck:jni';
    VM_args.version := JNI_VERSION_1_4;
    VM_args.options := @Options;
    VM_args.nOptions := 2;
// Load the VM Errcode := JavaVM.LoadVM(VM_args);
if Errcode < 0 then begin WriteLn(Format('Error loading JavaVM, error code = %d', [Errcode])); Exit; end;
// Create a Java environment from the JVM's Env (another wrapper class) JNIEnv := TJNIEnv.Create(JavaVM.Env);
// Create the Java array of integers JavaIntArray := JNIEnv.NewIntArray(9);
// Quit if this fails if JavaIntArray = nil then begin WriteLn('Can''t get int array'); Exit; end;
// Fill the Delphi array with some values for Loop:=0 to 8 do begin DelphiIntArray[Loop]:=(Loop*Loop); end;
// Set the Java int array with the values in the Delphi int array JNIEnv.SetIntArrayRegion(JavaIntArray,0,9,@DelphiIntArray);
// Find the class in the file system. This is why we added // the current directory to the Java classpath above. Cls := JNIEnv.FindClass('TestApp'); if Cls = nil then begin WriteLn('Can''t find class: TestApp'); Exit; end;
// Get its default constructor Mid := JNIEnv.GetMethodID(Cls, '', '()V'); if Mid = nil then begin WriteLn('Can''t get default constructor for class'); Exit; end;
// Create the object obj := JNIEnv.NewObjectA(Cls, Mid, nil);
// Fail if we can't if Obj = nil then begin WriteLn('Can''t get get object'); Exit; end;
// Get method Mid := JNIEnv.GetMethodID(Cls, 'test', '([I)V'); if Mid = nil then begin WriteLn('Can''t find method: test'); Exit; end;
// Finally, call the method with the Java int array ... JNIEnv.CallObjectMethodA(obj, Mid, PJValue(@JavaIntArray));
except on E : Exception do WriteLn('Error: ' + E.Message); end;
end.

The TestApp.java source is here:

public class TestApp
{
    void test(int[] params)
    {
        System.out.println("params.length="+params.length);
for (int i=0; i<e;params.length; i++) { System.out.println("params["+i+"]="+params[i]); } } }

Posted by daen at 02:27 PM

June 13, 2004

JNI and Delphi

Using the Java Native Interface with Delphi

Posted by daen at 12:45 AM

April 25, 2004

PCA/chemical components for Delphi

Epina and Software Development Lohninger

Posted by daen at 01:24 AM

February 29, 2004

Delphi RTTI library

RTTI Library

Posted by daen at 11:56 PM

February 20, 2004

Use the Source Luke ...

Project JEDI's JVCL consists of over 300 open source Mozilla Public License (MPL) components for use in Delphi.

Posted by daen at 12:24 AM

January 12, 2004

How to get an enum's name in Delphi

In this article (Under The Hood With Dr.Bob - How to Get an Enum's Name) the inestimable Bob Swart describes how to get an enum value as a string.


Posted by daen at 03:35 PM

December 22, 2003

Building DLLs with G++ for Delphi

Building DLL with G++

testfn.c

__declspec(dllexport) int testfn(int i, int j)
{
  return i+j;
}

Compiling

g++ -x c -c testfn.c

Linking DLL

dllwrap --dllname=tst.dll testfn.o

Using in Delphi

function testfn(i: integer; j: integer): integer; cdecl; external 'tst.dll';

Posted by daen at 09:51 PM