用CreateActCtx Win32 API创建一个激活上下文

本文关键字:一个 激活 上下文 CreateActCtx Win32 API 创建 | 更新日期: 2023-09-27 18:07:08

我试图使用CreateActCtx Win32 API创建一个激活上下文。在互联网上使用这个函数的代码并不多,但我在谷歌上搜索了很多之后,找到了两个讨论这个函数的博客。然而,我还没有找到更多的信息在这个API和它的调用形式。net在其他任何地方谷歌或SO这是奇怪的。我觉得奇怪的原因是,我正在尝试做一些虽然罕见,但在我看来相当合理的事情。我试图使用注册免费COM互操作,其中COM dll驻留在一个文件夹中,将在运行时决定。当dll位于同一文件夹中时,可以使用清单文件完成此操作。但是,当COM Dll位于执行程序集工作目录以外的另一个文件夹中时,必须显式地向操作系统提供清单文件。为什么必须在运行时期间决定文件夹是一个业务需求,此时不能更改。我们得快点把它弄出来。

我不是Pinvoke的专家,我真的不知道应该如何调试从API收到的错误消息。在这个特定的实例中,我收到一个错误87,它是"无效参数",如下所述。我试着阅读了更多关于Pinvoke的信息,并确保我使用了正确的托管类型来进行编组,并且按照MSDN文档中的描述正确使用了参数。在这一点上,我真的不知道如何进一步调试它!我完全不知道为什么这个方法返回一个错误消息。下面是我的代码(为了简洁起见,我删除了activate、release和其他相关方法):

// Activation context structure
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
internal struct ACTCTX
{
    public Int32 cbSize;
    public UInt32 dwFlags;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string lpSource;
    public UInt16 wProcessorArchitecture;
    public UInt16 wLangId;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string lpAssemblyDirectory;
    public UInt16 lpResourceName;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string lpApplicationName;
    public IntPtr hModule;
}
// Activation Context API Functions
[DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "CreateActCtx")]
internal extern static IntPtr CreateActCtx(ref ACTCTX actctx);
private IntPtr m_hActCtx = (IntPtr)0;
public ActivationContextWin32API()
{
    m_hActCtx = (IntPtr)0;
}
//private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;
private const int ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x008;
public bool CreateContext(string manifestPath, string rootFolder, out UInt32 dwError)
{
    dwError = 0;
    ACTCTX info = new ACTCTX();
    info.cbSize = Marshal.SizeOf(typeof(ACTCTX));
    info.lpSource = manifestPath;
    info.hModule = IntPtr.Zero;
    info.lpAssemblyDirectory = rootFolder;
    info.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
    info.lpResourceName = 2;
    lock (this)
    {
        m_hActCtx = CreateActCtx(ref info);
        if (m_hActCtx == (IntPtr)(-1))
        {
            dwError = (uint)Marshal.GetLastWin32Error();
            return false;
        }
    }
    return true;
}

我可以使用这个API的两种模式。一种是使用嵌入在EXE中的清单或使用已经单独存在的清单文件。目前这两个方法都返回错误消息。我已经测试了清单文件的格式,当我不通过API(所以清单文件是声音)时,它可以工作。

任何关于如何进一步调试的帮助将不胜感激。

用CreateActCtx Win32 API创建一个激活上下文

我认为你在结构翻译上已经做得很好了。我希望它是这样的:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct ACTCTX
{
    public int cbSize;
    public uint dwFlags;
    public string lpSource;
    public UInt16 wProcessorArchitecture;
    public UInt16 wLangId;
    public string lpAssemblyDirectory;
    public string lpResourceName;
    public string lpApplicationName;
    public IntPtr hModule;
}

我认为支持ANSI已经没有什么意义了。这意味着函数应该是:

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
extern static IntPtr CreateActCtx(ref ACTCTX actctx);

现在,当你试图将lpResourceName设置为资源索引时,这将会给你带来麻烦。所以你可以用这个作为你的结构声明:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct ACTCTX
{
    public int cbSize;
    public uint dwFlags;
    public string lpSource;
    public UInt16 wProcessorArchitecture;
    public UInt16 wLangId;
    public string lpAssemblyDirectory;
    public IntPtr lpResourceName;
    public string lpApplicationName;
    public IntPtr hModule;
}

设置lpResourceName(IntPtr)2

你有lpResourceName作为UInt16,这就是问题所在。根据进程体系结构,它可以是32位或64位,但永远不会是16位。