• 締切済み

WinAPIでの画像高速切り替え表示プログラム1

WinAPIを使用して、ビットマップ画像を8枚読み込み、それを連続高速表示するプログラムを作成しています。 今はSetTimerを使ってWM_TIMERを受け取ったときに画像をInvalidateRect(再描画)しています。 以下のソースで動作はするのですが、WM_TIMERは整数ミリ秒でしか設定できず、精度も悪く優先順位も遅いようなので他の方法を考えています。 画像8枚を6.25msecで切り替えて表示するというのをESCAPEするまで繰り返したいのですが...。 リフレッシュレートは160Hzにあげています。 QueryPerformanceFrequencyというものを使えばいいのかなと思っていますが、どこでどう使えばいいのか、それをどう受け取って再描画すればいいのかわかりません。 どなたかご教授お願いします。ソースファイルを書いていただけたら嬉しいです。 #include<windows.h> #define BMP_SUM8//画像の総数 #define TIMER_ID (100) // 作成するタイマの識別ID #define TIMER_ELAPSE (6) // WM_TIMERの発生間隔 LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){ HDC hdc; PAINTSTRUCT ps; HBITMAP hBitmap; int i; const char *filename[BMP_SUM]={"gazou0.bmp", "gazou1.bmp", "gazou2.bmp", "gazou3.bmp", "gazou4.bmp", "gazou5.bmp", "gazou6.bmp", "gazou7.bmp"}; static HDC hMemDC[BMP_SUM]; static BITMAP bmp; static int bmp_index;//現在の画像番号 LONG lResult; switch(uMsg) { case WM_CREATE: hdc=GetDC(hWnd); for(i=0;i<BMP_SUM;i++){ hBitmap=(HBITMAP)LoadImage(0,filename[i],IMAGE_BITMAP,0,0,LR_LOADFROMFILE); hMemDC[i]=CreateCompatibleDC(hdc); SelectObject(hMemDC[i],hBitmap); } GetObject(hBitmap,sizeof(BITMAP),&bmp); DeleteObject(hBitmap); ReleaseDC(hWnd,hdc); return 0; case WM_TIMER: if( wParam != TIMER_ID ) { break; // 識別IDが一致しないタイマメッセージはDefWindowProc()に任せる } if(++bmp_index >= BMP_SUM) bmp_index=0; for(i = 0; i < BMP_SUM; i++){ InvalidateRect( hWnd, NULL, FALSE ); } return 0; case WM_DESTROY: for(i=0;i<BMP_SUM;i++) DeleteDC(hMemDC[i]); PostQuitMessage(0); return 0; case WM_PAINT: hdc=BeginPaint(hWnd,&ps); BitBlt(hdc,0,0,bmp.bmWidth,bmp.bmHeight,hMemDC[bmp_index],0,0,SRCCOPY); EndPaint(hWnd,&ps); return 0; case WM_KEYDOWN: switch((CHAR)wParam) { case VK_ESCAPE: for(i=0;i<BMP_SUM;i++) DeleteDC(hMemDC[i]); PostQuitMessage(0); //WM_QUITメッセージを出す return 0; } } return DefWindowProc(hWnd,uMsg,wParam,lParam); } 入りきらないので2つに分けます。 続きは「WinAPIでの画像高速切り替え表示プログラム2」を見てください。

みんなの回答

  • sygh
  • ベストアンサー率76% (42/55)
回答No.7

// No.4: int main(int argc, char** argv) { ULONG_PTR token = 0; Gdiplus::GdiplusStartupInput input; Gdiplus::GdiplusStartup(&token, &input, 0); glutInit(&argc, argv); glutInitWindowSize(g_winSize.cx, g_winSize.cy); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); glutCreateWindow("Precise Flip Test"); glutDisplayFunc(OnDisplay); glutIdleFunc(OnIdle); glutReshapeFunc(OnReshape); glutKeyboardFunc(OnKeyboard); glEnable(GL_TEXTURE_2D); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearColor(0.0, 0.5, 0.5, 1.0); wchar_t fileName[1024] = {}; for (int i = 0; i < IMG_NUM; i++) { swprintf_s(fileName, L"image%02d.png", i); // 24/32bit PNG/BMP, 24bit TIFF/JPEG に対応。サイズは縦横 2^n のみ。 if (!g_texHolders[i].LoadFromFile(fileName)) { return -1; } } const BOOL isAvailableQPC = ::QueryPerformanceCounter(&g_start); assert(isAvailableQPC); glutMainLoop(); // 以下の解放処理は一応記述していますが、終了時に呼ばれません。 // GLUT では解放処理を OS に委ねています。 // WGL などを使ってすべて自前で制御するときは、明示的に解放/再作成できます。 for (int i = 0; i < IMG_NUM; i++) { g_texHolders[i].Release(); } Gdiplus::GdiplusShutdown(token); return 0; } 以上です。 Direct3Dのサンプルは書きませんのであしからず。

  • sygh
  • ベストアンサー率76% (42/55)
回答No.6

