Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Identifying files that load with a program 3

Status
Not open for further replies.

Bryan - Gendev

Programmer
Jan 9, 2011
408
AU
As part of solving a problem with my app I want to identify the location of an ini file that loads with an exe file.
I did this 10+ years ago but can't now remember which utility I used.
Could it have been one of Sysinternals Suite?

GenDev
Adelaide SA
 
An INI file doesn't load automatically with an EXE, you use either FILETOSTR() to read it or Windows API functions deicated to INIs:

Code:
*-- DECLARE DLL statements for reading/writing to INI files

DECLARE INTEGER GetPrivateProfileString IN Win32API ;
   String cSection, String cKey, String cDefault, String @cBuffer, ;
   Integer nBufferSize, String cINIFile

DECLARE INTEGER WritePrivateProfileString IN Win32API ;
   String cSection, String cKey, String cValue, String cINIFile

DECLARE INTEGER GetProfileString IN Win32API ;
   String cSection, String cKey, String cDefault, ;
   String @cBuffer, Integer nBufferSize

DECLARE INTEGER WriteProfileString IN Win32API ;
   String cSection, String cKey, String cValue

You'll likely find this in your project, if using Code References search for GetProfileString.


The advantage of GetProfileString is to explicitly get at the value of a key in a section, which are defined as follows:
Code:
[section1]
key1="value1"
key2="value2"
key3=3
[section2]
key1=42

If you have this in your.ini, reading key1 from section2 would mean using
Code:
Local lcBuffer, lnRet
lcBuffer=space(10)
lnRet = GetProfileString("section2","key1","0",@lcBuffer,LEN(lcBuffer),"c:\program files (x86)\your applilcation\your.ini")
If lnRet <0
   Error "Buffer too short for GetProfileString"
Else
   ? Left(lcBuffer,lnRet) && lnRet is the length of the returned string
Endif

Notice, even if your value is numeric in the INI, like key1=42, GetProfileString always returns a string, so in the sample case Left(lcBuffer,lnRet) is the string "42" not the number 42. A complete way to handle this will need to know the wanted result dat type, i.e. you better know what you store and want to get back from your INI. There are, by the way, no functions I know of that would simply list all section and key names within a given ini, that points out how writing and reading an ini is an "inside job", something you need to know yourself. It's not hard at all, you determine how you organize your ini entries, what sections you define and what keys, when you write out an ini and therefore it's always straight forward what you want to get back from it. You're prone to errors a user makes when changing a section or key name, intentionally or not.

That's one con, but could be handled by verifying that you get all from an INI you need and otherwise pointing out the INI is broken, incomplete, or in detail what section/key is missing. The other two bog Pros and cons are... Pro: An INI is easy to maintain with just notepad at hand, Con: Your code has it easier to get values from a DBF, where you can define fields in the data types you actually need (i.e. that's a pro for DBFs instead of INI and therefore a con for INIs)

And of course I have to disappoint you about what you think is automatically loaded. I have no idea whether you used some VFP based class/framework or even a third party tool, an EXE or anything else that makes reading in an INI easier for you, you have to look into your project and search it. I already gave one pointer to search for, the names of the API functions, you could also search for "INI", the section names and key names you find in your INI to likely find where you read them in, so even if you use some third party library for that, those detail searches should find them, shouldn't they?

Edit: I forgot to mention that an obvious place where you generate an INI is within your application setup, which could also be in places your EXE with no admin elevation later can either only read or not access at all, if that's badly designed. The usual place of an INI is the application folder, and that's write protected unless you install outside of the program files system directory or store your ini in system directories for which your EXE finally has sufficent permissions. Anyway it is, your EXE is typically only concerend with reading and writing to an existing ini file.

And the other thing I didn't explain yet is how the "Private" flavors of the functons differ. Well, see in detail: My personal opinion is you won't need this variants, they are not what "private" suggests concerning private (i.e. user specific) INI files.

Chriss
 
The short answer is that you would normally keep the INI file in the same directory as your main executable program. That way, you only need to use FULLPATH() to get its fully-qualified filename:

lcINI = FULLPATH("MyIni.INI")

And to read settings from the INI file, the recommended approach is to use GetPrivateProfileString(). I do that in a dedicated configuration manager class. That class handles all interaction with the INI file. No other part of the application knows anything about the INI file. That way, if I ever decide to use some other mechanism for configuration data (such as the Registry), I would only need to alter one bit of code.

