获取相同类型的多个 运行 个 COM 对象
Get multiple running COM objects of same Type
我正在尝试查找第一个可见的 Word 实例。我找到了一些有用的代码 here 并稍微修改了它。
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Office.Interop.Word;
namespace TestConsole
{
internal class Program
{
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);
private static void Main(string[] args)
{
Application word1 = new Application();
word1.Visible = false;
Application word2 = new Application();
word2.Visible = true;
Application word3 = new Application();
word3.Visible = false;
int index = 0;
while (true)
{
Application application = Program.GetRunningCOMObjectOfType<Application>(++index);
if (application != null)
{
Console.WriteLine($"{index}) IsVisible: {application.Visible}");
Debug.WriteLine($"{index}) IsVisible: {application.Visible}");
}
else
{
break;
}
}
Console.WriteLine("############# End of program #############");
Console.ReadLine();
}
public static T GetRunningCOMObjectOfType<T>(int index)
{
IRunningObjectTable runningObjectTable = null;
IEnumMoniker monikerList = null;
try
{
if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null)
{
return default(T);
}
runningObjectTable.EnumRunning(out monikerList);
monikerList.Reset();
IMoniker[] monikerContainer = new IMoniker[1];
IntPtr pointerFetchedMonikers = IntPtr.Zero;
int counter = 0;
while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
{
runningObjectTable.GetObject(monikerContainer[0], out object comInstance);
if (comInstance is T castedInstance)
{
if (index == ++counter)
{
return castedInstance;
}
}
}
}
finally
{
if (runningObjectTable != null)
{
Marshal.ReleaseComObject(runningObjectTable);
}
if (monikerList != null)
{
Marshal.ReleaseComObject(monikerList);
}
}
return default(T);
}
}
}
此代码的结果如下所示:
1) IsVisible: False
2) IsVisible: False
3) IsVisible: False
我希望在一个实例中 Visible 应该 return 为真。
似乎总是第一个实例是 returned。如果 word1 可见,则为所有实例 returned。
在努力使它工作之后,事实证明,这种通用方法是不可能的,因为某些组件在 运行 对象 [=30= 上注册了具有相同键的不同实例](ROT)。 IRunningObjectTable.GetObject
returns 本例中的第一个注册实例。例如。 Word 以其 ApplicationClass
.
的实例那样做
解法:
似乎没有干净通用的解决方案,但对我有用。 Word 还在 ROT 中注册 Document
的实例。所以我们可以很容易地得到这些文件,然后从那里我们得到申请。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Office.Interop.Word;
namespace ConsoleApp
{
internal class Program
{
#region public methods
private static void Main(string[] args)
{
Application word1 = new Application();
word1.Visible = false;
word1.Documents.Add();
Application word2 = new Application();
word2.Visible = true;
word2.Documents.Add();
word2.Documents.Add();
Application word3 = new Application();
word3.Visible = false;
word3.Documents.Add();
word3.Documents.Add();
word3.Documents.Add();
List<(IMoniker moniker, IBindCtx bindingContext, object instance)> x = Program.GetRunningComObjects();
foreach ( (IMoniker moniker, IBindCtx bindingContext, object instance) in x)
{
// get only the instances that
if (instance is Document doc && doc.Application.ActiveDocument == doc)
{
moniker.GetDisplayName(bindingContext, moniker, out string displayName);
Console.WriteLine($"{displayName}");
Application wordInstance = doc.Application;
Console.WriteLine($"\tVisible:\t{wordInstance.Visible}");
Console.WriteLine($"\tDocumentCount:\t{wordInstance.Documents.Count}");
}
}
word1.Quit(false);
word2.Quit(false);
word3.Quit(false);
Console.WriteLine();
Console.WriteLine("############## End of program ##############");
Console.WriteLine("############## press enter to continue ##############");
Console.ReadLine();
}
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);
private static List<(IMoniker moniker, IBindCtx bindingContext, object instance)> GetRunningComObjects()
{
List<(IMoniker, IBindCtx, object)> result = new List<(IMoniker, IBindCtx, object)>();
IRunningObjectTable runningObjectTable = null;
IEnumMoniker monikerList = null;
try
{
if (Program.GetRunningObjectTable(0, out runningObjectTable) != 0
|| runningObjectTable == null)
{
return result;
}
runningObjectTable.EnumRunning(out monikerList);
monikerList.Reset();
IMoniker[] monikerContainer = new IMoniker[1];
IntPtr pointerFetchedMonikers = IntPtr.Zero;
while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
{
Program.CreateBindCtx(0, out IBindCtx bindingContext);
runningObjectTable.GetObject(monikerContainer[0], out object comInstance);
result.Add((monikerContainer[0], bindingContext, comInstance));
}
}
finally
{
if (runningObjectTable != null)
{
Marshal.ReleaseComObject(runningObjectTable);
}
if (monikerList != null)
{
Marshal.ReleaseComObject(monikerList);
}
}
return result;
}
#endregion
}
}
此代码产生以下结果:
Dokument1
Visible: False
DocumentCount: 1
Dokument3
Visible: True
DocumentCount: 2
Dokument6
Visible: False
DocumentCount: 3
好的,这有点可疑。这种方法的一个严重问题是找不到没有文档的实例。
我正在尝试查找第一个可见的 Word 实例。我找到了一些有用的代码 here 并稍微修改了它。
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Office.Interop.Word;
namespace TestConsole
{
internal class Program
{
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);
private static void Main(string[] args)
{
Application word1 = new Application();
word1.Visible = false;
Application word2 = new Application();
word2.Visible = true;
Application word3 = new Application();
word3.Visible = false;
int index = 0;
while (true)
{
Application application = Program.GetRunningCOMObjectOfType<Application>(++index);
if (application != null)
{
Console.WriteLine($"{index}) IsVisible: {application.Visible}");
Debug.WriteLine($"{index}) IsVisible: {application.Visible}");
}
else
{
break;
}
}
Console.WriteLine("############# End of program #############");
Console.ReadLine();
}
public static T GetRunningCOMObjectOfType<T>(int index)
{
IRunningObjectTable runningObjectTable = null;
IEnumMoniker monikerList = null;
try
{
if (GetRunningObjectTable(0, out runningObjectTable) != 0 || runningObjectTable == null)
{
return default(T);
}
runningObjectTable.EnumRunning(out monikerList);
monikerList.Reset();
IMoniker[] monikerContainer = new IMoniker[1];
IntPtr pointerFetchedMonikers = IntPtr.Zero;
int counter = 0;
while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
{
runningObjectTable.GetObject(monikerContainer[0], out object comInstance);
if (comInstance is T castedInstance)
{
if (index == ++counter)
{
return castedInstance;
}
}
}
}
finally
{
if (runningObjectTable != null)
{
Marshal.ReleaseComObject(runningObjectTable);
}
if (monikerList != null)
{
Marshal.ReleaseComObject(monikerList);
}
}
return default(T);
}
}
}
此代码的结果如下所示:
1) IsVisible: False
2) IsVisible: False
3) IsVisible: False
我希望在一个实例中 Visible 应该 return 为真。 似乎总是第一个实例是 returned。如果 word1 可见,则为所有实例 returned。
在努力使它工作之后,事实证明,这种通用方法是不可能的,因为某些组件在 运行 对象 [=30= 上注册了具有相同键的不同实例](ROT)。 IRunningObjectTable.GetObject
returns 本例中的第一个注册实例。例如。 Word 以其 ApplicationClass
.
解法:
似乎没有干净通用的解决方案,但对我有用。 Word 还在 ROT 中注册 Document
的实例。所以我们可以很容易地得到这些文件,然后从那里我们得到申请。
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using Microsoft.Office.Interop.Word;
namespace ConsoleApp
{
internal class Program
{
#region public methods
private static void Main(string[] args)
{
Application word1 = new Application();
word1.Visible = false;
word1.Documents.Add();
Application word2 = new Application();
word2.Visible = true;
word2.Documents.Add();
word2.Documents.Add();
Application word3 = new Application();
word3.Visible = false;
word3.Documents.Add();
word3.Documents.Add();
word3.Documents.Add();
List<(IMoniker moniker, IBindCtx bindingContext, object instance)> x = Program.GetRunningComObjects();
foreach ( (IMoniker moniker, IBindCtx bindingContext, object instance) in x)
{
// get only the instances that
if (instance is Document doc && doc.Application.ActiveDocument == doc)
{
moniker.GetDisplayName(bindingContext, moniker, out string displayName);
Console.WriteLine($"{displayName}");
Application wordInstance = doc.Application;
Console.WriteLine($"\tVisible:\t{wordInstance.Visible}");
Console.WriteLine($"\tDocumentCount:\t{wordInstance.Documents.Count}");
}
}
word1.Quit(false);
word2.Quit(false);
word3.Quit(false);
Console.WriteLine();
Console.WriteLine("############## End of program ##############");
Console.WriteLine("############## press enter to continue ##############");
Console.ReadLine();
}
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);
private static List<(IMoniker moniker, IBindCtx bindingContext, object instance)> GetRunningComObjects()
{
List<(IMoniker, IBindCtx, object)> result = new List<(IMoniker, IBindCtx, object)>();
IRunningObjectTable runningObjectTable = null;
IEnumMoniker monikerList = null;
try
{
if (Program.GetRunningObjectTable(0, out runningObjectTable) != 0
|| runningObjectTable == null)
{
return result;
}
runningObjectTable.EnumRunning(out monikerList);
monikerList.Reset();
IMoniker[] monikerContainer = new IMoniker[1];
IntPtr pointerFetchedMonikers = IntPtr.Zero;
while (monikerList.Next(1, monikerContainer, pointerFetchedMonikers) == 0)
{
Program.CreateBindCtx(0, out IBindCtx bindingContext);
runningObjectTable.GetObject(monikerContainer[0], out object comInstance);
result.Add((monikerContainer[0], bindingContext, comInstance));
}
}
finally
{
if (runningObjectTable != null)
{
Marshal.ReleaseComObject(runningObjectTable);
}
if (monikerList != null)
{
Marshal.ReleaseComObject(monikerList);
}
}
return result;
}
#endregion
}
}
此代码产生以下结果:
Dokument1
Visible: False
DocumentCount: 1
Dokument3
Visible: True
DocumentCount: 2
Dokument6
Visible: False
DocumentCount: 3
好的,这有点可疑。这种方法的一个严重问题是找不到没有文档的实例。