Archive for the ‘Delphi 2010’ Category

Creating a Remote Desktop Plugin Using Delphi - Errata

Wednesday, January 9th, 2013

After I had implemented my plugin and it was just going into production, I discovered a rather ugly truth.  WTSVirtualChannelOpen will succeed even if the plugin is not installed on the client side.  You don’t find out about the problem until the first time you try to write to the channel, whereupon you get an obscure error.  Something about an “invalid function”.

It appears as though the only solution to this problem is to implement a “ping” function in the plugin. Then, on the server side, you need to open the virtual channel and ping the client.  If both of these succeed then the plugin exists, otherwise it does not.

You would think that Microsoft would at least check to see if the client has registered the virtual channel before returning a handle for it.

Creating a Remote Desktop Plugin Using Delphi - Part 5

Tuesday, December 4th, 2012

The last thing that I need to do to my remote desktop plugin is add the code required to send data from the client to the server.
(more…)

Creating a Remote Desktop Plugin Using Delphi - Part 4

Monday, December 3rd, 2012

Now that we have the client side ready to receive data from the server we need to look at programming the server side.  This actually seems to be quite a bit simpler that the client side.  There are just four functions; WTSVirtualChannelOpen, WTSVirtualChannelClose, WTSVirtualChannelRead and WTSVirtualWrite.  Actually, there are a few more, but these are the ones that you need to get by. (more…)

Creating a Remote Desktop Plugin Using Delphi - Part 2

Thursday, November 29th, 2012

In part 1 of this series I set about creating a remote desktop plugin using Delphi. It didn’t do anything except initialize itself and display a few message boxes indicating its progress.  Not very useful.

In order for this whole thing to work the way I want it to, the plugin is going to have to interact with the user on the remote workstation.  This means displaying a form that will have to be on top of the remote desktop window.  I had some vague misgivings that remote desktop in full screen mode would be an “always on top” window that would interfere with the interaction that I need. So, as the next step in the process, I changed the plugin to create a form, make it an “always on top” window and display my status messages in a TMemo contained in the form.  Surprisingly enough this actually works quite well.

(more…)

Creating a Remote Desktop Plugin Using Delphi - Part 1

Saturday, November 17th, 2012

I have recently found myself in a situation where I need to have an application running on a server using remote desktop interact with files and devices on the user’s local workstation.  Specifically, I need to be able to use a scanner to scan documents and / or retrieve files containing documents that have already been scanned. I found several commercial plugins that would drive the scanner but I couldn’t find anything that would allow me to interact with the user to allow her to import documents from a file.

Knowing that there are commercial plugins available, I know that it is possible to write a plugin for remote desktop.  So, I’m thinking; “How hard can it be?”.  Since we are virtually a pure Delphi shop I started looking for examples of Delphi plugins.  Surprisingly, I didn’t find any.  It is difficult to believe that no one else has done this, but I seem to be a pioneer. So, in this and the next few articles, I am going to create a remote desktop plugin using Delphi and remote desktop virtual channels and I am going to document the process here.

(more…)

Delphi, Firebird, IBObjects and Transaction Management

Thursday, November 17th, 2011

I recently had reason to revisit transaction management in our application.  The application makes extensive use of data aware grids (TDBGrid) and, as I am sure everyone is aware, it is impossible to explicitly manage transactions when you are displaying data in a grid.

Fortunately, IBObjects provides the means to automatically clean up transactions left hanging by TDBGrid.  Unfortunately, this isn’t as well documented or as straightforward as I would like.  So, after much research using the programmer’s secret weapon (Google) and a few questions posted to the IBObjects support group (thanks Jason) I think I finally understand how it works. (more…)

The Mystery of the Disappearing Icon

Thursday, November 3rd, 2011

I just finished creating a new custom control for Delphi.  I’ve done this dozens of times in the past and never had a problem until now.  This time the control’s icon simply would not appear in the tool palette.  No matter what I did, all I got was the default icon.

It turns out, that the first time I installed the component I had a problem with the DCR file that held the icon, so Delphi installed the control with the default icon.  Perfectly reasonable.  However, Delphi keeps a cache of custom controls that have been installed and, apparently, no matter how many times you uninstall and reinstall, it remembers certain settings (like the icon) from the cache.  To fix this problem, you need to manually edit the registry and remove the offending cache items.  To do this:

  1. Uninstall your custom control from the Delphi IDE.
  2. Open the registry editor (regedit.exe).
  3. Browse to HKEY_CURRENT_USER\Software\Embarcadero\DBS\9.0\Package Cache.  Where 9.0 is the version number for Delphi XE2.  Your version number may vary.
  4. Delete the key MyCustomControl.bpl where MyCustomControl is the name of your custom control’s package.
  5. Browse to HKEY_CURRENT_USER\Software\Embarcadero\DBS\9.0\Palette\Cache.
  6. Delete the key MyCustomControl.bpl where MyCustomControl is the name of your custom control’s package.
  7. Reinstall your custom control as your normally would.

Hopefully, the proper icon will now appear in the tool palette.

Delphi Compiler Versions

Tuesday, October 25th, 2011

If you ever need it, here is the list of Delphi compiler version numbers:

Compiler CompilerVersion Defined Symbol
Delphi XE5 26 VER260
Delphi XE4 25 VER250
Delphi XE3 24 VER240
Delphi XE2 23 VER230
Delphi XE 22 VER220
Delphi 2010 21 VER210
Delphi 2009 20 VER200
Delphi 2007 18.5 VER185
Delphi 2006 18 VER180
Delphi 2005 17 VER170
Delphi 8 16 VER160
Delphi 7 15 VER150
Delphi 6 14 VER140
Delphi 5 13 VER130
Delphi 4 12 VER120
Delphi 3 10 VER100
Delphi 2 9 VER90
Delphi 1 8 VER80

To make use of this information in a Delphi program you can define conditionally compiled bits, like this:

{$if CompilerVersion >= 20} // Delphi2010 or later

{$ifend}

OR

{$ifdef VER200} // Delphi2010

{$endif}

The Case of the Disappearing Rows

Friday, August 5th, 2011

I just lost a week of my life trying to track down a problem with a program that did a mass insert of records into a SQL Server database.  The program is written in Delphi 2007 and uses dbExpress components for access to SQL Server.

The problem first manifested after an 8 hour run when, lo and behold, the database only had 11K rows rather then the expected 884K.  I knew that the program was sound because I had just run the same program to load the same file into a Firebird database without a problem.  (That only took 3 hours by the way).

Now, the source file had some bogus data in it.  I knew this and the program made allowances for it by trapping the resulting exceptions, writing them to a log file and dropping the offending record.

It turns out that these data conversion errors fall into a class of SQL Server error known as “batch aborting” errors.  Whenever a batch aborting error occurs the current transaction is rolled back without so much as a “by your leave”.  You would think that this would be documented somewhere, in BIG BOLD LETTERS.  If it is, I can’t find it.  If it weren’t for Google I would still be scratching my head.

I found this table in an article on another web site:

Error Aborts
Duplicate primary key. Statement
NOT NULL violation. Statement
Violation of CHECK or FOREIGN KEY constraint. Statement
Most conversion errors, for instance conversion of non-numeric string to a numeric value. BATCH
Attempt to execute non-existing stored procedure. Statement
Missing or superfluous parameter to stored procedure to a procedure with parameters. Statement
Superfluous parameter to a parameterless stored procedure. BATCH
Exceeding the maximum nesting-level of stored procedures, triggers and functions. BATCH
Being selected as a deadlock victim. BATCH
Permission denied to table or stored procedure. Statement
ROLLBACK or COMMIT without any active transaction. Statement
Mismatch in number of columns in INSERT-EXEC. BATCH
Declaration of an existing cursor Statement
Column mismatch between cursor declaration and FETCH statement. Statement.
Running out of space for data file or transaction log. BATCH

The entire article, which is a good review of SQL Server error handling techniques, can be found here.

TFrame Displaying as TForm in Designer

Wednesday, June 15th, 2011

I came across an interesting quirk in the Delphi 2010 IDE today.  I was working in a project that uses a lot of TFrame components.  To make life easy on myself, or so I thought, I created a custom TFrame descendant and used that in place of the default TFrame.

Everything was working well until I displayed one of the frames as text, made some silly change and then redisplayed the frame’s visual designer.  At this point, the IDE started treating my TFrame as a TForm.  It displayed as a TForm in the visual designer, all of TForm’s properties appeared in the object inspector, all of TForm’s properties were being saved in the .DFM file.  I didn’t notice this right away.  Not until I recompiled everything and tried to run the application.  At this point I started to get ‘ClientHeight property not found’ errors when I tried to instantiate the TFrame.

This little gotcha cost me most of an afternoon.  I guess the rule is:

If you are designing a TFrame descendant use the visual designer only.  Do not try to edit the .DFM file as text.