News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

LVM_GETITEMTEXT working in XP but not windows 7

Started by jimg, February 11, 2013, 04:09:57 PM

Previous topic - Next topic

jimg

I stopped programming a few years ago for various reasons, but now I am trying to get an old program of mine working.  It works on windows XP, but I cannot figure out how to get it to work on Windows 7, 64 bit.

The attached test code was extracted from the program I'm trying to fix.  It finds the windows desktop listview, and prints out the position and caption of each item on the desktop.  On Windows 7 64bit, it prints the correct handles as determined by winspy, and the correct number of icons, and their correct positions, so I'm fairly sure I'm finding the listview properly, but for some reason LVM_GETITEMTEXT always return zero.

This is a console program.

Any help with this will be greatly appreciated.

Update:    See http://masm32.com/board/index.php?topic=1464.msg15440#msg15440 for solution.

dedndave

that'll teach ya to leave   :P

the only thing that comes to mind is the difference in window hierarchy for controls under win7
but, if you've looked at it with spy++, i would think you have already noticed that
maybe take a snapshot of the windows in one os and do a side-by-side comparison

sinsi

The order is strange in win7, here's how I find the listview

.const
workerw BYTE 'WorkerW',0
shellv  BYTE 'SHELLDLL_DefView',0
lview   BYTE 'SysListView32',0

.code
gethandle proc uses ebx esi ;returns EAX=handle else 0
sub esi,esi
   @@:  INVOKE FindWindowEx,0,esi,offset workerw,0
        test eax,eax
        jz @f
        mov esi,eax
        INVOKE FindWindowEx,eax,0,offset shellv,0
        test eax,eax
        jz @b
        INVOKE FindWindowEx,eax,0,offset lview,0
@@: ret
gethandle endp

Unfortunately there is also something called "desktop isolation" (or similar) which means not all info is available.
I think I can get icon positions but not text - can't remember, just gave up :(

japheth


I didn't test it, but there's a bug in the code: you may not assume that member pszText in LV_ITEM keeps the initial value - it may be changed by Windows - and further calls with this changed value have a high risk to fail.

sinsi

Quote from: japheth on February 11, 2013, 08:23:56 PM

I didn't test it, but there's a bug in the code: you may not assume that member pszText in LV_ITEM keeps the initial value - it may be changed by Windows - and further calls with this changed value have a high risk to fail.
I found that out the hard way, here's what I do, works in XP but not Vista+

gettext PROC USES EBX ESI EDI hwnd:DWORD,hprocess:DWORD,hprocessmem:DWORD,upto:DWORD,buff:DWORD
    LOCAL item:LVITEM

    INVOKE RtlZeroMemory,ADDR item,SIZEOF item
    mov esi,hprocessmem
    mov ebx,hprocess

    lea eax,[esi+SIZEOF item]
    push 1
    pop item._mask
    push upto
    pop item.iItem
    mov item.pszText,eax
    push 260
    pop item.cchTextMax

    INVOKE WriteProcessMemory,ebx,esi,ADDR item,SIZEOF item,0
    INVOKE SendMessage,hwnd,LVM_GETITEMTEXT,upto,esi
    mov edi,eax
    INVOKE ReadProcessMemory,ebx,item.pszText,buff,eax,0
    mov eax,edi
    ret
gettext endp

Even as admin I get a null string, same code worked OK in XP.

japheth

Quote from: sinsi on February 11, 2013, 08:42:54 PM
I found that out the hard way, here's what I do, works in XP but not Vista+

I see. However, in your code, shouldn't you first read the LV_ITEM structure back and THEN use the pszText member?

Btw, I found, at second glance, that Jimg does check in his code the "pszText has been changed"-case. Never mind.

sinsi

Quote from: japheth on February 11, 2013, 10:19:23 PM
I see. However, in your code, shouldn't you first read the LV_ITEM structure back and THEN use the pszText member?
Thanks for spotting that. I guess I have been lucky in that Windows doesn't change it.

jimg

I've finally figured out what the problem is, but not the solution.  Using Spy++ I found that the style of the desktop listview on windows 7 64 bit is LVS_OWNERDATA.   Microsoft specifically says "LVM_GETITEMTEXT is not supported under the LVS_OWNERDATA style."  This means it's a virtual listview, and doesn't keep the info around but asks the parent for it if it needs it.  Microsoft doesn't say anything about what to use instead.  I guess the only way would be to ask the parent ("SHELLDLL_DefView") for the info but have no idea how to get it to send to me instead of the listview.

Anyone have any ideas?  Anyone heard from Donkey lately?  He seems to be very proficient at figuring out this type of stuff.

dedndave

you can determine the "worker" control window handles
but, that's probably not necessary

it seems to me that the window would be sent a notification message of some sort

dedndave

a little different issue, but similar with regard to window hierarchy....

we wanted a GetOpenFileName dialog to appear in a specific position
because the hierarchy is different, i wrote some code to get the handles
as far as i know it works under win 7 - noone ever said that it didn't - lol

maybe you can get some ideas

http://www.masmforum.com/board/index.php?topic=16931.msg154917#msg154917

jj2007

Quote from: jimg on February 12, 2013, 12:50:11 PMhow to get it to send to me instead of the listview.

Subclassing & intercepting the appropriate message?
You might google for LVS_OWNERDATA LVM_GETITEMTEXT LVN_GETDISPINFO
... or check the NMLVDISPINFO structure:

If the LVITEM structure is receiving item text, the pszText and cchTextMax members specify the address and size of a buffer.

japheth

Quote from: jimg on February 12, 2013, 12:50:11 PM
Anyone have any ideas?

The "official" way to talk with the Windows shell is surely not to use Read/WriteProcessMemory or SendMessage.

There's a bunch of COM interfaces that are supposed to be used. The problem is that  not everything is well documented.

However, to get the text of folder items is simple:


;--- list the items on the desktop folder

.386
.model flat, stdcall
option casemap:none

.nolist
.nocref
include \wininc\include\windows.inc
include \wininc\include\shlobj.inc
include \wininc\include\shobjidl.inc
include \wininc\include\stdio.inc
.list
.cref

includelib kernel32.lib
includelib user32.lib
includelib gdi32.lib
includelib ole32.lib
includelib shell32.lib
includelib msvcrt.lib

CStr macro x:vararg
local xxx
.const
xxx db x,0
.code
exitm <offset xxx>
endm

.code

main proc

local sf:ptr IShellFolder
local il:ptr IEnumIDList
local iil:ptr ITEMIDLIST
local sr:STRRET

invoke SHGetDesktopFolder, addr sf
.if ( eax != S_OK )
invoke printf, CStr("SHGetDesktopFolder() failed [%X]",10), eax
ret
.endif

invoke vf( sf, IShellFolder, EnumObjects_ ), NULL, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS, addr il
.if ( eax != S_OK )
invoke printf, CStr("IShellFolder:EnumObjects() failed [%X]",10), eax
invoke vf( sf, IUnknown, Release )
ret
.endif

.while 1
invoke vf( il, IEnumIDList, Next ), 1, addr iil, NULL
.break .if ( eax == S_FALSE )
.if ( eax != S_OK )
invoke printf, CStr("IEnumIDList:Next() failed [%X]",10), eax
.break
.endif
invoke vf( sf, IShellFolder, GetDisplayNameOf ), iil, SHGDN_NORMAL, addr sr
.if ( eax == S_OK )
.if ( sr.uType == STRRET_CSTR )
invoke printf, CStr("Item: %s",10), addr sr.cStr
.elseif ( sr.uType == STRRET_WSTR )
invoke printf, CStr("Item: %S",10), sr.pOleStr
invoke CoTaskMemFree, sr.pOleStr
.else
invoke printf, CStr("IShellFolder:GetDisplayNameOf() returned unexpected uType=%u",10), sr.uType
.endif
.else
invoke printf, CStr("IShellFolder:GetDisplayNameOf() failed [%X]",10), eax
.endif
invoke CoTaskMemFree, iil
.endw

invoke vf( il, IUnknown, Release )
invoke vf( sf, IUnknown, Release )
ret
align 4

main endp

start:
invoke main
invoke ExitProcess, eax

end start


It's more difficult to get "UI-related" things like icon positions, because you'll need UI-related interfaces like IShellBrowser ( which, AFAICS, is only accessible for shell extensions ) or IFolderView.

Please be aware that the sample code doesn't use Masm32!

sinsi

I tried using SendMessage with LVM_EDITLABEL which is supposed to return the edit hwnd but got error 36B7 "The requested lookup key was not found in any active activation context."
Do I need a manifest or something? BTW, this is win8 but I had the same problem in win7.

Might play around with LVN_BEGINLABELEDIT, GetWindowText and LVN_ENDLABELEDIT.

jj2007

Quote from: japheth on February 12, 2013, 06:24:02 PM
However, to get the text of folder items is simple:

Provided you add somewhere
   includelib \WinInc\Lib\UUID.Lib

japheth

Quote from: jj2007 on February 12, 2013, 06:53:07 PM
Provided you add somewhere
   includelib \WinInc\Lib\UUID.Lib

Actually, that's a Masm bug: sometimes, when 2 or more externdefs ( or prototypes ) of the very same symbol name are found in the source, it assumes that it's a true external - although it is not at all referenced in the source.

If you use jwasm, the link error should disappear.

Quote
IShellBrowser ( which, AFAICS, is only accessible for shell extensions ) or IFolderView.

I meant IShellView, not IFolderView.