CatchPreferences (Windows)

Share your advanced PureBasic knowledge/code with the community.
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

CatchPreferences (Windows)

Post by breeze4me »

CatchPreferences function using API hooks.
Supports ASCII, UTF8, and Unicode with/without BOM.

DO NOT use WritePreferenceXXX functions. It is not guaranteed to work.

Windows only.

Tested with PB 5.73 and 6.10 b1 x64 x86.

Code: Select all

; API_HookEngine Module by Peyman
; https://www.purebasic.fr/english/viewtopic.php?t=64746
DeclareModule API_HookEngine_MOD
  Declare Hook(*OldFunctionAddress, *NewFunctionAddress)
  Declare UnHook(*hook_ptr)
EndDeclareModule

Module API_HookEngine_MOD
  EnableExplicit
  
  Structure opcode
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      mov.u
    CompilerElse
      mov.a
    CompilerEndIf
    addr.i
    push.a
    ret.a
  EndStructure
  
  Structure hookstruct
    addr.i
    hook.opcode
    orig.a[SizeOf(opcode)]
  EndStructure
  
  Procedure Hook(*OldFunctionAddress, *NewFunctionAddress)
    Protected *hook_ptr.hookstruct
    
    If *OldFunctionAddress = 0
      ProcedureReturn 0
    EndIf
    
    *hook_ptr = AllocateMemory(SizeOf(hookstruct))
    *hook_ptr\addr = *OldFunctionAddress
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      *hook_ptr\hook\mov = $B848
    CompilerElse
      *hook_ptr\hook\mov = $B8
    CompilerEndIf
    *hook_ptr\hook\addr = *NewFunctionAddress
    *hook_ptr\hook\push = $50
    *hook_ptr\hook\ret = $C3
    
    CopyMemory(*OldFunctionAddress, @*hook_ptr\orig, SizeOf(opcode))
    If WriteProcessMemory_(GetCurrentProcess_(), *OldFunctionAddress, @*hook_ptr\hook, SizeOf(opcode), 0) = 0
      FreeMemory(*hook_ptr)
      ProcedureReturn 0
    Else
      ProcedureReturn *hook_ptr
    EndIf
  EndProcedure
  
  Procedure UnHook(*hook_ptr.hookstruct)
    Protected retValue
    
    If *hook_ptr
      If *hook_ptr\addr
        If WriteProcessMemory_(GetCurrentProcess_(), *hook_ptr\addr, @*hook_ptr\orig, SizeOf(opcode), 0)
          retValue = *hook_ptr\addr
          FreeMemory(*hook_ptr)
          ProcedureReturn retValue
        EndIf
      EndIf
    EndIf
    
    ProcedureReturn 0
  EndProcedure
EndModule


Structure STRUC_MemStringData
  BufferSize.i
  *pBuffer
EndStructure

Procedure My_CreateFileW(lpFileName.s, dwDesiredAccess.l, dwShareMode.l, lpSecurityAttributes, dwCreationDisposition.l, dwFlagsAndAttributes.l, hTemplateFile)
  Protected i_Result, s_MemStringData.s, *MemStringData.STRUC_MemStringData
  
  If Left(lpFileName, 3) = "***"
    s_MemStringData = PeekS(@lpFileName + SizeOf(Character) * 3)
    If s_MemStringData
      *MemStringData = Val(s_MemStringData)
      If *MemStringData
        If *MemStringData\pBuffer And *MemStringData\BufferSize > 0
          i_Result = *MemStringData
        EndIf
      EndIf
    EndIf
  EndIf
  
  ProcedureReturn i_Result
EndProcedure

