Class: Winreg::Key
- Inherits:
-
Object
- Object
- Winreg::Key
- Defined in:
- lib/winreg.rb,
ext/winreg/winreg.c
Overview
An open registry key. Instances come only from Winreg.open/.create and Key#open/#create (no public .new). All methods raise Winreg::Closed on a closed key except close/closed?/path/view/created?/inspect.
Defined Under Namespace
Classes: Info
Instance Attribute Summary collapse
-
#path ⇒ Object
readonly
Canonical full path, long-form root ("HKEY_CURRENT_USER\Software\X").
-
#view ⇒ Object
readonly
:default | :v64 | :v32.
Class Method Summary collapse
-
._create(vroot, vsub, vsam) ⇒ Object
Key._create(root, subpath, sam) — private singleton.
-
._open(vroot, vsub, vsam) ⇒ Object
Key._open(root, subpath_or_nil, sam) — private singleton.
Instance Method Summary collapse
- #binary(name) ⇒ Object
- #binary?(name) ⇒ Boolean
- #close ⇒ Object
- #closed? ⇒ Boolean
-
#create(subpath, access: :read_write) ⇒ Object
Child open-or-create, same view rules.
- #created? ⇒ Boolean
-
#delete_key(name, recursive: false) ⇒ Object
Delete the named child subkey (handle-relative; the view is applied via RegDeleteKeyExW samDesired).
-
#delete_value(name) ⇒ Object
Delete a value (nil/"" addresses the default value).
- #dword(name) ⇒ Object
- #dword?(name) ⇒ Boolean
-
#each_key ⇒ Object
Yields each subkey name.
-
#each_value ⇒ Object
Yields (name, type, value) decoded exactly as #read.
- #info ⇒ Object
- #inspect ⇒ Object
-
#key?(name) ⇒ Boolean
NotFound -> false; ACCESS_DENIED -> true (the key exists, you may not open it); anything else raises.
- #key_names ⇒ Object
- #multi_string(name) ⇒ Object
- #multi_string?(name) ⇒ Boolean
-
#open(subpath, access: :read) ⇒ Object
Child open.
- #qword(name) ⇒ Object
- #qword?(name) ⇒ Boolean
-
#raw(name) ⇒ Object
Raw escape hatch: NEVER decodes, never raises MalformedValue.
-
#read(name) ⇒ Object
Generic typed read -> [type, value].
-
#read?(name) ⇒ Boolean
Like #read, but nil when the value does not exist.
-
#string(name, expand: false) ⇒ Object
REG_SZ or REG_EXPAND_SZ -> String (UTF-8), unexpanded unless expand: true (ExpandEnvironmentStringsW on the result, whatever the type).
-
#string?(name, expand: false) ⇒ Boolean
nil for a MISSING value (TypeMismatch still raises — a wrong type is a bug, not an absence).
-
#value?(name) ⇒ Boolean
RegQueryValueExW size probe; error 2 -> false.
-
#value_names ⇒ Object
Value names only — never decodes data, so hostile values can't abort it.
-
#watch(subtree: false, filter: :default) ⇒ Object
Without a block: an armed Winreg::Watch (changes between construction and the first #wait are caught).
-
#write(name, type, value) ⇒ Object
Generic typed write — symmetric with #read; validates identically to the typed writers (it dispatches to them).
- #write_binary(name, bytes) ⇒ Object
- #write_dword(name, int) ⇒ Object
- #write_expand_string(name, str) ⇒ Object
- #write_multi_string(name, ary) ⇒ Object
- #write_qword(name, int) ⇒ Object
-
#write_raw(name, type_tag, bytes) ⇒ Object
Raw escape hatch, symmetric with #raw: exact bytes under an arbitrary type tag.
-
#write_string(name, str) ⇒ Object
All writers return nil and require access: :read_write (else the OS raises AccessDenied).
Instance Attribute Details
#path ⇒ Object (readonly)
Canonical full path, long-form root ("HKEY_CURRENT_USER\Software\X").
245 246 247 |
# File 'lib/winreg.rb', line 245 def path @path end |
#view ⇒ Object (readonly)
:default | :v64 | :v32
248 249 250 |
# File 'lib/winreg.rb', line 248 def view @view end |
Class Method Details
._create(vroot, vsub, vsam) ⇒ Object
Key._create(root, subpath, sam) — private singleton. Creates all missing intermediate keys; NULL SECURITY_ATTRIBUTES inherits the parent SD.
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
# File 'ext/winreg/winreg.c', line 286
static VALUE
key_s_create(VALUE klass, VALUE vroot, VALUE vsub, VALUE vsam)
{
VALUE obj = key_alloc(cKey);
key_t *k = key_get(obj);
HKEY root = root_hkey(vroot);
DWORD sam = (DWORD)NUM2ULONG(vsam);
HKEY h = NULL;
DWORD disp = 0;
LONG rc;
WCHAR *w;
k->view_sam = sam & WOW64_VIEW_MASK;
w = to_wide(vsub);
rc = RegCreateKeyExW(root, w, 0, NULL, REG_OPTION_NON_VOLATILE, sam, NULL, &h, &disp);
xfree(w);
if (rc != ERROR_SUCCESS) raise_lstatus("RegCreateKeyExW", rc);
k->h = h;
k->created = (disp == REG_CREATED_NEW_KEY) ? 1 : 0;
k->closed = 0;
return obj;
}
|
._open(vroot, vsub, vsam) ⇒ Object
Key._open(root, subpath_or_nil, sam) — private singleton. A nil subpath is
a bare root: per the documented RegOpenKeyExW behavior a NULL lpSubKey on a
predefined key returns the SAME process-global pseudo-handle, so no OS call
is made; the Key wraps the predefined handle value with predefined set
and #close / the free hook skip RegCloseKey.
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'ext/winreg/winreg.c', line 257
static VALUE
key_s_open(VALUE klass, VALUE vroot, VALUE vsub, VALUE vsam)
{
VALUE obj = key_alloc(cKey);
key_t *k = key_get(obj);
HKEY root = root_hkey(vroot);
DWORD sam = (DWORD)NUM2ULONG(vsam);
HKEY h = NULL;
LONG rc;
WCHAR *w;
k->view_sam = sam & WOW64_VIEW_MASK;
if (NIL_P(vsub)) {
k->h = root;
k->predefined = 1;
k->closed = 0;
return obj;
}
w = to_wide(vsub);
rc = RegOpenKeyExW(root, w, 0, sam, &h);
xfree(w);
if (rc != ERROR_SUCCESS) raise_lstatus("RegOpenKeyExW", rc);
k->h = h;
k->closed = 0;
return obj;
}
|
Instance Method Details
#binary(name) ⇒ Object
319 320 321 |
# File 'lib/winreg.rb', line 319 def binary(name) typed_bytes(name, [TYPES[:binary]], "REG_BINARY") end |
#binary?(name) ⇒ Boolean
323 324 325 326 327 |
# File 'lib/winreg.rb', line 323 def binary?(name) binary(name) rescue Winreg::NotFound nil end |
#close ⇒ Object
527 528 529 530 531 532 533 534 535 536 537 538 |
# File 'ext/winreg/winreg.c', line 527
static VALUE
key_close(VALUE self)
{
key_t *k = key_get(self);
if (!k->closed) {
/* bare roots: flag flip only — never RegCloseKey a pseudo-handle */
if (k->h && !k->predefined) RegCloseKey(k->h);
k->h = NULL;
k->closed = 1;
}
return Qnil;
}
|
#closed? ⇒ Boolean
540 |
# File 'ext/winreg/winreg.c', line 540
static VALUE key_closed_p(VALUE self) { return key_get(self)->closed ? Qtrue : Qfalse; }
|
#create(subpath, access: :read_write) ⇒ Object
Child open-or-create, same view rules.
456 457 458 459 460 461 462 463 464 465 466 467 |
# File 'lib/winreg.rb', line 456 def create(subpath, access: :read_write) sam = Winreg.send(:access_mask, access) k = _create_child(subpath_arg(subpath), sam) k.send(:_init_meta, @root, child_subpath(subpath), @view) return k unless block_given? begin yield k ensure k.close end end |
#created? ⇒ Boolean
541 |
# File 'ext/winreg/winreg.c', line 541
static VALUE key_created_p(VALUE self) { return key_get(self)->created ? Qtrue : Qfalse; }
|
#delete_key(name, recursive: false) ⇒ Object
Delete the named child subkey (handle-relative; the view is applied via RegDeleteKeyExW samDesired). Non-recursive delete of a key that still has subkeys fails with an OSError; recursive: true performs a deepest- first walk in Ruby over the raise-safe primitives (interruptible; NOT atomic — same as RegDeleteTreeW), with the view applied to every open.
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/winreg.rb', line 474 def delete_key(name, recursive: false) n = subpath_arg(name) if recursive _delete_tree(n) else begin _delete_key(n) rescue Winreg::AccessDenied => e err = Winreg::AccessDenied.new("#{e.} (key may have subkeys; use recursive: true)") err.instance_variable_set(:@code, e.code) raise err end end nil end |
#delete_value(name) ⇒ Object
Delete a value (nil/"" addresses the default value). NotFound if absent.
413 414 415 416 |
# File 'lib/winreg.rb', line 413 def delete_value(name) _delete_value(norm_name(name)) nil end |
#dword(name) ⇒ Object
289 290 291 |
# File 'lib/winreg.rb', line 289 def dword(name) Winreg.send(:decode_int, typed_bytes(name, [TYPES[:dword]], "REG_DWORD"), 4, false, :dword) end |
#dword?(name) ⇒ Boolean
293 294 295 296 297 |
# File 'lib/winreg.rb', line 293 def dword?(name) dword(name) rescue Winreg::NotFound nil end |
#each_key ⇒ Object
Yields each subkey name.
509 510 511 512 513 514 515 516 517 518 |
# File 'lib/winreg.rb', line 509 def each_key return enum_for(:each_key) unless block_given? i = 0 while (name = _enum_key(i)) yield name i += 1 end self end |
#each_value ⇒ Object
Yields (name, type, value) decoded exactly as #read. Live, snapshot-less, kernel order arbitrary; concurrent mutation may skip or repeat entries. A value whose DATA is malformed raises MalformedValue mid-iteration — use #value_names + #raw for forensic robustness.
496 497 498 499 500 501 502 503 504 505 506 |
# File 'lib/winreg.rb', line 496 def each_value return enum_for(:each_value) unless block_given? i = 0 while (entry = _enum_value(i)) name, t, bytes = entry yield name, Winreg.send(:type_symbol, t), Winreg.send(:decode_value, t, bytes) i += 1 end self end |
#info ⇒ Object
537 538 539 540 541 |
# File 'lib/winreg.rb', line 537 def info a = _query_info t = Time.at((a[5] - Winreg.send(:filetime_epoch_delta)) / 10_000_000r) Info.new(a[0], a[2], a[1], a[3], a[4], t).freeze end |
#inspect ⇒ Object
250 251 252 |
# File 'lib/winreg.rb', line 250 def inspect closed? ? "#<Winreg::Key (closed)>" : "#<Winreg::Key #{@path} view=#{@view.inspect}>" end |
#key?(name) ⇒ Boolean
NotFound -> false; ACCESS_DENIED -> true (the key exists, you may not open it); anything else raises. View-consistent (probe carries the view).
427 428 429 430 431 432 433 434 435 |
# File 'lib/winreg.rb', line 427 def key?(name) probe = _open_child(subpath_arg(name), Winreg::KEY_QUERY_VALUE) probe.close true rescue Winreg::NotFound false rescue Winreg::AccessDenied true end |
#key_names ⇒ Object
531 532 533 534 535 |
# File 'lib/winreg.rb', line 531 def key_names out = [] each_key { |n| out << n } out end |
#multi_string(name) ⇒ Object
309 310 311 |
# File 'lib/winreg.rb', line 309 def multi_string(name) Winreg.send(:decode_multi, typed_bytes(name, [TYPES[:multi_sz]], "REG_MULTI_SZ")) end |
#multi_string?(name) ⇒ Boolean
313 314 315 316 317 |
# File 'lib/winreg.rb', line 313 def multi_string?(name) multi_string(name) rescue Winreg::NotFound nil end |
#open(subpath, access: :read) ⇒ Object
Child open. Relative path, backslashes allowed ("A\B"). The parent's WOW64 view is inherited automatically and CANNOT be overridden (the API encoding of the view-consistency rule). Block form ensure-closes.
442 443 444 445 446 447 448 449 450 451 452 453 |
# File 'lib/winreg.rb', line 442 def open(subpath, access: :read) sam = Winreg.send(:access_mask, access) k = _open_child(subpath_arg(subpath), sam) k.send(:_init_meta, @root, child_subpath(subpath), @view) return k unless block_given? begin yield k ensure k.close end end |
#qword(name) ⇒ Object
299 300 301 |
# File 'lib/winreg.rb', line 299 def qword(name) Winreg.send(:decode_int, typed_bytes(name, [TYPES[:qword]], "REG_QWORD"), 8, false, :qword) end |
#qword?(name) ⇒ Boolean
303 304 305 306 307 |
# File 'lib/winreg.rb', line 303 def qword?(name) qword(name) rescue Winreg::NotFound nil end |
#raw(name) ⇒ Object
Raw escape hatch: NEVER decodes, never raises MalformedValue. -> [Integer type_tag, String bytes (Encoding::BINARY)]
331 332 333 |
# File 'lib/winreg.rb', line 331 def raw(name) _read_raw(norm_name(name)) end |
#read(name) ⇒ Object
Generic typed read -> [type, value]. type is a Symbol from Winreg::TYPES
(or the raw Integer tag for types outside the table). Strings are
returned UNEXPANDED with at most one trailing NUL stripped; :multi_sz as
Array
261 262 263 264 |
# File 'lib/winreg.rb', line 261 def read(name) t, bytes = _read_raw(norm_name(name)) [Winreg.send(:type_symbol, t), Winreg.send(:decode_value, t, bytes)] end |
#read?(name) ⇒ Boolean
Like #read, but nil when the value does not exist.
267 268 269 270 271 |
# File 'lib/winreg.rb', line 267 def read?(name) read(name) rescue Winreg::NotFound nil end |
#string(name, expand: false) ⇒ Object
REG_SZ or REG_EXPAND_SZ -> String (UTF-8), unexpanded unless expand: true (ExpandEnvironmentStringsW on the result, whatever the type).
275 276 277 278 279 |
# File 'lib/winreg.rb', line 275 def string(name, expand: false) bytes = typed_bytes(name, [TYPES[:sz], TYPES[:expand_sz]], "REG_SZ/REG_EXPAND_SZ") s = Winreg.send(:decode_string, bytes) ? Winreg.(s) : s end |
#string?(name, expand: false) ⇒ Boolean
nil for a MISSING value (TypeMismatch still raises — a wrong type is a bug, not an absence). Same pattern for the other ? readers.
283 284 285 286 287 |
# File 'lib/winreg.rb', line 283 def string?(name, expand: false) string(name, expand: ) rescue Winreg::NotFound nil end |
#value?(name) ⇒ Boolean
RegQueryValueExW size probe; error 2 -> false.
421 422 423 |
# File 'lib/winreg.rb', line 421 def value?(name) _value_p(norm_name(name)) end |
#value_names ⇒ Object
Value names only — never decodes data, so hostile values can't abort it.
521 522 523 524 525 526 527 528 529 |
# File 'lib/winreg.rb', line 521 def value_names out = [] i = 0 while (entry = _enum_value(i)) out << entry[0] i += 1 end out end |
#watch(subtree: false, filter: :default) ⇒ Object
Without a block: an armed Winreg::Watch (changes between construction and the first #wait are caught). With a block: loops, yielding :changed per coalesced change; yields :deleted once and returns if the watched key is deleted; the Watch is ensure-closed. The Watch opens its OWN private KEY_NOTIFY|view handle from this key's path — closing this Key afterwards does not disturb it.
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 |
# File 'lib/winreg.rb', line 551 def watch(subtree: false, filter: :default) raise Winreg::Closed, "winreg: key is closed" if closed? mask = Winreg.send(:filter_mask, filter) vsam = Winreg.send(:view_mask, @view || :default) w = Watch.send(:_new, @root, @subpath, vsam, subtree ? true : false, mask) return w unless block_given? begin loop do event = w.wait yield event break if event == :deleted end nil ensure w.close end end |
#write(name, type, value) ⇒ Object
Generic typed write — symmetric with #read; validates identically to the typed writers (it dispatches to them). :dword_be packs 4 bytes BE; :none expects bytes; :link raises (links are read-surface only in v1); a raw Integer tag goes through #write_raw.
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/winreg.rb', line 378 def write(name, type, value) case type when :sz then write_string(name, value) when :expand_sz then (name, value) when :multi_sz then write_multi_string(name, value) when :dword then write_dword(name, value) when :qword then write_qword(name, value) when :dword_be v = Winreg.send(:int_in_range, value, 0xFFFF_FFFF, "REG_DWORD_BIG_ENDIAN") _write_raw(norm_name(name), TYPES[:dword_be], [v].pack("N")) when :binary then write_binary(name, value) when :none Winreg.send(:string_arg, value, ":none data") _write_raw(norm_name(name), TYPES[:none], value) when :link raise ArgumentError, "winreg: :link values are read-surface only (v1); " \ "registry symlink creation is out of scope" when Integer then write_raw(name, type, value) else raise ArgumentError, "winreg: unknown registry type #{type.inspect}" end nil end |
#write_binary(name, bytes) ⇒ Object
368 369 370 371 372 |
# File 'lib/winreg.rb', line 368 def write_binary(name, bytes) Winreg.send(:string_arg, bytes, "binary data") _write_raw(norm_name(name), TYPES[:binary], bytes) nil end |
#write_dword(name, int) ⇒ Object
356 357 358 359 360 |
# File 'lib/winreg.rb', line 356 def write_dword(name, int) v = Winreg.send(:int_in_range, int, 0xFFFF_FFFF, "REG_DWORD") _write_raw(norm_name(name), TYPES[:dword], [v].pack("V")) nil end |
#write_expand_string(name, str) ⇒ Object
346 347 348 349 |
# File 'lib/winreg.rb', line 346 def (name, str) _write_raw(norm_name(name), TYPES[:expand_sz], Winreg.send(:encode_sz, str)) nil end |
#write_multi_string(name, ary) ⇒ Object
351 352 353 354 |
# File 'lib/winreg.rb', line 351 def write_multi_string(name, ary) _write_raw(norm_name(name), TYPES[:multi_sz], Winreg.send(:encode_multi, ary)) nil end |
#write_qword(name, int) ⇒ Object
362 363 364 365 366 |
# File 'lib/winreg.rb', line 362 def write_qword(name, int) v = Winreg.send(:int_in_range, int, 0xFFFF_FFFF_FFFF_FFFF, "REG_QWORD") _write_raw(norm_name(name), TYPES[:qword], [v].pack("Q<")) nil end |
#write_raw(name, type_tag, bytes) ⇒ Object
Raw escape hatch, symmetric with #raw: exact bytes under an arbitrary type tag. No validation beyond bytes being a String + the 4 GiB guard.
404 405 406 407 408 409 410 |
# File 'lib/winreg.rb', line 404 def write_raw(name, type_tag, bytes) raise TypeError, "winreg: type tag must be an Integer, got #{type_tag.inspect}" unless type_tag.is_a?(Integer) Winreg.send(:string_arg, bytes, "raw data") _write_raw(norm_name(name), type_tag, bytes) nil end |
#write_string(name, str) ⇒ Object
All writers return nil and require access: :read_write (else the OS raises AccessDenied). The gem owns serialization, so the wire format is correct by construction (double-NUL REG_MULTI_SZ, cb incl. terminators).
341 342 343 344 |
# File 'lib/winreg.rb', line 341 def write_string(name, str) _write_raw(norm_name(name), TYPES[:sz], Winreg.send(:encode_sz, str)) nil end |