12
Component Object Model (COM) permite comunicarea interprocese si crearea dinamica de obiecte in orice limbaj de programare care suporta aceasta tehnologie. In esenta COM este o modalitate independenta de limbaj de implementare a unor obiecte astfel incat ele sa poata fi refolosite in medii diferite de cele in care au fost scrise. COM permite reutilizarea obiectelor fara a sti ce contin acestea.
In .NET framework un obiect COM trebuie sa fie inregistrat inainte ca el sa fie folosit. Inregistrarea se face ori folosin Visual Studio 2005, ori utilitarul Type Library Importer (TlbImp.exe). Sa prespupunem ca avem obiectul COM numit matlabGrafic.dll. Intai trebuie inregistrat in registy: Regsvr32 matlabGrafic.dll.
Deschideti Visual Studio 2005 command prompt. Navigati la DLL-ul pe care doriti sa il importati. Scrieti tlbimp .dll. Acest apel va crea un assembly .NET cu numele .dll. Daca doriti sa schimbati numele puteti folosii apelul> tlbimp .dll /out: .dll In continuare importati noul assembly ca o componenta .NET (Add Reference …)
TlbImp.exe> importa obiecte COM TlbExp.exe> creaza obiecte COM din assemblyuri .NET Regedit.exe Ildasm.exe> Interlediate Language Dissasambler. Regasm.exe> Assambly Registration Tool
Sa presupunem ca am adauga la referinte un obiect COM numit: Adobe Acrobat Reader 7.0 Document Browser si l-am instantiat. Folosirea lui e foarte simpla : axAcroPDF1.LoadFile(“fisiere.pdf”); axAcroPDF1.Print();
Exceptiile aruncate in interiorul componentelor COM pot fi prinse in exterior drept excepii RuntimeWrapperException. RuntimeWrapperException mosteneste System.Exception si adauga proprietatea WrappedException. Atunci cand este aruncata o exceptie in obiectum COM, .NET framework va arunca o exceptie proprie, care va avea in proprietatea WrappedException exceptia aruncata in COM.
Componentele COM nu suporta
Obiecte statice sau shared. Parametrii la constructor Supraincarcare Mostenirea devine inutila in unele cazuri Portabilitate (sistemele de operare fara registry nu pot sa foloseasca obiecte COM).
OBS: Parametrii nu pot fii transmisi decat prin referinta Atunci cand un parametru nu are nici o valoare va fi transmis Type.Missing
Pentru a crea o componenta compatibila COM puteti urma urmatorii pasi:
Pas1> Creati un .NET class library. Pas2> Deschideti dialog-ul Project Properties. Pas3> Dati click pe tab-ul Build. Pas4> Selectati optiunea Register For COM Interop Pas5> Build la assembly
Puteti ascunde metode din .NET class library COM-ului folosind atributul ComVisible.
Pentru a va asigura compatiblitatea cu COM e bine sa va asigurati ca urmatoarele cerinte sunt indeplinite:
Toate clasele trebuie sa aiba un constructor fara parametrii Toate tipurile expuse COM trebuie sa fie publice Toti membrii expusi COM trebuie sa fie publici Clasele abstracte nu vor putea fi consumate
Dupa ce va asigurati ca respectati aceste criterii puteti construii componenta COM ori folosin VS2005 ori utilitarul TlbExp.
Pasi pentru deployment: Compilare: Csc /t:library ClasaVisibilaCom.cs
Creare fisier TLB: Tlbexp ClasaVisibilaCom.dll /out: ClasaVisibilaComLib.dll
Creati un script de resurse ClasaVisibilaCom.res in care adaugati IDR_TYPELIB1 typelib “ClasaVisibilaComLib.tlb” Recompilati assembly-ul cu noul script resursa adaugat: Csc /t:library ClasaVisibilaCom.cs /win32res: ClasaVisibilaCom.res
.NET framework are posibilitatea de a folosii functii din DLL-uri unmanaged (deci nici componenta COM, nici .NET). Puteti astfel sa scrieti o functie in C++/Assembler si sa o folositi foarte simplu in o aplicatie .NET. Un posibil avantaj este viteza. Unii algoritmi se pot executa mult mai repede in cod unmanged decat in cod managed. Dezavantaje ar fi durata mare a operatie de conversie din tipuri managed in tipuri unamanged si posibilitatea aparitiei memory leak-urilor.
Cu Platform Invoke (P/Invoke) puteti apela metode din dll-uril unamanged. Pentru a putea folosii p/invoke trebuie sa: Creati o metoda statica external cu numele functiei pe care doriti sa o chemati Decorati cu atributul DllImport, unde speicificati ca parametru numele dll-ului din care importati functia Apleati acea metoda in cod
In general e bine sa construiti pentru DLL-urile pe care le folositi clase care sa incapsuleze functiile acestora, clase wrapper. Avantajele unei clase wrapper ar fi: Cei ce folosesc clasa nu vad nici o diferenta intre apelul de cod managed si apelul de cod unmanaged. Dezvoltatorii nu trebuie sa isi mai aminteasca numele si detaliile din codul unmanaged. Mai putine erori. Apelurile la P/Invoke sunt foarte sensibibile la erori in parametrii deci e bine sa faceti conversia o singura data bine si apoi sa refolositi.
Multe conversii de tipuri de date din managed in unmanaged se fac automat de catre runtime (ex: int, char, char*). Atunci cand conversia se poate face in mai multe tipuri se poate folosii atributul MarshalAs: Poate fi aplicat unei proprietati sau unui parametru, sau in apelul de metode Primeste un singur parametru: tipul de date din care se converteste. Acesta este un membru al enumerarii UnmanagedType.
Codul unmanaged in general se asteapta ca o structura primita ca parametru sa aiba membrii arantajati in o anumita ordine. In .NET framework runtime-ul poate optimiza aranjarea membrilor unei structuri astfel incat aranjarea membrilor in cod poate sa difere de cea ce ar fi trimisa la un apel de functie unmanaged. Modalitatea prin care specificam modul de aranjare al membrilor unei structuri este atributul StructLayout.
typedef struct tagLOGFONT { LONG lfHeight; LONG lfWidth; LONG lfEscapement; LONG lfOrientation; LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;
[StructLayout(LayoutKind.Sequential)] public class LOGFONT { public const int LF_FACESIZE = 32; public int lfHeight; public int lfWidth; public int lfEscapement; public int lfOrientation; public int lfWeight; public byte lfItalic; public byte lfUnderline; public byte lfStrikeOut; public byte lfCharSet; public byte lfOutPrecision; public byte lfClipPrecision; public byte lfQuality; public byte lfPitchAndFamily; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)] public string lfFaceName; }
Cea mai importanta proprietate din StructLayout este LayoutKind. Aceasta se poate specifica in constructor si este egala cu un membru al enumerarii LayoutKind: LayoutKind.Auto: Runtime-ul va aranja membrii structurii asa cum doreste. LayoutKind.Sequential: Runtime-ul va mentime membrii structurii in ordinea specificata de dezvoltator LayoutKind.Explicit: Runtime-ul va mentine membrii structurii in ordinea specificata de dezvoltaror folosind offset-uri de memorie
LayoutKind.Explicit presupune specificarea offsetului in bytes pentru fiecare membru al structurii
In codul unmanaged functiile callback sunt implementate cu ajutorul pointerilor la functie. In codul managed functiile callback sunt implementate cu ajutor delegatilor. Pentru a trimite un callback la cod unmanaged trebuie deci creat un delegat cu prototipul pointerului la functie.
Performanta:Overhead-ul produs de conversia tipurilor managed in unmanaged si invers poate scadea performanta aplicatiei. Pot sa apara si memory leak-uri. Type safety: Codul unmanaged nu este typesafe. Securitatea codului: In codul unmanaged nu exista securitate declarativa sau imperativa si o aplicatie care foloseste cod unmanaged s-ar putea sa nu functioneze in un mediu partially trusted. Versionarea: Cand scrieri o aplicatie managed puteti instala mai multe versiuni ale aceleasi componente iar aplicatia poate alege intre ele. Urmatoarele instalare ale acelelei aplicatii sau componente nu vor afecta compotamentul deja existent. Aplicatiile scrise in cod unmanaged nu au aceasta capabilitate.