【プログラミング】

便利ツール – Outlookで検索結果のメールのフォルダを調べる

私はメーラーに、Microsoft Office の Outlook を使用している。

Microsoft Office Outlook

この Outlook で、以下の不満がある。

Outlook上で検索を行い、検索結果として一覧されたメールアイテムが、どのフォルダに格納されているかが分からない。

特定のフォルダ上で検索を行う場合はそれでよいが、「すべてのメールボックス」を対象に検索した場合など、検索結果には表示されるけれど、その前後のメールを読みたいとか、どのプロジェクトのメールなのか、検索結果からはわからない。

ビューの設定で「フォルダ」列を追加すれば、そのメールアイテムの直上のフォルダ名はわかる。しかし、パスは分からない。
たとえば、プロジェクトごとにフォルダを分け、更にそのプロジェクト配下にサブフォルダを作って管理していたりすると、そのサブフォルダ名までしかわからないので、どのプロジェクトのサブフォルダなのかが分からない。

すなわち、以下のような構成にしていると、メールアイテムが「やりとり」「メモ」「アクセス情報」に格納されていることは分かるけれど、どのプロジェクトに格納されているのかは分からない。

+プロジェクト①
  +やりとり
  +メモ
  +アクセス情報
+プロジェクト②
  +やりとり
  +メモ
  +アクセス情報
+プロジェクト③
  +やりとり
  +メモ
  +アクセス情報

検索でヒットしたメールアイテムの前後のメールを読みたくても、関連情報を調べたくても、調べたいフォルダにアクセスできないのである。

そこで、選択したメールアイテムから親フォルダを探索し、パスを求めて表示するマクロを作ってみた。
こちら。

Option Explicit

'
'   選択されているメールアイテムのパスを取得する。
'
Sub FindMailItemTree()

    Dim strAllPath                  As String
    Dim objSelect                   As Outlook.Selection
    Dim lngItem                     As Long
    
    '   パス一覧をクリアする。
    strAllPath = ""
    
    '   選択されているアイテムを取得する。
    Set objSelect = Outlook.Application.ActiveExplorer.Selection
    
    If objSelect.Count <= 0 Then
        Call MsgBox("メールアイテムを選択してください。")
        Exit Sub
    End If
    
    '   選択されているアイテムを走査する。
    For lngItem = 1 To objSelect.Count Step 1
    
        Dim strPath                     As String
        Dim objItem                     As Object
    
        '   パスをクリアする。
        strPath = ""
    
        '   アイテムを取得する。
        Set objItem = objSelect.Item(lngItem)
        
        '   パスを走査する。
        Do While True

            Dim strName As String

            '   アイテムの名前を取得する。
            Select Case UCase(TypeName(objItem))
            
            '   メールアイテム?
            Case UCase("MailItem")
                strName = "【" & objItem.Subject & "】"
            
            '   フォルダ?
            Case UCase("MAPIFolder")
                strName = objItem.Name
            
            '   上記以外
            '   終了する。
            Case Else
                Exit Do
            
            End Select

            '   パスに名前を追加する。
            If strPath <> "" Then
                strPath = "\" & strPath
            End If
            strPath = strName & strPath

            '   親アイテムを取得する。
            Set objItem = objItem.Parent

        Loop
    
        '   パス一覧に追加する。
        strAllPath = strAllPath & strPath & vbCrLf & vbCrLf
    
    Next lngItem
    
    '   パス一覧を表示する。
    Call MsgBox(strAllPath)
  
End Sub

このマクロをOutlookに登録し、メールアイテムを選択して実行すると、そのメールアイテムのパスが、たとえば、以下の様に表示される。

プロジェクト①\やりとり\【メールの表題】

このマクロでは、パスの表示のみを行っているが、フォルダが取得されている状態で以下の様にすれば、そのフォルダを開く事もできると思う。

Call objItem.Display()

このプログラムは自由に使って頂いて構いませんが、自己責任でお願いします。

アフィリエイトリンク整形ツール

今年の1月に、AmazonがアフィリエイトAPIの利用ポリシーを変更した。

Product Advertising API

[重要] Product Advertising API 利用ポリシーの変更について

このブログのアフィリエイトは、ちょこちょこ売れはするものの、波もあり、残念ながら、新しいポリシーでは、APIを利用する権利を確実に維持できるほど、常に実績を上げられるほどではない。
本業が忙しくて、なかなか対処できなかったのだけれど、いい加減、何か対処をしようと重い腰を上げた。

さて、どうするか。

(1).Amazonのアフィリエイトを諦めて、楽天のアフィリエイトなどに切り替えて一本化する。

(2).Amazonのアフィリエイトをなんとかする。

