2013-09-23 5 views
-1

저는 구조체를 C++ 클라이언트에서 COM을 사용하는 C++ 서버로 전달하는 방법을 연구했습니다. 나는 많은 예를 발견했으나 실제로 나에게 설명해 주었다. 나는 5가 아니라, 내가 원하는 것을하는 방법에 대한 확고한 이해를 제공했다. COM 인터페이스를 통해 양면이있는 C++ 구조체를 전달하기 만하면된다. C++. 쉬워야 할거야, 그렇지?COM C++ 클라이언트에서 C++ 서버로 포인터를 통해 C++ 서버에 전달

서버 측의 IDL 파일에 다음과 같이 내 구조체를 설립 : 지금까지 내가 말할 수있는 작품

[ 
    uuid(7F0C9A48-3C41-425B-B4E6-8156B61D5355), 
    version(1.0) 
] 
typedef struct xxxData 
{ 
    int iWidth; 
    int iHeight; 
    SafeArray(short) pxxxData; 
} xxxData; 

// Fix for UUID DECLARATION FOR _uuidof() functionality 
// From http://go4answers.webhost4life.com/Example/error-c2787-no-guid-been-associated-158947.aspx 
cpp_quote("struct __declspec(uuid(\"{7F0C9A48-3C41-425B-B4E6-8156B61D5355}\")) xxxData;") 

.

지금 내 클라이언트는 다음과 같이 표시되는 GetImageData를 호출

[id(16)] HRESULT GetImageData([in,out] VARIANT* pData); 

이 기능을 다음과 같이 이제 내 고객 호출은 다음과 같습니다 그러나

VARIANT* pData = new VARIANT; 
VariantInit(pData); 
xxxData* data = new xxxxData; 
HRESULT hr = mpCOMEvents->GetImageData(pData); 
data = (FBIS_ImageData*)(pData->pvRecord); 
int length = data->iWidth * data->iHeight; 

, 길이는 나에게 잘못된 주소 위치를주고있다 . 내 pvRecord의 사용이 올바르지 않은지 궁금하고, 실제로 타입 변환 할 수 있는지 궁금합니다.

xxxData data; 
//SAFEARRAY *psa; 
IRecordInfo *pRI; 
HRESULT hr; 

/* Pass in Structure Information */ 
data.iHeight = 100; 
data.iWidth = 100; 

// Used http://vcfaq.mvps.org/com/4.htm as reference 
hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI); 
VariantInit(pData); 
pData->vt = VT_RECORD; 
pData->pvRecord = &data; 
pData->pRecInfo = pRI; 
pRI = NULL; 
+0

pxxxData 란 무엇입니까? 서버가 out-of-proc이고 서버가 전혀 사용할 수 없으면 서버 주소 공간의 잘못된 메모리 위치를 가리 킵니다. 또한 pImageData 회원 데이터는 무엇입니까? 코드 샘플이 완료되지 않은 것 같습니다 – Andrey

+0

죄송합니다. 코드를 조금 어색하게 수정하고 있습니다. 나는 그것을 위에 고치고 또한 나의 최신 수정을 제공했다는 것을 믿는다. 그 메모에, 당신 말이 맞아. 그것은 out-of-proc입니다. 이를 방지하기 위해 VARIANT *를 사용하여 VARIANT를 출력하는 대신 제안 하시겠습니까? – RootAtShell

답변

3

여기에 약간의 혼동이있다 :

여기 내 COM 서버 측이다. 당신이 자동화 친화를 목표로하지 않는 경우

은에 당신의 IDL을 변경

[size_is=iWidth*iHeight] unsigned short* pxxxData; 

이에 SAFEARRAY API를 사용하지 마십시오. 마샬링을 위해서는 프록시/스텁 DLL을 컴파일하고 등록해야합니다. 당신이 자동화 친화를 목표로하는 경우

은에 당신의 IDL을 변경

SAFEARRAY(short) pxxxData; 

이에 SAFEARRAY API를 사용합니까. 마샬링의 경우 형식 라이브러리를 컴파일하고 (선택적으로 포함) 등록해야합니다. 이렇게하면 초기 바인딩 (예 : VB6, tlbimp)이 가능해집니다.

사용자 정의 유형을 지원하는 언어/환경에서 작동합니다. (스크립팅 언어와 같은) 언어가 아닌 경우 구조체 대신 oleautomation/dual/IDispatch 기반 인터페이스를 사용해야하며 서버에서 구현해야합니다.


편집 : 질문에 대한 변경 사항을 기반으로합니다.

out 매개 변수는 GetImageData으로 지정해야합니다. 사용하지 않고 대체 할 수도 있습니다. 또한 호출이 아닌 리턴시 마샬링 만 필요합니다. 다음은 제안 사항입니다.

