Use PowerShell to send message to a window Control - c#

How to use powershell send a message to a window Control? I have a send example under C#, but I don't know how to write code in powershell.
//using System.Runtime.InteropServices;
[DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessageS(IntPtr hWnd, int Msg, uint wParam, string lParam);
[DllImport("user32.dll", EntryPoint = "FindWindowW", CharSet = CharSet.Unicode)]
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void TestQm2SendMessage()
{
var hwnd = FindWindow("QM_Editor", null);
if(hwnd == default(IntPtr)) return;
SendMessageS(hwnd, 12, 1, "Q ' M 'Macro295' C test C#");
}
I tried to convert C# code to powershell code, but it didn't work. Any suggestions are welcome
$code = #'
[DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)] public static extern IntPtr SendMessageS(IntPtr hWnd, int Msg, uint wParam, string lParam);
[DllImport("user32.dll", EntryPoint = "FindWindowW", CharSet = CharSet.Unicode)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
'#
$myAPI = Add-Type -MemberDefinition $code -Name myAPI -PassThru
$myAPI::SendMessageS($myAPI::FindWindow("QM_Editor", $Null), 12, 1, "Q ' M 'Macro295' C test C#");

Found on the Internet
function Out-Notepad
{
param
(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[String]
[AllowEmptyString()]
$Text
)
begin
{
$sb = New-Object System.Text.StringBuilder
}
process
{
$null = $sb.AppendLine($Text)
}
end
{
$text = $sb.ToString()
$process = Start-Process notepad -PassThru
$null = $process.WaitForInputIdle()
$sig = '
[DllImport("user32.dll", EntryPoint = "FindWindowEx")] public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll")] public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
'
$type = Add-Type -MemberDefinition $sig -Name APISendMessage -PassThru
$hwnd = $process.MainWindowHandle
[IntPtr]$child = $type::FindWindowEx($hwnd, [IntPtr]::Zero, "Edit", $null)
$null = $type::SendMessage($child, 0x000C, 0, $text)
}
}
#Get-Content -Path 'ะก:\Folder\File.txt' | Out-String | Out-Notepad
'Send this text to Notepad' | Out-Notepad

You can use .NET System.Windows.Forms.MessageBox. This will also work in Powershell.
[System.Windows.Forms.MessageBox]::Show("Your message here!", 'This is my title', 'YesNo', 'Information')

Related

Function in DLL always returns null

I'm writing a dll in C++ to get the serial hd, this is the code of the function:
__declspec(dllexport) char* __stdcall char* GetSerialHD()
{
WmiQueryResult res = getWmiQueryResult(L"SELECT SerialNumber FROM Win32_PhysicalMedia", L"SerialNumber");
char* ret = NULL;
std::string strValue;
char* strReturn = NULL;
for (const auto& item : res.ResultList) {
strValue.assign(item.begin(), item.end());
ULONG ulSize = strlen(MyString) + sizeof(char);
strReturn = (char*)::CoTaskMemAlloc(ulSize);
strcpy(strReturn, strValue.c_str());
break;
}
return strReturn;
}
The dll function calls it from an application written in C#:
[DllImport("mydll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetSerialHD();
...
string GetHD = GetSerialHD();
This function GetSerialHD() always returns null, but the same code in a console application C++ for testing works well.

Powershell Using pinvoke and low-level variables

I'm trying to create a script in PowerShell that extracts [ProductCode] from .msi package located somewhere on disk. I found that I need to use next two methods: MsiOpenPackage and MsiGetProperty. Based on that, I wrote next code snippet:
$signature_GetProperty = #'
[DllImport("msi.dll", CharSet=CharSet.Unicode)]
public static extern int MsiGetProperty(
int hInstall,
string szName,
[Out] StringBuilder szValueBuf,
ref int pchValueBuf);
'#
$signature_OpenPackage = #'
[DllImport("msi.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true, ExactSpelling = true)]
public static extern UInt32 MsiOpenPackageEx(
string szPackagePath,
UInt32 dwOptions,
void **hProduct);
'#
$OpenPackageType = Add-Type -MemberDefinition $signature_OpenPackage -Name "WinMsiOpenPackageEX" -Namespace Win32Functions -PassThru
$OpenPackageType::MsiOpenPackageEx($path, 1, STRUGGLING HERE)
$GetInfoType = Add-Type -MemberDefinition $signature_GetProperty -Name "WinGetProperty" -Namespace Win32GetProductCodeMSI -Using System.Text -PassThru
$GetInfoType::MsiGetProperty(AND HERE, "ProductCode",
I'm struggling with how should I declare and use variables that are defined as parameters MsiGetProperty and MsiOpenPackageEx.
For instance, last parameter in OpenPackage is void **hProduct. How should I declare it in .ps1 script in order to use later within MsiGetProperty function. The same goes with ref int pchValueBuf.
I'm sorry for such a lame question, but I would really appreciate any help or clarification or article to read about this type of issue.
Here's a snippet we're using internally:
Add-Type -TypeDefinition #"
using System;
using System.Runtime.InteropServices;
using System.Text;
public static class Msi
{
public static string GetProductVersion(string fileName)
{
IntPtr hInstall = IntPtr.Zero;
try
{
uint num = MsiOpenPackage(fileName, ref hInstall);
if ((ulong)num != 0)
{
throw new Exception("Cannot open database: " + num);
}
int pcchValueBuf = 255;
StringBuilder szValueBuf = new StringBuilder(255);
num = MsiGetProperty(hInstall, "ProductVersion", szValueBuf, ref pcchValueBuf);
if ((ulong)num != 0)
{
throw new Exception("Failed to Get Property ProductVersion: " + num);
}
return szValueBuf.ToString();
}
finally
{
if(hInstall != IntPtr.Zero)
{
MsiCloseHandle(hInstall);
}
}
}
[DllImport("msi.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
private static extern int MsiCloseHandle(IntPtr hAny);
[DllImport("msi.dll", CharSet = CharSet.Unicode, EntryPoint = "MsiOpenPackageW", ExactSpelling = true, SetLastError = true)]
private static extern uint MsiOpenPackage(string szDatabasePath, ref IntPtr hProduct);
[DllImport("msi.dll", CharSet = CharSet.Unicode, EntryPoint = "MsiGetPropertyW", ExactSpelling = true, SetLastError = true)]
private static extern uint MsiGetProperty(IntPtr hInstall, string szName, [Out] StringBuilder szValueBuf, ref int pchValueBuf);
}
"#
[Msi]::GetProductVersion("R:\PathTo.msi")

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?
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

Exporting an excel range to an EMF file in specified location

I'm having trouble getting my range exported to an emf file. It does get copied to the clipboard because I can paste it after the exception is thrown:
An exception of type 'System.NullReferenceException' occurred in
N1Narrator.dll but was not handled in user code Additional
information: Object reference not set to an instance of an object.
<--referencing 'img'
Here's my code:
if (intersectRange != null && name.RefersToRange.Cells.Count > 1)
{
name.RefersToRange.CopyPicture(Excel.XlPictureAppearance.xlScreen, Excel.XlCopyPictureFormat.xlPicture);
const int CF_ETAFILE = 14;
IntPtr intptr;
System.Drawing.Imaging.Metafile myMetaFile = null;
if (ClipboardFunctions.OpenClipboard(IntPtr.Zero))
{
if (ClipboardFunctions.IsClipboardFormatAvailable(CF_ETAFILE) != 0)
{
intptr = ClipboardFunctions.GetClipboardData(CF_ETAFILE);
myMetaFile = new Metafile(intptr, true);
ClipboardFunctions.CloseClipboard();
myMetaFile.Save(#"C:\Users\Nick\AppData\Local\Temp\Narrative1\N1Appraisal\ExcelTables\testtable.emf", ImageFormat.Emf);
}
}
}
I've looked into DataObject() as well for the Clipboard but to no avail. I don't want to use the Chart method, it'll be too slow.
Assuming the problem is using an Image datatype to save an EMF vector image not a bitmap from the Clipboard, you can use this code :
//using System.Runtime.InteropServices;
public class ClipboardFunctions
{
[DllImport("user32.dll", EntryPoint = "OpenClipboard", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenClipboard(IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "EmptyClipboard", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EmptyClipboard();
[DllImport("user32.dll", EntryPoint = "SetClipboardData", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetClipboardData(int uFormat, IntPtr hWnd);
[DllImport("user32.dll", EntryPoint = "CloseClipboard", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool CloseClipboard();
[DllImport("user32.dll", EntryPoint = "GetClipboardData", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr GetClipboardData(int uFormat);
[DllImport("user32.dll", EntryPoint = "IsClipboardFormatAvailable", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern short IsClipboardFormatAvailable(int uFormat);
}
private void button1_Click(object sender, EventArgs e)
{
const int CF_ETAFILE = 14;
IntPtr intptr;
System.Drawing.Imaging.Metafile myMetaFile = null;
if (ClipboardFunctions.OpenClipboard(this.Handle))
{
if (ClipboardFunctions.IsClipboardFormatAvailable(CF_ETAFILE) != 0)
{
intptr = ClipboardFunctions.GetClipboardData(CF_ETAFILE);
myMetaFile = new System.Drawing.Imaging.Metafile(intptr, true);
ClipboardFunctions.CloseClipboard();
myMetaFile.Save(#"c:\test.jpg", ImageFormat.Jpeg);
}
}
}
Here is a similar question/answer.
If you're not sure what format the image inside Excel is, then instead of calling Clipboard.GetImage(), try calling Clipboard.GetDataObject()
This returns an IDataObject, which you can in turn query by calling dataObject.GetFormats(). GetFormats() returns the type formats supported by the Clipboard object - there may be a more precise format supported that you can use to extract the data (but I am pretty sure Excel does use EMF for graphs).
Forgive me for my lack of skills in Stackoverflow comments.
sel.CopyPicture(Appearance: Excel.XlPictureAppearance.xlScreen, Format: Excel.XlCopyPictureFormat.xlBitmap);
var image = System.Windows.Forms.Clipboard.GetImage();
This code works fine for me for Excel 2013 addin. Notice I am using xlBitmap instead of xlPicture. Does this meet your requirements? Or I will give a try with xlPicture as well.

Impersonation in Windows Server 2008

I am trying to impersonate a specific user to perform some sql operations in our server. This is not a ASP.Net application. I used the provided code before and it worked. But, recently we have upgraded our environment from windows server 2000 to windows server 2008 R2. After that upgrade this code is not working for me. I need some help in understanding this problem and help solving it. Any and every help will be appreciated. Thanks.
The provided code is a pseudo code, trying to write to a file and do a sql operation.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Runtime.InteropServices;
using System.Text;
using System.IO;
using System.Security.Principal;
using System.Security.Permissions;
[assembly: SecurityPermissionAttribute(SecurityAction.RequestMinimum, UnmanagedCode = true)]
[assembly: PermissionSetAttribute(SecurityAction.RequestMinimum, Name = "FullTrust")]
public class Test
{
const int LOGON32_LOGON_INTERACTIVE = 2;
const int LOGON32_LOGON_NETWORK = 3;
const int LOGON32_LOGON_BATCH = 4;
const int LOGON32_LOGON_SERVICE = 5;
const int LOGON32_LOGON_UNLOCK = 7;
const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
const int LOGON32_PROVIDER_DEFAULT = 0;
const int SecurityImpersonation = 2;
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken
);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern int ImpersonateLoggedOnUser(
IntPtr hToken
);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RevertToSelf();
[DllImport("kernel32.dll", SetLastError = true)]
static extern int CloseHandle(IntPtr hObject);
public void TestImpersonation()
{
IntPtr lnToken = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
StringBuilder sb = new StringBuilder();
int TResult = LogonUser("itservices", "DFC", "St4hls345t", LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT, out lnToken);
if (TResult > 0)
{
bool retVal = DuplicateToken(lnToken, SecurityImpersonation, ref dupeTokenHandle);
if (false == retVal)
{
CloseHandle(lnToken);
Console.WriteLine("Exception thrown in trying to duplicate token.");
return;
}
WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
writeLog(DateTime.Now.ToString(#"MM-dd-yyyy HH:mm:ss") + " - Impersonation Applied" + Environment.NewLine);
runQuery();
impersonatedUser.Undo();
writeLog(DateTime.Now.ToString(#"MM-dd-yyyy HH:mm:ss") + " - Impersonation Reverted" + Environment.NewLine);
runQuery();
CloseHandle(lnToken);
}
else
{
writeLog(DateTime.Now.ToString(#"MM-dd-yyyy HH:mm:ss") + " - Impersonation not Applied" + Environment.NewLine);
}
return;
}
void writeLog(string message)
{
try
{
string filePath = #"E:\Impersonate\Testlog.txt";
File.AppendAllText(filePath, message);
}
catch
{
Console.WriteLine();
}
}
void runQuery()
{
SQLOperations sqlUtill = new SQLOperations();
string cmdTxt = "SELECT * FROM [tblChildOrder] where [StahlsWorkOrderID] = 'DREAMFUL0015799'";
DataTable dt = sqlUtill.executeQuery(cmdTxt);
if (dt != null)
{
Console.WriteLine();
}
else
{
Console.WriteLine();
}
}
}
Most upgrade that broke my code usually was caused by the upgrade changing permission to users. Double check the users, the permission they have and you should find the problem.

Categories