The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: Dasar on July 07, 2006, 04:18:29 PM

Title: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Dasar on July 07, 2006, 04:18:29 PM
hi all


i wrote this littel function to get the file name from a specific path, this function has 2 operands, the first is the address of the path, the

second is the destination "buffer" which will contain the file name,  for example:

"C:\somedir\SomeFile.exe"

my function supposed to put the "SomeFile.exe" in the destination buffer..

the function is written in this manner

GetFileName PROTO :DWORD, :DWORD

GetFileName proc lpPath: DWORD, dstBuffer: DWORD

=========================================================

i know that maybe there is a function in masm32 does the same mission, but i didn't search for any function, because the main

purpose of writing this function, is the training on write functions in asm...

=========================================================

there are no errors in the syntax of the code, it assembled and linked successfully, but at the run time; and once the program is

exectued,  it crashes


please take a look at the code, and tell me where is the mistake(s), and correct them for me, and if you have a little time, please fill it

with your comments, so i can understand your code easily..

please also tell me if there is an easier and better way to write such funcion.


=========================================================




this is all my code, filled with my comments "these comments written as my understanding"   :





.386
.model flat, stdcall

option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
;=================================================================


; prototype of my function

GetFileName PROTO :DWORD, :DWORD


;=================================================================

.data

  ThePath db "C:\ForTest\AnyFile.exe",0

;=================================================================

.data?

 
  TheFile db 50 dup(?)


;=================================================================

 

.code




comment *

my idea is:
1- Get the length of the full path
2- from the end of the path, Go back until we find "\"
3- Add 1 to that position, so we get the first char after "\"
4- copy all the chars from that position to the end of the path, to the destination "Buffer"
*




; Begin in the real work :-)


GetFileName proc lpPath:DWORD, dstBuffer:DWORD

mov eax, lpPath ; get the address of lpPath
xor edx, edx    ; edx = 0

@@:
mov cl, BYTE PTR [eax]
add eax, 1
inc edx
cmp cl, 0       ;used this, because i think there is a "zero" after the string
jne @B

; all code under previous @@, is for getting the length of the path, and put it in "edx"


mov eax, lpPath
add edx, eax
cmp BYTE PTR [edx], "\"
jne @F

@@:
dec edx
cmp BYTE PTR [edx], "\"
jne @B

; all code after the previous comment, if for getting the position of "\" , and put it in "edx"



inc edx         ; get the position of first char of the Real File Name :-)

mov eax, lpPath

add eax, edx    ; eax now has the address of the first char after "\" char

xor edx, edx     ; edx = 0, for using it in counting

push esi    ; wrote this because, as i understood, i have to preserve this reg as well as (ebx, edi), correct me
            ; if i'm wrong here, or if there are other registers must preserved ...


mov esi, dstBuffer   ; put the address of dstBuffer in "esi"

@@:
mov cl, BYTE PTR [eax + edx]
mov BYTE PTR [esi + edx], cl
inc edx
cmp cl, 0  ; used this, because i think there is a "zero" after the string
jne @B

pop esi         ; get back the value which was in esi

mov eax, edx    ; the return value is the length of the FileName  :)

ret           

GetFileName endp




  ; here is the test of GetFileName
 
  start:
  invoke GetFileName, Addr ThePath, Addr TheFile
  invoke MessageBox, 0, Addr TheFile, Addr TheFile, MB_OK
  invoke ExitProcess,0
  end start
 

; last comment: the program is crashed immediatily after running it !!!





thank you alot in advance :-)

Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Ossa on July 07, 2006, 04:48:23 PM
[edit] Realised this might sound a bit harsh/terse - it's not meant to be - I'm just in a rush [/edit]

Your main problem is here:

; all code after the previous comment, if for getting the position of "\" , and put it in "edx"

edx actually holds the address of the "\", not the offset into the string. This means that on these lines you problem continues:

add eax, edx    ; eax now has the address of the first char after "\" char

as eax now points to rubbish. So when you do:

mov cl, BYTE PTR [eax + edx]

You will be accessing invalid memory locations and your program will crash.

There are many thing you can do to fix this, but the easiest idea is to replace:

mov eax, lpPath

add eax, edx    ; eax now has the address of the first char after "\" char


with:

mov eax, edx




The other mistake is:

mov eax, lpPath
add edx, eax
cmp BYTE PTR [edx], "\"
jne @F

@@:
dec edx


The cmp/jne does nothing useful.

Ossa

[edit] If you read through your code, you will see that there is a way to do this using only pointers without using offsets into strings. [/edit]
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: P1 on July 07, 2006, 04:49:53 PM
From the MASM32 library:
NameFromPath proc lpPath:DWORD,lpBuffer:DWORD

Description

NameFromPath reads the filename from a complete path and returns it in the buffer specified in the parameter list.

