Win32
view release on metacpan or search on metacpan
Revision history for the Perl extension Win32.
0.62 [2026-05-11]
- revert SvUTF8 flagging in wstr_to_sv and my_ansipath under CP_UTF8 ACP;
make t/Unicode.t pass under CP_UTF8 ACP [PR/57]
0.61 [2026-05-10]
- handle CP_UTF8 system codepage in wstr_to_sv and my_ansipath [PR/53]
- fix inverted SKIP condition for non-English locale in t/HttpGetFile.t [PR/55]
- fix t/GetCurrentThreadId.t fork-emulation detection by sisyphus [PR/45]
0.60 [2026-05-05]
- add LICENSE and modernize README [PR/51]
- update Microsoft doc URLs to learn.microsoft.com
0.59_02 [2026-05-04]
- detect newer Windows 10/11, Windows Server 2019/2022/2025, and
Server SAC releases by Markus Demml [PR/42]
- fix 64-bit registry constant by jkahrman [PR/37]
- follow-up to 64-bit registry fix by Tony Cook [PR/39]
- fix memory deallocation for WinHttpGetProxyForUrl strings [PR/49]
- include t/HttpGetFile.t in MANIFEST so it ships in the CPAN tarball [PR/50]
- convert tests to Test::More by Graham Knop [PR/35]
- clarify documentation of minor versions by Ferenc Erki [PR/25]
0.59 [2022-05-05]
- add Win32::GetChipArch and use it in Win32::GetOSName to support arm/arm64
architecture by Pierrick Bouvier [PR/34]
0.58 [2022-01-17]
- add Win32::HttpGetFile (thanks to Craig Berry for the implementation
and Tomasz Konojacki for code review) [PR/30]
- skip failing Unicode.t on Cygwin because cwd() no longer returns an
ANSI (short) path there.
- Fixed test 14,15 of GetFullPathName.t when package is unpacked in a
top level folder (thanks to Jianhong Feng) [PR/20]
0.57 [2021-03-10]
- fix calling convention for PFNRegGetValueA [PR/28]
0.56 [2021-03-07]
t/ExpandEnvironmentStrings.t
t/GetCurrentThreadId.t
t/GetFileVersion.t
t/GetFolderPath.t
t/GetFullPathName.t
t/GetLongPathName.t
t/GetOSName.t
t/GetOSVersion.t
t/GetShortPathName.t
t/GuidGen.t
t/HttpGetFile.t
t/Names.t
t/Privileges.t
t/Unicode.t
persistent identifier in a distributed setting. To a very high degree
of certainty this function returns a unique value. No other
invocation, on the same or any other system (networked or not), should
return the same value.
The return value is formatted according to OLE conventions, as groups
of hex digits with surrounding braces. For example:
{09531CF1-D0C7-4860-840C-1C8C8735E2AD}
=item Win32::HttpGetFile(URL, FILENAME [, IGNORE_CERT_ERRORS])
Uses the WinHttp library to download the file specified by the URL
parameter to the local file specified by FILENAME. The optional third
parameter, if true, indicates that certficate errors are to be ignored
for https connections; please use with caution in a safe environment,
such as when testing locally using a self-signed certificate.
Only http and https protocols are supported. Authentication is not
supported. The function is not available when building with gcc prior to
4.8.0 because the WinHttp library is not available.
context also returns, in addition to the boolean status, a second
value containing message text related to the status.
If the call fails, C<Win32::GetLastError()> will return a numeric
error code, which may be a system error, a WinHttp error, or a
user-defined error composed of 1e9 plus the HTTP status code.
Scalar context example:
print Win32::GetLastError()
unless Win32::HttpGetFile('http://example.com/somefile.tar.gz',
'.\file.tgz');
List context example:
my ($ok, $msg) = Win32::HttpGetFile('http://example.com/somefile.tar.gz',
'.\file.tgz');
if ($ok) {
print "Success!: $msg\n";
}
else {
print "Failure!: $msg\n";
my $err = Win32::GetLastError();
if ($err > 1e9) {
printf "HTTP status: %d\n", ($err - 1e9);
}
);
if (status == ERROR_SUCCESS && val == 1)
XSRETURN_YES;
XSRETURN_NO;
}
#ifdef WINHTTPAPI
XS(w32_HttpGetFile)
{
dXSARGS;
WCHAR *url = NULL, *file = NULL, *hostName = NULL, *urlPath = NULL;
bool bIgnoreCertErrors = FALSE;
WCHAR msgbuf[ONE_K_BUFSIZE];
BOOL bResults = FALSE;
HINTERNET hSession = NULL,
hConnect = NULL,
hRequest = NULL;
HANDLE hOut = INVALID_HANDLE_VALUE;
BOOL bParsed = FALSE,
bAborted = FALSE,
bFileError = FALSE,
bHttpError = FALSE;
DWORD error = 0;
URL_COMPONENTS urlComp;
LPCWSTR acceptTypes[] = { L"*/*", NULL };
DWORD dwHttpStatusCode = 0, dwQuerySize = 0;
if (items < 2 || items > 3)
croak("usage: Win32::HttpGetFile($url, $file[, $ignore_cert_errors])");
url = sv_to_wstr(aTHX_ ST(0));
file = sv_to_wstr(aTHX_ ST(1));
if (items == 3)
bIgnoreCertErrors = (BOOL)SvIV(ST(2));
/* Initialize the URL_COMPONENTS structure, setting the required
* component lengths to non-zero so that they get populated.
*/
if(WinHttpGetProxyForUrl(hSession,
url,
&AutoProxyOptions,
&ProxyInfo)) {
if(!WinHttpSetOption(hRequest,
WINHTTP_OPTION_PROXY,
&ProxyInfo,
cbProxyInfoSize)) {
bAborted = TRUE;
Perl_warn(aTHX_ "Win32::HttpGetFile: setting proxy options failed");
}
/* WinHttpGetProxyForUrl allocates these with GlobalAlloc. */
if (ProxyInfo.lpszProxy) GlobalFree(ProxyInfo.lpszProxy);
if (ProxyInfo.lpszProxyBypass) GlobalFree(ProxyInfo.lpszProxyBypass);
}
}
/* Send a request. */
if (hRequest && !bAborted)
bResults = WinHttpSendRequest(hRequest,
newXS("Win32::GetConsoleOutputCP", w32_GetConsoleOutputCP, file);
newXS("Win32::GetOEMCP", w32_GetOEMCP, file);
newXS("Win32::SetConsoleCP", w32_SetConsoleCP, file);
newXS("Win32::SetConsoleOutputCP", w32_SetConsoleOutputCP, file);
newXS("Win32::GetProcessPrivileges", w32_GetProcessPrivileges, file);
newXS("Win32::IsDeveloperModeEnabled", w32_IsDeveloperModeEnabled, file);
#ifdef __CYGWIN__
newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
#endif
#ifdef WINHTTPAPI
newXS("Win32::HttpGetFile", w32_HttpGetFile, file);
#endif
XSRETURN_YES;
}
t/HttpGetFile.t view on Meta::CPAN
use strict;
use warnings;
use Test::More;
use Win32;
use Digest::SHA;
my $tmpfile = "http-download-test-$$.tgz";
END { 1 while unlink $tmpfile; }
unless (defined &Win32::HttpGetFile) {
plan skip_all => 'gcc before 4.8 does not have winhttp library';
}
# We can only verify specific error messages with a known locale.
my $english_locale = (Win32::FormatMessage(1) eq "Incorrect function.\r\n");
# We may not always have an internet connection, so don't
# attempt remote connections unless the user has done
# set PERL_WIN32_INTERNET_OK=1
plan tests => $ENV{PERL_WIN32_INTERNET_OK} ? 20 : 7;
# On Cygwin the test_harness will invoke additional Win32 APIs that
# will reset the Win32::GetLastError() value, so capture it immediately.
my $LastError;
sub HttpGetFile {
my $ok = Win32::HttpGetFile(@_);
$LastError = Win32::GetLastError();
return $ok;
}
sub HttpGetFileList {
my ($ok, $message) = Win32::HttpGetFile(@_);
$LastError = Win32::GetLastError();
return ($ok, $message);
}
is(HttpGetFile('nonesuch://example.com', 'NUL:'), "", "'nonesuch://' is not a real protocol");
is($LastError, '12006', "correct error code for unrecognized protocol");
is(HttpGetFile('http://!#@!&@$', 'NUL:'), "", "invalid URL");
is($LastError, '12005', "correct error code for invalid URL");
my ($ok, $message) = HttpGetFileList('nonesuch://example.com', 'NUL:');
is($ok, "", "'nonesuch://' is not a real protocol");
SKIP: {
skip "Cannot verify error on non-English locale setting", 1
unless $english_locale;
is($message, "The URL does not use a recognized protocol\r\n", "correct bad protocol message");
}
is($LastError, '12006', "correct error code for unrecognized protocol with list context return");
if ($ENV{PERL_WIN32_INTERNET_OK}) {
# The digest for version 0.57 should obviously stay the same even after new versions are released
is(Win32::HttpGetFile('https://cpan.metacpan.org/authors/id/J/JD/JDB/Win32-0.57.tar.gz', $tmpfile),
'1',
"successfully downloaded a tarball");
my $sha = Digest::SHA->new('sha1');
$sha->addfile($tmpfile, 'b');
is($sha->hexdigest,
'44a6d7d1607d7267b0dbcacbb745cec204f1c1a4',
"downloaded tarball has correct digest");
my ($ok, $message) = HttpGetFileList('https://cpan.metacpan.org/authors/id/Z/ZZ/ZILCH/nonesuch.tar.gz', 'NUL:');
is($ok, '', 'Download of nonexistent file from real site should fail with 404');
is($LastError - 1e9, '404', 'Correct 404 HTTP status for not found');
SKIP: {
skip "Cannot verify error on non-English locale setting", 1
unless $english_locale;
is($message, 'Not Found', 'Should get text of 404 message');
}
# Since all GitHub downloads use redirects, we can test that they work.
1 while unlink $tmpfile;
is(Win32::HttpGetFile('https://github.com/perl-libwin32/win32/archive/refs/tags/v0.57.zip', $tmpfile),
'1',
"successfully downloaded a zipball via redirect");
$sha = Digest::SHA->new('sha1');
$sha->addfile($tmpfile, 'b');
is($sha->hexdigest,
'9d282e2292e67fb2e25422dfb190474e30a38de3',
"downloaded GitHub zip archive has correct digest");
is(HttpGetFile('https://self-signed.badssl.com/index.html', 'NUL:'),
'',
'Cannot download from site with self-signed cert without ignoring cert errors');
is($LastError, '12175', "correct code for ERROR_WINHTTP_SECURE_FAILURE with self-signed certificate");
is(HttpGetFile('https://self-signed.badssl.com/index.html', 'NUL:', 1),
'1',
'Can download from site with self-signed cert using ignore cert errors parameter');
is(HttpGetFile('https://expired.badssl.com/index.html', 'NUL:'),
'',
'Cannot download from site with expired cert without ignoring cert errors');
is($LastError, '12175', "correct code for ERROR_WINHTTP_SECURE_FAILURE with expired certificate");
is(HttpGetFile('https://expired.badssl.com/index.html', 'NUL:', 1),
'1',
'Can download from site with expired cert using ignore cert errors parameter');
}
( run in 2.128 seconds using v1.01-cache-2.11-cpan-2398b32b56e )