Procedure My_ReadFile(*hFile.STRUC_MemStringData, *lpBuffer, nNumberOfBytesToRead.l, *lpNumberOfBytesRead.Long, *lpOverlapped)
  Protected i_Result, UTF8_BOM = $BFBBEF
  
  If *hFile And *lpBuffer
    If nNumberOfBytesToRead = 3 And *hFile\BufferSize >= 3
      CopyMemory(@UTF8_BOM, *lpBuffer, 3)
      If *lpNumberOfBytesRead
        *lpNumberOfBytesRead\l = 3
      EndIf
      i_Result = 1
      
    ElseIf nNumberOfBytesToRead > 3 And nNumberOfBytesToRead = *hFile\BufferSize
      CopyMemory(*hFile\pBuffer, *lpBuffer, nNumberOfBytesToRead)
      If *lpNumberOfBytesRead
        *lpNumberOfBytesRead\l = nNumberOfBytesToRead
      EndIf
      i_Result = 1
      
    Else
      Debug "Something wrong?"
      
      If nNumberOfBytesToRead > *hFile\BufferSize
        nNumberOfBytesToRead = *hFile\BufferSize
      EndIf
      
      CopyMemory(*hFile\pBuffer, *lpBuffer, nNumberOfBytesToRead)
      If *lpNumberOfBytesRead
        *lpNumberOfBytesRead\l = nNumberOfBytesToRead
      EndIf
      i_Result = 1
    EndIf
  EndIf
  
  ProcedureReturn i_Result
EndProcedure

Procedure.l My_GetFileSize(*hFile.STRUC_MemStringData, *lpFileSizeHigh)
  Protected i_Size
  If *hFile : i_Size = *hFile\BufferSize + 3 : EndIf
  ProcedureReturn i_Size
EndProcedure

