Delphi 10.2中MCI播放MIDI文件重复播放失败问题求助
Delphi MCI MIDI 播放问题修复:重复选同一文件停止+MCI_CLOSE类型错误
问题根源
- 重复选文件失败:原代码仅在播放失败时关闭设备,正常播放完成依赖
MM_MCINOTIFY消息关闭,但重复选同一文件时,旧设备实例可能未及时释放,导致MCI无法重新打开同一文件。 - MCI_CLOSE类型错误:Delphi中
NULL是变体类型,而mciSendCommand第四个参数要求DWORD_PTR,直接传NULL会触发类型转换异常,需替换为0或合法指针。
修复步骤&代码
第一步:添加设备ID成员变量
在窗体类的private区域添加变量,跟踪当前打开的MIDI设备:
private FCurrentMidiDeviceID: Integer; // 保存当前播放的MIDI设备ID,初始化为0
第二步:修改PlayMidiFile函数
修复设备清理、参数初始化和类型错误:
uses MMSystem; Function TForm1.PlayMidiFile(FileName: string): Word; var wDeviceID: Integer; dwReturn : Word; mciOpen : TMCI_Open_Parms; mciPlay : TMCI_Play_Parms; mciStat : TMCI_Status_Parms; mciStop : TMCI_Generic_Parms; begin // 先清理旧设备:停止播放并关闭 if FCurrentMidiDeviceID <> 0 then begin mciStop.dwCallback := 0; mciSendCommand(FCurrentMidiDeviceID, MCI_STOP, 0, DWORD_PTR(@mciStop)); mciSendCommand(FCurrentMidiDeviceID, MCI_CLOSE, 0, 0); FCurrentMidiDeviceID := 0; end; // 初始化参数结构体,避免残留旧数据 ZeroMemory(@mciOpen, SizeOf(TMCI_Open_Parms)); mciOpen.lpstrDeviceType := 'sequencer'; mciOpen.lpstrElementName := PChar(FileName); // 打开MIDI设备 dwReturn := mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE or MCI_OPEN_ELEMENT, DWORD_PTR(@mciOpen)); if dwReturn <> 0 then begin Result := dwReturn; Exit; end; wDeviceID := mciOpen.wDeviceID; FCurrentMidiDeviceID := wDeviceID; // 检查MIDI映射器端口 ZeroMemory(@mciStat, SizeOf(TMCI_Status_Parms)); mciStat.dwItem := MCI_SEQ_STATUS_PORT; dwReturn := mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, DWORD_PTR(@mciStat)); if dwReturn <> 0 then begin mciSendCommand(wDeviceID, MCI_CLOSE, 0, 0); FCurrentMidiDeviceID := 0; Result := dwReturn; Exit; end; // 启动播放,绑定窗体句柄接收完成通知 ZeroMemory(@mciPlay, SizeOf(TMCI_Play_Parms)); mciPlay.dwCallback := DWORD_PTR(Handle); dwReturn := mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, DWORD_PTR(@mciPlay)); if dwReturn <> 0 then begin mciSendCommand(wDeviceID, MCI_CLOSE, 0, 0); FCurrentMidiDeviceID := 0; Result := dwReturn; end else Result := 0; end;
第三步:添加MM_MCINOTIFY消息处理
播放完成后自动关闭设备,避免资源占用:
procedure TForm1.WndProc(var Message: TMessage); begin inherited; if Message.Msg = MM_MCINOTIFY then begin // 播放成功完成时关闭设备 if (Message.WParam = MCI_NOTIFY_SUCCESSFUL) and (FCurrentMidiDeviceID <> 0) then begin mciSendCommand(FCurrentMidiDeviceID, MCI_CLOSE, 0, 0); FCurrentMidiDeviceID := 0; end; end; end;
关键修复说明
- 强制清理旧设备:每次播放新文件前,先停止并关闭之前的设备,彻底释放文件占用,解决重复选同一文件的问题。
- 替换NULL为0:将原代码中
mciSendCommand(..., NULL)改为mciSendCommand(..., 0),适配Delphi类型系统,消除转换错误。 - 初始化参数结构体:用
ZeroMemory清空参数,避免结构体残留旧值导致MCI命令执行异常。 - 跟踪设备ID:用成员变量记录当前设备,确保所有操作都能准确指向目标设备。
内容的提问来源于stack exchange,提问作者larshgf
相关产品推荐
相关产品推荐

