RJ's blog - stuff that interests, frustrates and fasinates me RSS 2.0
 Thursday, September 25, 2008

Making a Client/Server application using Datasnap is pretty straight forward.  The big change over the old DCom design is the connection from client to server, and the server activation method – simply put there is no auto activation.  You’ll have to start the server before running the client.   The old client side TDComConnection is replaced by a TSQLConnection – yes, that’s the same connection component used from the server (TSqlQuery) to the database.

 

Let’s create the server first. 

1.       Create a new VCL forms application, then add a Server Module (File\New\Other – Delphi Files page) to the application. 

2.       From the Tool Palette \ Datasnap Server – drop a TDSServer, TDSServerClass and TDSTCPServerTransport on the applications main form.

3.       For both the ServerTransport and the ServerClass, set the Server property to the TDSSserver component.

4.       On the ServerClass, add an event in for the OnGetClass method.   In the event itself you need to assign the PersistentClass variable to the ServerModule you created in step one.  You’ll need to add it to the main forms uses clause first.  ie.  PersistentClass := TDSServerModule2;

5.       On your Server Module, drop a TSqlConnection, TSqlQuery and TDatasetProvider component.  Set the SqlConnection.ConnectionName to your database,  SqlQuery.SqlConnection to the SqlConnection, and DatasetProvider.Dataset to the SqlQuery.  Also add the SqlQuery.Sql to query a table in your database.

6.       Build the server

 

 

Now that we’ve got the server created, lets build the client.

1.       Create a new VCL forms application.

2.       Drop a TSqlConnection on to the main client form, set the Driver property to DATASNAP.  Assign params for the following Key|Value combinations:  DriverName | Datasnap,  Hostname | LocalHost,  Port | 211.  Note the Port 211 is the default, however you can reassign the client & server ports to a different value.  Each Datasnap server must use a different port, running a 2nd datasnap server that is using the same port of an existing running one will cause a 'Socket already in use' error message.

3.       Add a TDSProviderConnection to the main form, set the SqlConnection property to the TSqlConnection on the client form, and set the ServerClassName to the object name of the TDSServerModule that you created in step 1 of the server (ie.  TDSServerModule1).

4.       Add a TClientDataset, TDatasource, and TDBGrid.   Set the ClientDataset.RemoveServer to the DSProviderConnection, the Datasource.Dataset to the ClientDataset, and the DBGrid.Datasource to the Datasource.

5.       At this point, if you run the server then edit the ClientDataset.ProviderName property, it should show your DatasetProvider in the drop down list if everything is hooked up properly.  Setting the ClientDataset.Active = True should display the results of your query in the DBGrid.

 

That’s all there is to the basics of how to connect a client application to the server using the Datasnap architecture. 

 

Some good reading on the basics of Datasnap:

Thursday, September 25, 2008 1:29:06 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
DataSnap | Delphi
 Friday, September 19, 2008

Been doing some R&D with the new Delphi 2009 and checking out the new Datasnap architecture, I came across this gem of a problem.  The TDSServerClass CreateInstance calls the TDSServerModule create method, but the TDSServerClass DestroyInstance doesn't call the ServerModule Destroy.  Thus any code put in the destroy isn't called (like closing the database connection), and the ServerModule is a memory leak.  I bumped in to this problem by putting something as trivial as an OutputDebugString into the DestroyInstance event.

Per a comment by Leonel (CodeGear) in the NG's, if there is any code in the TDSServerClass.DestroyInstance, then the intent is that the developer has to free the ServerModule themself.   This violates one of the most basic rules of Delphi, as layed out by Danny Thorpe in his book "Delphi Component Design", that having or not having code in an event by itself alters the component.  The real kicker of this is that the help on the TDSServerClass methods is pretty sparse, and no where does it detail this component changing 'feature'.

The work around to this is either a) don't put any code in the TDSServerClass.DestroyInstances, or if you must then use DSDestroyEventObject.ServerClassInstance to reference the ServerModule and destroy it.

Friday, September 19, 2008 8:25:02 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
DataSnap | Delphi
 Friday, December 07, 2007

