使用PowerShell包装现有COM对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用PowerShell包装现有COM对象相关的知识,希望对你有一定的参考价值。

使用PowerShell和System.DirectoryServices,我得到了一个如下所示的对象:

   TypeName: System.__ComObject

Name                      MemberType Definition
----                      ---------- ----------
CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateObjRef(type requestedType)
Equals                    Method     bool Equals(System.Object obj)
GetHashCode               Method     int GetHashCode()
GetLifetimeService        Method     System.Object GetLifetimeService()
GetType                   Method     type GetType()
InitializeLifetimeService Method     System.Object InitializeLifetimeService()
ToString                  Method     string ToString()

我能找到的所有示例代码都涉及从PowerShell创建新的COM对象,而不是包装已返回的现有对象。我如何有用地处理这个对象(枚举和使用实际的属性和方法)?

注意:这个对象确实有一个类型库(“ActiveDs”),但由于某种原因我无法开箱即用,因为一个不同的问题(Loading a Type Library via PowerShell and scripting Windows Live Writer)建议应该是这种情况。

这是一个单线显示如何获得这样的对象:

((new-object DirectoryServices.DirectoryEntry -a '
LDAP://somedc').Properties.GetEnumerator() |?{$_.PropertyName -eq 'usnChanged' }).Value[0] | Get-Member
答案

PowerShell反射无法正确“查看”这些对象的属性和方法。为了获得属性和方法,我使用了一些包装器函数。这是一个例子:

function Get-Property {
  param(
    [__ComObject] $object,
    [String] $propertyName
  )
  $object.GetType().InvokeMember($propertyName,"GetProperty",$NULL,$object,$NULL)
}

function Set-Property {
  param(
    [__ComObject] $object,
    [String] $propertyName,
    $propertyValue
  )
  [Void] $object.GetType().InvokeMember($propertyName,"SetProperty",$NULL,$object,$propertyValue)
}

function Invoke-Method {
  param(
    [__ComObject] $object,
    [String] $methodName,
    $methodParameters
  )
  $output = $object.GetType().InvokeMember($methodName,"InvokeMethod",$NULL,$object,$methodParameters)
  if ( $output ) { $output }
}

$ADS_ESCAPEDMODE_ON = 2      # see ADS_ESCAPE_MODE_ENUM
$ADS_SETTYPE_DN = 4          # see ADS_SETTYPE_ENUM
$ADS_FORMAT_X500_PARENT = 8  # see ADS_FORMAT_ENUM

$Pathname = New-Object -ComObject "Pathname"
# store initial EscapedMode
$escapedMode = Get-Property $PathName "EscapedMode"
# Enable all escaping
Set-Property $PathName "EscapedMode" @($ADS_ESCAPEDMODE_ON)
Invoke-Method $Pathname "Set" @("CN=Ken Dyer,OU=H/R,DC=fabrikam,DC=com",$ADS_SETTYPE_DN)
Invoke-Method $Pathname "Retrieve" @($ADS_FORMAT_X500_PARENT)
# outputs 'OU=H/R,DC=fabrikam,DC=com'
$escapedMode = Set-Property $PathName "EscapedMode" @($escapedMode)
# set EscapedMode property back to initial value

请注意,Set-Property和Invoke-Method使用数组作为最终参数,因此在调用这些函数时使用@()。

另一答案

比尔斯图尔特的一个不同的方法:

这个想法是通常你不需要/想要创建ComObject的多个实例:

Function Invoke-ComObject([Parameter(Mandatory = $true)]$ComObject, [Switch]$Method, [Parameter(Mandatory = $true)][String]$Property, $Value) {
    If ($ComObject -IsNot "__ComObject") {
        If (!$ComInvoke) {$Global:ComInvoke = @{}}
        If (!$ComInvoke.$ComObject) {$ComInvoke.$ComObject = New-Object -ComObject $ComObject}
        $ComObject = $ComInvoke.$ComObject
    }
    If ($Method) {$Invoke = "InvokeMethod"} ElseIf ($MyInvocation.BoundParameters.ContainsKey("Value")) {$Invoke = "SetProperty"} Else {$Invoke = "GetProperty"}
    [__ComObject].InvokeMember($Property, $Invoke, $Null, $ComObject, $Value)
}; Set-Alias ComInvoke Invoke-ComObject

如果它涉及一个方法,则需要添加–Method开关,对于属性,cmdlet将根据是否提供值自动确定是否需要获取或设置属性。使用此cmdlet,您不需要先创建ComObject并检索例如从一个简单的oneliner获得ComputerNameADSystemInfo(DN):

ComInvoke ADSystemInfo ComputerName

要与PathName做同样的事情:

$EscapedMode = ComInvoke PathName EscapedMode
ComInvoke PathName EscapedMode @($ADS_ESCAPEDMODE_ON)
ComInvoke Pathname -Method Set @("CN=Ken Dyer,OU=H/R,DC=fabrikam,DC=com", $ADS_SETTYPE_DN)
ComInvoke Pathname -Method Retrieve @($ADS_FORMAT_X500_PARENT)
ComInvoke PathName EscapedMode @($EscapedMode)

一个名字NameTranslate例子:

ComInvoke -Method NameTranslate Init @(1, "domain.com")
ComInvoke -Method NameTranslate Set @(8, "User001")
ComInvoke -Method NameTranslate Get @(1)

或者,如果您确实想拥有多个实例,可以先创建ComObject实例,然后将其提供给ComInvoke函数:

$NameTranslate = New-Object -ComObject NameTranslate
ComInvoke -Method $NameTranslate Init @(1, "domain.com")
ComInvoke -Method $NameTranslate Set @(8, "User001")
ComInvoke -Method $NameTranslate Get @(1)

有关最新的Invoke-ComObject版本,请参阅:https://powersnippets.com/invoke-comobject/

以上是关于使用PowerShell包装现有COM对象的主要内容,如果未能解决你的问题,请参考以下文章

PowerShell Windows 窗体包装器

替换或删除后台堆栈上现有片段的代码不起作用

如何在 Java 中实现包装装饰器?

KVC:如何测试现有密钥

如何组合绑定片段而不将它们包装在 XML 文字中

想要将 PSList 包装在 Powershell 函数中以使用管道值