Разработка DLL на языке С++, страница 6

using System;

using System.Runtime.InteropServices;

namespace MyDLLClientCSharp

{

class GaussTest

{

[STAThread]

static void Main()

{

}

}

}

Директива (InteropServices) необходима для того, чтобы стал доступным класс DllImportAttribute, производный от Attribute. Эти классы поддерживают атрубут DllImport, который мы будем использовать для описания импортируемых функций.

Сначала рассмотрим, как вызвать функцию, экспортируемую какой-нибудь не нашей, а известной DLL. Например, вызовем API-функцию GetDriveType, которая живет в Kernel32.dll. Информацию об этой функции я получил в MSDN. Введите в класс GaussTest определение импортируемой функции.

[DllImport("Kernel32")]

public static extern int GetDriveType (string lpRootPathName);

Маршалинг параметров проходит на удивление просто. Известный тип данных LPCTSTR (то есть const char*, но умеющий подстроиться под Unicode) как-то бесшовно (без помощи атрибутов) отображается в тип string. Теперь в пространство имен MyDLLClientCSharp (до объявления класса GaussTest) внесите описание вспомогательного типа данных, перечисляющего некоторые из возможных типов дисководов.

public enum DriveTypes : byte { Removable=2, LocalDisk=3, Network=4, CDROM=5 };

Информацию о константах, соответствующих типам дисководов (DRIVE_REMOVABLE и т.д.), я получил там же, где описана функция GetDriveType. Константы невозможно импортировать, поэтому приходится создавать их двойники, что и сделано в предыдущем фрагменте кода.

Теперь можно пользоваться функцией GetDriveType, но сначала следует получить массив строк с именами всех доступных дисководов. Это можно сделать прямо в .NET (без помощи API-функций), причем несколькими способами. Самым простым, видимо, является обращение к статическому методу класса Environment:

string[] drives = Environment.GetLogicalDrives();

Чуть более сложным (так как это потребует вставки using) является обращение к методу класса Directory:

string[] drives = Directory.GetLogicalDrives();

Более сложным является обращение к внутренней базе данных Windows, например, так:

ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * From Win32_LogicalDisk");

ManagementObjectCollection drives = searcher.Get();

foreach (ManagementObject drive in drives)

{

switch (int.Parse (drive["DriveType"].ToString()))

{

// И т.д.

}

}

Здорово, правда? Здесь я почувствовал, что ларец Пандорры слегка приоткрылся. Но мы оставим базу данных WMI до лучших времен, а сейчас используем самый простой метод опроса дисков. Вместо существующей версии функции Main вставьте следующий код.

[STAThread]

static void Main()

{

string[] drives = Environment.GetLogicalDrives();

Console.WriteLine ("\nThe system has:\n");

foreach (string drive in drives)

{

string name = drive.Remove (drive.Length - 1, 1), info = null;

switch ((DriveTypes)GetDriveType (drive))    // API function call

{

case DriveTypes.Removable:  info = "Removable disk "; break;

case DriveTypes.LocalDisk:  info = "LocalDisk disk "; break;

case DriveTypes.Network:    info = "Network   disk "; break;

case DriveTypes.CDROM:      info = "CDROM     disk "; break;

default:                          info = "Unknown   disk "; break;

}

Console.WriteLine (info + name);

}

Console.WriteLine ('\n');

}

Запустив приложение, вы увидите нечто, вроде:

The system has:

Removable disk A:

LocalDisk disk C:

LocalDisk disk D:

CDROM     disk F:

Пример управления маршалингом параметров можно увидеть в MSDN (ищите тему: Platform Invoke Tutorial). Приведем его суть:

[DllImport("msvcrt.dll")]

public static extern int puts([MarshalAs(UnmanagedType.LPStr)] string s);

static void Main() { puts ("Hello PInvoke"); }