Öğretici: API'yi ComWrappers
kullanma
Bu öğreticide, iyileştirilmiş ve AOT dostu bir COM birlikte çalışma çözümü sağlamak için türü düzgün bir şekilde alt sınıfa ComWrappers
almayı öğreneceksiniz. Bu öğreticiye başlamadan önce COM, mimarisi ve mevcut COM birlikte çalışma çözümleri hakkında bilgi sahibi olmanız gerekir.
Bu öğreticide aşağıdaki arabirim tanımlarını uygulayacaksınız. Bu arabirimler ve uygulamaları şunları gösterir:
- COM/.NET sınırında türleri sıralama ve özetleme kaldırma.
- .NET'te yerel COM nesnelerini kullanmaya yönelik iki farklı yaklaşım.
- .NET 5 ve sonrasında özel COM birlikte çalışabilirlik özelliğini etkinleştirmek için önerilen desen.
Bu öğreticide kullanılan tüm kaynak kodu dotnet/samples deposunda kullanılabilir.
Not
.NET 8 SDK ve sonraki sürümlerinde, sizin için otomatik olarak bir API uygulaması oluşturmak için bir ComWrappers
kaynak oluşturucu sağlanır. Daha fazla bilgi için bkz ComWrappers
. kaynak oluşturma.
C# tanımları
interface IDemoGetType
{
string? GetString();
}
interface IDemoStoreType
{
void StoreString(int len, string? str);
}
Win32 C++ tanımları
MIDL_INTERFACE("92BAA992-DB5A-4ADD-977B-B22838EE91FD")
IDemoGetType : public IUnknown
{
HRESULT STDMETHODCALLTYPE GetString(_Outptr_ wchar_t** str) = 0;
};
MIDL_INTERFACE("30619FEA-E995-41EA-8C8B-9A610D32ADCB")
IDemoStoreType : public IUnknown
{
HRESULT STDMETHODCALLTYPE StoreString(int len, _In_z_ const wchar_t* str) = 0;
};
Tasarıma ComWrappers
genel bakış
API, ComWrappers
.NET 5+ çalışma zamanıyla COM birlikte çalışma zamanı gerçekleştirmek için gereken en düşük etkileşimi sağlamak üzere tasarlanmıştır. Bu, yerleşik COM birlikte çalışma sistemiyle var olan özelliklerin çoğunun mevcut olmadığı ve temel yapı bloklarından oluşturulması gerektiği anlamına gelir. API'nin iki birincil sorumluluğu şunlardır:
- Verimli nesne tanımlama (örneğin, bir
IUnknown*
örnek ve yönetilen nesne arasında eşleme). - Çöp Toplayıcı (GC) etkileşimi.
Bu verimlilikler, SARMALAYıCı oluşturma ve alma işleminin ComWrappers
API üzerinden geçmesi gerekerek gerçekleştirilir.
ComWrappers
API'nin çok az sorumluluğu olduğundan, birlikte çalışma çalışmalarının çoğunun tüketici tarafından ele alınması gerektiği anlamına gelir; bu doğrudur. Bununla birlikte, ek iş büyük ölçüde mekaniktir ve bir kaynak oluşturma çözümü tarafından gerçekleştirilebilir. Örnek olarak, C#/WinRT araç zinciri , WinRT birlikte çalışma desteği sağlamak için üzerine ComWrappers
kurulmuş bir kaynak oluşturma çözümüdür.
Alt sınıf uygulama ComWrappers
Alt sınıf sağlamak ComWrappers
, COM'a yansıtılan yönetilen nesneler ve .NET'e yansıtılan COM nesneleri için sarmalayıcılar oluşturmak ve kaydetmek için .NET çalışma zamanına yeterli bilgi vermek anlamına gelir. Alt sınıfın ana hatlarını incelemeden önce bazı terimler tanımlamamız gerekir.
Yönetilen Nesne Sarmalayıcı – Yönetilen .NET nesneleri, non-.NET bir ortamdan kullanımı etkinleştirmek için sarmalayıcılar gerektirir. Bu sarmalayıcılar geçmişte COM Çağrılabilir Sarmalayıcılar (CCW) olarak adlandırılır.
Yerel Nesne Sarmalayıcı – non-.NET dilde uygulanan COM nesneleri, sarmalayıcıların .NET'ten kullanımı etkinleştirmesini gerektirir. Bu sarmalayıcılar geçmişte Çalışma Zamanı Çağrılabilir Sarmalayıcıları (RCW) olarak adlandırılır.
1. Adım – Amaçlarını uygulamak ve anlamak için yöntemler tanımlama
Türü genişletmek ComWrappers
için aşağıdaki üç yöntemi uygulamanız gerekir. Bu yöntemlerin her biri, kullanıcının bir sarmalayıcı türü oluşturma veya silme işlemine katılımını temsil eder. ComputeVtables()
ve CreateObject()
yöntemleri sırasıyla bir Yönetilen Nesne Sarmalayıcı ve Yerel Nesne Sarmalayıcı oluşturur. ReleaseObjects()
yöntemi, sağlanan sarmalayıcı koleksiyonunun temel alınan yerel nesneden "serbest bırakılması" için istekte bulunmak için çalışma zamanı tarafından kullanılır. Çoğu durumda, yalnızca Başvuru İzleyicisi çerçevesini ReleaseObjects()
içeren gelişmiş bir senaryoda çağrıldığından yönteminin gövdesini oluşturabilirNotImplementedException.
// See referenced sample for implementation.
class DemoComWrappers : ComWrappers
{
protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) =>
throw new NotImplementedException();
protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags) =>
throw new NotImplementedException();
protected override void ReleaseObjects(IEnumerable objects) =>
throw new NotImplementedException();
}
Yöntemini uygulamak ComputeVtables()
için hangi yönetilen türleri desteklemek istediğinize karar verin. Bu öğreticide, daha önce tanımlanmış iki arabirimi ( ve ) ve IDemoStoreType
iki arabirimi (IDemoGetType
DemoImpl
) uygulayan bir yönetilen türü destekleyeceğiz.
class DemoImpl : IDemoGetType, IDemoStoreType
{
string? _string;
public string? GetString() => _string;
public void StoreString(int _, string? str) => _string = str;
}
yöntemi için CreateObject()
neyi desteklemek istediğinizi de belirlemeniz gerekir. Ancak bu durumda, COM sınıflarını değil yalnızca ilgilendiğimiz COM arabirimlerini biliyoruz. COM tarafından tüketilen arabirimler, .NET tarafından yansıtılan arabirimlerle aynıdır (yani IDemoGetType
ve IDemoStoreType
).
Bu öğreticide uygulamayacağız ReleaseObjects()
.
2. Adım – Uygulama ComputeVtables()
Yönetilen Nesne Sarmalayıcı ile başlayalım; bu sarmalayıcılar daha kolaydır. Bunları COM ortamına yansıtmak için her arabirim için bir Sanal Yöntem Tablosu veya vtable oluşturacaksınız. Bu öğreticide, bir vtable'ı işaretçi dizisi olarak tanımlayacaksınız; burada her işaretçi bir arabirimdeki işlevin uygulamasını temsil eder; burada sıra çok önemlidir. COM'da her arabirim öğesinden IUnknown
devralır. Türün IUnknown
şu sırada tanımlanan üç yöntemi vardır: QueryInterface()
, AddRef()
ve Release()
. IUnknown
Yöntemler geldikten sonra belirli arabirim yöntemleri gelir. Örneğin, ve IDemoStoreType
değerini göz önünde bulundurunIDemoGetType
. Kavramsal olarak, türlerin vtable'ları aşağıdaki gibi görünür:
IDemoGetType | IDemoStoreType
==================================
QueryInterface | QueryInterface
AddRef | AddRef
Release | Release
GetString | StoreString
öğesine baktığımızda DemoImpl
ve StoreString()
için GetString()
zaten bir uygulamamız var ama işlevler ne olacakIUnknown
? Bir IUnknown
örneğin nasıl uygulanacağı bu öğreticinin kapsamı dışındadır, ancak içinde ComWrappers
el ile yapılabilir. Ancak bu öğreticide çalışma zamanının bu bölümü işlemesine izin vereceksiniz. yöntemini kullanarak ComWrappers.GetIUnknownImpl()
uygulamayı alabilirsinizIUnknown
.
Tüm yöntemleri uygulamış gibi görünebilirsiniz, ancak ne yazık ki com sanal makinesinde yalnızca IUnknown
işlevler kullanılabilir. COM çalışma zamanının dışında olduğundan, uygulamanız için DemoImpl
yerel işlev işaretçileri oluşturmanız gerekir. Bu işlem C# işlev işaretçileri ve UnmanagedCallersOnlyAttribute
kullanılarak yapılabilir. COM işlev imzasını taklit eden bir işlev oluşturarak vtable'a eklenecek bir static
işlev oluşturabilirsiniz. Aşağıda için IDemoGetType.GetString()
COM imzası örneği verilmiştir. COM ABI'den ilk bağımsız değişkenin örneğin kendisi olduğunu hatırlayın.
[UnmanagedCallersOnly]
public static int GetString(IntPtr _this, IntPtr* str);
sarmalayıcı uygulaması IDemoGetType.GetString()
, sıralama mantığından ve ardından sarmalanan yönetilen nesneye bir gönderimden oluşmalıdır. Dağıtım için tüm durum, sağlanan _this
bağımsız değişken içinde yer alır. Bağımsız _this
değişken aslında türünde ComInterfaceDispatch*
olacaktır. Bu tür, Vtable
daha sonra tartışılacak tek bir alanı olan düşük düzeyli bir yapıyı temsil eder. Bu türün ve düzeninin diğer ayrıntıları çalışma zamanının uygulama ayrıntılarıdır ve buna bağlı olmamalıdır. Yönetilen örneği bir ComInterfaceDispatch*
örnekten almak için aşağıdaki kodu kullanın:
IDemoGetType inst = ComInterfaceDispatch.GetInstance<IDemoGetType>((ComInterfaceDispatch*)_this);
Artık bir vtable'a eklenebilen bir C# yönteminiz olduğuna göre, vtable'ı oluşturabilirsiniz. Belleği kaldırılabilir derlemelerle çalışacak şekilde ayırmaya yönelik kullanımına RuntimeHelpers.AllocateTypeAssociatedMemory()
dikkat edin.
GetIUnknownImpl(
out IntPtr fpQueryInterface,
out IntPtr fpAddRef,
out IntPtr fpRelease);
// Local variables with increment act as a guard against incorrect construction of
// the native vtable. It also enables a quick validation of final size.
int tableCount = 4;
int idx = 0;
var vtable = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(
typeof(DemoComWrappers),
IntPtr.Size * tableCount);
vtable[idx++] = fpQueryInterface;
vtable[idx++] = fpAddRef;
vtable[idx++] = fpRelease;
vtable[idx++] = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr*, int>)&ABI.IDemoGetTypeManagedWrapper.GetString;
Debug.Assert(tableCount == idx);
s_IDemoGetTypeVTable = (IntPtr)vtable;
Vtable'ların ayrılması, uygulamasının ComputeVtables()
ilk bölümüdür. Ayrıca, desteklemeyi planladığınız türler için kapsamlı COM tanımları oluşturmanız gerekir; düşünün ve com'dan hangi bölümlerinin kullanılabilir olması gerektiğini düşünün DemoImpl
. Artık, yapılandırılan vtable'ları kullanarak, COM'da yönetilen nesnenin ComInterfaceEntry
tam görünümünü temsil eden bir dizi örnek oluşturabilirsiniz.
s_DemoImplDefinitionLen = 2;
int idx = 0;
var entries = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(
typeof(DemoComWrappers),
sizeof(ComInterfaceEntry) * s_DemoImplDefinitionLen);
entries[idx].IID = IDemoGetType.IID_IDemoGetType;
entries[idx++].Vtable = s_IDemoGetTypeVTable;
entries[idx].IID = IDemoStoreType.IID_IDemoStoreType;
entries[idx++].Vtable = s_IDemoStoreVTable;
Debug.Assert(s_DemoImplDefinitionLen == idx);
s_DemoImplDefinition = entries;
Yönetilen Nesne Sarmalayıcı için vtable'ların ve girişlerin ayrılması, verilerin türün tüm örnekleri için kullanılabilmesi için önceden yapılabilir ve yapılması gerekir. Buradaki iş bir static
oluşturucuda veya modül başlatıcıda gerçekleştirilebilir, ancak yöntemin mümkün olduğunca basit ve hızlı olması için ComputeVtables()
önceden yapılması gerekir.
protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags,
out int count)
{
if (obj is DemoImpl)
{
count = s_DemoImplDefinitionLen;
return s_DemoImplDefinition;
}
// Unknown type
count = 0;
return null;
}
yöntemini uyguladıktan ComputeVtables()
sonra, ComWrappers
alt sınıf örnekleri için Yönetilen Nesne Sarmalayıcıları DemoImpl
üretebilecektir. çağrısından GetOrCreateComInterfaceForObject()
döndürülen Yönetilen Nesne Sarmalayıcı'nın türünde IUnknown*
olduğunu unutmayın. Sarmalayıcıya geçirilen yerel API farklı bir arabirim gerektiriyorsa, bu arabirim için bir Marshal.QueryInterface()
gerçekleştirilmelidir.
var cw = new DemoComWrappers();
var demo = new DemoImpl();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
3. Adım – Uygulama CreateObject()
Yerel Nesne Sarmalayıcı oluşturma işlemi, Yönetilen Nesne Sarmalayıcı oluşturmaktan çok daha fazla uygulama seçeneğine ve çok daha fazla ayrıntıya sahiptir. Ele alınması gereken ilk soru, alt sınıfın ComWrappers
destekleyici COM türlerinde ne kadar izinli olacağıdır. Mümkün olan tüm COM türlerini desteklemek için önemli miktarda kod yazmanız veya bazı akıllı kullanımlarını Reflection.Emit
kullanmanız gerekir. Bu öğreticide, yalnızca hem IDemoStoreType
hem de IDemoGetType
uygulayan COM örneklerini destekleyeceksiniz. Sonlu bir küme olduğunu bildiğiniz ve sağlanan tüm COM örneklerinin her iki arabirimi de uygulaması gerektiğini kısıtladığınız için, tek, statik olarak tanımlanmış bir sarmalayıcı sağlayabilirsiniz; ancak com'da dinamik durumlar her iki seçeneği de keşfedeceğimiz kadar yaygındır.
Statik Yerel Nesne Sarmalayıcı
Önce statik uygulamaya bakalım. Statik Yerel Nesne Sarmalayıcı, .NET arabirimlerini uygulayan bir yönetilen tür tanımlamayı içerir ve yönetilen türdeki çağrıları COM örneğine iletebilir. Statik sarmalayıcının kaba bir ana hattı aşağıdadır.
// See referenced sample for implementation.
class DemoNativeStaticWrapper
: IDemoGetType
, IDemoStoreType
{
public string? GetString() =>
throw new NotImplementedException();
public void StoreString(int len, string? str) =>
throw new NotImplementedException();
}
Bu sınıfın bir örneğini oluşturmak ve sarmalayıcı olarak sağlamak için bazı ilkeler tanımlamanız gerekir. Bu tür sarmalayıcı olarak kullanılıyorsa, her iki arabirimi de uyguladığından, temel alınan COM örneğinin her iki arabirimi de uygulaması gerektiği görünür. Bu ilkeyi benimsediğinize göre, COM örneğindeki çağrılar Marshal.QueryInterface()
aracılığıyla bunu onaylamanız gerekir.
int hr = Marshal.QueryInterface(ptr, ref IDemoGetType.IID_IDemoGetType, out IntPtr IDemoGetTypeInst);
if (hr != 0)
{
return null;
}
hr = Marshal.QueryInterface(ptr, ref IDemoStoreType.IID_IDemoStoreType, out IntPtr IDemoStoreTypeInst);
if (hr != 0)
{
Marshal.Release(IDemoGetTypeInst);
return null;
}
return new DemoNativeStaticWrapper()
{
IDemoGetTypeInst = IDemoGetTypeInst,
IDemoStoreTypeInst = IDemoStoreTypeInst
};
Dinamik Yerel Nesne Sarmalayıcı
Dinamik sarmalayıcılar, türlerin statik olarak değil çalışma zamanında sorgulanması için bir yol sağladığından daha esnektir. Bu desteği sağlamak için kullanacaksınız IDynamicInterfaceCastable
; daha fazla ayrıntıya buradan ulaşabilirsiniz. Yalnızca bu DemoNativeDynamicWrapper
arabirimi uyguladığını gözlemleyin. Arabirimin sağladığı işlevsellik, çalışma zamanında hangi türün desteklendiğini belirleme şansıdır. Bu öğreticinin kaynağı oluşturma sırasında statik bir denetim yapar, ancak bu yalnızca kod paylaşımına yöneliktir çünkü denetim çağrısı yapılana DemoNativeDynamicWrapper.IsInterfaceImplemented()
kadar ertelenebilir.
// See referenced sample for implementation.
internal class DemoNativeDynamicWrapper
: IDynamicInterfaceCastable
{
public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType) =>
throw new NotImplementedException();
public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) =>
throw new NotImplementedException();
}
Şimdi dinamik olarak desteklenecek arabirimlerden DemoNativeDynamicWrapper
birine bakalım. Aşağıdaki kod, varsayılan arabirim yöntemleri özelliğinin kullanılmasını sağlarIDemoStoreType
.
[DynamicInterfaceCastableImplementation]
unsafe interface IDemoStoreTypeNativeWrapper : IDemoStoreType
{
public static void StoreString(IntPtr inst, int len, string? str);
void IDemoStoreType.StoreString(int len, string? str)
{
var inst = ((DemoNativeDynamicWrapper)this).IDemoStoreTypeInst;
StoreString(inst, len, str);
}
}
Bu örnekte dikkat edilmesi gereken iki önemli nokta vardır:
DynamicInterfaceCastableImplementationAttribute
özniteliği. Bu öznitelik, bir yöntemden döndürülen herhangi birIDynamicInterfaceCastable
türde gereklidir. IL kırpmayı kolaylaştırma avantajına sahiptir ve bu da AOT senaryolarının daha güvenilir olduğu anlamına gelir.- için atama
DemoNativeDynamicWrapper
. Bu, öğesinin dinamik doğasınınIDynamicInterfaceCastable
bir parçasıdır. 'denIDynamicInterfaceCastable.GetInterfaceImplementation()
döndürülen tür, uygulayanIDynamicInterfaceCastable
türü "paketle" kullanmak için kullanılır. Buradaki örnek, işaretçininthis
olduğu gibi davranmadığıdır çünkü 'denDemoNativeDynamicWrapper
'e bir servis talebine izin vermemiz gerekirIDemoStoreTypeNativeWrapper
.
Çağrıları COM örneğine iletme
Hangi Yerel Nesne Sarmalayıcının kullanıldığına bakılmaksızın, COM örneğinde işlevleri çağırabilmeniz gerekir. uygulaması IDemoStoreTypeNativeWrapper.StoreString()
, C# işlev işaretçilerini kullanma unmanaged
örneği olarak işlev görebilir.
public static void StoreString(IntPtr inst, int len, string? str)
{
IntPtr strLocal = Marshal.StringToCoTaskMemUni(str);
int hr = ((delegate* unmanaged<IntPtr, int, IntPtr, int>)(*(*(void***)inst + 3 /* IDemoStoreType.StoreString slot */)))(inst, len, strLocal);
if (hr != 0)
{
Marshal.FreeCoTaskMem(strLocal);
Marshal.ThrowExceptionForHR(hr);
}
}
Şimdi com örneğinin başvurusunu kaldırarak vtable uygulamasına erişmesini inceleyelim. COM ABI, bir nesnenin ilk işaretçisinin türün vtable'ı olduğunu ve buradan istenen yuvaya erişilebileceğini tanımlar. COM nesnesinin adresinin olduğunu 0x10000
varsayalım. İşaretçi boyutundaki ilk değer, bu örnekteki 0x20000
vtable'ın adresi olmalıdır. Vtable'a geçtikten sonra uygulamaya erişmek StoreString()
için dördüncü yuvayı (sıfır tabanlı dizin oluşturmada dizin 3) ararsınız.
COM instance
0x10000 0x20000
VTable for IDemoStoreType
0x20000 <Address of QueryInterface>
0x20008 <Address of AddRef>
0x20010 <Address of Release>
0x20018 <Address of StoreString>
İşlev işaretçisinin olması, nesne örneğini ilk parametre olarak geçirerek bu nesnedeki üye işlevine göndermenizi sağlar. Bu desen, Yönetilen Nesne Sarmalayıcı uygulamasının işlev tanımlarına göre tanıdık görünmelidir.
CreateObject()
Yöntem uygulandıktan sonra, ComWrappers
alt sınıf hem IDemoStoreType
hem de IDemoGetType
uygulayan COM örnekleri için Yerel Nesne Sarmalayıcıları üretebilecektir.
IntPtr iunk = ...; // Get a COM instance from native code.
object rcw = cw.GetOrCreateObjectForComInstance(iunk, CreateObjectFlags.UniqueInstance);
4. Adım – Yerel Nesne Sarmalayıcı yaşam süresi ayrıntılarını işleme
ComputeVtables()
ve CreateObject()
uygulamaları bazı sarmalayıcı ömrü ayrıntılarını ele alır, ancak dikkat edilmesi gereken başka noktalar da vardır. Bu kısa bir adım olsa da, tasarımın karmaşıklığını ComWrappers
önemli ölçüde artırabilir.
ve Release()
yöntemlerine AddRef()
yapılan çağrılar tarafından denetlenen Yönetilen Nesne Sarmalayıcı'nın aksine, Yerel Nesne Sarmalayıcının ömrü GC tarafından belirsiz bir şekilde işlenmez. Buradaki soru, COM örneğini temsil eden Yerel Nesne Sarmalayıcısı'nı IntPtr
ne zaman çağırdığıdırRelease()
? İki genel demet vardır:
Yerel Nesne Sarmalayıcı'nın Sonlandırıcısı, COM örneğinin
Release()
yöntemini çağırmaktan sorumludur. Bu yöntemi çağırmanın güvenli olduğu tek zaman budur. Bu noktada, .NET çalışma zamanında Yerel Nesne Sarmalayıcı'ya başka başvuru olmadığı GC tarafından doğru şekilde belirlendi. COM Dairelerini düzgün bir şekilde destekliyorsanız burada karmaşıklık olabilir; daha fazla bilgi için Dikkat edilmesi gereken ek noktalar bölümüne bakın.Yerel Nesne Sarmalayıcı içinde
Dispose()
uygularIDisposable
ve çağırırRelease()
.
Not
Desen IDisposable
yalnızca çağrı sırasında bayrağı geçirildiyse CreateObject()
CreateObjectFlags.UniqueInstance
desteklenmelidir. Bu gereksinime uyulmazsa, atılan Yerel Nesne Sarmalayıcıları atıldıktan sonra yeniden kullanılabilir.
ComWrappers
Alt sınıfı kullanma
Artık test edilebilen bir ComWrappers
alt sınıfınız var. ve IDemoStoreType
uygulayan IDemoGetType
bir COM örneği döndüren bir yerel kitaplık oluşturmaktan kaçınmak için Yönetilen Nesne Sarmalayıcı'yı kullanır ve bunu bir COM örneği olarak ele alırsınız; yine de COM'yi geçirmek için bu mümkün olmalıdır.
Önce bir Yönetilen Nesne Sarmalayıcı oluşturalım. Örneğin DemoImpl
örneğini oluşturun ve geçerli dize durumunu görüntüleyin.
var demo = new DemoImpl();
string? value = demo.GetString();
Console.WriteLine($"Initial string: {value ?? "<null>"}");
Şimdi, bir COM ortamına DemoComWrappers
geçirebileceğiniz bir ve Yönetilen Nesne Sarmalayıcısı örneği oluşturabilirsiniz.
var cw = new DemoComWrappers();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
Yönetilen Nesne Sarmalayıcı'yı bir COM ortamına geçirmek yerine, bu COM örneğini yeni almış gibi yapın, böylece bunun yerine yerel nesne sarmalayıcı oluşturursunuz.
var rcw = cw.GetOrCreateObjectForComInstance(ccw, CreateObjectFlags.UniqueInstance);
Yerel Nesne Sarmalayıcı ile bunu istenen arabirimlerden birine dönüştürebilmeniz ve normal yönetilen nesne olarak kullanabilmeniz gerekir. Örneği inceleyebilir DemoImpl
ve yönetilen örneği sarmalayan yönetilen Nesne Sarmalayıcısını sarmalayan Yerel Nesne Sarmalayıcı üzerindeki işlemlerin etkisini gözlemleyebilirsiniz.
var getter = (IDemoGetType)rcw;
var store = (IDemoStoreType)rcw;
string msg = "hello world!";
store.StoreString(msg.Length, msg);
Console.WriteLine($"Setting string through wrapper: {msg}");
value = demo.GetString();
Console.WriteLine($"Get string through managed object: {value}");
msg = msg.ToUpper();
demo.StoreString(msg.Length, msg.ToUpper());
Console.WriteLine($"Setting string through managed object: {msg}");
value = getter.GetString();
Console.WriteLine($"Get string through wrapper: {value}");
Alt sınıfınız ComWrapper
destekleyecek CreateObjectFlags.UniqueInstance
şekilde tasarlandığından, GC'nin gerçekleşmesini beklemek yerine Yerel Nesne Sarmalayıcı'yı hemen temizleyebilirsiniz.
(rcw as IDisposable)?.Dispose();
com etkinleştirme ile ComWrappers
COM nesnelerinin oluşturulması genellikle bu belgenin kapsamı dışında karmaşık bir senaryo olan COM Etkinleştirmesi aracılığıyla gerçekleştirilir. İzlenecek kavramsal bir desen sağlamak için, COM Etkinleştirmesi için kullanılan API'yi tanıtır CoCreateInstance()
ve ile ComWrappers
nasıl kullanılabileceğini gösteririz.
Uygulamanızda aşağıdaki C# kodunun olduğunu varsayalım. Aşağıdaki örnek, COM örneğini uygun arabirime göre sıralamak için bir COM sınıfını ve yerleşik COM birlikte çalışma sistemini etkinleştirmek için kullanır CoCreateInstance()
. kullanımı typeof(I).GUID
bir onay ile sınırlıdır ve kodun AOT kullanımına uygun olması durumunda etkilenebilen bir yansıma kullanımı örneği olduğunu unutmayın.
public static I ActivateClass<I>(Guid clsid, Guid iid)
{
Debug.Assert(iid == typeof(I).GUID);
int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out object obj);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
return (I)obj;
}
[DllImport("Ole32")]
private static extern int CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
int dwClsContext,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppObj);
Yukarıdakini kullanıma ComWrappers
dönüştürmek için P/Invoke'dan CoCreateInstance()
öğesinin kaldırılması MarshalAs(UnmanagedType.Interface)
ve el ile sıralamanın gerçekleştirilmesi gerekir.
static ComWrappers s_ComWrappers = ...;
public static I ActivateClass<I>(Guid clsid, Guid iid)
{
Debug.Assert(iid == typeof(I).GUID);
int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out IntPtr obj);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
return (I)s_ComWrappers.GetOrCreateObjectForComInstance(obj, CreateObjectFlags.None);
}
[DllImport("Ole32")]
private static extern int CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
int dwClsContext,
ref Guid riid,
out IntPtr ppObj);
Yerel Nesne Sarmalayıcı için sınıf oluşturucusunda etkinleştirme mantığını ekleyerek fabrika ActivateClass<I>
stilindeki işlevleri soyutlamanız da mümkündür. Oluşturucu, yeni yapılan yönetilen nesneyi etkinleştirilmiş COM örneğiyle ilişkilendirmek için API'yi kullanabilir ComWrappers.GetOrRegisterObjectForComInstance()
.
Dikkat edilecek diğer noktalar
Yerel AOT – JIT derlemesi önlendiğinden, önceden (AOT) derlemesi gelişmiş başlangıç maliyeti sağlar. Bazı platformlarda JIT derleme gereksinimini ortadan kaldırmak da genellikle gereklidir. AOT'nin desteklenmesi API'nin ComWrappers
bir hedefiydi, ancak herhangi bir sarmalayıcı uygulaması, yansıma kullanma gibi AOT'nin kesildiği durumları yanlışlıkla tanıtmamaya dikkat etmelidir. Type.GUID
özelliği, yansımanın kullanıldığı ancak belirgin olmayan bir şekilde kullanıldığı bir örnektir. özelliği, Type.GUID
türün özniteliklerini incelemek için yansıma kullanır ve ardından değerini oluşturmak için türün adını ve derlemeyi içerme olasılığını kullanır.
Kaynak oluşturma : COM birlikte çalışma ve uygulama ComWrappers
için gereken kodun çoğu büyük olasılıkla bazı araçlar tarafından otomatik olarak oluşturulabilir. Tür Kitaplığı (TLB), IDL veya Birincil Birlikte Çalışma Derlemesi (PIA) gibi uygun COM tanımları göz önünde bulundurularak her iki sarmalayıcı türü için de kaynak oluşturulabilir.
Genel kayıt – ComWrappers
API, COM birlikte çalışmasının yeni bir aşaması olarak tasarlandığından, mevcut sistemle kısmen tümleştirmenin bir yolunun olması gerekiyordu. API'de çeşitli destek için genel bir örneğin kaydedilmesine ComWrappers
izin veren genel olarak etkileyen statik yöntemler vardır. Bu yöntemler, yerleşik COM birlikte çalışma sistemine benzer şekilde her durumda kapsamlı COM birlikte çalışma desteği sağlamayı bekleyen örnekler için ComWrappers
tasarlanmıştır.
Başvuru İzleyicisi desteği – Bu destek WinRT senaryoları için birincil olarak kullanılır ve gelişmiş bir senaryodur. Çoğu ComWrapper
uygulama için CreateComInterfaceFlags.TrackerSupport
veya CreateObjectFlags.TrackerObject
bayrağı bir NotSupportedExceptionoluşturmalıdır. Bu desteği bir Windows platformunda veya hatta Windows dışı bir platformda etkinleştirmek istiyorsanız, C#/WinRT araç zincirine başvurmanız kesinlikle önerilir.
Daha önce ele alınan yaşam süresi, tür sistemi ve işlevsel özelliklerin yanı sıra, COM ile uyumlu bir uygulama ComWrappers
için dikkat edilmesi gereken ek noktalar vardır. Windows platformunda kullanılacak herhangi bir uygulama için aşağıdaki noktalar vardır:
Daireler – COM'un iş parçacığı oluşturma organizasyon yapısı "Apartmanlar" olarak adlandırılır ve kararlı operasyonlar için uyulması gereken katı kurallara sahiptir. Bu öğreticide, daireye duyarlı Yerel Nesne Sarmalayıcıları uygulanmaz, ancak üretime hazır tüm uygulamalar daireye duyarlı olmalıdır. Bunu başarmak için Windows 8'de kullanıma sunulan API'yi kullanmanızı
RoGetAgileReference
öneririz. Windows 8 öncesi sürümler için Genel Arabirim Tablosu'nu göz önünde bulundurun.Güvenlik – COM, sınıf etkinleştirme ve yakın izin için zengin bir güvenlik modeli sağlar.
Geri Bildirim
https://aka.ms/ContentUserFeedback.
Çok yakında: 2024 boyunca, içerik için geri bildirim mekanizması olarak GitHub Sorunları’nı kullanımdan kaldıracak ve yeni bir geri bildirim sistemiyle değiştireceğiz. Daha fazla bilgi için bkz.Gönderin ve geri bildirimi görüntüleyin