Layanan Pemanggilan Platform (Platform Invocation Services), yang lebih dikenal sebagai P/Invoke, adalah fitur dari implementasi Common Language Infrastructure, seperti Common Language Runtime dari Microsoft, yang memungkinkan kode terkelola untuk memanggil kode asli.
Kode terkelola, seperti C# atau VB.NET, menyediakan akses asli terhadap kelas, metode, dan jenis yang didefinisikan di dalam pustaka yang menyusun .NET Framework. Walaupun .NET Framework menyediakan rangkaian kegunaan yang luas, mungkin ada kekurangan akses ke banyak pustaka sistem operasi tingkat rendah yang biasanya ditulis dalam kode tidak terkelola atau pustaka pihak ketiga yang juga ditulis dalam kode tidak terkelola. P/Invoke adalah teknik yang dapat digunakan oleh programmer untuk mengakses fungsi-fungsi di pustaka tersebut. Panggilan ke fungsi-fungsi di dalam pustaka tersebut terjadi dengan cara mendeklarasikan tanda tangan fungsi tidak terkelola tersebut di dalam kode terkelola, yang menjadi fungsi sesungguhnya yang dapat dipanggil seperti halnya metode terkelola yang lain. Deklarasi tersebut merujuk ke jalur file dan mendefinisikan parameter dan pengembalian fungsi dalam jenis terkelola yang kemungkinan besar disusun secara implisit ke dan dari jenis tidak terkelola oleh runtime bahasa umum (CLR). Ketika jenis data tidak terkelola menjadi terlalu rumit untuk konversi sederhana yang implisit dari dan ke jenis terkelola, kerangka tersebut memungkinkan pengguna untuk mendefinisikan atribut di fungsi, pengembalian, dan/atau parameter untuk dengan eksplisit memperbaiki cara data disusun sehingga tidak mengarah ke kondisi abnormal yang dapat terjadi ketika ingin melakukannya secara implisit.
Ada banyak abstraksi dari konsep pemrograman tingkat rendah yang tersedia bagi programmer kode terkelola, dibandingkan dengan pemrograman dalam bahasa tidak terkelola. Sebagai hasilnya, programmer yang hanya berpengalaman dengan kode terkelola akan harus memoles konsep pemrograman seperti pointer, struktur, dan passing by reference untuk melampaui rintangan dalam menggunakan P/Invoke.
Arsitektur
Ringkasan
Dua varian dari P/Invoke yang sekarang digunakan adalah:
Eksplisit
- Kode asli diimpor melalui pustaka tautan dinamis (DLLs)
- Metadata yang dimasukkan ke rakitan pemanggil mendefinisikan bagaimana kode asli dapat dipanggil dan data diakses (biasanya memerlukan specifier sumber beratribut untuk membantu kompilator dalam menghasilkan kaitan susunan)
- Definisi ini adalah bagian dari "Eksplisit"
Implisit
- Dengan menggunakan C++/CLI, suatu aplikasi dapat secara bersamaan menggunakan tumpukan terkelola (dengan cara melacak pointer) dan bagian memori asli yang ada, tanpa deklarasi eksplisit.(Implisit)
- Keuntungan utama dalam hal ini adalah, jika struktur data asli yang mendasar berubah, selama penamaannya kompatibel, perubahan yang mengakibatkan kerusakan dapat dicegah.
- Maka, menambah/menghapus/menyusun ulang struktur di header asli akan diperbolehkan secara transparan selama nama anggota struktur tidak ikut berubah.
Detail
Ketika menggunakan P/Invoke, CLR menangani pemuatan dan konversi DLL akan jenis tidak terkelola sebelumnya ke jenis CTS (juga disebut sebagai penyusunan parameter). [butuh rujukan]Untuk melakukan ini, CLR:
- Mendapatkan lokasi DLL yang memuat fungsi.
- Memuat DLL ke dalam memori.
- Mendapatkan lokasi alamat dari fungsi di memori dan mendorong argumen ke dalam stack tersebut, dan menyusun data sesuai keperluan.
P/Invoke berguna dalam menggunakan C standar (tidak terkelola) atau DLL C++. Dapat digunakan ketika pemrogram memerlukan akses ke API Windows yang ekstensif, karena kurangnya pembungkus yang tersedia bagi banyak fungsi yang disediakan oleh pustaka Windows. Jika suatu API Win32 tidak dipaparkan oleh .NET Framework, pembungkus untuk API tersebut harus ditulis secara manual.
Jebakan
Menulis pembungkus P/Invoke bisa menjadi sulit dan mudah salah. Menggunakan DLL asli berarti bahwa pemrogram tidak lagi mendapat manfaat dari keamanan jenis dan pengumpulan sampah yang biasanya disediakan di lingkungan .NET. Ketika digunakan secara tidak benar, dapat mengakibatkan masalah seperti kesalahan segmentasi atau kebocoran memori. Mendapatkan tanda tangan fungsi warisan yang tepat untuk digunakan di lingkungan .NET dapat menjadi rumit, yang kemudian dapat menyebabkan masalah di atas. Untuk tujuan inilah alat-alat dan situs web tersedia untuk memperoleh tanda tangan semacam itu, untuk membantu menghindari masalah tanda tangan. [1]
Jebakan lainnya termasuk:
- Perataan data yang salah akan jenis yang ditentukan pengguna di bahasa terkelola: ada banyak cara bagaimana data dapat diratakan tergantung dari kompilator atau arahan kompilator dalam bahasa C dan harus diperhatikan saat memberitahu CLR secara eksplisit bagaimana cara meratakan data untuk jenis non-blittable types. Contoh umum dari hal ini adalah saat mencoba mendefinisikan jenis data di .NET untuk merepresentasikan gabungan dalam C. Dua variabel yang berbeda saling tumpang tindih di memori, dan mendefinisikan dua variabel ini di dalam satu jenis di .NET akan mengakibatkan mereka berada di lokasi yang berbeda di memori, maka atribut khusus harus digunakan untuk memperbaiki masalah ini.
- Gangguan atas lokasi data oleh pengumpul sampah dari bahasa terkelola: jika ada satu referensi yang lokal terhadap metode di .NET dan diteruskan ke fungsi asli, ketika metode terkelola tersebut kembali, pengumpul sampah mungkin mengeklaim kembali referensi tersebut. Harus diperhatikan bahwa referensi objek disematkan disematkan, menghalanginya untuk dapat diambil atau dipindahkan oleh pengumpul sampah, yang dapat menyebabkan akses yang tidak valid oleh modul asli.
Ketika menggunakan C++/CLI, CIL yang dikeluarkan bebas berinteraksi dengan objek yang ditemukan di tumpukan terkelola dan lokasi memori asli mana pun yang dapat ditemukan, secara bersamaan. Objek yang bertempat di tumpukan terkelola dapat dipanggil, diubah atau di-konstruksi, dengan menggunakan notasi sederhana "object->field" untuk menetapkan nilai atau menentukan panggilan metode. Keuntungan performa yang signifikan dihasilkan dari eliminasi perubahan konteks yang tidak diperlukan, persyaratan memori pun berkurang (tumpukan menjadi lebih pendek).
Ini hadir dengan tantangan-tantangan baru:
- Kode rentan terhadap Double Thunking[1] apabila tidak ditangani secara khusus.
- Masalah Loader Lock [2]
Referensi-referensi ini menentukan solusi untuk setiap masalah tersebut saat ditemukan. Keuntungan utama adalah eliminasi dari deklarasi struktur, urutan dari deklarasi bidang dan masalah perataan tidak ada di dalam konteks Interop C++.
Contoh
Contoh dasar
Contoh sederhana pertama ini menunjukkan bagaimana cara mendapatkan versi dari DLL tertentu:
Tanda tangan fungsi DllGetVersion di API Windows:
HRESULT DllGetVersion
(
DLLVERSIONINFO* pdvi
)
Kode C# P/Invoke code untuk memanggil fungsi DllGetVersion:
[StructLayout(LayoutKind.Sequential)]
private struct DLLVERSIONINFO {
public int cbSize;
public int dwMajorVersion;
public int dwMinorVersion;
public int dwBuildNumber;
public int dwPlatformID;
}
[DllImport("shell32.dll")]
static extern int DllGetVersion(ref DLLVERSIONINFO pdvi);
Contoh kedua ini menunjukkan bagaimana cara mengekstrak ikon di file:
Tanda tangan fungsiExtractIcon di API Windows:
HICON ExtractIcon
(
HINSTANCE hInst,
LPCTSTR lpszExeFileName,
UINT nIconIndex
);
Kode C# P/Invoke untuk memanggil fungsi ExtractIcon:
[DllImport("shell32.dll")]
static extern IntPtr ExtractIcon(
IntPtr hInst,
[MarshalAs(UnmanagedType.LPStr)] string lpszExeFileName,
uint nIconIndex);
Contoh rumit berikut ini menunjukkan bagaimana cara membagi peristiwa di antara dua proses di platform Windows:
Tanda tangan fungsi CreateEvent :
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
Kode C# P/Invoke untuk memanggil fungsi CreateEvent:
[DllImport("kernel32.dll", SetLastError=true)]
static extern IntPtr CreateEvent(
IntPtr lpEventAttributes,
bool bManualReset,
bool bInitialState,
[MarshalAs(UnmanagedType.LPStr)] string lpName);
Contoh yang lebih rumit
// native declaration
typedef struct _PAIR
{
DWORD Val1;
DWORD Val2;
} PAIR, *PPAIR;
// Compiled with /clr; use of #pragma managed/unmanaged can lead to double thunking;
// avoid by using a stand-alone .cpp with .h includes.
// This would be located in a .h file.
template<>
inline CLR_PAIR^ marshal_as<CLR_PAIR^, PAIR> (const PAIR&Src) { // Note use of de/referencing. It must match your use.
CLR_PAIR^ Dest = gcnew CLR_PAIR;
Dest->Val1 = Src.Val1;
Dest->Val2 = Src.Val2;
return Dest;
};
CLR_PAIR^ mgd_pair1;
CLR_PAIR^ mgd_pair2;
PAIR native0,*native1=&native0;
native0 = NativeCallGetRefToMemory();
// Using marshal_as. It makes sense for large or frequently used types.
mgd_pair1 = marshal_as<CLR_PAIR^>(*native1);
// Direct field use
mgd_pair2->Val1 = native0.Val1;
mgd_pair2->val2 = native0.val2;
return(mgd_pair1); // Return to C#
Alat
Ada beberapa alat yang dirancang untuk membantu produksi tanda tangan P/Invoke.
Menulis aplikasi utilitas yang akan mengimpor file header C++ dan file DLL asli dan menghasilkan rakitan antarmuka secara otomatis ternyata agak sulit. Masalah utama ketika menghasilkan pengimpor/pengekspor untuk tanda tangan P/Invoke tersebut adalah ketidakjelasan beberapa jenis parameter panggilan fungsi C++.
Brad Adams menyebut ini: Masalah P/Invoke.
Masalahnya berada di fungsi C++ seperti berikut ini:
__declspec(dllexport) void MyFunction(char *params);
Jenis apa yang harus kita gunakan untuk parameter params di tanda tangan P/Invoke kita? Ini bisa jadi string C++ yang dihentikan null, atau array char atau mungkin merupakan parameter output char. Jadi haruskah kita menggunakan string, StringBuilder, char [] atau ref char?
Terlepas dari masalah ini, ada beberapa alat yang tersedia untuk membuat produksi tanda tangan P/Invoke menjadi lebih sederhana.
Salah satu alat yang tercantum di bawah ini, xInterop C++ .NET Bridge Diarsipkan 2018-11-11 di Wayback Machine. telah memecahkan masalah ini dengan menerapkan berbagai pengganti dari metode C++ yang sama di dunia .NET, pengembang dapat kemudian memilih pengganti yang tepat untuk melakukan panggilan.
PInvoke.net
PInvoke.net adalah wiki yang memuat tanda tangan P/Invoke untuk sejumlah besar API Windows standar. Wiki ini dimiliki oleh Redgate Software dan memiliki sekitar 50000 hits per bulan.
Tanda tangan tersebut dibuat secara manual oleh para pengguna wiki. Tanda tangan tersebut dapat dicari menggunakan add-in gratis di Microsoft Visual Studio.
PInvoker
PInvoker adalah aplikasi yang mengimpor DLL asli dan file .h C++ dan mengekspor DLL interop P/Invoke yang terbentuk sepenuhnya dan telah dikompilasi. Ia mengatasi masalah ketidakjelasan dengan cara membungkus pointer asli parameter fungsi di kelas antarmuka .NET tertentu di PInvoker. Alih-alih menggunakan jenis parameter .NET standar di definisi metode P/Invoke (char[], string, dsb.) aplikasi ini menggunakan kelas antarmuka tersebut di panggilan fungsi P/Invoke.
Misalnya, jika kita mempertimbangkan contoh kode di atas, PInvoker akan menghasilkan fungsi P/Invoke .NET yang menerima kelas antarmuka .NET yang membungkus pointer char * asli. Konstruksi kelas ini bisa dari string atau dari array char []. Struktur memori asli yang sebenarnya untuk keduanya sama, namun konstruktor kelas antarmuka yang bersangkutan untuk setiap jenisnya akan memenuhi memori dengan cara yang berbeda. Tanggung jawab untuk memutuskan jenis .NET apa yang perlu diteruskan ke dalam fungsi menjadi tanggungan pengembang.
Asisten Interop Microsoft
Asisten Interop Microsoft adalah alat gratis yang tersedia dengan biner dan kode sumber yang dapat diunduh dari CodePlex. Alat ini di-lisensi di bawah Microsoft Limited Public License (Ms-LPL).
Alat ini memiliki dua bagian:
- Pengonversi yang menerima sebagian kecil kode file header C++ asli yang mengandung struct dan definisi metode. Alat ini kemudian menghasilkan kode P/Invoke C# untuk Anda salin dan tempel ke dalam aplikasi Anda.
- Database yang dapat dicari, berisi konstan, metode dan definisi struct API Windows yang di konversi.
Karena alat ini menghasilkan kode sumber C# dan bukan dll yang dikompilasi, pengguna bebas untuk melakukan perubahan yang diperlukan terhadap kode sebelum digunakan. Maka masalah ketidakjelasan dipecahkan dengan cara aplikasi memilih satu jenis .NET khusus untuk digunakan di tanda tangan metode P/Invoke dan bila perlu pengguna dapat mengubahnya ke jenis yang dibutuhkan.
P/Invoke Wizard
P/Invoke Wizard Diarsipkan 2009-04-02 di Wayback Machine. menggunakan metode yang hampir sama dengan Asisten Interop Microsoft di mana ia menerima kode file .h C++ asli dan menghasilkan kode C# (atau VB.NET) untuk Anda tempel ke dalam kode aplikasi .NET Anda.
Ia juga memiliki pilihan untuk kerangka mana yang ingin Anda targetkan: .NET Framework untuk desktop atau Compact Framework untuk perangkat pintar Windows Mobile (dan Windows CE).
xInterop C++ . NET Bridge
xInterop C++ .NET Bridge Diarsipkan 2022-01-25 di Wayback Machine. adalah aplikasi windows untuk menciptakan bungkus C# untuk DLL C++ dan jembatan C++ untuk mengakses rakitan .NET, dilengkapi dengan pustaka C#/.NET yang membungkus kelas C++ standar, seperti string, iostream, dsb., kelas dan objek C++ dapat diakses dari .NET.
Alat ini menghasilkan DLL pembungkus C# dengan kode sumber dari DLL C++ asli yang ada dan file header terkait yang dibutuhkan oleh alat tersebut untuk membuat DLL pembungkus C#. Tanda tangan P/Invoke dan susunan data dihasilkan oleh aplikasi tersebut. Pembungkus C# yang dihasilkan memiliki antarmuka yang hampir sama dengan rekan C++ dengan jenis parameter yang diubah ke kode .NET.
Alat ini mengenali kelas templat yang tidak diekspor dari DLL C++ dan membuat instans dari kelas templat tersebut dan mengekspornya menjadi DLL pelengkap dan antarmuka C++ terkaitnya dapat digunakan di .NET.
Lihat juga
- Jenis Blittable
- Java Native Interface, cara standar untuk program Java untuk mengakses kode asli
- Java Native Access, padanan Java dari P/Invoke
- File pustaka Windows
- J/Direct, API padanan untuk Microsoft Java Virtual Machine yang tidak lagi dipelihara
Referensi
- ^ "Double Thunking (C++)".
- ^ "Initialization of Mixed Assemblies".
Pranala eksternal