我正在尝试将从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
答案 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.lib
,sicuio.lib
,sicudt.lib
,sicuin.lib
相关联。)
现在u_fgets()
不再导致访问冲突。当然,现在我的.exe
差不多大了23 MB。
答案 1 :(得分:4)
问题是“icuio51.dll”(发布)与静态CRT 相关联!因此它不与共享CRT共享相同的 FILE指针!这就是为什么它在“锁定”中崩溃的原因......
另一方面:'icuio51d.dll'(Debug)与相同的共享CRT (msvcr110d.dll)链接,因此正在使用相同的共享文件*指针。
这是它在“调试”中工作但不在“发布”中的原因。
解决方案:您必须使用正确的设置重新编译ICU,以便始终使用“共享CRT”(/ MD和/ MDd)... 为此,您必须执行以下步骤:
答案 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}}函数,替换两个不同的文件以及调试和静态库。
它将显示文件结构的字节,也许我们将学习有关问题所在的有用信息。