COM-объекты, RCW и динамические c ... что-то не так, не так ли? - PullRequest
0 голосов
/ 30 апреля 2020

C#, полное. NET 4.7 framework

Вот код, демонстрирующий проблему. Консольные приложения, один поток.

        static void Main()
        {
            var appType = System.Type.GetTypeFromProgID("Word.Application");
            dynamic app = System.Activator.CreateInstance(appType);
            dynamic documents = app.Documents;
            object ConfirmConversions = false;
            dynamic document = documents.Open(@"document.docx", ref ConfirmConversions);

            var b1 = true;
            var b2 = true;

            if (b1)
            {
                object doNotSaveChanges = 0;
                document.Close(SaveChanges: ref doNotSaveChanges);
            }

            if (b2)
            {
                // Exception here for b1 = true -AND- b2 = true. Why ?!
                System.Action<object> a = (o) => { };
1:              a(document);
                System.Runtime.InteropServices.Marshal.FinalReleaseComObject(document);
            }

            // Exception here for b1 = true -OR- b2 = true
2:          var name = document.Name;

            ...
        }

Если b1 = true и b2 = false, исключение находится в строке 2:

System.Runtime.InteropServices.COMException
  HResult=0x80010108
  Message=The object invoked has disconnected from its clients. (Exception from HRESULT: 0x80010108 (RPC_E_DISCONNECTED))
  Source=mscorlib
  StackTrace:
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at System.Dynamic.ComRuntimeHelpers.CheckThrowException(Int32 hresult, ExcepInfo& excepInfo, UInt32 argErr, String message)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)

Этого следует ожидать, так как COM-объект был закрыт и RCW не с чем разговаривать.

Теперь, если b1 = false и b2 = true, исключение находится в строке 2:

System.Runtime.InteropServices.InvalidComObjectException
  HResult=0x80131527
  Message=COM object that has been separated from its underlying RCW cannot be used.
  Source=System.Core
  StackTrace:
   at System.Dynamic.DynamicMetaObject.Create(Object value, Expression expression)
   at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
   at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)

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

Но когда b1 = true и b2 = true, в строке 1 есть исключение!:

System.Runtime.InteropServices.COMException
  HResult=0x80010114
  Message=The requested object does not exist. (Exception from HRESULT: 0x80010114)
  Source=mscorlib
  StackTrace:
   at System.WeakReference.Create(Object target, Boolean trackResurrection)
   at System.Dynamic.BindingRestrictions.InstanceRestriction.GetExpression()
   at System.Dynamic.BindingRestrictions.ToExpression()
   at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
   at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
   at System.Dynamic.UpdateDelegates.UpdateAndExecuteVoid2[T0,T1](CallSite site, T0 arg0, T1 arg1)

Это то, что я не понимаю. Почему происходит сбой, даже если RCW находится в области видимости и не должен был быть выпущен?

Если эта программа изменена, чтобы не использовать 'dynamici c', но полагаться на старые добрые сборки взаимодействия, это исключение не отображается и все работает просто отлично. То же самое происходит, если сначала «документ» приводится к «объекту» - без исключения.

Итак, вопрос в том, почему использование «dynamici c» меняет это поведение? Что происходит за кулисами здесь с динамическим c объектом и почему?

PS. Давайте не будем обсуждать, необходим ли FinalReleaseComObject. Неважно, любой метод, который принимает «документ» в качестве параметра, потерпит неудачу даже до того, как элемент управления войдет в этот метод.

...