Есть две проблемы:
Во-первых, вы не можете получить значения, переданные обратно как [ref]
параметры из VBScript, если они не имеют тип VARIANT
в коде C ++.
VBScript использует технологию позднего связывания, называемую COM Automation, которая направляет каждый вызов метода к COM-объектам через один общий вызов метода: IDISPATCH:Invoke(...)
. (Visual Basic использует ту же технологию, когда вы уменьшаете значение переменной As Object
и выполняете вызовы для нее)
Invoke()
принимает строку, которая является именем метода, который вы вызываете, и массив параметров (плюс другие вещи, которые здесь не важны).
Ваш объект C ++ не должен беспокоиться об этом, потому что ATL поддерживает нечто, называемое Dual Interface, что сделает всю неприятную работу за вас. Когда ваш объект получает вызов IDISPATCH:Invoke()
, ATL будет:
- Найдите запрошенное имя метода и определите соответствующий метод в вашем классе (если он существует, в противном случае он выдаст ошибку обратно в VBScript).
- При необходимости преобразуйте любые входные параметры из
VARIANT
(технически VARIANTARG
, который почти идентичен) в соответствующий тип данных в соответствии с сигнатурой метода (и выдаст ошибку, если они не соответствуют вашим метод ожидает)
- Вызовите метод
GetReportAccessRights()
с параметрами без упаковки.
Когда ваш метод GetReportAccessRights()
возвращается, ATL перепаковывает параметр [retval]
в новый VARIANT
(технически VARIANTARG
) и возвращает его в VBScript.
Теперь вы также можете передавать [ref]
значений, но они должны быть VARIANT
с. ATL не будет перепаковывать никакие значения параметров, кроме [retval]
для вас, поэтому вы должны использовать тип VARIANT *
для любого аргумента [ref]
, который вы хотите вернуть обратно вызывающей стороне. Когда вы это сделаете, 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 ожидают, что "True" и 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 будет вызывать для вас)
Edit:
Я забыл упомянуть кое-что еще: VBScript НЕ поддерживает [out]
параметров; только [ref]
параметры. НЕ объявляйте ваши параметры как [out]
в C ++. Если ваш метод объявляет [out]
параметры, VBScript будет действовать так, как если бы они были [ref]
параметрами. Это приведет к утечке входящих значений параметров. Если один из аргументов [out] изначально имел строку, эта память будет утечка; если у него есть объект, этот объект никогда не будет уничтожен.