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

.net 添加类型变体:-MemberType vs.-TypeDefinition参数,.net,powershell,types,add-type,.net,Powershell,Types,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 max

有人能解释一下这种添加类型的方法之间的区别吗

$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);
    }
}
"@
我经常看到后者的例子,但前者我只在一些示例代码中见过,用于固定到任务栏。这仅仅是给猫剥皮的两种不同方式,还是在某些用例中需要前者? 而且,如果两者都一直有效,那么将后一种方法与前一种方法中的代码一起使用会是什么样子

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

我已经实现了代码的基础上,我从后

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

1:如何重构Add类型以使用Here字符串? 编辑:这似乎有效,所以我将问题改为:这是最好/最优雅的解决方案,还是可以改进

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字符串是最好的方法

另一方面,使用
将参数名称与其值分开是可行的,但并不常见;通常使用空格(例如,
-name实用程序
而不是
-name:Utility

  • 关于2:
没有充分的理由在类型定义中使用
[System.Text.StringBuilder]

使用here字符串、常规字符串或字符串数组。
除了在Windows API调用中使用之外,正如您所演示的,在PuthS壳中考虑使用<代码> [ Stase.Text StringBuilder ] <代码>的唯一原因是,如果性能是最重要的,则需要从动态创建的片段中建立一个非常大的字符串。 Gordon自己指出,在
LoadString()
Windows API函数的
sb
参数中使用
[System.Text.StringBuilder]
是必要的,因为它是一个接收字符串的out参数,而
[string]
类型是不可变的

  • 关于3:
可以将这两种方法结合起来,一方面是p/Invoke signatures
[DllImport(…
-MemberType
相结合,另一方面是自定义类型定义(
class BasicTest…
)与
-TypeDefinition
相结合(有关这两种方法的背景信息,请参阅本文底部)

旁注re
script
作用域:
script
是脚本中的默认作用域,因此在脚本的顶层,您创建的所有变量都是隐式脚本作用域;因此,
$script:Shell32=…
实际上与脚本顶层作用域中的
$Shell32=…
相同。您甚至可以引用将函数内部的脚本级别变量作为
$Shell32
(尽管为了清晰起见,您可能希望使用
$script:Shell32
)。唯一需要
$script:
范围限定符的时间是您是否创建了一个本地
$Shell32
变量(例如,隐式地,只需分配给
$Shell32
)这会影响脚本级别1

  • 下面的代码是一个重构,它创建了一个带有
    Add Type-TypeDefinition
    的助手类,p/Invoke签名集成到该类中(不需要单独的
    Add Type-MemberDefinition
    调用)

    • 创建的helper类型只有一个静态方法,
      GetVerbName()
      。请注意,我已经从p/Invoke签名中删除了
      public
      access修饰符,使它们成为私有的,因为它们现在只在类内部需要

    • helper方法在每次调用时都会加载并释放
      “shell32.dll
      dll,但我不认为这是性能方面的问题

    • 您可以扩展此方法,将所有非PowerShell代码移动到此helper类中(调用
      Shell.Application
      COM对象)。如果这样做,并实现了一个
      PinToTaskBar
      静态方法,您只需引用
      [PxTools.TaskBarStartMenuHelper]::PinToTaskBar()
      在脚本中的任何位置,甚至不必担心脚本级别的变量

  • 我已经用更干净的
    Enum
    定义替换了
    $verbs
    哈希表,但请注意,这只适用于PSv5+

还请注意,在会话中重复调用
Add Type
是可以的,只要类型定义没有更改。后续调用实际上是一个快速、无声的禁止操作。
换句话说:无需显式检查类型是否存在并有条件地定义它。如果已加载同名的不同类型,则添加类型将失败,但这是可取的,因为您希望确保使用的是所需的类型。


背景资料 参考文献:

  • 第一种方法-
    AddType-MemberDefinition…-Name…-NameSpace
    通常用于传递一个字符串,该字符串包含公共签名,该签名通过一个自定义的助手类启用对本机代码(如Windows API函数)的访问,该类将这些函数公开为s
    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
    
    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 -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);
    '@  
    
    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);
      }  
    }
    '@