C # -Мое приложение и Interopability (DLL / COM) с внешним приложением - PullRequest
0 голосов
/ 24 мая 2011

Я разрабатывал приложение C #, которое использует взаимодействие DLL с приложением внешней базы данных.

Это внешнее приложение запускается одновременно с моим приложением C # и доступно, пока мое приложение C #

Теперь реальный вопрос связан с управлением объектами, которые мне нужно создать для взаимодействия с внешним приложением.

Когда я объявляю объекты, которые доступны из ссылочной DLL, эти объектыесть методы, которые работают с файлами (которые являются собственностью) и запускают некоторые запросы (например, если это было сделано с помощью этого внешнего графического интерфейса приложения).Эти объекты уничтожаются "мной" с помощью Marshal.ReleaseComObject(A_OBJECT), в то время как другие запускаются в другой области приложения, с помощью AppDomain.CreateDomain("A_DOMAIN"), выполняют операции и вызывают AppDomain.Unload("A_DOMAIN"), освобождая библиотеки DLL, используемые для операции ...

Эти обходные пути сделаны для того, чтобы это внешнее приложение не «блокировало» файлы, используемые в этих операциях, что позволяет удалять или перемещать их из папки.* Оба эти «решения» были получены методом проб и ошибок, потому что я не владею каким-либо руководством по API.Верны ли эти решения?Можете ли вы объяснить мне различия?Мне действительно нужно работать с обоими решениями или одного должно хватить?

Спасибо!

Ответы [ 2 ]

1 голос
/ 24 мая 2011

Первое решение - лучшее. Неуправляемый COM использует схему подсчета ссылок; IUnknown является базовым интерфейсом подсчета ссылок: http://msdn.microsoft.com/en-us/library/ms680509(VS.85).aspx. Когда счетчик ссылок достигает нуля, он освобождается.

Когда вы создаете COM-объект в .NET, вокруг COM-объекта создается оболочка. Оболочка поддерживает указатель на базовый IUnknown. Когда происходит сборка мусора, оболочка вызывает основную функцию IUnknown :: Release () для освобождения COM-объекта во время финализации. Как вы заметили, проблема в том, что иногда COM-объект блокирует определенные критические ресурсы. Вызывая Marshal.ReleaseComObject, вы вызываете немедленный вызов IUnknown :: Release без необходимости ждать (или инициировать) общей сборки мусора. Если никакие другие ссылки на COM-объект не хранятся, он будет немедленно освобожден. Конечно, после этого момента оболочка .NET становится недействительной.

Второе решение, очевидно, работает из-за вызова GC.Collect (). Решение является более неуклюжим, медленнее и менее надежным (COM-объект не обязательно должен быть сборщиком мусора: поведение зависит от конкретной версии .NET Framework). Использование AppDomain ничего не дает, поскольку ваш код фактически ничего не делает, кроме создания пустого домена и его последующей выгрузки. Домены приложений полезны для изоляции загруженных сборок .NET Framework. Поскольку задействован неуправляемый код COM, домены приложений не будут действительно полезными (если вам нужна изоляция, используйте изоляцию процесса). Вторая функция, вероятно, может быть переписана как:

    public int DbNumRecs(string file) {
        if (!File.Exists(file)) {
            return -1;
        }
        // don't need to use AppDomain
        COMMONIDEACONTROLSLib db = null; // don't need to initialize class here
        try {
            db = objApp.OpenDatabase(file);
            return (int)db.Count;
        } catch (Exception) } // don't need to declare unused ex variable
            return -1;
        } finally {
            try {
                if (db != null) {
                    db.Close();
                    Marshal.ReleaseComObject(db);
                }
                objApp.CloseDatabase(file); // is this line really needed?
            } catch (Exception) {} // silently ignore exceptions when closing
        }
    }
1 голос
/ 24 мая 2011

Вы неправильно используете домены приложений. Тот факт, что вы создаете новый домен приложений до строки X, не означает, что строка X фактически выполняется в этом домене приложений.

Вам необходимо маршалировать прокси-класс через свой AppDomain и использовать его в текущем.

public sealed class DatabaseProxy : MarshallByRefObject
{
    public int NumberOfRecords()
    {    
        COMMONIDEACONTROLSLib db = new COMMONIDEACONTROLSLibClass();
        try
        {
            db = objApp.OpenDatabase(file);
            int count = (int)db.Count;

            db.Close();
            objApp.CloseDatabase(file);

            return count;
        }
        catch (Exception ex)
        {
            return -1;
        }
    }
}

и

public int NumberOfRecords()
{    

    System.AppDomain newDomain = null;

    try
    {
        newDomain = System.AppDomain.CreateDomain();
        var proxy = newDomain.CreateInstanceAndUnwrap(
                                  typeof(DatabaseProxy).Assembly.FullName,
                                  typeof(DatabaseProxy).FullName);
        return proxy.NumberOfRecords();
    }
    finally
    {
        System.AppDomain.Unload(newDomain);
    }
}

Вы можете создать маршалл для самого COM-объекта, а не создавать его через прокси-сервер. Этот код полностью написан здесь и не проверен, поэтому может содержать ошибки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...