Ok, I must admit I'm a Johnny-come-lately to the whole rebasing thing, it's been covered over and over, with the need for it being stressed by all.  And yet, here I am after 10+ years of maintaining the Win32 dlls we use without having done it.  I'm back to beating my head on my DLL hell issue (more updates on that later), and one of the things I finally decided to take care of was getting the DLL's rebased.

It's one of those topics that seems like it'd be difficult. I did a bunch of research trying to figure out the best way to decide on starting addresses (seemed hard to find), and then determine how much spare memory I should reserve for future growth, calculating starting addresses backwards, fighting with the Delphi 2007 compile options because the base address didn't seem to be working, and then I read how simple this really is.  No need for all the manual gyrations & calculations I was trying to do - I was making it far tougher than it needs to be.

Because this topic has been covered so many times by so many that are better writers than I am, I'm not going to bother with the whys of it.  But here's the short version of it how to do it.  Get your hands on a copy of Rebase.exe from Microsoft, it comes with VS so you probably already have it.   List your dll's, give an output file for the logs and a starting address, and you're done. 

>rebase.exe -b 0x68000000 -d -C coffbase.log -l rebase.log -v mydll1.dll mydll2.dll mydll3.dll
Where:
-b = Initial base address (MS recommends starting at 0x68000000)
-d = Top down rebase (MS recommended practice)
-C = Output coff_base.txt file
-l = write image bases to log file
-v = verbose output

It's that simple, there was no calculations needed, Rebase.exe handles them all.  I created a simple .bat file to run the rebase command.  I just execute the .bat after I recompile the exe's and bingo, all done.  Yes, it really is that simple.  Gonna add this step to our FinalBuilder build process now, it's so trivial I'm embarrased to admit I put off doing this for years.

Friday, December 07, 2007 10:30:07 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Sunday, November 11, 2007

I needed to take a TDataset and save the data out to a CSV file the other day. It wasn't until I actually tried to do it that I realized that Delphi didn't have an option for ClientDataset.SaveToFile that supported CSV - only binary and xml format.  Ended up grabbing some code off of an old post by Bill Todd to handle it.  I added in the ability to toggle on/off writing the fieldnames as a header row.

Ironicially, I just noticed that Steve Trefethen recently did something similar in C#, interesting to see the differences in code.  I might have to play around and make a LoadFromCsv reader now to populate a TClientDataset, just cause.  :)

// example: SaveToCsv(ClientDataset1,'c:\temp\testfile.csv','"',',');
procedure SaveToCsv(DataSet: TDataSet; AsciiFilePath: String;
  Delimiter, Separator: Char; WriteHeader: boolean = true);
var
  AsciiFile:   System.Text;
  I:           Integer;
  LastField:   Integer;
begin
  Assign(AsciiFile, AsciiFilePath);
  Rewrite(AsciiFile);
  LastField := DataSet.FieldCount - 1;
  if WriteHeader then
  begin
    for I := 0 to LastField do
    begin
      Write(AsciiFile, Delimiter + DataSet.Fields[I].FieldName + Delimiter);
      if I < LastField then
        Write(AsciiFile, Separator);
    end;
    Writeln(AsciiFile, '');  // Write CRLF at the end of row
  end;
  while not DataSet.EOF do
  begin
    for I := 0 to LastField do
    begin
      if not (DataSet.Fields[I].DataType in
        [ftBCD, ftCurrency, ftFloat, ftInteger, ftSmallInt, ftWord]) then
      begin
        Write(AsciiFile, Delimiter);
        Write(AsciiFile, DataSet.Fields[I].AsString);
        Write(AsciiFile, Delimiter);
      end
      else
        Write(AsciiFile, DataSet.Fields[I].AsString);
      if I < LastField then
        Write(AsciiFile, Separator);
    end;
    Writeln(AsciiFile, '');  // Write CRLF at the end of row
    DataSet.Next;
  end;
  System.Close(AsciiFile);
end;
Sunday, November 11, 2007 1:20:55 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Monday, August 20, 2007

After determining that the DLL hang simply is not inside of the code that we maintain, my next step was to suspect that it had something to do with the various 3rd party controls we use or somewhere within Delphi 2006 itself.   My output debug strings in the finalization sections showed that the hang happens after the last finalization we use.  So I thought I'd drop some into the finalization sections of the 3rd party controls (DevExpress, ReportBuilder, SvCom, TeeChart, to name a few), then rebuild all of those packages (yes Virginia, there is a good reason to spend the extra money and buy the sourcecode versions of 3rd party software).  However after realizing I had 853 finalization sections that needed Outputdebugstring's (ODS) added, I nixed that idea.

The next step then was to get Delphi 2007 installed and do a rebuild.  For the first pass I just upgraded D2007 and used the existing versions of 3rd party recompiled in D2007.  Sent the newly recompiled test case off to our customer and they responded with "Yup, still happens.  But guess what we found out?  Remember we said that we tested it with hyperthreading turned off?  It seems that we must not have, because when we turn it off the lockup goes away.  We're looking into possible hardware / bios issues now."  Sweet ... 60+ hours into this issue and they now realize that one of the tests I asked for on day 1 they never did.

Still a number of unanswered questions though, got those queued up and will be researching & testing them once I get all 3rd party upgraded.

Monday, August 20, 2007 9:01:51 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Monday, August 13, 2007

Looks like CodeGear has taken a step backwards with Delphi installs.  The RAD team at Borland went out of their weay to ensure that different versions of Delphi would live happily together on the same PC.   After installing Delphi 2007, my D2006 is now broken.

Something else that I found VERY irritating.  You use a download program to do the install.  I run the app, tells me that it's got 800mb gig of data to download and off it goes.  Downloads done, I run the setup, reboot the PC and start D2007.   It comes up with an option "There is a new version available, do you want to download Update 1?"  When I answered yes it informed me that it had to uninstall D2007, dowload another 800 mb of data, and then reinstall.  What the hell??  Why didn't they prompt me with that before the first download? 

Oh, and the best part of this is that you have to keep the local cache of the installation files if you want to be able to run any future updates - so your 800mb install is 1.6gig (give or take).

And now there's an update 2.. which is only a partial update, if you kept the Cache during the install of Update 1.  If you didn't keep it, time to uninstall D2007 - reinstall base image, reinstall update 1 (keeping the Cache for both of them), and then install update 2.

Chris Pattinson's blog about Update 2
The readme for Update 2

Monday, August 13, 2007 10:56:04 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi | Random
 Thursday, August 09, 2007

Been working on a problem for a customer of ours for the last couple weeks (off and on).  On one specific configuration of Dell computers, with a certain disk image on it, they're getting system lockups when the client application does an FreeLibrary.   Same image on different computers, no problem.  The new computer without this image on it, no problem.   Unfortunately this customer has purchased over 600 of these PC's, they *have* to use this image, and are in the process of rolling them out to their end users.  So this is an issue that must be resolved, we can't punt on it.  The real bitch of this issue is that we can't duplicate the issue here on any computer I've used, no other customer sees the problem, and due to security reasons they won't ship us one of their PC's so I can debug it, nor will they let come onto their site and do it there. 

So I'm debugging remotely, old school style.  Make up test cases, put in lots of outputdebug strings and ship it to their tech guy to try out, and he emails me the results back.  After 40+ hrs of research and test cases I'm no closer to the solution, but I am certain it happens somewhere after the FreeLibrary - something is hanging.

Research led me to a couple articles that I want to make note of for the future, I know I'll be needing this info again one day. 

Quick overview of how processes exit on Windows XP
LoadLibraryEx(DONT_RESOLVE_DLL_REFERENCES) is fundamentally flawed
Some reasons not to do anything scary in your DLLMiain
Why are DLLs unloaded in the "wrong" order?

We're using a bunch of packages (BPL's) in our client & dll, but compiling without packages the error still happens.  I've got a suspicion the problem is in the finalization code that happens, so I've built up another test case and added outputdebugstrings into all of the finalizations that occur.  There's no critical sections in these apps, so that shouldn't be an issue.   Ok, back to banging my head on the desk for a while.

