SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

現役エンジニア直伝! 「現場」で使えるコンポーネント活用術(ActiveReports)

Windows Azure上で帳票コンポーネント「ActiveReports for .NET 6.0J」を動作させてみた

  • X ポスト
  • このエントリーをはてなブックマークに追加

 帳票作成コンポーネント「ActiveReports」は、2010年6月30日に提供されたActiveReports for .NET 6.0J SP2で、Visual Studio 2010に対応しました。このVisual Studio 2010対応版を使って何か新しいことができないかと考えた時に、マイクロソフトが提供するクラウド環境「Windows Azure」上で、ActiveReportsを動かしてみようと思いつきました。今回は、ActiveReportsをWindows Azure上で動作させる際に、筆者が検証した手順をひとつひとつ紐解きながら、実装方法を解説していきます。

  • X ポスト
  • このエントリーをはてなブックマークに追加

クラウド上で帳票ツールを動かしてみる

 帳票作成コンポーネント「ActiveReports」は、2010年6月30日に提供されたActiveReports for .NET 6.0J SP2で、Visual Studio 2010に対応しました。このVisual Studio 2010対応版を使って何か新しいことができないかと考えた時に、マイクロソフトが提供するクラウド環境「Windows Azure」上で、ActiveReportsを動かしてみようと思いつきました。

 Windows AzureではASP.NET Webアプリケーションが動作しますし、ActiveReportsもASP.NET Webアプリケーション上で動かしてPDFを生成できるので、この2つを組み合わせれば、Windows Azureの鬼門の1つである印刷系機能への解決策を見つけることができるのでは、という発想です。

 今回は、ActiveReportsをWindows Azure上で動作させる際に、私が検証した手順をひとつひとつ紐解きながら、実装方法を解説していきます。

※注1

 今回の記事は、グレープシティの保証対象外の使い方をしています。また、筆者としても実際の業務アプリケーション構築案件としては未使用のため、業務アプリケーションとしての使用に耐えうるかは未知数です。Windows AzureのアーキテクチャーとActiveReportsの特性を踏まえて「動くかも!」と試してみた結果、予想以上にきちんと動いたために記事化に至ったというのが経緯である事を、あらかじめご了承ください。

※追記(2010年10月4日 17:40)

 公開当初に紹介していた実装方法では、ActiveReportsをワーカーロール側で動作させるには、不十分だということが判明したため、一部文章とサンプルファイルを更新しました。

Windows Azureとは?

 Windows Azureの詳しい解説は、ActiveReportsの使い方とは別の話になってしまうので割愛しますが、ActiveReportsをWindows Azure上で動作させるために必要な概念について、簡単に説明しておきます。

 Windows Azure Platformは、AppFablic、Compute Service、Storage Service、SQL Azureなどから構成されています。今回は、この中のCompute Serviceを使ってActiveReportsを動作させます。

図1 Windows Azure Platformの構造
図1 Windows Azure Platformの構造

 Windows Azure Compute Serviceは、IISが使える「Webロール」とIISが搭載されていない「ワーカーロール」があり、ASP.NET WebアプリケーションであればWebロールだけでも作成できます。しかし、ビジネスロジックなどはワーカーロールに実装して、それをWebロールから呼び出して使う方がよいため、今回のサンプルでは、次のようなデータの流れで実装していきます。理由は後述します。

  1. ブラウザからWebロール上のASP.NET Webアプリケーションに接続
  2. Webロールからワーカーロールを呼び出す
  3. ワーカーロールでActiveReportsを動作させて、PDFのバイナリデータを作成
  4. 作成されたPDFバイナリデータをWebRoleからブラウザに応答
  5. ブラウザ側のAdobe Readerプラグインで表示
図2 データの流れ
図2 データの流れ

Windows Azure環境の認証レベルについて

 ActiveReportsは、Full認証レベル環境での動作を前提としています。

 今回、Windows Azure環境で動作させるにあたり、Windows Azureの認証レベルについて調べてみると、『MSDN:Windows Azure SDK Trust Policy Reference』内に、「Windows Azure trust」として詳しく定義されていました。これによると、コードアクセスセキュリティ(CAS)のレベルで、ASP.NET medium trust相当(部分的には相違)になっていることが分かります。

 ActiveReportsのヘルプには、「Medium信頼レベル環境でPDFエクスポートを使用する場合、セキュリティの制限によりシステムフォントフォルダにアクセスできない」ため、指定したフォントや位置が正しく印字されない可能性があると記載されています。解決策として、「PDFフォントファクトリ」を使う方法が記載されているので、この方法で回避できるかどうかも確認していきたいと思います。

※注2

 Windows Azure側の機能として、「ServiceConfiguration.cscfg」ファイルのロール定義にenableNativeCodeExecution="true"を追記すればFull認証にできるようなので、こちらの方法でも認証レベルの問題は回避できるかもしれません。

ASP.NET medium trustでのActiveReportsの動作を試す

 Windows Azure trustがASP.NET medium trust相当だとするならば、まずはActiveReportsをASP.NET medium trustで動作させ、どのような制限が加わるか、そして、それをどのように解決すればよいかを調べてみます。

 ASP.NET 4でのActiveReports for .NET 6.0Jの使用は、Visual Studio 2010対応版で動作保障されています。そのため、ASP.NET medium trustの動きを事前に把握することで、動作保障のないWindows Azure環境での動きで不具合が生じたときに、Windows Azure環境特有の問題かどうかの判断ができるはずです。

プログラムの流れ

  1. 「Default.aspx」で[PDF]ボタンをクリックし、「CZ1009Reports.aspx」にリダイレクトする
  2. 「CZ1009Reports.aspx」では、PDF形式のバイナリデータを「CZ1009Reports.aspx」からブラウザへのレスポンスとする
  3. ブラウザ側でAdobe ReaderによりPDF表示
リスト1 PDFファイルをバイナリデータとして生成
Imports DataDynamics.ActiveReports
Imports DataDynamics.ActiveReports.Export.Pdf

