On 2020-01-29 00:43, Tobias Boege wrote:
On Tue, 28 Jan 2020, ToddAndMargo via perl6-users wrote:
This all came up when I tried to match
RegSetValueExW(
_In_ HKEY hKey,
_In_opt_ LPCWSTR lpValueName,
_Reserved_ DWORD Reserved,
_In_ DWORD dwType,
_In_reads_bytes_opt_(cbData) CONST BYTE * lpData,
_In_ DWORD cbData
where CbData can either be a UTF little endian C string,
terminated by a nul or a four byte little endian
unsigned integer (no two's complement allowed) depending
on the value of lpValueName (REG_SZ, REG_DWORD, etc.)
I wound up doing this:
subset StrOrDword where Str | UInt;
sub WinRegSetValue( WinRegHives $Hive, Str $SubKey, Str $KeyName, ValueNames
$ValueType, StrOrDword $ValueData, Bool $Debug = False )
returns DWORD is export( :WinRegSetValue ) {
Are you really 100% sure that you interpreted this API correctly? I see how
a DWORD cbData can be a four-byte unsigned integer: it gives the length of
lpData in bytes, as documented [1].
But then a DWORD is 4 bytes long. Reusing these 4 bytes for an alternative
interface where you may pass a UTF-whatever string that is at most 4 bytes
encoded, including the NUL terminator... seems too insane. And there is no
mention of that in the documentation page [1]. I do not think that cbData
is ever used for anything but to indicate the length of the buffer lpData.
It is lpData which can have a multitude of types (and serialization formats),
the intended one to be taken from the dwType argument (not lpValueName).
My advice is still the same I gave in my very first reply to this thread:
make your function a multi and write a candidate for each dwType. You have
to write different code for serializing an integer vs. a string to pass as
lpData anyway and the compiler can detect native types in multi dispatch
for you:
# REG_DWORD
multi WinRegSetValue(…, uint32 $data) {
use experimental :pack;
RegSetValueExW(…, REG_DWORD, pack("L", $data), 4)
}
# REG_SZ
multi WinRegSetValue(…, Str $data) {
my $blob = "$data\0".encode
RegSetValueExW(…, REG_SZ, $blob, $blob.bytes)
}
# REG_BINARY
multi WinRegSetValue(…, blob8 $data) {
RegSetValueExW(…, REG_BINARY, $data, $data.bytes)
}
Regards,
Tobias
[1]
https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regsetvalueexw
Hi Tobias,
I don't have to get so complicated. Code looks sweet
though. I will probably write it down for use
on other things. Thank you!
Oh yes. I can pass it strings and DWORD with ease.
I check the value of lpValueName and then proceed.
lpData is basically an array of bytes. I set the
bytes up based on lpValueName. And you have to
tell cbData how many bytes are legitimate
in lpData.
It they pass me a string for REG_DWORD or a UInt for
a REG_SZ, I wag the finger wagged at them big time.
For example:
if $ValueData.^name ne "Int" || $ValueData < 0 { # UInt gets
"boxed" to an Int
$ErrStr = "ERROR: $SubName\n\n" ~
" ValueData must be an Unsigned Integer when used
with $ValueType\n\n" ~
" Cowardly exiting\n\n";
say $ErrStr;
WinMsg( "ValueData Error", $ErrStr );
exit;
}
Hmmmmm... I forgot the "Bummer Dude!"
Would you like to see the actual code? I am not
posting it here as it is several hundred lines long
and then I'd get the finger wagged at me. Every
thing is spread across several modules.
-T