News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Use GetFullPathName to get directory part

Started by Landsfiskalen, September 30, 2014, 09:13:12 AM

Previous topic - Next topic

Landsfiskalen

Hi all,
I'm completely at a loss here. I'm calling "GetModuleFileName" to get the full path + filename for my program. Now I want to use "GetFullPathName" to weed out the directory part only.


invoke GetModuleFileName, hInst, addr szExebuffer, MAX_PATH
this gives me the full path + filename in szExebuffer.

Now I want to find out only the directory part using GetFullPathName. Any help on how I go about it?


Any help is greatly appreciated.

dedndave

start at the end of the string and work toward the beginning
the first "\" or ":" you find tells you where the filename starts
of course, that function never returns a ":" without a "\", so you can just look for "\"
but, to make a more universal function, you might search either

i would suggest you use the UNICODE version,
noting that UNICODE pathnames must be prepended with "\\?\" for many functions
GetModuleFileNameW

jj2007

Dave is ambitious, as usual :biggrin:

Here is a very simple example with no error checking (there won't be errors):

include \masm32\include\masm32rt.inc
.data?
buffer   db 1000 dup(?)

.code
start:   mov esi, offset buffer
   invoke GetModuleFileName, rv(GetModuleHandle, 0), esi, MAX_PATH
   print esi, 13, 10
   lea edx, [esi+len(esi)]   ; add the length of the full path
   .Repeat
      dec edx
   .Until byte ptr [edx]=="\"
   mov byte ptr [edx], 0   ; insert a zero to delimite the string
   inkey esi, 13, 10
   exit
end start


Welcome to the Forum. How did you find us, and why do you want to learn assembler? It's a rare art nowadays ;-)

fearless

Here is a routine i commonly use, where i create an .ini filename based on the current module name, checking for . and \ and determining where to store the file based on the current OS version. Should give you an idea on what to do hopefully.

.486
.model flat,stdcall
option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib
include shell32.inc
includelib shell32.lib

.data
szBackslash            db "\",0            ; backslash char
szExtEXE             db ".exe",0         ; exe file extension
szExtINI             db ".ini",0            ; ini file extension
;szIniFilename        db MAX_PATH dup (0)    ; buffer to hold our ini filename

.code
;--------------------------------------------------------------------------------------
; Create an .ini filename based on the executables name.
;
; Example Usage:
;
; Invoke CreateIniFilename, Addr szIniFilename
;
; 22/01/2014 - Added lpszBaseModuleName param (optional) if not NULL will copy
; the base module name to this buffer.
;--------------------------------------------------------------------------------------
CreateIniFilename    PROC    lpszIniFile:DWORD, lpszBaseModuleName:DWORD
    LOCAL VersionInformation:OSVERSIONINFO
    LOCAL ModuleFullPathname[MAX_PATH]:BYTE
    LOCAL ModuleName[MAX_PATH]:BYTE
    LOCAL hInst:DWORD
    LOCAL ppidl:DWORD
    LOCAL LenFilePathName:DWORD
    LOCAL PosFullStop:DWORD
    LOCAL PosBackSlash:DWORD
   
    Invoke GetModuleFileName, NULL, Addr ModuleFullPathname, Sizeof ModuleFullPathname
    Invoke lstrlen, Addr ModuleFullPathname            ; length of module path
    mov LenFilePathName, eax                        ; save var for later
   
    ;----------------------------------------------------------------------
    ; Find the fullstop position in the module full pathname
    ;----------------------------------------------------------------------
    mov PosFullStop, 0
    lea esi, ModuleFullPathname
    add esi, LenFilePathName
    mov ecx, LenFilePathName
    .WHILE ecx >= 0
        movzx eax, byte ptr [esi]
        .IF al == 46d ; 46d = 2Eh is full stop .
            mov PosFullStop, ecx ; save fullstop position
            .BREAK
        .ELSE
            dec esi ; move down string by 1
            dec ecx ; decrease ecx counter
        .ENDIF
    .ENDW
    .IF PosFullStop == 0 ; if for some reason we dont have the position
        mov eax, FALSE         ; we should probably exit with an error
        ret
    .ENDIF
    ;----------------------------------------------------------------------
   
    ; Determine what OS we are running on
    mov VersionInformation.dwOSVersionInfoSize, SIZEOF OSVERSIONINFO
    Invoke GetVersionEx, Addr VersionInformation
    mov eax, VersionInformation.dwMajorVersion

    ;----------------------------------------------------------------------
    ; Find the backslash position in the module full pathname
    ;----------------------------------------------------------------------
    mov PosBackSlash, 0
    lea esi, ModuleFullPathname
    add esi, PosFullStop
    mov ecx, PosFullStop
    .WHILE ecx >= 0
        movzx eax, byte ptr [esi]
        .IF al == 92 ; 92d = 5Ch is backslash \
            mov PosBackSlash, ecx ; save backslash position
            .BREAK
        .ELSE
            dec esi ; move down string by 1
            dec ecx ; decrease ecx counter
        .ENDIF
    .ENDW
    .IF PosBackSlash == 0 ; if for some reason we dont have the position
        mov eax, FALSE          ; we should probably exit with an error
        ret
    .ENDIF       
   
    ; Fetch just the module name based on last backslash position
    ; and the fullstop positions that we found above.
    lea edi, ModuleName
    lea esi, ModuleFullPathname
    add esi, PosBackSlash
    inc esi ; skip over the \
   
    mov ecx, PosBackSlash
    inc ecx ; skip over the \
    .WHILE ecx < PosFullStop
        movzx eax, byte ptr [esi]
        mov byte ptr [edi], al
        inc esi
        inc edi
        inc ecx
    .ENDW
    mov byte ptr [edi], 0 ; zero last byte to terminate string.
    ;----------------------------------------------------------------------


    .IF eax > 5 ; Vista / Win7           
        ;----------------------------------------------------------------------
        ; Glue all the bits together to make the new ini file location
        ;
        ; include shell32.inc & includelib shell32.lib required for the
        ; SHGetSpecialFolderLocation & SHGetPathFromIDList functions
        ;----------------------------------------------------------------------
        Invoke GetModuleHandle, NULL
        mov hInst, eax       
        Invoke SHGetSpecialFolderLocation, hInst, CSIDL_APPDATA, Addr ppidl
        Invoke SHGetPathFromIDList, ppidl, lpszIniFile
        Invoke lstrcat, lpszIniFile, Addr szBackslash    ; add a backslash to this path
        Invoke lstrcat, lpszIniFile, Addr ModuleName    ; and add our app exe name
        Invoke GetFileAttributes, lpszIniFile
        .IF eax != FILE_ATTRIBUTE_DIRECTORY               
            Invoke CreateDirectory, lpszIniFile, NULL    ; create directory if needed
        .ENDIF
        Invoke lstrcat, lpszIniFile, Addr szBackslash    ; add a backslash to this as well       

        Invoke lstrcat, lpszIniFile, Addr ModuleName ; add module name to our folder path
        invoke lstrcat, lpszIniFile, Addr szExtINI
        ;----------------------------------------------------------------------
       
    .ELSE ; WinXP
        Invoke lstrcpyn, lpszIniFile, Addr ModuleFullPathname, PosFullStop
        Invoke lstrcat, lpszIniFile, Addr szExtINI
    .ENDIF
    .IF lpszBaseModuleName != NULL ; save the result to address specified by user
        Invoke lstrcpy, lpszBaseModuleName, Addr ModuleName ; (2nd parameter)
    .ENDIF
    mov eax, TRUE
    ret
CreateIniFilename    ENDP

END

hutch--

You have this procedure in the MASM32 library.


GetPathOnly proc src:DWORD, dst:DWORD


Feed a GetModuleFileName result to it and get the bare path back.

sinsi


jj2007

Quote from: sinsi on September 30, 2014, 04:33:39 PM
Even simpler, use PathRemoveFileSpec

Never thought of that one :t

include \masm32\include\masm32rt.inc
uselib Shlwapi

.data?
buffer   db MAX_PATH dup(?)

.code
start:
   mov esi, offset buffer
   invoke GetModuleFileName, rv(GetModuleHandle, 0), esi, sizeof buffer
   invoke PathRemoveFileSpec, esi
   inkey esi
   exit

end start

TWell

How about using SearchPath() with 0 to path  and "." to filename ?

hutch--

Here is the other MASM32 library module to get this result. It should be tweaked up to a 256 character path but it works fine.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    .486                  ; set processor model
    .model flat, stdcall  ; 32 bit memory model
    option casemap :none  ; case sensitive

    include \masm32\include\kernel32.inc

    .code

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

align 4

GetAppPath proc lpPathBuffer:DWORD

    invoke GetModuleFileName,0,lpPathBuffer,128  ; return length in eax
    add eax, lpPathBuffer

  ; ---------------------------------------
  ; scan backwards for first "\" character
  ; ---------------------------------------
  @@:
    dec eax                             ; dec ECX
    cmp BYTE PTR [eax],'\'              ; compare if "\"
    jne @B                              ; jump back to @@: if not "\"

    mov BYTE PTR [eax+1],0              ; write zero terminator after "\"

    mov eax, lpPathBuffer

    ret

GetAppPath endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end


Here is a quick update on the old algo.


; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

comment * -----------------------------------------------------
                        Build this  template with
                       "CONSOLE ASSEMBLE AND LINK"
        ----------------------------------------------------- *

    AppPath PROTO

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    print rv(AppPath),13,10

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

AppPath proc

    .data?
      appath db 260 dup (?)
    .data
      ppth dd appath
    .code

    mov eax, rv(GetModuleFileName,0,ppth,260)
    add eax, ppth

  @@:
    sub eax, 1
    cmp BYTE PTR [eax], "\"
    jne @B

    mov BYTE PTR [eax+1], 0
    mov eax, ppth
    ret

AppPath endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start

jj2007

Quote from: TWell on September 30, 2014, 06:11:33 PM
How about using SearchPath() with 0 to path  and "." to filename ?

Do you have a working example? Mine returns always the full path...

MbExeFolder$:   D:\masm32\MasmBasic\Res\
GmFileName:     D:\masm32\MasmBasic\Res\SkelMbEmpty.exe
SearchPath:     D:\masm32\MasmBasic\Res\SkelMbEmpty.exe#


include \masm32\MasmBasic\MasmBasic.inc      ; download
  Init

  PrintLine "MbExeFolder$:", Tb$, MbExeFolder$      ; far too simple ;-)

  sub esp, MAX_PATH*2      ; create two buffers
  mov esi, esp
  invoke GetModuleFileName, 0, esi, MAX_PATH
  PrintLine "GmFileName:", Tb$, esi

  invoke SetLastError, 0      ; test for errors
  lea edi, [esi+MAX_PATH]      ; destination buffer
  invoke SearchPath, esi, Chr$("."), 0, MAX_PATH, edi, 0
  ; deb 4, "sp", eax, $Err$()      ; no errors!
  mov dword ptr [edi+eax], "#"      ; set a marker
  PrintLine "SearchPath:", Tb$, edi
  Exit
