Automatizare OLE pentru C + + programatori
Introducere
Am postat acest articol inițial în 1997. Automatizare OLE a fost fascinant pentru mine, din cauza complexității sale și a documentației impenetrabil, dar atunci când, între locurile de muncă, am primit șansa de a încerca și crack o dată și pentru tot ce am făcut descoperirea de bun venit, care, deși cu mult mai complicat decât este necesar pentru a fi, Principiile au fost relativ simplu. Tehnologia include de fapt, unele lucruri cu adevărat inteligente, în special în ceea ce privește comunicarea inter-proces.
Acum este 2012. De atunci, Microsoft C + + compilator a parcurs un drum lung, și. NET sa întâmplat. Asta. NET datorează atât de mult să se COM, într-o mare măsură, susțin principiile pe care se bazează COM.
COM, "Component Object Model", este un standard de cruce în limba dezvoltat de Microsoft pentru funcții precizând și clase furnizate de biblioteci sau de executabile care pot include o descriere a conținutului binar cunoscut ca un "Type Library". Modalitati de obiecte COM pot fi executate în cadrul limitelor de proces sau o mașină prin intermediul unui mecanism numit "OLE Automation" - deși COM termeni și "OLE Automation" sunt adesea folosite alternativ. Acest mașini remoting este oferit perfect și, adesea, invizibil de Microsoft. Principalul motiv pentru a lua un interes în zilele noastre este că acesta este un model de obiect COM care stă la baza Visual Basic for Applications, și aceasta este încă în limbajul de programare cele mai utilizate pe scară largă.
Clase de bine concepute COM ar trebui să fie foarte ușor de utilizat: este modul în care non-programatorii pot face programarea orientată pe obiecte.
Crearea claselor în C + +, deși, nu este atât de ușor. Microsoft au creat o varietate de instrumente pentru a sprijini acest proces, dar, deși "de import #" este o binefacere, cele mai multe dintre ceilalți mi se pare inutil personal: o caracteristică deosebit de enervant fiind necesitatea de a moșteni o implementare a unei clase dintr-o interfata. Acest lucru înseamnă că, pentru a lua un exemplu finanțe, vă sunt împiedicate de la moștenirea ConvertibleBond de la Bond - care v-ar dori în mod normal, să facă - pentru că fiecare are să moștenească de la clasa sa interfața corespunzătoare loc. Există mai multe moduri în jurul acest lucru, dar ele sunt complicate. Pentru mine, ierarhia de clasă ar trebui să fie cea mai potrivită pentru sarcina în parte, și nu ar trebui să fie dictată de mediul de programare.
COM obiect
Visual Basic / COM este o mulțime ca o versiune cut-jos de C + +, dar cu un strat care include verificarea automată limitele și referință numărare a preveni script VB din crashing programul. Ea are facilitate suplimentară de a fi capabil de a executa metode ca apeluri de proceduri la distanță. Spre deosebire de C + +, aceasta este construit-in și nu necesită utilaje suplimentare.
C + + declarație
ObjectType *T, *TArray[6];corespunde Dim T As ObjectType, TArray(0 To 5) As ObjectType în VB. "ObjectType" este o clasă definită de utilizator, care, în cazul VB este un obiect COM. Pe plan intern, fiecare obiect este un pointer la un obiect C + + derivat din IDispatch, o clasă abstractă, care conține două servicii principale: (i) de colectare a gunoiului și (ii) dinamic (sau "târziu") obligatorii. Să ne examina, la rândul său: (i) colectarea gunoiului Luați în considerare următoarea bucată de C + +: ObjectType *T = new ObjectType, *U; U = T; T->DoSomething(); delete T; U->DoSomething(); Desigur, acest lucru se va prăbuși, probabil, programul, și, desigur, nu ai făcut acest lucru sau echivalent pentru că îți dai seama de pericol. Bine. Nici eu Ei bine, aproape niciodată ... VB are încredere în tine mult mai putin. Atitudinea lui VB este că nu trebuie să accident programului sub nici o formă, așa că nu vă permite să ștergeți obiecte. În schimb, mediul ține evidența fiecărui obiect a fi utilizate în cadrul programului, precum și atunci când un obiect nu mai este în uz acesta devine șters automat.Paradigmă care are ștergerea obiectelor nu mai sunt în folosință controlate de mediu, mai degrabă decât programatorul este cunoscut sub numele de colectare a gunoiului. Într-un mediu de gunoi colectate-cod este mai scurtă, dar, mai important, este imposibil de a accesa un obiect șters sau este nevalid.Echivalentă VB Dim T As New ObjectType, U As ObjectType Set U = T T.DoSomething Set T = Nothing U.DoSomething la cele de mai sus nu va da o eroare, deoarece încercarea de a șterge obiectul de Setare T = Nimic nu elimină o referință, dar nu se șterge de fapt.Obiectul nu va fi ștearsă până când variabila U iese din sfera de aplicare. De colectare a gunoiului este o caracteristică a tuturor obiectelor COM și este pusă în aplicare de către toate clasele care decurg din COM IUnknown clasă abstractă: struct IUnknown { virtual HRESULT QueryInterface(const GUID * const riid, void **ppvObject) = 0; virtual unsigned long AddRef() = 0; virtual unsigned long Release() = 0; }; Ultimele două metode sunt pentru colectarea gunoiului. Interesul față de un obiect este exprimat prin crearea aceasta sau sunand la AddRef; nici un interes în continuare este exprimată prin apelarea lansării.Obiectul este responsabil pentru ștergerea sine atunci când este chemat de lansare timp una mai mult decât au existat cereri anterioare AddRef. Acest lucru este cel mai frecvent pusă în aplicare de către având un m_refs unsigned long de referință numărul de care este setat la un obiect atunci când este construit, cu AddRef și puse în aplicare de lansare, după cum urmează: unsigned long DerivedFromIUnknown::AddRef() {return m_refs++;} unsigned long DerivedFromIUnknown::Release() { if (--m_refs > 0) return m_refs; delete this; // destructor trebuie să fie declarate ca fiind virtuală pentru ca aceasta să funcționeze return 0; // Rețineți că nu trebuie să facă referire m_refs după "șterge acest" } (Valorile de returnare sunt doar pentru scopuri de depanare). Acest sistem este mult mai simplu tip de colectare a gunoiului, dar are o rezervă importantă, și unul care a contribuit la dispariția COM. Dacă obiectul A are B, obiect ca o proprietate, cu B obiect având în același timp obiect A ca o proprietate, o situație cunoscută ca o referință ciclic, atunci aceste referințe reciproce va rămâne atunci când referințele externe din program (cunoscut sub numele de "rădăcini") sunt luat. Cele mai contează individuale de referință nu ajung niciodată la zero, prin urmare, și obiectele nu se elimină, oferindu-ne o scurgere de memorie! Microsoft a face cu această problemă prin a vă spune, atunci când crearea de clase COM în VB, doar pentru a vă asigura că nu aveți referințe ciclice! Dacă veți face, atunci scurgeri ulterioare de memorie sunt în totalitate vina ta, se pare. Personal, mi se pare referințe ciclice a fi util, si resping restricție. Puteți, desigur, doar rupe manual cicluri la puncte strategice pentru a preveni scurgerile, dar o soluție mai bună este de a avea de colectare a gunoiului mai sofisticate. Microsoft clar că da:. Colectare a gunoiului NET este perfect capabil de a face cu referințe ciclice. Acest lucru ne aduce la un singur lucru, în favoarea COM peste NET:. Într-COM nu există nici o cerință de a pune în aplicare și de lansare AddRef în mod arătată aici. Sisteme mai sofisticate, care se ocupă în mod corespunzător cu trimiteri ciclice sunt posibile (ceva, într-adevăr, care a fost explorat, dacă nu chiar documentate, de autor). În. NET, pe de altă parte, un "gestionat" obiect este supusă gunoier Microsoft, care poate sau nu poate dori, și nu pot substitui dumneavoastră. Cu toate acestea, marca asincron-matura schema pe care o folosesc are dezavantaje, și anume: (i) face munca grea procesor inutil și (ii) nu se știe când un obiect, marcate ca gunoiul, va fi în cele din urmă eliminat, ceea ce descurajează un de la a face ceva substanțial într-un destructor. Prima metodă este în IUnknown pentru beneficiul COM sine. La nivel global-id-ul unic (GUID) este un identificator de șaisprezece octet care identifică obiectul COM. Numele obiectului și numele programului sau biblioteca pe care serviciile pe care le sunt în mod normal, suficient pentru a identifica obiectul, dar GUID este destinat să fie unic nu doar pentru a obiectului / bibliotecă, ci la o anumită versiune a acesteia. Ea vine în propriile sale, de exemplu, stocarea structurată. Aici un fișier, cunoscut ca un recipient, conține un obiect COM, care a încorporat în ea obiecte suplimentare COM, un cuibărit care poate continua să adâncime arbitrară. GUID de la șeful de fiecare dintre aceste fluxuri de octeți identifică care urmează. Potrivirea de GUID-urile pentru programul / biblioteca / versiune care serviciile pe care le reprezintă o funcție realizată de registry de sistem, și permite o cerere pentru a deschide și vizualiza documente OLE fără a fi nevoie să știu care sunt acestea. Acesta este mecanismul prin care, de exemplu, s-ar putea încorpora o parte a unei foi de calcul Excel într-un document Word. Pericolul de a folosi numele și nu GUID este faptul că o nouă versiune a programului ar putea numi fluxuri de fel, dar utilizați un alt format de fișier (deși, după ce a spus că, ar putea la fel de ușor uita sa oferi un GUID nou, dar să nu lui insista pe asta). QueryInterface este numit în VB atunci când trebuie să verifice faptul că obiectul este într-adevăr un obiect de automatizare OLE (de exemplu, moștenit de la IDispatch, o sub-clasă de IUnknown - care urmează să fie investigate următoare), care este atunci cand obiectul este creat prin VB CreateObject sau sa echivalentă. O punere în aplicare ar putea fi, după cum urmează: const GUID IID_DerivedFromIUnknown = {0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x0, 0x0, 0x00, 0x00, 0x00, 0x01}; (înlocui aceste numere cu un ID unic obținut din GUIDGEN.exe). HRESULT DerivedFromIUnknown::QueryInterface(const GUID * const riid, void **ppv) { if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_DerivedFromIUnknown) { *ppv = this; AddRef(); return NOERROR; } *ppv = NULL; return ResultFromScode(E_NOINTERFACE); } Pentru a verifica dacă un IUnknown este, de asemenea, un IDispatch, COM trece în GUID pentru IDispatch la această metodă după ce obiect este creat, oferindu "Run-time error '430 ': Clasa nu suportă OLE Automation" în cazul în care un răspuns negativ este dat (în mod evident, aceasta nu va fi aici). QueryInterface este util atunci când IDispatch este doar unul din multe interfețe că bunurile obiect. Pointeri la alte interfețe, puse în aplicare în legătură cu acestea, dar de obiecte separate, ar trebui să fie, de asemenea, obținută prin invocarea această metodă cu GUID-urile relevante. (ii) Dynamic Binding În COM, tipul unui obiect nu trebuie să fie cunoscută până la timpul de rulare.VB Declarația Dim T As Object identifică T ca un obiect care acceptă IUnknown (deci nu trebuie chiar să fie de tip IDispatch, deși dacă orice prelucrare se face, QueryInterface va fi chemat pe acesta pentru a obține o interfață IDispatch). Înainte de a VB a devenit conștient de biblioteci de tip, acesta a fost singurul tip de obiect definite de utilizator care a fost posibil. Acest lucru a însemnat că o declarație ca x = T.Method(param1, param2) a trebuit să fie tratate într-un mod care presupune nici o cunoaștere prealabilă a T. obiectului string "Metoda" ar fi trecut la obiect, cu întrebarea, "nu va sprijini acest lucru?", folosind metoda GetIDsOfNames IDispatch. Dacă răspunsul a fost "da" un act de identitate vor fi transmise înapoi, care ar putea fi apoi introdus în metoda Invocă cu parametrii, dacă este cazul, să execute acțiunea dorită. Pentru maximă generalitate, parametrii vor fi transmise ca o matrice de lungime variabilă a tipurilor VARIANT polimorfice, precum și metoda de Invocă ar reveni o variantă. În cazul în care metoda de returnat o valoare, apoi a fost de până la VB pentru ao constrânge în tipul de variabile i se atribuie la.Atribuirea și interogarea de "date" membri sunt cazuri speciale ale acestui. sarcinile T.Prop = value value = T.Prop care a stabilit sau pentru a obține Prop proprietate a obiectului de proprietate UPT doar de declanșare și proprietatea sa, deoarece metodele (de acces la date interna este de doar dacă aceste metode, ascunderea datelor este astfel construit în, o necesitate, în orice caz, deoarece datele pot locui în un spațiu de memorie diferit. Acest lucru face afirmații de genul List1.Left = 100 posibil, care nu numai atribuie stânga proprietatea marginea mâna a obiectului List1 la 100, dar se mișcă de control, de asemenea). Aici este definiția IDispatch: struct IDispatch : IUnknown { virtual HRESULT GetTypeInfoCount(unsigned int *pctinfo) = 0; virtual HRESULT GetTypeInfo(unsigned int itinfo, unsigned long lcid, ITypeInfo **pptinfo) = 0; virtual HRESULT GetIDsOfNames(const GUID * const riid, char *rgszNames, unsigned int cNames, unsigned long lcid, long *rgdispid) = 0; virtual HRESULT Invoke(long dispidMember, const GUID * const riid, unsigned long lcid, unsigned short wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo, unsigned int *puArgErr) = 0; }; Primele două metode oferă legătură într-o bibliotecă de tip, echivalentul unui fișier antet, care urmează să fie explorate următoare, care conține informații despre obiect (acestea sunt echivalentul a gettype. NET (), dar mult mai greu de a utiliza).Celelalte două furnizeze dinamic funcționalitatea invocarea metoda descrisă mai înainte. Acestea sunt cele mai ușor de implementat folosind DispGetIDsOfNames OLE funcții și DispInvoke care utilizează definiția obiectului în bibliotecă de tip pentru a da răspunsuri adecvate. Tip Biblioteca Biblioteca tip este mecanismul prin care VB, sau orice alt recipient, ajunge să știți despre obiecte definite de utilizator. Acesta conține informațiile de aici care face declarațiile posibile ale formularului T Dim Ca ObjectType în cazul în care ObjectType este o clasă definită în biblioteca de tip. Fișierul sursă, cu extensia IDL (Interface Description Language) este o versiune extinsă a unui fișier header C + +. Acest lucru este compilat într-un binar de tip TLB (Type Library), printr-un program numit Midl. Un fișier IDL pot fi incluse în proiect. Efectuarea VB sau VBA conștient de acest lucru, atunci este o chestiune simplă de localizarea și, inclusiv TLB din Tools / dialog Referinte. Dacă acest lucru este de succes, obiectele vor fi vizibile în Object Browser VBA. Pentru a înțelege sintaxa unui fișier IDL este necesar să se realizeze că apelurile de la distanță procedura sunt o parte esențială a COM. Obiectul poate avea reședința într-un spațiu de memorie diferite, sau chiar o altă mașină. Pointeri nu poate fi, prin urmare, a trimis. Pointeri apar doar ca parametri, în scopul de a marca în bucăți de memorie, care urmează să fie trimise prin procesul de limita, sau pentru a marca în memorie pentru primirea răspunsului. Concesiuni nu sunt făcute pentru servere COM (in proces sau servere DLL), care se află în spațiul de memorie aceeași. Cuvintele cheie în și în afara sunt aplicate la fiecare parametru din fiecare metodă, pentru a indica dacă este să fie trimis, primit, sau ambele. Există restricții privind tipurile. Matricele trebuie să fie de tip SAFEARRAY care conține informații legat. Siruri de caractere sunt de tip BSTR, care conține un număr de caractere. Gestionarea memoriei ambelor aceste tipuri trebuie să fie făcut de către COM, care prevede API-uri pentru acest scop. Un pointer la un alt obiect COM, cu toate acestea, poate fi trecut, caz în care, în cazul în care obiectul este un proces separat, COM stabilește mașini (un proxy pentru obiectul la sfârșitul client, și un tichet de la server) pentru a activați manipulare de la distanță. Mai multe detalii in anexa. O bibliotecă de tip poate include, de asemenea, siruri de caractere de ajutor și sensibile la context referințe la un fișier de ajutor. Acesta oferă, de asemenea, o alternativa la fisierele BAS pentru declarațiile funcțiilor plane DLL-uri, prin intermediul declarației modulul. Parametrii de funcții într-o secțiune modul sunt însă supuse acelorași stringencies ca metode de obiect (deși inutil ca DLL se află în același spațiu de memorie). Aici este fișierul IDL în mostra de cod: [uuid(C7E9002B-9E7F-43b5-971D-E2539E6039C2), version(1.0), helpstring ("COMDemo: Demo of COM object defined in C++")] library COMDemo { Un UUID este necesar pentru bibliotecă în ansamblul său pentru a permite OLE să-l urmărească în registrul sistemului.GUIDGEN.exe Programul generează aceste ID-uri (se presupune) la nivel global unice. Cand TLB ieșire este menționată în VB / VBA, Object Browser va afișa numele bibliotecii și de a ajuta sir dat aici pentru a ajuta la identificarea. Noi folosim această UUID pentru a obține un pointer la tipul de bibliotecă prin intermediul LoadRegTypeLib. importlib("stdole2.tlb"); Pentru a obține definițiile IDispatch, etc [uuid(2BB79939-EE89-4ae0-BF7D-E7FB175A87CF), oleautomation, dual, hidden] interface SimpleDispatch : IDispatch { /* These two methods are just to occupy two slots in the virtual function table. They will never be directly called by a container ... */ [hidden] HRESULT VirtualDestructor([in]IUnknown *stream); // spurious parameter hides function from Object Browser [hidden] IUnknown *IID_This(); // incorrect parameter hides function from Object Browser }; O secțiune interfață definește o interfață timpurie legat - în mod normal, dar nu neapărat, un C + + clasa a fi accesate prin intermediul funcției sale de masă virtuală. SimpleDispatch a fost creat aici ca o clasă de bază convenabilă pentru obiectele COM altele. Ca destructorul este declarată virtuală la acest nivel se va ocupa un spațiu în tabelul de functii virtuale, după funcțiile IDispatch, și trebuie să fie cazați.Funcția următoare este, de asemenea, unul intern pentru obținerea IID clasa. Ambele vor fi ascunse de la client. HRESULT este un cod standardizat de eroare OLE (și nu va apărea în Object Browser). Se poate ridica erori VB prin returnarea altceva decât noerror. Cu excepția cazului în "On Error" este setat, acest lucru se va întrerupe execuția programului VBA și pop-up-o cutie de mesaj. Dacă parametrul final a atribui retval, atunci aceasta este valoarea returnata de metoda, sau a proprietății lua, și apare în Object Browser ca atare. // Custom class definitions ... [uuid(7C8721D6-3D22-48a1-A945-5FF9815C5807), odl, oleautomation, dual, helpstring("Name/value pair")] interface ITestObj : SimpleDispatch { [propget, helpstring("Name of quantity")] HRESULT Name([out,retval]BSTR *); [propput] HRESULT Name([in]BSTR); [propget, id(0), helpstring("Value (default property)")] HRESULT Value([out, retval]double *); [propput, id(0)] HRESULT Value([in]double); [helpstring("square of value")] HRESULT Square([out,retval]double *square); }; Aici, în cele din urmă, este definitia unei clase COM simplu.Oleautomation atribut combinată cu derivarea (deși indirect) de la IDispatch face interfața vizibil la VB.Dublă atribut indică metodele pot fi numit fie direct, folosind mai devreme cu caracter obligatoriu, sau indirect, prin legare târzie (folosind IDispatch :: Invocă).Clasă cuprinde aici pur și simplu de numele proprietății șir, o valoare de proprietate numerică și o metodă pentru returnarea Piața pătrat de valoare. Deși există cinci funcții aici, Object Browser VBA va arăta doar două proprietăți și o metodă (și o poate face proprietati read-only prin omiterea metoda propput).Id atribut (0) face proprietatea valoarea "default" proprietate a obiectului, care (în afară de o declarație Set) este folosit atunci când numele obiectului este folosit fără calificare. [uuid(5FC711F1-B9C7-4dcc-8CCC-E39F9E0F7556)] coclass TestObj { interface ITestObj; }; }; A grupuri coclass una sau mai multe secțiuni interfață și definiții dispinterface împreună (un dispinterface este similar cu o interfață, dar numai suporta târziu obligatoriu). Există doar o singură interfață aici, dar dacă există mai mult de un interfețelor trebuie să fie reciproc accesibile prin QueryInterface. De asemenea, un coclass trebuie să fie creatable, care este de a spune ar trebui să fie capabil de a crea o instanta a obiectului cu ajutorul (în VB): Dim T As Object Set T = CreateObject("COMDemo.TestObj") sau, în cazul în care unul are o bibliotecă de tip Dim T As COMDemo.TestObj Set T = New COMDemo.TestObj sau pur și simplu Dim T As New COMDemo.TestObj Cunoștințe despre modul de a crea obiect este stocat în registrul sistemului. Pașii sunt după cum urmează:
|
Sciencespaces
© 2023 Created with the assistance of @ReuN Support Team.
All rights protected.
|
|