忍者ブログ
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

長らく工事中でしたが公開してみようかなと思うw
どうもシュクレールです。

※ちなみに私はRO引退中ですん。
アカウントは生きてるけど復帰はしらんです。

http://www1.ttcn.ne.jp/komyu/sao/yuindex.htm
ID:user
PASS:guest
ベーシック認証入れているのでアクセスにはパスが必要です。

しばしば言ってましたが、
とある同盟を監視することを主目的としています。
また、環境設定(ソースに直書き)しないと使えません。
まぁ、自分の勉強含めてコメント多いソースなので
分かる人が読めばすぐわかると思います。
limechatでの使い方とかディレクトリの配置は、
limechatに従ってください。

ってことで、当時こんなのつくってたよーっていう
それだけのはなしです。

ものすごーーーーーーーーく勉強になったのは覚えている。
これいじってるだけで休日が終わるくらい楽しかったwww
PR
エクセルを立ち上げる際にはWorkbooks.Open(pass) 
エクセルにアクセスするためにはGetObject(pass) この2つを使い分けなければなりません。
 
GetObjectは開いているファイルにアクセスするのものなので、ファイルが開いてない場合には意図したとおりに動いてくれません。
 
今回の主題でもある、多重制御の必要性については結構ややこしい話があります。
ので、考え方を今回で、ソースについては次回に回します。
 
まずは、jscriptでは現在開いている窓のファイル名の取得(Mutex等)ができないということを念頭において以下を見て下さい。
 
エクセルを操作する処理を行う場合に、
「処理先のエクセルが本当に今開いているの?」
というのを確認しないといけません。
 
Workbooks.Openは2回目以降は「読み取り専用」としてエクセルが開きます。
処理のたびに開いていては窓が増えるだけなのです。
なので、あくまでも
Workbooks.Open→GetObjectという手順を守らなければなりません。
 
開いてなければWorkbooks.Openを使って、
開いてればGetObjectを使いたいので、この切り分けを判断させる必要があります。
 
あるいは運用で幾らかは回避できます。
・開いてあることを前提とする

こうすればよいのですが、キーワード反応で処理を行う場合、この縛りは結構なウェイトがあります。ので、やっぱり多重制御をしようという話になります。

ならば、開いてなかったら処理をスキップさせるということも考えられますが、

開いているかいないかを判断させる方法がわからない。

という現実があります。というのも冒頭の通り、ファイル名を取得する方法がないからです。

幸い?にもプロセスを参照する方法がjscriptにはあります。なので、プロセスを参照して、「EXCEL.EXE」が存在しているかしてないかを見分けるということはできます。

しかしこの場合、ターゲットのエクセルが開いているかどうかはわかりません。
もしかしたら別のエクセルが開いている可能性もあります。

たしかに、ターゲットが開いているぞ。というのを確認するために、ファイル名以外でなにかないかと考えた所、

「プロセスID」というのがあります。

じゃぁターゲットのプロセスIDを取得するためにどうしたらいいか。

基本固定値じゃありません。開くたびに変動します。

なのでプロセスを取得するためにはファイルを開く時しかありません。

しかもEXCEL.EXEしか分別できないので、ターゲットを開くときに他のエクセルが開いていたら正しく取得できないかもしれません。

ややこしいのでまとめると

①ターゲットのエクセルが開いているのかを知りたい
②ファイル名参照がjscriptではできない。
③プロセス(EXCEL.EXE)は知ることができる
④他のエクセルが開いていたら勘違いしちゃうかも
⑤じゃぁプロセスID(PID)で判断しよう
⑥どうやって取得するの?
⑦jscript上で立ち上げさせて、その時にPIDを保持しとこうか
⑧EXCEL.EXEまでしかわからないから、他のエクセルと勘違いしたりしない?
⑨立ち上げの時だけは他のエクセルが開いていない状態。という制限を設ければ必ずターゲットのエクセルのPIDが取得できるだろう。

という話の流れができます。

というわけでプログラム的な制御は以下のようなものになります。

◆エクセル立ち上げ部分
エクセルをプログラムから立ち上げさせて、PIDを取得する。
その際、他のエクセルが立ち上がっていたら警告する。

よって

STEP1:ターゲットのファイルがまず存在しているかを確認
STEP2:他のエクセルが立ち上がっていないかを確認
STEP3:上記2点がtrueならばターゲットを立ち上げて良いよ(PID取得)

以降は他のエクセルが立ち上がっても影響はなし。


◆エクセルへのアクセス
プロセスを参照して、格納したPIDと合致すれば「ターゲットが開いている」と判断できるのでアクセスを許可する

STEP1:PIDが格納されているかを確認。そして読み込み
STEP2:ターゲットのファイルが存在しているかを確認
STEP3:プロセスにEXCEL.EXEって今存在しているかを確認
STEP4:すべてtrueなら、「それの」PIDを読み込んだPIDとマッチングさせる
STEP5:合致すればアクセスOK

いきなりプロセス上のPIDを全参照して、合致するかを見てもいいのですが、
本当は開いていないときに、
ランダム?で配布されるPIDが万が一、違うアプリで同じになるということもありえないわけではないと思うので。
まずはEXCEL.EXEをさがして、そのPIDを参照するという形にします。

Mutex使えればこんなことしなくて済むんですけどね・・・

という結構めんどくさいお話でした。

ソースは次回へ
今回からしばらくはエクセル関連の操作を紹介していきます。
一番コアな技術部分になっています。

とにかく難しいことはおいといて、素直にエクセルを開く方法。

1|var objExcel = new ActiveXObject("Excel.Application");
2|objExcel.Visible = true;
3|objExcel.Workbooks.Open(ファイルパス);
4|objExcel.DisplayAlerts = false;
5|objExcel = null;

これだけです。
1行目でオブジェクトにエクセルを指定します。
2行目のvisibleはエクセルを表示するかどうかの指定です。ここでは表示にしています。
3行目でエクセルを開いています。ファイルパスの部分には開きたいエクセルの絶対パスが
指定されます。
4行目はアラート関連を表示しないようにします。ユーザーの返答を求めるような動作をされると困るからです。というのもLimechatの5秒ルールのためです。
5行目はオブジェクトの開放です。忘れずに。

次に、このすでに開いているエクセルに何か処理をする場合の方法です。

1|var objExcel;
2|objExcel = new ActiveXObject("Excel.Application");
3|objExcel = GetObject(ファイルパス);
4|objExcel.Sheets("Sheet1").Select;
//////処理///////
objExcel.Save();
objExcel = null;

開くときとは違ってgetobjectを使います。
これは既に開いている状態のエクセルをまさにゲットオブジェクトして、なにか処理をして、終わったら保存とオブジェクトの開放を行うっていう内容です。
何度もアクセスする場合、オブジェクトの開放しとかないと次回動きがおかしくなるので、開放はきっちりと。

処理については、色々出来ます。
ここからはエクセルマクロのVBAと同じ要領なので、行いたい操作をマクロ録画してあとからソース確認するといいと思います。

一例を以下に

・特定のセルへの書き込み
objExcel.ActiveSheet.Cells(X,Y).Value = XXXX;

セル(X.Y)に変数XXXXの中身を書き込みます。
もし、特定の文字を書きこみたい場合には
XXXXではなくダブルクオーテーションで囲みます。
シングルクオオーテーションでもいいっすけど。
objExcel.ActiveSheet.Cells(X,Y).Value = "何か文字";
消したい時はnullを代入すればセルは初期化されます。

・行の挿入
objExcel.ActiveSheet.rows("2:2").Insert;
こうすると2行目に1列行を挿入します。

・セルの読み込み
書き込みの逆です
XXXX = objExcel.ActiveSheet.Cells(X,Y).Value;

変数XXXXにセルの中身が入ります。

こんな感じで。

