ssCAROのブログ

色んなとこで見つけたプログラムのメモ置き場っぽい

画面作成中にユーザーコントロールのLoadイベントを処理させない

ユーザーコントロールを使用したForm画面の作成中に次のエラーが出てForm画面が開かなくなった。
ユーザー '' はログインできませんでした。

ユーザーコントロールのLoadイベントにApp.configからDBへの接続情報を取得してDBからデータを取得、表示をするプログラムを追加してからエラーが出ていた。

Form画面を開くときに、配置したユーザーコントロールのLoadイベントが実行されApp.configから接続情報を取得できていないのが原因で、DBへの接続ができずエラーとなっていた。

画面作成中は、Loadイベントが実行されないようにしようと思って調べた時のメモ。

Private Sub UserCtrl_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    '開発中は処理をしない
    If Me.DesignMode Then Return

    'DBへの接続と取得・表示

End Private

参考URL:.NET:Tips > デザイン:デザイン時に処理を実行させない

ユーザーコントロール入れ子になっているとき、子のユーザーコントロールのDesignModeはFalseを返すようです。
その情報を調べた時のメモ。
参考URL:デザインモード(this.DesignMode)を正しく得ることができない場合があります
参考URL:入れ子させた UserControl の DesignMode が false になるのを回避したい

DataGridViewのセル編集が3回クリックで編集できるようになる

DataGridViewでセルを直接編集するために編集を有効にしたけどセルを3回クリックしないと、編集モードにならなかった。

1回目のクリックで、セルが選択され次のダブルクリックで編集モードになってる?

ダブルクリックで編集モードとなるようにしたかったのでプログラムのメモ。

'EditModeを次の設定に変更する
'DataGridView1.EditMode = DataGridViewEditMode.EditProgrammatically
Private Sub DataGridView1_CellDoubleClick(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellDoubleClick
    Dim dgv As DataGridView = DirectCast(sender, DataGridView)

    If {"Column1", "Column2", "Column3"}.Contains(dgv.Columns(e.ColumnIndex).Name) AndAlso e.RowIndex >= 0 Then
        dgv.BeginEdit(True)
    End If
End Sub

NASの共有フォルダにつながらない

Windows 10 Pro バージョン 1809をクリーンインストールしました。
データをNASから取ってこようとしたけど、共有フォルダにつながらない。
\\<IPアドレス> にアクセスできませんとメッセージが出るのみ、エラー内容が特定できないと表示される。

色々しらべて解決したのでメモ。

[コントロール パネル]→[ネットワークと共有センター]を開く。
共有の詳細設定の変更をクリックする。
現在のプロファイルの以下を有効にする。

  • ネットワーク探索を有効にする。
  • ファイルとプリンターの共有を有効にする。

[コントロール パネル]→[プログラムと機能]を開く。
Windowsの機能の有効化または無効化をクリックする。

  • SMB 1.0/CIFS File Sharing Support の中を全部チェック状態にする。

これで共有フォルダにつながるようになった!

PC(Windows 10)の時刻を設定するために盾マークのexeを作成

Windows 10になってからではないけど、システム時刻をプログラムから合わせるにはユーザーアカウント制御を何とかするか、管理者権限で実行するかしないと出来なくなった。

タスクスケジューラに登録して管理者で実行する方法もあるみたい。

ユーザーアカウント制御の実行を聞かれるのはしょうがないと諦めて右クリックして"管理者として実行"をしなくても良いように盾マークのexeを作成することにした。
参考URL:再試行でDebugできない~VS2015

次のプログラムを「NTPサーバの現在日時をシステム時計に設定するサンプル(VB.NET)」を転記しています。
タイムサーバーは、time.windows.com です。

Public Class Form1

    ' システム時計の日時設定APIの引数
    <System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)>
    Public Structure SystemTime
        Public wYear As Short
        Public wMonth As Short
        Public wDayOfWeek As Short
        Public wDay As Short
        Public wHour As Short
        Public wMinute As Short
        Public wSecond As Short
        Public wMiliseconds As Short
    End Structure

    ' システム時計の日時設定APIの定義
    <System.Runtime.InteropServices.DllImport("kernel32.dll")>
    Public Shared Function SetLocalTime(ByRef sysTime As SystemTime) As Boolean
    End Function

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        ' NTPサーバへの接続用UDP生成
        Dim objSck As System.Net.Sockets.UdpClient
        Dim ipAny As System.Net.IPEndPoint = New System.Net.IPEndPoint(System.Net.IPAddress.Any, 0)
        objSck = New System.Net.Sockets.UdpClient(ipAny)

        ' NTPサーバへのリクエスト送信
        Dim sdat As Byte() = New Byte(47) {}
        sdat(0) = &HB
        objSck.Send(sdat, sdat.GetLength(0), "time.windows.com", 123)

        ' NTPサーバから日時データ受信
        Dim rdat As Byte() = objSck.Receive(ipAny)

        ' 1900年1月1日からの経過時間(日時分秒)
        Dim lngAllS As Long ' 1900年1月1日からの経過秒数
        Dim lngD As Long    ' 日
        Dim lngH As Long    ' 時
        Dim lngM As Long    ' 分
        Dim lngS As Long    ' 秒

        ' 1900年1月1日からの経過秒数計算
        lngAllS = CLng(
                  rdat(40) * Math.Pow(2, (8 * 3)) +
                  rdat(41) * Math.Pow(2, (8 * 2)) +
                  rdat(42) * Math.Pow(2, (8 * 1)) +
                  rdat(43))

        ' 1900年1月1日からの経過(日時分秒)計算
        lngD = lngAllS \ (24 * 60 * 60)   ' 日
        lngS = lngAllS Mod (24 * 60 * 60) ' 残りの秒数
        lngH = lngS \ (60 * 60)           ' 時
        lngS = lngS Mod (60 * 60)         ' 残りの秒数
        lngM = lngS \ 60                  ' 分
        lngS = lngS Mod 60                ' 秒

        ' 現在の日時(DateTime)計算
        Dim dtTime As DateTime = "1900/01/01"
        dtTime = dtTime.AddDays(lngD)
        dtTime = dtTime.AddHours(lngH)
        dtTime = dtTime.AddMinutes(lngM)
        dtTime = dtTime.AddSeconds(lngS)

        ' グリニッジ標準時から日本時間への変更
        dtTime = dtTime.AddHours(9)

        ' 現在の日時表示
        System.Diagnostics.Trace.WriteLine(dtTime)

        ' システム時計の日時設定
        SetSysTime(dtTime)

    End Sub

    ' システム時計の日時設定
    Private Sub SetSysTime(ByVal dtm As DateTime)
        Dim sTime As New SystemTime
        sTime.wYear = dtm.Year
        sTime.wMonth = dtm.Month
        sTime.wDay = dtm.Day
        sTime.wHour = dtm.Hour
        sTime.wMinute = dtm.Minute
        sTime.wSecond = dtm.Second
        sTime.wMiliseconds = dtm.Millisecond
        SetLocalTime(sTime)
    End Sub

End Class

ソリューション エクスプローラーからプロジェクトを右クリックして[追加]→[新しい項目]をクリックする。
"アプリケーション マニフェスト ファイル"を選択して追加する。
app.manifestにある次行を変更する。(コメントされているのをコピーして貼り付けると良い)
変更前:<requestedExecutionLevel level="asInvoker" uiAccess="false" />
変更後:<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
f:id:ssCARO:20200630152608p:plain

ソリューションをリビルドしたらexeに盾マークが付いている。
このexeを実行すると、ユーザーアカウント制御が聞かれるので"はい"を押す。

DataGridViewのセル結合(行のセルを固定)

以前に書いた記事で「DataGridViewのセル結合」をしたときだと、行を固定した場合、上手く描画できません。

その対応したプログラムを書いたのでメモ。

  • 表を見せるのみです。
  • 行列の追加、編集、削除や、セル幅の変更は考慮していません。
  • 行列のヘッダーは無効にしています。
  • DataGridViewコントロールの名前は、「DataGridView1」です。
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

    Init()

End Sub

Private Sub Init()

    'ちらつき防止
    Dim type As System.Type = GetType(DataGridView)
    Dim propertyInfo As System.Reflection.PropertyInfo = type.GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.NonPublic)
    propertyInfo.SetValue(DataGridView1, True, Nothing)

    DataGridView1.AllowUserToAddRows = False
    DataGridView1.ReadOnly = True
    DataGridView1.AllowUserToDeleteRows = False
    DataGridView1.ColumnHeadersVisible = False
    DataGridView1.RowHeadersVisible = False

    Dim i As Integer
    Dim j As Integer
    Dim colAdd As DataGridViewColumn
    For i = 1 To 3
        colAdd = New DataGridViewColumn
        colAdd.Name = String.Format("Column{0}", i)
        colAdd.Width = 60
        colAdd.CellTemplate = New DataGridViewTextBoxCell
        DataGridView1.Columns.Add(colAdd)
    Next

    '結合したいセルには同じテキストを入れておく
    Dim cellText(,) As String = {
        {"店舗", "店舗", "売上"},
        {"店舗名", "担当エリア", "売上"},
        {"北支店01", "駅北", "10000"},
        {"北支店02", "駅南", "9000"},
        {"西支店03", "商店街", "12000"},
        {"東支店04", "住宅区", "8000"},
        {"南支店05", "商業区", "11000"},
        {"北支店06", "駅北", "10000"},
        {"北支店07", "駅南", "9000"},
        {"西支店08", "商店街", "12000"},
        {"東支店09", "住宅区", "8000"},
        {"南支店10", "商業区", "11000"},
        {"北支店11", "駅北", "10000"},
        {"北支店12", "駅南", "9000"},
        {"西支店13", "商店街", "12000"},
        {"東支店14", "住宅区", "8000"},
        {"南支店15", "商業区", "11000"},
        {"北支店16", "駅北", "10000"},
        {"北支店17", "駅南", "9000"},
        {"西支店18", "商店街", "12000"},
        {"東支店19", "住宅区", "8000"},
        {"南支店20", "商業区", "11000"}
    }
    Dim rowAdd As DataGridViewRow
    For i = 0 To cellText.GetLength(0) - 1
        rowAdd = New DataGridViewRow
        rowAdd.CreateCells(DataGridView1)
        For j = 0 To rowAdd.Cells.Count - 1
            rowAdd.Cells(j).Value = cellText(i, j)
        Next
        DataGridView1.Rows.Add(rowAdd)
    Next

    'ヘッダー行を固定とする
    DataGridView1.Rows(1).Frozen = True

End Sub

