4

我正在尝试获取 Unicode 字体字形范围(Delphi 6):

var GS:PGlyphSet;
    GSSize:LongWord;
    rng:TWCRange;
begin
  GSSize := GetFontUnicodeRanges(Canvas.Handle, nil);
  GetMem(Pointer(GS), GSSize);
  try
    GS.cbThis:=GSSize;
    GS.flAccel:=0;
    GS.cGlyphsSupported:=0;
    GS.cRanges:=0;
    if GetFontUnicodeRanges(Canvas.Handle, GS)<>0 then begin
      for i:=0 to GS.cRanges-1 do begin
        rng := GS.ranges[i];

奇怪的是它Length(GS.ranges)是 1,但 GS.cRanges 是 309,当我尝试访问第二个范围时GS.ranges[1],我当然会得到一个范围检查错误。在我打开范围检查之前,它以某种神奇的方式工作。

参考类型(来自Windows模块):

PWCRange = ^TWCRange;
{$EXTERNALSYM tagWCRANGE}
tagWCRANGE = packed record
  wcLow: WCHAR;
  cGlyphs: SHORT;
end;
TWCRange = tagWCRANGE;

PGlyphSet = ^TGlyphSet;
{$EXTERNALSYM tagGLYPHSET}
tagGLYPHSET = packed record
  cbThis: DWORD;
  flAccel: DWORD;
  cGlyphsSupported: DWORD;
  cRanges: DWORD;
  ranges: array[0..0] of TWCRange;
end;
TGlyphSet = tagGLYPHSET;
4

1 回答 1

6

这个结构使用了所谓的 struct hack:

The ranges member is a variable length array, placed inline in the struct. But you cannot actually encode that in a static C type. That's why you call the function to find out how much memory to allocate, and then heap allocate the struct. If you allocated it on the stack, or using SizeOf(...) then the struct would be too small.

The simplest thing to do is to disable range checking for the code that accesses ranges. Although the type declaration says that only 0 is a valid index for ranges, in fact 0..cRanges-1 are valid.

If you don't want to disable range checking for the relevant code, then take a pointer the element 0, and then use pointer arithmetic in your loop.

var
  rng: PWCRange; 
....
rng := @GS.ranges[0];
for i:=0 to GS.cRanges-1 do begin
  // use rng^
  inc(rng);
end;

This is, in my view, the cleanest way to write code for sequential access. For random access, and with range checking in force, you'd be compelled to declare some extra types to defeat range checking:

type 
  TWCRangeArray = array [0..(MaxInt div SizeOf(TWCRange))-1] of TWCRange; 
  PWCRangeArray = ^TWCRangeArray; 

And then use type casting to access individual elements:

rng := PWCRangeArray(@GS.ranges)[i];
于 2013-09-06T07:18:28.600 回答