1
votes

I'm trying to invoke a COM object's method. The method (in OLEView) looks like this:-

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

The last two parameters are optional, and I want to 'omit' them from the call (Just passing NULL doesn't work).

My understanding is that all the COM wrappers generated by the Delphi/C++Builder type library importers will convert each passed parameter to a variant. But to pass an 'omitted' parameter I think I have to construct a special Variant like this.

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

This seems to mean I can't use any of the auto-generated generated component wrappers.

How (using C++Builder or Delphi - I should be able to convert Delphi to C++) can I call the method, and pass an 'omitted' variant?

Update

Here's how I tried to call the method, following the advice in David Heffernan's answer.

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

But this gives this error:

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

As I'm using the non "Disp" object, I believe I'm using early binding.

I'm trying to avoid posting a 45000 line foo_TLB.pas file, but here (I hope) are the relevant sections.

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

2 Answers

2
votes

Heisenberg option 1

You are using an automation interface, perhaps with late bound COM, and the parameters are all variants.

Pass EmptyParam to these parameters.

function EmptyParam: OleVariant;

Contains an OleVariant that represents an unused optional parameter on a dual interface.

EmptyParam can be used for Variants that represent optional parameters whose value is unassigned. This is useful when the code that marshals method calls requires a fixed number of parameters, even if some of them are optional.

When marshaling interface calls that include optional parameters, COM requires a value for those parameters even when they are not used. EmptyParam returns an OleVariant you can pass as that value to indicate that the parameter is not used.

Heisenberg option 2

You are using early bound COM and the parameters are interfaces.

Pass nil to these parameters to omit them. If the parameters are COM interfaces rather than variants, that is the only possible option you have. A COM interface variable must be either a valid interface, or nil.

1
votes

According to MSDN specification for IDL, optional is only valid for parameters of type VARIANT or VARIANT*.

So a function definition in IDL with [in, optional] rsCategory* category, is ill-formed.

Supposing you have to use someone else's object with this signature, my advice would be to treat it as if it were [in], i.e. actually create the objects to pass into it (and release them afterwards).

You weren't clear on whether you are using early-binding or late-binding. Via C++Builder you can do both, if the interface is actually a dualinterface of course. (I don't use Delphi but presumably you can do both there too). In C++Builder the auto-generated late-binding wrappers appear to ignore optional when it is not a VARIANT; I suppose Delphi would be the same.

Late-binding works by passing a list of VARIANTs to the IDispatch::Invoke function, where each variant contains one argument. You could try manually calling Invoke for this function, bypassing the wrappers. (Look in the wrapper for the example code).

Like passing NULL with early binding; this might work if you're using the server in-process, where the server directly receives what you send it - presumably if they thought that using optional was correct here, then their server code will actually support it.

However if you are out-of-process, then you have to rely on marshaling of your parameters, and (I'm not sure about this) but I would expect that standard marshaling may not be able to cope with trying to omit this parameter.