WinAPI: GetFontUnicodeRanges - I do not understand the result

I am trying to get Unicode font glyph ranges (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]; 

It is strange that Length(GS.ranges) is 1, but GS.cRanges is 309, and when I try to access the second range of GS.ranges[1] , I get, of course, a range validation error. Before I turned the range check, it worked in some magical way.

Types of links (from the Windows module):

 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
source share
1 answer

This structure uses the so-called struct hack:

The ranges element is a variable-length array placed inline in a struct. But you cannot encode this in a static C type. This is why you call the function to find out how much memory is allocated, and then the heap allocates the structure. If you allocated it on the stack or use SizeOf(...) , then the structure will be too small.

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

If you do not want to disable range checking for the corresponding code, then take a pointer to 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, in my opinion, is the cleanest way to write code for sequential access. For random access and with range checking, you will need to declare some additional types to prevent 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]; 
+6
source

Source: https://habr.com/ru/post/1500870/


All Articles