結果を得るにはまず過程を知ろう
野蛮なプログラミング、第13回開始です。
本文に対しても、ツイッターでも告知でも、そこそこスキを頂いて、多少は読んでもらえてるとは思っているんですが、どうも疑問とか質問とか来ないと不安ですな。分からない事や、おめーそりゃおかしいだろとか文句やら批判やら、何でも受け付けてます。私は根っからマゾなので、叩かれて伸びるタイプです。
前回のような宿題を出して、その解答を翌週に出すというのは、今後もやっていこうと思ってますが、現時点ではあまりに反応なくて、まだ機は熟してないかなと。もっと宣伝がんばらないとなあ。内容には自信があるので。
これまでの構成で続けていくかも色々考えました。で、今回は内容は変わってませんが、並びが多少違います。その意味は読んで頂ければ分かるかと。
【前回】
◆今日のIT界隈:MicrosoftはEdgeにどこまで本気なのか
自分が使っているWindows10のPC2台でも、数日前に、クロムベース版の「Edge」が入りました。相変わらず勝手にタスクバーにピン留めしたり五月蠅いなあというのはさておき、メモリー食いの「Google Chrome」(本家「Chromium」と「Google Chrome」は厳密にいうと違うんですが、そこはちょっと棚上げして)より、消費量が抑えられているらしく、概ね好評のようです。
Windows10のメニュー構成やら何やらを見ていると、今までの「Internet Explore」は、このままフェードアウトする運命にしか見えないんですが、いいんすかねー、それで。
企業が自社のWebアプリを使う際に、動作保証するブラウザを「Internet Explore」にしている所は、まだ結構ある筈で。というのは、Windows10への移行が済んでない所が多いからです。が、昔に比較して、こういう対応は早くなっているのかもしれない。それ以上に、現場での現役は、去年の11月までしかやっていなかった自分が、この半年の動きについていけてないのかもしれない。
いや「Edge」がWindowsのデフォルトブラウザになるというのは、結構前に発表されてますから、問題はそこではなく。ヨソで書かれたソースを基礎にして、それを自社OSのメインブラウザに据えてしまうというMicorsoftの方針についてです。いいのかそれでというのは。
ただ、ここはいいんですという、面白くも何ともない結論になるわけです。
過去にもこういう事をやってます。一番大きいのは、Windows9xシリーズからWindowsNT系への移行でしょうか。Windows3.1から95、98、Meと続いたクライアントOSと、サーバーOSであったWindowsNTを、Windows2000で完全統合した件。
MS-DOSという自社OSの流れで接ぎ木されて32ビット化されたWindows95系と、IBMとの共同開発であるOS/2から、DECのVMSというOSの設計思想を取り入れて分岐し、新しいOSとして発表したWindowsNT。
ここでMicrosoftが驚異的なのは、Windows95系という捨て去ったOSで動いていたアプリが、まったく違うソースコード前提のWindows2000のみならず、現在のWindows10の64ビット版でも、概ね動く事です。
そして、その理由こそが、95時代に提供され始めた「Win32API」が未だ開発者用のコアに据えられているという事実なんですね。つまり、この連載ともド直結しているという。枯れた技術こそ安定して動く。
ちなみに、WindowsNTの開発話は、技術的な部分よりドキュメンタリーとしての面白さという事になりますが、名著「闘うプログラマー」という本にまとめられていますので、興味のある方は是非。「デスマーチ」という言葉を生んだ本でもあります。
この辺りが、過去OSで動いていたアプリを、すぐ見捨てちゃうMacが企業のクライアントOSに採用されない理由なんだよなあと思いつつ「Internet Explore」 を久々に立ち上げたら、Twitterは表示できないわ、noteもレイアウト崩れまくるわで、全然使いモンにならない。あれーっ??? ……帰ってきてくれービル・ゲイツ先生! 世界平和より、技術者の幸福を優先してくれー!
◆今日の進捗
今回の記事をまとめようと思って、この辺りで苦しんでいました。
自分は、色々な事を、大掴まえするというか、曖昧に理解するのは、かなり得意なんですが、人にモノを教える場合は、それじゃ駄目。という事を繰り返し、自分に言い聞かせています。
◆今日の必須ワード
「デフォルト設定」「デバッガ」「MS932」
最後の「MS932」は、過去に挙げた「ANSI」「Shift JIS」とニアイコールですが、結構この表現が多い事に気付き、リトライという感じです。
◆今日のIT小ネタ:開発環境における文字コード設定
この連載を開始するにあたり、久々に「Visual Studio 2008」をいじったんですが、ここ数年、現場でやっていたのは、ずっとJAVA(+α)だった為、随分と忘れていた事がありました。
業務でいじっていたのは、延々古いバージョンのプロジェクトを、新しい「Visual Studio」に移行しながら使っていた事もあり(つまりひとつのシステムを延々保守していたんですね)、「2008」という、これですら既にMicrosoftのサポートが切れている古い開発環境であるのに、そこでの新規機能に、まるで対応出来ていない。
しかも、新規でC++のプロジェクトを作って、色々いじるのは、もう15年以上ぶりの事でした。
「Visual Studio」にせよ「Eclipse」で開発するにせよ(そしてどのバージョンにせよ)、新規にプロジェクトを作ると「デフォルト設定」というのがあります。勝手にツールが色々な設定をしてくれるわけです。多くの場合「デフォでOK」な場合が多いんですが、文字列をいじる場合、これが多大な影響を及ぼす設定がありました。
例の文字コードです。前々回の小ネタに色々書いてます。
自分の感覚だと、「Visual Studio」は「Shift-JIS」、「Eclipse」は「Unicode(UTF-8)」になっているモノという思い込みが強過ぎて、「2008」でC++の新規プロジェクトを作ると、ソースを編集するエディタの「デフォルト設定」が「Unicode」しかも「UTF-16」になっているという事に全然気づきませんでした。
過去の別システムの古いソースコードを参考にしようとも、そのままだと全然動かない。随分と苦しみました。
結果として、今の開発ツールでWin32APIを使ったアプリを開発する際に、何を注意すればいいのか、散々勉強するハメになりましたので、オーライなんですが。
「Eclipse」は、サクラエディタや秀丸エディタと同じように、常に現状の文字コードが右下に表示されています。意識しましょう。ファイル単位で切り替えられるので、かなり便利です。
一方「Visual Studio 2008」はプロジェクト毎に設定しなければいけないので、小回りは効きませんし、わざわざプロジェクト設定を覗かないと、今どうなっているのかが分からないので、結構分かり難いです。
一般的に「Eclipse」の場合、Webアプリを開発するなら「UTF-8」、Windows上で動くアプリを開発するなら「MS932」にするのがお勧めです。
で、ここで今日の必須ワードの「MS932」なんですが「ANSI」とは、ほぼイコール、「Shift-JIS」ともニアイコールです。何でそんなに呼び方自体が色々あるんだといえば、Microsoftが勝手に色々拡張した経緯があって、恐らく、それをどこまで取り込んでいるか等の違いだと思うんですが、この微妙な違いは自分もまだ勉強中。自信がないとこです。
◆今日の本題:ログ出力はデバッガより便利
プログラミングをしたら、動くか確認します。テストです。中々思ったようには、動いてくれない。そこで登場するのが「デバッガ」です。
どんな言語でも、どんな開発環境でも、プログラミングの最初の一歩的なガイド文章には、多くの場合、この「デバッガ」の使い方にも触れられていると思います。
上手く動かない原因を、プログラムを実行中に途中で止めて、変数やら何やらの中身をチェックし、探っていく。その為のものです。
ところが、GUIなアプリ(今時の言い方をすれば「デスクトップ環境」)、つまり、WindowsやらiOSやらAndroid上で動くアプリは、常にOSと通信をしながら動いています。
例えば、隠れていたアプリを再表示しようとした場合、OSから、キミを再表示するってユーザーが言ってるから、自分自身を再描画してくれる? 的なメッセージが飛んでくるわけです。
スマートフォンだとありませんが、MacやWindowsPCだと、例えばアプリのサイズを変えたり、移動したりした時も、この再描画しろメッセージが飛んできます。
ここに上手く動かない原因がありそうだとなった時、さらに詳細を追求する為に、デバッガで、この再描画メッセージを受けて、動作するコード部分を調べるわけですが……
・OSから再描画命令→アプリの再描画処理
だけなら問題ないんですが、
・OSから再描画命令→アプリの再描画処理→開発ツールのデバッグ画面
と処理が遷移すると、
・OSから再描画命令→アプリの再描画処理→開発ツールのデバッグ画面→OSから再描画命令→アプリの再描画処理→開発ツールの……
このように処理がループしてしまう場合が多く。つまりデバッグにならなくなってしまうんですね。多くのベテラン技術者も経験している事と思います。では、どうするか。
知りたい事は何なのかに焦点を合わせ、1回デバッガで止め、内容を把握したら、次からは止めない設定にして、再度処理を動かす。……というような対応しかないと思います。
自分はこれが面倒臭くて。なので、あまりデバッガで止めません(勿論、止める事もありますよ)。替わりにデバッグの目安として使うのがログ出力です。ほとんどの開発環境には、実行時の状況を出力する用のログ出力機能はある筈なんですが、これが開発環境によって違い過ぎる。それを理解するのに時間が掛かったり、ちょっと使わないと忘れてしまったり。
そこで、自分はどんな言語でも大体使えるログ出力をプログラミングしてみました。
まず、プログラミングする時に、自分のひとつの癖をつけます。
001 void NewMethod01()
002 {
003 LogOut("NewMethod01 Start");
004
005 LogOut("NewMethod01 End");
006 }
新規の関数(メソッド、サブルーチン、etc)を作る場合、開発ツールが自動生成したり、自分で新規に書いたりするわけですが、とにかく、まだ空のうちに、上記のようにLogを出力する関数を書いてしまう。「LogOut」というのが、それです。
次に、INIファイルを作ります。EXEと同じ名前でEXEと同じディレクトリに置く事を前提として、拡張子のみ「.ini」にします。ここにログを出力するか否かのオプションを用意します。
ログを出力するという機能だけなら、別にこんな事をする必要はないんですが、テストが終わって、さて完成、リリース(納品、発表、etc)だとなったら、もうソースはいじりたくない。だからINIファイルで出力を止める。それを可能にするよう、最初から作っておけば便利という話です。
001 [LogOption]
002 LogOut=ON
で、LogOut関数の中身はこんな具合。まずはC++です。
001 #include <windows.h>
002 #include <stdio.h>
003
004 #define CS_L1024 1024
005 #define CS_L64 64
006 #define CS_ON "ON"
007 #define CS_OFF "OFF"
008 #define CS_INISEC "LogOption"
009 #define CS_INIKEY "LogOut"
010
011 void LogOut(LPCTSTR str)
012 {
013 // ローカル時間を取得
014 SYSTEMTIME SysTime;
015 ::GetLocalTime(&SysTime);
016
017 // INIファイル・LOGファイルのフルパスを作成するにEXEのフルパスを取得
018 // INIファイル:xxx.exeの場合、xxx.ini
019 // LOGファイル:xxx.exeの場合、xxx.log
020 HINSTANCE hInst = ::GetModuleHandle(NULL);
021 char chExePath[1024];
022 ::GetModuleFileName(hInst, (LPCH)chExePath, sizeof(chExePath));
023
024 // フルパスを分解
025 char chDrive[CS_L64];
026 char chDir[CS_L64];
027 char chFname[CS_L1024];
028 char chExt[CS_L64];
029 char chIniPath[CS_L1024];
030 _splitpath_s( chExePath, chDrive, chDir, chFname, chExt );
031 // 拡張子をINIに変更
032 _makepath_s( chIniPath, 1024, chDrive, chDir, chFname, "ini" );
033
034 // INIファイルのLog出力フラグがONか確認
035 // INIファイルが存在しない場合、CS_OFFが戻るので、処理中断
036 // LOG出力なので、エラー処理は行わず、単純に処理終了
037 char chWk[256];
038 ::GetPrivateProfileString( CS_INISEC, CS_INIKEY, CS_OFF, chWk, CS_L1024, chIniPath );
039 if (strcmp(chWk, CS_ON) != 0) {
040 return;
041 }
042
043 // 拡張子をLOGに変更
044 char chLogPath[CS_L1024];
045 _makepath_s( chLogPath, 1024, chDrive, chDir, chFname, "log" );
046 // LOGファイルを作成(存在しなければ新規作成する)
047 HANDLE hFile = ::CreateFileA( chLogPath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
048 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
049 if (hFile == INVALID_HANDLE_VALUE) {
050 return;
051 }
052
053 // 日付時間と与えられた文字列を編集
054 char chWriteBuf[CS_L1024];
055 sprintf_s( chWriteBuf, "%04d/%02d/%02d %02d:%02d:%02d ... %s\r\n",
056 SysTime.wYear, SysTime.wMonth, SysTime.wDay,
057 SysTime.wHour, SysTime.wMinute, SysTime.wSecond, str);
058
059 // LOGファイルの終端に移動
060 DWORD dwRc = ::SetFilePointer( hFile , 0 , NULL , FILE_END );
061 if ( dwRc == INVALID_SET_FILE_POINTER) {
062 return;
063 }
064
065 // LOGファイル書き出し&終了処理
066 char chReturnSize[CS_L64];
067 ::WriteFile(hFile , chWriteBuf , strlen(chWriteBuf), (LPDWORD)chReturnSize , 0);
068 ::CloseHandle( hFile );
069
070 return;
071 }
で、結果出力したログファイルはこんな感じ。
001 2020/06/21 13:34:10 ... m8rcvrGCC main Start ! 日本語OK?
002 2020/06/21 13:34:10 ... m8rcvrGCC MyRegisterClass Start !
003 2020/06/21 13:34:10 ... m8rcvrGCC MyRegisterClass End !
004 2020/06/21 13:34:10 ... m8rcvrGCC InitInstance Start !
005 2020/06/21 13:34:10 ... m8rcvrGCC WM_CREATE !
006 2020/06/21 13:34:10 ... m8rcvrGCC MainOnCreate Start !
007 2020/06/21 13:34:11 ... m8rcvrGCC MainOnCreate Timer ON IDEvent = 3300,m_pTimer = 31956 !
008 2020/06/21 13:34:11 ... m8rcvrGCC MainOnCreate End !
009 2020/06/21 13:34:11 ... m8rcvrGCC DoCreateStatusBar Start !
010 2020/06/21 13:34:11 ... m8rcvrGCC DoCreateStatusBar End !
011 2020/06/21 13:34:11 ... m8rcvrGCC DoCreateButton Start !
012 2020/06/21 13:34:11 ... m8rcvrGCC DoCreateButton End !
013 2020/06/21 13:34:11 ... m8rcvrGCC DoCreateStatic Start !
014 2020/06/21 13:34:11 ... m8rcvrGCC DoCreateStatic End !
015 2020/06/21 13:34:11 ... m8rcvrGCC WM_PAINT INIT !
016 2020/06/21 13:34:11 ... m8rcvrSDK WM_CTLCOLORSTATIC Start !
017 2020/06/21 13:34:11 ... m8rcvrGCC InitInstance End !
018 2020/06/21 13:34:13 ... m8rcvrGCC WM_SYSCOMMAND !
019 2020/06/21 13:34:17 ... m8rcvrGCC WM_COMMAND !
020 2020/06/21 13:34:17 ... m8rcvrGCC ID_BUTTON01 Clicked !
021 2020/06/21 13:34:18 ... m8rcvrGCC WM_DESROY !
022 2020/06/21 13:34:18 ... m8rcvrGCC MainOnDestroy Start !
023 2020/06/21 13:34:18 ... m8rcvrGCC MainOnDestroy End !
続いて、JAVAだとこうなります。INIファイル、LOGファイルのフルパスを作成するところ以外は、C++とまったく同じ事をやっていることに注目して頂ければ。
001 import com.sun.jna.Pointer;
002 import com.sun.jna.WString;
003
004 public static long GENERIC_WRITE = 0x40000000L;
005 public static long GENERIC_READ = 0x80000000L;
006 public static long OPEN_ALWAYS = 4L;
007 public static long FILE_ATTRIBUTE_NOMAL = 0x00000080L;
008 public static long FILE_END = 2L;
009 public static long INVALID_SET_FILE_POINTER = 0xFFFFFFFFL;
010
011
012 public static String CS_MCAP = "m8rcvrJAVA";
013 public static String CS_ON = "ON";
014 public static String CS_OFF = "OFF";
015 public static String CS_SEC = "LogOption";
016 public static String CS_KEY = "LogOut";
017 public static long CS_L1024 = 1024L;
018 public static long CS_L64 = 64L;
019
020 public static void LogOut(String str) {
021
022 // ローカル時間を取得
023 SYSTEMTIME SysTime = new SYSTEMTIME();
024 Kernel32.INSTANCE.GetLocalTime( SysTime );
025
026 // JarフルパスからINIファイル名を作成
027 // INIファイルはJarと同じディレクトリに置く前提
028 WString wstrIniFileName = new WString(System.getProperty("user.dir")
029 + "\\" + CS_MCAP + ".ini");
030
031 // INIファイルのLog出力フラグがONか確認
032 // INIファイルが存在しない場合、CS_OFFが戻るので、処理中断
033 // LOG出力なので、エラー処理は行わず、単純に処理終了
034 long lLen = 0L;
035 char[] chRet = new char[(int)CS_L1024];
036 lLen = Kernel32.INSTANCE.GetPrivateProfileStringW(
037 new WString(CS_SEC), new WString(CS_KEY), new WString(CS_OFF),
038 chRet, CS_L1024, wstrIniFileName);
039 String strRet = new String(chRet);
040 strRet = strRet.substring(0, (int)lLen);
041 if (strRet.equals(CS_ON) == false) {
042 return;
043 }
044
045 // LOGファイルを作成(存在しなければ新規作成する)
046 String strLogFileName = new String(System.getProperty("user.dir")
047 + "\\" + CS_MCAP + ".log");
048 long lFile = Kernel32.INSTANCE.CreateFileA(
049 strLogFileName, GENERIC_READ | GENERIC_WRITE, 0L, 0L, OPEN_ALWAYS,
050 FILE_ATTRIBUTE_NOMAL, 0L);
051 if (lFile == 0) {
052 return;
053 }
054
055 // 日付時間と与えられた文字列を編集
056 String strWrite = String.format("%04d/%02d/%02d %02d:%02d:%02d ... %s\r\n",
057 SysTime.wYear, SysTime.wMonth, SysTime.wDay, SysTime.wHour,
058 SysTime.wMinute, SysTime.wSecond, str);
059
060 // LOGファイルの終端に移動
061 long lRetSize = 0L;
062 long lRc = Kernel32.INSTANCE.SetFilePointer( lFile, 0, 0, FILE_END );
063 if (lRc == INVALID_SET_FILE_POINTER) {
064 return;
065 }
066
067 // LOGファイル書き出し&終了処理
068 Kernel32.INSTANCE.WriteFile( lFile, strWrite, strWrite.getBytes().length, lRetSize, 0L );
069 Kernel32.INSTANCE.CloseHandle( lFile );
070
071 return;
072 }
但し、JAVAの場合、別ファイルで「構造体もどき」を作っておく必要があります。JAVAは構造体を持てないので、多少トリッキー。この構造体とクラスの問題は、結構難しいので、今回は一応ソース載せときますが、説明は、おいおいということで。
001 import java.util.Arrays;
002 import java.util.List;
003 import com.sun.jna.Pointer;
004 import com.sun.jna.Structure;
005
006 public class SYSTEMTIME extends Structure {
007
008 @Override
009 protected List<String> getFieldOrder() {
010 return Arrays.asList("wYear", "wMonth", "wDayOfWeek", "wDay",
011 "wHour", "wMinute", "wSecond", "wMilliseconds");
012 }
013
014 public short wYear;
015 public short wMonth;
016 public short wDayOfWeek;
017 public short wDay;
018 public short wHour;
019 public short wMinute;
020 public short wSecond;
021 public short wMilliseconds;
022
023 public SYSTEMTIME() {
024 super();
025 }
026
027 public SYSTEMTIME(Pointer p) {
028 useMemory(p);
029 read();
030 }
031
032 }
続いてExcel + VBAなんですが、まずは上記のC++やJAVAと同じく、Win32APIを使って、同じロジックにしようとして、結果動かなかった奴。くそーこの連載上、初めての挫折です。どなたか、解析してくれないかしら。この発想じゃ無理なのか、何か直せば動くのか。
001 ' 上手く動きません
002 Declare PtrSafe Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
003 Declare PtrSafe Sub GetLocalTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
004 Declare PtrSafe Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As SECURITY_ATTRIBUTES, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As LongPtr) As LongPtr
005 Declare PtrSafe Function SetFilePointer Lib "kernel32" (ByVal hFile As LongPtr, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
006 Declare PtrSafe Function WriteFile Lib "kernel32" (ByVal hFile As LongPtr, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As OVERLAPPED) As Long
007 Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long
008
009 Type SYSTEMTIME
010 wYear As Integer
011 wMonth As Integer
012 wDayOfWeek As Integer
013 wDay As Integer
014 wHour As Integer
015 wMinute As Integer
016 wSecond As Integer
017 wMilliseconds As Integer
018 End Type
019
020 Const CS_MCAP = "m8rcvrVBA"
021 Const CS_INISEC = "LogOption"
022 Const CS_INIKEY = "LogOut"
023 Const CS_ON = "ON"
024 Const CS_OFF = "OFF"
025 Const CS_SPACE = " "
026
027 Public Sub LogOut(str As String)
028
029 ' ローカル時間を取得
030 Dim SysTime As SYSTEMTIME
031 Call GetLocalTime(SysTime)
032
033 ' INIファイル・LOGファイルのフルパスをシートのフルパスから作成
034 Dim strIn As String
035 Dim strIniFileName As String
036 strIniFileName = ThisWorkbook.Path + "\" + CS_MCAP + ".ini" "
037
038 ' INIファイルのLog出力フラグがONか確認
039 ' INIファイルが存在しない場合、CS_OFFが戻るので、処理中断
040 ' LOG出力なので、エラー処理は行わず、単純に処理終了
041 Dim lRc As Long
042 strIn = CS_SPACE
043 lRc = GetPrivateProfileString(CS_INISEC, CS_INIKEY, CS_OFF, strIn, Len(strIn), strIniFileName)
044 strIn = Left(strIn, lRc)
045 If strIn <> CS_ON Then
046 Exit Sub
047 End If
048
049 Dim strLogFileName As String
050 strLogFileName = ThisWorkbook.Path + "\" + CS_MCAP + ".log" "
051
052 ' LOGファイルを作成(存在しなければ新規作成する)
053 Dim lpHandle As LongPtr
054 Dim SecAttr As SECURITY_ATTRIBUTES
055 lpHandle = CreateFile(strLogFileName, GENERIC_READ Or GENERIC_WRITE, 0, SecAttr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0)
056 If lpHandle = INVALID_HANDLE_VALUE Then
057 Exit Sub
058 End If
059
060 ' 日付時間と与えられた文字列を編集
061 Dim strOut As String
062 strOut = Format(SysTime.wYear, "0000") & "/" & Format(SysTime.wMonth, "00") & "/" & Format(SysTime.wDay, "00") & " " _
063 & Format(SysTime.wHour, "00") & ":" & Format(SysTime.wMinute, "00") & ":" & Format(SysTime.wSecond, "00") & " ... " & str
064
065 ' LOGファイルの終端に移動
066 lRc = SetFilePointer(lpHandle, 0, 0, FILE_END)
067 If lRc = INVALID_SET_FILE_POINTER Then
068 Exit Sub
069 End If
070
071 ' LOGファイル書き出し&終了処理
072 Dim lRet As Long
073 Dim OvLap As OVERLAPPED
074 Dim lWk
075 lWk = Len(strOut)
076 lRc = WriteFile(lpHandle, strOut, Len(strOut), lRet, OvLap)
077 lRc = CloseHandle(lpHandle)
078
079 End Sub
で、ちゃんと動くように直した奴が、下記です。
001 Declare PtrSafe Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long
002 Declare PtrSafe Sub GetLocalTime Lib "kernel32" (lpSystemTime As SYSTEMTIME)
003
004 Type SYSTEMTIME
005 wYear As Integer
006 wMonth As Integer
007 wDayOfWeek As Integer
008 wDay As Integer
009 wHour As Integer
010 wMinute As Integer
011 wSecond As Integer
012 wMilliseconds As Integer
013 End Type
014
015 Const CS_MCAP = "m8rcvrVBA"
016 Const CS_INISEC = "LogOption"
017 Const CS_INIKEY = "LogOut"
018 Const CS_ON = "ON"
019 Const CS_OFF = "OFF"
020 Const CS_SPACE = " "
021
022 Public Sub LogOut(str As String)
023
024 ' ローカル時間を取得
025 Dim SysTime As SYSTEMTIME
026 Call GetLocalTime(SysTime)
027
028 ' INIファイル・LOGファイルのフルパスをシートのフルパスから作成
029 Dim strIniFileName As String
030 strIniFileName = ThisWorkbook.Path + "\" + CS_MCAP + ".ini" "
031
032 ' INIファイルのLog出力フラグがONか確認
033 ' INIファイルが存在しない場合、CS_OFFが戻るので、処理中断
034 ' LOG出力なので、エラー処理は行わず、単純に処理終了
035 Dim strIn As String
036 strIn = CS_SPACE
037 lRc = GetPrivateProfileString(CS_INISEC, CS_INIKEY, CS_OFF, strIn, Len(strIn), strIniFileName)
038 strIn = Left(strIn, lRc)
039 If strIn <> CS_ON Then
040 Exit Sub
041 End If
042
043 Dim strLogFileName As String
044 strLogFileName = ThisWorkbook.Path + "\" + CS_MCAP + ".log" "
045
046 On Error GoTo ERR_HANDLER
047
048 ' LOGファイルを作成(存在しなければ新規作成する)
049 Open strLogFileName For Append As #1
050
051 ' 日付時間と与えられた文字列を編集
052 Dim strOut As String
053 strOut = Format(SysTime.wYear, "0000") & "/" & Format(SysTime.wMonth, "00") & "/" & Format(S ysTime.wDay, "00") & " " _
054 & Format(SysTime.wHour, "00") & ":" & Format(SysTime.wMinute, "00") & ":" & Format(SysTime.wSecond, "00") & " ... " & str
055
056 ' LOGファイル書き出し&終了処理
057 Print #1, strOut
058 Close #1
059
060 ERR_HANDLER:
061
062 End Sub
ログ出力部分を、Win32APIの替わりに、「Open」「Print」「Close」というVBA本来の関数を使って書き直したコードです。いやあ、楽チンだなあ(苦笑)。
というわけで、初めての挫折を味わいつつ、今回はここまで。
【次回】
Coming Soon
サポートよろしく!