调用dll文件阅读文本

Vim、Emacs配置和使用
lh-n
帖子: 40
注册时间: 2009-02-23 15:52

调用dll文件阅读文本

#1

帖子 lh-n » 2012-08-14 23:36

我用的XP,已经安装了中英文语音阅读库,手里有个dll文件SpeechText.dll

在命令行中执行 :ec libcall("SpeechText", "MT_SpeechText", "VIM speak"), 可以发出语音,但是VIM随即崩溃。有什么办法可以让它不崩溃?
lh-n
帖子: 40
注册时间: 2009-02-23 15:52

Re: 调用dll文件阅读文本

#2

帖子 lh-n » 2012-08-15 0:02

查看了帮助,里面说“函数必须只能接受单个参数,或者是字符指针,或者是长整数,而且必须返回字符指针或者 NULL”。查看了一下源代码,该函数返回bool值。谁能帮我改一下并编译成dll文件?这个我不会

代码: 全选

#include <windows.h>
#include <sapi.h>

#pragma comment(lib, "ole32.lib")
#pragma comment(lib, "sapi.lib")

ISpVoice * gpVoice = NULL;

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
        switch (fdwReason) {
                case DLL_PROCESS_ATTACH:
                        if (FAILED(CoInitialize(NULL))) {
                                return FALSE;
                        }
                        if (FAILED(CoCreateInstance(CLSID_SpVoice, NULL, CLSCTX_ALL, IID_ISpVoice, (void **)&gpVoice))) {
                                return FALSE;
                        }
                        if (!gpVoice) return FALSE;
                        break;
                case DLL_PROCESS_DETACH:
                        /*
                        if (gpVoice) gpVoice->Release();
                        */
                        gpVoice = NULL;
                        CoUninitialize();
                        break;
                default:
                        break;
        }
        return TRUE;
}

BOOL WINAPI MT_SpeechText(char const * text, BOOL async)
{
        WCHAR * msg;
        BOOL ret;
        int len;
        DWORD dwFlags;

        len = strlen(text) + 1;
        msg = new WCHAR[len];
        MultiByteToWideChar(CP_ACP, 0, text, len, msg, len);
        if (async) {
                dwFlags = SPF_ASYNC;
        } else {
                dwFlags = SPF_DEFAULT;
        }
        if (FAILED(gpVoice->Speak(msg, dwFlags, NULL))) {
                ret = FALSE;
        } else {
                ret = TRUE;
        }
        delete msg;
        return ret;
} 
头像
自由建客
帖子: 13468
注册时间: 2008-07-30 23:21
系统: Debian stable AMD64

Re: 调用dll文件阅读文本

#3

帖子 自由建客 » 2012-08-15 0:37

libcall() 接受的输入参数不是长整数,而是 int。返回值是 char*。
更微妙的是,这个 char* 指向的那片区域很可能是非法的。
lh-n
帖子: 40
注册时间: 2009-02-23 15:52

Re: 调用dll文件阅读文本

#4

帖子 lh-n » 2012-08-15 8:22

自由建客 写了:libcall() 接受的输入参数不是长整数,而是 int。返回值是 char*。
更微妙的是,这个 char* 指向的那片区域很可能是非法的。
我输入的参数是字符串,而且运行成功,只是返回bool值引起崩溃。如果修改MT_SpeechText函数,让其返回NULL,应该就不会崩溃了吧?
头像
lilydjwg
论坛版主
帖子: 4258
注册时间: 2009-04-11 23:46
系统: Arch Linux
联系:

Re: 调用dll文件阅读文本

#5

帖子 lilydjwg » 2012-08-15 9:53

lh-n 写了:查看了帮助,里面说“函数必须只能接受单个参数,或者是字符指针,或者是长整数,而且必须返回字符指针或者 NULL”。查看了一下源代码,该函数返回bool值。谁能帮我改一下并编译成dll文件?这个我不会
不会 cl.exe。你要是给 gcc 能读的源码我直接在 Linux 上就能给你编译了。
wz520
帖子: 36
注册时间: 2010-09-23 13:47

Re: 调用dll文件阅读文本

#6

帖子 wz520 » 2012-08-15 11:45

以前写过几个给 libcall() 调用的DLL,粗略看了一下:
1. 如果 DLL 的函数返回数值类型,在 Vim 脚本里要用 libcallnr() 而不是 libcall() 。

2. 应该是出错的主要原因:DLL 的函数必须是 C 调用约定(具体见 :h libcall())。WINAPI 宏其实就是指定函数是 pascal 调用约定。调用约定不一样,函数返回后堆栈会被破坏,导致程序杯具。解决方法就是把 WINAPI 改成 __cdecl 。(如果使用纯C编译,可以直接把 WINAPI 删掉,但是看你代码里有 new 和 delete ,应该是按 C++ 编译的)

3. MT_SpeechText() 有两个参数,但是 Vim 的 libcall() 和 libcallnr() 只能传 1 个参数。如果你想让第二个参数起作用,另外写两个函数,分别调用 MT_SpeechText(text, TRUE) 和 MT_SpeechText(text, FALSE) ,然后从 Vim 脚本里根据需要调用这两个函数就 OK 了。
头像
自由建客
帖子: 13468
注册时间: 2008-07-30 23:21
系统: Debian stable AMD64

Re: 调用dll文件阅读文本

#7

帖子 自由建客 » 2012-08-15 17:04

lh-n 写了:
自由建客 写了:libcall() 接受的输入参数不是长整数,而是 int。返回值是 char*。
更微妙的是,这个 char* 指向的那片区域很可能是非法的。
我输入的参数是字符串,而且运行成功,只是返回bool值引起崩溃。如果修改MT_SpeechText函数,让其返回NULL,应该就不会崩溃了吧?
我没说输入不能是字符串,只是纠正你说的长整数的想法,它不支持 int 之外的整数类型。
libcallnr() 返回类型为 int,可直接用于你的 BOOL。
lh-n
帖子: 40
注册时间: 2009-02-23 15:52

Re: 调用dll文件阅读文本

#8

帖子 lh-n » 2012-08-15 20:21

wz520 写了:以前写过几个给 libcall() 调用的DLL,粗略看了一下:
1. 如果 DLL 的函数返回数值类型,在 Vim 脚本里要用 libcallnr() 而不是 libcall() 。

2. 应该是出错的主要原因:DLL 的函数必须是 C 调用约定(具体见 :h libcall())。WINAPI 宏其实就是指定函数是 pascal 调用约定。调用约定不一样,函数返回后堆栈会被破坏,导致程序杯具。解决方法就是把 WINAPI 改成 __cdecl 。(如果使用纯C编译,可以直接把 WINAPI 删掉,但是看你代码里有 new 和 delete ,应该是按 C++ 编译的)

3. MT_SpeechText() 有两个参数,但是 Vim 的 libcall() 和 libcallnr() 只能传 1 个参数。如果你想让第二个参数起作用,另外写两个函数,分别调用 MT_SpeechText(text, TRUE) 和 MT_SpeechText(text, FALSE) ,然后从 Vim 脚本里根据需要调用这两个函数就 OK 了。
你好,谢谢你的分析。这个代码不是我写的,C++我也是才学了个皮毛,电脑上也没有C++编译器,你能帮我改一下源代码并编译成dll吗?
lh-n
帖子: 40
注册时间: 2009-02-23 15:52

Re: 调用dll文件阅读文本

#9

帖子 lh-n » 2012-08-15 20:22

自由建客 写了:
lh-n 写了:
自由建客 写了:libcall() 接受的输入参数不是长整数,而是 int。返回值是 char*。
更微妙的是,这个 char* 指向的那片区域很可能是非法的。
我输入的参数是字符串,而且运行成功,只是返回bool值引起崩溃。如果修改MT_SpeechText函数,让其返回NULL,应该就不会崩溃了吧?
我没说输入不能是字符串,只是纠正你说的长整数的想法,它不支持 int 之外的整数类型。
libcallnr() 返回类型为 int,可直接用于你的 BOOL。
谢谢你,我用libcallnr()也是一样的崩溃。
头像
lilydjwg
论坛版主
帖子: 4258
注册时间: 2009-04-11 23:46
系统: Arch Linux
联系:

