Delphi / C ++ Builder COM的可选参数

时间:2014-06-30 13:21:53

标签: delphi com c++builder variant

我正在尝试调用COM对象的方法。方法(在OLEView中)如下所示: -

    VARIANT_BOOL GetValues(
                    [out] rsValues** values, 
                    [in, optional] rsCategory* category, 
                    [in, optional] rsProcess* process);

最后两个参数是可选的,我想从调用中“省略”它们(只是传递NULL不起作用)。

我的理解是Delphi / C ++ Builder类型库导入器生成的所有COM包装器都会将每个传递的参数转换为变量。但是为了传递一个'省略'参数,我想我必须像这样构造一个特殊的Variant。

  varOpt.vt = VT_ERROR;
  varOpt.sCode = DISP_E_PARAMNOTFOUND;

这似乎意味着我无法使用任何自动生成的生成组件包装器。

如何(使用C ++ Builder Delphi - 我应该能够将Delphi转换为C ++)我可以调用该方法,并传递一个“省略”的变体吗?

更新

以下是我按照David Heffernan的回答中的建议尝试调用该方法的方法。

procedure TForm38.Wibble(obj : IrsObject);
var vals : IrsValues;
begin
  ps.GetValues(vals, emptyparam, emptyparam); 

但是这会给出这个错误:

[DCC Error] E2010 Incompatible types: 'IrsCategory' and 'OleVariant'
[DCC Error] E2010 Incompatible types: 'IrsProcess' and 'OleVariant'

由于我使用非“Disp”对象,我相信我正在使用早期绑定。

我试图避免发布45000行foo_TLB.pas文件,但这里(我希望)是相关部分。

IrsCategory = interface;
IrsCategoryDisp = dispinterface;
IrsProcess = interface;
IrsProcessDisp = dispinterface;

// *********************************************************************//
// Interface: IrsObject
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {861817F7-EA49-49D1-89FC-395BCA57E342}
// *********************************************************************//
  IrsObject = interface(IDispatch)
    ['{861817F7-EA49-49D1-89FC-395BCA57E342}']
    ...
    function GetValues(out values : IrsValue; 
                              const Category: IrsCategory; 
                              const Process: IrsProcess):): WordBool; safecall;
    ...
  end;


// *********************************************************************//
// Interface: IrsObjectDisp
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {861817F7-EA49-49D1-89FC-395BCA57E342}
// *********************************************************************// 

IrsObjectDisp = dispinterface ... // basically the same, repeated

2 个答案:

答案 0 :(得分:2)

海森堡选项1

您正在使用自动化界面,可能使用后期绑定的COM,参数都是变体。

EmptyParam传递给这些参数。

function EmptyParam: OleVariant;
     

包含OleVariant,表示双接口上未使用的可选参数。

     

EmptyParam可用于表示其值未分配的可选参数的变量。当编组方法调用的代码需要固定数量的参数时,这很有用,即使其中一些参数是可选的。

     

当编组包含可选参数的接口调用时,即使不使用这些参数,COM也需要这些参数的值。 EmptyParam返回一个OleVariant,您可以将该值作为该值传递,以指示未使用该参数。

海森堡选项2

您正在使用早期绑定的COM,参数是接口。

nil传递给这些参数以省略它们。如果参数是COM接口而不是变体,那么这是您唯一可能的选择。 COM接口变量必须是有效接口或nil

答案 1 :(得分:1)

根据MSDN specification for IDLoptional仅对VARIANTVARIANT*类型的参数有效。

因此IDL中[in, optional] rsCategory* category,的函数定义格式不正确。

假设您必须使用此签名的其他人的对象,我的建议是将其视为[in],即实际创建传递给它的对象(并在之后释放它们)。

您不清楚您是使用早期绑定还是后期绑定。如果接口实际上是双接口,那么通过C ++ Builder你可以做到这两点。 (我不使用Delphi,但可能你也可以在那里做两个)。在C ++ Builder中,当自动生成的后期绑定包装不是optional时,它似乎会忽略VARIANT;我想Delphi会是一样的。

后期绑定通过将VARIANT的列表传递给IDispatch :: Invoke函数来工作,其中每个变量包含一个参数。您可以尝试手动调用此函数的Invoke,绕过包装器。 (查看示例代码的包装器)。

喜欢通过早期绑定传递NULL;如果您正在使用服务器进程中,这可能会有效,服务器直接接收您发送的内容 - 大概是如果他们认为使用optional在这里是正确的,那么他们的服务器代码实际上会支持它。

然而,如果你是在进程外,那么你必须依赖于你的参数的编组,并且(我不确定这一点)但我希望标准的编组可能无法应对尝试省略这个参数。