Question: I have a COM server with a method as IDL:
[id(2), helpstring("method ExtractAvailableScanners")]
HRESULT ExtractAvailableScanners(
[in] VARIANT scanFilter, [out] VARIANT* scanPresent,
[out,retval] LONG* retVal);
In the header file this becomes:
STDMETHOD(ExtractAvailableScanners)
(VARIANT scanFilter, VARIANT* scanPresent, LONG* retVal);
The implementation:
STDMETHODIMP CSBIdentify::ExtractAvailableScanners
(VARIANT scanFilter, VARIANT* scanPresent, LONG* retVal)
{
// TODO: Return the available scanners given a scanner lookup filter
CInternals ints;
//Find all the device strings
CComVariant Result;
ints.CollectDeviceStrings(&Result);
//Extraction of the wanted ones
CComVariant* pScanners = new CComVariant;
pScanners->vt = VT_SAFEARRAY;
ints.FilterScanners(scanFilter, &Result, pScanners);
// Cleanup
// ========
scanPresent = pScanners;
return S_OK;
}
//The class CInternals is added in here to complete the picture
int CInternals::CollectDeviceStrings(CComVariant* pList) { HRESULT hr = S_OK; BOOL bRet = FALSE; HRESULT hres = S_OK;
// Step 3: ---------------------------------------------------
// Obtain the initial locater to WMI -------------------------
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
CError::PresetError( "Failed to create IWbemLocator object in SBIdentify::GetDevices", E_FAIL );
return hres;
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (e.g. Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres))
{
CError::PresetError( "Could not connect to IWbemServices proxy in SBIdentify::GetDevices", E_FAIL );
pLoc->Release();
return hres;
}
// CTraceLog::TraceMsg( "Connected to ROOT\\CIMV2 WMI namespace" );
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
CError::PresetError( "Could not set proxy blanket in SBIdentify::GetDevices", E_FAIL );
pSvc->Release();
pLoc->Release();
return hres;
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// Use WBEM_FLAG_BIDIRECTIONAL flag to ensure the enumerator is resettable
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_PnPEntity"),
WBEM_FLAG_BIDIRECTIONAL | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
CError::PresetError( "Query on Win32_PnPEntity failed in SBIdentify::GetDevices", E_FAIL );
pSvc->Release();
pLoc->Release();
return hres;
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
int n = 0;
CComPtr< IWbemClassObject > pclsObj;
ULONG uReturn = 0;
//Read the list to determine its length
while (pEnumerator)
{
pclsObj = NULL;
hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if(0 == uReturn)
break;
n++;
}
pEnumerator->Reset();
//The full read mechanism
VARIANT Result;
Result.vt = VT_SAFEARRAY | VT_BSTR;
VARIANT* pResult = &Result;
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = n;
LONG ix[] = {0};
int i = -1;
pResult->parray = ::SafeArrayCreate(VT_BSTR, 1, rgsabound);
if(pResult->parray == NULL)
{
CError::PresetError( "SafeArrayCreate() failed in SBIdentify::GetDevices", E_OUTOFMEMORY );
pSvc->Release();
pLoc->Release();
return E_OUTOFMEMORY;
}
while (pEnumerator)
{
pclsObj = NULL;
pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if(0 == uReturn)
break;
i++;
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
if(hr != S_OK)
{
CError::PresetError( "<Get> failed in SBIdentify::GetDevices", hr );
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
return hr;
}
wcout << " Name : " << vtProp.bstrVal << endl;
ix[0] = i;
hr = SafeArrayPutElement(pResult->parray, ix, vtProp.bstrVal);
if(hr != S_OK)
{
CError::PresetError( "SafeArrayPutElement() failed in SBIdentify::GetDevices", hr );
pSvc->Release();
pLoc->Release();
pEnumerator->Release();
return hr;
}
VariantClear(&vtProp);
}
pList->Attach(pResult);
return hr;
}
in the CS file in C#
public void ExtractScanners(ref ListBox listBox1)
{
String[] oNames = {"LS1/LiteUe", "Sagem"};
// object oResult = new IntPtr(Int32);
// Object oGeneric;// = new object();
// System.Array oResult;
// IntPtr i = (IntPtr)8;// 27;
// Object oResult = Marshal.GetObjectForNativeVariant(i);
// Object oResult;// = null;
// String[] oResult;
// IntPtr oResult;
try
{
iRet = myCom.ExtractAvailableScanners(oNames, out oResult);
listBox1.Items.Add("GetAvailableDevices ok");
}
catch (COMException comEx)
{
ReportCOMError(comEx, ref listBox1);
}
catch (ArgumentException argEx)
{
ReportArgError(argEx, ref listBox1);
}
}
The point is that none of the 'out oResult' objects work.
Any advise is welcome.