Attribute VB_Name = "Ini" Option Explicit 'Global Const gblsIniFileNothing$ = "NO9999NE" #Const ReadIniFileDebug = 0 Declare Function WritePrivateProfileString& Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpAppName$, ByVal lpKeyName As Any, ByVal lpString$, ByVal lpFileName$) Declare Function GetPrivateProfileString& Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lSection As String, ByVal lKey As String, ByVal lDefault As String, ByVal lReturn As String, ByVal nSize As Long, ByVal lFile As String) Declare Function GetProfileString& Lib "kernel32" Alias "GetProfileStringA" (ByVal lpAppName$, ByVal lpKeyName As Any, ByVal lpDefault$, ByVal lpReturnedString$, ByVal nSize&) Declare Function WriteProfileString& Lib "kernel32" Alias "WriteProfileStringA" (ByVal lpApplicationName$, ByVal lpKeyName$, ByVal lpString$) Declare Function WritePrivateProfileSection Lib "kernel32" Alias "WritePrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpString As String, ByVal lpFileName As String) As Long ' this one returns the names of the elements in a given section (; each key separated from its neighbors by a null; 2 nulls mark the end) Declare Function GetPrivateProfileElements& Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lSection As String, ByVal lKey As Long, ByVal lDefault As String, ByVal lReturn As String, ByVal nSize As Long, ByVal lFile As String) ' this one returns the entire lines in a given section (==; each key/value pair separated from its neighbors by a null; 2 nulls mark the end) Declare Function GetPrivateProfileSection Lib "kernel32" Alias "GetPrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long Function ReadIniFile(ByVal sIniFileName$, ByVal sIniFileSection$, ByVal sIniElement$, ByVal sDefaultValue$, ByVal iMaximumLength%) As String ' this is a generic easier-to-remember centralized function for reading from any ' .ini-type file ' transparent to UTF-8 Dim sString$ Dim iRc% Dim lLength As Long 'Debug.Print "RIF: >" + sIniFileName$ + "< >" + sIniFileSection$ + "< >" + sIniElement$ + "<" DoEvents ' this is a common point for reading information fron an INI file and preparing ' it in the standard manner lLength = iMaximumLength% ' allocate storage sString$ = Space$(lLength + 1) ' read the file iRc = GetPrivateProfileString(sIniFileSection$, sIniElement, sDefaultValue$, sString$, lLength, sIniFileName$) ' remove any trailing null passed by Windows/C iRc = InStr(sString$, Chr(0)) If iRc > 0 Then sString$ = Mid(sString$, 1, iRc - 1) End If DoEvents ' return what you found (if anything) ReadIniFile = sString$ End Function Sub WriteIniFile(ByVal sIniFileName$, ByVal sIniFileSection$, ByVal sIniElementName$, ByVal sIniElementValue$, Optional ByVal iFile% = 0) ' transparent to UTF-8 Dim iRetVal% Dim sFileName$, sSectionName$, sKeyName$, sKeyValue$ 'Debug.Print "WIF >" + sIniFileName$ + "< >" + sIniFileSection$ + "< >" + sIniElementName$ + "< >" + sIniElementValue$ + "<" sFileName$ = sIniFileName$ sSectionName$ = sIniFileSection$ sKeyName$ = sIniElementName$ sKeyValue$ = sIniElementValue$ iRetVal% = WritePrivateProfileString(sSectionName$, sKeyName$, sKeyValue$, sFileName$) ' this is just for debugging, I think: copy out information written to ' the INI-type file to some additional file whose 'handle' we've been passed If iFile% > 0 Then Print #iFile%, "File: " + sFileName$ Print #iFile%, " [" + sSectionName$ + "]" Print #iFile%, " " + sKeyName$ + "=" + sKeyValue$ Print #iFile%, " Returns: " + str(iRetVal%) End If End Sub Public Function ReadIniKeys(ByVal sIniFileName$, ByVal sIniFileSection$, Optional ByVal sSeparator$ = vbTab) As String ' this returns a list of the "keys" (the stuff to the left of the equals sign) ' in a given section of a given ini file; to get the full contents of ' each line in the section, use ReadIniSection ' transparent to UTF-8 Dim lNullPointer As Long, lLength As Long, lReturn As Long Dim sDefault$, sString$, sFile$, sSection$ Dim iRc%, iOut% sSection$ = sIniFileSection$ 'lNullPointer is already zero, so we don't have to set it to anything sDefault$ = "" sString$ = Space(32000) + Chr(0) ' maximum length retrievable I think lLength = 32001 sFile$ = sIniFileName$ ' we obviously can't get ourselves into the business of replacing one thing ' with itself--this way infinite loops lie! If sSeparator$ = Chr(0) Then Exit Function End If lReturn = GetPrivateProfileElements(sSection$, lNullPointer, sDefault$, sString$, lLength, sFile$) ' double null marks the end of the information read from the section lReturn = InStr(sString$, Chr(0) + Chr(0)) If lReturn > 0 Then sString$ = Mid(sString$, 1, lReturn - 1) End If Do iRc% = InStr(sString$, Chr(0)) ' the following allows us to avoid reference to ' the 'stuff' function Select Case iRc Case 0 Exit Do Case 1 sString$ = sSeparator$ + Mid(sString$, 2) Case Else sString$ = Mid(sString$, 1, iRc - 1) + sSeparator$ + Mid(sString$, iRc + 1) End Select Loop sString$ = Trim(sString$) If sString$ = sSeparator$ Then sString$ = "" End If ReadIniKeys = sString$ End Function Public Function ReadIniSection(ByVal sIniFileName$, ByVal sIniFileSection$, Optional ByVal sSeparator$ = vbTab) As String ' this returns the entire contents of a given section in a given .INI file; ' for a list of just the "keys" (the stuff to the left of the equals sign), ' use ReadIniKeys '20040203: utf-8 ok Dim lNullPointer As Long, lLength As Long, lReturn As Long Dim sDefault$, sString$, sFile$, sSection$ Dim iRc%, iSeparatorLength%, iOut% Dim lRc As Long ' NOTE that under Windows 95 [and probably 98], ' GetProvateProfileSection is limited to a return size ' of 32K; so we're going to pass along something ' even less than that sSection$ = sIniFileSection$ 'lNullPointer is automatically zero sDefault$ = "" sString$ = Space(32000) + Chr(0) lLength = 32001 sFile$ = sIniFileName$ iSeparatorLength% = Len(sSeparator$) lReturn = GetPrivateProfileSection(sSection$, sString$, lLength, sFile$) ' remove the trailing double-null that marks the end of the list lReturn = InStr(sString$, Chr(0) + Chr(0)) If lReturn > 0 Then sString$ = Mid(sString$, 1, lReturn - 1) End If ' we obviously can't get ourselves into the business of replacing one thing ' with itself--this way infinite loops lie! If sSeparator$ = Chr(0) Then Exit Function End If ' convert any internal nulls to the desired separator character Do lRc = InStr(sString$, Chr(0)) ' the following allows us to avoid reference to ' the 'stuff' function Select Case lRc Case 0 Exit Do Case 1 sString$ = sSeparator$ + Mid(sString$, 2) Case Else sString$ = Mid(sString$, 1, lRc - 1) + sSeparator$ + Mid(sString$, lRc + 1) End Select Loop sString$ = Trim(sString$) ' if the string consists only of the separator character, it doesn't ' exist at all If sString$ = sSeparator$ Then sString$ = "" End If ReadIniSection = Trim(sString$) End Function Public Function IsGoodIniLine(ByVal sIniLineToTest$, ByRef iReturnedLocationOfEqualsSign%) As Boolean ' NOTE that this function is not transparent to UTF-8: if the ' first character in the label for a line contains any character ' other than those listed in the first test, this function ' will fail ' set default value IsGoodIniLine = True If Len(sIniLineToTest$) = 0 Then IsGoodIniLine = False Exit Function End If If InStr("_|ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", UCase(Mid(sIniLineToTest$, 1, 1))) = 0 Then IsGoodIniLine = False Exit Function End If iReturnedLocationOfEqualsSign% = InStr(sIniLineToTest$, "=") If iReturnedLocationOfEqualsSign% = 0 Then IsGoodIniLine = False Exit Function End If ' else it must be a good 'un! return with default value of true and ' the pointer to the equals sign End Function Public Function ReadIniFileOrNothing(ByVal sIniFileName$, ByVal sIniFileSection$, ByVal sIniElement$, ByVal iMaximumLength%) As String ' this is a generic easier-to-remember centralized function for reading from any ' .ini-type file ' the difference between this and ReadIniFile is that if this function ' detects the default value, it returns an empty string ' transparent to UTF-8 Dim sString$, sDefault$ Dim iRc%, iOut% Dim lLength As Long Const sIniFileNothing$ = "NO9!9!9!9NE" If iMaximumLength% < Len(sIniFileNothing$) + 3 Then iMaximumLength% = Len(sIniFileNothing$) + 3 End If ' set the default return value to something that's not likely to be the value ' we find in the INI file sDefault$ = sIniFileNothing$ If iMaximumLength% < Len(sDefault$) Then iMaximumLength% = Len(sDefault$) + 5 End If ' use this value in a call to the 'standard' routine elsewhere in this file sString$ = ReadIniFile(sIniFileName$, sIniFileSection$, sIniElement$, sDefault$, iMaximumLength%) ' if the returned string is our not-likely string, then return ' nothing at all; otherwise, return whatever it is that you've found If sString$ = sDefault$ Then ReadIniFileOrNothing = "" Else ReadIniFileOrNothing = sString$ End If End Function Public Sub WriteIniSection(ByVal sIniFileName$, ByVal sIniSectionName$, ByVal sIniStanzaContents$) ' transparent to UTF-8 ' this is a generic easier-to-remember centralized function for writing an entire ' stanza at one blow ' sIniStanzaContents must be structured in this manner: ' * each separate item consists of a label, "=", and a value, terminated by a null character ' * the final item in the stanza is terminated by a double null Dim lResult As Long Dim sTrueContents$ If Len(sTrueContents$) = 0 Then sTrueContents$ = Chr(0) + Chr(0) + Chr(0) Else sTrueContents$ = sIniStanzaContents$ + Chr(0) + Chr(0) End If lResult = WritePrivateProfileSection(sIniSectionName$, sTrueContents$, sIniFileName$) Debug.Print "Result of writing this section: " + str(lResult) End Sub Public Sub FormStateSave(ByVal bUseRegistry As Boolean, ByVal sIniFileName$, ByVal sStanza$, ByRef fForm As Form) Dim sElement$, sValue$ sElement$ = fForm.Name Select Case fForm.WindowState Case vbMaximized sValue$ = "2" Case vbMinimized sValue$ = "1" Case Else With fForm sValue$ = "0 " + Trim(str(.Top)) + " " + Trim(str(.Left)) + " " + Trim(str(.Height)) + " " + Trim(str(.Width)) End With End Select If bUseRegistry Then SaveSetting sIniFileName$, sStanza$, sElement$, sValue$ Else WriteIniFile sIniFileName$, sStanza$, sElement$, sValue$ End If End Sub Public Sub FormStateRestore(ByVal bUseRegistry As Boolean, sIniFileName$, ByVal sStanza$, ByRef fForm As Form) Dim sElement$, sValue$, sState$ sElement$ = fForm.Name If bUseRegistry Then sValue$ = GetRegistrySettingOrNothing(sIniFileName$, sStanza$, sElement$) Else sValue$ = ReadIniFileOrNothing(sIniFileName$, sStanza$, sElement$, 200) End If If Len(sValue$) = 0 Then fForm.WindowState = vbDefault ' default: normal Else GetNextPiece sValue$, sState$, " " Select Case sState$ Case "0" fForm.WindowState = vbDefault ' default: normal If Len(sValue$) > 0 Then GetNextPiece sValue$, sState$, " " ' top fForm.Top = Val(sState$) If Len(sValue$) > 0 Then GetNextPiece sValue$, sState$, " " ' left fForm.Left = Val(sState$) If Len(sValue$) > 0 Then GetNextPiece sValue$, sState$, " " ' height fForm.Height = Val(sState$) If Val(sState$) > 0 Then GetNextPiece sValue$, sState$, " " ' width fForm.Width = Val(sState$) End If End If End If End If Case "1" fForm.WindowState = vbDefault ' minimized; but we're going to start out as "normal" Case "2" fForm.WindowState = vbMaximized End Select End If End Sub