2024年3月22日金曜日

古いファイルを消すすくリプト(Windows , vbs)

概要

Windows環境でバックアップを掃除するツール
バッチファイルとvbsの組み合わせでTask起動して使う想定です

使い方

CSCRIPT LogDelete.vbs D:\Folder days > backupexec.log
CSCRIPT LogDelete.vbs D:\Folder1\ 30 > bkfiledl.log
CSCRIPT LogDelete.vbs D:\Folder2\ 60 >> bkfiledl.log

コード

Option Explicit

'CScript myname.vbs %1 %2 %1=フォルダ , %2=日数

    Dim DeleteLogPath    ' 削除基準フォルダ
    Dim DelDayCount      ' 削除する判断を行う日数
    
    Dim fso              ' FileSystemObjects
    Dim oTargetFolder    ' Folder Objects
    Dim oFile            ' File Objects
    'dim objRE

    'Set objRE = CreateObject("VBScript.RegExp")
    
    DeleteLogPath = Wscript.Arguments.Item(0)        ' 引数としてフォルダを受け取る。ない場合には例外
    DelDayCount   = Wscript.Arguments.Item(1)        ' 引数として、削除する過去日数を受け取る。ない場合には例外
    'objRE.Pattern = "^" & Wscript.Arguments.Item(2) & "$"

    'Object Create
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set oTargetFolder = fso.GetFolder(DeleteLogPath)
    
    WSCript.Echo (Now & " >> 指定された日以前のファイルを削除する ")
    WScript.Echo ("処理対象ディレクトリ >> " & DeleteLogPath)

    'File Delete
    For Each oFile In oTargetFolder.Files
    	'If objRE.Test(oFile.Name) Then
	        If DateAdd("d",DelDayCount,oFile.DateLastModified) < Now Then
	            Wscript.Echo (oFile.Name & " **** " & oFile.DateLastModified)
	            oFile.Delete
	        End If
        'End If
    Next

2023年1月23日月曜日

Excelの行列を文字から数値へ (26進数変換 vba)

いわゆる26進数変換です
VBAでコード書いていると列や行の位置を指定する事がありますが、固定文字列でコーディングすると変更があった際に面倒なので
基本的には数字で定義しておいて、変化に強くしておきます。 
 実装、テスト済なのでそのまま貼ってお使いください。
テスト実装も記載済です



Option Explicit

Public Function ToBase26(ByVal self As Long) As String
    If self <= 0 Then
        ToBase26 = ""
        Exit Function
    End If
    
    Dim n As Long
    n = IIf((self Mod 26 = 0), 26, self Mod 26)
    
    If self = n Then
        ToBase26 = Chr((n + 64))
    Else
        ToBase26 = ToBase26((self - n) / 26) & Chr(n + 64)
    End If
    
End Function

Public Function FromBase26(ByVal self As String) As Long

    Dim charLength As Integer
    Dim i As Integer
    Dim steps As Integer
    
    If self = "" Then
        FromBase26 = 0
        Exit Function
    End If
    Dim result As Long
    
    Dim chars As Byte
    charLength = Len(self)

    For i = charLength - 1 To 0 Step -1
        Dim current As Integer
        chars = asc(Mid(self, i + 1, 1))
        current = CInt(chars) - 64      'ASC To 26
        If current < 1 Or current > 26 Then
            FromBase26 = 0
            Exit Function
        End If
        If (steps = 0) Then
            result = result + current
        Else
            result = result + (current * (26 ^ steps))
        End If
        steps = steps + 1
    Next

    FromBase26 = result
End Function

Public Function ToBase26Test()

    Dim i As Integer
    Dim tmp As String
    Dim ar As String
    For i = 1 To 2000
        tmp = ToBase26(i)
        ar = ar + tmp & vbTab
    Next
    
    Debug.Print ar

End Function

Public Function FromBase26Test()

    Dim tmp As Integer
    Dim i As Integer
    
    'ToBase26のテストがOKであれば本テスト実施可能
    
    For i = 1 To 2000
        tmp = FromBase26(ToBase26(i))
        If (tmp <> i) Then
            Debug.Print (ToBase26(i))
            Stop
        End If
    Next

    ' Stopしなければ全部OK


End Function

2022年9月21日水曜日

複数のExcelファイルのシートをひとまとめにする

必要に応じ都度作っている気がするのでテンプレとして残しておきます。 概略:複数のExcelファイルのシートを全部自分の場所へコピーしてくる 備考:エラーハンドリングなどはしてません
Option Explicit
Public Sub MergeDocuments()

    Const listIndex As String = "A"
    
    Dim i As Long
    Dim list As Collection
    Dim wk As String
    
    Dim currentwb As Excel.Workbook
    Dim currentws As Worksheet
    
    Dim wb As Excel.Workbook
    Dim ws As Excel.Worksheet
    
    Set list = New Collection
    
    'Step 1 シートの内容から対象ファイルのフルパスを取得する
    i = 1
    Do
        wk = ActiveSheet.Range(listIndex & CStr(i)).Cells(1, 1).Value
        
        If Len(wk) > 0 Then
        
            list.Add wk
        Else
            Exit Do
        End If
        i = i + 1
    Loop

    Set currentwb = ThisWorkbook
    Set currentws = ActiveSheet
    
    'Step 2 ファイルを順次開き、自分にコピーする
    Application.DisplayAlerts = False
    For i = 1 To list.Count
        wk = list(i)
        Set wb = Excel.Application.Workbooks.Open(wk, False, True)
        
        For Each ws In wb.Worksheets
            ws.Copy before:=currentwb.Worksheets(currentws.Index)
        Next
        wb.Close
        Set wb = Nothing
    Next

    MsgBox "Success"

End Sub

2020年6月8日月曜日

mysql(5.1) + カーソルloop使ったプログラムのテンプレ

たまに、ツールとしてデータベースプログラムを書くのですが、1から書くのではなく、いつもどこかの作成済みプログラムを流用しているので、流用も面倒くさくなり、テンプレ化することにしました。
他の人にも使ってもらえるよう、コメント多めです。

【概略】
データを取得して、繰り返しする処理のテンプレです。
データベースへの書き込み処理も考慮してトランザクションも記載してあります。

【コード】


-- プロシージャ入れ替え用のDROP
DROP PROCEDURE IF EXISTS PROCEDURENAME;
delimiter ///
CREATE DEFINER = `root`@`localhost` PROCEDURE `PROCEDURENAME`
 (IN param1 VARCHAR(10)
 ,IN param2 VARCHAR(10))
BEGIN
    -- Loop検知・保持する変数
    DECLARE done INT DEFAULT 0;
    -- 通常変数を記載する際にはカーソルの前に書く
    DECLARE local_aa VARCHAR(10);
    DECLARE local_bb VARCHAR(10);
    -- カーソル定義
    DECLARE l_cur CURSOR FOR select a ,b from xx; -- カーソル定義

-- データなしを検知するハンドラ
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
-- 例外を検知するハンドラ
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
    ROLLBACK;
    SET autocommit=1;
    SELECT 0 AS RESULT;
END;

-- メイン処理
-- オートコミットを切って、自立トランザクションにしています
    SET autocommit=0;
    START TRANSACTION;

-- カーソルを開く
    open l_cur;
-- 繰り返し処理
    REPEAT
-- 1件データ取得
        FETCH l_cur into local_aa , local_bb;
-- EOF判定
        IF NOT done THEN
            -- データ処理
            -- これを書いておくとコンソールにデバッグのように出力される
            SELECT now() AS CURRENT_DATETIME local_aa AS aa , local_bb AS bb;

            -- この例では別ストアドを呼ぶ
            call sub_procedure(param1,param2,local_aa,local_bb);
        END IF;

    UNTIL done END REPEAT;

-- 後片づけ
    close l_cur;

    COMMIT;
    SET autocommit=1;

-- 呼び出し元用に正常終了を返す    
    SELECT 1 AS RESULT;

end;
///
delimiter ;

【Tips】mysqlのfunctionでテキストの行数を数える

いつ何のために作ったか忘れたのですが、ごみ箱行きはもったいないのでここに残しておきます。

キーワード:
・テキストの行数を数える
・functionの作り方
・Loopの使い方



drop function if exists util_getRowCount;
DELIMITER ///
CREATE DEFINER=`root`@`localhost` function util_getRowCount(target longtext)
returns varchar(1000)
-- returns decimal(11,0)
BEGIN
    declare lineMaxCount    int;
    declare sLine           VARCHAR(100);
    declare retValue        decimal(11,0);
    declare lineCount       int;
    declare i               int;
    declare isComment       int;
    declare isBlank         int;
    declare lCodeCharCount  int;
    declare currentTarget   longtext;
    declare vDebug          VARCHAR(10000);
    -- 引数チェック
    if CHAR_LENGTH(target) = 0 THEN
        return 0;
    END IF;

    -- 変数初期化
    set i = 0;
    set lineCount = 0;
    set vDebug    = '';
    set lCodeCharCount = 0;

    -- 利用変数準備
    select CHAR_LENGTH(target) - CHAR_LENGTH(REPLACE(target,'\n','')) +1 into lineMaxCount;
    set currentTarget = REPLACE(target,'\r\n','\n');
    set currentTarget = REPLACE(target,'\r','\n');
    charLoopLabel: LOOP
        -- increment 
        SET i = i + 1;

        -- 抜ける条件
        IF i > lineMaxCount THEN
            LEAVE charLoopLabel;
        END IF;

        -- 行として判定する
        set sLine = replace(substring_index(currentTarget,'\n',1),'\t',' ');
        set isComment = 0;
        set isBlank   = 0;
        
        -- コメント判定
        if substr(trim(sLine),1,2)= '--' THEN
            set isComment = 1;
        end if;

        -- 空行判定
        if length(trim(REPLACE(sLine,'\t',''))) = 0 THEN
            set isBlank = 1;
        end if;

        if (isComment = 0 and isBlank = 0) THEN
            SET lineCount = lineCount + 1;
            set lCodeCharCount = lCodeCharCount + CHAR_LENGTH(sLine);
        end if;
        -- 判定した行を取り除く
        set currentTarget = substr(currentTarget,CHAR_LENGTH(sLine) + 2);
        
        -- set vDebug = CONCAT(vDebug,isComment,isBlank,'xx',sLine,'_');
    END LOOP charLoopLabel;
    return CONCAT(lineCount , ',' , lCodeCharCount);
    -- return lineCount;
    -- set vDebug = CONCAT(vDebug,'__result__',lineCount);
    -- return vDebug;

-- return CHAR_LENGTH(target);

end;
///
delimiter ;
-- select util_getRowCount('12345\n22345\n33345') AS '3';
-- select util_getRowCount('12345\n22345\n33345\n\n\n\n\n') AS '3';
-- select util_getRowCount('12345\n22345\n33345\n\n\n\n\n    -- comment \n -- comment2\n44444') AS '4';
-- select util_getRowCount('日本語\n日本語2\n日本語3\n\n\n\n\n    -- コメント1 \n -- comment2\n44444\n') AS '4';

2019年10月21日月曜日

【未解決】CSVダウンロードを行うと、ブラウザ(Edge)がクラッシュする

自社製品で発生した事象。未解決です。

事象

CSVダウンロードを行うと、ブラウザ(Edge)がクラッシュする

Windowsのアプリケーションログ

障害が発生しているアプリケーション名: MicrosoftEdge.exe、バージョン: 11.0.18362.418、タイム スタンプ: 0x5d995b38
障害が発生しているモジュール名: EMODEL.dll、バージョン: 11.0.18362.418、タイム スタンプ: 0x5d9957cb
例外コード: 0xc0000409
障害オフセット: 0x000000000035b4f4
障害が発生しているプロセス ID: 0x3edc
障害が発生しているアプリケーションの開始時刻: 0x01d584b9670fceb4
障害が発生しているアプリケーション パス: C:\WINDOWS\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\MicrosoftEdge.exe
障害が発生しているモジュール パス: C:\WINDOWS\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\EMODEL.dll
レポート ID: 04e20477-ddaf-4338-bfe2-39c8d13a63e3
障害が発生しているパッケージの完全な名前: Microsoft.MicrosoftEdge_44.18362.387.0_neutral__8wekyb3d8bbwe
障害が発生しているパッケージに関連するアプリケーション ID: MicrosoftEdge

確認したこと

ポップアップブロック →元々Off
Windows Defender SmartScan → OnだったのでOff
他のCSVダウンロード可能なサイトにてダウンロード → 正常にダウンロードできる

まとめ(途中報告)

これ以降、まだ詳しくは調べていませんが、現時点考えられる可能性は2つ。
1.自社製品がCSVダウンロードの処理でクライアントへ送っているレスポンスに不備
2.Edgeのバグ

1については開発者ツールで追えそうもないのでパケットキャプチャになりそう。
2については、まだネット上にも情報が足りないので、断定はできなさそう。

時期を見て、1については詳細調査を行いたいと思ってます。

2019年10月18日金曜日

【.netFramework】フォーム継承利用時のイベントハンドラの処理順

ある程度のシステムになると、基底フォームを作成して共通処理を実装し、個別は継承先のフォームで実装する。というケースが多くなると思います。

キーボードイベントの処理順がうまくいかないと技術者から聞かれ、過去の記憶を確認すべく、サンプル書いて確認しました。(サンプルコード(zip)は記事の一番下に置いてあります)

【結果】

OnKey****の場合、継承先から順番に処理される。(上書き用途)
Key****のイベントハンドラの場合、基底から処理される

なお、本検証結果は「一般的な実装方法を用いた場合」の結果であり、色々とコードで工夫をなさった場合にはこの限りではありません。例)On***系の実装でbase.On***を呼ばない。とか、基底のイベントハンドラを登録するタイミングを継承先で指定するなど、振る舞いを変える方法はあると思います。

【検証した方法】

1.以下3つのフォームを用意する
  ・基底フォーム (TestBaseForm)
  ・基底を継承したフォーム (InheritForm1)
  ・さらに継承したフォーム(InheritInheritForm2)
2.それぞれに、同じメソッド、イベントを実装
3.プログラムを実行して、順番を把握

【実装イメージ】

【テストコード】


/// 同じコードを継承先のフォームにも記載
public partial class TestBaseForm : Form
{
    string _myFormName = "TestBaseForm"; // ここは実装するフォーム名にする
    public TestBaseForm()
    {
        InitializeComponent();
        this.KeyPreview = true;
        this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form_KeyDown);
        this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.Form_KeyPress);
        this.KeyUp += new KeyEventHandler(this.Form_KeyUp);
    }
    protected override void OnKeyDown(KeyEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(string.Format("{0}:{1}", _myFormName, System.Reflection.MethodBase.GetCurrentMethod().Name));
        base.OnKeyDown(e);
    }
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(string.Format("{0}:{1}", _myFormName, System.Reflection.MethodBase.GetCurrentMethod().Name));
        base.OnKeyPress(e);
    }
    protected override void OnKeyUp(KeyEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(string.Format("{0}:{1}", _myFormName, System.Reflection.MethodBase.GetCurrentMethod().Name));
        base.OnKeyUp(e);
    }
    private void Form_KeyDown(object sender, KeyEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(string.Format("{0}:{1}", _myFormName, System.Reflection.MethodBase.GetCurrentMethod().Name));
    }
    private void Form_KeyUp(object sender, KeyEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(string.Format("{0}:{1}", _myFormName, System.Reflection.MethodBase.GetCurrentMethod().Name));
    }
    private void Form_KeyPress(object sender, KeyPressEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine(string.Format("{0}:{1}", _myFormName, System.Reflection.MethodBase.GetCurrentMethod().Name));
    }
}

【実行ログ】

Log内容 Method 基底 継承1 継承2
InheritInheritForm2:OnKeyDown OnKeyDown    
InheritForm1:OnKeyDown OnKeyDown    
TestBaseForm:OnKeyDown OnKeyDown    
TestBaseForm:Form_KeyDown KeyDown    
InheritForm1:Form_KeyDown KeyDown    
InheritInheritForm2:Form_KeyDown KeyDown    
InheritInheritForm2:OnKeyPress OnKeyPress    
InheritForm1:OnKeyPress OnKeyPress    
TestBaseForm:OnKeyPress OnKeyPress    
TestBaseForm:Form_KeyPress KeyPress    
InheritForm1:Form_KeyPress KeyPress    
InheritInheritForm2:Form_KeyPress KeyPress    
InheritInheritForm2:OnKeyUp OnKeyUp    
InheritForm1:OnKeyUp OnKeyUp    
TestBaseForm:OnKeyUp OnKeyUp    
TestBaseForm:Form_KeyUp KeyUp    
InheritForm1:Form_KeyUp KeyUp    
InheritInheritForm2:Form_KeyUp KeyUp    

【まとめ】

フォームを継承して利用する際には、継承元に実装するロジックの用途、継承先での振る舞いを考慮したうえで実装を決めることをお勧めします。

【ソース】

20191018_KeyboardEventOrderTest_source.zip

2019年10月14日月曜日

WindowsUpdateでprintspoolerが起動しなくなった

保守案件です。Windows10で印刷ができなくなる事象に対する一部始終です。

【経緯】

・ある特定のPCでのみ発生。
・事象としては印刷ができない。
・ドライバなどを入れなおす。など試してみてもらってもダメ、SE保守となりました。

【把握できていること】

・プリンタスプーラサービス(Print Spooler)が停止している。
・サービスを手動で起動しても、暫くたつと停止してしまう。

【こちらで把握したこと】

・イベントビューアで、エラー状況を確認。2019/10/04よりPrintSpoolerがエラーを出しているようです。
・WindowsUpdateが最後に動いたのが2019/10/04
・ネットで「printspooler 停止してしまう windows10」で調べたところ、2019年9月4週に発信されたWindowsUpdateによる事象で、Microsoftも把握しているとの事。

【処置】

・WindowsUpdate  →「最新です」とはなっていたが、「最新を取得」でいくつかモジュールがインストールされました。OSの再起動を求められたので再起動
・WindowsUpdate(2回目)  →おなじく、「最新です」となっていたのですが、「最新を取得」でいくつかモジュールがインストールされました。1回目と同じで、net Frameworkのパッチなのが気になる。。。
・再起動後も現象は変わらず。 KB4524148を当てろ。という情報があり、当てようとしても、既にインストールされている。 https://www.catalog.update.microsoft.com/Search.aspx?q=KB4524148%20
・「KB4524148」で調べると新情報。 KB4524148をアンインストールしてください。と。

【結果】

・「KB4524148」をアンインストールすることで正常に動作するようになりました。

2019年9月18日水曜日

Google Pixel3に乗り換えて2週間使った感想

つい数日前、破格でPixel3を衝動買い。
前機種はXperiaXZで、購入後3年ほど経過していたのですがなかなか良い機種に出会ておらず、
今回大幅値引きとなっていたことがきっかけで、詳細確認せず購入しました。

2週間使って、諸々気づいたことがあるのでメモしておきます。
なお、始めに断っておきますが、Pixelそのものの評価ではなく、あくまでも自分の機種変更に対する主観です。(Xperia to Pixel)

はじめに

Android To Androidだからなのか、時代の流れなのか、Pixelを起動したら機種変更用のソフトウェアが入っていて、
旧スマートフォンとPixelをUSBで接続することにより、アプリ、データの移行を行ってくれました。
アプリの移行はともかく、アプリの認証情報などは移行できないだろうと思っていたら、
そこそこの数のアプリが認証情報(ログイン情報)も移行してくれていました。
ただ、すべてではありませんのでご注意を。Felica関連は一切移行しないので、手動での移行が必要です。


移行して良かったこと

指紋認証が便利

Xperiaは横面、Pixelは背面です。この背面が非常に便利。丁度持つ部分にセンサーがついてます。
複数の指を登録しておけば、ほぼストレスなくロックを解除できます。

充電が早い

メーカーのウリでもあるようですね。とにかく早いです。

スリムになった

あまり力を入れると折れるのではないかと思うくらい、スリムです。

Pixelに変えて困ったこと

キャストの仕様が変わった

元々、Miracast対応の機器にスマートフォンを接続して利用していました。
この機器にはPixel3はつながらないという事象。(具体的にはEZCastとFireTV)
FireTVはFireTV側にソフトを入れればOKということでインストールして対処。
EZCastは2週間たった現在、まだ解消法が見つかっていません。

MicroSDカードに対応していない

とはいえ、32GB -> 64GBなので、アプリ容量としては充分
USBも3.0に対応しているので、USBメモリでのデータ管理へ移行することにしました。

その他

対応していないアプリがいくつか移行できていませんでした。(対応していないのだから当たり前)
FeliCa のポートが前から後ろに変更。バンカーリングをつけて利用するためきちんと認識するか心配でしたが今のところ問題なく認識しています。

音声認識が賢くなった?利用していた入力ソフトが変わったせいかもしれませんが、便利になりました。

カメラが高性能みたいですね。(細かくは見ていない)

Gmailの検索を活用する(Tips)

今年に入り社内メールがgmailとなったので、自分なりに使っていて便利だったことをTipsとして残しておこうと思います。

話題の対象

Webでgmailにログインすると上部に出てくる「検索」の使い方です。(アプリでも同様かと思います:未検証)
なお、一部の演算子(記述方式)は、googleの検索でも使えます。

まずはじめに

Googleのヘルプで、使い方が載っています
https://support.google.com/mail/answer/7190?hl=ja
とはいえ、単一項の説明だけなので、組み合わせて使うことにより効果を発揮することも多いです。

基本的な使い方

  1. キーワードを入れれば、キーワード検索
  2. メールアドレスを入れれば、そのメールアドレスに対する送受信を対象。
  3. 複合キーワードの場合、スペースを入れる(AND検索)
  4. OR検索の場合、A OR B とする (ORは大文字で) もしくは、{ } で括る

よく使いそうな事例

あるメールアドレスからの受信のみを検索

from:メールアドレス
逆に、送信の場合、to:メールアドレス

ある特定のラベルが付与されたメール

label:ラベル名
例)label:処理済

ある特定のラベルを除外する

-label:ラベル名 -(マイナス)をつけるだけ。

添付ファイルのあるメールを検索

has:attachment

複数のラベルを検索

例){label:処理済   label:処理不要}  ※ORを使って両方出します