Bagikan melalui


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 HRESULTbulat 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 pengubah in parameter dan 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 GeneratedComInterfaceAttribute atribut yang berasal dari GeneratedComInterfaceantarmuka -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.

Lihat juga