.NET Interoperability and Unmanaged Code

Download Report

Transcript .NET Interoperability and Unmanaged Code

Програмиране за .NET Framework
http://www.nakov.com/dotnet-project/
Взаимодействие с
неуправляван код
Мартин Кулов
Изпълнителен директор
CodeAttest
www.codeattest.com
Необходими знания






Базови познания за общата система от
типове в .NET (Common Type System)
Базови познания за езика C#
Базови познания за езика C++
Базови познания за технологията COM
Базови познания за програмиране под
Win32 със C и C++
Познания за атрибутите в .NET Framework
Съдържание


Какво е взаимодействие?
Платформено извикване (P/Invoke)







Атрибут DllImport
Преобразуване на данни (marshalling)
Преобразуване на структури
Преобразуване на класове
Преобразуване на низове
Атрибут MarshalAs
Имплементиране на функция за обратно
извикване
Съдържание (2)

Взаимодействие с COM







Извикване на COM компонент от
управляван код
Runtime Callable Wrapper (RCW)
Разкриване на .NET компонент като COM
компонент
COM Callable Wrapper (CCW)
Изисквания към .NET типове за
ползване от COM
Взаимодействие със C++ (IJW)
Препоръки за използване на .NET
типове от COM
Какво е взаимодействие?



Microsoft .NET Framework е
сравнително нова технология
Съществува огромно количество
готов неуправляван код
Предимствата на управлявания код




Безопасност на типовете
Защита на паметта
Отражение на типовете
Обща среда или виртуална машина

CLR срещу JVM
Среда за контролирано
изпълнение .NET
.NET Framework
.NET приложение
.NET Framework
Class Libraries
Common Language
Runtime (CLR)
COM
Platform
Interop
Windows API
Windows Kernel
Архитектурата на JVM
Java Platform
Java приложение
Java
Class Libraries
Java Virtual
Machine (JVM)
COM
JNI
Windows API
Windows Kernel
Платформено извикване
(P/Invoke)




Подходящ за сравнително прости
Windows функции (simple flat API)
Подходящ за малък брой методи от
неуправляван код
Атрибутът DllImport служи за
извикване на неуправляван код
За удобство DLL функциите могат да
се организират в клас
Атрибут DllImport






Използва се за дефиниране на връзка с
неуправляван код
Прилага се върху методи
При конструиране се указва името на
DLL, който съдържа метода
Полето EntryPoint указва името на
извиквания метод
Полето CharSet определя как да се
кодират символните низове
Полето SetLastError дава възможност
за извличане на грешка
Как работи P/Invoke?




Намира и зарежда в паметта указания
DLL (изпълнява се само веднъж)
Зарежда адреса на функцията,
подава аргументите й и преобразува
данните
Подава контрола на неуправлявания
код
P/Invoke може да хвърли изключение
от неуправлявания код
P/Invoke – пример
static IntPtr IDI_ASTERISK = (IntPtr) 32516;
[DllImport("user32.dll", EntryPoint="LoadIconW",
ExactSpelling=true, CharSet=CharSet.Unicode)]
public static extern IntPtr LoadPredefinedIcon(
IntPtr hinst, IntPtr icon);
private void Form1_Load(object sender,
System.EventArgs e)
{
// извличаме манипулатора на системната икона
IntPtr hicon = LoadPredefinedIcon(IntPtr.Zero,
IDI_ASTERISK);
// създаваме нов обект икона от получения манипулатор
Icon icon = Icon.FromHandle(hicon);
// променяме иконата на главния прозорец
this.Icon = (Icon) icon.Clone();
}
Командата DUMPBIN




Стартира се от командния ред
Част е от MS Visual C++
Позволява разглеждането на
външните функции на даден DLL
Пример:
DUMPBIN
…
ordinal
…
446
447
…
/EXPORTS C:\WINDOWS\system32\user32.dll
hint RVA
name
1BD 0000CBBB LoadIconA
1BE 000188E3 LoadIconW
Демонстрация #1

Използване на P/Invoke
Преобразуване на данни
(marshalling)





