如何使用unicode版本的Windows API:mciSendString(),Python

如何使用unicode版本的Windows API:mciSendString(),Python,python,winapi,Python,Winapi,我正在Windows10:playsound上测试Python包 它似乎对路径名的某些字符有问题,例如“c:\sauté”和宽字符。所以它找不到文件 命令的错误275: 打开“C:\sauté.wav”别名playsound_0.4091468603477375 找不到指定的文件。确保路径和文件名正确 我尝试使用unicode版本mciSendStringW()。原来mciSendStringW根本无法识别编码的命令。我不知道我现在还能做什么 def winCommand(*命令): buf=c

我正在Windows10:playsound上测试Python包

它似乎对路径名的某些字符有问题,例如“c:\sauté”和宽字符。所以它找不到文件

命令的错误275: 打开“C:\sauté.wav”别名playsound_0.4091468603477375 找不到指定的文件。确保路径和文件名正确

我尝试使用unicode版本
mciSendStringW()
。原来mciSendStringW根本无法识别编码的命令。我不知道我现在还能做什么

def winCommand(*命令):
buf=c_缓冲区(255)
command=''.join(command).encode(getfilesystemencoding())
errorCode=int(windell.winmm.mciSendStringA(命令,buf,254,0))
如果出现错误代码:
errorBuffer=c_缓冲区(255)
windell.winmm.mcigeterorstringa(错误代码,错误缓冲区,254)
exceptionMessage=('\n Error'+str(errorCode)+'用于命令:'
'\n'+命令.解码()+
'\n'+errorBuffer.value.decode())
引发播放声音异常(例外消息)
返回基本单位值
prj现场:(包括安装和快速启动指南)

src代码:

Microsoft mciSendString函数:

使用宽函数时,
mciSendStringW
,不应对字符串进行编码。因此,您的行应该简单地读取
command=''.join(command)
。至少在使用Python 3.6的Windows10机器上是这样

要再次检查,可以运行下面的代码。第二个错误代码是296,这只是抱怨它是错误的文件类型,因为我们为测试创建了一个空文件

