MIDL (Microsoft Interface Definition Language)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 20:05, 1 июня 2016.
MIDL (Microsoft Interface Definition Language)
Парадигма мультипарадигменный: рефлексивно-ориентированный, объектно-ориентированный, императивный, скриптовый
Спроектировано Microsoft Corporation
Печать дисциплины статическая, слабая
Портал: msdn.microsoft.com
Под влиянием
C, С++

Microsoft Interface Definition Language (MIDL) определяет интерфейсы между клиентскими и серверными программами. Microsoft включает компилятор MIDL в комплект средств Software Development Kit (SDK), что позволяет разработчикам создавать файлы языка описания интерфейсов (IDL) и файлы конфигурации приложения (ACF), необходимые для удаленного вызова процедур (RPC) интерфейсов и COM/DCOM интерфейсов.

Области применения

MIDL может быть использован во всех клиент-серверных приложениях, написанных для операционных систем семейства Windows. Он также может применяться для создания клиентских и серверных программ для гетерогенных сетевых сред, включающих такие операционные системы, как Unix и Apple. Для взаимодействия Microsoft поддерживает стандарт Open Group (ранее известный как Фонд открытого программного обеспечения) DCE RPC.

Необходимые навыки для использования MIDL

При использовании MIDL с RPC, требуется знакомство с языком программирования C/C++ и знание RPC парадигмы. При использовании MIDL с COM (англ. Component Object Model — объектная модель компонентов), необходимо понимание как C++ и RPC применяются в COM, или, в качестве альтернативы, требуется опыт работы с OLE Automation моделью сценариев и типом библиотек.

Интерфейс в распределённых объектах

В распределенных вычислительных системах, интерфейс представляет собой набор определений и удаленных функций, который позволяет двум и более программам взаимодействовать различными способами. В приложении RPC, интерфейс определяет:* Как клиентские и серверные приложения идентифицируют себя.

  • Как данные передаются между клиентом и сервером.
  • Удаленные процедуры, которые могут обратиться к клиентскому приложению.
  • Типы данных для параметров и возвращаемых значений удаленных процедур.

Microsoft Interface Definition Language (MIDL) служит для реализации интерфейсов, используемых в распределенных приложениях. С MIDL, приложение может иметь один или несколько интерфейсов. Каждый интерфейс определяет уникальный распределенный протокол между программами клиента и сервера. Приложения, основанные на удаленных вызовов процедур (RPC), Component Object Model (COM), и Distributed Component Object Model (DCOM) указывают свои интерфейсы, используя MIDL.

Interface Definition (IDL) File

Файл, который содержит в себе интерфейс и определения библиотек, называется файлом IDL, и имеет IDL-расширение. На практике, компилятор MIDL будет разбирать файл описания интерфейса, независимо от его расширения. Интерфейс идентифицируется набором ключевых слов. Каждый интерфейс состоит из заголовка и тела. Заголовок содержит атрибуты, которые применяются ко всему интерфейсу. Тело содержит оставшиеся определения интерфейса.

Пример IDL файла

Example.idl

В примере файла IDL определяется два COM интерфейса. Из этого IDL файла, утилита Midl.exe будет генерировать прокси/заглушки, файлы заголовков и проводить маршалинг кода.

//
// Example.idl 
//
import "mydefs.h","unknwn.idl"; 
[
object,
uuid(a03d1420-b1ec-11d0-8c3a-00c04fc31d2f),
] interface IFace1 : IUnknown
{
HRESULT MethodA([in] short Bread, [out] BKFST * pBToast);
HRESULT MethodB([in, out] BKFST * pBPoptart);
};[
object,
uuid(a03d1421-b1ec-11d0-8c3a-00c04fc31d2f),
pointer_default(unique)
] interface IFace2 : IUnknown
{
HRESULT MethodC([in] long Max,
                [in, max_is(Max)] BkfstStuff[ ],
                [out] long * pSize,
                [out, size_is( , *pSize)] BKFST ** ppBKFST);
};

Директива IDL import используется, чтобы подключить файл заголовка, Mydefs.h, который содержит пользовательские типы, и Unknwn.idl, содержащий определение IUnknown, от которого наследуются IFace1 и IFace2.

