0Pricing
C# Academy · Pelajaran

Dasar-Dasar P/Invoke

Deklarasikan dan panggil fungsi native menggunakan DllImport, lalu pahami marshaling primitif, string, dan struct.

Dasar-Dasar P/Invoke adalah pelajaran C# Academy gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar C# Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus C# Academy mencakup 4 pelajaran total.

Apa Itu P/Invoke?

Layanan Pemanggilan Platform (P/Invoke) memungkinkan C# memanggil fungsi dalam pustaka bersama natif (.dll di Windows, .so di Linux, .dylib di macOS). Ini adalah mekanisme standar untuk memanggil API Win32 atau pustaka ABI C apa pun dari .NET.

Panggilan P/Invoke Pertama Anda

Deklarasikan fungsi natif dengan [DllImport] dan tentukan nama pustakanya. CLR menangani pencarian dan pemuatan pustaka serta pengubahan representasi argumen.

using System.Runtime.InteropServices;

// Calling MessageBoxW from user32.dll (Windows)
internal static partial class NativeMethods
{
    [DllImport("user32.dll",
        EntryPoint = "MessageBoxW",
        CharSet = CharSet.Unicode,
        SetLastError = true)]
    internal static extern int MessageBox(
        IntPtr hwnd,
        string text,
        string caption,
        uint type);
}

// Call it:
NativeMethods.MessageBox(IntPtr.Zero, "Hello!", "P/Invoke", 0);

// Windows-only — wrap in RuntimeInformation.IsOSPlatform check
// for cross-platform code

Memanggil Pustaka Native Kustom

P/Invoke berfungsi dengan fungsi apa pun yang diekspor oleh C, bukan hanya API sistem operasi. Buat pustaka natif, ekspor fungsi dengan keterkaitan C, lalu panggil fungsi tersebut dari C#.

// mymath.h / mymath.c:
// extern "C" double AddNumbers(double a, double b) { return a + b; }
// Compile: gcc -shared -o libmymath.so mymath.c

// C# declaration:
[DllImport("mymath",              // libmymath.so / mymath.dll
    EntryPoint = "AddNumbers",
    CallingConvention = CallingConvention.Cdecl)]
private static extern double AddNumbers(double a, double b);

// Call:
double result = AddNumbers(3.14, 2.72); // 5.86

// .NET resolves the library via:
// 1. Absolute path if provided
// 2. App directory
// 3. OS library path (PATH / LD_LIBRARY_PATH / DYLD_LIBRARY_PATH)

Mengubah Representasi Tipe Dasar

CLR secara otomatis mengubah representasi sebagian besar tipe primitif antara bentuk terkelola dan natif. Mengetahui pemetaannya akan membantu menghindari bug yang sulit terlihat.

// C Type       → C# Type
// int (32-bit)  → int  or  System.Int32
// long (64-bit) → long or  System.Int64
// float         → float
// double        → double
// bool          → [MarshalAs(UnmanagedType.Bool)] bool
// char*  (ANSI) → string  (CharSet.Ansi)
// wchar_t*      → string  (CharSet.Unicode)
// void*         → IntPtr
// size_t        → UIntPtr

// Example with explicit marshalling:
[DllImport("libc", EntryPoint = "strlen", CharSet = CharSet.Ansi)]
private static extern UIntPtr StrLen(
    [MarshalAs(UnmanagedType.LPStr)] string s);

int len = (int)StrLen("hello"); // 5

Meneruskan Struktur ke Kode Natif

Gunakan [StructLayout(LayoutKind.Sequential)] untuk memastikan struktur ditata dalam memori persis seperti yang diharapkan oleh kode natif.

// C struct:
// struct Point { int x; int y; };
// void DrawPoint(struct Point p);

[StructLayout(LayoutKind.Sequential)]
public struct Point
{
    public int X;
    public int Y;
}

[DllImport("graphics", EntryPoint = "DrawPoint",
    CallingConvention = CallingConvention.Cdecl)]
private static extern void DrawPoint(Point p);

// Pass by value:
DrawPoint(new Point { X = 10, Y = 20 });

// Pass by pointer (ref or out):
[DllImport("graphics", EntryPoint = "GetCenter",
    CallingConvention = CallingConvention.Cdecl)]
private static extern void GetCenter(out Point center);
GetCenter(out var pt);

Penanganan Kesalahan: GetLastWin32Error

Fungsi API Windows memberi sinyal kesalahan melalui GetLastError(). Tetapkan SetLastError = true dalam [DllImport], lalu panggil Marshal.GetLastWin32Error() setelah pemanggilan tersebut.

[DllImport("kernel32.dll",
    EntryPoint = "CreateFileW",
    CharSet = CharSet.Unicode,
    SetLastError = true)]
private static extern IntPtr CreateFile(
    string fileName,
    uint desiredAccess,
    uint shareMode,
    IntPtr securityAttributes,
    uint creationDisposition,
    uint flagsAndAttributes,
    IntPtr templateFile);

const uint GENERIC_READ = 0x80000000;
const uint OPEN_EXISTING  = 3;

var handle = CreateFile("test.txt", GENERIC_READ, 0,
    IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);

if (handle == (IntPtr)(-1))
{
    int error = Marshal.GetLastWin32Error();
    throw new System.ComponentModel.Win32Exception(error);
}

Mengubah Representasi String dan Buffer

