2014年12月4日木曜日

[MSAccess] マクロ定義を全てエクスポート

Microsoft Access のマクロをテキストファイルにエクスポートする機能はありません、マクロ オブジェクトの定義内容をテキストファイルに保存することになります。下にサンプルを挙げますが、参照設定として以下の追加が必要です。
  • Microsoft Scripting Runtime
参照設定は、Visual Basic Editor(VBE)のメニューから、ツール(T) > 参照設定(R) と辿ります。

MSAccess マクロ オブジェクトをテキストファイルに出力するサンプル
Sub ExportMacro()
    Dim Fso As New Scripting.FileSystemObject
    Dim OutDir As String

    OutDir = Fso.GetParentFolderName(CurrentDb.Name) & "\macro"
    If Not Fso.FolderExists(OutDir) Then
        Fso.CreateFolder (OutDir)
    End If

    Dim Containers As DAO.Containers
    Dim Container As DAO.Container
    Dim Document As DAO.Document

    Set Containers = CurrentDb.Containers
    Set Container = Containers("Scripts")
    For Each Document In Container.Documents
        SaveAsText ObjectType:=AcObjectType.acMacro, ObjectName:=Document.Name, FileName:=OutDir & "\" & Document.Name
    Next
End Sub

(※補足)CurrentDb.Containers("Scripts") といった具合に続けて実装したほうがシンプルだけど、「オブジェクトが正しくない」とエラーになります。

[Windows] 共有ドライブのサブフォルダに、ドライブを割り当てる

■ サブフォルダにドライブを割り当て

エクスプローラーからネットワーク ドライブを割り当てる場合は、共有ドライブおよび共有フォルダーに対する割り当てしかできません。しかしサブフォルダーにドライブを割り当てる方法はあります。

コマンドでドライブを割り当てれば、サブフォルダにドライブを割り当てられます。
(例:Z ドライブとして割り当てる場合)
> NET USE Z: \\ServerName\ShareName\FolderName /USER:DomainName\UserId MyPassword

■ 同じサーバについて複数の割り当て

さらに同じサーバにある複数のフォルダにドライブを割り当てるとエラーになります。これはWindowsのクライアント側の制約で、複数の割り当てのセッションを区別できないためである様子。

・エラーメッセージ
システム エラー 1219 が発生しました。
同じユーザーによる、サーバーまたは共有リソースへの複数のユーザー名での複数の接続は許可されません。サーバーまたは共有リソースへの以前の接続をすべて切断してから、再試行してください。


まず行う対策としては、使用中のセッションを閉じてやれば良い。

> NET USE Z: /DELETE

しかし、複数のフォルダに割り当てる方法はあります。例えばIPアドレスやサーバ名で使い分けると同じサーバと見なされません。

> NET USE X: \\ServerName\ShareName\FolderName1 /USER:DomainName\UserId
> NET USE Y: \\ServerName.DomainName\ShareName\FolderName2 /USER:DomainName\UserId
> NET USE Z: \\192.168.x.x\ShareName\FolderName3 /USER:DomainName\UserId


もっと割り当てる場合は、サーバ名を複数用意したり、サーバのIPアドレスを追加、hostsファイルに同じサーバへの名前を複数作成するなどで可能となる。

■ 参考資料

Windows XP でネットワーク ドライブの接続および切断を行う方法
http://support.microsoft.com/kb/308582/ja

第21回 ファイル共有プロトコルSMB/CIFS(その2) (3/3)
http://www.atmarkit.co.jp/ait/articles/0412/08/news096_3.html

2014年11月30日日曜日

[SQL] MSAccessで「JOIN 式はサポートされていません」エラー

Microsoft Access における JOIN 式(特に外部結合)で、結合条件に加えて抽出条件を使用するSQL

[顧客] LEFT OUTER JOIN [購入明細] ON
  [顧客].ID = [購入明細].ID AND
  [購入明細].種別 = '食品'


Oracle などではこれで可能だが、
MSAccess では「JOIN 式はサポートされていません」エラーが発生して、LEFT OUTER JOIN 句に結合条件以外の条件を記述できない。もちろん外部結合の条件を WHERE 句には書けない。

ただし MSAccess にも迂回策があり、結合条件を合わせてカッコでまとめると通せる。

[顧客] LEFT OUTER JOIN [購入明細] ON
  ([顧客].ID = [購入明細].ID AND
   [購入明細].種別 = '食品')

2014年11月8日土曜日

[VBA] Access のウィンドウを非表示にする

Microsoft Office のユーザフォームを残して、Access 本体の画面を表示しない方法です。

これは Windows API の ShowWindow 関数を使用する方法です。この方法では、Access のウィンドウを最小化しています。ウィンドウが隠れているだけなので、Windowsのタスクバーから Access のタスクをクリックすればウィンドウが表示されます。

Access の場合は、データベースの起動時に実行する AutoExec マクロなどからこの処理を実行すればよい。(AutoExec から実行するプロシージャは Function にする必要あり)

Const SW_SHOWMINIMIZED = 2

Declare Function ShowWindow Lib "User32" ( _
    ByVal hWnd As Long, ByVal nCmdShow As Long) As Long

Function WindowMinimize() As Boolean
    Dim result As Long
    result = ShowWindow(Application.hWndAccessApp, SW_SHOWMINIMIZED)
    DoCmd.OpenForm ("サンプルフォーム")
    WindowMinimized = True
End Sub

■ 参考資料

ShowWindow
http://msdn.microsoft.com/ja-jp/library/cc411211.aspx

データベースを開いたときに実行されるマクロを作成する(Access 2007)
http://office.microsoft.com/ja-jp/access-help/HA010336239.aspx

2014年11月6日木曜日

[MSAccess] Accessの終了 ボタンを無効にする

Microsoft Access 2010 のファイルメニューにある「データベースを閉じる」「終了」ボタンを無効にする方法です。Access 2013 では、「閉じる」ボタンと名称が少し違います。これはMS Access 2010 以降に限ります、2007 だと ファイルメニューの「Microsoft Office Backstage ビュー」が無く、別の実装が必要です。2007 における設定はこちら

■ 手順1

システム テーブルを表示する設定

Access の Office ボタン > Access のオプション > メニューの「カレント データベース」 > ナビゲーションの「ナビゲーション オプション」ボタン と辿る

「ナビゲーション オプション」ウィンドウの 表示オプション 内にある、「システム オブジェクトの表示」を有効にする。

■ 手順2

システム テーブルを作成する。上記の「システム オブジェクトの表示」が有効になっていないと作成しても表示されません。(USys と名前を付けただけで、システム オブジェクトの扱いになってしまう)

・テーブル名:USysRibbons
・カラム
フィールド名データ型
IDオートナンバー
RibbonNameテキスト型(サイズ 255)
RibbonXmlメモ型

■ 手順3

「データベースを閉じる」「終了」ボタンを無効にするリボン (XML) 項目を定義します。
<?xml version="1.0" encoding="utf-8"?>
<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui">
  <commands>
    <command idMso="FileCloseDatabase" enabled="false" />
    <command idMso="FileExit" enabled="false" />
  </commands>
  <backstage>
    <button idMso="FileCloseDatabase" visible="true" />
    <button idMso="FileExit" visible="true" />
  </backstage>
</customUI>

このXMLテキストを上記で作成した USysRibbons テーブルの RibbonXml カラムにレコードとして挿入します。RibbonName には任意の名前を付けます。無効にするのでなくボタンを表示しないならば、visible 属性を false にすればよい。

■ 手順4

作成したリボンのカスタマイズ設定を有効にする。

Access の Office ボタン > Access のオプション > メニューの「カレント データベース」 と辿り、
リボンとツールバーのオプションの「リボン名」で、上記で準備した USysRibbons テーブルの RibbonName カラムの名前を選択します。一旦データベースを閉じて開き直さないと、登録した名前が選択できないかもしれません。

これでデータベースを閉じて、開き直すとリボンのカスタマイズ設定が有効になります。

■ 補足

Access 2013 だと、デフォルトでは「データベースを閉じる」ボタンが無いと思います、カスタマイズしないと出てきません。

■ 参考資料

Office 2010 の Backstage ビューについて (開発者向け)
http://msdn.microsoft.com/ja-jp/library/office/ee691833.aspx
Office 2010 の Backstage ビューのカスタマイズ (開発者向け)
http://msdn.microsoft.com/ja-jp/library/office/ee815851.aspx

2014年11月3日月曜日

[VBA] Office の閉じるボタンを無効にする

VBA から Microsoft Office (Word/Excel/Access)の閉じる[×]ボタンを無効にする方法です。Windows API を使います。
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, _
    ByVal bRevert As Long) As Long

Private Declare Function EnableMenuItem Lib "user32" (ByVal hMenu As Long, _
    ByVal uIDEnableItem As Long, ByVal uEnable As Long) As Long

Const MF_GRAYED = &H1&
Const MF_BYCOMMAND = &H0&
Const SC_CLOSE = &HF060&

Sub CloseButtonEnable()
    Dim hWnd As Long
    Dim hMenu As Long
    Dim result As Long

    hWnd = Application.hWndAccessApp
    hMenu = GetSystemMenu(hWnd, 0)
    result = EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND Or MF_GRAYED)
End Sub
これを起動時に実行すれば閉じる[×]ボタンが無効になります。

■ 補足

ただし閉じる[×]ボタンを押してもファイルは閉じなくなりますが、Office のバージョンによって見た目が随分違います。
  • Office 2007: 見た目は変わらない。
  • Office 2010: 赤い×ボタンが若干暗い色に変わる
  • Office 2013: 見た目は変わらない。

これ以外にもある Office の「閉じる」操作は、
・Windows のタスクバーから、Officeのタスクを閉じる。
・メニューから Office を終了する。
・ショートカットキー [Ctrl] + [F4] で閉じる。
・Shift キーを押しながら起動すると、VBAマクロを無効にして起動できる。
・タスクマネージャ から、プロセスをKILL

■ 参考資料

GetSystemMenu
http://msdn.microsoft.com/ja-jp/library/cc364748.aspx

EnableMenuItem
http://msdn.microsoft.com/ja-jp/library/cc410786.aspx

アプリケーション ウィンドウの閉じるボタンおよび [ファイル] メニューの [終了] を無効にする方法
http://support.microsoft.com/kb/300688/ja

[MSAccess] Access2007の終了 ボタンを無効にする

Microsoft Access 2007 の「Accessの終了」「データベースを閉じる」ボタンを無効にする方法です。これはMSAccess 2007 に限ります、2010 以降になると「Office ボタン」が無くなっており、別の方法になります。

■ 手順1

システム テーブルを表示する設定

Access の Office ボタン > Access のオプション > メニューの「カレント データベース」 > ナビゲーションの「ナビゲーション オプション」ボタン と辿る

「ナビゲーション オプション」ウィンドウの 表示オプション 内にある、「システム オブジェクトの表示」を有効にする。

■ 手順2

システム テーブルを作成する。上記の「システム オブジェクトの表示」が有効になっていないと作成しても表示されません。(USys と名前を付けただけで、システム オブジェクトの扱いになってしまう)

・テーブル名:USysRibbons
・カラム
フィールド名データ型
IDオートナンバー
RibbonNameテキスト型(サイズ 255)
RibbonXmlメモ型

■ 手順3

「Accessの終了」「データベースを閉じる」ボタンを無効にするリボン (XML) 項目を定義します。
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
  <commands>
    <command idMso="FileExit" enabled="false" />
  </commands>
  <ribbon>
    <officeMenu>
      <control idMso="FileCloseDatabase" enabled="false" />
    </officeMenu>
  </ribbon>
</customUI>

このXMLテキストを上記で作成した USysRibbons テーブルの RibbonXml カラムにレコードとして挿入します。RibbonName には任意の名前を付けます。

■ 手順4

作成したリボンのカスタマイズ設定を有効にする。

Access の Office ボタン > Access のオプション > メニューの「カレント データベース」 と辿り、
リボンとツールバーのオプションの「リボン名」で、上記で準備した USysRibbons テーブルの RibbonName カラムの名前を選択します。一旦データベースを閉じて開き直さないと、登録した名前が選択できないかもしれません。

