佐々木屋

技術的なことから趣味まで色々書きます

管理者権限で自動実行されない・・・

Windows7と8(8.1)まではスタートアップから問題なく管理者権限で実行できていましたが、Windows10からは実行不可となっています。

UACを解除したり、アプリケーションマニフェストを変更したりしてみましたが効果ナシ・・・。ということで、別の方法を模索していましたが、サービスにするほどでもない(起動時一発で済むような)ので、最終的にタスクスケジューラにする方法で落着。

タスクスケジューラを作成します。
f:id:sasaki816:20191222221610j:plain:w600

作成画面の「トリガー」で、「ログオン時」を選択します。
f:id:sasaki816:20191222221615j:plain

あとはお好みで設定します。
マニフェストを変更しないのであれば、管理者権限で実行するにチェックするのをお忘れなく。


UACについてはこちら。
sasaki816.hatenablog.com


マニフェスト変更についてはこちら。
sasaki816.hatenablog.com

トリガーの登録

SQLServerのトリガ登録の覚えです。

INSERTやUPDATEなどでテーブルに変更があった場合に処理を呼ぶ場合に使用するのが「トリガ」です。
トリガーはテーブルごとの設定になります。

CREATE TRIGGER [ トリガー名 ] ON [ 対象テーブル名 ] 
AFTER { [ INSERT] , [ UPDATE ] , [ DELETE ] } 
AS BEGIN
[ 実行したいスクリプト ]
END



例えば、テーブル更新時に登録日時を自動で入れたい場合などは以下のようにします。

CREATE TRIGGER [dbo].[trig_取引明細TEST] ON  [dbo].[取引明細TEST] 
AFTER INSERT
AS BEGIN
UPDATE 取引明細EST SET 売上日 = getdate() WHERE 支店CD = (SELECT 支店CD FROM inserted)
END



insertedテーブルにはINSERT、UPDATEステートメント実行で影響を受けた行がコピーされます。そこのキーを指定すれば実テーブルを更新できる、というわけです。

従って、insertedテーブルにキーが無く、更新テーブルにもキーが無い場合は、実テーブルの意図しない行が更新されてしまう可能性がありますので注意が必要ということになります。


なお、設定はテーブルの「トリガー」に格納されます。
f:id:sasaki816:20191002131330j:plain

iPhoneのSafari対応 Javascriptからクリップボードを設定

会社の携帯が変更になるようで、それに伴い色々設定作業が入ります。
設定作業自体は部下にやらせるとして、やはり不用意にパスワードなどは見せたくないわけで。

そうした時にパスワードをクリップボード経由で扱えばいいのですが、iPhoneクリップボードはちょっと特殊のようで。


仕組みとして直接クリップボードへ値を入れることは出来ません。但し、テキストボックスの値を疑似的に選択状態にすればクリップボードへの代入が可能になります。

手順としては、ダミーのテキストボックス(txtPassword )を1個用意してそこに値を設定し、その値を選択状態にしてクリップボードへコピーします。

var txtPassword = document.getElementById("txtPassword ");
var range = document.createRange();
range.selectNode(txtPassword);
window.getSelection().addRange(range);
document.execCommand('copy');
alert("コピーしました。");

後は空文字をテキストボックスに代入し、それを同様の処理でクリップボードへコピーすればクリップボード内を削除出来ます。ダミーのテキストボックスの値は文字色を白にしたり、コピー直後にリダイレクトして値を削除したり、色々工夫すれば担当者レベルであれば目くらましになるでしょう。

MIMETypeの取得

ファイルのMIMETypeを取得する方法です。
.NET Frameworkによって違いますので、お好みの方をどうぞ。

.NET Framework4.5以上

System.Web.MimeMappingクラスのGetMimeMappingメソッドへファイル名を渡すことで取得可能です。

System.Web.MimeMapping.GetMimeMapping("ファイル名");
System.Web.MimeMapping.GetMimeMapping("ファイル名")

GetMimeMappingメソッドはフルパスを渡しても結果的には一緒です。恐らく内部的にファイル拡張子から判断しているものと思われます。

ですので、拡張子が認識できない場合はnullを返さずoctet-streamを返します。ファイル有無も関係ありません。

.NET Framework4.0まで

System.Web.MimeMappingクラスでは取得できませんので、ClassRootレジストリからContent Typeを取得する方法となります。

使い方によりけりですが、GetMimeMappingメソッドと合わせるならデフォルトでoctet-streamを返す仕様が好ましいです。あえてnullを返すという手もありますが、臨機応変にどうぞ。

private string GetMimeTypeByExtension(string ext) {
    string res = "application/octet-stream";

    try {
        var key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
        if (key != null) {
            var mimetype = key.GetValue("Content Type");
            if (mimetype != null) res = (string)mimetype;
        }
    }
    catch { }

    return res;
}
Private Function GetMimeTypeByExtension(ByVal ext As String) As String
    Dim res As String = "application/octet-stream"

    Try
        Dim key = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext)
        If key IsNot Nothing Then
            Dim mimetype = key.GetValue("Content Type")
            If mimetype IsNot Nothing Then res = CType(mimetype, String)
        End If
    Catch ex As Exception
    End Try

    Return res
End Function



if文のところはnull条件演算子を使えばもっと簡潔に書けますが、.NET Framework4.0では使用できないので少し読みにくいですが仕方ありません。

IIS管理外の画像ファイルを表示させる

ASP.NETIIS管理内のディレクトリの画像ファイルをImageコントロールへ動的に表示させるためには以下で簡単にできますが、

img.ImageUrl = ”相対パス”

これを別のディレクトリに置いた画像を表示させたい場合は、System.Drawing名前空間のBitmapクラスへインスタンス展開して、それをResponse.OutputStreamに向けて保存すれば表示可能です。

protected void Page_Load(object sender, EventArgs e) {
    Bitmap bmp = new Bitmap(@"pngファイル絶対パス");
    Response.ContentType = "image/png";
    Response.Flush();
    bmp.Save(Response.OutputStream, ImageFormat.Png);
    Response.End();
}
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim bmp As New Bitmap("pngファイル絶対パス")
    Response.ContentType = "image/png"
    Response.Flush()
    bmp.Save(Response.OutputStream, ImageFormat.Png)
    Response.End()
End Sub



で、ここまでは簡単な話ですが、ここで終わってはツマランわけで。
折角なので、MIMEや保存時のImageFormatを表示させるファイルの拡張子によって動的に設定できれば汎用性はぐっとあがります。

最初の1行目のビットマップクラスへの展開によって、ContentTypeとImageFormatを動的に取得するようなクラスを考えればよいことになります。

オブジェクト指向の観点を考慮しつつ構築する演習としてみましょう。
ということで、このまま演習へ引き継ぎます。

Strategyパターン

デザインパターンの中でもよく見るものですが、オブジェクト指向が分からないとチンプンカンプンになります。

「戦略」という意味で、メソッドなどにちりばめられた静的なアルゴリズムを、ifやswitch(VB.NETはSelect Case)などの条件分岐で動的にアルゴリズムごと変更してしまう手法です。

このパターンによってアルゴリズムに変更が生じても、条件分岐で意図しない影響が出ないようになります。

まずインターフェースを定義します。

Public interface IStrategy {
    void Work();
}
Public Interface IStrategy
    Sub Walk()
End Interface

次にそれらを実装したクラスを定義します。

public class Class1 : IStrategy {
    public void Walk() {
        Console.WriteLine("テクテク歩く");
    }
}

public class Class2 : IStrategy {
    public void Walk() {
        Console.WriteLine("ノソノソ歩く");
    }
}

public class Class3 : IStrategy {
    public void Walk() {
        Console.WriteLine("サクサク歩く");
    }
}
Public Class Class1
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("テクテク歩く")
    End Sub
End Class

Public Class Class2
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("ノソノソ歩く")
    End Sub
End Class

Public Class Class3
    Implements IStrategy

    Public Sub Walk() Implements IStrategy.Work
        Console.WriteLine("サクサク歩く")
    End Sub
End Class

最後に、インターフェースを通じて必要な処理を返す(委譲させるための)クラスを定義します。

public class Context {
    public IStrategy Strategy { set; get; }

    public void Execute() {
        Strategy.Walk();
    }
}
Public Class Context
    Public Property Strategy As IStrategy

    Public Sub Execute()
        Strategy.Walk()
    End Sub
End Class

こうすることで、メインコードからアルゴリズムを切り離してカプセル化することにより、不用意な操作やアルゴリズムの変更に対応できるようになります。

var cls = new Context();

cls.Strategy = new Class1();
cls.Execute();

cls.Strategy = new Class2();
cls.Execute();

cls.Strategy = new Class3();
cls.Execute();
Dim cls As New Context()

cls.Strategy = New Class1()
cls.Execute()

cls.Strategy = New Class2()
cls.Execute()

cls.Strategy = New Class3()
cls.Execute()

オブジェクト指向の導入

課題

手続き型でよく見る配列に情報を持つ場合(カンマ区切りなども含めて)があります。一番の問題は何番目に何のデータを保持しているのかが分かりにくいということです。これは非常に危険なデータの持ち方でもあります。

string[] person = new string[2];
person[0] = "山田太郎";
person[1] = "21";
person[2] = "090********";
Dim person(2) As String
person(0) = "山田太郎"
person(1) = "21"
person(2) = "090********"


リファクタリング

オブジェクト指向を導入することで一気に解決します。Personクラスをオブジェクトとして構築し、そこで管理させればよいのです。

public class Person {
    public string KanjiName { get; set; }
    public int Age { get; set; }
    public string Tell { get; set; }
}

Person p = new Person();
p.KanjiName = "山田太郎";
p.Age = 21;
p.Tell = "090********";
Public Class Person
    Public Property KanjiName As String
    Public Property Age As Integer
    Public Property Tell As String
End Class

Dim p As New Person()
p.KanjiName = "山田太郎"
p.Age = 21
p.Tell = "090********"

クラス化することでプロパティで桁数チェックや体裁チェックなども可能となります。その他にも、複数のインスタンスを作成してジェネリックコレクションにすれば、配列によるインデックス管理も行わなくてよくなります。