2

を使用してアクティベーションコンテキストを作成しようとしていますCreateActCtxWin32 API。この機能が使用されているコードはインターネット上にあまりありませんが、グーグルで検索した結果、これについて話している 2 つのブログを見つけることができ、ここまでたどり着きました。ただし、この API とその呼び出しフォーム .Net に関する詳細は、Google または SO のどこにもありません。奇妙だと思う理由は、私がやろうとしていることは、めったにないことですが、私の意見ではむしろ正当化されるからです。COM Dlls が実行時に決定されるフォルダーに存在する、登録不要の COM 相互運用機能を使用しようとしています。DLL が同じフォルダーにある場合、これはマニフェスト ファイルで実行できます。ただし、COM Dll が実行中のアセンブリの作業ディレクトリとは別のフォルダーにある場合は、マニフェスト ファイルを OS に明示的に提供する必要があります。実行時にフォルダーを決定する必要がある理由は、現時点では変更できないビジネス要件です。これを早く出さなければなりません。

私は Pinvoke の専門家ではないので、API から受け取ったエラー メッセージをどのようにデバッグすればよいかよくわかりません。この特定の例では、ここで説明されているように、「無効なパラメーター」であるエラー 87 が表示されます。Pinvoke の詳細を読んで、マーシャリングの目的で適切なマネージド型を使用していること、および MSDN ドキュメントに記載されているようにパラメーターを適切に使用していることを確認しました。この時点で、これをさらにデバッグする方法がよくわかりません! このメソッドがエラー メッセージを返す理由について、私は完全に迷っています。これが私のコードです(簡潔にするために、アクティブ化、リリース、およびその他の関連メソッドを削除しました):

// 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 を使用できるモードは 2 つあります。1 つは、EXE に埋め込まれたマニフェストを使用するか、既に個別に存在するマニフェスト ファイルを使用することです。現在、両方のメソッドがエラー メッセージを返しています。私はすでにマニフェスト ファイルの形式をテストしており、API を使用しない場合でも機能します (つまり、マニフェスト ファイルは健全です)。

これをさらにデバッグする方法についての助けをいただければ幸いです。

4

1 に答える 1

2

構造変換のちょっとした食事ができたと思います。私はそれを次のようにします:

[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

あなたはそうでしたlpResourceNameUInt16そしてそれが問題でした。プロセス アーキテクチャに応じて 32 ビットまたは 64 ビットのいずれかになりますが、16 ビットになることはありません。

于 2015-08-10T19:35:57.013 に答える