How can I get a HWND from a InternetExplorer object

595 Views Asked by At

I am trying to get the current folder location of a Windows Explorer window. With this snippet, I am able to iterate all the windows and get their locations. However, I need to match them up to their window handles so that I can manipulate the windows style, position, parent, title, etc based on the folder location.

Alternatively, is it possible to get a InternetExplorer object from a HWND instead?

public void doStuff()
{
    var t = Type.GetTypeFromProgID("Shell.Application");
    var o = Activator.CreateInstance(t) as Shell32.Shell;

    try
    {
        var ws = o.Application.Windows();

        for (int i = 0; i < ws.Count; i++)
        {
            var ie = ws.Item(i);

            if (ie == null)
                continue;

            var path = System.IO.Path.GetFileName((string)ie.FullName);

            if (path.ToLower() != "explorer.exe")
                continue;

            // Gets the folder that it is showing
            temp = ie.LocationURL.ToString();
            temp = new Uri(temp).LocalPath;

            // How can I get the window handle?
            // ie.hwnd returns the top most window handle
            // So if the explorer window is embedded, it returns the wrong window handle
        }
    }
    catch (Exception x)
    {
        Console.log(x.Message);
    }
    finally
    {
        if (o != null)
            Marshal.FinalReleaseComObject(o);
    }
}
1

There are 1 best solutions below

3
Keith Miller On

Edit:

No telling what was taking your code so long. In PowerShell, it took 90 milliseconds to retreive the HWND-FolderPath association, not 15 seconds! I would expect it to be even faster in compiled code:

PS C:\> $shell.windows() | select HWND, @{ N = 'Path'  ; E = { $_.Document.Folder.Self.Path }}

   HWND Path
   ---- ----
1180948 C:\Users\keith\Documents
 198040 C:\Users\keith\Pictures
 328592 C:\Windows
 721672 C:\Users\keith\Videos
 198538 C:\Users\keith\DummyDesktop


PS C:\> Measure-Command -Expression  {$shell.windows() | select HWND, @{ N = 'Path'  ; E = { $_.Document.Folder.Self.Path }}}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 90
Ticks             : 904963
TotalDays         : 1.04741087962963E-06
TotalHours        : 2.51378611111111E-05
TotalMinutes      : 0.00150827166666667
TotalSeconds      : 0.0904963
TotalMilliseconds : 90.4963

The step-by-step approach in my answer was for the sake of comprehension. Assuming the Shell object is creaeted earlier, you just need the one line above.



I've just been working with File Explorer windows via the Shell.Application COM objects in PowerShell, so you'll have to do some code translation, but the objects, properties , and methods should be the same.

The Shell object's Windows method returns a Shellwindows object that "Represents a collection of the open windows that belong to the Shell." This contains both Internet Explorer and File Explorer ( including Control Panel ) windows:

PS C:\>>$Shell = New-Object -ComObject shell.application
PS C:\> $shell.windows() |
>>   Format-Table -AutoSize -Wrap -Property Name, TYpe, LocationName, LocationURL -GroupBy FullName


   FullName: C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE

Name              Type          LocationName LocationURL
----              ----          ------------ -----------
Internet Explorer HTML Document Google       https://www.google.com/?gws_rd=ssl#spf=1627718454134


   FullName: C:\WINDOWS\explorer.exe

Name          Type LocationName  LocationURL
----          ---- ------------  -----------
File Explorer      Documents     file:///C:/Users/keith/Documents
File Explorer      Control Panel
File Explorer      Libraries
File Explorer      Quick access
File Explorer      Network

but all window are indeed InternetExplorer objects.

PS C:\>>$Shell.Windows() | Get-Member


   TypeName: System.__ComObject#{d30c1661-cdaf-11d0-8a3e-00c04fc9e26e}

