[SETUPLIB] Add support for removing sections and key values, and remove some hacks (#6815)

- Use LIST_ENTRY instead of custom list pointers;
- Fix key/section unlinking before freeing.
This commit is contained in:
Hermès Bélusca-Maïto 2024-04-24 12:58:09 +02:00
parent b9ca9b0061
commit e151ef9ae1
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
3 changed files with 201 additions and 199 deletions

View file

@ -1006,9 +1006,8 @@ QueryBootStoreOptions(
IN OUT PBOOT_STORE_OPTIONS BootOptions
/* , IN PULONG BootOptionsLength */ )
{
NTSTATUS Status = STATUS_SUCCESS;
PBOOT_STORE_CONTEXT BootStore = (PBOOT_STORE_CONTEXT)Handle;
PWCHAR TimeoutStr;
PCWSTR TimeoutStr;
if (!BootStore || !BootOptions)
return STATUS_INVALID_PARAMETER;
@ -1036,36 +1035,34 @@ QueryBootStoreOptions(
{
BootOptions->Version = FreeLdr;
Status = IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"DefaultOS", (PWCHAR*)&BootOptions->CurrentBootEntryKey);
if (!NT_SUCCESS(Status))
BootOptions->CurrentBootEntryKey = 0;
BootOptions->CurrentBootEntryKey = 0;
IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"DefaultOS", (PCWSTR*)&BootOptions->CurrentBootEntryKey);
Status = IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"TimeOut", &TimeoutStr);
if (NT_SUCCESS(Status) && TimeoutStr)
BootOptions->Timeout = 0;
if (IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"TimeOut", &TimeoutStr) && TimeoutStr)
{
BootOptions->Timeout = _wtoi(TimeoutStr);
else
BootOptions->Timeout = 0;
}
}
else if (BootStore->Type == NtLdr)
{
BootOptions->Version = NtLdr;
Status = IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"default", (PWCHAR*)&BootOptions->CurrentBootEntryKey);
if (!NT_SUCCESS(Status))
BootOptions->CurrentBootEntryKey = 0;
BootOptions->CurrentBootEntryKey = 0;
IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"default", (PCWSTR*)&BootOptions->CurrentBootEntryKey);
Status = IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"timeout", &TimeoutStr);
if (NT_SUCCESS(Status) && TimeoutStr)
BootOptions->Timeout = 0;
if (IniGetKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"timeout", &TimeoutStr) && TimeoutStr)
{
BootOptions->Timeout = _wtoi(TimeoutStr);
else
BootOptions->Timeout = 0;
}
}
return STATUS_SUCCESS; // FIXME: use Status; instead?
return STATUS_SUCCESS;
}
NTSTATUS
@ -1110,9 +1107,8 @@ SetBootStoreOptions(
L"DefaultOS", (PCWSTR)BootOptions->CurrentBootEntryKey);
RtlStringCchPrintfW(TimeoutStr, ARRAYSIZE(TimeoutStr), L"%d", BootOptions->Timeout);
IniInsertKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
NULL, INSERT_FIRST, // INSERT_LAST, // FIXME!! There is a bug in the INI parser where a given key can be inserted twice in the same section...
L"TimeOut", TimeoutStr);
IniAddKey(((PBOOT_STORE_INI_CONTEXT)BootStore)->OptionsIniSection,
L"TimeOut", TimeoutStr);
return STATUS_SUCCESS;
}
@ -1129,7 +1125,7 @@ FreeLdrEnumerateBootEntries(
NTSTATUS Status = STATUS_SUCCESS;
PINICACHEITERATOR Iterator;
PINI_SECTION OsIniSection;
PWCHAR SectionName, KeyData;
PCWSTR SectionName, KeyData;
UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) +
max(sizeof(NTOS_OPTIONS), sizeof(BOOT_SECTOR_OPTIONS))];
PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
@ -1140,7 +1136,7 @@ FreeLdrEnumerateBootEntries(
if (!Iterator) return STATUS_SUCCESS;
do
{
PWCHAR InstallName;
PCWSTR InstallName;
ULONG InstallNameLength;
/* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */
@ -1195,9 +1191,8 @@ FreeLdrEnumerateBootEntries(
if (!OsIniSection)
goto DoEnum;
/* Check for supported boot type "Windows2003" */
Status = IniGetKey(OsIniSection, L"BootType", &KeyData);
if (!NT_SUCCESS(Status) || !KeyData)
/* Check for supported boot type */
if (!IniGetKey(OsIniSection, L"BootType", &KeyData) || !KeyData)
{
/* Certainly not a ReactOS installation */
DPRINT1("No BootType value present!\n");
@ -1222,15 +1217,13 @@ FreeLdrEnumerateBootEntries(
/* Check its SystemPath */
Options->OsLoadPath = NULL;
Status = IniGetKey(OsIniSection, L"SystemPath", &KeyData);
if (NT_SUCCESS(Status))
if (IniGetKey(OsIniSection, L"SystemPath", &KeyData))
Options->OsLoadPath = KeyData;
// KeyData == SystemRoot;
/* Check the optional Options */
Options->OsLoadOptions = NULL;
Status = IniGetKey(OsIniSection, L"Options", &KeyData);
if (NT_SUCCESS(Status))
if (IniGetKey(OsIniSection, L"Options", &KeyData))
Options->OsLoadOptions = KeyData;
}
else
@ -1251,20 +1244,17 @@ FreeLdrEnumerateBootEntries(
/* Check its BootDrive */
Options->Drive = NULL;
Status = IniGetKey(OsIniSection, L"BootDrive", &KeyData);
if (NT_SUCCESS(Status))
if (IniGetKey(OsIniSection, L"BootDrive", &KeyData))
Options->Drive = KeyData;
/* Check its BootPartition */
Options->Partition = NULL;
Status = IniGetKey(OsIniSection, L"BootPartition", &KeyData);
if (NT_SUCCESS(Status))
if (IniGetKey(OsIniSection, L"BootPartition", &KeyData))
Options->Partition = KeyData;
/* Check its BootSector */
Options->BootSectorFileName = NULL;
Status = IniGetKey(OsIniSection, L"BootSectorFile", &KeyData);
if (NT_SUCCESS(Status))
if (IniGetKey(OsIniSection, L"BootSectorFile", &KeyData))
Options->BootSectorFileName = KeyData;
}
else
@ -1300,7 +1290,7 @@ NtLdrEnumerateBootEntries(
{
NTSTATUS Status = STATUS_SUCCESS;
PINICACHEITERATOR Iterator;
PWCHAR SectionName, KeyData;
PCWSTR SectionName, KeyData;
UCHAR xxBootEntry[FIELD_OFFSET(BOOT_STORE_ENTRY, OsOptions) + sizeof(NTOS_OPTIONS)];
PBOOT_STORE_ENTRY BootEntry = (PBOOT_STORE_ENTRY)&xxBootEntry;
PNTOS_OPTIONS Options = (PNTOS_OPTIONS)&BootEntry->OsOptions;
@ -1312,7 +1302,7 @@ NtLdrEnumerateBootEntries(
if (!Iterator) return STATUS_SUCCESS;
do
{
PWCHAR InstallName, OsOptions;
PCWSTR InstallName, OsOptions;
ULONG InstallNameLength, OsOptionsLength;
/* Poor-man quotes removal (improvement over bootsup.c:UpdateFreeLoaderIni) */

View file

@ -16,60 +16,38 @@
/* PRIVATE FUNCTIONS ********************************************************/
static
PINI_KEYWORD
static VOID
IniCacheFreeKey(
PINI_KEYWORD Key)
_In_ PINI_KEYWORD Key)
{
PINI_KEYWORD Next;
/* Unlink the key */
RemoveEntryList(&Key->ListEntry);
if (Key == NULL)
return NULL;
Next = Key->Next;
if (Key->Name != NULL)
{
/* Free its data */
if (Key->Name)
RtlFreeHeap(ProcessHeap, 0, Key->Name);
Key->Name = NULL;
}
if (Key->Data != NULL)
{
if (Key->Data)
RtlFreeHeap(ProcessHeap, 0, Key->Data);
Key->Data = NULL;
}
RtlFreeHeap(ProcessHeap, 0, Key);
return Next;
}
static
PINI_SECTION
static VOID
IniCacheFreeSection(
PINI_SECTION Section)
_In_ PINI_SECTION Section)
{
PINI_SECTION Next;
/* Unlink the section */
RemoveEntryList(&Section->ListEntry);
if (Section == NULL)
return NULL;
Next = Section->Next;
while (Section->FirstKey != NULL)
/* Free its data */
while (!IsListEmpty(&Section->KeyList))
{
Section->FirstKey = IniCacheFreeKey(Section->FirstKey);
PLIST_ENTRY Entry = RemoveHeadList(&Section->KeyList);
PINI_KEYWORD Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
IniCacheFreeKey(Key);
}
Section->LastKey = NULL;
if (Section->Name != NULL)
{
if (Section->Name)
RtlFreeHeap(ProcessHeap, 0, Section->Name);
Section->Name = NULL;
}
RtlFreeHeap(ProcessHeap, 0, Section);
return Next;
}
static
@ -78,10 +56,13 @@ IniCacheFindSection(
_In_ PINICACHE Cache,
_In_ PCWSTR Name)
{
PINI_SECTION Section;
PLIST_ENTRY Entry;
for (Section = Cache->FirstSection; Section; Section = Section->Next)
for (Entry = Cache->SectionList.Flink;
Entry != &Cache->SectionList;
Entry = Entry->Flink)
{
PINI_SECTION Section = CONTAINING_RECORD(Entry, INI_SECTION, ListEntry);
if (_wcsicmp(Section->Name, Name) == 0)
return Section;
}
@ -94,10 +75,13 @@ IniCacheFindKey(
_In_ PINI_SECTION Section,
_In_ PCWSTR Name)
{
PINI_KEYWORD Key;
PLIST_ENTRY Entry;
for (Key = Section->FirstKey; Key; Key = Key->Next)
for (Entry = Section->KeyList.Flink;
Entry != &Section->KeyList;
Entry = Entry->Flink)
{
PINI_KEYWORD Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
if (_wcsicmp(Key->Name, Name) == 0)
return Key;
}
@ -208,43 +192,33 @@ IniCacheAddKeyAorW(
Key->Data = DataU;
/* Insert the key into section */
if (Section->FirstKey == NULL)
if (IsListEmpty(&Section->KeyList))
{
Section->FirstKey = Key;
Section->LastKey = Key;
InsertHeadList(&Section->KeyList, &Key->ListEntry);
}
else if ((InsertionType == INSERT_FIRST) ||
((InsertionType == INSERT_BEFORE) &&
((AnchorKey == NULL) || (AnchorKey == Section->FirstKey))))
(!AnchorKey || (&AnchorKey->ListEntry == Section->KeyList.Flink))))
{
/* Insert at the head of the list */
Section->FirstKey->Prev = Key;
Key->Next = Section->FirstKey;
Section->FirstKey = Key;
InsertHeadList(&Section->KeyList, &Key->ListEntry);
}
else if ((InsertionType == INSERT_BEFORE) && (AnchorKey != NULL))
else if ((InsertionType == INSERT_BEFORE) && AnchorKey)
{
/* Insert before the anchor key */
Key->Next = AnchorKey;
Key->Prev = AnchorKey->Prev;
AnchorKey->Prev->Next = Key;
AnchorKey->Prev = Key;
InsertTailList(&AnchorKey->ListEntry, &Key->ListEntry);
}
else if ((InsertionType == INSERT_LAST) ||
((InsertionType == INSERT_AFTER) &&
((AnchorKey == NULL) || (AnchorKey == Section->LastKey))))
(!AnchorKey || (&AnchorKey->ListEntry == Section->KeyList.Blink))))
{
Section->LastKey->Next = Key;
Key->Prev = Section->LastKey;
Section->LastKey = Key;
/* Insert at the tail of the list */
InsertTailList(&Section->KeyList, &Key->ListEntry);
}
else if ((InsertionType == INSERT_AFTER) && (AnchorKey != NULL))
else if ((InsertionType == INSERT_AFTER) && AnchorKey)
{
/* Insert after the anchor key */
Key->Next = AnchorKey->Next;
Key->Prev = AnchorKey;
AnchorKey->Next->Prev = Key;
AnchorKey->Next = Key;
InsertHeadList(&AnchorKey->ListEntry, &Key->ListEntry);
}
return Key;
@ -305,19 +279,10 @@ IniCacheAddSectionAorW(
return NULL;
}
Section->Name = NameU;
InitializeListHead(&Section->KeyList);
/* Append the section */
if (Cache->FirstSection == NULL)
{
Cache->FirstSection = Section;
Cache->LastSection = Section;
}
else
{
Cache->LastSection->Next = Section;
Section->Prev = Cache->LastSection;
Cache->LastSection = Section;
}
InsertTailList(&Cache->SectionList, &Section->ListEntry);
return Section;
}
@ -540,14 +505,9 @@ IniCacheLoadFromMemory(
ULONG KeyValueSize;
/* Allocate inicache header */
*Cache = (PINICACHE)RtlAllocateHeap(ProcessHeap,
HEAP_ZERO_MEMORY,
sizeof(INICACHE));
if (*Cache == NULL)
{
DPRINT("RtlAllocateHeap() failed\n");
*Cache = IniCacheCreate();
if (!*Cache)
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Parse ini file */
Section = NULL;
@ -737,16 +697,17 @@ IniCacheLoad(
VOID
IniCacheDestroy(
PINICACHE Cache)
_In_ PINICACHE Cache)
{
if (Cache == NULL)
if (!Cache)
return;
while (Cache->FirstSection != NULL)
while (!IsListEmpty(&Cache->SectionList))
{
Cache->FirstSection = IniCacheFreeSection(Cache->FirstSection);
PLIST_ENTRY Entry = RemoveHeadList(&Cache->SectionList);
PINI_SECTION Section = CONTAINING_RECORD(Entry, INI_SECTION, ListEntry);
IniCacheFreeSection(Section);
}
Cache->LastSection = NULL;
RtlFreeHeap(ProcessHeap, 0, Cache);
}
@ -765,110 +726,110 @@ IniGetSection(
return IniCacheFindSection(Cache, Name);
}
NTSTATUS
PINI_KEYWORD
IniGetKey(
PINI_SECTION Section,
PWCHAR KeyName,
PWCHAR *KeyData)
_In_ PINI_SECTION Section,
_In_ PCWSTR KeyName,
_Out_ PCWSTR* KeyData)
{
PINI_KEYWORD Key;
if (Section == NULL || KeyName == NULL || KeyData == NULL)
if (!Section || !KeyName || !KeyData)
{
DPRINT("Invalid parameter\n");
return STATUS_INVALID_PARAMETER;
return NULL;
}
*KeyData = NULL;
Key = IniCacheFindKey(Section, KeyName);
if (Key == NULL)
{
return STATUS_INVALID_PARAMETER;
}
if (!Key)
return NULL;
*KeyData = Key->Data;
return STATUS_SUCCESS;
return Key;
}
PINICACHEITERATOR
IniFindFirstValue(
PINI_SECTION Section,
PWCHAR *KeyName,
PWCHAR *KeyData)
_In_ PINI_SECTION Section,
_Out_ PCWSTR* KeyName,
_Out_ PCWSTR* KeyData)
{
PINICACHEITERATOR Iterator;
PLIST_ENTRY Entry;
PINI_KEYWORD Key;
if (Section == NULL || KeyName == NULL || KeyData == NULL)
if (!Section || !KeyName || !KeyData)
{
DPRINT("Invalid parameter\n");
return NULL;
}
Key = Section->FirstKey;
if (Key == NULL)
Entry = Section->KeyList.Flink;
if (Entry == &Section->KeyList)
{
DPRINT("Invalid parameter\n");
return NULL;
}
*KeyName = Key->Name;
*KeyData = Key->Data;
Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
Iterator = (PINICACHEITERATOR)RtlAllocateHeap(ProcessHeap,
0,
sizeof(INICACHEITERATOR));
if (Iterator == NULL)
if (!Iterator)
{
DPRINT("RtlAllocateHeap() failed\n");
return NULL;
}
Iterator->Section = Section;
Iterator->Key = Key;
*KeyName = Key->Name;
*KeyData = Key->Data;
return Iterator;
}
BOOLEAN
IniFindNextValue(
PINICACHEITERATOR Iterator,
PWCHAR *KeyName,
PWCHAR *KeyData)
_In_ PINICACHEITERATOR Iterator,
_Out_ PCWSTR* KeyName,
_Out_ PCWSTR* KeyData)
{
PLIST_ENTRY Entry;
PINI_KEYWORD Key;
if (Iterator == NULL || KeyName == NULL || KeyData == NULL)
if (!Iterator || !KeyName || !KeyData)
{
DPRINT("Invalid parameter\n");
return FALSE;
}
Key = Iterator->Key->Next;
if (Key == NULL)
Entry = Iterator->Key->ListEntry.Flink;
if (Entry == &Iterator->Section->KeyList)
{
DPRINT("No more entries\n");
return FALSE;
}
Key = CONTAINING_RECORD(Entry, INI_KEYWORD, ListEntry);
Iterator->Key = Key;
*KeyName = Key->Name;
*KeyData = Key->Data;
Iterator->Key = Key;
return TRUE;
}
VOID
IniFindClose(
PINICACHEITERATOR Iterator)
_In_ PINICACHEITERATOR Iterator)
{
if (Iterator == NULL)
if (!Iterator)
return;
RtlFreeHeap(ProcessHeap, 0, Iterator);
}
@ -886,6 +847,18 @@ IniAddSection(
return IniCacheAddSectionAorW(Cache, Name, wcslen(Name), TRUE);
}
VOID
IniRemoveSection(
_In_ PINI_SECTION Section)
{
if (!Section)
{
DPRINT("Invalid parameter\n");
return;
}
IniCacheFreeSection(Section);
}
PINI_KEYWORD
IniInsertKey(
_In_ PINI_SECTION Section,
@ -915,6 +888,32 @@ IniAddKey(
return IniInsertKey(Section, NULL, INSERT_LAST, Name, Data);
}
VOID
IniRemoveKeyByName(
_In_ PINI_SECTION Section,
_In_ PCWSTR KeyName)
{
PINI_KEYWORD Key;
UNREFERENCED_PARAMETER(Section);
Key = IniCacheFindKey(Section, KeyName);
if (Key)
IniCacheFreeKey(Key);
}
VOID
IniRemoveKey(
_In_ PINI_SECTION Section,
_In_ PINI_KEYWORD Key)
{
UNREFERENCED_PARAMETER(Section);
if (!Key)
{
DPRINT("Invalid parameter\n");
return;
}
IniCacheFreeKey(Key);
}
PINICACHE
IniCacheCreate(VOID)
@ -925,11 +924,12 @@ IniCacheCreate(VOID)
Cache = (PINICACHE)RtlAllocateHeap(ProcessHeap,
HEAP_ZERO_MEMORY,
sizeof(INICACHE));
if (Cache == NULL)
if (!Cache)
{
DPRINT("RtlAllocateHeap() failed\n");
return NULL;
}
InitializeListHead(&Cache->SectionList);
return Cache;
}
@ -940,6 +940,7 @@ IniCacheSaveByHandle(
HANDLE FileHandle)
{
NTSTATUS Status;
PLIST_ENTRY Entry1, Entry2;
PINI_SECTION Section;
PINI_KEYWORD Key;
ULONG BufferSize;
@ -951,23 +952,25 @@ IniCacheSaveByHandle(
/* Calculate required buffer size */
BufferSize = 0;
Section = Cache->FirstSection;
while (Section != NULL)
Entry1 = Cache->SectionList.Flink;
while (Entry1 != &Cache->SectionList)
{
Section = CONTAINING_RECORD(Entry1, INI_SECTION, ListEntry);
BufferSize += (Section->Name ? wcslen(Section->Name) : 0)
+ 4; /* "[]\r\n" */
Key = Section->FirstKey;
while (Key != NULL)
Entry2 = Section->KeyList.Flink;
while (Entry2 != &Section->KeyList)
{
Key = CONTAINING_RECORD(Entry2, INI_KEYWORD, ListEntry);
BufferSize += wcslen(Key->Name)
+ (Key->Data ? wcslen(Key->Data) : 0)
+ 3; /* "=\r\n" */
Key = Key->Next;
Entry2 = Entry2->Flink;
}
Section = Section->Next;
if (Section != NULL)
Entry1 = Entry1->Flink;
if (Entry1 != &Cache->SectionList)
BufferSize += 2; /* Extra "\r\n" at end of each section */
}
@ -985,22 +988,24 @@ IniCacheSaveByHandle(
/* Fill file buffer */
Ptr = Buffer;
Section = Cache->FirstSection;
while (Section != NULL)
Entry1 = Cache->SectionList.Flink;
while (Entry1 != &Cache->SectionList)
{
Section = CONTAINING_RECORD(Entry1, INI_SECTION, ListEntry);
Len = sprintf(Ptr, "[%S]\r\n", Section->Name);
Ptr += Len;
Key = Section->FirstKey;
while (Key != NULL)
Entry2 = Section->KeyList.Flink;
while (Entry2 != &Section->KeyList)
{
Key = CONTAINING_RECORD(Entry2, INI_KEYWORD, ListEntry);
Len = sprintf(Ptr, "%S=%S\r\n", Key->Name, Key->Data);
Ptr += Len;
Key = Key->Next;
Entry2 = Entry2->Flink;
}
Section = Section->Next;
if (Section != NULL)
Entry1 = Entry1->Flink;
if (Entry1 != &Cache->SectionList)
{
Len = sprintf(Ptr, "\r\n");
Ptr += Len;

View file

@ -11,26 +11,19 @@ typedef struct _INI_KEYWORD
{
PWSTR Name;
PWSTR Data;
struct _INI_KEYWORD *Next;
struct _INI_KEYWORD *Prev;
LIST_ENTRY ListEntry;
} INI_KEYWORD, *PINI_KEYWORD;
typedef struct _INI_SECTION
{
PWSTR Name;
PINI_KEYWORD FirstKey;
PINI_KEYWORD LastKey;
struct _INI_SECTION *Next;
struct _INI_SECTION *Prev;
LIST_ENTRY KeyList;
LIST_ENTRY ListEntry;
} INI_SECTION, *PINI_SECTION;
typedef struct _INICACHE
{
PINI_SECTION FirstSection;
PINI_SECTION LastSection;
LIST_ENTRY SectionList;
} INICACHE, *PINICACHE;
typedef struct _PINICACHEITERATOR
@ -70,40 +63,44 @@ IniCacheLoad(
VOID
IniCacheDestroy(
PINICACHE Cache);
_In_ PINICACHE Cache);
PINI_SECTION
IniGetSection(
_In_ PINICACHE Cache,
_In_ PCWSTR Name);
NTSTATUS
PINI_KEYWORD
IniGetKey(
PINI_SECTION Section,
PWCHAR KeyName,
PWCHAR *KeyData);
_In_ PINI_SECTION Section,
_In_ PCWSTR KeyName,
_Out_ PCWSTR* KeyData);
PINICACHEITERATOR
IniFindFirstValue(
PINI_SECTION Section,
PWCHAR *KeyName,
PWCHAR *KeyData);
_In_ PINI_SECTION Section,
_Out_ PCWSTR* KeyName,
_Out_ PCWSTR* KeyData);
BOOLEAN
IniFindNextValue(
PINICACHEITERATOR Iterator,
PWCHAR *KeyName,
PWCHAR *KeyData);
_In_ PINICACHEITERATOR Iterator,
_Out_ PCWSTR* KeyName,
_Out_ PCWSTR* KeyData);
VOID
IniFindClose(
PINICACHEITERATOR Iterator);
_In_ PINICACHEITERATOR Iterator);
PINI_SECTION
IniAddSection(
_In_ PINICACHE Cache,
_In_ PCWSTR Name);
VOID
IniRemoveSection(
_In_ PINI_SECTION Section);
PINI_KEYWORD
IniInsertKey(
_In_ PINI_SECTION Section,
@ -118,6 +115,16 @@ IniAddKey(
_In_ PCWSTR Name,
_In_ PCWSTR Data);
VOID
IniRemoveKeyByName(
_In_ PINI_SECTION Section,
_In_ PCWSTR KeyName);
VOID
IniRemoveKey(
_In_ PINI_SECTION Section,
_In_ PINI_KEYWORD Key);
PINICACHE
IniCacheCreate(VOID);