Public Class ActiveReportsClass
    Private block As Object = Nothing
    ''' <summary>
    ''' PDFデータを取得する
    ''' </summary>
    Public Function GetPdfDatas() As Byte()
        Dim memStream As System.IO.MemoryStream
        Dim ds As DataSet = GetRecords()

        Using _rpt As New CZ1009ReportsClass
            _rpt.Document.Printer.PrinterName = ""
            _rpt.PageSettings.PaperKind = System.Drawing.Printing.PaperKind.A4
            _rpt.PageSettings.Orientation = Document.PageOrientation.Portrait
            _rpt.PageSettings.Margins.Top = ActiveReport.CmToInch(0.5F)
            _rpt.PageSettings.Margins.Bottom = ActiveReport.CmToInch(0.5F)
            'データを割り当てる
            _rpt.DataSource = ds.Tables("Enviroment")
            ' レポートを作成
            _rpt.Run(False)
            'pdfエクスポートオブジェクトを生成します
            Using _pdf = New PdfExport
                ' PDFの出力用のメモリストリームを作成します
                memStream = New System.IO.MemoryStream
                ' メモリストリームにPDFエクスポートを行います
                _pdf.Security.Use128Bit = True
                _pdf.Security.OwnerPassword = "hatsune"
                _pdf.Security.Permissions = PdfPermissions.AllowPrint
                _pdf.Security.Encrypt = True
                _pdf.Export(_rpt.Document, memStream)
            End Using
        End Using
        Return memStream.ToArray()
    End Function
End Class

medium trust制限をかけないときの動作

 Visual Studio 2010のASP.NET Webアプリケーションテンプレートで新規作成したASP.NET Webアプリケーションは、medium trust制限がかかっていません。この状態でまずは動作させて、プログラムコードが意図したとおりに動くかを確認します。

図3 medium trust制限をかけないときの動作
図3 medium trust制限をかけないときの動作

ASP.NET medium trustでの動作

 ASP.NET medium trustに設定するためには、「Web.config」にtrust要素を追加します。

リスト2 ASP.NET medium trust を設定するWeb.config

<?xml version="1.0" encoding="utf-8"?>

<configuration>
    <system.web>
      <trust level="Medium"></trust>
      <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
    </system.web>

</configuration>
図4 ASP.NET medium trustでの動作
図4 ASP.NET medium trustでの動作

 図4のとおり、medium trustの場合、指定したフォントと異なるフォントが印字され、位置なども正しくありません。

PDFフォントファクトリを設定する

 ActiveReportsのヘルプに、「PDFフォントファクトリ」を使えばmedium trustであっても、指定したフォントが印字されることが記載されていたので、試してみます。

 PDFフォントファクトリの設定は「Web.config」で行います。

リスト3 PDFフォントファクトリを設定するWeb.config
<?xml version="1.0" encoding="utf-8"?>

<configuration>
  <configSections>
    <sectionGroup name="ActiveReports.PdfExport">
      <section name="FontFactory"
        type="DataDynamics.ActiveReports.Web.FontFactorySectionHandler,ActiveReports.Web,
    	Version=6.2.2659.1, Culture=neutral,PublicKeyToken=cc4967777c49a3ff"
        requirePermission="false"/>
    </sectionGroup>
  </configSections>
  <ActiveReports.PdfExport>
    <FontFactory Mode="File">
      <AddFolder VirtualPath="~/Fonts" Recurse="true"/>
      <Substitute Font="Helv" To="Helvetica"/>
      <SetFallbackFont Font="Arial"/>
      <!-- font link nodes -->
      <AddFontLink Font="Arial" List="SimSun,gulim,PMingLiU"/>
      <AddFontLink Font="Tahoma" List="MS UI Gothic,SimSun,gulim,PMingLiU"/>
      <AddFontLink Font="MS UI Gothic" List="SimSun,gulim,PMingLiU,Microsoft Sans Serif" IsDefault="true"/>
    </FontFactory>
  </ActiveReports.PdfExport>

  <system.web>
      <trust level="Medium"></trust>
      <compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
    </system.web>

</configuration>

 次に、プロジェクトに「Fonts」フォルダを作成して、そこに「C:\Windows\Fonts」フォルダから目的のフォントファイルをコピーしておきます(サンプルにはフォルダが含まれていますが、フォントファイルは含んでいません)。

 PDFフォントファクトリを設定し、実際に動かしてみると、図5のように正しいPDFへの印字結果を得ることができました。プログラムコード側は1行の変更も必要ありませんでした。

図5 PDFフォントファクトリ設定時の動作
図5 PDFフォントファクトリ設定時の動作

 なお、PDFフォントファクトリを使ったアプリケーションを配布するときは、今回のサンプルのように、「Fonts」フォルダにコピーするフォントファイルは、設置先の実環境からコピーするのがよいでしょう。

図6 Fontsフォルダの設置
図6 Fontsフォルダの設置

 しかし、最も良いのは、medium trustでアプリケーションを実行する必要があるかどうかを検討することだと思います。

Windows Azureクラウドサービステンプレートを使う

 Visual Studio 2010でWindows Azureアプリケーションを作成するためには、「Windows Azure Tools for Microsoft Visual Studio 2010 1.2」をインストールする必要があります。

 Windows Azure Toolsを追加インストール済のVisual Studio 2010で新規プロジェクトの作成を行うと、テンプレートに[Cloud]-[Windows Azureクラウドサービス]が追加されており、このテンプレートからWindows Azureアプリケーションが作成できます。

図7 Windows Azureクラウドサービステンプレート
図7 Windows Azureクラウドサービステンプレート

ロールの選択

 テンプレートを選択して[OK]をクリックすると、使用するロールの選択ダイアログが表示されます。ASP.NET WebアプリケーションをWindows Azure上で実現するためには、[ASP.NET Webロール]を選択します。

 前述のとおり今回のサンプルでは、ASP.NET Webロールに加え、ActiveReportsでPDFを生成するコードの稼働ロールとしてワーカーロールも使います。これは、ワーカーロールはWindowsサービスのようなロールで、負荷の高い処理などはASP.NET Webロールから呼び出すワーカーロール側に設置するのが、Windows Azureの実用的なコード配置の1つの基準だからです。ただし、今回はActiveReportsを使う方法に主眼を置いているため、サンプルコードを少しでもシンプルにするべく非同期ではなく同期呼び出しとしています。

 ロールを選択し、右側のリストボックスでロール名を変更したら、[OK]ボタンをクリックします(後から変更するよりも、この時点で名前をつけることをお勧めします)。これで、自動的にWindows Azureアプリケーション用のソリューションが作成されます。

図8 ロールの選択
図8 ロールの選択

ソリューションの初期状態からの変更

 Visual Studio 2010のASP.NET WebアプリケーションテンプレートやASP.NET Webサイトテンプレートには空のテンプレートがあり、不要なaspxファイルが生成されません。しかし、今回のサンプルのようにWindows AzureクラウドサービステンプレートでASP.NET Webロールを生成すると、「Defalut.aspx」「About.aspx」「Login.aspx」ファイルが生成されます。そこで、ASP.NET Webロールから不要なaspxファイルやフォルダを削除して、必要最小限の構成にします。

図9 ソリューションの設定
図9 ソリューションの設定

Default.aspxページを作成

 Default.aspxページに、PDFを表示するためのボタンを配置します。Windos Azureアプリケーションといっても、ASP.NET Webロールであれば、ページのデザインはASP.NET Webフォームと同じです。ツールボックスからButtonコントロールをドラッグ&ドロップして、プロパティを設定すれば完成です。

Windows Azureに発行

 WebロールのGUIができたので、コンパイルエラーがなければ、ActiveReports関連のコードを組み込む前にWindows Azure上で動作するかどうかをクラウドサービスに発行して動きを確認してみます。

 クラウドサービスに発行する前に、利用するためのアカウント設定が必要です。詳しくは筆者ブログのエントリー「Windows AzureをMSDN特別導入プランで無料利用するための設定方法」などを参考にしてください。

 クラウドサービスに発行するには、ソリューションエクスプローラーからクラウドサービス定義のプロジェクトを右クリックして、[発行]を選択します。[クラウドサービスをWindows Azureに配置します]を選択し、資格情報などを選んで[OK]をクリックすれば、自動的にWindows Azureに転送が行われ、配置されます。

図10 クラウドサービス発行の流れ
図10 クラウドサービス発行の流れ

Windows Azureアプリケーションの実行

 発行が完了したら、最後に表示されているWebサイトのURLをクリックすると、Windows Azureアプリケーションをブラウザから呼び出せます。

図11 Windows Azureアプリケーション
図11 Windows Azureアプリケーション

 もちろん、この時点ではPDF作成部分は組み込んでいないので[PDF出力]ボタンをクリックしても何も起こらないのが正常な動作です。ここまで確認できたら、次はActiveReports関連のコードを組み込んでいきます。

ActiveReportsを動作させるワーカーロールコード

 PDFはワーカーロール側で生成するため、ActiveReports関連のコードはすべてワーカーロール側に記述します。サンプルプログラム作成開始時点では、もちろんWindows Azure特有の問題やその対応方法も分からなかったので、ActiveReportsを使用するクラスライブラリの作成時と同じ手順でプログラミングしてみました。その時の作業を順番に追ってみます。

ActiveReportsの参照設定

 まず、ソリューションエクスプローラーで「CZ1009CloudWorkerRoleワーカーロール」を右クリックして、「Graprcity ActiveReports for .NET」「Document」「PDF Export」「Viewer」のコンポーネントへの参照を追加します。

図12 参照の追加
図12 参照の追加

ActiveReports 6.0Jファイルの追加

 参照の追加が完了したら、再びソリューションエクスプローラーで「CZ1009CloudWorkerRoleワーカーロール」を右クリックして、[追加]-[新しい項目]を選択し、ActiveReportsの帳票定義体ファイルを追加します。

図13 「ActiveReports 6.0Jファイル」の追加
図13 「ActiveReports 6.0Jファイル」の追加

 図13を見れば分かるように、ActiveReportsの定義体には、2つのファイル形式があります。「ActiveReports 6.0Jファイル」を選択した場合には、クラスファイルとして定義体が追加され、WindowsフォームやWebフォームのようにビルド後はアセンブリの一部となります。もちろん、IDEで該当ファイルをダブルクリックすれば、ActiveReportsの帳票デザイナが開きます。

 もう1つの「ActiveReports 6.0Jファイル(XMLベース)」を選択した場合は、rpxファイルとしてプロジェクトに追加され、ビルド後もアセンブリとは別のrpxファイルとして動作環境に配置されます。つまり、動作環境上のrpxファイルを編集することで、再ビルドなしに帳票定義体の修正ができるという利点があります。業務アプリケーションでは、帳票のちょっとした修正というのは意外と多いため、筆者はこのファイル形式をよく使っています。

 しかし、この形式は実行時にrpxファイルの読み込みが必要なため、動作環境のどこにrpxファイルがあるかが分かっていなくてはなりません。ASP.NET Webロールの場合は、「My.Request.PhysicalApplicationPath」プロパティで位置が判明しますが、ワーカーロールの場合は、どのようにファイルパスを取ればよいか明確には分からなかったため、今回はアセンブリの一部となる「ActiveReports 6.0Jファイル」形式を採用しました。

ActiveReports 6.0J帳票定義体の定義

 ActiveReportsの帳票定義体デザイナは、非常に使いやすい操作性になっています。基本的には、Windowsフォームデザイナと同じようにツールボックスからコントロールをドラッグ&ドロップし、配置するときにはスナップ線を頼りに最適な位置を決めることができます。Windowsフォームデザイナと異なるのは、コントロールとコントロールの間が0ピクセル、つまり開きがない状態が最適な位置と設定されている点です。

 一覧表などを作る際、表のタイトルをPageHeaderセクションに配置し、データ部分をDetailセクションに配置するような場合でも、セクションをまたがってスナップ線が表示できるため、タイトル位置とデータ位置を容易に合わせることができます。

図14 ActiveReportsの帳票定義体デザイナ
図14 ActiveReportsの帳票定義体デザイナ

 また、CrossSectionLineコントロールやCrossSectionBoxコントロールを使えば、セクション間をまたがった罫線も簡単に引くことができ、一覧表の罫線部分も簡単に定義できます。さらに、PageFooterセクションまで含めておけば、“データがなくてもページの下までいつも同じ大きさで枠線を引いておく”といった設定も簡単に実現できます。

図15 CrossSectionBoxによる罫線定義
図15 CrossSectionBoxによる罫線定義

 ただし、CrossSectionLineコントロールとCrossSectionBoxコントロールのStartプロパティとEndプロパティのプロパティ設定値はcm(センチメートル)単位ではなく、インチ単位なので注意が必要です。プロパティで位置を決めるときは、cmからインチへの単位変換を行い、その値を小数点以下まで含めてプロパティに設定するようにしないと、Lineなど他のコントロールがcmでプロパティを設定しているので、微妙にずれることがあります。

HTTP内部エンドポイントの追加

 Webロールからワーカーロールを呼び出すには、Windows Azure Storage Serviceのキューを使って非同期に連携する方法もありますが、非同期生成したPDFデータの扱いなどの考慮も必要になるため、今回はワーカーロールにHTTPで呼び出す内部インターフェース(内部エンドポイント)を定義して、それを同期的に呼び出すシンプルな方式にしました。

 HTTPを使った内部エンドポイントを定義するには、ソリューションエクスプローラーからクラウドサービス定義(サンプルでは「CZ1001Cloud」)にあるワーカーロール定義のプロパティで、エンドポイントの追加を行います。今回定義するエンドポイントは、種類を「internal」としてAzure内部でのみ使用するように定義し、プロトコルに「http」を指定してWebロールからHTTPで呼び出せるようにします。

HTTP内部エンドポイントの設定

 HTTP内部エンドポイントを設定したら、生成直後のワーカーロールにはRunメソッド、OnStartメソッド、RoleEnviromentChangingメソッドのコードが既に記載されているので、Runメソッドにエンドポイントが呼び出された時の処理を追加します。

リスト4 エンドポイントに対する処理
Private Shared listener As New HttpListener

Public Overrides Sub Run()
    Dim endpoint As IPEndPoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints("HttpEndpoint").IPEndpoint

    listener.Prefixes.Add(String.Format("http://{0}:{1}/", endpoint.Address, endpoint.Port))
    listener.Start()
    Do While (True)
        Dim context As HttpListenerContext = listener.GetContext()

        Using mes As System.IO.Stream = context.Response.OutputStream
            Try
                Dim byteMsg As Byte() = GetPdfDatas(False)

                mes.Write(byteMsg, 0, byteMsg.Length)
            Catch ex As Exception
                Dim msg As String = ex.Message
                Dim byteMsg As Byte() = System.Text.Encoding.UTF8.GetBytes(msg)

                mes.Write(byteMsg, 0, byteMsg.Length)
            End Try
        End Using
    Loop
End Sub

 エンドポイントの処理は、エンドポイントに対してlistenerを定義して呼び出しを待ち合わせし、呼び出されたら対応した処理を行うということになります。

 listenerの定義は、エンドポイント名を「HttpEndPoint」とした場合、InstanceEndpoints("HttpEndpoint")でエンドポイントを取得して、HttpListenerに追加(Add)します。後は、Startメソッドでlistenerを開始してGetContextメソッドで呼び出しを待ちます。

PDF生成コードの作成

 GetContextメソッドで待ち合わせていた呼び出しが行われたら、PDF作成用のGetPdfDatasメソッドを呼び出します。GetPdfDatasメソッドには、次のようなコードを記述します。

リスト5 PDFファイルをバイナリデータとして生成

     :
    (中略)
     :
Imports DataDynamics.ActiveReports
Imports DataDynamics.ActiveReports.Export.Pdf

Public Class WorkerRole
    Inherits RoleEntryPoint
     :
    (中略)
     :
    Private block As Object = Nothing
    ''' <summary>
    ''' PDFデータを取得する
    ''' </summary>
    Public Function GetPdfDatas() As Byte()
        Dim memStream As System.IO.MemoryStream
        Dim ds As DataSet = GetRecords

        Using _rpt As New CZ1009Reports
            _rpt.Document.Printer.PrinterName = ""
            _rpt.PageSettings.PaperKind = System.Drawing.Printing.PaperKind.A4
            _rpt.PageSettings.Orientation = Document.PageOrientation.Portrait
            _rpt.PageSettings.Margins.Top = ActiveReport.CmToInch(0.5F)
            _rpt.PageSettings.Margins.Bottom = ActiveReport.CmToInch(0.5F)
            'データを割り当てる
            _rpt.DataSource = ds.Tables("Enviroment")
            ' レポートを作成します
            _rpt.Run(False)
            ' PDFエクスポートオブジェクトを生成します
            Using _pdf = New PdfExport
                ' PDFの出力用のメモリストリームを作成します
                memStream = New System.IO.MemoryStream
                ' メモリストリームにPDFエクスポートを行います
                _pdf.Security.Use128Bit = True
                _pdf.Security.OwnerPassword = "hatsune"
                _pdf.Security.Permissions = PdfPermissions.AllowPrint
                _pdf.Security.Encrypt = True
                _pdf.Export(_rpt.Document, memStream)
            End Using
        End Using
        Return memStream.ToArray()
    End Function
     :
    (中略)
     :
