-2

More and more users complain that they cannot install/start my application.

What is the problem:

Using System.SYSUTILS.ForceDirectories(...) a directory structure is built consisting of 4 directories, which do not exist in the first place. This happens when the application starts for the first time. On some machines this seems to be impossible, because ForceDirectories() returns False.

I know exactly where the error occurs (the following is just a code snippet):

if ( not TDirectory.Exists(self.TempInstallPath) ) then
begin
    if (System.SYSUTILS.ForceDirectories(self.TempInstallPath)) then
      begin
        uGlobalFiles.DeleteDirectoryContents(self.TempInstallPath, self.GetLoggingFunc());
        if ( not self.copyDatabase(source) ) then
          raise TCopyDatabase_Exception.Create('Database could not be copied to: ' + sLineBreak + self.TempInstallPath);
      end
      else
      begin
        raise TPathCouldNotBeCreated_Exception.Create('adding a new directory failed with error: ' + sLineBreak + SysErrorMessage(GetLastError)
                + sLineBreak
                + 'destination folder: ' + self.TempInstallPath
                + sLineBreak
                + 'local user folder:' + self.LocaluserPath
                + sLineBreak
                + 'session time stamp:' + self.TimeStamp);
      end;
end;

Which path is used:

For good reason I decided on creating the paths and information in the Local AppData folder of the user. I get the value from the registry:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders

What I did so far: So far, I was not able to reproduce the exact error. I tried to take ownership from the user, but that shows a different error. Also messing around with special permissions in that directory - denying "create folder" etc - all to no avail.

Is there anything I am missing or that I can do in those cases?

Help is much appreciated, thanks a lot!

UPDATE1

Now I get the local user folder doing this:

function GetShellFolder(CSIDLFolder : integer) : string;
begin
SetLength(Result, MAX_PATH);
  SHGetSpecialFolderPath(0, PChar(Result), CSIDLFolder, false);  // (CSIDL_LOCAL_APPDATA -- FOLDERID_LocalAppData)
  SetLength(Result, StrLen(PChar(Result)));
  if (Result <> '') then
    Result  := IncludeTrailingBackslash(Result);
end;

UPDATE2

Since I already used SysErrorMessage(GetLastError), I know the error message, which literally says that there is no error. I also know (from the customer) that the first folder (out of the 4 folders to create) was created as a hidden folder, the others were not created at all. She can create folders using the context menu, though.

UPDATE3

The change mentioned in UPDATE1 resolved the error. Since nothing was stored in the registry:

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders

the program tried to write directly into C:\Program Files... (path of executable) where some users do not have permissions to write.

I accepted the solution of Andre Ruebel, but everybody who commented taught me something on the matter. Thanks to everybody.

UPDATE4

To sum it all up. The problem was indeed the way the local user path was extracted. In the case of this specific user, the registry key had no value. Therefore the data was copied to the installation directory, which lies under Program Files...

To make a long story short: The application data needs read/write access which the user is not privileged with, so the error occurred. Thanks again to all the answers, they lead me into the right direction.

rocksteady
  • 2,320
  • 5
  • 24
  • 40
  • 1
    Not sure if this is relevant, but when opening that registry key right the first entry below (Standard) says: "!Do not use this registry key" "Use the SHGetFolderPath or SHGetKnownFolderPath function instead" – Uwe Raabe Jun 07 '16 at 12:53
  • 2
    That's not how to find a special folder location. Use the api function. – David Heffernan Jun 07 '16 at 13:20
  • Thanks, I am always happy to learn something new. You know, this is legacy code at its best and I am more of a python guy :) – rocksteady Jun 07 '16 at 13:26
  • I found [this](http://stackoverflow.com/questions/12771473/finding-a-windows-users-true-application-data-folder) which explains it quite well. – rocksteady Jun 07 '16 at 13:36
  • Do some debugging. Find out what the error code is. Since you know what it is, and since you did not tell us, one can only assume that you ignored this critical information. Also find out the value of `self.TempInstallPath`. – David Heffernan Jun 07 '16 at 14:14
  • The problem is, that I cannot reproduce the error, like I said. The information I have is sparse. – rocksteady Jun 07 '16 at 14:42
  • 1
    So do some debugging. You don't need to reproduce it yourself. You need to obtain logged debugging output from the computer that can reproduce it. You are wasting your time trying to guess what the problem is. Don't ever do that. Don't ever fix problems until you understand them. Otherwise you'll just break something else. So, do the debugging, obtain the information, understand the problem, and then attempt to find a solution. – David Heffernan Jun 07 '16 at 15:04
  • 1
    You should really consider using an actual installer in order to avoid such issues. Writing your own installer, especially for later versions of Windows, is major overhead. Use Inno Setup, for example. – Jerry Dodge Jun 07 '16 at 15:15
  • @DavidHeffernan You are so right. The only problem: This is a business environment and I am not allowed to debug on customers machines. I must try to reproducde it. – rocksteady Jun 07 '16 at 18:16
  • These are hard rules :) – rocksteady Jun 07 '16 at 18:16
  • @JerryDodge I use InnoSetup and this is a good point you made. You know, I just started to take over this legacy code and I am happy about every kind of help - even 3 downvotes :) – rocksteady Jun 07 '16 at 18:19
  • 2
    Of course you can debug it. Do what I said. Add logging to your program. Get diagnostics. It's called trace debugging. A long forgotten skill with modern devs who think debugging means IDE. – David Heffernan Jun 07 '16 at 20:59
  • Ok, thank you. I am going to have a deeper look into that. To be honest, this sems to be the best answer in my case. – rocksteady Jun 08 '16 at 06:06
  • This may be unrelated but I think forcedirectories returns false if the folder already exists. If you're throwing an error in your prog based on false then.... I always check for folder before attempting to create it. Just a thought. – Admiral Noisey Bottom Jun 08 '16 at 06:52
  • I have the line `if ( not TDirectory.Exists(self.TempInstallPath) ) then` in it so this is safe. – rocksteady Jun 08 '16 at 08:01
  • 1
    ForceDirectories returns true of the directory already exists. It would be insane to do otherwise. Which means your check is pointless and should be removed. – David Heffernan Jun 08 '16 at 22:36

1 Answers1

3

use

var
  P: array [0 .. max_path] of Char;
begin
  SHGetFolderPath(0, CSIDL_APPDATA, 0, 0, @P[0]);
end;

to get the Appdata directory. If the forcedirectories function fails, use

SysErrorMessage(getlasterror)

to get a meaningful error message.

Andre Ruebel
  • 518
  • 3
  • 12