ページ 1 / 1
Win32 プログラミング
Posted: 2017年1月02日(月) 22:16
by 駆け出し
こんばんは。今、自分でWindowsMediaPlayerもどきを作っています。
CunstomDrawを使い、すでに再生されたエリアは青く塗るようにしたいのですが、塗られないままで、そのままです。(画像を添付します。)
どこが間違っているかを、ご教示していただけないでしょうか。(音源は、ネット上に公開されている著作権フリーのものを利用しております) MFP【Marron Fields Production】
こちらのソースは最新のものではありません。
CustomDraw部分
► スポイラーを表示
コード:
#include "strdef.h"
LRESULT CustomDrawProc(HWND hWnd, InternalData indata,WPARAM wp, LPARAM lp)
{
LPNMHDR lpNotify = (LPNMHDR)lp;
LPNMCUSTOMDRAW lpDraw;
HBRUSH brush, oldBrush;
int Pos, max, pos_x;
RECT rc;
switch (lpNotify->code)
{
case NM_CUSTOMDRAW:
if (lpNotify->hwndFrom == indata.hTrack)
{
lpDraw = (LPNMCUSTOMDRAW)lpNotify;
switch (lpDraw->dwDrawStage)
{
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
switch (lpDraw->dwItemSpec)
{
case TBCD_CHANNEL:
//再生が終わった場所は青く塗る
//現在位置の取得
Pos = SendMessage(indata.hTrack, TBM_GETPOS, 0, 0);
//最大数の取得
max = indata.maxSrc;
if (!max)
max++;
//比計算
//max : rc.right-rc.left = pos : x
pos_x = (lpDraw->rc.right - lpDraw->rc.left)*Pos / max;
//とりあえず真っ白に塗り込む
FillRect(lpDraw->hdc, &lpDraw->rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
//左側
rc = lpDraw->rc;
rc.right =(LONG)pos_x;
// rc.left = 0;
brush = CreateSolidBrush(RGB(0, 0, 255));
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, brush);
FillRect(lpDraw->hdc, &rc, brush);
SelectObject(lpDraw->hdc, oldBrush);
DeleteObject(brush);
return CDRF_SKIPDEFAULT;
case TBCD_THUMB:
brush = CreateSolidBrush(RGB(255, 255, 255));
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, brush);
Ellipse(lpDraw->hdc, lpDraw->rc.left, lpDraw->rc.top, lpDraw->rc.right, lpDraw->rc.bottom);
SelectObject(lpDraw->hdc, oldBrush);
DeleteObject(brush);
return CDRF_SKIPDEFAULT;
}
break;
}
break;
}
}
return FORWARD_WM_NOTIFY(hWnd, wp, lp, DefWindowProc);
}
ウィンドウプロシージャ-
► スポイラーを表示
コード:
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
static InternalData indata;
static MCI_PLAY_PARMS play;
static MCI_STATUS_PARMS mciStatus;
static HWND hMCI;
RECT rc;
switch (msg) {
case WM_DESTROY:
mciSendCommandW(indata.open.wDeviceID, MCI_CLOSE, 0, 0);
PostQuitMessage(0);
return 0;
case WM_CREATE:
InitCommonControls();
InitSoundStruct(L"C:\\Games\\Minecraft\\testsound.mp3", L"MPEGVideo", &indata.open);
SetTimer(hwnd, 1, 999, NULL);
play.dwCallback = (DWORD)hwnd;
//トラックバー作成
indata.hTrack = CreateTrackbar(hwnd);
return 0;
case WM_TIMER:
if (indata.Status)
{
DWORD dwSrc, dwMin, dwSrc_all, dwMin_all;
WCHAR Temp[512];
mciStatus.dwItem = MCI_STATUS_POSITION;
mciSendCommandW(indata.open.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
//秒と分を取得(今は使っていない)
dwSrc = mciStatus.dwReturn / 1000; //msrc->src
dwMin = dwSrc / 60;
dwSrc %= 60;
dwSrc_all = indata.maxSrc % 60;
dwMin_all = indata.maxSrc / 60;
swprintf_s(Temp, L"%02d:%02d", dwMin, dwSrc);
SendMessageW(indata.hTrack, TBM_SETPOS, TRUE, mciStatus.dwReturn / 1000);
SetWindowTextW(hwnd, Temp);
}
return 0;
case WM_LBUTTONDOWN:
mciSendCommandW(indata.open.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD_PTR)& play);
//長さを取得
//とりあえず戻り値は'm'srcにする
mciStatus.dwItem = MCI_FORMAT_MILLISECONDS;
mciSendCommandW(indata.open.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&mciStatus);
//局の長さを取得
mciStatus.dwItem = MCI_STATUS_LENGTH;
mciSendCommandW(indata.open.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
indata.Status = TRUE;
indata.maxSrc = mciStatus.dwReturn / 1000;
//トラックバーに関する設定(最後まで再生したときにつまみが一番右側にいるようにする)
SendMessageW(indata.hTrack, TBM_SETRANGE, TRUE, MAKELPARAM(0, indata.maxSrc));
return 0;
case WM_SIZE:
GetClientRect(hwnd, &rc);
MoveWindow(indata.hTrack, 0, rc.bottom - 25, LOWORD(lp), HIWORD(lp), TRUE);
return 0;
case WM_NOTIFY:
return CustomDrawProc(hwnd, indata, wp, lp);
case MM_MCINOTIFY:
if (lp == indata.open.wDeviceID) {
if (wp == MCI_NOTIFY_SUCCESSFUL) {
//シークバーを先頭に戻す
mciSendCommandW(indata.open.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
//再生終了
indata.Status = FALSE;
}
return 0;
}
break;
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
MP3ファイル読み込み部分
► スポイラーを表示
コード:
int InitSoundStruct(LPCWSTR lpFilePath,LPCWSTR lpstrDeviceType,MCI_OPEN_PARMSW *retTemp)
{
MCI_OPEN_PARMSW MPEGStruct;
MCIERROR result;
TCHAR MsgBuf[1024];
ZeroMemory(&MPEGStruct, sizeof(MCI_OPEN_PARMSW));
MPEGStruct.lpstrDeviceType = lpstrDeviceType;
MPEGStruct.lpstrElementName = lpFilePath;
result = mciSendCommandW(
0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD_PTR)&MPEGStruct
);
if (result)
{
mciGetErrorStringW(result, MsgBuf, sizeof(MsgBuf));
OutputDebugStringEx(L"%s\n",MsgBuf);
*retTemp = MPEGStruct;
return
create_temp_file(lpFilePath, lpstrDeviceType, retTemp);
}
*retTemp = MPEGStruct;
return TRUE;
}
//synchsafeを10新数に変換
int unsynchsafe(char size[4])
{
int ToRet = 0;
if ((size[0] & 0x0F) || ((size[0] | size[1] | size[2] | size[3]) & 0x80))
{
}
else {
int Footer = size[0] & 0x10;
ToRet = size[0] << 21;
ToRet += size[1] << 14;
ToRet += size[2] << 7;
ToRet += size[3];
ToRet += 10;
if (Footer)
ToRet += 10;
}
return ToRet;
}
//ID3タグがあるとき、(少なくとも自分の環境では)MCIは読み込んでくれないのでとりあえずtempファイルに書き出し、それを読み込ませる
int create_temp_file(LPCWSTR lpFilePath,LPCWSTR lpstrDeviceType,MCI_OPEN_PARMSW *retTemp)
{
FILE *fp,*wfp;
ID3V2HEADER head; //ID3プロファイルがあるかどうか
DWORD size = 0;
char mp3head[3] = { 0 }, temp[16] = { 0 };
// char _size[32];
_wfopen_s(&fp, lpFilePath, L"rb");
if (!fp)
{
OutputDebugStringEx(
L"%s:ファイル\"%s\"\nの読み込みに失敗しました。\n",
__FUNCSIG__, lpFilePath
);
return 0;
}
ZeroMemory(&head, sizeof(head));
fread(&head, sizeof(head), 1, fp);
//strcmpだと比較できない(\0がないため)
if (head.tag[0] != 'I' ||
head.tag[1] != 'D' ||
head.tag[2] != '3'
)
{
OutputDebugStringW(L"ID3 header not found\n");
fclose(fp);
return 0;
}
//
//
//syncfaceを普通の10進数に変換
size = unsynchsafe(head.size);
//headerはとりあえず読み飛ばす
long file_size;
char *buffer;
//ファイルサイズを取得する
//戦闘に移動
fseek(fp, 0, SEEK_SET);
if (fseek(fp, 0, SEEK_END) != 0) {
/* エラー処理 */
goto err;
}
file_size = ftell(fp);
if (file_size == -1) {
/* エラー処理 */
goto err;
}
buffer = (char*)malloc(file_size - size);
if (buffer == NULL) {
/* エラー処理 */
goto err;
}
//mp3の先頭に移動
fseek(fp, size, SEEK_SET);
//本体読み込み
fread(buffer, file_size - size, 1, fp);
_wfopen_s(&wfp, TEMP_FILE_NAME, L"wb");
if (!wfp)
goto err;
fwrite(buffer, file_size - size, 1, wfp);
fclose(fp);
fclose(wfp);
free(buffer);
{
MCI_OPEN_PARMSW MPEGStruct;
MCIERROR result;
TCHAR MsgBuf[1024];
ZeroMemory(&MPEGStruct, sizeof(MCI_OPEN_PARMSW));
MPEGStruct.lpstrDeviceType = lpstrDeviceType;
MPEGStruct.lpstrElementName = TEMP_FILE_NAME;
result = mciSendCommandW(
0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD_PTR)&MPEGStruct
);
if (result)
{
mciGetErrorStringW(result, MsgBuf, sizeof(MsgBuf));
OutputDebugStringEx(L"%s",MsgBuf);
*retTemp = MPEGStruct;
goto Err;
}
*retTemp = MPEGStruct;
return TRUE;
}
return TRUE;
err:
fclose(fp);
return FALSE;
}
strdef.h
► スポイラーを表示
コード:
include <windows.h>
#include <stdio.h>
#include <string.h>
#include <mmsystem.h>
#include <windows.h>
#include <vfw.h>
#include <commctrl.h>
#include <stdio.h>
#include <assert.h>
#include <tchar.h>
#include <shlwapi.h>
#include <windowsx.h>
#pragma comment( lib, "shlwapi.lib" )
#pragma comment(lib,"msvfw32.lib")
#pragma comment(lib,"winmm.lib")
#pragma comment(lib, "comctl32.lib")
#define TEMP_FILE_NAME L"soundfile.mp3"
typedef struct tagID3V2HEADER
{
char tag[3];
char version[2];
char flags;
char size[4]; //syncface
} ID3V2HEADER;
typedef struct tagInternalData
{
//現在再生しているデータに関するもの
DWORD maxSrc; //現在再生しているものの、長さ(単位:src)
MCI_OPEN_PARMS open;
BOOL Status; //現在再生しているかどうか
//トラックバーコントロール
HWND hTrack;
}InternalData;
int InitSoundStruct(LPCWSTR lpFilePath, LPCWSTR lpstrDeviceType, MCI_OPEN_PARMSW *retTemp);
//ID3ヘッダに関する処理
int create_temp_file(LPCWSTR lpFilePath, LPCWSTR lpstrDeviceType, MCI_OPEN_PARMSW *retTemp);
HWND CreateTrackbar(HWND hWnd);
LRESULT CustomDrawProc(HWND hWnd, InternalData indata , WPARAM wp, LPARAM lp);
//OutputDebugStringWの改良版
int OutputDebugStringEx(LPCWSTR lpOutputString, ...);
Re: Win32 プログラミング
Posted: 2017年1月04日(水) 09:00
by Math
VS2015上で再現してみようと思ったけどこれだけでは出来なさそうなので。”Debug"をされているようですが、switch (lpDraw->dwDrawStage)にブレークポイントを設定して[F11]でステップ実行されたらわかるのではありませんか?
Re: Win32 プログラミング
Posted: 2017年1月05日(木) 01:10
by 駆け出し
こんばんは。Debugをして、いくつか試行錯誤してみたのですが、関数:FillRectの戻り値は常に1なのです...
FillRect関数(MSDN)
また、いくつか試行錯誤した結果、余計に自分でもわからなくなってきてしまいました。(CustomDraw.cppの抜粋を見ていただけると助かります)
デタッチ(?)していないので結果が反映されないよ、ということは読んだのですが、c++かつ無償版のVC2015にはついていない機能を使っていたもので...
また、検証用に現在私が書いたソースコードすべて(ヘッダ、リソース含む)を置いておきます(バグあるかもです...)。
ヘッダファイルとソースファイルのパスを分けておいているので、一部修正しないと検証できないかもしれないです。お手数をおかけしますが、よろしくお願いします。
[追記]
いくつかのファイルにバグがあることと、処理を変更したので、新しい書き込みから、そちらに上がっているファイルはコピペしてください。
Zipを置いておきましたので、そちらからDownloadよろしくお願いします。
また、こちらのコードは最新のものではありません。
res.rc
► スポイラーを表示
コード:
#include "H\rc.h"
#include <windows.h>
SOUNDPLAYER MENU
{
POPUP "ファイル(&F)"
{
MENUITEM "SoundPlayerの終了(&E)", IDM_EXIT
MENUITEM SEPARATOR
MENUITEM "開く(&O)",IDM_OPEN
MENUITEM "再生(&P)",IDM_PLAY
}
}
strdef.h
► スポイラーを表示
コード:
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <mmsystem.h>
#include <windows.h>
#include <vfw.h>
#include <commctrl.h>
#include <stdio.h>
#include <assert.h>
#include <tchar.h>
#include <shlwapi.h>
#include <windowsx.h>
#pragma comment( lib, "shlwapi.lib" )
#pragma comment(lib,"msvfw32.lib")
#pragma comment(lib,"winmm.lib")
#pragma comment(lib, "comctl32.lib")
#define TEMP_FILE_NAME L"soundfile.mp3"
typedef struct tagID3V2HEADER
{
char tag[3];
char version[2];
char flags;
char size[4]; //syncface
} ID3V2HEADER;
typedef struct tagInternalData
{
//現在再生しているデータに関するもの
DWORD maxSrc;
MCI_OPEN_PARMS open;
MCI_PLAY_PARMS play;
BOOL Status; //現在再生しているかどうか
int NowPlay; //現在再生してるファイルのアドレス
int MaxPlay; //減殺再生しているファイル軍の総数
WCHAR *ParentDir; //親ディレクトリ
WCHAR **File; //これから再生するファイル名
//トラックバーコントロール
HWND hTrack;
}InternalData;
//
//MCIを使い、サウンドファイルを読み込む
//lpFilePath...読み込ませたいファイル名
//lpstrDeviceType...そのファイルのタイプ(今のところはmp3のみを考える)
//rettemp...作成された構造体の情報
//callflag...読み込み失敗時に、create_temp_fileを呼び出すかどうか
//
//return TRUE...成功 / FALSE...失敗
//
int InitSoundStruct(LPCWSTR lpFilePath, LPCWSTR lpstrDeviceType, MCI_OPEN_PARMSW *retTemp, int callflag = TRUE);
//
//ID3タグがあるとき、それがない状態のmp3を作成する
//lpFilePath...ID3タグがあるかもしれないファイルパス
//lpstrDeviceType...そのファイルのタイプ(今のところはmp3のみを考える)
//rettemp...作成された構造体の情報
//
//return TRUE...成功 / FALSE...失敗
//
int create_temp_file(LPCWSTR lpFilePath, LPCWSTR lpstrDeviceType, MCI_OPEN_PARMSW *retTemp);
//
//トラックバーの作成
//hWnd...メインウィンドウのハンドル
//
//return 作成されたトラックバーのハンドル (失敗時はNULL)
//
HWND CreateTrackbar(HWND hWnd);
//
//ウィンドウのカスタムドローを一括して行う
//hWnd...メインウィンドウのハンドル
//indata...アプリが一括して管理するデータの構造体
//wp,lp...ウィンドウプロシージャから受け取った値
//
//return 様々な値
//
LRESULT CustomDrawProc(HWND hWnd, InternalData indata, WPARAM wp, LPARAM lp);
//
//音声を再生する
//indata...変更されるべきデータを持った構造体
//
//return 常にTRUE
//
int StartPlaySound(InternalData *indata);
//
//OutputDebugStringの改造版
//(lpOutputString,...)...出力される文字列
//
//return 成功...TRUE / 失敗...FALSE
//
int OutputDebugStringEx(LPCWSTR lpOutputString, ...);
//
//Userに開かれるべきファイルを訪ねる
//hWnd...親ウィンドのハンドル
//temp...情報を受け取ることができる構造体
//
//return TRUE...成功 /FALSE...失敗
//
int GetFileNames(HWND hWnd, InternalData *temp);
//
//作成されたファイルパスデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData_indata(InternalData *indata);
//
//作成されたMCIデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData_MCI(InternalData *indata);
//
//作成されたファイルパス、MCIデータといったデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData(InternalData *indata);
rc.h
► スポイラーを表示
コード:
#define DF_IDC_TRACKBAR (101211)
#define IDM_EXIT (1001)
#define IDM_OPEN (1002)
#define IDM_PLAY (1003)
BaseFunc.cpp
► スポイラーを表示
コード:
#include "strdef.h"
//
//OutputDebugStringの改造版
//(lpOutputString,...)...出力される文字列
//
//return 成功...TRUE / 失敗...FALSE
//
int OutputDebugStringEx(LPCWSTR lpOutputString, ...)
{
WCHAR *Str;
va_list val;
size_t len;
va_start(val, lpOutputString);
__try {
len = _vscwprintf(lpOutputString, val) + sizeof(WCHAR)/*NULL*/;
}
__except (1)
{
//OutputDebugStringEx("%s%s");みたいな感じだとここに来る
va_end(val);
return 0;
}
if ((
Str = (WCHAR*)malloc(len * sizeof(WCHAR))
) == NULL)
{
va_end(val);
return 0;
}
vswprintf_s(Str, len, lpOutputString, val);
OutputDebugStringW(Str);
va_end(val);
free(Str);
return 1;
}
CustomDraw.cppより
► スポイラーを表示
//
//今回質問させていただいている場所
//
//[蛇足]
//現在、トラックバーには現在再生している曲の秒数分のRANGEが作成されています。
//一秒ごとにWM_TIMERを使い、つまみの位置を移動させる処理をしています。
//このとき、ほとんどの場合つまみの位置(x,y)と、実際の座標(xr,yr)が同じではないのです(y=yrは多分成り立ちますが...)
//(それは、トラックバーの横幅と、RANGEの数がたいてい異なるから)
//そこで、現在すでに再生されてた分(1RANGE/秒)進んでいるつまみの位置を座標に変換しなければならないと考えました。
//ex) n秒の長さの曲を読み込み、現在k秒(n>1,0<k<n,n,kは0以上の整数)経過したとき
//|RANGE |1|2|3|4|5|6|....|k |.... |n-1|n |曲の終わり|
//|つまみの位置| ... |ここ |.... |ここまでくる |曲の終わり|
//|実際の座標 | ... |pos_x|.... |lpDraw->right|曲の終わり|
//そこで次の比例式をかんがえました。
//lpDraw->rc.right : indata.maxSrc = pos_x : Pos;
//⇔pos_x = (lpDraw->rc.right * Pos) / indata.maxSrc(=max) (ただしindata.maxSrc≠0だが、すでに0は回避している)
pos_x = (int)((lpDraw->rc.right * Pos) / max);
//ところが。うまくいかないのです...
//また、なぜか次のようにすると、トラックバーをダブルクリックするまでは、つまみがすでに進んだところは青くなります
pos_x = (int)(lpDraw->rc.right * Pos);
CustomDraw.cpp
► スポイラーを表示
コード:
#include "strdef.h"
//
//ウィンドウのカスタムドローを一括して行う
//hWnd...メインウィンドウのハンドル
//indata...アプリが一括して管理するデータの構造体
//wp,lp...ウィンドウプロシージャから受け取った値
//
//return 様々な値
//
LRESULT CustomDrawProc(HWND hWnd, InternalData indata,WPARAM wp, LPARAM lp)
{
LPNMHDR lpNotify = (LPNMHDR)lp;
LPNMCUSTOMDRAW lpDraw;
static HBRUSH bbrush,wbrush, oldBrush;
int Pos, max, pos_x, ret;
RECT rc;
//ブラシが作成されていないようなら作成
if (!wbrush)
{
wbrush = CreateSolidBrush(RGB(255, 255, 255));
bbrush = CreateSolidBrush(RGB(0, 0, 255));
}
switch (lpNotify->code)
{
case NM_CUSTOMDRAW:
//トラックバーに関する処理だったとき
if (lpNotify->hwndFrom == indata.hTrack)
{
lpDraw = (LPNMCUSTOMDRAW)lpNotify;
switch (lpDraw->dwDrawStage)
{
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
switch (lpDraw->dwItemSpec)
{
case TBCD_CHANNEL:
// return CDRF_DODEFAULT;
case TBCD_TICS:
//再生が終わった場所は青く塗る
//現在位置の取得
Pos = SendMessage(indata.hTrack, TBM_GETPOS, 0, 0);
//最大数の取得
max = indata.maxSrc;
if (!max)
max++; // /0 を回避
//とりあえず真っ白に塗り込む
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, wbrush);
FillRect(lpDraw->hdc, &lpDraw->rc, wbrush);
SelectObject(lpDraw->hdc, oldBrush);
//左側
//
//今回質問させていただいている場所
//
//[蛇足]
//現在、トラックバーには現在再生している曲の秒数分のRANGEが作成されています。
//一秒ごとにWM_TIMERを使い、つまみの位置を移動させる処理をしています。
//このとき、ほとんどの場合つまみの位置(x,y)と、実際の座標(xr,yr)が同じではないのです(y=yrは多分成り立ちますが...)
//(それは、トラックバーの横幅と、RANGEの数がたいてい異なるから)
//そこで、現在すでに再生されてた分(1RANGE/秒)進んでいるつまみの位置を座標に変換しなければならないと考えました。
//ex) n秒の長さの曲を読み込み、現在k秒(n>1,0<k<n,n,kは0以上の整数)経過したとき
//|RANGE |1|2|3|4|5|6|....|k |.... |n-1|n |曲の終わり|
//|つまみの位置| ... |ここ |.... |ここまでくる |曲の終わり|
//|実際の座標 | ... |pos_x|.... |lpDraw->right|曲の終わり|
//そこで次の比例式をかんがえました。
//lpDraw->rc.right : indata.maxSrc = pos_x : Pos;
//⇔pos_x = (lpDraw->rc.right * Pos) / indata.maxSrc(=max) (ただしindata.maxSrc≠0だが、すでに0は回避している)
pos_x = (int)((lpDraw->rc.right * Pos) / max);
//ところが。うまくいかないのです...
//また、なぜか次のようにすると、トラックバーをダブルクリックするまでは、つまみがすでに進んだところは青くなります
pos_x = (int)(lpDraw->rc.right * Pos);
rc = lpDraw->rc;
rc.right = (LONG)pos_x;
rc.left = 0;
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, bbrush);
ret = FillRect(lpDraw->hdc, &rc, bbrush);
SelectObject(lpDraw->hdc, oldBrush);
OutputDebugStringEx(L"(%d,%d),", ret, pos_x);
return CDRF_SKIPDEFAULT;
case TBCD_THUMB:
//つまみを描画(今回は楕円)
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, wbrush);
Ellipse(lpDraw->hdc, lpDraw->rc.left, lpDraw->rc.top, lpDraw->rc.right, lpDraw->rc.bottom);
SelectObject(lpDraw->hdc, oldBrush);
return CDRF_SKIPDEFAULT;
default:
return CDRF_DODEFAULT;
}
break;
}
break;
}
}
//終了
return DefWindowProcW(hWnd, WM_NOTIFY, wp, lp);
}
Main.cpp
► スポイラーを表示
コード:
#include "strdef.h"
#include "rc.h"
//ウィンドウハンドル
HWND hwnd;
//インスタンスハンドル
HINSTANCE hinst;
//ウィンドウ横幅
#define WIDTH 500
#define HEIGHT 300
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
static InternalData indata;
static MCI_STATUS_PARMS mciStatus;
RECT rc;
WCHAR Temp[MAX_PATH * 2];
switch (msg) {
case WM_DESTROY:
mciSendCommandW(indata.open.wDeviceID, MCI_CLOSE, 0, 0);
PostQuitMessage(0);
return 0;
case WM_CREATE:
InitCommonControls();
SetTimer(hwnd, 1, 1000, NULL);
indata.play.dwCallback = (DWORD)hwnd;
//トラックバー作成
indata.hTrack = CreateTrackbar(hwnd);
return 0;
case WM_TIMER:
if (indata.Status)
{
DWORD dwSrc, dwMin, dwSrc_all, dwMin_all;
WCHAR Temp[512];
mciStatus.dwItem = MCI_STATUS_POSITION;
mciSendCommandW(indata.open.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
//秒と分を取得
dwSrc = mciStatus.dwReturn / 1000; //msrc->src
dwMin = dwSrc / 60;
dwSrc %= 60;
dwSrc_all = indata.maxSrc % 60;
dwMin_all = indata.maxSrc / 60;
swprintf_s(Temp, L"%02d:%02d", dwMin, dwSrc);
SendMessageW(indata.hTrack, TBM_SETPOS, TRUE, mciStatus.dwReturn / 1000);
UpdateWindow(indata.hTrack);
SetWindowTextW(hwnd, Temp);
}
return 0;
case WM_SIZE:
GetClientRect(hwnd, &rc);
MoveWindow(indata.hTrack, 0, rc.bottom - 25, LOWORD(lp), HIWORD(lp), TRUE);
return 0;
case WM_NOTIFY:
return CustomDrawProc(hwnd, indata, wp, lp);
case MM_MCINOTIFY:
if (lp == indata.open.wDeviceID)
{
if (wp == MCI_NOTIFY_SUCCESSFUL)
{
//シークバーを先頭に戻す
//再生するファイルに関する処理
mciSendCommandW(indata.open.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
if (indata.NowPlay == indata.MaxPlay)
indata.NowPlay = 0;
else
indata.NowPlay++;
//次のファイルの再生
DeleteInternalData_MCI(&indata);
swprintf_s(Temp, L"%s\\%s", indata.ParentDir, indata.File[indata.NowPlay]);
InitSoundStruct(Temp, L"MPEGVideo", &indata.open);
StartPlaySound(&indata);
}
return 0;
}
break;
case WM_COMMAND:
switch (LOWORD(wp))
{
case IDM_EXIT:
SendMessageW(hwnd, WM_DESTROY, 0, 0);
break;
case IDM_PLAY:
// StartPlaySound(&indata);
break;
case IDM_OPEN:
//もし再生していたら、削除
DeleteInternalData(&indata);
if (GetFileNames(hwnd, &indata))
{
swprintf_s(Temp, L"%s\\%s", indata.ParentDir, indata.File[0]);
InitSoundStruct(Temp, L"MPEGVideo", &indata.open);
StartPlaySound(&indata);
}
break;
}
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MSG msg;
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hCursor = wc.hIcon = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = L"MainWindow";
wc.lpszMenuName = L"SOUNDPLAYER";
if (!RegisterClassW(&wc)) {
assert(FALSE);
return -1;
}
//インスタンスハンドル
hinst = hInstance;
hwnd = CreateWindowW(wc.lpszClassName, L"MainWindow", WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
0, 0, 400, 400, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
assert(FALSE);
return -1;
}
//GetMessageは0か-1のときエラー
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
DispatchMessage(&msg);
}
//クラス解放
UnregisterClass(L"test", hinst);
return msg.wParam;
}
SoundAPI.cpp
► スポイラーを表示
コード:
#include "strdef.h"
//
//MCIを使い、サウンドファイルを読み込む
//lpFilePath...読み込ませたいファイル名
//lpstrDeviceType...そのファイルのタイプ(今のところはmp3のみを考える)
//rettemp...作成された構造体の情報
//callflag...読み込み失敗時に、create_temp_fileを呼び出すかどうか
//
//return TRUE...成功 / FALSE...失敗
//
int InitSoundStruct(LPCWSTR lpFilePath,LPCWSTR lpstrDeviceType,MCI_OPEN_PARMSW *retTemp,int callflag)
{
MCIERROR result;
WCHAR MsgBuf[1024];
MCI_OPEN_PARMSW MPEGStruct = { 0 };
if (!retTemp)
return FALSE;
MPEGStruct.lpstrDeviceType = lpstrDeviceType;
MPEGStruct.lpstrElementName = lpFilePath;
result = mciSendCommandW(
0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD_PTR)&MPEGStruct
);
if (result)
{
//失敗
mciGetErrorStringW(result, MsgBuf, sizeof(MsgBuf));
OutputDebugStringW(MsgBuf);
*retTemp = MPEGStruct;
//callflagで判断
return callflag == 1 ?
create_temp_file(lpFilePath, lpstrDeviceType, retTemp) : FALSE;
}
*retTemp = MPEGStruct;
return TRUE;
}
//
//syncheface数(7bit)を普通の8bit数に変換
//size[4]...synchface数
//
//return 普通の8bit数(4byte)
//
int unsynchsafe(char size[4])
{
int ToRet = 0;
if ((size[0] & 0x0F) || ((size[0] | size[1] | size[2] | size[3]) & 0x80))
{
}
else {
int Footer = size[0] & 0x10;
ToRet = size[0] << 21;
ToRet += size[1] << 14;
ToRet += size[2] << 7;
ToRet += size[3];
ToRet += 10;
if (Footer)
ToRet += 10;
}
return ToRet;
}
//
//ID3タグがあるとき、それがない状態のmp3を作成する
//lpFilePath...ID3タグがあるかもしれないファイルパス
//lpstrDeviceType...そのファイルのタイプ(今のところはmp3のみを考える)
//rettemp...作成された構造体の情報
//
//return TRUE...成功 / FALSE...失敗
//
int create_temp_file(LPCWSTR lpFilePath,LPCWSTR lpstrDeviceType,MCI_OPEN_PARMSW *retTemp)
{
FILE *fp,*wfp;
ID3V2HEADER head; //ID3プロファイルがあるかどうか
DWORD size = 0;
// char _size[32];
_wfopen_s(&fp, lpFilePath, L"rb");
if (!fp)
{
OutputDebugStringEx(
L"%s:ファイル\"%s\"\nの読み込みに失敗しました。\n",
__FUNCSIG__, lpFilePath
);
return 0;
}
ZeroMemory(&head, sizeof(head));
fread(&head, sizeof(head), 1, fp);
if (head.tag[0] != 'I' ||
head.tag[1] != 'D' ||
head.tag[2] != '3'
)
{
OutputDebugStringW(L"ID3 header not found\n");
goto err;
}
//
//ID3を持たないmp3ファイルを書き出す
//
//
//syncfaceを普通の8bit数に変換
size = unsynchsafe(head.size);
//headerはとりあえず読み飛ばす
long file_size;
char *buffer;
//ファイルサイズを取得する
//戦闘に移動
fseek(fp, 0, SEEK_SET);
if (fseek(fp, 0, SEEK_END) != 0) {
/* エラー処理 */
goto err;
}
file_size = ftell(fp);
if (file_size == -1) {
/* エラー処理 */
goto err;
}
buffer = (char*)malloc(file_size - size);
if (buffer == NULL) {
/* エラー処理 */
goto err;
}
//mp3の先頭に移動
fseek(fp, size, SEEK_SET);
//本体読み込み
fread(buffer, file_size - size, 1, fp);
_wfopen_s(&wfp, TEMP_FILE_NAME, L"wb");
if (!wfp)
goto err;
fwrite(buffer, file_size - size, 1, wfp);
fclose(fp);
fclose(wfp);
free(buffer);
//再読み込みを試みる
return InitSoundStruct(
TEMP_FILE_NAME,
lpstrDeviceType,
retTemp,
FALSE);
err:
fclose(fp);
return 0;
}
//
//音声を再生する
//indata...変更されるべきデータを持った構造体
//
//return 常にTRUE
//
int StartPlaySound(InternalData *indata)
{
MCI_STATUS_PARMS mciStatus;
//再生が終了したときに、通知を受け取る
mciSendCommandW(indata->open.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD_PTR)&indata->play);
//
//曲の長さを取得
//
//とりあえず戻り値は'm'srcにする
mciStatus.dwItem = MCI_FORMAT_MILLISECONDS;
mciSendCommandW(indata->open.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)&mciStatus);
mciStatus.dwItem = MCI_STATUS_LENGTH;
mciSendCommandW(indata->open.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
indata->Status = TRUE;
indata->maxSrc = mciStatus.dwReturn / 1000;
//トラックバーに関する設定
SendMessageW(indata->hTrack, TBM_SETRANGE, TRUE, MAKELPARAM(0, indata->maxSrc));
return TRUE;
}
//
//作成されたファイルパスデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData_indata(InternalData *indata)
{
int i;
//データがない場合何もしない
if (!indata->Status)
return TRUE;
//削除
for (i = 0; i < indata->MaxPlay; i++)
free(indata->File[i]);
free(indata->File);
free(indata->ParentDir);
return TRUE;
}
//
//作成されたMCIデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData_MCI(InternalData *indata)
{
//データがない場合何もしない
if (!indata->Status)
return TRUE;
//MCIの終了
mciSendCommandW(indata->open.wDeviceID, MCI_CLOSE, 0, 0);
return TRUE;
}
//
//作成されたファイルパス、MCIデータといったデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData(InternalData *indata)
{
DeleteInternalData_indata(indata);
DeleteInternalData_MCI(indata);
return TRUE;
}
UserInerface.cpp
► スポイラーを表示
コード:
#include "strdef.h"
#include "rc.h"
//
//トラックバーの作成
//hWnd...メインウィンドウのハンドル
//
//return 作成されたトラックバーのハンドル (失敗時はNULL)
//
HWND CreateTrackbar(HWND hWnd)
{
long lMin = 0;
long lMax = 20;
long lFirstPosition = 0;
long lPageSize = 5;
HWND hTrackbar;
/* コモンコントロールの初期化 */
InitCommonControls();
/* トラックバーコントロールの作成 */
hTrackbar = CreateWindowEx(
0, TRACKBAR_CLASS, NULL,
WS_CHILD | WS_VISIBLE |
TBS_NOTICKS | TBS_TOOLTIPS,
0, 0, 200, 30,
hWnd, (HMENU)DF_IDC_TRACKBAR, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
if (!hTrackbar)
return NULL;
/* 範囲のセット */
SendMessageW(hTrackbar, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(lMin, lMax));
/* 1ページのサイズのセット */
SendMessageW(hTrackbar, TBM_SETPAGESIZE, 0, (LPARAM)lPageSize);
/* 初期値のセット */
SendMessageW(hTrackbar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)lFirstPosition);
/* フォーカスのセット */
// SetFocus(hTrackbar);
return hTrackbar;
}
//
//Userに開かれるべきファイルを訪ねる
//hWnd...親ウィンドのハンドル
//temp...情報を受け取ることができる構造体
//
//return TRUE...成功 /FALSE...失敗
//
int GetFileNames(HWND hWnd, InternalData *temp)
{
const long MAXFILELENGTH = MAX_PATH * 5;
OPENFILENAMEW ofn;
WCHAR fileName[MAXFILELENGTH] = { 0 };
int iCnt = 0, nPos;
size_t Dirlen;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_HIDEREADONLY;
ofn.lpstrFile = fileName;
ofn.nMaxFile = sizeof(fileName);
ofn.lpstrFilter = L"全てのファイル(*.*)\0*.*\0";
ofn.lpstrDefExt = L"*.*";
ofn.nFilterIndex = 1;
if (GetOpenFileNameW(&ofn))
{
nPos = lstrlen(fileName) + 1;
//とりあえずディレクトリを割り出す
Dirlen = wcslen(fileName) + 1;
if ((temp->ParentDir = (WCHAR*)malloc(Dirlen * sizeof(WCHAR))) == NULL)
return FALSE;
//残りメモリも確保しておく(多くても損はしない)
if ((temp->File = (WCHAR**)malloc(MAXFILELENGTH * sizeof(WCHAR))) == NULL)
{
free(temp->ParentDir);
return FALSE;
}
ZeroMemory(temp->File, MAXFILELENGTH * sizeof(WCHAR));
//データをコピー
//(strcpy_sだとメモリをよこせと言ってくるので)
memcpy(temp->ParentDir, fileName, Dirlen * sizeof(WCHAR));
if (fileName[nPos])
{
// 複数ファイルが選択された
do {
// パスをコピー
if (fileName[nPos]) temp->File[iCnt] = (WCHAR*)malloc((wcslen(&fileName[nPos]) + 1) * sizeof(WCHAR));
else break;
if (!temp->File[iCnt])
{
//leak
free(temp->File);
free(temp->ParentDir);
return FALSE;
}
wcscpy_s(temp->File[iCnt], (wcslen(&fileName[nPos]) + 1), &fileName[nPos]);
OutputDebugStringEx(L"%s\n", temp->File[iCnt]);
iCnt++;
nPos += (wcslen(&fileName[nPos]) + 1);
} while (fileName[nPos]);
}
else {
temp->File[0] = (WCHAR*)malloc((wcslen(PathFindFileNameW(fileName)) + 1) * sizeof(WCHAR));
if (!temp->File[0])
{
free(temp->File);
free(temp->ParentDir);
return FALSE;
}
// 1つだけ選択された
lstrcpy(temp->File[0], fileName);
iCnt = 1;
}
}
else
return FALSE;
//準備
temp->NowPlay = 0;
temp->MaxPlay = iCnt;
return TRUE;
}
Re: Win32 プログラミング
Posted: 2017年1月06日(金) 00:07
by 駆け出し
こんばんは。連投失礼します。
まず、検証用に置かせていただいたソースコードですが、バグがありました。申し訳ないです。
UserInterface.cppのGetUserFilesの、一つのファイルのみが選択されたときの処理を完全にDebugするのを忘れてました。(複数のほうのみチェックして油断してました。)
つきましては、UserInerface.cppは次のようになります。また、新たに関数を追加しています。
こちらのコードは最新のものではありません。
UserInerface.cpp,strdef.h,Main.cpp,CunstomDraw.cpp
► スポイラーを表示
UserInterface.cpp
► スポイラーを表示
コード:
#include "strdef.h"
#include "rc.h"
//
//トラックバーの作成
//hWnd...メインウィンドウのハンドル
//
//return 作成されたトラックバーのハンドル (失敗時はNULL)
//
HWND CreateTrackbar(HWND hWnd)
{
long lMin = 0;
long lMax = 20;
long lFirstPosition = 0;
long lPageSize = 5;
HWND hTrackbar;
/* コモンコントロールの初期化 */
InitCommonControls();
/* トラックバーコントロールの作成 */
hTrackbar = CreateWindowEx(
0, TRACKBAR_CLASS, NULL,
WS_CHILD | WS_VISIBLE |
TBS_NOTICKS | TBS_TOOLTIPS,
0, 0, 200, 30,
hWnd, (HMENU)DF_IDC_TRACKBAR, (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE), NULL);
if (!hTrackbar)
return NULL;
/* 範囲のセット */
SendMessageW(hTrackbar, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(lMin, lMax));
/* 1ページのサイズのセット */
SendMessageW(hTrackbar, TBM_SETPAGESIZE, 0, (LPARAM)lPageSize);
/* 初期値のセット */
SendMessageW(hTrackbar, TBM_SETPOS, (WPARAM)TRUE, (LPARAM)lFirstPosition);
/* フォーカスのセット */
// SetFocus(hTrackbar);
return hTrackbar;
}
//
//トラックバーに関する設定を行う
//indata...設定されるべきトラックバーのハンドル
//
void SetTrackbarStatus(InternalData indata)
{
/* 範囲のセット */
SendMessageW(indata.hTrack, TBM_SETRANGE, (WPARAM)TRUE, (LPARAM)MAKELONG(0, indata.maxSrc));
/* 1ページのサイズのセット */
SendMessageW(indata.hTrack, TBM_SETPAGESIZE, 0, (LPARAM)indata.maxSrc);
}
//
//Userに開かれるべきファイルを訪ねる
//hWnd...親ウィンドのハンドル
//temp...情報を受け取ることができる構造体
//
//return TRUE...成功 /FALSE...失敗
//
int GetFileNames(HWND hWnd, InternalData *temp)
{
const long MAXFILELENGTH = MAX_PATH * 5;
OPENFILENAMEW ofn;
WCHAR fileName[MAXFILELENGTH] = { 0 };
int iCnt = 0, nPos;
size_t Dirlen;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_HIDEREADONLY;
ofn.lpstrFile = fileName;
ofn.nMaxFile = sizeof(fileName);
ofn.lpstrFilter = L"全てのファイル(*.*)\0*.*\0";
ofn.lpstrDefExt = L"*.*";
ofn.nFilterIndex = 1;
if (GetOpenFileNameW(&ofn))
{
nPos = lstrlen(fileName) + 1;
//とりあえずディレクトリを割り出す
Dirlen = wcslen(fileName) + 1;
if ((temp->ParentDir = (WCHAR*)malloc(Dirlen * sizeof(WCHAR))) == NULL)
return FALSE;
//残りメモリも確保しておく(多くても損はしない)
if ((temp->File = (WCHAR**)malloc(MAXFILELENGTH * sizeof(WCHAR))) == NULL)
{
free(temp->ParentDir);
return FALSE;
}
ZeroMemory(temp->File, MAXFILELENGTH * sizeof(WCHAR));
//データをコピー
//(strcpy_sだとメモリをよこせと言ってくるので)
memcpy(temp->ParentDir, fileName, Dirlen * sizeof(WCHAR));
if (fileName[nPos])
{
// 複数ファイルが選択された
do {
// パスをコピー
if (fileName[nPos]) temp->File[iCnt] = (WCHAR*)malloc((wcslen(&fileName[nPos]) + 1) * sizeof(WCHAR));
else break;
if (!temp->File[iCnt])
{
//leak
free(temp->File);
free(temp->ParentDir);
return FALSE;
}
wcscpy_s(temp->File[iCnt], (wcslen(&fileName[nPos]) + 1), &fileName[nPos]);
OutputDebugStringEx(L"%s\n", temp->File[iCnt]);
iCnt++;
nPos += (wcslen(&fileName[nPos]) + 1);
} while (fileName[nPos]);
}
else {
//親ディレクトリおよびファイルに関するメモリを確保しなおす
ZeroMemory(temp->ParentDir + (Dirlen - wcslen(PathFindFileNameW(fileName)) -2/*NULL + \\*/), wcslen(PathFindFileNameW(fileName)) * sizeof(WCHAR));
//親ディレクトリ名だけを確保
temp->File[0] = (WCHAR*)malloc((wcslen(PathFindFileNameW(fileName) + 1) * sizeof(WCHAR)));
if (!temp->File[0])
return FALSE;
wcscpy_s(temp->File[0], wcslen(PathFindFileNameW(fileName)) + 1, PathFindFileNameW(fileName));
iCnt = 1;
}
}
else
return FALSE;
//準備
temp->NowPlay = 0;
temp->MaxPlay = iCnt;
return TRUE;
}
strdef.h
► スポイラーを表示
コード:
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <mmsystem.h>
#include <windows.h>
#include <vfw.h>
#include <commctrl.h>
#include <stdio.h>
#include <assert.h>
#include <tchar.h>
#include <shlwapi.h>
#include <windowsx.h>
#pragma comment( lib, "shlwapi.lib" )
#pragma comment(lib,"msvfw32.lib")
#pragma comment(lib,"winmm.lib")
#pragma comment(lib, "comctl32.lib")
#define TEMP_FILE_NAME L"soundfile.mp3"
typedef struct tagID3V2HEADER
{
char tag[3];
char version[2];
char flags;
char size[4]; //syncface
} ID3V2HEADER;
typedef struct tagInternalData
{
//現在再生しているデータに関するもの
DWORD maxSrc;
MCI_OPEN_PARMS open;
MCI_PLAY_PARMS play;
BOOL Status; //現在再生しているかどうか
int NowPlay; //現在再生してるファイルのアドレス
int MaxPlay; //減殺再生しているファイル軍の総数
WCHAR *ParentDir; //親ディレクトリ
WCHAR **File; //これから再生するファイル名
//トラックバーコントロール
HWND hTrack;
}InternalData;
//
//MCIを使い、サウンドファイルを読み込む
//lpFilePath...読み込ませたいファイル名
//lpstrDeviceType...そのファイルのタイプ(今のところはmp3のみを考える)
//rettemp...作成された構造体の情報
//callflag...読み込み失敗時に、create_temp_fileを呼び出すかどうか
//
//return TRUE...成功 / FALSE...失敗
//
int InitSoundStruct(LPCWSTR lpFilePath, LPCWSTR lpstrDeviceType, MCI_OPEN_PARMSW *retTemp, int callflag = TRUE);
//
//ID3タグがあるとき、それがない状態のmp3を作成する
//lpFilePath...ID3タグがあるかもしれないファイルパス
//lpstrDeviceType...そのファイルのタイプ(今のところはmp3のみを考える)
//rettemp...作成された構造体の情報
//
//return TRUE...成功 / FALSE...失敗
//
int create_temp_file(LPCWSTR lpFilePath, LPCWSTR lpstrDeviceType, MCI_OPEN_PARMSW *retTemp);
//
//トラックバーの作成
//hWnd...メインウィンドウのハンドル
//
//return 作成されたトラックバーのハンドル (失敗時はNULL)
//
HWND CreateTrackbar(HWND hWnd);
//
//トラックバーに関する設定を行う
//indata...設定されるべきトラックバーのハンドル
//
void SetTrackbarStatus(InternalData indata);
//
//ウィンドウのカスタムドローを一括して行う
//hWnd...メインウィンドウのハンドル
//indata...アプリが一括して管理するデータの構造体
//wp,lp...ウィンドウプロシージャから受け取った値
//
//return 様々な値
//
LRESULT CustomDrawProc(HWND hWnd, InternalData indata, WPARAM wp, LPARAM lp);
//
//音声を再生する
//indata...変更されるべきデータを持った構造体
//
//return 常にTRUE
//
int StartPlaySound(InternalData *indata);
//
//OutputDebugStringの改造版
//(lpOutputString,...)...出力される文字列
//
//return 成功...TRUE / 失敗...FALSE
//
int OutputDebugStringEx(LPCWSTR lpOutputString, ...);
//
//Userに開かれるべきファイルを訪ねる
//hWnd...親ウィンドのハンドル
//temp...情報を受け取ることができる構造体
//
//return TRUE...成功 /FALSE...失敗
//
int GetFileNames(HWND hWnd, InternalData *temp);
//
//作成されたファイルパスデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData_indata(InternalData *indata);
//
//作成されたMCIデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData_MCI(InternalData *indata);
//
//作成されたファイルパス、MCIデータといったデータをすべて削除する
//indata...削除されるべきデータを持った構造体
//
//return ... 常にTRUE
//
int DeleteInternalData(InternalData *indata);
Main.cpp
► スポイラーを表示
コード:
#include "strdef.h"
#include "rc.h"
//ウィンドウハンドル
HWND hwnd;
//インスタンスハンドル
HINSTANCE hinst;
//ウィンドウ横幅
#define WIDTH 500
#define HEIGHT 300
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
static InternalData indata;
static MCI_STATUS_PARMS mciStatus;
RECT rc;
WCHAR Temp[MAX_PATH * 2];
switch (msg) {
case WM_DESTROY:
mciSendCommandW(indata.open.wDeviceID, MCI_CLOSE, 0, 0);
PostQuitMessage(0);
return 0;
case WM_CREATE:
InitCommonControls();
SetTimer(hwnd, 1, 1000, NULL);
indata.play.dwCallback = (DWORD)hwnd;
//トラックバー作成
indata.hTrack = CreateTrackbar(hwnd);
return 0;
case WM_TIMER:
if (indata.Status)
{
DWORD dwSrc, dwMin, dwSrc_all, dwMin_all;
WCHAR Temp[512];
mciStatus.dwItem = MCI_STATUS_POSITION;
mciSendCommandW(indata.open.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD_PTR)&mciStatus);
//秒と分を取得
dwSrc = mciStatus.dwReturn / 1000; //msrc->src
dwMin = dwSrc / 60;
dwSrc %= 60;
dwSrc_all = indata.maxSrc % 60;
dwMin_all = indata.maxSrc / 60;
swprintf_s(Temp, L"%02d:%02d", dwMin, dwSrc);
SendMessageW(indata.hTrack, TBM_SETPOS, TRUE, mciStatus.dwReturn / 1000);
SetTrackbarStatus(indata);
SetWindowTextW(hwnd, Temp);
}
return 0;
case WM_SIZE:
GetClientRect(hwnd, &rc);
MoveWindow(indata.hTrack, 0, rc.bottom - 25, LOWORD(lp), HIWORD(lp), TRUE);
return 0;
case WM_NOTIFY:
return CustomDrawProc(hwnd, indata, wp, lp);
case MM_MCINOTIFY:
if (lp == indata.open.wDeviceID)
{
if (wp == MCI_NOTIFY_SUCCESSFUL)
{
//シークバーを先頭に戻す
//再生するファイルに関する処理
mciSendCommandW(indata.open.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
if (indata.NowPlay == indata.MaxPlay)
indata.NowPlay = 0;
else
indata.NowPlay++;
//次のファイルの再生
DeleteInternalData_MCI(&indata);
swprintf_s(Temp, L"%s\\%s", indata.ParentDir, indata.File[indata.NowPlay]);
InitSoundStruct(Temp, L"MPEGVideo", &indata.open);
StartPlaySound(&indata);
}
return 0;
}
break;
case WM_COMMAND:
switch (LOWORD(wp))
{
case IDM_EXIT:
SendMessageW(hwnd, WM_DESTROY, 0, 0);
break;
case IDM_PLAY:
// StartPlaySound(&indata);
break;
case IDM_OPEN:
//もし再生していたら、削除
DeleteInternalData(&indata);
if (GetFileNames(hwnd, &indata))
{
swprintf_s(Temp, L"%s\\%s", indata.ParentDir, indata.File[0]);
InitSoundStruct(Temp, L"MPEGVideo", &indata.open);
StartPlaySound(&indata);
}
break;
}
}
return DefWindowProcW(hwnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
MSG msg;
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WinProc;
wc.cbClsExtra = wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hCursor = wc.hIcon = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = L"MainWindow";
wc.lpszMenuName = L"SOUNDPLAYER";
if (!RegisterClassW(&wc)) {
assert(FALSE);
return -1;
}
//インスタンスハンドル
hinst = hInstance;
hwnd = CreateWindowW(wc.lpszClassName, L"MainWindow", WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
0, 0, 400, 400, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
assert(FALSE);
return -1;
}
//GetMessageは0か-1のときエラー
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
DispatchMessage(&msg);
}
//クラス解放
UnregisterClass(L"test", hinst);
return msg.wParam;
}
CustomDraw.cpp
► スポイラーを表示
コード:
#include "strdef.h"
//
//ウィンドウのカスタムドローを一括して行う
//hWnd...メインウィンドウのハンドル
//indata...アプリが一括して管理するデータの構造体
//wp,lp...ウィンドウプロシージャから受け取った値
//
//return 様々な値
//
LRESULT CustomDrawProc(HWND hWnd, InternalData indata,WPARAM wp, LPARAM lp)
{
LPNMHDR lpNotify = (LPNMHDR)lp;
LPNMCUSTOMDRAW lpDraw;
static HBRUSH bbrush,wbrush, oldBrush;
int Pos, max, pos_x, ret;
RECT rc;
//ブラシが作成されていないようなら作成
if (!wbrush)
{
wbrush = CreateSolidBrush(RGB(255, 255, 255));
bbrush = CreateSolidBrush(RGB(0, 0, 255));
}
switch (lpNotify->code)
{
case NM_CUSTOMDRAW:
//トラックバーに関する処理だったとき
if (lpNotify->hwndFrom == indata.hTrack)
{
lpDraw = (LPNMCUSTOMDRAW)lpNotify;
switch (lpDraw->dwDrawStage)
{
case CDDS_PREPAINT:
return CDRF_NOTIFYITEMDRAW;
case CDDS_ITEMPREPAINT:
switch (lpDraw->dwItemSpec)
{
case TBCD_CHANNEL:
// return CDRF_DODEFAULT;
case TBCD_TICS:
//再生が終わった場所は青く塗る
//現在位置の取得
Pos = SendMessage(indata.hTrack, TBM_GETPOS, 0, 0);
//最大数の取得
max = indata.maxSrc;
if (!max)
max++; // /0 を回避
//とりあえず真っ白に塗り込む
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, wbrush);
FillRect(lpDraw->hdc, &lpDraw->rc, wbrush);
SelectObject(lpDraw->hdc, oldBrush);
//左側
//
//今回質問させていただいている場所
//
//[蛇足]
//現在、トラックバーには現在再生している曲の秒数分のRANGEが作成されています。
//一秒ごとにWM_TIMERを使い、つまみの位置を移動させる処理をしています。
//このとき、ほとんどの場合つまみの位置(x,y)と、実際の座標(xr,yr)が同じではないのです(y=yrは多分成り立ちますが...)
//(それは、トラックバーの横幅と、RANGEの数がたいてい異なるから)
//そこで、現在すでに再生されてた分(1RANGE/秒)進んでいるつまみの位置を座標に変換しなければならないと考えました。
//ex) n秒
//|RANGE |1|2|3|4|5|6|....|k |.... |n-1|n |曲の終わり|
//|つまみの位置| ... |ここ |.... |ここまでくる |曲の終わり|
//|実際の座標 | ... |pos_x|.... |lpDraw->right|曲の終わり|
//そこで次の比例式をかんがえました。
//lpDraw->rc.right : indata.maxSrc = pos_x : Pos;
//⇔pos_x = (lpDraw->rc.right * Pos) / indata.maxSrc(=max) (ただしindata.maxSrc≠0だが、すでに0は回避している)
pos_x = (int)(((lpDraw->rc.right + lpDraw->rc.left) * Pos) / max)+lpDraw->rc.left; //初期位置
rc = lpDraw->rc;
rc.right = (LONG)pos_x;
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, bbrush);
ret = FillRect(lpDraw->hdc, &rc, bbrush);
SelectObject(lpDraw->hdc, oldBrush);
OutputDebugStringEx(
L"RECT(x){left,right},ret=(%d){%d,%d},%d\n",
rc.right-rc.left,rc.left,rc.right,ret
);
return CDRF_SKIPDEFAULT;
case TBCD_THUMB:
//つまみを描画(今回は楕円)
oldBrush = (HBRUSH)SelectObject(lpDraw->hdc, wbrush);
Ellipse(lpDraw->hdc, lpDraw->rc.left, lpDraw->rc.top, lpDraw->rc.right, lpDraw->rc.bottom);
SelectObject(lpDraw->hdc, oldBrush);
return CDRF_SKIPDEFAULT;
default:
return CDRF_DODEFAULT;
}
break;
}
break;
}
}
//終了
return DefWindowProcW(hWnd, WM_NOTIFY, wp, lp);
}
次に、青い部分の描画ですがWM_TIMERイベントがあるごとにSetTrackbarStatusを呼び出すことにより、更新部分を反映させることに成功しました。
勝手に一人で掲示板を荒らし、申し訳ありませんでした。
Re: Win32 プログラミング
Posted: 2017年1月06日(金) 00:30
by Math
>勝手に一人で掲示板を荒らし、申し訳ありませんでした。
掲示板を書くだけで気合いがはいるしプログラムも整理されるし”荒らし”てるわけではないと思います。
時にはすぐ”自己解決しました。”と風の如く去る人も...
"掲示板の効果”はで絶大ですよ。
Re: Win32 プログラミング
Posted: 2017年1月06日(金) 00:40
by Math
>関数:FillRectの戻り値は常に1なのです...
1はもしかしてTRUEでは...
Re: Win32 プログラミング
Posted: 2017年1月06日(金) 08:41
by Math
実際の開発現場ではスタブ (stub)とドライバー(driver)という考え方がある。
https://ja.wikipedia.org/wiki/%E3%82%B9 ... F%E3%83%96
このようにスタブ毎にテスト出来ないと自分でもわけが分からない"スパゲティ"プログラムになる。基本設計書をまず見せて頂き1ファイル事にテスト可能な設計になっているか検討する必要があります
Re: Win32 プログラミング
Posted: 2017年1月07日(土) 00:21
by Math
描画プログラムをテストするプログラムを考えてみました。
コード:
#include <windows.h>//////// 検証用プログラム:ドライバー:描画チェッカー
#include <stdio.h>////////// 2017/01/06 Writen by Math
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;PAINTSTRUCT ps;static INT imode;POINT points[3];static INT ret;
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:printf("<<<--->>>\n\n");
imode = MessageBox(
NULL , TEXT("return値を確認しますか?。") ,
TEXT("SetPolyFillMode") , MB_YESNO
);
if (imode == IDYES) imode = WINDING;
else imode = ALTERNATE;printf("001--->>>\n\n");
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd , &ps);/////////////////////////////////////
SelectObject(hdc , CreateSolidBrush(0xFF));
SetPolyFillMode(hdc , imode);
ret=Rectangle( hdc, 10, 20, 100, 110 );printf("ret=%d\n",ret);
ret=Ellipse( hdc, 10, 20, 100, 110);printf("ret=%d\n",ret);
points[0].x = 190; points[0].y =60;
points[1].x = 120; points[1].y = 140;
points[2].x = 260; points[2].y = 140;
ret=Polygon( hdc, points, 3 );printf("ret=%d\n",ret);
ret=TextOut(hdc, 130,10, TEXT("Hello World!"), 12);printf("ret=%d\n",ret);
EndPaint(hwnd , &ps);
DeleteObject(SelectObject(hdc , GetStockObject(WHITE_BRUSH)));
return 0;
}
return DefWindowProc(hwnd , msg , wp , lp);
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow ) {////////////
AllocConsole();///////////コンソール///////////////// ---[Debug Print]---
FILE* out = 0; freopen_s( &out, "CON", "w", stdout );
FILE* in = 0; freopen_s( &in, "CON", "r", stdin );
/// /////////Class////////////////////////
HWND hwnd; MSG msg; WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("Form1");/////////Class//////////
if (!RegisterClass(&winc)) return -1;////////Registe////////
hwnd = CreateWindow(
TEXT("Form1") , TEXT("Form1Samp") , WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT , CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);//////////////////////////////////////////LOOP/////////////
if (hwnd == NULL) return -1;
while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
//コンソール解放////////////////////////////コンソール/////// ---[Debug Print]---
fclose( out ); fclose( in ); FreeConsole(); ////Close////////
return msg.wParam;
}
///
Re: Win32 プログラミング
Posted: 2017年1月07日(土) 08:48
by Math
プロジェクト・ファイルを送って頂けるとコマンドライン・コンパイルができるので大変助かります。よろしくお願い致します。(.vcxprojファイルです)
Re: Win32 プログラミング
Posted: 2017年1月08日(日) 08:42
by 駆け出し
おはようございます。
といいうことは、ここで○○を実装、あそこで××を実装、といった具合に決めてからコードを書くということでしょうか?
プロジェクト・ファイルを送って頂けるとコマンドライン・コンパイルができるので大変助かります。よろしくお願い致します。(.vcxprojファイルです)
添付しておきます。
コピペさせて申し訳ないです。これからはプロジェクトごと送ります...
.vcxprojは送れないようなので、zipにして送ります。
また、たぶん上にあるソースとは変わっているので、今編集している状態のソースを送ります.
Re: Win32 プログラミング
Posted: 2017年1月08日(日) 08:43
by 駆け出し
vcxobj ファイル
Re: Win32 プログラミング
Posted: 2017年1月08日(日) 08:47
by 駆け出し
今現在のソースコード
Re: Win32 プログラミング
Posted: 2017年1月08日(日) 08:58
by Math
>といいうことは、ここで○○を実装、あそこで××を実装、といった具合に決めてからコードを書くということでしょうか?
そうです。設計書をもとにスケルトンを作らないでいきなりプログラムを書くといずれ破綻します。
>.vcxprojは送れないようなので、zipにして送ります。
他のトピックスでは送ったり、送られたりしてますよ。コンテキストメニューの”送る”からエディタに送ればいいはずです。でも全部をzipで貰う方がありがたいですね。
Re: Win32 プログラミング
Posted: 2017年1月08日(日) 09:03
by 駆け出し
>といいうことは、ここで○○を実装、あそこで××を実装、といった具合に決めてからコードを書くということでしょうか?
そうです。設計書をもとにスケルトンを作らないでいきなりプログラムを書くといずれ破綻します。
なるほど、自分これまで何度か破たんしてる(なんでこの処理をしているかわからなくなる)ので、これからは何を実装するか決めてから実装します。
Re: Win32 プログラミング
Posted: 2017年1月08日(日) 11:09
by Math
Windows10,VS2015で正常に動きました。
Re: Win32 プログラミング
Posted: 2017年1月08日(日) 22:13
by 駆け出し
こんばんは。
Mathさん、いろいろ教えていただいてありがとうございました。
これにて、解決とさせていただきます。