WinAPI:GetFontUnicodeRanges - 我不明白结果

Pau*_*aul 4 delphi unicode fonts range

我想获得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];
Run Code Online (Sandbox Code Playgroud)

奇怪的是它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;
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 6

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

ranges成员是一个可变长度数组,内嵌在结构中.但是你无法在静态C类型中实际编码.这就是为什么你调用函数来找出要分配多少内存,然后堆分配结构.如果你在堆栈上分配它,或者使用SizeOf(...)那么结构将太小.

最简单的方法是禁用访问代码的范围检查ranges.虽然类型声明说只有0一个有效的索引ranges,实际上0..cRanges-1是有效的.

如果您不想禁用相关代码的范围检查,则将指针指向元素0,然后在循环中使用指针算法.

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

在我看来,这是编写顺序访问代码的最简洁方法.对于随机访问,并且有效范围检查,您将被迫声明一些额外的类型以阻止范围检查:

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

然后使用类型转换来访问各个元素:

rng := PWCRangeArray(@GS.ranges)[i];
Run Code Online (Sandbox Code Playgroud)