Pengubahan representasi string memerlukan perhatian cermat terhadap pengodean dan kepemilikan. Gunakan StringBuilder untuk buffer keluaran dan atribut MarshalAs untuk menentukan pengodean.

// Read into a buffer:
[DllImport("kernel32.dll",
    EntryPoint = "GetComputerNameW",
    CharSet = CharSet.Unicode,
    SetLastError = true)]
private static extern bool GetComputerName(
    System.Text.StringBuilder lpBuffer,
    ref uint nSize);

uint size = 256;
var buffer = new System.Text.StringBuilder((int)size);
if (GetComputerName(buffer, ref size))
    Console.WriteLine(buffer.ToString());

// Return a string owned by native code (don't free it):
[DllImport("mylib", CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
private static extern string GetVersion();

Penunjuk Fungsi dan Panggilan Balik

Teruskan delegate terkelola sebagai penunjuk fungsi C. CLR membuat thunk, tetapi Anda harus menjaga delegate tetap hidup (menyimpan referensi) atau GC akan mengumpulkannya dan kode natif akan mengalami kegagalan.

// C callback signature: typedef int (*Comparer)(const void*, const void*);
// void qsort(void* base, size_t nitems, size_t size, Comparer compar);

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int CompareCallback(IntPtr a, IntPtr b);

[DllImport("libc", CallingConvention = CallingConvention.Cdecl)]
private static extern void QSort(
    int[] data, UIntPtr count, UIntPtr size, CompareCallback compare);

// Keep the delegate ALIVE for the duration of the call:
private static readonly CompareCallback _compare =
    (a, b) => Marshal.ReadInt32(a).CompareTo(Marshal.ReadInt32(b));

var data = new[] { 5, 2, 8, 1, 3 };
QSort(data, (UIntPtr)data.Length, (UIntPtr)sizeof(int), _compare);

API NativeLibrary

Kelas NativeLibrary menyediakan pemuatan pustaka eksplisit, resolusi penunjuk fungsi, dan penyesuaian jalur lintas platform — alternatif modern untuk resolusi nama pustaka implisit.

using System.Runtime.InteropServices;

// Explicit load:
var handle = NativeLibrary.Load("/usr/lib/libssl.so.3");

// Resolve a function pointer:
var addPtr = NativeLibrary.GetExport(handle, "AddNumbers");
var addFn = Marshal.GetDelegateForFunctionPointer<Func<double,double,double>>(addPtr);
double result = addFn(1.0, 2.0);

// Unload:
NativeLibrary.Free(handle);

// Custom resolver (called when DllImport can't find a library):
NativeLibrary.SetDllImportResolver(typeof(MyNative).Assembly,
    (libName, assembly, searchPath) =>
    {
        if (libName == "mymath")
            return NativeLibrary.Load("/opt/mymath/libmymath.so");
        return IntPtr.Zero;
    });

Dunia Nyata: Memanggil OpenSSL

Contoh dunia nyata: memanggil fungsi ringkasan SHA-256 milik OpenSSL dari C# menggunakan P/Invoke.

internal static class OpenSslInterop
{
    [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr EVP_MD_CTX_new();

    [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
    private static extern void EVP_MD_CTX_free(IntPtr ctx);

    [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr EVP_sha256();

    [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)]
    private static extern int EVP_DigestInit_ex(IntPtr ctx, IntPtr type, IntPtr engine);

    // In practice, use System.Security.Cryptography.SHA256 instead:
    // var hash = SHA256.HashData(data);
    // P/Invoke to OpenSSL is only needed when you require
    // non-managed cryptographic operations or specific OpenSSL features
}

Pemeriksaan Singkat

Mengapa Anda harus menjaga delegate tetap hidup saat meneruskannya sebagai panggilan balik natif?

Ringkasan: Dasar-Dasar P/Invoke

Poin-poin utama:

  • P/Invoke: memanggil fungsi C-ABI asli melalui deklarasi [DllImport]
  • CLR secara otomatis memetakan tipe primitif; gunakan anotasi [MarshalAs] untuk string dan tipe khusus
  • [StructLayout(LayoutKind.Sequential)]: memastikan tata letak memori struktur sesuai dengan yang diharapkan kode asli
  • Atur SetLastError = true lalu panggil Marshal.GetLastWin32Error() untuk menangani galat Win32
  • Pertahankan referensi delegasi tetap hidup saat meneruskannya sebagai callback asli
  • NativeLibrary: pemuatan eksplisit, penyelesaian simbol, dan penentu khusus untuk mengendalikan jalur lintas platform

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Dasar-Dasar P/Invoke” gratis?

Ya — teks lengkap “Dasar-Dasar P/Invoke” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus C# Academy, upgrade ke CoddyKit PRO. Kursus C# Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Dasar-Dasar P/Invoke”?

Deklarasikan dan panggil fungsi native menggunakan DllImport, lalu pahami marshaling primitif, string, dan struct. Kamu berlatih C# Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai C# Academy?

Tidak diperlukan pengalaman sebelumnya. C# Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.

Berapa lama pelajaran “Dasar-Dasar P/Invoke” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran C# Academy ini?

Ya. Setiap pelajaran C# Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Dasar-Dasar P/Invoke
  2. LibraryImport & P/Invoke yang Dibuat dari Sumber
  3. Kode Tidak Aman, Pointer & Buffer Tetap
  4. Interoperabilitas COM & Runtime Callable Wrapper
← Kembali ke C# Academy