前述の通り、Amazonのアフィリエイトでバンバン売れているわけではないし、実は、送料が有料化されてから、私自身がAmazonをあまり利用しなくなったので、正直、(1)でもいいとは思う。
しかし、せっかくの機会なので、以下の事もやりたい。

(A).楽天アフィリエイトのリンクを、このブログ用に簡単に装飾/変換したい。

Amazonでも楽天でも、提供されているツールを使うと、簡単に商品リンクは作れるのだけれど、基本的には、そのまま貼り付ける事を前提としてて、サイズや装飾などはお任せになる。

Amazon 楽天

APIであれば、商品名、商品画像、商品リンクといった商品の基本情報だけを取得して、好きなようにレイアウトを作成して出力できるのだけれど、ツールの場合はそうはいかない。

そこで、ツールで取得した貼り付け用のHTMLを整形して、このブログ用のHTMLに変換するプログラムを作成する事にする。

ツールの画面はダサダサだけど、こんな感じ。

Amazon 楽天

 

変換ツールを動かしてみる。

Amazon 楽天

ツールで変換したアフィリエイトリンクを貼ってみる。
こちらは、サイズが小さい、ページ下部の「広告」ブロック用。
Aliexpressも加えて比較。

Amazon 楽天 Aliexpress

こちらは、サイズが大きい、本文用。

 

APIで作るリンクであれば、コードを「タグ+コード」を書き込むくらいでアフィリエイトリンクが作れるけれど、ツールから作成したリンクを整形するには、たくさんコピペをしなければならないので、少々めんどくさい。

しかし、Amazonと楽天のアフィリエイトを、あまり区別せずにブログに貼れるようになったので、とりあえず、これでいいことにする。

便利ツール – Excel全シート トップ・スクロール

このエピソードの続き。

便利ツール – ドットファイル作成
便利ツール – ファイル・バックアップ
便利ツール Windows PowerShell版

Excelで資料を作っていて、シート数が何十枚にもなってしまう事がある。
Excelファイルは、保存する時に各シートのスクロール位置を覚えていて、次に開いた時に、スクロール位置が復元されて表示される。
これはこれで便利とは言えるけれど、最終的に作成したExcelファイルを他の人や客先に渡すとき、体裁としては、すべてのシートを一番上にスクロールした状態にしておきたいと思う事がある。
残念な事に、Excelには、「すべてのシートをトップまでスクロールさせる」事を一発の操作で行う方法はないので、シートを一枚づつ開きながら、トップまでスクロールさせていうかないといけないので、とてもメンドクサイ。
更に、Excelファイルを開いた時に、1枚目のシートを開いた状態にしたい。

それを一発操作で行うツールを作ってみた。

ExcelSheetTop.zip

コマンドラインでは、以下の様に実行する。

cscript ExcelSheetTop.vbs file1 file2 file3...

で、コマンドラインで使うのはメンドクサイので、これを「送る」に入れて使う。
こんな感じに。

sendto

便利便利。(^^)

fetch_feed() で小ハマリ

WordPressで構築されたサイトがあって、毎日未明にデータベースを自動定時バックアップしているのだが、ふとバックアップされているファイルを確認してみたところ、異常に大きいサイズになっている事を発見した。

バックアップ・ファイル

最大のファイルは は935MB(約1GB)もある。異常に大きい。バックアップの実行にも、5分近くを要しているようだ。
普通に投稿するだけでは、絶対にこんなサイズにはならないはずだ。
何がこんなに大きいのだろう。

phpMyAdminでテーブルの一覧を確認してみる。

テーブルのサイズ

「wp_options」のサイズが、なんと 8.8GB になっている。
レコード数も、phpMyAdminで正しく表示できない(マイナス表示)ほど大きくなってしまっているようだ。
なんだこれは。

更にテーブルの中を開いてみる。

 書き込まれていたレコード

「_transient_timeout_feed_~」「_transient_feed_~」「_transient_timeout_feed_mod_~」「_transient_feed_mod_~」というレコードがずらりと並んでいる。
なんだこれは。

このサイトにアクセスしたり、リロードする度に、どんどんレコードが増えていく。
私自身は「wp_options」にレコードを書き込んむ処理を組み込んだ覚えはない。
どの処理がレコードを追加しているのだろう。

よくみると、増加していくレコードの名称の中に「feed」とある。
このサイトの WordPress に組み込んだプログラムで、「feed」には見覚えがある。
このサイトでは、他のサイトのRSSフィードを読み込んで自身のページに表示するプログラムがあり、WordPress の fetch_feed() を使用している。
おそらくこれだろうとアタリを付けて、この処理をコメントアウトした上で、リロードしてみる。
レコードは増加しない。これだ!

しかし、fetch_feed() の引数はURLのみで、動作を制御するオプションはなく、マニュアルを読んでも「wp_options」への書き込みを抑止するような設定などは見当たらない。
どうすればよいのだ。

