Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>First: Late binding in C# is quite a pain. It's best to avoid it. Second: Late binding in C# is a pain. Use the PIA!</p> <p>Ok, that being said, here is what you need to do in order to use late binding: Remove the reference to the Office 2003 PIAs and instead add a COM import of the interface required by <code>AccessibleObjectFromWindow</code>, i.e. the <code>Excel.Window</code> interface:</p> <pre><code>[Guid("00020893-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface ExcelWindow { } </code></pre> <p>You can retrieve this interface using a tool like <a href="http://www.red-gate.com/products/reflector/" rel="noreferrer">Reflector</a> (or by simply pressing F12 on the type <code>Excel.Window</code> while the reference to the Excel PIA is still in your project)</p> <p>That being done you will have to modify the signature of <code>AccessibleObjectFromWindow</code> to match the imported <code>ExcelWindow</code> interface:</p> <pre><code>[DllImport("Oleacc.dll")] static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, out ExcelWindow ptr); </code></pre> <p>Finally, you must use reflection to get the <code>Excel.Application</code> object from the <code>ExcelWindow</code> object:</p> <pre><code>object xlApp = ptr.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, ptr, null); </code></pre> <p>If your code is going to make a lot of calls into Excel's OM it might be easier to use VB with <code>Option Strict</code> turned off (or wait for C#4.0 ;-). Or, if you don't want to change from C#, it might be a good idea to create a wrapper class for the late binding calls.</p> <hr> <p><strong>Full Sample</strong></p> <p>Here is a fully functional sample (based on an <a href="http://blogs.msdn.com/b/andreww/archive/2008/11/30/starting-or-connecting-to-office-apps.aspx" rel="noreferrer">article</a> by Andrew Whitechapel):</p> <pre><code>using System; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Text; namespace ExcelLateBindingSample { /// &lt;summary&gt; /// Interface definition for Excel.Window interface /// &lt;/summary&gt; [Guid("00020893-0000-0000-C000-000000000046")] [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface ExcelWindow { } /// &lt;summary&gt; /// 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))" /// &lt;/summary&gt; class UILanguageHelper : IDisposable { private CultureInfo _currentCulture; public UILanguageHelper() { // save current culture and set culture to en-US _currentCulture = System.Threading.Thread.CurrentThread.CurrentCulture; System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); } public void Dispose() { // reset to original culture System.Threading.Thread.CurrentThread.CurrentCulture = _currentCulture; } } class Program { [DllImport("user32.dll", SetLastError = true)] static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("Oleacc.dll")] static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, out ExcelWindow ptr); public delegate bool EnumChildCallback(int hwnd, ref int lParam); [DllImport("User32.dll")] public static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam); [DllImport("User32.dll")] public static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount); public static bool EnumChildProc(int hwndChild, ref int lParam) { StringBuilder buf = new StringBuilder(128); GetClassName(hwndChild, buf, 128); if (buf.ToString() == "EXCEL7") { lParam = hwndChild; return false; } return true; } static void Main(string[] args) { // 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: // int hwnd = (int)Process.GetProcessById(excelPid).MainWindowHandle; // int hwnd = (int)FindWindow("XLMAIN", null); if (hwnd != 0) { int hwndChild = 0; // Search the accessible child window (it has class name "EXCEL7") EnumChildCallback cb = new EnumChildCallback(EnumChildProc); EnumChildWindows(hwnd, cb, ref hwndChild); if (hwndChild != 0) { // 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 uint OBJID_NATIVEOM = 0xFFFFFFF0; Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}"); ExcelWindow ptr; int hr = AccessibleObjectFromWindow(hwndChild, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), out ptr); if (hr &gt;= 0) { // 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 (UILanguageHelper fix = new UILanguageHelper()) { object xlApp = ptr.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, ptr, null); object version = xlApp.GetType().InvokeMember("Version", BindingFlags.GetField | BindingFlags.InvokeMethod | BindingFlags.GetProperty, null, xlApp, null); Console.WriteLine(string.Format("Excel version is: {0}", version)); } } } } } } } </code></pre> <p>And this would be the same solution without PIAs in VB (Note that OM call are much more readable; however, the code to get access to the OM would be the same):</p> <pre><code>Option Strict Off Imports System.Globalization Imports System.Runtime.InteropServices Imports System.Text Module ExcelLateBindingSample ''' &lt;summary&gt; ''' Interface definition for Excel.Window interface ''' &lt;/summary&gt; &lt;Guid("00020893-0000-0000-C000-000000000046"), _ InterfaceType(ComInterfaceType.InterfaceIsIDispatch)&gt; _ Public Interface ExcelWindow End Interface ''' &lt;summary&gt; ''' 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))" ''' &lt;/summary&gt; 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 &lt;DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)&gt; _ Private Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr End Function &lt;DllImport("Oleacc.dll")&gt; _ 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 &lt;DllImport("User32.dll")&gt; _ Public Function EnumChildWindows(ByVal hWndParent As Integer, ByVal lpEnumFunc As EnumChildCallback, ByRef lParam As Integer) As Boolean End Function &lt;DllImport("User32.dll")&gt; _ 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() ' 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 &lt;&gt; 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 &lt;&gt; 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 = &amp;HFFFFFFF0&amp; Dim IID_IDispatch As New Guid("{00020400-0000-0000-C000-000000000046}") Dim ptr As ExcelWindow Dim hr As Integer = AccessibleObjectFromWindow(hwndChild, OBJID_NATIVEOM, IID_IDispatch.ToByteArray(), ptr) If hr &gt;= 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 Sub End Module </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload