Below is how I would have implemented the solution. In short, I'd use constants instead of magical values, and I'd hide C-ish bits in a sub that provides an interface better suited to Perl.
use v5.10.0;
use strict;
use warnings;
use Win32::API qw( );
use constant MAX_PATH => 260;
use constant MAX_FEATURE_CHARS => 38;
use constant {
INSTALLSTATE_NOTUSED => -7, # component disabled
INSTALLSTATE_BADCONFIG => -6, # configuration data corrupt
INSTALLSTATE_INCOMPLETE => -5, # installation suspended or in p
+rogress
INSTALLSTATE_SOURCEABSENT => -4, # run from source, source is una
+vailable
INSTALLSTATE_MOREDATA => -3, # return buffer overflow
INSTALLSTATE_INVALIDARG => -2, # invalid function argument
INSTALLSTATE_UNKNOWN => -1, # unrecognized product or featur
+e
INSTALLSTATE_BROKEN => 0, # broken
INSTALLSTATE_ADVERTISED => 1, # advertised feature
INSTALLSTATE_REMOVED => 1, # component being removed (actio
+n state, not settable)
INSTALLSTATE_ABSENT => 2, # uninstalled (or action state a
+bsent but clients remain)
INSTALLSTATE_LOCAL => 3, # installed on local drive
INSTALLSTATE_SOURCE => 4, # run from source, CD or net
INSTALLSTATE_DEFAULT => 5, # use default, local or source
};
my %INSTALLSTATE_DESC = (
INSTALLSTATE_NOTUSED() => 'The component being requested is d
+isabled on the computer',
INSTALLSTATE_BADCONFIG() => '[configuration data corrupt]',
INSTALLSTATE_INCOMPLETE() => '[installation suspended or in prog
+ress]',
INSTALLSTATE_SOURCEABSENT() => 'The component source is inaccessib
+le',
INSTALLSTATE_MOREDATA() => '[return buffer overflow]',
INSTALLSTATE_INVALIDARG() => 'One of the function parameters is
+invalid',
INSTALLSTATE_UNKNOWN() => 'The product code or component ID i
+s unknown',
INSTALLSTATE_BROKEN() => '[broken]',
INSTALLSTATE_ADVERTISED() => '[advertised feature]',
INSTALLSTATE_REMOVED() => '[component being removed (action s
+tate, not settable)]',
INSTALLSTATE_ABSENT() => 'The component is not installed',
INSTALLSTATE_LOCAL() => 'The component is installed locally
+',
INSTALLSTATE_SOURCE() => 'The component is installed to run
+from source',
INSTALLSTATE_DEFAULT() => '[use default, local or source]',
);
{
# UINT MsiGetShortcutTarget(
# __in LPCTSTR szShortcutTarget,
# __out LPTSTR szProductCode,
# __out LPTSTR szFeatureId,
# __out LPTSTR szComponentCode
# );
my $MsiGetShortcutTarget = Win32::API->new(
'msi.dll', 'MsiGetShortcutTarget', 'PPPP', 'N');
sub MsiGetShortcutTarget {
my ($szShortcutTarget) = @_;
$szShortcutTarget .= "\0";
my $szProductCode = "\0" x 39;
my $szFeatureId = "\0" x (MAX_FEATURE_CHARS+1);
my $szComponentCode = "\0" x 39;
return () if $^E = $MsiGetShortcutTarget->Call(
$szShortcutTarget,
$szProductCode,
$szFeatureId,
$szComponentCode,
);
s/\0.*//s for $szProductCode, $szFeatureId, $szComponentCode;
return ( $szProductCode, $szFeatureId, $szComponentCode );
}
}
{
# INSTALLSTATE MsiGetComponentPath(
# __in LPCTSTR szProduct,
# __in LPCTSTR szComponent,
# __out LPTSTR lpPathBuf,
# __inout DWORD *pcchBuf
# );
my $MsiGetComponentPath = Win32::API->new(
'msi.dll', 'MsiGetComponentPath', 'PPPP', 'I');
sub MsiGetComponentPath {
my ($szProduct, $szComponent) = @_;
$szProduct .= "\0";
$szComponent .= "\0";
my $lpPathBuf = "\0" x MAX_PATH;
my $pcchBuf = pack('L', MAX_PATH);
my $rv = $MsiGetComponentPath->Call(
$szProduct,
$szComponent,
$lpPathBuf,
$pcchBuf,
);
return ( $rv, substr($lpPathBuf, 0, unpack('L', $pcchBuf)) );
}
}
{
my $shortcut = 'Microsoft Office Word 2007.lnk';
my ($product, $feature, $component) = MsiGetShortcutTarget($shortc
+ut)
or die("MsiGetShortcutTarget: $^E\n");
say "product: $product";
say "feature: $feature";
say "component: $component";
my ($install_state, $path) =
MsiGetComponentPath($product, $component);
say "install state: $INSTALLSTATE_DESC{$install_state}";
say "install path: $path";
}
Untested (since I don't know of what advertised shortcuts exist on my machine).
Update: Changed +CONSTANT to CONSTANT() for hash keys.
|