我正在使用来自第三方COM Server的API的项目。 COM服务器是一个本地服务器(进程外exe),我无法控制它。
我试图从runnin对象表中访问COM对象,以便在每个应用程序实例启动的COM对象的几个实例之间进行选择:
private static List<object> GetRunningInstances(string progId) {
// get Running Object Table ...
IRunningObjectTable Rot = null;
GetRunningObjectTable(0, out Rot);
if (Rot == null)
return null;
// get enumerator for ROT entries
IEnumMoniker monikerEnumerator = null;
Rot.EnumRunning(out monikerEnumerator);
if (monikerEnumerator == null) return null;
monikerEnumerator.Reset();
List<object> instances = new List<object>();
IntPtr pNumFetched = new IntPtr();
IMoniker[] monikers = new IMoniker[1];
while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0) {
IBindCtx bindCtx;
CreateBindCtx(0, out bindCtx);
if (bindCtx == null) continue;
Guid clsid = Type.GetTypeFromProgID(progId).GUID;
string displayName;
Guid monikerClsid;
Guid riid = Marshal.GenerateGuidForType(typeof(IApplication));
object obj;
monikers[0].GetDisplayName(bindCtx, null, out displayName);
monikers[0].GetClassID(out monikerClsid);
if (displayName.IndexOf(clsid.ToString(), StringComparison.OrdinalIgnoreCase) > 0) {
//monikers[0].BindToObject(bindCtx, null, ref unkid, out obj);
Rot.GetObject(monikers[0], out obj);
instances.Add((IApplication)obj);
}
}
}
如果我启动目标应用程序的两个实例,则ROT转储显示相应COM对象的两个实例(此处名为IApplication),因为GetDisplayName显示在注册表中注册的接口IApplication的正确clsid。
问题是我从Rot.GetObject获取的对象被描述为System .__ ComObject,并且无法转换为IApplication(InvalidCastException,因为QueryInterface因E_NOINTERFACE而失败),即使它们的名字对象描述了正确的clsid ......
我尝试将它以编程方式投射到我项目中的每个可用类型,只是为了查看,但唯一的成功是将其转换为System .__ ComObject ...
我也尝试使用IMoniker.BindToObject而不是Rot.GetObject,但这一次,当我提供相应的接口GUID时,我得到一个FileNotFound异常。当我为IUnknown提供riid时BindToObject工作,但它给了我一个System .__ ComObject我不能演员(回到原点!)。
有关信息,在ROT转储中,我还可以显示与目标应用程序中的打开项目对应的文件名字对象,但我也无法从此创建COM对象。
任何人都知道如何从ROT中正确检索对象?
谢谢, 问候。
经过几天的关注并重新审视这个问题后,我发现名字的显示名称中的CLSID与我想要的完全相同但是有两位数字(测试索引结果是错)。
在仔细阅读了clsid后,事实证明,名字对象的显示名称中的clsid是IApplication的coclass的clsid,而不是IApplication的clsid。
我尝试将对象转换为&#34; ApplicationClass&#34; (前面提到的coclass)但这给了我一个例外。我得到的额外信息(法语)可以翻译如下:不可能将__ComObject包装器实例转换为另一个类,但只要底层的com组件支持接口IID的QueryInterface调用,它就可以将这些实例转换为接口
关于如何从这里开始的任何想法?
答案 0 :(得分:0)
如果您仍然有问题,我现在就不知道了,但是既然还没有答案,我想分享一下我在处理ROT上的多个COM接口的几周研究之后所做的工作。
我已经从ROT中获取了所有对象,并将它们存储在哈希表中,以确保通过键/值实现唯一性,但是您可以根据需要将其调整为列表。 您只需要获取运行对象的值即可添加列表。
public static Hashtable GetRunningObjectTable()
{
Hashtable result = new Hashtable();
IntPtr numFetched = new IntPtr();
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
result[runningObjectName] = runningObjectVal;
}
return result;
}
之后,您可以将存储在列表中的对象转换为COM接口。例如,下面我还分享了适用于Rhapsody COM接口的方式;
Hashtable runningObjects = GetRunningObjectTable();
IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
while (rotEnumerator.MoveNext())
{
string candidateName = (string)rotEnumerator.Key;
if (!candidateName.StartsWith("Rhapsody"))
continue;
rhapsody.RPApplication app = rotEnumerator.Value as rhapsody.RPApplication;
if (app == null)
continue;
// Do your stuff app (com object) in here..
}