Jump to content

Mark Williams

Members
  • Content Count

    274
  • Joined

  • Last visited

Posts posted by Mark Williams


  1. Thanks again.

     

    But by using either active := false or CloseConnectionDef am I having to reconnect all over again with all the necessary overhead? I am not trying to switch server, merely which table I wish to query.  

     

    I am obviously keen to avoid any additional overhead.

     

    I also still don't understand why I only get this error when I connect with an internal ip not with a local ip. It seems to me that if there is an error with the one there should also be an error with the other. 

     

     


  2. Within an ISAPI dll I create a pooled connection:

     

    Params := TStringList.Create;
     try
      Params.Add('DriverID=PG');
      Params.Add('User_Name=*****');
      Params.Add('UnknownFormat=BYTEA');
      Params.Add('Pooled=True');
    
      FDManager := TFDManager.Create(Nil);
      with FDManager do
        begin
           ResourceOptions.SilentMode:=True;
           ResourceOptions.AutoReconnect:=True;
           AddConnectionDef(FD_POOLED_CONN, 'PG', Params);
          Active := true;
        end;
     finally
       Params.Free;
     end;

    The server is left as the default ie localhost.

     

    The server is a machine on my local network (ip 192.168.0.12). It is also a web server with an external id (say 52.132.222.67)

     

    It hosts a number of databases and to change the referenced database I call the following:

      var
        Def : IFDStanConnectionDef;
    begin
    
      try
        Def : =FDManager.ConnectionDefs.FindConnectionDef(FD_POOLED_CONN);
    
        if Def.Params.Values['Database'] <> dbName then
          Def.Params.Values['Database'] := dbName;
    
        if assigned(Query) then
          Query.Close;
    
        if assigned(Query) then
          Query.ConnectionName := FD_POOLED_CONN;
    
      except
        on E: Exception do
          AddToLog('Error SetDatabase: '+e.Message, leMajorError);
      end;

    If I call the dll via the external ip address then my function to change the referenced database works just fine.

     

    If I use local ip it fails with the following error:

    Quote

    [FireDAC] [Stan] [Def] -251. Cannot change definition [pooled_connection]. It has associated connection.

    Why do I get this error if I use the local ip, but not if I call the dll with the external ip and is there any way around it?


  3. I use a Firedac query with a PostgreSQL database

     

    I start with an empty query (only option changed from default is CachedUpdates to true) and append records to it as required.

     

    To get the required table structure I query the database table using a select statement that I know will return no records (docid=0)

     

    Then I add the new records using Append. 

     

    Following this I add an index:

     if indexes.FindIndex('doc_id')<>Nil then
       exit;
     Index := Indexes.Add;
     Index.Name := 'doc_id';
     Index.Fields := 'docid';
     Index.Active := true;
     IndexesActive := true;
     IndexName := 'doc_id';

    When I try to call FindKey with different docid values it returns true but never changes the actual record. I have the same problem if I use Locate.

     

    If I iterate using First, Next I can retrieve the correct records.

     

    I should also add that I tried to assign this table's data to a FDMemTable, but the assignment failed.

     

    I can only assume there is something wrong with my table structure, but I have been unable to discover what. It also seems odd that both FindKey and Locate report true, but do not move to the correct record which I know exists.

     

    I can make this work by looping through the query record by record, but as it as to do this a potentially large number of times there will be a big performance hit.

     

    Does anyone have any ideas why FindKey does not work in the above scenario?


  4. In case any one is interested there is a partial solution to this. Create a dummy document and close it before opening your actual document. Word apparently only latches on to the first instance opened. So:

    //Open fake doc
    dummyDoc:=WordApp.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True);
    dummyDoc.Close(false, emptyParam, emptyParam);
    
    //Open actual document
     WordApp.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True);
    RealDoc:=WordApp.ActiveDocument;

    Thanks to the guys at Add-In Express for this suggestion. 

     

    However, it has some limitations:

    1. It doesn't seem to work if you have a Word window hung somewhere in the background.
    2. It doesn't catch new word windows opened within your instance of Word. So if your users open a new or existing word document  from eg the backstage of your word instance, it will open in your instance. 

    To overcome both the problems I have implemented the following:

    1. Disable all functionality within my word instance for opening existing or new documents.
    2. Use the DocumentChange event to catch any documents that may still be loaded in your instance of Word, but do the following to overcome the issues mentioned in the previous post:
    •  In the documentChange event check if there is more than one document in your word instance.
    • If there is, fire a timer event (interval of 100 ms seems to be sufficient) and add similar code to that shown in the first post to the timer event.

    Still a clunky last resort, but I really need to stop word documents being opened in my instance.


  5. I am automating Word via TWordApplication. I would like to create the word document in its own unique instance of Word. It is possible to open the document in its own instance by setting the ConnectKind to ckNewInstance, but Word is then a bit of a bandit and if the user open a new word document other than via my app my word instance gets hijacked by the new Word document.

     

    It doesn't sound like such a big problem, but these are some of the consequences:

    1. If the pirate document enters modal mode so does my document and my app can't do anything with it. Of course, if the user puts my document into modal mode I have the same issue and I have to respond to OLEExceptions for rejected calls. But in he case of my own document I can pop up it up from my app for the user to deal with the modal dialog before proceeding. If it is a pirate document (and there could well be more than one)  it is a bit yuk to tell the user to either find it themselves and kill the modal dialog or to pop up all possible docs that might be modal.
    2. Even worse than this is that if the user opens a blank document via my exe and then open an existing word document not via my app Word commits high sea piracy and hijacks my blank document completely so that unless I monitor this my app is dealing with the wrong document altogether.

     

    There is a registry hack to force Word to open in a new instance every time, but I don't want to force that on users, plus I think it requires admin privileges.

     

    In the absence of a ckUniqueInstance constant I have been trying to replicate that effect by monitoring the onDocumentChange event of the Word application and checking if the application's documents count goes above 1. If so I close the pirate document and re-open it in its own instance.

     

    I appreciate this is not perhaps the most elegant solution and I also suspect I am going to be told not to interfere with the user experience in this way. I having quite made my mind up as yet. But  it almost works, subject to two issues (so far) one major, one minor and I am curious to see if I can get it to work.

     

    First, when my app opens a new blank document I add a custom variable to the document simply so that it is flagged as having content. This stops Word trying to hijack the document when an existing document is subsequently opened.

     Doc.Variables.Add('AnyOldRubbish', 1); //this flags the document as having content

    And then in the DocumentChange event:

     

    procedure TForm.DocumentChange(ASender: TObject); 
      var 
        i : integer; 
        WordAppT : TWordApplication; 
        Template, FileName : OleVariant; 
    begin 
      if wordApp.Documents.Count>1 then 
        begin 
          for i:=WordApp.Documents.Count downTo 1 do 
          begin 
            Template := EmptyParam; 
     
            if WordApp.Documents.Item(i) <> Doc then 
              begin 
                FileName := WordApp.Documents.Item(i).FullName; 
                if not FileExists(FileName) then 
                  FileName := ''; 
                Template := WordApp.Documents.Item(i).Get_AttachedTemplate; 
                if Uppercase(Template) = 'NORMAL.DOTM' then 
                  Template := EmptyParam; 
     
                //Close the offending document! 
                WordApp.Documents.Item(i).Close(false, emptyParam, emptyParam); {PROBLEM CODE - see below} 
     
                //Reopen it in a separate instance of Word 
                WordAppT := TWordApplication.Create(Nil); 
                WordAppT.ConnectKind := ckNewInstance; 
     
                if FileName<>'' then 
                  WordAppT.Documents.open(FileName, EmptyParam, EmptyParam, EmptyParam, EmptyParam, 
                  EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, 
                  EmptyParam, EmptyParam, EmptyParam, EmptyParam) 
                else 
                 WordAppT.Documents.Add(EmptyParam, EmptyParam, EmptyParam, True); 
     
                WordAppT.Visible := true; 
                WordAPPT.ActiveWindow.Activate; 
              end; 
          end; 
        end; 
    end; 

    The two problems:

    1. By adding the variable I am marking blank documents as having been changed when they should be marked as unchanged. Not a major problem. Would be good not to have to do this, but if I can solve problem 2 I can deal with this issue by checking the word count property of the document to see if there has been any substantive change, Not perfec, but good enough for my purposes.
    2. The above code works neatly when the user tries to open a blank pirate document, but not when they try to open an existing document. In the latter case, whilst the pirate document does get opened in its own new instance, the closing it in the first place causes my original document to become unresponsive and I can't for the life of me work out why.

     

    If I break the process down into separate chunks it works fine:

    • Open my document.
    • Open an offending existing document outside of my app.
    • Add a button to close the offending document.
    • Add another button to reopen the offending document.

     

    This works leading me to believe it might be a timing issue. So I tried adding a sleep before and after the call to close the pirate document, but made no difference.


  6. Has anyone had problems installing 10.3.3 via the web installer. It keeps failing on me some way through the process saying it can't unzip a file.

     

    I have a partial installation, but only 32bit. I also have no idea what's missing.

     

    I have tried installing both 32 bit and 64 bit windows versions. I have also tried installing just 64 bit, but that also fails during process.

     


  7. 1 hour ago, Arnaud Bouchez said:

    Note that 32-bit and 64-bit COM servers are registered separated, IIRC.

     

    To communicate between apps, I would rather use a regular REST server. Our little mORMot can re-use an interface to define the service, just like with COM.
    Check https://medium.com/@step.bester/web-apis-with-mormot-891c0ecd3950

    Thanks for the tip. I will look into it if I have problems via com. My out of process server is only intended for (and will only work with) a Word Add-In that I am developing. So I may run into problems with 32 bit via 64 bit if users are running 32 bit word. So that's something I'm going to have to overcome.


  8. 12 hours ago, Anders Melander said:
    • Run Delphi as as administrator.
    • Enter /regserver in Run|Parameters

    Thanks that worked. I originally just ran my app from a shortcut with the /regserver parameter, but that didn't seem to work ie no entries in the register. Running it through the IDE with /regserver did the job. 

     

    However, how do I import this type library into Delphi? As I mention above it is 64 bit and therefore you can't do it via the IDE so I have tried with tlibimp.exe via a cmd prompt. It still report an error loading the type library. 


  9. I have converted an existing 64bit app into an out of process com server by adding an automation object, but now I am completely lost as to how to register it and how to import the type library.

     

    RegSvr32 fails with "DLLRegisterServer" not found. I have tried running with "/regserver" switch. In non admin mode it crashes and in admin mode it asks if you want to make changes etc, but then doesn't seem to run the app (no sign of it in processes) although no error appears.

     

    I tried using tlibimp.exe to import the type library, but that reported an error loading the type library. I assume that is because there is no type library to import on account of the failure to register the server in the first place!


  10. 11 hours ago, Cristian Peța said:

    THTTPClient  doesn't work on Windows 7 and XP with SSL.

    The post seems to mention a fix, but I haven't tried it. I have an old Windows 7 machine here somewhere and will check it out. However, since Windows 7 is no longer supported and, presumably, no longer secure to use and my app has to be run in a secure environment, it is probably okay for me to say that Windows 7 is not supported.


  11. I'm doing a lot more than polling an url, but I don't appear to be having any issues with THTTPClient and it is pretty simple to set up and use and it doesn't need OpenSSL,  I personally can't see any particular reason to switch to Indy. although it would be pretty simple to do so,

     

    I was just wondering if there were any known issues/frailties with THTTPClient that would provide an imperative for switching to Indy.


  12. I am configuring my server (TWebModule) to dispense with TLS where the request is from a machine on a local network.

     

    To do this I am checking the following properties of TWebRequest:

       ServerPort - to see if the request has come in on 443

      If not 443 then

        RemoteAddr - to see if the ip address of the requestee falls within the private ranges:

         10.0.0.0        -   10.255.255.255  
         172.16.0.0      -   172.31.255.255  
         192.168.0.0     -   192.168.255.255

    If it doesn't  reject the request and ask it to be made over http.

     

    Is this a sound approach? Are there any dangers I should be aware of?

     

    If this is a sound approach I assume I should  add a config files to specify the permitted ip ranges so that it could include private networks that fall out

     


  13. I have been testing TIDHttp against THTTPClient.

     

    Using a local server with TLS1 and nothing else going on but the test downloads I have found that there is little difference in speed between TIDHttp and THttpClient.

     

    I have been using THTTPClient up until now, but thought I had better check out TIDHTTP. 

     

    If speed is not a reason for changing and I am otherwise happy with the THTTPClient are there any other reasons I should consider changing for?


  14. I don't think there is.  

     

    20 hours ago, bazzer747 said:

    I also use IndexOf to check if a certain item is in the list, but that checks the Name side

    IndexOf checks the whole item including the NameValueSeparator. Presumably you mean IndexofName.

     

    You can use ValueFromIndex to iterate through each of the items in the TStrings:

     

    for i := 0 to myList.count-1 do
      if myList.ValueFromIndex='myvalue' then 
    	begin
    	 //Got it
    	 break;
    	end;

     


  15. Within a Delphi document management application I use COM to open MS word documents. Hyperlinks can be added to the Word document which are references to image files maintained by the Delphi application. When the user clicks on a hyperlink in Word a message is returned to the Delphi app which in turn responds by opening a form which displays the required image.

     

    Whilst I want the image form to be top of the z-rder and activated I would like to keep the Word window just below it in the z-order ie above any other open windows in the Delphi application including the main form.

     

    First I tried:

    ImgForm := TFrmImage.Create(Nil);
    ImgForm.ParentWindow := Wordwnd;
    ImgForm.Show;
    
    SetWindowPos(WordWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE OR SWP_NOACTIVATE);
    SetWindowPos(ImgForm.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE OR SWP_NOACTIVATE);

    This didn't work. The application main form appeared on top of the Word window.

     

    I then tried creating the image form without activating it:

    ImgForm := TFrmImage.Create(Nil);
    ImgForm.visible := false;
    ImgForm.ParentWindow := Wordwnd;
    
    SetWindowPos(WordWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE OR SWP_NOACTIVATE);
    SetWindowPos(ImgForm.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE OR SWP_NOACTIVATE);

    Same result!.

     

    I am using a Delphi Add-In within Word to provide additional ribbon functions etc. Somewhat tongue in cheek I tried a different approach. When the user clicks on a hyperlink the Add-In creates a new form "HostForm" and then sends  a request through to the Delphi app to open ImgForm. The Delphi App does the following:

    ImgForm := TImgForm.Create(Nil);
    ImgForm.Visible := false;
    ImgForm.borderStyle := bsNone;
    ImgForm.ParentWindow := Wnd; //ie the handle of HostForm
    ImgForm.Align := alClient;
    ImgForm.Visible := true;

    Much to my amazement it worked. ImgForm was embedded in HostForm. HostForm was top of the Zorder with the word window below it and the Delphi app below the word window.

     

    The only thing that didn't work was the alignment. A couple of questions:

     

    1. Is it possible simply to align ImgForm to HostForm or will I need to deal with it in HostForm's resize event?
    2. I have noticed that if  I close the Delphi app ImgForm is also closed ie it just disappears from HostForm and if I close HostForm, ImgForm remains in existence albeit it cannot be seen. These effects are I suppose to be expected and should be easily handled. But am I missing more potentially significant problems that will be more difficult or  even impossible to handle?

     

     


  16. 1 hour ago, David Heffernan said:

    Wait a minute. If its a 64 bit add in then why are you casting the pointer to a 32 bit integer? 

    Ah ha! The penny at last drops. The dangers of copying code that works in 32 bit, but no longer in 64 bit.

     

    My DLL is 64bit, but when I created a test executable to try and send the message I must have compiled it as 32 bit rather than 64 so it worked.

     

    The SendMessage needed to be:

    WinAPI.Windows.SendMessage(Hdl, WM_COPYDATA, 0, LPARAM(@copyDataStruct));

    That was the only reason the message was not getting through and had I realised this 48 hours ago I might have some hair left. 

     

    It's time for that Lockdown drink! I will raise a glass to you

    • Like 1
×