次回は、エクセルの多重起動について。
順番的にちょっとこれ先にやったほうが良かったかもしれないんだけど。というかこの辺はLimechatのヘルプを見て動かしてもらったほうが早いと思うのですが、一応解説しておきます。

Limechatのスクリプトの読み込みについてですが、
[設定]→[スクリプトの設定]
の画面から、[スクリプトフォルダを開く]でLimechat用のワークスペースが開きます。
このフォルダに.jsファイルをぶち込むと、リストに名前が出てきます。
適応したいサーバーの名前と.jsファイルの交点で右クリックをすると○印が付いて、その瞬間にスクリプトのコンパイルが走ります。

コンソール画面を開いておくと、コンパイル失敗時にのみなんか色々警告がでてきますので参照して下さい。

あとはいろいろトリガがあります。
例えば、

function event::onLoad(){
本文
}

であれば、スクリプトが走る初回に本文が実行されるような内容になります。発言があった場合や、特定のキーワードに反応する、その他のトリガもありますので、詳細はヘルプを見て下さい。

function event::onLoad(){
exam();
}

function exam(){
本文
}

とすれば、起動時にexam()の中身を呼び出すことができます。
1~3で解説しているサンプルについてはこの本文のみを抽出しています。のでfunctionで囲わないと動きません。

色々なfunction(関数と呼ぶ)を作って、トリガや必要に応じてこれらを呼び出すことでスクリプトが作られていきます。

・トリガ
・処理(関数)

この2つが揃ってスクリプトが動きます。

さて、いろいろ関数を作っていくと、お互いに変数の中身をやり取りしたいといったことが生じます。
処理だけを行う関数があって、その関数に値を渡すことで回答を得るような場合です。
そのような場合はグローバル関数や引数を使うことが必要になります。

自分はソフトは専門じゃないし、仕事で使うような場面は無いので、趣味だけで偏ったり間違った知識だとは思うのですが。

関数内で宣言した変数は、その変数の中でのみ有効になります。
なので、

function A(){
var i=10;
B();
}

function B(){
print(i);
}

とやっても,
変数iをBでpurintしても何も出て来ません。というかコンパイルが通りませんね。
また、

function A(){
var i;
}

function B(){
var i;
}

なんてことをやっても、別の変数として扱われます。不便のようにも思えますが
ループを回すだけの適当な変数の時とかは便利ですね。

話は戻りますが、前の関数Bでi=10を受け取るには引数かグローバル変数を使う必要があります。

////////////////////引数////////////////////////
function A(){
var i=10;
B(i);
}

function B(i){
print(i);
}
///////////////////////////////////////////////

とすればBにiを渡すことができます。
もしくは、

//////////////グローバル変数//////////////////
var i;

function A(){
i=10;
B();
}

function B(){
print(i);
}
///////////////////////////////////////////////

と、関数の外で変数を宣言してしまえば、すべての関数を行き来できる単一の変数として扱うことができます。
関数BからまたAに戻って関数Cに別の処理をお願いしたい。などという場合にはグローバルで宣言してしまったほうが楽でが、名前の頭には気を付けなければなりません。
というか個人的には内部変数であっても名前被らないようになんとなくしてしまうんですが。

ちなみに、引数とグローバル変数は異なる性質があります。
それは、引数だと片道通行になります。
関数AからBへは変数iを渡すことはできますが、処理後の値を関数Aに戻すことはできません。
戻すには戻り値(返り値)であるreturnを使わなければなりません。が割愛します。

一方のグローバル変数は全関数で共通の変数なので、行き来するという概念ではなく、どっからでも使うことが出来る関数となります。

グローバル変数はトラブルやバグのもとになりやすいのでむやみに使用しないほうがいいですが、スクリプトの規模が小さければ特に気にする必要もないかとは思います。が、ループさせるためだけの変数とかには使わないほうがいいですね。

ユーザーが設定する部分や、やり取りの多い変数について用いると良いかと思います。

