1

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?

Delphi.Boy
  • 1,199
  • 4
  • 17
  • 38

1 Answers1

1

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Many many thanks. I will apply all these changes to my code. But the question is, assuming that I have received a WPD_EVENT_OBJECT_ADDED event, is this the proper way to get name of the file that is added? And why I'm getting garbage? – Delphi.Boy Mar 09 '16 at 01:30
  • Okay! I realized that I was using the wrong constant. Now I can get the correct file name using WPD_OBJECT_ORIGINAL_FILE_NAME. – Delphi.Boy Mar 09 '16 at 06:16