カテゴリー: Development

ExcelVBA覚書 ファイル起動

ExcelVBAでファイルを起動する方法。
Excelのブックとかでなく、JpegとかTextファイルとかを起動したい場合の記載方法。
(これでやれば、ファイルなら既定のアプリでファイルを開いてくれる)

CreateObject("Wscript.Shell").Run

参考URL
1) 別のやり方も載ってる
moug ExcelVBA 他アプリを起動する

2) 引数について詳しいのはこっち
VBScript Tips & サンプル プログラムを実行する、ファイルやフォルダを開く

Excel VBA覚書 ファイルの所有者を取得する

このブログのアクセスって明らかに平日が多い。
記載した技術情報が役に立ってくれれば幸いなのだが・・・

さて、ファイルの所有者ってとってこれんのか?って話。
とってこれるみたい。

Kameya blog「VBScriptでフォルダ・ファイルの所有者の確認を行う」を参考に、ExcelVBAで作成してみた。

'-----------------------------------------------------
' ファイルの所有者を取得する
' 引数 :ファイルパス(File Only)
'-----------------------------------------------------
Private Function GetOwner(fpath As String) As String
    
    Dim WMIService  As Object
    Dim objSet      As Object
    Dim obj         As Object
    Dim buf         As String
    
    'ちゃんと渡してよ!って抜ける
    If Len(fpath) = 0 Then Exit Function
    
    Set WMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
    
    buf = ""
    
    'TODO 本当はちゃんとエスケープしないとダメ
    Set objSet = WMIService.ExecQuery("ASSOCIATORS OF {Win32_LogicalFileSecuritySetting='" _
                                    & Replace(fpath, "\", "\\") _
                                    & "'} WHERE AssocClass=Win32_LogicalFileOwner ResultRole=Owner")
    For Each obj In objSet
        buf = buf & obj.AccountName & ","
    Next
    If Len(buf) > 0 Then
        GetOwner = Left(buf, Len(buf) - 1)
    End If
    
    Set obj = Nothing
    Set objSet = Nothing
    Set WMIService = Nothing

End Function

参照したサイトだと記載されていなかったのだが、¥マークは二重にしないといけない。
(常識?いや、CとかC++とかC#とか・・・ならともかく、VB書いてるときは頭がBasicモードになってしまい、忘れてしまうのだ。)
フォルダの所有者を確認するときは、WHERE句が変わるはず。

Struts2 ForとかDoとかWhileとか・・・

まぁとりあえず、ぐるぐる回したいときね。

あるんですよ。

鍵のついたキーホルダーの端っこを指にはめ込んで、鍵をぐるぐる意味もなく回したりね。

いや、そんな話でなくて・・・iterator

ソフトウェア技術ドキュメントを勝手に翻訳 iterator

ex) 1,2,3,4,5と順に・・・

<s:iterator status=”stat” value=”{1,2,3,4,5}” >

これなんかは、{1,3,6,10} とか括弧の中身を変えてしまうと、また違った使い方ができる。

ex) とりあえず5回

<s:iterator status=”stat” value=”(5).{ #this }” >

(5)ってところを変数つかって(data_max)とかできちゃうよね。

って、forつくりゃいいじゃんか!って思うんだけどね。
まぁ、人それぞれ、言語もそれぞれってことです。

SQLServer覚書 Excel→DB

Excelのデータを1行ずつ挿入していくのが面倒なので、シートまるっとINSERTをやってみた。

引数は以下の通り
workbook_path:元データのブック
sheet_name:workbook_pathのどのシートを元データとするのか指定
db_src:DBの場所
db_name:DBの名前
login_id:DBのユーザID
login_pw:DBのユーザパスワード
table_name:DBのどのテーブルを操作するのか(ここではデータの投入先)

Private Function ExportFromXLSX(workbook_path as String,sheet_name as String, db_src as String, db_name as String,login_id as String, login_pw as String, table_name as String) As Boolean

	Dim cn as ADODB.Connection
	Dim recs_aff as Long

	On Error Goto ErrFunc
	
	Set cn = New ADODB.Connection

	'2007バージョンのExcelファイルを開く
	cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & workbook_path & ";" & _
	        "Extended Properties=Excel 12.0"

	'Jetプロバイダを使用してインポート
	sql = "INSERT INTO [odbc;Driver={SQL Server};" & _
						"Server=" & db_src & ";" _ 
						& "Database=" & db_name & ";"  _
						& "UID=" & login_id  _
						& "PWD=" & login_pw _
						& "]." & table_name _
						& " SELECT * FROM [" & sheet_name & "$]"
	    
	cn.Execute sql, recs_aff, adExecuteNoRecords
	cn.Close
	Set cn = Nothing

	ExportFromXLSX = True
	Exit Function
	
ErrFunc:
	
	'エラーメッセージ表示
	' ---- (省略) ----
	
	If Not cn Is Nothing Then
		If cn.State=1 Then cn.Close
		Set cn = Nothing
	End If
	
End Function

こんなことやらなきゃいけないのは、SQLServer側からExcelシートの内容をとってこようとしたときに、分散クエリほにゃほにゃ・・・というエラーが出てしまったからなのだ。
なのでExcelシートから、SQLServer側に送るイメージ。

Excel覚書 結合セルとの格闘

今日は昨日やったことの続きをやろうと思っていたら、昨日作ったロジックがどうも思い通りに動いていない。

検索絡みだ。

Find関数を使って、セル範囲内の値検索をしたかったのだが、同値のセルを検索してくれないとキタ。
Find(…) → FindeNext() を最初のセル、または戻り値がNothingになるまで繰り返すのだが、どうもうまくいかない。

で、結合セルが含まれているせいで、検索がうまくいかないのかと、結合セルをすべて解除させたシートを一つ作って、Ctrl+Fで検索してみたのだが、やはりうまくいかなかった。

Ctrl+Fの検索ダイアログでうまくいかないものをマクロでうまくいかそうなどというのは、無駄な抵抗なので、もう行列全体を1つ1つ当たっていくことにした。

With Worksheets(xx)
    For c=1 to 100 Step 1
      For r=1 to 100 Step 1
        If Cstr(.Cells(r,c).Value) = CheckText Then
                  ''' ここに処理 '''
            End If
            ''' ここに下の結合セルのMax行数抽出のサンプル挿入 '''
      Next r
    Next c
End With

こんな感じに1セルずつ当たっていくわけ。なんとも、面倒な話だが、仕方がない。

しかし、対象セル範囲の中に結合セルがあるので、厄介だ。
というより、結合されている場合は、先頭のセルだけ確認すればよいわけで、検索時間を短縮できると考えた。

下の「結合セルのMax行数抽出のサンプル」は縦結合しかされていないので、横結合の場合はまた変わってくるわけだが・・・

    Dim rng As Range   '確認対象セルを設定すること
    If Not rng.MergeCells Then
        '結合されていない場合
        min_row = rng.Row
        max_row = rng.Row
    Else
        '結合されている場合
        min_row = rng.MergeArea.Cells(1, 1).Row
        max_row = SearchMergeRowMin + rng.MergeArea.Rows.Count - 1
    End If

次に確認するセルは、同じ列の max_row+1 となる。

Excelオセェ~と思ってしまう事象について

現在仕事でExcel2010を使っているのだが、会社のサーバにあるドキュメントなんぞを開けようものなら、

ダウンロード中・・・

みたいなメッセージが出て、もう異様に遅い。

ってなことで、ググってみた。
そうすると、信頼できる場所にしたら、マシになるかもね!なんて話が。

Microsoft Officeサポート:信頼できる場所を追加、削除、変更する

さっそくやってみたさ。

って、ネットワーク上のフォルダが指定できひんやないかい!!!

ほな、あとは、

[レジストリ エディター] で、次のレジストリを見つけます。
※ レジストリが見つからない場合、手順 “4.” 以降は実施する必要はありません。
HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\11.0\Excel\Security\FileValidation
“EnableOnLoad” をダブル クリックし、値のデータに 1 を設定し [OK] をクリックします。
[ファイル]-[レジストリ エディタの終了] をクリックします。

出典:http://support.microsoft.com/kb/2575312/ja

のあたりを見ていくしかないですなぁ・・・

Win覚書 Explorer起動時の参照先フォルダ

WindowsXPからWindows7に変えていろいろムカつくことがある。

その1つは、Explorerを立ち上げた時に、お気に入りだのマイドキュメントだのが、どで~んと拡がること。

あのね、そんなところにファイル置きませんから。

まったく、余計なことしやがって・・・

ということで、起動時に拡げたいフォルダをDドライブのほうにしてやる。

Explorerのプロパティを開いて、explorer.exe の後ろにほにゃほにゃと追加
%windir%\explorer.exe /e, /n, D:\
適用させて、Explorerを立ち上げれば、Dドライブが見えて、他は縮んだままになってる。

おせっかいはほどほどにしてほしいものだ。

Excel覚書 改行コードを検索

検索の入力欄に、Ctrl+Jで設定(見た目わからないけど、改行コードが入っている)
 ↓
検索ボタンクリック
 ↓
改行コードのあるセルが選択される

見た目わからないのが困るんです。

PostgreSQL覚書 UPDATE(副クエリー)

副クエリーを使ったUPDATEの方法。

扱っているブログは結構あるものの、複数項目をUPDATEする方法について書いてあるものが少ない気がする。


UPDATE table1 t1
 SET (data1,data2,data3)=(t2.data1,t2.data2,t3.data3)
 FROM table2 t2
 WHERE t1.xx_cd = t2.xx_cd;

ExcelVBA覚書 Workbookのつくり方

Application.Workbooks.Add
で新規ブックを作ることはできるのだが、2003バージョンで上限が6.5万行ぐらいのシートができてしまった。

これを強制的に100万行対応のファイルにできないものかと思っていたところ、

Dim def As XlFileFormat ‘DefaultSaveFormat 保管場所
With Application
  def = .DefaultSaveFormat
  .DefaultSaveFormat = xlExcel12 ’50 Excel2007-
  .Workbooks.Add
End With

というふうにApplication.DefaultSaveFormatプロパティを一時的に変えてから新規ブックを作成してやればよいことがわかった。
処理の最後に、また元の値(def)に戻してやればよい。

Java覚書 LinkedHashMap繰り返し

HashMapの繰り返し処理の方法

// LinkedHashMapの繰り返し(同値が見つかったらキーを返す)
private String getKey(LinkedHashMap<String,String> hmap
			, String targetVal) {
	for (Map.Entry<String, String> ent : hmap.entrySet()) {
		if (ent.getValue().toString().equals(targetVal)) {
			return ent.getKey().toString();
		}
	}
	return null;
}

Google Analytics でぶぶぶぅ~(?)

今年に入ってから忙しくてGoogle Analyticsを確認できていなかったのだが、どうも2014年1月からの集計ができてなかった。
できていない理由はよくわからないが、それまでWordPressのプラグインを使ってて全然問題もなかったのに、いきなり使えなくなってしまった。
プラグインが古くて使えないのか?、もうこのプラグインは使えないのか?と、別のプラグインで試してみたのだが、結局集計できずじまい。

もう、仕方なく、テーマのヘッダーファイルに無理やりスクリプトを記載して保存した。
不安だから、GoogleのTag Managerも登録してみた。
(でも、Tag Manager自体よくわからない。)

ら・・・うまくいった。

で、結局何が原因なのかはわからないが、@Pagesの広告がGoogle広告に変わったからかなぁ・・・などと思ったりする。

<覚書>
自分のアクセスは集計から除外する

http://11dax.com/google-analytics-2-460.html

自分のIPアドレスを教えてくれるサイト
http://www.cman.jp/network/support/go_access.cgi

PostgreSQL覚書 Triggerあれこれ

10年前にOracleで作成して以来、久しぶりにトリガーを作成してみた。
しかも、PostgreSQLで。

いろいろできなかったことがあったので調べてみたが、結局わからないところは代替案(要するに適当)で作った。
トリガー作成なんて、今後もあまりやらないだろうから、覚書は細かく書いておくことに。

まず、Functionを作ってからTriggerを作成。

--------------------------------------------------------
-- 走らせる処理はこっちに書く(言語はplpgsql)
--------------------------------------------------------
CREATE OR REPLACE FUNCTION SET_XX_FUNC()
  RETURNS trigger AS $$
    DECLARE
        -- 変数定義 --
    BEGIN
        -- 処理 --
    END;
$$ LANGUAGE ’plpgsql’;

--------------------------------------------------------
-- {table}に設定したテーブルに行が挿入、または行更新された後に処理を走らせる
--------------------------------------------------------
CREATE OR REPLACE TRIGGER SET_XX
 AFTER INSERT OR UPDATE ON {table} FOR EACH ROW
 EXECUTE PROCEDURE SET_XX_FUNC();

さて、ここまでは良い。
こっからだ。

--------------------------------------------------------
--★ 変数の設定
--------------------------------------------------------
DECLARE item_a int;
DECLARE item_b int :=999 ;
item_a := 1;

>>1つ目は変数のみ定義。
>>2つ目は変数定義と同時に初期値を設定する方法。
>>3つ目は定義した変数に値を入れる方法。

--------------------------------------------------------
--★ 情報の取得方法その1(普通のSELECT文)
--------------------------------------------------------
DECLARE item_a int;
DECLARE item_b int;
DECLARE item_c int;
-- (中略) --
SELECT a,b,c INTO item_a,item_b,item_c 
FROM {table} WHERE key1=NEW.key1 ;

>>SELECT文で取得したデータを変数に入れる場合の書き方はこんな感じ。
  SELECT a INTO item_a, b INTO item_b … ってな書き方はNG。
>>トリガーで更新・挿入したデータの情報を取得する場合は、NEW.{カラム名}で。

--------------------------------------------------------
--★ 情報の取得方法その2(Executeを利用したSELECT文)
--------------------------------------------------------
DECLARE add_months text = ’2months’;
DECLARE next_kigen text = ’’;
-- (中略) --
EXECUTE ’SELECT TO_CHAR(
           DATE_TRUNC(’’month’’
                , CURRENT_TIMESTAMP 
	               || ’ + interval ’ || quote_literal(add_months) || ’)’ 
	               || ’ + interval ’’-1days’’,’’YYYYMMDD’’)’
 INTO next_kigen; 

>>普通のSELECT文ではどうしても無理なものは、文字列のSELECT文を実行することになる。
  上の例は、現在日時から翌月末の日付(+2ヶ月-1日)を取得する方法。
  add_monthsがころころ変わる場合(3か月後、4か月後と)、普通のSELECT文ではどうしてもできなかった。
  あとは、サブクエリ―を使う場合もSELECT文では書けないと思っておいたほうがよい。
  (テーブル別名=エイリアスが使えないので。)
  なので、文字列を生成して
       EXECUTE 文字列 INTO 変数名;
  で検索結果をINTOの後ろの変数に格納させる。
  文字列内のシングルクォーテーションは’’と、2連続にすること。
>>quote_literal関数はよくわからないが、変数の型にあったquateを設定すると思う。
  文字列の間で変数を使いたいときは、必ずこの関数を使う。(使わんかったら動かんかったし)
  ちなみに、DATE_TRUNC(‘month’,{date_type})は月初を求めるときの関数。

--------------------------------------------------------
--★ 更新後の{item1}が0以下なら処理をやめる(条件文の例)
--------------------------------------------------------
IF NEW.{item1} <= 0 THEN
  RETURN NEW;
END IF;

>>途中で処理を抜ける処理の書き方がイマイチわからなかったので、とりあえずRETURN NEWしてる。
  (正しい方法を教えて!!)

--------------------------------------------------------
--★ 検索結果がないときの判定
--------------------------------------------------------
IF NOT FOUND THEN
  RETURN NEW;
END IF;

>>なのだが、これってカーソル処理やFOR文でしか作動しないように思う。

——————————————————–
–★ OLDとNEW
——————————————————–
OLDは更新前のレコードで、NEWは更新後のレコード。
NEW.{カラム名} という感じで使う。
なので、DELETEのトリガーの場合はOLDしかないし、INSERTの場合はNEWしかない。
今回は、AFTER INSERT OR UPDATE ~つまり更新・挿入後に行う~の処理なので、NEWしか使っていない。

衰えた脳みそをフル活用させて1日がかりで作ったのだが、うまく動かなかった ・・・・

で、デバッグ。
本当はログ出力して経過をみたかったのだけれど、ログ出力の仕方がよくわからないし、テスト環境と本番環境で異なる設定をしてもなぁ・・・と思って、動作途中で強制エラーを発生させることにした。

--------------------------------------------------------
--★ RAISE EXCEPTION(強制的に例外を発生させる)
--------------------------------------------------------
DECLARE add_months text = ’2months’;
-- (中略) 
RAISE EXCEPTION '% ここまで終わったぜ', add_months;

>>エラーメッセージに「2months ここまで終わったぜ」と表示される。
  (%は可変値で、2つ目以降の引数で設定する。Cとかの%dとかと同じ)
  なので、とりあえず、ここまでは走ってるよなぁ・・・ということで、
  次はこの例外発生処理を後ろに移動させてから実行する。
  これを何度か繰り返して、なんとか思い通りに動作するところまで行った。

とりあえず、直近で必要な情報はこんなところ。
今回はカーソルとかは使わなかったので、使うときはまた追記なり投稿なりしようと思う。
EXECUTEとかの使い方が難しいし、面倒だった。

まぁ、こんだけ書いておけば、次回はさほどオロオロすることもないだろう。

PostgreSQL覚書 日付8ケタ数値に変換

SQLServerとPostgreSQLを行ったり来たりしてると、どうもこんがらがって困る。

<現在日付を8桁にする>
to_char(current_timestamp,’YYYYMMDD’)::numeric

SQLServerだとCONVERTを使うんで、CONVERT・・・・と書きそうになった。

Xcode覚書 Core Data/マイグレーション

XcodeでMaster-Detailのテンプレートを使って、お勉強をしていたのだが、Detailに表示するデータを増やしたくて、xcdatamodeldを開いて、Attributionを増やした。

って、それだけで動かなくなった・・・

調べたら「マイグレーション」をしなければならない!ってなってて、「マイグレーション」ってなんやねん?ってなった。

よくわからないが、

・古いデータが最新のデータ構造と違うから動かへんねん
・せやから、古いデータ消しなはれ
・でもな、マイグレーションいうのがあって、項目の増減をどないかしてくれるらしいわ
・自動でマイグレーションを行わせたいんやったら、コーディネータ構築のoptionを変えとき!

まぁ、大阪弁で言うならこんな感じか・・・
(はしょりすぎだけど)

で、ソースは、

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];

と、変数を設定しておいて、

[persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:url
options:options
error:&err]);

デフォルトでnillになっているoptionsパラメータ?を設定した変数に変更する。

(自分への覚書)
Entityを追加したり、Attributeを増減させたりした後、
Core DataのNSManagedObject Subclassファイルを新しく作成するのを忘れずに。

Xcode覚書 main.mでエラー確認

Buildが成功しても、なぜかエラーになるので、エラー原因を突き止めるための方法。

困ったことに、また忘れてしまい、過去に作ったコードを探しまくり、ようやく見つけた。

 @autoreleasepool {
  @try {
   return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  } @catch (NSException *exception) {
   NSLog(@"%@",exception);
  }
 }

TryーCatchでエラーをログに表示させれば、ちょっとは原因に近づけるはず。(ハズ)

ExcelVBA覚書 ロック関連と数式セル判断

なんだかんだで、Zoo Keeper対戦中。
文句言ってても、結局すきなんだろぉ~・・・えぇ好きです(ポッ)、と下らん一人芝居をしたところで、メモ。

‘———————————————————

まずは、Excelマクロのロックあれこれ。

Const LOCK_PW as String = "pw"
Dim wb as Workbook
Dim ws as Worksheet
Dim rng as Range

と仮設定して・・・

‘ブックのロック/ロック解除

