第82章 メモリの有効利用


ウィンドウズのプログラムでメモリを動的に 確保するにはどうすればよいのでしょうか。 ここでは、32ビット版についての解説をします。 Windows 95では2の32乗バイト(4Gバイト)の 物理メモリまでアクセスできます。 いろいろ面倒な理屈があるのですが、ここではあまり触れず (と、いうか筆者があまり知らない)に、使い方だけを 解説します。

HGLOBAL GlobalAlloc( UINT uFlags, // フラグ DWORD dwBytes // 確保するバイト数 );

ウィンドウズ環境でメモリを動的に確保するには通常GlobalAlloc を使います。

フラグは代表的なものにGMEM_FIXEDとGMEM_MOVEABLEがあります。 16ビット時代にはGMEM_FIXEDはメモリブロックを移動できないので あまり使わないよういわれていましたが、32ビットでは 仮想アドレスが返されるので物理的にメモリブロックが移動できなくなる 訳ではないようです。従ってどんどん使ってください。

GMEM_FIXEDを使った場合、GlobalAllocは、mallocと同じように使えます。

int *p; p = (int *)GlobalAlloc(GMEM_FIXED, sizeof(int) * 500);

といったような感じで使えます。 使い終わったらGlobalFreeで解放します。

HGLOBAL GlobalFree( HGLOBAL hMem );

次に、GMEM_MOVEABLEの場合は少し面倒です。次のようにして 使います。

int *p; HGLOBAL hGlobal; hGlobal = GlobalAlloc(GMEM_MOVEABLE, sizeof(int) * 1024); p = (int *)GlobalLock(hGlobal); ..... GlobalUnlock(hGlobal); GlobalFree(hGlobal);

使う前にGlobalLockで移動しないようにロックするわけです。 なお、確保したメモリをすべて0で初期化するには GMEM_ZEROINTを使います。また、GMEM_MOVEABLE | GMEM_ZEROINT はGHNDと定義されています。

では、サンプルを見てみましょう。メニューのGMEM_MOVEABLE または、GMEM_FIXED を選択するとint型を1024個確保して、それぞれに 数値を代入します。そして、クライアント領域に それぞれの値を書き出すという単純なものです。

// mem01.rcの一部 // 自分でリソーススクリプトを作る人はwindows.hと自作ヘッダーファイル // をインクルードしてください。 /////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "メモリ(&X)" BEGIN MENUITEM "GMEM_MOVEABLE(&M)", IDM_MOVEABLE MENUITEM "GMEM_FIXED(&F)", IDM_FIXED END END

ごく普通のメニューです。

// mem01.cpp #define STRICT #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int MyMoveableMemory(HWND); int MyFixedMemory(HWND); char szClassName[] = "mem01"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; //hPrevInstが常にNULLでないときはhPrevInstのチェックをしてください if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }

これも特に説明は不要です。

//ウィンドウ・クラスの登録 //WNDCLASSEXを使えない環境ではWNDCLASSを使ってください BOOL InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメモリ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

これも、説明は不要ですね。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_MOVEABLE: MyMoveableMemory(hWnd); break; case IDM_FIXED: MyFixedMemory(hWnd); break; break; } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

メニューからIDM_MOVEABLEや、IDM_FIXEDが選択されたら それぞれ自作関数を呼び出しています。

int MyMoveableMemory(HWND hWnd) { int *p, i, j; char str[256]; char *str_org = "Number = %d"; HGLOBAL hGlobal; HDC hdc; TEXTMETRIC tm; RECT rc; hGlobal = GlobalAlloc(GHND, 1024*sizeof(int)); if (hGlobal == NULL) { MessageBox(hWnd, "メモリ確保に失敗しました", "失敗", MB_OK); return -1; } p = (int *)GlobalLock(hGlobal); if(p == NULL) { MessageBox(hWnd, "失敗", "Error", MB_OK); return -2; } for (i = 0; i < 1024; i++) { *(p + i) = i * 10; } hdc = GetDC(hWnd); GetTextMetrics(hdc, &tm); GetClientRect(hWnd, &rc); j = 0; for(i = 0; i < 1024; i++) { wsprintf(str, str_org, *(p + i)); if (j * tm.tmHeight >= (rc.bottom - rc.top)) j = 0; TextOut(hdc, 20, j * tm.tmHeight, str, strlen(str)); j++; } ReleaseDC(hWnd, hdc); GlobalUnlock(hGlobal); if(GlobalFree(hGlobal) != NULL) { MessageBox(hWnd, "メモリの解放に失敗しました", "失敗", MB_OK); return -2; } return 0; }

移動可能メモリーを確保して数値を代入して、今度は 代入した数値を読み出してクライアント領域に描画しているだけです。 GetTextMetrics関数についてはすでに 第77章で解説してあります。

int MyFixedMemory(HWND hWnd) { int *p, i, j; HDC hdc; TEXTMETRIC tm; RECT rc; char str[256], *str_org = "Number = %d"; p = (int *)GlobalAlloc(GMEM_FIXED, sizeof(int) * 1024); if (p == NULL) { MessageBox(hWnd, "メモリ確保に失敗しました", "Error", MB_OK); return -1; } for (i = 0; i < 1024; i++) *(p + i) = i * 10; hdc = GetDC(hWnd); GetTextMetrics(hdc, &tm); GetClientRect(hWnd, &rc); j = 0; for (i = 0; i < 1024; i++) { wsprintf(str, str_org, *(p + i)); if (j * tm.tmHeight >= (rc.bottom - rc.top)) j = 0; TextOut(hdc, 20, tm.tmHeight * j, str, strlen(str)); j++; } ReleaseDC(hWnd, hdc); if(GlobalFree(p) != NULL) { MessageBox(hWnd, "メモリ解放に失敗しました", "Error", MB_OK); return -2; } return 0; }

これも、メモリ確保の方法をGMEM_FIXEDにしただけであとは MyMoveableMemoryとほとんど同じです。

さて、このメモリの動的確保を利用して 第74章からの自作メモ帳を もう少しましなものに作り替えてみてください。


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Oct/11/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。