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