Различни платформи – различни
типове
Необходимост от преобразуване
Прави се автоматично (standard
interop marshaller)
Може да заеме допълнително памет
Може да се промени стандартното
действие
Преобразуване – съответствия
Неуправляван
тип
Управляван
тип
HANDLE
System.IntPtr
BYTE
System.Byte
WORD
System.UInt16
DWORD
System.UInt32
FLOAT
System.Single
LPSTR, LPCSTR,
LPWSTR, LPCWSTR
System.String или
System.StringBuilder
Преобразуване на структури


Структурите се предават по стойност
Използват се, когато неуправляван
код използва предаване по стойност
или чрез указател
DLLFunc(POINT x)  ManagedFunc(POINT x)
DLLFunc(POINT* x)  ManagedFunc(ref POINT
x)

Атрибутът StructLayout дефинира
как да се преобразува структурата
към неуправляван код


LayoutKind.Sequential
LayoutKind.Explicit
Преобразуване на структури
– пример
[StructLayout(LayoutKind.Explicit)]
public struct SYSTEM_INFO
{
[FieldOffset(0)] public UInt16 ProcessorArchitecture;
[FieldOffset(4)] public UInt32 PageSize;
[FieldOffset(20)] public UInt32 NumberOfProcessors;
[FieldOffset(24)] public UInt32 ProcessorType;
[FieldOffset(28)] public UInt32 AllocationGranularity;
[FieldOffset(30)] public UInt16 ProcessorLevel;
}
[DllImport("kernel32.dll",
EntryPoint="GetNativeSystemInfo")]
public static extern void GetSysInfo(
ref SYSTEM_INFO sysinfo);
Преобразуване на класове


Класовете се предават по адрес
Използват се при единичен или
двоен указател
DLLFunc(Job* x)  ManagedFunc(Job x)
DLLFunc(Job** x)  ManagedFunc(ref Job x)

Атрибутът StructLayout се прилага
и за класове
Преобразуване на низове





Символните низове в .NET са
постоянни (immutable)
Символните низове в неуправляван
код са просто адрес в паметта
Могат да имат различно кодиране
Атрибутът MarshalAs позволява
правилното преобразуване
Когато символният низ е с
неопределена дължина, се използва
класът StringBuilder
Атрибут MarshalAs





Указва начина на преобразуване
Прилага се върху параметри, полета
и резултат, когато има двусмислие
Не е задължителен
При конструиране се указва
неуправляван тип, към които ще се
преобразува
Полето SizeConst указва броя на
знаците на подавания низ по
стойност
Преобразуване – пример
Неуправляван
C++
struct STOCK
{
TCHAR ID[32];
char*
C#
[StructLayout(LayoutKind.Sequential,
CharSet=CharSet.Auto)]
struct STOCK
{
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst=32)]
public String ID;
Name;
[MarshalAs(UnmanagedType.LPStr)]
public String Name;
WCHAR* Location;
}
[MarshalAs(UnmanagedType.LPWStr)]
public String Location;
}
Преобразуване – пример
[DllImport("kernel32.dll", EntryPoint="GetModuleFileName",
ExactSpelling=false, CharSet=CharSet.Auto)]
public static extern UInt32 GetModuleFileName(IntPtr
hModule, StringBuilder lpFileName, UInt32 nSize);
[STAThread]
static void Main(string[] args)
{
StringBuilder moduleName = new StringBuilder(10);
UInt32 uiSize = GetModuleFileName(IntPtr.Zero,
moduleName, (uint) moduleName.Capacity + 1);
moduleName.Length = (int) uiSize;
Console.WriteLine("Executable path: {0}",
moduleName.ToString());
}
Имплементиране на функция
за обратно извикване


Функцията за обратно извикване (callback)
служи за получаване на резултат от друга
функция, която вие сте извикали
Неуправляваният код извиква функция от
управлявания код
BOOL EnumWindows(WNDENUMPROC lpEnumFunc,
LPARAM lParam)

В управлявания код се дефинира
променлива от тип delegate
public delegate bool CallBack(int hWnd,
int lParam);
Демонстрация #2

Преобразуване на данни
Взаимодействие с COM

Какво е COM?



COM = Common Object Model – компонентен
модел, използван широко при Win32
приложения
COM осигурява взаимодействие между Win32
приложения посредством компоненти
Интерфейсът IUnknown



