Freigeben über


Marshallen von Daten mit Plattformaufruf

Zum Aufrufen von Funktionen, die aus einer nicht verwalteten Bibliothek exportiert werden, erfordert eine .NET Framework-Anwendung einen Funktionsprototyp in verwaltetem Code, der die nicht verwaltete Funktion darstellt. Gehen Sie zum Erstellen eines Prototypen, der den Aufruf der Plattform für das korrekte Marshalling von Daten ermöglicht, gehen Sie folgendermaßen vor:

  • Wenden Sie das DllImportAttribute Attribut auf die statische Funktion oder Methode im verwalteten Code an.

  • Ersetzen Sie verwaltete Datentypen durch nicht verwaltete Datentypen.

Sie können die dokumentation verwenden, die mit einer nicht verwalteten Funktion bereitgestellt wird, um einen entsprechenden verwalteten Prototyp zu erstellen, indem Sie das Attribut mit seinen optionalen Feldern anwenden und verwaltete Datentypen für nicht verwaltete Typen ersetzen. Anweisungen zum Anwenden der DllImportAttributeDLL-Funktionen finden Sie unter Verwenden nicht verwalteter DLL-Funktionen.

Dieser Abschnitt enthält Beispiele, die veranschaulichen, wie Sie Prototypen für verwaltete Funktionen erstellen, um Argumente an Funktionen zu übergeben und Rückgabewerte von funktionen zu empfangen, die von nicht verwalteten Bibliotheken exportiert werden. Die Beispiele zeigen auch, wann das MarshalAsAttribute-Attribut und die Marshal-Klasse zum expliziten Marshallen von Daten verwendet werden.

Datentypen für den Plattformaufruf

In der folgenden Tabelle sind Datentypen aufgeführt, die in den Funktionen Windows-APIs und C-Stil verwendet werden. Viele nicht verwaltete Bibliotheken enthalten Funktionen, die diese Datentypen als Parameter übergeben und Werte zurückgeben. In der dritten Spalte sind der entsprechende integrierte .NET Framework-Werttyp oder die entsprechende Klasse aufgeführt, die Sie in verwaltetem Code verwenden. In einigen Fällen können Sie einen Typ der gleichen Größe für den typ ersetzen, der in der Tabelle aufgeführt ist.

Nicht verwalteter Typ in Windows-APIs Nicht verwalteter C-Sprachtyp Verwalteter Typ BESCHREIBUNG
VOID void System.Void Wird auf eine Funktion angewendet, die keinen Wert zurückgibt.
HANDLE void * System.IntPtr oder System.UIntPtr 32 Bits unter 32-Bit-Windows-Betriebssystemen, 64 Bit auf 64-Bit-Windows-Betriebssystemen.
BYTE unsigned char System.Byte 8 Bits
SHORT short System.Int16 16 Bits
WORD unsigned short System.UInt16 16 Bits
INT int System.Int32 32 Bits
UINT unsigned int System.UInt32 32 Bits
LONG long System.Int32 32 Bits
BOOL long System.Boolean oder System.Int32 32 Bits
DWORD unsigned long System.UInt32 32 Bits
ULONG unsigned long System.UInt32 32 Bits
CHAR char System.Char Mit ANSI ergänzen.
WCHAR wchar_t System.Char Mit Unicode dekorieren.
LPSTR char * System.String oder System.Text.StringBuilder Mit ANSI ergänzen.
LPCSTR const char * System.String oder System.Text.StringBuilder Mit ANSI ergänzen.
LPWSTR wchar_t * System.String oder System.Text.StringBuilder Mit Unicode dekorieren.
LPCWSTR const wchar_t * System.String oder System.Text.StringBuilder Mit Unicode dekorieren.
FLOAT float System.Single 32 Bits
DOUBLE double System.Double 64 Bits

Entsprechende Typen in Visual Basic, C# und C++ finden Sie in der Einführung in die .NET Framework-Klassenbibliothek.

PinvokeLib.dll

Der folgende Code definiert die Bibliotheksfunktionen, die von PInvokeLib.dllbereitgestellt werden. Viele Beispiele in diesem Abschnitt rufen diese Bibliothek auf.

Beispiel

// PInvokeLib.cpp : Defines the entry point for the DLL application.
//

#define PINVOKELIB_EXPORTS
#include "PInvokeLib.h"

#include <strsafe.h>
#include <objbase.h>
#include <stdio.h>

#pragma comment(lib,"ole32.lib")

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }

    return TRUE;
}

//******************************************************************
// This is the constructor of a class that has been exported.
CTestClass::CTestClass()
{
    m_member = 1;
}

int CTestClass::DoSomething( int i )
{
    return i*i + m_member;
}

PINVOKELIB_API CTestClass* CreateTestClass()
{
    return new CTestClass();
}

PINVOKELIB_API void DeleteTestClass( CTestClass* instance )
{
    delete instance;
}

//******************************************************************
PINVOKELIB_API int TestArrayOfInts( int* pArray, int size )
{
    int result = 0;

    for ( int i = 0; i < size; i++ )
    {
        result += pArray[ i ];
        pArray[i] += 100;
    }
    return result;
}

//******************************************************************
PINVOKELIB_API int TestRefArrayOfInts( int** ppArray, int* pSize )
{
    int result = 0;

    // CoTaskMemAlloc must be used instead of the new operator
    // because code on the managed side will call Marshal.FreeCoTaskMem
    // to free this memory.

    int* newArray = (int*)CoTaskMemAlloc( sizeof(int) * 5 );

    for ( int i = 0; i < *pSize; i++ )
    {
        result += (*ppArray)[i];
    }

    for ( int j = 0; j < 5; j++ )
    {
        newArray[j] = (*ppArray)[j] + 100;
    }

    CoTaskMemFree( *ppArray );
    *ppArray = newArray;
    *pSize = 5;

    return result;
}

//******************************************************************
PINVOKELIB_API int TestMatrixOfInts( int pMatrix[][COL_DIM], int row )
{
    int result = 0;

    for ( int i = 0; i < row; i++ )
    {
        for ( int j = 0; j < COL_DIM; j++ )
        {
            result += pMatrix[i][j];
            pMatrix[i][j] += 100;
        }
    }
    return result;
}

//******************************************************************
PINVOKELIB_API int TestArrayOfStrings( char* ppStrArray[], int count )
{
    int result = 0;
    STRSAFE_LPSTR temp;
    size_t len;
    const size_t alloc_size = sizeof(char) * 10;

    for ( int i = 0; i < count; i++ )
    {
        len = 0;
        StringCchLengthA( ppStrArray[i], STRSAFE_MAX_CCH, &len );
        result += len;

        temp = (STRSAFE_LPSTR)CoTaskMemAlloc( alloc_size );
        StringCchCopyA( temp, alloc_size, (STRSAFE_LPCSTR)"123456789" );

       // CoTaskMemFree must be used instead of delete to free memory.

       CoTaskMemFree( ppStrArray[i] );
       ppStrArray[i] = (char *) temp;
   }

   return result;
}

//******************************************************************
PINVOKELIB_API int TestArrayOfStructs( MYPOINT* pPointArray, int size )
{
    int result = 0;
    MYPOINT* pCur = pPointArray;

    for ( int i = 0; i < size; i++ )
    {
        result += pCur->x + pCur->y;
        pCur->y = 0;
        pCur++;
    }

    return result;
}

//******************************************************************
PINVOKELIB_API int TestStructInStruct( MYPERSON2* pPerson2 )
{
    size_t len = 0;

    StringCchLengthA( pPerson2->person->last, STRSAFE_MAX_CCH, &len );
    len = sizeof(char) * ( len + 2 ) + 1;

    STRSAFE_LPSTR temp = (STRSAFE_LPSTR)CoTaskMemAlloc( len );
    StringCchCopyA( temp, len, (STRSAFE_LPSTR)"Mc" );
    StringCbCatA( temp, len, (STRSAFE_LPSTR)pPerson2->person->last );

    CoTaskMemFree( pPerson2->person->last );
    pPerson2->person->last = (char *)temp;

    return pPerson2->age;
}

//******************************************************************
PINVOKELIB_API int TestArrayOfStructs2( MYPERSON* pPersonArray, int size )
{
    int result = 0;
    MYPERSON* pCur = pPersonArray;
    STRSAFE_LPSTR temp;
    size_t len;

    for ( int i = 0; i < size; i++ )
    {
        len = 0;
        StringCchLengthA( pCur->first, STRSAFE_MAX_CCH, &len );
        len++;
        result += len;
        len = 0;
        StringCchLengthA( pCur->last, STRSAFE_MAX_CCH, &len );
        len++;
        result += len;

        len = sizeof(char) * ( len + 2 );
        temp = (STRSAFE_LPSTR)CoTaskMemAlloc( len );
        StringCchCopyA( temp, len, (STRSAFE_LPCSTR)"Mc" );
        StringCbCatA( temp, len, (STRSAFE_LPCSTR)pCur->last );
        result += 2;

        // CoTaskMemFree must be used instead of delete to free memory.
        CoTaskMemFree( pCur->last );
        pCur->last = (char *)temp;
        pCur++;
   }

   return result;
}

//******************************************************************
PINVOKELIB_API void TestStructInStruct3( MYPERSON3 person3 )
{
    printf( "\n\nperson passed by value:\n" );
    printf( "first = %s last = %s age = %i\n\n",
            person3.person.first,
            person3.person.last,
            person3.age );
}

//*********************************************************************
PINVOKELIB_API void TestUnion( MYUNION u, int type )
{
    if ( ( type != 1 ) && ( type != 2 ) )
    {
        return;
    }
    if ( type == 1 )
    {
        printf( "\n\ninteger passed: %i", u.i );
    }
    else if ( type == 2 )
    {
        printf( "\n\ndouble passed: %f", u.d );
    }
}

//******************************************************************
PINVOKELIB_API void TestUnion2( MYUNION2 u, int type )
{
    if ( ( type != 1 ) && ( type != 2 ) )
    {
        return;
    }
    if ( type == 1 )
    {
        printf( "\n\ninteger passed: %i", u.i );
    }
    else if ( type == 2 )
    {
        printf( "\n\nstring passed: %s", u.str );
    }
}

//******************************************************************
PINVOKELIB_API void TestCallBack( FPTR pf, int value )
{
    printf( "\nReceived value: %i", value );
    printf( "\nPassing to callback..." );
    bool res = (*pf)(value);

    if ( res )
    {
        printf( "Callback returned true.\n" );
    }
    else
    {
        printf( "Callback returned false.\n" );
    }
}

//******************************************************************
PINVOKELIB_API void TestCallBack2( FPTR2 pf2, char* value )
{
    printf( "\nReceived value: %s", value );
    printf( "\nPassing to callback..." );
    bool res = (*pf2)(value);

    if ( res )
    {
        printf( "Callback2 returned true.\n" );
    }
    else
    {
        printf( "Callback2 returned false.\n" );
    }
}

//******************************************************************
PINVOKELIB_API void TestStringInStruct( MYSTRSTRUCT* pStruct )
{
    wprintf( L"\nUnicode buffer content: %s\n", pStruct->buffer );

    // Assuming that the buffer is big enough.
    StringCbCatW( pStruct->buffer, pStruct->size, (STRSAFE_LPWSTR)L"++" );
}

//******************************************************************
PINVOKELIB_API void TestStringInStructAnsi( MYSTRSTRUCT2* pStruct )
{
    printf( "\nAnsi buffer content: %s\n", pStruct->buffer );

    // Assuming that the buffer is big enough.
    StringCbCatA( (STRSAFE_LPSTR) pStruct->buffer, pStruct->size, (STRSAFE_LPSTR)"++" );
}

//******************************************************************
PINVOKELIB_API void TestOutArrayOfStructs( int* pSize, MYSTRSTRUCT2** ppStruct )
{
    const int cArraySize = 5;
    *pSize = 0;
    *ppStruct = (MYSTRSTRUCT2*)CoTaskMemAlloc( cArraySize * sizeof( MYSTRSTRUCT2 ));

    if ( ppStruct != NULL )
    {
        MYSTRSTRUCT2* pCurStruct = *ppStruct;
        LPSTR buffer;
        *pSize = cArraySize;

        STRSAFE_LPCSTR teststr = "***";
        size_t len = 0;
        StringCchLengthA(teststr, STRSAFE_MAX_CCH, &len);
        len++;

        for ( int i = 0; i < cArraySize; i++, pCurStruct++ )
        {
            pCurStruct->size = len;
            buffer = (LPSTR)CoTaskMemAlloc( len );
            StringCchCopyA( buffer, len, teststr );
            pCurStruct->buffer = (char *)buffer;
        }
    }
}

//************************************************************************
PINVOKELIB_API char * TestStringAsResult()
{

    const size_t alloc_size = 64;
    STRSAFE_LPSTR result = (STRSAFE_LPSTR)CoTaskMemAlloc( alloc_size );
    STRSAFE_LPCSTR teststr = "This is return value";
    StringCchCopyA( result, alloc_size, teststr );

    return (char *) result;
}

//************************************************************************
PINVOKELIB_API void SetData( DataType typ, void* object )
{
    switch ( typ )
    {
        case DT_I2: printf( "Short %i\n", *((short*)object) ); break;
        case DT_I4: printf( "Long %i\n", *((long*)object) ); break;
        case DT_R4: printf( "Float %f\n", *((float*)object) ); break;
        case DT_R8: printf( "Double %f\n", *((double*)object) ); break;
        case DT_STR: printf( "String %s\n", (char*)object ); break;
        default: printf( "Unknown type" ); break;
    }
}

