解析包含parray作为BSTR'的安全数组的COM[out]变量*;c#net中的s

解析包含parray作为BSTR'的安全数组的COM[out]变量*;c#net中的s,c#,c++,com,atl,C#,C++,Com,Atl,问题:我有一个方法为IDL的COM服务器: [id(2), helpstring("method ExtractAvailableScanners")] HRESULT ExtractAvailableScanners( [in] VARIANT scanFilter, [out] VARIANT* scanPresent, [out,retval] LONG* retVal); 在头文件中,它变成: STDMETHO

问题:我有一个方法为IDL的COM服务器:

  [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::CollectDeviceString(CComVariant*pList) { HRESULT hr=S_正常; 布尔-布雷特=假; HRESULT hres=S_正常

// 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;
关键是“out-oResult”对象都不起作用


任何建议都是受欢迎的。

< P>看起来你的C++实现是错误的。你不设置任何代码> ReValue/COD>任何地方,也就是你复制错误的值到<代码> ScStime。调用代码不知道你使用了<代码>新< /COD>分配它,并且因为它是C,所以它甚至没有办法释放它。首先,您可以使用
VariantInit
(该
CComVariant
是一个包装器)分配变量,然后直接将字段复制到结果参数中。此外,我看不出您是如何为返回创建安全数组的

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;
}

看起来你的C++实现是错误的。你不设置任何代码> ReValue/Cube >,也就是你把错误的值复制到<代码> ScScVest[/COD]。调用代码不知道你使用了<代码>新的< /CODE >分配它,并且因为它是C*,它甚至无法释放它。通常,你分配变量。使用

VariantInit
CComVariant
是一个包装器),然后直接将字段复制到结果参数中。此外,我看不出您是如何为返回创建安全数组的

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;
}

请查看[1800信息]的优秀帖子以供参考

让我澄清几个细节。当他说:

  // why set this here?
  scanners.vt = VT_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;

请查看[1800信息]的优秀帖子以供参考

让我澄清几个细节。当他说:

  // why set this here?
  scanners.vt = VT_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;

我会尝试:pScanners->vt=vt|u SAFEARRAY | vt|BSTR;我会尝试:pScanners->vt=vt|u SAFEARRAY | vt|BSTR;详细信息在类内。正如你看到的,我在这个网站上是全新的,我不知道如何扩展问题的详细信息。retVal中的信息对问题没有影响。你的其他评论是有效的。C#“out oResult”在这个特定的例子中没有以任何方式初始化,并且-是的-我已经使用new将parm放在堆上。如果我知道对象或结果的类型,我应该能够将其用作变量结构,并执行通常的操作,例如设置scanPresent->vt=vt_SAFEARRAY,然后按照通常的方式使用BSTR加载它。但是这是如何实现的呢?我认为您对new的处理有点不清楚。Variatinition()不分配任何内容;它只是初始化现有存储。在这种情况下,PSCanner是本地的,因此Softbee可以自由使用new,如果他选择(我不会),但他必须像往常一样在返回之前删除它。如果他真的需要返回堆分配的变体(如有VARIANT**out参数),则必须使用CoTaskMemAlloc()分配空间,然后使用它调用VariantInit。如果要编辑问题,请单击“编辑”按钮-靠近问题结束和答案开始的底部。详细信息在课堂内容中。正如你所看到的,我在这个网站上是全新的,我不知道如何扩展问题的详细信息。retVal中的信息对问题没有影响。你的其他评论是有效的。C#“out oResult”在这个特定的例子中没有以任何方式初始化,并且-是的-我已经使用new将parm放在堆上。如果我知道对象或结果的类型,我应该能够将其用作变量结构,并执行通常的操作,例如设置scanPresent->vt=vt_SAFEARRAY,然后按照通常的方式使用BSTR加载它。但是这是如何实现的呢?我认为您对new的处理有点不清楚。Variatinition()不分配任何内容;它只是初始化现有存储。在这种情况下,PSCanner是本地的,因此Softbee可以自由使用new,如果他选择(我不会),但他必须像往常一样在返回之前删除它。如果他真的需要返回堆分配的变体(如有VARIANT**out参数),则必须使用CoTaskMemAlloc()分配空间,然后使用它调用VariantInit。如果要编辑问题,请单击“编辑”按钮-靠近问题结束和答案开始的底部。通过添加内部,您可以看到PSCanner携带调用方结果中所需的信息。问题仍然是,在传递给COM时对象的类型应该是什么,应该是scanPresent(如果其类型是变体*)然后,如果指针的传输不合法,它本身就会被填充答案。感谢您一直支持我。添加了CInternals后,您可以看到PSCanner携带调用方结果中所需的信息。问题仍然是,传递给COM的对象的类型应该是什么(如果它的类型是VARIANT*)本身,那么如果指针的传输是不合法的,那么就用答案填充它。我感谢您在这方面与我在一起。