简体   繁体   English

在 Powershell 中使用 SOAP 呈现带参数的 SSRS 报告

[英]Render SSRS Report with parameters using SOAP in Powershell

I've been toying with this for days with no luck.我一直在玩这个好几天没有运气。 Essentially I'm trying to build a simple library to render SSRS reports using Powershell.本质上,我正在尝试构建一个简单的库来使用 Powershell 呈现 SSRS 报告。 I'm using Powershell in an attempt to ease development later on (Instead of coding a C# app for each project).我正在使用 Powershell 试图简化以后的开发(而不是为每个项目编写 C# 应用程序)。 Mostly this will be used to schedule various things with reports.大多数情况下,这将用于通过报告安排各种事情。

I've got report rendering mostly working in Powershell.我的报告呈现主要在 Powershell 中工作。 The one thing I can't figure out is how to supply parameters to the report before calling the render method.我无法弄清楚的一件事是如何在调用渲染方法之前向报告提供参数。 I've found plenty of code pertaining to C# and VB (which I've used in other SSRS projects), however I'm unable to convert this to Powershell.我发现了大量与 C# 和 VB 相关的代码(我在其他 SSRS 项目中使用过),但是我无法将其转换为 Powershell。

As I'm fairly new to Powershell, I'm unfamiliar with the proper way to do this.由于我对 Powershell 相当陌生,因此我不熟悉执行此操作的正确方法。 Here's the code I've been using:这是我一直在使用的代码:

$ReportExecutionURI = "http://glitas10//ReportServer//ReportExecution2005.asmx?wsdl"
$ReportPath = "/Financial/ExpenseReportStub"
$format = "PDF"

$deviceInfo = "<DeviceInfo><NoHeader>True</NoHeader></DeviceInfo>"
$extension = ""
$mimeType = ""
$encoding = ""
$warnings = $null
$streamIDs = $null

$Reports = New-WebServiceProxy -Uri $ReportExecutionURI -UseDefaultCredential

# Load the report 
$Report = $Reports.GetType().GetMethod("LoadReport").Invoke($Reports, @($ReportPath, $null))

# Render the report
$RenderOutput = $Reports.Render($format, $deviceInfo, [ref] $extension, [ref] $mimeType, [ref] $encoding, [ref] $warnings, [ref] $streamIDs)

That works fine on reports that don't require parameters, obviously.显然,这对不需要参数的报告很有效。

Any ideas on what I need to do to instantiate the proper object and pass parameters?关于我需要做什么来实例化正确的对象和传递参数的任何想法?

Here's some information on the solution that I ended up using, in case anyone else needs to do the same.这是我最终使用的解决方案的一些信息,以防其他人需要做同样的事情。 It works really well.它真的很好用。

The first approach that worked was building a DLL to use by the Powershell script.第一种可行的方法是构建一个 DLL 以供 Powershell 脚本使用。 This worked fine, but it causes two problems.这工作正常,但会导致两个问题。 First, your script had to tote around a DLL.首先,您的脚本必须围绕一个 DLL。 Second, this DLL was tied to a specific SSRS server.其次,这个 DLL 被绑定到一个特定的 SSRS 服务器。 In order to access another server, you had to use multiple DLLs.为了访问另一台服务器,您必须使用多个 DLL。

Eventually, I moved back to using a web proxy.最终,我改回使用网络代理。 The key here is to use namespaces so that you can instantiate a ParameterValue object.这里的关键是使用命名空间,以便您可以实例化 ParameterValue 对象。 Here's the code:这是代码:

# Create a proxy to the SSRS server and give it the namespace of 'RS' to use for
# instantiating objects later.  This class will also be used to create a report
# object.
$reportServerURI = "http://<SERVER>/ReportServer/ReportExecution2005.asmx?WSDL"
$RS = New-WebServiceProxy -Class 'RS' -NameSpace 'RS' -Uri $reportServerURI -UseDefaultCredential
$RS.Url = $reportServerURI

# Set up some variables to hold referenced results from Render
$deviceInfo = "<DeviceInfo><NoHeader>True</NoHeader></DeviceInfo>"
$extension = ""
$mimeType = ""
$encoding = ""
$warnings = $null
$streamIDs = $null

# Next we need to load the report. Since Powershell cannot pass a null string
# (it instead just passses ""), we have to use GetMethod / Invoke to call the
# function that returns the report object.  This will load the report in the
# report server object, as well as create a report object that can be used to
# discover information about the report.  It's not used in this code, but it can
# be used to discover information about what parameters are needed to execute
# the report.
$reportPath = "/PathTo/Report"
$Report = $RS.GetType().GetMethod("LoadReport").Invoke($RS, @($reportPath, $null))

# Report parameters are handled by creating an array of ParameterValue objects.
$parameters = @()

$parameters += New-Object RS.ParameterValue
$parameters[0].Name  = "Parameter 1"
$parameters[0].Value = "Value"

$parameters += New-Object RS.ParameterValue
$parameters[1].Name  = "Parameter 2"
$parameters[1].Value = "Value"

# Add the parameter array to the service.  Note that this returns some
# information about the report that is about to be executed.
$RS.SetExecutionParameters($parameters, "en-us") > $null

# Render the report to a byte array.  The first argument is the report format.
# The formats I've tested are: PDF, XML, CSV, WORD (.doc), EXCEL (.xls),
# IMAGE (.tif), MHTML (.mhtml).
$RenderOutput = $RS.Render('PDF',
    $deviceInfo,
    [ref] $extension,
    [ref] $mimeType,
    [ref] $encoding,
    [ref] $warnings,
    [ref] $streamIDs
)

