2009-06-08 5 views
1

아주 간단합니다. ByRef 매개 변수를 사용하여 동시에 세 개의 변수를 반환하는 C++ 함수가 있습니다.클래식 ASP VBScript 매개 변수 ByRef를 COM C++에 전달

STDMETHODIMP CReportManager::GetReportAccessRights(long lReportCode, VARIANT_BOOL *bShared, VARIANT_BOOL *bRunOnly, VARIANT_BOOL *bCopy) 

그러나, VBScript를 ASP 코드는 C++ 함수를 호출 할 때 bShares, bRunOnly 및 bCopy에 대한 새 값을 선택하지 않는 것 같습니다.

해결할 수있는 방법이 있습니까? 아무도 왜 이런 방식으로 작동하는지 설명 할 수 있습니까?

답변

4

이 두 가지 문제가 있습니다 : 그들은 C++ 코드를 입력 VARIANT의하지 않는 한

첫째, 당신은, VBScript를에서 [ref] 매개 변수로 다시 전달 된 값을 검색 할 수 없습니다.

VBScript는 IDISPATCH:Invoke(...)이라는 단일 일반 메서드 호출을 통해 COM 개체에 대한 모든 메서드 호출을 라우팅하는 COM 자동화라는 후기 바인딩 기술을 사용합니다.

Invoke() 당신이 호출하는 메소드의 이름 인 문자열을 사용 및 매개 변수 (플러스 다른 것들의 배열 즉 아닙니다 (당신이 변수 As Object을 어둡게하고 전화를 걸 때 Visual Basic의이 같은 기술을 사용) 여기서 중요한).

당신의 C++ 객체는 ATL이 모든 불쾌한 작업을 수행 할 듀얼 인터페이스라는 것을 지원하기 때문에 걱정할 필요가 없습니다.

  • 요청 된 메소드 이름을보고 클래스에 대응하는 방법을 확인

    (존재하는 경우는, 그렇지 않으면 VBScript를 다시 오류가 발생됩니다) : 개체가 IDISPATCH:Invoke()에 전화를 수신하면, ATL는 것이다.
  • (기술적으로는 VARIANTARG, 거의 동일 함)에서 메서드의 서명에 따라 적절한 데이터 형식으로 변환합니다 (메서드가 예상 한 것과 일치하지 않으면 오류가 발생 함)
  • 패키지되지 않은 매개 변수를 사용하여 GetReportAccessRights() 메서드를 호출하십시오. 당신의 GetReportAccessRights() 방법 반환, ATL 새로운 VARIANT (techincally VARIANTARG)에 [retval] 매개 변수를 재 포장 및 VBScript를 해당 반환

.

지금, 당신은 뿐만 아니라 다시 [ref] 값을 전달할 수 있습니다,하지만 그들은에이 VARIANT들 수있다 . ATL은 [retval] 이외의 매개 변수 값을 다시 패키지하지 않으므로 [ref] 인수에 VARIANT * 유형을 사용해야 발신자에게 되돌려 보낼 수 있습니다. 그렇게하면 ATL에서 매개 변수를 그대로두고 VBScript에서 올바르게 매개 변수를 다시 받게됩니다.

이 변종하려면 COM 헤더는 내가 여기에 사용합니다있는 편리한 매크로와 상수 우리에게 제공 (VT_BOOL, V_VT(), V_BOOL()는 실패()) :

// I usually initialize to Empty at the top of the method, 
// before anything can go wrong. 
VariantInit(bAllShared); 

// My bad -- ignore the above. It applies to [out] parameters only. 
// Because bAllShared is passed as a [ref] variable, 
// calling VariantInit() on them would leak any preexisting value. 
// Instead, read the incoming value from the variable (optional), 
// then "clear" them before storing new values (mandatory): 

// This API figures out what's in the variable and releases it if needed 
// * Do nothing on ints, bools, etc. 
// * Call pObj->Release() if an Object 
// * Call SysFreeString() if a BSTR 
// etc 
VariantClear(bAllShared); 

을 초기화; 이전 값이 유출됩니다.

읽기에 VARIANT :

