From: "cfis (Charlie Savage) via ruby-core" Date: 2025-11-18T05:51:09+00:00 Subject: [ruby-core:123843] [Ruby Bug#21498] Windows - Ruby Overrides C Library APIs thus breaking them Issue #21498 has been updated by cfis (Charlie Savage). Hi @YO4 - thank you for your reply. I absolutely agree a cooperative approach is best. I am saddened / quite surprised to hear that you think I have criticized someone. I certainly had no intention to do that. If I did then I apologize. I have used Ruby on Windows for almost 20 years now and would like to help make Ruby even better. In that spirit, let me move onto your technical questions. I agree with your root cause statement. The global shared resources are global per UCRT instance. They are safe as long as objects don't pass between different UCRTs. Can I guarantee removing the overrides will fix all problems - no. But it does at least fix the problems introduced by the UCRT functions that are overridden. I am guessing the reason to export these overridden functions from the Ruby library (versus just keeping them internal) is to use them in Ruby extensions. Thus if a UCRT object, like a file handle, is created by the Ruby executable then an extension could use it. But does that actually happen? Wouldn't a Ruby extension instead use the offical rb_* APIs thus avoiding any issues? One idea - I can make a debug build of Ruby and a release build of my extension to see if the debug version of UCRT recognizes the file handle from fclose is from a different UCRT version. If it can, then we could run the Ruby test suite with a debug build of Ruby and release builds of built-in extensions and see what errors get generated. Not sure this will work, but I will give it a try. I that works, then perhaps we can feel confident this change won't break anything. I do think removing the overrides is the right thing to do, especially if they are not actually needed (I am encouraged that the github tests passed, that at least seems to indicate they are not). I do agree that having a runtime check as an additional safety measure is a good idea too. I prefer is this is not the user's problem though. This is a difficult problem to recognize (excellent work that you did), and the user doesn't have a lot of recourse but go rebuild everything themselves which is much to hard for the average user. ---------------------------------------- Bug #21498: Windows - Ruby Overrides C Library APIs thus breaking them https://bugs.ruby-lang.org/issues/21498#change-115242 * Author: cfis (Charlie Savage) * Status: Open * Assignee: windows * ruby -v: *all* on windows (testing with ruby 3.4.3 (2025-04-14 revision d0b7e5b6a0) +PRISM [x64-mswin64_140]) * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- 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 ---Files-------------------------------- Screenshot 2025-11-06 233918.png (223 KB) primitives.bmp (360 KB) primitives_bad.bmp (360 KB) -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/