1.21 jigowatts

Great Scott!

実行ポリシーについて

概要

PowerShellスクリプトを実行したらなんか実行が無効だってエラーが出たよ!それは実行ポリシーのせい。ありがたいけど、ちょっと面倒な実行ポリシーについてです。

> .\hoge.ps1
.\hoge.ps1 : このシステムではスクリプトの実行が無効になっているため、ファイル C:\src\hoge.ps1
 を読み込むことができません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。
発生場所 行:1 文字:1
+ .\hoge.ps1
+ ~~~~~~~~~~
    + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

実行ポリシーについて

スクリプトを実行する際の条件設定が以下の6種類存在する。デフォルトではRestrictedとなっているためスクリプトを実行することができない。誤操作防止的な理由ってことかな。

  • Restricted: 構成ファイルの読み込みやスクリプトの実行を行いません。既定値は "Restricted" です。
  • AllSigned: すべてのスクリプトと構成ファイルが信頼された発行元によって署名されていることを要求します (ユーザーがローカル コンピューターに書き込むスクリプトを含む)。
  • RemoteSigned: インターネットからダウンロードされたすべてのスクリプトおよび構成ファイルが、信頼された発行元によって署名されていることを要求します。
  • Unrestricted: すべての構成ファイルを読み込み、すべてのスクリプトを実行します。インターネットからダウンロードされた署名されていないスクリプトを実行する場合、スクリプトを実行する前に確認を求められます。
  • Bypass: 何もブロックされず、警告もメッセージも表示されません。
  • Undefined: 現在のスコープから現在割り当てられている実行ポリシーを削除します。このパラメーターは、グループ ポリシー スコープ内で設定された実行ポリシーは削除しません。
https://technet.microsoft.com/ja-JP/library/dd347628.aspx

実行ポリシーの確認

現在の実行ポリシーを確認するには以下のコマンドレットを実行する。-Listパラメータを渡すと優先順で実行ポリシーの一覧が取得できるぞ。

> Get-ExecutionPolicy
Bypass
> Get-ExecutionPolicy -List

        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process          Bypass
  CurrentUser    RemoteSigned
 LocalMachine      Restricted

実行ポリシーのスコープはProcessはメモリに、CurrentUserLocalMachineレジストリに格納される。また、グループポリシーでも設定することができる。

実行ポリシーの設定

実行ポリシーを変更したい場合は、管理者権限でPowerShellを起動しSet-ExecutionPolicyコマンドレットで変更することができる。ただ、ポリシー的に実行後に設定を戻しておいたほうがいいかも。
後でだと結構忘れちゃうんで-Scopeパラメータで特定のセッションに対してのみ変更をするほうがよさげ。これなら管理者権限でなくても変更できるし: )

> Set-ExecutionPolicy -Scope Process RemoteSigned -Force

PowerShell起動時に-ExecutionPolicyパラメータで指定してもOK。設定したポリシーは閉じるときに削除される。以下はコマンドプロンプトからスクリプトを実行する例。

CMD> PowerShell -ExecutionPolicy Bypass -file .\hello.ps1

実行ポリシーはひと手間かかるけど、間違えて.batファイルを実行して血の気が引く思いをするよりかはマシかも?

タスクスケジューラで時間のかかるタスクを待つ

tips

タスクスケジューラに登録したタスクを実行!

Start-ScheduledTask <タスク名>

そこそこ時間のかかるタスクで、終了を検知したかったので仕方なくポーリングすることに。
最初はschtasksコマンドでバッチ書いてたけどメンドクサってなったのでPowerShellです。

■task.ps1

Start-ScheduledTask simpletask

while ((Get-ScheduledTask simpletask).State -eq 'Running'){
    Write-Host 'Running...'
    Start-Sleep 1
}

Write-Host 'Complete!'

【C#】サイズと表示位置を指定してスクリーンキーボードを起動する

概要

ひょんなことからスクリーンキーボード(osk.exe)を起動するプログラムを作ることになりました。タイトルにあるとおり、キーボードのサイズと表示位置を制御したいとのこと。またWindowsAPIかよ(^ω^)

f:id:sh_yoshida:20180201011927p:plain

環境

Windows10
Visual Studio Community 2017

スクリーンキーボードを起動

まずはキーボードを起動してみましょう。

static void Main(string[] args)
{
    using (Process process = new Process())
    {
        process.StartInfo.FileName = "osk.exe";
        process.Start();
    }

    Console.ReadKey();
}

アラートが。

スクリーン キーボードを起動できません。

stackoverflowによると、Any CPUビルドだとダメっぽい。プロジェクトプロパティのビルドの設定で、32ビットを優先チェックボックスを外すか、x64ビルドとすれば動くとのことだったのでプラットフォームターゲットをx64でビルドすることで起動することを確認しました。

サイズと表示位置を指定する

SetWindowPosというWindowsAPIを使えば実現できそうだったのですが、引数のウィンドウハンドルがprocess.MainWindowHandleでは取れない!取得するまでにやたら時間がかかるので、タイムアウトを設けておいて取得できるまでひたすら頑張る。遅ぇぇぇ

while (0 >= (int)process.MainWindowHandle)
{
    if (IsTimeout(startTime, timeSpan))
    {
        Console.WriteLine("Timeout");
        Environment.Exit(0);
    }

    System.Threading.Thread.Sleep(1000);
    Console.WriteLine("...");
    process.Refresh();
}

これでサイズと表示位置を制御できると思ったら早かった。うまく動かないんです。Marshal.GetLastWin32Error()でエラーコードを取得してエラーメッセージを見てみたらアクセス拒否!管理者権限で実行しないとダメでした。くぅー、そんなん書いてある?見つけられなかったよ。。。

苦労したけど、なんとか動いた全部入りコードがこちら。

using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace OnScreenKeyboard
{
    class Program
    {

        private class User32
        {
            internal const UInt32 WM_SYSCOMMAND = 0x112;
            internal const UInt32 SC_RESTORE = 0xf120;
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

            [DllImport("user32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            internal static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int uFlags);

            internal const int SWP_NOSIZE = 0x0001;
            internal const int SWP_NOMOVE = 0x0002;
            internal const int SWP_NOZORDER = 0x0004;
            internal const int SWP_SHOWWINDOW = 0x0040;
            internal const int SWP_ASYNCWINDOWPOS = 0x4000;
            internal const int HWND_TOP = 0;
            internal const int HWND_BOTTOM = 1;
            internal const int HWND_TOPMOST = -1;
            internal const int HWND_NOTOPMOST = -2;
        }

        private class Kernel32
        {
            [DllImport("kernel32.dll")]
            internal static extern uint FormatMessage(uint dwFlags, IntPtr lpSource,
                uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr Arguments);

            internal const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;

            internal static string GetMessage(int errorCode)
            {
                var message = new StringBuilder(255);
                FormatMessage(
                    FORMAT_MESSAGE_FROM_SYSTEM,
                    IntPtr.Zero,
                    (uint)errorCode,
                    0,
                    message,
                    message.Capacity,
                    IntPtr.Zero
                    );
                return message.ToString();
            }
        }

        static void Main(string[] args)
        {
            var onScreenKeyboad = "osk.exe";
            IntPtr windowHandle = IntPtr.Zero;

            string processName = System.IO.Path.GetFileNameWithoutExtension(onScreenKeyboad);

            var query = from process in Process.GetProcesses()
                        where process.ProcessName == processName
                        select process;

            var keyboardProcess = query.FirstOrDefault();

            if (keyboardProcess == null)
            {
                using (Process process = new Process())
                {
                    process.StartInfo.FileName = onScreenKeyboad;
                    process.Start();

                    process.WaitForInputIdle();

                    var startTime = DateTime.Now;
                    var timeSpan = new TimeSpan(0, 0, 10);


                    while (0 >= (int)process.MainWindowHandle)
                    {
                        if (IsTimeout(startTime, timeSpan))
                        {
                            Console.WriteLine("Timeout");
                            Environment.Exit(0);
                        }

                        System.Threading.Thread.Sleep(1000);
                        Console.WriteLine("...");
                        process.Refresh();
                    }

                    windowHandle = process.MainWindowHandle;


                    var result = User32.SetWindowPos(windowHandle, 0, 0, 500, 1000, 300, User32.SWP_NOZORDER | User32.SWP_SHOWWINDOW);
                    if (!result)
                    {
                        var errorCode = Marshal.GetLastWin32Error();
                        var message = Kernel32.GetMessage(errorCode);
                        Console.WriteLine(message);
                    }
                }
            }
            else
            {
                windowHandle = keyboardProcess.MainWindowHandle;
                User32.SendMessage(windowHandle, User32.WM_SYSCOMMAND, new IntPtr(User32.SC_RESTORE), new IntPtr(0));
                Console.WriteLine("ReOpen");
            }

            Console.WriteLine("Standby");
            Console.ReadKey();
        }

        static bool IsTimeout(DateTime startTime, TimeSpan timeSpan)
        {
            if (DateTime.Now - startTime > timeSpan)
            {
                return true;
            }
            return false;
        }
    }
}

頭で考えるな、感じるんだ!