//************************************************************************
PINVOKELIB_API void TestArrayInStruct( MYARRAYSTRUCT* pStruct )
{
    pStruct->flag = true;
    pStruct->vals[0] += 100;
    pStruct->vals[1] += 100;
    pStruct->vals[2] += 100;
}
// PInvokeLib.h : The header file for the DLL application.
//

#pragma once

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the PINVOKELIB_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// PINVOKELIB_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef PINVOKELIB_EXPORTS
#define PINVOKELIB_API __declspec(dllexport)
#else
#define PINVOKELIB_API __declspec(dllimport)
#endif

// Define the test structures

typedef struct _MYPOINT
{
    int x;
    int y;
} MYPOINT;

typedef struct _MYPERSON
{
    char* first;
    char* last;
} MYPERSON;

typedef struct _MYPERSON2
{
    MYPERSON* person;
    int age;
} MYPERSON2;

typedef struct _MYPERSON3
{
    MYPERSON person;
    int age;
} MYPERSON3;

union MYUNION
{
    int i;
    double d;
};

union MYUNION2
{
    int i;
    char str[128];
};

typedef struct _MYSTRSTRUCT
{
    wchar_t* buffer;
    UINT size;
} MYSTRSTRUCT;

typedef struct _MYSTRSTRUCT2
{
    char* buffer;
    UINT size;
} MYSTRSTRUCT2;

typedef struct _MYARRAYSTRUCT
{
    bool flag;
    int vals[3];
} MYARRAYSTRUCT;

// constants and pointer definitions

const int COL_DIM = 5;

typedef bool (CALLBACK *FPTR)( int i );

typedef bool (CALLBACK *FPTR2)( char* str );

// Data type codes
enum DataType
{
    DT_I2 = 1,
    DT_I4,
    DT_R4,
    DT_R8,
    DT_STR
};

// This is an exported class.
class PINVOKELIB_API CTestClass
{
public:
    CTestClass( void );
    int DoSomething( int i );

private:
    int m_member;
};

// Exports for PInvokeLib.dll

#ifdef __cplusplus
extern "C"
{
#endif

PINVOKELIB_API CTestClass* CreateTestClass();

PINVOKELIB_API void DeleteTestClass( CTestClass* instance );

PINVOKELIB_API int TestArrayOfInts( int* pArray, int size );

PINVOKELIB_API int TestRefArrayOfInts( int** ppArray, int* pSize );

PINVOKELIB_API int TestMatrixOfInts( int pMatrix[][COL_DIM], int row );

PINVOKELIB_API int TestArrayOfStrings( char* ppStrArray[], int size );

PINVOKELIB_API int TestArrayOfStructs( MYPOINT* pPointArray, int size );

PINVOKELIB_API int TestArrayOfStructs2( MYPERSON* pPersonArray, int size );

PINVOKELIB_API int TestStructInStruct( MYPERSON2* pPerson2 );

PINVOKELIB_API void TestStructInStruct3( MYPERSON3 person3 );

PINVOKELIB_API void TestUnion( MYUNION u, int type );

PINVOKELIB_API void TestUnion2( MYUNION2 u, int type );

PINVOKELIB_API void TestCallBack( FPTR pf, int value );

PINVOKELIB_API void TestCallBack2( FPTR2 pf2, char* value );

// buffer is an in/out param
PINVOKELIB_API void TestStringInStruct( MYSTRSTRUCT* pStruct );

// buffer is in/out param
PINVOKELIB_API void TestStringInStructAnsi( MYSTRSTRUCT2* pStruct );

PINVOKELIB_API void TestOutArrayOfStructs( int* pSize, MYSTRSTRUCT2** ppStruct );

PINVOKELIB_API char* TestStringAsResult();

PINVOKELIB_API void SetData( DataType typ, void* object );

PINVOKELIB_API void TestArrayInStruct( MYARRAYSTRUCT* pStruct );

#ifdef __cplusplus
}
#endif

Um die Bibliotheksfunktionen aus verwaltetem Code aufzurufen, implementieren Sie verwaltete Prototypen für jede Funktion, die Sie aufrufen möchten. Wenn der nicht verwaltete Code benutzerdefinierte Typen verwendet, müssen Sie diese Typen auch in Ihrem verwalteten Code deklarieren. Versehen Sie jeden Prototyp mit dem DllImportAttribute-Attribut, und verwenden Sie StructLayoutAttribute, um das Layout der verwalteten Strukturen so zu steuern, dass sie den nicht verwalteten Entsprechungen gleichen.

Verwaltete Typdeklarationen

Der folgende Code zeigt die verwalteten Entsprechungen der nicht verwalteten Typen, die in PInvokeLib.h. Jede Struktur verwendet StructLayout , um sicherzustellen, dass das Feldlayout dem nicht verwalteten Layout entspricht:

// Managed type declarations that correspond to the unmanaged types in PinvokeLib.dll.

[StructLayout(LayoutKind.Sequential)]
internal struct MyPoint
{
    public int x;
    public int y;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct MyPerson
{
    public string first;
    public string last;
}

[StructLayout(LayoutKind.Sequential)]
internal struct MyPerson2
{
    public IntPtr person; // Pointer to a MyPerson structure
    public int age;
}

[StructLayout(LayoutKind.Sequential)]
internal struct MyPerson3
{
    public MyPerson person; // Embedded MyPerson structure
    public int age;
}

[StructLayout(LayoutKind.Explicit)]
internal struct MyUnion
{
    [FieldOffset(0)] public int i;
    [FieldOffset(0)] public double d;
}

[StructLayout(LayoutKind.Sequential)]
internal struct MyArrayStruct
{
    public bool flag;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public int[] vals;
}
' Managed type declarations that correspond to the unmanaged types in PinvokeLib.dll.

<StructLayout(LayoutKind.Sequential)>
Friend Structure MyPoint
    Public x As Integer
    Public y As Integer
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Friend Structure MyPerson
    Public first As String
    Public last As String
End Structure

<StructLayout(LayoutKind.Sequential)>
Friend Structure MyPerson2
    Public person As IntPtr ' Pointer to a MyPerson structure
    Public age As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Friend Structure MyPerson3
    Public person As MyPerson ' Embedded MyPerson structure
    Public age As Integer
End Structure

<StructLayout(LayoutKind.Explicit)>
Friend Structure MyUnion
    <FieldOffset(0)> Public i As Integer
    <FieldOffset(0)> Public d As Double
End Structure

<StructLayout(LayoutKind.Sequential)>
Friend Structure MyArrayStruct
    Public flag As Boolean

    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)>
    Public vals As Integer()
End Structure

Prototypen verwalteter Funktionen

Der folgende Code zeigt die DllImport Deklarationen, die nicht verwaltete Funktionen für den verwalteten Code von PinvokeLib.dll verfügbar machen:

internal static class NativeMethods
{
    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfInts(
        [In, Out] int[] array, int size);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestRefArrayOfInts(
        ref IntPtr array, ref int size);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestMatrixOfInts(
        [In, Out] int[,] matrix, int row);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfStrings(
        [In, Out] string[] array, int size);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfStructs(
        [In, Out] MyPoint[] pointArray, int size);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfStructs2(
        [In, Out] MyPerson[] personArray, int size);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestStructInStruct(ref MyPerson2 person2);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void TestStructInStruct3(MyPerson3 person3);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void TestUnion(MyUnion u, int type);

    [DllImport("PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern void TestArrayInStruct(ref MyArrayStruct myStruct);
}
Friend Class NativeMethods
    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfInts(
        <[In], Out> ByVal array() As Integer, ByVal size As Integer) As Integer
    End Function

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestRefArrayOfInts(
        ByRef array As IntPtr, ByRef size As Integer) As Integer
    End Function

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestMatrixOfInts(
        <[In], Out> ByVal matrix(,) As Integer, ByVal row As Integer) As Integer
    End Function

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfStrings(
        <[In], Out> ByVal array() As String, ByVal size As Integer) As Integer
    End Function

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfStructs(
        <[In], Out> ByVal pointArray() As MyPoint, ByVal size As Integer) As Integer
    End Function

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfStructs2(
        <[In], Out> ByVal personArray() As MyPerson, ByVal size As Integer) As Integer
    End Function

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestStructInStruct(
        ByRef person2 As MyPerson2) As Integer
    End Function

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Sub TestStructInStruct3(ByVal person3 As MyPerson3)
    End Sub

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Sub TestUnion(ByVal u As MyUnion, ByVal type As Integer)
    End Sub

    <DllImport("PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Sub TestArrayInStruct(ByRef myStruct As MyArrayStruct)
    End Sub
End Class

Weitere Informationen und Beispiele finden Sie in den folgenden Artikeln: