ICU u_fgetfile与VS2012的发布版本中的运行时不兼容

时间:2013-07-08 21:05:17

标签: c visual-studio-2012 icu visual-c++

我正在尝试将从u_fgetfile返回的句柄传递给fseek / fread函数。

将我的应用程序与调试运行时库(/ MTd / MDd)链接时没有崩溃,但如果我链接到静态版本,这个简单的代码崩溃了:

#include <stdio.h>
#include "unicode\ustdio.h"

int main()
{
    UFILE* file;
    file = u_fopen("C:\\test.txt","r",NULL,"UTF-8");
    fseek(u_fgetfile(file),3,SEEK_SET);
}

现在这种情况发生在ICU的官方版本和我使用Visual Studio 2012构建自定义版本时(在调试或发布中构建ICU并不重要)。

我唯一发现的是FILE结构中似乎存在一些不匹配,但我真的不知道。

编辑:

作为为此问题添加赏金的一部分,这是一个功能齐全的VS2012项目,其中包含重现程序(与上面发布的代码相同)和包含源代码和二进制文件的icu。在此处获取:http://goo.gl/urTuU

4 个答案:

答案 0 :(得分:8)

在我看来,问题是在_lock_file内,其中说:

    /*
     * The way the FILE (pointed to by pf) is locked depends on whether
     * it is part of _iob[] or not
     */
    if ( (pf >= _iob) && (pf <= (&_iob[_IOB_ENTRIES-1])) )
    {
        /*
         * FILE lies in _iob[] so the lock lies in _locktable[].
         */
        _lock( _STREAM_LOCKS + (int)(pf - _iob) );
        /* We set _IOLOCKED to indicate we locked the stream */
        pf->_flag |= _IOLOCKED;
    }
    else
        /*
         * Not part of _iob[]. Therefore, *pf is a _FILEX and the
         * lock field of the struct is an initialized critical
         * section.
         */
        EnterCriticalSection( &(((_FILEX *)pf)->lock) );

A&#34;正常&#34; FILE*将进入顶部分支,从u_fgetfile返回的指针将进入底部分支。这里假设它是_FILEX*,很可能根本不正确。

如我们所见,运行时进行比较以查看文件指针fb是否在_iob内。但是,在调试器中,我们可以清楚地看到它远远超出它(至少在发布版本中)。

鉴于u_fgetfile只返回存储在FILE*结构中的UFILE,我们可以检查finit_owner中的ufile.c以了解FILE* 1}}首先在我们的结构中结束。在阅读该代码之后,我必须假设在发布版本中,CRT中存在两个独立的_iob数组实例,但在调试版本中,只存在一个实例。

要解决此问题,您需要确保在与主应用程序相同的线程中创建FILE*。为此,您可以使用u_finit,如下所示:

FILE* filePointer = fopen("test.txt","r");
UFILE* file = u_finit(filePointer,NULL,"UTF-8");

fseek(filePointer,3,SEEK_SET); // <- won't crash

关于此之后出现的问题,在我看来,基本问题就是简单地在库之间共享FILE*,因为它们具有FILE*的单独存储区域而失败。我发现这有点令人困惑,但我对所涉及的组件没有必要的理解(并且Windows C运行时代码的样式也没有帮助)。

因此,如果在ICU中分配了FILE*,那么您就无法将其锁定在主应用程序中,反之亦然(尝试读取或搜索将始终涉及锁定)。

除非我对此问题有一个非常明显的解决方案,否则我建议您在主应用程序中模拟u_fgets()(或其他任何您需要的)的行为。
据我所知,u_fgets()只需调用fread()来读取文件中的数据,然后使用ucnv_toUnicode(),转换器存储在UFILE中(您可以使用u_fgetConverter()),将读取的数据转换为UChar*

似乎有效的一种方法是静态链接ICU。我不知道这是否适合你,但它似乎解决了我的问题。

我下载了最新版本的ICU(51.2)并使用this helpful script进行了编译。然后,我将项目与icu-release-static-win32-vs2012中的库相关联(与sicuuc.libsicuio.libsicudt.libsicuin.lib相关联。)

现在u_fgets()不再导致访问冲突。当然,现在我的.exe差不多大了23 MB。

答案 1 :(得分:4)

问题是“icuio51.dll”(发布)与静态CRT 相关联!因此它不与共享CRT共享相同的 FILE指针!这就是为什么它在“锁定”中崩溃的原因......

另一方面:'icuio51d.dll'(Debug)与相同的共享CRT (msvcr110d.dll)链接,因此正在使用相同的共享文件*指针。

这是它在“调试”中工作但不在“发布”中的原因。

解决方案:您必须使用正确的设置重新编译ICU,以便始终使用“共享CRT”(/ MD和/ MDd)... 为此,您必须执行以下步骤:

  1. 打开解决方案“allinone \ allinone.sln”
  2. 编辑项目“io”
  3. 的属性
  4. 将“C / C ++ |代码生成|运行时库”从“多线程(/ MT)”更改为“Win32”平台的“多线程DLL(/ MD)”(x64似乎是正确的!)< / LI>
  5. 重新编译您的项目

答案 2 :(得分:3)

您的问题是由于链接错误的库。

我使用官方ICU和VS2010完全反映了相反的结果 - 发布版本在Debug版本崩溃时有效。

问题是(恕我直言)将调试和发布VC运行时链接并在不同版本的函数之间传递FILE指针。 Release中fopen的结果传递给Debug中的fseek。它们在区分_iob和FILEX文件(不同的_IOB_ENTRIES)方面似乎不兼容。

将Debug项目与Release ICU链接(反之亦然)会导致此问题。

答案 3 :(得分:0)

第一个问题:是否可以编译和使用c ++ 11功能(因为那时我们将有更多工具来分析实际发生​​的情况。)

首先要尝试使用这种代码查看文件结构:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main()
{
    unsigned int size = sizeof(FILE);
    FILE* file = fopen("main.c", "r");
    unsigned int i = 0;
    unsigned char buffer[size];
    memcpy(buffer, file, size);
    printf("The %u bytes at address %p are: \n", size, file);
    for (i = 0; i < size; ++i) {
        printf("%02X ", (unsigned int)(buffer[i]));
        if ((i+1)%64 == 0) {
            printf("\n");
        }
    }
    printf("\n");
    fclose(file);
    return 0;
}

替换FILE的{​​{1}}和UFILE的{​​{1}}函数,替换两个不同的文件以及调试和静态库。

它将显示文件结构的字节,也许我们将学习有关问题所在的有用信息。