Looking through an old database I had created I came across some code I had put together a while back to read and write INI files.
If you’ve searched online a little, I’m sure you’ve found APIs such as the GetPrivateProfileString function, that can be used to do this, but as much as I can, I try to minimize my use of ActiveX and APIs and this is one case where an API is not required to perform basic text search and writing, because after all an INI file is nothing more than a text file.
As time has passed, I’ve also learnt the value of using INI files to store user preferences. While it is possible to store such data in the database front-end itself, it is lost upon updating. Another option would be to use the registry, but I dislike using the registry except for registration information and the likes. Beyond which, registry settings cannot easily be pushed out to other new computers. By using a simple INI file, you can store any information you choose, it remains intact when updates are performed and can be transferred with great ease to other computers so the user can retain their settings.
What Information Might We Store In a INI File?
Here are a few random examples of the types of things that one might save as part of a user’s INI File:
- Default output directory (for reports/exports)
- Default input directory (file imports, images, …)
- Theme
- Font (family, size, …)
- Prefered Date/Time format
- Preferred Currency format
- Language
- etc.
Just be careful never to store sensitive/confidential information after all the INI file is a text file and anyone can open it, or even edit it. So don’t store information relating to usernames, passwords, security level settings, … at least not without first encrypting the data.
Working With INI Files Using Pure VBA
Enough with the talk, below are the functions involved.
Public bSectionExists As Boolean
Public bKeyExists As Boolean
'---------------------------------------------------------------------------------------
' Procedure : Ini_ReadKeyVal
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Read an INI file's Key
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Uses Late Binding, so none required
' No APIs either! 100% VBA
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sIniFile : Full path and filename of the INI file to read
' sSection : INI Section to search for the Key to read the Key from
' sKey : Name of the Key to read the value of
'
' Usage:
' ~~~~~~
' ? Ini_Read(Application.CurrentProject.Path & "\MyIniFile.ini", "LINKED TABLES", "Path")
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2012-08-09 Initial Release
' 2 2025-06-03 Updated Copyright.
'---------------------------------------------------------------------------------------
Function Ini_ReadKeyVal(ByVal sIniFile As String, _
ByVal sSection As String, _
ByVal sKey As String) As String
On Error GoTo Error_Handler
Dim sIniFileContent As String
Dim aIniLines() As String
Dim sLine As String
Dim i As Long
sIniFileContent = ""
bSectionExists = False
bKeyExists = False
'Validate that the file actually exists
If FileExist(sIniFile) = False Then
MsgBox "The specified ini file: " & vbCrLf & vbCrLf & _
sIniFile & vbCrLf & vbCrLf & _
"could not be found.", vbCritical + vbOKOnly, "File not found"
GoTo Error_Handler_Exit
End If
sIniFileContent = ReadFile(sIniFile) 'Read the file into memory
aIniLines = Split(sIniFileContent, vbCrLf)
For i = 0 To UBound(aIniLines)
sLine = Trim(aIniLines(i))
If bSectionExists = True And Left(sLine, 1) = "[" And Right(sLine, 1) = "]" Then
Exit For 'Start of a new section
End If
If sLine = "[" & sSection & "]" Then
bSectionExists = True
End If
If bSectionExists = True Then
If Len(sLine) > Len(sKey) Then
If Left(sLine, Len(sKey) + 1) = sKey & "=" Then
bKeyExists = True
Ini_ReadKeyVal = Mid(sLine, InStr(sLine, "=") + 1)
End If
End If
End If
Next i
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
'Err.Number = 75 'File does not exist, Permission issues to write is denied,
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: Ini_ReadKeyVal" & vbCrLf & _
"Error Description: " & Err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
'---------------------------------------------------------------------------------------
' Procedure : Ini_WriteKeyVal
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Writes a Key value to the specified INI file's Section
' If the file does not exist, it will be created
' If the Section does not exist, it will be appended to the existing content
' If the Key does not exist, it will be appended to the existing Section content
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
' (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
' Req'd Refs: Uses Late Binding, so none required
' No APIs either! 100% VBA
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sIniFile : Full path and filename of the INI file to edit
' sSection : INI Section to search for the Key to edit
' sKey : Name of the Key to edit
' sValue : Value to associate to the Key
'
' Usage:
' ~~~~~~
' Call Ini_WriteKeyVal(Application.CurrentProject.Path & "\MyIniFile.ini", "LINKED TABLES", "Paths", "D:\")
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2012-08-09 Initial Release
' 2 2025-06-03 Updated version to address 2 bugs found in previous
' version (Thanks to Xavier Batlle for bringing 1 of
' the bugs to my attention).
' Updated Copyright.
'---------------------------------------------------------------------------------------
Function Ini_WriteKeyVal(ByVal sIniFile As String, _
ByVal sSection As String, _
ByVal sKey As String, _
ByVal sValue As String) As Boolean
On Error GoTo Error_Handler
Dim sIniFileContent As String
Dim aIniLines() As String
Dim sLine As String
Dim sNewLine As String
Dim i As Long
Dim bFileExist As Boolean
Dim bInSection As Boolean
Dim bSectionExists As Boolean
Dim bKeyExists As Boolean
Dim bKeyAdded As Boolean
sIniFileContent = ""
bSectionExists = False
bKeyExists = False
bKeyAdded = False
bInSection = False
' Validate that the file actually exists
bFileExist = FileExist(sIniFile)
If bFileExist Then
sIniFileContent = ReadFile(sIniFile) ' Read the file into memory
If Len(sIniFileContent) > 0 Then
aIniLines = Split(sIniFileContent, vbCrLf) ' Break the content into individual lines
Else
ReDim aIniLines(0)
aIniLines(0) = ""
End If
Else
' File does not exist, start with empty lines
ReDim aIniLines(0)
aIniLines(0) = ""
End If
sIniFileContent = "" ' Reset it
For i = 0 To UBound(aIniLines)
sLine = Trim(aIniLines(i))
sNewLine = ""
' Detect section
If sLine = "[" & sSection & "]" Then
bSectionExists = True
bInSection = True
ElseIf Left(sLine, 1) = "[" And Right(sLine, 1) = "]" Then
' If we were in the target section and hit a new section, check if key was added
If bInSection And Not bKeyExists Then
If Len(sIniFileContent) > 0 Then sIniFileContent = sIniFileContent & vbCrLf
sIniFileContent = sIniFileContent & sKey & "=" & sValue
bKeyAdded = True
End If
bInSection = False
End If
' If inside the target section, check for the key
If bInSection And Not bKeyExists Then
If InStr(1, sLine, sKey & "=", vbTextCompare) = 1 Then
sNewLine = sKey & "=" & sValue
bKeyExists = True
bKeyAdded = True
End If
End If
' Build the new content
If Len(sIniFileContent) > 0 Then sIniFileContent = sIniFileContent & vbCrLf
If sNewLine = "" Then
sIniFileContent = sIniFileContent & sLine
Else
sIniFileContent = sIniFileContent & sNewLine
End If
Next i
' If section was not found, add it and the key at the end
If Not bSectionExists Then
If Len(sIniFileContent) > 0 Then sIniFileContent = sIniFileContent & vbCrLf
sIniFileContent = sIniFileContent & "[" & sSection & "]" & vbCrLf & sKey & "=" & sValue
ElseIf Not bKeyAdded Then
' Section exists, but key was not found, so add it at the end of the section
If Right(sIniFileContent, 2) <> vbCrLf Then sIniFileContent = sIniFileContent & vbCrLf
sIniFileContent = sIniFileContent & sKey & "=" & sValue
End If
' Write to the ini file the new content
Call OverwriteTxt(sIniFile, sIniFileContent)
Ini_WriteKeyVal = True
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: Ini_WriteKeyVal" & vbCrLf & _
"Error Description: " & Err.Description, _
vbOKOnly + vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
The above also requires the following helper functions
'---------------------------------------------------------------------------------------
' Procedure : FileExist
' DateTime : 2007-Mar-06 13:51
' Author : CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Test for the existance of a file; Returns True/False
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' strFile - name of the file to be tested for including full path
'---------------------------------------------------------------------------------------
Function FileExist(strFile As String) As Boolean
On Error GoTo Err_Handler
FileExist = False
If Len(Dir(strFile)) > 0 Then
FileExist = True
End If
Exit_Err_Handler:
Exit Function
Err_Handler:
MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: FileExist" & vbCrLf & _
"Error Description: " & Err.Description, _
vbCritical, "An Error has Occurred!"
GoTo Exit_Err_Handler
End Function
'---------------------------------------------------------------------------------------
' Procedure : OverwriteTxt
' Author : Daniel Pineault, CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Output Data to an external file (*.txt or other format)
' ***Do not forget about access' DoCmd.OutputTo Method for
' exporting objects (queries, report,...)***
' Will overwirte any data if the file already exists
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' sFile - name of the file that the text is to be output to including the full path
' sText - text to be output to the file
'
' Usage:
' ~~~~~~
' Call OverwriteTxt("C:\Users\Vance\Documents\EmailExp2.txt", "Text2Export")
'
' Revision History:
' Rev Date(yyyy/mm/dd) Description
' **************************************************************************************
' 1 2012-Jul-06 Initial Release
'---------------------------------------------------------------------------------------
Function OverwriteTxt(sFile As String, sText As String)
On Error GoTo Err_Handler
Dim FileNumber As Integer
FileNumber = FreeFile ' Get unused file number
Open sFile For Output As #FileNumber ' Connect to the file
Print #FileNumber, sText; ' Append our string
Close #FileNumber ' Close the file
Exit_Err_Handler:
Exit Function
Err_Handler:
MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: OverwriteTxt" & vbCrLf & _
"Error Description: " & Err.Description, _
vbCritical, "An Error has Occurred!"
GoTo Exit_Err_Handler
End Function
'---------------------------------------------------------------------------------------
' Procedure : ReadFile
' Author : CARDA Consultants Inc.
' Website : http://www.cardaconsultants.com
' Purpose : Faster way to read text file all in RAM rather than line by line
' Copyright : The following may be altered and reused as you wish so long as the
' copyright notice is left unchanged (including Author, Website and
' Copyright). It may not be sold/resold or reposted on other sites (links
' back to this site are allowed).
' Input Variables:
' ~~~~~~~~~~~~~~~~
' strFile - name of the file that is to be read
'
' Usage Example:
' ~~~~~~~~~~~~~~~~
' MyTxt = ReadText("c:\tmp\test.txt")
' MyTxt = ReadText("c:\tmp\test.sql")
' MyTxt = ReadText("c:\tmp\test.csv")
'---------------------------------------------------------------------------------------
Function ReadFile(ByVal strFile As String) As String
On Error GoTo Error_Handler
Dim FileNumber As Integer
Dim sFile As String 'Variable contain file content
FileNumber = FreeFile
Open strFile For Binary Access Read As FileNumber
sFile = Space(LOF(FileNumber))
Get #FileNumber, , sFile
Close FileNumber
ReadFile = sFile
Error_Handler_Exit:
On Error Resume Next
Exit Function
Error_Handler:
MsgBox "The following error has occurred." & vbCrLf & vbCrLf & _
"Error Number: " & Err.Number & vbCrLf & _
"Error Source: ReadFile" & vbCrLf & _
"Error Description: " & Err.Description, _
vbCritical, "An Error has Occurred!"
Resume Error_Handler_Exit
End Function
With the above code, you can now easily read any INI file key value and write/update key values by doing something like
Sub TestReadKey()
MsgBox "INI File: " & Application.CurrentProject.Path & "\MyIniFile.ini" & vbCrLf & _
"Section: SETTINGS" & vbCrLf & _
"Section Exist: " & bSectionExists & vbCrLf & _
"Key: License" & vbCrLf & _
"Key Exist: " & bKeyExists & vbCrLf & _
"Key Value: " & Ini_ReadKeyVal(Application.CurrentProject.Path & "\MyIniFile.ini", "SETTINGS", "License")
'You can validate the value by checking the bSectionExists and bKeyExists variable to ensure they were actually found in the ini file
End Sub
Sub TestWriteKey()
If Ini_WriteKeyVal(Application.CurrentProject.Path & "\MyIniFile.ini", "SETTINGS", "License", "JBXR-HHTY-LKIP-HJNB-GGGT") = True Then
MsgBox "The key was written"
Else
MsgBox "An error occurred!"
End If
End Sub
Working With INI Files Using APIs
For the sake of completeness, I thought I’d briefly demonstrate the usage of the Windows APIs for manipulating INI Files.
The basic code could look like:
#If VBA7 Then
Private Declare PtrSafe Function GetPrivateProfileStringA Lib "kernel32" ( _
ByVal lpApplicationName As String, _
ByVal lpKeyName As String, _
ByVal lpDefault As String, _
ByVal lpReturnedString As String, _
ByVal nSize As Long, _
ByVal lpFileName As String) As Long
Private Declare PtrSafe Function WritePrivateProfileStringA Lib "kernel32" ( _
ByVal lpApplicationName As String, _
ByVal lpKeyName As String, _
ByVal lpString As String, _
ByVal lpFileName As String) As Long
#Else
Private Declare Function GetPrivateProfileStringA Lib "kernel32" ( _
ByVal lpApplicationName As String, _
ByVal lpKeyName As String, _
ByVal lpDefault As String, _
ByVal lpReturnedString As String, _
ByVal nSize As Long, _
ByVal lpFileName As String) As Long
Private Declare Function WritePrivateProfileStringA Lib "kernel32" ( _
ByVal lpApplicationName As String, _
ByVal lpKeyName As String, _
ByVal lpString As String, _
ByVal lpFileName As String) As Long
#End If
Function ReadIniValue(ByVal sFile As String, _
ByVal sSection As String, _
ByVal sKey As String, _
Optional ByVal sDefault As String = "") As String
Dim sBuffer As String
Dim lRet As Long
sBuffer = String$(1024, vbNullChar) ' Buffer for the result
lRet = GetPrivateProfileStringA(sSection, sKey, sDefault, sBuffer, Len(sBuffer), sFile)
If lRet > 0 Then
ReadIniValue = Left$(sBuffer, lRet)
Else
ReadIniValue = sDefault
End If
End Function
Function WriteIniValue(ByVal sFile As String, _
ByVal sSection As String, _
ByVal sKey As String, _
ByVal sValue As String) As Boolean
Dim lRet As Long
lRet = WritePrivateProfileStringA(sSection, sKey, sValue, sFile)
WriteIniValue = (lRet <> 0)
End Function
Function DeleteIniKey(ByVal sFile As String, _
ByVal sSection As String, _
ByVal sKey As String) As Boolean
'Supplying a vbNullString actually deletes the key and/or section!
Dim lRet As Long
lRet = WritePrivateProfileStringA(sSection, sKey, vbNullString, sFile)
DeleteIniKey = (lRet <> 0)
End Function
Function DeleteIniSection(ByVal sFile As String, _
ByVal sSection As String) As Boolean
Dim lRet As Long
lRet = WritePrivateProfileStringA(sSection, vbNullString, vbNullString, sFile)
DeleteIniSection = (lRet <> 0)
End Function
and below are a couple examples of how they are used:
Sub WriteIniValue_Test()
' If the Ini file does not already exist, it will be created automatically
Debug.Print WriteIniValue(Application.CurrentProject.Path & "\MyIniFile.ini", "Settings", "Licenses", "MyValue")
End Sub
Sub ReadIniValue_Test()
Debug.Print ReadIniValue(Application.CurrentProject.Path & "\MyIniFile.ini", "Settings", "Licenses")
End Sub
Sub DeleteIniKey_Test()
Debug.Print DeleteIniKey(Application.CurrentProject.Path & "\MyIniFile.ini", "Settings", "License")
End Sub
Sub DeleteIniSection_Test()
Debug.Print DeleteIniSection(Application.CurrentProject.Path & "\MyIniFile.ini", "Settings")
End Sub
Page History
| Date | Summary of Changes |
|---|---|
| 2017-01-30 | Initial Release |
| 2025-06-03 | Updated Ini_WriteKeyVal Added the API Approach section |
why not store the user preferences in the database Back-end itself (with userId column of course)?
this what it’s has all the benefit of INI file, and it’s not limited to specific computer
It is another, very viable options which I have used as well.
Dear sir
I no nothing of basic, i just staed a few days ago. Well! It’s a beat hard.
A demo of *.INI file will help.
Best regards
Just perform a search on your own PC, you’ll find hundreds of ini files as examples.
Here’s an fictitious example:
Excellent example for avoiding API. Had issue with running in Excel 2010, replaced ‘Application.CurrentProject.Path’ with ActiveWorkbook.path (or Application.ActiveWorkbook.Path) to fix.
Thank you! Very well written and extremely useful code.
Hi there! Thank you very much for these super useful code snippets!
May I ask under which license (eg. MIT) you’re releasing this code?
Thank you
Most, if not all, of the procedures include a copyright notice. As of late, I have been trying to update them all to
Basically, recognize that you didn’t create it, keep it available to all, but feel free to use it as you please.
Hi,
When a new section1 as added with appropiate key and another section2 with key is added I come up with the following. After above adding (1+2) another new key is added for section1. However, this key for section1 is not inserted under the embrella of section1 but added at the end of the ini file resulting in storing the key under the latest section
Example:
[Test1]
Value1=aaa
[Test2]
Value2=bbbb
When I add Value11=cccc as key for section [Test1] the file consist of the following:
[Test1]
Value1=aaa
[Test2]
Value2=bbbb
Valu11=ccc
Any idea what to change in the source of the splendid VBA source btw. I like the idea in skipping API’s.
Could you post your code so I can replicate things on my end and see what can be done.
Love the code and use it extensively. Similar problem, and its probably easy to replicate.
You have an ini file with some sections, say
[foo]
[bar]
Now you write something to [foo] that has not existed before in [foo].
Ini_WriteKeyVal(LOCALSETTINGS_FILE, “foo”, “somedate”, Date)
Now oddly somedate=24.01.2020 is not inserted to [foo] but rather placed at the end of the file (also ending up in section [bar]). That’s not what I would expect. Any idea?
Best, Stefan
I have updated the code for the Ini_WriteKeyVal() function and I believe the issue to be resolved, but haven’t had the opportunity to do a lot of testing to confirm it.
Small inconsistency found in Ini_WriteKeyVal:
——————————————————
1) If bInSection = True Then
2) If sLine “[” & sSection & “]” Then
3) If Left(sLine, 1) = “[” And Right(sLine, 1) = “]” Then
4) ‘Our section exists, but the key wasn’t found, so append it
5) sNewLine = sKey & “=” & sValue
6) i = i – 1
7) bInSection = False ‘ we’re switching section
8) bKeyAdded = True
9) End If
10) End If
11) If Len(sLine) > Len(sKey) Then
12) If Left(sLine, Len(sKey) + 1) = sKey & “=” Then
13) sNewLine = sKey & “=” & sValue
14) bKeyExists = True
15) bKeyAdded = True
16) End If
17) End If
18) End If
———————————————————————
Line (4) has to be
4) If Left(sLine, 1) = “[” And Right(sLine, 1) = “]” And And bKeyAdded = False Then
needs to check for “bKeyAdded” otherwise on top of updating the key a new key (a replica) will be every time appended to the section.
Hi, yes this is resolving the issue,
thank you Maurizio
Daniel
this is not fixing it for me somehow. It just makes for every section, which has the attribute is changed. I dont get why.
Works excellent at first ! Added this code to a new module and starts using it from my existing code. Thanks !
Added this code and created the option on storing some setting outside of excel for a common user to change some list he should maintain.
As this excel overwrites some formules after executing vba code, changing was prohibited for normal users. and I had to change this each time for them
Hello,
I’m getting an error when trying to launch this code in Word VBA, on this line:
sFile = Space(LOF(FileNumber))
of this function:
Function ReadFile(ByVal strFile As String) As String
The error I’m getting says “Error: Expected Array”
I googled the “LOF” function and the “Space” function to check what their inputs are, and I don’t understand what array VBA could be talking about.
Thank you for the code posted here. Looking forward to using it!
I couldn’t say. I’ve used that function (https://www.devhut.net/vba-read-file-into-memory/) for years and never seen such a message. I suspect something else is going on and the root cause doesn’t lie there. Does your code compile without errors?
Hi Daniel. When I click Debug > Compile, I get the same error message stating “Compile Error: Expecting Array” with “Space” highlighted in the VBA window.
I copy / pasted all of the code you posted into a separate Module from anything else, so that nothing I have could interfere with this code and visa versa.
Any ideas? Does the code require any kind of Reference Library to be added other than the typical ones? Thank you for any help, Anthony
No special references required.
I copy/pasted and tested and it works fine for me. Try: https://1drv.ms/u/s!AjYnefG2siYSjUF6V8EEUl4POCy4?e=CwXbF7
Hello again! Thank you for taking the time to try it yourself and send that file over. Does this code only work in Access? I was trying to run it in Word, maybe that’s where I’m going wrong lol.
I’ve never tested in Word.
It worked fine for me: https://1drv.ms/u/s!AjYnefG2siYSjUXHvJZPWxz-DRVy?e=1ADOiU
Hi Daniel,
Using an entirely separate dotm file, the code is working just fine, so there must be something in another module causing the issue. I should have thought to try that first, my apologies. Thank you for the tremendous help and awesome code.
Thanks again Daniel for another great subject matter.
I implemented this same thing in my last project, only in a much more primitive way.
I like this better than storing this in the backend DB. Although some keys are also stored in the DB for other reasons.
I agree with Mário, a YouTube video would be a good watch.