[Done]error: Invalid memory access

TailBite specific forum

Moderators: gnozal, ABBKlaus, lexvictory

morosh
Enthusiast
Enthusiast
Posts: 293
Joined: Wed Aug 03, 2011 4:52 am
Location: Beirut, Lebanon

[Done]error: Invalid memory access

Post by morosh »

Hello:
I asked in "Coding questions" about new look openfilerequester, and I get answered the following, which is working very well:

Code: Select all

; skywalk
; Since the built-in PureBasic OFN Requester does not allow Details View!
; Fill the OFN structure and use a OFNHook Procedure.
; Portions modified from Sparkie's code:
; http://www.purebasic.fr/english/viewtopic.php?p=133878#p133878
EnableExplicit
#sNull$ = "-999"
Structure ScanPBDataTypes
  ; These take up no memory and are allowed to grow without redim
  ; when the structure is pointed at a memory buffer.
  ; ex. *buf\d[i] ; 'i' being incremented in a loop
  ; Essentially, this is 1 big StructureUnion.
  b.b[0]  ; byte
  w.w[0]  ; word
  l.l[0]  ; long
  i.i[0]  ; integer
  q.q[0]  ; quad
  f.f[0]  ; float
  d.d[0]  ; double
  a.a[0]  ; ascii
  c.c[0]  ; character
  u.u[0]  ; unicode
  ;s.s{Use 1-MAX String Length expected 255}  ; string
  s.s[0]  ; This supports 1 String scan. *p\s = @x$, *p\s[1] does not work.
EndStructure

Macro FL_FileExists(Fname)
  ;  0 = File or Folder does not exist
  ;  1 = File Exists
  ; -1 = Folder Exists
  ; Since FileSize() returns:
  ;       -1 = File Not found.
  ;       -2 = File is a directory.
  ;        0 = Empty File, > 1 = File Exists
  ;        FileSize() respects '*' wildcards and reports results accordingly.
  FileSize(Fname) + 1
EndMacro