End Class

 メソッドの中のコードは、リスト1で紹介したASP.NET Webアプリケーションのコードとまったく同じです。

ActiveReportsを動作させるWebロールコード

ASP.NET Webロールからワーカーロールを呼び出す

 今回のサンプルでは、Default.aspxの[PDF出力]ボタンをクリックすると「My.Response.Redirect("CZ1009Reports.aspx")」を実行して、CZ1009Reports.aspxページを呼び出します。このCZ1009Reports.aspxページで、ワーカーロールを呼び出します。

 ワーカーロールの呼び出し方は、動的に決定される内部エンドポイントのIPアドレスとポート番号を取得し、webClient.DownloadDataメソッドを使って呼び出します。

リスト6 ASP.NET Webロールからワーカーロールを呼び出す
Public Class CZ1009Reports
    Inherits System.Web.UI.Page
    Private block As Object = Nothing

    Protected Sub Page_Load(ByVal sender As Object,
                            ByVal e As System.EventArgs) _
                        Handles Me.Load
        If Not Me.IsPostBack Then
            Call GetRecords(isEmbedFonts)
        End If
    End Sub

    Private Function GetRecords(ByVal isEmbedFonts As Boolean) As Boolean
        Using webClient As New System.Net.WebClient
            Dim userID As String = My.User.Name
            Dim workerEndPoint = Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.Roles("CZ1009CloudWorkerRole").Instances(0).InstanceEndpoints("HttpEndpoint")
            Dim pdfStream() As Byte
            Dim pdf() As Byte = System.Text.Encoding.UTF8.GetBytes("%PDF")

            pdfStream = webClient.DownloadData("http://" & workerEndPoint.IPEndpoint.ToString)
            If pdfStream(0) = pdf(0) AndAlso pdfStream(1) = pdf(1) AndAlso pdfStream(2) = pdf(2) AndAlso pdfStream(3) = pdf(3) Then
                Response.ContentType = "application/pdf"
                Response.AddHeader("content-disposition", "inline; filename=AzureActiveReports.PDF")
            End If
            Response.BinaryWrite(pdfStream)
            Response.End()
        End Using
        Return True
    End Function
