Read listview data from another process - windows

This is a kind of GUI automation application whereby I want to read the data from a listview from another process.
The listview class is SysListView32 and has following styles set LVS_OWNERDRAWFIXED
Generally I am able to read the text from listview using the following procedure
Allocate memory in the memory space of other process
Send message to listview to read the text with the pointer of buffer allocated in that process
Read the buffer
It works fine when the listview is not ownerdrawn but in this case, the listview appears to be drawn by the owner, i.e. the listitem has no data.
Is it possible to read the text from such a listview either by the method I have discussed or by any method or by hooking the api or whatsoever method ?

The control must still add LVITEMs to the list view. But of course there's no obligation to put anything useful in them. Specifying a null pszText or iImage would work just fine if the app does its own drawing. It will implement a WM_DRAWITEM message handler and use internal data to render the item.
There is no way to find out where that data is stored. You could fake your own WM_DRAWITEM message, albeit that it is very hard to do since you must inject code to create the HDC, but that just gets you pixels, not bytes. Using OCR would be a major outlier solution. Realistically you'll need to throw in the towel on this one.

Related

TreeView Control will leak normal ImageList but ListView control doesn't?

I was using task manager to watch my dynamically created controls and found each time I'd create a TreeView with an ImageList, the GDI objects count would increase by 4 each time I destroyed the tree and created again. However, a ListView never had a problem.
I know of the TVS_CHECKBOXES issues with the state images and was already destroying of the state imagelist, but I then implemented:
ImageList_Destroy(TreeView_SetImageList(GetHandle(), nullptr, TVSIL_NORMAL));
and now the resource leak is gone.
So far it looks like you have to manually clean up images in WM_DESTROY for the following:
Button_SetImageList() - Have to set it to switch it to `BUTTON_IMAGELIST.himl=BCCL_NOGLYPH` to clear it.
TreeView_SetImageList(LVILS_STATE) - if you set it or used `TVS_CHECKBOXES`
TreeView_SetImageList(LVILS_NORMAL) - if you set it
BM_SETIMAGE and STM_SETIMAGE - destroy your own but also set to NULL and destroy returned handle to get rid of potential hidden bitmap handle if different handle than your own.
But a ListView is different, is that by design or should I just go ahead in WM_DESTROY with something like:
ImageList_Destroy(ListView_SetImageList(GetHandle(), nullptr, LVSIL_STATE));
ImageList_Destroy(ListView_SetImageList(GetHandle(), nullptr, LVSIL_SMALL));
ImageList_Destroy(ListView_SetImageList(GetHandle(), nullptr, LVSIL_NORMAL));
Note that using WM_NCDESTROY is too late for TreeViews.
The two controls are inconsistent in this regard.
The tree control does not take ownership of the imagelist you give it, so you remain responsible for freeing it.
The listview control does take ownership, unless you set the LVS_SHAREIMAGELISTS window style.
Note that the tree control also has a related quirk; if you set the TVS_CHECKBOXES style, you are responsible for freeing the state image list even though you didn't create it.
What you are seeing is documented behavior.
For a TreeView:
TVM_SETIMAGELIST message
TreeView_SetImageList macro
The tree-view control will not destroy the image list specified with this message. Your application must destroy the image list when it is no longer needed.
As well as:
Tree-View Control Window Styles
Constant
Description
TVS_CHECKBOXES
Version 4.70. Enables check boxes for items in a tree-view control. A check box is displayed only if an image is associated with the item. When set to this style, the control effectively uses DrawFrameControl to create and set a state image list containing two images. State image 1 is the unchecked box and state image 2 is the checked box. Setting the state image to zero removes the check box altogether. For more information, see Working with state image indexes.Version 5.80. Displays a check box even if no image is associated with the item. Once a tree-view control is created with this style, the style cannot be removed. Instead, you must destroy the control and create a new one in its place. Destroying the tree-view control does not destroy the check box state image list. You must destroy it explicitly. Get the handle to the state image list by sending the tree-view control a TVM_GETIMAGELIST message. Then destroy the image list with ImageList_Destroy.If you want to use this style, you must set the TVS_CHECKBOXES style with SetWindowLong after you create the treeview control, and before you populate the tree. Otherwise, the checkboxes might appear unchecked, depending on timing issues.
Compared to a ListView:
LVM_SETIMAGELIST message
ListView_SetImageList macro
The current image list will be destroyed when the list-view control is destroyed unless the LVS_SHAREIMAGELISTS style is set. If you use this message to replace one image list with another, your application must explicitly destroy all image lists other than the current one.

