如何使用C ++ CLI在winforms中实现可靠的重复媒体按钮?

问题描述 投票:-1回答:1

我有一个winforms项目,为AxWMPLib::AxWindowsMediaPlayer实现了一个重复按钮:

double positionA = 0.0;
double positionB = 0.0;

AxWMPLib::AxWindowsMediaPlayer^ axWindowsMediaPlayer;
System::Windows::Forms::Timer^ timer;

System::Void stopTimer() {
    if (this->timer != nullptr) {
        this->timer->Stop();
        this->positionA = 0.0;
        this->positionB = 0.0;
        this->timer = nullptr;
    }
}

System::Void startTimer() {
    stopTimer();
    this->timer = gcnew System::Windows::Forms::Timer();
    this->timer->Tick += gcnew System::EventHandler(this, &MyClass::timerTick);
    this->timer->Enabled = true;
    this->timer->Interval = 100;
    this->timer->Start();
}

System::Void timerTick(System::Object^ sender, System::EventArgs^ e) {
    double currentTime = this->axWindowsMediaPlayer->Ctlcontrols->currentPosition;
    if (currentTime < this->positionA || currentTime > this->positionB) {
        stopTimer();
        return;
    }
    else if (System::Convert::ToInt32(currentTime) != System::Convert::ToInt32(this->positionB)) return;

    this->axWindowsMediaPlayer->Ctlcontrols->currentPosition = this->positionA;
}

System::Windows::Forms::Timer似乎不可靠,因为它只能工作一会儿,直到您开始执行其他操作(例如浏览或执行更多任务)后,它才中断,重复序列停止,媒体继续播放。为此有可靠的解决方案吗?

编辑:我确实是从CP读到的:

您应注意,Windows不是实时操作系统。您将不会获得固定的计时器间隔;特别是当系统负载高或磁盘或网络数据传输很多。

这是有道理的,这就是为什么它在浏览操作后会损坏。因此,我对它进行了深入研究,并根据Microsoft文档中的说明找到了使用多媒体计时器的更好解决方案:

多媒体计时器服务允许应用程序安排计时器事件具有最高的硬件分辨率(或精度)平台。这些多媒体计时器服务可让您安排计时器事件比其他计时器服务具有更高的分辨率。

这些计时器服务对于需要高分辨率定时。例如,MIDI音序器需要一个高分辨率计时器,因为它必须保持MIDI事件的速度在1毫秒的分辨率内。

在阅读它使我进入CreateTimerQueueTimer函数后,因为根据Microsoft文档,它已被弃用:

本主题描述了过时的功能。新应用程序应使用CreateTimerQueueTimer函数来创建计时器。

所以我使用CreateTimerQueueTimer进行以下解决方案:

#include "pch.h"
#include <windows.h>

using namespace System;

VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired);

public ref class MyClass {
public:
    int myInt = 0;
    MyClass(int a) {
        this->myInt = a;
    }

    System::Void myFoo() {
        System::Console::WriteLine("myInt value is: " + this->myInt);
    }
};

public delegate System::Void Del();
int main(array<System::String ^> ^args)
{
    HANDLE hTimer = NULL;
    HANDLE hTimerQueue = NULL;

    // Create the timer queue.
    hTimerQueue = CreateTimerQueue();
    if (NULL == hTimerQueue)
    {
        System::Console::WriteLine("CreateTimerQueue failed (%d)", GetLastError());
        return 2;
    }

    MyClass^ a = gcnew MyClass(28);
    Del^ d = gcnew Del(a, &MyClass::myFoo);

    // Set a timer to call the timer routine every 1 second.
    if (!CreateTimerQueueTimer(&hTimer, hTimerQueue,
        (WAITORTIMERCALLBACK)TimerRoutine, (PVOID)&d, 0, 1000, 0))
    {
        System::Console::WriteLine("CreateTimerQueueTimer failed (%d)", GetLastError());
        return 3;
    }

    // Delete all timers in the timer queue.
    /*if (!DeleteTimerQueue(hTimerQueue))
        printf("DeleteTimerQueue failed (%d)\n", GetLastError());*/

    while (true) {}

    return 0;
}

VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    if (lpParam == NULL)
    {
        System::Console::WriteLine("TimerRoutine lpParam is NULL");
    }
    else
    {
        // lpParam points to the myFoo function;
        ((MyClass^&)lpParam)->myFoo();

        if (TimerOrWaitFired)
        {
            System::Console::WriteLine("The wait timed out.");
        }
        else
        {
            System::Console::WriteLine("The wait event was signaled.");
        }
    }
}

这是一个独立的测试,任何人都可以通过在Visual Studio上启动CLR控制台应用程序进行复制,它似乎可以正常工作,但是我无法更改myInt的值,当它打印出它的值时,它会给我一个随机值,但如果该int是静态的,则可以工作,但是在您的真实项目中,正如您在EDIT之前注意到的那样,如果不是int的示例,那么我使用的是axWindowsMediaPlayer->Ctlcontrols->currentPosition,它返回一个double值,如果它是AxWindowsMediaPlayer的实例],所以我得到一个空指针异常,并且当AxWindowsMediaPlayer静态时,currentPosition的值返回0。当涉及到回调和委托时,我可能会有一个根本性的误解,所以我认为它与编程语言本身的关系更大C ++ CLI语法,这就是我添加c ++标记的原因,也许这是一个简单的逻辑,有人可以向我解释。这是输出:

myInt value is: 16467968
The wait timed out.
.net winforms c++-cli clr
1个回答
0
投票

我只是发现我没有回调或委托问题,而是指针和引用的误解,这使我的对象为null,这就是为什么要打印该值的原因,因为那是一个null变量,所以我也意识到不需要委托。我解决了这个问题,并在我的项目中实现了该计时器解决方案,该解决方案比以前的解决方案要好得多,但是我不确定是否可以将此解决方案称为“可靠的”,因为在进行浏览操作时,有时会干扰重复序列以超越花费一点时间。我已经解决了这一问题,我认为没有更好的解决方案,因为该解决方案基于Microsoft解决方案,可实现高分辨率定时。

#include "pch.h"
#include <windows.h>

using namespace System;

public ref class MyClass {
public:
    int myInt = 0;
    MyClass(int a) {
        this->myInt = a;
    }

    System::Void myFoo() {
        System::Console::WriteLine("myInt value is: " + this->myInt);
    }
};

VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
    if (lpParam == NULL)
    {
        System::Console::WriteLine("TimerRoutine lpParam is NULL");
    }
    else
    {
        // lpParam points to the myFoo function;
        ((MyClass^&)lpParam)->myFoo();

        if (TimerOrWaitFired)
        {
            System::Console::WriteLine("The wait timed out.");
        }
        else
        {
            System::Console::WriteLine("The wait event was signaled.");
        }
    }
}

int main(array<System::String ^> ^args)
{
    HANDLE hTimer = NULL;
    HANDLE hTimerQueue = NULL;

    // Create the timer queue.
    hTimerQueue = CreateTimerQueue();
    if (NULL == hTimerQueue)
    {
        System::Console::WriteLine("CreateTimerQueue failed (%d)", GetLastError());
        return 2;
    }

    MyClass^ a = gcnew MyClass(28);

    // Set a timer to call the timer routine every millisecond.
    if (!CreateTimerQueueTimer(&hTimer, hTimerQueue,
        (WAITORTIMERCALLBACK)TimerRoutine, (PVOID&)a, 0, 1, 0))
    {
        System::Console::WriteLine("CreateTimerQueueTimer failed (%d)", GetLastError());
        return 3;
    }

    // Delete all timers in the timer queue.
    /*if (!DeleteTimerQueue(hTimerQueue))
        printf("DeleteTimerQueue failed (%d)\n", GetLastError());*/

    while (true) {}

    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.