如何在线程应用程序中使用mciSendString [英] How to use mciSendString in a threaded application

查看:104
本文介绍了如何在线程应用程序中使用mciSendString的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嘿大家,

我有一个看似简单的问题,但我无法理解它。

我有将其缩小到这个:

当我使用 mciSendString 打开,播放和暂停wav文件时,播放/暂停命令仅在从同一个线程发送的''open''命令被调用。



所以这样做:

 mciSendString(openC:\ file.wavtype waveaudio alias audiofile,Nothing,0,IntPtr.Zero)
mciSendString(play audiofile from 0,Nothing,0,IntPtr.Zero)
thread.sleep(300)
mciSendString(pause audiofile,Nothing,0,IntPtr.Zero)
thread.sleep(300)
mciSendString(resume audiofile, Nothing,0,IntPtr.Zero)
thread.sleep(300)
mciSendString(close audiofile,Nothing,0,IntPtr.Zero)



但这不是:

 dim t1 as thread,t2 as thread 
t1 = new Thread(addressof openFile)
t1.start()
t2 = new Thread(addressof playFile)
t2.start()
''等

private sub openFile()
mciSendString(open& fileName&type waveaudio alias audiofile,Nothing,0,IntPtr.Zero)
end sub
private sub playFile()
mciSendString(play audiofile from 0,Nothing,0,IntPtr.Zero)
end sub
''etc。





这当然不是实际的代码,但你明白了。

当我用<调试它时code> console.writeline(Thread.CurrentThread.ManagedThreadId),我可以看到在这些情况下线程具有相同的ThreadId,命令成功(mciSendString的返回值为零),以及在那些情况下,mciSendString命令被具有不同ThreadIds的线程调用,它们失败(返回值为263 - INVALID_DEVICE_NAME)。

我用Google搜索了我的大脑,但我找不到如何创建的示例一个具有常量ThreadId的线程,或者如何在某个线程中简单地调用一个方法。

我可能被我读过的全部代码和tr所蒙蔽呐喊,但现在似乎我无法得到答案。

如果有人能指出我正确的方向,那将是超级酷。

解决方案

好的,我为那些正在寻找类似问题解决方案的人做了一些工作。



我的工作包括创建专用线程,等待发出 mciSendString 命令。基本上,它遵循生产者/消费者模式,其中生产者和消费者通过专用对象的锁定进行通信。





  public   class  mciTest 
private mciConsumerThread as 主题

public sub new ()
' 消费者方面以自己的线程开始
' 创建一个调用mciSendString方法的地方
mciConsumerThread = 线程( AddressOf threadedMCIconsumer)
mciConsumerThread.IsBackground = True
mciConsumerThread.Start()
end sub

私有 Sub threadedMCIconsumer()
执行
SyncLock MonitorLock
' 等待MonitorLock对象的重新发送;
' 当此语句退出时,新的mciCommand
' 已经发布
Monitor.Wait( MonitorLock)
' 该对象已被释放,因此请进行mci调用
' 来自此主题并放入返回值
' syncMciResult字段
如果 syncMciCommand<> 然后
syncMciResult = MciSendString(syncMciCommand, Nothing 0 IntPtr .Zero)
结束 如果
' 告诉另一个线程已获得结果。
Monitor.Pulse(MonitorLock)' [这将退出Monitor.wait(MonitorLock)
'另一个线程上的命令。]
' 重新开始循环,等待下一个命令。
结束 SyncLock
循环
' 由于这是一个后台线程,循环退出
'
结束 Sub


' 生成器是被调用的命令,而不是直接发出
' mciSendString:

公共 功能 mciReplacement( ByVal 命令作为 字符串正如 整数

Dim nonLockedReturn as 整数 ' 这将是返回值

SyncLock MonitorLock
' MonitorObject已暂时被
' threadedMCIconsumer线程释放。
' 抓住syncMciCommand变量并记住mciCommand
syncMciCommand =命令

' 释放MonitorObject,所以另一个线程
' 可以处理mc iSendString
Monitor.Pulse(MonitorLock)' [这将退出Monitor.wait(MonitorLock)命令
' 在另一个主题上。]

' 现在等到mci-thread表示结果到达。
Monitor .Wait(MonitorLock)
' 结果已写入syncMciResult
nonLockedReturn = syncMciResult
结束 SyncLock

' 使用nonLockedReturn安全地从SyncLock中获取结果
返回 nonLockedReturn
结束 功能

' 需要定义同步变量
私有 MonitorLock as new 对象()
私有 mciReturnValue 作为 整数
私有 syncMciCommand as 字符串

结束



对媒体进行线程安全调用控制接口(mci),使用上面的类如下:

  Dim  cMci  as   new  mciTest()
cMci.mciReplacement( openC:\ test.wav type waveaudio alias audiofile
cMci.mciReplacement( play audiofile
cMci.mciReplacement( pause audiofile
cMci.mciReplacement ( resume audiofile


< blockquote>许多年后..但是我正在研究这个问题...它基本上是因为MCI API需要一个消息循环在应用程序中处于活动状态(所以......它必须是Windows Forms才能让生活更轻松)



http://msdn.microsoft。 com / en-us / magazine / cc163417.aspx [ ^ ]



这篇文章给了我一个想法...所以我决定在mciCall上实现一个Invoke和InvokeRequeried,以确保调用将在Main Application Thread中进行。当然我需要发送所有类的Form引用来使用它,但这不是什么大问题,至少在我的项目中它不是。



我希望如果有人遇到同样的问题可以解决这个问题:D



我今天知道NUGET包许多扩展和库做的工作..但我试试让我的项目在我的控制之下,尽可能使用较少的第三方库..所以我很确定很多人会发现它很有用。



谢谢



这是最终代码



 使用系统; 
使用 System.Threading;
使用 System.Text;
使用 System.Runtime.InteropServices;
使用 System.Windows.Forms;

public class mciSafeCall
{

[DllImport( winmm.dll)]
private static extern int mciSendString( string strCommand,StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);


private int syncMciResult;
private string syncMciCommand;
private Form formulario;

public mciSafeCall(表格)
{
formulario = form;
syncMciCommand = ;
}

public void mciReplacement( string 命令)
{
syncMciCommand = Command;
if (!string.IsNullOrEmpty(syncMciCommand))
{
if (formulario.InvokeRequired)
formulario.Invoke( new Action(()= >
{
syncMciResult = mciSendString(syncMciCommand, null 0 IntPtr .Zero);
}));
else
syncMciResult = mciSendString(syncMciCommand, null 0 IntPtr .Zero);

}
}



}


来自阅读这篇CP文章似乎问题与竞争同一对象的2个线程有关。阅读这篇文章制作多线程VB.NET应用程序简介 [ ^ ]


Hey everybody,
I''ve got what seems to be a simple problem, but I just can''t get my head around it.
I have narrowed it down to this:
When I use mciSendString to open, play and pause a wav file, the play/pause commands only work when being sent from the very same thread the ''open'' command was called.

So this works:

mciSendString("open ""C:\file.wav"" type waveaudio alias audiofile", Nothing, 0, IntPtr.Zero)
mciSendString("play audiofile from 0", Nothing, 0, IntPtr.Zero)
thread.sleep(300)
mciSendString("pause audiofile", Nothing, 0, IntPtr.Zero)
thread.sleep(300)
mciSendString("resume audiofile", Nothing, 0, IntPtr.Zero)
thread.sleep(300)
mciSendString("close audiofile", Nothing, 0, IntPtr.Zero)


But this does not:

dim t1 as thread, t2 as thread
t1 = new Thread(addressof openFile)
t1.start()
t2 = new Thread(addressof playFile)
t2.start()
''etc.
 
private sub openFile()
mciSendString("open """ & fileName & """ type waveaudio alias audiofile", Nothing, 0, IntPtr.Zero)
end sub
private sub playFile()
mciSendString("play audiofile from 0", Nothing, 0, IntPtr.Zero)
end sub
''etc.



This is, of course, not the actual code, but you get the idea.
When I debug it with console.writeline(Thread.CurrentThread.ManagedThreadId), I can see that in those cases the threads have the same ThreadId, the commands succeed (return value of mciSendString is zero), and in those cases the mciSendString commands get called by threads with different ThreadIds, they fail (Return value is 263 -- INVALID_DEVICE_NAME).
I googled my brain out, but I cannot find an example of how to create a thread that has a constant ThreadId, or how to simply invoke a method in a certain thread.
I might be blinded by the whole lotta code I read and tried out, but right now it seems I can''t get to the answer.
If somebody could point me to the right direction, that would be super cool.

解决方案

Okay, I worked something out, for those who are looking for a solution to similar problems.

Mine consists of creating a dedicated thread, waiting to issue an mciSendString command. Basically, it follows the producer/consumer pattern, where the producer and consumer commnuicate over locks of a dedicated object.


public class mciTest
  private mciConsumerThread as Thread

  public sub new()
    ' The consumer side is started in its own thread
    ' create one from where the mciSendString method gets called
    mciConsumerThread = New Thread(AddressOf threadedMCIconsumer)
    mciConsumerThread.IsBackground = True
    mciConsumerThread.Start()
  end sub

  Private Sub threadedMCIconsumer()
    Do
        SyncLock MonitorLock
            ' wait for relase of MonitorLock object;
            ' when this statement exits, a new mciCommand
            ' has been issued
            Monitor.Wait(MonitorLock)
            ' the object has been released, so make the mci call
            ' from this thread and put the return value into the
            ' syncMciResult field
            If syncMciCommand <> "" Then
                syncMciResult = MciSendString(syncMciCommand, Nothing, 0, IntPtr.Zero)
            End If
            ' tell the other thread that a result has been obtained.
            Monitor.Pulse(MonitorLock) ' [This exits the Monitor.wait(MonitorLock) 
                                       ' command on the other thread.]
            ' start loop all over again, waiting for the next command.
        End SyncLock
    Loop
    ' As this is a background-thread, the loop exits
    ' when the thread is aborted from outside.
  End Sub


  ' The producer is the command being called instead of issuing 
  ' mciSendString directly:

  Public Function mciReplacement(ByVal Command As String) As Integer

    Dim nonLockedReturn as Integer ' this will be the return value

    SyncLock MonitorLock
        ' The MonitorObject has been freed temporarily by the 
        ' threadedMCIconsumer thread.
        ' Grab the syncMciCommand variable and memorize the mciCommand
        syncMciCommand = Command

        ' Release the MonitorObject, so the other thread 
        ' can process the mciSendString
        Monitor.Pulse(MonitorLock) ' [This exits the Monitor.wait(MonitorLock) command 
                                   ' on the other thread.]
        
        ' Now wait until the mci-thread signals the arrival of the result.
        Monitor.Wait(MonitorLock)
        ' result has been written into syncMciResult
        nonLockedReturn = syncMciResult
    End SyncLock

    ' use nonLockedReturn to get the result safely out of SyncLock
    Return nonLockedReturn
  End Function

  'The synced variables need to be defined
  Private MonitorLock as new Object()
  Private mciReturnValue As Integer
  Private syncMciCommand as String

End Class


To make thread safe calls to the media control interface (mci), use the above class as follows:

Dim cMci as new mciTest()
cMci.mciReplacement("open ""C:\test.wav"" type waveaudio alias audiofile")
cMci.mciReplacement("play audiofile")
cMci.mciReplacement("pause audiofile")
cMci.mciReplacement("resume audiofile")


Many year after.. but I was reserching on this problem... it is basically because the MCI API needs a Message Loop to be active in the application (So.. it has to be Windows Forms to make the life easy)

http://msdn.microsoft.com/en-us/magazine/cc163417.aspx[^]

This post gave me an idea... so I decided to implement an Invoke and InvokeRequeried over the mciCall always to make sure the calls will be made inside the Main Application Thread. Of course I need to send all over the classes the Form reference to use it, but is not a big deal, at least in my project it isn''t.

I hope if someone has the same problem can solve it with this :D

I know today with NUGET Package many extensions and libraries do the job.. but I try to make my project under my control, using the less third party libraries as possible.. so I am pretty sure many people would find it useful.

Thanks

Here is the final code

using System;
using System.Threading;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public class mciSafeCall
{

    [DllImport("winmm.dll")]
    private static extern int mciSendString(string strCommand, StringBuilder strReturn, int iReturnLength, IntPtr hwndCallback);


    private int syncMciResult;
    private string syncMciCommand;
    private Form formulario;

    public mciSafeCall(Form form)
    {
        formulario = form;
        syncMciCommand = "";
    }

    public void mciReplacement(string Command)
    {
        syncMciCommand = Command;
        if (!string.IsNullOrEmpty(syncMciCommand))
        {
            if (formulario.InvokeRequired)
                formulario.Invoke(new Action(() =>
                {
                    syncMciResult = mciSendString(syncMciCommand, null, 0, IntPtr.Zero);
                }));
            else
                syncMciResult = mciSendString(syncMciCommand, null, 0, IntPtr.Zero);

        }
    }



}


From reading this CP article it seems the problem is something to do with the 2 threads competing for the same object. Have a read of the article here Introduction to making multithreaded VB.NET Apps[^]


这篇关于如何在线程应用程序中使用mciSendString的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