Can one get grid data from a VSFlexGrid window using the WinAPI?

WinAPI can get text from particular windows. However, VSFlexGrids are resistant to this since their grid data don't seem contained in easily-accessible child windows.
I've used Spy++ to analyze the VSFlexGrid in question, and it has no children. Yet, when I click a particular grid cell, a new child window seems to be dynamically created.
Question: Given this behavior, is there an easy way to get grid data from a VSFlexGrid using the Windows API, or some other "efficient" method? I'd like not to have to scrape screenshots or click each grid cell individually, as those methods would be slower than a WinAPI call (if a WinAPI method actually exists).

Setting the text and background colours of a super classed listbox control

I am in the process of writing a super classed version of the Windows LISTBOX common-control to add extra functionality.
A standard control sends the WM_CTLCOLORLISTBOX message to its parent which allows both its text and background colours to be specified at run time within an appropriate message handler. However WM_CTLCOLORLISTBOX is not sent to the control itself, therefore cannot be encapsulated and handled internally.
The scenario I am attempting to address is to change the background and text colours depending on the control's enabled/disabled state. The standard behaviour of leaving the listbox background the same shade regardless of its state looks ugly and inconsistent to me. Is there another way to set these values within the encapsulation, yet hand-off all other painting tasks to the base-class window procedure?
I wondered about using SetClassLongPtr(). However, not only would this not address the text colour but if I understand rightly it would change the background for ALL controls of that class currently in existence and not the specific control whose state has changed.
The answer should be obvious - since WM_CTLCOLORLISTBOX is sent to the parent window, you have to subclass the parent window in order to receive the message. There is no getting around that. However, some wrapper UI frameworks, like VCL, are designed to forward such messages to the control that generated then, so controls can handle their own messages. But if you are not using a wrapper framework and are working with Win32 directly, you have to handle the parent messages yourself. Refer to MSDN for details about subclassing a given HWND:
Subclassing Controls

Windows Phone 7.1 data binding confusion

I'm trying to work out how to catch a data binding in the act (either intercept or post-process) to customize the display of data in the target control.
I know about IValueConvertor and understand that I can transform a simple value into another simple value, but I don't believe this is enough for my needs... which are:
The control in this case is a TextBlock and the data values from the objects in my ObservableCollection are variable length strings. I want to render the strings in multiple colours by splitting them into pieces and programatically creating a <Run Foreground="xxx" Text="yyy"/> for each piece inside the TextBlock.
Since the strings are variable length and the colours have to be programatically determined from the content of the string, I don't believe I can pre-create the <Run>s in the XAML, so I have to somehow get in on the data binding action and generate the <Run>s at bind-time (or very soon after).
Binding.NotifyOnTargetUpdated would seem to be a way to set up an event handler to do the work, but that's not available in the Windows Phone cut-down Silverlight implementation.
Any ideas? All search results seem to point to the above, but I'm looking for that little bit more.
Having apparently exhausted all the cut-down Silverlight on Windows Phone options for programmatically hooking the rendering of ListBoxItems via the data model, I ended up adding a Loaded="..." event handler to the XAML for the TextBlock.
It doesn't feel like the nicest solution, but perhaps that's just my code-preference talking and it's actually the right way to do it on Windows Phone.
In any case, because I'm hooked to the TextBlock directly, I'm not sure how to then get to the databound object on the ListBoxItem containing the TextBlock... if anyone has advice on how to get back up the tree to the generated ListBoxItem then I could use the bound object directly instead of retrieving it from elsewhere.
Note that since the ListBoxItem is generated, I didn't find where to put a Loaded="..." event handler for that in the XAML. The ListBox.ItemTemplate doesn't accept a Loaded attribute.
Update: this doesn't work!
The Loaded event handler fires when the TextBlock is first created and loaded, so the substitution works initially.
BUT
The generated ListBoxItem seems to be recycled (I guess by the ListBox.ItemContainerGenerator which doesn't want to use excessive amounts of memory by instantiating a whole new container when there are many off-screen entries in the list that won't need to be seen for a while) and when this happens, the Loaded event DOES NOT FIRE.
Since I modify the content when the TextBlock was first Loaded, this breaks the binding association so when the ListBoxItem is recycled, it now contains old/incorrect data.
Still no solution.
I'm thinking about trying to use an IValueConvertor and somehow pass a reference to the binding target... now sure how yet though.
Update 2: finally got it to work...
Sticking with the Loaded="..." event handler, it is possible to disable recycling of previously-generated ListBoxItems by configuring the VirtualizingStackPanel used by the ListBox under the covers.
In the XAML for the ListBox set VirtualizingStackPanel.VirtualizationMode="Standard" to force a new ListBoxItem to be generated each time instead of recycling previously-generated ones.
This means the Loaded event handler is called every time and I can replace the ordinary text of the TextBlock with the <Run>s to produce dynamically coloured text.

