Festlegen von Eigenschaften für mehrere Objekte
Einige Gerätetreiber unterstützen das Festlegen von Eigenschaften für mehrere Objekte in einem einzelnen Funktionsaufruf. Dies wird als Massenschreibvorgang bezeichnet. Ihre Anwendung kann einen Massenschreibvorgang mithilfe der in der folgenden Tabelle beschriebenen Schnittstellen ausführen.
| Schnittstelle | BESCHREIBUNG |
|---|---|
| IPortableDeviceContent-Schnittstelle | Bietet Zugriff auf die inhaltsspezifischen Methoden. |
| IPortableDeviceProperties-Schnittstelle | Ermöglicht den Zugriff auf die eigenschaftenspezifischen Methoden. |
| IPortableDevicePropertiesBulk-Schnittstelle | Unterstützt den Massenschreibvorgang. |
| IPortableDevicePropVariantCollection-Schnittstelle | Wird verwendet, um die Objektbezeichner für den Massenvorgang zu speichern. |
| IPortableDeviceValuesCollection-Schnittstelle | Wird verwendet, um die zu schreibenden Eigenschaften zu identifizieren. |
Die WriteContentPropertiesBulk Funktion im ContentProperties.cpp-Modul der Beispielanwendung veranschaulicht einen Massenschreibvorgang.
Die erste Aufgabe in diesem Beispiel besteht darin, zu bestimmen, ob der angegebene Treiber Massenvorgänge unterstützt. Dies wird erreicht, indem QueryInterface für ein IPortableDeviceProperties-Objekt aufgerufen und überprüft wird, ob IPortableDevicePropertiesBulk vorhanden ist.
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");
}
}
Die nächste Aufgabe umfasst das Erstellen eines IPortableDeviceValuesCollection-Objekts. Dies ist das -Objekt, das die Eigenschaftswerte enthält, die im Beispiel geschrieben werden.
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);
}
}
Danach erstellt das Beispiel eine Instanz der IPortableDevicePropertiesBulkCallback-Schnittstelle. Die Anwendung verwendet die Methoden in dieser Schnittstelle, um den Fortschritt des asynchronen Massenschreibvorgangs nachzuverfolgen.
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);
}
}
Die nächste Funktion, die die Beispielanwendung aufruft, ist die CreateIPortableDevicePropVariantCollectionWithAllObjectIDs Hilfsfunktion. Diese Funktion listet rekursiv alle Objekte auf einem bestimmten Gerät auf und gibt eine IPortableDevicePropVariantCollection-Schnittstelle zurück, die einen Bezeichner für jedes gefundene Objekt enthält. Diese Funktion wird im Modul ContentEnumeration.cpp definiert.
// 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);
}
Das IPortableDevicePropVariantCollection-Objekt enthält eine Auflistung indizierter PROPVARIANT-Werte desselben VARTYPE. In diesem Fall enthalten diese Werte einen angegebenen Objektbezeichner für jedes auf dem Gerät gefundene Objekt.
Die Objektbezeichner und ihre jeweiligen Namenseigenschaften werden in einem IPortableDeviceValuesCollection-Objekt gespeichert. Die Namenseigenschaften sind so organisiert, dass dem ersten Objekt die Namenseigenschaft "NewName0" zugewiesen wird, dem zweiten Objekt die Namenseigenschaft "NewName1" usw.
Der folgende Auszug aus dem Beispiel veranschaulicht, wie das IPortableDeviceValuesCollection-Objekt mit Objektbezeichnern und zeichenfolgen neuen Namen initialisiert wurde.
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);
}
}
Nachdem im Beispiel das IPortableDeviceValuesCollection-Objekt erstellt wurde, das die Objektbezeichner- und Namenspaare enthält, kann der asynchrone Vorgang gestartet werden.
Der asynchrone Schreibvorgang beginnt, wenn das Beispiel die IPortableDevicePropertiesBulk::QueueSetValuesByObjectList-Methode aufruft. Diese Methode benachrichtigt den Treiber, dass ein Massenvorgang beginnen wird. Danach ruft das Beispiel die IPortableDeviceBulk::Start-Methode auf, um tatsächlich mit dem Schreiben der neuen Namenswerte zu beginnen.
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);
}
}
Beachten Sie, dass das Beispiel unendlich lange auf den Abschluss des Vorgangs wartet. Wenn es sich um eine Produktionsanwendung handelt, muss der Code geändert werden.