(转)FILE指针传递给DLL为何无法正确操作--隐式重复定义的陷阱

      FILE指针传递给DLL为何无法正确操作--隐式重复定义的陷阱 作者: Panic 2006年1月10日

  程序环境:

  win2k pro sp4

  vc6 sp5

  问题提出:(已经省略无关内容)

  主 题: 请教一个问题: 主程序里面打开一个文件,将文件指针传入dll中,在dll中对文件执行读写 回复根帖

  作 者: zill_li (书生)

  发表时间: 2006-1-9 21:33:20

  正文内容:

  int main(int argc, char* argv[])

  {

  FILE * stream;

  if( (stream=fopen("F://1.txt","w+")) !=NULL )

  {

  printf("fp1=%X/n", stream);

  testFile( stream );

  }

  return 0;

  }

  //dll中

  void testFile(FILE *fp)

  {

  int xx=1234;

  fread(&xx,sizeof(int),1,fp);

  }

  这样出错, 提示00000010地址不能为写!

  先解释几个基本概念:

  1,重复定义

  出现变量或者函数重复定义,VC6一般报这样一个错误:fatal error LNK1169: one or more multiply defined symbols found

  这个错误表明某个符号在工程中被定义了两份。这种情况一般发生在,某个变量在多个源文件(注:区别于头文件)出现定义。

  2,工程间源文件分享

  这种分享有两种方式,一种是通过.h文件单独分享,一般情况下,这种分享是安全的。

  另一种是通过.h + .lib,或者 .h + .c,.cpp共享,这时候就会发生所谓的“隐式重复定义”。

  观察问题中的结构,可以发现问题的起因就是FILE *指针的共享,那么在fopen,fwrite之类的函数中,到底对FILE *做了些什么呢?

  调试这个源代码,可以在

  _file.c

  文件中跟踪到如下代码:

  void __cdecl _lock_file (

  void *pf

  )

  {

  /*

  * The way the FILE (pointed to by pf) is locked depends on whether

  * it is part of _iob[] or not

  */

  if ( (pf >= (void *)_iob) && (pf <= (void *)(&_iob[_IOB_ENTRIES-1])) )

  /*

  * FILE lies in _iob[] so the lock lies in _locktable[].

  */

  _lock( _STREAM_LOCKS + ((FILE *)pf - _iob) );

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

  }

  这是在fread中调用到的一个函数,注意函数中的这句:

  if ( (pf >= (void *)_iob) && (pf <= (void *)(&_iob[_IOB_ENTRIES-1])) )

  这里和 pf 比较的变量 _iob 是唯一没有跟随FILE * (pf)一起传递的变量,那么问题是否就出在这里呢?

  在这个文件中,找到了 _iob的定义:

  FILE _iob[_IOB_ENTRIES] = {

  /* _ptr, _cnt, _base, _flag, _file, _charbuf, _bufsiz */

  /* stdin (_iob[0]) */

  { _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ },

  /* stdout (_iob[1]) */

  { NULL, 0, NULL, _IOWRT, 1, 0, 0 },

  /* stderr (_iob[3]) */

  { NULL, 0, NULL, _IOWRT, 2, 0, 0 },

  };

  这样一来,问题就清楚了。

  _file.c是一个公共源文件,工程exe和dll分别引用了这个文件。

  于是在exe中和dll中,这个变量_iob就存在了两份(隐式重复定义),而这个变量是影响相关函数执行结果的一个变量。(这个问题也会出现在使用lib作为公共源文件的场合)

  在exe中,调用fopen,fread等函数的时候,内部调用的是链接进exe文件的_iob,而dll的导出接口调用的,是链接进dll内部的同名变量。

  问题发生了,exe中的改变并不会影响dll中同名变量的数值,那么当在exe中想当然的认为正确的数值,到了dll中就面目全非了,结果必然是出错。

  解决这个问题的方法有三个:

  1,修改DLL,把所有相关的函数,包括fopen,fread,fwite等全部导出。

  这样做可以确保所有调用引用的_iob都指向DLL内部的版本,但是由于涉及的函数比较多,操作复杂,并且影响DLL外部接口的清晰。

  2,修改程序逻辑,把传递FILE * 改为传递自定义的文件缓冲区指针。

  这样可以完全避免问题,同时程序的接口也将更简单。

  3,修改_file.c及相关库的实现。

  这么作的好处是一劳永逸,并且也为其他库的使用者避免了麻烦(当然,用户还是需要更新库文件并且重新编译链接),只是这个恐怕需要库供应商的支持,并且是否可行还有待商榷。

  在目前的情况下,恐怕2是唯一可行的方案,而且不但如此,还需要在FILE *的使用说明上面,醒目的加一句:FILE *不允许在exe和DLL之间作为接口参数传递。

  查阅了msdn,并没有找到相关的描述。

  不过相信在其他库中,应该还存在其他的类似问题,这里友情提醒大家在编码的时候多加留意。

  补充:感谢小明的建议 可以把工程的C++ library设成(Debug) Multithread DLL版本就不会有这个问题了。

  原本只修改了工程的,所以问题依旧,现在两边都修改就没有问题了。

  可以把工程的C++ library设成(Debug) Multithread DLL版本就不会有这个问题了。

  事实证明,不行~:P

  这不是一个多线程问题。

  不行么?想不通阿,据我的经验这是明显的两份crt的问题,由于静态联编crt导致操作FILE的函数在实际运行时有exe中的和dll中的两份,又,众所周知crt依赖于一些全局的数据,两份crt必然有两份数据,比如FILE和HANDLE的对应关系表之类的,用其中一份打开一个文件,只能在一处留下记录,调用另一处的fread,这个FILE*肯定是非法的

  解决方法一楼小明说得有理,不是利用mt,而是要那个dll,就是动态联编crt,于是无论如何,进程中只会有一个msvcrt.dll,一些需要的全局数据也只有一份了

  类似的比如exe和dll传递c++对象的也可用类似方法解决

  此法敝人屡试不爽,不知为何这次不行,着实想不通

  不知楼主是否exe和dll都是动态联编crt的,仅仅一边动态联编也是不行的

  现在可以了,刚才dll的链接没有改。

  用CreateFile 等API函数也不会出错,当时我也测试过运行库的修改,不过我当时是把主函数工程改成(Debug) Multithread ,而dll里面的是用(Debug) Multithread DLL, 这样是不行的。原来都必须是(Debug) Multithread DLL才行

  多谢各位帮忙! 提前祝大家新春快乐。

  还是建议避免用类似的接口,毕竟不是所有的库都提供这么多运行库选项的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值