; BufferSize: The byte length of the buffer.
; StringFormat: 0, #PB_UTF8, #PB_Unicode, #PB_Ascii
; Flags: #PB_Preference_NoSpace, #PB_Preference_GroupSeparator (Flags of the OpenPreferences function)
Procedure CatchPreferences(*Buffer, BufferSize, StringFormat = 0, Flags = 0)
  Protected i_Result
  Protected UTF8_BOM.l = $BFBBEF, UTF16LE_BOM.u = $FEFF
  Protected *MemStringData.STRUC_MemStringData
  Protected *s_UTF8, s_Contents.s, s_Name.s, i_Lib
  Protected *CreateFileW, *ReadFile, *GetFileSize, *NewCreateFileW, *NewReadFile, *NewGetFileSize
  
  If Not (*Buffer And BufferSize > 0 And (StringFormat = 0 Or StringFormat = #PB_UTF8 Or StringFormat = #PB_Unicode Or StringFormat = #PB_Ascii))
    ProcedureReturn 0
  EndIf
  
  *MemStringData = AllocateMemory(SizeOf(STRUC_MemStringData))
  If *MemStringData = 0 : ProcedureReturn 0 : EndIf
  
  ;Search BOM.
  If BufferSize >= 2
    If PeekU(*Buffer) = UTF16LE_BOM
      *Buffer + 2
      BufferSize - 2
      StringFormat = #PB_Unicode
    EndIf
  EndIf
  
  If BufferSize >= 3
    If CompareMemory(*Buffer, @UTF8_BOM, 3)
      *Buffer + 3
      BufferSize - 3
      StringFormat = #PB_UTF8
      
      *MemStringData\pBuffer = *Buffer
      *MemStringData\BufferSize = BufferSize
    EndIf
  EndIf
  
  Repeat
    If BufferSize <= 0 : Break : EndIf
    
    Select StringFormat
      Case 0, #PB_Ascii
        s_Contents = PeekS(*Buffer, BufferSize, #PB_Ascii)
        
      Case #PB_Unicode
        s_Contents = PeekS(*Buffer, BufferSize / SizeOf(Character), #PB_Unicode)
        
      Case #PB_UTF8
        If *MemStringData\pBuffer = 0
          *MemStringData\pBuffer = *Buffer
          *MemStringData\BufferSize = BufferSize
        EndIf
    EndSelect
    
    If s_Contents
      *s_UTF8 = UTF8(s_Contents)
      If *s_UTF8
        *MemStringData\pBuffer = *s_UTF8
        *MemStringData\BufferSize = MemorySize(*s_UTF8) - 1
      EndIf
    EndIf
    
    If *MemStringData\pBuffer And *MemStringData\BufferSize > 0
      i_Lib = OpenLibrary(#PB_Any, "kernel32.dll")
      If i_Lib = 0 : Break : EndIf
      
      *CreateFileW = GetFunction(i_Lib, "CreateFileW")
      *ReadFile = GetFunction(i_Lib, "ReadFile")
      *GetFileSize = GetFunction(i_Lib, "GetFileSize")
      
      If *CreateFileW And *ReadFile And *GetFileSize
        *NewCreateFileW = API_HookEngine_MOD::Hook(*CreateFileW, @My_CreateFileW())
        *NewReadFile = API_HookEngine_MOD::Hook(*ReadFile, @My_ReadFile())
        *NewGetFileSize = API_HookEngine_MOD::Hook(*GetFileSize, @My_GetFileSize())
      EndIf
      
      If *NewCreateFileW And *NewReadFile And *NewGetFileSize
        
        s_Name = "***" + Str(*MemStringData)
        
        If Flags
          i_Result = OpenPreferences(s_Name, Flags)
        Else
          i_Result = OpenPreferences(s_Name)
        EndIf
        
        *CreateFileW = API_HookEngine_MOD::UnHook(*NewCreateFileW)
        *ReadFile = API_HookEngine_MOD::UnHook(*NewReadFile)
        *GetFileSize = API_HookEngine_MOD::UnHook(*NewGetFileSize)
        
        If Not (*CreateFileW And *ReadFile And *GetFileSize)
          i_Result = 0
        EndIf
      EndIf
    EndIf
    
    Break
  ForEver
  
  If i_Lib : CloseLibrary(i_Lib) : EndIf
  If *s_UTF8 : FreeMemory(*s_UTF8) : EndIf
  FreeMemory(*MemStringData)
  
  ProcedureReturn i_Result
EndProcedure

Code: Select all

; Example 1:

If CatchPreferences(?start_asc, ?end_asc - ?start_asc, #PB_Ascii)
  
  If ExaminePreferenceGroups()
    While NextPreferenceGroup()
      Debug "Group: " + PreferenceGroupName()
      
      If ExaminePreferenceKeys()
        While NextPreferenceKey()
          kn.s = PreferenceKeyName()
          kv.s = PreferenceKeyValue()
          If kn
            Debug "        " + kn + " = " + kv
          EndIf
        Wend
      EndIf
    Wend
  EndIf
  
;   If PreferenceGroup("~~~~~")
;     Debug ReadPreferenceString("~~~~~", "")
;   EndIf
  
  ClosePreferences()
EndIf

DataSection
  start_asc:
  IncludeBinary "your pref file ~~~~~~~~~~"
  end_asc:
EndDataSection

Code: Select all

; Example 2:

s.s = ~";test pref.\n" +
      ~"[Global] \n" +
      ~"value1 = 100\n" +
      ~"value2 = 200\n" +
      ~"value3 = -300\n" +
      ~"value4 = -400\n" +
      ~"\n" +
      ~"[Data]\n" +
      ~"old1 = test1\n" +
      ~"old2 = test2\n" +
      ~"old3 = 13.5\n"
sz = StringByteLength(s)

If CatchPreferences(@s, sz, #PB_Unicode)
  
  If ExaminePreferenceGroups()
    While NextPreferenceGroup()
      Debug "Group: " + PreferenceGroupName()
      
      If ExaminePreferenceKeys()
        While NextPreferenceKey()
          kn.s = PreferenceKeyName()
          kv.s = PreferenceKeyValue()
          If kn
            Debug "        " + kn + " = " + kv
          EndIf
        Wend
      EndIf
    Wend
  EndIf
  
  Debug "----------------------------------"
  If PreferenceGroup("Global")
    Debug ReadPreferenceQuad("value3", 0)
  EndIf
  
  If PreferenceGroup("Data")
    Debug ReadPreferenceFloat("old3", 0)
  EndIf
  
  ClosePreferences()
EndIf
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: CatchPreferences (Windows)

Post by Kwai chang caine »

Hello Breeze4me
Apparently your two codes works here :wink:
Group: Global
value1 = 100
value2 = 200
value3 = -300
value4 = -400
Group: Data
old1 = test1
old2 = test2
old3 = 13.5
----------------------------------
-300
13.5
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply