CurlFTP.h
#pragma once
#include <curl/curl.h>
#include <string>
/*
功能:上传文件到FTP服务器,支持进度条,支持上传操作完成后通知调用者线程。
限制:[1]当前进程只能有一个CCurlFTP实例
[2]只支持逐个文件上传
作者:kagula
时间:2010-08-19
最后修改时间:2012-10
修改次数:4
修改日志:[1-1]修正makeFullName函数不能正确处理含“\\”字符文件路径的问题。
[1-2]添加断点上传功能。
[2-1]为方便应用层以同步方式使用,做了些必要修改。
[2-2]对代码进行改进。
[3-1]添加删除文件夹功能。
[4-1]添加测试FTP连接功能
环境:VS2008SP1 + libcurl 7.21.1 + boost 1.43
备注:可以从下面的URL下载libcurl,并编译成DLL(Release)形式!
http://curl.haxx.se/libcurl/
《libcurl教程》
http://blog.csdn.net/JGood/archive/2009/11/08/4787670.aspx
索引关键词:VC实现上传文件到Windows FTP服务器
*/
#define _CF_BUF_SIZE_ 256
//下面是回调Class
class IUploadEvent
{
public:
//回调函数只是完成变量之间赋值,必须马上返回控制权给CCurlFTP实例所在的线程
//通知当前进度,取值范围0~1
virtual void setProgress(float fV)=0;
//通知上传操作完成,true成功,false失败!
virtual void done(bool bSuccess)=0;
};
//CCurlFTP_FILE_INFO这个结构体用来存放文件在服务器中的信息
struct CCurlFTP_FILE_INFO{
std::string urlFileName;
time_t filetime;
double filesize;
};
//下面是实现上传功能的Class
class CCurlFTP
{
public:
CCurlFTP(void);
~CCurlFTP(void);
/********************************以下为外部接口*************************************/
//设置FTP服务器地址
//入口参数:ip: URL地址
// usrpwd:用户名:密码 注意:中间用“:”符号分隔
//接口使用示例:ip=L"ftp://192.168.0.211"; usrpwd=L"lijun:123456";
//
//同时[1]测试服务器是否可以连接[2]如果可以连接,是否支持断点续传?
//运行结果可以通过调用isSupportREST()方法和isAbilityConn()方法得到。
void setServeAddr(const wchar_t *ip,const wchar_t *usrpwd);
//上传文件名为filename的文件到FTP服务器。注意,这是个同步函数!
//参数说明:源文件名(可以是绝对路径),上传后文件名
//接口使用示例:filename=L"待上传测试文件.rar";uploadFilename=NULL;不更改文件名
bool uploadFile(wchar_t *filename,wchar_t *uploadFilename);
//设置回调实例
void setCaller(IUploadEvent *call)
{
m_call = call;
}
//取消当前上传任务
void abort();
//当前上传任务是否在运行
bool isRun() { return m_bRun; }
//删除指定的文件,删除成功返回true
bool deleteFile(std::wstring filename);
bool deleteFile(std::string fileName);
//删除指定的文件夹,删除成功返回true
bool deleteFolder(std::wstring foldername);
bool deleteFolder(std::string foldername);
//新建指定的文件夹,成功返回true
bool createFolder(std::wstring foldername);
bool createFolder(std::string foldername);
//文件服务器是否支持断点续传
bool isSupportREST();
//读取文件在文件服务器的信息
bool getRemoteFileInfo(std::string sRemoteFileName,CCurlFTP_FILE_INFO &FileInfo);
//测试FTP服务器是否可以连接
int isAbilityConn();
/********************************以下为内部接口*************************************/
//读取文件中的数据。回调函数
static size_t readFromDisk(char *bufptr, size_t size, size_t nitems, void *userp);
char * w2c(const wchar_t *pwstr,char *pcstr, size_t len);
long long getFileSize() { return m_filesize; }
float getProgress() { return m_fProgress; }
int getStatus() { return m_nStatus; }
int getIsAbilityConn() { return m_isAbilityConn; }
void setAbilityConn(int n) { m_isAbilityConn = n; }
protected:
private:
static curl_off_t m_filesize;
//上传操作状态
/*
m_nStatus:执行文件上传的状态
-1,没有上传任务 或 上传文件失败
0,上传文件成功
1,正在上传文件
*/
static int m_nStatus;
//用户是否终止
static bool m_bAbort;
//当前是否在执行上传操作,若是m_bRun=true,elsewise the variable m_bRun is false!
static bool m_bRun;
//最后一次执行上传的远程文件信息
struct CCurlFTP_FILE_INFO m_fi;
static float m_fProgress;
//
static bool m_isREST;
//是否可连接
/*
21:可连接
67:密码错误
7: 无法连接,可能IP
*/
static int m_isAbilityConn;
CURL * m_easyhandle;
char m_ip[_CF_BUF_SIZE_];
char m_usrpwd[_CF_BUF_SIZE_];
char m_errbuf[_CF_BUF_SIZE_];
wchar_t m_filename[_CF_BUF_SIZE_];
static IUploadEvent *m_call;
bool isFileExist(wchar_t * filename);
bool isFileRead(wchar_t * filename);
char * makeFullName(char *ip,wchar_t * filename,wchar_t * uploadfilename);
};
CurlFTP.cpp
#include "StdAfx.h"
#include "CurlFTP.h"
#include <io.h>
#include <stdlib.h>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include "MyUtility.h"
#pragma comment(lib,"libcurl_imp.lib")
//并把libcurl.dll文件放到当前程序的运行路径中
boost::mutex g_ftp;
IUploadEvent *CCurlFTP::m_call = NULL;
curl_off_t CCurlFTP::m_filesize = 0;
float CCurlFTP::m_fProgress = 0;
int CCurlFTP::m_nStatus = -1;//没有上传任务
bool CCurlFTP::m_bRun = false;//当前没有任务运行
bool CCurlFTP::m_bAbort = false;
bool CCurlFTP::m_isREST = false;
int CCurlFTP::m_isAbilityConn = 1;//=1状态未知,=0成功
static size_t throw_away(void *ptr, size_t size, size_t nmemb, void *data)
{
(void)ptr;
(void)data;
/* we are not interested in the headers itself,
so we only return the size we would have saved ... */
return (size_t)(size * nmemb);
}
CCurlFTP::CCurlFTP(void)
{
memset(m_ip,0,sizeof(m_ip));
memset(m_usrpwd,0,sizeof(m_usrpwd));
curl_global_init(CURL_GLOBAL_DEFAULT);
}
CCurlFTP::~CCurlFTP(void)
{
curl_global_cleanup();
}
void CCurlFTP::setServeAddr(const wchar_t *ip,const wchar_t *usrpwd)
{
boost::lock_guard<boost::mutex> l(g_ftp);
w2c(ip,m_ip,wcslen(ip)*2+1);
w2c(usrpwd,m_usrpwd,wcslen(usrpwd)*2+1);
if( 0 == isAbilityConn() )
{
m_isREST = isSupportREST();
}
}
//return the number of bytes it wrote in the buffer. Returning 0 will signal the end of the upload
size_t CCurlFTP::readFromDisk(char *bufptr, size_t size, size_t nitems, void *userp)
{
boost::lock_guard<boost::mutex> l(g_ftp);
#define fp ((FILE*)userp)
static int nRead;
if (fp==NULL)
{
OutputDebugStringA("size_t CCurlFTP::readFromDisk fp==NULL return 0;");
return 0;
}
if (CCurlFTP::m_bRun==false)
{
OutputDebugStringA("size_t CCurlFTP::readFromDisk CCurlFTP::m_bRun==false return 0;");
return 0;
}
if (feof(fp))
{
OutputDebugStringA("size_t CCurlFTP::readFromDisk feof(fp) return 0;");
return 0;
}
if (CCurlFTP::m_bAbort)
{
OutputDebugStringA("size_t CCurlFTP::readFromDisk CCurlFTP::m_bAbort return 0;");
return 0;
}
//显示当前文件位置
int nPos;
nPos = ftell(fp);
m_fProgress = nPos/( (float)CCurlFTP::m_filesize );
if (CCurlFTP::m_call)
{
m_call->setProgress( m_fProgress );
}
//从磁盘中读数据,通常size=1,nitems=16*1024
nRead = fread(bufptr,size,nitems,fp);
if (nRead >0)
return nRead;
return 0;
}
bool CCurlFTP::uploadFile(wchar_t *bufFileName,wchar_t *uploadFilename)
{
//待上传文件是否存在
if (!isFileExist(bufFileName))
{
OutputDebugString(L"[CCurlFTP::uploadFile]文件不存在\n");
return false;
}
//待上传文件是否可读
if (!isFileRead(bufFileName))
{
OutputDebugString(L"[CCurlFTP::uploadFile]文件无法读取\n");
return false;
}
//检查是否已经有任务在运行
if (m_bRun)
{
OutputDebugString(L"[CCurlFTP::uploadFile]不允许同时上传多个文件!\n");
return false;
}
{
boost::lock_guard<boost::mutex> l(g_ftp);
m_bRun = true;
m_bAbort = false;
}
//查看路徑里面是不是有文件夾名稱,若是
//為用戶新建文件夾(不管文件夾是否存在)
std::wstring wsFN = uploadFilename;
std::string path = ws2s(wsFN);
std::string folder = getFolderName(path);
createFolder(folder);
//取待上传文件,文件大小
OutputDebugString(L"[CCurlFTP::uploadFile]取待上传文件,文件大小!\n");
FILE *file=_wfopen(bufFileName,L"rb");
fseek(file,0,SEEK_END);
m_filesize = ftell(file);
fseek(file,0,SEEK_SET);
memset(m_filename,0,sizeof(m_filename));
wcsncpy(m_filename,bufFileName,sizeof(m_filename)/sizeof(wchar_t));
//判断FTP服务器是否支持断点续传
OutputDebugString(L"[CCurlFTP::uploadFile]判断FTP服务器是否支持断点续传!\n");
bool isREST = m_isREST;
if(!isREST)
OutputDebugString(L"[CCurlFTP::uploadFile] 警告! FTP服务器不支持断点续传!\n");
//取要上传的文件在服务器中的信息
OutputDebugString(L"[CCurlFTP::uploadFile]取要上传的文件在服务器中的信息!\n");
char * pURL = makeFullName(m_ip,bufFileName,uploadFilename);
OutputDebugStringA("m_ip=");OutputDebugStringA(m_ip);OutputDebugStringA("\n");
OutputDebugStringW(L"bufFileName=");OutputDebugStringW(bufFileName);OutputDebugStringA("\n");
OutputDebugStringW(L"uploadFilename=");OutputDebugStringW(uploadFilename);OutputDebugStringA("\n");
OutputDebugStringA("pURL=");OutputDebugStringA(pURL);OutputDebugStringA("\n");
bool isFileInfo = getRemoteFileInfo(std::string(pURL),m_fi);
if(isFileInfo)
{
OutputDebugString(L"[CCurlFTP::uploadFile] FTP服务器已经存在同名文件!\n");
}
//准备上传
OutputDebugString(L"[CCurlFTP::uploadFile]准备上传!\n");
m_fProgress = .0f;
long long oldRemoteFileSize=0;
CURLcode code;
m_easyhandle = curl_easy_init();
curl_easy_setopt(m_easyhandle, CURLOPT_URL, pURL);
curl_easy_setopt(m_easyhandle, CURLOPT_READFUNCTION, CCurlFTP::readFromDisk);
curl_easy_setopt(m_easyhandle, CURLOPT_READDATA , file); //文件指针传给readFromDisk!
curl_easy_setopt(m_easyhandle, CURLOPT_UPLOAD , 1L);
curl_easy_setopt(m_easyhandle, CURLOPT_INFILESIZE_LARGE, m_filesize);
curl_easy_setopt(m_easyhandle, CURLOPT_USERPWD , m_usrpwd);
curl_easy_setopt(m_easyhandle, CURLOPT_ERRORBUFFER, m_errbuf);
curl_easy_setopt(m_easyhandle, CURLOPT_FTPPORT, "-"); /* disable passive mode */
curl_easy_setopt(m_easyhandle, CURLOPT_FTP_CREATE_MISSING_DIRS, 1L);
curl_easy_setopt(m_easyhandle, CURLOPT_VERBOSE, 1L);
//如果(支持断点续传 &&
//要上传的文件已经存在于服务器中 &&
//要上传的文件上次只上传了一部份)
if(isREST && isFileInfo && m_fi.filesize<m_filesize)
{
//进行断点续传
OutputDebugStringA("进行断点续传\n");
fseek(file,m_fi.filesize,SEEK_SET);
oldRemoteFileSize = m_fi.filesize;
curl_easy_setopt(m_easyhandle, CURLOPT_NOBODY, 0L);
curl_easy_setopt(m_easyhandle, CURLOPT_HEADER, 0L);
//curl_easy_setopt(m_easyhandle, CURLOPT_RESUME_FROM_LARGE, true);
curl_easy_setopt(m_easyhandle, CURLOPT_APPEND, 1L);
} else
{
//默认 启动覆盖上传任务
OutputDebugStringA("默认 启动覆盖上传任务\n");
curl_easy_setopt(m_easyhandle, CURLOPT_APPEND, 0L);
}
m_nStatus=1;//设置“正在上传”状态
code = curl_easy_perform(m_easyhandle);
curl_easy_cleanup(m_easyhandle);
{
boost::lock_guard<boost::mutex> l(g_ftp);
fclose(file);
m_bRun = false;
m_bAbort = false;
}
bool bR;
if(code!=0)
{
OutputDebugString(L"上传可能失败!");
bR = false;
} else {
OutputDebugString(L"上传可能成功!");
bR=true;
}
//Patch libcurl返回状态不准确的问题.begin
OutputDebugString(L"Patch libcurl返回状态不准确的问题.begin\n");
getRemoteFileInfo(std::string(pURL),m_fi);
if( true==bR &&
m_fi.filesize==oldRemoteFileSize &&
m_filesize!=oldRemoteFileSize)
{
bR = false;
OutputDebugString(L"Patch libcurl返回状态不准确的问题.修正为false!\n");
std::wstring wsFN;
if(uploadFilename==NULL)
{
wsFN=bufFileName;
if(wsFN.find_last_of(L'//')!=-1)
wsFN = wsFN.substr(wsFN.find_last_of(L'//')+1,wsFN.length());
else if(wsFN.find_last_of(L'\\')!=-1)
wsFN = wsFN.substr(wsFN.find_last_of(L'\\')+1,wsFN.length());
}
else
{
wsFN = uploadFilename;
}
OutputDebugStringA("删除FTP上的文件!\n");
deleteFile(wsFN.c_str());
} else if( code==18 &&
m_fi.filesize!=oldRemoteFileSize &&
m_filesize==m_fi.filesize)
{
bR = true;
OutputDebugString(L"Patch libcurl返回状态不准确的问题.修正为true!\n");
}
OutputDebugString(L"Patch libcurl返回状态不准确的问题.end\n");
//Patch libcurl返回状态不准确的问题.end
if(bR)
{
m_nStatus = 0;
}
else
{
m_nStatus = -1;
char msg[_CF_BUF_SIZE_];
sprintf(msg,"uploadFile error code=%d\n",code);
OutputDebugStringA(msg);
}
if(m_call) //回调
m_call->done(bR);
//不管上传有没有成功,上传后,重置m_fProgress(上传进度)
m_fProgress = .0f;
if (!bR)
OutputDebugStringA(m_errbuf);
return bR;
}
bool CCurlFTP::deleteFile(std::wstring fileName)
{
char buf[MAX_PATH];
memset(buf,0,sizeof(buf));
w2c(fileName.c_str(),buf,fileName.length()*2+1);
return deleteFile(std::string(buf));
}
bool CCurlFTP::deleteFile(std::string fileName)
{
std::string sCmd("DELE ");
sCmd = sCmd + fileName;
struct curl_slist *headers=NULL; /* init to NULL is important */
headers = curl_slist_append(headers, sCmd.c_str());
m_easyhandle = curl_easy_init();
curl_easy_setopt(m_easyhandle, CURLOPT_URL, m_ip);
curl_easy_setopt(m_easyhandle, CURLOPT_USERPWD , m_usrpwd);
curl_easy_setopt(m_easyhandle, CURLOPT_ERRORBUFFER, m_errbuf);
curl_easy_setopt(m_easyhandle, CURLOPT_QUOTE, headers);
// curl_easy_setopt(easyhandle, CURLOPT_POSTQUOTE, headers); // 在数据传输之后操行删除操作
CURLcode code = curl_easy_perform(m_easyhandle); /* transfer ftp data! */
curl_slist_free_all(headers); /* free the header list */
curl_easy_cleanup(m_easyhandle);
bool bR = (code!=0)?false:true;
if (!bR)
OutputDebugStringA(m_errbuf);
return bR;
}
bool CCurlFTP::deleteFolder(std::wstring foldername)
{
char buf[MAX_PATH];
memset(buf,0,sizeof(buf));
w2c(foldername.c_str(),buf,foldername.length()*2+1);
return deleteFolder(std::string(buf));
}
bool CCurlFTP::deleteFolder(std::string foldername)
{
std::string sCmd("RMD ");
sCmd = sCmd + foldername;
struct curl_slist *headers=NULL; /* init to NULL is important */
headers = curl_slist_append(headers, sCmd.c_str());
m_easyhandle = curl_easy_init();
curl_easy_setopt(m_easyhandle, CURLOPT_URL, m_ip);
curl_easy_setopt(m_easyhandle, CURLOPT_USERPWD , m_usrpwd);
curl_easy_setopt(m_easyhandle, CURLOPT_ERRORBUFFER, m_errbuf);
curl_easy_setopt(m_easyhandle, CURLOPT_QUOTE, headers);
//curl_easy_setopt(m_easyhandle, CURLOPT_POSTQUOTE, headers); // 在数据传输之后操行删除操作
CURLcode code = curl_easy_perform(m_easyhandle); /* transfer ftp data! */
curl_slist_free_all(headers); /* free the header list */
curl_easy_cleanup(m_easyhandle);
bool bR = (code!=0)?false:true;
if (!bR)
OutputDebugStringA(m_errbuf);
return bR;
}
bool CCurlFTP::createFolder(std::wstring foldername)
{
char buf[MAX_PATH];
memset(buf,0,sizeof(buf));
w2c(foldername.c_str(),buf,foldername.length()*2+1);
return createFolder(std::string(buf));
}
bool CCurlFTP::createFolder(std::string foldername)
{
std::string sCmd("MKD ");
sCmd = sCmd + foldername;
struct curl_slist *headers=NULL; /* init to NULL is important */
headers = curl_slist_append(headers, sCmd.c_str());
m_easyhandle = curl_easy_init();
curl_easy_setopt(m_easyhandle, CURLOPT_URL, m_ip);
curl_easy_setopt(m_easyhandle, CURLOPT_USERPWD , m_usrpwd);
curl_easy_setopt(m_easyhandle, CURLOPT_ERRORBUFFER, m_errbuf);
curl_easy_setopt(m_easyhandle, CURLOPT_QUOTE, headers);
//curl_easy_setopt(m_easyhandle, CURLOPT_POSTQUOTE, headers); // 在数据传输之后操行删除操作
CURLcode code = curl_easy_perform(m_easyhandle); /* transfer ftp data! */
curl_slist_free_all(headers); /* free the header list */
curl_easy_cleanup(m_easyhandle);
bool bR = (code!=0)?false:true;
if (!bR)
OutputDebugStringA(m_errbuf);
return bR;
}
int CCurlFTP::isAbilityConn()
{
m_easyhandle = curl_easy_init();
curl_easy_setopt(m_easyhandle, CURLOPT_URL, m_ip);
curl_easy_setopt(m_easyhandle, CURLOPT_USERPWD , m_usrpwd);
curl_easy_setopt(m_easyhandle, CURLOPT_ERRORBUFFER, m_errbuf);
curl_easy_setopt(m_easyhandle, CURLOPT_TIMEOUT_MS,3000);
CURLcode code = curl_easy_perform(m_easyhandle);
if(code>=0)
curl_easy_cleanup(m_easyhandle);
m_isAbilityConn = code;
return m_isAbilityConn;
}
bool CCurlFTP::isSupportREST()
{
std::string sCmd("REST 100");
struct curl_slist *headers=NULL; // init to NULL is important
headers = curl_slist_append(headers, sCmd.c_str());
m_easyhandle = curl_easy_init();
curl_easy_setopt(m_easyhandle, CURLOPT_URL, m_ip);
curl_easy_setopt(m_easyhandle, CURLOPT_USERPWD , m_usrpwd);
curl_easy_setopt(m_easyhandle, CURLOPT_ERRORBUFFER, m_errbuf);
curl_easy_setopt(m_easyhandle, CURLOPT_QUOTE, headers);
CURLcode code = curl_easy_perform(m_easyhandle);
curl_slist_free_all(headers); // free the header list
curl_easy_cleanup(m_easyhandle);
bool bR = code==0?true:false;
if (!bR)
OutputDebugStringA(m_errbuf); OutputDebugStringA("\n");
return bR;
}
bool CCurlFTP::getRemoteFileInfo(std::string url,CCurlFTP_FILE_INFO &FileInfo)
{
CURL *curl;
CURLcode res;
FileInfo.urlFileName = url;
m_easyhandle = curl_easy_init();
if(m_easyhandle) {
curl_easy_setopt(m_easyhandle, CURLOPT_URL, url.c_str());
curl_easy_setopt(m_easyhandle, CURLOPT_USERPWD , m_usrpwd);
/* No download if the file */
curl_easy_setopt(m_easyhandle, CURLOPT_NOBODY, 1L);
/* Ask for filetime */
curl_easy_setopt(m_easyhandle, CURLOPT_FILETIME, 1L);
/* No header output: TODO 14.1 http-style HEAD output for ftp */
curl_easy_setopt(m_easyhandle, CURLOPT_HEADERFUNCTION, throw_away);
curl_easy_setopt(m_easyhandle, CURLOPT_HEADER, 0L);
/* Switch on full protocol/debug output */
/* curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); */
curl_easy_setopt(m_easyhandle, CURLOPT_ERRORBUFFER, m_errbuf);
res = curl_easy_perform(m_easyhandle);
if(CURLE_OK == res) {
res = curl_easy_getinfo(m_easyhandle, CURLINFO_FILETIME, &FileInfo.filetime);
///if((CURLE_OK == res) && filetime) {...}
res = curl_easy_getinfo(m_easyhandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &FileInfo.filesize);
//if((CURLE_OK == res) && (filesize>0)) {...}
}
/* always cleanup */
curl_easy_cleanup(m_easyhandle);
}
if(CURLE_OK == res)
return true;
return false;
}
bool CCurlFTP::isFileExist(wchar_t * filename)
{
if(_waccess(filename, 0) == 0)
return true;
return false;
}
bool CCurlFTP::isFileRead(wchar_t * filename)
{
if(_waccess(filename, 4) == 0)
return true;
return false;
}
void CCurlFTP::abort()
{
if(m_bRun)
m_bAbort = true;
}
char * CCurlFTP::makeFullName(char *ip,wchar_t * filename,wchar_t * uploadFilename)
{
static char cR[_CF_BUF_SIZE_];
int nPos = strlen(ip);
std::wstring wsFN=filename;
if(wsFN.find_last_of(L'//')!=-1)
wsFN = wsFN.substr(wsFN.find_last_of(L'//')+1,wsFN.length());
else if(wsFN.find_last_of(L'\\')!=-1)
wsFN = wsFN.substr(wsFN.find_last_of(L'\\')+1,wsFN.length());
memset(cR,0,sizeof(cR));
strncpy(cR,ip,sizeof(cR));
cR[nPos]='/';
//拼接字符串
char buf[_CF_BUF_SIZE_];
memset(buf,0,sizeof(buf));
if (uploadFilename==NULL)
w2c(wsFN.c_str(),buf,wsFN.length()*2+1);
else
w2c(uploadFilename,buf,wcslen(uploadFilename)*2+1);
strncpy((cR+nPos+1),buf,_CF_BUF_SIZE_-nPos);
return cR;
}
char *CCurlFTP::w2c(const wchar_t *pwstr,char *pcstr, size_t len)
{
int nlength=wcslen(pwstr);
//获取转换后的长度
int nbytes = WideCharToMultiByte( 0, // specify the code page used to perform the conversion
0, // no special flags to handle unmapped characters
pwstr, // wide character string to convert
nlength, // the number of wide characters in that string
NULL, // no output buffer given, we just want to know how long it needs to be
0,
NULL, // no replacement character given
NULL ); // we don't want to know if a character didn't make it through the translation
// make sure the buffer is big enough for this, making it larger if necessary
if(nbytes>len) nbytes=len;
// 通过以上得到的结果,转换unicode 字符为ascii 字符
WideCharToMultiByte( 0, // specify the code page used to perform the conversion
0, // no special flags to handle unmapped characters
pwstr, // wide character string to convert
nlength, // the number of wide characters in that string
pcstr, // put the output ascii characters at the end of the buffer
nbytes, // there is at least this much space there
NULL, // no replacement character given
NULL );
return pcstr ;
}