Jump to content

Yaron

Members
  • Content Count

    286
  • Joined

  • Last visited

  • Days Won

    2

Posts posted by Yaron


  1. Even though I have a subscription, I did not receive an invitation and now I have a quick Android project that I wanted to beta-test through the play store and... impossible.

     

    While this is a small hurdle now, there are projects that are due in a month or two that I simple can't release, it doesn't seem like google is accepting exemptions on new projects.

     

    • Sad 1

  2. I'm doing something similar and encountered some of the same issues.

     

    1. Since ISAPI is a DLL, did you add "IsMultiThread := True;" to let the memory manager know this DLL is using multiple threads (otherwise you can get random-seeming crashes)?

    2. I'm not sure your logging code is thread safe, it might be useful to add a critical section to your log writes.

    3. There should be no problem using global read-only variables across threads as long as these variables are not classes that do things in the background.

    4. If you're accessing a database, make sure to use connection pooling.

    5. Do you get a ~90sec freeze when trying to stop the application pool? Make sure to terminate the ISAPI dll correctly.

     

    This is my original thread in which the nice people of this forum helped me identify some of these issues:

     


  3. 15 minutes ago, TomDevOps said:

    Now I don't get this - why do you want to login this way? Why you do not want to use REST and login that way? I do not know if WebView allow to execute your own JavaScript - if so, you could pass credentials to the form this way and then call submit, but again, I would not do this way.

     

    I try to understand your architecture and design choices, can you elaborate, please?

     

    I believe I might be able login by using LoadFromStrings() to feed the html form and auto-activating JS to TWebBrowser with the form's action set to my login url, which will bypass the need to actually modify the html content.


  4. 3 minutes ago, TomDevOps said:

    I think I know what you want to achieve, I understand that you want to have HTML/CSS/JS to avoid designing for each platform, so make everything in React, then your web-app and mobile-app can share HTML/CSS/JS stuff 🙂 

     

    If React is not an option, then use REST for communication and WebView for displaying the results only, this is what I did for one of the app.

    I have already written a Delphi ISAPI DLL that hooks into the microsoft IIS server and outputs HTML/JS/CSS.  That part is done and working well in all browsers.

     

    Now, for convenience  I would like to create a separate Android app (using Delphi) that uses the TWebBrowser component to open the web server's URL and automatically log-in by filling in the html form fields (user/pass) and press the "submit" button.

    With this second part I'm struggling since I don't know how to get to the HTML content displayed by the TWebBrowser component and how to trigger the form's "submit" button.


  5. 21 minutes ago, Remy Lebeau said:

    A better solution would be to forgo the TWebBrowser frontend altogether and redesign your web app to accept REST requests on the backend server to do the work you need.  Then use a standard UI on the client side to display the results as needed.

    You would have to check if the underlying platform browsers provide any APIs to automate them in code.  VCL's TWebBrowser is a wrapper for Internet Explorer and exposes access to its DOM API.  FMX's TWebBrowser does not expose similar functionality.

    The Delphi back-end is already rest-based, but the whole point of using HTML/JS for the front-end is to avoid having to design the UI for each platform (web/desktop/android) and rely on CSS to style the UI for every device without having to deal with device resolution/scale within Delphi code.

     


  6. I wrote a web-facing application that uses a combination of javascript/css/html for the front-end and Delphi for the back-end.

    I decided to create a simple Android app that would wrap the web application using a TWebBrowser control that automatically navigates to the web application's URL.

     

    The web application's requires a login and once logged in, a token is generated sever-side and then embedded into the html of subsequent server requests for future authentication.

     

    For security reasons, I do not want to store the code used to create the token within the Android app, so the easiest way for me to perform the login would be to automatically fill in the web form fields and simulate a "submit" action.

     

    Searching the web, I only found examples of how to do this using the WinAPI version of TWebBrowser, but I'm trying to write something that will work on Android and possibly iOS, any ideas?


  7. Looks like I'm encountering an IIS issue when using this scheme.

    In the ISAPI's DLL's finalization code I call "FDManager.Close" and in IIS, when stopping the application pool associated with the DLL it can take ~60 seconds before I can restart the pool, any ideas?

     

    This is the error I get:

    Quote

     

    ---------------------------
    Cannot Start Application Pool
    ---------------------------
    There was an error while performing this operation.

     

    Details:

    The service cannot accept control messages at this time. (Exception from HRESULT: 0x80070425)

     

     

     

    When checking my logs, It doesn't seem to exit cleanly (the debug message that the "DB Closed" does not show up in the log), but there doesn't seem to be an exception, here's the code I use:

      Try FDManager.Close; Except
        on E : Exception do {$IFDEF TRACEDEBUG}AddDebugEntry('Exception disconnecting from DB : '+E.Message){$ENDIF};
      End;
      {$IFDEF TRACEDEBUG}AddDebugEntry('DB Closed');{$ENDIF}

     

    When I run the same code locally as an EXE that handles the HTTP requests, there are no issues, any ideas?

     


  8. Thank you!

    Took me a bit to understand the mechanics, but with your help I managed to rewrite the code with minimal changes and so far it's working.

     

    P.S.

    "FDManager.Active :=True;" no longer works in Delphi 10.3, it simply doesn't exist anymore, but "FDManager.Open" seems to be the replacement.

    • Like 1

  9. 3 hours ago, Jacek Laskowski said:

    In multithread application you must use separated connection per thread.

    Does that mean that for every http request (that requires DB access) I have to create a new instance of TFDConnection and then connect to the DB?

     

    This is the first time I'm writing an ISAPI dll with DB access and the threading model is not exactly clear to me.

    My original (perhaps flawed) assumption was that IIS will load additional DLLs in separate threads to handle concurrent http requests.

     

    I would like to avoid the 100ms penalty for connecting to the DB on each request, any tips?


  10. I am trying to build a system that will handle concurrent DB requests efficiently, there's not a lot of load, but it should be able to handle more than 1 query concurrently.

    I am using FireBird v3.0.4 and Delphi 10.3.2 with the TFDConnection component.

    I am hosting the code in an ISAPI DLL that runs on IIS 7.5.

     

    Right now I'm creating the DB connection using TFDConnection in the DLL's "initialization" section.

     

    However, I noticed that even under low-load (2-3 users), I occasionally get an "[FireDAC][Phys][FB]Error reading data from the connection." exception when performing a DB query (not the same DB query, it seems pretty random).

     

    I tried to research the error and didn't find anything clear, the best I got was :

    https://forums.embarcadero.com/thread.jspa?threadID=245750

    Which seems to indicate that I should instance a copy of TFDConnection for every query.

     

    Before I make significant code changes to test this, I would welcome any tips on the best approach to handling DB concurrency.

     


  11. 57 minutes ago, rvk said:

    Any reason to prefer nbackup above gbak?

     

    Not my quote:

    Quote

     

    nbackup is a new backup utility that comes with Firebird 2.0. It offers possibilities not present in gbak - Firebird's pre-existing backup tool - but doesn't replace the latter. Both programmes have their strengths and weaknesses; they will likely coexist for some time to come.


     

    I basically used it cause that's the example I was able to find in the documentation for exporting the DB.

     

     

    1 hour ago, Cristian Peța said:

    Was Task Scheduler not good enough for this?

    In theory I could have used the windows task scheduler, but I needed more control and I'm using the service to perform other timed DB actions.

    And I couldn't just call nbackup directly from the task scheduler as I'm erasing old DB files and numbering new backups based on existing backup file names (a running counter in the file name) and some more logic is required for this.

     


  12. It took me hours of research to find the tools & documentation and haven't found any up-to-date text describing a similar process, so I decided to post my findings here in case someone else has the same requirement.

     

    I wrote (in Delphi) a simple windows service that runs the FireSQL "nbackup.exe" command line tool to create a DB backup every [x] hours using this command line:

    "c:\Program Files\Firebird\Firebird_3_0\nbackup.exe" -U SYSDBA -P [password] -B 0 "c:\path\database.FDB" "[output_file]"

     

    The windows service keeps [x] number of backup files (erasing old files) and creates a new backup every [y] minutes.  Personally, I used 1 full-db backup per hour, covering an entire week (168 files).

     

    I then use the open-source SyncThing P2P file sync tool (also running as a windows service) to securely & automatically sync the DB backups with every PC assigned as off-site backup.

     

    100% automated, secure, zero cost.

    • Thanks 2

  13. This is a bit old, but I've been blocking the windows screen-saver for media playback in every version of windows since 1999.

     

    I'm actually handling several actions, first I capture the WM_POWERBROADCAST message in the form:

    procedure WMPOWERBROADCAST(var M : TMessage); message WM_POWERBROADCAST;
    const
      PBT_APMQUERYSUSPEND       = $0000;
      PBT_APMQUERYSTANDBY       = $0001;
      PBT_APMQUERYSUSPENDFAILED = $0002;
      PBT_APMQUERYSTANDBYFAILED = $0003;
      PBT_APMSUSPEND            = $0004;
      PBT_APMSTANDBY            = $0005;
      PBT_APMRESUMECRITICAL     = $0006;
      PBT_APMRESUMESUSPEND      = $0007;
      PBT_APMRESUMESTANDBY      = $0008;
      PBT_APMBATTERYLOW         = $0009;
      PBT_APMPOWERSTATUSCHANGE  = $000A;
      PBT_APMOEMEVENT           = $000B;
      PBT_APMRESUMEAUTOMATIC    = $0012;
    begin
      Inherited;
      Case M.WParam of
        PBT_APMQUERYSUSPEND,
        PBT_APMSUSPEND       :
        Begin
          If SystemSuspended = False then
          Begin
            SystemSuspended   := True;
          End;
        End;
        PBT_APMRESUMEAUTOMATIC :
        Begin
          SystemSuspended := False;
        End;
      End;
      M.Result := Integer(True);
    end;

     

    Then on WinMsg (Application.OnMessage := WinMsg;) I do:

    procedure TMainForm.WinMsg(var Msg: TMsg; var Handled: Boolean);
    var
      ModWnd : HWnd;
      I      : Integer;
      mPos   : TPoint;
    begin
      If (Msg.Message = SC_SCREENSAVE) or (Msg.Message = SC_MONITORPOWER) then
      Begin
          Msg.wParam  := 0;
          Msg.Message := SC_Move;
          Handled     := True;
      End
        else
      If (Msg.Message = WM_SYSCOMMAND) then
      Begin
        Case Msg.WPARAM of
          SC_MONITORPOWER,
          SC_SCREENSAVE    :
          Begin
              Msg.wParam  := 0;
              Msg.Message := SC_Move;
              Handled     := True;
          End;
        End;
      End;
    End;

     

    Then on WndProc ( procedure WndProc(var Msg : TMessage); override;) :

    Quote

     

    procedure TMainForm.WndProc(var Msg : TMessage);

    begin

      If Msg.Msg = WM_SYSCOMMAND then
      Begin
        {$IFDEF LOCAL}
        //If Msg.WParam and $FFF0 = SC_MAXIMIZE then ShowMessage('maximized');
        {$ENDIF}
        If ((OPOTScreenSaver = True) and (plState <> stClosed) and (MainForm.WindowState = wsMaximized)) or (OPScreenSaverAW = True) then
        Begin
          Case Msg.WParam and $FFF0 of
            SC_SCREENSAVE,
            SC_MONITORPOWER :
            Begin
              Msg.Result := 0;
            End;
            else inherited WndProc(Msg);
          End;
        End
        Else inherited WndProc(Msg);
      End
        else
      If (Msg.Msg = SC_SCREENSAVE) or (Msg.Msg = SC_MONITORPOWER) then

      Begin

        Msg.Result := 0;

      End
      Else inherited WndProc(Msg);
    End;

     

     

     

     

    Hope this helps.


  14. To give me the largest work-space, I designed the Delphi IDE UI to look like the attached "delphi_before.jpg".

     

    However, under some conditions, windows throws apps a resize event (e.g. when turning on a secondary monitor), and then the Delphi IDE UI automatically changes to the attached "delphi_after.jpg" (the tool-bar drops down a row).

     

    Is there any way to lock the UI so my preferred layout remains fixed?

     

    P.S. This is on Win7 64bit.

    delphi_before.jpg

    delphi_after.jpg


  15. Using Delphi 10.3.2:

    I use the function below to send HTML eMails through Amazon SES using Indy.

    However, no matter what I tried, non-latin characters appear as "�" (for example, instead of "Oriënt Express", gMail displays "Ori�nt Express").

     

    I need to  be able to include non-latin characters in both the subject and the eMail's HTML body, what am I doing wrong?

     

    P.S.

    Not sure if it matters, but the HTML does contain "<meta charset="UTF-8">" in the "<HEAD>" block and I verified the source was properly encoded by saving it to a local utf8 html file which displays the encoded characters correctly.

    procedure SendEmailIndy(
            const SMTPServer: string;
            const SMTPPort: integer;
            const SMTPUserName : string;
            const SMTPPassword : string;
            const FromName, FromAddress: string;
            const ToAddresses: string; //comma "," separated list of e-mail addresses
            const CCAddresses: string; //comma "," separated list of e-mail addresses
            const BCCAddresses: string; //comma "," separated list of e-mail addresses
            const Subject: string;
            const EmailBody: string;
            const IsBodyHtml: Boolean; //verses Plain Text
            const Attachments: TStrings;
                  UseTLS : Boolean);
    var
        smtp: TIdSMTP; // IdSmtp.pas
        TLSHandler : TIdSSLIOHandlerSocketOpenSSL; // TLS support
        msg: TidMessage; // IdMessage.pas
        builder: TIdCustomMessageBuilder; //IdMessageBuilder.pas
        s: string;
        emailAddress: string;
    begin
      TLSHandler := nil;
      msg := TidMessage.Create(nil);
      msg.ContentType := 'text/html';
      msg.CharSet     := 'UTF-8';
      msg.ContentTransferEncoding := '8bit';
    
      try
        if IsBodyHtml then
        begin
          builder := TIdMessageBuilderHtml.Create;
          TIdMessageBuilderHtml(builder).Html.Text := EmailBody;
        end
          else
        begin
          builder := TIdMessageBuilderPlain.Create;
        end;
    
        try
          Try
            if Attachments <> nil then for s in Attachments do builder.Attachments.Add(s);
            builder.FillMessage(msg);
          Except
            on E : Exception do {$IFDEF TRACEDEBUG}AddDebugEntry('Exception : '+E.Message){$ENDIF};
          End;
        finally
          builder.Free;
        end;
    
        msg.From.Name    := FromName;
        msg.From.Address := FromAddress;
        msg.Subject      := Subject;
    
        //If the message is plaintext then we must fill the body outside of the PlainText email builder.
        //(the PlainTextBuilder is unable to build plaintext e-mail)
        if not IsBodyHtml then msg.Body.Text := EmailBody;
    
        for s in ToAddresses.Split([',']) do
        begin
          emailAddress := Trim(s);
          if emailAddress <> '' then
          begin
            with msg.recipients.Add do
            begin
              //Name := '<Name of recipient>';
              Address := emailAddress;
            end;
          end;
        end;
    
        for s in CCAddresses.Split([',']) do
        begin
          emailAddress := Trim(s);
          if emailAddress <> '' then msg.CCList.Add.Address := emailAddress;
        end;
    
        for s in BCCAddresses.Split([',']) do
        begin
          emailAddress := Trim(s);
          if emailAddress <> '' then msg.BccList.Add.Address := emailAddress;
        end;
    
        smtp := TIdSMTP.Create(nil);
        try
          smtp.Host     := SMTPServer;
          smtp.Port     := SMTPPort;
          smtp.Username := SMTPUserName;
          smtp.Password := SMTPPassword;
    
          If UseTLS = True then
          Begin
            TLSHandler     := TIdSSLIOHandlerSocketOpenSSL.Create;
            smtp.IOHandler := TLSHandler;
            smtp.UseTLS    := TIdUseTLS.utUseRequireTLS;
          End;
    
          Try
            smtp.Connect;
            try
              try
                smtp.Send(msg)
              Except
                on E : Exception do {$IFDEF TRACEDEBUG}AddDebugEntry('SMTP Send Exception : '+E.Message){$ENDIF};
              End;
            finally
              smtp.Disconnect;
            end;
          Except
            on E : Exception do {$IFDEF TRACEDEBUG}AddDebugEntry('SMTP Connect Exception : '+E.Message){$ENDIF};
          End;
        finally
          smtp.Free;
          If TLSHandler <> nil then TLSHandler.Free;
        end;
      finally
        msg.Free;
      end;
    end;

     

×