Parameters
   1. lpPath is the address of the full path that has the file name.

   2. lpBuffer is the address of the buffer to receive the filename.

Return Value
The file name is returned in the buffer supplied in the second parameter.

Comments
The buffer to receive the filename must be large enough to acept the filename. For safety reasons if dealing with both long path and file name, the buffer can be made the same length as the source buffer.


Also use "Search", we had a good discussion on this, with many techniques discussed.

Regards,  P1  :8)
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Shantanu Gadgil on July 07, 2006, 05:02:09 PM
Hi Dasar,
the comments I have posted, though might seem terse, they were just quick "jot-downs" as I was reading through your code side-by-side. Each with its own heading  :bg :bg

praises:
well worked out algo...thats the way to think  :U

comment:
when target is byte no need to write "byte ptr"

suggestions/bugs(?):
save restore regs at top and end
use MAX_PATH for buf lengths
double line spacing not reqiured (for post text) :bdg
code indentation ???  :red :eek

debatable (lets just "my way of thinking"  :lol ):
be familiar with the so called "high level constructs"  :lol They are your friends  :green
.while/.endw, .if/.endif, etc  :8)


GetFileNameX proc pszFullFileName:DWORD, pszOutFileName:DWORD
push esi
push edi

mov esi,pszFullFileName
.while byte ptr [esi] != 0 ;go till end of fullfilename
inc esi
.endw

.while byte ptr [esi] != '\' ;move back till last '\'
dec esi
.endw
inc esi ; point to the next char after the '\'

xor eax,eax ;counter
mov edi, pszOutFileName
.while byte ptr [esi] != 0
movsb
inc eax
.endw
mov byte ptr [edi], 0

pop edi
pop esi
ret
GetFileNameX endp
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Dasar on July 07, 2006, 05:14:39 PM
thank you Ossa for your help, i'll try what you said

@P1 thank you for the code

but i said:

Quotei know that maybe there is a function in masm32 does the same mission, but i didn't search for any function, because the main

purpose of writing this function, is the training on write functions in asm...
 

:)

and i'll search for the topic you mention above :)


shantanu_gadgil thank you for your useful comments and your code :)

Quotebe familiar with the so called "high level constructs"   They are your friends 
.while/.endw, .if/.endif, etc

yes, i know these instructions, but i want to be familiar with "push, pop, mov, add, xor, call, cmp, jmp, jn, jne, etc..."   :)




any other hints or comments about my code from anyone is very appreciated  ^_^
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Ratch on July 07, 2006, 06:03:53 PM
Dasar,

Quoteplease also tell me if there is an easier and better way to write such funcion.

     Well, yes, maybe.  There is a book called Programming Windows, 5th edition,  by Charles Petzold.  In the 11th chapter he submits a C program called POPPAD3, which uses the Common Dialog Boxes.  This program opens files using filters like *.*,*.txt,*.bak, etc.  He does it by using the Common Dialog API like GetOpenFileName, of which you can read the documentation.  The program opens files, searches for text, reads text replaces text.  In other words, it is a poor man's file editor written to showcase the Common Dialog Boxes.  This might be too heavy for what you want, but it does give an alternative high level way of implementing what you are doing.  I have translated that program from C to MASM if you are interested.  If not, I just thought I would mention the Common Dialog Boxes for you to peruse.  Ratch
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: white scorpion on July 07, 2006, 07:37:26 PM
Funny...
I wrote exactly the same function for a program of mine. it even has the same name  :bg

GetFileName PROC FileNameLocal:DWORD,FileNameLocalSize:DWORD
    invoke GetModuleFileName,NULL,FileNameLocal,FileNameLocalSize
    invoke lstrlen,FileNameLocal
    mov ecx,eax
    mov eax,FileNameLocal
    add eax,ecx
@@:
    cmp byte ptr [eax],5Ch   ;first '\' from the back
    jz @F
    dec eax
    cmp eax,FileNameLocal
    jnz @B
    xor eax,eax
    ret
@@:
    inc eax
    invoke lstrcpy,FileNameLocal,eax
    xor eax,eax
    ret
GetFileName ENDP
;***********************************************************

Since i needed the name of only the program currently running, i put in GetModuleFileName to supply the data, but you could do it without that function as well.

here's a function changed to take as argument the complete path and which will use the same buffer to put the result in:

GetFileName PROC FileNameLocal:DWORD
    invoke lstrlen,FileNameLocal
    test eax,eax
    jnz @F
    xor eax,eax
    dec eax
    ret
@@:
    add eax,FileNameLocal
@@:
    cmp byte ptr [eax],5Ch   ;first '\' from the back
    jz @F
    dec eax
    cmp eax,FileNameLocal
    jnz @B
    xor eax,eax
    ret
@@:
    inc eax
    invoke lstrcpy,FileNameLocal,eax
    xor eax,eax
    ret
