OpenFileDlg, exemple
Cet exemple montre comment appeler une fonction non managée qui nécessite un pointeur vers une structure contenant une chaîne. De plus, il montre comment utiliser une classe managée pour représenter une structure non managée, comment appliquer les attributs InAttribute et OutAttribute pour remarshaler la classe vers l'appelant, et comment déclarer et initialiser différents champs de la classe pour produire la représentation non managée correcte.
L'exemple OpenFileDlg utilise la fonction non managée suivante, illustrée avec sa déclaration de fonction d'origine :
GetOpenFileName exportée à partir de Comdlg32.dll.
BOOL GetOpenFileName(LPOPENFILENAME lpofn);
La structure LPOPENFILENAME de l'API Win32 qui est passée à la fonction précédente contient les éléments suivants :
typedef struct tagOFN {
DWORD lStructSize;
//…
LPCTSTR lpstrFilter;
//…
LPTSTR lpstrFile;
DWORD nMaxFile;
LPTSTR lpstrFileTitle;
DWORD nMaxFileTitle;
LPCTSTR lpstrInitialDir;
LPCTSTR lpstrTitle;
//…
LPCTSTR lpstrDefExt;
//…
} OPENFILENAME, *LPOPENFILENAME;
Dans cet exemple, la classe OpenFileName contient les éléments de la structure d'origine en tant que membres de classe. La structure non managée est déclarée en tant que classe au lieu de l'être en tant que structure managée, afin de montrer comment une classe peut être utilisée lorsque la fonction non managée nécessite un pointeur vers une structure. Dans la mesure où une classe managée est un type référence, lorsque celle-ci est passée par valeur, un pointeur vers la classe est passé vers du code non managé. C'est précisément à quoi la fonction non managée s'attend.
L'attribut StructLayoutAttribute est appliqué à la classe afin de garantir que les membres sont disposés en mémoire de manière séquentielle, dans l'ordre de leur apparition. Le champ CharSet est défini de telle sorte que l'appel de code non managé peut choisir entre les formats Unicode et ANSI au moment de l'exécution, en fonction de la plateforme cible.
La classe LibWrap contient le prototype managé de la méthode GetOpenFileName qui passe la classe OpenFileName comme paramètre en entrée/sortie. En appliquant InAttribute et OutAttribute explicitement, l'exemple s'assure que OpenFileName est marshalé comme paramètre en entrée/sortie et que l'appelant peut voir les changements remarshalés. (Pour de meilleures performances, l'attribut directionnel par défaut pour une classe est In, ce qui empêche l'appelant de voir les changements remarshalés.) La classe App crée une nouvelle instance de la classe OpenFileName et utilise la méthode Marshal.SizeOf pour déterminer la taille, en octets, de la structure non managée.
Déclaration de prototypes
' Declare a class member for each structure element.
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Class OpenFileName
Public structSize As Integer = 0
Public hwnd As IntPtr = IntPtr.Zero
Public hinst As IntPtr = IntPtr.Zero
Public filter As String = Nothing
Public custFilter As String = Nothing
Public custFilterMax As Integer = 0
Public filterIndex As Integer = 0
Public file As String = Nothing
Public maxFile As Integer = 0
Public fileTitle As String = Nothing
Public maxFileTitle As Integer = 0
Public initialDir As String = Nothing
Public title As String = Nothing
Public flags As Integer = 0
Public fileOffset As Short = 0
Public fileExtMax As Short = 0
Public defExt as String = Nothing
Public custData As Integer = 0
Public pHook As IntPtr = IntPtr.Zero
Public template As String = Nothing
End Class
Public Class LibWrap
' Declare managed prototype for the unmanaged function.
Declare Auto Function GetOpenFileName Lib "Comdlg32.dll" ( _
<[In], Out> ByVal ofn As OpenFileName ) As Boolean
End Class
// Declare a class member for each structure element.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public class OpenFileName
{
public int structSize = 0;
public IntPtr hwnd = IntPtr.Zero;
public IntPtr hinst = IntPtr.Zero;
public string filter = null;
public string custFilter = null;
public int custFilterMax = 0;
public int filterIndex = 0;
public string file = null;
public int maxFile = 0;
public string fileTitle = null;
public int maxFileTitle = 0;
public string initialDir = null;
public string title = null;
public int flags = 0;
public short fileOffset = 0;
public short fileExtMax = 0;
public string defExt = null;
public int custData = 0;
public IntPtr pHook = IntPtr.Zero;
public string template = null;
}
public class LibWrap
{
// Declare a managed prototype for the unmanaged function.
[DllImport("Comdlg32.dll", CharSet=CharSet.Auto)]
public static extern bool GetOpenFileName([In, Out] OpenFileName ofn);
}
// Declare a class member for each structure element.
[StructLayout(LayoutKind::Sequential, CharSet=CharSet::Auto)]
public ref class OpenFileName
{
public:
int structSize;
IntPtr hwnd;
IntPtr hinst;
String^ filter;
String^ custFilter;
int custFilterMax;
int filterIndex;
String^ file;
int maxFile;
String^ fileTitle;
int maxFileTitle;
String^ initialDir;
String^ title;
int flags;
short fileOffset;
short fileExtMax;
String^ defExt;
int custData;
IntPtr pHook;
String^ tmplate;
OpenFileName()
{
// Initialize the fields.
for each (FieldInfo^ fi in this->GetType()->GetFields())
{
fi->SetValue(this, nullptr);
}
}
};
public ref class LibWrap
{
public:
// Declare a managed prototype for the unmanaged function.
[DllImport("Comdlg32.dll", CharSet=CharSet::Auto)]
static bool GetOpenFileName([In, Out] OpenFileName^ ofn);
};
Fonctions d'appel
Public Class App
Public Shared Sub Main()
Dim ofn As New OpenFileName()
ofn.structSize = Marshal.SizeOf(ofn)
ofn.filter = "Log files" & ChrW(0) & "*.log" & ChrW(0) & _
"Batch files" & ChrW(0) & "*.bat" & ChrW(0)
ofn.file = New String(New Char(256){})
ofn.maxFile = ofn.file.Length
ofn.fileTitle = new string(New Char(64){})
ofn.maxFileTitle = ofn.fileTitle.Length
ofn.initialDir = "C:\"
ofn.title = "Open file called using platform invoke..."
ofn.defExt = "txt"
If LibWrap.GetOpenFileName(ofn) Then
Console.WriteLine("Selected file with full path: {0}", ofn.file)
End If
End Sub
End Class
public class App
{
public static void Main()
{
OpenFileName ofn = new OpenFileName();
ofn.structSize = Marshal.SizeOf(ofn);
ofn.filter = "Log files\0*.log\0Batch files\0*.bat\0";
ofn.file = new string(new char[256]);
ofn.maxFile = ofn.file.Length;
ofn.fileTitle = new string(new char[64]);
ofn.maxFileTitle = ofn.fileTitle.Length;
ofn.initialDir = "C:\\";
ofn.title = "Open file called using platform invoke...";
ofn.defExt = "txt";
if (LibWrap.GetOpenFileName(ofn))
{
Console.WriteLine("Selected file with full path: {0}", ofn.file);
}
}
}
public ref class App
{
public:
static void Main()
{
OpenFileName^ ofn = gcnew OpenFileName();
ofn->structSize = Marshal::SizeOf(ofn);
ofn->filter = "Log files\0*.log\0Batch files\0*.bat\0";
ofn->file = gcnew String(gcnew array<Char>(256));
ofn->maxFile = ofn->file->Length;
ofn->fileTitle = gcnew String(gcnew array<Char>(64));
ofn->maxFileTitle = ofn->fileTitle->Length;
ofn->initialDir = "C:\\";
ofn->title = "Open file called using platform invoke...";
ofn->defExt = "txt";
if (LibWrap::GetOpenFileName(ofn))
{
Console::WriteLine("Selected file with full path: {0}", ofn->file);
}
}
};
Voir aussi
Concepts
Types de données d'appel de code non managé
Marshaling par défaut pour les chaînes
Autres ressources
Creating Prototypes in Managed Code