From: "cfis (Charlie Savage) via ruby-core" Date: 2025-11-07T05:05:03+00:00 Subject: [ruby-core:123714] [Ruby Bug#21498] Windows - Ruby Overrides C Library APIs thus breaking them Issue #21498 has been updated by cfis (Charlie Savage). The universal crt was introduced by Microsoft in 2015 (https://learn.microsoft.com/en-us/cpp/porting/upgrade-your-code-to-the-universal-crt?view=msvc-170) and Ruby for Windows has used it since Ruby 3.1 in 2021 (https://rubyinstaller.org/2021/12/31/rubyinstaller-3.1.0-1-released.html). According to Microsoft it "conforms closely to the ISO C99 standard". At this point in time, could Ruby drop some of its overrides on Windows. The UCRT function list is here: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/crt-alphabetical-function-reference?view=msvc-170 The ones the UCRT implements that Ruby overrides are: access=rb_w32_uaccess close=rb_w32_close dup2=rb_w32_dup2 fclose=rb_w32_fclose fstat=rb_w32_fstati128 (_fstat) get_osfhandle=rb_w32_get_osfhandle (_get_osfhandle) getpid=rb_w32_getpid isatty=rb_w32_isatty lseek=rb_w32_lseek mkdir=rb_w32_umkdir pipe=rb_w32_pipe (_pipe) read=rb_w32_read rename=rb_w32_urename rmdir=rb_w32_urmdir strerror=rb_w32_strerror unlink=rb_w32_uunlink utime=rb_w32_uutime (_utime) write=rb_w32_write ---------------------------------------- Bug #21498: Windows - Ruby Overrides C Library APIs thus breaking them https://bugs.ruby-lang.org/issues/21498#change-115102 * 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 -- 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/