GetFileName ENDP



The latter one should perfectly fit your needs, total size = 55 bytes
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Casper on July 07, 2006, 08:37:02 PM
White Scorpion,

There are a couple of things worth mentioning...
@@:
    cmp byte ptr [eax],5Ch   ;first '\' from the back
    jz @F
    cmp eax,FileNameLocal
    jnz @B
    xor eax,eax
    ret
@@:

The above code will test the same byte forever.  You are missing the code to decrement eax.  This is probably just an oversight but I thought I better mention it because there are those who just copy and paste code that we post.

In the both postings, however, when you abort because you have reached the beginning of the string you are setting the value of eax to zero when you should set it to one to report the error.

hth,
Casper
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: white scorpion on July 07, 2006, 09:14:47 PM
huh where is the dec eax missing ? :bg
Updated above code, must have accidentally removed it when updating the code ;)

2nd thing: the reason i end with 0 is that there actually is no real error:
- the stringsize is bigger then 0
- there is no \ in the string
- this means that the inputted string is already only the filename.
and since it returns the filename in the original buffer, the resulting filename can still be used as only a filename.

i can change it without any problems, and so can anyone else, but i decided it would be best to do it like this.
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Mark Jones on July 07, 2006, 11:40:44 PM
Dasar, have you tried stepping through your code in a debugger?

Load it using OllyDbg and keep pressing F8. This will let you see what happens as each line of code is processed. With some practice it will become trivial to fix bugs like what you are experiencing. :U
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: dsouza123 on July 08, 2006, 11:58:34 AM
Dasar

  Ossa nailed it, your program has one show stopping bug that kept your proc from working.
edx has an address not an index into the string.
Fortunately with 1 more instruction the procedure, thus program, works as intended.


    inc edx         ; get the position of first char of the Real File Name :-)

    mov eax, lpPath

                    ; this is the critical missing instruction, with this your program works !
    sub edx, eax    ; because edx is an address, to turn it into an index, subtract the address of the start of the string



dsouza123
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: dsouza123 on July 08, 2006, 01:35:07 PM
Dasar

  Since you are already are scanning the entire string for the terminating 0,
the test for a \ can be integrated at no cost, and omit later rescanning the string for the \.
  A convention: esi has the address of a source string, edi has the address of a destination string.

dsouza123


GetFileName proc lpPath:DWORD, dstBuffer:DWORD
    push esi
    push edi

    mov esi, lpPath ; get the address of lpPath
    mov eax, esi    ; eax has address of lpPath, and will have a valid address from the string just in case

    @@:
    mov cl, BYTE PTR [esi]
    add esi, 1
    cmp cl, "\"
    jne pastbackslashtest
    mov eax, esi    ; eax now has the address of the first char after "\" char
pastbackslashtest:
    cmp cl, 0       ; used this, because i think there is a "zero" after the string
    jne @B

    xor edx, edx    ; edx = 0, for using it in counting

    mov edi, dstBuffer   ; put the address of dstBuffer in "edi"

    @@:
    mov cl, BYTE PTR [eax + edx]
    mov BYTE PTR [edi + edx], cl
    inc edx
    cmp cl, 0       ; used this, because i think there is a "zero" after the string
    jne @B

    pop edi
    pop esi         ; get back the value which was in esi

    mov eax, edx    ; the return value is the length of the FileName  :)

    ret
GetFileName endp
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Dasar on July 08, 2006, 01:51:20 PM
@ Ratch


QuoteI have translated that program from C to MASM if you are interested

yes, if you can, please give me the code, i might need it oneday soon :)



@White Scorpion

QuoteFunny...
I wrote exactly the same function for a program of mine. it even has the same name

lol, yes that was funny :D  , and thank you alot for your code :)


@ Casper

QuoteThis is probably just an oversight but I thought I better mention it because there are those who just copy and paste code that we post.

thank you for the note, btw i'm one of those whom do not use copy/paste  :D



@Mark Jones

no, i didn't use a debugger, because i nearly don't know anything about debugging, but it seems that i will have an experience with them soon :)

thank you for your reply



@dsouza123

thank you alot for your help and comments  :-)
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Jimg on July 08, 2006, 02:21:31 PM
Another thing you might consider is just saving the name the first time through the string-

GetFileName proc uses esi edi lpPath:DWORD, dstBuffer:DWORD

       mov esi,lpPath     ; where to get the input
