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. Неважно, любой метод, который принимает «документ» в качестве параметра, потерпит неудачу даже до того, как элемент управления войдет в этот метод.