从ctypes导入c_缓冲区,Windell
从sys导入getfilesystemencoding
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
buf=c_缓冲区(255)
filesystemencoding=getfilesystemencoding()
filename=r'.\sauté.wav'
#如果文件不存在,请创建该文件
文件=打开(文件名为“w+”)
file.close()文件
#ASCII码
命令='打开'+文件名
字节\字符串\命令=命令.编码(文件系统编码)
errorCode=int(windell.winmm.mciSendStringA(byte_string_命令,buf,254,0))
#错误代码应为275:找不到该文件
errorBuffer=c_缓冲区(255)
windell.winmm.mcigeterorstringa(错误代码,错误缓冲区,254)
打印(“{}:{}.”格式(errorCode,errorBuffer.value.decode())
#统一码
errorCode=int(windell.winmm.mciSendStringW(命令,buf,254,0))
#错误代码应为296:无法播放指定的文件
errorBuffer=c_缓冲区(255)
windell.winmm.mcigeterorstringa(错误代码,错误缓冲区,254)
打印(“{}:{}.”格式(errorCode,errorBuffer.value.decode())

使用wide函数时,
mciSendStringW
不应对字符串进行编码。因此,您的行应该简单地读取
command=''.join(command)
。至少在使用Python 3.6的Windows10机器上是这样

要再次检查,可以运行下面的代码。第二个错误代码是296,这只是抱怨它是错误的文件类型,因为我们为测试创建了一个空文件

从ctypes导入c_缓冲区,Windell
从sys导入getfilesystemencoding
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
buf=c_缓冲区(255)
filesystemencoding=getfilesystemencoding()
filename=r'.\sauté.wav'
#如果文件不存在,请创建该文件
文件=打开(文件名为“w+”)
file.close()文件
#ASCII码
命令='打开'+文件名
字节\字符串\命令=命令.编码(文件系统编码)
errorCode=int(windell.winmm.mciSendStringA(byte_string_命令,buf,254,0))
#错误代码应为275:找不到该文件
errorBuffer=c_缓冲区(255)
windell.winmm.mcigeterorstringa(错误代码,错误缓冲区,254)
打印(“{}:{}.”格式(errorCode,errorBuffer.value.decode())
#统一码
errorCode=int(windell.winmm.mciSendStringW(命令,buf,254,0))
#错误代码应为296:无法播放指定的文件
errorBuffer=c_缓冲区(255)
windell.winmm.mcigeterorstringa(错误代码,错误缓冲区,254)
打印(“{}:{}.”格式(errorCode,errorBuffer.value.decode())

虽然返回“0”(成功),但没有声音。我的是Python3.7和Win10。我还尝试了utf-8、utf-16-le,但没有效果。

您需要添加
wait
标志。打开此标志后,您确实可以 等待调用的函数完成。你可以 播放你的文件。如果您将其移除,它将启动播放 然后马上关闭它

整个代码(ASCII):

UNICODE:

from ctypes import c_buffer, windll
from sys import getfilesystemencoding

if __name__ == '__main__':
    buf = c_buffer(255)
    filesystemencoding = getfilesystemencoding()
    filename = r'.\file_example.mp3'


    # ASCII
    command = r'open ' + filename + r' alias test2'
    waitcommand = r'play test2 wait'
    byte_string_command = command.encode(filesystemencoding)
    waiting = waitcommand.encode(filesystemencoding)

    # Unicode
    errorCode = int(windll.winmm.mciSendStringW(command, buf, 254, 0))
    # errorCode should be 296: The specified file cannot be played
    errorBuffer = c_buffer(255)
    windll.winmm.mciGetErrorStringA(errorCode, errorBuffer, 254)
    print("{}: {}".format(errorCode, errorBuffer.value.decode()))

    errorCode = int(windll.winmm.mciSendStringW(waitcommand, buf, 254, 0))
    # errorCode should be 275: Cannot find the file
    errorBuffer = c_buffer(255)
    windll.winmm.mciGetErrorStringA(errorCode, errorBuffer, 254)
    print("{}: {}".format(errorCode, errorBuffer.value.decode()))
如果代码正常工作,它将返回
0:执行了指定的命令

注意:

  • 返回用于在Unicode之间转换的编码的名称 文件名和字节文件名。为了获得最佳兼容性,str应该是 用于所有情况下的文件名,但将文件名表示为 还支持字节。接受或返回文件名的函数 应支持str或bytes,并在内部转换为 系统的首选表示形式

    This encoding is always ASCII-compatible.
    
    [os.fsencode()][2] and [os.fsdecode()][3] should be used to ensure that the
    correct encoding and errors mode are used.
    
    In the UTF-8 mode, the encoding is utf-8 on any platform.
    
    On macOS, the encoding is 'utf-8'.
    
    On Unix, the encoding is the locale encoding.
    
    On Windows, the encoding may be 'utf-8' or 'mbcs', depending on user
    configuration.
    
    Changed in version 3.6: Windows is no longer guaranteed to return
    'mbcs'. See PEP 529 and [_enablelegacywindowsfsencoding()][4] for more
    information.
    
    Changed in version 3.7: Return ‘utf-8’ in the UTF-8 mode.
    
  • 打开设备时,可以使用“别名”标志指定 设备的设备标识符。此标志允许您指定一个短字符 具有长文件名的复合设备的设备标识符,以及 它允许您打开同一文件或设备的多个实例

  • 如果您希望播放无等待,则需要处理
    MCI\U NOTIFY
    ,设置 回调窗口句柄,并在 游戏结束了

    :如果“notify”标志 已在命令字符串中指定


    虽然返回“0”(成功),但没有声音。我的是Python3.7和Win10。我也不知道
    This encoding is always ASCII-compatible.
    
    [os.fsencode()][2] and [os.fsdecode()][3] should be used to ensure that the
    correct encoding and errors mode are used.
    
    In the UTF-8 mode, the encoding is utf-8 on any platform.
    
    On macOS, the encoding is 'utf-8'.
    
    On Unix, the encoding is the locale encoding.
    
    On Windows, the encoding may be 'utf-8' or 'mbcs', depending on user
    configuration.
    
    Changed in version 3.6: Windows is no longer guaranteed to return
    'mbcs'. See PEP 529 and [_enablelegacywindowsfsencoding()][4] for more
    information.
    
    Changed in version 3.7: Return ‘utf-8’ in the UTF-8 mode.
    
    def _playsoundWin(sound, block = True):
    
    from ctypes import c_buffer, windll
    from random import random
    from time   import sleep
    
    def winCommand(*command):
        buf = c_buffer(255)
        command = ' '.join(command)
        # errorCode = int(windll.winmm.mciSendStringA(command, buf, 254, 0)) # original line
        errorCode = int(windll.winmm.mciSendStringW(command, buf, 254, 0))
        if errorCode:
            errorBuffer = c_buffer(255)
            # windll.winmm.mciGetErrorStringA(errorCode, errorBuffer, 254) # original line
            windll.winmm.mciGetErrorStringW(errorCode, errorBuffer, 254)
            exceptionMessage = ('\n    Error ' + str(errorCode) + ' for command:'
                                '\n        ' + command +
                                '\n    ' + errorBuffer.value)
            raise PlaysoundException(exceptionMessage)
        return buf.value
    
    alias = 'playsound_' + str(random())
    winCommand('open "' + sound + '" alias', alias)
    # winCommand('set', alias, 'time format milliseconds') # is not needed
    # durationInMS = winCommand('status', alias, 'length') # returns bytes!
    # durationInMS = durationInMS.decode() # needed for the original command
    # winCommand('play', alias, 'from 0 to', durationInMS)
    winCommand('play', alias, 'wait') # 'wait' does the trick
    
    if block:
        pass
        # sleep(float(durationInMS) / 1000.0) # don't know it's purpose