I was looking for a way to create a long pathname and found out CreateDirectory only creates single folder, so i wrote this function, is this the best way to do this? or is there a better way
;**************************************************************************
; Create each directory in turn from a folderpath
;**************************************************************************
CreateDirectoryPath PROC szFolderPath:DWORD
LOCAL FolderPathToCreate[MAX_PATH]:BYTE
push esi
push edi
mov esi, szFolderPath
lea edi, FolderPathToCreate
@@:
movzx eax, byte ptr [esi] ; read byte from address in esi
inc esi
cmp al, 0 ; test for zero
je ExitLoop ; if null byte then we exit our loop
cmp al, "\" ; test for "\" - means we hit an end of a folder whilst looping through each byte
jne LoopAgain ; jump over if its not, otherwise create folder path up to this point
mov byte ptr [edi], al ; write '\' byte to address in edi as edi contains our path up to this point
mov byte ptr [edi+1], 0 ; terminate path for the moment, whilst we create folders up to this point
Invoke CreateDirectory, Addr FolderPathToCreate, NULL
inc edi ; inc edi ready for next byte we will get going round loop again
jmp @B
LoopAgain:
mov byte ptr [edi], al ; write byte to address in edi
inc edi ; inc edi ready for next byte we will get going round loop again
jmp @B
ExitLoop:
mov byte ptr [edi], 0 ; terminate path and create last folder possibly
Invoke CreateDirectory, Addr FolderPathToCreate, NULL
mov eax, TRUE
pop edi
pop esi
ret
CreateDirectoryPath endp
Hi
I have 2 procs in the OA32 library, one for ANSI and one for UNICODE strings. The difference with your proc is that they first search if the directory exists and if not, up to this point it starts to create the folders.
Regards, Biterider
invoke CreateDirectoryA, addr bBuffer, NULL
or esi, esi
jz @@Exit
inc esi
jmp @@1
Don't you think a little check would be necessary here? Especially since you don't check after FindFirstFileA if the found item is really a folder (it could be a file - rare case but there are people who create files with no extension :bg)
Very similar is this one,who made a test before create the directory:
only directory are tested,DesPath is the full path of a file.
Quote
create_directory:
push edi
push esi
invoke lstrcpy,addr repertoire_destination,addr DesPath
lea edi,repertoire_destination
@@:
.if byte ptr [edi] != "\" && byte ptr [edi] != 0 ;pass the first e:\ minimum
inc edi
jmp @B
.endif
loop_create:
.if byte ptr [edi] == 0
jmp findecreate
.endif
.if byte ptr [edi] != "\" ;chercher \
inc edi
jmp loop_create
.endif
mov byte ptr [edi],0 ;efface le \ pour test
invoke FindFirstFile,addr repertoire_destination,addr FindFileData
.if eax != INVALID_HANDLE_VALUE
invoke FindClose,eax
.else
invoke CreateDirectory,addr repertoire_destination,NULL
.endif
mov byte ptr [edi],"\" ;efface le \ pour test
inc edi
jmp loop_create
findecreate:
pop esi
pop edi
findecreate_directory:
My mechanism is very similar, although using GetFileAttributes() is way more effective/efficient than FindFirstFile().
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364944%28v=vs.85%29.aspx
INVALID_FILE_ATTRIBUTES = -1 = INVALID_HANDLE_VALUE, it's missing from a lot of the include files. As far as I'm aware this function has existed since the dawn of Win32, not just XP (per MSDN), it's in WINBASE.H circa 1993.
You should also probably return the error code for the last CreateDirectory(), so you can catch the error rather than wait to fail with a subsequent CreateFile() call.
Also, you could use the path in-place by saving the directory marker replaced with a NUL at each iteration. This would also handle the condition where the passed in string exceeds MAX_PATH, but clearly not where the string is 'const' or in the code section. Depends on your code, and how it is called.
Consider also if you want to pass in a fully qualified path, or a network path, ie \\SERVER\Foo\Bar or C:\Foo\Bar. I mention this because it is probably cheaper to let CreateFile() fail once, then try to build out the tree, and try CreateFile() again if you are writing out a lot of subsequent files to the same directory.
QuoteINVALID_FILE_ATTRIBUTES = -1 = INVALID_HANDLE_VALUE, it's missing from a lot of the include files.
also missing is the WIN32_FILE_ATTRIBUTE_DATA structure definition
WIN32_FILE_ATTRIBUTE_DATA STRUCT
dwFileAttributes dd ?
ftCreationTime FILETIME <>
ftLastAccessTime FILETIME <>
ftLastWriteTime FILETIME <>
nFileSizeHigh dd ?
nFileSizeLow dd ?
WIN32_FILE_ATTRIBUTE_DATA ENDS
expanded, it looks like this
;WIN32_FILE_ATTRIBUTE_DATA STRUCT
; dwFileAttributes dd ?
; ftCreationTime FILETIME <>
; dwLowDateTime dd ?
; dwHighDateTime dd ?
; ftLastAccessTime FILETIME <>
; dwLowDateTime dd ?
; dwHighDateTime dd ?
; ftLastWriteTime FILETIME <>
; dwLowDateTime dd ?
; dwHighDateTime dd ?
; nFileSizeHigh dd ?
; nFileSizeLow dd ?
This example will create VERY FAST a full path dirs "c:\Lingo1\Lingo2\Lingo3\" on your disk C:
.686
.xmm
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
;****************************************
OBJECT_ATTRIBUTES struc
Len dd ?
RootDirectory dd ?
ObjectName dd ?
Attributes dd ?
SecurityDescriptor dd ?
SecurityQualityOfService dd ?
OBJECT_ATTRIBUTES ends
IO_STATUS_BLOCK struc
Status dd ?
Pointer dd ?
Information dd ?
IO_STATUS_BLOCK ends
;****************************************
.const
OBJ_CASE_INSENSITIVE equ 40h
FILE_DIRECTORY_FILE equ 1
FILE_CREATE equ 2
FILE_SUPERSEDED equ 0
FILE_EXISTS equ 4
FILE_SYNCHRONOUS_IO_NONALERT equ 20h
FILE_RESERVE_OPFILTER equ 100000h
FILE_OPEN_FOR_BACKUP_INTENT equ 4000h
.data?
Iostatblock IO_STATUS_BLOCK <?>
RootDirectoryHandlez dd ?
aNtCreateFile dd ?
aNtClose dd ?
.data
align 16
db 8 Dup(0)
RootDirectoryNamez db "\",0,"?",0,"?",0,"\",0
Drive db "C",0,":",0,"\",0
DirNames db "L",0,"i",0,"n",0,"g",0,"o",0,"1",0,"\",0
db "L",0,"i",0,"n",0,"g",0,"o",0,"2",0,"\",0
db "L",0,"i",0,"n",0,"g",0,"o",0,"3",0,"\",0
DirNamesEx db (32767*2 ) Dup(0)
align 16
Oaz dd sizeof OBJECT_ATTRIBUTES,0, RootDirectoryNameBufz,OBJ_CASE_INSENSITIVE,0,0
align 16
RootDirectoryNameBufz db 0,0,0,0
RootNOffsetz dd offset RootDirectoryNamez,0,0,0,0
szNTdll db "ntdll.dll",0
szNtCreateFile db "ZwCreateFile",0
szNtClose db "ZwClose",0
.code
option prologue:none
option epilogue:none
start:
invoke GetModuleHandle, addr szNTdll
mov esi, eax
invoke GetProcAddress, esi, addr szNtCreateFile
mov aNtCreateFile, eax
invoke GetProcAddress, esi, addr szNtClose
mov aNtClose, eax
mov esi, offset DirNames
call CreatePath ; return 0 if success or -1 if error
push 0
jmp ExitProcess
align 8
CreatePath:
cmp word ptr [esi],0 ; Looking for "\" or zero
je Exit
cmp byte ptr [esi],"\"
jne @f
mov byte ptr [esi], 0 ; esi->current offset in Buffer
mov eax, esi
sub eax, offset RootDirectoryNamez
mov dword ptr RootDirectoryNameBufz, eax
add eax, 2
mov word ptr RootDirectoryNameBufz+2, ax
call CreateDir
mov byte ptr [esi], "\"
test eax, eax ; STATUS_SUCCESS equ 0
jne ErrorExit
@@:
add esi,1
jne CreatePath
Exit:
xor eax, eax
ret
ErrorExit:
or eax, -1
ret
align 8
CreateDir:
push 0
push 0
push FILE_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT or FILE_OPEN_FOR_BACKUP_INTENT
push FILE_CREATE
push FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE
push FILE_ATTRIBUTE_DIRECTORY
push 0
push offset Iostatblock
push offset Oaz
push FILE_LIST_DIRECTORY or FILE_RESERVE_OPFILTER
push offset RootDirectoryHandlez
call aNtCreateFile
test eax, eax ; STATUS_SUCCESS equ 0
jne Not1
push RootDirectoryHandlez
call aNtClose
Yes1:
xor eax, eax ; STATUS_SUCCESS equ 0
ret
Not1:
cmp Iostatblock.Information, FILE_SUPERSEDED
je Yes1
cmp Iostatblock.Information, FILE_EXISTS
je Yes1
ret
end start
Quote from: clive on October 02, 2011, 10:30:21 AM
My mechanism is very similar, although using GetFileAttributes() is way more effective/efficient than FindFirstFile().
Good hint, Clive :U
Here is my implementation - 110 bytes, less than 7 paras, .const section-proof, and it
does fail if there is a file around with a desired folder name.
include \masm32\include\masm32rt.inc
.data
ThePath db "\masm32\examples\NoSuchFolder\One more", 0 ; if a file with this name is present, the algo will fail
.code
start:
push offset ThePath
call MbMkDir
.if eax!=16
print str$(eax), " - failed!!", 13, 10
mov eax, LastError$()
print eax, 9, " - but check for files with the same name!!", 13, 10
.else
print str$(eax), " - success!!", 13, 10
.endif
inkey "Bye"
exit
MbMkDir proc ; arg is pPath; returns 16 if success, zero for failure
push esi
push edi
push ebx
push ecx
mov ebx, esp ; shortest solution
sub esp, MAX_PATH ; create a buffer for the copy
mov edi, esp ; edi points to buffer
invoke lstrcpy, edi, [ebx+4*4+4] ; src=pPath
xchg ecx, len(edi) ; set counter for scasb
push edi
pop esi
NxtFile: mov al, "\"
repne scasb ; find the next backslash
dec edi
.if byte ptr [edi-1]!=":" && esi!=edi ; drives and leading \ should be ignored
.if !ecx && [edi]!=al ; last element may or may not have backslash
inc edi
.endif
mov byte ptr [edi], 0 ; zero-delimit current path
push ecx
invoke GetFileAttributes, esi
pop ecx
.if eax==INVALID_FILE_ATTRIBUTES ; folder not found
push ecx
invoke CreateDirectory, esi, 0
pop ecx
test eax, eax
je @Err
mov al, 16 ; folder successfully created
.endif
and eax, 16 ; really a folder?
je @Err
mov byte ptr [edi], "\" ; restore full path
.endif
inc edi
test ecx, ecx
ja NxtFile
@Err: mov esp, ebx
pop ecx
pop ebx
pop edi
pop esi
retn 4
MbMkDir endp
@Lingo: If I ever need to create a Million folders in less than 10 milliseconds, can I use your algo, or is it copyrighted?
I wonder why you need to have a "fail" if the directory already exists ? What is wrong with the idea of doing a "change directory" first and if that fails, create the new directory ?
When you are finished playing games, try this Shell API.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
comment * ------------------------------------------------------------------------------------------
Use the MASM32 Dialog Help from the HELP menu as reference
when building dialog applications using this type of code
------------------------------------------------------------------------------------------ *
DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
enable_common_controls PROTO
.data?
hWnd dd ?
hInstance dd ?
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
start:
invoke enable_common_controls
mov hInstance, rv(GetModuleHandle,NULL)
call main
invoke ExitProcess,eax
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
Dialog "SHCreateDirectoryEx Test", \ ; caption
"MS Sans Serif",10, \ ; font,pointsize
WS_OVERLAPPED or \ ; styles for
WS_SYSMENU or DS_CENTER, \ ; dialog window
3, \ ; number of controls
50,50,155,100, \ ; x y co-ordinates
1024 ; memory buffer size
DlgButton "Build Tree",WS_TABSTOP,106,5,40,13,IDOK
DlgButton "Cancel",WS_TABSTOP,106,20,40,13,IDCANCEL
DlgStatic "Cick the Build Tree Button",SS_LEFT,5,5,90,9,100
CallModalDialog hInstance,0,DlgProc,NULL
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
DlgProc proc hWin:DWORD,uMsg:DWORD,wParam:DWORD,lParam:DWORD
LOCAL pbuf :DWORD
LOCAL buffer[260]:BYTE
Switch uMsg
Case WM_INITDIALOG
invoke SendMessage,hWin,WM_SETICON,1,rv(LoadIcon,NULL,IDI_ASTERISK)
m2m hWnd, hWin
return 1
Case WM_COMMAND
Switch wParam
Case IDOK
mov pbuf, ptr$(buffer)
invoke GetCurrentDirectory,260,pbuf
mov pbuf, cat$(pbuf,"\test1\test2\test3\test4")
invoke SHCreateDirectoryEx,hWin,pbuf,NULL
mov pbuf, ptr$(buffer)
invoke GetCurrentDirectory,260,pbuf
mov pbuf, cat$(pbuf,"\test1\test2\test3\test44")
invoke SHCreateDirectoryEx,hWin,pbuf,NULL
mov pbuf, ptr$(buffer)
invoke GetCurrentDirectory,260,pbuf
mov pbuf, cat$(pbuf,"\test1\test2\test3\test444")
invoke SHCreateDirectoryEx,hWin,pbuf,NULL
fn MessageBox,hWin,"The game is done, I've won I've won quote she and whistled thrice.","Coleridge",MB_OK
Case IDCANCEL
jmp quit_dialog
EndSw
Case WM_CLOSE
quit_dialog:
invoke EndDialog,hWin,0
EndSw
return 0
DlgProc endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
enable_common_controls proc
LOCAL icce:INITCOMMONCONTROLSEX
; --------------------------------------
; comment out the styles you don't need.
; --------------------------------------
mov icce.dwSize, SIZEOF INITCOMMONCONTROLSEX ; set the structure size
xor eax, eax ; set EAX to zero
; or eax, ICC_ANIMATE_CLASS ; OR as many styles as you need to it
; or eax, ICC_BAR_CLASSES ; comment out the rest
; or eax, ICC_COOL_CLASSES
; or eax, ICC_DATE_CLASSES
; or eax, ICC_HOTKEY_CLASS
; or eax, ICC_INTERNET_CLASSES
; or eax, ICC_LISTVIEW_CLASSES
; or eax, ICC_PAGESCROLLER_CLASS
; or eax, ICC_PROGRESS_CLASS
; or eax, ICC_TAB_CLASSES
; or eax, ICC_TREEVIEW_CLASSES
; or eax, ICC_UPDOWN_CLASS
; or eax, ICC_USEREX_CLASSES
; or eax, ICC_WIN95_CLASSES
mov icce.dwICC, eax
invoke InitCommonControlsEx,ADDR icce ; initialise the common control library
; --------------------------------------
ret
enable_common_controls endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
:bg
Quote from: hutch-- on October 03, 2011, 12:03:53 AM
I wonder why you need to have a "fail" if the directory already exists ?
Creating C:\WINDOWS\system32\drivers\etc\hosts\One more folder: 0 - failed!!
Creating C:\WINDOWS\system32\drivers\etc\hosts2\One more folder: 16 - success!!
Try the same with the other algos posted.
QuoteC:\WINDOWS\system32\drivers\etc
i might be more inclined to test it on a more benign folder, however
a bug in your code might do some damage :P
Quote from: dedndaveQuoteC:\WINDOWS\system32\drivers\etc
i might be more inclined to test it on a more benign folder, however a bug in your code might do some damage :P
I should post my aggressive tree pruning counter-play to CreateDirectoryPath(), deletes everything in the tree, then the directory itself, much hilarity will ensue...
Back in the 80's we had a "DEL *.*" and "FORMAT C:" award for the employee of the week who managed to cause the most self-inflicted damage to their computer system. The computer equivalent to the Darwin Award.