mciSendStringで音が鳴らない???
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2020/10/21 20:56:31
> 今回、マルチスレッドで処理させようと思い、
> その部分から来た問題点だとは思っているのですが、
内容は精査していないのですが、下記の記事はどうでしょうか。
VB でも日本語でも無いですが、なんだか関係ありそうなことが記されているように見えます。
https://stackoverflow.com/questions/3597681/method-doesnt-work-when-executed-in-a-thread-but-works-otherwise-c-sharp
https://www.codeproject.com/Questions/183548/How-to-use-mciSendString-in-a-threaded-application
> その部分から来た問題点だとは思っているのですが、
内容は精査していないのですが、下記の記事はどうでしょうか。
VB でも日本語でも無いですが、なんだか関係ありそうなことが記されているように見えます。
https://stackoverflow.com/questions/3597681/method-doesnt-work-when-executed-in-a-thread-but-works-otherwise-c-sharp
https://www.codeproject.com/Questions/183548/How-to-use-mciSendString-in-a-threaded-application
投稿者 あせたけ  (社会人)
投稿日時
2020/10/21 21:26:42
早速のご回答ありがとうございます。
参考になりそうな記事と感じました。
見てみます。
参考になりそうな記事と感じました。
見てみます。
投稿者 るきお  (社会人)
投稿日時
2020/10/23 08:17:42
魔界の仮面弁士さんの情報で解決するかもしれませんが思ったことを書きます。
今回マルチスレッドで実行すると再生できないということなので、サンプルとしてはそのマルチスレッドで再生できないサンプルを投稿していただければ少なくともこちらで試すくらいのことはできます。
場合によっては、魔界の仮面弁士さんが紹介しているリンク先の解決方法をそれに適用してみることもやるかもしれません。
投稿いただいたプログラムが実行できないのもであったので、欠けている部分を補って、とにかく実行できるようにしようと少し作業し始めたのですが、肝心のマルチスレッド部分に関しては皆無でしたので実行を断念しました。
他の人がすぐ試せるとそれだけ解決につながる情報を得られる可能性が高くなります。そして、そのような簡単に試せる状態を作り出す過程で自分で解決方法を発見することもたくさんありますので、問題を再現できる最小限のプログラムを作るというようにアプローチされるのも良いなと思いました。
※技術的な中身としては私の第一印象はリンク先の意見とだいたい同じです。つまり、UIスレッドでしか実行できない処理なのではないかなと思いました。
今回マルチスレッドで実行すると再生できないということなので、サンプルとしてはそのマルチスレッドで再生できないサンプルを投稿していただければ少なくともこちらで試すくらいのことはできます。
場合によっては、魔界の仮面弁士さんが紹介しているリンク先の解決方法をそれに適用してみることもやるかもしれません。
投稿いただいたプログラムが実行できないのもであったので、欠けている部分を補って、とにかく実行できるようにしようと少し作業し始めたのですが、肝心のマルチスレッド部分に関しては皆無でしたので実行を断念しました。
他の人がすぐ試せるとそれだけ解決につながる情報を得られる可能性が高くなります。そして、そのような簡単に試せる状態を作り出す過程で自分で解決方法を発見することもたくさんありますので、問題を再現できる最小限のプログラムを作るというようにアプローチされるのも良いなと思いました。
※技術的な中身としては私の第一印象はリンク先の意見とだいたい同じです。つまり、UIスレッドでしか実行できない処理なのではないかなと思いました。
投稿者 あせたけ  (社会人)
投稿日時
2020/10/24 12:19:59
ご意見ありがとうございます。
現在こちらでチェックしている項目としては、
https://www.codeproject.com/Questions/183548/How-to-use-mciSendString-in-a-threaded-application%20%E7%AE%A1%E7%90%86
のmciTestクラスを作成、mciTestクラスのバグ修正、
mciReplacement(ByVal Command As String) に上の『音声処理鳴動』のcmd変数の結果を投げてみましたが、音が鳴りませんでした。
説明がなかなかややこしいのですが、とりあえず概要を…
Public Class Class1
Privete LCS as new List(Of Class2)
Public Event LCEvents(sender As Object, e As LCEventArgs)
Public Sub LCEvent(sender As Object, e As LCEventAugs)
_RaiseEvent LCEvents(sender, e)
End sub
End Class
Public Class2
Private objLCS As Class1
Private Polling_Thread As New System.Threading.Thread(AddressOf Polling_Exec)
Public Sub New(ByVal objLCS As Class1)
Me.ObjLCS = objLCS
Try
If Not Connection Then
Task.Run(Sub()
Open()
End Sub)
End If
Polling_Thread.Name = "Lan Converter " & LCIndex
Polling_Thread.Start()
Catch e As Exception
End Try
End Sub
Public Overridable Sub Open() Implements LCInterface.Open
Connection = sckOpen()
Send("W", 0)
End Sub
Private Function sckOpen() As Boolean Implements LCInterface.sckOpen
Try
If Me.Enabled Then
Me.objSck.Connect(Me.IP, Me.PORT)
Me.objStm = objSck.GetStream()
Return True
Else
Return False
End If
Catch ex As Exception
objLCS.LCEvent(Me, New LCEventArgs(Connect:=False, ErrNo:=LCEventArgs.ErrNoState.未接続, ErrDiscription:="接続できませんでした"))
Return False
End Try
End Function
Private Sub Polling_Exec() Implements LCInterface.Polling_Exec
Do
Send()
Recieve()
System.Threading.Thread.Sleep(Polling_Interval)
Loop
End Sub
Private Sub Send()
(状態を取得するコマンド送信)
End Sub
Private Sub Recieve()
(状態を受信する)
If (状態変化があった時) then
ObjLCS.LCEvent(Me,New LCEventAugs (InputData:= InputData))
End If
End Sub
End Class
概要としてはこの様な感じでしょうか…
現在こちらでチェックしている項目としては、
https://www.codeproject.com/Questions/183548/How-to-use-mciSendString-in-a-threaded-application%20%E7%AE%A1%E7%90%86
のmciTestクラスを作成、mciTestクラスのバグ修正、
mciReplacement(ByVal Command As String) に上の『音声処理鳴動』のcmd変数の結果を投げてみましたが、音が鳴りませんでした。
説明がなかなかややこしいのですが、とりあえず概要を…
Public Class Class1
Privete LCS as new List(Of Class2)
Public Event LCEvents(sender As Object, e As LCEventArgs)
Public Sub LCEvent(sender As Object, e As LCEventAugs)
_RaiseEvent LCEvents(sender, e)
End sub
End Class
Public Class2
Private objLCS As Class1
Private Polling_Thread As New System.Threading.Thread(AddressOf Polling_Exec)
Public Sub New(ByVal objLCS As Class1)
Me.ObjLCS = objLCS
Try
If Not Connection Then
Task.Run(Sub()
Open()
End Sub)
End If
Polling_Thread.Name = "Lan Converter " & LCIndex
Polling_Thread.Start()
Catch e As Exception
End Try
End Sub
Public Overridable Sub Open() Implements LCInterface.Open
Connection = sckOpen()
Send("W", 0)
End Sub
Private Function sckOpen() As Boolean Implements LCInterface.sckOpen
Try
If Me.Enabled Then
Me.objSck.Connect(Me.IP, Me.PORT)
Me.objStm = objSck.GetStream()
Return True
Else
Return False
End If
Catch ex As Exception
objLCS.LCEvent(Me, New LCEventArgs(Connect:=False, ErrNo:=LCEventArgs.ErrNoState.未接続, ErrDiscription:="接続できませんでした"))
Return False
End Try
End Function
Private Sub Polling_Exec() Implements LCInterface.Polling_Exec
Do
Send()
Recieve()
System.Threading.Thread.Sleep(Polling_Interval)
Loop
End Sub
Private Sub Send()
(状態を取得するコマンド送信)
End Sub
Private Sub Recieve()
(状態を受信する)
If (状態変化があった時) then
ObjLCS.LCEvent(Me,New LCEventAugs (InputData:= InputData))
End If
End Sub
End Class
概要としてはこの様な感じでしょうか…
投稿者 あせたけ  (社会人)
投稿日時
2020/10/24 12:22:39
概要で抜けていますが、
Class1で多数のClass2をリストにAddして、
Class1の方でClass2のイベントをまとめて投げています。
Class1で多数のClass2をリストにAddして、
Class1の方でClass2のイベントをまとめて投げています。
投稿者 あせたけ  (社会人)
投稿日時
2020/10/24 12:40:09
> UIスレッドでしか実行できない処理なのではないか
ちょっと調べてみました。
参考ソース
呼出元
--------------------------------------------------------------------
Public Class Main
Inhertits Sound
Public Sub New()
sound.sound(snd.Aleart) '...←これは音が鳴る・・・・・①
End Sub
(~中略~)
Private Sub OutputEventsCatch(ByVal sender As Object, ByVal e As LCEventArgs) Handles Snr.AleartEvent
sound.sound(snd.Aleart) '...←これが音が鳴らない…・・・・・②
End Sub
End Class
--------------------------------------------------------------------
①はメインスレッドで処理されています。
②は(概要で言う)クラス2のスレッドで処理されています。
②をメインスレッドで処理させるにはどうしたら良いのか?と言うのが鍵となりそうです…
ちょっと調べてみました。
参考ソース
呼出元
--------------------------------------------------------------------
Public Class Main
Inhertits Sound
Public Sub New()
sound.sound(snd.Aleart) '...←これは音が鳴る・・・・・①
End Sub
(~中略~)
Private Sub OutputEventsCatch(ByVal sender As Object, ByVal e As LCEventArgs) Handles Snr.AleartEvent
sound.sound(snd.Aleart) '...←これが音が鳴らない…・・・・・②
End Sub
End Class
--------------------------------------------------------------------
①はメインスレッドで処理されています。
②は(概要で言う)クラス2のスレッドで処理されています。
②をメインスレッドで処理させるにはどうしたら良いのか?と言うのが鍵となりそうです…
投稿者 あせたけ  (社会人)
投稿日時
2020/10/25 21:01:36
自己解決しました。
元々、スタートアップフォームであるForm1より、Mainクラスを呼出し、Mainクラスでメインの処理を担当、
前出、(概要のClass2)をマルチスレッド化して、イベントをMainクラスに投げ、Mainクラスで音を鳴らす予定でした。
(要点のみ)
※変更前
----------------------------------------------------------------------------------
Public Class Form1
private sub Form1_Load(ByVal sender As Object, ByVal e As eventAugs) Handles Mybase.Load
Dim m As New Main(Me)
End Sub
End Class
Public Class Main
Inherits Sound
Public Property f As Form1
Private WithEvents lcs As New LanConverter.LanConverters()
Private WithEvents Sensers As New SenserS()
Private Settings As New Settings()
Private Olc As New List(Of String)
Private Sub New(ByVal f as form)
Me.f = f
End Sub
Private Sub OutputEventsCatch(ByVal sender As Object, ByVal e As LanConverter.LCEventArgs) Handles Sensers.AleartEvent
Select Case e.InputStatus
Case SenserInterface.SenserInputStatus.通常
Sound(snd.None)
Case SenserInterface.SenserInputStatus.発報
Sound(snd.Warning)
(・・・省略・・・)
End Select
End Sub
End Class
----------------------------------------------------------------------------------
元々、スタートアップフォームであるForm1より、Mainクラスを呼出し、Mainクラスでメインの処理を担当、
前出、(概要のClass2)をマルチスレッド化して、イベントをMainクラスに投げ、Mainクラスで音を鳴らす予定でした。
(要点のみ)
※変更前
----------------------------------------------------------------------------------
Public Class Form1
private sub Form1_Load(ByVal sender As Object, ByVal e As eventAugs) Handles Mybase.Load
Dim m As New Main(Me)
End Sub
End Class
Public Class Main
Inherits Sound
Public Property f As Form1
Private WithEvents lcs As New LanConverter.LanConverters()
Private WithEvents Sensers As New SenserS()
Private Settings As New Settings()
Private Olc As New List(Of String)
Private Sub New(ByVal f as form)
Me.f = f
End Sub
Private Sub OutputEventsCatch(ByVal sender As Object, ByVal e As LanConverter.LCEventArgs) Handles Sensers.AleartEvent
Select Case e.InputStatus
Case SenserInterface.SenserInputStatus.通常
Sound(snd.None)
Case SenserInterface.SenserInputStatus.発報
Sound(snd.Warning)
(・・・省略・・・)
End Select
End Sub
End Class
----------------------------------------------------------------------------------
投稿者 あせたけ  (社会人)
投稿日時
2020/10/25 21:04:35
魔界の仮面弁士さん、るきおさんにヒントを頂き、
るきおさんの
> UIスレッドでしか実行できない処理なのではないか
が決めてとなり、Form1に1度処理を戻し、デリゲート処理する事で音が鳴りました。
※変更後
----------------------------------------------------------------------------------
Public Class Form1
private sub Form1_Load(ByVal sender As Object, ByVal e As eventAugs) Handles Mybase.Load
Dim m As New Main(Me)
End Sub
Public Sub soundPlay(ByVal snd As Sound.snd)
Sound.SoundCheck(Me, snd)
End Sub
End Class
End Class
Public Class Main
Public Property f As Form1
Private WithEvents lcs As New LanConverter.LanConverters()
Private WithEvents Sensers As New SenserS()
Private Settings As New Settings()
Private Olc As New List(Of String)
Private Sub New(ByVal f as form)
Me.f = f
End Sub
Private Sub OutputEventsCatch(ByVal sender As Object, ByVal e As LanConverter.LCEventArgs) Handles Sensers.AleartEvent
Select Case e.InputStatus
Case SenserInterface.SenserInputStatus.通常
f.soundPlay(Sound.snd.None)
Case SenserInterface.SenserInputStatus.発報
f.soundPlay(Sound.snd.Warning)
(・・・省略・・・)
End Select
End Sub
Public Class Sound
Delegate Sub SoundDelegate(ByVal f As Form1, ByVal s As snd)
Public Sub SoundCheck(ByVal f As Form1, ByVal s As snd)
If f.InvokeRequired Then
f.Invoke(New SoundDelegate(AddressOf SoundCheck), f, s)
Return
End If
Sound(s)
End Sub
(Soundメソッドは前出)
End Class
----------------------------------------------------------------------------------
何かあまり納得は行っていませんが、とりあえず音は鳴りましたので、ヨシとします…^^;
私感
大きなマルチスレッドプログラムは初めて組みますが、マルチスレッドプログラミングは難しい…^^;
メインスレッド? ワーカースレッド? フォアグラウンドスレッド? バックグラウンドスレッド?
もっと精進せねば…
ありがとうございました。
るきおさんの
> UIスレッドでしか実行できない処理なのではないか
が決めてとなり、Form1に1度処理を戻し、デリゲート処理する事で音が鳴りました。
※変更後
----------------------------------------------------------------------------------
Public Class Form1
private sub Form1_Load(ByVal sender As Object, ByVal e As eventAugs) Handles Mybase.Load
Dim m As New Main(Me)
End Sub
Public Sub soundPlay(ByVal snd As Sound.snd)
Sound.SoundCheck(Me, snd)
End Sub
End Class
End Class
Public Class Main
Public Property f As Form1
Private WithEvents lcs As New LanConverter.LanConverters()
Private WithEvents Sensers As New SenserS()
Private Settings As New Settings()
Private Olc As New List(Of String)
Private Sub New(ByVal f as form)
Me.f = f
End Sub
Private Sub OutputEventsCatch(ByVal sender As Object, ByVal e As LanConverter.LCEventArgs) Handles Sensers.AleartEvent
Select Case e.InputStatus
Case SenserInterface.SenserInputStatus.通常
f.soundPlay(Sound.snd.None)
Case SenserInterface.SenserInputStatus.発報
f.soundPlay(Sound.snd.Warning)
(・・・省略・・・)
End Select
End Sub
Public Class Sound
Delegate Sub SoundDelegate(ByVal f As Form1, ByVal s As snd)
Public Sub SoundCheck(ByVal f As Form1, ByVal s As snd)
If f.InvokeRequired Then
f.Invoke(New SoundDelegate(AddressOf SoundCheck), f, s)
Return
End If
Sound(s)
End Sub
(Soundメソッドは前出)
End Class
----------------------------------------------------------------------------------
何かあまり納得は行っていませんが、とりあえず音は鳴りましたので、ヨシとします…^^;
私感
大きなマルチスレッドプログラムは初めて組みますが、マルチスレッドプログラミングは難しい…^^;
メインスレッド? ワーカースレッド? フォアグラウンドスレッド? バックグラウンドスレッド?
もっと精進せねば…
ありがとうございました。
投稿者 るきお  (社会人)
投稿日時
2020/10/26 08:23:01
解決してよかったです。
マルチスレッドは難しいです。私は避けられるなら避けます。
例外処理やスレッド間の連携や、今回のようなスレッドアフィニティ(≒特定のスレッドで実施させたい)の問題。
こういった問題を解決するためにSyncronizationContextやExecutionContextなどの仕組みもあります。
System.Threading.Threadはマルチスレッドの機能としては古く、2008年ごろだったかにはTPL(System.Threading.Tasks,Task)が登場しています。その後、さらに簡単で非同期実行を実現する高機能な言語機能としてAyncとAwaitが登場しました。
どうしてもマルチスレッッド/非同期処理が必要な場合は、新しい機能から検討されると良いと思います。
私の知っている限りでは言語機能としてここまで強力なマルチスレッド機能があるのはVBとC#くらいではないかと思います。
下記の本は.NETのマルチスレッドについて体系的に説明してくれていてお勧めです。
プログラミング C#
https://www.amazon.co.jp/exec/obidos/ASIN/4873116503/vbschool-22
C#の本ですが、.NET FrameworkについてはVBもC#も同じですし、言語機能である Async, AwaitにつてもVBとC#で同じで参考になります。
この本はマルチスレッドだけの本ではありませんが、第17章 マルチスレッドが49ページ、第18章 非同期言語処理が20ページあり、かなり読み応えがあります。
マルチスレッドは難しいです。私は避けられるなら避けます。
例外処理やスレッド間の連携や、今回のようなスレッドアフィニティ(≒特定のスレッドで実施させたい)の問題。
こういった問題を解決するためにSyncronizationContextやExecutionContextなどの仕組みもあります。
System.Threading.Threadはマルチスレッドの機能としては古く、2008年ごろだったかにはTPL(System.Threading.Tasks,Task)が登場しています。その後、さらに簡単で非同期実行を実現する高機能な言語機能としてAyncとAwaitが登場しました。
どうしてもマルチスレッッド/非同期処理が必要な場合は、新しい機能から検討されると良いと思います。
私の知っている限りでは言語機能としてここまで強力なマルチスレッド機能があるのはVBとC#くらいではないかと思います。
下記の本は.NETのマルチスレッドについて体系的に説明してくれていてお勧めです。
プログラミング C#
https://www.amazon.co.jp/exec/obidos/ASIN/4873116503/vbschool-22
C#の本ですが、.NET FrameworkについてはVBもC#も同じですし、言語機能である Async, AwaitにつてもVBとC#で同じで参考になります。
この本はマルチスレッドだけの本ではありませんが、第17章 マルチスレッドが49ページ、第18章 非同期言語処理が20ページあり、かなり読み応えがあります。
VSコミュニティ2019、Windows10の環境です。
VB.NETにてソフト開発を行っていますが、
元々音が鳴っているクラスをそのまま使用しようと思い、
実績のあるクラスを流用しています。
今回、マルチスレッドで処理させようと思い、
その部分から来た問題点だとは思っているのですが、どう解決して良いのかわかりません^^;
★元々音がなっていた作成済のアプリケーション
・ソフト全体がシングルスレッド
★今回音が鳴らない作成中のアプリケーション
・イベント処理からのマルチスレッド処理
参考ソース
呼出元
--------------------------------------------------------------------
Public Class Main
Inhertits Sound
Public Sub New()
sound.sound(snd.Aleart) '...←これは音が鳴る
End Sub
(~中略~)
Private Sub OutputEventsCatch(ByVal sender As Object, ByVal e As LCEventArgs) Handles Snr.AleartEvent
sound.sound(snd.Aleart) '...←これが音が鳴らない…
End Sub
End Class
--------------------------------------------------------------------
呼出先(ベースクラス:Sound)
--------------------------------------------------------------------
Public Class Sound
Inherits File
Private Const className As String = "Sound"
<System.Runtime.InteropServices.DllImport("winmm.dll", CharSet:=System.Runtime.InteropServices.CharSet.Auto)>
Private Shared Function mciSendString(ByVal command As String, ByVal buffer As System.Text.StringBuilder, ByVal bufferSize As Integer, ByVal hwndCallback As IntPtr) As Integer
End Function
Public Declare Function mciGetErrorString Lib "winmm.dll" Alias "mciGetErrorStringA" (ByVal fdwError As Integer, ByVal lpszErrorText As String, ByVal cchErrorText As Integer) As Integer
''' <summary>
''' 音声ファイル操作用
''' </summary>
Private sound_Alert_Name As String = "Alert"
Private sound_Warning_Name As String = "Warning"
(~中略~)
''' <summary>
''' 音声鳴動処理
''' </summary>
''' <param name="s">音声の種類 [Enum]snd</param>
Public Sub Sound(ByVal s As snd)
Dim FileName As String = ""
Dim aliasName As String = ""
Dim Result As Integer
If s = snd.None Then
AleartSoundOff()
ElseIf s = snd.Aleart Then
FileName = sound_Alert
aliasName = sound_Alert_Name
ElseIf s = snd.Warning Then
FileName = sound_Warning
aliasName = sound_Warning_Name
End If
Dim cmd As String
'ファイルを開く
cmd = "open """ + FileName + """ type mpegvideo alias " + aliasName
Result = mciSendString(cmd, Nothing, 0, IntPtr.Zero)
If Result <> 0 Then
'エラーメッセージを取得して表示します。
Dim ErrorMessage As String = GetErrorString(Result)
MsgBox(ErrorMessage, MsgBoxStyle.Exclamation, "Error1 " & Result)
End If
'再生する
cmd = "play " + aliasName + " repeat"
Result = mciSendString(cmd, Nothing, 0, IntPtr.Zero)
If Result <> 0 Then
'エラーメッセージを取得して表示します。
Dim ErrorMessage As String = GetErrorString(Result)
MsgBox(ErrorMessage, MsgBoxStyle.Exclamation, "Error2 " & Result)
End If
End Sub
Private Sub AleartSoundOff()
Dim cmd As String
'停止する
cmd = "stop all"
mciSendString(cmd, Nothing, 0, IntPtr.Zero)
'閉じる
cmd = "close all"
mciSendString(cmd, Nothing, 0, IntPtr.Zero)
End Sub
End Class
--------------------------------------------------------------------
解決方法のヒントだけでも頂ければと思い、投稿させて頂きます。
※ 実現させたい事
外部の(多数の)接点入力装置の情報をLCEventAugsにてイベントで入手している。
(ここまでは正常に情報入手出来ている)
上記の入手イベントで異常があった際(入力があった際)に音を鳴らしたい。
(ここで音が鳴らない。上記参照)
※ mciSendString関数のエラーメッセージを確認する
https://www.umayadia.com/vbsample/dotnet-Samples151/Sample188GetErrorString.htm?pos=5900
による情報
ErrNo.266
指定されたデバイス ドライバーの読み込み中に不明な問題が発生しました。
ErrNo.263
指定されたデバイスが開かれていないか、または MCI で認識されません。