[#30549] [ANN] Ruby 1.8.6 has been released — "Akinori MUSHA" <knu@...>

 Ruby 1.8.6 をリリースしました。

14 messages 2007/03/12

[#30553] help: lib/shell for ruby 1.9 — keiju@... (Keiju ISHITSUKA)

けいじゅ@いしつかです.

13 messages 2007/03/13
[#30585] Re: help: lib/shell for ruby 1.9 — Yukihiro Matsumoto <matz@...> 2007/03/15

まつもと ゆきひろです

[#30587] Re: help: lib/shell for ruby 1.9 — keiju@... (石塚圭樹) 2007/03/15

けいじゅ@いしつかです.

[#30588] Re: help: lib/shell for ruby 1.9 — Yukihiro Matsumoto <matz@...> 2007/03/15

まつもと ゆきひろです

[ruby-dev:30670] Re: Unicode対応と文字化け(win32/registry)

From: moonwolf@...
Date: 2007-03-22 10:37:01 UTC
List: ruby-dev #30670
MoonWolfです。

Yukihiro Matsumoto <matz@ruby-lang.org>:
> |# 別件になりますがユニコード対応してないから文字化けがたまに起きますね。
> |# レジストリをダンプするプログラムを組もうとして嵌りました^^;
> 
> ま、Unicode対応しているとうたってる言語でも文字化けは発生し
> てるみたいですしね。で、今回はどのような経緯で文字化けしまし
> たか。同じような問題が今後発生しないような仕組みを考えるヒン
> トにしたいので、ぜひ教えてください。

Windowsのレジストリ内部は全てユニコードで記録されています。
現状のwin32/registryで呼び出しているANSI版のAPIでは
ANSI(CP932)書き込み→内部(UCS2)→ANSI(CP932)読み込みという風になります。
CP932しか使わないのであれば全く問題はありません。

今回発生した文字化けはレジストリのバックアップ/リストア処理で起きました。
レジストリを読み出す段階でCP932に無い文字は'?'に置き換えられてしまいます。
そのため、読み込んだ値をそのまま書き戻すということができませんでした。

とりあえず明示的にユニコードで読み書きするメソッドを追加してみました。
readのUTF-8版としてread_utf8、writeにはwrite_utf8というのを追加しています。

Attachments (1)

reg.patch (5.35 KB, text/x-diff)
Index: registry.rb
===================================================================
--- registry.rb	(revision 12112)
+++ registry.rb	(working copy)
@@ -330,6 +330,48 @@
     include Constants
     include Enumerable
     
+    # UTF-16 to UTF-8
+    def self::u16tou8(u16str)
+      u8 = ''
+      sur = nil
+      u16str.unpack("S*").each {|x|
+        if sur
+          if (0xdc00..0xdfff).include?(x)
+            sur = (x - 0xdc00) + 0x10000
+            u8 << [x].pack("U")
+            sur = nil
+          else
+            raise "Invalid UTF-16"
+          end
+        else
+          if (0xd800..0xdbff).include?(x)
+            sur = (x - 0xd800) << 10
+          else
+            u8 << [x].pack("U")
+          end
+        end
+      }
+      u8.chop
+    end
+    
+    # UTF-8 to UTF-16
+    def self::u8tou16(u8str)
+      ary = u8str.unpack("U*")
+      u16 =""
+      ary.each {|ucs|
+        case ucs
+        when 0..0xffff
+          u16 << [ucs].pack("S")
+        when 0x10000..0x10ffff
+          ucs -= 0x10000
+          h = (ucs >> 10) & 0x3ff + 0xd800
+          l = ucs & 0x3ff + 0xdc00
+          u16 << [h,l].pack("SS")
+        end
+      }
+      u16
+    end
+    
     #
     # Error
     #
@@ -382,6 +424,8 @@
         %w/RegEnumKeyExA    LLPPLLLP     L/,
         %w/RegQueryValueExA LPLPPP       L/,
         %w/RegSetValueExA   LPLLPL       L/,
+        %w/RegQueryValueExW LPLPPP       L/,
+        %w/RegSetValueExW   LPLLPL       L/,
         %w/RegDeleteValue   LP           L/,
         %w/RegDeleteKey     LP           L/,
         %w/RegFlushKey      L            L/,
@@ -453,10 +497,26 @@
         [ unpackdw(type), data[0, unpackdw(size)] ]
       end
       
+      def QueryValueW(hkey, name)
+        type = packdw(0)
+        size = packdw(0)
+        name = Win32::Registry::u8tou16(name)
+        check RegQueryValueExW.call(hkey, name, 0, type, 0, size)
+        data = '\x00\x00' * unpackdw(size)
+        check RegQueryValueExW.call(hkey, name, 0, type, data, size)
+        [ unpackdw(type), Win32::Registry::u16tou8(data[0, unpackdw(size)]) ]
+      end
+      
       def SetValue(hkey, name, type, data, size)
         check RegSetValueExA.call(hkey, name, 0, type, data, size)
       end
       
+      def SetValueW(hkey, name, type, data, size)
+        name = Win32::Registry::u8tou16(name)
+        data = Win32::Registry::u8tou16(data)
+        check RegSetValueExW.call(hkey, name, 0, type, data, data.size)
+      end
+      
       def DeleteValue(hkey, name)
         check RegDeleteValue.call(hkey, name)
       end
@@ -511,11 +571,11 @@
     end
     
     def self.wtime2time(wtime)
-      Time.at((wtime - 116444736000000000) / 10000000)
+      Time.at((wtime - 116444736000000000).quo(10000000))
     end
     
     def self.time2wtime(time)
-      time.to_i * 10000000 + 116444736000000000
+      time.to_i * 10000000 + time.usec * 10 + 116444736000000000
     end
     
     #
@@ -687,6 +747,21 @@
       end
     end
     
+    def read_utf8(name, *rtype)
+      type, data = API.QueryValueW(@hkey, name)
+      unless rtype.empty? or rtype.include?(type)
+        raise TypeError, "Type mismatch (expect #{rtype.inspect} but #{type} present)"
+      end
+      case type
+      when REG_SZ, REG_EXPAND_SZ
+        [ type, data ]
+      when REG_MULTI_SZ
+        [ type, data.split(/\0/) ]
+      else
+        read(name, *rtype)
+      end
+    end
+    
     def [](name, *rtype)
       type, data = read(name, *rtype)
       case type
@@ -702,6 +777,14 @@
     def read_s(name)
       read(name, REG_SZ)[1]
     end
+
+    def read_s_utf8(name)
+      type, data = API.QueryValueW(@hkey, name)
+      if type != REG_SZ
+        raise TypeError, "Type mismatch (#{type} present)"
+      end
+      data
+    end
     
     def read_s_expand(name)
       type, data = read(name, REG_SZ, REG_EXPAND_SZ)
@@ -712,6 +795,15 @@
       end
     end
     
+    def read_s_expand_utf8(name)
+      type, data = API.QueryValueW(@hkey, name)
+      if type == REG_EXPAND_SZ
+        Registry.expand_environ(data)
+      else
+        data
+      end
+    end
+    
     def read_i(name)
       read(name, REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD)[1]
     end
@@ -742,7 +834,30 @@
       end
       API.SetValue(@hkey, name, type, data, data.length)
     end
-    
+
+    def write_utf8(name, type, data)
+      case type
+      when REG_SZ, REG_EXPAND_SZ
+        API.SetValueW(@hkey, name, type, data, data.length)
+      when REG_MULTI_SZ
+        API.SetValueW(@hkey, name, type, data, data.length)
+      when REG_BINARY
+        data = data.to_s
+        API.SetValue(@hkey, name, type, data, data.length)
+      when REG_DWORD
+        data = API.packdw(data.to_i)
+        API.SetValue(@hkey, name, type, data, data.length)
+      when REG_DWORD_BIG_ENDIAN
+        data = [data.to_i].pack('N')
+        API.SetValue(@hkey, name, type, data, data.length)
+      when REG_QWORD
+        data = API.packqw(data.to_i)
+        API.SetValue(@hkey, name, type, data, data.length)
+      else
+        raise TypeError, "Unsupported type #{type}"
+      end
+    end
+
     def []=(name, rtype, value = nil)
       if value
         write name, rtype, value
@@ -765,6 +880,10 @@
       write name, REG_SZ, value.to_s
     end
     
+    def write_s_utf8(name, value)
+      write_utf8 name, REG_SZ, value.to_s
+    end
+    
     def write_i(name, value)
       write name, REG_DWORD, value.to_i
     end

In This Thread

Prev Next