Results 1 to 3 of 3

Thread: [RESOLVED] Get Excel Object From Windows Handle

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Dec 2016

    Resolved [RESOLVED] Get Excel Object From Windows Handle

    Hey Everyone, So I have been doing researching online for how to get a specific Excel workbook for automation with .Net. I started by getting the process Id for every running process and checking it's window title to find the specific workbook I needed. That worked well. However now, I need to get the Excel Object from that process Id. That has proven to be a difficult task, and I haven't found too many helpful resources online. Except for one resource on Stackoverflow.

    I took the code from the following link and tried to use it for my own purposes:


    In this code example it seems they need the window handle of the Excel workbook, and once they have the window handle, they can use the AccessibleObjectFromWindow API function to get the Excel object from the handle.

    It seems that you should be able to get the Excel object from the following line of code:

    Console.WriteLine(String.Format("Excel version is: {0}, ptr.Application.Version))

    Where ptr would give you the Excel Object. However I keep getting the following error:

    "Object Variable or With Block Variable not set"

    Here is the code I am using:

    Option Strict Off
    Imports System
    Imports System.Diagnostics
    Imports System.Collections.Generic
    Imports System.Runtime.InteropServices
    Imports System.Globalization
    Imports System.Text
    Imports NXOpen
    Imports NXOpen.UF
    Module ExcelLateBindingSample
        Dim theSession As Session = Session.GetSession()
        Dim theUFSession As UFSession = UFSession.GetUFSession()
        ''' <summary>
        ''' Interface definition for Excel.Window interface
        ''' </summary>
        <Guid("00020893-0000-0000-C000-000000000046"), _
        InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
        Public Interface ExcelWindow
        End Interface
        ''' <summary>
        ''' This class is needed as a workaround to http://support.microsoft.com/default.aspx?scid=kb;en-us;320369
        ''' Excel automation will fail with the follwoing error on systems with non-English regional settings:
        ''' "Old format or invalid type library. (Exception from HRESULT: 0x80028018 (TYPE_E_INVDATAREAD))" 
        ''' </summary>
        Class UILanguageHelper
            Implements IDisposable
            Private _currentCulture As CultureInfo
            Public Sub New()
                ' save current culture and set culture to en-US 
                _currentCulture = System.Threading.Thread.CurrentThread.CurrentCulture
                System.Threading.Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")
            End Sub
            Public Sub Dispose() Implements System.IDisposable.Dispose
                'reset to original culture 
                System.Threading.Thread.CurrentThread.CurrentCulture = _currentCulture
            End Sub
        End Class
        <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
        Private Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
        End Function
        <DllImport("Oleacc.dll")> _
        Private Function AccessibleObjectFromWindow(ByVal hwnd As Integer, ByVal dwObjectID As UInt32, ByVal riid() As Byte, ByRef ptr As ExcelWindow) As Integer
        End Function
        Public Delegate Function EnumChildCallback(ByVal hwnd As Integer, ByRef lParam As Integer) As Boolean
        <DllImport("User32.dll")> _
        Public Function EnumChildWindows(ByVal hWndParent As Integer, ByVal lpEnumFunc As EnumChildCallback, ByRef lParam As Integer) As Boolean
        End Function
        <DllImport("User32.dll")> _
        Public Function GetClassName(ByVal hWnd As Integer, ByVal lpClassName As StringBuilder, ByVal nMaxCount As Integer) As Integer
        End Function
        Public Function EnumChildProc(ByVal hwndChild As Integer, ByRef lParam As Integer) As Boolean
            Dim buf As New StringBuilder(128)
            GetClassName(hwndChild, buf, 128)
            If buf.ToString() = "EXCEL7" Then
                lParam = hwndChild
                Return False
            End If
            Return True
        End Function
        Sub Main()
            Dim n As Integer = 0
            'Dim hwnd As Integer
            ' Use the window class name ("XLMAIN") to retrieve a handle to Excel's main window.
            ' Alternatively you can get the window handle via the process id:
            'Dim hwnd As Integer = CInt(Process.GetProcessById(excelPid).MainWindowHandle);
            Dim hwnd As Integer = CInt(FindWindow("XLMAIN", Nothing))
            If hwnd <> 0 Then
                Dim hwndChild As Integer = 0
                ' Search the accessible child window (it has class name "EXCEL7") 
                Dim cb As New EnumChildCallback(AddressOf EnumChildProc)
                EnumChildWindows(hwnd, cb, hwndChild)
                If hwndChild <> 0 Then
                    'Method for finding specific Excel Workbook
                    'For Each p As Process In System.Diagnostics.Process.GetProcesses
                    '    If p.MainWindowTitle IsNot Nothing Then
                    '        If p.MainWindowTitle = "Microsoft Excel - Workbook Title  [Read-Only]" Then
                    '            hwnd = p.MainWindowHandle
                    '        End If
                    '        Echo(p.MainWindowTitle)
                    '        Echo(hwnd)
                    '    End If
                    If hwnd <> 0 Then
                        ' We call AccessibleObjectFromWindow, passing the constant OBJID_NATIVEOM (defined in winuser.h) 
                        ' and IID_IDispatch - we want an IDispatch pointer into the native object model.
                        Const OBJID_NATIVEOM As UInteger = &HFFFFFFF0&
                        Dim IID_IDispatch As New Guid("{00020400-0000-0000-C000-000000000046}")
                        Dim ptr As ExcelWindow
                        Dim hr As Integer = AccessibleObjectFromWindow(hwnd, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), ptr)
                        If hr >= 0 Then
                            ' We successfully got a native OM IDispatch pointer, we can QI this for
                            ' an Excel Application using reflection (and using UILanguageHelper to 
                            ' fix http://support.microsoft.com/default.aspx?scid=kb;en-us;320369)
                            Using fixCrash As New UILanguageHelper
                                Console.WriteLine(String.Format("Excel version is: {0}", ptr.Application.Version))
                            End Using
                        End If
                    End If
                End If
            End If
        End Sub
        Sub Echo(ByVal output As String)
        End Sub
    End Module

    I should note that the last If statement that checks to see if hr > 0 never becomes true. hr is always a negative number. So in order for me to see the value of ptr I had to comment out that last If statement.

    I honestly don't know what role hr is playing here so that very well may have something to do with the problem.

    Also in the example code the author gets the handles of the child windows as well. Why is that? Is that to find specific workbooks within the Main Excel Window?

    Thank you for your time.

  2. #2
    Join Date
    Oct 2010

    Re: Get Excel Object From Windows Handle

    Quote Originally Posted by JohnnyWaffles View Post
    ... how to get a specific Excel workbook for automation with .Net.
    This implies that you know the path to where the WorkBook is stored. If this is correct, then you can obtain an Excel automation COM reference to the Workbook using either: 1) Microsoft.VisualBasic.Interaction.GetObject("path") or 2) System.Runtime.InteropServices.Marshal.BindToMoniker("path"). The GetObject method just will call BindToMoniker.

    BindToMoniker will return an Excel.Workbook object for the already open Workbook or use excel to open it if needed. As this method return a System.Object, you will need to using either late binding or cast the return value to an Excel.Workbook to use early binding.

  3. #3

    Thread Starter
    Lively Member
    Join Date
    Dec 2016

    Re: Get Excel Object From Windows Handle

    Okay, so the workbook I need to get the object for is already opened, so GetObject(Path) won't work. So I will need to use System.Runtime.InteropServices.Marshal.BindToMoniker("path") and it will get the object for the already opened workbook?

    Also, would it make a difference if I didn't have access to the Microsoft.Office.Interop namespace? I think that is why I resorted to the code above, because you didn't need the above namespace. Will I still be able to get the open workbook using System.Runtime.InteropServices.Marshal.BindToMoniker("path") ?

    Edit 2:

    You know what I had success by using your recommended code:

     'open Excel file
            Dim objExcel as Object = GetObject("Excel.Application")
            Dim objWorkbook = objExcel.Workbooks.Open("Path")
    'Later on I was able to get the workbook instance by simply using:
            Dim objExcel as Object = GetObject("Path")
    Thank you for your help!
    Last edited by JohnnyWaffles; Jan 6th, 2017 at 01:35 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts


Click Here to Expand Forum to Full Width