Thursday, August 09, 2007 12:23:07 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Saturday, January 20, 2007

I made the trip down to CodeMash in Sandusky OH this past week.  I ended up driving the 6 hrs there instead of flying because there's no flight into Sandusky, I would have had to fly to Cleveland then rent a car and drive the hour to Sandusky... just didn't make sense to me.

I have to say I was fairly impressed with the conference, for their first attempt the organizers did a great job of it.  They had a good showing of speakers, including keynotes from Neal Ford, Bruce Eckel and Scott Guthrie.  Topic's ran the gamut - Java, Ruby, Ajax, .NET, Smart Clients, you name it they had a little bit of everything.  Ok, not entirely true.. there was no Delphi there, but I can't fault them for that since they never claimed there would be.  Even as such there was plenty to keep me busy there, I got a lot out of Mary Poppendieck's session on Lean Software Development.

All in all it was well worth the trip, I'm looking forward to CodeMash 2008.

Saturday, January 20, 2007 10:53:37 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
C# | Delphi
 Monday, December 18, 2006

When it comes to working on things, be it assembling a desk, doing a tuneup on an engine, or writing software - I tend to be a "Jump right in with both feet, start doing it and don't stop to pick up the manual until I get stuck" sorta guy.  I get that from my father - The-Big-Guy (dad) is one of those guys who's either built, fixed, worked on or learned just about anything you ever wanted to (and some things you didn't want to).  So of course I grew up learning how to work on everything too, the typical son wanting to be like his father.  I've worked on farms, in 4x4 repair shops, been a welder, resturant manager, 4 years in the Marine Corps, and of course 15 years developing software professionally (add another 10 or so as a hobbiest).   Among my friends and co-workers I'm a bit of a handy man, helping people out with home repairs, building rec rooms, rewiring basements, etc.  Well since my comfort zone for work is pretty broad, I normally don't bother learning what I'm doing before I start - as The Big Guy has often said "When all else fails, read directions".

Years ago while working at Quad/Tech (1994 ish) I needed to write an application for a small division of Quad/Graphics.  I had no resources, no help, and no time to do it in (3-4 months).  While Q/G was a big Microsoft shop and the rest of the IS department was busy working in Access & VB 2.0, I got my hands on a copy of this really slick little development tool called Delphi.  I had no training, but I loved what I saw and just dug in, working my way thru the help files and what little information I could get out of the World Wide Web.  And I was hooked - I knew Delphi was the language of the future, it was revolutionary - a thing of simplistic beauty and genius created by Borland.  For 12 or so years I've been programming in Delphi and it's always been a "just do it" sort of attitude from me.  Never been one for utilizing design patterns or standardized methodologies, code it now, get it working, refine & refactor it later as needed has been my modus operandi.

So you can see where my natural inclination to just dive in, fire up Visual Studio and start rewriting code comes from.  There is a part of me whispering "Dude, this is bigger than a breadbox.. you need to get your shit organized before you really start", but then there's the another voice coming from the other shoulder saying "Don't listen to him, you can do this.. it's not that different from Delphi - just go for it and worry about the details later".  An interesting conundrum for me really, do I go with what has always worked for me or should I try something new and actually start at the beginning?  There's a ton of interesting & informative books out there on the subject matter.. but no matter what, reading what someone else says you should do is never as interesting as actually doing it yourself. 

Oh well - as Confucius said "The journey of a thousand miles begins with a single step".  Don't have any .NET books handy, but I do have Visual Studio installed.  Maybe I'll play around for a little bit, and do some research on a good C# book tomorrow..

Monday, December 18, 2006 11:17:01 PM (Central Standard Time, UTC-06:00)  #    Comments [1] -
C# | Delphi | Random
Fundraising for LLS
TeamInTraining - Contribute Now
Archive
<March 2010>
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910


About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Rich Werning
Sign In
All Content © 2010, Rich Werning
My DasBlog theme is modified from 'Business' created by Christoph De Baene (delarou)