Имплементира се задължително от всички
COM компоненти
AddRef, Release – отброяват референциите
към компонента (reference counting)
QueryInterface – открива интерфейсите,
които компонентът поддържа (като reflection)
Взаимодействие с COM

GUID – 128-битово число, което уникално
идентифицира COM интерфейс, например:


Интерфейсът IDispatch


{645FF040-5081-101B-9F08-00AA002F954E}
Предоставя достъп по време на изпълнение
до свойствата и методите на компонента
(като reflection в .NET)
In-Process и Out-of-Process COM сървъри


In-Process – DLL библиотеки, работят в
адресното пространство на клиента
Out-of-Process – отделни изпълними
приложения (EXE), работят в отделно
адресно пространство
Взаимодействие с COM

Модели за COM компоненти STA и MTA



Типови библиотеки (Type Libraries)




Single-Threaded Apartment – всяка нишка си
има собствен апартамент, няма общи
данни, няма нужда от синхронизация
Multithreaded Apartment – всички нишки
споделят общ апартамент (общи данни),
синхронизацията е ръчна
Съдържат описание на COM компоненти
(като метаданните в асемблитата)
Представляват бинарни (.tlb) файлове
Описват типове, класове, структури и др.
ActiveX – разширение на COM модела,
което позволява работа в мрежова среда
Извикване на COM компонент от
управляван код





Необходимост от Interop асембли
Генериране на Interop асембли чрез
Visual Studio .NET
Генериране на Interop асембли чрез
tlbimp.exe
Програмно и нестандартно
генериране на Interop асембли
Разгръщане (deployment) на Interop
асембли
Runtime Callable Wrapper (RCW)




COM обектите се достъпват през
прокси наречено RCW
На всеки COM обект отговаря точно
един RCW
Позволява стандартно
преобразуване на данните
Достъпва стандартните COM
интерфейси IDispatch, IErrorInfo,
IUnknown
Извикване на COM компонент
чрез RCW
IUnknown
IDispatch
COM обект
IErrorInfo
ICustomer
RCW
.NET клиент
IUnknown
.NET клиент
IDispatch
COM обект
IErrorInfo
IWarehouse
RCW
Демонстрация #3

Използване на COM компонентата
"Microsoft Web Browser" от VS.NET
Разкриване на .NET компонент
като COM компонент
Атрибутът GuidAttribute задава
уникалния идентификатор на COM
компонента (GUID)
Атрибутът ProgId задава текстов
идентификатор на COM компонента


[GuidAttribute("D069E57A-981F-4841-8D68-E2F2342E92A2"),
ProgId("SomeApplication.SomeClass")]
public class SomeClass
{
// …
}

Aсемблито се регистрира в Windows
Registry чрез regasm.exe или с VS.NET
Разкриване на .NET компонент
като COM компонент



Необходимост от типова библиотека
Генериране на типовата библиотека
чрез tlbexp.exe
Използване на атрибути за контрол
върху типовата библиотека




CoClassAttribute
ComVisibleAttribute
GuidAttribute
…
COM Callable Wrapper (CCW)




.NET компонентите също се
достъпват от COM клиенти през
специално прокси
На всеки .NET компонент отговаря
точно един CCW
Стандартно преобразуване на
данните
Прокси класът имплементира
стандартните COM интерфейси
IUnknown, IDispatch, …
Извикване на .NET компонент
чрез CCW
COM клиент
COM клиент
CCW
.NET обект
Изисквания към .NET типове за
ползване от COM






Управляваните типове трябва да са
public
Методи, свойства, полета и събития
трябва да са public
Типовете трябва да имат публичен
конструктор по подразбиране
Типовете не могат да са абстрактни
Препоръчва се класовете да
имплементират интерфейс
Избягвайте статични методи
Демонстрация #4

Извикване на .NET компонент чрез
CCW
Взаимодействие със C++ (IJW)



C++ позволява директно извикване
на неуправляван код (It Just Works)
Забавянето от IJW прокси
имплементацията е малко (10-30 x86
инструкции за всяко извикване)
Изрично преобразуване на данните