これでデータベースを閉じて、開き直すとリボンのカスタマイズ設定が有効になります。

■ 補足

もちろんこの設定を加えると終了操作に制限がかかりますが、こういう対策で操作を制限しないと正しく機能しないのであれば、最初から C#.NET 等のWindowsソフトウェアとして提供するべきでは?
どうせ Microsoft Access で本格的に操作を制限しても抜け穴だらけです。無意識に操作してしまいがちなものだけを制限する程度の効果しかありません。

これで残っている「閉じる」操作は、
  • Windows のタスクバーから、Accessのタスクを閉じる。
  • 閉じる[×]ボタン で閉じる。
  • ショートカットキー [Ctrl] + [F4] で閉じる。
  • Shift キーを押しながら起動すると、リボンのカスタママイズを無効にして起動できる。
  • タスクマネージャ から、プロセスをKILL

■ 参考資料

リボンをカスタマイズする (Office Access 2007)
http://office.microsoft.com/ja-jp/access-help/HA010211415.aspx

2014年10月25日土曜日

[VBA] ネットワーク ドライブであることを確認する

VBAで、ディスクがネットワーク ドライブであることを確認する方法です

Windows Script Host の EnumNetworkDrives メソッドで、ネットワークドライブのコレクションを得られます。調査したいドライブ名がこのコレクションに含まれていれば、ネットワークドライブです。ローカル ドライブはこのコレクションには含まれません。

今PCが接続しているネットワークドライブの一覧は、コマンドプロンプトから
> net use
で確認できます。EnumNetworkDrives のコレクションはこれと同じ内容になっています。

■ サンプルロジック
' 参照設定:Windows Script Host Object Model
Dim Network As New WshNetwork
Dim Drives As IWshCollection
Set Drives = Network.EnumNetworkDrives

Dim i As Integer
For i = 0 To Drives.Length - 1 Step 2
    ' コレクションの偶数番目
    Debug.Print "論理ドライブのローカル名" & vbTab & Drives.Item(i)
    ' コレクションの奇数番目
    Debug.Print "UNC 共有名" & vbTab & Drives.Item(i + 1)
Next

■ 出力結果(例)
論理ドライブのローカル名 Z:
UNC 共有名 \\fileserver\共有フォルダ

■ 参考資料

EnumNetworkDrives メソッド
http://msdn.microsoft.com/ja-jp/library/cc364403.aspx

2014年10月22日水曜日

Excel から MSAccess へコピペすると、1レコード欠落する

Microsoft Excel から Access のテーブルへレコードをコピー&ペーストすると、1レコード欠落することがある。これは、Excel の「見出しチェックの機能」が影響しており、誤って見出しと識別された1行目のレコードが貼り付けられないことに起因している様子です。

Microsoftから「見出しチェックの機能」の詳しい仕様が、マイクロソフト コミュニティで提供されていました。

Access2010にてExcelからAccessテーブルへのコピー+ペーストで登録されないレコードがある
http://answers.microsoft.com/ja-jp/office/forum/office_2010-access/access2010%E3%81%AB%E3%81%A6excel%E3%81%8B/6192e312-a292-49e3-9ce5-65e15e5238d0

[VBA] RegExp.Replace で改行コードが変わってしまう

RegExp.Replace メソッドで置換したら、改行コードが CrLf から Lf になってしまう問題があります。これは行末 "$" までの置換を行った場合に起こる。

例えば、SQLのコメントを全て削るような置換パターンで発生する。
RegExp.Pattern = "--.+$"
RegExp.Replace(SQL, "")


このとき、実際は改行コード Cr まで含めた置換になっており、問題が発生します。
(置換前)ColName -- コメントCrLf    ※赤字が置換される部分
(置換後)ColName Lf
改行コード CrLf のうち、Cr が置換され消えてしまい Lf だけが残るので、改行コード CrLfLf に変換されたように感じられます。

.+(ドット プラス)や .*(ドット アアスタリスク)などの任意の文字の繰り返しパターンが問題になる。ドットは「改行文字を含まない任意の 1 文字」を表すので、リファレンスによれば、どうやら仕様通りとも言えるのだが・・・
  • $ : 入力文字列の末尾と一致します。RegExp オブジェクトの Multiline プロパティが設定されている場合、$'\n' または '\r' の直前にも一致します。
    → つまり、'\n' の直前の '\r' は一致してしまうのね。
  • .(ドット) : 改行文字 "\n" を除く任意の 1 文字に一致します。
    → つまり、キャリッジ リターン "\r" は除いていないのね。

いやいや!この仕様は無理がある。

ドットでない特定のパターンの繰り返しならば、改行コードが削られることは無い。
例えば、行末のスペースとタブのゴミを削るパターン
RegExp.Pattern = "( |\t)+$"
RegExp.Replace(SQL, "")


検証用のサンプルロジック

Const Sample As String = _
    "abc -- コメント" & vbCrLf & _
    "def" & vbCrLf & _
    "ghi"

Dim Reg As New RegExp
Reg.Global = True
Reg.MultiLine = True
Reg.Pattern = "--.+$"

Dim Text As String
Text = Reg.Replace(Sample, "")
For i = 1 To Len(Text)
    Debug.Print Mid(Text, i, 1) & vbTab & Asc(Mid(Text, i, 1))
Next

■ 参考資料

正規表現の構文
http://msdn.microsoft.com/ja-jp/library/cc392020.aspx

2014年10月18日土曜日

[VBA] 正規表現で行コメントを削除する

VBAを使って、行コメントや空行、およびタブやスペースだけの実質的な空行も削除するサンプルです。参照設定として「Microsoft VBScript Regular Expressions 5.5」が必要です。

簡略にはまとめて置換する Replace 関数で済ませたいが、改行コード CrLfLf に変わってしまった。
Dim Reg As New RegExp
Reg.MultiLine = True
Reg.Global = True

'SQL 行コメントの場合の正規表現パターン
Reg.Pattern = "--.*$"
'タブやスペースの場合の正規表現パターン
Reg.Pattern = "( |\t)+$"

Result = Reg.Replace(Text, "")


代替案として、改行コードを変更(元に戻す)するステップを加えます。

' 改行コード CrLf と Lf が混在している状態から、Lf に統一する
Text = Replace(Expression:=Text, Find:=vbCr, Replace:="", Count:=-1, Compare:=VbCompareMethod.vbBinaryCompare)
' 改行コードを Lf から CrLf に変換する
Text = Replace(Expression:=Text, Find:=vbLf, Replace:=vbCrLf, Count:=-1, Compare:=VbCompareMethod.vbBinaryCompare)


どうやら、RegExp による改行についての置換は問題がありそう。

[VBA] RegExp.Replace で改行コードが変わってしまう
http://mofuken.blogspot.jp/2014/10/vba-regexpreplace.html

■ 参考資料

Microsoft Visual Basic 6.0 で正規表現を使用する方法
http://support.microsoft.com/kb/818802

[VBA] モジュール等の名称を取得する

VBAで クラスモジュール/フォームの名称を参照する方法です。

■ クラス モジュール

Debug.Print TypeName(Me)

■ ユーザー フォーム

Debug.Print TypeName(Me)
もしくは
Debug.Print Me.Name

■ ワークシート(Excel)

Debug.Print Me.Name

標準モジュールでは Me が使えないので難しいです。

2014年10月6日月曜日

[VBA] カレントディレクトリのパスを確認する方法

VBA で開いたファイルのディレクトリを確認する方法です。

■ Word の場合

Document オブジェクトの Path プロパティを使います。
Debug.Print Me.Path

■ Excel の場合

Workbook オブジェクトの Path プロパティを使います。
Debug.Print ActiveWorkbook.Path

■ Access の場合

Database オブジェクトの Name プロパティを使います。ただしこれはファイル名まで含んでいるので、フォルダ名が欲しい場合はファイル名を削る必要があります。
(参照設定)Microsoft Scripting Runtime
Dim Fso As New FileSystemObject
Debug.Print Fso.GetParentFolderName(Database.Name)

2014年9月28日日曜日

[VBA] Excel のパスワードを設定する。

VBA で Excel の「読み取りパスワード」「書き込みパスワード」を設定する方法です。

パスワードの種類によってプロパティが分かれており、リファレンスからは見つけにくいが、
Workbook の Password プロパティ、もしくは Workbook.SaveAs メソッドのパラメーター を使います。

Dim XlsApp As New Excel.Application
Dim Book As Workbook
Set Book = XlsApp.Workbooks.Open("ファイル名.xlsx")
Book.Password = "password01" ' 読み取りパスワード
Call Book.SaveAs(Filename:="ファイル.xlsx", _
  Password:="password01", _ ' 読み取りパスワード
  WriteResPassword:="password01", _ ' 書き込みパスワード
  ReadOnlyRecommended:=True) ' 読み取り専用を推奨する
Book.Close

パスワードは「最大 15 文字」となる。

Workbook.SaveAs メソッド
http://msdn.microsoft.com/ja-jp/library/microsoft.office.tools.excel.workbook.saveas.aspx

2014年9月3日水曜日

[VBA/VBS] データ型を確認する方法

VBA/VBScript において、変数等のデータ型を確認する方法です。

■ Is 関数を使う

Is 関数を使って、データの状態を確認できますが、これでは(例)数値であるが、Integer か Double といった具体的なデータ型までは分かりません。関数の結果はブール値です。
  • IsArray(配列)
  • IsDate(日付または時刻)
  • IsEmpty(初期化の有無)
  • IsNull(Null 値)
  • IsNumeric(数値)
  • IsObject(オブジェクト)
VBScript の関数
http://msdn.microsoft.com/ja-jp/library/cc392480.aspx

■ TypeName 関数を使う

これが最も具体的なデータ型を確認できます。データ型を表す文字列の名称が得られます。

(例)Debug.Print TypeName("ABC")
(結果)String


TypeName 関数
http://msdn.microsoft.com/ja-jp/library/cc392344.aspx

■ VarType 関数を使う


TypeName と同等ですが、
  • 結果が VbVarType 列挙体 で得られる。
  • Object 型の区別が具体的でない (VbVarType.vbObject という1括りになる)
という違いがあります。データ型によって判定&分岐するならば、これが使いやすいと思います。

(例)Debug.Print VarType("ABC")
(結果)8 (= VbVarType.vbString)


VarType 関数
http://msdn.microsoft.com/ja-jp/library/cc392346.aspx

2014年8月30日土曜日

[Windows] ファイルを開いているプロセスを見つける

「*** は編集のためロックされています。」とか
「別のプログラムがこのフォルダーまたはファイルを開いているので、操作を完了できません。フォルダーまたはファイルを閉じてから再実行してください。」

とのエラーメッセージが表示されるものの、一体何がファイルを開いているのか分からない!そこでファイルを開いているプロセスを探す方法です。


### リモートから開いている共有フォルダのファイルを探す ###

ネットワーク端末から該当する PC のファイルにアクセスされている場合の対処について。

■ コマンドで作業する

ファイルの一覧を表示するには、このコマンドを実行します。
> net file
アクセスされてるファイルが一覧表示されます。他に「一覧にエントリが存在しません。」と表示されればファイルは開かれていません。

ファイルを閉じるには、このコマンドを実行します。
> net file ID(※注) /Close
※注:ID は開いているファイルの ID を記述する

■ 画面で作業する

コントロール パネル > システムとセキュリティ > 管理ツール > コンピューターの管理 と辿って
ツリーの「システムツール > 共有フォルダー > セッション」もしくは「開いているファイル」を開く。
閉じたい項目を右クリックして「セッションを閉じる」もしくは「開いているファイルを閉じる」を選択する。


### ローカルで開いているファイルを探す ###

ローカルのプロセスがファイルをロックしている場合の対処について。

■ タスクマネージャ から探す

Windows タスクマネージャ を開いて、「プロセス」タブからプロセスの「コマンド ライン」を確認する。
コマンドラインはデフォルトでは表示されていないので、表示列を追加する必要がある。
(手順)「表示」メニュー > 列の選択 > 「コマンド ライン」をチェック

ドキュメントであればコマンドの引数として起動されている場合が多いので、これで多くは確認することができる。

■ TASKLIST コマンドで探す

