【问题标题】:Excel.Application.Workbooks collection does not contain workbook open from onedriveExcel.Application.Workbooks 集合不包含从 onedrive 打开的工作簿
【发布时间】:2019-09-24 20:57:05
【问题描述】:

我有两个打开的工作簿。一个来自标准文件系统文件夹,一个来自我的 onedrive for business 文件夹(实际上是 sharepoint 365)

当我执行以下操作时:

Excel.Application app = (Excel.Application)Marshal.GetActiveObject("Excel.Application");

foreach(Excel.Workbook workbook in app.Workbooks)
{
    Debug.WriteLine(workbook.Name);
}

仅找到从我的本地文件夹打开的工作簿和Workbooks.Count == 1

那么我怎样才能获得从我的 onedrive 文件夹中打开的工作簿呢?我需要能够附加到这个打开的工作簿,但如果文件存储在 sharepoint 365 上,传统方法似乎不起作用

【问题讨论】:

  • SharePoint 上的工作簿是如何在本地计算机上的 Excel 中打开的?我怀疑 发生的事情是它是在 Excel 的不同实例 中打开的。如果您转到 Excel 的 View 并选择 Switch Windows,我猜其他工作簿没有列出?这将证实我的猜测。 GetActiveObject 查询 Windows ROT; Office 应用程序仅在 ROT 中列出一个应用程序实例,其他任何应用程序实例都不存在。如果您查看任务管理器,您可能还会看到两个 Excel 进程?
  • 其实你是对的,我发现这是真的。我在 Code Project 上找到了一个不错的项目,名为 Automate multiple Excel instances,它为我提供了获取所需工作簿的工具。如果您转发为答案,我会标记它。
  • 实际上,我认为您发现的答案会更好:-) 我可以指出人们需要使用 Office 应用程序的多个实例。 (我们认为在本地拥有所有相关信息非常重要,因为您永远不知道其他材料何时会消失!但如果您觉得您从其他来源“复制”了很多,请添加致谢并链接到它。)非常欢迎您将我的“猜测”中的内容作为您的答案的一部分。完成后“Ping”我,我会投赞成票 - 我认为在网站上拥有它很重要 :-)

标签: c# excel office-interop


【解决方案1】:

我在 Code Project 上找到了我的问题的解决方案。 JamesFaix 的一个项目叫Automate multiple Excel instances.

我只需要从这个项目中获得几个代码片段即可完成查找和绑定到正在运行的 Excel 工作簿的任务。我将在这里详细介绍这些,但这不是我的工作,所以请查看源文章。使用下面详述的代码,我现在可以获取给定的 Excel 工作簿实例:

private bool TryGetOpenWorkbook(out Excel.Workbook workbook)
{
bool workbookFound = false;
workbook = null;

foreach (Process excelProcesses in Process.GetProcessesByName("EXCEL"))
{
    Excel.Application excelApp = excelProcesses.AsExcelApp();
    foreach (Excel.Workbook wb in excelApp.Workbooks)
    {
        if (wb.Name.Equals(_workbookName))
        {
            workbook = wb;
            workbookFound = true;
        }
    }
    // Release Process
    excelApp.ReleaseComObject();
    excelProcesses.ReleaseComObject();
}

return workbookFound;
}

首先是System.Diagnostics.Process类的扩展方法:

/// <summary>
/// Gets the Excel instance running in the given process, or null if none exists.
/// </summary>
/// <param name="process">The process.</param>
public static Excel.Application AsExcelApp(this Process process)
{
    if (process == null) throw new ArgumentNullException(nameof(process));

    var handle = process.MainWindowHandle.ToInt32();
    var result = NativeMethods.AppFromMainWindowHandle(handle);
    //Debug.Assert(result != null);
    return result;
}

然后是几个静态方法:

internal static class NativeMethods
{
private const string USER32 = "User32.dll";
private const string OLEACC = "Oleacc.dll";

public static Excel.Application AppFromMainWindowHandle(int mainWindowHandle)
    {
        if (mainWindowHandle == 0) throw new ArgumentOutOfRangeException("Window handle cannot be 0.", nameof(mainWindowHandle));

        int childHandle = 0;
        EnumChildWindows(mainWindowHandle, NextChildWindowHandle, ref childHandle);

        var win = ExcelWindowFromHandle(childHandle);

        return win?.Application;
    }

public static Excel.Window ExcelWindowFromHandle(int handle)
    {
        Excel.Window result;
        AccessibleObjectFromWindow(handle, windowObjectId, windowInterfaceId, out result);
        return result;
    }

/// <summary>Enumerates the child windows that belong to the specified parent window by passing the handle to each child window, in turn, 
/// to an application-defined callback function. EnumChildWindows continues until the last child window is enumerated or 
/// the callback function returns false.</summary>
/// <param name="hWndParent">A handle to the parent window whose child windows are to be enumerated. If this parameter is NULL,
/// this function is equivalent to EnumWindows.</param>
/// <param name="lpEnumFunc">A point to an application-defined callback function.</param>
/// <param name="lParam">An application-defined value to be passed tot he callback function.</param>
/// <returns>The return value is not used.</returns>
[DllImport(USER32)]
private static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);

/// <summary>An application-defined callback function used with the EnumChildWindows function. 
/// It receives the child window handles.  The WNDENUMPROC type defines a pointer to this callback function.
/// EnumChildProc is a placeholder for the application-defined function name.</summary>
/// <param name="hwnd">A handle to the child window of the parent window specified in EnumChildWindows.</param>
/// <param name="lParam">The application-defined value given in EnumChildWindows.</param>
/// <returns>To continue enumeration, the callback function must return TRUE; to stop enumeration it must return FALSE.</returns>
private delegate bool EnumChildCallback(int hwnd, ref int lParam);

private static bool NextChildWindowHandle(int currentChildHandle, ref int nextChildHandle)
    {
        //  Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + " - NextChildWindowHandle(" + currentChildHandle + ")");
        const string excelClassName = "EXCEL7";

        var result = true;

        var className = GetClassName(currentChildHandle);
        // Debug.WriteLine(currentChildHandle + " ClassName: " + className);
        if (className == excelClassName)
        {
            nextChildHandle = currentChildHandle;
            result = false;
        }
        //  Debug.WriteLine(DateTime.Now.ToString("hh:mm:ss.fff") + " - NextChildWindowHandle(" + currentChildHandle + ", ref " + nextChildHandle + ") => " + result);
        return result;
    }


/// <summary>Retrieves the address of the specified interface for the object associated with the specified window.</summary>
/// <param name="hwnd">Specifies the handle of a window for which an object is to be retrieved. 
/// To retrieve an interface pointer to the cursor or caret object, specify NULL and use the appropriate ID in dwObjectID.</param>
/// <param name="dwObjectID">Specifies the object ID. This value is one of the standard object identifier constants or a custom object ID
/// such as OBJID_NATIVEOM, which is the object ID for the Office native object model.</param>
/// <param name="riid">Specifies the reference identifier of the requested interface. This value is either IID_IAccessible or IID_Dispatch,
/// but it can also be IID_IUnknown, or the IID of any interface that the object is expected to support.</param>
/// <param name="ppvObject">Address of a pointer variable that receives the address of the specified interface.</param>
/// <returns>If successful, returns S_OK; otherwise returns E_INVALIDARG, E_NOINTERFACE, or another standard COM error code.</returns>
[DllImport(OLEACC)]
private static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, out Excel.Window ppvObject);

private const uint windowObjectId = 0xFFFFFFF0;
private static readonly byte[] windowInterfaceId = new Guid("{00020400-0000-0000-C000-000000000046}").ToByteArray();

#region Get class name

//Gets the name of the COM class to which the specified window belongs.
private static string GetClassName(int windowHandle)
{
    var buffer = new StringBuilder(128);
    GetClassName(windowHandle, buffer, 128);
    return buffer.ToString();
}

/// <summary>Retrieves the name of the class to which the specified window belongs.</summary>
/// <param name="hWnd">A handle to the window and, indirectly, the class to which the window belongs.</param>
/// <param name="lpClassName">The class name string.</param>
/// <param name="nMaxCount">The length of the lpClassName buffer, in characters. The buffer must be large enough to include
/// the terminating null character; otherwise, the class name string is truncated to nMaxCount-1 characters.</param>
/// <returns>If the function succeeds, the number of characters copied to the buffer not including the terminating null character;
/// otherwise 0.</returns>
[DllImport(USER32, CharSet = CharSet.Unicode)]
private static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);

#endregion

}

【讨论】:

    猜你喜欢
    • 2021-08-20
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-16
    • 2013-06-23
    • 1970-01-01
    相关资源
    最近更新 更多