[英]C# : Get COM object from the Running Object Table
我正在使用來自第三方 COM 服務器的 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 與我想要的不完全相同,但有兩個數字(indexof 測試結果是錯誤的)。
仔細閱讀 clsid 后,發現名稱顯示名稱中的 clsid 是 IApplication 的 coclass 的 clsid,而不是 IApplication 的 clsid。
我嘗試將對象強制轉換為“ApplicationClass”(前面提到的 coclass),但這給了我一個例外。 我得到的附加信息(法語)可以翻譯如下: 無法將 __ComObject 包裝器實例轉換為另一個類,但只要底層 com 組件支持接口 IID 的 QueryInterface 調用,就可以將這些實例轉換為接口。
關於如何從這里進行的任何想法?
如果您仍然有問題,我現在不知道,但由於還沒有答案,我想分享我在經過幾周關於在 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..
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.