# Convert array bytes to file and write
$Stream = New-Object System.IO.FileStream("output.pdf"), Create, Write
$Stream.Write($RenderOutput, 0, $RenderOutput.Length)
$Stream.Close()

It seems rather easy, and it is.这似乎很容易,而且确实如此。 This method works exceptionally well and is the method I'm using now to render and email scheduled reports, as it provides much more flexibility than the built in SSRS scheduling.这种方法非常有效,是我现在用来呈现和通过电子邮件发送计划报告的方法,因为它比内置的 SSRS 计划提供了更大的灵活性。 In addition, it's relatively fast.此外,它相对较快。 One of the scripts I'm using to mail out reports can render and send out about 20-30 reports a minute.我用来邮寄报告的脚本之一每分钟可以渲染和发送大约 20-30 份报告。

I had a similar issue.我有一个类似的问题。 It took time to figure out the issue.花了一些时间来弄清楚这个问题。 You should not "revoke" the report without parameters if needed Therefore the code should look like this:如果需要,您不应在没有参数的情况下“撤销”报告因此代码应如下所示:

try {
    <# Despose and clear resources if open #>
    if ($RS) { $RS.Dispose() }
    if ($Stream) { $Stream.Close() }

    <# Create Report Service #>
    [string]$reportServerURI = "<SSRS Service URL>"
    $RS = New-WebServiceProxy -Class 'RS' -NameSpace 'RS' -Uri $reportServerURI -UseDefaultCredential
    $RS.Url = $reportServerURI
    <# Set up some variables to hold referenced results from Render #>
    $deviceInfo = "<DeviceInfo><NoHeader>True</NoHeader></DeviceInfo>"
    $extension = ""
    $mimeType = ""
    $encoding = ""
    $warnings = $null
    $streamIDs = $null

    <# Initial Report #>
    $reportPath = "<Full path/URL to rdl file>"
    ## Do not revoke the report ## $Report = $RS.GetType().GetMethod("LoadReport").Invoke($RS, @($reportPath, $null))
    <# Initial Report Parameters Array #> 
    $Parameters = $RS.GetType().GetMethod("LoadReport").Invoke($RS, @($reportPath, $null)).Parameters
    
    <# Populate Report Parameters values #>
    $Params = @()
    Foreach ($Parameter in $Parameters ) {
        $par1 = New-Object RS.ParameterValue;
        $Par1.Name = $Parameter.Name;
        $Par1.Label = $Parameter.Name;
        switch ($Par1.Name) {
                "<1st Param Name>"      { $par1.Value = <1st Param Value>; break }
                "<2nd Param Name>"      { $par1.Value = <2nd Param Value>; break }
                ...
                "<#n Param Name>"      { $par1.Value = <#n Param Value>; break }
            }
        $Params += $Par1;
        }

    <# Execute/invoke the report with the parameters #>
    $RS.SetExecutionParameters($Params, "en-us") > $null
    
    <# Set report render output format#>
    [string]$format = <"PDF","Excel" etc.>

    <# Eecute Report render #>
    try { $RenderOutput = $RS.Render($format,
    $deviceInfo,
    [ref] $extension,
    [ref] $mimeType,
    [ref] $encoding,
    [ref] $warnings,
    [ref] $streamIDs)       
    } catch { Log-Message -message "Unable to render or save the report due to an error." -IsError $true; throw
    }
    
    <# Convert array bytes to file and write #>
    $Stream = New-Object System.IO.FileStream(<Final Report Output File), Create, Write
    $Stream.Write($RenderOutput, 0, $RenderOutput.Length)
    $Stream.Close()
    if ($RS) { $RS.Dispose() }
    }catch{ Log-Message -message "Error in Execute-Report, could not stream or other error." -IsError $true; throw }

Problem solved.问题解决了。

Had the same issue, furthermore wanted to send the generated MHT file as an email body: The following was found to work The old CDO.Message is the only thing I found that allows sending a MHTML file as an email body.有同样的问题,还想将生成的 MHT 文件作为电子邮件正文发送: 发现以下内容有效 旧的 CDO.Message 是我发现的唯一允许将 MHTML 文件作为电子邮件正文发送的内容。 Below is a (working) translation of a VB program Old but simple ;-)!下面是一个 VB 程序的(工作)翻译旧但简单;-)!

################## Send MHTML email ##############################
# use antiquated CDO to send mhtml as email body

$smtpServer = "my-mail-server"
$smtpSubject = "MHT file sent as body of email"
$smtpTo = "you@work.com"
$smtpFrom = "me@home.org"
$MHTMLfile = "my-MHT-File.mht
# e.g. from an SSRS.Render


$AdoDbStream = New-Object -ComObject ADODB.Stream
$AdoDbStream.Charset = "ascii"
$AdoDbStream.Open()
$AdoDbStream.LoadFromFile($MHTMLfile)
$CdoMessage = New-Object -ComObject CDO.Message
$CdoMessage.DataSource.OpenObject($AdoDbStream,"_Stream")

$SendUsingPort = 2
$smtpPort = 25

$cfg = "http://schemas.microsoft.com/cdo/configuration/"
$CdoMessage.Configuration.Fields.Item($cfg + "sendusing") =  $SendUsingPort
$CdoMessage.Configuration.Fields.Item($cfg + "smtpserver") = $SmtpServer
$CdoMessage.Configuration.Fields.Item($cfg + "smtpserverport") = $smtpPort 

$CdoMessage.To      = $smtpTo
$CdoMessage.From    = $smtpFrom
$CdoMessage.Subject = $smtpSubject

$CdoMessage.MimeFormatted = $true
$CdoMessage.Configuration.Fields.Update()

WRITE-HOST "Sending email"
$CdoMessage.Send()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM