I'm trying to track when a file is created on a WPD-compatible device, such as a digital camera or Android phone. I register to receive events with Advice
and my callback is called correctly, but I can't get the file name (perhaps the OBJECT_NAME) properly. Here is what I have:
TPortableDeviceEventsCallback = class(TInterfacedObject, IPortableDeviceEventCallback)
public
function OnEvent(const pEventParameters: IPortableDeviceValues): HResult; dynamic; stdcall;
end;
.
.
.
function TPortableDeviceEventsCallback.OnEvent(const pEventParameters: IPortableDeviceValues): HResult;
var
ObjName: PWideChar;
begin
pEventParameters.GetStringValue(WPD_EVENT_PARAMETER_OBJECT_NAME, ObjName);
Log(string(ObjName));
end;
I only get garbage instead of the added/removed object name. What am I missing here?
First, OnEvent()
should not be declared as dynamic
. It is already virtual
in IPortableDeviceEventCallback
.
Second, you are not doing any error handling on IPortableDeviceValues.GetStringValue()
, or freeing the memory it returns. It should look more like this:
function TPortableDeviceEventsCallback.OnEvent(const pEventParameters: IPortableDeviceValues): HResult;
var
Hr: HResult;
ObjName: PWideChar;
begin
Hr := pEventParameters.GetStringValue(WPD_EVENT_PARAMETER_OBJECT_NAME, ObjName);
case Hr of
S_OK: begin
try
Log('Object Name: ' + String(ObjName));
finally
CoTaskMemFree(ObjName);
end;
end;
DISP_E_TYPEMISMATCH: begin
Log('Object Name is not a string!');
end;
$80070490: // HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
begin
Log('Object Name is not found!');
end;
else
// some other error
Log('Error getting Object Name: $' + IntToHex(Hr, 8));
end;
Result := S_OK;
end;
Third, you are not looking at the value of the WPD_EVENT_PARAMETER_EVENT_ID
parameter (which is the only required parameter) to know what event you are receiving in order to know what parameters are available with it. Different events have different parameter values.
Try enumerating the available values to see what you are actually receiving in each event:
function TPortableDeviceEventsCallback.OnEvent(const pEventParameters: IPortableDeviceValues): HResult;
var
Hr: HResult;
Count, I: DWORD;
Key: PROPERTYKEY;
Value: PROPVARIANT;
begin
Log('Event received');
Hr := pEventParameters.GetCount(Count);
if FAILED(Hr) or (Count = 0) then Exit;
Log('Param count: ' + IntToStr(Count));
for I := 0 to Count-1 do
begin
Hr := pEventParameters.GetAt(I, Key, Value);
if FAILED(Hr) then
begin
Log('Cant get parameter at index ' + IntToStr(I));
Continue;
end;
try
Log('Param Key: ' + GuidToString(Key.fmtid) + ', Value type: $' + IntToHex(Value.vt, 4));
// log content of Value based on its particular data type as needed...
finally
PropVariantClear(Value);
end;
end;
Result := S_OK;
end;