vc小知识

TCHAR类型到BSTR类型的转换
//实现将TCHAR转换成BSTR类型
inline BSTR TCHAR2BSTR(TCHAR* szBuffer)
{
 WCHAR* wszBuff;
    #ifndef UNICODE  //若未定义宽字符UNICODE,则将多字节转换为宽字节
      wszBuff= new WCHAR[512];
      MultiByteToWideChar(CP_ACP, 0, szBuffer,-1, wszBuff,512);//将多字节字符转换为宽字符
    #else
         wszBuff = szBuffer;
    #endif
   BSTR newBstr=::SysAllocString(wszBuff);
#ifndef UNICODE
   delete[] wszBuff;
#endif
   return newBstr;
}
UNICODE和多字节
1.UNICODE(宽字符)和多字节

多字节字符就是由一个或者多个字节序列所组成的字符。每个字节序列都在已扩展的字符集中呈现了一个单独的字符。多字节字符在类似于 Kanji 的字符集中被使用。

宽字符是始终有 16 位宽度的多语言字符代码。字符常数的类型是 char;而宽字符的类型就是 wchar_t。自从宽字符始终是固定尺寸以来,使用宽字符已经简化了使用国际化字符集的编程。

宽字符串文字 L"hello" 会变成由六个 wchar_t 类型所组成的数组。

{L'h', L'e', L'l', L'l', L'o', 0}

Unicode 规格就是宽字符规格。并且运行时库需要经常在多字节与包括 mbstowcs、mbtowc,wcstombs 和 wctomb 在内的宽字符之间进行转换。

2.多字节字符串与宽字符串的转换

多字节字符串与宽字符串的转换可使用C API者Win32 API.
C API: mbstowcs(将一个多字节字符转换成相应的宽字符),wcstombs(将一个宽字符转换成相应的多字节字符),头文件<stdlib.h>
Win32 API: MultiByteToWideChar, WideCharToMultiByte

下面着重介绍Win32 API的用法,C API的用法较为简单可参照Win32 API。

首先是WideCharToMultiByte

通常你需要配置4个参数(其他参数如是使用即可),红色标记的部分。
依次是源宽字符串,需要转换的长度(-1,则为转换整个字符串),目标多字节字符串,目标缓冲区长度。
返回值表示转换为目标多字节字符串实际需要的长度(包括结束符)。
所以通常需要调用WideCharToMultiByte两次:第一次产生目标缓冲区长度,第二次产生目标字符串,像下面这样
wchar_t* wcs = L"中国,你好!I Love You!";
int lengthOfMbs = WideCharToMultiByte( CP_ACP, 0, wcs, -1, NULL, 0, NULL, NULL);
char* mbs = new char[ lengthOfMbs ];
WideCharToMultiByte( CP_ACP, 0, wcs, -1, mbs, lengthOfMbs, NULL, NULL);    
delete mbs;
mbs = NULL;
MultiByteToWideChar的用法类似
char* mbs = "中国,你好!I Love You!";
int lengthOfWcs = MultiByteToWideChar( CP_ACP, 0, mbs, -1, NULL, 0 );
wchar_t* wcs = new wchar_t[ lengthOfWcs ];
MultiByteToWideChar( CP_ACP, 0, mbs, -1, wcs, lengthOfWcs );
delete wcs;
wcs = NULL;

下面两个函数封装了转换过程
#include <Windows.h>
#include <string>

std::string WcsToMbs( const std::wstring& wcs ) {    
    int lengthOfMbs = WideCharToMultiByte( CP_ACP, 0, wcs.c_str(), -1, NULL, 0, NULL, NULL);
    char* mbs = new char[ lengthOfMbs ];
    WideCharToMultiByte( CP_ACP, 0, wcs.c_str(), -1, mbs, lengthOfMbs, NULL, NULL);
    std::string result = mbs;
    delete mbs;
    mbs = NULL;
    return result;
}

std::wstring MbsToWcs( const std::string& mbs ) {    
    int lengthOfWcs = MultiByteToWideChar( CP_ACP, 0, mbs.c_str(), -1, NULL, 0 );
    wchar_t* wcs = new wchar_t[ lengthOfWcs ];
    MultiByteToWideChar( CP_ACP, 0, mbs.c_str(), -1, wcs, lengthOfWcs );
    std::wstring result = wcs;
    delete wcs;
    wcs = NULL;
    return result;
}

C++inline函数使用

(一)inline函数(摘自C++ Primer的第三版)

         在函数声明或定义中函数返回类型前加上关键字inline即把min()指定为内联。

   inline int min(int first, int secend) {/****/};

       inline 函数对编译器而言必须是可见的,以便它能够在调用点内展开该函数。与非inline函数不同的是,inline函数必须在调用该函数的每个文本文件中定 义。当然,对于同一程序的不同文件,如果inline函数出现的话,其定义必须相同。对于由两个文件compute.C和draw.C构成的程序来说,程 序员不能定义这样的min()函数,它在compute.C中指一件事情,而在draw.C中指另外一件事情。如果两个定义不相同,程序将会有未定义的行 为:

       为保证不会发生这样的事情,建议把inline函数的定义放到头文件中。在每个调用该inline函数的文件中包含该头文件。这种方法保证对每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。

(二)内联函数的编程风格(摘自高质量C++/C 编程指南)

关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。

如下风格的函数Foo 不能成为内联函数:inline void Foo(int x, int y); // inline 仅与函数声明放在一起void Foo(int x, int y){}而如下风格的函数Foo 则成为内联函数:void Foo(int x, int y);inline void Foo(int x, int y) // inline 与函数定义体放在一起{}所以说,inline 是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。

一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的 声明、定义体前面都加了inline 关键字,但我认为inline 不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C 程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。

   定义在类声明之中的成员函数将自动地成为内联函数, 例如class A{public:void Foo(int x, int y) { } // 自动地成为内联函数。将成员函数的定义体放在类声明之中虽然能带来书写上的方便,但不是一种良好的编程风格,上例应该改成:// 头文件class A{public:void Foo(int x, int y);}// 定义文件inline void A::Foo(int x, int y){} 
慎用内联内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数?如果所有的函数都是内联函数, 还用得着“内联”这个关键字吗?内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比 于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜 使用内联:(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开 销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数 和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。
注意点:
内联函数既能够去除函数调用所带来的效率负担又能够保留一般函数的优点。然而,内联函数并不是万能药,在一些情况下,它甚至能够降低程序的性能。因此在使用的时候应该慎重。 
1.我们先来看看内联函数给我们带来的好处:从一个用户的角度来看,内联函数看起来和普通函数一样,它可以有参数和返回值,也可以有自己的作用域,然而它却不会引入一般函数调用所带来的负担。另外,它可以比宏更安全更容易调试。当然有一点应该意识到,inline specifier 仅仅是对编译器的建议,编译器有权利忽略这个建议。那么编译器是如何决定函数内联与否呢?一般情况下关键性因素包括函数体的大小,是否有局部对象被声明,函数的复杂性等等。 
2.那么如果一个函数被声明为inline但是却没有被内联将会发生什么呢?理论上,当编译器拒绝内联一个函数的时候,那个函数会像普通函数一样被对待,但是还会出现一些其他的问题。例如下面这段代码: 
// filename Time.h 
#include<ctime> 
#include<iostream> 
using namespace std; 
class Time 

public: 
inline void Show() { for (int i = 0; i<10; i++) cout<<time(0)<<endl;} 
}; 
因为成员函数Time::Show()包括一个局部变量和一个for循环,所以编译器一般拒绝inline,并且把它当作一个普通的成员函数。但是这个包含类声明的头文件会被单独的#include进各个独立的编译单元中: 
// filename f1.cpp 
#include "Time.hj" 
void f1() 

Time t1; 
t1.Show(); 

// filename f2.cpp 
#include "Time.h" 
void f2() 

Time t2; 
t2.Show(); 

结果编译器为这个程序生成了两个相同成员函数的拷贝: 
void f1(); 
void f2(); 
int main() 

f1(); 
f2(); 
return 0; 

当程序被链接的时候,linker将会面对两个相同的Time::Show()拷贝,于是函数重定义的连接错误发生。但是老一些的C++实现对付这种情况的办法是通过把一个un-inlined函数当作static来处理。因此每一份函数拷贝仅仅在自己的编译单元中。可见,这样链接错误就解决了,但是在程序中却会留下多份函数拷贝。在这种情况下,程序的性能不但没有提升,反而增加了编译和链接时间以及最终可执行体的大小。但是幸运的是,新的C++标准中关于un-inlined函数的说法已经改变。一个符合标准C++实现应该只生成一份函数拷贝。然而,要想所有的编译器都支持这一点可能还需要很长时间。 
另外关于内联函数还有两个更令人头疼的问题。第一个问题是该如何进行维护。 一个函数开始的时候可能以内联的形式出现,但是随着系统的扩展,函数体可能要求添加额外的功能,结果内联函数就变得不太可能,因此需要把inline specifier 去除以及把函数体放到一个单独的源文件中。另一个问题是当内联函数被应用在代码库的时候产生。当内联函数改变的时候,用户必须重新编译他们的代码以反映这种改变。然而对于一个非内联函数,用户仅仅需要重新链接就可以了。 
这里想要说的是,内联函数并不是一个增强性能的灵丹妙药。只有当函数非常短小的时候它才能得到我们想要的效果,但是如果函数并不是很短而且在很多地方都被调用的话,那么将会使得可执行体的体积增大。最令人烦恼的还是当编译器拒绝内联的时候。在老的实现中,结果很不尽人意,虽然在新的实现中有很大的改善,但是仍然还是不那么完善的。一些编译器能够足够的聪明来指出哪些函数可以内联哪些不能,但是,大多数编译器就不那么聪明了,因此这就需要我们的经验来判断。
如果内联函数不能增强功能,就避免使用它! 
*/ 
/* 
用指针代替数组 
在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度更快,占用空间更少。使用多维数组时差异更明显。下面的代码作用是相同的,但是效率不一样。 
char* pBuffer = new char[100]; 
nTestNumber = 0; 
for(dwIndex = 0;dwIndex < 100;dwIndex ++) 

nTestNumber = pBuffer[dwIndex]; 

char* pTemp = pBuffer; 
for(dwIndex = 0;dwIndex < 100;dwIndex ++) 

nTestNumber = *(pTemp ++); 

指针方法的优点是,pBuffer的地址每次装入地址pTemp后,在每次循环中只需对pTemp增量操作。 
在数组索引方法中,每次循环中都必须进行基于dwIndex值求数组下标的复杂运算。 
API, ATL, MFC等
   最近遇到ATL, MFC, COM等一些缩略词,对它们的具体含义以及它们之间的关系不太清楚,于是从网上搜了搜,找到如下资料,感觉对于理解这些词有很大帮助。
C/C++都是一种编程语言,程序员用它来创作(我喜欢用创作这个词,让写代码有一些艺术气息...)各种各样的程序,简单如计算闰年,复杂到一个系统地实现。当然, 编写标准C程序的时候,我们还经常会调用各种库函数来辅助完成某些功能;初学者使用得最多的C库函数就是printf了,这些库函数是由你所使用的编译器厂商提供的。在Windows平台下,也有类似的函数可供调用;不同的是,这些函数是由Windows 操作系统 本身提供的。

    Windows操作系统提供了各种各样的函数,以方便我们开发Windows应用程序。这些函数是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称为API函数。我们在编写Windows程序时所说的API函数,就是指系统提供的函数,所有主要的Windows函数都在Windows.h头文件中进行了声明。

       使用windows API创建的能在windows上运行的程序统称为windows程序。

       这些API随着系统的更新升级,他们也是在不断更新和扩充,但是,其行为基本保持不变以保证不同平台对应用程序的兼容性,面对越来越多地API,我们怎么获取可靠的帮助呢?最大的帮助就是MSDN,你可以把MSDN理解为微软向开发人员提供的一套帮助系统,其中包含大量的开发文档、技术文章和示例代码。MSDN包含的信息非常全面,程序员不但可以利用MSDN来辅助开发,还可以利用MSDN来进行学习,从而提高自己。对于初学者来说,学会使用MSDN并从中汲取知识,是必须要掌握的技能。

      我们还经常听人说Win32 SDK开发,那么什么是SDK呢。SDK的全称是Software Development Kit,中文译为软件开发包。假如现在我们要开发呼叫中心,在购买语音卡的同时,厂商就会提供语音卡的SDK开发包,以方便我们对语音卡的编程操作。这个开发包通常都会包含语音卡的API函数库、帮助文档、使用手册、辅助工具等资源。也就是说,SDK实际上就是开发所需资源的一个集合,再具体点说,你知道CreatePorcess这个API,那怎么使用,你需要有头文件,当然还需要提供功能的系统DLL库的引出库lib,这些都在SDK中。现在读者应该明白Win32 SDK的含义了吧,即Windows 32位平台下的软件开发包,包括了API函数、帮助文档、微软提供的一些辅助开发工具。
  提示:API和SDK是一种广泛使用的专业术语,并没有专指某一种特定的API和SDK,例如,语音卡API、语音卡SDK、Java API、Java SDK等。自己公开的DLL函数也可以叫API!!!    
一般来讲,狭义上的API指 MS公开的函数。比如MSDN中介绍的函数。    广义的API可以包括所有的函数,你自己的函数也算,未公开的也是。    指世界上一切函数。都可以叫API--pplication Programming Interface ;    SDK也不仅仅指MS的开发包,你自己的程序如果需要让别人作2次开发,你就会提供 一些函数接口让别人编程,你提供的材料也叫SDK.   

      有了语言(C\C++),有了开发资源(SDK)、有了帮助文档(MSDN),ok,我们可以编写windows程序了,等等、工具有没有更好的工具呢,人类之所以进步就在于会使用工具,所以,为了更加高效的开发程序,一些集成开发环境诞生了,其中, Visual   C++就是一个支持C\C++语言开发的集成开发环境(IDE)。记住,Vc++不是什么新的编程语言,他只是 IDE,只是一个编程的辅助工具,具体来说,Visual   C++包含了源程序编辑器、C\C++编译器、MFC和ATL等,其中、MFC和ATL可以简单的理解为再次封装的Windows的系统接口,原生接口就是API 。

      其中,MFC,微软基础类(Microsoft Foundation Classes),实际上是微软提供的,用于在C++环境下编写应用程序的一个框架和引擎,也可以说,MFC是Win API与C++的结合后的再一次封装。

      OWL(borland公司,其已经逐渐淡处)、VCL(Borland公司--现在已和Inprise合并专--为DELPHI设计的,其是由OWL演变的)和MFC(ms专为vc++设计的)是不同公司提供的三大类库,更确切点说,他们都是应用框架。

      DELPHI:也是一种集成开发环境,不过他支持的语言是源至于pascal的Object Pascal.他使用的框架就是VCL.

       BCB:就是Inprise公司使用了Delphi的IDE和VCL,再配上C++语言推出的开发环境C++Builder,很多人很念旧,所以冠以Borland C++ Builder之名,简称为BCB。

      ATL是全新的COM 开发工具 :---- ATL 是ActiveX Template Library 的缩写,它是一套C++模板库。 使用ATL 能 够 快速地开发出高效、简洁的代码, 同时对COM 组件的开发提供最大限度 的代码自动生成以及可视化支 持。从Microsoft Visual C++ 5.0 版本开始,Microsoft 把ATL 集成 到Visual C++ 开发环境中。

     SDK + C 完全可以进行所有的windows程序开发,当然,你还可以采用MFC + C++,当然,你要用SDK + C++ 也是你的自由,但是MFC + C可是impossible的事情,因为,MFC就是C++写的,C可不支持类哦...

ATL不同于MFC,它完全面向COM组件,其技术路线也不同于MFC,MFC使用的是C++中的继承、封装、嵌套等常规技术,而ATL使用了C++中模板、多继承等高级技术,甚至还用到了STL。所以学习和使用ATL要求我们必须熟悉这些C++高级特性。另一方面,ATL结构完全针对COM中的诸多规范,这就要求使用人员必须非常了解COM规范,才有可能真正把ATL用好。


GDI+编程的肤浅使用及Char ,CString,WCHAR 之间的转换小结
一,建立GDI+的VC&VS开发环境
先下了这个库的头文件和库文件,然后在VC工程配置添加库和头文件。具体的操作是这样的:
VS2005下的操作:
(1)在菜单项:项目->添加现有项,选择VC安装目录下(VC\Platform SDK\Lib\gdiplus.lib).
(2)在stdafx.h中添加如下代码:
#include <gdiplus.h>
using namespace Gdiplus;
(3)在App类中添加全局变量
ULONG_PTR gdiplusToken;   //全局变量,表示对GDI+的一个引用
(4)App类的InitInstance中添加如下代码(在CWinApp::InitInstance();之前)
GDI+系统资源初始化
 GdiplusStartupInput gdiplusstartupinput;
 GdiplusStartup(&gdiplusToken, &gdiplusstartupinput, NULL);
 /
(5)在App类的析构函数中添加代码
GdiplusShutdown(gdiplusToken);//销毁GDI+资源
如此即可以在View或Fram类里面使用gdiplus相应的类了
二,如何在一张图片(常见的几种格式)上作图
方法大致如此:
首先定义一个Bitmap或Image的对象
如Image img(L"example.jpg");
然后由Image或Bitmap 的一个静态函数FromImage();
得到一个Graphics 的对象指针。Graphics是一个有作图接口的类,支持常见的如画点,线,矩形,椭圆,字符串,填充等操作。显然这儿作图的所谓有的DC应该是内存DC,也就是图片文件的数据区内存。所以做图后img对象调用其保存函数
img.Save(....);后打开保存后的图片,就能看见作图的效果了。
另外,还有一个问题在于如何将一个小图片复制到一张大图片,或者说几张图片一起拼接成一大图片。应该是网上的资源是挺丰富的,但是人应该是学以致用的。学会理解,活用,才是在网上学习的最好方法吧。我对此问题的大致操作如下:
先建立一个大图片的对象,并指定其宽高:Bitmap bigBmp(width,height);
获取小图片对象:Bitmap smallBmp(L"resource.jpg");
由上面总结的在图片上绘图的方法得到Graphics *并在smallBmp完成相应字符串等的绘制。
同样的得到一个指向bigBmp的Graphics*,将完成做图后的小图片smallBmp画到指定的位置就OK了。即:
Bitmap::FromImage(bigBmp)->DrawImage(&smallBmp,x,y);
三:关于Char* ,CString ,WCHAR*之间的转换问题
GDI+所有类的接口函数如果要传递字符串作为参数的话,似乎都用UNICODE串,即WCHAR*。我开始也被整得晕头转向,因为窗口编程所用往往是CString,用IO流读文件数据又得到char *。得益于网上牛人们的总结,我用到以下几种基本方法去实现三者间的转换:
char * 转WCHAR *:
::MultiByteToWideChar(CP_ACP,0,(const char *)res,int count,char * dest,int count);
类似地,WCHAR *转char *:
WideCharToMultiByte(CP_ACP,   0,.........);
CString 转WCHAR *:
wchar_t * p=str.AllocSysStrinig()
也有A2W(str)的,但是要包括ATL转换头文件#include;
并且在A2W前使用USES_CONVERSION宏。
其它:
char*转CString:
除了直接赋值外,还可使用CString::Format进行。
如char * p="sfdasf"; 
CString str=p; 或者str.Format("%s",p);
CString 转char *
1.直接强制类型转换:
 CString ss="sfasf";
  char *p=(LPSTR)(LPCSTR)ss;
2.CString::GetBuffer或LockBuffer
char * p=str.GetBuffer();
char * pt=str.LockBuffer();
WCHAR *转CString
在网上没有找到相关的文档,想想应该是可以直接赋值的
但是试验发现虽无编译错误,但是用中文的时候却生乱码,用字母的时候却是正常,想其中肯定没有错误,只是用MessageBox显示的时候乱码应该有其它原因,比如说使用了双字节字符集DBCS来显示汉字(纯属猜测而已)。总体来说在Windows编程中:#define UNICODE 
则CString .TCHAR,等均用的是UNICODE码,一个字符占两个字节。
 
msdn无法打开主页msdnstart.htm的解决办法
    今天写程序时遇到一个类不太明白,于是和往常一样,打开MSDN求助。可是,和往常不同的是,今天MSDN打开后却出现首页已取消导航,无论点击什么内容都无法显示相应的帮助,还弹出了“Internet explorer 无法打开主页msdnstart.htm(来自.......)”的提示.  怎么办?于是先百度,再GOOGLE,有人说重装MSDN,甚至有人说重装系统。我先是尝试着重启机器,不行,然后把MSDN卸了重新安装问题却还存在。总不能真的要我重装系统吧?不行,再找找办法。于是再次GOOGLE加百度,发现了几个看起来比较有效的办法,不过基本上都是一个办法,只是其中的个别细节不同。

1.用管理员身份登录。

2.打开C:\Documents   and   Settings。在我的系统中(Vista 系统),此文件夹是快捷方式的,最初点击怎么也打不开,后来右键点击,选择“资源管理器”总算是进去了。进入Administrator\AppData\local文件夹.

3.打开   工具-文件夹选项到“查看”页,把“隐藏受保护的操作系统文件(推荐)”前的勾去掉,还要选上“显示所有文件和文件夹”确定后,目录里就能看到   Temporary   Internet   Files   文件夹.(如果不先选上“显示所有文件和文件夹”,就找不到Tempory Internet Files文件夹,此文件夹是隐藏的。)把此文件夹删除就可以了。但是删除此文件夹时会提示“你需要权限”。打开文件夹(这里此文件夹也是快捷方式形式的,点击也打不开。但不知道为什么,我第一次打开时不是快捷方式的,所以就很顺利地进去了。至于这个快捷方式形式的文件夹如何打开,可能按照http://blog.csdn.net/jszj/archive/2009/06/03/4238373.aspx所说的在命令行模式下可以进去吧,但是由于我对DOS命令不熟悉,所以也不知道怎么打开。但是我知道不是只有这一种办法可以打开这类文件夹的,有时候用资源管理器也可以打开。我就是这样打开Tempory Internet Files文件夹的),把里面的内容逐一进行删除,除了那个删不掉的文件以外(当然也可能不用删那么多文件,可是到底删除哪些文件就可以解决这个问题呢?谁也不清楚,所以就只好将能删的都删了。^_^)删除这些文件后,再次启动MSDN,好了!哈哈,忙乎了大半个上午,总算问题给解决了。又学了一招了!哈哈!以后遇到此类问题也就知道如何下手解决了!

VC中的CImage类
(VC++)一个比CBitmap更优秀的类
    今天查找如何实现DIB位图的背景透明的资料时,发现有论坛上有人问到CImage类的透明问题,于是对其很感兴趣。于是搜集了一些资料,发现以下内容对该类的介绍和使用介绍比较详细,于是就拷贝至此,以备后用。

VC中向数据库中添加图形、声音文件的方法
1.添加图形数据到数据库的方法。
   主要是函数AppendChunk.
 try
 {
         UpdateData(true);
   if (m_id.IsEmpty())
   {
   // MessageBox(L"请输入编号!");
    MessageBox("请输入编号!");
    return;
   }
   if (m_name.IsEmpty())
   {
             // MessageBox(L"请输入姓名!");
      MessageBox("请输入姓名!");
    return;
   }
   char *m_buffer;  //从BMP文件中读取的数据存放在此变量中
   CFile file;
   if (!file.Open(m_strText, CFile::modeRead))
   {
    //MessageBox(L"无法打开BMP文件!");
     MessageBox("无法打开BMP文件!");
    return;
   }
   DWORD m_filelen;  //保存文件的长度
   m_filelen = (DWORD)file.GetLength();
   m_buffer = new char[m_filelen+1];  //根据文件长度分配数组空间
   //读取BMP文件到m_buffer中
   if (file.Read(m_buffer, m_filelen) != m_filelen)
   {
    MessageBox("读取BMP文件时出现错误!");
    return;
   }
   ADOConn m_adoConn;
   m_adoConn.OnInitADOConn();
   _bstr_t sql;
   //sql = "select * from picture";
   sql = "select * from picture1";
   _RecordsetPtr m_pRecordset;
   m_pRecordset = m_adoConn.GetRecordSet(sql);
   m_pRecordset->AddNew(); //添加新行
   VARIANT varblob;
   SAFEARRAY *psa;
   SAFEARRAYBOUND  rgsabound[1];
   rgsabound[0].lLbound = 0;
   rgsabound[0].cElements = m_filelen;
   //创建数组
   psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
   //将m_buffer中的图像数据写入数组psa
   for(long i=0;i<(long)m_filelen;i++)
   {
    SafeArrayPutElement(psa, &i, m_buffer++);
   }
   varblob.vt = VT_ARRAY |VT_UI1;//    VARIANT 中存放数据的类型为VT_ARRAY(指向SAFEARRAY的指针)和VT_UI1(指示byte的指针)
   varblob.parray = psa;                        //    parray为存放指向SAFEARRAY类型的指针
   m_pRecordset->GetFields()->GetItem(L"id")->Value = (_bstr_t)m_id;
   m_pRecordset->GetFields()->GetItem(L"name")->Value = (_bstr_t)m_name;
   //调用AppenChunk函数,将图像数据写入photo字段
   m_pRecordset->GetFields()->GetItem(L"photo")->AppendChunk(varblob);
   //更新数据库
   m_pRecordset->Update();
   m_adoConn.ExitConnect();
 }
 catch (...)
 {
  MessageBox("操作失败");
  return;
 }
  m_combo.InsertString(m_idNum, m_id);//将新添加的编号加入下拉列表中
 m_combo.SetCurSel(m_idNum);
 m_idNum++;
 //清空控件中的内容
 GetDlgItem(IDC_ID)->SetWindowText("");
 GetDlgItem(IDC_NAME)->SetWindowText("");
 m_picture.SetBitmap(NULL);
 
2.添加声音文件到数据库中
  CString strName, strkzm;
 try
 {
          CFile  file;
    if (!file.Open(m_soundFileName, CFile::modeRead))
    {
                MessageBox("读取文件失败",MB_OK);
    return;
    }
    DWORD filelen;
    filelen = (DWORD)file.GetLength();
    char* m_pBuffer;
    m_pBuffer = new char[filelen+1];
    if (file.Read(m_pBuffer, filelen)!=filelen) //将文件中的数据读入缓冲区
    {
                 MessageBox("读取文件出错!");
     return;
    }
          ADOConn m_adoConn;
    m_adoConn.OnInitADOConn();
    _bstr_t sql;
    sql = "select * from sound1";
    _RecordsetPtr m_pRecordset;
    m_pRecordset = m_adoConn.GetRecordSet(sql);
    m_pRecordset->AddNew();   //将声音文件添加到数据库中
    VARIANT varblob;
    SAFEARRAY *psa;
    SAFEARRAYBOUND sabound[1];
    sabound[0].lLbound =0;
    sabound[0].cElements = filelen;
    //创建SAFEARRAY数组
    psa = SafeArrayCreate(VT_UI1, 1, sabound);
    //给SAFEARRAY数组赋值
    for (long i=0;i<(long)filelen;i++)    //将缓冲区中的声音文件数据逐字节放入到SAFEARRAY指针中
    {
     SafeArrayPutElement(psa, &i, m_pBuffer);
    }
    //将SAFEARRAY数组封装到VARIANT中,用于将其数据传递出去
    varblob.vt = VT_ARRAY | VT_UI1;
    varblob.parray= psa;
   
    int filenamelen = m_soundFileName.GetLength();
    strkzm = m_soundFileName.Right(3);
    strName = m_soundFileName.Left(filenamelen-4);
    
    m_pRecordset->GetFields()->GetItem("name")->Value = (_bstr_t)strName;
    m_pRecordset->GetFields()->GetItem("postfix")->Value = (_bstr_t)strkzm;
    m_pRecordset->GetFields()->GetItem("sound")->AppendChunk(varblob);
    m_pRecordset->Update();
    m_adoConn.ExitConnect();
 }
 catch(...)   //捕捉所有的异常
 {
  MessageBox("操作失败!");
  return;
 }
 m_soundCombo.AddString(strName);//将文件名加入到文件名列表中
 m_soundCombo.SetCurSel(0);
 MessageBox("添加声音文件成功!");
 
3.添加文本文件到数据库中:
  UpdateData(true);
   ADOConn m_adoConn;
   m_adoConn.OnInitADOConn();
   CString str="";
   TCHAR curDir[500];
   int len= ::GetCurrentDirectory(500, curDir);
   if (curDir[len] !='\\')
   {
    curDir[len++]='\\';
    curDir[len]= '\0';
   }
   CString filepath = curDir;
   filepath += m_textFileName;
   CFile file(filepath, CFile::modeRead);
   char sread[10000];
   file.Read(sread, 10000);
   for(int i=0;i<file.GetLength();i++)
   {
    str +=sread[i];
   }
 try
 {
        _bstr_t sql;
    sql = "select * from textTable";
    _RecordsetPtr m_pRecordset;
    m_pRecordset = m_adoConn.GetRecordSet(sql);
    m_pRecordset->AddNew();   //将声音文件添加到数据库中
         
    CString strName=m_textFileName.Left(m_textFileName.GetLength()-4);//.Right(3);
    m_pRecordset->GetFields()->GetItem("name")->Value = (_bstr_t)strName;
    m_pRecordset->GetFields()->GetItem(L"textstr")->AppendChunk((_bstr_t)str);
    m_pRecordset->Update();
    m_adoConn.ExitConnect();
    file.Close();
 }
 catch(...)   //捕捉所有的异常
 {
  MessageBox("操作失败!");
  return;
 }
 MessageBox("添加文本文件成功!");
4. 从数据库中读出数据,并将其写入文件:
    以下是从数据库读出文本数据,并生成文件:
    ADOConn m_adoConn;
 m_adoConn.OnInitADOConn();
 _bstr_t sql;
 CString strName= m_textFileName.Left(m_textFileName.GetLength() - 4 );
 sql = "select * from textTable where name = '" + (_bstr_t)strName + "' ";
 _RecordsetPtr m_pRecordset = m_adoConn.GetRecordSet(sql);
 long filelen=m_pRecordset->GetFields()->GetItem("textstr")->ActualSize;
 char *m_pBuffer;
 if (filelen>0)
 {
  _variant_t  varblob;
  varblob=m_pRecordset->GetFields()->GetItem("textstr")->GetChunk(filelen);
  if (varblob.vt == (VT_ARRAY | VT_UI1))
  {
   if (m_pBuffer = new char[filelen+1])
   {
    char *pBuf=new char[filelen+1];
    SafeArrayAccessData(varblob.parray, (void**)&pBuf);
    memcpy(m_pBuffer, pBuf, filelen);
    SafeArrayUnaccessData(varblob.parray);
   }
  }
 }
 //获取当前文件所在的路径
   TCHAR curDir[500];
   int len= ::GetCurrentDirectory(500, curDir);
   if (curDir[len] !='\\')
   {
    curDir[len++]='\\';
    curDir[len]= '\0';
   }
   CString filepath = curDir;
  filepath +=m_textFileName;
   CFile file(filepath, CFile::modeCreate|CFile::modeReadWrite);
   file.Write(m_pBuffer, filelen);
   file.Flush();
   file.Close();
以上要注意:数据库表中存放图形、声音、文本数据的字段的数据类型应该为OLE 对象,否则从数据库中读取数据生成的文件打开将是乱码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值