Pembuatan sumber untuk ComWrappers

.NET 8 memperkenalkan generator sumber yang membuat implementasi ComWrappers API untuk Anda. Mesin mengenali GeneratedComInterfaceAttribute.

Sistem interop COM bawaan runtime .NET (bukan yang dihasilkan oleh sumber), khusus Windows, menghasilkan stub IL—aliran instruksi IL yang di-JIT—pada runtime untuk memfasilitasi transisi dari kode terkelola ke COM, dan sebaliknya. Karena stub IL ini dihasilkan pada runtime, itu tidak kompatibel dengan NativeAOT dan pemangkasan IL. Pembuatan stub pada runtime juga dapat membuat mendiagnosis masalah marshalling menjadi sulit.

Interop bawaan menggunakan atribut seperti ComImport atau DllImport, yang mengandalkan pembuatan kode saat runtime. 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 membuat ComWrappers semudah COM yang terintegrasi, tetapi menyajikannya 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 baik internal maupun public.

[GeneratedComClass]
internal partial class Foo : IFoo
{
    public void Method(int i)
    {
        // Do things
    }
}

Pada waktu kompilasi, pembangkit menciptakan implementasi API COMWrappers, dan Anda dapat menggunakan tipe StrategyBasedComWrappers atau tipe turunan yang disesuaikan 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 mempertimbangkan atribut MarshalUsingAttribute dan beberapa penggunaan atribut MarshalAsAttribute 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 tipe pengembalian dari tipe 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 tipe pengembalian HRESULT, tipe bilangan bulat 4 byte yang mewakili status kesalahan dan 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 seperti apa tanda tangan asli yang diharapkan oleh 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 InterfaceTypeAttribute diasumsikan berasal dari IUnknown. Ini berbeda dari COM bawaan di mana defaultnya adalah InterfaceIsDual.

Pengaturan standar 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 data, termasuk array elemen yang langsung sesuai memori, memiliki semantik [In].

  • [In] dan [Out] atribut hanya diperbolehkan pada array. Jika perilaku [Out] atau [In, Out] diperlukan pada jenis lain, gunakan pengubah parameter in dan pengubah out.

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 atribut GeneratedComInterfaceAttribute yang diturunkan dari antarmuka yang diberi atribut GeneratedComInterface 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 menutupi anggota antarmuka dasarnya, jika memilikinya.

Selain itu, setiap perubahan pada offset metode virtual yang dihasilkan dalam rantai antarmuka dasar, yang ditentukan dalam assembly lain, tidak akan diperhitungkan dalam antarmuka turunan hingga 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 keterbatasan dan melebihkan fitur di antar batas perakitan.

Marshal API

Beberapa API di Marshal tidak kompatibel dengan COM yang dihasilkan sumber. Ganti metode ini dengan metode yang sesuai pada ComWrappers implementasi.

Lihat juga