Ejemplo OpenFileDlg
En este ejemplo se muestra cómo llamar a una función no administrada que espera recibir un puntero a una estructura que contiene una cadena. Además, se muestra cómo utilizar una clase administrada para representar una estructura no administrada, cómo aplicar los atributos InAttribute y OutAttribute para calcular la referencia de la clase y devolverla al llamador, y cómo declarar e inicializar diferentes campos de la clase para producir la representación no administrada correcta.
En el ejemplo OpenFileDlg se utiliza la siguiente función no administrada, que se muestra con su declaración de función original:
GetOpenFileName exportada desde Comdlg32.dll.
BOOL GetOpenFileName(LPOPENFILENAME lpofn);
La estructura LPOPENFILENAME de la API Win32, que se pasa a la función anterior, contiene los elementos siguientes:
typedef struct tagOFN {
DWORD lStructSize;
//…
LPCTSTR lpstrFilter;
//…
LPTSTR lpstrFile;
DWORD nMaxFile;
LPTSTR lpstrFileTitle;
DWORD nMaxFileTitle;
LPCTSTR lpstrInitialDir;
LPCTSTR lpstrTitle;
//…
LPCTSTR lpstrDefExt;
//…
} OPENFILENAME, *LPOPENFILENAME;
En este ejemplo, la clase OpenFileName contiene los elementos de la estructura original como miembros de clase. La estructura no administrada se declara como una clase, en lugar de como una estructura administrada, para mostrar cómo se puede utilizar una clase cuando la función no administrada espera recibir un puntero a una estructura. Como una clase administrada es un tipo de referencia, cuando se pasa por valor, al código no administrado se pasa un puntero a la clase. Eso es precisamente lo que la función no administrada espera recibir.
El atributo StructLayoutAttribute se aplica a la clase para garantizar que los miembros se organizan secuencialmente en la memoria, en el orden en que aparecen. El campo CharSet está configurado de forma que la invocación de plataforma pueda elegir entre los formatos ANSI y Unicode en tiempo de ejecución, en función de la plataforma de destino.
La clase LibWrap contiene el prototipo administrado del método GetOpenFileName, que pasa la clase OpenFileName como un parámetro In/Out. Al aplicar InAttribute y OutAttribute de forma explícita, en el ejemplo se garantiza que se calcula una referencia de OpenFileName como un parámetro In/Out y el llamador puede ver los cambios del cálculo. Por razones de rendimiento, el atributo direccional de una clase es In, lo que impide que el llamador vea los cambios del cálculo. La clase App crea una nueva instancia de la clase OpenFileName y utiliza el método Marshal.SizeOf para determinar el tamaño, en bytes, de la estructura no administrada.
Declaración de prototipos
' 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);
};
Llamadas a funciones
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);
}
}
};
Vea también
Conceptos
Calcular referencias de cadenas
Tipos de datos de invocación de plataforma
Cálculo de referencias predeterminado para cadenas
Otros recursos
Creating Prototypes in Managed Code