End Class

ActiveReportsを動作させた実行例

Windows Azureでの実行

 ビルドしてコンパイルエラーがなかったら、Windows Azureに発行して動作を確認してみましょう。

 結果は、[PDF出力]ボタンをクリックするとCZ1009Reports.aspxページが呼び出されるところまではいいのですが、その後は残念ながら“ActiveReportsのDLLがWindows Azure上に存在しない”というエラーになってしまいます。

開発環境での実行

 Windows AzureアプリケーションをVisual Studio 2010上で動かすと、Development FabricとDevelopment Storageが起動され、その環境を使って動作します。これは、ASP.NET WebアプリケーションをVisual Studio 2010上で動かしたときのASP.NET開発サーバーに相当するものです。

 今回のサンプルを開発環境で動作させると、ActiveReportsが開発環境に入っているので、当然、Windows Azureで動作させたときのエラーは発生しません。しかし、そのかわりにリスト5の「_pdf.Export(_rpt.Document, memStream)」行で「Failed To read TTF file, or no CMAP table found」という実行時エラーが発生してしまいました。

図16 開発環境で発生した実行時エラー
図16 開発環境で発生した実行時エラー

Windows Azure環境の問題点切り分けと対処方法

ActiveReportsのDLLをWindows Azureに設置する

 最初に、問題点をクリアにするために、“開発環境にActiveReportsがあってWindows Azure環境にはActiveReportsがない”という状態を解消し、開発環境とWindows Azure環境を同一環境にしたいと思います。

 GAC(Global Assembly Cache)にあるDLLをコピーするためには、通常と同じように、参照設定にあるActiveReportsのDLLファイルの[ローカルコピー]プロパティを「True」に設定すれば、ビルドしたアセンブリと同じ実行時フォルダにコピーされるはずです。

