Jump to content




Recent Status Updates

  • Photo
      18 Aug
    KodeKool

    When faced with a wall of errors and no hope to fix them, remember the following "Programs always do what you tell them to, and seldom what you want them to, but eventually you'll run out of things that can go wrong and it'll just work. and that's the secret to good programming."

    Show comments (2)
View All Updates

Developed by Kemal Taskin
Photo
- - - - -

Using Special Folders to Comply with UAC Enabled Windows, with Delphi code

pascal

  • Please log in to reply
2 replies to this topic

#1 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,318 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 11 November 2010 - 03:15 AM

With Windows is getting stricter and stricter on its security making life a bit harder for software developers. For example, it used to be no problem at all to alter files in root folder of C:\ or files in Program Files or even files under Windows folder. But now we need administrative privilege to alter files under those "critical" locations. And with UAC enabled, even if the user definitely has the administrative privilege they will still be annoyed by elevation privilege request dialog each time we have to alter the "critical" files. This thread, for example, display some problem on handling stricter Windows security.


In the old days, we used to store our programs' data and configuration in the same location as our programs/binaries. It's easier to manage, indeed. While you still be allowed to do that, but in UAC enabled Windows now you can not alter those files unless the user has administrative privilege AND explicitly permit the program to do that action. In some programs with some kind of "critical" mission (e.g. disk defragmenter, anti virus, installer, or uninstaller) this should be no problem. The user will understand that the program has important mission. But what if our program is doing ordinary actions, like text editing, games, or news reader? They will be curious if such ordinary tasks requires elevation, while in fact we only need the elevation to alter our configuration file.


The answer is simple, just don't put your program's data and configuration file under those "critical" folders. However you can not just put them into some random locations, or the user maybe confused and delete them accidentally. There are some locations predefined by Windows which you can use for this purpose. Utilization of these predefined locations also means you can achieve some kind of uniformity, thus easier to handle than putting in random location.


This article will explain my own strategy in deploying programs under UAC enabled Windows by utilizing the predefined folder locations.


Some Predefined Locations

Windows has predefined some locations. By predefined I mean some locations that you can get by asking Windows with supplying a constant value. So given the same environment (i.e. same interactive user that logged on and no critical windows configuration change since the last query), the query will return the same value all the time.


Some of these predefined folder locations that we will use are:


APPDATA

This folder is unique for each user. So each user will get different folder when asking Windows for this folder. But for the same user the returned folder stays the same. Note that the content of this folder is subject for roaming profile, it will be copied to wherever machine the user is logging on if they (and if their networking system support) use/activate their roaming profile.


This folder is very useful if you want to store program's data or configuration that only applicable for a specific user. For example, this folder (or LOCAL_APPDATA folder if you don't want roaming capability) is the perfect choice to store user's preference, such as prefered GUI arrangements, most recent files, or things like that.


LOCAL_APPDATA

Same like APPDATA, this folder is unique for each user. But its content will not copied over to another machine even if the user using roaming profile. It's a real local folder. Same like APPDATA, this folder is perfect choice to store user's preference.


COMMON_APPDATA

This folder is unique for a machine. So every user will get the same folder when asking windows for this folder. This folder is very useful if you want to place program's data or configuration that is applicable for all users. Originally the content of this folder is writable only by admin users. Standard users can only read from this location. But you can create your own folder in this location and adjust the required read/write permissions on your folder.


COMMON_DOCUMENTS

This folder is similar with COMMON_APPDATA. The difference is that by default its content is available to read and write by all users. So storing files here will be exposed uniformly to all users without need to adjusting write access for non-admin users.


MY DOCUMENTS

This folder is usually unique for each user. So each user will get different folder when asking Windows. But note that I have said "usually unique" instead of "unique". That's because users can easily modify their My Documents folders which may caused several users have their "My Documents" pointing to the same folder.


I usually use this folder as the starting folder when displaying "Save As" dialog when the user wants to save something. I never use this folder to save program's data or configuration. Not only because it will break its "My" part (means only users should store documents there, not us), but also due to its rather unreliability (remember that users can easily change it, which will make us to "start over" again with our program's data and configuration).


My Strategy


  • Always store binaries of your software under Program Files folder.

    This way in UAC enabled Windows you will get lesser possibility of virus or any other malware programs hijack your programs. Because they need administrative privilege to alter anything stored under Program Files. In order to get that they need to ask explicitly for permission from the user. Now they can't infect your programs stealthily anymore.

  • Store critical program's data or configuration under COMMON_APPDATA.

    What I mean by critical is any data that should be altered only by administrative users.
  • Store program's data or configuration that is common or shared throughout the machine AND must be WRITABLE by all users in COMMON_DOCUMENTS folder.
  • Store users preference data or configuration under APPDATA or LOCAL_APPDATA, depends on whether you want the data for roaming or not.
  • Offer to store any file that saved manually by users under MY DOCUMENTS folder.

By following this strategy, I never have problem with UAC blocking access to my program's data or configuration file. Only in rare occasion when the user specifically ask to alter files under COMMON_APPDATA my program will elevate itself (thus asking for permission). I never altered files under "Program Files" so I don't have to ask for elevation each time my program runs.


Delphi Implementation


Now how do we get these folders programmatically? To get them, you need to ask Windows using api SHGetSpecialFolderLocation and SHGetPathFromIDList. These apis are contained in shell32.dll library. Most Delphi versions (I don't know which exactly, but at least from Delphi 7) already imported them. Usually you only need to add ShlObj unit in your uses list. But since windows api and Delphi have different implementation of string values, we need some adjustment before getting the folder information in proper format.


Here is Delphi code to get those predefined folder:


// function to ask windows for a special folder and convert the "windows string" into

// Delphi string

function GetSpecialFolder(const ASpecialFolderID: Integer): string;

var

  vSFolder :  pItemIDList;

  vSpecialPath : array[0..MAX_PATH] of Char;

begin

  SHGetSpecialFolderLocation(0, ASpecialFolderID, vSFolder);

  SHGetPathFromIDList(vSFolder, vSpecialPath);

  Result := StrPas(vSpecialPath);

end;


// now the constants that we must pass to windows to specify which special folder we want

const

  // constant to get APPDATA folder

  CSIDL_APPDATA          = $001A;

  // constant to get LOCAL_APPDATA folder

  CSIDL_LOCAL_APPDATA       = $001C;

  // constant to get COMMON_APPDATA folder

  CSIDL_COMMON_APPDATA      = $0023;

  // constant to get COMMON_DOCUMENTS folder

  CSIDL_COMMON_DOCUMENTS = $002E;

  // constant to get MY DOCUMENTS folder

  CSIDL_PERSONAL         = $0005;


// example how to use the GetSpecialFolder function and the constants  

function GetLocalAppDataFolder: string;

begin

  Result := GetSpecialFolder(CSIDL_LOCAL_APPDATA);

end;


Edited by LuthfiHakim, 25 November 2012 - 08:01 AM.

  • 0

#2 random12

random12

    CC Lurker

  • Just Joined
  • Pip
  • 1 posts
  • Programming Language:C#, Delphi/Object Pascal, Transact-SQL
  • Learning:C#, PHP, JavaScript, Delphi/Object Pascal

Posted 25 November 2012 - 01:42 AM

Usefull tips, thank you LutfiHakim.
  • 0

#3 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1,318 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 04 December 2012 - 08:31 PM

^^ Hi, welcome to codecall! And thank you if you found this article useful.
  • 0





Also tagged with one or more of these keywords: pascal