タスクマネージャで多くのプロセスを目で追うのが面倒であれば、コマンドでファイル名(の一部)を探せば早い。これはコマンドライン引数でなく、プロセスのウィンドウタイトルを検索することになるのだが、ドキュメントであればファイル名がウィンドウタイトルに使われる傾向が高いことを利用する。このためアプリケーションのデータファイルだと効果が無いが、そういう場合はアプリとファイルの繋がりは特定できるので、こうやって探す必要も無いのでは...?

> tasklist /V | findstr (ファイル名の一部)

プロセスを停止するには、
> taskkill /PID (プロセスID) /T

■ OPENFILES コマンドで探す

これはローカルで開いているファイルをリストアップできるが、スイッチを「有効」に切り替えてシステムの再起動が必要になる。これはフラグを有効にすると、パフォーマンスに負荷がかかるためにデフォルトでは無効になっているのだが、再起動するならロックの調査する必要はないですよね...
コマンドライン引数でないファイルのOpenについては、この方法で見つけることができます。

> openfiles /Local ON
(再起動が必要)
> openfiles /Query | findstr (フォルダ名やファイル名の一部)

■ System.Diagnostics.Process クラスで探す

Process.StartInfo プロパティ(ProcessStartInfo クラス)のうち Arguments プロパティでコマンド ライン引数を確認できるが、これは下記のように条件が厳しくてほとんど使えない。

http://msdn.microsoft.com/ja-jp/library/system.diagnostics.process.startinfo.aspx
プロセスの開始に Start メソッドを使用しなかった場合、StartInfo プロパティはプロセス開始に使用されたパラメーターを反映しません。

■ Windows PowerShell の Get-Process コマンドで探す

実はこの出力は上記の System.Diagnostics.Process クラスになるので、同じ理由で使えない...

(例)Acrobat Reader を検索
PS> Get-Process | Where-Object {$_.ProcessName -match 'AcroRd'}

Get-Process
http://technet.microsoft.com/ja-jp/library/hh849832.aspx

Get-Process コマンドレットの使用
http://technet.microsoft.com/ja-jp/library/ee176855.aspx

■ WMI で探す (コマンド)

WMI(Windows Management Instrumentation)を使って探す。ただし PowerShell のほうが柔軟に操作できるので、次に挙げるほうを選択したほうが良い。

> wmic PROCESS where "CommandLine like '%SampleName ※注%'" get /format:list | findstr /R "CommandLine ProcessId"

※注:フォルダ名やファイル名の一部を記述

Windows Management Instrumentation コマンド ライン
http://msdn.microsoft.com/ja-jp/library/cc784189.aspx

WMIC のヒント集
http://msdn.microsoft.com/ja-jp/library/cc758713.aspx

WMIC の動詞
http://msdn.microsoft.com/ja-jp/library/cc784966.aspx

WMIC で形式を作成および編集する
http://msdn.microsoft.com/ja-jp/library/cc757287.aspx

wmic
http://msdn.microsoft.com/ja-jp/library/aa394531.aspx

■ WMI で探す (Windows PowerShell)

wmic コマンドの PowerShell 版です。

PS> Get-WmiObject -Class Win32_Process | Where-Object {$_.CommandLine -match 'フォルダ名やファイル名の一部'} | Format-Table Caption,CommandLine,ProcessId

Get-WmiObject
http://technet.microsoft.com/ja-jp/library/hh849824.aspx

Win32_Process class
http://msdn.microsoft.com/ja-jp/library/aa394372.aspx

■ 外部のツールで探す

外部製品のプロセスモニタ ソフト等を使って、ファイルのOpenについての操作を探す。ただしファイルのOpenだけでも相当数出力されるので上手くフィルタリングしないと追えません。
これだと、あらゆる調査が可能です。

Process Monitor
http://technet.microsoft.com/ja-jp/sysinternals/bb896645.aspx

2014年8月29日金曜日

[Windows] GoogleUpdate のタスクを停止したい

GoogleUpdate が常時起動するが、これは結構念入りに仕込まれていてなかなか停止できない。
インターネットから隔離された PC などでは意味が無いので止めた方がいい。(ただし普通の PC では止めないほうが良いと思います。)

### GoogleUpdate の停止方法 ###

■ タスクの停止

コントロール パネル > システムとセキュリティ と辿って、「タスク スケジューラ」を起動する。(もしくは taskschd.msc コマンドを実行)
「タスク スケジューラ」画面から GoogleUpdate 関連のタスクを右クリックして「無効」に設定する。タスク名は恐らく以下のものと思われますが、他にも登録されていることがある。
  • GoogleUpdateTaskMachineCore
  • GoogleUpdateTaskMachineUA

■ サービスの停止

コントロール パネル > システムとセキュリティ > 管理ツール と辿って、「サービス」を起動する。(もしくは services.msc コマンドを実行)
「サービス」画面から GoogleUpdate 関連のサービスを右クリックしてプロパティを開き、「スタートアップの種類」を「手動」もしくは「無効」に変更する。

■ スタートアップの停止

コントロール パネル > システムとセキュリティ > 管理ツール と辿って、「システム構成」を起動する。(もしくは msconfig コマンドを実行)
「システム構成」画面の「スタートアップ」タブを開いて、GoogleUpdate 関連の項目のチェックを外す。



これで問題は解決するはずです。
え!?物理的に GoogleUpdate を削除してしまいたい!?というのであれば、もはや手動でも一切 Update できなくなっても消してしまいますか?全くもってお勧めいたしません。

### GoogleUpdate の削除方法 ###

■ プログラムの削除

C:\Program Files\Google\Update フォルダを削除
C:\Users\(ユーザ名)\AppData\Local\Google\Update フォルダを削除

■ タスクの削除

上記の「タスク スケジューラ」画面で GoogleUpdate 関連のタスクを右クリックして「削除」する。

■ サービスの削除

サービスを削除するコマンドを実行する。(サービスの名前は、念のため自分の環境で確認すべき)
sc delete gupdate
sc delete gupdatem

■ スタートアップの削除

「システム構成」画面の「スタートアップ」タブを開いて、GoogleUpdate 関連の項目のチェックを元に戻したうえで、レジストリ エディタを起動(regedit コマンドを実行)します。(スタートアップ をいったん有効にしないと、削除する文字列値が表示されません。)
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run にある「Google Update」文字列値を削除する。

■ レジストリの登録を削除

レジストリを GoogleUpdate で検索して削除する、結構ありますよ...

2014年8月14日木曜日

ペットボトル給水キャップの水量を調整

100円ショップで売っている、プランター用の水やりキャップの水量を調節する方法です。傾向として水が流れ出る勢いが早すぎることになると思います。

今回は、100円ショップのセリア(Seria)で購入した給水キャップで試してみる。


ペットボトルとの相性にもよると思うが、水量は相当多く500mlサイズで30分程度だった。キャップに開いている穴は2つでサイズは相当小さいが、水量が早い原因は穴のサイズではない

原因はキャップとペットボトルの接合部分から空気が吸い込まれてしまうためです。この製品はペットボトルとキャップの密接な接合具合で水量が影響してしまうので、汎用的なペットボトルを想定した商品としてはどうしても無理がある。100円ショップでは売っているけど、クレームの元になると思われてホームセンターではあまり取り扱っていないのも納得できる。

そこで水量調節のために、ちょっと改良します。

ペットボトルとの接合部分を密着するためにパッキンを入れます。薄いシート状の梱包材などを使ってキャップの内側にぴったり納まるサイズに丸くカットします。


パッキン中央の穴のサイズはよほど小さくない限り流量とは関係が無いので適当に開けてよい。今回は見た目重視でパンチ穴を開けました。梱包材がなければラップとかで代用できるが、繰り返し使用する耐久が無いです。

これをキャップに入れてペットボトルを付けると、今度は流量がほとんど無くなった。でもペットボトルを逆さにして水やりキャップの小穴に指先を添えてみると水面張力で水がゆっくり吸い出されてきます。これが水量を最も押さえた状態です。もっと水を流したいならば、パッキンに空気が入る切れ目を入れてやると早くなります。

2014年7月23日水曜日

[Windows] PCの起動後に毎回 Java Update のポップアップが表示される

PCの起動後に毎回 Java 自動アップデートのポップアップが表示され、Javaをアップグレードする許可を求められる。さらに「更新を自動的にチェックする」設定を解除しても、起動毎に実行されてしまう。

この対策する前に、念のため注意喚起をしておく必要があります。
http://www9.nhk.or.jp/nw9/marugoto/2014/04/0414.html
セキュリティーホールで狙われやすいのがホームページ閲覧ソフトに組み込んで使う Java というソフトや文書を閲覧する Adobe Reader といわれています。


対策1 Javaコントロール・パネル で Java 更新設定を変更する

これが最も妥当な方法です。

ただし管理者権限で変更しないと、変更しても一向に反映されません。この管理者とは管理者ユーザー(Administratorアカウント)を指すのではなく、UAC の管理者特権を意味します。困るのは管理者権限が無くても設定の変更でエラーにならない、でも一向に反映されない!こと。

管理者権限の使用は、アプリケーションを起動するときに右クリックして「管理者として実行」を選択することで管理者特権で実行できます。

Javaコントロール・パネルの起動は、
  • Windows のスタートメニューから Java のショートカット、
    例えば「Configure Java」などを右クリックして管理者権限で起動する。
  • これが無ければ、Java のインストールディレクトリ
    (例) C:\Program Files\Java\jre7\bin
    から javacpl.exe を右クリックして管理者権限で起動する。
Javaコントロール・パネル の「更新」タブから、「更新を自動的にチェックする」チェックを外します。


対策2 Java Auto Updater を起動させない

ちょっと乱暴な方法。プロセスが常駐していて不人気である jusched プロセスが起動しないようにします。

コントロール パネル > システムとセキュリティ > 管理ツール
と辿って「システム構成」を開く、この「スタートアップ」タブから、Java の項目 (jusched.exe コマンドの項目) のチェックを外します。

ただ基本は Javaコントロール・パネル から操作するものだから、この状態で Java 自動更新を有効にすると、Java のスタートアップ項目が1つ追加されて [On / Off] の2つになっちゃう。Javaコントロール・パネル で「更新を自動的にチェックする」設定を解除しておけば jusched プロセスは停止するし常駐もしなくなります。


Java Auto Updater の仕組みについて

jusched.exe は Java アップデート スケジューラ、自動更新が有効だとプロセスが常駐している。
jucheck.exe は Java Auto Updaterプログラム(以前のJavaバージョンでは、という但し書きが Java 7 の資料にあったので、Java 8 だと違うのか?)

Javaコントロール・パネルを管理者権限で起動しないと設定の変更が保存できないのは、設定先がレジストリだからと思われる。

HKEY_CURRENT_USER\Software\JavaSoft\Java Update\Policy キーに EnableAutoUpdateCheck というバイナリ値があると、jusched プロセスは常駐しない(更新を自動的にチェックしない)様子です、バイナリ (REG_BINARY) なので中身はよく分からない。

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Update\Policy キー
こちらは Java 自動更新の有効/無効とは関係がなさそう。

jusched プロセスのログはこちら
%LOCALAPPDATA%\Temp\jusched.log


関連する資料のリンク

Javaコントロール・パネルでJava更新設定が保存されないのはなぜですか。
https://java.com/ja/download/help/javacpl.xml

Java自動更新とは何ですか。通知設定を変更するにはどうすればよいですか。
https://java.com/ja/download/help/java_update.xml

Java コントロールパネル (Update)
http://docs.oracle.com/javase/jp/7/technotes/guides/jweb/jcp.html

Windows Online Installation and Update FAQ
http://www.oracle.com/technetwork/java/javase/windows-faq-140694.html#processes
To shutdown these processes — jucheck.exe and jusched.exe —, simply uncheck the "Check for Updates Automatically" checkbox in the Update tab of Java Control Panel.

[MS Access] テーブルにコピペするとカラムが増えてしまう

Excel 等から MS Access のテーブルへデータをコピペした場合に、コピーした列数が多いとテーブルの定義が変わってしまい、カラムが増えてしまいます。これを防ぐには、データシート ビューにおけるオプションを変更します。

MS Access の「ファイル」メニュー > オプション > カレント データベース
と辿って、「データシート ビューでテーブルのデザインを変更できるようにする。」チェックを外します。

2014年7月20日日曜日

[VBA] If ステートメントでエラーとなる考慮

VBA では、If...Then...Else ステートメントに複数の条件をつなげるとエラーになるケースを考慮する必要がある。

他の言語ならば AND 演算子の条件は False と判定された時点でその後の条件式を処理しないが、VBA の場合は全ての条件式を処理してしまうので、条件式の前のチェックを続けて確認するとエラーとなってしまう。

(例) If 式1 And 式2 And 式3 Then の場合は
  • 他言語: 式1が False ならば、式2以降は判定しない。
  • VBA: 式1の結果に関わらず、式2以降も全て判定する。
これだと、式2を処理する前のチェックを式1で行うとエラーとなってしまうので、注意が必要である。

1.Null チェックと合わせて条件式をつなげる

Collection が Null の場合は、条件式1 (Not IsNull) 結果が False であるにも関わらず、後続の条件式も判定してしまう。
If Not IsNull(Collection) And Collection.Count > 0 Then
    Debug.Print "Count > 0"
End If
エラーメッセージ: 424 オブジェクトが必要です。

2.数値チェックと合わせて条件式をつなげる

Value が Null や Nothing の場合は、IsNumeric 関数が False であるにも関わらず、後続の CInt 関数でエラーとなってしまう。
If IsNumeric(Value) And CInt(Value) > 0 Then
    Debug.Print "Value > 0"
End If
エラーメッセージ: 91 オブジェクト変数または With ブロック変数が設定されていません。

2014年7月19日土曜日

Windows 標準の機能 でメールを送信する

メールを送信する方法はいくつかありますが、状況によって使い分ける必要があります。
判断材料としては、主に以下の選択肢になると思います。
  • SMTP認証の方法
  • 実行する環境

メール送信ライブラリ
SMTP 認証環境
ライブラリSMTP over SSLTLS/STARTTLS.NETVBA、VBS
CDO.Message×
System.Web.Mail×
System.Net.Mail×

SMTP 認証 はメールサーバが提供しているサービスにより使い分けることになります。
例えば Gmail ならこちら
https://support.google.com/mail/answer/13287

外部のネットワークへの送信は、プロバイダ(ISP) の Outbound Port 25 Blocking メール送信規制により ポート25番でのメール送信はできないはず。通常ならばサブミッションポート587番 (Submission Port) の STARTTLS 方式による SMTP 認証を使ってメールを送信することになります。

各ライブラリ毎のメール送信方法はこちら

[Windows] System.Net.Mail によるメール送信

System.Net.Mail を使って外部へメール送信する方法です。

VBA、VBScript のデフォルト状態では System.Net.Mail を使えません。
System.Net.Mail では TLS/STARTTLS (ポート587) の SMTP 認証方式を使います。SMTP over SSL (ポート465) には対応していないため、この場合は以下のエラーになります。
エラーメッセージ: 操作がタイムアウトしました。

System.Net.Mail がサポートする SMTP 認証方式について
http://msdn.microsoft.com/ja-jp/library/system.net.mail.smtpclient.enablessl.aspx
トランスポート層セキュリティを使用した安全な SMTP のための SMTP サービス拡張 (STARTTLS) のみをサポートします。
事前に SSL セッションが確立され、その後にプロトコル コマンドが送信されるという、代替接続方法もあります。 この接続方法は、SMTP/SSL、SMTP over SSL、または SMTPS と呼ばれることがあり、既定ではポート 465 を使用します。 SSL を使用するこの代替接続方法は、現在はサポートされていません。


Google のメールサーバ情報
https://support.google.com/mail/answer/13287

Windows PowerShell から System.Net.Mail を使って Gmail へメールを送信するサンプルです。

Windows PowerShell における System.Net.Mail メール送信サンプル

$mail = New-Object System.Net.Mail.MailMessage(送信者アドレス, 送信先アドレス)
$mail.Subject = "System.Net.Mail By Windows PowerShell"
$mail.Body = "Test Mail. " + [System.DateTime]::Now

$smtp = New-Object System.Net.Mail.SmtpClient
$smtp.Host = "smtp.gmail.com"
$smtp.Port = 587
$smtp.EnableSsl = $TRUE
$smtp.DeliveryMethod = [System.Net.Mail.SmtpDeliveryMethod]::Network
$smtp.Credentials = New-Object System.Net.NetworkCredential(Googleユーザ名, Googleパスワード)
$smtp.Send($mail)

関連する資料のリンク

System.Net.Mail 名前空間
http://msdn.microsoft.com/ja-jp/library/system.net.mail.aspx

[Windows] System.Web.Mail によるメール送信

System.Web.Mail を使って外部へメール送信する方法です。

ただしこのクラスの使用は現在推奨されていません。代わりに System.Net.Mail 名前を使うよう推奨されています。VBA、VBScript のデフォルト状態では System.Web.Mail を使えません。Windows PowerShell の場合は、アセンブリの読み込みが必要です。

メールの送信については、実装は CDO.Message を使用しています。
http://msdn.microsoft.com/ja-jp/library/system.web.mail.smtpmail.aspx
CDOSYS (Collaboration Data Objects for Windows 2000) メッセージ コンポーネントを使用してメッセージを送信するための、プロパティとメソッドを提供します。

CDO.Message では SMTP over SSL (ポート465) の SMTP 認証方式を使います。TLS/STARTTLS (ポート587) の SMTP 認証方式には対応していないため、この場合は以下のエラーになります。
エラーメッセージ: 転送においてサーバーに接続できませんでした。

Google のメールサーバ情報
https://support.google.com/mail/answer/13287

Windows PowerShell から System.Web.Mail を使って Gmail へメールを送信するサンプルです。

Windows PowerShell における System.Web.Mail メール送信サンプル

# アセンブリの読み込み
# 正式には LoadWithPartialName(非推奨) でなく Load メソッドを使うべき
[System.Reflection.Assembly]::LoadWithPartialName("System.Web")

$mail = New-Object System.Web.Mail.MailMessage
$mail.From = 送信者アドレス
$mail.To = 送信先アドレス
$mail.Subject = "System.Web.Mail By Windows PowerShell"
$mail.Body = "Test Mail. " + [System.DateTime]::Now

$uri = "http://schemas.microsoft.com/cdo/configuration/"
$mail.Fields.Item($uri + "sendusing") = 2
$mail.Fields.Item($uri + "smtpserver") = "smtp.gmail.com"
$mail.Fields.Item($uri + "smtpserverport") = 465
$mail.Fields.Item($uri + "smtpauthenticate") = 1
$mail.Fields.Item($uri + "sendusername") = Googleユーザ名
$mail.Fields.Item($uri + "sendpassword") = Googleパスワード
$mail.Fields.Item($uri + "smtpusessl") = $TRUE

# $smtp = New-Object System.Web.Mail.SmtpMail
[System.Web.Mail.SmtpMail]::SmtpServer = "smtp.gmail.com"
[System.Web.Mail.SmtpMail]::Send($mail)

名前空間URLのフィールドについて

