一、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);
     ;
 }

 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));
          == 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();

          != 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 = ];
     // Extract arguments...
     ; 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, x);
         )
         {
             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, 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, x);
         )
         {
             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, 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, x);
         VariantClear(&x);
     }

     // pMail->cc
     {
         VARIANT x;
         x.vt = VT_BSTR;
         x.bstrVal = SysAllocString(cc.c_str());
         AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L, x);
         VariantClear(&x);
     }

     // pMail->bcc
     {
         VARIANT x;
         x.vt = VT_BSTR;
         x.bstrVal = SysAllocString(bcc.c_str());
         AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L, x);
         VariantClear(&x);
     }

     // pMail->body
     {
         VARIANT x;
         x.vt = VT_BSTR;
         x.bstrVal = SysAllocString(body.c_str());
         AutoWrap(DISPATCH_PROPERTYPUT, NULL, pMail, L, 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);
         )
         {
             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, path, x, x, path);
         )
         {
             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, vtModal);
         )
         {
             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);
     )
     {
         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);
     ;
 }

  这里人工地把调用相关的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());

          != to.size())
             olMail.put_To(to.c_str());
         else
         {
             olNs.Logoff();
             return;
         }

          != subject.size())olMail.put_Subject(subject.c_str());
          != body.size())olMail.put_Body(body.c_str());
          != cc.size())olMail.put_CC(cc.c_str());
          != bcc.size())olMail.put_BCC(bcc.c_str());

         olMail.Save();

          != 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. Selenium的PO模式(Page Object Model)[python版]

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

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

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

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

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

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

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

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

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

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

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

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

    本文内容     1.概述     2.windows与document     3.对话框     4.定时调用     5.URL解析与访问历史     6.浏览器和屏幕信息 ★概述     &q ...

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

    1.结构 BOM是Browser Object Model的缩写,简称浏览器对象模型 BOM提供了独立于内容而与浏览器窗口进行交互的对象 由于BOM主要用于管理窗口与窗口之间的通讯,因此其核心对象是w ...

随机推荐

  1. j2ee项目服务器怎样部署?

    1.右击项目  >>  点击如图1中❶  >>  Project(选择项目)  >>  Add  如图1: 图1 2.点击Add  >>  Server ...

  2. 【XLL 框架库函数】 TempActiveRow/TempActiveRow12

    创建一个包含所有激活工作表行的 XLOPER/XLOPER12 LPXLOPER TempActiveRow(WORD row); LPXLOPER12 TempActiveRow12(ROW row ...

  3. Spring框架学习路线和概述

    Spring的Ioc Spring的AOP , AspectJ Spring的事务管理 , 三大框架的整合. Spring框架的概述: 1. 什么是Spring Spring是分层的JavaSE/EE ...

  4. 通过工厂方式配置bean

    src\dayday\CarFactoryBean.java package dayday;import org.springframework.beans.factory.FactoryBean;/ ...

  5. WebForm aspx页面传值---7种方式

    1.get方式 发送页 <form id="form1" runat="server">    <div>        <a h ...

  6. 巧用freemarker

    使用Freemarker宏进行可扩展式模块化编程 该文是转载而来,并非我本人所写,但是觉得真心不错,所以收藏一下 一.前言 今天的文章聊一下freemarker的一些特性:宏,我们将使用它写出一些模块 ...

  7. plsql很好用的自定义设置【转载】

    本文是转载的,目的是方便自已随时可以查看.转载地址:http://blog.itpub.net/24496241/viewspace-740917/ 目的:方便自已随时可以查看 1.格式化SQL语句在 ...

  8. Manacher&#39;s algorithm: 最长回文子串算法

    Manacher 算法是时间.空间复杂度都为 O(n) 的解决 Longest palindromic substring(最长回文子串)的算法.回文串是中心对称的串,比如 'abcba'.'abcc ...

  9. 将 JAR 转为 EXE – EXE4J 的使用教程(第一期)(转载)

    http://www.iteknical.com/convert-jar-to-exe-phase-i-exe4j-tutorial/

  10. 开放平台鉴权以及OAuth2.0介绍

    OAuth 2.0 协议 OAuth是一个开发标准,允许用户授权第三方网站或应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的内容. OAuth 2.0 ...