COM не может запустить внепроцессный сервер .Net, скомпилированный как AnyCPU - PullRequest
9 голосов
/ 16 февраля 2012

Я пытаюсь заставить COM запустить мой внепроцессный COM-сервер .NET.Это работает, если серверный процесс скомпилирован с x64, но если я использую AnyCPU (что я и хочу), то он на некоторое время зависает и в конечном итоге дает сбой с 0x80080005 (CO_E_SERVER_EXEC_FAILURE).Как мне заставить это работать?

  • Я работаю на 64-разрядной машине: Windows 7 с Visual Studio 2008 SP1.
  • Я вижу в диспетчере задач, что он делаетзапустить мой сервер.Поэтому, я думаю, проблема в связи между COM и сервером (регистрация класса).
  • Мое тестовое клиентское приложение написано на C #, но не имеет значения, скомпилировано ли оно для x86 или x64.Проблема также возникает с чем-то, написанным на 32-битном C ++.
  • Если я перекомпилирую сервер с использованием x64 и запусту его, а затем перестрою обратно как AnyCPU, тогда COM сможет его запустить.Перезагрузка вернет меня к исходной ситуации.Возможно, COM заранее не знает, какая битность будет использоваться, и предыдущее выполнение помогает.
  • Я нашел сообщение в блоге Энди Макмаллена и попытался передать CLSCTX_ACTIVATE_64_BIT_SERVER в CoCreateInstance (), ноэто вызывает ошибку раньше: 0x80040154 (REGDB_E_CLASSNOTREG).Я делаю что-то не так в своей регистрации COM?Ниже вы можете увидеть, что это очень просто.Регистрация происходит при работе в 64 битах, и проблема возникает, когда клиент 64 бит, поэтому Wow6432Node не должен быть задействован.

У другого главы была похожая проблема , ноответ MSFT сбивает с толку.Кажется, он предполагает, что он может работать только через DCOM (см. Ссылку) или COM +.Я подозреваю, что либо это будет ужасно много работы, и существенно хуже, чем распространение моего .exe, созданного как x64 и x86.

Вы можете быть удивлены, почему я реализую IPersistFile.Это потому, что моя настоящая проблема заключается в том, чтобы заставить BindMoniker () работать из 32-битной программы C ++ в моей программе AnyCPU .Net.Я сократил мою проблему до более простого примера, представленного здесь.

