mfc-ml

2005年05月

31

[mfc 50863] Re: [mfc 50862] Re: 自前のアンインストーラ

とっちゃんです。

> すっかり忘れていましたが、上記のようなことをOSを判断して行う
> サンプルを作成していました。
> よろしければ、参考になさってみてください。

> ■ファイルを再起動時に削除 - for Programmers
> http://www.wac-jp.com/programmers/win32/DeleteFileDelayUntilReboot.html

>> 9x系で失敗しているとすると可能性が高いところでは、
>> ショートネームになっていないというのがあるかな。
>> WININIT.INIによる削除処理は16bitコードで行われているので
>> ロングネームではだめというのはついつい忘れてしまいがちです。

> よく覚えていませんが、私のサンプルでもショートファイル名にしてますね。
> あと、WritePrivateProfileString APIを普通に使っちゃうとダメです。
> 1つしかファイルがないときはいいんですが、2回目以降、上書きしちゃうんで。

合ったんですね。ちゃんと探せよ>おれ(^^;
フムフム...
最後の、フラッシュはなくても大丈夫ですよ(おいらの実装には入ってません)。

あとは、似たり寄ったりですね。
大きな違いは、MoveFileEx が実装されているかは成功するかどうかで
判断しているってくらいかなぁ...
失敗してなおかつ非NT系の場合は WININIT.INI で処理って言う形になってます。<おいらの実装

後は、元独自インストーラのルーチンなのでコピー機能も含まれてるかという違いくらいですね(^^;
WININIT.INI への NUL 追加も同じ段取りです(^^;;


ショートネームの問題は、95 時代の書籍でこのことを書いてあるやつに載ってたと記憶素子が言ってます(^^;
まだ、MS-DOSにいる状態で処理しているから、8.3 じゃなきゃだめよ~んと(^^;

>> HDDに限定してループしている場合は、問題ないかもしれませんが、
>> FindNextFile はある一定の条件を満たすと続きがある場合でも FALSE を返してきますので、
>              :
>> としておくとよいでしょう。
>> こうしておけば、途中で止まることなく確実にリストアップできます。
>> これもKBがあるはずです(^^;

> HDD内でしか使ってなかったので、知らなかった。。メモメモ。

私の記憶が確かなら、HDDでは発生しないはずですが、なにせ、XP出た頃だったかその前
だったかという古い話なので(^^;
それも、KB出た当初はあまりはっきりした情報がわかってなくて、なんだかわからんけど
見たいなノリだったし(^^;
確か、最終更新でショートネーム生成ロジックが9文字以上を吐き出すバグが原因って
出ていたような...(でも直せないから、GetLastError()で確認してねって...)。

なにせ、今じゃ、::FindNextFile( ... ) || ::GetLastError() == ERROR_MORE_DATA って書くのが
体に染み込んじゃってますので(^^;
どこなら気にしなくてもいいというのは全く考慮してません(^^;;
31

[mfc 50862] Re: 自前のアンインストーラ


社本@ワックです。

> 失敗するのは9x系ということは無いですか?
> 9x系では、過去の発言にもあるようにWININIT.INI を使って
> 消すようにする必要があります(MoveFileExは常に失敗する)。

すっかり忘れていましたが、上記のようなことをOSを判断して行う
サンプルを作成していました。
よろしければ、参考になさってみてください。

■ファイルを再起動時に削除 - for Programmers
http://www.wac-jp.com/programmers/win32/DeleteFileDelayUntilReboot.html

> 9x系で失敗しているとすると可能性が高いところでは、
> ショートネームになっていないというのがあるかな。
> WININIT.INIによる削除処理は16bitコードで行われているので
> ロングネームではだめというのはついつい忘れてしまいがちです。

よく覚えていませんが、私のサンプルでもショートファイル名にしてますね。
あと、WritePrivateProfileString APIを普通に使っちゃうとダメです。
1つしかファイルがないときはいいんですが、2回目以降、上書きしちゃうんで。

> HDDに限定してループしている場合は、問題ないかもしれませんが、
> FindNextFile はある一定の条件を満たすと続きがある場合でも FALSE を返してきますので、
             :
> としておくとよいでしょう。
> こうしておけば、途中で止まることなく確実にリストアップできます。
> これもKBがあるはずです(^^;

HDD内でしか使ってなかったので、知らなかった。。メモメモ。


// 社本 明弘
// http://www.wac-jp.com/programmers/
// http://www.ailight.jp/blog/sha256/
// Microsoft MVP for VC++ (Oct 2003 - Oct 2005)
31

[mfc 50861] Re: [mfc 50859] Re: 自前のアンインストーラ

とっちゃんです。

> ただ、最終的に何らかの形(EXE、DLL、リソース)でアンイン
> ストーラがtempフォルダへコピーしたモジュールを削除する
> 際に使用させて頂くかもしれません。
> (なんでかMoveFileEx()が失敗するので・・・^^;)

失敗するのは9x系ということは無いですか?
9x系では、過去の発言にもあるようにWININIT.INI を使って
消すようにする必要があります(MoveFileExは常に失敗する)。

NT系の場合の削除は
MoveFileEx( srcpath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT );
でOKなはずです。

もし、失敗する場合は、GetLastError() でエラーコードをチェックしてみると
よいと思います(ほとんどはこれで情報が特定できる)。
NT系で失敗するというと、可能性としては、対象ファイルが無いか
リードオンリーかのどちらかだと思います(リードオンリーは消してしまうかも)。
あとは、権限の問題があるくらいだと思います。

9x系で失敗しているとすると可能性が高いところでは、
ショートネームになっていないというのがあるかな。
WININIT.INIによる削除処理は16bitコードで行われているので
ロングネームではだめというのはついつい忘れてしまいがちです。
ちなみに、削除については何個同じものを消すように書き込んでいても
エラーにはなりません。

> 早速ですがインストール先のアンインストーラがコンパネから
> 起動された際にtempフォルダへコピーするEXEを作成しまし
> た。(コンソールアプリケーション:UnInstallerSub.exe)
> アンインストーラからCreateProcess()でUnInstallerSub.exeを
> 起動します。
> 第二引数でのコマンドラインのパラメータでインストール先(削
> 除先)のパラメータをもらいます。

少なくともWindows2000以降+IME2002以降(OfficeXP添付版およびWindowsXP標準版)の
環境では、コンソールが出てしまうと追加と削除が応答なしになる可能性が格段にあがります。

この現象は追加と削除が、自分の立ち上げたプロセス+そこから作られたプロセスの
終了を監視するという仕様+ConIme の仕様に関する問題からくる回避不能な
現象です(KBにもいくつもの類似情報が出ています)。

OfficeXP が出たばっかりの頃思いっきりはまりました(--;
(当時はバッチファイルでクリーンナップしていた)

GUI でもコマンドライン引数は取得できますので、コンソールではなく
GUI形式で UnInstallerSub.exe を作ってください。
これで、この現象は出なくなります。

> ところがインストール先にはLogフォルダなるものが更にありま
> して、ここには様々なEXEやDLLが同時の名前+日付でログファ
> イルを作成します。
> ディレクトリ以下のファイルを一掃するにはどのようにしたらよ
> ろしいのでしょうか・・・
> DeleteFile()はワイルドカードが使えるのでしょうか・・・^^;
> 使えなかった場合はどうするのでしょうか・・・^^;

このログファイルは削除してもいいのですか?それとも残しておく必要があるのでしょうか?
ま、削除してよいから聞いているのだと思いますので(^^;

任意のフォルダを中身ごと削除する場合は、
FindFirstFile/FineNextFile API でループ&再帰処理でファイル+フォルダを削除する
というのが常套手段です。

社本さんのところにソースがありますので、リンクしておきます。
http://www.wac-jp.com/programmers/win32/RecurseRemoveDirectory.html

HDDに限定してループしている場合は、問題ないかもしれませんが、
FindNextFile はある一定の条件を満たすと続きがある場合でも FALSE を返してきますので、
do{
    ...削除ルーチン
}while( ::FindNextFile( Handle, &wfd ) );

do{
    ...削除ルーチン
}while( ::FindNextFile( Handle, &wfd ) || ::GetLastError() == ERROR_MORE_DATA );

としておくとよいでしょう。
こうしておけば、途中で止まることなく確実にリストアップできます。
これもKBがあるはずです(^^;
31

[mfc 50860] Re: [mfc 50857] Re: [mfc 50856] Re: [mfc 50851] ソケットプログラム

HMMNです。


>
> OnReceive()関数ないでSend()関数を使っています。サーバ側の処理になります。 

> Send()されているかをエラー処理を追加して調べた結果、エラーが
> あり正確に送信されていませんでした。
>
> 色々と調べているのですが「データを送信しても安全である」状態と言うのが
> 分からない状況です。
>

OnReceive()は、データ受信側で利用するメソッドです。
データを受け取っているドライバー(正確には違いますが...)が受信バッファーにデータ 

が届き、取り出せますよ、という状態になったときにコールされるものです。
アプリケーションの目的にもよりますが、一般的にはここでReceive()をコールするタイ
ミングです。

CAvmReceiveSocketを派生させる際に、OnSend()を定義してください。
このメソッドで、Send()を1回だけコールしてみてください。
残りのデータは、OnSend()がコールされたときにSend()を使って渡してください。
これを、データがなくなるまで繰り返せば、データを正しく送出できると思います。

「データを送信しても安全である」というタイミングは、OnSend()がコールされる
タイミングです。また、接続直後で送信のバッファーにデータがないときも、その
タイミングです。

>
> void CAvmReceiveSocket::OnReceive(int nErrorCode)
> {
>
> TCHAR szBuff[SEND_DATA_SIZE];
> TCHAR nkBuff[SEND_DATA_SIZE];
> CString strGetFile;
>
> ZeroMemory(szBuff, sizeof(szBuff));
> int Len = Receive(szBuff, SEND_DATA_SIZE);
> strGetFile = szBuff;
>
> long lTotal = 0;
> int nBytesSent;
> int nError;
>
> if (strGetFile == "Get File"){
>
>  CFile AvmFile;
>  CFile AvmFile2;
>  BOOL bRet = AvmFile.Open("NK.bin", CFile::modeRead|CFile::typeBinary);
>
>  while(UINT nRead = AvmFile.Read(nkBuff, SEND_DATA_SIZE)){
>
>   lTotal += nRead;
>   //Sleep( 500 );
>   nBytesSent = Send(nkBuff, nRead);
>   if (nBytesSent == SOCKET_ERROR){
>    nError = WSAGetLastError();
>   } else {
>
>   }
>
>  }
>   AvmFile.Flush();
>  AvmFile.Close();
> }
>
> CString str;
> str.Format( _T("%ld bytes") , lTotal );
> m_pDlg->m_editReceive.SetWindowText( str );
> delete this;
>
> }
>
31

[mfc 50859] Re: 自前のアンインストーラ


回答者各位。
お世話になっております。 藤沢です。


色々な御意見ありがとうございます。
非常に勉強になります。

HMMN様から提供して頂いた手法も存じてはいたのですが、
やはりフォルダはユーザがアンインストールを実行した時点
で削除したいと言う個人的な願いがあり今回は控えさせて頂
こうかと思っております。
ただ、最終的に何らかの形(EXE、DLL、リソース)でアンイン
ストーラがtempフォルダへコピーしたモジュールを削除する
際に使用させて頂くかもしれません。
(なんでかMoveFileEx()が失敗するので・・・^^;)

社本様から頂いた手法は未知なる領域なので今回は入り口
だけ勉強させて頂こうかと思います。

基本的にはとっちゃん様の手法でいこうかと考えております。
それもまずはもう一つEXEを作成して試してみようかと思いま
す。

早速ですがインストール先のアンインストーラがコンパネから
起動された際にtempフォルダへコピーするEXEを作成しまし
た。(コンソールアプリケーション:UnInstallerSub.exe)
アンインストーラからCreateProcess()でUnInstallerSub.exeを
起動します。
第二引数でのコマンドラインのパラメータでインストール先(削
除先)のパラメータをもらいます。

UnInstallerSub.exeの中で
 TCHAR szCmdLine[MAX_PATH];
 lstrcpyn( szCmdLine, GetCommandLine(), MAX_PATH );
 lstrcpyn( szCmdLine, PathGetArgs( szCmdLine ), MAX_PATH );
だとかしてインストール先(削除先)のフルパスは取得出来て
おります。
(ファイルに書いて確認済み。)

その後RemoveDirectory()でインストール先のディレクトリごと
削除しようとしたのですが、このAPIの仕様によりフォルダの中
身が空でないと失敗するようでした。

ところがインストール先にはLogフォルダなるものが更にありま
して、ここには様々なEXEやDLLが同時の名前+日付でログファ
イルを作成します。
ディレクトリ以下のファイルを一掃するにはどのようにしたらよ
ろしいのでしょうか・・・
DeleteFile()はワイルドカードが使えるのでしょうか・・・^^;
使えなかった場合はどうするのでしょうか・・・^^;


不明な点などありましたらご連絡ください。
よろしくお願い致します。
記事検索
Amazon.co.jp
  • ライブドアブログ