gpt4 book ai didi

.net - 添加类型变化 : -MemberType vs. -TypeDefinition 参数

转载 作者:行者123 更新时间:2023-12-03 00:14:20 26 4
gpt4 key购买 nike

有人可以解释这种方法与 Add-Type 之间的区别吗

$definition = [Text.StringBuilder]"" 
[void]$definition.AppendLine('[DllImport("user32.dll")]')
[void]$definition.AppendLine('public static extern int LoadString(IntPtr h, uint id, System.Text.StringBuilder sb, int maxBuffer);')
[void]$definition.AppendLine('[DllImport("kernel32.dll")]')
[void]$definition.AppendLine('public static extern IntPtr LoadLibrary(string s);')
Add-Type -memberDefinition:$definition.ToString() -name:Utility -namespace:PxTools

和这样的事情
Add-Type -typeDefinition @"
public class BasicTest
{
public static int Add(int a, int b)
{
return (a + b);
}
public int Multiply(int a, int b)
{
return (a * b);
}
}
"@

我经常看到后者的例子,但前者我只在一些示例代码中看到过,以固定到任务栏。这只是给猫剥皮的两种不同方式,还是在某些用例中需要前者?
而且,如果两者都一直有效,那么将后一种方法与前一种中的代码一起使用会是什么样子?

编辑:我考虑过将此作为一个新线程,但在我看来这是对原始问题的扩展,所以希望这是正确的方法。

我已经根据从 this 学到的知识实现了代码邮政...
$targetFile = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Snipping Tool.lnk"
$action = 'PinToTaskbar'

$verbs = @{
'PinToStartMenu' = 5381
'UnpinFromStartMenu' = 5382
'PinToTaskbar' = 5386
'UnpinFromTaskbar' = 5387
}

try {
$type = [type]"PxTools.Utility"
} catch {
$definition = [Text.StringBuilder]""
[void]$definition.AppendLine('[DllImport("user32.dll")]')
[void]$definition.AppendLine('public static extern int LoadString(IntPtr h, uint id, System.Text.StringBuilder sb, int maxBuffer);')
[void]$definition.AppendLine('[DllImport("kernel32.dll")]')
[void]$definition.AppendLine('public static extern IntPtr LoadLibrary(string s);')
Add-Type -memberDefinition:$definition.ToString() -name:Utility -namespace:PxTools
}
if ($script:Shell32 -eq $null) {
$script:Shell32 = [PxTools.Utility]::LoadLibrary("shell32.dll")
}
$maxVerbLength = 255
$verb = new-object Text.StringBuilder "", $maxVerbLength
[void][PxTools.Utility]::LoadString($script:Shell32, $verbs.$action, $verb, $maxVerbLength)
$verbAsString = $verb.ToString()
try {
$path = Split-Path $targetFile -parent -errorAction:stop
$file = Split-Path $targetFile -leaf -errorAction:stop
$shell = New-Object -com:"Shell.Application" -errorAction:stop
$folder = $shell.Namespace($path)
$target = $($folder.Parsename($file)).Verbs() | Where-Object {$_.Name -eq $verbAsString}
$target.DoIt()
Write-Host "$($action): $file"
} catch {
Write-Host "Error managing shortcut"
}

现在我有三个关于重构的问题。

1:如何重构 Add-Type 以使用 Here-String?
编辑:这似乎有效,所以我将问题修改为,这是最好/最优雅的解决方案,还是可以改进?
Add-Type -memberDefinition:@"
[DllImport("user32.dll")]
public static extern int LoadString(IntPtr h, uint id, System.Text.StringBuilder sb, int maxBuffer);
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string s);
"@ -name:Utility -namespace:PxTools

2:Test.StringBuilder 既用作 $definition 字符串的类型,又用于 LoadString 方法。这是必需的,还是可以在不使用 StringBuilder 的情况下实现?
编辑:我在上面的重构中消除了 SB 作为数据类型,但不确定在 LoadString 中是否这样做。可能是因为在更改代码之前,我仍在努力弄清楚代码在做什么。

3:是否 $script:Shell32位需要像这样处理,或者它不能被卷入类型吗?而且,原来的全局范围(我改为脚本范围)真的有必要吗?在我看来,除非我这样做数百次,否则多次调用 LoadLibrary 不会有什么大不了的?

