fgets crashing in a managed/unmanaged environment

Started by
4 comments, last by Lutz 14 years, 2 months ago
Hey, I have C# code calling a managed C++ dll, which in turn calls an unmanaged C++ static library linked into the managed C++ dll, all compiled with VS 2008 on Windows. I'm calling the following simple code from the unmanaged C++ part
FILE* pf = fopen("text.txt", "rt");
fgets(buf, sizeof(buf) - 1, pf);
and get an exception in _lock_file (_file.c):
if ( (pf >= _iob) && (pf <= (&_iob[_IOB_ENTRIES-1])) )
{
  _lock( _STREAM_LOCKS + (int)(pf - _iob) );
  pf->_flag |= _IOLOCKED;
}
else
  EnterCriticalSection( &(((_FILEX *)pf)->lock) );
where pf->lock is NULL. I've made sure that the text.txt exists, the buffer is valid and everything, that's definitely not the problem. What I understand so far is that in different versions of the CRT Microsoft used two different ways to store locks: In some external array called _iob or directly inside the FILE struct casting FILE to a _FILEX struct containing the actual FILE* and a lock (see here). What appears to be happening is that fopen and fgets use different versions of the _iob array. fopen creates pf by using an _iob array starting at 0x1..., but fgets uses some _iob at 0x7..., so the "else" code path is taken causing the crash. Now I can understand the problem when you're calling fopen from one version of the CRT and pass the FILE* into a function compiled against another version of the CRT. That appears to be the problem that most people have. But I'm not doing that. I'm calling fopen and in the next line fgets, so how could those two lines use two different CRT versions? Even more odd, when I replace fgets with fread, it works! Stepping through the code, it also goes into _lock_file, but has the correct _iob array starting at 0x1... I'm really puzzled here. The project structure is pretty complex and I can't give all the details. But has anyone ever experienced a similar problem? I had similar issues with std::string where the default constructor would crash. I'm assuming that some project settings might be weird, but I don't know where to start. Any help appreciated. - Lutz
Advertisement
Hard to say, but a couple of things:
Of what type is buf? I'm assuming it's a stack allocated char array (based on your usage of sizeof, which would be completely incorrect if it's a pointer type).

Why aren't you validating that the file was opened properly?

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Are you linking to the DLL version of the CRT? In both the managed C++ DLL and the static library (you should get warnings during linking if you're not, but it's best to check the settings manually).
Quote:Original post by Washu
Hard to say, but a couple of things:
Of what type is buf? I'm assuming it's a stack allocated char array (based on your usage of sizeof, which would be completely incorrect if it's a pointer type).

Why aren't you validating that the file was opened properly?

Buf is char buf[512]. This is only example code. I'm validating pf in my "real" code, that's not the problem. pf is valid. I've looked into it and fread works.

Quote:Original post by Codeka
Are you linking to the DLL version of the CRT? In both the managed C++ DLL and the static library (you should get warnings during linking if you're not, but it's best to check the settings manually).

I found msvcrtd.lib in the additional dependencies of the managed C++ dll. Is that bad? Oh yeah, I should probably mention that it works in release (where I link against msvcrt.lib (without the d)). The static C++ library does not have additional dependencies. From my understanding of how the linker works, it is linked when the managed C++ DLL is linked, right, so wouldn't it use the same version of the CRT? In particular, why would fopen and fgets use a different _iob array?
Quote:Original post by Lutz
Quote:Original post by Codeka
Are you linking to the DLL version of the CRT? In both the managed C++ DLL and the static library (you should get warnings during linking if you're not, but it's best to check the settings manually).

I found msvcrtd.lib in the additional dependencies of the managed C++ dll. Is that bad? Oh yeah, I should probably mention that it works in release (where I link against msvcrt.lib (without the d)). The static C++ library does not have additional dependencies. From my understanding of how the linker works, it is linked when the managed C++ DLL is linked, right, so wouldn't it use the same version of the CRT? In particular, why would fopen and fgets use a different _iob array?
You shouldn't need to specify dependencies manually at all (i.e. remove the "msvcrtd.lib" from the "Additional Dependencies" option). If you get compiler errors after doing that, then it probably means you need to set up the "Runtime Library" option properly.

I don't know why exactly this can be a problem, but messing with the version of the CRT it links against (and choosing a different version between libraries and trying to "force" it by manually specifying things in Additional Dependencies) has always been a bit of a recipe for disaster in my experience.
Just noticed that I get the "warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use /NODEFAULTLIB:library" when I rebuild, so you are apparently very right. Compiling without the library seems to work, but we're using cmake here which generates the solution files, so cmake puts them in and I'm not familiar with the system and the guy who is left. Oh joy...

This topic is closed to new replies.

Advertisement