'StringBuilder in inlined C# in PowerShell (2.0 and above)
The C# I am trying to inline is at the bottom, but my error is occurring at the second DllImport, because StringBuilder is not loaded. I have tried a number of things with regards to loading assemblies, adding Using references in the C#, etc. This being one example. But nothing is working and I am pretty sure I am just missing something very obvious to C# programmers, and totally non obvious to my brain.
$assemblies = ('mscorlib.dll')
$code = @'
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern int LoadString(IntPtr hInstance, uint wID, StringBuilder lpBuffer, int nBufferMax);
public static class PxTest
{
// Note
}
'@
Add-Type -memberDefinition $code -referencedAssemblies $assemblies -namespace Px -name Test -errorAction stop
I have also tried $assemblies = ('system.text') which then tries to load system.text.dll that isn't found. Which is what sent me to the dll above, which is where StringBuilder is found I believe.
C# code to ultimately use
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern int LoadString(IntPtr hInstance, uint wID, StringBuilder lpBuffer, int nBufferMax);
public static bool PinUnpinTaskbar(string filePath, bool pin)
{
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
int MAX_PATH = 255;
var actionIndex = pin ? 5386 : 5387; // 5386 is the DLL index for"Pin to Tas&kbar", ref. http://www.win7dll.info/shell32_dll.html
StringBuilder szPinToStartLocalized = new StringBuilder(MAX_PATH);
IntPtr hShell32 = LoadLibrary("Shell32.dll");
LoadString(hShell32, (uint)actionIndex, szPinToStartLocalized, MAX_PATH);
string localizedVerb = szPinToStartLocalized.ToString();
string path = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
// create the shell application object
dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
dynamic directory = shellApplication.NameSpace(path);
dynamic link = directory.ParseName(fileName);
dynamic verbs = link.Verbs();
for (int i = 0; i < verbs.Count(); i++)
{
dynamic verb = verbs.Item(i);
if (verb.Name.Equals(localizedVerb))
{
verb.DoIt();
return true;
}
}
return false;
}
EDIT: Trying to understand the edits. I get the added tags, but replacing some body text with... the same text? That I don't get. And that's at least what I am seeing. StringBuilder is is replaced with StringBuilder is, etc. huh?
Solution 1:[1]
It is because StringBuilder is not fully qualified and / or the namespace System.Text is not used
So either change the parameter to
System.Text.StringBuilder lpBuffer
or add the UsingNamespace parameter to the Add-Type cmdlet
Add-Type -memberDefinition $code -referencedAssemblies $assemblies -namespace Px -UsingNamespace 'System.Text' -name Test -errorAction stop
As your environment is fixed to PoSh 2.0 and Win7 I rewrote the code to handle the dynamic stuff via PowerShell (which is anyway more idiomatic in this case) and to get the localized verb via the C# code you provided so finally it looks like this which provides you a convenient PowerShell function PinUnpinTaskbar
$assemblies = ('mscorlib.dll')
$code = @'
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
internal static extern int LoadString(IntPtr hInstance, uint wID, StringBuilder lpBuffer, int nBufferMax);
public static string GetLocalizedPinToStartVerb(bool pin)
{
int MAX_PATH = 255;
int actionIndex = pin ? 5386 : 5387; // 5386 is the DLL index for"Pin to Tas&kbar", ref. http://www.win7dll.info/shell32_dll.html
StringBuilder szPinToStartLocalized = new StringBuilder(MAX_PATH);
IntPtr hShell32 = LoadLibrary("Shell32.dll");
LoadString(hShell32, (uint)actionIndex, szPinToStartLocalized, MAX_PATH);
return szPinToStartLocalized.ToString();
}
'@
Add-Type -MemberDefinition $code `
-ReferencedAssemblies $assemblies `
-Namespace Px `
-UsingNamespace 'System.Text' `
-Name Helper `
-ErrorAction stop
function PinUnpinTaskbar([string]$FilePath, [boolean]$Pin) {
if (Test-Path $FilePath) {
$localizedVerb = [Px.Helper]::GetLocalizedPinToStartVerb($Pin)
$path = [IO.Path]::GetDirectoryName($FilePath)
$fileName = [IO.Path]::GetFileName($FilePath)
$shellAppType = [Type]::GetTypeFromProgID("Shell.Application")
$shellAppInst = [Activator]::CreateInstance($shellAppType)
$directory = $shellAppInst.NameSpace($path)
$link = $directory.ParseName($fileName)
$verbs = $link.Verbs()
for ($i = 0; $i -lt $verbs.Count; ++$i) {
$verb = $verbs.Item($i)
Write-Host $verb.Name
if ($verb.Name.Equals($localizedVerb)) {
$verb.DoIt()
return $true
}
}
return $false
}
else {
Write-Error "File '$FilePath' does not exist"
}
}
Script in action on a vanilla Win7 machine
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