// No.3: void OnDisplay() { glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); { glLoadIdentity(); glColor3d(1, 1, 0); glDisable(GL_BLEND); glRasterPos2d(-0.9, 0.9); static char message[256]; sprintf_s(message, "%3u FPS", g_fpsValue); DrawString(GLUT_BITMAP_HELVETICA_18, message); } glPopMatrix(); glRotated(2, 0, 0, 0.25); glBindTexture(GL_TEXTURE_2D, g_texHolders[g_currentBmpIndex].GetTextureID()); glColor3d(1, 1, 1); glEnable(GL_BLEND); glBegin(GL_POLYGON); { // UV と頂点位置の設定。 const double wInv = static_cast<double>(g_texHolders[g_currentBmpIndex].GetWidth()) / g_winSize.cx; const double hInv = static_cast<double>(g_texHolders[g_currentBmpIndex].GetHeight()) / g_winSize.cy; glTexCoord2d(0, 0); glVertex2d(-wInv, +hInv); glTexCoord2d(0, 1); glVertex2d(-wInv, -hInv); glTexCoord2d(1, 1); glVertex2d(+wInv, -hInv); glTexCoord2d(1, 0); glVertex2d(+wInv, +hInv); } glEnd(); glutSwapBuffers(); } void OnIdle() { LARGE_INTEGER stop = {}, freq = {}; ::QueryPerformanceCounter(&stop); ::QueryPerformanceFrequency(&freq); const LONGLONG diffStartStop = stop.QuadPart - g_start.QuadPart; if (diffStartStop * FPS_LIMIT < freq.QuadPart) { return; } static UINT fpsUpdateCounter; const UINT FPS_UPDATE_SKIP_FRAME_COUNT = 160; if (++fpsUpdateCounter >= FPS_UPDATE_SKIP_FRAME_COUNT) { g_fpsValue = (diffStartStop > 0) ? static_cast<UINT>(freq.QuadPart / diffStartStop) : 9999; fpsUpdateCounter = 0; } ::QueryPerformanceCounter(&g_start); g_currentBmpIndex = (g_currentBmpIndex + 1) % IMG_NUM; glutPostRedisplay(); // GLUT では既定で垂直同期されるので、FPS は基本的にモニタのリフレッシュレート以上にはならない。 } void OnReshape(int width, int height) { g_winSize.cx = width; g_winSize.cy = height; } void OnKeyboard(unsigned char key, int x, int y) { if (key == 27) // Esc { exit(0); } }

  • sygh
  • ベストアンサー率76% (42/55)
回答No.5

// No.2: class TexHolder { GLuint m_textureId; UINT m_width; UINT m_height; public: TexHolder() : m_textureId(GL_INVALID_VALUE) , m_width() , m_height() { } GLuint GetTextureID() const { return m_textureId; } UINT GetWidth() const { return m_width; } UINT GetHeight() const { return m_height; } bool IsValid() const { return (m_textureId != GL_INVALID_VALUE && m_textureId != GL_INVALID_OPERATION); } bool Generate() { this->Release(); glGenTextures(1, &m_textureId); return this->IsValid(); } void Release() { if (this->IsValid()) { glDeleteTextures(1, &m_textureId); m_textureId = GL_INVALID_VALUE; m_width = 0; m_height = 0; } } bool LoadFromFile(LPCWSTR pFilePath) { if (!this->Generate()) { this->Release(); return false; } Bitmap bmp(pFilePath); if (bmp.GetLastStatus() != Ok) { this->Release(); return false; } m_width = bmp.GetWidth(); m_height = bmp.GetHeight(); const PixelFormat pf = bmp.GetPixelFormat(); if (m_width != m_height || !IsModulo2(m_width)) { this->Release(); return false; } if (pf != PixelFormat32bppARGB && pf != PixelFormat24bppRGB) { this->Release(); return false; } BitmapData bmpData; bmp.LockBits(&Rect(0, 0, m_width, m_height), ImageLockModeRead, pf, &bmpData); glBindTexture(GL_TEXTURE_2D, m_textureId); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (pf == PixelFormat32bppARGB) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmpData.Width, bmpData.Height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, bmpData.Scan0); } else { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bmpData.Width, bmpData.Height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, bmpData.Scan0); } glBindTexture(GL_TEXTURE_2D, 0); bmp.UnlockBits(&bmpData); return true; } virtual ~TexHolder() { this->Release(); } }; namespace { const int IMG_NUM = 8; const UINT FPS_LIMIT = 160; TexHolder g_texHolders[IMG_NUM]; LARGE_INTEGER g_start; UINT g_fpsValue; UINT g_currentBmpIndex; SIZE g_winSize = {800, 800}; }

  • sygh
  • ベストアンサー率76% (42/55)
回答No.4

OpenGLの話は長くなるので、GLUTとGDI+による簡単なソースのみにとどめて、詳しい解説は割愛します。 入門の鉄板サイトを紹介しておきますので、あとはご自分で調べてください。 OpenGLの勉強をこれから始める人、あるいはOpenGL用ウィンドウの生成などの面倒な部分を省きたい場合はGLUTを使うと楽ですが、Windows専用でもいいから細かい制御までしたい場合や、MFCなどのGUIフレームワークと連携したい場合はWGLを使ってレンダリング コンテキストを直に作成したりします。 例えば、GLUTのglutMainLoop()は一度呼び出すと制御を返さないので、終了するにはウィンドウのクローズ ボタンを押すか、exit()で強制終了させる以外になくなります。解放処理も記述できません。 他にも、GLEWを使うと、垂直同期やマルチサンプル アンチエイリアス、GLSLといった上位機能へのアクセスが楽になります。 OpenGLおよび関連ライブラリはほとんど全てがオープンソースでクロス プラットフォームなので、組込機器や技術論文などによく使われていますが、Windows限定であればDirect3Dのほうが圧倒的に情報量が多く、大抵の場合OpenGLより高速で、さらにC++との相性が良いですが、COMの知識が必須となります。 グラフィックAPIはすさまじく変化が激しいので、人に聞くよりも自ら情報収集に努めないと、はっきり言ってついていけないです。映画やゲーム、アニメなどを見れば一目瞭然ですが、この10年で飛躍的に進化を遂げた分野のうちのひとつです。 http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html http://wisdom.sakura.ne.jp/system/opengl/index.html http://glew.sourceforge.net/ http://msdn.microsoft.com/en-us/library/ms533798.aspx // No.1: #include <cstdlib> #include <GL/glut.h> #include <Windows.h> #include <GdiPlus.h> #include <vector> #include <cassert> #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; template<typename T> inline bool IsModulo2(const T& x) { return !(x & (x - 1)); } void DrawString(void* font, const char* str) { while (*str) { glutBitmapCharacter(font, *str); ++str; } }

  • sygh
  • ベストアンサー率76% (42/55)
