Jump to content

IniRead() Bug?


Recommended Posts

Ok, I'm not big on using .ini files, so haven't opted to mess with them much in the past.

But, attempting to assist someone with a question in the forum here (topic) I threw together what seemed to be a simple example. The results I got were unexpected.

A forum search didn't reveal any other idiots claiming a bug in IniRead(), so I'll post my question, and await the revelation as to what obvious, in-my-face, stupid blunder I've made.

I assume everyone has a system.ini file in their Windows directory, and that it contains both a "timer" key and a "wave" key.

Why then, below, does the read of "timer" succeed, but the read of "wave" fail?

$Path = @WindowsDir & "\system.ini"
$key1 = IniRead($Path, "drivers", "wave",'')
MsgBox(1,"", $key1)
$key1 = IniRead($Path, "drivers", "timer",'')
MsgBox(1,"", $key1)
Link to comment
Share on other sites

you example works fine for me...(win7)

This is very strange, the read of "wave", for me, returns an empty string, instead of "mmdrv.dll"

(I'm running Windows XP Pro SP3)

Edit: Hmm, I'll look over my system.ini file with WinHex and see if there are any peculiar characters hiding around that key...

Edit2: There's nothing odd in my system.ini file. If I copy system.ini to system2.ini, then the script above works. So, my best wild-ass guess is that somehow IniRead() is not retrieving the actual system.ini file as it exists on disk, but instead is pulling something from the environment, or memory, and that copy of system.ini is missing the "wave" key. Maybe my X-FI Extreme soundcard driver temporarily removes that entry each time I boot my PC. Something is intercepting a direct read of the system.ini file by the IniRead() function.

Edited by Spiff59
Link to comment
Share on other sites

In this example, IniRead() fails to retrieve the "wave" key, IniReadSection() fails to retrieve the "wave" key, but the line is displayed via the _FileReadToArray function:

#include<file.au3>
#include<array.au3>

$Path = @WindowsDir & "\system.ini"
$key1 = IniRead($Path, "drivers", "wave",'')
MsgBox(1,"", $key1)
$key1 = IniRead($Path, "drivers", "timer",'')
MsgBox(1,"", $key1)

$var = IniReadSection($Path, "drivers")
For $i = 1 To $var[0][0]
    MsgBox(4096, "", "Key: " & $var[$i][0] & @CRLF & "Value: " & $var[$i][1])
Next

Dim $array
_FileReadToArray($Path, $array)
_ArrayDisplay($array)

So, using File... functions, instead of Ini... functions, will give me the actual data within the system.ini file on disk. Using the Ini... functions somehow presents some cached version, or version from memory. If this is how IniRead() and IniReadSection() behave (or Windows behaves), maybe it ought to be documented in the help file???

typo

Edited by Spiff59
Link to comment
Share on other sites

From MSDN (kernel32.dll's GetPrivateProfileString function):

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

This mapping is likely if an application modifies system-component initialization files, such as Control.ini, System.ini, and Winfile.ini. In these cases, the function retrieves information from the registry, not from the initialization file; the change in the storage location has no effect on the function's behavior.

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

Look in the registry for the name of the initialization file under the IniFileMapping key.

Look for the section name specified by lpAppName. This will be a named value under the key that has the name of the initialization file, or a subkey with this name, or the name will not exist as either a value or subkey.

If the section name specified by lpAppName is a named value, 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, 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, then there will be an unnamed value (shown as <No Name>) that specifies the default location in the registry where you will find the keys for the section.

If there is no subkey or entry for the section name, then look for the actual initialization file 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.

So, in the case of "system-component initialization files", IniRead() and IniReadSection() can not be trusted to return the values in the files as they exist on disk. Anyone think this worthy of comment in the help file?

Edit: FYI, I found the following .ini files are mapped to the registry on my machine:

Clock.ini

control.ini

ImageFileExecutionOptions.ini

KeyboardLayout.ini

msacm.ini

Ntbackup.ini

ntnet.ini

regedt32.ini

schdpl32.ini

system.ini

win.ini

winfile.ini

Edited by Spiff59
Link to comment
Share on other sites

Yup, function is looking in the registry to find that:

drivers REG_SZ   #SYS:Microsoft\Windows NT\CurrentVersion\drivers

*think*
goto
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\drivers

... to find that only -
timer      REG_SZ    timer.drv

exists;]

Nothing is stopping you from implementing your own IniRead() function, for example you can do:

Dim $sKey = __IniRead(@WindowsDir & '\system.ini', 'drivers', 'wave', '')
ConsoleWrite($sKey & @LF)

Func __IniRead($sFile, $sSection, $sKeyName, $sDefault)
    Local $hFile
    Local $sSrc
    Local $aMatch
    
    Local $hFile = FileOpen($sFile, 0)
    If $hFile = -1 Then Return SetError(1, 0, False)
    
    $sSrc = FileRead($hFile)
    FileClose($hFile)
    
    $aMatch = StringRegExp($sSrc, '(?m)(?s)^\[\Q' & $sSection & '\E\]\r\n(.*?(?=^\[|\z))', 1)
    If IsArray($aMatch) Then 
        $aMatch = StringRegExp($aMatch[0], '(?m)^\Q' & $sKeyName & '\E\s*=(.*)', 1)
        If IsArray($aMatch) Then Return $aMatch[0]
        Return ''
    Else
        Return ''
    EndIf
EndFunc

:)

Edited by Authenticity
Link to comment
Share on other sites

Nothing is stopping you from implementing your own IniRead() function, for example you can do:

Frankly, I have little interest in .ini files or the Ini... functions. I consider all the structure unnecessary bells-and-whistles and would rather just deal with simple text files. I am though a stickler for details and documentation, and when I come across something that doesn't behave as expected it piques my interest. I'd definately vote for a two-line addition to the .ini functions in the help file stating something like:

Note: Certain system-component initialization files may be mapped to the registry, in these cases this function reads from, or writes to, the registry and does not access the .ini file on disk. Registry values may differ from those in the file.

PS - I tested IniWrite() and IniWriteSection(), they write to the registry, and not the disk, for mapped .ini files.

Edited by Spiff59
Link to comment
Share on other sites

Edit: FYI, I found the following .ini files are mapped to the registry on my machine:

That would also explain something I found when trying to debug your problem:

I copied system.ini to my scriptdir, changed the timer value to something else and it was still returning the old value.

I then deleted all the sections in the copied system.ini and made my own, with it's own made-up section/values.

I did a IniReadSectionNames, looped throug those to execute IniReadSection and _Array display the results and I got what (I thought) was in the original system.ini plus what was in my re-written copy...

That's when I gave up and went to bed!

Thanks for resolving this Spiff59.

Edited by ResNullius
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...