问题:我有一个COM服务器,其方法为IDL:
[id(2), helpstring("method ExtractAvailableScanners")]
HRESULT ExtractAvailableScanners(
[in] VARIANT scanFilter, [out] VARIANT* scanPresent,
[out,retval] LONG* retVal);
在头文件中,这变为:
STDMETHOD(ExtractAvailableScanners)
(VARIANT scanFilter, VARIANT* scanPresent, LONG* retVal);
实施:
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;
}
//此处添加了CInternals类以完成图片
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;
}
在C#的CS文件中
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);
}
}
重点是'out oResult'对象都不起作用。
欢迎任何建议。
答案 0 :(得分:3)
看起来你的C ++实现是错误的。您没有在任何地方设置retVal
,也没有将错误的值复制到scanPresent
。调用代码无法知道您使用new
分配它,并且由于它是C#,即使它已经存在,它也无法释放它。通常,您使用VariantInit
分配VARIANT(CComVariant
是这个的包装器),然后直接将字段复制到result参数中。另外,我看不出你是如何为返回创建安全数组的。
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 Scanners;
// why set this here?
pScanners.vt = VT_SAFEARRAY;
// what does this call do? It should be allocating the new safe array
// using the normal methods for creating safe arrays
ints.FilterScanners(scanFilter, &Result, &Scanners);
// Cleanup
// ========
Scanners.Detach(scanPresent);
// what to put in here?
*retVal = something;
return S_OK;
}
答案 1 :(得分:0)
请查看[1800 INFORMATION]的优秀帖子以供参考。
让我澄清一些细节。当他说:
// why set this here?
scanners.vt = VT_SAFEARRAY;
他在问,因为这还不足以创造一个SAFEARRAY。在不同的函数中初始化类的片段是非常糟糕的做法。无论如何,FilterScanners()需要在内部执行此操作,还有更多:
// Local dimension bounds
// 'x' is the number of dimensions, as in this VB6:
// Dim Abc(a,b,c) 'has three dimensions
SAFEARRAYBOUND sab[x];
// Set the dimensions, as in:
// Dim Abc(0 TO TOTAL_BOUND_0, 0 TO TOTAL_BOUND_1, ...) 'VB6
sab[0].lLbound = 0;
sab[0].cElements = TOTAL_BOUND_0;
sab[1].lLbound = 0;
sab[1].cElements = TOTAL_BOUND_1;
// ... etc.
// This API creates the actual SafeArray in the COM Heap.
// Replace proper VT_VARIANT below with your type
SAFEARRAY * pSA = SafeArrayCreate(VT_VARIANT, x, sab); // x same as before
// Fill-in the elements of the array as required.
// Remember to use SafeArrayAccessData() and SafeArrayUnaccessData()
// Stuff the pointer to the SAFEARRAY in the VARIANT output argument:
// "OR" whatever the type of the array is. Think in VB6 terms!
// Dim scanners(...) As Variant ' VT_SAFEARRAY | VT_VARIANT
// Dim scanners(...) As String ' VT_SAFEARRAY | VB_BSTR
// etc.
VariantInit(pScanners); // Always recommended to clear the VARIANT before using it
pScanners->vt = VT_SAFEARRAY | VT_VARIANT; // set the type
pScanners->pparray = pSA;