Google
 



AirparkLab
■プログラミングTIPS Windows API関係


目次に戻る
◆ ドライブのセクタに直接アクセスする
ドライブのセクタに直接アクセスするには,CreateFile( ) と ReadFile( ) および WriteFile( ) を使います。 CreateFile( ) に引数として渡すファイル名(ドライブ名)は,\\.\PhysicalDriveN という記述にします。 ここで, N は,0,1,2 などのシステム内の物理ドライブ番号に相当します。
論理ドライブの場合は,\\.\X: という記述にします。同じく X:は,A,B,C などのパーティション文字です。

以下に,ドライブのセクタに直接アクセスするサンプルコードを記載します。 ここではセクタ数が決めうちですが,1セクタが何バイトかを調べるには, DeviceIoControl( ) で制御コード IOCTL_DISK_GET_DRIVE_GEOMETRY を使うと分かります。 ヘルプを見るなり,ググってみてください。 #define _WIN32_WINNT 0x0501 #include <windows.h> #define DRIVENAME "\\\\.\\PHYSICALDRIVE3" #define SECTORSIZE 512 /* ドライブセクタに直接アクセスする 入力:uiSector アクセスセクタ番号 uiCount アクセスセクタ数 pucBuffer 読み書き用バッファアドレス uiBufferSize バッファサイズ[バイト] iDoRead アクセスフラグ(読み込みの場合TRUE,書き込みの場合FALSE) 出力:正常終了した場合はTRUE,エラーの場合はFALSE */ int DRIVE_SECTOR_IO( unsigned int uiSector, unsigned int uiCount, unsigned char *pucBuffer, unsigned int uiBufferSize, int iDoRead ) { int iRet; HANDLE hFile; DWORD dwRet, dwSize; /* 読み込みの場合 */ if( iDoRead ){ hFile = CreateFile( DRIVENAME, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if( hFile == INVALID_HANDLE_VALUE ) { iRet = FALSE; } else { dwRet = SetFilePointer( hFile, (uiSector * SECTORSIZE), NULL, FILE_BEGIN ); if( dwRet != INVALID_SET_FILE_POINTER ){ iRet = ReadFile( hFile, pucBuffer, (SECTORSIZE * uiCount), &dwSize, NULL ); if( iRet == 0 ){ iRet = FALSE; } } else { iRet = FALSE; } CloseHandle( hFile ); } } /* 書き込みの場合 */ else { hFile = CreateFile( DRIVENAME, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if( hFile == INVALID_HANDLE_VALUE ) { iRet = FALSE; } else { dwRet = SetFilePointer( hFile, (uiSector * SECTORSIZE), NULL, FILE_BEGIN ); if( dwRet != INVALID_SET_FILE_POINTER ){ iRet = WriteFile( hFile, pucBuffer, (SECTORSIZE * uiCount), &dwSize, NULL ); if( iRet == 0 ){ iRet = FALSE; } } else { iRet = FALSE; } CloseHandle( hFile ); } } return iRet; }
目次に戻る

◆ 物理ドライブのサイズを取得する
フォーマットされていない物理ドライブのサイズを取得するには,CreateFile( ) と DeviceIoControl( ) を 使います。CreateFile( ) に引数として渡すファイル名(ドライブ名)は,\\.\PhysicalDriveN という記述にします。 ここで, N は,0,1,2 などのシステム内の物理ドライブ番号に相当します。
論理ドライブの場合は,\\.\X: という記述にします。同じく X:は,A,B,C などのパーティション文字です。

