c# - 如何从用 C++ 编写的外部 dll 中获取 const char * 到 C#

标签 c# c++ dll sphinx unity3d

我将 CMU 的 PocketSphinx 与 Unity 集成,方法是在 Visual Studio 2010 中将我自己的 C++ 项目编译为 DLL,我从 Unity Pro 中的 C# 脚本调用该项目。我知道这个 dll 可以工作,因为我用完全相同的代码制作了另一个项目作为 exe,编译它,它作为一个独立的程序完美地工作。我正在使用 pocketsphinx_continuous 项目示例,它获取麦克风输入并将文本输出到控制台。我已将此代码自定义为从 Unity 内部调用,它应该作为字符串而不是控制台输出回我的 C# 代码。我觉得我几乎可以正常工作了,但是 const char * 只是没有将其作为字符串返回。如果我使用此声明,我最终会遇到访问冲突错误:

私有(private)静态外部字符串 recognize_from_microphone();

所以,我尝试使用这个:

private static extern IntPtr recognize_from_microphone();

然后,我使用这行代码尝试打印该函数的输出:

print("你刚才说"+ Marshal.PtrToStringAnsi(recognize_from_microphone()));

但是,我得到的只是“你刚才说”的返回。如果我这样做,我可以设法取回内存地址: print("you just said "+ recognize_from_microphone());所以,我知道有些东西正在返回。

这是我的 C++ 代码(其中大部分最初是用 C 语言编写的,作为来自 pocketsphinx 的示例代码):

char* MakeStringCopy (const char* str) 
{
  if (str == NULL) return NULL;
  char* res = (char*)malloc(strlen(str) + 1);
  strcpy(res, str);
  return res;
}


extern __declspec(dllexport) const char * recognize_from_microphone()
{
//this is a near complete duplication of the code from main()
char const *cfg;
config = cmd_ln_init(NULL, ps_args(), TRUE,
"-hmm", MODELDIR "\\hmm\\en_US\\hub4wsj_sc_8k",
"-lm", MODELDIR "\\lm\\en\\turtle.DMP",
"-dict", MODELDIR "\\lm\\en\\turtle.dic",
NULL);

if (config == NULL)
{
   return "config is null";
}

ps = ps_init(config);
if (ps == NULL)
{
   return "ps is null";
}

ad_rec_t *ad;
int16 adbuf[4096];
int32 k, ts, rem;
char const *hyp;
char const *uttid;
cont_ad_t *cont;
char word[256];
char words[1024] = "";
//char temp[] = "hypothesis";
//hyp = temp;

if ((ad = ad_open_dev(cmd_ln_str_r(config, "-adcdev"),
                      (int)cmd_ln_float32_r(config, "-samprate"))) == NULL)
    E_FATAL("Failed to open audio device\n");

/* Initialize continuous listening module */
if ((cont = cont_ad_init(ad, ad_read)) == NULL)
    E_FATAL("Failed to initialize voice activity detection\n");
if (ad_start_rec(ad) < 0)
    E_FATAL("Failed to start recording\n");
if (cont_ad_calib(cont) < 0)
    E_FATAL("Failed to calibrate voice activity detection\n");

for (;;) {
    /* Indicate listening for next utterance */
    //printf("READY....\n");
    fflush(stdout);
    fflush(stderr);

    /* Wait data for next utterance */
    while ((k = cont_ad_read(cont, adbuf, 4096)) == 0)
        sleep_msec(100);

    if (k < 0)
        E_FATAL("Failed to read audio\n");

    /*
     * Non-zero amount of data received; start recognition of new utterance.
     * NULL argument to uttproc_begin_utt => automatic generation of utterance-id.
     */
    if (ps_start_utt(ps, NULL) < 0)
        E_FATAL("Failed to start utterance\n");

    ps_process_raw(ps, adbuf, k, FALSE, FALSE);
    //printf("Listening...\n");
    fflush(stdout);

    /* Note timestamp for this first block of data */
    ts = cont->read_ts;

    /* Decode utterance until end (marked by a "long" silence, >1sec) */
    for (;;) {

        /* Read non-silence audio data, if any, from continuous listening module */
        if ((k = cont_ad_read(cont, adbuf, 4096)) < 0)
            E_FATAL("Failed to read audio\n");
        if (k == 0) {
            /*
             * No speech data available; check current timestamp with most recent
             * speech to see if more than 1 sec elapsed.  If so, end of utterance.
             */
            if ((cont->read_ts - ts) > DEFAULT_SAMPLES_PER_SEC)
                break;
        }
        else {
            /* New speech data received; note current timestamp */
            ts = cont->read_ts;
        }

        /*
         * Decode whatever data was read above.
         */
        rem = ps_process_raw(ps, adbuf, k, FALSE, FALSE);

        /* If no work to be done, sleep a bit */
        if ((rem == 0) && (k == 0))
            sleep_msec(20);
    }

    /*
     * Utterance ended; flush any accumulated, unprocessed A/D data and stop
     * listening until current utterance completely decoded
     */
    ad_stop_rec(ad);
    while (ad_read(ad, adbuf, 4096) >= 0);
    cont_ad_reset(cont);
    fflush(stdout);
    /* Finish decoding, obtain and print result */
    ps_end_utt(ps);

    hyp = ps_get_hyp(ps, NULL, &uttid);
    fflush(stdout);

    /* Exit if the first word spoken was GOODBYE */
   //actually, for unity, exit if any word was spoken at all! this will avoid an infinite loop of doom!
    if (hyp) {
        /*sscanf(hyp, "%s", words);
        if (strcmp(word, "goodbye") == 0)*/
            break;
    }
   else
     return "nothing returned";
    /* Resume A/D recording for next utterance */
    if (ad_start_rec(ad) < 0)
        E_FATAL("Failed to start recording\n");
}
cont_ad_close(cont);
ad_close(ad);
ps_free(ps);
const char *temp = new char[1024];
temp = MakeStringCopy(hyp);
return temp;}

