2009-06-01 9 views
0

Question: J'ai un serveur COM avec une méthode comme IDL:Resolve COM [out] VARIANT * contenant parray comme SAFEARRAY de son BSTR en C# .net

[id(2), helpstring("method ExtractAvailableScanners")] 
     HRESULT ExtractAvailableScanners(
       [in] VARIANT scanFilter, [out] VARIANT* scanPresent, 
       [out,retval] LONG* retVal); 

Dans le fichier d'en-tête cela devient:

STDMETHOD(ExtractAvailableScanners) 
    (VARIANT scanFilter, VARIANT* scanPresent, LONG* retVal); 

la mise en œuvre:

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

// la classe CInternals est ajoutée ici pour compléter le tableau

int CInternals :: CollectDeviceStrings (CComVariant * pList) { HRESULT hr = S_OK; BOOL bRet = FAUX; 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; 

}

dans le fichier CS en 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); 
     } 
    } 

Le point est qu'aucun des objets du 'hors oResult' fonctionne.

Tout conseil est le bienvenu.

+0

Je voudrais essayer: pScanners-> vt = VT_SAFEARRAY | VT_BSTR; – arul

Répondre

3

Il semble que votre implémentation C++ soit incorrecte. Vous ne définissez pas retVal n'importe où, vous copiez également la mauvaise valeur dans scanPresent. Le code appelant n'a aucun moyen de savoir que vous l'avez alloué en utilisant new, et puisqu'il s'agit de C#, il n'aurait aucun moyen de le libérer même si c'était le cas. Normalement, vous allouer le VARIANT en utilisant VariantInit (le CComVariant est un wrapper autour de cela), puis copiez directement les champs dans le paramètre résultat. De plus, je ne vois pas comment vous créez le tableau sécurisé pour le retour.

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

Les détails sont en classe CInternals. Comme vous pouvez le voir, je suis tout nouveau sur ce site et je ne sais pas comment développer les détails de la question. L'information dans retVal n'aurait aucun effet sur le problème. Votre autre commentaire est valide Le C# "out oResult" n'a pas été initialisé de quelque façon que ce soit dans cet exemple particulier et -yes- J'ai mis le parm sur le tas en utilisant new. Si je savais quel type d'objet oResult devrait être, je devrais être capable de l'utiliser comme une structure VARIANT et faire l'habituel comme la définition de scanPresent-> vt = VT_SAFEARRAY et ensuite le charger avec les BSTR comme d'habitude. Mais comment cela est-il réalisé ? –

+0

Je pense que votre traitement de la nouvelle est un peu flou. VariantInit() n'alloue rien; il initialise juste le stockage existant. Dans ce scénario, pScanners est un local, donc Softbee est libre d'utiliser new s'il le souhaite (je ne le ferais pas), mais il doit ensuite le supprimer avant de revenir, comme d'habitude.S'il avait vraiment besoin de renvoyer un Variant alloué par tas (comme si vous aviez un argument VARIANT ** out), vous deviez allouer l'espace avec CoTaskMemAlloc(), puis appeler VariantInit avec lui. –

+0

Si vous voulez éditer la question, cliquez sur le bouton "modifier" - il se trouve au bas de l'endroit où la question se termine et les réponses commencent –

0

Veuillez consulter l'excellent article de [1800 INFORMATION] pour référence. Permettez-moi de clarifier quelques détails.

Quand il dit:

// why set this here? 
    scanners.vt = VT_SAFEARRAY; 

Il demande parce que ce n'est pas suffisant pour créer un SAFEARRAY par n'importe quel étirement. C'est vraiment une mauvaise pratique d'initialiser des morceaux d'une classe à différentes fonctions comme ça. FilterScanners() a besoin de le faire en interne de toute façon, plus plus:

// 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; 
+0

Après avoir ajouté CInternals, vous pouvez voir que pScanners contient les informations nécessaires au résultat de l'appelant . La question est toujours, quel devrait être le type de l'objet tel qu'il est passé à COM, devrait scanPresent (si son type est un VARIANT *) lui-même alors être rempli avec la réponse si le transfert d'un pointeur n'est pas légal. Je vous remercie de rester avec moi à ce sujet. –

Questions connexes