Re: 调用dll文件阅读文本

#10

帖子 lilydjwg » 2012-08-15 21:57

给我 sapi 的头文件和库文件,我试试能不能帮你编译一个。
lh-n
帖子: 40
注册时间: 2009-02-23 15:52

Re: 调用dll文件阅读文本

#11

帖子 lh-n » 2012-08-15 22:05

lilydjwg 写了:给我 sapi 的头文件和库文件,我试试能不能帮你编译一个。
不好意思,我没有也不懂那些东西 :em06
还是谢谢你!
头像
自由建客
帖子: 13468
注册时间: 2008-07-30 23:21
系统: Debian stable AMD64

Re: 调用dll文件阅读文本

#12

帖子 自由建客 » 2012-08-16 19:55

汗!现在才注意到 MT_SpeechText() 是两个参数。
wz520
帖子: 36
注册时间: 2010-09-23 13:47

Re: 调用dll文件阅读文本

#13

帖子 wz520 » 2012-08-17 13:26

lh-n 写了: 你好,谢谢你的分析。这个代码不是我写的,C++我也是才学了个皮毛,电脑上也没有C++编译器,你能帮我改一下源代码并编译成dll吗?
搞定了。VC2010 Express 编译。在我的 GVim 7.3.629 上测试有声音,不会崩溃。
另附最终用于编译的源码。

前面也说了,因为 MT_SpeechText() 要两个参数,最好不要从 Vim 脚本里调用,而用 SpeechText() 和 SpeechTextAsync() 代替。具体见压缩包里的源码。

不过,貌似不支持中文,也许是我没装中文语音阅读库。如果你也不行,那么 Vim 命令改成下面这样子试试:
:echo libcall("SpeechText", "SpeechText", iconv("你好", 'utf-8', 'cp936'))
附件
SpeechText.zip
(38.47 KiB) 已下载 92 次
lh-n
帖子: 40
注册时间: 2009-02-23 15:52

Re: 调用dll文件阅读文本

#14

帖子 lh-n » 2012-08-17 21:49

wz520 写了:
lh-n 写了: 你好,谢谢你的分析。这个代码不是我写的,C++我也是才学了个皮毛,电脑上也没有C++编译器,你能帮我改一下源代码并编译成dll吗?
搞定了。VC2010 Express 编译。在我的 GVim 7.3.629 上测试有声音,不会崩溃。
另附最终用于编译的源码。

前面也说了,因为 MT_SpeechText() 要两个参数,最好不要从 Vim 脚本里调用,而用 SpeechText() 和 SpeechTextAsync() 代替。具体见压缩包里的源码。

不过,貌似不支持中文,也许是我没装中文语音阅读库。如果你也不行,那么 Vim 命令改成下面这样子试试:
:echo libcall("SpeechText", "SpeechText", iconv("你好", 'utf-8', 'cp936'))
非常感谢,用你的方法不不但不会崩溃,也可以阅读中文。
但是还有个问题,该函数只能用一次,第二次用就不会发声了。
----------------------------------------------------------
可能是我的语音引擎的问题,前面用的是Neospeech的引擎,我又试了系统自带的语音引擎没问题。这到底是什么原因呢?
wz520
帖子: 36
注册时间: 2010-09-23 13:47

Re: 调用dll文件阅读文本

#15

帖子 wz520 » 2012-08-22 7:17

lh-n 写了:非常感谢,用你的方法不不但不会崩溃,也可以阅读中文。
但是还有个问题,该函数只能用一次,第二次用就不会发声了。
----------------------------------------------------------
可能是我的语音引擎的问题,前面用的是Neospeech的引擎,我又试了系统自带的语音引擎没问题。这到底是什么原因呢?
这我就不知道了,没搞过TTS相关的编程。我只是在你贴的代码的基础上做了很简单的修改,没有改动核心的东西。
而且我的系统也只装了自带的引擎,没办法调试 NeoSpeech 的效果。
要么……求个 NeoSpeech 语音库下载,让我试着调试一下? :em03
回复