Having read the Help file and studied and tested the functions, it appears
that the only FF_INI function that actually writes to an INI file is:
FF_INI_SetKey
If this is a correct conclusion, then:
1. How would I write only a section header? For example, I want to create a new INI with only one section header: [CLIENTS]
2. How do I write a line with only the comment character ";" ?
3. Is it possible to write a blank line?
I understand I can do anything to the file using standard file access methods, but I'm asking with regard to FF_INI functions because of the convenience they provide in most every other need related to INI-files...
If I missed some FF_INI functions or my conclusion is incorect, please let me know that, too.
(Why would I think I'd have to ask for such information? :lol: )
-John
ADDED:
=====
Also noticed that a key that starts with a ";" gets pushed to the end of the section:
FF_INI_SetKey (temp, "CLIENTS", ";", " ") 'writes: ;= but AFTER the next line!!! ???
FF_INI_SetKey (temp, "CLIENTS", "0003", "Sample, Joe")
Is it possible to comment out an existing line and leave it in place without it getting moved to the bottom of the Section?
-j
========
Hmmm... no replies. How far off base am I?
-j
bump?
hmmm.... I must have missed this question :oops: I will fire up FireFly tonight and get you a definitive answer. The ini functions were written a long, long, long time ago and I seem to remember that they were from Paul Noble (not sure)???
I just tried to do the things that you question in your post - and it looks like I am running into the same brick wall as you. The FF_INI_SetKey is a simple wrapper around the WinAPI function WritePrivateProfileString and it has some traits that seem odd. For example, it always seems to want to write a key/value pair for a section - if no key/value is being written then it effectively deletes the section if no other key/values exist in the section. This is why you can't seem to write "[CLIENTS]" by itself. I couldn't write a blank line either.
I would not trust that the order that you read and write the sections/keys/values will always be as you expect. Windows is handling all this behind the scenes. That is why your ";" is sometimes appearing at the end rather than the beginning like you want it to.
If your end users are able to edit this ini then I recommend that you use PB's built in file routines to create and handle the file as you need rather than relying on the ini functions. If you are creating a simple ini function to save/restore program state information, then the ini functions should suffice.
OK Paul, I appreciate your looking into it. I just needed to confirm that I wasn't missing something obvious.
If I don't have that kind of control over things in the file, then I'll edit my specifications!
...I've done a lot of technical writing, and I not-so-kiddingly used to bill myself out as:
"The Cleanup Team - We turn your bugs into Features!"
Thanks for your help,
-John
(I think the routines were from Eros Olmos.)
Does WritePrivateProfileSection do the same thing? Its help somewhat acts like it doesn't care about the values.
Roger,
In the materials I was able to locate, I don't remember anything that discussed whether the positions of the values can change (or not).
In all my testing, however, I've never seen the order changed by Win.
Paul,
Here's the link to the Eros Olmi posting:
http://www.powerbasic.com/support/forums/Forum7/HTML/001467.html
Also, I've written a function that permits Setting a Special Line. After I finish testing, I'll post it.
I'll be adding it to my "EO-ini.inc", and if you find it useful, Paul, you can add it to your products.
Thanks for your help,
-John
I've tested this code and it does what I wanted it to do. Feel free to use it
or adapt it as you choose. No guarantees expressed or implied. YMMV. :D
'jmINI.inc
'
'2006-11-18 by jhm
' created: INI_SetSpecialLine()
'=================================
'to supplement INI_ functions in eoINI.inc and FireFly
'this code helps in setting a special line into an INI:
'- a Section header with no keys, a blank, or a comment
'============================================
#IF NOT %DEF(%jmIniINC)
%jmIniINC = 1
#IF NOT %DEF(%WINAPI)
#INCLUDE "WIN32API.INC"
#ENDIF
#IF NOT %DEF(%EOINIINC) 'may not **always** want the others, but mostly yes...
#INCLUDE "eoINI.inc"
#ENDIF
'==========================================
'added 2006-11-18 by jhm
%BEFORE_SECTION = 1001
%TOP_OF_SECTION = 1002
%BOTTOM_OF_SECTION = 1003
'These can now be changed to numeric equates if desired:
$BLANK = " " 'better to use $SPC or SPACE$(1) etc...
$COMMENT = ";" 'initially saw API kill the line if no trailing space, but haven't seen that lately...?
'not really needed, but used during testing:
'$OTHER = "textline" 'not allowed unless preceeded with a ";"
$OTHER = "[NEWSECTION]" ' yes, inserting just a section header is OK
'============================================ the reason for doing everything else in the file:
FUNCTION INI_SetSpecialLine(BYVAL iniFullspec AS STRING, BYVAL Txt AS STRING, BYVAL Where AS LONG, BYVAL TgtSection AS STRING) AS LONG
'This function supplements the INI_ functions; helps insert special types of lines into an INI file.
'- a blank line
'- a comment line
'- a [Section] line without any keys below it
'note that although "Before a Section" and "Bottom of Section" produce the same result, each is specified differently
LOCAL FilBuf AS LONG, tmp AS STRING, Index AS LONG, sLeft, sRight AS STRING
FUNCTION = %TRUE 'assume success unless an error arises
FilBuf& = FREEFILE
OPEN iniFullspec FOR BINARY AS #FilBuf&
IF ERR THEN
FUNCTION = %FALSE
EXIT FUNCTION
END IF
GET$ #FilBuf&, LOF(FilBuf&), tmp$
Index = INSTR(tmp$, TgtSection) ' locate the section of interest!
IF Index = 0 THEN
FUNCTION = %FALSE
EXIT FUNCTION
END IF
'divide the INI into sLeft$ and sRight$, using the Index from above
SELECT CASE Where
CASE %BEFORE_SECTION
sLeft$ = LEFT$(tmp$, IIF(Index -2 >= 1, Index-2, 0))
sRight$ = MID$(tmp$, Index)
CASE %TOP_OF_SECTION
sLeft$ = LEFT$(tmp$, Index + LEN(TgtSection))
Index = INSTR(Index, tmp$, $CRLF)
sRight$ = MID$(tmp$, Index+2)
CASE %BOTTOM_OF_SECTION
Index = INSTR(Index+1, tmp$, "[")' is actually start of next Section; but looks/acts like %BEFORE_SECTION
sLeft$ = LEFT$(tmp$, Index-1)
sRight$ = MID$(tmp$, Index)
END SELECT
'get set to write
SEEK #FilBuf&, 1
'create the output according to what was requested
SELECT CASE Txt
CASE $BLANK
PUT$ #FilBuf&, sLeft$ & $CRLF & sRight$
CASE $COMMENT
'PUT$ #FilBuf&, sLeft$ & "; " & $CRLF & sRight$ ' barely a visible divider...
PUT$ #FilBuf&, sLeft$ & ";"& STRING$(15, "-") & $CRLF & sRight$ 'much better!
CASE ELSE ' anything that's not a blank or a comment!
'$OTHER = "textline" ' not allowed unless preceeded with a ";"
IF LEFT$(Txt,1) = "[" THEN '
'section header with no keys
PUT$ #FilBuf&, sLeft$ & Txt & $CRLF & sRight$
ELSE
PUT$ #FilBuf&, sLeft$ & ";" & Txt & $CRLF & sRight$ 'these are allowed
END IF
END SELECT
CLOSE #FilBuf&
END FUNCTION
'==============================================
#ENDIF
Here's my test program for the new routine:
You can copy eoini.inc from:
http://www.powerbasic.com/support/forums/Forum7/HTML/001467.html
'write comment to section.bas
'============================
'the purpose of this program is to exercise and test one function: INI_SetSpecialLine()
'
'INI_SetSpecialLine() lets you write ONLY: a blank line, or a comment, or a section name
'
'developed 2006-11-10 by John Montenigro
'==========================================
#COMPILE EXE
#DIM ALL
#INCLUDE "win32api.inc"
#INCLUDE "eoini.inc" 'INI routines developed by Eros Olmi
'for code and more details about "eoini.inc", see:
' http://www.powerbasic.com/support/forums/Forum7/HTML/001467.html
#INCLUDE "jmINI.inc" ' <==== SEE PREVIOUS FORUM MESSAGE
'------------------------------------------------ utility routines for this app
SUB CreateMasterTestIni(BYVAL FileSpec AS STRING)
' create "TestIni.i_i"
LOCAL FilBuf AS LONG, x AS STRING
x$ = "[Section 1]" & $CRLF & _
"Key 1=Key1Value" & $CRLF & _
"[Section 2]" & $CRLF & _
"Key 1=Key1Value" & $CRLF & _
"Key 2=Key2Value" & $CRLF & _
"[Section 3]" & $CRLF & _
"Key 1=Key1Value" & $CRLF & _
"Key 2=Key2Value" & $CRLF & _
"Key 3=Key3Value" & $CRLF & $CRLF
FilBuf& = FREEFILE
OPEN FileSpec FOR BINARY AS #FilBuf&
IF ERR THEN EXIT SUB
PUT$ #FilBuf&, x$
CLOSE #FilBuf&
END SUB
FUNCTION INI_Report(BYVAL iniFile AS STRING) AS STRING
LOCAL Count AS LONG
LOCAL SectionList, KeyList, SectionName, Message AS STRING
'Create a CRLF separated list of all section names inside IniFile
SectionList = INI_GetSectionsList(IniFile)
'show the keys under/in each section in the IniFile
FOR Count = 1 TO PARSECOUNT(SectionList,$CRLF)
SectionName = PARSE$(SectionList,$CRLF,Count)
KeyList = INI_GetSectionKeyList(IniFile, SectionName)
Message = Message & _
"This is section number : " & STR$(INI_GetSectionIndex(IniFile, SectionName)) & $CRLF & _
"Section name is: " & SectionName & $CRLF & _
"Inside this section there are " & STR$(INI_GetSectionTotalKeys(IniFile, SectionName)) & " keys." & $CRLF & _
$CRLF & _
"Keys inside this section are: " & $CRLF & _
$CRLF & _
KeyList & $CRLF & $CRLF
KeyList = ""
SectionName = ""
NEXT Count
FUNCTION = Message
END FUNCTION
'---------------------------------------------------------- general utility
FUNCTION GetWholeFile(BYVAL FileSpec AS STRING) AS STRING
LOCAL FilBuf AS LONG, tmp AS STRING
FilBuf& = FREEFILE
OPEN FileSpec FOR BINARY AS #FilBuf&
IF ERR THEN
FUNCTION = ""
EXIT FUNCTION
END IF
GET$ #FilBuf&, LOF(FilBuf&), tmp$
CLOSE #FilBuf&
FUNCTION = tmp$
END FUNCTION
FUNCTION Exist(File$) AS LONG
LOCAL Dummy&
Dummy& = GETATTR(File$)
FUNCTION = (ERRCLEAR = 0)
END FUNCTION
FUNCTION AppPath() AS STRING
LOCAL zTmp AS ASCIIZ * 256
LOCAL sTmp AS STRING
LOCAL LenExeName AS LONG
LenExeName& = GetModuleFileName(BYVAL %NULL, zTmp, SIZEOF(zTmp))
IF LenExeName& THEN
LenExeName& = MIN&(LenExeName&, SIZEOF(zTmp))
sTmp = LEFT$(zTmp, LenExeName&)
sTmp = LEFT$(sTmp,INSTR(-1,sTmp,"\"))
FUNCTION = sTmp
END IF
END FUNCTION
'===============================================
FUNCTION PBMAIN() AS LONG
LOCAL IniFile, stdIniFile, TgtSection, x AS STRING
StdIniFile = AppPath & "TestIni.I_I" 'this is the unaltered original; we revert to it after each test
IniFile = AppPath & "TestIni.INI"
IF ISFALSE(Exist(StdIniFile)) THEN
CreateMasterTestIni(StdIniFile)
END IF
' MSGBOX INI_Report(IniFile) 'to see what's there at the start
TgtSection = "[Section 2]" 'let's do all our tests on this one section
'The general concept is:
' INI_SetSpecialLine(iniFile, What, Where, TgtSection)
FILECOPY StdIniFile, iniFile 'prep
INI_SetSpecialLine(iniFile, $BLANK, %BEFORE_SECTION, TgtSection) 'this is the work
x$ = "$BLANK, %BEFORE_SECTION 2" 'prep
MSGBOX GetWholeFile(iniFile),,x$ 'prep
MSGBOX INI_Report(IniFile),,x$ 'report
FILECOPY StdIniFile, iniFile
INI_SetSpecialLine(iniFile, $BLANK, %TOP_OF_SECTION, TgtSection)
x$ = "$BLANK, %TOP_OF_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
FILECOPY StdIniFile, iniFile
INI_SetSpecialLine(iniFile, $BLANK, %BOTTOM_OF_SECTION, TgtSection)
x$ = "$BLANK, %BOTTOM_OF_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
FILECOPY StdIniFile, iniFile
INI_SetSpecialLine(iniFile, $COMMENT, %BEFORE_SECTION, TgtSection)
x$ = "$COMMENT, %BEFORE_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
FILECOPY StdIniFile, iniFile
INI_SetSpecialLine(iniFile, $COMMENT, %TOP_OF_SECTION, TgtSection)
x$ = "$COMMENT, %TOP_OF_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
FILECOPY StdIniFile, iniFile
INI_SetSpecialLine(iniFile, $COMMENT, %BOTTOM_OF_SECTION, TgtSection)
x$ = "$COMMENT, %BOTTOM_OF_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
FILECOPY StdIniFile, iniFile
INI_SetSpecialLine(iniFile, $OTHER, %BEFORE_SECTION, TgtSection)
x$ = "$OTHER, %BEFORE_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
FILECOPY StdIniFile, iniFile
'NOTE HOW THIS takes over existing Section's keys! *****
INI_SetSpecialLine(iniFile, $OTHER, %TOP_OF_SECTION, TgtSection)
x$ = "$OTHER, %TOP_OF_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
FILECOPY StdIniFile, iniFile
INI_SetSpecialLine(iniFile, $OTHER, %BOTTOM_OF_SECTION, TgtSection)
x$ = "$OTHER, %BOTTOM_OF_SECTION 2"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
'OK, all work correctly
'now test to see if we can add a key using the other eoini.inc file
'INI_SetKey (IniFile, "SECTION 1", "NewKey", "This is new") 'this works OK
INI_SetKey (IniFile, "NEWSECTION", "NewKey", "This is new") 'Yesss!
x$ = "$Adding NewKey with value"
MSGBOX GetWholeFile(iniFile),,x$
MSGBOX INI_Report(IniFile),,x$
'that's it - all works as intended
END FUNCTION