見出し画像

結果を得るにはまず過程を知ろう

野蛮なプログラミング、第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


サポートよろしく!