We help IT Professionals succeed at work.

Constructing objects from mem buffer

Jacco
Jacco asked
on
I am constructing instances of TImageInfoCodec from a buffer. The routine below works, but I am not very handy with all those pointer types and widestrings and therefore am not sure if this is the way to go...

If possible want an easier (maybe better) way the extract the widestrings from the buffer. There might also be a generic way to do this for the whole class. (At least the C++ code I saw for this was very very short!)

type
  TImageCodecInfo = class
    CLSID: TGUID;
    GUID: TGUID;
    CodecName: WideString;
    DllName: WideString;
    FormatDescription: WideString;
    FilenameExtension: WideString;
    MimeType: WideString;
    Flags: LongWord;
    Version: LongWord;
    SigCount: LongWord;
    SigSize: LongWord;
    SigPattern: array of string;
    SigMask: array of string;
    constructor CreateFromBuffer(var aBuffer: PByteArray);
  end;

{ TImageCodecInfo }

constructor TImageCodecInfo.CreateFromBuffer(var aBuffer: PByteArray);
var
  b: PByte;
  c: PChar;
  i, j: Integer;

  function GetWideStringFromBuf(var a: PByte): WideString;
  var
    s: WideChar;
    p: Pointer;
  begin
    Result := '';
    p := Pointer(PInteger(a)^);
    if p <> nil then
    begin
      s := PWideChar(p)^;
      repeat
        Result := Result + s;
        p := Pointer(Integer(p)+2);
        s := PWideChar(p)^;
      until s = #0;
    end;
    Inc(a, 4);
  end;

begin
  b := PByte(aBuffer);
  CLSID := PGUID(b)^;
  Inc(b, SizeOf(TGUID));
  GUID := PGUID(b)^;
  Inc(b, SizeOf(TGUID));
  CodecName := GetWideStringFromBuf(b);
  Form1.Memo1.Lines.Add(CodecName);
  DllName := GetWideStringFromBuf(b);
  Form1.Memo1.Lines.Add(DllName);
  FormatDescription := GetWideStringFromBuf(b);
  Form1.Memo1.Lines.Add(FormatDescription);
  FilenameExtension := GetWideStringFromBuf(b);
  Form1.Memo1.Lines.Add(FilenameExtension);
  MimeType := GetWideStringFromBuf(b);
  Form1.Memo1.Lines.Add(MimeType);
  Flags := PLongWord(b)^;
  Inc(b, 4);
  Version := PLongWord(b)^;
  Inc(b, 4);
  SigCount := PLongWord(b)^;
  Inc(b, 4);
  SigSize := PLongWord(b)^;
  Inc(b, 4);
  SetLength(SigPattern, SigCount);
  SetLength(SigMask, SigCount);
  c := PChar(PInteger(b)^);
  for i := 1 to SigCount do
  begin
    for j := 1 to SigSize do
    begin
      SigPattern[i-1] := SigPattern[i-1] + c[j-1];
    end;
    Form1.Memo1.Lines.Add(SigPattern[i-1]);
  end;
  Inc(b, 4);
  c := PChar(PInteger(b)^);
  for i := 1 to SigCount do
  begin
    for j := 1 to SigSize do
    begin
      SigMask[i-1] := SigMask[i-1] + c[j-1];
    end;
    Form1.Memo1.Lines.Add(SigMask[i-1]);
  end;
  Inc(b, 4);
  aBuffer := PByteArray(b);
end;

In C++ it was like this:

class ImageCodecInfo
{
public:          
    CLSID Clsid;
    GUID  FormatID;
    const WCHAR* CodecName;
    const WCHAR* DllName;
    const WCHAR* FormatDescription;
    const WCHAR* FilenameExtension;
    const WCHAR* MimeType;
    DWORD Flags;
    DWORD Version;
    DWORD SigCount;
    DWORD SigSize;
    const BYTE* SigPattern;
    const BYTE* SigMask;
};

inline Status
GetImageDecoders(
    IN UINT numDecoders,
    IN UINT size,
    OUT ImageCodecInfo *decoders)
{
    return DllExports::GdipGetImageDecoders(numDecoders, size, decoders);
}
Comment
Watch Question

Commented:
Here comes my try, not tested, but should work:

function GetWideStringFromBuf(var a: PByte) : WideString;
begin
  result := WideString(PWideChar(a));
  inc(a, Length(result) * 2 + 2);
end;

Regards, Madshi.

Commented:
Then instead of this:

 c := PChar(PInteger(b)^);
 for i := 1 to SigCount do
 begin
   for j := 1 to SigSize do
   begin
     SigPattern[i-1] := SigPattern[i-1] + c[j-1];
   end;
   Form1.Memo1.Lines.Add(SigPattern[i-1]);
 end;

You can probably do this:

 SetString(SigPattern[0], pchar(PInteger(b)^), SigSize);
 for i := 1 to SigCount - 1 do
   SigPattern[i] := SigPattern[0];

Should also be much faster. Same logic for SigMask, of course...

Author

Commented:
Your first comment  didnt't work properly because the WideStrings are not stored in the middle of the buffer but at the end. So you first have to fetch the pointer. But your solution helped me make this one:

function GetWideStringFromBuf2(var a: PByte) : WideString;
begin
  Result := WideString(PWideChar(Pointer(PInteger(a)^)));
  Inc(a, 4);
end;

And this works!

