| Pakan: Catatan Monopoli Judul: Catatan Studi tentang "Prinsip dan Aplikasi Com" | Penulis: FGS_FGS Komentar |
| Catatan Studi "Prinsip dan Aplikasi" - Bagian 1 COM Prinsip http://savetime.delphibs.com Waktu mulai: 2004.1.30 Terakhir dimodifikasi: 2004.2.1 Format artikel ini adalah: Teks secara otomatis dibungkus oleh jendela; (Isi artikel ini pada dasarnya dikutip dari buku "Com Principles and Applications". Hak cipta dimiliki oleh penulis Pan Aimin. Tolong jangan menggunakannya di media publik) Daftar isi ==================================================== =================== =================================== ========= ⊙ Tinjauan Bab 1 Apa itu com Com objek dan antarmuka Model proses com Com reusability ⊙ Bab 2 Model Objek COM GUID IDENTIFIER yang unik secara global Com objek Antarmuka com Antarmuka Deskripsi Bahasa IDL Antarmuka iunknown Prinsip Antarmuka untuk Objek COM ⊙ Bab 3 Implementasi COM Informasi pendaftaran komponen com Daftarkan komponen com Fungsi Pabrik Kelas dan DllgetObjectClass Fungsi CogetClassObject Fungsi CocreateInstance / CocreateInstanceex Inisialisasi Perpustakaan COM Manajemen Memori Perpustakaan Com Program Komponen Memuat dan Menghapus Instalasi Fungsi Umum untuk Perpustakaan Com Jenis HRESULT ⊙ Fitur Com Bab 4 Reusability: Inklusi dan Agregasi Proses transparansi (untuk dipelajari) Keamanan (untuk dipelajari) Fitur multithreading (untuk dipelajari) ⊙ Bab 5 Mengembangkan Aplikasi COM dengan Visual C ++ Deskripsi beberapa file header yang disediakan oleh Win32 SDK Beberapa makro yang terkait dengan antarmuka com ==================================================== =================== =================================== ========= teks ==================================================== =================== =================================== ========= ⊙ Tinjauan Bab 1 ==================================================== =================== =================================== ========= Apa itu com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM adalah standar komponen yang diusulkan oleh Microsoft. Dalam standar COM, program komponen juga disebut modul. Program komponen dapat berisi satu atau lebih objek komponen. ) adalah pembawa kode yang menyediakan objek COM. Objek COM berbeda dari konsep objek dalam bahasa yang berorientasi objek umum (seperti C ++). Objek com bersifat independen bahasa. Fitur ini dimungkinkan untuk berinteraksi dengan objek komponen yang dikembangkan oleh berbagai bahasa pemrograman. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Com objek dan antarmuka -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Mirip dengan konsep objek dalam C ++, suatu objek adalah contoh kelas; dan kelas adalah definisi yang menggabungkan satu set data dan fungsi terkait. Aplikasi yang menggunakan objek (atau objek lain) disebut pelanggan, kadang -kadang juga disebut pengguna objek. Antarmuka adalah seperangkat fungsi yang terkait secara logis, dan fungsinya juga disebut fungsi anggota antarmuka. Menurut kustom, nama antarmuka sering diawali dengan "i". Objek memberi pelanggan berbagai bentuk layanan melalui fungsi anggota antarmuka. Dalam model COM, objek itu sendiri tidak terlihat oleh pelanggan, dan ketika pelanggan meminta layanan, itu hanya dapat dilakukan melalui antarmuka. Setiap antarmuka diidentifikasi oleh pengidentifikasi unik (GUID) 128-bit secara global (GUID). Klien memperoleh penunjuk antarmuka melalui GUID, dan kemudian melewati penunjuk antarmuka, dan klien dapat memanggil fungsi anggota yang sesuai. Mirip dengan antarmuka, setiap komponen juga diidentifikasi oleh pedoman 128-bit, yang disebut CLSID (pengidentifikasi kelas, pengidentifikasi kelas atau ID kelas). Menggunakan CLSID untuk mengidentifikasi objek dapat memastikan (mungkin) keunikan pada skala global. Bahkan, setelah klien berhasil membuat objek, ia mendapatkan pointer ke antarmuka objek. itu. Menurut spesifikasi COM, jika objek COM mengimplementasikan beberapa antarmuka, antarmuka objek lainnya dapat diperoleh dari antarmuka tertentu. Dari proses ini, kita juga dapat melihat bahwa klien hanya berurusan dengan objek COM melalui antarmuka, dan objek hanyalah seperangkat antarmuka untuk klien. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Model proses com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Objek komponen layanan yang disediakan oleh COM memiliki dua model proses saat menerapkan: objek dalam proses dan objek di luar proses. Jika itu adalah objek dalam proses, itu berjalan di ruang proses klien; jika itu adalah objek di luar proses, ia berjalan di ruang proses lain pada mesin yang sama atau di ruang mesin jarak jauh. Program Layanan dalam Proses: Program layanan dimuat ke dalam ruang proses pelanggan. Program Layanan Lokal: Program layanan berjalan pada mesin yang sama dengan program klien. Program Layanan Jarak Jauh: Program layanan berjalan pada mesin yang berbeda dari klien, dan dapat berupa modul DLL atau file EXE. Jika program layanan jarak jauh diimplementasikan dalam formulir DLL, mesin jarak jauh membuat proses proxy. Meskipun objek COM memiliki model proses yang berbeda, perbedaan ini transparan untuk program klien. Namun, saat mengimplementasikan objek COM, Anda harus tetap dengan hati -hati memilih model proses. Keuntungan dari model dalam proses adalah bahwa mereka efisien, tetapi komponen yang tidak stabil akan menyebabkan proses klien macet, sehingga komponen dapat membahayakan klien; Model-dari proses juga akan memiliki masalah, yang mungkin karena komponen dan pelanggan dalam prosesnya berada di ruang alamat yang sama, ada kemungkinan konflik yang tinggi)? Stabilitas, dan proses komponen tidak akan membahayakan program klien. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Com reusability -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Karena standar COM didasarkan pada tingkat kode biner, reusability objek COM berbeda dari proses penggunaan kembali objek dalam bahasa yang berorientasi objek umum seperti C ++. Untuk program klien dari objek COM, hanya menggunakan layanan yang disediakan oleh objek melalui antarmuka, dan tidak tahu proses implementasi di dalam objek. Daripada implementasi spesifik. Com menggunakan dua mekanisme untuk mewujudkan penggunaan kembali objek. Kami berasumsi bahwa ada dua objek COM, dan Object 1 berharap untuk menggunakan kembali fungsi objek 2. Kami memanggil Objek 1 Objek Eksternal dan Objek 2 Objek Internal. (1) Cara inklusif. Objek 1 berisi objek 2. Ketika Objek 1 perlu menggunakan fungsi objek 2, itu dapat dengan mudah menyerahkan implementasi ke Objek 2. Meskipun Objek 1 dan Objek 2 mendukung antarmuka yang sama, Objek 1 adalah ketika mengimplementasikan antarmuka , implementasi objek 2 dipanggil. (2) Metode agregasi. Objek 1 Cukup mengirimkan antarmuka objek 2 ke klien. ada. ==================================================== =================== =================================== ========= ⊙ Bab 2 Model Objek COM ==================================================== =================== =================================== ========= GUID IDENTIFIER yang unik secara global -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Spesifikasi COM menggunakan pedoman pengidentifikasi unik 128-bit secara global untuk mengidentifikasi objek dan antarmuka, yang merupakan angka acak dan tidak memerlukan lembaga khusus untuk mengalokasikan dan mengelola. Karena Guid adalah angka acak, keunikan tidak sepenuhnya dijamin, tetapi kemungkinan pengidentifikasi yang digandakan sangat kecil. Secara teori, jika sebuah mesin menghasilkan 100.000.000 guid per detik, dapat dijamin bahwa 3240 tahun (dalam arti probabilitas) tidak akan diulang. Guid dapat dijelaskan dalam C/C ++ menggunakan struktur ini: typedef struct _guid { Data DWORD1; Data kata2; Data kata3; Data byte4 [8]; } GUID; Contoh: {64BF4372-1007-B0AA-444553540000} Anda dapat mendefinisikan GUID sebagai berikut: Extern "C" Const Guid CLSID_MYSPELLCHECKER = {0x54bf0093, 0x1048, 0x399d, {0xb0, 0xa3, 0x45, 0x33, 0x43, 0x90, 0x47, 0x47}}; Visual C ++ menyediakan dua program untuk menghasilkan GUID: uuidgen.exe (baris perintah) dan guidgen.exe (dialog). Perpustakaan COM menyediakan fungsi API berikut yang dapat menghasilkan GUID: HResult Cocreateguid (Guid *pGuid); Jika GUID berhasil, fungsi mengembalikan poin S_OK dan PGUID ke nilai GUID yang dihasilkan. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Com objek -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Dalam spesifikasi COM, objek COM tidak didefinisikan secara ketat, tetapi COM menyediakan model komponen yang berorientasi objek, dan komponen COM memberi pelanggan entitas yang dienkapsulasi dalam bentuk objek. Entitas di mana program klien berinteraksi dengan program COM adalah objek COM. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Antarmuka com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Secara teknis, antarmuka adalah struktur data yang berisi serangkaian fungsi, melalui kode klien yang dapat memanggil fungsi objek komponen. Antarmuka mendefinisikan satu set fungsi anggota, yang merupakan semua informasi yang diekspos oleh objek komponen. Biasanya kami menyebut tabel fungsi fungsi antarmuka tabel fungsi virtual (vtable), dan pointer ke vTable adalah pvtable. Untuk suatu antarmuka, tabel fungsi virtualnya ditentukan, sehingga jumlah fungsi anggota antarmuka tidak berubah, dan urutan fungsi anggota juga tidak berubah; Dalam definisi antarmuka, semua informasi ini harus ditentukan pada tingkat biner. Pointer Antarmuka ----> PvTable ----> Fungsi Pointer 1-> | ----------- | Fungsi Pointer M_Data1 2 -> | M_Data2 Pointer Function 3-> | ------------ | Parameter pertama dari setiap fungsi anggota antarmuka adalah pointer ke instance objek (= ini). Informasi, ketika dipanggil, antarmuka dapat mengetahui objek COM mana yang beroperasi. Dalam fungsi anggota antarmuka, variabel string harus menggunakan pointer karakter unicode. Oleh karena itu, jika karakter ANSI digunakan di dalam program komponen, dua ekspresi karakter harus dikonversi. Tentu saja, dalam hal membangun program komponen dan program klien, Anda dapat menggunakan jenis parameter yang Anda tentukan diri Anda selama mereka kompatibel dengan jenis parameter yang dapat dikenali COM. Visual C ++ menyediakan dua konversi string: namespace _com_util { Bstr convertStringTobstr (const char *psrc) throw (_com_error); Bstr convertBStrtoString (bstrc psrc) lempar (_com_error); } BSTR adalah string lebar-byte-byte, yang merupakan tipe data otomatis yang paling umum digunakan. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Antarmuka Deskripsi Bahasa IDL -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Spesifikasi COM menggunakan spesifikasi DCE OSF untuk menggambarkan antarmuka panggilan jarak jauh IDL (Bahasa Deskripsi Antarmuka) dan memperluas untuk membentuk bahasa deskripsi antarmuka COM. Antarmuka Deskripsi Bahasa menyediakan metode deskripsi untuk antarmuka yang tidak bergantung pada bahasa apa pun, sehingga dapat menjadi bahasa umum antara program komponen dan program klien. Bahasa Deskripsi Antarmuka IDL yang digunakan oleh spesifikasi COM tidak hanya dapat digunakan untuk mendefinisikan antarmuka COM, tetapi juga menentukan beberapa tipe data yang umum digunakan dan struktur data khusus. Fitur, bahkan mendukung deskripsi array panjang variabel. IDL mendukung jenis pointer, yang sangat mirip dengan C/C ++. Misalnya: Idiksi Antarmuka { HResult initialize () HResult loadLibrary ([in] string); HResult insertword ([dalam] string, [dalam] string); HResult deleteWord ([dalam] string); HResult lookupword ([dalam] string, [out] string *); HResult restorelibrary ([in] string); HResult freelibrary (); } Microsoft Visual C ++ menyediakan alat midl yang dapat mengkompilasi antarmuka IDL Deskripsi file ke dalam C/C ++-Antarmuka yang kompatibel deskripsi file header (.h). -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Antarmuka iunknown -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Definisi IDL iunknown: Antarmuka Iunknown { HResult queryInterface ([dalam] refiid iid, [out] batal ** ppv); Ulong Addref (void); Pelepasan ulong (batal); } Definisi C ++ Iunkown: Kelas iunknown { virus hResult _stdcall queryInterface (const iid & iid, void ** ppv) = 0; virtual ulong _stdcall addref () = 0; virus ulong _stdcall rilis () = 0; } -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Prinsip Antarmuka untuk Objek COM -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Spesifikasi COM menetapkan aturan berikut untuk fungsi QueryInterface: 1. Untuk pointer antarmuka yang berbeda dari objek yang sama, antarmuka yang dikenakan oleh query harus persis sama. Artinya, penunjuk antarmuka yang dikenakan iunknown untuk setiap objek adalah unik. Oleh karena itu, untuk dua pointer antarmuka, kita dapat menentukan apakah mereka menunjuk ke objek yang sama dengan menilai apakah antarmuka yang mereka ajakan sama. 2. Refleksivitas antarmuka. Meminta antarmuka itu sendiri harus selalu berhasil, misalnya: pidictionary-> queryInterface (iid_dictionary, ...) harus mengembalikan s_ok. 3. Simetri Antarmuka. Jika Anda meminta dari satu penunjuk antarmuka ke penunjuk antarmuka lain, lalu kembali dari penunjuk antarmuka kedua ke penunjuk antarmuka pertama harus berhasil, misalnya: pidictionary-> queryInterface (iid_spellcheck, (void **) & pispellcheck); Jika pencarian berhasil, maka periksa kembali antarmuka IID_Dictionary dari PispellCheck pasti akan berhasil. 4. Transitivitas Antarmuka. Jika Anda menanyakan penunjuk antarmuka kedua dari penunjuk antarmuka pertama dan penunjuk antarmuka ketiga dapat diminta dari penunjuk antarmuka kedua, maka Anda pasti dapat meminta penunjuk antarmuka pertama dari penunjuk antarmuka ketiga. 5. Antarmuka waktu kueri tidak relevan. Jika penunjuk antarmuka tertentu dapat ditemukan pada saat tertentu, maka penunjuk antarmuka yang sama akan diperiksa kapan saja di masa depan, dan kueri pasti akan berhasil. Singkatnya, tidak peduli antarmuka dari mana kita mulai, kita selalu dapat mencapai antarmuka apa pun, dan kita selalu dapat kembali ke antarmuka asli. ==================================================== =================== =================================== ========= ⊙ Bab 3 Implementasi COM ==================================================== =================== =================================== ========= Informasi pendaftaran komponen com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Informasi tentang semua komponen pada mesin saat ini hkey_class_root/clsid Komponen in-process HKEY_CLASS_ROOT/CLSID/GUID/INPROCSERVER32 Komponen out-of-process hkey_class_root/clsid/guid/localserver32 Kategori untuk komponen mana (CATID) HKEY_CLASS_ROOT/CLSID/GUID/Kategori yang diimplementasikan Informasi konfigurasi antarmuka com hkey_class_root/antarmuka Proxy dl/stub dll hkey_class_root/clsid/guid/proxystubclsid Hkey_class_root/clsid/guid/proxystubclsid32 Informasi tentang jenis pustaka hkey_class_root/typelib String penamaan progid hkey_class_root/ (misalnya "comctl.treectrl") Component GUID HKEY_CLASS_ROOT/COMTRL.TREECONTROL/CLSID Nomor versi default hkey_class_root/comtrl.treecontrol/curver (mis. Curver = "comtrl.treectrl.1", lalu Hkey_class_root/comtrl.treecontrol.1 juga ada) Semua kategori komponen dari mesin saat ini HKEY_CLASS_ROOT/KOMPONEN KOMPONEN COM menyediakan dua fungsi API CLSIDFROMPROGID dan PROGIDFROMCLSID untuk mengonversi progid dan CLSID. Jika komponen COM mendukung set antarmuka yang sama, mereka dapat diklasifikasikan ke dalam kelas yang sama, dan komponen dapat diklasifikasikan ke dalam beberapa kelas. Misalnya, jika semua objek otomatisasi mendukung antarmuka Idispatch, mereka dapat diklasifikasikan sebagai "objek otomatisasi". Informasi kategori juga dijelaskan oleh GUID, yang disebut CATID. Tujuan utama dari kategori komponen adalah bahwa pelanggan dapat dengan cepat menemukan jenis objek komponen tertentu pada mesin. Antarmuka, sekarang menggunakan kategori komponen, dapat menyimpan proses kueri. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Daftarkan komponen com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- REGSRV32.EXE digunakan untuk mendaftarkan komponen dalam proses, yang memanggil DLLRegisterServer dan DllunRegisterServer fungsi DLL untuk menyelesaikan pendaftaran dan pembatalan program komponen. Jika operasi berhasil, kembalikan true, jika tidak, kembalikan salah. Untuk program komponen di luar proses, situasinya sedikit berbeda karena merupakan program yang dapat dieksekusi itu sendiri dan tidak dapat memberikan fungsi masuk untuk digunakan program lain. Oleh karena itu, spesifikasi COM menetapkan bahwa komponen di luar proses yang mendukung pendaftaran diri harus mendukung dua parameter baris perintah /regserver dan /unregserver untuk menyelesaikan operasi pendaftaran dan pembatalan. Parameter baris perintah bergantung pada case, dan "/" dapat diganti dengan "-". Jika operasi berhasil, program mengembalikan 0, jika tidak, mengembalikan Non-0 berarti kegagalan. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Fungsi Pabrik Kelas dan DllgetObjectClass -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Pabrik Kelas adalah basis produksi objek COM. Pabrik kelas itu sendiri juga merupakan objek COM, yang mendukung antarmuka khusus IclassFactory: Kelas iClassFactory: Iunknown publik { virtual hResult _stdcall createInstance (iunknown *PunkNOwnouter, Const IID & IID, batal ** ppv) = 0; virtual hResult _stdcall lockserver (blok bool) = 0; } Fungsi anggota CreateInstance digunakan untuk membuat objek COM yang sesuai. Parameter pertama punkNOwnouter digunakan untuk kasus di mana kelas objek dikumpulkan, dan umumnya diatur ke nol; Pointer Antarmuka. Fungsi anggota LockServer digunakan untuk mengontrol masa pakai komponen. Objek Pabrik Kelas dibuat oleh fungsi DLL-DLLGETCLASSObject: HResult DllgetClassObject (const clsid & clsid, const iid & iid, (void **) ppv); Parameter pertama dari fungsi DlLgetClassObject adalah CLSID dari objek yang akan dibuat. Karena komponen dapat mengimplementasikan beberapa kelas objek COM, perlu untuk menentukan CLSID dalam parameter fungsi DLLGetClassObject untuk membuat pabrik kelas yang benar. Dua parameter lainnya IID dan PPV merujuk ke antarmuka IID yang ditentukan dan penunjuk antarmuka pabrik kelas penyimpanan, masing -masing. Setelah menerima instruksi untuk membuat objek, perpustakaan COM memanggil fungsi DLLGetClassObject dari komponen dalam proses, membuat objek pabrik kelas dari fungsi, dan mengembalikan penunjuk antarmuka objek pabrik kelas. Setelah perpustakaan atau pelanggan COM memiliki penunjuk antarmuka ke pabrik kelas, mereka dapat membuat objek COM yang sesuai melalui fungsi anggota Create Instance dari iClassFactory. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Fungsi CogetClassObject -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Di perpustakaan COM, ada tiga API yang dapat digunakan untuk pembuatan objek, yaitu CogetClassObject, CocreateInstnace, dan CocreateInstanceex. Biasanya, program klien memanggil salah satunya untuk menyelesaikan pembuatan objek dan mengembalikan penunjuk antarmuka awal objek. Perpustakaan COM dan pabrik kelas juga berinteraksi melalui ketiga fungsi ini. HResult cogetclassObject (const clsid & clsid, dword dwclscontext, Coserverinfo *pserverinfo, const iid & iid, (void **) ppv); Fungsi CogetClassObject pertama kali menemukan pabrik kelas dari kelas COM yang ditentukan oleh CLSID, dan kemudian terhubung ke objek pabrik kelas. Jika itu adalah objek komponen dalam proses, CogetClassObject memanggil DLLGetClassObject dari modul DLL untuk memperkenalkan fungsi, lulus parameter CLSID, IID dan PPV ke fungsi DLLGetClassObject, dan mengembalikan antarmuka pointer dari objek pabrik kelas. Biasanya, IID adalah pengidentifikasi iid_iclassfactory dari iClassfactory. Jika objek pabrik kelas juga mendukung antarmuka lain yang dapat digunakan untuk membuat operasi, pengidentifikasi antarmuka lain juga dapat digunakan. Misalnya, antarmuka iClassFactory2 dapat diminta untuk memverifikasi status lisensi pengguna pada waktu pembuatan. Antarmuka iClassFactory2 adalah ekstensi ke iClassFactory, yang meningkatkan keamanan pembuatan komponen. Parameter DWCLScontext menentukan kategori komponen, yang dapat ditentukan sebagai komponen dalam proses, komponen out-of-process atau objek kontrol dalam proses (mirip dengan objek proksi komponen out-of-process, terutama digunakan dalam teknologi OLE). Parameter IID dan PPV sesuai dengan parameter DLLGetClassObject, masing -masing, dan digunakan untuk menentukan antarmuka IID dan penunjuk antarmuka untuk menyimpan objek kelas. Parameter pServerInfo digunakan untuk menentukan informasi server saat membuat objek jarak jauh. Situasi ini jauh lebih rumit jika objek pabrik kelas yang dibuat oleh fungsi CogetClassObject terletak di komponen di luar proses. Pertama, fungsi COGETClassObject memulai proses komponen dan kemudian menunggu sampai proses komponen mendaftarkan pabrik kelas objek kelas COM yang didukungnya untuk com. Jadi fungsi CogetClassObject mengembalikan informasi pabrik kelas yang sesuai di Com. Oleh karena itu, ketika proses di luar komponen dimulai oleh perpustakaan COM (dengan parameter baris perintah "/embedding"), ia harus mendaftarkan objek pabrik kelas COM yang didukung ke dalam COM melalui fungsi CoreGisterClassObject sehingga perpustakaan COM dapat Buat objek COM untuk digunakan. Ketika proses keluar, fungsi CorevokeClassObject harus dipanggil untuk memberi tahu bahwa objek pabrik kelas yang didaftarkan tidak lagi valid. Program komponen memanggil fungsi CoregisterClassObject dan fungsi CorevokeClassObject harus dipasangkan untuk memastikan konsistensi informasi COM. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Fungsi CocreateInstance / CocreateInstanceex -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- HResult CocreateInstance (const clsid & clsid, iunknown *Punknownouter, DWORD DWCLScontext, Const IID & IID, (void **) PPV); CocreateInstance adalah fungsi pembantu yang dibungkus yang benar -benar menyebut fungsi CogetClassObject di dalamnya. Arti parameter clsid dan dwclscontext dari cocreateInstance konsisten dengan parameter yang sesuai dari COGETClassObject (parameter CocreateInstance IID dan PPV berbeda dari CogetClassObjec pabrik). Parameter PunkNOwnouter konsisten dengan parameter yang sesuai di Createinstance of the Class Factory Interface, dan terutama digunakan dalam kasus di mana objek dikumpulkan. Fungsi CocreateInstance merangkum proses membuat objek melalui pabrik kelas. CocreateInstance dapat diimplementasikan dengan kode berikut: (CATATAN SIVETIME: Aplikasi PPV Pointer dalam kode berikut tampaknya batal **) HResult CocreateInstance (const clsid & clsid, iunknown *Punknownouter, DWORD DWCLSCONTEXT, CONST IID & IID, VOID *PPV) { IClassFactory *pcf; HRESULT HR; hr = cogetclassObject (clsid, dwclscontext, null, iid_iclassfactory, (void *) pcf); if (gagal (hr)) mengembalikan jam; hr = pcf-> createInstance (punknOwnouter, iid, (void *) ppv); pfc-> rilis (); Return HR; } Dari kode ini, kita dapat melihat bahwa fungsi CocreateInstance pertama -tama menggunakan fungsi CogetClassObject untuk membuat objek pabrik kelas, dan kemudian menggunakan penunjuk antarmuka dari objek pabrik kelas untuk membuat objek COM nyata. dikembalikan, sehingga kelas digunakan. Namun, menggunakan CocreateInstance tidak membuat objek pada mesin jarak jauh, karena ketika memanggil CogetClassObject, parameter ketiga yang digunakan untuk menentukan informasi server diatur ke nol. Jika Anda ingin membuat objek jarak jauh, Anda dapat menggunakan fungsi ekstensi CocreateInstance CocreateInstanceex: HResult Cocreateinstanceex (const clsid & clsid, iunknown *Punknownouter, DWORD DWCLSCONTEXT, COSERVERINFO *PSERVERINFO, DWORD DWCOUNT, Multi_qi *rgmultiqi); Tiga parameter pertama sama dengan CocreateInstance. Pointer antarmuka. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Inisialisasi Perpustakaan COM -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Sebelum memanggil fungsi Perpustakaan COM, untuk membuat fungsi valid, fungsi inisialisasi perpustakaan COM harus dipanggil: HResult Coinitialize (iMalloc *pMalloc); PMALLOC digunakan untuk menentukan alokasi memori, dan prinsip alokasi memori dapat ditentukan oleh aplikasi. Secara umum, jika kita secara langsung mengatur parameter ke NULL, pustaka COM akan menggunakan alokasi memori yang disediakan default. Nilai pengembalian: S_OK berarti inisialisasi berhasil S_False menunjukkan bahwa inisialisasi berhasil, tetapi panggilan ini bukan pertama kalinya fungsi inisialisasi disebut dalam proses ini. S_unexpected menunjukkan bahwa kesalahan terjadi selama proses inisialisasi dan aplikasi tidak dapat menggunakan pustaka COM. Biasanya, suatu proses menginisialisasi pustaka COM hanya sekali, dan tidak masuk akal untuk menginisialisasi perpustakaan COM beberapa kali di unit modul yang sama. Satu -satunya fungsi yang tidak perlu menginisialisasi perpustakaan COM adalah untuk mendapatkan versi COM Library: Dword cobuildversion (); Nilai Pengembalian: Nomor Versi Utama 16-Bit Tinggi Nomor versi 16 digit lebih rendah Setelah program COM menggunakan Layanan Perpustakaan COM, biasanya sebelum program keluar, perlu untuk menghubungi fungsi Layanan Perpustakaan COM yang diakhiri untuk membebaskan sumber daya yang dikelola oleh Perpustakaan COM: void couninitialize (void); Catatan: Proses atau modul program apa pun yang memanggil fungsi Coinitialize dan mengembalikan S_OK harus memiliki panggilan fungsi couninitialize yang sesuai untuk memastikan bahwa perpustakaan COM secara efektif menggunakan sumber daya. (? Jika Coinitialize dipanggil dalam modul dan mengembalikan S_OK, akankah modul lain yang juga menggunakan perpustakaan COM memiliki kesalahan setelah memanggil fungsi Counitialize? Atau akankah perpustakaan COM secara otomatis memeriksa modul mana yang digunakan?) -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Manajemen Memori Perpustakaan Com -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Karena program komponen COM dan program klien terhubung melalui standar level biner, semua operasi yang melibatkan interaksi memori antara klien, perpustakaan COM dan komponen (alokasi dan rilis tidak dalam modul yang sama) dalam aplikasi COM harus digunakan Manajer Memori yang konsisten. Standar manajemen memori yang disediakan oleh COM sebenarnya adalah antarmuka iMalloc: // IID_IMALLOC: {00000002-0000-0000-C000-000000000046} Kelas ImAlloc: IUKNKNOWN PUBLIK { batal * alloc (ulong cb) = 0; void * realloc (void * pv, ulong cb) = 0; void bebas (batal *pv) = 0; Ulong getSize (void *pv) = 0; // Mengembalikan ukuran memori yang dialokasikan int didalloc (void *pv) = 0; // Tentukan apakah penunjuk memori dialokasikan oleh manajer memori void heapminimize () = 0; // Sistem Operasi untuk Optimalisasi Kinerja } Dapatkan Pointer Antarmuka ImAlloc: HResult cogetmalloc (dword dwmemcontext, imalloc ** ppmalloc); Parameter pertama dari fungsi cogetmalloc DWMemContext digunakan untuk menentukan jenis manajer memori. Perpustakaan COM berisi dua manajer memori, satu adalah manajer memori yang ditentukan selama inisialisasi atau manajer default internalnya, juga dikenal sebagai manajer pekerjaan (Allocator Tugas). Memctx_task dalam parameter DWMemContext; memproses dan diteruskan ke proses kedua, menggunakan memori ini dalam proses kedua atau bahkan melepaskannya. Selama nilai pengembalian fungsi adalah S_OK, PPMALLOC menunjuk ke pointer antarmuka memori dari perpustakaan COM, dan Anda dapat menggunakannya untuk melakukan operasi memori. Perpustakaan COM merangkum tiga fungsi API yang dapat digunakan untuk alokasi dan rilis memori: void * cotaskmemalloc (Ulong CB); void cotaskfree (void *pv); void cotaskmemrealloc (void *pv, ulong cb); Ketiga fungsi ini ditugaskan ke tiga fungsi anggota yang sesuai dengan ImAlloc: Alloc, Realloc, dan Free. Contoh: Bagaimana program COM menemukan nilai progid yang sesuai dari nilai CLSID: Wchar *pwprogid; char pszprogid [128]; hResult = :: progidfromclsid (clsid_dictionary, & pwprogid); if (hResult! = s_ok) { ... } WCSTOMBS (PSZProgid, PWPROGID, 128); Cotaskmemfree (pwprogid); // Catatan: memori harus dilepaskan Setelah memanggil fungsi COM progidfromclsid untuk dikembalikan, karena perpustakaan COM mengalokasikan ruang memori untuk variabel output PWPROGID, aplikasi harus memanggil fungsi CotaskMemFree untuk membebaskan memori setelah menggunakan variabel PWPROGID. Contoh ini menggambarkan situasi di mana memori dialokasikan di perpustakaan COM dan memori dibebaskan dalam program panggilan. Beberapa fungsi lain di pustaka COM juga memiliki karakteristik yang sama, terutama beberapa fungsi yang berisi parameter output panjang tidak terbatas. -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Program Komponen Memuat dan Menghapus Instalasi -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Memuat komponen dalam proses: 客户程序调用COM 库的CoCreateInstance 或CoGetClassObject 函数创建COM 对象,在CoGetClassObject 函数中,COM 库根据系统注册表中的信息,找到类标识符CLSID 对应的组件程序(DLL 文件)的全路径,然后调用LoadLibrary(实际上是CoLoadLibrary)函数,并调用组件程序的DllGetClassObject 引出函数。DllGetClassObject 函数创建相应的类厂对象,并返回类厂对象的IClassFactory 接口。至此CoGetClassObject 函数的任务完成,然后客户程序或者CoCreateInstance 函数继续调用类厂对象的CreateInstance 成员函数,由它负责COM 对象的创建工作。 CoCreateInstance |-CoGetClassObject |-Get CLSID -> DLLfile path |-CoLoadLibrary |-DLLfile.DllGetClassObject |-return IClassFactory |-IClassFactory.CreateInstnace 进程外组件的装载: 在COM 库的CoGetClassObject 函数中,当它发现组件程序是EXE 文件(由注册表组件对象信息中的LocalServer 或LocalServer32 值指定)时,COM 库创建一个进程启动组件程序,并带上“/Embedding”命令行参数,然后等待组件程序;而组件程序在启动后,当它检查到“/Embedding”命令行参数后,就会创建类厂对象,然后调用CoRegisterClassObject 函数把类厂对象注册到COM 中。当COM 库检查到组件对象的类厂之后,CoGetClassObject 函数就把类厂对象返回。由于类厂与客户程序运行在不同的进程中,所以客户程序得到的是类厂的代理对象。一旦客户程序或COM 库得到了类厂对象,它就可以完成组件对象的创建工作。 进程内对象和进程外对象的不同创建过程仅仅影响了CoGetClassObject 函数的实现过程,对于客户程序来说是完全透明的。 CoGetClassObject |-LocalServer/LocalServer32 |-Execute EXE /Embedding |-Create class factory |-CoRegisterClassObject ( class factory ) |-return class factory (proxy) 进程内组件的卸载: 只有当组件程序满足了两个条件时,它才能被卸载,这两个条件是:组件中对象数为0,类厂的锁计数为0。满足这两个条件时,DllCanUnloadNow 引出函数返回TRUE。COM 提供了一个函数CoFreeUnusedLibraries,它会检测当前进程中的所有组件程序,当发现某个组件程序的DllCanUnloadNow 函数返回TRUE 时,就调用FreeLibrary 函数(实际上是CoFreeLibrary 函数)把该组件从程序从内存中卸出。 该由谁来调用CoFreeUnusedLibraries 函数呢?因为在组件程序执行过程中,它不可能把自己从内存中卸出,所以这个任务应该由客户来完成。客户程序随时都可以调用CoFreeUnusedLibraries 函数完成卸出工作,但通常的做法是,在程序的空闲处理过程中调用CoFreeUnusedLibraries 函数,这样做既可以避免程序中处处考虑对CoFreeUnusedLibraries 函数的调用,又可以使不再使用的组件程序得到及时清除,提高资源的利用率,COM 规范也推荐这种做法。 进程外组件的卸载: 进程外组件的卸载比较简单,因为组件程序运行在单独的进程中,一旦其退出的条件满足,它只要从进程的主控函数返回即可。在Windows 系统中,进程的主控函数为WinMain。 前面曾经说过,在组件程序启动运行时,它调用CoRegisterClassObject 函数,把类厂对象注册到COM 中,注册之后,类厂对象的引用计数始终大于0,因此单凭类厂对象的引用计数无法控制进程的生存期,这也是引入类厂对象的加锁和减锁操作的原因。进程外组件的载条件与DllCanUnloadNow 中的判断类似,也需要判断COM 对象是否还存在、以及判断是否锁计数器为0,只有当条件满足了,进程的主函数才可以退出。 从原则上讲,进程外组件程序的卸载就是这么简单,但实际上情况可能复杂一些,因为有些组件程序在运行过程中可以创建自己的对象,或者包含用户界面的程序在运行过程中,用户手工关闭了进程,那么进程对这些动作的处理要复杂一些。例如,组件程序在运行过程中,用户又打开了一个文件并进行操作,那么即使原先创建的对象被释放了,而且锁计数器也为0,进程也不能退出,它必须继续为用户服务,就像是用户打开的进程一样。对这种程序,可以增加一个“用户控制”标记flag,如果flag 为FALSE,则可以按简单的方法直接退出程序即可;如果flag 为TRUE,则表明用户参与了控制,组件进程不能马上退出,但应该调用CoRevokeClassObject 函数以便与CoRegisterClassObject 调用相响呼应,把进程留给用户继续进行。 如果组件程序在运行过程中,用户要关闭进程,而此时并不满足进程退出条件,那么进程可以采取两种办法:第一种方法,把应用隐藏起来,并把flag 标记设置为FALSE,然后组件程序继续运行直到卸载条件满足为止;另一种办法是,调用CoDisconnectObject 函数,强迫脱离对象与客户之间的关系,并强行终止进程,这种方法比较粗暴,不提倡采用,但不得已时可以也使用,以保证系统完成一些高优先级的操作。 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- COM 库常用函数 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 初始化函数CoBuildVersion 获得COM 库的版本号 CoInitialize COM 库初始化 CoUninitialize COM 库功能服务终止 CoFreeUnusedLibraries 释放进程中所有不再使用的组件程序 GUID 相关函数IsEqualGUID 判断两个GUID 是否相等 IsEqualIID 判断两个IID 是否相等 IsEqualCLSID 判断两个CLSID 是否相等(*为什么要3个函数) CLSIDFromProgID 字符串组件标识转换为CLSID 形式 StringFromCLSID CLSID 形式标识转化为字符串形式 IIDFromString 字符串转换为IID 形式 StringFromIID IID 形式转换为字符串 StringFromGUID2 GUID 形式转换为字符串(*为什么有2) 对象创建函数CoGetClassObject 获取类厂对象 CoCreateInstance 创建COM 对象 CoCreateInstanceEx 创建COM 对象,可指定多个接口或远程对象 CoRegisterClassObject 登记一个对象,使其它应用程序可以连接到它 CoRevokeClassObject 取消对象的登记 CoDisconnectObject 断开其它应用与对象的连接 内存管理函数CoTaskMemAlloc 内存分配函数 CoTaskMemRealloc 内存重新分配函数 CoTaskMemFree 内存释放函数 CoGetMalloc 获取COM 库内存管理器接口 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- HRESULT 类型 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 大多数COM 函数以及一些接口成员函数的返回值类型均为HRESULT 类型。HRESULT 类型的返回值反映了函数中的一些情况,其类型定义规范如下: 31 30 29 28 16 15 0 |-----|--|------------------------|-----------------------------------| 类别码(30-31) 反映函数调用结果: 00 调用成功 01 包含一些信息 10 警告 11 错误 自定义标记(29) 反映结果是否为自定义标识,1 为是,0 则不是; 操作码(16-28) 标识结果操作来源,在Windows 平台上,其定义如下: #define FACILITY_WINDOWS 8 #define FACILITY_STORAGE 3 #define FACILITY_RPC 1 #define FACILITY_SSPI 9 #define FACILITY_WIN32 7 #define FACILITY_CONTROL 10 #define FACILITY_NULL 0 #define FACILITY_INTERNET 12 #define FACILITY_ITF 4 #define FACILITY_DISPATCH 2 #define FACILITY_CERT 11 操作结果码(0-15) 反映操作的状态,WinError.h 定义了Win32 函数所有可能返回结果。 以下是一些经常用到的返回值和宏定义: S_OK 函数执行成功,其值为0 (注意,其值与TRUE 相反) S_FALSE 函数执行成功,其值为1 S_FAIL 函数执行失败,失败原因不确定 E_OUTOFMEMORY 函数执行失败,失败原因为内存分配不成功 E_NOTIMPL 函数执行失败,成员函数没有被实现 E_NOTINTERFACE 函数执行失败,组件没有实现指定的接口 不能简单地把返回值与S_OK 和S_FALSE 比较,而要用SECCEEDED 和FAILED 宏进行判断。 ==================================================== =================== =================================== ========= ⊙ 第四章COM 特性 ==================================================== =================== =================================== ========= 可重用性:包容和聚合 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 包容模型: 组件对象在接口的实现代码中执行自身创建的另一个组件对象的接口函数(客户/服务器模型)。这个对象同时实现了两个(或更多)接口的代码。 聚合模型: 组件对象在接口的查询代码中把接口传递给自已创建的另一个对象的接口查询函数,而不实现该接口的代码。另一个对象必须实现聚合模型(也就是说,它知道自己正在被另一个组件对象聚合),以便QueryInterface 函数能够正常运作。 在组件对象被聚合的情况下,当客户请求它所不支持的接口或者请求IUnknown 接口时,它必须把控制交给外部对象,由外部对象决定客户程序的请求结果。 聚合模型体现了组件软件真正意义上的重用。 聚合模型实现的关键在CoCreateInstance 函数和IClassFactory 接口: HRESULT CoCreateInstance(const CLSID& clsid, IUnknown *pUnknownOuter, DWORD dwClsContext, const IID& iid, (void **)ppv); // class IClassFactory : public IUnknown virtual HRESULT _stdcall CreateInstance(IUnknown *pUnknownOuter, const IID& iid, void **ppv) = 0; 其中pUnknownOuter 参数用于指定组件对象是否被聚合。如果pUnknownOuter 参数为NULL,说明组件对象正常使用,否则说明被聚合使用,pUnknownOuter 是外部组件对象的接口指针。 聚合模型下的被聚合对象的引用计数成员函数也要进行特别处理。在未被聚合的情况下,可以使用一般的引用计数方法。在被聚合时,由客户调用AddRef/Release 函数时,必须转向外部组件对象的AddRef/Release 方法。这时,外部组件对象要控制被聚合的对象必须采用其它的引用计数接口。 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 进程透明性(待学) 安全性(待学) 多线程特性(待学) -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- ==================================================== =================== =================================== ========= ⊙ 第五章用Visual C++ 开发COM 应用 ==================================================== =================== =================================== ========= Win32 SDK 提供的一些头文件的说明 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- Unknwn.h 标准接口IUnknown 和IClassFacatory 的IID 及接口成员函数的定义 Wtypes.h 包含COM 使用的数据结构的说明 Objidl.h 所有标准接口的定义,即可用于C 语言风格的定义,也可用于C++ 语言 Comdef.h 所有标准接口以及COM 和OLE 内部对象的CLSID ObjBase.h 所有的COM API 函数的说明 Ole2.h 所有经过封装的OLE 辅助函数 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- 与COM 接口有关的一些宏 -------------------------------------------------- -------------------------------------------------- -------------------------------------------------- ---------------------------- DECLARE_INTERFACE(iface) 声明接口iface,它不从其他的接口派生 DECLARE_INTERFACE_(iface, baseiface) 声明接口iface,它从接口baseiface 派生 STDMETHOD(method) 声明接口成员函数method,函数返回类型为HRESULT STDMETHOD_(type, method) 声明接口成员函数method,函数返回类型为type ==================================================== =================== =================================== ========= ⊙ 结束 ==================================================== =================== =================================== ========= |