Вот код клиента:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    [DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)]
    [return: MarshalAs(UnmanagedType.Interface)]
    static extern object CoCreateInstance(
       [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
       [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter,
       CLSCTX dwClsContext,
       [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);

    [Flags]
    enum CLSCTX : uint
    {
        CLSCTX_LOCAL_SERVER = 0x4,
        CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000,
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        IPersistFile pf = (IPersistFile)CoCreateInstance(
            new Guid("1984D314-FC8D-44bc-9146-8A13500666A6"),
            null,
            CLSCTX.CLSCTX_LOCAL_SERVER,
            new Guid("0000010b-0000-0000-C000-000000000046"));  // IPersistFile
        pf.Load("c:\\bozo", 0);
    }
}

, а вот сервер:

static class Program
{
    [STAThread]
    static void Main()
    {
        if (Environment.CommandLine.Contains("/reg")) {
            RegistryKey cls = Registry.LocalMachine.CreateSubKey(String.Format(
                "SOFTWARE\\Classes\\CLSID\\{0}", PersistFile.ClassID.ToString("B")));
            cls.SetValue("InprocHandler32", "Ole32.dll");
            RegistryKey ls32 = cls.CreateSubKey("LocalServer32");
            ls32.SetValue(null, '"' + Application.ExecutablePath + '"');
            ls32.SetValue("ServerExecutable", Application.ExecutablePath);
        }

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        RegistrationServices reg = new RegistrationServices();
        reg.RegisterTypeForComClients(
            typeof(PersistFile),
            RegistrationClassContext.LocalServer,
            RegistrationConnectionType.MultipleUse);

        Application.Run(new Form1());
    }
}

[ComVisible(true),
 Guid("1984D314-FC8D-44bc-9146-8A13500666A6"),
 ClassInterface(ClassInterfaceType.None)]
public class PersistFile : IPersistFile
{
    public static Guid ClassID
    {
        get
        {
            GuidAttribute a = (GuidAttribute)typeof(PersistFile).GetCustomAttributes(typeof(GuidAttribute), false)[0];
            return new Guid(a.Value);
        }
    }

    #region IPersistFile
    public void GetClassID(out Guid pClassID)
    {
        MessageBox.Show("GetClassID");
        pClassID = ClassID;
    }

    public int IsDirty()
    {
        MessageBox.Show("IsDirty");
        return 1;
    }

    public void Load(string pszFileName, int dwMode)
    {
        MessageBox.Show(String.Format("Load {0}", pszFileName));
    }

    public void Save(string pszFileName, bool fRemember)
    {
        MessageBox.Show("Save");
        throw new NotImplementedException();
    }

    public void SaveCompleted(string pszFileName)
    {
        MessageBox.Show("SaveCompleted");
        throw new NotImplementedException();
    }

    public void GetCurFile(out string ppszFileName)
    {
        MessageBox.Show("GetCurFile");
        throw new NotImplementedException();
    }
    #endregion
}

Ответы [ 2 ]

1 голос
/ 04 июня 2013

Я думаю, проблема во время выполнения. Я создал COM-сервер, который регистрируется с использованием библиотеки C ++ (регистрация выполняется без нареканий). У меня возникли проблемы при переходе на AnyCPU из .NET (CS).

Архитектура:

  • Интерфейс COM библиотеки C ++ (построен на платформах x64 и x86)
  • Оболочка библиотеки .NET (CS) (правильно создает необходимую библиотеку C64 для x64 / x86)
  • .NET-приложение (CS) - COM-клиент или COM-сервер

Ужасные вещи случаются при регистрации приложения .NET, созданного как «AnyCPU». Как только COM-клиент вызывает COM-сервер через DCOM, приложение сервера запускается, но клиент получает ошибку, что COM-сервер не может быть запущен.

Я пошел дальше, проанализировал регистрационные данные с помощью procmon и других инструментов и пришел к тому же выводу:

  • x86 регистрирует классы в CLASSES \ Wow6432Node
  • x64 и AnyCPU регистрируют классы в CLASSES (на компьютере с Windows x64 точно такие же ключи; держу пари, что x86 и AnyCPU будут регистрировать одно и то же на компьютере с архитектурой x86)

Теперь я провел еще несколько экспериментов: COM-клиент x86 / x64 / AnyCPU может без проблем подключиться к любому COM-серверу x86 / x64, но не может каким-либо образом подключиться к COM-серверу AnyCPU ...

Затем я выполнил следующие тестовые примеры:

  1. Попросите регистр COM-сервера x86, замените исполняемый файл COM-сервером AnyCPU: COM-клиент запускал COM-сервер x86, но без связи ... он запускал сервер снова и снова ..
  2. Попросите регистр COM-сервера x64, замените исполняемый файл COM-сервером AnyCPU: COM-клиент запускал COM-сервер x64, но без связи ... он запускал сервер снова и снова ..
  3. Зарегистрируйте COM-сервер AnyCPU, замените исполняемый файл COM-сервером x86: COM-клиент смог успешно запустить и подключиться к COM-серверу x86.
  4. Зарегистрируйте AnyCPU COM-сервер, замените исполняемый файл COM-сервером x64: COM-клиент смог успешно запустить и подключиться к COM-серверу x64.
  5. Зарегистрируйте COM-сервер x86, замените исполняемый файл COM-сервером x64: COM-клиент смог успешно запустить и подключиться к COM-серверу x64.
  6. Зарегистрируйте COM-сервер x64, замените исполняемый файл COM-сервером x86: COM-клиент смог успешно запустить и подключиться к COM-серверу x86.

Где, черт возьми, проблема со связью? Это очень странно ... Ни одно из представленных решений (CLSCTX_ACTIVATE_64_BIT_SERVER, PreferredServerBitness или corflags) не помогло.

Кто-нибудь еще продвинулся в этом вопросе? Должны ли мы связаться с Microsoft?

1 голос
/ 22 февраля 2012

Попробуйте использовать класс RegistrationServices для регистрации вашей сборки.Он также выберет правильные пути в реестре и выполнит некоторые другие действия.

Пример:

Assembly currentAssembly = Assembly.GetExecutingAssembly();
System.Runtime.InteropServices.RegistrationServices regAsm = new System.Runtime.InteropServices.RegistrationServices();
bool isRegistered = regAsm.RegisterAssembly(currentAssembly, System.Runtime.InteropServices.AssemblyRegistrationFlags.SetCodeBase);

Также я думаю, что клиентские сборки .NET имеют некоторые проблемы с серверами .NET com,но я не могу найти никакого ресурса для этого ...

Надеюсь, это поможет ...

...