여러 개체에 대한 속성 설정
일부 디바이스 드라이버는 단일 함수 호출에서 여러 개체에 대한 설정 속성을 지원합니다. 이를 대량 쓰기라고 합니다. 애플리케이션은 다음 표에 설명된 인터페이스를 사용하여 대량 쓰기를 수행할 수 있습니다.
인터페이스 | Description |
---|---|
IPortableDeviceContent 인터페이스 | 콘텐츠별 메서드에 대한 액세스를 제공합니다. |
IPortableDeviceProperties 인터페이스 | 속성별 메서드에 대한 액세스를 제공합니다. |
IPortableDevicePropertiesBulk 인터페이스 | 대량 쓰기 작업을 지원합니다. |
IPortableDevicePropVariantCollection 인터페이스 | 대량 작업에 대한 개체 식별자를 저장하는 데 사용됩니다. |
IPortableDeviceValuesCollection 인터페이스 | 작성할 속성을 식별하는 데 사용됩니다. |
샘플 애플리케이션의 ContentProperties.cpp 모듈의 함수는 WriteContentPropertiesBulk
대량 쓰기 작업을 보여 줍니다.
이 샘플에서 수행된 첫 번째 작업은 지정된 드라이버가 대량 작업을 지원하는지 여부를 결정하는 것입니다. 이 작업은 IPortableDeviceProperties 개체에서 QueryInterface를 호출하고 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");
}
}
다음 작업에서는 IPortableDeviceValuesCollection 개체를 만들어야 합니다. 샘플에서 쓸 속성 값이 들어 있는 개체입니다.
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);
}
}
이 후 샘플은 IPortableDevicePropertiesBulkCallback 인터페이스의 instance 만듭니다. 애플리케이션은 이 인터페이스의 메서드를 사용하여 비동기 대량 쓰기 작업의 진행률을 추적합니다.
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);
}
}
샘플 애플리케이션에서 호출하는 다음 함수는 도우미 함수입니다 CreateIPortableDevicePropVariantCollectionWithAllObjectIDs
. 이 함수는 지정된 디바이스의 모든 개체를 재귀적으로 열거하고 찾은 각 개체에 대한 식별자가 포함된 IPortableDevicePropVariantCollection 인터페이스 를 반환합니다. 이 함수는 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);
}
IPortableDevicePropVariantCollection 개체는 동일한 VARTYPE의 인덱싱된 PROPVARIANT 값 컬렉션을 보유합니다. 이 경우 이러한 값에는 디바이스에 있는 각 개체에 대해 지정된 개체 식별자가 포함됩니다.
개체 식별자와 해당 이름 속성은 IPortableDeviceValuesCollection 개체에 저장됩니다. 이름 속성은 첫 번째 개체에 "NewName0"의 이름 속성이 할당되고, 두 번째 개체에는 "NewName1" 등의 이름 속성이 할당되도록 구성됩니다.
샘플에서 발췌한 다음 예제에서는 개체 식별자 및 새 이름 문자열을 사용하여 IPortableDeviceValuesCollection 개체를 초기화하는 방법을 보여 줍니다.
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);
}
}
샘플이 개체 식별자와 이름 쌍을 포함하는 IPortableDeviceValuesCollection 개체를 만들면 비동기 작업을 시작할 수 있습니다.
비동기 쓰기 작업은 샘플이 IPortableDevicePropertiesBulk::QueueSetValuesByObjectList 메서드를 호출할 때 시작됩니다. 이 메서드는 대량 작업이 시작된다는 것을 드라이버에 알 수 있습니다. 그런 다음 샘플은 IPortableDeviceBulk::Start 메서드를 호출하여 실제로 새 이름 값 작성을 시작합니다.
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);
}
}
샘플은 작업이 완료되기까지 무한히 긴 시간을 기다립니다. 프로덕션 애플리케이션인 경우 코드를 수정해야 합니다.
관련 항목