在C#中,Marshal.GetActiveObject()会抛出MK_E_UNAVAILABLE异常。

17
以下VBScript代码完全正常运行:
Dim App 
Set App = GetObject("","QuickTest.Application")
App.Quit

但是当我将其翻译成以下C#代码时:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        object qtApp = Marshal.GetActiveObject("QuickTest.Application");
        (qtApp as QuickTest.Application).Quit();
    }
}

我遇到了异常:

未处理的异常类型为 'System.Runtime.InteropServices.COMException',发生在 mscorlib.dll 中

附加信息: (来自 HRESULT 的异常:0x800401E3 (MK_E_UNAVAILABLE))

我不认为这个问题与 ROT 有关,因为 VBScript 代码可以正常运行。那么 C# 代码出了什么问题呢?

3个回答

32
我发现以管理员权限(即管理员模式)运行调试器/IDE可能会导致这个问题,当您尝试检测的进程在没有提升权限的情况下运行时。

2
我真是太傻了,没有看到你的评论。所以我一直尝试尝试,直到想到从受限制的命令提示符中运行我的程序。然后它奏效了。然后我来到这里添加我的新发现的知识,然后看到了你的评论。噢好吧。干杯! - Mike Nakis

13

Marshal.GetActiveObject通过使用progID来获取活动对象,在使用此代码显示ROT中的对象之前,请检查您的progID。

using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using Microsoft.Win32;
...
class Program
{
    private const int S_OK = 0x00000000;

    [DllImport("ole32.dll")]
    private static extern int GetRunningObjectTable(uint reserved, out IRunningObjectTable pprot);

    [DllImport("ole32.dll")]
    private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);      

    private static void OleCheck(string message, int result)
    {
        if (result != S_OK)
            throw new COMException(message, result);
    }

    private static System.Collections.Generic.IEnumerable<IMoniker> EnumRunningObjects()
    {           
        IRunningObjectTable objTbl;
        OleCheck("GetRunningObjectTable failed", GetRunningObjectTable(0, out objTbl));
        IEnumMoniker enumMoniker;
        IMoniker[] monikers = new IMoniker[1];
        objTbl.EnumRunning(out enumMoniker);
        enumMoniker.Reset();
        while (enumMoniker.Next(1, monikers, IntPtr.Zero) == S_OK)
        {
            yield return monikers[0];
        }
    }

    private static bool TryGetCLSIDFromDisplayName(string displayName, out string clsid)
    {
        var bBracket = displayName.IndexOf("{");
        var eBracket = displayName.IndexOf("}");
        if ((bBracket > 0) && (eBracket > 0) && (eBracket > bBracket))
        {
            clsid = displayName.Substring(bBracket, eBracket - bBracket + 1);
            return true;
        }
        else 
        {
            clsid = string.Empty;
            return false;
        }   
    }

    private static string ReadSubKeyValue(string keyName, RegistryKey key)
    {
        var subKey = key.OpenSubKey(keyName);
        if (subKey != null)
        {
            using(subKey)
            {
                var value = subKey.GetValue("");
                return value == null ? string.Empty : value.ToString();
            }
        }
        return string.Empty;
    }

    private static string GetMonikerString(IMoniker moniker)
    {
        IBindCtx ctx;
        OleCheck("CreateBindCtx failed", CreateBindCtx(0, out ctx));
        var sb = new StringBuilder();
        string displayName;
        moniker.GetDisplayName(ctx, null, out displayName);
        sb.Append(displayName);
        sb.Append('\t');
        string clsid; 
        if (TryGetCLSIDFromDisplayName(displayName, out clsid))
        {
            var regClass = Registry.ClassesRoot.OpenSubKey("\\CLSID\\" + clsid);
            if (regClass != null)
            {
                using(regClass)
                {
                    sb.Append(regClass.GetValue(""));
                    sb.Append('\t');
                    sb.Append(ReadSubKeyValue("ProgID", regClass));
                    sb.Append('\t');
                    sb.Append(ReadSubKeyValue("LocalServer32", regClass));
                }
            }
        }
        return sb.ToString();
    }

    [STAThread]
    public static void Main(string[] args)
    {
        Console.WriteLine("DisplayName\tRegId\tProgId\tServer");
        foreach(var moniker in EnumRunningObjects())
        {
            Console.WriteLine(GetMonikerString(moniker));
        }
    }
}  

1
谢谢!我使用了你的代码来列出ROT中的对象,但是在列表中没有找到任何与QuickTest相关的对象。但奇怪的是,vbscript代码仍然可以工作!是因为vbscript的GetObject()函数不像它的C#对应函数一样查找ROT吗? - TomCaps
1
如果路径名是一个零长度的字符串(“”),GetObject将返回指定类型的新对象实例。因此,您的VBScript将创建QuickTest.Application的新实例,而您的C#代码必须如下所示: var qtApp = new QuickTest.Application(); ... - MishaU
我将C#代码更改为var qtApp = new QuickTest.Application();,它可以工作了!非常感谢! - TomCaps
如果没有ProgID,该怎么办? - Fowl

3
问题也可能由于没有使用提升的特权而触发。这似乎在多年后已经改变,因此请尝试使用或不使用提升的特权运行IDE或目标程序的所有排列组合。
截至2017年8月,为了在Visual Studio 2015调试器下运行Outlook 365实例,我必须按照以下步骤避免MK_E_UNAVAILABLE错误:
- 以管理员身份启动Outlook - 以管理员身份启动Visual Studio
然后我的在调试器中运行的程序就能成功地获取正在运行的Outlook实例了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接