「_transient_timeout_」「削除」といったキーワードでググると、このようなデータを削除するプラグインが存在しているようだ。

Delete Expired Transients
Clean Options
※ただし、おそらく削除できる機能があると思われるけれど、私自身はこれらを試していません。

これらのプラグインを導入する事も考えtが、実は対象となるサイトは多数あるし、今後の WordPress のアップデートの事を考えると、できるだけプラグインは導入したくない。

どうしようか迷ったのだが、これらのレコードは、ひたすら追加されるのみで、古いレコードが使われる事はないようなので、古いレコードを自動削除するプログラムを作成し、定期的に実行する事にした。

しかし、ここでまた問題が発生した。
「wp_options」には、レコードの生成日時を記録するフィールドが無い。
どうするか。
う~ん、と悩んで、「wp_options.option_id」を使用する事にした。
まず「wp_options.option_id」の最大値を取得した上で、最新の数百レコードを残して、それより小さい値のレコードを削除する。
つまり、こうだ。

DELETE
FROM
wp_options
WHERE
(option_id < ((SELECT MAX(options_id) FROM wp_options) - 200))
AND
(
(option_name LIKE '_transient_timeout_feed_%')
OR
(option_name LIKE '_transient_feed_%')
OR
(option_name LIKE '_transient_timeout_feed_mod_%')
OR
(option_name LIKE '_transient_feed_mod_%')

)

「_mod_」の条件は、冗長な意味のない記述だけど、とりあえず、気にしない。

このSQLを、WordPressデータベースのプリフィックスごと、データベースごとに処理する様にし、cronで定期的に実行するようにする。

準備したプログラムを実行した上でバックアップを実行、バックアップされたファイルを確認する。

バックアップ・ファイル

よし、普通のサイズになった。
バックアップに要する時間も数秒~十数秒程度になった。
これでヨシ。

array_unique() で小ハマリ

PHPの関数 array_unique() で小ハマリしたのでメモ。

array_unique() は、配列から重複する要素を削除する関数。

たとえば、こんなプログラム。

$list = array('A', 'B', 'A', 'C');
$list = array_unique($list);
for ($cnt = 0; $cnt < count($list); $cnt++)
{
print($list[$cnt] . "\n");
}

このプログラムは、「A」「B」「A」「C」という配列から、array_unique() によって、重複する「A」の余分な要素を取り除き、「A」「B」「C」という配列にするプログラムである。

このプログラムにおける、期待する実行結果は、こうである。

A
B
C

ところが、実際にプログラムを実行すると、この様に出力される。

A
B

最後の要素「C」が出力されない。
なぜだ。

実行結果だけを見ると、count($list) の値が「2」であるように見える。
確認してみよう。

$list = array('A', 'B', 'A', 'C');
$list = array_unique($list);
print(sprintf("===%d===\n", count($list))); // ★追加★
for ($cnt = 0; $cnt < count($list); $cnt++)
{
print($list[$cnt] . "\n");
}

実行してみると…

===3===
A
B

count($list) の値は「3」なのに、最後の要素が出力されない。
なぜだ。

var_dump() で配列の内容を一覧してみよう。
$list を初期化した後と、array_unique() を通した後に var_dump() を入れてみる。

$list = array('A', 'B', 'A', 'C');
var_dump($list); // ★追加★
$list = array_unique($list);
var_dump($list); // ★追加★
for ($cnt = 0; $cnt < count($list); $cnt++)
{
print($list[$cnt] . "\n");
}

実行してみると…

array(4) {
[0]=>
string(1) "A"
[1]=>
string(1) "B"
[2]=>
string(1) "A"
[3]=>
string(1) "C"

}
array(3) {
[0]=>
string(1) "A"
[1]=>
string(1) "B"
[3]=>
string(1) "C"

}
A
B

なるほど、array_unique() を通した後は、重複している要素番号[2]が抜けて、 [0][1][3] と飛んだ状態で格納されている。
要素数(つまり count($list))は「3」なので、for は [0][1][2] でループするのだけれど、最後の要素の添え字は [3] だから出力されないのか。
おそらく、[2] は null なのだな。

確認してみよう。

$list = array('A', 'B', 'A', 'C');
$list = array_unique($list);
for ($cnt = 0; $cnt < count($list); $cnt++)
{
if ($list[$cnt] == null) // ★追加★
{
print("NULL!\n");
}
print($list[$cnt] . "\n");

}

実行してみる。

A
B
NULL!

では、array_unique() を通した後の配列を正しくループさせるにはどうしたらいいのだろう。
sort() を通して配列を並べ替えたらどうだろうか。

$list = array('A', 'B', 'A', 'C');
$list = array_unique($list);
sort($list); // ★追加★
for ($cnt = 0; $cnt < count($list); $cnt++)
{
print($list[$cnt] . "\n");
}

できた!

A
B
C

しかし、これでは配列がソートされてしまう。ソートしたくない場合はどうしたらいいのだろうか。
array_系の関数をざっと見てみる... これか、array_merge() を使用して、1個の配列だけをマージしてみたらどうか。
やってみよう。

$list = array('A', 'B', 'A', 'C');
$list = array_unique($list);
$list = array_merge($list); // ★追加★
for ($cnt = 0; $cnt < count($list); $cnt++)
{
print($list[$cnt] . "\n");
}

できた!

A
B
C

もっといいやり方があるような気がするのだが、とりあえず、これでヨシとしよう。
なかなか奥が深い。

読書メーターのブログ・パーツ V2

このエピソードの続き。

読書メーターのブログ・パーツ

読書メーターのサイトがリニューアルされた。

読書メーター
読書メーター

このブログでは、ページの右下に読書メーターに登録した読んだ本の情報を表示しているのだが、読書メーターが提供しているブログ・パーツは不具合があって、更新内容が正しく反映されないので、自作のブログ・パーツで情報を表示している。
リニューアル後用のブログ・パーツがあればと思うのだが…リニューアル後のサイトでは、ブログ・パーツのダウンロード・ページなどが見当たらない。

そういう事で、自作のブログ・パーツをリニュアル後用に改修する事にした。
自作のブログ・パーツは、非ログインでも参照できるユーザのマイ・ページにアクセスしてHTMLをダウンロードし、「読んだ本」「読んだページ」「感想・レビュー」をHTMLから抽出している。

bookmeter-1

リニューアル前後で基本的な構造は同じようなので、アクセスするURLと、抽出するための正規表現を書き換えた。
ついでに、出力する値を3桁のカンマ区切りに変更。

ブログ・パーツ

とりあえず、これでOK。

Aliexpress アフィリエイト WordPress プラグイン

最近、Aliexpress から商品を購入する機会が多くなってきた。

Aliexpress
Aliexpress

中国サイト、中国ショップ、中国製品は、正直言って品質がよくないので苦労する事も少なくないし、ショップの対応の質は低いし、購入してから商品が到着するまで1ヶ月くらいかかるなど、いろいろ難点はあるのだけれど、やはり、圧倒的な安さは大きな魅力だし、安価な商品でも送料無料(海外からの発送なのに!)が多いのも魅力を感じる。

さて、最近、このブログに設置されている Google AdSense の広告ボックスに、Aliexpress の広告が表示されている事に気が付いた。

Aliexpress広告

もしかして、Aliexpress ってアフィリエイトやっているのか?
検索してみると…あった!
Aliexpress のアフィリエイトってあるんだ。知らなかった。

AliExpress Affiliate
AliExpress Affiliate

さっそく登録してみた。

管理画面にアクセスして、いろいろ調べてみる。
まだ調べが足りないのかもしれないけど、Amazonや楽天のアフィリエイトに比べると、あまり使い勝手がよくない。
基本的にブログで商品を紹介する場合、ピンポイントでその商品を紹介する場合がほとんどなので、「リンクを作成」という操作をしたら、画像、商品名、リンクなどがパッケージされたHTMLのセットが生成されてほしいのだけれど、そのようなリンクは作成できず、ピンポイントの商品は、たとえば、以下のようなURLしか作成できない。

http://s.click.aliexpress.com/e/e27AUFeA2

画像などは自分でダウンロードするなどして準備してサーバにアップし、商品名などもコピペして持ってくるなどしないといけない。
ひじょうにメンドクサイ。

と、いう事で、できるだけ簡単にアフィリエイトのリンクが作成できるように、WordPress 用のプラグインを作ってみた。

Aliexpress アフィリエイト WordPress プラグイン

Aliexpress の商品の詳細ページには、<meta>データに Open Graph Protocol のデータが埋め込まれているので、このデータの「og:title」から商品名を、「og:image」から商品の画像を抽出して利用する事にする。
また、取得した商品名には、末尾に「カテゴリ」とか、「Aliexpress.com」とか、「Alibaba Group」とか、商品名そのものとは直接関係ない文字列が入っているので、これらは削除して使用する事にする。

<meta property="og:title" content="Original brand wansenda On sale 1PC Micro sd card 8GB 16GB 32GB 64GB class10 Real capacity Memory Card TF card free card adapter-in Memory Card from Consumer Electronics on Aliexpress.com | Alibaba Group" />
<meta property="og:image" content="http://g01.a.alicdn.com/kf/HTB1RvP0LXXXXXctXVXXq6xXFXXXa/Original-brand-wansenda-On-sale-1PC-Micro-sd-card-8GB-16GB-32GB-64GB-class10-Real-capacity.jpg"/>