Still testing the other comment.

Thanks a lot Madshi.

Author

Commented:
There was an error in my code, the sigmask and sigpattern are retrieved like this. Maybe I can use the SetString you suggested anyway.

 c := PChar(PInteger(b)^);
  for i := 1 to SigCount do
  begin
    for j := 1 to SigSize do
    begin
      SigPattern[i-1] := SigPattern[i-1] + c[(i-1) * SigSize + j-1];
    end;
    Form1.Memo1.Lines.Add(SigPattern[i-1]);
  end;
  Inc(b, 4);

Regards Jacco
Commented:
for i := 0 to SigCount - 1 do
  SetString(SigPattern[i], pchar(PInteger(b)^)[i * SigSize], SigSize);

Author

Commented:
Thanks Madshi it works now.

For anyone interested the completed unit look like this:

unit GdiPlusImageCodec;

interface

uses
  SysUtils, Contnrs;

type
  TImageCodecInfo = class
  private
    fCLSID: TGUID;
    fGUID: TGUID;
    fCodecName: WideString;
    fDllName: WideString;
    fFormatDescription: WideString;
    fFilenameExtension: WideString;
    fMimeType: WideString;
    fFlags: LongWord;
    fVersion: LongWord;
    fSigCount: LongWord;
    fSigSize: LongWord;
    fSigPattern: array of string;
    fSigMask: array of string;
  protected
    constructor CreateFromBuffer(var aBuffer: PByteArray);
  public
    property CLSID: TGUID read fCLSID;
    property GUID: TGUID read fGUID;
    property CodecName: WideString read fCodecName;
    property DllName: WideString read fDllName;
    property FormatDescription: WideString read fFormatDescription;
    property FilenameExtension: WideString read fFilenameExtension;
    property MimeType: WideString read fMimeType;
    property Flags: LongWord read fFlags;
    property Version: LongWord read fVersion;
    property SigCount: LongWord read fSigCount;
    property SigSize: LongWord read fSigSize;
  end;

  TImageCodecInfoList = class(TObjectList)
  private
    function GetImageCodecInfo(i: Integer): TImageCodecInfo;
    procedure Load;
  public
    property ImageCodecInfo[i: Integer]: TImageCodecInfo read GetImageCodecInfo; default;
  end;

function ImageCodecInfoList: TImageCodecInfoList;

implementation

uses
  Windows, GdiPLus;

var
  fImageCodecInfoList: TImageCodecInfoList;

function ImageCodecInfoList: TImageCodecInfoList;
begin
  if not Assigned(fImageCodecInfoList) then
  begin
    fImageCodecInfoList := TImageCodecInfoList.Create;
    fImageCodecInfoList.Load;
  end;
  Result := fImageCodecInfoList;
end;

constructor TImageCodecInfo.CreateFromBuffer(var aBuffer: PByteArray);
var
  b: PByte;
  c: PChar;
  i: Integer;

  function GetWideStringFromBuf(var a: PByte) : WideString;
  begin
    Result := WideString(PWideChar(Pointer(PInteger(a)^)));
    Inc(a, 4);
  end;

begin
  b := PByte(aBuffer);
  fCLSID := PGUID(b)^;
  Inc(b, SizeOf(TGUID));
  fGUID := PGUID(b)^;
  Inc(b, SizeOf(TGUID));
  fCodecName := GetWideStringFromBuf(b);
  fDllName := GetWideStringFromBuf(b);
  fFormatDescription := GetWideStringFromBuf(b);
  fFilenameExtension := GetWideStringFromBuf(b);
  fMimeType := GetWideStringFromBuf(b);
  fFlags := PLongWord(b)^;
  Inc(b, 4);
  fVersion := PLongWord(b)^;
  Inc(b, 4);
  fSigCount := PLongWord(b)^;
  Inc(b, 4);
  fSigSize := PLongWord(b)^;
  Inc(b, 4);
  SetLength(fSigPattern, SigCount);
  SetLength(fSigMask, SigCount);
  c := PChar(PInteger(b)^);
  for i := 0 to SigCount-1 do
  begin
    SetString(fSigPattern[i], c, SigSize);
    Inc(c, SigSize);
  end;
  Inc(b, 4);
  c := PChar(PInteger(b)^);
  for i := 0 to SigCount-1 do
  begin
    SetString(fSigMask[i], c, SigSize);
    Inc(c, SigSize);
  end;
  Inc(b, 4);
  aBuffer := PByteArray(b);
end;

{ TImageCodecInfoList }

function TImageCodecInfoList.GetImageCodecInfo(i: Integer): TImageCodecInfo;
begin
  Result := TImageCodecInfo(Items[i]);
end;

procedure TImageCodecInfoList.Load;
var
  lMem, lDummy: PByteArray;
  liCodec, liMemSize, liCount: LongWord;
begin
  GdiPlusCheck(GdipGetImageDecodersSize(liCount, liMemSize));
  GetMem(lMem, liMemSize);
  try
    GdiPlusCheck(GdipGetImageDecoders(liCount, liMemSize, lMem));
    lDummy := lMem;
    for liCodec := 0 to 7 do
      Add(TImageCodecInfo.CreateFromBuffer(lDummy));
  finally
    FreeMem(lMem);
  end;
end;

initialization
finalization
  if Assigned(fImageCodecInfoList) then
    fImageCodecInfoList.Free;
end.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.