Realización de una llamada semisincrónica con C++

Las llamadas semisincrónicas son los medios recomendados para llamar a los métodos de WMI, como IWbemServices::ExecMethod, y a los métodos de proveedor, como el método Chkdsk de la clase win32\_LogicalDisk.

Una desventaja del procesamiento sincrónico es que el subproceso del autor de la llamada se bloquea hasta que se completa la llamada. El bloqueo puede provocar un retraso en el tiempo de procesamiento. Por el contrario, una llamada asincrónica debe implementar SWbemSink en el script. En C++, el código asincrónico debe implementar la interfaz IWbemObjectSink, usar varios subprocesos y controlar el flujo de información de vuelta al autor de la llamada. Los conjuntos grandes de resultados de las consultas, por ejemplo, pueden tardar una cantidad considerable de tiempo en entregarse y obligan al autor de la llamada a dedicar recursos significativos del sistema para controlar la entrega.

El procesamiento semisincrónico resuelve el bloqueo del subproceso y los problemas de entregas no controladas mediante el sondeo de un objeto de estado especial que implementa la interfaz IWbemCallResult. A través de IWbemCallResult, puede mejorar la velocidad y eficacia de las consultas, enumeraciones y notificaciones de eventos.

En el procedimiento siguiente se describe cómo realizar una llamada semisincrónica con la interfaz IWbemServices.

Para realizar una llamada semisincrónica con la interfaz IWbemServices

  1. Realice la llamada como normal, pero con la marca WBEM_FLAG_RETURN_IMMEDIATELY establecida en el parámetro IFlags.

    Puede combinar WBEM_FLAG_RETURN_IMMEDIATELY con otras marcas válidas para el método concreto. Por ejemplo, use la marca WBEM_FLAG_FORWARD_ONLY para todas las llamadas que devuelven enumeradores. Establecer estas marcas en combinación ahorra tiempo y espacio, y mejora la capacidad de respuesta.

  2. Realice un sondeo para conocer sus resultados.

    Si llama a un método que devuelve un enumerador, como IWbemServices::CreateClassEnum o IWbemServices::ExecQuery, puede sondear los enumeradores con los métodos IEnumWbemClassObject::Next o IEnumWbemClassObject::NextAsync. La llamada a IEnumWbemClassObject::NextAsync no se bloquea y se devuelve inmediatamente. En segundo plano, WMI comienza a entregar el número solicitado de objetos mediante una llamada a IWbemObjectSink::Indicate. A continuación, WMI se detiene y espera otra llamada a NextAsync.

    Si llama a un método que no devuelve un enumerador, como IWbemServices::GetObject, debe establecer el parámetro ppCallResult en un puntero válido. Use IWbemCallResult::GetCallStatus en el puntero devuelto para recuperar WBEM_S_NO_ERROR.

  3. Finalice la llamada.

    Para una llamada que devuelve un enumerador, WMI llama a IWbemObjectSink::SetStatus para notificar la finalización de la operación. Si no necesita el resultado completo, publique el enumerador llamando al método IEnumWbemClassObject::Release. La llamada a Release da como resultado que WMI cancele la entrega de todos los objetos que permanecen.

    Para una llamada que no usa un enumerador, recupere el objeto GetCallStatus mediante el parámetro plStatus del método.

El ejemplo de código de C++ de este tema requiere las siguientes instrucciones #include para compilarse correctamente.

#include <comdef.h>
#include <wbemidl.h>

En el ejemplo de código siguiente se muestra cómo realizar una llamada semisincrónica a GetObject.

void GetObjSemiSync(IWbemServices *pSvc)
{

    IWbemCallResult *pCallRes = 0;
    IWbemClassObject *pObj = 0;
    
    HRESULT hRes = pSvc->GetObject(_bstr_t(L"MyClass=\"AAA\""), 0,
        0, 0, &pCallRes
        );
        
    if (hRes || pCallRes == 0)
        return;
        
    while (true)
    {
        LONG lStatus = 0;
        HRESULT hRes = pCallRes->GetCallStatus(5000, &lStatus);
        if ( hRes == WBEM_S_NO_ERROR || hRes != WBEM_S_TIMEDOUT )
            break;

        // Do another task
    }

    hRes = pCallRes->GetResultObject(5000, &pObj);
    if (hRes)
    {
        pCallRes->Release();
        return;
    }

    pCallRes->Release();

    // Use the object.

    // ...

    // Release it.
    // ===========
        
    pObj->Release();    // Release objects not owned.            
  
}

Llamada a un método