Procedure.i SF_CharReplace(*p.ScanPBDataTypes, StrFrom$="|", StrTo$=#NULL$)
  ; REV:  111227, skywalk
  ; SYNTAX:   nNulls = SF_CharReplace(@x$,"|",#Null$,0)
  ; DEFAULT:  Edit a string in memory to embed null char's = Chr(0)
  ; RETURN:   # of char replacements made.
  ; If StrFrom$=#Null$, then buffer end is assumed to be double null position.
  ; Accepts multiple chars, but only works with the 1st char.
  ; Works with unicode or ascii.
  Protected.i i, msLen, nChars
  Protected.i CharFrom = Asc(StrFrom$)
  Protected.i CharTo = Asc(StrTo$)
  ; Determine msLen
  If CharFrom = #Null   ; Search memory for double nulls = end of string buffer
    While msLen < 64000
      If *p\c[msLen] = 0 And *p\c[msLen+1] = 0
        Break
      EndIf
      msLen + 1
    Wend
    msLen - 1  ; Ignore the final #Null later in code
  Else
    msLen = MemoryStringLength(*p) - 1  ; Get Len in characters, not bytes.
   ; StringByteLength(PeekS(*p))        ; Returns nBytes and depends on ascii/unicode switch!
  EndIf
  For i = 0 To msLen   ; Since walking through String by Character, no need to count bytes/char.
    If *p\c[i] = CharFrom
      *p\c[i] = CharTo
      nChars + 1
    EndIf
  Next i
  ;ShowMemoryViewer(@*p,msLen+32)
  ProcedureReturn nChars
EndProcedure

Procedure.i Split(s$, Delm$, Array sA.s(1), trimsp.i=0, UseCase.i=#PB_String_CaseSensitive)
  ; REV:  100405, skywalk
  ;       modified from Trond PB forum code
  ;       www.purebasic.fr/english/viewtopic.php?f=13&t=26738
  ; REV:  111227, skywalk
  ;       removed ReSize parameter. sA() adjusts to size required = nStrings-1
  ;       added multi-char delimiter, case sensitivity, and unicode.
  ;       UseCase = 0-#PB_String_CaseSensitive or 1-#PB_String_NoCase
  ; Return: Count of elements or nStrings found.
  ;         If none, then entire string is assigned to sA(0)
  ; s$ = normal null terminated string
  ; VB6 syntax -> sA() = Split(expression[, delimiter[, count[, compare]]])
  ; SizeOf(Character) is converted to a constant by compiler so no speed hit for multiple use
  Protected.i i, nStrings
  Protected.i sLen = Len(s$)
  Protected.i DelmLen = Len(Delm$)
  Protected *s.Character = @s$  ; Character Structure points to s$ contents, not its address
  Protected.i lastp = *s
  Protected Delmc.c
  If DelmLen = 0  ; "" or empty
    ReDim sA(0)
    sA(0) = s$
    nStrings = 1
  ElseIf sLen = 0 ; s$ = ""
    ReDim sA(0)
    sA(0) = #NULL$
    nStrings = 0
  Else            ; OK to Split
    Dim sA(sLen/DelmLen+1)    ; Start array at worst case estimate
    If UseCase = #PB_String_CaseSensitive And (DelmLen = 1 Or Delm$ = #CRLF$) ; use faster single character split routine
      If DelmLen = 1
        Delmc = Asc(Delm$)
      Else            ; Delm$ = #CRLF$
        Delmc = #LF   ; use the 2nd char, then trim trailing #CR$ from elements
      EndIf
      While *s\c      ; > 0 means valid Character, = 0 means String terminated.
        If *s\c <> Delmc
          *s + SizeOf(Character)
        Else
          *s\c = 0    ; Terminate string with Chr(0) here
          sA(nStrings) = PeekS(lastp)
          lastp = *s + SizeOf(Character)  ; remember last pointer
          nStrings + 1
          *s + (DelmLen) * SizeOf(Character)
        EndIf
      Wend
    Else              ; use slower multi-char split routine
      While *s\c      ; > 0 means valid Character, = 0 means String terminated.
        If CompareMemoryString(*s, @Delm$, UseCase, DelmLen) ; <> 0 means different memory
          *s + SizeOf(Character)
        Else                                                 ; = 0  means identical memory
          *s\c = 0    ; Terminate string with Chr(0) here
          sA(nStrings) = PeekS(lastp)
          lastp = *s + (DelmLen) * SizeOf(Character)  ; remember last pointer
          nStrings + 1
          *s + (DelmLen) * SizeOf(Character)
        EndIf
      Wend
    EndIf
    If *s <> lastp    ; reached last valid element
      sA(nStrings) = PeekS(lastp)
    EndIf
    ReDim sA(nStrings)
    If trimsp
      For i = 0 To nStrings
        sa(i) = Trim(sa(i))
      Next i
      If trimsp = 9
        For i = 0 To nStrings
          sa(i) = Trim(sa(i), #TAB$)
        Next i
      EndIf
    EndIf
    If delm$ = #CRLF$
      For i = 0 To nStrings
        sa(i) = RTrim(sa(i),#CR$)
      Next i
    EndIf
    nStrings + 1      ; Catch #Null$ due to dangling Delm$, "1,2,"
  EndIf
  ProcedureReturn nStrings
EndProcedure
;-{ GUI FILE STUFF
#OSVEX_LENGTH           = 88  ; If > Win2K = 88 Else = 76
#CDN_INITDONE           = #CDN_FIRST - 0
#CDN_SELCHANGE          = #CDN_FIRST - 1
#CDN_FOLDERCHANGE       = #CDN_FIRST - 2
#OFN_VIEW_REPORT        = $702C
#OFN_VIEW_LIST          = $702B
#OFN_VIEW_LARGEICON     = $7029
#OFN_VIEW_SMALLICON     = $702A
#OFN_VIEW_THUMBNAIL     = $702D
#OFN_VIEW_THUMBNAIL_2K  = $7031
#OFN_VIEW_TILE          = $702E
#OFN_ENABLESIZING       = $800000
#OFS_FILE_OPEN_FLAGS    = #OFN_EXPLORER | #OFN_LONGNAMES | #OFN_CREATEPROMPT
#OFS_FILE_SAVE_FLAGS    = #OFN_EXPLORER | #OFN_LONGNAMES | #OFN_OVERWRITEPROMPT | #OFN_HIDEREADONLY
Structure OFNdata
  Title$
  DefFilePath$
  Pattern$
  PatternPos.i
  MultiSelect.i
  Array Files$(0)
  nFiles.i
EndStructure
Structure OFNgui Extends OFNdata  ; Used by OFN Dialog gui
  guiX.i
  guiY.i
  guiWd.i
  guiHt.i
EndStructure
Global OFNreq.OFNgui, OFNres.OFNdata

; Custom message for move/resize OpenFileRequester
Global gui_OFN_MoveDlg = RegisterWindowMessage_(@"gui_OFN_MoveDlg")
Procedure gui_OFN_HookProc(hW, msg, wP, lP)
  Protected.i hParent, hLV, ri
  Select msg
  Case #WM_INITDIALOG
    ;                                 wd,           ht
    PostMessage_(hW, gui_OFN_MoveDlg, OFNreq\guiWd, OFNreq\guiHt)
  Case gui_OFN_MoveDlg
    ; Reposition @ resize dialog window
    hParent = GetParent_(hW)
    MoveWindow_(hParent, OFNreq\guiX, OFNreq\guiY, wP, lP, 1)
  Case #WM_NOTIFY
    ; hW is the handle to the dialog
    ; hParent is the handle to the common control
    ; hLV is the handle to the listview itself
    hParent = GetParent_(hW)
    hLV = FindWindowEx_(hParent, 0, "SHELLDLL_DefView", #NULL$)
    If hLV
      ri = SendMessage_(hLV, #WM_COMMAND, #OFN_VIEW_REPORT, #Null)
    EndIf
  EndSelect
  ProcedureReturn 0
EndProcedure

Procedure.i gui_PathRequester(*pReq.OFNgui, *pRes.OFNdata)
  ; REV:  120322, skywalk
  ; Process user response to OpenPathRequester Directory Selection(s)
  ; Results go in structure OFNres
  ; If MultiSelect=1,  then OFNres\File$(nFiles-1) contains the list of directories
  ;               =0,  then OFNres\File$(0)        contains 1 selected directory
  ; SYNTAX: nPaths = gui_PathRequester(hW, @myOFNreq, @myOFNres)
  ;p$ = PathRequester("Select path(s) to Pack...", "C:\") ; ONLY SUPPORTS 1 PATH SELECTION
  Protected.i i, k, wID, xlID, buID, wW, wH
  *pRes\nFiles = 0
  wID = OpenWindow(#PB_Any, *pReq\guiX, *pReq\guiY, *pReq\guiWd, *pReq\guiHt, *pReq\Title$, #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget)
  If wID
    StickyWindow(wID, 1)
    xlID = ExplorerListGadget(#PB_Any, 10, 10, *pReq\guiWd-20, *pReq\guiHt-50, *pReq\DefFilePath$, #PB_Explorer_NoFiles|#PB_Explorer_MultiSelect|#PB_Explorer_GridLines|#PB_Explorer_FullRowSelect)
    buID = ButtonGadget(#PB_Any, *pReq\guiWd/2-40, *pReq\guiHt-GadgetHeight(xlID)-10, 80, 30, "Done")
    Repeat
      Select WaitWindowEvent()
      Case #PB_Event_Gadget
        If EventGadget() = buID
          *pRes\nFiles = CountGadgetItems(xlID)
          ReDim *pRes\Files$(*pRes\nFiles-1)
          For i = 0 To *pRes\nFiles - 1
            If GetGadgetItemState(xlID, i) & #PB_Explorer_Selected
              *pRes\Files$(k) = GetGadgetText(xlID) + GetGadgetItemText(xlID, i)
              k + 1
            EndIf
          Next
          *pRes\nFiles = k
          If k
            ReDim *pRes\Files$(k-1)
            *pRes\DefFilePath$ = GetPathPart(*pRes\Files$(0))
          Else
            ReDim *pRes\Files$(0)
            *pRes\DefFilePath$ = *pReq\DefFilePath$
          EndIf
          Break
        EndIf
      Case #PB_Event_SizeWindow
        wW = WindowWidth(wID)
        wH = WindowHeight(wID)
        ResizeGadget(xlID, #PB_Ignore, #PB_Ignore, wW-20, wH-50)
        ResizeGadget(buID, wW/2-40, GadgetY(xlID)+GadgetHeight(xlID)+5, #PB_Ignore, #PB_Ignore)
      Case #PB_Event_CloseWindow
        Break
      EndSelect
    ForEver
  EndIf
  StickyWindow(wID, 0)
  CloseWindow(wID)
  ProcedureReturn *pRes\nFiles
EndProcedure

Procedure.i gui_FileRequester(hW.i, *pReq.OFNgui, *pRes.OFNdata)
  ; REV:  120106, skywalk
  ; Process user response to OpenFileRequester File Selection(s)
  ; Results go in structure OFNres
  ; If MultiSelect=1,  then OFNres\File$(nFiles-1) contains the list of files
  ;               =0,  then OFNres\File$(0)        contains the 1 selected file
  ; SYNTAX: nFiles = gui_FileRequester(hW, @myOFNreq, @myOFNres)
  Protected.i i, j, *selectedFile, bufferSize
  Protected.s folder$, nextFile$
  ; For Filter to function properly, replace "|" with null Chr(0) directly in memory
  ; Filter must end with 2 null chars
  If Right(*pReq\Pattern$,2) <> "||"
    *pReq\Pattern$ + "||"
  EndIf
  SF_CharReplace(@*pReq\Pattern$)
  If *pReq\MultiSelect
    bufferSize = 32000
  Else
    bufferSize = 512
  EndIf
  ; Buffer to hold selected file name(s)
  *selectedFile = AllocateMemory(bufferSize)
  If Len(GetFilePart(*pReq\DefFilePath$))
    PokeS(*selectedFile,*pReq\DefFilePath$)
  Else
    PokeB(*selectedFile, 0)   ; 1st byte must be null if no initial file name is to be displayed
  EndIf
  ;ShowMemoryViewer(*selectedFile,750)
  Protected myOFN.OPENFILENAME
  myOFN\hwndOwner         = hW
  myOFN\lStructSize       = SizeOf(OPENFILENAME) + 12 ; #OSVEX_LENGTH = 88
  myOFN\hInstance         = #Null
  myOFN\lpstrFilter       = @*pReq\Pattern$
  myOFN\lpstrCustomFilter = #Null
  myOFN\nMaxCustFilter    = #Null
  myOFN\nFilterIndex      = *pReq\patternPos
  myOFN\lpstrFile         = *selectedFile
  myOFN\nMaxFile          = bufferSize
  myOFN\lpstrFileTitle    = #Null
  myOFN\nMaxFileTitle     = #Null
  myOFN\lpstrInitialDir   = @*pReq\DefFilePath$
  myOFN\lpstrTitle        = @*pReq\Title$
  myOFN\flags             = #OFS_FILE_OPEN_FLAGS | #OFN_ENABLEHOOK | #OFN_ENABLESIZING  ;| #OFN_NOCHANGEDIR
  myOFN\lpfnHook          = @gui_OFN_HookProc()
  If *pReq\multiSelect
    myOFN\flags | #OFN_ALLOWMULTISELECT
  EndIf
  GetOpenFileName_(@myOFN)
  ; Deal with multi selections which are null terminated within *selectedFile
  ; With multi selections, the buffer null terminates folder and files
  ; If 1st string ends with the folder, we know we have multiple selections
  ;ShowMemoryViewer(*selectedFile,750)
  folder$ = PeekS(*selectedFile)
  If *pReq\MultiSelect And FileSize(GetFilePart(folder$)) = -1
    *pRes\nFiles = SF_CharReplace(*selectedFile,#NULL$,",")
    nextFile$ = PeekS(*selectedFile)
    nextFile$ = Mid(nextFile$,FindString(nextFile$,",")+1)
    *pRes\nFiles = Split(nextFile$,",",*pRes\Files$())
    *pRes\DefFilePath$ = folder$
    If Right(*pRes\DefFilePath$,1) <> "\"
      *pRes\DefFilePath$ + "\"
    EndIf
    If *pRes\nFiles = 0
      ReDim *pRes\Files$(0)
      *pRes\Files$(0) = #sNull$
    EndIf
  Else    ; 1 file selected
    If FL_FileExists(folder$) > 0
      *pRes\nFiles = 1
      *pRes\Files$(0) = GetFilePart(Folder$)
      *pRes\DefFilePath$ = GetPathPart(Folder$)
    Else
      *pRes\nFiles = 0
      *pRes\Files$(0) = #sNull$
    EndIf
  EndIf 
  FreeMemory(*selectedFile)
  ProcedureReturn *pRes\nFiles
EndProcedure
;-}
Enumeration
  #gadResults
  #gadBrowseFiles
  #gadBrowsePaths
  #gadClear
EndEnumeration
Define.i i,evG
Define.s s$
If #PB_Compiler_Unicode
  s$ = "Get Files w/Details View in Unicode"
Else
  s$ = "Get Files w/Details View in Ascii"
EndIf
If OpenWindow(0,0,0,440,340,s$,#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  EditorGadget(#gadResults,20,40,400,300)
  ButtonGadget(#gadBrowseFiles,20,0,100,20,"Browse For File(s)...")
  ButtonGadget(#gadBrowsePaths,130,0,100,20,"Browse For Path(s)...")
  ButtonGadget(#gadClear,390,0,30,20,"Clear")
  Repeat
    Select WaitWindowEvent(5)
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      evG = EventGadget()
      Select evG
      Case #gadClear
        ClearGadgetItems(#gadResults)
      Case #gadBrowseFiles
        OFNreq\guiX         = 20
        OFNreq\guiY         = 20
        OFNreq\guiWd        = 300
        OFNreq\guiHt        = 300
        OFNreq\Title$       = "Select File(s)"
        OFNreq\DefFilePath$ = "d:\"
        OFNreq\MultiSelect  = 1
        OFNreq\PatternPos   = 1
        OFNreq\Pattern$     = "All Files (*.*)|*.*|CSV Files (*.csv)|*.csv|Text Files (*.txt)|*.txt||"
        If gui_FileRequester(WindowID(0), @OFNreq, @OFNres)
          AddGadgetItem(#gadResults,-1, Str(OFNres\nFiles) + " File(s) From '" + OFNres\DefFilePath$ + "' =")
          For i = 0  To OFNres\nFiles - 1
            AddGadgetItem(#gadResults,#PB_Any,OFNres\Files$(i))
          Next i
        EndIf
      Case #gadBrowsePaths
        OFNreq\guiX         = 20
        OFNreq\guiY         = 20
        OFNreq\guiWd        = 300
        OFNreq\guiHt        = 300
        OFNreq\Title$       = "Select Path(s)..."
        OFNreq\DefFilePath$ = "c:\"
        OFNreq\MultiSelect  = 1
        OFNreq\PatternPos   = 1
        OFNreq\Pattern$     = "All Files (*.*)|*.*|CSV Files (*.csv)|*.csv|Text Files (*.txt)|*.txt||"
        If gui_PathRequester(@OFNreq,@OFNres)
          AddGadgetItem(#gadResults,-1, Str(OFNres\nFiles) + " Path(s) From '" + OFNres\DefFilePath$ + "' =")
          For i = 0  To OFNres\nFiles - 1
            AddGadgetItem(#gadResults,#PB_Any,OFNres\Files$(i))
          Next i
        EndIf
      EndSelect
    EndSelect
  ForEver
EndIf
Now I liked to add this new file requester to my personal library, so I create the following lib:

Code: Select all

Global resultsdll.s

; skywalk
; Since the built-in PureBasic OFN Requester does not allow Details View!
; Fill the OFN structure and use a OFNHook Procedure.
; Portions modified from Sparkie's code:
; http://www.purebasic.fr/english/viewtopic.php?p=133878#p133878
EnableExplicit
#sNull$ = "-999"
Structure ScanPBDataTypes
  ; These take up no memory and are allowed to grow without redim
  ; when the structure is pointed at a memory buffer.
  ; ex. *buf\d[i] ; 'i' being incremented in a loop
  ; Essentially, this is 1 big StructureUnion.
  b.b[0]  ; byte
  w.w[0]  ; word
  l.l[0]  ; long
  i.i[0]  ; integer
  q.q[0]  ; quad
  f.f[0]  ; float
  d.d[0]  ; double
  a.a[0]  ; ascii
  c.c[0]  ; character
  u.u[0]  ; unicode
  ;s.s{Use 1-MAX String Length expected 255}  ; string
  s.s[0]  ; This supports 1 String scan. *p\s = @x$, *p\s[1] does not work.
EndStructure

Macro FL_FileExists(Fname)
  ;  0 = File or Folder does not exist
  ;  1 = File Exists
  ; -1 = Folder Exists
  ; Since FileSize() returns:
  ;       -1 = File Not found.
  ;       -2 = File is a directory.
  ;        0 = Empty File, > 1 = File Exists
  ;        FileSize() respects '*' wildcards and reports results accordingly.
  FileSize(Fname) + 1
EndMacro

Procedure.i SF_CharReplace(*p.ScanPBDataTypes, StrFrom$="|", StrTo$=#NULL$)
  ; REV:  111227, skywalk
  ; SYNTAX:   nNulls = SF_CharReplace(@x$,"|",#Null$,0)
  ; DEFAULT:  Edit a string in memory to embed null char's = Chr(0)
  ; RETURN:   # of char replacements made.
  ; If StrFrom$=#Null$, then buffer end is assumed to be double null position.
  ; Accepts multiple chars, but only works with the 1st char.
  ; Works with unicode or ascii.
  Protected.i i, msLen, nChars
  Protected.i CharFrom = Asc(StrFrom$)
  Protected.i CharTo = Asc(StrTo$)
  ; Determine msLen
  If CharFrom = #Null   ; Search memory for double nulls = end of string buffer
    While msLen < 64000
      If *p\c[msLen] = 0 And *p\c[msLen+1] = 0
        Break
      EndIf
      msLen + 1
    Wend
    msLen - 1  ; Ignore the final #Null later in code
  Else
    msLen = MemoryStringLength(*p) - 1  ; Get Len in characters, not bytes.
   ; StringByteLength(PeekS(*p))        ; Returns nBytes and depends on ascii/unicode switch!
  EndIf
  For i = 0 To msLen   ; Since walking through String by Character, no need to count bytes/char.
    If *p\c[i] = CharFrom
      *p\c[i] = CharTo
      nChars + 1
    EndIf
  Next i
  ;ShowMemoryViewer(@*p,msLen+32)
  ProcedureReturn nChars
EndProcedure

Procedure.i Split(s$, Delm$, Array sA.s(1), trimsp.i=0, UseCase.i=#PB_String_CaseSensitive)
  ; REV:  100405, skywalk
  ;       modified from Trond PB forum code
  ;       www.purebasic.fr/english/viewtopic.php?f=13&t=26738
  ; REV:  111227, skywalk
  ;       removed ReSize parameter. sA() adjusts to size required = nStrings-1
  ;       added multi-char delimiter, case sensitivity, and unicode.
  ;       UseCase = 0-#PB_String_CaseSensitive or 1-#PB_String_NoCase
  ; Return: Count of elements or nStrings found.
  ;         If none, then entire string is assigned to sA(0)
  ; s$ = normal null terminated string
  ; VB6 syntax -> sA() = Split(expression[, delimiter[, count[, compare]]])
  ; SizeOf(Character) is converted to a constant by compiler so no speed hit for multiple use
  Protected.i i, nStrings
  Protected.i sLen = Len(s$)
  Protected.i DelmLen = Len(Delm$)
  Protected *s.Character = @s$  ; Character Structure points to s$ contents, not its address
  Protected.i lastp = *s
  Protected Delmc.c
  If DelmLen = 0  ; "" or empty
    ReDim sA(0)
    sA(0) = s$
    nStrings = 1
  ElseIf sLen = 0 ; s$ = ""
    ReDim sA(0)
    sA(0) = #NULL$
    nStrings = 0
  Else            ; OK to Split
    Dim sA(sLen/DelmLen+1)    ; Start array at worst case estimate
    If UseCase = #PB_String_CaseSensitive And (DelmLen = 1 Or Delm$ = #CRLF$) ; use faster single character split routine
      If DelmLen = 1
        Delmc = Asc(Delm$)
      Else            ; Delm$ = #CRLF$
        Delmc = #LF   ; use the 2nd char, then trim trailing #CR$ from elements
      EndIf
      While *s\c      ; > 0 means valid Character, = 0 means String terminated.
        If *s\c <> Delmc
          *s + SizeOf(Character)
        Else
          *s\c = 0    ; Terminate string with Chr(0) here
          sA(nStrings) = PeekS(lastp)
          lastp = *s + SizeOf(Character)  ; remember last pointer
          nStrings + 1
          *s + (DelmLen) * SizeOf(Character)
        EndIf
      Wend
    Else              ; use slower multi-char split routine
      While *s\c      ; > 0 means valid Character, = 0 means String terminated.
        If CompareMemoryString(*s, @Delm$, UseCase, DelmLen) ; <> 0 means different memory
          *s + SizeOf(Character)
        Else                                                 ; = 0  means identical memory
          *s\c = 0    ; Terminate string with Chr(0) here
          sA(nStrings) = PeekS(lastp)
          lastp = *s + (DelmLen) * SizeOf(Character)  ; remember last pointer
          nStrings + 1
          *s + (DelmLen) * SizeOf(Character)
        EndIf
      Wend
    EndIf
    If *s <> lastp    ; reached last valid element
      sA(nStrings) = PeekS(lastp)
    EndIf
    ReDim sA(nStrings)
    If trimsp
      For i = 0 To nStrings
        sa(i) = Trim(sa(i))
      Next i
      If trimsp = 9
        For i = 0 To nStrings
          sa(i) = Trim(sa(i), #TAB$)
        Next i
      EndIf
    EndIf
    If delm$ = #CRLF$
      For i = 0 To nStrings
        sa(i) = RTrim(sa(i),#CR$)
      Next i
    EndIf
    nStrings + 1      ; Catch #Null$ due to dangling Delm$, "1,2,"
  EndIf
  ProcedureReturn nStrings
EndProcedure
;-{ GUI FILE STUFF
#OSVEX_LENGTH           = 88  ; If > Win2K = 88 Else = 76
#CDN_INITDONE           = #CDN_FIRST - 0
#CDN_SELCHANGE          = #CDN_FIRST - 1
#CDN_FOLDERCHANGE       = #CDN_FIRST - 2
#OFN_VIEW_REPORT        = $702C
#OFN_VIEW_LIST          = $702B
#OFN_VIEW_LARGEICON     = $7029
#OFN_VIEW_SMALLICON     = $702A
#OFN_VIEW_THUMBNAIL     = $702D
#OFN_VIEW_THUMBNAIL_2K  = $7031
#OFN_VIEW_TILE          = $702E
#OFN_ENABLESIZING       = $800000
#OFS_FILE_OPEN_FLAGS    = #OFN_EXPLORER | #OFN_LONGNAMES | #OFN_CREATEPROMPT
#OFS_FILE_SAVE_FLAGS    = #OFN_EXPLORER | #OFN_LONGNAMES | #OFN_OVERWRITEPROMPT | #OFN_HIDEREADONLY
Structure OFNdata
  Title$
  DefFilePath$
  Pattern$
  PatternPos.i
  MultiSelect.i
  Array Files$(0)
  nFiles.i
EndStructure
Structure OFNgui Extends OFNdata  ; Used by OFN Dialog gui
  guiX.i
  guiY.i
  guiWd.i
  guiHt.i
EndStructure
Global OFNreq.OFNgui, OFNres.OFNdata

; Custom message for move/resize OpenFileRequester
Global gui_OFN_MoveDlg = RegisterWindowMessage_(@"gui_OFN_MoveDlg")
Procedure gui_OFN_HookProc(hW, msg, wP, lP)
  Protected.i hParent, hLV, ri
  Select msg
  Case #WM_INITDIALOG
    ;                                 wd,           ht
    PostMessage_(hW, gui_OFN_MoveDlg, OFNreq\guiWd, OFNreq\guiHt)
  Case gui_OFN_MoveDlg
    ; Reposition @ resize dialog window
    hParent = GetParent_(hW)
    MoveWindow_(hParent, OFNreq\guiX, OFNreq\guiY, wP, lP, 1)
  Case #WM_NOTIFY
    ; hW is the handle to the dialog
    ; hParent is the handle to the common control
    ; hLV is the handle to the listview itself
    hParent = GetParent_(hW)
    hLV = FindWindowEx_(hParent, 0, "SHELLDLL_DefView", #NULL$)
    If hLV
      ri = SendMessage_(hLV, #WM_COMMAND, #OFN_VIEW_REPORT, #Null)
    EndIf
  EndSelect
  ProcedureReturn 0
EndProcedure

Procedure.i gui_FileRequester(hW.i, *pReq.OFNgui, *pRes.OFNdata)
  ; REV:  120106, skywalk
  ; Process user response to OpenFileRequester File Selection(s)
  ; Results go in structure OFNres
  ; If MultiSelect=1,  then OFNres\File$(nFiles-1) contains the list of files
  ;               =0,  then OFNres\File$(0)        contains the 1 selected file
  ; SYNTAX: nFiles = gui_FileRequester(hW, @myOFNreq, @myOFNres)
  Protected.i i, j, *selectedFile, bufferSize
  Protected.s folder$, nextFile$
  ; For Filter to function properly, replace "|" with null Chr(0) directly in memory
  ; Filter must end with 2 null chars
  If Right(*pReq\Pattern$,2) <> "||"
    *pReq\Pattern$ + "||"
  EndIf
  SF_CharReplace(@*pReq\Pattern$)
  If *pReq\MultiSelect
    bufferSize = 32000
  Else
    bufferSize = 512
  EndIf
  ; Buffer to hold selected file name(s)
  *selectedFile = AllocateMemory(bufferSize)
  If Len(GetFilePart(*pReq\DefFilePath$))
    PokeS(*selectedFile,*pReq\DefFilePath$)
  Else
    PokeB(*selectedFile, 0)   ; 1st byte must be null if no initial file name is to be displayed
  EndIf
  ;ShowMemoryViewer(*selectedFile,750)
  Protected myOFN.OPENFILENAME
  myOFN\hwndOwner         = hW
  myOFN\lStructSize       = SizeOf(OPENFILENAME) + 12 ; #OSVEX_LENGTH = 88
  myOFN\hInstance         = #Null
  myOFN\lpstrFilter       = @*pReq\Pattern$
  myOFN\lpstrCustomFilter = #Null
  myOFN\nMaxCustFilter    = #Null
  myOFN\nFilterIndex      = *pReq\patternPos
  myOFN\lpstrFile         = *selectedFile
  myOFN\nMaxFile          = bufferSize
  myOFN\lpstrFileTitle    = #Null
  myOFN\nMaxFileTitle     = #Null
  myOFN\lpstrInitialDir   = @*pReq\DefFilePath$
  myOFN\lpstrTitle        = @*pReq\Title$
  myOFN\flags             = #OFS_FILE_OPEN_FLAGS | #OFN_ENABLEHOOK | #OFN_ENABLESIZING  ;| #OFN_NOCHANGEDIR
  myOFN\lpfnHook          = @gui_OFN_HookProc()
  If *pReq\multiSelect
    myOFN\flags | #OFN_ALLOWMULTISELECT
  EndIf
  GetOpenFileName_(@myOFN)
  ; Deal with multi selections which are null terminated within *selectedFile
  ; With multi selections, the buffer null terminates folder and files
  ; If 1st string ends with the folder, we know we have multiple selections
  ;ShowMemoryViewer(*selectedFile,750)
  folder$ = PeekS(*selectedFile)
  If *pReq\MultiSelect And FileSize(GetFilePart(folder$)) = -1
    *pRes\nFiles = SF_CharReplace(*selectedFile,#NULL$,",")
    nextFile$ = PeekS(*selectedFile)
    nextFile$ = Mid(nextFile$,FindString(nextFile$,",")+1)
    *pRes\nFiles = Split(nextFile$,",",*pRes\Files$())
    *pRes\DefFilePath$ = folder$
    If Right(*pRes\DefFilePath$,1) <> "\"
      *pRes\DefFilePath$ + "\"
    EndIf
    If *pRes\nFiles = 0
      ReDim *pRes\Files$(0)
      *pRes\Files$(0) = #sNull$
    EndIf
  Else    ; 1 file selected
    If FL_FileExists(folder$) > 0
      *pRes\nFiles = 1
      *pRes\Files$(0) = GetFilePart(Folder$)
      *pRes\DefFilePath$ = GetPathPart(Folder$)
    Else
      *pRes\nFiles = 0
      *pRes\Files$(0) = #sNull$
    EndIf
  EndIf 
  FreeMemory(*selectedFile)
  ProcedureReturn *pRes\nFiles
EndProcedure

 ProcedureDLL.s OpenFileRequester4(title.s,Path1.s, Filtre.s)
 Protected  i.a
  resultsdll=""
  OFNreq\guiX         = 20
  OFNreq\guiY         = 20
  OFNreq\guiWd        = 300
  OFNreq\guiHt        = 300
  OFNreq\Title$       = title
  OFNreq\DefFilePath$ = Path1
  OFNreq\MultiSelect  = 1
  OFNreq\PatternPos   = 1
  OFNreq\Pattern$     = Filtre
  If gui_FileRequester(WindowID(0), @OFNreq, @OFNres)
    For i = 0  To OFNres\nFiles - 1
      resultsdll+OFNres\Files$(i)
      Debug OFNres\Files$(i)
   Next i
  EndIf
  ProcedureReturn resultsdll
EndProcedure
then I compile it to lib using TailBite, result OK, then restart compiler

then I tried to test it with the following small program:

Code: Select all

OpenWindow(0,0,0,440,340,"hello",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  
Path1.s = "C:\"  
Filtre1.s = "Text (*.txt)|*.txt;*.bat|PureBasic (*.pb)|*.pb|All Files (*.*)|*.*"
tmp.s= OpenFileRequester4("title",Path1, Filtre1)

Debug tmp

when I run it, the requester starts, I can select files, then
I had error at the line: tmp.s= OpenFileRequester4("title",Path1, Filtre1)
Invalid memory access.

what's wrong??
any help is appreciated

thank you
PureBasic: Surprisingly simple, diabolically powerful
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Re: error: Invalid memory access

Post by ABBKlaus »

Most likely you have code outside of procedures.

And i did not see any Init() procedures.

Try moving this line :

Code: Select all

Global gui_OFN_MoveDlg = RegisterWindowMessage_(@"gui_OFN_MoveDlg")
into this:

Code: Select all

ProcedureDll GUI_OFN_Init()
  Global gui_OFN_MoveDlg = RegisterWindowMessage_(@"gui_OFN_MoveDlg")
EndProcedure
morosh
Enthusiast
Enthusiast
Posts: 293
Joined: Wed Aug 03, 2011 4:52 am
Location: Beirut, Lebanon

Re: error: Invalid memory access

Post by morosh »

thanks fro reply!

I created :

Code: Select all

ProcedureDll GUI_OFN_Init()
  Global gui_OFN_MoveDlg = RegisterWindowMessage_(@"gui_OFN_MoveDlg")
EndProcedure
then I call it from OpenFileRequester4(title.s,Path1.s, Filtre1.s) like the following:

Code: Select all

ProcedureDLL.s OpenFileRequester4(title.s,Path1.s, Filtre1.s)
   Protected  i.a
   GUI_OFN_Init()

  resultsdll=""
  OFNreq\guiX         = 20
  OFNreq\guiY         = 20
  OFNreq\guiWd        = 300
  OFNreq\guiHt        = 300
  OFNreq\Title$       = title
  OFNreq\DefFilePath$ = Path1
  OFNreq\MultiSelect  = 1
  OFNreq\PatternPos   = 1
  OFNreq\Pattern$     = Filtre1
  If gui_FileRequester(WindowID(0), @OFNreq, @OFNres)
    For i = 0  To OFNres\nFiles - 1
      resultsdll+OFNres\Files$(i)
      Debug OFNres\Files$(i)
    Next i
  EndIf
  ProcedureReturn resultsdll
EndProcedure

then tools --> TailBite --> OK --> Restart compiler
then i've gone to my previous program:

Code: Select all

OpenWindow(0,0,0,440,340,"hello",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  
Path1.s = "C:\"  
Filtre1.s = "Text (*.txt)|*.txt;*.bat|PureBasic (*.pb)|*.pb|All Files (*.*)|*.*"

tmp.s= OpenFileRequester4("title",Path1, Filtre1)

Debug tmp
Nothing changed, always "Invalid memory access, (read error at address 0)" :(
PureBasic: Surprisingly simple, diabolically powerful
ABBKlaus
Addict
Addict
Posts: 1143
Joined: Sat Apr 10, 2004 1:20 pm
Location: Germany

Re: error: Invalid memory access

Post by ABBKlaus »

The problem is only when the array in the structure isn´t dimensioned, here´s a fixed version :

PS : in your library-code there is a hardcoded WindowID(0) :mrgreen:

Code: Select all

ProcedureDLL GUI_OFN_Init()
  ; Custom message for move/resize OpenFileRequester
  Global gui_OFN_MoveDlg = RegisterWindowMessage_(@"gui_OFN_MoveDlg")
  Global OFNreq.OFNgui, OFNres.OFNdata
  Dim OFNreq\Files$(1)
  Dim OFNres\Files$(1)
EndProcedure
morosh
Enthusiast
Enthusiast
Posts: 293
Joined: Wed Aug 03, 2011 4:52 am
Location: Beirut, Lebanon

Re: error: Invalid memory access

Post by morosh »

Thanks a lot!! now it's working :D

about WindowID(0), I changed it to a parameter

Code: Select all

ProcedureDLL.s OpenFileRequester4(wid.l,title.s,Path1.s, Filtre1.s)
   Protected  i.a
   GUI_OFN_Init()

  resultsdll=""
  OFNreq\guiX         = 20
  OFNreq\guiY         = 20
  OFNreq\guiWd        = 300
  OFNreq\guiHt        = 300
  OFNreq\Title$       = title
  OFNreq\DefFilePath$ = Path1
  OFNreq\MultiSelect  = 1
  OFNreq\PatternPos   = 1
  OFNreq\Pattern$     = Filtre1
  If gui_FileRequester(wid, @OFNreq, @OFNres)
    For i = 0  To OFNres\nFiles - 1
      resultsdll+OFNres\Files$(i)
      Debug OFNres\Files$(i)
    Next i
  EndIf
  ProcedureReturn resultsdll
EndProcedure
Have a nice day
PureBasic: Surprisingly simple, diabolically powerful
Post Reply