Атрибут object идентифицирует интерфейс в качестве объекта интерфейса и сообщает компилятору о необходимости сгенерировать код MIDL прокси/заглушек вместо RPC на клиенте и серверных заглушек. Методы интерфейса должны возвращать тип HRESULT, чтобы позволить основному механизму RPC сообщить об ошибках при вызове методов, которые не получилось завершить из-за проблем с сетью.

Использование uuid атрибута определяет идентификатор интерфейса (IID). Каждый интерфейс, класс и тип библиотеки должны быть идентифицированы собственным уникальным идентификатором. Для генерации набора с уникальными идентификаторами для интерфейсов и других компонентов используется утилита Uuidgen.exe.

Ключевое слово interface определяет имя интерфейса. Все объекты должны быть производными, прямо или косвенно, от IUnknown.

Параметр in определяет передаваемое значение, устанавливаемое вызывающим методом. Параметр out определяет данные, которые будут переданы обратно. Использование обоих атрибутов для одного параметра указывает, что он используется как для отправки данных и для их передачи назад вызывающей стороне.

Атрибут pointer_default определяет тип указателя по умолчанию (unique, ref, or ptr) для всех указателей кроме тех, что включенны в списки параметров. Если тип по умолчанию не задан, MIDL предполагает, что отдельные указатели являются unique. Тем не менее, если у вас есть несколько уровней указателей, вы должны явно указать тип указателя по умолчанию, даже если вы хотите, чтобы тип был unique.

В предыдущем примере, массив BkfstStuff[] является динамически расширяемым массивом, размер которого определяется во время выполнения программы. Атрибут max_is определяет переменную, содержащую максимальное значение индекса массива.

Атрибут size_is также используется, чтобы указать размер массива, или, как в предыдущем примере, несколько уровней указателей. В примере, вызов может быть сделан, несмотря на то, что заранее может быть не известно, сколько данных будет возвращено.

Example2.idl

Следующий пример IDL (который повторно использует интерфейсы, описанные в предыдущем примере IDL) показывает различные способы получения типов информационных библиотек для интерфейсов.

//
// Example2.idl
//

