C#,Powershell,未记录的WinApi函数GetFontResourceInfoW

C#,Powershell,未记录的WinApi函数GetFontResourceInfoW,c#,powershell,winapi,C#,Powershell,Winapi,我有一个AutoIt脚本,它使用一个未记录的gdi32函数(GetFontResourceInfoW)(AutoIt:_WinAPI_GetFontResourceInfo) 它返回字体文件的名称(.fon、.ttf、.ttc等。是否已安装) 剧本写得很好。我现在想在Powershell中重新编码。 功能原型(来自)为: 我尝试了以下操作,但它没有返回fontname $code=@' using System; using System.Collections.Generic; using S

我有一个AutoIt脚本,它使用一个未记录的gdi32函数(GetFontResourceInfoW)(AutoIt:_WinAPI_GetFontResourceInfo)

它返回字体文件的名称(.fon、.ttf、.ttc等。是否已安装) 剧本写得很好。我现在想在Powershell中重新编码。 功能原型(来自)为:

我尝试了以下操作,但它没有返回fontname

$code=@'
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

public static class  FontUtil{

    [DllImport("gdi32.dll")] 
        public static extern bool GetFontResourceInfoW(string lpszFilename, ref UInt32 cbBuffer, out IntPtr lpBuffer, UInt32 dwQueryType); 
}
'@
Add-Type $code

[string]$fn = 'c:\windows\fonts\arial.ttf'
[Uint32]$b = 260
[IntPtr]$LocalStructPtr = Runtime.InteropServices.Marshal]::AllocHGlobal(260)
$ret=[fontutil]::GetFontResourceInfoW($fn, [ref] $b, [ref] $LocalStructPtr,[UInt32]1)
[Runtime.InteropServices.Marshal]::PtrToStringAuto($LocalStructPtr,$b)
[Runtime.InteropServices.Marshal]::FreeHGlobal($LocalStructPtr)
我认为paramaters或互操作封送有问题

有什么想法吗

out IntPtr lpBuffer
这是不正确的声明。应该是:

IntPtr lpBuffer
然后函数调用变为:

$ret=[fontutil]::GetFontResourceInfoW($fn, [ref] $b, $LocalStructPtr,[UInt32]1)
您传递的是指针变量的地址,而不是指针变量的值。这是调用
AllocHGlobal
时分配的内存地址


还要注意的是,您的缓冲区可以容纳130个字符,而不是260个字符,因为UTF-16代码单元的宽度是两个字节。这可能很好,但可能不是您所期望的。

对于封送字符串,
StringBuilder
比手动分配缓冲区方便得多:

$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')
当然,在PowerShell中编写C#代码也是完全可能的(封送拆收器不依赖于语言),我只是认为这是一种更清晰的关注点分离

$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')