Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1713 lines
56 KiB

/*******************************************************************************
*
* (C) COPYRIGHT MICROSOFT CORP., 2000
*
* TITLE: IStiUSD.cpp
*
* VERSION: 1.0
*
* DATE: 18 July, 2000
*
* DESCRIPTION:
* Implementation of the WIA sample scanner IStiUSD methods.
*
*******************************************************************************/
#include "pch.h"
#ifndef INITGUID
#include <initguid.h>
#endif
extern HINSTANCE g_hInst; // used for WIAS_LOGPROC macro
/**************************************************************************\
* CWIADevice::CWIADevice
*
* Device class constructor
*
* Arguments:
*
* None
*
* Return Value:
*
* None
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
CWIADevice::CWIADevice(LPUNKNOWN punkOuter):
m_cRef(1),
m_punkOuter(NULL),
m_dwLastOperationError(0),
m_dwLockTimeout(DEFAULT_LOCK_TIMEOUT),
m_bDeviceLocked(FALSE),
m_hDeviceDataHandle(NULL),
m_bPolledEvent(FALSE),
m_hFakeEventKey(NULL),
m_guidLastEvent(GUID_NULL),
m_pIDrvItemRoot(NULL),
m_pStiDevice(NULL),
m_pIWiaLog(NULL),
m_bADFEnabled(FALSE),
m_bADFAttached(TRUE),
m_lClientsConnected(0),
m_pScanAPI(NULL),
m_NumSupportedFormats(0),
m_pSupportedFormats(NULL),
m_NumSupportedCommands(0),
m_NumSupportedEvents(0),
m_pCapabilities(NULL),
m_NumInitialFormats(0),
m_pInitialFormats(NULL)
{
//
// initialize internal structures
//
memset(&m_EventOverlapped,0,sizeof(m_EventOverlapped));
memset(m_EventData,0,sizeof(m_EventData));
memset(&m_SupportedTYMED,0,sizeof(m_SupportedTYMED));
memset(&m_SupportedDataTypes,0,sizeof(m_SupportedDataTypes));
memset(&m_SupportedIntents,0,sizeof(m_SupportedIntents));
memset(&m_SupportedCompressionTypes,0,sizeof(m_SupportedCompressionTypes));
memset(&m_SupportedResolutions,0,sizeof(m_SupportedResolutions));
memset(&m_SupportedPreviewModes,0,sizeof(m_SupportedPreviewModes));
memset(&m_RootItemInitInfo,0,sizeof(m_RootItemInitInfo));
memset(&m_ChildItemInitInfo,0,sizeof(m_ChildItemInitInfo));
// See if we are aggregated. If we are (almost always the case) save
// pointer to the controlling Unknown , so subsequent calls will be
// delegated. If not, set the same pointer to "this".
if (punkOuter) {
m_punkOuter = punkOuter;
} else {
// Cast below is needed in order to point to right virtual table
m_punkOuter = reinterpret_cast<IUnknown*>(static_cast<INonDelegatingUnknown*>(this));
}
#ifdef UNKNOWN_LENGTH_FEEDER_ONLY_SCANNER
//
// since this driver supports a scanner that has only a feeder, the
// ADF Enabled flag needs to be set to TRUE by default. This will
// control the behavior of DrvAcquireItemData() method.
//
m_bADFEnabled = TRUE;
#endif
}
/**************************************************************************\
* CWIADevice::~CWIADevice
*
* Device class destructor
*
* Arguments:
*
* None
*
* Return Value:
*
* None
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
CWIADevice::~CWIADevice(void)
{
SetNotificationHandle(NULL);
//
// WIA member destruction
//
// Tear down the driver item tree.
if (m_pIDrvItemRoot) {
WIAS_LWARNING(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("~CWIADevice, Deleting Device Item Tree (this is OK)"));
DeleteItemTree();
m_pIDrvItemRoot = NULL;
}
// free any IO handles opened
if (m_hDeviceDataHandle) {
WIAS_LWARNING(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("~CWIADevice, Closing DefaultDeviceDataHandle"));
CloseHandle(m_hDeviceDataHandle);
m_hDeviceDataHandle = NULL;
}
// Delete allocated arrays
DeleteCapabilitiesArrayContents();
DeleteSupportedIntentsArrayContents();
if (m_pIWiaLog)
m_pIWiaLog->Release();
if (m_pScanAPI) {
// disable fake scanner device
m_pScanAPI->FakeScanner_DisableDevice();
delete m_pScanAPI;
m_pScanAPI = NULL;
}
}
/**************************************************************************\
* CWIADevice::PrivateInitialize()
*
* PrivateInitialize is called from the CreateInstance() method of the
* WIA driver's class factory. It is needed to initialize the WIA
* driver object (CWIADevice). If this initialization fails the
* object will not be returned to the caller. This prevents the creation
* of a nonfunctional WIA device.
*
* Arguments:
*
* None
*
* Return Value:
*
* S_OK - if the operation succeeded
* E_xxx - Error code if the operation fails
*
\**************************************************************************/
HRESULT CWIADevice::PrivateInitialize()
{
//
// attempt to create the IWiaLog interface to log status and errors to
// wiaservc.log file
//
HRESULT hr = CoCreateInstance(CLSID_WiaLog, NULL, CLSCTX_INPROC_SERVER,IID_IWiaLog,(void**)&m_pIWiaLog);
if (S_OK == hr) {
m_pIWiaLog->InitializeLog((LONG)(LONG_PTR)g_hInst);
WIAS_LTRACE(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,WIALOG_LEVEL1,("Logging COM object created successfully for wiascanr.dll"));
} else {
WIAS_LTRACE(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,WIALOG_LEVEL1,("Logging COM object not be created successfully for wiascanr.dll (STI only?)"));
hr = S_OK;
}
return hr;
}
/**************************************************************************\
* CWIADevice::Initialize
*
* Initialize is called by the WIA service when the driver is first loaded.
* Initialize is also called when a client uses the legacy STI APIs and
* calls IStillImage::CreateDevice() method.
*
* This method should initialize the WIA driver and the device for use.
* WIA drivers can store the pIStiDevControl interface if it needs it at a
* later time. IStiDevControl::AddRef() must be called before storing this
* interface. If you do not need to store the interface, then ignore it.
* DO NOT RELEASE the IStiDevControl interface if you have not called
* IStiDevControl::AddRef() first. This may cause unpredictable results.
*
* The IStiDeviceControl interface is needed to get information about your
* device's ports. The port name used in a CreateFile call can be obtained
* by calling the IStiDeviceControl::GetMyDevicePortName() method.
*
* For devices on shared ports, such as serial port devices, opening the port
* in Initialize() is not recommended. The port should only be opened in calls
* to IStiUsd::LockDevice(). The closing of the ports should be internally
* controlled to provide fast access. (opening and closing in
* LockDevice/UnLockDevice is very inefficent. CreateFile() could cause a delay
* making the user's experience appear slow, and nonresponsive)
*
* If this WIA driver can not support multiple CreateFile() calls on the same
* device port, then the IStiDevControl::GetMyDeviceOpenMode() should be called.
*
* The WIA driver should check the returned MODE value for the flag
* STI_DEVICE_CREATE_DATA and open the port accordingly.
*
* The following flags could be set:
*
* STI_DEVICE_CREATE_STATUS - open port for status
* STI_DEVICE_CREATE_DATA - open port for data
* STI_DEVICE_CREATE_BOTH - open port for status and data
*
* If the device port needs to be opened, a call to CreateFile() should be
* used. When opening a port the flag FILE_FLAG_OVERLAPPED should be used.
* This will allow OVERLAPPED i/o to be used when access the device. Using
* OVERLAPPED i/o will help control responsive access to the hardware. When
* a problem is detected the WIA driver can call CancelIo() to stop all
* current hardware access.
*
* Arguments:
*
* pIStiDevControl - device interface used for obtaining port information
* dwStiVersion - STI version
* hParametersKey - HKEY for registry reading/writing
*
* Return Value:
*
* S_OK - if the operation was successful
* E_xxx - if the operation failed
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::Initialize(
PSTIDEVICECONTROL pIStiDevControl,
DWORD dwStiVersion,
HKEY hParametersKey)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::Initialize");
if (!pIStiDevControl) {
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("CWIADevice::Initialize, invalid device control interface"));
return STIERR_INVALID_PARAM;
}
HRESULT hr = S_OK;
//
// Get the mode of the device to check why we were created. status, data, or both...
//
DWORD dwMode = 0;
hr = pIStiDevControl->GetMyDeviceOpenMode(&dwMode);
if(FAILED(hr)){
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("CWIADevice::Initialize, couldn't get device open mode"));
return hr;
}
if(dwMode & STI_DEVICE_CREATE_DATA)
{
//
// device is being opened for data
//
}
if(dwMode & STI_DEVICE_CREATE_STATUS)
{
//
// device is being opened for status
//
}
if(dwMode & STI_DEVICE_CREATE_BOTH)
{
//
// device is being opened for both data and status
//
}
//
// Get the name of the device port to be used in a call to CreateFile().
//
WCHAR szDevicePortNameW[MAX_PATH];
memset(szDevicePortNameW,0,sizeof(szDevicePortNameW));
hr = pIStiDevControl->GetMyDevicePortName(szDevicePortNameW,sizeof(szDevicePortNameW)/sizeof(WCHAR));
if(FAILED(hr)) {
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("CWIADevice::Initialize, couldn't get device port"));
return hr;
}
//
// uncomment the code block below to have the driver create the kernel mode file
// handles and open a communication channel to the device.
//
/*
//
// Open kernel mode device driver. Use the FILE_FLAG_OVERLAPPED flag for proper cancellation
// of kernel mode operations and asynchronous file IO. The CancelIo() call will function
// properly if this flag is used. It is recommended to use this flag.
//
m_hDeviceDataHandle = CreateFileW(szDevicePortNameW,
GENERIC_READ | GENERIC_WRITE, // Access mask
0, // Share mode
NULL, // SA
OPEN_EXISTING, // Create disposition
FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED, // Attributes
NULL );
m_dwLastOperationError = ::GetLastError();
hr = (m_hDeviceDataHandle != INVALID_HANDLE_VALUE) ?
S_OK : MAKE_HRESULT(SEVERITY_ERROR,FACILITY_WIN32,m_dwLastOperationError);
if (FAILED(hr)) {
return hr;
}
*/
if (SUCCEEDED(hr)) {
//
// creation of fake scanner device is here.
//
#ifdef UNKNOWN_LENGTH_FEEDER_ONLY_SCANNER
hr = CreateFakeScanner(&m_pScanAPI,UNKNOWN_FEEDER_ONLY_SCANNER_MODE);
#else
hr = CreateFakeScanner(&m_pScanAPI,FLATBED_SCANNER_MODE);
#endif
if (m_pScanAPI) {
//
// initialize fake scanner device
//
hr = m_pScanAPI->FakeScanner_Initialize();
} else {
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("Initialize, Could not create FakeScanner API object"));
hr = E_OUTOFMEMORY;
WIAS_LHRESULT(m_pIWiaLog, hr);
}
}
//
// Open DeviceData section to read driver specific information
//
HKEY hKey = hParametersKey;
HKEY hOpenKey = NULL;
if (RegOpenKeyEx(hKey, // handle to open key
TEXT("DeviceData"), // address of name of subkey to open
0, // options (must be NULL)
KEY_QUERY_VALUE|KEY_READ, // just want to QUERY a value
&hOpenKey // address of handle to open key
) == ERROR_SUCCESS) {
//
// This is where you read registry entries for your device.
// The DeviceData section is the proper place to put this information. Information about
// your device should have been written using the WIA device's .INF installation file.
// You can access this information from this location in the Registry.
// The hParameters HKEY is owned by the WIA service. DO NOT CLOSE THIS KEY. Always close
// any HKEYS opened by this WIA driver after you are finished.
//
//
// close registry key when finished, reading DeviceData information.
//
RegCloseKey(hOpenKey);
} else {
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("CWIADevice::Initialize, couldn't open DeviceData KEY"));
return E_FAIL;
}
return hr;
}
/**************************************************************************\
* CWIADevice::GetCapabilities
*
* GetCapabilities is called by the WIA service to obtain the USD's
* capabilities. The WIA driver should fill out the following fields in the
* STI_USD_CAPS structure:
*
* 1. dwVersion field with STI_VERSION letting the WIA service know what
* version of STI this driver supports.
* 2. dwGenericCaps field with supported capabilities of the WIA device.
*
*
* Arguments:
*
* pUsdCaps - Pointer to a STI_USD_CAPS USD capabilities structure.
*
* Return Value:
*
* S_OK - if the data can be written correctly
* E_xxx - Error code if the operation fails
*
*
* Remarks:
*
* This WIA driver sets the following capability flags:
*
* 1. STI_GENCAP_WIA - This driver supports WIA
* 2. STI_USD_GENCAP_NATIVE_PUSHSUPPORT - This driver supports push buttons
* 3. STI_GENCAP_NOTIFICATIONS - This driver requires the use of interrupt
* events.
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::GetCapabilities(PSTI_USD_CAPS pUsdCaps)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::GetCapabilities");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if (!pUsdCaps) {
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("CWIADevice::GetCapabilities, NULL parameter"));
return E_INVALIDARG;
}
memset(pUsdCaps, 0, sizeof(STI_USD_CAPS));
pUsdCaps->dwVersion = STI_VERSION; // STI verison
pUsdCaps->dwGenericCaps = STI_GENCAP_WIA| // WIA support
STI_USD_GENCAP_NATIVE_PUSHSUPPORT| // button support
STI_GENCAP_NOTIFICATIONS; // interrupt event support
return S_OK;
}
/**************************************************************************\
* CWIADevice::GetStatus
*
* GetStatus is called by the WIA service for two major operations:
*
* 1. Checking device ON-LINE status.
* 2. Polling for device events. (like a push button event)
*
* Determining the operation request can be done by checking the StatusMask
* field of the STI_DEVICE_STATUS structure. The StatusMask field can be
* any of the following requests. (STI_DEVSTATUS_ONLINE_STATE or
* STI_DEVSTATUS_EVENTS_STATE)
*
* STI_DEVSTATUS_ONLINE_STATE = check if the device is ON-LINE.
*
* This operation request should be fill by setting the dwOnlinesState
* field of the STI_DEVICE_STATUS structure.
* Value that should be used are:
*
* STI_ONLINESTATE_OPERATIONAL - Device is ON-LINE and operational
* STI_ONLINESTATE_OFFLINE - Device is OFF-LINE and NOT operational
* STI_ONLINESTATE_PENDING - Device has I/O operations pending
* STI_ONLINESTATE_ERROR - Device is currently in an error state
* STI_ONLINESTATE_PAUSED - Device is paused
* STI_ONLINESTATE_PAPER_JAM - Device has a paper jam
* STI_ONLINESTATE_PAPER_PROBLEM - Device has a paper problem
* STI_ONLINESTATE_IO_ACTIVE - Device is active, but not accepting
* commands at this time
* STI_ONLINESTATE_BUSY - Device is busy
* STI_ONLINESTATE_TRANSFERRING - Device is currently transferring data
* STI_ONLINESTATE_INITIALIZING - Device is being initialized
* STI_ONLINESTATE_WARMING_UP - Device is warming up
* STI_ONLINESTATE_USER_INTERVENTION - Device requires user intervention
* to resolve a problem
* STI_ONLINESTATE_POWER_SAVE - Device is in power save mode
*
*
* STI_DEVSTATUS_EVENTS_STATE = check for device events.
*
* This operation request should be filled by setting the dwEventHandlingState
* field of the STI_DEVICE_STATUS structure.
* Values that should be used are:
*
* STI_EVENTHANDLING_PENDING - Device has an event pending and is wanting
* to report it to the WIA service.
*
* It is always a good idea to clear the STI_EVENTHANDLING_PENDING flag from
* the dwEventHandlingState member to make sure that it is properly set when
* a device event occurs.
*
* When the STI_EVENTHANDLING_PENDING is set the WIA service is signaled that
* an event has occured in the WIA driver. The WIA service calls the
* GetNotificationData() entry point to get more information about the event.
* GetNotificationData() is called for polled events and interrupt events.
* This is where you should fill out the proper event information to return to
* the WIA service.
*
*
* Arguments:
*
* pDevStatus - Pointer to a STI_DEVICE_STATUS device status structure.
*
* Return Value:
*
* S_OK - if the data can be written correctly
* E_INVALIDARG - if an invalid parameter was passed in to the GetStatus
* method.
* E_xxx - Error code if the operation fails
*
*
* Remarks:
*
* This WIA driver should set the m_guidLastEvent to the proper event GUID when
* an event is detected. It should also set the m_bPolledEvent flag to TRUE
* indicating that the event was received from a "polled source" (the GetStatus()
* call). The m_guidLastEvent is checked at a later time when the WIA service
* calls GetNotificationData().
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::GetStatus(PSTI_DEVICE_STATUS pDevStatus)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::GetStatus");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if(!pDevStatus)
{
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("CWIADevice::GetStatus, NULL parameter"));
return E_INVALIDARG;
}
HRESULT hr = S_OK;
//
// If we are asked, verify the device is online.
//
if (pDevStatus->StatusMask & STI_DEVSTATUS_ONLINE_STATE) {
WIAS_LTRACE(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,WIALOG_LEVEL2,("GetStatus, WIA is asking the device if we are ONLINE"));
//
// assume the device is OFF-LINE before continuing. This will validate
// that the on-line check was successful.
//
pDevStatus->dwOnlineState = STI_ONLINESTATE_OFFLINE;
hr = m_pScanAPI->FakeScanner_DeviceOnline();
if (SUCCEEDED(hr)) {
WIAS_LTRACE(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,WIALOG_LEVEL2,("GetStatus, Device is ONLINE"));
//
// device is ON-LINE and operational
//
pDevStatus->dwOnlineState |= STI_ONLINESTATE_OPERATIONAL;
} else {
//
// device is OFF-LINE and NOT operational
//
WIAS_LERROR(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("GetStatus, Device is OFFLINE"));
}
}
//
// If we are asked, verify state of an event handler (front panel buttons, user controlled attachments, etc..).
//
//
// If your device requires polling, then this is where you would specify the event
// result.
//
// *** It is not recommended to have polled events. Interrupt events are better
// for performance, and reliability. See the SetNotificationsHandle() method for how to
// implement interrupt events.
//
//
// clear the dwEventHandlingState field first to make sure we are really setting
// a pending event.
//
pDevStatus->dwEventHandlingState &= ~STI_EVENTHANDLING_PENDING;
if (pDevStatus->StatusMask & STI_DEVSTATUS_EVENTS_STATE) {
//
// set the polled event source flag to true, because this will control the
// behavior of the GetNotificationData() method.
//
m_bPolledEvent = TRUE;
//
// set the polled event result here, for the GetNotificationData() method to
// read and report.
//
m_guidLastEvent = GUID_NULL; // WIA_EVENT_SCAN_IMAGE is an example of an event GUID
if (m_guidLastEvent != GUID_NULL) {
//
// if the event GUID is NOT GUID_NULL, set the STI_EVENTHANDLING_PENDING flag
// letting the WIA service know that we have an event ready. This will tell
// the WIA service to call GetNotificationData() for the event specific information.
//
pDevStatus->dwEventHandlingState |= STI_EVENTHANDLING_PENDING;
}
}
return S_OK;
}
/**************************************************************************\
* CWIADevice::DeviceReset
*
* DeviceReset is called to reset the device to a power-on state.
*
* Arguments:
*
* None
*
* Return Value:
*
* S_OK - if device reset was successful
* E_xxx - Error code if the operation fails
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::DeviceReset(void)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::DeviceReset");
if(!m_pScanAPI){
return E_UNEXPECTED;
}
return m_pScanAPI->FakeScanner_ResetDevice();
}
/**************************************************************************\
* CWIADevice::Diagnostic
*
* Diagnostic is called when the device needs to be tested. The device
* default property page "Test Device" button calls this method.
*
* The WIA driver should set the following fields of the STI_DIAG structure:
* sErrorInfo - error information contained in a STI_ERROR_INFO structure,
* detailing the test results of the the diagnostic method.
* The WIA device should set the following members:
* dwGenericError - set to NOERROR if the device passed
* the diagnostic test, or to an error
* code if the device failed the test.
* dwVendorError - set this to give more information
* about the failure. Remember that
* this code is specific to this device
* only. The caller of Diagnostic will
* not know the meaning of this vendor-
* specific code.
* szExtendedErrorText - Extended error text. This member
* is limited to 255 characters.
* dwBasicDiagCode - this code indicates the type of test to be performed.
* Currently this must be set to STI_DIAGCODE_HWPRESENCE.
* dwVendorDiagCode - OPTIONAL - vendor supplied diagnostic test code.
* dwStatusMask - RESERVED FOR FUTURE USE
*
* Arguments:
*
* pBuffer - Pointer to a STI_DIAG diagnostic result structure.
*
* Return Value:
*
* S_OK - if the data can be written correctly
* E_xxx - Error code if the operation fails
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::Diagnostic(LPSTI_DIAG pBuffer)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::Diagnostic");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if(!pBuffer){
return E_INVALIDARG;
}
if(!m_pScanAPI){
return E_UNEXPECTED;
}
//
// initialize response buffer
//
memset(&pBuffer->sErrorInfo,0,sizeof(pBuffer->sErrorInfo));
//
// This sample device does nothing during this diagnostic method call.
// It is recommended to perform a quick test of the hardware to verify
// that it is functional within operational parameters.
//
pBuffer->sErrorInfo.dwGenericError = NOERROR;
pBuffer->sErrorInfo.dwVendorError = 0;
return m_pScanAPI->FakeScanner_Diagnostic();
}
/**************************************************************************\
* CWIADevice::Escape
*
* Escape is called to pass information directly to the hardware. This
* method can be called directly using the original STI (legacy) APIs or
* the IWiaItemExtras::Escape() method.
* Any WIA application can obtain access to the IWiaItemExtras interface.
* It is recommended that you validate all incoming and outgoing calls to
* this method.
*
* Recommended validation order:
* 1. validate the Function code first. If it is not one your codes, fail
* immediately. This will help to prevent incorrect codes from being
* processed in your driver.
* 2. validate the incoming buffer second. If it is not valid, fail
* immediately. Bad incoming buffers coulsd crash the WIA driver.
* 3. validate the outgoing buffer third. If it is not valid, fail
* immediately. If you can not complete the request by writing the
* necessary data why process it?
* 4. validate the outgoing size buffer last. This is important. If you can
* not write the amount of data you just wrote to the outgoing buffer
* then that data can not be properly processed by the caller.
*
* Arguments:
*
* EscapeFunction - Command to be issued.
* pInData - Input data to be passed with command.
* cbInDataSize - Size of input data.
* pOutData - Output data to be passed back from command.
* cbOutDataSize - Size of output data buffer.
* pcbActualData - Size of output data actually written.
*
* Return Value:
*
* S_OK - if the operation was successful
* E_xxx - Error code if the operation fails
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::Escape(
STI_RAW_CONTROL_CODE EscapeFunction,
LPVOID pInData,
DWORD cbInDataSize,
LPVOID pOutData,
DWORD cbOutDataSize,
LPDWORD pcbActualData)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::Escape");
//
// only process EscapeFunction codes that are known to your driver.
// Any application can send escape calls to your driver using the
// IWiaItemExtras interface Escape() method call. The driver must
// be prepared to validate all incoming calls to Escape().
//
//
// since this driver does not support any escape functions it will reject all
// incoming EscapeFunction codes.
//
// If your driver supports an EscapeFunction, then add your function code to the
// switch statement, and set hr = S_OK. This will allow the function to continue
// to the incoming/outgoing buffer validation.
//
HRESULT hr = E_NOTIMPL;
switch(EscapeFunction){
case 0:
default:
break;
}
//
// if an EscapeFunction code is supported, then first validate the incoming and
// outgoing buffers.
//
if(S_OK == hr){
//
// validate the incoming data buffer
//
if(IsBadReadPtr(pInData,cbInDataSize)){
hr = E_UNEXPECTED;
}
//
// if the incoming buffer is valid, proceed to validate the
// outgoing buffer
//
if(S_OK == hr){
if(IsBadWritePtr(pOutData,cbOutDataSize)){
hr = E_UNEXPECTED;
} else {
//
// validate the outgoing size pointer
//
if(IsBadWritePtr(pcbActualData,sizeof(DWORD))){
hr = E_UNEXPECTED;
}
}
}
//
// now that buffer validation is complete, proceed to process the proper
// EscapeFunction code.
//
if (S_OK == hr) {
//
// only process a validated EscapeFunction code, and buffers
//
}
}
//
// if your driver will not support this entry point then it needs to return
// E_NOTIMPL. (error, not implemented)
//
return hr;
}
/**************************************************************************\
* CWIADevice::GetLastError
*
* GetLastError is called to obtain the last operation error code reported
* by the WIA device. This is an error code that is specific to the WIA
* driver. If the caller wants more information about this error code then
* they will call GetLastErrorInfo(). The GetLastErrorInfo() method is
* responsible for adding more details about the requested error. (strings,
* extended codes...etc)
*
* Arguments:
*
* pdwLastDeviceError - Pointer to a DWORD indicating the last error code.
*
* Return Value:
*
* S_OK - if the data can be written correctly
* E_xxx - Error code if the operation fails
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::GetLastError(LPDWORD pdwLastDeviceError)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::GetLastError");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if (!pdwLastDeviceError) {
return E_INVALIDARG;
}
*pdwLastDeviceError = m_dwLastOperationError;
return S_OK;
}
/**************************************************************************\
* CWIADevice::LockDevice
*
* LockDevice is called to lock a WIA device for exclusive access. This
* method can be called directly by an application using the legacy STI
* APIs, or by the WIA service.
*
* A device should return STIERR_DEVICE_LOCKED if it has already been
* locked by another client. The WIA service will call LockDevice to
* control the exclusive access to the WIA device.
*
* Do not get this confused with drvLockWiaDevice(). IStiUsd::LockDevice
* can get called directly by an appliation using the legacy STI APIs.
* drvLockWiaDevice() is only called by the WIA service. No appliation can
* call drvLockWiaDevice().
*
* Arguments:
*
* None
*
* Return Value:
*
* S_OK - if the device was successfully locked
* STIERR_DEVICE_LOCKED - Error code if the device is already locked
*
* Remarks:
*
* The WIA shell extension, which controls the visibility of a WIA device
* in the "My Computer" and "Control Panel", calls LockDevice() directly.
* This is used to lock the the device so the IStiUsd::Diagnostic() method
* can get called when the user presses the "TEST Device" button.
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::LockDevice(void)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::LockDevice");
//
// be very careful about how long locks are held. An application can
// come in using the STI APIs only, and lock your device. This lock,
// if held indefinitly could prevent access to your device from other
// applications installed on the system.
//
// It might be useful to implement some sort of reasonable locking timeout
// mechanism. If a lock is held too long, then it should be auto-unlocked.
// to allow others to access the device.
// *** This should only be done, if you know it is safe to unlock your hardware.
// Cases that may take a long time (like HUGE scans) can hold a lock for the entire
// length. A good question to ask in a long-locking case, is, "what is my driver
// doing, that requires such a long locking time?"
//
HRESULT hr = S_OK;
if (m_bDeviceLocked) {
hr = STIERR_DEVICE_LOCKED;
} else {
m_bDeviceLocked = TRUE;
}
return hr;
}
/**************************************************************************\
* CWIADevice::UnLockDevice
*
* UnLockDevice is called to unlock a WIA device from exclusive access. This
* method can be called directly by an application using the legacy STI
* APIs, or by the WIA service.
*
* A device should return STIERR_NEEDS_LOCK if it has not already been
* locked by another client. The WIA service will call UnLockDevice to
* release exclusive access to the WIA device.
*
* Do not get this confused with drvUnLockWiaDevice(). IStiUsd::UnLockDevice
* can get called directly by an appliation using the legacy STI APIs.
* drvUnLockWiaDevice() is only called by the WIA service. No appliation can
* call drvUnLockWiaDevice().
*
* Arguments:
*
* None
*
* Return Value:
*
* S_OK - if the device was successfully locked
* STIERR_NEEDS_LOCK - Error code if the device isn't already locked
*
* Remarks:
*
* The WIA shell extension, which controls the visibility of a WIA device
* in the "My Computer" and "Control Panel", calls UnLockDevice() directly.
* (see IStiUsd::LockDevice() for details)
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::UnLockDevice(void)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::UnLockDevice");
HRESULT hr = S_OK;
if (!m_bDeviceLocked)
hr = STIERR_NEEDS_LOCK;
else {
m_bDeviceLocked = FALSE;
}
return hr;
}
/**************************************************************************\
* CWIADevice::RawReadData
*
* RawReadData is called to access the hardware directly. Any application
* that uses the legacy STI APIs can call RawReadData(). This method is
* used when an application (useally a vendor-supplied application or library)
* wants to read data from an open device port (controlled by this WIA driver).
*
* Arguments:
*
* lpBuffer - buffer for returned data
* lpdwNumberOfBytes - number of bytes to read/returned
* lpOverlapped - overlap
*
* Return Value:
*
* S_OK - if the read was successful
* E_xxx - if the operation failed
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::RawReadData(
LPVOID lpBuffer,
LPDWORD lpdwNumberOfBytes,
LPOVERLAPPED lpOverlapped)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::RawReadData");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if(!lpdwNumberOfBytes){
return E_INVALIDARG;
}
DWORD dwNumberOfBytes = *lpdwNumberOfBytes;
if(!lpBuffer){
return E_INVALIDARG;
}
//
// lpOverlapped is used by a call to ReadFile, or DeviceIOControl call. This
// parameter can be NULL according to those APIs.
//
HRESULT hr = E_NOTIMPL;
//
// if your driver will not support this entry point then it needs to return
// E_NOTIMPL. (error, not implemented)
//
return hr;
}
/**************************************************************************\
* CWIADevice::RawWriteData
*
* RawWriteData is called to access the hardware directly. Any application
* that uses the legacy STI APIs can call RawWriteData(). This method is
* used when an application (useally a vendor-supplied application or library)
* wants to write data to an open device port (controlled by this WIA driver).
*
* Arguments:
*
* lpBuffer - buffer for returned data
* dwNumberOfBytes - number of bytes to write
* lpOverlapped - overlap
*
* Return Value:
*
* S_OK - if the write was successful
* E_xxx - if the operation failed
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::RawWriteData(
LPVOID lpBuffer,
DWORD dwNumberOfBytes,
LPOVERLAPPED lpOverlapped)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::RawWriteData");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if(!lpBuffer){
return E_INVALIDARG;
}
//
// lpOverlapped is used by a call to ReadFile, or DeviceIOControl call. This
// parameter can be NULL according to those APIs.
//
HRESULT hr = E_NOTIMPL;
//
// if your driver will not support this entry point then it needs to return
// E_NOTIMPL. (error, not implemented)
//
return hr;
}
/**************************************************************************\
* CWIADevice::RawReadCommand
*
* RawReadCommand is called to access the hardware directly. Any application
* that uses the legacy STI APIs can call RawReadCommand(). This method is
* used when an application (useally a vendor-supplied application or library)
* wants to read data from an open device port (controlled by this WIA driver).
*
* Arguments:
*
* lpBuffer - buffer for returned data
* lpdwNumberOfBytes - number of bytes to read/returned
* lpOverlapped - overlap
*
* Return Value:
*
* S_OK - if the read was successful
* E_xxx - if the operation failed
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::RawReadCommand(
LPVOID lpBuffer,
LPDWORD lpdwNumberOfBytes,
LPOVERLAPPED lpOverlapped)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::RawReadCommand");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if(!lpdwNumberOfBytes){
return E_INVALIDARG;
}
DWORD dwNumberOfBytes = *lpdwNumberOfBytes;
if(!lpBuffer){
return E_INVALIDARG;
}
//
// lpOverlapped is used by a call to ReadFile, or DeviceIOControl call. This
// parameter can be NULL according to those APIs.
//
HRESULT hr = E_NOTIMPL;
//
// if your driver will not support this entry point then it needs to return
// E_NOTIMPL. (error, not implemented)
//
return hr;
}
/**************************************************************************\
* CWIADevice::RawWriteCommand
*
* RawWriteCommand is called to access the hardware directly. Any application
* that uses the legacy STI APIs can call RawWriteCommand(). This method is
* used when an application (useally a vendor-supplied application or library)
* wants to write data to an open device port (controlled by this WIA driver).
*
* Arguments:
*
* lpBuffer - buffer for returned data
* nNumberOfBytes - number of bytes to write
* lpOverlapped - overlap
*
* Return Value:
*
* S_OK - if the write was successful
* E_xxx - if the operation failed
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::RawWriteCommand(
LPVOID lpBuffer,
DWORD dwNumberOfBytes,
LPOVERLAPPED lpOverlapped)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::RawWriteCommand");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if(!lpBuffer){
return E_INVALIDARG;
}
//
// lpOverlapped is used by a call to ReadFile, or DeviceIOControl call. This
// parameter can be NULL according to those APIs.
//
HRESULT hr = E_NOTIMPL;
//
// if your driver will not support this entry point then it needs to return
// E_NOTIMPL. (error, not implemented)
//
return hr;
}
/**************************************************************************\
* CWIADevice::SetNotificationHandle
*
* SetNotificationHandle is called by the WIA service or internally by this
* driver to start or stop event notifications. The WIA service will pass
* in a valid handle (created using CreateEvent() ), indicating that it
* wants the WIA driver to signal this handle when an event occurs in the
* hardware.
*
* NULL can be passed to this SetNotificationHandle() method. NULL
* indicates that the WIA driver is to STOP all device activity, and exit
* any event wait operations.
*
* Arguments:
*
* hEvent - HANDLE to an event created by the WIA service using CreateEvent()
* This parameter can be NULL, indicating that all previous event
* waiting should be stopped.
*
* Return Value:
*
* S_OK - if the operation was successful
* E_xxx - Error Code if the operation fails
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::SetNotificationHandle(HANDLE hEvent)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::SetNotificationHandle");
HRESULT hr = S_OK;
if (hEvent && (hEvent != INVALID_HANDLE_VALUE)) {
//
// A valid handle indicates that we are asked to start our "wait"
// for device interrupt events
//
WIAS_LWARNING(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("SetNotificationHandle, hEvent = %d",hEvent));
//
// reset last event GUID to GUID_NULL
//
m_guidLastEvent = GUID_NULL;
//
// clear EventOverlapped structure
//
memset(&m_EventOverlapped,0,sizeof(m_EventOverlapped));
//
// fill overlapped hEvent member with the event passed in by the WIA service.
// This handle will be automatically signaled when an event is triggered at
// the hardware level.
//
m_EventOverlapped.hEvent = hEvent;
//
// clear event data buffer. This is the buffer that will be used to determine
// what event was signaled from the device.
//
memset(m_EventData,0,sizeof(m_EventData));
//
// the code block below starts an interrupt event session using DeviceIoControl()
//
#ifdef USE_REAL_EVENTS
//
// use the following call for interrupt events on your device
//
DWORD dwError = 0;
BOOL bResult = DeviceIoControl( m_hDeviceDataHandle,
IOCTL_WAIT_ON_DEVICE_EVENT,
NULL,
0,
&m_EventData,
sizeof(m_EventData),
&dwError,
&m_EventOverlapped );
if (bResult) {
hr = S_OK;
} else {
hr = HRESULT_FROM_WIN32(::GetLastError());
}
#else // USE_REAL_EVENTS
//
// THIS IS FOR TESTING ONLY, DO NOT USE THIS METHOD FOR PROCESSING DEVICE EVENTS!!!
//
if (m_hFakeEventKey) {
RegCloseKey(m_hFakeEventKey);
m_hFakeEventKey = NULL;
}
DWORD dwDisposition = 0;
if (RegCreateKeyEx(HKEY_CURRENT_USER,
HKEY_WIASCANR_FAKE_EVENTS,
0,
NULL,
0,
KEY_READ,
NULL,
&m_hFakeEventKey,
&dwDisposition) == ERROR_SUCCESS) {
if (RegNotifyChangeKeyValue(
m_hFakeEventKey, // handle to key to watch
FALSE, // subkey notification option
REG_NOTIFY_CHANGE_LAST_SET, // changes to be reported
m_EventOverlapped.hEvent, // handle to event to be signaled
TRUE // asynchronous reporting option
) == ERROR_SUCCESS ) {
}
}
#endif // USE_REAL_EVENTS
} else {
//
// stop any hardware waiting events here, the WIA service has notified us to kill
// all hardware event waiting
//
WIAS_LWARNING(m_pIWiaLog,WIALOG_NO_RESOURCE_ID,("SetNotificationHandle, Disabling event Notifications"));
#ifdef USE_REAL_EVENTS
//
// Stop hardware interrupt events. This will actually stop all activity on the device.
// Since DeviceIOControl was used with OVERLAPPED i/o functionality the CancelIo()
// can be used to stop all kernel mode activity.
//
//
// NOTE: It is important to use overlapped i/o calls with all activity involving the
// kernel mode drivers. This will allow for proper time-outs and cancellation
// of device requests.
//
if(m_hDeviceDataHandle){
if(!CancelIo(m_hDeviceDataHandle)){
//
// cancelling of the IO failed, call GetLastError() here to determine the cause.
//
LONG lError = ::GetLastError();
}
}
#else // USE_REAL_EVENTS
if (m_hFakeEventKey) {
RegCloseKey(m_hFakeEventKey);
m_hFakeEventKey = NULL;
}
#endif // USE_REAL_EVENTS
}
return hr;
}
/**************************************************************************\
* CWIADevice::GetNotificationData
*
* GetNotificationData is called by the WIA service to get information about
* an event that has just been signaled. GetNotificationsData can be called
* as a result of one of two event operations.
*
* 1. GetStatus() reported that there was an event pending, by setting the
* STI_EVENTHANDLING_PENDING flag in the STI_DEVICE_STATUS structure.
*
* 2. The hEvent handle passed in by SetNotificationHandle() was signaled
* by the hardware, or by calling SetEvent() directly.
*
* The WIA driver is responsible for filling out the STINOTIFY structure
* members:
* dwSize - size of the STINOTIFY structure.
* guidNotificationCode - GUID that represents the event that is to be
* responded to. This should be set to GUID_NULL
* if no event is to be sent. This will tell the
* WIA service that no event really happened.
* abNotificationData - OPTIONAL - vendor specific information. This data
* is limited to 64 bytes of data ONLY.
*
* Arguments:
*
* pBuffer - Pointer to a STINOTIFY structure.
*
* Return Value:
*
* S_OK - if the data can be written successfully
* E_xxx - Error code if the operation fails
*
* Remarks:
*
* This WIA driver checks the m_bPolledEvent flag to determine where to get
* the event information from. If this flag is TRUE, it uses the event GUID
* already set by the GetStatus() call. (see GetStatus comments for details)
* If this flag is FALSE, then the WIA driver needs to look at the OVERLAPPED
* result for more information. The m_EventData BYTE array should contain
* information about the event. This is also where you can ask the device
* more information about what event just occured.
*
* Sample Notes:
*
* This WIA sample driver uses the Windows Registry to simulate the interrupt
* event signaling. It is not recommended to use this method in a production
* driver. The #define USE_REAL_EVENTS should be defined to build a WIA driver
* that uses real device events.
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::GetNotificationData( LPSTINOTIFY pBuffer )
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::GetNotificationData");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if(!pBuffer){
return E_INVALIDARG;
}
DWORD dwBytesRet = 0;
//
// check the event source flag, m_bPolledEvent.
// * If it is TRUE, just pass the event set in GetStatus() call to
// the STINOTIFY buffer and continue.
//
// * If it is FALSE, then you need to read the m_EventData buffer
// to determine what event was fired.
//
if (m_bPolledEvent == FALSE) {
#ifdef USE_REAL_EVENTS
BOOL bResult = GetOverlappedResult(m_hDeviceDataHandle, &m_EventOverlapped, &dwBytesRet, FALSE );
if (bResult) {
//
// read the m_EventData buffer to determine the proper event.
// set the m_guidLastEvent member to the proper event GUID
// set the m_guidLastEvent to GUID_NULL when an event has
// not happened that you are concerned with
//
}
#else // USE_REAL_EVENTS
if(m_hFakeEventKey){
LONG lEventCode = 0;
DWORD dwEventCodeSize = sizeof(lEventCode);
DWORD dwType = REG_DWORD;
if(RegQueryValueEx(m_hFakeEventKey,
WIASCANR_DWORD_FAKE_EVENT_CODE,
NULL,
&dwType,
(BYTE*)&lEventCode,
&dwEventCodeSize) == ERROR_SUCCESS){
//
// process event code
//
switch(lEventCode){
case ID_FAKE_SCANBUTTON:
m_guidLastEvent = WIA_EVENT_SCAN_IMAGE;
break;
case ID_FAKE_COPYBUTTON:
m_guidLastEvent = WIA_EVENT_SCAN_PRINT_IMAGE;
break;
case ID_FAKE_FAXBUTTON:
m_guidLastEvent = WIA_EVENT_SCAN_FAX_IMAGE;
break;
case ID_FAKE_NOEVENT:
default:
break;
}
}
}
#endif // USE_REAL_EVENTS
}
//
// If the event was triggered, then fill in the STINOTIFY structure with
// the proper event information
//
if (m_guidLastEvent != GUID_NULL) {
memset(pBuffer,0,sizeof(STINOTIFY));
pBuffer->dwSize = sizeof(STINOTIFY);
pBuffer->guidNotificationCode = m_guidLastEvent;
//
// reset the Last event GUID to GUID_NULL to clear the cached event
//
m_guidLastEvent = GUID_NULL;
} else {
return STIERR_NOEVENTS;
}
return S_OK;
}
/**************************************************************************\
* CWIADevice::GetLastErrorInfo
*
* GetLastErrorInfo is called to get more information about a WIA device
* specific error code. This code could come from the IStiUsd::GetLastError()
* method call, or a vendor specific application may want more information
* about a known error code.
*
*
* Arguments:
*
* pLastErrorInfo - Pointer to extended device error data.
*
* Return Value:
*
* S_OK - if the data can be written correctly
* E_xxx - Error code if the operation fails
*
* History:
*
* 03/05/2002 Original Version
*
\**************************************************************************/
STDMETHODIMP CWIADevice::GetLastErrorInfo(STI_ERROR_INFO *pLastErrorInfo)
{
CWiaLogProc WIAS_LOGPROC(m_pIWiaLog,
WIALOG_NO_RESOURCE_ID,
WIALOG_LEVEL3,
"CWIADevice::GetLastErrorInfo");
//
// If the caller did not pass in the correct parameters, then fail the
// call with E_INVALIDARG.
//
if (!pLastErrorInfo) {
return E_INVALIDARG;
}
pLastErrorInfo->dwGenericError = m_dwLastOperationError;
pLastErrorInfo->szExtendedErrorText[0] = '\0';
return S_OK;
}