tags:

views:

358

answers:

2

Hello all,

I have currently run into an issue while toying with Lua and the alien module to use Win32 API and such from Lua scripts. So far I have only had a single issue with alien which is with the use of API that use certain structures such as CreateFontIndirect.

For example:

HFONT CreateFontIndirectA( const LOGFONT& lplf );

LOGFONT:

typedef struct tagLOGFONT {
 LONG  lfHeight;
 LONG  lfWidth;
 LONG  lfEscapement;
 LONG  lfOrientation;
 LONG  lfWeight;
 BYTE  lfItalic;
 BYTE  lfUnderline;
 BYTE  lfStrikeOut;
 BYTE  lfCharSet;
 BYTE  lfOutPrecision;
 BYTE  lfClipPrecision;
 BYTE  lfQuality;
 BYTE  lfPitchAndFamily;
 TCHAR lfFaceName[LF_FACESIZE];
}LOGFONT, *PLOGFONT;

The issue lies with the font face name. I cannot get Lua to keep a string inside the structure itself, it always pushes a pointer into the structure. So there is no way, that I can figure out, to be able to use this API purely from Lua.

This is what I got working to a point:

 LOGFONT = alien.defstruct {
  { 'lfHeight', 'long' },
  { 'lfWidth', 'long' },
  { 'lfEscapement', 'long' },
  { 'lfOrientation', 'long' },
  { 'lfWeight', 'long' },
  { 'lfItalic', 'byte' },
  { 'lfUnderline', 'byte' },
  { 'lfStrikeOut', 'byte' },
  { 'lfCharSet', 'byte' },
  { 'lfOutPrecision', 'byte' },
  { 'lfClipPrecision', 'byte' },
  { 'lfQuality', 'byte' },
  { 'lfPitchAndFamily', 'byte' },
  { 'lfFaceName', 'string' } -- This line isn't working properly.
 }



 gdi32 = alien.load( "gdi32.dll" )
 gdi32.CreateFontIndirectA:types {
  ret = 'long',
  abi = 'stdcall',
  'pointer'
 }

An example to call it:

 local lf = LOGFONT:new()
 lf.lfHeight   = 14
 lf.lfWidth    = 0
 lf.lfEscapement  = 0
 lf.lfOrientation = 0
 lf.lfWeight   = 400
 lf.lfItalic   = 0
 lf.lfUnderline  = 0
 lf.lfStrikeOut  = 0
 lf.lfCharSet  = 0
 lf.lfOutPrecision = 0
 lf.lfClipPrecision = 0
 lf.lfQuality  = 0
 lf.lfPitchAndFamily = 0
 lf.lfFaceName   = 'Terminal'

 local hFont = gdi32.CreateFontIndirectA( lf() )

Debugging my application that runs my script shows that the api is being called properly, everything is passed properly except the font face. I've tried various different methods to get it working but I cant get it to go as needed.

Any tips on fixing this without hard-coding anything else into the exe?

+3  A: 

Hi,

Alien's string type is for pointers to strings only, that is why your example is not working. Try this:

-- LF_FACESIZE = ? -- put the value of the LF_FACESIZE constant here

LOGFONT = alien.defstruct {
  { 'lfHeight', 'long' },
  { 'lfWidth', 'long' },
  { 'lfEscapement', 'long' },
  { 'lfOrientation', 'long' },
  { 'lfWeight', 'long' },
  { 'lfItalic', 'byte' },
  { 'lfUnderline', 'byte' },
  { 'lfStrikeOut', 'byte' },
  { 'lfCharSet', 'byte' },
  { 'lfOutPrecision', 'byte' },
  { 'lfClipPrecision', 'byte' },
  { 'lfQuality', 'byte' },
  { 'lfPitchAndFamily', 'byte' },
  { 'lfFaceName', 'char' } 
 }

LOGFONT.size = LOGFONT.size + LF_FACESIZE - 1 -- so Alien allocates enough space
                                              -- for the whole array

function get_lfname(lf) -- gets the lfFaceName field as a Lua string
  local out = {}
  local offset = LOGFONT.offsets.lfFaceName
  local buf = lf()
  for i = offset, offset+LF_FACESIZE-1 do
    local c = buf:get(i, "char")
    if c ~= 0 then
      out[#out+1] = string.char(c)
    else
      break
    end
  end
  return table.concat(out)
end

function set_lfname(lf, s) -- sets the Lua string s as the lfFaceName
  local offset = LOGFONT.offsets.lfFaceName
  local buf = lf()
  for i = 1, LF_FACESIZE do
    if i <= #s then
      buf:set(offset+i, string.byte(string.sub(s, i, i)), "char")
    else
      buf:set(offset+i, 0, "char")
      break
    end
  end
end

Now just allocate an LOFGONF structure as usual, but use the get_lfname and set_lfname functions to work with the lfFaceName attribute:

local lf = LOGFONT:new()
lf.lfHeight   = 14
lf.lfWidth    = 0
lf.lfEscapement  = 0
lf.lfOrientation = 0
lf.lfWeight   = 400
lf.lfItalic   = 0
lf.lfUnderline  = 0
lf.lfStrikeOut  = 0
lf.lfCharSet  = 0
lf.lfOutPrecision = 0
lf.lfClipPrecision = 0
lf.lfQuality  = 0
lf.lfPitchAndFamily = 0
set_lfname(lf, 'Terminal')

local hFont = gdi32.CreateFontIndirectA( lf() )

Tacking an array at the end is a common pattern for structures in C programming that I forgot. I am going to put direct support for it in the next version of Alien.

mascarenhas
A: 

Thanks much for the response mascarenhas, this solution worked. I did have to adjust your set_lfname function though, as the offset+i-1 was misaligned and was overwriting the lfPitchAndFamily byte in the structure, removed the -1 and it works great. :)

Please delete this answer after copying its content to a comment under mascarenhas's answer, and then accept that answer (click the check-mark to its left).
John Zwinck