図17 参照設定したDLLをローカルコピーする
図17 参照設定したDLLをローカルコピーする

 この設定により、ActiveReportsのDLLファイルがWindows Azureの配布対象になるので、発行を行えば他のアセンブリと一緒にWindows Azureにコピーされ、環境を一緒にした訳ですから、Windows Azureでも“ActiveReportsのDLLがない”というエラーではなく、「Failed To read TTF file, or no CMAP table found」という実行時エラーになるはずです。

 そこで、実際に発行してみてWindows Azureでの動作を確認してみます。

Windows Azure上でActiveReportsからPDF出力

 結果は、正常に動作し、PDFがブラウザに表示されました。

図18 Windows Azure上でActiveReportsからPDF出力
図18 Windows Azure上でActiveReportsからPDF出力

 しかも、単に表示されただけではなく、先ほどASP.NET WebアプリケーションをASP.NET medium trustで動作させたときに確認したような、印字ずれも生じていないPDFになっています。

 つまり、Windows Azureのワーカーロール(別途確認していますがWebロールも同様)の認証レベルは、ActiveReportsの動作環境的には、medium trustとは異なると言えると思います。

開発環境の問題点切り分けと対処方法

 Windows Azureの実行環境では発生しない「Failed To read TTF file, or no CMAP table found」のエラーですが、開発環境であるDevelopment Fabric上では発生してしまいます。やはり、実行環境と開発環境では多少の違いがあるのだと想定できます。

 そこで、ASP.NET WebアプリケーションでActiveReportsがmedium trustでの制限を回避する方法として先ほど紹介した、PDFフォントファクトリ機能を使ってみたいと思います。

