Call Autohotkey script from C# - c#

I am complete beginner with C#, but advanced user with autohotkey.
If I have this script, how could I call that from C#?
ins::suspend
SendMode Input
Lbutton::
Loop
{
GetKeyState, state, Lbutton, P
if state=U
break
Sendinput {Click down}
Sleep 25
Sendinput {Click up}
Sleep 25
}
return
Could you show me simple example, so I can get to understand how to do it.

This is a may be reached through AutoHotkey.dll (that has COM Interface).
You to need download this library, move in c:\Windows\System32.
And register for the system (Run, % "regsvr32.exe AutoHotkey.dll", % "c:\Windows\System32").
Then in VS create a console application project, and choose Project tab/Add reference.
In opened window find AutoHotkey library, click on "Add" button, then close the window.
So now you have connected this library in your project, and this you'll see in reference folder.
Select all in Program.cs and replace on this code:
using System.Threading;
using AutoHotkey;
namespace work_with_AHK_object
{
class Program
{
static void Main()
{
/// write content for ahk script (thread)
string scriptContent=""
//+"#NoTrayIcon\n"
+"#KeyHistory, 0\n"
+"#NoEnv\n"
//+"ListLines, Off\n"
//+"DetectHiddenWindows, On\n"
//+"Process, Priority,, High\n"
+"SetBatchLines, -1\n"
+"SetMouseDelay, 25\n"
//+"Menu, Tray, Icon, % \"shell32.dll\", -153\n"
//+"WinSet, AlwaysOnTop, On, % \"ahk_id\"A_ScriptHwnd\n"
//+"WinSet, Style, -0xC00000, % \"ahk_id\"A_ScriptHwnd\n"
//+"WinMove, % \"ahk_id\"A_ScriptHwnd,, 888, 110, 914, 812\n"
//+"ListLines\n"
//+"ListLines, On\n"
+"TrayTip,, % \"Ready to use!\"\n" /// some notice
+""
+"Ins::\n"
+" Suspend\n"
+" Loop, % A_IsSuspended ? 1:2\n"
+" SoundBeep, 12500, 50\n"
+" KeyWait, % A_ThisHotkey\n"
+" Return\n"
+""
+"LButton::\n"
+" Loop\n"
+" Send, {Click}\n"
+" Until, !GetKeyState(\"LButton\", \"P\")\n"
+" Return\n"
+""
+"Space::\n"
+" Suspend, Off\n"
+" ExitApp";
/// initialize instance
CoCOMServer ahkThread=new CoCOMServer();
/// launch a script in a separate thread
ahkThread.ahktextdll(scriptContent);
/// wait for exit
while (ahkThread.ahkReady()!=0) Thread.Sleep(1000);
}
}
}
Open project property, in Application tab change it Output type to Windows Application.

I know this is quite an old post but I just had this issue myself and struggled quite a bit to find a solution, since I couldn´t use any of the available wrapper projects for AHK in C#.
If it´s an issue for you as well to register a the dll or to use a wrapper you can use the approach from this post on the ahk forums by basi.
Essentially you only need to place the dll in your project folder, include it into the project and set "Copy to Output Directory" to "Copy if newer" in the properties.
Then import the dll functions like this:
[DllImport(
"AutoHotkey.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode,
EntryPoint = "ahkdll")]
private static extern int ahkdll(
[MarshalAs(UnmanagedType.LPWStr)] string scriptFilePath,
[MarshalAs(UnmanagedType.LPWStr)] string parameters = "",
[MarshalAs(UnmanagedType.LPWStr)] string title = "");
[DllImport(
"AutoHotkey.dll",
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode,
EntryPoint = "ahktextdll")]
private static extern int ahktextdll(
[MarshalAs(UnmanagedType.LPWStr)] string script,
[MarshalAs(UnmanagedType.LPWStr)] string parameters = "",
[MarshalAs(UnmanagedType.LPWStr)] string title = "");
ahkdll allows to run a script from a file, ahktextdll allows to directly insert a script as a string.
I only tested this with the v1 dll from HotKeyIt (i used the one in the win32w folder).

Sleep is easy:
System.Threading.Thread.Sleep(25);
Getting the Key and MouseDown events (ins:: and Lbutton::) when your application is not the active one is a lot more complicated. It can be achieved by using global hooks. Have a look at this CodeProject article A Simple C# Global Low Level Keyboard Hook
Ultimately it depends on why you want to use C# when AHK offers you a much simpler environment to achieve similar things.
I cant think of any simple example which does the job.

Your going to want to use send keys
this video is a great example!
http://www.youtube.com/watch?v=FyDEelZbmEc

Related

How To Programmatically Execute "Clear File Explorer History"

I'm currently creating a WPF application, and like to add as a small side feature the ability to clear the windows file explorer history.
If one were to manually do this operation, it is possible via the file menu within a file explorer window,
as shown here.
My goal is pretty much to programmatically execute the same action as this button does, but I've been unable to find what executable or user32.dll method is behind this operation (if it exists), and been also unsuccessful on finding the full logic behind it (namely, finding what folder and files it targets), to replicate it.
Can you help me?
As the comment by dxiv suggested, you can achieve this via the following:
enum ShellAddToRecentDocsFlags
{
Pidl = 0x001,
Path = 0x002,
PathW = 0x003
}
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern void SHAddToRecentDocs(ShellAddToRecentDocsFlags flag, string path);
// How To Clear Everything
SHAddToRecentDocs(ShellAddToRecentDocsFlags.Pidl, null);

How to copy content of an opened notepad instance?

I need to get the text found in an notepad process instance that isn't saved on the hard-drive. For example the notepad app is opened and contains a string "This is a notepad Window" but still isn't saved, I wanna get the string inside the notepad without saving it to a file.
I must complete this task, in powershell. But if it can't be done in powershell C# is the next best option. The closest thing to my case I found in an answer on stack overflow is the following code in C#, but I couldn't really figure out how to make use of it
I've tried dumping the memory data of the notepad app, but it wasn't useful.
I've tried creating an com object for the notepad app but it wasn't doable.
Code I talked about:
[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);
private void btnCopyToNotepad_Click(object sender, EventArgs e)
{
StartNotepad();
Process[] notepads = null;
while (notepads == null || notepads.Length == 0)
{
notepads = Process.GetProcessesByName("notepad");
Thread.Sleep(500);
}
if (notepads.Length == 0) return;
if (notepads[0] != null)
{
Clipboard.SetText(textBox1.Text);
SendMessage(FindWindowEx(notepads[0].MainWindowHandle, new IntPtr(0), "Edit", null), 0x000C, 0, textBox1.Text);
}
}
private static void StartNotepad()
{
Process.Start("notepad.exe");
}
I expect to be able to copy the text from a running notepad instance without saving it on the hard-drive.
As for...
I expect to be able to copy the text from a running notepad instance
without saving it on the hard-drive.
...why are you trying to grab something that you had to put there to start with?
Or... are you saying, some other process started and wrote something to notepad?
If so, then capture why not just content from the other process before it goes to notepad?
Anyway, you can just do something like this directly and avoid all the C# stuff...
Old school...
$wshell = New-Object -ComObject wscript.shell
$wshell.AppActivate((Get-Process -Name notepad).MainWindowTitle)
$wshell.SendKeys("^{A}^{C}")
Or via .Net
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
[Microsoft.VisualBasic.Interaction]::AppActivate((Get-Process -Name notepad).MainWindowTitle)
[System.Windows.Forms.SendKeys]::SendWait("^{A}^{C}")
Update for OP
As for …
Any Chance we can get the text from the memory ? Without using the
Send Keys ?
Not without using some other 3rd UI Automation tool, or writing your own or memory dumping (but the last one is a serialization thing, soooo..., it would be just as easy to save the notepad session in a tempo location and call it up later).
There 3rdP tools around for PowerShell UI automation. Be aware some are no longer updated, though they still work.
Still, you'd have to get these on every system you need to hit.
Here is a list:
WASP
UiAutomation
selenium
FlaUI
BitCollectors.UIAutomationLib
AutoIt Scripting Language
https://www.autoitconsulting.com/site/scripting/autoit-cmdlets-for-windows-powershell
Otherwise, you end up digging at this.
Inspect
UI Automation Overview
As per the author, with FlaUI, you would do something like this.
Add-Type -Path "\Documents\WindowsPowerShell\Modules\FlaUI\src\FlaUI.UIA3\bin\Debug\FlaUI.Core.dll"
Add-Type -Path "\Documents\WindowsPowerShell\Modules\FlaUI\src\FlaUI.UIA3\bin\Debug\FlaUI.UIA3.dll"
$app = [FlaUI.Core.Application]::AttachOrLaunch('notepad')
$uia = New-Object FlaUI.UIA3.UIA3Automation
$mw = $app.GetMainWindow($uia)
$Document = $mw.FindFirstChild($uia.ConditionFactory.ByControlType([FlaUI.Core.Definitions.ControlType]::Document))
$mw.Title
$Document.Patterns.Value.Pattern.Value.Value
Sure, SendKeys can be finicky (but easy to use), and the list are more polished, but use similar approaches.

Set C# console application to Unicode output

I have a C# console application, and I was trying to do some ASCII art within it. However, some of the characters I wanted to use are Unicode. So, I was searching the internet/SO and couldn't find a consolidated answer on how to set the console to be Unicode in a C# console application.
TDLR: How do I set the console in a C# console application to be Unicode?
Edit: I did find this post after searching for something not related to this question.
It turns out that there are multiple things you need to set up in order to make the console display Unicode characters.
Set the console to a Unicode supported font. To do this, run your C# console application once with Console.ReadKey(); so the window stays open. Right click on the title bar of the window and select Properties. These options will persist when debugging through Visual Studio. You might need to use the Default menu instead for persisting the options throughout the system. In the Fonts tab, you need to set the font to Lucida Console. This font supports Unicode characters. The related post can be found here.
Set the console's code page to UTF-8. This one is a bit tricky. Because, you have to execute a command in the console window to change the code page. For whatever reason, this option is not available as a console preference. To do this, you'll need to make a separate cmd.exe process, and use this instead of the normal console provided.
var cmd = new Process
{
StartInfo =
{
FileName = "cmd.exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
CreateNoWindow = true,
UseShellExecute = false
}
};
cmd.Start();
cmd.StandardInput.WriteLine("chcp 65001");
cmd.StandardInput.Flush();
cmd.StandardInput.Close();
The first part of the code above will create a new cmd.exe process. The settings given to StartInfo will make sure that Console is redirected to this new process. The second part of the code sends a command to this console window and runs it. That command, chcp 65001, sets the console's code page to UTF-8. Related posts can be found here and here.
Set the OutputEncoding to UTF-8. This is the only way that Console.WriteLine will actually output Unicode characters. Setting this is very simple.
Console.OutputEncoding = Encoding.UTF8;
Now, any output from Console will be in Unicode. The related post can be found here.
So, that's it! I hope this information helps someone. :-)
Another option is to use P/Invoke to change the code page directly:
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleOutputCP(uint wCodePageID);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCP(uint wCodePageID);
static async Task<int> Main(string[] args)
{
SetConsoleOutputCP(65001);
SetConsoleCP(65001);
Console.WriteLine("This is how you say hello in Japanese: こんにちは");
return 0;
}
}
Output:
The solution I prefer is to change the active console code page of my profile to utf-8 using region under control panel. (see picture)
Restart and select a font which can display the necessary characters.

DllNotFoundException when importing "winmm.dll" in WP8.1

I'm trying to import winmm.dll on a WP8.1 app to try and control device volume. Based on research from Google, I have created a Windows Runtime Component to wrap the actual function call, and then I call this from the main app. Since the issue is clearly in the wrapper, here's the code:
public sealed class VolumeControl
{
[DllImport("winmm.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
internal static extern int waveOutSetVolume(IntPtr uDeviceID, int dwVolume);
public static void Set(int volume)
{
// get volume as proportion of maximum
double newVolume = ushort.MaxValue * volume / 10.0;
// convert this into volume for two channels
uint v = ((uint)newVolume) & 0xffff;
uint vAll = v | (v << 16);
// set volume
waveOutSetVolume(IntPtr.Zero, (int)vAll);
}
I have also enabled unsafe code in the wrapper's project properties. DllImport is possible for native libraries in WP8.1, as far as I understand. I don't expect this app to pass certification on the Windows Store, but still I can't see why this code wouldn't work on a developer unlocked device.
Any idea if I've missed something here?
On Windows Mobile, all waveform audio function are implemented in 'coredll.dll'. Use this DLL instead of 'winmm.dll'.
The documentation has the answer:
Requirements
Minimum supported client
Windows 2000 Professional [desktop apps only]
In other words this function is not available from Windows Store app.
Turns out that WP8.1 has a WinMMBase.dll rather than plain old winmm.dll.
I found this by running a web server hack to browse the System32 folder (see xda-developers). After downloading the dll and inspecting it with DLL Export (http://www.nirsoft.net/utils/dll_export_viewer.html), I found that it does have the waveSetOutVolume function. The function itself doesn't seem to affect the volume though, but that wasn't the point of the question I guess :)

"No Disk" error using GDAL from C#/.NET

I am using Tamas Szekeres builds of GDAL including the C# bindings in a desktop GIS application using C# and .net 4.0
I am including the entire GDAL distribution in a sub-directory of my executable with the following folder structure:
\Plugins\GDAL
\Plugins\GDAL\gdal
\Plugins\GDAL\gdal-data
\Plugins\GDAL\proj
We are using EPSG:4326, and the software is built using 32-bit target since the GDAL C# API is using p/invoke to the 32-bit libraries (could try 64 bit since Tamas provides these, haven't gotten around to it yet).
When I run my application I get the following error
This error typically happens when software tries to access a device that is no longer attached, such as a removable drive. It is not possible to "catch" this exception because it pops up a system dialog.
After dismissing the dialog using any of the buttons, the software continues to execute as designed.
The error occurs the first time I call the following method
OSGeo.OSR.CoordinateTransformation.TransformPoint(double[] inout);
The strange stuff:
The error occurs on one, and only one computer (so far)
I've run this software in several other computers both 32 and 64 bit without problems
The error does not ocurr on the first run after compiling the GDAL shim library I am using, it only occurrs on each subsequent run
it happens regardless of release, or debug builds
it happens regardless of whether the debugger is attached or not
it happens regardless of whether I turn on or off Gdal.UseExceptions or Osr.UseExceptions();
disabling removable drives causes the bug to disappear. This is not what I consider a real solution as I will not be able to ask a customer to do this.
I have tried the following:
catching the error
changing GDAL directories and environment settings
changing computers and operating systems: this worked
used SysInternals ProcMon to trace what files are being opened with no luck, they all appear to be files that exist
I re-built the computer in question when the hard drive failed, to no avail.
"cleaning" the registry using CCleaner
files in GDAL Directory are unchanged on execution
Assumptions
Error is happening in unmanaged code
During GDAL initialization, some path is referring to a drive on the computer that is no longer attached.
I am also working on the assumption this is limited to a computer configuration error
Configuration
Windows 7 Pro
Intel Core i7 920 # 2,67GHz
12.0 GB RAM
64-bit OS
Drive C: 120 GB SSD with OS, development (Visual Studio 10), etc
Drive D: 1 TB WD 10,000k with data, not being accessed for data.
The Question
I either need a direction to trap the error, or a tool or technique that will allow me to figure out what is causing it. I don't want to release the software with the possibility that some systems will have this behaviour.
I have no experience with this library, but perhaps some fresh eyes might give you a brainwave...
Firstly, WELL WRITTEN QUESTION! Obviously this problem really has you stumped...
Your note about the error not occurring after a rebuild screams out: Does this library generate some kind of state file, in its binary directory, after it runs?
If so, it is possible that it is saving incorrect path information into that 'configuration' file, in a misguided attempt to accelerate its next start-up.
Perhaps scan this directory for changes between a 'fresh build' and 'first run'?
At very least you might find a file you can clean up on shut-down to avoid this alert...
HTH
Maybe you can try this:
Run diskmgmt.msc
Change the driveletter for Disk 2 (right click) if my assumption that Disk 2 is a Removable Disk is true
Run your application
If this removes the error, something in the application is referring to the old driveletter
It could be in the p/invoked libs
Maybe see: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46501 It talks about gcc somehow compiling a driveletter into a binary
+1 Great question, but It is not possible to "catch"
Its one of these awful solutions that will turn up on DailyWTF in 5 years. But for now it is stored here http://www.pinvoke.net/default.aspx/user32.senddlgitemmessage
using Microsoft.VisualBasic; //this reference is for the Constants.vbNo;
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SendDlgItemMessage(IntPtr hDlg, int nIDDlgItem, uint Msg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
// For Windows Mobile, replace user32.dll with coredll.dll
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetDlgItemText(IntPtr hDlg, int nIDDlgItem,[Out] StringBuilder lpString, int nMaxCount);
public void ClickSaveBoxNoButton()
{
//In this example, we've opened a Notepad instance, entered some text, and clicked the 'X' to close Notepad.
//Of course we received the 'Do you want to save...' message, and we left it sitting there. Now on to the code...
//
//Note: this example also uses API calls to FindWindow, GetDlgItemText, and SetActiveWindow.
// You'll have to find those separately.
//Find the dialog box (no need to find a "parent" first)
//classname is #32770 (dialog box), dialog box title is Notepad
IntPtr theDialogBoxHandle; // = null;
string theDialogBoxClassName = "#32770";
string theDialogBoxTitle = "Notepad";
int theDialogItemId = Convert.ToInt32("0xFFFF", 16);
StringBuilder theDialogTextHolder = new StringBuilder(1000);
//hardcoding capacity - represents maximum text length
string theDialogText = string.Empty;
string textToLookFor = "Do you want to save changes to Untitled?";
bool isChangeMessage = false;
IntPtr theNoButtonHandle; // = null;
int theNoButtonItemId = (int)Constants.vbNo;
//actual Item ID = 7
uint theClickMessage = Convert.ToUInt32("0x00F5", 16);
//= BM_CLICK value
uint wParam = 0;
uint lParam = 0;
//Get a dialog box described by the specified info
theDialogBoxHandle = FindWindow(theDialogBoxClassName, theDialogBoxTitle);
//a matching dialog box was found, so continue
if (theDialogBoxHandle != IntPtr.Zero)
{
//then get the text
GetDlgItemText(theDialogBoxHandle, theDialogItemId, theDialogTextHolder, theDialogTextHolder.Capacity);
theDialogText = theDialogTextHolder.ToString();
}
//Make sure it's the right dialog box, based on the text we got.
isChangeMessage = Regex.IsMatch(theDialogText, textToLookFor);
if ((isChangeMessage))
{
//Set the dialog box as the active window
SetActiveWindow(theDialogBoxHandle);
//And, click the No button
SendDlgItemMessage(theDialogBoxHandle, theNoButtonItemId, theClickMessage, (System.UIntPtr)wParam, (System.IntPtr)lParam);
}
}
It turns out there was no way to definitely answer this question.
I ended up "solving" the problem by figuring out that there was some hardware registered on the system that wasn't present. It is still a mystery to me why, after several years, only GDAL managed to provoke this bug.
I will put the inability to catch this exception down to the idiosyncrasies involved with p/invoke and the hardware error thrown at a very low level on the system.
You could add custom error handlers to gdal. This may help:
Link
http://trac.osgeo.org/gdal/ticket/2895

Categories

Resources