reset: mov edi,dstBuffer  ; where to store the output
@@:    lodsb              ; get a character
       cmp al,'\'         ; is it start of possible file name?
       je reset           ; yes, discard this character and reset output address
       stosb              ; no, save possible file name character
       or al,al           ; are we done?
       jne @b             ; no, get another character

       mov eax,edi        ; compute length of output
       sub eax,dstBuffer
       dec eax
       ret
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Dasar on July 08, 2006, 02:41:22 PM
thank you Jimg :)
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Ratch on July 08, 2006, 04:09:22 PM
Dasar,
     OK, here it is all ZIPped up.  I know it is not quite what you asked for, but it does show show how to use the Common Dialog Boxes to interrogate the user for a file name.  There are two executables; one from a MASM build and the other from a C build by Charles Petzold.  You can tell from the size which one is MASM or C.  It's a big program for a beginner, so ask if you have any questions.  Ratch

[attachment deleted by admin]
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Hjortur on July 11, 2006, 07:00:11 AM
Hi, I am pretty new to MASM32 assembly, but I have used GAS (Gnu assembler) for a while.  But anyway I want to ask, whould it not be possible to "declear" (not sure how it is spelled) in the .data section something like this


  .data
  mypath db "c:/myfolder/somefile.asd",0
  dummybyte db "A"


and then work your way backwards from the address of dummybyte and look for the first "/" (last?) to show up?
or does MASM do some alignement for you so e.g if  dummybyte's address is 43678 would byte at address 43677 be the 0 at the end of mypath string?

Sorry for my not so good English, I am from Iceland so I am not a native English speaker. But I try to speak correctly

thx in advance
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: hutch-- on July 11, 2006, 07:13:02 AM
Hi Hjortur,

Welcome on board. Let me see if I understand you correctly, you want to be able to overwrite the path "mypath" with some other data ?

If its just getting the file name without the attached path, that easy to do. Start with the address of "mypath", scan forward and store the offset of each "/" character in the same register. When you get to the terminating zero then either copy the data from the offset of the last "/" to another buffer or just return the value of the offset added to the address. Either method will work fine.
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Shantanu Gadgil on July 11, 2006, 09:52:10 AM
To hutch,
What he wants to ask (I think) is "Wouldn't be possible to just go _back_ from "dummybyte", rather than going "forward and back" as shown in the code?"

To Hjortur,
Yes, it is possible...but ONLY in this case. :)

The problems with that algorithm:
What if the data is changing in the variable "mypath"?
What if some shorter name, than the original, were to overwrite in place of the declared "mypath", the efffective value of "mypath" would be "C:/ZZZ/\0lder/somefile.asd",0 (The \0 is ASCII ZERO ofcourse :) :) )

See...like this many other problems are there.

So it would be appropriate to declare "mypath" as some big buffer (usually MAX_PATH) and manipulate from front.

There is very less "sanity checking" in the code I gave, as to, what if there is NO '\' in the text :eek :eek

HTH,
Shantanu
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: dsouza123 on July 11, 2006, 12:25:10 PM

.data
  mypath db "c:\fairlylongdirectoryname\secondleveldir\filename.ext",0
  dummybyte db "A"

.code

  mypath gets overwritten to
  "c:\small\newname.ext",0,yname\secondleveldir\filename.ext",0


now searching backwards from the "A" returns the wrong filename.

Searching from the end would be quicker but if the string gets modified, it may give the wrong result.
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Casper on July 11, 2006, 01:42:03 PM
This is exactly the reason why it is always good programming practice to zero a buffer before it is written to.  By the way, if the buffer is cleared first, then a reverse search would first search for the first occurrence of a nonzero value then parse to the first occurrence of '\' while watching for the start of the buffer which means there is no '\'

A good exercise in programming basics, all around.
Paul
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Dasar on July 12, 2006, 06:50:32 AM
Thank you Ratch
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Ratch on July 12, 2006, 07:41:17 AM
Dasar,

     Ok, I think I have something you are looking for and can use.  It is a subroutine that enables you to find the filename in a directory string without knowing the length of the string, performing backward searches,  complex parsing, zeroing buffers, or otherwise getting tied up in a knot.  The subroutine is something I wrote a while back called MFNDCHAR (Multiple Find Characters).  Using an advanced algo, it will search a string for as many characters as you want to push onto the stack.  It will then spit out the address in EAX upon finding the first instance of any of the search characters.  It searches a string in DWORD gulps, instead of teensy BYTE comparisons.  The parameters are "sticky", meaning that the subroutine does not remove them from the stack.  This means they can be used for another search without PUSHing the same parameters all over again.  It also means that the user is responsible for balancing the stack after the last subroutine call.  It is also important that a zero be PUSHed onto the stack before any other parameter.  The example given shows the algo for filename searches.  Simply find each backward slash and put the next character address into a variable called SaveAdr.  When a zero at the end of the string is encountered, the last value of SaveAdr is the filename.  It is all in the zipped file.  Ratch

[attachment deleted by admin]
Title: Re: i need help, to complete my first function in masm32 "GetFileName" :)...
Post by: Hjortur on July 12, 2006, 08:04:43 PM
thx shantanu_gadgil you guessed what I was trying to say.  And thank you for clearing this for me.