VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索
查看: 4109|回复: 10

[技术文章] ODBC 存储/读取 图像数据 (VC2008代码)

[复制链接]
xieglt 发表于 2016-7-20 11:58:52 | 显示全部楼层 |阅读模式
1、几点说明:
I、do{}while(FALSE);这个代码块在本文所附代码中随处可见。通常写法如下:
  1.    do
  2.    {
  3.         if(! XXXX)
  4.         {
  5.            break;
  6.         }

  7.         if(! XXXX)
  8.         {
  9.            break;
  10.         }

  11.         ...
  12.    }while(FALSE);
复制代码


   这种写法是为了避免判断语句过多 if() 嵌套过多导致代码不清晰,
   if()嵌套写法如下。

  1.    if()
  2.    {
  3.       if()
  4.       {
  5.           if()
  6.           {
  7.              ....
  8.           }
  9.       }
  10.    }
复制代码


   当然,也可以使用 goto,但结构化程序设计并不提倡 goto。goto写法如下

  1.    if(! XXXX)
  2.    {
  3.        goto _label
  4.    }

  5.    if(! XXXX)
  6.    {
  7.        goto _label
  8.    }

  9.    ....

  10. _lable:
复制代码



II、单例模式:单例模式也是本文所附代码常用的一种方法。
    单例模式写法如下:

  1. class XXXX
  2. {
  3.         private:
  4.                 class CGarbo
  5.                 {
  6.                         public:
  7.                                 CGarbo()
  8.                                 {
  9.                                      ....
  10.                                 }
  11.                                 ~CGarbo()
  12.                                 {
  13.                                      ....
  14.                                 }
  15.                 };
  16.         //把内嵌类定义为friend,是为了支持VC6,
  17.         //vc2008支持的 c++ 标准不需要定义为friend
  18.         friend class CGarbo;
  19.         static CGarbo m_Garbo;
  20. }
复制代码


     为什么要用单例模式?因为我们在程序里经常会遇到一些
     必须执行且仅需要执行一次的代码,这时,我们就可以用
     单例模式把这些代码封装起来。拿程序中的 GdiPlus 为例。

     在使用GdiPlus对象之前必须初始化GdiPlus
  1. Gdiplus::GdiplusStartup(&m_gdiplusToken,&m_gdiplusStartupInput,NULL);
复制代码

     使用完毕后必须释放GdiPlus
      
  1. Gdiplus::GdiplusShutdown(m_gdiplusToken);
复制代码


     这时候,我们就可以用单例模式把这两个函数封装起来。
     当然,如果你不喜欢单例模式,也可以显示调用这两个函数。

III、strcpy 与 strcpy_s、sprintf 与 sprintf_s

     在VC6里头没有 strcpy_s 与 sprintf_s 函数,
     为了避免破坏栈,微软在后来的版本中用一系列加了_s的函数
     代替了原来的函数,当然,原来的函数还是可以用,不过编译
     时会有警告。

IV、本文所附代码里用到了 stl 的 string 和 Gdiplus
    代码的写法是std::string XXXX;   Gdiplus::XXXX();
    (每处使用都在前面加了命名空间)
    而不是  using namespace std;
            using namespace Gdiplus;
            
    为什么?我也不是很清楚,不过据说,using namespace XXXX 并不是
    一个好的编码习惯。

V、Gdiplus。VC6并不支持Gdiplus,所以需要下载Gdiplus的.H文件和.Lib文件
   附件里包涵了Gdiplus的 .H 和.Lib,放置在VC6工程目录之外即可。
   ConvertImage.H头文件里做了预处理。

VI、关于数据库
    本代码测试了Access 以及 mysql数据库
    其中Access数据库的 float 字段写入失败,mysql成功。
    Access数据库用 OLE对象 来存储二进制文件
    mysql数据库用 BOLB 来存储二进制文件,用 LONGBLOB 可存储1G大小的文件。