以下に,物理ドライブ3のサイズを取得するサンプルコードを記載します。 #define _WIN32_WINNT 0x0501 #include <windows.h> #define DRIVENAME "\\\\.\\PHYSICALDRIVE3" void xxxx() { HANDLE hFile; GET_LENGTH_INFORMATION tLenInf; DWORD dwSize; /* 物理ドライブのサイズを取得する */ hFile = CreateFile( DRIVENAME, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if( hFile != INVALID_HANDLE_VALUE ){ iRet = DeviceIoControl( hFile, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &tLenInf, sizeof(tLenInf), &dwSize, NULL ); if( iRet == S_OK ){ // tLenInf.Length.QuadPart にサイズが LONGLONG 型で入っています。 } CloseHandle( hFile ); } } 【2009.12.25追記】
ちなみにフォーマットされたドライブの容量や空きを調べるには GetDiskFreeSpaceEx( ) を使います。
引数にフォルダ名(調査対象ドライブであればどこでも可)を指定します。 ULARGE_INTEGER unFreeBytesAvailable; ULARGE_INTEGER unTotalNumberOfBytes; ULARGE_INTEGER unTotalNumberOfFreeBytes; BOOL bRet = GetDiskFreeSpaceEx( p_szFolderName, &unFreeBytesAvailable, &unTotalNumberOfBytes, &unTotalNumberOfFreeBytes ); if( bRet ){ // unFreeBytesAvailable.QuadPart; 呼び出し側が利用できるバイト数 // unTotalNumberOfBytes.QuadPart; ディスク全体のバイト数 // unTotalNumberOfFreeBytes.QuadPart; ディスク全体の空きバイト数 }
目次に戻る

 
◆ ビープ音
「マイコン」の時代は良く使っていたビープ音ですが, Windowsプログラミングでは使ったことがありませんでした。
たまたま他のヘルプを見ていたらサンプルコードにその存在があるのを発見したのでここにおぼえがき。
単純な音は Beep( ) で,システムのWAVサウンドを鳴らす場合は MessageBeep( ) が使えるようです。 詳細はヘルプを参照してください。
目次に戻る

 
◆ 絶対パスと相対パス
絶対パスを相対パスに変換するには,PathRelativePathTo() を使います。 基準となるフォルダまたはファイル szSrcPath に対して,szDestPath の相対パスを szRelPath に格納するには 以下のようにします。 #include <shlwapi.h> char szRelPath[MAX_PATH]; PathRelativePathTo( szRelPath, szSrcPath, FILE_ATTRIBUTE_DIRECTORY, szDestPath, FILE_ATTRIBUTE_NORMAL ); szSrcPath と szDestPath は,ファイルまたはフォルダのどちらでも構わないようです。 ファイルのときは FILE_ATTRIBUTE_NORMAL,フォルダのときは FILE_ATTRIBUTE_DIRECTORY をフラグ指定してください。

なお,コンソールプロジェクトの場合リンク時にエラーがでるかもしれません。 そのときは以下の行を挿入してください。

#pragma comment( lib, "shlwapi.lib")

【2007.01.07追記】
逆に相対パスから絶対パスに変換する関数はないのでしょうか?ないようなので作ってみました。 // 相対パスから絶対パスに変換 // pszPath 絶対パス格納領域 // nBufferLength 絶対パス格納領域のサイズ // pszFrom 基準パス(存在するフォルダまたはファイル名,カレントディレクトリに対する相対でも可) // pszTo 基準パスに対する相対パス(フォルダまたはファイル名,存在しなくても可) // _fullpath()を使用してカレントディレクトリを変えずに絶対パスを取得します。 // 変換に成功した場合TRUE, 失敗した場合はFALSEが戻ります。 BOOL PathAbsolutePathTo( LPTSTR pszPath, DWORD nBufferLength, LPCTSTR pszFrom, LPCTSTR pszTo ) { // エラーチェック if( pszPath == NULL || pszFrom == NULL || pszTo == NULL ){ return FALSE; } // 基準パスの存在確認 CHAR szBasePath[MAX_PATH]; strcpy( szBasePath, pszFrom ); if( szBasePath[strlen(szBasePath)-1] == '\\' ){ szBasePath[strlen(szBasePath)-1] = '\0'; } HANDLE hHandle; WIN32_FIND_DATA fData; hHandle = FindFirstFile( szBasePath, &fData ); if( hHandle == INVALID_HANDLE_VALUE ){ return( FALSE ); } // 基準パスがファイルの場合,フォルダを取得 if( !(fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ){ char szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szFName[_MAX_FNAME], szExt[_MAX_EXT]; _splitpath( szBasePath, szDrive, szDir, szFName, szExt ); sprintf( szBasePath, "%s%s", szDrive, szDir ); } FindClose( hHandle ); // カレントディレクトリを保存 CHAR szCurrentBak[MAX_PATH]; DWORD nRet; nRet = GetCurrentDirectory( MAX_PATH, szCurrentBak ); if( nRet == 0 ){ return FALSE; } // カレントディレクトリを基準パスに移動 nRet = SetCurrentDirectory( szBasePath ); if( nRet == 0 ){ return FALSE; } // 絶対パスを取得 char *p = _fullpath( pszPath, pszTo, nBufferLength ); if( p == NULL ){ return FALSE; } // カレントディレクトリを元に戻す nRet = SetCurrentDirectory( szCurrentBak ); if( nRet == 0 ){ return FALSE; } return TRUE; }
目次に戻る

 
◆ 特殊フォルダへのパス
ウインドウズディレクトリやシステムディレクトリはインストールされている環境や 95系やNT系で異なります。したがって"c:\\windows\\system"などと決め打ちはできません。

このような特殊フォルダへのパスは以下のようにして取得することができます。 char szPath[MAX_PATH]; // 取得するバッファ // カレントディレクトリの取得 GetCurrentDirectory( MAX_PATH, szPath ); // システムディレクトリの取得 GetSystemDirectory( szPath, MAX_PATH ); // ウインドウズディレクトリの取得 GetWindowsDirectory( szPath, MAX_PATH ); // テンポラリディレクトリの取得 (これだけ文字列末に\がつくことに注意) GetTempPath( MAX_PATH, szPath ); // デスクトップディレクトリの取得 #include <winnetwk.h> // MFCの場合、#include "stdafx.h" より後で記述すること #include <shlobj.h> LPMALLOC pMalloc; if( SHGetMalloc( &pMalloc ) == NOERROR ){ ITEMIDLIST *pidl; if( SHGetSpecialFolderLocation( NULL, CSIDL_DESKTOPDIRECTORY, &pidl ) == NOERROR ){ SHGetPathFromIDList( pidl, szPath ); } if( pidl ) pMalloc->Free( pidl ); pMalloc->Release( ); } デスクトップディレクトリなど仮想的なフォルダを取得するにはシェルの提供する関数 SHGetSpecialFolderLocation( )と、シェルが管理するメモリ領域(IMallocインターフェイス)を 使用する必要があります。詳細は同関数のヘルプを参照してください。 この方法でデスクトップ以外にもマイコンピュータフォルダやプリンタフォルダなどに アクセスできます。
目次に戻る

 
◆ 実行ファイルの存在するディレクトリ
カレントディレクトリではなく実行ファイルの存在ディレクトリを調査するには GetModuleFileName( )関数を使います。この関数は実行ファイル名のドライブ名から拡張子まで フルパスで取得できてしまうので、ディレクトリだけ抽出するために_splitpath( )関数を実行します。 // インストールフォルダの取得 char szPath[_MAX_PATH]; GetModuleFileName( NULL, szPath, _MAX_PATH ); // フルパスを分解 char szDrive[_MAX_DRIVE], szDir[_MAX_DIR], szName[_MAX_FNAME], szExt[_MAX_EXT]; _splitpath( szPath, szDrive, szDir, szName, szExt );
目次に戻る

 
◆ ファイルの作成時刻を取得
ファイルの作成時刻や更新時刻を取得するにはFindFirstFile( )を使います。下の例では作成時刻を取得しています。 ファイルの時刻は独自な値なのでローカル時刻にするのと人が理解できる形にする2つの変換が必要です。 WIN32_FIND_DATA stFindData; HANDLE hFile = FindFirstFile( szFileName, &stFindData ); if( hFile != INVALID_HANDLE_VALUE ){ FindClose( hFile ); FILETIME ftFileTimeLocal; FileTimeToLocalFileTime( &stFindData.ftCreationTime, &ftFileTimeLocal ); SYSTEMTIME stFileTime; FileTimeToSystemTime( &ftFileTimeLocal, &stFileTime ); } MFC が使える環境であれば,下記のようにCFileStatusクラスを使っても取得できます。 CFileStatus rStatus; if( CFile::GetStatus( "filename", rStatus ) ){ CTime Date = rStatus.m_mtime; // ファイル最終変更日時 int nSize = rStatus.m_size; // ファイルの論理サイズ } else // ファイルが存在しない
目次に戻る

 
◆ フォルダ(ディレクトリ)の確認と作成
フォルダの存在確認を_chdir( )で行い、エラーがあれば存在しないフォルダということで _mkdir( )を実行してフォルダの新規作成が行えます。以下にサンプルを表示します。 #include <direct.h> CString rDir; rDir = "c:\\tmp"; int nChkDir = _chdir( (LPCSTR)rDir ); if( nChkDir == -1 ) _mkdir( (LPCSTR)rDir ); 上記の例ではディレクトリが存在するときカレントディレクトリが変わってしまうので気を付けてください。
ディレクトリの存在確認だけだったら
ここをご覧ください。

【2006.10.28追記】
フォルダを新規作成する場合,_mkdir( ) や CreateDirectory( ) では新しく作るフォルダが 2つ以上あるとエラーになります。一度に作れるフォルダは1つだけみたいです。 unix の mkdirhier みたいに存在しない親フォルダも含めて一度に作ってくれる関数はないのでしょうか?

ありました! MakeSureDirectoryPathExists( ) です。 #include <imagehlp.h> #pragma comment( linker, "/DEFAULTLIB:imagehlp.lib" ) BOOL bRet; // フォルダ名をフルパスで指定します。末尾は必ず\をつけます。 bRet = MakeSureDirectoryPathExists( "C:\\TEST0\\TEST1\\TEST2\\" ); // ファイル名も与えることができます。ファイルは作成されませんが // ファイルを格納する場所までのフォルダを全て作ってくれます。 bRet = MakeSureDirectoryPathExists( "C:\\TEST0\\TEST3\\TEST4\\TEST.TXT" );
目次に戻る

 
◆ ファイル・フォルダの取り扱い
ファイルやフォルダの削除、移動、コピーおよび名前の変更方法は以下の通りです。 // ファイルのコピー BOOL flag, ret; flag = TRUE; // コピー先に同名ファイルがあればコピーしないで失敗を戻す flag = FALSE; // コピー先に同名ファイルがあれば上書きコピーして成功を戻す BOOL ret = CopyFile( "ExistingFileName", "NewFileName", flag ); // ret == FALSE でエラー // ファイル・フォルダ(サブフォルダ含む)の移動および名前の変更 // この関数で移動と名前変更ができますが // ドライブをまたがるフォルダ移動はできません。 BOOL ret = MoveFile( "ExistingFileName", "NewFileName" ); // ret == FALSE でエラー // ファイルの削除 BOOL ret = DeleteFile( "FileName" ); // ret == FALSE でエラー // フォルダの削除(フォルダが空でないと失敗します) BOOL ret = RemoveDirectory( "PathName" ); // ret == FALSE でエラー フォルダのコピーはシェル関数 SHFileOperation( ) を使います。 エクスプローラが行うファイル処理を実行するAPIで、フラグにより確認ダイアログの 表示なども自動的にしてくれます。下はこれを使ってフォルダのコピー関数を作成してみたものです。 #include <windows.h> // フォルダのコピー // [引数] // char *src; // コピー元フォルダ名 // char *dst; // コピー先フォルダ名 // ※いずれも存在フォルダを指定すること // HWND hwnd; // この関数を実行するウインドウのハンドル BOOL CopyDirectory( char *src, char *dst, HWND hwnd = NULL ) { if( strcmp( src, dst ) == 0 ) return( FALSE ); char szzSrcDir[MAX_PATH], szzDstDir[MAX_PATH]; ZeroMemory( szzSrcDir, MAX_PATH ); ZeroMemory( szzDstDir, MAX_PATH ); strcpy( szzSrcDir, src ); strcpy( szzDstDir, dst ); SHFILEOPSTRUCT FileOp; FILEOP_FLAGS flag = FOF_NOCONFIRMATION|FOF_NOCONFIRMMKDIR|FOF_SILENT; ZeroMemory( &FileOp, sizeof(SHFILEOPSTRUCT) ); FileOp.hwnd = hwnd; // 実行するウインドウのハンドル FileOp.wFunc = FO_COPY; // コピーを指定 FileOp.pFrom = szzSrcDir; // 二つのNULLで終端されたコピー元フォルダ名 FileOp.pTo = szzDstDir; // 二つのNULLで終端されたコピー先フォルダ名 FileOp.fFlags = flag; // ダイアログは表示しない int ret = SHFileOperation( &FileOp ); if( ret ) return( FALSE ); else return( TRUE ); } SHFileOperation( )関数はコピーの他にも削除、移動、リネームができます。
削除の場合は pFrom メンバに削除したいファイル名を指定します( pTo は無視されます)。 またフラグに FOF_ALLOWUNDO を指定して削除するとゴミ箱に入ります。
ダイアログを表示させるなど詳細は SHFILEOPSTRUCT のヘルプを参照されたし。

フォルダのコピーは DOSコマンドの xcopy を使用した方が遙かに早いという報告もあります。
ShellExecute( )CreateProcess( )で 実行できます。
xcopy の使い方はコマンドラインで xcopy /? |more とやってみて調べてください。
目次に戻る

 
◆ ファイル・フォルダの検索
ある特定のファイルを検索する関数は以下の通りです。引数にフルパスを指定して、ファイルが 存在すればTRUEを、存在しなければFALSEを戻します。 // ファイルの検索 BOOL FileSearch( char *pFilename ) { HANDLE hnd; WIN32_FIND_DATA data; hnd = FindFirstFile( pFilename, &data ); if( hnd != INVALID_HANDLE_VALUE ){ FindClose( hnd ); return( TRUE ); } else return( FALSE ); } さらに例えば拡張子BMPを持つファイルを複数検索するには以下のように行います。 /* c:\windows\*.bmp を検索(ワイルドカードOK) */ HANDLE hFile; // 検索ハンドル WIN32_FIND_DATA data; // 発見ファイル情報を受ける変数 /* 検索実行 */ hFile = FindFirstFile( "c:\\windows\\*.bmp", &data ); if( hFile != INVALID_HANDLE_VALUE ){ do{ // 見つかった // ファイル名は data.cFileName に書かれている } while( FindNextFile( hFile, &data ) ); // 次のファイルを検索 FindClose( hFile ); // ハンドルの後始末 } サブフォルダ以下も探すには,再帰的に検索を行う必要があります。

ここに ファイルやフォルダ関連の便利な関数を用意してみました。 以下のような関数があります。 フォルダの検索 BOOL FolderSearch( char *pFolder ); ファイルの検索 BOOL FileSearch( char *pFilename ); ファイルの再帰的検索 BOOL RecursiveSearchFile( char *filename, char *dir, char *fullpath ); フォルダ名の取得 BOOL GetFolderName( char *pPath, char *pFolderName ); ファイル名のみの取得 char* GetElementName( char *pPath ); 末尾の'\'を削除 BOOL RemoveTailSeparater( char *pPath );
目次に戻る

 
◆ 画像ファイルのロードと描画
ビットマップファイルを単純に描画したいだけなら以下のようすればできます。 BYTE *pFileImage = ビットマップのファイルイメージ; BITMAPFILEHEADER *pBMFileHeader = (BITMAPFILEHEADER *)pFileImage; if( pBMFileHeader->bfType == *((WORD *)"BM") ){ BITMAPINFOHEADER *pBMInfoHeader = (BITMAPINFOHEADER *)(pFileImage + sizeof(BITMAPFILEHEADER)); BYTE *pBMData = (BYTE *)(pFileImage + pBMFileHeader->bfOffBits); ::StretchDIBits( pDC->GetSafeHdc(), 0, 0, pBMInfoHeader->biWidth, pBMInfoHeader->biHeight, 0, 0, pBMInfoHeader->biWidth, pBMInfoHeader->biHeight, pBMData, (BITMAPINFO *)pBMInfoHeader, DIB_RGB_COLORS, SRCCOPY ); } ::StretchDIBits() は JPEG や PNG ファイルも描画できるようです。詳細はヘルプを参照してください。

ビットマップファイルやsusieプラグインに対応した画像ファイルのロードと描画を行うクラスを作成してみました (初期のプログラムなのでちょっと恥ずかしいです)。

ロードクラス描画クラスを参照してみてください。
コメントに使い方を記述してありあます。
目次に戻る

 
◆ GetLastErrorとFormatMessage
Windows API関数を実行したときの詳細なエラーコードは GetLastError( ) 関数で取得できます。 このコードが何を示すかは C:\MSDEV\INCLUDE\WINERROR.H を調べると大体分かりますが, 以下のように FormatMessage( ) 関数を使うと日本語でエラーメッセージを取得することができます。 DWORD dwErr = GetLastError(); char szMsgBuff[256]; FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), szMsgBuff, 256, NULL );
目次に戻る

 
◆ お手軽なロックの防止
サブスレッドを使うまでもないけど,処理が長くてウインドウが無反応になるのを防ぐには 以下のコードを処理の中に入れるといいです。 // ウインドウメッセージの処理(大量データを処理したときのロック防止) MSG msg; if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ){ TranslateMessage( &msg ); DispatchMessage( &msg ); } ただ上記の方法だとウインドウを閉じるなどのボタンまで効いてしまうので,処理の最中に 操作されて都合の悪いコントロールなどはあらかじめ無効にしておくのが無難です。 メッセージの内容を見てDispatchするかどうか判断してもいいかも。
目次に戻る

 
◆ IMEの無効化
エディットコントロールなどにメールアドレスを入力するようなとき, 自動的に日本語入力を禁止にしてあげると使い勝手がいいかもしれません。

IMEを無効にしたり有効にしたりするには ImmAssociateContext( ) 関数を使用します。 以下に使用例を示します。クラスウィザードで日本語入力を禁止にするコントロールのWM_SETFOCUSと WM_KILLFOCUSにハンドラを実装して以下の様にコーディングします。 #include "imm.h" #pragma comment( linker, "/DEFAULTLIB:imm32.lib" ) // メンバ変数 HWND m_hWndFocused; HIMC m_hIMC; // IMEを無効にするコントロールのWM_SETFOCUSハンドラ void C????Dlg::OnSetfocusNoIME( ) { // IMEを無効にする m_hWndFocused = GetFocus( )->GetSafeHwnd( ); if( m_hWndFocused ) m_hIMC = ImmAssociateContext( m_hWndFocused, NULL ); } // IMEを無効にするコントロールのWM_KILLFOCUSハンドラ void C????Dlg::OnKillfocusNoIME( ) { // IMEを有効にする if( m_hWndFocused ) ImmAssociateContext( m_hWndFocused, m_hIMC ); }
目次に戻る

 
◆ MAPIでメールの送信
MAPIとは Messaging Application Programming Interface の略称で,これに対応した別のメールクライアント アプリケーションを経由してメールに関する機能を使用できるようになる関数群です。ネットワークやメール送信 プロトコルの知識がなくてもメールに関するアプリケーションを作成することができます。多くのメール増殖型 コンピュータウイルスがMAPIを悪用しているのもインタフェースが簡単だからです。