ちなみに、グローバル変数はコンパイル時に作られるので、functionで囲まなくても変数として動いてくれます。

ちなみにちなみに、関数を分けて呼び出す利点については、まさに数学的な関数とおなじで
y=x+1
の関数を用意しておけば、xがわかるだけでyが求まるので、いちいち何度も同じ計算をスクリプトで書く必要性がなくなるからです。

ってそれくらいは当たり前ですよね。

ってことでちょっと閑話休題的な第4回でした。
これもどちらかと言うと初期設定に近い内容です。
が、実際にはスクリプトがランするたびに行なっています。

ユイはメインPCでこのスクリプトを動かして、2PC目でログの取得を行い、メインPCから2PC目のログファイルを参照するように作られています。
(2PC目でLimechatを動かしてメインPCはLimechatを見る専用にすればよかったんじゃねとは今でこそ思う・・・)
その際にネットワークのドライブをマウントしておくとアクセスが楽になるのでそのようにしてます。
おそらくネットワークパスを変数に格納しちゃってアクセスしても問題ないと思うんだけど、collectfortのときの残骸ではなんでわざわざマウントしてたのかな・・・。と謎なのですが、TIPSとしてはネタになるので。

ちなみにドライブのマウントは、
指定のアドレスをZ:ドライブみたいにアクセスできるようにするラベル貼り作業のことです。

PCが落ちるか、マウントの開放をするまで保持されます。

事前準備としては2PCめとメインPCをホームネットワークみたいに同じネットワーク上においておきます。そしてアクセスしたいフォルダを共有設定にしておきます。

手順としては

①参照したいネットワークフォルダのパスを変数宣言
②マウント名を変数宣言
③現在のネットワークドライブ名みて被ってないか確認(フラグ1)
④被ってたらそのパスを調べて①と同じか確認(フラグ2)
⑤-1マウント(フラグ3)(フラグ1が立ってなかったら)
⑤-2アクセス可能宣言(フラグ3)(フラグ1が立たってフラグ2が立っていたら)

ここでは3つのフラグを使います。
何度もスクリプトがランする場合、毎回マウントしないようにチェックする必要があります。
そこで、まずは被りがないかを確認します。
初回アクセスの時は被らないのでフラグ1が立ちません
二回目以降は被るのでフラグ1を立たせます。

フラグ1が立っていない初回アクセスの時は何も考えずにマウントしちゃって構いません。
が、フラグ1が立った場合、それはドライブ名が被っている証拠ですが、2回目だから被っているのか、それともこのパソコンの設定でもう既にその名前が使われてしまっているのか。が判断付きません。よってそれをフラグ2でしらべています。ドライブのパスを調べて、同じならフラグを立たせる(二回目以降のアクセス)とみなしています。

結果としてフラグ3が立てば何も問題はない。という結論に締めています。