回答No.3

// No.3 開始。 LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static const LPCTSTR fileNames[BMP_NUM] = { _T("image00.bmp"), _T("image01.bmp"), _T("image02.bmp"), _T("image03.bmp"), _T("image04.bmp"), _T("image05.bmp"), _T("image06.bmp"), _T("image07.bmp"), }; switch (uMsg) { case WM_CREATE: { HDC hdc = ::GetDC(hWnd); for (UINT i = 0; i < BMP_NUM; i++) { g_bmpHolders[i].LoadBitmapFromFile(fileNames[i], hdc); } RECT clientRect = {}; ::GetClientRect(hWnd, &clientRect); const int clientWidth = clientRect.right - clientRect.left; const int clientHeight = clientRect.bottom - clientRect.top; const bool isBackBufCreated = g_backBuffer.UpdateBackBufferBitmap(hdc, clientWidth, clientHeight); assert(isBackBufCreated); ::ReleaseDC(hWnd, hdc); } return 0; case WM_DESTROY: ::PostQuitMessage(0); return 0; case WM_KEYDOWN: switch (wParam) { case VK_ESCAPE: ::PostQuitMessage(0); return 0; default: break; } } return ::DefWindowProc(hWnd, uMsg, wParam, lParam); } // No.3 終了。 ちなみに Direct3D だとダブルバッファリングやモニタのリフレッシュレートとの垂直同期(VSync)は、ほぼ自動でやってくれます。

progshoshinsha
質問者

お礼

またまたソースを書いてくださってありがとうございます。 Direct3DというのはDirectXの一部(?)と聞きました。 OpenGLは少しやったことがあるのですが、Direct3Dについては何もわかりません。 OpenGLでも可能でしょうか。とは言ってもOpenGLもさわりだけしかやっておらずさわりで挫折しました。 OpenGLでも可能ならば、どのようにすればいいかヒントを頂けますでしょうか。 本当に何度もすみません。 よろしくお願いします。

  • sygh
  • ベストアンサー率76% (42/55)
回答No.2

// No.2 開始。 namespace { const UINT BMP_NUM = 8; // 画像の総数。 //const UINT FPS_LIMIT = 60; const UINT FPS_LIMIT = 160; // 1000[ms] / 6.25[ms] BmpHolder g_backBuffer; BmpHolder g_bmpHolders[BMP_NUM]; UINT g_fpsValue; // ブラシを使わずに単色で塗りつぶす。 void FillSolidRect(HDC hdc, COLORREF clr, const RECT* pRect) { ::SetBkColor(hdc, clr); ::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, pRect, NULL, 0, NULL); } void RenderMyObjects(HDC hdc, const RECT* pCanvasRect) { static UINT currentBmpIndex; // 現在の画像番号。 { FillSolidRect(hdc, RGB(0, 128, 128), pCanvasRect); g_bmpHolders[currentBmpIndex].Render(hdc, 30, 30); static TCHAR message[256]; _stprintf_s(message, _T("%3u FPS"), g_fpsValue); RECT rect = { 4, 4, 4, 4 }; ::DrawText(hdc, message, _tcslen(message), &rect, DT_NOCLIP); } currentBmpIndex = (currentBmpIndex + 1) % BMP_NUM; } // アイドル時に描画する。 void OnIdle(HWND hWnd) { HDC hdc = ::GetDC(hWnd); { // 直接ウィンドウの DC に描画すると、描画の過程が見えてしまうのでフリッカー(ちらつき)が発生する。 // フリッカー防止のため、一旦バックバッファ(裏画面)用のビットマップに描画し、 // その後バックバッファをフロントバッファ(表画面)であるウィンドウに転送する。 RECT clientRect = {}; ::GetClientRect(hWnd, &clientRect); RenderMyObjects(g_backBuffer.GetMemDC(), &clientRect); ::BitBlt(hdc, 0, 0, g_backBuffer.GetWidth(), g_backBuffer.GetHeight(), g_backBuffer.GetMemDC(), 0, 0, SRCCOPY); } ::ReleaseDC(hWnd, hdc); } } // No.2 終了。

  • sygh
  • ベストアンサー率76% (42/55)
回答No.1

プログラム2のお礼で「ダブル バッファリング」に関して質問を受けたので、こちらに記載します。 インクルードとメイン関数は前回と同じです。 ちなみに画面のちらつきは、フレームレートというよりは描画途中の過程が表画面に見えてしまうことが原因です。実際、前回提示したコードでは、FPS表示部分は直接描画しているため、微妙にちらついているのが分かります。一旦裏画面に描画して、一気に表画面に転送するようにすると、ちらつきが見えなくなります。 // No.1 開始。 class BmpHolder { BITMAP m_bmpInfo; HBITMAP m_hBitmap; HDC m_hMemDC; bool m_isBackBuffer; public: BmpHolder() : m_bmpInfo() , m_hBitmap() , m_hMemDC() , m_isBackBuffer() {} virtual ~BmpHolder() { this->Release(); } LONG GetWidth() const { return m_bmpInfo.bmWidth; } LONG GetHeight() const { return m_bmpInfo.bmHeight; } HDC GetMemDC() const { return m_hMemDC; } // 描画されるビットマップをファイルからロードする。 bool LoadBitmapFromFile(LPCTSTR pFileName, HDC hdc) { this->Release(); m_hBitmap = static_cast<HBITMAP>(::LoadImage(NULL, pFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE)); assert(m_hBitmap != NULL); if (m_hBitmap) { m_isBackBuffer = false; ::GetObject(m_hBitmap, sizeof(BITMAP), &m_bmpInfo); m_hMemDC = ::CreateCompatibleDC(hdc); assert(m_hMemDC != NULL); ::SelectObject(m_hMemDC, m_hBitmap); return true; } else { return false; } } // バックバッファ用のビットマップを更新する。 // ウィンドウの作成時・リサイズ時に明示的に呼び出すか、フレームの描画前に毎回呼び出す。 bool UpdateBackBufferBitmap(HDC hdc, int newWidth, int newHeight) { // 現在の幅・高さと同じバックバッファであれば更新しない。 if (this->GetWidth() == newWidth && this->GetHeight() == newHeight && m_isBackBuffer) { return true; } this->Release(); m_hBitmap = ::CreateCompatibleBitmap(hdc, newWidth, newHeight); assert(m_hBitmap != NULL); if (m_hBitmap) { m_isBackBuffer = true; ::GetObject(m_hBitmap, sizeof(BITMAP), &m_bmpInfo); m_hMemDC = ::CreateCompatibleDC(hdc); assert(m_hMemDC != NULL); ::SelectObject(m_hMemDC, m_hBitmap); return true; } else { return false; } } void Render(HDC hdc, int posX, int posY) { if (m_hMemDC) { ::BitBlt(hdc, posX, posY, this->GetWidth(), this->GetHeight(), m_hMemDC, 0, 0, SRCCOPY); } } void Release() { if (m_hMemDC) { ::DeleteDC(m_hMemDC); } if (m_hBitmap) { ::DeleteObject(m_hBitmap); } m_bmpInfo = BITMAP(); m_isBackBuffer = false; } }; // No.1 終了。

関連するQ&A

  • BitBlt関数について

    お世話になっております。   またまた質問ですが、   画像を表示したいので、BitBlt関数を使ってみたんですが、   エラーもなく、実行できました。   しかし、画像が表示されません。   サイトや本を何回も見て、間違いがないか見たんですけど、間違いがまったく見当たりません。   LRESULT CALLBACK VisualProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch(uMsg) { case WM_CREATE: memdc = CreateCompatibleDC( NULL ); hBitmap = ( HBITMAP )LoadImage( NULL , TEXT("player.bmp") , IMAGE_BITMAP , 0 , 0 , LR_LOADFROMFILE | LR_CREATEDIBSECTION ); SelectObject(memdc, hBitmap); return 0; case WM_PAINT: hdc = BeginPaint(VisualWnd,&ps); BitBlt(hdc, 30,30, 34, 32, memdc, 0, 0, SRCCOPY); EndPaint(VisualWnd,&ps); return 0; .............................................................................. 画像は、リソースで追加→既存の項目→ピクチャ でやりました。 それで、ピクチャからプロジェクトのファイルに移動させました。   それでもできません。     一体どのようにやればいいのでしょうか。 わかりやすく教えてください。

  • なぜhButton1ボタンからのWM_COMMANDはフックできてクライアントエリアのWM_RBUTTONDOWNはフックできないのでしょうか?

    #define STRLBUTTON TEXT("マウス左ボタンが押されました from mainProc") #define STRRBUTTON TEXT("マウス右ボタンが押されました from my_HookProc") #define STRCOMMAND TEXT("ボタンが押されました") HWND hButton1; LRESULT CALLBACK my_HookProc(int nCode, WPARAM wParam, LPARAM lParam) { CWPRETSTRUCT *pcwpRetStruct = (CWPRETSTRUCT *)lParam; HDC hDC; if(nCode==HC_ACTION) { hDC = GetDC(pcwpRetStruct->hwnd); switch(pcwpRetStruct->message) { case WM_COMMAND: TextOut(hDC, 10, 10, STRCOMMAND, strlen(STRCOMMAND)); break; case WM_RBUTTONDOWN: TextOut(hDC, 10, 10, STRRBUTTON, strlen(STRRBUTTON)); break; } ReleaseDC(pcwpRetStruct->hwnd, hDC); } return CallNextHookEx(NULL, nCode, wParam, lParam); } LRESULT CALLBACK mainProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HHOOK hHook; HDC hDC; switch(uMsg) { case WM_DESTROY: UnhookWindowsHookEx(hHook); PostQuitMessage(0); return 0; case WM_CREATE: hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, my_HookProc, NULL, GetCurrentThreadId() ); if(!hHook) MessageBox(NULL, "hooking failed", NULL, MB_OK); hButton1 = CreateWindow( "BUTTON", "hButton1", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 40, 100, 20, hWnd, NULL, ((LPCREATESTRUCT)lParam)->hInstance, NULL ); return 0; case WM_LBUTTONDOWN: hDC = GetDC(hWnd); TextOut(hDC, 10, 10, STRLBUTTON, strlen(STRLBUTTON)); ReleaseDC(hWnd, hDC); return 0; } return DefWindowProc(hWnd, uMsg, wParam, lParam); }

  • WinAPIでスクリーン画像を映し続けるプログラムその2

    タイマー1秒ごとに画面を更新させたいのですが、リージョンの範囲しか更新されないため、ウインドウを最上層に置いておくと、画面がかわりません。下記ソースはそれを確かめるため、タイマーで更新する画面を100×100pixlから1秒ごとに徐々に大きくしていったところ、やはりペイントが違う画面で覆った部分しかそのとおり更新されていませんでした。参考書だとこんな感じでできる気がするのですが、InvalidateRect( hwnd, NULL, TRUE );のところでウインドウ全体を更新しなければいけない範囲にするべきだと思うのですが、具体的な解決策がわかりません。 お力を貸していただけると助かります。 #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void getScreenShot(HBITMAP hBmpShot, int iX, int iY, int iWidth, int iHeight) ; /*ビットマップハンドル*/ static HBITMAP _bmpShot = 0; int popo=100; int _iWidth=1024, _iHeight=400; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; /* ウインドウクラス設定 */ wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "vcshot"; RegisterClass(&wndclass); /* メインウインドウ作成 */ HWND hwMain = CreateWindow("vcshot", "", WS_POPUP | WS_VISIBLE , 0,0, _iWidth, _iHeight, NULL, NULL, hInstance, NULL); ShowWindow(hwMain, iCmdShow); /* メッセージループ */ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { switch (iMsg) { case WM_CREATE: { /*先に外側で作成してしまう方が得策かもしれない*/ HDC hDC = ::GetDC(0); _bmpShot = ::CreateCompatibleBitmap(hDC, _iWidth, _iHeight); ::ReleaseDC(0, hDC); /* スクリーンショット取得 */ getScreenShot(_bmpShot, 0, 0, _iWidth, _iHeight); SetTimer(hwnd , 1 , 1000 , NULL); return 0; } case WM_TIMER:{ popo=popo+20; getScreenShot(_bmpShot, 0, 0, popo, popo); InvalidateRect( hwnd, NULL, TRUE ); return 0;} case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps); /* ビットマップが作成されていれば描画 */ if(_bmpShot != NULL) { BITMAP bmp; /*この関数でビットマップから、詳細を知る事が出来る*/ ::GetObject(_bmpShot, sizeof(BITMAP), &bmp); /*以下決まり文句*/ HDC _hdcShot = ::CreateCompatibleDC(0); ::SelectObject(_hdcShot, _bmpShot); BitBlt(hdc, 0, 0, _iWidth, _iHeight, _hdcShot, 0, 0, SRCCOPY); /*使い終えたら直に閉じる*/ ::DeleteDC(_hdcShot); } EndPaint(hwnd, &ps); return 0; } case WM_DESTROY : /* ビットマップが作成されていたら関連リソースを削除 */ if(_bmpShot != NULL) { /*SelectObject(_hdcShot, _bmpOld);*/ DeleteObject(_bmpShot); /*DeleteObject(_hdcShot);←よく見ると、デバイスコンテキストに対してDeleteObjectを使用しています。*/ } PostQuitMessage(0); return 0; } return DefWindowProc (hwnd, iMsg, wParam, lParam); } void getScreenShot(HBITMAP hBmpShot, int iX, int iY, int iWidth, int iHeight) { /* キャプチャサイズを保存 _iWidth = iWidth; _iHeight = iHeight; */ /* 画面のデバイスコンテキスト取得 */ HDC hdcScreen = GetDC(0); /* ビットマップ描画用デバイスコンテキスト作成 */ HDC _hdcShot = CreateCompatibleDC(hdcScreen); /* スクリーンショット保存用ビットマップ作成 */ /*_bmpShot = CreateCompatibleBitmap(hdcScreen, iWidth, iHeight);*/ /* デバイスコンテキストにビットマップを設定 */ /*_bmpOld = (HBITMAP)*/SelectObject(_hdcShot, hBmpShot); /* 画面上の領域をビットマップに描く */ BitBlt(_hdcShot, 0, 0, iWidth, iHeight, hdcScreen, 0, 0, SRCCOPY); /* 画面のデバイスコンテキスト解放 */ ReleaseDC(0, hdcScreen); /*使用したら直に閉じる*/ ::DeleteDC(_hdcShot); }

  • BMPの保存

    プリントスクリーンをして、それをBMP形式で保存しようとしているのですが、保存後のファイルを開いてウィンドウを動かすと、それにつれ逐次、画像が更新されていきます。 この問題の原因がわかる方、助言をお願いします。 プログラムはC++で作成しています。 下にソースを載せています。 宜しくお願いします。 #include<windows.h> LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; static BITMAPINFO bmpInfo; static LPDWORD lpPixel; static HBITMAP hBitmap; static HDC hMemDC; HWND desktop; RECT rc; static int width,height; switch(uMsg) { case WM_CREATE: //スクリーンの情報を得る desktop=GetDesktopWindow(); GetWindowRect(desktop,&rc); width=rc.right; height=rc.bottom; //DIBの情報を設定する bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth=width; bmpInfo.bmiHeader.biHeight=height; bmpInfo.bmiHeader.biPlanes=1; bmpInfo.bmiHeader.biBitCount=32; bmpInfo.bmiHeader.biCompression=BI_RGB; //DIBSection作成 hdc=GetDC(hWnd); hBitmap=CreateDIBSection(hdc,&bmpInfo,DIB_RGB_COLORS,(void**)&lpPixel,NULL,0); hMemDC=CreateCompatibleDC(hdc); SelectObject(hMemDC,hBitmap); ReleaseDC(hWnd,hdc); //スクリーンをDIBSectionにコピー hdc=GetDC(desktop); BitBlt(hMemDC,0,0,width,height,hdc,0,0,SRCCOPY); ReleaseDC(desktop,hdc); return 0; case WM_DESTROY: //自らlpPixelを解放するべからず DeleteDC(hMemDC); DeleteObject(hBitmap); //BMPを削除した時、lpPixelも自動的に解放される PostQuitMessage(0); return 0; case WM_PAINT: hdc=BeginPaint(hWnd,&ps); //表画面へ転送 BitBlt(hdc,0,0,width,height,hMemDC,0,0,SRCCOPY); EndPaint(hWnd,&ps); return 0; } SaveBitmap((HWND)hWnd,"PrintScreen.BMP"); return DefWindowProc(hWnd,uMsg,wParam,lParam); } int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR lpCmdLine,int nCmdShow) { WNDCLASS wc; MSG msg; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL,IDI_APPLICATION); wc.hCursor = LoadCursor(NULL,IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = __FILE__; if(!RegisterClass(&wc)) return 0; HWND hWnd=CreateWindow( __FILE__,"スクリーンキャプチャ", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT,CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT, NULL,NULL,hInstance,NULL); if(hWnd==NULL) return 0; BOOL bRet; while((bRet=GetMessage(&msg,NULL,0,0))!=0){ if(bRet==-1) break; DispatchMessage(&msg); } return (int)msg.wParam; }

  • WinAPI Directshow 動画

    WindowsプログラミングでDirectshowを使って動画処理をしています。 最終的には、動画処理した結果をウィンドウに順に表示させていくプログラムを作りたいです。 そのため、まずは、下の手順のようなプログラムを現在作っています。 (1) 動画を1フレームごとに取り込む (2) 情報をビットマップに書き出す (3) ビットマップを読み込む (4) ウィンドウに描画する (5) (1)~(4)を繰り返す プログラムはVisualStudio2005を使い、ウィンドウズアプリケーションで作成しています。 ウィンドウにビットマップが表示されてほしいのですが、何も表示されません。 2週間悩んでいますが、解決しません;; 以下にソースを貼らせていただきますので、アドバイスいただきたいです。 ※ソースの量が多いので、WM_TIMER, WM_PAINTの部分を中心に載せています。  (その他の部分は、最初から書かれているソースからほぼ変更していないです)  なお、WM_TIMER部分については、文字数の関係から別アカウントから回答1に書かせていただきます。 ----------------------- <追加グローバル宣言> int douga_flag=0; //メニューで動画処理を選ぶと1になります IGraphBuilder *pigb = NULL; /* フィルタグラフ用 */ IMediaControl *pimc = NULL; IMediaSeeking *pims = NULL; IBaseFilter *pibf = NULL; /* サンプルグラブ用 */ ISampleGrabber *pisg = NULL; IVideoWindow *pivw =NULL; AM_MEDIA_TYPE amt; HRESULT hr; OAFilterState fs; long n; LONGLONG nn1, flame_number; CImage1 gazou; /* ビットマップ画像のクラス */ // gazou.width(横サイズ), gazou.height(縦サイズ), gazou.bmmap_data(画像情報の入った配列) /* 動画→lpBmpDataに情報がはいる */ typedef struct { HINSTANCE hi; int x; // 表示開始位置 int y; HWND hwnd; // 自分のウィンドウハンドル BYTE *lpBmpData; // BMPのデータ部分 BITMAPINFOHEADER bih; } IMG0; IMG0 img00; ----------------------- <WndProc(主に WM_PAINT のソース)> LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; int id, ww, hh; PAINTSTRUCT ps; HBITMAP hBmp, hBmpOld; BITMAP bmp_info; HDC hdc, hdc_mem; switch (message) {  ...省略... case WM_TIMER: //文字数の関係から別アカウントから回答1に書きます break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); hdc_mem = CreateCompatibleDC(hdc); hBmp = LoadBitmap(hInst, TEXT("bitmap.bmp")); GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info); ww = bmp_info.bmWidth; hh = bmp_info.bmHeight; hBmpOld = (HBITMAP)SelectObject(hdc_mem, hBmp); BitBlt(hdc, 0, 0, ww, hh, hdc_mem, 0, 0, SRCCOPY); SelectObject(hdc_mem, hBmpOld); DeleteDC(hdc_mem); DeleteObject( hBmp ); // ロードしたビットマップを削除する EndPaint(hWnd, &ps); break; ...省略... }

  • WinAPIでスクリーン画像を映し続けるプログラム

    通常のスクリーン画面を加工して更新し続ける、たとえば拡大ツールのようなプログラムを作るにあたりまして、 ひとまずスクリーンショットを1秒ごとに更新し続ける動作をさせたいのですが、うまくいきません。 下記のソースはWEBのサンプルをお借りし参考書を見ながら 作りました。 ずっと動かしているとメモリ使用量が上がってしまったりします。 ご指導いただけると助かります。 スクリーン画像を映し続けるプログラム/** 画面キャプチャし続ける */ #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void getScreenShot(int iX, int iY, int iWidth, int iHeight); HBITMAP _bmpShot = NULL, _bmpOld; HDC _hdcShot = NULL; int _iWidth, _iHeight; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ MSG msg; WNDCLASS wndclass; /* ウインドウクラス設定 */ wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "vcshot"; RegisterClass(&wndclass); /* メインウインドウ作成 */ HWND hwMain = CreateWindow("vcshot", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 680, NULL, NULL, hInstance, NULL); ShowWindow(hwMain, iCmdShow); /* メッセージループ */ while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; switch (iMsg) { case WM_CREATE: /* スクリーンショット取得 */ getScreenShot(0, 0, 600, 480); SetTimer(hwnd , 1 , 100 , NULL); return 0; case WM_TIMER: getScreenShot(0, 0, 600, 480); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); /* ビットマップが作成されていれば描画 */ if (_bmpShot != NULL) { BitBlt(hdc, 0, 0, _iWidth, _iHeight, _hdcShot, 0, 0, SRCCOPY); } EndPaint(hwnd, &ps); return 0; case WM_DESTROY : /* ビットマップが作成されていたら関連リソースを削除 */ if (_bmpShot != NULL) { SelectObject(_hdcShot, _bmpOld); DeleteObject(_bmpShot); DeleteObject(_hdcShot); } PostQuitMessage(0); return 0; } return DefWindowProc (hwnd, iMsg, wParam, lParam); } void getScreenShot(int iX, int iY, int iWidth, int iHeight) { /* キャプチャサイズを保存 */ _iWidth = iWidth; _iHeight = iHeight; /* 画面のデバイスコンテキスト取得 */ HDC hdcScreen = GetDC(0); /* スクリーンショット保存用ビットマップ作成 */ _bmpShot = CreateCompatibleBitmap(hdcScreen, iWidth, iHeight); /* ビットマップ描画用デバイスコンテキスト作成 */ _hdcShot = CreateCompatibleDC(hdcScreen); /* デバイスコンテキストにビットマップを設定 */ _bmpOld = (HBITMAP)SelectObject(_hdcShot, _bmpShot); /* 画面上の領域をビットマップに描く */ BitBlt(_hdcShot, 0, 0, iWidth, iHeight, hdcScreen, 0, 0, SRCCOPY); /* 画面のデバイスコンテキスト解放 */ // ReleaseDC(NULL, hdcScreen); }

  • デバイスコンテキストの宣言に関するエラー?について

    Run-Time Check Failure #3 - The variable 'hMemDC' is being used without being defined. というエラーが出ます。 自分としては、WM_CREATEの処理で、初期化を行っているつもりなのですが・・・ 力を貸して頂ければ有難いです。よろしくお願いします。 ちなみに、関係のありそうな部分だけ掲載しています。 この部分だけ残して、他の部分をコメントアウトしてコンパイルしても、同じエラーメッセージが出ました。 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; RECT rect; HDC hDC; PAINTSTRUCT ps; HDC hMemDC; HBITMAP hMemBmp; HDC hMjDC; HBITMAP hMjBmp; BITMAP MjBmp; /* POINT posMJ; int MJshot; POINT posMJshot;*/ switch (message) { case WM_CREATE: GetClientRect(hWnd, &rect); // クライアント領域の大きさをrectに格納 hDC = GetDC(hWnd); hMemDC = CreateCompatibleDC(hDC); // hDCと互換性をとるメモリデバイスコンテキストhMemDCを構築 hMemBmp = CreateCompatibleBitmap(hMemDC, rect.right, rect.bottom); hMjDC = CreateCompatibleDC(hMemDC); // hMemDCと互換性をとるメモリデバイスコンテキストhMjDCを構築 hMjBmp = (HBITMAP)LoadImage( // ビットマップイメージMYJET(リソース)をhMjBmpに設定 (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), MAKEINTRESOURCE(IDB_MYJET), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR ); SetTimer(hWnd, 1, 10, NULL); // 100分の1秒毎にWM_TIMERメッセージを送るタイマー1をセット break; case WM_PAINT: hDC = BeginPaint(hWnd, &ps); // hDCにディスプレイのデバイスコンテキストのハンドルを格納 BitBlt( // hDCにバックバッファhMemDCを転送 hDC, 0, 0, rect.right, rect.bottom, hMemDC, 0, 0, SRCCOPY ); EndPaint(hWnd, &ps); break;

  • windowsAPI 画像の表示

    画像が表示できません。   表示は   LoadBitmap() と BitBlt() だと思うんですけど。。。   何回やってもできません。   LRESULT CALLBACK WindowProc ( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { HMENU hMenu, hSubMenu; MENUITEMINFO mii; HDC hdc,hBuffer; RECT rect; TEXTMETRIC tm; PTSTR pstr; PAINTSTRUCT ps; static HFONT hFont; static HDC hMemDC; static HBITMAP hBitmap,hPrevBitmap; static BITMAP bitmap; switch(uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_CLOSE: if(MessageBox(hWnd,TEXT("ゲームを終了しますか?"),TEXT("アスパラインフォメーション"),MB_YESNO | MB_ICONINFORMATION) == IDYES){ DestroyWindow(hWnd); } return 0; case WM_COMMAND: //有効なメニューが選択され、メニューの選択が終了した switch(LOWORD(wParam)) { case IDM_NEW: pstr = TEXT("新しくゲームを始めますか?"); if(MessageBox(hWnd , pstr , TEXT("アスパラインフォメーション") , MB_YESNO | MB_ICONINFORMATION) == IDYES){ gamesystem = 1; InvalidateRect(hWnd,NULL,TRUE); } break; case IDM_OPEN: pstr = TEXT("ゲームをロードしますか?"); if(MessageBox(hWnd , pstr , TEXT("アスパラインフォメーション") , MB_YESNO | MB_ICONINFORMATION) == IDYES){ } break; case IDM_CLOSE: pstr = TEXT("ゲームを終了しますか?"); if(MessageBox(hWnd , pstr , TEXT("アスパラインフォメーション") , MB_YESNO | MB_ICONINFORMATION) == IDYES){ DestroyWindow(hWnd); } break; default: return 0; } return 0; case WM_MENUSELECT: //メニュー項目が選択された switch (LOWORD(wParam)) { case IDM_NEW: pstr = TEXT("新しくゲームを開始します"); break; case IDM_OPEN: pstr = TEXT("ゲームをロードします"); break; case IDM_CLOSE: pstr = TEXT("ゲームを終了します"); break; default: pstr = TEXT("ポップアップメニューが選択されています"); break; } hdc = GetDC(hWnd); GetClientRect(hWnd , &rect); GetTextMetrics(hdc , &tm); Rectangle(hdc , 0 , rect.bottom - tm.tmHeight * 2, rect.right , rect.bottom); TextOut(hdc , 5 , rect.bottom - tm.tmHeight * 1.5 , pstr , lstrlen(pstr)); ReleaseDC(hWnd , hdc); return 0; case WM_CREATE: mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_TYPE | MIIM_ID; mii.fType = MFT_STRING; hMenu = CreateMenu(); hSubMenu = CreatePopupMenu(); mii.dwTypeData = TEXT("ニューゲーム(&N)"); mii.wID = IDM_NEW; InsertMenuItem(hSubMenu , 0 , TRUE , &mii); mii.dwTypeData = TEXT("ロード(&O)"); mii.wID = IDM_OPEN; InsertMenuItem(hSubMenu , 1 , TRUE , &mii); mii.dwTypeData = TEXT("終了(&X)"); mii.wID = IDM_CLOSE; InsertMenuItem(hSubMenu , 2 , TRUE , &mii); mii.fMask = MIIM_TYPE | MIIM_SUBMENU; mii.hSubMenu = hSubMenu; mii.dwTypeData = TEXT("ファイル(&F)"); InsertMenuItem(hMenu , 0 , TRUE , &mii); SetMenu(hWnd , hMenu); hFont = CreateFont( 40,0,0,0,750, FALSE,FALSE,FALSE, SHIFTJIS_CHARSET,OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY, VARIABLE_PITCH | FF_ROMAN,NULL); hBitmap = LoadBitmap( ((LPCREATESTRUCT)lParam)->hInstance , TEXT("KYARA") ); return 0; case WM_KEYDOWN: InvalidateRect(hWnd,NULL,FALSE); gamesystem += 1; return 0; case WM_PAINT: hdc = BeginPaint(hWnd,&ps); hBuffer = CreateCompatibleDC(hdc); SelectObject(hBuffer , hBitmap); BitBlt(hdc , 100 , 100 , 34 , 32 , hBuffer , 70 , 80 , SRCCOPY); DeleteDC(hBuffer); if(gamesystem == 1){ ・・・・・・・・・・・・・・・・・   といった感じです。   間違えがあれば、ご指摘お願いします。   また、わかりやすいサンプルコードを載せていただけたら嬉しいです。

  • ソースを見てください。お願いします

    始めまして、Missing0001と申します。 ウィンドウズプログラムをしていて、 他のところは正常に動いているのにMoveWindow関数の場所だけ思うように動きません。 どなたかソースの間違っているところを指摘していただけないでしょうか? したいことはButtonのマウスでの移動です。 BtProc・・・サブクラス(Button)のプロシージャ DrawRect・・・Buttonのサイズの四角形を描いたり消したりするもの LRESULT CALLBACK BtProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { static BOOL Btn_Flag = false; static POINTS end,old_end; char Str[512]; switch(uMsg) { case WM_LBUTTONDOWN: Btn_Flag = true; old_end = MAKEPOINTS(lParam); DrawRect(hWnd,old_end); break; case WM_MOUSEMOVE: if(Btn_Flag) { end = MAKEPOINTS(lParam); DrawRect(hWnd,old_end); DrawRect(hWnd,end); old_end = end; } else { return CallWindowProc(fnBtProc,hWnd,uMsg,wParam,lParam); } break; case WM_LBUTTONUP: if(Btn_Flag) { DrawRect(hWnd,end); MoveWindow(hWnd,end.x,end.y,50,30,true); ←ここがうまくいかない Btn_Flag = false; } else { return CallWindowProc(fnBtProc,hWnd,uMsg,wParam,lParam); } } return CallWindowProc(fnBtProc,hWnd,uMsg,wParam,lParam); }

  • 別関数に渡す変数のポインタが難しい

    構造体初心者で、ポインタもよく分かっていません。 12, 8 と表示するつもりのソースだけど、実行結果は違っていました。 直してください。 ソースは長いけど、スケルトンに構造体とTextOutを付けたぐらいのものです。 #include <windows.h> struct MYOBJ { int a; int b; }; LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int) { MSG msg; WNDCLASS wc; 投稿できなかったからここを消しました。 wc.lpszClassName = "x"; if(RegisterClass(&wc) == 0)return 0; HWND hWnd = CreateWindow("x","",WS_OVERLAPPEDWINDOW|WS_VISIBLE,0,0,320,240,NULL,NULL,hInst,NULL); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } void myfunc(struct MYOBJ *obj)//ここが違うかもしれません。 { obj->a += 2; obj->b -= 2; } LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hDC; PAINTSTRUCT ps; char buf[30]; switch(uMsg) { case WM_CREATE: struct MYOBJ myobj; myobj.a = 10; myobj.b = 10; myfunc(&myobj);//ここが違うかもしれません。 return 0; case WM_PAINT: hDC = BeginPaint(hWnd, &ps); wsprintf(buf, "%d, %d", myobj.a, myobj.b); TextOut(hDC, 0, 0, buf, strlen(buf)); EndPaint(hWnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; }

専門家に質問してみよう