import "example.idl","oaidl.idl"; 
[
uuid(a03d1422-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace3 interface"),
pointer_default(unique);
dual,
oleautomation
] 
interface IFace3 : IDispatch
{
   HRESULT MethodD([in] BSTR OrderIn,
                   [out, retval] * pTakeOut);
}; //end IFace3 def

[
uuid(a03d1423-b1ec-11d0-8c3a-00c04fc31d2f),
version(1.0),
helpstring("Example Type Library"),
] library ExampleLib
{
  importlib("stdole32.tlb");
  interface IFace3;
  [
  uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
  helpstring("Breakfast Component Class")
  ] coclass BkfstComponent
    {
    [default]interface IFace1;
    interfaceIFace2
    }; //end coclass def

[
uuid(a03d1424-b1ec-11d0-8c3a-00c04fc31d2f),
helpstring("IFace4 interface"),
pointer_default(unique);
dual,
oleautomation
] 
interface IFace4 : IDispatch
{
[propput] HRESULT MethodD([in] BSTR OrderIn);
[propget] HRESULT MethodE([out, retval] * pTakeOut);
}; //end IFace4 def}; //end library def

Атрибут helpstring является обязательным; вы используете его для краткого описания объекта или предоставления строки состояния. Это помогает строкам быть читаемым из объекта браузера, например, из того, который предоставляет Microsoft Visual Basic.

Атрибут dual на IFace3 создает интерфейс, который является одновременно интерфейсом доставки и интерфейсом COM Поскольку он является производным от IDispatch. Двойной интерфейс поддерживает автоматизацию, что определяет атрибут oleautomation. IFace3 импортирует oaidl.idl, чтобы получить определение IDispatch.

Оператор library определяет библиотеку типов ExampleLib, которая имеет свои собственные атрибуты uuid, helpstring, и version.

В определении типа библиотека, директива importlib импортирует составленный тип библиотеки. Все определения типа библиотека должны быть внесены в библиотеку базового типа, определенную в Stdole32.tlb.

Предложенное определение типа библиотека демонстрирует три различных пути включения интерфейсов. IFace3 подключается лишь путем обращения его к объявлению библиотеки.

Оператор coclass определяет совершенно новый класс компонента, BkfstComponent, который включает в себя два ранее определенных интерфейса, IFace1 и IFace2. Атрибут default определяет IFace1 как интерфейс по умолчанию.

IFace4 описывается в операторе library. Атрибут propput на MethodD показывает, что метод выполняет определенное действие со свойством с тем же именем. Атрибут propget указывает, что метод извлекает информацию из свойства с тем же именем, что и метод. Атрибут retval в MethodD обозначает выходной параметр, который содержит возвращаемое значение функции.

Компиляция MIDL

Используя IDL файл, например Example2.idl, который определяет один или несколько COM интерфейсов и типов библиотек, компилятор MIDL (Midl.exe) генерирует файлы, описанные в следующей таблице в качестве вывода по умолчанию.

Имя файла Описание
Example2.h Файл заголовка, содержащий определения типа и объявления функций для всех интерфейсов, определенных в файле IDL, а также опережающее объявление для подпрограмм, вызывающих заглушки.
Example2_p.c Прокси/заглушка файл, который включает в себя суррогатные точки входа и для клиентов, и для серверов.
Example2_i.c Файл идентификатор интерфейса, который определяет идентификатор GUID для каждого интерфейса, указанного в файле IDL.
Example2.tlb Файл, который содержит информацию о типах и объектах.
Dlldata.c Содержит данные, необходимые для создания DLL прокси/заглушки.

Вы можете использовать файл заголовка и все .c файлы для создания DLL прокси, который может поддерживать интерфейс, используемый как клиентскими приложениями, так и объектами серверов. Вы можете использовать файл заголовка интерфейса (Example2.h) и файл идентификатор интерфейса (Example2_i.c) при создании исполняемого файла для клиентского приложения, использующего интерфейс. Вы можете включить файл типа библиотека в качестве ресурса в ваш EXE или DLL, или вы можете отправить его в виде отдельного файла.

Параметры компиляции MIDL

Вы можете использовать следующие параметры командной строки, чтобы переопределить некоторые значения по умолчанию компилятора MIDL и выбрать оптимизацию подходящую для вашего приложения.

Параметр Описание
/acf Используется для явной подачи ACF файла. Этот переключатель также позволяет использовать различные имена интерфейсов в файлах IDL и ACF.
/dlldata Определяет имя файла для генерируемого файла данных DLL для DLL прокси. Имя файла по умолчанию Dlldata.c.
/env Указывает MIDL генерировать заглушки или тип библиотека для целевой среды.
/header, /h Задает имя файла заголовка интерфейса. По умолчанию именем является то, что лежит в IDL файле с расширением .h.
/iid Определяет файл идентификатор интерфейса, который перекрывает файл идентификатор интерфейса по умолчанию в интерфейсе для COM.
/lcid Обеспечивает полную поддержку DBCS, так что вы можете использовать международные символы в свои входных файлах, именах файлов и путей к каталогам.
/no_format_opt По умолчанию, чтобы уменьшить размер кода, MIDL устраняет дубликаты дескрипторов. Этот переключатель отключает эту возможность оптимизации.
/Oi, /Oic, /Oif Указывает MIDL использовать полностью интерпретированный метод маршалинга. /Oic и /Oicf коммутаторы обеспечивают дополнительные улучшения производительности.
/out Определяет каталог, в который компилятор MIDL запишет выходные файлы. Каталог вывода может быть указан буквой имени диска, абсолютным именем пути, или и тем и другим. По умолчанию, MIDL выводит файлы в текущий каталог.
/proxy Определяет имя прокси-файла интерфейса для интерфейса COM. По умолчанию используется имя файла IDL плюс "_p.c".
/tlb Определяет имя файла типа библиотека. По умолчанию используется имя файла IDL, с .tlb расширением.

Загрузка и регистрация типа библиотека

Автоматическая динамическая ссылочная библиотека, Oleaut32.dll, предоставляет несколько функций, которые можно вызывать для загрузки и регистрации файлов типа библиотека. Вызов LoadTypeLibEx, как показано в следующем примере, и загружает библиотеку, и создает записи в реестре.

Пример

ITypeLib *pTypeLib;
HRESULT hr;
hr = LoadTypeLibEx("example.tlb", REGKIND_REGISTER, &pTypeLib);
if(SUCCEEDED(hr))
{
    pTypeLib->Release();
} else {
    exit(0); // Handle errors here.
}

Сборка и регистрация прокси DLL

Если вы выбрали прокси/заглушка для маршалинга вашего приложения, то .c и .h файлы, которые MIDL сгенерирует должны быть скомпилированы и слинкованы так, чтобы создать прокси DLL, и DLL должен быть отражён в системном реестре, чтобы клиенты могли найти свои интерфейсы. MIDL-сгенерированный файл Dlldata.c содержит необходимые процедуры и другую информацию для создания и регистрации прокси/заглушка DLL.

Первым шагом в построении DLL является написание файла определения модуля для линкера, как показано в следующем примере:

LIBRARY        example.dll
DESCRIPTION    'generic proxy/stub DLL'
EXPORTS        DllGetClassObject      @1 PRIVATE
               DllCanUnloadNow        @2 PRIVATE
               DllRegisterServer      @4 PRIVATE
               DllUnregisterServer    @5 PRIVATE

В качестве альтернативы, вы можете указать эти экспортируемые функции в LINK в командной строке вашего Makefile.

Экспортируемые функции, объявленные в Rpcproxy.h, которая включает в себя Dlldata.c и реализацию по умолчанию, являются частью библиотеки RPC run-time. COM использует эти функции, чтобы создать фабрику классов, разгрузить библиотеки DLL (убедившись, что не существует ни одного объекта или lock элемента), получить информацию о прокси-DLL и самостоятельно зарегистрироваться и отменить регистрацию DLL-прокси. Чтобы воспользоваться этими функциями, вы должны ссылаться на Cpreprocessor / D (или -D) опция, когда вы компилируете файлы Dlldata.c и Example_p.c, как показано в следующем Makefile:

example.h example.tlb example_p.c example_i.c dlldata.c : example.idl
    midl example.idl
dlldata.obj : dlldata.c
    CL /c /DWIN32 /DREGISTER_PROXY_DLL dlldata.c
example.obj : example_p.c
    CL /c /DWIN32 /DREGISTER_PROXY_DLL example_p.c
iids.obj : example_i.c
PROXYSTUBOBJS = dlldata.obj example.obj iids.obj
PROXYSTUBLIBS = kernel32.lib rpcndr.lib rpcns4.lib rpcrt4.lib uuid.lib
proxy.dll : $(PROXYSTUBOBJX) example.def
    link /dll /out:proxy.dll /def:example.def
        $(PROXYSTUBOBJS) $(ORIXYSTUBLIBS)
    regsvr32 /s proxy.dll

Если вы не укажете эти определения препроцессора во время компиляции, эти функции не определятся автоматически. (То есть, макросы в Rpcproxy.c ничего не расширят) Тогда вы должны определить их явно в другом исходном файле, модуль которого будет также включен в окончательную линковку и компиляцию в командной строке компилятора C.

Когда определяется REGISTER_PROXY_DLL, Rpcproxy.h обеспечивает дополнительное условное управление компиляции с PROXY_CLSID = GUID, PROXY_CLSID_IS = явное значение GUID, и ENTRY_PREFIX = префикс строки.

Ручная регистрация прокси DLL

Если по какой-то причине вы не можете использовать прокси-процедуры по умолчанию для регистрации заглушек, вы можете вручную зарегистрировать библиотеку DLL, добавив следующие записи в системный реестр, используя Regedt32.exe.

HKEY_CLASSES_ROOT
   Interface
      iid
         (Default) = ICustomInterfaceName
         ProxyStubClsid32 = {clsid}

HKEY_CLASSES_ROOT
   CLSID
      clsid
         (Default) = ICustomInterfaceName_PSFactory
         InprocServer32 = proxstub.dll

Литература

  1. Microsoft Interface Definition Language
  2. Interface Definition (IDL) File
  3. Anatomy of an IDL File
  4. MIDL Compilation
  5. MIDL Compiler Options
  6. Loading and Registering a Type Library
  7. Building and Registering a Proxy DLL

Автор статьи: Желудков А.В.