Sign in to follow this  
Lutz

fgets crashing in a managed/unmanaged environment

Recommended Posts

Lutz    462
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

Share this post


Link to post
Share on other sites
Washu    7829
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?

Share this post


Link to post
Share on other sites
Codeka    1239
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).

Share this post


Link to post
Share on other sites
Lutz    462
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?

Share this post


Link to post
Share on other sites
Codeka    1239
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.

Share this post


Link to post
Share on other sites
Lutz    462
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...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this