上記で使用した 名前空間URL (http://schemas.microsoft.com/cdo/configuration/) のフィールドについては、こちらの記事を参照してください。
CDO.Messageによるメール送信

関連する資料のリンク

System.Web.Mail 名前空間
http://msdn.microsoft.com/ja-jp/library/system.web.mail.aspx

System.Net.Mail 名前空間
http://msdn.microsoft.com/ja-jp/library/system.net.mail.aspx

CDO.Message オブジェクト
http://msdn.microsoft.com/ja-jp/library/cc421174.aspx

http://schemas.microsoft.com/cdo/configuration/ 名前空間
http://msdn.microsoft.com/en-us/library/ms526318.aspx

Windows 標準の機能 (CDO.Message) でメールを送信する

CDO.Message を使って外部へメールを送信する方法です。
メールを送信する方法はいくつかありますが、CDO を選択する理由としては以下の場合になると思います。
  • VBA、VBScript を使ってメールを送信する。
  • SMTPサーバのポート465 (SMTP over SSL) を使う。

CDO.Message は SMTP over SSL (ポート465) の SMTP 認証方式を使います。TLS/STARTTLS (ポート587) の SMTP 認証方式には対応していないため、この場合は以下のエラーになります。
エラーメッセージ: -2147220973 転送においてサーバーに接続できませんでした。

Google のメールサーバ情報
https://support.google.com/mail/answer/13287

VBScript もしくは Windows PowerShell から CDO.Message を使って Gmail へメールを送信するサンプルです。

VBScript の CDO.Message メール送信サンプル

Set msg = CreateObject("CDO.Message")
msg.From = 送信者アドレス
msg.To = 送信先アドレス
msg.Subject = "CDO.Message Mail by VBScript"
msg.TextBody = "Test Mail. " & vbTab & Now

uri = "http://schemas.microsoft.com/cdo/configuration/"
msg.Configuration.Fields.Item(uri & "sendusing") = 2
msg.Configuration.Fields.Item(uri & "smtpserver") = "smtp.gmail.com"
msg.Configuration.Fields.Item(uri & "smtpserverport") = 465
msg.Configuration.Fields.Item(uri & "smtpauthenticate") = 1
msg.Configuration.Fields.Item(uri & "smtpusessl") = true
msg.Configuration.Fields.Item(uri & "sendusername") = Googleユーザ名
msg.Configuration.Fields.Item(uri & "sendpassword") = Googleパスワード
msg.Configuration.Fields.Update
msg.Send

Windows PowerShell の CDO.Message メール送信サンプル

$msg = New-Object -ComObject CDO.Message
$msg.From = 送信者アドレス
$msg.To = 送信先アドレス
$msg.Subject = "CDO.Message Mail by Windows PowerShell"
$msg.TextBody = "Test Mail. " + [System.DateTime]::Now

$uri = "http://schemas.microsoft.com/cdo/configuration/"
$msg.Configuration.Fields.Item($uri + "sendusing") = 2
$msg.Configuration.Fields.Item($uri + "smtpserver") = "smtp.gmail.com"
$msg.Configuration.Fields.Item($uri + "smtpserverport") = 465
$msg.Configuration.Fields.Item($uri + "smtpauthenticate") = 1
$msg.Configuration.Fields.Item($uri + "smtpusessl") = $TRUE
$msg.Configuration.Fields.Item($uri + "sendusername") = Googleユーザ名
$msg.Configuration.Fields.Item($uri + "sendpassword") = Googleパスワード
$msg.Configuration.Fields.Update()
$msg.Send()

名前空間URLのフィールドについて

CDOEX の Namespace URL について
上記で使用した 名前空間URL (http://schemas.microsoft.com/cdo/configuration/) のフィールドについてまとめます。

■sendusing フィールドについて
CdoSendUsing Enum 定数を使います
  • cdoSendUsingPickup (1)
    ローカルのSMTP(ピックアップ・ディレクトリ)を使用する
  • cdoSendUsingPort (2)
    SMTP ポートに接続する
  • cdoSendUsingExchange (3)
    Microsoft Exchange を使用する

sendusing Field
http://msdn.microsoft.com/en-us/library/ms873037.aspx

CdoSendUsing Enum
http://msdn.microsoft.com/en-us/library/ms876415.aspx

■smtpauthenticate フィールドについて
CdoProtocolsAuthentication Enum 定数を使います

  • cdoAnonymous (0)
    認証しません
  • cdoBasic (1)
    Basic認証を使います
  • cdoNTLM (2)
    NTLM認証を使います

smtpauthenticate Field
http://msdn.microsoft.com/en-us/library/ms873042.aspx

CdoProtocolsAuthentication Enum
http://msdn.microsoft.com/en-us/library/ms870465.aspx

関連する資料のリンク

CDO.Message オブジェクト
http://msdn.microsoft.com/ja-jp/library/cc421174.aspx

http://schemas.microsoft.com/cdo/configuration/ 名前空間
http://msdn.microsoft.com/en-us/library/ms526318.aspx

CDOEX Enumerations(URI のフィールド一覧)
http://msdn.microsoft.com/en-us/library/ms877952.aspx

2014年7月15日火曜日

PowerShell で「コンストラクターが見つかりません」エラー

Windows PowerShell でインスタンスを作成すると「コンストラクターが見つかりません。型*の適切なコンストラクターが見つかりません。」というエラーが発生します。

Singleton パターンなどのコンストラクターが無いクラスは、新規のインスタンスを作成できないので New-Object コマンドレットは使えません。静的メンバにアクセスする場合は[角かっこ]で記述します。

■ 記述例
[System.Web.Mail.SmtpMail]::SmtpServer = "smtp.gmail.com"

■ エラーメッセージの例
PS> $smtp = New-Object System.Web.Mail.SmtpMail
New-Object : コンストラクターが見つかりません。型 System.Web.Mail.SmtpMail の適切なコンストラクターが見つかりません。
発生場所 行:1 文字:19
+ $smtp = New-Object <<<<  System.Web.Mail.SmtpMail
    + CategoryInfo          : ObjectNotFound: (:) [New-Object]、PSArgumentException
    + FullyQualifiedErrorId : CannotFindAppropriateCtor,Microsoft.PowerShell.Commands.NewObjectCommand

■ 関連する資料のリンク

静的なクラスとメソッドの使用
http://technet.microsoft.com/ja-jp/library/dd347632.aspx

SmtpMail クラス
http://msdn.microsoft.com/ja-jp/library/system.web.mail.smtpmail.aspx

[PowerShell] Set-ExecutionPolicy コマンドで「レジストリ キー へのアクセスが拒否されました。」エラー

Windows PowerShell の Set-ExecutionPolicy コマンドで実行ポリシーを変更する際に「レジストリ キー へのアクセスが拒否されました。」というエラーが発生した、これは「管理者権限」が無いために発生します。この管理者とは管理者ユーザー(Administratorアカウント)を指すのではなく、UAC の管理者特権を意味します。Windows PowerShell を起動する時に右クリックして「管理者として実行」を選択することで解決できます。

エラーメッセージの例
PS> Set-ExecutionPolicy RemoteSigned

Set-ExecutionPolicy : レジストリ キー 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' へのアクセスが拒否されました。
発生場所 行:1 文字:20
+ Set-ExecutionPolicy <<<<  RemoteSigned
    + CategoryInfo          : NotSpecified: (:) [Set-ExecutionPolicy]、UnauthorizedAccessException
    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand

PowerShell で .NET の「型が見つかりません」エラー

Windows PowerShell で .NET Framework のクラスを使うと「型が見つかりません。この型を含むアセンブリが読み込まれていることを確認してください。」というエラーが発生した。これは .NET Framework のアセンブリはデフォルトでは全てロードされていないために発生しています。追加で必要なアセンブリを読み込む必要があります。

「型が見つかりません」エラーの例

PS> $mail = New-Object System.Web.Mail.MailMessage
New-Object : 型 [System.Web.Mail.MailMessage] が見つかりません。この型を含むアセンブリが読み込まれていることを確認してください。
発生場所 行:1 文字:19
+ $mail = New-Object <<<<  System.Web.Mail.MailMessage
    + CategoryInfo          : InvalidType: (:) [New-Object]、PSArgumentException
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

手順1: まず MSDN でこのクラスのアセンブリを確認する

(例)MailMessage クラス
http://msdn.microsoft.com/ja-jp/library/system.web.mail.mailmessage.aspx
アセンブリ: System.Web (System.Web.dll 内)

手順2: アセンブリ名を確認したら、そのアセンブリを読み込む

LoadWithPartialName メソッドを使う場合
PS> [System.Reflection.Assembly]::LoadWithPartialName("System.Web")
GAC    Version        Location
---    -------        --------
True   v2.0.50727     C:\windows\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll
ただし、LoadWithPartialName メソッドは便利だけど非推奨になっているので、正式にやるならば、次の Load メソッドを使います。Load メソッドはアセンブリの完全な表示名を要求されます。表示名に使う PublicKeyToken など各項目は、目的の dll ファイルを右クリックして「プロパティ」を参照すれば分かります。

Load メソッドを使う場合
PS> [System.Reflection.Assembly]::Load("System.Web, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a")

正しく読み込まれたことを確認できます
PS> [AppDomain]::CurrentDomain.GetAssemblies() | FINDSTR System.Web


■ 関連する資料のリンク

.NET Framework 2.0 で廃止予定の型とメンバの一覧 (アセンブリ単位)
http://msdn.microsoft.com/ja-jp/library/cc825681.aspx
LoadWithPartialName(String partialName)
このメソッドは非推奨になりました。代わりに、Assembly.Load() を使用してください。

アセンブリの読み込みのベスト プラクティス
http://msdn.microsoft.com/ja-jp/library/dd153782.aspx
LoadWithPartialName メソッドは、現在は使用されない形式として設定されています。 代わりに Assembly.Load メソッドを使用して、アセンブリの完全な表示名を指定することをお勧めします。

Assembly.LoadWithPartialName メソッド
http://msdn.microsoft.com/ja-jp/library/system.reflection.assembly.loadwithpartialname.aspx

Assembly.Load メソッド
http://msdn.microsoft.com/ja-jp/library/system.reflection.assembly.load.aspx

2014年7月13日日曜日

PowerShell を実行すると「スクリプトの実行がシステムで無効になっている」エラー

Windows PowerShell スクリプトを実行すると、スクリプトの実行が無効になっているというエラーが発生します。
PS> .\Test1.ps1
スクリプトの実行がシステムで無効になっているため、ファイル Test1.ps1 を読み込めません。詳細については、「get-help about_signing」と入力してヘルプを参照してください。
発生場所 行:1 文字:12
+ .\Test1.ps1 <<<<
    + CategoryInfo          : NotSpecified: (:) []、PSSecurityException
    + FullyQualifiedErrorId : RuntimeException

デフォルトでは実行ポリシーが無効になっており、第一歩からくじけそうです。
ポリシーを変更する必要があります。変更するには「管理者権限」が必要です。この管理者とは管理者ユーザー(Administratorアカウント)を指すのではなく、UAC の管理者特権を意味します。Windows PowerShell を起動する時に右クリックして「管理者として実行」を選択することで解決できます。

ポリシーの変更コマンド
PS> Set-ExecutionPolicy RemoteSigned

PowerShell 実行ポリシーの一覧
  • Restricted (デフォルトの設定)
    実行できるスクリプトはありません。Windows PowerShell は対話型モードでのみ使用できます。
  • AllSigned
    信頼できる発行元が署名したスクリプトのみを実行できます。
  • RemoteSigned (作成したスクリプトを実行するならば、このポリシーを選びます)
    ダウンロードしたスクリプトは信頼できる発行元が署名した場合にのみ実行できます。
  • Unrestricted
    制限なし。すべての Windows PowerShell スクリプトを実行できます。


管理者特権が無いと、このようなエラーとなります。
Set-ExecutionPolicy : レジストリ キー 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' へのアクセスが拒否されました。
発生場所 行:1 文字:20
+ Set-ExecutionPolicy <<<<  Restricted
    + CategoryInfo          : NotSpecified: (:) [Set-ExecutionPolicy]、UnauthorizedAccessException
    + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand

関連する資料のリンク

Set-ExecutionPolicy コマンドレットの使用
http://technet.microsoft.com/ja-jp/library/ee176961.aspx

2014年7月11日金曜日

[Windowsコマンド] システム エラー 5 が発生しました。アクセスが拒否されました。

コマンドプロンプトで、「システム エラー 5 が発生しました。」というエラーメッセージが出た場合について。
(例)
C:\>net start "task scheduler"
システム エラー 5 が発生しました。
アクセスが拒否されました。

これは管理者権限がないことで発生することがあります。この管理者とは、管理者ユーザー(Administratorアカウント)を指すのではなく、UAC の管理者特権を意味します。

ユーザー アカウント制御(UAC: User Account Control)が有効であると、管理者ユーザーであっても権限が制限されます。これは コマンド プロンプト を起動する時に「管理者として実行」を選択することで解決できます。管理者権限で実行すればコマンドプロンプトのタイトルに「管理者:」と表示されています。

コマンドプロンプトのアイコンを右クリックして「管理者として実行」を選択する、もしくはショートカットキーであれば、コマンドプロンプト(cmd.exe)を選択して、[Ctrl] + [Shift] + [Enter] とすれば管理者特権で実行可能です。

[Windowsコマンド] FOR ループで変数の値が変わらない

Windows のコマンドプロンプトもしくはコマンドスクリプトで、FOR ループの中にある環境変数が更新されない。これは遅延環境変数の展開が無効になっている場合に発生します。

遅延環境変数が無効(デフォルトの状態)

遅延環境変数が無効であれば、繰り返し毎に変数は変更されません。
FOR /L %%i IN (1,1,3) DO (
    SET value=%%i
    ECHO %TIME% %value%
    TIMEOUT /T 1
)
<実行結果>
9:31:34.61 3
9:31:34.61 3
9:31:34.61 3
時刻、カウンタともに変わらない。

遅延環境変数が有効

遅延環境変数を有効 (SETLOCAL ENABLEDELAYEDEXPANSION) にすると、繰り返し毎に変数が変更されるようになります。
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /L %%i IN (1,1,3) DO (
    SET value=%%i
    ECHO !TIME! !value!
    TIMEOUT /T 1
)
ENDLOCAL
<実行結果>
9:31:37.16 1
9:31:38.13 2
9:31:39.21 3
毎回異なっている。

FOR 以外の繰り返し

遅延環境変数が無効 (SETLOCAL DISABLEDELAYEDEXPANSION) であっても、FOR を使わない繰り返しであれば変数は毎回変更されます。
SETLOCAL DISABLEDELAYEDEXPANSION
:LOOP
    IF %value% GEQ 6 GOTO :EOF

    SET /A value=%value% + 1
    ECHO %TIME% %value%
    TIMEOUT /T 1
GOTO :LOOP
ENDLOCAL
<実行結果>
9:31:40.22 4
9:31:41.20 5
9:31:42.19 6
毎回異なっている。

2014年7月10日木曜日

[MS Access] 読み取り専用のリンクテーブル

Microsoft Access のリンクテーブルからデータ編集を禁止したいという要望は稀にあるが、リンクテーブルに ReadOnly 設定は残念ながら無い、だだ対策はある。

1.データベースアカウントを参照権限に限定する

リンクテーブルからのデータ編集を防ぎたいというのは、セキュリティの観点からデータ操作を制限したいという意図のはず。もし外部の DBMS に接続しているリンクテーブルならば、データベースアカウントの権限から 挿入(INSERT)/更新(UPDATE) 権限を除きます。

Oracleならば、データを編集すると ORA-01031: 権限が不足しています エラーになります。

2.ビューにリンクする

読み取り専用の VIEW に対してリンクすれば、リンクテーブルからのデータ編集を防げます。
Oracleならば、WITH READ ONLY 句 を付けた VIEW のデータを編集すると、ORA-01732: このビューではデータ操作が無効です エラーになります。

3.ODBCデータソースを読み取り専用にする

ODBC データソース を使用しているリンクテーブルならば、使用するODBC データソースの「読み取り専用」設定を ON にする。これは「ODBC データソース アドミニストレーター」画面から設定を変更できます。

<画面の起動方法>
コントロールパネル > システムとセキュリティ > 管理ツール > データ ソース (ODBC)

4.リンクテーブルでなくパススルー クエリを使う

ODBC データソース を使用しているリンクテーブルならば、リンクテーブルを止めてパススルー クエリに置き換えれば、データを編集できません。

HOW TO: Base Subforms on SQL Pass-Through Queries in Access 2000
http://support.microsoft.com/kb/209116
SQL パススルー クエリは読み取り専用です

Access で SQL パススルー クエリを作成する方法
http://support.microsoft.com/kb/303968/ja

5.リンクテーブルでなくクエリを使う(ODBC データソース の場合)

(次第に変な対策になりつつありますが)編集できないクエリを使います。
ただしパススルー クエリを使わないならば、クエリの入力 Table としてリンクテーブルが必要になります。これだとリンクテーブルを使われることになる(元の課題に戻る)ので、リンクテーブルを「隠しオブジェクト」に設定します。

ただし「隠しオブジェクト」だからといって一切触れないわけではないです、ご注意を!

さらに、リンクテーブルのデータをそのまま表示するだけの単純なクエリだとデータが編集可能なので、クエリのプロパティを変更します。

<クエリのプロパティを変更>
クエリの「レコードセット」プロパティを「スナップショット」に変更

6.リンクテーブルでなくクエリを使う(外部の Access ファイルの場合)

Access ならばリンクテーブルが無くてもクエリの入力 Table として使えます。外部の Access ファイルをパススルークエリでリンクすることはできません。
(SQL例)
SELECT * FROM [社員] IN [C:\data\名簿.accdb]
FROM 句
http://office.microsoft.com/ja-jp/access-help/HA001231451.aspx

もしくは、テーブルを結合する時に便利な書き方
SELECT * FROM [C:\data\名簿.accdb].[社員]

これもデータを全て表示するだけの単純なクエリだとデータが編集可能なので、クエリのプロパティを変更します

<クエリのプロパティを変更>
クエリの「レコードセット」プロパティを「スナップショット」に変更

7.データベースファイルを読み取り専用にする(外部の Access ファイルの場合)

もちろんファイルを読み取り専用にしてしまうと一切更新できません!。ただリンク先のファイルを読み取り専用にしてしまえば、リンクテーブルからデータの編集を防ぐことはできます。

2014年7月7日月曜日

[VBA] Err.Raise で「プロシージャの呼び出し、または引数が不正です」エラー

VBA のエラー処理として Err.Raise すると、以下のエラーメッセージが発生する場合について。
(エラーメッセージ) 5 VBAProject プロシージャの呼び出し、または引数が不正です。

Err オブジェクトの Number プロパティに 0 を設定することはできません(マイナスは可)。エラーコードが定数でない場合は 0 を代入していないことを確認してください。
Err.Raise Number:=0, Description:="Test Error"


関連する資料のリンク

Err オブジェクト (Visual Basic)
http://msdn.microsoft.com/ja-jp/library/ka13cy19.aspx

[Windows] ディスクの空き領域を照会するコマンド

Windowsのコマンドでディスクの空き領域を照会する方法です。

FSUTIL コマンドで空き領域を照会できます。ただし管理者特権でコマンドプロンプトを起動しておく必要があります。コマンドプロンプト(cmd.exe)を右クリックして、「管理者として実行」を選択することで管理者特権で実行できます。

(例) C ドライブの空き領域を確認する
fsutil volume diskfree C:
空きバイト総数               : 163447545856
バイト総数                   : 201504845824
利用可能な空きバイト総数     : 163447545856
単位がByteなので、GBに変換するには 1,073,741,824 (= 1,024 * 1,024 * 1,024) で割ります。

2014年7月6日日曜日

[MS Access] リンクテーブルの一覧を抽出する

VBA でリンクテーブルに一覧を抽出するには、DAO.TableDef の Attributes プロパティとビット演算することで判別できます。

リンクテーブルにも以下のような区別があります。
  • TableDefAttributeEnum.dbAttachedODBC
    MS SQLServer など、ODBC を使うリンクテーブル
  • TableDefAttributeEnum.dbAttachedTable
    MS Access などの非ODBCのリンクテーブル

リンクテーブルの一覧を表示するサンプル
Dim TableDef As DAO.TableDef
For Each TableDef In CurrentDb.TableDefs
    If TableDef.Attributes And (TableDefAttributeEnum.dbAttachedODBC Or TableDefAttributeEnum.dbAttachedTable) Then
        ' ODBC および 非ODBCリンクテーブル の場合
        Debug.Print TableDef.Name
    End If
Next
特定のリンクテーブルのみ抽出する場合は、条件式を以下のようにします。
If TableDef.Attributes And TableDefAttributeEnum.dbAttachedODBC Then
    ' MS SQLServer など(ODBCリンクテーブル)の場合
End If

If TableDef.Attributes And TableDefAttributeEnum.dbAttachedTable Then
    ' MS Access など(非ODBCリンクテーブル)の場合
End If

関連する資料のリンク

TableDefAttributeEnum 列挙 (DAO)
http://msdn.microsoft.com/ja-jp/library/office/ff194433.aspx

[Windows] コマンドスクリプトからユーザ入力を求める

コマンドスクリプトの途中でユーザ入力を必要とする場合は、以下の方法があります。

1.選択肢から選ぶ

1文字だけを入力させる場合には、CHOICEコマンドが使えます。指定した入力以外は受け付けません。入力結果は、ERRORLEVEL 環境変数で確認できます。

<サンプル>
これは Yes/No/Cancel を入力させる場合の例です。
choice /C YNC /M "Yes/No/Cancel を選択してください"
If ERRORLEVEL 3 (
 echo Input Cancel
) ELSE If ERRORLEVEL 2 (
 echo Input No
) ELSE If ERRORLEVEL 1 (
 echo Input Yes
)
<実行結果>
Yes/No/Cancel を選択してください [Y,N,C]? Y
Input Yes

2.テキストの入力を求める

柔軟に入力を求める場合は、SET /P コマンドが使えます。

<サンプル>
set /P USER_INPUT="入力してください: "
入力してください: hello
echo %USER_INPUT%
hello

3.入力するまで待つ

ユーザ入力を待つ場合は、PAUSEコマンドが使えます。何の入力でも良いので、ユーザ入力があるまで待ちます。

<サンプル>
pause
続行するには何かキーを押してください . . .

4.スリープする

一時的にsleepするならばTIMEOUTコマンドが使えます。何も入力しないとタイムアウトするまで待ちますが、ユーザ入力があると処理を続行します。

<サンプル>
timeout /t 5
5 秒待っています。続行するには何かキーを押してください ...

[MS Access] VBScript を使ってデータベースアクセス

VBScript から Microsoft Access へ接続する場合は、VBA とほぼ同等のコードで実装できるが、VBScript の言語使用として若干注意することがある、それは
  • データ型は全てバリアント型
  • On Error GoTo は使用できない
データ型については、As Integerみたいなデータ型定義を付けられないことになる。エラー処理については、ステップ毎にエラーチェックすることになるので、やはり面倒です。エラーチェックを関数にまとめてもステップ毎に洩れなく確認することには変わりないので、あまり複雑な処理を VBScript でやるのは辛いです。

VBScript のデータ型
http://msdn.microsoft.com/ja-jp/library/cc392195.aspx
VBScript では、バリアント型 (Variant) と呼ばれるデータ型のみを使用します。

VBScript でのエラー処理
http://msdn.microsoft.com/ja-jp/library/cc407944.aspx
VBScript で On Error GoTo は使用できません。次の例に示すように、その代わりに On Error Resume Next を使用し、その後、Errors コレクションの Err.Number プロパティと Count プロパティの両方を調べます。

VBScript から Microsoft Access へ接続して、SELECT文を実行するサンプルです。
Option Explicit

Function GetNow()
    Dim Engine, Database, Recordset

    On Error Resume Next

    Set Engine = CreateObject("DAO.DBEngine.120")
    If Err.Number <> 0 Or Engine.Errors.Count > 0 Then
        WScript.Echo Err.Number & vbTab & Err.Source & vbTab & Err.Description
        GetNow = False
        Exit Function
    End If

    Set Database = Engine.OpenDatabase("C:\work\SAMPLE1.mdb")
    If Err.Number <> 0 Or Engine.Errors.Count > 0 Then
        WScript.Echo Err.Number & vbTab & Err.Source & vbTab & Err.Description
        GetNow = False
        Exit Function
    End If

    Set Recordset = Database.OpenRecordset("SELECT now")  ' システム時刻nowのみ
    If Err.Number <> 0 Or Engine.Errors.Count > 0 Then
        WScript.Echo Err.Number & vbTab & Err.Source & vbTab & Err.Description
        GetNow = False
        Exit Function
    End If

    WScript.Echo Recordset(0)
    GetNow = True
End Function

if GetNow then
    WScript.Quit(0)
Else
    WScript.Echo "異常終了しました。"
    WScript.Quit(1)
End If

実行方法は、CScript [File Name].vbs

[MS Access] INDEX の定義を一覧表示する

Microsoft Access のインデックス定義を一覧表示するには、DAO.Index のプロパティを参照することで確認できます。
例えば
Sub OutputIndexes()
    Dim TableDef As DAO.TableDef
    Dim Index As DAO.Index

    For Each TableDef In CurrentDb.TableDefs
        If TableDef.Attributes And TableDefAttributeEnum.dbSystemObject Then
            ' システムテーブルは除く
        Else
            For Each Index In TableDef.Indexes
                Debug.Print TableDef.Name & "," & GetIndexDef(Index)
            Next
        End If
    Next
End Sub

Function GetIndexDef(Index As DAO.Index) As String
    Dim Text As String
    Text = Index.Name

    If Index.Primary Then
        Text = Text & ",Primary"
    Else
        Text = Text & ","
    End If
    If Index.Unique Then
        Text = Text & ",Unique"
    Else
        Text = Text & ","
    End If
    If Index.Foreign Then
        Text = Text & ",Foreign"
    Else
        Text = Text & ","
    End If
    If Index.Required Then
        Text = Text & ",Required"
    Else
        Text = Text & ","
    End If
    If Index.IgnoreNulls Then
        Text = Text & ",IgnoreNulls"
    Else
        Text = Text & ","
    End If
    If Index.Clustered Then
        Text = Text & ",Clustered"
    Else
        Text = Text & ","
    End If

    Dim Field As DAO.Field
    For Each Field In Index.Fields
       Text = Text & "," & Field.Name
    Next

    GetIndexDef = Text
End Function

関連する資料のリンク

Index オブジェクト (DAO)
http://msdn.microsoft.com/ja-jp/library/office/ff197655.aspx

2014年7月5日土曜日

[MS Access] システムテーブル(MSys*) を区別する方法

Microsoft Access のシステムテーブルを VBA で区別するには名前 (MSys*) という方法もあるけれど、プロパティから判定するには TableDefAttributeEnum 列挙体 を使う。
Dim TableDef As DAO.TableDef
For Each TableDef In CurrentDb.TableDefs
    If TableDef.Attributes And TableDefAttributeEnum.dbSystemObject Then
        Debug.Print "システム テーブル: " & TableDef.Name
    End If
Next

関連する資料のリンク

TableDefAttributeEnum 列挙 (DAO)
http://msdn.microsoft.com/ja-jp/library/office/ff194433.aspx

Windows 標準の機能(makecab コマンド)でファイルを分割する

使いなれたファイル分割ソフトが使えない環境でも、makecab コマンドで cab ファイル形式に分割できます。

<使い方>
最小限の設定で使う場合の設定手順

1.設定ファイル (directives_file) を作る

例えば、data.ddf というファイルとします。
.Set MaxDiskSize=CDROM                ; Default is 1.44M floppy
.Set CabinetNameTemplate=data.*.cab   ; Default is *.cab( * は連番になる)
.Set DiskDirectoryTemplate=           ; Default is disk*( * は連番になる)
.Set Compress=OFF                     ; Default is ON(圧縮効果があるファイルならどうぞ)
source-file-name.zip                  ; 分割するファイル、もちろん絶対パスや "" で囲む場合もあり

MaxDiskSize は定型サイズ (1.44M / 1.25M / 1.2M / 720K / 360K / CDROM) が使えます。だが CDROM 以外は全部小さすぎるので、適当なサイズを指定するならば Byte で指定することになります、(M / K の単位は定型サイズしか使えなかった。)

DiskDirectoryTemplate は分割した cab ファイルが入るサブディレクトリがいらないならば、空を指定します。

2.コマンドを実行

makecab /F data.ddf

data.*.cab とともに、setup.inf、setup.rpt ファイルも出力されます

3.分割したファイルを結合

1番目の cab ファイルをダブルクリックして目的のファイルを展開すれば、ファイルが結合された状態で展開されます。


関連する資料のリンク

コマンドライン リファレンス (Makecab)
http://technet.microsoft.com/ja-jp/library/hh875545.aspx

directives_file のパラメータ概要
Microsoft Cabinet Format
http://technet.microsoft.com/ja-jp/library/bb417343.aspx#var

2014年7月3日木曜日

[Windows] FSUTIL を実行すると、「管理者特権が必要です」エラー

コマンドプロンプトで fsutil コマンドを実行すると、「FSUTIL ユーティリティには管理者特権が必要です」エラーが表示される。この管理者とは、管理者ユーザー(Administratorアカウント)を指すのではなく、UAC の管理者特権を意味します。

ユーザー アカウント制御(UAC: User Account Control)が有効であると、管理者ユーザーであっても権限が制限されます。これは コマンド プロンプト を起動する時に「管理者として実行」を選択することで解決できます。

管理者特権でコマンドを実行するにはどうすればいいですか。
http://windows.microsoft.com/ja-jp/windows/command-prompt-faq

ショートカットキーであれば、コマンドプロンプト(cmd.exe)を選択して、[Ctrl] + [Shift] + [Enter] とすれば管理者特権で実行可能です。


コマンドプロンプトは、下記の理由で互換モードの設定はできません。
メッセージ:「このプログラムはこのバージョンの Windows の一部であるため、互換モードを設定できません。」
しかし、一般のアプリケーションであれば管理者特権のエラーを回避するために「互換モード」の選択肢があります。

ヒント: 必ず管理者として実行するようにアプリケーションを構成する
http://technet.microsoft.com/ja-jp/windows/ff189336.aspx


セキュリティ的に問題があるので全く勧めるつもりはありませんが、UAC をオフにもできます。

ユーザー アカウント制御をオンまたはオフにする
http://windows.microsoft.com/ja-jp/windows/turn-user-account-control-on-off

2014年7月2日水曜日

[MS Access] DAO では 十進数(Decimal)型の定義ができない

Microsoft Access へ DAO で接続して CREATE TABLE 文を実行する場合に、Decimal型のフィールド(カラム)が含まれているとエラーになる。

エラーメッセージ:「3292 DAO.Database フィールド定義の構文エラーです。」

Microsoft Access のユーザー インターフェイスからは Decimal型 を使用することができますが、DDL ステートメントでは使えません。対策としては、CREATE TABLE の実行だけは DAO でなく ADO を使用すると解決できます。

同じく DDL からは使えないデータ型は以下の通り。
  1. 十進数(Decimal)
  2. オートナンバー型(AutoNumber)
  3. ハイパーリンク型(HyperLink)
  4. ルックアップ ウィザード(Lookup)

関連する資料のリンク

Access で使用できるフィールドのデータ型 (MDB)
http://office.microsoft.com/ja-jp/access-help/HP005238518.aspx

データ型とフィールド プロパティの概要
http://office.microsoft.com/ja-jp/access-help/HA010233292.aspx

How to use common Data Definition Language (DDL) SQL statements for the Jet database engine
http://support.microsoft.com/kb/180841
CREATE TABLE TestAllTypes (
  MyText       TEXT(50),
  MyMemo       MEMO,
  MyByte       BYTE,
  MyInteger    INTEGER,
  MyLong       LONG,
  MyAutoNumber COUNTER,
  MySingle     SINGLE,
  MyDouble     DOUBLE,
  MyCurrency   CURRENCY,
  MyReplicaID  GUID,
  MyDateTime   DATETIME,
  MyYesNo      YESNO,
  MyOleObject  LONGBINARY,
  MyBinary     BINARY(50)
)

2014年7月1日火曜日

[MS Access] リンクテーブルを動的に更新する。

実は負荷が高いAccessアプリケーションでリンクテーブルを処理中に頻繁に更新すると、

エラーメッセージ:「3276 DAO.QueryDef データベースのオブジェクト参照が正しくありません。」
http://answers.microsoft.com/ja-jp/office/forum/office_2010-access/access2010%E3%81%A7%E3%83%87%E3%83%BC%E3%82%BF/98c99dce-007d-e011-9b4b-68b599b31bf5
(指定した Database オブジェクトは、OpenDatabase メソッドで作成されたものではないか、または既に変更されているために無効になっています。)

という再現性が困難なエラーに悩まされることがあります。それには注意しつつ・・・
TableDef オブジェクトがリンクテーブルであることを判定するには、TableDefAttributeEnum 列挙体を使います。

以下は、リンクテーブルの接続先を切り替えるサンプル
Dim TableDef As DAO.TableDef
For Each TableDef In CurrentDb.TableDefs
    If TableDef.Attributes And TableDefAttributeEnum.dbAttachedTable Then
        TableDef.Connect = ";DATABASE=C:\work\Sample1.accdb"
    End If
Next

関連する資料のリンク

TableDefAttributeEnum 列挙 (DAO)
http://msdn.microsoft.com/ja-jp/library/office/ff194433.aspx

TableDef.Attributes プロパティ (DAO)
http://msdn.microsoft.com/ja-jp/library/office/ff834701.aspx

MS Access のクエリ定義を全てエクスポート

Microsoft Access のクエリ定義をファイルにエクスポートする機能はありません、クエリのSQLをテキストファイルに出力することになります。
下にサンプルを挙げますが、参照設定として以下の追加が必要です。
  • Microsoft Scripting Runtime
参照設定は、Visual Basic Editor(VBE)のメニューから、ツール(T) > 参照設定(R) と辿ります。

VBAモジュールをファイルにエクスポートするサンプル
Sub ExportQuery()
    Dim Fso As New Scripting.FileSystemObject
    Dim Fout As TextStream
    Dim OutDir As String

    OutDir = Fso.GetParentFolderName(CurrentDb.Name) & "\query"
    If Not Fso.FolderExists(OutDir) Then
        Fso.CreateFolder (OutDir)
    End If

    Dim QueryDef As DAO.QueryDef
    For Each QueryDef In CurrentDb.QueryDefs
        Set Fout = Fso.CreateTextFile(Filename:=OutDir & "\" & QueryDef.Name & ".sql")
        Fout.Write QueryDef.SQL
        Fout.Close
    Next
End Sub

2014年6月30日月曜日

MS Access のVBAモジュールを全てエクスポート

Microsoft Access のVBAモジュール(プログラム ソースコード)を一括でファイルにエクスポートするには、VBComponent.Export メソッドを使います。
下にサンプルを挙げますが、参照設定として以下の2つが人必要です。
  • Microsoft Visual Basic for Applications Extensibility
  • Microsoft Scripting Runtime
参照設定は、Visual Basic Editor(VBE)のメニューから、ツール(T) > 参照設定(R) と辿ります。

VBAモジュールをファイルにエクスポートするサンプル
Sub ExportComponents()
    Dim Fso As New Scripting.FileSystemObject
    Dim OutDir As String

    OutDir = Fso.GetParentFolderName(CurrentDb.Name) & "\src"
    If Not Fso.FolderExists(OutDir) Then
        Fso.CreateFolder (OutDir)
    End If

    Dim Component As VBComponent
    For Each Component In Application.VBE.ActiveVBProject.VBComponents
        Component.Export Filename:=OutDir & "\" & Component.Name & GetExtension(Component.Type)
    Next
End Sub

Function GetExtension(ComponentType As vbext_ComponentType) As String
    Select Case ComponentType 
    Case vbext_ComponentType.vbext_ct_ClassModule
        GetExtension= ".cls"
    Case vbext_ComponentType.vbext_ct_MSForm
        GetExtension= ".frm"
    Case vbext_ComponentType.vbext_ct_StdModule
        GetExtension= ".bas"
    Case vbext_ComponentType.vbext_ct_ActiveXDesigner
        GetExtension= ".cls"
    Case vbext_ComponentType.vbext_ct_Document
        GetExtension= ".cls"
    End Select
End Function

MS Access を使った大規模な開発のリスク

Microsoft Accessで大規模なデータベースを構築するのは無茶ですが、コストに負けて採用してしまうことはある。それでも MS Access を使うことになった場合は、以下のことに気を付けるべきです。

1.データサイズ

Access の仕様として公開されている制限があります。
  • Access データベース (.accdb) ファイルのサイズは 2GB
  • テーブルのサイズも 2GB
Access 2010 の仕様
http://office.microsoft.com/ja-jp/access-help/HA010341462.aspx

運用中に 2GB に到達してしまうと、データベースファイルはほぼ確実に破損します。ファイルを分割すれば、ファイルサイズの制限は解決できるかもしれませんが、テーブルサイズの制限を超過してしまう場合は、テーブルを分割して解決できるでしょうか?

じつは仕様書には書かれていない制限もいくつかあります。例えば、
  • クエリ結果のサイズ(下記を参照)
  • 連続で開く処理と閉じる処理を行うと、データファイルが破損する。(下記を参照)
これ以外にも MS Accessで大量のデータを扱うには以下の懸念があります。
  • オプティマイザー、統計情報が貧弱
  • 最適な実行計画に誘導することができない
  • SQLチューニングのツールが無い
また分割したテーブルを UNION 統合する必要があるならば検索時間は長くなり、大量のデータで処理回数が増加すれば処理が終わらないという問題が出てきます。テーブルのサイズ上限は Access の採用可否の判断材料とすべきです。

2.クエリサイズ

エラーメッセージ:「3183 DAO.Database クエリを完了できません。クエリ結果のサイズがデータベースの最大サイズ (2GB) より大きいか、クエリ結果を一時的に保存するディスクの空き容量が不足しています。」

クエリの仕様として Accessの仕様書にも書くべきだと思うが、クエリにも制限があります。さらに「クエリ結果のサイズ」が指すものが曖昧で、出力結果のサイズでもあり、内部の一時領域でもある様子です。内部的にはどういう実行計画になるか分からないので、この制限を意識するデータ量になるならば、Access の採用可否の判断材料とすべきです。

3.連続で開く処理と閉じる処理を行うと、データファイルが破損する

これ言い換えると、「繰り返しのベースとなるレコードが 40,000 件を超えており、データファイルが分割されているため処理毎に接続を切り替えるとデータファイルが破損する。」これも 40,000件 をAccess の採用可否の判断材料としたほうが無難です、理由は下記に挙げるように想定外の問題が多いため。

http://support.microsoft.com/kb/283849
Access 2002 以降の破損したデータベースをトラブルシューティングおよび修復する方法
ループ処理の中で、開く処理と閉じる処理を大量に行うことを避けます (40,000 回を超えて、連続で開く処理と閉じる処理を行うと破損する場合があります)。


この対策としては、接続したままにする手段がある。
  • リンクテーブルを開いたままにする
  • DAO.DBEngine.OpenDatabaseでデータベースを開いたままにする

http://office.microsoft.com/ja-jp/access-help/HP005187453.aspx
Access データベースのパフォーマンスを向上させる
リンクしたデータベースを開いたままにしておくと、メイン データベース、テーブル、およびフォームを開く際のパフォーマンスが向上します。(中略)また、関連する .ldb ファイルの作成や削除を防ぐこともできます。


ただしリンクテーブルとDAO.DBEngine.OpenDatabase は接続が衝突する。
エラーメッセージ:「3734 DAO.Workspace マシン 'localhost' のユーザ 'Admin' がデータベースを開けない状態、またはロックできない状態にしています。」

それに繰り返し回数が少なくても、必ずしも安全ではないのは以下の通り。
エラーメッセージ:「3051 DAO.Database ファイル 'C:\work\Sample.laccdb' を開くか、書き込むことができませんでした。他のユーザが排他的に開いているか、データの表示と書き込みを行う権限がありません。」
排他モードでなくともシングルユーザでの接続を繰り返すと発生する可能性がある。ロックファイル(laccdb)のオーバーヘッドは無視できないと考えたほうが良い。データベースを同時に使用できるユーザーの数は 255 まで可能である仕様なのは本当なのだろうか?

MS Access の実行計画を参照する

Microsoft Access にもオプティマイザと統計情報はある。ただし最適な実行計画に誘導することはできないので、検索速度の不安定リスクは消えない。

Access 2000 でクエリを最適化する方法
http://support.microsoft.com/kb/209126/ja

MS Access における検索のパフォーマンス改善は、SQL について対策するのであれば Rushmore を使えるようにすることが第一の目標となる。オプティマイザが決定した実行計画を出力するには、レジストリのキーを設定することで操作できる。
SQLの実行毎に、showplan.out ファイルがユーザのマイ ドキュメント(C:\Users\UserName\Documents)もしくは、カレントディレクトリに出力されます。ここに using rushmore と出力されると、クエリの検索で Rushmore が使えたことが分かる。
  1. キーを追加する (※ Access 2007 の場合)
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\12.0\Access Connectivity Engine\Engines\Debug
  2. このキーに文字列値を追加する
    JETSHOWPLAN=ON
JETSHOWPLAN in Access2013 (※ Access 2013 の場合)
http://social.msdn.microsoft.com/Forums/office/en-US/b786a029-fa8c-4556-a40c-e749ba73499e/jetshowplan-in-access2013

出力例1(主キーの場合)

- Inputs to Query -
Table 'TEST_TABLE1'
- End inputs to Query -

01) Restrict rows of table TEST_TABLE1
      using index 'Pri-Key'
      for expression "USER_NAME='User1'"

出力例2(rushmoreの場合)

- Inputs to Query -
Table 'TEST_TABLE1'
- End inputs to Query -

01) Restrict rows of table TEST_TABLE1
      using rushmore
      for expression "USER_NAME='User1'"
02) Group result of '01)'

ただし SHOWPLAN の出力は恐ろしく使えない。例えば、
  • 複数のSQLを実行したら、どのSQLの実行計画か全く分からない
    複数のテーブルを結合すると、Temp02 とか内部の名称で出力されるので、アプリケーションのボトルネックとなる SQL をコストの重い順に探すような使い方はできない
  • SHOWPLAN についてのリファレンスが MSDN に無い!
  • 出力内容が恐ろしく薄い
    SQLの区別や処理時間やコストなど、チューニングに必要な情報が出力されない
これでは、検索の実行計画に Rushmore を使ったかどうかのチェックを、SQL の1つ毎に確認する程度の使い方しかできない。


Rushmore や、これ以外のパフォーマンスチューニングについての資料など。

Access データベースのパフォーマンスを向上させる
http://office.microsoft.com/ja-jp/access-help/HP005187453.aspx

Access のパフォーマンスを向上させる
http://office.microsoft.com/ja-jp/access-help/HA010235589.aspx

インデックスの作成と使用によりパフォーマンスを向上させる
http://office.microsoft.com/ja-jp/access-help/HA010210347.aspx

ACC: Access 2.0、Access 95 および Microsoft Access 97 でクエリを最適化する方法
http://support.microsoft.com/kb/112112

Visual Basic でクエリーを最適化する方法
http://support.microsoft.com/kb/172199/ja

