delphiwinapidelphi-xe7wpd

Delphi WPD Event Callback - Get Filename


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?


Solution

  • 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;