[id(16)] HRESULT GetImageData([out] VARIANT* pData); 

클라이언트 코드에 항상 xxxData가 생성됩니다.여기 제안입니다 :

// If pData is in-out, this is not safe, use CoTaskMemAlloc(sizeof(VARIANT)) instead. 
// The callee may override the buffer by assuming it was CoTaskMemAlloc'ed, thus 
// assuming it can CoTaskMemFree the original location and set the pointer to a new 
// CoTaskMemAlloc'ed location. 
// The callee may be a proxy. 
// Assuming it's out only, we can provide any location with enough space for a VARIANT. 
VARIANT vData; 
VariantInit(&vData); 
xxxData* data; // remove memory leak 
HRESULT hr = mpCOMEvents->GetImageData(&vData); 
// error handling removed for clarity (I hope) 
data = (xxxData*)(vData.pvRecord); 
int length = data->iWidth * data->iHeight; 
// ... use data ... 
// Don't forget to clear the variant, or there'll be a memory leak 
// It implies: 
// vData.pRecInfo->RecordDestroy(vData.pvRecord); 
//  This should recursively release memory allocated in each field 
//  and finally release the memory allocated for the struct itself. 
// vData.pRecInfo->Release(); 
VariantClear(&vData); 
// don't use data past this point 

서버 코드가 잠재적 발신자 또는 다른 호출 기능에 의해 덮어 쓰기된다는 것을 의미 스택을 가리 키도록 pData->pvRecord을 설정하는 것입니다. 다음은 제안 사항입니다.

xxxData* data; // Changed to pointer 
IRecordInfo *pRI; 
HRESULT hr; 

// data.iHeight = 100; // removed 
// data.iWidth = 100; // removed 

hr = GetRecordInfoFromGuids(LIBID_xxxLib, 1, 0, 0x409, _uuidof(xxxData), &pRI); 
// error handling removed for clarity (I hope) 
VariantInit(pData); 
// This will allocate memory for the struct itself 
// For fields that require memory allocation, follow "normal" COM rules, 
// such as using CoTaskMemAlloc for buffers, SysAllocString or similar for BSTRs, 
// etc. 
// For each inner (pointed to) structure, you should call RecordCreate on the 
// respective IRecordInfo instance for that type. 
data = (xxxData*)pRI->RecordCreate(); 
data->iHeight = 100; // new 
data->iWidth = 100; // new 
// If pData is in-out, this will leak, use VariantClear instead. 
// Assuming it's out only, use VariantInit as it points to (allocated) garbage. 
VariantInit(pData); 
pData->vt = VT_RECORD; 
pData->pvRecord = data; // data is already a pointer 
pData->pRecInfo = pRI; 
pRI = NULL; 
// This won't (normally) leak, the caller must call VariantClear on the out VARIANT. 
// The caller may be a stub. 
+0

http://vcfaq.mvps.org/com/4.htm의 발자취를 따라 가면서 VARIANT에 구조체를 채우고 VARIANT를 전달하려고합니다. 내가 구조체를 직접 전달했다는 것을 알았지 만 이후 VARIANT 타입으로 변경하고 위 참조에서 코드를 활용했습니다. 이 참조가 보이는 것처럼 쉬운가요? – RootAtShell

+0

질문에있는 현재 코드에 따르면'unsigned short '에 대한 포인터로'struct'를 정의했습니다. 첫째,'unsigned short '는 [automation compatible] (http://msdn.microsoft.com/en-us/library/windows/desktop/aa367129 (v = vs.85) .aspx)로 문서화되어 있지 않습니다. 둘째, 표준 마샬 러는 항상 하나의 요소를 마샬링합니다. 'size_is'를 추가하더라도 [이 특성은 typelib에 반영되지 않았습니다.] (http://support.microsoft.com/kb/236970/en-us). 자동화를 사용하여 배열을 마샬링해야하는 경우 ** 반드시 SAFEARRAY를 사용해야합니다. – acelent

+0

자동화 호환 목록에 대해 감사드립니다. 목록에 대한 참조를 찾으려고했지만 시도 할 수 없었습니다. 즉, 구조체에 xxxData를 저장하기 위해 SafeArray를 사용할 것임을 알았습니다. 한 걸음 더 나아가 자, 나는 위의 코드를 편집하여 이러한 변경 사항을 반영하고 거의 모든 것을 수정했습니다. COM에서 검색 한 후 iHeight 및 iWidth에 제대로 액세스 할 수 없다는 내 의견에 질문이 있습니다. – RootAtShell

관련 문제