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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
// Queue.cpp : Implementation of CQueue
#include "stdafx.h"
#include "Queue.h"
#include <devioctl.h>
#include <initguid.h>
#include "Queue.tmh"
// Add table used to lookup the Access required for Wpd Commands
BEGIN_WPD_COMMAND_ACCESS_MAP(g_WpdCommandAccessMap)
DECLARE_WPD_STANDARD_COMMAND_ACCESS_ENTRIES
// Add any custom commands here e.g.
// WPD_COMMAND_ACCESS_ENTRY(MyCustomCommand, WPD_COMMAND_ACCESS_READWRITE)
END_WPD_COMMAND_ACCESS_MAP
// This enables use to use VERIFY_WPD_COMMAND_ACCESS to check command access function for us.
DECLARE_VERIFY_WPD_COMMAND_ACCESS;
/******************************************************************************
* This function calls the WpdBaseDriver to handle the WPD message. In order
* to do this it does the following:
*
* - Deserializes pBuffer into an IPortableDeviceValues which holds the command
* input parameters from the WPD application.
* - Creates an IPortableDeviceValues for the results.
* - Calls the WpdBaseDriver to handle the message. (The results of this
* operation are put into the previously created results IPortableDeviceValues.)
* - The results IPortableDeviceValues is then serialized back into pBuffer, making
* sure that it does not overrun ulOutputBufferLength.
*
*****************************************************************************/
HRESULT CQueue::ProcessWpdMessage(
ULONG ControlCode,
_In_ ContextMap* pClientContextMap,
_In_ IWDFDevice* pDevice,
_In_reads_bytes_(ulInputBufferLength) PVOID pInBuffer,
ULONG ulInputBufferLength,
_Out_writes_bytes_to_(ulOutputBufferLength, *pdwBytesWritten) PVOID pOutBuffer,
ULONG ulOutputBufferLength,
_Out_ DWORD* pdwBytesWritten)
{
HRESULT hr = S_OK;
CComPtr<IPortableDeviceValues> pParams;
CComPtr<IPortableDeviceValues> pResults;
CComPtr<WpdBaseDriver> pWpdBaseDriver;
*pdwBytesWritten = 0;
if (hr == S_OK)
{
hr = m_pWpdSerializer->GetIPortableDeviceValuesFromBuffer((BYTE*)pInBuffer,
ulInputBufferLength,
&pParams);
CHECK_HR(hr, "Failed to deserialize command parameters from input buffer");
}
// Verify that that command was sent with the appropriate access
if (hr == S_OK)
{
hr = VERIFY_WPD_COMMAND_ACCESS(ControlCode, pParams, g_WpdCommandAccessMap);
CHECK_HR(hr, "Wpd Command was sent with incorrect access flags");
}
// Create the WPD results collection
if (hr == S_OK)
{
hr = CoCreateInstance(CLSID_PortableDeviceValues,
NULL,
CLSCTX_INPROC_SERVER,
IID_IPortableDeviceValues,
(VOID**)&pResults);
CHECK_HR(hr, "Failed to CoCreate CLSID_PortableDeviceValues");
}
// Insert the client context map as one of this driver's private properties. This is
// just a convenient place holder which allows other methods down the chain to
// access the context map.
if (hr == S_OK)
{
hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP, pClientContextMap);
CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_CLIENT_CONTEXT_MAP");
}
// Insert the IWDFDevice interface as one of this driver's private properties. This is
// just a convenient place holder which allows other methods down the chain to
// access the WUDF Device object.
if (hr == S_OK)
{
hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT, pDevice);
CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_WUDF_DEVICE_OBJECT");
}
// Insert the IWpdSerializer interface as one of this driver's private properties. This is
// just a convenient place holder which allows other methods down the chain to
// access the WPD Serializer object.
if (hr == S_OK)
{
hr = pParams->SetIUnknownValue(PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT, m_pWpdSerializer);
CHECK_HR(hr, "Failed to set PRIVATE_SAMPLE_DRIVER_WPD_SERIALIZER_OBJECT");
}
// Get the WpdBaseDriver so we can dispatch the message
if (hr == S_OK)
{
hr = GetWpdBaseDriver(pDevice, &pWpdBaseDriver);
CHECK_HR(hr, "Failed to get WpdBaseDriver");
}
if (hr == S_OK)
{
hr = pWpdBaseDriver->DispatchWpdMessage(pParams, pResults);
CHECK_HR(hr, "Failed to handle WPD command");
}
if (hr == S_OK)
{
hr = m_pWpdSerializer->WriteIPortableDeviceValuesToBuffer(ulOutputBufferLength,
pResults,
(BYTE*)pOutBuffer,
pdwBytesWritten);
CHECK_HR(hr, "Failed to serialize results to output buffer");
}
return hr;
}
/******************************************************************************
* This method gets the WpdBaseDriver associated with the UMDF device object.
* The caller should Release *ppWpdBaseDriver when it is done.
*
* When this device was created, we assigned the WpdBaseDriver as the context.
* So, in order to retrieve the correct WpdBaseDriver for this device, we simply
* get the device context.
*****************************************************************************/
HRESULT CQueue::GetWpdBaseDriver(
_In_ IWDFDevice* pDevice,
_Outptr_result_nullonfailure_ WpdBaseDriver** ppWpdBaseDriver)
{
HRESULT hr = S_OK;
WpdBaseDriver* pContext = NULL;
if((pDevice == NULL) || (ppWpdBaseDriver == NULL))
{
hr = E_POINTER;
CHECK_HR(hr, "Cannot have NULL parameter for pDevice or ppWpdBaseDriver");
}
*ppWpdBaseDriver = NULL;
if(SUCCEEDED(hr))
{
hr = pDevice->RetrieveContext((void**)&pContext);
if(SUCCEEDED(hr))
{
if(pContext != NULL)
{
pContext->AddRef();
*ppWpdBaseDriver = pContext;
}
else
{
hr = E_UNEXPECTED;
CHECK_HR(hr, "Device context is NULL");
}
}
}
return hr;
}
// CQueue
STDMETHODIMP_ (void)
CQueue::OnCreateFile(
_In_ IWDFIoQueue* pQueue,
_In_ IWDFIoRequest* pRequest,
_In_ IWDFFile* pFileObject
)
{
UNREFERENCED_PARAMETER(pQueue);
// This critical section protects the section of code where we
// Create the serializer and results interfaces used in handling I/O messages.
// We only need to create them once, then we hang on to them for the lifetime of this
// queue object.
CComCritSecLock<CComAutoCriticalSection> Lock(m_CriticalSection);
HRESULT hr = S_OK;
// Create the WPD serializer
if ((hr == S_OK) &&
(m_pWpdSerializer == NULL))
{
hr = CoCreateInstance(CLSID_WpdSerializer,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWpdSerializer,
(VOID**)&m_pWpdSerializer);
CHECK_HR(hr, "Failed to CoCreate CLSID_WpdSerializer");
}
// Create the client context map and associate it with the File Object
// so we can obtain it on a per-client basis.
if (hr == S_OK)
{
ContextMap* pClientContextMap = new ContextMap();
if(pClientContextMap != NULL)
{
hr = pFileObject->AssignContext(this, (void*)pClientContextMap);
CHECK_HR(hr, "Failed to set client context map");
// Release the client context map if we cannot set it
// properly
if(FAILED(hr))
{
pClientContextMap->Release();
pClientContextMap = NULL;
}
}
else
{
hr = E_OUTOFMEMORY;
CHECK_HR(hr, "Failed to create client context map");
}
}
pRequest->Complete(hr);
return;
}
STDMETHODIMP_ (void)
CQueue::OnDeviceIoControl(
_In_ IWDFIoQueue* pQueue,
_In_ IWDFIoRequest* pRequest,
ULONG ControlCode,
SIZE_T InputBufferSizeInBytes,
SIZE_T OutputBufferSizeInBytes
)
{
UNREFERENCED_PARAMETER(InputBufferSizeInBytes);
UNREFERENCED_PARAMETER(OutputBufferSizeInBytes);
HRESULT hr = S_OK;
DWORD dwBytesWritten = 0;
if(IS_WPD_IOCTL(ControlCode))
{
BYTE* pInputBuffer = NULL;
SIZE_T cbInputBuffer = 0;
BYTE* pOutputBuffer = NULL;
SIZE_T cbOutputBuffer = 0;
ContextMap* pClientContextMap = NULL;
CComPtr<IWDFMemory> pMemoryIn;
CComPtr<IWDFMemory> pMemoryOut;
CComPtr<IWDFDevice> pDevice;
CComPtr<IWDFFile> pFileObject;
//
// Get input memory buffer, the memory object is always returned even if the
// underlying buffer is NULL
//
pRequest->GetInputMemory(&pMemoryIn);
pInputBuffer = (BYTE*) pMemoryIn->GetDataBuffer(&cbInputBuffer);
//
// Get output memory buffer, the memory object is always returned even if the
// underlying buffer is NULL
//
pRequest->GetOutputMemory(&pMemoryOut);
pOutputBuffer = (BYTE*) pMemoryOut->GetDataBuffer(&cbOutputBuffer);
// Get the Context map for this client
pRequest->GetFileObject(&pFileObject);
if (pFileObject != NULL)
{
hr = pFileObject->RetrieveContext((void**)&pClientContextMap);
CHECK_HR(hr, "Failed to get Contextmap from WDF File Object");
if (hr == S_OK)
{
// Get the device object
pQueue->GetDevice(&pDevice );
hr = ProcessWpdMessage(ControlCode,
pClientContextMap,
pDevice,
pInputBuffer,
(DWORD)cbInputBuffer,
pOutputBuffer,
(DWORD)cbOutputBuffer,
&dwBytesWritten);
}
}
else
{
hr = E_UNEXPECTED;
CHECK_HR(hr, "WDF File Object is NULL");
}
}
else
{
hr = E_UNEXPECTED;
CHECK_HR(hr, "Received invalid/unsupported IOCTL code '0x%lx'",ControlCode);
}
// Complete the request
if (hr == S_OK)
{
pRequest->CompleteWithInformation(hr, dwBytesWritten);
}
else
{
pRequest->Complete(hr);
}
return;
}
STDMETHODIMP_ (void)
CQueue::OnCleanup(
_In_ IWDFObject* pWdfObject
)
{
// Destroy the client context map
HRESULT hr = S_OK;
ContextMap* pClientContextMap = NULL;
hr = pWdfObject->RetrieveContext((void**)&pClientContextMap);
if((hr == S_OK) && (pClientContextMap != NULL))
{
pClientContextMap->Release();
pClientContextMap = NULL;
}
}
|