| 피드 : 독점 노트 제목 : "COM 원칙 및 응용 프로그램"에 대한 연구 노트 | 저자 : FGS_FGS 의견 |
| "COM 원칙 및 응용 프로그램"연구 노트 -1 부 원칙 http://savetime.delphibs.com 시작 시간 : 2004.1.30 마지막 수정 : 2004.2.1 이 기사의 형식은 다음과 같습니다. 텍스트는 자동으로 창으로 포장됩니다. (이 기사의 내용은 기본적으로 "COM 원칙 및 응용 프로그램"이라는 책에서 발췌 한 것입니다. 저작권은 저자 Pan Aimin이 소유하고 있습니다. 공개 미디어에서 사용하지 마십시오) 목차 =================================================== ================================================== ========== ⊙ 1 장 개요 com이란 무엇입니까? com 객체 및 인터페이스 com 프로세스 모델 com 재사용 성 ⊙ 2 장 COM 객체 모델 전 세계적으로 고유 한 식별자 안내서 com 객체 com 인터페이스 인터페이스 설명 언어 IDL iunknown 인터페이스 COM 객체의 인터페이스 원칙 COM의 3 장 구현 com 구성 요소 등록 정보 COM 구성 요소를 등록하십시오 클래스 팩토리 및 dllgetobjectclass 함수 CogetClassObject 함수 CocreateInstance / CocreateInstanceex 기능 COM 라이브러리의 초기화 COM 라이브러리의 메모리 관리 구성 요소 프로그램로드 및 제거 COM 라이브러리의 일반적인 기능 Hresult 유형 ⊙ 4 장 COM 기능 재사용 성 : 포함 및 집계 프로세스 투명성 (배운) 안전 (배우기) 멀티 스레딩 기능 (배운) 짐서 5 장 Visual C ++를 사용하여 COM 응용 프로그램 개발 Win32 SDK가 제공 한 일부 헤더 파일에 대한 설명 COM 인터페이스와 관련된 일부 매크로 =================================================== ================================================== ========== 텍스트 =================================================== ================================================== ========== ⊙ 1 장 개요 =================================================== ================================================== ========== com이란 무엇입니까? --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- Com은 Microsoft가 제안한 구성 요소 표준입니다. 구성 요소 프로그램 간의 상호 작용 표준을 정의 할뿐만 아니라 구성 요소 프로그램이 실행되는 데 필요한 환경도 제공합니다. COM 표준에서 구성 요소 프로그램을 모듈이라고도합니다. 이는 프로세스 내 구성 요소 또는 실행 가능한 프로그램 (예 : EXE 프로그램)이라고 할 수 있습니다. 구성 요소 프로그램에는 하나 이상의 구성 요소 객체가 포함될 수 있습니다. )는 COM 객체를 제공하는 코드 캐리어입니다. COM 객체는 일반적인 객체 지향 언어의 객체 개념과 다릅니다. com 객체는 언어 독립적입니다. 이 기능은 다양한 프로그래밍 언어로 개발 된 구성 요소 객체와 상호 작용할 수 있습니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- com 객체 및 인터페이스 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- C ++의 객체의 개념과 유사하게, 객체는 클래스의 인스턴스이며, 클래스는 일련의 관련 데이터와 함수를 결합한 정의입니다. 객체 (또는 다른 객체)를 사용하는 응용 프로그램을 고객이라고하며 때로는 객체 사용자라고도합니다. 인터페이스는 논리적으로 관련된 함수 세트이며 그 함수를 인터페이스 멤버 함수라고도합니다. Custom에 따르면 인터페이스 이름은 종종 "i"로 접두사됩니다. 객체는 인터페이스 멤버 기능을 통해 고객에게 다양한 형태의 서비스를 제공합니다. COM 모델에서 객체 자체는 고객에게 보이지 않으며 고객이 서비스를 요청할 때 인터페이스를 통해서만 수행 할 수 있습니다. 각 인터페이스는 128 비트 전 세계 고유 식별자 (Guid)로 식별됩니다. 클라이언트는 Guid를 통해 인터페이스 포인터를 얻은 다음 인터페이스 포인터를 전달하고 클라이언트는 해당 멤버 함수를 호출 할 수 있습니다. 인터페이스와 마찬가지로 각 구성 요소는 ClsID (클래스 식별자, 클래스 식별자 또는 클래스 ID)라는 128 비트 안내서로 식별됩니다. 실제로 클라이언트가 객체를 성공적으로 생성 한 후에는 객체의 인터페이스에 대한 포인터가 하나 이상의 인터페이스를 구현하기 때문에 클라이언트는 인터페이스를 호출하여 제공 할 수 있습니다. 모든 서비스. COM 사양에 따라 COM 객체가 여러 인터페이스를 구현하면 특정 인터페이스에서 객체의 다른 인터페이스를 얻을 수 있습니다. 이 프로세스에서 클라이언트는 인터페이스를 통해 COM 객체 만 처리하고 객체는 클라이언트를위한 일련의 인터페이스 일뿐입니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- com 프로세스 모델 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM이 제공하는 서비스 구성 요소 개체에는 구현시 2 개의 프로세스 모델이 있습니다 : 프로세스 내 개체 및 프로세스 외 객체. 프로세스 내 개체 인 경우 클라이언트 프로세스 공간에서 실행됩니다. 프로세스 외 객체 인 경우 동일한 컴퓨터 또는 원격 기계 공간에서 실행됩니다. 프로세스 서비스 프로그램 : 서비스 프로그램은 고객의 프로세스 공간에로드됩니다. 지역 서비스 프로그램 : 서비스 프로그램은 클라이언트 프로그램과 동일한 컴퓨터에서 실행됩니다. 서비스 프로그램은 일반적으로 EXE 파일입니다. 원격 서비스 프로그램 : 서비스 프로그램은 클라이언트와 다른 컴퓨터에서 실행되며 DLL 모듈 또는 EXE 파일 일 수 있습니다. 원격 서비스 프로그램이 DLL 양식으로 구현되면 원격 시스템은 프록시 프로세스를 만듭니다. COM 객체에는 프로세스 모델이 다르지만이 차이는 클라이언트 프로그램에 투명합니다. 따라서 클라이언트 프로그램은 구성 요소 객체를 사용하면 COM 사양을 준수하는 한이 차이의 존재를 무시할 수 있습니다. 그러나 COM 객체를 구현할 때는 여전히 프로세스 모델을 신중하게 선택해야합니다. 프로세스 내 모델의 장점은 효율적이지만 불안정한 구성 요소가 클라이언트 프로세스가 충돌하게되므로 구성 요소는 클라이언트를 위험에 빠뜨릴 수 있습니다. -프로세스 모델도 문제가있을 것입니다. 프로세스의 구성 요소와 고객이 동일한 주소 공간에 있기 때문에 충돌 가능성이 높기 때문일 수 있습니다. 안정성과 구성 요소 프로세스는 클라이언트 프로세스가 여러 클라이언트 프로세스에 서비스를 제공 할 수 있지만, 프로세스 외 구성 요소는 비교적 낮습니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- com 재사용 성 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 표준은 이진 코드 레벨을 기반으로하기 때문에 COM 객체의 재사용 성은 C ++와 같은 일반 객체 지향 언어의 객체의 재사용 프로세스와 다릅니다. COM 객체의 클라이언트 프로그램의 경우 인터페이스를 통해 객체가 제공하는 서비스 만 사용하므로 객체 내부의 구현 프로세스를 알지 못하므로 구성 요소 객체의 재사용은 구성 요소 개체의 동작을 기반으로합니다. 이에 대한 특정 구현보다는 이것이 재사용의 열쇠입니다. COM은 객체의 재사용을 실현하기 위해 두 가지 메커니즘을 사용합니다. 우리는 두 개의 com 객체가 있다고 가정하고 객체 1 객체 2의 기능을 재사용하기를 희망합니다. 우리는 객체 1을 외부 객체와 객체 2를 내부 객체라고 부릅니다. (1) 포괄적 인 방법. 객체 1에는 객체 2가 포함됩니다. 객체 1은 객체 2의 함수를 사용해야 할 때 단순히 구현을 객체 2에 넘겨 줄 수 있습니다. 객체 1과 객체 2는 동일한 인터페이스를 지원하지만 객체 1은 실제로 인터페이스를 구현할 수 있습니다 , 객체 2의 구현이 호출됩니다. (2) 집계 방법. 객체 1 객체 2의 인터페이스를 클라이언트에 제출하면 객체 2의 인터페이스가 구현되지 않지만 객체 2의 인터페이스를 클라이언트 프로그램에 노출 시키며 클라이언트 프로그램은 내부 객체 2를 알지 못합니다. 존재하다. =================================================== ================================================== ========== ⊙ 2 장 COM 객체 모델 =================================================== ================================================== ========== 전 세계적으로 고유 한 식별자 안내서 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 사양은 128 비트 전 세계 고유 식별자 안내서를 사용하여 객체 및 인터페이스를 식별하는데, 이는 임의의 숫자이며 특수 대행사가 할당 및 관리를 요구하지 않습니다. Guid는 무작위 숫자이기 때문에 독창성은 절대적으로 보장되지 않지만 식별자가 두 배가 될 가능성은 매우 적습니다. 이론적으로, 기계가 초당 100,000,000 개의 안내서를 생성하면 3240 년 (확률의 의미에서)이 반복되지 않을 것임을 보장 할 수 있습니다. Guid는이 구조를 사용하여 C/C ++에 설명 할 수 있습니다. typedef struct _guid { dword data1; 단어 data2; 단어 데이터 3; 바이트 데이터 4 [8]; } 안내; 예 : {64BF4372-1007-B0AA-44453540000} 다음과 같이 안내를 정의 할 수 있습니다. extern "c"const guid clsid_myspellchecker = {0x54bf0093, 0x1048, 0x399d, {0xb0, 0xa3, 0x45, 0x33, 0x43, 0x90, 0x47, 0x47}}; Visual C ++는 uuidgen.exe (명령 줄)와 Guidgen.exe (대화)의 두 가지 프로그램을 제공합니다. COM 라이브러리는 안내를 생성 할 수있는 다음 API 기능을 제공합니다. hresult cocreateguid (Guid *pguid); GUID가 성공적으로 생성되면 함수는 S_OK를 반환하고 PGUID는 결과 GUID 값을 가리 킵니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- com 객체 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 사양에서 COM 객체는 엄격하게 정의되지 않지만 COM은 객체 지향 구성 요소 모델을 제공하고 COM 구성 요소는 고객에게 객체 형식으로 캡슐화 된 엔티티를 제공합니다. 클라이언트 프로그램이 COM 프로그램과 상호 작용하는 엔티티는 구성 요소 모델의 이름과 위치에 관심이 없지만 어떤 COM 객체와 상호 작용하는지 알아야합니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- com 인터페이스 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- 기술적으로 말하면, 인터페이스는 클라이언트 코드가 구성 요소 객체의 함수를 호출 할 수있는 일련의 함수를 포함하는 데이터 구조입니다. 인터페이스는 구성 요소 객체에 의해 노출 된 모든 정보를 사용하여 구성 요소 개체의 서비스를 얻습니다. 일반적으로 인터페이스 함수 테이블 가상 함수 테이블 (VTable)을 호출하고 vtable에 대한 포인터는 pvtable입니다. 인터페이스의 경우 가상 기능 테이블이 결정되므로 인터페이스의 멤버 함수 수는 변경되지 않으며 멤버 함수의 순서도 변경되지 않습니다. 인터페이스의 정의 에서이 모든 정보는 이진 수준에서 결정되어야합니다. 인터페이스 포인터 ----> pvtable ----> 포인터 함수 1-> | ---------- | M_DATA1 포인터 기능 2-> | M_DATA2 포인터 기능 3-> | ----------- | 각 인터페이스 멤버 함수의 첫 번째 매개 변수는 객체 인스턴스에 대한 포인터입니다 (=이)는 인터페이스 자체가 독립적으로 사용되지 않아 특정 COM 객체에 존재해야하기 때문에이 포인터는 객체 인스턴스의 속성을 제공 할 수 있습니다. . 정보가 호출되면 인터페이스는 어떤 com 객체를 작동하는지 알 수 있습니다. 인터페이스 멤버 함수에서 문자열 변수는 유니 코드 문자 포인터를 사용해야합니다. 따라서 구성 요소 프로그램 내에서 ANSI 문자를 사용하는 경우 두 문자 표현식을 변환해야합니다. 물론, 구성 요소 프로그램 및 클라이언트 프로그램을 설정하는 경우 Com이 인식 할 수있는 매개 변수 유형과 호환되는 한 자신을 정의하는 매개 변수 유형을 사용할 수 있습니다. Visual C ++는 두 가지 문자열 변환을 제공합니다. 네임 스페이스 _com_util { bstr convertstringtobstr (const char *psrc) 던지기 (_com_error); bstr convertbstrtostring (bstr psrc) 던지기 (_com_error); } BSTR은 가장 일반적으로 사용되는 자동화 된 데이터 유형 인 Double Byte Width String입니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- 인터페이스 설명 언어 IDL --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 사양은 OSF의 DCE 사양을 사용하여 원격 통화 인터페이스 IDL (인터페이스 설명 언어)을 설명하고 COM 인터페이스의 설명 언어를 형성하도록 확장합니다. 인터페이스 설명 언어는 어떤 언어에도 의존하지 않는 인터페이스에 대한 설명 방법을 제공하므로 구성 요소 프로그램과 클라이언트 프로그램 사이의 공통 언어가 될 수 있습니다. COM 사양에 의해 사용되는 IDL 인터페이스 설명 언어는 COM 인터페이스를 정의하는 데 사용될뿐만 아니라 인터페이스 멤버 함수에 대해 일반적으로 사용되는 일부 데이터 유형 및 사용자 정의 데이터 구조를 정의 할 수 있습니다. 기능, 가변 길이 배열의 설명조차도 기능. IDL은 C/C ++와 매우 유사한 포인터 유형을 지원합니다. 예를 들어: 인터페이스 Idictionary { hresult initialize () hresult loadlibrary ([in] string); hresult insertword ([in] string, [in] string); Hresult deleteword ([in] string); hresult lookupword ([in] string, [out] string *); hresult restorelibrary ([in] string); Hresult Freelibrary (); } Microsoft Visual C ++는 IDL 인터페이스 설명 파일을 C/C ++로 컴파일 할 수있는 MIDL 도구를 제공합니다.-호환 인터페이스 설명 헤더 파일 (.H). --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- iunknown 인터페이스 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- iunknown의 IDL 정의 : 인터페이스 iunknown { hresult queryinterface ([in] refiid iid, [out] void ** ppv); ulong addref (void); ulong 릴리스 (void); } Iunkown의 C ++ 정의 : 클래스 iunknown { 바이러스 hresult _stdCall QueryInterface (const iid & iid, void ** ppv) = 0; virtual ulong _stdcall addref () = 0; 바이러스 ulong _stdcall release () = 0; } --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 객체의 인터페이스 원칙 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 사양은 QueryInterface 함수에 대한 다음 규칙을 설정합니다. 1. 동일한 객체의 다른 인터페이스 포인터의 경우 쿼리로 얻은 iunknown 인터페이스는 정확히 동일해야합니다. 즉, 각 객체의 iunknown 인터페이스 포인터는 고유합니다. 따라서 두 개의 인터페이스 포인터의 경우 쿼리중인 iunknown 인터페이스가 동일인지 판단하여 동일한 객체를 가리키는 지 여부를 결정할 수 있습니다. 2. 인터페이스 반사성. 인터페이스 자체를 쿼리하는 것은 항상 성공해야합니다. pidictionary-> QueryInterface (iid_dictionary, ...)는 s_ok를 반환해야합니다. 3. 인터페이스 대칭. One Interface Pointer에서 다른 인터페이스 포인터로 쿼리 한 다음 두 번째 인터페이스 포인터에서 첫 번째 인터페이스 포인터로 돌아가는 경우 : 예를 들어 성공해야합니다. pidictionary-> QueryInterface (iid_spellcheck, (void **) & pispellCheck); 검색이 성공하면 PispellCheck에서 IID_Dictionary 인터페이스를 다시 확인하는 것이 확실히 성공할 것입니다. 4. 인터페이스 트랜 티 설정. 첫 번째 인터페이스 포인터에서 두 번째 인터페이스 포인터를 쿼리하고 두 번째 인터페이스 포인터에서 세 번째 인터페이스 포인터를 쿼리 할 수 있다면 세 번째 인터페이스 포인터에서 첫 번째 인터페이스 포인터를 확실히 쿼리 할 수 있습니다. 5. 인터페이스 쿼리 시간 관련이 없습니다. 특정 순간에 특정 인터페이스 포인터를 찾을 수 있다면 앞으로 언제든지 동일한 인터페이스 포인터를 확인하고 쿼리가 성공적으로 성공할 것입니다. 요컨대, 우리가 어떤 인터페이스를 시작하든 언제든지 인터페이스에 도달 할 수 있으며 항상 원래 인터페이스로 돌아갈 수 있습니다. =================================================== ================================================== ========== COM의 3 장 구현 =================================================== ================================================== ========== com 구성 요소 등록 정보 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- 현재 컴퓨터의 모든 구성 요소에 대한 정보 HKEY_CLASS_ROOT/CLSID 임시 구성 요소 HKEY_CLASS_ROOT/CLSID/GUID/InPROCSERVER32 프로세스 외 구성 요소 HKEY_CLASS_ROOT/CLSID/GUID/LOCALSERVER32 어떤 구성 요소 (catid) hkey_class_root/clsid/guid/구현 카테고리 범주 com 인터페이스 구성 정보 hkey_class_root/인터페이스 프록시 dll/stub dll hkey_class_root/clsid/guid/proxystubclsid HKEY_CLASS_ROOT/CLSID/GUID/PROXYSTUBCLSID32 유형 라이브러리에 대한 정보 hkey_class_root/typelib 문자열 명명 progid hkey_class_root/ (예 : "comctl.treectrl") 구성 요소 안내 hkey_class_root/comtrl.treecontrol/clsid 기본 버전 번호 hkey_class_root/comtrl.treecontrol/curver (예 : curver = "comtrl.treectrl.1" hkey_class_root/comtrl.treecontrol.1도 존재합니다) 현재 컴퓨터의 모든 구성 요소 범주 hkey_class_root/구성 요소 범주 Com은 두 가지 API 기능을 제공하여 ClsidfromProgid 및 progidfromclsid를 제공하여 Progid 및 Clsid를 변환합니다. COM 구성 요소가 동일한 인터페이스 세트를 지원하면 동일한 클래스로 분류 될 수 있으며 구성 요소를 여러 클래스로 분류 할 수 있습니다. 예를 들어, 모든 자동화 객체가 Idispatch 인터페이스를 지원하는 경우 "자동화 객체"로 분류 될 수 있습니다. 카테고리 정보는 Catid라는 Guid에 의해 설명됩니다. 구성 요소 범주의 주요 목적은 고객이 시스템에서 특정 유형의 구성 요소 개체를 빠르게 발견 할 수 있으므로 모든 구성 요소 객체를 메모리에로드 한 다음 필요한 것들이 구현되는지 묻습니다. 이제 구성 요소 범주를 사용하는 인터페이스는 쿼리 프로세스를 저장할 수 있습니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 구성 요소를 등록하십시오 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- regsrv32.exe는 구성 요소 프로그램의 등록 및 취소를 완료하기 위해 DLLEGISTESSERVER 및 DLL의 DLLERGISTERSERVER 기능을 호출하는 임시 구성 요소를 등록하는 데 사용됩니다. 작업이 성공하면 true를 반환하고 그렇지 않으면 False를 반환하십시오. 프로세스 외 구성 요소 프로그램의 경우 상황은 실행 프로그램 자체이기 때문에 약간 다르며 다른 프로그램이 사용할 수있는 입력 기능을 제공 할 수 없습니다. 따라서 COM 사양은 자체 등록을 지원하는 프로세스 외 구성 요소를 규정하여 등록 및 취소 작업을 완료하기 위해 두 개의 명령 줄 매개 변수 /regserver 및 /regserver를 지원해야합니다. 명령 줄 매개 변수는 대소 문자에 따라 다르며 "/"는 "-"로 대체 할 수 있습니다. 작업이 성공하면 프로그램은 0을 반환합니다. 그렇지 않으면 0이 아닌 반환은 실패를 의미합니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- 클래스 팩토리 및 dllgetobjectclass 함수 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- 클래스 팩토리는 COM 객체의 생산 기반입니다. 클래스 팩토리 자체는 또한 COM 객체로 특수한 인터페이스 iClassFactory를 지원합니다. 클래스 iclassfactory : public iunknown { 가상 hresult _stdcall createInstance (iunknown *punknownouter, const iid & iid, void ** ppv) = 0; 가상 hresult _stdcall Lockserver (bool block) = 0; } CreateInstance 멤버 함수는 해당 COM 객체를 작성하는 데 사용됩니다. 첫 번째 매개 변수는 객체 클래스가 집계되는 경우에 사용되며, 두 번째 매개 변수 IID는 객체가 생성 된 후에야하는 초기 인터페이스 IID입니다 인터페이스 포인터. Lockserver 멤버 기능은 구성 요소의 수명을 제어하는 데 사용됩니다. 클래스 팩토리 객체는 dll- 파생 함수 dllgetclassobject에 의해 생성됩니다. Hresult dllgetclassobject (const clsid & clsid, const iid & iid, (void **) ppv); DLLGetCassObject 함수의 첫 번째 매개 변수는 생성 할 객체의 CLSID입니다. 구성 요소는 여러 COM 객체 클래스를 구현할 수 있으므로 올바른 클래스 팩토리를 생성하려면 DLLGetClassObject 함수의 매개 변수에 CLSID를 지정해야합니다. 다른 두 매개 변수 IID 및 PPV는 각각 지정된 인터페이스 IID 및 스토리지 클래스 팩토리 인터페이스 포인터를 나타냅니다. COM 라이브러리는 객체를 생성하기위한 명령을 수신 한 후 프로세스에서 구성 요소의 dllgetClassObject 함수를 호출하고 함수에서 클래스 팩토리 객체를 생성하고 클래스 팩토리 객체의 인터페이스 포인터를 반환합니다. COM 라이브러리 또는 고객이 클래스 팩토리에 대한 인터페이스 포인터가 있으면 IClassFactory의 멤버 함수를 통해 해당 COM 객체를 생성 할 수 있습니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- CogetClassObject 함수 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 라이브러리에는 객체 생성에 사용할 수있는 세 가지 API, 즉 CogetclassObject, CocreateInstnace 및 CocreateInstanceEx가 있습니다. 일반적으로 클라이언트 프로그램은 그 중 하나를 호출하여 객체 생성을 완료하고 객체의 초기 인터페이스 포인터를 반환합니다. COM 라이브러리 및 클래스 공장 도이 세 가지 기능을 통해 상호 작용합니다. Hresult CogetClassObject (const clsid & clsid, dword dwclscontext, coserverinfo *pserverinfo, const iid & iid, (void **) ppv); CogetClassObject 함수는 먼저 CLSID에 의해 지정된 COM 클래스의 클래스 공장을 찾은 다음 필요한 경우 CogetClassObject 기능이 구성 요소 코드를로드합니다. 처리 중부 구성 요소 객체 인 경우 CogetClassObject는 DLL 모듈의 DLLGETCLASSOBJECT를 호출하여 함수를 소개하고 매개 변수 CLSID, IID 및 PPV를 DLLGetCassObject 함수로 전달하고 클래스 팩토리 객체의 인터페이스 포인터를 반환합니다. 일반적으로 IID는 iclassFactory의 식별자 iid_iclassfactory입니다. 클래스 팩토리 객체가 작업을 생성하는 데 사용할 수있는 다른 인터페이스를 지원하는 경우 다른 인터페이스 식별자도 사용할 수 있습니다. 예를 들어, ICLASSFACTORY2 인터페이스를 요청하여 생성 시간에 사용자의 라이센스 상태를 확인할 수 있습니다. ICLASSFACTORY2 인터페이스는 ICLASSFactory로의 확장으로 구성 요소 생성의 보안을 향상시킵니다. 매개 변수 dwclsContext는 구성 요소 범주를 지정하며,이 프로세스 구성 요소, 프로세스 외 구성 요소 또는 임시 프로세스 제어 객체 (주로 사용 된 프로세스 외 구성 요소의 프록시 개체와 유사하게 지정할 수있는 구성 요소 범주를 지정합니다. 올레 기술에서). 파라미터 IID 및 PPV는 각각 dllgetClassObject의 매개 변수에 해당하며 클래스 객체를 저장하기위한 인터페이스 IID 및 인터페이스 포인터를 지정하는 데 사용됩니다. 매개 변수 pserverinfo는 원격 객체를 만들 때 서버 정보를 지정하는 데 사용됩니다. CogetClassObject 함수에 의해 생성 된 클래스 팩토리 객체가 프로세스 외 구성 요소에있는 경우 상황이 훨씬 더 복잡합니다. 먼저, CogetClassObject 함수는 구성 요소 프로세스를 시작한 다음 구성 요소 프로세스가 COM에 지원하는 COM 클래스 객체의 클래스 공장을 등록 할 때까지 대기합니다. 따라서 CogetClassObject 함수는 해당 클래스 팩토리 정보를 COM으로 반환합니다. 따라서 COM 라이브러리 (명령 행 매개 변수 "/Embedding")에서 구성 요소 외 프로세스를 시작하면 COM 라이브러리가 COREGISTERCLASSOBJECT 함수를 통해 지원되는 COM 클래스 팩토리 객체를 COM에 등록하여 COM 라이브러리가 할 수 있습니다. 사용하기 위해 COM 객체를 만듭니다. 프로세스가 종료되면 CoreVokeClassObject 함수를 호출하여 등록 된 클래스 팩토리 객체가 더 이상 유효하지 않음을 알려야합니다. 구성 요소 프로그램은 COREGISTERCLASSOBJECT 함수를 호출하고 CORVOKECLASSOBJECT 함수를 짝을 이루어 COM 정보의 일관성을 보장해야합니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- CocreateInstance / CocreateInstanceex 기능 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- Hresult Cocreateinstance (Const Clsid & Clsid, iunknown *Punknownouter, dword dwclscontext, const iid & iid, (void **) ppv); CocreateInstance는 실제로 CogetClassObject 함수를 호출하는 랩핑 된 도우미 기능입니다. Clsid의 매개 변수의 의미 Clsid 및 DwclsContext의 CocreateInstance는 CogetClassObject의 해당 매개 변수와 일치합니다 (CocreateInstance의 IID 및 PPV 매개 변수는 CogetClassObject, 객체를 나타내는 인터페이스 정보이며 다른 하나는 클래스를 나타내는 인터페이스 정보입니다. 공장). 매개 변수 PunkNownouter는 클래스 팩토리 인터페이스를 작성하는 해당 매개 변수와 일치하며 주로 객체가 집계되는 경우에 사용됩니다. CocreateInstance 함수는 클래스 팩토리를 통해 객체 생성 프로세스를 캡슐화합니다. CocreateInstance는 다음 코드로 구현할 수 있습니다. (Savetime Note : 다음 코드에서 PPV 포인터의 적용은 무효 인 것 같습니다 **) 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 (실패 (hr)) 반환 hr; HR = pcf-> CreateInstance (PunkNownouter, iid, (void *) ppv); pfc-> release (); 반환 hr; } 이 코드에서 CocreateInstance 함수는 먼저 CogetClassObject 함수를 사용하여 클래스 팩토리 객체를 생성 한 다음 클래스 팩토리 객체의 인터페이스 포인터를 사용하여 실제 COM 객체를 생성합니다 수업이 사용되도록 반환됩니다. 그러나 CocreateInstance를 사용하면 원격 컴퓨터에 객체가 생성되지 않습니다. CogetClassObject를 호출 할 때 서버 정보를 지정하는 데 사용되는 세 번째 매개 변수는 NULL로 설정되므로. 원격 객체를 만들려면 CocreateInstance의 확장 기능 CocreateInstanceEx를 사용할 수 있습니다. Hresult CocreateInstanceex (Const Clsid & Clsid, iunknown *Punknownouter, dword dwclscontext, coserverinfo *pserverinfo, dword dwcount, multi_qi *rgmultiqi); 첫 번째 매개 변수는 CocreateInstance와 동일합니다. Pserverinfo는 CogetClassoJbect 매개 변수와 동일합니다. 인터페이스 포인터. 목적은 한 번에 여러 객체를 얻는 것입니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 라이브러리의 초기화 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 라이브러리의 기능을 호출하기 전에 함수를 유효하게하기 위해 COM 라이브러리의 초기화 기능을 호출해야합니다. hresult coinitialize (imalloc *pmalloc); PMALLOC은 메모리 할당자를 지정하는 데 사용되며 메모리 할당 원리는 응용 프로그램에 의해 지정 될 수 있습니다. 일반적으로 매개 변수를 NULL로 직접 설정하면 COM 라이브러리가 기본 제공 메모리 할당자를 사용합니다. 반환 값 : S_OK는 초기화가 성공했음을 의미합니다 S_FALSE는 초기화가 성공했음을 나타내지만이 호출은이 프로세스에서 초기화 함수가 처음으로 호출되지 않았습니다. s_unexpected는 초기화 프로세스 중에 오류가 발생했으며 응용 프로그램은 COM 라이브러리를 사용할 수 없음을 나타냅니다. 일반적으로 프로세스는 COM 라이브러리를 한 번만 초기화하며 동일한 모듈 장치에서 COM 라이브러리를 여러 번 초기화하는 것은 의미가 없습니다. COM 라이브러리를 초기화 할 필요가없는 유일한 기능은 COM 라이브러리 버전을 얻는 것입니다. dword cobuildversion (); 반환 값 : 16 비트 주요 버전 번호 낮은 16 자리 버전 번호 COM 프로그램이 일반적으로 프로그램이 종료되기 전에 COM 라이브러리 서비스를 사용한 후에는 COM 라이브러리가 유지 관리하는 리소스를 자유롭게하려면 종료 된 COM 라이브러리 서비스 기능을 호출해야합니다. 공극 상담 (void); 참고 : Coinitialize 함수를 호출하고 S_OK를 반환하는 모든 프로세스 또는 프로그램 모듈은 COM 라이브러리가 리소스를 효과적으로 활용하도록 해당 협력 기능 호출을 가져야합니다. (? Coinitialize가 모듈에서 호출되고 S_OK를 반환하면 COM 라이브러리를 사용하는 다른 모듈에 COM 라이브러리가 오류가 발생하면 COM 라이브러리가 사용되는 모듈을 자동으로 확인합니까?) --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 라이브러리의 메모리 관리 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- COM 구성 요소 프로그램 및 클라이언트 프로그램은 이진 수준 표준을 통해 연결되므로 COM 애플리케이션에서 클라이언트, COM 라이브러리 및 구성 요소 (할당 및 릴리스) 간의 메모리 상호 작용과 관련된 모든 작업은 일관된 메모리 관리자를 사용해야합니다. COM이 제공하는 메모리 관리 표준은 실제로 IMALLOC 인터페이스입니다. // IID_IMALLOC : {00000002-0000-0000-C000-0000000046} 클래스 imalloc : public iunknown { void * alloc (ulong cb) = 0; void * realloc (void * pv, ulong cb) = 0; void free (void *pv) = 0; ulong getsize (void *pv) = 0; // 할당 된 메모리 크기를 반환합니다 int didalloc (void *pv) = 0; void heapminimize () = 0; // 성능 최적화를위한 운영 체제 } imalloc 인터페이스 포인터를 얻으십시오. Hresult CogetMalloc (dword dwmemcontext, imalloc ** ppmalloc); CogetMalloc 함수 DWMemContext의 첫 번째 매개 변수는 메모리 관리자의 유형을 지정하는 데 사용됩니다. COM 라이브러리에는 두 개의 메모리 관리자가 포함되어 있으며, 하나는 초기화 중에 지정된 메모리 관리자 또는 작업 관리자 (작업 할당)로도 유효합니다 MEMCTX_TASK DWMEMCONTEXT 매개 변수의 MEMCTX_TASK는 OLE 시스템에서 제공하는 크로스 프로세스 공유 할당 자입니다. 이 메모리를 두 번째 프로세스에서 사용하거나 공개하는 두 번째 프로세스로 전달하고 전달했습니다. 함수의 반환 값이 S_OK 인 경우 PPMalloc은 COM 라이브러리의 메모리 관리자 인터페이스 포인터를 가리키며 사용 후에는 릴리스 멤버 기능을 호출하여 릴리스 제어를 수행해야합니다. COM 라이브러리는 메모리 할당 및 해제에 사용할 수있는 세 가지 API 기능을 캡슐화합니다. void * cotaskmemalloc (ulong cb); void cotaskfree (void *pv); void cotaskmemrealloc (void *pv, ulong cb); 이 세 가지 기능은 IMALLOC에 해당하는 3 개의 멤버 기능에 할당됩니다 : Alloc, Realloc 및 Free. 예 : COM 프로그램이 CLSID 값에서 해당 프로위 값을 찾는 방법 : WCHAR *PWPROGID; char pszprogid [128]; hresult = :: progidfromclsid (clsid_dictionary, & pwprogid); if (hresult! = s_ok) { ... } wcstombs (pszprogid, pwprogid, 128); COTASKMEMEMFREE (PWPROGID); COM 라이브러리가 출력 변수 pwprogid에 대한 메모리 공간을 할당하기 때문에 COM 함수 progidfromclsid를 호출 한 후, 응용 프로그램은 pwprogid 변수를 사용한 후 CotaskMemfree 함수를 호출해야합니다. 이 예제는 COM 라이브러리에 메모리가 할당되고 호출 프로그램에서 메모리가 해제되는 상황을 보여줍니다. COM 라이브러리의 다른 기능들도 비슷한 특성, 특히 무기한 길이 출력 매개 변수를 포함하는 일부 기능을 가지고 있습니다. --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- 구성 요소 프로그램로드 및 제거 --------------------------------------------------------- --------------------------------------------------------- --------------------------------------------------------- ---------------------------- 프로세스에있는 구성 요소 로딩 : 客户程序调用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 =================================================== ================================================== ========== ⊙ 结束 =================================================== ================================================== ========== |