Pembuatan sumber untuk ComWrappers
.NET 8 memperkenalkan generator sumber yang membuat implementasi ComWrappers API untuk Anda. Generator mengenali GeneratedComInterfaceAttribute.
Sistem interop RUNTIME .NET bawaan (tidak dihasilkan sumber), khusus Windows, COM menghasilkan stub IL—aliran instruksi IL yang di-JIT-ed—pada waktu proses untuk memfasilitasi transisi dari kode terkelola ke COM, dan sebaliknya. Karena stub IL ini dihasilkan pada waktu proses, itu tidak kompatibel dengan pemangkasan NativeAOT dan IL. Pembuatan stub pada run time juga dapat membuat mendiagnosis masalah marshalling menjadi sulit.
Interop bawaan menggunakan atribut seperti ComImport
atau DllImport
, yang mengandalkan pembuatan kode pada waktu proses. Kode berikut menunjukkan contoh ini:
[ComImport]
interface IFoo
{
void Method(int i);
}
[DllImport("MyComObjectProvider")]
static nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();
[DllImport("MyComObjectProvider")]
static void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);
// Use the system to create a Runtime Callable Wrapper to use in managed code
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)Marshal.GetObjectForIUnknown(ptr);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
IFoo foo = GetManagedIFoo();
nint ptr = Marshal.GetIUnknownForObject(foo);
GivePointerToComInterface(ptr);
ComWrappers
API memungkinkan berinteraksi dengan COM di C# tanpa menggunakan sistem COM bawaan, tetapi memerlukan boilerplate yang substansial dan kode tidak aman yang ditulis tangan. Generator antarmuka COM mengotomatiskan proses ini dan memudahkan ComWrappers
COM bawaan, tetapi memberikannya dengan cara yang dapat dipangkas dan ramah AOT.
Penggunaan dasar
Untuk menggunakan generator antarmuka COM, tambahkan GeneratedComInterfaceAttribute atribut dan GuidAttribute pada definisi antarmuka yang ingin Anda impor atau ekspos ke COM. Jenis harus ditandai partial
dan memiliki internal
atau public
visibilitas agar kode yang dihasilkan dapat mengaksesnya.
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
void Method(int i);
}
Kemudian, untuk mengekspos kelas yang mengimplementasikan antarmuka ke COM, tambahkan GeneratedComClassAttribute ke kelas penerapan. Kelas ini juga harus partial
dan atau internal
public
.
[GeneratedComClass]
internal partial class Foo : IFoo
{
public void Method(int i)
{
// Do things
}
}
Pada waktu kompilasi, generator membuat implementasi COMWrappers API, dan Anda dapat menggunakan StrategyBasedComWrappers jenis atau jenis turunan kustom untuk mengonsumsi atau mengekspos antarmuka COM.
[LibraryImport("MyComObjectProvider")]
private static partial nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();
[LibraryImport("MyComObjectProvider")]
private static partial void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);
// Use the ComWrappers API to create a Runtime Callable Wrapper to use in managed code
ComWrappers cw = new StrategyBasedComWrappers();
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)cw.GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.None);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
ComWrappers cw = new StrategyBasedComWrappers();
Foo foo = new();
nint ptr = cw.GetOrCreateComInterfaceForObject(foo, CreateComInterfaceFlags.None);
GivePointerToComInterface(ptr);
Menyesuaikan marshalling
Generator antarmuka COM menghormati MarshalUsingAttribute atribut dan beberapa penggunaan MarshalAsAttribute atribut untuk menyesuaikan marshalling parameter. Untuk informasi selengkapnya, lihat cara menyesuaikan marshalling yang dihasilkan sumber dengan MarshalUsing
atribut dan menyesuaikan parameter marshalling dengan MarshalAs
atribut . Properti GeneratedComInterfaceAttribute.StringMarshalling dan GeneratedComInterfaceAttribute.StringMarshallingCustomType berlaku untuk semua parameter dan mengembalikan jenis jenis string
di antarmuka jika mereka tidak memiliki atribut marshalling lainnya.
HRESULT implisit dan PreserveSig
Metode COM dalam C# memiliki tanda tangan yang berbeda dari metode asli. COM Standar memiliki jenis pengembalian , jenis bilangan HRESULT
bulat 4 byte yang mewakili kesalahan dan status keberhasilan. Nilai pengembalian ini HRESULT
disembunyikan secara default dalam tanda tangan C# dan dikonversi ke pengecualian saat nilai kesalahan dikembalikan. Parameter "keluar" terakhir dari tanda tangan COM asli secara opsional dapat dikonversi menjadi pengembalian dalam tanda tangan C#.
Misalnya, cuplikan berikut menunjukkan tanda tangan metode C# dan tanda tangan asli yang sesuai yang disimpulkan generator.
void Method1(int i);
int Method2(float i);
HRESULT Method1(int i);
HRESULT Method2(float i, _Out_ int* returnValue);
Jika Anda ingin menangani HRESULT
sendiri, Anda dapat menggunakan PreserveSigAttribute pada metode untuk menunjukkan generator tidak boleh melakukan transformasi ini. Cuplikan berikut menunjukkan tanda tangan asli apa yang diharapkan generator ketika [PreserveSig]
diterapkan. Metode COM harus mengembalikan HRESULT
, sehingga nilai pengembalian metode apa pun dengan PreserveSig
harus int
.
[PreserveSig]
int Method1(int i, out int j);
[PreserveSig]
int Method2(float i);
HRESULT Method1(int i, int* j);
HRESULT Method2(float i);
Untuk informasi selengkapnya, lihat Terjemahan tanda tangan metode implisit di interop .NET
Ketidaksesuaian dan perbedaan dengan COM bawaan
IUnknown
hanya
Satu-satunya basis antarmuka yang didukung adalah IUnknown
. Antarmuka dengan InterfaceTypeAttribute yang memiliki nilai selain InterfaceIsIUnknown tidak didukung dalam COM yang dihasilkan sumber. Antarmuka apa pun tanpa diasumsikan InterfaceTypeAttribute
berasal dari IUnknown
. Ini berbeda dari COM bawaan di mana defaultnya adalah InterfaceIsDual.
Marsekal default dan dukungan
COM yang dihasilkan sumber memiliki beberapa perilaku marshalling default yang berbeda dari COM bawaan.
Dalam sistem COM bawaan, semua jenis memiliki atribut implisit
[In]
kecuali untuk array elemen blittable, yang memiliki atribut implisit[In, Out]
. Dalam COM yang dihasilkan sumber, semua jenis, termasuk array elemen yang dapat di-blittable, memiliki[In]
semantik.[In]
dan[Out]
atribut hanya diperbolehkan pada array. Jika[Out]
perilaku atau[In, Out]
diperlukan pada jenis lain, gunakan pengubahin
parameter danout
.
Antarmuka turunan
Dalam sistem COM bawaan, jika Anda memiliki antarmuka yang berasal dari antarmuka COM lainnya, Anda harus mendeklarasikan metode bayangan untuk setiap metode dasar pada antarmuka dasar dengan new
kata kunci. Untuk informasi selengkapnya, lihat pewarisan antarmuka COM dan .NET.
[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
void Method1(int i);
void Method2(float i);
}
[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
new void Method1(int i);
new void Method2(float f);
void Method3(long l);
void Method4(double d);
}
Generator antarmuka COM tidak mengharapkan bayangan metode dasar. Untuk membuat metode yang mewarisi dari yang lain, cukup tunjukkan antarmuka dasar sebagai antarmuka dasar C# dan tambahkan metode antarmuka turunan. Untuk informasi selengkapnya, lihat dokumen desain.
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
void Method1(int i);
void Method2(float i);
}
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
void Method3(long l);
void Method4(double d);
}
Perhatikan bahwa antarmuka dengan GeneratedComInterface
atribut hanya dapat mewarisi dari satu antarmuka dasar yang memiliki GeneratedComInterface
atribut .
Antarmuka turunan di seluruh batas perakitan
Di .NET 8, tidak didukung untuk menentukan antarmuka dengan GeneratedComInterfaceAttribute atribut yang berasal dari GeneratedComInterface
antarmuka -atribut yang didefinisikan dalam rakitan lain.
Di .NET 9 dan versi yang lebih baru, skenario ini didukung dengan batasan berikut:
- Jenis antarmuka dasar harus dikompilasi yang menargetkan kerangka kerja target yang sama dengan jenis turunan.
- Jenis antarmuka dasar tidak boleh membayangi anggota antarmuka dasarnya, jika memilikinya.
Selain itu, setiap perubahan pada offset metode virtual yang dihasilkan dalam rantai antarmuka dasar yang ditentukan dalam rakitan lain tidak akan diperhitungkan dalam antarmuka turunan sampai proyek dibangun kembali.
Catatan
Di .NET 9 dan versi yang lebih baru, peringatan dipancarkan saat mewarisi antarmuka COM yang dihasilkan di seluruh batas perakitan untuk memberi tahu Anda tentang batasan dan jebakan penggunaan fitur ini. Anda dapat menonaktifkan peringatan ini untuk mengakui batasan dan mewarisi di seluruh batas perakitan.
API Marsekal
Beberapa API di Marshal tidak kompatibel dengan COM yang dihasilkan sumber. Ganti metode ini dengan metode yang sesuai pada ComWrappers
implementasi.