比 较 复 杂 的 数 据 库 中 一 般 会 有 位 图 数 据( 比 如 相 片)。 虽 然 这 类“OLE 对 象” 的 插 入、 删 除 以 及 替 换 操 作 在ACCESS 里 容 易 实 现, 在VC 中 却 显 得 复 杂 而 且 颇 费 周 折。 以 下 把 作 者 用VC 处 理ACCESS 数 据 库 中 的 位 图 数 据 的 体 会 简 单 叙 述 一 下, 以 请 教 于 大 家。
在 CdaoRecordset 派 生 类 的 对 象 中,VC 自 动 为ACCESS 的“OLE 对 象” 域 生 成 一 个CLongBinary 对 象。 该 类 虽 然 较 简 单, 在 程 序 里 却 需 要 使 用 全 局 函 数GlobalAlloc() 和GlobalFree() 处 理 与 它 的 内 存 句 柄m_hData 有 关 操 作, 访 问 数 据 前 后 要 调 用GlobalLock() 和GlobalUnlock(), 而 且 还 要 给 它 的m_dwDataLength 赋 值, 使 用 起 来 相 对 复 杂, 所 以 一 般 推 荐 使 用CByteArray 类。 这 只 需 要 在CdaoRecordset 派 生 类 对 象 的 数 据 说 明 里 修 改 一 下, 并 把DoFieldExchange() 里 的DFX_LongBinary() 改 成DFX_Binary() 即 可。
作 者 定 义 了 一 个 以CObject 为 基 类 的CDib 类(CDaoRecordView 的 派 生 类 里 定 义 了CDib 对 象 成 员m_DIB), 其 中 包 括 一 下 成 员 和 方 法:
CByteArray m_bufDIB; BOOL Create(CByteArray& ba); BOOL Create(CFile& bmpFile); BOOL Paint(HDC hDC);
m_bufDIB 是 存 储 位 图 数 据 的 缓 冲 区。 为 简 便 起 见, 它 不 包 含 包 装 信 息 和BITMAPFILEHEADER 结 构。 这 样 对 数 据 库 更 新 后, 原 有 的“OLE 对 象” 类 型 将 变 成“ 长 二 进 制 数 据”, 不 能 在ACCESS 里 查 看 了。
第 一 个Create() 重 载 方 法 的 参 数ba 是 记 录 集 的 位 图 数 据( 比 如m_image), 使 用CByteArray::Copy() 把 数 据 复 制 给m_bufDIB; 第 二 个Create() 方 法 的 参 数bmpFile 是 已 打 开 的 位 图 文 件, 使 用CFile::ReadHuge() 把 文 件 里 的 数 据 读 入m_bufDIB( 放 弃 前 面 的BITMAPFILEHEADER 结 构):
DWORD dwBufSize; dwBufSize = bmpFile.GetLength();// 获 得 文 件 长 度 bmpFile.Seek((long)sizeof(BITMAPFILEHEADER), CFile::begin);// 放 弃 文 件 头 dwBufSize-=sizeof(BITMAPFILEHEADER); m_bufDIB.SetSize(dwBufSize );// 设 置 缓 冲 区 大 小 file.ReadHuge((LPSTR)(m_bufDIB.GetData()), dwBufSize); ……
Paint() 方 法 调 用 了SetDIBitsToDevice() 函 数( 根 据 情 况 也 可 以 使 用StretchDIBits ()), 参 数hDC 是CDaoRecordview 的 资 源 中 的 一 个 静 态 控 制 的 设 备 句 柄, 作 为SetDIBitsToDevice() 的 第 一 个 参 数。 如 果 不 是16 或24 位 的 位 图, 还 需 要 建 立 和 设 置 调 色 板。Paint() 方 法 除 了 在 CDaoRecordView 派 生 类 的OnMove() 里 调 用 外, 也 被OnPaint() 调 用( 最 好 不 在OnDraw() 里 调 用):
void CDerivedView::OnPaint() { CPaintDC dc(this); CClientDC dc1(&m_ctlImage); if(m_DIB.Create(m_pSet->m_image)) m_DIB.Paint(dc1.m_hDC); }
作 者 首 先 采 用 的 方 法 是, 每 当 打 开 一 个 位 图 文 件, 调 用m_DIB.Create() 和m_DIB.Paint(), 然 后 复 制 给m_pSet->m_image, 再 设 置“ 脏” 标 识:
if(m_DIB.Create(bmpFile)) { CClientDC dc(&m_ctlImage); m_DIB.Paint(dc.m_hDC); (m_pSet->m_image).Copy(m_DIB.m_bufDIB); SetFieldDirty(&(m_pSet->m_image)); }
记 录 滚 动 时,OnMove() 调 用Update() 对 数 据 进 行 更 新。
但 是 这 样 做 的 结 果 是, 只 有 在 域 的 内 容 不 为 空(NULL) 的 时 候 才 能 更 新 数 据。 也 就 是 说, 添 加“ 长 二 进 制 数 据” 不 能 实 现。
最 后 发 现 使 用SeieldValue() 可 以 实 现 添 加 和 替 换。 但 由 于 作 者 未 知 的 原 因, 还 需 要 把 另 外 某 个 域 设 置 为“ 脏” 才 行:
if(m_DIB.Create(bmpFile)) { CClientDC dc(&m_ctlImage); m_DIB.Paint(dc.m_hDC); (m_pSet->m_image).Copy(m_DIB.m_bufDIB); // 只 为OnPaint() 调 用 时 使 用 m_pSet->SetFieldValue(_T("[image]"), COleVariant(m_DIB.m_bufDIB)); m_pSet->SetFieldDirty(&(m_pSet->m_name)); // 任 意 另 外 一 个 域 }如 果 打 算 删 除 数 据 库 里 的 位 图 数 据, 可 以 把 一 个“ 空” 的CByteArray 对 象 替 换 原 来 的 就 行 了。