-
Notifications
You must be signed in to change notification settings - Fork 1
/
VerbHelpers.h
184 lines (161 loc) · 5.56 KB
/
VerbHelpers.h
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
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#pragma once
// template class that encapsulates a local server class factory to be declared on the stack
// that will factory an already existing object provided to the constructor
// usually that object is the application or a sub object within the
// application.
//
// class __declspec(uuid("<object CLSID>")) CMyClass : public IUnknown
// {
// public:
// IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv);
//
// as follows
//
// CStaticClassFactory<CMyClass> classFactory(this);
// hr = classFactory.Register(CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE);
// if (SUCCEEDED(classFactory.Register(CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE)))
// {
// classFactory.MessageLoop()
// }
template <class TObjectToFactory> class CStaticClassFactory : public IClassFactory
{
public:
CStaticClassFactory(IUnknown *punkObject) : _dwRegisterClass(0), _punkObject(punkObject)
{
_punkObject->AddRef();
}
~CStaticClassFactory()
{
if (_dwRegisterClass)
{
CoRevokeClassObject(_dwRegisterClass);
}
_punkObject->Release();
}
HRESULT Register(CLSCTX classContent, REGCLS classUse)
{
return CoRegisterClassObject(__uuidof(TObjectToFactory), static_cast<IClassFactory *>(this),
classContent, classUse, &_dwRegisterClass);
}
// IUnknown
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = { QITABENT(CStaticClassFactory, IClassFactory), { 0 }, };
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return 2;
}
IFACEMETHODIMP_(ULONG) Release()
{
return 1;
}
// IClassFactory
IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
if (punkOuter)
{
*ppv = NULL;
return CLASS_E_NOAGGREGATION;
}
return _punkObject->QueryInterface(riid, ppv); // : TObjectToFactory::CreateInstance(riid, ppv);
}
IFACEMETHODIMP LockServer(BOOL /* fLock */)
{
return S_OK;
}
private:
DWORD _dwRegisterClass;
IUnknown *_punkObject;
};
// this class encapsulates the management of a message loop for an
// application. it supports queing a callback to the application via the message
// loop to enable the app to return from a call and continue processing that call
// later. this behavior is needed when implementing a shell verb as verbs
// must not block the caller. to use this class:
//
// 1) inherit from this class
//
// class CApplication : CAppMessageLoop
//
// 2) in the invoke function, for example IExecuteCommand::Execute() or IDropTarget::Drop()
// queue a callback by callong QueueAppCallback();
//
// IFACEMETHODIMP CExecuteCommandVerb::Execute()
// {
// QueueAppCallback();
// }
//
// 3) implement OnAppCallback, this is the code that will execute the queued callback
// void OnAppCallback()
// {
// // do the work here
// }
class CAppMessageLoop
{
protected:
// this timer is used to exit the message loop if the the application is
// activated but not invoked. this is needed if there is a failure when the
// verb is being invoked due to low resources or some other uncommon reason.
// without this the app would not exit in this case. this timer needs to be
// canceled once the app learns that it has should remain running.
static UINT const uTimeout = 30 * 1000; // 30 seconds
CAppMessageLoop() : _uTimeoutTimerId(SetTimer(NULL, 0, uTimeout, NULL)), _uPostTimerId(0)
{
}
void QueueAppCallback()
{
// queue a callback on OnAppCallback() by setting a timer of zero seconds
_uPostTimerId = SetTimer(NULL, 0, 0, NULL);
if (_uPostTimerId)
{
CancelTimeout();
}
}
// cancel the timeout timer, this should be called when the appliation
// knows that it wants to keep running, for example when it recieves the
// incomming call to invoke the verb, this is done implictly when the
// app queues a callback
void CancelTimeout()
{
if (_uTimeoutTimerId)
{
KillTimer(NULL, _uTimeoutTimerId);
_uTimeoutTimerId = 0;
}
}
void MessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (msg.message == WM_TIMER)
{
KillTimer(NULL, msg.wParam); // all are one shot timers
if (msg.wParam == _uTimeoutTimerId)
{
_uTimeoutTimerId = 0;
}
else if (msg.wParam == _uPostTimerId)
{
_uPostTimerId = 0;
OnAppCallback();
}
PostQuitMessage(0);
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void virtual OnAppCallback() = 0; // app must implement
private:
UINT_PTR _uTimeoutTimerId; // timer id used to exit the app if the app is not called back within a certain time
UINT_PTR _uPostTimerId; // timer id used to to queue a callback to the app
};