// C++
#include <shlobj.h>

size_t GetItemIDListSize(const LPITEMIDLIST pidl)
{
  size_t size = 0;
  LPBYTE p = LPBYTE(pidl);
  while( p != NULL )
  {
    if( static_cast(p + size)->mkid.cb == 0 )
    {
      size += sizeof( USHORT ); // size of terminator;
      break;
    }
    size += static_cast(p + size)->mkid.cb;
  }
  return size;
}

LPITEMIDLIST CopyItemIDList(const LPITEMIDLIST pidl)
{
  LPMALLOC pMalloc;
  LPITEMIDLIST pidlResult;
  if( pidl == NULL )
  {
    return NULL;
  }
  if( ! SUCCEEDED(SHGetMalloc(& pMalloc))
  {
    return NULL;
  }
  size_t size = GetItemIDListSize(pidl);
  pidlResult = pMalloc->Alloc( size );
  if( pidlResult != NULL )
  {
    CopyMemory( pidlResult, pidl, size );
  }
  pMalloc->Release();
  return pidlResult;
}
// Delphi
uses Windows, ActiveX, ShlObj;

type
  PByte = ^Byte;

function GetItemIDListSize(pidl: PItemIDList): Integer;
var
  p: PChar;
begin
  Result := 0;
  p := PChar(pidl);
  while p <> nil do
  begin
    if PItemIDList(p + Result)^.mkid.cb = 0 then
    begin
      Inc( Result, sizeof(Word) ); // size of terminator;
      break;
    end;
    Inc( Result, PItemIDList(p + Result)^.mkid.cb );
  end;
end;

function CopyItemIDList(pidl: PItemIDList): PItemIDList;
var
  pMalloc: IMalloc;
  size: Integer;
begin
  Result := nil;
  if not Assigned( pidl ) then
  begin
    Exit;
  end;
  if not SUCCEEDED(SHGetMalloc(pMalloc)) then
  begin
    Exit;
  end;
  size := GetItemIDListSize(pidl);
  Result := pMalloc.Alloc( size );
  if Assigned(Result) then
  begin
    CopyMemory( Result, pidl, size );
  end;
  pMalloc := nil; // Interface reference releasing
                  // will be proceed automatically
                  // by assigning a nil value
end;