2009-06-08 11 views
1

これはかなり簡単です。 ByRefパラメータを使用して3つの変数を同時に返す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の新しい値をピックアップしていないようです。

dim bAllShared, bAllCopy, bAllRunOnly 
bAllShared = true 
bAllCopy = true 
bAllRunOnly = true 
m_oReportManager.GetReportAccessRights CLng(m_lRptCod), CBool(bAllShared), CBool(bAllRunOnly), CBool(bAllCopy) 
'bAllShared always equals true 

これを解決する方法はありますか?誰でもこの方法でなぜ動作するのか説明できますか?

答えて

4

は二つの問題があります:彼らはC++のコードを入力VARIANTである場合を除き

まず、あなたは、VBScriptのから[ref]パラメータとして渡さ値を取得することはできません。

VBScriptはCOMオートメーションと呼ばれるレイトバインディングテクノロジを使用します。このテクノロジは、COMオブジェクトへのすべてのメソッド呼び出しを単一の汎用メソッド呼び出し、つまりIDISPATCH:Invoke(...)によってルーティングします。 (Visual Basicでは、変数As ObjectをDimして呼び出しを行うときに同じ技術を使用します)

Invoke()は、呼び出しているメソッドの名前である文字列とそれ以外のものここで重要です)。

あなたのC++オブジェクトは心配する必要はありません。なぜならATLはDual Interfaceと呼ばれるものをサポートしているからです。

  • 要求されたメソッド名を検索し、自分のクラスに対応するメソッドを識別し

    (それが存在する場合には、それ以外の場合は、VBScriptで戻ってエラーがスローされます):あなたのオブジェクトはIDISPATCH:Invoke()にコールを受信すると、ATLはなります。
  • VARIANT(技術的にはVARIANTARG、ほぼ同じ)からメソッドの署名に従って適切なデータ型に変換します(メソッドが期待するものと一致しない場合はエラーが発生します)
  • パッケージ化されていないパラメータを使用してGetReportAccessRights()メソッドを呼び出します。あなたのGetReportAccessRights()メソッドの戻りは、ATLは新しいVARIANT(techincally VARIANTARG)へ[retval]パラメータを再パッケージとVBScriptに戻りますことを

さて、あなたはだけでなく、バ​​ック[ref]値を渡すことができますが、彼らはにはVARIANT sとしてい。 ATLは[retval]以外のパラメータ値を再パッケージ化しないので、[ref]引数にはVARIANT *の型を使用して呼び出し元に戻します。そうすると、ATLはパラメータをそのままにして、VBScriptはそれを正しく受信します。

は、バリアントを操作するには、COMヘッダは、私がここに使用しますこれは、便利なマクロと定数を提供してくれます(VT_BOOL、V_VT()、V_BOOL()は、FAILED()):

// 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と同じように動作することを期待するので、自分自身を変換してみてください。幸いなことに、COMは、そのための素晴らしいユーティリティAPIを持っています

// 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) 

引数CBool(something)などを渡すので、実際の変数ではなく一時変数(CBool​​(...)の戻り値)を戻していますbAllSharedなどです。正しいC++実装であっても、返された値は中間値として破棄されます。

次のようなメソッドを呼び出す必要があります:そうですね

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

を。値を「変換する」必要はありません。 VBScriptはあなたが何をしても常にVARIANTを渡します。上記のように、bool型などの入力パラメータであっても、ATLはCBool()を呼び出します。

ATLはCBool​​を()呼び出し?はい?VBScript関数ということではなく、CBool​​は()VariantChangeType()周りの単純なラッパーであり、それはATLがあなたのために呼ぶものです)

編集: VBScriptは[out]パラメータをサポートしていません。 [ref]パラメータのみ。 C++でパラメータを[out]と宣言しないでください。メソッドが[out]パラメータを宣言する場合、VBScriptは[ref]パラメータのように動作します。これにより、パラメータの入ってくる値が漏れることになります。 [out]引数の1つがもともと文字列を持っていた場合、そのメモリはリークされます。オブジェクトがあれば、そのオブジェクトは決して破壊されません。

+0

恐ろしい返信です!ありがとう。 – ssorrrell

+0

+1は非常に徹底的な答えです。 – Tester101

0

この場合実装された別の厄介な解決策は、VB6を使用してC++関数呼び出しをラップし、3つの参照変数をVB6 COMオブジェクトの関数として提供することでした。

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 
関連する問題