{*******************************************************}
{                                                       }
{       Tocsg.MTP                                       }
{                                                       }
{       Copyright (C) 2022 kku                          }
{                                                       }
{*******************************************************}

unit Tocsg.MTP;

interface

uses
  System.SysUtils, System.Classes, Tocsg.Obj, Winapi.Windows,
  System.Generics.Collections;

type
  PMtpEnt = ^TMtpEnt;
  TMtpEnt = record
    sName,
    sDesc,
    sMfg,
    sDevId: String;
  end;
  TMtpEntList = TList<PMtpEnt>;

  TManagerMtpDev = class(TTgObject)
  private
    MtpEntList_: TMtpEntList;
    procedure OnMptEntNotify(Sender: TObject; const Item: PMtpEnt; Action: TCollectionNotification);
  public
    Constructor Create;
    Destructor Destroy; override;

    procedure RefreshMptDev;
    function GetMtpEntByDevId(sDevId: String): PMtpEnt;

    property MtpEntList: TMtpEntList read MtpEntList_;
  end;

implementation

uses
  Tocsg.Driver, Tocsg.Exception, Tocsg.Safe;

Constructor TManagerMtpDev.Create;
begin
  Inherited Create;
  MtpEntList_ := TMtpEntList.Create;
  MtpEntList_.OnNotify := OnMptEntNotify;
end;

Destructor TManagerMtpDev.Destroy;
begin
  FreeAndNil(MtpEntList_);
  Inherited;
end;

procedure TManagerMtpDev.OnMptEntNotify(Sender: TObject; const Item: PMtpEnt; Action: TCollectionNotification);
begin
  if Action = cnRemoved then
    Dispose(Item);
end;

procedure TManagerMtpDev.RefreshMptDev;
var
  hDev: HDEVINFO;
  sdd: TSPDevInfoData;
  i, c: Integer;
  dwBufSize,
  dwStatus, dwProblem,
  dwPropertyRegDataType: DWORD;
  pBuf: Pointer;
  sInfo1, sInfo2: String;
  InfoList: TStringList;
  pEnt: PMtpEnt;

  function GetPropStr(dwProp: DWORD): String;
  begin
    Result := '';

    dwBufSize := 0;
    if pBuf <> nil then
    begin
      FreeMem(pBuf);
      pBuf := nil;
    end;

    while not SetupDiGetDeviceRegistryProperty(hDev, sdd,
             dwProp, dwPropertyRegDataType, pBuf, dwBufSize, dwBufSize) do
    begin
      if GetLastError = ERROR_INSUFFICIENT_BUFFER then
      begin
        if pBuf <> nil then
          FreeMem(pBuf);
        pBuf := AllocMem(dwBufSize);
      end else break;
    end;

    if pBuf <> nil then
      Result := PChar(pBuf); // 값 여러줄 있는거 무시하고 첫번째만 가져옴
  end;

begin
  try
    hDev := SetupDiGetClassDevs(@GUID_DEVCLASS_WPD, nil, 0, DIGCF_PRESENT);
    if hDev = INVALID_HANDLE_VALUE then
      exit;

    pBuf := nil;
    try
      ZeroMemory(@sdd, SizeOf(sdd));
      sdd.cbSize := SizeOf(sdd);

      Guard(InfoList, TStringList.Create);

      i := 0;
      while SetupDiEnumDeviceInfo(hDev, i, sdd) do
      begin
        sInfo1 := UpperCase(Trim(GetPropStr(SPDRP_COMPATIBLEIDS)));
        sInfo2 := UpperCase(Trim(GetPropStr(SPDRP_SERVICE)));

//        if GetPropStr(SPDRP_DEVTYPE) = 'MTP' then // todo : "버스에서 보고된 장치 설명" 값을 가져오는 방법을 모름.. 22_0630 15:18:12 kku
        if (sInfo1 = 'USB\MS_COMP_MTP') or (sInfo2 = 'WUDFWPDMTP') then
        begin
          New(pEnt);
//          _Trace(GetPropStr(SPDRP_COMPATIBLEIDS)); // USB\MS_COMP_MTP
//          _Trace(GetPropStr(SPDRP_SERVICE)); // WUDFWpdMtp
//          _Trace(GetPropStr(SPDRP_CLASS));
//          _Trace(GetPropStr(SPDRP_CLASSGUID));
//          _Trace(GetPropStr(SPDRP_DRIVER));
//          _Trace(GetPropStr(SPDRP_CONFIGFLAGS));
//          _Trace(GetPropStr(SPDRP_LOCATION_INFORMATION));
//          _Trace(GetPropStr(SPDRP_PHYSICAL_DEVICE_OBJECT_NAME));
//          _Trace(GetPropStr(SPDRP_CAPABILITIES));
//          _Trace(GetPropStr(SPDRP_UI_NUMBER));
//          _Trace(GetPropStr(SPDRP_UPPERFILTERS));
//          _Trace(GetPropStr(SPDRP_LOWERFILTERS));
//          _Trace(GetPropStr(SPDRP_BUSTYPEGUID));
//          _Trace(GetPropStr(SPDRP_LEGACYBUSTYPE));
//          _Trace(GetPropStr(SPDRP_BUSNUMBER));
//          _Trace(GetPropStr(SPDRP_UI_NUMBER));
//          _Trace(GetPropStr(SPDRP_ENUMERATOR_NAME));
//          _Trace(GetPropStr(SPDRP_SECURITY));
//          _Trace(GetPropStr(SPDRP_SECURITY_SDS));
//          _Trace(GetPropStr(SPDRP_DEVTYPE));
//          _Trace(GetPropStr(SPDRP_EXCLUSIVE));
//          _Trace(GetPropStr(SPDRP_CHARACTERISTICS));
//          _Trace(GetPropStr(SPDRP_ADDRESS));
//          _Trace(GetPropStr(SPDRP_UI_NUMBER_DESC_FORMAT));
//          _Trace(GetPropStr(SPDRP_DEVICE_POWER_DATA));
//          _Trace(GetPropStr(SPDRP_REMOVAL_POLICY));
//          _Trace(GetPropStr(SPDRP_LOCATION_PATHS));

          pEnt.sName := GetPropStr(SPDRP_FRIENDLYNAME);
          pEnt.sDesc := GetPropStr(SPDRP_DEVICEDESC);
          pEnt.sMfg := GetPropStr(SPDRP_MFG);
          pEnt.sDevId := GetPropStr(SPDRP_HARDWAREID);
          MtpEntList_.Add(pEnt);
        end;
        Inc(i);
      end;

    finally
      SetupDiDestroyDeviceInfoList(hDev);
      if pBuf <> nil then
        FreeMem(pBuf);
    end;
  except
    on E: Exception do
      ETgException.TraceException(E, 'Fail .. RefreshMptDev()');
  end;
end;

function TManagerMtpDev.GetMtpEntByDevId(sDevId: String): PMtpEnt;
var
  i: Integer;
begin
  Result := nil;
  for i := 0 to MtpEntList_.Count - 1 do
    if MtpEntList_[i].sDevId = sDevId then
    begin
      Result := MtpEntList_[i];
      exit;
    end;
end;

end.
