490 likes | 633 Views
Програмиране за .NET Framework. http:// www.nakov.com / dotnet-project /. Взаимодействие с неуправляван код. Мартин Кулов. Изпълнителен директор CodeAttest. www.codeattest.com. Необходими знания. Базови познания за общата система от типове в .NET (Common Type System)
E N D
Програмиране за .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) Platform Interop COM Windows API Windows Kernel
Архитектурата на JVM Java Platform Java приложение Java Class Libraries Java Virtual Machine (JVM) JNI COM 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 /EXPORTS C:\WINDOWS\system32\user32.dll … ordinal hint RVA name … 446 1BD 0000CBBB LoadIconA 447 1BE 000188E3 LoadIconW …
Демонстрация #1 • Използване на P/Invoke
Преобразуване на данни (marshalling) • Различни платформи – различни типове • Необходимост от преобразуване • Прави се автоматично (standard interop marshaller) • Може да заеме допълнително памет • Може да се промени стандартното действие
Преобразуване на структури • Структурите се предават по стойност • Използват се, когато неуправляван код използва предаване по стойност или чрез указател • Атрибутът StructLayout дефинира как да се преобразува структурата към неуправляван код • LayoutKind.Sequential • LayoutKind.Explicit • DLLFunc(POINT x) ManagedFunc(POINT x) • DLLFunc(POINT* x) ManagedFunc(ref POINT x)
Преобразуване на структури – пример [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);
Преобразуване на класове • Класовете се предават по адрес • Използват се при единичен или двоен указател • Атрибутът StructLayoutсе прилага и за класове DLLFunc(Job* x) ManagedFunc(Job x) DLLFunc(Job** x) ManagedFunc(ref Job x)
Преобразуване на низове • Символните низове в .NET са постоянни (immutable) • Символните низове в неуправляван код са просто адрес в паметта • Могат да имат различно кодиране • Атрибутът MarshalAs позволява правилното преобразуване • Когато символният низ е с неопределена дължина, се използва класът StringBuilder
Атрибут MarshalAs • Указва начина на преобразуване • Прилага се върху параметри, полета и резултат, когато има двусмислие • Не е задължителен • При конструиране се указва неуправляван тип, към които ще се преобразува • Полето SizeConst указва броя на знаците на подавания низ по стойност
Преобразуване – пример [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) служи за получаване на резултат от друга функция, която вие сте извикали • Неуправляваният код извиква функция от управлявания код • В управлявания код се дефинира променлива от тип delegate BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam) 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 интерфейс, например: • {645FF040-5081-101B-9F08-00AA002F954E} • Интерфейсът IDispatch • Предоставя достъп по време на изпълнение до свойствата и методите на компонента (като reflection в .NET) • In-Process и Out-of-Process COM сървъри • In-Process – DLL библиотеки, работят в адресното пространство на клиента • Out-of-Process – отделни изпълними приложения (EXE), работят в отделно адресно пространство
Взаимодействие с COM • Модели за COM компоненти STA и MTA • Single-Threaded Apartment – всяка нишка си има собствен апартамент, няма общи данни, няма нужда от синхронизация • Multithreaded Apartment – всички нишки споделят общ апартамент (общи данни), синхронизацията е ръчна • Типови библиотеки (Type Libraries) • Съдържат описание на 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
IUnknown IDispatch COM обект COM обект IErrorInfo ICustomer IUnknown IDispatch IErrorInfo IWarehouse Извикване на COM компонент чрез RCW .NET клиент RCW .NET клиент RCW
Демонстрация #3 • Използване на COM компонентата "Microsoft Web Browser"от VS.NET
Разкриване на .NET компонент като COM компонент • Атрибутът GuidAttribute задава уникалния идентификатор на COM компонента (GUID) • Атрибутът ProgId задава текстов идентификатор на COM компонента • Aсемблито се регистрира в Windows Registry чрез regasm.exe или с VS.NET [GuidAttribute("D069E57A-981F-4841-8D68-E2F2342E92A2"), ProgId("SomeApplication.SomeClass")] public class SomeClass { // … }
Разкриване на .NET компонент като COM компонент • Необходимост от типова библиотека • Генериране на типовата библиотека чрез tlbexp.exe • Използване на атрибути за контрол върху типовата библиотека • CoClassAttribute • ComVisibleAttribute • GuidAttribute • …
COM Callable Wrapper (CCW) • .NET компонентите също се достъпват от COM клиенти през специално прокси • На всеки .NET компонент отговаря точно един CCW • Стандартно преобразуване на данните • Прокси класът имплементира стандартните COM интерфейси IUnknown, IDispatch, …
Извикване на .NET компонент чрез CCW .NET обект COM клиент CCW COM клиент
Изисквания към .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
Взаимодействиес неуправляван код Въпроси?
Упражнения • Имплементирайте Windows Forms приложение, което показва списък с активните в момента процеси. За всеки процес трябва да се покаже следната информация: идентификатора му (PID), името на файла, от който е зареден, приоритета му, обема на минималната и максималната му работна памет (working set). Използвайте Windows API функциите EnumProcesses(), OpenProcess(), GetModuleBaseName(), GetPriorityClass(), GetProcessWorkingSetSize() и CloseHandle(), като ги извиквате през P/Invoke . Дефинициите са в библиотеките kernel32.dll и psapi.dll. Използвайте документацията и примерите от MSDN за да видите как се използват посочените функции. Визуализирайте по подходящ начин извлечената информация за процесите.
Упражнения • Имплементирайте 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/ • Calling Win32 DLLs in C# with P/Invoke • Migrating Native Code to the .NET CLR • Improving .NET Application Performance and Scalability – MS Patterns and Practices – http://msdn.microsoft.com/library/en-us/dnpag/html/scalenet.asp • 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=C7B955C7-231A-406C-9FA5-AD09EF3BB37F