Using Rushmore Query Optimization to Speed Data Access
http://msdn.microsoft.com/en-us/library/aa975907.aspx

2014年6月29日日曜日

[VBA] 文字列と数値を比較しても、エラーにならないケース

VBAでは、関数の戻り値と異なるデータ型を比較してもエラーとならない。
例えば
If 3 < Left("ABC", 1) Then
  Debug.Print "3 < A"
Else
  Debug.Print "3 >= A"
End If

<処理結果>
3 < A


ただし、数値と文字のリテラルを比較すると実行時エラーとなるので、方式が統一されているわけではない。
例えば
If 3 < "A" Then
  Debug.Print "3 < A"
End If

<処理結果>
実行時エラー13:型が一致しません。

[MS Access] SQLのユーザ定義関数で Err.Raise しても捕まえられない

Microsoft Access で、SQLのユーザ定義関数の中でランタイム エラーを生成する場合は、Err.Raise メソッドではSQLを実行した側でエラー処理ルーチン (On Error GoTo) で捕まえることができない。

ランタイム エラーを捕まえられないと、VBAのエラーダイアログが表示されてしまう。SQLの中で発生したランタイム エラーを捕まえるには、CVErr関数でユーザー指定のエラーを戻すことになります。ただし欠点があり、関数戻り値のデータ型をVariant型に変更する必要があります。Variant型で数値を戻すと、SQL中では文字列として扱われるので厄介なのです。

