一、Outlook Object Model简介

  Outlook Object Model(OOM)是outlook为开发者提供的一个COM组件,我们可以在程序中使用它来发送邮件、管理邮箱等。相关介绍可以参见以下链接:

  https://msdn.microsoft.com/en-us/library/ms268893.aspx

  可能有人会说用shellExcute也可以实现对outlook的操作,这里正好说到为什么要用Outlook Object Model的原因之一。如果用shellExcute来操作,必须严格保证传入的参数的编码和长宽字节符合outlook的要求,也就是说在进行实际的邮件操作之前需要对字符串参数进行繁琐的判断和处理(如果是自己写的小程序玩一玩的请随意),而用OOM来操作则不需要关心这些问题。

  OOM的另外一个优点就是接口简单,使用方便。要兼容所有的outlook版本也比较方便,这个下面再说。

二、在c++中的使用方式

  因为微软的官网上关于OOM的例程主要是VBA和c#的,相关的例子有很多,这里就不多介绍了,主要介绍怎么在c++中使用。

  可以参见下面这个链接:

  http://1code.codeplex.com/SourceControl/changeset/view/60353#585447

  主要有三种方式来使用OOM:

  1、在程序中用#import命令来导入OOM的类型库,然后使用c++里的智能指针来调用相关的函数和属性。例程如下:

 #import "C:\Program Files\Common Files\Microsoft Shared\OFFICE15\mso.dll" no_namespace \
rename("DocumentProperties","_DocumentProperties") \
rename("RGB", "MsoRGB") #import "D:\office\Office15\MSOUTL.OLB" \
rename("GetOrganizer", "GetOrganizerAE")\
rename_namespace("Outlook") using namespace Outlook;
void SendMail_UsingOOM(const std::wstring &, const std::wstring &, const std::wstring &, const std::wstring &, const std::wstring &, const std::wstring &, bool);
int main()
{
SendMail_UsingOOM(L"a@email.com", L"aa@email.com", L"aaa@email.com", L"OOM_Test", L"It`s OOM Test.Do not care about it.", L"C:\\Users\\Desktop\\aaa.pdf", true);
return ;
} void SendMail_UsingOOM(const std::wstring &to,const std::wstring &cc,const std::wstring &bcc,const std::wstring &subject,const std::wstring &body,const std::wstring &attachmentPath,bool showUI)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
try
{
_ApplicationPtr spApp("Outlook.Application");
_NameSpacePtr pMAPI = spApp->GetNamespace("MAPI");
pMAPI->Logon(); _MailItemPtr olMail(spApp->CreateItem(olMailItem));
if ( == to.size())
{
pMAPI->Logoff();
return;
}
olMail->PutTo(_bstr_t(to.c_str()));
olMail->PutCC(_bstr_t(cc.c_str()));
olMail->PutBCC(_bstr_t(bcc.c_str()));
olMail->PutSubject(_bstr_t(subject.c_str()));
olMail->PutBody(_bstr_t(body.c_str()));
olMail->Save(); if ( != attachmentPath.size())
{
AttachmentsPtr olAtta = olMail->GetAttachments();
olAtta->Add(attachmentPath.c_str(), , , attachmentPath.c_str());
}
HRESULT result = NULL;
if (showUI)
{
result = olMail->Display(showUI);
}
else
{
result = olMail->Send();
}
pMAPI->Logoff();
}
catch (_com_error &err)
{
wprintf(L"Outlook throws the error: %s\n", err.ErrorMessage());
wprintf(L"Description: %s\n", (LPCWSTR)err.Description());
}
CoUninitialize();
}

  要注意,这里的智能指针并不是你自己定义的,而是COM组件提供的。比如上面程序里的_ApplicationPtr、_NameSpacePtr等,因为是智能指针,也不需要手动释放,非常方便。

  这里需要导入两个类型库文件:mso.dll和msoutl.olb,前者是office的,后者是outlook的。在导入后会生成mso.tlh、mso.tli、msoutl.tlh和msoutl.tli,tlh文件就相当于头文件,tli文件相当于cpp文件。实际上这个生成的过程只是最开始的时候执行了一次,生成的tlh、tli文件完全可以放到其他项目中包含(可能会产生重名问题,需要手动更改)。

  在实际过程中,会发现import可能出现很多乱七八糟的错误,比如这样:

 c:\OutlookAdd-In\OutlookAdd-In\debug\msoutl.tlh() : error C2556: 'Outlook::AddressEntryPtr Outlook::_AppointmentItem::GetOrganizer(void)' : overloaded function differs only by return type from '_bstr_t Outlook::_AppointmentItem::GetOrganizer(void)'
c:\OutlookAdd-In\OutlookAdd-In\debug\msoutl.tlh() : see declaration of 'Outlook::_AppointmentItem::GetOrganizer'

  这其实是方法名冲突的问题,上面这个例子是GetOrganizer(void)这个方法冲突了,可以用rename关键字来修改namespace里实际生成的方法名以避免冲突。这里可以参见以下链接:

  https://social.msdn.microsoft.com/Forums/office/en-US/f51cde52-0faa-42a2-bf61-c18b6f5b0e64/error-while-building-the-outlook-addin-code-with-ms-outlook-2010?forum=outlookdev

  http://blog.chinaunix.net/uid-20791902-id-292054.html

  2、第二种方式是使用COM组件提供的API来调用相关的函数,这种方式需要我们使用特定的API函数,将要调用的方法名和相关参数作为参数传入这些API中。例程如下:

 // laterbind.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
struct Wrap_errno
{
HRESULT hr;
EXCEPINFO info;
};
Wrap_errno AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp,
LPOLESTR ptName, int cArgs...)
{
// Begin variable-argument list
va_list marker;
va_start(marker, cArgs); Wrap_errno err;
memset(&err, , sizeof err); if (!pDisp)
{
err.hr = E_INVALIDARG;
return err;
} // Variables used
DISPPARAMS dp = { NULL, NULL, , };
DISPID dispidNamed = DISPID_PROPERTYPUT;
DISPID dispID;
HRESULT hr; // Get DISPID for name passed
hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, , LOCALE_USER_DEFAULT, &dispID);
if (FAILED(hr))
{
err.hr = hr;
return err;
} // Allocate memory for arguments
VARIANT *pArgs = new VARIANT[cArgs + ];
// Extract arguments...
for (int i = ; i < cArgs; i++)
{
pArgs[i] = va_arg(marker, VARIANT);
} // Build DISPPARAMS
dp.cArgs = cArgs;
dp.rgvarg = pArgs; // Handle special-case for property-puts
if (autoType & DISPATCH_PROPERTYPUT)
{
dp.cNamedArgs = ;
dp.rgdispidNamedArgs = &dispidNamed;
} // Make the call
EXCEPINFO excepInfo;
memset(&excepInfo, , sizeof excepInfo);
hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT,
autoType, &dp, pvResult, &excepInfo, NULL);
if (FAILED(hr))
{
err.hr = hr;
err.info = excepInfo;
delete[] pArgs;
return err;
} // End variable-argument section
va_end(marker); delete[] pArgs; return err;
} Wrap_errno SendMail_UsingOOM(
const std::wstring &to,
const std::wstring &cc,
const std::wstring &bcc,
const std::wstring &subject,
const std::wstring &body,
const std::wstring &attachmentPath,
bool showUI
)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // Define vtMissing for optional parameters in some calls.
VARIANT vtMissing;
vtMissing.vt = VT_EMPTY; CLSID clsid;
HRESULT hr; Wrap_errno err;
memset(&err, , sizeof err); LPCOLESTR progID = L"Outlook.Application";
hr = CLSIDFromProgID(progID, &clsid);
if (FAILED(hr))
{
CoUninitialize();
return err;
}
IDispatch *pOutlookApp = NULL;
hr = CoCreateInstance(
clsid, // CLSID of the server
NULL,
CLSCTX_LOCAL_SERVER, // Outlook.Application is a local server
IID_IDispatch, // Query the IDispatch interface
(void **)&pOutlookApp); // Output if (FAILED(hr))
{
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
}
CoUninitialize();
return err;
} // pNS = pOutlookApp->GetNamespace("MAPI")
IDispatch *pNS = NULL;
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = SysAllocString(L"MAPI"); VARIANT result;
VariantInit(&result);
err = AutoWrap(DISPATCH_METHOD, &result, pOutlookApp, L"GetNamespace", , x);
if (err.hr < )
{
VariantClear(&result);
VariantClear(&x);
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
}
if (pNS != NULL)
{
pNS->Release();
}
CoUninitialize();
return err;
}
pNS = result.pdispVal;
VariantClear(&x);
} // pNS->Logon(vtMissing, vtMissing, true, true)
{
VARIANT vtShowDialog;
vtShowDialog.vt = VT_BOOL;
vtShowDialog.boolVal = VARIANT_TRUE;
VARIANT vtNewSession;
vtNewSession.vt = VT_BOOL;
vtNewSession.boolVal = VARIANT_TRUE; AutoWrap(DISPATCH_METHOD, NULL, pNS, L"Logon", , vtNewSession,
vtShowDialog, vtMissing, vtMissing);
} // pMail = pOutlookApp->CreateItem(Outlook::olMailItem);
IDispatch *pMail = NULL;
{
VARIANT x;
x.vt = VT_I4;
x.lVal = ; // Outlook::olMailItem VARIANT result;
VariantInit(&result);
err = AutoWrap(DISPATCH_METHOD, &result, pOutlookApp, L"CreateItem", , x);
if (err.hr < )
{
if (pMail != NULL)
{
pMail->Release();
}
if (pNS != NULL)
{
pNS->Release();
}
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
}
VariantClear(&x);
VariantClear(&result);
CoUninitialize();
return err;
}
pMail = result.pdispVal;
} // pMail->Subject = _bstr_t(L"Feedback of All-In-One Code Framework");
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = SysAllocString(subject.c_str());
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L"Subject", , x);
VariantClear(&x);
} // pMail->To = _bstr_t(L"codefxf@microsoft.com");
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = SysAllocString(to.c_str());
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L"To", , x);
VariantClear(&x);
} // pMail->cc
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = SysAllocString(cc.c_str());
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L"Cc", , x);
VariantClear(&x);
} // pMail->bcc
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = SysAllocString(bcc.c_str());
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L"Bcc", , x);
VariantClear(&x);
} // pMail->body
{
VARIANT x;
x.vt = VT_BSTR;
x.bstrVal = SysAllocString(body.c_str());
AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L"Body", , x);
VariantClear(&x);
} // pAtta = pMail->GetAttachments
// pAtta->Add(source,type,position,displayname)
IDispatch *pAtta = NULL;
{
VARIANT result;
VariantInit(&result);
err = AutoWrap(DISPATCH_PROPERTYGET, &result, pMail, L"Attachments", );
if (err.hr < )
{
if (pMail != NULL)
{
pMail->Release();
}
if (pNS != NULL)
{
pNS->Release();
}
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
}
VariantClear(&result);
CoUninitialize();
return err;
}
pAtta = result.pdispVal; VARIANT path;
path.vt = VT_BSTR;
path.bstrVal = SysAllocString(attachmentPath.c_str());
VARIANT x;
x.vt = VT_I4;
x.lVal = ;
err = AutoWrap(DISPATCH_METHOD, NULL, pAtta, L"Add", , path, x, x, path);
if (err.hr < )
{
if (pAtta != NULL)
{
pAtta->Release();
}
if (pMail != NULL)
{
pMail->Release();
}
if (pNS != NULL)
{
pNS->Release();
}
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
}
VariantClear(&result);
VariantClear(&path);
CoUninitialize();
return err;
}
VariantClear(&path);
} // pMail->Display(true);
{
VARIANT vtModal;
vtModal.vt = VT_BOOL;
vtModal.boolVal = VARIANT_TRUE;
err = AutoWrap(DISPATCH_METHOD, NULL, pMail, L"Display", , vtModal);
if (err.hr < )
{
if (pMail != NULL)
{
pMail->Release();
}
if (pNS != NULL)
{
pNS->Release();
}
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
}
VariantClear(&vtModal);
CoUninitialize();
return err;
}
} // pNS->Logoff()
err = AutoWrap(DISPATCH_METHOD, NULL, pNS, L"Logoff", );
if (err.hr < )
{
if (pMail != NULL)
{
pMail->Release();
}
if (pNS != NULL)
{
pNS->Release();
}
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
}
CoUninitialize();
return err;
} if (pMail != NULL)
{
pMail->Release();
}
if (pNS != NULL)
{
pNS->Release();
}
if (pOutlookApp != NULL)
{
pOutlookApp->Release();
} CoUninitialize();
err.hr = S_OK;
return err;
}
int main()
{
SendMail_UsingOOM(L"aaa@email.com", L"aaaa@email.com", L"aaaa@email.com", L"OOM_Test", L"It`s OOM Test.Do not care it.", L"C:\\Users\\Desktop\\aaa.pdf", true);
return ;
}

  这里人工地把调用相关的API封装在了AutoWrap函数里,通过调用该函数即可调用相关操作的函数,可以看到主要的API就是CLSIDFromProgID、CoCreateInstance、GetIDsOfNames、Invoke这些COM函数,不熟悉的同学可以去看下com操作相关的资料。

  要使用上面这种方式来操作OOM,你需要首先知道OOM提供的接口以及不同接口、不同属性之间的关系,因为你是通过com的API来调用OOM,它并没有生成外部代码,所以接口的逻辑在外部是不可见的。我们可以通过微软的官网来查看OOM的接口:

  https://msdn.microsoft.com/en-us/library/office/microsoft.office.interop.outlook.aspx

  

  3、第三种使用方式是在MFC工程中使用,依次打开MFC里的类向导-添加类-类型库中的MFC类,如下:

将msoutl.olb中的相关接口导入工程中,可以用哪个就导入哪个接口,类向导会为每个接口都生成一个类。一般是在接口的名字里以“C”代替“_”来命名。如下图:

之后我们就可以以类的形式来使用这些接口了,非常方便。例程如下:

 void CAboutDlg::SendMail_UsingOOM(
const std::wstring &to,
const std::wstring &cc,
const std::wstring &bcc,
const std::wstring &subject,
const std::wstring &body,
const std::wstring &attachmentPath,
bool showUI
)
{
try
{
CApplication olApp;
COleException e;
if (!olApp.CreateDispatch(L"Outlook.Application", &e)) {
CString str;
str.Format(L"CreateDispatch() failed w/error 0x%08lx", e.m_sc);
AfxMessageBox(str, MB_SETFOREGROUND);
return;
}
// Logon. Doesn't hurt if you are already running and logged on...
CNameSpace olNs(olApp.GetNamespace(L"MAPI"));
COleVariant covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
olNs.Logon(covOptional, covOptional, covOptional, covOptional); // Prepare a new mail message
CMailItem olMail(olApp.CreateItem()); if ( != to.size())
olMail.put_To(to.c_str());
else
{
olNs.Logoff();
return;
} if ( != subject.size())olMail.put_Subject(subject.c_str());
if ( != body.size())olMail.put_Body(body.c_str());
if ( != cc.size())olMail.put_CC(cc.c_str());
if ( != bcc.size())olMail.put_BCC(bcc.c_str()); olMail.Save(); if ( != attachmentPath.size())
{
CAttachments olAtta(olMail.get_Attachments());
VARIANT vFile;
vFile = _variant_t(attachmentPath.c_str());//COleVariant(attachmentPath.c_str());
VARIANT vType;
vType.vt = VT_I4;
vType.lVal = ;
VARIANT vPosition;
vPosition.vt = VT_I4;
vPosition.lVal = ;
VARIANT vNick;
vNick.vt = VT_BSTR;
vNick.bstrVal = SysAllocString(attachmentPath.c_str());
olAtta.Add(vFile, vType, vPosition, vNick);
SysFreeString(vNick.bstrVal);
} if (showUI)
{
VARIANT vParam;
vParam.vt = VT_BOOL;
vParam.boolVal = showUI;
olMail.Display(vParam);
}
else
{
// Send the message!
olMail.Send();
}
olNs.Logoff();
//olApp.Quit();
}
catch (_com_error &err)
{
MessageBox(L"Outlook throws the error: %s\n", err.ErrorMessage());
MessageBox(L"Description: %s\n", (LPCWSTR)err.Description());
}
}

在COM里面传递的数据全部要通过VARIANT的类型来传递,它的vt属性表示传递的数据类型,不同的数据类型要用不同的联合属性表示,对应关系参见以下链接:

  http://blog.csdn.net/heaven13483/article/details/8259110

三、eraly binding和late binding

  有的同学说:在非MFC的c++项目里,用第一种方法就够了,干嘛要介绍第二种呢。的确,第一种方法有生成好的接口,方便我们理清接口逻辑关系、进行调试和维护,这种在导入后就已经生成固定接口的方式叫做early binding。程序像调用普通函数一样,在调用时知道这个函数的功能和逻辑,能够进行类型检查等安全措施。而第二种方法里,程序只是调用API告诉com组件我要调用哪个函数,至于它具体执行的功能程序并不知道,这就叫做late binding,也可以叫做动态绑定。

  early binding确实有很多优点,由于接口在导入后就已经固定,它的速度比late binding快一倍;它能进行类型检查方便维护;它的逻辑代码简单......它的缺点是导入类型库并不稳定,有时候总是会出现各种错误。另外还有outlook版本兼容性的问题,这个下面再谈。

  使用late binding的优点正是不需要特殊的类型库或者头文件,直接调用com的API就可以了,也就是说,这种方法在哪都能用。

  详细地介绍可以参考以下链接:

  https://msdn.microsoft.com/en-us/library/office/bb610234.aspx?f=255&MSPPError=-2147217396

  https://support.microsoft.com/en-us/kb/245115

四、outlook版本库兼容问题

  因为可能不同用户安装的的office版本是不同的,所以可能他们使用的outlook版本库是不同的。不同的outlook版本有不同的类型库,对应如下:

Outlook Version Type Library
97 msoutl8.olb
98 msoutl85.olb
2000 msoutl9.olb
2002 msoutl.olb
2003 msoutl.olb

   可以参考以下链接:

  https://support.microsoft.com/en-us/kb/220600

  怎么才能使我们的程序兼容不同的outlook版本呢?有以下两个方案:

  1、使用early bind/MFC,使用要兼容的最早的版本的类库。因为early bind必须要选择一个类库来导入,而office版本是向下兼容的,所以如果我们最早要兼容到office 2003,就选择office 2003的版本的类型库。

  2、使用late binding。这应该是late binding最大的优点了,即它不需要指定outlook版本,它会自动根据用户当前安装的office版本选择要使用的类型库。

在c++中使用Outlook Object Model发送邮件的更多相关文章

  1. 在C#开发中如何使用Client Object Model客户端代码获得SharePoint 网站、列表的权限情况

    自从人类学会了使用火,烤制的方式替代了人类的消化系统部分功能,从此人类的消化系统更加简单,加速了人脑的进化:自从SharePoint 2010开始有了Client Side Object Model ...

  2. Qt 中的对象模型(Object Model)

    原标题:Qt 中的对象模型(Object Model)90不太后,余生皆折腾 本节内容主要讲了 Qt 对象模型比标准 C++ 对象模型多了什么内容,并介绍了组成 Qt 对象模型基础的相关的类.最后说明 ...

  3. 【SSH网上商城项目实战24】Struts2中如何处理多个Model请求

       转自: https://blog.csdn.net/eson_15/article/details/51465067 1. 问题的提出 Struts2中如果实现了ModelDriven<m ...

  4. Selenium的PO模式(Page Object Model)[python版]

     Page Object Model 简称POM  普通的测试用例代码: .... #测试用例 def test_login_mail(self): driver = self.driver driv ...

  5. Selenium的PO模式(Page Object Model)|(Selenium Webdriver For Python)

            研究Selenium + python 自动化测试有近两个月了,不能说非常熟练,起码对selenium自动化的执行有了深入的认识. 从最初无结构的代码,到类的使用,方法封装,从原始函数 ...

  6. 解决在使用client object model的时候报“object does not belong to a list”错误

    在查看别人代码的时候,发现了个有意思的问题,使用client object model将一个文件check in 我使用的是如下语句获取file Microsoft.SharePoint.Client ...

  7. SharePoint Client Object Model API 介绍以及工作原理解析

    CSOM和ServerAPI 的对比 SharePoint从2010开始引入了Client Object Model的API(后文中用CSOM来代替),从名字来看,我们可以简单的看出,该API是面向客 ...

  8. BOM (Browser Object Model) 浏览器对象模型

    l对象的角色,因此所有在全局作用域中声明的变量/函数都会变成window对象的属性和方法; // PS:尝试访问未声明的变量会抛出错误,但是通过查询window对象,可以知道某个可能未声明的对象是否存 ...

  9. 文本对象模型(Document Object Model)

    本文内容: 1. 概述 2. DOM中的节点类型 3. DOM节点的选取 4. 存取元素属性 5.DOM元素的增删 6.小结 ★ 概述 文本对象模型(DOM)是一个能够让程序和脚本动态访问和更新文档内 ...

随机推荐

  1. Tips--8080端口被占用了怎么办

    下面以win7为例: 打开任务管理器   ctrl+alt+. 找到8080端口对应的PID 停止相应PID的进程即可:

  2. Redis 与 Memcached 的区别

    [转]Redis 与 Memcached 的区别 传统 MySQL + Memcached 架构遇到的问题     实际上 MySQL 是适合进行海量数据存储的,通过 Memcached 将热点数据加 ...

  3. js中的运算符和条件语句

    js中的运算符大体上可以分为4类:1算术运算符.2一元操作符.3比较运算符.4逻辑运算符. 算术运算符一般指的是加减乘除求余这五种操作符:+,-,*,/,%.通过算术运算符可以对js中的变量进行操作. ...

  4. SAP-MM:发票、贷方凭证、事后借记、后续贷记

    发票和事后借记 相同点:增加对供应商的应付款 不同点:针对同一订单收货,发票要先于事后借记(事后借记是对供应商后期发票金额的补充):发票和金额.订单数量有关系,而事后借记只是订单金额调整的凭证,仅仅是 ...

  5. 【转】仿Android 联系人SideBar排序,根据拼音A-Z字母快速导航,以及输入搜索条件过滤,显示姓名的文字图片

    1.首先我们把这几个工具类拷贝到自己的项目中,这些都是很常见的类: CharacterParser       –这是用来把中文转成拼音的工具类 PinyinComparator   –拼音首字母的比 ...

  6. PermGen space错误解决方法

    在看下文之前,首先要确认意见事情,就是你是怎样启动tomcat的,我们在平时的开发环境其中,都是通过startup.bat方式启动tomcat的,那么你依照以下的方式,去改动/bin/catalina ...

  7. android控件上面实现提醒信息

    android开发中,经常会用到显示一个提醒信息,比如个人中心,有新信息,购买商品后,在购物车控件,显示购物数量等.我们可以用,2个控件来实现,或者用层叠图. 还有一种简单方便的办法,使用别人的开源代 ...

  8. [转] Java中ArrayList类的用法

    1.什么是ArrayList ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处: 动态的增加和减少元素 实现了ICollection和ILis ...

  9. NSPredicate查询日期的问题

    查询日期的时候日期可以根据参数传进去,但不能在字符串中传参后在作为查询条件 简单比较以下两段代码 NSDate* date1=[NSDate date]; NSDate* date2=date1; r ...

  10. Android(java)学习笔记244:多媒体之SurfaceView

    1. SurfaceView:     完成单位时间内界面的快速切换(游戏界面流畅感). 我们之前知道一般的View,只能在主线程里面显示,主线程中更新UI.但是SurfaceView可以在子线程中里 ...