wb.Protect LOCK_PW     'ロック
wb.UnProtect LOCK_PW   '解除

‘シートのロック/ロック解除

If Not ws.ProtectContents Then ws.Protect LOCK_PW , AllowFiltering:=True    'ロック
If ws.ProtectContents Then ws.UnProtect LOCK_PW   '解除

(ロックしてもオートフィルターは使えるようにしといたほうが無難)

‘セルのロック/ロック解除

rng.Locked = True     'ロック
rng.Locked = False    '解除

シートはロックされているかどうか確認するプロパティがあるので、ロックがかかっていなければロックをかけるようにしないと、Excel2013ではロック・ロック解除に時間がかかるので、無駄に時間を食うロジックになってしまう。
セルロックはセルのロックをかけてもシートロックかけてないと有効にはならないので注意。

‘———————————————————

‘セルが数式なのかどうか

If rng.HasFormula Then
    '数式です!
Else
    '数式じゃありません!
End If

SQLServer覚書 日付8桁に、文字を数値に、数値を文字に

前の投稿から結構日数が経ってしまった。
というか、1月以上あいてしまった。

最近、SQLServerに接続してうんたらかんたら・・・というような開発をしている。
面倒だ・・・

DBによって、いろいろ関数がちがうのが困る。
例えば、OracleとかPostgreSQLだと、Now()で取ってこれる現在日時も、SQLServerだとGetDate()。
PostgreSQLだと、カラム名::intとかすればキャストできてしまうわけだが、SQLServerだと、Cast関数か、Convert関数を使う。

