2017-12-03 90 views
0

다음과 같은 서명을 사용하여 C++로 작성된 COM 개체가 있습니다. variant에 BSTR (VT_BYREF | VT_BSTR이 아니라 VT_BSTR 만 포함)이 있다고 가정합니다.입력 BSTR VARIANT에 대해 VariantChangeType을 호출 할 수 있습니까?

HRESULT myfunc(/*[in]*/ VARIANT param) 

유형을 다른 것으로 변경하고 싶습니다. VariantChangeType의 첫 번째 매개 변수가 두 번째 매개 변수와 동일하면 "변형이 제자리에서 변환됩니다."

그래서 변환 할 수 있습니까?

HRESULT myfunc(/*[in]*/ VARIANT param) 
{ 
    VariantChangeType(&param, param, 0, VT_I4); 
} 

아니면 두 번째 변형으로 복사해야합니까?

HRESULT myfunc(/*[in]*/ VARIANT param) 
{ 
    VARIANT temp; 
    VariantInit(&temp); 
    VariantChangeType(&temp, param, 0, VT_I4); 
} 

나의 이해는 전자가 클라이언트에 의해 소유되고 클라이언트에 의해 해제해야 BSTR을 무료 것이기 때문에 후자가 필요하다는 것입니다.

+0

아니요, VariantChangeType()은 결과를 다른 변형으로 저장합니다. 첫 번째 인수로 전달한 것. 그래서 BSTR을 공개 할 이유가 전혀 없습니다. –

+0

@HansPassant VariantChangeType()에 대한 첫 번째 매개 변수는 두 번째 매개 변수와 동일 할 수 있습니다. 즉, variant가 제자리에서 변환 될 것임을 의미합니다. 그래서, 아마도 더 나은 진술, 내가 자리를 변환하거나 두 번째 변형으로 변환해야합니까? 내가 명확하게 질문을 업데이 트했습니다. – jveazey

+1

FWIW, 문제를 설명하기위한 요지를 만들었습니다. https://gist.github.com/Neuroboy23/efb3d45783faf03bd87ee2a69519ce84 –

답변

2

VariantChangeType을 사용하면 명백하지 않을 수도 있지만 두 번째 변형이 필요합니다.

variant가 값으로 전달 되더라도 variant에 포함 된 모든 포인터는 동일한 메모리 주소를 가리 킵니다. BSTR은 포인터이므로 매개 변수가 VARIANT 대신 BSTR 인 것처럼 BSTR의 원래 주소가 함수에 전달됨을 의미합니다. VariantChangeType (현재 위치에서) 또는 VariantClear가 (호출 측이 소유) 원래 변종은 여전히 ​​BSTR의 주소를 포함하지 않는하지만 주소가 더 이상 BSTR을 보유하고 있다는 것을 의미 SysFreeString을 트리거 사용

. "Variant Manipulation Functions" 문서에서

...

해제 또는 VT_BSTR 유형 변형의 유형을 변경, SysFreeString가 포함 된 문자열이라고합니다.

내가 분명히 밝히지 않은 이유는 위에서 설명한 모든 내용이 그렇지 않아도이 코드가 작동하는 것입니다.

#ifndef WIN32_LEAN_AND_MEAN 
#define WIN32_LEAN_AND_MEAN 
#endif // !WIN32_LEAN_AND_MEAN 
#include <Windows.h> 
#include <OleAuto.h> 

HRESULT myfuncbad(/*[in]*/ VARIANT param) 
{ 
    // In-place conversion 
    VariantChangeType(&param, &param, 0, VT_I4); 
    return S_OK; 
} 

HRESULT myfuncgood(/*[in]*/ VARIANT param) 
{ 
    VARIANT temp; 
    VariantInit(&temp); 
    // Copy and convert into a new VARIANT 
    VariantChangeType(&temp, &param, 0, VT_I4); 
    VariantClear(&temp); 
    return S_OK; 
} 

int main() 
{ 
    VARIANT input; 
    VariantInit(&input); 
    V_BSTR(&input) = SysAllocString(L"1"); 
    V_VT(&input) = VT_BSTR; 

    myfuncgood(input); 
    wprintf(L"Memory location of BSTR = 0x%x\n", (unsigned)(V_BSTR(&input))); 
    wprintf(L"Contents of BSTR = %s\n", V_BSTR(&input)); 

    myfuncbad(input); 
    wprintf(L"Memory location of BSTR = 0x%x\n", (unsigned)(V_BSTR(&input))); 
    wprintf(L"Contents of BSTR = %s\n", V_BSTR(&input)); 
} 

다음

Memory location of BSTR = 0x2d1af0c 
Contents of BSTR = 1 
Memory location of BSTR = 0x2d1af0c 
Contents of BSTR = 1 

