Bug #21498
openWindows - Ruby Overrides C Library APIs thus breaking them
Description
I am trying to wrap a simple C++ library, https://github.com/baderouaich/BitmapPlusPlus, as a Ruby extension.
However when I use the extension to write a bitmap to disk the bitmap is corrupted. This is because the library uses std::ofstream which eventually uses the C API fclose to write the final bytes to the bitmap file and then closes it. The problem is that Ruby overrides fclose
and replaces it with rb_w32_fclose
. It then exports its custom version from from x64-vcruntime140-ruby340.dll. And the exported version is broken (at least from the standpoint of the C standard library).
Note this has been a long standing issue. The first report I see is from 2013:
https://bugs.ruby-lang.org/issues/8569
More recently in 2020 (which explains the issue very well):
https://github.com/NREL/OpenStudio/issues/3942#issuecomment-610673401
I understand that Ruby is trying to provide a platform independent API. But the problem is this solution breaks any third party libraries that rely on these C APIs (which of course are very common). And there is no good workaround (see https://github.com/NREL/OpenStudio/issues/3942#issuecomment-611072774).
So would it be possible for Ruby to stop exporting custom versions of basic C APIs? The code that does it is here:
https://github.com/ruby/ruby/blob/master/win32/mkexports.rb#L41
Ruby of course could still use its custom versions of fclose
, read, write etc within ruby.exe and x64-vcruntime140-ruby340.dll. But they should not be exported from x64-vcruntime140-ruby340.dll and thus be off limits to extensions. If a case comes up where an extension really needs access to rb_w32_fclose instead of fclose then an extension developer can use an #ifdef _WIN32
to do so and work across platforms. That at least puts the developer in control versus now where I don't see any way I can wrap the bitmap library as a Ruby extension.
From my experience the biggest problem is the replacing of fclose with rb_w32_fclose.
This is the list of generated overrides:
FD_CLR=rb_w32_fdclr
FD_ISSET=rb_w32_fdisset
Sleep=rb_w32_Sleep
accept=rb_w32_accept
access=rb_w32_uaccess
bind=rb_w32_bind
close=rb_w32_close
connect=rb_w32_connect
dup2=rb_w32_dup2
fclose=rb_w32_fclose
fstat=rb_w32_fstati128
get_osfhandle=rb_w32_get_osfhandle
getcwd=rb_w32_ugetcwd
getenv=rb_w32_ugetenv
gethostbyaddr=rb_w32_gethostbyaddr
gethostbyname=rb_w32_gethostbyname
gethostname=rb_w32_gethostname
getpeername=rb_w32_getpeername
getpid=rb_w32_getpid
getppid=rb_w32_getppid
getprotobyname=rb_w32_getprotobyname
getprotobynumber=rb_w32_getprotobynumber
getservbyname=rb_w32_getservbyname
getservbyport=rb_w32_getservbyport
getsockname=rb_w32_getsockname
getsockopt=rb_w32_getsockopt
inet_ntop=rb_w32_inet_ntop
inet_pton=rb_w32_inet_pton
ioctlsocket=rb_w32_ioctlsocket
isatty=rb_w32_isatty
listen=rb_w32_listen
lseek=rb_w32_lseek
lstat=rb_w32_ulstati128
mkdir=rb_w32_umkdir
mmap=rb_w32_mmap
mprotect=rb_w32_mprotect
munmap=rb_w32_munmap
pipe=rb_w32_pipe
pread=rb_w32_pread
pwrite=rb_w32_pwrite
read=rb_w32_read
recv=rb_w32_recv
recvfrom=rb_w32_recvfrom
rename=rb_w32_urename
rmdir=rb_w32_urmdir
select=rb_w32_select
send=rb_w32_send
sendto=rb_w32_sendto
setsockopt=rb_w32_setsockopt
shutdown=rb_w32_shutdown
socket=rb_w32_socket
stati128=rb_w32_ustati128
strcasecmp=msvcrt.stricmp
strerror=rb_w32_strerror
strncasecmp=msvcrt.strnicmp
times=rb_w32_times
unlink=rb_w32_uunlink
utime=rb_w32_uutime
utimensat=rb_w32_uutimensat
utimes=rb_w32_uutimes
write=rb_w32_write