Private Sub DataGridView1_CellPainting(sender As Object, e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting

    '結合したいセルをここに書く
    MergeCell(e, New Point(0, 0), New Point(1, 0))
    MergeCell(e, New Point(2, 0), New Point(2, 1))
    MergeCell(e, New Point(0, 1), New Point(0, 1))
    MergeCell(e, New Point(1, 1), New Point(1, 1))

End Sub

'Cell1には、セルの開始位置(X, Y)
'Cell2には、セルの終了位置(X, Y)
Private Sub MergeCell(ByRef e As System.Windows.Forms.DataGridViewCellPaintingEventArgs, Cell1 As Point, Cell2 As Point)

    If (e.RowIndex >= Cell1.Y AndAlso e.RowIndex <= Cell2.Y) AndAlso (e.ColumnIndex >= Cell1.X AndAlso e.ColumnIndex <= Cell2.X) Then

        Dim rect As New Rectangle With {.X = 0, .Y = 0, .Width = 0, .Height = 0}
        Dim i As Integer
        Dim firstRowIndex As Integer

        '固定セルであれば、先頭から描画されているとする
        If DataGridView1.Rows(e.RowIndex).Frozen = True Then
            firstRowIndex = 0
        Else
            firstRowIndex = DataGridView1.FirstDisplayedScrollingRowIndex
        End If

        '開始セルの位置
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.Y + 1 To firstRowIndex
            rect.Y -= DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        For i = firstRowIndex + 1 To Cell1.Y
            rect.Y += DataGridView1(Cell1.X, i - 1).Size.Height
        Next
        '結合セルが画面外にあるときの位置を考慮
        For i = Cell1.X + 1 To DataGridView1.FirstDisplayedScrollingColumnIndex
            rect.X -= DataGridView1(i - 1, Cell1.Y).Size.Width
        Next
        For i = DataGridView1.FirstDisplayedScrollingColumnIndex + 1 To Cell1.X
            rect.X += DataGridView1(i - 1, Cell1.Y).Size.Width
        Next

        '終了セルの幅
        For i = Cell1.Y To Cell2.Y
            rect.Height += DataGridView1(Cell2.X, i).Size.Height
        Next
        For i = Cell1.X To Cell2.X
            rect.Width += DataGridView1(i, Cell2.Y).Size.Width
        Next

        'セル位置の補正
        rect.X += 1
        rect.Y += 1

        'グラデーションをかけてヘッダーぽく見せる
        Dim gb As New System.Drawing.Drawing2D.LinearGradientBrush(rect, SystemColors.ControlLightLight, SystemColors.Control, System.Drawing.Drawing2D.LinearGradientMode.Vertical)
        gb.GammaCorrection = True

        '通常の塗りつぶし
        'e.Graphics.FillRectangle(New SolidBrush(SystemColors.Control), rect)
        e.Graphics.FillRectangle(gb, rect)
        e.Graphics.DrawRectangle(New Pen(DataGridView1.GridColor), rect)

        gb.Dispose()

        '描画するセル位置の文字をヘッダーテキストとして表示
        Dim headerText As String = DataGridView1(e.ColumnIndex, e.RowIndex).Value
        TextRenderer.DrawText(e.Graphics, headerText, e.CellStyle.Font, rect, e.CellStyle.ForeColor, TextFormatFlags.HorizontalCenter Or TextFormatFlags.VerticalCenter)

        e.Handled = True

    End If

End Sub

f:id:ssCARO:20200630145615p:plain

WebBrowserコントロールでポップアップ表示されるユーザーとパスワードの入力

WebBrowserコントロールで機器接続用の専用ブラウザを作っていてユーザーとパスワードの入力をポップアップで聞いてくるのがあった。 f:id:ssCARO:20200630145514p:plain

色々調べるとBASIC認証すれば接続できたので、そのときのメモ。

接続するURLを次のようにして接続した。
httpとIPアドレスの間に、<ユーザー名>:<パスワード>@ を追加した。

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim userName As String = "mainte"
    Dim password As String = "99999"
    Dim urlString As String = "http://" & userName & ":" & password & "@192.168.0.254/"
    ExWebBrowser1.Navigate(urlString)
End Sub

リソースモニターの内容をファイルに保存する(VBS)

メモリの使用量を長期にわたって保存する必要がでてきた。
リソースモニターを開いたときの各プロセスのメモリの値をファイル保存する。

スクリプトをタスク スケジューラに登録して定期的に実行するようにした。

ResourceLog.vbs

'各プロセスのメモリ使用率をCSVで保存する
'ファイル名は"PerfLog_yyyyMMdd_HHmmss.csv""
Const savePath = "D:\"

Dim oClassSet
Dim oClass
Dim oLocator
Dim oService
Dim sMesStr

Dim oRange()
Dim oRangeA()
Dim oRangeB()
Dim i
Dim j
Dim iRow
Dim cpuPercent

Set oLocator = CreateObject("WbemScripting.SWbemLocator")
Set oService = oLocator.ConnectServer

iRow = 0


'1回目の計測
Set oClassSet = oService.ExecQuery("SELECT * FROM Win32_PerfRawData_PerfProc_Process")

ReDim oRangeA(oClassSet.Count - 1, 10)

i = 0
For Each oClass In oClassSet

    oRangeA(i, 0) = oClass.Name
    oRangeA(i, 1) = oClass.IDProcess
    oRangeA(i, 2) = oClass.PageFaultsPersec
    oRangeA(i, 3) = oClass.PrivateBytes / 1024
    oRangeA(i, 4) = oClass.WorkingSet / 1024
    oRangeA(i, 5) = (oClass.WorkingSet - oClass.WorkingSetPrivate) / 1024
    oRangeA(i, 6) = oClass.WorkingSetPrivate / 1024
    oRangeA(i, 7) = oClass.PercentPrivilegedTime
    oRangeA(i, 8) = oClass.PercentUserTime
    oRangeA(i, 9) = oClass.Timestamp_Sys100NS
    
    i = i + 1
Next

Set oClass = Nothing
Set oClassSet = Nothing


'1秒の待ち
WScript.Sleep 1000


'2回目の計測
Set oClassSet = oService.ExecQuery("SELECT * FROM Win32_PerfRawData_PerfProc_Process")

ReDim oRangeB(oClassSet.Count - 1, 10)

i = 0
For Each oClass In oClassSet

    oRangeB(i, 0) = oClass.Name
    oRangeB(i, 1) = oClass.IDProcess
    oRangeB(i, 2) = oClass.PageFaultsPersec
    oRangeB(i, 3) = oClass.PrivateBytes / 1024
    oRangeB(i, 4) = oClass.WorkingSet / 1024
    oRangeB(i, 5) = (oClass.WorkingSet - oClass.WorkingSetPrivate) / 1024
    oRangeB(i, 6) = oClass.WorkingSetPrivate / 1024
    oRangeB(i, 7) = oClass.PercentPrivilegedTime
    oRangeB(i, 8) = oClass.PercentUserTime
    oRangeB(i, 9) = oClass.Timestamp_Sys100NS
    
    i = i + 1
Next

Set oClass = Nothing
Set oClassSet = Nothing


Set oService = Nothing
Set oLocator = Nothing


On Error Resume Next

ReDim oRange(1 + UBound(oRangeB), 10)

oRange(0, 0) = "名称"
oRange(0, 1) = "プロセスID"
oRange(0, 2) = "コミット(KB)"
oRange(0, 3) = "ワーキングセット(KB)"
oRange(0, 4) = "共有可能(KB)"
oRange(0, 5) = "プライベート(KB)"
oRange(0, 6) = "CPU使用率(%)"

For i = 0 To UBound(oRangeB)

    cpuPercent = 0
    For j = 0 To UBound(oRangeA)
        If oRangeB(i, 0) = oRangeA(j, 0) And oRangeB(i, 1) = oRangeA(j, 1) Then
        
            cpuPercent = ((CDbl(oRangeB(i, 7)) + CDbl(oRangeB(i, 8))) - (CDbl(oRangeA(j, 7)) + CDbl(oRangeA(j, 8)))) / (CDbl(oRangeB(i, 9)) - CDbl(oRangeA(j, 9)))
            
            Exit For
        End If
    Next
    
    oRange(1 + i, 0) = oRangeB(i, 0)
    oRange(1 + i, 1) = oRangeB(i, 1)
    oRange(1 + i, 2) = oRangeB(i, 3)
    oRange(1 + i, 3) = oRangeB(i, 4)
    oRange(1 + i, 4) = oRangeB(i, 5)
    oRange(1 + i, 5) = oRangeB(i, 6)
    oRange(1 + i, 6) = FormatNumber(cpuPercent, 2, -1, 0, 0)
Next


Dim oFs
Dim oText
Dim sPath
Dim sText()

ReDim sText(UBound(oRange, 2))

sPath = savePath & "\PerfLog_" & FormatDay() & ".csv"

Set oFs = CreateObject("Scripting.FileSystemObject")
Set oText = oFs.CreateTextFile(sPath, True)

For i = 0 To UBound(oRange)
    For j = 0 To UBound(sText)
        sText(j) = oRange(i, j)
    Next
    oText.Write Join(sText, ",") & vbCrLf
Next

oText.Close

Set oText = Nothing
Set oFs = Nothing

Function FormatDay()

    Dim sDate, dtNow

    dtNow = Now
    sDate = FormatDateTime(dtNow, vbShortDate) & " " & Right("0" & FormatDateTime(dtNow, vbLongTime), 8)
    FormatDay = Replace(Replace(Replace(sDate, "/", ""), ":", ""), " ", "_")

End Function