どっちが良いかはともかく、揃えてくれ・・・と思う。

さて、日付を8桁のYYYYMMDD形式にする方法。
現在時刻を8桁の文字列にする方法(CONVERT関数)
convert(varchar(8), getdate(), 112)

引数の最後の112ってのが「yyyymmdd」形式という意味。
これが、111だと「yyyy/mm/dd」、110だと「yyyy-mm-dd」になる。
(始めの引数をvarchar(10)にしておかねば・・・)

文字を数値に(CONVERT関数)
convert(int, ‘1234’)

数値を文字に(STR関数)
str(1234)

VBAでADOを利用しているが、数値のままだと文字連結でエラーになってしまった。
なので文字列型にしてから連結するためにSTR関数を使う。

Excelマクロ覚書 グラフを図でコピー

シートコピーとかするとグラフとデータをまるっとコピーできるが、そういうわけにもいかないときもある。
例えば、グラフのデータが別シートにあるとき。
これは困ったものだ、どうしたものか・・・と悩んでいたら、

「図でコピーして図で保存しておけばよいではないか!」

と思い立った。
これなら、グラフの元データの場所を考える必要ないし、ピクチャ形式で保存しておけば、容量もさほどとらないのではと思い、早速マクロを組んでみる。

CopyPictureというプロシージャ(関数)を利用するのだが、貼り付け関数は、シート関数にしかないので、貼り付けたいセルをSelectしてから、シート関数で貼り付けるという、少々バカバカしいというか、わざわざSelectする手間がいるところが嫌なところだ。

とりあえず、組んで走らせてみると、想定通りグラフは図形として貼りついて、オホホホホーーーとか思っていたら、動作検証をしてくださっているお客様から、

「うまくいかないんですよね~」とメールがやってきた。

やってみると、本当にうまくいってない。
グラフのプロットエリアがどんどんずれていって、プロットエリアが小粒になってしまっておる。

なぜじゃ!ナゼにじゃ!!

私が触ってた時はうまくいってたのに・・・と思って色々試すと、うまくいくときといかないときがある。

こういうのが一番困るんだよ。

と思いながら眺めていたら、どうやらシートの表示(ズーム)に影響しているのだと分かった。
解像度とかの絡みなのだろうか。

とりあえず、コピー元シートをアクティブにして、
 ActiveWindow.zoom = 100
とした後で、貼り付け先シートに貼り付けてやると、プロットエリアがずれないで貼りつくようになった。

おそらく、コピー元と貼り付け先のズームを同じにしてやらないと、綺麗には貼りついてくれないのだろう。
やれやれ、まったく困ったものだ。