1.ランタイム エラーを捕まえられないケース

Public Function Func1(Value As Integer) As Integer
  Err.Raise Number:=100, Source:="UserDefined", Description:="メッセージ"
  Func1 = Value
End Function

Sub Test1()
  On Error GoTo ErrorHandler
  Call CurrentDb.OpenRecordset("SELECT Func1(123)")
  Exit Sub

ErrorHandler:
  ' ここには到達できない。
  Debug.Print Err.Number & vbTab & Err.Source & vbTab & Err.Description
End Sub


2.CVErr関数の戻り値を返すケース

Public Function Func2(Value As Integer) As Variant
  Func2 = CVErr(100)
End Function

Sub Test2()
  On Error GoTo ErrorHandler
  Call CurrentDb.OpenRecordset("SELECT Func2(123)")
  Exit Sub

ErrorHandler:
  ' ここを通ることができる。
  Debug.Print Err.Number & vbTab & Err.Source & vbTab & Err.Description
End Sub

2014年6月28日土曜日

VBAの関数は、Nullでもエラーにならない

VBAの関数で引数にNullを指定してもエラーとならないのは、深い知識が無くても使えるための言語仕様だと思うけど、想定外の結果を招くことがある。
例えば
If Month(Null) > 3 Then
  Debug.Print "Null > 3"
Else
  Debug.Print "Null <= 3 !?"
End If


<処理結果>
Null <= 3 !?
こういう場合に Else だけでは想定外の結果となる。


ただし、数値と文字の比較ではエラーとなる。
例えば
If 3 < "" Then Debug.Print "3 < ''"

<処理結果>
13 VBAProject 型が一致しません。

VBAの繰り返し処理内で変数を定義しても、2回目以降が初期化されない

VBAの繰り返し処理内で変数を定義しても、初期化は初回だけで2回目以降が初期化されない。
初回だけは初期化されているので誤魔化されてしまうため、余計に注意が必要です。

Dim i As Integer
For i = 1 To 5
  ' 繰り返し処理内で変数定義
  Dim Value1 As String ' 初期化されない!
  Dim Value2 As Integer ' 初期化されない!
  If i Mod 2 = 0 Then
    ' 偶数
    Value1 = CStr(i)
    Value2 = i
  End If

  Debug.Print i & "回目" & vbTab & Value2 & vbTab & Value2
Next


<処理結果>
1回目 0 0 ← 初期化されている
2回目 2 2
3回目 2 2 ← 初期化されていない!
4回目 4 4
5回目 4 4 ← 初期化されていない!

Kernel-Power 41 エラーの PowerButtonTimestamp を日時に変換する

Windows で電源ボタンを長押し(4秒以上)して電源切断した場合のシステムイベントログ "Kernel-Power 41" に含まれる PowerButtonTimestamp の値ですが、Windowsの時刻の起点 (1900年1月1日 0時0分0秒 (UTC)) を基準に算出しても全く合わない。仕様を探してもまるで情報が見つかりませんでした。

これを逆算して起点を算出すると、以下のような結果になる。
  • 時刻の起点は 1601/01/01 09:00:00 (GMT+0900)
  • タイムスタンプの単位は 100ナノ秒

この方法で PowerButtonTimestamp の日時を算出します。

結果:  

2014年6月27日金曜日

Windows パフォーマンスモニターのログ頻度を調整する

パフォーマンスモニターを何時間も継続して走行していると、ログのサイズが大きくなって分析が困難です。サンプルの間隔を調整することで、効率的に運用できます。この間隔については、Microsoft からガイドラインが提示されています。

http://technet.microsoft.com/ja-jp/magazine/2008.08.pulse.aspx
通常の運用時、ベースラインを確立するための適切なサンプリング間隔は 15 分です。問題が発生するまでの平均経過時間が約 4 時間の場合は、サンプリング間隔を 15 秒に設定します。問題が発生するまでの時間が 8 時間以上の場合は、サンプリング間隔を 5 分以上に設定します。

データコレクタには3種類(下記を参照)ありますが、このうち調整を行えるのは「パフォーマンス カウンター」のみです。
調整するには、
  1. パフォーマンスモニターにユーザ定義として作成した「データコレクタセット」を選択する。
  2. 定義に含めたデータコレクタのうち、「パフォーマンス カウンター」であるものを右クリックして、プロパティを選択
  3. プロパティの設定画面で、「パフォーマンス カウンター」タブを開いて、「サンプルの間隔」を変更します。

(参考)データコレクタの一覧
  • パフォーマンス カウンター
  • イベント トレース データ
  • システム構成情報

2014年6月21日土曜日

Windows標準の機能で動作をモニタリングする

なるべくWindows標準の機能でモニタリングするしかないときに有用なツールのまとめ

・リソース モニター

Windowsの[Start] > アクセサリ > システムツール > リソース モニター
http://technet.microsoft.com/ja-jp/windows/ee350564.aspx
CPU、メモリ、ディスク、ネットワーク のモニタリングが簡単にできるが、特徴としてリアルタイムで確認できる反面、ずっと見張っていないと問題を見逃してしまう。

・パフォーマンス モニター

コントロール パネル > システムとセキュリティ > 管理ツール > パフォーマンス モニター
http://technet.microsoft.com/ja-jp/library/cc749115.aspx
http://technet.microsoft.com/ja-jp/library/cc749249.aspx
デフォルトの「データ コレクター セット」でモニタを開始することもできるが、通常は「データ コレクター セット」のユーザ定義を準備する。特徴としては詳細な情報をモニタリングできるが、走行中は結果を確認できない。
かなり詳細なデータ コレクターが揃っているが、各データ コレクターのリファレンスは残念ながら見つけられません。

主なデータ コレクターは以下
  • CPU(Processor)
  • メモリ(Memory)
  • ディスク(物理:PhysicalDisk /論理:LogicalDisk)
  • ネットワーク(ICMP, TCP, IP, UDP, etc.)
  • 他にもたくさん

・Firewall のログ

コントロール パネル > システムとセキュリティ > Windows ファイアウォール > 詳細設定
「セキュリティが強化された Windows ファイアウォール」画面の「監視」メニューから、
ログ設定を開始する。
ネットワークのトラブルを解析する情報が得られます。ログできる情報は以下
  • 破棄されたパケット
  • 正常な接続

・ODBCのトレースログ

http://support.microsoft.com/kb/274551/ja
コントロール パネル > システムとセキュリティ > 管理ツール > データ ソース (ODBC)
「ODBC データソース アドミニストレーター」画面の「トレース」タブから、「トレースの開始」ボタンを押下
ODBC接続のトラブルを解析する情報が得られます。


他には、Microsoft製品ならばインストールしても良いならば、以下の選択肢がある。

・Microsoft Message Analyzer

http://msdn.microsoft.com/en-us/library/jj649776.aspx
インストールが必要
ネットワークパケットをモニターできるが、未だ資料があまり無くて、
次に挙げる1世代古い「Microsoft ネットワーク モニター」のほうが楽だし、翻訳された情報も多い。

・Microsoft ネットワーク モニター

http://support.microsoft.com/kb/933741/ja
インストールが必要
ネットワークパケットをモニターできる。

・Process Monitor

http://technet.microsoft.com/ja-jp/sysinternals/bb896645.aspx
インストールが必要
プロセスのファイル/レジストリ/ネットワーク アクセスをモニタできる。

2014年6月19日木曜日

cronのジョブが二重に起動されてしまう

ある時点から、cronのジョブが複数起動されてしまうようになったとすると、恐らく最初に疑うべきは、crond が複数起動しているのではないか?

例えばこんな状態に

ps -ef | grep crond
[root@localhost work]# ps -ef | grep crond
root 5129 1 0 22:59 ? 00:00:00 crond
root 5132 4899 0 22:59 pts/0 00:00:00 grep crond
root 20368 1 0 00:28 ? 00:00:00 crond

2014年5月3日土曜日

Ruby で uninitialized constant MethodName (NameError)

def Test1
  puts "Hello, World."
end

Test1


これだけで実行すると、uninitialized constant エラーが発生する。
in `<main>': uninitialized constant Test1 (NameError)

これはRubyのコーディング規約である、
・メソッド名はすべて小文字
・クラス名は一文字目を大文字
に違反しているため、Test1 はクラスと誤認されていることでエラーとなっている。
これは慣れないうちにエラーメッセージから推測するのはちょっと辛いと思われる。