あ、3回目にしてなのですが、このブログはスペースコードが使いにくいので、{ による段差って使ってません。本当に見難くてごめんなさい。

というわけでsample

*ブログの関係上¥と<は全角を使用しています。

01|var NetworkPath = "¥¥¥¥XX-PC¥¥RagnarokOnline¥¥chat";
02|var MountDriveName = "Z:"
03|
04|var objNtWork = new ActiveXObject("Wscript.NetWork");
05|var objDrv = objNtWork.EnumNetworkDrives();
06|var flag1 = false;
07|var flag2 = false;
08|var flag3 = false;
09|for(i=0;i<objDrv.Count() - 1;i+=2){
10|if(objDrv.Item(i) == MountDriveName){
11|flag1 = true;
12|
13|var rep = NetworkPath.replace(/¥¥/," ¥ " );
14|rep = rep.toUpperCase();
15|var comp = objDrv.Item(i+1).replace(/¥¥/," ¥ " );
16|comp = comp.toUpperCase();
17|if(rep == comp){
18|flag2 = true;
19|}
20|}
21|}
22|
23|if(flag1 == false){
24|objNtWork.MapNetworkDrive(MountDriveName,NetworkPath);
25|flag3 = true;
26|}else{
27|if(flag2 == false){
28|//エラーメッセージ的な処理
29|}else{
30|flag3 = true;
31|}
32|}
33|objNtWork = null;
34|
35|//以下でflag3がtrueの時に実際にアクセスするように組む。

1行ずつみていきましょう

01行は①のことです

02行は②のことです

04行はWSHです。しばしば出てきます。MSDNライブラリ:WindowsScriptHost

05行目で
WSHのNetWorkオブジェクトの中のEnumNetworkDrivesメソッドを参照します。

今更ですがオブジェクトってのは、なんかいろんな情報がつまったそのタイトルを掘り下げていく作業です。
例えるならオブジェクトが会社名(学校名)でメソッドが部署名(学年)って感じですかね。
なのでこのオブジェクトの中に格納されている情報を数えたりすることができたりします。

話は戻って。

06~08行はフラグの宣言と初期化(false)

09行からは結果だけ言うと、ネットワークドライブの数を数えて、ひとつひとつ名前被りをしていないか確認しています。
ループカウントが2なのはたしか1つのドライブに対してなんかカウントが2つ上がるからだったとか思うけどよくは覚えてない。

10行目でItemプロパティを使って、取得したネットワークドライブの情報からドライブ名を引き抜きます。で、名前被りがあったらflag1をtrueにする。名前被りがなければ何も考えずにマウント作業へ。

13行目からパスチェックを行います。
名前被りがあった場合に二回目以降のアクセスなのかを判定します。
Itemのなかのドライブ名の次(i+1)にはそのドライブのパスが格納されているので、まずはjscriptのめんどくさいところである¥¥を¥に置き換えて、見慣れたパス表記に直します。また、参照時には大文字小文字を区別してしまうのでtoUppercaseで全部大文字にしてしまいます。
ということを、こちらが手打ちで設定した01行目のパスにも行なって、もし同じ場合には、18秒目でflag2を立てます。つまり二回目以降のアクセスだから大丈夫だよってことです。

あとは前述したフラグのたち具合にそって処理の振り分け。

24行目で実際のマウント作業
MapNetworkDriveメソッドをつかいます
ついでにflag3をたてる。

26以降は、flag3を立てるか立てないかの処理です。

さいごのnullはオブジェクトの開放です。

オブジェクト使い終わったら解放するようにしておきましょう。
リソースがなんたらとか言うのがあった気がする。

で、実際にアクセスする場合の方法については、また別項目で書きます。

Itemの中身が見たい場合には
ループのどっか適切な場所に
log(objDrv.Item(i));
とかいれておけば
Limechatのスクリプトコンソールで見ることができます。

logはデバッグでよく使うので、Limechaのヘルプ読んで使い方覚えておきましょう。
HOME  → NEXT
プロフィール
HN:
komyu/シュクレール/修造
性別:
非公開
自己紹介:
こうぶつはけものみみとのーびす(♀)とエリーン
主にROとTERAとラノベと雑多なkomyuの日記帳です。
新生FF14
TERA紋章しみゅ
ROサポートツール
タイムライン
RSSボットが記事更新をつぶやくのがほとんどっス…
ROキャラクター紹介


TERAキャラ紹介

シュクレール(バサ)



syuzo(エレ)



サケマス(アチャ)


その他サブ

ウリエルキゥィス(スレLv54)

Vermillion(サラLv56)

パズドラ

157,318,362
フレンド募集中
枠なきゃ増やす
最終プレイ3日以上になったら消す
カレンダー
11 2024/12 01
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
ツリーカテゴリ
ブログ内検索
最新コメント
[04/30 のな]
[03/22 NEIN]
[03/22 NEIN]
[10/31 イロイ]
[05/02 NONAME]
Copyright (C) 2024 のーびす監察日記 All Rights Reserved.
Photo by 戦場に猫 Template Design by kaie
忍者ブログ [PR]