Technical Articles
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-------------------------------------------------------------------
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.
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.