Name                 MemberType Definition
----                 ---------- ----------
ClientToWindow       Method     void ClientToWindow (int, int)
ExecWB               Method     void ExecWB (OLECMDID, OLECMDEXECOPT, Variant, Variant)
GetProperty          Method     Variant GetProperty (string)
GoBack               Method     void GoBack ()
GoForward            Method     void GoForward ()
GoHome               Method     void GoHome ()
GoSearch             Method     void GoSearch ()
Navigate             Method     void Navigate (string, Variant, Variant, Variant, Variant)
Navigate2            Method     void Navigate2 (Variant, Variant, Variant, Variant, Variant)
PutProperty          Method     void PutProperty (string, Variant)
QueryStatusWB        Method     OLECMDF QueryStatusWB (OLECMDID)
Quit                 Method     void Quit ()
Refresh              Method     void Refresh ()
Refresh2             Method     void Refresh2 (Variant)
ShowBrowserBar       Method     void ShowBrowserBar (Variant, Variant, Variant)
Stop                 Method     void Stop ()
AddressBar           Property   bool AddressBar () {get} {set}
Application          Property   IDispatch Application () {get}
Busy                 Property   bool Busy () {get}
Container            Property   IDispatch Container () {get}
Document             Property   IDispatch Document () {get}
FullName             Property   string FullName () {get}
FullScreen           Property   bool FullScreen () {get} {set}
Height               Property   int Height () {get} {set}
HWND                 Property   int HWND () {get}
Left                 Property   int Left () {get} {set}
LocationName         Property   string LocationName () {get}
LocationURL          Property   string LocationURL () {get}
MenuBar              Property   bool MenuBar () {get} {set}
Name                 Property   string Name () {get}
Offline              Property   bool Offline () {get} {set}
Parent               Property   IDispatch Parent () {get}
Path                 Property   string Path () {get}
ReadyState           Property   tagREADYSTATE ReadyState () {get}
RegisterAsBrowser    Property   bool RegisterAsBrowser () {get} {set}
RegisterAsDropTarget Property   bool RegisterAsDropTarget () {get} {set}
Resizable            Property   bool Resizable () {get} {set}
Silent               Property   bool Silent () {get} {set}
StatusBar            Property   bool StatusBar () {get} {set}
StatusText           Property   string StatusText () {get} {set}
TheaterMode          Property   bool TheaterMode () {get} {set}
ToolBar              Property   int ToolBar () {get} {set}
Top                  Property   int Top () {get} {set}
TopLevelContainer    Property   bool TopLevelContainer () {get}
Type                 Property   string Type () {get}
Visible              Property   bool Visible () {get} {set}
Width                Property   int Width () {get} {set}

The GUID of the TypeMame reveals that it's essentially a wrapper for the IWebBrowser2 interface.

It's the easily overlooked Document property that you want to access next. The link reveals why this is easy to overlook when you're in an Explorer/Shell frame-of-mnid. It exposes the following methods and properties:

PS C:\>>$FolderWIndows[0].Document | Get-Member


   TypeName: System.__ComObject#{29ec8e6c-46d3-411f-baaa-611a6c9cac66}

Name               MemberType Definition
----               ---------- ----------
FilterView         Method     void FilterView (string)
PopupItemMenu      Method     string PopupItemMenu (FolderItem, Variant, Variant)
SelectedItems      Method     FolderItems SelectedItems ()
SelectItem         Method     void SelectItem (Variant, int)
SelectItemRelative Method     void SelectItemRelative (int)
Application        Property   IDispatch Application () {get}
CurrentViewMode    Property   uint CurrentViewMode () {get} {set}
FocusedItem        Property   FolderItem FocusedItem () {get}
Folder             Property   Folder Folder () {get}
FolderFlags        Property   uint FolderFlags () {get} {set}
GroupBy            Property   string GroupBy () {get} {set}
IconSize           Property   int IconSize () {get} {set}
Parent             Property   IDispatch Parent () {get}
Script             Property   IDispatch Script () {get}
SortColumns        Property   string SortColumns () {get} {set}
ViewOptions        Property   int ViewOptions () {get}

But its Folder property returns the Folder object for the folder displayed in the window. It's the Folder object, along with its associated FolderItem object, that expose properties and methods associated with items in the Shell namespace. The FolderItem is retrieved via the Self property of the Folder:

PS C:\>>$FolderWIndows.Document.Folder | Get-Member


   TypeName: System.__ComObject#{a7ae5f64-c4d7-4d7f-9307-4d24ee54b841}

