forked from encoderlee/javascript_call_cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJsCallCppDlg.cpp
More file actions
236 lines (199 loc) · 7.21 KB
/
Copy pathJsCallCppDlg.cpp
File metadata and controls
236 lines (199 loc) · 7.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// JsCallCppDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "JsCallCpp.h"
#include "JsCallCppDlg.h"
#include "afxdialogex.h"
#include <MsHTML.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CJsCallCppDlg 对话框
CJsCallCppDlg::CJsCallCppDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CJsCallCppDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CJsCallCppDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EXPLORER1, m_webbrowser);
}
BEGIN_MESSAGE_MAP(CJsCallCppDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()
// CJsCallCppDlg 消息处理程序
BOOL CJsCallCppDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
//加载资源文件中的HTML,IDR_HTML1就是HTML文件在资源文件中的ID
wchar_t self_path[MAX_PATH] = { 0 };
GetModuleFileName(NULL, self_path, MAX_PATH);
CString res_url;
res_url.Format(L"res://%s/%d", self_path, IDR_HTML1);
m_webbrowser.Navigate(res_url, NULL, NULL, NULL, NULL);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。
void CJsCallCppDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CJsCallCppDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
//我自己给我的两个函数拟定的数字ID,这个ID可以取0-16384之间的任意数
enum
{
FUNCTION_ShowMessageBox = 1,
FUNCTION_GetProcessID = 2,
};
//不用实现,直接返回E_NOTIMPL
HRESULT STDMETHODCALLTYPE CJsCallCppDlg::GetTypeInfoCount(UINT *pctinfo)
{
return E_NOTIMPL;
}
//不用实现,直接返回E_NOTIMPL
HRESULT STDMETHODCALLTYPE CJsCallCppDlg::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
return E_NOTIMPL;
}
//JavaScript调用这个对象的方法时,会把方法名,放到rgszNames中,我们需要给这个方法名拟定一个唯一的数字ID,用rgDispId传回给它
//同理JavaScript存取这个对象的属性时,会把属性名放到rgszNames中,我们需要给这个属性名拟定一个唯一的数字ID,用rgDispId传回给它
//紧接着JavaScript会调用Invoke,并把这个ID作为参数传递进来
HRESULT STDMETHODCALLTYPE CJsCallCppDlg::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
//rgszNames是个字符串数组,cNames指明这个数组中有几个字符串,如果不是1个字符串,忽略它
if (cNames != 1)
return E_NOTIMPL;
//如果字符串是ShowMessageBox,说明JavaScript在调用我这个对象的ShowMessageBox方法,我就把我拟定的ID通过rgDispId告诉它
if (wcscmp(rgszNames[0], L"ShowMessageBox") == 0)
{
*rgDispId = FUNCTION_ShowMessageBox;
return S_OK;
}
//同理,如果字符串是GetProcessID,说明JavaScript在调用我这个对象的GetProcessID方法
else if (wcscmp(rgszNames[0], L"GetProcessID") == 0)
{
*rgDispId = FUNCTION_GetProcessID;
return S_OK;
}
else
return E_NOTIMPL;
}
//JavaScript通过GetIDsOfNames拿到我的对象的方法的ID后,会调用Invoke,dispIdMember就是刚才我告诉它的我自己拟定的ID
//wFlags指明JavaScript对我的对象干了什么事情!
//如果是DISPATCH_METHOD,说明JavaScript在调用这个对象的方法,比如cpp_object.ShowMessageBox();
//如果是DISPATCH_PROPERTYGET,说明JavaScript在获取这个对象的属性,比如var n = cpp_object.num;
//如果是DISPATCH_PROPERTYPUT,说明JavaScript在修改这个对象的属性,比如cpp_object.num = 10;
//如果是DISPATCH_PROPERTYPUTREF,说明JavaScript在通过引用修改这个对象,具体我也不懂
//示例代码并没有涉及到wFlags和对象属性的使用,需要的请自行研究,用法是一样的
//pDispParams就是JavaScript调用我的对象的方法是传递进来的参数,里面有一个数组保存着所有参数
//pDispParams->cArgs就是数组中有多少个参数
//pDispParams->rgvarg就是保存着参数的数组,请使用[]下标来访问,每个参数都是VARIANT类型,可以保存各种类型的值
//具体是什么类型用VARIANT::vt来判断,不多解释了,VARIANT这东西大家都懂
//pVarResult就是我们给JavaScript的返回值
//其它不用管
HRESULT STDMETHODCALLTYPE CJsCallCppDlg::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
//通过ID我就知道JavaScript想调用哪个方法
if (dispIdMember == FUNCTION_ShowMessageBox)
{
//检查是否只有一个参数
if (pDispParams->cArgs != 1)
return E_NOTIMPL;
//检查这个参数是否是字符串类型
if (pDispParams->rgvarg[0].vt != VT_BSTR)
return E_NOTIMPL;
//放心调用
ShowMessageBox(pDispParams->rgvarg[0].bstrVal);
return S_OK;
}
else if (dispIdMember == FUNCTION_GetProcessID)
{
DWORD id = GetProcessID();
*pVarResult = CComVariant(id);
return S_OK;
}
else
return E_NOTIMPL;
}
//JavaScript拿到我们传递给它的指针后,由于它不清楚我们的对象是什么东西,会调用QueryInterface来询问我们“你是什么鬼东西?”
//它会通过riid来问我们是什么东西,只有它问到我们是不是IID_IDispatch或我们是不是IID_IUnknown时,我们才能肯定的回答它S_OK
//因为我们的对象继承于IDispatch,而IDispatch又继承于IUnknown,我们只实现了这两个接口,所以只能这样来回答它的询问
HRESULT STDMETHODCALLTYPE CJsCallCppDlg::QueryInterface(REFIID riid, void **ppvObject)
{
if (riid == IID_IDispatch || riid == IID_IUnknown)
{
//对的,我是一个IDispatch,把我自己(this)交给你
*ppvObject = static_cast<IDispatch*>(this);
return S_OK;
}
else
return E_NOINTERFACE;
}
//我们知道COM对象使用引用计数来管理对象生命周期,我们的CJsCallCppDlg对象的生命周期就是整个程序的声明周期
//我的这个对象不需要你JavaScript来管,我自己会管,所以我不用实现AddRef()和Release(),这里乱写一些。
//你要return 1;return 2;return 3;return 4;return 5;都可以
ULONG STDMETHODCALLTYPE CJsCallCppDlg::AddRef()
{
return 1;
}
//同上,不多说了
//题外话:当然如果你要new出一个c++对象来并扔给JavaScript来管,你就需要实现AddRef()和Release(),在引用计数归零时delete this;
ULONG STDMETHODCALLTYPE CJsCallCppDlg::Release()
{
return 1;
}
DWORD CJsCallCppDlg::GetProcessID()
{
return GetCurrentProcessId();
}
void CJsCallCppDlg::ShowMessageBox(const wchar_t *msg)
{
MessageBox(msg, L"这是来自javascript的消息");
}
BEGIN_EVENTSINK_MAP(CJsCallCppDlg, CDialogEx)
ON_EVENT(CJsCallCppDlg, IDC_EXPLORER1, 259, CJsCallCppDlg::DocumentCompleteExplorer1, VTS_DISPATCH VTS_PVARIANT)
END_EVENTSINK_MAP()
//该事件响应函数会在WebBrowser加载HTML文档完毕后被调用
//此时调用JavaScript的SaveCppObject函数,把我自己(this)交给它,SaveCppObject会把我这个对象保存到全局变量var cpp_object;中
//以后JavaScript就可以通过cpp_object来调用我这个C++对象的方法了
//注意,因为我的程序只用m_webbrowser.Navigate加载了一个HTML,所以这里我没有检查VARIANT* URL
//如果你的程序中有多次m_webbrowser.Navigate,需要通过VARIANT* URL判断这次事件响应是对应着哪次Navigate
void CJsCallCppDlg::DocumentCompleteExplorer1(LPDISPATCH pDisp, VARIANT* URL)
{
CComQIPtr<IHTMLDocument2> document = m_webbrowser.get_Document();
CComDispatchDriver script;
document->get_Script(&script);
CComVariant var(static_cast<IDispatch*>(this));
script.Invoke1(L"SaveCppObject", &var);
}