end start

Landsfiskalen

Wow! Thanks for all the nice input! PathRemoveFileSpec sounds like the way to go. Will check it out as soon as I can.

jj2007>> I've been dabbling with x86 assembler on and off for the last 10-15 years. Mainly I return to it because it's fun. :) I used to code 6502/6510 assembler back in the days of the C64, and I really enjoy the low level aspect of it. I used to be a member on the old forum, but forgot my username. :/

Thanks for all the help lads!


TWell

Quote from: jj2007 on September 30, 2014, 08:25:11 PM
Quote from: TWell on September 30, 2014, 06:11:33 PM
How about using SearchPath() with 0 to path  and "." to filename ?

Do you have a working example? Mine returns always the full path...
Sure.686
.MODEL FLAT, STDCALL
OPTION CASEMAP :NONE

ExitProcess PROTO STDCALL :DWORD
SearchPathA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
INCLUDELIB kernel32.lib

MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD
INCLUDELIB user32.lib

InitCommonControls PROTO
INCLUDELIB comctl32.lib

.data
dd InitCommonControls ; for manifest only
msg1 db "SearchPath",0
msg2 db ".",0
buf db 260 dup(?)

.code
start PROC
INVOKE SearchPathA,0,ADDR msg2,0,260,ADDR buf,0
INVOKE MessageBoxA,0,ADDR buf,ADDR msg1,0
INVOKE ExitProcess,eax
;mov eax, InitCommonControls
start ENDP
END start

Gunther

Hi Landsfiskalen,

welcome to the forum and have a lot of fun.

Gunther
You have to know the facts before you can distort them.

jj2007

Quote from: TWell on October 01, 2014, 12:33:16 AM
Quote from: jj2007 on September 30, 2014, 08:25:11 PM
Do you have a working example? Mine returns always the full path...
Sure

That works indeed: SearchPathA, 0, ...
But I wonder why it doesn't work if I use GetModuleFileNameA instead of NULL ::)