Name                       MemberType Definition
----                       ---------- ----------
CopyHere                   Method     void CopyHere (Variant, Variant)
DismissedWebViewBarricade  Method     void DismissedWebViewBarricade ()
GetDetailsOf               Method     string GetDetailsOf (Variant, int)
Items                      Method     FolderItems Items ()
MoveHere                   Method     void MoveHere (Variant, Variant)
NewFolder                  Method     void NewFolder (string, Variant)
ParseName                  Method     FolderItem ParseName (string)
Synchronize                Method     void Synchronize ()
Application                Property   IDispatch Application () {get}
HaveToShowWebViewBarricade Property   bool HaveToShowWebViewBarricade () {get}
OfflineStatus              Property   int OfflineStatus () {get}
Parent                     Property   IDispatch Parent () {get}
ParentFolder               Property   Folder ParentFolder () {get}
Self                       Property   FolderItem Self () {get}
ShowWebViewBarricade       Property   bool ShowWebViewBarricade () {get} {set}
Title                      Property   string Title () {get}


PS C:\>>$FolderWIndows.Document.Folder.Self | Get-Member


   TypeName: System.__ComObject#{edc817aa-92b8-11d1-b075-00c04fc33aa5}

Name             MemberType Definition
----             ---------- ----------
ExtendedProperty Method     Variant ExtendedProperty (string)
InvokeVerb       Method     void InvokeVerb (Variant)
InvokeVerbEx     Method     void InvokeVerbEx (Variant, Variant)
Verbs            Method     FolderItemVerbs Verbs ()
Application      Property   IDispatch Application () {get}
GetFolder        Property   IDispatch GetFolder () {get}
GetLink          Property   IDispatch GetLink () {get}
IsBrowsable      Property   bool IsBrowsable () {get}
IsFileSystem     Property   bool IsFileSystem () {get}
IsFolder         Property   bool IsFolder () {get}
IsLink           Property   bool IsLink () {get}
ModifyDate       Property   Date ModifyDate () {get} {set}
Name             Property   string Name () {get} {set}
Parent           Property   IDispatch Parent () {get}
Path             Property   string Path () {get}
Size             Property   int Size () {get}
Type             Property   string Type () {get}

and it's the Path propety of the FolderItem that you want.

PS C:\>>$FolderWIndows | select HWND,
>>                              @{ N = 'Title' ; E = { $_.Document.Folder.Title }},
>>                              @{ N = 'Path'  ; E = { $_.Document.Folder.Self.Path }}

   HWND Title         Path
   ---- -----         ----
3080380 Documents     C:\Users\keith\Documents
  68614 Control Panel ::{26EE0668-A00A-44D7-9371-BEB064C98683}
 853746 Libraries     ::{031E4825-7B94-4DC3-B131-E946B44C8DD5}
1245532 Quick access  ::{679F85CB-0220-4080-B29B-5540CC05AAB6}
 199810 Network       ::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}

In relation to File Explorer and the Shell namespace, the path for the FolderItem can be ambiguous. You can determine the namespace path by recursivley eevaluating the ParentFolder object until you reach the Desktop, root of the shell namespace, which has no parent.

Function Get-NSPath ( $oFolder )
{
    If     (( $oParent = $oFolder.ParentFolder ).ParentFolder )
    {
           (( Get-NSPath $oParent ) , $oFolder.Title ) -join '\'
    }
    ElseIf ( $oParent )
    {
           $oFolder.Title       # Desktop/NavPane Item
    }
}   # If oFolder was the virtual Desktop, Namespace path is null.

After opening the DOcumentes folder from several locations:

$FolderWindows = $shell.Windows() | Where FullName -match 'explorer.exe$'
PS C:\>>$FolderWIndows | select HWND,
>>                              @{ N = 'Title'     ; E = { $_.Document.Folder.Title }},
>>                              @{ N = 'Path'      ; E = { $_.Document.Folder.Self.Path }},
>>                              @{ N = 'NameSpace' ; E = { Get-NSPath $_.Document.Folder }}

   HWND Title     Path                     NameSpace
   ---- -----     ----                     ---------
 265366 Documents C:\Users\keith\Documents This PC\Documents
 331000 Documents C:\Users\keith\Documents Keith Miller\Documents
2950776 Documents C:\Users\keith\Documents This PC\Windows (C:)\Users\keith\Documents
 396606 Documents C:\Users\keith\Documents Libraries\Documents\Documents