Jump to content

Mark Williams

Members
  • Content Count

    274
  • Joined

  • Last visited

Posts posted by Mark Williams


  1. 5 hours ago, Remy Lebeau said:

    The MainForm is established by the first call to TApplication.CreateForm().  Changing the project options simply changes the default code that calls CreateForm() in the main .dpr file.  If there is no MainForm assigned when Application.Run() is called, Run() simply exits immediately.

    Ok. Thanks that makes sense. I was testing whether mainform was assigned in the onCreate event of the first form created.


  2. 1 minute ago, Attila Kovacs said:

    That means Application.ShowMainForm := False; and Application.Terminate; if login fails, and you won't get a flickering mainform and you will have a clean termination of the application including calling the finalization section in the mainform.

    I have Application.ShowMainForm:=false, but it still flickers after call to terminate. I cannot figure out why it is now doing this. It wasn't when I originally posted and as far as I can see my code is the same. But as mentioned above, there is apparently no mainform.


  3. 48 minutes ago, Attila Kovacs said:

    I'm not sure what your "exit" does, and I can't see your code, but with the code below finalization is called:

     

    if not DoLogIn() then

    begin

        Application.ShowMainForm := False;
        Application.Terminate;
        Application.Run;
    end;

    The "exit" does nothing. I was under the misapprehension that the current function would run to its conclusion even after terminate call.

     

    Unfortunately, I have been fiddling around trying to find out how to solve the problem and now I can't recreate it! My main form flashes on the screen at the point of application.terminate and it closes properly. Can't seem to get it back to its original state. Will just have to live with the form momentarily flashing on screen. Thanks for the help.


  4. 17 minutes ago, John Kouraklis said:

    Is this a VCL app or FMX?

     

    Where does the code appear? In the proj file? if so, it does not really remove the form from being auto created. You just don't use the feature in the IDE

     

    Try creating the form using the default way: frmMain:=TfrmMain.Create.... and then assign it to the Application as the MainForm

     

    VCL

     

    The form creation code is in the proj file.

     

    You can't assign the form as the main form other than in project options. It's readonly. Which has made me realise I don't have a mainform. Maybe that's the problem

    • Like 1

  5. In order to pop up a login box I have removed my main form from the autocreation section and I create it in the project file as follows:

     

     Application.Initialize;
     Application.MainFormOnTaskbar := False;
     Application.CreateForm(TfrmMain, frmMain);
     Application.Run;

    The reason I do this is to stop the main form showing if the login process is cancelled.

     

    The main form has an initialization and finalization section.

     

    If the login process is cancelled so that the main form never shows, the initialization section is called, but the finalization section does not get called. In fact, the app hangs and has to be terminated via task manager.

     

    if not DoLogIn(self, CurrServer, MainUser, ltStartUp) then
      begin     
        Application.Terminate;
        exit;
      end;

     

    If I insert a Show command prior to Application.Terminate all works as expected.

     

    Does anyone know why Application.Terminate does not terminate the app without a call to "Show" and is there aw away around this (other than calling Show)?


  6. 6 minutes ago, Ondrej Kelle said:

    Simple assignment to Content should be sufficient:

    
    Response.Content := doc.XML.Text;

    This will both encode the incoming string value and set ContentLength automatically. (See TISAPIResponse.SetContent in Web.Win.IsapiHTTP.)

    In your case the encoded stream was longer than the original text, e.g. due to insertion of escape characters. By setting ContentLength explicitly you have effectively truncated the encoded stream.

    That makes sense. Many thanks.


  7. 8 hours ago, Attila Kovacs said:

    I have no clue, but you could watch this video, how to create a console application from the broker to be able to debug your code.

     

    No video link? However, don't think it would help. There is no exception and in any event my code is wrapped in try...except statements with exceptions outputted to a log file. Also, as mentioned in my post I cannot now reproduce the problem and don't understand why it was happening in the first place. I am just keen to avoid a repeat.


  8. I'm using TWebModule in an ISAPI DLL. I am using it to return data from a database in xml format using IXMLDocument. Last night I had a problem with text data which included characters such as  "/". I assume the returned text was being formatted somewhere along the line to insert escape characters with the result that the received xml at the other end is always slightly short so that you end up with a badly formed xml document. So, for example, if the returned text had two "/" characters the xml document when received is short the last two characters. 

    My code on the server side is:

    Response.Content := doc.XML.Text;
    Response.ContentLength :=length(doc.XML.text);

    I also tried determining the ContentLength using "length(Response.Content)", but that made no difference.

     

    I edited the data in the database to remove the "/" characters and all worked fine.

     

    I have now tried to replicate the problem so that I can seek some advice as to possible causes, but the xml is no longer being truncated. 

     

    I am puzzled and keen to avoid the issue for the future, but unable to provide any more info because I can't now replicate the problem. 

     

    Does anyone have any ideas as to what may have caused the problem initially?

     

     


  9. I would like to shellexecute one app from another. Both apps require username and password. I would like to be able to pass the password securely from one app to the other.

     

    Obviously, command-line parameters are out and I would imaging a memory mapped file will also not be secure.

     

    Is there any relatively simple and secure way of achieving this.


  10. On ‎9‎/‎3‎/‎2019 at 2:13 PM, Attila Kovacs said:

    Btw. the fact that your broker works in garden mode, where the dll will be loaded every time you have a new request (as I understand it), indicates that you are using globals in non thread-safe mode.

    Spot on! That was the problem. As for WebGarden, I have check and you create a WebGarden by increasing the ApplicationPool's Maximum Worker Processes above 1. I am now running as non-web garden.

     

    I have done a lot of research based on your helpful points above and eventually the mist began to clear. I now have a working ISAPI DLL which appears to be thread safe. Unfortunately, I couldn't find any good examples of how to do what I was trying to do.

     

    My DLL uses global variables stored in an inifile. I don't want to load the inifile each time a new thread is fired to avoid repeated loading of the ini file.. So I need to load the inifile once when the DLL is first loaded. Also, I need to have a threadsafe logging system and finally I use a MimeTable in the DLL and I don't want to load that for each request.

     

    Below is my relevant code (with the detail removed) to show I have gone about the above. It is working as far as I can see, but I would appreciate any tips for improvement or re potential pitfalls. Hopefully, this will then help others who are new ISAPI dll. It took me many hours of head scratching to get this far.

     

    The dpr

    library WebServer;
    
    uses
      Winapi.ActiveX,
      System.Win.ComObj,
      System.SysUtils,
      Web.WebBroker,
      Web.Win.ISAPIApp,
      Web.Win.ISAPIThreadPool,
      WinAPI.Isapi2,
      WinAPI.Windows,
      System.SyncObjs,
      WebModule in 'WebModule.pas' {WebModule1: TWebModule};
    
    {$R *.res}
    
    
    //Read online somewhere that it is best to initialize/Finalize your globals in the following function 
    //rather than in Initialization/Finalization sections of the webmodule}
    function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall;
    begin
      Result := Web.Win.ISAPIApp.GetExtensionVersion(Ver);
      CriticalSection := TCriticalSection.Create;
      StartServer;
    end;
    
    function TerminateVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall;
    begin
      Result := Web.Win.ISAPIApp.GetExtensionVersion(Ver);
      CloseServer;
      CriticalSection.Free;
    end;
    
    exports
      GetExtensionVersion,
      HttpExtensionProc,
      TerminateExtension;
    
    
    begin
      CoInitFlags := COINIT_MULTITHREADED;
      Application.Initialize;
      Application.WebModuleClass := WebModuleClass; 
      {Application.MaxConnections:=100;} //if you want to increase beyond the default 32 for example
      NumberOfThreads:=Application.MaxConnections; //see comments following code samples. This is necessary even if you do not want to change the MaxConnections
      Application.Run;
    end.

    Stripped down WebModule

    unit WebModule;
    
    interface
    
    uses WinApi.Windows, System.SysUtils, System.Classes, Web.CGIHTTP, Web.HTTPApp, Web.ReqMulti, System.SyncObjs, 
     ThreadFileLog, IDGlobalProtocols {, etc...};
    
    type TLogType = (leNone, leCriticalError, leMajorError, leMinorError, leMajorEvents, leDevelopment);
    
    type
        TWebModule1 = class(TWebModule)
        FDQuery: TFDQuery;
        FDPhysPgDriverLink: TFDPhysPgDriverLink;
        FDConnection: TFDConnection;
        procedure WebModule1HeadAction(Sender: TObject; Request: TWebRequest;
          Response: TWebResponse; var Handled: Boolean);
        procedure WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest;
          Response: TWebResponse; var Handled: Boolean);
        procedure WebModuleAfterDispatch(Sender: TObject; Request: TWebRequest;
          Response: TWebResponse; var Handled: Boolean);
    
      private 
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      WebModuleClass: TComponentClass = TWebModule1;
      CriticalSection: TCriticalSection;
    
      const
        
        EncryptionKey='Q9__M_f_qc1EX8ce98HsQrYG_s4aRGQj5G_Ixu4XTQs___esfxdH_kf_lg5_8lnk';
    
        Procedure StartServer;
        Procedure CloseServer;
    
    implementation
    
    Uses web.WebBroker;
    
    var //these will be accessible to all threads
      ServerStarted:boolean;
      LogType:TLogType;
      LogFile, PGVendorLib:string;
      FThreadFileLog : TThreadFileLog;
      MimeTable:TIDMimeTable;
    
    
    {%CLASSGROUP 'System.Classes.TPersistent'}
    
    {$R *.dfm}
    
    
    procedure AddToLog(str: string; whichLogType:TLogType);
      var
        Log:string;
    begin
      try
        if (LogType=leNone) or (ord(whichLogType)>ord(LogType)) then //we are not logging these types of events or errors
          exit;
        case whichLogType of
          leCriticalError: Log:='CRITICAL ERROR: ';
          leMajorError: Log:='MAJOR ERROR: ';
          leMinorError: Log:='MINOR ERROR: ';
          leMajorEvents: Log:='MAJOR EVENT: ';
          leDevelopment: Log:='DEVELOPMENT: ';
        end;
    
        Log:=Log+str;
        FThreadFileLog.Log(LogFile, Log);
      Except
      end;
    end;
    
    Procedure CreateMimeTable;
    begin
      try
        if not assigned(MimeTable) then
          begin
            MimeTable:=TIDMimeTable.create;
            AddToLog('Mime Table Create', leDevelopment);
          end;
      except
       on E: Exception do
          AddToLog('Error CreateMimeTable: '+e.Message, leMajorError)
      end;
    end;
    
    
    Procedure StartServer;
        var
          appPath:String;
          iniFile:TIniFile;
    begin
      if serverStarted then //Probably overkill as should only be called once
        exit;
      CriticalSection.Enter;
      try
        try
          FThreadFileLog := TThreadFileLog.Create();
    
          appPath:=GetModuleName(HInstance);
          if pos('\\?\', appPath)>0 then //this is something to do with the Windows function GetModuleFileName and uncpaths
            appPath:=copy(appPath, 5, MaxInt);
          iniFile:=TIniFile.Create(changeFileExt(appPath, '.ini'));
          
          try
            if not FileExists(iniFile.FileName) then
              begin
                LogFile:='c:\ProgramData\Log.txt';
                AddToLog('IniFile does not exist at location: "'+iniFile.FileName, leCriticalError);
                Exit;
              end;
    
            AddToLog('Server Started', leMajorEvents);
            LogType:=TLogType(iniFile.ReadInteger('GENERAL', 'log_type', 1));
            LogFile:=inifile.ReadString('GENERAL', 'log_file', '');
            if (LogType<>leNone) and (LogFile<>'') then
              begin
                if not DirectoryExists(ExtractFilePath(LogFile)) 
                and not ForceDirectories(ExtractFilePath(LogFile)) then            
                  LogFile:='c:\ProgramData\Log.txt';             
              end;
            
            PGVendorLib:=inifile.ReadString('LOCATIONS', 'pg_vendor_lib', '');
    
          finally
            iniFile.Free;
          end;
    
          AddToLog('LogType:'+inttostr(ord(logType)), leDevelopment);
          AddToLog('LogFile: '+LogFile, leDevelopment);
          AddToLog('FDPhysPGDriverLink.VendorLib: '+PGVendorLib, leDevelopment);
          
          AddToLog('Server Started', leMajorEvents);
          ServerStarted:=true;
        except
          try AddToLog('Server could not be loaded: '+SysErrorMessage(GetLastError), leCriticalError); except end;
        end;
      finally
        CriticalSection.Leave;
      end;
    end;
    
    Procedure CloseServer;
    begin
     CriticalSection.Enter;
      try
        if assigned(MimeTable) then
          try MimeTable.Free; except end;
        if assigned(FThreadFileLog) then
          try FThreadFileLog.Free; except end;
        try AddToLog('Server Closed', leMajorEvents); except end;
      finally
        CriticalSection.Leave;
      end;
    end;
    
    procedure TWebModule1.WebModule1HeadAction(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
      var fileName:string;
    begin
      Handled:=true;
      try
    	//If this action needed the mimetable it would call CreateMimeTable at this point
        FileName:=trim(Request.GetFieldByName('Location'));
        if FileName='' then
          begin
            SetResponse(Response, 400, 'Bad Request', 'Head');
            exit;
          end;
    
    
        if fileExists(FileName) then
          SetResponse(Response, 200, 'OK')
        else
          SetResponse(Response, 404, 'NotFound');
      except
      end;
    end;
    
    procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
    begin
      FDPhysPgDriverLink.VendorLib:=PGVendorLib;
      AddToLog('Before Dispatch - PathInfo: '+Request.PathInfo, leDevelopment);
      AddToLog('Before Dispatch - Method: '+Request.Method, leDevelopment);
      AddToLog('MaxConnections: : '+Application.MaxConnections.ToString, leDevelopment);
      AddToLog('ActiveConnections: : '+Application.ActiveCount.ToString, leDevelopment);
      AddToLog('InactiveConnections: : '+Application.InActiveCount.ToString, leDevelopment);
      AddToLog('CachedConnections: : '+Ord(Application.CacheConnections).ToString, leDevelopment);
    end;
    
    procedure TWebModule1.WebModuleAfterDispatch(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
    begin
      AddToLog('After Dispatch - Method: '+Request.Method, leDevelopment);
    end;
    
    end.

    ThreadFileLog unit

    unit ThreadFileLog;
    
    interface
    
    uses Windows, ThreadUtilities, System.Classes;
    
    type
        PLogRequest = ^TLogRequest;
        TLogRequest = record
            LogText  : String;
            FileName : String;
        end;
    
        TThreadFileLog = class(TObject)
        private
            FThreadPool: TThreadPool;
            procedure HandleLogRequest(Data: Pointer; AThread: TThread);
        public
            constructor Create();
            destructor Destroy; override;
            procedure Log(const FileName, LogText: string);
        end;
    
    implementation
    
    uses
      System.SysUtils;
    
    
    
    procedure LogToFile(const FileName, LogString: String);
      var
        c1, c2 : dword;
        i:integer;
        s:string;
    begin
      try
        c1 := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0,
        nil, OPEN_ALWAYS, 0, 0);
    
        i:=0;
        while c1=INVALID_HANDLE_VALUE do
          begin
            if i>200 then //2 secs elapsed and haven't been able to access log
              exit
            else
              begin
                inc(i);
                Sleep(10);
                c1 := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0,
                nil, OPEN_ALWAYS, 0, 0);
              end;
          end;
    
      if c1 <> INVALID_HANDLE_VALUE then
        begin
          SetFilePointer(c1, 0, nil, FILE_END);
          S:=DateTimeToStr(Now)+': '+LogString+sLineBreak+SLineBreak;
          WriteFile(c1, s[1], length(s)*SizeOf(Char), c2, nil);
          CloseHandle(c1);
        end
      except
      end;
    end;
    
    constructor TThreadFileLog.Create();
    begin
        FThreadPool := TThreadPool.Create(HandleLogRequest, 1);
    end;
    
    destructor TThreadFileLog.Destroy;
    begin
        FThreadPool.Free;
        inherited;
    end;
    
    procedure TThreadFileLog.HandleLogRequest(Data: Pointer; AThread: TThread);
    var
        Request: PLogRequest;
    begin
        Request := Data;
        try
            LogToFile(Request^.FileName, Request^.LogText);
        finally
          Dispose(Request);
        end;
    end;
    
    procedure TThreadFileLog.Log(const FileName, LogText: string);
    var
        Request: PLogRequest;
    begin
        New(Request);
        Request^.LogText  := LogText;
        Request^.FileName := FileName;
        FThreadPool.Add(Request);
    end;
    
    end.

    Other possible useful information:

     

    The Application

    The application created in the project file is a TWebApplication not a TApplication.

    It exposes MaxConnections, ActiveConnections and InActiveConnections, CachedConnection.

     

    MaxConnections

    The maximum no of connections to your DLL. By default this is 32. So up to 32 new threads will be spawned (but note comments re NumberOfThreads below). You can increase or decrease this number as requi

    ActiveConnections

    This reports the number of connections currently in use.

     

    InactiveConnections

    If CachedConnections is true new threads are not disposed of after use, but cached for future use. whilst cached they are recorded as InactiveConnections.

     

    ISAPIThreadPool unit

    This is added by default and deals with threading.

     

    NumberOfThreads

    Global variable declared in the ISAPIThreadPool unit. It is set by default to 25. So whilst the default MaxConnections is 32 (so you can theoretically have 32 connections) unless you increase the NumberOfThreads you will in effect be allowed only 25 connections! The example project code deals with this.

     

    • Like 1

  11. WebGarden? Is this when you increase the Maximum Worker Processes of the Application Pool above 1? If so, it works when I do this, but I appreciate it is not the solution and I have reduced it again to 1.

     

    Nothing in WebModule's OnCreate and onDestroy. BTW what event should I use to free objects etc?

     

    Nothing in the Event Viewer.

     

    I am writing everything to a log file. See my last but one post.

     

    Also, can you advise me how I can monitor the ActiveConnections property from the WebModule? I can't figure out how to access the Application object.

     

    BTW thanks. I'll get some test projects uploaded asap.

     

     


  12. Web

    26 minutes ago, Attila Kovacs said:

    show web.config and MCVE

    Web.config:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.net>
            <mailSettings>
                <smtp from="">
                    <network host="" />
                </smtp>
            </mailSettings>
        </system.net>
    </configuration>

    I'll have to work on a MCVE. But do you have any suggestionsat this point why multi-threading may not to be working?


  13. I think maybe I didn't explain myself very well. Apologies. Plus I have managed to improve things a little since my first post. I noticed that my DLL was including Madshi's exception units. I have removed these and there is now an improvement. Whereas the DLL appeared to be hanging on concurrent calls resulting in a timeout error, my client app now gets a ENetHTTPClientException "Server returned an invalid or unrecognised response". But whereas the DLL was freezing it now continues to run albeit rejecting any concurrent requests.

     

    I don't know what code to post as I am not getting any exceptions for any of my event handlers (I do have the event wrapped in try except blocks). I add a log entry in the BeforeDispatch event to advise what method etc has been called and one in the AfterDispatch to advise when it is done. I log the http response codes issued by the handlers when they are done.

     

    I have set up a test app to fire off rapid requests via threads to download 14 files at slightly staggered intervals (10ms) so that they overlap. Accordingly, there are probably 14 concurrent requests being made at the same time. Only 1 returns successfully all the others fire a ENetHTTPException.

     

    The first request executes correctly. I log the BeforeDispatch and AfterDispatch events and these fire ok. I also log the Response returned by the dlls' download method and this logs correctly for the first call, which means it has completed okay.

     

    For concurrent calls, the BeforeDispatch event is not called. Also, within each method I log the fact it has been called as the first line of the method's code. For concurrent calls, the DLL is not even entering the handler for the method being called. It does not trigger any exception even though wrapped in a try except block.

     

    I think the problem is a threading problem within the DLL, but I don't understand why. As mentioned above, if within the ISS ApplicationPool for the DLL I increase the number of Maximum Working Processes above 1 then all is okay (presumably as long as I don't make concurrent requests that exceed the max working processes) save for the fact that each (concurrent?) request seems to run a new DLL instance rather than making use of DLL threading. Whilst this sort of resolves the problem, I am certain it is not the way to do this from all I have read on the web.

     

    To get threading working I can't work out whether there is something I need to configure on my website (it is configured for limitless connections), the ApplicationPool (can't see anything) or the app (and I used the wizard to create it so I would have thought not..

     

    Below is the code from the DLL dpr file:

    library MWCWebServer;
    
    uses
      Winapi.ActiveX,
      System.Win.ComObj,
      System.SysUtils,
      Web.WebBroker,
      Web.Win.ISAPIApp,
      Web.Win.ISAPIThreadPool,
      WebModule in 'WebModule.pas' {WebModule1: TWebModule};
    
    {$R *.res}
    
    exports
      GetExtensionVersion,
      HttpExtensionProc,
      TerminateExtension;
    
    
    begin
      try
      CoInitFlags := COINIT_MULTITHREADED;
      Application.Initialize;
      Application.WebModuleClass := WebModuleClass;
      {Application.MaxConnections:=200;
      NumberOfThreads:=Application.MaxConnections;
      Application.CacheConnections:=true;}
      Application.Run;
    end.

    I have tried setting MaximumConnections, CacheConnections. NumberOfThreads and none of these seem to do anything. But I can't imagine for a moment that the default no of threads for a webmodule is 1. So I guess it has to be something else.

     

    I have tried to monitor how many connections there are. But within the webModule I cannot figure out how to access the Application property!

     

     


  14. I have written an ISAPI DLL (Delphi 10.3) for IIS to carry out various functions (download/upload of documents, query tables etc).

    It was working fine until I started firing more than one request at a time then the DLL simply crashed and I had to restart WWW service.

    The DLL is being used to request smallish files (20-30KB) over a local network. These are being requested one at a time. If I make these requests the same time as one or two Head requests that all goes Kaput.

    I though it may be the TWebRequestHandler's maxConnections so I set that to 200. It made no difference.

    After a bit of digging around I noticed that the IIS ApplicationPool that I created for the DLL has a "Maximum Worker Processes" property and that this was set to 1. I have set this to 20 and now the DLL seems to be able to handle more than one concurrent request.

    However, I noticed from my DLL's log file that it was now creating a new instance of the DLL for every request. I had a dig around on the web and from the information I found I have come to the conclusion that Maximum Worker Processes is probably not what I need. It seems to be designed for long-running processes, which is not what my DLL is designed for.

     

    If not Maximum Worker Processes and increasing MaxConnections doesn't work how do I get my DLL to deal with concurrent requests.

     


  15. 5 hours ago, Dany Marmur said:

    Why do you need to send "multiline" text in the header? It sounds like food for the payload.

    Anyway, my interpretation of the link above is that each key-value pair in the headers should NOT contain CRLF.

    So Base64 encoding would be my first labrat.

    I tried Base64. This may have worked, but I think I'm running into a limitation as to permissible size of header. 

     

    I absolutely agree that it is food for the payload, but I have tried and failed to submit/receive content using THTTPClient and TWebRequest components.

     

    My code for submitting the component is below, but when I try and access the TWebRequest's content it remains stubbornly empty.

     

      FHttpClient:=THttpClient.Create;
      try
        With FHttpClient do
          begin  
            data:=TStringStream.Create;
            try
              data.WriteString(content);
              ContentType:='text/html';
              Accept:='text/plain, */*';
              AcceptCharSet:='utf-8';
    
              LResponse:=FHttpClient.Post(Script, data);          
            finally
              data.Free;
            end;
          end;
      

    I have now resorted to submitting it as multipartdata, which works, but seems overkill just to submit a string.


  16. Hi Thanks,

     

    As far as I can see (and with my limited understanding of the protocols) it is compliant with the requirements for headers. Each line break is coded +Chr(32)+sLineBreak; There are no line breaks inserted other than in this manner, but I still get the error.

     

    Does anyone know of any function for encoding text in away that will satisfy the protocol?


  17. Trying to analyse the structure of a table using TFDMetaDataQuery with MetaDataKind set to mkTableFields.

     

    This returns just about everything I need apart from (as far I can see) a field's default value.

     

    I can see it returns TFDDataAttributes and that this includes an attribute caDefault which advises if a field has a default value, but I can see now way of ascertaining what the default value is.

     

    The metadata structure can be found at:

    http://docwiki.embarcadero.com/RADStudio/Rio/en/Metadata_Structure_(FireDAC)

    • Like 1

  18. No you are right. I have created a new project and copied the main form from the example project to my new project and then pointed my project at the copied form. It works fine.

     

    However, if I create a new project and replicate the form from the example folder, it keeps spitting out the floating point error. I have stripped back the example form so it consists of nothing more that a button and a TOpenDialog. I have pared back its code to about 20 lines and it still works okay.

     

    But when I try to recreate (rather than copy) the example form in a new project it bums out again. Even if I copy and paste the entire .pas unit for the example form into my new .pas and even if I right click and view as text and then paste from the example to the new form.

     

    So it seems to be something about the way the form is configured, but as to what that is I have no idea.

×