OSの起動時刻の取得

解決


tororo  2004-05-15 13:38:08  No: 8967  IP: [192.*.*.*]

OSが起動したときの時刻をTDateTime型で取得したいのですが
やりかたが分かりません。
どなたか分かる方おしえてください。お願いします。

編集    削除
sadoyama  2004-05-15 20:42:35  No: 8968  IP: [192.*.*.*]

OSが起動した時刻を直接求める方法は分かりませんが、
おおよその時刻であれば、GetTickCount API で起動してからの経過時間が得られますので、現在の時刻より逆算すれば得られます。
GetTickCountが返す型はIntegerで、単位はミリ秒です。
秒単位で欲しければ1000で割ることになります。

編集    削除
sadoyama  2004-05-15 20:55:31  No: 8969  IP: [192.*.*.*]

失礼しました。
その値をTDateTime型で得たいのでしたね。
起動時刻ではありませんが、起動後の経過時間をTDateTime型に変換する例です。

procedure TForm1.Button1Click(Sender: TObject);
var
  TimeStamp: TTimeStamp;
  DateTime: TDateTime;
begin
  TimeStamp := MSecsToTimeStamp(GetTickCount);
  DateTime := TimeStampToDateTime(TimeStamp);
  Showmessage(TimeToStr(DateTime));
end;

編集    削除
333  2004-05-15 22:05:54  No: 8970  IP: [192.*.*.*]

GetTickCount の戻り値は DWORD なので、およそ 49.7 日でカウントアップ
するのが問題なんですよね。そこをクリアできればいいんですけど。

編集    削除
tororo  2004-05-15 22:16:21  No: 8971  IP: [192.*.*.*]

つまり起動時刻が知りたければ
現在の時刻から経過時間をひけばいいのですね?
しかし、
procedure TForm1.Button1Click(Sender: TObject);
var
  TimeStamp: TTimeStamp;
  DateTime: TDateTime;
begin
  TimeStamp := MSecsToTimeStamp(GetTickCount);
  DateTime := TimeStampToDateTime(TimeStamp);
  Showmessage(DateTimeToStr(Now -  DateTime));
end;
と書いたらおかしな値がでました
どこかおかしいですか?

編集    削除
sadoyama  URL  2004-05-16 05:27:13  No: 8972  IP: [192.*.*.*]

>と書いたらおかしな値がでました。どこかおかしいですか?
ということは、私のコードでエラーにならなかったのですね。
TimeStampは時間だけを得る関数で、DateTimeは日付と時刻。
Now -  DateTimeとやってしまうと、年月の引き算も入ってしまっておかしな値が生まれますが、333さんが書いているように、GetTickCount の戻り値は DWORD。
TTimeStamp型に代入できて処理できてしまうのは変だと引っかかっていたのですが、Delphi3だと通ってしまうようです。
Delphi6で試したらエラーになりました。というわけで、私のコードは誤りです。

また、「逆算すれば得られます」と簡単に書いてしまいましたが、結構面倒ですね。
Delphi6でテストしたコードを載せます。
ただし、午前0時をまたぐ場合については、更に条件分岐が必要となります。
また、333さんの言う49時間も、その場合にはお手上げでしょう。
Windowsが起動したときに、出勤時刻をどこかに記録していてくれない限り。

procedure TForm1.Button1Click(Sender: TObject);
var
  StartTime: TDateTime;
  NowMse, PastMse, StartMse: LongInt;
  StartH, StartM, StartS, StartMs, TempS: Word;
begin
  // 現在時刻を本日の0時から経過したミリ秒で得る
  NowMse := MilliSecondOfTheDay(Now);

  // 起動時からの経過時間をミリ秒で得る
  PastMse := GettickCount;

  // 起動時刻を本日の0時から経過したミリ秒数で得る
  StartMse := NowMse - PastMse;

  // 起動時刻を時分秒ミリ秒に分ける
  TempS := StartMse div 1000;
  StartH := TempS div 3600;
  StartM := TempS div 60 mod 60;
  StartS := TempS mod 60;
  StartMs := StartMse mod 1000;

  // TdateTime型を生成する
  StartTime := EnCodeTime(StartH, StartM, StartS, StartMs);

  Showmessage(TimeToStr(StartTime));
end;

編集    削除
にしの  2004-05-16 05:39:31  No: 8973  IP: [192.*.*.*]

日付はdouble型ですから、そんなに難しく考えることはないと思いますよ。

procedure TForm1.Button2Click(Sender: TObject);
var
  d: double;
  n: TDateTime;
begin
  d := GetTickCount;
  n := Now;
  d := ((((d / 1000) / 60) / 60) / 24); // 変換(ミリ秒->秒->分->時間->日)
  n := n - d; // 現在時刻から起動後の時間を引く
  ShowMessage(DateTimeToStr(n));
end;

編集    削除
sadoyama  2004-05-16 06:44:05  No: 8974  IP: [192.*.*.*]

にしのさんwrote
>d := ((((d / 1000) / 60) / 60) / 24); 
そうだ。日にちに合わせればTDateTime型の値になったのだ。
思いつきませんでした。
習わないと分らないことでした。感謝。

編集    削除
333  2004-05-16 07:09:44  No: 8975  IP: [192.*.*.*]

にしのさんのレス内容は、真っ先に浮かびましたが、

49.7 日でカウントアップ

この問題が解決出来ないので書きませんでした。
GetTickCount だけでは、正確な起動時刻は分からないのでは?

検索していくつか答えらしきものを見つけましたけど、どうも
自分のところではうまくいっていません。

編集    削除
tororo  2004-05-17 06:33:10  No: 8976  IP: [192.*.*.*]

にしのさんの方法でうまくいくことができました。
49.7日でカウントアップということですが、
解決方法などが見つかりましたらまた御教授ください。
ソース付きで詳しく解説してくださってありがとうございました。

編集    削除
にしの  2004-05-17 18:38:31  No: 8977  IP: [192.*.*.*]

レジストリに起動日時が書いてありそうですが、見つかりませんでした。
# ShutdownTimeというのはあったのですが、バイナリでSYSTEMTIMEなのかFILETIMEなのかも未確認

月に1度GetTimeCountをチェックして、カウントアップのチェックを行えば誤魔化せます。
常駐でなくても、タスクに登録しておけばよろしいかと。

編集    削除
333  2004-05-17 19:22:16  No: 8978  IP: [192.*.*.*]

うーむ、それならスタートアップに登録して、起動時刻をファイルかレジストリに
記録するだけのアプリのほうがよいのではないでしょうか。

編集    削除
にしの  2004-05-18 05:22:15  No: 8979  IP: [192.*.*.*]

GetTickCountがいつから開始するか正確なことはわかりませんが、スタートアップだとかなりずれますよ。

編集    削除
333  2004-05-18 05:53:43  No: 8980  IP: [192.*.*.*]

> スタートアップだとかなりずれますよ。

えーと、どのくらい正確なのが必要かんもよりますが、20秒くらいならいいんでは。

> 月に1度GetTimeCountをチェックして、カウントアップのチェックを行えば誤魔化せます。

これよりかなりましだと思いますけど。月に1度、忘れないで実行するのは難しい
と思います。

編集    削除
にしの  2004-05-18 06:09:31  No: 8981  IP: [192.*.*.*]

> これよりかなりましだと思いますけど。月に1度、忘れないで実行するのは難しい
> と思います。

これは、

> 常駐でなくても、タスクに登録しておけばよろしいかと。

と書いたように、タスクで月に一度実行させればよいかと思います。ウィルスチェックを週一度するのと同じような物です。

同じ間隔で、カウントアップの時間(47.9日)以内であれば、カウントアップしたかどうかの判別は大小の比較だけでできますから。

タスクが消されるかもしれないという問題は、スタートアップも同程度ですし。

編集    削除
333  2004-05-18 08:44:35  No: 8982  IP: [192.*.*.*]

> カウントアップしたかどうかの判別は大小の比較だけでできますから。

そうですね。GetTickCount で現在の数値が前回記録されたのより小さければ
カウントアップしたということで、レジストリかファイルかにあるカウントアップ
した回数を記録する数値をインクリメントするのでしょう。しかし、その場合、
新規に起動されて数値が小さくなった場合とどうやって区別しますか?

起動時刻を記録したほうが簡単だと思います。

編集    削除
にしの  2004-05-18 09:53:47  No: 8983  IP: [192.*.*.*]

> 新規に起動されて数値が小さくなった場合とどうやって区別しますか?
普通に考えれば、コマンドオプションをつけてタスクに登録でしょう。
スタートアップに登録されたショートカットを実行された場合も、同様の考慮が必要だと思いますが。

> 起動時刻を記録したほうが簡単だと思います。
考えてみたら、スタートアップでGetTickCountから逆算した起動時刻を記録であれば、こっちのほうが簡単ですね。
# 投稿者がどの程度の精度を必要としているかにもよります
ショートカットだと上に書いたような問題が残ってしまうので、やはりレジストリのrunでしょうか。

面倒ですが、サービスに登録してしまう(常駐させてしまう)という手もあります。

編集    削除