Tooltip only shows when running from source

I have a hierarchical flexgrid control with the ToolTipText property set, and when I run from source the tooltip displays as it should. But when I compile it and run that way, the tooltip doesn't display.
I've tried to remove anything listening to MouseMove in the hopes that that would fix it, and when I add some code to put the tooltip text into a message box, it appears to be set correctly. Can anyone think of why this would be happening?
Update: It appears that the problem arises when I host the grid inside another user control. E.g.: make container.ctl, which is just a blank control but with ControlContainer = True. Then make gridholder.ctl, which is a mshfg inside of a container.ctl. Lastly, embed gridholder.ctl into some form. Tooltips on the flexgrid don't appear to show up.
I'm interested to see how reproducible this is...
I haven't found a workaround for this issue yet, but I have a better idea of why it's happening after some testing and stepping through some of the VB6 runtime code in WinDBG.
The first interesting thing is that VB6 doesn't use the standard tooltip display mechanisms provided by Windows. For example, it doesn't use WM_NOTIFY messages to show/hide tooltips, or any of the other "standard" tooltip support described in the documentation explaining how tooltips work in Windows.
Instead, the VB6 runtime has its own way of managing and displaying tooltips. In principle, it's similar to in some ways to the standard Windows way of dealing with tooltips, but it's also different in a quite a few areas.
A breakdown of how VB6 does tooltips:
When a VB6 program starts, the runtime uses SetWindowsHookEx to install a mouse hook for the program's main thread.
The mouse hook intercepts all mouse messages sent to the program, in particular all WM_MOUSEMOUSE messages
Whenever the mouse hook runs, it calls an internal method in the VB6 runtime to get the object pointer (HCTL) of the control that the mouse is currently over top of. Note that this is an actual COM interface pointer, not a window handle.
It translates the HCTL to the corresponding window handle (HWND).
It checks to see if the mouse position is within that window's rectangle.
If so, it retrieves the ToolTipText property for the control. If this is not empty, it creates a tooltip window and displays the tooltip after a 700ms delay.
The problem with the MSHFlexGrid (and I imagine other controls that are not standard VB6 controls) is that this code doesn't retrieve the correct HCTL when you hover over the control and it's inside a custom container.
In that case, the code retrieves the HCTL of the custom container, not the HCTL of the MSHFlexGrid itself. Therefore, it retrieves the container's ToolTipText property (which is empty) and not the grid's ToolTipText, and therefore won't display a tooltip.
I'm not sure exactly why it does this, because as noted in the comments on your question, all of this works correctly if you use a PictureBox as your container.
I suspect the PictureBox has code to handle this correctly that is not included when you create your own container.
I'll update this answer with an actual workaround if I can find one. The only thing I can think right now is to somehow "sync" your container's ToolTipText property with the grid's ToolTipText property, so that when VB6 requests the container's ToolTipText, it will return the value of the grid's ToolTextTip property instead.
This is easier said than done, however, because ToolTipText is an extender property, and extender properties take precedence over properties that you write yourself that have the same name.
After a bit of research, I found what I think is the underlying problem. Your user control is not implementing any method for the controls to interact with. User Controls that are Container Controls need to implement the Extender functionality. These two links are the best I've found on the subject so far.
http://www.justvb.net/obook/ch7.htm#UsingtheExtenderObject
http://msdn.microsoft.com/en-us/library/aa733622(v=vs.60).aspx

Resources