INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Jobs

Mysterious TIniFile Coding Issue

Mysterious TIniFile Coding Issue

(OP)
I have a program here that's basically been working well. I've chosen to share it, but I get a report back on something from someone. The report indicates that the message in the code below is being triggered.

CODE

ProgramPath := configini.ReadString('Configs', 'ProgramPath', 'Error');
if ProgramPath = 'Error' then
  HandleError('ProgramPath not found in ini file.') 

The program is distributed with a stock INI file and short of the user going in and completely deleting or changing the tag, the message should logically never appear. Is there something about any of the newer operating systems than XP which makes TIniFile not work properly? The Create line is not too far up the chain from the shown code in terms of logic, so there shouldn't be much of a chance that anything is corrupted in the meantime.

For this program, failure to read this line constitutes a show-stopper, but I can't duplicate it on anything I have, so I'd really be interested if there are issues that I might not be aware of given what I'm using or not?

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Mysterious TIniFile Coding Issue

(OP)
Write permission? Is that default when you create a IniFile and only read it? The design of the program indicates that the INI file be in the same folder as the program. Are INI files in program-oriented directories limited in this way on the newer OSes?

FWIW, I thought I'd include the code behind the Create method, just in case there's some hangup about it in newer OSes.

CODE

function GetHomeDirectory: string;
// returns the directory that the main program is running in.
  begin
    Result := ExtractFilePath(Paramstr(0));
  end;

configini := TIniFile.Create(GetHomeDirectory + 'myprogram.ini'); 

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Mysterious TIniFile Coding Issue

To create a file (not only INI) within a folder you must have write permission in this folder.
As I said before, try creating the INI within another folder, or try to test execute your application as administrator.

note: only the administrator usually has permission to write in the Program Files folder

http://www.imoveisemexposicao.com.br/imoveis-venda-são_paulo-residencial-apartamento

RE: Mysterious TIniFile Coding Issue

Glenn,

I suppose this is W7 we are talking about?
if your program file is located in the program files directory it could well be that the ini file your program reads is NOT the ini file under the program files folder.
as Imex already implied UAC may be the culpritt here.

if you created the ini file with unsufficient rights, it ends up under this path:

C:\Users\<username>\AppData\Local\VirtualStore\Program Files\<your program folder>\

Cheers,
Daddy

-----------------------------------------------------
Helping people is my job...

RE: Mysterious TIniFile Coding Issue

Adding a general advice,

Configuration data that is not user related but program relate should reside in the %ProgramData% folder
The XP equivalent is \Documents and Settings\All Users\Application Data

/Daddy

-----------------------------------------------------
Helping people is my job...

RE: Mysterious TIniFile Coding Issue

Hmm Daddy is as usual correct in this, but he's not going into much detail as to how you would for example, find what %ProgramData% actually is on any Windows platform. Or how to get the permissions to write to it when you do, and it is quite involved.
I'm working through it now (as I need to update several of my own apps) and will post the results as soon as I have them, unless Daddy gets there first that is!

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

I would hazard a guess that the JCL fileutils.pas already has functions to get these paths. You can also read the environment variables which should point you to the correct folders.

RE: Mysterious TIniFile Coding Issue

Djang: I haven't looked at JCL fileutils yet? But have done a good deal of web searching for 'working' answers to this (or put another way, wasted a whole day)

First of All I am using Delphi XE (1) under Windows 7, User logged in as a normal account.

Getting the paths to the system folders is simple enough
You need to pass in a constant that is declared in the shfolder unit
e.g. CSIDL_COMMON_APPDATA for the Users Program Data folder

So thanks to Zarko Gajic of about.com Delphi for this

CODE

function GetSpecialFolderPath(folder : integer) : string;
const SHGFP_TYPE_CURRENT = 0;
var  path: array [0..MAX_PATH] of char;
begin
   if SUCCEEDED(SHGetFolderPath(0,folder,0,SHGFP_TYPE_CURRENT,@path[0])) then
     Result := path
   else
     Result := '';
 end; 


Then you need to get permission to write in here (assuming the user wont have admin rights)

The most common thing I found was to raise the privileges of your application by using a custom manifest.

Created used this manifest source, there are several example of this on the web

CODE

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity  version="3.1.0.0"
  processorArchitecture="*"
  name="RTO_Monitor"
  type="win32"/>
  <description>elevate execution level</description>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="highestAvailable"
                                 uiAccess="true"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!--The ID below indicates application support for Windows Vista   -->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <!--The ID below indicates application support for Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
    </application>
  </compatibility>
</assembly> 

Most speak about compiling this externally using BRCC32, but I found that (in XE anyway) adding the manifest source and the .rc file (below) to the project is the only way to get this to compile at all.
I think XE compiles it for you, but I am not 100% about this.

CODE

1 24 "RTO_Monitor.manifest" 

adding this to the main code

CODE

implementation
{$R *.DFM}
{$R 'RTO_Monitor.res'} 

It seems to be important not to have any references to XPMan in the code I have none.

Having done this recompiling the project throws up these warnings (for me)

[DCC Warning] W1056 Warning: Duplicate resource: Type 14 (ICON GROUP), ID MAINICON; File C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.RES resource kept; file C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.res resource discarded.
[DCC Warning] W1056 Warning: Duplicate resource: Type 14 (ICON GROUP), ID MAINICON; File C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.RES resource kept; file C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.res resource discarded.
[DCC Warning] W1056 Warning: Duplicate resource: Type 16 (VERSIONINFO), ID 1; File C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.RES resource kept; file C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.res resource discarded.
[DCC Warning] W1056 Warning: Duplicate resource: Type 16 (VERSIONINFO), ID 1; File C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.RES resource kept; file C:\Users\Steve\Newsoft\321\RTO_Monitor\RTO_Monitor.res resource discarded.

When I have seen this sort of thing before I have found that, something will be wrong, perhaps here the .res is not actually going to work, as is born out by the results..
But I am not sure about this at all, no idea about why the warnings occur or how to fix them.


But the project does run.
However....
When attempting to write an ini file to the location it falls over. (access denied)

CODE

