9

我正在尝试将从 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

4 回答 4

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) );

一个“正常”FILE*将进入顶部分支,从返回的指针u_fgetfile将进入底部分支。这里假设它是 a _FILEX*,这很可能根本不正确。

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

鉴于u_fgetfile只返回FILE*存储在UFILE结构中的 a,我们可以先检查一下finit_ownerufile.c看看FILE*最终在我们的结构中是如何结束的。阅读该代码后,我必须假设在发布版本_iob中,CRT 中存在两个单独的数组实例,但在调试版本中,仅存在一个实例。

为了解决这个问题,您需要确保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*在库之间共享 a ,这会失败,因为它们有单独的存储区域用于FILE*. 我觉得这有点令人困惑,但我对所涉及的组件没有必要的理解(而且 Windows C 运行时代码的风格也无济于事)。

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

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

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

我下载了最新版本的 ICU (51.2) 并用这个有用的脚本编译了它。icu-release-static-win32-vs2012然后,我将项目与(用sicuuc.lib, sicuio.lib, sicudt.lib,链接)中的库链接起来sicuin.lib

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

于 2013-07-16T00:57:01.330 回答
5

问题是“icuio51.dll”(Release)是针对静态 CRT链接的!因此它不与共享 CRT 共享同一个FILE 指针!这就是它在“锁定”中崩溃的原因......

另一方面:“icuio51d.dll”(调试)链接到相同的共享 CRT(msvcr110d.dll),因此使用相同的共享 FILE* 指针。

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

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

  1. 打开解决方案“allinone\allinone.sln”
  2. 编辑项目“io”的属性
  3. 将“Win32”平台的“C/C++|代码生成|运行时库”从“多线程 (/MT)”更改为“多线程 DLL (/MD)”(x64 似乎是正确的!)
  4. 重新编译你的项目
于 2013-07-18T08:40:20.320 回答
3

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

我已经用官方 ICU 和 VS2010 完全复制了相反的结果 - 发布构建工作,而调试构建崩溃。

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

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

于 2013-07-14T05:01:06.410 回答
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为你的UFILE和你的fopen函数。u_fopen

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

于 2013-07-12T20:29:35.823 回答