0

I am working on refactoring a large amount of code from an unmanaged C++ assembly into a C# assembly. There is currently a mixed-mode assembly going between the two with, of course, a mix of managed and unmanaged code. There is a function I am trying to call in the unmanaged C++ which relies on FILE*s (as defined in stdio.h). This function ties into a much larger process which cannot be refactored into the C# code yet, but which now needs to be called from the managed code.

I have searched but cannot find a definitive answer to what kind of underlying system pointer the System::IO::FileStream class uses. Is this just applied on top of a FILE*? Or is there some other way to convert a FileStream^ to a FILE*? I found FileStream::SafeFileHandle, on which I can call DangerousGetHandle().ToPointer() to get a native void*, but I'm just trying to be certain that if I cast this to FILE* that I'm doing the right thing...?

void Write(FILE *out)
{
    Data->Write(out); // huge bulk of code, writing the data
}

virtual void __clrcall Write(System::IO::FileStream ^out)
{
    // is this right??
    FILE *pout = (FILE*)out->SafeFileHandle->DangerousGetHandle().ToPointer();
    Write(pout);
}
Deduplicator
  • 44,692
  • 7
  • 66
  • 118
monkey0506
  • 2,489
  • 1
  • 21
  • 27
  • Looks like your best bet. There doesn't seem to be any guarantee the "OS file handle" is actually a FILE* though. But if it is, it seems unlikely to change. You're probably going to have to prevent the handle from being released while in native code, via a GC.KeepAlive on the out stream. – heinrichj Sep 30 '14 at 06:52
  • Does that atually make any difference? If the data is large, I'd have expected fairly little overhead... – Mats Petersson Sep 30 '14 at 07:01
  • 2
    No, FILE is a strict C runtime abstraction, in no way related to a Windows handle. You can hack this with _fdopen() for 32-bit code. – Hans Passant Sep 30 '14 at 09:40
  • @MatsPetersson the issue isn't the speed of the code, it's the amount of it. There are probably well over a thousand lines (just for writing this particular data file) of C++ code that needs to be refactored into C# (moving away from a mixed-mode assembly into a pure C# one). Writing the file is too big to refactor yet, but I am refactoring other parts of the class which means I need to be able to call the native, unmanaged Write method from the managed code. – monkey0506 Sep 30 '14 at 17:53

1 Answers1

1

You'll need _open_osfhandle followed by _fdopen.

Casting is not magic. Just because the input and types output are right for your situation doesn't mean the values are.

Community
  • 1
  • 1
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • I definitely understand that casting is NOT magic, but I haven't done much Windows-specific programming in C++. Whether or not the underlying pointer was of the FILE type was part of my question. Thanks for the clarification! – monkey0506 Oct 01 '14 at 02:30
  • Well, even if `HANDLE` did _wrap_ a `FILE*`, that pointer probably wouldn't be retrievable by a cast. – MSalters Oct 01 '14 at 07:18
  • The point being that I didn't even realize that `HANDLE` was a type unto itself. – monkey0506 Oct 01 '14 at 07:23
  • Even with a valid HANDLE, _open_osfhandle seems to always return -1. `virtual void __clrcall Write(FileStream ^out) { if (!out->CanWrite) {} /* throw */ SafeFileHandle^ safeHandle = out->SafeFileHandle; if (safeHandle->IsInvalid) {} /* throw */ if (safeHandle->IsClosed) {} /* throw */ void *handle = safeHandle->DangerousGetHandle().ToPointer(); int fd = _open_osfhandle((intptr_t)handle, _O_APPEND | _O_RDONLY); if (fd == -1) {} /* throw */ FILE *fp = _fdopen(fd, "a+"); if (fp == NULL) {} /* throw */ Write(fp); System::GC::KeepAlive(out); }` – monkey0506 Oct 12 '14 at 06:14