Не се ползват атрибути
Позволява да се забележат по-лесно
проблеми с производителността
Подходящ е за приложения, които
ползват предимно неуправляван код
IJW извикване от C++ – пример
#using <mscorlib.dll>
#include <stdio.h>
#include <iostream>
using namespace std;
void main()
{
// Declare unmanaged pointer of type char*
const char* str = "IJW (It Just Works)";
// Call unmanaged function "printf"
printf("%s\n", str);
// Call unmanaged function "ostream::operator <<"
cout << str << endl;
// Call managed function "Console::WriteLine"
System::Console::WriteLine(str);
}
Препоръки за използване на
.NET типове от COM





Използвайте “chunky” вместо
“chatty” интерфейси
Имплементирайте IDisposable за
неуправляваните ресурси
Избягвайте късно свързване
Указвайте името на метода който
искате да извикате изрично
Оптимизирайте преобразуването на
данни
Препоръки за използване на
.NET типове от COM


Може да използвате
SuppressUnmanagedCode атрибута за
критични по скорост извиквания
Следете броячите за взаимодействие




Брояч на CCW
Брояч на преобразуванията
(marshallings)
Брояч на “корените” (stubs)
Използвайте CLR Spy за да откриете
евентуални проблеми
Демонстрация #5

Използване на броячи и CLR Spy
Взаимодействие с
неуправляван код
Въпроси?
Упражнения
1.
Имплементирайте Windows Forms приложение,
което показва списък с активните в момента
процеси. За всеки процес трябва да се покаже
следната информация: идентификатора му (PID),
името на файла, от който е зареден, приоритета му,
обема на минималната и максималната му работна
памет (working set). Използвайте Windows API
функциите EnumProcesses(), OpenProcess(),
GetModuleBaseName(), GetPriorityClass(),
GetProcessWorkingSetSize() и CloseHandle(),
като ги извиквате през P/Invoke . Дефинициите са в
библиотеките kernel32.dll и psapi.dll.
Използвайте документацията и примерите от MSDN
за да видите как се използват посочените функции.
Визуализирайте по подходящ начин извлечената
информация за процесите.
Упражнения
2.
3.
4.
Имплементирайте Windows Forms приложение,
което визуализира PDF документи с помощта на
COM компонента "Adobe Acrobat Control for
ActiveX".
Създайте Windows Forms контрола, която
реализира играта "морски шах". Направете
контролата достъпна като COM сървър. Направете
HTML страница, с която да визуализирате
контролата в Internet Explorer.
Реализирайте конзолно приложение, което по даден
XML файл, съдържащ списък от фирми и
информация за тях, генерира MS Excel документ,
съдържащ същата информация във вид на
таблица. Всяка фирма се описва с име, адрес и
телефон. За връзка с MS Excel използвайте COM
компонентата "Microsoft Office Spreadsheet".
Използвана литература

MSDN Library – http://msdn.microsoft.com








Interoperating with Unmanaged Code
An Overview of Managed/Unmanaged Code
Interoperability
Beyond (COM) Add Reference: Has Anyone Seen
the Bridge?
Using the .NET Framework SDK Interoperability
Tools
Calling a .NET Component from a COM
Component
Microsoft Office and .NET Interoperability
The Myth of .NET Purity, Reloaded
Platform Invocation Services
Използвана литература

MSDN Magazine –
http://msdn.microsoft.com/msdnmag/



Improving .NET Application Performance and
Scalability – MS Patterns and Practices –
http://msdn.microsoft.com/library/enus/dnpag/html/scalenet.asp



Calling Win32 DLLs in C# with P/Invoke
Migrating Native Code to the .NET CLR
Chapter 7 – Improving Interop Performance
Checklist: Interop Performance
P/Invoke .NET: The Interop wiki! –
http://www.pinvoke.net/
Използвана литература


Microsoft .NET/COM Migration and Interoperability –
http://msdn.microsoft.com/library/default.asp?url=
/library/en-us/dnbda/html/cominterop.asp
CLR Spy – http://www.gotdotnet.com/Community/
UserSamples/Details.aspx?SampleGuid=C7B955C7231A-406C-9FA5-AD09EF3BB37F