Jump to content

Mark Williams

Members
  • Content Count

    274
  • Joined

  • Last visited

Posts posted by Mark Williams


  1. 8 minutes ago, aehimself said:

    Just imagine your application running on a terminal server used by 100+. They will surely want to separate their data. 

    I don't actually want them to know where the data is and I also want it to be shared amongst anyone else who has access to the app, but only via my app.


  2. 3 minutes ago, Stefan Glienke said:

    There is no C:\Users\Public\Public Documents - that's just how they get displayed in the explorer - click into the address bar and you see its C:\Users\Public\Documents (by default - it can be anywhere else)

    Damn tricky fellas! I should have realised that!

     

    It's the third option. So I will place in Public Documents

     

    ShellGetFolderPath(CSIDL_COMMON_DOCUMENTS, Path);
    path := includeTrailingPathDelimiter(path);
    stream.saveToFile(path+'stream.txt');

    Saves to C:\Users\Public\Public Documents' .

     

    So all is good.

     

    I don't actually want users to try and open the files directly from the folder. My app just needs to know where they are so it can build a list of them (FindFirst...) and offer them via the app for opening. They could be anywhere as far as the app is concerned, it just needs to know how to find them.


  3. From within an app I save data obtained over the internet from a database to a local file so that when the user needs to reopen it can be loaded locally rather than across the net. Also, the user can save other data they are working on for later restoration.

     

    My inclination is to place these file in "C:\ProgramData" (CSIDL_COMMON_APPDATA), but I notice Embarcadero, for example, save new projects off "C:\Users\Public\Public Documents". Foxit also use it.

     

    Two questions:

    1. What is considered best practice for storing this sort of data?
    2.  How do you get the "C:\Users\Public\Public Documents"? There doesn't appear to be a constant value to use with SHGetFolderPathA to get it. All I can find is  CSIDL_COMMON_DOCUMENTS and that gets "C:\User\Public\Documents", which doesn't exist on my version of Windows10. 

  4. 3 minutes ago, Remy Lebeau said:

    Then you need to grep the RTL source files to find it, or just add the declaration to your own code. 

    I did try that, but still couldn't find it any reference to it.

     

    4 minutes ago, Remy Lebeau said:

    The *debugger* doesn't care about DLL reference, so I assume you mean the *compiler* instead?

    Yes. Sorry. Tired and brain functioning slower than usual snail pace.

     

    5 minutes ago, Remy Lebeau said:

    Of course it can.  Every Win32 API DLL can be, Microsoft ships both 32bit and 64bit versions of them.

    I have discovered that today. Because the because the compiler didn't recognise the unquoted reference to Shell32 I thought that the functions must have been re-packaged differently for 64 bit somehow (Shell64 or some such) .

     

    Anyway simply putting quote marks round Shell32 in my old function code worked fine. Live and learn.


  5. 22 minutes ago, David Heffernan said:

    GetLastError is bad advice. COM doesn't report errors that way. If you would just show a complete, minimal example, it would be easy to help. 

    Possibly, however it worked a treat in this case and helped me an identify the problem. Please see my last post. As for the code I have posted it earlier (see a few posts back) and note Fred's comments as to UnCoinitialize.


  6. You need a datasource that points to the master dataset.

     

    The Detail dataset needs to have its Mastersource property pointing to the dataSource (this step needs to be followed for both of the options specified below).

     

    You can then either:

     

    • use the object inspector for the Detail dataset to define the relationship with the Master table (you need to set IndexFieldNames, MasterFields and as already mentioned MasterSource)
    • or if the Detail dataset is an FDQuery you can define the relationship via a parameterized query entered into the SQL.Text property eg "SELECT [fields] FROM [mytable] WHERE [detail_table_foreign_key] = :[master_table_primary_key

    So in the latter case, if you had a master table called "employers" with aprimary key called "e_id" and a detail table called "employees" with a foreign key called "employer_id" your Detail query would need to be as follows: 

    SELECT * FROM employees WHERE employer_id = :e_id

    The parameter is case sensitive. It must match precisely the name of the primary key field in the Master table.
     

     


  7. 34 minutes ago, FredS said:

    On Windows 10 1903.18362.476.

     

    Also your code doesn't `CoUninitialize` if IIDL is nil.

    ..and what does GetLastError say when you get IIDL=nil?

     

    I found the code online some years ago. Hadn't noticed the failure to CoUninitialize. Thanks.

     

    I managed to get my old code to compile this evening by enclosing Shell32 in single quotes.

     

    I didn't realise that an error message would be generated if ILCreateFromPath returned nil. Thanks also for that suggestion.

     

    I can now see why my code was doing nothing. Unfortunately, the reason was rather embarrassing. for test purposes, I had hard coded the file path rather than use a dialog and as a result I was submitting a non-existent file!

     

    However, without your suggestion re GetLastError I would never have realised that (at least not for another few years).


  8. 31 minutes ago, FredS said:

    On Windows 10 1903.18362.476.

     

    Also your code doesn't `CoUninitialize` if IIDL is nil.

    ..and what does GetLastError say when you get IIDL=nil?

     

     

    38 minutes ago, FredS said:

    shell32 is declared in Winapi.ShlObj in the implementation section.

    
    const
      shell32 = 'shell32.dll';

     

    You in Berlin or UP, or something else?

    You know asking Qs without supplying sufficient information simply leads to what you see here..

     

    Sorry. I mistakenly thought Shell32 was restricted to 32 bit and that I was looking another function. I will know better in future (hopefully).

     

    Delphi 10.3 update 1. Latest However see response to your last message.


  9. 2 minutes ago, David Heffernan said:

    If you have 32 bit code that works then that same code will work in 64 bit. We can't see your code. 

    It's pretty much the same as mentioned in the link above. However:

    //Following is to enable opening of a folder and selecting of file
    const
      OFASI_EDIT = $0001;
      OFASI_OPENDESKTOP = $0002;
    
    {$IFDEF UNICODE}
    function ILCreateFromPath(pszPath: PChar): PItemIDList stdcall; external shell32
      name 'ILCreateFromPathW';
    {$ELSE}
    function ILCreateFromPath(pszPath: PChar): PItemIDList stdcall; external shell32
      name 'ILCreateFromPathA';
    {$ENDIF}
    procedure ILFree(pidl: PItemIDList) stdcall; external shell32;
    function SHOpenFolderAndSelectItems(pidlFolder: PItemIDList; cidl: Cardinal;
      apidl: pointer; dwFlags: DWORD): HRESULT; stdcall; external shell32;
    
    
    
    
    
    function OpenFolderAndSelectFile(const FileName: string): boolean;
    var
      IIDL: PItemIDList;
    begin
      result := false;
       CoInitialize(Nil);
      IIDL := ILCreateFromPath(PChar(FileName));
      if IIDL <> nil then
        try
          result := SHOpenFolderAndSelectItems(IIDL, 0, nil, 0) = S_OK;
        finally
          ILFree(IIDL);
          CoUninitialize;
        end;
    end;

    This works fine in older version of Delphi and 32bit  apps.

     

    It doesn't compile on 64 bit platform. I noted that Remy enclosed his references to Shell32 in single quotes. I have done the same and it now compiles. However, it doesn't work. Doesn't throw up any errors it just doesn't do anything.

     

    It fails to create a PItemIDList on the call to ILCreateFromPath. I have read on the Microsoft forums (MS Forum) that this may be a bug with Windows 10 64 bit only. @FredS mentions above that he has it working in Berlin and 64 bit.  He doesn't mention if that is on Windows 10 or not. 


  10. 40 minutes ago, Remy Lebeau said:

    Why not simply declare the function yourself in your own code?

    
    function SHOpenFolderAndSelectItems(pidlFolder: PCIDLIST_ABSOLUTE; cidl: UINT; apidl: PCUITEMID_CHILD_ARRAY; dwFlags: DWORD): HRESULT; stdcall; external 'shell32';

     

    I've tried that. I can't find which unit declares PCUITEMID_CHILD_ARRAY and my debugger is also unhappy with the reference to 'shell32'. Can Shell32 be referenced in 64 bit apps?


  11. 13 hours ago, FredS said:

     

    Works fine in Berlin+ Win64..

    I had it working in 32 bit using very similar code to that in the forum message which Dave Nottage has linked above.

     

    I can't get it to work in 64 bit. The references to "Shell32" throw up unrecognised errors and I am not sure what to replace them with. Have had a look at the Windows documentation and it says its a Shell32 function. 

    Can you explain what you did to get it to work in 64bit please?


  12. 17 minutes ago, David Heffernan said:

    TerminateThread does the job. But do expect your process to fail as a result. 

    Uhm... Are you sure? You said above it couldn't be killed. However, if it works it would solve the problem. I would only be killing the thread in circumstances where the thread had been cause to hung by MS Office. 

     

    I may just go with the clunky solution I referred to in original post and only allow Office to try and open powerpoint where Office 2010 is installed.


  13. 19 minutes ago, Anders Melander said:

    Variations of this question has been asked countless times. Always with the same answer: Don't do it.

    Why would you expect a different advice this time?

     

    Move the conversion into a separate process and kill that process instead.

    Hadn't thought of a separate process. That's an excellent idea. Thanks


  14. I am looking for a way to kill a hung thread, no questions asked!

     

    I've tried to find an answer to this on the web, but the replies are usually "that's really bad dude, debug your code" which I would say is sound advice.

     

    However, I am using OLE to open MS Powerpoint to convert to Powerpoint docs to pdf. This needs to be done in (sometimes large) batches along with many other conversion routines for other types of files.

     

    The problem is that if you try and open a password protected Powerpoint the process simply hangs, nothing you can do about it. Word is fine. You just feed it a fake password and it excepts if the password is wrong. Unfortunately, Powerpoint doesn't allow you to do that unless you use it's ProtectedWindowView. This will raise an exception if a password is needed. However, there are two problems with it:

     

    • It is only available for 2010 onwards (minor issue)
    • It's really clunky as it visibly opens Powerpoint and there is no way of stopping it doing that as far as I can see (deal breaker for me). 

     

    So my idea was to deal with each Powerpoint document in a separate thread. If after a period of time the thread doesn't terminate I can assume it has hung due to password issues, but then I need some way of killing it.


  15. I am trying to load a TFDQuery from a memory stream using the default format.

     

    I can do this fine from  file:

    Query.LoadFromFile('c:\data.DAT', sfAuto);

    If I load the same first into a stream and then call LoadFromStream I get error code 710 "Invalid Binary Storage Format". 

     

    I have the DefaultStoreExt of the ResourceOptions for the Query's connection component set to ".DAT" and its DefaultStoreFormat to sfAuto.

     

    Is there anything else I should be doing?

     

    NB I have also tried setting the DefaultStoreExt without the lading period.


  16. 10 hours ago, aehimself said:

    Completely agree with @David Heffernan here. From what I imagine your app is like, you would have easier (and more user friendly) results looking into writing a Word plugin instead. 

    I don't have any experience with Office plugins. I thought about it at the time and it looked difficult and I seem to remember there were certain things I thought I would be unable to do using a plugin. I considered having a look at Add-in Express, but I'm keen to avoid reliance on third party tools as far as possible. I think I'll have a look at it and see if it can do what I want.

     


  17. I create an instance of Word using TWordApplication:

    WordApp := TWordApplication.Create(frmWord);
    WordApp.ConnectKind := ckNewInstance;

    The Word app is owned by my form. TFrmWord is basically just a form with a toolbar, which is designed to sit above the Word app. FrmWord needs:

    • to be visible when the Word app it has created is visible.
    • minimize when the Word app is minimized.
    • Move when the Word app is moved.
    • Resize when the Word app is resized

     

    And vice versa for the Word app (ie any movement, resizing, minmimizing etc needs to be relected back to my form).

     

    Basically, I am trying to get this to work as if my form and the Word instance are one form. Before anyone suggests, I have tried OLEContainers. They are, of course, buggy, but there are other problems:

    • It is problematic with the Word ribbon when more than one container's open.
    • It has horrific refresh problems when the OLEContainer is moved on to a second monitor.
    • If the user opens a word side panel (eg search or help) and then closes it the Word object does not refresh with the result that there is blank space where the panel used to be and detecting this and trying to refresh is difficult.
    • There were a number of other problems, which I can't remember now, but so many as I gave up the ghost.

     

    So I worked with SetWindowPos and timer's instead. It took weeks of trial and error to do what I want it to do and in the end I had it working reasonably well. However, it seems that in a recent Windows update the way SetWindowPos works has changed. Apparently, MS has decided to place further restrictions on one app trying to pop itself on top of another active app. I can understand why, but it has caused problems for the way I have tried to marry up my form and the Word app. Before I spend weeks more time trying to figure out how now to do this, I thought it was worth a post to see if anyone can think of a simpler way of achieving this.

×