本文中に、たとえば、以下のようなタグを本文中に記述する。
タグの中は「:」で区切って商品コードとアフィリエイト・コードを記述する。
商品コードは、商品の詳細ページのURL中にあるコード、アフィリエイト・コードは、アフィリエイトの管理画面で「Ad Center」→「Deep Link URLs」を操作して得られるURL中にあるコードだ。

[aliexpress]32709218468:e27AUFeA2[/aliexpress][aliexpress]32260198252:uV3nIYnA6[/aliexpress]
[aliexpress]32654385390:jM7iYfiQv[/aliexpress][aliexpress]32682370439:3FaMfIAqr[/aliexpress]

以下のようなアフィリエイト用のHTMLに置き換えられる。

また、前述の通り、商品名と画像は Aliexpress の商品の詳細ページから取得するが、アクセスの度に Aliexpress にアクセスするわけにはいかないので、商品名と画像はキャッシュするようにした。
キャッシュしたデータは一定時間(とりあえず24時間)経過後に自動削除し、再取得するようにしてある。

とりあえず VER 1.0。
運用しながら改良していこう。

■2016/08/05 追記

このプラグインを作成している途中で、Aliexpress のアフィリエイトのリンクのブロックを受けてしまった(正しくは、ブロックを受けたと「思われる」なのだけど)。
アフィリエイトのURLにアクセスすると、商品の詳細ページが表示されず、ログインを要求されるようになった。ログインすれば詳細ページに遷移する。
異なるIPアドレスからアクセスすれば、ブロックされずに普通に商品の詳細ページが表示されるので、私の環境のみがブロックされた状態になってしまったようだ。

もちろん、開発中には Aliexpress へのアクセスが頻繁に発生したとは思うけれど、アクセスした回数は、多く見積もっても100回程度じゃないかな。Aliexpress 側のアクセス制限は小さい値になっているようなので、注意が必要だ。

正確には測っていないけれど、6時間程度でブロックは解除され、正常に戻った。
一旦ブロックされてしまうと、6時間程度はメンテナンスが困難な状態になってしまうので、Aliexpress のアフィリエイトの実装をする場合は、誤って多数のアクセスが飛ばない様に注意する必要だ。

Raspberry pi シャットダウンUSBキー V2

このエピソードの続き。

Raspberry pi シャットダウンUSBキー

Raspberry pi をお手軽にシャットダウンできるUSBキー V2 を作ってみた。

Raspberry pi シャットダウンUSBキー V2

前版では、SDカードにファイルを書き込み、特定のファイルが存在しているか否かでシャットダウンを実行するように作成した。
しかし、SDカードを準備して、0バイトのファイルを1個だけ置くという、ひじょうに無駄の多い構成で、たとえば、SDカードの故障によってファイルが読み出せず、シャットダウンに移行できない状況も発生し得る。
もう少しシンプルな構成にできないだろうか。

前版では、「情報=ファイル」という頭があって、SDカードに情報を書き込んだのだけれど、SDカードに拘らず、USBデバイスの固有情報を利用して、特定のUSBデバイスが挿されたらシャットダウンをかける様にする事はできないだろうか。

SDカードを挿していないカードリーダーを Raspberry pi に挿し、接続されているUSBデバイスを一覧してみる。

$ lsusb | sort
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 004: ID 1267:0201 Logic3 / SpectraVideo plc A4Tech SWOP-3 Mouse
Bus 001 Device 005: ID 0566:3002 Monterey International Corp.
Bus 001 Device 012: ID 05e3:0751 Genesys Logic, Inc.
$

上記の例では、最後の「Bus 001 Device 012」がカードリーダーだ。
カードリーダーのみでもUSBデバイスとして認識された。

カードリーダーの詳細を表示してみる。

$ lsusb -v -s 12
Bus 001 Device 012: ID 05e3:0751 Genesys Logic, Inc.
Couldn't open device, some information will be missing
Device Descriptor:
bLength                18
bDescriptorType         1
bcdUSB               2.00
bDeviceClass            0 (Defined at Interface level)
bDeviceSubClass         0
bDeviceProtocol         0
bMaxPacketSize0        64
idVendor           0x05e3 Genesys Logic, Inc.
idProduct          0x0751
bcdDevice           14.03
iManufacturer           3
iProduct                4
iSerial                 0

(省略)

$

取得したUSBデバイスの情報から、「idVendor」「idProduct」あたりが固有情報(に近い情報)として使えそうだ。

これらの情報を抽出し、特定の値であればシャットダウンを実行するようにする。

$ lsusb -v -s 12 2> /dev/null | egrep "id(Vendor|Product)"
idVendor           0x05e3 Genesys Logic, Inc.
idProduct          0x0751
$

これならば、SDカードを準備する必要はない。
USBカードリーダー単体であれば、Amazonでは200円程度から、中国通販サイトなら20円程度からある。
また、中国通販サイトでSDカードを購入すると、多くの場合、カードリーダーもセットで送られてくるので、ずんべ の手元にも余ったものがけっこうあり、それらを活用できる。
必要な機材点数が減り、コストも削減できた。同時に故障率も減るものと考えていいだろう。

USBカードリーダ

更に言うと、シャットダウン・キーとするUSBデバイスは、別にカードリーダーである必要もない。マウスでも、キーボードでも、USBデバイスなら何でもいい。

もちろん、シャットダウン用の「idVendor」「idProduct」と同一のUSBデバイスを挿してしまうとシャットダウンが始まってしまうわけだけれど、別に一般ピープルが使用するわけではなく、限定された環境で使うものなので、気にしない事にする。

Raspberry pi シャットダウンUSBキー

Raspberry pi をお手軽にシャットダウンできるUSBキーを作ってみた。

Raspberry pi + シャットダウンUSBキー

Raspberry pi をシャットダウンするには、基本的には、ログインしてコマンドを入力する必要がある。
シャットダウン用のボタンを付けてシャットダウンをかける方法は、WEBで様々書かれているけれど、ずんべ は電子回路には明るくないし、すぐに実装したかったので、手元にあるSDカード/USBメモリを使って、お手軽にシャットダウンする方法を考えてみた。

まぁ、「考えてみた」と言っても、たいした実装ではなく、以下のように動作させ、シャットダウンをかけるだけである。

(1).Raspberry pi にUSBメモリを挿す。
(2).USBメモリが自動的にマウントされる。
(3).マウントされたUSBメモリ内に「/shutdown.txt」が存在するか確認する。
(4).「/shutdown.txt」が存在していれば、シャットダウンする。

お手軽だ。(^^)

USBメモリは、こんな感じ。

シャットダウンUSBキー

USBメモリの中には、0バイトのファイルが1個だけ置いてある。

$ find . -type f -exec ls -l {} \;
-rwxr-xr-x 1 root root 0 Jul 14 03:33 ./shutdown.txt
$

USBメモリが挿されたか否かは、おそらく何らかの方法でリアルタイムに検出できるのだろうけれど、お手軽に cron による起動で、5分間隔くらいで実行する事にする。

$ EDITOR=cat crontab -e 2> /dev/null | grep shutdown.pl
5,10,15,20,25,30,35,40,45,50,55 * * * * /home/pi/foo/bar/shutdown.pl
$

USBメモリが挿されているか否かの検査は、こんな感じで。

$ mount| grep /media/ | grep vfat
/dev/sda1 on /media/pi/E405-3402 type vfat ( ・・・ )
$

この出力から、マウントされているディレクトリ(上記の場合「/media/pi/E405-3402」)を求める。
そして、その直下に「shutdown.txt」が存在するか検査し、存在していれば、シャットダウンを実行する。

$ ls -l /media/pi/E405-3402/shutdown.txt
-rwxr-xr-x 1 root root 0 Jul 14 03:33 /media/pi/E405-3402/shutdown.txt
$

今回作成したものは「シャットダウン・キー」なのだけれど、USBメモリ内に置くファイルを「reboot.txt」や「apatch_restart.txt」などとして、「リブート・キー」や「Apache再起動キー」など、キーボードなしで Raspberry pi に動作指示を与えるキー・セットを作る事もできると思う。


