
改造MFC类
CCEFDemoDoc
不需改动CMFCCEFView
- 添加头文件
CClientHandler.h
和MFCCEFDoc.h
- 添加继承
public ClientHandler::Delegate
,并添加其虚函数
- 添加头文件
virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) override;
// Called when the browser is closing.
virtual void OnBrowserClosing(CefRefPtr<CefBrowser> browser) override;
// Called when the browser has been closed.
virtual void OnBrowserClosed(CefRefPtr<CefBrowser> browser) override;
// Set the window URL address.
virtual void OnSetAddress(std::string const & url) override;
// Set the window title.
virtual void OnSetTitle(std::string const & title) override;
// Set fullscreen mode.
virtual void OnSetFullscreen(bool const fullscreen) override;
// Set the loading state.
virtual void OnSetLoadingState(bool const isLoading,
bool const canGoBack,
bool const canGoForward) override;
void CCEFDemoView::OnBrowserCreated(CefRefPtr<CefBrowser> browser)
{
m_browser = browser;
}
void CCEFDemoView::OnBrowserClosing(CefRefPtr<CefBrowser> browser)
{
}
void CCEFDemoView::OnBrowserClosed(CefRefPtr<CefBrowser> browser)
{
if (m_browser != nullptr &&
m_browser->GetIdentifier() == browser->GetIdentifier())
{
m_browser = nullptr;
m_clientHandler->DetachDelegate();
}
}
void CCEFDemoView::OnSetAddress(std::string const & url)
{
auto main = static_cast<CMainFrame*>(m_wndMain);
if (main != nullptr)
{
auto newurl = CString{ url.c_str() };
if (newurl.Find(m_startUrl) >= 0)
newurl = "";
//main->SetUrl(newurl);
}
}
void CCEFDemoView::OnSetTitle(std::string const & title)
{
::SetWindowText(m_hWnd, CefString(title).ToWString().c_str());
}
void CCEFDemoView::OnSetFullscreen(bool const fullscreen)
{
}
void CCEFDemoView::OnSetLoadingState(bool const isLoading,
bool const canGoBack,
bool const canGoForward)
{
}
void CCEFDemoView::OnInitialUpdate()
{
CView::OnInitialUpdate();
InitStartUrl();
auto rect = RECT{ 0 };
GetClientRect(&rect);
CefWindowInfo info;
info.SetAsChild(GetSafeHwnd(), rect);
CefBrowserSettings browserSettings;
browserSettings.web_security = STATE_DISABLED;
m_clientHandler = new ClientHandler(this);
m_clientHandler->CreateBrowser(info, browserSettings, CefString(m_startUrl));
}
void CCEFDemoView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if (m_clientHandler != nullptr)
{
if (m_browser != nullptr)
{
auto hwnd = m_browser->GetHost()->GetWindowHandle();
auto rect = RECT{ 0 };
GetClientRect(&rect);
::SetWindowPos(hwnd, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
}
}
void CCEFDemoView::InitStartUrl()
{
TCHAR path_buffer[_MAX_PATH] = { 0 };
TCHAR drive[_MAX_DRIVE] = { 0 };
TCHAR dir[_MAX_DIR] = { 0 };
TCHAR fname[_MAX_FNAME] = { 0 };
TCHAR ext[_MAX_EXT] = { 0 };
::GetModuleFileName(NULL, path_buffer, sizeof(path_buffer));
auto err = _tsplitpath_s(path_buffer, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
if (err != 0) {}
auto s = CString{ dir };
s += _T("html");
err = _tmakepath_s(path_buffer, _MAX_PATH, drive, (LPCTSTR)s, _T("index"), _T("html"));
if (err != 0) {}
m_startUrl = CString{ path_buffer };
m_startUrl.Replace(_T('\'), _T('/'));
m_startUrl = CString{ _T("file:///") } +m_startUrl;
}
- 重写CView的
OnActivateView
和PreTranslateMessage
函数
void CCEFDemoView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
m_wndMain = AfxGetMainWnd();
return CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}
//截获键盘F5消息,刷新页面
BOOL CCEFDemoView::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
if (pMsg->wParam == VK_F5)
{
m_browser->Reload();
}
}
return CView::PreTranslateMessage(pMsg);
}
- 添加
OnInitialUpdate
和OnSize
函数 - 添加私有函数
InitStartUrl
和如下变量
CefRefPtr<ClientHandler> m_clientHandler;
CefRefPtr<CefBrowser> m_browser;
CWnd* m_wndMain = nullptr;
CString m_startUrl;
- 在
CCEFDemoView.cpp
中添加消息映射ON_WM_SIZE()
- 析构函数如下:
if (m_clientHandler != nullptr)
m_clientHandler->DetachDelegate();
背景
Visual Studio C++ 创建新项目蹦出来如下选项:
Win32控制台应用程序,Win32项目,MFC有什么区别?
main.cpp
vs2017新建项目没有mfc,已经使用visualstudio安装程序勾选“MFC和ATL支持”并安装了,已有的MFC项目双击也能打开且正常显示,但是点击菜单新建—项目时却没有MFC,百度知道上没有解答.这里有没有能回答呢?
新建一个MFC SDI项目
设置为MFC标准,在静态库中使用MFC。
新建MFC项目
正文:
-
Win32控制台,没有界面,命令行执行生成的文件则直接在后台运行,运行效果如下:
生成的“.exe”文件相当于Linux系统下用gcc编译出来一个“.out”文件,直接运行操作即可,只有命令符,没有界面。
-
Win32项目
这个就有界面了,但是界面里面的控件,基本上要自己去实现。 -
MFC项目(Microsoft Foundation Class Library,微软基础类库)
这个也有界面,可是界面里面的控件就不需要自己实现了,常用的控件都已经在MFC库内写好,直接拖动即可。
一、通过MFC ODBC进行操作
一、CEF简介
Chromium Embedded
Framework
(CEF)
是一个开源项目,允许在第三方应用中集成谷歌浏览器。原生支持C和C++,通过非官方扩展支持其他语言,如C#,
Java, Delphi 或
Python。本文较详细的记录了将CEF集成到MFC单文档程序的过程。
使用环境为:windows10 64位系统,visual studio 2017,按照32位编译。
参考链接
参考于知乎问答“对于一个新手来说,在使用Visual
Studio新建项目时,win32控制台应用程序和win32项目、空项目、MFC应用程序有哪些区别?”
“杜小豆”答主的回答,感谢。
至此,记录完毕。
记录时间:2017-1-19
记录地点:深圳WZ
至此,数据源添加完毕
二、准备工作
下载CEF,最新版本在http://cefbuilds.com/下载,但好像被墙了,我一直没法登陆,后来在下载链接下载到cef_binary_3.2171.1901_windows32。
Paste_Image.png
解压到任意目录,用VS2017打开cefclient2010.sln
,升级到VS2017格式。编译其中的libcef_dll_wrapper
项目,会出现以下错误。
Paste_Image.png
产生原因为:SDK中的solution文件是用VS2010产生的,我的VS版本为VS2017,转换后,有些Project编译选项中,Treat
Warnings As
Errors(把警告看作错误来处理)选项开启了。按照下图,把此选项关闭,就可以正常编译了。
Paste_Image.png
编译成功后,在outDebuglib
这个目录里会生成一个文件:libcef_dll_wrapper.lib
,这个文件在开发我们项目的时候会使用。
复制资源文件
将cef_binary_3.2171.1901_windows32Resources
目录下的文件,拷贝到程序的Debug目录
Paste_Image.png
运行生成的程序,可得如下界面,说明成功。
Paste_Image.png
2、如果使用的mysql是64位的,需要将项目的解决方案平台由win32改成x64
准备CEF文件
- 将
cef_binary_3.2171.1901_windows32
中的include
目录拷贝到CEFDemo文件夹中。 - 将
cef_binary_3.2171.1901_windows32
目录下Debug
中的dll文件拷贝到CEFDemo的Debug
目录中。 - 将
libcef.lib
和上面生成的libcef_dll_wrapper.lib
拷贝到项目文件夹下的lib
目录(新建)中。 - 配置项目属性
C/C++ ->常规
附加包含目录$(ProjectDir)lib;$(ProjectDir)include;
链接器->输入
添加附加依赖项$(ProjectDir)liblibcef.lib;$(ProjectDir)liblibcef_dll_wrapper.lib;
- 在
Debug
目录下新建一个html
文件夹,并添加一个index.html
用来在程序界面中呈现。
填写相关的数据库连接配置和使用的数据库,可点击Test进行测试,查看是否成功连接,设置完成后点击OK
三、新建MFC项目
#include <Windows.h>
#include <iostream>
#include <iomanip>
#include <sql.h>
#include <odbcss.h>
#include <sqlext.h>
#define MAXBUFLEN 255
#define MaxNameLen 20
#import "c:program filescommon filessystemadomsado15.dll" no_namespace rename("EOF", "adoEOF")
SQLHENV henv = SQL_NULL_HENV;//定义环境句柄
SQLHDBC hdbc1 = SQL_NULL_HDBC;//定义数据库连接句柄
SQLHSTMT hstmt1 = SQL_NULL_HSTMT;//定义语句句柄
using namespace std;
int main()
{
RETCODE retcode;//错误返回码
retcode = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv);
if (retcode < 0)//错误处理
{
cout << "allocate ODBC Environment handle errors." << endl;
return -1;
}
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, SQL_IS_INTEGER);
if (retcode < 0) //错误处理
{
cout << "the ODBC is not version3.0 " << endl;
return -1;
}
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc1);
if (retcode < 0) //错误处理
{
cout << "allocate ODBC connection handle errors." << endl;
return -1;
}
char* szDSN = "MySqlODBC";//添加数据源时,为其起的名字
char* szUID = "root";
char* szAuthStr = "123456";
retcode = SQLConnect(hdbc1,
(SQLCHAR*)szDSN,
(SWORD)strlen(szDSN),
(SQLCHAR*)szUID,
(SWORD)strlen(szUID),
(SQLCHAR*)
szAuthStr,
(SWORD)strlen(szAuthStr));
if (retcode < 0) //错误处理
{
cout << "connect to ODBC datasource errors." << endl;
return -1;
}
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc1, &hstmt1);
if (retcode < 0)
{
cout << "allocate ODBC statement handle errors." << endl;
return -1;
}
retcode = SQLExecDirect(hstmt1, (SQLCHAR*)"SElECT * FROM book", SQL_NTS);
if (retcode < 0)
{
cout << "Executing statement throught ODBC errors." << endl;
return -1;
}
SQLCHAR bookname[MaxNameLen + 1];
SQLCHAR size[MaxNameLen + 1];
SQLLEN columnLen = 0;
retcode = SQLBindCol(hstmt1, 2, SQL_C_CHAR, bookname, MaxNameLen, &columnLen);
retcode = SQLBindCol(hstmt1, 3, SQL_C_CHAR, size, MaxNameLen, &columnLen);
while ((retcode = SQLFetch(hstmt1)) != SQL_NO_DATA)
{
cout <<setw(9) <<"BookName:" << bookname << endl;
cout << setw(9) << "Size:" << size << endl;
}
system("pause");
}
添加必要的类
添加ClientHandler
类,具体如下:
#pragma once
#include "include/base/cef_lock.h"
#include "include/cef_client.h"
class ClientHandler : public CefClient,
public CefDisplayHandler,
public CefLifeSpanHandler,
public CefLoadHandler
{
public:
// Implement this interface to receive notification of ClientHandler
// events. The methods of this class will be called on the main thread.
class Delegate
{
public:
// Called when the browser is created.
virtual void OnBrowserCreated(CefRefPtr<CefBrowser> browser) = 0;
// Called when the browser is closing.
virtual void OnBrowserClosing(CefRefPtr<CefBrowser> browser) = 0;
// Called when the browser has been closed.
virtual void OnBrowserClosed(CefRefPtr<CefBrowser> browser) = 0;
// Set the window URL address.
virtual void OnSetAddress(std::string const & url) = 0;
// Set the window title.
virtual void OnSetTitle(std::string const & title) = 0;
// Set fullscreen mode.
virtual void OnSetFullscreen(bool const fullscreen) = 0;
// Set the loading state.
virtual void OnSetLoadingState(bool const isLoading,
bool const canGoBack,
bool const canGoForward) = 0;
protected:
virtual ~Delegate() {}
};
public:
ClientHandler(Delegate* delegate);
~ClientHandler();
void CreateBrowser(CefWindowInfo const & info, CefBrowserSettings const & settings, CefString const & url);
// CefClient methods:
virtual CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
virtual CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
virtual CefRefPtr<CefLoadHandler> GetLoadHandler() override { return this; }
// CefDisplayHandler methods:
virtual void OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url) override;
virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) override;
virtual void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen);
// CefLifeSpanHandler methods:
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
virtual bool DoClose(CefRefPtr<CefBrowser> browser) override;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
// CefLoadHandler methods:
virtual void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
bool isLoading,
bool canGoBack,
bool canGoForward) override;
virtual void OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl) override;
// This object may outlive the Delegate object so it's necessary for the
// Delegate to detach itself before destruction.
void DetachDelegate();
private:
// Include the default reference counting implementation.
IMPLEMENT_REFCOUNTING(ClientHandler);
// Include the default locking implementation.
IMPLEMENT_LOCKING(ClientHandler);
private:
Delegate* m_delegate;
};
#include "stdafx.h"
#include "ClientHandler.h"
#include <sstream>
#include <string>
#include "include/base/cef_bind.h"
#include "include/cef_app.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_helpers.h"
ClientHandler::ClientHandler(Delegate* delegate)
: m_delegate(delegate)
{
}
ClientHandler::~ClientHandler()
{
}
void ClientHandler::CreateBrowser(CefWindowInfo const & info, CefBrowserSettings const & settings, CefString const & url)
{
CefBrowserHost::CreateBrowser(info, this, url, settings, nullptr);
}
void ClientHandler::OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString& url)
{
CEF_REQUIRE_UI_THREAD();
// Only update the address for the main (top-level) frame.
if (frame->IsMain())
{
if (m_delegate != nullptr)
m_delegate->OnSetAddress(url);
}
}
void ClientHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title)
{
CEF_REQUIRE_UI_THREAD();
if (m_delegate != nullptr)
m_delegate->OnSetTitle(title);
}
void ClientHandler::OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen)
{
CEF_REQUIRE_UI_THREAD();
if (m_delegate != nullptr)
m_delegate->OnSetFullscreen(fullscreen);
}
void ClientHandler::OnAfterCreated(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
if (m_delegate != nullptr)
m_delegate->OnBrowserCreated(browser);
}
bool ClientHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
if (m_delegate != nullptr)
m_delegate->OnBrowserClosing(browser);
return false;
}
void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
CEF_REQUIRE_UI_THREAD();
if (m_delegate != nullptr)
m_delegate->OnBrowserClosed(browser);
}
void ClientHandler::OnLoadError(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
ErrorCode errorCode,
const CefString& errorText,
const CefString& failedUrl)
{
CEF_REQUIRE_UI_THREAD();
// Don't display an error for downloaded files.
if (errorCode == ERR_ABORTED)
return;
// Display a load error message.
std::stringstream ss;
ss << "<html><body bgcolor="white">"
"<h2>Failed to load URL " << std::string(failedUrl) <<
" with error " << std::string(errorText) << " (" << errorCode <<
").</h2></body></html>";
frame->LoadString(ss.str(), failedUrl);
}
void ClientHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> browser, bool isLoading, bool canGoBack, bool canGoForward)
{
CEF_REQUIRE_UI_THREAD();
if (m_delegate != nullptr)
m_delegate->OnSetLoadingState(isLoading, canGoBack, canGoForward);
}
void ClientHandler::DetachDelegate()
{
m_delegate = nullptr;
}
修改CCEFDemoApp
类
- 修改头文件
- 添加引用
include/cef_base.h
和include/cef_app.h
- 重写函数
ExitInstance()
和PumpMessage()
- 添加引用
int CCEFDemoApp::ExitInstance()
{
AfxOleTerm(FALSE);
UninitializeCef();
return CWinApp::ExitInstance();
}
BOOL CCEFDemoApp::PumpMessage()
{
auto result = CWinApp::PumpMessage();
CefDoMessageLoopWork();
return result;
}
- 添加私有函数和变量:
InitializeCef()
、UninitializeCef()
,和CefRefPtr<CefApp> m_app
void CCEFDemoApp::InitializeCef()
{
CefMainArgs mainargs(m_hInstance);
CefSettings settings;
settings.multi_threaded_message_loop = false;
CefInitialize(mainargs, settings, m_app, nullptr);
}
void CCEFDemoApp::UninitializeCef()
{
CefShutdown();
}
- 修改源文件
- 添加头文件引用
CEFDemoView.h
- 在
InitInstance
的CWinApp::InitInstance();
之前添加InitializeCef();
初始化CEF - 添加公有函数
void SetUrl(CString const & url)
- 添加消息映射
afx_msg void OnClose()
- 添加私有成员
CCEFDemoView* GetView()
- 添加头文件引用
既然使用到了ODBC那么就要需要添加数据源了
项目中使用的数据库名和表数据请参考C++操作mysql方法总结(1)中的介绍