I was looking at a code sample by Raymond Chen, available here. From what I understand, he obtains a shell item using the IShellWindows interface. Then, using that item's IDispatch interface and a call to QueryInterface, he hops over to the item's IWebBrowserApp interface. And then a few lines later, it appears that he hops over to the item's IServiceProvider interface. My question is, before using QueryInterface, how would you even know that an IShellWindows item might support the IWebBrowserApp and IServiceProvider interfaces? For example, I don't see any documentation listing all the interfaces that an IShellWindows item supports.
MSDN does not usually tell you which interfaces a object implements but if you look around you will often find some documentation and related interfaces you can QI. And just to make it clear, a interface is just a contract and multiple objects can implement a certain interface so you can't really blame Microsoft for not having a definitive list.
Let's try to pick apart your specific example.
The object that implements IShellWindows (CLSID_ShellWindows) does not really have any other interesting interfaces, you just care about its list of windows.
IShellWindows -> (IDispatch ->) IWebBrowserApp:
IShellWindows has a collection of open Internet Explorer and Explorer windows. For whatever reason it just gives you a IDispatch for each window instead of letting you ask for a specific interface. Possibly just because IShellWindows is also scriptable by Windows Scripting Host/Visual Basic and IDispatch plays a big role there.
The Shell windows collection includes file explorer windows and web browser windows Internet Explorer and 3rd-party web browsers). Normally each Shell window implements IDispatch; IShellWindows::Item and IShellWindows::FindWindowSW provide ways to access a Shell window's IDispatch interface.
..and the connection between IShellWindows and IWebBrowserApp/IWebBrowser2:
exdisp.h contains the following programming interfaces
IShellWindows
IWebBrowser2
IWebBrowserApp -> IShellBrowser:
Objects that have access to the site chain of the browser can get a reference to the browser on IShellBrowser using IServiceProvider::QueryService, with Service IDs such as SID_STopLevelBrowser and SID_SCommDlgBrowser. See the Knowledge Base article Retrieve the Top-Level IWebBrowser2 Interface from an ActiveX Control for more information on using service IDs.
The fact that the web browser and shell are connected like this should be no surprise for people that were interested in Windows around the Windows 98/IE 4 time frame. Internet Explorer and File Explorer were basically the same thing; Explorer could display web pages and IE could display the "file list" (IShellView).
IShellBrowser -> IShellView:
just a simple call to QueryActiveShellView.
There is a key point here; IShellFolder/IShellView can be implemented by a 3rd-party shell extension. Explorer implements IShellBrowser and it is IShellBrowser that hosts IShellView, and 3rd-party ISVs can also create file browsers that implement IShellBrowser. In theory you could have a file explorer app created by one company hosting a shell view created by a different company with no Microsoft code involved. IShellBrowser and IShellView is how they see each other.
IShellView -> IFolderView:
There is no direct connection here but if you look around you can connect the dots.
IShellFolderView is supported by the IShellView object that is returned from SHCreateShellFolderViewEx
[IShellFolderView is no longer available for use as of Windows 7. Instead, use IFolderView2 and IFolderView.]
In other cases where you can't find specific documentation you just have to try to query for interfaces you are interested in. The shell also has a ton of undocumented interfaces and a debugger is your only choice if you want to experiment with those.
Related
Hello dear stackoverflow friends
A few months ago I asked you a question about QuickAccess (Pin/Unpin). Unfortunately we are still facing this issue (Unable to unpin virtual objects). After having called Microsoft Support a few times, they replied us we should subscribe to premier support... Too expensive for a small company.
So we have decided to Mimic the Windows 7 Favorites in our Shell Namespace Extension.
No big deal in the Explorer, calling ShellExecuteEx with view's handle and lnk's target object absolute pidl works like a charm.
Too good to be true, yeah, as it is not working as expected in IFileDialogs...
First the lnks (wrapped in a virtual object, but parsing name is Filesystem Path) weren't for obvious reasons shown in the dialogs. So we tried with attributes such as "SFGAO_FOLDER", "SFGAO_FILESYSANCESTOR" etc. but then the shell was calling IShellFolder's EnumObjects and that's not what we want.
So we decided to have a go with the Interface IObjectWithSite, implementing it in our Favorites' folder. We were then able to consume events from IFileDialogs such as OnSelectionChange. Then we tried the same method as in the explorer, getting view handle (first querying IOleWindow interface, getting window handle, creating view in parent IShellFolder using window handle) and calling ShellExecuteEx... And the result is.. A big application (Notepad, Word etc.) crash and a new Explorer Window with the right virtual object selected.
Probably is my approach too complicated, have you got any ideas?
Thanks a lot!
Thank to Simon, I was able to find a solution, still work in progress. The issue is that the IContextMenu, IContextMenu2 and also IContextMenu3 interfaces are implemented in the solution. On Windows 7, the .lnk menu handler was called, fine, on Windows 10 on the other hand you need to call SHCreateDefaultContextMenu, then merging the menu handlers (SHCreateDefaultContextMenu will then Call IShellFolder::GetUIObjectOf with IID_IQueryAssociations). So the code was dated... Thanks!
In Windows 7's Windows Explorer list view (what allegedly is not list view at all) in the Details view, you can start selection marquee in the first (Name) column. You just need to start outside the actual name.
The same is true for default-style list view control in Details view.
But if you set the list view control to the Explorer style (using the SetWindowTheme), what should mimic the Windows Explorer, this does not work anymore. You can start selection in the second and later columns only.
SetWindowTheme(listView1.Handle, "explorer", null);
Is there any way to make list view mimic the Explorer selection style?
I suppose there's no settings to enable such behavior and this would have to be coded. Like handling the mouse down and triggering selection. But I have no idea how to do that.
Thanks.
Ntb, I'm using C++Builder, but this should be purely Win32 issue. I've tested this with WinForms too (hence the C# sample above).
Is there any way to make list view it mimic the Explorer selection style?
No, SysListView32 in explorer theme does not behave that way. The control used by the modern Explorer is actually DirectUIHwnd. And you are not able to use one of them.
The only way to get the behaviour of DirectUIHwnd is to code it yourself. I expect that's possible to do but I'd also expect it to be very difficult to achieve.
Actually there is a way to mimic Explorer selection behavior. It requires a lot of additional declarative work, but it is possible.
You need to get undocumented IListView interface via undocumented LVM_QUERYINTERFACE message (note that interface declaration and GUIDs are different for Windows Vista and Windows 7+). Details about constants and declarations can be found here:
IListView at Geoff Chappell - Software Analyst or
Undocumented List View Features at Code Project.
After acquiring the interface all you need is simply a call to SetSelectionFlags(1, 1) method. Voila you are done.
IS there any way to embed an OLE object in the Wordpad application using some kind of scripting, say vb script.
Using native application we can do it through Insert->Object and then select any object that needs to be embedded. I need to do it through vbscript like we can do it in Excel/Word/Powerpoint.
Please let me know.
Thanks.
Wordpad does expose an interface that can be instantiated using the ProgID "Wordpad.Document.1" as seen below. However, it doesn't appear to have a type library associated with it (there's nothing to see in an object browser) and there is no documentation of any publicly accessible methods or properties. It would seem to me that this was never intended to be used as a scriptable interface despite the fact that the following code will execute without error.
Set objWordpadDocument = CreateObject("Wordpad.Document.1")
I'm working on a little macro record/replay tool which can automate a few very old Visual Basic 6 GUIs we have. To do so, I'm identifying the controls by their name (the value of the name property of a control, that is).
One part of this tool needs to determine the name of a control given its HWND. For newer Visual Basic applications which were done using VB.NET, I can use the WM_GETCONTROLNAME window message. This works nicely.
However, this message is not understood by older windows. Is there any way to do this for controls of Visual Basic 6 applications? A solution which does not require being in the process of the GUI would be preferrable, but if I had a solution which only works inside the GUI process then that would be acceptable as well (since I can do the injection myself).
UPDATE: One thing I just tried, this moderate success: I used the AccessibleObjectFromWindow to check for implementations of the IAccessible interface of the object which shows the given HWND. In case I get an implementation (it seems that many [all?] Visual Basic controls implement this interface), I use the accName property to read out the "accessible name". Sometimes this does yield a useful string, but usually it doesn't.
I believe the only way would be getting inside the process and obtaining a pointer to the Form object, yet I have no idea how to do it from outside.
Is it possible you add support for the WM_GETCONTROLNAME to those older applications?
Or maybe, you could identify the controls by some other, natively-available properties?
Other that that, as Raymond is saying, there isn't much you can do.
Can you modify the vb6 apps? if so in each form load event you could iterate me.controls and use the SetProp(ctrl.hwnd, "MYNAME:" & ctrl.name, 0) api to add the name to the window's own property list, then in your other app you can EnumProps(ctrl_HWND) looking for the one that begins with MYNAME: and parse out the value.
In other words, can I count on a control ID as a reliable identifier?
From some reading I've done, it sounds like .NET controls can have control IDs that change with every run, is this so for Win32 apps as well, or are they something that's hardcoded in the source?
The window/control in question is actually an Internet Explorer dialog if that helps.
In general win32 dialog resource IDs do not change when you run the app. However, they are internal implementation details and as such they are subject to change whenever an update (patch, service pack, major release) to the application is made.
In general all of IE's dialogs use hard-coded control IDs. There may be some that are dynamic. If you give the specific control I might be able to give you a better answer.
The answer is "it depends on the circumstances, but in the majority of programs, these will not change across multiple executions." In general, a control ID or resource ID will be the same across every execution of the same program.
In most implementations, resources are stored in the resource section of the PE executable and are assigned a resource ID within that data structure. Usually, the developer specifies the resource in a .rc file.
The exceptional case is via APIs such as CreateDialogIndirect() which allow IDs specified through the API at runtime. Such dynamic creation is uncommon, however. Consistency of resource IDs and control IDs is the expected condition, so even in the case of the CreateXXXIndirect() API, users of the API would be ill-advised to chose a varying ID.
Microsoft has spent years trying to deal with applications which embed assumptions about internal windows implementation details. One of the biggest causes of compatibility problems for IE8 was caused by applications which made assumptions about the window order of IE controls. When the UI was changed in IE8, the controls moved and a number of browser plugins broke hideously.
In general you should never ever make assumptions about controls in an application that you didn't write - your code WILL break in the future (not might break, will break).
As a general rule, no they don't change between runs. Control IDs are usually specified with a dialog template, and that's a static resource compiled into an .exe or .dll. Controls can also be created using a regular call to CreateWindow or CreateWindowEx. In such cases, the ID is usually a constant, but it could be anything in principal (even a random value).
In any case, if you're planning to muck around with a dialog in another application, then you are asking for trouble. Control IDs can and do change between different versions of a program.
It depends on the control. Controls that are dynamically created could have a different ID every time they are created. Controls based on static dialog resources will have static IDs.
Even if a dialog is backed by a dialog resource template, controls can be added dynamically at runtime and those controls could have dynamically generated IDs.
Depending on your intent, it may be acceptable to store and reuse it for future lookups or traces; but in general it isn't safe.
If the window is based upon a dialog template, items declared in the template generally don't change. It is perfectly safe under typical circumstances to use these as identifiers.
Sometimes the window class name or a portion of it can be used as an identifier instead, depending on the window host.
If the dialog is one of the standard prompts from internet explorer, you may want to use text stored in adjacent controls or the dialog caption as additional verification info (if localized versions of IE are not an issue). If the dialog is a window that embeds an instance of MSHTML/IE; none of these options may be viable- but you can use OLE accessibility to get at the document shown and then browse the DOM from there.