Wie komme ich an die Details im Windows-Explorer?
Der Windows Explorer (nicht der Internet Explorer) ist der
Standard-Dateimanager in Windows. Hiermit kann man sich die hierarchische
Struktur der Dateien, Ordner und Laufwerke auf dem Computer zeigen lassen sowie
erstellen, kopieren, verschieben und umbenennen.
Über Erweiterungen sind weitere und neue Operationen möglich.
In der Details-Ansicht werden auf der rechten Seite Dateien und Ordner mit der
Angabe einiger Details - wie Name, Größe, Typ, Datum - gezeigt:
Welche Details angezeigt werden, kann man einstellen, indem man über die Zeile
mit den Spaltenüberschriften zieht, auf die rechte Maustaste klickt und
mit der linken Maustaste das gewünschte Detail anklickt:
Es gibt eine Menge Details, an die man gerne auch mit einem Programm kommen möchte.
Die zuständige Shell-Methode für diese Details heißt ab Win2000
IShellFolder2::GetDetailsOf .
Für ältere Windows-Versionen hat Microsoft die zunächst undokumentierte Methode
IShellDetails::GetDetailsOf
nun dokumentiert. Hier wird sie nicht weiter vertieft.
Als Shell-Objekt bzw. OLE-Objekt ist es nicht ganz einfach sich diese Methode
dienstbar zu machen. Der Gang ist folgendermaßen:
CoInitialize,ShGetMalloc
GetShDeskTopFolder
DeskTop-Folder -> ParseDisplayName
DeskTop-Folder -> BindToObject
MeinFolder -> ParseDisplayName
pMeinFolder -> QueryInterface
pShellFolder2->GetDetailsOf, StrRetToStr
Nasm & GoLink
; Assemblieren: nasm.exe -fwin32 -o <Name>.obj <NameExt>.nasm
; Linken: GoLink.exe /console /entry _main <Name>.obj
; kernel32.dll shell32.dll shlwapi.dll ole32.dll user32.dll
; Aufrufen: <Name>.exe <VollerPfad>
; Achtung: 1) Keinerlei Fehlerprüfung
; 2) Die gewünschte Datei muss mit vollem Pfad angegeben werden,
; z.B.: c:\tmp\test.jpg
BITS 32
EXTERN GetCommandLineW, LocalFree, \
AttachConsole, GetStdHandle, WriteFile ; kernel32.dll
EXTERN CommandLineToArgvW, SHGetMalloc, SHGetDesktopFolder ; shell32.dll
EXTERN StrRetToStrA ; shlwapi.dll
EXTERN CoInitialize,CoUninitialize,CoTaskMemFree ; ole32.dll
EXTERN CharToOemA, wsprintfA ; user32.dll
SECTION .data
IID_IShellFolder: ; 000214E6-0000-0000-C000-000000000046
dd 0x000214E6
dw 0,0
db 0xC0,0,0,0,0,0,0,0x46
IID_IShellFolder2: ; 93F2F68C-1D1B-11d3-A30E-00C04F79ABD1
dd 0x93F2F68C
dw 0x1D1B,0x11d3
db 0xA3,0x0E,0,0xC0,0x4F,0x79,0xAB,0xD1
wsprintfFormat db '%2d %s: %s',10,0
SECTION .bss
pMalloc RESD 1
pDesktopFolder RESD 1
pMeinFolder RESD 1
pShellFolder2 RESD 1
pidlPfad RESD 1
pidlDatei RESD 1
pTitel RESD 1
pDetail RESD 1
pDriveDir RESD 1
pNameExt RESD 1
szArglist RESD 1 ; CommandLineToArgvW
nArgs RESD 1 ; CommandLineToArgvW
len RESD 1 ; Länge von echo (Resultat wsprintfA)
dummy RESD 1
sd RESB 272 ; SHELLDETAILS
echo RESD 260
SECTION .text
GLOBAL _main
_main:
; 1. Kommandozeilenargument splitten: drive:\dir\name.ext -> drive:\dir<null>name.ext
call GetCommandLineW
push nArgs ; int *pNumArgs
push eax ; LPCWSTR lpCmdLine von GetCommandLineW
call CommandLineToArgvW
mov [szArglist], eax
mov esi, [eax+4] ; 1. Parameter
mov [pDriveDir], esi
.A: ; Dateinamen-Backslash suchen und nullen
lodsw ; UTF16-Buchstabe (2 Bytes)
cmp ax, '\' ; Backslash?
cmovz edx, esi ; if (z) {edx = esi}
test ax, ax ; Ende der Kommandozeile?
jne .A ; nein: Schleife
mov word [edx-2], 0 ; letzten Backslash nullen
mov [pNameExt], edx ; Zeiger auf Dateinamen
; CoInitialize (NULL)
push 0
call CoInitialize
; Memory Manager zur Befreiung der PIDLs
push pMalloc ; LPMALLOC *ppMalloc
call SHGetMalloc
; IShellFolder
push pDesktopFolder ; LPSHELLFOLDER *ppshf
call SHGetDesktopFolder
; Dir -> ITEMIDLIST (pDesktopFolder->ParseDisplayName)
push 0 ; ULONG *pdwAttributes
push pidlPfad ; PIDLIST_RELATIVE *ppidl
push 0 ; ULONG *pchEaten
push dword [pDriveDir] ; LPWSTR pszDisplayName
push 0 ; IBindCtx *pbc
push 0 ; HWND hwnd
mov edx, [pDesktopFolder] ; this
push edx
mov eax, [edx]
call [eax+12] ; IShellFolder::ParseDisplayName
; pMeinFolder = pDesktopFolder->BindToObject
push pMeinFolder ; VOID** ppvResult
push IID_IShellFolder ; GUID* riid,
push 0 ; IBindCtx* pbc,
push dword [pidlPfad] ; LPCITEMIDLIST pidl
mov edx, [pDesktopFolder] ; this
push edx
mov eax, [edx]
call [eax+20] ; IShellFolder::BindToObject
;pMalloc->Free(pidlPfad);
push dword [pidlPfad] ; LPCITEMIDLIST pidl
mov edx, [pMalloc] ; this
push edx
mov eax, [edx]
call [eax+20] ; IMalloc::Free
;pDesktopFolder->Release();
mov edx, [pDesktopFolder] ; this
push edx
mov eax, [edx]
call [eax+8] ; IShellFolder::Release
; Datei -> ITEMIDLIST (pMeinFolder->ParseDisplayName)
push 0 ; ULONG *pdwAttributes
push pidlDatei ; PIDLIST_RELATIVE *ppidl
push 0 ; ULONG *pchEaten
push dword [pNameExt] ; LPWSTR pszDisplayName
push 0 ; IBindCtx *pbc
push 0 ; HWND hwnd
mov edx, [pMeinFolder] ; this
push edx
mov eax, [edx]
call [eax+12] ; IShellFolder::ParseDisplayName
; pShellFolder2 = pMeinFolder->QueryInterface
push pShellFolder2 ; void **ppvObject
push IID_IShellFolder2 ; REFIID riid
mov edx, [pMeinFolder] ; this
push edx
mov eax, [edx]
call [eax] ; IShellFolder::QueryInterface
; pShellFolder2->AddRef()
mov edx, [pShellFolder2] ; this
push edx
mov eax, [edx]
call [eax+4] ; IShellFolder2::AddRef
; for (esi=0;;++esi) esi ist die laufende Nummer der Spalte
xor esi, esi
not esi
.FOR:
add esi, 1
; pShellFolder2->GetDetailsOf(NULL, i, &sd)
push sd ; SHELLDETAILS *psd
push esi ; UINT iColumn
push 0 ; PCUITEMID_CHILD pidl = 0 (Spaltentitel ermitteln)
mov edx, [pShellFolder2] ; this
push edx
mov eax, [edx]
call [eax+72] ; IShellFolder2::GetDetailsOf
test eax, eax
jne .ENDFOR ; keine Spaltentitel mehr: Break
; StrRetToStr (&sd.str,NULL,&pTitel)
push pTitel ; LPTSTR *ppszName
push 0 ; PCUITEMID_CHILD pidl
push (sd + 8) ; STRRET *pstr (SHELLDETAILS.str)
call StrRetToStrA
; pShellFolder2->GetDetailsOf(pidlDatei, i, &sd)
push sd ; SHELLDETAILS *psd
push esi ; UINT iColumn
push dword [pidlDatei] ; PCUITEMID_CHILD pidl
mov edx, [pShellFolder2] ; this
push edx
mov eax, [edx]
call [eax+72] ; IShellFolder2::GetDetailsOf
test eax, eax ; Detail gefunden?
jnz short .FOR ; Nein: Continue
; StrRetToStr (&sd.str,NULL,&pDetail)
push pDetail ; LPTSTR *ppszName
push 0 ; PCUITEMID_CHILD pidl
push (sd + 8) ; STRRET *pstr (SHELLDETAILS.str)
call StrRetToStrA
; sprintf (echo,"%2d %s: %s\n",i,pTitel,pDetail)
push dword [pDetail]
push dword [pTitel]
push esi
push wsprintfFormat ; LPCTSTR lpFmt
push echo ; LPTSTR lpOut
call wsprintfA
add esp, 20 ; cdecl
mov [len], eax ; Länge speichern für WriteFile
; CoTaskMemFree (pTitel); CoTaskMemFree (pDetail)
push dword [pTitel] ; LPVOID pv
call CoTaskMemFree
push dword [pDetail] ; LPVOID pv
call CoTaskMemFree
; CharToOem (Ansi,Oem) - auskommentieren, wenn ANSI-Ausgabe gewünscht
push echo ; LPSTR lpszDst
push echo ; LPCTSTR lpszSrc,
call CharToOemA
; Ausgabe auf StdOut
push -11 ; DWORD nStdHandle = STD_OUTPUT_HANDLE
call GetStdHandle
push 0 ; LPOVERLAPPED lpOverlapped
push dummy ; LPDWORD lpNumberOfBytesWritten
push dword [len] ; DWORD nNumberOfBytesToWrite
push echo ; LPCVOID lpBuffer
push eax ; HANDLE hFile: StdOut von GetStdHandle
call WriteFile
jmp near .FOR
.ENDFOR:
;pMalloc->Free(pidlDatei);
push dword [pidlDatei] ; LPCITEMIDLIST pidl
mov edx, [pMalloc] ; this
push edx
mov eax, [edx]
call [eax+20] ; IMalloc::Free
;pMeinFolder->Release();
mov edx, [pMeinFolder] ; this
push edx
mov eax, [edx]
call [eax+8] ; IShellFolder::Release
; pMalloc -> Release
mov edx, [pMalloc] ; this
push edx
mov eax, [edx]
call [eax+8] ; IMalloc::Release
; CoUninitialize ()
call CoUninitialize
; Kommondozeilenargumente befreien
push dword [szArglist] ; HLOCAL hMem
call LocalFree
; Ende
mov eax, 0 ; Exitcode
ret ; Ende
Weitere Informationen:
Using COM in Assembly Language
http://www.codebreakers-journal.com/content/view/166/96/
Ralph 'rkhb' Bauer März 2009