그런데 왜 같은이 코드가 실행되고 출력 무엇인가? 그게 BSTR allocations are cached으로 밝혀졌습니다. 따라서 VariantChangeType (in-place) 또는 VariantClear이 호출되는 경우에도 BSTR 할당은 잠시 동안 지속될 수 있습니다.이러한 함수에 전달 된 변형이 즉시 명백 해지지만 변형의 "값 별"복사본에 BSTR이 계속 표시 될 수 있습니다.

BSTR은 기술적으로는 myfuncbad에 의해 해제되었으며 더 이상 호출자가 참조하지 않아야합니다. 또한 원래 변형에 VariantClear을 호출하면 오류가 발생할 수 있습니다.

추가 독서는 인플레 이스 변환 저장입니다

+2

여기서 중요한 것은/* [in] */annotation입니다. 그것은 idl이 가지고 있다는 것을 의미하므로 전달 된 버퍼는 여러분의 것이 아닙니다. 피 호출자에게 읽기 전용입니다. 따라서 버퍼를 변경할 수 없습니다. 그게 다에요. –

-1

내가 "구성 요소 개체 모델의 규칙"을 읽을 때 복사본을 만들 안전 할 것이나, (https://msdn.microsoft.com/en-us/library/ms810016.aspx) : 다음 규칙 •

는 반환 값을 포함한 멤버 함수를, 인터페이스 매개 변수에 적용◦ 매개 변수가있는 경우 호출자는 메모리를 할당하고 해제해야합니다.

당신이 이야기하는 사례는 값에 의한 전달입니다 (이 호출에 대해 BSTR을 포함하는 VARIANT와 관계 없음). 따라서이 경우 호출 수신자가 매개 변수를 소유하고 있다고 믿습니다. 호출자가 값의 지속적인 실행 가능성을 보장하려면 사본을 만드는 것은 호출자의 몫입니다.

+0

BSTR은 SysAllocString에 의해 할당 된 포인터입니다. VT_BYREF가 아니더라도 참조 용입니다. 따라서 기술적으로 "가치에 의해"전달되지 않습니다. – jveazey

+1

나는 이것에 @veveazey와 함께있다. 호출자는 항상 'VARIANT'할당을 해제하려고합니다. 포함 된 메모리가 이미 할당 취소 된 경우 충돌이 발생합니다. –

-1

. 당신은 일시적인 변형이 필요 없습니다.

나는 이런 식으로 내 ATL의 COM의 모든 코드에서 사용 :

CComVariant v; 
GetSomeData(v); // Assume v returns a VT_BSTR variant. 
HRESULT hr = v.ChangeType(VT_I4); 
if (FAILED(hr)) 
    ... 

이 코드는 논의 된 방법으로 인플레 이스 변환으로 변환합니다. 이전에 VARIANTBSTR 사용 카운트 값이 감소되기 전에 내부적으로 결과는 VarI4FromBSTR으로 계산됩니다.

이 문제에 대해 잘 모르기 때문에 일부 디버그 세션에서이를 확인했습니다.

편집 마지막으로 나는 MSDN that confirms this에있는 진술을 발견했습니다.

VT_BSTR의 경우 문자열의 소유자는 하나뿐입니다. 변형의 모든 문자열은 SysAllocString 함수로 할당해야합니다. VT_BSTR 유형의 변형 유형을 해제하거나 변경하면 포함 된 문자열에서 SysFreeString이 호출됩니다.

코드가 @jveazey의 대답에서 작동한다는 것은 BSTR 캐싱과 아무 관련이 없습니다. 실제 현장 변환이 있습니다!

+1

불행히도 이것은 위험한 답변입니다.당신이 제공하는 예제에서는 메소드 호출로부터'VARIANT'를 받기 때문에 안전합니다. - 메소드 호출이 완료되고,'VARIANT'의 소유자와 그것의 포함 된'BSTR'이 호출자이고, 괜찮 으면 호출자가 변환하려고하는 경우. 그러나 OP의 질문은 'VARIANT'와 BSTR이 포함 된 상황에 관한 질문이다. 이 경우 호출자가 소유 한 대상의 할당을 취소 할 것이므로 (나중에 호출자를 다시 할당하려고 시도 할 것이므로) 유형을 변경하는 것이 안전하지 않습니다. –

+0

MSDN 명시 적으로 "릴리스 또는 VT_BSTR 형식으로 변형 형식을 변경할 때 SysFreeString 포함 된 문자열에 대해 호출됩니다." 이 예제의 호출 수신자 (myfuncbad)는 호출자가 소유 한 것을 해제하기 때문에 좋지 않습니다. – jveazey

+0

내 대답을 확인하는 MSDN 링크가 추가되었습니다! – xMRi