どのメールクライアントを利用するかはインターネットエクスプローラから, ツール − インターネットオプション − プログラム − 電子メール の設定を行うことで選択が可能です。

下にメール送信のサンプルプログラムを掲載します。 #include "mapi.h" typedef ULONG (WINAPI *MAPI_SEND_MAIL_PROC)( LHANDLE lhSession,\ ULONG ulUIParam, lpMapiMessage lpMessage, FLAGS flFlags, ULONG ulReserved ); // 送信元情報 MapiRecipDesc Sender; Sender.ulReserved = 0; Sender.ulRecipClass = MAPI_ORIG; Sender.lpszName = "送信元メールアドレス"; Sender.lpszAddress = NULL; Sender.ulEIDSize = 0; Sender.lpEntryID = NULL; // 受信先情報(複数の場合は配列にする) MapiRecipDesc Receiver; Receiver.ulReserved = 0; Receiver.ulRecipClass = MAPI_TO; // MAPI_CC, MAPI_BCCもあり Receiver.lpszName = "受信先メールアドレス"; Receiver.lpszAddress = NULL; Receiver.ulEIDSize = 0; Receiver.lpEntryID = NULL; // 添付ファイル情報(複数の場合は配列にする) MapiFileDesc AttachFile; AttachFile.ulReserved = 0; AttachFile.flFlags = 0; AttachFile.nPosition = 0xffffffff; AttachFile.lpszPathName = "添付ファイル名"; AttachFile.lpszFileName = NULL; AttachFile.lpFileType = NULL; // 送信設定 MapiMessage Message; Message.ulReserved = 0; Message.lpszSubject = "件名"; Message.lpszNoteText = "本文"; Message.lpszMessageType = NULL; Message.lpszDateReceived = NULL; Message.lpszConversationID = NULL; Message.flFlags = MAPI_RECEIPT_REQUESTED; Message.lpOriginator = &Sender; // 送信元情報へのポインタ Message.nRecipCount = 1; // 受信先情報の数 Message.lpRecips = &Receiver; // 受信先情報へのポインタ Message.nFileCount = 1; // 添付ファイル情報の数(なければ0) Message.lpFiles = &AttachFile; // 添付ファイル情報へのポインタ(なければNULL) // メールクライアントソフトでダイアログを表示するかのフラグ FLAGS flag = 0; if( ダイアログを表示する場合 ) flag = MAPI_DIALOG; // MAPIルーチンを使用して送信 HINSTANCE hInst; hInst = LoadLibrary( "mapi32.dll" ); if( hInst == NULL ){ AfxMessageBox( "mapi32.dllがロードできませんでした。送信に失敗しました。" ); } else { MAPI_SEND_MAIL_PROC MAPISendMailProc = (MAPI_SEND_MAIL_PROC)GetProcAddress( hInst, "MAPISendMail" ); if( MAPISendMailProc( 0, 0, &Message, flag, 0 ) != SUCCESS_SUCCESS ){ AfxMessageBox( "送信に失敗しました。" ); } else AfxMessageBox( "送信完了しました。" ); FreeLibrary( hInst ); }
目次に戻る

 
◆ フォント一覧
インストールされているフォントの一覧を取得するにはEnumFontFamiliesEx( )というAPI関数を使用します。 Enum〜とかいう関数は何らかの情報を列挙する関数で,以下のようにコールバック関数とセットで使用します。 // フォント列挙コールバック関数 int CALLBACK EnumFontFamExProc( ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntm, int FontType, LPARAM lParam ) { CComboBox *pComboBox = (CComboBox *)lParam; // 日本語TrueTypeFontのみコンボボックスにフォント名を追加 if( FontType == TRUETYPE_FONTTYPE // && lpelfe->elfLogFont.lfPitchAndFamily & FIXED_PITCH // 等幅条件 && lpelfe->elfLogFont.lfCharSet == SHIFTJIS_CHARSET ){ pComboBox->AddString( lpelfe->elfLogFont.lfFaceName ); } // フォントの列挙を継続するには1を,中止するには0を戻す。 return( 1 ); } // フォント一覧を取得する(コンボボックスを初期化するときなど) HDC hdc = ::GetDC( this->GetSafeHwnd( ) ); LOGFONT LogFont; ZeroMemory( &LogFont, sizeof(LOGFONT) ); LogFont.lfCharSet = DEFAULT_CHARSET; // ここではコンボボックスに一覧をセットするために // そのアドレスをパラメータとして渡している。 EnumFontFamiliesEx( hdc, &LogFont, (FONTENUMPROC)EnumFontFamExProc, (LPARAM)&m_ComboBox, 0 ); ::ReleaseDC( this->GetSafeHwnd( ), hdc ); // MS ゴシックをデフォルトで選択 m_ComboTTF.SelectString( -1, "MS ゴシック" ); なお,フォントを選択する場合にはCFontDialogというコモンダイアログを利用する方法もあります。
目次に戻る

 
◆ IE拡張メニュー
インターネットエクスプローラはレジストリへの登録によってコンテキストメニュー (右クリックメニュー)の項目追加が行えます。

以下のようにレジストリを登録してインターネットエクスプローラ上に表示された 画像を右クリックすると、「画像を処理...」というメニュー項目がコンテキストメニュー に追加され、その項目を実行するとスクリプト c:\hoge\script.htm が実行されます。 HKEY_CURRENT_USER +----Software +----Microsoft +----Internet Explorer +----MenuExt +----画像を処理... (標準) "file://c:\hoge\script.htm" contexts 0x00000002 Flags 0x00000000 このIE拡張メニューを簡単に登録・削除する関数を作ってみました。
これです。
目次に戻る

 
◆ インターネット上のファイルを取得する
インターネット関連のプログラムは winsock API を使ってガリガリ組まないと いけないんだろうなぁ。簡単にアクセスできるようなクラスは VC++6.0 以降でも 買わないとないんだろうなぁ…。と悶々としていたら、wininet API というものが あることを知りました!

これはインターネットエクスプローラ3.0以降をインストールするともれなく付いてくる wininet.dll が提供してくれる API で、HTTP や FTP といったよく使われるプロトコルだけに 対応した関数しかないようですが、それでも十分です。 wininet.dll の詳細は検索サイトなどで検索してご覧ください。

DLL なので VC++4.0 の環境でももちろん使用でき(
DLLのアクセス参照)、 早速インターネット上のファイルをダウンロードする関数を作ってみました。 これです。

まだまだ VC++4.0 でいけるじゃん!!
と思ったら、VC++.NETって Windows98 にはインストールできないのね…。
目次に戻る

 
◆ サブスレッドを作成する
まずはおさらい。
タスクとプロセスという言葉は同じ意味で使用されています。また一つのプロセスは少なくとも一つの実行単位である 「メインスレッド」を持ち,必要に応じてサブスレッドを作成することができます。Windowsはプロセス単位に メモリを割り当て、そのプロセスに属するスレッドを実行します。Windows以外のOSではスレッドという概念が 存在せず,タスク=実行単位というものもあるので私は混乱していました。

さてメインスレッドはCWinAppを作成した時点で自動的に作成されていますが(CWinAppはCWinThreadの派生クラス), サブスレッドの関数は以下のように作成します。 // サブスレッドの作成 UINT SubThreadFunction( LPVOID pParam ) { BOOL *pFlag = (BOOL *)pParam; // 時間のかかる処理loop { // キャンセル要求の確認 if( *pFlag == TRUE ) break; } // サブスレッド終了 return( 0 ); // 戻り値は好きに定義できる } サブスレッドには32ビットの変数を引き渡せます。キャンセルフラグへのアドレスなどを渡すと良いでしょう。
サブスレッドの終了はサブスレッド自身が return( ) します。AfxEndThread( ) を実行する方法も ありますが、実行するとすぐにスレッドが廃棄されてローカルのクラスオブジェクトの デストラクタが実行されずメモリリークすることがあるので注意してください。

あとはメインスレッドのサブスレッドを実行したい場所で AfxBeginThread( (AFX_THREADPROC)SubThreadFunction, (LPVOID)&Flag ); とすればサブスレッドがすぐに作成されて実行されます。
終了を待ったり一時停止させたりなど,より高度なコントロールをしたい場合はCreateThread( )を 使います。AfxBeginThread( )と併せてヘルプを見てください。

これら一連の流れのサンプルソースは
ここです。

注意点としては,MFCの制限でサブスレッドからメインスレッドのCWnd派性クラスを 操作できないらしいです。サブスレッド実行中にメインスレッドの操作をしたい場合は、 サブスレッドからメインスレッドへウインドウメッセージを送信すると良いでしょう。

またサブスレッドで既存ファイルを上書きオープンするか新規ファイルを作成するかしても スレッド終了後に消えてしまう?という不可解な現象に遭遇したことがあります。謎です。
目次に戻る

 
◆ 他のアプリケーションを実行する
他のアプリケーションを実行するには ShellExecute( ) が使えます(
ここ参照)が、 コマンド名とオプションを別々に与えなければならずちょっと面倒くさいので、 コマンドライン文字列をそのまま実行できる CreateProcess( ) がお勧めです。 PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory( &si, sizeof( STARTUPINFO ) ); si.cb = sizeof( STARTUPINFO ); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWNORMAL; char Command[] = "C:\\WINDOWS\\Notepad.exe"; BOOL ret = CreateProcess( NULL, Command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); if( ret == FALSE ) AfxMessageBox( "外部ツールの起動に失敗しました" ); else { if( 終了を待つ場合 ) WaitForSingleObject( pi.hProcess, INFINITE ); // スレッドハンドルとプロセスハンドルの解放 CloseHandle( pi.hThread ); CloseHandle( pi.hProcess ); } なおペイントブラシ(C:\WINDOWS\PBRUSH.EXE)を上記方法で終了を待ってもうまく行きません。 終了しないうちに WaitForSingleObject( ) から戻ってきてしまいます。 これは C:\WINDOWS\PBRUSH.EXE は C:\PROGRAM FILES\ACCESSORIES\MSPAINT.EXE を呼んで すぐに終了しているからです。
目次に戻る

 
◆ ファイルの関連付け
ファイルをダブルクリックすると拡張子に応じたアプリケーションが起動する「関連付け」を アプリケーションから設定するにはレジストリを操作する必要があります。

以下のようにレジストリツリーを構成すると拡張子".aaa"に対するアイコンの設定と 関連付けが行えます。regedit.exe で他の拡張子の場合と比べてみてください。 HKEY_CLASSES_ROOT +----.aaa (標準) "aaa_auto_file" | ~ | +----aaa_auto_file (標準) (値の設定なし) | +----DefaultIcon (標準) "c:\windows\notepad.exe,1" | +----shell (標準) (値の設定なし) | +----open (標準) (値の設定なし) | +----command (標準) "c:\windows\notepad.exe "%1"" レジストリを操作する関数群は以下の通り。 レジストリキーの作成(オープン): RegCreateKeyEx( ) レジストリキーのオープン: RegOpenKeyEx( ) レジストリ項目の読み出し: RegQueryValueEx( ) レジストリ項目の書き込み: RegSetValueEx( ) レジストリ項目の削除: RegDeleteValue( ) レジストリキーのクローズ: RegCloseKey( ) レジストリキーの削除: RegDeleteKey( ) サンプルソースは
ここです。


【レジストリ追加TIPS】

★CRegKey
VC++4.0でレジストリを操作するには上記 RegCreateKeyEx( ) とか RegOpenKeyEx( ) とかを シコシコ呼ばねばならなかったのですが,新しいCRegKey という便利なクラスが追加されていました。ぐはー。

★RegEnumValue
この関数を使うと,ひとつのレジストリキーに所属するレジストリエントリを列挙して,データも参照できます。

★SDI/MDIアプリケーション
MFCアプリケーションウィザードでSDI/MDIアプリケーションを作成した場合, 以下のレジストリがデフォルトで使用されます。

・プロジェクトの保存フォルダにある [Project名].reg に記載されたレジストリ。
・HKEY_CURRENT_USER\Software\アプリケーション ウィザードで生成されたローカル アプリケーション


前者はドキュメントの拡張子に対応したものです。
後者は「最近使ったファイル」が格納されるレジストリで,アプリケーション固有の設定を保存するためにも使用されます。

デフォルトのままでは情けないのでキー名を会社やプロジェクト名に変更してください。 CXXXXApp::InitInstance() 内に TODO: があります。

このキーに各種設定などを保存したり読み出したりするには,以下のようにします。 // 数値の読み書き int n = AfxGetApp()->GetProfileInt( "SETUP", "ID", 0 ); AfxGetApp()->WriteProfileInt( "SETUP", "ID", 1 ); // 文字列の読み書き CString str = AfxGetApp()->GetProfileString( "HISTORY", "LOG", "default" ); AfxGetApp()->WriteProfileString( "HISTORY", "LOG", "test" );
目次に戻る

 
◆ ウインドウをフォアグラウンドへ
ウインドウを最前面に持ってくるのは SetForegroundWindow( ) で可能 …だったのですが、 Windows98以降ではタスクバーが点滅するだけで最前面には来なくなってしまいました。

しかし以下の関数を実装すれば可能となります。 // ウィンドウをフォアグラウンドにする(Win98対応) BOOL SetForegroundWindow98( HWND hWnd ) { DWORD target, foreground; BOOL ret; foreground = GetWindowThreadProcessId( GetForegroundWindow( ), NULL ); target = GetWindowThreadProcessId( hWnd, NULL ); AttachThreadInput( target, foreground, TRUE ); ret = SetForegroundWindow( hWnd ); AttachThreadInput( target, foreground, FALSE ); return( ret ); }
目次に戻る

 
◆ ウインドウメッセージ
ウインドウ(CWnd派生クラス)は全てウインドウメッセージというものを基本に動作しています。 ウインドウの作成、描画、キー入力、マウスの移動、クリック、全てです。 これらのメッセージは WM_???? というように c:\msdev\include\winuser.h で全て定義されています。
MFCが登場する前はこのウインドウメッセージを直接処理してWindowsプログラミングをしていました。

MFCではウインドウメッセージを直接扱わなくてもある程度のプログラミングができますが、 アプリケーション間で情報をやりとりしようとしたり、仮想的なキー入力を発生させようとしたりするには やはりこのウインドウメッセージを使う必要が出てきます。逆に言えば、ウインドウメッセージを使えば ウインドウの全てを制御できると言っても過言ではありません。

ウインドウメッセージを送信する関数は SendMessage( ), または PostMessage( ) です。 受信(通知されたメッセージを元に動作を行う)処理は基本的に WindowProc( ) です。 メッセージの中には上位で処理されて WindowProc( ) には降りてこないものもあるので、 その時は PreTranslateMessage( ) を使えば全てのメッセージの通知を取得することができます。
また親ウインドウに通知したメッセージを処理するには OnChildNotify( ) が使えます。 MFCコントロールの派生クラスをクラスウィザードを使わずに作成した場合、派生クラス側で メッセージを処理する際に使うことになります。
これらの関数は全て CWnd のメンバ関数です。詳細はヘルプなどを参照してください。

さて少しハードですが、以下にダイアログアプリケーション同士で通信するサンプルを示します。

前準備として受信側ダイアログアプリケーションの識別用キーワードをダイアログに 埋め込みます。リソースエディタで受信側のメインダイアログ上にスタティックテキストを 作成します。ID は IDC_USER_DIALOG_KEY でキャプションは USER_DIALOG_KEY とでもして おきます。このキャプションが受信相手を特定するキーワードになるのでできるだけユニークな ものにすると良いです。なおこのスタティックテキストはプロパティで可視オプションを外して 非可視にしておくのと無効オプションを付けて無効にしておくのを忘れなでください。

次に通信メッセージのIDを定義します。システムで用いられているIDと衝突しないように WM_APP+n という値を使用します。 // 送信側ダイアログ CSendDlg.h および受信側ダイアログ CRecvDlg.h // メッセージIDを定義する #define USER_MESSAGE (WM_APP+100) さて今度は実際にメッセージを送信するタイミングでのコーディングです。

まず送信側ダイアログが受信側ダイアログのウインドウハンドルを調査します。 一般的には FindWindow( ) 関数を使用しますが、ダイアログベースのアプリケーションは 検索できないので (FindWindow( )の引数で与えるクラス名としてダイアログクラス名を使うと 検索してくれないようです。かといってタイトルで検索しようにも不定の場合どうしようも ありません。)、ここでは EnumWindows( ) を用います。

EnumWindows( ) は全ての起動中ウインドウを検索し引数で与えられるコールバック関数を実行します。 コールバック関数は検索されたウインドウハンドルを与えられるので、それが期待するウインドウか どうか調査して次のウインドウを再検索するか検索を終了するかを戻り値として返します。 調査では前準備で埋め込んでおいたキーワードが存在するかをチェックします。 // 送信側ダイアログ CSendDlg.cpp // EnumWindows( )関数用コールバック関数のプロトタイプ宣言 BOOL CALLBACK EnumWindowsProcSingle( HWND hWnd, LPARAM param ); // 送信側ダイアログ CSendDlg.cpp // 実際に送信を行うタイミング 〜前略〜 // EnumWindows( )で受信側のウインドウハンドルを検索する BOOL Ret; EnumWindows( EnumWindowsProc, (LPARAM)&Ret ); if( Ret == TRUE ) // 受信側に送信完了; else // 受信側が見つからなかった; 〜後略〜 // 送信側ダイアログ CSendDlg.cpp // ウインドウハンドル検索用コールバック関数 BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM param ) { BOOL *pRet = (BOOL *)param; *pRet = FALSE; // 検索されたウインドウで // キーワードを埋め込まれたオブジェクトがあるか調査する CWnd *pDlg = CWnd::FromHandle( hWnd ); CWnd *pItem = pDlg->GetDlgItem( IDC_USER_DIALOG_KEY ); // オブジェクトがなければ次のウインドウを検索 if( pItem == NULL ) return( TRUE ); // キーワードの確認 CString Key; pItem->GetWindowText( Key ); // 受信ダイアログに埋め込んだものと異なれば次のウインドウを検索 if( Key != "USER_DIALOG_KEY" ) return( TRUE ); // 受信側が識別できたのでメッセージを送信する WPARAM wparam = 通知したい任意の値1; LPARAM lparam = 通知したい任意の値2; SendMessage( hWnd, USER_MESSAGE, wparam, lparam ); *pRet = TRUE; return( FALSE ); // 送信完了したので検索終了 } 受信側では、クラスウィザードでウインドウプロシージャ WindowProc( ) 関数を 実装(オーバーライド)して、メッセージを以下のように受け取ります。 // 受信ダイアログ CRecvDlg.cpp // ウインドウメッセージハンドラ LRESULT CRecvDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { if( message == USER_MESSAGE ){ 知りたい値1 = wParam; 知りたい値2 = lParam; 任意の処理( ); } return CDialog::WindowProc(message, wParam, lParam); } 以上で一連の送信・受信が完了しました。

受信処理が終了するまで送信側の SendMessage( ) 関数は戻ってきません。送信だけして受信処理が 終わらなくても構わない場合は代わりに PostMessage( ) 関数を使うと良いでしょう。 ただしその場合の受信は WindowProc( ) 関数でなく、任意のタイミングで GetMessage( ) 関数 またはPeekMessage( ) 関数を呼び出して能動的に受信しにいかなければなりません。

さらに応用です。アプリケーション間で通信を行う場合、その情報として WPARAM と LPARAM 型の パラメータをやりとりできましたが、もっと大きなメモリの内容を相手に伝えることは できるでしょうか?

LPARAM に送信側ワークエリアのアドレスをセットすればうまくいきそうですが、実際は期待通り 動作してくれません。Win32ではプロセス空間がアプリケーション毎に分離されているので 単純にアドレスをパラメータとして SendMessage( ) しても受信側がそのアドレスを見ると なんだか別物なのです…。

では完全に不可能なのかというとそうではなくて、実は WM_COPYDATA というメッセージが用意されて おり、メモリ内容のコピーを送信することは可能なのです。 // 送信側 char szBuf[MAX_PATH]; // コピーしたいバッファ strcpy( szBuf, "Test" ); // バッファ内容を任意にセット DWORD type = 0; // バッファ内容のタイプを任意に指定 COPYDATASTRUCT cds; cds.dwData = type; // バッファ内容のタイプ cds.cbData = strlen( szBuf ) + 1; // バッファ内容サイズ // (テキストの場合NULL文字も数に入れる) cds.lpData = (PVOID)szBuf; // バッファ先頭アドレス SendMessage( hWnd, WM_COPYDATA, (WPARAM)hWnd, (LPARAM)&cds ); // 受信側ウインドウメッセージハンドラ LRESULT CRecvDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { if( message == WM_COPYDATA ){ COPYDATASTRUCT *pcds = (COPYDATASTRUCT *)lParam; DWORD type = pcds->dwData; DWORD size = pcds->cbData; char *pBuf = (char *)pcds->lpData; // type 別に size 分の pBuf 内容を処理する } return CDialog::WindowProc(message, wParam, lParam); } あとこれは完全に抜け道ですが、ダイアログの識別に使った非可視のスタティックテキストは、 なんと送・受信両側からGet/SetWindowText( )ができるので、ちょっとしたテキストであれば 共有することが可能です。が、正規の使用法でないと思うので保証はしません。

ウインドウメッセージを使えば何でもできると書きましたが、例えばキーが押されていないのに ウインドウにキーが押されたと勘違いさせることができます。 これは、エディットボックスに長い文字列をセットすると右端が見えなくなってしまいますが、 Endキーが押されたかのようにウインドウメッセージを送信して右端を表示させるなど便利に使えます。 GetDlgItem( IDC_EDIT )->SendMessage( WM_KEYDOWN, (WPARAM)VK_END, (LPARAM)0 ); // ※リソースエディタでエディットボックスのスタイルの // 水平オートスクロールを有効にしておく
目次に戻る

 
◆ 二重起動の禁止
アプリケーションが二重に起動しないようにするには既に起動しているという 証を作成するMutexを使用します。Mutexの作成は何らかのキーワード文字列を用いて行います。 アプリケーションの最初で以下の処理を実装してください。 // キーワードとして実行ファイルのパスを使用 char szMutexKey[MAX_PATH]; GetModuleFileName( NULL, szMutexKey, MAX_PATH ); // 作成していないのにMutexをオープンできれば既に起動されている HANDLE hMutex; hMutex = OpenMutex( MUTEX_ALL_ACCESS, FALSE, szMutexKey ); if( hMutex ){ CloseHandle( hMutex ); // 既アプリをアクティブにする処理など // 終了 return FALSE; } // オープンできなければ未作成つまり未起動状態なので // 起動している証としてミューテックスを作成する hMutex = CreateMutex( FALSE, 0, szMutexKey ); // 〜メイン処理開始〜 // 終了処理でミューテックスを開放 ReleaseMutex( hMutex );
目次に戻る

 
◆ DLLを作成する
メッセージボックスを表示するDLLを作成してみます。

ファイル-新規作成-プロジェクトワークスペースでDynamic-Link Libraryを選択します。 ファイル-新規作成-テキストファイルでDLLのソースファイルを作成します。 以下の記述を行います。 #include <windows.h> extern "C" __declspec(dllexport) void DisplayMessage( LPCTSTR lpszMessage ) { ::MessageBox( NULL, lpszMessage, "DLLTEST", MB_OK ); } このファイルをdlltest.cppなどとして保存します。 また右クリックメニューで「プロジェクトへファイルの挿入」を行います。 あとはビルドしてDLLファイルの完成です。
次にこのDLLを実行するプログラムを作成します。

ファイル-新規作成-プロジェクトワークスペースでConsole Applicationを選択します。 ファイル-新規作成-テキストファイルでソースファイルを作成します。 以下の記述を行います。 #include <windows.h> typedef void (*DISPLAYMESSAGE)( LPCTSTR ); void main( void ) { HINSTANCE hDllInstance; DISPLAYMESSAGE DisplayMessage; hDllInstance = LoadLibrary( "..\\dlltest\\debug\\dlltest.dll" ); if( hDllInstance != NULL ){ DisplayMessage = (DISPLAYMESSAGE)GetProcAddress( hDllInstance, "DisplayMessage" ); if( DisplayMessage != NULL ) DisplayMessage( "DLLEXE!!" ); FreeLibrary( hDllInstance ); } } このファイルをdllexe.cppなどとして保存します。 また右クリックメニューで「プロジェクトへファイルの挿入」を行います。 あとはビルドして実行すればDLLの実行が行えます!!

やってみたら結構簡単で感激です!!!! (T_T)

【2008.05.01追記(VC7)】
Visual C++ .NET 2003 (VC7) では,プロジェクトを新規作成するとき選択するテンプレートに なんと Win32 Dynamic-Link Library がありません! MFC DLL はあるのに,何故?

…と思っていたら,Win32 プロジェクトをまず選択してからアプリケーションウィザードの ところで,左側にタブのような「概要」の下に「アプリケーションの設定」があるので, それを選択して DLL に設定すれば良かったんです…。同じ画面で「シンボルのエクスポート」も チェックしておくと,作成されるファイルにサンプルコードが追加されるのでわかりやすいでしょう。
目次に戻る

 
◆ DLLのアクセス
DLLの関数を自分のプログラムから実行するには以下のように行います。 分かってみると結構簡単だった。 /* Susieプラグインの使用例 */ /* ヘッダファイルなどでDLL内の関数定義を宣言 */ typedef int (WINAPI *GetPluginInfo)( int, LPSTR, int ); /* 〜 */ /* DLLのロード */ HINSTANCE hInst; hInst = LoadLibrary( "filname.dll" ); /* 〜 */ /* DLL関数の実行 */ char str[256]; GetPluginInfo proc; proc = (GetPluginInfo)GetProcAddress( hInst, "GetPluginInfo" ); if( proc != NULL ){ proc( 1, str, 256 ); ::MessageBox( NULL, str, NULL, NULL ); } /* 〜 */ /* DLLの開放(忘れずに!!) */ FreeLibrary( hInst );
目次に戻る

 
◆ ログインユーザ名とコンピュータ名の取得
ログインユーザ名の取得は以下のようにすれば行えます。 char szUser[256]; DWORD dwSize = 256; GetUserName( szUser, &dwSize ); また,コンピュータ名も以下のように取得できます。 char szComputer[256]; DWORD dwSize = 256; GetComputerName( szComputer, &dwSize ); 【VC7】
以下のようにするとより詳細な情報を取得できるようです。 #define SECURITY_WIN32 #include <security.h> #pragma comment( linker, "/DEFAULTLIB:secur32.lib" ) char szUser[256]; DWORD dwSize = 256; GetUserNameEx( NameUserPrincipal, szUser, &dwSize ); stdafx.h で以下の定義を変更 #define _WIN32_WINNT 0x0400 → 0x0500 char szComputer[256]; DWORD dwSize = 256; GetComputerNameEx( ComputerNameDnsFullyQualified, szComputer, &dwSize );
目次に戻る

 
◆ アプリ終了方法
任意のタイミングで自分自身を終了させたい場合は以下のようにします。 this->SendMessage( WM_CLOSE, (WPARAM)NULL, (LPARAM)NULL ); また自分以外の他のアプリを終了させたい場合は ::SendMessage( hWnd, WM_CLOSE, (WPARAM)NULL, (LPARAM)NULL ); 対象となるウインドウのハンドラを FindWindow( ) や EnumWindow( ) で 探しておく必要があります。EnumWindow( ) の使用例は
ここにあります。
目次に戻る

 
◆ メモリリークの調査法
自作アプリケーションを作成していて一番心配なのはメモリリークです。
MFCを使用してデバグ実行を終了させると、メモリリークがあれば以下のように表示されます。 Detected memory leaks! Dumping objects -> {35} normal block at 0x007408C0, 630 bytes long. Data: <<!DOCTYPE html p> 3C 21 44 4F 43 54 59 50 45 20 68 74 6D 6C 20 70 Object dump complete. 上記の{35}というのがメモリ割り当て番号なので、この割り当てが行われた時に ブレークできれば具体的にどのメモリがリークしたか分かります。 ブレークポイントを設定するには、割り当てが行われるよりも前の位置(プログラム最初の方であれば どこでも良いです)で、 _CrtSetBreakAlloc( 35 ); と一行入れておくだけで可能です。
そうしてデバグ実行させるとリークするメモリが割り当てられたときにブレークするので、 表示メニューのコールスタックウインドウを使ってメモリの割り当てに至るまでの流れを 追うことができます。

(VC7)
デバグ実行してメモリリークが発生した場合,その旨を出力ウインドウに表示してくれるのですが, そこにソースファイル名があればダブルクリックしてみてください。なんとそのメモリを確保した 場所にジャンプしてくれます。これはスゴイ!!


目次に戻る

 
◆ INIファイル
Microsoftは設定値などの保存をレジストリに行うよう指示していますが個人的にはなんかイヤです。 レジストリの量が増えることも理由の一つですが、再インストール時や他のPCに環境をコピーする 時に、アプリケーションの設定値がアプリのインストールフォルダにINIファイルとして存在すれば そのフォルダごとコピーするだけで復帰が済むからです。そのアプリが使用しているレジストリを 調査してレジストリを書き出して…なんてやってられません。 アンインストールの時も使用しているフォルダを丸ごと削除で簡単です。

というわけで設定値をレジストリでなく「適当なフォルダ」のINIファイルに保存するには, 以下のようにすれば簡単にできます。構文解析しなくて済むので楽ですね! /* 必要な変数の宣言 */ char dir[256], inifile[256], buf[256]; /* INIファイル名の調査 */ GetCurrentDirectory( MAX_PATH, dir ); wsprintf( inifile, "%s%s", dir, "\\setup.ini" ); /* INIファイルから設定の取得 */ GetPrivateProfileString( "APPNAME", "KEY", "128", buf, 256, inifile ); /* 設定値の反映 */ m_EditBox.SetWindowText( buf ); /* 〜 */ /* 設定値の取得 */ m_EditBox.GetWindowText( buf, 256 ); /* INIファイルへの保存 */ WritePrivateProfileString( "APPNAME", "KEY", buf, inifile ); 構造体データをそのままINIファイルに書き出したり読み込んだりする関数 WritePrivateProfileStruct( )やGetPrivateProfileStruct( )もあるようです。 設定値などを構造体で管理している場合、こちらの方が一発で簡単だけど、 バージョンアップ等で構造体の構造が変わってしまった場合など、以前に 保存した設定が受け継がれなくなってしまうのでプログラマが便利な分、 使用者が不便になってしまいます。

INIファイルを読み書きする同様な関数、AfxGetApp( )->WriteProfileString( ) と AfxGetApp( )->WriteProfileInt( ) は、INIファイルの保存先が c:\windows\アプリ名.ini になってしまいます。また同じく ::WriteProfileString( ) は c:\windows\win.ini 内に データを記述するので、これらを使うくらいならレジストリに保存した方がマシです。
目次に戻る

 
◆ コマンドラインパラメータの取得
コマンドラインパラメータの取得は簡単。
argc, argv が __argc, __argv という形でそのまま使用できます。 サンプルコードは以下です。 #include <stdlib.h> // デフォルト値のセット int nFlag = FALSE; int nType = 0; CString Filename = "c:\\test.dat"; // オプション解析 for( int i=1; i<__argc; i++ ){ // フラグ if( strcmp( __argv[i], "/F" ) == 0 || strcmp( __argv[i], "/f" ) == 0 ){ nFlag = TRUE; } // タイプ else if( strcmp( __argv[i], "/T" ) == 0 || strcmp( __argv[i], "/t" ) == 0 ){ i++; if( i >= __argc ) break; nType = atoi( __argv[i] ); } // ファイル名 else { Filename = __argv[i]; } }
目次に戻る

 
◆ RS232C
MFC App Wizardでプロジェクトを作成した場合,デフォルトでRS232C関連のヘッダファイルが 読み込まれないので(コンパイル時間を短縮するためだと思う),プロジェクト内のstdafx.hで 定義されている#define VC_EXTRALEANをコメントアウトする。 /* 必要な変数 */ HANDLE mHdl; DCB mDCB; COMMTIMEOUTS mTimeout; COMSTAT mStat; unsigned long mErrors; unsigned char RecvBuffer[512]; /* 初期化 */ mHdl = CreateFile( "COM1", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); GetCommState( mHdl, &mDCB ); BuildCommDCB( "baud=9600 parity=N data=8 stop=1", &mDCB ); mDCB.fRtsControl = RTS_CONTROL_DISABLE; mDCB.fOutxDsrFlow = FALSE; mDCB.fDsrSensitivity = FALSE; mDCB.fAbortOnError = FALSE; SetCommState( mHdl, &mDCB ); GetCommTimeouts( mHdl, &mTimeout ); mTimeout.ReadIntervalTimeout = MAXDWORD; mTimeout.ReadTotalTimeoutMultiplier = 1; mTimeout.ReadTotalTimeoutConstant = 500; mTimeout.WriteTotalTimeoutMultiplier = 1; mTimeout.WriteTotalTimeoutConstant = 500; SetCommTimeouts( mHdl, &mTimeout ); /* 送信例 (TESTという文字列と復帰改行コードを送信する)*/ unsigned long n; WriteFile( mHdl, "TEST\n\r", 6, &n, NULL ); Sleep( 1000 ); /* 1000ms sleep */ /* 受信例 (100ms毎に受信バッファを調査し,128バイト分読み込んだら終了) */ long i; i = 0; for( ; ; ){ ClearCommError( mHdl, &mErrors, &mStat ); if( mStat.cbInQue > 0 ){ ReadFile( mHdl, RecvBuffer, mStat.cbInQue, &n, NULL ); RecvBuffer[mStat.cbInQue] = 0; printf( "%s", RecvBuffer ); i += mStat.cbInQue; if( i > 128 ) break; } Sleep( 100 ); } /* 終了処理 */ CloseHandle( mHdl );

[追加情報] Microsoft Comm Control というコンポーネントを挿入することで, 簡単にRS232C制御が可能という情報あり!

…だったが,どこにも資料が見つからないので RS232C制御するクラスを自分で作ってしまいました。
これ です。 といっても上記のコードをラッピングしただけですけど。

【2010.04.27追記】
CreateFile( "COM1", ... ) でオープンできるのは COM9 までのようで,COM10 以上のシリアルポートを指定するためには, CreateFile( "\\\\.\\COM10", ... )とする必要があります(ソース)。 ちなみに,この記述方法はCOM1〜COM9でも適用できます。
目次に戻る

 
◆ ウェブページを表示する
あるボタンを押すと、いわゆるインターネットブラウザが起動して作者のホームページ を表示する。こうするとできます。 ShellExecute( NULL, NULL, "http://www.hoge.ne.jp", NULL, NULL, SW_SHOWNORMAL ); またローカルディスクにあるhtmlで書かれたヘルプファイルや、テキストファイルや その多諸々、関連づけられたファイルを開くにもこの方法が使えます。上記のURLの 部分をフルパスのファイル名にすればOKです。 以下の例は、プログラムがインストールされたディレクトリにある、manual.htmlという ファイルを開く例です。 // プログラムを実行したディレクトリを取得 char pszCurDir[256]; GetCurrentDirectory( 255L, pszCurDir ); char manual[256]; // ファイル名構築用バッファ sprintf( manual, "file://%s\\%s", pszCurDir, "manual.html" ); // ファイル名を指定して実行 ShellExecute( NULL, NULL, manual, NULL, NULL, SW_SHOWNORMAL ); ただしこの方法は、関連づけがユーザによっているので、必ずしもhtmlファイルなら ネットスケープが立ち上がるとは限りません。イソターネットエクスプローラかも しれないし、メモ帳かもしれません…。
またここでファイル名でなくプログラム名がフルパスで記述されると、 そのプログラムが起動します。便利ですねー(笑)。 エクスプローラの起動例 ShellExecute( NULL, NULL, "explorer.exe", "/e,c:\\windows", "c:\\windows", SW_SHOWNORMAL ); なお指定のファイルが無いなど正常に実行できなかった場合は ShellExecute( ) の戻り値が32以下だそうです。
目次に戻る






















©Yutaka Wada(ana53), AirparkLab ALL RIGHTS RESERVED.