今回は手元にある余り機材を集めて準備したので、こんな構成になった。
SDカードのサイズはミニ(マイクロではない、骨董品だ)。(^^;
容量は16MB(16GBではない、骨董品だ)。(^^;

シャットダウンUSBキーの構成

SDカードに書き込むファイルは「shutdown.txt」が1個だけで、サイズは0バイトなので、16MBでも容量はあり余っているのだが、今時16MB程度の容量など、写真を数枚入れたらいっぱいになってしまうので使い道がなかったのだが、やっと有効活用できた。

今回は余り機材で構成したので、SDカード+USBアダプタになったけれど、スマートな構成にしようとすれば、スティック型のUSBメモリになるのかな。そうなると、サイズ0バイトのファイルを1個を置くためだけに4GBや8GBは無駄だし、コストも無視できない。
やはり、少し回路系の勉強をして、スイッチを付けるべきかな。
少し勉強してみよう。

ActiveXで大ハマリ

ActiveXのインストールでハマったのでメモ。

お客様に納品しているソフトウェアは、開発言語が Microsoft Visual Basic 6.0(以下、VB6)で、AxtiveXで提供されているものがある。
ActiveXでソフトウェアを提供する事によって、Microsoft Internet Explorer(以下、IE)上で Windows ネイティブなプログラムを実行する事ができるため、WEB経由で動作するシステム上で、IE 上でシームレスにプログラムを動かせるので、便利に使用してソフトウェアを開発していた。
また、ActiveXが持つ機能を使用して VB6 のランタイムライブラリも自動的に必要なDLLのみがインストールされるのも便利なところだ。

しかしながら、昨今はセキュリティがひじょうに厳しくなった事もあって、ブラウザ上でネイティブなコードを実行できる ActiveX は避けられる方向にある。
代表例は Flash で、過去は、サイトのトップページで多用されていたが、現在は、スマートフォン端末やタブレット端末で使用できない事もあり、昨今では、ほとんど使われなくなった。

また、VB6 は、開発環境としては古いものであり、現行でマイクロソフトのサポートサイクル内にあるOSでは、Windows Vista までしか開発環境のインストールはサポートされていないものである。

開発ツール対応 OS 一覧

とは言え、お客様に納品しているソフトウェアは、そう簡単に新しいものには変えられないので、使える限りは、使い続ける事が原則だ。マイクロソフトの都合や、開発サイドの都合で、問題なく動いているものを、わざわざお金をかけて作り直しなどさせてはくれない。

そういうわけで、過去に VB6 + ActiveX で納品したソフトウェアの改修を行う事になったのだが、問題が発生した。

まず、ユーザ様の環境に合わせたテスト環境し、過去に納品した ActiveX のインストールを試してみる。
最新の IE である IE11 に合わせて若干の調整が必要であったが、インストールは問題なくでき、ActiveX は問題なく動作した。
ここまでは順調だ。

後日、プログラムを改修した ActiveX のパッケージで、インストールを試したところ、今度は問題が発生した。
ActiveX をインストールする旨のダイアログは表示されるが、インストールに失敗してしまう。エラーも何も出ない。

インストールはスタートする

インストールはスタートするのに、何も言わずに終了し、インストールに失敗してしまう。
なぜだ?
新しく作成した ActiveX のパッケージがおかしいのか?

試しに、先にインストールが成功した、過去に納品した ActiveX を一旦削除して、もう一度インストールをしてみると、こちらもインストールできなかった。
いったい何が起こっているのだ?

IE の「インターネット オプション」の設定を変更し、セキュリティを緩々にしてみる。
インストールできない。

テスト環境は ESXi 上に作成しているので、インストール操作を行う前にバックアップした環境を書き戻してみる(インストールに成功した環境に戻してみる)。
インストールできない。

んんんん??

ActiveX のインストールが行われる際に、IE は Temporary Internet Files にログを残すので、これを参照してみる。

IEのログ

すると…

*** Code Download Log entry (20 Nov 2015 @ 23:35:09) ***
Code Download Error: (hr = 800c0007) 要求されたリソースのデータは使用できません。Operation failed. Detailed Information:
CodeBase: http://***********************
CLSID: {********-****-****-****-************}
Extension:
Type:LOG: Item **********.ocx being processed.
--- Detailed Error Log Follows ---
LOG: Download OnStopBinding called (hrStatus = 0 / hrResponseHdr = 0).
LOG: Item **********.ocx being processed.
LOG: Item MSVCRT.DLL being processed.
LOG: Item MSINET.OCX being processed.
LOG: Item INETJP.DLL being processed.
LOG: Item COMDLG32.OCX being processed.
LOG: Item CMDLGJP.DLL being processed.
LOG: Item MSCOMCTL.OCX being processed.
LOG: Item MSCMCJP.DLL being processed.
LOG: Item msvbvm60.dll being processed.
LOG: Item OLEAUT32.DLL being processed.
LOG: Item OLEPRO32.DLL being processed.
LOG: Item ASYCFILT.DLL being processed.
LOG: Item STDOLE2.TLB being processed.
LOG: Item COMCAT.DLL being processed.
LOG: Item VB6JP.DLL being processed.
LOG: URL Download Complete: hrStatus:0, hrOSB:0, hrResponseHdr:0, URL:(http://***********************.CAB)
LOG: Download OnStopBinding called (hrStatus = 800c0007 / hrResponseHdr = 8007007e).
LOG: Redundant download attempted, but no more codebases available.
LOG: URL Download Complete: hrStatus:800c0007, hrOSB:0, hrResponseHdr:8007007e, URL:(http://activex.microsoft.com/controls/vb6/VB6JP.cab)
LOG: Reporting Code Download Completion: (hr:800c0007 (FAILED), CLASSID: d80b51ca..., szCODE:(http://***********************.CAB), MainType:(null), MainExt:(null))

2行目でエラー「800c0007」が記録されている。
その原因は、下から2行目の、リターンコード「8007007e」のようだ。
エラーとして記録されているURLは、これだ。

http://activex.microsoft.com/controls/vb6/VB6JP.cab

えぇ~?
activex.microsoft.com 上のパッケージにアクセスできない?

wget コマンドでダウンロードを試みてみる。

$ wget http://activex.microsoft.com/controls/vb6/VB6JP.cab
--2015-11-20 23:49:35--  http://activex.microsoft.com/controls/vb6/VB6JP.cab
activex.microsoft.com をDNSに問いあわせています... 61.213.151.18, 61.213.151.10
activex.microsoft.com|61.213.151.18|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 404 Not Found
2015-11-20 23:49:36 エラー 404: Not Found。
$

404 Not Found…。orz

nslookup で activex.microsoft.com を引いてみる。

$ nslookup activex.microsoft.com
Server:         XXX.XXX.XXX.XXX
Address:        XXX.XXX.XXX.XXX#53Non-authoritative answer:
activex.microsoft.com   canonical name = activex.microsoft.com.edgesuite.net.
activex.microsoft.com.edgesuite.net     canonical name = a1670.g2.akamai.net.
Name:   a1670.g2.akamai.net
Address: 61.213.151.10
Name:   a1670.g2.akamai.net
Address: 61.213.151.18
$

DNSは引けるが、何だよ、この akamai.net って。

2日待ってみたが、一向に復旧しない。
サーバ activex.microsoft.com の障害って、どこに連絡をすればよいのだ?
マイクロソフトのMSDNのサポートに電話をしてみる。
が…まったく要領を得ない。
サポート窓口の人は、技術的な事はまったくわからないようで、「ActiveX」も、「activex.microsoft.com」も、拡張子「.CAB」も、「DNS」も、「nslookup」も通じない。
文字通り、話にならない。まったく埒があかない。
結局、最後はこうだ。

このサーバは、時々アクセスできなくなる事があるようです。
しばらくお待ちいただければ復旧すると思います。
このサーバに関する連絡窓口はございません。

いやいや、「時々、アクセスできなくなる事がある」って、おかしいやろ。
「このサーバに関する連絡窓口はございません」って、おかしいやろ。
activex.microsoft.com が動いていないという事は、世界中で ActiveX のインストールができない状態になってるって事だぞ。そんな重要なサーバが、何日も落ちっぱなしっておかしいやろ。

ちなみに、実は7年前にも似たような事があった。

【プログラミング】ActiveXで大ハマリ

この時は、完全にアクセスできないのではなく、不安定な状態で、ダウンロードできたり、できなかったり、という状況で、パッケージをローカル・サーバに置くことで回避できた。
しかし今回は、まったくアクセスできないので、パッケージを手元に置こうにも、そのパッケージのダウンロードもできない。

どうにもならない。
マイクロソフト君、しっかりしてくれ。

■2015/11/26 追記

再度マイクロソフトに問い合わせてみたけれど、やっぱり「わからない」という回答。
なんだかなぁ。

仕方がないので、activex.microsoft.com からのダウンロードは諦め、開発PC上の DLL や OCX が正であると考えて、パッケージを作成するときに、DLL や OCX をすべてパッケージに含めるように設定を変更してパッケージを作成。
当然の如く、パッケージは肥大化し、約200KBだったCABが、10倍の2MBに。

$ ls -l
-rw-r--r--. 1 zunbe zunbe  206058  3月 13 21:56 2013 OLD-ActiveX.CAB
-rw-r--r--. 1 zunbe zunbe 2007507 11月 26 05:20 2015 NEW-ActiveX.CAB

これで一応、新しい ActiveX をインストールする事はできるようになった。
ファイルのサイズは大きくなってしまったが、これはヨシとする。
とりあえずは、これでいい。

しかし、まだ問題が。
プログラムの検証をするために、旧版の ActiveX をインストールしなければならない。
ずんべ 自身がインストールするだけなら、ランタイム・ライブラリをインストールした上で、旧版の ActiveX を regsvr32 で登録すれば何とかなるが、お客様もでも同様に新旧両方の ActiveX をインストールして検証をする必要があるので、お客様には「regsvr32で」というわけにはいかない。

仕方がないので、作成した新版のパッケージを元に、旧版のパッケージを手動で作成する事にする。
まず、作成した新版の CAB を一旦解凍する。
新版の ActiveX を、旧版の ActiveX に差し替える。
INF ファイルも手動で修正する。
再び CAB に固める。
作成した旧版のパッケージで、インストールをトライしてみると、無事成功。

やれやれだぜ。(空条承太郎風)

広告

まとめページ

取得した資格
登った山

広告

サイト内検索

WordPress

最近のコメント

広告

RSS

RSS 記事
RSS コメント
Server offered by
有限会社パテンティックソフトウェア
Profile for zunbe