Peter Peter - 10 days ago 5
Vb.net Question

How to get derived interface without having a property/function that returns it (OLE/COM)

I'm interfacing with a VB application using a C++ client code. I'm totally a novice in this (I'm just a Chemical Engineer), so please excuse me if the question is dumb.

So the following code in VB is required to access "BackDoor" interface, I can get all other interfaces because I follow the hierarchy (like strm = hyApp.ActiveDocument.Flowsheet.Streams.Item(strmName)

But as you can see in below code, the BackDoor interface was equaled to ProcessStream interface!! I don't understand this and don't know how to implement in in C++... Could you please help?

Code in VB:

Function GetMassExergy(strmName As String) As Double

GetMassExergy = EmptyValue_enum.HEmpty 'initialize to empty

Dim strm As ProcessStream

Dim bd As BackDoor

Dim corrNamesVar As TextFlexVariable

Dim corrNames() As String

Dim i As Integer

Dim exergMoniker As String

Dim exerg As RealVariable

Dim Bval As Double

strm = hyApp.ActiveDocument.Flowsheet.Streams.Item(strmName)

bd = strm

corrNamesVar = bd.BackDoorTextVariable("HysysCorrelation.300.[]:Name.0").Variable

corrNames = corrNamesVar.Values

For i = 0 To corrNames.Count - 1

If corrNames(i) = "Mass Exergy" Then

Exit For

End If

Next i

If i = corrNames.Count Then

'failed to find Mass Exergy Correlation

Exit Function

End If

exergMoniker = String.Format("HysysCorrelation.300.{0}:ExtraData.550.0", i)

exerg = bd.BackDoorVariable(exergMoniker).Variable

Bval = exerg.GetValue("kJ/kg")

GetMassExergy = Bval

End Function


Code in C++ :

void ConnectToHYSYS::GetBackDoor() {
//HyStream is already acquired using Hierarchy
IDispatch* hyStream;
// Try to Query BackDoor from hyCase interface
HRESULT hr = hyStream->QueryInterface(__uuidof(hyBackDoor), (void**)&hyBackDoorDisp);

//Last hr returns S_OK
if (SUCCEEDED(hr))
{
cout << "Got the BackDoor safely" << endl;
//Get BackDoor Text Variable,

VARIANT result;
VariantInit(&result);
// Try to Get a property from BackDoor interface (to make sure that it returned //an actual interface)
hr = COMMethod(DISPATCH_PROPERTYGET, &result, hyBackDoorDisp, L"BackDoorTextVariable", 1, "HysysCorrelation.300.[]:Name.0");
CheckForHr(hr);
BackDoorTextVariable = result.pdispVal;
if (SUCCEEDED(hr))
{
cout << "Got the BackDoor Text Variable safely" << endl;

}

if (FAILED(hr)) {
cout << "Couldnt get the BackDoor Text Variable" << endl;

}
}

if (FAILED(hr)) {
cout << "Couldnt get the BackDoor" << endl;
}

}


The following is COMMethod that I use to access properties inside interfaces (it works properly with all other interfaces)

HRESULT ConnectToHYSYS::COMMethod(int nType, VARIANT * pvResult, IDispatch * pDisp, LPOLESTR ptName, int cArgs...)
{
if (!pDisp) return E_FAIL;

va_list marker;
va_start(marker, cArgs);

DISPPARAMS dp = { NULL, NULL, 0, 0 };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
char szName[200];


// Convert down to ANSI
WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);

// Get DISPID for name passed...
HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
if (FAILED(hr)) {
return hr;
}
// Allocate memory for arguments...
VARIANT * pArgs = new VARIANT[cArgs + 1];
// Extract arguments...
for (int i = 0; i < cArgs; i++) {
pArgs[i] = va_arg(marker, VARIANT);
}

// Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs;

// Handle special-case for property-puts!
if (nType & DISPATCH_PROPERTYPUT) {
dp.cNamedArgs = 1;
dp.rgdispidNamedArgs = &dispidNamed;
}

// Make the call!
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, nType, &dp, pvResult, NULL, NULL);
if (FAILED(hr)) {
return hr;
}

// End variable-argument section...
va_end(marker);
delete[] pArgs;
return hr;
}


The line where I make the call is the only one that returns an error "0x80020008 Bad variable type"... I mean last hr line I wrote in COMMethod "hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, nType, &dp, pvResult, NULL, NULL);"

Answer

The VB code that you have is using early binding, and the C++ code that you have is using late binding.

Switch your C++ code to something similar to your VB code, e.g:

void ConnectToHYSYS::GetBackDoor() {
    IDispatch* hyStream = ...;

    // Use an actual hyBackDoor
    hyBackDoor* hyBackDoorDisp;    
    HRESULT hr = hyStream->QueryInterface(IID_PPV_ARGS(&hyBackDoorDisp));

    if (SUCCEEDED(hr)) {
        cout << "Got the BackDoor safely" << endl;

        // From the VB code, it seems BackDoorTextVariable is a TextFlexVariable
        hr = hyBackDoorDisp->get_BackDoorTextVariable(&BackDoorTextVariable);

        CheckForHr(hr);
        if (SUCCEEDED(hr)) {
            cout << "Got the BackDoor Text Variable safely" << endl;
        }

        if (FAILED(hr)) {
            cout << "Couldnt get the BackDoor Text Variable" << endl;
        }
    }

    if (FAILED(hr)) {
        cout << "Couldnt get the BackDoor" << endl;
    }
}

The reason that the dispatch object you get doesn't work is that usually only the first interface is handled by standard dispatchers. Even for custom or manual dispatchers that handle multiple interfaces, usually they don't dispatch hidden (or otherwise for private use) interfaces.