Solved

Cached ini files?

Posted on 2002-04-30
21
611 Views
Last Modified: 2008-03-03
Well - I've been playing with ini files for awhile... what I'm working on now is a program settings form which is SUPPOSED to let the user make changes to the main GUI.

I'm making use of the WritePrivateProfileString function built into the API.

-******Snip of Function*******_
Function putIniVal(ByVal strHeader As String, ByVal strKey As String, Value, ByVal strFilename As String) As Integer
'Write the value to the ini file.
    lngReturnValue = WritePrivateProfileString(strHeader, strKey, Value, strFilename)

-******ENDOF Snip of Function*******_

The putIniVal function *works* in that it does modify the ini file correctly. The problem I am having is that if I read the ini file right after I write to it, the changes are not reflected. If I close the program, restart the program the changes appear.



so if I do
putVal("Settings","GUICOLOR",255,My.ini")

and then

getinival("Settings","GUICOLOR",My.ini")

I don't get 255 I get the old value...

In my reading I found that win.ini is cached - are all ini's cached?

More importantly - what can I do to ensure that the putIniVal function makes the changes to the ini file immediately???


Cheers
Woot.
0
Comment
Question by:woottond
  • 9
  • 7
  • 5
21 Comments
 
LVL 16

Expert Comment

by:Richie_Simonetti
ID: 6981716
How do you know that file is wrote with putIniVal? you are reading it after modified and nothing happens!
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
ID: 6981722
Optionally, you could use getsetting and savesetting to store values in registry instead.
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
ID: 6981772
Also,
putVal("Settings","GUICOLOR",255,My.ini")
must be
("Settings","GUICOLOR","255","My.ini")

and then

getinival("Settings","GUICOLOR",My.ini")
must be
("Settings","GUICOLOR","My.ini")
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
ID: 6981793
test:
Option Explicit
Private Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long
Private Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long


Private Sub Form_Load()
Dim ret As Long, buff As String
buff = Space$(255)
'change the value:
ret = WritePrivateProfileString("settings", "GUICOLOR", "124", "test.ini")


'retrieve new value:
ret = GetPrivateProfileString("settings", "GUICOLOR", vbNullString, buff, 255, "test.ini")
MsgBox Left$(buff, ret)

End Sub
0
 

Author Comment

by:woottond
ID: 6981948
my putinival is working - it stores the value to the file. I've verified this in two ways - one open the file after the put - the value is there. Also if I stop the program and restart it the value is correctly returned by getinival.

I'm not a big fan of the registry - I'd prefer to use an ini file.

The "" marks are not nessisary - as it is an implicit cast. I can add them - but it doesn't really matter.

Sorry to sound negative - I'm just trying to state things as clearly as possible...

My gut feeling is that the ini file is being cached. The file gets modified. The getinival gets the value from the cache, and won't get the new value until the cache is purged. The right symptoms anyhow.
0
 
LVL 16

Accepted Solution

by:
Richie_Simonetti earned 200 total points
ID: 6982055
Just try my little code and you see that new value is retrieved. I did a little ini file with value GUICOLOR=255, after that i changed it to 124 and, as you see in msgbox, it was changed.
Hope it helps
0
 
LVL 75

Expert Comment

by:Anthony Perkins
ID: 6982335
Windows INI files are cached for speed.  In order to re-cache make the following call (Going from memory and untested so please save before using):

WritePrivateProfileString "", "", "", "your ini file name goes here"

Anthony

0
 
LVL 75

Expert Comment

by:Anthony Perkins
ID: 6982345
Richie,

I suspect woottond is using Windows 95/98 or Me.  Take a look at the remarks for the WritePrivateProfileString function from MSDN:

<quote>
Windows 95/98/Me: The system keeps a cached version of Win.ini to improve performance. If all three parameters are NULL, the function flushes the cache. The function always returns FALSE after flushing the cache, regardless of whether the flush succeeds or fails.

A section in the initialization file must have the following form:

[section]
key=string
      .
      .
      .
If the lpFileName parameter does not contain a full path and file name for the file, WritePrivateProfileString searches the Windows directory for the file. If the file does not exist, this function creates the file in the Windows directory.

If lpFileName contains a full path and file name and the file does not exist, WritePrivateProfileString creates the file. The specified directory must already exist.

Windows NT/2000/XP: The system maps most .ini file references to the registry, using the mapping defined under the following registry key:

HKEY_LOCAL_MACHINE\Software\Microsoft\
        Windows NT\CurrentVersion\IniFileMapping

Windows NT/Windows 2000 keeps a cache for the IniFileMapping registry key. Calling WritePrivateProfileStringW with the value of all arguments set to NULL will cause the system to refresh its cache of the IniFileMappingKey for the specified .ini file.

The profile functions use the following steps to locate initialization information:

Look in the registry for the name of the initialization file, say MyFile.ini, under IniFileMapping:
HKEY_LOCAL_MACHINE\Software\Microsoft\
        Windows NT\CurrentVersion\IniFileMapping\myfile.ini

Look for the section name specified by lpAppName. This will be a named value under myfile.ini, or a subkey of myfile.ini, or will not exist.
If the section name specified by lpAppName is a named value under myfile.ini, then that value specifies where in the registry you will find the keys for the section.
If the section name specified by lpAppName is a subkey of myfile.ini, then named values under that subkey specify where in the registry you will find the keys for the section. If the key you are looking for does not exist as a named value, then there will be an unnamed value (shown as <No Name>) that specifies the default location in the registry where you will find the key.
If the section name specified by lpAppName does not exist as a named value or as a subkey under myfile.ini, then there will be an unnamed value (shown as <No Name>) under myfile.ini that specifies the default location in the registry where you will find the keys for the section.
If there is no subkey for MyFile.ini, or if there is no entry for the section name, then look for the actual MyFile.ini on the disk and read its contents.
When looking at values in the registry that specify other registry locations, there are several prefixes that change the behavior of the .ini file mapping:

! - this character forces all writes to go both to the registry and to the .ini file on disk.
# - this character causes the registry value to be set to the value in the Windows 3.1 .ini file when a new user logs in for the first time after setup.
@ - this character prevents any reads from going to the .ini file on disk if the requested data is not found in the registry.
USR: - this prefix stands for HKEY_CURRENT_USER, and the text after the prefix is relative to that key.
SYS: - this prefix stands for HKEY_LOCAL_MACHINE\SOFTWARE, and the text after the prefix is relative to that key.
An application using the WritePrivateProfileStringW function to enter .ini file information into the registry should follow these guidelines:

Ensure that no .ini file of the specified name exists on the system.
Ensure that there is a key entry in the registry that specifies the .ini file. This entry should be under the path HKEY_LOCAL_MACHINE\SOFTWARE \Microsoft\Windows NT\CurrentVersion\IniFileMapping.
Specify a value for that .ini file key entry that specifies a section. That is to say, an application must specify a section name, as it would appear within an .ini file or registry entry. Here is an example: [My Section].
For system files, specify SYS for an added value.
For application files, specify USR within the added value. Here is an example: "My Section: USR: App Name\Section". And, since USR indicates a mapping under HKEY_CURRENT_USER, the application should also create a key under HKEY_CURRENT_USER that specifies the application name listed in the added value. For the example just given, that would be "App Name".
After following the preceding steps, an application setup program should call WritePrivateProfileStringW with the first three parameters set to NULL, and the fourth parameter set to the INI file name. For example:
WritePrivateProfileStringW( NULL, NULL, NULL, L"appname.ini" );
Such a call causes the mapping of an .ini file to the registry to take effect before the next system reboot. The system rereads the mapping information into shared memory. A user will not have to reboot their computer after installing an application in order to have future invocations of the application see the mapping of the .ini file to the registry.
The following sample code illustrates the preceding guidelines and is based on several assumptions:

There is an application named App Name.
That application uses an .ini file named AppName.ini.
There is a section in the .ini file that we want to look like this:
[Section1]
  FirstKey = It all worked out okay.
  SecondKey = By golly, it works.
  ThirdKey = Another test.
The user will not have to reboot the system in order to have future invocations of the application see the mapping of the .ini file to the registry.
Here is the sample code :

 
// include files
#include <stdio.h>
#include <windows.h>
 
// a main function
main()
 
{
  // local variables
  CHAR inBuf[80];
  HKEY  hKey1, hKey2;
  DWORD  dwDisposition;
  LONG   lRetCode;
 
  // try to create the .ini file key
  lRetCode = RegCreateKeyEx ( HKEY_LOCAL_MACHINE,
                              "SOFTWARE\\Microsoft\\Windows NT
                               \\CurrentVersion\\IniFileMapping\\appname.ini",
                              0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE,
                              NULL, &hKey1,
                              &dwDisposition);
 
  // if we failed, note it, and leave
  if (lRetCode != ERROR_SUCCESS){
    printf ("Error in creating appname.ini key\n");
    return (0) ;
    }
 
  // try to set a section value
  lRetCode = RegSetValueEx ( hKey1,
                             "Section1",
                             0,
                             REG_SZ,
                             "USR:App Name\\Section1",
                             20);
 
  // if we failed, note it, and leave
  if (lRetCode != ERROR_SUCCESS) {
    printf ( "Error in setting Section1 value\n");
    return (0) ;
    }
 
  // try to create an App Name key
  lRetCode = RegCreateKeyEx ( HKEY_CURRENT_USER,
                              "App Name",
                              0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE,
                              NULL, &hKey2,
                              &dwDisposition);
 
  // if we failed, note it, and leave
  if (lRetCode != ERROR_SUCCESS) {
    printf ("Error in creating App Name key\n");
    return (0) ;
    }
 
  // force the system to re-read the mapping into shared memory
  //    so that future invocations of the application will see it
  //    without the user having to reboot the system
  WritePrivateProfileStringW( NULL, NULL, NULL, L"appname.ini" );
 
  // if we get this far, all has gone well
  // let's write some added values
  WritePrivateProfileString ("Section1", "FirstKey",
                             "It all worked out okay.", "appname.ini");
  WritePrivateProfileString ("Section1", "SecondKey",
                             "By golly, it works.", "appname.ini");
  WritePrivateProfileSection ("Section1", "ThirdKey = Another Test.",
                              "appname.ini");
 
  // let's test our work
  GetPrivateProfileString ("Section1", "FirstKey",
                           "Bogus Value: Get didn't work", inBuf, 80,
                           "appname.ini");
  printf ("%s", inBuf);
 
  // okay, we are outta here
  return(0);
 
}

</quote>
0
 
LVL 16

Expert Comment

by:Richie_Simonetti
ID: 6982495
I didn't know that for woottond was using win.ini!
0
 
LVL 75

Expert Comment

by:Anthony Perkins
ID: 6982521
Richie,

I see what you mean, but these are the remarks for
WritePrivateProfileString not WriteProfileString, so I can only assume that they simply copied the remarks from this last function and that it applies equally to "private" configuration files.

Anthony
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:woottond
ID: 6983101
I am using win98 and I'm not using win.ini for the record.

Reading post but its going to take me a few minutes to get through it all
0
 

Author Comment

by:woottond
ID: 6983144
Well - I've tried some more debugging...

Richie - your code is the pretty much the same as mine. The only differences being in the get function. So I have used both.

First in form 1:

put(val)

Close Form 1, open Form 2:

get(val)

DOESN'T WORK!

now if I do:

put(val)
get(val) <--- do this first

Close Form 1, open Form 2:

get(val)


Sooo - I'm still getting the feeling that it is being cached. What I don't understand is why getting from the ini file before I close the form makes it behave properly in the next form...

I've got a working work around but I hate that type of thing... there has to be a reason...

BTW - Richie was your get(val) on the same form as your put(val)? This seems to be the cause of my problems!

Cheers for the help thus far!
Woot.
0
 

Author Comment

by:woottond
ID: 6983152
spoke to soon

DOESN'T WORK

    put(val)
    get(val) <--- do this first

    Close Form 1, open Form 2:

    get(val)

DOES WORK

    put(val)
    msgbox get(val)

    Close Form 1, open Form 2:

    get(val)


WEIRD
0
 

Author Comment

by:woottond
ID: 6983200
spoke to soon

DOESN'T WORK

    put(val)
    get(val) <--- do this first

    Close Form 1, open Form 2:

    get(val)

DOES WORK

    put(val)
    msgbox get(val)

    Close Form 1, open Form 2:

    get(val)


WEIRD
0
 

Author Comment

by:woottond
ID: 6983232
Found it.

Richie - apologies - you were right...

the write API function does write to the cached file.

the read API function must read from cache. Why you ask?

well I wrote my own ini file reader. it reads from the file directly. BUT if you use the API write then it must wait to have the file updated before you can read from it.

so
APIput(val)
APIget(val) <-- works
nonAPIget(val) <-- doesn't

BUT
nonAPIput(val)
nonAPIget(val) <-- works

I'm going to write my own write method - but I have found the problem.

If you are going to use API write you must use API read OR flush the buffer.

Richie - here are the points - cheers mate.
0
 

Author Comment

by:woottond
ID: 6983234
Good call... =)

don't mix API with nonAPI. Lesson learned.
0
 
LVL 75

Expert Comment

by:Anthony Perkins
ID: 6983272
Again, the API functions on Windows 9.x use cache for speed. If you want to re-write the cache then the following call will do the trick:

WritePrivateProfileString "", "", "", "your ini file name goes here"

Anthony
0
 

Author Comment

by:woottond
ID: 6983306
Good call... =)

don't mix API with nonAPI. Lesson learned.
0
 

Author Comment

by:woottond
ID: 6983598
I didn't actually get the writePrivateProfileString "", "", "", "your ini file name goes here"

trick to work... I didn't spend much time with it as I got one of the fellas here to write a getinival (and he wrote it in the time it took to post my response... :)

anyhow - your answer is correct and I can post some points for you...

0
 
LVL 16

Expert Comment

by:Richie_Simonetti
ID: 6983608
Thanks for "A" grade!
Tony, you know, 13 has a "bad mojo" ;)
0
 
LVL 75

Expert Comment

by:Anthony Perkins
ID: 6983734
woottond,

Thanks I appreciate that. It was not really necessary, I just thought you may have overlooked my message.

Richie,

Yeh, I noticed.  But then I "stole" some points from you
last week (Common Dialog box question), so what comes around goes around <g>

Anthony
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Introduction In a recent article (http://www.experts-exchange.com/A_7811-A-Better-Concatenate-Function.html) for the Excel community, I showed an improved version of the Excel Concatenate() function.  While writing that article I realized that no o…
Background What I'm presenting in this article is the result of 2 conditions in my work area: We have a SQL Server production environment but no development or test environment; andWe have an MS Access front end using tables in SQL Server but we a…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now