Establecer propiedades para varios objetos
Algunos controladores de dispositivos admiten la configuración de propiedades para varios objetos en una sola llamada de función, lo que se conoce como escritura masiva. La aplicación puede realizar una escritura masiva mediante las interfaces descritas en la tabla siguiente.
Interfaz | Descripción |
---|---|
IPortableDeviceContent (Interfaz) | Proporciona acceso a los métodos específicos del contenido. |
IPortableDeviceProperties (Interfaz) | Proporciona acceso a los métodos específicos de la propiedad. |
IPortableDevicePropertiesBulk (Interfaz) | Admite la operación de escritura masiva. |
IPortableDevicePropVariantCollection (Interfaz) | Se usa para almacenar los identificadores de objeto para la operación masiva. |
IPortableDeviceValuesCollection (Interfaz) | Se usa para identificar las propiedades que se van a escribir. |
La WriteContentPropertiesBulk
función del módulo ContentProperties.cpp de la aplicación de ejemplo muestra una operación de escritura masiva.
La primera tarea realizada en este ejemplo es determinar si el controlador especificado admite o no operaciones masivas. Esto se logra llamando a QueryInterface en un objeto IPortableDeviceProperties y comprobando la existencia de IPortableDevicePropertiesBulk.
HRESULT hr = S_OK;
GUID guidContext = GUID_NULL;
CSetBulkValuesCallback* pCallback = NULL;
CComPtr<IPortableDeviceProperties> pProperties;
CComPtr<IPortableDevicePropertiesBulk> pPropertiesBulk;
CComPtr<IPortableDeviceValues> pObjectProperties;
CComPtr<IPortableDeviceContent> pContent;
CComPtr<IPortableDeviceValuesCollection> pPropertiesToWrite;
CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
DWORD cObjectIDs = 0;
if (SUCCEEDED(hr))
{
hr = pDevice->Content(&pContent);
if (FAILED(hr))
{
printf("! Failed to get IPortableDeviceContent from IPortableDevice, hr = 0x%lx\n",hr);
}
}
if (SUCCEEDED(hr))
{
hr = pContent->Properties(&pProperties);
if (FAILED(hr))
{
printf("! Failed to get IPortableDeviceProperties from IPortableDevice, hr = 0x%lx\n",hr);
}
}
if (SUCCEEDED(hr))
{
hr = pProperties->QueryInterface(IID_PPV_ARGS(&pPropertiesBulk));
if (FAILED(hr))
{
printf("This driver does not support BULK property operations.\n");
}
}
La siguiente tarea implica la creación de un objeto IPortableDeviceValuesCollection . Este es el objeto que contiene los valores de propiedad que escribirá el ejemplo.
HRESULT hr = S_OK;
GUID guidContext = GUID_NULL;
CSetBulkValuesCallback* pCallback = NULL;
CComPtr<IPortableDeviceProperties> pProperties;
CComPtr<IPortableDevicePropertiesBulk> pPropertiesBulk;
CComPtr<IPortableDeviceValues> pObjectProperties;
CComPtr<IPortableDeviceContent> pContent;
CComPtr<IPortableDeviceValuesCollection> pPropertiesToWrite;
CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
DWORD cObjectIDs = 0;
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_PortableDeviceValuesCollection,
NULL,
CLSCTX_INPROC_SERVER,
IID_IPortableDeviceValuesCollection,
(VOID**) &pPropertiesToWrite);
if (FAILED(hr))
{
printf("! Failed to CoCreate IPortableDeviceValuesCollection for bulk property values, hr = 0x%lx\n", hr);
}
}
Después de esto, el ejemplo crea una instancia de la interfaz IPortableDevicePropertiesBulkCallback. La aplicación usará los métodos de esta interfaz para realizar un seguimiento del progreso de la operación asincrónica de escritura masiva.
HRESULT hr = S_OK;
GUID guidContext = GUID_NULL;
CSetBulkValuesCallback* pCallback = NULL;
CComPtr<IPortableDeviceProperties> pProperties;
CComPtr<IPortableDevicePropertiesBulk> pPropertiesBulk;
CComPtr<IPortableDeviceValues> pObjectProperties;
CComPtr<IPortableDeviceContent> pContent;
CComPtr<IPortableDeviceValuesCollection> pPropertiesToWrite;
CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
DWORD cObjectIDs = 0;
if (SUCCEEDED(hr))
{
pCallback = new CSetBulkValuesCallback();
if (pCallback == NULL)
{
hr = E_OUTOFMEMORY;
printf("! Failed to allocate CSetBulkValuesCallback, hr = 0x%lx\n", hr);
}
}
La siguiente función a la que llama la aplicación de ejemplo es la CreateIPortableDevicePropVariantCollectionWithAllObjectIDs
función auxiliar. Esta función enumera de forma recursiva todos los objetos de un dispositivo determinado y devuelve una interfaz IPortableDevicePropVariantCollection que contiene un identificador para cada objeto que encontró. Esta función se define en el módulo ContentEnumeration.cpp.
// 7) Call our helper function CreateIPortableDevicePropVariantCollectionWithAllObjectIDs
// to enumerate and create an IPortableDevicePropVariantCollection with the object
// identifiers needed to perform the bulk operation on.
if (SUCCEEDED(hr))
{
hr = CreateIPortableDevicePropVariantCollectionWithAllObjectIDs(pDevice,
pContent,
&pObjectIDs);
}
El objeto IPortableDevicePropVariantCollection contiene una colección de valores PROPVARIANT indexados del mismo VARTYPE. En este caso, estos valores contienen un identificador de objeto determinado para cada objeto encontrado en el dispositivo.
Los identificadores de objeto y sus propiedades de nombre respectivas se almacenan en un objeto IPortableDeviceValuesCollection . Las propiedades de nombre se organizan para que al primer objeto se le asigne una propiedad name de "NewName0", al segundo objeto se le asigna una propiedad name de "NewName1", etc.
En el siguiente extracto del ejemplo se muestra cómo se inicializó el objeto IPortableDeviceValuesCollection con identificadores de objeto y nuevas cadenas de nombre.
HRESULT hr = S_OK;
GUID guidContext = GUID_NULL;
CSetBulkValuesCallback* pCallback = NULL;
CComPtr<IPortableDeviceProperties> pProperties;
CComPtr<IPortableDevicePropertiesBulk> pPropertiesBulk;
CComPtr<IPortableDeviceValues> pObjectProperties;
CComPtr<IPortableDeviceContent> pContent;
CComPtr<IPortableDeviceValuesCollection> pPropertiesToWrite;
CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
DWORD cObjectIDs = 0;
if (SUCCEEDED(hr))
{
hr = pObjectIDs->GetCount(&cObjectIDs);
if (FAILED(hr))
{
printf("! Failed to get number of objectIDs from IPortableDevicePropVariantCollection, hr = 0x%lx\n", hr);
}
}
if (SUCCEEDED(hr))
{
for(DWORD dwIndex = 0; (dwIndex < cObjectIDs) && (hr == S_OK); dwIndex++)
{
CComPtr<IPortableDeviceValues> pValues;
PROPVARIANT pv = {0};
PropVariantInit(&pv);
hr = CoCreateInstance(CLSID_PortableDeviceValues,
NULL,
CLSCTX_INPROC_SERVER,
IID_IPortableDeviceValues,
(VOID**) &pValues);
if (FAILED(hr))
{
printf("! Failed to CoCreate CLSID_PortableDeviceValues, hr = 0x%lx\n", hr);
}
// Get the Object ID whose properties we will set
if (hr == S_OK)
{
hr = pObjectIDs->GetAt(dwIndex, &pv);
if (FAILED(hr))
{
printf("! Failed to get next Object ID from list, hr = 0x%lx\n", hr);
}
}
// Save them into the IPortableDeviceValues so the driver knows which object this proeprty set belongs to
if (hr == S_OK)
{
hr = pValues->SetStringValue(WPD_OBJECT_ID, pv.pwszVal);
if (FAILED(hr))
{
printf("! Failed to set WPD_OBJECT_ID, hr = 0x%lx\n", hr);
}
}
// Set the new values. In this sample, we attempt to set the name property.
if (hr == S_OK)
{
CAtlStringW strValue;
strValue.Format(L"NewName%d", dwIndex);
hr = pValues->SetStringValue(WPD_OBJECT_NAME, strValue.GetString());
if (FAILED(hr))
{
printf("! Failed to set WPD_OBJECT_NAME, hr = 0x%lx\n", hr);
}
}
// Add this property set to the collection
if (hr == S_OK)
{
hr = pPropertiesToWrite->Add(pValues);
if (FAILED(hr))
{
printf("! Failed to add values to collection, hr = 0x%lx\n", hr);
}
}
PropVariantClear(&pv);
}
}
Una vez que el ejemplo crea el objeto IPortableDeviceValuesCollection que contiene los pares de nombre y identificador de objeto, puede comenzar la operación asincrónica.
La operación de escritura asincrónica comienza cuando el ejemplo llama al método IPortableDevicePropertiesBulk::QueueSetValuesByObjectList . Este método notifica al controlador que está a punto de comenzar una operación masiva. Después de esto, el ejemplo llama al método IPortableDeviceBulk::Start para empezar a escribir realmente los nuevos valores de nombre.
HRESULT hr = S_OK;
GUID guidContext = GUID_NULL;
CSetBulkValuesCallback* pCallback = NULL;
CComPtr<IPortableDeviceProperties> pProperties;
CComPtr<IPortableDevicePropertiesBulk> pPropertiesBulk;
CComPtr<IPortableDeviceValues> pObjectProperties;
CComPtr<IPortableDeviceContent> pContent;
CComPtr<IPortableDeviceValuesCollection> pPropertiesToWrite;
CComPtr<IPortableDevicePropVariantCollection> pObjectIDs;
DWORD cObjectIDs = 0;
if (SUCCEEDED(hr))
{
hr = pPropertiesBulk->QueueSetValuesByObjectList(pPropertiesToWrite,
pCallback,
&guidContext);
if(SUCCEEDED(hr))
{
// Cleanup any previously created global event handles.
if (g_hBulkPropertyOperationEvent != NULL)
{
CloseHandle(g_hBulkPropertyOperationEvent);
g_hBulkPropertyOperationEvent = NULL;
}
// In order to create a simpler to follow example we create and wait infinitly
// for the bulk property operation to complete and ignore any errors.
// Production code should be written in a more robust manner.
// Create the global event handle to wait on for the bulk operation
// to complete.
g_hBulkPropertyOperationEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (g_hBulkPropertyOperationEvent != NULL)
{
// Call Start() to actually being the Asynchronous bulk operation.
hr = pPropertiesBulk->Start(guidContext);
if(FAILED(hr))
{
printf("! Failed to start property operation, hr = 0x%lx\n", hr);
}
}
else
{
printf("! Failed to create the global event handle to wait on for the bulk operation. Aborting operation.\n");
}
}
else
{
printf("! QueueSetValuesByObjectList Failed, hr = 0x%lx\n", hr);
}
}
Tenga en cuenta que el ejemplo espera un período de tiempo infinitamente largo para que se complete la operación. Si se trata de una aplicación de producción, el código tendría que modificarse.
Temas relacionados