ワーカーロールでPDFフォントファクトリ機能を使う

 ワーカーロール側でActiveReportsを使っていたので、「Fonts」フォルダを作成してフォントファイルをコピーし、ワーカーロールのApp.ConfigにPDFフォントファクトリの設定をしました。

 App.ConfigにSystem.Web要素がないため、trust要素をどうやって記述しようかと悩みましたが、開発環境であるDevelopment FabricがASP.NET medium trustで動作していると推測できるため、trust要素を書かなくても認証レベルはmedium trustであるはずだと考えました。

 さっそく動作させてみたのですが、「VirtualPath="~/Fonts"」の指定が間違えているとのエラーになってしまいます。つまり、PDFフォントファクトリの設定は、ワーカーロールでは難しいということです。

 Windows Azure環境では動作するので悔しいところですが、ワーカーロール側でActiveReportsを使うならば、“現時点では”開発環境でのテスト実行はあきらめないといけないようです。

Azure環境と開発環境で動くActiveReportsアプリ

 Windows Azure環境では動作して開発環境では動作しないという状況は、業務アプリケーションを構築する上ではかなりのマイナス要因です。そこで、ASP.NET medium trustでは動作させられたことを踏まえ、ASP.NET Webアプリケーションを動作させるWebロールだけを使って、ActiveReportsを使ったPDF出力を実装して確認してみたいと思います。

WebロールでPDFフォントファクトリ機能を使う

 最初に作成したASP.NET Webアプリケーションの構成と同様に、Webロール側でActiveReportsを使い、さらにPDFフォントファクトリ機能を使ってみました。すると、ワーカーロールの時と同じくWindows AzureでのActiveReportsのDLL問題は発生しましたが、同じ様にDLLをローカルコピーで配置する手法で解決できました。また、PDFフォントファクトリの設定をしなくても、Windows Azure上では動作します。

 後は、開発環境のDevelopment Fabric上での動作がエラーになる点を解決するだけです。PDFフォントファクトリの設定は次のようになります。

  1. ソリューションエクスプローラーで、すべてのファイルを表示
  2. WebロールでDLLをコピーするときは、DLLを参照設定して「ローカルコピー」プロパティを「True」に
  3. PDFフォントファクトリではActiveReports.Web.DLLを使うので、ActiveReports.Web.DLLも参照の追加を行い[ローカルコピー]プロパティ値も「True」に
  4. 「Fonts」フォルダを作成し、フォントをコピー
  5. 「Fonts」フォルダをプロジェクトに追加
  6. フォントファイルのプロパティを「コンテンツ」に設定

 上記のようにPDFフォントファクトリを設定したWindows Azureアプリケーションを、開発環境のDevelopment Fabric上で動かすと、正常に動作しました。そして、Windows Azure上に発行して動かしても、同じく正常に動作します。

さいごに

 最終的にうまくいきましたが、Windows Azureに既にインストールされているフォントファイルを、Windows Azure環境にコピーして使うしかないという点には、納得ができませんでした。コンポーネント側で、開発環境ではPDFフォントファクトリが有効になり、Windows Azureへの発行時には無視されるような仕組みが用意される。もしくは、プラットフォーム側でWindows AzureとDevelopment Fablicの相違がなくなる、といった対応が行われれば、今後、利用できる可能性が広がりそうです。

 Windows AzureでActiveReportsを使用する際のポイントは、配布先にActiveReportsが配布されていない、そして、インストールできないようなIIS環境で動作させたいときと類似しています。これは、ASP.NET 4が提供されているホスティングサービスを利用するときと同じです。

 その他にも、何点か調整事項がありましたが、最終的には開発環境でもWindows Azure環境でも動作しました。しかし、最初に記述したとおり、この方法はグレープシティの動作保証対象外の使い方なので、業務アプリケーション構築に使うかどうかは決心がつきかねるでしょう。できれば、この記事を元にグレープシティで動作検証やヘルプの記述の拡充を行っていただき、Windows Azureでの動作も保証対象に加えていただけたら喜ばしい限りです。

※注3

 Windows Azureのコンピューティング時間に対する課金はプログラムを発行した時点で発生します。また、00分を基準にした1時間単位で課金されるため、00分過ぎに発行して動作確認が終わったら、59分くらいまでに「Azure Services Developer Portal」から、アプリケーションの削除を行ってください。削除するには、「Hosted Service」で[Suspend]ボタンをクリックしてSTOP状態にしてから、[Delete]ボタンをクリックします。Suspendで、STOPの状態になっていても課金されるので注意してください。

製品情報

修正履歴

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5477 2010/10/04 17:42

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング