P/Invoke kernel32.dll实现全角半角转换

这个,之前放在PowerShell代码备忘录那边,但是这例子的体量确实太大,索性拆出来。

另外就是,这办法其实没什么必要用www

直接用VbStrConv就行了:文字列を全角/半角に変換するには?(VB.NET関数活用)

环境

操作系统Win10 1909,PowerShell版本($PSVersionTable.PSVersion.Major)是5,$PSCulture$PSUICulture均为ja-JP。另外有C#代码,使用dotNET Core 3.1。

另外在PowerShell 7.0.0-rc.1($PSVersionTable.PSVersion.ToString())上也测试成功了。

参考

实践

PowerShell

Program.ps1

Set-Variable -Name LOCALE_SYSTEM_DEFAULT -Value ([uint]0x0800) -Option Constant
Set-Variable -Name LCMAP_HALFWIDTH -Value ([uint]0x00400000) -Option Constant
Set-Variable -Name LCMAP_FULLWIDTH -Value ([uint]0x00800000) -Option Constant

$signature = @"
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
public static extern int LCMapString(
    uint Locale,
    uint dwMapFlags,
    string lpSrcStr,
    int cchSrc,
    StringBuilder lpDestStr,
    int cchDest);
"@
$mapString = Add-Type `
    -MemberDefinition $signature `
    -Name "Winnls" `
    -Namespace "Kernel32" `
    -UsingNamespace "System.Text" `
    -PassThru

function ToHalfWidth {
    param (
        [string]
        $fullWidth
    )
    $sb = [System.Text.StringBuilder]::new()
    $mapString::LCMapString(
        $LOCALE_SYSTEM_DEFAULT,
        $LCMAP_HALFWIDTH,
        $fullWidth,
        -1,
        $sb,
        $sb.Capacity) | Out-Null
    return $sb.ToString()
}

function ToFullWidth {
    param (
        [string]
        $halfWidth
    )
    $sb = [System.Text.StringBuilder]::new()
    $mapString::LCMapString(
        $LOCALE_SYSTEM_DEFAULT,
        $LCMAP_FULLWIDTH,
        $halfWidth,
        -1,
        $sb,
        $sb.Capacity) | Out-Null
    return $sb.ToString()
}

$fullWidth = "ウィキペディア"
$halfWidth = "ウィキペディア"

$f2h = ToHalfWidth($fullWidth);
$h2f = ToFullWidth($halfWidth);

Write-Host "Full Width: $fullWidth($($fullWidth.Length))`tHalf Width: $f2h($($f2h.Length))"
Write-Host "Half Width: $halfWidth($($halfWidth.Length))`tFull Width: $h2f($($h2f.Length))"

运行结果:

Full Width: ウィキペディア(7)   Half Width: ウィキペディア(9)
Half Width: ウィキペディア(9)        Full Width: ウィキペディア(7)

C Sharp

这个实际是在C#(dotNET Core 3.1的)那边做了实验之后再转到pwsh的。

Program.cs

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace ChrWidthConverter
{

    class Program
    {
        private const uint LOCALE_SYSTEM_DEFAULT = 0x0800;
        private const uint LCMAP_HALFWIDTH = 0x0040_0000;

        private const uint LCMAP_FULLWIDTH = 0x0080_0000;

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
        private static extern int LCMapString(
            uint Locale,
            uint dwMapFlags,
            string lpSrcStr,
            int cchSrc,
            StringBuilder lpDestStr,
            int cchDest);
        static void Main(string[] args)
        {
            var fullWidth = "ウィキペディア";
            var halfWidth = "ウィキペディア";

            var f2h = ToHalfWidth(fullWidth);
            var h2f = ToFullWidth(halfWidth);

            Console.WriteLine($"Full Width: {fullWidth}({fullWidth.Length})\tHalf Width: {f2h}({f2h.Length})");
            Console.WriteLine($"Half Width: {halfWidth}({halfWidth.Length})\tFull Width: {h2f}({h2f.Length})");
        }

        public static string ToHalfWidth(string fullWidth)
        {
            var sb = new StringBuilder();
            LCMapString(
                LOCALE_SYSTEM_DEFAULT,
                LCMAP_HALFWIDTH,
                fullWidth,
                -1,
                sb,
                sb.Capacity);
            return sb.ToString();
        }

        public static string ToFullWidth(string halfWidth)
        {
            var sb = new StringBuilder();
            LCMapString(
                LOCALE_SYSTEM_DEFAULT,
                LCMAP_FULLWIDTH,
                halfWidth,
                -1,
                sb,
                sb.Capacity);
            return sb.ToString();
        }
    }
}

运行(dotnet run)之后的结果和上面一致。

后记

其实最终只是再熟悉一遍P/Invoke而已。PowerShell那边有些写法需要斟酌一下才好,尤其是Add-Type -MemberDefinition的运用——虽然省事,不熟悉的话反倒很麻烦,倒不如写C#出个dll给PowerShell调。

InSb

InSb

只是跟工作和生活相关的记录