C#, Powershell, undocumeted WinApi function GetFontResourceInfoW
out IntPtr lpBuffer
This is declared incorrectly. It should be:
IntPtr lpBuffer
Then the function call becomes:
$ret=[fontutil]::GetFontResourceInfoW($fn, [ref] $b, $LocalStructPtr,[UInt32]1)
You were passing the address of the pointer variable, but instead you pass that pointer variable's value. That's the address of the memory you allocated when you called AllocHGlobal
.
Note also that your buffer has room for 130 characters rather than 260, because a UTF-16 code unit is two bytes wide. That's probably fine, but it might not be what you are expecting.
For marshaling strings, StringBuilder
is a lot more convenient than mucking around with manually allocated buffers:
$code=@'using System;using System.Collections.Generic;using System.Text;using System.IO;using System.ComponentModel;using System.Runtime.InteropServices;public static class FontUtils { const int QFR_DESCRIPTION = 1; [DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern bool GetFontResourceInfoW( string lpszFilename, [In, Out] ref int cbBuffer, [Out] StringBuilder lpBuffer, int dwQueryType ); public static string GetFontDescription(string fileName) { int bufferSize = 0; StringBuilder sb = new StringBuilder(); if (!GetFontResourceInfoW(fileName, ref bufferSize, sb, QFR_DESCRIPTION)) { throw new Win32Exception(); } sb.Capacity = bufferSize / sizeof(char); if (!GetFontResourceInfoW(fileName, ref bufferSize, sb, QFR_DESCRIPTION)) { throw new Win32Exception(); } return sb.ToString(); }}'@Add-Type $code[FontUtils]::GetFontDescription('c:\windows\fonts\arial.ttf')
Of course, it's perfectly possible to write the C# code in PowerShell as well (the marshaler isn't language dependent), I just think this is a cleaner separation of concerns.