「ほっ」と。キャンペーン
カテゴリ:C/C++開発( 16 )
VisualStudio 2010 のC++で作ったバイナリはWindows2000では動かない
今更、、という感じですが、VisualStudio 2010 C++(VC10) で作成したバイナリはWindows2000では動かないようです。
動かそうとすると、「xxxx.exeは有効なWin32アプリケーションではありません。」と怒られます。

有名な話らしいですが、C++系アプリを殆ど作らない3流PGには寝耳に水でした。

なので、社内とかでまだ 2000 を使っていて、ネイティブアプリを作る場合は、VistualStudio 2008 以前で作らないといけないようです。


なお、VS2008(VC9)の時は 98/ME/NT が切り捨てられたようです。

詳しくは、Visual Studio 2010 の試用レポートの表が参考になります。


幸いVS2008 ExpressEditionの環境が手元にあったので助かりました。古い開発環境も残しておかないといけないもんですね。。。


参考:
VisualStudio2010 と Windows2000 - プログラミングメモ
VC++2010を使ってWindows 2000対応ソフトをコンパイルする方法 - Windows 2000 Blog
Windows 2000 Sotaの雑記/ウェブリブログ
[PR]
by Jehoshaphat | 2012-10-22 01:18 | C/C++開発 | Trackback | Comments(0)
(C++)文字列結合のwcscat関数でハマった
当初、下記のようにしてたらアクセス違反の例外となりました。

TCHAR path[] = _T("d:\\a.txt");
 
TCHAR file[1024] = _T("d:\\a.txt");
wcscat( _T("notepad.exe "), file );

で、よく考えたら、wcscat は第一引数に、第二引数の文字列を追加するんですよね。
よって、第一引数には、追加後の文字数以上の大きさ無いといけません。

ボケミスでした。
[PR]
by jehoshaphat | 2010-09-12 23:21 | C/C++開発 | Trackback | Comments(0)
(C++)ファイルパスからフォルダパスを取得する
(VC++)自身のアプリケーションファイルのexeファイルのパスを取得する方法で、Windows API の GetModuleFileName 関数を使うと自身のフォルダパスを取得することができることを書きました。

で、今回はファイル名を含むフルパスから、フォルダパスを取得する方法です。
普通に文字列操作でやってもいいんですが、PathRemoveFileSpec というAPIがあるようなので、それを使ってみました。

PathRemoveFileSpec 関数を使うには、shlwapi.h のインクルードと shlwapi.lib とのリンクが必要なようです。
これは Visual Studio 2008 の場合、プロジェクトのプロパティ → 構成プロパティ → リンカ → 入力 → 追加の依存ファイル で shlwapi.lib と入力すればいいようです。このリンクの方法を探すのに時間がかかりましたw

#include <shlwapi.h>
 
//このexeのパス取得
DWORD dwRet;
TCHAR path[1024];
dwRet = GetModuleFileName(NULL, path, sizeof(path));
if(dwRet == 0) {
//エラー処理など(省略)
}
//exeのパスからファイル名を除去し、ディレクトリ取得
BOOL ret = PathRemoveFileSpec(path);

ただ、IEが無いとこの関数は使えないようです。

PathRemoveFileSpec
[Win32 API] ファイルのパスからディレクトリのパスを取得 - かえでのWebログ
[PR]
by jehoshaphat | 2010-09-02 23:33 | C/C++開発 | Trackback | Comments(2)
(C++)現在のログインしてるユーザ名を取得する
現在ログインしている Windows のユーザ名を取得する方法です。

Windows API の GetUserName関数 を使えばいいようです。

使い方はこんな感じです。
//現在のユーザ名取得
TCHAR user[1024];
DWORD dwUserSize; // 取得したユーザ名の文字列の長さ
if ( ! GetUserName(user,&dwUserSize) ){
return -1;
}
MessageBox(NULL,user,_T("ユーザ名"),MB_OK);
return 0;

user 配列にユーザ名が入ります。
[PR]
by jehoshaphat | 2010-09-02 23:32 | C/C++開発 | Trackback | Comments(0)
(C/C++)Windows APIのCreateProcessWithLogonW関数を使うとEXEが落ちる

(C++)別ユーザでプロセスを起動するにて、Windows API の CreateProcessWithLogonW 関数を使うことで別ユーザ権限でプロセスを起動できることを書きました。

で、このコードをコンソールアプリケーションとしてビルドしテストしたところ、特定のマシンだけ動かないという現象が起きたのです。(Visual C++ 2008 Express にて、Win32 としてビルド)
exeを起動した瞬間にコマンドプロンプト画面は表示されるものの一瞬で消えるという現象です。試しに、コードの最初の方に MessageBox も入れてみましたが、それも表示されません。どうやらexe起動時にほんとに落ちてるようです。

この現象が起きるのが Windows Server 2003 x64 版で5台のサーバのうち3台で発生します。
Windows Server 2003 の x86 環境ではおきません。

試しに Win32 コンソールアプリで、printf だけ使ったexeを動かしたところ、それは問題なく動きます。
ダメになるのは、CreateProcessWithLogonW 関数をコードの含めたときです。

MSサポート:32 ビット プロセスで CreateProcess 関数を呼び出しても別の 32 ビット プロセスが正しく起動されないによると、32bitアプリから、CreateProcess系の関数を動かすと、メモリリークのため開始できないとあります。
しかし、メモリリークしてる感じはないんですよね。exe立ち上げ瞬間落ちてますから。。

で、VC++のプロジェクト構成をいろいろ触っているうちに、構成プロパティ → C/C++ → 最適化 → 最適化 の値を変更することで動かなかった環境でも動くようになりました。
デフォルトで「最適化」の値が「実行速度 (/O2)」になってたんですが、これを「無効 (/Od)」もしくは「プログラム サイズ (/O1)」にすると動作するようになりました。

MSDN:リリース ビルド作成時によくある問題MSDN:コードの最適化によると、最適化によりまれに予期しないコードを生成し問題を引き起こすということが書かれてました。

結局詳細な原因がわからなかったのはモヤモヤしますが、最適化の設定の変更で動くようになったので、助かりました。
しかし、やはり C++ は難しいですね。
[PR]
by jehoshaphat | 2010-08-30 21:48 | C/C++開発 | Trackback | Comments(0)
(C++)別ユーザでプロセスを起動する
Windows API を使って別のユーザでプロセスを起動する方法です。
(.Net を使えば、ProcessStartInfo.UserName や ProcessStartInfo.Password 等で簡単にできるのかもしれませんが、.Net Framework が入ってない環境を考慮して C++ と Windows API で作成しました。またコマンドプロンプトで、 RUNAS を使うとできるのですが、パスワードを入力しないといけないので自作することとしました。)


別ユーザでプロセスを起動する Windows API としては、CreateProcessAsUserやCreateProcessWithLogonW、CreateProcessWithTokenW(Vista以降)などがあるようですが、今回は一番簡単そうな、CreateProcessWithLogonW 関数を使ってみました。

下記のような感じで可能です。administrator@hogedomain.local でWSH(VBScript)を実行するコードです。
#include "stdafx.h"
#include <windows.h>
 
 
STARTUPINFO sinfo;
PROCESS_INFORMATION pinfo;
ZeroMemory( &sinfo, sizeof( STARTUPINFO ) );
ZeroMemory( &pinfo, sizeof( PROCESS_INFORMATION ) );
sinfo.cb = sizeof( STARTUPINFO );
 
//別ユーザでプロセス起動
if( ! CreateProcessWithLogonW( _T("administrator"), //ユーザ名
_T("hogedomain.local"), //ドメイン名
_T("passwd"), //パスワード
0, //ログオン オプション LOGON_NETCREDENTIALS_ONLYを指定すると呼び出し元ユーザプロセスで動いてしまいました
NULL, //実行可能モジュール名
_T("cscript.exe \"d:\\a.vbs\""), //コマンドライン文字列
CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP , //作成フラグ
NULL, // 新しい環境ブロック
NULL, // カレントディレクトリの名前
&sinfo,
&pinfo)){
if (GetLastError() == ERROR_ACCESS_DENIED){
MessageBox(NULL, TEXT("アクセスが拒否されました。"), NULL, MB_ICONWARNING);
}
else
{
MessageBox(NULL, TEXT("プロセスの作成に失敗しました。"), NULL, MB_ICONWARNING);
}
return 0;
}
 
CloseHandle(pinfo.hThread);
CloseHandle(pinfo.hProcess);

上記は cscript でWSHを実行するため、コマンドライン文字列に定義しましたが、一般のexeを実行する場合は第5引数にexeのパスを指定するようです。
メモ帳を立ち上げる場合はこうなります。
CreateProcessWithLogonW( _T("administrator"), //ユーザ名
_T("hogedomain.local"), //ドメイン名
_T("passwd"), //パスワード
0, //ログオン オプション LOGON_NETCREDENTIALS_ONLYを指定すると呼び出し元ユーザプロセスで動いてしまいました
_T("notepad.exe"), //実行可能モジュール名
NULL, //コマンドライン文字列
CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP , //作成フラグ
NULL, // 新しい環境ブロック
NULL, // カレントディレクトリの名前
&sinfo,
&pinfo)


参考:
CreateProcessWithLogonW or CreateProcessAsUser - .NET Framework .NetのProcessStartInfo.UserName を使って、プロセスを起動した場合、CreateProcessWithLogonW が呼ばれるみたいです。
別ユーザーのプロセス 非常に参考なりました。
MSDN:CreateProcessWithLogonW
MSDN:別アカウントでプロセスを起動する方法

[PR]
by jehoshaphat | 2010-08-30 21:41 | C/C++開発 | Trackback | Comments(0)
(C++)ファイルの存在チェックを行う
Win32 API でファイル存在チェックを行う方法です。

PathFileExists( ファイル名 )関数を使えばいいようです。
存在すれば true ,しなければ false ( C的には1かそれ以外か) が返るようですね。

参考:
WINDOWS忘却録 PathFileExists -ファイルの存在を調べるAPI-
[MFC/Win32API] ファイル・フォルダの存在チェック - かえでのWebログ
[PR]
by jehoshaphat | 2010-05-23 07:41 | C/C++開発 | Trackback | Comments(0)
(C++)環境変数のパスを展開する
%systemroot% や %programfiles% との環境変数パスをC++で展開する方法です。

Win32 API の ExpandEnvironmentStrings 関数を使えばいいようです。
注意点は %appdata% だと %%appdata%% というように%でエスケープしないといけないという点です。

こんな感じで使います。

WCHAR CheckFileName[1024];
swprintf( CheckFileName , _T("%%APPDATA%%\\test.txt") );
WCHAR CheckFileName2[1024];
ExpandEnvironmentStrings( CheckFileName ,CheckFileName2,sizeof( CheckFileName2 ));

なお、各OSの環境変数パスの一覧はVistaとXPにおけるパス関係のユーザ環境変数の相違。 - 片っ端から忘れていけばいいじゃない。できれいにまとめられています。
[PR]
by jehoshaphat | 2010-05-20 23:49 | C/C++開発 | Trackback | Comments(0)
(C++)Win32APIを使ってテキストファイルを開き1行ずつ読み込もうとおもったけど...
年に1,2回ほどしか触らない C++ は相変わらず苦手な3流プログラマです。。

設定が入ってるテキストファイルを開き中身を変更するアプリを作ってるわけですが、早速ファイルオープンのところで詰まりました。

とりあえずWindows向けアプリということで Win32 API で作ります。
POSIX と Windows でのファイルオープン方法 - IT戦記:を参考にさせていただくと、CreateFileA関数を使うといいようです。
参考:
Win32 API でのファイルアクセス

ただ、CreateFileA 関数を使ってファイルの中身を読むにはReadFile関数を使うわけですが、これがファイルポインタを使ってバイト指定でしか読めないんですよね。

今回は行単位に読み込んで処理をしたいので、C言語の標準関数を使って、fopen() → fgets () → fclose()という流れでやることにしました。
しかし、これらの標準関数の使い方ももう忘れてます。(Cの教科書レベルに書いてる内容なんですがね。。)
ということで、ファイル入出力を参考にさせてもらいました。

なお、C標準関数ではファイルの変更できないので、一旦設定ファイルをコピーしてそれを開き中身変更して保存することにしました。
下記は a.txt の中身に「あいうえお」があれば「かきくけこ」に置換するサンプルです。


WCHAR chrSettingFile[1024];
swprintf( chrSettingFile , _T("C:\\a.txt") );
 
WCHAR chrSettingFile_Bkup[1024];
swprintf( chrSettingFile , _T("C:\\a_bk.txt") );
 
if( PathFileExists( chrSettingFile ) ) {
// CheckFileNameは存在した時の処理
printf("%s" , "ファイルあり");
//バックアップファイルコピー
if ( CopyFile( chrSettingFile , chrSettingFile_Bkup , false) == false){
return 1;
}

//ファイルポインタ(読み込み元)
FILE *fp_r;
if (( fp_r = _wfopen( chrSettingFile_Bkup , _T( "r" ) )) == NULL){
printf("%s" , "オープン失敗_bk");
return 1;
}
 
//ファイルポインタ(書き込み先元)
FILE *fp_w;
if (( fp_w = _wfopen( chrSettingFile , _T( "w" ) )) == NULL){
printf("%s" , "オープン失敗");
return 1;
}
 
WCHAR s[1024];
WCHAR setting[1024];
swprintf( setting , _T( "あいうえお" ) );

//1行読み込み
while ( fgetws( s , 1024 ,fp_r) != NULL){
WCHAR row[1024];
if ( wcsstr( s , setting ) != NULL){
// あいうえお があったら かきくけこ に置換する
swprintf( row , _T("かきくけこ\n") );
}else{
swprintf( row , s );
}
 
int ret = fputws( row , fp_w );
if( ret == EOF ) {
printf( "ファイル出力エラー\n" );
return 1;
}
}
fclose( fp_r );
fclose( fp_w );
}

[PR]
by jehoshaphat | 2010-05-20 23:46 | C/C++開発 | Trackback | Comments(0)
(C++)環境変数となってるパスを展開する
%systemroot% とか %ProgramFiles% とかの環境変数のパスをC++で展開する方法です。

Windowsにはそれ用のAPIが用意されているようなので、それを使えばいいようです。

APIは ExpandEnvironmentStringsを使います。
使い方はこんな感じです。

//MFCでCString使ったとき
CString filePath = _T("%ProgramFiles%\\xxx\\xxx.exe");
char chrPath[MAX_PATH];
CString filePath2 ;
//環境変数を展開
ExpandEnvironmentStrings(filePath,chrPath,sizeof( chrPath ));
filePath2 = chrPath;
 
//C++,Win32(MFC無し)
WCHAR CheckFileName[1024];
swprintf( CheckFileName , _T("%%APPDATA%%\\Common.xcu") );
WCHAR CheckFileName2[1024];
//環境変数を展開
ExpandEnvironmentStrings( CheckFileName ,CheckFileName2,sizeof( CheckFileName2 ));


参考:
環境変数の展開
[PR]
by jehoshaphat | 2010-05-12 23:55 | C/C++開発 | Trackback | Comments(0)