C++使用ADO存取图片
在项目中,我们需要把事故简图上传到总服务器,以便每个客户端都能下载或者查看。在网上找了找,向Server2000存储图片代码比较多,从数据库中读取图片并显示也不少,但是把图片从数据库中二进制数据转换为原图片保存在本地,就很少有C++代码了。花了大约4天
在项目中,我们需要把事故简图上传到总服务器,以便每个客户端都能下载或者查看。在网上找了找,向Server2000存储图片代码比较多,从数据库中读取图片并显示也不少,但是把图片从数据库中二进制数据转换为原图片保存在本地,就很少有C++代码了。花了大约4天时间,和师妹两个人找各种资料,终于解决了这个问题。下面就一步一步地讲一讲我们的解决方法:
一、使用数据库前的准备
我们使用ADO,是用_ConnectionPtr,_RecordsetPtr来操纵数据库的。还有一个_CommandPtr,本程序没有使用它。
为了使用ADO,需要导入ADO动态链接库。在工程的stdafx.h文件中,添加如下代码:
//导入ADO
#import "C:\Program Files\Common Files\System\ado\msado15.dll"\
rename_namespace("ADOCG")rename("EOF","EndOfFile")
using namespace ADOCG;
这些代码声明,在这个工程中使用ADO但不使用ADO的名字空间,并且为了避免常数冲突,将常数EOF改名为adoEOF。
再有就是要建一个简单的数据库,名字叫TestImage,里面有一个表Images,这个表有三个字段,分别是ID,Name,ImageData。
二、连接数据库
连接数据库的代码可以放入一个函数中,在想调用的地方调用。一般不推荐在CAPP类的Initalize()里连接数据库,在退出程序时关闭数据库连接。应该是在使用时连接,使用完马上关闭。项目中m_pConn是_ConnectionPtr类型的变量。
BOOL OpenConnection()
{
if(m_pConn == NULL)
{
m_pConn.CreateInstance("ADODB.Connection"); //创建_ConnectionPtr的一个实例
}
try
{
if(adStateClosed == m_pConn->State) //如果已关闭
{
m_pConn->Open("driver={SQL Server};Server=HP-CADD722B76A0;DATABASE=TestImage;UID=sa;PWD=sa","","",adModeUnknown); //因数据库而异
return true;
}
}
catch(_com_error e)
{
AfxMessageBox(_T("连接数据库失败!"));
return false;
}
}
三、打开数据集,操纵数据库
在使用_RecordSetPtr对象m_pRecord时,必须先创建这种对象的一个实例:
m_pRecord.CreateInstance( __uuidof(RecordSet) );
CString strSQL;
//获取表中最大的id,下一次插入时就用id+1
strSQL.Format(_T("Select count(*) as num, Max(ID) as maxid from Images"));
try
{
m_pRecord->Open(strSQL.AllocSysString(), m_pConn.GetInterfacePtr(),
adOpenDynamic, adLockUnspecified, adCmdText);
}
catch (_com_error e)
{
AfxMessageBox(_T("读取最大的id异常"));
eturn;
}
//从RecordSet中获取数据数目和当前数据库中最大的ID。
int num = m_pRecord->GetCollect("num");
int maxid;
if (num != 0)
{
maxid = m_pRecord->GetCollect("maxid");
}
else
{
maxid = 0;
}
strSQL.Format(_T("Select * from Images where ID = %d"), maxid);
//下面向数据库中插入图片等。
//首先从数据库中读id最大的那条数据,主要目的是为了将RecordSet初始化
m_pRecord.CreateInstance(__uuidof(Recordset));
上面这句一定要注意,因为上一次把一些数据放入m_pRecord中,这一次再放的时候,要重新创建一次,否则数据格式要么不匹配,要么保留有上一次的数据,定位困难。
m_pRecord->Open(strSQL.AllocSysString(), m_pConn.GetInterfacePtr(),
adOpenDynamic, adLockOptimistic, adCmdText); //这是AddNew方法要求的
CString imagepath = _T("F:/200713454/20090326.bmp");
CString imagename = imagepath.Right(12);
try
{
m_pRecord->AddNew(); //为记录集添加新的一行,更新时就会把这条新纪录放到数据库中
}
catch (_com_error e)
{
AfxMessageBox(_T("不能插入一条新的记录"));
return;
}
try
{
//使用putcollect插入非图像数据,使用SetImage2DB插入图像数据
m_pRecord->PutCollect("ID", _variant_t(maxid+1));
m_pRecord->PutCollect("Name", _variant_t(imagename));
SetImage2DB(imagepath);
}
catch (_com_error e)
{
AfxMessageBox(_T("插入图片有异常"));
return;
}
m_pRecord->Update();
//使用完毕,关闭m_pRecord,并设置为NULL,最后关闭数据库连接
m_pRecord->Close();
m_pRecord = NULL;
CloseConnection();
四、读取图片并存储到本地计算机
要将数据库中的二进制数据变为图片,最简单的方法就是用GDI+。GDI+有一个类是Image,可以用stream来创建对象,还可以用Save方法保存到本地,所以这个类很符合需要。
要使用GDI+,需要做些设置。首先在VS2005的项目属性中,加上gdiplus.lib。
然后在stdafx.h中添加代码
#include
using namespace Gdiplus;
在CApp类添加两个变量:
GdiplusStartupInput m_gdiplusstartUpInput;
ULONG_PTR m_GdiplusToken;
在CApp的InitInstance函数中添加GdiplusStartup(&m_GdiplusToken, &m_gdiplusstartUpInput, NULL);在ExitInstance函数中添加GdiplusShutdown(m_GdiplusToken);
以下是读取图片数据并保存到本地的代码实现:
OpenConnection();
m_pRecord.CreateInstance(__uuidof(Recordset));
CString strSQL;
strSQL.Format(_T("Select * from Images where ID = 1"));
try
{
m_pRecord->Open(strSQL.AllocSysString(), m_pConn.GetInterfacePtr(),
adOpenDynamic, adLockOptimistic, adCmdText);
}
catch (_com_error e)
{
AfxMessageBox(_T("读取图片信息异常"));
return;
}
LPVOID Data;
char* pbuf = NULL;
long lDatasize = m_pRecord->GetFields()->GetItem("ImageData")->ActualSize; //数据库中图像数据长度
CString imagename = m_pRecord->GetCollect("Name").bstrVal;
if (lDatasize > 0)
{
_variant_t varBLOB;
varBLOB = m_pRecord->GetFields()->GetItem("ImageData")->GetChunk(lDatasize);
Data = new char[lDatasize+1];
if (varBLOB.vt == (VT_ARRAY|VT_UI1))
{
SafeArrayAccessData(varBLOB.parray, (void **)&pbuf);
memcpy(Data, pbuf, lDatasize);
SafeArrayUnaccessData(varBLOB.parray);
}
}
IStream* pStm;
LONGLONG cb = lDatasize;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, cb);
LPVOID pvData;
if (hGlobal != NULL)
{
pvData = GlobalLock(hGlobal);
memcpy(pvData, Data, cb);
GlobalUnlock(hGlobal);
CreateStreamOnHGlobal(hGlobal, TRUE, &pStm);
}
else
{
AfxMessageBox(_T("Error"));
return;
}
CLSID encoderClsid;
GetEncoderClsid(L"image/bmp",&encoderClsid); //确定编码格式是bmp格式
Image image(pStm, TRUE);
CString imagepath;
imagepath.Format(_T("F:/200713454/%s"), imagename);
image.Save(imagepath, &encoderClsid, NULL); //把image中的数据按照bmp编码格式存到本地
m_pRecord->Close();
m_pRecord = NULL;
CloseConnection();
上面存储和读取数据的代码中用到了两个函数,GetEncoderClsid和SetImage2DB。它们的实现如下:
这个函数和上面的存/取函数都是一个类的成员函数,而m_pConn和m_pRecord是这个类的成员变量,所以
void CDlgTest::SetImage2DB(CString path)
{
VARIANT varChunk;
SAFEARRAY* psa;
SAFEARRAYBOUND rgsabound[1];
CFile f(path.operator LPCTSTR(),CFile::modeRead);
BYTE bval[ChunkSize+1];
long uIsRead=0;
while (1)
{
uIsRead=f.Read(bval,ChunkSize);
if (uIsRead==0) break;
rgsabound[0].cElements=uIsRead;
rgsabound[0].lLbound=0;
psa=SafeArrayCreate(VT_UI1,1,rgsabound);
for (long index=0;index
if (FAILED(SafeArrayPutElement(psa,&index,&bval[index])))
AfxMessageBox(_T("错误。"));
} } INT CDlgTest::GetEncoderClsid(const WCHAR* format, CLSID* pClsid) ImageCodecInfo* pImageCodecInfo = NULL; GetImageEncodersSize(&num, &size); pImageCodecInfo = (ImageCodecInfo*)(malloc(size)); GetImageEncoders(num, size, pImageCodecInfo); for(UINT j = 0; j
{ free(pImageCodecInfo); 这样就实现了存储图片和从数据库中把图片下载到本地。
varChunk.vt =VT_ARRAY|VT_UI1;
varChunk.parray=psa;
try
{
m_pRecord->Fields->GetItem("ImageData")->AppendChunk(varChunk);
}
catch (_com_error e)
{
AfxMessageBox(_T("错误。"));
}
::VariantClear(&varChunk);
::SafeArrayDestroyData(psa);
if (uIsRead
f.Close();
}
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
if(size == 0)
return -1; // Failure
if(pImageCodecInfo == NULL)
return -1; // Failure
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
return -1; // Failure
}

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

C#과 C의 역사와 진화는 독특하며 미래의 전망도 다릅니다. 1.C는 1983 년 Bjarnestroustrup에 의해 발명되어 객체 지향 프로그래밍을 C 언어에 소개했습니다. Evolution 프로세스에는 자동 키워드 소개 및 Lambda Expressions 소개 C 11, C 20 도입 개념 및 코 루틴과 같은 여러 표준화가 포함되며 향후 성능 및 시스템 수준 프로그래밍에 중점을 둘 것입니다. 2.C#은 2000 년 Microsoft에 의해 출시되었으며 C와 Java의 장점을 결합하여 진화는 단순성과 생산성에 중점을 둡니다. 예를 들어, C#2.0은 제네릭과 C#5.0 도입 된 비동기 프로그래밍을 소개했으며, 이는 향후 개발자의 생산성 및 클라우드 컴퓨팅에 중점을 둘 것입니다.

Visual Studio Code (VSCODE)에서 코드를 작성하는 것은 간단하고 사용하기 쉽습니다. vscode를 설치하고, 프로젝트를 만들고, 언어를 선택하고, 파일을 만들고, 코드를 작성하고, 저장하고 실행합니다. VSCODE의 장점에는 크로스 플랫폼, 무료 및 오픈 소스, 강력한 기능, 풍부한 확장 및 경량 및 빠른가 포함됩니다.

Golang은 동시성에서 C보다 낫고 C는 원시 속도에서 Golang보다 낫습니다. 1) Golang은 Goroutine 및 Channel을 통해 효율적인 동시성을 달성하며, 이는 많은 동시 작업을 처리하는 데 적합합니다. 2) C 컴파일러 최적화 및 표준 라이브러리를 통해 하드웨어에 가까운 고성능을 제공하며 극도의 최적화가 필요한 애플리케이션에 적합합니다.

Golang과 C의 성능 차이는 주로 메모리 관리, 컴파일 최적화 및 런타임 효율에 반영됩니다. 1) Golang의 쓰레기 수집 메커니즘은 편리하지만 성능에 영향을 줄 수 있습니다. 2) C의 수동 메모리 관리 및 컴파일러 최적화는 재귀 컴퓨팅에서 더 효율적입니다.

Golang과 C는 각각 공연 경쟁에서 고유 한 장점을 가지고 있습니다. 1) Golang은 높은 동시성과 빠른 발전에 적합하며 2) C는 더 높은 성능과 세밀한 제어를 제공합니다. 선택은 프로젝트 요구 사항 및 팀 기술 스택을 기반으로해야합니다.

Python은 배우고 사용하기 쉽고 C는 더 강력하지만 복잡합니다. 1. Python Syntax는 간결하며 초보자에게 적합합니다. 동적 타이핑 및 자동 메모리 관리를 사용하면 사용하기 쉽지만 런타임 오류가 발생할 수 있습니다. 2.C는 고성능 응용 프로그램에 적합한 저수준 제어 및 고급 기능을 제공하지만 학습 임계 값이 높고 수동 메모리 및 유형 안전 관리가 필요합니다.

Golang은 빠른 개발 및 동시 시나리오에 적합하며 C는 극도의 성능 및 저수준 제어가 필요한 시나리오에 적합합니다. 1) Golang은 쓰레기 수집 및 동시성 메커니즘을 통해 성능을 향상시키고, 고전성 웹 서비스 개발에 적합합니다. 2) C는 수동 메모리 관리 및 컴파일러 최적화를 통해 궁극적 인 성능을 달성하며 임베디드 시스템 개발에 적합합니다.

코드에서 코드 실행은 6 단계 만 가져갑니다. 1. 프로젝트 열기; 2. 코드 파일을 작성하고 작성합니다. 3. 터미널을 엽니 다. 4. 프로젝트 디렉토리로 이동; 5. 적절한 명령으로 코드를 실행하십시오. 6. 출력을 봅니다.