如果改变返回温度;返回“这里有一些字符串”;然后我看到文本出现在 Unity 中。不过,这没有用,因为我不需要硬编码文本,我需要语音识别代码的输出,它最终存储在 hyp 变量中。

谁能帮我弄清楚我做错了什么?谢谢!

最佳答案

问题是您不应该以这种方式在 C++ 中分配原始内存并在 C# 中使用它,谁将摆脱您在函数 MakeStringCopy 中分配的内存?

尝试这样的事情:

[DllImport("MyLibrary.dll")]
[return: MarshalAs(UnmanagedType.LPStr)] 
public static extern string GetStringValue();

通过这种方式,您告诉编码(marshal)拆收器 CLR 拥有因调用该函数而产生的内存,并且它将负责取消分配。

此外,.Net strings contain unicode chars ,这就是为什么在尝试为其分配 ANSI 字符时出现访问冲突错误的原因。使用属性 UnmanagedType.LPStr 还可以告诉编码(marshal)拆收器它应该期望的字符类型,以便它可以为您进行转换。

最后,根据this sample in MSDN,对于C++端的内存分配您应该在函数 MakeStringCopy 中使用函数 CoTaskMemAlloc 而不是 malloc

关于c# - 如何从用 C++ 编写的外部 dll 中获取 const char * 到 C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13022527/

相关文章:

c# - 在 Web 或辅助角色之外引用 Microsoft.WindowsAzure.ServiceRuntime

c# - T4模板多次处理输入

c# - 有没有办法找到特定层中的每个 RigidBody?

c# 7.0 : switch on System. 类型

c++ - STM32F103 微 Controller CAN 消息

c++ - 正则表达式捕获重复组

java - 可执行 jar 文件不会通过双击加载 dll 但它会运行

c# - 确保连接

c++ - 访问私有(private)类或 protected 类的构造函数?

c++ - 即使未包装在 extern "C"中,也会调用 DllMain() 吗?