The Init of my class declares the GetPrivateProfileString() function:

Code:
DECLARE INTEGER GetPrivateProfileString IN kernel32; 
	STRING   cAppName,; 
	STRING   cKeyName,; 
	STRING   cDefault,; 
	STRING   @cReturnedString,; 
	INTEGER  nSize,; 
	STRING   cFileName

And I have a Get method, which retrieves an individual setting:

Code:
FUNCTION Get
* Retrieves a given setting. Receives two parameters: (i) Category of required setting;
* (ii) name of required settings. Returns the value of the settings (always as a string).

* If the INI file cannot be found, or it does not contain the required value, an 
* empty string is returned.

LPARAMETERS tcSection, tcSetting

LOCAL lcBuffer, lnReply

IF PCOUNT() < 2 OR EMPTY(tcSetting) OR EMPTY(tcSection)
  ERROR "Config Manager: Invalid parameters to Get method"
ENDIF

* Create a buffer for the result
lcBuffer = REPLICATE(CHR(0), 255)

* Get the required value
lnReply = ;
  GetPrivateProfileString(tcSection, tcSetting, "", @lcBuffer, 255, lcIni)

* Return it
RETURN LEFT(lcBuffer, lnReply)

ENDFUNC

So, for example, if the file contains a section named [Folders], and one of the settings within that section is named TempFolder, you would call the Get method, passing "Folders" and "TempFolder" as the parameters. It would then return the value of the setting in question.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Off hand, I can't see how sysinternals can help with this. These are utilities that let you monitor things like memory usage and disk performance. I suppose you could use it to see which files are accessed when you launch your EXE. But wouldn't it be simpler just to search your drive for the relevant file? Or look inside your code to see where it is locating the file?

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Could it be, gendev, that you are still surprised by the change of mind of Microsoft on where to store which system directories from Windows version to Windows version?

My guess is you provide an INI with your inno setup and you specify it should go into a users (roaming or not) profile, so you have an INI per user, perhaps?
Is that the case?