// Always check that the value is of the proper type 
if (V_VT(bAllShared) == VT_BOOL) { 
    // good 
    bool myArg = (V_BOOL(bAllShared) == VARIANT_TRUE); 
} else { 
    // error, bad input 
} 

또는 더 나은, VBScript를 사용자가 "참"기대 때문에 항상 자신을 변환하려고해야하며, 1은 VARIANT_TRUE 같은 행동.

// This is exactly the same thing that VBScript does internally 
// when you call CBool(...) 
VARIANT v; 
VariantInit(&v); 
if(FAILED(VariantChangeType(&v, &bAllShared, 0, VT_BOOL)) 
{ 
    // error, can't convert 
} 
bool myArg = (V_BOOL(v) == VARIANT_TRUE); 

VARIANT에 쓸 수 :

// Internal working value 
bool isShared; 
... 

// set the Variant's type to VARIANT_BOOL 
V_VT(bAllShared) = VT_BOOL; 

// set the value 
V_BOOL(bAllShared) = (isShared ? VARIANT_TRUE : VARIANT_FALSE); 

이제 두 번째 문제는 샘플 VBScript 코드에 있습니다

m_oReportManager.GetReportAccessRights _ 
    CLng(m_lRptCod), CBool(bAllShared), CBool(bAllRunOnly), CBool(bAllCopy) 

다행히도, COM은 대한 멋진 유틸리티 API를 가지고 CBool(something) 등의 인수로 전달하기 때문에 실제 변수이 아닌 임시 변수 (CBool ​​(...)의 반환 값)를 전달합니다.등 올바른 C++ 구현으로도 반환 값은 중간 값으로 버려집니다.

은이 같은 메소드를 호출해야합니다 맞아

m_oReportManager.GetReportAccessRights _ 
    CLng(m_lRptCod), bAllShared, bAllRunOnly, bAllCopy 

합니다. 값을 "변환"할 필요가 없습니다. VBScript는 사용자의 작업과 상관없이 항상 VARIANT을 전달합니다. 위에서 말했듯이 bool 등의 입력 매개 변수에 대해서도 ATL이 CBool()을 호출하므로 걱정하지 마십시오.

(ATL은 네,하지만 CBool ​​()가 간단한 래퍼 주위 VariantChangeType()입니다? CBool은()?가 아닌 VBScript 함수 것을 호출하고, 그 ATL 당신을 위해 호출 할 것입니다)

편집 : 다른 것을 언급하는 것을 잊어 버렸습니다 : VBScript는 [out] 매개 변수를 지원하지 않습니다. [ref] 매개 변수 만 C++에서 매개 변수를 [out]으로 선언하지 마십시오. 메서드가 [out] 매개 변수를 선언하면 VBScript는 [ref] 매개 변수 인 것처럼 작동합니다. 그러면 들어오는 매개 변수의 값이 유출됩니다. [out] 인수 중 하나가 원래 문자열 인 경우 해당 메모리가 유출됩니다. 그것이 객체를 가지고 있다면, 그 객체는 결코 파괴되지 않을 것입니다.

+0

굉장한 답글 !! 감사. – ssorrrell

+0

+1 매우 철저한 대답. – Tester101

0

이 경우 구현 된 다른 해결책은 VB6을 사용하여 C++ 함수 호출을 래핑하고 VB6 COM 개체의 함수로 3 개의 참조 된 변수를 제공하는 것이 었습니다.

Option Explicit 
Private bSharedaccess As Boolean 
Private bRunOnlyaccess As Boolean 
Private bCopyaccess As Boolean 

Public Sub Initialize(ByVal oSession As Starbridge.Session, ByVal lReportID As Long) 

    bSharedaccess = True 
    bRunOnlyaccess = False 
    bCopyaccess = True 
    Call oSession.ReportManager.GetReportAccessRights(lReportID, bSharedaccess, bRunOnlyaccess, bCopyaccess) 

End Sub 

Public Function GetSharedAccess() 
    GetSharedAccess = bSharedaccess 
End Function 

Public Function GetRunOnlyAccess() 
    GetRunOnlyAccess = bRunOnlyaccess 
End Function 

Public Function GetCopyAccess() 
    GetCopyAccess = bCopyaccess 
End Function