使用PowerShell包装现有的COM对象

8
使用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"),但由于某种原因我无法直接使用它,正如另一个问题(通过PowerShell加载类型库和脚本Windows Live Writer)所建议的那样。
以下是一个一行代码,显示如何获取这样的对象:
((new-object DirectoryServices.DirectoryEntry -a '
LDAP://somedc').Properties.GetEnumerator() |?{$_.PropertyName -eq 'usnChanged' }).Value[0] | Get-Member

有一些专门设计用于与活动目录一起使用的 cmdlet。http://technet.microsoft.com/en-us/library/ee617195.aspx 如果您使用这些 cmdlet,可能会使您的生活更加轻松。 - Andrew Savinykh
你能否提供一个代码示例,返回你遇到问题的特定对象? - Andrew Savinykh
很遗憾,我无法安装任何新的模块;否则我肯定会使用它的。 - Reinderien
我已经添加了一个代码示例。 - Reinderien
这是我对自己的回答:在 .net 中,interop 和特别是 tlb/IDispatch 交互一直很笨拙。在大多数情况下,有方法使其可行。在您的情况下,它是专门为此目的设计的 AD cmdlets。由于对于大多数实际情况问题已经得到了解决,因此没有人真正投资于在这个领域做更多的工作。 - Andrew Savinykh
显示剩余2条评论
2个回答

6

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使用数组作为它们的最后一个参数,因此在调用这些函数时,请使用@( )

2
我已经使用了InvokeMember,但它很丑陋。实际上有一个TLB,但由于某种原因,PS似乎忽略了它。有没有办法列出“真正”的属性和方法可用,获取真正的类型名称或加载正确的TLB? - Reinderien

1

这是一种与Bill Stewart略有不同的方法:

这个想法是通常情况下你不需要/不想创建多个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并检索例如在简单的一行代码中从ADSystemInfo获取ComputerName(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/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接