Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to prevent a Delphi application from using Virtual Storage on Vista/Win 7 without enabling Runtime Themes?

Tags:

delphi

The question pretty much says it all.

I have an app with an older component that doesn't work right if runtime themes are enabled. But if I don't enable them, the app always ends up messing with the virtual store.

Thanks!

Update:

Using Mark's solution below, the application no longer writes to the Virtual Store. But, now it won't access a tdb file (Tiny Database file) that it needs. This tdb file is the same file that was being written to the Virtual store. Any ideas on how I can give it access to the tdb file and still prevent writing the Virtual Store?

like image 480
croceldon Avatar asked Jan 23 '23 02:01

croceldon


1 Answers

You need to add a manifest (resource) to your exe.

In the manifest is an XML Resource with content similar to that below. The TrustInfo is the key section that causes the VirtualStore not to be used.

This example has the Microsoft.Windows.Common-Controls assembly referenced which enables runtime themes. If you remove that from the manifest you can still keep the TrustInfo section.

Vista uses the TrustInfo to decide that the application "knows" about the UAC restrictions and does not use the VirtualStore for that application

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="Delphi 7"
    version="7.1.0.0" 
    processorArchitecture="*"/>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Microsoft.Windows.Common-Controls"
        version="6.0.0.0"
        publicKeyToken="6595b64144ccf1df"
        language="*"
        processorArchitecture="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
        </requestedPrivileges>
    </security>
  </trustInfo>
</assembly> 

Here is a page with more detail on how to create and use the manifest files:
http://ruminatedrumblings.blogspot.com/2008/03/vista-uac-manifest.html

And a Microsoft page on the Application Manifest Schema:
http://msdn.microsoft.com/en-us/library/bb756929.aspx

Of course once you do this you will no longer be able to write data to c:\program files\ or other protected locations with UAC turned on. That is why Microsoft created the virtual store in the first place. It is intended to keep old applications running that expected to be able to write to those (now protected) locations.

You have a few different options:

1: Change the file location

Move the tdb file to a different location. This is the ideal case but can require the most code changes. See the question "Correct way to design around Windows UAC Limitations" for some suggestions. The Microsoft recommendation is to store data that the user does not name under the "Application Data" folder. I tried this but it makes it very hard for users to find the data to move it to a different computer. I have moved all of my user data, even if the user does not specifically save the file, to the My Documents folder. That way when they get a new computer they can just move "My Documents" (and most do anyway) and all of my application data will move as well.

2: Change the permissions on the file to allow standard users to read/write the file. Either your installer could do this or you could updated it after the fact, but you will need to be running as administrator to make the change.

3: Force your application to run as administrator. If you set the execution level be "requireAdministrator" as Sertac notes you will be able to write to the files, but then your users will get a UAC Elevation prompt every time they run your application.

Also note that if you are upgrading users who have been running and saving data to the Virtual Store there is nothing that automatically moves that data to the new location. Once you add the manifest to your application it will start to see the files that are actually under c:\program files*. You may need to look for files in the virtual store and copy them to the new location for the user. Below is an example. In my case license files were stored under the install directory. After I upgraded my application I needed to look for the old license files and move them to the new location:

procedure TResetMain.CopyVirtFiles();
var
  VirtLicDir: string;
  NewLicDir: string;
  FileOp: TSHFileOp;

  TempPath : array[0..MAX_PATH] of Char;
begin


  SHGetFolderPath(Application.Handle, CSIDL_LOCAL_APPDATA, 0, 0, TempPath);
  VirtLicDir := TempPath + '\VirtualStore\Program Files\My Company\Licenses';

  NewLicDir := GetMyConfigDir();
  if NewLicDir <> '' then
  begin
    NewLicDir := IncludeTrailingPathDelimiter(NewLicDir) + 'User Licenses';
  end;

  // If the Virtual license directory exists but not the new directory we
  // know this is the first time the updated application has been run
  // and we need to move the files to the correct location.
  if DirectoryExists(VirtLicDir) and Not DirectoryExists(NewLicDir) then
  begin
    ForceDirectories(NewLicDir);

    FileOp := TSHFileOp.Create(nil);

    FileOp.FileList.Add(VirtLicDir + '\*.*');
    FileOp.Destination := NewLicDir;

    FileOp.Action := faMove;
    FileOp.SHOptions := [ofFilesOnly, ofNoConfirmation, ofNoConfirmMKDir, ofRenameOnCollision, ofSilent];
    FileOp.Execute;

    FreeAndNil(FileOp);
  end;

end;
like image 72
Mark Elder Avatar answered Mar 07 '23 02:03

Mark Elder