質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.95%

WebBrowserの動作に影響するPC環境の相違点

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,471

score 11

 前提・実現したいこと

単体では正常動作を確認できている実行ファイルが、
本番環境で他のASP.NETアプリケーションから呼び出すとエラーになります。
Foam_Loadの末尾にWebBrowserから指定URLにナビゲートする際に
エラーが発生しているのですが、
どこをどう修正すればいいのか検討がつかない状態です。

私自身がWeb系開発の初心者で、
周囲にもASP.NET Web フォームアプリケーション開発に詳しい人がいないため
初歩的な知識が抜けている可能性があります。

特定条件でのみ発生するため、環境の違いが原因だと考えています。
確認すべき点や、検索ワードのアドバイスをいただけないでしょうか。

【大まかな処理の流れ】
1.許可IPアドレスからPOSTがあればWebAPIがA.exeを実行。
2.A.exeの処理の途中でB.exeを実行。
3.B.exeの完了後にA.exeの残りの処理を実施。
4.A.exeの完了後にWebAPIからHTTPレスポンスを送信。

【実行ファイルB.exeの概要】
WebBrowserで特定のサイトにアクセスし、
ログイン~データ登録までを自動処理する
ASP.NET Webフォームアプリケーションです。

 発生している問題・エラーメッセージ

B.exeのWebBrowserでのサイトへの初回アクセス時に
サイト内のファーストパーティークッキーが無効な場合のエラー画面に遷移。
ログイン画面に入ることができず、残りの処理を行えないためエラーになります。
初回のDocumentCompletedイベントの段階で、アクセスしたかったURLの末尾に
おそらくエラー時のURLパラメータが追加されており、
改めてブラウザで開くとエラー内容のみ記載された画面が表示されました。
(「jsp」拡張子のウェブサイトについては全く知識が無いため
内部の処理はいまいち想像できていません。
クッキーに関係無くこのようなエラー画面を出すこともあるのでしょうか?)

本来アクセスしたいURL(例):https://xxxxxxx/login.jsp
エラー時に取得したURL(例):https://xxxxxxx/login.jsp?Try=y

WebAPI→A.exe呼び出し(2018/10/31追記)

Dim MyProc As Process
MyProc = New Process
MyProc.EnableRaisingEvents = False
MyProc.StartInfo.FileName = "C:\xxxxx\A.exe"
MyProc.StartInfo.Arguments = "99999" '引数はユーザーID
MyProc.Start()


A.exe→B.exe呼び出し(2018/10/31追記)

Dim procUp As New Process
procUp.StartInfo.FileName = "C:\xxxxx\B.exe"
'引数はエラーログとサイトでのアップロードに使用するファイルのパス
procUp.StartInfo.Arguments = "C:\xxxxx\err.txt" & " " & "C:\xxxxx\upload.csv"

Try
    procUp.Start()
Catch ex As Exception
    'exeの実行で例外が出た場合は即中止
    MyErrStream = New StreamWriter(wErrFilePath, True, System.Text.Encoding.GetEncoding("Shift-JIS"))
    MyErrStream.WriteLine("B.exe実行時に例外が発生しました。(" & ex.GetType().ToString & ")")
    ExeErrFG = True
End Try

If ExeErrFG = False Then
    procUp.WaitForExit(600000)
    If procUp.HasExited = False Then
        procUp.Kill()
    End If
    '(省略)
End If

B.exe内のWebBrowser関連の処理(2018/10/31追記)

Public WithEvents WebBrowser1 As New WebBrowser2()
Private pURL As String = "https://xxxxxxx/"

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        '(省略)

        '拡張したWebBrowser2をFormに追加
        WebBrowser1.Dock = DockStyle.Fill
        Controls.Add(WebBrowser1)
        WebBrowser1.ScriptErrorsSuppressed = True
        WebBrowser1.Navigate(pURL & "login.jsp") 'ログイン画面
End Sub

Private Sub WebBrowser1_DocumentCompleted(sender As Object, e As WebBrowserDocumentCompletedEventArgs) Handles WebBrowser1.DocumentCompleted
        '「試したこと」に記載しているクッキーのチェックを行ったコード
        'MyWStream.WriteLine("url:" & WebBrowser1.Document.Url.ToString)
        'MyWStream.WriteLine("cookie:" & WebBrowser1.Document.Cookie)

        If Environment.ExitCode = 9 Then
            Application.Exit()
        Else
            If WebBrowser1.Document.Url.ToString.Contains(pURL) Then
                '画面ごとに必要なhtml要素の有無を確認してから操作していく処理(省略)
            Else
                MyWStream.WriteLine("URLが不正です。(" & WebBrowser1.Document.Url.ToString & ")")
                AppCloseRtn(9)
                Exit Sub
            End If
        End If
End Sub


WebBrowser2クラス(2018/10/31追記)
(他の人が作りかけていたものを引き継いで手探りで調べながら作成しためコメントに誤りがあるかもしれません。)

Imports System.Runtime.InteropServices
Imports System.Security.Permissions

Public Class WebBrowser2
    Inherits WebBrowser

    Private cookie As AxHost.ConnectionPointCookie
    Private helper As WebBrowser2EventHelper

    'イベントデリゲートの宣言
    Public Event NavigateError As WebBrowserNavigateErrorEventHandler

    'デリゲートの宣言
    Public Delegate Sub WebBrowserNavigateErrorEventHandler(ByVal sender As Object, _
        ByVal e As WebBrowserNavigateErrorEventArgs)

    <PermissionSetAttribute(SecurityAction.LinkDemand, _
    Name:="FullTrust")> Protected Overrides Sub CreateSink()

        '派生元クラスのCreateSinkを呼び出す
        MyBase.CreateSink()

        'コントロールのイベント処理をActiveXに接続する
        helper = New WebBrowser2EventHelper(Me)
        cookie = New AxHost.ConnectionPointCookie( _
            Me.ActiveXInstance, helper, GetType(DWebBrowserEvents2))

    End Sub

    <PermissionSetAttribute(SecurityAction.LinkDemand, _
    Name:="FullTrust")> Protected Overrides Sub DetachSink()

        'ActiveXに接続されているコントロールのイベント処理を解放する
        If cookie IsNot Nothing Then
            cookie.Disconnect()
            cookie = Nothing
        End If

        '派生元クラスのDetachSinkを呼び出す
        MyBase.DetachSink()

    End Sub

    'ActiveXからのナビゲートエラーイベントによるコールバック
    Protected Overridable Sub OnNavigateError( _
        ByVal e As WebBrowserNavigateErrorEventArgs)

        RaiseEvent NavigateError(Me, e)

    End Sub

    'NavigateErrorイベント情報クラス
    Private Class WebBrowser2EventHelper
        Inherits StandardOleMarshalObject
        Implements DWebBrowserEvents2

        Private parent As WebBrowser2

        Public Sub New(ByVal parent As WebBrowser2)
            Me.parent = parent
        End Sub

        Public Sub NavigateError(ByVal pDisp As Object, _
            ByRef URL As Object, ByRef frame As Object, _
            ByRef statusCode As Object, ByRef cancel As Boolean) _
            Implements DWebBrowserEvents2.NavigateError

            ' Raise the NavigateError event.
            Me.parent.OnNavigateError( _
                New WebBrowserNavigateErrorEventArgs( _
                CStr(URL), CStr(frame), CInt(statusCode), cancel))

        End Sub

    End Class

End Class

'NavigateErrorイベントのプロパティセット
Public Class WebBrowserNavigateErrorEventArgs
    Inherits EventArgs

    Private urlValue As String
    Private frameValue As String
    Private statusCodeValue As Int32
    Private cancelValue As Boolean

    Public Sub New( _
        ByVal url As String, ByVal frame As String, _
        ByVal statusCode As Int32, ByVal cancel As Boolean)

        Me.urlValue = url
        Me.frameValue = frame
        Me.statusCodeValue = statusCode
        Me.cancelValue = cancel

    End Sub

    Public Property Url() As String
        Get
            Return urlValue
        End Get
        Set(ByVal value As String)
            urlValue = value
        End Set
    End Property

    Public Property Frame() As String
        Get
            Return frameValue
        End Get
        Set(ByVal value As String)
            frameValue = value
        End Set
    End Property

    Public Property StatusCode() As Int32
        Get
            Return statusCodeValue
        End Get
        Set(ByVal value As Int32)
            statusCodeValue = value
        End Set
    End Property

    Public Property Cancel() As Boolean
        Get
            Return cancelValue
        End Get
        Set(ByVal value As Boolean)
            cancelValue = value
        End Set
    End Property

End Class

' NavigateErrorのインターフェース定義 
<ComImport(), Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch), _
TypeLibType(TypeLibTypeFlags.FHidden)> _
Public Interface DWebBrowserEvents2

    <DispId(271)> Sub NavigateError( _
            <InAttribute(), MarshalAs(UnmanagedType.IDispatch)> _
            ByVal pDisp As Object, _
            <InAttribute()> ByRef URL As Object, _
            <InAttribute()> ByRef frame As Object, _
            <InAttribute()> ByRef statusCode As Object, _
            <InAttribute(), OutAttribute()> ByRef cancel As Boolean)

End Interface

 試したこと

エラー画面でクッキーが無効という旨が記載されていたため、
B.exeを実行する状況の条件を変えながらクッキーの確認を行いました。
※クッキー=WebBrowser.Document.Cookieの想定です。

■本番に一番近い状況を確認
・FiddlerからIISで実行しているWebAPIに必要データをPOSTし、一連の処理を実行。
→エラー画面に遷移。クッキーは空でした。

・WebAPIのIIS設定「IPアドレスおよびドメインの制限」で
B.exeで操作したいサイトのIPアドレスを許可エントリに追加して
FiddlerからIISで実行しているWebAPIに必要データをPOSTし、一連の処理を実行。
→エラー画面に遷移。クッキーは空でした。

■本番サーバ上で確認
・A.exeをコマンドプロンプトでを呼び出して大まかな処理の流れの2~3を実行。
→ログイン画面に遷移。クッキーの取得はできました。

・ASP.NETのWebサイトからA.exeを呼び出して大まかな処理の流れの2~3を実行。
→エラー画面に遷移。クッキーは空でした。

■開発環境で確認
・FiddlerからVisualStudioで実行しているWebAPIに必要データをPOSTし、
一連の処理を実行。
→ログイン画面に遷移。クッキーの取得はできました。
・ASP.NETのWebサイトからA.exeを呼び出して大まかな処理の流れの2~3を実行。
→ログイン画面に遷移。クッキーの取得はできました。

なお、クッキーにはIDなど毎回テストのたびに異なっている項目があるのですが、
B.exe側から強引にクッキーをセットしてログイン画面に入る、というような方法は
不可能という認識でいいでしょうか。

 補足情報(FW/ツールのバージョンなど)

【言語・環境等】
・VB
・ASP.NET v4.0
・Visual Studio 2013
・本番サーバ:Windows Server 2012 R2(IIS Version 8.5.9600.16384)
・開発環境:Windows 7 Professional(IIS Version 7.5.7600.16385)

 他に試したこと2

・アプリケーションプールの詳細設定でアプリケーションプールのIDを
DefaultAppPoolから管理者権限のあるユーザーに変更。(2018/11/09追記)
期待した動作は得られるようになりましたが下記の理由で没に。現在は設定していません。
・アプリケーションプールの権限が強すぎてセキュリティ面に不安がある。
・確認した日によって動作が変わるが、理由が不明。
【サーバーメンテナンス前】
・Administartorsグループのユーザーだとクッキー無効時のエラー画面に遷移。
・Administratorがログイン状態でないとクッキー無効時のエラー画面に遷移。
【サーバーメンテナンス後】
・Administratorsグループのユーザーでも正常動作。
・ログイン状態でなくても正常動作。

・WebBrowserControl.exeのバージョンをIE11に設定。(2018/11/09追記)
→クッキー無効時のエラー画面に遷移。

・HTTPClientでの実装(2018/11/09追記)
クッキーの一部が取得できませんでした。ブラウザでサイトを開いたときの動作をFiddlerで確認したところ、ログインページのソースで指定されている外部スクリプトをGET後、その内部の処理で作成したURLをGETしてCookieの設定を追加しているようでした。作成されるURLは毎回変わります。(用語・表現が正確ではないかもしれません。)JavaScriptはブラウザで実行するもの、という認識のためHTTPClientのみではログイン不可能なサイトだと考えています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • SurferOnWww

    2018/10/30 23:15

    > 本番環境で他のASP.NETアプリケーションから呼び出すとエラーになります。← そもそもできないことをしているのだと思いますけど。その部分だけでいいのでコードをアップできませんか?

    キャンセル

回答 2

checkベストアンサー

+4

詳しい状況が分からないし検証もできませんので多分に想像が混ざってますが・・・

単体では正常動作を確認できている実行ファイルが、本番環境で他のASP.NETアプリケーションから呼び出すとエラーになります。

IIS のように、UI を表示しないことが前提の Windows のプロセスやサービスはユーザー対話モードで実行されていません。(デフォルトから設定を変えていれば話は別ですが。Environment.UserInteractive プロパティで分かります)

また、ASP.NET は IIS のワーカープロセスで動きますが、ワーカープロセスはデフォルトで「アプリケーションプール ID」になり、権限は低いものになります。詳しくは以下の記事を見てください。

アプリケーション プール ID
https://technet.microsoft.com/ja-jp/library/ee886292.aspx

ASP.NET Web アプリはユーザー対話モードで実行されていないので、MessageBox などの UI を使用すると例外がスローされます。

リソースにアクセスする場合、権限の問題で例外がスローされることもよくある問題です。

・・・ここまでは想像ではありません。常識的なことなので、すでにご承知かもしれませんが。

この先は想像ですが・・・

質問者さんのケースのように、ASP.NET Web アプリから Process.Start で別アプリを起動すると、別アプリはワーカープロセスのアカウントで動くはずです。

質問者さんの別アプリは Windows Forms + WebBrowser アプリということですが、それを動かすワーカープロセスはユーザー対話モードでは動いていないので、UI の表示はできません。また、権限も低いです。

上記が「本番環境で他のASP.NETアプリケーションから呼び出すとエラー」の原因かどうかは分かりませんが、そのあたりを確認してはいかがでしょう?

「本番環境で」ということは、開発環境では動いてのではないかと想像していますが、もしそうだとすると開発環境では Visual Studio を管理者権限で立ち上げて、IIS Express 上で Web アプリを実行しているのではないですか? その場合はワーカープロセスはユーザー対話モード&管理者権限となるので、動いたのではないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/05 11:58

    > プロセスとして立ち上げることはできないことを前提にしても、

    ワーカープロセスに管理者権限を与えないかぎり「プロセスとして立ち上げることはできない」はその通りですので、自分的には話はそこで終わってしまいました。

    なので、すみませんが、その先を考える気力がありません・・・が、最後に一つだけ。

    IIS Express で動かしたときは、先にも書きましたように、ワーカープロセスはユーザー対話モード&管理者権限となるので、動いたのではないかと思います。

    IIS で動かしたときは、例えワーカープロセスに管理者権限を与えてもユーザー対話モードではないという違いがあります(自分の環境の Windows 10, IIS10 の場合)。

    関係あるかどうかは分かりませんが、そのぐらいの違いしか思い当たりません。

    ページに Label を 2 つ追加して、以下のようなコードで確認してみてください。

    Label1.Text = "UserInteractive: " + Environment.UserInteractive;
    Label2.Text = "User: " + System.Security.Principal.WindowsIdentity.GetCurrent().Name;

    キャンセル

  • 2018/11/05 15:15

    > ワーカープロセスに管理者権限を与えないかぎり「プロセスとして立ち上げることはできない」はその通りですので、自分的には話はそこで終わってしまいました。
    > なので、すみませんが、その先を考える気力がありません・・・が、最後に一つだけ。
    返信ありがとうございます。
    何度もお手数をおかけして申し訳ありませんでした。

    アドバイスいただいたテストコードを
    B.exeに入れて実行してみたところ下記の結果となりました。

    【開発環境】
    ・WebAPIをVisualStudioのデバッグ(IIS Express)実行した場合
    UserInteractive: True
    User: PC名\ユーザー名
    →B.exeは正常動作

    ・WebAPIをIISで実行した場合
    UserInteractive: False
    User: IIS APPPOOL\DefaultAppPool
    →B.exeは正常動作

    【本番環境】 ※すべてWebAPIはIISで実行
    ・DefaultAppPoolのIDが「ApplicationPoolIdentity」の場合
    UserInteractive: False
    User: IIS APPPOOL\DefaultAppPool
    →Cookie無効エラー画面へ遷移

    ・DefaultAppPoolのIDが「Administrator」かつ
     Administratoがログイン済みの場合
    UserInteractive: False
    User: PC名\Administrator
    →B.exeは正常動作

    ・DefaultAppPoolのIDが「Administrator」かつ
     Administratoがログアウトしている場合
    UserInteractive: False
    User: PC名\Administrator
    →B.exeは正常動作

    ・DefaultAppPoolのIDがAdministratorsグループのユーザーかつ
     誰もログインしていない場合
    UserInteractive: False
    User: PC名\Administratorsグループのユーザー
    →B.exeは正常動作

    > IIS で動かしたときは、例えワーカープロセスに管理者権限を与えてもユーザー対話モードではないという違いがあります(自分の環境の Windows 10, IIS10 の場合)。
    SurferOnWwwさんのおっしゃっている通りの結果になりました。
    ただ、この状態で想定した正常時の動作ができているパターンがあるということは
    B.exeはユーザー対話モード無しで動かせるものなのかもしれないと思いました。
    また、昨日コメントした2のほうの疑問点が今日は再現しなかったため
    ワーカープロセスの管理者権限以外で何か問題点はなかったか確認してみます。

    キャンセル

  • 2018/11/06 09:16

    自分的には終った話と言いながら余計なお世話かもしれませんが、一言言わせていただけると・・・

    技術的な興味と言うことなら話は別ですが、Microsoft の公式文書で出来ないと言われていることを裏技を見つけるなどして抜け道を探して何とかしようとしているとすると、自分的には付いていけないです(正直時間の無駄と思ってしまいます)。

    Form, WebBrowser の exe, それを ASP.NET のコードビハインドで Process.Start を使って起動するというのは考え直して、ASP.NET のコードビハインドで直接使える HttpClient などを使って実装するように方針転換してはいかがでしょう?

    そんなことはとっくに検討済みで、それができないから今のやり方になっているということでしたら失礼しました。

    キャンセル

0

WebAPIから実行ファイルの呼び出し(Webbrowser操作)は
どうしても外せない仕様だったため下記の手順で対処しました。

  1. サーバーにUsersグループのアカウントを追加。
  2. 1で作成したアカウントをRemote Desktop Userグループに登録。
  3. 1で作成したアカウントにリモートでログインし、
    Internet Explorerを起動。
  4. 1で作成したアカウントをRemote Desktop Userグループから解除。
  5. WebAPIが使用するアプリケーションプールのIDを
    カスタムアカウントで1で作成したアカウントに設定。

以前試したとき、作りたてのUsersグループのアカウントでは
Cookie無効エラーとなってうまく動かなかったのですが、
調査中に2~4の手順を行った流れで
Cookieが設定されるようになっていました。
リモートでのログイン状態でなくても動作することが確認できています。

抜け道的なやり方かもしれませんし
セキュリティ面もまだ不安が残りますが、
一応この手順で動くようになった、という意味で
解決方法として記載したいと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.95%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る