C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[5]: イベントを発生させることができない


(過去ログ 163 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■94376 / inTopicNo.1)  イベントを発生させることができない
  
□投稿者/ kumakuma (7回)-(2020/04/07(Tue) 10:09:46)

分類:[VB.NET/VB2005 以降] 

現在vb.net Framework 4.5.2にてVBAで使えるクラスライブラリを作成しています。
そこでShell.Execの拡張機能を作成しようとしたところ非同期で「イベント」を発生させることができず
困っております。
原因がわからない為様々な方法を試したのですがやはり「イベント」を発生させることができませんでした。
まことに申し訳ないのですが、よろしくお願いします。

引用返信 編集キー/
■94377 / inTopicNo.2)  Re[1]: イベントを発生させることができない
□投稿者/ kumakuma (8回)-(2020/04/07(Tue) 10:11:10)
現在vb.net Framework 4.5.2にてVBAで使えるクラスライブラリを作成しています。
そこでShell.Execの拡張機能を作成しようとしたところ非同期で「イベント」を発生させることができず
困っております。
原因がわからない為様々な方法を試したのですがやはり「イベント」を発生させることができませんでした。
まことに申し訳ないのですが、よろしくお願いします。

Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Text

<Guid(Shell.EventsId)>
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
<ComVisible(True)>
Public Interface IShellEvents
    <Description("標準出力にデータが記載された時のイベント。")>
    <DispId(1)> Sub OutputDataReceived(ByVal data As Object)
End Interface

<Guid(Shell.InterfaceId)>
<InterfaceType(ComInterfaceType.InterfaceIsDual)>
<ComVisible(True)>
Public Interface IShell
    <Description("子コマンドシェルでアプリケーションを実行します。")>
    Function Exec() As Integer
End Interface

<Guid(Shell.ClassId)>
<ComVisible(True)>
<ClassInterface(ClassInterfaceType.None)>
<ComSourceInterfaces(GetType(IShellEvents))>
<ComDefaultInterface(GetType(IShell))>
Public Class Shell : Implements IShell

    Public Const ClassId As String = "11A6BA24-E5C6-47FB-AF64-4FD635D14B95"
    Public Const InterfaceId As String = "88B16B14-0BA8-45A3-8F4A-BA56DB7C1CCE"
    Public Const EventsId As String = "A4FC31AA-D01D-449E-A6E6-AFC1BED947E2"

    Friend _proc As System.Diagnostics.Process	'実行プロセス

    ''' <summary>
    ''' 非同期読込情報
    ''' </summary>
    Friend Class AsyncReadObject
        Friend _proc As System.Diagnostics.Process
        Friend _esd As EventSeparaterDelegate
        Friend _rld As ReadLineDelegate
        Friend Property data As String
        Public Sub New(ByVal proc As System.Diagnostics.Process, ByVal esd As EventSeparaterDelegate, ByVal rld As ReadLineDelegate)
            Me._proc = proc '実行プロセス
            Me._esd = esd   'イベント振り分けデリケード
            Me._rld = rld   '出力読み取りデリケード
            Me.data = ""    '読込データ
        End Sub
    End Class

    ''' <summary>
    ''' Exec
    '''     子コマンドシェルでアプリケーションを実行します。
    ''' </summary>
    Public Function Exec() As Integer Implements IShell.Exec

        _proc = New System.Diagnostics.Process()
        Dim psi As New System.Diagnostics.ProcessStartInfo()

        '----- ファイルパスと引数を設定
        psi.FileName = "C:\WINDOWS\system32\cmd.exe"
        psi.Arguments = " /c dir c:\ /w"
        psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden
        psi.CreateNoWindow = True
        psi.RedirectStandardInput = True
        psi.RedirectStandardOutput = True
        psi.RedirectStandardError = True
        psi.UseShellExecute = False
        _proc.StartInfo = psi

        '----- 実行
        Call _proc.Start()

        '----- 非同期での出力とエラーの読み取り設定
        Dim esd As EventSeparaterDelegate = New EventSeparaterDelegate(AddressOf EventSeparater)   'イベント振り分け(イベント発生用)
        esd.Invoke("イベントテスト")                                                               '※正常にイベントが発生される
        Dim orld As ReadLineDelegate = New ReadLineDelegate(AddressOf ReadLine)                    '出力読み取り
        Dim oaro As AsyncReadObject = New AsyncReadObject(_proc, esd, orld)                        '非同期読取情報
        orld.BeginInvoke(oaro, New AsyncCallback(AddressOf ReadEnd), oaro)                         '出力読み取りを非同期で実行

        Return _proc.Id

    End Function
    ''' <summary>
    ''' ReadLine
    '''     出力読み取り
    ''' </summary>
    ''' <param name="aro">非同期読込情報</param>
    Friend Delegate Sub ReadLineDelegate(ByVal aro As AsyncReadObject)
    Friend Sub ReadLine(ByVal aro As AsyncReadObject)
        aro.data = aro._proc.StandardOutput.ReadLine()
    End Sub
    ''' <summary>
    ''' ReadEnd
    '''     出力読み取り完了
    ''' </summary>
    ''' <param name="iar">非同期ステータス</param>
    Friend Sub ReadEnd(ByVal iar As IAsyncResult)
        Dim aro As AsyncReadObject = CType(iar.AsyncState, AsyncReadObject)
        aro._rld.EndInvoke(iar)     '出力読み取りの終了
        aro._esd.Invoke(aro.data)   'イベント振り分け実行
        If Not aro._proc.StandardOutput.EndOfStream Then
            aro._rld.BeginInvoke(aro, New AsyncCallback(AddressOf ReadEnd), aro)    '出力読み取り再実行
        End If
    End Sub
    ''' <summary>
    ''' EventSeparater
    '''     イベント振り分け
    ''' </summary>
    ''' <param name="data">データ</param>
    Friend Delegate Sub EventSeparaterDelegate(ByVal data As Object)
    Friend Sub EventSeparater(ByVal data As Object)
        RaiseEvent OutputDataReceived(data) '※イベントが取得できない
    End Sub
    ''' <summary>
    ''' OutputDataReceived
    '''     標準出力にデータが記載された時
    ''' </summary>
    ''' <param name="data">データ</param>
    Friend Delegate Sub outputDataReceivedDelegate(ByVal data As Object)
    Friend Event OutputDataReceived As outputDataReceivedDelegate

End Class

引用返信 編集キー/
■94378 / inTopicNo.3)  Re[2]: イベントを発生させることができない
□投稿者/ kumakuma (9回)-(2020/04/07(Tue) 10:16:32)
'----- VBA側Class(cShell)
Option Compare Database

Public WithEvents Shell As ShellLibrary.Shell

Private Sub Class_Initialize()
    Set Shell = New ShellLibrary.Shell
End Sub

Private Sub Class_Terminate()
    Set Shell = Nothing
End Sub

Private Sub Shell_OutputDataReceived(ByVal data As Variant)
    Debug.Print "OutputDataReceived:" & data
End Sub

'----- VBA側Module
Function test1()
    Dim clsShell As cShell
    Set clsShell = New cShell

    Call clsShell.Shell.Exec()
End Function

引用返信 編集キー/
■94380 / inTopicNo.4)  Re[3]: イベントを発生させることができない
□投稿者/ Hongliang (986回)-(2020/04/07(Tue) 14:02:26)
原因自体は割と簡単な話で、VBAの実行が終了したため、NewしたShellLibrary.Shellオブジェクトが解放され、OutputDataReceivedの割り当ても解かれたためですね。

簡単には、ShellLibrary.Shellに(もちろんIShellにも)完了プロパティを用意して、完了するまで待つ(VBAの実行を終わらせない)ようにすればいいでしょう。
待ってる間Application.DoEventsさせておけば普通のOffce操作も支障はないですし。

純粋に非同期でやるならVSTOでOfficeアドインとして作ることになるんでしょうかね?
やったことないので何とも言えませんが。
引用返信 編集キー/
■94381 / inTopicNo.5)  Re[4]: イベントを発生させることができない
□投稿者/ kumakuma (10回)-(2020/04/07(Tue) 15:09:35)
No94380 (Hongliang さん) に返信
> 原因自体は割と簡単な話で、VBAの実行が終了したため、NewしたShellLibrary.Shellオブジェクトが解放され、OutputDataReceivedの割り当ても解かれたためですね。
> 
> 簡単には、ShellLibrary.Shellに(もちろんIShellにも)完了プロパティを用意して、完了するまで待つ(VBAの実行を終わらせない)ようにすればいいでしょう。
> 待ってる間Application.DoEventsさせておけば普通のOffce操作も支障はないですし。
> 
> 純粋に非同期でやるならVSTOでOfficeアドインとして作ることになるんでしょうかね?
> やったことないので何とも言えませんが。

ご指摘いただいた内容を確認するため取り急ぎ以下の処理を追加して
イベントが発生している事が確認できました。

'----- VBA側Module
Option Compare Database
#Const IsWin64 = VBA7 And Win64
#If IsWin64 Then
    Public Declare PtrSafe Sub Sleep Lib "KERNEL32" (ByVal dwMilliseconds As Long)
#Else
    Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If

Function test1()
    Dim clsShell As cShell
    Set clsShell = New cShell

    Call clsShell.Shell.Exec()

    For intIndex = 1 To 100
        Call Sleep(100)
        DoEvents
    Next intIndex

End Function

Hongliang様ありがとうございます。

解決済み
引用返信 編集キー/
■94382 / inTopicNo.6)  Re[5]: イベントを発生させることができない
□投稿者/ kumakuma (11回)-(2020/04/07(Tue) 15:34:54)
2020/04/07(Tue) 19:44:55 編集(投稿者)

※追記
フォームの場合以下の状態で正常に作動しました
(clsShellが解放されない為)

Option Compare Database

Private clsShell As cShell

Private Sub Form_Load()
Set clsShell = New cShell
End Sub

Private Sub Form_Unload(Cancel As Integer)
Set clsShell = Nothing
End Sub

Private Sub コマンド0_Click()
Call clsShell.Shell.Exec()
End Sub




解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -