Skip to Content
Technical Articles
Author's profile photo Stefan Schnell

How To Use COM Functions in PowerShell Easily, in Comparison with VBScript, Using the Example of CCo

Over two years ago I presented COM Connector (CCo) here – CCo makes the using of the NetWeaver RFC library very easy.

Slowly but surely the landscape of the script language environments changed in Windows OS – from Windows Script Host (WSH) to PowerShell. From this point of view it seems good to take also a movement.

Here a PowerShell example how to get all installed SAP components and its version numbers in comparison with a VBScript which offers the same information.

#-Begin-----------------------------------------------------------------

  #-Constants-----------------------------------------------------------
    $RFC_OK = 0
    $VarByRef = -1

  #-Includes------------------------------------------------------------
    ."Includes\COM.ps1"

  #-Function Get-SAPComponents------------------------------------------
    Function Get-SAPComponents {

      $SAP = $null
      $SAP = Create-Object "COMNWRFC"
      if ($SAP -eq $null) {
        Break
      }

      $hRFC = Invoke-Method $SAP "RfcOpenConnection" @(
        "ASHOST=NSP, SYSNR=00, CLIENT=001, USER=BCUSER")
      if ($hRFC -eq 0) {
        Free-Object $SAP
        Break
      }

      $hFuncDesc = Invoke-Method $SAP "RfcGetFunctionDesc" @(
        $hRFC, "DELIVERY_GET_INSTALLED_COMPS")
      if ($hFuncDesc -eq 0) {
        $rc = Invoke-Method $SAP "RfcCloseConnection" $hRFC
        Free-Object $SAP
        Break
      }

      $hFunc = Invoke-Method $SAP "RfcCreateFunction" $hFuncDesc
      if ($hFunc -eq 0) {
        $rc = Invoke-Method $SAP "RfcCloseConnection" $hRFC
        Free-Object $SAP
        Break
      }

      $rc = Invoke-Method $SAP "RfcInvoke" @($hRFC, $hFunc)
      if ($rc -eq $RFC_OK) {

        $rc = Invoke-Method $SAP "RfcGetTable" @(
          $hFunc, "TT_COMPTAB", $VarByRef)
        if ($rc -eq $RFC_OK) {

          $hTable = Get-Property $SAP "lngByRef"
          $rc = Invoke-Method $SAP "RfcGetRowCount" @(
            $hTable, $VarByRef)
          $RowCount = Get-Property $SAP "lngByRef"

          $rc = Invoke-Method $SAP "RfcMoveToFirstRow" $hTable

          for ($i = 1; $i -le $RowCount ; $i++) {

            $Row = Invoke-Method $SAP "RfcGetCurrentRow" $hTable

            $rc = Invoke-Method $SAP "RfcGetChars" @(
              $Row, "COMPONENT", $VarByRef, 30)
            $charBuffer = Get-Property $SAP "strByRef"
            $txt = $txt + $charBuffer

            $rc = Invoke-Method $SAP "RfcGetChars" @(
              $Row, "RELEASE", $VarByRef, 10)
            $charBuffer = Get-Property $SAP "strByRef"
            $txt = $txt + $charBuffer

            $rc = Invoke-Method $SAP "RfcGetChars" @(
              $Row, "EXTRELEASE", $VarByRef, 10)
            $charBuffer = Get-Property $SAP "strByRef"
            $txt = $txt + $charBuffer

            $rc = Invoke-Method $SAP "RfcGetChars" @(
              $Row, "COMP_TYPE", $VarByRef, 1)
            $charBuffer = Get-Property $SAP "strByRef"
            $txt = $txt + $charBuffer + "`r`n"

            if ($i -lt $RowCount) {
              $rc = Invoke-Method $SAP "RfcMoveToNextRow" $hTable
            }

          }
        }
      }

      $rc = Invoke-Method $SAP "RfcDestroyFunction" $hFunc
      $rc = Invoke-Method $SAP "RfcCloseConnection" $hRFC
      Free-Object $SAP
      Remove-Variable SAP
      $txt
    }

  #-Function Main-------------------------------------------------------
    Function Main {
      $Components = Get-SAPComponents
      Write-Host $Components
    }

  #-Main----------------------------------------------------------------
    Main

#-End-------------------------------------------------------------------

 

Here now the VBScript:

'-Begin-----------------------------------------------------------------

  '-Directives----------------------------------------------------------
    Option Explicit

  '-Constants-----------------------------------------------------------
    Const RFC_OK = 0

  '-Function GetSAPComponents-------------------------------------------
    Function GetSAPComponents()

      '-Variables-------------------------------------------------------
        Dim SAP, hRFC, rc, hFuncDesc, hFunc, hTable, RowCount, i, Row
        Dim charBuffer, strText

      Set SAP = CreateObject("COMNWRFC")
      If Not IsObject(SAP) Then
        Exit Function
      End If

      hRFC = SAP.RfcOpenConnection("ASHOST=NSP, SYSNR=00, " & _
        "CLIENT=001, USER=BCUSER")
      If hRFC = 0 Then
        Set SAP = Nothing
        Exit Function
      End If

      hFuncDesc = SAP.RfcGetFunctionDesc(hRFC, _
        "DELIVERY_GET_INSTALLED_COMPS")
      If hFuncDesc = 0 Then
        rc = SAP.RfcCloseConnection(hRFC)
        Set SAP = Nothing
        Exit Function
      End If

      hFunc = SAP.RfcCreateFunction(hFuncDesc)

      If hFunc = 0 Then
        rc = SAP.RfcCloseConnection(hRFC)
        Set SAP = Nothing
        Exit Function
      End If

      If SAP.RfcInvoke(hRFC, hFunc) = RFC_OK Then
        If SAP.RfcGetTable(hFunc, "TT_COMPTAB", hTable) = RFC_OK Then
          rc = SAP.RfcGetRowCount(hTable, RowCount)
          rc = SAP.RfcMoveToFirstRow(hTable)
          For i = 1 To RowCount
            Row = SAP.RfcGetCurrentRow(hTable)
            rc = SAP.RfcGetChars(Row, "COMPONENT", charBuffer, 30)
            strText = strText & Trim(charBuffer) & " "
            rc = SAP.RfcGetChars(Row, "RELEASE", charBuffer, 10)
            strText = strText & Trim(charBuffer) & " "
            rc = SAP.RfcGetChars(Row, "EXTRELEASE", charBuffer, 10)
            strText = strText & Trim(charBuffer) & " "
            rc = SAP.RfcGetChars(Row, "COMP_TYPE", charBuffer, 1)
            strText = strText & Trim(charBuffer) & vbCrLf
            If i < RowCount Then
              rc = SAP.RfcMoveToNextRow(hTable)
            End If
          Next
        End If
      End If

      rc = SAP.RfcDestroyFunction(hFunc)
      rc = SAP.RfcCloseConnection(hRFC)
      Set SAP = Nothing

      GetSAPComponents = strText
    End Function

  '-Sub Main------------------------------------------------------------
    Sub Main()
      MsgBox GetSAPComponents()
    End Sub

  '-Main----------------------------------------------------------------
    Main

'-End-------------------------------------------------------------------

 

As you can see it is very comparable.

To establish the comparibility I use an include file, which stores the COM access routines like Create-Object, Invoke-Method etc.

#-Begin-----------------------------------------------------------------

  #-Load assembly-------------------------------------------------------
  [Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic") > $Null
  [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > $Null

  #-Function Create-Object----------------------------------------------
  Function Create-Object([String]$objectName) {
    try {
      New-Object -ComObject $objectName
    }
    catch {
      [Void] [System.Windows.Forms.MessageBox]::Show(
        "Can't create object", "Important hint", 0)
    }  
  }

  #-Function Get-Object-------------------------------------------------
  Function Get-Object([String]$pathName, [String]$class) {
    [Microsoft.VisualBasic.Interaction]::GetObject($pathName, $class)
  }

  #-Sub Free-Object-----------------------------------------------------
  Function Free-Object([__ComObject]$object) {
    [Void] [System.Runtime.Interopservices.Marshal]::ReleaseComObject($object)
  }

  #-Function Get-Property-----------------------------------------------
  Function Get-Property([__ComObject]$object, [String]$propertyName,
    $propertyParameters) {
    $objectType = [System.Type]::GetType($object)
    $objectType.InvokeMember($propertyName,
      [System.Reflection.Bindingflags]::GetProperty,
      $null, $object, $propertyParameters)
  }

  #-Sub Set-Property----------------------------------------------------
  Function Set-Property([__ComObject]$object, [String]$propertyName, 
  	$propertyValue) {
    $objectType = [System.Type]::GetType($object)
    [Void] $objectType.InvokeMember($propertyName,
      [System.Reflection.Bindingflags]::SetProperty,
      $null, $object, $propertyValue)
  }

  #-Function Invoke-Method----------------------------------------------
  Function Invoke-Method([__ComObject]$object, [String]$methodName, 
  	$methodParameters) {
    $objectType = [System.Type]::GetType($object)
    $output = $objectType.InvokeMember($methodName,
      "InvokeMethod", $NULL, $object, $methodParameters)
    if ( $output ) { $output }
  }

#-End-------------------------------------------------------------------

 

/wp-content/uploads/2015/08/001_765268.jpg

With this tiny include you can use COM functions in PowerShell almost like in VBScript.

Let the games begin.

2016/03/08:
Minor changes for PowerShell 5 compatibility.

Assigned Tags

      1 Comment
      You must be Logged on to comment or reply to a post.
      Author's profile photo Stefan Schnell
      Stefan Schnell
      Blog Post Author

      Hello community,

      someone asked me why I use these special functions to call the COM methods, because it is possible to call COM methods with PowerShell directly.

      Not in any case PowerShell supports each COM library. In the case of CCo PowerShell doesn't recognize the methods in the library. I don't know the reason for this behavior. To get around this I use the methods from the dotNET framework.

      Cheers

      Stefan

      P.S. Here an example, the comparison between CCo and VBScript.

      #-Begin-----------------------------------------------------------------
      
        $CCo = New-Object -ComObject "COMNWRFC"
        $CCo | Get-Member | Sort Name | Where {$_.Name -match $Name}
        [Void][Runtime.Interopservices.Marshal]::ReleaseComObject($CCo)
      
        $VBS = New-Object -ComObject "MSScriptControl.ScriptControl"
        $VBS | Get-Member | Sort Name | Where {$_.Name -match $Name}
        [Void][Runtime.Interopservices.Marshal]::ReleaseComObject($VBS)
      
      #-End-------------------------------------------------------------------

      001.JPG