MATLAB 內建函數雖強大,但缺乏直接存取作業系統底層功能 (如螢幕截圖) 的介面。透過 MEX (MATLAB Executable) 機制,可將 C/C++ 撰寫、呼叫 Windows API 的程式編譯為 .mexw32/.mexw64 動態連結庫,在 MATLAB 中像呼叫內建函數一樣使用。本文完整示範截圖 MEX 函數的開發流程。
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[])
nlhs:輸出參數個數plhs:輸出 mxArray 指標陣列nrhs:輸入參數個數prhs:輸入 mxArray 指標陣列// 編譯: mex screensnap.c user32.lib gdi32.lib
// 使用: a = screensnap(0); imshow(a); % 0=隱藏 MATLAB 視窗, 1=包含
#include
#include
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
int cx, cy, recnum;
int dims[3];
char *pchar, *mloc;
bool bShowMatlab;
HWND hWin, hActWin;
HDC hDC, hMemDC;
RECT rect;
HBITMAP hBmp, hOldBmp;
BITMAPINFOHEADER bih;
// 1. 參數檢查
if (nrhs != 1) mexErrMsgTxt("Need 1 argument!");
if (!mxIsDouble(prhs[0])) mexErrMsgTxt("Input must be numeric!");
bShowMatlab = (*(double*)mxGetData(prhs[0]) != 0);
// 2. 取得桌面視窗 DC 與尺寸
hActWin = GetForegroundWindow(); // MATLAB 視窗
hWin = GetDesktopWindow();
hDC = GetWindowDC(hWin);
GetWindowRect(hWin, &rect);
cx = rect.right - rect.left;
cy = rect.bottom - rect.top;
// 3. 建立相容記憶體 DC 與位圖
hMemDC = CreateCompatibleDC(hDC);
hBmp = CreateCompatibleBitmap(hDC, cx, cy);
if (!hBmp) mexErrMsgTxt("CreateCompatibleBitmap failed!");
hOldBmp = (HBITMAP)SelectObject(hMemDC, hBmp);
// 4. 選擇性隱藏 MATLAB 視窗避免截到自己
if (!bShowMatlab) {
ShowWindow(hActWin, SW_HIDE);
Sleep(100); // 等待視窗消失
}
// 5. 螢幕複製到記憶體 DC
if (!BitBlt(hMemDC, 0, 0, cx, cy, hDC, 0, 0, SRCCOPY))
mexErrMsgTxt("BitBlt failed!");
// 6. 準備輸出 mxArray (cy x cx x 3, uint8)
dims[0] = cy; dims[1] = cx; dims[2] = 3;
plhs[0] = mxCreateNumericArray(3, dims, mxUINT8_CLASS, mxREAL);
pchar = (char*)mxGetData(plhs[0]);
// 7. 設定 DIB 標頭 (自下而上, 24-bit RGB)
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = cx;
bih.biHeight = -cy; // 負值 = 自上而下
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = bih.biYPelsPerMeter = 0;
bih.biClrUsed = bih.biClrImportant = 0;
// 8. 取得 DIB 像素資料
mloc = (char*)mxMalloc(cx * cy * 3);
recnum = GetDIBits(hMemDC, hBmp, 0, cy, mloc, (BITMAPINFO*)&bih, DIB_RGB_COLORS);
mexPrintf("Copied %d lines\n", recnum);
// 9. 資料重排: C 列優先 → MATLAB 列優先 (行優先)
// C: [row][col][channel] → MATLAB: [col][row][channel]
for (int k = 0; k < 3; ++k)
for (int j = 0; j < cy; ++j)
for (int i = 0; i < cx; ++i)
pchar[i*cy + j + k*cx*cy] = mloc[(j*cx + i)*3 + 2 - k]; // BGR→RGB
mxFree(mloc);
// 10. 清理 GDI 物件
SelectObject(hMemDC, hOldBmp);
DeleteDC(hMemDC);
ReleaseDC(hWin, hDC);
DeleteObject(hBmp);
// 11. 恢復 MATLAB 視窗
if (!bShowMatlab) ShowWindow(hActWin, SW_SHOW);
}
> mex screensnap.c user32.lib gdi32.lib
> a = screensnap(0); % 隱藏 MATLAB 視窗截圖
> imshow(a); % 顯示截圖
輸出為 cy x cx x 3 的 uint8 陣列,直接相容 imshow、imwrite 等影像處理函數。
| 項目 | 說明 |
|---|---|
| 參數驗證 | mexErrMsgTxt 拋出 MATLAB 可捕獲的錯誤 |
| 記憶體管理 | mxMalloc/mxFree 配合 MATLAB 記憶體管理器 |
| 陣列建立 | mxCreateNumericArray 指定維度/類型/實數 |
| 資料存取 | mxGetData 取得資料指標 |
| 像素格式轉換 | Windows DIB 為 BGR + 列優先,MATLAB 為 RGB + 行優先,需雙重轉換 |
| 視窗閃爍避免 | 截圖前 SW_HIDE MATLAB 視窗,Sleep(100) 確保重繪完成 |
rect 參數,調整 BitBlt 來源座標。EnumDisplayMonitors 取得各螢幕範圍。drawnow 顯示。GetDIBits,使用 LockBits (GDI+) 更高效。MEX 機制讓 MATLAB 無縫整合 C/C++ 與作業系統 API,極大擴展了應用範圍。本範例展示了從參數檢查、GDI 截圖、像素格式轉換到 MATLAB 陣列建構的完整流程,可作為開發其他 MEX 擴充 (如硬體控制、高效演算法、第三方庫封裝) 的範本。