3
votes

I wrote an application that uses a firebird database, using a server and a client. I originally coded the application using Delphi XE2 and all worked fine. I have now moved over to Delphi XE6.

I recompiled the server and there was no errors, but now that I recompile the client it gives me the following error:

ProjectPiping.exe raised execption class TDBXError with message "Remote error: Error reading DSServerModule1.ClientHeight: Propery CliehtHeight does not exist.

I have read online that you need to change the dfm file ontop from Object to Inherinted but that made no difference.

Is there anyone that can assist me as I do not see anyway that I can solve this except going back to Delphi XE2

3
DSServerModule1 doesn't have a property named ClientHeight. Or perhaps CliehtHeight. Looks like you didn't use the clipboard which is always a slight worry. It would help if we knew what DSServerModule1 was. Instead of trying to change the .dfm file at random without understanding what you are doing, it would be prudent to diagnose the problem first. We cannot see the .dfm file and we have no idea what it contains or what type your object is.David Heffernan
@DavidHeffernan I wrote a server application using the DSServerModule as I was using Firebird. So when I try to compile a Client that must access the server it gives me the above error. Like I said before it compiled and worked fine, this error started now that I have moved over to Delphi XE6Japster
Google tells us that this is a known problem: qc.embarcadero.com/wc/qcmain.aspx?d=110536David Heffernan
@DavidHeffernan Since this error only happens when I use Delphi XE6 I suppose I will have to go back to using Delphi XE2, or are there any other alternatives for datasnap when using firebirdJapster
I have no idea. I just googled your error message.David Heffernan

3 Answers

5
votes

Missing property errors are common for Frames and DataModules that do not directly inherit from base Delphi TFrame and TDataModule class. Under some circumstances (that change with every Delphi version) Delphi will not be able to access Frame or DataModule ancestors in order to determine whether it deals with TFrame or TDataModule descendant. When that happens, it will wrongly interpret Frame/DataModule as Form and pollute their .dfm with Form specific properties.

You don't have to rollback to XE2 and you can fight the issue by removing bad properties from .dfm file using Notepad or similar editor. Since this is operation you will be bound to repeat often while doing design work on such Frame/DataModule, version-control can be of great help. Just don't commit changes that are not supposed to be there. Another way of fighting against sporadic changes in .dfm files is to make them read-only once you have finished your design-time work with them.

Correct empty DataModule .dfm file:

inherited DataModule1: TDataModule1
  OldCreateOrder = False
  Height = 150
  Width = 215
end

Broken empty DataModule .dfm file

object DataModule1: TDataModule1
  Left = 0
  Top = 0
  ClientHeight = 188
  ClientWidth = 303
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
end
1
votes

This issue is caused by an uninstalled, disabled or otherwise not accessible design (component) package. If Delphi is not able to find the component a data-module inherits from, it will/may change the dfm-file and replace its properties with the most likely candidates it knows about. Because the dfm is related to UI Delphi will treat the object by default as a TForm and change the properties and events in the dfm accordingly. However, the source code is (thankfully) not synchronized with these changes, hence the Property does not exists error message(s) when trying to use the data-module. Code and dfm are out of sync.

Changing object to inherited is not the solution. You must reinstall or enable the missing component package first, close Delphi and then edit and save the dfm with notepad. Or better, revert to the previous sound version in your version control system. If that is no option (what?!) then you can get all the default properties and events back by just removing the bad ones (or all) from the dfm. The next time you open the dfm in Delphi it will be restored with the valid defaults.

You can prevent this issue in the future by not ignoring "Property does not exists" errors in the Delphi IDE but use the cancel option instead. This will prevent changing/saving the dfm and the project will function as normal during run-time.

1
votes

This workaround will allow your DataModule to be created anyway, by intercepting and ignoring errors about the non-existent TForm properties Delphi is inserting in your dfm (this is just a workaround, not a solution to the IDE problem you are experiencing)

1) Add these declarations to your datamodule class:

 private
    FSaveReaderOnError:TReaderError;
    procedure OnReaderError(Reader: TReader; const Message: string; var Handled: Boolean);
 protected
    procedure ReadState(Reader: TReader); override;

The ReadState method we are overriding is responsible of loading the DFM and it does it using the Reader:TReader object.

TReader exposes an event handler we can intercept to ignore errors:

procedure TMyDataModule.ReadState(Reader: TReader);
begin
   FSaveReaderOnError := Reader.OnError;
   try
      // install our error handler 
      reader.OnError := self.OnReaderError;
      // let the dfm loading continue
      inherited;
   finally
      // restore previous error handler
      Reader.OnError := FSaveReaderOnError;
      FSaveReaderOnError := nil;
   end;
end;

This is a the error handler:

procedure TMyDataModule.OnReaderError(Reader: TReader; const Message: string; var Handled: Boolean);
var Ignora:boolean;
    tmp:string;
begin
   if Assigned(FSaveReaderOnError) then begin
      // in case there already was an error handler, we call if first
      FSaveReaderOnError(Reader,Message,Handled);
      if handled = true then
          exit;     
   end;

   // ignore errors about missing form properties  
   if not message.StartsWith('Error reading '+self.name) then exit;
   if not message.EndsWith(' does not exist') then exit;
   if not message.Contains(' Property ') then exit;

   Handled := true;
   if message.Contains('Font')  then exit;
   if message.Contains('ClientHeight')  then exit;
   if message.Contains('ClientWidth')  then exit;
   if message.Contains('Color')  then exit;
   if message.Contains('PixelsPerInch')  then exit;
   if message.Contains('TextHeight')  then exit;
   Handled := false;
end;