最佳答案

  • 回复 1:

  • 是的,以这种方式使用 here-string 是最好的方法。

    附带说明,使用 :将参数名称与其值分开是可行的,但不寻常;通常,使用空格(例如, -name Utility 而不是 -name:Utility )。
  • 回复 2:

  • 没有充分的理由使用 [System.Text.StringBuilder]在类型定义中。
    使用此处字符串、常规字符串或字符串数​​组。
    除了在 Windows API 调用中使用之外,正如您所展示的,这是您考虑使用 [System.Text.StringBuilder] 的唯一原因。在 PowerShell 中,如果性能是最重要的,并且您需要从动态创建的片段中构建一个非常大的字符串。

    戈登本人指出,使用 [System.Text.StringBuilder]sb LoadString() 的参数Windows API 函数是必须的,因为它是一个接收字符串的输出参数,而 [string]类型是不可变的。
  • 回复 3:

  • 可以将这两种方法结合起来 - P/Invoke 签名 [DllImport(...-MemberType一方面,自定义类型定义( class BasicTest ... )和 -TypeDefinition另一方面(有关这两种方法的背景信息,请参阅本文底部)。

    附注重新 script范围: script是脚本内部的默认范围,因此在脚本的顶层,您创建的所有变量都是隐式脚本范围的;因此, $script:Shell32 = ...实际上与 $Shell32 = ... 相同在脚本的顶级范围内。您甚至可以从函数内部引用该脚本级变量,就像 $Shell32 (尽管为了清楚起见,您可能希望使用 $script:Shell32)。您唯一需要的时候 $script:范围限定符是如果您创建了本地 $Shell32隐藏脚本级别的变量(例如,隐式地,只需分配给 $Shell32 )。
  • 下面的代码是一个重构,它创建了一个带有 Add-Type -TypeDefinition 的辅助类。 P/Invoke 签名集成到其中(不需要单独的 Add-Type -MemberDefinition 调用)。
  • 创建的辅助类型只有一个静态方法,GetVerbName() .请注意,我已经删除了 public访问修饰符从 P/Invoke 签名使它们私有(private),因为它们现在只在类内部需要。
  • 辅助方法加载和释放 "shell32.dll DLL 每次调用,但我不希望这是一个明智的性能问题。
  • 您可以扩展此方法,将所有非 PowerShell 代码移动到此帮助程序类中(调用 Shell.Application COM 对象)。如果你这样做了,并实现了,比如 PinToTaskBar静态方法,你可以简单地引用 [PxTools.TaskBarStartMenuHelper]::PinToTaskBar()从脚本中的任何位置,甚至不必担心脚本级变量。
  • 我已经更换了 $verbs带有清洁器的哈希表 Enum定义,但请注意,这仅适用于 PSv5+。

  • Enum TaskBarStartMenuVerbs {  
    PinToStartMenu = 5384
    UnpinFromStartMenu = 5385
    PinToTaskbar = 5386
    UnpinFromTaskbar = 5387
    }

    Add-Type -TypeDefinition @'
    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace PxTools {
    public class TaskBarStartMenuHelper {

    [DllImport("user32.dll")]
    static extern int LoadString(IntPtr h, uint id, System.Text.StringBuilder sb, int maxBuffer);
    [DllImport("kernel32.dll")]
    static extern IntPtr LoadLibrary(string s);
    [DllImport("kernel32.dll")]
    static extern bool FreeLibrary(IntPtr h);

    public static string GetVerbName(uint verbId) {
    IntPtr h = LoadLibrary("shell32.dll");
    const int maxLen = 255;
    var sb = new StringBuilder(maxLen);
    LoadString(h, verbId, sb, maxLen);
    FreeLibrary(h);
    return sb.ToString();
    }
    }
    }
    '@

    # This returns 'Pin to tas&kbar' on a US English system.
    [PxTools.TaskBarStartMenuHelper]::GetVerbName([TaskBarStartMenuVerbs]::PinToTaskbar)

    另请注意,可以拨打 Add-Type在 session 中重复,只要类型定义没有改变。后续调用实际上是一种快速且无声的空操作。
    换句话说:不需要显式检查类型的存在并有条件地定义它。如果已经加载了同名的不同类型,则 Add-Type会失败,但这是可取的,因为您想确保使用的是您想要的类型。


    背景资料

    引用: Get-Help Add-Type .
  • 第一种方法 - AddType -MemberDefinition ... -Name ... -NameSpace通常用于传递 包含公共(public)的字符串 P/Invoke签名那个允许访问 native 代码,例如 Windows API 函数 通过自定义 辅助类将它们暴露为 静态方法 .
  • 执行第一条命令时,输入 [PxTools.Util]使用静态方法创建 [PxTools.Util]::LoadString()[PxTools.Util]::LoadLibrary()调用底层的 Windows API 函数。
  • 虽然 -MemberDefinition通常与 P/Invoke 签名一起使用,但不限于此,因为 -MemberDefinition只是将指定字符串包装在公共(public)类中的语法糖(有关详细信息,请参见下文)。因此,您可以传递任何在 class 中有效的内容。主体,例如属性和方法定义,作为更简洁地定义自定义类的一种方式,而不必包含显式 namespaceclass块。
    但是请注意,它总是一个 class已创建,因此您不能使用此方法定义 struct , 例如;此外,您不能使用 using直接陈述;相反,通过 -UsingNamespace 传递命名空间范围。
  • 未指定 -NameSpace value 将类型放入命名空间 Microsoft.PowerShell.Commands.AddType.AutoGeneratedTypes ,这意味着您必须将其称为 [Microsoft.PowerShell.Commands.AddType.AutoGeneratedTypes.<yourTypeName>] (注意这与没有 -TypeDefinition shell 的 namespace <name> { ... } 命令有何不同 - 见下文)。
  • 第二种方法 - Add-Type -TypeDefinition ... - 用于定义自定义类型(类)通过 包含 C# 源代码的字符串 (默认情况下;也支持 VisualBasic 和 JScript)。
    自定义类型可以是 class , delegate , enum , interface , 或 struct定义,通常包含在命名空间声明中。
  • 执行第二条命令时,输入 [BasicTest]创建(没有命名空间限定,因为源代码字符串中的类型定义未包含在 namespace <name> { ... } 中),使用静态方法 Add和实例方法 Multiply .

  • 在这两种情况下,成员必须声明为 public以便从 PowerShell 访问。

    请注意 -MemberDefinition语法本质上只是语法糖 因为它会自动为 提供一个围绕 P/Invoke 签名的类包装器。在您想直接调用它们的情况下促进对 native DLL 调用的访问 来自 PowerShell 而不是在用 Add-Type -TypeDefinition 定义的自定义类型内部使用它们.

    示例 :

    以下 -MemberDefinition称呼:
    Add-Type -Namespace PxTools -Name Utility -MemberDefinition @'
    [DllImport("user32.dll")]
    public static extern int LoadString(IntPtr h, uint id, System.Text.StringBuilder sb, int maxBuffer);
    [DllImport("kernel32.dll")]
    public static extern IntPtr LoadLibrary(string s);
    [DllImport("kernel32.dll")]
    public static extern bool FreeLibrary(IntPtr h);
    '@

    是以下 -TypeDefinition 的语法糖称呼:
    Add-Type -TypeDefinition @'
    using System;
    using System.Runtime.InteropServices;

    namespace PxTools {
    public class Utility {
    [DllImport("user32.dll")]
    public static extern int LoadString(IntPtr h, uint id, System.Text.StringBuilder sb, int maxBuffer);
    [DllImport("kernel32.dll")]
    public static extern IntPtr LoadLibrary(string s);
    [DllImport("kernel32.dll")]
    public static extern bool FreeLibrary(IntPtr h);
    }
    }
    '@

    换句话说: Add-Type -MemberDefinition :
  • 自动将源代码中定义的公共(public)静态方法包装在指定 namespace ( -Name ) 中具有指定名称 ( -NameSpace ) 的公共(public)类中,以便这些方法可以作为该类的静态方法调用,如 [<NameSpace>.<Name>]::<Method>(...)
  • 虽然这通常用于公开 P/Invoke 签名 - 调用非托管函数,特别是 Windows API 函数 - 但不限于此,您还可以使用该技术通常使用(公共(public))静态实用程序定义通用帮助程序类包含常规(托管)C# 代码的方法。请注意,您不能使用 using直接陈述;相反,通过 -UsingNamespace 传递命名空间范围。
  • 隐含地前置必要的 using声明 需要支持编译 P/Invoke 签名。
  • 关于.net - 添加类型变化 : -MemberType vs. -TypeDefinition 参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40296537/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com