FName := ChangeFileExt(extractfilename(Application.ExeName), '.INI' );
  FIniFile := TIniFile.Create(GetSpecialFolderPath(CSIDL_COMMON_APPDATA) + '\Application Data\Newsoft\RTO_Monitor\' + FName);
  with FIniFile do
     try
        WriteString(SECTION, 'LoadName', LoadFilename);
  //etc.. 

So it hast worked, now I must decide whether or not to waste another day, or just continue installing apps to a 'new' folder and carry on as before.
Unless anyone can see what is wrong with this?



Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

Hi sggaunt,

yes it's worth the hassle because this way you are adhering to the windows design guidelines and your application is guaranteed to work with future windows versions...
about CSIDL_COMMON_APPDATA:

Quote:

CSIDL_COMMON_APPDATA This folder should be used for application data that is not user specific. For example, an application may store a spell check dictionary, a database of clip-art or a log file in the CSIDL_COMMON_APPDATA folder. This information will not roam and is available to anyone using the computer. By default, this location is read-only for normal (non-admin, non-power) Users. If an application requires normal Users to have write access to an application specific subdirectory of CSIDL_COMMON_APPDATA, then the application must explicitly modify the security on that sub-directory during application setup. The modified security must be documented in the Vendor Questionnaire.

so to resume:
global application settings go under CSIDL_COMMON_APPDATA\<Your program name>, this should be one time setup data (ie the setup program should write these settings)
user data should go under CSIDL_LOCAL_APPDATA\<Your program name>

normally an end user application should not have the need to perform UAC elevation...

/Daddy

-----------------------------------------------------
Helping people is my job...

RE: Mysterious TIniFile Coding Issue

So I am attempting to write the .ini files (user program settings/ data file locations etc) to the wrong location anyway?
and don't need to do the elevation. Will try again.

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

yes indeed:

you need 2 separate ini files:

- one for the application/all users (ie data file locations - if they are the same for everyone)
- one for user specific settings

doing it like this also has the advantage that your application will work under Citrix/RDP environments...

/Daddy

-----------------------------------------------------
Helping people is my job...

RE: Mysterious TIniFile Coding Issue

Hmm diddnt work
I still get a write error

I hard coded it to ensure no string errors

FIniFile := TIniFile.Create('C:Users\Steve\Appdata\Local\Newsoft\RTO_Monitor\RTO_Monitor.ini');

Oddly 'I' can create folders/files in here with Windows explorer.
Is this something to do with the app being developed in it own folder?
But it is a sub folder of the current user in this case C:\Users\Steve\\

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

If I put the exe in the Appdata sub folder (where the .ini file should go) then it works!
Will it work in the Program files folder?

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

Yes that (Application in Program Files(x86)) worked too, the inifile was updated in the Appdata sub folder.
So it should work after an install.
Complicates testing a little!

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

Hmm still learning, I think the not writing was a bit of a red herring. As I later found that by doing this
in an attempt to be able to test, I was able to use both the Debug and release builds in the environment, even though I thought that the release version would not work.
On making an installer and deploying to my Vista Machine it fell over (no write access)
What I have found is that if the folders where I want to put the .ini exist then I can write the file (files can be written folders cannot be created)

CODE

{$IFDEF DEBUG}
    FIniFile := TIniFile.Create(FName);
    CommsModule.CPortDriver.LoadSettings(stIniFile, extractfilepath(Application.ExeName) + 'ComPort.ini' );
{$ELSE}
    FIniFile := TIniFile.Create(GetSpecialFolderPath(CSIDL_LOCAL_APPDATA) + '\Newsoft\RTO_Monitor\' + FName);
    CommsModule.CPortDriver.LoadSettings(stIniFile, GetSpecialFolderPath(CSIDL_LOCAL_APPDATA) + '\Newsoft\RTO_Monitor\ComPort.ini' );
{$ENDIF} 

So I can get the installer to create the folders, trying now.

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

Yes the installer should create the folder.

/Daddy

-----------------------------------------------------
Helping people is my job...

RE: Mysterious TIniFile Coding Issue

Some scripting was required but yes it does!
I use Innosetup, paid installers might have an option to do this for you?

The real problem was TInifile.create will not create a path in a system folder (why?)
But as I might want to distribute apps for testing without an installer, came up with this.

in FormActivate.

CODE

AppdataLoc := GetSpecialFolderPath(CSIDL_LOCAL_APPDATA) + '\'+ COMPANYNAME + '\' + ChangeFileExt(extractFilename(application.ExeName),'');

    if not sysutils.DirectoryExists(AppDataLoc) then
        begin
          if not SysUtils.ForceDirectories(AppDataLoc) then
               showmessage('ERROR: Failed to create '+  AppDataLoc);
        end;

    ReadIniFile; 


and in the Ini read/write functions

CODE

FName := ChangeFileExt(extractfilename(Application.ExeName), '.ini' );

{$IFDEF DEBUG}
    FIniFile := TIniFile.Create(FName);
    CommsModule.CPortDriver.StoreSettings(stIniFile,  extractfilepath(Application.ExeName) + 'ComPort.ini');
{$ELSE}
    if sysutils.DirectoryExists(AppDataLoc) then
      begin
         FIniFile := TIniFile.Create(AppDataLoc + '\' + FName);
         CommsModule.CPortDriver.StoreSettings(stIniFile, AppDataLoc +'\'+ 'ComPort.ini');
      end
    else
      exit;
{$ENDIF}

    with FIniFile do
     try
       ..... 

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

(OP)
Thanks for the conversation. I'm glad that bringing up the issue was helpful to someone else. This said in reading this, I'm trying to put all this together for something that I can do (and not test since I don't have access to anything to test it on) which will fix the problem. I don't mean any ill-will here, but unfortunately I'm really not seeing much regarding a solution, especially since the user I mentioned has tried it as an administrator as well.

What seems to be coming out of this is that any associated configuration data files have to be put into the directory path that qualifies as "Application Data", due to the user-unfriendliness of the newer versions of Windows. This would necessitate that any applications, no matter how small, would require an installer to function with any associated files, or to depend on the end-user to navigate the labyrinth of directories in the mystery of putting the file in the right place and hope they did it right (or can locate it to edit it). This would constitute something that would go down as "very bad", given the necessity of this file.

Ultimately, not being able to read this file in the indicated place would break about 3/4 of the intended end-user requirements for the program. As well, replacing the process with something else would require a 100% total rewrite of the application, which would require substantial time on such platforms in order to get it working properly.

From what it seems, the only good solution would be copying the distributed file to this "Application Data" directory, and then reading the file from there. But wouldn't that cause the exact same problems as indicated here previously, since any changes made to it would have to be copied back where the user would readily find it? Or am I misunderstanding this and it's a much easier fix than the other comments in this thread seems?

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Mysterious TIniFile Coding Issue

Glenn.
Not sure I fully understand what you want to do, but it sounds similar to my primary users requirement.
i.e He wants his customers to be able to replace their own ini files.
In fact he intend to distribute his own (much larger and more complex) VB.net application as an install to its own folder, in the root of the 'C' main drive, which as we have seen is now a No No.

As far as the App data folder thing goes, I don't see this as a problem now..
I found (finally) that in fact you don't 'need' an installer, but as the ini file routines cannot create a path it seems difficult.
But you can use 'ForceDirectories' to create a path to your folders in the App Data folder, before you write the ini file
Then you can write to it.

Users can access the application data folder and replace the ini file, but as it counts as a system folder it is set to hidden, so you don't see it by default in Windows explorer.
But that is easily changed in folder options.

Steve: N.M.N.F.
If something is popular, it must be wrong: Mark Twain

RE: Mysterious TIniFile Coding Issue

(OP)
As I mentioned in the first post, the program is distributed with an associated INI file. It's not being created at all during run-time, but read and edited. The program is designed to read it in the same directory as the program. There are checks for existence of this file, along with a number of other things (which should fail if what is written above is true, this is indicated).

What it looks like is that access to the file in the specific place is not guaranteed, despite this file being distributed with the program and logically being in the same place as the program. This is presenting the problem. What I'm trying to do is solve this without having to completely rewrite the program, which is what the above posts in this thread would seem to indicate.

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Mysterious TIniFile Coding Issue

@Glenn,

if your program resides directly under the root (not in program files) then it should not be an issue as UAC/Virtualstore only protects system folders.
You only have to make sure that the users have write access to the folder where your application resides.

Quote:

What seems to be coming out of this is that any associated configuration data files have to be put into the directory path that qualifies as "Application Data", due to the user-unfriendliness of the newer versions of Windows.

Small remark here, this was already the case with XP/ME. The biggest fault that MS ever made was granting XP users administrative access by default (something linux never did btw) and allowed for this scenario. The fixed this "mistake" in Vista. You can read the design guidelines here: Link and you will find the same limitations that are today in place, no writing in the program files folder allowed! In the end it is all about the developer not following the guidelines that created this situation...

/Daddy

-----------------------------------------------------
Helping people is my job...

RE: Mysterious TIniFile Coding Issue

(OP)
OK, so ultimately the only real thing I can do to maintain the accompanying INI as user-editable is not to allow the program to run under the Program Files folder at all?

It is not possible for anyone to acknowledge truth when their salary depends on them not doing it.

RE: Mysterious TIniFile Coding Issue

No, move the ini file to either Common to All or Current User Application Data directory, depending on desired use-case.

RE: Mysterious TIniFile Coding Issue

@Glenn,

that is one option, but better is to split application and configuration/data as stated before (and what TonHu says...)

/Daddy

-----------------------------------------------------
Helping people is my job...

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members!

Resources

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close