Microsoft MVP성태의 닷넷 이야기
VC++: 35. COM 이벤트에서 반환값을 가진 콜백 정의 [링크 복사], [링크+제목 복사],
조회: 22297
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)
(시리즈 글이 2개 있습니다.)
VC++: 35. COM 이벤트에서 반환값을 가진 콜백 정의
; https://www.sysnet.pe.kr/2/0/588

.NET Framework: 102. COM 개체의 이벤트를 구독하는 코드 제작
; https://www.sysnet.pe.kr/2/0/589





COM 이벤트에서 반환값을 가진 콜백 정의


사실, 이벤트라는 개념에서 반환값을 정의하는 것은 옳지 않습니다. 왜냐하면, 해당 반환값이 어떤 이벤트 핸들러에서 설정되었는지 알 수 없고, 또한 어떤 단계에서 반환값을 설정했다고 해도 그다음 이벤트 들에 대해 별도의 행위를 취할 수 있는 방법이 "기본적"으로는 제공되지 않기 때문입니다. (C#이나, ATL COM이나.)

물론, COM 개체(및 C#)에서 반환값을 아주 처리할 수 없는 것은 아닙니다. 자동 생성된 이벤트 코드 처리에 대한 정의를 바꿔주면 위에서 말했던 문제점들을 원하는 대로 바꿀 수 있습니다. 예를 들어, 다음과 같은 이벤트가 IDL 파일에 정의되었다고 가정하는 경우,

[
    uuid(AED7C2E7-C9C2-4D1B-A760-BE1A79C06EC2),
    helpstring("_ITestObjectControlEvents Interface")
]
dispinterface _ITestObjectControlEvents
{
    properties:
    methods:
       [id(0x01)] void OnMethodCalled( [in] int cookie, [out, retval] int *result ); 
};

VS.NET IDE에서 특정 COM 개체에 위의 이벤트를 구현하게 되면 다음과 같은 코드가 자동으로 생성됩니다.

[코드 1: 자동 생성된 이벤트 발생 코드]
HRESULT Fire_OnMethodCalled( int cookie,  int * result)
{
    HRESULT hr = S_OK;
    T * pThis = static_cast<T *>(this);
    int cConnections = m_vec.GetSize();

    for (int iConnection = 0; iConnection < cConnections; iConnection++)
    {
        pThis->Lock();
        CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
        pThis->Unlock();

        IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

        if (pConnection)
        {
            CComVariant avarParams[1];
            avarParams[0] = cookie;
            CComVariant varResult;
            if(result)
            {
                varResult.byref = result;
                varResult.vt = ;
            }

            DISPPARAMS params = { avarParams, NULL, 1, 0 };
            hr = pConnection->Invoke(0x01, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);
        }
    }
    return hr;
}

현재 VS.NET 2008에서 테스트해 보면, 위의 "varResult.vt = ;"와 같은 코드가 생성이 됩니다. 물론, ^^; 컴파일 오류가 발생하는데요. 이를 방지하기 위해서는 다음과 같이 수작업을 해줘야 합니다.

CComVariant avarParams[1];
avarParams[0] = cookie;
CComVariant varResult;
if(result)
{
    varResult.byref = result;
    varResult.vt = VT_I4 | VT_BYREF ;
}




이렇게 정의된 이벤트를 발생시키는 코드는 대강 다음과 같습니다.

STDMETHOD( DoCallback )()
{
    int result = 0;
    this->Fire_OnMethodCalled(5, &result);

    return S_OK;
}

보시는 것처럼, 만약 클라이언트 측에서 이벤트를 여러개 구독해 두었다면 적절한 반환값을 처리하는 것이 불가능합니다. Invoke를 여러 번 하는 동안, 특정 이벤트에서 반환값을 받았다고 해도, 다른 이벤트에 의해서 다시 재설정 될 여지가 있기 때문입니다.

원하는 데로 처리하기 위해서는 "[코드 1]"의 자동 생성된 이벤트 발생 코드를 변경해 주어야 합니다. 물론, 이러한 변경에 대해서는 다음 번에 자동 생성되는 시점에 영향을 받지 않도록 적절하게 구성을 해줘야 합니다.




여기서 하나 생각해 볼 것이 있다면... 반환값에 대한 처리입니다. 아시겠지만, 반환값은 IDL 구문에서 "[out,retval]" 특성이 지정된 변수에 대해 코드상으로 만들어지는데, VS.NET IDE에서 생성되는 자동화 코드는 이에 대해 적절하게 처리를 하지 못하고 있습니다.

"[코드 1]"에서 생성된 코드만 해도,

if (pConnection)
{
    CComVariant avarParams[1];
    avarParams[0] = cookie;
    CComVariant varResult;
    if(result)
    {
        varResult.byref = result;
        varResult.vt = VT_I4 | VT_BYREF;
    }

    DISPPARAMS params = { avarParams, NULL, 1, 0 };
    hr = pConnection->Invoke(...);
}

아쉽게도, 위와 같은 처리는 맞지 않습니다. 반환값을 받기 위해서는 varResult에 포인터를 넣는 코드에 상관없이 다음과 같은 식으로 처리해 줘야 합니다.

if (pConnection)
{
    CComVariant avarParams[1];
    avarParams[0] = cookie;
    CComVariant varResult;

    DISPPARAMS params = { avarParams, NULL, 1, 0 };
    hr = pConnection->Invoke(..., &varResult, ...);

    *result = varResult.intVal;    
}

얼핏 봐서는 varResult의 byref에 result 포인터 값을 넣어두고 Invoke를 호출했기 때문에 굳이 위와 같이 해주지 않고 "[코드 1]"에서와 같이 처리를 해도 되어야 할 것 같은데요. 이 해답은 Invoke의 정의를 보면 알 수 있습니다.

virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke( 
    /* [in] */ DISPID dispIdMember,
    /* [in] */ REFIID riid,
    /* [in] */ LCID lcid,
    /* [in] */ WORD wFlags,
    /* [out][in] */ DISPPARAMS *pDispParams,
    /* [out] */ VARIANT *pVarResult,
    /* [out] */ EXCEPINFO *pExcepInfo,
    /* [out] */ UINT *puArgErr) = 0;

"[out]"으로 명시되어 있죠. 물론, 만약 이 값이 "[in, out]"이었다면 varResult에 미리 포인터를 할당해 두고 호출하는 것이 가능했겠지요.

저 같은 경우에, 최근까지 위와 같은 정도로만 반환값을 받을 수 있을 거라 생각했었는데,,, 얼마 전에 본 코드에서 새로운 방법을 보게 되었습니다.

그 코드에서는 "반환값" 자체도 결국 "인자"의 한 유형이기 때문에 Invoke에 전달되는 인자를 더 늘리는 식으로 해결한 것이었습니다.

if (pConnection)
{
    CComVariant avarParams[2];
    avarParams[1] = cookie;
    CComVariant varResult;
    if(result)
    {
        avarParams[0].byref = result;
        avarParams[0].vt = VT_I4 | VT_BYREF;
    }

    DISPPARAMS params = { avarParams, NULL, 2, 0 };
    hr = pConnection->Invoke(0x01, ..., DISPATCH_METHOD, ¶ms, &varResult, ...);
    int t = 0;
}

재미있는 방법이죠. ^^ params에 대해서는 "[in], [out]"이기 때문에 정상적으로 포인터 result 값이 avarParams[0].byref를 통해 마샬링 되어지고 최종적으로 결과값이 할당이 되어 나오게 됩니다.




지금까지, COM의 이벤트에서 반환값을 처리하는 방법에 대해서 알아봤습니다. 2가지 방법 모두 자동 생성되는 코드를 변경해야 한다는 것을 잊어서는 안됩니다.

다음에는 위의 2가지 방법에 대해 .NET과의 interop을 알아보도록 하겠습니다. ^^



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/28/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 166  167  168  169  170  171  172  173  174  175  [176]  177  178  179  180  ...
NoWriterDateCnt.TitleFile(s)
596정성태10/2/200825416오류 유형: 58. WPF : 드롭다운 유형의 ComboBox가 펼쳐지지 않는 문제
595정성태10/1/200832986디버깅 기술: 16. Watson Bucket 정보를 이용한 CLR 응용 프로그램 예외 분석 [2]
594정성태9/22/200820965.NET Framework: 104. Win32Exception 클래스 소개
591정성태7/24/200817760오류 유형: 57. VS.NET 2008 TFC - 체크인 시에 비프 음과 함께 정지되는 현상
592정성태7/28/200817758    답변글 오류 유형: 57.1. VS.NET 2008 TFC - 체크인 시에 비프 음과 함께 정지되는 현상 [1]
590정성태7/20/200823551.NET Framework: 103. WPF - ControlTemplate을 코드에서 다뤄보기 [1]
589정성태6/17/200820438.NET Framework: 102. COM 개체의 이벤트를 구독하는 코드 제작 [1]
588정성태6/13/200822297VC++: 35. COM 이벤트에서 반환값을 가진 콜백 정의
587정성태6/10/200827084VS.NET IDE: 56. C#에서 아쉬운 __DATE__, __TIME__ 매크로 [2]
586정성태6/4/200824741오류 유형: 56. WPF 디자이너 - The string was not recognized as a valid DateTime [2]
585정성태6/4/200832815.NET Framework: 101. WPF - ActiveX 컨트롤 호스팅하는 방법 [2]
582정성태5/16/200824652오류 유형: 55. Windowless ActiveX controls are not supported
580정성태4/24/200823753VC++: 34. 64비트 윈도우즈에서의 이벤트 후킹
579정성태4/24/200823618VC++: 33. 변환 후의 RGS 파일 내용을 얻는 방법
577정성태4/16/200824486.NET Framework: 100. XML Serializer를 이용한 값 복사 [5]
575정성태4/7/200821620오류 유형: 54. TFS Source Control - 명령을 사용할 수 없음 [2]
574정성태3/31/200819893오류 유형: 53. TFS 연결 오류 - The workspace [...] exists on computer [...]
573정성태3/25/200823622Windows: 31. TS Web Access와 UAC [1]
570정성태3/17/200822992오류 유형: 52. TFS 연결 오류 - TF31001 [2]
569정성태3/16/200823880Team Foundation Server: 24. TFS 2008로 마이그레이션 (2) [2]
566정성태2/28/200825049.NET Framework: 99. AppDomain.GetEntryAssembly()를 우회하는 방법파일 다운로드1
564정성태2/16/200824570Windows: 30. TS Web Access + Vista SP1 [2]
563정성태2/16/200824019오류 유형: 51. Vista(UAC) + 웹 프로젝트 디버깅: System.UnauthorizedAccessException
562정성태2/12/200828396Windows: 29. Windows Server 2008 설치 [4]
561정성태1/10/200822070오류 유형: 50. IE 7 + 잘못된 HTC 파일 경로 = File not found [5]
559정성태1/1/200826985Windows: 28. Vista에서 끌어다 놓기로 GAC 등록하는 방법 [2]
... 166  167  168  169  170  171  172  173  174  175  [176]  177  178  179  180  ...