I guess Inno script allow you to use a constant like {userappdata}.
Inno knows where these system folders are, depending on the Windows, besides the user installing, perhaps. (There's a hint for another problem, as admins install what users later run without admin elevation).

Your software will also need to know where this path - {userappdata} for example - is based on the current user and the Windows OS, if you hardcode the base path C:\Users, that could work for Vista and later, but not XP and earlier and while all Windows versions since Wista have C:\Users as the base directory of User profiles, I'm not sure everything within that base path is always in the same relative paths and which Windows versions allow read access, write access or both in which system directories. Clearly C:\Program Files is usually only for reading and executing, never wrting, which is where placing INIs in app folders isn't useful anymore, unless you also tell your setup to make the INI file writable for a normal user, not for someone with administrative elevation.

On top of that redirection of write access to the VirtualStore subfolder in user profiles mean you don't get an error when you try to write into write protected directories, also since Vista, the general topic for this, including registry redirection, is UAC.

Now the deal is on one side you profit from needing no change in your Inno Setup projects, which has the detail knowlegde how to differentiate constants like {userappdata} with Windows versions. If your software doesn't, you could easily fail even reading in the INI, as you look into the wrong place.

UAC might be the reason to get no actual errors while acting on wrong paths or file names or with missing permissions, I still have no idea why your test run remained silent about any error, but I guess your problem isn't just a necessary command or function change, but a bigger picture about system directories and what we'd need to know to make a recommendation for a practical solution is whether you actually only need one INI for the application or one per user, your usage of "Roaming" suggests yu need a solution per user that also can act when a user changes work places. Whether user profiles roam is yet another topic on top of Windows versions, location of system paths, UAC and how the Setup software handles system directories, too.

I looked into the topic of roaming profile, and without having a URL from the top of my head I came across a mentioning that a user can write into his profile, but an application run from that user can't. Seems odd to me, as a VFP EXE would always run under the same permissions as the user using it.

I think what you need is advice about how to handle an INI in a good way and your code won't help you much, even if you could search it with the Code References search of VFP9. If your application doesn't work in respect of the ini to program something new would likely be the best solution instead of trying to fix the existing code. Problem is, that you'd still need to find out at least as much as is necessary to completely inactivate the previously used way of handling INIs to get away with the problems that code has.

Chriss
 
Think big, start small. After all I said the most relevant questions I would have is: Do you provide an INI file with your Inno setup and where do you store it and if you change permissions to it, how do you set them up? That's most relevant to know what your EXE needs to find it or to give advice about how to change your setup to put an INI in a better suited location. Because what has worked until now can break easily, if the reason is where different Windows versions differ in their system directories: The Inno setup does know the correct places, your code might not.

Chriss
 
Thankyou for your very explicit and detailed answers everyone BUT
IHAVE dealt with my ini in my app via GetProfileString etc but I am trying to see what happens with its ini file when I load the TMG app that my program links to.

GenDev
 
By way of explanation, my app has a MAIN objective to deal with files used as exhibits in TMG. In addition I have added 10 utility functions which may be useful in the management of exhibits.
One of these functions aims to let the user open TMG with an accent set against persons who have 1 or more exhibits. This function needs to access the ini file belonging to TMG, - to save the way it is - make changes and open TMG - then restore the ini if the user wants to.
This function was working 10 years ago to the best of my recollection and the code to read and modify the TMG ini file in the appdata/roaming folder is in my code BUT it does not work now.
My attempt to find out why is what my postings have been about.
Perhaps the appdata/roaming folders were accessible back then? I had a few hundred users of my app 10 years ago and have recently been requested to re-issue it - hence my work to 'check' its operation.
GenDev


 
I don't know what exhibits are in the technical context and what TMG.exe is. Well, I may not need to know.

Okay, to determine what files some closed source third party EXE accesses, sysinternals procmonitor is the tool of choice. You'll need to filter for the EXE you want to monitor for file accessing events.

But if you know its INI is where it is, what would it help you to confirm that that INI file is loaded from TMG? I guess your hope is, TMG loads some other INI somewhere else? If you explicitly see the INI then that's not needing that kind of investigation, does it?

gendev said:
This function was working 10 years ago...BUT it does not work now
That's well understood. Which Windows version(s) were you using then and are you using now?
I already told you Microsoft changes things, for example write permission in system folders. Roaming profile folders are quite special to work in. And what worked 10 years ago can break, yes.

So now the outset that changes is not only the Windows version (I hope you don't still use XP), but also whether you can still work that way with the INI files of another executable. The easiest thing to change is making the backup copy to another folder, maybe even to TEMP. If write permissions are the underlying problem, that would work, but then the problem would be restoring the old INI settings and changing them, anyway. So you'd need something that works in an elevated state to enable file access. That requires trust to the users getting elevated access.

Write permissions as a probela are easy to test, aren't they? You just have to take into account that tests within the IDE under your account only can tell you what you can do, not whats the normal user experience. So write an EXE that changes directory to the roaming folder and try to generate a file by STRTOFILE(), or FCREATE, for example and then run it under real conditions as a normal user and don't just test in the IDE under your account, even perhaps elevated and under your Windows version, that doesn't test under real conditions.

Chriss
 
Hi Chriss,
C> Which Windows version(s) were you using then and are you using now?

Both my app and TMG have worked under the 'current' version of Windows since about 2006 - at the moment my dev PC is running Win 11.

C> The easiest thing to change is making the backup copy to another folder.

Using the 'full' path/filename the FILE COPY command will not 'pull' the ini file from the roaming folder. I will try a lower level command today.

Sadly, as this function is only a minor part of my program I may be forced to remove it rather than have the user having to make selections in TMG which I was trying to make programmatically.
Thanks for all your help.

GenDev
 
Hello Mike,

sysinternals tools do much more, maybe you thought of other tools from them, there are many.

They show in detail which reg.keys and files / directorys an application handles (procmon). You can limit it to "events" of one application by simply dragging an icon on your window. Anf have a look on filters ....

we use them also if are problems attaching a file or which process is locking a file ("find handle" in process explorer)

Attached a screenshot.

Regards
tom

Just a hint
WSCC is a free tool, a shell for many tools (NIRSOFT, Sysinternals,..) and built in update function for them. I have it on my emergency ministick together with my keys.


2024-06-07_111159_yakgan.jpg
 
Thanks for that, Tom. Now you mention it, I remember using something from sysinternals about ten years ago, to work out which DBFs an application was using. We only had the executable, no source code. And we had to know which tables to migrate to a newer system. I don't remember the details, but I remember that it was successful.

I also used some of the NIRSOFT utilities, which you mentioned.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top