2.图像文件的操作
  
  跟图像文件相关的几个类

  Class CCaptureScreen;  
      截取屏幕图像类,提供一个公共函数
      BOOL        CaptureImage(CBaseBitmap & bmp,const RECT & rc);
      将指定区域内的屏幕图像数据截取到 CBaseBitmap 对象中
      函数里默认将图像截取为24位图像,

  Class CHeapMemory;     内存管理类,管理 new,delete 内存
      为什么要这个类?提供给 CConverImage 使用

  Class CGlobalMemory;   内存管理类,管理 GlobalAlloc,GlobalFree 分配的内存
      为什么要这个类?提供给 CConverImage 使用。
      CGlobalMemory 和 CHeapMemory 提供的对外接口是一样的。
      为什么有了CHeapMemory,还要这个CGlobalMemory?
      因为Gdiplus 操作内存图像数据总是用到 IStream 对象,也就是说,
      Gdiplus总是从一个 IStream 对象中读取图像数据或者总是将图像数据写
      到一个 IStream 对象中去。而微软提供的 IStream 对象的唯一来源就是
      API : CreateStreamOnHGlobal(HGLOBAL,BOOL,IStream *)
      从一个 Global 句柄上生成一个 IStream 对象,Global 句柄可以为NULL。
      据MSDN上说:GlobalAlloc 系列函数是为了兼容 Win16,现在都Win64了,
      难道我们还要用Win16?所以,在CGlobalMemory 之后又有了 CHeapMemory。
      但事实上,在操作大文件的时候,CGlobalMemory 的速度是优于 CHeapMemory 的。

      CHeapMemory 与 CGlobalMemory 都重载了操作符 const char * 与
      const unsigned char *  用于返回分配的内存的常量指针。

      operater const char * ();
      operater const unsigned char * ();

      这样,我们就可以在对象外对分配的内存进行读访问。

      但是C/C++提供了强制类型转换(这应该是C/C++最被诟病的地方)
      我们可以通过 (char *) 或 const_cast<char *>()将 const 属性去掉。
      然后,我们的封装就变成了扯淡。
      当然,我们可以不提供这样的界面,转而写成这样。

        ReadBuffer();
        WriteBuffer();

      然后,我们就必须先分配一段内存,然后把这段内存写入对象,或是从对象
      中读取数据到内存里。我觉得,这么做有点脱裤子放屁。

  Class CConvertImage;   封装Gdiplus类
      私有的构造函数,不提供生成对象,所有接口都以 static 函数提供

      将一个图像文件转换成指定格式的图像文件
      ImageF2F();

      将一个图像文件转换成指定格式并存储到 CGlobalMemory 对象中
      ImageF2G();

      将一个CGlobalMemory对象中的图像转换成指定格式的图像文件
      ImageG2F();

      将一个CGlobalMemory对象中的图像转换成指定格式并存储到
      另一个 CGlobalMemory 对象中
      ImageG2G();

      将一个图像文件转换成指定格式并存储到 CHeapMemory 对象中
      ImageF2H();
      
      将一个CHeapMemory对象中的图像转换成指定格式的图像文件
      ImageH2F();

      将一个CHeapMemory对象中的图像转换成指定格式并存储到
      另一个 CHeapMemory 对象中
      ImageH2H();

  Class CBaseBitmap;
      我们要显示图像,就必须把图像转换成Bitmap图像,JPG,PNG,TIFF,GIF
      等图像是不能直接显示的。这个类就是把其他格式的图像以Bitmap
      的形式显示出来。
      本类继承自 CHeapMemory ,当然,你可以把它改成继承自 CGlobalMemory
      只需要几个很小的改动就成。

      Bitmap图像格式如下:
      BITMAPFILEHEADER 结构(bitmap 文件头)
      BITMAPINFO 结构
      点阵数据

      显示图像的函数
      void StretchBitmap(HDC hdc,const RECT & rc);

      本函数是以缩放的方式显示图像,而缩放图像必然导致图像失真。
      微软为我们提供了一个API来平滑缩放。
      SetStretchBltMode(hdc,STRETCH_HALFTONE);
      当然,这是要付出代价的,这个算法比较复杂,以这种方式显示
      图像,CPU占用率是比较高的。

3、数据库操作
   本文代码用ODBC API 操作数据库。为什么用ODBC API?因为我喜欢!
   个人并不喜欢 mfc 封装的 ODBC 类,而ADO用来操作微软自己的ACCESS
   或SQL-Server还不错,而用来访问别的数据库如 MYSQL 或者 Oracle
   似乎也是基于 ODBC。

   ODBC API 结构简述

   SQLAllocHandle() 分配环境变量句柄
   SQLAllocHandle() 用环境变量句柄分配数据库句柄
   SQLConnect() 或 SQLDriverConnect() 用数据库句柄连接数据库
   SQLAllocHandle() 用数据库句柄分配statement句柄
   用 statement 句柄来执行 selct 、delete 、update 等语句
   SQLFreeHandle() 释放statement句柄
   SQLDisConnect() 断开数据库连接
   SQLFreeHandle() 释放数据库句柄
   SQLFreeHandle() 释放环境变量句柄

   数据库相关的类

   CCommonDB      
       用单例模式初始化环境变量句柄 和 数据库句柄
       以 protect 方式提供的构造函数,在构造函数里连接数据库
       以 protect 方式提供的析构函数,在析构函数里断开数据库连接
   
        数据表 ImageTrn 的结构
        typedef struct tagImageTrn
        {
                long index;
                char desc[255];
                TIMESTAMP_STRUCT dateTime;
                long length;
                float fTest;
                double dTest;
        }IMAGETRN,* LPIMAGETRN;

        union用于定义所有数据表结构
        typedef union tagTable
        {
                long none;
                IMAGETRN IT;
        }TABLE,* LPTABLE;

        如果你有一个表 Table1
        可以定义
        typedef struct tagTable1
        {
           ......
        }TABLE1,*LPTABLE1;

        typedef union tagTable
        {
                long none;
                IMAGETRN IT;
                TABLE1 T1;
        }TABLE,* LPTABLE;

   CDBAccess
       继承自CCommonDB,用于初始化 statement 句柄
       提供一系列 BindXXX 函数,将查询结构绑定到内存变量上
       提供一系列 ParamXXX 函数,将输入参数绑定到内存变量上

       提供一系列虚函数供子类重载
            OpenRecordSet()     ;查询
            PrepareInsert()     ;插入记录绑定参数
            InsertRecord()      ;插入记录
            DeleteRecord()      ;删除记录
            UpdateRecord()      ;更新记录
            Data()                ;取得查询结果

       public 接口两个
       GetNextRecord()
       GetPrevRecord()



    CImageTrn
        对表ImageTrn 进行读写操作的类,继承自DBAccess
        演示对数据库的访问。

        几个成员变量的简单说明:

        //用于SQLBindParameter 的最后一个参数
        SQLINTEGER m_BindIn[MAX_FIELDS_OF_TABLE];

        //用于SQLBindCol 的最后一个参数
        SQLINTEGER m_BindOut[MAX_FIELDS_OF_TABLE];

        //存储输入参数       
        TABLE m_tableIn;

        //存储查询结果
        TABLE m_tableOut;

    可以定义一个具有全局生命期的对象 CImageTrn a;
    这时,数据库就会自动连接,以后再定义CImageTrn对象,
    都不会再去连接数据库。当a对象析构时,自动断开数据库
    连接。

    如果你有一个表 TableTest,要访问表 TableTest,则可以
    仿照CImageTrn写一个CTableTest类。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?加入驿站

x

评分

参与人数 1威望 +2 驿站币 +2 激情 +2 收起 理由
Syc + 2 + 2 + 2 支持原创!

查看全部评分

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

Syc 发表于 2016-7-20 13:27:44 | 显示全部楼层
很不错的总结,把平时总遇到的一些 do-while,单例模式 等等都总结出来了。
这些是很多新手经常遇到且不懂的问题,感谢楼主分享,加精了!

备注:附件中的垃圾文件 *.ncb 已经去掉,将分卷的4个压缩包合并在一起了,方便大家下载!

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

tomok 发表于 2016-7-20 16:57:14 | 显示全部楼层
好好学习  了

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

李戈 发表于 2017-1-30 20:35:52 | 显示全部楼层
学习了!谢谢

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

tomok 发表于 2017-1-31 08:21:42 | 显示全部楼层
学学了
谢谢分享

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

runfog 发表于 2017-5-2 08:34:37 | 显示全部楼层
结构化程序设计并不提倡 goto

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

yuejingming 发表于 2017-5-7 10:59:20 | 显示全部楼层
看样子解释的还算明了

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

qw86474 发表于 2017-5-7 23:50:26 | 显示全部楼层
dddddddddddddd

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

qw86474 发表于 2017-5-7 23:50:41 | 显示全部楼层
dddddddddddddddddddd

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

runfog 发表于 2017-6-17 22:30:23 | 显示全部楼层
结构化程序设计并不提倡 goto

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

kolong5566 发表于 2017-11-8 06:54:22 | 显示全部楼层
長知識了呀,感謝分享唷

发帖求助前要善用论坛搜索功能,那里可能会有你要找的答案;

如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请编辑帖子并把分类改成【已解决】

如何回报帮助你解决问题的坛友,一个好办法就是给对方加【热心】【驿站币】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入驿站

本版积分规则

展开

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号 )

返回顶部
x

VC驿站微信公众号cctry2009

GMT+8, 2017-12-14 04:25

Powered by Discuz!

© 2009-2017 cctry.com

快速回复 返回顶部 返回列表