text: #include "win32ole.h"
  
  struct oletypelibdata {
      ITypeLib *pTypeLib;
  };
  
  static VALUE reg_get_typelib_file_path(HKEY hkey);
  static VALUE oletypelib_path(VALUE guid, VALUE version);
  static HRESULT oletypelib_from_guid(VALUE guid, VALUE version, ITypeLib **ppTypeLib);
  static VALUE foletypelib_s_typelibs(VALUE self);
  static VALUE oletypelib_set_member(VALUE self, ITypeLib *pTypeLib);
  static void oletypelib_free(void *ptr);
  static size_t oletypelib_size(const void *ptr);
  static VALUE foletypelib_s_allocate(VALUE klass);
  static VALUE oletypelib_search_registry(VALUE self, VALUE typelib);
  static void oletypelib_get_libattr(ITypeLib *pTypeLib, TLIBATTR **ppTLibAttr);
  static VALUE oletypelib_search_registry2(VALUE self, VALUE args);
  static VALUE foletypelib_initialize(VALUE self, VALUE args);
  static VALUE foletypelib_guid(VALUE self);
  static VALUE foletypelib_name(VALUE self);
  static VALUE make_version_str(VALUE major, VALUE minor);
  static VALUE foletypelib_version(VALUE self);
  static VALUE foletypelib_major_version(VALUE self);
  static VALUE foletypelib_minor_version(VALUE self);
  static VALUE foletypelib_path(VALUE self);
  static VALUE foletypelib_visible(VALUE self);
  static VALUE foletypelib_library_name(VALUE self);
  static VALUE ole_types_from_typelib(ITypeLib *pTypeLib, VALUE classes);
  static VALUE typelib_file_from_typelib(VALUE ole);
  static VALUE typelib_file_from_clsid(VALUE ole);
  static VALUE foletypelib_ole_types(VALUE self);
  static VALUE foletypelib_inspect(VALUE self);
  
  static const rb_data_type_t oletypelib_datatype = {
      "win32ole_typelib",
      {NULL, oletypelib_free, oletypelib_size,},
      0, 0, RUBY_TYPED_FREE_IMMEDIATELY
  };
  
  static VALUE
  reg_get_typelib_file_path(HKEY hkey)
  {
      VALUE path = Qnil;
      path = reg_get_val2(hkey, "win64");
      if (path != Qnil) {
          return path;
      }
      path = reg_get_val2(hkey, "win32");
      if (path != Qnil) {
          return path;
      }
      path = reg_get_val2(hkey, "win16");
      return path;
  }
  
  static VALUE
  oletypelib_path(VALUE guid, VALUE version)
  {
      int k;
      LONG err;
      HKEY hkey;
      HKEY hlang;
      VALUE lang;
      VALUE path = Qnil;
  
      VALUE key = rb_str_new2("TypeLib\\");
      rb_str_concat(key, guid);
      rb_str_cat2(key, "\\");
      rb_str_concat(key, version);
  
      err = reg_open_vkey(HKEY_CLASSES_ROOT, key, &hkey);
      if (err != ERROR_SUCCESS) {
          return Qnil;
      }
      for(k = 0; path == Qnil; k++) {
          lang = reg_enum_key(hkey, k);
          if (lang == Qnil)
              break;
          err = reg_open_vkey(hkey, lang, &hlang);
          if (err == ERROR_SUCCESS) {
              path = reg_get_typelib_file_path(hlang);
              RegCloseKey(hlang);
          }
      }
      RegCloseKey(hkey);
      return path;
  }
  
  static HRESULT
  oletypelib_from_guid(VALUE guid, VALUE version, ITypeLib **ppTypeLib)
  {
      VALUE path;
      OLECHAR *pBuf;
      HRESULT hr;
      path = oletypelib_path(guid, version);
      if (path == Qnil) {
          return E_UNEXPECTED;
      }
      pBuf = ole_vstr2wc(path);
      hr = LoadTypeLibEx(pBuf, REGKIND_NONE, ppTypeLib);
      SysFreeString(pBuf);
      return hr;
  }
  
  ITypeLib *
  itypelib(VALUE self)
  {
      struct oletypelibdata *ptlib;
      TypedData_Get_Struct(self, struct oletypelibdata, &oletypelib_datatype, ptlib);
      return ptlib->pTypeLib;
  }
  
  VALUE
  ole_typelib_from_itypeinfo(ITypeInfo *pTypeInfo)
  {
      HRESULT hr;
      ITypeLib *pTypeLib;
      unsigned int index;
      VALUE retval = Qnil;
  
      hr = pTypeInfo->lpVtbl->GetContainingTypeLib(pTypeInfo, &pTypeLib, &index);
      if(FAILED(hr)) {
          return Qnil;
      }
      retval = create_win32ole_typelib(pTypeLib);
      return retval;
  }
  
  /*
   * Document-class: WIN32OLE_TYPELIB
   *
   *   <code>WIN32OLE_TYPELIB</code> objects represent OLE tyblib information.
   */
  
  /*
   *  call-seq:
   *
   *     WIN32OLE_TYPELIB.typelibs
   *
   *  Returns the array of WIN32OLE_TYPELIB object.
   *
   *     tlibs = WIN32OLE_TYPELIB.typelibs
   *
   */
  static VALUE
  foletypelib_s_typelibs(VALUE self)
  {
      HKEY htypelib, hguid;
      DWORD i, j;
      LONG err;
      VALUE guid;
      VALUE version;
      VALUE name = Qnil;
      VALUE typelibs = rb_ary_new();
      VALUE typelib = Qnil;
      HRESULT hr;
      ITypeLib *pTypeLib;
  
      err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib);
      if(err != ERROR_SUCCESS) {
          return typelibs;
      }
      for(i = 0; ; i++) {
          guid = reg_enum_key(htypelib, i);
          if (guid == Qnil)
              break;
          err = reg_open_vkey(htypelib, guid, &hguid);
          if (err != ERROR_SUCCESS)
              continue;
          for(j = 0; ; j++) {
              version = reg_enum_key(hguid, j);
              if (version == Qnil)
                  break;
              if ( (name = reg_get_val2(hguid, StringValuePtr(version))) != Qnil ) {
                  hr = oletypelib_from_guid(guid, version, &pTypeLib);
                  if (SUCCEEDED(hr)) {
                      typelib = create_win32ole_typelib(pTypeLib);
                      rb_ary_push(typelibs, typelib);
                  }
              }
          }
          RegCloseKey(hguid);
      }
      RegCloseKey(htypelib);
      return typelibs;
  }
  
  static VALUE
  oletypelib_set_member(VALUE self, ITypeLib *pTypeLib)
  {
      struct oletypelibdata *ptlib;
      TypedData_Get_Struct(self, struct oletypelibdata, &oletypelib_datatype, ptlib);
      ptlib->pTypeLib = pTypeLib;
      return self;
  }
  
  static void
  oletypelib_free(void *ptr)
  {
      struct oletypelibdata *poletypelib = ptr;
      OLE_FREE(poletypelib->pTypeLib);
      free(poletypelib);
  }
  
  static size_t
  oletypelib_size(const void *ptr)
  {
      return ptr ? sizeof(struct oletypelibdata) : 0;
  }
  
  static VALUE
  foletypelib_s_allocate(VALUE klass)
  {
      struct oletypelibdata *poletypelib;
      VALUE obj;
      ole_initialize();
      obj = TypedData_Make_Struct(klass, struct oletypelibdata, &oletypelib_datatype, poletypelib);
      poletypelib->pTypeLib = NULL;
      return obj;
  }
  
  VALUE
  create_win32ole_typelib(ITypeLib *pTypeLib)
  {
      VALUE obj = foletypelib_s_allocate(cWIN32OLE_TYPELIB);
      oletypelib_set_member(obj, pTypeLib);
      return obj;
  }
  
  static VALUE
  oletypelib_search_registry(VALUE self, VALUE typelib)
  {
      HKEY htypelib, hguid, hversion;
      DWORD i, j;
      LONG err;
      VALUE found = Qfalse;
      VALUE tlib;
      VALUE guid;
      VALUE ver;
      HRESULT hr;
      ITypeLib *pTypeLib;
  
      err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib);
      if(err != ERROR_SUCCESS) {
          return Qfalse;
      }
      for(i = 0; !found; i++) {
          guid = reg_enum_key(htypelib, i);
          if (guid == Qnil)
              break;
          err = reg_open_vkey(htypelib, guid, &hguid);
          if (err != ERROR_SUCCESS)
              continue;
          for(j = 0; found == Qfalse; j++) {
              ver = reg_enum_key(hguid, j);
              if (ver == Qnil)
                  break;
              err = reg_open_vkey(hguid, ver, &hversion);
              if (err != ERROR_SUCCESS)
                  continue;
              tlib = reg_get_val(hversion, NULL);
              if (tlib == Qnil) {
                  RegCloseKey(hversion);
                  continue;
              }
              if (rb_str_cmp(typelib, tlib) == 0) {
                  hr = oletypelib_from_guid(guid, ver, &pTypeLib);
                  if (SUCCEEDED(hr)) {
                      oletypelib_set_member(self, pTypeLib);
                      found = Qtrue;
                  }
              }
              RegCloseKey(hversion);
          }
          RegCloseKey(hguid);
      }
      RegCloseKey(htypelib);
      return  found;
  }
  
  static void
  oletypelib_get_libattr(ITypeLib *pTypeLib, TLIBATTR **ppTLibAttr)
  {
      HRESULT hr;
      hr = pTypeLib->lpVtbl->GetLibAttr(pTypeLib, ppTLibAttr);
      if (FAILED(hr)) {
          ole_raise(hr, eWIN32OLERuntimeError,
  		  "failed to get library attribute(TLIBATTR) from ITypeLib");
      }
  }
  
  static VALUE
  oletypelib_search_registry2(VALUE self, VALUE args)
  {
      HKEY htypelib, hguid, hversion;
      double fver;
      DWORD j;
      LONG err;
      VALUE found = Qfalse;
      VALUE tlib;
      VALUE ver;
      VALUE version_str;
      VALUE version = Qnil;
      VALUE typelib = Qnil;
      HRESULT hr;
      ITypeLib *pTypeLib;
  
      VALUE guid = rb_ary_entry(args, 0);
      version_str = make_version_str(rb_ary_entry(args, 1), rb_ary_entry(args, 2));
  
      err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib);
      if(err != ERROR_SUCCESS) {
          return Qfalse;
      }
      err = reg_open_vkey(htypelib, guid, &hguid);
      if (err != ERROR_SUCCESS) {
          RegCloseKey(htypelib);
          return Qfalse;
      }
      if (version_str != Qnil) {
          err = reg_open_vkey(hguid, version_str, &hversion);
          if (err == ERROR_SUCCESS) {
              tlib = reg_get_val(hversion, NULL);
              if (tlib != Qnil) {
                  typelib = tlib;
                  version = version_str;
              }
          }
          RegCloseKey(hversion);
      } else {
          fver = 0.0;
          for(j = 0; ;j++) {
              ver = reg_enum_key(hguid, j);
              if (ver == Qnil)
                  break;
              err = reg_open_vkey(hguid, ver, &hversion);
              if (err != ERROR_SUCCESS)
                  continue;
              tlib = reg_get_val(hversion, NULL);
              if (tlib == Qnil) {
                  RegCloseKey(hversion);
                  continue;
              }
              if (fver < atof(StringValuePtr(ver))) {
                  fver = atof(StringValuePtr(ver));
                  version = ver;
                  typelib = tlib;
              }
              RegCloseKey(hversion);
          }
      }
      RegCloseKey(hguid);
      RegCloseKey(htypelib);
      if (typelib != Qnil) {
          hr = oletypelib_from_guid(guid, version, &pTypeLib);
          if (SUCCEEDED(hr)) {
              found = Qtrue;
              oletypelib_set_member(self, pTypeLib);
          }
      }
      return found;
  }
  
  
  /*
   * call-seq:
   *    WIN32OLE_TYPELIB.new(typelib [, version1, version2]) -> WIN32OLE_TYPELIB object
   *
   * Returns a new WIN32OLE_TYPELIB object.
   *
   * The first argument <i>typelib</i>  specifies OLE type library name or GUID or
   * OLE library file.
   * The second argument is major version or version of the type library.
   * The third argument is minor version.
   * The second argument and third argument are optional.
   * If the first argument is type library name, then the second and third argument
   * are ignored.
   *
   *     tlib1 = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     tlib2 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}')
   *     tlib3 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}', 1.3)
   *     tlib4 = WIN32OLE_TYPELIB.new('{00020813-0000-0000-C000-000000000046}', 1, 3)
   *     tlib5 = WIN32OLE_TYPELIB.new("C:\\WINNT\\SYSTEM32\\SHELL32.DLL")
   *     puts tlib1.name  # -> 'Microsoft Excel 9.0 Object Library'
   *     puts tlib2.name  # -> 'Microsoft Excel 9.0 Object Library'
   *     puts tlib3.name  # -> 'Microsoft Excel 9.0 Object Library'
   *     puts tlib4.name  # -> 'Microsoft Excel 9.0 Object Library'
   *     puts tlib5.name  # -> 'Microsoft Shell Controls And Automation'
   *
   */
  static VALUE
  foletypelib_initialize(VALUE self, VALUE args)
  {
      VALUE found = Qfalse;
      VALUE typelib = Qnil;
      int len = 0;
      OLECHAR * pbuf;
      ITypeLib *pTypeLib;
      HRESULT hr = S_OK;
  
      len = RARRAY_LEN(args);
      rb_check_arity(len, 1, 3);
  
      typelib = rb_ary_entry(args, 0);
  
      SafeStringValue(typelib);
  
      found = oletypelib_search_registry(self, typelib);
      if (found == Qfalse) {
          found = oletypelib_search_registry2(self, args);
      }
      if (found == Qfalse) {
          pbuf = ole_vstr2wc(typelib);
          hr = LoadTypeLibEx(pbuf, REGKIND_NONE, &pTypeLib);
          SysFreeString(pbuf);
          if (SUCCEEDED(hr)) {
              found = Qtrue;
              oletypelib_set_member(self, pTypeLib);
          }
      }
  
      if (found == Qfalse) {
          rb_raise(eWIN32OLERuntimeError, "not found type library `%s`",
                   StringValuePtr(typelib));
      }
      return self;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#guid -> The guid string.
   *
   *  Returns guid string which specifies type library.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     guid = tlib.guid # -> '{00020813-0000-0000-C000-000000000046}'
   */
  static VALUE
  foletypelib_guid(VALUE self)
  {
      ITypeLib *pTypeLib;
      OLECHAR bstr[80];
      VALUE guid = Qnil;
      int len;
      TLIBATTR *pTLibAttr;
  
      pTypeLib = itypelib(self);
      oletypelib_get_libattr(pTypeLib, &pTLibAttr);
      len = StringFromGUID2(&pTLibAttr->guid, bstr, sizeof(bstr)/sizeof(OLECHAR));
      if (len > 3) {
          guid = ole_wc2vstr(bstr, FALSE);
      }
      pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
      return guid;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#name -> The type library name
   *
   *  Returns the type library name.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     name = tlib.name # -> 'Microsoft Excel 9.0 Object Library'
   */
  static VALUE
  foletypelib_name(VALUE self)
  {
      ITypeLib *pTypeLib;
      HRESULT hr;
      BSTR bstr;
      VALUE name;
      pTypeLib = itypelib(self);
      hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, -1,
                                              NULL, &bstr, NULL, NULL);
  
      if (FAILED(hr)) {
          ole_raise(hr, eWIN32OLERuntimeError, "failed to get name from ITypeLib");
      }
      name = WC2VSTR(bstr);
      return name;
  }
  
  static VALUE
  make_version_str(VALUE major, VALUE minor)
  {
      VALUE version_str = Qnil;
      VALUE minor_str = Qnil;
      if (major == Qnil) {
          return Qnil;
      }
      version_str = rb_String(major);
      if (minor != Qnil) {
          minor_str = rb_String(minor);
          rb_str_cat2(version_str, ".");
          rb_str_append(version_str, minor_str);
      }
      return version_str;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#version -> The type library version String object.
   *
   *  Returns the type library version.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     puts tlib.version #-> "1.3"
   */
  static VALUE
  foletypelib_version(VALUE self)
  {
      TLIBATTR *pTLibAttr;
      ITypeLib *pTypeLib;
      VALUE version;
  
      pTypeLib = itypelib(self);
      oletypelib_get_libattr(pTypeLib, &pTLibAttr);
      version = rb_sprintf("%d.%d", pTLibAttr->wMajorVerNum, pTLibAttr->wMinorVerNum);
      pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
      return version;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#major_version -> The type library major version.
   *
   *  Returns the type library major version.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     puts tlib.major_version # -> 1
   */
  static VALUE
  foletypelib_major_version(VALUE self)
  {
      TLIBATTR *pTLibAttr;
      VALUE major;
      ITypeLib *pTypeLib;
      pTypeLib = itypelib(self);
      oletypelib_get_libattr(pTypeLib, &pTLibAttr);
  
      major =  RB_INT2NUM(pTLibAttr->wMajorVerNum);
      pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
      return major;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#minor_version -> The type library minor version.
   *
   *  Returns the type library minor version.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     puts tlib.minor_version # -> 3
   */
  static VALUE
  foletypelib_minor_version(VALUE self)
  {
      TLIBATTR *pTLibAttr;
      VALUE minor;
      ITypeLib *pTypeLib;
      pTypeLib = itypelib(self);
      oletypelib_get_libattr(pTypeLib, &pTLibAttr);
      minor =  RB_INT2NUM(pTLibAttr->wMinorVerNum);
      pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
      return minor;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#path -> The type library file path.
   *
   *  Returns the type library file path.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     puts tlib.path #-> 'C:\...\EXCEL9.OLB'
   */
  static VALUE
  foletypelib_path(VALUE self)
  {
      TLIBATTR *pTLibAttr;
      HRESULT hr = S_OK;
      BSTR bstr;
      LCID lcid = cWIN32OLE_lcid;
      VALUE path;
      ITypeLib *pTypeLib;
  
      pTypeLib = itypelib(self);
      oletypelib_get_libattr(pTypeLib, &pTLibAttr);
      hr = QueryPathOfRegTypeLib(&pTLibAttr->guid,
  	                       pTLibAttr->wMajorVerNum,
  			       pTLibAttr->wMinorVerNum,
  			       lcid,
  			       &bstr);
      if (FAILED(hr)) {
  	pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
  	ole_raise(hr, eWIN32OLERuntimeError, "failed to QueryPathOfRegTypeTypeLib");
      }
  
      pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
      path = WC2VSTR(bstr);
      return path;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#visible?
   *
   *  Returns true if the type library information is not hidden.
   *  If wLibFlags of TLIBATTR is 0 or LIBFLAG_FRESTRICTED or LIBFLAG_FHIDDEN,
   *  the method returns false, otherwise, returns true.
   *  If the method fails to access the TLIBATTR information, then
   *  WIN32OLERuntimeError is raised.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     tlib.visible? # => true
   */
  static VALUE
  foletypelib_visible(VALUE self)
  {
      ITypeLib *pTypeLib = NULL;
      VALUE visible = Qtrue;
      TLIBATTR *pTLibAttr;
  
      pTypeLib = itypelib(self);
      oletypelib_get_libattr(pTypeLib, &pTLibAttr);
  
      if ((pTLibAttr->wLibFlags == 0) ||
          (pTLibAttr->wLibFlags & LIBFLAG_FRESTRICTED) ||
          (pTLibAttr->wLibFlags & LIBFLAG_FHIDDEN)) {
          visible = Qfalse;
      }
      pTypeLib->lpVtbl->ReleaseTLibAttr(pTypeLib, pTLibAttr);
      return visible;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#library_name
   *
   *  Returns library name.
   *  If the method fails to access library name, WIN32OLERuntimeError is raised.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     tlib.library_name # => Excel
   */
  static VALUE
  foletypelib_library_name(VALUE self)
  {
      HRESULT hr;
      ITypeLib *pTypeLib = NULL;
      VALUE libname = Qnil;
      BSTR bstr;
  
      pTypeLib = itypelib(self);
      hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, -1,
                                              &bstr, NULL, NULL, NULL);
      if (FAILED(hr)) {
          ole_raise(hr, eWIN32OLERuntimeError, "failed to get library name");
      }
      libname = WC2VSTR(bstr);
      return libname;
  }
  
  static VALUE
  ole_types_from_typelib(ITypeLib *pTypeLib, VALUE classes)
  {
      long count;
      int i;
      HRESULT hr;
      BSTR bstr;
      ITypeInfo *pTypeInfo;
      VALUE type;
  
      count = pTypeLib->lpVtbl->GetTypeInfoCount(pTypeLib);
      for (i = 0; i < count; i++) {
          hr = pTypeLib->lpVtbl->GetDocumentation(pTypeLib, i,
                                                  &bstr, NULL, NULL, NULL);
          if (FAILED(hr))
              continue;
  
          hr = pTypeLib->lpVtbl->GetTypeInfo(pTypeLib, i, &pTypeInfo);
          if (FAILED(hr))
              continue;
  
          type = create_win32ole_type(pTypeInfo, WC2VSTR(bstr));
  
          rb_ary_push(classes, type);
          OLE_RELEASE(pTypeInfo);
      }
      return classes;
  }
  
  static VALUE
  typelib_file_from_typelib(VALUE ole)
  {
      HKEY htypelib, hclsid, hversion, hlang;
      double fver;
      DWORD i, j, k;
      LONG err;
      BOOL found = FALSE;
      VALUE typelib;
      VALUE file = Qnil;
      VALUE clsid;
      VALUE ver;
      VALUE lang;
  
      err = reg_open_key(HKEY_CLASSES_ROOT, "TypeLib", &htypelib);
      if(err != ERROR_SUCCESS) {
          return Qnil;
      }
      for(i = 0; !found; i++) {
          clsid = reg_enum_key(htypelib, i);
          if (clsid == Qnil)
              break;
          err = reg_open_vkey(htypelib, clsid, &hclsid);
          if (err != ERROR_SUCCESS)
              continue;
          fver = 0;
          for(j = 0; !found; j++) {
              ver = reg_enum_key(hclsid, j);
              if (ver == Qnil)
                  break;
              err = reg_open_vkey(hclsid, ver, &hversion);
  			if (err != ERROR_SUCCESS || fver > atof(StringValuePtr(ver)))
                  continue;
              fver = atof(StringValuePtr(ver));
              typelib = reg_get_val(hversion, NULL);
              if (typelib == Qnil)
                  continue;
              if (rb_str_cmp(typelib, ole) == 0) {
                  for(k = 0; !found; k++) {
                      lang = reg_enum_key(hversion, k);
                      if (lang == Qnil)
                          break;
                      err = reg_open_vkey(hversion, lang, &hlang);
                      if (err == ERROR_SUCCESS) {
                          if ((file = reg_get_typelib_file_path(hlang)) != Qnil)
                              found = TRUE;
                          RegCloseKey(hlang);
                      }
                  }
              }
              RegCloseKey(hversion);
          }
          RegCloseKey(hclsid);
      }
      RegCloseKey(htypelib);
      return  file;
  }
  
  static VALUE
  typelib_file_from_clsid(VALUE ole)
  {
      HKEY hroot, hclsid;
      LONG err;
      VALUE typelib;
      char path[MAX_PATH + 1];
  
      err = reg_open_key(HKEY_CLASSES_ROOT, "CLSID", &hroot);
      if (err != ERROR_SUCCESS) {
          return Qnil;
      }
      err = reg_open_key(hroot, StringValuePtr(ole), &hclsid);
      if (err != ERROR_SUCCESS) {
          RegCloseKey(hroot);
          return Qnil;
      }
      typelib = reg_get_val2(hclsid, "InprocServer32");
      RegCloseKey(hroot);
      RegCloseKey(hclsid);
      if (typelib != Qnil) {
          ExpandEnvironmentStrings(StringValuePtr(typelib), path, sizeof(path));
          path[MAX_PATH] = '\0';
          typelib = rb_str_new2(path);
      }
      return typelib;
  }
  
  VALUE
  typelib_file(VALUE ole)
  {
      VALUE file = typelib_file_from_clsid(ole);
      if (file != Qnil) {
          return file;
      }
      return typelib_file_from_typelib(ole);
  }
  
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#ole_types -> The array of WIN32OLE_TYPE object included the type library.
   *
   *  Returns the type library file path.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     classes = tlib.ole_types.collect{|k| k.name} # -> ['AddIn', 'AddIns' ...]
   */
  static VALUE
  foletypelib_ole_types(VALUE self)
  {
      ITypeLib *pTypeLib = NULL;
      VALUE classes = rb_ary_new();
      pTypeLib = itypelib(self);
      ole_types_from_typelib(pTypeLib, classes);
      return classes;
  }
  
  /*
   *  call-seq:
   *     WIN32OLE_TYPELIB#inspect -> String
   *
   *  Returns the type library name with class name.
   *
   *     tlib = WIN32OLE_TYPELIB.new('Microsoft Excel 9.0 Object Library')
   *     tlib.inspect # => "<#WIN32OLE_TYPELIB:Microsoft Excel 9.0 Object Library>"
   */
  static VALUE
  foletypelib_inspect(VALUE self)
  {
      return default_inspect(self, "WIN32OLE_TYPELIB");
  }
  
  void
  Init_win32ole_typelib(void)
  {
      cWIN32OLE_TYPELIB = rb_define_class("WIN32OLE_TYPELIB", rb_cObject);
      rb_define_singleton_method(cWIN32OLE_TYPELIB, "typelibs", foletypelib_s_typelibs, 0);
      rb_define_alloc_func(cWIN32OLE_TYPELIB, foletypelib_s_allocate);
      rb_define_method(cWIN32OLE_TYPELIB, "initialize", foletypelib_initialize, -2);
      rb_define_method(cWIN32OLE_TYPELIB, "guid", foletypelib_guid, 0);
      rb_define_method(cWIN32OLE_TYPELIB, "name", foletypelib_name, 0);
      rb_define_method(cWIN32OLE_TYPELIB, "version", foletypelib_version, 0);
      rb_define_method(cWIN32OLE_TYPELIB, "major_version", foletypelib_major_version, 0);
      rb_define_method(cWIN32OLE_TYPELIB, "minor_version", foletypelib_minor_version, 0);
      rb_define_method(cWIN32OLE_TYPELIB, "path", foletypelib_path, 0);
      rb_define_method(cWIN32OLE_TYPELIB, "ole_types", foletypelib_ole_types, 0);
      rb_define_alias(cWIN32OLE_TYPELIB, "ole_classes", "ole_types");
      rb_define_method(cWIN32OLE_TYPELIB, "visible?", foletypelib_visible, 0);
      rb_define_method(cWIN32OLE_TYPELIB, "library_name", foletypelib_library_name, 0);
      rb_define_alias(cWIN32OLE_TYPELIB, "to_s", "name");
      rb_define_method(cWIN32OLE_TYPELIB, "inspect", foletypelib_inspect, 0);
  }
  